"""면적산출 상세 오른쪽 일위대가 패널 · 칸 지정 모달(PLAN 39-4 D2 · D5 · D6) — 화면 조작은 ORCA. · 패널 = 그 열 일위대가 보기 갈래(`Labor_View`) · 조합 목록 · 찾기 · [이 열에 쓰기] · [조합 고치기] 없음 · 줄 누름 = 칸 지정 모달 → 스프레드시트 `setLink`(면적 문서 `연결` 한 곳 · 알약과 같은 길) · 시트 칸 값 · 연결이 바뀌면 `setQty` 로 바로 곱함(서버 안 부름) """ from __future__ import annotations from pathlib import Path ROOT = Path(__file__).resolve().parents[2] M02 = ROOT / "M02_MasterTemplete" def _text(name: str) -> str: return (M02 / name).read_text(encoding="utf-8") def test_면적_길은_일위대가_패널(): main = _text("M02_MasterTemplete_UI_Main.ts") body = main.split("async function mountSheet")[1].split("async function remount")[0] area = body.split('if (kind === "area") {')[1].split("\n }\n")[0] assert "apanel = buildAreaPanel({" in area and "host.append(apanel.root);" in area assert "picked: () => picked," in area and "pills: () => links.항목," in area assert "apanel?.refresh();" in body and "onSelect: (s) => (picked = s.칸)," in body assert "buildComboBar" not in main # 조합 줄 · [조합 고치기] 지움 assert body.index('if (at.layer === "project") return;') < body.index("buildAreaPanel") assert "apanel?.destroy();\n apanel = null;" in main # drop def test_패널_몸은_보기_갈래_줄_누름은_칸_지정(): panel = _text("M02_MasterTemplete_AreaPanel.ts") assert "createLaborView(openLink)" in panel assert "void view.show(opts.layer, opts.column, quantities(opts.sheet()));" in panel assert "view.setQty(quantities(opts.sheet()), cellsOf(opts.sheet()))" in panel assert panel.index("void view.show(") < panel.index("refresh(); // 칸 주소") assert "openAreaLink({" in panel and "if (!row.pill) return showToast(TEXT.noPill" in panel for gone in ("fetchUsableCombos", "setColumnCombo", "branchCombo"): assert gone not in panel def test_칸_지정_모달은_setLink_만(): link = _text("M02_MasterTemplete_AreaLink.ts") assert 'opts.sheet.setLink(opts.key, a1, opts.info, choice === "바꾸기")' in link assert ( "linksAt(book, sheetId, a1, opts.key)" in link and "askShare(" in link ) # 칸 하나 알약 여럿 assert "done(here)" in link and "done(address.input.value)" in link and "done(null)" in link assert "address.setError(TEXT.bad)" in link # 틀린 · 빈 주소는 모달 그대로 assert "pick.disabled = link.disabled = address.input.disabled = opts.readOnly;" in link assert "saveTemplate" not in link # 저장은 [저장] def test_빈_칸은_수량_없음_서버와_같음(): """39-8 ② — 빈 칸에 이은 알약 = 수량 없음(반영 「—」 · 「수량 A1 = 빈 칸」) · 0 칸 = 0 · 값 칸 = 값.""" from M02_MasterTemplete import M02_Labor_Doc as labor doc = { "시트": [ { "id": "s1", "칸": {"A2": {"값": 0}, "A3": {"값": "1,250"}, "A4": {"값": "글"}}, "연결": [ {"키": k, "칸": c} for k, c in (("빈", "A1"), ("영", "A2"), ("값", "A3"), ("글", "A4")) ], } ] } assert {k: float(v) for k, v in labor.area_values(doc).items()} == {"영": 0, "값": 1250} panel = _text("M02_MasterTemplete_AreaPanel.ts") body = panel.split("function quantities")[1].split("\n}\n")[0] assert 'toNumber(cell?.식 !== undefined ? (v && "수" in v ? v.수 : null) : cell?.값)' in body assert 'String(raw).replace(/,/g, "").trim()' in panel and 'typeof raw === "boolean"' in panel sheet = (ROOT / "A00_Common" / "spreadsheet" / "spreadsheet.ts").read_text(encoding="utf-8") assert 'const empty = !cell || (cell.식 === undefined && (cell.값 ?? "") === "");' in sheet rows = _text("M02_MasterTemplete_Labor_Rows.ts") assert '${tc("Labor_Qty")} ${at} = ${tc("In_QtyEmpty")}' in rows assert "if (!value) return `${link.칸} = ${TEXT.empty}`;" in _text( "M02_MasterTemplete_AreaLink.ts" ) def test_카드_알약_취향은_계정_받은_뒤_올림(): """39-8 ③ — 화면이 뜰 때 브라우저 옛 값이 계정 값을 덮지 않게 · 카드는 열 때마다 취향을 다시 읽음.""" state = (ROOT / "A00_Common" / "b_page_state.ts").read_text(encoding="utf-8") push = state.split("function pushUiPrefs")[1].split("export function syncUiPrefs")[0] assert "void (prefsPull ?? Promise.resolve())" in push and "prefs: localPrefs()" in push assert "prefsPull = pullUiPrefs();" in state cards = _text("M02_MasterTemplete_ItemCards.ts") load = cards.split("async function load(")[1] assert 'paintPills(readStateRaw("m02-card-pills") === "true");' in load def test_엑셀_칸_고르기_모달_부품(): """40-3 ① · 43-1 — 읽기만 시트(수량산출 모달 안에 바로 · `mountAreaSheet`) · 칸 누름 = 주소 · 풀기 · 손 · 확인 · 닫기는 일위대가 수량산출 모달(`Labor_QtyModal`) · 저장 안 함.""" pick = _text("M02_MasterTemplete_AreaCellPick.ts") assert 'export { linkAreaCell } from "../A00_Common/spreadsheet/spreadsheet_link_set";' in pick assert "export function mountAreaSheet(" in pick and "readOnly: true," in pick assert 'opts.onPick(choice === "바꾸기" ? { 칸: at, 바꾸기: true } : { 칸: at });' in pick qty = _text("M02_MasterTemplete_Labor_QtyModal.ts") assert "sheet = mountAreaSheet(host, {" in qty and 'pending = { kind: "unlink" };' in qty assert "엑셀에서 고르기" not in qty # 두 단계 모달 없앰(43-1) assert "linksAt(book, sheetId, at, opts.pill)" in pick and "askShare(" in pick assert "if ((e as MouseEvent).button === 0 && moved) void choose();" in pick assert "handle?.destroy();" in pick assert "saveTemplate" not in pick