Merge remote-tracking branch 'origin/sub_laptop_7' into sub_laptop_1

This commit is contained in:
2026-10-05 20:31:15 +09:00
3 changed files with 102 additions and 35 deletions
+48 -29
View File
@@ -5,8 +5,9 @@
`{종류:"단가승격", 키, 이름, 단위, 배, 본뜬, 담은로직:[{로직, 입력값}]}` —
줄 하나 · 입력값은 승격 때 고정 · 값 = 로직 기준 ÷ 배(1 단위 값 · `배` 는 저장 때)
precombo `{층}/precombo/SC-######.json` 사전조합
`{종류:"사전조합", 키, 이름, 단위, 본뜬, 담은로직:[{참조, 수량:{손}, 메모}]}` —
승격 · 사전조합만 · 수량은 손(기본 1) · 값 = Σ 안 줄 값 × 수량
`{종류:"사전조합", 키, 이름, 단위, 본뜬, 담은로직:[{참조, 수량:{손}, 메모}
| {로직, 입력값, 메모, 수량:{손}}]}` — 승격 · 사전조합 · 단가산출 로직(51-11) ·
수량은 손(기본 1) · 값 = Σ 안 줄 1 단위 값 × 수량(로직 줄 = 기준 ÷ 배)
참조 = `{층, 주인, 키}`(마스터 주인 null · 회사 `C` · 개인 `C/U`) — 종류는 키 머리(SP · SC) ·
위 층만 가리킴(개인 → 그 개인 · 그 회사 · 마스터 / 회사 → 그 회사 · 마스터 / 마스터 → 마스터) ·
같은 참조 두 번 · 순환(422)은 저장 때 막음 · 계산 때도 깊이 상한(`DEPTH`)에서 멈추고 까닭.
@@ -162,27 +163,21 @@ def clean_doc(kind: str, doc: Any) -> dict[str, Any]:
def _combo_lines(doc: dict, key: str) -> list[dict[str, Any]]:
"""참조 줄 `{참조, 수량, 메모}` · 로직 줄 `{로직, 입력값, 메모, 수량}`(51-11) — 검사는
일위대가와 같은 길(`labor.clean` · 같은 로직 · 같은 참조 두 번 · 입력값) · 수량 기본 {손: 1}."""
from M02_MasterTemplete import M02_Labor_Doc as labor
at_, seen, lines = here(), set(), []
for n, one in enumerate(doc.get("담은로직") or [], start=1):
at = f"사전조합 {n}째"
if not isinstance(one, dict) or one.get("로직") or one.get("참조") is None:
raise _bad(f"{at} · 사전조합은 단가승격 · 사전조합만 담음")
ref = check_ref(one["참조"], at, at_)
if ref_id(ref) in seen:
raise _bad(f"{at} · 같은 참조를 두 번 담음 「{ref['키']}」")
seen.add(ref_id(ref))
memo = one.get("메모")
lines.append(
{
"참조": ref,
"수량": labor._qty(one.get("수량"), at) or {"손": 1},
"메모": str(memo) if memo else None,
}
)
lines = []
for one in labor.clean({"담은로직": doc.get("담은로직") or []})["담은로직"]:
head = {"참조": one["참조"]} if "참조" in one else {"로직": one["로직"]}
if "로직" in one:
head["입력값"] = one["입력값"]
lines.append({**head, "수량": one["수량"] or {"손": 1}, "메모": one["메모"]})
at_ = here()
me = {"층": at_[0], "주인": at_[1], "키": key}
for one in lines:
if "참조" not in one:
continue
if ref_id(one["참조"]) == ref_id(me):
raise _bad(f"순환 — 자기 자신을 담음 「{key}」", 422)
_no_loop(one["참조"], [me, one["참조"]])
@@ -365,19 +360,44 @@ class Expand:
],
}
def _combo(self, doc: dict, trail: tuple[str, ...]) -> dict[str, Any]:
def _logic(self, one: dict, pill: dict | None) -> dict[str, Any]:
"""사전조합 안 로직 줄 — 일위대가 로직 줄과 같은 `run_line` · 배는 물량 알약 것."""
from M02_MasterTemplete import M02_Labor_Doc as labor
pill = pill or {}
got = labor.run_line(self.whole, one["로직"], one.get("입력값"))
times = labor._number(pill.get("배")) or Decimal(1)
base = got.get("기준")
return {
"이름": pill.get("이름") or one["로직"],
"단위": pill.get("단위"),
"기준": {k: Decimal(str(v)) / times for k, v in base.items()} if base else None,
"까닭": got.get("까닭") or (None if base else "돈 아닌 로직"),
}
def _combo(self, doc: dict, trail: tuple[str, ...]) -> dict[str, Any]:
from M01_MasterData import M01_MasterData_Store_Combo as combo_store
from M02_MasterTemplete import M02_Labor_Doc as labor
lines = [one for one in doc.get("담은로직") or [] if isinstance(one, dict)]
by_logic = {
code: pill
for pill in combo_store.combo_pills({"담은로직": lines})["물량"]
for code in pill["로직"]
}
inner, rows, why = [], [], None
for one in doc.get("담은로직") or []:
ref = one.get("참조") if isinstance(one, dict) else None
if not isinstance(ref, dict):
for one in lines:
ref = one.get("참조")
if isinstance(ref, dict):
head, sub = {"참조": ref}, self(ref, trail)
elif one.get("로직"): # 로직 줄(51-11) = 1 단위 값(기준 ÷ 배 · 승격과 같음)
head, sub = {"로직": one["로직"]}, self._logic(one, by_logic.get(one["로직"]))
else:
continue
sub = self(ref, trail)
qty = labor._number((one.get("수량") or {}).get("손"))
qty = Decimal(1) if qty is None else qty
keep = ("이름", "단위", "기준", "까닭")
inner.append({"참조": ref, "수량": qty, **{k: sub.get(k) for k in keep}})
inner.append({**head, "수량": qty, **{k: sub.get(k) for k in keep}})
if sub.get("기준") is None:
why = why or f"「{sub.get('이름')}」 {sub.get('까닭')}"
continue
@@ -424,11 +444,10 @@ def frozen(doc: dict[str, Any]) -> dict[str, dict[str, Any]]:
def frozen_logics(copies: dict[str, Any]) -> list[str]:
"""굳힌 단가승격이 담은 로직 키 — 프로젝트 단가 묶음에 넣음."""
"""굳힌 단가승격 · 사전조합이 담은 로직 키 — 프로젝트 단가 묶음에 넣음."""
return [
str(line["로직"])
for rid, doc in copies.items()
if kind_of(rid.rsplit("|", 1)[-1]) == "promote"
for line in (doc.get("담은로직") or [])[:1]
for doc in copies.values()
for line in doc.get("담은로직") or []
if isinstance(line, dict) and line.get("로직")
]