"use client"; import React, { useState } from "react"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { OrderCustomerSearch } from "./OrderCustomerSearch"; import { OrderItemRepeaterTable } from "./OrderItemRepeaterTable"; import { toast } from "sonner"; import { apiClient } from "@/lib/api/client"; interface OrderRegistrationModalProps { open: boolean; onOpenChange: (open: boolean) => void; onSuccess?: () => void; } export function OrderRegistrationModal({ open, onOpenChange, onSuccess, }: OrderRegistrationModalProps) { // 입력 방식 const [inputMode, setInputMode] = useState("customer_first"); // 판매 유형 (국내/해외) const [salesType, setSalesType] = useState("domestic"); // 단가 기준 (기준단가/거래처별단가) const [priceType, setPriceType] = useState("standard"); // 폼 데이터 const [formData, setFormData] = useState({ customerCode: "", customerName: "", contactPerson: "", deliveryDestination: "", deliveryAddress: "", deliveryDate: "", memo: "", // 무역 정보 (해외 판매 시) incoterms: "", paymentTerms: "", currency: "KRW", portOfLoading: "", portOfDischarge: "", hsCode: "", }); // 선택된 품목 목록 const [selectedItems, setSelectedItems] = useState([]); // 납기일 일괄 적용 플래그 (딱 한 번만 실행) const [isDeliveryDateApplied, setIsDeliveryDateApplied] = useState(false); // 저장 중 const [isSaving, setIsSaving] = useState(false); // 저장 처리 const handleSave = async () => { try { // 유효성 검사 if (!formData.customerCode) { toast.error("거래처를 선택해주세요"); return; } if (selectedItems.length === 0) { toast.error("품목을 추가해주세요"); return; } setIsSaving(true); // 수주 등록 API 호출 const orderData: any = { inputMode, salesType, priceType, customerCode: formData.customerCode, contactPerson: formData.contactPerson, deliveryDestination: formData.deliveryDestination, deliveryAddress: formData.deliveryAddress, deliveryDate: formData.deliveryDate, items: selectedItems, memo: formData.memo, }; // 해외 판매 시 무역 정보 추가 if (salesType === "export") { orderData.tradeInfo = { incoterms: formData.incoterms, paymentTerms: formData.paymentTerms, currency: formData.currency, portOfLoading: formData.portOfLoading, portOfDischarge: formData.portOfDischarge, hsCode: formData.hsCode, }; } const response = await apiClient.post("/orders", orderData); if (response.data.success) { toast.success("수주가 등록되었습니다"); onOpenChange(false); onSuccess?.(); // 폼 초기화 resetForm(); } else { toast.error(response.data.message || "수주 등록에 실패했습니다"); } } catch (error: any) { console.error("수주 등록 오류:", error); toast.error( error.response?.data?.message || "수주 등록 중 오류가 발생했습니다" ); } finally { setIsSaving(false); } }; // 취소 처리 const handleCancel = () => { onOpenChange(false); resetForm(); }; // 폼 초기화 const resetForm = () => { setInputMode("customer_first"); setSalesType("domestic"); setPriceType("standard"); setFormData({ customerCode: "", customerName: "", contactPerson: "", deliveryDestination: "", deliveryAddress: "", deliveryDate: "", memo: "", incoterms: "", paymentTerms: "", currency: "KRW", portOfLoading: "", portOfDischarge: "", hsCode: "", }); setSelectedItems([]); setIsDeliveryDateApplied(false); // 플래그 초기화 }; // 품목 목록 변경 핸들러 (납기일 일괄 적용 로직 포함) const handleItemsChange = (newItems: any[]) => { // 1️⃣ 플래그가 이미 true면 그냥 업데이트만 (일괄 적용 완료 상태) if (isDeliveryDateApplied) { setSelectedItems(newItems); return; } // 2️⃣ 품목이 없으면 그냥 업데이트 if (newItems.length === 0) { setSelectedItems(newItems); return; } // 3️⃣ 현재 상태: 납기일이 있는 행과 없는 행 개수 체크 const itemsWithDate = newItems.filter((item) => item.delivery_date); const itemsWithoutDate = newItems.filter((item) => !item.delivery_date); // 4️⃣ 조건: 정확히 1개만 날짜가 있고, 나머지는 모두 비어있을 때 일괄 적용 if (itemsWithDate.length === 1 && itemsWithoutDate.length > 0) { // 5️⃣ 전체 일괄 적용 const selectedDate = itemsWithDate[0].delivery_date; const updatedItems = newItems.map((item) => ({ ...item, delivery_date: selectedDate, // 모든 행에 동일한 납기일 적용 })); setSelectedItems(updatedItems); setIsDeliveryDateApplied(true); // 플래그 활성화 (다음부터는 일괄 적용 안 함) console.log("✅ 납기일 일괄 적용 완료:", selectedDate); console.log(` - 대상: ${itemsWithoutDate.length}개 행에 ${selectedDate} 적용`); } else { // 그냥 업데이트 setSelectedItems(newItems); } }; // 전체 금액 계산 const totalAmount = selectedItems.reduce( (sum, item) => sum + (item.amount || 0), 0 ); return ( 수주 등록 새로운 수주를 등록합니다
{/* 상단 셀렉트 박스 3개 */}
{/* 입력 방식 */}
{/* 판매 유형 */}
{/* 단가 기준 */}
{/* 거래처 정보 (항상 표시) */} {inputMode === "customer_first" && (
🏢 거래처 정보
{/* 거래처 */}
{ setFormData({ ...formData, customerCode: code || "", customerName: fullData?.customer_name || "", }); }} />
{/* 담당자 */}
setFormData({ ...formData, contactPerson: e.target.value }) } className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" />
{/* 납품처 */}
setFormData({ ...formData, deliveryDestination: e.target.value }) } className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" />
{/* 납품장소 */}
setFormData({ ...formData, deliveryAddress: e.target.value }) } className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" />
)} {inputMode === "quotation" && (
)} {inputMode === "unit_price" && (
)} {/* 추가된 품목 */}
{/* 전체 금액 표시 */} {selectedItems.length > 0 && (
전체 금액: {totalAmount.toLocaleString()}원
)} {/* 무역 정보 (해외 판매 시에만 표시) */} {salesType === "export" && (
🌏 무역 정보
{/* 인코텀즈 */}
{/* 결제 조건 */}
{/* 통화 */}
{/* 선적항 */}
setFormData({ ...formData, portOfLoading: e.target.value }) } className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" />
{/* 도착항 */}
setFormData({ ...formData, portOfDischarge: e.target.value }) } className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" />
{/* HS Code */}
setFormData({ ...formData, hsCode: e.target.value }) } className="flex h-8 w-full rounded-md border border-input bg-background px-3 py-2 text-xs ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 sm:h-10 sm:text-sm" />
)} {/* 메모 */}