103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
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<string>(UNION_PINNED_PREFIX);
|
|
const UNION_TAIL = new Set<string>(UNION_SUFFIX);
|
|
|
|
/** Remaining manufacturing steps — matches catalog LAB_WORKFLOW_STEPS order. */
|
|
const UNION_MIDDLE_ORDER: Record<string, number> = {
|
|
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<string>();
|
|
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>): string[] {
|
|
const out = new Set<string>();
|
|
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, string>,
|
|
): string {
|
|
const parts = splitProsthesisGroupKey(groupKey);
|
|
if (parts.length === 0) return groupKey;
|
|
return parts.map((code) => labels.get(code) ?? code).join(' + ');
|
|
}
|