Files
Aislo/resources/tester/test_729_hopyo_sk_link.py
T
eomsangdonandClaude Opus 5.5 4cdb41f80b feat(778-5): 블록 = 줄 판정 모음 · 확인한 블록만 단위 SK(바탕 없음 · 근거 꼬리표) · 확인 목록 받는 길(D-3 · 요구 623 · 624)
- 새 M02_Hopyo_SkUnit: 줄 판정(Units 잇기 + 매개값 · 사용자 고름 {차례: 노임|기계|자재|기준|뺌} · 꼬리표 「내가 고름」) · 단가 = M01 지금 값으로 다시 셈(우리금액) · 엑셀 금액 차 = 참고 · 블록 판정 「옮길 수 있음」 | 「고를 줄 n」 · 단위 SK 더한줄 = 인력 · 기계경비 로직 줄 · 재료(손료율 든 수량 · 별도계상) · 율 줄(778-4 꼴 · 줄 목록 = 그 행 add:#k) · 하위 참조 · 값 고정 줄 · 저장 꼴로 다듬어 같은 내용 SK 지문 같음
- Sangeun: 기초 · SK · 고름 대기 갈림(SkPatch.plan) 걷고 단위 SK 하나 · 확인 [{번, 고름?}] 에 든 블록만 만듦 · 하위 단산 먼저 · 고를 줄 남으면 못만듦 · 일위대가 만들기가 부르는 Maker.plan(번) = 「확인 전」 고름 대기(자동 없음) · 별칭 제안 걷음(바탕 기초 없음)
- Router: sangeun 몸 바탕 → 확인(줄 수 그대로)
- 시험 새 뜻: test_729(기초 · SK · 고름 대기 · 계수 · 못옮김 잇기 · 729-5 차이 SK 바탕 길 = 바탕 없는 단위 SK 로 사라짐 → 확인 블록 단위 SK · 줄 고름 · 뺌으로 갈음) · test_718_1 · test_572 · test_779_2 · test_m02_hopyo_flow(확인 목록을 보내야 SK 생김 · 같은 파일 안 같은 내용 단위 SK 없을 수 있음 · 바탕 없는 SK = SC 에 참조 그대로)
- 새 test_778_5: 작업반장 = 건설업 · 규격 차례 무관 · 글자 규격 = 후보 · 손료율 · 설치비 · 제경비 율 줄 · 여덟 파일 줄 연결 표 · 확인 안 한 블록 안 만듦 · 고름 · 400 · 404 · 다시 올림 같은 SK 재사용 · 일위대가 확인 전 못 이음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 14:16:12 +09:00

175 lines
9.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""실무문서 단가산출 → 줄 판정 · 확인한 블록만 단위 SK · 일위대가 이음 · 남은 줄 막음(PLAN 729-1 · 729-2 ·
778-5 D-3 · 요구 623).
북부청 임도(실물 엑셀)를 임시 층에 올려 맞춤셈 → [단가산출](확인 없음) = 판정만 · SK 0 →
기초잡석채집 · 운반 · 다짐 · 철근가공조립 Type-Ⅰ · Type-Ⅱ · 구조물되메우기 확인 = 바탕 없는 단위 SK(줄 = 엑셀 품 그대로 ·
인력 · 기계경비 로직 줄 · 율 줄 = 줄 목록 · 별도계상 철근 = 0 원) · 근거 꼬리표 = 맞춤셈 1위 ·
확인 안 한 단가산출 줄 = 일위대가 남은 줄(확인 전) · 확인한 블록 = SK 참조로 펼쳐 담음 ·
고를 줄 남은 블록(도장) = 못만듦 → 줄마다 고름 · 뺌 = 만듦 · 노임 = 이름 이음 · 기계 = EQ 이음 · master_data 원본 그대로.
층 = 임시 폴더.
"""
import json
from M01_MasterData import M01_MasterData_Store as m01
from M02_MasterTemplete import M02_Hopyo_Fit as fit
from M02_MasterTemplete import M02_Hopyo_Fixed as hfix
from M02_MasterTemplete import M02_Hopyo_Ours as ours
from M02_MasterTemplete import M02_Hopyo_Sangeun as sangeun
from M02_MasterTemplete import M02_Hopyo_SkUnit as skunit
from M02_MasterTemplete import M02_Hopyo_Store as hs
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete import M02_Template_Layers as layers
from resources.tester.test_m02_hopyo_excel import URL, _post, client, master_hash # noqa: F401
BUILD = "/api/m02/layers/personal/hopyo-build"
LEFT = "/api/m02/layers/personal/hopyo-unlinked"
SAMPLES = { # 블록 번 → 이름 앞
33: "기초잡석채집,운반,다짐",
47: "철근가공조립(현장)", # Type-Ⅰ(소량 시공) — 철근공 0.67 + 3.53 · 보통인부 0.22 + 1.18
72: "철근가공조립(현장)", # Type-ll
12: "구조물되메우기(인력100%)",
}
def _sk_docs(folder) -> dict[int, dict]:
out = {}
for path in sorted((folder / "sangeun").glob("SK-*.json")):
doc = json.loads(path.read_bytes())
for one in [doc["본뜬"], *doc["본뜬"].get("또", [])]: # 또 = 같은 내용 SK 를 쓴 블록(733-1)
out[int(one["키"].rsplit("단산", 1)[1])] = doc
return out
def _caller(folder, key: str, no: int) -> int:
"""그 블록을 부르는 첫 호표 번."""
return sangeun.calls(hs.read(folder, key))[1][no]["_호표"]
def test_실무_단가산출_확인한_블록만_단위_SK_일위대가_막음(client):
before = master_hash()
folder = layers.personal_dir(7, 42)
key = _post(client).json()["키"]
url = f"{URL.format('personal')}/{key}/sangeun"
assert client.post(url).status_code == 409 # 맞춤 셈 전
fit.run_job(folder, key)
got = client.post(url, json={}).json()
assert got["만듦"] == [] and not _sk_docs(folder) # 확인 없음 = 판정만
rows = {r["번"]: r for r in got["판정"]}
assert all(rows[n]["판정"] == skunit.GOOD for n in SAMPLES)
sure = {"확인": [{"번": n} for n in SAMPLES]}
made = client.post(url, json=sure).json()
assert sorted(x["번"] for x in made["만듦"]) == sorted(SAMPLES) and made["못만듦"] == []
docs = _sk_docs(folder)
for no, head in SAMPLES.items():
doc = docs[no]
assert doc["이름"].startswith(head) and doc["바탕"] is None, no
assert doc["본뜬"]["갈래"] == skunit.UNIT and doc["본뜬"]["근거"] == rows[no]["근거"]
mine = [x["수량"] for x in doc["더한줄"] if x.get("종류") == "인력"] # 엑셀 품 그대로
excel = [r["매개값"]["품"] for r in rows[no]["줄"] if r["묶음"] == "L"]
assert mine == excel, no
assert docs[12]["더한줄"] == [
{
"종류": "인력",
"요소": "LB000002",
"비목": "노무비",
"이름": "보통인부",
"단위": "인",
"수량": 0.1,
"처음수량": 0.1,
}
]
dump = [x for x in docs[33]["더한줄"] if x.get("로직") == "GC000268"]
assert any("덤프트럭" in ours.eq_of(_eq(x))[0] for x in dump) # 기계 = 기계경비 로직 줄
rates = [x for x in docs[47]["더한줄"] if (x.get("고정") or {}).get("비율")]
assert [x["고정"]["비율"]["기준"] for x in rates] == [
["add:#1", "add:#2"],
["add:#5", "add:#6"],
]
assert (
docs[47]["더한줄"][0]["이름"] == "철근" and docs[47]["더한줄"][0]["고정"]
) # 별도계상 0 원
again = client.post(url, json={}).json() # 다시 = 그대로
assert again["만듦"] == [] and again["그대로"] == len(SAMPLES)
# 일위대가 — 확인 안 한 단가산출 줄 = 남은 줄(확인 전) · 확인한 블록 = SK 펼쳐 담음
no = _caller(folder, key, 12)
body = {"층": "personal", "키": key, "번": no, "줄": []}
left = client.post(LEFT, json=body).json()
res = client.post(BUILD, json=body)
assert res.status_code == 409 and left["수"] > 0
assert res.json()["detail"]["남은줄"] == left["남은줄"]
assert not any(x.get("단산") == 12 for x in left["남은줄"])
assert not list((folder / "precombo").glob("SC-*.json")) # 막힘 = 저장 0
picks = [{"차례": x["차례"], "뺌": "우리에 없음"} for x in left["남은줄"]]
ok = client.post(BUILD, json={**body, "줄": picks})
assert ok.status_code == 200, ok.text
doc = json.loads((folder / "precombo" / f"{ok.json()['참조']['키']}.json").read_bytes())
marks = {(x.get("본뜬") or x.get("참조") or {}).get("키") for x in doc["담은로직"]}
assert docs[12]["키"] in marks # 단가산출 줄 = SK(바탕 없는 SK = 참조 그대로 · 740-1)
assert client.post(LEFT, json={**body, "줄": picks}).json()["수"] == 0
wait = _caller(folder, key, 2) # 확인 안 한 블록을 부르는 호표
rest = client.post(LEFT, json={"층": "personal", "키": key, "번": wait, "줄": []}).json()
assert any(x.get("까닭") == sangeun.WAIT_WHY for x in rest["남은줄"])
assert master_hash() == before
def test_고를_줄_남으면_못만듦_줄마다_고름_뺌(client):
"""778-5 — 도장(엑셀 자재 넷 = 글자 규격 · 후보) · 확인만 = 못만듦 · 줄마다 고름 · 뺌 = 만듦 ·
틀린 고름 400 · 고른 줄 꼬리표 「내가 고름」."""
before = master_hash()
folder = layers.personal_dir(7, 42)
key = _post(client).json()["키"]
url = f"{URL.format('personal')}/{key}/sangeun"
fit.run_job(folder, key)
rows = {r["번"]: r for r in client.post(url, json={}).json()["판정"]}
paint = rows[95]
picks = [r for r in paint["줄"] if r["상태"] != "이어짐"]
assert paint["판정"] == "고를 줄 4" and [r["묶음"] for r in picks] == ["M"] * 4
only = client.post(url, json={"확인": [{"번": 95}]}).json()
assert only["만듦"] == [] and only["못만듦"] == [
{"번": 95, "이름": paint["이름"], "까닭": "고를 줄 4"}
]
def sure(chosen: dict) -> object:
return client.post(url, json={"확인": [{"번": 95, "고름": chosen}]})
first = picks[0]
assert sure({str(first["차례"]): {"기계": "EQ000001"}}).status_code == 400
assert sure({str(first["차례"]): {"자재": "MT999999"}}).status_code == 400
code = first["후보"][0]["자재"]
chosen = {str(r["차례"]): {"뺌": "현장 지급"} for r in picks[1:]}
done = sure({str(first["차례"]): {"자재": code}, **chosen}).json()
assert [x["번"] for x in done["만듦"]] == [95]
doc = _sk_docs(folder)[95]
mat = [x for x in doc["더한줄"] if x.get("종류") == "재료"]
assert [(x["요소"], x["수량"]) for x in mat] == [(code, 0.322)]
mine = next(r for r in done["판정"] if r["번"] == 95)["줄"]
assert all("내가 고름" in r["꼬리표"] for r in mine if r["차례"] in {p["차례"] for p in picks})
assert [r["상태"] for r in mine if r["차례"] in {p["차례"] for p in picks[1:]}] == ["뺌"] * 3
assert master_hash() == before
def _eq(one: dict) -> str:
return str((one.get("입력값") or {}).get("기계") or "")
def test_노임_이름_이음_기계_EQ_이음_사용자_이음(client):
with store.at(layers.personal_dir(7, 42)):
linker = hfix.Linker(by_name=True)
labor = {"갈래": "노임", "이름": "보통인부", "단위": "인"}
price = {"노무비": hfix.Decimal(1), "재료비": hfix.Decimal(0), "경비": hfix.Decimal(0)}
assert linker.link(labor, price)["노임"] == "LB000002" # 단가 달라도 이름으로
machine = {"갈래": "기계", "이름": "덤프트럭", "규격": "15ton", "단위": "hr"}
code = linker.link(machine, price)["기계"]
assert code and "덤프트럭" in ours.eq_of(_eq_code(code))[0]
line = {"갈래": "자재", "이름": "없는자재", "규격": "", "단위": "개", "수량": 2,
"단가": {"재료비": 10}, "차례": 0} # fmt: skip
one = hfix.line_of(line, [], {"파일": "x", "호표": 1}, set(), linker, {"노임": "LB000002"})
assert one["고정"]["이음"] == {"노임": "LB000002", "자재": None, "기계": None}
assert hfix.linked(one) and not hfix.linked(hfix.line_of(line, [], {}, set(), linker))
def _eq_code(key: str) -> str:
"""EQ 키(EQ000001) → 기계 번호(0101-0007)."""
return next(k for k, v in m01.loaded()[1].numbers["EQ"].items() if v.get("키") == key)