diff options
Diffstat (limited to 'lib/general-contracts/approval-template-variables.ts')
| -rw-r--r-- | lib/general-contracts/approval-template-variables.ts | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/lib/general-contracts/approval-template-variables.ts b/lib/general-contracts/approval-template-variables.ts new file mode 100644 index 00000000..6924694e --- /dev/null +++ b/lib/general-contracts/approval-template-variables.ts @@ -0,0 +1,369 @@ +/** + * 일반계약 결재 템플릿 변수 매핑 함수 + * + * 제공된 HTML 템플릿의 변수명에 맞춰 매핑 + */ + +'use server'; + +import { format } from 'date-fns'; + +interface ContractSummary { + basicInfo: Record<string, unknown>; + items: Record<string, unknown>[]; + subcontractChecklist: Record<string, unknown> | null; + storageInfo?: Record<string, unknown>[]; +} + +/** + * 일반계약 데이터를 결재 템플릿 변수로 매핑 + * + * @param contractSummary - 계약 요약 정보 + * @returns 템플릿 변수 객체 (Record<string, string>) + */ +export async function mapContractToApprovalTemplateVariables( + contractSummary: ContractSummary +): Promise<Record<string, string>> { + const { basicInfo, items, subcontractChecklist } = contractSummary; + + // 날짜 포맷팅 헬퍼 + const formatDate = (date: any) => { + if (!date) return ''; + try { + const d = new Date(date); + if (isNaN(d.getTime())) return String(date); + return format(d, 'yyyy-MM-dd'); + } catch { + return String(date || ''); + } + }; + + // 금액 포맷팅 헬퍼 + const formatCurrency = (amount: any) => { + if (amount === undefined || amount === null || amount === '') return ''; + const num = Number(amount); + if (isNaN(num)) return String(amount); + return num.toLocaleString('ko-KR'); + }; + + // 계약기간 포맷팅 + const contractPeriod = basicInfo.startDate && basicInfo.endDate + ? `${formatDate(basicInfo.startDate)} ~ ${formatDate(basicInfo.endDate)}` + : ''; + + // 계약체결방식 + const contractExecutionMethod = basicInfo.executionMethod || ''; + + // 계약종류 + const contractType = basicInfo.type || ''; + + // 업체선정방식 + const vendorSelectionMethod = basicInfo.contractSourceType || ''; + + // 매입 부가가치세 + const taxType = basicInfo.taxType || ''; + + // SHI 지급조건 + const paymentTerm = basicInfo.paymentTerm || ''; + + // SHI 인도조건 + const deliveryTerm = basicInfo.deliveryTerm || ''; + const deliveryType = basicInfo.deliveryType || ''; + + // 사외업체 야드 투입 여부 + const externalYardEntry = basicInfo.externalYardEntry === 'Y' ? '예' : '아니오'; + + // 직종 + const workType = basicInfo.workType || ''; + + // 재하도 협력사 + const subcontractVendor = basicInfo.subcontractVendorName || ''; + + // 계약 내용 + const contractContent = basicInfo.notes || basicInfo.name || ''; + + // 계약성립조건 + let establishmentConditionsText = ''; + if (basicInfo.contractEstablishmentConditions) { + try { + const cond = typeof basicInfo.contractEstablishmentConditions === 'string' + ? JSON.parse(basicInfo.contractEstablishmentConditions) + : basicInfo.contractEstablishmentConditions; + + const active: string[] = []; + if (cond.regularVendorRegistration) active.push('정규업체 등록(실사 포함) 시'); + if (cond.projectAward) active.push('프로젝트 수주 시'); + if (cond.ownerApproval) active.push('선주 승인 시'); + if (cond.other) active.push('기타'); + establishmentConditionsText = active.join(', '); + } catch (e) { + console.warn('계약성립조건 파싱 실패:', e); + } + } + + // 계약해지조건 + let terminationConditionsText = ''; + if (basicInfo.contractTerminationConditions) { + try { + const cond = typeof basicInfo.contractTerminationConditions === 'string' + ? JSON.parse(basicInfo.contractTerminationConditions) + : basicInfo.contractTerminationConditions; + + const active: string[] = []; + if (cond.standardTermination) active.push('표준 계약해지조건'); + if (cond.projectNotAwarded) active.push('프로젝트 미수주 시'); + if (cond.other) active.push('기타'); + terminationConditionsText = active.join(', '); + } catch (e) { + console.warn('계약해지조건 파싱 실패:', e); + } + } + + // 협력사 정보 + const vendorCode = basicInfo.vendorCode || ''; + const vendorName = basicInfo.vendorName || ''; + const vendorContactPerson = basicInfo.vendorContactPerson || ''; + const vendorPhone = basicInfo.vendorPhone || ''; + const vendorEmail = basicInfo.vendorEmail || ''; + const vendorNote = ''; + + // 자재 정보 (최대 100건) + const materialItems = items.slice(0, 100); + const materialCount = items.length; + + // 보증 정보 + const guarantees: Array<{ + type: string; + order: number; + bondNumber: string; + rate: string; + amount: string; + period: string; + startDate: string; + endDate: string; + issuer: string; + }> = []; + + // 계약보증 + if (basicInfo.contractBond) { + const bond = typeof basicInfo.contractBond === 'string' + ? JSON.parse(basicInfo.contractBond) + : basicInfo.contractBond; + + if (bond && Array.isArray(bond)) { + bond.forEach((b: any, idx: number) => { + guarantees.push({ + type: '계약보증', + order: idx + 1, + bondNumber: b.bondNumber || '', + rate: b.rate ? `${b.rate}%` : '', + amount: formatCurrency(b.amount), + period: b.period || '', + startDate: formatDate(b.startDate), + endDate: formatDate(b.endDate), + issuer: b.issuer || '', + }); + }); + } + } + + // 지급보증 + if (basicInfo.paymentBond) { + const bond = typeof basicInfo.paymentBond === 'string' + ? JSON.parse(basicInfo.paymentBond) + : basicInfo.paymentBond; + + if (bond && Array.isArray(bond)) { + bond.forEach((b: any, idx: number) => { + guarantees.push({ + type: '지급보증', + order: idx + 1, + bondNumber: b.bondNumber || '', + rate: b.rate ? `${b.rate}%` : '', + amount: formatCurrency(b.amount), + period: b.period || '', + startDate: formatDate(b.startDate), + endDate: formatDate(b.endDate), + issuer: b.issuer || '', + }); + }); + } + } + + // 하자보증 + if (basicInfo.defectBond) { + const bond = typeof basicInfo.defectBond === 'string' + ? JSON.parse(basicInfo.defectBond) + : basicInfo.defectBond; + + if (bond && Array.isArray(bond)) { + bond.forEach((b: any, idx: number) => { + guarantees.push({ + type: '하자보증', + order: idx + 1, + bondNumber: b.bondNumber || '', + rate: b.rate ? `${b.rate}%` : '', + amount: formatCurrency(b.amount), + period: b.period || '', + startDate: formatDate(b.startDate), + endDate: formatDate(b.endDate), + issuer: b.issuer || '', + }); + }); + } + } + + // 보증 전체 비고 + const guaranteeNote = basicInfo.guaranteeNote || ''; + + // 하도급 체크리스트 + const checklistItems: Array<{ + category: string; + item1: string; + item2: string; + result: string; + department: string; + cause: string; + measure: string; + }> = []; + + if (subcontractChecklist) { + // 1-1. 작업 시 서면 발급 + checklistItems.push({ + category: '계약 시 [계약 체결 단계]', + item1: '1-1. 작업 시 서면 발급', + item2: '-', + result: subcontractChecklist.workDocumentIssued === '준수' ? '준수' : + subcontractChecklist.workDocumentIssued === '위반' ? '위반' : + subcontractChecklist.workDocumentIssued === '위반의심' ? '위반의심' : '', + department: subcontractChecklist.workDocumentIssuedDepartment || '', + cause: subcontractChecklist.workDocumentIssuedCause || '', + measure: subcontractChecklist.workDocumentIssuedMeasure || '', + }); + + // 1-2. 6대 법정 기재사항 명기 여부 + checklistItems.push({ + category: '계약 시 [계약 체결 단계]', + item1: '1-2. 6대 법정 기재사항 명기 여부', + item2: '-', + result: subcontractChecklist.sixLegalItems === '준수' ? '준수' : + subcontractChecklist.sixLegalItems === '위반' ? '위반' : + subcontractChecklist.sixLegalItems === '위반의심' ? '위반의심' : '', + department: subcontractChecklist.sixLegalItemsDepartment || '', + cause: subcontractChecklist.sixLegalItemsCause || '', + measure: subcontractChecklist.sixLegalItemsMeasure || '', + }); + + // 2. 부당 하도급 대금 결정 행위 + checklistItems.push({ + category: '계약 시 [계약 체결 단계]', + item1: '-', + item2: '2. 부당 하도급 대금 결정 행위 (대금 결정 방법)', + result: subcontractChecklist.unfairSubcontractPrice === '준수' ? '준수' : + subcontractChecklist.unfairSubcontractPrice === '위반' ? '위반' : + subcontractChecklist.unfairSubcontractPrice === '위반의심' ? '위반의심' : '', + department: subcontractChecklist.unfairSubcontractPriceDepartment || '', + cause: subcontractChecklist.unfairSubcontractPriceCause || '', + measure: subcontractChecklist.unfairSubcontractPriceMeasure || '', + }); + } + + // 총 계약 금액 계산 + const totalContractAmount = items.reduce((sum, item) => { + const amount = Number(item.contractAmount || item.totalLineAmount || 0); + return sum + (isNaN(amount) ? 0 : amount); + }, 0); + + // 변수 매핑 + const variables: Record<string, string> = { + // 계약 기본 정보 + '계약번호': String(basicInfo.contractNumber || ''), + '계약명': String(basicInfo.name || basicInfo.contractName || ''), + '계약체결방식': String(contractExecutionMethod), + '계약종류': String(contractType), + '구매담당자': String(basicInfo.managerName || basicInfo.registeredByName || ''), + '업체선정방식': String(vendorSelectionMethod), + '입찰번호': String(basicInfo.linkedBidNumber || ''), + '입찰명': String(basicInfo.linkedBidName || ''), + '계약기간': contractPeriod, + '계약일자': formatDate(basicInfo.registeredAt || basicInfo.createdAt), + '매입_부가가치세': String(taxType), + '계약_담당자': String(basicInfo.managerName || basicInfo.registeredByName || ''), + '계약부서': String(basicInfo.departmentName || ''), + '계약금액': formatCurrency(basicInfo.contractAmount), + 'SHI_지급조건': String(paymentTerm), + 'SHI_인도조건': String(deliveryTerm), + 'SHI_인도조건_옵션': String(deliveryType), + '선적지': String(basicInfo.shippingLocation || ''), + '하역지': String(basicInfo.dischargeLocation || ''), + '사외업체_야드_투입여부': externalYardEntry, + '프로젝트': String(basicInfo.projectName || basicInfo.projectCode || ''), + '직종': String(workType), + '재하도_협력사': String(subcontractVendor), + '계약내용': String(contractContent), + '계약성립조건': establishmentConditionsText, + '계약해지조건': terminationConditionsText, + + // 협력사 정보 + '협력사코드': String(vendorCode), + '협력사명': String(vendorName), + '협력사_담당자': String(vendorContactPerson), + '전화번호': String(vendorPhone), + '이메일': String(vendorEmail), + '비고': String(vendorNote), + + // 자재 정보 + '대상_자재_수': String(materialCount), + }; + + // 자재 정보 변수 (최대 100건) + materialItems.forEach((item, index) => { + const idx = index + 1; + variables[`플랜트_${idx}`] = String(item.plant || ''); + variables[`프로젝트_${idx}`] = String(item.projectName || item.projectCode || ''); + variables[`자재그룹_${idx}`] = String(item.itemGroup || item.itemCode || ''); + variables[`자재그룹명_${idx}`] = String(item.itemGroupName || ''); + variables[`자재번호_${idx}`] = String(item.itemCode || ''); + variables[`자재상세_${idx}`] = String(item.itemInfo || item.description || ''); + variables[`연간단가여부_${idx}`] = String(item.isAnnualPrice ? '예' : '아니오'); + variables[`수량_${idx}`] = formatCurrency(item.quantity); + variables[`구매단위_${idx}`] = String(item.quantityUnit || ''); + variables[`계약단가_${idx}`] = formatCurrency(item.contractUnitPrice || item.unitPrice); + variables[`수량단위_${idx}`] = String(item.quantityUnit || ''); + variables[`총중량_${idx}`] = formatCurrency(item.totalWeight); + variables[`중량단위_${idx}`] = String(item.weightUnit || ''); + variables[`계약금액_${idx}`] = formatCurrency(item.contractAmount || item.totalLineAmount); + }); + + // 총 계약 금액 + variables['총_계약금액'] = formatCurrency(totalContractAmount); + + // 보증 정보 변수 + guarantees.forEach((guarantee, index) => { + const idx = index + 1; + const typeKey = String(guarantee.type); + variables[`${typeKey}_차수_${idx}`] = String(guarantee.order); + variables[`${typeKey}_증권번호_${idx}`] = String(guarantee.bondNumber || ''); + variables[`${typeKey}_보증금율_${idx}`] = String(guarantee.rate || ''); + variables[`${typeKey}_보증금액_${idx}`] = String(guarantee.amount || ''); + variables[`${typeKey}_보증기간_${idx}`] = String(guarantee.period || ''); + variables[`${typeKey}_시작일_${idx}`] = String(guarantee.startDate || ''); + variables[`${typeKey}_종료일_${idx}`] = String(guarantee.endDate || ''); + variables[`${typeKey}_발행기관_${idx}`] = String(guarantee.issuer || ''); + }); + + // 보증 전체 비고 + variables['보증_전체_비고'] = String(guaranteeNote); + + // 하도급 체크리스트 변수 + checklistItems.forEach((item, index) => { + const idx = index + 1; + variables[`점검결과_${idx === 1 ? '1_1' : idx === 2 ? '1_2' : '2'}`] = String(item.result); + variables[`귀책부서_${idx === 1 ? '1_1' : idx === 2 ? '1_2' : '2'}`] = String(item.department); + variables[`원인_${idx === 1 ? '1_1' : idx === 2 ? '1_2' : '2'}`] = String(item.cause); + variables[`대책_${idx === 1 ? '1_1' : idx === 2 ? '1_2' : '2'}`] = String(item.measure); + }); + + return variables; +} + |
