Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Hopyo_JudgeApi.ts
T
eomsangdonandClaude Opus 5.5 0117dbdf2e feat(785-6): 판정 화면 새 파일 — 줄 판 · 블록 판 · 고름 캐시 · 새 길 타입
- Hopyo_JudgeApi.ts: 785-5 길 다섯 타입 · 부름(hopyo-judge 목록 · 호표 · 블록 · check · move)
- logic/Hopyo_Picks.ts: 고름 캐시 꼴 · 제안 = 미리 체크 · 호표 제안 모두 확인 = 다 풀린 제안만 · 입력 필요 남은 줄 막음 · 옮기기 몸 = 확인한 것만
- Hopyo_Rows.ts: 시트 아래 줄 판 — 1차 판정 칩 · 갈래 · 후보 복수 고름 · 수량 · 단위 환산 칸 · 엑셀 행 뛰기 · [확인] · [옮기기]
- Hopyo_Block.ts: 블록 판 — 절마다 기초 후보 · 맡은 절 · 입력값 칸(출처 꼬리표 · 입력 필요 · 엇갈림 · 기본) · 줄 대조 · 블록 판정 · 금액 참고 · [검산]
- 사전 ui_template_locale_m2_judge.ts · 자리 CSS Hopyo_Judge.css
- 시험 test_642_6_screen.py + helper_642_6_picks.cjs(북부청 예시 1 · 2 · 3 실제 풀이로)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 22:41:42 +09:00

263 lines
7.3 KiB
TypeScript

/* =============================================================================
* M02_MasterTemplete_Hopyo_JudgeApi.ts
* 엑셀 불러오기 새 판정 길(PLAN 785-5 · 785-6 · 요구 641 · 642 · 설계 642-A 2-9) — 타입 + 부름만.
* · GET `/layers/{파일 층}/hopyo-judge/{키}` = 호표 · 블록 목록(판정 수 · 만든)
* · GET `/layers/{파일 층}/hopyo-judge/{키}/{번}` = 호표 줄 판(785-3 줄 판 + 단산 줄 `풀이`)
* · GET `/layers/{파일 층}/hopyo-judge/{키}/block/{번}` = 블록 풀이(`Solve.block` 꼴 + 이름 · 근거 · 만든)
* · POST `/layers/{파일 층}/hopyo/{키}/check` = 고른 것 검산(저장 없음 · 서버 셈)
* · POST `/layers/{만들 층}/hopyo/{키}/move` = 확인한 것 옮김(서버가 다시 셈 · 금액 · 품 칸 안 보냄)
* 판정 · 금액은 서버 — 화면은 보이기 · 고르기만(옛 `Hopyo_Api.ts` 는 785-7 에서 걷음).
* ========================================================================== */
import { call } from "./M02_MasterTemplete_Api_Fetch";
import { at, type HopyoScope, type Money } from "./M02_MasterTemplete_Hopyo_Api";
export type PickKind =
"기초" | "단가산출" | "일위대가" | "인력" | "재료" | "유가" | "기계" | "율" | "고정";
export type RowVerdict = "제안 있음" | "2차 필요" | "후보 여럿" | "후보 없음";
export type BlockVerdict = "기초로 풂" | "기초+덧" | "줄 조합" | "고를 것";
/** 값 출처 — 길 B1~B6 · 사용자 · 기본 */
export interface PickSource {
길: string;
행?: number | null;
글?: string;
}
/** 고른 것(785-3 꼴 + 배) */
export interface Pick {
갈래: PickKind;
키: string | null;
이름: string;
규격?: string;
단위?: string;
단가?: Partial<Money> | number | null;
입력값: Record<string, unknown>;
출처: Record<string, PickSource>;
몫: number | null;
비목?: string;
기준?: unknown;
별도계상?: boolean;
층?: string;
주인?: string | null;
배?: number | null;
}
export interface Binding {
입력값: Record<string, unknown>;
출처: Record<string, PickSource>;
엇갈림: Record<string, { 값: unknown; 길: string; 행: number | null }[]>;
안씀: string[];
입력필요: string[];
비움: string[];
기본: Record<string, unknown>;
}
export type PairVerdict = "품 같음" | "품 다름" | "입력 필요" | "엑셀만" | "우리만";
/** 줄 대조 — 엑셀 단위 줄 ↔ 우리 줄 */
export interface PairLine {
엑셀: { 묶음: string; 이름: string; 품: number | null; 행: number | null; 별도?: boolean } | null;
우리: { 이름: string; 종류: string; 단위: string; 품: number | null; 까닭?: string } | null;
판정: PairVerdict;
표지?: string;
}
export type CandMoney = Money | { 계: null; 까닭: string };
/** 절 기초 후보 하나 */
export interface BaseCand {
키: string;
이름: string;
바인딩: Binding;
줄: PairLine[];
셈: Partial<Record<PairVerdict, number>>;
판정: string;
금액: CandMoney;
}
export interface Section {
제목: string;
번?: string | null;
판정: "기초 풂" | "후보 여럿" | "기초 없음";
고름: string | null;
후보: BaseCand[];
몫?: { 글?: string; 값: number | null; 행?: number | null } | null;
}
/** 덧 줄(엑셀만 줄 → 덧 줄 후보 · `Units` 잇기) */
export interface ExtraLine {
절: string;
행: number | null;
이름: string;
방법?: string;
후보?: {
자재?: string;
키?: string;
노임?: string;
이름?: string;
규격?: string;
단위?: string;
}[];
}
export interface Solved {
번: number;
판정: BlockVerdict;
고를수: number;
절: Section[];
덧: ExtraLine[];
}
export interface Basis {
표지: string | null;
글: string;
출처: [string, string][];
}
export interface JudgeBlock extends Solved {
이름: string;
단위: string;
부름: number[];
근거: Basis | null;
만든: string | null;
/** 엑셀 블록 금액(참고 · 길이 실을 때만) */
금액?: Partial<Money> | null;
}
/** 호표 줄 판 한 줄(785-3 + 엑셀 글 · 단산 줄 풀이) */
export interface JudgeRow {
차례: number;
행: number | null;
갈래: PickKind[];
근거: string;
번?: number;
후보: Pick[];
판정: RowVerdict;
수량: { 엑셀: number | null; 배: number | null; 갈래: string };
이름?: string;
규격?: string;
단위?: string;
단가?: Partial<Money> | null;
풀이?: Solved | null;
}
export interface JudgeBrief {
번: number;
이름: string;
규격: string;
단위: string;
줄수: number;
판정: Partial<Record<RowVerdict, number>>;
만든: string | null;
}
export interface JudgeBlockBrief {
번: number;
이름: string;
단위: string;
부름: number[];
표지: string | null;
만든: string | null;
}
export interface JudgeList {
키: string;
파일: string;
호표: JudgeBrief[];
블록: JudgeBlockBrief[];
}
export interface JudgeOne {
번: number;
이름: string;
규격: string;
단위: string;
만든: string | null;
줄: JudgeRow[];
}
export interface CheckBody {
호표?: number;
차례?: number;
블록?: number;
고른: Pick[];
}
export interface CheckOne {
차례: number;
갈래: PickKind;
키: string | null;
입력값: Record<string, unknown>;
입력필요: string[];
줄?: PairLine[];
판정: string;
금액: CandMoney;
}
export interface CheckResult {
고른: CheckOne[];
수량?: { 엑셀: number | null; 배: number | null; 몫: number | null };
}
export interface MoveBody {
층: string;
주인?: string | null;
호표: Record<string, Record<string, Pick[]>>;
블록: Record<string, Pick[]>;
}
export interface MoveBlocked {
호표?: number;
블록?: number;
차례?: number;
까닭: string;
칸: string[];
}
export interface MoveResult {
만듦: { 번: number; 키: string; 이름: string; 다시씀?: boolean; 재사용?: boolean }[];
단가산출: { 번: number; 키: string; 이름: string }[];
막힘: MoveBlocked[];
하위: { 번: number; 키: string; 새로: boolean }[];
장부: { 씀: number };
}
const enc = encodeURIComponent;
const toQuery = (to?: HopyoScope): string =>
to ? `?to=${to.layer}${to.owner ? `&to_owner=${enc(to.owner)}` : ""}` : "";
export const readJudge = (scope: HopyoScope, key: string, to?: HopyoScope): Promise<JudgeList> =>
call(at(scope, `/hopyo-judge/${enc(key)}${toQuery(to)}`));
export const readJudgeOne = (
scope: HopyoScope,
key: string,
no: number,
to?: HopyoScope,
): Promise<JudgeOne> => call(at(scope, `/hopyo-judge/${enc(key)}/${no}${toQuery(to)}`));
export const readJudgeBlock = (
scope: HopyoScope,
key: string,
no: number,
to?: HopyoScope,
): Promise<JudgeBlock> => call(at(scope, `/hopyo-judge/${enc(key)}/block/${no}${toQuery(to)}`));
/** 검산(저장 없음) — `signal` = 옛 요청 끊기 */
export const checkJudge = (
scope: HopyoScope,
key: string,
body: CheckBody,
signal?: AbortSignal,
): Promise<CheckResult> =>
call(at(scope, `/hopyo/${enc(key)}/check`), {
method: "POST",
body: JSON.stringify(body),
signal,
});
/** 옮기기 — `target` = 만들 층 · 몸 `층` · `주인` = 파일 층 */
export const moveJudge = (target: HopyoScope, key: string, body: MoveBody): Promise<MoveResult> =>
call(at(target, `/hopyo/${enc(key)}/move`), { method: "POST", body: JSON.stringify(body) });