Ver código fonte

remove the preview of 2.7 and correct 2,7 to fix data correct

fix負數倉
CANCERYS\kw093 3 horas atrás
pai
commit
4db3c4a684
2 arquivos alterados com 19 adições e 146 exclusões
  1. +0
    -43
      src/app/api/stockLedgerFix/client.ts
  2. +19
    -103
      src/components/StockLedgerFix/StockLedgerFixPageClient.tsx

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

@@ -95,33 +95,6 @@ export type StockLedgerFixScopeDetail = {
parts: StockLedgerFixCheckPart[];
};

export type StockLedgerFixAdjRow = {
lotLineId: number;
inventoryId: number | null;
itemCode: string | null;
lineIn: string;
lineOut: string;
ledgerIn: string;
ledgerOut: string;
missIn: string;
missOut: string;
overIssue: string;
};

export type StockLedgerFixAdjPreview = {
adjDate: string;
lotCount: number;
adjInCount: number;
adjOutCount: number;
skippedNegCount: number;
sumMissIn: string;
sumMissOut: string;
skuNet: string;
overIssueCount: number;
sumOverIssue: string;
rows: StockLedgerFixAdjRow[];
};

export type StockLedgerFixAdjResponse = {
adjDate: string;
insertedIn: number;
@@ -132,22 +105,6 @@ export type StockLedgerFixAdjResponse = {
dayRowsWritten: number;
};

export async function fetchStockLedgerFixAdjPreview(
adjDate?: string,
): Promise<StockLedgerFixAdjPreview> {
const response = await axiosInstance.get<StockLedgerFixAdjPreview>(
`${NEXT_PUBLIC_API_URL}/stock-ledger-fix/adj`,
{
params: {
rowLimit: 20,
...(adjDate ? { adjDate } : {}),
},
timeout: 600000,
},
);
return response.data;
}

export async function runStockLedgerFixAdj(
adjDate?: string,
): Promise<StockLedgerFixAdjResponse> {


+ 19
- 103
src/components/StockLedgerFix/StockLedgerFixPageClient.tsx Ver arquivo

@@ -27,7 +27,6 @@ import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs, { type Dayjs } from "dayjs";
import "dayjs/locale/zh-hk";
import {
fetchStockLedgerFixAdjPreview,
runStockLedgerFixAdj,
fetchStockLedgerFixDay,
fetchStockLedgerFixInventory,
@@ -41,7 +40,6 @@ import {
searchStockLedgerFixInventory,
searchStockLedgerFixLot,
downloadStockLedgerFixSql,
type StockLedgerFixAdjPreview,
type StockLedgerFixCheckPart,
type StockLedgerFixDayDetail,
type StockLedgerFixInventoryPreview,
@@ -291,10 +289,7 @@ const StockLedgerFixPageClient: React.FC = () => {
const [inventoryRunning, setInventoryRunning] = useState(false);
const [inventoryError, setInventoryError] = useState<string | null>(null);
const [inventoryMessage, setInventoryMessage] = useState<string | null>(null);
const adjLoadInFlight = useRef(false);
const adjRunInFlight = useRef(false);
const [adjPreview, setAdjPreview] = useState<StockLedgerFixAdjPreview | null>(null);
const [adjLoading, setAdjLoading] = useState(false);
const [adjRunning, setAdjRunning] = useState(false);
const [adjError, setAdjError] = useState<string | null>(null);
const [adjMessage, setAdjMessage] = useState<string | null>(null);
@@ -348,32 +343,6 @@ const StockLedgerFixPageClient: React.FC = () => {
}
}, []);

const loadAdjPreview = useCallback(async () => {
if (adjLoadInFlight.current) return;
const d = adjDate.trim();
if (!d) {
setAdjError("請填 ADJ 日期");
return;
}
if (d > dayjs().format("YYYY-MM-DD")) {
setAdjError("ADJ 日期不能是未來");
return;
}
adjLoadInFlight.current = true;
setAdjLoading(true);
setAdjError(null);
try {
setAdjPreview(await fetchStockLedgerFixAdjPreview(d));
} catch (e) {
console.error(e);
setAdjError(apiErrorMessage(e, "無法載入 ADJ 預覽(需要 ADMIN / TESTING)"));
setAdjPreview(null);
} finally {
setAdjLoading(false);
adjLoadInFlight.current = false;
}
}, [adjDate]);

useEffect(() => {
void loadInventory();
}, [loadInventory]);
@@ -450,13 +419,17 @@ const StockLedgerFixPageClient: React.FC = () => {

const onAdjApply = async () => {
if (adjRunInFlight.current) return;
const d = adjDate.trim() || adjPreview?.adjDate;
const d = adjDate.trim();
if (!d) {
setAdjError("請填 ADJ 日期並先預覽");
setAdjError("請填 ADJ 日期");
return;
}
if (d > dayjs().format("YYYY-MM-DD")) {
setAdjError("ADJ 日期不能是未來");
return;
}
const ok = window.confirm(
`會在 ${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 ?? "?"}。確定?`,
`會在 ${d} 對最後餘額不等於 onHandQty 的庫存桶各寫一筆 ADJ(入或出),掛在既有 lot 上。不改 lot line、不改 inventory。已對齊的桶不會再寫。確定?`,
);
if (!ok) return;
adjRunInFlight.current = true;
@@ -466,9 +439,8 @@ const StockLedgerFixPageClient: React.FC = () => {
try {
const res = await runStockLedgerFixAdj(d);
setAdjMessage(
`ADJ ${res.adjDate}:超發 remain→0 ${res.overIssuePatched}、入 ${res.insertedIn}、出 ${res.insertedOut}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`,
`ADJ ${res.adjDate}:入 ${res.insertedIn}、出 ${res.insertedOut}、lotQty ${res.filledLotQty}、balance ${res.filledBalance}、日結 ${res.dayRowsWritten}`,
);
await loadAdjPreview();
} catch (e) {
console.error(e);
setAdjError(apiErrorMessage(e, "ADJ 對齊失敗"));
@@ -744,13 +716,11 @@ const StockLedgerFixPageClient: React.FC = () => {

<Paper sx={{ p: 2 }}>
<Stack spacing={1.5}>
<Typography variant="h6">2.7 ADJ:超發 remain→0,再對齊 ledger</Typography>
<Typography variant="h6">2.7 ADJ:對齊最後餘額與 onHandQty</Typography>
<Typography variant="body2" color="text.secondary">
請先選 ADJ 日期再預覽。預設昨天;freeze dump 夜可選今天(例如 31/8 23:17 寫 ADJ 到
31/8)。先處理超發(line 入少於出):把該 lot 的 inQty 補到等於 outQty(remain=0,trigger
重算 inventory)。再處理 miss:line 與 ledger 有差就 ADJ,掛既有 lot 的入/出倉行(不改 lot
數量)。一次 Apply 共用一張入倉單、一張出倉單,下面多行;同一 ADJ 日期再跑會沿用這兩張單。
明細最多 20 列。
選 ADJ 日期後直接執行。預設昨天;freeze dump 夜可選今天。最後餘額低於 onHandQty
的桶補一筆入倉 ADJ,高於 onHandQty 的桶補一筆出倉 ADJ,掛在既有 lot 上。不改 lot
line、不改 inventory。一次執行共用一張入倉單、一張出倉單;已對齊的桶再跑不會再寫。
</Typography>
<Stack direction="row" spacing={1} alignItems="center" flexWrap="wrap" useFlexGap>
<TextField
@@ -760,29 +730,28 @@ const StockLedgerFixPageClient: React.FC = () => {
value={adjDate}
onChange={(e) => {
setAdjDate(e.target.value);
setAdjPreview(null);
setAdjMessage(null);
}}
disabled={adjLoading || adjRunning}
disabled={adjRunning}
InputLabelProps={{ shrink: true }}
inputProps={{ max: dayjs().format("YYYY-MM-DD") }}
/>
<Button
size="small"
disabled={adjLoading || adjRunning}
disabled={adjRunning}
onClick={() => {
setAdjDate(dayjs().format("YYYY-MM-DD"));
setAdjPreview(null);
setAdjMessage(null);
}}
>
今天(freeze 夜)
</Button>
<Button
size="small"
disabled={adjLoading || adjRunning}
disabled={adjRunning}
onClick={() => {
setAdjDate(dayjs().subtract(1, "day").format("YYYY-MM-DD"));
setAdjPreview(null);
setAdjMessage(null);
}}
>
昨天
@@ -790,65 +759,12 @@ const StockLedgerFixPageClient: React.FC = () => {
</Stack>
{adjError && <Alert severity="error">{adjError}</Alert>}
{adjMessage && <Alert severity="success">{adjMessage}</Alert>}
{adjLoading && !adjPreview && <CircularProgress size={24} />}
{adjPreview && (
<Typography variant="body2">
ADJ 日期 {adjPreview.adjDate};可處理 {adjPreview.lotCount} 條
lot(超發 remain→0 {adjPreview.overIssueCount}、合計 {adjPreview.sumOverIssue};miss 入{" "}
{adjPreview.adjInCount}、出 {adjPreview.adjOutCount});缺入合計{" "}
{adjPreview.sumMissIn}、缺出合計 {adjPreview.sumMissOut};SKU 淨額{" "}
{adjPreview.skuNet}
{adjPreview.skuNet !== "0" ? "(非 0 表示有缺入沒有對應缺出)" : ""}
{adjPreview.skippedNegCount > 0
? `;含反向 ADJ ${adjPreview.skippedNegCount} 條(ledger 多記)`
: ""}
。下列最多 20 列(超發優先)。
</Typography>
)}
{adjPreview && adjPreview.rows.length > 0 && (
<Table size="small">
<TableHead>
<TableRow>
<TableCell>lotLineId</TableCell>
<TableCell>itemCode</TableCell>
<TableCell align="right">line in/out</TableCell>
<TableCell align="right">ledger in/out</TableCell>
<TableCell align="right">缺入</TableCell>
<TableCell align="right">缺出</TableCell>
<TableCell align="right">超發</TableCell>
</TableRow>
</TableHead>
<TableBody>
{adjPreview.rows.map((r) => (
<TableRow key={r.lotLineId}>
<TableCell>{r.lotLineId}</TableCell>
<TableCell>{r.itemCode ?? "—"}</TableCell>
<TableCell align="right">
{r.lineIn} / {r.lineOut}
</TableCell>
<TableCell align="right">
{r.ledgerIn} / {r.ledgerOut}
</TableCell>
<TableCell align="right">{r.missIn}</TableCell>
<TableCell align="right">{r.missOut}</TableCell>
<TableCell align="right">{r.overIssue}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
{adjRunning && <CircularProgress size={24} />}
<Stack direction="row" spacing={1}>
<Button
variant="outlined"
disabled={adjLoading || adjRunning}
onClick={() => void loadAdjPreview()}
>
{adjLoading ? "預覽中…" : adjPreview ? "重新預覽" : "預覽"}
</Button>
<Button
variant="contained"
color="warning"
disabled={adjRunning || adjLoading || !adjPreview}
disabled={adjRunning}
onClick={() => void onAdjApply()}
>
{adjRunning ? "寫入 ADJ 中…" : "Apply ADJ"}


Carregando…
Cancelar
Salvar