feat(B09 단가 덮개): 노임 · 기계 단가 덮개 labor_cover · machine_cover — 셈 짝 · 저장 다듬기 · 거울 시험 (PLAN 707-3)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,10 @@
|
||||
중기사용료 = 손료(경비 · 기준가 천원 × 손료계수 ÷ 10,000) + 운전사(노무 · 인 × 시간 노임)
|
||||
+ 연료(재료 · ℓ/hr × 적용 단가) + 잡품(재료 · 연료 금액 × % ÷ 100)
|
||||
줄 = ROUNDDOWN(단가 × 수량, 1) · 비목 합 = ROUNDDOWN(Σ, 0) → 중기목록표
|
||||
노임 · 기계 단가 덮개(707-3) = 사용자 값 `labor_cover` {노무 키: 일당} · `machine_cover`
|
||||
{기계 키: {labor, material, expense}}(셋 다 수일 때만) — 굳힌 값 대신 ·
|
||||
줄 `covered` · `book`(굳힌 값) ·
|
||||
덮은 기계 = 중기사용료 줄은 그대로 · 비목 합 · 합 = 덮개 · 그 기계 「연결 없음」 뺌
|
||||
빈칸 규칙 = 값을 낸 원천이 없으면 None(0 을 채우지 않음) · 그 칸을 쓰는 합도 None ·
|
||||
「연결 없음」 목록(`gaps`)에 탭 · 줄 키 · 까닭을 남김(목차 탭이 이동 링크로 씀).
|
||||
"""
|
||||
@@ -39,6 +43,7 @@ SOURCES = ("관급", "물가정보", "물가자료", "유통물가", "거래가
|
||||
|
||||
#: 자재 줄 사용자 표시
|
||||
SUPPLY = ("관급", "별도")
|
||||
_PARTS = ("labor", "material", "expense")
|
||||
|
||||
|
||||
def _scaled(value: float, places: int, fn) -> float:
|
||||
@@ -95,10 +100,15 @@ def driver_hour(day: float | None, factor: dict[str, Any] | None) -> float | Non
|
||||
return rounddown(day * factor["num"] / factor["den"], 0)
|
||||
|
||||
|
||||
def labor_sheet(labor: list[dict[str, Any]], gaps: list) -> list[dict[str, Any]]:
|
||||
def labor_sheet(
|
||||
labor: list[dict[str, Any]], gaps: list, cover: dict[str, Any] | None = None
|
||||
) -> list[dict[str, Any]]:
|
||||
rows = []
|
||||
for no, row in enumerate(labor, 1):
|
||||
price = num(row.get("price"))
|
||||
mine = num((cover or {}).get(row["key"]))
|
||||
extra = {} if mine is None else {"covered": True, "book": price}
|
||||
price = price if mine is None else mine
|
||||
if price is None:
|
||||
gaps.append(gap("labor", row["key"], row["name"], "노임 단가 없음"))
|
||||
rows.append(
|
||||
@@ -107,11 +117,22 @@ def labor_sheet(labor: list[dict[str, Any]], gaps: list) -> list[dict[str, Any]]
|
||||
"code": row.get("code"),
|
||||
"unit": row.get("unit") or "인",
|
||||
"price": price,
|
||||
**extra,
|
||||
}
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def machine_cover(cover: Any) -> dict[str, dict[str, float]]:
|
||||
"""기계 덮개 — 노 · 재 · 경 셋 다 수인 줄만."""
|
||||
out = {}
|
||||
for key, cell in (cover if isinstance(cover, dict) else {}).items():
|
||||
got = {p: num((cell or {}).get(p)) if isinstance(cell, dict) else None for p in _PARTS}
|
||||
if None not in got.values():
|
||||
out[key] = got
|
||||
return out
|
||||
|
||||
|
||||
def _head(row: dict[str, Any], no: int) -> dict[str, Any]:
|
||||
return {
|
||||
"no": no,
|
||||
@@ -306,7 +327,7 @@ def basis_sheet(
|
||||
def compute_base(inputs: dict[str, Any], user: dict[str, Any]) -> dict[str, Any]:
|
||||
"""144 탭 일곱 + 「연결 없음」 목록."""
|
||||
gaps: list[dict[str, str]] = []
|
||||
labor_rows = labor_sheet(inputs.get("labor") or [], gaps)
|
||||
labor_rows = labor_sheet(inputs.get("labor") or [], gaps, user.get("labor_cover"))
|
||||
expense = expense_sheet(inputs.get("machines") or [], gaps)
|
||||
compare = compare_sheet(inputs.get("materials") or [], user.get("materials") or {}, gaps)
|
||||
basis = basis_sheet(inputs, labor_rows)
|
||||
@@ -316,6 +337,13 @@ def compute_base(inputs: dict[str, Any], user: dict[str, Any]) -> dict[str, Any]
|
||||
kind: by_key[key] for kind, key in (inputs.get("fuel_keys") or {}).items() if key in by_key
|
||||
}
|
||||
blocks = [machine_block(m, drivers, fuels, gaps) for m in inputs.get("machines") or []]
|
||||
covers = machine_cover(user.get("machine_cover"))
|
||||
for b in blocks:
|
||||
if b["key"] in covers: # 707-3 덮개 = 비목 합 · 합(줄은 그대로)
|
||||
b["book"] = {p: b[p] for p in (*_PARTS, "sum")}
|
||||
b.update(covers[b["key"]], covered=True)
|
||||
b["sum"] = total([b[p] for p in _PARTS])
|
||||
gaps = [g for g in gaps if not (g["tab"] == "machine_blocks" and g["key"] in covers)]
|
||||
machine = [
|
||||
{
|
||||
"no": no,
|
||||
@@ -327,6 +355,7 @@ def compute_base(inputs: dict[str, Any], user: dict[str, Any]) -> dict[str, Any]
|
||||
"labor": b["labor"],
|
||||
"material": b["material"],
|
||||
"expense": b["expense"],
|
||||
**({"covered": True} if b.get("covered") else {}),
|
||||
}
|
||||
for no, b in enumerate(blocks, 1)
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user