feat(M01): 몫 줄 단가 = 단가 x 몫 비율 · 줄 모달 윗줄 요소조각 · 로직 알약이 그 로직을 엶 (PLAN 5장)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
2026-09-24 11:10:52 +09:00
co-authored by Claude Sonnet 5
parent cebd49980a
commit a523066de4
4 changed files with 69 additions and 13 deletions
@@ -26,6 +26,7 @@ import {
openMaterialModal,
openNoteModal,
openRefModal,
openLogic,
openTableModal,
type PickCond,
} from "./M01_MasterData_UI_LogicLab_Modal";
@@ -47,7 +48,7 @@ function toFragments(pieces: TextPiece[], ctx: DetailContext): Fragment[] {
openTableModal({ title: piece.글, table: ref.키, row: ref.행, col: ref.열 });
else if (piece.kind === "요소")
openRefModal({ title: piece.글, ref: ref.키, prices: ctx.prices });
else openNoteModal(piece.글, `${tl("Modal_Logic")} · ${ref.키}`);
else openLogic(ref.키);
};
return { pill: { label: piece.글, onClick: open }, ...dim };
});
@@ -93,7 +94,10 @@ interface Entry {
amount: number | null;
/** 덧줄이면 true */
extra: boolean;
open: () => void;
open: (self: Entry) => void;
/** 서버 조각으로 그린 단가 자리 · 수량 풀이 — 줄 모달이 씀(붙기 전엔 없음) */
unitFrag?: Fragment[];
piecesFrag?: Fragment[];
}
/** 재료 고르기 줄 — `요소` 가 글 대신 {구분·상세구분·규격·대표} 객체 */
@@ -194,7 +198,15 @@ const spread = (e: Entry, line?: CalcLine): Entry[] => {
...e,
group: OF_COST[k] ?? "other",
amount: v,
...(parts.length > 1 ? { share: k } : {}),
...(parts.length > 1
? {
share: k,
// 몫 줄 단가 = 단가 × (몫 금액 ÷ 줄 금액) — 곱하기 수량이 몫 금액과 맞게
...(line && line.금액 && line.단가 != null
? { price: formatMoney((line.단가 * v) / line.금액) }
: {}),
}
: {}),
}));
};
@@ -217,8 +229,10 @@ function entries(ctx: DetailContext): Entry[] {
price: price === undefined || price === null ? "" : formatMoney(price),
amount: line ? line.금액 : null,
extra: false,
open: () =>
open: (self) =>
openMaterialModal({
unitFrag: self.unitFrag,
qtyFrag: self.piecesFrag,
title: item.이름 ?? (cond ? condLabel(cond) : item.요소),
expr: item.수량,
...(cond ? { cond, unit: item.단위 } : { element: item.요소 }),
@@ -242,8 +256,10 @@ function entries(ctx: DetailContext): Entry[] {
price: "",
amount: line ? line.금액 : null,
extra: true,
open: () =>
open: (self) =>
openMaterialModal({
unitFrag: self.unitFrag,
qtyFrag: self.piecesFrag,
title: extra.이름,
expr: extra.식,
prices: ctx.prices,
@@ -267,7 +283,9 @@ function attachPieces(all: Entry[], ctx: DetailContext): void {
if (lines.length !== mine.length) continue;
mine.forEach((e, i) => {
const pieces = lines[i].조각;
if (pieces?.length) e.qtyFrag = toFragments(pieces, ctx);
if (pieces?.length) e.qtyFrag = e.piecesFrag = toFragments(pieces, ctx);
const unit = lines[i].요소조각;
if (unit?.length) e.unitFrag = toFragments(unit, ctx);
});
}
}
@@ -300,8 +318,8 @@ function lineRow(e: Entry): HTMLElement {
cellOf(e.amount === null ? "" : formatMoney(e.amount), "m01-logic__money"),
],
});
tr.addEventListener("click", e.open);
tr.addEventListener("keydown", (ev) => ev.key === "Enter" && e.open());
tr.addEventListener("click", () => e.open(e));
tr.addEventListener("keydown", (ev) => ev.key === "Enter" && e.open(e));
return tr;
}