feat(744-1): 둘째 기초 세부 원문 품 · 되돌리기 기억(처음수량 · 뺀더한줄) — 서버
- 둘째 기초 펼침 줄 덧칸 원문 {수량, 금액, 비목} = 그 기초 1 기준단위 몫(M01 원문 품 · 머리 수량 안 곱함) · 줄 수량 · 금액 = 머리 수량 곱한 몫(상자 합) 그대로
- 더한줄 항목 처음수량 = 넣을 때 수량(없으면 저장 때 지금 수량) · SK · 일위대가 줄 패치 모두
- 뺀더한줄 [{차례, 줄}] = [기초 빼기] · 더한 항목 빼기 기억(셈 안 함 · 못 읽는 줄 버림) · 일위대가 줄 패치도
- 733 지문에서 처음수량 · 뺀더한줄 뺌 · Labor_Patch.as_doc = 셈 칸(CALC)만(화면 짝 꼴 그대로)
- 이음 = PLAN 744 아래 줄(화면 = sub2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,8 @@ from typing import Any
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
|
||||
FIELD = "패치"
|
||||
KEEP = ("덮개", "뺀줄", "더한줄")
|
||||
CALC = ("덮개", "뺀줄", "더한줄") # 셈에 드는 칸(`as_doc` · 화면 짝 꼴)
|
||||
KEEP = (*CALC, "뺀더한줄") # 뺀더한줄 = [되살림] 기억(744-1)
|
||||
TEMP = "SK-000000" # 이름 없는 줄 단가산출 — 저장 안 함
|
||||
|
||||
|
||||
@@ -41,7 +42,9 @@ def clean(one: dict, where: str, whole: Any, row: dict | None, opened: list | No
|
||||
)
|
||||
except sd.DocError as e:
|
||||
raise _bad(f"{where} · {e}") from e
|
||||
out = {k: got[k] for k in KEEP if got[k]}
|
||||
out = {k: got[k] for k in KEEP if got.get(k)}
|
||||
for add in out.get("더한줄") or []: # 처음수량(744-1) = 넣을 때 수량 · 없으면 지금 수량
|
||||
add.setdefault("처음수량", add["수량"])
|
||||
return {FIELD: out} if out else {}
|
||||
|
||||
|
||||
@@ -71,7 +74,7 @@ def _keyed(value: Any, ids: list[str]) -> dict:
|
||||
|
||||
def as_doc(one: dict, whole: Any, name: str = "줄", unit: str = "식") -> dict[str, Any]:
|
||||
"""일위대가 줄 → SK 문서(바탕 = 그 로직 · 그 SK) — 고름 · 자재표시는 cid 로."""
|
||||
body = {k: (one.get(FIELD) or {}).get(k) or ([] if k != "덮개" else {}) for k in KEEP}
|
||||
body = {k: (one.get(FIELD) or {}).get(k) or ([] if k != "덮개" else {}) for k in CALC}
|
||||
head = {"키": TEMP, "이름": name, "단위": unit, **body}
|
||||
if "참조" in one:
|
||||
return {**head, "바탕": {"참조": one["참조"]}}
|
||||
|
||||
@@ -24,7 +24,7 @@ from common_util.common_util_derived_store import derived_key
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
|
||||
DROP = {"키", "이름", "구분", "상세구분", "본뜬", "메모", "별칭", "바탕판", "원값", "계산값", "열"}
|
||||
DROP |= {"일위대가번", "종류", "수정일", "출처"}
|
||||
DROP |= {"일위대가번", "종류", "수정일", "출처", "처음수량", "뺀더한줄"} # 744-1 되돌림 기억
|
||||
UNIT = {"m3": "㎥", "m2": "㎡", "m³": "㎥", "m²": "㎡"}
|
||||
EMPTY = (None, "", [], {})
|
||||
|
||||
|
||||
@@ -249,7 +249,8 @@ def tag_of(whole: Any, code: str) -> str:
|
||||
|
||||
|
||||
def _detail(one: dict, at: int, whole: Any) -> dict[str, Any]:
|
||||
"""둘째 기초 세부 줄(화면) — 수량 · 금액 · 비목 = 세부 × 더한줄 수량 · cid `add:#k/세부 cid` ·
|
||||
"""둘째 기초 세부 줄(화면) — 수량 · 금액 · 비목 = 세부 × 더한줄 수량(상자 합 몫) · `원문` =
|
||||
그 기초 1 기준단위 몫(M01 원문 품 · 744-1 화면 품 칸) · cid `add:#k/세부 cid` ·
|
||||
`바탕값` = 덮기 전 1 기준단위 품 · 단가 · `뺀세부` = 뺀 줄 이름."""
|
||||
sub = sub_run(one, whole)
|
||||
plain = sub_run(one, whole, False) if sd.sub_patched(one) else sub
|
||||
@@ -272,6 +273,7 @@ def _detail(one: dict, at: int, whole: Any) -> dict[str, Any]:
|
||||
**scaled,
|
||||
"비목": parts,
|
||||
"cid": f"add:#{at}/{cid}",
|
||||
"원문": {k: line.get(k) for k in ("수량", "금액", "비목")},
|
||||
"바탕값": _was(was[cid]) if cid in was else None,
|
||||
**({"덮개": [k for k in sd.COVER_KEYS if k in mine]} if mine else {}),
|
||||
**({"원값": mine["원값"]} if "원값" in mine else {}),
|
||||
@@ -439,6 +441,8 @@ def clean_doc(doc: Any, here_: refs.Here) -> dict[str, Any]:
|
||||
base = _base_of(rough, whole, refs.read_ref, ())
|
||||
out = sd.clean(doc, base["줄"], whole)
|
||||
out["바탕"], out["더한줄"] = rough["바탕"], rough["더한줄"]
|
||||
for one in out["더한줄"]: # 처음수량(744-1) = 넣을 때 수량 · 없으면 지금 수량
|
||||
one.setdefault("처음수량", one["수량"])
|
||||
if out["덮개"]:
|
||||
bare = run({**out, "덮개": {}}, whole) # 덮기 전 값 = 원값(그때 M01)
|
||||
was = {x["cid"]: x for x in bare.get("줄") or []}
|
||||
|
||||
@@ -196,9 +196,25 @@ def _added(raw: Any, where: str, whole: Any) -> dict:
|
||||
else:
|
||||
raise DocError(f"{where} · 종류는 인력 · 재료 · 기계 · 로직 · 참조 · 고정")
|
||||
mark = {"본뜬": raw["본뜬"]} if isinstance(raw.get("본뜬"), dict) else {} # 출처(740-1)
|
||||
if raw.get("처음수량") is not None: # 넣을 때 수량(744-1 [되돌림] 값)
|
||||
mark["처음수량"] = plain(_num(raw["처음수량"], f"{where} 처음수량"))
|
||||
return {**head, "이름": name, "단위": unit, "수량": qty, **mark}
|
||||
|
||||
|
||||
def _gone_adds(raw: Any, whole: Any) -> list[dict]:
|
||||
"""뺀 더한줄 기억(744-1 · [되살림]) `[{차례, 줄}]` — 셈 안 함 · 못 읽는 줄은 버림."""
|
||||
out = []
|
||||
for one in raw if isinstance(raw, list) else []:
|
||||
at = one.get("차례") if isinstance(one, dict) else None
|
||||
if not isinstance(at, int) or isinstance(at, bool) or at < 0:
|
||||
continue
|
||||
try:
|
||||
out.append({"차례": at, "줄": _added(one.get("줄"), "뺀 더한줄", whole)})
|
||||
except DocError:
|
||||
continue
|
||||
return out
|
||||
|
||||
|
||||
def _sub_patch(raw: dict, where: str) -> dict:
|
||||
"""둘째 기초 세부 고침(727-2) — `덮개` {세부 cid: 덮개} · `뺀줄` [세부 cid] · 빈 칸은 안 둠 ·
|
||||
`원값` · `바탕판` = 서버 몫(버림)."""
|
||||
@@ -327,6 +343,8 @@ def clean(doc: Any, base_row: dict | None = None, whole: Any = None) -> dict[str
|
||||
if len(set(drop)) != len(drop):
|
||||
raise DocError("뺀줄에 같은 줄 두 번")
|
||||
out["뺀줄"] = drop
|
||||
if gone := _gone_adds(doc.get("뺀더한줄"), whole):
|
||||
out["뺀더한줄"] = gone
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
||||
"""744-1 서버 — 둘째 기초 세부 품 원문 · 되돌리기 기억(임시 storage · M01 읽기만).
|
||||
|
||||
- 둘째 기초 `펼침` 줄 `원문` = 그 기초 1 기준단위 몫(GF000316 품 그대로) · 줄 수량 · 금액 = 머리
|
||||
수량 곱한 몫(상자 합) 그대로 · 머리 수량 = 더한줄 수량
|
||||
- 더한줄 `처음수량` = 넣을 때 수량(없으면 저장 때 지금 수량) · 셈 · 지문 안 듦
|
||||
- `뺀더한줄 [{차례, 줄}]` = [기초 빼기] · 더한 항목 빼기 기억 · 셈 · 지문 안 듦 · 못 읽는 줄 버림 ·
|
||||
일위대가 줄 패치도 같음
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_Library_Same as same
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
from resources.tester.test_m02_labor_ref import world # noqa: F401
|
||||
|
||||
GF = "GF000099" # 원/㎥
|
||||
BIG = "GF000316" # 기준단위 100㎥ — ㎥ 바탕에서 처음 수량 0.01
|
||||
PCT = "GC001109"
|
||||
MAN = {"종류": "인력", "요소": "LB000002", "수량": 1}
|
||||
|
||||
|
||||
def _sk(folder, **doc):
|
||||
with store.at(folder):
|
||||
made = refs.create("sangeun", {"이름": "744", "단위": "㎥", "더한줄": [], **doc})
|
||||
return store.read("sangeun", made["이름"])["문서"]
|
||||
|
||||
|
||||
def _calc(folder, doc):
|
||||
with store.at(folder):
|
||||
return calc.calc_doc(doc)
|
||||
|
||||
|
||||
def test_둘째_기초_세부_원문_품_머리_수량(world):
|
||||
doc = _sk(
|
||||
world["personal"], 바탕={"로직": GF}, 더한줄=[{"종류": "로직", "로직": BIG, "수량": 0.01}]
|
||||
)
|
||||
got = _calc(world["personal"], doc)
|
||||
head = next(x for x in got["줄"] if x["cid"] == "add:#0")
|
||||
m01_lines = labor.run_line(m01.loaded()[1], BIG, {})["줄"]
|
||||
assert head["수량"] == pytest.approx(0.01)
|
||||
for row, line in zip(head["펼침"], m01_lines):
|
||||
assert row["원문"]["수량"] == pytest.approx(float(line["수량"]), rel=1e-9) # 원문 그대로
|
||||
assert row["수량"] == pytest.approx(float(line["수량"]) * 0.01, rel=1e-9) # 상자 몫
|
||||
assert sum(x["금액"] for x in head["펼침"]) == pytest.approx(head["금액"], rel=1e-9)
|
||||
assert sum(x["원문"]["금액"] for x in head["펼침"]) * 0.01 == pytest.approx(head["금액"])
|
||||
|
||||
|
||||
def test_처음수량_넣을때_값_셈_지문_안_듦(world):
|
||||
me = world["personal"]
|
||||
plain = _sk(me, 바탕={"로직": PCT}, 단위="㎡", 더한줄=[{**MAN, "수량": 3}])
|
||||
assert plain["더한줄"][0]["처음수량"] == 3 # 없으면 저장 때 지금 수량
|
||||
kept = _sk(me, 바탕={"로직": PCT}, 단위="㎡", 더한줄=[{**MAN, "수량": 3, "처음수량": 1}])
|
||||
assert kept["더한줄"][0]["처음수량"] == 1
|
||||
assert _calc(me, kept)["기준"]["계"] == _calc(me, plain)["기준"]["계"]
|
||||
assert same.finger("sangeun", kept) == same.finger("sangeun", plain)
|
||||
|
||||
|
||||
def test_뺀더한줄_기억_셈_지문_안_듦_못읽는_줄_버림(world):
|
||||
me = world["personal"]
|
||||
gone = [
|
||||
{"차례": 0, "줄": {"종류": "로직", "로직": BIG, "수량": 0.01, "덮개": {}}},
|
||||
{"차례": 1, "줄": MAN},
|
||||
{"차례": -1, "줄": MAN}, # 차례 틀림
|
||||
{"차례": 2, "줄": {"종류": "로직", "로직": "GX999999", "수량": 1}}, # 없는 기초
|
||||
{"차례": 3}, # 줄 없음
|
||||
]
|
||||
doc = _sk(me, 바탕={"로직": GF}, 뺀더한줄=gone)
|
||||
assert [x["차례"] for x in doc["뺀더한줄"]] == [0, 1]
|
||||
assert doc["뺀더한줄"][1]["줄"]["요소"] == "LB000002"
|
||||
bare = _sk(me, 바탕={"로직": GF})
|
||||
assert _calc(me, doc)["기준"]["계"] == _calc(me, bare)["기준"]["계"]
|
||||
assert same.finger("sangeun", doc) == same.finger("sangeun", bare)
|
||||
|
||||
|
||||
def test_일위대가_줄_패치_기억_처음수량(world):
|
||||
me = world["personal"]
|
||||
patch = {"더한줄": [{**MAN, "수량": 2}], "뺀더한줄": [{"차례": 0, "줄": MAN}]}
|
||||
with store.at(me):
|
||||
sc = refs.create(
|
||||
"precombo", {"이름": "744", "단위": "㎡", "담은로직": [{"로직": PCT, "패치": patch}]}
|
||||
)
|
||||
line = store.read("precombo", sc["이름"])["문서"]["담은로직"][0]
|
||||
assert line["패치"]["더한줄"][0]["처음수량"] == 2
|
||||
assert line["패치"]["뺀더한줄"][0]["줄"]["요소"] == "LB000002"
|
||||
Reference in New Issue
Block a user