소스 검색

庫存調整 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; expiryDate: string;
warehouseId: number; warehouseId: number;
uom?: string | null; uom?: string | null;
remarks?: string | null;
} }


export interface StockAdjustmentRequest { export interface StockAdjustmentRequest {
@@ -33,6 +34,19 @@ export interface MessageResponse {
errorPosition: string | null; 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) => { export const submitStockAdjustment = async (data: StockAdjustmentRequest) => {
const result = await serverFetchJson<MessageResponse>( const result = await serverFetchJson<MessageResponse>(
`${BASE_API_URL}/stockAdjustment/submit`, `${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 { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import dayjs from "dayjs"; import dayjs from "dayjs";
import CheckIcon from "@mui/icons-material/Check"; 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 { useSession } from "next-auth/react";
import { AUTH, hasAbility } from "@/authorities"; import { AUTH, hasAbility } from "@/authorities";


@@ -59,7 +59,7 @@ interface Props {
onStockAdjustmentSuccess?: () => void | Promise<void>; 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> = ({ const InventoryLotLineTable: React.FC<Props> = ({
inventoryLotLines, pagingController, setPagingController, totalCount, inventory, inventoryLotLines, pagingController, setPagingController, totalCount, inventory,
filterLotNo, filterLotNo,
@@ -99,7 +99,11 @@ const InventoryLotLineTable: React.FC<Props> = ({
remarks: '', remarks: '',
}); });
const originalAdjustmentLinesRef = useRef<AdjustmentEntry[]>([]); 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 [adjustmentEntries, setAdjustmentEntries] = useState<AdjustmentEntry[]>([]);
const [isAdjustSaving, setIsAdjustSaving] = useState(false);
useEffect(() => { useEffect(() => {
if (stockTransferModalOpen) { if (stockTransferModalOpen) {
fetchWarehouseListClient() fetchWarehouseListClient()
@@ -153,9 +157,34 @@ const prevAdjustmentModalOpenRef = useRef(false);
})); }));
setAdjustmentEntries(initial); setAdjustmentEntries(initial);
originalAdjustmentLinesRef.current = 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); setPendingRemovalLineId(null);
setRemovalReasons({}); setRemovalReasons({});
} else if (!stockAdjustmentModalOpen) {
remarksFetchGenRef.current += 1;
} }
}, [stockAdjustmentModalOpen, inventory, availableLotLines]); }, [stockAdjustmentModalOpen, inventory, availableLotLines]);


@@ -164,12 +193,15 @@ const prevAdjustmentModalOpenRef = useRef(false);
setPendingRemovalLineId(null); setPendingRemovalLineId(null);
setRemovalReasons({}); setRemovalReasons({});
setAdjustmentEntries( 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]); }, [availableLotLines]);


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


const handleAdjustmentSave = useCallback(async () => { const handleAdjustmentSave = useCallback(async () => {
if (!inventory) return; 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 { 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); setIsUploading(true);
await submitStockAdjustment({ await submitStockAdjustment({
itemId: inventory.itemId, itemId: inventory.itemId,
@@ -264,8 +307,10 @@ const prevAdjustmentModalOpenRef = useRef(false);
msgError(message || t("Save failed")); msgError(message || t("Save failed"));
} finally { } finally {
setIsUploading(false); setIsUploading(false);
setIsAdjustSaving(false);
adjustSaveInFlightRef.current = false;
} }
}, [adjustmentEntries, inventory, t, toApiLine, onStockAdjustmentSuccess]);
}, [adjustmentEntries, inventory, t, toApiLine, onStockAdjustmentSuccess, removalReasons]);


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


불러오는 중...
취소
저장