diff --git a/src/app/api/stockLedgerFix/client.ts b/src/app/api/stockLedgerFix/client.ts index 2b0d9778..2d16121c 100644 --- a/src/app/api/stockLedgerFix/client.ts +++ b/src/app/api/stockLedgerFix/client.ts @@ -46,6 +46,11 @@ export type StockLedgerFixRunResponse = { stillMissUom: number; stillMissInventoryId: number; stillMissLotQty: number; + deletedUnlinkedLot?: number; + existingLotMatch?: number; + existingLotMismatch?: number; + deletedBlankOutLine?: number; + alignedOutLine?: number; }; export type StockLedgerFixInventoryPreview = { @@ -103,6 +108,7 @@ export type StockLedgerFixAdjResponse = { filledLotQty: number; filledBalance: number; dayRowsWritten: number; + adjustmentRecords?: number; }; export async function runStockLedgerFixAdj( @@ -265,7 +271,7 @@ export async function downloadStockLedgerFixSql( const a = document.createElement("a"); a.href = url; const partTag = - parts && parts.length > 0 && parts.length < 5 + parts && parts.length > 0 && parts.length < 6 ? `_${parts.join("-").replaceAll(".", "")}` : ""; a.download = `stock_ledger_fix_${from.replaceAll("-", "")}_${to.replaceAll("-", "")}${partTag}.sql`; diff --git a/src/components/StockLedgerFix/StockLedgerFixPageClient.tsx b/src/components/StockLedgerFix/StockLedgerFixPageClient.tsx index 08bcc880..bad36cd1 100644 --- a/src/components/StockLedgerFix/StockLedgerFixPageClient.tsx +++ b/src/components/StockLedgerFix/StockLedgerFixPageClient.tsx @@ -43,6 +43,7 @@ import { type StockLedgerFixCheckPart, type StockLedgerFixDayDetail, type StockLedgerFixInventoryPreview, + type StockLedgerFixRunResponse, type StockLedgerFixScopeDetail, type StockLedgerFixSearchInventoryHit, type StockLedgerFixSearchLotHit, @@ -70,10 +71,11 @@ const DAY_STEP_LABEL: Record = { }; /** Export SQL parts (aligned with fix steps + 1.0 / 2.7). */ -const EXPORT_PARTS = ["1.0", "2.3", "ledger", "2.6", "2.7"] as const; +const EXPORT_PARTS = ["1.0", "2.1", "2.3", "ledger", "2.6", "2.7"] as const; type ExportPart = (typeof EXPORT_PARTS)[number]; const DEFAULT_EXPORT_PARTS: Record = { "1.0": false, + "2.1": false, "2.3": false, ledger: true, "2.6": true, @@ -81,6 +83,7 @@ const DEFAULT_EXPORT_PARTS: Record = { }; const FULL_EXPORT_PARTS: Record = { "1.0": true, + "2.1": true, "2.3": true, ledger: true, "2.6": true, @@ -88,10 +91,11 @@ const FULL_EXPORT_PARTS: Record = { }; const EXPORT_PART_LABEL: Record = { "1.0": "1.0 inventory", + "2.1": "2.1 軟刪流水帳+出倉列批號", "2.3": "2.3 inventoryId", ledger: "ledger lot/uom/lotQty/balance", "2.6": "2.6 日結", - "2.7": "2.7 ADJ+入出倉單", + "2.7": "2.7 ADJ+入出倉單+調整記錄", }; function selectedExportParts(flags: Record): ExportPart[] { @@ -115,6 +119,16 @@ function stepsPayload(flags: Record): string[] | undefined return selected; } +function lotLineFixNote(res: StockLedgerFixRunResponse): string { + const deleted = res.deletedUnlinkedLot ?? 0; + const matched = res.existingLotMatch ?? 0; + const mismatched = res.existingLotMismatch ?? 0; + const blankOut = res.deletedBlankOutLine ?? 0; + const alignedOut = res.alignedOutLine ?? 0; + if (deleted === 0 && matched === 0 && mismatched === 0 && blankOut === 0 && alignedOut === 0) return ""; + return `、軟刪未連批號 ${deleted}、出倉列空而軟刪 ${blankOut}、出倉列已改跟帳本 ${alignedOut}、已有批號相符 ${matched}、不相符 ${mismatched}`; +} + function apiErrorMessage(e: unknown, fallback: string): string { if (e && typeof e === "object" && "response" in e) { const data = (e as { response?: { data?: unknown } }).response?.data; @@ -380,7 +394,7 @@ const StockLedgerFixPageClient: React.FC = () => { try { const res = await runStockLedgerFixDay(date, steps); setFixMessage( - `已修 ${res.date}(${stepLabel}):lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`, + `已修 ${res.date}(${stepLabel}):lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}${lotLineFixNote(res)}`, ); await loadDay(date); } catch (e) { @@ -429,7 +443,7 @@ const StockLedgerFixPageClient: React.FC = () => { return; } const ok = window.confirm( - `會在 ${d} 對最後餘額不等於 onHandQty 的庫存桶各寫一筆 ADJ(入或出),掛在既有 lot 上。不改 lot line、不改 inventory。已對齊的桶不會再寫。確定?`, + `會在 ${d} 對最後餘額不等於 onHandQty 的庫存桶各寫一筆 ADJ(入或出),掛在既有 lot 上,並寫入庫存調整記錄。不改 lot line、不改 inventory。已對齊的桶不會再寫;缺調整記錄的舊 ADJ 會補上。確定?`, ); if (!ok) return; adjRunInFlight.current = true; @@ -439,7 +453,7 @@ const StockLedgerFixPageClient: React.FC = () => { try { const res = await runStockLedgerFixAdj(d); setAdjMessage( - `ADJ ${res.adjDate}:入 ${res.insertedIn}、出 ${res.insertedOut}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`, + `ADJ ${res.adjDate}:入 ${res.insertedIn}、出 ${res.insertedOut}、調整記錄 ${res.adjustmentRecords ?? 0}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`, ); } catch (e) { console.error(e); @@ -535,7 +549,7 @@ const StockLedgerFixPageClient: React.FC = () => { try { const res = await runStockLedgerFixInventoryScope(scope.id); setFixMessage( - `已修 inventory ${scope.id}:lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`, + `已修 inventory ${scope.id}:lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}${lotLineFixNote(res)}`, ); await loadInventoryScope(scope.id); } catch (e) { @@ -560,7 +574,7 @@ const StockLedgerFixPageClient: React.FC = () => { try { const res = await runStockLedgerFixLotScope(scope.id); setFixMessage( - `已修 lot ${scope.id}:lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、日結 ${res.dayRowsWritten}`, + `已修 lot ${scope.id}:lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、日結 ${res.dayRowsWritten}${lotLineFixNote(res)}`, ); await loadLotScope(scope.id); } catch (e) { @@ -616,7 +630,7 @@ const StockLedgerFixPageClient: React.FC = () => { try { const res = await runStockLedgerFixRange(from, to, steps); setFixMessage( - `區間修完成 ${res.date}(${stepLabel}):lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`, + `區間修完成 ${res.date}(${stepLabel}):lot ${res.filledLotLineId}、uom ${res.filledUomId}、inventory ${res.filledInventoryId}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}${lotLineFixNote(res)}`, ); setSelected(dayjs(to)); setChainProgress(null); @@ -907,8 +921,8 @@ const StockLedgerFixPageClient: React.FC = () => { 匯出已修 SQL(新庫用) - 勾選要匯出的段落(同日修步驟)。套檔順序:1.0 → ledger(±2.3)→ 2.7 入出倉單+ADJ INSERT → - 2.6。完整檔(含 1.0+2.3)且正式庫 freeze/id 同源時,可跳過正式庫 1.0/2.3。預設只匯 + 勾選要匯出的段落(同日修步驟)。套檔順序:1.0 → 2.1 → ledger(±2.3)→ 2.7 入出倉單+調整記錄+ADJ → + 2.6。完整檔且正式庫 freeze/id 同源、日期蓋過全部已修日子時,結果與已修庫相同。預設只匯 ledger+日結(舊行為)。