Merge remote-tracking branch 'origin/dev' into sub_laptop_6
This commit is contained in:
@@ -235,6 +235,8 @@ async function b09(withNext) {
|
||||
return { ok: true, status: 200, json: async () => any() };
|
||||
};
|
||||
const tabs = any();
|
||||
let unfinished = null; // 701-3 빈 줄 띠 글 — 있으면 [>] 막힘
|
||||
tabs.unfinished = () => unfinished;
|
||||
tabs.DRAFT = { builders: {}, extend: [], panels: [], actions: [] };
|
||||
if (withNext) tabs.DRAFT.next = () => calls.push("next");
|
||||
const nextBtn = Object.assign(new Node("button"), { disabled: true });
|
||||
@@ -290,6 +292,9 @@ async function b09(withNext) {
|
||||
out.completeFail = await nav(1);
|
||||
failOn = null;
|
||||
out.prev = await nav(-1);
|
||||
unfinished = "빈 줄 2 · 미완성";
|
||||
out.blocked = await nav(1);
|
||||
unfinished = null;
|
||||
// [초기화]
|
||||
const reset = dock.children[0];
|
||||
const press = async () => {
|
||||
|
||||
@@ -125,15 +125,12 @@ def test_제목_합_빈_줄과_원가계산서_이음():
|
||||
assert rows["sec:1.1"]["sum"] == 361494 + 1119272 and rows["sec:1.1"]["blank"] == 0
|
||||
assert rows["sec:1"]["sum"] == 361494 + 1119272 + 11003825 # 빈 줄은 0 이 아니라 빈 줄 수
|
||||
assert rows["sec:1"]["blank"] == 1 and "빈 줄" not in rows["sec:1"]["note"] # 257-2 비고 빈 줄 없음
|
||||
# 145-4 이음 = {material, labor, expense, supply_net} · 빈 줄 있으면 None
|
||||
assert sheets["bill"]["sums"] == {
|
||||
"material": None,
|
||||
"labor": None,
|
||||
"expense": None,
|
||||
"supply_net": 69474220,
|
||||
}
|
||||
# 145-4 이음 = {material, labor, expense, supply_net, blank} · 701-3 빈 줄 있어도 아는 금액까지
|
||||
known = {"material": 2771766, "labor": 7565300, "expense": 2627525, "supply_net": 69474220}
|
||||
assert sheets["bill"]["sums"] == {**known, "blank": 2} # 절토(연암) · 막자갈(단가 없음)
|
||||
full = {**INPUTS, "bill": {"items": ITEMS[:3]}, "materials": MATERIALS[:-1]}
|
||||
sums = compute_estimation(full, USER)["bill"]["sums"]
|
||||
assert sums == {**known, "blank": 0}
|
||||
assert sums["labor"] == 307104 + 924896 + 6333300 # I9 + I10 + I14
|
||||
assert sums["material"] == 54390 + 194376 + 2043000 + 480000
|
||||
assert sums["expense"] == 2627525
|
||||
|
||||
@@ -106,7 +106,7 @@ def test_묶음_탭_단추가_보조_줄에_안내는_아래에():
|
||||
def test_잇기_쉘_안내_줄_아래_도구_비움():
|
||||
shell = (ROOT / "B09_Estimation" / "B09_Estimation_UI_Shell.ts").read_text("utf-8")
|
||||
assert (
|
||||
"panel.replaceChildren(table.root, ...(foot ? [foot] : []))" in shell
|
||||
"panel.replaceChildren(...banner, table.root, ...(foot ? [foot] : []))" in shell # 701-3 띠 = 표 위
|
||||
) # 229-1 레벨 단추 = 보조 탭 줄 · 안내 글 = 표 아래 · 203 레벨 안내 + 탭 안내 = 한 영역(맨 아래)
|
||||
assert "...(hint ? [hint] : []), table.root" not in shell # 위 자리 없음
|
||||
assert re.search(r"bars\.draw\(tab\);\s*bars\.setTools\(\[\]\);", shell)
|
||||
|
||||
@@ -88,6 +88,7 @@ def test_B09_다음_저장_완료_인쇄(r):
|
||||
"toast:error",
|
||||
]
|
||||
assert b["prev"] == [False, []] # [<] = 공용 틀 몫
|
||||
assert b["blocked"] == [True, ["toast:warning"]] # 701-3 빈 줄 있으면 저장 · 완료 없이 머묾
|
||||
|
||||
|
||||
def test_B09_초기화(r):
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
# -*- 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
|
||||
@@ -0,0 +1,45 @@
|
||||
"""702-3 B09 단가연결 즉시 금액 · [저장] 단추 · 서고 사본 산근.
|
||||
|
||||
- 서고 사본(단가연결로 들여온 사전조합)의 단가산출 줄 = 산근 목록에 듦 · 대가 블록 단가 채움
|
||||
(옛 흠 — 산근이 구조물 사본만 훑어 서고 사본 산근 0 · 대가 빈칸)
|
||||
- 셈 미리보기 길(`POST …/estimation/calc`) = 화면 캐시로 서버 셈 · 저장 안 함 · 로직 연결 줄 금액
|
||||
- 화면 — 연결 즉시 뒤에서 셈(차례 번호 · 옛 요청 끊기) · [저장] 단추 · 옮길 때 물음
|
||||
임시 층(견본 마스터 · 임시 storage) · 실제 마스터 · 프로젝트는 안 씀.
|
||||
"""
|
||||
|
||||
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 import B09_Estimation_Prices as prices # noqa: E402
|
||||
from B09_Estimation import B09_Estimation_Sangeun as sangeun # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Calc_Base import compute_base # noqa: E402
|
||||
from B09_Estimation.B09_Estimation_Calc_Labor import compute_labor # noqa: E402
|
||||
from resources.tester.test_204_2_b09_bill_library import ( # noqa: E402, F401
|
||||
daega_inputs,
|
||||
library,
|
||||
linked_resource_keys,
|
||||
world,
|
||||
)
|
||||
|
||||
B09 = ROOT / "B09_Estimation"
|
||||
|
||||
|
||||
def test_서고_사본_산근_목록에_듦_대가_단가_채움(world):
|
||||
root = world["root"]
|
||||
folder = library.import_copy(root, "precombo", "system", None, world["key"])["폴더"]
|
||||
daega = daega_inputs(root)
|
||||
block = next(b for b in daega["blocks"] if b["id"] == daega["libs"][folder])
|
||||
wanted = [ln["key"] for ln in block["lines"] if ln["kind"] == "sangeun"]
|
||||
base = compute_base(prices.build_inputs(None, linked_resource_keys(root)), {})
|
||||
sg = sangeun.build(root, base)
|
||||
assert [r["key"] for r in sg["sg_list"]] == wanted # 둘 다 산근 · 블록 줄 키 그대로
|
||||
assert all(r["sum"] for r in sg["sg_list"])
|
||||
assert sg["sg_list"][0]["uses"] == ["서고 서고 조합"]
|
||||
by_key = {r["key"]: r for r in sg["sg_list"]}
|
||||
labor = compute_labor(daega, {**base, "gaps": []}, by_key)
|
||||
row = next(r for r in labor["daega"] if r["key"] == block["id"])
|
||||
assert row["sum"] and not row.get("blank")
|
||||
@@ -9,6 +9,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from resources.tester import test_m02_tree as base
|
||||
@@ -158,3 +159,27 @@ def test_항목_추가_창_카드_격자_표_접기():
|
||||
assert "max-height: calc(100dvh - var(--app-header-h));\n overflow-y: auto;" in css
|
||||
main = (m02 / "M02_MasterTemplete_UI_Main.ts").read_text("utf-8")
|
||||
assert "host.hidden = !host.hidden;" in main and 'title.classList.toggle("is-folded"' in main
|
||||
|
||||
|
||||
def test_복사_생성_이름_원래_이름_복사_같은_이름_둘_없음(world):
|
||||
"""701-4 — 복사 생성(대상 `복사이름`) = 「원래 이름 복사」 · 또 하면 「… 복사(1)」 · 그냥 붙여넣기는 그대로."""
|
||||
company = _company(world)
|
||||
wall = company.item(["옹벽"])["key"]
|
||||
client = _as(world)
|
||||
body = {
|
||||
"from_layer": "company",
|
||||
"대상": [{"경로": [], "키": wall, "복사이름": True}],
|
||||
"경로": [],
|
||||
}
|
||||
first = client.post(PASTE.format("personal"), json=body).json()
|
||||
second = client.post(PASTE.format("personal"), json=body).json()
|
||||
plain = client.post(PASTE.format("personal"), json={**body, "대상": [{"경로": [], "키": wall}]})
|
||||
names = [r["경로"][-1] for r in plain.json()["items"]]
|
||||
assert sorted(names) == ["옹벽", "옹벽 복사", "옹벽 복사(1)"]
|
||||
assert first["알림"]["바뀐이름"] == ["옹벽 복사"] and second["알림"]["바뀐이름"] == [
|
||||
"옹벽 복사(1)"
|
||||
]
|
||||
assert len(set(names)) == len(names) # 같은 이름 둘 없음
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
ui = (root / "M02_MasterTemplete/M02_MasterTemplete_TreePanel.ts").read_text("utf-8")
|
||||
assert "refs: [{ 경로: [], 키: key, 복사이름: true }]" in ui # 복사 생성 길만
|
||||
|
||||
Reference in New Issue
Block a user