import { HttpStatus, Injectable, OnModuleInit } from '@nestjs/common'; import { AppException, ErrorCode } from '../../common/errors'; import { CatalogEntityKind } from '@prisma/client'; import { PrismaService } from '../../../prisma/prisma.service'; import { CatalogLabelService, CatalogLocale, normalizeCatalogLocale, } from '../catalog/catalog-label.service'; export type ProsthesisTypeCatalogEntry = { code: string; sortOrder: number; label: string; category: string; subcategory: string; chartRegion: string; stackGroup: string; }; type CatalogRow = { sortOrder: number; category: string; subcategory: string; chartRegion: string; stackGroup: string; }; @Injectable() export class ProsthesisCatalogService implements OnModuleInit { private loaded = false; private byCode = new Map(); constructor( private readonly prisma: PrismaService, private readonly catalogLabels: CatalogLabelService, ) {} async onModuleInit() { await this.refresh(); } async refresh(): Promise { const rows = await this.prisma.prosthesisType.findMany({ where: { isActive: true }, orderBy: [{ sortOrder: 'asc' }, { code: 'asc' }], select: { code: true, sortOrder: true, category: true, subcategory: true, chartRegion: true, stackGroup: true, }, }); this.byCode = new Map( rows.map((row) => [ row.code, { sortOrder: row.sortOrder, category: row.category, subcategory: row.subcategory, chartRegion: row.chartRegion, stackGroup: row.stackGroup, }, ]), ); this.loaded = true; } async list(localeInput?: string | null): Promise { await this.refresh(); const locale = normalizeCatalogLocale(localeInput); const codes = [...this.byCode.keys()]; const labels = await this.catalogLabels.resolveLabels( CatalogEntityKind.PROSTHESIS_TYPE, codes, locale, ); return codes .map((code) => { const row = this.byCode.get(code)!; return { code, sortOrder: row.sortOrder, label: labels.get(code) ?? code, category: row.category, subcategory: row.subcategory, chartRegion: row.chartRegion, stackGroup: row.stackGroup, }; }) .sort((a, b) => a.sortOrder - b.sortOrder || a.code.localeCompare(b.code)); } assertKnownProsthesisType(code: string): void { this.ensureLoaded(); if (!this.byCode.has(code)) { throw new AppException(ErrorCode.CATALOG_UNKNOWN_PROSTHESIS_TYPE, HttpStatus.BAD_REQUEST); } } async getStepCodesForProsthesisType(prosthesisTypeCode: string): Promise { const type = await this.prisma.prosthesisType.findUnique({ where: { code: prosthesisTypeCode }, select: { steps: { orderBy: { stepOrder: 'asc' }, select: { labWorkflowStep: { select: { code: true } } }, }, }, }); if (!type) { return []; } return type.steps.map((s) => s.labWorkflowStep.code); } async resolveStepLabels(stepCodes: string[], locale: CatalogLocale): Promise> { return this.catalogLabels.resolveLabels( CatalogEntityKind.LAB_WORKFLOW_STEP, stepCodes, locale, ); } private ensureLoaded() { if (!this.loaded) { throw new Error('Prosthesis catalog is not loaded yet'); } } }