From eb9082dc307b4de1e4f7056127d80d955d2c8c22 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 1 Oct 2026 13:13:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(m01-sl):=20=ED=91=9C=20=EC=83=81=EC=9E=90?= =?UTF-8?q?=20Table=20=EC=99=84=EC=84=B1=20=E2=80=94=20=EB=82=B4=20?= =?UTF-8?q?=ED=91=9C=20=EB=B6=99=EC=97=AC=EB=84=A3=EA=B8=B0=20=C2=B7=20?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EC=A1=B0=EA=B1=B4=20=C2=B7=20=EB=B9=88?= =?UTF-8?q?=EC=B9=B8=20=EB=AC=B4=EA=B4=80=20=C2=B7=20=EC=84=A0=20=EC=88=98?= =?UTF-8?q?=EC=8B=9D=20=EB=B3=B4=EC=A1=B4=20=C2=B7=20=EC=8B=9C=ED=97=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_019ACSGaHdLgnkEkoA4LDtMU --- .../SheetLogic/M01_SL_Table_Build.ts | 6 ++++- resources/tester/test_m01_sl_table_build.py | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts b/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts index 3e62745ac..8f8984437 100644 --- a/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts +++ b/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts @@ -159,7 +159,7 @@ export function planLookup(info: TblInfo, roles: TblRoles): TblPlan { const bl = blankable.slice(0, 2); const subsets = bl.length === 2 ? [[bl[0]], [bl[1]], [bl[0], bl[1]]] : bl.length === 1 ? [[bl[0]]] : []; - variants.push(...subsets); + variants.push(...subsets.filter((sub) => sub.length < exact.length)); // 모든 조건 칸이 빈칸인 변형은 열쇠가 없음 } const keyCols = exact.length ? variants : []; const kStart = W + 1; @@ -310,6 +310,8 @@ export function applyTable( if (si < 0) throw new Error("상자의 시트가 없음"); const plan = planLookup(info, roles); const sheet: Sheet = next.workbook.시트[si]; + // 이미 있던 입력 칸 내용(예시값 · 선을 그은 참조 수식)은 같은 이름이면 그대로 가져감 + const oldIn = Object.fromEntries(node.pillsIn.map((p) => [p.name, sheet.칸[p.cell]])); const keep = Object.entries(sheet.칸).filter( ([a]) => colIndex(a.replace(/\d+/g, "")) < plan.helperFrom, ); @@ -319,6 +321,8 @@ export function applyTable( Object.assign(sheet.칸, blockCells(info)); } Object.assign(sheet.칸, plan.cells); + for (const [name, cell] of Object.entries(plan.inCells)) + if (oldIn[name]) sheet.칸[cell] = oldIn[name]; const pills = pillsOf(info, plan, opts.outside); const gone = (dir: "in" | "out", name: string): boolean => !(dir === "in" ? pills.pillsIn : pills.pillsOut).some((p) => p.name === name); diff --git a/resources/tester/test_m01_sl_table_build.py b/resources/tester/test_m01_sl_table_build.py index c135e728c..2676fd680 100644 --- a/resources/tester/test_m01_sl_table_build.py +++ b/resources/tester/test_m01_sl_table_build.py @@ -36,6 +36,17 @@ const out = jobs.map((job) => { return { cells, plan }; } if (job.op === "paste") return m.parsePaste(job.text); + if (job.op === "reapply") { + const base = { key: null, name: "t", resultUnit: "원/㎡", edges: [], + workbook: { 종류: "통합문서", 판: 1, 열: "t", 서식: [{}], 시트: [{ id: "s1", 이름: "표", 칸: {} }] }, + nodes: [{ id: "n1", kind: "table", name: "표", sheetId: "s1", pillsIn: [], pillsOut: [], stage: 1 }] }; + const opts = { ref: "T1", fillBlock: true, outside: false }; + const first = m.applyTable(base, "n1", job.info, job.roles, opts).sl; + const cell = first.nodes[0].pillsIn[0].cell; + first.workbook.시트[0].칸[cell] = { 식: "'다른 시트'!B2" }; + const second = m.applyTable(first, "n1", job.info, job.roles, { ...opts, fillBlock: false }).sl; + return { cell, kept: second.workbook.시트[0].칸[cell], pills: second.nodes[0].pillsIn.map((p) => p.name), name: second.nodes[0].name }; + } return null; }); process.stdout.write(JSON.stringify(out)); @@ -173,3 +184,16 @@ def test_붙여넣기_읽기(): assert info["vals"] == {"할증": ""} assert info["rows"][1] == {"경사": "급", "범위": [10, None], "할증": 10} assert run([{"op": "paste", "text": "한 줄뿐"}])[0]["error"] + + +def test_다시_짜도_선_수식은_그대로(): + out = run([{"op": "reapply", "info": MINE, "roles": {"cond": ["경사"], "out": ["할증"]}}])[0] + assert out["kept"] == {"식": "'다른 시트'!B2"} + assert out["pills"] == ["경사"] and out["name"] == "표 · 견본" + + +def test_표가_비었어도_수식이_깨지지_않음(): + """내 표를 막 만든 때(줄이 다 빈칸) — 빈칸 변형 열쇠가 빈 수식이 되면 안 됨.""" + empty = {**MINE, "rows": [{}, {}]} + r = solve(empty, {"경사": "급"}, ["할증"]) + assert r["상태"] == "맞는 줄 없음" and r["할증"] == "#N/A"