Преглед изворни кода

庫存調整 Remarks 寫入 DB 並回填

fix負數倉
CANCERYS\kw093 пре 1 недеља
родитељ
комит
d5701925f3
2 измењених фајлова са 75 додато и 14 уклоњено
  1. +14
    -0
      src/app/api/stockAdjustment/actions.ts
  2. +61
    -14
      src/components/InventorySearch/InventoryLotLineTable.tsx

+ 14
- 0
src/app/api/stockAdjustment/actions.ts Прегледај датотеку

@@ -16,6 +16,7 @@ export interface StockAdjustmentLineRequest {
expiryDate: string;
warehouseId: number;
uom?: string | null;
remarks?: string | null;
}

export interface StockAdjustmentRequest {
@@ -33,6 +34,19 @@ export interface MessageResponse {
errorPosition: string | null;
}

export interface StockAdjustmentRemarksResponse {
lotNo: string | null;
remarks: string;
}

/** FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.7 | 2026-09-08 */
export const fetchLatestAdjustmentRemarks = async (itemId: number) => {
return serverFetchJson<StockAdjustmentRemarksResponse[]>(
`${BASE_API_URL}/stockAdjustment/latestRemarks?itemId=${itemId}`,
{ method: "GET" },
);
};

export const submitStockAdjustment = async (data: StockAdjustmentRequest) => {
const result = await serverFetchJson<MessageResponse>(
`${BASE_API_URL}/stockAdjustment/submit`,


+ 61
- 14
src/components/InventorySearch/InventoryLotLineTable.tsx Прегледај датотеку

@@ -32,7 +32,7 @@ import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs";
import CheckIcon from "@mui/icons-material/Check";
import { submitStockAdjustment, StockAdjustmentLineRequest } from "@/app/api/stockAdjustment/actions";
import { submitStockAdjustment, StockAdjustmentLineRequest, fetchLatestAdjustmentRemarks } from "@/app/api/stockAdjustment/actions";
import { useSession } from "next-auth/react";
import { AUTH, hasAbility } from "@/authorities";

@@ -59,7 +59,7 @@ interface Props {
onStockAdjustmentSuccess?: () => void | Promise<void>;
}

/** FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.6 | 2026-09-07 */
/** FP-MTMS Version Checklist | Functions Ref. No. 3 | v1.0.7 | 2026-09-08 */
const InventoryLotLineTable: React.FC<Props> = ({
inventoryLotLines, pagingController, setPagingController, totalCount, inventory,
filterLotNo,
@@ -99,7 +99,11 @@ const InventoryLotLineTable: React.FC<Props> = ({
remarks: '',
});
const originalAdjustmentLinesRef = useRef<AdjustmentEntry[]>([]);
const adjustSaveInFlightRef = useRef(false);
const loadedRemarksByLotRef = useRef<Map<string, string>>(new Map());
const remarksFetchGenRef = useRef(0);
const [adjustmentEntries, setAdjustmentEntries] = useState<AdjustmentEntry[]>([]);
const [isAdjustSaving, setIsAdjustSaving] = useState(false);
useEffect(() => {
if (stockTransferModalOpen) {
fetchWarehouseListClient()
@@ -153,9 +157,34 @@ const prevAdjustmentModalOpenRef = useRef(false);
}));
setAdjustmentEntries(initial);
originalAdjustmentLinesRef.current = initial;
loadedRemarksByLotRef.current = new Map();
const fetchGen = ++remarksFetchGenRef.current;
const itemId = inventory.itemId;
fetchLatestAdjustmentRemarks(itemId)
.then((rows) => {
if (fetchGen !== remarksFetchGenRef.current) return;
const byLot = new Map<string, string>();
for (const row of rows ?? []) {
const lot = row.lotNo?.trim();
const remarks = row.remarks?.trim();
if (!lot || !remarks || byLot.has(lot)) continue;
byLot.set(lot, remarks);
}
loadedRemarksByLotRef.current = byLot;
const apply = (line: AdjustmentEntry): AdjustmentEntry => {
const lot = line.lotNo?.trim();
const remarks = (lot && byLot.get(lot)) || line.remarks || '';
return { ...line, remarks };
};
setAdjustmentEntries((prev) => prev.map(apply));
originalAdjustmentLinesRef.current = originalAdjustmentLinesRef.current.map(apply);
})
.catch(console.error);
}
setPendingRemovalLineId(null);
setRemovalReasons({});
} else if (!stockAdjustmentModalOpen) {
remarksFetchGenRef.current += 1;
}
}, [stockAdjustmentModalOpen, inventory, availableLotLines]);

@@ -164,12 +193,15 @@ const prevAdjustmentModalOpenRef = useRef(false);
setPendingRemovalLineId(null);
setRemovalReasons({});
setAdjustmentEntries(
(availableLotLines ?? []).map((line) => ({
...line,
adjustedQty: line.availableQty ?? 0,
originalQty: line.availableQty ?? 0,
remarks: '',
}))
(availableLotLines ?? []).map((line) => {
const lot = line.lotNo?.trim();
return {
...line,
adjustedQty: line.availableQty ?? 0,
originalQty: line.availableQty ?? 0,
remarks: (lot && loadedRemarksByLotRef.current.get(lot)) || '',
};
})
);
}, [availableLotLines]);

@@ -241,15 +273,26 @@ const prevAdjustmentModalOpenRef = useRef(false);
expiryDate,
warehouseId: line.warehouse?.id ?? 0,
uom: line.uom ?? null,
remarks: line.remarks?.trim() || null,
};
}, []);

const handleAdjustmentSave = useCallback(async () => {
if (!inventory) return;
const itemCode = inventory.itemCode;
const originalLines = originalAdjustmentLinesRef.current.map((line) => toApiLine(line, itemCode));
const currentLines = adjustmentEntries.map((line) => toApiLine(line, itemCode));
if (adjustSaveInFlightRef.current) return;
adjustSaveInFlightRef.current = true;
setIsAdjustSaving(true);
try {
const itemCode = inventory.itemCode;
const currentIds = new Set(adjustmentEntries.map((line) => line.id));
const originalLines = originalAdjustmentLinesRef.current.map((line) => {
const api = toApiLine(line, itemCode);
if (!currentIds.has(line.id)) {
api.remarks = removalReasons[line.id]?.trim() || null;
}
return api;
});
const currentLines = adjustmentEntries.map((line) => toApiLine(line, itemCode));
setIsUploading(true);
await submitStockAdjustment({
itemId: inventory.itemId,
@@ -264,8 +307,10 @@ const prevAdjustmentModalOpenRef = useRef(false);
msgError(message || t("Save failed"));
} finally {
setIsUploading(false);
setIsAdjustSaving(false);
adjustSaveInFlightRef.current = false;
}
}, [adjustmentEntries, inventory, t, toApiLine, onStockAdjustmentSuccess]);
}, [adjustmentEntries, inventory, t, toApiLine, onStockAdjustmentSuccess, removalReasons]);

const handleOpenAddEntry = useCallback(() => {
setAddEntryForm({
@@ -857,7 +902,7 @@ const prevAdjustmentModalOpenRef = useRef(false);
color="primary"
startIcon={<SaveIcon />}
onClick={handleAdjustmentSave}
disabled={!hasAdjustmentChange}
disabled={!hasAdjustmentChange || isAdjustSaving}
>
{t("Save")}
</Button>
@@ -1004,7 +1049,9 @@ const prevAdjustmentModalOpenRef = useRef(false);
},
}}
/>
) : null}
) : (
line.remarks || null
)}
</TableCell>
<TableCell align="center">
{pendingRemovalLineId === line.id ? (


Loading…
Откажи
Сачувај