import type { Prisma } from '@prisma/client'; const UNION_PINNED_PREFIX = [ 'intraoral_scan', 'choosing_abutment', 'design', 'model_create', ] as const; const UNION_SUFFIX = ['packing', 'shipping'] as const; const UNION_PINNED = new Set(UNION_PINNED_PREFIX); const UNION_TAIL = new Set(UNION_SUFFIX); /** Remaining manufacturing steps — matches catalog LAB_WORKFLOW_STEPS order. */ const UNION_MIDDLE_ORDER: Record = { milling_dry: 1, milling_wet: 2, printer_resin: 3, printer_metal: 4, pressing: 5, sinter: 6, build_up: 7, stain: 8, glaze: 9, polish_prep: 10, }; /** Union stacked workflows: pin scan → abutment → design → model, then catalog order, pack/ship last. */ export function unionWorkflowStepCodes(lists: readonly (readonly string[])[]): string[] { const present = new Set(); for (const list of lists) { for (const code of list) { if (code) present.add(code); } } const out: string[] = []; for (const code of UNION_PINNED_PREFIX) { if (present.has(code)) out.push(code); } const middle = [...present].filter((code) => !UNION_PINNED.has(code) && !UNION_TAIL.has(code)); middle.sort((a, b) => { const da = UNION_MIDDLE_ORDER[a] ?? 100; const db = UNION_MIDDLE_ORDER[b] ?? 100; if (da !== db) return da - db; return a.localeCompare(b); }); out.push(...middle); for (const code of UNION_SUFFIX) { if (present.has(code)) out.push(code); } return out; } export function prosthesisGroupKey(codes: readonly string[]): string { return [...new Set(codes.filter(Boolean))].sort().join('+'); } export function splitProsthesisGroupKey(key: string): string[] { return key.split('+').map((part) => part.trim()).filter(Boolean); } export function prosthesisTypeTaskWhere(code: string): Prisma.LabCaseTaskWhereInput { return { OR: [ { prosthesisTypeCode: code }, { prosthesisTypeCode: { startsWith: `${code}+` } }, { prosthesisTypeCode: { endsWith: `+${code}` } }, { prosthesisTypeCode: { contains: `+${code}+` } }, ], }; } export function prosthesisTypeCaseWhere(code: string): Prisma.LabCaseWhereInput { return { OR: [ { toothProsthesis: { some: { prosthesisTypeCode: code } } }, { tasks: { some: prosthesisTypeTaskWhere(code) } }, ], }; } export function atomicProsthesisCodes(codes: Iterable): string[] { const out = new Set(); for (const code of codes) { if (!code) continue; for (const part of splitProsthesisGroupKey(code)) out.add(part); } return [...out]; } export function prosthesisGroupLabel( groupKey: string, labels: ReadonlyMap, ): string { const parts = splitProsthesisGroupKey(groupKey); if (parts.length === 0) return groupKey; return parts.map((code) => labels.get(code) ?? code).join(' + '); }