Merge remote-tracking branch 'origin/sub_laptop_5' into main_laptop_1
This commit is contained in:
@@ -42,6 +42,7 @@ from M01_MasterData import M01_MasterData_Store as m01
|
||||
from M01_MasterData import M01_MasterData_Store_Combo as combo_store
|
||||
from M02_MasterTemplete import M02_Labor_Fixed as fixed
|
||||
from M02_MasterTemplete import M02_Labor_Inputs as labor_inputs
|
||||
from M02_MasterTemplete import M02_Labor_Patch as patch
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_Labor_Spec as spec
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
@@ -127,6 +128,7 @@ def clean(doc: Any) -> dict[str, Any]:
|
||||
"별칭": alias,
|
||||
"메모": str(memo) if memo else None,
|
||||
"수량": _qty(one.get("수량"), where),
|
||||
**patch.ref_clean(one, ref, where), # 그 줄 덮개(715-3)
|
||||
}
|
||||
)
|
||||
continue
|
||||
@@ -155,6 +157,7 @@ def clean(doc: Any) -> dict[str, Any]:
|
||||
**({"켜쌓기율": rate} if rate is not None else {}),
|
||||
**({"고름": picks} if picks else {}),
|
||||
**({"승격": True} if one.get("승격") is True else {}),
|
||||
**patch.clean(one, where, whole, logic), # 그 줄 덮개(715-3)
|
||||
**(
|
||||
{"자재표시": shown} if (shown := _mat_marks(one.get("자재표시"), logic)) else {}
|
||||
),
|
||||
@@ -512,7 +515,7 @@ def calc(
|
||||
lines = []
|
||||
for one in doc.get("담은로직") or []:
|
||||
if _is_ref(one):
|
||||
lines.append(ref_line(one["참조"], expand(one["참조"])))
|
||||
lines.append(ref_line(one["참조"], patch.ref(one, expand)))
|
||||
continue
|
||||
if fixed.is_fixed(one):
|
||||
lines.append(fixed.line(whole, one[fixed.FIXED]))
|
||||
@@ -525,6 +528,9 @@ def calc(
|
||||
"분모": pill.get("단위"),
|
||||
"배": pill.get("배") or 1,
|
||||
}
|
||||
if one.get(patch.FIELD): # 줄 덮개(715-3) = 이름 없는 단가산출 셈 · 별도계상까지
|
||||
lines.append({**line, **patch.run(whole, one, read)})
|
||||
continue
|
||||
got = run_line(whole, code, one.get("입력값"), one.get("켜쌓기율"), one.get("고름"))
|
||||
lines.append({**line, **drop_apart(got, one.get("자재표시"))})
|
||||
mats = [
|
||||
|
||||
@@ -11,6 +11,7 @@ from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Labor_Fixed as fixed
|
||||
from M02_MasterTemplete import M02_Labor_Patch as patch
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
|
||||
@@ -81,10 +82,13 @@ class Expand:
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
|
||||
pill = pill or {}
|
||||
got = labor.run_line(
|
||||
self.whole, one["로직"], one.get("입력값"), one.get("켜쌓기율"), one.get("고름")
|
||||
)
|
||||
got = labor.drop_apart(got, one.get("자재표시")) # 별도계상 로직 자재 뺌(222-1)
|
||||
if one.get(patch.FIELD): # 줄 덮개(715-3) = 이름 없는 단가산출 · 별도계상까지
|
||||
got = patch.run(self.whole, one, self.read)
|
||||
else:
|
||||
got = labor.run_line(
|
||||
self.whole, one["로직"], one.get("입력값"), one.get("켜쌓기율"), one.get("고름")
|
||||
)
|
||||
got = labor.drop_apart(got, one.get("자재표시")) # 별도계상 로직 자재 뺌(222-1)
|
||||
times = labor._number(pill.get("배")) or Decimal(1)
|
||||
base = got.get("기준")
|
||||
return {
|
||||
@@ -109,9 +113,10 @@ class Expand:
|
||||
inner, rows, why = [], [], None
|
||||
for one in lines:
|
||||
ref = one.get("참조")
|
||||
if isinstance(ref, dict):
|
||||
head, sub = {"참조": ref}, self(ref, trail)
|
||||
elif one.get("로직"): # 로직 줄(51-11) = 1 단위 값(기준 ÷ 배 · 승격과 같음)
|
||||
if isinstance(ref, dict): # 줄 덮개(715-3) 있는 SK 참조 = 그 줄만 다시 셈
|
||||
head = {"참조": ref}
|
||||
sub = self(ref, trail) if not one.get(patch.FIELD) else patch.ref(one, self)
|
||||
elif one.get("로직"): # 로직 줄(51-11) = 1 단위 값(기준 ÷ 배 · 승격과 같음) · 덮개 있음
|
||||
head, sub = {"로직": one["로직"]}, self._logic(one, by_logic.get(one["로직"]))
|
||||
elif fixed.is_fixed(one): # 고정 줄(705-3) = 줄 단가 · 비율 줄 = 끝에(`ratio_row`)
|
||||
head, sub = {"고정": one["고정"]}, fixed.line(self.whole, one["고정"])
|
||||
@@ -156,7 +161,14 @@ class Expand:
|
||||
return {
|
||||
"이름": doc.get("이름"),
|
||||
"단위": doc.get("단위"),
|
||||
"머리": head_of(doc),
|
||||
"기준": None if why else labor._sum(rows),
|
||||
"까닭": why,
|
||||
"안": inner,
|
||||
}
|
||||
|
||||
|
||||
def head_of(doc: dict) -> str | None:
|
||||
"""상세 머리 「1 ㎡ 당」(715-3 · 일위대가 = 기준단위 1 당) — 단위 빈 옛 문서는 None."""
|
||||
unit = str(doc.get("단위") or "").strip()
|
||||
return f"1 {unit} 당" if unit else None
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
"""일위대가 · 사전조합 줄 덮개(PLAN 715-3 · 요구서 내역12 4장 16 · 4-1 확정 5).
|
||||
|
||||
줄 칸 `패치` = 단가산출(SK) 문서 꼴 `{덮개, 뺀줄, 더한줄}`(한 벌 · `M02_Sangeun_Doc`) — 그 줄에만 ·
|
||||
로직 줄(`{로직, 입력값, 고름, 자재표시, 패치}`) · 단가산출 참조 줄(`{참조: SK, 패치}`)만 받음 ·
|
||||
줄 `고름` · `자재표시`(호표 차례 열쇠)는 바탕 줄 차례 그대로 — 셈 때 cid 로 바꿔 뺀줄 뒤에도 제자리.
|
||||
더한줄은 인력 · 재료 · 기계 · 로직 · 고정만(참조는 일위대가에 참조 줄로).
|
||||
셈 = 줄을 이름 없는 SK 문서로(`as_doc`) → `M02_Sangeun_Calc` 한 벌 · 로직 줄 기준 = 결과단위 값(배는
|
||||
알약 · `settle` 그대로) · 참조 줄 = 1 단위 값. [단가산출로 저장] = `as_doc` + 이름 · 단위 → 층 SK.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
|
||||
FIELD = "패치"
|
||||
KEEP = ("덮개", "뺀줄", "더한줄")
|
||||
TEMP = "SK-000000" # 이름 없는 줄 단가산출 — 저장 안 함
|
||||
|
||||
|
||||
def _bad(text: str):
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
|
||||
return store.StoreError(400, text)
|
||||
|
||||
|
||||
def clean(one: dict, where: str, whole: Any, row: dict | None, opened: list | None = None) -> dict:
|
||||
"""줄 칸 `패치` 정리 — 없거나 비면 `{}`(칸 안 둠) · 틀리면 400."""
|
||||
raw = one.get(FIELD)
|
||||
if not raw:
|
||||
return {}
|
||||
if not isinstance(raw, dict):
|
||||
raise _bad(f"{where} · 패치는 묶음")
|
||||
if any("참조" in x for x in raw.get("더한줄") or [] if isinstance(x, dict)):
|
||||
raise _bad(f"{where} · 줄 더한줄엔 참조 못 넣음 — 일위대가에 참조 줄로")
|
||||
body = {k: raw.get(k) for k in KEEP}
|
||||
try:
|
||||
got = sd.clean(
|
||||
{"키": TEMP, "이름": "줄", "단위": "식", **body, "열린입력": opened or []}, row, whole
|
||||
)
|
||||
except sd.DocError as e:
|
||||
raise _bad(f"{where} · {e}") from e
|
||||
out = {k: got[k] for k in KEEP if got[k]}
|
||||
return {FIELD: out} if out else {}
|
||||
|
||||
|
||||
def ref_clean(one: dict, ref: dict, where: str) -> dict:
|
||||
"""단가산출 참조 줄 패치 — 그 SK 의 열린입력이면 품 배율만."""
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
|
||||
if not one.get(FIELD):
|
||||
return {}
|
||||
if refs.kind_of(ref["키"]) != sd.KIND:
|
||||
raise _bad(f"{where} · 줄 덮개는 로직 줄 · 단가산출 참조 줄만")
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
|
||||
doc = refs.read_ref(ref) or {}
|
||||
return clean(one, where, m01.loaded()[1], None, doc.get("열린입력"))
|
||||
|
||||
|
||||
def _keyed(value: Any, ids: list[str]) -> dict:
|
||||
"""차례 열쇠 → cid 열쇠(바탕 줄 차례)."""
|
||||
out = {}
|
||||
for n, v in (value or {}).items() if isinstance(value, dict) else ():
|
||||
at = int(n) if str(n).isdigit() else -1
|
||||
if 0 <= at < len(ids):
|
||||
out[ids[at]] = v
|
||||
return out
|
||||
|
||||
|
||||
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}
|
||||
head = {"키": TEMP, "이름": name, "단위": unit, **body}
|
||||
if "참조" in one:
|
||||
return {**head, "바탕": {"참조": one["참조"]}}
|
||||
try:
|
||||
ids = sd.line_ids(whole.logic(one["로직"]))
|
||||
except Exception: # noqa: BLE001 — 없는 로직 = 셈이 까닭
|
||||
ids = []
|
||||
extra = {"켜쌓기율": one["켜쌓기율"]} if one.get("켜쌓기율") is not None else {}
|
||||
return {
|
||||
**head,
|
||||
**extra,
|
||||
"바탕": {"로직": one["로직"]},
|
||||
"입력값": dict(one.get("입력값") or {}),
|
||||
"고름": _keyed(one.get("고름"), ids),
|
||||
"자재표시": _keyed(one.get("자재표시"), ids),
|
||||
}
|
||||
|
||||
|
||||
def run(whole: Any, one: dict, read: Any = None) -> dict[str, Any]:
|
||||
"""패치 있는 로직 줄 기준(`run_line` 꼴 · 별도계상 뺀 뒤 · 기준 = 결과단위 값)."""
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
|
||||
try:
|
||||
doc = sd.clean(as_doc(one, whole), None, whole)
|
||||
return calc.run(doc, whole, None, read or refs.read_ref)
|
||||
except sd.DocError as e:
|
||||
return {"기준": None, "까닭": str(e)}
|
||||
|
||||
|
||||
def ref(one: dict, expand: Any) -> dict[str, Any]:
|
||||
"""참조 줄 펼침 — 패치 있는 SK 참조는 그 줄만 다시 셈(캐시 안 씀) · 그 밖 `expand` 그대로."""
|
||||
from M02_MasterTemplete import M02_Sangeun_Calc as calc
|
||||
|
||||
if not one.get(FIELD):
|
||||
return expand(one["참조"])
|
||||
doc = as_doc(one, expand.whole)
|
||||
base = expand.read(one["참조"]) or {}
|
||||
doc = {**doc, "이름": base.get("이름") or doc["이름"], "단위": base.get("단위") or doc["단위"]}
|
||||
return calc.unit(doc, expand.whole, expand, ())
|
||||
|
||||
|
||||
def to_sangeun(one: dict, name: str, unit: str, whole: Any) -> dict[str, Any]:
|
||||
"""[단가산출로 저장] 문서 — 줄의 바탕 · 입력값 · 고름 · 덮개 → 이름 붙인 SK(키는 서버)."""
|
||||
if "로직" not in one and "참조" not in one:
|
||||
raise _bad("로직 줄 · 단가산출 참조 줄만 단가산출로 저장")
|
||||
doc = as_doc(one, whole, name, unit)
|
||||
doc.pop("키")
|
||||
return doc
|
||||
@@ -163,7 +163,10 @@ def clean_doc(kind: str, doc: Any) -> dict[str, Any]:
|
||||
out: dict[str, Any] = {"종류": TITLE[kind], "키": key}
|
||||
if not name:
|
||||
raise _bad("사전조합 이름이 빔")
|
||||
out.update(이름=name, 단위=unit or None, 담은로직=_combo_lines(doc, key))
|
||||
lines = _combo_lines(doc, key)
|
||||
if not unit and (lines or doc.get("자재")): # 일위대가 = 기준단위 1 당(요구 27 · 715-3) ·
|
||||
raise _bad("일위대가 단위가 빔 — 1 단위 값의 단위") # 빈 새 문서(이름만)는 받음
|
||||
out.update(이름=name, 단위=unit or None, 담은로직=lines)
|
||||
out["자재"] = _combo_mats(doc, out["담은로직"])
|
||||
out.update({k: str(doc.get(k) or "").strip() for k in ("구분", "상세구분")})
|
||||
out["본뜬"] = doc["본뜬"] if isinstance(doc.get("본뜬"), dict) else None
|
||||
@@ -226,9 +229,9 @@ def _combo_lines(doc: dict, key: str) -> list[dict[str, Any]]:
|
||||
|
||||
lines = []
|
||||
for one in labor.clean({"담은로직": doc.get("담은로직") or []})["담은로직"]:
|
||||
head = {k: one[k] for k in ("참조", "고정") if k in one} or {"로직": one["로직"]}
|
||||
head = {k: one[k] for k in ("참조", "고정", "패치") if k in one} # 패치 = 줄 덮개(715-3)
|
||||
if "로직" in one:
|
||||
head["입력값"] = one["입력값"]
|
||||
head.update(로직=one["로직"], 입력값=one["입력값"])
|
||||
head.update({k: one[k] for k in ("켜쌓기율", "고름", "자재표시") if k in one})
|
||||
head.update({"별칭": one["별칭"]} if one["별칭"] else {}) # 실무 품명(324-3)
|
||||
lines.append({**head, "수량": one["수량"] or {"손": 1}, "메모": one["메모"]})
|
||||
@@ -331,7 +334,8 @@ def users(ref: Ref) -> list[str]:
|
||||
"""`ref` 를 담은 일위대가 · 사전조합 · 단가산출(바탕 · 더한줄) — 「어디 이름」 목록(모든 층 훑음 ·
|
||||
프로젝트 뺌)."""
|
||||
rid, out = ref_id(ref), []
|
||||
for kind, what in (("labor", "일위대가"), ("precombo", "사전조합"), ("sangeun", "단가산출")):
|
||||
# 「일위대가」 = 사전조합 새 이름(내역 14 확정 1) · 구조물 조합 일위대가 = 「구조물 조합」
|
||||
for kind, what in (("labor", "구조물 조합"), ("precombo", "일위대가"), ("sangeun", "단가산출")):
|
||||
for at, path in layers.layer_files(kind):
|
||||
try:
|
||||
doc = json.loads(path.read_bytes())
|
||||
|
||||
@@ -170,6 +170,10 @@ def unit(doc: dict, whole: Any, expand: Any, trail: tuple) -> dict[str, Any]:
|
||||
return {**head, "기준": got["1단위"], "까닭": why, "줄": lines, "짝잃음": got["짝잃음"]}
|
||||
|
||||
|
||||
def head_of(doc: dict) -> str:
|
||||
return f"1 {doc.get('단위')} 당"
|
||||
|
||||
|
||||
# ── 저장 ──────────────────────────────────────────────────────────────
|
||||
def _check_refs(doc: dict, here_: refs.Here) -> None:
|
||||
me = {"층": here_[0], "주인": here_[1], "키": doc["키"]}
|
||||
@@ -227,4 +231,22 @@ def calc_doc(doc: Any, given: dict | None = None) -> dict[str, Any]:
|
||||
got = run(clean, whole, given, trail=(me,))
|
||||
except sd.DocError as e:
|
||||
raise _fail(e) from e
|
||||
return labor._json(got)
|
||||
return labor._json({**got, "머리": head_of(clean)})
|
||||
|
||||
|
||||
def from_line(line: Any, name: str, unit: str) -> dict[str, Any]:
|
||||
"""[단가산출로 저장](715-3) — 일위대가 줄(바탕 · 입력값 · 고름 · 줄 덮개) → 지금 층 새 SK ·
|
||||
답 = `{이름: 새 키, 줄: 그 SK 참조 줄}`(일위대가 문서는 화면 [저장] 때 바뀜)."""
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
from M02_MasterTemplete import M02_Labor_Patch as patch
|
||||
|
||||
if not isinstance(line, dict):
|
||||
raise store.StoreError(400, "줄이 묶음 아님")
|
||||
doc = patch.to_sangeun(line, name, unit, m01.loaded()[1])
|
||||
made = refs.create(sd.KIND, doc)
|
||||
at = refs.here()
|
||||
keep = {k: line[k] for k in ("별칭", "메모", "수량") if k in line}
|
||||
return {
|
||||
"이름": made["이름"],
|
||||
"줄": {"참조": {"층": at[0], "주인": at[1], "키": made["이름"]}, **keep},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"""M02 단가산출(SK) 셈 길(PLAN 715-1b) — `POST /api/m02/layers/{층}/sangeun/calc`.
|
||||
"""M02 단가산출(SK) 셈 길(PLAN 715-1b) — `POST /api/m02/layers/{층}/sangeun/calc` ·
|
||||
[단가산출로 저장](715-3) `POST /api/m02/layers/{층}/sangeun/from-line`(층 쓰기 권한).
|
||||
|
||||
저장 안 함 · 로그인 사용자 · 층 읽기 권한(`Router_Items._root`) · 화면은 최신 응답만.
|
||||
몸 = `문서`(저장 전 화면 문서) 또는 `키`(그 층 저장본) + `입력값`(열린입력 · 시험 값).
|
||||
@@ -37,6 +38,24 @@ def _calc(body: SangeunCalcBody) -> dict[str, Any]:
|
||||
return calc.calc_doc(doc, body.입력값)
|
||||
|
||||
|
||||
class FromLineBody(BaseModel):
|
||||
줄: dict[str, Any]
|
||||
이름: str
|
||||
단위: str
|
||||
|
||||
|
||||
@router.post("/layers/{layer}/sangeun/from-line")
|
||||
def sangeun_from_line(
|
||||
layer: Layer,
|
||||
body: FromLineBody,
|
||||
owner: str | None = None,
|
||||
session: Session = Depends(verify_session),
|
||||
) -> dict:
|
||||
"""[단가산출로 저장](715-3) — 일위대가 줄 → 그 층 새 단가산출 · 답 = 새 키 + 바꿀 참조 줄."""
|
||||
folder = _root(session, layer, owner, write=True)
|
||||
return _call(folder, calc.from_line, body.줄, body.이름, body.단위)
|
||||
|
||||
|
||||
@router.post("/layers/{layer}/sangeun/calc")
|
||||
def calc_sangeun(
|
||||
layer: Layer,
|
||||
|
||||
Reference in New Issue
Block a user