Files
Aislo/resources/tester/test_715_sangeun_store.py
T
eomsangdonandClaude Opus 5.5 4c8a06559a feat(740-1): 단가산출 · 일위대가 서로 독립 — 넣기 펼침(M01 키 줄 + 덮개 사본 · 본뜬) · 초기화 길 · 옮기기 함수 · B09 덮개 줄 굳힘
- 새 M02_Library_Flat: SK 에 SK = 더한줄 로직 한 줄에 원본 몸 통째(덮개 · 뺀줄 · 고름 · 더한줄 · % 덧줄까지 같은 금액) · SC 에 SK = 로직 줄 + 패치 · SK 에 SC = 줄마다 더한줄(수량 = 저장 수량.손 › 실무수량 › 1) · 바탕 SK · 패치 SK 참조 = 덮개 겹침 · 줄 본뜬 {층, 주인, 키, 이름, 때}
- 저장 · 읽기 · 층 복사 때 서버가 펼침(못 펼친 줄 = 옛 참조 그대로) · SC 안 SC(하위 호표)는 저장 · 읽기에서 참조 그대로(엑셀 하위 호표 끝수 · B09 하위 대가) · 찾아 넣기 길만 펴 넣음
- 길: POST {kind}-flat(넣을 줄 · 못 펼침 400) · POST {kind}-reset(미리 보기 걷힐 수 · 씀)
- 초기화 = 덮개 · 뺀줄 · 더한줄 단가 걷음(안쪽 넣은 몸까지) · 항목 · 수량 · 입력값 · 고름 남김
- 옮기기 = migrate(apply) · 전후 1 단위 금액 같을 때만 씀 · 공유 storage 는 브레인
- 둘째 기초 세부 셈 = 넣은 SK 몸이면 이름 없는 SK 로(Sangeun_Calc.sub_run)
- B09: SC 사본 덮개 줄 = 굳힌 줄(굳힌 {키, 줄 …}) → 사본 셈 · 대가 블록이 덮개를 읽음(옛 산근 길은 덮개 무시 · 실측 6,451 대 M02 210,866)
- 733 지문 = 옛 참조 줄 펼친 꼴로 대조
- 낡은 시험 고침(까닭 = 단가산출이 낀 참조가 이제 펼쳐 담김): 715 store(바탕 · 사전조합 SK · 층 복사) · 717-2a 목록 바탕 · 717-4 SC 안 SK · 719-1 원본판(원본 고쳐도 그대로) · 719-2 속 일위대가(빠진 줄 없음 · 쓰지 않는 _calc_given 지움) · 725-1 SK 안 SC · 729 SK 줄 본뜬 · 733-3 쓰는 곳 = 구조물 조합(SK 안 SK 참조 없음) · 572 지문 대조 대상

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 19:47:33 +09:00

194 lines
8.2 KiB
Python

# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
"""M02 단가산출(SK) 서고 · 셈(PLAN 715-1b · 715-2) — 임시 storage.
고친 줄 값 = 손셈 · 덧줄 % 엔진이 다시 셈 · 뺀줄 뒤 고름 제자리 · 짝 잃음 남김 · 원값 서버 ·
바탕 · 사전조합에 SK 넣기 = 펼쳐 담음(740-1 · 원본 지워도 그대로) · 층 복사 왕복 같은 값.
"""
from decimal import Decimal
import pytest
from M01_MasterData import M01_MasterData_Store as m01
from M02_MasterTemplete import M02_Labor_Ref as refs
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 COMPANY, MINE, world # noqa: F401 — 같은 임시 층
PCT = "GC001109" # 원/㎡ · 인력 둘 + 「노무비 * 3 / 100」
TWO_MATS = "GC000565" # 원/m · 재료 줄 둘(검색어)
def _sk(folder, **doc):
with store.at(folder):
return refs.create("sangeun", {"이름": "내 단가산출", "단위": "㎡", "더한줄": [], **doc})[
"이름"
]
def _doc(folder, key):
with store.at(folder):
return store.read("sangeun", key)["문서"]
def _calc(folder, key, given=None):
with store.at(folder):
return calc.calc_doc(_doc(folder, key), given)
def _whole():
return m01.loaded()[1]
def _bare(code):
from M02_MasterTemplete import M02_Labor_Doc as labor
return labor.run_line(_whole(), code, {})
def test_qty_cover_hand_and_pct_recomputed(world):
"""품 덮개 = 손셈 · 「노무비 * 3 / 100」 덧줄 다시 셈 · 원값 = 서버가 셈."""
bare = _bare(PCT)
first, second = bare["줄"][0], bare["줄"][1]
key = _sk(
world["personal"], 바탕={"로직": PCT}, 덮개={"labor:LB000036#0": {"qty": 0.5, "원값": 9}}
)
doc = _doc(world["personal"], key)
assert doc["덮개"]["labor:LB000036#0"]["원값"]["qty"] == pytest.approx(float(first["수량"]))
assert len(doc["바탕판"]) == 16
got = _calc(world["personal"], key)
labor_sum = Decimal("0.5") * Decimal(str(first["단가"])) + Decimal(str(second["금액"]))
assert got["기준"]["노무비"] == pytest.approx(float(labor_sum))
assert got["기준"]["경비"] == pytest.approx(float(labor_sum * 3 / 100), abs=1)
assert got["줄"][0]["덮개"] == ["qty"] and got["줄"][0]["cid"] == "labor:LB000036#0"
assert got["짝잃음"] == []
# 덧줄 율 덮개 = 5 %
key = _sk(
world["personal"],
바탕={"로직": PCT},
덮개={"pct:" + bare["줄"][2]["이름"] + "#0": {"qty": 5}},
)
got = _calc(world["personal"], key)
assert got["기준"]["경비"] == pytest.approx(float(bare["기준"]["노무비"]) * 0.05, abs=1)
def test_drop_then_pick_stays_on_its_line(world):
"""뺀줄 뒤 고름 제자리 — 둘째 재료 줄을 고른 자재가 첫 재료 줄을 뺀 뒤에도 그 줄 품목."""
import master_pick as mp
whole = _whole()
row = whole.logic(TWO_MATS)
item = row["호표"][2]
picks = mp.search(whole, item["요소"], {}, item.get("단위"))
want = picks[1] if len(picks) > 1 else picks[0]
first_cid = "material:" + row["호표"][1]["이름"] + "#0"
second_cid = "material:" + item["이름"] + "#0"
key = _sk(
world["personal"],
단위="m",
바탕={"로직": TWO_MATS},
뺀줄=[first_cid],
고름={second_cid: want},
자재표시={second_cid: {"별도계상": "직접계산"}}, # 기본 = 별도계상(222-1) 줄 빠짐
)
got = _calc(world["personal"], key)
names = [x["이름"] for x in got["줄"]]
assert row["호표"][1]["이름"] not in names
line = next(x for x in got["줄"] if x["cid"] == second_cid)
assert line["품목"]["키"] == want
def test_lost_cover_kept_and_listed(world):
key = _sk(world["personal"], 바탕={"로직": PCT}, 덮개={"labor:LB999999#0": {"qty": 1}})
got = _calc(world["personal"], key)
assert got["짝잃음"][0]["cid"] == "labor:LB999999#0"
assert "labor:LB999999#0" in _doc(world["personal"], key)["덮개"]
assert got["기준"]["계"] == pytest.approx(float(_bare(PCT)["기준"]["계"]))
def test_chain_base_flattened_delete_free(world):
"""바탕 SK 넣기(740-1) = 펼쳐 담음 — 바탕 = M01 기초 · 위 덮개 겹침 · 값 같음 ·
원본 지워도 그대로."""
a = _sk(world["personal"], 바탕={"로직": PCT})
b = _sk(
world["personal"], 바탕={"참조": {**MINE, "키": a}}, 덮개={"labor:LB000002#0": {"qty": 0}}
)
assert _doc(world["personal"], b)["바탕"] == {"로직": PCT}
got = _calc(world["personal"], b)
bare = _bare(PCT)
assert got["기준"]["노무비"] == pytest.approx(float(bare["줄"][0]["금액"]))
with store.at(world["personal"]):
store.delete("sangeun", a) # 쓰는 곳 없음(서로 독립)
assert _calc(world["personal"], b)["기준"] == got["기준"]
def test_precombo_holds_sangeun(world):
"""사전조합에 SK 넣기(740-1) = 로직 줄 + 패치(SK 몸) · 1 단위 값 × 수량 · SK 지워도 그대로."""
from M02_MasterTemplete import M02_Labor_Doc as labor
sk = _sk(world["personal"], 바탕={"로직": PCT}, 덮개={"labor:LB000036#0": {"qty": 0.5}})
one = _calc(world["personal"], sk)["1단위"]["계"]
with store.at(world["personal"]):
sc = refs.create(
"precombo",
{
"이름": "조합",
"단위": "㎡",
"담은로직": [{"참조": {**MINE, "키": sk}, "수량": {"손": 2}}],
},
)["이름"]
line = store.read("precombo", sc)["문서"]["담은로직"][0]
assert line["로직"] == PCT and line["패치"]["덮개"] and line["본뜬"]["키"] == sk
store.delete("sangeun", sk)
pill = {f"{refs.ref_id({**MINE, '키': sc})}#0": 2} # 723-1 안 줄 수량 = 쓰는 쪽 알약
got = labor.calc({"담은로직": [{"참조": {**MINE, "키": sc}, "수량": {"손": 1}}]}, pill)
assert got["반영합"]["계"] == pytest.approx(one * 2, abs=1)
def test_layer_copy_round_trip(world):
"""개인 B(바탕 = 개인 A) → 회사로 복사 = 펼친 몸(바탕 = 기초 · 본뜬) → 개인으로 다시 =
같은 값."""
a = _sk(world["personal"], 바탕={"로직": PCT}, 덮개={"labor:LB000036#0": {"qty": 0.5}})
b = _sk(world["personal"], 바탕={"참조": {**MINE, "키": a}}, 뺀줄=["labor:LB000002#0"])
want = _calc(world["personal"], b)["기준"]
src = _doc(world["personal"], b)
with store.at(world["company"]):
made = refs.create(
"sangeun", refs.carry(refs.origin_mark(src, {**MINE, "키": b}), ("personal", "7/42"))
)
assert not list((world["company"] / "sangeun").glob("*.json"))[1:] # A 따로 복사 안 함
copy = _doc(world["company"], made["이름"])
assert copy["바탕"] == {"로직": PCT} and copy["본뜬"]["키"] == b
assert _calc(world["company"], made["이름"])["기준"] == want
with store.at(world["personal"]):
back = refs.create(
"sangeun",
refs.carry(refs.origin_mark(copy, {**COMPANY, "키": made["이름"]}), ("company", "7")),
)
assert _calc(world["personal"], back["이름"])["기준"] == want
def test_refusals(world):
with store.at(world["personal"]):
for bad, status in (
({"이름": "x", "더한줄": []}, 400), # 단위 빔
({"이름": "x", "단위": "㎡", "더한줄": [], "바탕": {"로직": "GC999999"}}, 400),
(
{
"이름": "x",
"단위": "㎡",
"더한줄": [],
"바탕": {"참조": {**MINE, "키": "SK-000999"}},
},
400,
),
({"이름": "x", "단위": "㎡"}, 400), # 뼈대(더한줄) 없음
):
with pytest.raises(store.StoreError) as e:
refs.create("sangeun", bad)
assert e.value.status == status, bad
key = _sk(world["personal"], 바탕={"로직": PCT})
with store.at(world["personal"]), pytest.raises(store.StoreError) as e:
calc.calc_doc({**_doc(world["personal"], key), "바탕": {"참조": {**MINE, "키": key}}})
assert e.value.status == 422