Files
Aislo/resources/tester/test_masterdata_procure.py
T
eomsangdonandClaude Sonnet 5 a905edd87f feat(master_data): 관급 다산 줄 우선 채움 · 못 채움 800 → 495 · 못 채운 까닭 비고
- 아스콘 지역 포함(시군 붙은 글 끊기) · R3-H/A1-H 규격 번호 떼기 · 파형강관 ×t·1RS·PE 피복 · 교량받침·합성목재·혼합골재 제조사 괄호 짝
- 못 채운 495줄 비고에 까닭(조달청 종합쇼핑몰에 없음 · 짝 못 지음) · 경질폴리염화비닐관 받기
- 다산 4,552줄 기준 채움 4,057 · 맞음 3,442 · 다름 615 · 못 채움 495 · 로직 일괄 시험 멈춤 327 그대로
- 시험 13건 · 설계 문서 대조표 갱신

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

205 lines
7.8 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
def test_교량받침_이름_괄호_제조사와_관급_꼬리():
up_raw = {
"MASCntrct_교량받침": [
raw_row(
"교량받침, 부흥시스템, BHS-C-700-110-F, 250×300×60mm, 700kN, 고정단, 일체형탄성받침",
"전지역",
599000,
maker="부흥시스템",
),
raw_row(
"교량받침, 타사, X-700-F, 250×300×60mm, 700kN, 고정단",
"전지역",
500000,
maker="타사",
),
]
}
x = item("m", "교량받침(부흥시스템/예산)-관급", "250×300×60mm,700kN,고정단")
(r,) = up.refill([x], up_raw)
assert up.decide(x, r, "2026-09-20")[0] == 599000 # 더 싼 타사가 아니라 이름의 제조사
def test_파형강관_t표기와_PE피복_글자():
def cor(spec, price):
return raw_row(spec, "전지역", price, maker="가")
raw = {
"MASCntrct_파형강관": [
cor("파형강관, 가, A, Φ300×t1.6mm, 직관(1RS), PE내면피복", 33820),
cor("파형강관, 가, B, Φ300×t1.6mm, 1RS, PE양면피복", 36310),
cor("파형강관, 가, C, Φ300×t1.6mm, 1RS, 아연도금", 31870),
]
}
_, (val, _) = fill_raw(item("n", "파형강관(관급)-거양", "Φ300×t1.6mm, 1RS, PE양면피복"), raw)
assert val == 36310
def fill_raw(x, raw):
(r,) = up.refill([x], raw)
return r, up.decide(x, r, "2026-09-20")