Merge remote-tracking branch 'origin/sub_laptop_5' into sub_laptop_2
# Conflicts: # M02_MasterTemplete/M02_Labor_Doc.py
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
"""M02 일위대가 문서 `labor/<열id>.json` — 정리 · 검사 · 계산 · 알약(PLAN 39-1a · 설계_정함 1장).
|
||||
|
||||
문서 = `{종류, 판, 열, 일위대가번, 단위, 본뜬,
|
||||
담은로직: [{로직, 별칭, 메모, 입력값, 수량, 켜쌓기율?, 고름?, 승격?} | {참조: {층, 주인, 키}, 별칭, 메모, 수량}],
|
||||
자재: [{코드, 이름, 규격, 단위, 수량}], 계산값}` — 이름은 구조물 열 이름(따로 안 둠).
|
||||
담은로직: [{로직, 별칭, 메모, 입력값, 수량, 켜쌓기율?, 고름?, 승격?, 자재표시?} |
|
||||
{참조: {층, 주인, 키}, 별칭, 메모, 수량}],
|
||||
자재: [{코드, 이름, 규격, 단위, 수량, 자재구분?, 별도계상?, 운반비?}], 계산값}` —
|
||||
이름은 구조물 열 이름(따로 안 둠).
|
||||
수량 = `null`(면적산출 알약 칸 값 · 로직 알약 「이름|단위」 · 자재 알약 「자재|MT키」) | `{손: 수}`
|
||||
(손이 칸보다 앞섬) · 같은 로직 두 번 금지 · 입력값 = 그 로직 입력 이름만(빈 값 = 자동값).
|
||||
금액: 기준 = 결과단위 그대로(서버 단독 · M01 엔진) · 반영 = 기준 × 수량 ÷ 배 · 돈 아닌 로직은
|
||||
@@ -18,6 +20,8 @@
|
||||
고름 = `{호표 차례: MT키}`(엔진 `choosing` 그대로) · 금액은 로직 재료비 안이라 합 · 알약에 안 더함(보기만) · 사용자 자재와
|
||||
같은 품목 두 번은 [저장] 때 400.
|
||||
승격(134-1) = 로직 줄 「단가승격」 표시(`true` 만 · 없음 = 단가산출) · 금액 무관 · B09 호표 몫.
|
||||
자재 세 칸(148-4) = `MARKS` 허용 값만(그 밖 · 빈 것 버림 · 금액 무관) — 사용자 자재 = 줄 덧칸 ·
|
||||
로직 자재 = 로직 줄 `자재표시 {호표 차례: {세 칸}}`(차례가 재료 줄인 것만 · 틀리면 버림).
|
||||
% 덧줄(149-4) = 식 「X * Y / 100」 덧줄에 덧칸 `율`(Y) · `규격`(「노무비의 %」 등 · `pct_extras`) — 표기만 ·
|
||||
단위 · 수량 · 금액 그대로(「식 · 1」) · 화면이 율 · % 로 그림 · M01 시험계산 표도 이 함수.
|
||||
"""
|
||||
@@ -34,12 +38,18 @@ from M01_MasterData import M01_MasterData_Store_Combo as combo_store
|
||||
from M02_MasterTemplete import M02_Labor_Inputs as labor_inputs
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_Material_Own as own
|
||||
|
||||
mf = m01.cm.mf
|
||||
mm = mf.mp.mm
|
||||
COSTS = mf.COST_ITEMS # 노무비 · 재료비 · 경비
|
||||
MATERIAL_HEAD = "자재|"
|
||||
_MAT_COLS = ("코드", "이름", "규격", "단위")
|
||||
MARKS = {
|
||||
"자재구분": ("일반", "관급", "사급"),
|
||||
"별도계상": ("별도계상", "직접계산"),
|
||||
"운반비": ("포함", "미포함"),
|
||||
}
|
||||
ALIAS_MAX = 40 # 별칭(44-6) 글자 수 상한 — 화면 칸과 같음
|
||||
_BRACE = re.compile(r"\{([^{}]+)\}") # 품목 조건의 {입력}
|
||||
|
||||
@@ -128,6 +138,9 @@ 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 {}),
|
||||
**(
|
||||
{"자재표시": shown} if (shown := _mat_marks(one.get("자재표시"), logic)) else {}
|
||||
),
|
||||
}
|
||||
)
|
||||
mats, codes = [], set()
|
||||
@@ -135,9 +148,11 @@ def clean(doc: Any) -> dict[str, Any]:
|
||||
where = f"자재 {at}째"
|
||||
code = str(one.get("코드") or "") if isinstance(one, dict) else ""
|
||||
try:
|
||||
row = whole.get(code) if code[:2] == mm.MARKET else None
|
||||
row = whole.get(code) if code[:2] == mm.MARKET else own.find(code)
|
||||
except mf.FormulaError:
|
||||
row = None
|
||||
if row is None and own.is_own(code):
|
||||
raise _bad(f"{where} · 개인 자재 목록에 없는 코드(개인 층에서만) 「{code}」")
|
||||
if row is None:
|
||||
raise _bad(f"{where} · M01 자재품목에 없는 코드 「{code}」")
|
||||
if code in codes:
|
||||
@@ -148,6 +163,7 @@ def clean(doc: Any) -> dict[str, Any]:
|
||||
"코드": code,
|
||||
**{k: row.get(k) or "" for k in _MAT_COLS[1:]},
|
||||
"수량": _qty(one.get("수량"), where),
|
||||
**marks(one),
|
||||
}
|
||||
)
|
||||
out = {
|
||||
@@ -195,6 +211,24 @@ def _picks(value: Any, logic: dict, whole: Any, where: str) -> dict[str, str] |
|
||||
return out
|
||||
|
||||
|
||||
def marks(one: Any) -> dict[str, str]:
|
||||
"""자재 세 칸(148-4) — 허용 값만."""
|
||||
got = one if isinstance(one, dict) else {}
|
||||
return {k: got[k] for k, allowed in MARKS.items() if got.get(k) in allowed}
|
||||
|
||||
|
||||
def _mat_marks(value: Any, logic: dict) -> dict[str, dict[str, str]] | None:
|
||||
"""로직 자재 세 칸(148-4) `{호표 차례: {세 칸}}` — 맨 위 층 재료 줄 차례 · 허용 값만 ·
|
||||
빈 것 = None."""
|
||||
rows = logic.get("호표") or []
|
||||
out = {}
|
||||
for at, got in (value if isinstance(value, dict) else {}).items():
|
||||
n = int(at) if str(at).isdigit() else -1
|
||||
if 0 <= n < len(rows) and rows[n].get("종류") == "재료" and (kept := marks(got)):
|
||||
out[str(n)] = kept
|
||||
return out or None
|
||||
|
||||
|
||||
def _rate(value: Any, code: str, where: str) -> int | float | None:
|
||||
"""켜쌓기율(85-2) — 0 초과 200 이하 · 소수 한 자리 · 100 · 대상 밖 로직은 None(안 둠)."""
|
||||
if value is None or value == "":
|
||||
@@ -366,7 +400,20 @@ def settle(result: dict, doc: dict, given: dict[str, Any]) -> dict[str, Any]:
|
||||
|
||||
def material_price(master: Any, code: str, unit: str | None) -> dict[str, Any]:
|
||||
"""자재 한 줄 기준 — `{기준단가, 가격: {값 열 다섯}, 기준칸, 까닭}` · 기준단가 = `mm.element`
|
||||
(값 열 가운데 낮은 값) · 기준칸 = 그 값이 나온 열 이름(출처 `키.열`) · 원본 자료는 읽기만."""
|
||||
(값 열 가운데 낮은 값) · 기준칸 = 그 값이 나온 열 이름(출처 `키.열`) · 원본 자료는 읽기만.
|
||||
개인 자재(148-3 · `PM`) = 줄 「값」 하나(사본 `단가.json` 안 · 없으면 지금 층 목록 · 값 열 없음)."""
|
||||
if own.is_own(code):
|
||||
try:
|
||||
row = master.get(code)
|
||||
except mf.FormulaError:
|
||||
row = own.find(code) or {}
|
||||
price = row.get("값")
|
||||
return {
|
||||
"기준단가": price,
|
||||
"가격": {slot: None for slot in mm.SLOTS},
|
||||
"기준칸": None,
|
||||
"까닭": None if price is not None else "단가 없음",
|
||||
}
|
||||
row = master.get(code) or {}
|
||||
prices = {slot: row.get(slot) for slot in mm.SLOTS}
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user