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

This commit is contained in:
2026-09-13 20:00:38 +09:00
12 changed files with 1018 additions and 63 deletions
@@ -50,4 +50,4 @@
- 실무 레시피 350개 중 품셈 근거문구 보유는 82개뿐이다. 근거 없는 268개는 현행값으로 임의 확정하지 않는다.
- 과거 임도품셈 절 번호가 현행 산림사업 표준품셈에서 크게 바뀌었다. 번호만 같은 다른 절을 자동 연결하지 않는다.
- JSON의 `수량문자_확인`은 후보 절 본문에 같은 숫자가 있는지를 세는 1차 검색값이다. `일치` 확정은 위 두 항목처럼 조건과 행을 직접 대조한 경우만 한다.
- 후속 수동 대조는 근거 절이 확인된 11개부터 하고, 복수 절 참조 69개는 과거판-현행판 절 대응표가 있어야 안전하다.
- 후속 판 대응표는 `33_임도품셈_판_대응표.md`와 `30_원자료/임도품셈_판_대응표.csv`에 작성했다. 확정 2·추정 11·미확인 1이며, 추정·미확인은 자동 치환에 쓰지 않는다.
@@ -0,0 +1,28 @@
# 과거 임도품셈 ↔ 2026 현행 산림사업 표준품셈 대응표
작성일: 2026-09-13
산출물: `30_원자료/임도품셈_판_대응표.csv`
현행 기준: 산림청고시 제2025-82호, 2026-01-01 시행
## 결과
STmate 레시피에 남은 임도계 품셈 인용 **14개를 전수 등록**했다.
| 판정 | 수 | 뜻 |
|---|---:|---|
| 확정 | 2 | 명칭·적용조건과 관측 수량을 현행 원문에서 직접 대조 |
| 추정 | 11 | 명칭·공종 맥락으로 현행 후보를 특정했으나 과거 원문판 또는 수량 대조가 부족 |
| 미확인 | 1 | 현행 후보가 작업방식까지 같다는 근거가 없음 |
확정은 `2-3 제근 → 9-21 제근`과, STmate에서 `4-16-2`로 인용한 **절취없는 단끊기** → `5-16-1 단끊기` 두 건이다. 후자는 보통인부 `0.34·0.36·1.17인/100m`가 현행 세 공정과 각각 일치하는 범위에서만 확정했다.
## 코드 사용 규칙
- 대응표의 현행 절은 `work_item_master_2026-01-01.json`의 `FP-*` 코드와 표 ID를 함께 기록했다.
- 과거 한 절이 현행 여러 절로 나뉜 후보는 슬래시로 모두 보존했다. 대표 코드 하나를 임의 선택하지 않는다.
- `추정`과 `미확인`은 코드 자동 치환에 사용하지 않는다. 값도 옮기지 않고 확인 대기로 세운다.
- 과거 판본 원문이 확보되면 표 제목·주석·수량을 대조해 `추정 → 확정` 또는 `미확인`으로 갱신한다.
## 경계
저장소에는 2026 현행판만 있고 STmate가 인용한 과거 임도품셈 원문판은 없다. 따라서 같은 이름이라는 이유만으로 확정하지 않았다. 특히 `7-3 노면정리`는 현행 `3-4-3`의 인력식 노면정지·노면굴기와 작업방식이 달라 현행 절을 `미확인`으로 남겼다.
@@ -0,0 +1,15 @@
과거판,과거_절,과거_인용명,STmate_적용공종,현행판,현행_절,현행_FP코드,현행_명칭,현행_표ID,판정,근거와_경계
"판본 미확인(STmate 2024~2025 출력)","번호 미기재","되메우기 및 다짐","구조물 되메우기(사질양토) 기계100%(굴삭기 0.7㎥)","산림청고시 2025-82호(2026-01-01)","9-14-1","FP-09-14-01","되메우기","F0279","추정","공종명과 적용맥락 일치. 과거 절 번호·원문 미확보"
"판본 미확인(STmate 2024~2025 출력)","5-1-2 사","암터파기","구조물터파기(육상암 0~1m) 기계100%(굴삭기 0.7㎥)","산림청고시 2025-82호(2026-01-01)","9-13-7","FP-09-13-07","육상 암절취(0~1m)","F0267","추정","구조물·육상암·0~1m 조건 일치. 수량 전수 대조 전"
"판본 미확인(STmate 2024~2025 출력)","7-3","노면정리(건설품셈 기계 시공 적용)","노면정리","산림청고시 2025-82호(2026-01-01)","미확인","미확인","미확인","미확인","미확인","현행 3-4-3은 노면정지·노면굴기 인력 보수표라 기계식 노면정리와 동일하다고 볼 수 없음"
"판본 미확인(STmate 2024~2025 출력)","4-16-2","선떼붙이기공","단끊기","산림청고시 2025-82호(2026-01-01)","5-16 / 5-16-1 / 5-16-2","FP-05-16 / FP-05-16-01 / FP-05-16-02","선떼붙이기공 / 단끊기 / 선떼붙이기","F0121 / F0122","추정","과거 한 절이 현행 부모와 두 하위 절로 분리된 후보. 단끊기 일부만 별도 확정"
"판본 미확인(STmate 2024~2025 출력)","4-16-2","절취없는 단끊기","단끊기 절취없음","산림청고시 2025-82호(2026-01-01)","5-16-1","FP-05-16-01","단끊기","F0121","확정","보통인부 0.34·0.36·1.17인/100m가 현행 세 공정과 각각 일치. 적용범위는 절취없는 구성에 한정"
"판본 미확인(STmate 2024~2025 출력)","3-6 가","임도성토면 다짐","성토사면다짐 B/H0.7+콤팩터","산림청고시 2025-82호(2026-01-01)","9-17-1","FP-09-17-01","비탈면 다짐","미확인","추정","성토사면과 현행 비탈면 다짐의 기능상 후보. 현행 마스터에 직접 표가 없어 수량 대조 필요"
"판본 미확인(STmate 2024~2025 출력)","9-3","임도성토면다짐","성토사면다짐 굴삭기0.7㎥","산림청고시 2025-82호(2026-01-01)","9-17-1","FP-09-17-01","비탈면 다짐","미확인","추정","같은 STmate 자료 안에서도 과거 절 인용이 3-6과 9-3으로 갈림. 과거 판본부터 확인 필요"
"판본 미확인(STmate 2024~2025 출력)","4-16-4","파종공","씨뿌리기(성토면) 줄파종","산림청고시 2025-82호(2026-01-01)","5-18","FP-05-18","씨뿌리기(줄)","F0125","추정","줄파종·씨뿌리기 명칭과 적용맥락 일치. 과거 원문·수량 미대조"
"판본 미확인(STmate 2024~2025 출력)","3-3-2","굴삭기 적용 암절취","암절취 굴삭기0.7㎥+대형브레카","산림청고시 2025-82호(2026-01-01)","9-4 / 9-4-1 / 9-4-2","FP-09-04 / FP-09-04-01 / FP-09-04-02","암절취 / 암파쇄 / 집토","F0241 / F0242","추정","현행은 암절취를 암파쇄+집토 단계합산형으로 분리. 과거 한 절과 일대다 후보"
"판본 미확인(STmate 2024~2025 출력)","2-3","제근(100㎡당)","제근","산림청고시 2025-82호(2026-01-01)","9-21","FP-09-21","제근","F0294","확정","밀림 보통인부 0.05인이 현행 9-21과 직접 일치"
"판본 미확인(STmate 2024~2025 출력)","5-1-1 나","암절취","측구터파기(암) 기계100%(굴삭기 0.4㎥)","산림청고시 2025-82호(2026-01-01)","9-12-2","FP-09-12-02","암절취","F0259","추정","측구터파기·암 조건으로 현행 절 후보 특정. 수량·장비조건 미대조"
"판본 미확인(STmate 2024~2025 출력)","3-7 가","층따기","층따기","산림청고시 2025-82호(2026-01-01)","9-18","FP-09-18","층따기","F0287","추정","명칭 직접 일치하지만 과거 원문판이 없어 절 동일성 확정 불가"
"판본 미확인(STmate 2024~2025 출력)","3-3-1","토사 굴삭기 적용","토사절취(흙깎기) 굴삭기0.7㎥","산림청고시 2025-82호(2026-01-01)","9-3-2","FP-09-03-02","기계","F0240","추정","현행 9-3 토사깍기의 기계 하위 절 후보. 수량·토질조건 미대조"
"판본 미확인(STmate 2024~2025 출력)","4-39","쇄석·혼합석 부설","혼합석부설 T=0.1m","산림청고시 2025-82호(2026-01-01)","11-4","FP-11-04","쇄석․혼합석 부설","F0328","추정","명칭 직접 일치하지만 두께·장비·수량 조건 미대조"
1 과거판 과거_절 과거_인용명 STmate_적용공종 현행판 현행_절 현행_FP코드 현행_명칭 현행_표ID 판정 근거와_경계
2 판본 미확인(STmate 2024~2025 출력) 번호 미기재 되메우기 및 다짐 구조물 되메우기(사질양토) 기계100%(굴삭기 0.7㎥) 산림청고시 2025-82호(2026-01-01) 9-14-1 FP-09-14-01 되메우기 F0279 추정 공종명과 적용맥락 일치. 과거 절 번호·원문 미확보
3 판본 미확인(STmate 2024~2025 출력) 5-1-2 사 암터파기 구조물터파기(육상암 0~1m) 기계100%(굴삭기 0.7㎥) 산림청고시 2025-82호(2026-01-01) 9-13-7 FP-09-13-07 육상 암절취(0~1m) F0267 추정 구조물·육상암·0~1m 조건 일치. 수량 전수 대조 전
4 판본 미확인(STmate 2024~2025 출력) 7-3 노면정리(건설품셈 기계 시공 적용) 노면정리 산림청고시 2025-82호(2026-01-01) 미확인 미확인 미확인 미확인 미확인 현행 3-4-3은 노면정지·노면굴기 인력 보수표라 기계식 노면정리와 동일하다고 볼 수 없음
5 판본 미확인(STmate 2024~2025 출력) 4-16-2 선떼붙이기공 단끊기 산림청고시 2025-82호(2026-01-01) 5-16 / 5-16-1 / 5-16-2 FP-05-16 / FP-05-16-01 / FP-05-16-02 선떼붙이기공 / 단끊기 / 선떼붙이기 F0121 / F0122 추정 과거 한 절이 현행 부모와 두 하위 절로 분리된 후보. 단끊기 일부만 별도 확정
6 판본 미확인(STmate 2024~2025 출력) 4-16-2 절취없는 단끊기 단끊기 절취없음 산림청고시 2025-82호(2026-01-01) 5-16-1 FP-05-16-01 단끊기 F0121 확정 보통인부 0.34·0.36·1.17인/100m가 현행 세 공정과 각각 일치. 적용범위는 절취없는 구성에 한정
7 판본 미확인(STmate 2024~2025 출력) 3-6 가 임도성토면 다짐 성토사면다짐 B/H0.7+콤팩터 산림청고시 2025-82호(2026-01-01) 9-17-1 FP-09-17-01 비탈면 다짐 미확인 추정 성토사면과 현행 비탈면 다짐의 기능상 후보. 현행 마스터에 직접 표가 없어 수량 대조 필요
8 판본 미확인(STmate 2024~2025 출력) 9-3 임도성토면다짐 성토사면다짐 굴삭기0.7㎥ 산림청고시 2025-82호(2026-01-01) 9-17-1 FP-09-17-01 비탈면 다짐 미확인 추정 같은 STmate 자료 안에서도 과거 절 인용이 3-6과 9-3으로 갈림. 과거 판본부터 확인 필요
9 판본 미확인(STmate 2024~2025 출력) 4-16-4 파종공 씨뿌리기(성토면) 줄파종 산림청고시 2025-82호(2026-01-01) 5-18 FP-05-18 씨뿌리기(줄) F0125 추정 줄파종·씨뿌리기 명칭과 적용맥락 일치. 과거 원문·수량 미대조
10 판본 미확인(STmate 2024~2025 출력) 3-3-2 굴삭기 적용 암절취 암절취 굴삭기0.7㎥+대형브레카 산림청고시 2025-82호(2026-01-01) 9-4 / 9-4-1 / 9-4-2 FP-09-04 / FP-09-04-01 / FP-09-04-02 암절취 / 암파쇄 / 집토 F0241 / F0242 추정 현행은 암절취를 암파쇄+집토 단계합산형으로 분리. 과거 한 절과 일대다 후보
11 판본 미확인(STmate 2024~2025 출력) 2-3 제근(100㎡당) 제근 산림청고시 2025-82호(2026-01-01) 9-21 FP-09-21 제근 F0294 확정 밀림 보통인부 0.05인이 현행 9-21과 직접 일치
12 판본 미확인(STmate 2024~2025 출력) 5-1-1 나 암절취 측구터파기(암) 기계100%(굴삭기 0.4㎥) 산림청고시 2025-82호(2026-01-01) 9-12-2 FP-09-12-02 암절취 F0259 추정 측구터파기·암 조건으로 현행 절 후보 특정. 수량·장비조건 미대조
13 판본 미확인(STmate 2024~2025 출력) 3-7 가 층따기 층따기 산림청고시 2025-82호(2026-01-01) 9-18 FP-09-18 층따기 F0287 추정 명칭 직접 일치하지만 과거 원문판이 없어 절 동일성 확정 불가
14 판본 미확인(STmate 2024~2025 출력) 3-3-1 토사 굴삭기 적용 토사절취(흙깎기) 굴삭기0.7㎥ 산림청고시 2025-82호(2026-01-01) 9-3-2 FP-09-03-02 기계 F0240 추정 현행 9-3 토사깍기의 기계 하위 절 후보. 수량·토질조건 미대조
15 판본 미확인(STmate 2024~2025 출력) 4-39 쇄석·혼합석 부설 혼합석부설 T=0.1m 산림청고시 2025-82호(2026-01-01) 11-4 FP-11-04 쇄석․혼합석 부설 F0328 추정 명칭 직접 일치하지만 두께·장비·수량 조건 미대조
@@ -102,7 +102,7 @@
| `jb_shapes.py` · `jb_block_map.py` · `jb_expansion_diff.py` | JB 43개 형태·블록·확장 차이 |
| `rate_grid_defs.py` · `settings_axis_map.py` | 폼에서 그리드·선택축 추출 |
후속 레시피 분석은 `20_분석/31_일위대가_레시피_사전.md`와 `20_분석/32_특수산식과_현행품셈_대조.md`를 본다.
후속 레시피 분석은 `20_분석/31_일위대가_레시피_사전.md`부터 `20_분석/33_임도품셈_판_대응표.md`까지 본다.
## 다음
@@ -210,6 +210,77 @@ def test_재귀는_5단까지() -> None:
assert six["complete"] is False # 윗 표 → d1 → … → d5 = 6단
def test_수동_단가는_막힌_줄을_세우고_미확정으로_센다() -> None:
"""PLAN 3장 ③ — 모르터(코드 미정)에 수동 단가 · 0.02349㎥ × 재료 80,000 = 1,879.2."""
manual = {
"2": {
"material": 80000,
"labor": 0,
"expense": 0,
"source": "견적",
"entered_at": "2026-09-13",
}
}
table = unit_price_table(TEMPLATE, _sheet(), BOOK, _variant, (), manual)
mortar = next(row for row in table["rows"] if row["seq"] == 2)
assert mortar["manual"] is True and mortar["manual_source"] == "견적"
assert mortar["material"] == pytest.approx(1879.2) and mortar["reason"] == ""
assert table["blocked"] == 0 and table["complete"] is True and table["unconfirmed"] == 1
# 단가표에 값이 있는 줄도 수동 단가가 이김 — 그래도 미확정.
manual["1"] = {"material": 1, "labor": 0, "expense": 0}
again = unit_price_table(TEMPLATE, _sheet(), BOOK, _variant, (), manual)
assert again["rows"][0]["total"] == pytest.approx(2.6) and again["unconfirmed"] == 2
def test_줄_조합_저장본은_양식과_같으면_지우고_수동_단가는_남은_줄만() -> None:
from B08_Quantity.B08_Quantity_Engine_StructureUnitPrice import (
save_manual,
save_rows,
with_rows,
)
template = {"type_id": "masonry_wet", **TEMPLATE}
rows = TEMPLATE["unit_price"]["rows"]
edited = [*rows[:2], {"seq": 4, "name": "추가", "quantity": 1.0, "ref_code": "B-FP-12-25"}]
merged, changed = save_rows({"other": []}, "masonry_wet", template, edited)
assert changed and merged["masonry_wet"] == edited and "other" in merged
back, changed = save_rows(merged, "masonry_wet", template, [dict(row) for row in rows])
assert changed and back == {"other": []}
assert with_rows(template, edited)["unit_price"]["rows"] == edited
assert with_rows({"code": "x"}, None)["unit_price"]["rows"] == []
price = {"material": 10.0, "labor": 0.0, "expense": 0.0, "source": "견적"}
first = save_manual({}, "masonry_wet", edited, {"2": price, "3": price}, "2026-09-01")
assert list(first["masonry_wet"]) == ["2"] # 뺀 줄(3)의 값은 안 남음
same = save_manual(first, "masonry_wet", edited, {"2": price}, "2026-09-13")
assert same["masonry_wet"]["2"]["entered_at"] == "2026-09-01" # 값이 같으면 옛 날짜
moved = save_manual(
first, "masonry_wet", edited, {"2": {**price, "material": 11.0}}, "2026-09-13"
)
assert moved["masonry_wet"]["2"]["entered_at"] == "2026-09-13"
assert save_manual(first, "masonry_wet", edited, {}, "2026-09-13") == {}
def test_고르개는_갈래별로_낱말이_모두_든_항목과_단가를_낸다() -> None:
from B08_Quantity.B08_Quantity_Engine_StructureUnitPrice import search_titles
from B09_Estimation.B09_Estimation_PriceBook import PriceBook, PriceKind, PriceTitle
book = PriceBook()
slots = [None] * 5
book.add_title(
PriceTitle("M-001", PriceKind.MATERIAL, "시멘트", "40kg", "포", [*slots, Decimal(5000)])
)
book.add_title(PriceTitle("M-002", PriceKind.MATERIAL, "시멘트 모르터", "1:3", "m3"))
book.add_title(PriceTitle("B-FP-01", PriceKind.UNIT_PRICE, "모르터 비비기", "", "m3"))
found = search_titles(book, "시멘트", "resource")
assert [item["code"] for item in found] == ["M-001", "M-002"]
assert found[0]["price"] == pytest.approx(5000.0) and found[1]["price"] is None
assert found[1]["unit"] == "㎥"
assert [item["code"] for item in search_titles(book, "모르터 1:3", "resource")] == ["M-002"]
assert [item["code"] for item in search_titles(book, "모르터", "work")] == ["B-FP-01"]
assert search_titles(book, " ", "work") == []
@pytest.fixture()
def client(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> TestClient:
root = tmp_path / "project"
@@ -0,0 +1,181 @@
"""구조물도 일위대가 줄 고치기 창구 (2026-09-13, PLAN 3장 ③).
겨누는 것
① 줄 더하기·빼기를 저장하면 표가 그 조합으로 서고, 양식대로 보내면 저장본이 지워짐
② 수동 단가는 막힌 줄을 세우고 「미확정」으로 셈 · 넣은 날짜는 서버가 붙임
③ ⛔ 저장 자리는 둘 — [내 라이브러리에 저장]은 줄 조합만 싣고 수동 단가는 안 실음
④ 다른 양식을 가져오면 그 종류의 줄 조합·수동 단가가 비워짐
⑤ 고르개가 실제 단가표에서 품셈·자원을 찾음 · 차례가 겹치면 거절
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT))
import B08_Quantity.B08_Quantity_Engine_StructureLibrary as library_module # noqa: E402
import B08_Quantity.B08_Quantity_Router_Material as material_module # noqa: E402
import B08_Quantity.B08_Quantity_Router_StructureSheet as router_module # noqa: E402
from B05_Profile.B05_Profile_Structures_Repository import save_structures # noqa: E402
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402
from B08_Quantity.B08_Quantity_Engine_StructureTemplate import load_template # noqa: E402
from common_util.common_util_auth import verify_session # noqa: E402
from common_util.common_util_project_settings import quantity_settings # noqa: E402
PROJECT_ID = "55555555-5555-5555-5555-555555555555"
SHEETS = f"/api/projects/{PROJECT_ID}/quantity/structure-sheets"
@pytest.fixture()
def project(tmp_path: Path) -> Path:
root = tmp_path / "project"
root.mkdir()
wall = StructureInstance.model_validate(
{
"type_id": "masonry_wet",
"placement": "interval",
"start_m": 0.0,
"end_m": 10.0,
"options": {"height_m": 2.5, "back_len_cm": 45},
}
)
save_structures(str(root), [wall], base_revision=0)
return root
@pytest.fixture()
def client(project: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> TestClient:
async def fake_root(project_id):
return str(project)
async def no_route(project_id):
return {}
async def real_build(project_id):
from B09_Estimation.B09_Estimation_UnitPrice import cached_build
return cached_build()
monkeypatch.setattr(library_module, "STORAGE_BASE_DIR", str(tmp_path / "storage"))
monkeypatch.setattr(router_module, "_project_root", fake_root)
monkeypatch.setattr(router_module, "_price_build", real_build)
monkeypatch.setattr(material_module, "_section_modes", no_route)
monkeypatch.setattr(material_module, "_ground_types", no_route)
app = FastAPI()
app.include_router(router_module.router)
app.dependency_overrides[verify_session] = lambda: {"company_id": 7, "user_id": 42}
return TestClient(app)
def _key(client: TestClient) -> str:
return client.get(SHEETS).json()["sheets"][0]["key"]
def _table(client: TestClient, key: str) -> dict:
response = client.get(f"{SHEETS}/unit-price", params={"sheet_key": key})
assert response.status_code == 200, response.text
return response.json()
def test_줄을_고치고_수동_단가를_넣으면_저장_자리가_둘로_갈린다(
client: TestClient, project: Path, tmp_path: Path
) -> None:
key = _key(client)
before = _table(client, key)
assert before["editor"]["edited"] is False and before["unit_price"]["unconfirmed"] == 0
rows = before["editor"]["rows"]
# 기초잡석(3) 빼고 · 고정 수량 1.5 줄 더하기 · 모르터(2)에 수동 단가.
edited = [
rows[0],
rows[1],
{"seq": 4, "name": "추가 잡석", "quantity": 1.5, "ref_code": "B-FP-12-25"},
]
saved = client.put(
f"{SHEETS}/unit-price",
json={
"sheet_key": key,
"rows": edited,
"manual_prices": {
"2": {"material": 80000, "source": "견적 3곳 평균"},
"3": {"labor": 1},
},
},
)
assert saved.status_code == 200, saved.text
assert saved.json()["edited"] is True and saved.json()["manual_prices"] == 1
after = _table(client, key)
table = after["unit_price"]
assert [row["seq"] for row in table["rows"]] == [1, 2, 4]
mortar = table["rows"][1]
assert mortar["manual"] is True and mortar["manual_entered_at"]
assert table["unconfirmed"] == 1 and table["complete"] is True
assert table["rows"][2]["quantity"] == pytest.approx(1.5) and table["rows"][2]["total"] > 0
# 저장 자리 둘 — 줄 조합은 종류별, 수동 단가는 프로젝트 칸.
settings = quantity_settings(str(project))
assert [row["seq"] for row in settings["structure_unit_price_rows"]["masonry_wet"]] == [1, 2, 4]
assert settings["structure_manual_prices"]["masonry_wet"]["2"]["source"] == "견적 3곳 평균"
# ⛔ 내 라이브러리에는 줄 조합만 — 수동 단가 흔적이 없음.
mine = client.put(f"{SHEETS}/library/personal", json={"sheet_key": key})
assert mine.status_code == 200, mine.text
folder = tmp_path / "storage" / "7" / "42" / "library"
item = json.loads((folder / f"{mine.json()['code']}.json").read_text(encoding="utf-8"))
assert [row["seq"] for row in item["unit_price"]["rows"]] == [1, 2, 4]
assert "80000" not in json.dumps(item) and "견적" not in json.dumps(item)
# 양식대로 되돌리면 줄 조합 저장본이 지워짐.
back = client.put(
f"{SHEETS}/unit-price",
json={"sheet_key": key, "rows": after["editor"]["default_rows"], "manual_prices": {}},
)
assert back.json()["edited"] is False
assert "masonry_wet" not in quantity_settings(str(project))["structure_unit_price_rows"]
def test_다른_양식을_가져오면_줄_조합과_수동_단가가_비워진다(
client: TestClient, project: Path
) -> None:
key = _key(client)
rows = load_template("masonry_wet")["unit_price"]["rows"][:2]
client.put(
f"{SHEETS}/unit-price",
json={"sheet_key": key, "rows": rows, "manual_prices": {"2": {"material": 1}}},
)
taken = client.put(
f"{SHEETS}/library/import",
json={
"type_id": "masonry_wet",
"tier": "program",
"code": load_template("masonry_wet")["code"],
},
)
assert taken.status_code == 200, taken.text
assert (
taken.json()["cleared_unit_price_rows"] is True
and taken.json()["cleared_manual_prices"] == 1
)
settings = quantity_settings(str(project))
assert "masonry_wet" not in settings["structure_unit_price_rows"]
assert "masonry_wet" not in settings["structure_manual_prices"]
def test_고르개와_차례_겹침_거절(client: TestClient) -> None:
found = client.get(f"{SHEETS}/price-search", params={"q": "FP-12-25", "kind": "work"})
assert found.status_code == 200, found.text
assert any(item["code"] == "B-FP-12-25" for item in found.json()["items"])
resource = client.get(f"{SHEETS}/price-search", params={"q": "보통인부", "kind": "resource"})
assert resource.json()["items"], "노임 보통인부가 자원 갈래에 있어야 함"
twice = [{"seq": 1, "name": "a", "quantity": 1}, {"seq": 1, "name": "b", "quantity": 1}]
bad = client.put(f"{SHEETS}/unit-price", json={"sheet_key": _key(client), "rows": twice})
assert bad.status_code == 422