/**
* 일반계약 결재 템플릿 변수 매핑 함수
*
* 제공된 HTML 템플릿의 변수명에 맞춰 매핑
*/
'use server';
import { format } from 'date-fns';
interface ContractSummary {
basicInfo: Record;
items: Record[];
subcontractChecklist: Record | null;
storageInfo?: Record[];
}
/**
* 일반계약 데이터를 결재 템플릿 변수로 매핑
*
* @param contractSummary - 계약 요약 정보
* @returns 템플릿 변수 객체 (Record)
*/
export async function mapContractToApprovalTemplateVariables(
contractSummary: ContractSummary
): Promise> {
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(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;
}