Files
Aislo/M02_MasterTemplete/M02_MasterTemplete_Hopyo_JudgeApi.ts
eomsangdonandClaude Opus 5.5 0ec931dcde fix(785-6): 화면 검증 흠 고침 — 장부 짝 단산 줄 · 기초 칩 갈음 · 단가산출 탭 [옮기기] · 확인 · 만든 줄 칠
- logic/Hopyo_Picks.ts: 장부 짝 줄 제안 = 고른 것 모두(기초 둘 · 입력값 그대로)
- Hopyo_Rows.ts: 단산 줄 = 판정과 상관없이 블록 풀이 칩(장부 짝 제안 있음 줄도) · 이름 없는 후보 = 키
- Hopyo_Block.ts: 기초 칩 = 키로 끄고 켬(장부 고른 것 → 블록 풀이 바인딩 · 입력 필요 다시 보임)
- Hopyo_SheetSk.ts: 단가산출 판 아래에도 [옮기기](같은 캐시) — 호표 목록 판이 숨는 탭
- logic/Hopyo_SheetMap.ts · SheetNav · Sheet: 판정 화면 확인함 · 만든 SC 호표 줄 = 초록(밖 줄 그대로) · 확인 바뀜 = 다시 칠
- 시험: test_642_6 장부 짝 · 줄 칠 · helper_642_6 칠 셈 · helper_778_5 Rows 대역

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:39:51 +09:00

273 lines
7.8 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[];
/** 절 몫(곱수) — 수 · 옛 꼴 {값} */
몫?: number | { 값: 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;
/** 별칭 장부 짝(짝 = 지난번 고른 것이 후보 · 우리에 없음 · 안 씀) */
장부?: { 갈래: string; 층?: string | null; 제안?: unknown; 규격다름?: boolean } | 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;
/** 블록 풀이 파생 저장본(셈 전 = 없음) */
판정?: BlockVerdict | null;
고를수?: number | null;
}
export interface JudgeList {
키: string;
파일: string;
/** 블록 풀이 뒤 셈 중(블록 판정 · 고를수 None) */
셈중?: boolean;
호표: 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[]>;
/** 이미 옮긴 호표 · 블록 덮기(아니면 409) */
덮음?: boolean;
}
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) });