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.
 
 

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