Browse Source

update

master
CANCERYS\kw093 2 days ago
parent
commit
59c1a4c727
1 changed files with 130 additions and 106 deletions
  1. +130
    -106
      src/components/PickOrderSearch/PickQcStockInModalVer3.tsx

+ 130
- 106
src/components/PickOrderSearch/PickQcStockInModalVer3.tsx View File

@@ -34,6 +34,23 @@ import { fetchPickOrderQcResult, savePickOrderQcResult } from "@/app/api/qc/acti
import {
updateInventoryLotLineStatus
} from "@/app/api/inventory/actions"; // ✅ 导入新的 API
import { dayjsToInputDateString } from "@/app/utils/formatUtil";
import dayjs from "dayjs";
interface Props extends CommonProps {
itemDetail: GetPickOrderLineInfo & {
pickOrderCode: string;
qcResult?: PurchaseQcResult[]
};
qcItems: ExtendedQcItem[];
setQcItems: Dispatch<SetStateAction<ExtendedQcItem[]>>;
selectedLotId?: number;
onStockOutLineUpdate?: () => void;
lotData: LotPickData[];
pickQtyData?: PickQtyData;
selectedRowId?: number;
// ✅ Add callback to update pickQtyData in parent
onPickQtyUpdate?: (updatedPickQtyData: PickQtyData) => void;
}

// Define QcData interface locally
interface ExtendedQcItem extends QcItemWithChecks {
@@ -44,6 +61,7 @@ interface ExtendedQcItem extends QcItemWithChecks {
stableId?: string; // ✅ Also add stableId for better row identification
}


const style = {
position: "absolute",
top: "50%",
@@ -58,7 +76,11 @@ const style = {
maxHeight: "90vh",
overflowY: "auto",
};

interface PickQtyData {
[lineId: number]: {
[lotId: number]: number;
};
}
interface CommonProps extends Omit<ModalProps, "children"> {
itemDetail: GetPickOrderLineInfo & {
pickOrderCode: string;
@@ -117,6 +139,8 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
selectedLotId,
onStockOutLineUpdate,
lotData,
pickQtyData,
selectedRowId,
}) => {
const {
t,
@@ -222,10 +246,16 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
try {
const qcAccept = qcDecision === "1";
const acceptQty = Number(accQty) || null;
const validationErrors : string[] = [];
const selectedLot = lotData.find(lot => lot.stockOutLineId === selectedLotId);
// ✅ Add safety check for selectedLot
if (!selectedLot) {
console.error("Selected lot not found");
return;
}
const itemsWithoutResult = qcItems.filter(item => item.qcPassed === undefined);
if (itemsWithoutResult.length > 0) {
validationErrors.push(`${t("QC items without result")}: ${itemsWithoutResult.map(item => item.code).join(", ")}`);
@@ -244,7 +274,7 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
qcItem: item.code,
qcDescription: item.description || "",
isPassed: item.qcPassed,
failQty: item.qcPassed ? 0 : (selectedLot?.requiredQty || 0),
failQty: item.qcPassed ? 0 : (selectedLot.requiredQty || 0),
remarks: item.remarks || "",
})),
};
@@ -257,35 +287,64 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
return;
}
// ✅ Fix: Update stock out line status based on QC decision
if (selectedLotId) { // ✅ Remove qcData.qcAccept condition
// ✅ Handle different QC decisions
if (selectedLotId) {
try {
const allPassed = qcData.qcItems.every(item => item.isPassed);
// ✅ Fix: Use correct backend enum values
const newStockOutLineStatus = allPassed ? 'completed' : 'rejected';
console.log("Updating stock out line status after QC:", {
stockOutLineId: selectedLotId,
newStatus: newStockOutLineStatus
});
// ✅ Fix: 1. Update stock out line status with required qty field
if (selectedLot) {
if (qcDecision === "1") {
// ✅ QC Decision 1: Accept - Update lot's required pick qty to actual pick qty
// ✅ Use selectedLotId to get the actual pick qty from pickQtyData
const actualPickQty = pickQtyData?.[selectedRowId || 0]?.[selectedLot?.lotId || 0] || 0;
console.log("QC Decision 1 - Accept: Updating lot required pick qty to actual pick qty:", {
lotId: selectedLot.lotId,
currentRequiredQty: selectedLot.requiredQty,
newRequiredQty: actualPickQty
});
// Update stock out line status to completed
const newStockOutLineStatus = 'completed';
await updateStockOutLineStatus({
id: selectedLotId,
status: newStockOutLineStatus,
qty: actualPickQty // Use actual pick qty
});
} else if (qcDecision === "2") {
// ✅ QC Decision 2: Report and Re-pick - Return to no accept and pick another lot
console.log("QC Decision 2 - Report and Re-pick: Returning to no accept and allowing another lot pick");
// Update stock out line status to rejected (for re-pick)
const newStockOutLineStatus = 'rejected';
await updateStockOutLineStatus({
id: selectedLotId,
status: newStockOutLineStatus,
qty: selectedLot.requiredQty || 0
});
} else {
console.warn("Selected lot not found for stock out line status update");
}
// ✅ Fix: 2. If QC failed, also update inventory lot line status
if (!allPassed) {
// ✅ Update inventory lot line status to unavailable so user can pick another lot
try {
if (selectedLot) {
console.log("Updating inventory lot line status for failed QC:", {
console.log("Updating inventory lot line status to unavailable for re-pick:", {
inventoryLotLineId: selectedLot.lotId,
status: 'unavailable'
});
await updateInventoryLotLineStatus({
inventoryLotLineId: selectedLot.lotId,
status: 'unavailable'
});
console.log("Inventory lot line status updated to unavailable - user can now pick another lot");
} catch (error) {
console.error("Failed to update inventory lot line status:", error);
}
// ✅ Also update inventory lot line status for failed QC items
if (!allPassed) {
try {
console.log("Updating inventory lot line status for failed QC items:", {
inventoryLotLineId: selectedLot.lotId,
status: 'unavailable'
});
@@ -294,18 +353,16 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
inventoryLotLineId: selectedLot.lotId,
status: 'unavailable'
});
console.log("Inventory lot line status updated to unavailable");
} else {
console.warn("Selected lot not found for inventory lot line status update");
console.log("Failed QC items inventory lot line status updated to unavailable");
} catch (error) {
console.error("Failed to update failed QC items inventory lot line status:", error);
}
}
} catch (error) {
console.error("Failed to update inventory lot line status:", error);
}
}
console.log("Stock out line status updated successfully after QC");
// Call callback to refresh data
// Call callback to refresh data
if (onStockOutLineUpdate) {
onStockOutLineUpdate();
}
@@ -316,8 +373,8 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
console.log("QC results saved successfully!");
// ✅ Show warning dialog for failed QC items
if (!qcData.qcItems.every((q) => q.isPassed) && qcData.qcAccept) {
// ✅ Show warning dialog for failed QC items when accepting
if (qcDecision === "1" && !qcData.qcItems.every((q) => q.isPassed)) {
submitDialogWithWarning(() => {
closeHandler?.({}, 'escapeKeyDown');
}, t, {title:"有不合格檢查項目,確認接受出庫?", confirmButtonText: "Confirm", html: ""});
@@ -327,7 +384,6 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
closeHandler?.({}, 'escapeKeyDown');
} catch (error) {
console.error("Error in QC submission:", error);
// ✅ Enhanced error logging
if (error instanceof Error) {
console.error("Error details:", error.message);
console.error("Error stack:", error.stack);
@@ -336,9 +392,8 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
setIsSubmitting(false);
}
},
[qcItems, closeHandler, t, itemDetail, qcDecision, accQty, selectedLotId, onStockOutLineUpdate, lotData],
[qcItems, closeHandler, t, itemDetail, qcDecision, accQty, selectedLotId, onStockOutLineUpdate, lotData, pickQtyData, selectedRowId],
);

// DataGrid columns (QcComponent style)
const qcColumns: GridColDef[] = useMemo(
() => [
@@ -529,77 +584,46 @@ const PickQcStockInModalVer3: React.FC<Props> = ({
)}
<Grid item xs={12}>
<FormControl>
<Controller
name="qcDecision"
control={control}
defaultValue="1"
render={({ field }) => (
<RadioGroup
row
aria-labelledby="demo-radio-buttons-group-label"
{...field}
value={field.value}
onChange={(e) => {
const value = e.target.value.toString();
if (value != "1" && Boolean(errors.acceptQty)) {
setValue("acceptQty", itemDetail.requiredQty ?? 0);
}
field.onChange(value);
}}
>
<FormControlLabel
value="1"
control={<Radio />}
label="接受出庫"
/>
<Box sx={{mr:2}}>
<TextField
type="number"
label={t("acceptQty")}
sx={{ width: '150px' }}
value={(qcDecision == 1)? accQty : 0 }
disabled={qcDecision != 1}
{...register("acceptQty", {
//required: "acceptQty required!",
})}
error={Boolean(errors.acceptQty)}
helperText={errors.acceptQty?.message?.toString() || ""}
/>
</Box>
<FormControl>
<Controller
name="qcDecision"
control={control}
defaultValue="1"
render={({ field }) => (
<RadioGroup
row
aria-labelledby="demo-radio-buttons-group-label"
{...field}
value={field.value}
onChange={(e) => {
const value = e.target.value.toString();
if (value != "1" && Boolean(errors.acceptQty)) {
setValue("acceptQty", itemDetail.requiredQty ?? 0);
}
field.onChange(value);
}}
>
<FormControlLabel
value="1"
control={<Radio />}
label="接受出庫"
/>

<FormControlLabel
value="2"
control={<Radio />}
sx={{"& .Mui-checked": {color: "red"}}}
label="不接受並重新揀貨"
/>
<FormControlLabel
value="3"
control={<Radio />}
sx={{"& .Mui-checked": {color: "blue"}}}
label="上報品檢結果"
/>
</RadioGroup>
)}
/>
</FormControl>
</Grid>
{qcDecision == 3 && (
<Grid item xs={12}>
<EscalationComponent
forSupervisor={false}
isCollapsed={isCollapsed}
setIsCollapsed={setIsCollapsed}
//escalationCombo={[]} // ✅ Add missing prop
/>
</Grid>
)}
<Grid item xs={12} sx={{ mt: 2 }}>
{/* ✅ Combine options 2 & 3 into one */}
<FormControlLabel
value="2"
control={<Radio />}
sx={{"& .Mui-checked": {color: "blue"}}}
label="上報品檢結果並重新揀貨"
/>
</RadioGroup>
)}
/>
</FormControl>
</Grid>
/*
<Grid item xs={12} sx={{ mt: 2 }}>
<Stack direction="row" justifyContent="flex-start" gap={1}>
<Button
variant="contained"


Loading…
Cancel
Save