- update_관급단가_느슨.py 신설 — 엄격한 짝이 안 선 줄을 갈래 안에서 규격 글자를 느슨하게 맞춰 다시 봄 (아스콘 · H형강 · HI-VP · PE · VR관 · 돌망태 · 무게수로관 뚜껑 · 벤치플룸 · 유공관 · 이음관) - 아스콘 관리번호 A3-W 떼기 · 못 채운 까닭 비고를 지금 말로 다듬음 - 자재품목 관급 열 212줄 채움(다른 열·줄 그대로) · 시험 · 설계 문서 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8
485 lines
21 KiB
Python
485 lines
21 KiB
Python
"""관급 단가 짝짓기 — 작은 견본 응답으로 레미콘 지역·규격 · 아스콘 · 값 갈림 보류 · 지역 불일치 미채움 (`resources/master_data/scripts/update_관급단가.py`)."""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "master_data/scripts"))
|
||
|
||
import update_관급단가 as up # noqa: E402
|
||
import update_관급단가_느슨 as slow # 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"}
|
||
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def no_estx(monkeypatch):
|
||
"""다산 옛 값(원문 estx)은 안 읽음 — 느슨한 짝은 값을 주는 시험만 켬."""
|
||
monkeypatch.setattr(slow, "old_of", lambda key: None)
|
||
|
||
|
||
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", "KP 접합부속(관급)", "D80mm"))
|
||
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")
|
||
|
||
|
||
def test_벤치플룸_표기_달라도_모델_치수_종별로_짝():
|
||
def bp(spec, price, region="전지역(제주,도서지역 제외)"):
|
||
return raw_row(f"철근콘크리트벤치플룸관, 가, {spec}", region, price, maker="가")
|
||
|
||
raw = {
|
||
"MASCntrct_플룸": [
|
||
bp("500C, 500×500×2000mm, 3종", 97100),
|
||
bp("500C, 500×500×2000mm, 3종", 86900, "전북(도서지역 제외)"),
|
||
bp("500C, 500×500×2000mm, 2종", 50000),
|
||
bp("(부품)440×80×500mm, 수로관뚜껑", 1),
|
||
bp("300, 300×200×2000mm, 2종", 40000),
|
||
]
|
||
}
|
||
_, (val, _) = fill_raw(item("o", "벤치플륨관(3종)(관급)-전북", "500C(500*500)"), raw)
|
||
assert val == 86900 # 3종만 · 전북 줄 중 최저 · 부품 줄 아님
|
||
_, (val, _) = fill_raw(item("p", "벤치플륨관(2종)", "300×200, 140kg"), raw)
|
||
assert val == 40000 # kg 는 조달청 표기에 없음
|
||
|
||
|
||
def test_소형고압블록_형_두께_색():
|
||
def sb(spec, price, region="전지역(제주,도서 산간지역 제외)"):
|
||
return raw_row(f"보차도용콘크리트블록, 가, X, {spec}", region, price)
|
||
|
||
raw = {
|
||
"MASCntrct_블록": [
|
||
sb("220×110×60mm, 소형고압블록, 불투수", 7000), # 색 안 적힘 = 회색
|
||
sb("220×110×60mm, 소형고압블록, 불투수, 컬러", 11000),
|
||
sb("220×110×60mm, 잡초방지소형고압블록, 불투수", 1),
|
||
sb("200×100×60mm, 소형고압블록, 불투수", 6500),
|
||
]
|
||
}
|
||
name = "소형고압블록(관급)-대구경북" # 붙여 쓴 두 도
|
||
assert fill_raw(item("q", name, "U형 T=6cm, 회색"), raw)[1][0] == 7000
|
||
assert fill_raw(item("r", name, "U형 T=6cm, 칼라"), raw)[1][0] == 11000
|
||
assert fill_raw(item("s", name, "I형 T=6cm, 회색"), raw)[1][0] == 6500
|
||
assert fill_raw(item("t", name, "U형 T=6cm, 적색"), raw)[1][0] is None
|
||
|
||
|
||
def test_무게수로관_모델_치수():
|
||
raw = {
|
||
"MASCntrct_배수로": [
|
||
raw_row(
|
||
"철근콘크리트용배수로관, 가, M-400C, 400×400×2000mm, 이형수로관", "전지역", 90800
|
||
),
|
||
raw_row("철근콘크리트용배수로관, 나, CM-400C, 400×400×2000mm, 이형수로관", "전지역", 1),
|
||
]
|
||
}
|
||
_, (val, _) = fill_raw(item("u", "무게수로관-관급", "M-400C(400*400*120),680kg"), raw)
|
||
assert val == 90800
|
||
|
||
|
||
def test_보강_같은_치수만_채움_없으면_비움():
|
||
import update_관급단가_합침 as boost
|
||
|
||
def c(
|
||
name,
|
||
spec,
|
||
price,
|
||
unit="개",
|
||
note="관급: 조달청 다수공급자계약 2026-09-20 · 계약 2020-01-01~ · 전지역 · 도착도 · 포함",
|
||
):
|
||
return {"이름": name, "규격": spec, "단위": unit, "관급": price, "비고": note}
|
||
|
||
def row(name, spec, unit="개"):
|
||
return {"키": "k", "이름": name, "규격": spec, "단위": unit, "출처": "자재품목 X"}
|
||
|
||
P, PV = up.places, up.PROVINCE
|
||
g = lambda r, *cs: boost._same(r, cs[0], P, PV) # noqa: E731
|
||
grate = row("스틸그레이팅", "700×1000×75(I-75×7×4)")
|
||
assert g(grate, c("스틸그레이팅", "700×1000×75mm, 7×4mm, 뚜껑, 격자형", 1))
|
||
assert not g(grate, c("스틸그레이팅", "700×1000×75mm, 7×4mm, 뚜껑, 투수형", 1)) # 특수형
|
||
assert not g(grate, c("스틸그레이팅", "700×1000×75mm, 5×3mm, 뚜껑", 1)) # 바 치수 다름
|
||
assert not g(grate, c("스틸그레이팅", "(부품)700×1000×75mm, 7×4mm, 뚜껑", 1))
|
||
assert not g(grate, c("스틸그레이팅", "710×1000×75mm, 7×4mm, 뚜껑", 1)) # 가까운 치수로 안 이음
|
||
assert g(row("갈매기표지판(단면)", "300×450mm", "EA"), c("갈매기표지판", "300×450×2mm", 1))
|
||
assert not g(
|
||
row("갈매기표지판(단면)", "300×450mm", "EA"), c("갈매기표지판", "300×450×2mm, 양면", 1)
|
||
)
|
||
assert not g(
|
||
row("갈매기표지판(단면)", "300×450mm", "EA"),
|
||
c("갈매기표지판", "300×450×23mm, 태양광LED", 1),
|
||
)
|
||
assert not g(
|
||
row("주철맨홀뚜껑(원형)", "Φ648 KS", "EA"), c("주철맨홀뚜껑", "원형, Φ648×110mm", 1)
|
||
)
|
||
assert g(row("보행매트(관급)", "1,000×t30mm", "M"), c("보행매트", "1000×t30mm", 1, "m"))
|
||
assert not g(
|
||
row("보행매트(관급)", "1,000×t30mm", "M"), c("보행매트", "1000×t30mm", 1, "개")
|
||
) # 단위 다름
|
||
|
||
|
||
def test_보강_새_줄은_목록_품명만_같은_규격_한_줄():
|
||
import update_관급단가_합침 as boost
|
||
|
||
assert "스틸그레이팅" not in boost.NEW and "주철맨홀뚜껑" not in boost.NEW # 다산에 있는 품명
|
||
assert boost.NEW["철제가드레일"] == "안전시설"
|
||
keys = {}
|
||
rows = boost.new_rows(
|
||
{"키": {}, "다음": {}},
|
||
lambda b, t, n, f: keys.setdefault(n, f"MT{len(keys) + 1:06d}"),
|
||
{("낙석방지책", "존재")},
|
||
)
|
||
assert all(r["이름"] in boost.NEW and r["출처"].startswith("자재품목 조달청 ") for r in rows)
|
||
assert len({r["키"] for r in rows}) == len(rows)
|
||
|
||
|
||
# ── 느슨한 두 번째 짝짓기 — 다산 옛 값 ±5% 안일 때만 채움 ─────────────────────────────
|
||
|
||
|
||
def loose(name, spec, raw, old, monkeypatch):
|
||
"""엄격한 짝이 안 서는 줄을 다산 옛 값 `old` 로 다시 봄 → (새 값, 비고)."""
|
||
monkeypatch.setattr(slow, "old_of", lambda key: old)
|
||
(r,) = up.refill([item("z", name, spec)], raw)
|
||
return r["새값"], up.decide(item("z", name, spec), r, "2026-09-20")[1]
|
||
|
||
|
||
def test_H형강_치수가_없어도_강종_단가가_치수와_무관하면_채움(monkeypatch):
|
||
def hb(spec, price, corp="가"):
|
||
return raw_row(f"H빔, {corp}, X, {spec}", "전지역", price, corp=corp, maker=corp)
|
||
|
||
raw = {
|
||
"MASCntrct_H빔": [hb("100×100×6×8mm, SS275", 1228830), hb("150×150×7×10mm, SS275", 1228830)]
|
||
}
|
||
val, note = loose("H 형강(관급)", "SS275, 248*124*5*8mm", raw, 1228830, monkeypatch)
|
||
assert val == 1228830 and "느슨" in note
|
||
val, note = loose("H 형강(관급)", "SS275, 248*124*5*8mm", raw, 1400000, monkeypatch)
|
||
assert val is None and "5% 넘게 다름" in note # 다산 값과 ±5% 밖이면 안 채움
|
||
|
||
|
||
def test_H형강_치수마다_단가가_다르면_안_채움(monkeypatch):
|
||
def hb(spec, price):
|
||
return raw_row(f"H빔, 가, X, {spec}", "전지역", price, corp="가", maker="가")
|
||
|
||
raw = {
|
||
"MASCntrct_H빔": [hb("100×100×6×8mm, SS275", 1200000), hb("150×150×7×10mm, SS275", 1300000)]
|
||
}
|
||
val, note = loose("H 형강(관급)", "SS275, 248*124*5*8mm", raw, 1200000, monkeypatch)
|
||
assert val is None and "치수마다 톤당 단가가 달라" in note
|
||
|
||
|
||
def test_아스콘_제외_묶음은_뺀_구가_조달청_제외_목록에_드는_줄로(monkeypatch):
|
||
def asc(region, price):
|
||
return raw_row("아스팔트콘크리트 조합공통품목, BB-2, 가열, 3등급", region, price)
|
||
|
||
raw = {
|
||
"MASCntrct_아스팔트콘크리트": [
|
||
asc(
|
||
"서울 전지역(강북구,노원구,도봉구 제외),인천(전지역),경기 전지역(가평군 제외)",
|
||
100000,
|
||
),
|
||
asc("서울시 전지역(강서구,관악구 제외) 경기도 전지역(과천시 제외)", 105000),
|
||
]
|
||
}
|
||
spec = "조합공통품목, BB-2, 가열, 3등급"
|
||
val, note = loose(
|
||
"아스콘(관급)-서울,인천,경기(강북,노원,도봉구 제외)", spec, raw, 101000, monkeypatch
|
||
)
|
||
assert val == 100000 and "제외 목록" in note
|
||
val, _ = loose("아스콘(관급)-서울,경기(강서,관악구 제외)", spec, raw, 104000, monkeypatch)
|
||
assert val == 105000
|
||
val, note = loose("아스콘(관급)-서울,경기(강남,강동구 제외)", spec, raw, 104000, monkeypatch)
|
||
assert val is None and "제외 목록도 안 맞음" in note # 뺀 구가 어느 묶음에도 없음 · 값도 갈림
|
||
|
||
|
||
def test_아스콘_관리번호_A3_W_는_떼고_짝(monkeypatch):
|
||
raw = {
|
||
"MASCntrct_아스팔트콘크리트": [
|
||
raw_row("아스팔트콘크리트 조합공통품목, BB-2, 중온, 3등급", "충청남도(천안시)", 121830)
|
||
]
|
||
}
|
||
x = item("y", "아스콘(관급)-충남(천안시)", "조합공통품목, A3-W-BB2, BB-2, 중온, 3등급")
|
||
(r,) = up.refill([x], raw)
|
||
assert r["새값"] == 121830
|
||
|
||
|
||
def test_돌망태_개비온_m_cm_표기를_mm_로_맞추고_값이_넓게_갈리면_가까운_줄(monkeypatch):
|
||
def gb(spec, price, corp):
|
||
return raw_row(f"금속돌망태, {corp}, X, {spec}", "전지역", price, corp=corp, maker=corp)
|
||
|
||
raw = {
|
||
"MASCntrct_돌망태": [
|
||
gb("1500×1000×1000mm, 아연도금철선4종Φ3.2mm, 사각상자형", 35800, "가"),
|
||
gb("1500×1000×1000mm, 아연도금철선4종Φ3.2mm, 사각상자형", 43040, "나"),
|
||
gb("1500×1000×1000mm, 아연도금철선4종Φ3.2mm, 사각매트리스형", 1, "다"), # 매트리스는 뺌
|
||
]
|
||
}
|
||
val, note = loose("사각개비온(3.2mm)(관급)", "1.5L×1.0W×1.0H", raw, 43040, monkeypatch)
|
||
assert val == 43040 and "가장 가까운 줄" in note and "나" in note
|
||
val, _ = loose("사각개비온(3.2mm)(관급)", "1.5L×1.0W×1.0H", raw, 36000, monkeypatch)
|
||
assert val == 35800 # 최저가가 ±5% 안이면 최저가
|
||
|
||
|
||
def test_관_같은_지름_후보를_함께_보고_다산_값에_가장_가까운_줄(monkeypatch):
|
||
def pvc(dia, price, grade):
|
||
return raw_row(
|
||
f"압력용경질폴리염화비닐관, 고리, X-{grade}, Φ{dia}mm×4m, HIVP/삼중벽, 직관, {grade}",
|
||
"전지역",
|
||
price,
|
||
)
|
||
|
||
raw = {"MASCntrct_경질폴리염화비닐관": [pvc(16, 2550, "PN10"), pvc(16, 3930, "PN20")]}
|
||
val, note = loose("HI-VP(삼중벽)직관(관급)", "D16mm×4m", raw, 3930, monkeypatch)
|
||
assert val == 3930 and "가장 가까운 줄" in note
|
||
val, note = loose("HI-VP(삼중벽)직관(관급)", "D16mm×4m", raw, 3000, monkeypatch)
|
||
assert val is None and "5% 넘게 다름" in note # 어느 줄도 ±5% 안이 아님
|
||
|
||
|
||
def test_VR관은_진동및전압철근콘크리트관_보통관_최저가(monkeypatch):
|
||
def vr(dia, price, corp):
|
||
return raw_row(
|
||
f"진동및전압철근콘크리트관, {corp}, X, 보통관/A형, Φ{dia}×2500mm",
|
||
"전지역",
|
||
price,
|
||
corp=corp,
|
||
maker=corp,
|
||
)
|
||
|
||
raw = {
|
||
"MASCntrct_콘크리트관": [vr(300, 60440, "가"), vr(300, 70720, "나"), vr(400, 69890, "가")]
|
||
}
|
||
val, note = loose("VR관(관급)", "D=300mm", raw, 60000, monkeypatch)
|
||
assert val == 60440 and "다수공급자계약 가 " in note and "2가지 중 최저" in note
|
||
|
||
|
||
def test_무게수로관_뚜껑은_모델과_치수_순서_달라도_짝(monkeypatch):
|
||
raw = {
|
||
"MASCntrct_배수로": [
|
||
raw_row(
|
||
"철근콘크리트용배수로관, 대명, M-400T, (부품)640×150×2000mm, 수로관뚜껑",
|
||
"전지역",
|
||
76600,
|
||
),
|
||
raw_row(
|
||
"철근콘크리트용배수로관, 대명, M-700T, (부품)980×150×2000mm, 수로관뚜껑",
|
||
"전지역",
|
||
113400,
|
||
),
|
||
]
|
||
}
|
||
val, _ = loose("무게수로관뚜껑-관급", "M-400T(640*2000*150),530kg", raw, 76600, monkeypatch)
|
||
assert val == 76600
|
||
val, _ = loose("무게수로관뚜껑-관급", "M-700T(1000*2000*150),760kg", raw, 113400, monkeypatch)
|
||
assert val == 113400 # 980 ↔ 1000 은 3% 안
|
||
|
||
|
||
def test_벤치플룸_다산_모델_글자가_달라도_치수_종별_지역으로_짝(monkeypatch):
|
||
raw = {
|
||
"MASCntrct_플룸": [
|
||
raw_row(
|
||
"철근콘크리트벤치플룸관, 가, HCT-500A, 500×400×2000mm, 3종",
|
||
"경북,대구(도서지역 제외)",
|
||
77400,
|
||
),
|
||
raw_row(
|
||
"철근콘크리트벤치플룸관, 나, 500A, 500×400×2000mm, 3종",
|
||
"전북(도서지역 제외)",
|
||
75800,
|
||
),
|
||
]
|
||
}
|
||
val, _ = loose("벤치플륨관(3종)(관급)-대구경북", "400A(500*400)", raw, 77400, monkeypatch)
|
||
assert val == 77400 # 전북 줄은 지역이 달라 안 봄
|
||
|
||
|
||
def test_다산_옛_값이_없는_줄은_느슨하게_안_채움(monkeypatch):
|
||
monkeypatch.setattr(slow, "old_of", lambda key: None)
|
||
raw = {"MASCntrct_H빔": [raw_row("H빔, 가, X, 100×100×6×8mm, SS275", "전지역", 1228830)]}
|
||
(r,) = up.refill([item("z", "H 형강(관급)", "SS275, 248*124*5*8mm")], raw)
|
||
assert r["새값"] is None
|