Files
Aislo/resources/tester/test_masterdata_procure.py
T
eomsangdonandClaude Sonnet 5 e44365365b feat(master_data): 관급 제조사별 최저가 채움 · 조달청 2차 받기 · 짝짓기 갈래 · 새 줄 380
- 제조사별로 값이 갈리면 같은 규격 최저가 + 비고에 제조사 (보류 394 채움)
- 조달청 분류 23개 더 받음 (원본 gz 보관) · 시멘트 · 조경석 · 주철관 · 이음관 · 골재 · 측구수로관 · 볼라드 · 맨홀 · 용접철망 · 돌망태 · 매트 짝짓기
- 조달청에만 있는 금속돌망태 372 · 토목용부직포 8 을 자재품목에 새 줄로 들임 (키대장 갱신)
- 다산 값 대조 기준 ±5% · 짝짓기 시험 11건 · 설계 문서 갱신

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
2026-09-20 18:06:15 +09:00

163 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""관급 단가 짝짓기 — 작은 견본 응답으로 레미콘 지역·규격 · 아스콘 · 값 갈림 보류 · 지역 불일치 미채움 (`resources/master_data/scripts/update_관급단가.py`)."""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "master_data/scripts"))
import update_관급단가 as up # noqa: E402
def raw_row(
spec, region, price, corp="테스트조합", maker="조합공통품목", cond="", bgn="2026-06-01"
):
return {
"prdctSpecNm": spec,
"prdctSplyRgnNm": region,
"cntrctPrceAmt": str(price),
"cntrctCorpNm": corp,
"prdctMakrNm": maker,
"prdctDlvryCndtnNm": cond,
"cntrctBgnDate": bgn,
"rgstDt": bgn,
"cntrctMthdNm": "다수 공급자 계약",
"vatAplDivNm": "포함",
"prdctUnit": "㎥",
}
def item(key, name, spec):
return {"키": key, "이름": name, "규격": spec, "관급": 1, "비고": "조달 26.7"}
RAW = {
"MASCntrct_레미콘": [
raw_row("조합공통품목, 25-18-080", "경기도 수원시", 100000),
raw_row("조합공통품목, 25-18-080", "강원도 원주시", 90000),
raw_row("조합공통품목, 25-21-080", "경기도 수원시", 105000),
raw_row("삼표, 25-18-080", "경기도 수원시", 1, maker="삼표"), # 제조사 줄 — 조합공통 아님
],
"MASCntrct_아스팔트콘크리트": [
raw_row("아스팔트콘크리트 조합공통품목, WC-2, 가열, 3등급", "경기도 수원시", 70000),
raw_row("순환 아스팔트콘크리트 조합공통품목, WC-2, 가열, 3등급", "경기도 수원시", 60000),
],
"MASCntrct_시멘트": [
raw_row("시멘트, 갑, 포틀랜드시멘트(1종), 포장40kg", "전지역(제주제외)", 5820, maker="갑"),
raw_row("시멘트, 갑, 포틀랜드시멘트(1종), 포장40kg", "제주", 6470, maker="갑"),
],
"MASCntrct_조경석": [
raw_row(
"조경석, 을산업, B-1, 300×400×500mm, R5이상~15미만, 보령성주석",
"충남",
29980,
maker="을산업",
),
raw_row(
"조경석, 병산업, C-1, 300×400×500mm, R5이상~15미만, 함양석",
"충남",
20000,
maker="병산업",
),
],
"Ucntrct_주철관": [
raw_row("수도용덕타일주철관, Φ80mm×6m, 2종, KP메커니컬/시멘트라이닝", "전지역", 148240),
raw_row("하수도용덕타일주철관, Φ80mm×6m, 3종, KP메커니컬/시멘트라이닝", "전지역", 118940),
],
"MASCntrct_봉강": [
raw_row("SD400, D10", "전지역(제주제외)", 827640, maker="가철강", cond="생산공장 상차도"),
raw_row("SD400, D10", "전지역(제주제외)", 830000, maker="나철강", cond="생산공장 상차도"),
],
}
def fill(x):
(r,) = up.refill([x], RAW)
return r, up.decide(x, r, "2026-09-20")
def test_레미콘_지역_규격_짝():
r, (val, note) = fill(item("a", "레미콘(관급)-경기 수원", "조합공통품목, 25-18-80")) # 80 = 080
assert val == 100000 and "조달청" in note
r, (val, _) = fill(item("b", "레미콘(관급)-경기 수원", "조합공통품목, 25-21-080"))
assert val == 105000
def test_레미콘_지역_불일치는_안_채움():
r, (val, note) = fill(item("c", "레미콘(관급)-울진", "조합공통품목, 25-18-080"))
assert val is None and "지역 못 찾음" in note
def test_레미콘_규격_없으면_안_채움():
r, (val, note) = fill(item("d", "레미콘(관급)-경기 수원", "조합공통품목, 30-24-150"))
assert val is None and "강도 없음" in note
def test_아스콘_순환_구분():
_, (val, _) = fill(item("e", "아스콘(관급)-경기 수원", "조합공통품목, WC-2, 가열, 3등급"))
assert val == 70000
_, (val, _) = fill(item("f", "순환아스콘(관급)-경기 수원", "조합공통품목, WC-2, 가열, 3등급"))
assert val == 60000
def test_제조사별_값_갈리면_최저_제조사_비고():
_, (val, note) = fill(item("g", "이형철근(SD400)-관급", "HD=10mm 생산공장 상차도"))
assert val == 827640 and "가철강 최저" in note
def test_짝_없는_갈래는_안_채움():
_, (val, note) = fill(item("h", "벤치플륨(관급)", "300"))
assert val is None and "짝짓기 없음" in note
def test_호출_오류에_키_안_찍힘(monkeypatch):
def boom(url, **kw):
raise OSError(f"연결 실패 {url}")
monkeypatch.setattr(up.cc, "fetch", boom)
try:
up.call("op", "SECRETKEY123", a=1)
except RuntimeError as e:
assert "SECRETKEY123" not in str(e) and "***" in str(e)
else:
raise AssertionError("예외가 나야 함")
def test_시멘트_제주는_제주_줄만():
_, (val, _) = fill(item("i", "시멘트 (관급)", "40kg入(포장품)"))
assert val == 5820
_, (val, _) = fill(item("j", "시멘트, 제주 (관급)", "40kg入(포장품)"))
assert val == 6470
def test_조경석_산지가_규격에_있으면_그_줄만():
spec = "300×400×500,R5이상~15미만,보령성주석(을산업)"
_, (val, _) = fill(item("k", "조경석(관급)", spec))
assert val == 29980 # 더 싼 병산업 줄이 아니라 산지·업체가 맞는 줄
def test_주철관_종별이_맞는_줄만():
_, (val, _) = fill(item("l", "KP 주철직관(2종)시멘트라이닝/관급", "D80mm×6m"))
assert val == 148240 # 3종(118,940)은 안 씀
def test_조달청에만_있는_품목은_같은_규격_최저가로_새_줄():
import update_관급단가_기타 as misc
def r(price, maker):
x = raw_row(
f"금속돌망태, {maker}, M-1, 1000×1000×1000mm, 사각상자형", "전지역", price, maker=maker
)
x["prdctClsfcNoNm"] = "금속돌망태"
return x
raw = {"MASCntrct_돌망태": [r(46800, "가"), r(44000, "나")]}
g = misc.groups(raw)
assert list(g) == ["조달청 금속돌망태 1000×1000×1000mm, 사각상자형"]
name, spec, rows = g["조달청 금속돌망태 1000×1000×1000mm, 사각상자형"]
val, note = up.decide(
None,
{"새값": 44000, "까닭": up.pick(rows)[1], "조달청": up.info(up.pick(rows)[0])},
"2026-09-20",
)
assert val == 44000 and "나 최저" in note