FPSMS-frontend
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.
 
 

1383 wiersze
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. 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 interface BatchScanRequest {
  512. userId: number;
  513. lines: BatchScanLineRequest[];
  514. }
  515. export interface BatchScanLineRequest {
  516. pickOrderLineId: number;
  517. inventoryLotLineId: number | null; // 如果有 lot,提供 lotId;如果没有则为 null
  518. pickOrderConsoCode: string;
  519. lotNo: string | null; // 用于日志和验证
  520. itemId: number;
  521. itemCode: string;
  522. stockOutLineId: number | null; // ✅ 新增:如果已有 stockOutLineId,直接使用
  523. }
  524. export const batchScan = async (data: BatchScanRequest) => {
  525. console.log("📤 batchScan - Request body:", JSON.stringify(data, null, 2));
  526. const response = await serverFetchJson<PostPickOrderResponse<BatchScanRequest>>(
  527. `${BASE_API_URL}/stockOutLine/batchScan`,
  528. {
  529. method: "POST",
  530. body: JSON.stringify(data),
  531. headers: {
  532. "Content-Type": "application/json",
  533. },
  534. },
  535. );
  536. console.log("📥 batchScan - Response:", response);
  537. return response;
  538. };
  539. export const fetchDoPickOrderDetail = async (
  540. doPickOrderId: number,
  541. selectedPickOrderId?: number
  542. ): Promise<DoPickOrderDetail> => {
  543. const url = selectedPickOrderId
  544. ? `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}?selectedPickOrderId=${selectedPickOrderId}`
  545. : `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}`;
  546. const response = await serverFetchJson<DoPickOrderDetail>(url, {
  547. method: "GET",
  548. });
  549. return response;
  550. };
  551. export const updatePickExecutionIssueStatus = async (
  552. data: UpdatePickExecutionIssueRequest
  553. ): Promise<PostPickOrderResponse> => {
  554. const result = await serverFetchJson<PostPickOrderResponse>(
  555. `${BASE_API_URL}/pickExecution/updateIssueStatus`,
  556. {
  557. method: "POST",
  558. body: JSON.stringify(data),
  559. headers: { "Content-Type": "application/json" },
  560. }
  561. );
  562. revalidateTag("pickExecutionIssues");
  563. return result;
  564. };
  565. export async function fetchStoreLaneSummary(storeId: string, requiredDate?: string, releaseType?: string): Promise<StoreLaneSummary> {
  566. const dateToUse = requiredDate || dayjs().format('YYYY-MM-DD');
  567. const url = `${BASE_API_URL}/doPickOrder/summary-by-store?storeId=${encodeURIComponent(storeId)}&requiredDate=${encodeURIComponent(dateToUse)}&releaseType=${encodeURIComponent(releaseType || 'all')}`;
  568. const response = await serverFetchJson<StoreLaneSummary>(
  569. url,
  570. {
  571. method: "GET",
  572. cache: "no-store",
  573. next: { revalidate: 0 }
  574. }
  575. );
  576. return response;
  577. }
  578. // 按车道分配订单
  579. export async function assignByLane(
  580. userId: number,
  581. storeId: string,
  582. truckLanceCode: string,
  583. truckDepartureTime?: string,
  584. requiredDate?: string
  585. ): Promise<any> {
  586. const response = await serverFetchJson(
  587. `${BASE_API_URL}/doPickOrder/assign-by-lane`,
  588. {
  589. method: "POST",
  590. headers: {
  591. "Content-Type": "application/json",
  592. },
  593. body: JSON.stringify({
  594. userId,
  595. storeId,
  596. truckLanceCode,
  597. truckDepartureTime,
  598. requiredDate,
  599. }),
  600. }
  601. );
  602. return response;
  603. }
  604. // 新增:获取已完成的 DO Pick Orders API
  605. export const fetchCompletedDoPickOrders = async (
  606. userId: number,
  607. searchParams?: CompletedDoPickOrderSearchParams
  608. ): Promise<CompletedDoPickOrderResponse[]> => {
  609. const params = new URLSearchParams();
  610. if (searchParams?.deliveryNoteCode) {
  611. params.append('deliveryNoteCode', searchParams.deliveryNoteCode);
  612. }
  613. if (searchParams?.shopName) {
  614. params.append('shopName', searchParams.shopName);
  615. }
  616. if (searchParams?.targetDate) {
  617. params.append('targetDate', searchParams.targetDate);
  618. }
  619. const queryString = params.toString();
  620. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders/${userId}${queryString ? `?${queryString}` : ''}`;
  621. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  622. method: "GET",
  623. });
  624. return response;
  625. };
  626. export const updatePickOrderHideStatus = async (pickOrderId: number, hide: boolean) => {
  627. const response = await serverFetchJson<UpdateDoPickOrderHideStatusRequest>(
  628. `${BASE_API_URL}/pickOrder/update-hide-status/${pickOrderId}?hide=${hide}`,
  629. {
  630. method: "POST",
  631. headers: { "Content-Type": "application/json" },
  632. },
  633. );
  634. revalidateTag("pickorder");
  635. return response;
  636. };
  637. export const fetchFGPickOrders = async (pickOrderId: number) => {
  638. const response = await serverFetchJson<FGPickOrderResponse>(
  639. `${BASE_API_URL}/pickOrder/fg-pick-orders/${pickOrderId}`,
  640. {
  641. method: "GET",
  642. },
  643. );
  644. return response;
  645. };
  646. export const fetchFGPickOrdersByUserId = async (userId: number) => {
  647. const response = await serverFetchJson<FGPickOrderResponse[]>(
  648. `${BASE_API_URL}/pickOrder/fg-pick-orders/${userId}`,
  649. {
  650. method: "GET",
  651. },
  652. );
  653. return response;
  654. };
  655. export const updateSuggestedLotLineId = async (suggestedPickLotId: number, newLotLineId: number) => {
  656. const response = await serverFetchJson<PostPickOrderResponse<UpdateSuggestedLotLineIdRequest>>(
  657. `${BASE_API_URL}/suggestedPickLot/update-suggested-lot/${suggestedPickLotId}`,
  658. {
  659. method: "POST",
  660. body: JSON.stringify({ newLotLineId }),
  661. headers: { "Content-Type": "application/json" },
  662. },
  663. );
  664. revalidateTag("pickorder");
  665. return response;
  666. };
  667. export const autoAssignAndReleasePickOrder = async (userId: number): Promise<AutoAssignReleaseResponse> => {
  668. const response = await serverFetchJson<AutoAssignReleaseResponse>(
  669. `${BASE_API_URL}/pickOrder/auto-assign-release/${userId}`,
  670. {
  671. method: "POST",
  672. headers: { "Content-Type": "application/json" },
  673. },
  674. );
  675. revalidateTag("pickorder");
  676. return response;
  677. };
  678. export const autoAssignAndReleasePickOrderByStore = async (
  679. userId: number,
  680. storeId: string
  681. ): Promise<AutoAssignReleaseResponse> => {
  682. const url = `${BASE_API_URL}/pickOrder/auto-assign-release-by-store?userId=${userId}&storeId=${encodeURIComponent(storeId)}`;
  683. const response = await serverFetchJson<AutoAssignReleaseResponse>(url, {
  684. method: "POST",
  685. headers: { "Content-Type": "application/json" },
  686. // no body
  687. next: { tags: ["pickorder"] },
  688. });
  689. revalidateTag("pickorder");
  690. return response;
  691. };
  692. export const checkPickOrderCompletion = async (userId: number): Promise<PickOrderCompletionResponse> => {
  693. const response = await serverFetchJson<PickOrderCompletionResponse>(
  694. `${BASE_API_URL}/pickOrder/check-pick-completion/${userId}`,
  695. {
  696. method: "GET",
  697. headers: { "Content-Type": "application/json" },
  698. },
  699. );
  700. return response;
  701. };
  702. export const recordPickExecutionIssue = async (data: PickExecutionIssueData) => {
  703. const result = await serverFetchJson<PostPickOrderResponse>(
  704. `${BASE_API_URL}/pickExecution/recordIssue`,
  705. {
  706. method: "POST",
  707. body: JSON.stringify(data),
  708. headers: { "Content-Type": "application/json" },
  709. },
  710. );
  711. revalidateTag("pickorder");
  712. return result;
  713. };
  714. export const resuggestPickOrder = async (pickOrderId: number) => {
  715. console.log("Resuggesting pick order:", pickOrderId);
  716. const result = await serverFetchJson<PostPickOrderResponse>(
  717. `${BASE_API_URL}/suggestedPickLot/resuggest/${pickOrderId}`,
  718. {
  719. method: "POST",
  720. headers: { "Content-Type": "application/json" },
  721. },
  722. );
  723. revalidateTag("pickorder");
  724. return result;
  725. };
  726. export const updateStockOutLineStatus = async (data: {
  727. id: number;
  728. status: string;
  729. qty?: number;
  730. remarks?: string;
  731. }) => {
  732. console.log("Updating stock out line status:", data);
  733. const result = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  734. `${BASE_API_URL}/stockOutLine/updateStatus`,
  735. {
  736. method: "POST",
  737. body: JSON.stringify(data),
  738. headers: { "Content-Type": "application/json" },
  739. },
  740. );
  741. revalidateTag("pickorder");
  742. return result;
  743. };
  744. // Missing function 1: newassignPickOrder
  745. export const newassignPickOrder = async (data: AssignPickOrderInputs) => {
  746. const response = await serverFetchJson<PostPickOrderResponse>(
  747. `${BASE_API_URL}/pickOrder/assign`,
  748. {
  749. method: "POST",
  750. body: JSON.stringify(data),
  751. headers: { "Content-Type": "application/json" },
  752. },
  753. );
  754. revalidateTag("pickorder");
  755. return response;
  756. };
  757. // Missing function 2: releaseAssignedPickOrders
  758. export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => {
  759. const response = await serverFetchJson<PostPickOrderResponse>(
  760. `${BASE_API_URL}/pickOrder/release-assigned`,
  761. {
  762. method: "POST",
  763. body: JSON.stringify(data),
  764. headers: { "Content-Type": "application/json" },
  765. },
  766. );
  767. revalidateTag("pickorder");
  768. return response;
  769. };
  770. // Get latest group name and create it automatically
  771. export const getLatestGroupNameAndCreate = async () => {
  772. return serverFetchJson<PostPickOrderResponse>(
  773. `${BASE_API_URL}/pickOrder/groups/latest`,
  774. {
  775. method: "GET",
  776. next: { tags: ["pickorder"] },
  777. },
  778. );
  779. };
  780. // Get all groups
  781. export const fetchAllGroups = cache(async () => {
  782. return serverFetchJson<PickOrderGroupInfo[]>(
  783. `${BASE_API_URL}/pickOrder/groups/list`,
  784. {
  785. method: "GET",
  786. next: { tags: ["pickorder"] },
  787. },
  788. );
  789. });
  790. // Create or update groups (flexible - can handle both cases)
  791. export const createOrUpdateGroups = async (data: SavePickOrderGroupRequest) => {
  792. const response = await serverFetchJson<PostPickOrderResponse>(
  793. `${BASE_API_URL}/pickOrder/groups/create`,
  794. {
  795. method: "POST",
  796. body: JSON.stringify(data),
  797. headers: { "Content-Type": "application/json" },
  798. },
  799. );
  800. revalidateTag("pickorder");
  801. return response;
  802. };
  803. // Get groups by pick order ID
  804. export const fetchGroupsByPickOrderId = cache(async (pickOrderId: number) => {
  805. return serverFetchJson<PickOrderGroupInfo[]>(
  806. `${BASE_API_URL}/pickOrder/groups/${pickOrderId}`,
  807. {
  808. method: "GET",
  809. next: { tags: ["pickorder"] },
  810. },
  811. );
  812. });
  813. export const fetchPickOrderDetails = cache(async (ids: string) => {
  814. return serverFetchJson<GetPickOrderInfoResponse>(
  815. `${BASE_API_URL}/pickOrder/detail/${ids}`,
  816. {
  817. method: "GET",
  818. next: { tags: ["pickorder"] },
  819. },
  820. );
  821. });
  822. export interface PickOrderLotDetailResponse {
  823. lotId: number | null; // ✅ 改为可空
  824. lotNo: string | null; // ✅ 改为可空
  825. expiryDate: string | null; // ✅ 改为可空
  826. location: string | null; // ✅ 改为可空
  827. stockUnit: string | null;
  828. inQty: number | null;
  829. availableQty: number | null; // ✅ 改为可空
  830. requiredQty: number;
  831. actualPickQty: number;
  832. suggestedPickLotId: number | null;
  833. lotStatus: string | null;
  834. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable' | 'rejected';
  835. stockOutLineId: number | null; // ✅ 添加
  836. stockOutLineStatus: string | null; // ✅ 添加
  837. stockOutLineQty: number | null; // ✅ 添加
  838. totalPickedByAllPickOrders: number | null; // ✅ 添加
  839. remainingAfterAllPickOrders: number | null; // ✅ 添加
  840. noLot: boolean; // ✅ 关键:添加 noLot 字段
  841. outQty?: number; // ✅ 添加
  842. holdQty?: number; // ✅ 添加
  843. }
  844. interface ALLPickOrderLotDetailResponse {
  845. // Pick Order Information
  846. pickOrderId: number;
  847. pickOrderCode: string;
  848. pickOrderTargetDate: string;
  849. pickOrderType: string;
  850. pickOrderStatus: string;
  851. pickOrderAssignTo: number;
  852. groupName: string;
  853. // Pick Order Line Information
  854. pickOrderLineId: number;
  855. pickOrderLineRequiredQty: number;
  856. pickOrderLineStatus: string;
  857. // Item Information
  858. itemId: number;
  859. itemCode: string;
  860. itemName: string;
  861. uomCode: string;
  862. uomDesc: string;
  863. // Lot Information
  864. lotId: number;
  865. lotNo: string;
  866. expiryDate: string;
  867. location: string;
  868. outQty: number;
  869. holdQty: number;
  870. stockUnit: string;
  871. availableQty: number;
  872. requiredQty: number;
  873. actualPickQty: number;
  874. totalPickedByAllPickOrders: number;
  875. suggestedPickLotId: number;
  876. lotStatus: string;
  877. stockOutLineId?: number;
  878. stockOutLineStatus?: string;
  879. stockOutLineQty?: number;
  880. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected';
  881. processingStatus: string;
  882. }
  883. interface SuggestionWithStatus {
  884. suggestionId: number;
  885. suggestionQty: number;
  886. suggestionCreated: string;
  887. lotLineId: number;
  888. lotNo: string;
  889. expiryDate: string;
  890. location: string;
  891. stockOutLineId?: number;
  892. stockOutLineStatus?: string;
  893. stockOutLineQty?: number;
  894. suggestionStatus: 'active' | 'completed' | 'rejected' | 'in_progress' | 'unknown';
  895. }
  896. // 在 actions.ts 中修改接口定义
  897. export interface FGPickOrderHierarchicalResponse {
  898. fgInfo: {
  899. doPickOrderId: number;
  900. ticketNo: string;
  901. storeId: string;
  902. shopCode: string;
  903. shopName: string;
  904. truckLanceCode: string;
  905. departureTime: string;
  906. };
  907. pickOrders: Array<{
  908. pickOrderId: number;
  909. pickOrderCode: string;
  910. doOrderId: number;
  911. deliveryOrderCode: string;
  912. consoCode: string;
  913. status: string;
  914. targetDate: string;
  915. pickOrderLines: Array<{
  916. id: number;
  917. requiredQty: number;
  918. status: string;
  919. item: {
  920. id: number;
  921. code: string;
  922. name: string;
  923. uomCode: string;
  924. uomDesc: string;
  925. };
  926. lots: Array<any>; // 可以是空数组
  927. }>;
  928. }>;
  929. }
  930. export interface CheckCompleteResponse {
  931. id: number | null;
  932. name: string;
  933. code: string;
  934. type?: string;
  935. message: string | null;
  936. errorPosition: string;
  937. }
  938. export interface LotSubstitutionConfirmRequest {
  939. pickOrderLineId: number;
  940. stockOutLineId: number;
  941. originalSuggestedPickLotId: number;
  942. newInventoryLotNo: string;
  943. }
  944. export const confirmLotSubstitution = async (data: LotSubstitutionConfirmRequest) => {
  945. const response = await serverFetchJson<PostPickOrderResponse>(
  946. `${BASE_API_URL}/pickOrder/lot-substitution/confirm`,
  947. {
  948. method: "POST",
  949. body: JSON.stringify(data),
  950. headers: { "Content-Type": "application/json" },
  951. },
  952. );
  953. revalidateTag("pickorder");
  954. return response;
  955. };
  956. export const checkAndCompletePickOrderByConsoCode = async (consoCode: string): Promise<CheckCompleteResponse> => {
  957. const response = await serverFetchJson<CheckCompleteResponse>(
  958. `${BASE_API_URL}/pickOrder/check-complete/${consoCode}`,
  959. {
  960. method: "POST",
  961. headers: {
  962. "Content-Type": "application/json",
  963. },
  964. },
  965. );
  966. revalidateTag("pickorder");
  967. return response;
  968. };
  969. export const fetchPickOrderDetailsOptimized = cache(async (userId?: number) => {
  970. const url = userId
  971. ? `${BASE_API_URL}/pickOrder/detail-optimized?userId=${userId}`
  972. : `${BASE_API_URL}/pickOrder/detail-optimized`;
  973. return serverFetchJson<any[]>(
  974. url,
  975. {
  976. method: "GET",
  977. next: { tags: ["pickorder"] },
  978. },
  979. );
  980. });
  981. const fetchSuggestionsWithStatus = async (pickOrderLineId: number) => {
  982. try {
  983. const response = await fetch(`/api/suggestedPickLot/suggestions-with-status/${pickOrderLineId}`);
  984. const suggestions: SuggestionWithStatus[] = await response.json();
  985. return suggestions;
  986. } catch (error) {
  987. console.error('Error fetching suggestions with status:', error);
  988. return [];
  989. }
  990. };
  991. export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): Promise<any> => {
  992. try {
  993. console.log("🔍 Fetching hierarchical pick order lots for userId:", userId);
  994. const data = await serverFetchJson<any>(
  995. `${BASE_API_URL}/pickOrder/all-lots-hierarchical/${userId}`,
  996. {
  997. method: 'GET',
  998. next: { tags: ["pickorder"] },
  999. }
  1000. );
  1001. console.log(" Fetched hierarchical lot details:", data);
  1002. return data;
  1003. } catch (error) {
  1004. console.error("❌ Error fetching hierarchical lot details:", error);
  1005. return {
  1006. pickOrder: null,
  1007. pickOrderLines: []
  1008. };
  1009. }
  1010. });
  1011. export const fetchLotDetailsByDoPickOrderRecordId = async (doPickOrderRecordId: number): Promise<{
  1012. fgInfo: any;
  1013. pickOrders: any[];
  1014. }> => {
  1015. try {
  1016. console.log("🔍 Fetching lot details for doPickOrderRecordId:", doPickOrderRecordId);
  1017. const data = await serverFetchJson<{
  1018. fgInfo: any;
  1019. pickOrders: any[];
  1020. }>(
  1021. `${BASE_API_URL}/pickOrder/lot-details-by-do-pick-order-record/${doPickOrderRecordId}`,
  1022. {
  1023. method: 'GET',
  1024. next: { tags: ["pickorder"] },
  1025. }
  1026. );
  1027. console.log(" Fetched hierarchical lot details:", data);
  1028. return data;
  1029. } catch (error) {
  1030. console.error("❌ Error fetching lot details:", error);
  1031. return {
  1032. fgInfo: null,
  1033. pickOrders: []
  1034. };
  1035. }
  1036. };
  1037. export const fetchAllPickOrderDetails = cache(async (userId?: number) => {
  1038. if (!userId) {
  1039. return {
  1040. consoCode: null,
  1041. pickOrders: [],
  1042. items: []
  1043. };
  1044. }
  1045. // Use the correct endpoint with userId in the path
  1046. const url = `${BASE_API_URL}/pickOrder/detail-optimized/${userId}`;
  1047. return serverFetchJson<GetPickOrderInfoResponse>(
  1048. url,
  1049. {
  1050. method: "GET",
  1051. next: { tags: ["pickorder"] },
  1052. },
  1053. );
  1054. });
  1055. export const fetchPickOrderLineLotDetails = cache(async (pickOrderLineId: number) => {
  1056. return serverFetchJson<PickOrderLotDetailResponse[]>(
  1057. `${BASE_API_URL}/pickOrder/lot-details/${pickOrderLineId}`,
  1058. {
  1059. method: "GET",
  1060. next: { tags: ["pickorder"] },
  1061. },
  1062. );
  1063. });
  1064. export const createPickOrder = async (data: SavePickOrderRequest) => {
  1065. console.log(data);
  1066. const po = await serverFetchJson<PostPickOrderResponse>(
  1067. `${BASE_API_URL}/pickOrder/create`,
  1068. {
  1069. method: "POST",
  1070. body: JSON.stringify(data),
  1071. headers: { "Content-Type": "application/json" },
  1072. },
  1073. );
  1074. revalidateTag("pickorder");
  1075. return po;
  1076. }
  1077. export const assignPickOrder = async (ids: number[]) => {
  1078. const pickOrder = await serverFetchJson<any>(
  1079. `${BASE_API_URL}/pickOrder/conso`,
  1080. {
  1081. method: "POST",
  1082. body: JSON.stringify({ ids: ids }),
  1083. headers: { "Content-Type": "application/json" },
  1084. },
  1085. );
  1086. // revalidateTag("po");
  1087. return pickOrder;
  1088. };
  1089. export const consolidatePickOrder = async (ids: number[]) => {
  1090. const pickOrder = await serverFetchJson<any>(
  1091. `${BASE_API_URL}/pickOrder/conso`,
  1092. {
  1093. method: "POST",
  1094. body: JSON.stringify({ ids: ids }),
  1095. headers: { "Content-Type": "application/json" },
  1096. },
  1097. );
  1098. return pickOrder;
  1099. };
  1100. export const consolidatePickOrder_revert = async (ids: number[]) => {
  1101. const pickOrder = await serverFetchJson<any>(
  1102. `${BASE_API_URL}/pickOrder/deconso`,
  1103. {
  1104. method: "POST",
  1105. body: JSON.stringify({ ids: ids }),
  1106. headers: { "Content-Type": "application/json" },
  1107. },
  1108. );
  1109. // revalidateTag("po");
  1110. return pickOrder;
  1111. };
  1112. export const fetchPickOrderClient = cache(
  1113. async (queryParams?: Record<string, any>) => {
  1114. if (queryParams) {
  1115. const queryString = new URLSearchParams(queryParams).toString();
  1116. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1117. `${BASE_API_URL}/pickOrder/getRecordByPage?${queryString}`,
  1118. {
  1119. method: "GET",
  1120. next: { tags: ["pickorder"] },
  1121. },
  1122. );
  1123. } else {
  1124. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1125. `${BASE_API_URL}/pickOrder/getRecordByPage`,
  1126. {
  1127. method: "GET",
  1128. next: { tags: ["pickorder"] },
  1129. },
  1130. );
  1131. }
  1132. },
  1133. );
  1134. export const fetchPickOrderWithStockClient = cache(
  1135. async (queryParams?: Record<string, any>) => {
  1136. if (queryParams) {
  1137. const queryString = new URLSearchParams(queryParams).toString();
  1138. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1139. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock?${queryString}`,
  1140. {
  1141. method: "GET",
  1142. next: { tags: ["pickorder"] },
  1143. },
  1144. );
  1145. } else {
  1146. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1147. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock`,
  1148. {
  1149. method: "GET",
  1150. next: { tags: ["pickorder"] },
  1151. },
  1152. );
  1153. }
  1154. },
  1155. );
  1156. export const fetchConsoPickOrderClient = cache(
  1157. async (queryParams?: Record<string, any>) => {
  1158. if (queryParams) {
  1159. const queryString = new URLSearchParams(queryParams).toString();
  1160. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1161. `${BASE_API_URL}/pickOrder/getRecordByPage-conso?${queryString}`,
  1162. {
  1163. method: "GET",
  1164. next: { tags: ["pickorder"] },
  1165. },
  1166. );
  1167. } else {
  1168. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1169. `${BASE_API_URL}/pickOrder/getRecordByPage-conso`,
  1170. {
  1171. method: "GET",
  1172. next: { tags: ["pickorder"] },
  1173. },
  1174. );
  1175. }
  1176. },
  1177. );
  1178. export const fetchPickOrderLineClient = cache(
  1179. async (queryParams?: Record<string, any>) => {
  1180. if (queryParams) {
  1181. const queryString = new URLSearchParams(queryParams).toString();
  1182. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1183. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`,
  1184. {
  1185. method: "GET",
  1186. next: { tags: ["pickorder"] },
  1187. },
  1188. );
  1189. } else {
  1190. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1191. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage`,
  1192. {
  1193. method: "GET",
  1194. next: { tags: ["pickorder"] },
  1195. },
  1196. );
  1197. }
  1198. },
  1199. );
  1200. export const fetchStockOutLineClient = cache(
  1201. async (pickOrderLineId: number) => {
  1202. return serverFetchJson<StockOutLine[]>(
  1203. `${BASE_API_URL}/stockOutLine/getByPickOrderLineId/${pickOrderLineId}`,
  1204. {
  1205. method: "GET",
  1206. next: { tags: ["pickorder"] },
  1207. },
  1208. );
  1209. },
  1210. );
  1211. export const fetchConsoDetail = cache(async (consoCode: string) => {
  1212. return serverFetchJson<PreReleasePickOrderSummary>(
  1213. `${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`,
  1214. {
  1215. method: "GET",
  1216. next: { tags: ["pickorder"] },
  1217. },
  1218. );
  1219. });
  1220. export const releasePickOrder = async (data: ReleasePickOrderInputs) => {
  1221. console.log(data);
  1222. console.log(JSON.stringify(data));
  1223. const po = await serverFetchJson<{ consoCode: string }>(
  1224. `${BASE_API_URL}/pickOrder/releaseConso`,
  1225. {
  1226. method: "POST",
  1227. body: JSON.stringify(data),
  1228. headers: { "Content-Type": "application/json" },
  1229. },
  1230. );
  1231. revalidateTag("pickorder");
  1232. return po;
  1233. };
  1234. export const createStockOutLine = async (data: CreateStockOutLine) => {
  1235. console.log("triggering");
  1236. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1237. `${BASE_API_URL}/stockOutLine/create`,
  1238. {
  1239. method: "POST",
  1240. body: JSON.stringify(data),
  1241. headers: { "Content-Type": "application/json" },
  1242. },
  1243. );
  1244. revalidateTag("pickorder");
  1245. return po;
  1246. };
  1247. export const updateStockOutLine = async (data: UpdateStockOutLine) => {
  1248. console.log(data);
  1249. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1250. `${BASE_API_URL}/stockOutLine/update`,
  1251. {
  1252. method: "POST",
  1253. body: JSON.stringify(data),
  1254. headers: { "Content-Type": "application/json" },
  1255. },
  1256. );
  1257. revalidateTag("pickorder");
  1258. return po;
  1259. };
  1260. export const completeConsoPickOrder = async (consoCode: string) => {
  1261. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1262. `${BASE_API_URL}/pickOrder/consoPickOrder/complete/${consoCode}`,
  1263. {
  1264. method: "POST",
  1265. headers: { "Content-Type": "application/json" },
  1266. },
  1267. );
  1268. revalidateTag("pickorder");
  1269. return po;
  1270. };
  1271. export const fetchConsoStatus = cache(async (consoCode: string) => {
  1272. return serverFetchJson<{ status: string }>(
  1273. `${BASE_API_URL}/stockOut/get-status/${consoCode}`,
  1274. {
  1275. method: "GET",
  1276. next: { tags: ["pickorder"] },
  1277. },
  1278. );
  1279. });
  1280. export interface ReleasedDoPickOrderResponse {
  1281. id: number;
  1282. storeId: string;
  1283. ticketNo: string;
  1284. pickOrderId: number;
  1285. ticketStatus: string;
  1286. doOrderId: number;
  1287. shopId: number;
  1288. handledBy: number;
  1289. ticketReleaseTime: string;
  1290. }
  1291. export const fetchReleasedDoPickOrders = async (): Promise<ReleasedDoPickOrderResponse[]> => {
  1292. const response = await serverFetchJson<ReleasedDoPickOrderResponse[]>(
  1293. `${BASE_API_URL}/doPickOrder/released`,
  1294. {
  1295. method: "GET",
  1296. },
  1297. );
  1298. return response;
  1299. };