From 91d5f478ccefdf9b3883fa3ee3e70e4422cab58a Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 5 Nov 2026 06:11:43 +0900 Subject: [PATCH] fix(stmate-test): resolve 5 UI/calculation issues found during browser control verification --- tools/stmate-test/app.js | 40 +++++++++++++++++++++++++++++- tools/stmate-test/calc.js | 21 +++++++++++++--- tools/stmate-test/view_dan_ilwi.js | 25 ++++++++++++++++--- tools/stmate-test/view_prj_boq.js | 8 +++--- 4 files changed, 83 insertions(+), 11 deletions(-) diff --git a/tools/stmate-test/app.js b/tools/stmate-test/app.js index 82d593062..af0ced7e7 100644 --- a/tools/stmate-test/app.js +++ b/tools/stmate-test/app.js @@ -188,8 +188,39 @@ renderWorkspace(); }); onClick("modal-apply-expnd", () => { + const code = ($("expnd-code") || {}).value; + const ilwi = + (ctx.state.ilwiItems || []).find((x) => x.code === code) || ctx.state.ilwiItems[0]; + if (ilwi && ilwi.lines && ilwi.lines.length) { + ilwi.lines.forEach((ln, idx) => { + const r = ctx.state.resources.find((x) => x.id === ln.refId || x.code === ln.refId); + const recno = (Date.now() % 90000) + idx + 1; + ctx.state.boqItems.push({ + id: "BOQ_" + recno, + code: r ? r.code : ln.refId, + name: `[전개] ${r ? r.name : ln.refId}`, + refId: ln.refId, + rowType: "normal", + qty: ln.qty || 1, + changedQty: ln.qty || 1, + _recno: recno, + }); + if (ctx.dbfData && ctx.dbfData.boq_rows) { + ctx.dbfData.boq_rows.splice(5 + idx, 0, { + _recno: recno, + ITNUM: "", + BDCODE1: r ? r.code : ln.refId, + NAME1: `[전개] ${r ? r.name : ln.refId}`, + SIZE1: r ? r.spec : "", + UNIT1: r ? r.unit : "식", + QTY1: String(ln.qty || 1), + ENDCODE: "0.0000", + }); + } + }); + } closeModal(); - alert("선택 공종의 일위대가/산근 구성행이 전개되었습니다."); + renderWorkspace(); }); bindEach(".btn-apply-qty-expr", "onclick", (btn) => { const id = btn.getAttribute("data-id"); @@ -455,6 +486,13 @@ const code = $("ilwi-btn-apply-f9").getAttribute("data-code"); const txt = ($("smt-formula-textarea") || {}).value || ""; ctx.uiState.editedSmtMap[code] = txt; + if (ctx.dbfData && ctx.dbfData.costn_memos) { + const prev = ctx.dbfData.costn_memos[code] || { code, blockNo: 1 }; + ctx.dbfData.costn_memos[code] = Object.assign({}, prev, { + text: txt, + byteLen: txt.length, + }); + } const evalRes = ViewDanIlwi.evaluateSmtFormulaText(txt, ctx); if (evalRes.sumT > 0) ViewPrjBoq.syncCostnUnitPrice(ctx, code, evalRes.sumM, evalRes.sumL, evalRes.sumS); diff --git a/tools/stmate-test/calc.js b/tools/stmate-test/calc.js index 392c112bc..3fd13c219 100644 --- a/tools/stmate-test/calc.js +++ b/tools/stmate-test/calc.js @@ -327,7 +327,10 @@ const StmateCalc = (() => { }); const directT = directM + directL + directS, govCombinedRaw = govRawTotal + govFeeTotal; - const totals = { directM, directL, directS, directT, govRawTotal, govFeeTotal, govCombinedRaw }; + const totals = Object.assign( + { directM, directL, directS, directT }, + { govRawTotal, govMatTotal: govRawTotal, govFeeTotal, govCombinedRaw }, + ); return Object.assign({ rows, directTotal: directT, totals }, totals); } @@ -348,7 +351,7 @@ const StmateCalc = (() => { const truncAmt = truncInd(rawCalcAmt); const base = Object.assign( { key: code, code, label, formula: baseDesc }, - { baseDesc, baseAmt, rate: ratePct, ratePct }, + { baseDesc, baseAmt, base: baseAmt, rate: ratePct, ratePct }, ); if (!origKanjubRes) return Object.assign(base, { amount: truncAmt, modeUsed: "원설계" }); const origRow = origKanjubRes.itemMap[code], @@ -507,6 +510,7 @@ const StmateCalc = (() => { ["label", label], ["formula", formula], ["baseAmt", baseAmt], + ["base", baseAmt], ["rate", rate], ["amount", amount], ]); @@ -658,8 +662,19 @@ const StmateCalc = (() => { kanjubOrig = calcKanjub(data, boqOrig, null); const boqRev = calcBoq(data, resources.map, machines.map, sanCal.map, ilwi.map, true), kanjubRev = calcKanjub(data, boqRev, kanjubOrig); + const revMap = Object.fromEntries((boqRev.rows || []).map((r) => [r.id, r])); + (boqOrig.rows || []).forEach((r) => { + const rv = revMap[r.id]; + r.changedTAmt = rv ? rv.tAmt : r.tAmt; + r.diffTAmt = r.changedTAmt - r.tAmt; + }); const base = { resources, machines, sanCal, ilwi, boqOrig, kanjubOrig }; - Object.assign(base, { boqRev, kanjubRev, resList: resources.list }); + Object.assign(base, { + boqRev, + kanjubRev, + resList: resources.list, + revComputed: { revBoq: boqRev, revKj: kanjubRev }, + }); return Object.assign(base, { boqComputed: boqOrig, kanjubComputed: kanjubOrig }); } diff --git a/tools/stmate-test/view_dan_ilwi.js b/tools/stmate-test/view_dan_ilwi.js index 5986b85d9..a765aa785 100644 --- a/tools/stmate-test/view_dan_ilwi.js +++ b/tools/stmate-test/view_dan_ilwi.js @@ -82,13 +82,14 @@ const vExpr = m[2].trim(); const val = evalMathWithVars(vExpr); if (val > 0 || vExpr === "0") { - vars[vName] = Math.round(val * 10000) / 10000; + vars[vName] = Math.round(val * 100) / 100; assignedHere.push(`${vName} = ${vars[vName]}`); } } - // 2) 자원 참조 (&X00026L& / Q = , &M00080& * 0.00438'㎥' = ) + // 2) 자원 참조 (&X00026L& / Q = , &M00080& * 0.00438'㎥' = ) 또는 주석형 단가 요약 const refMatch = ln.match(/&([A-Z]\d{4,5})([LMS])?&(.*)/); + const commentCostMatch = !refMatch && ln.match(/(재료비|노무비|경\s*비)\s*[:=]\s*([\d,]+)/); let refInfo = null; if (refMatch) { const refCode = refMatch[1]; @@ -116,6 +117,20 @@ baseUnitPrice, calcAmt: roundedAmt, }; + } else if (commentCostMatch) { + const cKind = commentCostMatch[1].replace(/\s+/g, ""); + const cVal = num(commentCostMatch[2], 0); + const cTag = cKind === "노무비" ? "L" : cKind === "경비" ? "S" : "M"; + if (cTag === "L") sumL += cVal; + else if (cTag === "S") sumS += cVal; + else sumM += cVal; + refInfo = { + refCode: cKind, + compTag: cTag, + refName: "단가 직접지정", + baseUnitPrice: cVal, + calcAmt: cVal, + }; } linesOut.push({ @@ -357,8 +372,10 @@ ctx.uiState.selectedIlwiCode && bdxRows.some((r) => String(r.CODE).trim() === ctx.uiState.selectedIlwiCode) ? ctx.uiState.selectedIlwiCode - : bdxRows[0] - ? String(bdxRows[0].CODE).trim() + : bdxRows.find((r) => Boolean(memos[String(r.CODE || "").trim()])) || bdxRows[0] + ? String( + (bdxRows.find((r) => Boolean(memos[String(r.CODE || "").trim()])) || bdxRows[0]).CODE, + ).trim() : ""; const selRow = bdxRows.find((r) => String(r.CODE).trim() === selCode) || {}; diff --git a/tools/stmate-test/view_prj_boq.js b/tools/stmate-test/view_prj_boq.js index ce8f1a625..3b7774942 100644 --- a/tools/stmate-test/view_prj_boq.js +++ b/tools/stmate-test/view_prj_boq.js @@ -518,14 +518,16 @@ `; } if (toolKind === "wExpnd") { + const bdxList = (ctx.state.ilwiItems || []).slice(0, 30); return `
-

[wExpnd / wM_Expnd] 일위대가·단가산출 내역서 풀어넣기 — 호표 구성행을 내역서 하위 행으로 전개합니다.

-
+

[wExpnd / wM_Expnd] 일위대가·단가산출 내역서 풀어넣기 — 선택한 호표의 재료·노무·경비 구성행을 내역서에 직접 전개합니다.

+
+
-
+
`; }