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.
 
 

1593 lines
44 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. /** Legacy: do_pick_order_record.handler_name; workbench: delivery_order_pick_order.handlerName */
  357. handlerName?: string | null;
  358. }
  359. // 新增:搜索参数接口
  360. export interface CompletedDoPickOrderSearchParams {
  361. targetDate?: string;
  362. shopName?: string;
  363. deliveryNoteCode?: string;
  364. /** 卡車/車道(後端 truckLanceCode 模糊匹配) */
  365. truckLanceCode?: string;
  366. }
  367. export interface PickExecutionIssue {
  368. id: number;
  369. pickOrderId: number;
  370. pickOrderCode: string;
  371. pickOrderCreateDate: string;
  372. pickExecutionDate: string;
  373. pickOrderLineId: number;
  374. issueNo: string;
  375. joPickOrderId?: number;
  376. doPickOrderId?: number;
  377. issueCategory: string;
  378. itemId: number;
  379. itemCode: string;
  380. itemDescription: string;
  381. lotId?: number;
  382. lotNo?: string;
  383. storeLocation?: string;
  384. requiredQty: number;
  385. actualPickQty?: number;
  386. missQty?: number;
  387. badItemQty?: number;
  388. issueRemark?: string;
  389. pickerName?: string;
  390. handleStatus: string;
  391. handleDate?: string;
  392. handledBy?: string;
  393. created: string;
  394. createdBy: string;
  395. modified: string;
  396. modifiedBy: string;
  397. }
  398. export interface FetchPickExecutionIssuesParams {
  399. type?: "jo" | "do" | "material";
  400. }
  401. export const fetchAllPickExecutionIssues = cache(
  402. async (params?: FetchPickExecutionIssuesParams): Promise<PickExecutionIssue[]> => {
  403. const queryParams = new URLSearchParams();
  404. if (params?.type) {
  405. queryParams.append("type", params.type);
  406. }
  407. const url = `${BASE_API_URL}/pickExecution/issues/all${
  408. queryParams.toString() ? `?${queryParams.toString()}` : ""
  409. }`;
  410. const result = await serverFetchJson<PickExecutionIssue[]>(url, {
  411. method: "GET",
  412. headers: { "Content-Type": "application/json" },
  413. });
  414. return result ?? [];
  415. }
  416. );
  417. export interface UpdatePickExecutionIssueRequest {
  418. issueId: number;
  419. handleStatus: string;
  420. handleDate?: string;
  421. handledBy?: string;
  422. handleRemark?: string;
  423. }
  424. export interface StoreLaneSummary {
  425. storeId: string;
  426. rows: LaneRow[];
  427. defaultTruckCount: number | null;
  428. }
  429. export interface LaneRow {
  430. truckDepartureTime: string;
  431. lanes: LaneBtn[];
  432. }
  433. export interface LaneBtn {
  434. truckLanceCode: string;
  435. loadingSequence?: number | null;
  436. unassigned: number;
  437. total: number;
  438. }
  439. export interface QrPickBatchSubmitRequest {
  440. userId: number;
  441. lines: QrPickSubmitLineRequest[];
  442. }
  443. export interface QrPickSubmitLineRequest {
  444. stockOutLineId: number;
  445. pickOrderLineId: number;
  446. inventoryLotLineId: number | null; // ✅ 修复:应该是 nullable
  447. requiredQty: number | null; // ✅ 修复:添加 requiredQty
  448. actualPickQty: number | null; // ✅ 修复:添加 actualPickQty
  449. stockOutLineStatus: string | null; // ✅ 修复:添加 stockOutLineStatus
  450. pickOrderConsoCode: string | null; // ✅ 修复:添加 pickOrderConsoCode
  451. noLot: boolean; // ✅ 修复:添加 noLot
  452. }
  453. export interface UpdateStockOutLineStatusByQRCodeAndLotNoRequest {
  454. pickOrderLineId: number,
  455. inventoryLotNo: string,
  456. stockInLineId?: number | null,
  457. stockOutLineId: number,
  458. itemId: number,
  459. status: string
  460. }
  461. export interface batchSubmitListRequest {
  462. userId: number;
  463. lines: batchSubmitListLineRequest[];
  464. }
  465. export interface batchSubmitListLineRequest {
  466. stockOutLineId: number; // 修复:改为 stockOutLineId(不是 stockInLineId)
  467. pickOrderLineId: number;
  468. inventoryLotLineId: number | null; // 添加:后端需要的字段
  469. requiredQty: number;
  470. actualPickQty: number;
  471. stockOutLineStatus: string;
  472. pickOrderConsoCode: string;
  473. noLot: boolean;
  474. // 移除:lotNo 和 stockInLineId(后端不需要)
  475. }
  476. export const batchSubmitList = async (data: batchSubmitListRequest) => {
  477. // ✅ 确保发送的是对象,不是数组
  478. const requestBody = Array.isArray(data) ? data[0] : data;
  479. console.log("📤 batchSubmitList - Request body type:", Array.isArray(requestBody) ? "array" : "object");
  480. console.log("📤 batchSubmitList - Request body:", JSON.stringify(requestBody, null, 2));
  481. const response = await serverFetchJson<PostPickOrderResponse<batchSubmitListRequest>>(
  482. `${BASE_API_URL}/stockOutLine/batchSubmitList`,
  483. {
  484. method: "POST",
  485. body: JSON.stringify(requestBody), // ✅ 确保是对象
  486. headers: {
  487. "Content-Type": "application/json", // ✅ 明确指定 Content-Type
  488. },
  489. },
  490. );
  491. return response;
  492. };
  493. export const updateStockOutLineStatusByQRCodeAndLotNo = async (data: UpdateStockOutLineStatusByQRCodeAndLotNoRequest) => {
  494. console.log(" Frontend: Calling updateStockOutLineStatusByQRCodeAndLotNo with data:", data);
  495. try {
  496. const response = await serverFetchJson<PostPickOrderResponse<UpdateStockOutLineStatusByQRCodeAndLotNoRequest>>(
  497. `${BASE_API_URL}/stockOutLine/updateStatusByQRCodeAndLotNo`,
  498. {
  499. method: "POST",
  500. headers: {
  501. "Content-Type": "application/json",
  502. },
  503. body: JSON.stringify(data),
  504. },
  505. );
  506. console.log("✅ Frontend: API call successful, response:", response);
  507. return response;
  508. } catch (error) {
  509. console.error("❌ Frontend: API call failed:", error);
  510. throw error;
  511. }
  512. };
  513. export const batchQrSubmit = async (data: QrPickBatchSubmitRequest) => {
  514. const response = await serverFetchJson<PostPickOrderResponse<QrPickBatchSubmitRequest>>(
  515. `${BASE_API_URL}/stockOutLine/batchQrSubmit`,
  516. {
  517. method: "POST",
  518. body: JSON.stringify(data),
  519. },
  520. );
  521. return response;
  522. };
  523. export interface BatchScanRequest {
  524. userId: number;
  525. lines: BatchScanLineRequest[];
  526. }
  527. export interface BatchScanLineRequest {
  528. pickOrderLineId: number;
  529. inventoryLotLineId: number | null; // 如果有 lot,提供 lotId;如果没有则为 null
  530. pickOrderConsoCode: string;
  531. lotNo: string | null; // 用于日志和验证
  532. itemId: number;
  533. itemCode: string;
  534. stockOutLineId: number | null; // ✅ 新增:如果已有 stockOutLineId,直接使用
  535. }
  536. export const batchScan = async (data: BatchScanRequest) => {
  537. console.log("📤 batchScan - Request body:", JSON.stringify(data, null, 2));
  538. const response = await serverFetchJson<PostPickOrderResponse<BatchScanRequest>>(
  539. `${BASE_API_URL}/stockOutLine/batchScan`,
  540. {
  541. method: "POST",
  542. body: JSON.stringify(data),
  543. headers: {
  544. "Content-Type": "application/json",
  545. },
  546. },
  547. );
  548. //console.log("📥 batchScan - Response:", response);
  549. return response;
  550. };
  551. export const fetchDoPickOrderDetail = async (
  552. doPickOrderId: number,
  553. selectedPickOrderId?: number
  554. ): Promise<DoPickOrderDetail> => {
  555. const url = selectedPickOrderId
  556. ? `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}?selectedPickOrderId=${selectedPickOrderId}`
  557. : `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}`;
  558. const response = await serverFetchJson<DoPickOrderDetail>(url, {
  559. method: "GET",
  560. });
  561. return response;
  562. };
  563. export const updatePickExecutionIssueStatus = async (
  564. data: UpdatePickExecutionIssueRequest
  565. ): Promise<PostPickOrderResponse> => {
  566. const result = await serverFetchJson<PostPickOrderResponse>(
  567. `${BASE_API_URL}/pickExecution/updateIssueStatus`,
  568. {
  569. method: "POST",
  570. body: JSON.stringify(data),
  571. headers: { "Content-Type": "application/json" },
  572. }
  573. );
  574. revalidateTag("pickExecutionIssues");
  575. return result;
  576. };
  577. export async function fetchStoreLaneSummary(storeId: string, requiredDate?: string, releaseType?: string): Promise<StoreLaneSummary> {
  578. const dateToUse = requiredDate || dayjs().format('YYYY-MM-DD');
  579. const url = `${BASE_API_URL}/doPickOrder/summary-by-store?storeId=${encodeURIComponent(storeId)}&requiredDate=${encodeURIComponent(dateToUse)}&releaseType=${encodeURIComponent(releaseType || 'all')}`;
  580. const label = `[API] fetchStoreLaneSummary ${storeId}`;
  581. console.time(label);
  582. try {
  583. const response = await serverFetchJson<StoreLaneSummary>(url, {
  584. method: "GET",
  585. cache: "no-store",
  586. next: { revalidate: 0 },
  587. });
  588. console.timeEnd(label);
  589. return response;
  590. } catch (error) {
  591. console.error(`[API] Error in fetchStoreLaneSummary ${storeId}:`, error);
  592. throw error;
  593. }
  594. }
  595. // 按车道分配订单
  596. export async function assignByLane(
  597. userId: number,
  598. storeId: string,
  599. truckLanceCode: string,
  600. truckDepartureTime?: string,
  601. loadingSequence?: number | null,
  602. requiredDate?: string
  603. ): Promise<any> {
  604. const response = await serverFetchJson(
  605. `${BASE_API_URL}/doPickOrder/assign-by-lane`,
  606. {
  607. method: "POST",
  608. headers: {
  609. "Content-Type": "application/json",
  610. },
  611. body: JSON.stringify({
  612. userId,
  613. storeId,
  614. truckLanceCode,
  615. truckDepartureTime,
  616. loadingSequence,
  617. requiredDate,
  618. }),
  619. }
  620. );
  621. return response;
  622. }
  623. // 新增:获取已完成的 DO Pick Orders API
  624. export const fetchCompletedDoPickOrders = async (
  625. userId: number,
  626. searchParams?: CompletedDoPickOrderSearchParams
  627. ): Promise<CompletedDoPickOrderResponse[]> => {
  628. const params = new URLSearchParams();
  629. if (searchParams?.deliveryNoteCode) {
  630. params.append('deliveryNoteCode', searchParams.deliveryNoteCode);
  631. }
  632. if (searchParams?.shopName) {
  633. params.append('shopName', searchParams.shopName);
  634. }
  635. if (searchParams?.targetDate) {
  636. params.append('targetDate', searchParams.targetDate);
  637. }
  638. if (searchParams?.truckLanceCode) {
  639. params.append("truckLanceCode", searchParams.truckLanceCode);
  640. }
  641. const queryString = params.toString();
  642. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders/${userId}${queryString ? `?${queryString}` : ''}`;
  643. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  644. method: "GET",
  645. });
  646. return response;
  647. };
  648. /** DO workbench: completed tickets from `delivery_order_pick_order.ticketStatus = completed`. **/
  649. /*
  650. export const fetchCompletedDoPickOrdersWorkbench = async (
  651. userId: number,
  652. searchParams?: CompletedDoPickOrderSearchParams,
  653. ): Promise<CompletedDoPickOrderResponse[]> => {
  654. const params = new URLSearchParams();
  655. if (searchParams?.deliveryNoteCode) {
  656. params.append("deliveryNoteCode", searchParams.deliveryNoteCode);
  657. }
  658. if (searchParams?.shopName) {
  659. params.append("shopName", searchParams.shopName);
  660. }
  661. if (searchParams?.targetDate) {
  662. params.append("targetDate", searchParams.targetDate);
  663. }
  664. if (searchParams?.truckLanceCode) {
  665. params.append("truckLanceCode", searchParams.truckLanceCode);
  666. }
  667. const queryString = params.toString();
  668. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders-workbench/${userId}${
  669. queryString ? `?${queryString}` : ""
  670. }`;
  671. return serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  672. method: "GET",
  673. });
  674. };
  675. */
  676. /** 全部已完成 DO 提貨記錄(不限經手人),需後端 `/completed-do-pick-orders-all` */
  677. export const fetchCompletedDoPickOrdersAll = async (
  678. searchParams?: CompletedDoPickOrderSearchParams
  679. ): Promise<CompletedDoPickOrderResponse[]> => {
  680. const params = new URLSearchParams();
  681. if (searchParams?.deliveryNoteCode) {
  682. params.append("deliveryNoteCode", searchParams.deliveryNoteCode);
  683. }
  684. if (searchParams?.shopName) {
  685. params.append("shopName", searchParams.shopName);
  686. }
  687. if (searchParams?.targetDate) {
  688. params.append("targetDate", searchParams.targetDate);
  689. }
  690. if (searchParams?.truckLanceCode) {
  691. params.append("truckLanceCode", searchParams.truckLanceCode);
  692. }
  693. const queryString = params.toString();
  694. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders-all${queryString ? `?${queryString}` : ""}`;
  695. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  696. method: "GET",
  697. });
  698. return response;
  699. };
  700. /** 強制完成進行中的 do_pick_order(僅改狀態並歸檔,不調整揀貨數量) */
  701. export const forceCompleteDoPickOrder = async (
  702. doPickOrderId: number,
  703. ): Promise<PostPickOrderResponse> => {
  704. return serverFetchJson<PostPickOrderResponse>(
  705. `${BASE_API_URL}/doPickOrder/force-complete/${doPickOrderId}`,
  706. { method: "POST", headers: { "Content-Type": "application/json" } },
  707. );
  708. };
  709. /** 撤銷使用者領取,可再次分配 */
  710. export const revertDoPickOrderAssignment = async (
  711. doPickOrderId: number,
  712. ): Promise<PostPickOrderResponse> => {
  713. return serverFetchJson<PostPickOrderResponse>(
  714. `${BASE_API_URL}/doPickOrder/revert-assignment/${doPickOrderId}`,
  715. { method: "POST", headers: { "Content-Type": "application/json" } },
  716. );
  717. };
  718. export const updatePickOrderHideStatus = async (pickOrderId: number, hide: boolean) => {
  719. const response = await serverFetchJson<UpdateDoPickOrderHideStatusRequest>(
  720. `${BASE_API_URL}/pickOrder/update-hide-status/${pickOrderId}?hide=${hide}`,
  721. {
  722. method: "POST",
  723. headers: { "Content-Type": "application/json" },
  724. },
  725. );
  726. revalidateTag("pickorder");
  727. return response;
  728. };
  729. export const fetchFGPickOrders = async (pickOrderId: number) => {
  730. const response = await serverFetchJson<FGPickOrderResponse>(
  731. `${BASE_API_URL}/pickOrder/fg-pick-orders/${pickOrderId}`,
  732. {
  733. method: "GET",
  734. },
  735. );
  736. return response;
  737. };
  738. export const fetchFGPickOrdersByUserId = async (userId: number) => {
  739. const response = await serverFetchJson<FGPickOrderResponse[]>(
  740. `${BASE_API_URL}/pickOrder/fg-pick-orders/${userId}`,
  741. {
  742. method: "GET",
  743. },
  744. );
  745. return response;
  746. };
  747. /** DO workbench: FG headers from `delivery_order_pick_order`, not `do_pick_order_line`. */
  748. /*
  749. export const fetchFGPickOrdersByUserIdWorkbench = async (userId: number) => {
  750. return serverFetchJson<FGPickOrderResponse[]>(
  751. `${BASE_API_URL}/pickOrder/fg-pick-orders-workbench/${userId}`,
  752. {
  753. method: "GET",
  754. // Must be fresh: determines whether shell shows Floor/Lane panel or Detail.
  755. cache: "no-store",
  756. next: { revalidate: 0 },
  757. },
  758. );
  759. };
  760. */
  761. export const updateSuggestedLotLineId = async (suggestedPickLotId: number, newLotLineId: number) => {
  762. const response = await serverFetchJson<PostPickOrderResponse<UpdateSuggestedLotLineIdRequest>>(
  763. `${BASE_API_URL}/suggestedPickLot/update-suggested-lot/${suggestedPickLotId}`,
  764. {
  765. method: "POST",
  766. body: JSON.stringify({ newLotLineId }),
  767. headers: { "Content-Type": "application/json" },
  768. },
  769. );
  770. revalidateTag("pickorder");
  771. return response;
  772. };
  773. export const autoAssignAndReleasePickOrder = async (userId: number): Promise<AutoAssignReleaseResponse> => {
  774. const response = await serverFetchJson<AutoAssignReleaseResponse>(
  775. `${BASE_API_URL}/pickOrder/auto-assign-release/${userId}`,
  776. {
  777. method: "POST",
  778. headers: { "Content-Type": "application/json" },
  779. },
  780. );
  781. revalidateTag("pickorder");
  782. return response;
  783. };
  784. export const autoAssignAndReleasePickOrderByStore = async (
  785. userId: number,
  786. storeId: string
  787. ): Promise<AutoAssignReleaseResponse> => {
  788. const url = `${BASE_API_URL}/pickOrder/auto-assign-release-by-store?userId=${userId}&storeId=${encodeURIComponent(storeId)}`;
  789. const response = await serverFetchJson<AutoAssignReleaseResponse>(url, {
  790. method: "POST",
  791. headers: { "Content-Type": "application/json" },
  792. // no body
  793. next: { tags: ["pickorder"] },
  794. });
  795. revalidateTag("pickorder");
  796. return response;
  797. };
  798. export const checkPickOrderCompletion = async (userId: number): Promise<PickOrderCompletionResponse> => {
  799. const response = await serverFetchJson<PickOrderCompletionResponse>(
  800. `${BASE_API_URL}/pickOrder/check-pick-completion/${userId}`,
  801. {
  802. method: "GET",
  803. headers: { "Content-Type": "application/json" },
  804. },
  805. );
  806. return response;
  807. };
  808. export const recordPickExecutionIssue = async (data: PickExecutionIssueData) => {
  809. const result = await serverFetchJson<PostPickOrderResponse>(
  810. `${BASE_API_URL}/pickExecution/recordIssue`,
  811. {
  812. method: "POST",
  813. body: JSON.stringify(data),
  814. headers: { "Content-Type": "application/json" },
  815. },
  816. );
  817. revalidateTag("pickorder");
  818. return result;
  819. };
  820. export const resuggestPickOrder = async (pickOrderId: number) => {
  821. console.log("Resuggesting pick order:", pickOrderId);
  822. const result = await serverFetchJson<PostPickOrderResponse>(
  823. `${BASE_API_URL}/suggestedPickLot/resuggest/${pickOrderId}`,
  824. {
  825. method: "POST",
  826. headers: { "Content-Type": "application/json" },
  827. },
  828. );
  829. revalidateTag("pickorder");
  830. return result;
  831. };
  832. export const updateStockOutLineStatus = async (data: {
  833. id: number;
  834. status: string;
  835. qty?: number;
  836. remarks?: string;
  837. }) => {
  838. console.log("Updating stock out line status:", data);
  839. const result = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  840. `${BASE_API_URL}/stockOutLine/updateStatus`,
  841. {
  842. method: "POST",
  843. body: JSON.stringify(data),
  844. headers: { "Content-Type": "application/json" },
  845. },
  846. );
  847. revalidateTag("pickorder");
  848. return result;
  849. };
  850. // Missing function 1: newassignPickOrder
  851. export const newassignPickOrder = async (data: AssignPickOrderInputs) => {
  852. const response = await serverFetchJson<PostPickOrderResponse>(
  853. `${BASE_API_URL}/pickOrder/assign`,
  854. {
  855. method: "POST",
  856. body: JSON.stringify(data),
  857. headers: { "Content-Type": "application/json" },
  858. },
  859. );
  860. revalidateTag("pickorder");
  861. return response;
  862. };
  863. // Missing function 2: releaseAssignedPickOrders
  864. export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => {
  865. const response = await serverFetchJson<PostPickOrderResponse>(
  866. `${BASE_API_URL}/pickOrder/release-assigned`,
  867. {
  868. method: "POST",
  869. body: JSON.stringify(data),
  870. headers: { "Content-Type": "application/json" },
  871. },
  872. );
  873. revalidateTag("pickorder");
  874. return response;
  875. };
  876. // Get latest group name and create it automatically
  877. export const getLatestGroupNameAndCreate = async () => {
  878. return serverFetchJson<PostPickOrderResponse>(
  879. `${BASE_API_URL}/pickOrder/groups/latest`,
  880. {
  881. method: "GET",
  882. next: { tags: ["pickorder"] },
  883. },
  884. );
  885. };
  886. // Get all groups
  887. export const fetchAllGroups = cache(async () => {
  888. return serverFetchJson<PickOrderGroupInfo[]>(
  889. `${BASE_API_URL}/pickOrder/groups/list`,
  890. {
  891. method: "GET",
  892. next: { tags: ["pickorder"] },
  893. },
  894. );
  895. });
  896. // Create or update groups (flexible - can handle both cases)
  897. export const createOrUpdateGroups = async (data: SavePickOrderGroupRequest) => {
  898. const response = await serverFetchJson<PostPickOrderResponse>(
  899. `${BASE_API_URL}/pickOrder/groups/create`,
  900. {
  901. method: "POST",
  902. body: JSON.stringify(data),
  903. headers: { "Content-Type": "application/json" },
  904. },
  905. );
  906. revalidateTag("pickorder");
  907. return response;
  908. };
  909. // Get groups by pick order ID
  910. export const fetchGroupsByPickOrderId = cache(async (pickOrderId: number) => {
  911. return serverFetchJson<PickOrderGroupInfo[]>(
  912. `${BASE_API_URL}/pickOrder/groups/${pickOrderId}`,
  913. {
  914. method: "GET",
  915. next: { tags: ["pickorder"] },
  916. },
  917. );
  918. });
  919. export const fetchPickOrderDetails = cache(async (ids: string) => {
  920. return serverFetchJson<GetPickOrderInfoResponse>(
  921. `${BASE_API_URL}/pickOrder/detail/${ids}`,
  922. {
  923. method: "GET",
  924. next: { tags: ["pickorder"] },
  925. },
  926. );
  927. });
  928. export interface PickOrderLotDetailResponse {
  929. lotId: number | null; // ✅ 改为可空
  930. lotNo: string | null; // ✅ 改为可空
  931. expiryDate: string | null; // ✅ 改为可空
  932. location: string | null; // ✅ 改为可空
  933. stockUnit: string | null;
  934. inQty: number | null;
  935. availableQty: number | null; // ✅ 改为可空
  936. requiredQty: number;
  937. actualPickQty: number;
  938. suggestedPickLotId: number | null;
  939. lotStatus: string | null;
  940. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable' | 'rejected';
  941. stockOutLineId: number | null; // ✅ 添加
  942. stockOutLineStatus: string | null; // ✅ 添加
  943. stockOutLineQty: number | null; // ✅ 添加
  944. totalPickedByAllPickOrders: number | null; // ✅ 添加
  945. remainingAfterAllPickOrders: number | null; // ✅ 添加
  946. noLot: boolean; // ✅ 关键:添加 noLot 字段
  947. outQty?: number; // ✅ 添加
  948. holdQty?: number; // ✅ 添加
  949. }
  950. interface ALLPickOrderLotDetailResponse {
  951. // Pick Order Information
  952. pickOrderId: number;
  953. pickOrderCode: string;
  954. pickOrderTargetDate: string;
  955. pickOrderType: string;
  956. pickOrderStatus: string;
  957. pickOrderAssignTo: number;
  958. groupName: string;
  959. // Pick Order Line Information
  960. pickOrderLineId: number;
  961. pickOrderLineRequiredQty: number;
  962. pickOrderLineStatus: string;
  963. // Item Information
  964. itemId: number;
  965. itemCode: string;
  966. itemName: string;
  967. uomCode: string;
  968. uomDesc: string;
  969. // Lot Information
  970. lotId: number;
  971. lotNo: string;
  972. expiryDate: string;
  973. location: string;
  974. outQty: number;
  975. holdQty: number;
  976. stockUnit: string;
  977. availableQty: number;
  978. requiredQty: number;
  979. actualPickQty: number;
  980. totalPickedByAllPickOrders: number;
  981. suggestedPickLotId: number;
  982. lotStatus: string;
  983. stockOutLineId?: number;
  984. stockOutLineStatus?: string;
  985. stockOutLineQty?: number;
  986. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected';
  987. processingStatus: string;
  988. }
  989. interface SuggestionWithStatus {
  990. suggestionId: number;
  991. suggestionQty: number;
  992. suggestionCreated: string;
  993. lotLineId: number;
  994. lotNo: string;
  995. expiryDate: string;
  996. location: string;
  997. stockOutLineId?: number;
  998. stockOutLineStatus?: string;
  999. stockOutLineQty?: number;
  1000. suggestionStatus: 'active' | 'completed' | 'rejected' | 'in_progress' | 'unknown';
  1001. }
  1002. // 在 actions.ts 中修改接口定义
  1003. export interface FGPickOrderHierarchicalResponse {
  1004. fgInfo: {
  1005. doPickOrderId: number;
  1006. ticketNo: string;
  1007. storeId: string;
  1008. shopCode: string;
  1009. shopName: string;
  1010. truckLanceCode: string;
  1011. departureTime: string;
  1012. };
  1013. pickOrders: Array<{
  1014. pickOrderId: number;
  1015. pickOrderCode: string;
  1016. doOrderId: number;
  1017. deliveryOrderCode: string;
  1018. consoCode: string;
  1019. status: string;
  1020. targetDate: string;
  1021. pickOrderLines: Array<{
  1022. id: number;
  1023. requiredQty: number;
  1024. status: string;
  1025. item: {
  1026. id: number;
  1027. code: string;
  1028. name: string;
  1029. uomCode: string;
  1030. uomDesc: string;
  1031. };
  1032. lots: Array<any>; // 可以是空数组
  1033. }>;
  1034. }>;
  1035. }
  1036. export interface CheckCompleteResponse {
  1037. id: number | null;
  1038. name: string;
  1039. code: string;
  1040. type?: string;
  1041. message: string | null;
  1042. errorPosition: string;
  1043. }
  1044. export interface LotSubstitutionConfirmRequest {
  1045. pickOrderLineId: number;
  1046. stockOutLineId: number;
  1047. originalSuggestedPickLotId: number;
  1048. newInventoryLotNo: string;
  1049. newStockInLineId: number;
  1050. }
  1051. export const confirmLotSubstitution = async (data: LotSubstitutionConfirmRequest) => {
  1052. const response = await serverFetchJson<PostPickOrderResponse>(
  1053. `${BASE_API_URL}/pickOrder/lot-substitution/confirm`,
  1054. {
  1055. method: "POST",
  1056. body: JSON.stringify(data),
  1057. headers: { "Content-Type": "application/json" },
  1058. },
  1059. );
  1060. revalidateTag("pickorder");
  1061. return response;
  1062. };
  1063. export const checkAndCompletePickOrderByConsoCode = async (consoCode: string): Promise<CheckCompleteResponse> => {
  1064. const response = await serverFetchJson<CheckCompleteResponse>(
  1065. `${BASE_API_URL}/pickOrder/check-complete/${consoCode}`,
  1066. {
  1067. method: "POST",
  1068. headers: {
  1069. "Content-Type": "application/json",
  1070. },
  1071. },
  1072. );
  1073. revalidateTag("pickorder");
  1074. return response;
  1075. };
  1076. export const fetchPickOrderDetailsOptimized = cache(async (userId?: number) => {
  1077. const url = userId
  1078. ? `${BASE_API_URL}/pickOrder/detail-optimized?userId=${userId}`
  1079. : `${BASE_API_URL}/pickOrder/detail-optimized`;
  1080. return serverFetchJson<any[]>(
  1081. url,
  1082. {
  1083. method: "GET",
  1084. next: { tags: ["pickorder"] },
  1085. },
  1086. );
  1087. });
  1088. const fetchSuggestionsWithStatus = async (pickOrderLineId: number) => {
  1089. try {
  1090. const response = await fetch(`/api/suggestedPickLot/suggestions-with-status/${pickOrderLineId}`);
  1091. const suggestions: SuggestionWithStatus[] = await response.json();
  1092. return suggestions;
  1093. } catch (error) {
  1094. console.error('Error fetching suggestions with status:', error);
  1095. return [];
  1096. }
  1097. };
  1098. export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): Promise<any> => {
  1099. try {
  1100. console.log("🔍 Fetching hierarchical pick order lots for userId:", userId);
  1101. const data = await serverFetchJson<any>(
  1102. `${BASE_API_URL}/pickOrder/all-lots-hierarchical/${userId}`,
  1103. {
  1104. method: 'GET',
  1105. next: { tags: ["pickorder"] },
  1106. }
  1107. );
  1108. console.log(" Fetched hierarchical lot details:", data);
  1109. return data;
  1110. } catch (error) {
  1111. console.error("❌ Error fetching hierarchical lot details:", error);
  1112. return {
  1113. pickOrder: null,
  1114. pickOrderLines: []
  1115. };
  1116. }
  1117. });
  1118. /** DO workbench: hierarchical lots where header is `delivery_order_pick_order`. */
  1119. export const fetchAllPickOrderLotsHierarchicalWorkbench = cache(async (userId: number): Promise<any> => {
  1120. try {
  1121. const data = await serverFetchJson<any>(
  1122. `${BASE_API_URL}/pickOrder/all-lots-hierarchical-workbench/${userId}`,
  1123. {
  1124. method: "GET",
  1125. next: { tags: ["pickorder"] },
  1126. },
  1127. );
  1128. return data;
  1129. } catch (error) {
  1130. console.error("❌ Error fetching workbench hierarchical lot details:", error);
  1131. return {
  1132. pickOrder: null,
  1133. pickOrderLines: [],
  1134. };
  1135. }
  1136. });
  1137. export const fetchLotDetailsByDoPickOrderRecordId = async (doPickOrderRecordId: number): Promise<{
  1138. fgInfo: any;
  1139. pickOrders: any[];
  1140. }> => {
  1141. try {
  1142. console.log("🔍 Fetching lot details for doPickOrderRecordId:", doPickOrderRecordId);
  1143. const data = await serverFetchJson<{
  1144. fgInfo: any;
  1145. pickOrders: any[];
  1146. }>(
  1147. `${BASE_API_URL}/pickOrder/lot-details-by-do-pick-order-record/${doPickOrderRecordId}`,
  1148. {
  1149. method: 'GET',
  1150. next: { tags: ["pickorder"] },
  1151. }
  1152. );
  1153. console.log(" Fetched hierarchical lot details:", data);
  1154. return data;
  1155. } catch (error) {
  1156. console.error("❌ Error fetching lot details:", error);
  1157. return {
  1158. fgInfo: null,
  1159. pickOrders: []
  1160. };
  1161. }
  1162. };
  1163. export const fetchAllPickOrderDetails = cache(async (userId?: number) => {
  1164. if (!userId) {
  1165. return {
  1166. consoCode: null,
  1167. pickOrders: [],
  1168. items: []
  1169. };
  1170. }
  1171. // Use the correct endpoint with userId in the path
  1172. const url = `${BASE_API_URL}/pickOrder/detail-optimized/${userId}`;
  1173. return serverFetchJson<GetPickOrderInfoResponse>(
  1174. url,
  1175. {
  1176. method: "GET",
  1177. next: { tags: ["pickorder"] },
  1178. },
  1179. );
  1180. });
  1181. export const fetchPickOrderLineLotDetails = cache(async (pickOrderLineId: number) => {
  1182. return serverFetchJson<PickOrderLotDetailResponse[]>(
  1183. `${BASE_API_URL}/pickOrder/lot-details/${pickOrderLineId}`,
  1184. {
  1185. method: "GET",
  1186. next: { tags: ["pickorder"] },
  1187. },
  1188. );
  1189. });
  1190. export const createPickOrder = async (data: SavePickOrderRequest) => {
  1191. console.log(data);
  1192. const po = await serverFetchJson<PostPickOrderResponse>(
  1193. `${BASE_API_URL}/pickOrder/create`,
  1194. {
  1195. method: "POST",
  1196. body: JSON.stringify(data),
  1197. headers: { "Content-Type": "application/json" },
  1198. },
  1199. );
  1200. revalidateTag("pickorder");
  1201. return po;
  1202. }
  1203. export const assignPickOrder = async (ids: number[]) => {
  1204. const pickOrder = await serverFetchJson<any>(
  1205. `${BASE_API_URL}/pickOrder/conso`,
  1206. {
  1207. method: "POST",
  1208. body: JSON.stringify({ ids: ids }),
  1209. headers: { "Content-Type": "application/json" },
  1210. },
  1211. );
  1212. // revalidateTag("po");
  1213. return pickOrder;
  1214. };
  1215. export const consolidatePickOrder = async (ids: number[]) => {
  1216. const pickOrder = await serverFetchJson<any>(
  1217. `${BASE_API_URL}/pickOrder/conso`,
  1218. {
  1219. method: "POST",
  1220. body: JSON.stringify({ ids: ids }),
  1221. headers: { "Content-Type": "application/json" },
  1222. },
  1223. );
  1224. return pickOrder;
  1225. };
  1226. export const consolidatePickOrder_revert = async (ids: number[]) => {
  1227. const pickOrder = await serverFetchJson<any>(
  1228. `${BASE_API_URL}/pickOrder/deconso`,
  1229. {
  1230. method: "POST",
  1231. body: JSON.stringify({ ids: ids }),
  1232. headers: { "Content-Type": "application/json" },
  1233. },
  1234. );
  1235. // revalidateTag("po");
  1236. return pickOrder;
  1237. };
  1238. export const fetchPickOrderClient = cache(
  1239. async (queryParams?: Record<string, any>) => {
  1240. if (queryParams) {
  1241. const queryString = new URLSearchParams(queryParams).toString();
  1242. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1243. `${BASE_API_URL}/pickOrder/getRecordByPage?${queryString}`,
  1244. {
  1245. method: "GET",
  1246. next: { tags: ["pickorder"] },
  1247. },
  1248. );
  1249. } else {
  1250. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1251. `${BASE_API_URL}/pickOrder/getRecordByPage`,
  1252. {
  1253. method: "GET",
  1254. next: { tags: ["pickorder"] },
  1255. },
  1256. );
  1257. }
  1258. },
  1259. );
  1260. export const fetchPickOrderWithStockClient = cache(
  1261. async (queryParams?: Record<string, any>) => {
  1262. if (queryParams) {
  1263. const queryString = new URLSearchParams(queryParams).toString();
  1264. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1265. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock?${queryString}`,
  1266. {
  1267. method: "GET",
  1268. next: { tags: ["pickorder"] },
  1269. },
  1270. );
  1271. } else {
  1272. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1273. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock`,
  1274. {
  1275. method: "GET",
  1276. next: { tags: ["pickorder"] },
  1277. },
  1278. );
  1279. }
  1280. },
  1281. );
  1282. export const fetchConsoPickOrderClient = cache(
  1283. async (queryParams?: Record<string, any>) => {
  1284. if (queryParams) {
  1285. const queryString = new URLSearchParams(queryParams).toString();
  1286. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1287. `${BASE_API_URL}/pickOrder/getRecordByPage-conso?${queryString}`,
  1288. {
  1289. method: "GET",
  1290. next: { tags: ["pickorder"] },
  1291. },
  1292. );
  1293. } else {
  1294. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1295. `${BASE_API_URL}/pickOrder/getRecordByPage-conso`,
  1296. {
  1297. method: "GET",
  1298. next: { tags: ["pickorder"] },
  1299. },
  1300. );
  1301. }
  1302. },
  1303. );
  1304. export const fetchPickOrderLineClient = cache(
  1305. async (queryParams?: Record<string, any>) => {
  1306. if (queryParams) {
  1307. const queryString = new URLSearchParams(queryParams).toString();
  1308. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1309. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`,
  1310. {
  1311. method: "GET",
  1312. next: { tags: ["pickorder"] },
  1313. },
  1314. );
  1315. } else {
  1316. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1317. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage`,
  1318. {
  1319. method: "GET",
  1320. next: { tags: ["pickorder"] },
  1321. },
  1322. );
  1323. }
  1324. },
  1325. );
  1326. export const fetchStockOutLineClient = cache(
  1327. async (pickOrderLineId: number) => {
  1328. return serverFetchJson<StockOutLine[]>(
  1329. `${BASE_API_URL}/stockOutLine/getByPickOrderLineId/${pickOrderLineId}`,
  1330. {
  1331. method: "GET",
  1332. next: { tags: ["pickorder"] },
  1333. },
  1334. );
  1335. },
  1336. );
  1337. export const fetchConsoDetail = cache(async (consoCode: string) => {
  1338. return serverFetchJson<PreReleasePickOrderSummary>(
  1339. `${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`,
  1340. {
  1341. method: "GET",
  1342. next: { tags: ["pickorder"] },
  1343. },
  1344. );
  1345. });
  1346. export const releasePickOrder = async (data: ReleasePickOrderInputs) => {
  1347. console.log(data);
  1348. console.log(JSON.stringify(data));
  1349. const po = await serverFetchJson<{ consoCode: string }>(
  1350. `${BASE_API_URL}/pickOrder/releaseConso`,
  1351. {
  1352. method: "POST",
  1353. body: JSON.stringify(data),
  1354. headers: { "Content-Type": "application/json" },
  1355. },
  1356. );
  1357. revalidateTag("pickorder");
  1358. return po;
  1359. };
  1360. export const createStockOutLine = async (data: CreateStockOutLine) => {
  1361. console.log("triggering");
  1362. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1363. `${BASE_API_URL}/stockOutLine/create`,
  1364. {
  1365. method: "POST",
  1366. body: JSON.stringify(data),
  1367. headers: { "Content-Type": "application/json" },
  1368. },
  1369. );
  1370. revalidateTag("pickorder");
  1371. return po;
  1372. };
  1373. export const updateStockOutLine = async (data: UpdateStockOutLine) => {
  1374. console.log(data);
  1375. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1376. `${BASE_API_URL}/stockOutLine/update`,
  1377. {
  1378. method: "POST",
  1379. body: JSON.stringify(data),
  1380. headers: { "Content-Type": "application/json" },
  1381. },
  1382. );
  1383. revalidateTag("pickorder");
  1384. return po;
  1385. };
  1386. export const completeConsoPickOrder = async (consoCode: string) => {
  1387. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1388. `${BASE_API_URL}/pickOrder/consoPickOrder/complete/${consoCode}`,
  1389. {
  1390. method: "POST",
  1391. headers: { "Content-Type": "application/json" },
  1392. },
  1393. );
  1394. revalidateTag("pickorder");
  1395. return po;
  1396. };
  1397. export const fetchConsoStatus = cache(async (consoCode: string) => {
  1398. return serverFetchJson<{ status: string }>(
  1399. `${BASE_API_URL}/stockOut/get-status/${consoCode}`,
  1400. {
  1401. method: "GET",
  1402. next: { tags: ["pickorder"] },
  1403. },
  1404. );
  1405. });
  1406. export interface ReleasedDoPickOrderResponse {
  1407. id: number;
  1408. storeId: string;
  1409. ticketNo: string;
  1410. pickOrderId: number;
  1411. ticketStatus: string;
  1412. doOrderId: number;
  1413. shopId: number;
  1414. handledBy: number;
  1415. ticketReleaseTime: string;
  1416. }
  1417. export const fetchReleasedDoPickOrders = async (): Promise<ReleasedDoPickOrderResponse[]> => {
  1418. const response = await serverFetchJson<ReleasedDoPickOrderResponse[]>(
  1419. `${BASE_API_URL}/doPickOrder/released`,
  1420. {
  1421. method: "GET",
  1422. },
  1423. );
  1424. return response;
  1425. };
  1426. // 新增:Released Do Pick Order 列表項目(對應後端 ReleasedDoPickOrderListItem)
  1427. export interface ReleasedDoPickOrderListItem {
  1428. id: number;
  1429. requiredDeliveryDate: string | null;
  1430. shopCode: string | null;
  1431. shopName: string | null;
  1432. storeId: string | null;
  1433. truckLanceCode: string | null;
  1434. truckDepartureTime: string | null;
  1435. deliveryOrderCodes: string[];
  1436. }
  1437. // 修改:fetchReleasedDoPickOrders 支援 shopName 篩選,並回傳新結構
  1438. export const fetchReleasedDoPickOrdersForSelection = async (
  1439. shopName?: string,
  1440. storeId?: string,
  1441. truck?: string
  1442. ): Promise<ReleasedDoPickOrderListItem[]> => {
  1443. const params = new URLSearchParams();
  1444. if (shopName?.trim()) params.append("shopName", shopName.trim());
  1445. if (storeId?.trim()) params.append("storeId", storeId.trim());
  1446. if (truck?.trim()) params.append("truck", truck.trim());
  1447. const query = params.toString();
  1448. const url = `${BASE_API_URL}/doPickOrder/released${query ? `?${query}` : ""}`;
  1449. const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, {
  1450. method: "GET",
  1451. });
  1452. return response ?? [];
  1453. };
  1454. export const fetchReleasedDoPickOrdersForSelectionToday = async (
  1455. shopName?: string,
  1456. storeId?: string,
  1457. truck?: string
  1458. ): Promise<ReleasedDoPickOrderListItem[]> => {
  1459. const params = new URLSearchParams();
  1460. if (shopName?.trim()) params.append("shopName", shopName.trim());
  1461. if (storeId?.trim()) params.append("storeId", storeId.trim());
  1462. if (truck?.trim()) params.append("truck", truck.trim());
  1463. const query = params.toString();
  1464. const url = `${BASE_API_URL}/doPickOrder/released-today${query ? `?${query}` : ""}`;
  1465. const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, {
  1466. method: "GET",
  1467. });
  1468. return response ?? [];
  1469. };
  1470. export const fetchReleasedDoPickOrderCountByStore = async (
  1471. storeId: string
  1472. ): Promise<number> => {
  1473. const list = await fetchReleasedDoPickOrdersForSelection(undefined, storeId);
  1474. return list.length;
  1475. };
  1476. // 新增:依 doPickOrderId 分配
  1477. export const assignByDoPickOrderId = async (
  1478. userId: number,
  1479. doPickOrderId: number
  1480. ): Promise<PostPickOrderResponse> => {
  1481. const response = await serverFetchJson<PostPickOrderResponse>(
  1482. `${BASE_API_URL}/doPickOrder/assign-by-id`,
  1483. {
  1484. method: "POST",
  1485. headers: { "Content-Type": "application/json" },
  1486. body: JSON.stringify({ userId, doPickOrderId }),
  1487. }
  1488. );
  1489. revalidateTag("pickorder");
  1490. return response;
  1491. };