- 새 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>
175 lines
9.3 KiB
Python
175 lines
9.3 KiB
Python
"""실무문서 단가산출 → 줄 판정 · 확인한 블록만 단위 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)
|