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.
 
 

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