- 새 공용 부품 ui_template_hopyo_grid(.ts · .css) — 호표 머리 줄(이름 · 규격 · 단위 · 1 단위 합계) · 구성 줄(품명 · 규격 · 수량 · 단위 · 합계 · 노무비 · 재료비 · 경비 단가 / 금액 · 비고 · 구분 꼬리표) · 합계 줄 빈 줄 붉게 · 부품 모음 견본 칸 - M02 Labor_Grid — 문서 + 서버 calc 답 그대로(단가 = 기준 ÷ 배 · 금액 = 반영 · 합계 = 반영합 · 수량 없음 줄 = 빈 줄) · Labor_Detail 맨 위 격자(카드 · 상세 같이) · 읽기만 카드 = 격자만 - B09 일위대가표(호표보기 · 박스보기 바탕 줄) = 같은 부품 · 줄 값 · 이동 · 줄 꼴 그대로 + 구분 칸 · 머리 1 단위 합계 - 시험: test_704_3 새로 · B09 표 시험 다섯(155 · 157-4 · 201-1 · 202-1 · 304)은 격자 파일을 이어 붙이게 고침(줄 글이 공용 부품으로 옮김) · hopyo_screen 상세 줄 수 693 → 700 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
4.0 KiB
Python
86 lines
4.0 KiB
Python
"""157-4 B09 일위대가표 품셈 규격 — 기계 · 직종 원문번호 → 「이름 규격」 · 번호 = 풍선(157-3 `raw`).
|
|
|
|
- 품셈 블록 `spec` = 「기계 굴착기(무한궤도) 0.6㎥ · …」 · `spec_raw` = 옛 글(번호) · 줄 그대로
|
|
- 일위대가목록표 · 일위대가표 규격 칸 풍선 = 「원문번호 …」(바꾼 칸만)
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B09_Estimation import B09_Estimation_Labor_Source as source # noqa: E402
|
|
from M02_MasterTemplete import M02_Item_Prices as item_prices # noqa: E402
|
|
|
|
B09 = ROOT / "B09_Estimation"
|
|
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
|
|
|
|
|
|
def _block(code, given):
|
|
whole = item_prices.load_prices(item_prices.to_json(item_prices.collect([code])))
|
|
builder = source.Builder()
|
|
bid = builder.poomsem(whole, {"로직": code, "입력값": given})
|
|
return builder.blocks[bid]
|
|
|
|
|
|
def test_기계_번호는_이름_규격_번호는_spec_raw():
|
|
given = {"기계": "0201-0060", "지역": "전국평균", "보정작업": "해당 없음"}
|
|
block = _block("GC000267", given)
|
|
assert block["spec"] == "기계 굴착기(무한궤도) 0.6㎥ · 보정작업 해당 없음 · 지역 전국평균"
|
|
assert block["spec_raw"] == "기계 0201-0060 · 보정작업 해당 없음 · 지역 전국평균"
|
|
assert len(block["lines"]) == 4
|
|
|
|
|
|
def test_직종_번호도_이름():
|
|
given = {"기계": "2106-0002", "지역": "전국평균", "조종원직종": "1048"}
|
|
block = _block("GC000275", given)
|
|
assert "1048" in block["spec_raw"] and "1048" not in block["spec"]
|
|
assert not re.search(r"\d{4}-\d{4}", block["spec"])
|
|
|
|
|
|
def test_규격_칸_풍선():
|
|
spec, raw = "기계 굴착기(무한궤도) 0.6㎥", "기계 0201-0060"
|
|
sheets = {
|
|
"daega": [{"no": 1, "key": "DG:a", "kind": "품셈", "name": "n", "spec": spec,
|
|
"spec_raw": raw, "unit": "시간"}],
|
|
"daega_blocks": [{"id": "DG:a", "no": 1, "kind": "품셈", "name": "n", "spec": spec,
|
|
"spec_raw": raw, "unit": "시간", "cite": "", "lines": [
|
|
{"kind": "daega", "key": "DG:a", "name": "n", "spec": spec,
|
|
"spec_raw": raw, "qty": 1, "unit": "시간", "price": 1,
|
|
"amount": 1, "prices": {"labor": 1, "material": 0, "expense": 0},
|
|
"amounts": {"labor": 1, "material": 0, "expense": 0}}],
|
|
"labor": 1, "material": 0, "expense": 0, "sum": 1}],
|
|
} # fmt: skip
|
|
sangeun = (B09 / "B09_Estimation_UI_Tabs_Sangeun.ts").read_text("utf-8")
|
|
grid = IMPORTS.sub("", (ROOT / "ui_template/ui_template_hopyo_grid.ts").read_text("utf-8"))
|
|
src = (
|
|
grid
|
|
+ re.search(r"^export const raw =.*?;$", sangeun, re.MULTILINE | re.DOTALL)[0]
|
|
+ "".join(
|
|
IMPORTS.sub("", (B09 / f"B09_Estimation_{n}.ts").read_text("utf-8"))
|
|
for n in ("UI_Tabs", "UI_Tabs_Labor")
|
|
)
|
|
) # 산근 탭의 풍선 함수 `raw` 만(같은 이름 상수가 겹쳐 파일째는 못 잇음)
|
|
body = f"""
|
|
const sheets = {json.dumps(sheets, ensure_ascii=False)};
|
|
const list = buildDaegaList({{ sheets }});
|
|
const blocks = buildDaegaBlocks({{ sheets }});
|
|
console.log(JSON.stringify({{
|
|
list: list.tip(0, "spec"), listName: list.tip(0, "name"),
|
|
head: blocks.tip(0, "spec"), line: blocks.tip(1, "spec"), total: blocks.tip(2, "spec"),
|
|
}}));"""
|
|
result = subprocess.run(
|
|
["node", "--experimental-strip-types", "--no-warnings",
|
|
"--input-type=module-typescript", "-e", src + body],
|
|
capture_output=True, text=True, encoding="utf-8", timeout=60,
|
|
) # fmt: skip
|
|
assert result.returncode == 0, result.stderr
|
|
got = json.loads(result.stdout)
|
|
tip = "원문번호 기계 0201-0060"
|
|
assert got == {"list": tip, "listName": None, "head": tip, "line": tip, "total": None}
|