Merge remote-tracking branch 'origin/dev' into sub_laptop_1
This commit is contained in:
@@ -381,6 +381,19 @@
|
||||
"F0393"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"code": "AR-M-249d0a01",
|
||||
"kind": "material",
|
||||
"name": "파쇄기날",
|
||||
"spec": "이동식 임목파쇄기용",
|
||||
"unit": "개",
|
||||
"source": {
|
||||
"pum_edition": "2026-01-01",
|
||||
"pum_table_ids": [
|
||||
"F0237"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
"""조립(묶음) 줄이 조각의 「일부만」·「밑수 없음」 을 올림 — 2026-09-14 브레인 ㉱ 첫째(구조 결함).
|
||||
|
||||
종전 `_composite_row` 는 조각 제목이 **있기만 하면** 금액에 더했음 → 조각 단가가 일부 몫만 섰거나(인력만)
|
||||
밑수를 모르는 표여도 조립 줄이 **온전한 금액처럼** 섰음. 보통 줄은 그 둘을 막는데 조립 줄만 새던 자리.
|
||||
⇒ 조각마다 보통 줄과 같은 두 검사 — 걸리면 금액을 안 세우고 어느 조각이 왜인지 사유.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill
|
||||
from B09_Estimation.B09_Estimation_UnitPrice import cached_build
|
||||
|
||||
|
||||
def _composite(parts: list[dict]) -> dict:
|
||||
return {
|
||||
"work_items": [
|
||||
{
|
||||
"work_item_code": None,
|
||||
"name": "시험 묶음",
|
||||
"spec": "",
|
||||
"unit": "개소",
|
||||
"quantity": "1",
|
||||
"in_bill": True,
|
||||
"composite_parts": parts,
|
||||
"composite_not_ready": [],
|
||||
}
|
||||
],
|
||||
"materials": [],
|
||||
}
|
||||
|
||||
|
||||
def _line(parts: list[dict]):
|
||||
rows = build_bill(_composite(parts)).rows
|
||||
return next(r for r in rows if r.code is None and r.name == "시험 묶음")
|
||||
|
||||
|
||||
def test_일부만_선_조각이_있으면_묶음_금액을_안_세우고_까닭() -> None:
|
||||
build = cached_build()
|
||||
partial = next(c for c in build.partial_ratio if f"B-{c}" in build.book.titles)
|
||||
line = _line([{"code": partial, "quantity": 1}])
|
||||
assert line.amount_krw is None, (partial, line.amount_krw)
|
||||
assert partial in line.note and "일부만" in line.note, line.note
|
||||
|
||||
|
||||
def test_밑수_없는_조각도_같이_막음() -> None:
|
||||
build = cached_build()
|
||||
missing = next(c for c in build.basis_missing if f"B-{c}" in build.book.titles)
|
||||
line = _line([{"code": missing, "quantity": 1}])
|
||||
assert line.amount_krw is None and "밑수" in line.note, (missing, line.note)
|
||||
|
||||
|
||||
def test_온전한_조각만이면_종전대로_금액() -> None:
|
||||
line = _line([{"code": "FP-09-15-02", "quantity": 2}])
|
||||
assert line.amount_krw and line.amount_krw > 0, line.note
|
||||
@@ -0,0 +1,63 @@
|
||||
"""이동식 임목 파쇄 8-11 — 2026-09-15 브레인 판정(㉰ 둘째).
|
||||
|
||||
원문 L4651: 이동식 임목 파쇄기 93.25KW · Q = 3.5 ㎥/hr · 비고 「잡재료 : 주연료비의 16% · 소모품비(파쇄기날) 0.00125개/hr」
|
||||
[주]① 1일 8시간 ② 1일 기계운전자 1인 · 보통인부 2인 ③ 장비 운반비 별도 ④ 우드그랩 별도 ⑤ 연료 13.5ℓ(디젤)
|
||||
건설품셈 운전경비표(8-4 L3458) 7205-0125 93.25㎾ 줄은 연료·잡품 칸이 「-」 → 우리 운전경비 레코드가 없어
|
||||
기계 층이 안 서고 일위대가도 조용히 없었음. 8-11 [주]⑤·비고가 **유일한 값**이라 그대로 씀(판정할 것 없음).
|
||||
파쇄기날은 규격·단가가 없어 「자재 단가」 칸 + 사유(화약류·치즐과 같은 통로).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from B09_Estimation.B09_Estimation_UnitPrice import cached_build
|
||||
|
||||
BLADE = "AR-M-249d0a01"
|
||||
Q = Decimal("3.5")
|
||||
|
||||
|
||||
def _rows(book, code: str) -> list:
|
||||
own = book.details.get(code) or []
|
||||
return [
|
||||
*own,
|
||||
*(r for d in own if d.ref_code.startswith("D-") for r in book.details[d.ref_code]),
|
||||
]
|
||||
|
||||
|
||||
def test_파쇄기_기계_층이_8_11_주5_연료와_잡재료_16퍼센트로_섬() -> None:
|
||||
book = cached_build().book
|
||||
rows = {r.ref_code: r for r in book.details["X-7205-0125"]}
|
||||
assert rows["M-FUEL-경유"].quantity == Decimal("13.5"), rows
|
||||
misc = next(r for r in book.details["X-7205-0125"] if r.percent_of_material is not None)
|
||||
assert misc.percent_of_material == Decimal(16)
|
||||
assert any(code.endswith("#시간") for code in rows), rows # 기계운전자 1인(조종원 시간당)
|
||||
|
||||
|
||||
def test_파쇄_일위대가는_기계_1_Q_와_보통인부_2인_8시간() -> None:
|
||||
build = cached_build()
|
||||
book = build.book
|
||||
assert book.titles["B-FP-08-11"].unit == "㎥"
|
||||
rows = _rows(book, "B-FP-08-11")
|
||||
machine = next(r for r in rows if r.ref_code == "X-7205-0125")
|
||||
assert machine.quantity == Decimal(1) / Q and machine.output == Q, machine
|
||||
labor = next(r for r in rows if r.ref_code == "1002")
|
||||
assert labor.quantity == Decimal(2) / Decimal(8) / Q, labor
|
||||
assert not any(r.ref_code.startswith("1048") for r in rows) # 운전자는 기계 층에 — 두 번 안 셈
|
||||
|
||||
|
||||
def test_파쇄기날은_단가가_없으면_사유_있으면_시간당_수량_나누기_Q() -> None:
|
||||
build = cached_build()
|
||||
assert "FP-08-11" in build.material_uses.get(BLADE, [])
|
||||
assert any("파쇄기날" in label for label in build.unattached.get("FP-08-11", []))
|
||||
priced = cached_build(material_prices=((BLADE, "150000", "견적"),))
|
||||
blade = next(r for r in _rows(priced.book, "B-FP-08-11") if r.ref_code == BLADE)
|
||||
assert blade.quantity == Decimal("0.00125") / Q, blade
|
||||
assert not any("파쇄기날" in label for label in priced.unattached.get("FP-08-11", []))
|
||||
|
||||
|
||||
def test_원문_주의_별도_계상은_사유로() -> None:
|
||||
from B09_Estimation.B09_Estimation_KnownGaps import known_gap_note
|
||||
|
||||
note = known_gap_note("FP-08-11")
|
||||
assert "운반비" in note and "우드그랩" in note, note
|
||||
Reference in New Issue
Block a user