FPSMS-frontend
25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
 
 

238 satır
6.9 KiB

  1. "use client";
  2. import { NEXT_PUBLIC_API_URL } from "@/config/api";
  3. import { clientAuthFetch } from "@/app/utils/clientAuthFetch";
  4. import {
  5. exportMultiSheetToXlsx,
  6. } from "@/app/(main)/chart/_components/exportChartToXlsx";
  7. import { reportExcelT as tx } from "./reportI18n";
  8. import type { TFunction } from "i18next";
  9. export interface BomShopSyncReportSummary {
  10. totalAttempts?: number;
  11. success?: number;
  12. skippedUnchanged?: number;
  13. failed?: number;
  14. syncDateStart?: string;
  15. syncDateEnd?: string;
  16. }
  17. export interface BomShopSyncRow {
  18. syncLogId?: number;
  19. syncDateTime?: string;
  20. bomId?: number;
  21. bomRoutingCode?: string;
  22. finishedItemCode?: string;
  23. finishedItemName?: string;
  24. m18HeaderCode?: string;
  25. version?: string;
  26. m18RecordId?: number;
  27. syncStatus?: string;
  28. synced?: boolean;
  29. m18ApiStatus?: boolean;
  30. failureReason?: string;
  31. message?: string;
  32. }
  33. export interface BomShopSyncMaterialRow {
  34. syncLogId?: number;
  35. syncDateTime?: string;
  36. bomId?: number;
  37. finishedItemCode?: string;
  38. m18HeaderCode?: string;
  39. version?: string;
  40. syncStatus?: string;
  41. lineNo?: string;
  42. materialName?: string;
  43. udfProductM18Id?: number;
  44. udfBaseUnit?: string;
  45. udfQty?: number;
  46. udfSupplierM18Id?: number;
  47. udfPurchaseUnitM18Id?: number;
  48. }
  49. export interface BomShopSyncReportResponse {
  50. summary?: BomShopSyncReportSummary;
  51. syncRows?: BomShopSyncRow[];
  52. materialRows?: BomShopSyncMaterialRow[];
  53. }
  54. function bomSyncLabels(t?: TFunction) {
  55. return {
  56. sheetSync: tx(t, "excel.bomSync.sheetSync", "BOM同步記錄"),
  57. sheetMaterials: tx(t, "excel.bomSync.sheetMaterials", "BOM物料明細"),
  58. noData: tx(t, "excel.noData", "(篩選範圍內無資料)"),
  59. syncTime: tx(t, "excel.bomSync.syncTime", "同步時間"),
  60. finishedItemCode: tx(t, "excel.bomSync.finishedItemCode", "成品貨號"),
  61. finishedItemName: tx(t, "excel.bomSync.finishedItemName", "成品名稱"),
  62. bomRoutingCode: tx(t, "excel.bomSync.bomRoutingCode", "BOM路由編號"),
  63. version: tx(t, "excel.bomSync.version", "版本"),
  64. status: tx(t, "excel.bomSync.status", "狀態"),
  65. failureReason: tx(t, "excel.bomSync.failureReason", "失敗原因"),
  66. message: tx(t, "excel.bomSync.message", "訊息"),
  67. lineNo: tx(t, "excel.bomSync.lineNo", "行號"),
  68. materialName: tx(t, "excel.bomSync.materialName", "物料名稱"),
  69. uom: tx(t, "excel.bomSync.uom", "單位"),
  70. qty: tx(t, "excel.bomSync.qty", "用量"),
  71. statusSuccess: tx(t, "excel.bomSync.statusSuccess", "成功"),
  72. statusSkipped: tx(t, "excel.bomSync.statusSkipped", "略過(內容未變)"),
  73. statusFailed: tx(t, "excel.bomSync.statusFailed", "失敗"),
  74. };
  75. }
  76. function emptySyncSheetRow(
  77. L: ReturnType<typeof bomSyncLabels>,
  78. note?: string,
  79. ): Record<string, unknown> {
  80. return {
  81. [L.syncTime]: note ?? L.noData,
  82. [L.finishedItemCode]: "",
  83. [L.finishedItemName]: "",
  84. [L.bomRoutingCode]: "",
  85. "M18 BOM Code": "",
  86. [L.version]: "",
  87. "M18 Record Id": "",
  88. [L.status]: "",
  89. [L.failureReason]: "",
  90. [L.message]: "",
  91. "BOM Id": "",
  92. "Sync Log Id": "",
  93. };
  94. }
  95. function emptyMaterialSheetRow(
  96. L: ReturnType<typeof bomSyncLabels>,
  97. note?: string,
  98. ): Record<string, unknown> {
  99. return {
  100. [L.syncTime]: note ?? L.noData,
  101. [L.finishedItemCode]: "",
  102. "M18 BOM Code": "",
  103. [L.version]: "",
  104. [L.status]: "",
  105. [L.lineNo]: "",
  106. [L.materialName]: "",
  107. "M18 Product Id": "",
  108. [L.uom]: "",
  109. [L.qty]: "",
  110. "M18 Supplier Id": "",
  111. "M18 Purchase Unit Id": "",
  112. "Sync Log Id": "",
  113. };
  114. }
  115. export async function fetchBomShopSyncReportData(
  116. criteria: Record<string, string>,
  117. ): Promise<BomShopSyncReportResponse> {
  118. const queryParams = new URLSearchParams(criteria).toString();
  119. const url = `${NEXT_PUBLIC_API_URL}/report/bom-shop-sync-history?${queryParams}`;
  120. const response = await clientAuthFetch(url, {
  121. method: "GET",
  122. headers: { Accept: "application/json" },
  123. });
  124. if (response.status === 401 || response.status === 403)
  125. throw new Error("Unauthorized");
  126. if (!response.ok)
  127. throw new Error(`HTTP error! status: ${response.status}`);
  128. return (await response.json()) as BomShopSyncReportResponse;
  129. }
  130. function syncStatusLabel(
  131. status: string | undefined,
  132. L: ReturnType<typeof bomSyncLabels>,
  133. ): string {
  134. switch (status) {
  135. case "SUCCESS":
  136. return L.statusSuccess;
  137. case "SKIPPED_UNCHANGED":
  138. return L.statusSkipped;
  139. case "FAILED":
  140. return L.statusFailed;
  141. default:
  142. return status ?? "";
  143. }
  144. }
  145. function toSyncExcelRow(
  146. r: BomShopSyncRow,
  147. L: ReturnType<typeof bomSyncLabels>,
  148. ): Record<string, unknown> {
  149. return {
  150. ...emptySyncSheetRow(L, ""),
  151. [L.syncTime]: r.syncDateTime ?? "",
  152. [L.finishedItemCode]: r.finishedItemCode ?? "",
  153. [L.finishedItemName]: r.finishedItemName ?? "",
  154. [L.bomRoutingCode]: r.bomRoutingCode ?? "",
  155. "M18 BOM Code": r.m18HeaderCode ?? "",
  156. [L.version]: r.version ?? "",
  157. "M18 Record Id": r.m18RecordId ?? "",
  158. [L.status]: syncStatusLabel(r.syncStatus, L),
  159. [L.failureReason]: r.failureReason ?? "",
  160. [L.message]: r.message ?? "",
  161. "BOM Id": r.bomId ?? "",
  162. "Sync Log Id": r.syncLogId ?? "",
  163. };
  164. }
  165. function toMaterialExcelRow(
  166. r: BomShopSyncMaterialRow,
  167. L: ReturnType<typeof bomSyncLabels>,
  168. ): Record<string, unknown> {
  169. return {
  170. ...emptyMaterialSheetRow(L, ""),
  171. [L.syncTime]: r.syncDateTime ?? "",
  172. [L.finishedItemCode]: r.finishedItemCode ?? "",
  173. "M18 BOM Code": r.m18HeaderCode ?? "",
  174. [L.version]: r.version ?? "",
  175. [L.status]: syncStatusLabel(r.syncStatus, L),
  176. [L.lineNo]: r.lineNo ?? "",
  177. [L.materialName]: r.materialName ?? "",
  178. "M18 Product Id": r.udfProductM18Id ?? "",
  179. [L.uom]: r.udfBaseUnit ?? "",
  180. [L.qty]: r.udfQty ?? "",
  181. "M18 Supplier Id": r.udfSupplierM18Id ?? "",
  182. "M18 Purchase Unit Id": r.udfPurchaseUnitM18Id ?? "",
  183. "Sync Log Id": r.syncLogId ?? "",
  184. };
  185. }
  186. export async function generateBomShopSyncReportExcel(
  187. criteria: Record<string, string>,
  188. reportTitle: string = "M18 BOM Shop 同步記錄",
  189. t?: TFunction,
  190. ): Promise<void> {
  191. const L = bomSyncLabels(t);
  192. const data = await fetchBomShopSyncReportData(criteria);
  193. const syncRows =
  194. (data.syncRows ?? []).length > 0
  195. ? (data.syncRows ?? []).map((r) => toSyncExcelRow(r, L))
  196. : [emptySyncSheetRow(L)];
  197. const materialRows =
  198. (data.materialRows ?? []).length > 0
  199. ? (data.materialRows ?? []).map((r) => toMaterialExcelRow(r, L))
  200. : [emptyMaterialSheetRow(L)];
  201. const start = criteria.syncDateStart;
  202. const end = criteria.syncDateEnd;
  203. let datePart: string;
  204. if (start && end && start === end) {
  205. datePart = start;
  206. } else if (start || end) {
  207. datePart = `${start || ""}_to_${end || ""}`;
  208. } else {
  209. datePart = new Date().toISOString().slice(0, 10);
  210. }
  211. const filename = `${reportTitle}_${datePart.replace(/[^\d\-_/]/g, "")}`;
  212. exportMultiSheetToXlsx(
  213. [
  214. { name: L.sheetSync, rows: syncRows },
  215. { name: L.sheetMaterials, rows: materialRows },
  216. ],
  217. filename,
  218. );
  219. }