Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -17,6 +17,8 @@ SK 단위 환산(728-2) = 배 = 바탕 기준단위(「100본」) → SK 단위(
|
||||
화면 셈 길 줄 · 계 = SK 1 단위 몫(`per_unit`).
|
||||
바탕 재료(747-1) = 별도계상 빈 칸 = 직접계산(M01 기초처럼 재료비에 듦 · 「별도계상」 고른 줄만 밖) ·
|
||||
일위대가 덮개 줄(`line` · `M02_Labor_Patch.run`)은 일위대가 기본(222-1 · 162-1) 그대로.
|
||||
보기 넷 한 답(750-1) = 화면 셈 길 답에 `글`(읽는 식) · `중간`(`M02_Sangeun_Views`) · 일위대가 줄
|
||||
모달 = 문서 `줄모드` true → `line`(일위대가 그 줄 셈과 같은 값).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -32,6 +34,7 @@ from M02_MasterTemplete import M02_Labor_Fixed as fixed
|
||||
from M02_MasterTemplete import M02_Labor_Ref as refs
|
||||
from M02_MasterTemplete import M02_MasterTemplete_Store as store
|
||||
from M02_MasterTemplete import M02_Sangeun_Doc as sd
|
||||
from M02_MasterTemplete import M02_Sangeun_Views as views
|
||||
|
||||
COSTS = labor.COSTS
|
||||
Ref = dict[str, Any]
|
||||
@@ -328,10 +331,12 @@ def run(
|
||||
trail: tuple = (),
|
||||
detail: bool = False,
|
||||
line: bool = False,
|
||||
seen: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
"""SK 셈(저장 안 함) — `run_line` 답 + `{1단위, 배, 정체, 짝잃음, 바탕판}` · 줄마다 `cid` ·
|
||||
고친 칸 `덮개`(칸 이름) · `원값`. 틀린 문서 · 순환 = `sd.DocError` ·
|
||||
`detail` = 둘째 기초 세부 펼침(`_detail` · 화면 셈 길만) · `line` = 일위대가 덮개 줄."""
|
||||
`detail` = 둘째 기초 세부 펼침(`_detail` · 화면 셈 길만) · 그때 `seen` = 보기 덧칸(750-1) ·
|
||||
`line` = 일위대가 덮개 줄."""
|
||||
from M02_MasterTemplete.M02_Labor_Expand import Expand
|
||||
|
||||
got = resolve(doc, whole, read, trail, given, line)
|
||||
@@ -345,6 +350,10 @@ def run(
|
||||
names = {str(i.get("이름")) for i in row.get("입력") or []}
|
||||
values = {k: v for k, v in {**got["입력값"], **(given or {})}.items() if k in names}
|
||||
out = labor.run_line(over, key, values, got["켜쌓기율"], got["고름"] or None)
|
||||
eng = out.get("줄") or []
|
||||
seen = (
|
||||
views.text_of(over, key, values, got["고름"], got["정체"], eng) if detail and seen else {}
|
||||
)
|
||||
covers = doc.get("덮개") or {}
|
||||
lines = []
|
||||
for at, (cid, line) in enumerate(zip(got["정체"], out.get("줄") or [])):
|
||||
@@ -368,7 +377,7 @@ def run(
|
||||
]
|
||||
base = doc.get("바탕") or {}
|
||||
tag = tag_of(whole, base["로직"]) if "로직" in base else (base.get("참조") or {}).get("키")
|
||||
return {**out, **head, "1단위": unit, "빠진줄": gone, "꼬리표": tag}
|
||||
return {**out, **head, **seen, "1단위": unit, "빠진줄": gone, "꼬리표": tag}
|
||||
|
||||
|
||||
def unit(doc: dict, whole: Any, expand: Any, trail: tuple) -> dict[str, Any]:
|
||||
@@ -495,8 +504,9 @@ def _was(line: dict) -> dict[str, Any]:
|
||||
return {k: sd.plain(Decimal(str(v))) for k, v in keep.items() if v is not None}
|
||||
|
||||
|
||||
def calc_doc(doc: Any, given: dict | None = None) -> dict[str, Any]:
|
||||
"""셈 길(`M02_Sangeun_Router`) — 지금 층 문서 · 저장 전 화면 문서 · 틀림 400 · 순환 422."""
|
||||
def calc_doc(doc: Any, given: dict | None = None, seen: bool = True) -> dict[str, Any]:
|
||||
"""셈 길(`M02_Sangeun_Router`) — 지금 층 문서 · 저장 전 화면 문서 · 틀림 400 · 순환 422 ·
|
||||
`seen` = 보기 덧칸(`글` · `중간`) 지음."""
|
||||
from M01_MasterData import M01_MasterData_Store as m01
|
||||
|
||||
whole = m01.loaded()[1]
|
||||
@@ -504,10 +514,11 @@ def calc_doc(doc: Any, given: dict | None = None) -> dict[str, Any]:
|
||||
clean = sd.clean(doc, None, whole)
|
||||
_check_refs(clean, refs.here()) # 저장 전 문서도 위 층만 · 있는 문서 · 순환 422
|
||||
me = refs.ref_id({"층": refs.here()[0], "주인": refs.here()[1], "키": clean["키"]})
|
||||
got = run(clean, whole, given, trail=(me,), detail=True)
|
||||
line = isinstance(doc, dict) and doc.get("줄모드") is True # 일위대가 줄 모달(750-1)
|
||||
got = run(clean, whole, given, trail=(me,), detail=True, line=line, seen=seen)
|
||||
except sd.DocError as e:
|
||||
raise _fail(e) from e
|
||||
return labor._json({**per_unit(got, clean), "머리": head_of(clean)})
|
||||
return labor._json({**views.finish(per_unit(got, clean)), "머리": head_of(clean)})
|
||||
|
||||
|
||||
def from_line(
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
[단가산출로 저장](715-3) `POST /api/m02/layers/{층}/sangeun/from-line`(층 쓰기 권한).
|
||||
|
||||
저장 안 함 · 로그인 사용자 · 층 읽기 권한(`Router_Items._root`) · 화면은 최신 응답만.
|
||||
몸 = `문서`(저장 전 화면 문서) 또는 `키`(그 층 저장본) + `입력값`(열린입력 · 시험 값).
|
||||
몸 = `문서`(저장 전 화면 문서) 또는 `키`(그 층 저장본) + `입력값`(열린입력 · 시험 값) ·
|
||||
`보기` false = 보기 덧칸(`글` · `중간` · 750-1) 안 지음(바탕 원값 셈처럼 금액만 쓸 때).
|
||||
문서 읽기 · 쓰기 · 새로 · 복사 · 지우기는 양식 길 · 서고 길 그대로(종류 `sangeun`).
|
||||
⚠ 등록은 `main.py`(로그인만).
|
||||
"""
|
||||
@@ -27,6 +28,7 @@ class SangeunCalcBody(BaseModel):
|
||||
문서: dict[str, Any] | None = None
|
||||
키: str | None = None
|
||||
입력값: dict[str, Any] | None = None
|
||||
보기: bool = True
|
||||
|
||||
|
||||
def _calc(body: SangeunCalcBody) -> dict[str, Any]:
|
||||
@@ -35,7 +37,7 @@ def _calc(body: SangeunCalcBody) -> dict[str, Any]:
|
||||
if not sd.KEY.match(str(body.키 or "")):
|
||||
raise store.StoreError(400, "문서 또는 키(SK-######)")
|
||||
doc = store.read(sd.KIND, str(body.키))["문서"]
|
||||
return calc.calc_doc(doc, body.입력값)
|
||||
return calc.calc_doc(doc, body.입력값, body.보기)
|
||||
|
||||
|
||||
class FromLineBody(BaseModel):
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
"""M02 단가산출(SK) 보기 넷 = 셈 답 하나(PLAN 750-1 · 요구 588-1) — 화면 셈 길 답 덧칸.
|
||||
|
||||
- `글` = 읽는 식(M01 `/text` 와 같은 꼴 · `master_text.lines` + `master_sheet_line.annotate`) ·
|
||||
SK 고친 줄(`_Over`) · 같은 입력(자동값 + 입력값) · 같은 고름(`choosing`) 위에서 지음.
|
||||
줄마다 `cid` = SK 줄 cid(`annotate` 와 같은 차례 — 호표 줄 × `_shares` 비목 · 뒤 덧줄).
|
||||
금액 · 소계 · 계 = SK `줄` · `기준` 그대로(`finish` — 별도계상 뺌 · 켜쌓기율 · 단위 환산 ÷배 ·
|
||||
못 셈 줄 까닭) · 식 글(`글` · `이름글`)은 바탕 단위 셈 그대로 + 머리 `나눔`(배 ≠ 1).
|
||||
- `중간` = 시험 계산 중간값(수 · 글만) — 시험 계산 = {lines: `줄`, sums: `기준`, middle: `중간`}.
|
||||
계산 자리 = 서버 단독(CLAUDE.md 5장 ③) · 화면은 그리기만.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
from M02_MasterTemplete import M02_Labor_Doc as labor
|
||||
|
||||
mf = labor.m01.cm.mf
|
||||
import master_sheet_line as msl # noqa: E402 — scripts 길은 `m01` 이 둠
|
||||
import master_text as mtx # noqa: E402
|
||||
|
||||
_ERRORS = (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError)
|
||||
|
||||
|
||||
def text_of(over: Any, key: str, values: dict, picks: Any, ids: list, eng: list) -> dict:
|
||||
"""`{글, 중간}` — `eng` = `run_line` 줄(별도계상 빼기 전 · 차례 맞춤용) · 못 지음 = 까닭."""
|
||||
row = over.logic(key)
|
||||
auto = {k: v for k, v in labor.m01.mau.values(over, row).items() if v is not None}
|
||||
given = {**auto, **labor.m01._dec(values or {})}
|
||||
try:
|
||||
with mf.mp.choosing(picks or None):
|
||||
text = msl.annotate(over, key, given, mtx.lines(over, key, given))
|
||||
mid = mf.run(over, key, given).get("중간") or {}
|
||||
except _ERRORS as e:
|
||||
return {"글": {"ok": False, "reason": str(e) or type(e).__name__}, "중간": {}}
|
||||
if text.get("ok") and "groups" in text and "결과" not in row:
|
||||
_tag(row, ids, eng, text)
|
||||
keep = (Decimal, int, float, str, bool)
|
||||
return {"글": text, "중간": {k: v for k, v in mid.items() if isinstance(v, keep)}}
|
||||
|
||||
|
||||
def _tag(row: dict, ids: list, eng: list, text: dict) -> None:
|
||||
"""글 줄마다 `cid` — `annotate` 와 같은 차례(호표 줄 × 몫 비목 → 덧줄)."""
|
||||
slots = {g["비목"]: iter(g["줄"]) for g in text["groups"]}
|
||||
items, adds = row.get("호표") or [], row.get("덧줄") or []
|
||||
for n, line in enumerate(eng):
|
||||
if n < len(items):
|
||||
parts = [p for p, _ in mtx._shares(line)] if line.get("비목") else []
|
||||
else:
|
||||
parts = [adds[n - len(items)]["비목"]] if n - len(items) < len(adds) else []
|
||||
for part in parts:
|
||||
one = next(slots.get(part, iter(())), None)
|
||||
if one is not None and n < len(ids):
|
||||
one["cid"] = ids[n]
|
||||
|
||||
|
||||
def finish(ans: dict) -> dict:
|
||||
"""화면 답(`per_unit` 뒤) — 글 금액 · 소계 · 계 = SK 줄 · 기준 · SK 에 없는 줄(별도계상) 뺌."""
|
||||
text = ans.get("글") or {}
|
||||
base = ans.get("기준")
|
||||
if not text.get("ok") or "groups" not in text or not base:
|
||||
return ans
|
||||
sk = {x["cid"]: x for x in ans.get("줄") or [] if x.get("cid")}
|
||||
groups = []
|
||||
for g in text["groups"]:
|
||||
kept = []
|
||||
for one in g["줄"]:
|
||||
line = sk.get(one.get("cid"))
|
||||
if line is None and one.get("cid"):
|
||||
continue # SK 에서 빠진 줄(별도계상)
|
||||
if line is not None:
|
||||
parts = line.get("비목") or {}
|
||||
money = parts.get(g["비목"]) if len(parts) > 1 else line.get("금액")
|
||||
one = {**one, "금액": money}
|
||||
if line.get("까닭"):
|
||||
one["까닭"] = line["까닭"]
|
||||
kept.append(one)
|
||||
if kept:
|
||||
groups.append({**g, "줄": kept, "소계": base.get(g["비목"])})
|
||||
times = ans.get("화면배")
|
||||
head = {"나눔": times} if times not in (None, 1) else {}
|
||||
return {**ans, "글": {**text, "groups": groups, "계": base.get("계"), **head}}
|
||||
@@ -632,6 +632,7 @@ export function lineDoc(
|
||||
덮개: patch?.덮개 ?? {},
|
||||
뺀줄: patch?.뺀줄 ?? [],
|
||||
더한줄: patch?.더한줄 ?? [],
|
||||
줄모드: true, // 셈 = 일위대가 그 줄 셈(별도계상 기본 · 750-1 · 서버 `calc_doc`)
|
||||
};
|
||||
if (row.참조) return { ...head, 바탕: { 참조: row.참조 } };
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user