From 42243ba19b2fdf4e409cae2561295e325a993fa0 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 21 Sep 2026 21:35:19 +0900 Subject: [PATCH] =?UTF-8?q?fix(M01):=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0=20=EC=8B=9C=ED=97=98=20=E2=80=94=20=ED=98=B8=ED=91=9C?= =?UTF-8?q?=20=EB=AC=B6=EC=9D=8C=EC=9D=84=20=EC=84=9C=EB=B2=84=20=EB=B9=84?= =?UTF-8?q?=EB=AA=A9=20=EB=AA=AB=EC=9C=BC=EB=A1=9C=20=EB=82=98=EB=88=94=20?= =?UTF-8?q?=C2=B7=20=E3=80=8C=ED=91=9C=20=EC=B0=BE=EA=B8=B0=20=EC=B0=BE?= =?UTF-8?q?=EA=B8=B0=E3=80=8D=20=EA=B2=B9=EC=B9=A8=20=C2=B7=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EA=B1=B8=EB=A6=B0=20=EC=A4=84=20=EB=A7=9E=ED=9E=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 호표 묶음 소계가 텍스트 수식 소계와 같아짐(로직을 부르는 줄도 노무비·재료비·경비로 나뉘어 「그 밖」이 사라짐) - 표 줄의 빈 칸은 조건에서 빼 서버 찾기()와 같게 걸린 줄을 맞힘 - 표 꼬리표를 「표」로 줄이고 걸린 줄 없음 안내글을 바로잡음 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT --- .../M01_MasterData_UI_LogicLab_Detail.ts | 19 ++++++++++++++----- .../M01_MasterData_UI_LogicLab_Modal.ts | 12 +++++++++++- .../M01_MasterData_UI_LogicLab_Text.ts | 7 ++++--- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab_Detail.ts b/M01_MasterData/M01_MasterData_UI_LogicLab_Detail.ts index 02d734f3..e6db241e 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab_Detail.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab_Detail.ts @@ -147,20 +147,27 @@ function infoBox(ctx: DetailContext): HTMLElement { ]); } +/** 계산된 줄은 서버가 준 비목별 몫으로 묶음에 나눠 넣음 — 로직을 부르는 줄도 노무비·재료비·경비로 갈림 */ +const spread = (e: Entry, line?: CalcLine): Entry[] => { + const parts = Object.entries(line?.비목 ?? {}).filter(([, v]) => v !== 0); + if (!parts.length) return [e]; + return parts.map(([k, v]) => ({ ...e, group: OF_COST[k] ?? "other", amount: v })); +}; + function entries(ctx: DetailContext): Entry[] { const middles: NamedFormula[] = ctx.row.중간 ?? []; const ho = ctx.row.호표 ?? []; - const out: Entry[] = ho.map((item, i) => { + const out: Entry[] = ho.flatMap((item, i) => { const line = ctx.lines?.[i]; const brief = ctx.prices[item.요소]; const price = line ? line.단가 : brief?.값; - return { + const entry: Entry = { group: groupOfHo(item), name: item.이름 ?? item.요소, spec: item.규격 ?? brief?.규격 ?? "", unit: item.단위 ?? "", qty: item.수량, - qtyTag: qtyKind(item.수량), + qtyTag: qtyKind(item.수량) === tx("Ho_QtyTable") ? tl("Tag_Table") : qtyKind(item.수량), price: price === undefined || price === null ? "" : formatNumber(price), amount: line ? line.금액 : null, extra: false, @@ -174,10 +181,11 @@ function entries(ctx: DetailContext): Entry[] { values: ctx.values, }), }; + return spread(entry, line); }); (ctx.row.덧줄 ?? []).forEach((extra, i) => { const line = ctx.lines?.[ho.length + i]; - out.push({ + const entry: Entry = { group: OF_COST[extra.비목 ?? ""] ?? "other", name: extra.이름, spec: "", @@ -195,7 +203,8 @@ function entries(ctx: DetailContext): Entry[] { middles, values: ctx.values, }), - }); + }; + out.push(...spread(entry, line)); }); return out; } diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab_Modal.ts b/M01_MasterData/M01_MasterData_UI_LogicLab_Modal.ts index c1b7bf78..f6cd210a 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab_Modal.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab_Modal.ts @@ -34,7 +34,17 @@ function tableView( table: TableRow, values: Record, ): HTMLElement { - const hit = matchRow(table, find.find.conds, { values, middle: {} }); + // 표 줄의 빈 칸은 「무엇이든」 — 서버 찾기()와 같게 그 칸은 조건에서 뺌 + const hit = + (table.줄 ?? []).find((line) => + matchRow( + { ...table, 줄: [line] }, + find.find.conds.filter( + ([col]) => line[col] !== undefined && line[col] !== null && line[col] !== "", + ), + { values, middle: {} }, + ), + ) ?? null; const cols = [...Object.keys(table.조건 ?? {}), ...Object.keys(table.값칸 ?? {})]; const rows = (table.줄 ?? []).map((line) => el("tr", { diff --git a/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts b/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts index 72b44b35..66fea422 100644 --- a/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts +++ b/M01_MasterData/M01_MasterData_UI_LogicLab_Text.ts @@ -27,7 +27,8 @@ const TEXT = { G_labor: ["인력", "Labor"], G_material: ["자재", "Material"], G_cost: ["경비", "Expense"], - G_other: ["그 밖", "Other"], + G_other: ["그 밖 (인력·자재·경비 어디에도 안 맞는 줄)", "Other (fits none of the three)"], + Tag_Table: ["표", "Table"], Subtotal: ["소계", "Subtotal"], Total: ["계", "Total"], Extra: ["덧줄", "Extra"], @@ -53,8 +54,8 @@ const TEXT = { Modal_NoData: ["표나 단가 자료가 없는 줄 — 수량 식만 있음", "No table or price data"], Modal_Hit: ["노란 줄 = 지금 넣은 값으로 걸린 줄", "Highlighted row matches the trial values"], Modal_NoHit: [ - "걸린 줄을 아직 못 맞힘 — 「시험 계산」 에 값을 넣고 다시 누르세요", - "No matching row yet — enter trial values and reopen", + "지금 값으로는 걸린 줄이 없음 — 값을 바꿔 보거나, 이 표를 안 쓰는 갈래일 수 있음", + "No row matches the current values — change them, or this table is on an unused branch", ], } as const satisfies Record;