FPSMS-frontend
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 

1387 Zeilen
37 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. issueRemark: string;
  197. pickerName: string;
  198. handledBy?: number;
  199. badReason?: string;
  200. reason?: string;
  201. }
  202. export type AutoAssignReleaseResponse = {
  203. id: number | null;
  204. name?: string | null;
  205. code?: string | null;
  206. type?: string | null;
  207. message?: string | null;
  208. errorPosition?: string | null;
  209. entity?: any;
  210. };
  211. export interface PickOrderCompletionResponse {
  212. id: number | null;
  213. name: string;
  214. code: string;
  215. type?: string;
  216. message: string | null;
  217. errorPosition: string;
  218. entity?: {
  219. hasCompletedOrders: boolean;
  220. completedOrders: Array<{
  221. pickOrderId: number;
  222. pickOrderCode: string;
  223. consoCode: string;
  224. isCompleted: boolean;
  225. stockOutStatus: string;
  226. totalLines: number;
  227. unfinishedLines: number;
  228. }>;
  229. allOrders: Array<{
  230. pickOrderId: number;
  231. pickOrderCode: string;
  232. consoCode: string;
  233. isCompleted: boolean;
  234. stockOutStatus: string;
  235. totalLines: number;
  236. unfinishedLines: number;
  237. }>;
  238. };
  239. }
  240. export interface UpdateSuggestedLotLineIdRequest {
  241. newLotLineId: number;
  242. }
  243. export interface stockReponse{
  244. id: number;
  245. status: string;
  246. qty: number;
  247. lotId: number;
  248. lotNo: string;
  249. location: string;
  250. availableQty: number;
  251. noLot: boolean;
  252. }
  253. export interface FGPickOrderResponse {
  254. // 新增:支持多个 pick orders
  255. doPickOrderId: number;
  256. pickOrderIds?: number[];
  257. pickOrderCodes?: string[]; // 改为数组
  258. deliveryOrderIds?: number[];
  259. deliveryNos?: string[]; // 改为数组
  260. numberOfPickOrders?: number;
  261. lineCountsPerPickOrder?: number[];// 新增:pick order 数量
  262. // 保留原有字段用于向后兼容(显示第一个 pick order)
  263. pickOrderId: number;
  264. pickOrderCode: string;
  265. pickOrderConsoCode: string;
  266. pickOrderTargetDate: string;
  267. pickOrderStatus: string;
  268. deliveryOrderId: number;
  269. deliveryNo: string;
  270. deliveryDate: string;
  271. shopId: number;
  272. shopCode: string;
  273. shopName: string;
  274. shopAddress: string;
  275. ticketNo: string;
  276. shopPoNo: string;
  277. numberOfCartons: number;
  278. DepartureTime: string;
  279. truckLanceCode: string;
  280. storeId: string;
  281. qrCodeData: number;
  282. }
  283. export interface DoPickOrderDetail {
  284. doPickOrder: {
  285. id: number;
  286. store_id: string;
  287. ticket_no: string;
  288. ticket_status: string;
  289. truck_id: number;
  290. truck_departure_time: string;
  291. shop_id: number;
  292. handled_by: number | null;
  293. loading_sequence: number;
  294. ticket_release_time: string | null;
  295. TruckLanceCode: string;
  296. ShopCode: string;
  297. ShopName: string;
  298. RequiredDeliveryDate: string;
  299. };
  300. pickOrders: Array<{
  301. pick_order_id: number;
  302. pick_order_code: string;
  303. do_order_id: number;
  304. delivery_order_code: string;
  305. consoCode: string;
  306. status: string;
  307. targetDate: string;
  308. }>;
  309. selectedPickOrderId: number;
  310. lotDetails: any[]; // 使用现有的 lot detail 结构
  311. pickOrderCodes?: string;
  312. deliveryNos?: string;
  313. }
  314. export interface AutoAssignReleaseByStoreRequest {
  315. userId: number;
  316. storeId: string; // "2/F" | "4/F"
  317. }
  318. export interface UpdateDoPickOrderHideStatusRequest {
  319. id: number;
  320. name: string;
  321. code: string;
  322. type: string;
  323. message: string;
  324. errorPosition: string;
  325. }
  326. export interface CompletedDoPickOrderResponse {
  327. id: number;
  328. doPickOrderRecordId: number; // ✅ 新增
  329. recordId: number | null;
  330. pickOrderId: number;
  331. pickOrderIds: number[]; // 新增:所有 pick order IDs
  332. pickOrderCode: string;
  333. pickOrderCodes: string; // 新增:所有 pick order codes (逗号分隔)
  334. pickOrderConsoCode: string;
  335. pickOrderStatus: string;
  336. deliveryOrderId: number;
  337. deliveryOrderIds: number[]; // 新增:所有 delivery order IDs
  338. deliveryNo: string;
  339. deliveryNos: string; // 新增:所有 delivery order codes (逗号分隔)
  340. deliveryDate: string;
  341. shopId: number;
  342. shopCode: string;
  343. shopName: string;
  344. shopAddress: string;
  345. ticketNo: string;
  346. shopPoNo: string;
  347. numberOfCartons: number;
  348. truckLanceCode: string;
  349. DepartureTime: string; // 新增
  350. storeId: string;
  351. completedDate: string;
  352. fgPickOrders: FGPickOrderResponse[];
  353. deliveryNoteCode: number;
  354. }
  355. // 新增:搜索参数接口
  356. export interface CompletedDoPickOrderSearchParams {
  357. targetDate?: string;
  358. shopName?: string;
  359. deliveryNoteCode?: string;
  360. }
  361. export interface PickExecutionIssue {
  362. id: number;
  363. pickOrderId: number;
  364. pickOrderCode: string;
  365. pickOrderCreateDate: string;
  366. pickExecutionDate: string;
  367. pickOrderLineId: number;
  368. issueNo: string;
  369. joPickOrderId?: number;
  370. doPickOrderId?: number;
  371. issueCategory: string;
  372. itemId: number;
  373. itemCode: string;
  374. itemDescription: string;
  375. lotId?: number;
  376. lotNo?: string;
  377. storeLocation?: string;
  378. requiredQty: number;
  379. actualPickQty?: number;
  380. missQty?: number;
  381. badItemQty?: number;
  382. issueRemark?: string;
  383. pickerName?: string;
  384. handleStatus: string;
  385. handleDate?: string;
  386. handledBy?: string;
  387. created: string;
  388. createdBy: string;
  389. modified: string;
  390. modifiedBy: string;
  391. }
  392. export interface FetchPickExecutionIssuesParams {
  393. type?: "jo" | "do" | "material";
  394. }
  395. export const fetchAllPickExecutionIssues = cache(
  396. async (params?: FetchPickExecutionIssuesParams): Promise<PickExecutionIssue[]> => {
  397. const queryParams = new URLSearchParams();
  398. if (params?.type) {
  399. queryParams.append("type", params.type);
  400. }
  401. const url = `${BASE_API_URL}/pickExecution/issues/all${
  402. queryParams.toString() ? `?${queryParams.toString()}` : ""
  403. }`;
  404. const result = await serverFetchJson<PickExecutionIssue[]>(url, {
  405. method: "GET",
  406. headers: { "Content-Type": "application/json" },
  407. });
  408. return result ?? [];
  409. }
  410. );
  411. export interface UpdatePickExecutionIssueRequest {
  412. issueId: number;
  413. handleStatus: string;
  414. handleDate?: string;
  415. handledBy?: string;
  416. handleRemark?: string;
  417. }
  418. export interface StoreLaneSummary {
  419. storeId: string;
  420. rows: LaneRow[];
  421. }
  422. export interface LaneRow {
  423. truckDepartureTime: string;
  424. lanes: LaneBtn[];
  425. }
  426. export interface LaneBtn {
  427. truckLanceCode: string;
  428. unassigned: number;
  429. total: number;
  430. }
  431. export interface QrPickBatchSubmitRequest {
  432. userId: number;
  433. lines: QrPickSubmitLineRequest[];
  434. }
  435. export interface QrPickSubmitLineRequest {
  436. stockOutLineId: number;
  437. pickOrderLineId: number;
  438. inventoryLotLineId: number | null; // ✅ 修复:应该是 nullable
  439. requiredQty: number | null; // ✅ 修复:添加 requiredQty
  440. actualPickQty: number | null; // ✅ 修复:添加 actualPickQty
  441. stockOutLineStatus: string | null; // ✅ 修复:添加 stockOutLineStatus
  442. pickOrderConsoCode: string | null; // ✅ 修复:添加 pickOrderConsoCode
  443. noLot: boolean; // ✅ 修复:添加 noLot
  444. }
  445. export interface UpdateStockOutLineStatusByQRCodeAndLotNoRequest {
  446. pickOrderLineId: number,
  447. inventoryLotNo: string,
  448. stockOutLineId: number,
  449. itemId: number,
  450. status: string
  451. }
  452. export interface batchSubmitListRequest {
  453. userId: number;
  454. lines: batchSubmitListLineRequest[];
  455. }
  456. export interface batchSubmitListLineRequest {
  457. stockOutLineId: number; // 修复:改为 stockOutLineId(不是 stockInLineId)
  458. pickOrderLineId: number;
  459. inventoryLotLineId: number | null; // 添加:后端需要的字段
  460. requiredQty: number;
  461. actualPickQty: number;
  462. stockOutLineStatus: string;
  463. pickOrderConsoCode: string;
  464. noLot: boolean;
  465. // 移除:lotNo 和 stockInLineId(后端不需要)
  466. }
  467. export const batchSubmitList = async (data: batchSubmitListRequest) => {
  468. // ✅ 确保发送的是对象,不是数组
  469. const requestBody = Array.isArray(data) ? data[0] : data;
  470. console.log("📤 batchSubmitList - Request body type:", Array.isArray(requestBody) ? "array" : "object");
  471. console.log("📤 batchSubmitList - Request body:", JSON.stringify(requestBody, null, 2));
  472. const response = await serverFetchJson<PostPickOrderResponse<batchSubmitListRequest>>(
  473. `${BASE_API_URL}/stockOutLine/batchSubmitList`,
  474. {
  475. method: "POST",
  476. body: JSON.stringify(requestBody), // ✅ 确保是对象
  477. headers: {
  478. "Content-Type": "application/json", // ✅ 明确指定 Content-Type
  479. },
  480. },
  481. );
  482. return response;
  483. };
  484. export const updateStockOutLineStatusByQRCodeAndLotNo = async (data: UpdateStockOutLineStatusByQRCodeAndLotNoRequest) => {
  485. console.log(" Frontend: Calling updateStockOutLineStatusByQRCodeAndLotNo with data:", data);
  486. try {
  487. const response = await serverFetchJson<PostPickOrderResponse<UpdateStockOutLineStatusByQRCodeAndLotNoRequest>>(
  488. `${BASE_API_URL}/stockOutLine/updateStatusByQRCodeAndLotNo`,
  489. {
  490. method: "POST",
  491. headers: {
  492. "Content-Type": "application/json",
  493. },
  494. body: JSON.stringify(data),
  495. },
  496. );
  497. console.log("✅ Frontend: API call successful, response:", response);
  498. return response;
  499. } catch (error) {
  500. console.error("❌ Frontend: API call failed:", error);
  501. throw error;
  502. }
  503. };
  504. export const batchQrSubmit = async (data: QrPickBatchSubmitRequest) => {
  505. const response = await serverFetchJson<PostPickOrderResponse<QrPickBatchSubmitRequest>>(
  506. `${BASE_API_URL}/stockOutLine/batchQrSubmit`,
  507. {
  508. method: "POST",
  509. body: JSON.stringify(data),
  510. },
  511. );
  512. return response;
  513. };
  514. export interface BatchScanRequest {
  515. userId: number;
  516. lines: BatchScanLineRequest[];
  517. }
  518. export interface BatchScanLineRequest {
  519. pickOrderLineId: number;
  520. inventoryLotLineId: number | null; // 如果有 lot,提供 lotId;如果没有则为 null
  521. pickOrderConsoCode: string;
  522. lotNo: string | null; // 用于日志和验证
  523. itemId: number;
  524. itemCode: string;
  525. stockOutLineId: number | null; // ✅ 新增:如果已有 stockOutLineId,直接使用
  526. }
  527. export const batchScan = async (data: BatchScanRequest) => {
  528. console.log("📤 batchScan - Request body:", JSON.stringify(data, null, 2));
  529. const response = await serverFetchJson<PostPickOrderResponse<BatchScanRequest>>(
  530. `${BASE_API_URL}/stockOutLine/batchScan`,
  531. {
  532. method: "POST",
  533. body: JSON.stringify(data),
  534. headers: {
  535. "Content-Type": "application/json",
  536. },
  537. },
  538. );
  539. console.log("📥 batchScan - Response:", response);
  540. return response;
  541. };
  542. export const fetchDoPickOrderDetail = async (
  543. doPickOrderId: number,
  544. selectedPickOrderId?: number
  545. ): Promise<DoPickOrderDetail> => {
  546. const url = selectedPickOrderId
  547. ? `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}?selectedPickOrderId=${selectedPickOrderId}`
  548. : `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}`;
  549. const response = await serverFetchJson<DoPickOrderDetail>(url, {
  550. method: "GET",
  551. });
  552. return response;
  553. };
  554. export const updatePickExecutionIssueStatus = async (
  555. data: UpdatePickExecutionIssueRequest
  556. ): Promise<PostPickOrderResponse> => {
  557. const result = await serverFetchJson<PostPickOrderResponse>(
  558. `${BASE_API_URL}/pickExecution/updateIssueStatus`,
  559. {
  560. method: "POST",
  561. body: JSON.stringify(data),
  562. headers: { "Content-Type": "application/json" },
  563. }
  564. );
  565. revalidateTag("pickExecutionIssues");
  566. return result;
  567. };
  568. export async function fetchStoreLaneSummary(storeId: string, requiredDate?: string, releaseType?: string): Promise<StoreLaneSummary> {
  569. const dateToUse = requiredDate || dayjs().format('YYYY-MM-DD');
  570. const url = `${BASE_API_URL}/doPickOrder/summary-by-store?storeId=${encodeURIComponent(storeId)}&requiredDate=${encodeURIComponent(dateToUse)}&releaseType=${encodeURIComponent(releaseType || 'all')}`;
  571. const response = await serverFetchJson<StoreLaneSummary>(
  572. url,
  573. {
  574. method: "GET",
  575. cache: "no-store",
  576. next: { revalidate: 0 }
  577. }
  578. );
  579. return response;
  580. }
  581. // 按车道分配订单
  582. export async function assignByLane(
  583. userId: number,
  584. storeId: string,
  585. truckLanceCode: string,
  586. truckDepartureTime?: string,
  587. requiredDate?: string
  588. ): Promise<any> {
  589. const response = await serverFetchJson(
  590. `${BASE_API_URL}/doPickOrder/assign-by-lane`,
  591. {
  592. method: "POST",
  593. headers: {
  594. "Content-Type": "application/json",
  595. },
  596. body: JSON.stringify({
  597. userId,
  598. storeId,
  599. truckLanceCode,
  600. truckDepartureTime,
  601. requiredDate,
  602. }),
  603. }
  604. );
  605. return response;
  606. }
  607. // 新增:获取已完成的 DO Pick Orders API
  608. export const fetchCompletedDoPickOrders = async (
  609. userId: number,
  610. searchParams?: CompletedDoPickOrderSearchParams
  611. ): Promise<CompletedDoPickOrderResponse[]> => {
  612. const params = new URLSearchParams();
  613. if (searchParams?.deliveryNoteCode) {
  614. params.append('deliveryNoteCode', searchParams.deliveryNoteCode);
  615. }
  616. if (searchParams?.shopName) {
  617. params.append('shopName', searchParams.shopName);
  618. }
  619. if (searchParams?.targetDate) {
  620. params.append('targetDate', searchParams.targetDate);
  621. }
  622. const queryString = params.toString();
  623. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders/${userId}${queryString ? `?${queryString}` : ''}`;
  624. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  625. method: "GET",
  626. });
  627. return response;
  628. };
  629. export const updatePickOrderHideStatus = async (pickOrderId: number, hide: boolean) => {
  630. const response = await serverFetchJson<UpdateDoPickOrderHideStatusRequest>(
  631. `${BASE_API_URL}/pickOrder/update-hide-status/${pickOrderId}?hide=${hide}`,
  632. {
  633. method: "POST",
  634. headers: { "Content-Type": "application/json" },
  635. },
  636. );
  637. revalidateTag("pickorder");
  638. return response;
  639. };
  640. export const fetchFGPickOrders = async (pickOrderId: number) => {
  641. const response = await serverFetchJson<FGPickOrderResponse>(
  642. `${BASE_API_URL}/pickOrder/fg-pick-orders/${pickOrderId}`,
  643. {
  644. method: "GET",
  645. },
  646. );
  647. return response;
  648. };
  649. export const fetchFGPickOrdersByUserId = async (userId: number) => {
  650. const response = await serverFetchJson<FGPickOrderResponse[]>(
  651. `${BASE_API_URL}/pickOrder/fg-pick-orders/${userId}`,
  652. {
  653. method: "GET",
  654. },
  655. );
  656. return response;
  657. };
  658. export const updateSuggestedLotLineId = async (suggestedPickLotId: number, newLotLineId: number) => {
  659. const response = await serverFetchJson<PostPickOrderResponse<UpdateSuggestedLotLineIdRequest>>(
  660. `${BASE_API_URL}/suggestedPickLot/update-suggested-lot/${suggestedPickLotId}`,
  661. {
  662. method: "POST",
  663. body: JSON.stringify({ newLotLineId }),
  664. headers: { "Content-Type": "application/json" },
  665. },
  666. );
  667. revalidateTag("pickorder");
  668. return response;
  669. };
  670. export const autoAssignAndReleasePickOrder = async (userId: number): Promise<AutoAssignReleaseResponse> => {
  671. const response = await serverFetchJson<AutoAssignReleaseResponse>(
  672. `${BASE_API_URL}/pickOrder/auto-assign-release/${userId}`,
  673. {
  674. method: "POST",
  675. headers: { "Content-Type": "application/json" },
  676. },
  677. );
  678. revalidateTag("pickorder");
  679. return response;
  680. };
  681. export const autoAssignAndReleasePickOrderByStore = async (
  682. userId: number,
  683. storeId: string
  684. ): Promise<AutoAssignReleaseResponse> => {
  685. const url = `${BASE_API_URL}/pickOrder/auto-assign-release-by-store?userId=${userId}&storeId=${encodeURIComponent(storeId)}`;
  686. const response = await serverFetchJson<AutoAssignReleaseResponse>(url, {
  687. method: "POST",
  688. headers: { "Content-Type": "application/json" },
  689. // no body
  690. next: { tags: ["pickorder"] },
  691. });
  692. revalidateTag("pickorder");
  693. return response;
  694. };
  695. export const checkPickOrderCompletion = async (userId: number): Promise<PickOrderCompletionResponse> => {
  696. const response = await serverFetchJson<PickOrderCompletionResponse>(
  697. `${BASE_API_URL}/pickOrder/check-pick-completion/${userId}`,
  698. {
  699. method: "GET",
  700. headers: { "Content-Type": "application/json" },
  701. },
  702. );
  703. return response;
  704. };
  705. export const recordPickExecutionIssue = async (data: PickExecutionIssueData) => {
  706. const result = await serverFetchJson<PostPickOrderResponse>(
  707. `${BASE_API_URL}/pickExecution/recordIssue`,
  708. {
  709. method: "POST",
  710. body: JSON.stringify(data),
  711. headers: { "Content-Type": "application/json" },
  712. },
  713. );
  714. revalidateTag("pickorder");
  715. return result;
  716. };
  717. export const resuggestPickOrder = async (pickOrderId: number) => {
  718. console.log("Resuggesting pick order:", pickOrderId);
  719. const result = await serverFetchJson<PostPickOrderResponse>(
  720. `${BASE_API_URL}/suggestedPickLot/resuggest/${pickOrderId}`,
  721. {
  722. method: "POST",
  723. headers: { "Content-Type": "application/json" },
  724. },
  725. );
  726. revalidateTag("pickorder");
  727. return result;
  728. };
  729. export const updateStockOutLineStatus = async (data: {
  730. id: number;
  731. status: string;
  732. qty?: number;
  733. remarks?: string;
  734. }) => {
  735. console.log("Updating stock out line status:", data);
  736. const result = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  737. `${BASE_API_URL}/stockOutLine/updateStatus`,
  738. {
  739. method: "POST",
  740. body: JSON.stringify(data),
  741. headers: { "Content-Type": "application/json" },
  742. },
  743. );
  744. revalidateTag("pickorder");
  745. return result;
  746. };
  747. // Missing function 1: newassignPickOrder
  748. export const newassignPickOrder = async (data: AssignPickOrderInputs) => {
  749. const response = await serverFetchJson<PostPickOrderResponse>(
  750. `${BASE_API_URL}/pickOrder/assign`,
  751. {
  752. method: "POST",
  753. body: JSON.stringify(data),
  754. headers: { "Content-Type": "application/json" },
  755. },
  756. );
  757. revalidateTag("pickorder");
  758. return response;
  759. };
  760. // Missing function 2: releaseAssignedPickOrders
  761. export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => {
  762. const response = await serverFetchJson<PostPickOrderResponse>(
  763. `${BASE_API_URL}/pickOrder/release-assigned`,
  764. {
  765. method: "POST",
  766. body: JSON.stringify(data),
  767. headers: { "Content-Type": "application/json" },
  768. },
  769. );
  770. revalidateTag("pickorder");
  771. return response;
  772. };
  773. // Get latest group name and create it automatically
  774. export const getLatestGroupNameAndCreate = async () => {
  775. return serverFetchJson<PostPickOrderResponse>(
  776. `${BASE_API_URL}/pickOrder/groups/latest`,
  777. {
  778. method: "GET",
  779. next: { tags: ["pickorder"] },
  780. },
  781. );
  782. };
  783. // Get all groups
  784. export const fetchAllGroups = cache(async () => {
  785. return serverFetchJson<PickOrderGroupInfo[]>(
  786. `${BASE_API_URL}/pickOrder/groups/list`,
  787. {
  788. method: "GET",
  789. next: { tags: ["pickorder"] },
  790. },
  791. );
  792. });
  793. // Create or update groups (flexible - can handle both cases)
  794. export const createOrUpdateGroups = async (data: SavePickOrderGroupRequest) => {
  795. const response = await serverFetchJson<PostPickOrderResponse>(
  796. `${BASE_API_URL}/pickOrder/groups/create`,
  797. {
  798. method: "POST",
  799. body: JSON.stringify(data),
  800. headers: { "Content-Type": "application/json" },
  801. },
  802. );
  803. revalidateTag("pickorder");
  804. return response;
  805. };
  806. // Get groups by pick order ID
  807. export const fetchGroupsByPickOrderId = cache(async (pickOrderId: number) => {
  808. return serverFetchJson<PickOrderGroupInfo[]>(
  809. `${BASE_API_URL}/pickOrder/groups/${pickOrderId}`,
  810. {
  811. method: "GET",
  812. next: { tags: ["pickorder"] },
  813. },
  814. );
  815. });
  816. export const fetchPickOrderDetails = cache(async (ids: string) => {
  817. return serverFetchJson<GetPickOrderInfoResponse>(
  818. `${BASE_API_URL}/pickOrder/detail/${ids}`,
  819. {
  820. method: "GET",
  821. next: { tags: ["pickorder"] },
  822. },
  823. );
  824. });
  825. export interface PickOrderLotDetailResponse {
  826. lotId: number | null; // ✅ 改为可空
  827. lotNo: string | null; // ✅ 改为可空
  828. expiryDate: string | null; // ✅ 改为可空
  829. location: string | null; // ✅ 改为可空
  830. stockUnit: string | null;
  831. inQty: number | null;
  832. availableQty: number | null; // ✅ 改为可空
  833. requiredQty: number;
  834. actualPickQty: number;
  835. suggestedPickLotId: number | null;
  836. lotStatus: string | null;
  837. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable' | 'rejected';
  838. stockOutLineId: number | null; // ✅ 添加
  839. stockOutLineStatus: string | null; // ✅ 添加
  840. stockOutLineQty: number | null; // ✅ 添加
  841. totalPickedByAllPickOrders: number | null; // ✅ 添加
  842. remainingAfterAllPickOrders: number | null; // ✅ 添加
  843. noLot: boolean; // ✅ 关键:添加 noLot 字段
  844. outQty?: number; // ✅ 添加
  845. holdQty?: number; // ✅ 添加
  846. }
  847. interface ALLPickOrderLotDetailResponse {
  848. // Pick Order Information
  849. pickOrderId: number;
  850. pickOrderCode: string;
  851. pickOrderTargetDate: string;
  852. pickOrderType: string;
  853. pickOrderStatus: string;
  854. pickOrderAssignTo: number;
  855. groupName: string;
  856. // Pick Order Line Information
  857. pickOrderLineId: number;
  858. pickOrderLineRequiredQty: number;
  859. pickOrderLineStatus: string;
  860. // Item Information
  861. itemId: number;
  862. itemCode: string;
  863. itemName: string;
  864. uomCode: string;
  865. uomDesc: string;
  866. // Lot Information
  867. lotId: number;
  868. lotNo: string;
  869. expiryDate: string;
  870. location: string;
  871. outQty: number;
  872. holdQty: number;
  873. stockUnit: string;
  874. availableQty: number;
  875. requiredQty: number;
  876. actualPickQty: number;
  877. totalPickedByAllPickOrders: number;
  878. suggestedPickLotId: number;
  879. lotStatus: string;
  880. stockOutLineId?: number;
  881. stockOutLineStatus?: string;
  882. stockOutLineQty?: number;
  883. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected';
  884. processingStatus: string;
  885. }
  886. interface SuggestionWithStatus {
  887. suggestionId: number;
  888. suggestionQty: number;
  889. suggestionCreated: string;
  890. lotLineId: number;
  891. lotNo: string;
  892. expiryDate: string;
  893. location: string;
  894. stockOutLineId?: number;
  895. stockOutLineStatus?: string;
  896. stockOutLineQty?: number;
  897. suggestionStatus: 'active' | 'completed' | 'rejected' | 'in_progress' | 'unknown';
  898. }
  899. // 在 actions.ts 中修改接口定义
  900. export interface FGPickOrderHierarchicalResponse {
  901. fgInfo: {
  902. doPickOrderId: number;
  903. ticketNo: string;
  904. storeId: string;
  905. shopCode: string;
  906. shopName: string;
  907. truckLanceCode: string;
  908. departureTime: string;
  909. };
  910. pickOrders: Array<{
  911. pickOrderId: number;
  912. pickOrderCode: string;
  913. doOrderId: number;
  914. deliveryOrderCode: string;
  915. consoCode: string;
  916. status: string;
  917. targetDate: string;
  918. pickOrderLines: Array<{
  919. id: number;
  920. requiredQty: number;
  921. status: string;
  922. item: {
  923. id: number;
  924. code: string;
  925. name: string;
  926. uomCode: string;
  927. uomDesc: string;
  928. };
  929. lots: Array<any>; // 可以是空数组
  930. }>;
  931. }>;
  932. }
  933. export interface CheckCompleteResponse {
  934. id: number | null;
  935. name: string;
  936. code: string;
  937. type?: string;
  938. message: string | null;
  939. errorPosition: string;
  940. }
  941. export interface LotSubstitutionConfirmRequest {
  942. pickOrderLineId: number;
  943. stockOutLineId: number;
  944. originalSuggestedPickLotId: number;
  945. newInventoryLotNo: string;
  946. newStockInLineId: number;
  947. }
  948. export const confirmLotSubstitution = async (data: LotSubstitutionConfirmRequest) => {
  949. const response = await serverFetchJson<PostPickOrderResponse>(
  950. `${BASE_API_URL}/pickOrder/lot-substitution/confirm`,
  951. {
  952. method: "POST",
  953. body: JSON.stringify(data),
  954. headers: { "Content-Type": "application/json" },
  955. },
  956. );
  957. revalidateTag("pickorder");
  958. return response;
  959. };
  960. export const checkAndCompletePickOrderByConsoCode = async (consoCode: string): Promise<CheckCompleteResponse> => {
  961. const response = await serverFetchJson<CheckCompleteResponse>(
  962. `${BASE_API_URL}/pickOrder/check-complete/${consoCode}`,
  963. {
  964. method: "POST",
  965. headers: {
  966. "Content-Type": "application/json",
  967. },
  968. },
  969. );
  970. revalidateTag("pickorder");
  971. return response;
  972. };
  973. export const fetchPickOrderDetailsOptimized = cache(async (userId?: number) => {
  974. const url = userId
  975. ? `${BASE_API_URL}/pickOrder/detail-optimized?userId=${userId}`
  976. : `${BASE_API_URL}/pickOrder/detail-optimized`;
  977. return serverFetchJson<any[]>(
  978. url,
  979. {
  980. method: "GET",
  981. next: { tags: ["pickorder"] },
  982. },
  983. );
  984. });
  985. const fetchSuggestionsWithStatus = async (pickOrderLineId: number) => {
  986. try {
  987. const response = await fetch(`/api/suggestedPickLot/suggestions-with-status/${pickOrderLineId}`);
  988. const suggestions: SuggestionWithStatus[] = await response.json();
  989. return suggestions;
  990. } catch (error) {
  991. console.error('Error fetching suggestions with status:', error);
  992. return [];
  993. }
  994. };
  995. export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): Promise<any> => {
  996. try {
  997. console.log("🔍 Fetching hierarchical pick order lots for userId:", userId);
  998. const data = await serverFetchJson<any>(
  999. `${BASE_API_URL}/pickOrder/all-lots-hierarchical/${userId}`,
  1000. {
  1001. method: 'GET',
  1002. next: { tags: ["pickorder"] },
  1003. }
  1004. );
  1005. console.log(" Fetched hierarchical lot details:", data);
  1006. return data;
  1007. } catch (error) {
  1008. console.error("❌ Error fetching hierarchical lot details:", error);
  1009. return {
  1010. pickOrder: null,
  1011. pickOrderLines: []
  1012. };
  1013. }
  1014. });
  1015. export const fetchLotDetailsByDoPickOrderRecordId = async (doPickOrderRecordId: number): Promise<{
  1016. fgInfo: any;
  1017. pickOrders: any[];
  1018. }> => {
  1019. try {
  1020. console.log("🔍 Fetching lot details for doPickOrderRecordId:", doPickOrderRecordId);
  1021. const data = await serverFetchJson<{
  1022. fgInfo: any;
  1023. pickOrders: any[];
  1024. }>(
  1025. `${BASE_API_URL}/pickOrder/lot-details-by-do-pick-order-record/${doPickOrderRecordId}`,
  1026. {
  1027. method: 'GET',
  1028. next: { tags: ["pickorder"] },
  1029. }
  1030. );
  1031. console.log(" Fetched hierarchical lot details:", data);
  1032. return data;
  1033. } catch (error) {
  1034. console.error("❌ Error fetching lot details:", error);
  1035. return {
  1036. fgInfo: null,
  1037. pickOrders: []
  1038. };
  1039. }
  1040. };
  1041. export const fetchAllPickOrderDetails = cache(async (userId?: number) => {
  1042. if (!userId) {
  1043. return {
  1044. consoCode: null,
  1045. pickOrders: [],
  1046. items: []
  1047. };
  1048. }
  1049. // Use the correct endpoint with userId in the path
  1050. const url = `${BASE_API_URL}/pickOrder/detail-optimized/${userId}`;
  1051. return serverFetchJson<GetPickOrderInfoResponse>(
  1052. url,
  1053. {
  1054. method: "GET",
  1055. next: { tags: ["pickorder"] },
  1056. },
  1057. );
  1058. });
  1059. export const fetchPickOrderLineLotDetails = cache(async (pickOrderLineId: number) => {
  1060. return serverFetchJson<PickOrderLotDetailResponse[]>(
  1061. `${BASE_API_URL}/pickOrder/lot-details/${pickOrderLineId}`,
  1062. {
  1063. method: "GET",
  1064. next: { tags: ["pickorder"] },
  1065. },
  1066. );
  1067. });
  1068. export const createPickOrder = async (data: SavePickOrderRequest) => {
  1069. console.log(data);
  1070. const po = await serverFetchJson<PostPickOrderResponse>(
  1071. `${BASE_API_URL}/pickOrder/create`,
  1072. {
  1073. method: "POST",
  1074. body: JSON.stringify(data),
  1075. headers: { "Content-Type": "application/json" },
  1076. },
  1077. );
  1078. revalidateTag("pickorder");
  1079. return po;
  1080. }
  1081. export const assignPickOrder = async (ids: number[]) => {
  1082. const pickOrder = await serverFetchJson<any>(
  1083. `${BASE_API_URL}/pickOrder/conso`,
  1084. {
  1085. method: "POST",
  1086. body: JSON.stringify({ ids: ids }),
  1087. headers: { "Content-Type": "application/json" },
  1088. },
  1089. );
  1090. // revalidateTag("po");
  1091. return pickOrder;
  1092. };
  1093. export const consolidatePickOrder = async (ids: number[]) => {
  1094. const pickOrder = await serverFetchJson<any>(
  1095. `${BASE_API_URL}/pickOrder/conso`,
  1096. {
  1097. method: "POST",
  1098. body: JSON.stringify({ ids: ids }),
  1099. headers: { "Content-Type": "application/json" },
  1100. },
  1101. );
  1102. return pickOrder;
  1103. };
  1104. export const consolidatePickOrder_revert = async (ids: number[]) => {
  1105. const pickOrder = await serverFetchJson<any>(
  1106. `${BASE_API_URL}/pickOrder/deconso`,
  1107. {
  1108. method: "POST",
  1109. body: JSON.stringify({ ids: ids }),
  1110. headers: { "Content-Type": "application/json" },
  1111. },
  1112. );
  1113. // revalidateTag("po");
  1114. return pickOrder;
  1115. };
  1116. export const fetchPickOrderClient = cache(
  1117. async (queryParams?: Record<string, any>) => {
  1118. if (queryParams) {
  1119. const queryString = new URLSearchParams(queryParams).toString();
  1120. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1121. `${BASE_API_URL}/pickOrder/getRecordByPage?${queryString}`,
  1122. {
  1123. method: "GET",
  1124. next: { tags: ["pickorder"] },
  1125. },
  1126. );
  1127. } else {
  1128. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1129. `${BASE_API_URL}/pickOrder/getRecordByPage`,
  1130. {
  1131. method: "GET",
  1132. next: { tags: ["pickorder"] },
  1133. },
  1134. );
  1135. }
  1136. },
  1137. );
  1138. export const fetchPickOrderWithStockClient = cache(
  1139. async (queryParams?: Record<string, any>) => {
  1140. if (queryParams) {
  1141. const queryString = new URLSearchParams(queryParams).toString();
  1142. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1143. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock?${queryString}`,
  1144. {
  1145. method: "GET",
  1146. next: { tags: ["pickorder"] },
  1147. },
  1148. );
  1149. } else {
  1150. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1151. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock`,
  1152. {
  1153. method: "GET",
  1154. next: { tags: ["pickorder"] },
  1155. },
  1156. );
  1157. }
  1158. },
  1159. );
  1160. export const fetchConsoPickOrderClient = cache(
  1161. async (queryParams?: Record<string, any>) => {
  1162. if (queryParams) {
  1163. const queryString = new URLSearchParams(queryParams).toString();
  1164. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1165. `${BASE_API_URL}/pickOrder/getRecordByPage-conso?${queryString}`,
  1166. {
  1167. method: "GET",
  1168. next: { tags: ["pickorder"] },
  1169. },
  1170. );
  1171. } else {
  1172. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1173. `${BASE_API_URL}/pickOrder/getRecordByPage-conso`,
  1174. {
  1175. method: "GET",
  1176. next: { tags: ["pickorder"] },
  1177. },
  1178. );
  1179. }
  1180. },
  1181. );
  1182. export const fetchPickOrderLineClient = cache(
  1183. async (queryParams?: Record<string, any>) => {
  1184. if (queryParams) {
  1185. const queryString = new URLSearchParams(queryParams).toString();
  1186. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1187. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`,
  1188. {
  1189. method: "GET",
  1190. next: { tags: ["pickorder"] },
  1191. },
  1192. );
  1193. } else {
  1194. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1195. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage`,
  1196. {
  1197. method: "GET",
  1198. next: { tags: ["pickorder"] },
  1199. },
  1200. );
  1201. }
  1202. },
  1203. );
  1204. export const fetchStockOutLineClient = cache(
  1205. async (pickOrderLineId: number) => {
  1206. return serverFetchJson<StockOutLine[]>(
  1207. `${BASE_API_URL}/stockOutLine/getByPickOrderLineId/${pickOrderLineId}`,
  1208. {
  1209. method: "GET",
  1210. next: { tags: ["pickorder"] },
  1211. },
  1212. );
  1213. },
  1214. );
  1215. export const fetchConsoDetail = cache(async (consoCode: string) => {
  1216. return serverFetchJson<PreReleasePickOrderSummary>(
  1217. `${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`,
  1218. {
  1219. method: "GET",
  1220. next: { tags: ["pickorder"] },
  1221. },
  1222. );
  1223. });
  1224. export const releasePickOrder = async (data: ReleasePickOrderInputs) => {
  1225. console.log(data);
  1226. console.log(JSON.stringify(data));
  1227. const po = await serverFetchJson<{ consoCode: string }>(
  1228. `${BASE_API_URL}/pickOrder/releaseConso`,
  1229. {
  1230. method: "POST",
  1231. body: JSON.stringify(data),
  1232. headers: { "Content-Type": "application/json" },
  1233. },
  1234. );
  1235. revalidateTag("pickorder");
  1236. return po;
  1237. };
  1238. export const createStockOutLine = async (data: CreateStockOutLine) => {
  1239. console.log("triggering");
  1240. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1241. `${BASE_API_URL}/stockOutLine/create`,
  1242. {
  1243. method: "POST",
  1244. body: JSON.stringify(data),
  1245. headers: { "Content-Type": "application/json" },
  1246. },
  1247. );
  1248. revalidateTag("pickorder");
  1249. return po;
  1250. };
  1251. export const updateStockOutLine = async (data: UpdateStockOutLine) => {
  1252. console.log(data);
  1253. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1254. `${BASE_API_URL}/stockOutLine/update`,
  1255. {
  1256. method: "POST",
  1257. body: JSON.stringify(data),
  1258. headers: { "Content-Type": "application/json" },
  1259. },
  1260. );
  1261. revalidateTag("pickorder");
  1262. return po;
  1263. };
  1264. export const completeConsoPickOrder = async (consoCode: string) => {
  1265. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1266. `${BASE_API_URL}/pickOrder/consoPickOrder/complete/${consoCode}`,
  1267. {
  1268. method: "POST",
  1269. headers: { "Content-Type": "application/json" },
  1270. },
  1271. );
  1272. revalidateTag("pickorder");
  1273. return po;
  1274. };
  1275. export const fetchConsoStatus = cache(async (consoCode: string) => {
  1276. return serverFetchJson<{ status: string }>(
  1277. `${BASE_API_URL}/stockOut/get-status/${consoCode}`,
  1278. {
  1279. method: "GET",
  1280. next: { tags: ["pickorder"] },
  1281. },
  1282. );
  1283. });
  1284. export interface ReleasedDoPickOrderResponse {
  1285. id: number;
  1286. storeId: string;
  1287. ticketNo: string;
  1288. pickOrderId: number;
  1289. ticketStatus: string;
  1290. doOrderId: number;
  1291. shopId: number;
  1292. handledBy: number;
  1293. ticketReleaseTime: string;
  1294. }
  1295. export const fetchReleasedDoPickOrders = async (): Promise<ReleasedDoPickOrderResponse[]> => {
  1296. const response = await serverFetchJson<ReleasedDoPickOrderResponse[]>(
  1297. `${BASE_API_URL}/doPickOrder/released`,
  1298. {
  1299. method: "GET",
  1300. },
  1301. );
  1302. return response;
  1303. };