FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

actions.ts 14 KiB

2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
1ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
2ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. });