main_laptop_1 -> main byeonghap (4 hwangyeong 585 commits) #12

Merged
eomsangdon merged 585 commits from main_laptop_1 into main 2026-09-08 17:26:30 +09:00
Showing only changes of commit 4deaf0239c - Show all commits
+52 -1
View File
@@ -128,6 +128,12 @@ NOTE_METHOD_MISSING = "시공법 미지정으로 공종을 못 고름"
#: 아니라 공종 이름이라 성분 목록에는 안 온다.
REBAR_PREFIXES = ("이형철근", "원형철근", "철근")
#: 줄이 왜 막혔나 — **받는 쪽이 「사용자가 입력하면 풀리는 것」과 「우리가 만들어야 하는 것」을
#: 화면에서 갈라야** 한다(2026-09-07 3자 확정). 8-27 표에서 이미 가른 그 축이다.
BLOCKED_INPUT_MISSING = "input_missing" # 저장 제원 칸이 비어 있음 — 입력하면 풀림
BLOCKED_UNIT_DATA_MISSING = "unit_data_missing" # 원단위·표준 물량 자료가 없음
BLOCKED_FORMULA_MISSING = "formula_missing" # 수량 산출식 자체가 없음
#: 사면 계열 — 토공집계에 함께 실리지만 출처가 사면적이다.
SLOPE_GROUPS = frozenset({"성토면다짐", "초류종자살포", "지장목제거", "층따기", "면고르기"})
@@ -459,6 +465,14 @@ def _earthwork_rows(
"station_from": None,
"station_to": None,
"spec_detail": "",
"composite_parts": None,
"structure_kind": None,
"spec_class": None,
"spec_class_basis": "",
# 토공 줄은 막힐 자리가 없다 — 그래도 **칸은 둔다**(계약이 한 모양이어야 한다).
"blocked_kind": None,
"blocked_reason": "",
"composite_not_ready": None,
# 합계 줄과 무대 줄은 값은 내되 내역에 안 선다.
"in_bill": bool(row.get("in_bill", True)) and not is_subtotal,
"excavation_method": methods.get(ground) if ground else None,
@@ -499,9 +513,17 @@ def _haul_rows(
"ground_class": row.get("ground") or None,
"haul_distance_m": float(row.get("average_distance_m") or 0.0),
"haul_equipment": equipment,
"excavation_method": None,
"station_from": None,
"station_to": None,
"spec_detail": "",
"composite_parts": None,
"structure_kind": None,
"spec_class": None,
"spec_class_basis": "",
"blocked_kind": None,
"blocked_reason": "",
"composite_not_ready": None,
"in_bill": in_bill,
"in_bill_reason": str(entry.get("reason") or ""),
"origin": ORIGIN_HAUL,
@@ -510,6 +532,30 @@ def _haul_rows(
return rows, unmatched
def blocked_of(structure: dict[str, Any], class_basis: str = "") -> tuple[str | None, str]:
"""(막힌 갈래, 사유). 안 막혔으면 `(None, "")`.
⚠ 사유 문구는 **`B08_Quantity_Wording` 것을 그대로** 쓴다 — 두 벌로 짜면 갈린다.
전개 알림(`notes`)에 이미 사람 말로 적혀 있으므로 그것을 그대로 옮긴다.
"""
notes = [str(note) for note in structure.get("notes") or []]
if structure.get("components"):
# 물량은 섰는데 **단가 갈래**를 못 고른 자리(돌쌓기 뒷길이 등).
if class_basis and "입력되지 않았습니다" in class_basis:
return BLOCKED_INPUT_MISSING, class_basis
return None, ""
for note in notes:
if "입력되지 않았습니다" in note:
return BLOCKED_INPUT_MISSING, note
if "자료에 없습니다" in note or "표준 물량 자료" in note:
return BLOCKED_UNIT_DATA_MISSING, note
if "산출식이 아직 없습니다" in note:
return BLOCKED_FORMULA_MISSING, note
if notes:
return BLOCKED_UNIT_DATA_MISSING, notes[0]
return None, ""
def _structure_rows(
unit_quantity_table: dict[str, Any], mapping: WorkItemMapping
) -> tuple[list[dict[str, Any]], list[str]]:
@@ -534,9 +580,10 @@ def _structure_rows(
composite = mapping.composite_for(type_id) if code is None else None
kind = structure_kind(structure) if composite else None
parts: list[dict[str, Any]] | None = None
parts_missing: list[str] = []
parts_missing: list[dict[str, Any]] = []
if composite:
parts, parts_missing = composite_quantities(structure, composite, mapping)
blocked_kind, blocked_reason = blocked_of(structure, class_basis)
if code is None and composite is None:
unmatched.append(f"{wording_type_label(type_id)} — 품셈 공종을 아직 못 이었습니다")
elif entry.get("class_from") == "back_length" and class_key is None:
@@ -558,11 +605,15 @@ def _structure_rows(
"haul_equipment": None,
"station_from": structure.get("start_m"),
"station_to": structure.get("end_m"),
"excavation_method": None,
"spec_detail": _spec_detail(structure),
# 품셈에 그 이름의 공종이 없어 여러 공종을 묶는 자리 — 빈 코드와 구별한다.
"composite_parts": parts,
# 철근이 있나 없나로 자동 판정 — 사람이 고르는 값이 아니다.
"structure_kind": kind,
# ⚠ 줄마다 **왜 막혔는지**를 싣는다 — 안 실으면 받는 쪽 화면이 빈다.
"blocked_kind": blocked_kind,
"blocked_reason": blocked_reason,
# 규격 갈래(뒷길이 …㎝ 이하) — 못 고르면 사유가 남는다.
"spec_class": class_key,
"spec_class_basis": class_basis,