FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 

1608 строки
44 KiB

  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. ;