From 535de26b9cf3242c04543d6891d78352b9593a60 Mon Sep 17 00:00:00 2001
From: dujinkim
Date: Tue, 11 Nov 2025 09:22:58 +0000
Subject: (최겸) 구매 수정사항 개발
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
lib/rfq-last/attachment/vendor-response-table.tsx | 2 +-
lib/rfq-last/quotation-compare-view.tsx | 46 ++-------
lib/rfq-last/table/rfq-table-columns.tsx | 2 +-
lib/rfq-last/table/rfq-table.tsx | 2 +-
lib/rfq-last/validations.ts | 2 +-
.../vendor/batch-update-conditions-dialog.tsx | 112 ++++++---------------
lib/rfq-last/vendor/rfq-vendor-table.tsx | 56 ++++++++---
lib/rfq-last/vendor/send-rfq-dialog.tsx | 8 +-
8 files changed, 90 insertions(+), 140 deletions(-)
(limited to 'lib/rfq-last')
diff --git a/lib/rfq-last/attachment/vendor-response-table.tsx b/lib/rfq-last/attachment/vendor-response-table.tsx
index 22f813b3..b09487d6 100644
--- a/lib/rfq-last/attachment/vendor-response-table.tsx
+++ b/lib/rfq-last/attachment/vendor-response-table.tsx
@@ -590,7 +590,7 @@ export function VendorResponseTable({
) : (
<>
- {selectedVendor} 문서 확정
+ {selectedVendor} 설계전송 문서 확정
>
)}
diff --git a/lib/rfq-last/quotation-compare-view.tsx b/lib/rfq-last/quotation-compare-view.tsx
index 3bb27b55..7a4fd751 100644
--- a/lib/rfq-last/quotation-compare-view.tsx
+++ b/lib/rfq-last/quotation-compare-view.tsx
@@ -70,7 +70,6 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
const [selectedResponse, setSelectedResponse] = React.useState(null);
const [selectedVendorName, setSelectedVendorName] = React.useState("");
const [selectedContractType, setSelectedContractType] = React.useState<"PO" | "CONTRACT" | "BIDDING" | "">("");
- const [selectionReason, setSelectionReason] = React.useState("");
const [cancelReason, setCancelReason] = React.useState("");
const [isSubmitting, setIsSubmitting] = React.useState(false);
const router = useRouter()
@@ -323,11 +322,6 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
return;
}
- if (!selectionReason.trim()) {
- toast.error("선정 사유를 입력해주세요.");
- return;
- }
-
setIsSubmitting(true);
try {
const vendor = data.vendors.find(v => v.vendorId === parseInt(selectedVendorId));
@@ -348,8 +342,8 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
vendorCode: vendor.vendorCode,
totalAmount: latestResponse.totalAmount,
currency: latestResponse.currency,
- selectionReason: selectionReason,
- priceRank: latestResponse.rank || 0,
+ selectionReason: "",
+ priceRank: 0,
hasConditionDifferences: latestResponse.conditionDifferences.hasDifferences,
criticalDifferences: latestResponse.conditionDifferences.criticalDifferences,
});
@@ -357,7 +351,6 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
if (result.success) {
toast.success("업체가 성공적으로 선정되었습니다.");
setShowSelectionDialog(false);
- setSelectionReason("");
router.refresh()
} else {
throw new Error(result.error || "업체 선정 중 오류가 발생했습니다.");
@@ -410,24 +403,25 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
)}
-
+ */}
>
) : (
선정 금액: {formatAmount(selectedVendor.totalAmount, selectedVendor.currency)}
선정일: {selectedVendor.selectionDate ? format(new Date(selectedVendor.selectionDate), "yyyy년 MM월 dd일", { locale: ko }) : "-"}
- 선정 사유: {selectedVendor.selectionReason || "-"}
{selectedVendor.contractNo && (
<>
@@ -508,6 +501,7 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
setSelectedContractType("PO");
setShowContractDialog(true);
}}
+ disabled={data.rfqInfo.rfqCode?.startsWith("I")}
className="gap-1 bg-green-600 hover:bg-green-700 text-xs"
>
@@ -929,7 +923,7 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
{/* 납기일 */}
- | 납기일 |
+ PR납기 요청일 |
{data.vendors.map((vendor) => {
const latestResponse = vendor.responses[0];
return (
@@ -1245,36 +1239,17 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
)}
-
- 가격 순위
-
- #{latestResponse?.rank || 0}
-
-
{latestResponse?.conditionDifferences.hasDifferences && (
- 제시 조건과 차이가 있습니다. 선정 사유를 명확히 기재해주세요.
+ 제시 조건과 차이가 있습니다.
)}
-
-
-
);
})()}
@@ -1284,7 +1259,6 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
variant="outline"
onClick={() => {
setShowSelectionDialog(false);
- setSelectionReason("");
}}
disabled={isSubmitting}
>
@@ -1292,7 +1266,7 @@ export function QuotationCompareView({ data }: QuotationCompareViewProps) {
diff --git a/lib/rfq-last/table/rfq-table-columns.tsx b/lib/rfq-last/table/rfq-table-columns.tsx
index e8a5ba94..6976e1c5 100644
--- a/lib/rfq-last/table/rfq-table-columns.tsx
+++ b/lib/rfq-last/table/rfq-table-columns.tsx
@@ -34,7 +34,7 @@ const getStatusBadgeVariant = (status: string) => {
case "RFQ 생성": return "outline";
case "구매담당지정": return "secondary";
case "견적요청문서 확정": return "default";
- case "Short List 확정": return "default";
+ case "TBE 요청": return "default";
case "TBE 완료": return "default";
case "RFQ 발송": return "default";
case "견적접수": return "default";
diff --git a/lib/rfq-last/table/rfq-table.tsx b/lib/rfq-last/table/rfq-table.tsx
index 46bb4670..80f1422e 100644
--- a/lib/rfq-last/table/rfq-table.tsx
+++ b/lib/rfq-last/table/rfq-table.tsx
@@ -258,7 +258,7 @@ export function RfqTable({
{ label: "RFQ 생성", value: "RFQ 생성" },
{ label: "구매담당지정", value: "구매담당지정" },
{ label: "견적요청문서 확정", value: "견적요청문서 확정" },
- { label: "Short List 확정", value: "Short List 확정" },
+ { label: "TBE 요청", value: "TBE 요청" },
{ label: "TBE 완료", value: "TBE 완료" },
{ label: "RFQ 발송", value: "RFQ 발송" },
{ label: "견적접수", value: "견적접수" },
diff --git a/lib/rfq-last/validations.ts b/lib/rfq-last/validations.ts
index 6a5816d4..6b39d52d 100644
--- a/lib/rfq-last/validations.ts
+++ b/lib/rfq-last/validations.ts
@@ -17,7 +17,7 @@ import { RfqLastAttachments } from "@/db/schema";
{ value: "RFQ 생성", label: "RFQ 생성" },
{ value: "구매담당지정", label: "구매담당지정" },
{ value: "견적요청문서 확정", label: "견적요청문서 확정" },
- { value: "Short List 확정", label: "Short List 확정" },
+ { value: "TBE 요청", label: "TBE 요청" },
{ value: "TBE 완료", label: "TBE 완료" },
{ value: "RFQ 발송", label: "RFQ 발송" },
{ value: "견적접수", label: "견적접수" },
diff --git a/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx b/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx
index c258293b..6112aed4 100644
--- a/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx
+++ b/lib/rfq-last/vendor/batch-update-conditions-dialog.tsx
@@ -1096,36 +1096,18 @@ export function BatchUpdateConditionsDialog({
checked={fieldsToUpdate.materialPrice}
onCheckedChange={(checked) => {
setFieldsToUpdate({ ...fieldsToUpdate, materialPrice: !!checked });
- if (checked) {
- form.setValue("materialPriceRelatedYn", true);
- }
}}
/>
- (
-
-
-
- 연동제 적용 요건문의
-
-
- 원자재 가격 연동 여부
-
-
-
-
-
-
- )}
- />
+
+
+ 연동제 적용 요건문의
+
+
+ 원자재 가격 연동 여부
+
+
{/* Spare Part */}
@@ -1140,33 +1122,18 @@ export function BatchUpdateConditionsDialog({
}
}}
/>
- (
-
-
-
- Spare Part
-
-
- 예비 부품 요구사항
-
-
-
-
-
-
- )}
- />
+
+
+ Spare Part
+
+
+ 예비 부품 요구사항
+
+
- {form.watch("sparepartYn") && fieldsToUpdate.sparepart && (
+ {fieldsToUpdate.sparepart && (
- (
-
-
-
- 초도품 관리
-
-
- 초도품 관리 요구사항
-
-
-
-
-
-
- )}
- />
+
+
+ 초도품 관리
+
+
+ 초도품 관리 요구사항
+
+
- {form.watch("firstYn") && fieldsToUpdate.first && (
+ {fieldsToUpdate.first && (
{
try {
setIsUpdatingShortList(true);
- // response가 있는 벤더들만 필터링
- const vendorsWithResponse = selectedRows.filter(vendor =>
- vendor.response && vendor.response.vendor&& vendor.response.isDocumentConfirmed
- );
+ // response가 있는 벤더들 필터링
+ const vendorsWithResponse = selectedRows.filter(vendor =>
+ vendor.response && vendor.response.vendor
+ );
- if (vendorsWithResponse.length === 0) {
- toast.warning("응답이 있는 벤더를 선택해주세요.");
- return;
- }
+ if (vendorsWithResponse.length === 0) {
+ toast.warning("응답이 있는 벤더를 선택해주세요.");
+ return;
+ }
+
+ // 문서확정된 벤더들 필터링
+ const vendorsWithConfirmedDocs = vendorsWithResponse.filter(vendor =>
+ vendor.response.isDocumentConfirmed
+ );
+
+ // 문서확정되지 않은 벤더가 있는 경우 경고 메시지 표시
+ const vendorsWithoutConfirmedDocs = vendorsWithResponse.filter(vendor =>
+ !vendor.response.isDocumentConfirmed
+ );
+
+ if (vendorsWithoutConfirmedDocs.length > 0) {
+ toast.warning("벤더회신문서를 확인하시고 설계전송 문서 확정해주세요");
+ return;
+ }
+
+ // 문서확정된 벤더만 TBE 요청 처리
+ if (vendorsWithConfirmedDocs.length === 0) {
+ toast.warning("문서가 확정된 벤더가 없습니다.");
+ return;
+ }
- const vendorIds = vendorsWithResponse
+ const vendorIds = vendorsWithConfirmedDocs
.map(vendor => vendor.vendorId)
.filter(id => id != null);
@@ -361,8 +382,8 @@ export function RfqVendorTable({
router.refresh();
}
} catch (error) {
- console.error("Short List 확정 실패:", error);
- toast.error("Short List 확정에 실패했습니다.");
+ console.error("TBE 요청 실패:", error);
+ toast.error("TBE 요청에 실패했습니다.");
} finally {
setIsUpdatingShortList(false);
}
@@ -1478,7 +1499,7 @@ export function RfqVendorTable({
},
{
id: "responseDetail",
- header: "회신상세",
+ header: "제출여부",
cell: ({ row }) => {
const hasResponse = !!row.original.response?.submission?.submittedAt;
@@ -1820,7 +1841,10 @@ export function RfqVendorTable({
// 참여 의사가 있는 선택된 벤더 수 계산
const participatingCount = selectedRows.length;
const shortListCount = selectedRows.filter(v => v.shortList).length;
- const vendorsWithResponseCount = selectedRows.filter(v => v.response && v.response.vendor && v.response.isDocumentConfirmed).length;
+ // TBE 요청 버튼용: 응답이 있는 벤더 수 (문서확정 여부와 무관)
+ const vendorsWithResponseCount = selectedRows.filter(v => v.response && v.response.vendor).length;
+ // 문서확정된 벤더 수
+ const vendorsWithConfirmedDocsCount = selectedRows.filter(v => v.response && v.response.vendor && v.response.isDocumentConfirmed).length;
// 견적서가 있는 선택된 벤더 수 계산 (취소되지 않은 벤더만)
const quotationCount = nonCancelledRows.filter(row =>
@@ -1897,7 +1921,7 @@ export function RfqVendorTable({
)}
- {/* Short List 확정 버튼 */}
+ {/* TBE 요청 버튼 */}
{!rfqCode?.startsWith("F") &&