feat(B08): 채집석 공제 소요량을 냄 — 빼지는 않음 (사용자 확정 ②)
돌은 기본 「캔다」(별표2 「야면석 등은 **가급적 현장에서 채취·사용**하도록 운반거리를 조사한다」 · 실무 견적 다섯 권에 야면석 구입 단가 0건). 구조물마다 「구입」으로 바꿀 수 있음. ⚠⚠ **채집석 공제는 사토에서 한 번만 뺀다.** B08 은 소요량(`collected_stone_deduction_m3`, ㎥ **양수**)을 내기만 하고 공제하지 않으며, 빼는 자리는 유토곡선의 사토뿐임 — 실어 내는 몫(spoil_m3 − natural_spoil_m3)에서 먼저 빼고 모자라면 자연방토에서 뺌. (랩탑 메인의 통로에도 같은 문장을 박아 둠 — 두 곳이 같은 말이라야 나중에 누가 봐도 안 갈림.) ⚠ **부호를 넘기지 않음.** 실무 시트가 `−274.66` 이라 그대로 넘기면 두 번 뒤집힘. 밑수 — 면석 몸통(면적×뒷길이×몫) + 고임돌 + 막자갈. **이미 우리 식 안에 있는 값들**이라 새 수치를 지어내지 않음. 목적지는 `haul_deduction` 이라 자재총괄(`material` 만 모음)에 안 섞임. 인계 맨 위에 구조물 합산 한 값으로 실림. 시험 754 통과(새 6건 포함). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -183,6 +183,11 @@ def build_handoff(
|
|||||||
result["basis_unit_warnings"] = verify_unit_matches_basis(
|
result["basis_unit_warnings"] = verify_unit_matches_basis(
|
||||||
work_items, extra=table.declared_units()
|
work_items, extra=table.declared_units()
|
||||||
)
|
)
|
||||||
|
# ⚠ 채집석 공제 — **양수 ㎥ 로 넘기기만** 한다. 빼는 자리는 유토곡선의 사토뿐이다
|
||||||
|
# (2026-09-09 세 창 확정 · 부호를 넘기면 두 번 뒤집힌다).
|
||||||
|
result["collected_stone_deduction_m3"] = float(
|
||||||
|
(unit_quantity_table or {}).get("collected_stone_deduction_m3") or 0.0
|
||||||
|
)
|
||||||
result["placing_notes"] = placing_notes
|
result["placing_notes"] = placing_notes
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -165,6 +165,26 @@ STONE_MASONRY = {
|
|||||||
#: ⚠ 우리에게 **잡석다짐 폭이 아직 없다**(기초잡석은 품셈에 공종만 있고 두께·폭이 없음).
|
#: ⚠ 우리에게 **잡석다짐 폭이 아직 없다**(기초잡석은 품셈에 공종만 있고 두께·폭이 없음).
|
||||||
#: 그래서 **터파기 폭(평균두께 + 여유 0.2m)을 잠정으로** 쓰고 근거 문구에 그 사실을 적는다.
|
#: 그래서 **터파기 폭(평균두께 + 여유 0.2m)을 잠정으로** 쓰고 근거 문구에 그 사실을 적는다.
|
||||||
#: 잡석다짐 폭이 정해지면 이 한 줄만 바꾸면 된다.
|
#: 잡석다짐 폭이 정해지면 이 한 줄만 바꾸면 된다.
|
||||||
|
#: 돌을 **사 오나 캐나** — 기본은 「캔다」(2026-09-09 사용자 확정 ②).
|
||||||
|
#: 법이 그쪽을 권한다 — 별표2 「석축 등에 필요한 야면석 등은 **가급적 현장에서
|
||||||
|
#: 채취·사용**하도록 운반거리를 조사한다」. 실무 견적 다섯 권에도 야면석 **구입 단가가
|
||||||
|
#: 0건**이고 울진 일위대가는 「구입/채집」 두 벌을 갖고 있다.
|
||||||
|
#: ⚠ 구조물마다 바꿀 수 있다 — 저장 제원에 「구입」이라 적힌 구조물만 공제에서 빠진다.
|
||||||
|
STONE_SUPPLY_KEYS = ("stone_supply", "stone_source")
|
||||||
|
STONE_SUPPLY_PURCHASED = {"구입", "구입품", "사서", "purchase", "purchased", "buy"}
|
||||||
|
|
||||||
|
#: ⚠⚠ **채집석 공제는 사토에서 한 번만 뺀다** (2026-09-09 세 창 확정 · 랩탑 메인의 통로에도
|
||||||
|
#: 같은 문장이 박혀 있다 — 두 곳이 같은 말이라야 나중에 누가 봐도 안 갈린다).
|
||||||
|
#:
|
||||||
|
#: 채집석 공제는 사토에서 한 번만 뺀다.
|
||||||
|
#: B08 은 소요량(collected_stone_deduction_m3, ㎥ 양수)을 내기만 하고 공제하지 않으며,
|
||||||
|
#: 빼는 자리는 유토곡선의 사토뿐이다 —
|
||||||
|
#: 실어 내는 몫(spoil_m3 − natural_spoil_m3)에서 먼저 빼고 모자라면 자연방토에서 뺀다.
|
||||||
|
#:
|
||||||
|
#: ⚠ **부호를 넘기지 않는다.** 실무 시트가 `−274.66` 으로 적혀 있어 그대로 넘기면 두 번
|
||||||
|
#: 뒤집힌다. 우리는 **양수**로 주고, 빼는 것은 받는 쪽이 한다.
|
||||||
|
COLLECTED_STONE_KEY = "collected_stone_deduction_m3"
|
||||||
|
|
||||||
BLINDING_THICKNESS_M = 0.10
|
BLINDING_THICKNESS_M = 0.10
|
||||||
#: 버림을 뺄 수 있는 칸 — 「기본은 넣고, 빼고 싶으면 뺀다」(사용자 확정 ⑭).
|
#: 버림을 뺄 수 있는 칸 — 「기본은 넣고, 빼고 싶으면 뺀다」(사용자 확정 ⑭).
|
||||||
#: 저장 제원에 이 칸이 없으면 **넣는 쪽**이 기본이다.
|
#: 저장 제원에 이 칸이 없으면 **넣는 쪽**이 기본이다.
|
||||||
@@ -185,7 +205,10 @@ DESTINATION = {
|
|||||||
"버림콘크리트": "unit_price",
|
"버림콘크리트": "unit_price",
|
||||||
"모르터": "unit_price",
|
"모르터": "unit_price",
|
||||||
"거푸집": "unit_price",
|
"거푸집": "unit_price",
|
||||||
"물구멍관": "material", # ⚠ 자재 카탈로그가 이름으로 찾는다 — 공백 없는 한 낱말(B09 규약)
|
"물구멍관": "material",
|
||||||
|
# ⚠ 자재도 토공도 아닌 자리 — **유토곡선이 사토에서 뺄 밑수**다. 자재총괄은
|
||||||
|
# `material` 만 모으므로 여기 섞이지 않는다.
|
||||||
|
"채집석": "haul_deduction", # ⚠ 자재 카탈로그가 이름으로 찾는다 — 공백 없는 한 낱말(B09 규약)
|
||||||
}
|
}
|
||||||
|
|
||||||
# ⚠ 배합 성분 — 산출물에 나타나면 안 된다(㉢). B09 일위대가가 배합표로 분해한다.
|
# ⚠ 배합 성분 — 산출물에 나타나면 안 된다(㉢). B09 일위대가가 배합표로 분해한다.
|
||||||
@@ -235,6 +258,17 @@ class StructureQuantity:
|
|||||||
billing_quantity: float = 0.0
|
billing_quantity: float = 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def is_collected_stone(options: dict[str, Any]) -> bool:
|
||||||
|
"""이 구조물의 돌을 **캐서 쓰는가**. 정한 적이 없으면 「캔다」(확정 ②)."""
|
||||||
|
for key in STONE_SUPPLY_KEYS:
|
||||||
|
raw = options.get(key)
|
||||||
|
if raw is None:
|
||||||
|
continue
|
||||||
|
if str(raw).strip().lower() in STONE_SUPPLY_PURCHASED:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def wants_blinding(options: dict[str, Any]) -> bool:
|
def wants_blinding(options: dict[str, Any]) -> bool:
|
||||||
"""버림 콘크리트를 넣을지. **정한 적이 없으면 넣는다**(사용자 확정 ⑭).
|
"""버림 콘크리트를 넣을지. **정한 적이 없으면 넣는다**(사용자 확정 ⑭).
|
||||||
|
|
||||||
@@ -519,7 +553,8 @@ def stone_masonry(
|
|||||||
# 막자갈 = 입적 − (면적 × 뒷길이 × 뒤채움몫 + 고임돌).
|
# 막자갈 = 입적 − (면적 × 뒷길이 × 뒤채움몫 + 고임돌).
|
||||||
# 뒤채움 몫은 **돌 종류로 갈린다** — 깬돌·잡석 1/2 · 야면석 1/3 (교본 7-3).
|
# 뒤채움 몫은 **돌 종류로 갈린다** — 깬돌·잡석 1/2 · 야면석 1/3 (교본 7-3).
|
||||||
wedge = masonry_area * _num(table["wedge_stone_m3_per_m2"]) # None 이면 0 — 막자갈에서 안 뺌
|
wedge = masonry_area * _num(table["wedge_stone_m3_per_m2"]) # None 이면 0 — 막자갈에서 안 뺌
|
||||||
rubble = volume - (masonry_area * (back_cm / 100.0) * body_ratio + wedge)
|
stone_body = masonry_area * (back_cm / 100.0) * body_ratio # 면석 몸통 체적
|
||||||
|
rubble = volume - (stone_body + wedge)
|
||||||
if rubble > 0:
|
if rubble > 0:
|
||||||
components.append(
|
components.append(
|
||||||
Component(
|
Component(
|
||||||
@@ -576,6 +611,22 @@ def stone_masonry(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 채집석 — **캐서 쓰는 구조물**의 돌 체적. 사토에서 뺄 밑수이고 **여기서 빼지 않는다.**
|
||||||
|
if is_collected_stone(options):
|
||||||
|
collected = max(stone_body + wedge + max(rubble, 0.0), 0.0)
|
||||||
|
if collected > 0:
|
||||||
|
components.append(
|
||||||
|
Component(
|
||||||
|
"채집석",
|
||||||
|
"㎥",
|
||||||
|
collected,
|
||||||
|
DESTINATION["채집석"],
|
||||||
|
f"면석 몸통 {stone_body:.3f} + 고임돌 {wedge:.3f} + 막자갈"
|
||||||
|
f" {max(rubble, 0.0):.3f} ㎥ · 현장 채집분 ·"
|
||||||
|
" ⚠ 여기서 빼지 않음 — 사토에서 한 번만 뺌",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# 버림 콘크리트 — 기초 바닥에 까는 얇은 층. 빼려면 저장 제원에서 「안 넣음」으로 둔다.
|
# 버림 콘크리트 — 기초 바닥에 까는 얇은 층. 빼려면 저장 제원에서 「안 넣음」으로 둔다.
|
||||||
base_width = thickness + constants["excavation_extra_m"]
|
base_width = thickness + constants["excavation_extra_m"]
|
||||||
blinding = _blinding_component(base_width, length_m, options)
|
blinding = _blinding_component(base_width, length_m, options)
|
||||||
@@ -836,5 +887,15 @@ def build_table(
|
|||||||
"surcharge_applied": False,
|
"surcharge_applied": False,
|
||||||
"mix_components_found": violations,
|
"mix_components_found": violations,
|
||||||
"structure_count": len(quantities),
|
"structure_count": len(quantities),
|
||||||
|
# ⚠ 사토에서 뺄 밑수 — **양수**로 낸다. 빼는 것은 유토곡선(랩탑 메인) 몫이다.
|
||||||
|
COLLECTED_STONE_KEY: round(
|
||||||
|
sum(
|
||||||
|
component.amount
|
||||||
|
for item in quantities
|
||||||
|
for component in item.components
|
||||||
|
if component.name == "채집석"
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
),
|
||||||
"amount_spread": spread_by_unit(totals.values(), value_key="amount"),
|
"amount_spread": spread_by_unit(totals.values(), value_key="amount"),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user