fix(B09 불러오기): 코드로만 가리킨 내역 줄도 일위대가에 이음 · 끝수 줄 꼬리표 · 요율 없음 총공사비 숨김 (707-7)
- 영월 꼴(STmate 엑셀 내역 줄 = 호표 코드만) = 일위대가표 같은 코드 호표 번 채움 → 서고 사본 21 · 이음 34 - 이은 줄 차가 비목마다 1 원/단위 안 = 「끝수」(STmate 하위 호표 반올림) · 끝 알림 한 줄 「엑셀 합계와 차 n 원 = 하위 호표 반올림 끝수 m 줄」(브레인 정함 = 끝수 줄도 이음) - 요율 못 읽은 공사(.estx · .STC) = 총공사비 칸 안 보임 - 시험 test_707_7 11(영월 끝수 · 일위대가 시트 없음 까닭) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -45,6 +45,21 @@ def read(name: str, data: bytes) -> dict[str, Any]:
|
||||
_PARTS = {"labor": "노무비", "material": "재료비", "expense": "경비"}
|
||||
|
||||
|
||||
def numbered(bill: dict[str, Any], book: dict[str, Any]) -> dict[str, Any]:
|
||||
"""번 없이 코드로만 가리킨 대가 줄(STmate 엑셀 영월 꼴) →
|
||||
일위대가표 같은 코드 호표 번을 채운 사본."""
|
||||
by_code = {h.get("코드"): int(h["번"]) for h in book.get("호표") or [] if h.get("코드")}
|
||||
|
||||
def fill(line: dict[str, Any]) -> dict[str, Any]:
|
||||
hopyo = line.get("호표") or {}
|
||||
no = by_code.get(hopyo.get("코드"))
|
||||
if hopyo.get("종류") != "대가" or "번" in hopyo or no is None:
|
||||
return line
|
||||
return {**line, "호표": {**hopyo, "번": no}}
|
||||
|
||||
return {**bill, "줄": [fill(line) for line in bill["줄"]]}
|
||||
|
||||
|
||||
def agree(bill: dict[str, Any], book: dict[str, Any]) -> tuple[set[int], list[dict[str, Any]]]:
|
||||
"""내역 줄 대가 번 가운데 내역 단가 = 일위대가표 호표 금액(비목마다 1 원 안)인 번만 이음 ·
|
||||
다른 줄 = 내역 단가 그대로(경쟁사 금액 재현) →
|
||||
|
||||
@@ -204,7 +204,8 @@ def apply(
|
||||
def differences(
|
||||
parsed: dict[str, Any], report: dict[str, Any], rows: list[dict[str, Any]]
|
||||
) -> list[dict[str, Any]]:
|
||||
"""불러온 직후 엑셀 금액 ≠ 우리 금액인 줄 [{행, 이름, 엑셀, 우리, 까닭}]."""
|
||||
"""불러온 직후 엑셀 금액 ≠ 우리 금액인 줄 [{행, 이름, 엑셀, 우리, 까닭}] ·
|
||||
이은 줄 차가 비목마다 1 원/단위 안 = 「끝수」(STmate 하위 호표 반올림 · 707-7)."""
|
||||
by_key = {r["key"]: r for r in rows}
|
||||
out = []
|
||||
for line in parsed["줄"]:
|
||||
@@ -219,7 +220,8 @@ def differences(
|
||||
elif row.get("qty") != line["수량"]:
|
||||
why = "수량 = B08(단가만)"
|
||||
elif not row.get("fixed"):
|
||||
why = "이음 단가 = 우리 셈"
|
||||
near = abs(ours - excel) <= len(PARTS) * abs(line["수량"] or 0) + 1
|
||||
why = "끝수" if near else "이음 단가 = 우리 셈"
|
||||
else:
|
||||
why = "끝수"
|
||||
out.append(
|
||||
|
||||
@@ -168,22 +168,24 @@ def _linked(root: Path, folders: dict[int, str]) -> dict[int, dict[str, Any]]:
|
||||
|
||||
def _own_links(
|
||||
root: Path, name: str, data: bytes, parsed: dict[str, Any], tag: str, who: Any
|
||||
) -> tuple[dict[int, dict[str, Any]], dict[str, Any]]:
|
||||
) -> tuple[dict[int, dict[str, Any]], dict[str, Any], dict[str, Any]]:
|
||||
"""707-7 — 파일 일위대가 · 단가산출 → 이 공사 서고 사본(층 쓰기 0) →
|
||||
(줄 연결, 단계 수 · 까닭)."""
|
||||
(줄 연결, 단계 수 · 까닭, 내역 — 코드로만 가리킨 줄에 호표 번 채움)."""
|
||||
if _raw(name):
|
||||
return {}, {"까닭": f"{Path(name).suffix} 구성 줄은 못 읽음 — 같은 공사 엑셀에서"}
|
||||
return {}, {"까닭": f"{Path(name).suffix} 구성 줄은 못 읽음 — 같은 공사 엑셀에서"}, parsed
|
||||
try:
|
||||
book = bill_hopyo.read(name, data)
|
||||
except ExcelError as error:
|
||||
return {}, {"까닭": str(error)}
|
||||
return {}, {"까닭": str(error)}, parsed
|
||||
built = bill_hopyo.build(book, name, tag, who)
|
||||
parsed = bill_hopyo.numbered(parsed, book)
|
||||
numbers, other = bill_hopyo.agree(parsed, book)
|
||||
folders = {
|
||||
no: got for no in sorted(numbers) if (got := bill_hopyo.copy(root, built, tag, no, who))
|
||||
}
|
||||
links = _linked(root, folders)
|
||||
return links, {**built["수"], "사본": len(links), "실패": built["실패"], "단가다름": other}
|
||||
step = {**built["수"], "사본": len(links), "실패": built["실패"], "단가다름": other}
|
||||
return links, step, parsed
|
||||
|
||||
|
||||
def _numbers(parsed: dict[str, Any]) -> set[int]:
|
||||
@@ -258,7 +260,7 @@ async def apply_bill(
|
||||
steps: dict[str, Any] = {}
|
||||
if body.hopyo:
|
||||
args = (root, path.name, data, parsed, body.file_key, who_of(session))
|
||||
links, steps["일위대가"] = await asyncio.to_thread(_own_links, *args)
|
||||
links, steps["일위대가"], parsed = await asyncio.to_thread(_own_links, *args)
|
||||
else:
|
||||
found = await asyncio.to_thread(_found, session, path.name, _numbers(parsed))
|
||||
links = await asyncio.to_thread(_links, root, found, who_of(session))
|
||||
|
||||
@@ -120,9 +120,15 @@ function report(got: Row, user: () => Row, done: (payload: Row, user: Row) => vo
|
||||
.map(([k, name]) => `${name} 이음 ${s[k]?.linked ?? 0}`)
|
||||
.join(" · "),
|
||||
),
|
||||
`${pair("직접비", sum.엑셀직접, sum.우리직접)} · ${pair("총공사비", sum.엑셀총, sum.우리총)}`,
|
||||
pair("직접비", sum.엑셀직접, sum.우리직접) +
|
||||
(sum.우리총 == null ? "" : ` · ${pair("총공사비", sum.엑셀총, sum.우리총)}`), // 요율 없음 = 셈 없음
|
||||
].filter((t): t is string => !!t);
|
||||
const off: Row[] = got.어긋남 || [];
|
||||
const tail = off.filter((x) => x.까닭 === "끝수").length; // 이은 줄 1 원/단위 안 = 하위 호표 반올림
|
||||
if (tail) {
|
||||
const gap = Math.abs(Number(sum.엑셀직접 || 0) - Number(sum.우리직접 || 0));
|
||||
lines.push(`엑셀 합계와 차 ${won(gap)} 원 = 하위 호표 반올림 끝수 ${tail} 줄(줄당 1 원/단위)`);
|
||||
}
|
||||
const other: Row[] = hp?.단가다름 || [];
|
||||
const next =
|
||||
"다음 — 붙인 고정 줄은 펼친 일위대가에서 이 공사 단가로 다시 잇기 · 우리 공종 잇기는 줄 우클릭 · [저장]해야 남음";
|
||||
@@ -168,7 +174,14 @@ function report(got: Row, user: () => Row, done: (payload: Row, user: Row) => vo
|
||||
]
|
||||
: []),
|
||||
...(off.length
|
||||
? [createText(`엑셀 금액과 다른 줄 ${off.length}`, "warning", 14), differences(off)]
|
||||
? [
|
||||
createText(
|
||||
`엑셀 금액과 다른 줄 ${off.length}${tail ? `(끝수 ${tail})` : ""}`,
|
||||
"warning",
|
||||
14,
|
||||
),
|
||||
differences(off),
|
||||
]
|
||||
: []),
|
||||
createText(next, "muted", 14),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user