feat(B09): 미확정 산식 줄에 사유를 실음 + 갈래 이름 다리(리핑암↔파쇄암)

① 수량 산식이 확정 대기인 줄에 그 사실을 함께 실음 (계획서 4-12 3단계)
   구조물터파기가 내역서에서 가장 큰 줄(11,263,899원)이 됐는데 그 밑수가 사용자
   확정을 기다리는 식 위에 서 있음. 값은 그대로 두고 **드러내기만** 함.
     「⚠ 터파기 수량 산식이 확정 대기입니다 — 실무 시트는 기초만(기초폭 × 깊이 0.3)
       세고 우리는 벽 전체를 셉니다(m당 실무 0.35 vs 우리 2.30, 약 6.6배).
       실무 식으로 확정되면 이 줄 금액이 약 1/6.6 로 줄어듭니다.」
   되메우기·잔토처리도 같은 뿌리라 함께 표시. ⚠ 측구터파기는 다른 뿌리라 대상 아님

② `VARIANT_ALIASES` — 축이 달라 다르게 불리는 갈래를 **이름을 갈지 않고** 이음
   우리 「리핑암」은 B05·B06 지반유형(캘 수 있는가), 품셈 「파쇄암」은 운반 상태
   (부서졌는가)라 일반적으로 같은 말이 아님 — 발파암도 캐고 나면 파쇄암 상태임.
    다만 산림품셈 10-11 f 표가 토사·파쇄암·발파암을 따로 두어 그 표의 파쇄암은
   발파를 뺀 나머지이고, 10-12 [주]③ 「토사 1.3, **암절취 1.35**, 발파암 1.625」가
   10-11 의 파쇄암 1/1.35 와 **같은 값**이라 그 표 안에서는 암절취(리핑) = 파쇄암임.
   그 문장을 주석에 그대로 남김

검증: pytest 290 통과 · `find_variant_code("FP-10-11","리핑암")` → `B-FP-10-11#파쇄암`

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-09 08:11:49 +09:00
co-authored by Claude Opus 5
parent 1dceeb5bef
commit 1d5145eaa5
2 changed files with 62 additions and 0 deletions
@@ -307,6 +307,13 @@ def _leaf_row(
)
return row
# ⚠ **수량이 미확정 산식 위에 서 있는 줄**은 금액과 함께 그 사실을 싣는다.
# 금액이 커질수록 더 그렇다 — 지금 구조물터파기가 내역서에서 가장 큰 줄인데
# 그 밑수가 사용자 확정을 기다리고 있다(계획서 4-12 3단계).
pending = pending_formula_note(node.code)
if pending:
row.note = " / ".join(part for part in (row.note, pending) if part)
# 쓰인 차례를 기억한다 — 실무 참조번호(「단산 46」)가 그 차례다.
if price_code not in result.used_unit_prices:
result.used_unit_prices.append(price_code)
@@ -323,6 +330,41 @@ def _leaf_row(
return row
#: 수량 산식이 **사용자 확정을 기다리는** 공종 — 금액은 세우되 그 사실을 함께 싣는다.
#:
#: ⚠ **값을 우리가 바꾸지 않는다.** 실무 시트와 어긋나는 것이 확인됐을 뿐이고, 어느 쪽으로
#: 갈지는 설계 판단이다(계획서 4-12 3단계, 확정 대기).
#: 돌쌓기(찰) H=2.0·ℓ3=45 · m당 기준으로 실무 대비
#: 터파기 실무 0.35 우리 2.30 (약 6.6배) — 실무는 기초만(기초폭 × 깊이 0.3),
#: 우리는 벽 전체를 센다
#: 되메우기 실무 0.185 우리 0.40 · 잔토정리 실무 0.165 우리 1.90 (위 둘의 차)
_PENDING_FORMULA: dict[str, str] = {
"FP-09-13": (
"⚠ 터파기 수량 산식이 확정 대기입니다 — 실무 시트는 기초만(기초폭 × 깊이 0.3) 세고 "
"우리는 벽 전체를 셉니다(m당 실무 0.35 vs 우리 2.30, 약 6.6배). 실무 식으로 확정되면 "
"이 줄 금액이 약 1/6.6 로 줄어듭니다."
),
"FP-09-14": (
"⚠ 되메우기 수량 산식이 확정 대기입니다 — 터파기와 같은 뿌리입니다"
"(m당 실무 0.185 vs 우리 0.40)."
),
"FP-09-15": (
"⚠ 잔토처리 수량 산식이 확정 대기입니다 — 터파기·되메우기의 차라 같은 뿌리입니다"
"(m당 실무 0.165 vs 우리 1.90)."
),
}
def pending_formula_note(code: str | None) -> str:
"""그 공종의 수량 산식이 확정 대기인가 — 맞으면 실을 문구."""
if not code:
return ""
for prefix, note in _PENDING_FORMULA.items():
if str(code).startswith(prefix):
return note
return ""
def _material_row(material: HandoffMaterial, result: BillResult) -> BillRow:
"""자재 한 줄. 공급 구분이 안 갈렸으면 **어느 쪽에도 안 넣는다**."""
row = BillRow(
@@ -306,6 +306,25 @@ def normalize_variant_key(text: str) -> str:
return "".join("~" if ch in _TILDE_CHARS else ch for ch in tight)
#: 갈래 이름이 **축이 달라 다르게 불리는** 자리 — 이름을 갈지 않고 여기서 잇는다.
#:
#: ⚠ **우리 축과 품셈 축이 다르다.** 우리 「리핑암」은 B05·B06 의 **지반유형**(캘 수 있는가)이
#: 낳은 이름이고, 품셈 운반표의 「파쇄암」은 **운반할 때의 상태**(부서졌는가)를 가리킨다.
#: 그래서 일반적으로는 「리핑암 = 파쇄암」이 아니다 — **발파암도 캐고 나면 파쇄암 상태**다
#: (건설품셈 8장 「발파 또는 리퍼작업 등에 의하여 얻어진 암과 파쇄암…」).
#:
#: ⭐ **다만 이 표 안에서는 성립한다.** 산림품셈 10-11 f 표가 **토사·파쇄암·발파암을 따로**
#: 두었으므로 그 표의 「파쇄암」은 **발파를 뺀 나머지 = 리퍼로 얻은 것**이다. 결정적 증거는
#: 10-12 [주]③ 이다 — 「적재 재료의 토량환산계수(L)는 토사 1.3, **암절취 1.35**, 발파암
#: 1.625 적용한다」. 10-11 f 표의 **파쇄암이 1/1.35** 라 **암절취(리핑)과 같은 값**이다.
#:
#: ⚠ **어느 쪽 이름도 갈지 않는다** — 우리 이름을 갈면 B05·B06 이 깨지고, 품셈 이름을 갈면
#: 원문과 어긋난다. 잇는 자리는 여기 한 곳뿐이다.
VARIANT_ALIASES: dict[str, str] = {
"리핑암": "파쇄암",
}
def find_variant_code(
work_item_code: str,
variant_value: str,
@@ -320,6 +339,7 @@ def find_variant_code(
wanted = normalize_variant_key(variant_value)
if not wanted:
return None
wanted = VARIANT_ALIASES.get(wanted, wanted)
prefix = f"B-{work_item_code}#"
candidates = [code for code in prices.book.titles if code.startswith(prefix)]