Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -106,8 +106,8 @@ def test_같은층_위층_찾기_참조_지문(client):
|
||||
{"층": "system", "주인": None, "키": "SK-000006"}
|
||||
]
|
||||
assert same.find_same(me, "sangeun", {**SK, "바탕": None, "더한줄": []}) == []
|
||||
# 참조 = 가리킨 문서 지문 — 층 넘어 같은 내용 사본을 가리키면 같음
|
||||
# 참조 = 펼친 꼴(740-1) 지문 — 층 넘어 같은 내용 사본을 가리키면 같음 · 다른 내용이면 다름
|
||||
a = {**SK, "바탕": {"참조": {"층": "company", "주인": "7", "키": "SK-000005"}}, "덮개": {}}
|
||||
b = {**a, "바탕": {"참조": {"층": "system", "주인": None, "키": "SK-000006"}}}
|
||||
c = {**a, "바탕": {"참조": {"층": "personal", "주인": "7/42", "키": "SK-000003"}}}
|
||||
c = {**a, "바탕": {"참조": {"층": "personal", "주인": "7/42", "키": "SK-000004"}}}
|
||||
assert same.finger("sangeun", a) == same.finger("sangeun", b) != same.finger("sangeun", c)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""M02 단가산출(SK) 서고 · 셈(PLAN 715-1b · 715-2) — 임시 storage.
|
||||
|
||||
고친 줄 값 = 손셈 · 덧줄 % 엔진이 다시 셈 · 뺀줄 뒤 고름 제자리 · 짝 잃음 남김 · 원값 서버 ·
|
||||
순환 422 · 쓰는 곳 지우기 409 · 사전조합이 SK 를 담음 · 층 복사 왕복 같은 값.
|
||||
바탕 · 사전조합에 SK 넣기 = 펼쳐 담음(740-1 · 원본 지워도 그대로) · 층 복사 왕복 같은 값.
|
||||
"""
|
||||
|
||||
from decimal import Decimal
|
||||
@@ -106,32 +106,28 @@ def test_lost_cover_kept_and_listed(world):
|
||||
assert got["기준"]["계"] == pytest.approx(float(_bare(PCT)["기준"]["계"]))
|
||||
|
||||
|
||||
def test_chain_loop_422_and_delete_409(world):
|
||||
"""바탕 사슬 A ← B · B 를 A 의 바탕으로 = 422 · 쓰는 곳 있는 A 지우기 = 409(이름)."""
|
||||
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"]):
|
||||
doc = store.read("sangeun", a)
|
||||
with pytest.raises(store.StoreError) as e:
|
||||
store.write(
|
||||
"sangeun", a, doc["판"], {**doc["문서"], "바탕": {"참조": {**MINE, "키": b}}}
|
||||
)
|
||||
assert e.value.status == 422
|
||||
with pytest.raises(store.StoreError) as e:
|
||||
store.delete("sangeun", a)
|
||||
assert e.value.status == 409 and "단가산출 개인 7/42" in str(e.value.detail)
|
||||
store.delete("sangeun", a) # 쓰는 곳 없음(서로 독립)
|
||||
assert _calc(world["personal"], b)["기준"] == got["기준"]
|
||||
|
||||
|
||||
def test_precombo_holds_sangeun(world):
|
||||
"""사전조합 참조 줄이 SK 를 담음 — 1 단위 값 × 수량 · SK 지우기 막음."""
|
||||
"""사전조합에 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",
|
||||
@@ -141,17 +137,17 @@ def test_precombo_holds_sangeun(world):
|
||||
"담은로직": [{"참조": {**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)
|
||||
with pytest.raises(store.StoreError) as e:
|
||||
store.delete("sangeun", sk)
|
||||
one = _calc(world["personal"], sk)["1단위"]["계"]
|
||||
assert got["반영합"]["계"] == pytest.approx(one * 2, abs=1)
|
||||
assert e.value.status == 409 and "일위대가 개인 7/42" in str(e.value.detail)
|
||||
|
||||
|
||||
def test_layer_copy_round_trip(world):
|
||||
"""개인 B(바탕 = 개인 A) → 회사로 복사 = A 도 같이(새 키 · 본뜬) → 개인으로 다시 = 같은 값."""
|
||||
"""개인 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)["기준"]
|
||||
@@ -160,15 +156,15 @@ def test_layer_copy_round_trip(world):
|
||||
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["바탕"]["참조"]["층"] == "company" and copy["본뜬"]["키"] == b
|
||||
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 _doc(world["personal"], back["이름"])["바탕"]["참조"]["층"] == "company"
|
||||
assert _calc(world["personal"], back["이름"])["기준"] == want
|
||||
|
||||
|
||||
|
||||
@@ -32,15 +32,15 @@ def test_sangeun_row_base_edits_users(world):
|
||||
assert row["바탕"] == f"{PCT} {name}".strip()
|
||||
assert row["고친줄수"] == 2 and row["쓰는곳수"] == 0
|
||||
assert row["제목"] == "목록 시험" and row["단위"] == "㎡"
|
||||
# 바탕 참조 SK · 더한줄 하나 → 그 SK 쓰는 곳 1
|
||||
# 바탕 참조 SK(740-1) = 펼쳐 담음 → 바탕 = 기초 · 고친 줄 = 겹친 덮개 · 뺀줄 + 더한줄 · 쓰는 곳 0
|
||||
b = _sk(
|
||||
mine,
|
||||
바탕={"참조": {**MINE, "키": a}},
|
||||
더한줄=[{"종류": "인력", "요소": "LB000036", "이름": "보통인부", "단위": "인", "수량": 1}],
|
||||
)
|
||||
row_b = _row(mine, "sangeun", b)
|
||||
assert row_b["바탕"] == a and row_b["고친줄수"] == 1
|
||||
assert _row(mine, "sangeun", a)["쓰는곳수"] == 1
|
||||
assert row_b["바탕"] == f"{PCT} {name}".strip() and row_b["고친줄수"] == 3
|
||||
assert _row(mine, "sangeun", a)["쓰는곳수"] == 0
|
||||
# 바탕 없음
|
||||
c = _sk(mine)
|
||||
row_c = _row(mine, "sangeun", c)
|
||||
|
||||
@@ -72,12 +72,14 @@ def test_list_amount_and_labor_line_holds_upper_sk(world): # noqa: F811
|
||||
},
|
||||
)["이름"]
|
||||
line = store.read("precombo", sc)["문서"]["담은로직"][0]
|
||||
saved, inner = line["참조"], [line["수량"]]
|
||||
saved, inner = line["본뜬"], [line["수량"]]
|
||||
outer = {"담은로직": [{"참조": {**MINE, "키": sc}, "수량": {"손": 1}}]}
|
||||
got = labor.calc(outer, {})
|
||||
fed = labor.calc(outer, {f"{refs.ref_id({**MINE, '키': sc})}#0": 3})
|
||||
direct = labor.calc({"담은로직": [{"참조": {**COMPANY, "키": sk}, "수량": {"손": 3}}]}, {})
|
||||
assert saved == {**COMPANY, "키": sk} # 참조 = 회사 SK 그대로
|
||||
# 740-1 — 회사 SK = 로직 줄 + 패치로 펼쳐 담음 · 출처 = 본뜬
|
||||
assert {k: saved[k] for k in ("층", "주인", "키")} == {**COMPANY, "키": sk}
|
||||
assert line["로직"] and "참조" not in line
|
||||
# 723-1 · 내역 17-1 — 일위대가 안 줄 수량은 저장 안 함(null) · 쓰는 자리(바깥 문서)에서 들어옴
|
||||
assert inner == [None]
|
||||
assert got["반영합"]["계"] == 0 # 안 줄 수량 없음 = 금액 빠짐
|
||||
|
||||
@@ -117,10 +117,10 @@ def test_B09_서고_사본_값_같음_원본판(world, tmp_path):
|
||||
assert library.source_version(built) == mark # 다시 모아도 같은 판
|
||||
again = library.import_copy(root, "precombo", "personal", "7/42", sc)
|
||||
assert again == {"폴더": made["폴더"], "만듦": False}
|
||||
with store.at(world["personal"]): # SK 고침 → 원본판 다름
|
||||
with store.at(world["personal"]): # 740-1 — SK 고쳐도 SC 는 펼친 사본이라 원본판 그대로
|
||||
now = store.read("sangeun", ref["ref"]["키"])
|
||||
store.write("sangeun", ref["ref"]["키"], now["판"], {**now["문서"], "덮개": {}})
|
||||
assert library.source_version(library._build("precombo", "personal", "7/42", sc)[0]) != mark
|
||||
assert library.source_version(library._build("precombo", "personal", "7/42", sc)[0]) == mark
|
||||
|
||||
|
||||
def test_SK_없는_사본_그대로():
|
||||
|
||||
@@ -21,7 +21,7 @@ from B09_Estimation import B09_Estimation_Refresh as refresh
|
||||
from B09_Estimation import B09_Estimation_Sangeun as sangeun
|
||||
from B09_Estimation.B09_Estimation_Calc_Base import compute_base
|
||||
from B09_Estimation.B09_Estimation_Calc_Labor import compute_labor
|
||||
from B09_Estimation.B09_Estimation_Labor_Source import ASK, daega_inputs
|
||||
from B09_Estimation.B09_Estimation_Labor_Source import daega_inputs
|
||||
from B09_Estimation.B09_Estimation_Linked import linked_resource_keys
|
||||
from M02_MasterTemplete import M02_Item_Copy as item_copy
|
||||
from M02_MasterTemplete import M02_Item_Snapshot as snapshots
|
||||
@@ -122,24 +122,18 @@ def test_SK_단가_덮개_속_일위대가_펼침(world, tmp_path):
|
||||
if not _engine_ready(world):
|
||||
pytest.skip("715-1b 엔진 입력 덧칸(단가 · 값묶음) 뒤 — 지금은 M02 SK 셈도 못 함")
|
||||
key = _mine(world, full=True)
|
||||
# 723-1 속 일위대가 안 줄 수량 없음 → M02 SK = 그 줄만 빠짐(725-2) · B09 = 그 줄만 빈 줄
|
||||
gone = _calc(world["personal"], key)["빠진줄"]
|
||||
assert len(gone) == 1 and "수량 없음" in gone[0]["까닭"]
|
||||
# 740-1 — 속 일위대가 = 넣을 때 펼쳐 담음(SC 로직 줄 = 로직 더한줄 · 본뜬) → 빠진 줄 없음
|
||||
live = _calc(world["personal"], key)
|
||||
added = _added(world, key)
|
||||
assert live["빠진줄"] == [] and not any("참조" in x for x in added)
|
||||
assert added[-1]["로직"] == PCT and added[-1]["본뜬"]["키"].startswith("SC-")
|
||||
root, folder = _lib(world, tmp_path, key)
|
||||
daega, done, _ = _b09(root)
|
||||
block = done[daega["libs"][folder]]
|
||||
lines = block["lines"]
|
||||
assert lines[1]["kind"] == "fixed" and lines[1]["prices"]["labor"] == pytest.approx(200000)
|
||||
sc_line = next(ln for ln in lines if ln["kind"] == "daega")
|
||||
sc = done[sc_line["key"]]
|
||||
assert sc["kind"] == "사전조합" and sc_line["qty"] == 2
|
||||
assert sc["blank"] == 1 and sc["lines"][0]["why"] == ASK and block["blank"] >= 1
|
||||
# 이 공사에서 입력(702 덮개 qty 1) = 옛 손 1 과 같은 값 → M02 셈(알약 값 1) 과 같은 금액
|
||||
cover = {sc["cover"]: {sc["lines"][0]["cid"]: {"qty": 1}}}
|
||||
block = _b09(root, cover)[1][daega["libs"][folder]]
|
||||
assert block["blank"] == 0
|
||||
sc_ref = next(x for x in _added(world, key) if "참조" in x)["참조"]
|
||||
_same_money(block, _calc_given(world["personal"], key, {f"{refs.ref_id(sc_ref)}#0": 1}))
|
||||
_same_money(block, live)
|
||||
|
||||
|
||||
def _added(world, key):
|
||||
@@ -147,24 +141,6 @@ def _added(world, key):
|
||||
return store.read("sangeun", key)["문서"]["더한줄"]
|
||||
|
||||
|
||||
def _calc_given(folder, key, pills):
|
||||
"""M02 SK 셈 — 속 일위대가 안 줄 수량 = 알약 값(723-1 `Expand` given)."""
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
from M02_MasterTemplete.M02_Labor_Expand import Expand
|
||||
|
||||
whole = m01.loaded()[1]
|
||||
with store.at(folder):
|
||||
clean = sd.clean(store.read("sangeun", key)["문서"], None, whole)
|
||||
me = refs.ref_id({"층": refs.here()[0], "주인": refs.here()[1], "키": clean["키"]})
|
||||
got = calc.run(
|
||||
clean, whole, None, refs.read_ref, Expand(whole, refs.read_ref, pills), (me,)
|
||||
)
|
||||
assert not got.get("까닭")
|
||||
return got
|
||||
|
||||
|
||||
def test_구조물_사본_SK_줄_업데이트_덮개_입력값_고른자재_굳힌키_그대로(
|
||||
world, tmp_path, monkeypatch
|
||||
):
|
||||
|
||||
@@ -76,8 +76,8 @@ def test_line_without_input_only_that_line_drops(world, monkeypatch): # noqa: F
|
||||
assert up["기준"] is None and "입력 「지역」 없음" in up["까닭"]
|
||||
|
||||
|
||||
def test_precombo_without_qty_only_that_line_drops(world): # noqa: F811
|
||||
"""수량 없는 일위대가 줄(723-1) = 그 줄만 금액 — · 까닭 · 나머지 계 같음."""
|
||||
def test_precombo_in_sk_flattened_no_gap(world): # noqa: F811
|
||||
"""740-1 — SK 에 넣은 일위대가 = 줄마다 더한줄로 펼쳐 담음(수량 = 넣은 수 × 1) · 빠진 줄 없음."""
|
||||
with store.at(world["personal"]):
|
||||
sc = refs.create(
|
||||
"precombo", {"이름": "sub7임시SC", "단위": "㎥", "담은로직": [{"로직": PCT}]}
|
||||
@@ -86,10 +86,9 @@ def test_precombo_without_qty_only_that_line_drops(world): # noqa: F811
|
||||
doc = _sk(world["personal"], 더한줄=[line, PCT_LINE])
|
||||
rest = _sk(world["personal"], 더한줄=[PCT_LINE])
|
||||
got, want = _run(world["personal"], doc), _run(world["personal"], rest)
|
||||
gap = _line(got, "add:#0")
|
||||
assert gap["금액"] is None and gap["까닭"] == calc.NO_QTY_WHY and gap["이름"] == "sub7임시SC"
|
||||
assert [x["cid"] for x in got["빠진줄"]] == ["add:#0"]
|
||||
assert got["기준"]["계"] == pytest.approx(float(want["기준"]["계"]), rel=1e-9)
|
||||
assert doc["더한줄"][0]["로직"] == PCT and doc["더한줄"][0]["본뜬"]["키"] == sc["이름"]
|
||||
assert got["빠진줄"] == []
|
||||
assert got["기준"]["계"] == pytest.approx(3 * float(want["기준"]["계"]), rel=1e-9)
|
||||
|
||||
|
||||
def test_screen_shows_dropped_lines():
|
||||
|
||||
@@ -126,8 +126,8 @@ def test_실무_단가산출_기초_SK_고름대기_일위대가_막음(client):
|
||||
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())
|
||||
refs_sk = [x for x in doc["담은로직"] if str((x.get("참조") or {}).get("키")).startswith("SK")]
|
||||
assert refs_sk # 단가산출 줄 = SK 참조(고정 줄 대신)
|
||||
refs_sk = [x for x in doc["담은로직"] if str((x.get("본뜬") or {}).get("키")).startswith("SK")]
|
||||
assert refs_sk # 단가산출 줄 = SK 펼쳐 담음(740-1 · 로직 줄 + 패치 · 본뜬 = 그 SK)
|
||||
assert client.post(LEFT, json={**body, "줄": picks}).json()["수"] == 0
|
||||
assert master_hash() == before
|
||||
|
||||
@@ -223,7 +223,8 @@ def test_별칭_체크_줄_못옮김_SK_남은줄_그대로_SK_남은못옮김(c
|
||||
ok = client.post(BUILD, json={**body, "줄": [check, *picks]})
|
||||
assert ok.status_code == 200, ok.text
|
||||
doc = json.loads((folder / "precombo" / f"{ok.json()['참조']['키']}.json").read_bytes())
|
||||
assert any((x.get("참조") or {}).get("키") == sk["키"] for x in doc["담은로직"]) # 차이 SK 참조
|
||||
# 차이 SK = 펼쳐 담음(740-1 · 본뜬 = 그 SK)
|
||||
assert any((x.get("본뜬") or {}).get("키") == sk["키"] for x in doc["담은로직"])
|
||||
assert master_hash() == before
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
묶음 = 이 층 + 위 층 같은 지문 · 합치기 = 쓰는 곳 참조 → 남김(금액 같음) · 지운 문서 `_합친것/` ·
|
||||
되돌림 = 문서 · 참조 되살림 · 지문 · 판 다르면 409 · 권한 밖 · 같은 참조 두 번 = 그 문서 남김 ·
|
||||
쓰는 곳 = 구조물 조합(740-1 — 단가산출 · 일위대가끼리는 펼쳐 담겨 참조 없음) ·
|
||||
위 층과 같은 아래 층 문서 = 남김 = 위 층 키 · 일반 사용자 회사 층 = 403.
|
||||
"""
|
||||
|
||||
@@ -55,6 +56,27 @@ def _top(ref_keys, at=MINE):
|
||||
]
|
||||
|
||||
|
||||
def _user(folder, ref_keys, at=MINE, col="x1"):
|
||||
"""쓰는 곳 = 구조물 조합(740-1 — 단가산출 · 일위대가끼리는 펼쳐 담겨 참조 없음)."""
|
||||
lines = [{**x, "수량": {"손": x["수량"]}} for x in _top(ref_keys, at)]
|
||||
with store.at(folder):
|
||||
store.write("labor", col, "", {"열": col, "담은로직": lines, "자재": []})
|
||||
return col
|
||||
|
||||
|
||||
def _lines(folder, col):
|
||||
with store.at(folder):
|
||||
return store.read("labor", col)["문서"]["담은로직"]
|
||||
|
||||
|
||||
def _sum(folder, col):
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
|
||||
with store.at(folder):
|
||||
doc = labor.clean({**store.read("labor", col)["문서"], "열": col})
|
||||
return float(labor.calc(doc, {})["반영합"]["계"])
|
||||
|
||||
|
||||
def _merge(api, keep, gone, 판, layer="personal"):
|
||||
body = {"남김": keep, "지움": gone, "판": 판}
|
||||
return api.post(URL.format(layer) + "/merge", json=body)
|
||||
@@ -64,14 +86,14 @@ def test_권한_밖이면_안_지움(api, world, monkeypatch):
|
||||
me = world["personal"]
|
||||
a = _make(me, 이름="가", 바탕={"로직": PCT})
|
||||
c = _make(me, 이름="다", 바탕={"로직": PCT})
|
||||
top = _make(me, 이름="위", 단위="식", 바탕=None, 더한줄=_top([c]))
|
||||
top = _user(me, [c])
|
||||
monkeypatch.setattr(same_router, "_can_write", lambda session, at: False)
|
||||
판 = api.get(URL.format("personal")).json()["판"]
|
||||
res = _merge(api, {**MINE, "키": a}, [c], 판)
|
||||
left = res.json()["남김"]
|
||||
assert res.status_code == 200 and left[0]["키"] == c and "권한 밖" in left[0]["쓰는곳"][0]
|
||||
assert res.json()["합침"] == [] and (me / "sangeun" / f"{c}.json").is_file()
|
||||
assert _read(me, top)["더한줄"][0]["참조"]["키"] == c
|
||||
assert _lines(me, top)[0]["참조"]["키"] == c
|
||||
|
||||
|
||||
def test_합치기_참조_옮김_금액_같음_되돌림(api, world):
|
||||
@@ -79,8 +101,8 @@ def test_합치기_참조_옮김_금액_같음_되돌림(api, world):
|
||||
a = _make(me, 이름="밑", 바탕={"로직": PCT})
|
||||
b = _make(me, 이름="다른 이름 같은 내용", 바탕={"로직": PCT})
|
||||
_make(me, 이름="다른 내용", 바탕={"로직": PCT}, 더한줄=_top([])[-1:])
|
||||
top = _make(me, 이름="위", 단위="식", 바탕=None, 더한줄=_top([b]))
|
||||
before = _total(me, top)
|
||||
top = _user(me, [b])
|
||||
before = _sum(me, top)
|
||||
got = api.get(URL.format("personal"))
|
||||
assert got.status_code == 200, got.text
|
||||
view = got.json()
|
||||
@@ -96,16 +118,16 @@ def test_합치기_참조_옮김_금액_같음_되돌림(api, world):
|
||||
assert out["묶음"] == [] and [g["키"] for g in out["합친것"]] == [b]
|
||||
assert not (me / "sangeun" / f"{b}.json").exists()
|
||||
assert (me / same_router.GONE / f"{b}.json").is_file()
|
||||
assert _read(me, top)["더한줄"][0]["참조"]["키"] == a
|
||||
assert _total(me, top) == pytest.approx(before)
|
||||
assert _lines(me, top)[0]["참조"]["키"] == a
|
||||
assert _sum(me, top) == pytest.approx(before)
|
||||
|
||||
back = api.post(URL.format("personal") + "/undo", json={"키": b})
|
||||
assert back.status_code == 200, back.text
|
||||
assert back.json()["되돌린곳수"] == 1 and back.json()["합친것"] == []
|
||||
assert (me / "sangeun" / f"{b}.json").is_file()
|
||||
assert not (me / same_router.GONE / f"{b}.json").exists()
|
||||
assert _read(me, top)["더한줄"][0]["참조"]["키"] == b
|
||||
assert _total(me, top) == pytest.approx(before)
|
||||
assert _lines(me, top)[0]["참조"]["키"] == b
|
||||
assert _sum(me, top) == pytest.approx(before)
|
||||
assert api.post(URL.format("personal") + "/undo", json={"키": b}).status_code == 404
|
||||
|
||||
|
||||
@@ -130,24 +152,25 @@ def test_권한_밖_남김_둘다_담은_문서(api, world, monkeypatch):
|
||||
a = _make(me, 이름="가", 바탕={"로직": PCT})
|
||||
b = _make(me, 이름="나", 바탕={"로직": PCT})
|
||||
c = _make(me, 이름="다", 바탕={"로직": PCT})
|
||||
both = _make(me, 이름="둘 다", 단위="식", 바탕=None, 더한줄=_top([a, b]))
|
||||
before = _total(me, both)
|
||||
both = _user(me, [a, b])
|
||||
before = _sum(me, both)
|
||||
판 = api.get(URL.format("personal")).json()["판"]
|
||||
res = _merge(api, {**MINE, "키": a}, [b, c], 판) # 둘 다 담은 문서 = 두 줄 모두 a · 금액 같음
|
||||
# 둘 다 담은 구조물 조합 = 같은 참조 두 번이 되어 못 옮김 → b 남김 · c 만 합침 · 금액 같음
|
||||
res = _merge(api, {**MINE, "키": a}, [b, c], 판)
|
||||
assert res.status_code == 200, res.text
|
||||
assert [r["키"] for r in res.json()["합침"]] == [b, c]
|
||||
assert [x["참조"]["키"] for x in _read(me, both)["더한줄"][:2]] == [a, a]
|
||||
assert _total(me, both) == pytest.approx(before)
|
||||
for key in (b, c):
|
||||
assert api.post(URL.format("personal") + "/undo", json={"키": key}).status_code == 200
|
||||
assert [r["키"] for r in res.json()["합침"]] == [c]
|
||||
assert [r["키"] for r in res.json()["남김"]] == [b]
|
||||
assert [x["참조"]["키"] for x in _lines(me, both)[:2]] == [a, b]
|
||||
assert _sum(me, both) == pytest.approx(before)
|
||||
assert api.post(URL.format("personal") + "/undo", json={"키": c}).status_code == 200
|
||||
|
||||
|
||||
def test_위층과_같으면_남김_위층_키(api, world):
|
||||
me, firm = world["personal"], world["company"]
|
||||
up = _make(firm, 이름="회사 것", 바탕={"로직": PCT})
|
||||
mine = _make(me, 이름="내 것", 바탕={"로직": PCT})
|
||||
top = _make(me, 이름="위", 단위="식", 바탕=None, 더한줄=_top([mine]))
|
||||
before = _total(me, top)
|
||||
top = _user(me, [mine])
|
||||
before = _sum(me, top)
|
||||
view = api.get(URL.format("personal")).json()
|
||||
rows = view["묶음"][0]["문서"]
|
||||
assert [(r["층"], r["키"], r["위층"]) for r in rows] == [
|
||||
@@ -156,8 +179,8 @@ def test_위층과_같으면_남김_위층_키(api, world):
|
||||
]
|
||||
res = _merge(api, {**COMPANY, "키": up}, [mine], view["판"])
|
||||
assert res.status_code == 200, res.text
|
||||
assert _read(me, top)["더한줄"][0]["참조"] == {**COMPANY, "키": up}
|
||||
assert _total(me, top) == pytest.approx(before)
|
||||
assert _lines(me, top)[0]["참조"] == {**COMPANY, "키": up}
|
||||
assert _sum(me, top) == pytest.approx(before)
|
||||
assert (firm / "sangeun" / f"{up}.json").is_file()
|
||||
# 위 층 문서는 지울 수 없음 · 일반 사용자는 회사 층 합치기 403
|
||||
assert _merge(api, {**MINE, "키": mine}, [up], view["판"]).status_code in (403, 404)
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
|
||||
"""740-1 단가산출(SK) · 일위대가(SC) 서로 독립 · 연결 = M01 키 · 초기화(임시 storage · M01 읽기만).
|
||||
|
||||
- 넣기 = 펼침(SK 에 SK = 몸 통째 한 줄 · SC 에 SK = 로직 줄 + 패치 · SK 에 SC = 줄마다
|
||||
더한줄 · SC 에 SC = 넣기 길만 안 줄 펴 넣음 · 저장 · 읽기는 하위 호표 참조 그대로) · 줄 `본뜬` ·
|
||||
펼치기 전후 금액 같음 · 원본 고쳐도 · 지워도 그대로
|
||||
- 분석 579-2 실측 E1(129,073 → 481,406) = 이제 안 일어남
|
||||
- 바탕 SK 참조 · 패치 SK 참조 = 덮개 겹침 · 못 펼침(겹침 · 바탕 없음 · 환산) = 넣기 400 ·
|
||||
저장은 옛 참조
|
||||
- 초기화 = 덮개 · 뺀줄 · 더한줄 단가 걷음 → M01 값 · 옵션 · 항목 · 수량 남음 · 길(미리 보기 · 씀)
|
||||
- 옮기기 함수 = 옛 참조 문서 펼쳐 씀(전후 같음 · 겹침 남김 · 두 번째 = 할 것 없음) · 733 지문 같음
|
||||
- B09 = SC 덮개 줄 굳힘 → 대가 블록 금액 = M02(옛 산근 길은 덮개를 안 읽었음)
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from common_util.common_util_auth import verify_session
|
||||
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_Flat as flat
|
||||
from M02_MasterTemplete import M02_Library_Same as same
|
||||
from M02_MasterTemplete import M02_Library_Same_Router as same_router
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
from resources.tester.test_m02_labor_ref import MINE, world # noqa: F401
|
||||
|
||||
GF = "GF000099" # 측구터파기 토사 · 원/㎥
|
||||
PCT = "GC001109" # 원/㎡ · 지붕잇기공 · 보통인부 · 「노무비 * 3 / 100」
|
||||
ADD = {
|
||||
"종류": "인력",
|
||||
"요소": "LB000002",
|
||||
"비목": "노무비",
|
||||
"이름": "보통인부",
|
||||
"단위": "인",
|
||||
"수량": 0.5,
|
||||
}
|
||||
USER = {"user_id": 42, "company_id": 7, "role": "USER", "is_master": False, "name": "갑"}
|
||||
|
||||
|
||||
def _whole():
|
||||
return m01.loaded()[1]
|
||||
|
||||
|
||||
def _raw(folder, kind, **doc):
|
||||
"""옛 꼴 문서(참조 줄 그대로) — 저장 길을 거치지 않고 파일로."""
|
||||
(folder / kind).mkdir(parents=True, exist_ok=True)
|
||||
key = f"{refs.PREFIX[kind]}-{len(list((folder / kind).glob('*.json'))) + 1:06d}"
|
||||
head = {"종류": refs.TITLE[kind], "키": key, "단위": "㎡"}
|
||||
body = {"입력값": {}, "덮개": {}, "뺀줄": [], "고름": {}, "자재표시": {}, "더한줄": []}
|
||||
full = {**head, **(body if kind == "sangeun" else {"자재": []}), **doc}
|
||||
(folder / kind / f"{key}.json").write_text(json.dumps(full, ensure_ascii=False), "utf-8")
|
||||
return key
|
||||
|
||||
|
||||
def _make(folder, kind, **doc):
|
||||
with store.at(folder):
|
||||
extra = {"더한줄": []} if kind == "sangeun" else {}
|
||||
return refs.create(kind, {"단위": "㎡", **extra, **doc})["이름"]
|
||||
|
||||
|
||||
def _read(folder, kind, key):
|
||||
with store.at(folder):
|
||||
return store.read(kind, key)["문서"]
|
||||
|
||||
|
||||
def _sk_total(folder, doc):
|
||||
with store.at(folder):
|
||||
return float(calc.run(sd.clean(doc, None, _whole()), _whole())["1단위"]["계"])
|
||||
|
||||
|
||||
def _sc_value(folder, key, read=None):
|
||||
with store.at(folder):
|
||||
got = flat._value({**MINE, "키": key}, read or refs.read_ref, _whole())
|
||||
assert got is not None
|
||||
return got["계"]
|
||||
|
||||
|
||||
def _y(folder):
|
||||
"""원본 SK — 바탕 PCT(% 덧줄) · 품 덮개 · 인력 더한줄(덧줄 밑에 듦)."""
|
||||
return _make(
|
||||
folder,
|
||||
"sangeun",
|
||||
이름="원본",
|
||||
바탕={"로직": PCT},
|
||||
덮개={"labor:LB000036#0": {"qty": 0.036}},
|
||||
더한줄=[ADD],
|
||||
)
|
||||
|
||||
|
||||
def test_SK_에_SK_몸_통째_금액_같음_원본_고쳐도_지워도_그대로(world):
|
||||
me = world["personal"]
|
||||
y = _y(me)
|
||||
legacy = {
|
||||
"이름": "밖",
|
||||
"단위": "㎥",
|
||||
"바탕": {"로직": GF},
|
||||
"더한줄": [{"참조": {**MINE, "키": y}, "수량": 2}],
|
||||
}
|
||||
before = _sk_total(me, {"키": "SK-000099", **legacy})
|
||||
x = _make(me, "sangeun", **legacy)
|
||||
doc = _read(me, "sangeun", x)
|
||||
entry = doc["더한줄"][0]
|
||||
assert not refs.refs_in(doc)
|
||||
assert entry["로직"] == PCT and entry["덮개"] and entry["더한줄"][0]["요소"] == "LB000002"
|
||||
assert entry["본뜬"]["키"] == y and entry["본뜬"]["이름"] == "원본" and entry["본뜬"]["때"]
|
||||
assert _sk_total(me, doc) == pytest.approx(before, rel=1e-9)
|
||||
with store.at(me): # 원본 고침 · 지움 → 넣은 쪽 그대로
|
||||
now = store.read("sangeun", y)
|
||||
store.write(
|
||||
"sangeun", y, now["판"], {**now["문서"], "덮개": {"labor:LB000002#0": {"qty": 2}}}
|
||||
)
|
||||
assert refs.users({**MINE, "키": y}) == []
|
||||
store.delete("sangeun", y)
|
||||
assert _sk_total(me, _read(me, "sangeun", x)) == pytest.approx(before, rel=1e-9)
|
||||
with store.at(me): # 화면 셈 길 = 넣은 줄 세부 펼침(안 더한줄 = add:#0/add:#0)
|
||||
got = calc.calc_doc(_read(me, "sangeun", x))
|
||||
sub = next(r for r in got["줄"] if r["cid"] == "add:#0")["펼침"]
|
||||
assert "add:#0/add:#0" in [r["cid"] for r in sub]
|
||||
|
||||
|
||||
def test_SC_에_SK_패치_겹침_E1_원본_고쳐도_그대로(world):
|
||||
"""분석 579-2 E1 — 밖 SC(SK 참조 + 줄 패치 · 안 SC) 의 원본 SK 다른 줄을 고쳐도 밖 금액
|
||||
그대로."""
|
||||
me = world["personal"]
|
||||
sk = _make(me, "sangeun", 이름="밑", 바탕={"로직": PCT}, 더한줄=[])
|
||||
inner = _make(me, "precombo", 이름="안", 담은로직=[{"로직": "GC001111"}])
|
||||
lines = [
|
||||
{"참조": {**MINE, "키": sk}, "패치": {"덮개": {"labor:LB000036#0": {"qty": 0.5}}}},
|
||||
{"참조": {**MINE, "키": inner}},
|
||||
]
|
||||
old = _raw(me, "precombo", 이름="옛 밖", 담은로직=lines)
|
||||
before = _sc_value(me, old)
|
||||
host = _make(me, "precombo", 이름="밖", 담은로직=lines)
|
||||
doc = _read(me, "precombo", host)
|
||||
assert doc["담은로직"][0]["로직"] == PCT and doc["담은로직"][0]["본뜬"]["키"] == sk
|
||||
assert doc["담은로직"][0]["패치"]["덮개"] == {"labor:LB000036#0": {"qty": 0.5}}
|
||||
assert doc["담은로직"][1]["참조"]["키"] == inner # 하위 호표(SC 안 SC) = 참조 그대로
|
||||
assert _sc_value(me, host) == pytest.approx(before, rel=1e-9)
|
||||
with store.at(me): # E1 — 원본 SK 의 덮지 않은 줄 품 2
|
||||
now = store.read("sangeun", sk)
|
||||
store.write(
|
||||
"sangeun", sk, now["판"], {**now["문서"], "덮개": {"labor:LB000002#0": {"qty": 2}}}
|
||||
)
|
||||
assert _sc_value(me, old) > before * 3 # 옛 참조 = 따라감(129,073 → 481,406 꼴)
|
||||
assert _sc_value(me, host) == pytest.approx(before, rel=1e-9) # 펼친 것 = 그대로
|
||||
|
||||
|
||||
def test_SC_에_SC_펴넣기_실무수량_겹침_넣기_400(world):
|
||||
me = world["personal"]
|
||||
hands = {"층": "실무자료", "실무수량": {"줄": {"0": 3}, "자재": {}}}
|
||||
inner = _make(me, "precombo", 이름="안", 담은로직=[{"로직": PCT}], 본뜬=hands)
|
||||
outer_hands = {"층": "실무자료", "실무수량": {"줄": {"0": 2, "1": 5}, "자재": {}}}
|
||||
lines = [{"로직": GF}, {"참조": {**MINE, "키": inner}, "별칭": "안쪽"}]
|
||||
old = _raw(me, "precombo", 이름="옛", 담은로직=lines, 본뜬=outer_hands)
|
||||
assert flat.flat_doc("precombo", _read(me, "precombo", old))["담은로직"][1][
|
||||
"참조"
|
||||
] # 저장 · 읽기
|
||||
got = flat.flat_sc(_read(me, "precombo", old), refs.read_ref, _whole()) # 넣기 길 = 펴 넣음
|
||||
assert [x["로직"] for x in got["담은로직"]] == [GF, PCT] and got["담은로직"][1][
|
||||
"별칭"
|
||||
] == "안쪽"
|
||||
assert got["본뜬"]["실무수량"]["줄"] == {"0": 2, "1": 15} # 바깥 5 × 안 3
|
||||
|
||||
def mine(r):
|
||||
return got if r["키"] == old else refs.read_ref(r)
|
||||
|
||||
assert _sc_value(me, old, mine) == pytest.approx(_sc_value(me, old), rel=1e-9)
|
||||
# 같은 기초가 겹침 — 넣기 길 = 400
|
||||
dup = [{"로직": PCT}, {"참조": {**MINE, "키": inner}}]
|
||||
kept = _make(me, "precombo", 이름="겹침", 담은로직=dup)
|
||||
assert _read(me, "precombo", kept)["담은로직"][1]["참조"]["키"] == inner
|
||||
with store.at(me), pytest.raises(store.StoreError) as e:
|
||||
flat.flat_lines("precombo", {**MINE, "키": inner}, None, {"담은로직": [{"로직": PCT}]})
|
||||
assert e.value.status == 400 and "겹침" in str(e.value.detail)
|
||||
|
||||
|
||||
def test_SK_에_SC_줄마다_더한줄(world):
|
||||
me = world["personal"]
|
||||
sc = _make(me, "precombo", 이름="조합", 담은로직=[{"로직": GF}])
|
||||
with store.at(me):
|
||||
got = flat.flat_lines("sangeun", {**MINE, "키": sc}, 2)["더한줄"]
|
||||
assert len(got) == 1 and got[0]["로직"] == GF and got[0]["수량"] == 2 # 실무수량 없음 = 1 × 2
|
||||
assert got[0]["본뜬"]["키"] == sc
|
||||
# 742-1 저장 수량(수량.손)이 있으면 그 수 · 안 SC 펴기 = 바깥 × 안
|
||||
from decimal import Decimal
|
||||
|
||||
hand = flat.sc_adds({"담은로직": [{"로직": GF, "수량": {"손": 3}}]}, Decimal(2), _whole(), {})
|
||||
assert hand[0]["수량"] == 6
|
||||
lines, _, _ = flat._spread(
|
||||
{"담은로직": [{"로직": GF, "수량": {"손": 3}}]}, {"수량": {"손": 2}}, None, {}
|
||||
)
|
||||
assert lines[0]["수량"] == {"손": 6}
|
||||
doc = {"키": "SK-000099", "이름": "x", "단위": "㎥", "바탕": None, "더한줄": got}
|
||||
one = float(labor.run_line(_whole(), GF, {})["기준"]["계"])
|
||||
assert _sk_total(me, doc) == pytest.approx(2 * one, rel=1e-9)
|
||||
|
||||
|
||||
def test_바탕_SK_겹침_못_펼침_까닭(world):
|
||||
me = world["personal"]
|
||||
under = _make(
|
||||
me, "sangeun", 이름="밑", 바탕={"로직": PCT}, 덮개={"labor:LB000036#0": {"qty": 0.1}}
|
||||
)
|
||||
over = {"이름": "위", "단위": "㎡", "바탕": {"참조": {**MINE, "키": under}}, "더한줄": [ADD]}
|
||||
over |= {"덮개": {"labor:LB000036#0": {"배율": 2}}, "뺀줄": ["labor:LB000002#0"]}
|
||||
before = _sk_total(me, {"키": "SK-000099", **over})
|
||||
got = flat.flat_doc("sangeun", {"키": "SK-000099", **over})
|
||||
assert got["바탕"] == {"로직": PCT} and got["덮개"]["labor:LB000036#0"]["qty"] == 0.2
|
||||
assert got["뺀줄"] == ["labor:LB000002#0"]
|
||||
assert _sk_total(me, got) == pytest.approx(before, rel=1e-9)
|
||||
# SC 에 바탕 없는 SK · 단위 환산 SK = 못 넣음
|
||||
bare = _make(me, "sangeun", 이름="바탕 없음", 바탕=None, 더한줄=[ADD])
|
||||
with store.at(me):
|
||||
for key, why in ((bare, "바탕 기초 없는"),):
|
||||
with pytest.raises(store.StoreError) as e:
|
||||
flat.flat_lines("precombo", {**MINE, "키": key})
|
||||
assert e.value.status == 400 and why in str(e.value.detail)
|
||||
|
||||
|
||||
def test_초기화_SK_SC_M01_값_옵션_남음(world):
|
||||
me = world["personal"]
|
||||
rock = {"보정작업": "암석작업(연암・보통암・경암)"}
|
||||
entry = {
|
||||
"종류": "로직",
|
||||
"로직": PCT,
|
||||
"입력값": {},
|
||||
"수량": 1,
|
||||
"덮개": {"labor:LB000036#0": {"qty": 1}},
|
||||
}
|
||||
priced = {**ADD, "단가": 1}
|
||||
x = _make(
|
||||
me,
|
||||
"sangeun",
|
||||
이름="고친 것",
|
||||
단위="㎥",
|
||||
바탕={"로직": GF},
|
||||
입력값=rock,
|
||||
덮개={"labor:LB000002#0": {"qty": 1}},
|
||||
뺀줄=["logic:GC000267#0"],
|
||||
더한줄=[entry, priced],
|
||||
)
|
||||
with store.at(me):
|
||||
look = flat.reset_doc("sangeun", x, None, True, None)
|
||||
assert look["걷힘"] == 4 and _read(me, "sangeun", x)["덮개"] # 미리 보기 = 안 씀
|
||||
done = flat.reset_doc("sangeun", x, None, False, None)
|
||||
doc = _read(me, "sangeun", x)
|
||||
assert doc["덮개"] == {} and doc["뺀줄"] == [] and doc["입력값"] == rock
|
||||
assert [x.get("요소") or x.get("로직") for x in doc["더한줄"]] == [PCT, "LB000002"]
|
||||
assert "덮개" not in doc["더한줄"][0] and "단가" not in doc["더한줄"][1]
|
||||
assert done["걷힘"] == 4
|
||||
plain = {"키": "SK-000099", "이름": "x", "단위": "㎥", "바탕": {"로직": GF}, "입력값": rock}
|
||||
plain["더한줄"] = [{**entry, "덮개": {}}, ADD]
|
||||
assert _sk_total(me, doc) == pytest.approx(_sk_total(me, plain), rel=1e-9)
|
||||
sc = _make(
|
||||
me,
|
||||
"precombo",
|
||||
이름="줄 덮개",
|
||||
담은로직=[
|
||||
{"로직": PCT, "패치": {"덮개": {"labor:LB000036#0": {"qty": 1}}, "더한줄": [priced]}}
|
||||
],
|
||||
)
|
||||
with store.at(me):
|
||||
got = flat.reset_doc("precombo", sc, None, False, None)
|
||||
line = _read(me, "precombo", sc)["담은로직"][0]
|
||||
assert got["걷힘"] == 2 and line["패치"] == {"더한줄": [{**ADD, "수량": 0.5}]}
|
||||
|
||||
|
||||
def test_길_넣기_초기화_권한(world):
|
||||
app = FastAPI()
|
||||
app.include_router(same_router.router)
|
||||
app.dependency_overrides[verify_session] = lambda: USER
|
||||
client = TestClient(app)
|
||||
me = world["personal"]
|
||||
y = _y(me)
|
||||
url = "/api/m02/layers/personal/{}-{}"
|
||||
res = client.post(url.format("sangeun", "flat"), json={"참조": {**MINE, "키": y}, "수량": 1})
|
||||
assert res.status_code == 200, res.text
|
||||
assert res.json()["더한줄"][0]["본뜬"]["키"] == y
|
||||
bad = client.post(url.format("precombo", "flat"), json={"참조": {**MINE, "키": "SK-000777"}})
|
||||
assert bad.status_code == 400
|
||||
look = client.post(url.format("sangeun", "reset"), json={"키": y})
|
||||
assert look.status_code == 200 and look.json()["걷힘"] == 1
|
||||
firm = client.post("/api/m02/layers/company/sangeun-reset", json={"키": y, "미리보기": False})
|
||||
assert firm.status_code == 403 # 일반 사용자 회사 층 쓰기 막힘
|
||||
|
||||
|
||||
def test_옮기기_함수_전후_같음_겹침_남김(world):
|
||||
me = world["personal"]
|
||||
y = _y(me)
|
||||
inner = _make(me, "precombo", 이름="안", 담은로직=[{"로직": GF}])
|
||||
sk = _raw(
|
||||
me,
|
||||
"sangeun",
|
||||
이름="옛 SK",
|
||||
단위="㎥",
|
||||
바탕={"로직": GF},
|
||||
더한줄=[{"참조": {**MINE, "키": y}, "수량": 1}],
|
||||
)
|
||||
sc = _raw(
|
||||
me,
|
||||
"precombo",
|
||||
이름="옛 SC",
|
||||
담은로직=[{"참조": {**MINE, "키": y}}, {"참조": {**MINE, "키": inner}}],
|
||||
)
|
||||
dup = _raw(
|
||||
me, "precombo", 이름="겹침", 담은로직=[{"로직": GF}, {"참조": {**MINE, "키": inner}}]
|
||||
)
|
||||
before = {k: _sc_value(me, k) for k in (sc, dup)} | {
|
||||
sk: _sk_total(me, _read(me, "sangeun", sk))
|
||||
}
|
||||
look = {r["키"]: r for r in flat.migrate(False)}
|
||||
assert look[sk]["같음"] and look[sc]["같음"] and not look[sc]["씀"]
|
||||
assert look[sc]["남은참조"] == look[dup]["남은참조"] == [inner] # 하위 호표 = 그대로
|
||||
rows = {r["키"]: r for r in flat.migrate(True)}
|
||||
assert rows[sk]["씀"] and rows[sc]["씀"] and not rows[dup]["씀"]
|
||||
assert not refs.refs_in(json.loads((me / "sangeun" / f"{sk}.json").read_bytes()))
|
||||
left = refs.refs_in(json.loads((me / "precombo" / f"{sc}.json").read_bytes()))
|
||||
assert [r["키"] for r in left] == [inner]
|
||||
assert _sk_total(me, _read(me, "sangeun", sk)) == pytest.approx(before[sk], rel=1e-9)
|
||||
assert _sc_value(me, sc) == pytest.approx(before[sc], rel=1e-9)
|
||||
assert _sc_value(me, dup) == pytest.approx(before[dup], rel=1e-9)
|
||||
again = flat.migrate(False) # 두 번째 = 하위 호표 참조만 남음(바꿀 것 없음)
|
||||
assert [r["키"] for r in again] == [sc, dup] and all(r["남은참조"] == [inner] for r in again)
|
||||
|
||||
|
||||
def test_733_지문_옛_참조_펼친_것_같음(world):
|
||||
me = world["personal"]
|
||||
y = _y(me)
|
||||
old = _raw(me, "precombo", 이름="옛", 담은로직=[{"참조": {**MINE, "키": y}}, {"로직": GF}])
|
||||
new = _make(me, "precombo", 이름="새", 담은로직=[{"참조": {**MINE, "키": y}}, {"로직": GF}])
|
||||
assert not refs.refs_in(_read(me, "precombo", new))
|
||||
fingers = {k: same.finger("precombo", _read(me, "precombo", k)) for k in (old, new)}
|
||||
assert fingers[old] == fingers[new]
|
||||
|
||||
|
||||
def test_B09_SC_덮개_줄_대가_금액_M02_같음(world, tmp_path):
|
||||
"""SC 덮개 줄(SK 넣기 꼴) — 옛 B09 = 산근(덮개 안 읽음) · 이제 굳힌 줄 대가 = M02 값."""
|
||||
from B09_Estimation import B09_Estimation_Bill_Library as library
|
||||
from resources.tester.test_719_2_sk_project import _b09, _project
|
||||
|
||||
me = world["personal"]
|
||||
cover = {"labor:LB000036#0": {"qty": 0.5}}
|
||||
sc = _make(
|
||||
me,
|
||||
"precombo",
|
||||
이름="덮은 줄",
|
||||
담은로직=[{"로직": PCT, "패치": {"덮개": cover, "더한줄": [ADD]}}],
|
||||
)
|
||||
m02 = _sc_value(me, sc)
|
||||
bare = float(labor.run_line(_whole(), PCT, {})["기준"]["계"])
|
||||
assert m02 > bare * 2 # 덮개 · 더한줄이 값에 듦(옛 B09 산근 = bare)
|
||||
root = _project(tmp_path / "proj", [])
|
||||
made = library.import_copy(root, "precombo", "personal", "7/42", sc)
|
||||
daega, done, _ = _b09(root)
|
||||
block = done[daega["libs"][made["폴더"]]]
|
||||
line = block["lines"][0]
|
||||
assert line["kind"] == "daega" and block["blank"] == 1 # 안 줄 수량 = 이 공사에서 입력(723-4)
|
||||
cover_qty = {block["cover"]: {line["cid"]: {"qty": 1}}}
|
||||
block = _b09(root, cover_qty)[1][daega["libs"][made["폴더"]]]
|
||||
assert block["blank"] == 0
|
||||
assert block["sum"] == pytest.approx(m02, abs=5) # 끝수(내림)만 다름
|
||||
Reference in New Issue
Block a user