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 `
| ${esc(labels[key] || EstModel.labels[key] || key)} | `).join("")}
|---|
| ${esc(typeof row[key] === "number" ? number(row[key]) : key === "LineData" ? EstText.clean(row[key] || "") : (row[key] ?? ""))} | `).join("")}