- 새 파일 `M02_MasterTemplete_Labor_National`: 사전조합 `본뜬.전국자재` 를 자재목록 아래 표로(코드 「전국」 · 이름 · 규격 · 수량 · 단위 · 세 칸 글 · 「지역은 프로젝트에서」 · 금액 칸 비움 · 단추 없음) · 없으면 안 그림 · 셈 · 문서 꼴 · 저장 길 그대로 - 상세(`Labor_Detail`) 자재 칸에 부르는 줄 하나 · `Labor_Api` 본뜬 타입에 `전국자재` 칸 - 확인 창 자재 표 세 칸: 서버가 대상마다 `세칸`(`Material.marks` · 만들기와 같은 값)을 실음 → 전국-관급 줄 = 관급(옛 기본값 「사급」) - 시험: 전국 자재 줄 있는 것 · 없는 것 · 단추 없음 · 글 명사형 · 읽기 길 · 세 칸 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XWh5wNNycMkbwMpACz1rte
429 lines
23 KiB
Python
429 lines
23 KiB
Python
"""일위대가 실무자료 맞춤(PLAN 324-2 · `M02_Hopyo_Match`) — 규격 → 입력값 · 단위 · 환산 ·
|
||
맞춤 차례 · 기억 우선 · 길(보기 · 미리 채움 · 확정) · master_data 원본 그대로. 층은 임시 폴더.
|
||
"""
|
||
|
||
import pytest
|
||
|
||
from M02_MasterTemplete import M02_Hopyo_Fit as fit
|
||
from M02_MasterTemplete import M02_Hopyo_Match as hm
|
||
from M02_MasterTemplete import M02_Template_Layers as layers
|
||
|
||
# 올리기 길 시험의 `client` 마당을 같이 씀
|
||
# ruff: noqa: F811
|
||
from resources.tester.test_m02_hopyo_excel import URL, _post, client, master_hash # noqa: F401
|
||
|
||
mm = hm.mm
|
||
|
||
|
||
# ── 규격 · 단위 · 환산 ─────────────────────────────────────────────────
|
||
def test_규격_이름_붙은_수():
|
||
assert hm.numbers("D=1,000mm,일반형") == {"D": (1000, "mm")}
|
||
assert hm.numbers("H:1.2m") == {"H": (hm.Decimal("1.2"), "m")}
|
||
assert {k: float(v) for k, (v, _) in hm.numbers("B=2.3,H=0.4,야면석").items()} == {
|
||
"B": 2.3,
|
||
"H": 0.4,
|
||
}
|
||
assert hm.numbers("L3=35cm적용,장비사용") == {"L3": (35, "cm")}
|
||
assert hm.numbers("Φ40×200") == {} and mm.plain_spec("Φ40×200") == "ø40x200"
|
||
|
||
|
||
def test_단위_정규화():
|
||
got = [mm.plain_unit(u) for u in ("㎥", "M", "Ton", "TON", "EA", "㎡")]
|
||
assert got == ["m3", "m", "톤", "톤", "개", "m2"]
|
||
|
||
|
||
@pytest.mark.parametrize(
|
||
("have", "want", "spec", "times", "kind"),
|
||
[
|
||
("㎥", "㎥", "", 1, "같음"),
|
||
("M", "m", "", 1, "같음"),
|
||
("kg", "ton", "", 0.001, "단위표"),
|
||
("㎡", "㎥", "T=150mm", 0.15, "추정"),
|
||
("M", "㎡", "B=2.3,H=0.4", 2.3, "추정"),
|
||
("M", "㎥", "B=2.3,T=0.2", 0.46, "추정"),
|
||
("개소", "㎥", "", None, "입력"),
|
||
("㎥", "㎡ · ㎥", "", 1, "두 단위"),
|
||
("M", "㎡ · ㎥", "", None, "입력"),
|
||
],
|
||
)
|
||
def test_환산(have, want, spec, times, kind):
|
||
assert hm.factor(have, want, spec) == {"배": times, "갈래": kind}
|
||
|
||
|
||
SPECS = [
|
||
{"이름": "뒷길이", "단위": "㎝", "고르기": [25, 35, 45], "자동값": None},
|
||
{"이름": "관경", "단위": "mm", "자동값": None},
|
||
{"이름": "설치높이", "단위": "m", "자동값": None},
|
||
{
|
||
"이름": "기계",
|
||
"고르기": ["0201-0020", "0201-0060"],
|
||
"고르기이름": {
|
||
"0201-0020": "굴착기(무한궤도) 0.2㎥",
|
||
"0201-0060": "굴착기(무한궤도) 0.6㎥",
|
||
},
|
||
"자동값": None,
|
||
},
|
||
{"이름": "횟수", "고르기": ["3회사용시", "4회사용시"], "자동값": None},
|
||
{"이름": "단위당", "고르기": ["㎡당", "㎥당"], "자동값": None},
|
||
{"이름": "지역", "고르기": ["전국평균", "서울"], "자동값": "전국평균"},
|
||
{"이름": "보정작업", "고르기": ["해당 없음", "전석섞인토사"], "자동값": None},
|
||
]
|
||
|
||
|
||
def test_규격에서_입력값():
|
||
line = {"이름": "찰붙임(4회)", "규격": "L3=35cm적용,굴삭기0.6㎥,D=1,000mm,H:1.2m", "단위": "㎥"}
|
||
values, unread = hm.spec_inputs(line, SPECS)
|
||
assert values == {
|
||
"뒷길이": 35,
|
||
"관경": 1000,
|
||
"설치높이": 1.2,
|
||
"기계": "0201-0060",
|
||
"횟수": "4회사용시",
|
||
"단위당": "㎥당",
|
||
}
|
||
assert unread == ["보정작업"] # 지역 = 자동값 · 못 읽은 것만 표시
|
||
other = {"이름": "찰붙임", "규격": "L3=40cm", "단위": "㎡"}
|
||
values, unread = hm.spec_inputs(other, SPECS[:1])
|
||
assert values == {} and unread == ["뒷길이"] # 고르기 밖 수 = 빈 값 + 표시
|
||
|
||
|
||
# ── 맞춤 응답(338 · 설계 3-3) ─────────────────────────────────────────
|
||
def _hopyo(client, no, layer="personal"):
|
||
res = client.get(f"{URL.format(layer)}/HP-000001/{no}")
|
||
assert res.status_code == 200, res.text
|
||
return {one["이름"]: one["맞춤"] for one in res.json()["호표"]["줄"]}
|
||
|
||
|
||
def _ready(client, layer="personal"):
|
||
assert _post(client, layer).status_code == 200
|
||
folder = layers.personal_dir(7, 42) if layer == "personal" else client.system
|
||
before = _hopyo(client, 1, layer)["레미콘타설(장비사용)"]
|
||
assert before["상태"] == "후보" and "셈 중" in before["꼬리표"] # 셈 전 = 이름 · 근거 후보
|
||
fit.run_job(folder, "HP-000001")
|
||
return folder
|
||
|
||
|
||
def test_새_상태_체크_제안(client):
|
||
before = master_hash()
|
||
_ready(client)
|
||
first = _hopyo(client, 1)
|
||
remicon = first["레미콘타설(장비사용)"]
|
||
assert remicon["상태"] == "맞음" and remicon["체크"] is True and "들어감" not in remicon
|
||
target = remicon["대상"][0]
|
||
assert target["로직"] == "GC000198" and target["입력값"]["소량감률"] == 50
|
||
assert target["출처"]["소량감률"] == "블록" and target["배"] == 1
|
||
assert remicon["품"]["같음"]["콘크리트공"] and remicon["품"]["엑셀"]["인력"]["콘크리트공"]
|
||
plywood = first["합판거푸집"]
|
||
assert plywood["상태"] == "맞음" and set(plywood["갈리는칸"]) == {"측압", "폼타이규격"}
|
||
mat = first["레미콘(25-21-120)"] # 358-2 · 361-1 — 「별도계상」 표시 · 지역 변형만 = 전국
|
||
assert mat["상태"] == "맞음" and mat["체크"] is True and "별도계상" in mat["꼬리표"]
|
||
assert mat["대상"][0] | {"환산": None, "세칸": None} == {
|
||
"전국": True,
|
||
"이름": "레미콘(전국)-관급",
|
||
"규격": "25-21-120",
|
||
"단위": "㎥",
|
||
"입력값": {},
|
||
"못읽음": [],
|
||
"출처": {},
|
||
"환산": None,
|
||
"세칸": None,
|
||
"몫": None,
|
||
"배": 1,
|
||
} # 특정 지역 키 없음
|
||
assert "전국 — 지역은 프로젝트에서" in mat["꼬리표"]
|
||
assert not any("못 가림" in t for t in mat["꼬리표"])
|
||
assert mat["후보"][0]["전국"] and len(mat["후보"]) == 129 # 전국 + 지역 변형 128(접어 고름)
|
||
assert all(c["변형"] and c["자재"] for c in mat["후보"][1:])
|
||
assert all(c["규격"].endswith("25-21-120") for c in mat["후보"])
|
||
dig = _hopyo(client, 3)["구조물터파기(인력100%)"]
|
||
assert dig["상태"] == "다른 점" and dig["체크"] is False
|
||
assert dig["대상"][0]["로직"] == "GC000083" and len(dig["후보"]) == 5
|
||
assert "특별인부 엑셀 0.3542 · 우리 0.3704" in dig["꼬리표"]
|
||
ref = _hopyo(client, 45)["고화매트재료비"]
|
||
assert ref["상태"] == "호표" and ref["체크"] is False and ref["글"] == "제46호표 먼저 확인"
|
||
assert ref["대상"][0]["호표"] == 46 and ref["대상"][0]["단위"] == "㎡"
|
||
pct = _hopyo(client, 46)["잡재료비"]
|
||
assert pct["상태"] == "%" and pct["체크"] is False
|
||
whole = client.get(f"{URL.format('personal')}/HP-000001").json()
|
||
assert whole["맞춤"][1] == 415 and 0 < whole["맞춤"][0] < 415 and whole["셈"]["끝"]
|
||
assert next(h for h in whole["호표"] if h["번"] == 61)["맞춤"] == [0, 0]
|
||
one = client.get(f"{URL.format('personal')}/HP-000001/1").json()["호표"]
|
||
assert one["셈"]["끝"] and one["만든"] is None and one["맞춤"][0] >= 2
|
||
assert master_hash() == before
|
||
|
||
|
||
def _ledger(client, rows, layer="personal"):
|
||
res = client.put(f"/api/m02/layers/{layer}/hopyo-ledger", json={"줄": rows})
|
||
assert res.status_code == 200, res.text
|
||
|
||
|
||
def test_별칭_체크_규칙(client):
|
||
_ready(client)
|
||
dig = "구조물터파기(인력100%)|토사|m3"
|
||
remicon = "레미콘타설(장비사용)|철근구조물-소량타설|m3"
|
||
_ledger(
|
||
client,
|
||
{
|
||
dig: {"갈래": "짝", "짝": [{"로직": "GC000083", "배": 1}]},
|
||
remicon: {"갈래": "짝", "짝": [{"로직": "GC000199", "배": 1}]},
|
||
"합판거푸집|6회|m2": {"갈래": "짝", "짝": [{"로직": "GC000211", "배": 1}]},
|
||
"면목|구멍|개": {"갈래": "우리에 없음"},
|
||
},
|
||
)
|
||
got = _hopyo(client, 3)["구조물터파기(인력100%)"]
|
||
assert got["상태"] == "별칭" and got["체크"] is True and got["대상"][0]["출처"] == {}
|
||
assert got["별칭"]["층"] == "personal" and not got["별칭"]["근거다름"]
|
||
first = _hopyo(client, 1)
|
||
other = first["레미콘타설(장비사용)"] # 숫자 판정 맞음 = GC000198 ≠ 장부 짝 → 판정 먼저(358-3)
|
||
assert other["상태"] == "맞음" and other["체크"] is True and "별칭" not in other
|
||
assert other["대상"][0]["로직"] == "GC000198" and "장부와 다름" in other["꼬리표"]
|
||
assert "GC000199" in {c["로직"] for c in other["후보"]} # 장부 짝 = 후보 끝 덤(안 지움)
|
||
loose = first["합판거푸집"] # 규격 4회 ↔ 장부 6회 — 느슨 열쇠 = 꼬리표 · 후보 덤만(229-4 ①)
|
||
assert loose["상태"] == "맞음" and "별칭" not in loose and "별칭 · 규격 다름" in loose["꼬리표"]
|
||
assert all("별칭" not in t["출처"].values() for t in loose["대상"])
|
||
assert "GC000211" in {c["로직"] for c in loose["후보"]} | {t["로직"] for t in loose["대상"]}
|
||
_ledger(
|
||
client, {remicon: {"갈래": "짝", "짝": [{"로직": "GC000198", "입력값": {"소량감률": 50}}]}}
|
||
)
|
||
plain = _hopyo(client, 7)["레미콘타설(장비사용)"] # 무근구조물 ↔ 장부 철근구조물-소량타설
|
||
assert plain["상태"] == "맞음" and plain["체크"] is True and "별칭" not in plain
|
||
assert plain["대상"][0]["로직"] == "GC000198" and plain["대상"][0]["입력값"]["소량감률"] == 0
|
||
assert "별칭 · 규격 다름" in plain["꼬리표"]
|
||
_ledger(client, {dig: {"갈래": "짝", "짝": [{"로직": "GF000101", "배": 1}]}})
|
||
off = _hopyo(client, 3)["구조물터파기(인력100%)"] # 산림 로직 ↔ 엑셀 건설 3-2-1
|
||
assert off["별칭"]["근거다름"] and off["체크"] is False and "근거 다름" in off["꼬리표"]
|
||
|
||
|
||
def test_품_비교_같은_장비_여러_줄():
|
||
from M02_MasterTemplete import M02_Hopyo_Line as lines
|
||
|
||
ex = {"인력": {}, "장비": [{"갈래": ["굴착기"], "시간": t} for t in (0.0334, 0.0371, 0.0889)]}
|
||
one = {"인력": {}, "장비": [{"갈래": ["굴착기"], "시간": 0.0889}], "자재": []}
|
||
assert not lines._qty({"품": ex}, {"우리품": one})["같음"]["굴착기"] # 값이 달라도 ✓ 이던 흠
|
||
many = {**one, "장비": ex["장비"]}
|
||
assert lines._qty({"품": ex}, {"우리품": many})["같음"]["굴착기"]
|
||
|
||
|
||
def test_장부_뺌_위_층(client):
|
||
client.who.update(role="SYSTEM_ADMIN")
|
||
key = "구조물터파기(인력100%)|토사|m3"
|
||
_ledger(client, {key: {"갈래": "안 씀"}}, "system")
|
||
client.who.update(role="USER")
|
||
_ready(client)
|
||
got = _hopyo(client, 3)["구조물터파기(인력100%)"]
|
||
assert got["상태"] == "뺌" and got["뺌"] == "안 씀" and got["별칭"]["층"] == "system"
|
||
assert hm.chain(layers.personal_dir(7, 42))[1] == layers.company_dir(7)
|
||
|
||
|
||
def test_미리_셈_목록_낱개(client):
|
||
folder = _ready(client)
|
||
url = f"{URL.format('personal')}/HP-000001/1/guess"
|
||
one = client.post(url, json={"차례": 1, "대상": {"로직": "GC000198"}})
|
||
assert one.status_code == 200, one.text
|
||
assert one.json()["대상"][0]["환산"] == {"배": 1, "갈래": "같음"}
|
||
assert client.post(url, json={"차례": 1, "대상": {"로직": "XX"}}).status_code == 400
|
||
body = [{"로직": "GC000198", "입력값": {"타설방법": "장비사용타설", "소량감률": 50}}]
|
||
got = hm.guess(folder, "HP-000001", 1, 1, body)
|
||
assert got["대상"][0]["출처"]["소량감률"] == "고름"
|
||
assert got["품"]["같음"]["콘크리트공"] and got["품"]["엑셀"]["인력"]["콘크리트공"]
|
||
plain = hm.guess(folder, "HP-000001", 1, 1, [{"로직": "GC000198", "입력값": {"소량감률": 0}}])
|
||
assert not plain["품"]["같음"]["콘크리트공"] # 옵션을 바꾸면 우리 품이 바뀜
|
||
|
||
|
||
def test_로직_추가_고르기_줄은_이_층과_위_층(client):
|
||
client.who.update(role="SYSTEM_ADMIN")
|
||
assert _post(client, "system").status_code == 200
|
||
client.who.update(role="USER")
|
||
assert _post(client).status_code == 200
|
||
rows = client.get("/api/m02/layers/personal/hopyo-picks").json()["줄"]
|
||
assert [r["층"] for r in rows].count("personal") == 68
|
||
assert [r["층"] for r in rows].count("system") == 68
|
||
three = next(r for r in rows if r["층"] == "system" and r["번"] == 3)
|
||
assert {k: three[k] for k in ("주인", "키", "이름", "단위")} == {
|
||
"주인": None,
|
||
"키": "HP-000001",
|
||
"이름": "임도표지석",
|
||
"단위": "개소",
|
||
}
|
||
assert three["맞춤"][1] == 4
|
||
system = client.get("/api/m02/layers/system/hopyo-picks").json()["줄"]
|
||
assert {r["층"] for r in system} == {"system"} # 마스터 = 마스터만
|
||
|
||
|
||
def test_뜻_낱말이_안_겹치면_후보_아님():
|
||
assert hm.words("면 목") == {"면목"} and hm.words("구조물되메우기(인력100%)") == {
|
||
"구조물되메우기"
|
||
}
|
||
assert hm.words("되메우기 및 다짐(소형장비)") == {"되메우기", "다짐"}
|
||
soil = {"이름": "잔토처리(인력100%)", "규격": "", "단위": "㎥", "갈래": "단가산출"}
|
||
assert hm.candidates(soil, "로직") == [] # 「처리」 두 글자만 겹침(소금처리 · 바탕처리)
|
||
stone = {"이름": "임도표지석(받침,글새김포함)", "규격": "", "단위": "㎥", "갈래": "자재"}
|
||
assert hm.candidates(stone, "자재") == [] # 속도표지판 · 도로표지용페인트
|
||
got = hm.b_order(soil)
|
||
assert got["상태"] == "없음" and got["top"] == []
|
||
fill = {"이름": "구조물되메우기(인력100%)", "규격": "", "단위": "㎥", "갈래": "단가산출"}
|
||
assert hm.candidates(fill, "로직")[0]["이름"] == "되메우기" # 한쪽 낱말에 듦 = 겹침
|
||
|
||
|
||
# ── 이름 닮은 로직(358-1 · 옛 324 데스크탑 다섯 그대로) ─────────────────────
|
||
def test_이름후보_옛_데스크탑_다섯():
|
||
fill = {"이름": "구조물되메우기(인력100%)", "규격": "", "단위": "㎥", "갈래": "단가산출"}
|
||
got = [c["로직"] for c in hm.name_cands(fill)]
|
||
assert got == ["GF000113", "GF000194", "GC000108", "GC000109", "GC000451"] # 되메우기 다섯
|
||
dowel = {"이름": "다웰바설치", "규격": "Φ32×1000", "단위": "EA", "갈래": "단가산출"}
|
||
assert [c["로직"] for c in hm.name_cands(dowel)][:3] == ["GC000226", "GF000201", "GF000202"]
|
||
soil = {"이름": "잔토처리(인력100%)", "규격": "", "단위": "㎥", "갈래": "단가산출"}
|
||
assert hm.name_cands(soil) == [] # 뜻 낱말 안 겹치면 후보 아님 그대로
|
||
same = {"이름": "되메우기", "규격": "", "단위": "㎥", "갈래": "단가산출"}
|
||
assert hm.name_cands(same)[0] == {**hm.name_cands(same)[0], "로직": "GF000113", "점수": 9}
|
||
|
||
|
||
def test_이름후보_뜻겹침_출처_표시(client):
|
||
_ready(client)
|
||
dig = _hopyo(client, 3)["구조물터파기(인력100%)"]
|
||
assert dig["이름후보"] and all({"뜻겹침", "출처같음", "책"} <= set(c) for c in dig["이름후보"])
|
||
assert dig["후보"][0]["출처같음"] is True and dig["후보"][0]["책"] == "건설품셈" # 3-2-1
|
||
assert [c["로직"] for c in dig["후보"]][0] == "GC000083" # 차례 · 판정 그대로(표시만)
|
||
mat = _hopyo(client, 1)["레미콘(25-21-120)"]
|
||
assert "이름후보" not in mat # 자재 줄은 로직 이름후보 없음
|
||
|
||
|
||
# ── 자재 · 별도계상 줄(358-2 · `M02_Hopyo_Material`) ─────────────────────────
|
||
MATERIAL_ANSWER = { # 북부청 임도 자재 · 별도계상 줄 → 우리 자재(정답 표 · 사람 확인)
|
||
("면목", "0.03×0.03×0.0424"): "MT007567", # 면 목 42, 30×30(0.03 m = 30 mm)
|
||
("자물쇠", "50m/m"): "MT008011",
|
||
("열연강판", "3.0mm"): "MT000884", # 3.0≤t<4.5 kg(같은 단위 먼저)
|
||
("원형돌망태(몸통)", "#8,45×45"): "MT006349",
|
||
("원형돌망태(뚜껑)", "#8,45×45"): "MT006350",
|
||
}
|
||
|
||
|
||
def test_자재_다듬기_규격_수():
|
||
from M02_MasterTemplete import M02_Hopyo_Material as hmat
|
||
|
||
line, apart = hmat.clean({"이름": "조약돌(별도계상)", "규격": "Ø100~200mm내외", "갈래": "자재"})
|
||
assert apart and line["이름"] == "조약돌"
|
||
assert hmat.clean({"이름": "레미콘(25-21-120)", "규격": "별도계상"})[0]["규격"] == ""
|
||
assert hmat.base("레미콘(관급)-강원도(홍천군(내면 제외))") == hmat.base("레미콘(서울)-사급")
|
||
assert (
|
||
hmat.base("와이어메쉬(용접철망)관급")
|
||
== hmat.base("와이어메쉬(용접철망),서울")
|
||
== "와이어메쉬"
|
||
)
|
||
row = {"spec": "", "nums": [42.0, 30.0, 30.0], "ranges": []}
|
||
assert hmat.spec_fit({"이름": "면목", "규격": "0.03×0.03×0.0424"}, row) == 1.0
|
||
rng = {"spec": "", "nums": [], "ranges": hmat._ranges("2.0≤T<3.0")}
|
||
assert hmat.spec_fit({"이름": "열연강판", "규격": "3.0mm"}, rng) == 0.0 # 끝 3.0 은 안 듦
|
||
got = hmat.marks(
|
||
{"이름": "레미콘", "규격": "별도계상", "갈래": "별도계상", "비고": "운반비 별도"}
|
||
)
|
||
assert got == {"별도계상": "별도계상", "자재구분": "관급", "운반비": "미포함"}
|
||
assert hmat.marks({"이름": "면목", "단가": {"재료비": 360}}, "면 목") == {
|
||
"별도계상": "직접계산",
|
||
"자재구분": "사급",
|
||
}
|
||
|
||
|
||
def test_북부청_자재_별도계상_맞음_늚(client):
|
||
_ready(client)
|
||
seen, sure = {}, 0
|
||
for no in range(1, 69):
|
||
res = client.get(f"{URL.format('personal')}/HP-000001/{no}")
|
||
if res.status_code != 200:
|
||
continue
|
||
for one in res.json()["호표"]["줄"]:
|
||
key = (one["이름"], one["규격"])
|
||
if one["갈래"] in ("자재", "별도계상") and key not in seen:
|
||
seen[key] = one["맞춤"]
|
||
sure = sum(m["상태"] == "맞음" for m in seen.values())
|
||
assert len(seen) == 30 and sure >= 8 # 옛 2 → 8(면목 · 레미콘 둘 · 와이어메쉬 · 열연강판 …)
|
||
for key, code in MATERIAL_ANSWER.items():
|
||
assert seen[key]["상태"] == "맞음" and seen[key]["대상"][0]["자재"] == code, key
|
||
pipe = seen[("탄소강관(백)", "D=125mm(외경139.8mm)")] # 이름 달라도 규격 수로 후보
|
||
assert pipe["상태"] == "후보" and "139.8" in pipe["후보"][0]["규격"]
|
||
mortar = seen[("드라이모르터", "조적용레미탈")] # 이름 오타 · 규격 같음 = 1위 후보
|
||
assert mortar["후보"][0]["자재"] == "MT004251"
|
||
|
||
|
||
# ── 지역만 다른 자재 = 전국(361-1) ─────────────────────────────────────
|
||
REMICON = {"이름": "레미콘(25-21-120)", "규격": "별도계상", "단위": "㎥", "갈래": "별도계상"}
|
||
H_BEAM = {"이름": "H 형강", "규격": "SS275, 100*100*6*8mm", "단위": "Ton", "갈래": "자재"}
|
||
|
||
|
||
def test_전국_지역_줄글_전국품목():
|
||
from M02_MasterTemplete import M02_Hopyo_Material as hmat
|
||
|
||
rows, tags = hmat.sure(hmat.clean(REMICON)[0]) # 지역 낱말 없음 · (전국) 품목 없음
|
||
assert rows[0]["one"] == {
|
||
"전국": True,
|
||
"이름": "레미콘(전국)-관급",
|
||
"규격": "25-21-120",
|
||
"단위": "㎥",
|
||
}
|
||
assert tags == [hmat.NATIONAL_TAG] and all("자재" in r["one"] for r in rows[1:])
|
||
paid = {**REMICON, "규격": "", "갈래": "자재", "단가": {"재료비": 90000}}
|
||
assert hmat.national(paid)["이름"] == "레미콘(전국)-사급" # 재료비 줄 = 사급 꼬리
|
||
named = {**REMICON, "비고": "강원도 강릉시"} # 줄 글에 지역 = 그 지역(원문)
|
||
rows, tags = hmat.sure(hmat.clean(named)[0])
|
||
assert rows[0]["one"]["이름"] == "레미콘(관급)-강원도(강릉시)" and tags == [hmat.REGION_TAG]
|
||
assert hmat.national(named) is None
|
||
rows, tags = hmat.sure(hmat.clean(H_BEAM)[0]) # (전국) 품목 있음 = 그것
|
||
assert rows[0]["one"]["자재"] == "MT001694" and tags == []
|
||
assert hmat.national(H_BEAM) is None
|
||
seoul = hmat.sure(hmat.clean({**H_BEAM, "비고": "서울"})[0])[0][0]["one"]
|
||
assert seoul["이름"] == "H 형강(서울)"
|
||
|
||
|
||
def test_전국_고르기_지역_되돌림(client):
|
||
_ready(client)
|
||
no, at = (
|
||
1,
|
||
next(
|
||
x["차례"]
|
||
for x in client.get(f"{URL.format('personal')}/HP-000001/1").json()["호표"]["줄"]
|
||
if x["이름"] == "레미콘(25-21-120)"
|
||
),
|
||
)
|
||
path = f"{URL.format('personal')}/HP-000001/{no}/guess"
|
||
seoul = client.post(path, json={"차례": at, "대상": [{"자재": "MT004258", "배": 1}]})
|
||
assert seoul.status_code == 200 and seoul.json()["대상"][0]["자재"] == "MT004258"
|
||
back = client.post(path, json={"차례": at, "대상": [{"전국": True, "배": 1}]})
|
||
assert back.status_code == 200, back.text
|
||
got = back.json()["대상"][0]
|
||
assert got["전국"] and "자재" not in got and got["이름"] == "레미콘(전국)-관급"
|
||
other = next( # 전국 자리표 없는 줄(로직 줄) = 400
|
||
x["차례"]
|
||
for x in client.get(f"{URL.format('personal')}/HP-000001/1").json()["호표"]["줄"]
|
||
if x["이름"] == "레미콘타설(장비사용)"
|
||
)
|
||
bad = client.post(path, json={"차례": other, "대상": [{"전국": True, "배": 1}]})
|
||
assert bad.status_code == 400
|
||
|
||
|
||
# ── 단계 대조 보임(358-3) — 목록 수 = 확인 창 수 · 연결 두 칸 · 목록 열 때 뒤 셈 ─────────────
|
||
def test_목록_수_확인_창_수_한_규칙(client):
|
||
_ready(client)
|
||
whole = client.get(f"{URL.format('personal')}/HP-000001").json()
|
||
need = done = 0
|
||
for brief in whole["호표"]:
|
||
one = client.get(f"{URL.format('personal')}/HP-000001/{brief['번']}").json()["호표"]
|
||
lines = one["줄"]
|
||
assert brief["맞춤"] == [sum(x["맞춤"]["체크"] for x in lines), len(lines)], brief["번"]
|
||
inside = [x for x in lines if x["맞춤"]["상태"] not in ("%", "노임", "뺌")]
|
||
assert (brief["연결할"], brief["연결된"]) == (one["연결할"], one["연결된"])
|
||
assert one["연결할"] == len(inside) and one["연결된"] == sum(
|
||
x["맞춤"]["체크"] for x in inside
|
||
)
|
||
need, done = need + one["연결할"], done + one["연결된"]
|
||
assert (whole["연결할"], whole["연결된"]) == (need, done) and 0 < done <= need < 415
|
||
pct = next(h for h in whole["호표"] if h["번"] == 46) # 잡재료비 % 줄 = 셈 밖
|
||
assert pct["연결할"] < pct["맞춤"][1]
|
||
|
||
|
||
def test_목록_열_때_뒤_셈_예약(client, monkeypatch):
|
||
assert _post(client).status_code == 200
|
||
asked = []
|
||
monkeypatch.setattr(fit, "ensure", lambda folder, key: asked.append(key) or {"끝": False})
|
||
rows = client.get(URL.format("personal")).json()["줄"]
|
||
assert [r["키"] for r in rows] == asked == ["HP-000001"]
|