"use client"; import { NEXT_PUBLIC_API_URL } from "@/config/api"; import { clientAuthFetch } from "@/app/utils/clientAuthFetch"; import { exportMultiSheetToXlsx, } from "@/app/(main)/chart/_components/exportChartToXlsx"; import { reportExcelT as tx } from "./reportI18n"; import type { TFunction } from "i18next"; export interface BomShopSyncReportSummary { totalAttempts?: number; success?: number; skippedUnchanged?: number; failed?: number; syncDateStart?: string; syncDateEnd?: string; } export interface BomShopSyncRow { syncLogId?: number; syncDateTime?: string; bomId?: number; bomRoutingCode?: string; finishedItemCode?: string; finishedItemName?: string; m18HeaderCode?: string; version?: string; m18RecordId?: number; syncStatus?: string; synced?: boolean; m18ApiStatus?: boolean; failureReason?: string; message?: string; } export interface BomShopSyncMaterialRow { syncLogId?: number; syncDateTime?: string; bomId?: number; finishedItemCode?: string; m18HeaderCode?: string; version?: string; syncStatus?: string; lineNo?: string; materialName?: string; udfProductM18Id?: number; udfBaseUnit?: string; udfQty?: number; udfSupplierM18Id?: number; udfPurchaseUnitM18Id?: number; } export interface BomShopSyncReportResponse { summary?: BomShopSyncReportSummary; syncRows?: BomShopSyncRow[]; materialRows?: BomShopSyncMaterialRow[]; } function bomSyncLabels(t?: TFunction) { return { sheetSync: tx(t, "excel.bomSync.sheetSync", "BOM同步記錄"), sheetMaterials: tx(t, "excel.bomSync.sheetMaterials", "BOM物料明細"), noData: tx(t, "excel.noData", "(篩選範圍內無資料)"), syncTime: tx(t, "excel.bomSync.syncTime", "同步時間"), finishedItemCode: tx(t, "excel.bomSync.finishedItemCode", "成品貨號"), finishedItemName: tx(t, "excel.bomSync.finishedItemName", "成品名稱"), bomRoutingCode: tx(t, "excel.bomSync.bomRoutingCode", "BOM路由編號"), version: tx(t, "excel.bomSync.version", "版本"), status: tx(t, "excel.bomSync.status", "狀態"), failureReason: tx(t, "excel.bomSync.failureReason", "失敗原因"), message: tx(t, "excel.bomSync.message", "訊息"), lineNo: tx(t, "excel.bomSync.lineNo", "行號"), materialName: tx(t, "excel.bomSync.materialName", "物料名稱"), uom: tx(t, "excel.bomSync.uom", "單位"), qty: tx(t, "excel.bomSync.qty", "用量"), statusSuccess: tx(t, "excel.bomSync.statusSuccess", "成功"), statusSkipped: tx(t, "excel.bomSync.statusSkipped", "略過(內容未變)"), statusFailed: tx(t, "excel.bomSync.statusFailed", "失敗"), }; } function emptySyncSheetRow( L: ReturnType, note?: string, ): Record { return { [L.syncTime]: note ?? L.noData, [L.finishedItemCode]: "", [L.finishedItemName]: "", [L.bomRoutingCode]: "", "M18 BOM Code": "", [L.version]: "", "M18 Record Id": "", [L.status]: "", [L.failureReason]: "", [L.message]: "", "BOM Id": "", "Sync Log Id": "", }; } function emptyMaterialSheetRow( L: ReturnType, note?: string, ): Record { return { [L.syncTime]: note ?? L.noData, [L.finishedItemCode]: "", "M18 BOM Code": "", [L.version]: "", [L.status]: "", [L.lineNo]: "", [L.materialName]: "", "M18 Product Id": "", [L.uom]: "", [L.qty]: "", "M18 Supplier Id": "", "M18 Purchase Unit Id": "", "Sync Log Id": "", }; } export async function fetchBomShopSyncReportData( criteria: Record, ): Promise { const queryParams = new URLSearchParams(criteria).toString(); const url = `${NEXT_PUBLIC_API_URL}/report/bom-shop-sync-history?${queryParams}`; const response = await clientAuthFetch(url, { method: "GET", headers: { Accept: "application/json" }, }); if (response.status === 401 || response.status === 403) throw new Error("Unauthorized"); if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); return (await response.json()) as BomShopSyncReportResponse; } function syncStatusLabel( status: string | undefined, L: ReturnType, ): string { switch (status) { case "SUCCESS": return L.statusSuccess; case "SKIPPED_UNCHANGED": return L.statusSkipped; case "FAILED": return L.statusFailed; default: return status ?? ""; } } function toSyncExcelRow( r: BomShopSyncRow, L: ReturnType, ): Record { return { ...emptySyncSheetRow(L, ""), [L.syncTime]: r.syncDateTime ?? "", [L.finishedItemCode]: r.finishedItemCode ?? "", [L.finishedItemName]: r.finishedItemName ?? "", [L.bomRoutingCode]: r.bomRoutingCode ?? "", "M18 BOM Code": r.m18HeaderCode ?? "", [L.version]: r.version ?? "", "M18 Record Id": r.m18RecordId ?? "", [L.status]: syncStatusLabel(r.syncStatus, L), [L.failureReason]: r.failureReason ?? "", [L.message]: r.message ?? "", "BOM Id": r.bomId ?? "", "Sync Log Id": r.syncLogId ?? "", }; } function toMaterialExcelRow( r: BomShopSyncMaterialRow, L: ReturnType, ): Record { return { ...emptyMaterialSheetRow(L, ""), [L.syncTime]: r.syncDateTime ?? "", [L.finishedItemCode]: r.finishedItemCode ?? "", "M18 BOM Code": r.m18HeaderCode ?? "", [L.version]: r.version ?? "", [L.status]: syncStatusLabel(r.syncStatus, L), [L.lineNo]: r.lineNo ?? "", [L.materialName]: r.materialName ?? "", "M18 Product Id": r.udfProductM18Id ?? "", [L.uom]: r.udfBaseUnit ?? "", [L.qty]: r.udfQty ?? "", "M18 Supplier Id": r.udfSupplierM18Id ?? "", "M18 Purchase Unit Id": r.udfPurchaseUnitM18Id ?? "", "Sync Log Id": r.syncLogId ?? "", }; } export async function generateBomShopSyncReportExcel( criteria: Record, reportTitle: string = "M18 BOM Shop 同步記錄", t?: TFunction, ): Promise { const L = bomSyncLabels(t); const data = await fetchBomShopSyncReportData(criteria); const syncRows = (data.syncRows ?? []).length > 0 ? (data.syncRows ?? []).map((r) => toSyncExcelRow(r, L)) : [emptySyncSheetRow(L)]; const materialRows = (data.materialRows ?? []).length > 0 ? (data.materialRows ?? []).map((r) => toMaterialExcelRow(r, L)) : [emptyMaterialSheetRow(L)]; const start = criteria.syncDateStart; const end = criteria.syncDateEnd; let datePart: string; if (start && end && start === end) { datePart = start; } else if (start || end) { datePart = `${start || ""}_to_${end || ""}`; } else { datePart = new Date().toISOString().slice(0, 10); } const filename = `${reportTitle}_${datePart.replace(/[^\d\-_/]/g, "")}`; exportMultiSheetToXlsx( [ { name: L.sheetSync, rows: syncRows }, { name: L.sheetMaterials, rows: materialRows }, ], filename, ); }