Files

34 lines
1.9 KiB
JavaScript

// 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;