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

actions.tsx 13 KiB

9ヶ月前
9ヶ月前
6ヶ月前
3ヶ月前
9ヶ月前
9ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
5ヶ月前
3ヶ月前
3ヶ月前
3ヶ月前
3ヶ月前
5ヶ月前
6ヶ月前
5ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
6ヶ月前
1ヶ月前
1ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
4ヶ月前
6ヶ月前
6ヶ月前
2ヶ月前
6ヶ月前
1ヶ月前
1ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. "use server";
  2. import { BASE_API_URL } from "@/config/api";
  3. // import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
  4. import { revalidateTag } from "next/cache";
  5. import { cache } from "react";
  6. import { serverFetch, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
  7. import { QcItemResult } from "../settings/qcItem";
  8. import { RecordsRes } from "../utils";
  9. import { DoResult } from ".";
  10. import { GridRowId, GridRowSelectionModel } from "@mui/x-data-grid";
  11. import { GET } from "../auth/[...nextauth]/route";
  12. import { stringify } from "querystring";
  13. import { convertObjToURLSearchParams } from "@/app/utils/commonUtil";
  14. export interface CreateConsoDoInput {
  15. ids: GridRowSelectionModel;
  16. }
  17. export interface DoDetail {
  18. id: number;
  19. code: string;
  20. supplierCode: string;
  21. shopCode: string;
  22. shopName: string;
  23. currencyCode: string;
  24. orderDate: string;
  25. estimatedArrivalDate: string;
  26. completeDate: string;
  27. status: string;
  28. deliveryOrderLines: DoDetailLine[];
  29. }
  30. export interface DoDetailLine {
  31. id: number;
  32. itemNo: string;
  33. qty: number;
  34. price: number;
  35. status: string;
  36. itemName?: string;
  37. uomCode?: string;
  38. uom?: string;
  39. shortUom?: string;
  40. }
  41. export interface DoSearchAll {
  42. id: number;
  43. code: string;
  44. status: string;
  45. estimatedArrivalDate: number[];
  46. orderDate: number[];
  47. supplierName: string;
  48. shopName: string;
  49. shopAddress?: string;
  50. }
  51. export interface DoSearchLiteResponse {
  52. records: DoSearchAll[];
  53. total: number;
  54. }
  55. export interface ReleaseDoRequest {
  56. id: number;
  57. }
  58. export interface ReleaseDoResponse {
  59. id: number;
  60. entity: { status: string }
  61. }
  62. export interface AssignByStoreRequest {
  63. storeId: string; // "2/F" or "4/F"
  64. assignTo: number;
  65. }
  66. export interface AssignByStoreResponse {
  67. id: number;
  68. code: string;
  69. name: string;
  70. type: string;
  71. message: string;
  72. errorPosition: string;
  73. entity: any;
  74. }
  75. export interface PrintDeliveryNoteRequest{
  76. doPickOrderId: number;
  77. printerId: number;
  78. printQty: number;
  79. numOfCarton: number;
  80. isDraft: boolean;
  81. }
  82. export interface PrintDeliveryNoteResponse{
  83. success: boolean;
  84. message?: string
  85. }
  86. export interface PrintDNLabelsRequest{
  87. doPickOrderId: number;
  88. printerId: number;
  89. printQty: number;
  90. numOfCarton: number;
  91. }
  92. export interface PrintDNLabelsRespone{
  93. success: boolean;
  94. message?: string
  95. }
  96. export interface BatchReleaseRequest {
  97. ids: number[];
  98. }
  99. export interface BatchReleaseResponse {
  100. success: boolean;
  101. message?: string
  102. }
  103. export interface getTicketReleaseTable {
  104. id: number;
  105. storeId: string | null;
  106. ticketNo: string | null;
  107. pickOrderId: number | null;
  108. doOrderId: number | null;
  109. pickOrderCode: string | null;
  110. deliveryOrderCode: string | null;
  111. loadingSequence: number | null;
  112. ticketStatus: string | null;
  113. truckId: number | null;
  114. truckDepartureTime: string | null;
  115. shopId: number | null;
  116. handledBy: number | null;
  117. ticketReleaseTime: string | null;
  118. ticketCompleteDateTime: string | null;
  119. truckLanceCode: string | null;
  120. shopCode: string | null;
  121. shopName: string | null;
  122. requiredDeliveryDate: string | null;
  123. handlerName: string | null;
  124. numberOfFGItems: number;
  125. /** 進行中 do_pick_order 為 true,才可呼叫 force-complete / revert-assignment(id 為 do_pick_order 主鍵) */
  126. isActiveDoPickOrder?: boolean;
  127. }
  128. export interface TruckScheduleDashboardItem {
  129. storeId: string | null;
  130. truckId: number | null;
  131. truckLanceCode: string | null;
  132. truckDepartureTime: string | number[] | null;
  133. numberOfShopsToServe: number;
  134. numberOfPickTickets: number;
  135. totalItemsToPick: number;
  136. numberOfTicketsReleased: number;
  137. firstTicketStartTime: string | number[] | null;
  138. numberOfTicketsCompleted: number;
  139. lastTicketEndTime: string | number[] | null;
  140. pickTimeTakenMinutes: number | null;
  141. }
  142. export interface SearchDeliveryOrderInfoRequest {
  143. code: string;
  144. shopName: string;
  145. status: string;
  146. orderStartDate: string;
  147. orderEndDate: string;
  148. estArrStartDate: string;
  149. estArrEndDate: string;
  150. pageSize: number;
  151. pageNum: number;
  152. }
  153. export interface SearchDeliveryOrderInfoResponse {
  154. records: DeliveryOrderInfo[];
  155. total: number;
  156. }
  157. export interface DeliveryOrderInfo {
  158. id: number;
  159. code: string;
  160. shopName: string;
  161. supplierName: string; // 改为必需字段
  162. status: string;
  163. orderDate: string;
  164. estimatedArrivalDate: string;
  165. deliveryOrderLines: DoDetailLine[];
  166. }
  167. export const fetchDoRecordByPage = cache(async (data?: SearchDeliveryOrderInfoRequest) => {
  168. const queryStr = convertObjToURLSearchParams(data)
  169. console.log("queryStr", queryStr)
  170. const response = serverFetchJson<SearchDeliveryOrderInfoResponse>(
  171. `${BASE_API_URL}/jo/getRecordByPage?${queryStr}`,
  172. {
  173. method: "GET",
  174. headers: { "Content-Type": "application/json" },
  175. next: {
  176. tags: ["jos"]
  177. }
  178. }
  179. )
  180. return response
  181. })
  182. export const fetchTicketReleaseTable = cache(async (startDate: string, endDate: string)=> {
  183. return await serverFetchJson<getTicketReleaseTable[]>(
  184. `${BASE_API_URL}/doPickOrder/ticket-release-table/${startDate}&${endDate}`,
  185. {
  186. method: "GET",
  187. }
  188. );
  189. });
  190. export const fetchTruckScheduleDashboard = cache(async (date?: string) => {
  191. const url = date
  192. ? `${BASE_API_URL}/doPickOrder/truck-schedule-dashboard?date=${date}`
  193. : `${BASE_API_URL}/doPickOrder/truck-schedule-dashboard`;
  194. return await serverFetchJson<TruckScheduleDashboardItem[]>(
  195. url,
  196. {
  197. method: "GET",
  198. }
  199. );
  200. });
  201. export const startBatchReleaseAsyncSingle = cache(async (data: { doId: number; userId: number }) => {
  202. const { doId, userId } = data;
  203. return await serverFetchJson<{ id: number|null; code: string; entity?: any }>(
  204. `${BASE_API_URL}/doPickOrder/batch-release/async-single?userId=${userId}`,
  205. {
  206. method: "POST",
  207. body: JSON.stringify(doId),
  208. headers: { "Content-Type": "application/json" },
  209. }
  210. );
  211. });
  212. export const startBatchReleaseAsync = cache(async (data: { ids: number[]; userId: number }) => {
  213. const { ids, userId } = data;
  214. return await serverFetchJson<{ id: number|null; code: string; entity?: any }>(
  215. `${BASE_API_URL}/doPickOrder/batch-release/async?userId=${userId}`,
  216. {
  217. method: "POST",
  218. body: JSON.stringify(ids),
  219. headers: { "Content-Type": "application/json" },
  220. }
  221. );
  222. });
  223. export const getBatchReleaseProgress = cache(async (jobId: string) => {
  224. return await serverFetchJson<{ id: number|null; code: string; entity?: any }>(
  225. `${BASE_API_URL}/doPickOrder/batch-release/progress/${jobId}`,
  226. { method: "GET" }
  227. );
  228. });
  229. export const assignPickOrderByStore = cache(async (data: AssignByStoreRequest) => {
  230. return await serverFetchJson<AssignByStoreResponse>(`${BASE_API_URL}/doPickOrder/assign-by-store`,
  231. {
  232. method: "POST",
  233. body: JSON.stringify(data),
  234. headers: { "Content-Type": "application/json" },
  235. })
  236. })
  237. export const releaseAssignedPickOrderByStore = cache(async (data: AssignByStoreRequest) => {
  238. return await serverFetchJson<AssignByStoreResponse>(`${BASE_API_URL}/doPickOrder/release-assigned-by-store`,
  239. {
  240. method: "POST",
  241. body: JSON.stringify(data),
  242. headers: { "Content-Type": "application/json" },
  243. })
  244. })
  245. export async function releaseDo(input: ReleaseDoRequest) {
  246. const response = await serverFetchJson<ReleaseDoResponse>(`${BASE_API_URL}/do/release`, {
  247. method: 'POST',
  248. body: JSON.stringify(input),
  249. headers: {
  250. 'Content-Type': 'application/json',
  251. },
  252. });
  253. revalidateTag('do');
  254. return response;
  255. }
  256. export const preloadDo = () => {
  257. fetchDoList();
  258. };
  259. export const fetchDoList = cache(async () => {
  260. return serverFetchJson<DoResult[]>(`${BASE_API_URL}/do/list`, {
  261. next: { tags: ["doList"] },
  262. });
  263. });
  264. export const fetchDoDetail = cache(async (id: number) => {
  265. return serverFetchJson<DoDetail>(`${BASE_API_URL}/do/detail/${id}`, {
  266. method: "GET",
  267. headers: { "Content-Type": "application/json" },
  268. next: {
  269. tags: ["doDetail"]
  270. }
  271. });
  272. });
  273. export async function fetchDoSearch(
  274. code: string,
  275. shopName: string,
  276. status: string,
  277. orderStartDate: string,
  278. orderEndDate: string,
  279. estArrStartDate: string,
  280. estArrEndDate: string,
  281. pageNum?: number,
  282. pageSize?: number,
  283. truckLanceCode?: string
  284. ): Promise<DoSearchLiteResponse> {
  285. // 构建请求体
  286. const requestBody: any = {
  287. code: code || null,
  288. shopName: shopName || null,
  289. status: status || null,
  290. estimatedArrivalDate: estArrStartDate || null, // 使用单个日期字段
  291. truckLanceCode: truckLanceCode || null,
  292. pageNum: pageNum || 1,
  293. pageSize: pageSize || 10,
  294. };
  295. // 如果日期不为空,转换为 LocalDateTime 格式
  296. if (estArrStartDate) {
  297. requestBody.estimatedArrivalDate = estArrStartDate; // 格式: "2026-01-19T00:00:00"
  298. } else {
  299. requestBody.estimatedArrivalDate = null;
  300. }
  301. const url = `${BASE_API_URL}/do/search-do-lite`;
  302. const data = await serverFetchJson<DoSearchLiteResponse>(url, {
  303. method: "POST",
  304. headers: { "Content-Type": "application/json" },
  305. body: JSON.stringify(requestBody),
  306. });
  307. return data;
  308. }
  309. export async function fetchDoSearchList(
  310. code: string,
  311. shopName: string,
  312. status: string,
  313. orderStartDate: string,
  314. orderEndDate: string,
  315. etaFrom: string,
  316. etaTo: string,
  317. page = 0,
  318. size = 500
  319. ): Promise<DoSearchAll[]> {
  320. const params = new URLSearchParams();
  321. if (code) params.append("code", code);
  322. if (shopName) params.append("shopName", shopName);
  323. if (status) params.append("status", status);
  324. if (orderStartDate) params.append("orderFrom", orderStartDate);
  325. if (orderEndDate) params.append("orderTo", orderEndDate);
  326. if (etaFrom) params.append("etaFrom", etaFrom);
  327. if (etaTo) params.append("etaTo", etaTo);
  328. params.append("page", String(page));
  329. params.append("size", String(size));
  330. const res = await fetch(`/api/delivery-order/search-do-list?${params.toString()}`);
  331. const pageData = await res.json(); // Spring Page 结构
  332. return pageData.content; // 前端继续沿用你原来的 client-side 分页逻辑
  333. }
  334. export async function printDN(request: PrintDeliveryNoteRequest){
  335. const params = new URLSearchParams();
  336. params.append('doPickOrderId', request.doPickOrderId.toString());
  337. params.append('printerId', request.printerId.toString());
  338. if (request.printQty !== null && request.printQty !== undefined) {
  339. params.append('printQty', request.printQty.toString());
  340. }
  341. params.append('numOfCarton', request.numOfCarton.toString());
  342. params.append('isDraft', request.isDraft.toString());
  343. try {
  344. const response = await serverFetch(`${BASE_API_URL}/do/print-DN?${params.toString()}`, {
  345. method: "GET",
  346. });
  347. if (response.ok) {
  348. return { success: true, message: "Print job sent successfully (DN)" } as PrintDeliveryNoteResponse;
  349. }
  350. const errorText = await response.text();
  351. console.error("Print DN error:", errorText);
  352. return {
  353. success: false,
  354. message: "No data found for this pick order."
  355. } as PrintDeliveryNoteResponse;
  356. } catch (error) {
  357. console.error("Error in printDN:", error);
  358. return {
  359. success: false,
  360. message: "No data found for this pick order."
  361. } as PrintDeliveryNoteResponse;
  362. }
  363. }
  364. export async function printDNLabels(request: PrintDNLabelsRequest){
  365. const params = new URLSearchParams();
  366. params.append('doPickOrderId', request.doPickOrderId.toString());
  367. params.append('printerId', request.printerId.toString());
  368. if (request.printQty !== null && request.printQty !== undefined) {
  369. params.append('printQty', request.printQty.toString());
  370. }
  371. params.append('numOfCarton', request.numOfCarton.toString());
  372. const response = await serverFetchWithNoContent(`${BASE_API_URL}/do/print-DNLabels?${params.toString()}`,{
  373. method: "GET"
  374. });
  375. return { success: true, message: "Print job sent successfully (labels)"} as PrintDeliveryNoteResponse
  376. }
  377. export interface Check4FTruckBatchResponse {
  378. hasProblem: boolean;
  379. problems: ProblemDoDto[];
  380. }
  381. export interface ProblemDoDto {
  382. deliveryOrderId: number;
  383. deliveryOrderCode: string;
  384. targetDate: string;
  385. availableTrucks: TruckInfoDto[];
  386. }
  387. export interface TruckInfoDto {
  388. id: number;
  389. truckLanceCode: string;
  390. departureTime: string;
  391. storeId: string;
  392. shopCode: string;
  393. shopName: string;
  394. }
  395. export const check4FTrucksBatch = cache(async (doIds: number[]) => {
  396. return await serverFetchJson<Check4FTruckBatchResponse>(`${BASE_API_URL}/do/check-4f-trucks-batch`, {
  397. method: "POST",
  398. body: JSON.stringify(doIds),
  399. headers: { "Content-Type": "application/json" },
  400. });
  401. });
  402. export async function fetchAllDoSearch(
  403. code: string,
  404. shopName: string,
  405. status: string,
  406. estArrStartDate: string,
  407. truckLanceCode?: string // 添加这个参数
  408. ): Promise<DoSearchAll[]> {
  409. // 使用一个很大的 pageSize 来获取所有匹配的记录
  410. const requestBody: any = {
  411. code: code || null,
  412. shopName: shopName || null,
  413. status: status || null,
  414. estimatedArrivalDate: estArrStartDate || null,
  415. truckLanceCode: truckLanceCode || null, // 添加这个字段
  416. pageNum: 1,
  417. pageSize: 10000, // 使用一个很大的值来获取所有记录
  418. };
  419. if (estArrStartDate) {
  420. requestBody.estimatedArrivalDate = estArrStartDate;
  421. } else {
  422. requestBody.estimatedArrivalDate = null;
  423. }
  424. const url = `${BASE_API_URL}/do/search-do-lite`;
  425. const data = await serverFetchJson<DoSearchLiteResponse>(url, {
  426. method: "POST",
  427. headers: { "Content-Type": "application/json" },
  428. body: JSON.stringify(requestBody),
  429. });
  430. return data.records;
  431. }