summaryrefslogtreecommitdiff
path: root/lib/bidding
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-12-05 09:18:28 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-12-05 09:18:28 +0000
commite467b3b7905a200b98daa3787565c08a309a6dda (patch)
tree9ab8d1e7cd2bda128d7acb75fdb7811a9ef1cee9 /lib/bidding
parent5233996e9bd676ce763a12e58c7955c7ca8f26c5 (diff)
(최겸) 계약 승인 요청 시 결재 상신 개발, 입찰 화학물질 soap 개발
Diffstat (limited to 'lib/bidding')
-rw-r--r--lib/bidding/actions.ts22
-rw-r--r--lib/bidding/detail/table/price-adjustment-dialog.tsx10
-rw-r--r--lib/bidding/service.ts322
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: []
+ }
+ }
+}