diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-13 08:56:27 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-10-13 08:56:27 +0000 |
| commit | b9a2081a76e669688d5884f20482b37cc8acca22 (patch) | |
| tree | 385e78c05d193a54daaced836f1e1152696153a8 /lib/rfq-last | |
| parent | e84cf02a1cb4959a9d3bb5bbf37885c13a447f78 (diff) | |
(최겸, 임수민) 구매 입찰, 견적(그룹코드, tbe에러) 수정, data-room 수정
Diffstat (limited to 'lib/rfq-last')
| -rw-r--r-- | lib/rfq-last/attachment/rfq-attachments-table.tsx | 6 | ||||
| -rw-r--r-- | lib/rfq-last/service.ts | 29 | ||||
| -rw-r--r-- | lib/rfq-last/vendor/batch-update-conditions-dialog.tsx | 69 | ||||
| -rw-r--r-- | lib/rfq-last/vendor/vendor-detail-dialog.tsx | 10 |
4 files changed, 101 insertions, 13 deletions
diff --git a/lib/rfq-last/attachment/rfq-attachments-table.tsx b/lib/rfq-last/attachment/rfq-attachments-table.tsx index 3098f8f5..d97d32fd 100644 --- a/lib/rfq-last/attachment/rfq-attachments-table.tsx +++ b/lib/rfq-last/attachment/rfq-attachments-table.tsx @@ -416,7 +416,7 @@ export function RfqAttachmentsTable({ if (activeTab !== '구매') { return <span className="text-muted-foreground text-sm">-</span>; } - + return ( <DropdownMenu> <DropdownMenuTrigger asChild> @@ -442,7 +442,7 @@ export function RfqAttachmentsTable({ 새 버전 업로드 </DropdownMenuItem> <DropdownMenuSeparator /> - <DropdownMenuItem + <DropdownMenuItem onClick={() => handleAction({ type: "delete", row })} className="text-red-600" > @@ -455,7 +455,7 @@ export function RfqAttachmentsTable({ size: 60, enablePinning: true, }, - ], [handleAction]); + ], [handleAction, activeTab]); const advancedFilterFields: DataTableAdvancedFilterField<RfqAttachment>[] = [ { id: "serialNo", label: "일련번호", type: "text" }, diff --git a/lib/rfq-last/service.ts b/lib/rfq-last/service.ts index f536a142..f600d04b 100644 --- a/lib/rfq-last/service.ts +++ b/lib/rfq-last/service.ts @@ -2574,6 +2574,35 @@ export async function getRfqAttachments(rfqId: number) { return fullInfo.attachments; } +/** + * 특정 벤더의 현재 조건 조회 + */ +export async function getVendorConditions(rfqId: number, vendorId: number) { + const fullInfo = await getRfqFullInfo(rfqId); + const vendor = fullInfo.vendors?.find(v => v.vendorId === vendorId); + + if (!vendor) { + throw new Error('벤더 정보를 찾을 수 없습니다.'); + } + + return { + currency: vendor.currency, + paymentTermsCode: vendor.paymentTermsCode, + incotermsCode: vendor.incotermsCode, + incotermsDetail: vendor.incotermsDetail, + deliveryDate: vendor.deliveryDate, + contractDuration: vendor.contractDuration, + taxCode: vendor.taxCode, + placeOfShipping: vendor.placeOfShipping, + placeOfDestination: vendor.placeOfDestination, + materialPriceRelatedYn: vendor.materialPriceRelatedYn, + sparepartYn: vendor.sparepartYn, + firstYn: vendor.firstYn, + firstDescription: vendor.firstDescription, + sparepartDescription: vendor.sparepartDescription, + }; +} + // RFQ 발송용 데이터 타입 export interface RfqSendData { diff --git a/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx b/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx index 7eae48db..70d5569f 100644 --- a/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx +++ b/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx @@ -44,7 +44,7 @@ import { format } from "date-fns"; import { ko } from "date-fns/locale"; import { cn } from "@/lib/utils"; import { toast } from "sonner"; -import { updateVendorConditionsBatch } from "../service"; +import { updateVendorConditionsBatch, getVendorConditions } from "../service"; import { Badge } from "@/components/ui/badge"; import { TAX_CONDITIONS } from "@/lib/tax-conditions/types"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; @@ -72,8 +72,8 @@ interface BatchUpdateConditionsDialogProps { } // 타입 정의 -interface SelectOption { - id: number; +type SelectOption = { + id?: number; code: string; description: string; } @@ -169,7 +169,7 @@ export function BatchUpdateConditionsDialog({ setIncotermsLoading(true); try { const data = await getIncotermsForSelection(); - setIncoterms(data); + setIncoterms(data as unknown as SelectOption[]); } catch (error) { console.error("Failed to load incoterms:", error); toast.error("Incoterms 목록을 불러오는데 실패했습니다."); @@ -182,7 +182,7 @@ export function BatchUpdateConditionsDialog({ setPaymentTermsLoading(true); try { const data = await getPaymentTermsForSelection(); - setPaymentTerms(data); + setPaymentTerms(data as unknown as SelectOption[]); } catch (error) { console.error("Failed to load payment terms:", error); toast.error("결제조건 목록을 불러오는데 실패했습니다."); @@ -195,7 +195,7 @@ export function BatchUpdateConditionsDialog({ setShippingLoading(true); try { const data = await getPlaceOfShippingForSelection(); - setShippingPlaces(data); + setShippingPlaces(data as unknown as SelectOption[]); } catch (error) { console.error("Failed to load shipping places:", error); toast.error("선적지 목록을 불러오는데 실패했습니다."); @@ -208,7 +208,7 @@ export function BatchUpdateConditionsDialog({ setDestinationLoading(true); try { const data = await getPlaceOfDestinationForSelection(); - setDestinationPlaces(data); + setDestinationPlaces(data as unknown as SelectOption[]); } catch (error) { console.error("Failed to load destination places:", error); toast.error("도착지 목록을 불러오는데 실패했습니다."); @@ -217,6 +217,33 @@ export function BatchUpdateConditionsDialog({ } }, []); + // 벤더별 조건 로드 함수 + const loadVendorConditions = React.useCallback(async (vendorId: number) => { + try { + const conditions = await getVendorConditions(rfqId, vendorId); + // 가져온 조건으로 폼 초기화 + form.reset({ + currency: conditions.currency || "", + paymentTermsCode: conditions.paymentTermsCode || "", + incotermsCode: conditions.incotermsCode || "", + incotermsDetail: conditions.incotermsDetail || "", + deliveryDate: conditions.deliveryDate || undefined, + contractDuration: conditions.contractDuration || "", + taxCode: conditions.taxCode || "", + placeOfShipping: conditions.placeOfShipping || "", + placeOfDestination: conditions.placeOfDestination || "", + materialPriceRelatedYn: conditions.materialPriceRelatedYn || false, + sparepartYn: conditions.sparepartYn || false, + firstYn: conditions.firstYn || false, + firstDescription: conditions.firstDescription || "", + sparepartDescription: conditions.sparepartDescription || "", + }); + } catch (error) { + console.error("Failed to load vendor conditions:", error); + toast.error("벤더 조건을 불러오는데 실패했습니다."); + } + }, [rfqId, form]); + // 초기 데이터 로드 React.useEffect(() => { if (open) { @@ -224,13 +251,35 @@ export function BatchUpdateConditionsDialog({ loadPaymentTerms(); loadShippingPlaces(); loadDestinationPlaces(); + + // 선택된 벤더가 1개일 때만 해당 벤더의 조건을 가져옴 + if (selectedVendors.length === 1) { + loadVendorConditions(selectedVendors[0].id); + } } - }, [open, loadIncoterms, loadPaymentTerms, loadShippingPlaces, loadDestinationPlaces]); + }, [open, loadIncoterms, loadPaymentTerms, loadShippingPlaces, loadDestinationPlaces, selectedVendors, loadVendorConditions]); // 다이얼로그 닫힐 때 초기화 React.useEffect(() => { if (!open) { - form.reset(); + // 선택된 벤더가 2개 이상이거나 없다면 기본값으로 초기화 + if (selectedVendors.length !== 1) { + form.reset({ + currency: "", + paymentTermsCode: "", + incotermsCode: "", + incotermsDetail: "", + contractDuration: "", + taxCode: "", + placeOfShipping: "", + placeOfDestination: "", + materialPriceRelatedYn: false, + sparepartYn: false, + firstYn: false, + firstDescription: "", + sparepartDescription: "", + }); + } setFieldsToUpdate({ currency: false, paymentTermsCode: false, @@ -244,7 +293,7 @@ export function BatchUpdateConditionsDialog({ first: false, }); } - }, [open, form]); + }, [open, form, selectedVendors]); // 제출 처리 const onSubmit = async (data: FormValues) => { diff --git a/lib/rfq-last/vendor/vendor-detail-dialog.tsx b/lib/rfq-last/vendor/vendor-detail-dialog.tsx index 074924eb..08288dd6 100644 --- a/lib/rfq-last/vendor/vendor-detail-dialog.tsx +++ b/lib/rfq-last/vendor/vendor-detail-dialog.tsx @@ -36,6 +36,7 @@ import { Paperclip, Info, Edit, + X, } from "lucide-react"; import { format } from "date-fns"; import { ko } from "date-fns/locale"; @@ -160,6 +161,15 @@ export function VendorResponseDetailDialog({ </DialogDescription> </div> <div className="flex items-center gap-2"> + <Button + variant="ghost" + size="sm" + onClick={() => onOpenChange(false)} + className="h-8 w-8 p-0" + > + <X className="h-4 w-4" /> + <span className="sr-only">창 닫기</span> + </Button> {/* {onEdit && ( <Button variant="outline" size="sm" onClick={onEdit}> <Edit className="h-4 w-4 mr-2" /> |
