FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

actions.ts 44 KiB

8ヶ月前
6ヶ月前
7ヶ月前
8ヶ月前
9ヶ月前
7ヶ月前
10ヶ月前
9ヶ月前
6ヶ月前
6ヶ月前
10ヶ月前
9ヶ月前
8ヶ月前
10ヶ月前
8ヶ月前
10ヶ月前
10ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
6ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
6ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
4ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
4ヶ月前
7ヶ月前
7ヶ月前
4ヶ月前
4ヶ月前
7ヶ月前
4ヶ月前
7ヶ月前
4ヶ月前
6ヶ月前
7ヶ月前
7ヶ月前
7ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
4ヶ月前
7ヶ月前
7ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
7ヶ月前
8ヶ月前
3ヶ月前
8ヶ月前
8ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
3ヶ月前
4ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
8ヶ月前
6ヶ月前
8ヶ月前
9ヶ月前
10ヶ月前
9ヶ月前
10ヶ月前
9ヶ月前
10ヶ月前
10ヶ月前
5ヶ月前
10ヶ月前
7ヶ月前
10ヶ月前
10ヶ月前
9ヶ月前
9ヶ月前
9ヶ月前
7ヶ月前
9ヶ月前
7ヶ月前
10ヶ月前
7ヶ月前
7ヶ月前
10ヶ月前
8ヶ月前
9ヶ月前
9ヶ月前
9ヶ月前
9ヶ月前
9ヶ月前
9ヶ月前
8ヶ月前
7ヶ月前
6ヶ月前
6ヶ月前
5ヶ月前
5ヶ月前
5ヶ月前
6ヶ月前
5ヶ月前
5ヶ月前
6ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608
  1. "use server";
  2. import { cache } from 'react';
  3. import { Pageable, serverFetchBlob, serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil";
  4. import { JobOrder, JoStatus, Machine, Operator } from ".";
  5. import { BASE_API_URL } from "@/config/api";
  6. import { revalidateTag } from "next/cache";
  7. import { convertObjToURLSearchParams } from "@/app/utils/commonUtil";
  8. import { FileResponse } from "@/app/api/pdf/actions";
  9. export interface SaveJo {
  10. bomId: number;
  11. planStart: string;
  12. planEnd: string;
  13. reqQty: number;
  14. type: string;
  15. //jobType?: string;
  16. jobTypeId?: number;
  17. productionPriority?: number;
  18. }
  19. export interface SaveJoResponse {
  20. id: number;
  21. }
  22. export interface SearchJoResultRequest extends Pageable {
  23. code: string;
  24. itemName?: string;
  25. planStart?: string;
  26. planStartTo?: string;
  27. jobTypeName?: string;
  28. joSearchStatus?: string;
  29. }
  30. export interface productProcessLineQtyRequest {
  31. productProcessLineId: number;
  32. outputFromProcessQty: number;
  33. outputFromProcessUom: string;
  34. defectQty: number;
  35. defectUom: string;
  36. scrapQty: number;
  37. scrapUom: string;
  38. }
  39. export interface SearchJoResultResponse {
  40. records: JobOrder[];
  41. total: number;
  42. }
  43. // DEPRECIATED
  44. export interface SearchJoResult {
  45. id: number;
  46. code: string;
  47. itemCode: string;
  48. name: string;
  49. reqQty: number;
  50. uom: string;
  51. status: JoStatus;
  52. }
  53. export interface UpdateJoRequest {
  54. id: number;
  55. status: string;
  56. }
  57. // For Jo Button Actions
  58. export interface CommonActionJoRequest {
  59. id: number;
  60. }
  61. export interface CommonActionJoResponse {
  62. id: number;
  63. entity: { status: JoStatus }
  64. }
  65. // For Jo Process
  66. export interface IsOperatorExistResponse<T> {
  67. id: number | null;
  68. name: string;
  69. code: string;
  70. type?: string;
  71. message: string | null;
  72. errorPosition: string | keyof T;
  73. entity: T;
  74. }
  75. export interface isCorrectMachineUsedResponse<T> {
  76. id: number | null;
  77. name: string;
  78. code: string;
  79. type?: string;
  80. message: string | null;
  81. errorPosition: string | keyof T;
  82. entity: T;
  83. }
  84. export interface JobOrderDetail {
  85. id: number;
  86. code: string;
  87. name: string;
  88. reqQty: number;
  89. uom: string;
  90. pickLines: any[];
  91. jobTypeName: string;
  92. status: string;
  93. }
  94. export interface UnassignedJobOrderPickOrder {
  95. pickOrderId: number;
  96. pickOrderCode: string;
  97. pickOrderConsoCode: string;
  98. pickOrderTargetDate: string;
  99. pickOrderStatus: string;
  100. jobOrderId: number;
  101. jobOrderCode: string;
  102. jobOrderName: string;
  103. reqQty: number;
  104. uom: string;
  105. planStart: string;
  106. planEnd: string;
  107. }
  108. export interface AssignJobOrderResponse {
  109. id: number | null;
  110. code: string | null;
  111. name: string | null;
  112. type: string | null;
  113. message: string | null;
  114. errorPosition: string | null;
  115. }
  116. export interface PrintPickRecordRequest{
  117. pickOrderId: number;
  118. printerId: number;
  119. printQty: number;
  120. floor?: "2F" | "3F" | "4F" | "ALL";
  121. }
  122. export interface PrintPickRecordResponse{
  123. success: boolean;
  124. message?: string
  125. }
  126. export interface PrintFGStockInLabelRequest {
  127. stockInLineId: number;
  128. printerId: number;
  129. printQty?: number;
  130. }
  131. export const printFGStockInLabel = cache(async(data: PrintFGStockInLabelRequest) => {
  132. const params = new URLSearchParams();
  133. if (data.stockInLineId) {
  134. params.append('stockInLineId', data.stockInLineId.toString());
  135. }
  136. params.append('printerId', data.printerId.toString());
  137. if (data.printQty !== undefined && data.printQty !== null) {
  138. params.append('printQty', data.printQty.toString());
  139. }
  140. return serverFetchWithNoContent(
  141. `${BASE_API_URL}/jo/print-FGStockInLabel?${params.toString()}`,
  142. {
  143. method: "GET",
  144. next: {
  145. tags: ["printFGStockInLabel"],
  146. },
  147. }
  148. );
  149. });
  150. export interface UpdateJoReqQtyRequest {
  151. id: number;
  152. reqQty: number;
  153. }
  154. // 添加更新 reqQty 的函数
  155. export const updateJoReqQty = cache(async (data: UpdateJoReqQtyRequest) => {
  156. return serverFetchJson<SaveJoResponse>(`${BASE_API_URL}/jo/updateReqQty`, {
  157. method: "POST",
  158. body: JSON.stringify(data),
  159. headers: { "Content-Type": "application/json" },
  160. })
  161. })
  162. export const recordSecondScanIssue = cache(async (
  163. pickOrderId: number,
  164. itemId: number,
  165. data: {
  166. qty: number; // verified qty (actual pick qty)
  167. missQty?: number; // 添加:miss qty
  168. badItemQty?: number; // 添加:bad item qty
  169. isMissing: boolean;
  170. isBad: boolean;
  171. reason: string;
  172. createdBy: number;
  173. type?: string; // type 也应该是可选的
  174. }
  175. ) => {
  176. return serverFetchJson<any>(
  177. `${BASE_API_URL}/jo/second-scan-issue/${pickOrderId}/${itemId}`,
  178. {
  179. method: "POST",
  180. headers: { "Content-Type": "application/json" },
  181. body: JSON.stringify(data),
  182. next: { tags: ["jo-second-scan"] },
  183. },
  184. );
  185. });
  186. export interface ProductProcessResponse {
  187. id: number;
  188. productProcessCode: string;
  189. status: string;
  190. startTime?: string;
  191. endTime?: string;
  192. date: string;
  193. bomId?: number;
  194. jobOrderId?: number;
  195. }
  196. export interface ProductProcessLineResponse {
  197. id: number,
  198. bomprocessId: number,
  199. operatorId: number,
  200. operatorName: string,
  201. equipmentId: number,
  202. handlerId: number,
  203. seqNo: number,
  204. name: string,
  205. description: string,
  206. equipmentDetailId: number,
  207. equipment_name: string,
  208. equipmentDetailCode: string,
  209. status: string,
  210. byproductId: number,
  211. byproductName: string,
  212. byproductQty: number,
  213. byproductUom: string,
  214. scrapQty: number,
  215. defectQty: number,
  216. defectUom: string,
  217. outputFromProcessQty: number,
  218. outputFromProcessUom: string,
  219. durationInMinutes: number,
  220. prepTimeInMinutes: number,
  221. postProdTimeInMinutes: number,
  222. startTime: string,
  223. endTime: string,
  224. isOringinal: boolean,
  225. }
  226. export interface ProductProcessWithLinesResponse {
  227. id: number;
  228. productProcessCode: string;
  229. status: string;
  230. startTime?: string;
  231. endTime?: string;
  232. date: string;
  233. bomId?: number;
  234. jobOrderId?: number;
  235. jobOrderCode: string;
  236. jobOrderStatus: string;
  237. bomDescription: string;
  238. jobType: string;
  239. isDark: string;
  240. bomBaseQty: number;
  241. isDense: number;
  242. isFloat: string;
  243. timeSequence: number;
  244. complexity: number;
  245. scrapRate: number;
  246. allergicSubstance: string;
  247. itemId: number;
  248. itemCode: string;
  249. itemName: string;
  250. outputQty: number;
  251. outputQtyUom: string;
  252. productionPriority: number;
  253. submitedBagRecord?: boolean;
  254. jobOrderLines: JobOrderLineInfo[];
  255. productProcessLines: ProductProcessLineResponse[];
  256. }
  257. export interface UpdateProductProcessLineQtyRequest {
  258. productProcessLineId: number;
  259. outputFromProcessQty: number;
  260. outputFromProcessUom: string;
  261. byproductName: string;
  262. byproductQty: number;
  263. byproductUom: string;
  264. defectQty: number;
  265. defectUom: string;
  266. defect2Qty: number;
  267. defect2Uom: string;
  268. defect3Qty: number;
  269. defect3Uom: string;
  270. defectDescription: string;
  271. defectDescription2: string;
  272. defectDescription3: string;
  273. scrapQty: number;
  274. scrapUom: string;
  275. }
  276. export interface UpdateProductProcessLineQtyResponse {
  277. id: number;
  278. outputFromProcessQty: number;
  279. outputFromProcessUom: string;
  280. defectQty: number;
  281. defectUom: string;
  282. defect2Qty: number;
  283. defect2Uom: string;
  284. defect3Qty: number;
  285. defect3Uom: string;
  286. defectDescription: string;
  287. defectDescription2: string;
  288. defectDescription3: string;
  289. scrapQty: number;
  290. scrapUom: string;
  291. byproductName: string;
  292. byproductQty: number;
  293. byproductUom: string;
  294. }
  295. export interface AllProductProcessResponse {
  296. id: number;
  297. productProcessCode: string;
  298. status: string;
  299. startTime?: string;
  300. endTime?: string;
  301. date: string;
  302. bomId?: number;
  303. }
  304. export interface AllJoborderProductProcessInfoResponse {
  305. id: number;
  306. productProcessCode: string;
  307. status: string;
  308. startTime?: string;
  309. endTime?: string;
  310. date: string;
  311. matchStatus: string;
  312. bomId?: number;
  313. productionPriority: number;
  314. assignedTo: number;
  315. pickOrderId: number;
  316. pickOrderStatus: string;
  317. itemCode: string;
  318. itemName: string;
  319. bomDescription?: string | null;
  320. lotNo: string;
  321. requiredQty: number;
  322. jobOrderId: number;
  323. timeNeedToComplete: number;
  324. uom: string;
  325. isDrink?: boolean | null;
  326. stockInLineId: number;
  327. /** Stock-in-line current status (e.g. receiving/received/partially_completed/completed/rejected). */
  328. stockInLineStatus?: string | null;
  329. jobOrderCode: string;
  330. productProcessLineCount: number;
  331. FinishedProductProcessLineCount: number;
  332. lines: ProductProcessInfoResponse[];
  333. }
  334. export interface JobOrderProductProcessPageResponse {
  335. content: AllJoborderProductProcessInfoResponse[];
  336. totalJobOrders: number;
  337. page: number;
  338. size: number;
  339. }
  340. export interface ProductProcessInfoResponse {
  341. id: number;
  342. operatorId?: number;
  343. operatorName?: string;
  344. equipmentId?: number;
  345. equipmentName?: string;
  346. startTime?: string;
  347. endTime?: string;
  348. status: string;
  349. }
  350. export interface ProductProcessLineQrscanUpadteRequest {
  351. productProcessLineId: number;
  352. //operatorId?: number;
  353. //equipmentId?: number;
  354. equipmentTypeSubTypeEquipmentNo?: string;
  355. staffNo?: string;
  356. }
  357. export interface NewProductProcessLineQrscanUpadteRequest{
  358. productProcessLineId: number;
  359. equipmentCode?: string;
  360. staffNo?: string;
  361. }
  362. export interface ProductProcessLineDetailResponse {
  363. id: number,
  364. productProcessId: number,
  365. bomProcessId: number,
  366. operatorId: number,
  367. equipmentType: string,
  368. operatorName: string,
  369. handlerId: number,
  370. seqNo: number,
  371. isDark: string,
  372. isDense: number,
  373. isFloat: string,
  374. outputQtyUom: string,
  375. outputQty: number,
  376. pickOrderId: number,
  377. jobOrderCode: string,
  378. jobOrderId: number,
  379. name: string,
  380. description: string,
  381. equipment: string,
  382. startTime: string,
  383. endTime: string,
  384. defectQty: number,
  385. defectUom: string,
  386. scrapQty: number,
  387. scrapUom: string,
  388. byproductId: number,
  389. byproductName: string,
  390. byproductQty: number,
  391. byproductUom: string | undefined,
  392. totalStockQty: number,
  393. insufficientStockQty: number,
  394. sufficientStockQty: number,
  395. productionPriority: number,
  396. productProcessLines: ProductProcessLineInfoResponse[],
  397. jobOrderLineInfo: JobOrderLineInfo[],
  398. }
  399. export interface JobOrderProcessLineDetailResponse {
  400. id: number;
  401. productProcessId: number;
  402. bomProcessId: number;
  403. operatorId: number;
  404. equipmentType: string | null;
  405. operatorName: string;
  406. handlerId: number;
  407. seqNo: number;
  408. durationInMinutes: number;
  409. name: string;
  410. description: string;
  411. equipmentId: number;
  412. startTime: string | number[]; // API 返回的是数组格式
  413. endTime: string | number[];
  414. stopTime: string | number[];
  415. totalPausedTimeMs?: number; // API 返回的是数组格式
  416. status: string;
  417. submitedBagRecord: boolean;
  418. outputFromProcessQty: number;
  419. outputFromProcessUom: string;
  420. defectQty: number;
  421. defectUom: string;
  422. defectDescription: string;
  423. defectQty2: number;
  424. defectUom2: string;
  425. defectDescription2: string;
  426. defectQty3: number;
  427. defectUom3: string;
  428. defectDescription3: string;
  429. scrapQty: number;
  430. scrapUom: string;
  431. byproductId: number;
  432. byproductName: string;
  433. byproductQty: number;
  434. byproductUom: string;
  435. productProcessIssueId: number;
  436. productProcessIssueStatus: string;
  437. }
  438. export interface JobOrderLineInfo {
  439. id: number,
  440. itemId: number,
  441. itemCode: string,
  442. itemName: string,
  443. type: string,
  444. reqQty: number,
  445. baseReqQty: number,
  446. stockReqQty: number,
  447. stockQty: number,
  448. baseStockQty: number,
  449. reqUom: string,
  450. reqBaseUom: string,
  451. stockUom: string,
  452. stockBaseUom: string,
  453. availableStatus: string,
  454. bomProcessId: number,
  455. bomProcessSeqNo: number,
  456. isOringinal: boolean
  457. }
  458. export interface ProductProcessLineInfoResponse {
  459. id: number,
  460. bomprocessId: number,
  461. operatorId: number,
  462. operatorName: string,
  463. equipmentId: number,
  464. handlerId: number,
  465. seqNo: number,
  466. name: string,
  467. description: string,
  468. equipment_name: string,
  469. equipmentDetailCode: string,
  470. status: string,
  471. byproductId: number,
  472. byproductName: string,
  473. byproductQty: number,
  474. byproductUom: string,
  475. scrapQty: number,
  476. defectQty: number,
  477. defectUom: string,
  478. durationInMinutes: number,
  479. prepTimeInMinutes: number,
  480. postProdTimeInMinutes: number,
  481. outputFromProcessQty: number,
  482. outputFromProcessUom: string,
  483. startTime: string,
  484. endTime: string
  485. }
  486. export interface FloorPickCount {
  487. floor: string;
  488. finishedCount: number;
  489. totalCount: number;
  490. }
  491. export interface AllJoPickOrderResponse {
  492. id: number;
  493. pickOrderId: number | null;
  494. pickOrderCode: string | null;
  495. jobOrderId: number | null;
  496. jobOrderCode: string | null;
  497. jobOrderTypeId: number | null;
  498. jobOrderType: string | null;
  499. itemId: number;
  500. itemName: string;
  501. bomDescription?: string | null;
  502. lotNo: string | null;
  503. planStart?: string | number[] | null;
  504. reqQty: number;
  505. uomId: number;
  506. uomName: string;
  507. jobOrderStatus: string;
  508. finishedPickOLineCount: number;
  509. floorPickCounts: FloorPickCount[];
  510. noLotPickCount?: FloorPickCount | null;
  511. suggestedFailCount?: number;
  512. }
  513. export interface UpdateJoPickOrderHandledByRequest {
  514. pickOrderId: number;
  515. itemId: number;
  516. userId: number;
  517. }
  518. export interface JobTypeResponse {
  519. id: number;
  520. name: string;
  521. }
  522. export interface SaveProductProcessIssueTimeRequest {
  523. productProcessLineId: number;
  524. reason: string;
  525. }
  526. export interface JobOrderLotsHierarchicalResponse {
  527. pickOrder: PickOrderInfoResponse;
  528. pickOrderLines: PickOrderLineWithLotsResponse[];
  529. }
  530. /** JO Workbench: same shape as [JobOrderLotsHierarchicalResponse] but `pickOrder.jobOrder` includes BOM code/name. */
  531. export interface JobOrderLotsHierarchicalWorkbenchResponse {
  532. pickOrder: PickOrderInfoWorkbenchResponse;
  533. pickOrderLines: PickOrderLineWithLotsResponse[];
  534. }
  535. export interface PickOrderInfoResponse {
  536. id: number | null;
  537. code: string | null;
  538. consoCode: string | null;
  539. targetDate: string | null;
  540. type: string | null;
  541. status: string | null;
  542. assignTo: number | null;
  543. jobOrder: JobOrderBasicInfoResponse;
  544. }
  545. export interface PickOrderInfoWorkbenchResponse {
  546. id: number | null;
  547. code: string | null;
  548. consoCode: string | null;
  549. targetDate: string | null;
  550. type: string | null;
  551. status: string | null;
  552. assignTo: number | null;
  553. jobOrder: JobOrderBasicInfoWorkbenchResponse;
  554. }
  555. export interface JobOrderBasicInfoResponse {
  556. id: number;
  557. code: string;
  558. name: string;
  559. }
  560. /** BOM header code/name from job order's BOM (workbench hierarchical API only). */
  561. export interface JobOrderBasicInfoWorkbenchResponse {
  562. id: number;
  563. code: string;
  564. name: string;
  565. itemCode: string | null;
  566. itemName: string | null;
  567. }
  568. export interface PickOrderLineWithLotsResponse {
  569. id: number;
  570. itemId: number | null;
  571. itemCode: string | null;
  572. itemName: string | null;
  573. requiredQty: number | null;
  574. totalAvailableQty?: number | null;
  575. uomCode: string | null;
  576. uomDesc: string | null;
  577. status: string | null;
  578. handler: string | null;
  579. lots: LotDetailResponse[];
  580. stockouts?: StockOutLineDetailResponse[];
  581. }
  582. export interface StockOutLineDetailResponse {
  583. id: number | null;
  584. status: string | null;
  585. qty: number | null;
  586. lotId: number | null;
  587. lotNo: string | null;
  588. location: string | null;
  589. availableQty: number | null;
  590. noLot: boolean;
  591. /** Workbench API: matched suggest_pick_lot qty for this SOL lot line */
  592. suggestedPickQty?: number | null;
  593. suggestedPickLotId?: number | null;
  594. }
  595. export interface LotDetailResponse {
  596. lotId: number | null;
  597. lotNo: string | null;
  598. expiryDate: string | null;
  599. location: string | null;
  600. availableQty: number | null;
  601. requiredQty: number | null;
  602. actualPickQty: number | null;
  603. processingStatus: string | null;
  604. lotAvailability: string | null;
  605. pickOrderId: number | null;
  606. pickOrderCode: string | null;
  607. pickOrderConsoCode: string | null;
  608. pickOrderLineId: number | null;
  609. stockOutLineId: number | null;
  610. stockInLineId: number | null;
  611. suggestedPickLotId: number | null;
  612. stockOutLineQty: number | null;
  613. stockOutLineStatus: string | null;
  614. routerIndex: number | null;
  615. routerArea: string | null;
  616. routerRoute: string | null;
  617. uomShortDesc: string | null;
  618. matchStatus?: string | null;
  619. matchBy?: number | null;
  620. matchQty?: number | null;
  621. }
  622. export interface JobOrderListForPrintQrCodeResponse {
  623. id: number;
  624. code: string;
  625. name: string;
  626. reqQty: number;
  627. stockOutLineId: number;
  628. stockOutLineQty: number;
  629. stockOutLineStatus: string;
  630. finihedTime: string;
  631. }
  632. export interface UpdateJoPlanStartRequest {
  633. id: number;
  634. planStart: string; // Format: YYYY-MM-DDTHH:mm:ss or YYYY-MM-DD
  635. }
  636. export const saveProductProcessIssueTime = cache(async (request: SaveProductProcessIssueTimeRequest) => {
  637. return serverFetchJson<any>(
  638. `${BASE_API_URL}/product-process/Demo/ProcessLine/issue`,
  639. {
  640. method: "POST",
  641. headers: { "Content-Type": "application/json" },
  642. body: JSON.stringify(request),
  643. }
  644. );
  645. });
  646. export const saveProductProcessResumeTime = cache(async (productProcessIssueId: number) => {
  647. return serverFetchJson<any>(
  648. `${BASE_API_URL}/product-process/Demo/ProcessLine/resume/${productProcessIssueId}`,
  649. {
  650. method: "POST",
  651. }
  652. );
  653. });
  654. export const deleteJobOrder=cache(async (jobOrderId: number) => {
  655. return serverFetchJson<any>(
  656. `${BASE_API_URL}/jo/demo/deleteJobOrder/${jobOrderId}`,
  657. {
  658. method: "POST",
  659. }
  660. );
  661. });
  662. export const setJobOrderHidden = cache(async (jobOrderId: number, hidden: boolean) => {
  663. const response = await serverFetchJson<any>(`${BASE_API_URL}/jo/set-hidden`, {
  664. method: "POST",
  665. headers: { "Content-Type": "application/json" },
  666. body: JSON.stringify({ id: jobOrderId, hidden }),
  667. });
  668. revalidateTag("jos");
  669. return response;
  670. });
  671. export const fetchAllJobTypes = cache(async () => {
  672. return serverFetchJson<JobTypeResponse[]>(
  673. `${BASE_API_URL}/jo/jobTypes`,
  674. {
  675. method: "GET",
  676. }
  677. );
  678. });
  679. export const updateJoPickOrderHandledBy = cache(async (request: UpdateJoPickOrderHandledByRequest) => {
  680. return serverFetchJson<any>(
  681. `${BASE_API_URL}/jo/update-jo-pick-order-handled-by`,
  682. {
  683. method: "POST",
  684. body: JSON.stringify(request),
  685. headers: { "Content-Type": "application/json" },
  686. },
  687. );
  688. });
  689. export const fetchJobOrderLotsHierarchicalByPickOrderId = cache(async (pickOrderId: number) => {
  690. return serverFetchJson<JobOrderLotsHierarchicalResponse>(
  691. `${BASE_API_URL}/jo/all-lots-hierarchical-by-pick-order/${pickOrderId}`,
  692. {
  693. method: "GET",
  694. next: { tags: ["jo-hierarchical"] },
  695. },
  696. );
  697. });
  698. /** JO Workbench: in−out available (matches scan-pick); stockouts include suggestedPickQty / suggestedPickLotId when SPL matches SOL lot line */
  699. export const fetchJobOrderLotsHierarchicalByPickOrderIdWorkbench = cache(
  700. async (pickOrderId: number) => {
  701. return serverFetchJson<JobOrderLotsHierarchicalWorkbenchResponse>(
  702. `${BASE_API_URL}/jo/all-lots-hierarchical-by-pick-order-workbench/${pickOrderId}`,
  703. {
  704. method: "GET",
  705. next: { tags: ["jo-hierarchical-workbench"] },
  706. },
  707. );
  708. },
  709. );
  710. // NOTE: Do NOT wrap in `cache()` because the list needs to reflect just-completed lines
  711. // immediately when navigating back from JobPickExecution.
  712. export interface FetchAllJoPickOrdersFilters {
  713. jobOrderCode?: string | null;
  714. pickOrderCode?: string | null;
  715. itemName?: string | null;
  716. bomDescription?: string | null;
  717. planStart?: string | null;
  718. }
  719. export const fetchAllJoPickOrders = async (
  720. type?: string | null,
  721. floor?: string | null,
  722. filters?: FetchAllJoPickOrdersFilters,
  723. ) => {
  724. const params = new URLSearchParams();
  725. if (type) params.set("type", type);
  726. if (floor) params.set("floor", floor);
  727. if (filters?.jobOrderCode) params.set("jobOrderCode", filters.jobOrderCode);
  728. if (filters?.pickOrderCode) params.set("pickOrderCode", filters.pickOrderCode);
  729. if (filters?.itemName) params.set("itemName", filters.itemName);
  730. if (filters?.bomDescription) params.set("bomDescription", filters.bomDescription);
  731. if (filters?.planStart) params.set("planStart", filters.planStart);
  732. const query = params.toString() ? `?${params.toString()}` : "";
  733. return serverFetchJson<AllJoPickOrderResponse[]>(
  734. `${BASE_API_URL}/jo/AllJoPickOrder${query}`,
  735. // Force re-fetch. This page reflects real-time pick completion state.
  736. { method: "GET", cache: "no-store" }
  737. );
  738. };
  739. export const fetchProductProcessLineDetail = cache(async (lineId: number) => {
  740. return serverFetchJson<JobOrderProcessLineDetailResponse>(
  741. `${BASE_API_URL}/product-process/Demo/ProcessLine/detail/${lineId}`,
  742. {
  743. method: "GET",
  744. }
  745. );
  746. });
  747. export const updateProductProcessLineQty = cache(async (request: UpdateProductProcessLineQtyRequest) => {
  748. return serverFetchJson<UpdateProductProcessLineQtyResponse>(
  749. `${BASE_API_URL}/product-process/Demo/ProcessLine/update/qty/${request.productProcessLineId}`,
  750. {
  751. method: "POST",
  752. headers: { "Content-Type": "application/json" },
  753. body: JSON.stringify(request),
  754. }
  755. );
  756. });
  757. export const updateProductProcessLineQrscan = cache(async (request: ProductProcessLineQrscanUpadteRequest) => {
  758. const requestBody: any = {
  759. productProcessLineId: request.productProcessLineId,
  760. //operatorId: request.operatorId,
  761. //equipmentId: request.equipmentId,
  762. equipmentTypeSubTypeEquipmentNo: request.equipmentTypeSubTypeEquipmentNo,
  763. staffNo: request.staffNo,
  764. };
  765. if (request.equipmentTypeSubTypeEquipmentNo !== undefined) {
  766. requestBody["EquipmentType-SubType-EquipmentNo"] = request.equipmentTypeSubTypeEquipmentNo;
  767. }
  768. return serverFetchJson<any>(
  769. `${BASE_API_URL}/product-process/Demo/update`,
  770. {
  771. method: "POST",
  772. headers: { "Content-Type": "application/json" },
  773. body: JSON.stringify(requestBody),
  774. }
  775. );
  776. });
  777. export const newUpdateProductProcessLineQrscan = cache(async (request: NewProductProcessLineQrscanUpadteRequest) => {
  778. return serverFetchJson<any>(
  779. `${BASE_API_URL}/product-process/Demo/NewUpdate`,
  780. {
  781. method: "POST",
  782. headers: { "Content-Type": "application/json" },
  783. body: JSON.stringify(request),
  784. }
  785. );
  786. });
  787. export const fetchAllJoborderProductProcessInfo = cache(async (type?: string | null) => {
  788. const query = type
  789. ? `?type=${encodeURIComponent(type)}`
  790. : "";
  791. return serverFetchJson<AllJoborderProductProcessInfoResponse[]>(
  792. `${BASE_API_URL}/product-process/Demo/Process/all${query}`,
  793. {
  794. method: "GET",
  795. next: { tags: ["productProcess"] },
  796. }
  797. );
  798. });
  799. export const fetchJoborderProductProcessesPage = cache(async (params: {
  800. /** Job order planStart 區間起(YYYY-MM-DD,含當日) */
  801. date?: string | null;
  802. itemCode?: string | null;
  803. jobOrderCode?: string | null;
  804. bomIds?: number[] | null;
  805. qcReady?: boolean | null;
  806. type?: string | null;
  807. includePutaway?: boolean | null;
  808. /** all | completed | notCompleted */
  809. putawayStatus?: string | null;
  810. page?: number;
  811. size?: number;
  812. }) => {
  813. const {
  814. date,
  815. itemCode,
  816. jobOrderCode,
  817. bomIds,
  818. qcReady,
  819. includePutaway,
  820. putawayStatus,
  821. type,
  822. page = 0,
  823. size = 50,
  824. } = params;
  825. const queryParts: string[] = [];
  826. if (date) {
  827. queryParts.push(`date=${encodeURIComponent(date)}`);
  828. }
  829. if (itemCode) queryParts.push(`itemCode=${encodeURIComponent(itemCode)}`);
  830. if (jobOrderCode) queryParts.push(`jobOrderCode=${encodeURIComponent(jobOrderCode)}`);
  831. if (bomIds && bomIds.length > 0) queryParts.push(`bomIds=${bomIds.join(",")}`);
  832. if (qcReady !== undefined && qcReady !== null) queryParts.push(`qcReady=${qcReady}`);
  833. if (type) queryParts.push(`type=${encodeURIComponent(type)}`);
  834. if (includePutaway !== undefined && includePutaway !== null) {
  835. queryParts.push(`includePutaway=${includePutaway}`);
  836. }
  837. if (putawayStatus) queryParts.push(`putawayStatus=${encodeURIComponent(putawayStatus)}`);
  838. queryParts.push(`page=${page}`);
  839. queryParts.push(`size=${size}`);
  840. const query = queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
  841. return serverFetchJson<JobOrderProductProcessPageResponse>(
  842. `${BASE_API_URL}/product-process/Demo/Process/search${query}`,
  843. {
  844. method: "GET",
  845. next: { tags: ["productProcessSearch"] },
  846. }
  847. );
  848. });
  849. /*
  850. export const updateProductProcessLineQty = async (request: UpdateProductProcessLineQtyRequest) => {
  851. return serverFetchJson<UpdateProductProcessLineQtyResponse>(
  852. `${BASE_API_URL}/product-process/lines/${request.productProcessLineId}/update/qty`,
  853. {
  854. method: "POST",
  855. headers: { "Content-Type": "application/json" },
  856. body: JSON.stringify(request),
  857. }
  858. );
  859. };
  860. */
  861. export const startProductProcessLine = async (lineId: number) => {
  862. return serverFetchJson<any>(
  863. `${BASE_API_URL}/product-process/Demo/ProcessLine/start/${lineId}`,
  864. {
  865. method: "POST",
  866. headers: { "Content-Type": "application/json" },
  867. }
  868. );
  869. };
  870. export const completeProductProcessLine = async (lineId: number) => {
  871. return serverFetchJson<any>(
  872. `${BASE_API_URL}/product-process/Demo/ProcessLine/complete/${lineId}`,
  873. {
  874. method: "POST",
  875. headers: { "Content-Type": "application/json" },
  876. }
  877. );
  878. };
  879. // 查询所有 production processes
  880. export const fetchProductProcesses = cache(async () => {
  881. return serverFetchJson<{ content: ProductProcessResponse[] }>(
  882. `${BASE_API_URL}/product-process`,
  883. {
  884. method: "GET",
  885. next: { tags: ["productProcess"] },
  886. }
  887. );
  888. });
  889. // 根据 ID 查询
  890. export const fetchProductProcessById = cache(async (id: number) => {
  891. return serverFetchJson<ProductProcessResponse>(
  892. `${BASE_API_URL}/product-process/${id}`,
  893. {
  894. method: "GET",
  895. next: { tags: ["productProcess"] },
  896. }
  897. );
  898. });
  899. export const updateProductProcessPriority = cache(async (productProcessId: number, productionPriority: number) => {
  900. return serverFetchJson<any>(
  901. `${BASE_API_URL}/product-process/Demo/Process/update/priority/${productProcessId}/${productionPriority}`,
  902. {
  903. method: "POST",
  904. }
  905. );
  906. });
  907. // 根据 Job Order ID 查询
  908. export const fetchProductProcessesByJobOrderId = cache(async (jobOrderId: number) => {
  909. return serverFetchJson<ProductProcessWithLinesResponse[]>(
  910. `${BASE_API_URL}/product-process/demo/joid/${jobOrderId}`,
  911. {
  912. method: "GET",
  913. next: { tags: ["productProcess"] },
  914. }
  915. );
  916. });
  917. export const newProductProcessLine = cache(async (lineId: number) => {
  918. return serverFetchJson<any>(
  919. `${BASE_API_URL}/product-process/Demo/ProcessLine/new/${lineId}`,
  920. {
  921. method: "POST",
  922. }
  923. );
  924. });
  925. // 获取 process 的所有 lines
  926. export const fetchProductProcessLines = cache(async (processId: number) => {
  927. return serverFetchJson<ProductProcessLineResponse[]>(
  928. `${BASE_API_URL}/product-process/${processId}/lines`,
  929. {
  930. method: "GET",
  931. next: { tags: ["productProcessLines"] },
  932. }
  933. );
  934. });
  935. // 创建 production process
  936. export const createProductProcess = async (data: {
  937. bomId: number;
  938. jobOrderId?: number;
  939. date?: string;
  940. }) => {
  941. return serverFetchJson<{ id: number; productProcessCode: string; linesCreated: number }>(
  942. `${BASE_API_URL}/product-process`,
  943. {
  944. method: "POST",
  945. headers: { "Content-Type": "application/json" },
  946. body: JSON.stringify(data),
  947. }
  948. );
  949. };
  950. // 更新 line 产出数据
  951. export const updateLineOutput = async (lineId: number, data: {
  952. outputQty?: number;
  953. outputUom?: string;
  954. defectQty?: number;
  955. defectUom?: string;
  956. scrapQty?: number;
  957. scrapUom?: string;
  958. byproductName?: string;
  959. byproductQty?: number;
  960. byproductUom?: string;
  961. }) => {
  962. return serverFetchJson<ProductProcessLineResponse>(
  963. `${BASE_API_URL}/product-process/lines/${lineId}/output`,
  964. {
  965. method: "PUT",
  966. headers: { "Content-Type": "application/json" },
  967. body: JSON.stringify(data),
  968. }
  969. );
  970. };
  971. export const updateSecondQrScanStatus = cache(async (pickOrderId: number, itemId: number, userId: number, qty: number) => {
  972. return serverFetchJson<any>(
  973. `${BASE_API_URL}/jo/update-match-status`,
  974. {
  975. method: "POST",
  976. body: JSON.stringify({
  977. pickOrderId,
  978. itemId,
  979. userId,
  980. qty
  981. }),
  982. headers: {
  983. 'Content-Type': 'application/json',
  984. },
  985. next: { tags: ["update-match-status"] },
  986. },
  987. );
  988. });
  989. export const submitSecondScanQuantity = cache(async (
  990. pickOrderId: number,
  991. itemId: number,
  992. data: { qty: number; isMissing?: boolean; isBad?: boolean; reason?: string; userId?: number }
  993. ) => {
  994. return serverFetchJson<any>(
  995. `${BASE_API_URL}/jo/second-scan-submit/${pickOrderId}/${itemId}`,
  996. {
  997. method: "POST",
  998. headers: { "Content-Type": "application/json" },
  999. body: JSON.stringify(data),
  1000. next: { tags: ["jo-second-scan"] },
  1001. },
  1002. );
  1003. });
  1004. // 获取未分配的 Job Order pick orders
  1005. export const fetchUnassignedJobOrderPickOrders = cache(async () => {
  1006. return serverFetchJson<UnassignedJobOrderPickOrder[]>(
  1007. `${BASE_API_URL}/jo/unassigned-job-order-pick-orders`,
  1008. {
  1009. method: "GET",
  1010. next: { tags: ["jo-unassigned"] },
  1011. },
  1012. );
  1013. });
  1014. // 分配 Job Order pick order 给用户
  1015. export const assignJobOrderPickOrder = async (pickOrderId: number, userId: number) => {
  1016. return serverFetchJson<AssignJobOrderResponse>(
  1017. `${BASE_API_URL}/jo/assign-job-order-pick-order/${pickOrderId}/${userId}`,
  1018. {
  1019. method: "POST",
  1020. headers: { "Content-Type": "application/json" },
  1021. }
  1022. );
  1023. };
  1024. export const unAssignJobOrderPickOrder = async (pickOrderId: number) => {
  1025. return serverFetchJson<AssignJobOrderResponse>(
  1026. `${BASE_API_URL}/jo/unassign-job-order-pick-order/${pickOrderId}`,
  1027. {
  1028. method: "POST",
  1029. headers: { "Content-Type": "application/json" },
  1030. }
  1031. );
  1032. };
  1033. // 获取 Job Order 分层数据
  1034. export const fetchJobOrderLotsHierarchical = cache(async (userId: number) => {
  1035. return serverFetchJson<JobOrderLotsHierarchicalResponse>(
  1036. `${BASE_API_URL}/jo/all-lots-hierarchical/${userId}`,
  1037. {
  1038. method: "GET",
  1039. next: { tags: ["jo-hierarchical"] },
  1040. },
  1041. );
  1042. });
  1043. export const fetchCompletedJobOrderPickOrders = cache(async (userId: number) => {
  1044. return serverFetchJson<any>(
  1045. `${BASE_API_URL}/jo/completed-job-order-pick-orders/${userId}`,
  1046. {
  1047. method: "GET",
  1048. next: { tags: ["jo-completed"] },
  1049. },
  1050. );
  1051. });
  1052. /*
  1053. // 获取已完成的 Job Order pick orders
  1054. export const fetchCompletedJobOrderPickOrdersrecords = cache(async () => {
  1055. return serverFetchJson<any>(
  1056. `${BASE_API_URL}/jo/completed-job-order-pick-orders-only`,
  1057. {
  1058. method: "GET",
  1059. next: { tags: ["jo-completed"] },
  1060. },
  1061. );
  1062. });
  1063. */
  1064. export const fetchCompletedJobOrderPickOrdersrecords = async (completedDate?: string | null) => {
  1065. const q =
  1066. completedDate && String(completedDate).trim() !== ""
  1067. ? `?date=${encodeURIComponent(String(completedDate).trim())}`
  1068. : "";
  1069. return serverFetchJson<any>(`${BASE_API_URL}/jo/completed-job-order-pick-orders-only${q}`, {
  1070. method: "GET",
  1071. cache: "no-store",
  1072. });
  1073. };
  1074. export const fetchJobOrderPickOrdersrecords = async (
  1075. date?: string | null,
  1076. status?: string | null,
  1077. ) => {
  1078. const params = new URLSearchParams();
  1079. if (date && String(date).trim() !== "") {
  1080. params.set("date", String(date).trim());
  1081. }
  1082. if (status && String(status).trim() !== "" && String(status) !== "All") {
  1083. params.set("status", String(status).trim());
  1084. }
  1085. const q = params.toString() ? `?${params.toString()}` : "";
  1086. return serverFetchJson<any>(`${BASE_API_URL}/jo/job-order-pick-orders${q}`, {
  1087. method: "GET",
  1088. cache: "no-store",
  1089. });
  1090. };
  1091. export const fetchJobOrderPickOrderLotDetailsForPick = cache(async (pickOrderId: number) => {
  1092. return serverFetchJson<any[]>(`${BASE_API_URL}/jo/job-order-pick-order-lot-details/${pickOrderId}`, {
  1093. method: "GET",
  1094. headers: { "Content-Type": "application/json" }
  1095. })
  1096. })
  1097. export const fetchJoForPrintQrCode = cache(async (date: string) => {
  1098. return serverFetchJson<JobOrderListForPrintQrCodeResponse[]>(
  1099. `${BASE_API_URL}/jo/joForPrintQrCode/${date}`,
  1100. {
  1101. method: "GET",
  1102. next: { tags: ["jo-print-qr-code"] },
  1103. },
  1104. );
  1105. });
  1106. // 获取已完成的 Job Order pick order records
  1107. export const fetchCompletedJobOrderPickOrderRecords = cache(async (userId: number) => {
  1108. return serverFetchJson<any[]>(
  1109. `${BASE_API_URL}/jo/completed-job-order-pick-order-records/${userId}`,
  1110. {
  1111. method: "GET",
  1112. next: { tags: ["jo-records"] },
  1113. },
  1114. );
  1115. });
  1116. export const fetchJobOrderDetailByCode = cache(async (code: string) => {
  1117. return serverFetchJson<JobOrderDetail>(
  1118. `${BASE_API_URL}/jo/detailByCode/${code}`,
  1119. {
  1120. method: "GET",
  1121. next: { tags: ["jo"] },
  1122. },
  1123. );
  1124. });
  1125. export const isOperatorExist = async (username: string) => {
  1126. const isExist = await serverFetchJson<IsOperatorExistResponse<Operator>>(
  1127. `${BASE_API_URL}/jop/isOperatorExist`,
  1128. {
  1129. method: "POST",
  1130. body: JSON.stringify({ username }),
  1131. headers: { "Content-Type": "application/json" },
  1132. },
  1133. );
  1134. revalidateTag("po");
  1135. return isExist;
  1136. };
  1137. export const isCorrectMachineUsed = async (machineCode: string) => {
  1138. const isExist = await serverFetchJson<isCorrectMachineUsedResponse<Machine>>(
  1139. `${BASE_API_URL}/jop/isCorrectMachineUsed`,
  1140. {
  1141. method: "POST",
  1142. body: JSON.stringify({ machineCode }),
  1143. headers: { "Content-Type": "application/json" },
  1144. },
  1145. );
  1146. revalidateTag("po");
  1147. return isExist;
  1148. };
  1149. export const fetchJos = cache(async (data?: SearchJoResultRequest) => {
  1150. const queryStr = convertObjToURLSearchParams(data)
  1151. console.log("queryStr", queryStr)
  1152. const fullUrl = `${BASE_API_URL}/jo/getRecordByPage?${queryStr}`;
  1153. console.log("fetchJos full URL:", fullUrl);
  1154. console.log("fetchJos BASE_API_URL:", BASE_API_URL);
  1155. const response = await serverFetchJson<SearchJoResultResponse>(
  1156. `${BASE_API_URL}/jo/getRecordByPage?${queryStr}`,
  1157. {
  1158. method: "GET",
  1159. headers: { "Content-Type": "application/json" },
  1160. next: {
  1161. tags: ["jos"]
  1162. }
  1163. }
  1164. )
  1165. // console.log("fetchJos response:", response)
  1166. return response
  1167. })
  1168. export interface PostPickOrderResponse<T = null> {
  1169. id: number | null;
  1170. name: string;
  1171. code: string;
  1172. type?: string;
  1173. message: string | null;
  1174. errorPosition: string
  1175. entity?: T | T[];
  1176. consoCode?: string;
  1177. }
  1178. export interface PickExecutionIssueData {
  1179. type: string;
  1180. pickOrderId: number;
  1181. pickOrderCode: string;
  1182. pickOrderCreateDate: string;
  1183. pickExecutionDate: string;
  1184. pickOrderLineId: number;
  1185. itemId: number;
  1186. itemCode: string;
  1187. itemDescription: string;
  1188. lotId: number|null;
  1189. lotNo: string|null;
  1190. storeLocation: string;
  1191. requiredQty: number;
  1192. actualPickQty: number;
  1193. missQty: number;
  1194. badItemQty: number;
  1195. badPackageQty?: number;
  1196. /** Optional: frontend-only reference to stock_out_line.id for the picked lot. */
  1197. stockOutLineId?: number;
  1198. issueRemark: string;
  1199. pickerName: string;
  1200. handledBy?: number;
  1201. badReason?: string;
  1202. reason?: string;
  1203. }
  1204. /** 无 miss/bad/bad package:仅后端 hold + SOL checked,不写 pick_execution_issue(避免 DUPLICATE)。 */
  1205. export const applyPickExecutionHoldAndChecked = async (data: PickExecutionIssueData) => {
  1206. const result = await serverFetchJson<PostPickOrderResponse>(
  1207. `${BASE_API_URL}/pickExecution/applyHoldAndChecked`,
  1208. {
  1209. method: "POST",
  1210. body: JSON.stringify(data),
  1211. headers: { "Content-Type": "application/json" },
  1212. },
  1213. );
  1214. revalidateTag("pickorder");
  1215. return result;
  1216. };
  1217. export const updateJo = cache(async (data: UpdateJoRequest) => {
  1218. return serverFetchJson<SaveJoResponse>(`${BASE_API_URL}/jo/update`,
  1219. {
  1220. method: "POST",
  1221. body: JSON.stringify(data),
  1222. headers: { "Content-Type": "application/json" },
  1223. })
  1224. })
  1225. export const releaseJo = cache(async (data: CommonActionJoRequest) => {
  1226. const response = serverFetchJson<CommonActionJoResponse>(`${BASE_API_URL}/jo/release`,
  1227. {
  1228. method: "POST",
  1229. body: JSON.stringify(data),
  1230. headers: { "Content-Type": "application/json" },
  1231. })
  1232. // Invalidate the cache after releasing
  1233. revalidateTag("jo");
  1234. return response;
  1235. })
  1236. export const startJo = cache(async (data: CommonActionJoRequest) => {
  1237. const response = serverFetchJson<CommonActionJoResponse>(`${BASE_API_URL}/jo/start`,
  1238. {
  1239. method: "POST",
  1240. body: JSON.stringify(data),
  1241. headers: { "Content-Type": "application/json" },
  1242. })
  1243. // Invalidate the cache after starting
  1244. revalidateTag("jo");
  1245. return response;
  1246. })
  1247. export const manualCreateJo = cache(async (data: SaveJo) => {
  1248. return serverFetchJson<SaveJoResponse>(`${BASE_API_URL}/jo/manualCreate`, {
  1249. method: "POST",
  1250. body: JSON.stringify(data),
  1251. headers: { "Content-Type": "application/json" }
  1252. })
  1253. })
  1254. export const fetchCompletedJobOrderPickOrdersWithCompletedSecondScan = cache(async (userId: number) => {
  1255. return serverFetchJson<any[]>(`${BASE_API_URL}/jo/completed-job-order-pick-orders-with-completed-second-scan/${userId}`, {
  1256. method: "GET",
  1257. headers: { "Content-Type": "application/json" }
  1258. })
  1259. })
  1260. export const fetchCompletedJobOrderPickOrderLotDetails = cache(async (pickOrderId: number) => {
  1261. return serverFetchJson<any[]>(`${BASE_API_URL}/jo/completed-job-order-pick-order-lot-details/${pickOrderId}`, {
  1262. method: "GET",
  1263. headers: { "Content-Type": "application/json" }
  1264. })
  1265. })
  1266. export const fetchCompletedJobOrderPickOrderLotDetailsForCompletedPick = cache(async (pickOrderId: number) => {
  1267. return serverFetchJson<any[]>(`${BASE_API_URL}/jo/completed-job-order-pick-order-lot-details-completed-pick/${pickOrderId}`, {
  1268. method: "GET",
  1269. headers: { "Content-Type": "application/json" }
  1270. })
  1271. })
  1272. export async function PrintPickRecord(request: PrintPickRecordRequest){
  1273. const params = new URLSearchParams();
  1274. params.append('pickOrderId', request.pickOrderId.toString())
  1275. params.append('printerId', request.printerId.toString())
  1276. if (request.printQty !== null && request.printQty !== undefined) {
  1277. params.append('printQty', request.printQty.toString());
  1278. }
  1279. if (request.floor) {
  1280. params.append('floor', request.floor);
  1281. }
  1282. //const response = await serverFetchWithNoContent(`${BASE_API_URL}/jo/print-PickRecord?${params.toString()}`,{
  1283. const response = await serverFetchWithNoContent(`${BASE_API_URL}/jo/print-PickRecord?${params.toString()}`,{
  1284. method: "GET"
  1285. });
  1286. return { success: true, message: "Print job sent successfully (Pick Record)" } as PrintPickRecordResponse;
  1287. }
  1288. export interface ExportFGStockInLabelRequest {
  1289. stockInLineId: number;
  1290. }
  1291. export const fetchFGStockInLabel = async (data: ExportFGStockInLabelRequest): Promise<FileResponse> => {
  1292. const reportBlob = await serverFetchBlob<FileResponse>(
  1293. `${BASE_API_URL}/jo/FGStockInLabel`,
  1294. {
  1295. method: "POST",
  1296. body: JSON.stringify(data),
  1297. headers: { "Content-Type": "application/json" },
  1298. },
  1299. );
  1300. return reportBlob;
  1301. };
  1302. export const updateJoPlanStart = cache(async (data: UpdateJoPlanStartRequest) => {
  1303. return serverFetchJson<SaveJoResponse>(`${BASE_API_URL}/jo/update-jo-plan-start`,
  1304. {
  1305. method: "POST",
  1306. body: JSON.stringify(data),
  1307. headers: { "Content-Type": "application/json" },
  1308. })
  1309. })
  1310. export interface UpdateProductProcessLineStatusRequest {
  1311. productProcessLineId: number;
  1312. status: string;
  1313. }
  1314. export const updateProductProcessLineStatus = async (request: UpdateProductProcessLineStatusRequest) => {
  1315. return serverFetchJson<any>(
  1316. `${BASE_API_URL}/product-process/Demo/ProcessLine/update/status`,
  1317. {
  1318. method: "POST",
  1319. body: JSON.stringify(request),
  1320. headers: { "Content-Type": "application/json" },
  1321. }
  1322. );
  1323. };
  1324. export const passProductProcessLine = async (lineId: number) => {
  1325. return serverFetchJson<any>(
  1326. `${BASE_API_URL}/product-process/Demo/ProcessLine/pass/${lineId}`,
  1327. {
  1328. method: "POST",
  1329. headers: { "Content-Type": "application/json" },
  1330. }
  1331. );
  1332. };
  1333. export interface UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTimeRequest {
  1334. productProcessLineId: number;
  1335. processingTime: number;
  1336. setupTime: number;
  1337. changeoverTime: number;
  1338. }
  1339. export const updateProductProcessLineProcessingTimeSetupTimeChangeoverTime = async (lineId: number, request: UpdateProductProcessLineProcessingTimeSetupTimeChangeoverTimeRequest) => {
  1340. return serverFetchJson<any>(
  1341. `${BASE_API_URL}/product-process/Demo/ProcessLine/update/processingTimeSetupTimeChangeoverTime/${lineId}`,
  1342. {
  1343. method: "POST",
  1344. body: JSON.stringify(request),
  1345. headers: { "Content-Type": "application/json" },
  1346. }
  1347. );
  1348. };
  1349. export interface MaterialPickStatusItem {
  1350. id: number;
  1351. pickOrderId: number | null;
  1352. pickOrderCode: string | null;
  1353. jobOrderId: number | null;
  1354. jobOrderCode: string | null;
  1355. itemId: number | null;
  1356. itemCode: string | null;
  1357. itemName: string | null;
  1358. jobOrderQty: number | null;
  1359. uom: string | null;
  1360. pickStartTime: string | null; // ISO datetime string
  1361. pickEndTime: string | null; // ISO datetime string
  1362. numberOfItemsToPick: number;
  1363. numberOfItemsWithIssue: number;
  1364. pickStatus: string | null;
  1365. }
  1366. export const fetchMaterialPickStatus = cache(async (date?: string): Promise<MaterialPickStatusItem[]> => {
  1367. const params = new URLSearchParams();
  1368. if (date) params.set("date", date); // yyyy-MM-dd
  1369. const qs = params.toString();
  1370. const url = `${BASE_API_URL}/jo/material-pick-status${qs ? `?${qs}` : ""}`;
  1371. return await serverFetchJson<MaterialPickStatusItem[]>(
  1372. url,
  1373. {
  1374. method: "GET",
  1375. }
  1376. );
  1377. })
  1378. export interface ProcessStatusInfo {
  1379. processName?: string | null;
  1380. equipmentName?: string | null;
  1381. equipmentDetailName?: string | null;
  1382. /** 經手人姓名(對應 product process line.handler) */
  1383. handlerName?: string | null;
  1384. startTime?: string | null;
  1385. endTime?: string | null;
  1386. isRequired: boolean;
  1387. }
  1388. export interface JobProcessStatusResponse {
  1389. jobOrderId: number;
  1390. jobOrderCode: string;
  1391. itemCode: string;
  1392. itemName: string;
  1393. status: string;
  1394. processingTime: number | null;
  1395. setupTime: number | null;
  1396. changeoverTime: number | null;
  1397. planEndTime?: string | null;
  1398. processes: ProcessStatusInfo[];
  1399. }
  1400. export const fetchJobProcessStatus = cache(
  1401. async (date?: string, productProcessStatus?: string | null) => {
  1402. const params = new URLSearchParams();
  1403. if (date) params.set("date", date); // yyyy-MM-dd
  1404. if (productProcessStatus && productProcessStatus.length > 0) {
  1405. params.set("productProcessStatus", productProcessStatus);
  1406. }
  1407. const qs = params.toString();
  1408. const url = `${BASE_API_URL}/product-process/Demo/JobProcessStatus${qs ? `?${qs}` : ""}`;
  1409. return serverFetchJson<JobProcessStatusResponse[]>(url, {
  1410. method: "GET",
  1411. next: { tags: ["jobProcessStatus"] },
  1412. });
  1413. },
  1414. );
  1415. // ===== Operator KPI Dashboard =====
  1416. export interface OperatorKpiProcessInfo {
  1417. jobOrderId?: number | null;
  1418. jobOrderCode?: string | null;
  1419. productProcessId?: number | null;
  1420. productProcessLineId?: number | null;
  1421. processName?: string | null;
  1422. equipmentName?: string | null;
  1423. equipmentDetailName?: string | null;
  1424. startTime?: string | number[] | null;
  1425. endTime?: string | number[] | null;
  1426. processingTime?: number | null;
  1427. itemCode?: string | null;
  1428. itemName?: string | null;
  1429. }
  1430. export interface OperatorKpiResponse {
  1431. operatorId: number;
  1432. operatorName?: string | null;
  1433. staffNo?: string | null;
  1434. totalProcessingMinutes: number;
  1435. totalJobOrderCount: number;
  1436. currentProcesses: OperatorKpiProcessInfo[];
  1437. }
  1438. export const fetchOperatorKpi = cache(async (date?: string) => {
  1439. const params = new URLSearchParams();
  1440. if (date) params.set("date", date);
  1441. const qs = params.toString();
  1442. const url = `${BASE_API_URL}/product-process/Demo/OperatorKpi${qs ? `?${qs}` : ""}`;
  1443. return serverFetchJson<OperatorKpiResponse[]>(url, {
  1444. method: "GET",
  1445. next: { tags: ["operatorKpi"] },
  1446. });
  1447. });
  1448. // ===== Equipment Status Dashboard =====
  1449. export interface EquipmentStatusProcessInfo {
  1450. jobOrderId?: number | null;
  1451. jobOrderCode?: string | null;
  1452. productProcessId?: number | null;
  1453. productProcessLineId?: number | null;
  1454. processName?: string | null;
  1455. operatorName?: string | null;
  1456. startTime?: string | number[] | null;
  1457. processingTime?: number | null;
  1458. }
  1459. export interface EquipmentStatusPerDetail {
  1460. equipmentDetailId: number;
  1461. equipmentDetailCode?: string | null;
  1462. equipmentDetailName?: string | null;
  1463. equipmentId?: number | null;
  1464. equipmentTypeName?: string | null;
  1465. status: string;
  1466. repairAndMaintenanceStatus?: boolean | null;
  1467. latestRepairAndMaintenanceDate?: string | null;
  1468. lastRepairAndMaintenanceDate?: string | null;
  1469. repairAndMaintenanceRemarks?: string | null;
  1470. currentProcess?: EquipmentStatusProcessInfo | null;
  1471. }
  1472. export interface EquipmentStatusByTypeResponse {
  1473. equipmentTypeId: number;
  1474. equipmentTypeName?: string | null;
  1475. details: EquipmentStatusPerDetail[];
  1476. }
  1477. export const fetchEquipmentStatus = cache(async () => {
  1478. const url = `${BASE_API_URL}/product-process/Demo/EquipmentStatus`;
  1479. return serverFetchJson<EquipmentStatusByTypeResponse[]>(url, {
  1480. method: "GET",
  1481. next: { tags: ["equipmentStatus"] },
  1482. });
  1483. });
  1484. export const deleteProductProcessLine = async (lineId: number) => {
  1485. return serverFetchJson<any>(
  1486. `${BASE_API_URL}/product-process/Demo/ProcessLine/delete/${lineId}`,
  1487. {
  1488. method: "POST",
  1489. headers: { "Content-Type": "application/json" },
  1490. }
  1491. );
  1492. };
  1493. ;