- 아스콘 지역 포함(시군 붙은 글 끊기) · 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
205 lines
7.8 KiB
Python
205 lines
7.8 KiB
Python
"""관급 단가 짝짓기 — 작은 견본 응답으로 레미콘 지역·규격 · 아스콘 · 값 갈림 보류 · 지역 불일치 미채움 (`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")
|