- B09 데이터 점검 → 내역 점검 · 공기 옵션 → 공사기간 조건 · 박스보기/호표보기 → 카드 보기/표 보기 · 하이퍼링크/팝업 → 탭 이동/곁 창 · 굳힌 단가 → 이 공사 단가 책 · 산식/요율 출처 → 계산식/요율 근거 · 빈 줄 → 금액 빈 줄 · 단가연결 칸 램프 → 단가연결 칸 ● · 새 산근 → 새 단가산출(산근) · 연결 없음 → 일위대가 없음 - 단가연결 창 종류 일위대가 사전조합 → 일위대가(자료집) · 일위대가 호표 → 이 공사 일위대가 · 단가산출 호표 → 이 공사 단가산출 · 대가N/산근 N호표 → 일위대가 N호/단가산출 N호 · 구조물 조합 줄 번호(SS…) 숨김 · 넣을 값 창 제목 번호 → 알림 글 끝 - B08 조합 사본 · 조합 연결 · 노랑 램프 → 구조물 조합 · 구조물 조합 고르기 · 노랑 ●(locale_b4 B08_Quantity_* 글만 · 한시 브레인 승인) - ☰ 서랍 · 진행 판 끝 「내 자료집(일위대가 · 구조물 조합)」 = M02 일위대가조합 개인 층(떠남 물음 뒤) - 낡은 시험 글 고침(새 낱말): 143-1 · 157-2 · 182 · 203-1 · 204-2 · 304 · 311 · 701-3 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
97 lines
3.8 KiB
Python
97 lines
3.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""701-3 — 원가계산서 = 아는 금액까지 + 「빈 줄 n · 미완성」 띠 · 인쇄 · 확정은 빈 줄 0 일 때만.
|
|
|
|
- 설계내역서 `sums` = 빈 줄이 있어도 아는 비목 합 + `blank`(파이썬 · TS 짝 같은 값)
|
|
- 총괄 · 원가계산서 = 그 합으로 셈(빈칸 아님)
|
|
- [>] 확정 = 서버가 정본으로 다시 셈 · 빈 줄 있으면 409 · 단계 완료 안 함
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
|
|
from test_145_3_b09_bill import INPUTS, USER, _run_ts # noqa: E402
|
|
|
|
from B09_Estimation import B09_Estimation_Router as router # noqa: E402
|
|
from B09_Estimation.B09_Estimation_Calc_Bill import compute_estimation # noqa: E402
|
|
from B09_Estimation.B09_Estimation_Calc_Total import compute_total # noqa: E402
|
|
from B09_Estimation.B09_Estimation_Rates import rate_table # noqa: E402
|
|
|
|
|
|
def test_빈_줄_있어도_아는_금액_합_거울():
|
|
py = compute_estimation(INPUTS, USER)
|
|
sums = py["bill"]["sums"]
|
|
assert sums["blank"] == 2 and sums["labor"] == 7565300 and sums["material"] == 2771766
|
|
assert _run_ts(INPUTS, USER)["bill"]["sums"] == sums
|
|
|
|
|
|
def test_원가계산서_아는_금액으로_셈():
|
|
sums = compute_estimation(INPUTS, USER)["bill"]["sums"]
|
|
estimate = {**USER["estimate"], "estimated_price": 5e8, "duration_days": 120} # 요율 구간
|
|
total = compute_total(sums, estimate, rate_table())
|
|
assert total["sums"]["direct"] == 7565300 + 2771766 + 2627525
|
|
assert total["cost"]["grand"] is not None and total["cost"]["grand"] > total["sums"]["direct"]
|
|
|
|
|
|
def _complete(monkeypatch, blank: int) -> list[str]:
|
|
marks: list[str] = []
|
|
|
|
async def root(_pid):
|
|
return Path("x")
|
|
|
|
async def payload(_pid, _root):
|
|
return {"sheets": {"bill": {"totals": {"direct": {"blank": blank}}}}}
|
|
|
|
async def mark(pid, stage, how):
|
|
marks.append(f"{stage}:{how}")
|
|
|
|
monkeypatch.setattr(router, "_project_root", root)
|
|
monkeypatch.setattr(router, "payload", payload)
|
|
monkeypatch.setattr(router, "mark_stage", mark)
|
|
asyncio.run(router.estimation_complete("P1"))
|
|
return marks
|
|
|
|
|
|
def test_확정_빈_줄_있으면_409_단계_그대로(monkeypatch):
|
|
with pytest.raises(HTTPException) as caught:
|
|
_complete(monkeypatch, 3)
|
|
assert caught.value.status_code == 409 and "빈 줄 3 · 미완성" in caught.value.detail
|
|
assert _complete(monkeypatch, 0) == ["6:next"]
|
|
|
|
|
|
def test_띠_글_빈_줄_0_이면_없음():
|
|
source = (ROOT / "B09_Estimation/B09_Estimation_Calc_Base.ts").read_text(encoding="utf-8")
|
|
tabs = (ROOT / "B09_Estimation/B09_Estimation_UI_Tabs.ts").read_text(encoding="utf-8")
|
|
start = tabs.index("export const blankRows")
|
|
end = tabs.index("\n};\n", tabs.index("export const unfinished")) + 4
|
|
rows = [
|
|
{"kind": "item", "cost": "direct", "sum": None},
|
|
{"kind": "item", "cost": "direct", "sum": 5},
|
|
]
|
|
script = (
|
|
"type Row = Record<string, any>;\n"
|
|
+ tabs[start:end]
|
|
+ f"\nconsole.log(JSON.stringify([unfinished({{ bill: {{ rows: {json.dumps(rows)} }} }}),"
|
|
" unfinished({ bill: { rows: [] } })]));"
|
|
)
|
|
assert "export type Row" in source # 셈 짝 Row 꼴 그대로(흉내 타입)
|
|
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
|
|
banner, none = json.loads(result.stdout)
|
|
assert banner.startswith("금액 빈 줄 1 · 미완성") and "인쇄 · 확정은 빈 줄 0 일 때만" in banner
|
|
assert none is None
|