main_laptop_1 -> main byeonghap (4 hwangyeong 585 commits) #12
@@ -72,6 +72,8 @@ class ObservedUnitTable:
|
||||
entries: list[dict[str, Any]] = field(default_factory=list)
|
||||
sources: dict[str, Any] = field(default_factory=dict)
|
||||
not_found: dict[str, Any] = field(default_factory=dict)
|
||||
#: 값을 바꾸는 **설계 조건**인데 우리 제원에 칸이 없는 것 — 화면이 보이게 한다.
|
||||
pending_choices: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def find(self, type_id: str, spec: dict[str, Any]) -> dict[str, Any] | None:
|
||||
"""규격이 **모두** 맞는 줄만 돌려준다. 하나라도 어긋나면 없는 것으로 본다."""
|
||||
@@ -101,6 +103,7 @@ def load_observed_table(path: Path | None = None) -> ObservedUnitTable:
|
||||
entries=list(payload.get("entries") or []),
|
||||
sources=payload.get("sources") or {},
|
||||
not_found=payload.get("not_found") or {},
|
||||
pending_choices=payload.get("pending_choices") or {},
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -288,7 +288,21 @@ OBSERVED_SPEC_KEYS: dict[str, tuple[str, ...]] = {
|
||||
EXPANDERS = {
|
||||
"masonry_wet": lambda h, l, o: stone_masonry(h, l, o, wet=True),
|
||||
"masonry_dry": lambda h, l, o: stone_masonry(h, l, o, wet=False),
|
||||
"boulder_masonry": lambda h, l, o: stone_masonry(h, l, o, wet=False),
|
||||
# ⚠⚠ **큰돌쌓기(`boulder_masonry`)를 여기에 두지 않는다** (2026-09-07 발견).
|
||||
# 큰돌쌓기는 품셈 **13-6** 이고 돌쌓기는 **13-4** 다 — **규격 축이 다르다.**
|
||||
# 돌쌓기는 **뒷길이**(35·45·55·60㎝), 큰돌쌓기는 **직경**(40~60·60~80·80~100㎝).
|
||||
# 앞서 `stone_masonry(dry)` 로 전개하고 있었는데, 그러면 직경 60~80㎝ 짜리가
|
||||
# **「뒷길이 45㎝」 계수로 돌아 조용히 틀린 값**이 나온다(고임돌 0.15·야면석 0.88 …).
|
||||
# ⚠ 값이 나오기는 하므로 어떤 시험도 안 잡던 자리다 — 「값이 있기는 하니 안 보이는」 그것.
|
||||
# 전개식·관측 원단위가 설 때까지 **미확보로 드러낸다.**
|
||||
}
|
||||
|
||||
#: 전개식을 일부러 안 두는 종류 — 왜 안 두는지 사람이 읽게 적는다.
|
||||
EXPANDER_WITHHELD = {
|
||||
"boulder_masonry": (
|
||||
"큰돌쌓기는 품셈 13-6 이고 규격 축이 **직경**(40~60·60~80·80~100㎝)이다. "
|
||||
"돌쌓기(13-4)의 **뒷길이** 계수로 돌리면 조용히 틀린 값이 나온다 — 전개식 미확보."
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +377,11 @@ def expand(
|
||||
end_m=end if structure.get("end_m") is not None else None,
|
||||
options=dict(options),
|
||||
)
|
||||
withheld = EXPANDER_WITHHELD.get(type_id)
|
||||
if withheld:
|
||||
result.notes.append(f"전개식 미확보 — {withheld}")
|
||||
return result
|
||||
|
||||
expander = EXPANDERS.get(type_id)
|
||||
if expander is None:
|
||||
# 전개식이 없으면 **관측 원단위표**를 본다(치수가 저장돼 있지 않은 종류).
|
||||
@@ -456,6 +475,9 @@ def build_table(
|
||||
"formwork_reuse_missing": formwork_missing,
|
||||
# 동바리 — 대상이 없으면 0 이 아니라 「없음」이라고 말한다.
|
||||
"shoring": shoring_status(),
|
||||
# ⚠ 값을 바꾸는 설계 조건인데 우리 제원에 칸이 없는 것 — 화면에 드러낸다.
|
||||
# 「무엇을 정해야 하는지」만으로는 부족하고 **「정하면 얼마나 달라지는지」**까지.
|
||||
"pending_choices": (observed.pending_choices or {}).get("items") or [],
|
||||
"totals": sorted(totals.values(), key=lambda entry: entry["name"]),
|
||||
# 할증 전 값임을 응답에 못 박는다 — 자재총괄이 한 번만 붙인다(㉠).
|
||||
"surcharge_applied": False,
|
||||
|
||||
@@ -102,6 +102,14 @@ export interface FormworkInfo {
|
||||
formwork_notes?: string[];
|
||||
formwork_reuse_missing?: string[];
|
||||
shoring?: { applicable: boolean; reason: string; pending_types: string[] };
|
||||
/** 값을 바꾸는 설계 조건인데 우리 제원에 칸이 없는 것 — 화면에 드러낸다. */
|
||||
pending_choices?: {
|
||||
label: string;
|
||||
default?: unknown;
|
||||
where?: string;
|
||||
effect?: string;
|
||||
scope?: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
/** 성분이 어디로 가는지 — 화면에서도 보이게 한다. 규칙이 코드에만 있으면 잊힌다. */
|
||||
@@ -349,6 +357,19 @@ export function renderUnitQuantityGrid(response: MaterialResponse): HTMLElement
|
||||
wrap.append(line);
|
||||
}
|
||||
|
||||
// ⚠ 값을 바꾸는 설계 조건인데 칸이 없는 것 — 「무엇을 정해야 하는지」만으로는 부족하고
|
||||
// **「정하면 얼마나 달라지는지」**까지 보여야 사용자가 판단한다.
|
||||
for (const choice of info.pending_choices ?? []) {
|
||||
const line = document.createElement("p");
|
||||
line.className = "b08-quantity__notice";
|
||||
const parts = [`⚠ 미확정: ${choice.label}`];
|
||||
if (choice.effect) parts.push(choice.effect.replace(/\*\*/g, ""));
|
||||
if (choice.where) parts.push(`근거 ${choice.where}`);
|
||||
if (choice.scope) parts.push(choice.scope.replace(/\*\*/g, ""));
|
||||
line.textContent = parts.join(" · ");
|
||||
wrap.append(line);
|
||||
}
|
||||
|
||||
// ⚠ 품셈에 그 이름의 공종이 없어 **여러 공종으로 나뉘어 서는** 구조물 — 무엇으로
|
||||
// 나뉘는지와 각 조각의 물량·갈래를 보인다. 코드만으로는 사람이 검증할 수 없다.
|
||||
for (const group of response.composite ?? []) {
|
||||
|
||||
@@ -304,5 +304,32 @@
|
||||
"source": "품셈 13-6-2 [주]⑩ 「큰돌쌓기(찰쌓기)의 뒤채움콘크리트량은 0.2㎥기준으로 하고 현지여건에 따라 0.3㎥까지 적용할 수 있다」"
|
||||
}
|
||||
]
|
||||
},
|
||||
"pending_choices": {
|
||||
"note": "값을 바꾸는 **설계 조건**인데 우리 제원에 칸이 없는 것. 기본값과 「정하면 얼마나 달라지는지」를 함께 적어 화면이 보이게 한다.",
|
||||
"items": [
|
||||
{
|
||||
"key": "anti_suction_sheet",
|
||||
"label": "흡출방지재·차수시트 시공",
|
||||
"default": false,
|
||||
"where": "품셈 13-6·13-7 [주]② — 「흡출방지재 또는 차수시트를 시공하는 경우는 ( )의 값을 적용한다」",
|
||||
"effect": "인부 수량이 갈림 — 큰돌쌓기 메쌓기 직경 40~60㎝ 기준 보통인부 1.04 → 1.17 인/10㎡ (약 +12.5 %)",
|
||||
"scope": "⚠ 큰돌쌓기(13-6)·큰돌붙이기(13-7)에만 걸린다. 지금 쓰는 돌쌓기(13-4)에는 괄호 값 자체가 없다."
|
||||
},
|
||||
{
|
||||
"key": "timber_crib_unit",
|
||||
"label": "목재틀흙막이 원단위",
|
||||
"default": "품셈 13-13-1 그대로",
|
||||
"where": "품셈 13-13-1 (단위: 인/㎥당)",
|
||||
"effect": "1㎥당 건축목공 16.975인 + 보통인부 1.848인. ⚠ 실무 감각에 맞는지 아무도 판단 못 했고, 각재·판재 자재가 카탈로그에 없어 **지금 값은 모자란 값**이다."
|
||||
},
|
||||
{
|
||||
"key": "bill_quantity_digits",
|
||||
"label": "내역서 수량 표시 자릿수",
|
||||
"default": null,
|
||||
"where": "`단수처리_규칙.md` 에 **금액 자리만 있고 수량 자리가 없음**",
|
||||
"effect": "표시 자릿수와 계산 자릿수가 다르면 보는 사람이 반올림해 곱해 보고 「틀렸다」고 한다(2026-09-07 실제로 그렇게 오진한 일이 있었다)."
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user