feat(786-2): 불러오기 [수정] → 모달 「확인 — 제N호표」 옛 꼴 되살림 · 새 기능 「새」 배지

- 새 Hopyo_Check(모달 껍데기 · 머리 = 호표 · 엑셀 단가 · 제안 n / 줄 m · 확인 n · 만든 일위대가 ·
  [호표 제안 모두 확인](새) · [일위대가 생성] = 옮기기 길 이 호표만 · [구조물 조합으로] · [엑셀 보기])
- 새 Hopyo_CheckRows(로직 표 + 자재 표 = 구조물 조합 상세 꼴 · Labor_Rows moneyCell · PRICE_SLOTS ·
  Labor_MatMarks 빌려 씀 · 칸마다 엑셀 / 우리 단가 · 품 비교 · [고르기] · [원문 보기] · [옵션] · 추가 / 로직없음)
- 새 Hopyo_ToItem(구조물 조합 나무 add-item → 그 일위대가 문서에 SC 참조 한 줄 · 옮겨진 뒤만 켬)
- Hopyo_Rows: 시트 아래 줄 판 몸 걷음 → 줄 칸 부품 createRowKit(판정 · 고름 칩 · 블록 풀이 · 단위 환산 · [확인]) ·
  옮기기 한 번 runMove(오른쪽 [옮기기] · 모달 [일위대가 생성] 한 벌)
- Router: POST /hopyo/{키}/price 우리 금액 미리 셈(저장 없음 · Make.Move.pick · Lines.picks → Labor_Doc.calc 재사용)
- Sheet: [수정] = openHopyoCheck · 시트 아래 자리는 단가산출 블록 판(SheetSk) 몫으로 남김
- 안 살림: 맞음·별칭 일괄 체크 · 품셈으로 바꾸기 · 모두 고정으로(지운 서버 길 몫) · 사용안함(새 옮기기 길에 뺌 없음)
- 시험: 새 test_786_2_check_modal(우리 금액 예시 1 = 94,227 · 예시 3 입력 필요 → 0 · 쓰기 0 · 모달 · 새 배지 ·
  공용 import 만 · 날 button/input 0) · test_642_6 단언 = 줄 판 createJudgeRows → 모달 openHopyoCheck(까닭: 줄 판 몸 걷음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-05 07:26:37 +09:00
co-authored by Claude Opus 5.5
parent bcc570ff31
commit e91fd4398e
10 changed files with 1130 additions and 169 deletions
+60
View File
@@ -33,6 +33,7 @@ from M02_MasterTemplete import M02_Hopyo_Paste as hopyo_paste
from M02_MasterTemplete import M02_Hopyo_Relink as hopyo_relink
from M02_MasterTemplete import M02_Hopyo_Row as hopyo_row
from M02_MasterTemplete import M02_Hopyo_Store as hopyo_store
from M02_MasterTemplete import M02_Labor_Doc as labor
from M02_MasterTemplete import M02_Layer_Access as access
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete.M02_MasterTemplete_Router_Items import Layer, Session, _call, _root
@@ -173,6 +174,65 @@ def hopyo_move(
return _call(folder, hopyo_maker.move, *args)
class PriceBody(BaseModel):
"""우리 금액 미리 셈(786-2) — 파일 층 `층` · `주인` · `호표` 번 · `줄 {차례: [고른 것]}`."""
model_config = ConfigDict(extra="forbid")
층: Layer
주인: str | None = None
호표: int
줄: dict[int, list[Pick]] = Field(default_factory=dict, max_length=300)
def _price(folder: Path, source: Path, key: str, no: int, rows: dict[int, list[dict]]) -> dict:
"""줄마다 옮기기와 같은 길(`Make.Move.pick` 재검사 · `Make.Lines.picks` 줄 · 자재)로
한 줄 문서 → `Labor_Doc.calc`(정본 셈 · 저장 없음 · 같은 기초 둘째 = 미리 참조) ·
막힘 = 그 줄 까닭만."""
mv = hopyo_maker.Move(folder, source, key, None)
if no not in mv.units:
raise store.StoreError(404, f"없는 제{no}호표")
lines = mv.units[no].get("줄") or []
b = hopyo_build.Builder(folder, source, key, no, None)
b.dry, b.strict = True, False
where = {"파일": mv.hp.get("파일") or "", "호표": no}
out: dict[int, dict[str, Any]] = {}
for at, picks in rows.items():
if not 0 <= at < len(lines) or not picks:
continue
try:
got = [mv.pick(p, lines[at], f"제{no}호표 {at + 1}째 줄") for p in picks]
except hopyo_maker.Stuck as e:
out[at] = {"금액": None, "까닭": [e.why], "칸": e.cells}
continue
body = hopyo_maker.Lines(b, lines, where, "", hopyo_maker.hfix.Linker(False))
body.picks(at, lines[at], got)
doc = {"종류": "일위대가", "열": "hopyo-price", "단위": None, "담은로직": body.lines}
try:
calc = labor.calc(labor.clean({**doc, "자재": list(body.mats.values())}), {})
except (ValueError, store.StoreError) as e:
out[at] = {"금액": None, "까닭": [str(getattr(e, "detail", e))], "칸": []}
continue
why = [x["까닭"] for x in [*calc.get("줄", []), *calc.get("자재", [])] if x.get("까닭")]
out[at] = {"금액": calc.get("반영합"), "자재": calc.get("자재", []), "까닭": why, "칸": []}
return {"줄": out}
@router.post("/layers/{layer}/hopyo/{key}/price")
def hopyo_price(
layer: Layer,
key: str,
body: PriceBody,
owner: str | None = None,
session: Session = Depends(verify_session),
) -> dict:
"""우리 금액 미리 셈(786-2 [수정] 모달 · 저장 없음) — 경로 층 = 만들 층(참조 읽기) ·
몸 층 = 파일 층 → `{줄 {차례: {금액 {노무비 · 재료비 · 경비 · 계}, 자재, 까닭, 칸}}}`."""
folder = _root(session, layer, owner)
source = _root(session, body.층, body.주인)
rows = {at: [p.model_dump(exclude_none=True) for p in ps] for at, ps in body.줄.items()}
return _call(folder, _price, folder, source, key, body.호표, rows)
# ── 옛 길(785-7 까지 · 옛 화면) · 그대로 쓰는 길 ─────────────────────────────
HOPYO_MAX_BYTES = 20 * 1024 * 1024