feat(b09): 설계내역서 손 줄 · 수량 덮어쓰기 · 호표 연결 셈 짝(166-2 1걸음)

- Calc_Bill 짝(py · ts) — user.bill qty 덮어쓰기 · 손 줄 끼우기 · ref_price 하나(dg · sg · logic)
- Sangeun — 손 줄 M01 로직 = 새 산근(사본 뒤 번호) · sg_hand · 손 줄 자원 키
- Store — bill 다듬기 · Router — 단가 수요에 손 줄 로직 · 호표 목록 넘김
- UI_Shell 짝 셈에 호표 목록 · 새 UI_Tabs_Bill_Edit(우클릭 메뉴 · 고르기 창)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015KYZ2HWWdUwZWYnn3tQz7X
This commit is contained in:
2026-10-10 00:09:43 +09:00
co-authored by Claude Opus 5.5
parent 35d6b68c41
commit b868d80bb3
7 changed files with 530 additions and 33 deletions
+12 -3
View File
@@ -107,13 +107,16 @@ function note(textValue: string): HTMLElement {
return p;
}
/** 산근(145-1) = 서버 단독 — 받은 그대로 두는 칸 */
/** 산근(145-1) = 서버 단독 — 받은 그대로 두는 칸(+ sg_hand = 손 줄 로직 → 산근 키 · 166-2) */
const SERVER_ONLY = ["sg", "sg_list", "sg_gaps", "sg_notes"] as const;
/** 브라우저 셈 — 서버 짝과 같은 식(조작 중) · 산근은 서버 답 그대로(+ 「연결 없음」) */
export function computeSheets(payload: Row, user: Row): Row {
const server: Row = payload.sheets || {};
const kept = Object.fromEntries(SERVER_ONLY.map((k) => [k, server[k] ?? []]));
const kept: Row = {
...Object.fromEntries(SERVER_ONLY.map((k) => [k, server[k] ?? []])),
sg_hand: server.sg_hand ?? {},
};
// 대가(145-2) — 산근 단가는 서버 답 목록 그대로 · 내역(145-3)은 대가 단가로 블록마다
const sg = Object.fromEntries((kept.sg_list as Row[]).map((r) => [r.key, r]));
const labor = computeLabor(
@@ -121,7 +124,13 @@ export function computeSheets(payload: Row, user: Row): Row {
{ ...computeBase(payload.inputs, user), gaps: [] },
sg,
);
const base = computeEstimation(payload.inputs, user, (owner: string) => daegaPrice(labor, owner));
const books = { daega: labor.daega, sg_list: kept.sg_list, sg_hand: kept.sg_hand };
const base = computeEstimation(
payload.inputs,
user,
(owner: string) => daegaPrice(labor, owner),
books,
);
const { gaps: daegaGaps, ...daega } = labor;
return {
...base,