FPSMS-frontend
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 

439 lignes
14 KiB

  1. // actions.ts
  2. "use server";
  3. import { cache } from 'react';
  4. import { serverFetchJson } from "@/app/utils/fetchUtil"; // 改为 serverFetchJson
  5. import { BASE_API_URL } from "@/config/api";
  6. export interface RecordsRes<T> {
  7. records: T[];
  8. total: number;
  9. }
  10. export interface InventoryLotDetailResponse {
  11. id: number;
  12. inventoryLotId: number;
  13. itemId: number;
  14. itemCode: string;
  15. itemName: string;
  16. lotNo: string;
  17. expiryDate: string;
  18. productionDate: string;
  19. stockInDate: string;
  20. inQty: number;
  21. outQty: number;
  22. holdQty: number;
  23. availableQty: number;
  24. uom: string;
  25. warehouseCode: string;
  26. warehouseName: string;
  27. warehouseSlot: string;
  28. warehouseArea: string;
  29. warehouse: string;
  30. varianceQty: number | null;
  31. status: string;
  32. remarks: string | null;
  33. stockTakeRecordStatus: string;
  34. stockTakeRecordId: number | null;
  35. firstStockTakeQty: number | null;
  36. secondStockTakeQty: number | null;
  37. firstBadQty: number | null;
  38. secondBadQty: number | null;
  39. approverQty: number | null;
  40. approverBadQty: number | null;
  41. finalQty: number | null;
  42. bookQty: number | null;
  43. }
  44. export const getInventoryLotDetailsBySection = async (
  45. stockTakeSection: string,
  46. stockTakeId?: number | null,
  47. pageNum?: number,
  48. pageSize?: number
  49. ) => {
  50. console.log('🌐 [API] getInventoryLotDetailsBySection called with:', {
  51. stockTakeSection,
  52. stockTakeId,
  53. pageNum,
  54. pageSize
  55. });
  56. const encodedSection = encodeURIComponent(stockTakeSection);
  57. let url = `${BASE_API_URL}/stockTakeRecord/inventoryLotDetailsBySection?stockTakeSection=${encodedSection}&pageNum=${pageNum}&pageSize=${pageSize}`;
  58. if (stockTakeId != null && stockTakeId > 0) {
  59. url += `&stockTakeId=${stockTakeId}`;
  60. }
  61. console.log(' [API] Full URL:', url);
  62. const response = await serverFetchJson<RecordsRes<InventoryLotDetailResponse>>(
  63. url,
  64. {
  65. method: "GET",
  66. },
  67. );
  68. console.log('[API] Response received:', response);
  69. return response;
  70. }
  71. export interface SaveStockTakeRecordRequest {
  72. stockTakeRecordId?: number | null;
  73. inventoryLotLineId: number;
  74. qty: number;
  75. badQty: number;
  76. //stockTakerName: string;
  77. remark?: string | null;
  78. }
  79. export interface AllPickedStockTakeListReponse {
  80. id: number;
  81. stockTakeSession: string;
  82. lastStockTakeDate: string | null;
  83. status: string|null;
  84. approverName: string | null;
  85. currentStockTakeItemNumber: number;
  86. totalInventoryLotNumber: number;
  87. stockTakeId: number;
  88. stockTakerName: string | null;
  89. totalItemNumber: number;
  90. startTime: string | null;
  91. endTime: string | null;
  92. planStartDate: string | null;
  93. stockTakeSectionDescription: string | null;
  94. reStockTakeTrueFalse: boolean;
  95. }
  96. export const getApproverInventoryLotDetailsAll = async (
  97. stockTakeId?: number | null,
  98. pageNum: number = 0,
  99. pageSize: number = 100
  100. ) => {
  101. const params = new URLSearchParams();
  102. params.append("pageNum", String(pageNum));
  103. params.append("pageSize", String(pageSize));
  104. if (stockTakeId != null && stockTakeId > 0) {
  105. params.append("stockTakeId", String(stockTakeId));
  106. }
  107. const url = `${BASE_API_URL}/stockTakeRecord/approverInventoryLotDetailsAll?${params.toString()}`;
  108. const response = await serverFetchJson<RecordsRes<InventoryLotDetailResponse>>(
  109. url,
  110. {
  111. method: "GET",
  112. },
  113. );
  114. return response;
  115. }
  116. export const importStockTake = async (data: FormData) => {
  117. const importStockTake = await serverFetchJson<string>(
  118. `${BASE_API_URL}/stockTake/import`,
  119. {
  120. method: "POST",
  121. body: data,
  122. },
  123. );
  124. return importStockTake;
  125. }
  126. export const getStockTakeRecords = async () => {
  127. const stockTakeRecords = await serverFetchJson<AllPickedStockTakeListReponse[]>( // 改为 serverFetchJson
  128. `${BASE_API_URL}/stockTakeRecord/AllPickedStockOutRecordList`,
  129. {
  130. method: "GET",
  131. },
  132. );
  133. return stockTakeRecords;
  134. }
  135. export const getStockTakeRecordsPaged = async (
  136. pageNum: number,
  137. pageSize: number,
  138. params?: { sectionDescription?: string; stockTakeSections?: string }
  139. ) => {
  140. const searchParams = new URLSearchParams();
  141. searchParams.set("pageNum", String(pageNum));
  142. searchParams.set("pageSize", String(pageSize));
  143. if (params?.sectionDescription && params.sectionDescription !== "All") {
  144. searchParams.set("sectionDescription", params.sectionDescription);
  145. }
  146. if (params?.stockTakeSections?.trim()) {
  147. searchParams.set("stockTakeSections", params.stockTakeSections.trim());
  148. }
  149. const url = `${BASE_API_URL}/stockTakeRecord/AllPickedStockOutRecordList?${searchParams.toString()}`;
  150. const res = await serverFetchJson<RecordsRes<AllPickedStockTakeListReponse>>(url, { method: "GET" });
  151. return res;
  152. };
  153. export const getApproverStockTakeRecords = async () => {
  154. const stockTakeRecords = await serverFetchJson<AllPickedStockTakeListReponse[]>( // 改为 serverFetchJson
  155. `${BASE_API_URL}/stockTakeRecord/AllApproverStockTakeList`,
  156. {
  157. method: "GET",
  158. },
  159. );
  160. return stockTakeRecords;
  161. }
  162. export const createStockTakeForSections = async () => {
  163. const createStockTakeForSections = await serverFetchJson<Map<string, string>>(
  164. `${BASE_API_URL}/stockTake/createForSections`,
  165. {
  166. method: "POST",
  167. },
  168. );
  169. return createStockTakeForSections;
  170. }
  171. export const saveStockTakeRecord = async (
  172. request: SaveStockTakeRecordRequest,
  173. stockTakeId: number,
  174. stockTakerId: number
  175. ) => {
  176. try {
  177. const result = await serverFetchJson<any>(
  178. `${BASE_API_URL}/stockTakeRecord/saveStockTakeRecord?stockTakeId=${stockTakeId}&stockTakerId=${stockTakerId}`,
  179. {
  180. method: "POST",
  181. headers: {
  182. "Content-Type": "application/json",
  183. },
  184. body: JSON.stringify(request),
  185. },
  186. );
  187. console.log('saveStockTakeRecord: request:', request);
  188. console.log('saveStockTakeRecord: stockTakeId:', stockTakeId);
  189. console.log('saveStockTakeRecord: stockTakerId:', stockTakerId);
  190. return result;
  191. } catch (error: any) {
  192. // 尝试从错误响应中提取消息
  193. if (error?.response) {
  194. try {
  195. const errorData = await error.response.json();
  196. const errorWithMessage = new Error(errorData.message || errorData.error || "Failed to save stock take record");
  197. (errorWithMessage as any).response = error.response;
  198. throw errorWithMessage;
  199. } catch {
  200. throw error;
  201. }
  202. }
  203. throw error;
  204. }
  205. }
  206. export interface BatchSaveStockTakeRecordRequest {
  207. stockTakeId: number;
  208. stockTakeSection: string;
  209. stockTakerId: number;
  210. //stockTakerName: string;
  211. }
  212. export interface BatchSaveStockTakeRecordResponse {
  213. successCount: number;
  214. errorCount: number;
  215. errors: string[];
  216. }
  217. export const batchSaveStockTakeRecords = cache(async (data: BatchSaveStockTakeRecordRequest) => {
  218. return serverFetchJson<BatchSaveStockTakeRecordResponse>(`${BASE_API_URL}/stockTakeRecord/batchSaveStockTakeRecords`,
  219. {
  220. method: "POST",
  221. body: JSON.stringify(data),
  222. headers: { "Content-Type": "application/json" },
  223. })
  224. })
  225. // Add these interfaces and functions
  226. export interface SaveApproverStockTakeRecordRequest {
  227. stockTakeRecordId?: number | null;
  228. qty: number;
  229. badQty: number;
  230. approverId?: number | null;
  231. approverQty?: number | null;
  232. approverBadQty?: number | null;
  233. }
  234. export interface BatchSaveApproverStockTakeRecordRequest {
  235. stockTakeId: number;
  236. stockTakeSection: string;
  237. approverId: number;
  238. variancePercentTolerance?: number | null;
  239. }
  240. export interface BatchSaveApproverStockTakeRecordResponse {
  241. successCount: number;
  242. errorCount: number;
  243. errors: string[];
  244. }
  245. export interface BatchSaveApproverStockTakeAllRequest {
  246. stockTakeId: number;
  247. approverId: number;
  248. variancePercentTolerance?: number | null;
  249. }
  250. export const saveApproverStockTakeRecord = async (
  251. request: SaveApproverStockTakeRecordRequest,
  252. stockTakeId: number
  253. ) => {
  254. try {
  255. const result = await serverFetchJson<any>(
  256. `${BASE_API_URL}/stockTakeRecord/saveApproverStockTakeRecord?stockTakeId=${stockTakeId}`,
  257. {
  258. method: "POST",
  259. headers: {
  260. "Content-Type": "application/json",
  261. },
  262. body: JSON.stringify(request),
  263. },
  264. );
  265. return result;
  266. } catch (error: any) {
  267. if (error?.response) {
  268. try {
  269. const errorData = await error.response.json();
  270. const errorWithMessage = new Error(errorData.message || errorData.error || "Failed to save approver stock take record");
  271. (errorWithMessage as any).response = error.response;
  272. throw errorWithMessage;
  273. } catch {
  274. throw error;
  275. }
  276. }
  277. throw error;
  278. }
  279. }
  280. export const batchSaveApproverStockTakeRecords = cache(async (data: BatchSaveApproverStockTakeRecordRequest) => {
  281. return serverFetchJson<BatchSaveApproverStockTakeRecordResponse>(
  282. `${BASE_API_URL}/stockTakeRecord/batchSaveApproverStockTakeRecords`,
  283. {
  284. method: "POST",
  285. body: JSON.stringify(data),
  286. headers: { "Content-Type": "application/json" },
  287. }
  288. )
  289. }
  290. )
  291. export const batchSaveApproverStockTakeRecordsAll = cache(async (data: BatchSaveApproverStockTakeAllRequest) => {
  292. return serverFetchJson<BatchSaveApproverStockTakeRecordResponse>(
  293. `${BASE_API_URL}/stockTakeRecord/batchSaveApproverStockTakeRecordsAll`,
  294. {
  295. method: "POST",
  296. body: JSON.stringify(data),
  297. headers: { "Content-Type": "application/json" },
  298. }
  299. )
  300. })
  301. export const updateStockTakeRecordStatusToNotMatch = async (
  302. stockTakeRecordId: number
  303. ) => {
  304. try {
  305. const result = await serverFetchJson<any>(
  306. `${BASE_API_URL}/stockTakeRecord/updateStockTakeRecordStatusToNotMatch?stockTakeRecordId=${stockTakeRecordId}`,
  307. {
  308. method: "POST",
  309. },
  310. );
  311. return result;
  312. } catch (error: any) {
  313. if (error?.response) {
  314. try {
  315. const errorData = await error.response.json();
  316. const errorWithMessage = new Error(errorData.message || errorData.error || "Failed to update stock take record status");
  317. (errorWithMessage as any).response = error.response;
  318. throw errorWithMessage;
  319. } catch {
  320. throw error;
  321. }
  322. }
  323. throw error;
  324. }
  325. }
  326. export const getInventoryLotDetailsBySectionNotMatch = async (
  327. stockTakeSection: string,
  328. stockTakeId?: number | null,
  329. pageNum: number = 0,
  330. pageSize: number = 10
  331. ) => {
  332. const encodedSection = encodeURIComponent(stockTakeSection);
  333. let url = `${BASE_API_URL}/stockTakeRecord/inventoryLotDetailsBySectionNotMatch?stockTakeSection=${encodedSection}&pageNum=${pageNum}`;
  334. // Only add pageSize if it's not "all" (which would be a large number)
  335. if (pageSize < 100000) {
  336. url += `&pageSize=${pageSize}`;
  337. }
  338. // If pageSize is large (meaning "all"), don't send it - backend will return all
  339. if (stockTakeId != null && stockTakeId > 0) {
  340. url += `&stockTakeId=${stockTakeId}`;
  341. }
  342. const response = await serverFetchJson<RecordsRes<InventoryLotDetailResponse>>(
  343. url,
  344. {
  345. method: "GET",
  346. },
  347. );
  348. return response;
  349. }
  350. export interface SearchStockTransactionResult {
  351. records: StockTransactionResponse[];
  352. total: number;
  353. }
  354. export interface SearchStockTransactionRequest {
  355. startDate: string | null;
  356. endDate: string | null;
  357. itemCode: string | null;
  358. itemName: string | null;
  359. type: string | null;
  360. pageNum: number;
  361. pageSize: number;
  362. }
  363. export interface StockTransactionResponse {
  364. id: number;
  365. transactionType: string;
  366. itemId: number;
  367. itemCode: string | null;
  368. itemName: string | null;
  369. balanceQty: number | null;
  370. qty: number;
  371. type: string | null;
  372. status: string;
  373. transactionDate: string | null;
  374. date: string | null; // 添加这个字段
  375. lotNo: string | null;
  376. stockInId: number | null;
  377. stockOutId: number | null;
  378. remarks: string | null;
  379. }
  380. export interface StockTransactionListResponse {
  381. records: RecordsRes<StockTransactionResponse>;
  382. }
  383. export const searchStockTransactions = cache(async (request: SearchStockTransactionRequest) => {
  384. const params = new URLSearchParams();
  385. if (request.itemCode) params.append("itemCode", request.itemCode);
  386. if (request.itemName) params.append("itemName", request.itemName);
  387. if (request.type) params.append("type", request.type);
  388. if (request.startDate) params.append("startDate", request.startDate);
  389. if (request.endDate) params.append("endDate", request.endDate);
  390. params.append("pageNum", String(request.pageNum || 0));
  391. params.append("pageSize", String(request.pageSize || 100));
  392. const queryString = params.toString();
  393. const url = `${BASE_API_URL}/stockTakeRecord/searchStockTransactions${queryString ? `?${queryString}` : ''}`;
  394. const response = await serverFetchJson<RecordsRes<StockTransactionResponse>>(
  395. url,
  396. {
  397. method: "GET",
  398. next: { tags: ["Stock Transaction List"] },
  399. }
  400. );
  401. // 回傳 records 與 total,供分頁正確顯示
  402. return {
  403. records: response?.records || [],
  404. total: response?.total ?? 0,
  405. };
  406. });