Files
Aislo/M02_MasterTemplete/logic/M02_Hopyo_Imported_Model.ts
T
eomsangdonandClaude Opus 5.5 ca72718e26 feat(793-3b): 불러오기 단산 줄 = 가져온 식 단가산출 모달 — 블록 → 가져온 식 SK 길 · 검산 · 엑셀/우리 단가 · 이음 고르기 · 근거 세 단 · 우리 기초로 바꾸기 · 입력 필요 · 줄 잇기 · 결과 칩
- 서버: 올림 때 산근 시트 둠(실무자료/산근/<HP>.json · HP 지울 때 같이) · POST …/sangeun/from-hopyo(블록 → 793-2a 내보내기 → 793-2 만들기 · 같은 내용 = 그 SK 다시 씀 · 옛 HP 409) · STmate 끝전 = 엑셀 계와 맞는 쪽
- 화면: [수정] 모달 단산 줄 · 오른쪽 블록 판 [가져온 식으로 열기](새) → 시트 모달 + 머리 판 · [이 줄에 잇기] = 줄 고른 것 SK(입력 필요 남으면 확인 막힘) · 단산 줄 결과 칩
- 시험: test_793_3b_import_screen 6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:09:00 +09:00

135 lines
4.8 KiB
TypeScript

/* =============================================================================
* logic/M02_Hopyo_Imported_Model.ts
* 불러오기 단산 줄 = 가져온 식 단가산출(PLAN 793-3b · 요구 646 · 647) — 꼴 · 읽기 · 바꾸기만.
* · SK `가져온` 덧칸 꼴(793-2 `M02_Sangeun_Imported.py` 머리) · 근거 제안 세 단(확정 · 근접 · 사람 고름 · 793-3a 가 채움)
* · 단산 줄 고른 것 = {갈래 단가산출, 키 SK-…, 층, 주인, 필요(입력 필요), 표지(결과 칩)} — 필요가 비어야 확인 · 옮김
* · 줄 결과 칩 = 가져온 식 있음 · 근거 확정 · 근접 · 사람 고름 · 입력 필요 n(「2차 필요」 대신)
* 화면 · fetch · sessionStorage 없음(`CLAUDE.md` 4장).
* ========================================================================== */
import type { DraftPick } from "./M02_MasterTemplete_Hopyo_Picks";
export type Money4 = Record<"노무비" | "재료비" | "경비" | "계", number>;
export type Tier = "확정" | "근접" | "사람";
export const TIERS: Tier[] = ["확정", "근접", "사람"];
/** 근거 후보 하나(793-3a) */
export interface BasisCand {
기초: string;
이름?: string;
옵션?: Record<string, unknown>;
품검산?: unknown;
단: Tier;
까닭?: string;
차이?: string | number;
}
export interface CheckLine {
자리: string;
행?: number | null;
셈?: number | null;
엑셀?: number | null;
같음: boolean;
까닭?: string;
}
/** SK 문서 덧칸 `가져온` */
export interface ImportedMeta {
로직?: { 이름?: string; 호표?: { 이름?: string; 이음?: string | null }[] };
단가기준?: "엑셀" | "우리";
원본?: {
파일?: string;
시트?: string;
행?: number[];
갈래?: string;
엑셀금액?: Money4;
글줄?: { 행?: number | null; 글?: string; 자리?: string; 값?: number | null }[];
};
검산?: {
같음: boolean;
까닭?: string | null;
셈?: Money4 | null;
엑셀?: Money4;
줄: CheckLine[];
};
근거후보?: BasisCand[];
바꿈?: { 전?: Money4 | null; 후?: Money4 | null; 차이?: Money4 | null };
}
export interface ImportedDoc {
이름?: string;
단위?: string;
바탕?: { 로직?: string } | null;
입력값?: Record<string, unknown>;
가져온?: ImportedMeta;
[k: string]: unknown;
}
/** 가져온 식(아직 우리 기초로 안 바꿈) */
export const isImported = (d: ImportedDoc | null | undefined): boolean => !!d?.가져온?.로직;
/** 근거 후보 세 단 */
export function tiers(list: BasisCand[] | undefined): Record<Tier, BasisCand[]> {
const out: Record<Tier, BasisCand[]> = { 확정: [], 근접: [], 사람: [] };
for (const c of list ?? []) if (TIERS.includes(c.단)) out[c.단].push(c);
return out;
}
/** 검산 다른 줄 */
export const wrongLines = (d: ImportedDoc): CheckLine[] =>
(d.가져온?.검산?.줄 ?? []).filter((x) => !x.같음);
const blank = (v: unknown): boolean => v === undefined || v === null || String(v).trim() === "";
/** 입력 필요 중 아직 빈 칸 */
export const stillNeed = (need: string[], values: Record<string, unknown> | undefined): string[] =>
need.filter((k) => blank(values?.[k]));
/** 단산 줄 고른 것 — 그 SK(필요 = 빈 입력 필요 칸 · `missing` 이 막음) */
export function skPick(
sk: string,
doc: ImportedDoc,
scope: { layer: string; owner: string | null },
need: string[],
marks: string[],
): DraftPick & { 표지: string[] } {
const values = doc.입력값 ?? {};
return {
갈래: "단가산출",
키: sk,
이름: String(doc.이름 ?? sk),
단위: String(doc.단위 ?? ""),
입력값: Object.fromEntries(need.map((k) => [k, values[k] ?? ""])),
출처: {},
몫: null,
층: scope.layer,
주인: scope.owner,
필요: need,
표지: marks,
};
}
/** 결과 표지 — 가져온 식 있음 | 우리 기초(+ 근거 단) */
export function marksOf(doc: ImportedDoc, tier: Tier | null): string[] {
if (isImported(doc)) return ["가져온 식 있음"];
return tier ? [`근거 ${tier}`] : ["사람 고름"];
}
/** 단산 줄 결과 칩 글 — SK 고른 것이 있으면 그 표지 + 입력 필요 n · 없으면 null(옛 판정 칩) */
export function rowChips(picks: (DraftPick & { 표지?: string[] })[]): string[] | null {
const sk = picks.find((p) => p.갈래 === "단가산출" && /^SK-/.test(String(p.키 ?? "")));
if (!sk) return null;
const need = stillNeed(sk.필요 ?? [], sk.입력값);
return [...(sk.표지 ?? ["가져온 식 있음"]), ...(need.length ? [`입력 필요 ${need.length}`] : [])];
}
/** 금액 차이 글(후 − 전) */
export function gapText(
before: Money4 | null | undefined,
after: Money4 | null | undefined,
): string {
if (!before || !after) return "";
const d = after.계 - before.계;
return `${d > 0 ? "+" : ""}${d.toLocaleString("ko-KR")}`;
}