From 198cbcf93b20d3849705dcdbba439326d82b0cae Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 8 Dec 2025 03:24:15 +0000 Subject: (최겸) 기술영업 rfq 프로젝트 숨기기 기능 추가 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/soap/ecc/mapper/bidding-and-pr-mapper.ts | 1 + lib/techsales-rfq/repository.ts | 1 + lib/techsales-rfq/service.ts | 28 ++++++++++++++++++++-- .../table/detail-table/rfq-detail-table.tsx | 3 ++- .../vendor-contact-selection-dialog.tsx | 22 +++++++++++++---- .../vendor-response/detail/project-info-tab.tsx | 13 +++++++--- .../table/vendor-quotations-table-columns.tsx | 7 ++++-- 7 files changed, 63 insertions(+), 12 deletions(-) (limited to 'lib') diff --git a/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts b/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts index 9bf61452..4cdaf90d 100644 --- a/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts +++ b/lib/soap/ecc/mapper/bidding-and-pr-mapper.ts @@ -296,6 +296,7 @@ export async function mapECCBiddingHeaderToBidding( // PR 정보 prNumber, // 첫번째 PR의 ZREQ_FN 값 hasPrDocument: false, // PR문서는 POS를 말하는 것으로 보임. + plant: eccHeader.WERKS || null, // 플랜트 코드(WERKS) // 상태 및 설정 status: 'bidding_generated', // 입찰생성 상태 diff --git a/lib/techsales-rfq/repository.ts b/lib/techsales-rfq/repository.ts index e6138651..61072d3f 100644 --- a/lib/techsales-rfq/repository.ts +++ b/lib/techsales-rfq/repository.ts @@ -94,6 +94,7 @@ export async function selectTechSalesRfqsWithJoin( // 담당자 및 비고 picCode: techSalesRfqs.picCode, + hideProjectInfoForVendors: techSalesRfqs.hideProjectInfoForVendors, remark: techSalesRfqs.remark, cancelReason: techSalesRfqs.cancelReason, description: techSalesRfqs.description, diff --git a/lib/techsales-rfq/service.ts b/lib/techsales-rfq/service.ts index 13d0bbce..8ce41cba 100644 --- a/lib/techsales-rfq/service.ts +++ b/lib/techsales-rfq/service.ts @@ -557,6 +557,7 @@ export async function sendTechSalesRfqToVendors(input: { email?: string | null; epId?: string | null; }; + hideProjectInfoForVendors?: boolean; }) { unstable_noStore(); try { @@ -573,6 +574,7 @@ export async function sendTechSalesRfqToVendors(input: { materialCode: true, description: true, rfqType: true, + hideProjectInfoForVendors: true, }, with: { biddingProject: true, @@ -604,6 +606,23 @@ export async function sendTechSalesRfqToVendors(input: { } const isResend = rfq.status === TECH_SALES_RFQ_STATUSES.RFQ_SENT; + const effectiveHideProjectInfo = + typeof input.hideProjectInfoForVendors === "boolean" + ? input.hideProjectInfoForVendors + : rfq.hideProjectInfoForVendors ?? false; + + if ( + typeof input.hideProjectInfoForVendors === "boolean" && + input.hideProjectInfoForVendors !== rfq.hideProjectInfoForVendors + ) { + await db + .update(techSalesRfqs) + .set({ + hideProjectInfoForVendors: input.hideProjectInfoForVendors, + updatedAt: new Date(), + }) + .where(eq(techSalesRfqs.id, input.rfqId)); + } // 현재 사용자 정보 조회 const sender = await db.query.users.findFirst({ @@ -728,6 +747,9 @@ export async function sendTechSalesRfqToVendors(input: { const rfqItemsResult = await getTechSalesRfqItems(rfq.id); const rfqItems = rfqItemsResult.data || []; + const projectNameForVendor = effectiveHideProjectInfo ? "" : rfq.biddingProject?.projNm || ""; + const projectCodeForVendor = effectiveHideProjectInfo ? "" : rfq.biddingProject?.pspid || ""; + // 이메일 컨텍스트 구성 const emailContext = { language: language, @@ -735,8 +757,8 @@ export async function sendTechSalesRfqToVendors(input: { id: rfq.id, code: rfq.rfqCode, title: rfqItems.length > 0 ? rfqItems.map(item => item.itemList).join(', ') : '', - projectCode: rfq.biddingProject?.pspid || '', - projectName: rfq.biddingProject?.projNm || '', + projectCode: projectCodeForVendor, + projectName: projectNameForVendor, description: rfq.remark || '', dueDate: rfq.dueDate ? formatDate(rfq.dueDate, "KR") : 'N/A', materialCode: rfq.materialCode || '', @@ -990,6 +1012,7 @@ export async function getTechSalesVendorQuotation(quotationId: number) { projMsrm: quotation.projMsrm, ptypeNm: quotation.ptypeNm, } : null, + hideProjectInfoForVendors: quotation.hideProjectInfoForVendors ?? false, }, // 벤더 정보 @@ -1414,6 +1437,7 @@ export async function getVendorQuotations(input: { dueDate: techSalesRfqs.dueDate, rfqStatus: techSalesRfqs.status, description: techSalesRfqs.description, + hideProjectInfoForVendors: techSalesRfqs.hideProjectInfoForVendors, // 프로젝트 정보 (직접 조인) projNm: biddingProjects.projNm, // 아이템 개수 diff --git a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx index aee15594..d8ced6f8 100644 --- a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx +++ b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx @@ -257,7 +257,7 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps contactId: number; contactEmail: string; contactName: string; - }>) => { + }>, options?: { hideProjectInfoForVendors?: boolean }) => { if (!selectedRfqId) { toast.error("선택된 RFQ가 없습니다."); return; @@ -301,6 +301,7 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps name: session.data.user.name || undefined, email: session.data.user.email || undefined, }, + hideProjectInfoForVendors: options?.hideProjectInfoForVendors, }); if (result.success) { diff --git a/lib/techsales-rfq/table/detail-table/vendor-contact-selection-dialog.tsx b/lib/techsales-rfq/table/detail-table/vendor-contact-selection-dialog.tsx index d83394bb..8daa9be7 100644 --- a/lib/techsales-rfq/table/detail-table/vendor-contact-selection-dialog.tsx +++ b/lib/techsales-rfq/table/detail-table/vendor-contact-selection-dialog.tsx @@ -49,7 +49,10 @@ interface VendorContactSelectionDialogProps { onOpenChange: (open: boolean) => void vendorIds: number[] rfqId?: number // RFQ ID 추가 - onSendRfq: (selectedContacts: SelectedContact[]) => Promise + onSendRfq: ( + selectedContacts: SelectedContact[], + options: { hideProjectInfoForVendors: boolean } + ) => Promise } export function VendorContactSelectionDialog({ @@ -63,6 +66,7 @@ export function VendorContactSelectionDialog({ const [selectedContacts, setSelectedContacts] = useState([]) const [isLoading, setIsLoading] = useState(false) const [isSending, setIsSending] = useState(false) + const [hideProjectInfoForVendors, setHideProjectInfoForVendors] = useState(false) // 벤더 contact 정보 조회 useEffect(() => { @@ -77,6 +81,7 @@ export function VendorContactSelectionDialog({ setVendorsWithContacts({}) setSelectedContacts([]) setIsLoading(false) + setHideProjectInfoForVendors(false) } }, [open]) @@ -177,7 +182,7 @@ export function VendorContactSelectionDialog({ try { setIsSending(true) - await onSendRfq(selectedContacts) + await onSendRfq(selectedContacts, { hideProjectInfoForVendors }) onOpenChange(false) } catch (error) { console.error("RFQ 발송 오류:", error) @@ -328,8 +333,17 @@ export function VendorContactSelectionDialog({
-
- 총 {selectedContacts.length}명의 연락처가 선택됨 +
+
+ 총 {selectedContacts.length}명의 연락처가 선택됨 +
+