FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1519 lines
42 KiB

  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 { serverFetchJson } from "@/app/utils/fetchUtil";
  7. import { QcItemResult } from "../settings/qcItem";
  8. import { RecordsRes } from "../utils";
  9. import {
  10. ConsoPickOrderResult,
  11. PickOrderLineWithSuggestedLot,
  12. PickOrderResult,
  13. PreReleasePickOrderSummary,
  14. StockOutLine,
  15. } from ".";
  16. import { PurchaseQcResult } from "../po/actions";
  17. import { StringNullableChain } from "lodash";
  18. // import { BASE_API_URL } from "@/config/api";
  19. import dayjs from "dayjs";
  20. export interface SavePickOrderLineRequest {
  21. itemId: number
  22. qty: number
  23. uomId: number
  24. }
  25. export interface SavePickOrderRequest {
  26. type: string
  27. targetDate: string
  28. pickOrderLine: SavePickOrderLineRequest[]
  29. }
  30. export interface PostPickOrderResponse<T = null> {
  31. id: number | null;
  32. name: string;
  33. code: string;
  34. type?: string;
  35. message: string | null;
  36. errorPosition: string
  37. entity?: T | T[];
  38. consoCode?: string;
  39. }
  40. export interface PostStockOutLiineResponse<T> {
  41. id: number | null;
  42. name: string;
  43. code: string;
  44. type?: string;
  45. message: string | null;
  46. errorPosition: string | keyof T;
  47. entity: T | T[] | null;
  48. }
  49. export interface ReleasePickOrderInputs {
  50. consoCode: string;
  51. assignTo: number;
  52. }
  53. export interface CreateStockOutLine {
  54. consoCode: string;
  55. pickOrderLineId: number;
  56. inventoryLotLineId: number;
  57. qty: number;
  58. }
  59. export interface UpdateStockOutLine {
  60. id: number;
  61. // consoCode: String,
  62. itemId: number;
  63. qty: number;
  64. pickOrderLineId: number;
  65. inventoryLotLineId?: number;
  66. status: string;
  67. pickTime?: string;
  68. // pickerId: number?
  69. }
  70. export interface PickOrderQcInput {
  71. qty: number;
  72. status: string;
  73. qcResult: PurchaseQcResult[];
  74. }
  75. export interface PickOrderApprovalInput {
  76. allowQty: number;
  77. rejectQty: number;
  78. status: string;
  79. }
  80. export interface GetPickOrderInfoResponse {
  81. consoCode: string | null;
  82. pickOrders: GetPickOrderInfo[];
  83. items: CurrentInventoryItemInfo[];
  84. }
  85. export interface GetPickOrderInfo {
  86. id: number;
  87. code: string;
  88. consoCode: string | null; // 添加 consoCode 属性
  89. targetDate: string | number[]; // Support both formats
  90. type: string;
  91. status: string;
  92. assignTo: number;
  93. groupName: string; // Add this field
  94. pickOrderLines: GetPickOrderLineInfo[];
  95. }
  96. export interface GetPickOrderLineInfo {
  97. id: number;
  98. itemId: number;
  99. itemCode: string;
  100. itemName: string;
  101. availableQty: number| null;
  102. requiredQty: number;
  103. uomShortDesc: string;
  104. uomDesc: string;
  105. suggestedList: any[];
  106. pickedQty: number;
  107. noLotLines: NoLotLineDto[];
  108. }
  109. export interface NoLotLineDto {
  110. stockOutLineId: number;
  111. status: string;
  112. qty: number;
  113. created: string;
  114. modified: string;
  115. }
  116. export interface CurrentInventoryItemInfo {
  117. id: number;
  118. code: string;
  119. name: string;
  120. uomDesc: string;
  121. availableQty: number;
  122. requiredQty: number;
  123. }
  124. export interface SavePickOrderGroupRequest {
  125. groupIds?: number[];
  126. names?: string[];
  127. targetDate?: string;
  128. pickOrderId?: number | null;
  129. }
  130. export interface PickOrderGroupInfo {
  131. id: number;
  132. name: string;
  133. targetDate: string | null;
  134. pickOrderId: number | null;
  135. }
  136. export interface AssignPickOrderInputs {
  137. pickOrderIds: number[];
  138. assignTo: number;
  139. }
  140. export interface LotDetailWithStockOutLine {
  141. lotId: number;
  142. lotNo: string;
  143. expiryDate: string;
  144. location: string;
  145. stockUnit: string;
  146. availableQty: number;
  147. requiredQty: number;
  148. actualPickQty: number;
  149. suggestedPickLotId: number;
  150. lotStatus: string;
  151. lotAvailability: string;
  152. stockOutLineId?: number;
  153. stockOutLineStatus?: string;
  154. stockOutLineQty?: number;
  155. }
  156. export interface PickAnotherLotFormData {
  157. pickOrderLineId: number;
  158. lotId: number;
  159. qty: number;
  160. type: string;
  161. handlerId?: number;
  162. category?: string;
  163. releasedBy?: number;
  164. recordDate?: string;
  165. }
  166. export const recordFailLot = async (data: PickAnotherLotFormData) => {
  167. const result = await serverFetchJson<PostPickOrderResponse>(
  168. `${BASE_API_URL}/suggestedPickLot/recordFailLot`,
  169. {
  170. method: "POST",
  171. body: JSON.stringify(data),
  172. headers: { "Content-Type": "application/json" },
  173. },
  174. );
  175. revalidateTag("pickorder");
  176. return result;
  177. };
  178. export interface PickExecutionIssueData {
  179. type: string;
  180. pickOrderId: number;
  181. pickOrderCode: string;
  182. pickOrderCreateDate: string;
  183. pickExecutionDate: string;
  184. pickOrderLineId: number;
  185. itemId: number;
  186. itemCode: string;
  187. itemDescription: string;
  188. lotId: number|null;
  189. lotNo: string|null;
  190. storeLocation: string;
  191. requiredQty: number;
  192. actualPickQty: number;
  193. missQty: number;
  194. badItemQty: number;
  195. badPackageQty?: number;
  196. /** Optional: frontend-only reference to stock_out_line.id for the picked lot. */
  197. stockOutLineId?: number;
  198. issueRemark: string;
  199. pickerName: string;
  200. handledBy?: number;
  201. badReason?: string;
  202. reason?: string;
  203. }
  204. export type AutoAssignReleaseResponse = {
  205. id: number | null;
  206. name?: string | null;
  207. code?: string | null;
  208. type?: string | null;
  209. message?: string | null;
  210. errorPosition?: string | null;
  211. entity?: any;
  212. };
  213. export interface PickOrderCompletionResponse {
  214. id: number | null;
  215. name: string;
  216. code: string;
  217. type?: string;
  218. message: string | null;
  219. errorPosition: string;
  220. entity?: {
  221. hasCompletedOrders: boolean;
  222. completedOrders: Array<{
  223. pickOrderId: number;
  224. pickOrderCode: string;
  225. consoCode: string;
  226. isCompleted: boolean;
  227. stockOutStatus: string;
  228. totalLines: number;
  229. unfinishedLines: number;
  230. }>;
  231. allOrders: Array<{
  232. pickOrderId: number;
  233. pickOrderCode: string;
  234. consoCode: string;
  235. isCompleted: boolean;
  236. stockOutStatus: string;
  237. totalLines: number;
  238. unfinishedLines: number;
  239. }>;
  240. };
  241. }
  242. export interface UpdateSuggestedLotLineIdRequest {
  243. newLotLineId: number;
  244. }
  245. export interface stockReponse{
  246. id: number;
  247. status: string;
  248. qty: number;
  249. lotId: number;
  250. lotNo: string;
  251. location: string;
  252. availableQty: number;
  253. noLot: boolean;
  254. }
  255. export interface FGPickOrderResponse {
  256. // 新增:支持多个 pick orders
  257. doPickOrderId: number;
  258. pickOrderIds?: number[];
  259. pickOrderCodes?: string[]; // 改为数组
  260. deliveryOrderIds?: number[];
  261. deliveryNos?: string[]; // 改为数组
  262. numberOfPickOrders?: number;
  263. lineCountsPerPickOrder?: number[];// 新增:pick order 数量
  264. // 保留原有字段用于向后兼容(显示第一个 pick order)
  265. pickOrderId: number;
  266. pickOrderCode: string;
  267. pickOrderConsoCode: string;
  268. pickOrderTargetDate: string;
  269. pickOrderStatus: string;
  270. deliveryOrderId: number;
  271. deliveryNo: string;
  272. deliveryDate: string;
  273. shopId: number;
  274. shopCode: string;
  275. shopName: string;
  276. shopAddress: string;
  277. ticketNo: string;
  278. shopPoNo: string;
  279. numberOfCartons: number;
  280. DepartureTime: string;
  281. truckLanceCode: string;
  282. storeId: string;
  283. qrCodeData: number;
  284. }
  285. export interface DoPickOrderDetail {
  286. doPickOrder: {
  287. id: number;
  288. store_id: string;
  289. ticket_no: string;
  290. ticket_status: string;
  291. truck_id: number;
  292. truck_departure_time: string;
  293. shop_id: number;
  294. handled_by: number | null;
  295. loading_sequence: number;
  296. ticket_release_time: string | null;
  297. TruckLanceCode: string;
  298. ShopCode: string;
  299. ShopName: string;
  300. RequiredDeliveryDate: string;
  301. };
  302. pickOrders: Array<{
  303. pick_order_id: number;
  304. pick_order_code: string;
  305. do_order_id: number;
  306. delivery_order_code: string;
  307. consoCode: string;
  308. status: string;
  309. targetDate: string;
  310. }>;
  311. selectedPickOrderId: number;
  312. lotDetails: any[]; // 使用现有的 lot detail 结构
  313. pickOrderCodes?: string;
  314. deliveryNos?: string;
  315. }
  316. export interface AutoAssignReleaseByStoreRequest {
  317. userId: number;
  318. storeId: string; // "2/F" | "4/F"
  319. }
  320. export interface UpdateDoPickOrderHideStatusRequest {
  321. id: number;
  322. name: string;
  323. code: string;
  324. type: string;
  325. message: string;
  326. errorPosition: string;
  327. }
  328. export interface CompletedDoPickOrderResponse {
  329. id: number;
  330. doPickOrderRecordId: number; // ✅ 新增
  331. recordId: number | null;
  332. pickOrderId: number;
  333. pickOrderIds: number[]; // 新增:所有 pick order IDs
  334. pickOrderCode: string;
  335. pickOrderCodes: string; // 新增:所有 pick order codes (逗号分隔)
  336. pickOrderConsoCode: string;
  337. pickOrderStatus: string;
  338. deliveryOrderId: number;
  339. deliveryOrderIds: number[]; // 新增:所有 delivery order IDs
  340. deliveryNo: string;
  341. deliveryNos: string; // 新增:所有 delivery order codes (逗号分隔)
  342. deliveryDate: string;
  343. shopId: number;
  344. shopCode: string;
  345. shopName: string;
  346. shopAddress: string;
  347. ticketNo: string;
  348. shopPoNo: string;
  349. numberOfCartons: number;
  350. truckLanceCode: string;
  351. DepartureTime: string; // 新增
  352. storeId: string;
  353. completedDate: string;
  354. fgPickOrders: FGPickOrderResponse[];
  355. deliveryNoteCode: number;
  356. }
  357. // 新增:搜索参数接口
  358. export interface CompletedDoPickOrderSearchParams {
  359. targetDate?: string;
  360. shopName?: string;
  361. deliveryNoteCode?: string;
  362. /** 卡車/車道(後端 truckLanceCode 模糊匹配) */
  363. truckLanceCode?: string;
  364. }
  365. export interface PickExecutionIssue {
  366. id: number;
  367. pickOrderId: number;
  368. pickOrderCode: string;
  369. pickOrderCreateDate: string;
  370. pickExecutionDate: string;
  371. pickOrderLineId: number;
  372. issueNo: string;
  373. joPickOrderId?: number;
  374. doPickOrderId?: number;
  375. issueCategory: string;
  376. itemId: number;
  377. itemCode: string;
  378. itemDescription: string;
  379. lotId?: number;
  380. lotNo?: string;
  381. storeLocation?: string;
  382. requiredQty: number;
  383. actualPickQty?: number;
  384. missQty?: number;
  385. badItemQty?: number;
  386. issueRemark?: string;
  387. pickerName?: string;
  388. handleStatus: string;
  389. handleDate?: string;
  390. handledBy?: string;
  391. created: string;
  392. createdBy: string;
  393. modified: string;
  394. modifiedBy: string;
  395. }
  396. export interface FetchPickExecutionIssuesParams {
  397. type?: "jo" | "do" | "material";
  398. }
  399. export const fetchAllPickExecutionIssues = cache(
  400. async (params?: FetchPickExecutionIssuesParams): Promise<PickExecutionIssue[]> => {
  401. const queryParams = new URLSearchParams();
  402. if (params?.type) {
  403. queryParams.append("type", params.type);
  404. }
  405. const url = `${BASE_API_URL}/pickExecution/issues/all${
  406. queryParams.toString() ? `?${queryParams.toString()}` : ""
  407. }`;
  408. const result = await serverFetchJson<PickExecutionIssue[]>(url, {
  409. method: "GET",
  410. headers: { "Content-Type": "application/json" },
  411. });
  412. return result ?? [];
  413. }
  414. );
  415. export interface UpdatePickExecutionIssueRequest {
  416. issueId: number;
  417. handleStatus: string;
  418. handleDate?: string;
  419. handledBy?: string;
  420. handleRemark?: string;
  421. }
  422. export interface StoreLaneSummary {
  423. storeId: string;
  424. rows: LaneRow[];
  425. defaultTruckCount: number | null;
  426. }
  427. export interface LaneRow {
  428. truckDepartureTime: string;
  429. lanes: LaneBtn[];
  430. }
  431. export interface LaneBtn {
  432. truckLanceCode: string;
  433. unassigned: number;
  434. total: number;
  435. }
  436. export interface QrPickBatchSubmitRequest {
  437. userId: number;
  438. lines: QrPickSubmitLineRequest[];
  439. }
  440. export interface QrPickSubmitLineRequest {
  441. stockOutLineId: number;
  442. pickOrderLineId: number;
  443. inventoryLotLineId: number | null; // ✅ 修复:应该是 nullable
  444. requiredQty: number | null; // ✅ 修复:添加 requiredQty
  445. actualPickQty: number | null; // ✅ 修复:添加 actualPickQty
  446. stockOutLineStatus: string | null; // ✅ 修复:添加 stockOutLineStatus
  447. pickOrderConsoCode: string | null; // ✅ 修复:添加 pickOrderConsoCode
  448. noLot: boolean; // ✅ 修复:添加 noLot
  449. }
  450. export interface UpdateStockOutLineStatusByQRCodeAndLotNoRequest {
  451. pickOrderLineId: number,
  452. inventoryLotNo: string,
  453. stockInLineId?: number | null,
  454. stockOutLineId: number,
  455. itemId: number,
  456. status: string
  457. }
  458. export interface batchSubmitListRequest {
  459. userId: number;
  460. lines: batchSubmitListLineRequest[];
  461. }
  462. export interface batchSubmitListLineRequest {
  463. stockOutLineId: number; // 修复:改为 stockOutLineId(不是 stockInLineId)
  464. pickOrderLineId: number;
  465. inventoryLotLineId: number | null; // 添加:后端需要的字段
  466. requiredQty: number;
  467. actualPickQty: number;
  468. stockOutLineStatus: string;
  469. pickOrderConsoCode: string;
  470. noLot: boolean;
  471. // 移除:lotNo 和 stockInLineId(后端不需要)
  472. }
  473. export const batchSubmitList = async (data: batchSubmitListRequest) => {
  474. // ✅ 确保发送的是对象,不是数组
  475. const requestBody = Array.isArray(data) ? data[0] : data;
  476. console.log("📤 batchSubmitList - Request body type:", Array.isArray(requestBody) ? "array" : "object");
  477. console.log("📤 batchSubmitList - Request body:", JSON.stringify(requestBody, null, 2));
  478. const response = await serverFetchJson<PostPickOrderResponse<batchSubmitListRequest>>(
  479. `${BASE_API_URL}/stockOutLine/batchSubmitList`,
  480. {
  481. method: "POST",
  482. body: JSON.stringify(requestBody), // ✅ 确保是对象
  483. headers: {
  484. "Content-Type": "application/json", // ✅ 明确指定 Content-Type
  485. },
  486. },
  487. );
  488. return response;
  489. };
  490. export const updateStockOutLineStatusByQRCodeAndLotNo = async (data: UpdateStockOutLineStatusByQRCodeAndLotNoRequest) => {
  491. console.log(" Frontend: Calling updateStockOutLineStatusByQRCodeAndLotNo with data:", data);
  492. try {
  493. const response = await serverFetchJson<PostPickOrderResponse<UpdateStockOutLineStatusByQRCodeAndLotNoRequest>>(
  494. `${BASE_API_URL}/stockOutLine/updateStatusByQRCodeAndLotNo`,
  495. {
  496. method: "POST",
  497. headers: {
  498. "Content-Type": "application/json",
  499. },
  500. body: JSON.stringify(data),
  501. },
  502. );
  503. console.log("✅ Frontend: API call successful, response:", response);
  504. return response;
  505. } catch (error) {
  506. console.error("❌ Frontend: API call failed:", error);
  507. throw error;
  508. }
  509. };
  510. export const batchQrSubmit = async (data: QrPickBatchSubmitRequest) => {
  511. const response = await serverFetchJson<PostPickOrderResponse<QrPickBatchSubmitRequest>>(
  512. `${BASE_API_URL}/stockOutLine/batchQrSubmit`,
  513. {
  514. method: "POST",
  515. body: JSON.stringify(data),
  516. },
  517. );
  518. return response;
  519. };
  520. export interface BatchScanRequest {
  521. userId: number;
  522. lines: BatchScanLineRequest[];
  523. }
  524. export interface BatchScanLineRequest {
  525. pickOrderLineId: number;
  526. inventoryLotLineId: number | null; // 如果有 lot,提供 lotId;如果没有则为 null
  527. pickOrderConsoCode: string;
  528. lotNo: string | null; // 用于日志和验证
  529. itemId: number;
  530. itemCode: string;
  531. stockOutLineId: number | null; // ✅ 新增:如果已有 stockOutLineId,直接使用
  532. }
  533. export const batchScan = async (data: BatchScanRequest) => {
  534. console.log("📤 batchScan - Request body:", JSON.stringify(data, null, 2));
  535. const response = await serverFetchJson<PostPickOrderResponse<BatchScanRequest>>(
  536. `${BASE_API_URL}/stockOutLine/batchScan`,
  537. {
  538. method: "POST",
  539. body: JSON.stringify(data),
  540. headers: {
  541. "Content-Type": "application/json",
  542. },
  543. },
  544. );
  545. console.log("📥 batchScan - Response:", response);
  546. return response;
  547. };
  548. export const fetchDoPickOrderDetail = async (
  549. doPickOrderId: number,
  550. selectedPickOrderId?: number
  551. ): Promise<DoPickOrderDetail> => {
  552. const url = selectedPickOrderId
  553. ? `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}?selectedPickOrderId=${selectedPickOrderId}`
  554. : `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}`;
  555. const response = await serverFetchJson<DoPickOrderDetail>(url, {
  556. method: "GET",
  557. });
  558. return response;
  559. };
  560. export const updatePickExecutionIssueStatus = async (
  561. data: UpdatePickExecutionIssueRequest
  562. ): Promise<PostPickOrderResponse> => {
  563. const result = await serverFetchJson<PostPickOrderResponse>(
  564. `${BASE_API_URL}/pickExecution/updateIssueStatus`,
  565. {
  566. method: "POST",
  567. body: JSON.stringify(data),
  568. headers: { "Content-Type": "application/json" },
  569. }
  570. );
  571. revalidateTag("pickExecutionIssues");
  572. return result;
  573. };
  574. export async function fetchStoreLaneSummary(storeId: string, requiredDate?: string, releaseType?: string): Promise<StoreLaneSummary> {
  575. const dateToUse = requiredDate || dayjs().format('YYYY-MM-DD');
  576. const url = `${BASE_API_URL}/doPickOrder/summary-by-store?storeId=${encodeURIComponent(storeId)}&requiredDate=${encodeURIComponent(dateToUse)}&releaseType=${encodeURIComponent(releaseType || 'all')}`;
  577. const label = `[API] fetchStoreLaneSummary ${storeId}`;
  578. console.time(label);
  579. try {
  580. const response = await serverFetchJson<StoreLaneSummary>(url, {
  581. method: "GET",
  582. cache: "no-store",
  583. next: { revalidate: 0 },
  584. });
  585. console.timeEnd(label);
  586. return response;
  587. } catch (error) {
  588. console.error(`[API] Error in fetchStoreLaneSummary ${storeId}:`, error);
  589. throw error;
  590. }
  591. }
  592. // 按车道分配订单
  593. export async function assignByLane(
  594. userId: number,
  595. storeId: string,
  596. truckLanceCode: string,
  597. truckDepartureTime?: string,
  598. requiredDate?: string
  599. ): Promise<any> {
  600. const response = await serverFetchJson(
  601. `${BASE_API_URL}/doPickOrder/assign-by-lane`,
  602. {
  603. method: "POST",
  604. headers: {
  605. "Content-Type": "application/json",
  606. },
  607. body: JSON.stringify({
  608. userId,
  609. storeId,
  610. truckLanceCode,
  611. truckDepartureTime,
  612. requiredDate,
  613. }),
  614. }
  615. );
  616. return response;
  617. }
  618. // 新增:获取已完成的 DO Pick Orders API
  619. export const fetchCompletedDoPickOrders = async (
  620. userId: number,
  621. searchParams?: CompletedDoPickOrderSearchParams
  622. ): Promise<CompletedDoPickOrderResponse[]> => {
  623. const params = new URLSearchParams();
  624. if (searchParams?.deliveryNoteCode) {
  625. params.append('deliveryNoteCode', searchParams.deliveryNoteCode);
  626. }
  627. if (searchParams?.shopName) {
  628. params.append('shopName', searchParams.shopName);
  629. }
  630. if (searchParams?.targetDate) {
  631. params.append('targetDate', searchParams.targetDate);
  632. }
  633. if (searchParams?.truckLanceCode) {
  634. params.append("truckLanceCode", searchParams.truckLanceCode);
  635. }
  636. const queryString = params.toString();
  637. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders/${userId}${queryString ? `?${queryString}` : ''}`;
  638. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  639. method: "GET",
  640. });
  641. return response;
  642. };
  643. /** 全部已完成 DO 提貨記錄(不限經手人),需後端 `/completed-do-pick-orders-all` */
  644. export const fetchCompletedDoPickOrdersAll = async (
  645. searchParams?: CompletedDoPickOrderSearchParams
  646. ): Promise<CompletedDoPickOrderResponse[]> => {
  647. const params = new URLSearchParams();
  648. if (searchParams?.deliveryNoteCode) {
  649. params.append("deliveryNoteCode", searchParams.deliveryNoteCode);
  650. }
  651. if (searchParams?.shopName) {
  652. params.append("shopName", searchParams.shopName);
  653. }
  654. if (searchParams?.targetDate) {
  655. params.append("targetDate", searchParams.targetDate);
  656. }
  657. if (searchParams?.truckLanceCode) {
  658. params.append("truckLanceCode", searchParams.truckLanceCode);
  659. }
  660. const queryString = params.toString();
  661. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders-all${queryString ? `?${queryString}` : ""}`;
  662. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  663. method: "GET",
  664. });
  665. return response;
  666. };
  667. /** 強制完成進行中的 do_pick_order(僅改狀態並歸檔,不調整揀貨數量) */
  668. export const forceCompleteDoPickOrder = async (
  669. doPickOrderId: number,
  670. ): Promise<PostPickOrderResponse> => {
  671. return serverFetchJson<PostPickOrderResponse>(
  672. `${BASE_API_URL}/doPickOrder/force-complete/${doPickOrderId}`,
  673. { method: "POST", headers: { "Content-Type": "application/json" } },
  674. );
  675. };
  676. /** 撤銷使用者領取,可再次分配 */
  677. export const revertDoPickOrderAssignment = async (
  678. doPickOrderId: number,
  679. ): Promise<PostPickOrderResponse> => {
  680. return serverFetchJson<PostPickOrderResponse>(
  681. `${BASE_API_URL}/doPickOrder/revert-assignment/${doPickOrderId}`,
  682. { method: "POST", headers: { "Content-Type": "application/json" } },
  683. );
  684. };
  685. export const updatePickOrderHideStatus = async (pickOrderId: number, hide: boolean) => {
  686. const response = await serverFetchJson<UpdateDoPickOrderHideStatusRequest>(
  687. `${BASE_API_URL}/pickOrder/update-hide-status/${pickOrderId}?hide=${hide}`,
  688. {
  689. method: "POST",
  690. headers: { "Content-Type": "application/json" },
  691. },
  692. );
  693. revalidateTag("pickorder");
  694. return response;
  695. };
  696. export const fetchFGPickOrders = async (pickOrderId: number) => {
  697. const response = await serverFetchJson<FGPickOrderResponse>(
  698. `${BASE_API_URL}/pickOrder/fg-pick-orders/${pickOrderId}`,
  699. {
  700. method: "GET",
  701. },
  702. );
  703. return response;
  704. };
  705. export const fetchFGPickOrdersByUserId = async (userId: number) => {
  706. const response = await serverFetchJson<FGPickOrderResponse[]>(
  707. `${BASE_API_URL}/pickOrder/fg-pick-orders/${userId}`,
  708. {
  709. method: "GET",
  710. },
  711. );
  712. return response;
  713. };
  714. export const updateSuggestedLotLineId = async (suggestedPickLotId: number, newLotLineId: number) => {
  715. const response = await serverFetchJson<PostPickOrderResponse<UpdateSuggestedLotLineIdRequest>>(
  716. `${BASE_API_URL}/suggestedPickLot/update-suggested-lot/${suggestedPickLotId}`,
  717. {
  718. method: "POST",
  719. body: JSON.stringify({ newLotLineId }),
  720. headers: { "Content-Type": "application/json" },
  721. },
  722. );
  723. revalidateTag("pickorder");
  724. return response;
  725. };
  726. export const autoAssignAndReleasePickOrder = async (userId: number): Promise<AutoAssignReleaseResponse> => {
  727. const response = await serverFetchJson<AutoAssignReleaseResponse>(
  728. `${BASE_API_URL}/pickOrder/auto-assign-release/${userId}`,
  729. {
  730. method: "POST",
  731. headers: { "Content-Type": "application/json" },
  732. },
  733. );
  734. revalidateTag("pickorder");
  735. return response;
  736. };
  737. export const autoAssignAndReleasePickOrderByStore = async (
  738. userId: number,
  739. storeId: string
  740. ): Promise<AutoAssignReleaseResponse> => {
  741. const url = `${BASE_API_URL}/pickOrder/auto-assign-release-by-store?userId=${userId}&storeId=${encodeURIComponent(storeId)}`;
  742. const response = await serverFetchJson<AutoAssignReleaseResponse>(url, {
  743. method: "POST",
  744. headers: { "Content-Type": "application/json" },
  745. // no body
  746. next: { tags: ["pickorder"] },
  747. });
  748. revalidateTag("pickorder");
  749. return response;
  750. };
  751. export const checkPickOrderCompletion = async (userId: number): Promise<PickOrderCompletionResponse> => {
  752. const response = await serverFetchJson<PickOrderCompletionResponse>(
  753. `${BASE_API_URL}/pickOrder/check-pick-completion/${userId}`,
  754. {
  755. method: "GET",
  756. headers: { "Content-Type": "application/json" },
  757. },
  758. );
  759. return response;
  760. };
  761. export const recordPickExecutionIssue = async (data: PickExecutionIssueData) => {
  762. const result = await serverFetchJson<PostPickOrderResponse>(
  763. `${BASE_API_URL}/pickExecution/recordIssue`,
  764. {
  765. method: "POST",
  766. body: JSON.stringify(data),
  767. headers: { "Content-Type": "application/json" },
  768. },
  769. );
  770. revalidateTag("pickorder");
  771. return result;
  772. };
  773. export const resuggestPickOrder = async (pickOrderId: number) => {
  774. console.log("Resuggesting pick order:", pickOrderId);
  775. const result = await serverFetchJson<PostPickOrderResponse>(
  776. `${BASE_API_URL}/suggestedPickLot/resuggest/${pickOrderId}`,
  777. {
  778. method: "POST",
  779. headers: { "Content-Type": "application/json" },
  780. },
  781. );
  782. revalidateTag("pickorder");
  783. return result;
  784. };
  785. export const updateStockOutLineStatus = async (data: {
  786. id: number;
  787. status: string;
  788. qty?: number;
  789. remarks?: string;
  790. }) => {
  791. console.log("Updating stock out line status:", data);
  792. const result = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  793. `${BASE_API_URL}/stockOutLine/updateStatus`,
  794. {
  795. method: "POST",
  796. body: JSON.stringify(data),
  797. headers: { "Content-Type": "application/json" },
  798. },
  799. );
  800. revalidateTag("pickorder");
  801. return result;
  802. };
  803. // Missing function 1: newassignPickOrder
  804. export const newassignPickOrder = async (data: AssignPickOrderInputs) => {
  805. const response = await serverFetchJson<PostPickOrderResponse>(
  806. `${BASE_API_URL}/pickOrder/assign`,
  807. {
  808. method: "POST",
  809. body: JSON.stringify(data),
  810. headers: { "Content-Type": "application/json" },
  811. },
  812. );
  813. revalidateTag("pickorder");
  814. return response;
  815. };
  816. // Missing function 2: releaseAssignedPickOrders
  817. export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => {
  818. const response = await serverFetchJson<PostPickOrderResponse>(
  819. `${BASE_API_URL}/pickOrder/release-assigned`,
  820. {
  821. method: "POST",
  822. body: JSON.stringify(data),
  823. headers: { "Content-Type": "application/json" },
  824. },
  825. );
  826. revalidateTag("pickorder");
  827. return response;
  828. };
  829. // Get latest group name and create it automatically
  830. export const getLatestGroupNameAndCreate = async () => {
  831. return serverFetchJson<PostPickOrderResponse>(
  832. `${BASE_API_URL}/pickOrder/groups/latest`,
  833. {
  834. method: "GET",
  835. next: { tags: ["pickorder"] },
  836. },
  837. );
  838. };
  839. // Get all groups
  840. export const fetchAllGroups = cache(async () => {
  841. return serverFetchJson<PickOrderGroupInfo[]>(
  842. `${BASE_API_URL}/pickOrder/groups/list`,
  843. {
  844. method: "GET",
  845. next: { tags: ["pickorder"] },
  846. },
  847. );
  848. });
  849. // Create or update groups (flexible - can handle both cases)
  850. export const createOrUpdateGroups = async (data: SavePickOrderGroupRequest) => {
  851. const response = await serverFetchJson<PostPickOrderResponse>(
  852. `${BASE_API_URL}/pickOrder/groups/create`,
  853. {
  854. method: "POST",
  855. body: JSON.stringify(data),
  856. headers: { "Content-Type": "application/json" },
  857. },
  858. );
  859. revalidateTag("pickorder");
  860. return response;
  861. };
  862. // Get groups by pick order ID
  863. export const fetchGroupsByPickOrderId = cache(async (pickOrderId: number) => {
  864. return serverFetchJson<PickOrderGroupInfo[]>(
  865. `${BASE_API_URL}/pickOrder/groups/${pickOrderId}`,
  866. {
  867. method: "GET",
  868. next: { tags: ["pickorder"] },
  869. },
  870. );
  871. });
  872. export const fetchPickOrderDetails = cache(async (ids: string) => {
  873. return serverFetchJson<GetPickOrderInfoResponse>(
  874. `${BASE_API_URL}/pickOrder/detail/${ids}`,
  875. {
  876. method: "GET",
  877. next: { tags: ["pickorder"] },
  878. },
  879. );
  880. });
  881. export interface PickOrderLotDetailResponse {
  882. lotId: number | null; // ✅ 改为可空
  883. lotNo: string | null; // ✅ 改为可空
  884. expiryDate: string | null; // ✅ 改为可空
  885. location: string | null; // ✅ 改为可空
  886. stockUnit: string | null;
  887. inQty: number | null;
  888. availableQty: number | null; // ✅ 改为可空
  889. requiredQty: number;
  890. actualPickQty: number;
  891. suggestedPickLotId: number | null;
  892. lotStatus: string | null;
  893. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable' | 'rejected';
  894. stockOutLineId: number | null; // ✅ 添加
  895. stockOutLineStatus: string | null; // ✅ 添加
  896. stockOutLineQty: number | null; // ✅ 添加
  897. totalPickedByAllPickOrders: number | null; // ✅ 添加
  898. remainingAfterAllPickOrders: number | null; // ✅ 添加
  899. noLot: boolean; // ✅ 关键:添加 noLot 字段
  900. outQty?: number; // ✅ 添加
  901. holdQty?: number; // ✅ 添加
  902. }
  903. interface ALLPickOrderLotDetailResponse {
  904. // Pick Order Information
  905. pickOrderId: number;
  906. pickOrderCode: string;
  907. pickOrderTargetDate: string;
  908. pickOrderType: string;
  909. pickOrderStatus: string;
  910. pickOrderAssignTo: number;
  911. groupName: string;
  912. // Pick Order Line Information
  913. pickOrderLineId: number;
  914. pickOrderLineRequiredQty: number;
  915. pickOrderLineStatus: string;
  916. // Item Information
  917. itemId: number;
  918. itemCode: string;
  919. itemName: string;
  920. uomCode: string;
  921. uomDesc: string;
  922. // Lot Information
  923. lotId: number;
  924. lotNo: string;
  925. expiryDate: string;
  926. location: string;
  927. outQty: number;
  928. holdQty: number;
  929. stockUnit: string;
  930. availableQty: number;
  931. requiredQty: number;
  932. actualPickQty: number;
  933. totalPickedByAllPickOrders: number;
  934. suggestedPickLotId: number;
  935. lotStatus: string;
  936. stockOutLineId?: number;
  937. stockOutLineStatus?: string;
  938. stockOutLineQty?: number;
  939. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected';
  940. processingStatus: string;
  941. }
  942. interface SuggestionWithStatus {
  943. suggestionId: number;
  944. suggestionQty: number;
  945. suggestionCreated: string;
  946. lotLineId: number;
  947. lotNo: string;
  948. expiryDate: string;
  949. location: string;
  950. stockOutLineId?: number;
  951. stockOutLineStatus?: string;
  952. stockOutLineQty?: number;
  953. suggestionStatus: 'active' | 'completed' | 'rejected' | 'in_progress' | 'unknown';
  954. }
  955. // 在 actions.ts 中修改接口定义
  956. export interface FGPickOrderHierarchicalResponse {
  957. fgInfo: {
  958. doPickOrderId: number;
  959. ticketNo: string;
  960. storeId: string;
  961. shopCode: string;
  962. shopName: string;
  963. truckLanceCode: string;
  964. departureTime: string;
  965. };
  966. pickOrders: Array<{
  967. pickOrderId: number;
  968. pickOrderCode: string;
  969. doOrderId: number;
  970. deliveryOrderCode: string;
  971. consoCode: string;
  972. status: string;
  973. targetDate: string;
  974. pickOrderLines: Array<{
  975. id: number;
  976. requiredQty: number;
  977. status: string;
  978. item: {
  979. id: number;
  980. code: string;
  981. name: string;
  982. uomCode: string;
  983. uomDesc: string;
  984. };
  985. lots: Array<any>; // 可以是空数组
  986. }>;
  987. }>;
  988. }
  989. export interface CheckCompleteResponse {
  990. id: number | null;
  991. name: string;
  992. code: string;
  993. type?: string;
  994. message: string | null;
  995. errorPosition: string;
  996. }
  997. export interface LotSubstitutionConfirmRequest {
  998. pickOrderLineId: number;
  999. stockOutLineId: number;
  1000. originalSuggestedPickLotId: number;
  1001. newInventoryLotNo: string;
  1002. newStockInLineId: number;
  1003. }
  1004. export const confirmLotSubstitution = async (data: LotSubstitutionConfirmRequest) => {
  1005. const response = await serverFetchJson<PostPickOrderResponse>(
  1006. `${BASE_API_URL}/pickOrder/lot-substitution/confirm`,
  1007. {
  1008. method: "POST",
  1009. body: JSON.stringify(data),
  1010. headers: { "Content-Type": "application/json" },
  1011. },
  1012. );
  1013. revalidateTag("pickorder");
  1014. return response;
  1015. };
  1016. export const checkAndCompletePickOrderByConsoCode = async (consoCode: string): Promise<CheckCompleteResponse> => {
  1017. const response = await serverFetchJson<CheckCompleteResponse>(
  1018. `${BASE_API_URL}/pickOrder/check-complete/${consoCode}`,
  1019. {
  1020. method: "POST",
  1021. headers: {
  1022. "Content-Type": "application/json",
  1023. },
  1024. },
  1025. );
  1026. revalidateTag("pickorder");
  1027. return response;
  1028. };
  1029. export const fetchPickOrderDetailsOptimized = cache(async (userId?: number) => {
  1030. const url = userId
  1031. ? `${BASE_API_URL}/pickOrder/detail-optimized?userId=${userId}`
  1032. : `${BASE_API_URL}/pickOrder/detail-optimized`;
  1033. return serverFetchJson<any[]>(
  1034. url,
  1035. {
  1036. method: "GET",
  1037. next: { tags: ["pickorder"] },
  1038. },
  1039. );
  1040. });
  1041. const fetchSuggestionsWithStatus = async (pickOrderLineId: number) => {
  1042. try {
  1043. const response = await fetch(`/api/suggestedPickLot/suggestions-with-status/${pickOrderLineId}`);
  1044. const suggestions: SuggestionWithStatus[] = await response.json();
  1045. return suggestions;
  1046. } catch (error) {
  1047. console.error('Error fetching suggestions with status:', error);
  1048. return [];
  1049. }
  1050. };
  1051. export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): Promise<any> => {
  1052. try {
  1053. console.log("🔍 Fetching hierarchical pick order lots for userId:", userId);
  1054. const data = await serverFetchJson<any>(
  1055. `${BASE_API_URL}/pickOrder/all-lots-hierarchical/${userId}`,
  1056. {
  1057. method: 'GET',
  1058. next: { tags: ["pickorder"] },
  1059. }
  1060. );
  1061. console.log(" Fetched hierarchical lot details:", data);
  1062. return data;
  1063. } catch (error) {
  1064. console.error("❌ Error fetching hierarchical lot details:", error);
  1065. return {
  1066. pickOrder: null,
  1067. pickOrderLines: []
  1068. };
  1069. }
  1070. });
  1071. export const fetchLotDetailsByDoPickOrderRecordId = async (doPickOrderRecordId: number): Promise<{
  1072. fgInfo: any;
  1073. pickOrders: any[];
  1074. }> => {
  1075. try {
  1076. console.log("🔍 Fetching lot details for doPickOrderRecordId:", doPickOrderRecordId);
  1077. const data = await serverFetchJson<{
  1078. fgInfo: any;
  1079. pickOrders: any[];
  1080. }>(
  1081. `${BASE_API_URL}/pickOrder/lot-details-by-do-pick-order-record/${doPickOrderRecordId}`,
  1082. {
  1083. method: 'GET',
  1084. next: { tags: ["pickorder"] },
  1085. }
  1086. );
  1087. console.log(" Fetched hierarchical lot details:", data);
  1088. return data;
  1089. } catch (error) {
  1090. console.error("❌ Error fetching lot details:", error);
  1091. return {
  1092. fgInfo: null,
  1093. pickOrders: []
  1094. };
  1095. }
  1096. };
  1097. export const fetchAllPickOrderDetails = cache(async (userId?: number) => {
  1098. if (!userId) {
  1099. return {
  1100. consoCode: null,
  1101. pickOrders: [],
  1102. items: []
  1103. };
  1104. }
  1105. // Use the correct endpoint with userId in the path
  1106. const url = `${BASE_API_URL}/pickOrder/detail-optimized/${userId}`;
  1107. return serverFetchJson<GetPickOrderInfoResponse>(
  1108. url,
  1109. {
  1110. method: "GET",
  1111. next: { tags: ["pickorder"] },
  1112. },
  1113. );
  1114. });
  1115. export const fetchPickOrderLineLotDetails = cache(async (pickOrderLineId: number) => {
  1116. return serverFetchJson<PickOrderLotDetailResponse[]>(
  1117. `${BASE_API_URL}/pickOrder/lot-details/${pickOrderLineId}`,
  1118. {
  1119. method: "GET",
  1120. next: { tags: ["pickorder"] },
  1121. },
  1122. );
  1123. });
  1124. export const createPickOrder = async (data: SavePickOrderRequest) => {
  1125. console.log(data);
  1126. const po = await serverFetchJson<PostPickOrderResponse>(
  1127. `${BASE_API_URL}/pickOrder/create`,
  1128. {
  1129. method: "POST",
  1130. body: JSON.stringify(data),
  1131. headers: { "Content-Type": "application/json" },
  1132. },
  1133. );
  1134. revalidateTag("pickorder");
  1135. return po;
  1136. }
  1137. export const assignPickOrder = async (ids: number[]) => {
  1138. const pickOrder = await serverFetchJson<any>(
  1139. `${BASE_API_URL}/pickOrder/conso`,
  1140. {
  1141. method: "POST",
  1142. body: JSON.stringify({ ids: ids }),
  1143. headers: { "Content-Type": "application/json" },
  1144. },
  1145. );
  1146. // revalidateTag("po");
  1147. return pickOrder;
  1148. };
  1149. export const consolidatePickOrder = async (ids: number[]) => {
  1150. const pickOrder = await serverFetchJson<any>(
  1151. `${BASE_API_URL}/pickOrder/conso`,
  1152. {
  1153. method: "POST",
  1154. body: JSON.stringify({ ids: ids }),
  1155. headers: { "Content-Type": "application/json" },
  1156. },
  1157. );
  1158. return pickOrder;
  1159. };
  1160. export const consolidatePickOrder_revert = async (ids: number[]) => {
  1161. const pickOrder = await serverFetchJson<any>(
  1162. `${BASE_API_URL}/pickOrder/deconso`,
  1163. {
  1164. method: "POST",
  1165. body: JSON.stringify({ ids: ids }),
  1166. headers: { "Content-Type": "application/json" },
  1167. },
  1168. );
  1169. // revalidateTag("po");
  1170. return pickOrder;
  1171. };
  1172. export const fetchPickOrderClient = cache(
  1173. async (queryParams?: Record<string, any>) => {
  1174. if (queryParams) {
  1175. const queryString = new URLSearchParams(queryParams).toString();
  1176. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1177. `${BASE_API_URL}/pickOrder/getRecordByPage?${queryString}`,
  1178. {
  1179. method: "GET",
  1180. next: { tags: ["pickorder"] },
  1181. },
  1182. );
  1183. } else {
  1184. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1185. `${BASE_API_URL}/pickOrder/getRecordByPage`,
  1186. {
  1187. method: "GET",
  1188. next: { tags: ["pickorder"] },
  1189. },
  1190. );
  1191. }
  1192. },
  1193. );
  1194. export const fetchPickOrderWithStockClient = cache(
  1195. async (queryParams?: Record<string, any>) => {
  1196. if (queryParams) {
  1197. const queryString = new URLSearchParams(queryParams).toString();
  1198. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1199. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock?${queryString}`,
  1200. {
  1201. method: "GET",
  1202. next: { tags: ["pickorder"] },
  1203. },
  1204. );
  1205. } else {
  1206. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1207. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock`,
  1208. {
  1209. method: "GET",
  1210. next: { tags: ["pickorder"] },
  1211. },
  1212. );
  1213. }
  1214. },
  1215. );
  1216. export const fetchConsoPickOrderClient = cache(
  1217. async (queryParams?: Record<string, any>) => {
  1218. if (queryParams) {
  1219. const queryString = new URLSearchParams(queryParams).toString();
  1220. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1221. `${BASE_API_URL}/pickOrder/getRecordByPage-conso?${queryString}`,
  1222. {
  1223. method: "GET",
  1224. next: { tags: ["pickorder"] },
  1225. },
  1226. );
  1227. } else {
  1228. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1229. `${BASE_API_URL}/pickOrder/getRecordByPage-conso`,
  1230. {
  1231. method: "GET",
  1232. next: { tags: ["pickorder"] },
  1233. },
  1234. );
  1235. }
  1236. },
  1237. );
  1238. export const fetchPickOrderLineClient = cache(
  1239. async (queryParams?: Record<string, any>) => {
  1240. if (queryParams) {
  1241. const queryString = new URLSearchParams(queryParams).toString();
  1242. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1243. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`,
  1244. {
  1245. method: "GET",
  1246. next: { tags: ["pickorder"] },
  1247. },
  1248. );
  1249. } else {
  1250. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1251. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage`,
  1252. {
  1253. method: "GET",
  1254. next: { tags: ["pickorder"] },
  1255. },
  1256. );
  1257. }
  1258. },
  1259. );
  1260. export const fetchStockOutLineClient = cache(
  1261. async (pickOrderLineId: number) => {
  1262. return serverFetchJson<StockOutLine[]>(
  1263. `${BASE_API_URL}/stockOutLine/getByPickOrderLineId/${pickOrderLineId}`,
  1264. {
  1265. method: "GET",
  1266. next: { tags: ["pickorder"] },
  1267. },
  1268. );
  1269. },
  1270. );
  1271. export const fetchConsoDetail = cache(async (consoCode: string) => {
  1272. return serverFetchJson<PreReleasePickOrderSummary>(
  1273. `${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`,
  1274. {
  1275. method: "GET",
  1276. next: { tags: ["pickorder"] },
  1277. },
  1278. );
  1279. });
  1280. export const releasePickOrder = async (data: ReleasePickOrderInputs) => {
  1281. console.log(data);
  1282. console.log(JSON.stringify(data));
  1283. const po = await serverFetchJson<{ consoCode: string }>(
  1284. `${BASE_API_URL}/pickOrder/releaseConso`,
  1285. {
  1286. method: "POST",
  1287. body: JSON.stringify(data),
  1288. headers: { "Content-Type": "application/json" },
  1289. },
  1290. );
  1291. revalidateTag("pickorder");
  1292. return po;
  1293. };
  1294. export const createStockOutLine = async (data: CreateStockOutLine) => {
  1295. console.log("triggering");
  1296. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1297. `${BASE_API_URL}/stockOutLine/create`,
  1298. {
  1299. method: "POST",
  1300. body: JSON.stringify(data),
  1301. headers: { "Content-Type": "application/json" },
  1302. },
  1303. );
  1304. revalidateTag("pickorder");
  1305. return po;
  1306. };
  1307. export const updateStockOutLine = async (data: UpdateStockOutLine) => {
  1308. console.log(data);
  1309. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1310. `${BASE_API_URL}/stockOutLine/update`,
  1311. {
  1312. method: "POST",
  1313. body: JSON.stringify(data),
  1314. headers: { "Content-Type": "application/json" },
  1315. },
  1316. );
  1317. revalidateTag("pickorder");
  1318. return po;
  1319. };
  1320. export const completeConsoPickOrder = async (consoCode: string) => {
  1321. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1322. `${BASE_API_URL}/pickOrder/consoPickOrder/complete/${consoCode}`,
  1323. {
  1324. method: "POST",
  1325. headers: { "Content-Type": "application/json" },
  1326. },
  1327. );
  1328. revalidateTag("pickorder");
  1329. return po;
  1330. };
  1331. export const fetchConsoStatus = cache(async (consoCode: string) => {
  1332. return serverFetchJson<{ status: string }>(
  1333. `${BASE_API_URL}/stockOut/get-status/${consoCode}`,
  1334. {
  1335. method: "GET",
  1336. next: { tags: ["pickorder"] },
  1337. },
  1338. );
  1339. });
  1340. export interface ReleasedDoPickOrderResponse {
  1341. id: number;
  1342. storeId: string;
  1343. ticketNo: string;
  1344. pickOrderId: number;
  1345. ticketStatus: string;
  1346. doOrderId: number;
  1347. shopId: number;
  1348. handledBy: number;
  1349. ticketReleaseTime: string;
  1350. }
  1351. export const fetchReleasedDoPickOrders = async (): Promise<ReleasedDoPickOrderResponse[]> => {
  1352. const response = await serverFetchJson<ReleasedDoPickOrderResponse[]>(
  1353. `${BASE_API_URL}/doPickOrder/released`,
  1354. {
  1355. method: "GET",
  1356. },
  1357. );
  1358. return response;
  1359. };
  1360. // 新增:Released Do Pick Order 列表項目(對應後端 ReleasedDoPickOrderListItem)
  1361. export interface ReleasedDoPickOrderListItem {
  1362. id: number;
  1363. requiredDeliveryDate: string | null;
  1364. shopCode: string | null;
  1365. shopName: string | null;
  1366. storeId: string | null;
  1367. truckLanceCode: string | null;
  1368. truckDepartureTime: string | null;
  1369. deliveryOrderCodes: string[];
  1370. }
  1371. // 修改:fetchReleasedDoPickOrders 支援 shopName 篩選,並回傳新結構
  1372. export const fetchReleasedDoPickOrdersForSelection = async (
  1373. shopName?: string,
  1374. storeId?: string,
  1375. truck?: string
  1376. ): Promise<ReleasedDoPickOrderListItem[]> => {
  1377. const params = new URLSearchParams();
  1378. if (shopName?.trim()) params.append("shopName", shopName.trim());
  1379. if (storeId?.trim()) params.append("storeId", storeId.trim());
  1380. if (truck?.trim()) params.append("truck", truck.trim());
  1381. const query = params.toString();
  1382. const url = `${BASE_API_URL}/doPickOrder/released${query ? `?${query}` : ""}`;
  1383. const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, {
  1384. method: "GET",
  1385. });
  1386. return response ?? [];
  1387. };
  1388. export const fetchReleasedDoPickOrdersForSelectionToday = async (
  1389. shopName?: string,
  1390. storeId?: string,
  1391. truck?: string
  1392. ): Promise<ReleasedDoPickOrderListItem[]> => {
  1393. const params = new URLSearchParams();
  1394. if (shopName?.trim()) params.append("shopName", shopName.trim());
  1395. if (storeId?.trim()) params.append("storeId", storeId.trim());
  1396. if (truck?.trim()) params.append("truck", truck.trim());
  1397. const query = params.toString();
  1398. const url = `${BASE_API_URL}/doPickOrder/released-today${query ? `?${query}` : ""}`;
  1399. const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, {
  1400. method: "GET",
  1401. });
  1402. return response ?? [];
  1403. };
  1404. export const fetchReleasedDoPickOrderCountByStore = async (
  1405. storeId: string
  1406. ): Promise<number> => {
  1407. const list = await fetchReleasedDoPickOrdersForSelection(undefined, storeId);
  1408. return list.length;
  1409. };
  1410. // 新增:依 doPickOrderId 分配
  1411. export const assignByDoPickOrderId = async (
  1412. userId: number,
  1413. doPickOrderId: number
  1414. ): Promise<PostPickOrderResponse> => {
  1415. const response = await serverFetchJson<PostPickOrderResponse>(
  1416. `${BASE_API_URL}/doPickOrder/assign-by-id`,
  1417. {
  1418. method: "POST",
  1419. headers: { "Content-Type": "application/json" },
  1420. body: JSON.stringify({ userId, doPickOrderId }),
  1421. }
  1422. );
  1423. revalidateTag("pickorder");
  1424. return response;
  1425. };