Merge remote-tracking branch 'origin/dev' into main_laptop_1
This commit is contained in:
@@ -370,6 +370,7 @@ from B09_Estimation.B09_Estimation_BillOfQuantities_Rows import ( # noqa: E402
|
||||
_leaf_row,
|
||||
_material_row,
|
||||
_structure_price_row,
|
||||
bill_line, # noqa: F401 — 내역 줄 성분별 절사(골든셋 시험이 여기서 부름)
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from B09_Estimation.B09_Estimation_BillOfQuantities import (
|
||||
SUPPLY_OWNER,
|
||||
SUPPLY_UNKNOWN,
|
||||
@@ -20,10 +22,24 @@ from B09_Estimation.B09_Estimation_BillOfQuantities import (
|
||||
_MasterNode,
|
||||
_decimal,
|
||||
)
|
||||
from B09_Estimation.B09_Estimation_PriceBook import Money3
|
||||
from B09_Estimation.B09_Estimation_Rounding import OutputPlace, round_at
|
||||
from B09_Estimation.B09_Estimation_UnitPrice import UnitPriceBuild, find_variant_code
|
||||
|
||||
|
||||
def bill_line(unit: Money3, quantity) -> Money3:
|
||||
"""내역 줄 금액 — **성분마다** `절사(수량 × 성분 단가)`, 줄 합계는 셋의 합(명세 7장).
|
||||
|
||||
근거 STmate 16번 행 금액 1,515건(505줄 × 성분 셋) · 착공내역서 178건 전수 절사.
|
||||
⚠ 합을 한 번에 자르면 성분 합과 1~2원 갈림 — 원가계산서 직접비 밑수도 이 자른 성분의 합.
|
||||
"""
|
||||
return Money3(
|
||||
material=round_at(unit.material * quantity, OutputPlace.BOQ_ROW),
|
||||
labor=round_at(unit.labor * quantity, OutputPlace.BOQ_ROW),
|
||||
expense=round_at(unit.expense * quantity, OutputPlace.BOQ_ROW),
|
||||
)
|
||||
|
||||
|
||||
def _composite_row(
|
||||
item_no: str,
|
||||
item: HandoffWorkItem,
|
||||
@@ -54,7 +70,8 @@ def _composite_row(
|
||||
if not code or amount is None or f"B-{code}" not in unit_prices.book.titles:
|
||||
missing_parts.append(code or str(part.get("name") or "이름 없음"))
|
||||
continue
|
||||
scaled = unit_prices.book.resolve(f"B-{code}").scaled(amount)
|
||||
# 묶음도 호표 한 장 — 조각 줄 0.1원 · 성분 소계 원 미만 절사(아래 `floored`, 명세 7장).
|
||||
scaled = unit_prices.book.resolve(f"B-{code}").scaled(amount).floored(Decimal("0.1"))
|
||||
money = scaled if money is None else money + scaled
|
||||
|
||||
reasons: list[str] = []
|
||||
@@ -85,9 +102,10 @@ def _composite_row(
|
||||
)
|
||||
return row
|
||||
|
||||
line = money.scaled(item.quantity)
|
||||
money = money.floored(Decimal(1))
|
||||
line = bill_line(money, item.quantity)
|
||||
row.unit_price_krw = round_at(money.total, OutputPlace.UNIT_PRICE_ROW)
|
||||
row.amount_krw = round_at(line.total, OutputPlace.BOQ_ROW)
|
||||
row.amount_krw = line.total
|
||||
row.material_krw = line.material
|
||||
row.labor_krw = line.labor
|
||||
row.expense_krw = line.expense
|
||||
@@ -141,9 +159,9 @@ def _structure_price_row(
|
||||
return row
|
||||
|
||||
# 단가 = 호표 계금(구조물도 화면과 같은 값) · 금액 = 호표 성분 소계 × 수량(명세 7장).
|
||||
line = entry["money"].scaled(item.quantity)
|
||||
line = bill_line(entry["money"], item.quantity)
|
||||
row.unit_price_krw = entry["total"]
|
||||
row.amount_krw = round_at(line.total, OutputPlace.BOQ_ROW)
|
||||
row.amount_krw = line.total
|
||||
row.material_krw = line.material
|
||||
row.labor_krw = line.labor
|
||||
row.expense_krw = line.expense
|
||||
@@ -443,11 +461,11 @@ def _leaf_row(
|
||||
row.price_code = price_code
|
||||
|
||||
unit_money = unit_prices.book.resolve(price_code)
|
||||
line = unit_money.scaled(item.quantity)
|
||||
line = bill_line(unit_money, item.quantity)
|
||||
row.unit_price_krw = round_at(unit_money.total, OutputPlace.UNIT_PRICE_ROW)
|
||||
# 내역서 **본체** 행은 절사다 — 집계표(반올림)와 어긋나는 것이 정상.
|
||||
row.amount_krw = round_at(line.total, OutputPlace.BOQ_ROW)
|
||||
# 3분할은 전정밀로 들고 간다 — ⑤ 밑수가 비목마다 갈리므로 여기서 자르면 안 된다.
|
||||
# 내역서 **본체** 행은 성분마다 절사 — 집계표(반올림)와 어긋나는 것이 정상.
|
||||
row.amount_krw = line.total
|
||||
# 3분할도 자른 값 — ⑤ 직접비 밑수가 곧 성분별로 자른 줄 금액의 합(STmate 16번 원 단위 일치).
|
||||
row.material_krw = line.material
|
||||
row.labor_krw = line.labor
|
||||
row.expense_krw = line.expense
|
||||
|
||||
@@ -61,8 +61,10 @@ CATALOG_KINDS = frozenset({PriceKind.MATERIAL, PriceKind.LABOR, PriceKind.MACHIN
|
||||
#: 근거 STmate 17번 — 중기사용료 호표 성분 소계 398/398 절사
|
||||
#: (굴삭기 0.7㎥ 23,128 + 55,700 + 18,015 = 96,843 · 골든셋 실무 143 호표 전수).
|
||||
#: D(단가산출)도 같음 — 줄 `중기 성분 ÷ Q` 0.1원 · 머리 성분 원 미만(봉화 2024 제2호표 3,545.5 → 3,545).
|
||||
#: ⚠ 일위대가(B) 호표도 같은 규칙(345 중 94.2%)이나 **아래층(D)부터 맞춘 뒤** 붙임(PLAN 6장 판정).
|
||||
TRUNCATED_KINDS = frozenset({PriceKind.MACHINE_HOURLY, PriceKind.PRICE_BASIS})
|
||||
#: B(일위대가)도 같음 — 골든셋 실무 성분 소계 477/510(93.5%, 17번 원문 94.2%). 안 맞는 것은 반올림으로
|
||||
#: 뽑은 원본 한 건·적용률 줄 서식 — **새 반올림 규칙을 넣지 말 것**(PLAN 6장 판정).
|
||||
#: ⚠ 비율 줄(공구손료·제잡비·품 할증)도 줄 금액이라 0.1원 미만 절사.
|
||||
TRUNCATED_KINDS = frozenset({PriceKind.MACHINE_HOURLY, PriceKind.PRICE_BASIS, PriceKind.UNIT_PRICE})
|
||||
_TENTH = Decimal("0.1")
|
||||
_WON = Decimal(1)
|
||||
|
||||
@@ -290,6 +292,8 @@ class PriceBook:
|
||||
# 임도는 산림품셈 문구를 따른다 — 결과값은 어차피 같다.
|
||||
# ⚠ 「**상한**」이다 — 곱한 값 **이하**로 계상하는 값이라 설계자가 낮출 수 있다.
|
||||
share = direct_labor * row.percent_of_labor / Decimal(100)
|
||||
if truncate:
|
||||
share = share.quantize(_TENTH, rounding=ROUND_FLOOR)
|
||||
if row.percent_of_labor_target == "labor":
|
||||
# 품 할인·할증(1-4) — **품이 늘어난 것**이라 노무비로 들어가고, 뒤에 오는
|
||||
# 제잡비의 밑수에도 든다.
|
||||
@@ -317,7 +321,8 @@ class PriceBook:
|
||||
child = self.resolve(row.ref_code, (*_seen, code))
|
||||
if row.percent_of_parent is not None:
|
||||
# 비율 행 — 지금까지 쌓인 값의 %로 붙는다(공구손료 등).
|
||||
total = total + total.scaled(row.percent_of_parent / Decimal(100))
|
||||
share = total.scaled(row.percent_of_parent / Decimal(100))
|
||||
total = total + (share.floored(_TENTH) if truncate else share)
|
||||
continue
|
||||
scaled = child.scaled(row.quantity)
|
||||
if truncate:
|
||||
|
||||
@@ -1238,7 +1238,8 @@ def direct_cost_from_quantities(
|
||||
result.missing.append(raw_code)
|
||||
continue
|
||||
unit_money = book.resolve(code)
|
||||
line = unit_money.scaled(Decimal(str(quantity)))
|
||||
# 내역 줄과 같은 자르기 — 성분마다 1원 미만 절사(`BillOfQuantities_Rows.bill_line`).
|
||||
line = unit_money.scaled(Decimal(str(quantity))).floored(Decimal(1))
|
||||
result.material += line.material
|
||||
result.labor += line.labor
|
||||
result.expense += line.expense
|
||||
|
||||
Reference in New Issue
Block a user