fix(M01): 자재품목 구분→상세구분 거름 살림 · 로직 키 열기 코드 재확인

머리에 구분 묶음이 등록 안 된 파일(재료_자재품목)은 subs() 가 줄에서 바로
구분·상세구분을 모으게 고침 · 화면은 자재품목도 인력·기계와 같은 2단
거름을 씀. 로직 키 직접 열기(openKeyOn)는 이미 전체 로직 목록에서 찾아
여는 구조라 쪽 나눔의 영향을 안 받음 — 0331bb4d 코드 재확인.
This commit is contained in:
2026-09-20 23:29:21 +09:00
parent 709ccbd73d
commit 5cdfbefa54
3 changed files with 30 additions and 10 deletions
+14 -4
View File
@@ -166,15 +166,25 @@ def subs(file: str = "", group: str = "") -> dict:
)
)
heads = data.get(slot) or {}
if heads:
subs_list = [
{"name": name, "book": head.get("원문"), "details": details_of(name, head)}
for name, head in heads.items()
]
else:
# 머리에 구분 묶음이 등록 안 된 파일(재료_자재품목) — 줄에서 바로 모음
subs_list = [
{"name": name, "book": None, "details": details_of(name, {})}
for name in dict.fromkeys(r["구분"] for r in lines if r.get("구분"))
]
return {
"file": file,
"version": version,
"slot": slot,
"states": list(_STATES) if has_state else [],
"subs": [
{"name": name, "book": head.get("원문"), "details": details_of(name, head)}
for name, head in (data.get(slot) or {}).items()
],
"subs": subs_list,
}
+11 -5
View File
@@ -104,6 +104,7 @@ export interface FilterArgs {
const MATERIAL_NAMES: Record<string, string> = { : "유가·전력" };
/** 재료 컨테이너 차례 — 여기 없는 파일은 뒤에 */
const FUEL = "유가전력";
const MATERIAL = "자재품목";
const MATERIAL_ORDER = ["자재품목", "유가전력", "품셈재료"];
/** 파일 이름 → 목록 글자 — 「소요량_건설품셈_10장_창호…」 → 「건설품셈_10장_창호…」 */
@@ -138,6 +139,8 @@ export function buildSide(
const buttons = new Map<string, HTMLElement>();
// 하위 거름은 서버가 준 목록(파일 머리에 등록된 구분·세부분류) — 새 조사가 늘어도 화면은 그대로
const kinds = new Map<Group, SubInfo[]>();
/** 재료 그룹 안 파일별 구분 목록(자재품목·유가전력 각자) — file 이름이 열쇠 */
const materialKinds = new Map<string, SubInfo[]>();
const states = new Map<Group, string[]>();
const runs = new Map<string, () => void>();
let restoring = false;
@@ -311,16 +314,16 @@ export function buildSide(
.flatMap((f) => {
const name = fileLabel(f.file);
const label = group === "재료" ? (MATERIAL_NAMES[name] ?? name) : name;
if (group === "재료" && name === FUEL)
if (group === "재료" && (name === FUEL || name === MATERIAL))
return [
filter({
id: `${group}\n${f.file}`,
store: `m01.filter.${group}.${name}`,
subs: kinds.get(group) ?? [],
subs: materialKinds.get(f.file) ?? [],
total: f.rows,
title: label,
subLabel: L("M01_FuelSub"),
detailLabel: L("M01_FuelDetail"),
subLabel: name === FUEL ? L("M01_FuelSub") : L("M01_LaborSub"),
detailLabel: name === FUEL ? L("M01_FuelDetail") : L("M01_LaborDetail"),
onPick: (sub, detail, tail) =>
open(f, sub, sub || detail ? `${label} · ${tail}` : label, detail),
}),
@@ -348,7 +351,10 @@ export function buildSide(
states.set(group as Group, got.states ?? []);
} else if (group === "재료") {
const fuel = list.find((f) => fileLabel(f.file) === FUEL);
if (fuel) kinds.set(group, (await fetchSubsAll({ file: fuel.file })).subs);
if (fuel) materialKinds.set(fuel.file, (await fetchSubsAll({ file: fuel.file })).subs);
const material = list.find((f) => fileLabel(f.file) === MATERIAL);
if (material)
materialKinds.set(material.file, (await fetchSubsAll({ file: material.file })).subs);
} else if (group === "소요량" || group === "계수") {
kinds.set(group, (await fetchSubsAll({ group })).subs);
}
+5 -1
View File
@@ -362,7 +362,11 @@ def test_구분_목록(client: TestClient) -> None:
assert machine["slot"] == "구분" and names[0] == "[00]토공기계" and names[-1] == "임업기계"
earthwork = next(s for s in machine["subs"] if s["name"] == "[00]토공기계")
assert earthwork["book"] == "건설품셈" and "불도저" in earthwork["details"]
assert _get(client, "/api/m01/subs", file="재료_자재품목.json")["subs"] == []
material = _get(client, "/api/m01/subs", file="재료_자재품목.json")
material_names = [s["name"] for s in material["subs"]]
assert "기계·설비" in material_names
steel = next(s for s in material["subs"] if s["name"] == "철근·강재")
assert steel["details"]
def test_못_이은_줄만_거름(client: TestClient) -> None: