Ver código fonte

Liquibase 20260910:#1 stockUomId、#2–3 Option A trigger、#35 index

上線 app:#5 entity、#7 available = onHand − unavailable、#8 搜尋全 bucket、#34 Resolver(#10/#11 已接)
Admin 1.0:補 stockUomId、uomId=base(這就是 #1 的 data patch)
2.3 把 ledger 綁到該 bucket
2.7:先超發 remain→0(改 lot),再 miss;共用一張入倉單、一張出倉單
2.6 日結
fix負數倉
CANCERYS\kw093 1 semana atrás
pai
commit
ca36da92f2
2 arquivos alterados com 28 adições e 15 exclusões
  1. +7
    -0
      src/app/api/stockLedgerFix/client.ts
  2. +21
    -15
      src/components/StockLedgerFix/StockLedgerFixPageClient.tsx

+ 7
- 0
src/app/api/stockLedgerFix/client.ts Ver arquivo

@@ -52,12 +52,15 @@ export type StockLedgerFixInventoryPreview = {
inventoryRows: number; inventoryRows: number;
lotUomPairs: number; lotUomPairs: number;
missingUomPairs: number; missingUomPairs: number;
nullStockUomId: number;
}; };


export type StockLedgerFixInventoryResponse = { export type StockLedgerFixInventoryResponse = {
inserted: number; inserted: number;
updated: number; updated: number;
missingUomPairsAfter: number; missingUomPairsAfter: number;
patchedStockUomId: number;
nullStockUomIdAfter: number;
}; };


export type StockLedgerFixSearchInventoryHit = { export type StockLedgerFixSearchInventoryHit = {
@@ -101,6 +104,7 @@ export type StockLedgerFixAdjRow = {
ledgerOut: string; ledgerOut: string;
missIn: string; missIn: string;
missOut: string; missOut: string;
overIssue: string;
}; };


export type StockLedgerFixAdjPreview = { export type StockLedgerFixAdjPreview = {
@@ -112,6 +116,8 @@ export type StockLedgerFixAdjPreview = {
sumMissIn: string; sumMissIn: string;
sumMissOut: string; sumMissOut: string;
skuNet: string; skuNet: string;
overIssueCount: number;
sumOverIssue: string;
rows: StockLedgerFixAdjRow[]; rows: StockLedgerFixAdjRow[];
}; };


@@ -119,6 +125,7 @@ export type StockLedgerFixAdjResponse = {
adjDate: string; adjDate: string;
insertedIn: number; insertedIn: number;
insertedOut: number; insertedOut: number;
overIssuePatched: number;
filledLotQty: number; filledLotQty: number;
filledBalance: number; filledBalance: number;
dayRowsWritten: number; dayRowsWritten: number;


+ 21
- 15
src/components/StockLedgerFix/StockLedgerFixPageClient.tsx Ver arquivo

@@ -93,7 +93,7 @@ const EXPORT_PART_LABEL: Record<ExportPart, string> = {
"2.3": "2.3 inventoryId", "2.3": "2.3 inventoryId",
ledger: "ledger lot/uom/lotQty/balance", ledger: "ledger lot/uom/lotQty/balance",
"2.6": "2.6 日結", "2.6": "2.6 日結",
"2.7": "2.7 ADJ INSERT",
"2.7": "2.7 ADJ+入出倉單",
}; };


function selectedExportParts(flags: Record<ExportPart, boolean>): ExportPart[] { function selectedExportParts(flags: Record<ExportPart, boolean>): ExportPart[] {
@@ -426,7 +426,7 @@ const StockLedgerFixPageClient: React.FC = () => {
const onInventory = async () => { const onInventory = async () => {
if (inventoryRunInFlight.current) return; if (inventoryRunInFlight.current) return;
const ok = window.confirm( const ok = window.confirm(
"方案 A:保留現有 inventory.id,補缺 (itemId, uomId),並用 lot 的 in−out 重算全部數量。沒有 lot 的 UOM 會變成 0。不會改 inventory_lot_line。確定執行?",
"方案 A:保留現有 inventory.id;從 lot 回填 stockUomId(舊 uomId 是 stock 則改回 base);補缺 (itemId, stockUomId);用 lot 的 in−out 重算 onHand/unavailable。不寫 onHoldQty、不改 inventory_lot_line。確定執行?",
); );
if (!ok) return; if (!ok) return;
inventoryRunInFlight.current = true; inventoryRunInFlight.current = true;
@@ -436,7 +436,7 @@ const StockLedgerFixPageClient: React.FC = () => {
try { try {
const res = await runStockLedgerFixInventory(); const res = await runStockLedgerFixInventory();
setInventoryMessage( setInventoryMessage(
`inventory 1.0 完成:新增 ${res.inserted} 列、重算 ${res.updated} 列、之後仍缺 UOM ${res.missingUomPairsAfter}`,
`inventory 1.0 完成:回填 stockUomId ${res.patchedStockUomId} 列、新增 ${res.inserted} 列、重算 ${res.updated} 列、之後仍缺 UOM ${res.missingUomPairsAfter}、仍 NULL stockUomId ${res.nullStockUomIdAfter}`,
); );
await loadInventory(); await loadInventory();
} catch (e) { } catch (e) {
@@ -456,7 +456,7 @@ const StockLedgerFixPageClient: React.FC = () => {
return; return;
} }
const ok = window.confirm( const ok = window.confirm(
`會在 ${d} 新增 ADJ:入向 ${adjPreview?.adjInCount ?? 0} 筆、出向 ${adjPreview?.adjOutCount ?? 0} 筆(含反向調整)。缺入合計 ${adjPreview?.sumMissIn ?? "?"}、缺出合計 ${adjPreview?.sumMissOut ?? "?"}。目標:每 lot 最後 lotQtyAfter = line remain。已對齊的 lot 不會再動。確定?`,
`會在 ${d} 先修超發 remain→0(${adjPreview?.overIssueCount ?? 0} 條 lot,合計 ${adjPreview?.sumOverIssue ?? "?"},會改 lot inQty)。再補 miss ADJ:入向 ${adjPreview?.adjInCount ?? 0} 筆、出向 ${adjPreview?.adjOutCount ?? 0} 筆。入向全部掛同一張入倉單、出向全部掛同一張出倉單(miss 不改 lot 數量)。缺入合計 ${adjPreview?.sumMissIn ?? "?"}、缺出合計 ${adjPreview?.sumMissOut ?? "?"}。確定?`,
); );
if (!ok) return; if (!ok) return;
adjRunInFlight.current = true; adjRunInFlight.current = true;
@@ -466,7 +466,7 @@ const StockLedgerFixPageClient: React.FC = () => {
try { try {
const res = await runStockLedgerFixAdj(d); const res = await runStockLedgerFixAdj(d);
setAdjMessage( setAdjMessage(
`ADJ ${res.adjDate}:入 ${res.insertedIn}、出 ${res.insertedOut}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`,
`ADJ ${res.adjDate}:超發 remain→0 ${res.overIssuePatched}、入 ${res.insertedIn}、出 ${res.insertedOut}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`,
); );
await loadAdjPreview(); await loadAdjPreview();
} catch (e) { } catch (e) {
@@ -631,7 +631,7 @@ const StockLedgerFixPageClient: React.FC = () => {
const steps = stepsPayload(daySteps); const steps = stepsPayload(daySteps);
const stepLabel = steps?.join("、") ?? "全部 2.1–2.6"; const stepLabel = steps?.join("、") ?? "全部 2.1–2.6";
const ok = window.confirm( const ok = window.confirm(
`一次修 ${from} → ${to}(步驟:${stepLabel})。2.1–2.5 整段一次算、2.6 仍依日寫日結。過程中無法中停。確定?`,
`一次修 ${from} → ${to}(步驟:${stepLabel})。改為逐日跑(每天 commit)。中途失敗時已跑完的天會留下。確定?`,
); );
if (!ok) return; if (!ok) return;


@@ -724,7 +724,8 @@ const StockLedgerFixPageClient: React.FC = () => {
<Typography variant="body2"> <Typography variant="body2">
現有 inventory {inventoryPreview.inventoryRows} 列;lot 的 現有 inventory {inventoryPreview.inventoryRows} 列;lot 的
item+UOM {inventoryPreview.lotUomPairs} 組;缺{" "} item+UOM {inventoryPreview.lotUomPairs} 組;缺{" "}
{inventoryPreview.missingUomPairs} 組。
{inventoryPreview.missingUomPairs} 組;stockUomId 仍 NULL{" "}
{inventoryPreview.nullStockUomId} 列。
</Typography> </Typography>
)} )}
<Box> <Box>
@@ -742,11 +743,13 @@ const StockLedgerFixPageClient: React.FC = () => {


<Paper sx={{ p: 2 }}> <Paper sx={{ p: 2 }}>
<Stack spacing={1.5}> <Stack spacing={1.5}>
<Typography variant="h6">2.7 ADJ:line in/out 對齊 ledger</Typography>
<Typography variant="h6">2.7 ADJ:超發 remain→0,再對齊 ledger</Typography>
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
請先選 ADJ 日期再預覽。預設昨天;freeze dump 夜可選今天(例如 31/8 23:17 寫 ADJ 到 請先選 ADJ 日期再預覽。預設昨天;freeze dump 夜可選今天(例如 31/8 23:17 寫 ADJ 到
31/8)。比對每 lot 的 line in/out 與 ledger 加總;有差就 ADJ。目標:最後一筆
lotQtyAfter = line remain。明細最多 20 列。
31/8)。先處理超發(line 入少於出):把該 lot 的 inQty 補到等於 outQty(remain=0,trigger
重算 inventory)。再處理 miss:line 與 ledger 有差就 ADJ,掛既有 lot 的入/出倉行(不改 lot
數量)。一次 Apply 共用一張入倉單、一張出倉單,下面多行;同一 ADJ 日期再跑會沿用這兩張單。
明細最多 20 列。
</Typography> </Typography>
<Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap> <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap>
<TextField <TextField
@@ -789,15 +792,16 @@ const StockLedgerFixPageClient: React.FC = () => {
{adjLoading && !adjPreview && <CircularProgress size={24} />} {adjLoading && !adjPreview && <CircularProgress size={24} />}
{adjPreview && ( {adjPreview && (
<Typography variant="body2"> <Typography variant="body2">
ADJ 日期 {adjPreview.adjDate};可補 {adjPreview.lotCount} 條
lot(入 {adjPreview.adjInCount}、出 {adjPreview.adjOutCount});缺入合計{" "}
ADJ 日期 {adjPreview.adjDate};可處理 {adjPreview.lotCount} 條
lot(超發 remain→0 {adjPreview.overIssueCount}、合計 {adjPreview.sumOverIssue};miss 入{" "}
{adjPreview.adjInCount}、出 {adjPreview.adjOutCount});缺入合計{" "}
{adjPreview.sumMissIn}、缺出合計 {adjPreview.sumMissOut};SKU 淨額{" "} {adjPreview.sumMissIn}、缺出合計 {adjPreview.sumMissOut};SKU 淨額{" "}
{adjPreview.skuNet} {adjPreview.skuNet}
{adjPreview.skuNet !== "0" ? "(非 0 表示有缺入沒有對應缺出)" : ""} {adjPreview.skuNet !== "0" ? "(非 0 表示有缺入沒有對應缺出)" : ""}
{adjPreview.skippedNegCount > 0 {adjPreview.skippedNegCount > 0
? `;含反向 ADJ ${adjPreview.skippedNegCount} 條(ledger 多記)` ? `;含反向 ADJ ${adjPreview.skippedNegCount} 條(ledger 多記)`
: ""} : ""}
。下列最多 20 列。
。下列最多 20 列(超發優先)
</Typography> </Typography>
)} )}
{adjPreview && adjPreview.rows.length > 0 && ( {adjPreview && adjPreview.rows.length > 0 && (
@@ -810,6 +814,7 @@ const StockLedgerFixPageClient: React.FC = () => {
<TableCell align="right">ledger in/out</TableCell> <TableCell align="right">ledger in/out</TableCell>
<TableCell align="right">缺入</TableCell> <TableCell align="right">缺入</TableCell>
<TableCell align="right">缺出</TableCell> <TableCell align="right">缺出</TableCell>
<TableCell align="right">超發</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
@@ -825,6 +830,7 @@ const StockLedgerFixPageClient: React.FC = () => {
</TableCell> </TableCell>
<TableCell align="right">{r.missIn}</TableCell> <TableCell align="right">{r.missIn}</TableCell>
<TableCell align="right">{r.missOut}</TableCell> <TableCell align="right">{r.missOut}</TableCell>
<TableCell align="right">{r.overIssue}</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </TableBody>
@@ -949,7 +955,7 @@ const StockLedgerFixPageClient: React.FC = () => {
區間修(2.1–2.6 一次) 區間修(2.1–2.6 一次)
</Typography> </Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}> <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
使用上面勾的步驟,整段日期一次 API。2.1–2.5 用同一視窗算完;2.6 日結仍依日寫入。可含今天(freeze dump 夜)。最長約 20 分鐘
使用上面勾的步驟,一次 API 但改為逐日(與單日修相同 SQL,每天 commit)。可含今天(freeze dump 夜)。前端最長約 20 分鐘;大區間仍建議匯出 SQL
</Typography> </Typography>
<Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap> <Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap>
<TextField <TextField
@@ -984,7 +990,7 @@ const StockLedgerFixPageClient: React.FC = () => {
匯出已修 SQL(新庫用) 匯出已修 SQL(新庫用)
</Typography> </Typography>
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}> <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
勾選要匯出的段落(同日修步驟)。套檔順序:1.0 → ledger(±2.3)→ 2.7 INSERT →
勾選要匯出的段落(同日修步驟)。套檔順序:1.0 → ledger(±2.3)→ 2.7 入出倉單+ADJ INSERT →
2.6。完整檔(含 1.0+2.3)且正式庫 freeze/id 同源時,可跳過正式庫 1.0/2.3。預設只匯 2.6。完整檔(含 1.0+2.3)且正式庫 freeze/id 同源時,可跳過正式庫 1.0/2.3。預設只匯
ledger+日結(舊行為)。 ledger+日結(舊行為)。
</Typography> </Typography>


Carregando…
Cancelar
Salvar