// EST Plus 2 REA 최종보고서 §6-11, §6-12의 계산 경로. const EstWorkflows = (() => { function escalation(bid, contract, current, quantity) { if (!bid || !current) return { rate: 0, difference: 0, price: contract, amount: Math.trunc(contract * quantity) }; const rate = Math.trunc(((current - bid) / bid) * 10000) / 100; let difference = 0; if (bid >= contract && current >= bid) difference = Math.trunc(contract * rate / 100); else if (bid < contract && current >= contract) difference = Math.trunc(current - contract); const price = contract + difference; return { rate, difference, price, amount: Math.trunc(price * quantity) }; } function excluded(planned, actual, progress) { return Math.max(planned, actual, progress); } function resources(data) { const sources = Object.fromEntries(data.sources.map(s => [s.id,s])); const units = Object.fromEntries(data.units.map(u => [u.id,u])); const quantities = Object.create(null), errors = []; function expand(unitId, multiplier, path) { const unit = Object.hasOwn(units, unitId) ? units[unitId] : null; if (!unit) return; if (path.includes(unitId)) { errors.push(`순환 참조: ${unit.name}`); return; } for (const line of unit.lines) { if (line.calcIndex === 2 || line.calcIndex === 3 || line.type === '비율') continue; const quantity = multiplier * line.quantity; if (line.type === '일위대가') expand(line.unitId,quantity,[...path,unitId]); else if (Object.hasOwn(sources,line.sourceId)) quantities[line.sourceId] = (quantities[line.sourceId] || 0) + quantity; } } for (const row of data.estimate) expand(row.unitId,row.quantity,[]); return { rows: Object.entries(quantities).map(([id,quantity]) => ({source:sources[id],quantity})), errors }; } return { escalation, excluded, resources }; })(); if (typeof module !== 'undefined') module.exports = EstWorkflows;