diff options
Diffstat (limited to 'lib/bidding')
| -rw-r--r-- | lib/bidding/actions.ts | 22 | ||||
| -rw-r--r-- | lib/bidding/detail/table/price-adjustment-dialog.tsx | 10 | ||||
| -rw-r--r-- | lib/bidding/service.ts | 322 |
3 files changed, 348 insertions, 6 deletions
diff --git a/lib/bidding/actions.ts b/lib/bidding/actions.ts index 6bedbab5..64dc3aa8 100644 --- a/lib/bidding/actions.ts +++ b/lib/bidding/actions.ts @@ -20,7 +20,7 @@ import { createPurchaseOrder } from "@/lib/soap/ecc/send/create-po-bidding" import { getCurrentSAPDate } from "@/lib/soap/utils" import { generateContractNumber } from "@/lib/general-contracts/service" import { saveFile } from "@/lib/file-stroage" - +import { checkAndSaveChemicalSubstancesForBidding } from "./service" // TO Contract export async function transmitToContract(biddingId: number, userId: number) { try { @@ -738,6 +738,26 @@ export async function openBiddingAction(biddingId: number) { }) .where(eq(biddings.id, biddingId)) + // 4. 화학물질 조회 실행 (비동기로 실행해서 개찰 성능에 영향 없도록) + try { + // 개찰 트랜잭션이 완료된 후 화학물질 조회 시작 + setImmediate(async () => { + try { + const result = await checkAndSaveChemicalSubstancesForBidding(biddingId) + if (result.success) { + console.log(`입찰 ${biddingId} 화학물질 조회 완료: ${result.results.filter(r => r.success).length}/${result.results.length}개 업체`) + } else { + console.error(`입찰 ${biddingId} 화학물질 조회 실패:`, result.message) + } + } catch (error) { + console.error(`입찰 ${biddingId} 화학물질 조회 중 오류:`, error) + } + }) + } catch (error) { + // 화학물질 조회 실패해도 개찰은 성공으로 처리 + console.error('화학물질 조회 시작 실패:', error) + } + return { success: true, message: isDeadlinePassed ? '개찰이 완료되었습니다.' : '조기개찰이 완료되었습니다.' } }) diff --git a/lib/bidding/detail/table/price-adjustment-dialog.tsx b/lib/bidding/detail/table/price-adjustment-dialog.tsx index 14bbd843..96a3af0c 100644 --- a/lib/bidding/detail/table/price-adjustment-dialog.tsx +++ b/lib/bidding/detail/table/price-adjustment-dialog.tsx @@ -94,13 +94,13 @@ export function PriceAdjustmentDialog({ <DialogHeader> <DialogTitle>연동제 적용 설정</DialogTitle> <DialogDescription> - <span className="font-semibold text-primary">{vendor.vendorName}</span> 업체의 연동제 적용 여부 및 화학물질 정보를 설정합니다. + <span className="font-semibold text-primary">{vendor.vendorName}</span> 업체의 연동제 적용 여부를 설정합니다. </DialogDescription> </DialogHeader> <div className="space-y-6 py-4"> {/* 업체가 제출한 연동제 요청 여부 (읽기 전용) */} - <div className="flex flex-row items-center justify-between rounded-lg border p-4 bg-muted/50"> + {/* <div className="flex flex-row items-center justify-between rounded-lg border p-4 bg-muted/50"> <div className="space-y-0.5"> <Label className="text-base">업체 연동제 요청</Label> <p className="text-sm text-muted-foreground"> @@ -110,7 +110,7 @@ export function PriceAdjustmentDialog({ <span className={`font-medium ${vendor.isPriceAdjustmentApplicableQuestion ? 'text-green-600' : 'text-gray-500'}`}> {vendor.isPriceAdjustmentApplicableQuestion === null ? '미정' : vendor.isPriceAdjustmentApplicableQuestion ? '예' : '아니오'} </span> - </div> + </div> */} {/* SHI 연동제 적용여부 */} <div className="flex flex-row items-center justify-between rounded-lg border p-4"> @@ -147,7 +147,7 @@ export function PriceAdjustmentDialog({ </div> {/* 화학물질 여부 */} - <div className="flex flex-row items-center justify-between rounded-lg border p-4"> + {/* <div className="flex flex-row items-center justify-between rounded-lg border p-4"> <div className="space-y-0.5"> <Label className="text-base">화학물질 해당여부</Label> <p className="text-sm text-muted-foreground"> @@ -166,7 +166,7 @@ export function PriceAdjustmentDialog({ 해당 </span> </div> - </div> + </div> */} </div> <DialogFooter> diff --git a/lib/bidding/service.ts b/lib/bidding/service.ts index d45e9286..71ee01ab 100644 --- a/lib/bidding/service.ts +++ b/lib/bidding/service.ts @@ -44,6 +44,7 @@ import { saveFile, saveBuffer } from '../file-stroage' import { decryptBufferWithServerAction } from '@/components/drm/drmUtils' import { getVendorPricesForBidding } from './detail/service' import { getPrItemsForBidding } from './pre-quote/service' +import { checkChemicalSubstance, checkMultipleChemicalSubstances, type ChemicalSubstanceResult } from '@/lib/soap/ecc/send/chemical-substance-check' // 사용자 이메일로 사용자 코드 조회 @@ -3987,3 +3988,324 @@ export async function getBiddingSelectionItemsAndPrices(biddingId: number) { throw error } } + +// ======================================== +// 화학물질 조회 및 저장 관련 함수들 +// ======================================== + +/** + * 입찰 참여업체의 화학물질 정보를 조회하고 DB에 저장 + */ +// export async function checkAndSaveChemicalSubstanceForBiddingCompany(biddingCompanyId: number) { +// try { +// // 입찰 참여업체 정보 조회 (벤더 정보 포함) +// const biddingCompanyInfo = await db +// .select({ +// id: biddingCompanies.id, +// biddingId: biddingCompanies.biddingId, +// companyId: biddingCompanies.companyId, +// hasChemicalSubstance: biddingCompanies.hasChemicalSubstance, +// vendors: { +// vendorCode: vendors.vendorCode +// } +// }) +// .from(biddingCompanies) +// .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id)) +// .where(eq(biddingCompanies.id, biddingCompanyId)) +// .limit(1) + +// if (!biddingCompanyInfo[0]) { +// throw new Error(`입찰 참여업체를 찾을 수 없습니다: ${biddingCompanyId}`) +// } + +// const companyInfo = biddingCompanyInfo[0] + +// // 이미 화학물질 검사가 완료된 경우 스킵 +// if (companyInfo.hasChemicalSubstance !== null && companyInfo.hasChemicalSubstance !== undefined) { +// console.log(`이미 화학물질 검사가 완료된 입찰 참여업체: ${biddingCompanyId}`) +// return { +// success: true, +// message: '이미 화학물질 검사가 완료되었습니다.', +// hasChemicalSubstance: companyInfo.hasChemicalSubstance +// } +// } + +// // 벤더 코드가 없는 경우 스킵 +// if (!companyInfo.vendors?.vendorCode) { +// console.log(`벤더 코드가 없는 입찰 참여업체: ${biddingCompanyId}`) +// return { +// success: false, +// message: '벤더 코드가 없습니다.' +// } +// } + +// // 입찰의 PR 아이템들 조회 (자재번호 있는 것만) +// const prItems = await db +// .select({ +// id: prItemsForBidding.id, +// materialNumber: prItemsForBidding.materialNumber +// }) +// .from(prItemsForBidding) +// .where(and( +// eq(prItemsForBidding.biddingId, companyInfo.biddingId), +// isNotNull(prItemsForBidding.materialNumber), +// sql`${prItemsForBidding.materialNumber} != ''` +// )) + +// if (prItems.length === 0) { +// console.log(`자재번호가 있는 PR 아이템이 없는 입찰: ${companyInfo.biddingId}`) +// return { +// success: false, +// message: '조회할 자재가 없습니다.' +// } +// } + +// // 각 자재에 대해 화학물질 조회 +// let hasAnyChemicalSubstance = false +// const results: Array<{ materialNumber: string; hasChemicalSubstance: boolean; message: string }> = [] + +// for (const prItem of prItems) { +// try { +// const checkResult = await checkChemicalSubstance({ +// bukrs: 'H100', // 회사코드는 H100 고정 +// werks: 'PM11', // WERKS는 PM11 고정 +// lifnr: companyInfo.vendors.vendorCode, +// matnr: prItem.materialNumber! +// }) + +// if (checkResult.success) { +// const itemHasChemical = checkResult.hasChemicalSubstance || false +// hasAnyChemicalSubstance = hasAnyChemicalSubstance || itemHasChemical + +// results.push({ +// materialNumber: prItem.materialNumber!, +// hasChemicalSubstance: itemHasChemical, +// message: checkResult.message || '조회 성공' +// }) +// } else { +// results.push({ +// materialNumber: prItem.materialNumber!, +// hasChemicalSubstance: false, +// message: checkResult.message || '조회 실패' +// }) +// } + +// // API 호출 간 지연 +// await new Promise(resolve => setTimeout(resolve, 500)) + +// } catch (error) { +// results.push({ +// materialNumber: prItem.materialNumber!, +// hasChemicalSubstance: false, +// message: error instanceof Error ? error.message : 'Unknown error' +// }) +// } +// } + +// // 하나라도 Y(Y=true)이면 true, 모두 N(false)이면 false +// const finalHasChemicalSubstance = hasAnyChemicalSubstance + +// // DB에 결과 저장 +// await db +// .update(biddingCompanies) +// .set({ +// hasChemicalSubstance: finalHasChemicalSubstance, +// updatedAt: new Date() +// }) +// .where(eq(biddingCompanies.id, biddingCompanyId)) + +// console.log(`화학물질 정보 저장 완료: 입찰 참여업체 ${biddingCompanyId}, 화학물질 ${finalHasChemicalSubstance ? '있음' : '없음'} (${results.filter(r => r.hasChemicalSubstance).length}/${results.length})`) + +// return { +// success: true, +// message: `화학물질 조회 및 저장이 완료되었습니다. (${results.filter(r => r.hasChemicalSubstance).length}/${results.length}개 자재에 화학물질 있음)`, +// hasChemicalSubstance: finalHasChemicalSubstance, +// results +// } + +// } catch (error) { +// console.error(`화학물질 조회 실패 (입찰 참여업체 ${biddingCompanyId}):`, error) +// return { +// success: false, +// message: error instanceof Error ? error.message : 'Unknown error', +// hasChemicalSubstance: null, +// results: [] +// } +// } +// } + +/** + * 입찰의 모든 참여업체에 대한 화학물질 정보를 일괄 조회하고 저장 + */ +export async function checkAndSaveChemicalSubstancesForBidding(biddingId: number) { + try { + // 입찰의 모든 참여업체 조회 (벤더 코드 있는 것만) + const biddingCompaniesList = await db + .select({ + id: biddingCompanies.id, + companyId: biddingCompanies.companyId, + hasChemicalSubstance: biddingCompanies.hasChemicalSubstance, + vendors: { + vendorCode: vendors.vendorCode, + vendorName: vendors.vendorName + } + }) + .from(biddingCompanies) + .leftJoin(vendors, eq(biddingCompanies.companyId, vendors.id)) + .where(and( + eq(biddingCompanies.biddingId, biddingId), + isNotNull(vendors.vendorCode), + sql`${vendors.vendorCode} != ''` + )) + + if (biddingCompaniesList.length === 0) { + return { + success: true, + message: '벤더 코드가 있는 참여업체가 없습니다.', + results: [] + } + } + + // 입찰의 PR 아이템들 조회 (자재번호 있는 것만) + const prItems = await db + .select({ + materialNumber: prItemsForBidding.materialNumber + }) + .from(prItemsForBidding) + .where(and( + eq(prItemsForBidding.biddingId, biddingId), + isNotNull(prItemsForBidding.materialNumber), + sql`${prItemsForBidding.materialNumber} != ''` + )) + + if (prItems.length === 0) { + return { + success: false, + message: '조회할 자재가 없습니다.', + results: [] + } + } + + const materialNumbers = prItems.map(item => item.materialNumber!).filter(Boolean) + + // 각 참여업체에 대해 화학물질 조회 + const results: Array<{ + biddingCompanyId: number; + vendorCode: string; + vendorName: string; + success: boolean; + hasChemicalSubstance?: boolean; + message: string; + materialResults?: Array<{ materialNumber: string; hasChemicalSubstance: boolean; message: string }>; + }> = [] + + for (const biddingCompany of biddingCompaniesList) { + try { + // 이미 검사가 완료된 경우 스킵 + if (biddingCompany.hasChemicalSubstance !== null && biddingCompany.hasChemicalSubstance !== undefined) { + results.push({ + biddingCompanyId: biddingCompany.id, + vendorCode: biddingCompany.vendors!.vendorCode!, + vendorName: biddingCompany.vendors!.vendorName || '', + success: true, + hasChemicalSubstance: biddingCompany.hasChemicalSubstance, + message: '이미 검사가 완료되었습니다.' + }) + continue + } + + // 각 자재에 대해 화학물질 조회 + let hasAnyChemicalSubstance = false + const materialResults: Array<{ materialNumber: string; hasChemicalSubstance: boolean; message: string }> = [] + + for (const materialNumber of materialNumbers) { + try { + const checkResult = await checkChemicalSubstance({ + bukrs: 'H100', // 회사코드는 H100 고정 + werks: 'PM11', // WERKS는 PM11 고정 + lifnr: biddingCompany.vendors!.vendorCode!, + matnr: materialNumber + }) + + if (checkResult.success) { + const itemHasChemical = checkResult.hasChemicalSubstance || false + hasAnyChemicalSubstance = hasAnyChemicalSubstance || itemHasChemical + + materialResults.push({ + materialNumber, + hasChemicalSubstance: itemHasChemical, + message: checkResult.message || '조회 성공' + }) + } else { + materialResults.push({ + materialNumber, + hasChemicalSubstance: false, + message: checkResult.message || '조회 실패' + }) + } + + // API 호출 간 지연 + await new Promise(resolve => setTimeout(resolve, 500)) + + } catch (error) { + materialResults.push({ + materialNumber, + hasChemicalSubstance: false, + message: error instanceof Error ? error.message : 'Unknown error' + }) + } + } + + // 하나라도 Y이면 true, 모두 N이면 false + const finalHasChemicalSubstance = hasAnyChemicalSubstance + + // DB에 결과 저장 + await db + .update(biddingCompanies) + .set({ + hasChemicalSubstance: finalHasChemicalSubstance, + updatedAt: new Date() + }) + .where(eq(biddingCompanies.id, biddingCompany.id)) + + results.push({ + biddingCompanyId: biddingCompany.id, + vendorCode: biddingCompany.vendors!.vendorCode!, + vendorName: biddingCompany.vendors!.vendorName || '', + success: true, + hasChemicalSubstance: finalHasChemicalSubstance, + message: `조회 완료 (${materialResults.filter(r => r.hasChemicalSubstance).length}/${materialResults.length}개 자재에 화학물질 있음)`, + materialResults + }) + + } catch (error) { + results.push({ + biddingCompanyId: biddingCompany.id, + vendorCode: biddingCompany.vendors!.vendorCode!, + vendorName: biddingCompany.vendors!.vendorName || '', + success: false, + message: error instanceof Error ? error.message : 'Unknown error' + }) + } + } + + const successCount = results.filter(r => r.success).length + const totalCount = results.length + + console.log(`입찰 ${biddingId} 화학물질 일괄 조회 완료: ${successCount}/${totalCount} 성공`) + + return { + success: true, + message: `화학물질 일괄 조회 완료: ${successCount}/${totalCount} 성공`, + results + } + + } catch (error) { + console.error(`입찰 화학물질 일괄 조회 실패 (${biddingId}):`, error) + return { + success: false, + message: error instanceof Error ? error.message : 'Unknown error', + results: [] + } + } +} |
