Merge remote-tracking branch 'origin/main_desktop_1' into sub_desktop_1

This commit is contained in:
2026-09-08 04:12:46 +09:00
8 changed files with 1470 additions and 43 deletions
@@ -359,6 +359,54 @@ def expression_cells(table: dict[str, Any]) -> list[str]:
CREW_QUANTITY_MARKS = ("시공량", "작업량", "1일작업량")
# ⚠ **이름에 자간 공백이 든 기종·직종** — 같은 표 묶음 안에서도 표기가 갈린다
# (`13-6-1` 은 「굴 삭 기 (무한궤도)」, 바로 옆 `13-6-2` 는 「굴착기 (무한궤도)」).
# 받는 쪽이 이름으로 자원을 찾으므로 **표기가 갈리면 그 줄이 통째로 빠진다.**
# ⚠ **여기서 이름을 고치지 않는다** — 정규화는 값을 살리지만 **잘못된 줄도 함께 살린다**
# (서브 창 실례: 이름 정규화 직후 버킷계수 `K` 를 소요량으로 읽어 시간당 사용료가 이중).
# 깃발만 실어 받는 쪽이 대조하게 한다.
_SPACED_NAME_RE = re.compile(r"^(?=.*\S\s\S)[가-힣](?:\s+[가-힣])+")
#: 시공능력 공식 파라미터가 값 자리에 온 줄 — **소요량이 아니다.** 그냥 읽으면 이중계상.
_FORMULA_KEYS = frozenset({"K", "k", "f", "E", "Cm", "q", "qo", "Q", "㎝(sec)"})
#: 자원 줄임을 알리는 단위 칸. 이것과 수치가 함께 있어야 자원으로 본다.
_RESOURCE_UNITS = frozenset(
{"", "h", "hr", "시간", "", "", "", "", "kg", "", "", "", "m", "", "", "ton"}
)
def spaced_names(table: dict[str, Any]) -> list[str]:
"""자간 공백이 든 **자원 이름**. 표기가 갈리는 자리를 드러낸다.
⚠ **좁게 잡는다.** 「단 위」·「모 래」 같은 머리글·재료명까지 걸면 101건이 되어
목록이 잡음이 되고, 잡음이 되면 아무도 안 본다(오늘 아홉 번 겪은 병).
**그 줄에 단위 칸과 수치가 함께 있는 것**만 자원 줄로 본다.
"""
found: list[str] = []
for row in table.get("rows", []):
if not row:
continue
name = norm(row[0])
if not name or not _SPACED_NAME_RE.match(name):
continue
rest = [norm(cell) for cell in row[1:]]
has_unit = any(cell in _RESOURCE_UNITS for cell in rest)
has_number = any(_PLAIN_NUMBER_RE.match(cell) for cell in rest if cell)
if has_unit and has_number and name not in found:
found.append(name)
return found
def formula_rows(table: dict[str, Any]) -> list[str]:
"""값 자리에 시공능력 공식 기호가 온 줄. 자원으로 세면 이중계상이다."""
found: list[str] = []
for row in table.get("rows", []):
for cell in row:
text = norm(cell)
if text in _FORMULA_KEYS and text not in found:
found.append(text)
return found
def crew_table(table: dict[str, Any]) -> bool:
"""작업조 + 시공량으로 적힌 표인가."""
hay = " ".join(norm(h) for h in table.get("headers", []))
@@ -480,6 +528,10 @@ def build() -> dict[str, Any]:
"expression_cells": expression_cells(table),
# ⚠ 작업조 표 — 「4」가 소요량이 아니라 인원이다. 그냥 읽으면 35배 부푼다.
"crew_table": crew_table(table),
# ⚠ 이름 표기가 갈리는 줄 — 받는 쪽이 이름으로 찾으면 통째로 빠진다.
"spaced_names": spaced_names(table),
# ⚠ 공식 기호 줄 — 소요량이 아니다. 자원으로 세면 이중계상.
"formula_rows": formula_rows(table),
# 공식 기호가 이 표에 직접 있는가 — 없으면 앞 표에서 물려받는 모양이다.
"capacity_formula_here": capacity_formula_pending(table),
"variant_key": variant_axis(table),
+17 -4
View File
@@ -55,6 +55,17 @@ DATASET_PREFIX = "work_item_mapping_"
REBAR_DIR = Path(__file__).resolve().parents[1] / "resources" / "data_rebar"
REBAR_PREFIX = "rebar_complexity_"
def normalize_kind_key(label: str) -> str:
"""갈래 키 — **내부 공백만** 지운다 (2026-09-07 두 창 확정).
원문 표는 「보 통」처럼 자간 공백이 들어 있어 그대로 쓰면 양쪽이 안 맞는다.
⚠ **다른 글자는 손대지 않는다** — 정규화를 넓히면 오늘 아홉 번 겪은 그 병을
여기서 새로 만든다. 원문 문구는 버리지 않고 `label` 로 함께 싣는다.
"""
return "".join(str(label).split())
#: 줄이 어디서 왔나 — 되짚을 때 쓴다.
ORIGIN_EARTHWORK = "earthwork"
ORIGIN_STRUCTURE = "structure"
@@ -209,10 +220,11 @@ def composite_quantities(
entry["incomplete_note"] = spec["incomplete_note"]
if suffix == "euroform_type":
kind, why = euroform_type(str(structure.get("type_id") or ""))
entry["kind"] = kind
entry["kind"] = normalize_kind_key(kind) if kind else None
entry["kind_label"] = kind # 원문 문구 그대로
entry["kind_basis"] = why
if kind:
entry["code"] = f"{spec.get('code')}#{kind}"
entry["code"] = f"{spec.get('code')}#{normalize_kind_key(kind)}"
else:
entry["not_ready"] = True
entry["why"] = why
@@ -222,10 +234,11 @@ def composite_quantities(
complexity, why = rebar_complexity(
str(structure.get("type_id") or ""), structure.get("options") or {}
)
entry["kind"] = complexity
entry["kind"] = normalize_kind_key(complexity) if complexity else None
entry["kind_label"] = complexity # 원문 문구 그대로(자간 공백 포함)
entry["kind_basis"] = why
if complexity:
entry["code"] = f"{spec.get('code')}#{complexity}"
entry["code"] = f"{spec.get('code')}#{normalize_kind_key(complexity)}"
else:
entry["not_ready"] = True
entry["why"] = why
@@ -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 {},
)
@@ -233,6 +233,11 @@ def stone_masonry(
# 물구멍 — 벽면적 2㎡당 1개소, 개소당 0.5m.
# ⚠ 이것은 **관(파이프) 자재**이지 공제 대상이 아니다. 품셈 1-2-1 이 「공제하지 않는다」고
# 말하는 물구멍은 **콘크리트 체적에서 뺄 구멍**이고, 여기 값은 그 구멍에 넣는 **관 길이**다.
# ⚠⚠ **㉥ 이중계상** — 품셈 13-6-2·13-7-2 [주]③ 은 제잡비 **윗단** 값에
# 「물빼기 파이프 설치에 관계되는 노무비, 재료비를 포함한다」고 한다. 그 쪽을 쓰면
# 이 줄과 겹친다. **우리 선택은 이 줄을 세우고 제잡비는 아랫단(미설치)** 이다
# (`structure_unit_observed` 의 `double_count_rules`).
# 지금 쓰는 13-4 계열에는 제잡비 행 자체가 없어 겹치지 않는다(전수 확인).
# ⚠ 관종·지름은 미확정 — 법은 「지름 3~6㎝ 파이프」, 실무 관측은 Ø50. 규격이 정해지면
# 이름에 붙인다(`물구멍 Ø50`). 지어내지 않고 규격 없는 이름으로 둔다.
components.append(
@@ -283,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)의 **뒷길이** 계수로 돌리면 조용히 틀린 값이 나온다 — 전개식 미확보."
),
}
@@ -358,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:
# 전개식이 없으면 **관측 원단위표**를 본다(치수가 저장돼 있지 않은 종류).
@@ -451,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 ?? []) {
@@ -26,75 +26,235 @@
"entries": [
{
"type_id": "retaining_wall",
"spec": { "form": "반중력식", "height_m": 2.0 },
"spec": {
"form": "반중력식",
"height_m": 2.0
},
"unit": "m",
"source": "uljin_library",
"source_note": "§7 옹벽류 — 반중력식옹벽 H=2.0",
"components": [
{ "name": "콘크리트", "unit": "㎥", "amount": 1.35, "destination": "unit_price", "basis_note": "기초 0.75 + 벽체 0.60" },
{ "name": "버림콘크리트", "unit": "㎥", "amount": 0.15, "destination": "unit_price" },
{ "name": "유로폼", "unit": "㎡", "amount": 3.2, "destination": "unit_price", "basis_note": "배면+전면" },
{ "name": "합판거푸집", "unit": "㎡", "amount": 0.6, "destination": "unit_price", "basis_note": "기초" },
{ "name": "물구멍", "unit": "m", "amount": 0.32, "destination": "material", "basis_note": "Ø50" },
{ "name": "이형철근 D13", "unit": "kg", "amount": 13.45, "destination": "material" },
{ "name": "이형철근 D16", "unit": "kg", "amount": 30.42, "destination": "material" }
{
"name": "콘크리트",
"unit": "㎥",
"amount": 1.35,
"destination": "unit_price",
"basis_note": "기초 0.75 + 벽체 0.60"
},
{
"name": "버림콘크리트",
"unit": "㎥",
"amount": 0.15,
"destination": "unit_price"
},
{
"name": "유로폼",
"unit": "㎡",
"amount": 3.2,
"destination": "unit_price",
"basis_note": "배면+전면"
},
{
"name": "합판거푸집",
"unit": "㎡",
"amount": 0.6,
"destination": "unit_price",
"basis_note": "기초"
},
{
"name": "물구멍",
"unit": "m",
"amount": 0.32,
"destination": "material",
"basis_note": "Ø50"
},
{
"name": "이형철근 D13",
"unit": "kg",
"amount": 13.45,
"destination": "material"
},
{
"name": "이형철근 D16",
"unit": "kg",
"amount": 30.42,
"destination": "material"
}
]
},
{
"type_id": "pipe_inlet_basin",
"spec": { "inlet_basin_form": "돌집수정 ㄷ형" },
"spec": {
"inlet_basin_form": "돌집수정 ㄷ형"
},
"unit": "개소",
"source": "uljin_compare",
"source_note": "관보호공 돌집수정 ㄷ형 /개소",
"components": [
{ "name": "콘크리트", "unit": "㎥", "amount": 4.03, "destination": "unit_price" },
{ "name": "모르터", "unit": "㎥", "amount": 0.157, "destination": "unit_price" },
{ "name": "터파기", "unit": "㎥", "amount": 21.1, "destination": "earthwork", "basis_note": "토사 14.8 + 암 6.3 — 지반 구분은 토공집계가 다시 가름" },
{ "name": "되메우기", "unit": "㎥", "amount": 2.6, "destination": "earthwork" },
{ "name": "잔토처리", "unit": "㎥", "amount": 18.5, "destination": "earthwork" }
{
"name": "콘크리트",
"unit": "㎥",
"amount": 4.03,
"destination": "unit_price"
},
{
"name": "모르터",
"unit": "㎥",
"amount": 0.157,
"destination": "unit_price"
},
{
"name": "터파기",
"unit": "㎥",
"amount": 21.1,
"destination": "earthwork",
"basis_note": "토사 14.8 + 암 6.3 — 지반 구분은 토공집계가 다시 가름"
},
{
"name": "되메우기",
"unit": "㎥",
"amount": 2.6,
"destination": "earthwork"
},
{
"name": "잔토처리",
"unit": "㎥",
"amount": 18.5,
"destination": "earthwork"
}
]
},
{
"type_id": "pipe_inlet_basin",
"spec": { "inlet_basin_form": "돌집수정 ㄴ형" },
"spec": {
"inlet_basin_form": "돌집수정 ㄴ형"
},
"unit": "개소",
"source": "uljin_compare",
"source_note": "관보호공 돌집수정 ㄴ형 /개소",
"components": [
{ "name": "콘크리트", "unit": "㎥", "amount": 2.69, "destination": "unit_price" },
{ "name": "모르터", "unit": "㎥", "amount": 0.096, "destination": "unit_price" },
{ "name": "터파기", "unit": "㎥", "amount": 16.4, "destination": "earthwork", "basis_note": "토사 4.9 + 암 11.5" },
{ "name": "되메우기", "unit": "㎥", "amount": 1.2, "destination": "earthwork" },
{ "name": "잔토처리", "unit": "㎥", "amount": 15.2, "destination": "earthwork" }
{
"name": "콘크리트",
"unit": "㎥",
"amount": 2.69,
"destination": "unit_price"
},
{
"name": "모르터",
"unit": "㎥",
"amount": 0.096,
"destination": "unit_price"
},
{
"name": "터파기",
"unit": "㎥",
"amount": 16.4,
"destination": "earthwork",
"basis_note": "토사 4.9 + 암 11.5"
},
{
"name": "되메우기",
"unit": "㎥",
"amount": 1.2,
"destination": "earthwork"
},
{
"name": "잔토처리",
"unit": "㎥",
"amount": 15.2,
"destination": "earthwork"
}
]
},
{
"type_id": "pipe_inlet_basin",
"spec": { "inlet_basin_form": "□형(기본형)", "inlet_basin_material": "콘크리트", "pipe_diameter_mm": "800" },
"spec": {
"inlet_basin_form": "□형(기본형)",
"inlet_basin_material": "콘크리트",
"pipe_diameter_mm": "800"
},
"unit": "개소",
"source": "uljin_library",
"source_note": "§2 집수정 Ø800 — 내부 3.0×1.0×1.2, 벽 0.2, 바닥기초 3.4×1.4×0.2",
"components": [
{ "name": "콘크리트", "unit": "㎥", "amount": 2.84, "destination": "unit_price" },
{ "name": "합판거푸집", "unit": "㎡", "amount": 21.28, "destination": "unit_price" },
{ "name": "이형철근 D13", "unit": "kg", "amount": 4.78, "destination": "material" },
{ "name": "면목", "unit": "m", "amount": 12.67, "destination": "material", "basis_note": "A25" },
{ "name": "터파기", "unit": "㎥", "amount": 10.64, "destination": "earthwork" },
{ "name": "되메우기", "unit": "㎥", "amount": 6.44, "destination": "earthwork" },
{ "name": "잔토처리", "unit": "㎥", "amount": 4.2, "destination": "earthwork" }
{
"name": "콘크리트",
"unit": "㎥",
"amount": 2.84,
"destination": "unit_price"
},
{
"name": "합판거푸집",
"unit": "㎡",
"amount": 21.28,
"destination": "unit_price"
},
{
"name": "이형철근 D13",
"unit": "kg",
"amount": 4.78,
"destination": "material"
},
{
"name": "면목",
"unit": "m",
"amount": 12.67,
"destination": "material",
"basis_note": "A25"
},
{
"name": "터파기",
"unit": "㎥",
"amount": 10.64,
"destination": "earthwork"
},
{
"name": "되메우기",
"unit": "㎥",
"amount": 6.44,
"destination": "earthwork"
},
{
"name": "잔토처리",
"unit": "㎥",
"amount": 4.2,
"destination": "earthwork"
}
]
},
{
"type_id": "ford_pavement",
"spec": { "thickness_cm": 20 },
"spec": {
"thickness_cm": 20
},
"unit": "㎡",
"source": "uljin_compare",
"source_note": "콘크리트포장 T=20cm /㎡",
"components": [
{ "name": "레미콘", "unit": "㎥", "amount": 0.2, "destination": "unit_price" },
{ "name": "와이어메쉬", "unit": "㎡", "amount": 1.16, "destination": "material" },
{ "name": "터파기", "unit": "㎥", "amount": 0.2, "destination": "earthwork" },
{ "name": "잔토처리", "unit": "㎥", "amount": 0.2, "destination": "earthwork" }
{
"name": "레미콘",
"unit": "㎥",
"amount": 0.2,
"destination": "unit_price"
},
{
"name": "와이어메쉬",
"unit": "㎡",
"amount": 1.16,
"destination": "material"
},
{
"name": "터파기",
"unit": "㎥",
"amount": 0.2,
"destination": "earthwork"
},
{
"name": "잔토처리",
"unit": "㎥",
"amount": 0.2,
"destination": "earthwork"
}
]
}
],
@@ -113,9 +273,63 @@
},
{
"type_id": "retaining_wall",
"spec": { "form": "반중력식", "height_m": 1.6 },
"spec": {
"form": "반중력식",
"height_m": 1.6
},
"why": "울진 2공구에 H=1.6 이 실재하나 수치가 라이브러리에 없음. H=2.0 값을 비례로 줄이지 않음 — 기초·벽체는 높이에 비례하지 않음."
}
]
},
"double_count_rules": {
"note": "이 표의 값이 품셈 다른 자리와 겹치는 곳. 겹치면 한쪽만 쓴다.",
"rules": [
{
"key": "물빼기 파이프 ↔ 제잡비 윗단",
"where": "품셈 13-6-2·13-6-3·13-7-2 [주]③",
"quote": "물빼기 파이프를 설치한 경우는 윗단의 값, 설치하지 않는 경우는 아랫단의 값으로 하며, 상단에는 물빼기 파이프 설치에 관계되는 노무비, 재료비를 포함한다.",
"our_choice": "물구멍(관)을 **자재로 명시해 세고**, 큰돌쌓기·큰돌붙이기를 쓸 때는 **제잡비 아랫단(미설치)** 을 쓴다.",
"why": "물구멍을 자재 줄로 세우면 규격·수량이 눈에 보이고 되짚을 수 있다. 제잡비 윗단은 같은 것을 품 안에 녹이는 다른 방식이라 어느 쪽이든 하나만 골라야 한다.",
"scope": "⚠ 지금 쓰는 돌쌓기(13-4 계열)에는 **제잡비 행 자체가 없어** 겹치지 않는다(전수 확인). 이 규칙은 13-6·13-7 을 쓰게 될 때 걸린다.",
"guard": "제잡비 윗단과 물구멍 줄이 함께 서면 멈출 것 — B09 ㉥ 가드와 짝."
}
]
},
"原文_뒷받침": {
"note": "관측값이 **원문과 맞는 것이 확인된** 항목. 지금까지 관측값은 근거가 약한 참조였는데 이 줄은 원문 뒷받침이 있다.",
"items": [
{
"item": "채움콘크리트 0.2 ㎥/㎡",
"observed": "울진 라이브러리 돌쌓기(찰) 채움 0.2 ㎥/㎡",
"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 실제로 그렇게 오진한 일이 있었다)."
}
]
}
}
@@ -1,7 +1,7 @@
{
"schema_version": "1.0",
"dataset_id": "data_work_item_master_manifest",
"generated_at": "2026-09-08T01:45:21+09:00",
"generated_at": "2026-09-08T01:52:34+09:00",
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
"source": {
"dataset_id": "pum_forest",
@@ -12,8 +12,8 @@
"files": [
{
"file": "work_item_master_2026-01-01.json",
"sha256": "f80d3ad4bf3ee242d282d8d54d18305e1b89fbbb9fc414c31b95cecc8b84d47f",
"size_bytes": 813044
"sha256": "08b0c7c26c4aa569ace3b13490b8cbd00606254abb1439894eac750d387bfb03",
"size_bytes": 839464
},
{
"file": "form_undetermined_2026-01-01.json",
File diff suppressed because it is too large Load Diff