File size: 4,351 Bytes
c98875e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { DiagnosticCode, DiagnosticCodeDocument } from './schemas/diagnostic-code.schema';
import { ProcedureCode, ProcedureCodeDocument } from './schemas/procedure-code.schema';
import { CreateDiagnosticCodeDto } from './dto/create-diagnostic-code.dto';
import { CreateProcedureCodeDto } from './dto/create-procedure-code.dto';
import { SearchCodesDto } from './dto/search-codes.dto';

export interface GlobalSearchResult {
  icd10: Array<Record<string, any> & { type: string }>;
  cpt: Array<Record<string, any> & { type: string }>;
}

@Injectable()
export class CodesService {
  constructor(
    @InjectModel(DiagnosticCode.name) private icd10Model: Model<DiagnosticCodeDocument>,
    @InjectModel(ProcedureCode.name) private cptModel: Model<ProcedureCodeDocument>,
  ) {}

  // ─── ICD-10 ───────────────────────────────────────────────────────────────

  async searchICD10(dto: SearchCodesDto) {
    const { q, page = 1, limit = 20, category, billable } = dto;
    const filter: any = { isActive: true };

    if (q) {
      filter.$or = [
        { code: { $regex: q, $options: 'i' } },
        { description: { $regex: q, $options: 'i' } },
      ];
    }
    if (category) filter.category = category;
    if (billable !== undefined) filter.billable = billable;

    const [data, total] = await Promise.all([
      this.icd10Model
        .find(filter)
        .skip((page - 1) * limit)
        .limit(limit)
        .sort({ code: 1 }),
      this.icd10Model.countDocuments(filter),
    ]);

    return { data, total, page, limit, pages: Math.ceil(total / limit) };
  }

  async getICD10ByCode(code: string) {
    const result = await this.icd10Model.findOne({ code: code.toUpperCase() });
    if (!result) throw new NotFoundException(`ICD-10 code ${code} not found`);
    return result;
  }

  async createICD10(dto: CreateDiagnosticCodeDto) {
    return this.icd10Model.create(dto);
  }

  async getICD10Categories() {
    return this.icd10Model.distinct('category', { isActive: true });
  }

  // ─── CPT ──────────────────────────────────────────────────────────────────

  async searchCPT(dto: SearchCodesDto) {
    const { q, page = 1, limit = 20, category, billable } = dto;
    const filter: any = { isActive: true };

    if (q) {
      filter.$or = [
        { code: { $regex: q, $options: 'i' } },
        { description: { $regex: q, $options: 'i' } },
      ];
    }
    if (category) filter.category = category;
    if (billable !== undefined) filter.billable = billable;

    const [data, total] = await Promise.all([
      this.cptModel
        .find(filter)
        .skip((page - 1) * limit)
        .limit(limit)
        .sort({ code: 1 }),
      this.cptModel.countDocuments(filter),
    ]);

    return { data, total, page, limit, pages: Math.ceil(total / limit) };
  }

  async getCPTByCode(code: string) {
    const result = await this.cptModel.findOne({ code });
    if (!result) throw new NotFoundException(`CPT code ${code} not found`);
    return result;
  }

  async createCPT(dto: CreateProcedureCodeDto) {
    return this.cptModel.create(dto);
  }

  async getCPTCategories() {
    return this.cptModel.distinct('category', { isActive: true });
  }

  // ─── Global search ────────────────────────────────────────────────────────

  async globalSearch(q: string, limit = 10): Promise<GlobalSearchResult> {
    const filter = {
      isActive: true,
      $or: [
        { code: { $regex: q, $options: 'i' } },
        { description: { $regex: q, $options: 'i' } },
      ],
    };

    const [icd10, cpt] = await Promise.all([
      this.icd10Model.find(filter).limit(limit).lean(),
      this.cptModel.find(filter).limit(limit).lean(),
    ]);

    return {
      icd10: icd10.map((c) => ({ ...c, type: 'ICD-10' })),
      cpt: cpt.map((c) => ({ ...c, type: 'CPT' })),
    };
  }
}