코드 - 고정 줄 단가 = M02_Labor_Fixed.line(사본 묶음 · 이은 노임 · 자재 = 묶음 값) — B09 단가 책으로 다시 안 덮음 - 대가 · 산근 고친 칸 = 품만 실제로(단가 칸 덮기 줄 남던 흠 · 품 고친 뒤 줄 금액 다시 셈 흠 py · ts 짝) - 산근 글 = 엔진 단가 셈 그대로 · 별도계상 재료 줄 끝 「· 별도계상 = 0.0」 · 계 줄 = 블록 합 - 노무비 · 중기목록 이름에서 규격 겹침 뗌 · TS 바탕 셈 이름 PARTS → MACHINE_PARTS(짝 시험이 TS 를 이어 붙일 때 겹침) 시험(고친 까닭 = 단가 원천이 B09 책 → M02 엔진 · 노임 · 기계 덮개 · 단가 고친 칸 · 중기사용료 걷음) - 144 바탕 · 뼈대: 운전사 · 중기사용료 · 경비 · 덮개 시험 걷음 → 노무 · 중기 나열 · 사급 표시 · 자재만 책 - 145-1 · 145-2 · 155 · 157-1 · 166-2 · 166-6 · 719-3 · 719-4 · 723-4 · 778-4 · 792-3: 줄 단가 = own(엔진) · 산근 = Solver() · collect 새 부름 꼴 - 702 · 705-1 · 719-2: 단가 칸 덮개 = 안 읽음 · 고정 줄 = M02 셈(노임 새 판 따라가기 시험 걷음) - 203-1 · 296 · 703-3 · 795-4: 단가 책 = 자재 줄로 - 707 · 707-6 · 707-7: 단가표 불러오기 = 자재만 · 거창 이은 안내판 줄 = M02 사본 셈(엑셀 −50,000) - 201-1: 환율 · 중기사용료 표 걷음 · b09 저장 속도: 산근 앞 풀이 그대로(단가 표 같음 견줌 걷음) - 대상 113 파일 764 통과 · 25 실패 = 원래 실패(formatMoney · formatPrice 없음 · 단위 글 ㎜ · sec · 719-1 · 796-2e · 717-4 · 721 · 311 · 243 · 246) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
5.6 KiB
Python
124 lines
5.6 KiB
Python
"""144-2~4 B09 바탕 단가 셈 — 엑셀 검산 대조 · 거울(`B09_Estimation_Calc_Base.py` ↔ `.ts`).
|
|
|
|
견본 = 기번3 공통 엑셀(`2024년 간선임도사업(기번3).xlsx`) 자재단가대비표 5~12줄 같은 입력 ·
|
|
841-1 운전사 시간 노임 · 중기사용료 · 경비목록표 · 노임 · 기계 덮개 시험 걷음(B09 가 기계 시간당을
|
|
다시 셈하던 곳 · 값 = M02 단가산출 그대로) — 노무비 · 중기목록표 = 엔진 단가 나열만 봄.
|
|
"""
|
|
|
|
import json
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B09_Estimation.B09_Estimation_Calc_Base import ( # noqa: E402
|
|
compute_base,
|
|
round_half,
|
|
rounddown,
|
|
roundup,
|
|
)
|
|
|
|
IMPORTS = re.compile(r"^import[^;]*;\n", re.MULTILINE)
|
|
|
|
#: 841-1 노무비목록표 · 중기목록표 입력 = 대가 · 산근 줄 엔진 단가 나열(서버 `Supply.price_lists`)
|
|
LABOR = [
|
|
{"key": "LB000048", "name": "건설기계운전사", "spec": "", "unit": "인", "price": 267360},
|
|
{"key": "LB000002", "name": "보통인부", "spec": "", "unit": "인", "price": None},
|
|
]
|
|
MACHINES = [
|
|
{"key": "EQ000020", "name": "굴착기(무한궤도) 0.7㎥", "spec": "",
|
|
"labor": 1459, "material": 1077, "expense": 1012},
|
|
{"key": "EQ000001", "name": "빈 값", "spec": "", "labor": None, "material": 1, "expense": 1},
|
|
] # fmt: skip
|
|
|
|
|
|
def _mt(key, name, **prices):
|
|
return {"key": key, "name": name, "spec": "", "unit": "", "origin": "M01", "prices": prices}
|
|
|
|
|
|
MATERIALS = [
|
|
_mt("MO1", "휘발유", 관급=1369, 물가정보=0, 물가자료=0),
|
|
_mt("MO2", "경유", 관급=1272),
|
|
_mt("M3", "각재", 관급=500661, 물가정보=829341, 물가자료=748503),
|
|
_mt("M4", "못", 관급=1503, 물가정보=1935, 물가자료=1665),
|
|
_mt("M8", "화강석", 관급=0, 물가정보=288000, 물가자료=288000),
|
|
_mt("M9", "사용자 값 줄", 물가자료=1000),
|
|
_mt("b08:sand", "골재"),
|
|
]
|
|
|
|
INPUTS = {"labor": LABOR, "machines": MACHINES, "materials": MATERIALS}
|
|
USER = {"materials": {"M9": {"applied": 950, "supply": "관급"}, "M4": {"supply": "별도"},
|
|
"b08:sand": {"applied": None}}} # fmt: skip
|
|
|
|
|
|
def _run_ts(inputs, user):
|
|
calc = ROOT / "B09_Estimation" / "B09_Estimation_Calc_Base.ts"
|
|
body = (
|
|
f"\nconst i = {json.dumps(inputs, ensure_ascii=False)};"
|
|
f"\nconst u = {json.dumps(user, ensure_ascii=False)};"
|
|
"\nconsole.log(JSON.stringify(computeBase(i, u)));"
|
|
)
|
|
script = IMPORTS.sub("", calc.read_text(encoding="utf-8")) + body
|
|
result = subprocess.run(
|
|
["node", "--experimental-strip-types", "--no-warnings",
|
|
"--input-type=module-typescript", "-e", script],
|
|
capture_output=True, text=True, encoding="utf-8", timeout=60,
|
|
) # fmt: skip
|
|
assert result.returncode == 0, result.stderr
|
|
return json.loads(result.stdout)
|
|
|
|
|
|
def test_끝수_엑셀_자리():
|
|
assert rounddown(33412.7689, 1) == 33412.7 and rounddown(126000.9) == 126000
|
|
assert rounddown(-1.25, 1) == -1.2 # 0 쪽으로
|
|
assert roundup(69849380, -3) == 69850000 and roundup(69850000, -3) == 69850000
|
|
assert round_half(2.5) == 3 and round_half(0.125, 2) == 0.13 and round_half(-2.5) == -3
|
|
assert rounddown(0.1 + 0.2, 1) == 0.3 # 떠도는 끝수는 버리지 않음
|
|
|
|
|
|
def test_841_1_노무_중기목록_나열_걷은_표_빈():
|
|
s = compute_base(INPUTS, {})
|
|
assert [r["price"] for r in s["labor"]] == [267360, None]
|
|
assert ("labor", "LB000002") in {(g["tab"], g["key"]) for g in s["gaps"]}
|
|
got = {r["key"]: (r["sum"], r["labor"], r["material"], r["expense"]) for r in s["machine"]}
|
|
assert got == {"EQ000020": (3548, 1459, 1077, 1012), "EQ000001": (None, None, 1, 1)}
|
|
assert s["expense"] == [] and s["machine_blocks"] == [] # 경비목록표 · 중기사용료 걷음
|
|
assert s["basis"] == {"fx": [], "drivers": []} # 환율및기초자료(운전사 시간 노임) 걷음
|
|
|
|
|
|
def test_144_3_자재단가대비_최저_사용자_관급():
|
|
s = compute_base(INPUTS, USER)
|
|
rows = {r["key"]: r for r in s["compare"]}
|
|
# 엑셀 O 열 = 최저 · P 열 출처(0 은 뺌 · 같은 값이면 앞 칸 — 화강석 「물정」)
|
|
assert (rows["MO1"]["applied"], rows["MO1"]["applied_from"]) == (1369, "관급")
|
|
assert (rows["M3"]["applied"], rows["M3"]["lowest_from"]) == (500661, "관급")
|
|
assert rows["M4"]["applied"] == 1503 and rows["M4"]["supply"] == "별도"
|
|
assert (rows["M8"]["applied"], rows["M8"]["applied_from"]) == (288000, "물가정보")
|
|
assert (rows["M9"]["lowest"], rows["M9"]["applied"], rows["M9"]["applied_from"]) == (
|
|
1000,
|
|
950,
|
|
"사용자",
|
|
)
|
|
assert rows["M9"]["supply"] == "관급"
|
|
assert rows["b08:sand"]["applied"] is None # 출처 없음 · 사용자 빈칸 = 빈칸
|
|
material = {r["key"]: r["price"] for r in s["material"]}
|
|
assert material == {r["key"]: r["applied"] for r in s["compare"]} # 재료비목록표 = 적용 단가
|
|
gaps = [(g["tab"], g["key"]) for g in s["gaps"] if g["tab"] == "compare"]
|
|
assert gaps == [("compare", "b08:sand")]
|
|
|
|
|
|
def test_841_1_사급_표시_기본_끔():
|
|
rows = [{**MATERIALS[2], "supply": "별도"}] # M02 자재표시 기본 별도계상
|
|
s = compute_base({**INPUTS, "materials": rows}, {"materials": {"M3": {"supply": "사급"}}})
|
|
assert s["compare"][0]["supply"] is None
|
|
assert compute_base({**INPUTS, "materials": rows}, {})["compare"][0]["supply"] == "별도"
|
|
|
|
|
|
def test_거울_TS_가_같은_값():
|
|
for user in ({}, USER, {"materials": {"M3": {"supply": "사급"}}}):
|
|
assert _run_ts(INPUTS, user) == compute_base(INPUTS, user)
|