FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

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