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.

actions.ts 43 KiB

9 miesięcy temu
7 miesięcy temu
9 miesięcy temu
9 miesięcy temu
6 miesięcy temu
9 miesięcy temu
7 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
8 miesięcy temu
7 miesięcy temu
8 miesięcy temu
5 miesięcy temu
8 miesięcy temu
7 miesięcy temu
5 miesięcy temu
8 miesięcy temu
7 miesięcy temu
8 miesięcy temu
5 miesięcy temu
8 miesięcy temu
5 miesięcy temu
8 miesięcy temu
7 miesięcy temu
4 miesięcy temu
8 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
6 miesięcy temu
7 miesięcy temu
5 miesięcy temu
7 miesięcy temu
2 miesięcy temu
7 miesięcy temu
7 miesięcy temu
6 miesięcy temu
7 miesięcy temu
6 miesięcy temu
7 miesięcy temu
7 miesięcy temu
5 miesięcy temu
7 miesięcy temu
5 miesięcy temu
5 miesięcy temu
5 miesięcy temu
5 miesięcy temu
5 miesięcy temu
5 miesięcy temu
5 miesięcy temu
5 miesięcy temu
5 miesięcy temu
7 miesięcy temu
6 miesięcy temu
7 miesięcy temu
6 miesięcy temu
6 miesięcy temu
7 miesięcy temu
5 miesięcy temu
7 miesięcy temu
5 miesięcy temu
6 miesięcy temu
6 miesięcy temu
5 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
6 miesięcy temu
6 miesięcy temu
6 miesięcy temu
6 miesięcy temu
1 miesiąc temu
6 miesięcy temu
4 miesięcy temu
4 miesięcy temu
4 tygodni temu
4 miesięcy temu
4 miesięcy temu
4 miesięcy temu
4 miesięcy temu
4 miesięcy temu
2 miesięcy temu
4 miesięcy temu
2 miesięcy temu
2 miesięcy temu
5 miesięcy temu
6 miesięcy temu
4 miesięcy temu
6 miesięcy temu
1 miesiąc temu
4 miesięcy temu
1 miesiąc temu
6 miesięcy temu
6 miesięcy temu
1 miesiąc temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
6 miesięcy temu
6 miesięcy temu
6 miesięcy temu
7 miesięcy temu
6 miesięcy temu
7 miesięcy temu
7 miesięcy temu
6 miesięcy temu
6 miesięcy temu
6 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
8 miesięcy temu
4 miesięcy temu
8 miesięcy temu
4 miesięcy temu
8 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
5 miesięcy temu
7 miesięcy temu
6 miesięcy temu
4 miesięcy temu
2 miesięcy temu
6 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
7 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
5 miesięcy temu
6 miesięcy temu
7 miesięcy temu
5 miesięcy temu
7 miesięcy temu
8 miesięcy temu
7 miesięcy temu
8 miesięcy temu
9 miesięcy temu
9 miesięcy temu
7 miesięcy temu
7 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
7 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
9 miesięcy temu
1 miesiąc temu
1 miesiąc temu
1 miesiąc temu
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549
  1. "use server";
  2. import { BASE_API_URL } from "@/config/api";
  3. // import { ServerFetchError, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
  4. import { revalidateTag } from "next/cache";
  5. import { cache } from "react";
  6. import { serverFetchJson } from "@/app/utils/fetchUtil";
  7. import { QcItemResult } from "../settings/qcItem";
  8. import { RecordsRes } from "../utils";
  9. import {
  10. ConsoPickOrderResult,
  11. PickOrderLineWithSuggestedLot,
  12. PickOrderResult,
  13. PreReleasePickOrderSummary,
  14. StockOutLine,
  15. } from ".";
  16. import { PurchaseQcResult } from "../po/actions";
  17. import { StringNullableChain } from "lodash";
  18. // import { BASE_API_URL } from "@/config/api";
  19. import dayjs from "dayjs";
  20. export interface SavePickOrderLineRequest {
  21. itemId: number
  22. qty: number
  23. uomId: number
  24. }
  25. export interface SavePickOrderRequest {
  26. type: string
  27. targetDate: string
  28. pickOrderLine: SavePickOrderLineRequest[]
  29. }
  30. export interface PostPickOrderResponse<T = null> {
  31. id: number | null;
  32. name: string;
  33. code: string;
  34. type?: string;
  35. message: string | null;
  36. errorPosition: string
  37. entity?: T | T[];
  38. consoCode?: string;
  39. }
  40. export interface PostStockOutLiineResponse<T> {
  41. id: number | null;
  42. name: string;
  43. code: string;
  44. type?: string;
  45. message: string | null;
  46. errorPosition: string | keyof T;
  47. entity: T | T[] | null;
  48. }
  49. export interface ReleasePickOrderInputs {
  50. consoCode: string;
  51. assignTo: number;
  52. }
  53. export interface CreateStockOutLine {
  54. consoCode: string;
  55. pickOrderLineId: number;
  56. inventoryLotLineId: number;
  57. qty: number;
  58. }
  59. export interface UpdateStockOutLine {
  60. id: number;
  61. // consoCode: String,
  62. itemId: number;
  63. qty: number;
  64. pickOrderLineId: number;
  65. inventoryLotLineId?: number;
  66. status: string;
  67. pickTime?: string;
  68. // pickerId: number?
  69. }
  70. export interface PickOrderQcInput {
  71. qty: number;
  72. status: string;
  73. qcResult: PurchaseQcResult[];
  74. }
  75. export interface PickOrderApprovalInput {
  76. allowQty: number;
  77. rejectQty: number;
  78. status: string;
  79. }
  80. export interface GetPickOrderInfoResponse {
  81. consoCode: string | null;
  82. pickOrders: GetPickOrderInfo[];
  83. items: CurrentInventoryItemInfo[];
  84. }
  85. export interface GetPickOrderInfo {
  86. id: number;
  87. code: string;
  88. consoCode: string | null; // 添加 consoCode 属性
  89. targetDate: string | number[]; // Support both formats
  90. type: string;
  91. status: string;
  92. assignTo: number;
  93. groupName: string; // Add this field
  94. pickOrderLines: GetPickOrderLineInfo[];
  95. }
  96. export interface GetPickOrderLineInfo {
  97. id: number;
  98. itemId: number;
  99. itemCode: string;
  100. itemName: string;
  101. availableQty: number| null;
  102. requiredQty: number;
  103. uomShortDesc: string;
  104. uomDesc: string;
  105. suggestedList: any[];
  106. pickedQty: number;
  107. noLotLines: NoLotLineDto[];
  108. }
  109. export interface NoLotLineDto {
  110. stockOutLineId: number;
  111. status: string;
  112. qty: number;
  113. created: string;
  114. modified: string;
  115. }
  116. export interface CurrentInventoryItemInfo {
  117. id: number;
  118. code: string;
  119. name: string;
  120. uomDesc: string;
  121. availableQty: number;
  122. requiredQty: number;
  123. }
  124. export interface SavePickOrderGroupRequest {
  125. groupIds?: number[];
  126. names?: string[];
  127. targetDate?: string;
  128. pickOrderId?: number | null;
  129. }
  130. export interface PickOrderGroupInfo {
  131. id: number;
  132. name: string;
  133. targetDate: string | null;
  134. pickOrderId: number | null;
  135. }
  136. export interface AssignPickOrderInputs {
  137. pickOrderIds: number[];
  138. assignTo: number;
  139. }
  140. export interface LotDetailWithStockOutLine {
  141. lotId: number;
  142. lotNo: string;
  143. expiryDate: string;
  144. location: string;
  145. stockUnit: string;
  146. availableQty: number;
  147. requiredQty: number;
  148. actualPickQty: number;
  149. suggestedPickLotId: number;
  150. lotStatus: string;
  151. lotAvailability: string;
  152. stockOutLineId?: number;
  153. stockOutLineStatus?: string;
  154. stockOutLineQty?: number;
  155. }
  156. export interface PickAnotherLotFormData {
  157. pickOrderLineId: number;
  158. lotId: number;
  159. qty: number;
  160. type: string;
  161. handlerId?: number;
  162. category?: string;
  163. releasedBy?: number;
  164. recordDate?: string;
  165. }
  166. export const recordFailLot = async (data: PickAnotherLotFormData) => {
  167. const result = await serverFetchJson<PostPickOrderResponse>(
  168. `${BASE_API_URL}/suggestedPickLot/recordFailLot`,
  169. {
  170. method: "POST",
  171. body: JSON.stringify(data),
  172. headers: { "Content-Type": "application/json" },
  173. },
  174. );
  175. revalidateTag("pickorder");
  176. return result;
  177. };
  178. export interface PickExecutionIssueData {
  179. type: string;
  180. pickOrderId: number;
  181. pickOrderCode: string;
  182. pickOrderCreateDate: string;
  183. pickExecutionDate: string;
  184. pickOrderLineId: number;
  185. itemId: number;
  186. itemCode: string;
  187. itemDescription: string;
  188. lotId: number|null;
  189. lotNo: string|null;
  190. storeLocation: string;
  191. requiredQty: number;
  192. actualPickQty: number;
  193. missQty: number;
  194. badItemQty: number;
  195. badPackageQty?: number;
  196. /** Optional: frontend-only reference to stock_out_line.id for the picked lot. */
  197. stockOutLineId?: number;
  198. issueRemark: string;
  199. pickerName: string;
  200. handledBy?: number;
  201. badReason?: string;
  202. reason?: string;
  203. }
  204. export type AutoAssignReleaseResponse = {
  205. id: number | null;
  206. name?: string | null;
  207. code?: string | null;
  208. type?: string | null;
  209. message?: string | null;
  210. errorPosition?: string | null;
  211. entity?: any;
  212. };
  213. export interface PickOrderCompletionResponse {
  214. id: number | null;
  215. name: string;
  216. code: string;
  217. type?: string;
  218. message: string | null;
  219. errorPosition: string;
  220. entity?: {
  221. hasCompletedOrders: boolean;
  222. completedOrders: Array<{
  223. pickOrderId: number;
  224. pickOrderCode: string;
  225. consoCode: string;
  226. isCompleted: boolean;
  227. stockOutStatus: string;
  228. totalLines: number;
  229. unfinishedLines: number;
  230. }>;
  231. allOrders: Array<{
  232. pickOrderId: number;
  233. pickOrderCode: string;
  234. consoCode: string;
  235. isCompleted: boolean;
  236. stockOutStatus: string;
  237. totalLines: number;
  238. unfinishedLines: number;
  239. }>;
  240. };
  241. }
  242. export interface UpdateSuggestedLotLineIdRequest {
  243. newLotLineId: number;
  244. }
  245. export interface stockReponse{
  246. id: number;
  247. status: string;
  248. qty: number;
  249. lotId: number;
  250. lotNo: string;
  251. location: string;
  252. availableQty: number;
  253. noLot: boolean;
  254. }
  255. export interface FGPickOrderResponse {
  256. // 新增:支持多个 pick orders
  257. doPickOrderId: number;
  258. pickOrderIds?: number[];
  259. pickOrderCodes?: string[]; // 改为数组
  260. deliveryOrderIds?: number[];
  261. deliveryNos?: string[]; // 改为数组
  262. numberOfPickOrders?: number;
  263. lineCountsPerPickOrder?: number[];// 新增:pick order 数量
  264. // 保留原有字段用于向后兼容(显示第一个 pick order)
  265. pickOrderId: number;
  266. pickOrderCode: string;
  267. pickOrderConsoCode: string;
  268. pickOrderTargetDate: string;
  269. pickOrderStatus: string;
  270. deliveryOrderId: number;
  271. deliveryNo: string;
  272. deliveryDate: string;
  273. shopId: number;
  274. shopCode: string;
  275. shopName: string;
  276. shopAddress: string;
  277. ticketNo: string;
  278. shopPoNo: string;
  279. numberOfCartons: number;
  280. DepartureTime: string;
  281. truckLanceCode: string;
  282. storeId: string;
  283. qrCodeData: number;
  284. }
  285. export interface DoPickOrderDetail {
  286. doPickOrder: {
  287. id: number;
  288. store_id: string;
  289. ticket_no: string;
  290. ticket_status: string;
  291. truck_id: number;
  292. truck_departure_time: string;
  293. shop_id: number;
  294. handled_by: number | null;
  295. loading_sequence: number;
  296. ticket_release_time: string | null;
  297. TruckLanceCode: string;
  298. ShopCode: string;
  299. ShopName: string;
  300. RequiredDeliveryDate: string;
  301. };
  302. pickOrders: Array<{
  303. pick_order_id: number;
  304. pick_order_code: string;
  305. do_order_id: number;
  306. delivery_order_code: string;
  307. consoCode: string;
  308. status: string;
  309. targetDate: string;
  310. }>;
  311. selectedPickOrderId: number;
  312. lotDetails: any[]; // 使用现有的 lot detail 结构
  313. pickOrderCodes?: string;
  314. deliveryNos?: string;
  315. }
  316. export interface AutoAssignReleaseByStoreRequest {
  317. userId: number;
  318. storeId: string; // "2/F" | "4/F"
  319. }
  320. export interface UpdateDoPickOrderHideStatusRequest {
  321. id: number;
  322. name: string;
  323. code: string;
  324. type: string;
  325. message: string;
  326. errorPosition: string;
  327. }
  328. export interface CompletedDoPickOrderResponse {
  329. id: number;
  330. doPickOrderRecordId: number; // ✅ 新增
  331. recordId: number | null;
  332. pickOrderId: number;
  333. pickOrderIds: number[]; // 新增:所有 pick order IDs
  334. pickOrderCode: string;
  335. pickOrderCodes: string; // 新增:所有 pick order codes (逗号分隔)
  336. pickOrderConsoCode: string;
  337. pickOrderStatus: string;
  338. deliveryOrderId: number;
  339. deliveryOrderIds: number[]; // 新增:所有 delivery order IDs
  340. deliveryNo: string;
  341. deliveryNos: string; // 新增:所有 delivery order codes (逗号分隔)
  342. deliveryDate: string;
  343. shopId: number;
  344. shopCode: string;
  345. shopName: string;
  346. shopAddress: string;
  347. ticketNo: string;
  348. shopPoNo: string;
  349. numberOfCartons: number;
  350. truckLanceCode: string;
  351. DepartureTime: string; // 新增
  352. storeId: string;
  353. completedDate: string;
  354. fgPickOrders: FGPickOrderResponse[];
  355. deliveryNoteCode: number;
  356. }
  357. // 新增:搜索参数接口
  358. export interface CompletedDoPickOrderSearchParams {
  359. targetDate?: string;
  360. shopName?: string;
  361. deliveryNoteCode?: string;
  362. /** 卡車/車道(後端 truckLanceCode 模糊匹配) */
  363. truckLanceCode?: string;
  364. }
  365. export interface PickExecutionIssue {
  366. id: number;
  367. pickOrderId: number;
  368. pickOrderCode: string;
  369. pickOrderCreateDate: string;
  370. pickExecutionDate: string;
  371. pickOrderLineId: number;
  372. issueNo: string;
  373. joPickOrderId?: number;
  374. doPickOrderId?: number;
  375. issueCategory: string;
  376. itemId: number;
  377. itemCode: string;
  378. itemDescription: string;
  379. lotId?: number;
  380. lotNo?: string;
  381. storeLocation?: string;
  382. requiredQty: number;
  383. actualPickQty?: number;
  384. missQty?: number;
  385. badItemQty?: number;
  386. issueRemark?: string;
  387. pickerName?: string;
  388. handleStatus: string;
  389. handleDate?: string;
  390. handledBy?: string;
  391. created: string;
  392. createdBy: string;
  393. modified: string;
  394. modifiedBy: string;
  395. }
  396. export interface FetchPickExecutionIssuesParams {
  397. type?: "jo" | "do" | "material";
  398. }
  399. export const fetchAllPickExecutionIssues = cache(
  400. async (params?: FetchPickExecutionIssuesParams): Promise<PickExecutionIssue[]> => {
  401. const queryParams = new URLSearchParams();
  402. if (params?.type) {
  403. queryParams.append("type", params.type);
  404. }
  405. const url = `${BASE_API_URL}/pickExecution/issues/all${
  406. queryParams.toString() ? `?${queryParams.toString()}` : ""
  407. }`;
  408. const result = await serverFetchJson<PickExecutionIssue[]>(url, {
  409. method: "GET",
  410. headers: { "Content-Type": "application/json" },
  411. });
  412. return result ?? [];
  413. }
  414. );
  415. export interface UpdatePickExecutionIssueRequest {
  416. issueId: number;
  417. handleStatus: string;
  418. handleDate?: string;
  419. handledBy?: string;
  420. handleRemark?: string;
  421. }
  422. export interface StoreLaneSummary {
  423. storeId: string;
  424. rows: LaneRow[];
  425. defaultTruckCount: number | null;
  426. }
  427. export interface LaneRow {
  428. truckDepartureTime: string;
  429. lanes: LaneBtn[];
  430. }
  431. export interface LaneBtn {
  432. truckLanceCode: string;
  433. unassigned: number;
  434. total: number;
  435. }
  436. export interface QrPickBatchSubmitRequest {
  437. userId: number;
  438. lines: QrPickSubmitLineRequest[];
  439. }
  440. export interface QrPickSubmitLineRequest {
  441. stockOutLineId: number;
  442. pickOrderLineId: number;
  443. inventoryLotLineId: number | null; // ✅ 修复:应该是 nullable
  444. requiredQty: number | null; // ✅ 修复:添加 requiredQty
  445. actualPickQty: number | null; // ✅ 修复:添加 actualPickQty
  446. stockOutLineStatus: string | null; // ✅ 修复:添加 stockOutLineStatus
  447. pickOrderConsoCode: string | null; // ✅ 修复:添加 pickOrderConsoCode
  448. noLot: boolean; // ✅ 修复:添加 noLot
  449. }
  450. export interface UpdateStockOutLineStatusByQRCodeAndLotNoRequest {
  451. pickOrderLineId: number,
  452. inventoryLotNo: string,
  453. stockInLineId?: number | null,
  454. stockOutLineId: number,
  455. itemId: number,
  456. status: string
  457. }
  458. export interface batchSubmitListRequest {
  459. userId: number;
  460. lines: batchSubmitListLineRequest[];
  461. }
  462. export interface batchSubmitListLineRequest {
  463. stockOutLineId: number; // 修复:改为 stockOutLineId(不是 stockInLineId)
  464. pickOrderLineId: number;
  465. inventoryLotLineId: number | null; // 添加:后端需要的字段
  466. requiredQty: number;
  467. actualPickQty: number;
  468. stockOutLineStatus: string;
  469. pickOrderConsoCode: string;
  470. noLot: boolean;
  471. // 移除:lotNo 和 stockInLineId(后端不需要)
  472. }
  473. export const batchSubmitList = async (data: batchSubmitListRequest) => {
  474. // ✅ 确保发送的是对象,不是数组
  475. const requestBody = Array.isArray(data) ? data[0] : data;
  476. console.log("📤 batchSubmitList - Request body type:", Array.isArray(requestBody) ? "array" : "object");
  477. console.log("📤 batchSubmitList - Request body:", JSON.stringify(requestBody, null, 2));
  478. const response = await serverFetchJson<PostPickOrderResponse<batchSubmitListRequest>>(
  479. `${BASE_API_URL}/stockOutLine/batchSubmitList`,
  480. {
  481. method: "POST",
  482. body: JSON.stringify(requestBody), // ✅ 确保是对象
  483. headers: {
  484. "Content-Type": "application/json", // ✅ 明确指定 Content-Type
  485. },
  486. },
  487. );
  488. return response;
  489. };
  490. export const updateStockOutLineStatusByQRCodeAndLotNo = async (data: UpdateStockOutLineStatusByQRCodeAndLotNoRequest) => {
  491. console.log(" Frontend: Calling updateStockOutLineStatusByQRCodeAndLotNo with data:", data);
  492. try {
  493. const response = await serverFetchJson<PostPickOrderResponse<UpdateStockOutLineStatusByQRCodeAndLotNoRequest>>(
  494. `${BASE_API_URL}/stockOutLine/updateStatusByQRCodeAndLotNo`,
  495. {
  496. method: "POST",
  497. headers: {
  498. "Content-Type": "application/json",
  499. },
  500. body: JSON.stringify(data),
  501. },
  502. );
  503. console.log("✅ Frontend: API call successful, response:", response);
  504. return response;
  505. } catch (error) {
  506. console.error("❌ Frontend: API call failed:", error);
  507. throw error;
  508. }
  509. };
  510. export const batchQrSubmit = async (data: QrPickBatchSubmitRequest) => {
  511. const response = await serverFetchJson<PostPickOrderResponse<QrPickBatchSubmitRequest>>(
  512. `${BASE_API_URL}/stockOutLine/batchQrSubmit`,
  513. {
  514. method: "POST",
  515. body: JSON.stringify(data),
  516. },
  517. );
  518. return response;
  519. };
  520. export interface BatchScanRequest {
  521. userId: number;
  522. lines: BatchScanLineRequest[];
  523. }
  524. export interface BatchScanLineRequest {
  525. pickOrderLineId: number;
  526. inventoryLotLineId: number | null; // 如果有 lot,提供 lotId;如果没有则为 null
  527. pickOrderConsoCode: string;
  528. lotNo: string | null; // 用于日志和验证
  529. itemId: number;
  530. itemCode: string;
  531. stockOutLineId: number | null; // ✅ 新增:如果已有 stockOutLineId,直接使用
  532. }
  533. export const batchScan = async (data: BatchScanRequest) => {
  534. console.log("📤 batchScan - Request body:", JSON.stringify(data, null, 2));
  535. const response = await serverFetchJson<PostPickOrderResponse<BatchScanRequest>>(
  536. `${BASE_API_URL}/stockOutLine/batchScan`,
  537. {
  538. method: "POST",
  539. body: JSON.stringify(data),
  540. headers: {
  541. "Content-Type": "application/json",
  542. },
  543. },
  544. );
  545. //console.log("📥 batchScan - Response:", response);
  546. return response;
  547. };
  548. export const fetchDoPickOrderDetail = async (
  549. doPickOrderId: number,
  550. selectedPickOrderId?: number
  551. ): Promise<DoPickOrderDetail> => {
  552. const url = selectedPickOrderId
  553. ? `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}?selectedPickOrderId=${selectedPickOrderId}`
  554. : `${BASE_API_URL}/pickOrder/do-pick-order-detail/${doPickOrderId}`;
  555. const response = await serverFetchJson<DoPickOrderDetail>(url, {
  556. method: "GET",
  557. });
  558. return response;
  559. };
  560. export const updatePickExecutionIssueStatus = async (
  561. data: UpdatePickExecutionIssueRequest
  562. ): Promise<PostPickOrderResponse> => {
  563. const result = await serverFetchJson<PostPickOrderResponse>(
  564. `${BASE_API_URL}/pickExecution/updateIssueStatus`,
  565. {
  566. method: "POST",
  567. body: JSON.stringify(data),
  568. headers: { "Content-Type": "application/json" },
  569. }
  570. );
  571. revalidateTag("pickExecutionIssues");
  572. return result;
  573. };
  574. export async function fetchStoreLaneSummary(storeId: string, requiredDate?: string, releaseType?: string): Promise<StoreLaneSummary> {
  575. const dateToUse = requiredDate || dayjs().format('YYYY-MM-DD');
  576. const url = `${BASE_API_URL}/doPickOrder/summary-by-store?storeId=${encodeURIComponent(storeId)}&requiredDate=${encodeURIComponent(dateToUse)}&releaseType=${encodeURIComponent(releaseType || 'all')}`;
  577. const label = `[API] fetchStoreLaneSummary ${storeId}`;
  578. console.time(label);
  579. try {
  580. const response = await serverFetchJson<StoreLaneSummary>(url, {
  581. method: "GET",
  582. cache: "no-store",
  583. next: { revalidate: 0 },
  584. });
  585. console.timeEnd(label);
  586. return response;
  587. } catch (error) {
  588. console.error(`[API] Error in fetchStoreLaneSummary ${storeId}:`, error);
  589. throw error;
  590. }
  591. }
  592. // 按车道分配订单
  593. export async function assignByLane(
  594. userId: number,
  595. storeId: string,
  596. truckLanceCode: string,
  597. truckDepartureTime?: string,
  598. requiredDate?: string
  599. ): Promise<any> {
  600. const response = await serverFetchJson(
  601. `${BASE_API_URL}/doPickOrder/assign-by-lane`,
  602. {
  603. method: "POST",
  604. headers: {
  605. "Content-Type": "application/json",
  606. },
  607. body: JSON.stringify({
  608. userId,
  609. storeId,
  610. truckLanceCode,
  611. truckDepartureTime,
  612. requiredDate,
  613. }),
  614. }
  615. );
  616. return response;
  617. }
  618. // 新增:获取已完成的 DO Pick Orders API
  619. export const fetchCompletedDoPickOrders = async (
  620. userId: number,
  621. searchParams?: CompletedDoPickOrderSearchParams
  622. ): Promise<CompletedDoPickOrderResponse[]> => {
  623. const params = new URLSearchParams();
  624. if (searchParams?.deliveryNoteCode) {
  625. params.append('deliveryNoteCode', searchParams.deliveryNoteCode);
  626. }
  627. if (searchParams?.shopName) {
  628. params.append('shopName', searchParams.shopName);
  629. }
  630. if (searchParams?.targetDate) {
  631. params.append('targetDate', searchParams.targetDate);
  632. }
  633. if (searchParams?.truckLanceCode) {
  634. params.append("truckLanceCode", searchParams.truckLanceCode);
  635. }
  636. const queryString = params.toString();
  637. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders/${userId}${queryString ? `?${queryString}` : ''}`;
  638. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  639. method: "GET",
  640. });
  641. return response;
  642. };
  643. /** 全部已完成 DO 提貨記錄(不限經手人),需後端 `/completed-do-pick-orders-all` */
  644. export const fetchCompletedDoPickOrdersAll = async (
  645. searchParams?: CompletedDoPickOrderSearchParams
  646. ): Promise<CompletedDoPickOrderResponse[]> => {
  647. const params = new URLSearchParams();
  648. if (searchParams?.deliveryNoteCode) {
  649. params.append("deliveryNoteCode", searchParams.deliveryNoteCode);
  650. }
  651. if (searchParams?.shopName) {
  652. params.append("shopName", searchParams.shopName);
  653. }
  654. if (searchParams?.targetDate) {
  655. params.append("targetDate", searchParams.targetDate);
  656. }
  657. if (searchParams?.truckLanceCode) {
  658. params.append("truckLanceCode", searchParams.truckLanceCode);
  659. }
  660. const queryString = params.toString();
  661. const url = `${BASE_API_URL}/pickOrder/completed-do-pick-orders-all${queryString ? `?${queryString}` : ""}`;
  662. const response = await serverFetchJson<CompletedDoPickOrderResponse[]>(url, {
  663. method: "GET",
  664. });
  665. return response;
  666. };
  667. /** 強制完成進行中的 do_pick_order(僅改狀態並歸檔,不調整揀貨數量) */
  668. export const forceCompleteDoPickOrder = async (
  669. doPickOrderId: number,
  670. ): Promise<PostPickOrderResponse> => {
  671. return serverFetchJson<PostPickOrderResponse>(
  672. `${BASE_API_URL}/doPickOrder/force-complete/${doPickOrderId}`,
  673. { method: "POST", headers: { "Content-Type": "application/json" } },
  674. );
  675. };
  676. /** 撤銷使用者領取,可再次分配 */
  677. export const revertDoPickOrderAssignment = async (
  678. doPickOrderId: number,
  679. ): Promise<PostPickOrderResponse> => {
  680. return serverFetchJson<PostPickOrderResponse>(
  681. `${BASE_API_URL}/doPickOrder/revert-assignment/${doPickOrderId}`,
  682. { method: "POST", headers: { "Content-Type": "application/json" } },
  683. );
  684. };
  685. export const updatePickOrderHideStatus = async (pickOrderId: number, hide: boolean) => {
  686. const response = await serverFetchJson<UpdateDoPickOrderHideStatusRequest>(
  687. `${BASE_API_URL}/pickOrder/update-hide-status/${pickOrderId}?hide=${hide}`,
  688. {
  689. method: "POST",
  690. headers: { "Content-Type": "application/json" },
  691. },
  692. );
  693. revalidateTag("pickorder");
  694. return response;
  695. };
  696. export const fetchFGPickOrders = async (pickOrderId: number) => {
  697. const response = await serverFetchJson<FGPickOrderResponse>(
  698. `${BASE_API_URL}/pickOrder/fg-pick-orders/${pickOrderId}`,
  699. {
  700. method: "GET",
  701. },
  702. );
  703. return response;
  704. };
  705. export const fetchFGPickOrdersByUserId = async (userId: number) => {
  706. const response = await serverFetchJson<FGPickOrderResponse[]>(
  707. `${BASE_API_URL}/pickOrder/fg-pick-orders/${userId}`,
  708. {
  709. method: "GET",
  710. },
  711. );
  712. return response;
  713. };
  714. /** DO workbench: FG headers from `delivery_order_pick_order`, not `do_pick_order_line`. */
  715. export const fetchFGPickOrdersByUserIdWorkbench = async (userId: number) => {
  716. return serverFetchJson<FGPickOrderResponse[]>(
  717. `${BASE_API_URL}/pickOrder/fg-pick-orders-workbench/${userId}`,
  718. { method: "GET" },
  719. );
  720. };
  721. export const updateSuggestedLotLineId = async (suggestedPickLotId: number, newLotLineId: number) => {
  722. const response = await serverFetchJson<PostPickOrderResponse<UpdateSuggestedLotLineIdRequest>>(
  723. `${BASE_API_URL}/suggestedPickLot/update-suggested-lot/${suggestedPickLotId}`,
  724. {
  725. method: "POST",
  726. body: JSON.stringify({ newLotLineId }),
  727. headers: { "Content-Type": "application/json" },
  728. },
  729. );
  730. revalidateTag("pickorder");
  731. return response;
  732. };
  733. export const autoAssignAndReleasePickOrder = async (userId: number): Promise<AutoAssignReleaseResponse> => {
  734. const response = await serverFetchJson<AutoAssignReleaseResponse>(
  735. `${BASE_API_URL}/pickOrder/auto-assign-release/${userId}`,
  736. {
  737. method: "POST",
  738. headers: { "Content-Type": "application/json" },
  739. },
  740. );
  741. revalidateTag("pickorder");
  742. return response;
  743. };
  744. export const autoAssignAndReleasePickOrderByStore = async (
  745. userId: number,
  746. storeId: string
  747. ): Promise<AutoAssignReleaseResponse> => {
  748. const url = `${BASE_API_URL}/pickOrder/auto-assign-release-by-store?userId=${userId}&storeId=${encodeURIComponent(storeId)}`;
  749. const response = await serverFetchJson<AutoAssignReleaseResponse>(url, {
  750. method: "POST",
  751. headers: { "Content-Type": "application/json" },
  752. // no body
  753. next: { tags: ["pickorder"] },
  754. });
  755. revalidateTag("pickorder");
  756. return response;
  757. };
  758. export const checkPickOrderCompletion = async (userId: number): Promise<PickOrderCompletionResponse> => {
  759. const response = await serverFetchJson<PickOrderCompletionResponse>(
  760. `${BASE_API_URL}/pickOrder/check-pick-completion/${userId}`,
  761. {
  762. method: "GET",
  763. headers: { "Content-Type": "application/json" },
  764. },
  765. );
  766. return response;
  767. };
  768. export const recordPickExecutionIssue = async (data: PickExecutionIssueData) => {
  769. const result = await serverFetchJson<PostPickOrderResponse>(
  770. `${BASE_API_URL}/pickExecution/recordIssue`,
  771. {
  772. method: "POST",
  773. body: JSON.stringify(data),
  774. headers: { "Content-Type": "application/json" },
  775. },
  776. );
  777. revalidateTag("pickorder");
  778. return result;
  779. };
  780. export const resuggestPickOrder = async (pickOrderId: number) => {
  781. console.log("Resuggesting pick order:", pickOrderId);
  782. const result = await serverFetchJson<PostPickOrderResponse>(
  783. `${BASE_API_URL}/suggestedPickLot/resuggest/${pickOrderId}`,
  784. {
  785. method: "POST",
  786. headers: { "Content-Type": "application/json" },
  787. },
  788. );
  789. revalidateTag("pickorder");
  790. return result;
  791. };
  792. export const updateStockOutLineStatus = async (data: {
  793. id: number;
  794. status: string;
  795. qty?: number;
  796. remarks?: string;
  797. }) => {
  798. console.log("Updating stock out line status:", data);
  799. const result = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  800. `${BASE_API_URL}/stockOutLine/updateStatus`,
  801. {
  802. method: "POST",
  803. body: JSON.stringify(data),
  804. headers: { "Content-Type": "application/json" },
  805. },
  806. );
  807. revalidateTag("pickorder");
  808. return result;
  809. };
  810. // Missing function 1: newassignPickOrder
  811. export const newassignPickOrder = async (data: AssignPickOrderInputs) => {
  812. const response = await serverFetchJson<PostPickOrderResponse>(
  813. `${BASE_API_URL}/pickOrder/assign`,
  814. {
  815. method: "POST",
  816. body: JSON.stringify(data),
  817. headers: { "Content-Type": "application/json" },
  818. },
  819. );
  820. revalidateTag("pickorder");
  821. return response;
  822. };
  823. // Missing function 2: releaseAssignedPickOrders
  824. export const releaseAssignedPickOrders = async (data: AssignPickOrderInputs) => {
  825. const response = await serverFetchJson<PostPickOrderResponse>(
  826. `${BASE_API_URL}/pickOrder/release-assigned`,
  827. {
  828. method: "POST",
  829. body: JSON.stringify(data),
  830. headers: { "Content-Type": "application/json" },
  831. },
  832. );
  833. revalidateTag("pickorder");
  834. return response;
  835. };
  836. // Get latest group name and create it automatically
  837. export const getLatestGroupNameAndCreate = async () => {
  838. return serverFetchJson<PostPickOrderResponse>(
  839. `${BASE_API_URL}/pickOrder/groups/latest`,
  840. {
  841. method: "GET",
  842. next: { tags: ["pickorder"] },
  843. },
  844. );
  845. };
  846. // Get all groups
  847. export const fetchAllGroups = cache(async () => {
  848. return serverFetchJson<PickOrderGroupInfo[]>(
  849. `${BASE_API_URL}/pickOrder/groups/list`,
  850. {
  851. method: "GET",
  852. next: { tags: ["pickorder"] },
  853. },
  854. );
  855. });
  856. // Create or update groups (flexible - can handle both cases)
  857. export const createOrUpdateGroups = async (data: SavePickOrderGroupRequest) => {
  858. const response = await serverFetchJson<PostPickOrderResponse>(
  859. `${BASE_API_URL}/pickOrder/groups/create`,
  860. {
  861. method: "POST",
  862. body: JSON.stringify(data),
  863. headers: { "Content-Type": "application/json" },
  864. },
  865. );
  866. revalidateTag("pickorder");
  867. return response;
  868. };
  869. // Get groups by pick order ID
  870. export const fetchGroupsByPickOrderId = cache(async (pickOrderId: number) => {
  871. return serverFetchJson<PickOrderGroupInfo[]>(
  872. `${BASE_API_URL}/pickOrder/groups/${pickOrderId}`,
  873. {
  874. method: "GET",
  875. next: { tags: ["pickorder"] },
  876. },
  877. );
  878. });
  879. export const fetchPickOrderDetails = cache(async (ids: string) => {
  880. return serverFetchJson<GetPickOrderInfoResponse>(
  881. `${BASE_API_URL}/pickOrder/detail/${ids}`,
  882. {
  883. method: "GET",
  884. next: { tags: ["pickorder"] },
  885. },
  886. );
  887. });
  888. export interface PickOrderLotDetailResponse {
  889. lotId: number | null; // ✅ 改为可空
  890. lotNo: string | null; // ✅ 改为可空
  891. expiryDate: string | null; // ✅ 改为可空
  892. location: string | null; // ✅ 改为可空
  893. stockUnit: string | null;
  894. inQty: number | null;
  895. availableQty: number | null; // ✅ 改为可空
  896. requiredQty: number;
  897. actualPickQty: number;
  898. suggestedPickLotId: number | null;
  899. lotStatus: string | null;
  900. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable' | 'rejected';
  901. stockOutLineId: number | null; // ✅ 添加
  902. stockOutLineStatus: string | null; // ✅ 添加
  903. stockOutLineQty: number | null; // ✅ 添加
  904. totalPickedByAllPickOrders: number | null; // ✅ 添加
  905. remainingAfterAllPickOrders: number | null; // ✅ 添加
  906. noLot: boolean; // ✅ 关键:添加 noLot 字段
  907. outQty?: number; // ✅ 添加
  908. holdQty?: number; // ✅ 添加
  909. }
  910. interface ALLPickOrderLotDetailResponse {
  911. // Pick Order Information
  912. pickOrderId: number;
  913. pickOrderCode: string;
  914. pickOrderTargetDate: string;
  915. pickOrderType: string;
  916. pickOrderStatus: string;
  917. pickOrderAssignTo: number;
  918. groupName: string;
  919. // Pick Order Line Information
  920. pickOrderLineId: number;
  921. pickOrderLineRequiredQty: number;
  922. pickOrderLineStatus: string;
  923. // Item Information
  924. itemId: number;
  925. itemCode: string;
  926. itemName: string;
  927. uomCode: string;
  928. uomDesc: string;
  929. // Lot Information
  930. lotId: number;
  931. lotNo: string;
  932. expiryDate: string;
  933. location: string;
  934. outQty: number;
  935. holdQty: number;
  936. stockUnit: string;
  937. availableQty: number;
  938. requiredQty: number;
  939. actualPickQty: number;
  940. totalPickedByAllPickOrders: number;
  941. suggestedPickLotId: number;
  942. lotStatus: string;
  943. stockOutLineId?: number;
  944. stockOutLineStatus?: string;
  945. stockOutLineQty?: number;
  946. lotAvailability: 'available' | 'insufficient_stock' | 'expired' | 'status_unavailable'|'rejected';
  947. processingStatus: string;
  948. }
  949. interface SuggestionWithStatus {
  950. suggestionId: number;
  951. suggestionQty: number;
  952. suggestionCreated: string;
  953. lotLineId: number;
  954. lotNo: string;
  955. expiryDate: string;
  956. location: string;
  957. stockOutLineId?: number;
  958. stockOutLineStatus?: string;
  959. stockOutLineQty?: number;
  960. suggestionStatus: 'active' | 'completed' | 'rejected' | 'in_progress' | 'unknown';
  961. }
  962. // 在 actions.ts 中修改接口定义
  963. export interface FGPickOrderHierarchicalResponse {
  964. fgInfo: {
  965. doPickOrderId: number;
  966. ticketNo: string;
  967. storeId: string;
  968. shopCode: string;
  969. shopName: string;
  970. truckLanceCode: string;
  971. departureTime: string;
  972. };
  973. pickOrders: Array<{
  974. pickOrderId: number;
  975. pickOrderCode: string;
  976. doOrderId: number;
  977. deliveryOrderCode: string;
  978. consoCode: string;
  979. status: string;
  980. targetDate: string;
  981. pickOrderLines: Array<{
  982. id: number;
  983. requiredQty: number;
  984. status: string;
  985. item: {
  986. id: number;
  987. code: string;
  988. name: string;
  989. uomCode: string;
  990. uomDesc: string;
  991. };
  992. lots: Array<any>; // 可以是空数组
  993. }>;
  994. }>;
  995. }
  996. export interface CheckCompleteResponse {
  997. id: number | null;
  998. name: string;
  999. code: string;
  1000. type?: string;
  1001. message: string | null;
  1002. errorPosition: string;
  1003. }
  1004. export interface LotSubstitutionConfirmRequest {
  1005. pickOrderLineId: number;
  1006. stockOutLineId: number;
  1007. originalSuggestedPickLotId: number;
  1008. newInventoryLotNo: string;
  1009. newStockInLineId: number;
  1010. }
  1011. export const confirmLotSubstitution = async (data: LotSubstitutionConfirmRequest) => {
  1012. const response = await serverFetchJson<PostPickOrderResponse>(
  1013. `${BASE_API_URL}/pickOrder/lot-substitution/confirm`,
  1014. {
  1015. method: "POST",
  1016. body: JSON.stringify(data),
  1017. headers: { "Content-Type": "application/json" },
  1018. },
  1019. );
  1020. revalidateTag("pickorder");
  1021. return response;
  1022. };
  1023. export const checkAndCompletePickOrderByConsoCode = async (consoCode: string): Promise<CheckCompleteResponse> => {
  1024. const response = await serverFetchJson<CheckCompleteResponse>(
  1025. `${BASE_API_URL}/pickOrder/check-complete/${consoCode}`,
  1026. {
  1027. method: "POST",
  1028. headers: {
  1029. "Content-Type": "application/json",
  1030. },
  1031. },
  1032. );
  1033. revalidateTag("pickorder");
  1034. return response;
  1035. };
  1036. export const fetchPickOrderDetailsOptimized = cache(async (userId?: number) => {
  1037. const url = userId
  1038. ? `${BASE_API_URL}/pickOrder/detail-optimized?userId=${userId}`
  1039. : `${BASE_API_URL}/pickOrder/detail-optimized`;
  1040. return serverFetchJson<any[]>(
  1041. url,
  1042. {
  1043. method: "GET",
  1044. next: { tags: ["pickorder"] },
  1045. },
  1046. );
  1047. });
  1048. const fetchSuggestionsWithStatus = async (pickOrderLineId: number) => {
  1049. try {
  1050. const response = await fetch(`/api/suggestedPickLot/suggestions-with-status/${pickOrderLineId}`);
  1051. const suggestions: SuggestionWithStatus[] = await response.json();
  1052. return suggestions;
  1053. } catch (error) {
  1054. console.error('Error fetching suggestions with status:', error);
  1055. return [];
  1056. }
  1057. };
  1058. export const fetchAllPickOrderLotsHierarchical = cache(async (userId: number): Promise<any> => {
  1059. try {
  1060. console.log("🔍 Fetching hierarchical pick order lots for userId:", userId);
  1061. const data = await serverFetchJson<any>(
  1062. `${BASE_API_URL}/pickOrder/all-lots-hierarchical/${userId}`,
  1063. {
  1064. method: 'GET',
  1065. next: { tags: ["pickorder"] },
  1066. }
  1067. );
  1068. console.log(" Fetched hierarchical lot details:", data);
  1069. return data;
  1070. } catch (error) {
  1071. console.error("❌ Error fetching hierarchical lot details:", error);
  1072. return {
  1073. pickOrder: null,
  1074. pickOrderLines: []
  1075. };
  1076. }
  1077. });
  1078. /** DO workbench: hierarchical lots where header is `delivery_order_pick_order`. */
  1079. export const fetchAllPickOrderLotsHierarchicalWorkbench = cache(async (userId: number): Promise<any> => {
  1080. try {
  1081. const data = await serverFetchJson<any>(
  1082. `${BASE_API_URL}/pickOrder/all-lots-hierarchical-workbench/${userId}`,
  1083. {
  1084. method: "GET",
  1085. next: { tags: ["pickorder"] },
  1086. },
  1087. );
  1088. return data;
  1089. } catch (error) {
  1090. console.error("❌ Error fetching workbench hierarchical lot details:", error);
  1091. return {
  1092. pickOrder: null,
  1093. pickOrderLines: [],
  1094. };
  1095. }
  1096. });
  1097. export const fetchLotDetailsByDoPickOrderRecordId = async (doPickOrderRecordId: number): Promise<{
  1098. fgInfo: any;
  1099. pickOrders: any[];
  1100. }> => {
  1101. try {
  1102. console.log("🔍 Fetching lot details for doPickOrderRecordId:", doPickOrderRecordId);
  1103. const data = await serverFetchJson<{
  1104. fgInfo: any;
  1105. pickOrders: any[];
  1106. }>(
  1107. `${BASE_API_URL}/pickOrder/lot-details-by-do-pick-order-record/${doPickOrderRecordId}`,
  1108. {
  1109. method: 'GET',
  1110. next: { tags: ["pickorder"] },
  1111. }
  1112. );
  1113. console.log(" Fetched hierarchical lot details:", data);
  1114. return data;
  1115. } catch (error) {
  1116. console.error("❌ Error fetching lot details:", error);
  1117. return {
  1118. fgInfo: null,
  1119. pickOrders: []
  1120. };
  1121. }
  1122. };
  1123. export const fetchAllPickOrderDetails = cache(async (userId?: number) => {
  1124. if (!userId) {
  1125. return {
  1126. consoCode: null,
  1127. pickOrders: [],
  1128. items: []
  1129. };
  1130. }
  1131. // Use the correct endpoint with userId in the path
  1132. const url = `${BASE_API_URL}/pickOrder/detail-optimized/${userId}`;
  1133. return serverFetchJson<GetPickOrderInfoResponse>(
  1134. url,
  1135. {
  1136. method: "GET",
  1137. next: { tags: ["pickorder"] },
  1138. },
  1139. );
  1140. });
  1141. export const fetchPickOrderLineLotDetails = cache(async (pickOrderLineId: number) => {
  1142. return serverFetchJson<PickOrderLotDetailResponse[]>(
  1143. `${BASE_API_URL}/pickOrder/lot-details/${pickOrderLineId}`,
  1144. {
  1145. method: "GET",
  1146. next: { tags: ["pickorder"] },
  1147. },
  1148. );
  1149. });
  1150. export const createPickOrder = async (data: SavePickOrderRequest) => {
  1151. console.log(data);
  1152. const po = await serverFetchJson<PostPickOrderResponse>(
  1153. `${BASE_API_URL}/pickOrder/create`,
  1154. {
  1155. method: "POST",
  1156. body: JSON.stringify(data),
  1157. headers: { "Content-Type": "application/json" },
  1158. },
  1159. );
  1160. revalidateTag("pickorder");
  1161. return po;
  1162. }
  1163. export const assignPickOrder = async (ids: number[]) => {
  1164. const pickOrder = await serverFetchJson<any>(
  1165. `${BASE_API_URL}/pickOrder/conso`,
  1166. {
  1167. method: "POST",
  1168. body: JSON.stringify({ ids: ids }),
  1169. headers: { "Content-Type": "application/json" },
  1170. },
  1171. );
  1172. // revalidateTag("po");
  1173. return pickOrder;
  1174. };
  1175. export const consolidatePickOrder = async (ids: number[]) => {
  1176. const pickOrder = await serverFetchJson<any>(
  1177. `${BASE_API_URL}/pickOrder/conso`,
  1178. {
  1179. method: "POST",
  1180. body: JSON.stringify({ ids: ids }),
  1181. headers: { "Content-Type": "application/json" },
  1182. },
  1183. );
  1184. return pickOrder;
  1185. };
  1186. export const consolidatePickOrder_revert = async (ids: number[]) => {
  1187. const pickOrder = await serverFetchJson<any>(
  1188. `${BASE_API_URL}/pickOrder/deconso`,
  1189. {
  1190. method: "POST",
  1191. body: JSON.stringify({ ids: ids }),
  1192. headers: { "Content-Type": "application/json" },
  1193. },
  1194. );
  1195. // revalidateTag("po");
  1196. return pickOrder;
  1197. };
  1198. export const fetchPickOrderClient = cache(
  1199. async (queryParams?: Record<string, any>) => {
  1200. if (queryParams) {
  1201. const queryString = new URLSearchParams(queryParams).toString();
  1202. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1203. `${BASE_API_URL}/pickOrder/getRecordByPage?${queryString}`,
  1204. {
  1205. method: "GET",
  1206. next: { tags: ["pickorder"] },
  1207. },
  1208. );
  1209. } else {
  1210. return serverFetchJson<RecordsRes<PickOrderResult[]>>(
  1211. `${BASE_API_URL}/pickOrder/getRecordByPage`,
  1212. {
  1213. method: "GET",
  1214. next: { tags: ["pickorder"] },
  1215. },
  1216. );
  1217. }
  1218. },
  1219. );
  1220. export const fetchPickOrderWithStockClient = cache(
  1221. async (queryParams?: Record<string, any>) => {
  1222. if (queryParams) {
  1223. const queryString = new URLSearchParams(queryParams).toString();
  1224. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1225. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock?${queryString}`,
  1226. {
  1227. method: "GET",
  1228. next: { tags: ["pickorder"] },
  1229. },
  1230. );
  1231. } else {
  1232. return serverFetchJson<RecordsRes<GetPickOrderInfo[]>>(
  1233. `${BASE_API_URL}/pickOrder/getRecordByPageWithStock`,
  1234. {
  1235. method: "GET",
  1236. next: { tags: ["pickorder"] },
  1237. },
  1238. );
  1239. }
  1240. },
  1241. );
  1242. export const fetchConsoPickOrderClient = cache(
  1243. async (queryParams?: Record<string, any>) => {
  1244. if (queryParams) {
  1245. const queryString = new URLSearchParams(queryParams).toString();
  1246. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1247. `${BASE_API_URL}/pickOrder/getRecordByPage-conso?${queryString}`,
  1248. {
  1249. method: "GET",
  1250. next: { tags: ["pickorder"] },
  1251. },
  1252. );
  1253. } else {
  1254. return serverFetchJson<RecordsRes<ConsoPickOrderResult[]>>(
  1255. `${BASE_API_URL}/pickOrder/getRecordByPage-conso`,
  1256. {
  1257. method: "GET",
  1258. next: { tags: ["pickorder"] },
  1259. },
  1260. );
  1261. }
  1262. },
  1263. );
  1264. export const fetchPickOrderLineClient = cache(
  1265. async (queryParams?: Record<string, any>) => {
  1266. if (queryParams) {
  1267. const queryString = new URLSearchParams(queryParams).toString();
  1268. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1269. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage?${queryString}`,
  1270. {
  1271. method: "GET",
  1272. next: { tags: ["pickorder"] },
  1273. },
  1274. );
  1275. } else {
  1276. return serverFetchJson<RecordsRes<PickOrderLineWithSuggestedLot[]>>(
  1277. `${BASE_API_URL}/pickOrder/get-pickorder-line-byPage`,
  1278. {
  1279. method: "GET",
  1280. next: { tags: ["pickorder"] },
  1281. },
  1282. );
  1283. }
  1284. },
  1285. );
  1286. export const fetchStockOutLineClient = cache(
  1287. async (pickOrderLineId: number) => {
  1288. return serverFetchJson<StockOutLine[]>(
  1289. `${BASE_API_URL}/stockOutLine/getByPickOrderLineId/${pickOrderLineId}`,
  1290. {
  1291. method: "GET",
  1292. next: { tags: ["pickorder"] },
  1293. },
  1294. );
  1295. },
  1296. );
  1297. export const fetchConsoDetail = cache(async (consoCode: string) => {
  1298. return serverFetchJson<PreReleasePickOrderSummary>(
  1299. `${BASE_API_URL}/pickOrder/pre-release-info/${consoCode}`,
  1300. {
  1301. method: "GET",
  1302. next: { tags: ["pickorder"] },
  1303. },
  1304. );
  1305. });
  1306. export const releasePickOrder = async (data: ReleasePickOrderInputs) => {
  1307. console.log(data);
  1308. console.log(JSON.stringify(data));
  1309. const po = await serverFetchJson<{ consoCode: string }>(
  1310. `${BASE_API_URL}/pickOrder/releaseConso`,
  1311. {
  1312. method: "POST",
  1313. body: JSON.stringify(data),
  1314. headers: { "Content-Type": "application/json" },
  1315. },
  1316. );
  1317. revalidateTag("pickorder");
  1318. return po;
  1319. };
  1320. export const createStockOutLine = async (data: CreateStockOutLine) => {
  1321. console.log("triggering");
  1322. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1323. `${BASE_API_URL}/stockOutLine/create`,
  1324. {
  1325. method: "POST",
  1326. body: JSON.stringify(data),
  1327. headers: { "Content-Type": "application/json" },
  1328. },
  1329. );
  1330. revalidateTag("pickorder");
  1331. return po;
  1332. };
  1333. export const updateStockOutLine = async (data: UpdateStockOutLine) => {
  1334. console.log(data);
  1335. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1336. `${BASE_API_URL}/stockOutLine/update`,
  1337. {
  1338. method: "POST",
  1339. body: JSON.stringify(data),
  1340. headers: { "Content-Type": "application/json" },
  1341. },
  1342. );
  1343. revalidateTag("pickorder");
  1344. return po;
  1345. };
  1346. export const completeConsoPickOrder = async (consoCode: string) => {
  1347. const po = await serverFetchJson<PostStockOutLiineResponse<StockOutLine>>(
  1348. `${BASE_API_URL}/pickOrder/consoPickOrder/complete/${consoCode}`,
  1349. {
  1350. method: "POST",
  1351. headers: { "Content-Type": "application/json" },
  1352. },
  1353. );
  1354. revalidateTag("pickorder");
  1355. return po;
  1356. };
  1357. export const fetchConsoStatus = cache(async (consoCode: string) => {
  1358. return serverFetchJson<{ status: string }>(
  1359. `${BASE_API_URL}/stockOut/get-status/${consoCode}`,
  1360. {
  1361. method: "GET",
  1362. next: { tags: ["pickorder"] },
  1363. },
  1364. );
  1365. });
  1366. export interface ReleasedDoPickOrderResponse {
  1367. id: number;
  1368. storeId: string;
  1369. ticketNo: string;
  1370. pickOrderId: number;
  1371. ticketStatus: string;
  1372. doOrderId: number;
  1373. shopId: number;
  1374. handledBy: number;
  1375. ticketReleaseTime: string;
  1376. }
  1377. export const fetchReleasedDoPickOrders = async (): Promise<ReleasedDoPickOrderResponse[]> => {
  1378. const response = await serverFetchJson<ReleasedDoPickOrderResponse[]>(
  1379. `${BASE_API_URL}/doPickOrder/released`,
  1380. {
  1381. method: "GET",
  1382. },
  1383. );
  1384. return response;
  1385. };
  1386. // 新增:Released Do Pick Order 列表項目(對應後端 ReleasedDoPickOrderListItem)
  1387. export interface ReleasedDoPickOrderListItem {
  1388. id: number;
  1389. requiredDeliveryDate: string | null;
  1390. shopCode: string | null;
  1391. shopName: string | null;
  1392. storeId: string | null;
  1393. truckLanceCode: string | null;
  1394. truckDepartureTime: string | null;
  1395. deliveryOrderCodes: string[];
  1396. }
  1397. // 修改:fetchReleasedDoPickOrders 支援 shopName 篩選,並回傳新結構
  1398. export const fetchReleasedDoPickOrdersForSelection = async (
  1399. shopName?: string,
  1400. storeId?: string,
  1401. truck?: string
  1402. ): Promise<ReleasedDoPickOrderListItem[]> => {
  1403. const params = new URLSearchParams();
  1404. if (shopName?.trim()) params.append("shopName", shopName.trim());
  1405. if (storeId?.trim()) params.append("storeId", storeId.trim());
  1406. if (truck?.trim()) params.append("truck", truck.trim());
  1407. const query = params.toString();
  1408. const url = `${BASE_API_URL}/doPickOrder/released${query ? `?${query}` : ""}`;
  1409. const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, {
  1410. method: "GET",
  1411. });
  1412. return response ?? [];
  1413. };
  1414. export const fetchReleasedDoPickOrdersForSelectionToday = async (
  1415. shopName?: string,
  1416. storeId?: string,
  1417. truck?: string
  1418. ): Promise<ReleasedDoPickOrderListItem[]> => {
  1419. const params = new URLSearchParams();
  1420. if (shopName?.trim()) params.append("shopName", shopName.trim());
  1421. if (storeId?.trim()) params.append("storeId", storeId.trim());
  1422. if (truck?.trim()) params.append("truck", truck.trim());
  1423. const query = params.toString();
  1424. const url = `${BASE_API_URL}/doPickOrder/released-today${query ? `?${query}` : ""}`;
  1425. const response = await serverFetchJson<ReleasedDoPickOrderListItem[]>(url, {
  1426. method: "GET",
  1427. });
  1428. return response ?? [];
  1429. };
  1430. export const fetchReleasedDoPickOrderCountByStore = async (
  1431. storeId: string
  1432. ): Promise<number> => {
  1433. const list = await fetchReleasedDoPickOrdersForSelection(undefined, storeId);
  1434. return list.length;
  1435. };
  1436. // 新增:依 doPickOrderId 分配
  1437. export const assignByDoPickOrderId = async (
  1438. userId: number,
  1439. doPickOrderId: number
  1440. ): Promise<PostPickOrderResponse> => {
  1441. const response = await serverFetchJson<PostPickOrderResponse>(
  1442. `${BASE_API_URL}/doPickOrder/assign-by-id`,
  1443. {
  1444. method: "POST",
  1445. headers: { "Content-Type": "application/json" },
  1446. body: JSON.stringify({ userId, doPickOrderId }),
  1447. }
  1448. );
  1449. revalidateTag("pickorder");
  1450. return response;
  1451. };