Merge remote-tracking branches 'origin/sub_laptop_1' and 'origin/sub_laptop_3' into main_laptop_1

This commit is contained in:
2026-09-22 14:08:07 +09:00
14 changed files with 100 additions and 57 deletions
+4 -2
View File
@@ -1,5 +1,5 @@
{
"다음": {"LB": 409, "EQ": 655, "RT": 20, "MN": 7000, "MT": 30710, "MO": 74, "MP": 364, "FX": 6, "CF": 159, "QF": 477, "GF": 252, "CC": 341, "GC": 1104, "QC": 2060, "GX": 1, "UA": 2},
"다음": {"LB": 409, "EQ": 655, "RT": 20, "MN": 7000, "MT": 30712, "MO": 74, "MP": 364, "FX": 6, "CF": 159, "QF": 477, "GF": 252, "CC": 341, "GC": 1104, "QC": 2060, "GX": 1, "UA": 2},
"폐기": {"MT000001": "자재품목 휘발유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000002": "자재품목 경유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000003": "자재품목 선박용경유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000004": "자재품목 중유 — 값이 비고 글뿐 · 유가 테이블로 감", "MT000005": "자재품목 등유 — 값이 비고 글뿐 · 유가 테이블로 감", "LB000399": "이름 「〃」 — 직종이 아니라 되풀이 기호 · 소요량 13-6-9 에서 윗줄 직종으로 바로잡음", "MN": "재료_나라장터자재 — old 로 옮김 · 로직은 자재품목·품셈재료로 갈아 끼움", "LB000372": "미확보 고급기능사 — 측량노임 항공사진고급기능사 옛이름으로 옮김", "LB000393": "미확보 리베팅공 — 건설노임 철골공 옛이름으로 옮김(2010 통합표 연번10)", "LB000405": "미확보 Belt Conveyor 설치공 — 품셈 13-10-1 주② 배분으로 다섯 직종에 나눔", "MT000006": "자재품목 전력 — 재료_유가전력 전력 줄로 감", "MT000007": "자재품목 전력 — 재료_유가전력 전력 줄로 감", "LB000368": "미확보 기술사 — 기술사(건설) 옛이름으로 옮김", "LB000369": "미확보 특급기술자 — 특급기술자(건설) 옛이름으로 옮김", "LB000370": "미확보 고급기술자 — 고급기술자(건설) 옛이름으로 옮김", "LB000361": "미확보 중급기술자 — 중급기술자(건설) 옛이름으로 옮김", "LB000364": "미확보 초급기술자 — 초급기술자(건설) 옛이름으로 옮김", "LB000365": "미확보 고급숙련기술자 — 고급숙련기술자(건설) 옛이름으로 옮김", "LB000366": "미확보 중급숙련기술자 — 중급숙련기술자(건설) 옛이름으로 옮김", "LB000367": "미확보 초급숙련기술자 — 초급숙련기술자(건설) 옛이름으로 옮김", "LB000389": "미확보 기계기사 — 초급기술자(기계·설비) 옛이름으로 옮김", "LB000388": "미확보 기계산업기사 — 초급숙련기술자(기계·설비) 옛이름으로 옮김", "MO000055": {"원문번호": "주택용저압:기본_200이하", "파일": "재료_유가전력.json"}, "MO000056": {"원문번호": "주택용저압:기본_201~400", "파일": "재료_유가전력.json"}, "MO000057": {"원문번호": "주택용저압:기본_400초과", "파일": "재료_유가전력.json"}, "MO000059": {"원문번호": "주택용저압:전력량_다음200", "파일": "재료_유가전력.json"}, "MO000060": {"원문번호": "주택용저압:전력량_400초과", "파일": "재료_유가전력.json"}, "MO000061": {"원문번호": "주택용저압:월간최저요금", "파일": "재료_유가전력.json"}, "MO000062": {"원문번호": "일반용갑I저압:기본", "파일": "재료_유가전력.json"}, "MO000063": {"원문번호": "일반용갑I저압:전력량_여름철", "파일": "재료_유가전력.json"}, "MO000064": {"원문번호": "일반용갑I저압:전력량_봄가을철", "파일": "재료_유가전력.json"}, "MO000065": {"원문번호": "일반용갑I저압:전력량_겨울철", "파일": "재료_유가전력.json"}, "MO000066": {"원문번호": "산업용갑I저압:기본", "파일": "재료_유가전력.json"}, "MO000067": {"원문번호": "산업용갑I저압:전력량_여름철", "파일": "재료_유가전력.json"}, "MO000068": {"원문번호": "산업용갑I저압:전력량_봄가을철", "파일": "재료_유가전력.json"}, "MO000069": {"원문번호": "산업용갑I저압:전력량_겨울철", "파일": "재료_유가전력.json"}, "MO000070": {"원문번호": "임시전력갑:기본요금적용", "파일": "재료_유가전력.json"}, "MO000071": {"원문번호": "임시전력갑:전력량요금적용", "파일": "재료_유가전력.json"}, "MO000072": {"원문번호": "임시전력갑:월간최저요금", "파일": "재료_유가전력.json"}},
"키": {
"CC000001": {"원문번호": "공통 1-3-1 콘크리트 및 포장용 재료", "파일": "계수_건설품셈_01장_적용기준.json"},
@@ -36582,6 +36582,8 @@
"MT030706": {"원문번호": "품셈재료 MP000003 전국", "파일": "재료_자재품목.json"},
"MT030707": {"원문번호": "품셈재료 MP000003 지역별", "파일": "재료_자재품목.json"},
"MT030708": {"원문번호": "품셈재료 MP000005 전국", "파일": "재료_자재품목.json"},
"MT030709": {"원문번호": "품셈재료 MP000005 지역별", "파일": "재료_자재품목.json"}
"MT030709": {"원문번호": "품셈재료 MP000005 지역별", "파일": "재료_자재품목.json"},
"MT030710": {"원문번호": "품셈재료 MP000209", "파일": "재료_자재품목.json"},
"MT030711": {"원문번호": "품셈재료 MP000214", "파일": "재료_자재품목.json"}
}
}
@@ -2,7 +2,7 @@
`scripts/build_자재품목_분류.py` 가 만든 표 — 손으로 고치지 않음.
- 구분 24 · 상세구분 100 · 미분류 25 줄 (전체 30702 줄)
- 구분 24 · 상세구분 100 · 미분류 25 줄 (전체 30704 줄)
## 규칙
@@ -42,9 +42,9 @@ PE관 2907 · 이음쇠·부속 1003 · 주철관 828 · 강관 705 · PVC관 46
볼트·너트 526 · 와이어로프·클램프 208 · 앵커 189 · 못 41 · 철근커플러 38
### 전기·통신 (900)
### 전기·통신 (901)
케이블·전선 319 · 판넬·차단기 163 · 전선관·트레이 139 · 조명·전등 133 · 제어·계측기기 89 · 부속·기타 57
케이블·전선 319 · 판넬·차단기 164 · 전선관·트레이 139 · 조명·전등 133 · 제어·계측기기 89 · 부속·기타 57
### 밸브·수도자재 (761)
@@ -58,9 +58,9 @@ PE관 2907 · 이음쇠·부속 1003 · 주철관 828 · 강관 705 · PVC관 46
화물운임 552 · 시험·검사 62 · 폐기물 61 · 상하차 2
### 기계·설비 (626)
### 기계·설비 (627)
기계류 267 · 보일러·공조 172 · 놀이·운동·시설물 109 · 측정·시험기기 68 · 소방·가스 10
기계류 267 · 보일러·공조 173 · 놀이·운동·시설물 109 · 측정·시험기기 68 · 소방·가스 10
### 맨홀·수로·그레이팅 (602)
@@ -1,12 +1,14 @@
# -*- coding: utf-8 -*-
"""로직 호표의 재료 줄을 「고르기 조건」 으로 바꿈 — 요소 「MP……」·「MT……」 를 조건 묶음으로.
· 바탕
· 바탕
① 재료_품셈재료.json 의 「고르기」 칸 (`scripts/build_품셈재료_고르기.py` 가 채움)
② 자재품목 줄을 바로 가리키는 「MT……」 — 그 줄의 구분·상세구분·규격 + 대표 = 그 줄
③ 연결이 자재품목 키 하나인 품셈재료 「MP……」 — 그 자재품목 줄로 ② 와 같게
④ 연결이 지역·계약종별 조건 묶음(모래 · 자갈 · 전력)인 품셈재료 — 그 묶음을 호표에
그대로 박음(PLAN 7-4b 뒤 절반 · `master_material._region`·`_tariff` 가 품셈재료
간접 없이 바로 풂)
②③ 은 대표 줄이 옛 요소와 같은 줄이라 금액이 그대로임(고르기 화면만 열림)
· 고르기 칸이 비고 연결도 지역·계약종별을 계산 때 푸는 줄(모래 · 자갈 · 전력)은 옛 방식 그대로 둠
· 줄 텍스트의 「"요소": "MP……"」 한 줄만 바꿈 — 나머지 글자·차례 그대로
· 다시 돌려도 같은 결과(이미 바뀐 줄은 건드릴 것이 없음)
@@ -38,12 +40,18 @@ def _cond_of(row: dict) -> dict | None:
def _picks() -> dict[str, dict]:
"""요소 키 → 고르기 조건 — 품셈재료 고르기 칸 · 자재품목 줄 · 연결이 키 하나인 품셈재료."""
"""요소 키 → 고르기 조건 — 품셈재료 고르기 칸 · 자재품목 줄 · 연결이 키 하나인 품셈재료 ·
연결이 지역·계약종별 조건 묶음인 품셈재료(그 묶음을 그대로 호표에 박음 — PLAN 7-4b 뒤 절반)."""
market = {str(r[""]): r for r in json.loads(MARKET.read_text(encoding="utf-8"))[""]}
picks = {key: cond for key, row in market.items() if (cond := _cond_of(row))}
for row in json.loads(SRC.read_text(encoding="utf-8"))[""]:
link = row.get("연결")
cond = row.get("고르기") or (picks.get(link) if isinstance(link, str) else None)
if isinstance(link, str):
cond = row.get("고르기") or picks.get(link)
elif isinstance(link, dict) and (link.get("지역") == "입력" or link.get("계약종별") == "입력"):
cond = link
else:
cond = row.get("고르기")
if cond:
picks[str(row[""])] = cond
return picks
@@ -16,7 +16,7 @@ LINKED = "MP" # 품셈재료 테이블ID
MARKET = "MT" # 자재품목 테이블ID
PRICED = "MO" # 유가전력 테이블ID — 값 한 칸
SLOTS = ("물가자료", "유통물가", "물가정보", "거래가격", "관급") # 자재품목 값 열
PICK_KEYS = ("구분", "상세구분", "이름", "규격", "대표") # 재료 고르기 조건 칸 (`_틀.md` 7장)
PICK_KEYS = ("구분", "상세구분", "이름", "규격", "대표", "지역", "계약종별") # 재료 고르기 조건 칸 (`_틀.md` 7장)
REGION = "자재지역" # 로직 입력 — 「지역」 은 유가 고르기가 씀
TARIFF = "전력 계약종별" # 로직 입력 — 품셈·계약예규에 계약종별 규정 없음
_UNITS = {"ea": "", "ton": "", "t": "", "l": "", "리터": "", "set": "", "세트": ""}
@@ -64,11 +64,11 @@ def _market_index(master: mf.Master) -> list[tuple[str, str, str, str, str]]:
return master._market
def _tariff(master: mf.Master, ref: str, row: dict, env: dict) -> list[str]:
"""후보 조건{이름 · 계약종별: 입력} — 유가전력 전력 줄 가운데 그 계약종별·이름으로 시작하는 줄."""
cond = row["연결"]
def _tariff(master: mf.Master, label_ref, cond: dict, env: dict) -> list[str]:
"""후보 조건{이름 · 계약종별: 입력} — 유가전력 전력 줄 가운데 그 계약종별·이름으로 시작하는 줄.
`label_ref` 는 알림 글에 쓸 자리(호표 줄의 고르기 조건 · 또는 품셈재료 옛 키)."""
if TARIFF not in env:
raise mf.FormulaError(f"{master.label(ref)}」 입력 「{TARIFF}」 없음")
raise mf.FormulaError(f"{master.label(label_ref)}」 입력 「{TARIFF}」 없음")
want, kind = plain(cond["이름"]), plain(env[TARIFF])
hits = [
key
@@ -79,29 +79,26 @@ def _tariff(master: mf.Master, ref: str, row: dict, env: dict) -> list[str]:
and one.get("") is not None
]
if not hits:
raise mf.FormulaError(f"{master.label(ref)}」 계약종별 값 없음 — {env[TARIFF]}")
raise mf.FormulaError(f"{master.label(label_ref)}」 계약종별 값 없음 — {env[TARIFF]}")
return hits
def _candidates(master: mf.Master, ref: str, row: dict, env: dict) -> list[str]:
def _region(master: mf.Master, label_ref, cond: dict, env: dict, unit) -> list[str]:
"""후보 조건{이름 · 규격 · 지역: 입력} — 뿌리가 이름으로 끝나고 이름에 지역 글자 · 단위 같음."""
cond = row["연결"]
if cond.get("계약종별") == "입력":
return _tariff(master, ref, row, env)
if cond.get("지역") == "입력" and REGION not in env:
raise mf.FormulaError(f"{master.label(ref)}」 입력 「{REGION}」 없음")
raise mf.FormulaError(f"{master.label(label_ref)}」 입력 「{REGION}」 없음")
region = plain(env.get(REGION, "")) if cond.get("지역") == "입력" else ""
want, spec, unit = plain(cond["이름"]), plain_spec(cond.get("규격")), plain_unit(row["단위"])
want, spec, unit_p = plain(cond["이름"]), plain_spec(cond.get("규격")), plain_unit(unit)
hits = [
(s, key)
for root, name, s, u, key in _market_index(master)
if root.endswith(want) and region in name and u == unit and (not spec or s == spec)
if root.endswith(want) and region in name and u == unit_p and (not spec or s == spec)
]
hits = [(s, key) for s, key in hits if _offers(master, key)]
if not hits:
raise mf.FormulaError(f"{master.label(ref)}」 지역 값 없음 — {env.get(REGION)}")
raise mf.FormulaError(f"{master.label(label_ref)}」 지역 값 없음 — {env.get(REGION)}")
if len({s for s, _ in hits}) > 1:
raise mf.FormulaError(f"{master.label(ref)}」 후보 여럿 — 규격 선택 필요 ({len(hits)}줄)")
raise mf.FormulaError(f"{master.label(label_ref)}」 후보 여럿 — 규격 선택 필요 ({len(hits)}줄)")
return [key for _, key in hits]
@@ -183,8 +180,18 @@ def check_unit(master: mf.Master, where: str, cond: dict, unit: str | None) -> l
def check_pick(master: mf.Master, where: str, cond: dict, unit: str | None = None) -> list[str]:
"""고르기 조건 검사 — 칸 이름 · 구분·상세구분이 자재품목에 있는지 · 대표 줄 · 후보 0.
값에 `{입력}` 이 든 칸(관경 등)은 계산 때 채워지므로 검사에서 뺌."""
if set(cond) - set(PICK_KEYS) or not cond.get("구분"):
값에 `{입력}` 이 든 칸(관경 등)은 계산 때 채워지므로 검사에서 뺌.
지역·계약종별 조건은 구분 없이 이름 하나로 자재품목·유가전력을 넓게 찾음(_region · _tariff)
— 여기서는 이름 칸만 있는지 봄(후보 0 은 계산 때 판단)."""
if set(cond) - set(PICK_KEYS):
return [f"{where} · 재료 고르기 조건 모양 「{cond}」 — {' · '.join(PICK_KEYS)}"]
if ("지역" in cond and cond["지역"] != "입력") or (
"계약종별" in cond and cond["계약종별"] != "입력"
):
return [f"{where} · 재료 고르기 조건 모양 「{cond}」 — 지역·계약종별은 늘 「입력」"]
if cond.get("지역") == "입력" or cond.get("계약종별") == "입력":
return [] if cond.get("이름") else [f"{where} · 재료 고르기 조건 「{cond}」 — 이름 없음"]
if not cond.get("구분"):
return [f"{where} · 재료 고르기 조건 모양 「{cond}」 — {' · '.join(PICK_KEYS)}"]
cond = static(cond)
if not cond.get("구분"): # 구분 자체가 `{입력}` — 계산 때 채워져 여기서 좁힐 것이 없음
@@ -207,7 +214,17 @@ def element(
master: mf.Master, ref, env: dict, unit: str | None = None
) -> tuple[object, str | None]:
"""요소 값과 출처 — 자재품목은 제 줄, 품셈재료는 연결을 따라가 낮은 값 · 그 밖은 줄의 `값`.
`ref` 가 고르기 조건 묶음이면 대표 줄(없으면 단위 같은 조건 안 첫 줄)로 풂."""
`ref` 가 고르기 조건 묶음이면 대표 줄(없으면 단위 같은 조건 안 첫 줄)로 풂.
조건에 지역·계약종별 이 「입력」 이면 구분·상세구분 없이 이름 하나로 넓게 찾음(_region · _tariff)
— 호표 줄에 바로 써도 품셈재료 간접 없이 풀림(PLAN 7-4b 뒤 절반)."""
if isinstance(ref, dict) and (ref.get("계약종별") == "입력" or ref.get("지역") == "입력"):
keys = (
_tariff(master, ref, ref, env)
if ref.get("계약종별") == "입력"
else _region(master, ref, ref, env, unit)
)
offers = [x for key in keys for x in _offers(master, key)]
return min(offers, key=lambda x: x[0]) if offers else (None, None)
if isinstance(ref, dict):
ref = pick(master, ref, env, unit)
row = master.get(ref)
@@ -218,7 +235,12 @@ def element(
link = row.get("연결")
if not link:
return None, None
keys = [link] if isinstance(link, str) else _candidates(master, ref, row, env)
if isinstance(link, str):
keys = [link]
elif link.get("계약종별") == "입력":
keys = _tariff(master, ref, link, env)
else:
keys = _region(master, ref, link, env, row["단위"])
offers = [x for key in keys for x in _offers(master, key)]
return min(offers, key=lambda x: x[0]) if offers else (None, None)
@@ -838,7 +838,7 @@
},
{
"종류": "재료",
"요소": "MP000209",
"요소": {"구분": "기계·설비", "상세구분": "보일러·공조", "규격": "압접가열용", "대표": "MT030710"},
"이름": "버너 압접가열용",
"단위": "개",
"수량": "찾기(QC000648, 품명='버너', 레일규격=레일규격, 규격='압접가열용').수량",
@@ -941,7 +941,7 @@
},
{
"종류": "재료",
"요소": "MP000214",
"요소": {"구분": "전기·통신", "상세구분": "판넬·차단기", "규격": "테르밋 용접용", "대표": "MT030711"},
"이름": "퓨즈",
"단위": "개",
"수량": "찾기(QC000650, 품명='퓨즈', 레일규격=레일규격).수량",
@@ -1189,7 +1189,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "구멍땜 모래",
"단위": "㎥",
"수량": "구멍땜 * 찾기(QC000430).모래 / 100",
@@ -60,7 +60,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "모래",
"단위": "㎥",
"수량": "찾기(QC001347, 배합용적비=배합용적비).모래",
@@ -533,7 +533,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "모래",
"단위": "㎥",
"수량": "찾기(QC001358, 구분='모래').수량",
@@ -2415,7 +2415,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "찾기(QC001638, '두께(㎜)'=두께, '자세 및 직종'=자세, 구분='소요전력(㎾h)').수량",
@@ -2521,7 +2521,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "찾기(QC001641, '두께(㎜)'=두께, '자세 및 직종'=자세, 구분='전력소비량(㎾h)').수량",
@@ -2635,7 +2635,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "찾기(QC001642, '두께(㎜)'=두께, '자세 및 직종'=자세, 구분='소요전력(㎾h)').수량",
@@ -2744,7 +2744,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "찾기(QC001639, '자세 및 직종 두께(㎜)'=두께, 구분=만약(용접면 == '하향한면', '하향한면 용접', '하향양면 용접')).'소요전력(㎾h)'",
@@ -2853,7 +2853,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "찾기(QC001640, '자세 및 직종 두께(㎜)'=두께, 구분=만약(용접면 == '하향한면', '하향한면 용접', '하향양면 용접')).'소요전력(㎾h)'",
@@ -8736,7 +8736,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "모래",
"단위": "㎥",
"수량": "0.262",
@@ -8763,7 +8763,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "370",
@@ -9041,7 +9041,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "모래",
"단위": "㎥",
"수량": "0.242",
@@ -9068,7 +9068,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "310",
@@ -10289,7 +10289,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "모래",
"단위": "㎥",
"수량": "0.242",
@@ -10316,7 +10316,7 @@
},
{
"종류": "재료",
"요소": "MP000358",
"요소": {"이름": "전력량요금", "계약종별": "입력"},
"이름": "전력",
"단위": "㎾h",
"수량": "306",
@@ -2499,7 +2499,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "모래부설(또는 양질토사)",
"단위": "㎥",
"수량": "모래량",
@@ -3944,7 +3944,7 @@
},
{
"종류": "재료",
"요소": "MP000004",
"요소": {"이름": "모래", "규격": "", "지역": "입력"},
"이름": "모래",
"단위": "㎥",
"수량": "찾기(QF000385, 배합비=배합비).모래",
@@ -3952,7 +3952,7 @@
},
{
"종류": "재료",
"요소": "MP000006",
"요소": {"이름": "자갈", "규격": "", "지역": "입력"},
"이름": "자갈",
"단위": "㎥",
"수량": "찾기(QF000385, 배합비=배합비).자갈",
@@ -30722,6 +30722,8 @@
{ "키": "MT030706", "구분": "시멘트·골재", "상세구분": "골재", "이름": "모래(건조상태·전국)", "규격": "건조상태", "단위": "kg", "물가자료": null, "유통물가": null, "물가정보": null, "거래가격": null, "관급": null, "출처": "품셈재료 MP000003 · 산림품셈 12-1-3 주⑤", "비고": "품셈재료에서 옮김 · 값 미확보 · 전국 대표 값 자리(지역 갈림은 아래 줄)", "면수": null },
{ "키": "MT030707", "구분": "시멘트·골재", "상세구분": "골재", "이름": "모래(건조상태·지역별)", "규격": "건조상태", "단위": "kg", "물가자료": null, "유통물가": null, "물가정보": null, "거래가격": null, "관급": null, "출처": "품셈재료 MP000003 · 산림품셈 12-1-3 주⑤", "비고": "품셈재료에서 옮김 · 값 미확보 · 지역마다 값이 다름 — 필요한 지역마다 관리자가 줄을 더 만들어야 함", "면수": null },
{ "키": "MT030708", "구분": "시멘트·골재", "상세구분": "골재", "이름": "자갈(건조상태·전국)", "규격": "", "단위": "kg", "물가자료": null, "유통물가": null, "물가정보": null, "거래가격": null, "관급": null, "출처": "품셈재료 MP000005 · 산림품셈 12-1-3 주⑤", "비고": "품셈재료에서 옮김 · 값 미확보 · 전국 대표 값 자리(지역 갈림은 아래 줄)", "면수": null },
{ "키": "MT030709", "구분": "시멘트·골재", "상세구분": "골재", "이름": "자갈(건조상태·지역별)", "규격": "", "단위": "kg", "물가자료": null, "유통물가": null, "물가정보": null, "거래가격": null, "관급": null, "출처": "품셈재료 MP000005 · 산림품셈 12-1-3 주⑤", "비고": "품셈재료에서 옮김 · 값 미확보 · 지역마다 값이 다름 — 필요한 지역마다 관리자가 줄을 더 만들어야 함", "면수": null }
{ "키": "MT030709", "구분": "시멘트·골재", "상세구분": "골재", "이름": "자갈(건조상태·지역별)", "규격": "", "단위": "kg", "물가자료": null, "유통물가": null, "물가정보": null, "거래가격": null, "관급": null, "출처": "품셈재료 MP000005 · 산림품셈 12-1-3 주⑤", "비고": "품셈재료에서 옮김 · 값 미확보 · 지역마다 값이 다름 — 필요한 지역마다 관리자가 줄을 더 만들어야 함", "면수": null },
{ "키": "MT030710", "구분": "기계·설비", "상세구분": "보일러·공조", "이름": "버너", "규격": "압접가열용", "단위": "개", "물가자료": null, "유통물가": null, "물가정보": null, "거래가격": null, "관급": null, "출처": "품셈재료 MP000209 · 건설품셈 토목 4-5-1 가스압접 소모재료(참고자료)", "비고": "PLAN 7-4b: 자재품목 후보 1줄(MT009352 개장로용 가스버너, 915만원)은 산업용 화덕이라 다른 물건 — 못 이어 새 줄 · 값 미확보", "면수": null },
{ "키": "MT030711", "구분": "전기·통신", "상세구분": "판넬·차단기", "이름": "퓨즈", "규격": "테르밋 용접용", "단위": "개", "물가자료": null, "유통물가": null, "물가정보": null, "거래가격": null, "관급": null, "출처": "품셈재료 MP000214 · 건설품셈 토목 4-5-2 테르밋 용접 소모재료(참고자료)", "비고": "PLAN 7-4b: 자재품목 후보 다섯(전선퓨즈·통형퓨즈·고리퓨즈)이 모두 전기용이라 다른 물건 — 못 이어 새 줄 · 값 미확보", "면수": null }
]
}
@@ -217,12 +217,12 @@
{ "키": "MP000206", "원문번호": "바퀴숫돌 측면용", "이름": "바퀴숫돌", "규격": "측면용 A24 QWV1호 A205×25×25 KSL 6501", "단위": "개", "요구절": ["건설품셈 토목 4-5-1 가스압접 소모재료(참고자료)"], "연결": null, "고르기": null, "비고": "고르기 없음(자재품목 그 묶음에 같은 단위·이름 줄이 없음)" },
{ "키": "MP000207", "원문번호": "바퀴숫돌 평면용", "이름": "바퀴숫돌", "규격": "평면용 A24 QWV1호 A205×25×25 KSL 6501", "단위": "개", "요구절": ["건설품셈 토목 4-5-1 가스압접 소모재료(참고자료)"], "연결": null, "고르기": null, "비고": "고르기 없음(자재품목 그 묶음에 같은 단위·이름 줄이 없음)" },
{ "키": "MP000208", "원문번호": "바퀴숫돌 최종용", "이름": "바퀴숫돌", "규격": "최종용 A24 QWV 5호 A205×22×22", "단위": "개", "요구절": ["건설품셈 토목 4-5-1 가스압접 소모재료(참고자료)"], "연결": null, "고르기": null, "비고": "고르기 없음(자재품목 그 묶음에 같은 단위·이름 줄이 없음)" },
{ "키": "MP000209", "원문번호": "압접가열용 버너", "이름": "버너", "규격": "압접가열용", "단위": "개", "요구절": ["건설품셈 토목 4-5-1 가스압접 소모재료(참고자료)"], "연결": null, "고르기": null, "비고": "자재품목 후보 1줄 — 지역·규격 선택 필요 (MT009352) · 고르기 없음(자재품목 묶음에 짝이 없음 — 관리자가 화면에서 이음)" },
{ "키": "MP000209", "원문번호": "압접가열용 버너", "이름": "버너", "규격": "압접가열용", "단위": "개", "요구절": ["건설품셈 토목 4-5-1 가스압접 소모재료(참고자료)"], "연결": "MT030710", "고르기": null, "비고": "PLAN 7-4b: 후보 1줄(개장로용 가스버너, 915만원)은 산업용 화덕이라 다른 물건 — 새 줄(MT030710)로 이음 · 값 미확보" },
{ "키": "MP000210", "원문번호": "압접버너용 노즐", "이름": "노즐", "규격": "압접버너용", "단위": "개", "요구절": ["건설품셈 토목 4-5-1 가스압접 소모재료(참고자료)"], "연결": "MT030627", "고르기": null, "비고": "PLAN 7-4b: 자재품목 새 줄(MT030627)로 이음 · 값 미확보" },
{ "키": "MP000211", "원문번호": "테르밋용재", "이름": "테르밋용재", "규격": "점화용", "단위": "포", "요구절": ["건설품셈 토목 4-5-2 테르밋 용접 소모재료(참고자료)"], "연결": "MT030628", "고르기": null, "비고": "PLAN 7-4b: 자재품목 새 줄(MT030628)로 이음 · 값 미확보" },
{ "키": "MP000212", "원문번호": "테르밋 몰드", "이름": "몰드", "규격": "테르밋 용접용", "단위": "개", "요구절": ["건설품셈 토목 4-5-2 테르밋 용접 소모재료(참고자료)"], "연결": "MT030629", "고르기": null, "비고": "PLAN 7-4b: 자재품목 새 줄(MT030629)로 이음 · 값 미확보" },
{ "키": "MP000213", "원문번호": "테르밋 골무", "이름": "골무", "규격": "테르밋 용접용", "단위": "개", "요구절": ["건설품셈 토목 4-5-2 테르밋 용접 소모재료(참고자료)"], "연결": "MT030630", "고르기": null, "비고": "PLAN 7-4b: 자재품목 새 줄(MT030630)로 이음 · 값 미확보" },
{ "키": "MP000214", "원문번호": "테르밋 퓨즈", "이름": "퓨즈", "규격": "테르밋 용접용", "단위": "개", "요구절": ["건설품셈 토목 4-5-2 테르밋 용접 소모재료(참고자료)"], "연결": null, "고르기": null, "비고": "자재품목 후보 5줄 — 지역·규격 선택 필요 (MT009288, MT009289, MT009290 외) · 고르기 없음(자재품목 묶음에 짝이 없음 — 관리자가 화면에서 이음)" },
{ "키": "MP000214", "원문번호": "테르밋 퓨즈", "이름": "퓨즈", "규격": "테르밋 용접용", "단위": "개", "요구절": ["건설품셈 토목 4-5-2 테르밋 용접 소모재료(참고자료)"], "연결": "MT030711", "고르기": null, "비고": "PLAN 7-4b: 후보 다섯(전선퓨즈·통형퓨즈·고리퓨즈)이 모두 전기용이라 다른 물건 — 새 줄(MT030711)로 이음 · 값 미확보" },
{ "키": "MP000215", "원문번호": "강교 용접봉", "이름": "용접봉", "규격": "강교 제작용", "단위": "㎏", "요구절": ["건설품셈 토목 5-1-3 기타재료 소요량"], "연결": null, "고르기": {"구분": "잡재료", "상세구분": "용접·가스"}, "비고": "자재품목 후보 12줄 — 지역·규격 선택 필요 (MT003186, MT003187, MT003188 외)" },
{ "키": "MP000216", "원문번호": "철구(Shot ball)", "이름": "철구(Shot ball)", "규격": "", "단위": "㎏", "요구절": ["건설품셈 토목 5-2-1 소재 표면처리", "건설품셈 기계설비 9-2-1"], "연결": "MT030631", "고르기": null, "비고": "PLAN 7-4b: 자재품목 새 줄(MT030631)로 이음 · 값 미확보" },
{ "키": "MP000217", "원문번호": "무기질아연말샵프라이머", "이름": "무기질아연말 샵프라이머", "규격": "도막두께 20μm", "단위": "", "요구절": ["건설품셈 토목 5-2-1 소재 표면처리"], "연결": null, "고르기": null, "비고": "고르기 없음(자재품목 그 묶음에 같은 단위·이름 줄이 없음)" },
+9 -3
View File
@@ -384,11 +384,17 @@ def test_재료_고르기_줄_단가는_대표_자재품목의_낮은_값(client
def test_전력은_계약종별을_골라야_값이_섬(client: TestClient) -> None:
"""후보 조건 「계약종별: 입력」 — 화면에선 값 없음 · 계산 때 로직 입력으로 정해짐."""
"""후보 조건 「계약종별: 입력」(PLAN 7-4b 뒤 절반 — 품셈재료 간접 없이 호표에 바로 박음) —
계산은 로직 입력으로 값이 (mf.element 직접 확인). 화면 단가 미리보기(_pick_brief)
구분·상세구분 자리로만 후보를 찾아 지역·계약종별 조건은 아직 다뤄 None 아는 구멍."""
one = _get(client, "/api/m01/logic", key=key_of("GC000893"))
power = one["prices"][key_of("MP:전")]
assert power[""] is None and power["출처"] is None
cond = {"이름": "전력량요금", "계약종별": ""}
assert one["prices"][store.cm.mf.cond_text(cond)] is None
assert "전력 계약종별" in {x["이름"] for x in one["logic"]["입력"]}
with pytest.raises(store.cm.mf.FormulaError, match="전력 계약종별"):
store.cm.mf.element(store.cm.master(), cond, {})
value, source = store.cm.mf.element(store.cm.master(), cond, {"전력 계약종별": "주택용 저압"})
assert value is not None and source
def test_하위_거름과_참조_이름(client: TestClient) -> None:
+2 -2
View File
@@ -951,8 +951,8 @@ def test_품셈재료_고르기_칸_검사():
def test_고르기_조건_글자_열쇠():
"""화면 단가 미리보기가 조건 줄을 찾는 이름 — 빈 칸은 빈 글자."""
assert mm.cond_text({"구분": "관·수로", "상세구분": "흄관"}) == "관·수로|흄관|||"
assert mm.cond_text({"구분": "레미콘", "대표": "MT900002"}) == "레미콘||||MT900002"
assert mm.cond_text({"구분": "관·수로", "상세구분": "흄관"}) == "관·수로|흄관|||||"
assert mm.cond_text({"구분": "레미콘", "대표": "MT900002"}) == "레미콘||||MT900002||"
def test_품셈재료_고르기_칸이_실파일에_있음():
@@ -70,6 +70,9 @@ PM = json.loads((MD / "재료_품셈재료.json").read_text(encoding="utf-8"))["
"색소": "도료·방수·접착",
"에폭시": "도료·방수·접착",
"땜납": "잡재료",
# PLAN 7-4b 뒤 절반 ④ — 버너·퓨즈는 후보가 물건 자체가 달라 못 이어 새 줄을 만듦
"버너": "기계·설비",
"퓨즈": "전기·통신",
# PLAN 7-4b 뒤 절반 ① — 자재품목에 짝 없던 재료 106종을 새 줄로 만들어 이음(값 미확보)
"3-Bond": "잡재료",
"BASECHANNEL": "건축마감",