Merge remote-tracking branch 'origin/dev' into main_laptop_1

This commit is contained in:
2026-09-14 18:01:36 +09:00
5 changed files with 143 additions and 5 deletions
@@ -105,6 +105,8 @@ def test_찾기는_코드_없는_줄_이름으로_후보만_띄운다() -> None:
encoding="utf-8"
)
assert "query.value = row.name" in ui and "!row.ref_code && !row.work_item_code" in ui
# 후보 0 이면 다음 길(갈래 바꾸기 · 수동 단가)을 적음 — 조용히 「없음」으로 막히지 않게(2026-09-14 브레인)
assert "수동 단가를 넣을 것" in ui
def test_사용자가_코드를_고르면_금액이_선다() -> None:
@@ -216,6 +216,41 @@ def test_확정_때_기본_양식을_박는다(project: Path) -> None:
assert library_module.pin_program_templates(project) == 0 # 이미 박힌 것은 그대로
def test_복제는_고른_항목을_개인_단에_베끼고_프로젝트는_안_바꾼다(
client: TestClient, project: Path, storage: Path
) -> None:
"""PLAN 4장 「복제해서 내 것 만들기」 — 기본(또는 회사) 항목을 개인 단으로 한 번에 베낌."""
import B08_Quantity.B08_Quantity_Router_StmateLibrary as extra_router
client.app.include_router(extra_router.router)
base = load_template("masonry_wet")
folder = storage / "7" / "42" / "library"
for path in folder.glob("*.json"):
path.unlink() # 개인 단을 비워 새로 생기는지 봄
response = client.put(
f"{SHEETS}/library/clone",
json={"type_id": "masonry_wet", "tier": "program", "code": base["code"]},
)
assert response.status_code == 200, response.text
code = response.json()["code"]
saved = json.loads((folder / f"{code}.json").read_text(encoding="utf-8"))
assert saved["library_tier"] == "personal" and saved["code"] != base["code"]
assert saved["cloned_from"] == {"tier": "program", "code": base["code"], "name": base["name"]}
assert saved["rows"] == base["rows"]
assert not (project / "B08_Quantity" / "library").exists() # 프로젝트 작업본은 그대로
missing = client.put(
f"{SHEETS}/library/clone",
json={"type_id": "masonry_wet", "tier": "program", "code": "AX-ST-00000000"},
)
assert missing.status_code == 404
client.app.dependency_overrides[verify_session] = lambda: {"company_id": None, "user_id": 1}
denied = client.put(
f"{SHEETS}/library/clone",
json={"type_id": "masonry_wet", "tier": "program", "code": base["code"]},
)
assert denied.status_code == 403
def test_항목마다_양식형_고정형_종류가_붙는다(tmp_path: Path) -> None:
"""PLAN 4장 「항목마다 종류 배지」 — 명세 13장: 칸은 같고 `formula` 유무로만 갈림."""
folder = tmp_path / "personal"
@@ -236,6 +271,14 @@ def test_항목마다_양식형_고정형_종류가_붙는다(tmp_path: Path) ->
assert "양식형" in ui and "고정형" in ui and "item.kind" in ui
def test_복제_단추는_개인_단_항목에선_안_눌린다() -> None:
ui = (ROOT / "B08_Quantity" / "B08_Quantity_UI_StructureSheet_Library.ts").read_text(
encoding="utf-8"
)
assert "/clone" in ui and "복제해서 내 것으로" in ui
assert 'clone.disabled = tier === "personal"' in ui
def test_코드_모양이_아니면_박지_않는다(project: Path) -> None:
with pytest.raises(ValueError):
library_module.import_item(project, {"type_id": "masonry_wet", "code": "../x"}, "personal")