Files

446 lines
15 KiB
JavaScript

const EstForms = (() => {
const common = ["Code", "Name", "Size", "Unite"];
function data(id, project, state = {}) {
if (id === "standard-change")
return {
columns: ["status", "Code", "Name", "originalGN", "GN", "originalTC", "TC", "difference"],
rows: EstStandard.changes(project),
};
if (id === "estimate-change")
return {
columns: ["status", "Code", "Name", "originalGN", "GN", "originalTC", "TC", "difference"],
rows: EstEstimate.changes(project),
};
if (id === "material-change" || id === "labor-change") {
const kindKey = id.startsWith("material") ? "material" : "labor";
return {
columns: ["status", "Code", "Name", "originalGN", "GN", "originalTC", "TC", "difference"],
rows: EstBasicPrices.changes(project, kindKey),
};
}
if (id === "standard-usage")
return {
columns: [
"Code",
"Name",
"Size",
"Unite",
"GN",
"appliedPrice",
"sourcePrice",
"difference",
"TC",
"status",
],
rows: EstStandard.usage(project),
};
if (id === "estimate-usage")
return {
columns: [
"Code",
"Name",
"Size",
"Unite",
"GN",
"appliedPrice",
"sourcePrice",
"difference",
"TC",
"status",
],
rows: EstEstimate.usage(project),
};
if (id === "material-usage" || id === "labor-usage") {
const kindKey = id.startsWith("material") ? "material" : "labor";
return {
columns: [
"Code",
"Name",
"Size",
"Unite",
"GN",
"appliedPrice",
"sourcePrice",
"difference",
"TC",
"status",
],
rows: EstBasicPrices.usage(project, kindKey),
};
}
if (id === "machine-usage")
return {
columns: [
"Code",
"Name",
"Size",
"Unite",
"appliedPrice",
"originalPrice",
"difference",
"NC",
"GC",
"JC",
],
rows: EstMachine.usage(project),
};
if (id === "machine-change")
return {
columns: ["status", "Code", "Name", "Size", "Unite", "originalTC", "TC", "difference"],
rows: EstMachine.changes(project),
};
if (id === "change-costs")
return {
columns: ["Gubun", "Name", "originalCost", "changedCost", "difference"],
rows: EstBusiness.costChanges(project),
};
if (["annual-summary", "annual-items"].includes(id)) {
const output = EstBusiness.annual(project, false);
if (id === "annual-items")
return {
columns: ["stage", "Code", "Name", "Size", "Unite", "GN", "NC", "GC", "JC", "SC", "TC"],
rows: output.results.flatMap((item) =>
item.project.tables.EstMainDat.filter((row) => row.Code || row.Name).map((row) => ({
...row,
stage: item.name,
})),
),
errors: output.errors,
};
return {
columns: ["Name", "formula", "directCost", "contractCost"],
rows: project.annual.map((year, index) => ({
Name: year.name,
formula: year.formula,
directCost: output.results[index]?.project.calculation.direct.TC,
contractCost: output.results[index]?.project.calculation.total,
})),
errors: output.errors,
};
}
if (["progress-summary", "progress-costs"].includes(id)) {
const round =
project.progress.find((item) => item.id === state.roundId) || project.progress.at(-1);
if (!round) return { columns: ["Name"], rows: [], errors: ["기성 회차가 없습니다."] };
const result =
round.locked && round.result ? round.result : EstProgress.calculate(project, round.id);
if (id === "progress-costs")
return {
columns: ["Gubun", "Name", "ContractCost", "JunCost", "GumCost", "SaCost", "TotalCost"],
rows: result.costs || [],
errors: result.errors,
detail: `${round.name || "기성"} (${round.locked ? "확정" : "작성 중"})`,
};
return {
columns: [
"Code",
"Name",
"Unite",
"GN",
"TC",
"JunGN",
"JunTC",
"GumGN",
"GumTC",
"SaGN",
"SaTC",
"TotalGN",
"TotalTC",
],
rows: result.rows,
errors: result.errors,
detail: `${round.name || "기성"} (${round.locked ? "확정" : "작성 중"})`,
};
}
if (["escalation-summary", "escalation-costs"].includes(id)) {
const result = EstEscalation.calculate(project);
if (id === "escalation-costs")
return {
columns: ["Gubun", "Name", "ContractCost", "adjustedCost", "excludedCost"],
rows: result.wonga.map((row, index) => ({
...row,
ContractCost: result.contractWonga[index]?.Cost,
adjustedCost: row.Cost,
excludedCost: result.excludedWonga[index]?.Cost,
})),
errors: result.errors,
detail: `도급액 ${result.contractTotal} · 조정액 ${result.adjustment} · 조정률 ${result.rate}%`,
};
return {
columns: [
"Code",
"Name",
"Unite",
"GN",
"EscExcluded",
"EscApplied",
"TC",
"ENC",
"EGC",
"EJC",
"ESC",
],
rows: result.rows,
errors: result.errors,
detail: `도급액 ${result.contractTotal} · 조정액 ${result.adjustment} · 조정률 ${result.rate}%`,
};
}
if (id === 171) {
const output = EstReview.calculate(project);
const rows = output.baseline.tables.EstMainDat.map((row, index) => ({
row,
index,
result: output.reviewed.tables.EstMainDat[index],
}))
.filter(({ row }) => row.Code || row.Name)
.map(({ row, index, result }) => ({
Code: row.Code,
Name: row.Name,
originalGN: row.GN,
GN: result.GN,
originalTC: row.TC,
TC: result.TC,
difference: EstMath.num(result.TC) - EstMath.num(row.TC),
Reason: project.contractReview?.edits?.[EstReview.rowKey(row, index)]?.Reason || "",
}));
rows.unshift({
Name: "심사 도급액",
originalTC: output.before,
TC: output.after,
difference: output.after - output.before,
});
return {
columns: ["Code", "Name", "originalGN", "GN", "originalTC", "TC", "difference", "Reason"],
rows,
errors: output.errors,
};
}
if (id === 109)
return {
columns: ["Code", "Name", "start", "end", "weight", "progress"],
rows: EstSchedule.calculate(project).rows,
};
const table = {
1: "EstMainDat",
117: "EstMainDat",
2: "HeadMainDat",
115: "HeadMainDat",
3: "CostMainDat",
4: "MachMainDat",
119: "MachGijunDat",
101: "EstiMateDat",
21: "SilJukDat",
5: "StuffcDat",
6: "LaborcDat",
7: "Wonga",
22: "Wonga",
}[id];
if (table) {
let columns;
if (table === "Wonga") {
columns = ["Gubun", "Name", "Youl", "VSusic", "Cost"];
} else if (table === "CostMainDat") {
columns = ["Code", "LineData"];
} else if (table === "HeadMainDat") {
const gongMethod = Number(project.printSettings?.PrintGongMethod ?? 0);
if (id === 115 && gongMethod === 0) {
// 공람용: 수량만 인쇄
columns = [...common, "GN"];
} else if (id === 115 && gongMethod === 1) {
// 공람용: 단가만 인쇄
columns = [...common, "NU", "GU", "JU", "SU", "TC"];
} else {
// 기본(수량, 단가 인쇄)
columns = [...common, "GN", "NU", "GU", "JU", "SU", "TC"];
}
} else {
const gongMethod = Number(project.printSettings?.PrintGongMethod ?? 0);
if (id === 117 && gongMethod === 0) {
// 공람용: 수량만 인쇄
columns = [...common, "GN"];
} else if (id === 117 && gongMethod === 1) {
// 공람용: 단가/금액만 인쇄
columns = [...common, "TU", "TC", "NC", "GC", "JC", "SC"];
} else {
// 기본(수량, 단가/금액 인쇄)
columns = [...common, "GN", "TU", "TC", "NC", "GC", "JC", "SC"];
}
}
return { table, columns, rows: project.tables[table] || [] };
}
const totals = {
106: "EstMainDat",
118: "EstMainDat",
103: "HeadMainDat",
116: "HeadMainDat",
104: "CostMainDat",
105: "MachMainDat",
}[id];
if (totals) {
const rows =
totals === "EstMainDat"
? (project.tables[totals] || []).filter((r) => Number(r.Level) > 0)
: [...EstNative.groups(project.tables[totals] || []).values()].map((group) => {
const row = { ...group.rows[0] };
const titleTable = {
HeadMainDat: "HeadTitleDat",
CostMainDat: "CostTitleDat",
MachMainDat: "MachTitleDat",
}[totals];
const title = project.tables[titleTable]?.find((item) => item.Code === row.Code);
if (title) return { ...row, ...title };
for (const key of [...EstMath.components, "TC"])
row[key] = group.rows
.slice(1)
.filter((r) => Number(r.CalcIndex || 0) < 2)
.reduce((n, r) => n + EstMath.num(r[key]), 0);
return row;
});
const gongMethod = Number(project.printSettings?.PrintGongMethod ?? 0);
let columns;
if ([116, 118].includes(id) && gongMethod === 0) {
// 공람용 총괄표: 금액 숨김
columns = [...common];
} else {
columns = [...common, "NC", "GC", "JC", "SC", "TC"];
}
return { table: totals, columns, rows };
}
if ([107, 108].includes(id))
return {
columns: ["Code", "Name", "originalGN", "GN", "originalTC", "TC", "difference", "status"],
rows: EstBusiness.changes(project),
};
const kinds = {
13: 6,
110: 5,
111: 105,
161: 21,
162: 101,
112: 6,
113: 5,
114: 105,
163: 21,
164: 101,
};
if (kinds[id]) {
const output = EstResources.calculate(project, [112, 113, 114, 163, 164].includes(id));
return {
columns: [
...([112, 113, 114, 163, 164].includes(id) ? ["WorkCode", "WorkName"] : []),
"Code",
"Name",
"Size",
"Unite",
"quantity",
"amount",
],
errors: output.errors,
rows: output.rows.filter((r) => r.kind === kinds[id]),
};
}
if (id === 14) {
const target = (item) => {
if (!item.TargetKey) return item.TargetCode ?? item.LinkCode ?? "";
const row = project.tables.EstMainDat.find(
(entry) => entry.PkID != null && `id:${entry.PkID}` === item.TargetKey,
);
return row ? `${row.Name || "내역"} (${row.Code || item.TargetKey})` : item.TargetKey;
};
const manual = EstQuantity.manual(project.quantities || []),
rows = manual.rows.map((row) => ({
...row,
SheetName: "수식표",
formula: row.formula || "",
TargetCode: target(row),
})),
errors = [...manual.errors];
for (const sheet of project.quantitySheets || []) {
const calculated = EstQuantity.calculate(sheet);
errors.push(...calculated.errors.map((error) => `${sheet.Name || sheet.Code}: ${error}`));
for (const item of calculated.items) {
const index = sheet.rows.findIndex((row) => (row.markers || []).includes(item.Code));
rows.push({
...item,
SheetName: sheet.Name || sheet.Code,
formula: index < 0 ? "" : EstText.clean(calculated.rows[index].LineData),
TargetCode: target(item),
});
}
}
return {
columns: ["SheetName", "Code", "Name", "formula", "GN", "TargetCode", "Error"],
rows,
errors,
};
}
return null;
}
const labels = {
originalGN: "당초 수량",
originalTC: "당초 금액",
difference: "증감",
status: "상태",
quantity: "소요량/시간",
amount: "금액",
start: "시작일",
end: "종료일",
weight: "가중치",
progress: "진도(%)",
WorkCode: "공종 코드",
WorkName: "공종명",
formula: "산식",
SheetName: "산출서",
Error: "계산 오류",
Reason: "심사 사유",
JunGN: "전회 수량",
JunTC: "전회 금액",
GumGN: "금회 수량",
GumTC: "금회 금액",
SaGN: "사정 수량",
SaTC: "사정 금액",
TotalGN: "누계 수량",
TotalTC: "누계 금액",
EscExcluded: "제외 수량",
EscApplied: "적용 수량",
ENC: "노무비 증감",
EGC: "재료비 증감",
EJC: "경비 증감",
ESC: "산출경비 증감",
directCost: "순공사비",
contractCost: "도급액",
ContractCost: "도급금액",
JunCost: "전회 금액",
GumCost: "금회 금액",
SaCost: "사정 금액",
TotalCost: "누계 금액",
adjustedCost: "조정 금액",
excludedCost: "제외 금액",
originalCost: "당초 금액",
changedCost: "변경 금액",
stage: "차수",
appliedPrice: "내역 단가",
sourcePrice: "기초 단가",
TargetCode: "연결 내역",
TU: "단가",
TC: "금액",
NC: "노무비",
GC: "재료비",
JC: "경비",
SC: "산출경비",
};
const label = (key) => labels[key] || EstModel.labels[key] || key;
function grid(output) {
const esc = (v) =>
typeof EstApp !== "undefined"
? EstApp.esc(v)
: String(v ?? "")
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;"),
number = (v) => new Intl.NumberFormat("ko-KR", { maximumFractionDigits: 12 }).format(v);
return `<table><thead><tr>${output.columns.map((key) => `<th>${esc(labels[key] || EstModel.labels[key] || key)}</th>`).join("")}</tr></thead><tbody>${output.rows.map((row) => `<tr>${output.columns.map((key) => `<td class="${typeof row[key] === "number" ? "numeric" : ""}">${esc(typeof row[key] === "number" ? number(row[key]) : key === "LineData" ? EstText.clean(row[key] || "") : (row[key] ?? ""))}</td>`).join("")}</tr>`).join("")}</tbody></table>`;
}
return { data, grid, label };
})();