Merge remote-tracking branch 'origin/sub_desktop_1' into main_desktop_1
This commit is contained in:
@@ -96,6 +96,10 @@ class HandoffWorkItem:
|
||||
#: 갈래 축·원본값 — 「stone_cm」·「60~80」. **키 문자열은 우리가 만든다**(두 창 합의).
|
||||
variant_axis: str = ""
|
||||
variant_value: str = ""
|
||||
#: 규격 갈래를 B08 이 판정해 보낸 것(「철근구조물」)과 그 근거 문구.
|
||||
#: **근거는 산출근거 칸에 그대로 적는다** — 우리가 다시 지어내지 않는다.
|
||||
spec_class: str = ""
|
||||
spec_class_basis: str = ""
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
@@ -243,6 +247,8 @@ def parse_handoff(payload: dict[str, Any]) -> tuple[list[HandoffWorkItem], list[
|
||||
blocked_kind=row.get("blocked_kind") or "",
|
||||
variant_axis=row.get("variant_axis") or "",
|
||||
variant_value=str(row.get("variant_value") or ""),
|
||||
spec_class=row.get("spec_class") or "",
|
||||
spec_class_basis=row.get("spec_class_basis") or "",
|
||||
)
|
||||
for row in payload["work_items"]
|
||||
]
|
||||
|
||||
@@ -140,6 +140,9 @@ def _leaf_row(
|
||||
quantity=item.quantity,
|
||||
in_bill=item.in_bill,
|
||||
)
|
||||
if item.spec_class_basis:
|
||||
# 갈래 판정 근거는 **B08 문구를 그대로** 쓴다(두 벌로 짜지 않는다).
|
||||
row.note = " / ".join(part for part in (row.note, item.spec_class_basis) if part)
|
||||
if item.application_ratio_pct is not None:
|
||||
# ⚠ 곱하지 않는다 — B08 이 이미 곱한 값이다. 산출근거로만 적는다.
|
||||
row.note = f"반영률 {item.application_ratio_pct}% 적용 후 수량"
|
||||
|
||||
@@ -357,14 +357,9 @@ def match_table(
|
||||
result: AxisResult,
|
||||
) -> None:
|
||||
"""표 하나에 자원 축을 붙인다. 값이 안 서면 `unmatched` 로 보낸다."""
|
||||
# ⚠ **예시 서식 표는 자원 표가 아니다.** 품셈이 「ha당 …단가산출서(예시)」처럼
|
||||
# **빈 서식**을 실어 두는데(4-2 · 3-2 등), 그것을 읽으려 들면 구간 라벨·항목명이
|
||||
# 자원 이름으로 오해돼 못 맞춘 목록이 못 쓰게 된다(2026-09-08 실측 39줄).
|
||||
# 값이 있는데 안 서는 자리가 아니라 **애초에 값이 없는 표**다.
|
||||
headers = " ".join(str(c) for c in (table.get("condition_note") or []))
|
||||
if "단가산출서" in headers or "예시" in headers:
|
||||
result.skipped_forms["example_form"] = result.skipped_forms.get("example_form", 0) + 1
|
||||
return
|
||||
# 예시 서식(「ha당 …단가산출서(예시)」)은 **B08 이 마스터 원천에서 거른다**(8건).
|
||||
# 두 곳에서 같은 것을 거르면 **나중에 한쪽만 고쳐진다** — 원천이 이긴다.
|
||||
# ⚠ 짝 시험은 남겨 둔다(예시 서식이 자원으로 안 서는지) — 원천이 바뀌면 그것이 알려 준다.
|
||||
|
||||
from B09_Estimation.B09_Estimation_CrewOutput import match_crew_table
|
||||
|
||||
|
||||
@@ -908,11 +908,9 @@ export async function renderB09Estimation(root: HTMLElement): Promise<void> {
|
||||
}
|
||||
|
||||
if (bill.summary.missing.length > 0) {
|
||||
const note = document.createElement("div");
|
||||
note.className = "b09-hint";
|
||||
note.textContent = `${L("B09_Estimation_Boq_Missing")} (${bill.summary.missing.length})`;
|
||||
body.append(note);
|
||||
|
||||
// ⚠ **머리글을 한 번만 단다.** 종전엔 「못 세운 줄 (16)」 뒤에 갈래별 머리글이
|
||||
// 또 붙어 **같은 수가 두 번** 떴다. 안내를 더하는 것이 곧 다른 안내를 묻는 것이라
|
||||
// (2026-09-08 메인 창 지적), 한 화면에 뜨는 줄 수를 늘리지 않는다.
|
||||
// ⚠ **할 일이 다르므로 갈라 보인다** — 「사용자가 입력하면 풀리는 것」과
|
||||
// 「우리가 만들어야 하는 것」. 한 목록에 섞으면 사용자가 무엇을 해야 할지 못 읽는다.
|
||||
const needsInput = bill.summary.missing.filter(
|
||||
@@ -926,7 +924,10 @@ export async function renderB09Estimation(root: HTMLElement): Promise<void> {
|
||||
if (group.length === 0) continue;
|
||||
const head = document.createElement("div");
|
||||
head.className = "b09-hint";
|
||||
head.textContent = `${L(labelKey)} (${group.length})`;
|
||||
// 갈래가 하나뿐이면 「금액을 못 세운 줄」이라는 말을 앞에 붙여 뜻이 온전하게 한다.
|
||||
const prefix =
|
||||
needsInput.length > 0 && rest.length > 0 ? "" : `${L("B09_Estimation_Boq_Missing")} — `;
|
||||
head.textContent = `${prefix}${L(labelKey)} (${group.length})`;
|
||||
body.append(head);
|
||||
const list = document.createElement("ul");
|
||||
for (const item of group) {
|
||||
@@ -1019,6 +1020,16 @@ export async function renderB09Estimation(root: HTMLElement): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
// 자재가 아예 없으면 **빈 표 셋을 늘어놓지 않는다** — 「없다」 한 줄이면 된다
|
||||
// (2026-09-08 화면 전수에서 세 무리가 모두 「(0) — 0」 으로 뜨고 있었다).
|
||||
if (sheet.contractor.length === 0 && sheet.owner.length === 0 && sheet.unknown.length === 0) {
|
||||
const none = document.createElement("div");
|
||||
none.className = "b09-empty";
|
||||
none.textContent = L("B09_Estimation_Mat_None");
|
||||
body.append(none);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [labelKey, rows, total] of [
|
||||
["B09_Estimation_Mat_Contractor", sheet.contractor, sheet.contractor_total_krw],
|
||||
["B09_Estimation_Mat_Owner", sheet.owner, sheet.owner_total_krw],
|
||||
|
||||
@@ -709,6 +709,10 @@ export const ui_locales_b2 = {
|
||||
"관급·사급이 안 갈린 것 — 어느 합계에도 안 넣습니다",
|
||||
"Supply type undecided — excluded from both totals",
|
||||
],
|
||||
B09_Estimation_Mat_None: [
|
||||
"이 프로젝트에는 자재 줄이 없습니다 — 구조물이 없거나 자재가 안 나온 상태입니다.",
|
||||
"This project has no material rows yet.",
|
||||
],
|
||||
B09_Estimation_Mat_Empty: [
|
||||
"설계내역서를 먼저 불러오면 자재대가 섭니다.",
|
||||
"Load the bill first and the material sheet appears.",
|
||||
@@ -722,15 +726,15 @@ export const ui_locales_b2 = {
|
||||
"Check rows — quantity only, never priced",
|
||||
],
|
||||
B09_Estimation_Boq_NeedsInput: [
|
||||
"입력하면 풀리는 것 — 설계 화면에서 값을 고르면 금액이 섭니다",
|
||||
"입력하면 풀리는 것 (설계 화면에서 값을 고르면 금액이 섭니다)",
|
||||
"Waiting on input — pick the value on the design screen and the amount appears",
|
||||
],
|
||||
B09_Estimation_Boq_NeedsWork: [
|
||||
"우리가 만들어야 하는 것 — 원단위·전개식이 아직 없습니다",
|
||||
"우리가 만들어야 하는 것 (원단위·전개식 없음)",
|
||||
"Needs build — unit data or formula is missing",
|
||||
],
|
||||
B09_Estimation_Boq_Missing: [
|
||||
"금액을 못 세운 줄 — 0 으로 채우지 않고 그대로 보입니다",
|
||||
"금액을 못 세운 줄 — 0 으로 안 채웁니다",
|
||||
"Rows without an amount — shown as-is, not zero-filled",
|
||||
],
|
||||
B09_Estimation_Boq_Materials: ["자재 (별도 벌)", "Materials (separate set)"],
|
||||
|
||||
Reference in New Issue
Block a user