648 lines
36 KiB
JavaScript
648 lines
36 KiB
JavaScript
/**
|
||
* 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);
|