- M01_SL_Router.py — /api/m01/sl/list · get · save · delete · outside · 자리 SheetLogic/_data(git 밖) · 키 SL-0001 꼴 - 저장 때 바깥 값(품셈 표 · 노임 · 자재 · 장비 · 기존 로직 결과)을 마스터에서 다시 넣고 common_util_spreadsheet(Node)로 다시 셈 · 브라우저 계산값 버림 - 단가 · 장비 · 기존 로직은 /calc(Store.calc)와 같은 셈 · 표는 머리 줄 + 줄(범위는 「이름·아래」 「이름·위」) - main.py 등록 한 문장(시스템 관리자 · 폴더째 지우면 이 줄도 지움) - M01_SL_Api.ts 몸 — slList · slGet · slSave · slDelete · slOutside · slPlain(EasyCalc plainError) - 시험 test_m01_sl_router.py — 저장 · 열기 · 목록 · 지우기 · 서버 다시 셈 · 바깥 값 · 403 · 마스터 사본 그대로 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
128 lines
4.7 KiB
TypeScript
128 lines
4.7 KiB
TypeScript
/* =============================================================================
|
|
* M01_SL_Api.ts
|
|
* 시트 로직 Api — 목록 · 열기 · 저장(서버가 바깥 값을 다시 넣고 같은 엔진으로 다시 셈) · 지우기 ·
|
|
* 바깥 값 한 번에 받기(품셈 표 · 노임 · 자재 · 장비 · 기존 로직 결과)
|
|
* 서버 = `M01_SL_Router.py`(`/api/m01/sl/*`) · 정함 `docs/raw/M01_쉬운단가/시트로직_정함.md` 4 · 5 · 6
|
|
* ========================================================================== */
|
|
|
|
import { API_BASE_URL } from "@config/config_frontend";
|
|
import type { Workbook } from "../../A00_Common/spreadsheet/spreadsheet_types";
|
|
import { plainError } from "../EasyCalc/M01_EasyCalc_Api";
|
|
import { ApiError } from "../M01_MasterData_UI_Logic_Api";
|
|
|
|
/** 바깥 값 하나 — 값은 열 때 · 저장 때 마스터에서 새로(표는 사본으로 들고 있지 않음)
|
|
* · 표 = 품셈 표 통째(머리 줄 + 줄 · 범위 조건 · 값은 「이름·아래」 「이름·위」 두 칸)
|
|
* · 노임 · 자재 = 그 줄의 단가(`/calc` 와 같은 셈 · 준용 · 낮은 값)
|
|
* · 장비 = 기계경비 로직 부름 글(EasyCalc `machineElement` 의 element) + 넘길 입력값 → 한 시간 값
|
|
* · 로직 = 기존 로직 결과(`/calc` · 돈 로직은 `field` 비목 · 기본 계) */
|
|
export type SlOutside =
|
|
| { id: string; kind: "표"; table: string }
|
|
| { id: string; kind: "노임"; key: string }
|
|
| { id: string; kind: "자재"; key: string }
|
|
| {
|
|
id: string;
|
|
kind: "장비";
|
|
element: string;
|
|
inputs: Record<string, unknown>;
|
|
field?: SlField;
|
|
}
|
|
| {
|
|
id: string;
|
|
kind: "로직";
|
|
key: string;
|
|
inputs: Record<string, unknown>;
|
|
field?: SlField;
|
|
};
|
|
export type SlField = "노무비" | "재료비" | "경비" | "계";
|
|
|
|
/** 바깥 값이 들어갈 자리 — 시트 id · 칸(표는 이 칸을 왼쪽 위로 펼침) */
|
|
export interface SlOutsideAt {
|
|
item: SlOutside;
|
|
sheet: string;
|
|
cell: string;
|
|
}
|
|
|
|
/** 바깥 값 답 — 표는 `rows`(머리 줄 포함) · 그 밖은 `value` · 못 구하면 ok false + 쉬운 말 */
|
|
export interface SlOutsideValue {
|
|
id: string;
|
|
ok: boolean;
|
|
value?: number | null;
|
|
rows?: (string | number | null)[][];
|
|
name?: string;
|
|
unit?: string;
|
|
reason?: string;
|
|
}
|
|
|
|
/** 시트 로직 한 벌 = 통합문서 하나(상자 = 시트) + 바깥 값 자리 */
|
|
export interface SlLogic {
|
|
/** 새 로직 null · 저장하면 서버가 `SL-0001` 꼴로 줌 */
|
|
key: string | null;
|
|
이름: string;
|
|
결과단위: string;
|
|
book: Workbook;
|
|
outside: SlOutsideAt[];
|
|
/** 다른 창이 더하는 칸(상자 · 알약 · 선 …)은 그대로 저장 · 돌려줌 */
|
|
[extra: string]: unknown;
|
|
}
|
|
|
|
/** 목록 한 줄 */
|
|
export interface SlListItem {
|
|
key: string;
|
|
이름: string;
|
|
결과단위: string;
|
|
/** 마지막 저장 때(ISO) */
|
|
saved: string;
|
|
}
|
|
|
|
/** 저장 답 — `logic.book.계산값` 은 서버가 다시 셈한 값 · `values` 는 넣은 바깥 값 */
|
|
export interface SlSaved {
|
|
key: string;
|
|
logic: SlLogic;
|
|
values: SlOutsideValue[];
|
|
}
|
|
|
|
async function send<T>(path: string, body?: unknown): Promise<T> {
|
|
let response: Response;
|
|
try {
|
|
response = await fetch(`${API_BASE_URL}/m01/sl${path}`, {
|
|
method: body === undefined ? "GET" : "POST",
|
|
credentials: "include",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
});
|
|
} catch {
|
|
throw new ApiError(0, "");
|
|
}
|
|
const data = (await response.json().catch(() => ({}))) as { detail?: unknown } & T;
|
|
if (!response.ok) throw new ApiError(response.status, data.detail ?? response.statusText);
|
|
return data;
|
|
}
|
|
|
|
/** 내 시트 로직 목록(저장 차례) */
|
|
export async function slList(): Promise<SlListItem[]> {
|
|
return (await send<{ logics: SlListItem[] }>("/list")).logics;
|
|
}
|
|
|
|
export async function slGet(key: string): Promise<SlLogic> {
|
|
return (await send<{ logic: SlLogic }>(`/get?${new URLSearchParams({ key }).toString()}`)).logic;
|
|
}
|
|
|
|
/** 저장 — 서버가 바깥 값을 마스터에서 다시 넣고 같은 엔진으로 다시 셈(브라우저 계산값은 버림) */
|
|
export async function slSave(logic: SlLogic): Promise<SlSaved> {
|
|
return send<SlSaved>("/save", { logic });
|
|
}
|
|
|
|
export async function slDelete(key: string): Promise<void> {
|
|
await send<{ key: string }>("/delete", { key });
|
|
}
|
|
|
|
/** 바깥 값 한 번에 — 열 때 · 예시값을 바꿀 때 화면이 받아 시트에 넣음 */
|
|
export async function slOutside(items: SlOutside[]): Promise<SlOutsideValue[]> {
|
|
return (await send<{ values: SlOutsideValue[] }>("/outside", { items })).values;
|
|
}
|
|
|
|
/** 실패 → 쉬운 말(EasyCalc `plainError` 그대로) */
|
|
export function slPlain(e: unknown): string {
|
|
return plainError(e);
|
|
}
|