diff --git a/src/app/[locale]/(protected)/sales/stocks/[id]/page.tsx b/src/app/[locale]/(protected)/sales/stocks/[id]/page.tsx index f0071ca5..491ce152 100644 --- a/src/app/[locale]/(protected)/sales/stocks/[id]/page.tsx +++ b/src/app/[locale]/(protected)/sales/stocks/[id]/page.tsx @@ -4,7 +4,7 @@ * 재고생산 상세/수정 페이지 * * - 기본: 상세 보기 (StockProductionDetail) - * - ?mode=edit: 수정 (StockProductionForm) + * - ?mode=edit: 수정 (BendingLotForm — 등록과 동일 레이아웃) */ import { useState, useEffect } from 'react'; @@ -12,7 +12,7 @@ import { useParams, useSearchParams } from 'next/navigation'; import { Loader2 } from 'lucide-react'; import { toast } from 'sonner'; import { StockProductionDetail } from '@/components/stocks/StockProductionDetail'; -import { StockProductionForm } from '@/components/stocks/StockProductionForm'; +import { BendingLotForm } from '@/components/stocks/BendingLotForm'; import { getStockOrderById, type StockOrder } from '@/components/stocks/actions'; function EditStockContent({ id }: { id: string }) { @@ -58,7 +58,7 @@ function EditStockContent({ id }: { id: string }) { ); } - return ; + return ; } export default function StockDetailPage() { diff --git a/src/components/stocks/BendingLotForm.tsx b/src/components/stocks/BendingLotForm.tsx index 35e66ba5..beb8c852 100644 --- a/src/components/stocks/BendingLotForm.tsx +++ b/src/components/stocks/BendingLotForm.tsx @@ -38,10 +38,12 @@ import { resolveBendingItem, generateBendingLot, createBendingStockOrder, + updateBendingStockOrder, getMaterialLots, type BendingCodeMap, type BendingResolvedItem, type MaterialLot, + type StockOrder, } from './actions'; import type { DetailConfig } from '@/components/templates/IntegratedDetailTemplate/types'; @@ -63,6 +65,20 @@ const bendingCreateConfig: DetailConfig = { }, }; +const bendingEditConfig: DetailConfig = { + title: '절곡품 재고생산', + description: '절곡품 재고생산 정보를 수정합니다', + icon: Package, + basePath: '/sales/stocks', + fields: [], + actions: { + showBack: true, + showSave: true, + submitLabel: '저장', + backLabel: '취소', + }, +}; + // ============================================================================ // LOT 프리뷰 날짜코드 생성 // ============================================================================ @@ -213,13 +229,33 @@ function MaterialLotModal({ // Component // ============================================================================ -export function BendingLotForm() { +interface BendingLotFormProps { + initialData?: StockOrder; + isEditMode?: boolean; +} + +export function BendingLotForm({ initialData, isEditMode = false }: BendingLotFormProps) { const router = useRouter(); const params = useParams(); const locale = (params.locale as string) || 'ko'; const basePath = `/${locale}/sales/stocks`; - const [form, setForm] = useState(getInitialForm); + const [form, setForm] = useState(() => { + if (initialData?.bendingLot) { + const bl = initialData.bendingLot; + return { + regDate: getInitialForm().regDate, + prodCode: bl.prodCode || '', + specCode: bl.specCode || '', + lengthCode: bl.lengthCode || '', + quantity: initialData.targetStockQty || initialData.quantity || 1, + rawLotNo: bl.rawLotNo || '', + fabricLotNo: bl.fabricLotNo || '', + memo: initialData.memo || '', + }; + } + return getInitialForm(); + }); const [codeMap, setCodeMap] = useState(null); const [resolvedItem, setResolvedItem] = useState(null); const [resolveError, setResolveError] = useState(''); @@ -228,7 +264,9 @@ export function BendingLotForm() { const [rawLotModalOpen, setRawLotModalOpen] = useState(false); const [fabricLotModalOpen, setFabricLotModalOpen] = useState(false); - // 코드맵 로드 + const config = isEditMode ? bendingEditConfig : bendingCreateConfig; + + // 코드맵 로드 + edit mode 시 초기 품목 매핑 조회 useEffect(() => { async function loadCodeMap() { const result = await getBendingCodeMap(); @@ -238,6 +276,17 @@ export function BendingLotForm() { } if (result.success && result.data) { setCodeMap(result.data); + + // edit mode: 초기 데이터의 품목 매핑 자동 조회 + if (initialData?.bendingLot) { + const bl = initialData.bendingLot; + if (bl.prodCode && bl.specCode && bl.lengthCode) { + const itemResult = await resolveBendingItem(bl.prodCode, bl.specCode, bl.lengthCode); + if (itemResult.success && itemResult.data) { + setResolvedItem(itemResult.data); + } + } + } } else { toast.error(result.error || '코드맵 로딩에 실패했습니다.'); } @@ -363,12 +412,12 @@ export function BendingLotForm() { const lotData = lotResult.data; - // 2. 재고생산 저장 + // 2. 재고생산 저장/수정 const itemName = resolvedItem?.item_name || `${codeMap?.products.find((p) => p.code === form.prodCode)?.name || form.prodCode} ${codeMap?.specs.find((s) => s.code === form.specCode)?.name || form.specCode}`; - const saveResult = await createBendingStockOrder({ + const saveParams = { memo: form.memo, targetStockQty: form.quantity, bendingLot: { @@ -388,7 +437,11 @@ export function BendingLotForm() { quantity: form.quantity, unit: resolvedItem?.unit || 'EA', }, - }); + }; + + const saveResult = isEditMode && initialData + ? await updateBendingStockOrder(initialData.id, saveParams) + : await createBendingStockOrder(saveParams); if (saveResult.__authError) { toast.error('인증이 만료되었습니다.'); @@ -399,7 +452,7 @@ export function BendingLotForm() { return; } - toast.success('절곡품 재고생산이 등록되었습니다.'); + toast.success(isEditMode ? '절곡품 재고생산이 수정되었습니다.' : '절곡품 재고생산이 등록되었습니다.'); if (saveResult.data?.id) { router.push(`${basePath}/${saveResult.data.id}`); } else { @@ -412,8 +465,12 @@ export function BendingLotForm() { // 취소 const handleCancel = useCallback(() => { - router.push(basePath); - }, [router, basePath]); + if (isEditMode && initialData) { + router.push(`${basePath}/${initialData.id}`); + } else { + router.push(basePath); + } + }, [isEditMode, initialData, router, basePath]); // renderForm const renderFormContent = useMemo( @@ -550,15 +607,19 @@ export function BendingLotForm() {
- + -

- 일련번호는 저장 시 자동 확정됩니다 -

+ {!isEditMode && ( +

+ 일련번호는 저장 시 자동 확정됩니다 +

+ )}
@@ -661,6 +722,8 @@ export function BendingLotForm() { resolvedItem, resolveError, isSmokeBarrier, + isEditMode, + initialData, handleProdChange, handleSpecChange, handleLengthChange, @@ -670,8 +733,8 @@ export function BendingLotForm() { return ( <> (() => { - if (initialData) { - return { - productionReason: initialData.productionReason || '', - targetStockQty: initialData.targetStockQty ? String(initialData.targetStockQty) : '', - memo: initialData.memo || '', - remarks: initialData.remarks || '', - items: initialData.items.map((item) => ({ - id: item.id, - itemId: item.itemId, - itemCode: item.itemCode, - itemName: item.itemName, - specification: item.specification, - quantity: item.quantity, - unit: item.unit, - unitPrice: item.unitPrice, - amount: item.totalAmount, - })), - }; - } - return INITIAL_FORM; - }); - - const [fieldErrors, setFieldErrors] = useState>({}); - - // 필드 에러 초기화 - const clearFieldError = useCallback((field: string) => { - setFieldErrors((prev) => { - if (prev[field]) { - const { [field]: _, ...rest } = prev; - return rest; - } - return prev; - }); - }, []); - - // 품목 삭제 - const handleRemoveItem = useCallback((itemId: string) => { - setForm((prev) => ({ - ...prev, - items: prev.items.filter((item) => item.id !== itemId), - })); - }, []); - - // 품목 수량 변경 - const handleQuantityChange = useCallback((itemId: string, quantity: number) => { - setForm((prev) => ({ - ...prev, - items: prev.items.map((item) => - item.id === itemId - ? { ...item, quantity, amount: item.unitPrice * quantity } - : item - ), - })); - }, []); - - // 유효성 검사 - const validate = useCallback((): boolean => { - const errors: Record = {}; - - if (form.items.length === 0) { - errors.items = '품목을 1개 이상 추가해주세요'; - } - - setFieldErrors(errors); - return Object.keys(errors).length === 0; - }, [form.items]); - - // 저장 - const handleSave = useCallback(async () => { - if (!validate()) { - toast.error('입력 정보를 확인해주세요.'); - return; - } - - const formData: StockOrderFormData = { - orderTypeCode: 'STOCK', - memo: form.memo, - remarks: form.remarks, - productionReason: form.productionReason, - targetStockQty: Number(form.targetStockQty) || 0, - items: form.items.map((item): StockOrderItemFormData => ({ - itemId: item.itemId, - itemCode: item.itemCode, - itemName: item.itemName, - specification: item.specification, - quantity: item.quantity, - unit: item.unit || 'EA', - unitPrice: item.unitPrice, - })), - }; - - const result = isEditMode && initialData - ? await updateStockOrder(initialData.id, formData) - : await createStockOrder(formData); - - if (result.__authError) { - toast.error('인증이 만료되었습니다. 다시 로그인해주세요.'); - return; - } - - if (!result.success) { - toast.error(result.error || '저장에 실패했습니다.'); - return; - } - - toast.success(isEditMode ? '재고생산이 수정되었습니다.' : '재고생산이 등록되었습니다.'); - - if (result.data?.id) { - router.push(`${basePath}/${result.data.id}`); - } else { - router.push(basePath); - } - }, [form, isEditMode, initialData, validate, router, basePath]); - - // 취소 - const handleCancel = useCallback(() => { - if (isEditMode && initialData) { - router.push(`${basePath}/${initialData.id}`); - } else { - router.push(basePath); - } - }, [isEditMode, initialData, router, basePath]); - - // renderForm - const renderFormContent = useMemo( - () => - () => ( -
- {/* 기본 정보 */} - -
-
- - setForm((prev) => ({ ...prev, productionReason: e.target.value }))} - /> -
-
- - setForm((prev) => ({ ...prev, targetStockQty: String(value ?? 0) }))} - min={0} - /> -
-
-
- - {/* 비고 */} - -
-
- -