Files
Aislo/tools/stmate-test/view_kanjub_prt.js
T

648 lines
36 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* STMATE w 8.0 — 제잡비 계산(wM_KanJub) · 원가계산기준 제비율표(wM_KAN_RATE) · 계약/실행/기성(wM_Mk_Cont · wM_KanJub_K) · 보고서 인쇄/변환(PM_Prt 30종)
* 근거: 30_원자료/TWM_KANJUB.json, TWM_KAN_RATE.json, 22번 §5~6, 27번 계약·실행·기성, 28번 XML/JDL, 34번 30종 출력 시트
*/
(function (global) {
"use strict";
const Calc = global.STMATE_CALC;
const fmt = Calc.fmt;
const num = Calc.num;
/**
* 1. TWM_KANJUB (wM_KanJub 제잡비 계산 및 원가계산서 화면)
* - 상단 바: 설계차수(원설계/1~3차변경), 방식(원가계산/표준시장), 간접비 절사단위, 기준선택(JB_R), 요율수정, 결과보기
* - 중단 Pn0_Input: 16개 제잡비 설정 콤보/입력칸
* - 하단 Pg_Ctrl 4탭: 일반형식(T) | 수공형식(S) | 실적일반(TA) | 실적수공(SA) 원가계산서 그리드
*/
function renderWMKanJub(ctx) {
const kc = ctx.state.kanjubConfig;
const kj = ctx.computed.kanjubComputed;
const smt = (ctx.dbfData && ctx.dbfData.prjlst_smt) || {};
const jbrFiles = (ctx.sourcesData && ctx.sourcesData.jbr_files) || [];
return `
<div class="win-form">
<div class="win-form-header">
<span>🏛️ 제잡비 계산 및 원가계산서 [TWM_KANJUB · PRJLST.SMT ${Object.keys(smt).length}개 키 연동]</span>
<span>직접공사비: ${fmt(kj.DIRECT_COST)}원 → 도급액: ${fmt(kj.FINAL_SUPPLY)}원 + 관급자재: ${fmt(kj.GOV_MAT)}원 = 총공사비: ${fmt(kj.GRAND_TOTAL)}원</span>
</div>
<!-- TWM_KANJUB 상단 툴바 -->
<div class="win-subbar">
<label>차수(cBx_Chasu):</label>
<select id="kj-chasu" class="win-select">
<option value="1">원설계 (1_R_*)</option>
<option value="2">1차변경 (2_R_*)</option>
<option value="3">2차변경 (3_R_*)</option>
</select>
<label>산정방식(cBx_W_Type):</label>
<select id="kj-wtype" class="win-select">
<option value="0" ${kc.scheme === "T" || kc.scheme === "S" ? "selected" : ""}>제잡비 원가계산방식</option>
<option value="1" ${kc.scheme === "TA" || kc.scheme === "SA" ? "selected" : ""}>제잡비 표준시장방식(실적)</option>
</select>
<label>기준요율표(ST_BSC/*.JB_R):</label>
<select id="kj-jbr-select" class="win-select" style="width:190px;">
${jbrFiles.map((f) => `<option value="${f.path}" ${f.name.includes("2024") ? "selected" : ""}>${f.name}</option>`).join("")}
</select>
<button class="win-btn" id="kj-btn-apply-jbr">📥 JB_R 기준요율 적용</button>
<button class="win-btn" id="kj-btn-open-rate">📊 제비율표 편집(wM_KAN_RATE)</button>
<button class="win-btn primary" id="kj-btn-save-dbf" style="margin-left:auto;">💾 제잡비 계산결과 DBF/SMT 저장</button>
</div>
<!-- Pn0_Input: 16개 제잡비 설정 콤보 패널 (22번 문서 §5.1) -->
<div class="kanjub-16grid">
<div class="kanjub-field"><label>1. 공사종류(PCLASS)</label><select id="kj-opt-pclass"><option value="0">일반토목/산림토목</option><option value="1">건축공사</option><option value="2">조경공사</option><option value="7">전문/설계변경</option></select></div>
<div class="kanjub-field"><label>2. 공사기간(개월)</label><select id="kj-opt-term"><option value="2">6개월~12개월</option><option value="1">6개월 미만</option><option value="3">12개월 초과</option></select></div>
<div class="kanjub-field"><label>3. 고용보험 등급</label><select id="kj-opt-kobo"><option value="1.01">1등급~특등급 (1.01%)</option><option value="0.9">5등급 이하 (0.90%)</option></select></div>
<div class="kanjub-field"><label>4. 퇴직공제부금</label><select id="kj-opt-tbugum"><option value="0">적용안함 (0%)</option><option value="2.3">적용 (직접노무비×2.3%)</option></select></div>
<div class="kanjub-field"><label>5. 산안비 관급포함</label><select id="kj-opt-anjun-gov"><option value="1" ${kc.anjunGovVatExcluded ? "selected" : ""}>관급자재(부가세제외 ÷1.1) 포함</option><option value="0" ${!kc.anjunGovVatExcluded ? "selected" : ""}>관급 미포함 (재+직노만)</option></select></div>
<div class="kanjub-field"><label>6. 산안비 기초가산액</label><input type="number" id="kj-anjun-base" value="${kc.anjunBaseAdd}" step="1000" /></div>
<div class="kanjub-field"><label>7. 산안비 1.2배 상한</label><select id="kj-anjun-cap"><option value="1" ${kc.anjunCap12 ? "selected" : ""}>MIN(가산식, 무가산×1.2) 적용</option><option value="0" ${!kc.anjunCap12 ? "selected" : ""}>상한 미적용</option></select></div>
<div class="kanjub-field"><label>8. 간접비 절사단위</label><select id="kj-ind-trunc"><option value="0" ${kc.indirectTruncDigits === 0 ? "selected" : ""}>1원 단위 절사</option><option value="-1" ${kc.indirectTruncDigits === -1 ? "selected" : ""}>10원 단위 절사</option><option value="-3" ${kc.indirectTruncDigits === -3 ? "selected" : ""}>1,000원 단위 절사</option></select></div>
<div class="kanjub-field"><label>9. 부가가치세 기준</label><select id="kj-vat-mode"><option value="supply_10" ${kc.vatMode === "supply_10" ? "selected" : ""}>(1) 총원가의 10%</option><option value="mat_10" ${kc.vatMode === "mat_10" ? "selected" : ""}>(2) 재료비의 10%(면세)</option></select></div>
<div class="kanjub-field"><label>10. 도급액 절사(TOT_JEOL)</label><select id="kj-tot-jeol"><option value="-3" ${kc.totJeolDigits === -3 ? "selected" : ""}>1,000원 단위 절사</option><option value="-4" ${kc.totJeolDigits === -4 ? "selected" : ""}>10,000원 단위 절사</option><option value="0" ${kc.totJeolDigits === 0 ? "selected" : ""}>절사 안함(1원)</option></select></div>
<div class="kanjub-field"><label>11. 이윤 세전 끝전조정</label><select id="kj-profit-adj"><option value="1" ${kc.enableProfitEndAdj ? "selected" : ""}>켬 (이윤에서 세전 역산 차감)</option><option value="0" ${!kc.enableProfitEndAdj ? "selected" : ""}>끔 (소계에서 단순 차감)</option></select></div>
<div class="kanjub-field"><label>12. 관급자재 단수처리</label><select id="kj-gov-round"><option value="up" ${kc.govRoundMode === "up" ? "selected" : ""}>천원 올림(ROUNDUP,-3)</option><option value="down" ${kc.govRoundMode === "down" ? "selected" : ""}>천원 절사(ROUNDDOWN,-3)</option><option value="none" ${kc.govRoundMode === "none" ? "selected" : ""}>원단위 그대로</option></select></div>
</div>
<!-- Pg_Ctrl: 하단 4탭 (일반형식 T / 수공형식 S / 실적일반 TA / 실적수공 SA) -->
<div class="win-subbar" style="background:#dbeafe;">
<strong>원가계산서 양식 탭 (Pg_Ctrl):</strong>
${[
{ id: "T", label: "탭1: 일반형식 (T · 재+노+경)" },
{ id: "S", label: "탭2: 수공형식 (S · 경비 16세부비목)" },
{ id: "TA", label: "탭3: 실적일반 (TA · 표준시장단가 병행)" },
{ id: "SA", label: "탭4: 실적수공 (SA · 실적+수공)" },
]
.map(
(t) => `
<button class="win-btn kj-scheme-tab ${kc.scheme === t.id ? "active" : ""}" data-scheme="${t.id}">
${t.label}
</button>
`,
)
.join("")}
<span style="margin-left:auto;color:#1e3a8a;font-weight:700;">
이윤 끝전조정: ${fmt(kj.PROFIT_ADJ)}원 (초기 이윤 ${fmt(kj.IYUN_INIT)}원 → 최종 이윤 ${fmt(kj.IYUN)}원)
</span>
</div>
<div class="split-vert">
<div class="pane-box" style="flex:1;">
<div class="grid-scroll">
<table class="dbgrid">
<thead>
<tr>
<th style="width:45px;">순번</th>
<th style="width:120px;">비목코드</th>
<th style="width:220px;">원가계산서 항목명</th>
<th>산정기초 공식 및 밑수 설명 (REA 03·04·16번 문서 검증)</th>
<th style="width:140px;">산정기초 금액(원)</th>
<th style="width:105px;">적용요율(%)</th>
<th style="width:150px;">산출금액(원)</th>
</tr>
</thead>
<tbody>
${kj.lines
.map((ln, idx) => {
const isRateEditable = kc.rates[ln.key] !== undefined;
const isStrong =
ln.key === "DIRECT_COST" ||
ln.key === "TOTAL_COST" ||
ln.key === "FINAL_SUPPLY" ||
ln.key === "GRAND_TOTAL";
return `
<tr class="${isStrong ? "boq-row-T" : ""}">
<td class="center">${idx + 1}</td>
<td class="center"><code>${ln.key}</code></td>
<td><strong>${ln.label}</strong></td>
<td>${ln.formula}</td>
<td class="num">${ln.base !== null && ln.base !== undefined ? fmt(ln.base) : "-"}</td>
<td class="num">
${
isRateEditable
? `<input type="number" step="0.001" class="cell-input num kj-rate-input" data-key="${ln.key}" value="${ln.rate}" />`
: ln.rate !== null && ln.rate !== undefined
? fmt(ln.rate, 3) + "%"
: "-"
}
</td>
<td class="num"><strong>${fmt(ln.amount)}</strong></td>
</tr>
`;
})
.join("")}
</tbody>
</table>
</div>
</div>
</div>
</div>
`;
}
/**
* 2. TWM_KAN_RATE (wM_KAN_RATE 원가계산기준 제비율표 4탭 편집 화면)
* - Tab_Rn: 일반 제잡비율 (RATE_R.DBF 50행 × 35열)
* - Tab_RA: 실적공사 제잡비율 (RATE_RA.DBF 50행 × 20열)
* - Tab_M: 일반관리비율 (RATE_M.DBF 25행 × 10열)
* - Tab_B: 보증수수료율 (RATE.DBF / RATE_B)
*/
function renderWMKanRate(ctx) {
const rateTables = (ctx.dbfData && ctx.dbfData.rate_tables) || {};
const activeTab = ctx.uiState.kanRateTab || "RATE_R.DBF";
const rows = rateTables[activeTab] || [];
const colKeys = rows[0] ? Object.keys(rows[0]).filter((k) => !k.startsWith("_")) : [];
const jbrFiles = (ctx.sourcesData && ctx.sourcesData.jbr_files) || [];
const ROW_MEANINGS = {
0: "공사규모 기준액 구간 헤더",
6: "간접노무비(1~5열) · 기타경비(6~10열) — 5천만 미만",
7: "간접노무비 · 기타경비 — 5천만~3억 미만",
8: "간접노무비 · 기타경비 — 3억~10억 미만",
9: "간접노무비 · 기타경비 — 10억~30억 미만 (중규모)",
20: "선택 적용 행: 간접노무(1) · 기타경비(6) · 일반관리비(11) · 이윤(16) · 산안비(17) · 기초액(18)",
45: "산재보험료율(1열) · 고용보험료율(17~20열 등급별)",
46: "건강보험(1열) · 국민연금(6열) · 노인장기요양(11열)",
};
return `
<div class="win-form">
<div class="win-form-header">
<span>📊 원가계산기준 제비율표 조회·수정 [TWM_KAN_RATE · ${activeTab} (${rows.length}행 × ${colKeys.length}열)]</span>
<span>행20(선택적용) · 행45(산재/고용) · 행46(건보/연금/요양) 좌표 매핑 검증 완료</span>
</div>
<div class="win-subbar">
${[
{ id: "RATE_R.DBF", label: "탭1(Tab_Rn): 일반 제잡비율 (RATE_R 50×35)" },
{ id: "RATE_RA.DBF", label: "탭2(Tab_RA): 실적공사 제잡비율 (RATE_RA 50×20)" },
{ id: "RATE_M.DBF", label: "탭3(Tab_M): 일반관리비율 (RATE_M 25×10)" },
{ id: "RATE.DBF", label: "탭4(Tab_B): 보증/기타요율 (RATE)" },
]
.map(
(t) => `
<button class="win-btn kr-tab-btn ${activeTab === t.id ? "active" : ""}" data-tab="${t.id}">
${t.label}
</button>
`,
)
.join("")}
<select id="kr-jbr-select" class="win-select" style="margin-left:auto;width:210px;">
${jbrFiles.map((f) => `<option value="${f.path}">${f.name}</option>`).join("")}
</select>
<button class="win-btn primary" id="kr-btn-load-jbr">📂 선택한 ST_BSC/*.JB_R 불러오기</button>
</div>
<div class="split-vert">
<div class="pane-box" style="flex:1;">
<div class="grid-scroll">
<table class="dbgrid">
<thead>
<tr>
<th style="width:45px;">행(0~)</th>
<th style="width:240px;">STMATE 행 좌표 의미</th>
${colKeys.map((ck) => `<th style="width:72px;">${ck}</th>`).join("")}
</tr>
</thead>
<tbody>
${rows
.map((r, rIdx) => {
const highlight = ROW_MEANINGS[rIdx] ? "boq-row-T" : "";
return `
<tr class="${highlight}">
<td class="center"><strong>#${rIdx}</strong></td>
<td>${ROW_MEANINGS[rIdx] || `구간/공사종류 데이터 행 #${rIdx}`}</td>
${colKeys.map((ck) => `<td class="num">${r[ck]}</td>`).join("")}
</tr>
`;
})
.join("")}
</tbody>
</table>
</div>
</div>
</div>
</div>
`;
}
/**
* 3. 계약 / 실행예산 / 기성 / 설계변경 단계 통합 화면 (wM_Mk_Cont · wM_Boq_ExecX · wM_KanJub_K · 설계변경)
* 근거: 27_계약_실행_기성_단계.md
*/
function renderWStage(ctx) {
const stageSub = ctx.uiState.stageSubTab || "CONTRACT";
const bidRateM = num(ctx.uiState.bidRateM || 87.745);
const bidRateL = num(ctx.uiState.bidRateL || 87.745);
const bidRateS = num(ctx.uiState.bidRateS || 87.745);
const progPct = num(ctx.uiState.progPct || 65.0);
const prevPct = num(ctx.uiState.prevPct || 30.0);
const comp = ctx.computed;
const leafRows = comp.boqComputed.rows || [];
const revComp = comp.revComputed;
return `
<div class="win-form">
<div class="win-form-header">
<span>🔄 설계 후속 단계 관리 [27번 문서 — 계약내역(wM_Mk_Cont) · 실행예산(wM_Boq_ExecX) · 기성내역(wM_KanJub_K) · 설계변경 대비]</span>
<span>K_ORD 단계 전이: 설계(0) → 계약(1) → 변경(2) → 실행(3) → 기성(4)</span>
</div>
<div class="win-subbar">
<button class="win-btn stage-tab-btn ${stageSub === "CONTRACT" ? "active" : ""}" data-st="CONTRACT">1. 계약내역 변환 (wM_Mk_Cont · 낙찰율/목표금액 역산)</button>
<button class="win-btn stage-tab-btn ${stageSub === "EXEC" ? "active" : ""}" data-st="EXEC">2. 실행예산 (wM_Boq_ExecX · 실행단가 보정)</button>
<button class="win-btn stage-tab-btn ${stageSub === "GISUNG" ? "active" : ""}" data-st="GISUNG">3. 기성내역/제잡비 (wM_KanJub_K · 전회/금회/누계)</button>
<button class="win-btn stage-tab-btn ${stageSub === "REVISION" ? "active" : ""}" data-st="REVISION">4. 설계변경 증감대비표 (당초 vs 변경)</button>
</div>
${
stageSub === "CONTRACT"
? `
<div class="kanjub-16grid" style="grid-template-columns:repeat(4,1fr);">
<div class="kanjub-field"><label>재료비 낙찰율(%)</label><input type="number" id="st-bid-m" value="${bidRateM}" step="0.001" /></div>
<div class="kanjub-field"><label>노무비 낙찰율(%)</label><input type="number" id="st-bid-l" value="${bidRateL}" step="0.001" /></div>
<div class="kanjub-field"><label>경비 낙찰율(%)</label><input type="number" id="st-bid-s" value="${bidRateS}" step="0.001" /></div>
<div class="kanjub-field"><button class="win-btn primary" id="st-btn-apply-bid">계약단가(TAMT2) 일괄 산출</button></div>
</div>
<div class="split-vert">
<div class="pane-box" style="flex:1;">
<div class="pane-title">
<span>계약내역서 (설계단가 × 낙찰율 → 계약단가 및 계약금액 산출 · 적용제외 공정 보호 지원)</span>
<span>설계 직공비: ${fmt(comp.boqComputed.totals.directT)}원 → 계약 직공비: ${fmt(Math.floor((comp.boqComputed.totals.directT * bidRateM) / 100))}원</span>
</div>
<div class="grid-scroll">
<table class="dbgrid">
<thead>
<tr><th>코드</th><th>공종명</th><th>규격</th><th>단위</th><th>수량</th><th>설계단가</th><th>설계금액</th><th>계약단가(낙찰적용)</th><th>계약금액</th></tr>
</thead>
<tbody>
${leafRows
.map((r) => {
const cM = Math.floor((r.mUnit * bidRateM) / 100);
const cL = Math.floor((r.lUnit * bidRateL) / 100);
const cS = Math.floor((r.sUnit * bidRateS) / 100);
const cT = cM + cL + cS;
const cAmt = Math.floor(r.qty * cT);
return `
<tr>
<td class="center">${r.code}</td>
<td>${r.name}</td>
<td>${r.spec}</td>
<td class="center">${r.unit}</td>
<td class="num">${fmt(r.qty, 2)}</td>
<td class="num">${fmt(r.tUnit)}</td>
<td class="num">${fmt(r.tAmt)}</td>
<td class="num"><strong>${fmt(cT)}</strong></td>
<td class="num"><strong>${fmt(cAmt)}</strong></td>
</tr>
`;
})
.join("")}
</tbody>
</table>
</div>
</div>
</div>`
: ""
}
${
stageSub === "EXEC"
? `
<div class="split-vert">
<div class="pane-box" style="flex:1;">
<div class="pane-title"><span>[wM_Boq_ExecX] 실행예산 내역서 (계약금액 대비 현장 실행예산율 92.0% 기준 비교)</span></div>
<div class="grid-scroll">
<table class="dbgrid">
<thead><tr><th>코드</th><th>공종명</th><th>수량</th><th>설계금액</th><th>실행단가(92%)</th><th>실행예산금액</th><th>절감액(차액)</th></tr></thead>
<tbody>
${leafRows
.map((r) => {
const exUnit = Math.floor(r.tUnit * 0.92);
const exAmt = Math.floor(r.qty * exUnit);
return `
<tr>
<td class="center">${r.code}</td>
<td>${r.name}</td>
<td class="num">${fmt(r.qty, 2)}</td>
<td class="num">${fmt(r.tAmt)}</td>
<td class="num">${fmt(exUnit)}</td>
<td class="num"><strong>${fmt(exAmt)}</strong></td>
<td class="num" style="color:#137333;">${fmt(r.tAmt - exAmt)}</td>
</tr>
`;
})
.join("")}
</tbody>
</table>
</div>
</div>
</div>`
: ""
}
${
stageSub === "GISUNG"
? `
<div class="kanjub-16grid" style="grid-template-columns:repeat(3,1fr);">
<div class="kanjub-field"><label>전회 누적 기성율(%)</label><input type="number" id="st-prev-pct" value="${prevPct}" step="5" /></div>
<div class="kanjub-field"><label>금회 포함 누계기성율(%)</label><input type="number" id="st-prog-pct" value="${progPct}" step="5" /></div>
<div class="kanjub-field"><button class="win-btn primary" id="st-btn-apply-gisung">기성조서(전회·금회·누계·잔여) 재계산</button></div>
</div>
<div class="split-vert">
<div class="pane-box" style="flex:1;">
<div class="pane-title">
<span>[wM_KanJub_K] 기성내역 및 기성 제잡비 계산 (금회직공비 × 계약제잡비율 · 계약 | 전회 | 금회 | 누계 | 잔여)</span>
<span>금회 기성율: ${(progPct - prevPct).toFixed(1)}% | 누계 기성도급액: ${fmt(Math.floor((comp.kanjubComputed.FINAL_SUPPLY * progPct) / 100))}원</span>
</div>
<div class="grid-scroll">
<table class="dbgrid">
<thead>
<tr><th>코드</th><th>공종명</th><th>계약수량</th><th>계약금액</th><th>전회기성액(${prevPct}%)</th><th>금회기성수량</th><th>금회기성액(${(progPct - prevPct).toFixed(1)}%)</th><th>누계기성액(${progPct}%)</th><th>잔여금액</th></tr>
</thead>
<tbody>
${leafRows
.map((r) => {
const prevA = Math.floor((r.tAmt * prevPct) / 100);
const cumA = Math.floor((r.tAmt * progPct) / 100);
const curA = cumA - prevA;
const curQ = (r.qty * (progPct - prevPct)) / 100;
return `
<tr>
<td class="center">${r.code}</td>
<td>${r.name}</td>
<td class="num">${fmt(r.qty, 2)}</td>
<td class="num">${fmt(r.tAmt)}</td>
<td class="num">${fmt(prevA)}</td>
<td class="num">${fmt(curQ, 2)}</td>
<td class="num"><strong>${fmt(curA)}</strong></td>
<td class="num"><strong>${fmt(cumA)}</strong></td>
<td class="num">${fmt(r.tAmt - cumA)}</td>
</tr>
`;
})
.join("")}
</tbody>
</table>
</div>
</div>
</div>`
: ""
}
${
stageSub === "REVISION"
? `
<div class="split-vert">
<div class="pane-box" style="flex:1;">
<div class="pane-title">
<span>설계변경 원가계산 대비표 (당초 도급액: ${fmt(comp.kanjubComputed.FINAL_SUPPLY)}원 vs 변경 도급액: ${fmt(revComp.revKj.FINAL_SUPPLY)}원 · 사후정산 건보/요양/연금 당초고정 지원)</span>
</div>
<div class="grid-scroll">
<table class="dbgrid">
<thead>
<tr><th>코드</th><th>공종명</th><th>당초수량(QTY1)</th><th>변경수량(QTY2)</th><th>단가</th><th>당초금액</th><th>변경금액</th><th>증감액(차액)</th></tr>
</thead>
<tbody>
${leafRows
.map(
(r) => `
<tr>
<td class="center">${r.code}</td>
<td>${r.name}</td>
<td class="num">${fmt(r.qty, 2)}</td>
<td class="num"><input type="number" step="1" class="cell-input num rev-edit-qty2" data-id="${r.id}" value="${r.changedQty}" /></td>
<td class="num">${fmt(r.tUnit)}</td>
<td class="num">${fmt(r.tAmt)}</td>
<td class="num"><strong>${fmt(r.changedTAmt)}</strong></td>
<td class="num" style="color:${r.diffTAmt >= 0 ? "#0050b3" : "#c5221f"};"><strong>${fmt(r.diffTAmt)}</strong></td>
</tr>
`,
)
.join("")}
</tbody>
</table>
</div>
</div>
</div>`
: ""
}
</div>
`;
}
/**
* 4. PM_Prt (ST_Prt.CPM 30종 보고서 인쇄 미리보기 · SET_REP.DBF 75종 양식 · 엑셀/조달청 XML/JDL 내보내기)
*/
function renderPMPrt(ctx) {
const activeRep = ctx.uiState.activeReportSheet || "0608";
const setRepRows = ((ctx.dbfData && ctx.dbfData.set_rep_rows) || []).filter(
(r) => r.NAME || r.HEADTITLE,
);
const kj = ctx.computed.kanjubComputed;
const boqRows = global.STMATE_VIEW_PRJ_BOQ.evaluateFullBoqRows(ctx);
const costnRows = (ctx.dbfData && ctx.dbfData.costn_rows) || [];
const memos = (ctx.dbfData && ctx.dbfData.costn_memos) || {};
return `
<div class="win-form">
<div class="win-form-header">
<span>🖨️ 보고서 인쇄 및 엑셀/조달청 XML/JDL 변환 [PM_Prt · ST_Prt.CPM 30종 시트 · SET_REP.DBF ${setRepRows.length}종 양식]</span>
<span>인쇄 옵션: 굴림체 · 비고=코드/호표 · 수량/단위 표준 · 일반양식</span>
</div>
<div class="win-subbar">
<label>보고서 양식(SET_REP):</label>
<select id="prt-sheet-select" class="win-select" style="width:260px;">
<option value="0608" ${activeRep === "0608" ? "selected" : ""}>[0608] 공사원가계산서 (PM_Won_A)</option>
<option value="0601" ${activeRep === "0601" ? "selected" : ""}>[0601] 총괄설계내역서 (PM_Nae_Tot)</option>
<option value="0603" ${activeRep === "0603" ? "selected" : ""}>[0603] 설계내역서 (PM_Nae)</option>
<option value="0607" ${activeRep === "0607" ? "selected" : ""}>[0607] 관급자재내역서 (PM_Nae_Kwn)</option>
<option value="0643" ${activeRep === "0643" ? "selected" : ""}>[0643] 설계변경내역서/증감대비표 (CHANGE)</option>
<option value="0623" ${activeRep === "0623" ? "selected" : ""}>[0623/0673] 계약내역서·실행예산서</option>
<option value="0633" ${activeRep === "0633" ? "selected" : ""}>[0633] 기성부분내역서·수량조서</option>
<option value="0302" ${activeRep === "0302" ? "selected" : ""}>[0301/0302] 일위대가표·목록표 (PM_iLWi)</option>
<option value="0402" ${activeRep === "0402" ? "selected" : ""}>[0401/0402] 단가산출근거·목록표 (Edit_SanKun)</option>
<option value="0203" ${activeRep === "0203" ? "selected" : ""}>[0202/0203] 시간당중기사용료·목록표 (PM_JOONG)</option>
<option value="0101" ${activeRep === "0101" ? "selected" : ""}>[0101~0105] 노무·재료·경비·단가대비표</option>
<option value="0501" ${activeRep === "0501" ? "selected" : ""}>[0501~0507] 자원/중기/대가 수량금액집계표</option>
<option value="0201" ${activeRep === "0201" ? "selected" : ""}>[0201] 환율및기초자료 · 〓 INITIAL 〓</option>
<option value="SET_REP_ALL" ${activeRep === "SET_REP_ALL" ? "selected" : ""}>[전체] SET_REP.DBF 75종 양식 설정표</option>
</select>
<button class="win-btn primary" id="prt-btn-xls">📗 다중시트 엑셀(.xls 워크북) 내보내기</button>
<button class="win-btn" id="prt-btn-csv">📊 현재 시트 CSV 다운로드</button>
<button class="win-btn" id="prt-btn-xml">🏛️ 조달청 호환규정 XML(T3_C3B)</button>
<button class="win-btn" id="prt-btn-jdl">📄 조달청 JDL 내보내기</button>
<button class="win-btn" onclick="window.print()">🖨️ 인쇄</button>
</div>
<div class="split-vert">
<div class="pane-box" style="flex:1;padding:16px;overflow:auto;background:#fff;">
<h2 style="text-align:center;margin:0 0 6px;letter-spacing:4px;color:#0f294d;">
${
{
"0608": "공 사 원 가 계 산 서",
"0601": "총 괄 설 계 내 역 서",
"0603": "설 계 내 역 서",
"0607": "관 급 자 재 내 역 서",
"0643": "설 계 변 경 내 역 서 (증 감 대 비)",
"0623": "계 약 내 역 서 · 실 행 예 산 서",
"0633": "기 성 부 분 내 역 서 · 수 량 조 서",
"0302": "일 위 대 가 표 (목 록)",
"0402": "단 가 산 출 근 거",
"0203": "시 간 당 중 기 사 용 료",
"0101": "자 재 · 노 무 · 경 비 단 가 목 록 표",
"0501": "자 원 · 중 기 · 대 가 수 량 금 액 집 계 표",
"0201": "환 율 및 기 초 자 료 (〓 INITIAL 〓)",
SET_REP_ALL: "SET_REP.DBF 75종 양식 설정표",
}[activeRep] || "설 계 보 고 서"
}
</h2>
<p style="text-align:center;margin:0 0 12px;font-weight:700;color:#334155;">
공사명: ${ctx.state.projectMeta.pname} (총공사비: ${fmt(kj.GRAND_TOTAL)}원 / 도급액: ${fmt(kj.FINAL_SUPPLY)}원)
</p>
${
activeRep === "0608"
? `<table class="dbgrid">
<thead><tr><th>비목</th><th>산정기초(원)</th><th>요율(%)</th><th>금액(원)</th><th>산출근거</th></tr></thead>
<tbody>
${kj.lines
.map(
(ln) => `
<tr>
<td><strong>${ln.label}</strong></td>
<td class="num">${ln.base ? fmt(ln.base) : ""}</td>
<td class="num">${ln.rate !== null && ln.rate !== undefined ? fmt(ln.rate, 3) + "%" : ""}</td>
<td class="num"><strong>${fmt(ln.amount)}</strong></td>
<td>${ln.formula}</td>
</tr>`,
)
.join("")}
</tbody>
</table>`
: ""
}
${
["0601", "0603", "0607", "0643", "0623", "0633"].includes(activeRep)
? `<table class="dbgrid">
<thead><tr><th>공종</th><th>명칭</th><th>규격</th><th>단위</th><th>수량</th><th>재료비단가</th><th>노무비단가</th><th>경비단가</th><th>합계단가</th><th>합계금액</th><th>비고</th></tr></thead>
<tbody>
${boqRows
.filter((r) =>
activeRep === "0607"
? r._isExcl
: activeRep === "0601"
? /^(T\d|E\d|C\d)$/.test(String(r.BDCODE1 || "").trim())
: true,
)
.map(
(r) => `
<tr class="${r._rowClass}">
<td class="center">${r.ITNUM || ""}</td>
<td>${r.NAME1 || ""}</td>
<td>${r.SIZE1 || ""}</td>
<td class="center">${r.UNIT1 || ""}</td>
<td class="num">${r.QTY1 ? fmt(r.QTY1, 2) : ""}</td>
<td class="num">${r.MAMT1 ? fmt(r.MAMT1) : ""}</td>
<td class="num">${r.LAMT1 ? fmt(r.LAMT1) : ""}</td>
<td class="num">${r.SAMT1 ? fmt(r.SAMT1) : ""}</td>
<td class="num">${r.TAMT1 ? fmt(r.TAMT1) : ""}</td>
<td class="num"><strong>${r.T_AMT1 ? fmt(r.T_AMT1) : ""}</strong></td>
<td class="center">${r.BDCODE1 || ""}</td>
</tr>`,
)
.join("")}
</tbody>
</table>`
: ""
}
${
activeRep === "0402"
? Object.values(memos)
.map(
(m) => `
<div style="border:1px solid #94a3b8;padding:8px;margin-bottom:8px;">
<div style="font-weight:700;background:#eef2f8;padding:4px 8px;margin-bottom:4px;">[${m.code}] ${(costnRows.find((x) => x.CODE === m.code) || {}).NAME || ""}</div>
<pre style="margin:0;font-family:Consolas,monospace;font-size:11.5px;white-space:pre-wrap;">${m.text}</pre>
</div>`,
)
.join("")
: ""
}
${
["0302", "0203", "0101", "0501", "0201"].includes(activeRep)
? `<table class="dbgrid">
<thead><tr><th>코드</th><th>품명</th><th>규격</th><th>단위</th><th>재료비</th><th>노무비</th><th>경비</th><th>합계단가</th></tr></thead>
<tbody>
${costnRows
.filter((r) => {
const p = String(r.CODE || " ")[0].toUpperCase();
if (activeRep === "0302") return p === "B" || p === "D";
if (activeRep === "0203") return p === "X";
if (activeRep === "0201") return p === "L" || p === "X";
return p === "L" || p === "M" || p === "S";
})
.map(
(r) => `
<tr>
<td class="center"><strong>${r.CODE}</strong></td>
<td>${r.NAME || ""}</td>
<td>${r.SIZE || ""}</td>
<td class="center">${r.UNIT || ""}</td>
<td class="num">${fmt(num(r.MAMT1))}</td>
<td class="num">${fmt(num(r.LAMT1))}</td>
<td class="num">${fmt(num(r.SAMT1))}</td>
<td class="num"><strong>${fmt(num(r.TAMT1))}</strong></td>
</tr>`,
)
.join("")}
</tbody>
</table>`
: ""
}
${
activeRep === "SET_REP_ALL"
? `<table class="dbgrid">
<thead><tr><th>양식코드(INDX)</th><th>보고서명(NAME)</th><th>인쇄 표제(HEADTITLE)</th><th>컬럼수(FLDCNT)</th><th>필드1</th><th>필드2</th><th>필드3</th><th>필드4</th></tr></thead>
<tbody>
${setRepRows
.map(
(r) => `
<tr>
<td class="center"><strong>${r.INDX}</strong></td>
<td>${r.NAME}</td>
<td><strong>${r.HEADTITLE}</strong></td>
<td class="center">${r.FLDCNT}</td>
<td>${r.FLD_N1 || ""}</td>
<td>${r.FLD_N2 || ""}</td>
<td>${r.FLD_N3 || ""}</td>
<td>${r.FLD_N4 || ""}</td>
</tr>`,
)
.join("")}
</tbody>
</table>`
: ""
}
</div>
</div>
</div>
`;
}
global.STMATE_VIEW_KANJUB_PRT = {
renderWMKanJub,
renderWMKanRate,
renderWStage,
renderPMPrt,
};
})(window);