feat(802-2a): GC000100 암파쇄(유압식 할암공법) 수식 문서 넣음 — 단가산출 부름 값 = 끝수 뒤 채택(요구 714-3)

- 빌더 ADOPTED(채택된 차이) — GC000100 지역=경북 재료비 1 원 · 목록에 남기고 저장은 막지 않음 · 요약에 「채택된 차이」
- 수식 문서 1,518 → 1,519(건설품셈 03장 토공사 파일에 GC000100 49 줄 더함 · 다른 문서 그대로)
- test_802_2 기대 바꿈(채택 차이 · 저장 1,519) · test_802_3 「빈 문서」 길은 임시 사본에서 GC000100 문서를 지워 봄(낡은 기대: GC000100 문서 없음)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-09 13:19:17 +09:00
co-authored by Claude Sonnet 5.5
parent 22588ea7c4
commit 2094436577
5 changed files with 79 additions and 10 deletions
Binary file not shown.
@@ -12,7 +12,9 @@
· 계(돈 아닌 로직 = 결과)
원 단위 — 기본 옵션 전부 + 옵션 표본(로직마다 고르기 입력의 다른 갈래 하나씩 · 나머지 기본).
둘 다 못 셈 = 같음(따로 셈). 보정 없음 — 다르면 목록에 까닭.
- 저장 = 같은 것만(요구서_664 1 장 3) — 모든 대조가 같고 부르는 단가산출도 저장되는 문서 ·
- 채택된 차이(`ADOPTED`) = 사용자 요구 714-3 「1원 차이는 로직만 맞다면 문제 없음」 — 식 속 단가산출
부름 값 = 끝수 뒤(새 계산기 그대로) · 목록에 남기고 저장은 막지 않음.
- 저장 = 같은 것만(요구서_664 1 장 3) — 모든 대조가 같고(채택된 차이 빼고) 부르는 단가산출도 저장되는 문서 ·
`resources/master_data/수식문서/수식_<옛 로직 파일 꼬리>.json`(로직 파일마다
· 옛 로직 JSON 은 안 씀).
- 목록 = `docs/raw/요구_1104/_목록_802-2_변환대조.xlsx`(로직 · 옵션 · 옛 · 새 · 차이 · 까닭 + 요약).
@@ -44,6 +46,8 @@ RAW_SUB = (
"식 속 단가산출 부름 = 끝수 뒤 값"
"(옛 엔진 = 끝수 전 날 값 · `master_formula.run` 결과를 식에 씀)"
)
# 채택된 차이 — (로직, 옵션) · 요구 714-3
ADOPTED = {("GC000100", "지역=경북")}
def rows_of(files: dict) -> list[tuple[str, dict]]:
@@ -108,7 +112,7 @@ def why(doc: dict, old, new) -> str:
if isinstance(new, str) and not isinstance(old, str):
return f"새 계산기 못 셈 — {new}"
if _uses_raw_sub(doc):
return RAW_SUB
return RAW_SUB + " · 채택(요구 714-3)"
return "까닭 모름"
@@ -151,6 +155,7 @@ def build(log=print) -> dict:
)
for one in runs:
if not one["같음"]:
one["채택"] = (one["로직"], one["옵션"]) in ADOPTED
one["까닭"] = why(docs[one["로직"]], one["옛"], one["새"])
keep = _keep(docs, runs)
for one in runs: # 같아도 못 넣는 것 — 부르는 단가산출이 빠짐
@@ -165,6 +170,7 @@ def build(log=print) -> dict:
"옵션 표본": sum(1 for x in runs if x["옵션"]),
"옵션 같음": sum(1 for x in runs if x["옵션"] and x["같음"]),
"옵션 다름": sum(1 for x in runs if x["옵션"] and not x["같음"]),
"채택된 차이": sum(1 for x in runs if x.get("채택")),
"둘 다 못 셈": both_out,
"저장": len(keep),
"변환 알림": sum(1 for v in notes.values() if v),
@@ -192,7 +198,7 @@ def _calls(doc: dict) -> set[str]:
def _keep(docs: dict, runs: list[dict]) -> set[str]:
"""저장할 문서 — 모든 대조 같음 + 부르는 단가산출도 저장(닫힘).
키 구멍 부름은 그 로직 대조로 판정."""
keep = set(docs) - {x["로직"] for x in runs if not x["같음"]}
keep = set(docs) - {x["로직"] for x in runs if not x["같음"] and not x.get("채택")}
while True:
drop = {k for k in keep if not _calls(docs[k]) <= keep}
if not drop:
@@ -641,6 +641,55 @@
"끝전": "비목버림"
}
},
{
"꼴": "수식문서",
"판": 1,
"키": "GC000100",
"이름": "암파쇄(유압식 할암공법)",
"결과단위": "원/㎥",
"출처": "건설품셈 공통 3-3-8",
"변수": [
{"열쇠": "지역", "기호": "S", "아래": "지역", "위": "", "이름": "지역", "단위": "", "출처": "건설품셈 공통 8-1-7 5", "연결": {"갈래": "선택", "목록": ["전국평균", "전국", "서울", "경기", "강원", "충북", "충남", "전북", "경북", "경남", "부산", "제주", "대구", "인천", "대전", "울산", "세종", "전남광주"], "값": "전국평균"}},
{"열쇠": "보정작업", "기호": "S", "아래": "보정작업", "위": "", "이름": "보정작업", "단위": "", "출처": "건설품셈 공통 8-1-7 1", "연결": {"갈래": "선택", "목록": ["해당 없음", "암석작업(연암・보통암・경암)", "전석섞인토사"], "값": "해당 없음"}},
{"열쇠": "드릴경비", "기호": "C", "아래": "드릴", "위": "", "이름": "드릴경비", "단위": "", "연결": {"갈래": "식"}},
{"열쇠": "브레이커경비", "기호": "C", "아래": "브레이커", "위": "", "이름": "브레이커경비", "단위": "", "연결": {"갈래": "식"}},
{"열쇠": "P_기계시간당", "기호": "P", "아래": "기계시간당", "위": "", "이름": "기계 시간당 기계경비(손료 보정 없는 기종)", "단위": "원/시간", "연결": {"갈래": "단가산출", "키": "GC000268", "입력": {"기계": "'5405-0110'", "지역": "S_지역"}, "칸": "계"}},
{"열쇠": "Q_유압식크롤드^표", "기호": "Q", "아래": "유압식크롤드", "위": "표", "이름": "암파쇄(유압식 할암공법).유압식크롤러드릴", "단위": "hr", "연결": {"갈래": "표", "표": "QC000201", "조건": {}, "칸": "유압식크롤러드릴"}},
{"열쇠": "P_굴착기무한궤", "기호": "P", "아래": "굴착기무한궤", "위": "", "이름": "굴착기(무한궤도)+대형브레이커 시간당 기계경비", "단위": "원/시간", "연결": {"갈래": "단가산출", "키": "GC000271", "입력": {"기계": "'0201-0100'", "브레이커": "'0230-0010'", "지역": "S_지역", "보정작업": "S_보정작업"}, "칸": "계"}},
{"열쇠": "Q_굴착기대형브^표", "기호": "Q", "아래": "굴착기대형브", "위": "표", "이름": "암파쇄(유압식 할암공법).굴착기_대형브레이커", "단위": "hr", "연결": {"갈래": "표", "표": "QC000201", "조건": {}, "칸": "굴착기_대형브레이커"}},
{"열쇠": "Q_기계설비공^표", "기호": "Q", "아래": "기계설비공", "위": "표", "이름": "암파쇄(유압식 할암공법).기계설비공", "단위": "인", "연결": {"갈래": "표", "표": "QC000201", "조건": {}, "칸": "기계설비공"}},
{"열쇠": "w_기계설비공", "기호": "w", "아래": "기계설비공", "위": "", "이름": "기계설비공노임", "단위": "", "연결": {"갈래": "요소", "키": "LB000051"}},
{"열쇠": "Q_특별인부^표", "기호": "Q", "아래": "특별인부", "위": "표", "이름": "암파쇄(유압식 할암공법).특별인부", "단위": "인", "연결": {"갈래": "표", "표": "QC000201", "조건": {}, "칸": "특별인부"}},
{"열쇠": "w_특별인부", "기호": "w", "아래": "특별인부", "위": "", "이름": "특별인부노임", "단위": "", "연결": {"갈래": "요소", "키": "LB000003"}},
{"열쇠": "P_기계시간당2", "기호": "P", "아래": "기계시간당2", "위": "", "이름": "기계 시간당 기계경비(손료 보정 없는 기종)", "단위": "원/시간", "연결": {"갈래": "단가산출", "키": "GC000268", "입력": {"기계": "'5405-0110'", "지역": "S_지역"}}},
{"열쇠": "Q_발전기^표", "기호": "Q", "아래": "발전기", "위": "표", "이름": "암파쇄(유압식 할암공법).발전기", "단위": "hr", "연결": {"갈래": "표", "표": "QC000201", "조건": {}, "칸": "발전기"}},
{"열쇠": "P_기계시간당3", "기호": "P", "아래": "기계시간당3", "위": "", "이름": "기계 시간당 기계경비(손료 보정 없는 기종)", "단위": "원/시간", "연결": {"갈래": "단가산출", "키": "GC000268", "입력": {"기계": "'7505-0025'", "지역": "S_지역"}}},
{"열쇠": "Q_유압식할암기^표", "기호": "Q", "아래": "유압식할암기", "위": "표", "이름": "암파쇄(유압식 할암공법).유압식할암기", "단위": "hr", "연결": {"갈래": "표", "표": "QC000201", "조건": {}, "칸": "유압식할암기"}},
{"열쇠": "P_기계시간당4", "기호": "P", "아래": "기계시간당4", "위": "", "이름": "기계 시간당 손료(연료·조종원 없는 기종)", "단위": "원/시간", "연결": {"갈래": "단가산출", "키": "GC000273", "입력": {"기계": "'5501-0080'"}}},
{"열쇠": "P_굴착기무한2", "기호": "P", "아래": "굴착기무한2", "위": "", "이름": "굴착기(무한궤도)+대형브레이커 시간당 기계경비", "단위": "원/시간", "연결": {"갈래": "단가산출", "키": "GC000271", "입력": {"기계": "'0201-0100'", "브레이커": "'0230-0010'", "지역": "S_지역", "보정작업": "S_보정작업"}}},
{"열쇠": "Q_기계경비의^표", "기호": "Q", "아래": "기계경비의", "위": "표", "이름": "소모자재 요율.기계경비의", "단위": "%", "연결": {"갈래": "표", "표": "QC000202", "조건": {"장비": "'유압식 크롤러드릴'"}, "칸": "기계경비의"}},
{"열쇠": "Q_기계경비의2^표", "기호": "Q", "아래": "기계경비의2", "위": "표", "이름": "소모자재 요율.기계경비의", "단위": "%", "연결": {"갈래": "표", "표": "QC000202", "조건": {"장비": "'굴착기+대형브레이커'"}, "칸": "기계경비의"}}
],
"식": [
{"기호": "C_드릴", "식": "P_기계시간당 × Q_유압식크롤드^표"},
{"기호": "C_브레이커", "식": "P_굴착기무한궤 × Q_굴착기대형브^표"}
],
"금액": [
{"이름": "기계설비공", "단위": "인", "비목": "노무비", "수량": "Q_기계설비공^표", "단가": "w_기계설비공", "묶음": "인력"},
{"이름": "특별인부", "단위": "인", "비목": "노무비", "수량": "Q_특별인부^표", "단가": "w_특별인부", "묶음": "인력"},
{"이름": "유압식 크롤러드릴 110㎾", "단위": "시간", "비목": "묶음", "수량": "Q_유압식크롤드^표", "단가": "P_기계시간당2", "묶음": "유압식 크롤러드릴 110㎾"},
{"이름": "발전기 25㎾", "단위": "시간", "비목": "묶음", "수량": "Q_발전기^표", "단가": "P_기계시간당3", "묶음": "발전기 25㎾"},
{"이름": "유압식할암기 ∅80", "단위": "시간", "비목": "묶음", "수량": "Q_유압식할암기^표", "단가": "P_기계시간당4", "묶음": "유압식할암기 ∅80"},
{"이름": "굴착기+대형브레이커 1.0㎥", "단위": "시간", "비목": "묶음", "수량": "Q_굴착기대형브^표", "단가": "P_굴착기무한2", "묶음": "굴착기+대형브레이커 1.0㎥"},
{"이름": "소모자재(크롤러드릴)", "단위": "식", "비목": "재료비", "단가": "C_드릴 × Q_기계경비의^표 ÷ 100", "출처": "건설품셈 공통 3-3-8 주④", "묶음": "소모자재(크롤러드릴) · 소모자재(대형브레이커)"},
{"이름": "소모자재(대형브레이커)", "단위": "식", "비목": "재료비", "단가": "C_브레이커 × Q_기계경비의2^표 ÷ 100", "출처": "건설품셈 공통 3-3-8 주④", "묶음": "소모자재(크롤러드릴) · 소모자재(대형브레이커)"}
],
"끝수": {
"줄": {"방법": "버림", "자리": 1},
"비목": {"방법": "버림", "자리": 0},
"끝전": "비목버림"
}
},
{
"꼴": "수식문서",
"판": 1,
+16 -6
View File
@@ -4,7 +4,7 @@
① --check — 다시 돌린 결과 = 저장본(`resources/master_data/수식문서/`) · 목록 xlsx.
② 로직 1,519 전수 기본 옵션 = 옛 엔진 노 · 재 · 경 · 계(결과) 원 단위 같음.
③ 옵션 표본 — 로직마다 고르기 입력의 모든 갈래 한 번 이상 · 같음 수 · 다름 = 목록 · 까닭 모름 0.
④ 저장 = 같은 것만(부르는 단가산출까지) · 저장본을 읽어 다시 셈해도 같음.
④ 저장 = 같은 것만(부르는 단가산출까지 · 채택된 차이 GC000100 지역=경북 재료비 1 원 빼고) · 저장본을 읽어 다시 셈해도 같음.
⑤ 791 실무 손 옮김 견본 넷(번지 · 단계 · 합밖 · 줄 자리 · 끝전 · 조정 · 값묶음)도 변환 셈 = 엑셀 금액.
"""
@@ -56,7 +56,7 @@ def test_옵션_표본_모든_갈래_한_번_이상(result):
assert (row["키"], f"{spec['이름']}={opt}") in seen
info = result["요약"]
assert info["옵션 표본"] == want == info["옵션 같음"] + info["옵션 다름"]
assert info["옵션 같음"] >= 18700 and info["옵션 다름"] <= 1
assert info["옵션 같음"] >= 18700 and info["옵션 다름"] == info["채택된 차이"] == 1
def _whole():
@@ -69,15 +69,25 @@ def test_다름_목록_까닭(result):
assert sheet[0] == list(build.HEAD) and sheet[1:] == rows
diff = [x for x in result["대조"] if not x["같음"]]
assert all(x["까닭"] != "까닭 모름" for x in diff)
assert [(x["로직"], x["옵션"], x["까닭"]) for x in diff] == [
("GC000100", "지역=경북", build.RAW_SUB)
]
# 채택된 차이(요구 714-3) — 식 속 단가산출 부름 값 = 끝수 뒤 · 재료비 1 원 · 저장은 함
assert [(x["로직"], x["옵션"], x["채택"]) for x in diff] == [("GC000100", "지역=경북", True)]
assert diff[0]["까닭"].startswith(build.RAW_SUB)
assert diff[0]["옛"]["재료비"] != diff[0]["새"]["재료비"]
assert abs(diff[0]["새"]["재료비"] - diff[0]["옛"]["재료비"]) == 1
assert {k: diff[0]["옛"][k] for k in ("노무비", "경비")} == {
k: diff[0]["새"][k] for k in ("노무비", "경비")
}
def test_저장_같은_것만_다시_셈(result):
saved = ml.load_all()
assert set(saved) == result["저장"]
assert "GC000100" not in saved and len(saved) == result["요약"]["저장"] >= 1518
assert "GC000100" in saved and len(saved) == result["요약"]["저장"] >= 1519
base = next(x for x in result["대조"] if x["로직"] == "GC000100" and not x["옵션"])
assert (
base["같음"]
and build.new_answer(ml.Links(_whole(), saved), saved["GC000100"]) == base["옛"]
)
for key in ("GC000211", "GC001147", "GF000294", "GC000087"):
doc = saved[key]
got = build.new_answer(ml.Links(_whole(), saved), doc)
+5 -1
View File
@@ -67,7 +67,11 @@ def test_읽기_미리셈_연결기록(client):
def test_없는문서_빈문서_없는키_404(client):
fresh = _read(client, "GC000100") # 802-2a 로 문서 안 넣음
assert (
_read(client, "GC000100")["새"] is False
) # 802-2a 로 문서 넣음 — 임시 사본에서 지워 빈 문서 길을 봄
doc_screen.drop("GC000100")
fresh = _read(client, "GC000100")
assert fresh["새"] is True and fresh["문서"]["식"] == [] and fresh["문서"]["금액"] == []
assert fresh["셈"]["셈됨"] is False and fresh["셈"]["계"] is None
assert client.get("/api/m01/logic/doc", params={"key": "GX999999"}).status_code == 404