- 719-4 근거 탭 = 산근 블록마다 카드 · 공용 calcSheetRows 묶음 꼴(인력 · 재료 · 경비(장비 한 줄 + 중기사용료 펼침 넉 줄) · 하위 = 산근 n호표 한 줄) · 서버 줄 덧칸 sheet{쓰는입력 · 기준단위 · 환산근거}(master_sheet_line 한 벌) · 블록 기준단위 · 입력 · 중간값 글은 고치기 펼침 때 · 옛 풀이표는 곁 창에만
- 719-3 산근 덮개 sg_cover {산근 키: {「n:이름」: {qty · 노 · 재 · 경 단가}}} · 셈 짝 B09_Estimation_Sangeun_Cover.py ↔ .ts(서버 풀이 뒤 덧씌움 · 하위 산근 이어짐 · % 덧줄 밑수 비례 · 총계 · 목록표 · 빈칸 목록 · raw 로 두 번 덮어도 같음 · 짝 잃음 sg_orphans 알림)
- 고침 표(702 꼴) = 품 · 단가 두 번 눌러 고침 · 고친 칸 색 · 원래 값 칸 · 「이 공사에서 고침」 꼬리표 · 우클릭 · 카드 단추 [단가산출 값으로] · 고친 뒤 보던 자리 지킴
- 저장 다듬기 sg_cover · [업데이트] 알림 고친 칸 수에 산근 칸 · 셈 길(compute_sheets) · 화면 짝(computeSheets) 둘 다 덮개
- 금액 전후 같음 — 덮개 없음 = 서버 풀이 그대로 · 덧칸 뺀 옛 풀이와 13 블록 같음 · 일위대가 합 검증_랩탑메인 13,662,560 · 검증_sub5 10,149,562 그대로
- 시험 새로 test_719_3_sangeun_cover(8 · 거울 · 대가 · 원가계산서 · 업데이트 뒤 지킴) · test_719_4_sangeun_sheet(2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
107 lines
4.5 KiB
Python
107 lines
4.5 KiB
Python
"""719-4 B09 단가산출근거 = 공용 계산서 꼴 — 서버 줄 덧칸 · 화면 묶음 옮김(셈 없음) · 금액 같음.
|
|
|
|
- 서버 산근 줄 `sheet`{쓰는입력 · 기준단위 · 환산근거}(714-1 `master_sheet_line` 한 벌) ·
|
|
블록 `기준단위`
|
|
- 화면 `sheetGroups` = 노무비 · 재료비 · 경비 묶음(소계 = 블록 비목 총계 · 줄 금액 = 서버 값) →
|
|
공용 `bundleLines` = 인력 · 재료 · 경비(장비 한 줄 + 펼침 = 중기사용료 넉 줄) · 하위(산근 n호표)
|
|
- 덧칸만 더함 — 줄 값 · 총계 · 목록표는 덧칸 빼면 옛 풀이와 같음(`text` 줄 셈 그대로)
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
for p in (ROOT, ROOT / "resources" / "tester"):
|
|
if str(p) not in sys.path:
|
|
sys.path.insert(0, str(p))
|
|
|
|
from test_719_3_sangeun_cover import _inputs, _user # noqa: E402
|
|
|
|
from B09_Estimation import B09_Estimation_Sangeun as sangeun # noqa: E402
|
|
from B09_Estimation.B09_Estimation_Calc_Base import compute_base # noqa: E402
|
|
|
|
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def made():
|
|
sheets = compute_base(_inputs(), {})
|
|
return sheets, sangeun.collect([], sheets, sangeun.hand_sources(_user()))
|
|
|
|
|
|
def _ts(sg, machines):
|
|
files = [
|
|
"B09_Estimation/B09_Estimation_Calc_Base.ts",
|
|
"A00_Common/calc_sheet.ts",
|
|
"B09_Estimation/B09_Estimation_UI_Tabs_Sangeun_Sheet.ts",
|
|
]
|
|
src = "".join(IMPORTS.sub("", (ROOT / f).read_text("utf-8")) for f in files)
|
|
body = (
|
|
"\nconst currentLanguageIndex = 0;"
|
|
f"\nconst sg = {json.dumps(sg, ensure_ascii=False)};"
|
|
f"\nconst machines = {json.dumps(machines, ensure_ascii=False)};"
|
|
"\nconst noOf = Object.fromEntries(sg.map((b) => [b.key, b.no]));"
|
|
"\nconsole.log(JSON.stringify(sg.map((b) => {"
|
|
"\n const groups = sheetGroups(b, machines, noOf);"
|
|
"\n return { key: b.key, groups, bundles: bundleLines(groups) };"
|
|
"\n})));"
|
|
)
|
|
result = subprocess.run(
|
|
["node", "--experimental-strip-types", "--no-warnings",
|
|
"--input-type=module-typescript", "-"],
|
|
input=src + body, capture_output=True, text=True, encoding="utf-8", timeout=60,
|
|
) # fmt: skip
|
|
assert result.returncode == 0, result.stderr
|
|
return {x["key"]: x for x in json.loads(result.stdout)}
|
|
|
|
|
|
def test_서버_줄_덧칸(made):
|
|
_sheets, sg = made
|
|
for b in sg["sg"]:
|
|
assert "기준단위" in b
|
|
for ln in b["lines"]:
|
|
assert set(ln["sheet"]) >= {"쓰는입력", "기준단위", "환산근거"}, ln
|
|
assert "_goods" not in ln
|
|
net = next(b for b in sg["sg"] if b["logic"] == "GC000337")
|
|
assert net["기준단위"] == "㎡"
|
|
machine = next(ln for ln in net["lines"] if ln["kind"] == "machine")
|
|
assert "크레인" in machine["sheet"]["쓰는입력"]
|
|
|
|
|
|
def test_화면_묶음_옮김_금액_그대로(made):
|
|
sheets, sg = made
|
|
got = _ts(sg["sg"], sheets["machine_blocks"])
|
|
for b in sg["sg"]:
|
|
groups = got[b["key"]]["groups"]
|
|
cost = {"노무비": "labor", "재료비": "material", "경비": "expense"}
|
|
for g in groups:
|
|
part = cost[g["비목"]]
|
|
assert g["소계"] == b[part]
|
|
assert [x["금액"] for x in g["줄"]] == [
|
|
ln[part] for ln in b["lines"] if part in _parts(ln)
|
|
]
|
|
net = next(b for b in sg["sg"] if b["logic"] == "GC000337")
|
|
bundles = {x["묶음"]: x for x in got[net["key"]]["bundles"]}
|
|
assert set(bundles) == {"인력", "재료", "경비"}
|
|
equip = next(x for x in bundles["경비"]["줄"] if "몫" in x)
|
|
assert len(equip["몫"]) == 3 # 장비 한 줄 = 노 · 재 · 경
|
|
unfold = equip["몫"][0]["줄"]["펼침"]
|
|
assert unfold["단위"] == "시간" and len(unfold["줄"]) == 4 # 손료 · 운전사 · 연료 · 잡품
|
|
parent = next(b for b in sg["sg"] if b["logic"] == "GF000169")
|
|
sub = {x["묶음"]: x for x in got[parent["key"]]["bundles"]}["하위"]
|
|
assert all("산근" in x["이름"] and "호표" in x["이름"] for x in sub["줄"])
|
|
|
|
|
|
def _parts(ln):
|
|
if ln["kind"] == "labor":
|
|
return ["labor"]
|
|
if ln["kind"] in ("machine", "sangeun"):
|
|
return ["labor", "material", "expense"]
|
|
own = [p for p in ln.get("price") or {} if p in ("labor", "material", "expense")]
|
|
return own or [p for p in ("labor", "material", "expense") if ln.get(p) is None] or ["material"]
|