feat(M02): 면적산출 알약 = 일위대가 pills 길 — 로직 물량 「이름|단위」 + 자재 「자재|MT키」 · 다 고정 · 선택 입력 알약 빠짐 (PLAN 39-4 D7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W4uKaV4ZBhsJN4GHG7yRj4
This commit is contained in:
@@ -301,6 +301,20 @@ export const copyStructure = async (column: string, from: string): Promise<void>
|
||||
await scoped(`/structures/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" });
|
||||
};
|
||||
|
||||
/** 면적산출 알약 하나 — 로직 물량 「이름|단위」(`로직` · `배`) · 자재 「자재|MT키」(`자재`) */
|
||||
export interface LaborPill {
|
||||
키: string;
|
||||
이름: string;
|
||||
단위: string;
|
||||
로직?: string[];
|
||||
배?: number;
|
||||
자재?: { 코드: string; 이름: string; 규격: string; 단위: string };
|
||||
}
|
||||
|
||||
/** 그 열 일위대가의 면적산출 알약(저장본 · PLAN 39-1a pills 길) — 지금 층 */
|
||||
export const fetchLaborPills = (column: string): Promise<{ 열: string; 알약: LaborPill[] }> =>
|
||||
call(owned(activeLayer, `/layers/${activeLayer}/labor/${enc(column)}/pills`));
|
||||
|
||||
/** 다른 열(`from`) 면적산출 내용(알약 연결 포함)을 받는 열(`column`)로 가져옴 — 면적번 · 열은 그대로 */
|
||||
export const copyArea = async (column: string, from: string): Promise<void> => {
|
||||
await scoped(`/area/${enc(column)}/copy?from=${enc(from)}`, { method: "POST" });
|
||||
|
||||
@@ -24,14 +24,14 @@ import {
|
||||
import { openModal } from "@ui/ui_template_modal";
|
||||
import type { LinkOptions } from "../A00_Common/spreadsheet/spreadsheet_links";
|
||||
import { searchMaterials } from "../M01_MasterData/M01_MasterData_Api_Fetch";
|
||||
import { fetchComboInputs, fetchUsableCombos } from "./M02_MasterTemplete_Labor_Api";
|
||||
import { fetchUsableCombos } from "./M02_MasterTemplete_Labor_Api";
|
||||
import {
|
||||
copyArea,
|
||||
copyMaterial,
|
||||
copyStructure,
|
||||
fetchLaborPills,
|
||||
fetchStructureNumbers,
|
||||
getActiveLayer,
|
||||
readTemplate,
|
||||
saveTemplate,
|
||||
type StructureNumbers,
|
||||
type TreeEdit,
|
||||
@@ -67,9 +67,8 @@ const TEXT = {
|
||||
pickCopy: "{value} 복사해 가져오기",
|
||||
copied: "복사해 가져옴",
|
||||
copyFailed: "복사 실패 — {value}",
|
||||
noPrice: "이 열에 일위대가가 없어 알약 없음 — 구조물 조합 일위대가 줄에서 고름",
|
||||
noInputs: "일위대가 {value} 에 입력값이 없어 알약 없음",
|
||||
inputsFailed: "일위대가 입력값을 못 받음 — {value}",
|
||||
noPills: "이 열 일위대가에 담은 로직 · 자재가 없어 알약 없음 — 일위대가 상세에서 담음",
|
||||
pillsFailed: "일위대가 알약을 못 받음 — {value}",
|
||||
discardCards: "카드(도면 · 면적산출 · 재료산출)에 저장 안 한 고침이 있음 — 버리고 넘어갈까?",
|
||||
};
|
||||
|
||||
@@ -275,7 +274,8 @@ export const columnHead = (col: StructureColumn): string =>
|
||||
export const structureLabel = (number: string, col: StructureColumn): string =>
|
||||
[number, columnHead(col)].filter(Boolean).join(" ");
|
||||
|
||||
/** 면적산출 · 재료산출 편집 화면 위 알약 막대 — 면적 = 그 열 일위대가 입력값(물량) · 재료 = 자재DB 찾기.
|
||||
/** 면적산출 · 재료산출 편집 화면 아래 알약 막대 — 면적 = 그 열 일위대가의 로직 물량 「이름|단위」 + 자재 「자재|MT키」
|
||||
* (서버 pills 길 · 39-4 · 다 `고정` — 더하기 · 빼기는 일위대가 쪽) · 재료 = 자재DB 찾기.
|
||||
* 알약이 없으면 까닭 글(`notice`)을 같이 돌려줌 */
|
||||
export async function pillsFor(
|
||||
kind: SheetKind,
|
||||
@@ -285,20 +285,19 @@ export async function pillsFor(
|
||||
const own = await inputPills(column, "재료");
|
||||
return { links: { 항목: own, 모드: "자재", 자재찾기: searchMaterials } };
|
||||
}
|
||||
const own = await inputPills(column, "면적"); // 선택 입력 카드가 면적산출로 고른 입력
|
||||
const none = (notice: string) => ({ links: { 항목: own, 모드: "입력" as const }, notice });
|
||||
const table = await readTemplate(getActiveLayer(), "table", STRUCTURE_TABLE, null).catch(
|
||||
() => null,
|
||||
);
|
||||
const cols = (table?.문서 as { 열?: { id: string; 일위대가?: string | null }[] } | null)?.열;
|
||||
const combo = cols?.find((c) => c.id === column)?.일위대가;
|
||||
if (!combo) return none(TEXT.noPrice);
|
||||
const none = (notice: string) => ({ links: { 항목: [], 모드: "입력" as const }, notice });
|
||||
try {
|
||||
const got = await fetchComboInputs(combo);
|
||||
const 항목 = [...got.물량.map((q) => ({ 키: q.키, 이름: q.이름, 단위: q.단위 })), ...own];
|
||||
return 항목.length ? { links: { 항목, 모드: "입력" } } : none(fill(TEXT.noInputs, combo));
|
||||
const { 알약 } = await fetchLaborPills(column);
|
||||
const 항목 = 알약.map((p) => ({
|
||||
키: p.키,
|
||||
이름: p.이름,
|
||||
단위: p.단위,
|
||||
...(p.자재 ? { 자재: p.자재 } : {}),
|
||||
고정: true,
|
||||
}));
|
||||
return 항목.length ? { links: { 항목, 모드: "입력" } } : none(TEXT.noPills);
|
||||
} catch (error) {
|
||||
return none(fill(TEXT.inputsFailed, why(error)));
|
||||
return none(fill(TEXT.pillsFailed, why(error)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,3 +89,15 @@ def test_면적산출_카드_알약_줄_켬끔_기본_숨김():
|
||||
assert '"m02-card-pills": { bucket: "pref", scope: "global" },' in state
|
||||
# 상세(전체 편집기)는 켬끔 없음 — 늘 보임
|
||||
assert "is-pills-off" not in _text(M02 / "M02_MasterTemplete_UI_Main.ts")
|
||||
|
||||
|
||||
def test_면적산출_알약은_일위대가_pills_길():
|
||||
"""39-4 D7 — 로직 물량 「이름|단위」 + 자재 「자재|MT키」 · 다 고정 · 선택 입력 알약 빠짐."""
|
||||
struct = _text(M02 / "M02_MasterTemplete_Structure.ts")
|
||||
pills = struct.split("export async function pillsFor")[1].split("async function cardLinks")[0]
|
||||
area = pills.split('if (kind === "material")')[1].split("\n }\n", 1)[1]
|
||||
assert "await fetchLaborPills(column)" in area
|
||||
assert "고정: true" in area and "...(p.자재 ? { 자재: p.자재 } : {})" in area
|
||||
assert "inputPills" not in area and "fetchComboInputs" not in struct
|
||||
api = _text(M02 / "M02_MasterTemplete_Api_Fetch.ts")
|
||||
assert "/layers/${activeLayer}/labor/${enc(column)}/pills" in api
|
||||
|
||||
Reference in New Issue
Block a user