446 lines
15 KiB
JavaScript
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("&", "&")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">"),
|
|
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 };
|
|
})();
|