B.E.N.S.O.N 2 settimane fa
parent
commit
673b6818bf
1 ha cambiato i file con 54 aggiunte e 76 eliminazioni
  1. +54
    -76
      src/components/PoDetail/PoDetail.tsx

+ 54
- 76
src/components/PoDetail/PoDetail.tsx Vedi File

@@ -76,7 +76,6 @@ import { Controller, FormProvider, useForm } from "react-hook-form";
import dayjs, { Dayjs } from "dayjs";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { DatePicker, LocalizationProvider, zhHK } from "@mui/x-date-pickers";
import { debounce } from "lodash";
import LoadingComponent from "../General/LoadingComponent";
import { getMailTemplatePdfForStockInLine } from "@/app/api/mailTemplate/actions";
import { PrinterCombo } from "@/app/api/settings/printer";
@@ -233,7 +232,7 @@ const PoSearchList: React.FC<{

interface PolInputResult {
lotNo: string,
dnQty: number,
dnQty: string,
}

const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
@@ -250,15 +249,19 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
const [rows, setRows] = useState<PurchaseOrderLine[]>(
purchaseOrder.pol || [],
);
const [polInputList, setPolInputList] = useState<PolInputResult[]>([])
const [polInputList, setPolInputList] = useState<Record<number, PolInputResult>>({})
const PO_DETAIL_SELECTION_KEY = "po-detail-selection";
useEffect(() => {
setPolInputList(
(purchaseOrder.pol ?? []).map(() => ({
lotNo: "",
dnQty: 0,
} as PolInputResult))
);
setPolInputList((prev) => {
const next: Record<number, PolInputResult> = {};
(purchaseOrder.pol ?? []).forEach((pol) => {
next[pol.id] = prev[pol.id] ?? {
lotNo: "",
dnQty: "",
};
});
return next;
});
}, [purchaseOrder.pol]);
useEffect(() => {
try {
@@ -335,7 +338,7 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
}, [selectedIdsParam]);


const fetchPoDetail = useCallback(async (poId: string, preserveDnNo: boolean = false) => {
const fetchPoDetail = useCallback(async (poId: string, preserveDnNo: boolean = false, preferredPolId?: number) => {
try {
const result = await fetchPoInClient(parseInt(poId));
if (result) {
@@ -348,16 +351,12 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
});
setRows(result.pol || []);
if (result.pol && result.pol.length > 0) {
if (result.id === selectedPoId && selectedRow?.id) {
const polIndex = result.pol.findIndex((p) => p.id === selectedRow?.id)
// setSelectedRow(result.pol[polIndex]);
setStockInLine(result.pol[polIndex].stockInLine);
setProcessedQty(result.pol[polIndex].processed);
} else {
// setSelectedRow(result.pol[0]);
setStockInLine(result.pol[0].stockInLine);
setProcessedQty(result.pol[0].processed);
}
const targetPolId = preferredPolId ?? selectedRow?.id;
const targetPol =
result.pol.find((p) => p.id === targetPolId) ?? result.pol[0];
setSelectedRow(targetPol);
setStockInLine(targetPol.stockInLine);
setProcessedQty(targetPol.processed);
}
// if (focusField) {console.log(focusField);focusField.focus();}
}
@@ -446,6 +445,8 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
// const [open, setOpen] = useState(false);
const [processedQty, setProcessedQty] = useState(row.processed);
const [currStatus, setCurrStatus] = useState(row.status);
const [lotNoInput, setLotNoInput] = useState(polInputList[row.id]?.lotNo ?? "");
const [dnQtyInput, setDnQtyInput] = useState(polInputList[row.id]?.dnQty ?? "");
// const [stockInLine, setStockInLine] = useState(row.stockInLine);
const totalWeight = useMemo(
() => calculateWeight(row.qty, row.uom),
@@ -455,10 +456,6 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
() => returnWeightUnit(row.uom),
[row.uom],
);
const rowIndex = useMemo(() => {
return rows.findIndex((r) => r.id === row.id)
}, [])

useEffect(() => {
const polId = searchParams.get("polId") != null ? parseInt(searchParams.get("polId")!) : null
if (polId) {
@@ -479,6 +476,11 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
}
}, [processedQty, row.qty, row.stockUom?.stockQty]);

useEffect(() => {
setLotNoInput(polInputList[row.id]?.lotNo ?? "");
setDnQtyInput(polInputList[row.id]?.dnQty ?? "");
}, [polInputList, row.id]);

const handleRowSelect = () => {
// setSelectedRowId(row.id);
setSelectedRow(row);
@@ -501,7 +503,7 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
const handleStart = useCallback(
() => {
const orderQty = Number(row?.qty) ?? 0;
const acceptedQty = Number(polInputList[rowIndex].dnQty);
const acceptedQty = Number(dnQtyInput.trim());
if (isNaN(acceptedQty) || acceptedQty <= 0) {
alert("來貨數量必須大於0!");
@@ -518,11 +520,18 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
itemName: row.itemName,
purchaseOrderLineId: row.id,
acceptedQty: acceptedQty,
productLotNo: polInputList[rowIndex].lotNo || '',
productLotNo: lotNoInput || "",
};
const res = await createStockInLine(postData);
if (res) {
fetchPoDetail(selectedPoId.toString(), true);
setLotNoInput("");
setDnQtyInput("");
setPolInputList((prev) => ({
...prev,
[row.id]: { lotNo: "", dnQty: "" },
}));
setSelectedRow(row);
fetchPoDetail(selectedPoId.toString(), true, row.id);
}
console.log(res);
}, 200);
@@ -539,57 +548,24 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
doSubmit();
}
},
[polInputList, row, rowIndex, dnFormProps, selectedPoId, fetchPoDetail, t],
[dnQtyInput, row, dnFormProps, selectedPoId, fetchPoDetail, t, lotNoInput],
);

const handleChange = useCallback(debounce((e: React.ChangeEvent<HTMLInputElement>) => {
const raw = e.target.value;
const id = e.target.id
// const temp = polInputList
switch (id) {
case "lotNo":
if (raw.trim() === '') {
polInputList[rowIndex].lotNo = '';
break;
}
polInputList[rowIndex].lotNo = raw.trim();
break;
case "dnQty":
// Allow empty input
const trimmed = raw.trim();

if (trimmed === "") {
polInputList[rowIndex].dnQty = 0;
break;
}
/*
// Keep digits only
const cleaned = raw.replace(/[^\d]/g, '');
if (cleaned === '') {
// If the user typed only non-digits, keep previous value
break;
}
*/
const parsed = Number(trimmed);

// 不是合法數字(例如 "abc")
if (!Number.isFinite(parsed)) {
polInputList[rowIndex].dnQty = 0; // 或保留舊值,看你需求
break;
}

polInputList[rowIndex].dnQty = parsed; // 這裡允許小數,先存起來
break;
default:
break;
}
// setPolInputList(() => temp)
}, 300), [rowIndex]);
const syncRowInputToParent = useCallback((lotNo: string, dnQty: string) => {
setPolInputList((prev) => {
const current = prev[row.id] ?? { lotNo: "", dnQty: "" };
if (current.lotNo === lotNo && current.dnQty === dnQty) return prev;
return {
...prev,
[row.id]: { lotNo, dnQty },
};
});
}, [row.id]);

// const [focusField, setFocusField] = useState<HTMLInputElement>();

// 本批收貨數量(訂單單位): 使用者在該行輸入的 dnQty
const batchPurchaseQty = polInputList[rowIndex]?.dnQty ?? 0;
const batchPurchaseQty = Number(dnQtyInput.trim()) || 0;

// 已來貨總數(庫存單位): 同一 POL 底下所有 stock_in_line.acceptedQty 的合計
const totalStockReceived = row.stockInLine
@@ -686,8 +662,9 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
label="輸入貨品批號"
type="text" // Use type="text" to allow validation in the change handler
variant="outlined"
defaultValue={polInputList[rowIndex]?.lotNo ?? ''}
onChange={handleChange}
value={lotNoInput}
onChange={(e) => setLotNoInput(e.target.value)}
onBlur={() => syncRowInputToParent(lotNoInput, dnQtyInput)}
// onFocus={(e) => {setFocusField(e.target as HTMLInputElement);}}
/>
</TableCell>
@@ -697,8 +674,9 @@ const PoDetail: React.FC<Props> = ({ po, warehouse, printerCombo }) => {
label="此批送貨數量"
type="text" // Use type="text" to allow validation in the change handler
variant="outlined"
defaultValue={polInputList[rowIndex]?.dnQty ?? ''}
onChange={handleChange}
value={dnQtyInput}
onChange={(e) => setDnQtyInput(e.target.value)}
onBlur={() => syncRowInputToParent(lotNoInput, dnQtyInput)}
InputProps={{
inputProps: {
min: 0, // Optional: set a minimum value


Caricamento…
Annulla
Salva