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.
 
 

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