diff --git a/B08_Quantity/B08_Quantity_Build_WorkItemMaster.py b/B08_Quantity/B08_Quantity_Build_WorkItemMaster.py index 7f880e1b..e725c006 100644 --- a/B08_Quantity/B08_Quantity_Build_WorkItemMaster.py +++ b/B08_Quantity/B08_Quantity_Build_WorkItemMaster.py @@ -237,6 +237,58 @@ def detect_form(table: dict[str, Any], chapter: str | None) -> tuple[str, str]: BASIS_RE = re.compile(r"(\d[\d,.]*)\s*(㎥|m3|㎡|m2|㏊|ha|km|㎞|m|인|본|개|kg|㎏|톤|ton)\s*당") +# ⚠ **밑수는 표 안이 아니라 표 바로 위 본문에 있다** (2026-09-07 서브 창 제보로 파고 확인). +# `### 12-2. 표면 마무리` 다음 줄에 `(단위: ㎡당)` 이 오는 식이다. 표만 보면 못 찾고, +# 못 찾은 채로 두면 「10㎡당」 표를 1㎡당으로 알아 **곱셈이 10배 틀린다**. +# 앞서 확인한 「밑수가 **밀렸나**」와는 다른 물음이다 — 이번은 「**아예 안 적혔나**」다. +# ⚠ **「당」 또는 「단위:」 가 있어야 밑수다.** 둘 다 없으면 규격일 뿐이다 — +# 만들다 실제로 걸렸다: `(무한궤도,0.7㎥)` 를 「0.7㎥당」으로 읽어 5건이 잘못 잡혔다. +#: ⚠ `(단위: 인/㎡당)` 꼴 — **분모가 밑수**다. 값의 단위(인)를 밑수로 읽으면 뜻이 뒤집힌다. +SOURCE_BASIS_RATIO_RE = re.compile( + r"[((]\s*단위\s*[::]\s*[^))/]+/\s*([\d,]*\.?\d*)\s*" + r"(㎥|m3|㎡|m2|㏊|ha|㎞|km|m|매|본|개소|개|인|공|kg|㎏|톤|ton|주|식|대)\s*당\s*[))]" +) + +SOURCE_BASIS_RE = re.compile( + r"[((]\s*(?:" + r"단위\s*[::]\s*([\d,]*\.?\d*)\s*(?P㎥|m3|㎡|m2|㏊|ha|㎞|km|m|매|본|개소|개|인|공|kg|㎏|톤|ton|주|식|대)\s*당?" + r"|([\d,]*\.?\d*)\s*(?P㎥|m3|㎡|m2|㏊|ha|㎞|km|m|매|본|개소|개|인|공|kg|㎏|톤|ton|주|식|대)\s*당" + r")\s*[))]" +) +#: 표 위로 몇 줄까지 거슬러 볼 것인가. 더 올라가면 **앞 표의 밑수**를 잘못 물어 온다. +SOURCE_LOOKBACK = 6 + + +def basis_from_source(lines: list[str], line_no: int) -> tuple[float | None, str | None]: + """표 바로 위 본문에서 밑수를 읽는다. 못 찾으면 `(None, None)` — 1 로 단정하지 않는다. + + ⚠ 위로 거슬러 보되 **다른 표를 만나면 멈춘다**. 앞 표의 밑수를 물어 오면 조용히 틀린다. + """ + start = max(0, line_no - 1 - SOURCE_LOOKBACK) + for index in range(line_no - 2, start - 1, -1): + if index < 0 or index >= len(lines): + continue + text = lines[index].strip() + if text.startswith("|"): + break # 앞 표에 닿았다 — 그 위는 남의 밑수다 + if m := SOURCE_BASIS_RATIO_RE.search(text): + raw = (m.group(1) or "").replace(",", "") + try: + quantity = float(raw) if raw else 1.0 + except ValueError: + quantity = 1.0 + return quantity, m.group(2) + if m := SOURCE_BASIS_RE.search(text): + unit = m.group("u1") or m.group("u2") + raw = (m.group(1) if m.group("u1") else m.group(3)) or "" + raw = raw.replace(",", "") + try: + quantity = float(raw) if raw else 1.0 + except ValueError: + quantity = 1.0 + return quantity, unit + return None, None + def detect_basis(table: dict[str, Any]) -> tuple[float | None, str | None]: """「100㎥당」 같은 밑수. 없으면 `(None, None)` — 단위당 1 로 단정하지 않는다.""" @@ -250,6 +302,36 @@ def detect_basis(table: dict[str, Any]) -> tuple[float | None, str | None]: return None, None +# ⚠ **딱지가 비율을 달고 오는 표** — `인력(10%)` · `장비(90%)` (2026-09-07 서브 창 제보로 확인). +# 그 표는 소요량형이면서 **장비 몫이 시공능력 공식**(Q = 3600·q·K·f·E ÷ Cm)이라 +# **인력 10 % 만 값으로 서 있다.** 형태 한 낱말(`requirement`)로만 적으면 받는 쪽이 +# 그 단가를 전량에 곱해 **내역서가 9할 싸게** 선다. 그래서 **몫과 미완 여부를 따로 싣는다.** +SHARE_TAG_RE = re.compile( + r"^(자재|재료|재료비|자재비|잡재료|장비|기계|인력|노무|노무비|인건비|경비|공구손료)" + r"\s*[((]\s*(\d+(?:\.\d+)?)\s*[%%]\s*[))]$" +) +#: 시공능력 공식 파라미터. 이 기호가 있으면 그 몫은 **아직 조립이 안 된 것**이다. +CAPACITY_SYMBOLS = {"K", "k", "f", "E", "Cm", "㎝(sec)", "q", "qo", "Q"} + + +def resource_shares(table: dict[str, Any]) -> dict[str, float]: + """`{인력: 10.0, 장비: 90.0}` — 딱지에 붙은 몫. 없으면 빈 칸.""" + shares: dict[str, float] = {} + for row in table.get("rows", []): + if not row: + continue + if m := SHARE_TAG_RE.match(norm(row[0])): + shares[m.group(1)] = float(m.group(2)) + return shares + + +def capacity_formula_pending(table: dict[str, Any]) -> bool: + """장비 몫이 **시공능력 공식**으로만 적혀 있어 아직 값이 안 된 상태인가.""" + keys = {norm(row[0]) for row in table.get("rows", []) if row} + cells = {norm(c) for row in table.get("rows", []) for c in row} + return bool((keys | cells) & CAPACITY_SYMBOLS) + + def variant_axis(table: dict[str, Any]) -> list[str]: """행이 갈리는 축 — 표의 첫 열 값들(토질·암종·규격). 값 열은 뺀다.""" seen: list[str] = [] @@ -260,14 +342,31 @@ def variant_axis(table: dict[str, Any]) -> list[str]: return seen[:24] +def basis_quantity_is_grouped(quantity: float | None) -> bool: + """「10㎡당」처럼 **묶음 기준**인가. 1 이 아니면 곱셈이 그만큼 갈린다.""" + return quantity is not None and abs(quantity - 1.0) > 1e-9 + + def build() -> dict[str, Any]: data = json.loads(SOURCE.read_text(encoding="utf-8")) tables = data["variables"]["pum"]["tables"] + # 원문을 함께 연다 — 밑수가 표 밖(본문)에 있기 때문이다. 못 열면 밑수 없이 간다. + source_lines: list[str] = [] + for entry in data.get("sources") or []: + candidate = ROOT / str(entry.get("path") or "") + if candidate.is_file(): + source_lines = candidate.read_text(encoding="utf-8").splitlines() + break toc_table = next(t for t in tables if t["table_id"] == "F0001") nodes = parse_toc(toc_table["rows"]) by_number = {n["number"]: n for n in nodes} attached = 0 + # ⚠ 밑수를 못 찾은 표 목록. 「곱하면 안 되는 줄」을 받는 쪽이 가릴 수 있게 낸다 — + # 빈칸으로 두면 「1단위당」으로 오해되어 곱셈이 10배·100배 틀린다. + basis_missing: list[dict[str, Any]] = [] + basis_found = 0 + basis_grouped = 0 orphans: list[dict[str, Any]] = [] undetermined: list[dict[str, Any]] = [] @@ -278,7 +377,38 @@ def build() -> dict[str, Any]: number = section_number(section) chapter = number.split("-")[0] if number else None form, why = detect_form(table, chapter) - basis_qty, basis_unit = detect_basis(table) + # ⚠ **본문이 정본이다.** 표 안을 긁는 쪽은 보조 — 비고에 적힌 다른 기준 + # (「10㎡당」 같은 참고 문구)을 그 표의 밑수로 잘못 물어 온다. + # 실제로 13-3-1 이 본문 `(단위: ㎥당)` 인데 표 안 긁기가 `10㎡` 를 물어 왔다. + basis_qty = basis_unit = None + if source_lines: + basis_qty, basis_unit = basis_from_source(source_lines, int(table.get("line") or 0)) + if basis_qty is None and basis_unit is None: + basis_qty, basis_unit = detect_basis(table) + basis_source = "표 안" if basis_unit else None + else: + basis_source = "본문" + if basis_unit: + basis_found += 1 + if basis_quantity_is_grouped(basis_qty): + basis_grouped += 1 + elif form in ("requirement", "productivity"): + # 참조·계수표는 곱할 값이 아니므로 목록에 넣지 않는다 — 잡음이 되면 안 본다. + basis_missing.append( + { + "pum_table_id": table["table_id"], + "section": norm(table.get("section")), + "pum_form": form, + "line": table.get("line"), + } + ) + shares = resource_shares(table) + # ⚠ 「값이 일부만 선 표」를 정상으로 흘려보내지 않는다. **몫이 적혀 있으면 부분값**으로 + # 본다 — 관측한 25건 모두 장비 몫이 시공능력 공식으로만 적혀 있어 값이 아니었고, + # 공식 기호가 첫 표에만 있고 이어지는 표는 그것을 물려받는 모양이라 + # 「기호가 있는 표만」으로 세면 절반을 놓친다(9-13-2 가 그 경우). + # ⚠ 이 깃발은 **표시일 뿐 값을 지우지 않는다** — 넓게 잡아도 정상 값이 안 사라진다. + partial = bool(shares) entry = { "pum_table_id": table["table_id"], "section": section, @@ -287,6 +417,12 @@ def build() -> dict[str, Any]: "form_basis": why, "basis_quantity": basis_qty, "basis_unit": basis_unit, + # 밑수를 어디서 읽었나 — 본문(정본) / 표 안(보조). 없으면 None. + "basis_source": basis_source, + "resource_shares": shares, + "partial_ratio": partial, + # 공식 기호가 이 표에 직접 있는가 — 없으면 앞 표에서 물려받는 모양이다. + "capacity_formula_here": capacity_formula_pending(table), "variant_key": variant_axis(table), "condition_note": [norm(h) for h in table.get("headers", []) if norm(h)], "raw_row": table.get("rows", []), # 원문 셀 — B09 자원 축이 읽는다. @@ -314,36 +450,44 @@ def build() -> dict[str, Any]: "sha256": sha256_of(SOURCE), "file": SOURCE.name, } - return { - "schema_version": SCHEMA_VERSION, - "dataset_id": "work_item_master_forest", - "effective_date": data["effective_date"], - "generated_at": datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds"), - "dataset_version": src_meta, - "policy": { - "axis": "work_item_only", - "resource_axis_owner": "B09", - "no_invented_values": True, - "raw_row_preserved": True, + return ( + { + "schema_version": SCHEMA_VERSION, + "dataset_id": "work_item_master_forest", + "effective_date": data["effective_date"], + "generated_at": datetime.now(timezone.utc).astimezone().isoformat(timespec="seconds"), + "dataset_version": src_meta, + "policy": { + "axis": "work_item_only", + "resource_axis_owner": "B09", + "no_invented_values": True, + "raw_row_preserved": True, + }, + "stats": { + "toc_nodes": len(nodes), + "tables_total": len(tables) - 1, + "tables_attached": attached, + "tables_orphan": len(orphans), + "form_undetermined": len(undetermined), + "basis_found": basis_found, + "basis_missing": len(basis_missing), + "basis_grouped": basis_grouped, + }, + "orphan_tables": orphans, + "work_items": nodes, }, - "stats": { - "toc_nodes": len(nodes), - "tables_total": len(tables) - 1, - "tables_attached": attached, - "tables_orphan": len(orphans), - "form_undetermined": len(undetermined), - }, - "orphan_tables": orphans, - "work_items": nodes, - }, undetermined + undetermined, + basis_missing, + ) def main() -> None: - master, undetermined = build() + master, undetermined, basis_missing = build() OUT_DIR.mkdir(parents=True, exist_ok=True) date = master["effective_date"] master_path = OUT_DIR / f"work_item_master_{date}.json" undet_path = OUT_DIR / f"form_undetermined_{date}.json" + basis_path = OUT_DIR / f"basis_missing_{date}.json" master_path.write_text(json.dumps(master, ensure_ascii=False, indent=1), encoding="utf-8") undet_path.write_text( @@ -361,6 +505,24 @@ def main() -> None: encoding="utf-8", ) + basis_path.write_text( + json.dumps( + { + "schema_version": SCHEMA_VERSION, + "dataset_id": "work_item_master_basis_missing", + "effective_date": date, + "note": ( + "밑수(「10㎡당」 같은 기준 수량)를 못 찾은 표. **1 단위당으로 단정하지 말 것** — " + "곱셈이 10배·100배 틀린다. 값을 곱해야 하는 형태(requirement·productivity)만 담는다." + ), + "items": basis_missing, + }, + ensure_ascii=False, + indent=1, + ), + encoding="utf-8", + ) + manifest = { "schema_version": SCHEMA_VERSION, "dataset_id": "data_work_item_master_manifest", @@ -373,7 +535,7 @@ def main() -> None: "sha256": sha256_of(p), "size_bytes": p.stat().st_size, } - for p in (master_path, undet_path) + for p in (master_path, undet_path, basis_path) ], } (OUT_DIR / "_manifest.json").write_text( @@ -386,6 +548,8 @@ def main() -> None: f"표 귀속 {s['tables_attached']} / {s['tables_total']} (미귀속 {s['tables_orphan']})" ) print(f"형태 미판정 {s['form_undetermined']}") + print(f"밑수 확보 {s['basis_found']} (묶음 기준 {s['basis_grouped']})") + print(f"밑수 미확보 {s['basis_missing']} → {basis_path.name}") print(f"산출 {master_path.relative_to(ROOT)}") diff --git a/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py b/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py index 36c3afcd..f3e3eb17 100644 --- a/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py +++ b/B08_Quantity/B08_Quantity_Engine_EarthworkSummary.py @@ -44,7 +44,19 @@ class SummaryRow: item: str = "" # 공종 (토사·연암·…) spec: str = "" # 규격 (기계(굴삭기)·백호우·…) unit: str = "㎥" - amount: float = 0.0 + amount: float = 0.0 # 반영률을 **곱한 뒤** 값 — 내역서에 쓰는 값 + # ⚠ 반영률 **적용 전** 값과 쓴 율을 함께 남긴다 (2026-09-07 3자 계약). + # 곱하기는 **B08 한 곳에서만** 한다. B09 가 율만 보고 또 곱하면 값이 두 배가 된다. + # 반영률 개념이 없는 줄은 `None` 이고, 100 % 인 줄도 **100.0 을 적는다** — + # 칸이 비어 있으면 「적용됐는지」를 받는 쪽이 단정할 수 없다. + amount_gross: float | None = None + application_ratio_pct: float | None = None + # ⚠ 성·절토면이 갈리는 줄은 **늘 갈래별로** 싣는다 (2026-09-07 3자 계약 확정). + # 「율이 같을 때만 pct, 다를 때만 breakdown」으로 두면 받는 쪽에 갈래가 둘 생기고 + # 그게 **한쪽만 고쳐지는** 자리가 된다. `application_ratio_pct` 는 두 율이 같을 때만 + # 채우는 **편의값**이고, 정본은 아래 두 칸이다. + application_ratio_breakdown: dict[str, float] | None = None + quantity_breakdown: dict[str, float] | None = None note: str = "" # 내역서 줄이 되는가 — 무대처럼 품에 포함된 것은 False (PLAN 8-7 ㉡). in_bill: bool = True @@ -83,9 +95,7 @@ def _split_by_rock(total: float, source: SummaryInput) -> list[tuple[str, float, if given <= 0: return [("암", total, "")] note = "" if abs(given - 100.0) < 1e-9 else f"입력 합 {given:g} % → 100 % 로 안분" - return [ - (name, total * ratios[name] / given, note) for name in classes if ratios[name] > 0 - ] + return [(name, total * ratios[name] / given, note) for name in classes if ratios[name] > 0] def build_rows(source: SummaryInput) -> list[SummaryRow]: @@ -106,9 +116,7 @@ def build_rows(source: SummaryInput) -> list[SummaryRow]: ) for name, amount, note in _split_by_rock(earth.get(rock_key, 0.0), source): rows.append( - SummaryRow( - group=group, item=name, spec="굴삭기+브레카", amount=amount, note=note - ) + SummaryRow(group=group, item=name, spec="굴삭기+브레카", amount=amount, note=note) ) rows.append(SummaryRow(group="보정량계", amount=earth.get("adjusted_total_m3", 0.0))) @@ -125,18 +133,39 @@ def build_rows(source: SummaryInput) -> list[SummaryRow]: group="성토면다짐", unit="㎡", amount=fill_face * _ratio(source, "fill_slope_compaction"), + amount_gross=fill_face, + application_ratio_pct=_ratio(source, "fill_slope_compaction") * 100.0, + application_ratio_breakdown={"fill": _ratio(source, "fill_slope_compaction") * 100.0}, + quantity_breakdown={"fill": fill_face * _ratio(source, "fill_slope_compaction")}, note=_ratio_note(source, "fill_slope_compaction", "성토면"), ) ) seed = fill_face * _ratio(source, "seed_spray_fill") + cut_face * _ratio( source, "seed_spray_cut" ) + # ⚠ 성·절토면 율이 다를 수 있어 **한 줄에 하나의 율**로 못 적는다. 적용 전 합을 함께 두고 + # 율은 두 율이 같을 때만 적는다 — 다르면 `None` 이고 비고에 두 율이 적힌다. + seed_gross = fill_face + cut_face + seed_fill_ratio = _ratio(source, "seed_spray_fill") + seed_cut_ratio = _ratio(source, "seed_spray_cut") rows.append( SummaryRow( group="초류종자살포", spec="씨드스프레이", unit="㎡", amount=seed, + amount_gross=seed_gross, + application_ratio_pct=( + seed_fill_ratio * 100.0 if seed_fill_ratio == seed_cut_ratio else None + ), + application_ratio_breakdown={ + "fill": seed_fill_ratio * 100.0, + "cut": seed_cut_ratio * 100.0, + }, + quantity_breakdown={ + "fill": fill_face * seed_fill_ratio, + "cut": cut_face * seed_cut_ratio, + }, note=_seed_note(source), ) ) @@ -146,6 +175,16 @@ def build_rows(source: SummaryInput) -> list[SummaryRow]: group="지장목제거", unit="㎡", amount=removal * _ratio(source, "obstacle_removal"), + amount_gross=removal, + application_ratio_pct=_ratio(source, "obstacle_removal") * 100.0, + application_ratio_breakdown={ + "fill": _ratio(source, "obstacle_removal") * 100.0, + "cut": _ratio(source, "obstacle_removal") * 100.0, + }, + quantity_breakdown={ + "fill": slope.get("tree_removal_fill", 0.0) * _ratio(source, "obstacle_removal"), + "cut": slope.get("tree_removal_cut", 0.0) * _ratio(source, "obstacle_removal"), + }, note=_ratio_note(source, "obstacle_removal", "성토면+절토면"), ) ) @@ -215,6 +254,10 @@ def build_table(source: SummaryInput) -> dict[str, Any]: "spec": row.spec, "unit": row.unit, "amount": row.amount, + "amount_gross": row.amount_gross, + "application_ratio_pct": row.application_ratio_pct, + "application_ratio_breakdown": row.application_ratio_breakdown, + "quantity_breakdown": row.quantity_breakdown, "note": row.note, "in_bill": row.in_bill, } diff --git a/B08_Quantity/B08_Quantity_Engine_Formwork.py b/B08_Quantity/B08_Quantity_Engine_Formwork.py new file mode 100644 index 00000000..d37945dc --- /dev/null +++ b/B08_Quantity/B08_Quantity_Engine_Formwork.py @@ -0,0 +1,127 @@ +"""거푸집 사용횟수 — 접촉 면적에 **몇 회 쓰는 거푸집인지**를 붙인다 (B08 일감 ⑩). + +⚠ 사용횟수는 **관측값이 아니라 법이다** + 품셈 1-7-1 이 구조물 종류별로 정해 둔다 — 「3회 … 옹벽, 파라펫트, 날개벽 등 약간 복잡한 + 구조」. 그래서 실무 관측값으로 갈음하지 않고 **원문 문구를 그대로 데이터에 싣고** 우리 + 구조물이 그 줄의 어느 예시에 걸리는지를 적는다. 걸리는 예시가 없으면 지어내지 않는다. + +⚠⚠ **횟수별 재료 환산은 여기서 하지 않는다** (이중계상) + 품셈 12-4 의 「사용횟수별 기준수량에 대한 비율(%)」(합판 3회 46.1 % 등)은 **일위대가 + 재료비**에 걸리는 값이다. B08 이 면적에 그 비율을 곱해 넘기면 B09 가 또 곱해 두 번 준다. + **B08 이 내는 것은 「접촉 면적 + 몇 회짜리인가」까지다.** 비율표는 참고로만 싣는다. + +⚠ 동바리는 지금 대상이 없다 + 강관동바리(12-20)는 **슬래브를 떠받칠 때** 쓴다. 지금 서는 구조물(옹벽·집수정)은 벽체 + 거푸집뿐이라 대상이 아니고, 대상이 될 BOX암거·세월교는 치수·원단위가 미확보라 + 슬래브 면적 자체가 안 나온다. **없는 것을 0 으로 적지 않고 「대상 없음」이라고 말한다.** +""" + +from __future__ import annotations + +import json +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +DATASET_DIR = Path(__file__).resolve().parents[1] / "resources" / "data_formwork" +DATASET_PREFIX = "formwork_reuse_" + +#: 거푸집으로 보는 성분 이름. 정확히 같은 이름으로만 본다 — 부분일치면 「거푸집씻기」가 걸린다. +FORMWORK_NAMES = frozenset({"합판거푸집", "유로폼", "문양거푸집", "거푸집"}) + +NOTE_REUSE_MISSING = "사용횟수 미확보" +NOTE_NOT_APPLICABLE = "거푸집 대상 아님" + + +def _latest_dataset_path(directory: Path | None = None) -> Path | None: + folder = directory or DATASET_DIR + if not folder.is_dir(): + return None + files = sorted(folder.glob(DATASET_PREFIX + "*.json")) + return files[-1] if files else None + + +@dataclass +class FormworkTable: + """사용횟수표 한 판.""" + + effective_date: str = "" + source: dict[str, Any] = field(default_factory=dict) + type_map: list[dict[str, Any]] = field(default_factory=list) + reuse_by_class: list[dict[str, Any]] = field(default_factory=list) + reuse_ratio_pct: dict[str, Any] = field(default_factory=dict) + shoring: dict[str, Any] = field(default_factory=dict) + + def for_type(self, type_id: str) -> dict[str, Any] | None: + for row in self.type_map: + if row.get("type_id") == type_id: + return row + return None + + +def load_formwork_table(path: Path | None = None) -> FormworkTable: + """사용횟수표를 읽는다. 파일이 없으면 **빈 표** — 전부 「미확보」로 드러난다.""" + target = path or _latest_dataset_path() + if target is None or not target.is_file(): + return FormworkTable() + payload = json.loads(target.read_text(encoding="utf-8")) + return FormworkTable( + effective_date=str(payload.get("effective_date") or ""), + source=payload.get("source") or {}, + type_map=list(payload.get("type_map") or []), + reuse_by_class=list(payload.get("reuse_by_class") or []), + reuse_ratio_pct=payload.get("reuse_ratio_pct") or {}, + shoring=payload.get("shoring") or {}, + ) + + +def annotate( + structures: list[dict[str, Any]], table: FormworkTable | None = None +) -> tuple[list[str], list[str]]: + """산출물의 거푸집 성분에 사용횟수를 달아 준다. (알림, 미확보 종류) 를 돌려준다. + + 성분 딕셔너리를 **그 자리에서** 고친다 — 거푸집 줄만 손대고 나머지는 건드리지 않는다. + """ + found = table or load_formwork_table() + notes: list[str] = [] + missing: list[str] = [] + for structure in structures: + type_id = str(structure.get("type_id") or "") + entry = found.for_type(type_id) + targets = [ + component + for component in structure.get("components") or [] + if str(component.get("name") or "").strip() in FORMWORK_NAMES + ] + if not targets: + continue + if entry is None: + missing.append(type_id) + for component in targets: + component["reuse_count"] = None + component["reuse_note"] = NOTE_REUSE_MISSING + continue + count = entry.get("reuse_count") + for component in targets: + component["reuse_count"] = count + component["reuse_note"] = ( + f"품셈 1-7-1 {count}회 — 「{entry.get('matched_example')}」" + if count + else NOTE_NOT_APPLICABLE + ) + if count: + notes.append(f"{structure.get('name') or type_id} 거푸집 {count}회 (품셈 1-7-1)") + else: + missing.append(type_id) + return notes, sorted(set(missing)) + + +def shoring_status(table: FormworkTable | None = None) -> dict[str, Any]: + """동바리 — **대상이 없으면 없다고 말한다.** 0 으로 적으면 「없음」과 구별이 안 된다.""" + found = table or load_formwork_table() + shoring = found.shoring or {} + return { + "applicable": False, + "reason": str(shoring.get("note") or "슬래브 구조물이 없어 동바리 대상이 아님"), + "pending_types": list(shoring.get("targets_pending") or []), + } diff --git a/B08_Quantity/B08_Quantity_Engine_Handoff.py b/B08_Quantity/B08_Quantity_Engine_Handoff.py index 9c42b7d5..983589c4 100644 --- a/B08_Quantity/B08_Quantity_Engine_Handoff.py +++ b/B08_Quantity/B08_Quantity_Engine_Handoff.py @@ -18,6 +18,17 @@ ④예산내역서에 무대가 서서 운반비가 두 번 붙는다. 빼고 넘기지 않는 까닭은, 빠진 줄과 제외된 줄을 나중에 구별할 수 없기 때문이다. +⚠ **반영률은 B08 한 곳에서만 곱한다** (2026-09-07 3자 계약) + `quantity` 는 **곱한 뒤** 값이고 `quantity_gross` 는 곱하기 전 값이며 `application_ratio_pct` + 는 쓴 율이다. **셋을 함께 싣는 까닭**은 받는 쪽이 「이미 곱해졌나」를 단정할 수 있어야 + 하기 때문이다 — 율만 보내면 B09 가 또 곱해 값이 두 배가 된다. 100 % 인 줄도 `100.0` 을 + 적고, `None` 은 **반영률 개념이 없는 줄**에만 쓴다. + `verify_ratio_math()` 가 세 값이 서로 맞는지 실제로 재 본다. + 성·절토면이 갈리는 줄은 **늘** `application_ratio_breakdown`(갈래별 율)과 + `quantity_breakdown`(갈래별 물량)을 싣는다. `application_ratio_pct` 는 두 율이 같을 때만 + 채우는 **편의값**이다 — 「같을 때만 pct, 다를 때만 breakdown」으로 두면 받는 쪽에 갈래가 + 둘 생기고 그게 한쪽만 고쳐지는 자리가 된다(2026-09-07 3자 계약). + ⚠ `ground_class_set` 을 함께 싣는다 (2026-09-07 서브 이견 채택) 값이 「연암」이어도 **그 프로젝트가 몇 갈래 세트를 쓰는지**를 알아야 ④예산내역서에서 줄을 세울 수 있다(울진 2 · 거창 5 · 오솔길 1). 설정 파일을 안 봐도 **인계본만으로 ④가 서게** 한다. @@ -34,6 +45,8 @@ from dataclasses import dataclass, field from pathlib import Path from typing import Any, Iterable +from common_util.common_util_quantity_spread import spread_by_unit + DATASET_DIR = Path(__file__).resolve().parents[1] / "resources" / "data_work_item_mapping" DATASET_PREFIX = "work_item_mapping_" @@ -43,6 +56,16 @@ ORIGIN_STRUCTURE = "structure" ORIGIN_SLOPE = "slope" ORIGIN_HAUL = "haul" +#: 암 시공법 → 매핑표의 지반 이름. 품셈이 **긁어내기와 터뜨리기를 다른 공종**으로 두기 때문에 +#: 갈래 이름(연암·보통암…)만으로는 공종을 못 고른다(2026-09-07 일감 9 실서버에서 드러남). +METHOD_TO_GROUND = {"ripping": "리핑암", "blasting": "발파암"} +NOTE_METHOD_MISSING = "시공법 미지정으로 공종을 못 고름" + +#: 철근으로 보는 성분 이름 조각. **정확한 낱말이 아니라 앞머리**로 본다 — +#: 「이형철근 D13」·「원형철근」처럼 규격이 뒤에 붙기 때문이다. `철근콘크리트`는 성분 이름이 +#: 아니라 공종 이름이라 성분 목록에는 안 온다. +REBAR_PREFIXES = ("이형철근", "원형철근", "철근") + #: 사면 계열 — 토공집계에 함께 실리지만 출처가 사면적이다. SLOPE_GROUPS = frozenset({"성토면다짐", "초류종자살포", "지장목제거", "층따기", "면고르기"}) @@ -67,8 +90,10 @@ class WorkItemMapping: haul: list[dict[str, Any]] = field(default_factory=list) structure: list[dict[str, Any]] = field(default_factory=list) pending_user: dict[str, Any] = field(default_factory=dict) + composite: dict[str, Any] = field(default_factory=dict) + concrete_placing: dict[str, Any] = field(default_factory=dict) - def for_earthwork(self, group: str, ground: str | None) -> dict[str, Any] | None: + def for_earthwork(self, group: str, ground: str | None) -> dict[str, Any] | None: # noqa: D401 """공종+지반유형으로 찾는다. 지반이 갈리지 않는 공종은 `ground` 없는 줄이 받는다.""" exact = [ row @@ -93,6 +118,16 @@ class WorkItemMapping: return row return None + def composite_for(self, type_id: str) -> dict[str, Any] | None: + """품셈에 그 이름의 공종이 없어 **여러 공종을 묶는** 자리인가. + + 빈 코드로 두면 「매핑을 못 찾은 줄」과 구별이 안 된다 — 묶음을 적어 구별한다. + """ + for row in self.composite.get("items") or []: + if row.get("type_id") == type_id: + return row + return None + def load_mapping(path: Path | None = None) -> WorkItemMapping: """매핑표를 읽는다. 파일이 없으면 **빈 표** — 전 줄이 `unmatched` 로 드러난다.""" @@ -106,9 +141,34 @@ def load_mapping(path: Path | None = None) -> WorkItemMapping: haul=list(payload.get("haul") or []), structure=list(payload.get("structure") or []), pending_user=payload.get("pending_user") or {}, + composite=payload.get("composite") or {}, + concrete_placing=payload.get("concrete_placing") or {}, ) +def structure_kind(structure: dict[str, Any]) -> str: + """콘크리트 구조물 종류 — **원단위에 철근이 있나 없나로 판정한다.** + + 사람이 고르는 값이 아니다(2026-09-07 3자 확정). 옹벽 관측 원단위에 `D13`·`D16` 이 + 실려 있으므로 철근구조물로 자동으로 선다. 소형구조물 판정 기준은 아직 없다. + """ + for component in structure.get("components") or []: + name = str(component.get("name") or "").strip() + if any(name.startswith(prefix) for prefix in REBAR_PREFIXES): + return "철근구조물" + return "무근구조물" + + +def placing_code(mapping: WorkItemMapping, method: str | None) -> tuple[str | None, bool]: + """(타설 공종코드, 기본값을 쓴 것인가). 모르는 방식이면 기본으로 떨어지되 그 사실을 알린다.""" + table = mapping.concrete_placing or {} + codes = table.get("method_codes") or {} + default = str(table.get("default_method") or "") + if method in codes: + return codes[method], False + return codes.get(default), True + + def _spec_detail(structure: dict[str, Any]) -> str: """규격 표기 — 저장된 제원에서 만든다. 없는 값은 적지 않는다.""" parts: list[str] = [] @@ -121,8 +181,25 @@ def _spec_detail(structure: dict[str, Any]) -> str: return "·".join(parts) +def _mapping_ground(ground: str | None, methods: dict[str, str | None]) -> tuple[str | None, str]: + """갈래 이름을 **매핑표가 아는 이름**으로 바꾼다. + + 「토사」는 그대로 가고, 암 갈래는 **시공법이 정해져야** 리핑암·발파암으로 간다. + 안 정했으면 `(None, 사유)` — 찍지 않는다. 잘못 찍으면 공종이 조용히 틀린다. + """ + if ground is None or ground == "토사": + return ground, "" + method = methods.get(ground) + mapped = METHOD_TO_GROUND.get(method or "") + if mapped: + return mapped, "" + return None, NOTE_METHOD_MISSING + + def _earthwork_rows( - summary_table: dict[str, Any], mapping: WorkItemMapping + summary_table: dict[str, Any], + mapping: WorkItemMapping, + methods: dict[str, str | None], ) -> tuple[list[dict[str, Any]], list[str]]: """토공집계표 줄을 내역 줄로 옮긴다. @@ -138,10 +215,12 @@ def _earthwork_rows( ground = row.get("item") or None origin = ORIGIN_SLOPE if group in SLOPE_GROUPS else ORIGIN_EARTHWORK is_subtotal = group in SUBTOTAL_GROUPS - entry = mapping.for_earthwork(group, ground) + lookup_ground, method_note = _mapping_ground(ground, methods) + entry = mapping.for_earthwork(group, lookup_ground) if method_note == "" else None code = (entry or {}).get("work_item_code") if code is None and not is_subtotal: - unmatched.append(f"{group}({ground})" if ground else group) + label = f"{group}({ground})" if ground else group + unmatched.append(f"{label} — {method_note}" if method_note else label) rows.append( { "work_item_code": code, @@ -149,6 +228,12 @@ def _earthwork_rows( "spec": str(row.get("spec") or ""), "unit": str(row.get("unit") or "㎥"), "quantity": float(row.get("amount") or 0.0), + # 반영률 — 곱하기는 여기가 끝이다. 받는 쪽은 적기만 한다. + "quantity_gross": row.get("amount_gross"), + "application_ratio_pct": row.get("application_ratio_pct"), + # 율이 부분마다 다른 줄 — 받는 쪽이 문장을 안 뜯게 칸으로 준다. + "application_ratio_breakdown": row.get("application_ratio_breakdown"), + "quantity_breakdown": row.get("quantity_breakdown"), "ground_class": ground, "haul_distance_m": None, "haul_equipment": None, @@ -157,6 +242,7 @@ def _earthwork_rows( "spec_detail": "", # 합계 줄과 무대 줄은 값은 내되 내역에 안 선다. "in_bill": bool(row.get("in_bill", True)) and not is_subtotal, + "excavation_method": methods.get(ground) if ground else None, "in_bill_reason": "집계 합계 줄 — 검산용" if is_subtotal else str(row.get("note") or ""), @@ -186,6 +272,11 @@ def _haul_rows( "spec": str(row.get("ground") or ""), "unit": "㎥", "quantity": float(row.get("volume_m3") or 0.0), + # 운반에는 반영률 개념이 없다 — 그래서 `None` 이다(0 이 아니다). + "quantity_gross": None, + "application_ratio_pct": None, + "application_ratio_breakdown": None, + "quantity_breakdown": None, "ground_class": row.get("ground") or None, "haul_distance_m": float(row.get("average_distance_m") or 0.0), "haul_equipment": equipment, @@ -214,7 +305,9 @@ def _structure_rows( type_id = str(structure.get("type_id") or "") entry = mapping.for_structure(type_id) or {} code = entry.get("work_item_code") - if code is None: + composite = mapping.composite_for(type_id) if code is None else None + kind = structure_kind(structure) if composite else None + if code is None and composite is None: unmatched.append(f"구조물({type_id})") length = float(structure.get("length_m") or 0.0) rows.append( @@ -224,14 +317,24 @@ def _structure_rows( "spec": _spec_detail(structure), "unit": "m", "quantity": length, + "quantity_gross": None, + "application_ratio_pct": None, + "application_ratio_breakdown": None, + "quantity_breakdown": None, "ground_class": None, "haul_distance_m": None, "haul_equipment": None, "station_from": structure.get("start_m"), "station_to": structure.get("end_m"), "spec_detail": _spec_detail(structure), + # 품셈에 그 이름의 공종이 없어 여러 공종을 묶는 자리 — 빈 코드와 구별한다. + "composite_parts": (composite or {}).get("parts"), + # 철근이 있나 없나로 자동 판정 — 사람이 고르는 값이 아니다. + "structure_kind": kind, + # ⚠ 아직 일위대가가 안 선 공종 — 지금 세우면 절반짜리가 된다. + "composite_not_ready": (composite or {}).get("not_ready"), "in_bill": True, - "in_bill_reason": "", + "in_bill_reason": (composite or {}).get("why", ""), "origin": ORIGIN_STRUCTURE, } ) @@ -268,14 +371,16 @@ def build_handoff( mapping: WorkItemMapping | None = None, ground_class_set: str | None = None, ground_classes: list[str] | None = None, + ground_methods: dict[str, str | None] | None = None, ) -> dict[str, Any]: """B09 가 그대로 받는 모양. 없는 표는 건너뛰되 **빈 표와 구별해 적는다**.""" table = mapping or load_mapping() work_items: list[dict[str, Any]] = [] unmatched: list[str] = [] + methods = {key: value for key, value in (ground_methods or {}).items() if value} if summary_table: - rows, misses = _earthwork_rows(summary_table, table) + rows, misses = _earthwork_rows(summary_table, table, methods) work_items.extend(rows) unmatched.extend(misses) if haul_table: @@ -288,20 +393,44 @@ def build_handoff( unmatched.extend(misses) materials = _material_rows(material_table or {}) - return { + result: dict[str, Any] = { "work_items": work_items, "materials": materials, # 갈래 세트 — 「연암」이 몇 갈래 중 하나인지 알아야 ④가 선다. "ground_class_set": ground_class_set, "ground_classes": list(ground_classes or []), + "ground_methods": dict(methods), + # 시공법을 안 정해 공종을 못 고른 갈래 — 화면이 이 목록으로 안내를 띄운다. + "missing_method_classes": sorted( + { + str(row.get("ground_class")) + for row in work_items + if row.get("ground_class") + and row.get("ground_class") != "토사" + and row.get("work_item_code") is None + and row.get("origin") == ORIGIN_EARTHWORK + } + ), # 자재 쪽에만 할증이 있다 — 작업 공종에는 없다. + # ⚠ 세 갈래로 그대로 나른다(`applied`·`not_applied`·`rate_unavailable`). + # 「율이 없어 못 붙인 것」을 「붙였다」로 말하면 B09 가 나중에 한 번 더 붙인다. + "surcharge_status": (material_table or {}).get("surcharge_status"), "surcharge_applied_to_materials": bool((material_table or {}).get("surcharge_applied")), "unmatched_work_items": sorted(set(unmatched)), "mapping_pending_user": table.pending_user, "mapping_edition": table.effective_date, + "quantity_spread": spread_by_unit( + [row for row in work_items if row["in_bill"]], value_key="quantity" + ), + "material_spread": spread_by_unit( + [{"unit": row["unit"], "q": row["total_amount"]} for row in materials], value_key="q" + ), "bill_row_count": sum(1 for row in work_items if row["in_bill"]), "excluded_row_count": sum(1 for row in work_items if not row["in_bill"]), } + # ⚠ 검사는 **실제로 부른다** — 만들어 두고 안 부르면 없는 것과 같다. + result["ratio_math_warnings"] = verify_ratio_math(result) + return result def verify_no_code_on_materials(handoff: dict[str, Any]) -> list[str]: @@ -317,6 +446,25 @@ def verify_no_code_on_materials(handoff: dict[str, Any]) -> list[str]: return found +def verify_ratio_math(handoff: dict[str, Any], *, tolerance: float = 1e-6) -> list[str]: + """⚠ `quantity == quantity_gross × 율/100` 이 실제로 맞는지 재 본다. + + 세 칸을 실어 두고 **서로 어긋나면** 받는 쪽이 어느 값을 믿을지 알 수 없다. + 「만들어 두고 안 부르면 없는 것과 같다」를 피하려고 `build_handoff()` 가 직접 부른다. + """ + found: list[str] = [] + for row in handoff.get("work_items") or []: + gross = row.get("quantity_gross") + ratio = row.get("application_ratio_pct") + if gross is None or ratio is None: + continue + expected = float(gross) * float(ratio) / 100.0 + actual = float(row.get("quantity") or 0.0) + if abs(expected - actual) > max(tolerance, abs(expected) * 1e-9): + found.append(f"{row.get('name')}: {actual:g} ≠ {gross:g} × {ratio:g} %") + return found + + def verify_bill_flags(handoff: dict[str, Any]) -> list[str]: """⚠ 코드가 없는데 내역에 서는 줄이 있으면 알린다. @@ -324,8 +472,12 @@ def verify_bill_flags(handoff: dict[str, Any]) -> list[str]: """ found: list[str] = [] for row in handoff.get("work_items") or []: - if row.get("in_bill") and not row.get("work_item_code"): - found.append(str(row.get("name"))) + if not row.get("in_bill") or row.get("work_item_code"): + continue + # 묶음으로 서는 줄은 코드가 없어도 정상이다 — 무엇으로 묶이는지 적혀 있다. + if row.get("composite_parts"): + continue + found.append(str(row.get("name"))) return found diff --git a/B08_Quantity/B08_Quantity_Engine_MaterialSummary.py b/B08_Quantity/B08_Quantity_Engine_MaterialSummary.py index 90df4a85..c8283641 100644 --- a/B08_Quantity/B08_Quantity_Engine_MaterialSummary.py +++ b/B08_Quantity/B08_Quantity_Engine_MaterialSummary.py @@ -23,6 +23,10 @@ 「품셈 항목에 할증이 포함ㆍ표시된 경우 중복 적용 금지」. 성분이 그렇게 표시돼 오면 (`surcharge_included: True`) 율을 붙이지 않고 비고에 까닭을 남긴다. +⚠ 관급구분은 **세 값**이다 — `owner_supplied` · `contractor_supplied` · `unknown` + `unknown` 은 「아직 안 정함」이고 **지어내지 않겠다는 뜻**이다. B09 는 이 줄을 관급자재대에도 + 도급 재료비에도 넣지 않고 `missing` 으로 뺀다(2026-09-07 계약에 명시). + ⚠ 관급/사급은 **법이 아니라 발주 결정**이다 자재마다 정해진 값이 아니므로 지어내지 않는다. 프로젝트 설정 (`quantity.material_supply`)이 정한 것만 따르고, 안 정한 자재는 `unknown` 으로 남겨 @@ -46,6 +50,8 @@ from dataclasses import dataclass, field from pathlib import Path from typing import Any, Iterable +from common_util.common_util_quantity_spread import spread_by_unit + # ── 데이터 자리 ────────────────────────────────────────────────────── DATASET_DIR = Path(__file__).resolve().parents[1] / "resources" / "data_material_surcharge" DATASET_PREFIX = "material_surcharge_" @@ -69,6 +75,14 @@ INSTALL_BY_OWNER = "owner" # 관 직접설치 INSTALL_BY_LABELS = {INSTALL_BY_CONTRACTOR: "도급자설치", INSTALL_BY_OWNER: "관 직접설치"} NOTE_INSTALL_BY_MISSING = "설치 주체 미지정" +#: ⚠ 할증 상태는 **세 갈래**다 (2026-09-07 3자 계약 정정). +#: 두 갈래(`True`/`False`)로 두면 「율을 못 찾아 안 붙인 것」이 「붙였다」로 나가고, +#: 나중에 진짜 율이 들어왔을 때 B09 가 한 번 더 붙인다. **깃발과 실제가 어긋나지 않을 것**이 +#: 요건이므로 상태를 그대로 말한다. +SURCHARGE_APPLIED = "applied" # 한 줄이라도 실제로 붙음 +SURCHARGE_NOT_APPLIED = "not_applied" # 붙일 줄이 없음(자재 자체가 없음) +SURCHARGE_RATE_UNAVAILABLE = "rate_unavailable" # 자재는 있는데 율을 못 찾음 + NOTE_RATE_MISSING = "할증률 미확보" NOTE_INCLUDED = "품셈에 할증 포함 — 중복 적용 안 함" @@ -159,6 +173,15 @@ class MaterialRow: return " · ".join(parts) +def _surcharge_status(rows: list[MaterialRow]) -> str: + """할증이 실제로 붙었는가 — 세 갈래로 답한다.""" + if not rows: + return SURCHARGE_NOT_APPLIED + if any(row.surcharge_pct is not None and not row.surcharge_included for row in rows): + return SURCHARGE_APPLIED + return SURCHARGE_RATE_UNAVAILABLE + + def _supply_of(value: Any) -> tuple[str, str | None]: """설정 한 칸을 (관급구분, 설치주체) 로 읽는다. @@ -291,8 +314,11 @@ def build_table( } for row in ordered ], - # 이 표가 할증을 붙인 곳임을 못 박는다 — B09 는 다시 붙이지 않는다(㉠). - "surcharge_applied": True, + # ⚠ **깃발이 실제와 어긋나지 않게** 한다. 「붙일 자리였는데 율이 없어 못 붙였다」를 + # 「붙였다」로 말하면, 나중에 율이 들어왔을 때 B09 가 한 번 더 붙인다. + "surcharge_status": _surcharge_status(ordered), + # 옛 두 갈래 깃발 — **실제로 붙었을 때만** 참이다(호환을 위해 남긴다). + "surcharge_applied": _surcharge_status(ordered) == SURCHARGE_APPLIED, "surcharge_dataset": { "effective_date": table.effective_date, "source": table.source, @@ -303,4 +329,9 @@ def build_table( "double_count_warnings": verify_single_surcharge(unit_quantity_table), "skipped_by_destination": skipped, "row_count": len(ordered), + # 값의 크기가 말이 되나 — 자릿수 어긋남은 사람이 훑어야 보인다(단위별로 가른다). + "amount_spread": spread_by_unit( + [{"unit": row.unit, "amount": row.total_amount} for row in ordered], + value_key="amount", + ), } diff --git a/B08_Quantity/B08_Quantity_Engine_ObservedUnit.py b/B08_Quantity/B08_Quantity_Engine_ObservedUnit.py new file mode 100644 index 00000000..71c3d762 --- /dev/null +++ b/B08_Quantity/B08_Quantity_Engine_ObservedUnit.py @@ -0,0 +1,165 @@ +"""콘크리트 구조물 — **관측 원단위표** 조회 (B08 일감 ⑩ · PLAN 8-6·8-8). + +왜 전개식이 아니라 관측값인가 + 지식DB 가 못 박아 둔 사실이다 — **구조물별 표준 물량표는 품셈에 없다** + (`구조물_수량.md` 마지막 줄 · `배수공_수량.md` §2). 콘크리트 구조물의 물량은 + **설계 표준도**에서 나오는데 그 표준도가 원문(법·품셈)에 없다. 그래서 옹벽·집수정처럼 + 치수가 표준화된 것은 **실무 설계원본에서 뽑은 관측값**이 유일한 원천이다. + +두 근거가 한 표에 섞인다 — 그래서 줄마다 `basis` 를 단다 + · `derived` — 저장된 치수에서 **식으로** 나온 값(돌쌓기 계열, `..._Engine_UnitQuantity`). + · `observed` — 실무 관측 원단위표에서 **규격을 맞춰 꺼낸** 값(이 모듈). + 섞어 두고 근거를 안 적으면, 나중에 「이 값이 왜 이런가」를 아무도 못 되짚는다. + +⚠⚠ **보간하지 않는다** + 관측값은 **그 규격에서만** 맞다. `반중력식 H=2.0` 의 콘크리트 1.35 ㎥/m 를 H=1.6 으로 + 줄여 쓰면 틀린다 — 기초·벽 두께는 높이에 비례하지 않는다. 규격이 표에 없으면 + **「원단위 미확보」로 드러낸다.** 가까운 값을 갖다 쓰는 길을 두지 않는다. + +⚠ 치수를 지어내지 않는다 + BOX암거는 `structures.json` 에 **벽·저판·상판 두께가 없어** 전개식조차 못 세운다. + 두께를 가정하면 그 값이 콘크리트·거푸집·철근으로 **번져 나간다**. 미확보로 낸다. + +⚠ 이중계상 규칙은 그대로다 + ㉢ 배합을 분해하지 않는다(콘크리트 ㎥·모르터 ㎥ 까지). ㉠ 할증은 자재총괄 한 곳뿐. + 터파기·되메우기·잔토는 `destination: earthwork` 로 토공에 합산된다. +""" + +from __future__ import annotations + +import json +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any + +DATASET_DIR = Path(__file__).resolve().parents[1] / "resources" / "data_structure_unit" +DATASET_PREFIX = "structure_unit_observed_" + +#: 값이 어디서 왔나 — 한 표에 섞이므로 줄마다 단다. +BASIS_DERIVED = "derived" # 저장된 치수에서 식으로 +BASIS_OBSERVED = "observed" # 실무 관측 원단위표에서 + +NOTE_UNIT_MISSING = "원단위 미확보" + + +def _latest_dataset_path(directory: Path | None = None) -> Path | None: + folder = directory or DATASET_DIR + if not folder.is_dir(): + return None + files = sorted(folder.glob(DATASET_PREFIX + "*.json")) + return files[-1] if files else None + + +def _same(left: Any, right: Any) -> bool: + """규격 한 칸 비교. 숫자는 값으로, 나머지는 글자로 **정확히** 본다. + + `"800"` 과 `800` 은 같게 보되(입력 폼이 문자열을 준다), `2.0` 과 `1.6` 은 다르다 — + 가까운 값을 같다고 보는 길은 두지 않는다. + """ + if isinstance(left, (int, float)) and not isinstance(left, bool): + try: + return abs(float(left) - float(right)) < 1e-9 + except (TypeError, ValueError): + return False + return str(left).strip() == str(right).strip() + + +@dataclass +class ObservedUnitTable: + """관측 원단위표 한 판.""" + + effective_date: str = "" + 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) + + def find(self, type_id: str, spec: dict[str, Any]) -> dict[str, Any] | None: + """규격이 **모두** 맞는 줄만 돌려준다. 하나라도 어긋나면 없는 것으로 본다.""" + for entry in self.entries: + if entry.get("type_id") != type_id: + continue + wanted = entry.get("spec") or {} + if all(key in spec and _same(value, spec[key]) for key, value in wanted.items()): + return entry + return None + + def specs_for(self, type_id: str) -> list[dict[str, Any]]: + """그 종류로 표에 있는 규격 목록 — 「무엇이 있는지」를 화면이 보이게.""" + return [ + entry.get("spec") or {} for entry in self.entries if entry.get("type_id") == type_id + ] + + +def load_observed_table(path: Path | None = None) -> ObservedUnitTable: + """관측 원단위표를 읽는다. 파일이 없으면 **빈 표** — 전부 「미확보」로 드러난다.""" + target = path or _latest_dataset_path() + if target is None or not target.is_file(): + return ObservedUnitTable() + payload = json.loads(target.read_text(encoding="utf-8")) + return ObservedUnitTable( + effective_date=str(payload.get("effective_date") or ""), + entries=list(payload.get("entries") or []), + sources=payload.get("sources") or {}, + not_found=payload.get("not_found") or {}, + ) + + +def scale_for(entry: dict[str, Any], structure: dict[str, Any]) -> tuple[float, str]: + """관측값에 곱할 수 — 단위가 `m` 면 연장, `㎡` 면 면적, `개소` 면 1. + + ⚠ **규격을 늘리는 것이 아니라 개수를 세는 것**이다. `H=2.0 옹벽 10m` 는 같은 단면이 + 10m 이어진 것이라 곱해도 되지만, `H=1.6` 으로 바꾸는 것은 단면이 달라지므로 안 된다. + """ + options = structure.get("options") or {} + unit = str(entry.get("unit") or "개소") + if unit == "m": + length = options.get("length_m") + if length is None: + start, end = structure.get("start_m"), structure.get("end_m") + length = ( + abs(float(end) - float(start)) if start is not None and end is not None else 0.0 + ) + return float(length or 0.0), f"연장 {float(length or 0.0):g} m" + if unit == "㎡": + width = options.get("ford_width_m") + length = options.get("length_m") or 0.0 + area = float(width or 0.0) * float(length or 0.0) + return area, f"면적 {area:g} ㎡" + return 1.0, "1 개소" + + +def expand_observed( + type_id: str, + spec: dict[str, Any], + structure: dict[str, Any], + table: ObservedUnitTable | None = None, +) -> tuple[list[dict[str, Any]], list[str]]: + """(성분 목록, 알림). 규격이 표에 없으면 **빈 목록 + 미확보 알림**을 낸다.""" + found = (table or load_observed_table()).find(type_id, spec) + if found is None: + known = (table or load_observed_table()).specs_for(type_id) + detail = f" — 표에 있는 규격: {known}" if known else "" + return [], [f"{NOTE_UNIT_MISSING} ({type_id} {spec}){detail}"] + + scale, scale_note = scale_for(found, structure) + if scale <= 0: + return [], [f"{NOTE_UNIT_MISSING} — 곱할 연장·면적이 0 ({type_id})"] + + source_key = str(found.get("source") or "") + components: list[dict[str, Any]] = [] + for item in found.get("components") or []: + note = str(item.get("basis_note") or "") + components.append( + { + "name": item["name"], + "unit": item["unit"], + "amount": float(item["amount"]) * scale, + "destination": item.get("destination") or "material", + # 근거를 값 옆에 붙인다 — 관측값임을 화면·인계에서 바로 알아야 한다. + "basis": f"관측 원단위 {item['amount']:g}/{found.get('unit')} × {scale_note}" + + (f" ({note})" if note else ""), + "basis_kind": BASIS_OBSERVED, + "source": source_key, + } + ) + return components, [f"관측 원단위 적용 — {found.get('source_note') or source_key}"] diff --git a/B08_Quantity/B08_Quantity_Engine_Preparation.py b/B08_Quantity/B08_Quantity_Engine_Preparation.py new file mode 100644 index 00000000..7a3bf0ae --- /dev/null +++ b/B08_Quantity/B08_Quantity_Engine_Preparation.py @@ -0,0 +1,138 @@ +"""준비공·사방공 — **자리를 만들되 없는 값을 지어내지 않는다** (B08 일감 ⑪ · PLAN 8-3). + +8-3 대응표에서 ❌ 로 남아 있던 둘이다. 여기서 하는 일은 **줄을 세우고, 설 수 있는 줄은 +값을 채우고, 못 서는 줄은 왜 못 서는지 적는 것**이다. 빈 표를 내면 「빠뜨린 것」과 +「원래 없는 것」이 구별되지 않는다. + +⚠⚠ 지장목제거와 겹치지 않는다 (이중계상) + 벌목·지장목제거는 **이미 토공집계의 사면 계열로 서 있다**(`tree_removal_*` × 반영률). + 여기서 또 세우면 같은 나무를 두 번 벤다. 그래서 준비공의 벌목 줄은 **값을 내지 않고 + 「토공집계 지장목제거로 이미 섬」이라고 가리키기만** 한다. + +⚠ 값이 없는 줄의 사유를 적는다 + · 표토제거(9-15) — 면적은 사면적에서 나오나 **두께·대상 구간이 설계로 안 정해져 있다**. + · 제근·뿌리다듬기(9-20~21) — 단위가 **「개」(그루 수)**인데 입목 본수를 우리가 안 든다. + · 규준틀(11-2) — **개소** 산정 기준(구조물·절성토 구간별 몇 개소)이 안 정해져 있다. + +⚠ 사방공은 이 노선에 실물이 없으면 「해당 없음」이다 + 있는 것처럼 0 을 적지 않는다. 구조물 목록에 사방 시설이 서면 그때 값이 선다. +""" + +from __future__ import annotations + +from typing import Any, Iterable + +#: 사방 시설로 보는 구조물 종류 — **레지스트리의 실제 `type_id` 를 쓴다**(D 그룹 + 흙막이). +#: 목록에 없으면 그 노선에 사방공이 **없는** 것이다. 이름을 지어내면 영영 안 걸린다. +EROSION_CONTROL_TYPES = frozenset( + { + "erosion_check", # 골막이 + "bed_sill", # 바닥막이 + "check_dam_small", # 소형사방댐(복합형) + "revetment", # 기슭막이 + "soil_guard", # 흙막이 + } +) + +STATUS_COUNTED_ELSEWHERE = "다른 표에서 이미 섬" +STATUS_PENDING = "값을 낼 근거가 없음" +STATUS_NOT_APPLICABLE = "해당 없음" +STATUS_READY = "값 있음" + + +def preparation_rows(slope_totals: dict[str, float] | None = None) -> list[dict[str, Any]]: + """준비공 줄 — 값이 서는 것과 안 서는 것을 **한 목록에** 낸다.""" + slope = slope_totals or {} + tree_area = float(slope.get("tree_removal_fill", 0.0)) + float( + slope.get("tree_removal_cut", 0.0) + ) + return [ + { + "group": "준비공", + "item": "벌목·지장목제거", + "unit": "㎡", + "amount": None, + "status": STATUS_COUNTED_ELSEWHERE, + # ⚠ 값을 여기서 또 내면 같은 나무를 두 번 벤다. 참고로 면적만 보인다. + "reference_amount": tree_area, + "reason": "토공집계의 「지장목제거」로 이미 섬 — 여기서 또 세우면 이중계상", + "work_item_code": None, + }, + { + "group": "준비공", + "item": "표토제거", + "unit": "㎥", + "amount": None, + "status": STATUS_PENDING, + "reason": "면적은 사면적에서 나오나 **두께·대상 구간**이 설계로 안 정해져 있음 (품셈 9-15)", + "work_item_code": "FP-09-15", + }, + { + "group": "준비공", + "item": "제근·뿌리다듬기", + "unit": "개", + "amount": None, + "status": STATUS_PENDING, + "reason": "단위가 「개」(그루 수)인데 입목 본수를 들고 있지 않음 (품셈 9-20~21)", + "work_item_code": "FP-09-21", + }, + { + "group": "준비공", + "item": "규준틀", + "unit": "개소", + "amount": None, + "status": STATUS_PENDING, + "reason": "개소 산정 기준(구조물·절성토 구간별 몇 개소)이 안 정해져 있음 (품셈 11-2)", + "work_item_code": "FP-11-02", + }, + ] + + +def erosion_rows(structures: Iterable[dict[str, Any]] = ()) -> list[dict[str, Any]]: + """사방공 줄 — 이 노선에 사방 시설이 **있을 때만** 값이 선다.""" + found = sorted( + { + str(item.get("type_id")) + for item in structures + if str(item.get("type_id")) in EROSION_CONTROL_TYPES + } + ) + if not found: + return [ + { + "group": "사방공", + "item": "사방 시설", + "unit": "", + "amount": None, + "status": STATUS_NOT_APPLICABLE, + "reason": "이 노선에 사방 시설이 배치돼 있지 않음 — 있는 것처럼 0 을 적지 않음", + "work_item_code": None, + } + ] + return [ + { + "group": "사방공", + "item": type_id, + "unit": "개소", + "amount": None, + "status": STATUS_PENDING, + "reason": "구조물 원단위가 아직 없음 — 전개식·관측값 모두 미확보", + "work_item_code": None, + } + for type_id in found + ] + + +def build_table( + slope_totals: dict[str, float] | None = None, + structures: Iterable[dict[str, Any]] = (), +) -> dict[str, Any]: + """화면·인계가 그대로 쓰는 모양. **못 서는 줄도 목록에 남는다.**""" + rows = preparation_rows(slope_totals) + erosion_rows(structures) + return { + "columns": ["구분", "공종", "단위", "수량", "상태", "사유"], + "rows": rows, + "ready_count": sum(1 for row in rows if row["status"] == STATUS_READY), + "pending_count": sum(1 for row in rows if row["status"] == STATUS_PENDING), + "row_count": len(rows), + } diff --git a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py index 1325f3d9..ce9ff1da 100644 --- a/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py +++ b/B08_Quantity/B08_Quantity_Engine_UnitQuantity.py @@ -34,6 +34,17 @@ import math from dataclasses import dataclass, field from typing import Any, Iterable +from B08_Quantity.B08_Quantity_Engine_ObservedUnit import ( + BASIS_DERIVED, + BASIS_OBSERVED, + ObservedUnitTable, + expand_observed, + load_observed_table, +) +from B08_Quantity.B08_Quantity_Engine_Formwork import annotate as annotate_formwork +from B08_Quantity.B08_Quantity_Engine_Formwork import shoring_status +from common_util.common_util_quantity_spread import spread_by_unit + # ── 계수표 — 식에 박지 않고 여기서 고른다 ───────────────────────────── # 돌 뒷길이(㎝)별 원단위. 출처: `original/실무문서/_원단위라이브러리_울진소광.md` 「돌뒷길이별 원단위표」. # ⚠ 60㎝ 돌중량은 원본이 비어 있다 — 지어내지 않고 None 으로 둔다(PLAN 8-8 ㉮). @@ -98,6 +109,10 @@ class Component: amount: float destination: str basis: str = "" + # ⚠ 값이 어디서 왔나 — `derived`(치수에서 식으로) / `observed`(실무 관측 원단위표). + # 두 근거가 한 표에 섞이므로 줄마다 단다. 안 적으면 나중에 못 되짚는다. + basis_kind: str = BASIS_DERIVED + source: str = "" @dataclass(slots=True) @@ -249,6 +264,16 @@ def stone_masonry( # 구조물 종류 → 전개식. 없는 종류는 전개하지 않고 이름만 남긴다(지어내지 않는다). +# ⚠ **관측 원단위표로 가는 종류** — 치수가 저장돼 있지 않아 전개식을 못 세우는 것들이다. +# 값의 키(규격)를 저장 제원의 어느 칸에서 읽는지 여기 적는다. 표에 규격이 없으면 +# 「원단위 미확보」로 드러난다 — 가까운 값을 갖다 쓰지 않는다. +OBSERVED_SPEC_KEYS: dict[str, tuple[str, ...]] = { + "retaining_wall": ("form", "height_m"), + "ford_pavement": ("thickness_cm",), + # 배수관의 유입부 집수정은 관 자체와 **다른 줄**이다 — 관은 관대로 서고 집수정이 따로 선다. + "pipe_inlet_basin": ("inlet_basin_form", "inlet_basin_material", "pipe_diameter_mm"), +} + 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), @@ -256,7 +281,55 @@ EXPANDERS = { } -def expand(structure: dict[str, Any], names: dict[str, str] | None = None) -> StructureQuantity: +#: 한 구조물이 **여러 내역 줄**을 낳는 자리. 배수관은 관 자체와 유입부 집수정이 따로 선다 +#: (품셈도 관부설과 집수정을 다른 공종으로 둔다). 한 줄로 합치면 어느 쪽 물량인지 못 가른다. +ATTACHMENTS: dict[str, tuple[tuple[str, str, str], ...]] = { + # (붙는 종류, 그것이 있는지 보는 옵션 칸, 줄 이름 꼬리) + "pipe": (("pipe_inlet_basin", "inlet_basin_form", "유입부 집수정"),), +} + + +def attachments_of(structure: dict[str, Any]) -> list[dict[str, Any]]: + """구조물에 딸린 **별도 줄**을 만든다. 제원은 원본을 그대로 물려준다(치수 두 벌 금지).""" + rows: list[dict[str, Any]] = [] + options = structure.get("options") or {} + for type_id, gate_key, label in ATTACHMENTS.get(str(structure.get("type_id") or ""), ()): + if not options.get(gate_key): + continue # 그 부속이 없는 배치다 — 빈 줄을 만들지 않는다 + rows.append( + { + **structure, + "structure_id": f"{structure.get('structure_id')}-{type_id}", + "type_id": type_id, + "attachment_of": structure.get("structure_id"), + "attachment_parent_type": structure.get("type_id"), + "attachment_label": label, + } + ) + return rows + + +def _observed_components( + type_id: str, + structure: dict[str, Any], + observed: ObservedUnitTable | None, +) -> tuple[list[dict[str, Any]], list[str]]: + """관측 원단위표에서 꺼낸다. 규격 키가 정해져 있지 않은 종류는 건드리지 않는다.""" + keys = OBSERVED_SPEC_KEYS.get(type_id) + if keys is None: + return [], [] + options = structure.get("options") or {} + spec = {key: options[key] for key in keys if options.get(key) is not None} + if not spec: + return [], [f"{type_id} 규격이 비어 있음 — 관측 원단위를 고를 수 없음"] + return expand_observed(type_id, spec, structure, observed) + + +def expand( + structure: dict[str, Any], + names: dict[str, str] | None = None, + observed: ObservedUnitTable | None = None, +) -> StructureQuantity: """구조물 하나를 전개한다. 치수는 저장된 제원에서만 읽는다(치수 두 벌 금지).""" type_id = str(structure.get("type_id") or "") options = structure.get("options") or {} @@ -264,10 +337,15 @@ def expand(structure: dict[str, Any], names: dict[str, str] | None = None) -> St end = _num(structure.get("end_m")) length = _num(options.get("length_m")) or abs(end - start) height = _num(options.get("height_m")) + label = (names or {}).get(type_id, type_id) + if structure.get("attachment_label"): + # 「배수관 · 유입부 집수정」처럼 어디에 딸린 줄인지 이름에 남긴다. + parent = (names or {}).get(str(structure.get("attachment_parent_type") or ""), "") + label = f"{parent or label} · {structure['attachment_label']}".strip(" ·") result = StructureQuantity( structure_id=structure.get("structure_id"), type_id=type_id, - name=(names or {}).get(type_id, type_id), + name=label, length_m=length, height_m=height, start_m=start if structure.get("start_m") is not None else None, @@ -275,6 +353,12 @@ def expand(structure: dict[str, Any], names: dict[str, str] | None = None) -> St ) expander = EXPANDERS.get(type_id) if expander is None: + # 전개식이 없으면 **관측 원단위표**를 본다(치수가 저장돼 있지 않은 종류). + components, notes = _observed_components(type_id, structure, observed) + if components or notes: + result.components = [Component(**item) for item in components] + result.notes.extend(notes) + return result result.notes.append(f"'{type_id}' 전개식이 아직 없음 — 물량을 내지 않음") return result result.components, notes = expander(height, length, options) @@ -300,7 +384,13 @@ def build_table( structures: Iterable[dict[str, Any]], names: dict[str, str] | None = None ) -> dict[str, Any]: """화면·API 가 그대로 쓰는 모양. 성분별 총량과 구조물별 내역을 함께 낸다.""" - quantities = [expand(item, names) for item in structures] + observed = load_observed_table() + # 딸린 줄(배수관의 유입부 집수정 등)을 원본 뒤에 세운다 — 한 줄로 합치지 않는다. + expanded_inputs: list[dict[str, Any]] = [] + for item in structures: + expanded_inputs.append(item) + expanded_inputs.extend(attachments_of(item)) + quantities = [expand(item, names, observed) for item in expanded_inputs] violations = verify_no_mix_components(quantities) totals: dict[str, dict[str, Any]] = {} @@ -318,33 +408,44 @@ def build_table( ) entry["amount"] += component.amount + payload_structures = [ + { + "structure_id": item.structure_id, + "type_id": item.type_id, + "name": item.name, + "length_m": item.length_m, + "height_m": item.height_m, + "start_m": item.start_m, + "end_m": item.end_m, + "notes": item.notes, + "components": [ + { + "name": component.name, + "unit": component.unit, + "amount": component.amount, + "destination": component.destination, + "basis": component.basis, + "basis_kind": component.basis_kind, + "source": component.source, + } + for component in item.components + ], + } + for item in quantities + ] + # 거푸집 줄에 **몇 회짜리인지**를 달아 준다. 횟수별 재료 환산은 하지 않는다(B09 몫). + formwork_notes, formwork_missing = annotate_formwork(payload_structures) + return { - "structures": [ - { - "structure_id": item.structure_id, - "type_id": item.type_id, - "name": item.name, - "length_m": item.length_m, - "height_m": item.height_m, - "start_m": item.start_m, - "end_m": item.end_m, - "notes": item.notes, - "components": [ - { - "name": component.name, - "unit": component.unit, - "amount": component.amount, - "destination": component.destination, - "basis": component.basis, - } - for component in item.components - ], - } - for item in quantities - ], + "structures": payload_structures, + "formwork_notes": formwork_notes, + "formwork_reuse_missing": formwork_missing, + # 동바리 — 대상이 없으면 0 이 아니라 「없음」이라고 말한다. + "shoring": shoring_status(), "totals": sorted(totals.values(), key=lambda entry: entry["name"]), # 할증 전 값임을 응답에 못 박는다 — 자재총괄이 한 번만 붙인다(㉠). "surcharge_applied": False, "mix_components_found": violations, "structure_count": len(quantities), + "amount_spread": spread_by_unit(totals.values(), value_key="amount"), } diff --git a/B08_Quantity/B08_Quantity_Router_Earthwork.py b/B08_Quantity/B08_Quantity_Router_Earthwork.py index e9b9b314..a99b8f81 100644 --- a/B08_Quantity/B08_Quantity_Router_Earthwork.py +++ b/B08_Quantity/B08_Quantity_Router_Earthwork.py @@ -31,10 +31,12 @@ from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import SummaryInput from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import build_table as build_summary_table from B08_Quantity.B08_Quantity_Engine_EarthworkTable import StationArea, build_table from B08_Quantity.B08_Quantity_Engine_HaulSummary import build_table as build_haul_table +from B08_Quantity.B08_Quantity_Engine_Preparation import build_table as build_preparation_table from B08_Quantity.B08_Quantity_Engine_HaulSummary import summary_input_rows from B08_Quantity.B08_Quantity_Engine_SlopeArea import build_table as build_slope_table from B08_Quantity.B08_Quantity_Engine_SlopeLength import station_slopes from common_util.common_util_project_settings import ( + ROCK_METHODS, application_ratio, quantity_settings, rock_classes, @@ -93,12 +95,30 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse: }, ) ) + # 준비공·사방공 — 못 서는 줄도 사유와 함께 남긴다(빈 표는 「빠뜨림」과 구별이 안 됨). + table["preparation"] = build_preparation_table( + slope.get("totals") or {}, await _route_structures(project_id) + ) table["settings"] = settings table["project_root_known"] = project_root is not None table["route_id"] = route_id return JSONResponse(content=table) +async def _route_structures(project_id: UUID) -> list[dict[str, Any]]: + """배치된 구조물 목록 — 사방 시설이 있는지 보려는 것뿐이다. 없으면 빈 목록.""" + try: + stored_path = await run_with_connection(get_project_storage_relative_path, project_id) + root = resolve_stored_project_path(stored_path) + from B05_Profile.B05_Profile_Structures_Repository import load_structures + + _revision, items = load_structures(root) + return [item.model_dump() for item in items] + except Exception: + logger.warning("B08 준비공 — 구조물 목록을 못 읽음: project_id=%s", project_id) + return [] + + async def _project_settings(project_id: UUID) -> tuple[dict[str, Any], str | None]: """프로젝트 설정을 읽는다. 경로를 못 찾아도 기본값으로 화면은 선다.""" try: @@ -113,14 +133,22 @@ async def _project_settings(project_id: UUID) -> tuple[dict[str, Any], str | Non async def _stored_haul_plan(project_id: UUID, route_id: int) -> dict[str, Any] | None: - """정본에 남은 운반계획. [확정]을 아직 안 돌렸으면 없다.""" + """정본에 남은 **배분**(`mass_haul.haul_plan`). [확정]을 아직 안 돌렸으면 없다. + + ⚠ 정본에 저장되는 것은 유토곡선 한 벌(`mass_haul`)이고 **배분은 그 안의 `haul_plan`** 이다. + 바깥 껍데기를 그대로 넘기면 `blocks` 를 못 찾아 **운반 표가 영영 0줄**이 된다 — + [확정] 전에는 어차피 빈 표라 화면에서 티가 안 나던 자리다(2026-09-07 실증에서 잡음). + """ try: row = await run_with_connection(get_longitudinal_section, project_id, route_id) except Exception: logger.exception("B08 운반계획 조회 실패: route_id=%s", route_id) return None data = (row or {}).get("data") or {} - plan = data.get("mass_haul") if isinstance(data, dict) else None + mass_haul = data.get("mass_haul") if isinstance(data, dict) else None + if not isinstance(mass_haul, dict): + return None + plan = mass_haul.get("haul_plan") return plan if isinstance(plan, dict) and plan else None @@ -130,7 +158,12 @@ class QuantitySettingsBody(BaseModel): rock_class_set: str | None = None rock_classes: list[str] | None = None rock_ratios_pct: dict[str, float] | None = None + # 갈래별 시공법 — 값은 "ripping"·"blasting". 안 정한 갈래는 보내지 않는다. + rock_methods: dict[str, str] | None = None application_ratios_pct: dict[str, float] | None = None + # 자재별 관급/사급 — `{자재명: {"supply": …, "install_by": …}}`. + # 표 안에서 줄마다 고른 값이 여기로 온다(2026-09-07 확정). + material_supply: dict[str, Any] | None = None @router.put("/{project_id}/quantity/settings") @@ -150,8 +183,20 @@ async def save_quantity_settings(project_id: UUID, body: QuantitySettingsBody) - content={"status": "error", "message": "프로젝트 저장 폴더를 찾지 못했습니다."}, ) values = {key: value for key, value in body.model_dump().items() if value is not None} + if "rock_methods" in values: + # 「안 정함」(빈 값)은 저장하지 않는다 — 정한 것과 구별이 안 된다. 통째로 갈아 끼우므로 + # 여기서 버리면 그 갈래는 미지정으로 돌아간다. + values["rock_methods"] = { + name: method + for name, method in values["rock_methods"].items() + if method in ROCK_METHODS + } try: - saved = await asyncio.to_thread(save_section, root, "quantity", values) + # ⚠ 고른 값을 **되돌릴 수 있어야** 하는 칸은 통째로 갈아 끼운다 — 병합이면 + # 「안 정함」으로 되돌아가지 않는다(2026-09-07 화면에서 걸린 자리). + saved = await asyncio.to_thread( + _save_quantity, root, values, ("rock_methods", "material_supply") + ) except Exception: logger.exception("B08 설정 저장 실패(쓰기): project_id=%s", project_id) return JSONResponse( @@ -161,6 +206,12 @@ async def save_quantity_settings(project_id: UUID, body: QuantitySettingsBody) - return JSONResponse(content={"status": "success", "quantity": saved.get("quantity") or {}}) +def _save_quantity( + root: str, values: dict[str, Any], replace_keys: tuple[str, ...] +) -> dict[str, Any]: + return save_section(root, "quantity", values, replace_keys=replace_keys) + + @router.get("/{project_id}/quantity/earthwork-table") async def get_earthwork_table_for_current_route(project_id: UUID) -> JSONResponse: """경로를 안 주면 워크플로가 보고 있는 경로로 낸다 — 화면이 route_id 를 모를 때 쓴다.""" diff --git a/B08_Quantity/B08_Quantity_Router_Material.py b/B08_Quantity/B08_Quantity_Router_Material.py index c61790cb..53c2b928 100644 --- a/B08_Quantity/B08_Quantity_Router_Material.py +++ b/B08_Quantity/B08_Quantity_Router_Material.py @@ -28,7 +28,11 @@ from B05_Profile.B05_Profile_Structures_Schema import structure_type_map from B08_Quantity.B08_Quantity_Engine_Handoff import build_handoff, summarize from B08_Quantity.B08_Quantity_Engine_MaterialSummary import build_table as build_material_table from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table as build_unit_table -from common_util.common_util_project_settings import quantity_settings, rock_classes +from common_util.common_util_project_settings import ( + quantity_settings, + rock_classes, + rock_method, +) from common_util.common_util_storage import resolve_stored_project_path from config.config_db import run_with_connection @@ -143,6 +147,7 @@ async def get_handoff(project_id: UUID) -> JSONResponse: material_table=material_table, ground_class_set=settings.get("rock_class_set"), ground_classes=rock_classes(settings), + ground_methods={name: rock_method(settings, name) for name in rock_classes(settings)}, ) handoff["summary"] = summarize(handoff) handoff["skipped_structures"] = skipped diff --git a/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts b/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts index 87b97a84..ade6b38f 100644 --- a/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts +++ b/B08_Quantity/B08_Quantity_UI_EarthworkGrid.ts @@ -59,6 +59,10 @@ export interface QuantitySettings { rock_classes?: string[]; rock_ratios_pct?: Record; application_ratios_pct?: Record; + /** 갈래별 시공법 — `"ripping"`·`"blasting"`. 안 정한 갈래는 아예 없다. */ + rock_methods?: Record; + /** 자재별 관급/사급 — 표 안에서 줄마다 고른 값. */ + material_supply?: Record; } export interface EarthworkTable { @@ -188,10 +192,6 @@ const PAIR_LABELS = ["단면적", "입 적"]; const flatColumns = (): Column[] => GROUPS.flatMap((g) => g.sub.flatMap((s) => s.cols)); -/** 사면 열 개수 — 계열마다 (거리, 면적) 두 칸. */ -const slopeColumnCount = (): number => - SLOPE_GROUPS.reduce((n, group) => n + group.faces.length * 2, 0); - /** 측점 표기 — `20` → `NO.1`, `25` → `NO.1+5`. 실무 토적표가 이 모양이다. */ function stationLabel(chainage: number, interval = 20): string { const no = Math.floor(chainage / interval); diff --git a/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts b/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts index 9e06c426..c3d9dcbb 100644 --- a/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts +++ b/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts @@ -13,6 +13,25 @@ const STYLE_ID = "b08-earthwork-grid-style"; const CSS = ` .b08-grid { display: flex; flex-direction: column; gap: 8px; min-width: 0; height: 100%; } +/* 표 안에서 고르는 칸 — 관급/사급처럼 **줄마다 갈리는 값**을 여기서 정한다. */ +.b08-grid__select { + width: 100%; + min-width: 5.5rem; + padding: 0.15rem 0.25rem; + font: inherit; + color: var(--color-text); + background: var(--color-surface-raised); + border: 1px solid var(--color-border, rgba(128, 128, 128, 0.4)); + border-radius: 3px; +} +.b08-grid__select:disabled { + opacity: 0.45; /* 사급 줄의 설치 주체 — 뜻이 없으므로 흐리게 둔다 */ +} +/* 만진 줄은 표시가 남는다 — 무엇을 바꿨는지 보여야 한다. */ +.b08-grid__table td.is-changed { + box-shadow: inset 2px 0 0 var(--color-accent, #6c8ebf); +} + .b08-grid__caption { margin: 0; font-size: 12px; diff --git a/B08_Quantity/B08_Quantity_UI_MaterialGrid.ts b/B08_Quantity/B08_Quantity_UI_MaterialGrid.ts index bb301fd8..3da8cfa0 100644 --- a/B08_Quantity/B08_Quantity_UI_MaterialGrid.ts +++ b/B08_Quantity/B08_Quantity_UI_MaterialGrid.ts @@ -33,6 +33,7 @@ export interface MaterialTable { missing_rate_materials: string[]; missing_supply_materials: string[]; missing_install_by_materials: string[]; + amount_spread: Record; double_count_warnings: string[]; skipped_by_destination: Record; row_count: number; @@ -51,6 +52,12 @@ export interface UnitQuantityStructure { amount: number; destination: string; basis: string; + /** 값이 어디서 왔나 — `derived`(치수에서 식으로) / `observed`(실무 관측 원단위표). */ + basis_kind?: string; + source?: string; + /** 거푸집 줄만 — 몇 회짜리인가(품셈 1-7-1). 횟수별 재료 환산은 B09 몫이다. */ + reuse_count?: number | null; + reuse_note?: string; }[]; } @@ -67,6 +74,13 @@ export interface MaterialResponse { structure_count: number; } +/** 거푸집·동바리 안내에 쓰는 값. */ +export interface FormworkInfo { + formwork_notes?: string[]; + formwork_reuse_missing?: string[]; + shoring?: { applicable: boolean; reason: string; pending_types: string[] }; +} + /** 성분이 어디로 가는지 — 화면에서도 보이게 한다. 규칙이 코드에만 있으면 잊힌다. */ const DESTINATION_LABELS: Record = { earthwork: "토공 합산", @@ -110,8 +124,78 @@ function warning(title: string, items: string[]): HTMLElement | null { return element; } +/** 관급/사급 고르는 칸의 보기. **값은 영문 키, 표기는 한글**(B09 와 같은 낱말). */ +const SUPPLY_OPTIONS = [ + { value: "unknown", label: "미분류" }, + { value: "contractor_supplied", label: "사급" }, + { value: "owner_supplied", label: "관급" }, +]; +const INSTALL_BY_OPTIONS = [ + { value: "", label: "미지정" }, + { value: "contractor", label: "도급자설치" }, + { value: "owner", label: "관 직접설치" }, +]; + +export interface SupplyChoice { + supply: string; + install_by: string | null; +} + +export interface MaterialGridOptions { + /** 저장 전 변경분 — 고른 값은 여기 쌓이고 [저장]에서만 정본으로 간다. */ + choices: Record; + onChange: () => void; +} + +/** 표 안의 고르는 칸. 바꾼 줄은 **표시가 남는다** — 무엇을 만졌는지 보여야 한다. */ +function choiceCell( + value: string, + options: { value: string; label: string }[], + disabled: boolean, + onChange: (value: string) => void, +): HTMLTableCellElement { + const td = document.createElement("td"); + const select = document.createElement("select"); + select.className = "b08-grid__select"; + for (const option of options) { + const element = document.createElement("option"); + element.value = option.value; + element.textContent = option.label; + select.append(element); + } + select.value = value; + select.disabled = disabled; + select.addEventListener("change", () => { + onChange(select.value); + td.classList.add("is-changed"); + }); + td.append(select); + return td; +} + +/** 값의 크기 요약 — 자릿수가 어긋난 것은 사람이 훑어야 보인다. */ +function spreadLine(spread: MaterialTable["amount_spread"], title: string): HTMLElement | null { + const units = Object.keys(spread || {}); + if (!units.length) return null; + const element = document.createElement("p"); + element.className = "b08-grid__caption"; + element.textContent = + title + + " " + + units + .map((unit) => { + const s = spread[unit]; + return `${unit} 최소 ${num(s.min, 2)} · 중앙 ${num(s.median, 2)} · 최대 ${num(s.max, 2)}`; + }) + .join(" / "); + return element; +} + /** 자재총괄표 — 할증이 붙는 유일한 자리. */ -export function renderMaterialGrid(table: MaterialTable): HTMLElement { +export function renderMaterialGrid( + table: MaterialTable, + options?: MaterialGridOptions, +): HTMLElement { const wrap = document.createElement("div"); wrap.className = "b08-grid"; @@ -121,6 +205,9 @@ export function renderMaterialGrid(table: MaterialTable): HTMLElement { caption.textContent = `자재 ${table.row_count}종 · 할증률 ${edition} 판 적용 · 금액은 원가계산(B09)에서`; wrap.append(caption); + const spread = spreadLine(table.amount_spread, "물량 크기:"); + if (spread) wrap.append(spread); + for (const notice of [ warning("⚠ 중복 할증 위험", table.double_count_warnings), warning("할증률 미확보", table.missing_rate_materials), @@ -153,8 +240,46 @@ export function renderMaterialGrid(table: MaterialTable): HTMLElement { // 미확보는 빈칸이 아니라 「-」 — 빈칸이면 0 % 로 오해된다. tr.append(textCell(row.surcharge_pct === null ? "-" : num(row.surcharge_pct, 0))); tr.append(textCell(num(row.total_amount, 2))); - tr.append(textCell(row.supply_label)); - tr.append(textCell(row.install_by_label)); + if (options) { + // 관급/사급은 **자재마다 갈리는 발주 결정**이라 줄에서 고른다(2026-09-07 확정). + const chosen = options.choices[row.name] ?? { + supply: row.supply, + install_by: row.install_by, + }; + const installCell = choiceCell( + chosen.install_by ?? "", + INSTALL_BY_OPTIONS, + chosen.supply !== "owner_supplied", // 관급 줄에만 고를 수 있다 + (value) => { + const current = options.choices[row.name] ?? chosen; + options.choices[row.name] = { supply: current.supply, install_by: value || null }; + options.onChange(); + }, + ); + tr.append( + choiceCell(chosen.supply, SUPPLY_OPTIONS, false, (value) => { + const current = options.choices[row.name] ?? chosen; + const next = { + // 사급으로 되돌리면 설치 주체는 뜻을 잃으므로 비운다. + supply: value, + install_by: value === "owner_supplied" ? (current.install_by ?? null) : null, + }; + options.choices[row.name] = next; + // ⚠ 표를 다시 그리지 않으므로 **여기서 바로 열고 닫는다** — 안 그러면 관급을 골라도 + // 설치 주체 칸이 잠긴 채 남아 사용자가 못 정한다(만들고 화면에서 걸린 자리). + const select = installCell.querySelector("select") as HTMLSelectElement | null; + if (select) { + select.disabled = value !== "owner_supplied"; + select.value = next.install_by ?? ""; + } + options.onChange(); + }), + ); + tr.append(installCell); + } else { + tr.append(textCell(row.supply_label)); + tr.append(textCell(row.install_by_label)); + } tr.append(textCell(row.note, "b08-grid__note")); body.append(tr); } @@ -182,6 +307,20 @@ export function renderUnitQuantityGrid(response: MaterialResponse): HTMLElement const skipped = warning("건너뛴 구조물", response.skipped_structures); if (skipped) wrap.append(skipped); + // 거푸집 사용횟수 — 값이 아니라 **몇 회짜리인지**를 알려 주는 자리(품셈 1-7-1). + const info = unit as unknown as FormworkInfo; + const reuse = warning("거푸집 사용횟수", info.formwork_notes ?? []); + if (reuse) wrap.append(reuse); + const reuseMissing = warning("사용횟수 미확보", info.formwork_reuse_missing ?? []); + if (reuseMissing) wrap.append(reuseMissing); + if (info.shoring && !info.shoring.applicable) { + // 「없음」을 0 으로 적지 않는다 — 대상이 없는 것과 값이 0 인 것은 다르다. + const line = document.createElement("p"); + line.className = "b08-grid__caption"; + line.textContent = `동바리: 대상 없음 — ${info.shoring.reason.replace(/\*\*/g, "")}`; + wrap.append(line); + } + if (!unit.structures.length) { const empty = document.createElement("p"); empty.className = "b08-quantity__message"; @@ -194,7 +333,7 @@ export function renderUnitQuantityGrid(response: MaterialResponse): HTMLElement scroller.className = "b08-grid__scroll"; const element = document.createElement("table"); element.className = "b08-grid__table b08-grid__table--summary"; - element.append(headRow(["구조물", "규격", "성분", "단위", "수량", "갈 곳", "근거"])); + element.append(headRow(["구조물", "규격", "성분", "단위", "수량", "갈 곳", "근거", "출처"])); const body = document.createElement("tbody"); for (const structure of unit.structures) { @@ -221,6 +360,10 @@ export function renderUnitQuantityGrid(response: MaterialResponse): HTMLElement tr.append(textCell(num(component.amount, 3))); tr.append(textCell(DESTINATION_LABELS[component.destination] ?? component.destination)); tr.append(textCell(component.basis, "b08-grid__note")); + // ⚠ 식에서 나온 값과 실무 관측값이 한 표에 섞인다 — 어느 쪽인지 화면에서 보여야 + // 나중에 「이 값이 왜 이런가」를 되짚을 수 있다. + const kind = component.basis_kind === "observed" ? "실무 관측" : "치수 전개"; + tr.append(textCell(component.reuse_count ? `${kind} · ${component.reuse_count}회` : kind)); body.append(tr); } } diff --git a/B08_Quantity/B08_Quantity_UI_Page.ts b/B08_Quantity/B08_Quantity_UI_Page.ts index e9e7b040..65dbbb1c 100644 --- a/B08_Quantity/B08_Quantity_UI_Page.ts +++ b/B08_Quantity/B08_Quantity_UI_Page.ts @@ -19,7 +19,12 @@ import { } from "../A00_Common/b_workflow_nav"; import { renderEarthworkGrid, type EarthworkTable } from "./B08_Quantity_UI_EarthworkGrid"; import { injectEarthworkGridStyles } from "./B08_Quantity_UI_EarthworkGrid_Style"; -import { renderHaulGrid, renderSummaryGrid } from "./B08_Quantity_UI_SummaryGrid"; +import { + renderHaulGrid, + renderPreparationGrid, + renderSummaryGrid, + type PreparationTable, +} from "./B08_Quantity_UI_SummaryGrid"; import { renderMaterialGrid, renderUnitQuantityGrid, @@ -74,6 +79,10 @@ async function saveQuantitySettings(projectId: string, draft: DraftSettings): Pr rock_class_set: draft.rock_class_set ?? null, rock_ratios_pct: draft.rock_ratios_pct, application_ratios_pct: draft.application_ratios_pct, + // ⚠ 「안 정함」으로 되돌린 갈래까지 **통째로** 보낸다. 정한 것만 보내면 서버가 + // 병합해 옛 값이 남아 되돌릴 길이 없다(화면에서 걸린 자리). 빈 값은 서버가 버린다. + rock_methods: draft.rock_methods, + material_supply: draft.material_supply, }), }, ); @@ -124,11 +133,47 @@ function numberField(label: string, value: number, onInput: (value: number) => v return row; } +/** 고르는 칸. 첫 보기는 **「안 정함」**이고 그것이 기본이다 — 찍으면 값이 조용히 틀린다. */ +function selectField( + label: string, + value: string, + options: { value: string; label: string }[], + onChange: (value: string) => void, +): HTMLElement { + const row = document.createElement("label"); + row.className = "b08-quantity__field"; + const name = document.createElement("span"); + name.textContent = label; + const select = document.createElement("select"); + select.className = "b08-quantity__input"; + for (const option of options) { + const element = document.createElement("option"); + element.value = option.value; + element.textContent = option.label; + select.append(element); + } + select.value = value; + // 자동저장은 만들지 않는다 — 고른 값은 캐시에만 남는다(CLAUDE.md 5장). + select.addEventListener("change", () => onChange(select.value)); + row.append(name, select); + return row; +} + +/** 자재 한 줄의 관급/사급. `install_by` 는 **관급 줄에만** 뜻이 있다. */ +export interface SupplyChoice { + supply: string; + install_by: string | null; +} + /** 저장 전 변경분 — 조작은 여기 쌓이고 [저장]에서만 정본으로 간다. */ interface DraftSettings { rock_class_set?: string; rock_ratios_pct: Record; application_ratios_pct: Record; + // 갈래별 시공법 — `""` 는 「안 정함」이고 저장에서 빠진다. + rock_methods: Record; + // 자재별 관급/사급 — 표 안에서 줄마다 고른 값. + material_supply: Record; dirty: boolean; } @@ -154,16 +199,42 @@ function buildQuantitySidePanel( } // ── 지반 구성비 — 갈래 수는 프로젝트 세트가 정한다(코드에 안 박음, PLAN 8-13) ── - const classes = table?.summary?.rock_classes ?? []; + const classes = [...(table?.summary?.rock_classes ?? [])]; + // ⚠ 비율을 아직 안 넣었으면 집계가 **「암」 한 줄**로 나온다(갈래로 안 갈림). 그 줄에도 + // 시공법을 정할 수 있어야 공종이 선다 — 그때만 칸을 하나 더 낸다. + const hasRockFallback = (table?.summary?.rows ?? []).some((row) => row.item === "암"); + if (hasRockFallback && !classes.includes("암")) classes.push("암"); if (classes.length) { panel.append(field(L("B08_Quantity_Side_RockRatios"), "")); for (const name of classes) { - panel.append( - numberField(name, draft.rock_ratios_pct[name] ?? 0, (value) => { - draft.rock_ratios_pct[name] = value; - draft.dirty = true; - }), - ); + // 「암」은 비율을 넣으면 사라지는 되메움 줄이라 비율 칸을 두지 않는다. + if (name !== "암") { + panel.append( + numberField(name, draft.rock_ratios_pct[name] ?? 0, (value) => { + draft.rock_ratios_pct[name] = value; + draft.dirty = true; + }), + ); + } + // ⚠ 암 갈래는 **시공법까지 정해야** 공종이 갈린다 — 품셈이 긁어내기(암절취)와 + // 터뜨리기(발파암)를 다른 공종으로 두기 때문이다. 「토사」에는 안 붙인다. + if (name !== "토사") { + panel.append( + selectField( + ` ${name} ${L("B08_Quantity_Side_Method_Label")}`, + draft.rock_methods[name] ?? "", + [ + { value: "", label: L("B08_Quantity_Method_Unset") }, + { value: "ripping", label: L("B08_Quantity_Method_Ripping") }, + { value: "blasting", label: L("B08_Quantity_Method_Blasting") }, + ], + (value) => { + draft.rock_methods[name] = value; + draft.dirty = true; + }, + ), + ); + } } } @@ -183,7 +254,7 @@ function buildQuantitySidePanel( const saveButton = createButton({ label: L("B08_Quantity_Btn_Save"), - variant: "outlined", + variant: "ghost", onClick: () => { if (!projectId) { showToast(L("B08_Quantity_Save_Failed"), "error"); @@ -240,6 +311,7 @@ function buildQuantityBody( table: EarthworkTable | null, failed: boolean, material: MaterialResponse | null, + draft: DraftSettings, ): HTMLElement { const body = document.createElement("div"); body.className = "b08-quantity__body"; @@ -279,6 +351,15 @@ function buildQuantityBody( ? renderHaulGrid(table.haul, Boolean(table.haul_available)) : message(L("B08_Quantity_Haul_Missing")), }, + { + label: L("B08_Quantity_Tab_Preparation"), + build: () => { + const preparation = (table as unknown as { preparation?: PreparationTable }).preparation; + return preparation + ? renderPreparationGrid(preparation) + : message(L("B08_Quantity_Grid_Empty")); + }, + }, { label: L("B08_Quantity_Tab_UnitQuantity"), build: () => @@ -288,7 +369,12 @@ function buildQuantityBody( label: L("B08_Quantity_Tab_Material"), build: () => material - ? renderMaterialGrid(material.material) + ? renderMaterialGrid(material.material, { + choices: draft.material_supply, + onChange: () => { + draft.dirty = true; + }, + }) : message(L("B08_Quantity_Material_Failed")), }, ]; @@ -344,6 +430,8 @@ export async function renderB08Quantity(root: HTMLElement): Promise { rock_class_set: stored.rock_class_set, rock_ratios_pct: { ...(stored.rock_ratios_pct ?? {}) }, application_ratios_pct: { ...(stored.application_ratios_pct ?? {}) }, + rock_methods: { ...((stored.rock_methods ?? {}) as Record) }, + material_supply: { ...((stored.material_supply ?? {}) as Record) }, dirty: false, }; const reload = (): void => { @@ -372,7 +460,7 @@ export async function renderB08Quantity(root: HTMLElement): Promise { steps: workflowSteps(), activeStep: 5, leftPanel: buildQuantitySidePanel(projectId, table, draft, reload), - mainContent: buildQuantityBody(table, failed, material), + mainContent: buildQuantityBody(table, failed, material, draft), stages: workflowState?.stages, currentStage: workflowState?.current_stage, routes: WORKFLOW_STEP_ROUTES, diff --git a/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts b/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts index 317784e5..b4c03e32 100644 --- a/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts +++ b/B08_Quantity/B08_Quantity_UI_SummaryGrid.ts @@ -188,3 +188,74 @@ export function renderHaulGrid(table: HaulTable, available: boolean): HTMLElemen wrap.append(scroller); return wrap; } + +export interface PreparationRow { + group: string; + item: string; + unit: string; + amount: number | null; + status: string; + reason: string; + reference_amount?: number; + work_item_code: string | null; +} + +export interface PreparationTable { + columns: string[]; + rows: PreparationRow[]; + pending_count: number; + row_count: number; +} + +/** 준비공·사방공 — **못 서는 줄도 보인다.** + * + * 빈 표를 내면 「빠뜨린 것」과 「원래 없는 것」이 구별되지 않는다. 그래서 값이 없는 줄도 + * 상태와 사유를 달아 그대로 세운다. + */ +export function renderPreparationGrid(table: PreparationTable): HTMLElement { + const wrap = document.createElement("div"); + wrap.className = "b08-grid"; + + const caption = document.createElement("p"); + caption.className = "b08-grid__caption"; + caption.textContent = `${table.row_count}줄 · 값을 낼 근거가 아직 없는 줄 ${table.pending_count}개`; + wrap.append(caption); + + const scroller = document.createElement("div"); + scroller.className = "b08-grid__scroll"; + const element = document.createElement("table"); + element.className = "b08-grid__table b08-grid__table--summary"; + + const head = document.createElement("thead"); + const headRow = document.createElement("tr"); + for (const label of table.columns) { + const th = document.createElement("th"); + th.textContent = label; + headRow.append(th); + } + head.append(headRow); + + const body = document.createElement("tbody"); + let lastGroup = ""; + for (const row of table.rows) { + const tr = document.createElement("tr"); + tr.append(textCell(row.group === lastGroup ? "" : row.group, "b08-grid__station")); + lastGroup = row.group; + tr.append(textCell(row.item)); + tr.append(textCell(row.unit, "b08-grid__unit")); + // 값이 없으면 빈칸이 아니라 「-」 — 빈칸이면 0 으로 오해된다. + tr.append(textCell(row.amount === null ? "-" : num(row.amount, 2))); + tr.append(textCell(row.status)); + const note = textCell(row.reason.replace(/\*\*/g, ""), "b08-grid__note"); + if (row.reference_amount) { + note.append(document.createTextNode(` (참고 면적 ${num(row.reference_amount, 1)})`)); + } + tr.append(note); + body.append(tr); + } + + element.append(head, body); + scroller.append(element); + wrap.append(scroller); + return wrap; +} diff --git a/common_util/common_util_project_settings.py b/common_util/common_util_project_settings.py index c9099467..c274cf4d 100644 --- a/common_util/common_util_project_settings.py +++ b/common_util/common_util_project_settings.py @@ -33,7 +33,7 @@ from __future__ import annotations import json from pathlib import Path -from typing import Any +from typing import Any, Iterable from common_util.common_util_json import atomic_write_json @@ -57,6 +57,17 @@ ROCK_CLASS_SETS: dict[str, tuple[str, ...]] = { } DEFAULT_ROCK_CLASS_SET = "geochang5" +# 암 시공법 — 품셈이 공종을 가르는 기준. `None` 은 「아직 안 정함」이고 기본값이다. +ROCK_METHOD_RIPPING = "ripping" # 긁어내기 — 암절취 +ROCK_METHOD_BLASTING = "blasting" # 터뜨리기 — 발파암 +ROCK_METHODS = (ROCK_METHOD_RIPPING, ROCK_METHOD_BLASTING) + + +def rock_method(settings: dict[str, Any], rock_class: str) -> str | None: + """갈래 하나의 시공법. 안 정했으면 `None` — **기본값으로 때우지 않는다.**""" + value = (settings.get("rock_methods") or {}).get(rock_class) + return value if value in ROCK_METHODS else None + def default_settings() -> dict[str, Any]: """빈 설정. `estimation` 은 **자리만** 만든다 — 채우는 것은 B09 몫이다.""" @@ -68,13 +79,24 @@ def default_settings() -> dict[str, Any]: # 갈래별 비율(%). 설계자가 넣는 값이라 기본은 비워 둔다 — # 측점별 암질 판정에 기대지 않는다는 것이 8-1 사용자 확정이다. "rock_ratios_pct": {}, + # 갈래별 **시공법** — `{갈래이름: "ripping"|"blasting"}`. + # ⚠ 갈래 이름(연암·보통암…)만으로는 **긁어내는 암인지 터뜨리는 암인지** 알 수 없고, + # 품셈은 그 둘을 다른 공종으로 둔다(암절취 FP-09-04 / 발파암 FP-09-05). + # 기본은 **비워 둔다** — 찍으면 공종이 조용히 틀린다. 안 정하면 인계에서 + # 「시공법 미지정」으로 드러난다(2026-09-07 일감 9 에서 드러난 자리). + "rock_methods": {}, "conversion_factors_override": None, "haul_limits_m_override": None, "application_ratios_pct": {key: 100 for key in APPLICATION_RATIO_KEYS}, - # 자재총괄의 관급/사급 구분 — `{자재명: "public"|"private"}`. + # 자재총괄의 관급/사급 구분 — `{자재명: "owner_supplied"|"contractor_supplied"}` + # 또는 `{자재명: {"supply": …, "install_by": "contractor"|"owner"}}`. # ⚠ **법이 아니라 발주 결정**이라 기본은 비워 둔다. 안 정한 자재는 「미분류」로 # 화면에 드러난다 — 사급으로 조용히 넘기면 관급자재대가 새 나간다. "material_supply": {}, + # 콘크리트 타설 방식 — `ready_mixed`(FP-12-01-01) / `machine_mixed`(-02) / + # `hand_mixed`(-03). **설계 판단**이라 사용자가 고르는 값이고, 기본은 실무가 + # 쓰는 레디믹스트로 둔다. ⚠ 잠정이며 사용자 확정 대기 항목이다. + "concrete_placing_method": "ready_mixed", "dataset_versions": {}, }, "estimation": { @@ -124,16 +146,30 @@ def _merge(base: dict[str, Any], stored: dict[str, Any]) -> dict[str, Any]: SECTIONS = ("quantity", "estimation") -def save_section(project_root: str | Path, section: str, values: dict[str, Any]) -> dict[str, Any]: +def save_section( + project_root: str | Path, + section: str, + values: dict[str, Any], + *, + replace_keys: Iterable[str] = (), +) -> dict[str, Any]: """한 구획만 갈아 끼운다 — 남의 구획은 **손대지 않는다**. 두 페이지가 같은 파일을 쓰므로 통째로 덮으면 상대 값이 사라진다. 그래서 **통째로 쓰는 함수를 두지 않는다** — 쓰려면 반드시 구획 이름을 대야 한다. + + ⚠ `replace_keys` — **지울 수 있어야 하는 칸**은 병합이 아니라 통째로 갈아 끼운다. + 「고른 값을 안 정함으로 되돌리기」가 병합으로는 안 되기 때문이다(2026-09-07 화면에서 + 걸린 자리 — 시공법을 한 번 고르면 되돌릴 길이 없었다). """ if section not in SECTIONS: raise ValueError(f"모르는 구획: {section} (쓸 수 있는 것: {', '.join(SECTIONS)})") settings = load_settings(project_root) - settings[section] = _merge(settings.get(section) or {}, values) + merged = _merge(settings.get(section) or {}, values) + for key in replace_keys: + if key in values: + merged[key] = values[key] + settings[section] = merged settings["schema_version"] = SCHEMA_VERSION atomic_write_json(settings_path(project_root), settings) return settings diff --git a/common_util/common_util_quantity_spread.py b/common_util/common_util_quantity_spread.py new file mode 100644 index 00000000..13be2b74 --- /dev/null +++ b/common_util/common_util_quantity_spread.py @@ -0,0 +1,47 @@ +"""산출 요약 — 값의 **크기가 말이 되나**를 한눈에 보이는 자리 (2026-09-07 조율 창 권고). + +왜 있나 + 서브 창이 씨앗뿜어붙이기를 **합계 68.8원**으로 세워 두고도 몰랐던 일이 있었다. + 값이 **있기는 하니** 어떤 시험도 안 잡는다. 자릿수가 어긋난 것은 사람이 훑어야 보이고, + 훑으려면 **최솟값·중앙값·최댓값이 표 옆에 떠 있어야** 한다. + +⚠ 이것은 검사가 아니라 **눈에 띄게 하는 장치**다 + 기준을 정해 놓고 걸러 내지 않는다 — 임도 물량은 ㎥·㎡·m·ton·개가 섞여 있어 「얼마 이하면 + 이상하다」를 한 벌로 못 정한다. **단위별로 나눠** 내고 판단은 사람에게 맡긴다. +""" + +from __future__ import annotations + +from statistics import median +from typing import Any, Iterable + + +def spread(values: Iterable[float]) -> dict[str, float] | None: + """최솟값·중앙값·최댓값. 값이 없으면 `None` — 0 으로 만들지 않는다.""" + numbers = [float(v) for v in values if isinstance(v, (int, float))] + if not numbers: + return None + return { + "min": min(numbers), + "median": float(median(numbers)), + "max": max(numbers), + "count": len(numbers), + } + + +def spread_by_unit( + rows: Iterable[dict[str, Any]], *, value_key: str +) -> dict[str, dict[str, float]]: + """단위별로 갈라 낸다. ㎥ 와 ton 을 한 통에 넣으면 최솟값이 뜻을 잃는다.""" + buckets: dict[str, list[float]] = {} + for row in rows: + value = row.get(value_key) + if not isinstance(value, (int, float)): + continue + buckets.setdefault(str(row.get("unit") or "?"), []).append(float(value)) + result: dict[str, dict[str, float]] = {} + for unit, numbers in buckets.items(): + found = spread(numbers) + if found: + result[unit] = found + return result diff --git a/resources/data_formwork/formwork_reuse_2026-01-01.json b/resources/data_formwork/formwork_reuse_2026-01-01.json new file mode 100644 index 00000000..0c426ae5 --- /dev/null +++ b/resources/data_formwork/formwork_reuse_2026-01-01.json @@ -0,0 +1,68 @@ +{ + "schema_version": "1.0", + "dataset_id": "formwork_reuse", + "effective_date": "2026-01-01", + "note": "거푸집 사용횟수 — **품셈 1-7-1 원문**이 구조물 종류별로 정해 둔 값이다. 관측값이 아니라 법이므로 실무값으로 갈음하지 않는다.", + "source": { + "doc": "산림사업 표준품셈 1-7-1 거푸집 사용", + "table_id": "F0040", + "quote": "2회 T형보, 난간, 특히 복잡한 구조의 교각, 교대, 수문관의 본체 등 복잡한 구조 / 3회 슬래브, 교대, 교각, 옹벽, 파라펫트, 날개벽 등 약간 복잡한 구조 / 4회 측구, 수로, 확대기초, 우물통 등 비교적 간단한 구조 / 6회 수문 또는 관의 기초, 호안 및 보호공의 기초 등 극히 간단한 구조" + }, + "policy": { + "b08_delivers": "접촉 면적(㎡) + 사용횟수. **횟수별 재료 환산은 하지 않는다**.", + "b09_applies": "품셈 12-4 의 「사용횟수별 기준수량에 대한 비율(%)」은 일위대가 재료비에 걸린다. B08 이 여기서 곱하면 B09 와 겹쳐 두 번 준다.", + "unlisted_is_flagged": true + }, + "reuse_by_class": [ + { + "reuse_count": 2, + "class": "복잡한 구조", + "examples": ["T형보", "난간", "복잡한 교각", "교대", "수문관 본체"] + }, + { + "reuse_count": 3, + "class": "약간 복잡한 구조", + "examples": ["슬래브", "교대", "교각", "옹벽", "파라펫트", "날개벽"] + }, + { + "reuse_count": 4, + "class": "비교적 간단한 구조", + "examples": ["측구", "수로", "확대기초", "우물통"] + }, + { + "reuse_count": 6, + "class": "극히 간단한 구조", + "examples": ["수문 기초", "관의 기초", "호안 기초", "보호공 기초"] + } + ], + "type_map": [ + { + "type_id": "retaining_wall", + "reuse_count": 3, + "matched_example": "옹벽", + "note": "원문 3회 줄에 「옹벽」이 그대로 있음" + }, + { + "type_id": "pipe_inlet_basin", + "reuse_count": 6, + "matched_example": "보호공 기초", + "note": "관보호공 집수정 — 원문 6회 줄의 「호안 및 보호공의 기초」에 해당. ⚠ 벽체까지 6회로 볼지는 확인 필요" + }, + { + "type_id": "ford_pavement", + "reuse_count": null, + "note": "물넘이포장은 거푸집이 서지 않는 구조(면 포장) — 대상 아님" + } + ], + "reuse_ratio_pct": { + "note": "품셈 12-4 「사용횟수별 기준수량에 대한 비율(%)」. **B09 일위대가가 쓰는 값**이며 B08 은 참고로만 싣는다 — 여기서 곱하면 이중계상.", + "table_id": "F0336", + "plywood": { "1": 100.0, "2": 57.0, "3": 46.1, "4": 40.1, "5": 37.1, "6": 34.7 }, + "timber": { "1": 100.0, "2": 60.0, "3": 47.1, "4": 40.0, "5": 34.2, "6": 32.0 } + }, + "shoring": { + "note": "강관동바리(품셈 12-20)는 **슬래브를 떠받칠 때** 필요하다. 지금 서는 구조물(옹벽·집수정)은 벽체 거푸집만이라 대상이 아니다.", + "targets_pending": ["box_culvert", "ford_bridge"], + "why": "그 둘은 원단위·치수가 미확보라 슬래브 면적 자체가 안 나온다 — 동바리도 함께 미확보" + } +} diff --git a/resources/data_structure_unit/structure_unit_observed_2026-01-01.json b/resources/data_structure_unit/structure_unit_observed_2026-01-01.json new file mode 100644 index 00000000..41b98a68 --- /dev/null +++ b/resources/data_structure_unit/structure_unit_observed_2026-01-01.json @@ -0,0 +1,121 @@ +{ + "schema_version": "1.0", + "dataset_id": "structure_unit_observed", + "effective_date": "2026-01-01", + "note": "콘크리트 구조물의 **관측** 원단위표. 품셈에는 구조물별 표준 물량표가 없어(구조물_수량.md · 배수공_수량.md §2) 실무 설계원본에서 뽑은 값이다.", + "policy": { + "basis": "observed", + "no_interpolation": true, + "no_invented_dimensions": true, + "notes": [ + "⚠ 관측값은 **그 규격에서만** 맞다. 규격이 다르면 비례로 늘리지 않는다 — 벽 두께·기초는 높이에 비례하지 않는다.", + "⚠ 규격이 표에 없으면 「원단위 미확보」로 드러낸다. 가까운 값을 갖다 쓰지 않는다.", + "⚠ 줄마다 basis 를 싣는다 — 치수에서 나온 값(derived)과 한 표에 섞이기 때문이다." + ] + }, + "sources": { + "uljin_library": { + "doc": "울진소광 구조도 숨김탭 원단위 라이브러리", + "path": "resources/knowledge/original/실무문서/_원단위라이브러리_울진소광.md" + }, + "uljin_compare": { + "doc": "종합비교 04 — 임도 구조물 원단위 (울진 1공구 수량집계표 관측)", + "path": "resources/knowledge/original/실무문서/_종합비교/04_임도구조물_원단위.md" + } + }, + "entries": [ + { + "type_id": "retaining_wall", + "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" } + ] + }, + { + "type_id": "pipe_inlet_basin", + "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" } + ] + }, + { + "type_id": "pipe_inlet_basin", + "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" } + ] + }, + { + "type_id": "pipe_inlet_basin", + "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" } + ] + }, + { + "type_id": "ford_pavement", + "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" } + ] + } + ], + "not_found": { + "note": "규격은 우리 모델에 있으나 **관측 원단위가 어디에도 없는** 것. 지어내지 않는다.", + "items": [ + { + "type_id": "box_culvert", + "why": "울진 2공구에 BOX암거가 실재하나 원단위 라이브러리에 탭이 없음. 게다가 structures.json 의 BOX 제원은 body_width_m·body_height_m 와 날개벽뿐이라 **벽·저판·상판 두께가 없어 전개식도 못 세움**.", + "needs": "표준 단면(벽·저판·상판 두께) 확보 — 사용자 확정 대기" + }, + { + "type_id": "ford_bridge", + "why": "세월교 본체(날개벽 포함) 원단위 없음. 관 부분은 pipe 로 따로 섬.", + "needs": "표준도 물량 또는 실무 관측" + }, + { + "type_id": "retaining_wall", + "spec": { "form": "반중력식", "height_m": 1.6 }, + "why": "울진 2공구에 H=1.6 이 실재하나 수치가 라이브러리에 없음. H=2.0 값을 비례로 줄이지 않음 — 기초·벽체는 높이에 비례하지 않음." + } + ] + } +} diff --git a/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json b/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json index 646c7681..423e8e63 100644 --- a/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json +++ b/resources/data_work_item_mapping/work_item_mapping_2026-01-01.json @@ -125,6 +125,16 @@ "work_item_code": "FP-13-04-02", "master_name": "돌쌓기 > 메쌓기(장비)", "note": "인력 시공이면 FP-13-04-01" + }, + { + "type_id": "pipe_inlet_basin", + "work_item_code": "FP-12-15", + "master_name": "집수정" + }, + { + "type_id": "ford_pavement", + "work_item_code": "FP-12-06", + "master_name": "콘크리트 포장(인력시공)" } ], "pending_user": { @@ -132,14 +142,59 @@ "items": [ { "group": "지장목제거", - "candidates": ["FP-04-01 수확베기", "FP-04-02 단목베기", "FP-04-03 위험목 베기"], + "candidates": [ + "FP-04-01 수확베기", + "FP-04-02 단목베기", + "FP-04-03 위험목 베기" + ], "why": "품셈 4장은 벌목을 목적별로 가르는데 임도 지장목이 어느 쪽인지 원본이 말하지 않음" }, { "group": "흙깎기/측구터파기 암", - "candidates": ["FP-09-04 암절취(리핑)", "FP-09-05 발파암"], + "candidates": [ + "FP-09-04 암절취(리핑)", + "FP-09-05 발파암" + ], "why": "설계자가 넣는 암 갈래 이름(풍화암·연암·보통암·경암)이 리핑이냐 발파냐를 말하지 않음. 갈래마다 시공법을 지정하는 칸이 필요함" } ] + }, + "composite": { + "note": "품셈에 **그 이름의 공종이 없어** 여러 공종을 묶어 일위대가로 세우는 자리. 코드 하나로 못 적으므로 묶음을 적어 둔다 — 빈 코드로 두면 「매핑을 못 찾은 줄」과 구별이 안 된다.", + "items": [ + { + "type_id": "retaining_wall", + "parts": [ + "FP-12-01-01#철근구조물", + "FP-12-04 합판거푸집", + "FP-12-03 철근 현장가공 및 조림", + "FP-12-25 기초잡석" + ], + "why": "품셈 12장에 「옹벽」 공종이 없음. 실무 내역은 「반중력식옹벽 H=2.0」 한 줄이고 그 일위대가가 위 공종을 묶음.", + "needs": "일위대가 조립은 B09 몫 — B08 은 물량과 묶음만 넘김", + "placing_note": "타설 코드는 프로젝트 설정의 타설 방식으로 갈림(기본 레디믹스트). 철근구조물 판정은 원단위의 D13·D16 에서 자동으로 나옴.", + "not_ready": [ + "FP-12-03", + "FP-12-25" + ], + "not_ready_why": "B09 일위대가가 아직 안 섬 — 지금 세우면 절반짜리가 됨(2026-09-07 조율 창)" + } + ] + }, + "concrete_placing": { + "note": "콘크리트 타설은 **타설 방식 × 구조물 종류**로 갈린다. 방식은 설계 판단이라 프로젝트 설정(`quantity.concrete_placing_method`)이 고르고, 종류는 **원단위에 철근이 있나 없나로 자동 판정**한다 — 사람이 고르는 값이 아니다(2026-09-07 3자 확정).", + "method_codes": { + "ready_mixed": "FP-12-01-01", + "machine_mixed": "FP-12-01-02", + "hand_mixed": "FP-12-01-03" + }, + "default_method": "ready_mixed", + "default_is_provisional": true, + "structure_kinds": [ + "무근구조물", + "철근구조물", + "소형구조물" + ], + "kind_rule": "원단위 성분에 철근(이형철근·원형철근)이 있으면 철근구조물, 없으면 무근구조물. 소형구조물 판정 기준은 미확보." } } diff --git a/resources/data_work_item_master/_manifest.json b/resources/data_work_item_master/_manifest.json index 39bbea8d..8c85eaa2 100644 --- a/resources/data_work_item_master/_manifest.json +++ b/resources/data_work_item_master/_manifest.json @@ -1,7 +1,7 @@ { "schema_version": "1.0", "dataset_id": "data_work_item_master_manifest", - "generated_at": "2026-09-08T00:00:17+09:00", + "generated_at": "2026-09-08T01:00:54+09:00", "built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py", "source": { "dataset_id": "pum_forest", @@ -12,13 +12,18 @@ "files": [ { "file": "work_item_master_2026-01-01.json", - "sha256": "593653135d5a2871180e7a3921f9238629b275438ef47230412aa21e9bdd80c0", - "size_bytes": 725931 + "sha256": "fe454c56c9dc01ad7dae04a8f90d776d08c5ce33badeadb2606b35234bfce7eb", + "size_bytes": 785034 }, { "file": "form_undetermined_2026-01-01.json", "sha256": "7334dab9385bc1615a9cdb557ba814482e9a63d83b9e1232946918bfd8b5577f", "size_bytes": 37139 + }, + { + "file": "basis_missing_2026-01-01.json", + "sha256": "27f8d80d10b791df8502e633c1d279ce2acce63f871eae6b0c4eb26ca8290e2e", + "size_bytes": 20674 } ] } \ No newline at end of file diff --git a/resources/data_work_item_master/basis_missing_2026-01-01.json b/resources/data_work_item_master/basis_missing_2026-01-01.json new file mode 100644 index 00000000..d6f14406 --- /dev/null +++ b/resources/data_work_item_master/basis_missing_2026-01-01.json @@ -0,0 +1,872 @@ +{ + "schema_version": "1.0", + "dataset_id": "work_item_master_basis_missing", + "effective_date": "2026-01-01", + "note": "밑수(「10㎡당」 같은 기준 수량)를 못 찾은 표. **1 단위당으로 단정하지 말 것** — 곱셈이 10배·100배 틀린다. 값을 곱해야 하는 형태(requirement·productivity)만 담는다.", + "items": [ + { + "pum_table_id": "F0042", + "section": "2-1-1. 휘발유․오일", + "pum_form": "requirement", + "line": 1305 + }, + { + "pum_table_id": "F0043", + "section": "2-1-1. 휘발유․오일", + "pum_form": "requirement", + "line": 1328 + }, + { + "pum_table_id": "F0049", + "section": "2-1-4. 페인트 및 마킹테이프", + "pum_form": "requirement", + "line": 1402 + }, + { + "pum_table_id": "F0050", + "section": "2-1-4. 페인트 및 마킹테이프", + "pum_form": "requirement", + "line": 1417 + }, + { + "pum_table_id": "F0052", + "section": "2-1-4. 페인트 및 마킹테이프", + "pum_form": "requirement", + "line": 1444 + }, + { + "pum_table_id": "F0061", + "section": "2-1-11. 지상 약제살포", + "pum_form": "requirement", + "line": 1570 + }, + { + "pum_table_id": "F0075", + "section": "3-1. 경계표시", + "pum_form": "requirement", + "line": 1731 + }, + { + "pum_table_id": "F0077", + "section": "3-3. 작업로 설치", + "pum_form": "requirement", + "line": 1754 + }, + { + "pum_table_id": "F0078", + "section": "3-4-3. 임산물 운반로 및 작업로 보수비 산정", + "pum_form": "requirement", + "line": 1815 + }, + { + "pum_table_id": "F0080", + "section": "3-6. 산물 임내정리", + "pum_form": "requirement", + "line": 1867 + }, + { + "pum_table_id": "F0081", + "section": "3-7. 재해산물 수집", + "pum_form": "requirement", + "line": 1887 + }, + { + "pum_table_id": "F0082", + "section": "3-8. 드론 영상 촬영", + "pum_form": "requirement", + "line": 1901 + }, + { + "pum_table_id": "F0083", + "section": "4-1-1. 임업용 동력기계톱", + "pum_form": "productivity", + "line": 1929 + }, + { + "pum_table_id": "F0084", + "section": "4-1-2. 하베스터(부착형 스트로크)", + "pum_form": "productivity", + "line": 1947 + }, + { + "pum_table_id": "F0085", + "section": "4-1-2. 하베스터(부착형 스트로크)", + "pum_form": "requirement", + "line": 1957 + }, + { + "pum_table_id": "F0086", + "section": "4-2-1. 100본당", + "pum_form": "requirement", + "line": 1967 + }, + { + "pum_table_id": "F0087", + "section": "4-2-2. 1,000㎡당", + "pum_form": "requirement", + "line": 1997 + }, + { + "pum_table_id": "F0088", + "section": "4-3. 위험목 베기", + "pum_form": "requirement", + "line": 2012 + }, + { + "pum_table_id": "F0089", + "section": "4-4. 가지정리", + "pum_form": "requirement", + "line": 2041 + }, + { + "pum_table_id": "F0090", + "section": "4-5. 벌도 위험목 점검", + "pum_form": "requirement", + "line": 2052 + }, + { + "pum_table_id": "F0091", + "section": "4-6. 벌목부 작업안전 보조", + "pum_form": "requirement", + "line": 2062 + }, + { + "pum_table_id": "F0092", + "section": "5-1-1. 관목굴취", + "pum_form": "requirement", + "line": 2081 + }, + { + "pum_table_id": "F0093", + "section": "5-1-2. 교목굴취(나무높이)", + "pum_form": "requirement", + "line": 2099 + }, + { + "pum_table_id": "F0094", + "section": "5-1-3. 교목굴취(근원직경)", + "pum_form": "requirement", + "line": 2125 + }, + { + "pum_table_id": "F0095", + "section": "5-1-3. 교목굴취(근원직경)", + "pum_form": "requirement", + "line": 2156 + }, + { + "pum_table_id": "F0097", + "section": "5-1-5. 떼운반 적재 기준표", + "pum_form": "requirement", + "line": 2177 + }, + { + "pum_table_id": "F0098", + "section": "5-2. 뿌리돌림", + "pum_form": "requirement", + "line": 2194 + }, + { + "pum_table_id": "F0099", + "section": "5-3-1. 나무식재", + "pum_form": "requirement", + "line": 2219 + }, + { + "pum_table_id": "F0102", + "section": "5-3-2. 관목식재(단식)", + "pum_form": "requirement", + "line": 2254 + }, + { + "pum_table_id": "F0103", + "section": "5-3-3. 관목식재(군식)", + "pum_form": "requirement", + "line": 2272 + }, + { + "pum_table_id": "F0104", + "section": "5-3-4. 교목식재(나무높이)", + "pum_form": "requirement", + "line": 2291 + }, + { + "pum_table_id": "F0106", + "section": "5-3-5. 교목식재(흉고직경)", + "pum_form": "requirement", + "line": 2322 + }, + { + "pum_table_id": "F0108", + "section": "5-3-5. 교목식재(흉고직경)", + "pum_form": "requirement", + "line": 2355 + }, + { + "pum_table_id": "F0109", + "section": "5-4. 파종조림", + "pum_form": "requirement", + "line": 2366 + }, + { + "pum_table_id": "F0110", + "section": "5-5. 천연하종갱신", + "pum_form": "requirement", + "line": 2383 + }, + { + "pum_table_id": "F0111", + "section": "5-6. 움싹갱신", + "pum_form": "requirement", + "line": 2396 + }, + { + "pum_table_id": "F0112", + "section": "5-7. 생태보완조림", + "pum_form": "requirement", + "line": 2409 + }, + { + "pum_table_id": "F0113", + "section": "5-8. 큰나무 공익조림", + "pum_form": "requirement", + "line": 2426 + }, + { + "pum_table_id": "F0114", + "section": "5-9. 해안조림", + "pum_form": "requirement", + "line": 2439 + }, + { + "pum_table_id": "F0116", + "section": "5-11. 사초심기", + "pum_form": "requirement", + "line": 2475 + }, + { + "pum_table_id": "F0117", + "section": "5-12. 떼붙임(재배잔디)", + "pum_form": "requirement", + "line": 2496 + }, + { + "pum_table_id": "F0118", + "section": "5-13. 떼심기", + "pum_form": "requirement", + "line": 2511 + }, + { + "pum_table_id": "F0121", + "section": "5-16-1. 단끊기", + "pum_form": "requirement", + "line": 2565 + }, + { + "pum_table_id": "F0126", + "section": "5-19-1. 표토절취 및 모으기", + "pum_form": "requirement", + "line": 2653 + }, + { + "pum_table_id": "F0129", + "section": "5-21. 표토이식", + "pum_form": "requirement", + "line": 2689 + }, + { + "pum_table_id": "F0132", + "section": "5-22-4. 평떼 시비", + "pum_form": "requirement", + "line": 2731 + }, + { + "pum_table_id": "F0143", + "section": "5-27. 식재면 관리", + "pum_form": "requirement", + "line": 2905 + }, + { + "pum_table_id": "F0144", + "section": "5-28-1. 짚망", + "pum_form": "requirement", + "line": 2919 + }, + { + "pum_table_id": "F0153", + "section": "6-1. 비료주기", + "pum_form": "requirement", + "line": 3062 + }, + { + "pum_table_id": "F0154", + "section": "6-2-1. 둘레베기", + "pum_form": "requirement", + "line": 3079 + }, + { + "pum_table_id": "F0155", + "section": "6-2-2. 줄베기", + "pum_form": "requirement", + "line": 3089 + }, + { + "pum_table_id": "F0156", + "section": "6-2-3. 모두베기", + "pum_form": "requirement", + "line": 3105 + }, + { + "pum_table_id": "F0157", + "section": "6-3. 맹아제거", + "pum_form": "requirement", + "line": 3122 + }, + { + "pum_table_id": "F0159", + "section": "6-4-2. 덩굴 약제 살포처리", + "pum_form": "requirement", + "line": 3153 + }, + { + "pum_table_id": "F0160", + "section": "6-4-3. 소금처리", + "pum_form": "requirement", + "line": 3164 + }, + { + "pum_table_id": "F0161", + "section": "6-4-4. 뿌리제거", + "pum_form": "requirement", + "line": 3183 + }, + { + "pum_table_id": "F0163", + "section": "6-5. 어린나무 가꾸기", + "pum_form": "requirement", + "line": 3244 + }, + { + "pum_table_id": "F0164", + "section": "6-6. 가지치기 및 수형교정", + "pum_form": "requirement", + "line": 3276 + }, + { + "pum_table_id": "F0165", + "section": "6-7-1. 교목 시비", + "pum_form": "requirement", + "line": 3304 + }, + { + "pum_table_id": "F0166", + "section": "6-7-2. 관목 시비", + "pum_form": "requirement", + "line": 3318 + }, + { + "pum_table_id": "F0170", + "section": "7-1-1. 수확", + "pum_form": "requirement", + "line": 3371 + }, + { + "pum_table_id": "F0171", + "section": "7-1-1. 수확", + "pum_form": "requirement", + "line": 3381 + }, + { + "pum_table_id": "F0172", + "section": "7-1-2. 숲가꾸기, 병해충방제", + "pum_form": "requirement", + "line": 3395 + }, + { + "pum_table_id": "F0174", + "section": "7-3. 아키야윈치(임업용 윈치) 집재", + "pum_form": "requirement", + "line": 3439 + }, + { + "pum_table_id": "F0175", + "section": "7-4-1. 수확", + "pum_form": "requirement", + "line": 3452 + }, + { + "pum_table_id": "F0176", + "section": "7-4-2. 숲가꾸기, 산림병해충방제", + "pum_form": "requirement", + "line": 3476 + }, + { + "pum_table_id": "F0177", + "section": "7-5-1. 수확", + "pum_form": "requirement", + "line": 3496 + }, + { + "pum_table_id": "F0178", + "section": "7-5-2. 숲가꾸기, 병해충방제", + "pum_form": "requirement", + "line": 3519 + }, + { + "pum_table_id": "F0179", + "section": "7-6. 스윙야더 집재", + "pum_form": "requirement", + "line": 3542 + }, + { + "pum_table_id": "F0180", + "section": "7-7-1. 수확", + "pum_form": "requirement", + "line": 3556 + }, + { + "pum_table_id": "F0181", + "section": "7-7-2. 숲가꾸기, 병해충방제", + "pum_form": "requirement", + "line": 3580 + }, + { + "pum_table_id": "F0182", + "section": "7-8-1. 가선설치", + "pum_form": "requirement", + "line": 3607 + }, + { + "pum_table_id": "F0183", + "section": "7-8-2. 가선해체", + "pum_form": "requirement", + "line": 3619 + }, + { + "pum_table_id": "F0184", + "section": "7-8-3. 집재 소요인력", + "pum_form": "requirement", + "line": 3631 + }, + { + "pum_table_id": "F0185", + "section": "7-9-1. 수확", + "pum_form": "requirement", + "line": 3656 + }, + { + "pum_table_id": "F0186", + "section": "7-9-2. 숲가꾸기, 소나무재선충병방제", + "pum_form": "requirement", + "line": 3673 + }, + { + "pum_table_id": "F0191", + "section": "7-11. 동력상하차기(우드그래플) 집재-수확", + "pum_form": "requirement", + "line": 3745 + }, + { + "pum_table_id": "F0192", + "section": "7-12. 동력상하차기(우드그래플) 집적", + "pum_form": "requirement", + "line": 3763 + }, + { + "pum_table_id": "F0201", + "section": "8-1-1. 약제주입기", + "pum_form": "requirement", + "line": 3945 + }, + { + "pum_table_id": "F0202", + "section": "8-1-2. 약제주입병", + "pum_form": "requirement", + "line": 3964 + }, + { + "pum_table_id": "F0203", + "section": "8-2-1. 소나무재선충병", + "pum_form": "requirement", + "line": 3982 + }, + { + "pum_table_id": "F0204", + "section": "8-2-1. 소나무재선충병", + "pum_form": "requirement", + "line": 3999 + }, + { + "pum_table_id": "F0206", + "section": "8-2-1. 소나무재선충병", + "pum_form": "requirement", + "line": 4023 + }, + { + "pum_table_id": "F0207", + "section": "8-2-1. 소나무재선충병", + "pum_form": "requirement", + "line": 4041 + }, + { + "pum_table_id": "F0209", + "section": "8-2-2. 솔잎혹파리", + "pum_form": "requirement", + "line": 4080 + }, + { + "pum_table_id": "F0210", + "section": "8-2-2. 솔잎혹파리", + "pum_form": "requirement", + "line": 4108 + }, + { + "pum_table_id": "F0211", + "section": "8-2-2. 솔잎혹파리", + "pum_form": "requirement", + "line": 4136 + }, + { + "pum_table_id": "F0213", + "section": "8-2-3. 솔껍질깍지벌레", + "pum_form": "requirement", + "line": 4182 + }, + { + "pum_table_id": "F0214", + "section": "8-2-3. 솔껍질깍지벌레", + "pum_form": "requirement", + "line": 4210 + }, + { + "pum_table_id": "F0215", + "section": "8-2-3. 솔껍질깍지벌레", + "pum_form": "requirement", + "line": 4238 + }, + { + "pum_table_id": "F0217", + "section": "8-2-4. 솔나방", + "pum_form": "requirement", + "line": 4279 + }, + { + "pum_table_id": "F0219", + "section": "8-2-5. 푸사리움가지마름병", + "pum_form": "requirement", + "line": 4320 + }, + { + "pum_table_id": "F0224", + "section": "8-5. 페르몬 유인트랩", + "pum_form": "requirement", + "line": 4434 + }, + { + "pum_table_id": "F0232", + "section": "8-7. 방제 실행 등록", + "pum_form": "requirement", + "line": 4583 + }, + { + "pum_table_id": "F0235", + "section": "8-9. 잔가지줍기", + "pum_form": "requirement", + "line": 4623 + }, + { + "pum_table_id": "F0236", + "section": "8-10. 그물망 피복", + "pum_form": "requirement", + "line": 4631 + }, + { + "pum_table_id": "F0237", + "section": "8-11. 이동식 임목 파쇄", + "pum_form": "productivity", + "line": 4649 + }, + { + "pum_table_id": "F0238", + "section": "9-2. 노선 굴진 보조원", + "pum_form": "requirement", + "line": 4675 + }, + { + "pum_table_id": "F0241", + "section": "9-4-1. 암파쇄", + "pum_form": "productivity", + "line": 4719 + }, + { + "pum_table_id": "F0244", + "section": "9-5-2. 깎기(90%)", + "pum_form": "productivity", + "line": 4759 + }, + { + "pum_table_id": "F0251", + "section": "9-8-1. T=30㎝ 미만", + "pum_form": "requirement", + "line": 4860 + }, + { + "pum_table_id": "F0294", + "section": "9-21. 제근", + "pum_form": "requirement", + "line": 5512 + }, + { + "pum_table_id": "F0310", + "section": "10-7-4. 모노레일 운반", + "pum_form": "requirement", + "line": 5770 + }, + { + "pum_table_id": "F0313", + "section": "10-8-1. 짐내리기", + "pum_form": "requirement", + "line": 5813 + }, + { + "pum_table_id": "F0314", + "section": "10-8-2. 운반대 설치", + "pum_form": "requirement", + "line": 5829 + }, + { + "pum_table_id": "F0317", + "section": "10-10-1. 콘크리트 및 골재운반(지상)", + "pum_form": "requirement", + "line": 5884 + }, + { + "pum_table_id": "F0318", + "section": "10-10-2. 그 외 자재의 운반품셈", + "pum_form": "requirement", + "line": 5894 + }, + { + "pum_table_id": "F0337", + "section": "12-5. 문양거푸집(0~7m)", + "pum_form": "requirement", + "line": 6217 + }, + { + "pum_table_id": "F0339", + "section": "12-7-1. 포장절단", + "pum_form": "requirement", + "line": 6254 + }, + { + "pum_table_id": "F0340", + "section": "12-7-2. 줄눈설치", + "pum_form": "requirement", + "line": 6269 + }, + { + "pum_table_id": "F0341", + "section": "12-8. 콘크리트 포장 거푸집", + "pum_form": "requirement", + "line": 6280 + }, + { + "pum_table_id": "F0350", + "section": "12-12. 날개벽", + "pum_form": "requirement", + "line": 6421 + }, + { + "pum_table_id": "F0379", + "section": "12-29. 스페이셔 설치(몰탈 블록)", + "pum_form": "requirement", + "line": 6775 + }, + { + "pum_table_id": "F0405", + "section": "13-3. 기초다짐 및 뒤채움’ 항을 적용한다.", + "pum_form": "requirement", + "line": 7087 + }, + { + "pum_table_id": "F0412", + "section": "13-4-4. 찰쌓기(인력)", + "pum_form": "requirement", + "line": 7174 + }, + { + "pum_table_id": "F0413", + "section": "13-4-4. 찰쌓기(인력)", + "pum_form": "requirement", + "line": 7185 + }, + { + "pum_table_id": "F0430", + "section": "13-10-2. 나무 말뚝박기", + "pum_form": "requirement", + "line": 7491 + }, + { + "pum_table_id": "F0437", + "section": "13-12-1. 뭉기기", + "pum_form": "requirement", + "line": 7612 + }, + { + "pum_table_id": "F0438", + "section": "13-12-2. 지오셀(사면보강)", + "pum_form": "requirement", + "line": 7625 + }, + { + "pum_table_id": "F0444", + "section": "13-14. 식생토낭 및 포트", + "pum_form": "requirement", + "line": 7715 + }, + { + "pum_table_id": "F0447", + "section": "13-15-2. 목책 설치", + "pum_form": "requirement", + "line": 7745 + }, + { + "pum_table_id": "F0454", + "section": "8-2-3 굴착기(2025)」를 참조하여 적용계수를 달리 적용하도록 한다.", + "pum_form": "requirement", + "line": 7983 + }, + { + "pum_table_id": "F0455", + "section": "2-1. 풀베기, (1) 둘레베기(조림목 본수 2,700본/ha, 조림1년차)", + "pum_form": "requirement", + "line": 8031 + }, + { + "pum_table_id": "F0456", + "section": "2-2. 풀베기, (2) 모두베기(조림목 본수 2,700본/ha, 조림2년차)", + "pum_form": "requirement", + "line": 8061 + }, + { + "pum_table_id": "F0457", + "section": "2-3. 풀베기, (3) 줄베기(조림목 본수 2,700본/ha, 조림2년차)", + "pum_form": "requirement", + "line": 8092 + }, + { + "pum_table_id": "F0458", + "section": "2-4. 풀베기, (4) 맹아제거+둘레베기(제거대상 맹아 1,000본/ha. 조림목 본수 2,700본/ha, 조림1년차)", + "pum_form": "requirement", + "line": 8124 + }, + { + "pum_table_id": "F0459", + "section": "2-5. 덩굴제거, (1) 지상부 덩굴걷기(큰나무 피해지. 덩굴 피복도 20~40%미만)", + "pum_form": "requirement", + "line": 8156 + }, + { + "pum_table_id": "F0460", + "section": "2-6. 덩굴제거, (2) 지상부 약제살포(큰나무 피해지. 덩굴 피복도 60~80%미만)", + "pum_form": "requirement", + "line": 8189 + }, + { + "pum_table_id": "F0461", + "section": "2-7. 덩굴제거, (3) 뿌리굴취(풀베기단계 조림지 1㎝미만 500본, 1~4㎝ 400본, 4㎝초과 100본)", + "pum_form": "requirement", + "line": 8225 + }, + { + "pum_table_id": "F0462", + "section": "2-8. 덩굴제거, (4) 뿌리굴취(풀베기단계 조림지 1㎝미만 500본, 1~4㎝ 400본,", + "pum_form": "requirement", + "line": 8265 + }, + { + "pum_table_id": "F0463", + "section": "2-9. 어린나무가꾸기, (1) 치수림단계 (20m 간격 소작업로 설치, 제거대상 피복도 ‘소’, 가지치기 미실행)", + "pum_form": "requirement", + "line": 8303 + }, + { + "pum_table_id": "F0464", + "section": "2-10. 어린나무가꾸기, (2) 유령림단계 (제거대상 피복도 ‘밀’, 가지치기 잣나무 0~2m. 500본/ha)", + "pum_form": "requirement", + "line": 8344 + }, + { + "pum_table_id": "F0465", + "section": "2-11. 솎아베기, (1) 산물을 임내에 버리는 경우", + "pum_form": "requirement", + "line": 8385 + }, + { + "pum_table_id": "F0466", + "section": "2-12. 솎아베기, (2) 산물을 전간재로 생산하는 경우", + "pum_form": "requirement", + "line": 8434 + }, + { + "pum_table_id": "F0467", + "section": "2-13. 위험목 베기", + "pum_form": "requirement", + "line": 8496 + }, + { + "pum_table_id": "F0468", + "section": "2-14. 산물수집, (1) 인력집재(단목 집재 + 집적)", + "pum_form": "requirement", + "line": 8542 + }, + { + "pum_table_id": "F0469", + "section": "2-15. 산물수집, (2) 지면끌기집재(공정별 독립작업)", + "pum_form": "requirement", + "line": 8585 + }, + { + "pum_table_id": "F0470", + "section": "2-16. 산물수집, (3) 지면끌기집재 (동시작업)", + "pum_form": "requirement", + "line": 8655 + }, + { + "pum_table_id": "F0471", + "section": "2-17. 산물임내정리", + "pum_form": "requirement", + "line": 8720 + }, + { + "pum_table_id": "F0472", + "section": "3-1. 임업용 동력기계톱, 우드그래플, 소형트럭", + "pum_form": "requirement", + "line": 8753 + }, + { + "pum_table_id": "F0473", + "section": "3-2. 임업용 동력기계톱, 스마트집재기, 우드그래플, 초소형포워더", + "pum_form": "requirement", + "line": 8828 + }, + { + "pum_table_id": "F0474", + "section": "3-3. 하베스터, 타워야더, 우드그래플, 소형포워더", + "pum_form": "requirement", + "line": 8912 + }, + { + "pum_table_id": "F0475", + "section": "4-1. 소나무재선충병방제", + "pum_form": "requirement", + "line": 8989 + }, + { + "pum_table_id": "F0476", + "section": "4-2. 참나무시들음병방제", + "pum_form": "requirement", + "line": 9106 + } + ] +} \ No newline at end of file diff --git a/resources/data_work_item_master/work_item_master_2026-01-01.json b/resources/data_work_item_master/work_item_master_2026-01-01.json index 724c8a4e..2a294516 100644 --- a/resources/data_work_item_master/work_item_master_2026-01-01.json +++ b/resources/data_work_item_master/work_item_master_2026-01-01.json @@ -2,7 +2,7 @@ "schema_version": "1.0", "dataset_id": "work_item_master_forest", "effective_date": "2026-01-01", - "generated_at": "2026-09-08T00:00:17+09:00", + "generated_at": "2026-09-08T01:00:54+09:00", "dataset_version": { "dataset_id": "pum_forest", "effective_date": "2026-01-01", @@ -20,7 +20,10 @@ "tables_total": 475, "tables_attached": 456, "tables_orphan": 19, - "form_undetermined": 77 + "form_undetermined": 77, + "basis_found": 183, + "basis_missing": 144, + "basis_grouped": 36 }, "orphan_tables": [ { @@ -144,6 +147,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "2.5톤 차량", "소 묘", @@ -248,6 +255,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "단 위", "공사연장", @@ -867,6 +878,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "설계서의 총액", "설계서의 소계", @@ -931,6 +946,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "경 암 ( 硬 岩 )", "보 통 암 ( 普 通 硬 岩 )", @@ -1051,6 +1070,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자연상태의 체적", "흐트러진 상태의 체적" @@ -1094,6 +1117,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "사용고재(시멘트공대 및 공드람 제외)", "강재스크랩(Scrap)", @@ -1154,6 +1181,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "6톤 차량", "목재(원목)", @@ -1726,6 +1757,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "시 멘 트", "잔골재ㆍ채움재", @@ -1780,6 +1815,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "모 래", "부순돌ㆍ자갈ㆍ막자갈", @@ -1817,6 +1856,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "모 래" ], @@ -1839,6 +1882,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "원 형 철 근", "이 형 철 근", @@ -1911,6 +1958,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "목 재", "판 재", @@ -2058,6 +2109,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "6월∼9월", "10월∼12월", @@ -2100,6 +2155,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "3년차 이상", "2년차", @@ -2142,6 +2201,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "집단화 정도", "개소당 평균면적이 1~3ha 미만", @@ -2188,6 +2251,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업구역", "개소 평균 1~3ha 미만", @@ -2258,6 +2325,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "산지경사", "중 (15~30°)", @@ -2328,6 +2399,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "도 보", "1.3㎞∼2.5㎞ 미만", @@ -2390,6 +2465,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "식생을 제거하지 않고는 보행이 곤란하다", "보행하는데 약간의 어려움이 있다", @@ -2432,6 +2511,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "가슴높이 이상의 초본․관목", "가슴높이 미만의 초본․관목", @@ -2474,6 +2557,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "어렵다(높이 1.2m 이상이고, 직경이 4~6cm 이상)", "보통이다(높이 1.2m 이상이고, 직경이 4~6cm 미만)", @@ -2516,6 +2603,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "돌 등 장애물 함량이 30% 이상이고, 나무뿌리가 밀하게 분포할 경우", "돌 등 장애물 함량이 10~30%이고, 나무뿌리가 보통정도로 분포할 경우", @@ -2558,6 +2649,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "과밀(80%초과 피복)", "밀(60~80%미만 피복)", @@ -2610,6 +2705,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "대 (임지의 71% 이상 분포)", "중 (임지의 41%~70% 분포)", @@ -2652,6 +2751,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "돌, 도랑, 그루터기 등으로 주행이 매우 힘들다", "돌, 도랑, 그루터기 등으로 주행이 다소 힘들다", @@ -2694,6 +2797,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "가선을 이용한 기계장비의 원목․생산재 집재", "상향집재", @@ -2771,6 +2878,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "11~20m", "6~10m", @@ -2813,6 +2924,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "규격재 생산을 위한 조재" ], @@ -2845,6 +2960,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "산림병해충방제 (단목베기, 나무주사)", "10~29본/ha", @@ -2927,6 +3046,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "산림병해충방제 (산물수집-기계장비 집재)", "16~20cm", @@ -2975,6 +3098,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "매개충나무주사" ], @@ -3018,6 +3145,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "사면형", "종방향 복합사면(가시거리 불량, 고소작업차 사용 필요)", @@ -3070,6 +3201,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "접근성", "이착륙장~방재사업지까지의 이동거리 200m 이내 까지 차량접근 가능", @@ -3116,6 +3251,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "방제 수종", "소나무, 해송" @@ -3156,6 +3295,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "그물망 피복 시 임내 운반거리", "101~200m", @@ -3220,6 +3363,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "장비의 규격 (굴삭기)", "0.4㎥ 이상" @@ -3260,6 +3407,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업 가능 시간", "3시간 이하", @@ -3318,6 +3469,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "1", "2" @@ -3369,6 +3524,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "적용방법", "순 원 가", @@ -3601,6 +3760,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "2회", "3회", @@ -3666,6 +3829,10 @@ "form_basis": "품셈 제1장(적용기준)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "거 푸 집 씻 기", "콘크리트혼합 및 양생", @@ -3739,6 +3906,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 면적", "구분", @@ -4101,6 +4272,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통휘발유 (주연료)", "체인오일 (일반오일)", @@ -4141,6 +4316,10 @@ "form_basis": "'소요인력'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "예취기(휘발유)" ], @@ -4167,6 +4346,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "아키아윈치(휘발유)" ], @@ -4191,6 +4374,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "2드럼 케이블윈치(휘발유)" ], @@ -4215,6 +4402,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": 1.0, "basis_unit": "인", + "basis_source": "표 안", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "천공기 (휘발유)" ], @@ -4251,6 +4442,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "트랙터 부착 기계", "굴삭기 부착 기계", @@ -4364,6 +4559,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "트랙 접지력 보강", "블레이드 및 실린더 교체" @@ -4401,6 +4600,10 @@ "form_basis": "'소요량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "주재료", "페인트", @@ -4443,6 +4646,10 @@ "form_basis": "'소요량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "주재료", "페인트" @@ -4477,6 +4684,10 @@ "form_basis": "'소요량'", "basis_quantity": 1.0, "basis_unit": "km", + "basis_source": "표 안", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "주재료", "마킹테이프" @@ -4509,6 +4720,10 @@ "form_basis": "'소요량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "주재료", "페인트" @@ -4553,6 +4768,10 @@ "form_basis": "'소요인력'", "basis_quantity": 1.0, "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "농약 (Fluroxypyr -meptyl + Triclypyr-TEA 미탁제)" ], @@ -4577,8 +4796,12 @@ "source_line": 1477, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소금처리량(g)" ], @@ -4607,6 +4830,10 @@ "form_basis": "'소요량'", "basis_quantity": 100.0, "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "농약 (글리포세이트)", "친환경 비닐랩" @@ -4648,8 +4875,12 @@ "source_line": 1500, "pum_form": "requirement", "form_basis": "'수량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "수량", "천공테이프", @@ -4734,6 +4965,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "훈증약제", "훈증피복제", @@ -4796,8 +5031,12 @@ "source_line": 1535, "pum_form": "requirement", "form_basis": "'수 량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 160.0, + "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "수 량", "휘발유 (양수기)", @@ -4845,8 +5084,12 @@ "source_line": 1548, "pum_form": "requirement", "form_basis": "'수 량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "수 량", "휘발유 (연료)", @@ -4894,8 +5137,12 @@ "source_line": 1559, "pum_form": "requirement", "form_basis": "'수 량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "수 량", "휘발유(발전기)", @@ -4945,6 +5192,10 @@ "form_basis": "'수 량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "수 량", "경유(차량살포)" @@ -4987,6 +5238,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "그물망", "표식라벨" @@ -5027,6 +5282,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "메인파쇄기날", "분쇄기날" @@ -5070,6 +5329,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 면적", "구분", @@ -5452,6 +5715,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "45cc (배기량기준)" ], @@ -5486,6 +5753,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "예취기 (배기량 35cc)" ], @@ -5520,6 +5791,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "아키아윈치", "2드럼 케이블윈치", @@ -5689,6 +5964,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "배부식분무기" ], @@ -5723,6 +6002,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": 1.0, "basis_unit": "인", + "basis_source": "표 안", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "45cc (배기량 기준)" ], @@ -5757,6 +6040,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "5HP(양수기)" ], @@ -5791,6 +6078,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무인헬기(FAZER) 내용가동시간 1,400시간기준" ], @@ -5825,6 +6116,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무인멀티콥터(평균가) 내용가동시간 1,000시간 기준", "리튬폴리머 배터리 (평균가) (16,000mA, 32000mA)" @@ -5865,6 +6160,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "1톤(방제차량)", "45HP(동력분무기)" @@ -5905,6 +6204,10 @@ "form_basis": "헤더 '손료계수'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "-" ], @@ -5941,6 +6244,10 @@ "form_basis": "헤더 '기계손료'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "손료계수(10⁻⁷)", "굴착기(0.2~0.8㎥)", @@ -6002,6 +6309,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소요인력", "0.2" @@ -6028,6 +6339,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 면적", "구분", @@ -6960,6 +7275,10 @@ "form_basis": "헤더 '인/1일'", "basis_quantity": 1.0, "basis_unit": "km", + "basis_source": "표 안", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "구 분", "작업로 예정선 선정 및 표식" @@ -6988,6 +7307,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 면적", "구분", @@ -8032,6 +8355,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "구 분", "소작업로", @@ -8066,6 +8393,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 면적", "구분", @@ -9025,6 +9356,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "노면정지", "노면굴기" @@ -9063,8 +9398,12 @@ "source_line": 1824, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "모든 벌채산물 임내존치지역", "휴경지 등 정리", @@ -9157,6 +9496,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10㎥ 미만", "임내 정리" @@ -9215,6 +9558,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10㎥ 미만", "10m이하", @@ -9303,6 +9650,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "드론조종자 (건설기계조종원)", "계", @@ -9381,6 +9732,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 임․소반", "구분", @@ -10859,6 +11214,10 @@ "form_basis": "헤더 '/1인/1일'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "벌도목 구분", "단목", @@ -10909,6 +11268,10 @@ "form_basis": "헤더 '/1인/1일'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "벌도ㆍ조재 공정량(㎥)", "59.20" @@ -10935,6 +11298,10 @@ "form_basis": "값 단위 '(㎥)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "154" ], @@ -10967,6 +11334,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 임․소반", "구분", @@ -11787,6 +12158,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "벌목", "10㎝이하", @@ -11933,6 +12308,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "5m 미만", "벌목부 보통인부", @@ -11998,6 +12377,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "16~20cm", @@ -12080,6 +12463,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부" ], @@ -12114,6 +12501,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.13인" ], @@ -12146,6 +12537,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "1인" ], @@ -12196,6 +12591,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.3m 미만", "특 별 인 부", @@ -12247,6 +12646,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "1.0 이하", @@ -12340,6 +12743,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부(인)", "4이하", @@ -12509,6 +12916,10 @@ "form_basis": "값 단위 '(㎥)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼ 19", "20 ∼ 26", @@ -12559,8 +12970,12 @@ "source_line": 2167, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "줄떼", "평떼" @@ -12601,6 +13016,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "지게", "리어카", @@ -12686,6 +13105,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "3", @@ -12832,6 +13255,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소묘식재", "중묘식재", @@ -12872,6 +13299,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소나무", "낙엽송", @@ -12954,6 +13385,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소묘", "중묘", @@ -13000,6 +13435,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.3m 미만", "특 별 인 부", @@ -13051,6 +13490,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.3m 미만", "특 별 인 부", @@ -13102,6 +13545,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인 력 시 공", "특별인부(인)", @@ -13227,6 +13674,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인력품의 10%" ], @@ -13259,6 +13710,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부(인)", "4(5)이하", @@ -13412,6 +13867,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인력품의 10%" ], @@ -13434,6 +13893,10 @@ "form_basis": "값 단위 '(㎥)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "8 ∼ 17", "18 ∼ 22", @@ -13486,6 +13949,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "파종상 만들기", "80cm×80cm×5,000개", @@ -13550,6 +14017,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "지면긁기작업", "폭 80cm × 열간거리 2m (전면적의 40%)" @@ -13593,6 +14064,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "맹아근주 정리작업" ], @@ -13629,6 +14104,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "움싹본수조절", "치수이식", @@ -13679,6 +14158,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "큰나무 식재" ], @@ -13715,6 +14198,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부" ], @@ -13749,8 +14236,12 @@ "source_line": 2456, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㏊", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "묘목", "요소", @@ -13829,6 +14320,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부(인)" ], @@ -13865,6 +14360,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "줄떼", "평떼" @@ -13905,6 +14404,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "줄떼심기", "띠떼심기" @@ -13945,6 +14448,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": 1.0, "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "새채집", "요소", @@ -14014,8 +14521,12 @@ "source_line": 2546, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "말뚝", "보통인부", @@ -14079,6 +14590,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통토사", "절 취", @@ -14153,8 +14668,12 @@ "source_line": 2585, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "단끊기", "떼붙임", @@ -14219,8 +14738,12 @@ "source_line": 2603, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "단끊기", "줄떼심기", @@ -14276,8 +14799,12 @@ "source_line": 2617, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "단끊기", "돌쌓기", @@ -14326,8 +14853,12 @@ "source_line": 2630, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "종자", "비료", @@ -14422,6 +14953,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "6", "표토절취" @@ -14470,6 +15005,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": 30.0, "basis_unit": "㎥", + "basis_source": "표 안", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "15", "㎥ 당", @@ -14521,8 +15060,12 @@ "source_line": 2676, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "표토채취", "굴착기(무한궤도, 0.7㎥)", @@ -14579,6 +15122,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "채 취", "운 반", @@ -14641,8 +15188,12 @@ "source_line": 2712, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조경공", "보통인부" @@ -14684,8 +15235,12 @@ "source_line": 2721, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부" ], @@ -14722,6 +15277,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조경공", "보통인부", @@ -14792,8 +15351,12 @@ "source_line": 2752, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조경공", "보통인부" @@ -14835,8 +15398,12 @@ "source_line": 2761, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부" ], @@ -14880,8 +15447,12 @@ "source_line": 2771, "pum_form": "requirement", "form_basis": "'수량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "비 료", @@ -15000,8 +15571,12 @@ "source_line": 2796, "pum_form": "requirement", "form_basis": "'수량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "비 료", @@ -15112,6 +15687,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "초본 위주형", "초본, 야생화류", @@ -15196,6 +15775,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "초본 위주형", "초본, 야생화류", @@ -15288,8 +15871,12 @@ "source_line": 2861, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "인력", @@ -15352,6 +15939,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "9", "사토", @@ -15415,6 +16006,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "9", "사토", @@ -15478,6 +16073,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10% 이내", "개답", @@ -15526,6 +16125,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조경공", "보통인부" @@ -15575,6 +16178,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특 별 인 부", "보 통 인 부" @@ -15615,6 +16222,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "폭 1.5m 이하", "조 경 공", @@ -15668,6 +16279,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "K(버킷계수)", "f(체적환산계수)", @@ -15718,8 +16333,12 @@ "source_line": 2961, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1000.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "표시봉 설치" ], @@ -15754,8 +16373,12 @@ "source_line": 2973, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1000.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "지주목 설치" ], @@ -15790,8 +16413,12 @@ "source_line": 2985, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1000.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "대 절" ], @@ -15826,8 +16453,12 @@ "source_line": 2996, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1000.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소묘, 중묘", "대 묘" @@ -15866,8 +16497,12 @@ "source_line": 3007, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1000.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소 묘", "중 묘", @@ -15923,6 +16558,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인공림(10년이하 조림지)", "인공림(10년초과 성림지)", @@ -15978,6 +16617,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "낫" ], @@ -16014,6 +16657,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "묘목찾기", "줄 베 기", @@ -16080,6 +16727,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "묘목찾기", "모두베기", @@ -16146,6 +16797,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "괭이, 도끼 등" ], @@ -16191,6 +16846,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "기계작업" ], @@ -16229,6 +16888,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "약제살포", "작업보조" @@ -16275,6 +16938,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소금 처리", "2 ~ 6㎝미만", @@ -16345,6 +17012,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "뿌리 고살", "1 ~ 4㎝", @@ -16397,6 +17068,10 @@ "form_basis": "헤더 '할인'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "80% 이상", "60∼80%", @@ -16461,6 +17136,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부 (체인톱 사용)", "유령림 단계", @@ -16515,6 +17194,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0~0.5m", "0.5~1m", @@ -16618,6 +17301,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "11미만", "특별인부", @@ -16672,6 +17359,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특 별 인 부", "보 통 인 부" @@ -16710,8 +17401,12 @@ "source_line": 3330, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10000.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특 별 인 부", "보 통 인 부", @@ -16756,8 +17451,12 @@ "source_line": 3344, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "우 드 칩", "보 통 인 부", @@ -16826,6 +17525,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100m이하", "200m이하", @@ -16872,6 +17575,10 @@ "form_basis": "'ha당'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "ha당원목재적 평균거리", "100m이하", @@ -16940,6 +17647,10 @@ "form_basis": "'ha당'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "ha당원목재적 평균거리", "100m이하", @@ -17033,6 +17744,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "8", "10m이하", @@ -17210,8 +17925,12 @@ "source_line": 3419, "pum_form": "productivity", "form_basis": "헤더 'ha당 집재재적'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "20이하㎥", "0~100m", @@ -17290,6 +18009,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "50m 내외" ], @@ -17333,6 +18056,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "50이하", "0.1", @@ -17460,6 +18187,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "20m이하", "집재량" @@ -17517,6 +18248,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "30이하", "0.1", @@ -17644,6 +18379,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.1∼0.2㎥", "0∼40m", @@ -17720,6 +18459,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "60m 이하", "26㎥" @@ -17768,6 +18511,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "50이하", "0.1", @@ -17895,6 +18642,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100m 이하", "0∼20㎥", @@ -17980,6 +18731,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "120m 이하", "상향집재", @@ -18041,6 +18796,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "120m 이하", "상향집재", @@ -18102,6 +18861,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100m이하", "0.1", @@ -18238,6 +19001,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100m 이하", "0~20㎥", @@ -18320,6 +19087,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100m 이하", "0~20㎥", @@ -18392,6 +19163,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100m 이하", "0~20㎥", @@ -18465,8 +19240,12 @@ "source_line": 3707, "pum_form": "productivity", "form_basis": "헤더 'ha당 평균 작업량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "20㎥ 미만", "작업량" @@ -18511,6 +19290,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "원목의 길이", "1.2m", @@ -18579,6 +19362,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "원목의 길이", "1.2m", @@ -18647,6 +19434,10 @@ "form_basis": "'소요인력'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "원목집재와 동시에 부산물 수집시" ], @@ -18680,6 +19471,10 @@ "form_basis": "'소요인력'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "9㎝ 이하", "1.8m", @@ -18763,6 +19558,10 @@ "form_basis": "'소요인력'", "basis_quantity": 100.0, "basis_unit": "㎥", + "basis_source": "표 안", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "생산목 검척" ], @@ -18787,6 +19586,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "2.1m", "8", @@ -18886,6 +19689,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "1회 적 재 량", "용 재 (원목)", @@ -18966,6 +19773,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100", "회수", @@ -19042,6 +19853,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.5", "작업회수", @@ -19115,6 +19930,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100", "회수", @@ -19191,6 +20010,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100", "회수", @@ -19264,6 +20087,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "100", "회 수", @@ -19355,6 +20182,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "600개 이하", @@ -19447,6 +20278,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "300개 이하", @@ -19521,6 +20356,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10~12", "14~16", @@ -19676,6 +20515,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10~12", "14~16", @@ -19809,6 +20652,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "아바멕틴", "에마멕틴벤조에이트", @@ -19856,6 +20703,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10~12", "14~16", @@ -20011,6 +20862,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10~16", "18~22", @@ -20165,6 +21020,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "티아메톡삼 분산성액제 15%", "디노테퓨란(15)․에마멕틴벤조에이트(4.5) 분산성액제 19.5%", @@ -20219,6 +21078,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -20399,6 +21262,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -20579,6 +21446,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -20769,6 +21640,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "디노테퓨란(15)․에마멕틴벤조에이트(4.5) 분산성액제 19.5%", "이미다클로프리드 분산성액제 20%", @@ -20817,6 +21692,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -20997,6 +21876,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -21177,6 +22060,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -21357,6 +22244,10 @@ "form_basis": "헤더 '할인'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "사업 구분", "1-4-1", @@ -22031,6 +22922,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "공종", "조림", @@ -22633,6 +23528,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "위치 및 면적", "구분", @@ -23229,6 +24128,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "아바멕틴(1.8)·설폭사플로르(4.2) 분산성액제 6%" ], @@ -23253,6 +24156,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -23443,6 +24350,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "테부코나졸 유탁제 25%" ], @@ -23467,6 +24378,10 @@ "form_basis": "값 단위 '(개)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10∼12", "14∼16", @@ -23657,6 +24572,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "6", "8", @@ -23957,6 +24876,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "4cm", "6", @@ -24144,6 +25067,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "중부지방소나무" ], @@ -24178,8 +25105,12 @@ "source_line": 4420, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "8롤 미만", "롤트랩 설치", @@ -24240,6 +25171,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "트랩설치", "트랩통수거 및 교체", @@ -24295,6 +25230,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": 160.0, "basis_unit": "ha", + "basis_source": "표 안", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "소형헬기 (160ha당)", @@ -24398,7 +25337,11 @@ "pum_form": "requirement", "form_basis": "'㏊당'", "basis_quantity": 1.0, - "basis_unit": "㏊", + "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "산림항공방제", "유인헬기", @@ -24473,6 +25416,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소형헬기 (AS350)", "16배", @@ -24657,8 +25604,12 @@ "source_line": 4516, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "인", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "드론조종자 (건설기계 조종원)", "무인헬기 (ha당)", @@ -24738,8 +25689,12 @@ "source_line": 4536, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "인", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "드론조종자 (건설기계 조종원)", "멀티콥터 (ha당)", @@ -24829,8 +25784,12 @@ "source_line": 4557, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "인", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "차량살포 (동력분무기)", @@ -24884,6 +25843,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "250배", "500배", @@ -25026,6 +25989,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "방제실행 등록" ], @@ -25067,8 +26034,12 @@ "source_line": 4595, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10m이하", "1.0RM 이하", @@ -25135,8 +26106,12 @@ "source_line": 4611, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ha", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "10개 이하", "굴 삭 기 우드그랩", @@ -25192,6 +26167,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "잔가지 줍기" ], @@ -25226,6 +26205,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "벌목부", "1㎥", @@ -25301,6 +26284,10 @@ "form_basis": "본문 '㎥/hr'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "이동식 임목 파쇄기" ], @@ -25355,6 +26342,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부" ], @@ -25396,8 +26387,12 @@ "source_line": 4685, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부(인)" ], @@ -25436,6 +26431,10 @@ "form_basis": "행 키가 시공능력 공식 기호뿐 ['E', 'K', 'f', '㎝(sec)']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "K", "f", @@ -25497,6 +26496,10 @@ "form_basis": "헤더 '㎥/hr'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "대형브레이커+ 유압식백호우 (무한궤도,0.7㎥)", "보통암", @@ -25551,6 +26554,10 @@ "form_basis": "행 키가 시공능력 공식 기호뿐 ['E', 'K', 'f', '㎝(sec)']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "K", "f", @@ -25610,8 +26617,12 @@ "source_line": 4746, "pum_form": "requirement", "form_basis": "'수 량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "뇌 관", @@ -25714,6 +26725,10 @@ "form_basis": "헤더 '㎥/hr'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "연 암", "보통암", @@ -25764,6 +26779,10 @@ "form_basis": "행 키가 시공능력 공식 기호뿐 ['E', 'K', 'f', '㎝(sec)']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "K", "f", @@ -25823,8 +26842,12 @@ "source_line": 4786, "pum_form": "productivity", "form_basis": "헤더 '㎥/hr'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "대형브레이커+유압식백호우 (무한궤도,0.7㎥)" ], @@ -25866,8 +26889,12 @@ "source_line": 4803, "pum_form": "productivity", "form_basis": "헤더 '작업능력'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무근콘크리트" ], @@ -25904,8 +26931,12 @@ "source_line": 4834, "pum_form": "productivity", "form_basis": "헤더 '작업능력'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무근콘크리트" ], @@ -25951,8 +26982,12 @@ "source_line": 4851, "pum_form": "productivity", "form_basis": "헤더 '작업능력'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "철근콘크리트" ], @@ -25981,6 +27016,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인력", "보통인부", @@ -26040,8 +27079,12 @@ "source_line": 4878, "pum_form": "productivity", "form_basis": "헤더 '작업능력'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "철근콘크리트" ], @@ -26078,8 +27121,12 @@ "source_line": 4896, "pum_form": "productivity", "form_basis": "헤더 '작업능력'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "메쌓기", "뒷길이60㎝이상", @@ -26141,8 +27188,12 @@ "source_line": 4912, "pum_form": "productivity", "form_basis": "헤더 '작업능력'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트" ], @@ -26177,8 +27228,12 @@ "source_line": 4929, "pum_form": "productivity", "form_basis": "헤더 '작업능력'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "아스팔트" ], @@ -26224,6 +27279,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인력", "보통인부", @@ -26315,8 +27374,15 @@ "source_line": 4978, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력(10%)", "장비(90%)", @@ -26383,8 +27449,15 @@ "source_line": 4994, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -26467,8 +27540,15 @@ "source_line": 5009, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -26560,8 +27640,15 @@ "source_line": 5031, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "장비 (90%)", @@ -26628,8 +27715,15 @@ "source_line": 5045, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "장비 (90%)" @@ -26670,8 +27764,15 @@ "source_line": 5054, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "장비 (90%)" @@ -26712,8 +27813,15 @@ "source_line": 5063, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "장비 (90%)", @@ -26780,8 +27888,15 @@ "source_line": 5075, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "장비 (90%)" @@ -26822,8 +27937,15 @@ "source_line": 5084, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "장비 (90%)" @@ -26864,8 +27986,15 @@ "source_line": 5093, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -26956,8 +28085,15 @@ "source_line": 5108, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27024,8 +28160,15 @@ "source_line": 5120, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27092,8 +28235,15 @@ "source_line": 5132, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "장비 (90%)", @@ -27176,8 +28326,15 @@ "source_line": 5146, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "장비 (90%)", @@ -27236,8 +28393,15 @@ "source_line": 5157, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "장비 (90%)", @@ -27296,8 +28460,15 @@ "source_line": 5168, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27388,8 +28559,15 @@ "source_line": 5183, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27456,8 +28634,15 @@ "source_line": 5195, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27524,8 +28709,15 @@ "source_line": 5207, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27616,8 +28808,15 @@ "source_line": 5222, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27684,8 +28883,15 @@ "source_line": 5234, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": false, "variant_key": [ "인력 (10%)", "보통인부(인)", @@ -27761,8 +28967,15 @@ "source_line": 5248, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력 (10%)", "장비 (90%)", @@ -27831,6 +29044,10 @@ "form_basis": "행 키가 기호뿐 ['A', 'E', 'H', 'N', 'P', 'f']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "A", "N", @@ -27903,6 +29120,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "K(버킷계수)", "f(토량환산계수)", @@ -27955,6 +29176,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "T(표토두께)", "L(운반거리)", @@ -28052,6 +29277,10 @@ "form_basis": "행 키가 시공능력 공식 기호뿐 ['E', 'K', 'f', '㎝(sec)']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "K", "f", @@ -28104,6 +29333,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "V(다짐속도,km/hr)", "W(롤러 유효폭,m)", @@ -28168,6 +29401,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "흡입준비(t1)", "운반(t2)", @@ -28244,6 +29481,10 @@ "form_basis": "행 키가 기호뿐 ['E', 'L', 'V1', 'V2', 'e', 'f', 'q0', 't']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "L", "E", @@ -28320,6 +29561,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "굴착기 (무한궤도, 0.7㎥)", "f", @@ -28383,8 +29628,12 @@ "source_line": 5414, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부 (인)", "모래ㆍ사질토ㆍ점토ㆍ점질토", @@ -28456,8 +29705,12 @@ "source_line": 5430, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인력시공", "모래 또는 사질토", @@ -28514,8 +29767,12 @@ "source_line": 5442, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인력", "장비" @@ -28559,8 +29816,12 @@ "source_line": 5457, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인력", "장비", @@ -28629,8 +29890,12 @@ "source_line": 5489, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "주", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "보통인부", @@ -28686,8 +29951,12 @@ "source_line": 5505, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "주", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "굴착기(무한궤도)" @@ -28731,6 +30000,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "굴착기 (무한궤도)", "보통인부", @@ -28797,6 +30070,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "K(버킷계수)", "f(토량환산계수)", @@ -28858,6 +30135,10 @@ "form_basis": "'구역화물' — 값이 아니라 참조 지시", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "운반비", "하차비" @@ -28896,8 +30177,12 @@ "source_line": 5562, "pum_form": "reference", "form_basis": "'구역화물' — 값이 아니라 참조 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ton", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "운반비", "하차비" @@ -28945,8 +30230,12 @@ "source_line": 5573, "pum_form": "reference", "form_basis": "'별도계상' — 값이 아니라 참조 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "구 입", "운 반" @@ -28985,8 +30274,12 @@ "source_line": 5589, "pum_form": "reference", "form_basis": "'별도계상' — 값이 아니라 참조 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "골재생산", "운반" @@ -29025,8 +30318,12 @@ "source_line": 5600, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "운반비", "하차비" @@ -29067,6 +30364,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "운반", "t2=운반시간 참조", @@ -29220,8 +30521,12 @@ "source_line": 5642, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "토사", "20", @@ -29300,8 +30605,12 @@ "source_line": 5659, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부(인)" ], @@ -29356,6 +30665,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "소 재 (조재목)", "m", @@ -29444,8 +30757,12 @@ "source_line": 5702, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업반장(인)", "특별인부(인)" @@ -29487,8 +30804,12 @@ "source_line": 5713, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업반장(인)", "특별인부(인)", @@ -29537,8 +30858,12 @@ "source_line": 5723, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업반장(인)", "특별인부(인)", @@ -29589,6 +30914,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "차량구분", "단궤도" @@ -29622,6 +30951,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "시간(분)" ], @@ -29648,6 +30981,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부(인)" ], @@ -29674,6 +31011,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "연장", "작업반장", @@ -29758,6 +31099,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "연료비", "보통인부" @@ -29810,6 +31155,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "짐내리는 인부", "콘크리트", @@ -29878,6 +31227,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "운반대" ], @@ -29918,6 +31271,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "보통인부", @@ -29989,6 +31346,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트 블 록", "목제형틀", @@ -30100,6 +31461,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업반장", "보통인부", @@ -30154,6 +31519,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업반장", "보통인부", @@ -30208,6 +31577,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "L", "E", @@ -30322,6 +31695,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "K", "E0", @@ -30368,6 +31745,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "적재(t1)", "t2", @@ -30465,6 +31846,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인원(명)" ], @@ -30507,6 +31892,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인원(명)" ], @@ -30539,6 +31928,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "① 드론준비시간(분)", "② 왕복비행시간(분)", @@ -30600,6 +31993,10 @@ "form_basis": "'준용' — 값이 아니라 참조 지시", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "비계공", "2.4M 3.0M 3.5M 4.8M 6.0M" @@ -30657,6 +32054,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "건축목공", "보통인부" @@ -30697,6 +32098,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "건축목공", "보통인부" @@ -30735,8 +32140,12 @@ "source_line": 6071, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "부설장비", "k", @@ -30821,8 +32230,12 @@ "source_line": 6090, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무근구조물", "철근구조물", @@ -30867,8 +32280,12 @@ "source_line": 6102, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무근구조물", "철근구조물", @@ -30913,8 +32330,12 @@ "source_line": 6115, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무근구조물", "철근구조물", @@ -30949,8 +32370,12 @@ "source_line": 6128, "pum_form": "requirement", "form_basis": "값 단위 '(kg)'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "25", "(B)", @@ -31015,8 +32440,12 @@ "source_line": 6145, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "25", "(B)", @@ -31091,8 +32520,12 @@ "source_line": 6161, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "미 장 공" ], @@ -31117,6 +32550,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무근콘크리트", "8 ~ 12 cm", @@ -31158,6 +32595,10 @@ "form_basis": "행 키가 기호뿐 ['f₁']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "f₁" ], @@ -31186,6 +32627,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "Type-Ⅰ", "Type-Ⅱ", @@ -31223,6 +32668,10 @@ "form_basis": "행 키가 기호뿐 ['f₁']", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "f₁" ], @@ -31249,6 +32698,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "Type-Ⅰ", "Type-Ⅱ", @@ -31279,8 +32732,12 @@ "source_line": 6560, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "시멘트(kg)", "1:2:4", @@ -31335,6 +32792,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "설 치", "비 계 공" @@ -31378,8 +32839,12 @@ "source_line": 6171, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ton", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "철근공(인)", "간 단", @@ -31457,8 +32922,12 @@ "source_line": 6191, "pum_form": "requirement", "form_basis": "'수량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "횟수별", "합 판", @@ -31589,6 +33058,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "문양 스티로폴(자재비 포함)", "설치 및 해체", @@ -31642,6 +33115,10 @@ "form_basis": "'적용한다' — 값이 아니라 참조 지시", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트믹서트럭 직접타설인경우", "포장공", @@ -31719,6 +33196,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "명칭", "특별인부", @@ -31774,6 +33255,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "보통인부" @@ -31813,6 +33298,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "포장두께(㎝)", "형틀목공 보통인부", @@ -31882,8 +33371,12 @@ "source_line": 6299, "pum_form": "requirement", "form_basis": "'수 량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트(레미콘)", "거 푸 집", @@ -31968,8 +33461,12 @@ "source_line": 6316, "pum_form": "requirement", "form_basis": "'수 량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트", "거 푸 집", @@ -32046,8 +33543,12 @@ "source_line": 6331, "pum_form": "requirement", "form_basis": "'수 량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트", "거 푸 집" @@ -32092,8 +33593,12 @@ "source_line": 6340, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "유공관 설치", "배관공", @@ -32211,8 +33716,12 @@ "source_line": 6360, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "∅800mm", "VR관", @@ -32331,8 +33840,12 @@ "source_line": 6381, "pum_form": "requirement", "form_basis": "'수량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "∅800mm", "흄 관", @@ -32431,8 +33944,12 @@ "source_line": 6395, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "절단기", "일반기계운전사", @@ -32493,8 +34010,12 @@ "source_line": 6406, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "∅800mm", "파형강관", @@ -32595,6 +34116,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트 (레미콘)", "다짐:봉상후렉시블(45mm)", @@ -32703,8 +34228,12 @@ "source_line": 6440, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "콘크리트 (레미콘)", "다짐:봉상후렉시블(45mm)", @@ -32797,8 +34326,12 @@ "source_line": 6455, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "파형강관부설", "철 거" @@ -32846,8 +34379,12 @@ "source_line": 6464, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "구체콘크리트 (레미콘)", "다짐:봉상후렉시블(45mm)", @@ -32932,8 +34469,12 @@ "source_line": 6478, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "구체콘크리트 (레미콘)", "봉상후렉시블(45mm)", @@ -33078,6 +34619,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "무근콘크리트", "콘 크 리 트 공", @@ -33154,8 +34699,12 @@ "source_line": 6578, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "구체콘크리트 (철근)", "타설", @@ -33223,8 +34772,12 @@ "source_line": 6603, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ton", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "결속선(R=0.9mm)", "철 근 공", @@ -33282,6 +34835,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "이음철물", @@ -33365,8 +34922,12 @@ "source_line": 6630, "pum_form": "requirement", "form_basis": "분류 딱지 ['인력', '자재']", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "외관(60.6mm×2.3mm)", @@ -33439,8 +35000,12 @@ "source_line": 6644, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "인력", @@ -33492,8 +35057,12 @@ "source_line": 6654, "pum_form": "reference", "form_basis": "분류 딱지 표의 '재료비의' — 값이 아니라 비율 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료비", "설치비" @@ -33532,8 +35101,12 @@ "source_line": 6663, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "잡재료비(재료비의)", @@ -33603,8 +35176,15 @@ "source_line": 6691, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "뒷채움 자재비 및 운반비 별산", "인력 (10%)", @@ -33721,8 +35301,12 @@ "source_line": 6708, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": true, "variant_key": [ "소할(30%)", "적사", @@ -33805,8 +35389,12 @@ "source_line": 6722, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "기계", "디젤엔진(15HP)", @@ -33870,8 +35458,15 @@ "source_line": 6675, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "뒷채움 자재비 및 운반비 별산", "인력 (10%)", @@ -33979,8 +35574,12 @@ "source_line": 6748, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "접착제", @@ -34032,8 +35631,12 @@ "source_line": 6758, "pum_form": "requirement", "form_basis": "분류 딱지 ['자재']", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "인력(설치비)" @@ -34077,8 +35680,12 @@ "source_line": 6767, "pum_form": "requirement", "form_basis": "분류 딱지 ['인력', '자재']", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "시너", @@ -34132,6 +35739,10 @@ "form_basis": "'수량'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료비", "설치비" @@ -34179,8 +35790,12 @@ "source_line": 6786, "pum_form": "reference", "form_basis": "'별도계상' — 값이 아니라 참조 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "철근가공조립(간단)", @@ -34248,8 +35863,12 @@ "source_line": 6798, "pum_form": "reference", "form_basis": "'별도계상' — 값이 아니라 참조 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료", "탄성고무받침", @@ -34359,8 +35978,12 @@ "source_line": 6814, "pum_form": "reference", "form_basis": "분류 딱지 표의 '재료비의' — 값이 아니라 비율 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료비", "설치비" @@ -34399,8 +36022,12 @@ "source_line": 6823, "pum_form": "reference", "form_basis": "분류 딱지 표의 '재료비의' — 값이 아니라 비율 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료비", "설치비" @@ -34439,8 +36066,12 @@ "source_line": 6832, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료", "인력" @@ -34493,8 +36124,12 @@ "source_line": 6843, "pum_form": "reference", "form_basis": "'별도계상' — 값이 아니라 참조 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개소", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "인력", @@ -34562,8 +36197,12 @@ "source_line": 6855, "pum_form": "reference", "form_basis": "분류 딱지 표의 '회기준' — 값이 아니라 비율 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료비", "노무비" @@ -34602,8 +36241,12 @@ "source_line": 6864, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "ton", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "인력", @@ -34679,8 +36322,12 @@ "source_line": 6877, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "재료비" ], @@ -34713,8 +36360,12 @@ "source_line": 6885, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "설치", @@ -34768,6 +36419,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "제작비", "운송비", @@ -34812,8 +36467,12 @@ "source_line": 6905, "pum_form": "reference", "form_basis": "'별도계상' — 값이 아니라 참조 지시", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "인력", @@ -34876,6 +36535,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "패 널 류 보, 드롭헤드, 강관파이프, 훅  클래프, 웨지핀" ], @@ -34906,8 +36569,12 @@ "source_line": 6925, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "패 널", "내 부 패 널", @@ -34955,6 +36622,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "요 율" ], @@ -34991,6 +36662,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "복 잡", "형틀목공 보통인부", @@ -35037,6 +36712,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "복 잡", "보 통", @@ -35104,8 +36783,12 @@ "source_line": 6972, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "규격", "보통인부(인)" @@ -35156,8 +36839,12 @@ "source_line": 6986, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통인부", "㎥당" @@ -35198,8 +36885,12 @@ "source_line": 6995, "pum_form": "requirement", "form_basis": "'수량'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 100.0, + "basis_unit": "개", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작업반장", "굴착기", @@ -35260,6 +36951,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "40㎝이상∼60㎝미만", "뒷길이" @@ -35302,8 +36997,12 @@ "source_line": 7025, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": 0.36, - "basis_unit": "㎥", + "basis_quantity": 1.0, + "basis_unit": "인", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인 부", "㎥당" @@ -35355,6 +37054,10 @@ "form_basis": "값 단위 '(m)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "증가율(%)" ], @@ -35381,8 +37084,12 @@ "source_line": 7094, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "25㎝(17×17)", "30㎝(20×20)", @@ -35456,8 +37163,12 @@ "source_line": 7268, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "35cm 이하", "석공 보통인부", @@ -35512,8 +37223,12 @@ "source_line": 7039, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": 10.0, - "basis_unit": "㎡", + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "모래 기초다짐", "두 께 3㎝", @@ -35576,8 +37291,12 @@ "source_line": 7054, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "(mm)", "기초다짐 뒷채움", @@ -35643,8 +37362,12 @@ "source_line": 7069, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "골쌓기", "석공 (인)", @@ -35835,8 +37558,12 @@ "source_line": 7110, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "35cm 이하", "석공 보통인부", @@ -35891,8 +37618,12 @@ "source_line": 7128, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "야면석(㎥) 깬잡석(㎥) 깬 돌(㎥) 견치돌(㎥)" ], @@ -35935,8 +37666,12 @@ "source_line": 7136, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "골쌓기", "석 공 (인)", @@ -36117,8 +37852,12 @@ "source_line": 7152, "pum_form": "undetermined", "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "야면석(㎥) 호박돌(㎥)", "깬잡석(㎥) 깬 돌(㎥) 견치돌(㎥)" @@ -36167,6 +37906,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "상부의 두께(㎝) 하부의 두께(㎝)" ], @@ -36195,6 +37938,10 @@ "form_basis": "값 단위 '(m)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "∼1.5", "메쌓기(㎝) 찰쌓기(㎝)" @@ -36233,6 +37980,10 @@ "form_basis": "값 단위 '(m)'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "메쌓기", "절토", @@ -36301,8 +38052,12 @@ "source_line": 7200, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "35cm 이하", "석공 보통인부", @@ -36357,8 +38112,12 @@ "source_line": 7219, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "25㎝", "30㎝", @@ -36412,8 +38171,12 @@ "source_line": 7235, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "종 별", "뒷길이 (㎝)", @@ -36488,8 +38251,12 @@ "source_line": 7252, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "35cm 이하", "석공 보통인부", @@ -36553,8 +38320,12 @@ "source_line": 7288, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "직경 40㎝이상 ∼60㎝미만", "작업반장", @@ -36636,8 +38407,12 @@ "source_line": 7312, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "직경 40㎝이상 ∼60㎝미만", "작업반장", @@ -36719,8 +38494,12 @@ "source_line": 7339, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "직경 40㎝이상 ~60㎝미만", "작업반장", @@ -36811,8 +38590,12 @@ "source_line": 7367, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "직경 40㎝이상 ~60㎝미만", "작업반장", @@ -36894,8 +38677,12 @@ "source_line": 7390, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "직경 40㎝이상 ∼60㎝미만", "작업반장", @@ -36979,6 +38766,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "뒷길이 평균 적용" ], @@ -37015,8 +38806,12 @@ "source_line": 7429, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "ton", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "인 력", "석 공", @@ -37077,8 +38872,12 @@ "source_line": 7449, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "개", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "직경 15㎝ 길이 4m" ], @@ -37113,8 +38912,12 @@ "source_line": 7460, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통 인부 (인)" ], @@ -37145,8 +38948,12 @@ "source_line": 7471, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "개", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보 통 인 부 (인)" ], @@ -37197,6 +39004,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "계수" ], @@ -37231,6 +39042,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "8 9", "12", @@ -37387,8 +39202,12 @@ "source_line": 7520, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조약돌량(㎥)", "인력(인)", @@ -37434,8 +39253,12 @@ "source_line": 7534, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조립 설치", "보통인부", @@ -37520,8 +39343,12 @@ "source_line": 7551, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조약돌량(㎥)", "인력(인)", @@ -37583,8 +39410,12 @@ "source_line": 7565, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "조립 설치", "보통인부", @@ -37674,8 +39505,12 @@ "source_line": 7581, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "특별인부", "보통인부" @@ -37717,8 +39552,12 @@ "source_line": 7596, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "자재", "채움재", @@ -37813,6 +39652,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통토사", "절 취(㎥)", @@ -37873,6 +39716,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "작 업 반 장", "특 별 인 부", @@ -37909,6 +39756,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "4m 표준", "4 ~ 10m", @@ -37970,8 +39821,12 @@ "source_line": 7652, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보통구조", "중", @@ -38046,8 +39901,15 @@ "source_line": 7680, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": { + "인력": 10.0, + "장비": 90.0 + }, + "partial_ratio": true, + "capacity_formula_here": true, "variant_key": [ "인력(10%)", "장비(90%)", @@ -38099,8 +39961,12 @@ "source_line": 7691, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "식생매트설치", "특 별 인 부", @@ -38150,8 +40016,12 @@ "source_line": 7701, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎥", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "간 단 구 조", "보 통 구 조" @@ -38195,6 +40065,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "단 끊 기", "흙채우기(마대채우기)", @@ -38248,8 +40122,12 @@ "source_line": 7732, "pum_form": "requirement", "form_basis": "직종 표기((인)·인부·공)", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "m", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "보 통 인 부" ], @@ -38300,6 +40178,10 @@ "form_basis": "헤더·첫 행에 단위·밑수·직종 표지 없음", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "어려운 조건", "쉬운 조건" @@ -38333,6 +40215,10 @@ "form_basis": "직종 표기((인)·인부·공)", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "어려운 조건", "쉬운 조건" @@ -38389,8 +40275,12 @@ "source_line": 7766, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 1.0, + "basis_unit": "㎡", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "사 면", "도로/철도", @@ -38487,6 +40377,10 @@ "form_basis": "헤더 '단 위'", "basis_quantity": null, "basis_unit": null, + "basis_source": null, + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "폭 1.5m 이하", "조 경 공", @@ -38547,8 +40441,12 @@ "source_line": 7802, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.8㎥ 굴착기(우드그랩 부착)", "보통인부" @@ -38590,8 +40488,12 @@ "source_line": 7813, "pum_form": "reference", "form_basis": "헤더 '단 위'", - "basis_quantity": null, - "basis_unit": null, + "basis_quantity": 10.0, + "basis_unit": "본", + "basis_source": "본문", + "resource_shares": {}, + "partial_ratio": false, + "capacity_formula_here": false, "variant_key": [ "0.8㎥ 굴착기", "보통인부" diff --git a/ui_template/ui_template_locale_b2.ts b/ui_template/ui_template_locale_b2.ts index c4897bec..1c9317e4 100644 --- a/ui_template/ui_template_locale_b2.ts +++ b/ui_template/ui_template_locale_b2.ts @@ -728,6 +728,11 @@ export const ui_locales_b2 = { B08_Quantity_Tab_Haul: ["운반거리", "Haul Distance"], B08_Quantity_Tab_UnitQuantity: ["구조물 원단위", "Structure Unit Quantity"], B08_Quantity_Tab_Material: ["자재총괄", "Material Summary"], + B08_Quantity_Tab_Preparation: ["준비공·사방공", "Preparation & Erosion Control"], + B08_Quantity_Side_Method_Label: ["시공법", "Method"], + B08_Quantity_Method_Unset: ["안 정함", "Not set"], + B08_Quantity_Method_Ripping: ["긁어내기(암절취)", "Ripping"], + B08_Quantity_Method_Blasting: ["터뜨리기(발파암)", "Blasting"], B08_Quantity_Material_Failed: [ "자재총괄을 불러오지 못했습니다.", "Failed to load the material summary.",