diff --git a/M01_MasterData/SheetLogic/M01_SL_Table.ts b/M01_MasterData/SheetLogic/M01_SL_Table.ts index 8d046d340..97a341af4 100644 --- a/M01_MasterData/SheetLogic/M01_SL_Table.ts +++ b/M01_MasterData/SheetLogic/M01_SL_Table.ts @@ -165,7 +165,11 @@ async function loadInfo(key: string): Promise { /* ── 역할 · 알약 ── */ const rolesOf = (node: SlNode): TblRoles => ({ cond: node.pillsIn.map((p) => p.name), - out: node.pillsOut.filter((p) => !p.outside).map((p) => p.name), + out: [ + ...new Set( + node.pillsOut.filter((p) => !p.outside).map((p) => p.name.replace(/·(아래|위)$/, "")), + ), + ], }); /** 내 표 읽기 — 시트 블록에서 · 조건 칸은 입력 알약 이름 */ diff --git a/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts b/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts index 8f8984437..977fe606a 100644 --- a/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts +++ b/M01_MasterData/SheetLogic/M01_SL_Table_Build.ts @@ -77,17 +77,20 @@ const at = (r: number, c: number): string => `${colName(c)}${r}`; const MARGIN = 20; const quote = (s: string): string => `"${s.replace(/"/g, '""')}"`; -/** 블록 열 차례 — 조건 칸 · 값 칸 차례 그대로 · 범위 칸은 둘로 */ +/** 범위 칸인가 — 조건 칸이 「범위」 이거나 값 칸에 범위(배열) 줄이 하나라도 있으면(서버 표 블록과 같은 규칙) */ +export const isSplit = (info: TblInfo, c: string): boolean => + info.conds[c] === "범위" || (c in info.vals && info.rows.some((r) => Array.isArray(r[c]))); + +/** 블록 열 차례 — 조건 칸 → 값 칸 · 범위 칸은 「이름·아래」 「이름·위」 둘로(서버 `_table` 과 같은 차례) */ export function layoutOf(info: TblInfo): TblCol[] { const out: TblCol[] = []; - for (const c of Object.keys(info.conds)) - if (info.conds[c] === "범위") + for (const c of [...Object.keys(info.conds), ...Object.keys(info.vals)]) + if (isSplit(info, c)) out.push( { header: `${c}·아래`, of: c, part: "아래" }, { header: `${c}·위`, of: c, part: "위" }, ); else out.push({ header: c, of: c, part: "값" }); - for (const c of Object.keys(info.vals)) out.push({ header: c, of: c, part: "값" }); return out; } @@ -106,13 +109,8 @@ export function blockCells(info: TblInfo): Record { info.rows.forEach((row, i) => cols.forEach((col, c) => { const v = row[col.of]; - const x = Array.isArray(v) - ? col.part === "아래" - ? v[0] - : v[1] - : col.part === "값" - ? v - : null; + // 범위 칸에 숫자 하나면 아래 · 위에 같은 수 + const x = Array.isArray(v) ? (col.part === "아래" ? v[0] : v[1]) : v; const cell = cellOf(x); if (cell) cells[at(i + 2, c)] = cell; }), @@ -258,10 +256,11 @@ export function planLookup(info: TblInfo, roles: TblRoles): TblPlan { ); const outCells: Record = {}; for (const c of roles.out.filter((o) => o in info.vals)) { - outCells[c] = idxRow( - c, - `IF(${statusCell}=${quote("찾음")},INDEX(${rng(letter(c))},${idx}),NA())`, - ); + for (const h of isSplit(info, c) ? [`${c}·아래`, `${c}·위`] : [c]) + outCells[h] = idxRow( + h, + `IF(${statusCell}=${quote("찾음")},INDEX(${rng(letter(h))},${idx}),NA())`, + ); } return { cols, @@ -286,7 +285,7 @@ export function pillsOf( const pillsOut = Object.entries(plan.outCells).map(([name, cell]): SlPill => ({ name, cell, - unit: info.vals[name] || undefined, + unit: info.vals[name.replace(/·(아래|위)$/, "")] || undefined, })); if (outside) pillsOut.push({ name: "표", cell: "A1", outside: true }); return { pillsIn, pillsOut }; @@ -416,8 +415,10 @@ export function infoFromSheet(sheet: Sheet, prev: Pick