@blackcode_sa/metaestetics-api 1.12.67 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/admin/index.d.mts +801 -2
  2. package/dist/admin/index.d.ts +801 -2
  3. package/dist/admin/index.js +2332 -153
  4. package/dist/admin/index.mjs +2321 -153
  5. package/dist/backoffice/index.d.mts +40 -0
  6. package/dist/backoffice/index.d.ts +40 -0
  7. package/dist/backoffice/index.js +118 -18
  8. package/dist/backoffice/index.mjs +118 -20
  9. package/dist/index.d.mts +1097 -2
  10. package/dist/index.d.ts +1097 -2
  11. package/dist/index.js +4224 -2091
  12. package/dist/index.mjs +3941 -1821
  13. package/package.json +1 -1
  14. package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +140 -0
  15. package/src/admin/analytics/analytics.admin.service.ts +278 -0
  16. package/src/admin/analytics/index.ts +2 -0
  17. package/src/admin/index.ts +6 -0
  18. package/src/backoffice/services/README.md +17 -0
  19. package/src/backoffice/services/analytics.service.proposal.md +863 -0
  20. package/src/backoffice/services/analytics.service.summary.md +143 -0
  21. package/src/backoffice/services/category.service.ts +49 -6
  22. package/src/backoffice/services/subcategory.service.ts +50 -6
  23. package/src/backoffice/services/technology.service.ts +53 -6
  24. package/src/services/analytics/ARCHITECTURE.md +199 -0
  25. package/src/services/analytics/CLOUD_FUNCTIONS.md +225 -0
  26. package/src/services/analytics/GROUPED_ANALYTICS.md +501 -0
  27. package/src/services/analytics/QUICK_START.md +393 -0
  28. package/src/services/analytics/README.md +287 -0
  29. package/src/services/analytics/SUMMARY.md +141 -0
  30. package/src/services/analytics/USAGE_GUIDE.md +518 -0
  31. package/src/services/analytics/analytics-cloud.service.ts +222 -0
  32. package/src/services/analytics/analytics.service.ts +1632 -0
  33. package/src/services/analytics/index.ts +3 -0
  34. package/src/services/analytics/utils/appointment-filtering.utils.ts +138 -0
  35. package/src/services/analytics/utils/cost-calculation.utils.ts +154 -0
  36. package/src/services/analytics/utils/grouping.utils.ts +394 -0
  37. package/src/services/analytics/utils/stored-analytics.utils.ts +347 -0
  38. package/src/services/analytics/utils/time-calculation.utils.ts +186 -0
  39. package/src/services/appointment/appointment.service.ts +50 -6
  40. package/src/services/index.ts +1 -0
  41. package/src/services/procedure/procedure.service.ts +3 -3
  42. package/src/types/analytics/analytics.types.ts +500 -0
  43. package/src/types/analytics/grouped-analytics.types.ts +148 -0
  44. package/src/types/analytics/index.ts +4 -0
  45. package/src/types/analytics/stored-analytics.types.ts +137 -0
  46. package/src/types/index.ts +3 -0
  47. package/src/types/notifications/index.ts +21 -0
@@ -0,0 +1,393 @@
1
+ # Analytics Service - Quick Start Guide
2
+
3
+ ## 🚀 What We Can Do
4
+
5
+ The Analytics Service provides **comprehensive insights** into your clinic operations:
6
+
7
+ - **Financial Intelligence**: Revenue, costs, payment tracking
8
+ - **Practitioner Performance**: Doctor efficiency, cancellations, revenue
9
+ - **Procedure Analytics**: Popularity, profitability, product usage
10
+ - **Time Management**: Booked vs actual time, efficiency
11
+ - **Cancellation & No-Show Analysis**: Rates by clinic/doctor/patient/procedure
12
+ - **Patient Insights**: Lifetime value, retention, behavior
13
+ - **Product Usage**: Usage patterns, revenue contribution
14
+ - **Clinic Performance**: Overall metrics and comparisons
15
+
16
+ ---
17
+
18
+ ## 🔄 How It Works: Computed vs On-Demand
19
+
20
+ ### **Pre-Computed Analytics** (Default - Recommended) ⚡
21
+
22
+ **How:**
23
+ 1. Cloud Function runs **every 12 hours**
24
+ 2. Computes analytics for all clinics
25
+ 3. Stores in Firestore: `clinics/{clinicBranchId}/analytics/`
26
+ 4. Client reads cached data (instant!)
27
+
28
+ **Benefits:**
29
+ - ⚡ **Fast**: 1 document read = instant response
30
+ - 💰 **Cheap**: 1 read vs hundreds/thousands
31
+ - 📈 **Scalable**: Works with large datasets
32
+
33
+ **Example:**
34
+ ```typescript
35
+ // Automatically uses cached data if fresh (< 12 hours)
36
+ const dashboard = await analyticsService.getDashboardData(
37
+ { clinicBranchId: 'clinic-123' },
38
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
39
+ );
40
+ // Returns instantly! ⚡
41
+ ```
42
+
43
+ ### **On-Demand Calculation** (Fallback) 🔄
44
+
45
+ **When:**
46
+ - No cached data exists
47
+ - Cache is stale (> 12 hours)
48
+ - You set `useCache: false`
49
+ - Custom date ranges
50
+
51
+ **How:**
52
+ 1. Queries all appointments
53
+ 2. Calculates metrics in real-time
54
+ 3. Returns results
55
+
56
+ **Example:**
57
+ ```typescript
58
+ // Force on-demand calculation
59
+ const dashboard = await analyticsService.getDashboardData(
60
+ { clinicBranchId: 'clinic-123' },
61
+ dateRange,
62
+ { useCache: false } // Force calculation
63
+ );
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 📊 Available Analytics
69
+
70
+ ### 1. **Practitioner Analytics** 👨‍⚕️
71
+ ```typescript
72
+ const metrics = await analyticsService.getPractitionerAnalytics(
73
+ 'practitioner-id',
74
+ dateRange
75
+ );
76
+ // Returns: appointments, cancellations, no-shows, revenue, time efficiency, etc.
77
+ ```
78
+
79
+ ### 2. **Procedure Analytics** 🏥
80
+ ```typescript
81
+ // Single procedure
82
+ const procedure = await analyticsService.getProcedureAnalytics('procedure-id', dateRange);
83
+
84
+ // All procedures
85
+ const all = await analyticsService.getProcedureAnalytics(undefined, dateRange);
86
+
87
+ // Most popular
88
+ const popular = await analyticsService.getProcedurePopularity(dateRange, 10);
89
+
90
+ // Most profitable
91
+ const profitable = await analyticsService.getProcedureProfitability(dateRange, 10);
92
+ ```
93
+
94
+ ### 3. **Time Efficiency** ⏱️
95
+ ```typescript
96
+ const time = await analyticsService.getTimeEfficiencyMetrics(
97
+ { clinicBranchId: 'clinic-123' },
98
+ dateRange
99
+ );
100
+ // Returns: efficiency %, overrun, underutilization, etc.
101
+ ```
102
+
103
+ ### 4. **Cancellation Metrics** ❌
104
+ ```typescript
105
+ const cancellations = await analyticsService.getCancellationMetrics(
106
+ 'practitioner', // or 'clinic', 'patient', 'procedure'
107
+ dateRange
108
+ );
109
+ // Returns array with cancellation rates, reasons, lead times
110
+ ```
111
+
112
+ ### 5. **No-Show Metrics** 🚫
113
+ ```typescript
114
+ const noShows = await analyticsService.getNoShowMetrics(
115
+ 'practitioner', // or 'clinic', 'patient', 'procedure'
116
+ dateRange
117
+ );
118
+ // Returns array with no-show rates per entity
119
+ ```
120
+
121
+ ### 6. **Revenue Metrics** 💰
122
+ ```typescript
123
+ const revenue = await analyticsService.getRevenueMetrics(
124
+ { clinicBranchId: 'clinic-123' },
125
+ dateRange
126
+ );
127
+ // Returns: total revenue, by status, by payment status, unpaid, etc.
128
+ ```
129
+
130
+ ### 7. **Product Usage** 📦
131
+ ```typescript
132
+ const products = await analyticsService.getProductUsageMetrics(
133
+ undefined, // or 'product-id'
134
+ dateRange
135
+ );
136
+ // Returns: usage counts, revenue, quantities per product
137
+ ```
138
+
139
+ ### 8. **Patient Analytics** 👥
140
+ ```typescript
141
+ const patient = await analyticsService.getPatientAnalytics(
142
+ 'patient-id', // or undefined for all
143
+ dateRange
144
+ );
145
+ // Returns: lifetime value, retention, frequency, etc.
146
+ ```
147
+
148
+ ### 9. **Dashboard Data** 📊
149
+ ```typescript
150
+ const dashboard = await analyticsService.getDashboardData(
151
+ { clinicBranchId: 'clinic-123' },
152
+ dateRange
153
+ );
154
+ // Returns: comprehensive dashboard with all metrics
155
+ ```
156
+
157
+ ---
158
+
159
+ ## 🎯 Specific Use Case: Patient No-Show Per Doctor
160
+
161
+ ### **Question**: "Which patients have the highest no-show rate for each doctor?"
162
+
163
+ ### **Solution 1: No-Show Rates by Practitioner** ✅
164
+
165
+ ```typescript
166
+ // Get no-show metrics grouped by practitioner (doctor)
167
+ const noShowByPractitioner = await analyticsService.getNoShowMetrics(
168
+ 'practitioner',
169
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
170
+ );
171
+
172
+ // Returns array:
173
+ // [
174
+ // {
175
+ // entityId: 'practitioner-123',
176
+ // entityName: 'Dr. Smith',
177
+ // entityType: 'practitioner',
178
+ // totalAppointments: 100,
179
+ // noShowAppointments: 15,
180
+ // noShowRate: 15.0 // 15%
181
+ // },
182
+ // ...
183
+ // ]
184
+
185
+ // Sort by no-show rate (highest first)
186
+ const sorted = noShowByPractitioner
187
+ .sort((a, b) => b.noShowRate - a.noShowRate);
188
+
189
+ console.log('Doctors ranked by no-show rate:');
190
+ sorted.forEach((doctor, index) => {
191
+ console.log(`${index + 1}. ${doctor.entityName}: ${doctor.noShowRate}%`);
192
+ });
193
+ ```
194
+
195
+ ### **Solution 2: Patient No-Shows for a Specific Doctor** ✅
196
+
197
+ ```typescript
198
+ // Step 1: Get all appointments for a specific practitioner
199
+ const practitionerId = 'practitioner-123';
200
+ const dateRange = { start: new Date('2024-01-01'), end: new Date('2024-12-31') };
201
+
202
+ // Get practitioner analytics (includes no-show count)
203
+ const practitionerMetrics = await analyticsService.getPractitionerAnalytics(
204
+ practitionerId,
205
+ dateRange
206
+ );
207
+
208
+ console.log(`Dr. ${practitionerMetrics.practitionerName}:`);
209
+ console.log(` Total Appointments: ${practitionerMetrics.totalAppointments}`);
210
+ console.log(` No-Shows: ${practitionerMetrics.noShowAppointments}`);
211
+ console.log(` No-Show Rate: ${practitionerMetrics.noShowRate}%`);
212
+
213
+ // Step 2: Get patient-level breakdown for this doctor
214
+ // (You can filter appointments manually or use patient analytics)
215
+
216
+ // Get patient analytics filtered by practitioner
217
+ const allPatients = await analyticsService.getPatientAnalytics(undefined, dateRange);
218
+
219
+ // Filter patients who had appointments with this practitioner
220
+ // and calculate their no-show rates
221
+ const patientsWithThisDoctor = allPatients
222
+ .filter(patient => {
223
+ // Check if patient had appointments with this practitioner
224
+ // (You'd need to query appointments or enhance patient analytics)
225
+ return true; // Placeholder
226
+ })
227
+ .sort((a, b) => b.noShowRate - a.noShowRate);
228
+
229
+ console.log('\nPatients with highest no-show rates for this doctor:');
230
+ patientsWithThisDoctor.slice(0, 10).forEach((patient, index) => {
231
+ console.log(`${index + 1}. ${patient.patientName}: ${patient.noShowRate}%`);
232
+ });
233
+ ```
234
+
235
+ ### **Solution 3: Cross-Analysis (No-Shows by Patient AND Doctor)** ✅
236
+
237
+ ```typescript
238
+ // Get no-shows grouped by patient
239
+ const noShowByPatient = await analyticsService.getNoShowMetrics(
240
+ 'patient',
241
+ dateRange
242
+ );
243
+
244
+ // Get no-shows grouped by practitioner
245
+ const noShowByPractitioner = await analyticsService.getNoShowMetrics(
246
+ 'practitioner',
247
+ dateRange
248
+ );
249
+
250
+ // For detailed cross-analysis, you can:
251
+ // 1. Get all appointments
252
+ // 2. Filter no-shows
253
+ // 3. Group by practitioner AND patient
254
+ const appointments = await analyticsService['fetchAppointments'](
255
+ undefined,
256
+ dateRange
257
+ );
258
+
259
+ const noShows = appointments.filter(a => a.status === AppointmentStatus.NO_SHOW);
260
+
261
+ // Create a map: practitioner -> patients -> no-show count
262
+ const practitionerPatientMap = new Map<
263
+ string,
264
+ Map<string, { patientName: string; noShowCount: number; totalAppointments: number }>
265
+ >();
266
+
267
+ appointments.forEach(appointment => {
268
+ const practitionerId = appointment.practitionerId;
269
+ const patientId = appointment.patientId;
270
+ const patientName = appointment.patientInfo?.fullName || 'Unknown';
271
+
272
+ if (!practitionerPatientMap.has(practitionerId)) {
273
+ practitionerPatientMap.set(practitionerId, new Map());
274
+ }
275
+
276
+ const patientMap = practitionerPatientMap.get(practitionerId)!;
277
+ if (!patientMap.has(patientId)) {
278
+ patientMap.set(patientId, { patientName, noShowCount: 0, totalAppointments: 0 });
279
+ }
280
+
281
+ const patientData = patientMap.get(patientId)!;
282
+ patientData.totalAppointments++;
283
+
284
+ if (appointment.status === AppointmentStatus.NO_SHOW) {
285
+ patientData.noShowCount++;
286
+ }
287
+ });
288
+
289
+ // Display results
290
+ practitionerPatientMap.forEach((patientMap, practitionerId) => {
291
+ const practitioner = appointments.find(a => a.practitionerId === practitionerId);
292
+ const practitionerName = practitioner?.practitionerInfo?.name || 'Unknown';
293
+
294
+ console.log(`\n${practitionerName}:`);
295
+
296
+ const patientRates = Array.from(patientMap.entries())
297
+ .map(([patientId, data]) => ({
298
+ patientId,
299
+ patientName: data.patientName,
300
+ noShowRate: (data.noShowCount / data.totalAppointments) * 100,
301
+ noShowCount: data.noShowCount,
302
+ totalAppointments: data.totalAppointments,
303
+ }))
304
+ .sort((a, b) => b.noShowRate - a.noShowRate);
305
+
306
+ patientRates.forEach(patient => {
307
+ console.log(` ${patient.patientName}: ${patient.noShowRate.toFixed(1)}% (${patient.noShowCount}/${patient.totalAppointments})`);
308
+ });
309
+ });
310
+ ```
311
+
312
+ ---
313
+
314
+ ## 📝 Quick Examples
315
+
316
+ ### Example 1: Doctor Performance Dashboard
317
+ ```typescript
318
+ const practitionerMetrics = await analyticsService.getPractitionerAnalytics(
319
+ 'practitioner-id',
320
+ dateRange
321
+ );
322
+
323
+ console.log(`Doctor: ${practitionerMetrics.practitionerName}`);
324
+ console.log(`Appointments: ${practitionerMetrics.totalAppointments}`);
325
+ console.log(`Cancellation Rate: ${practitionerMetrics.cancellationRate}%`);
326
+ console.log(`No-Show Rate: ${practitionerMetrics.noShowRate}%`);
327
+ console.log(`Time Efficiency: ${practitionerMetrics.timeEfficiency}%`);
328
+ console.log(`Total Revenue: ${practitionerMetrics.totalRevenue} ${practitionerMetrics.currency}`);
329
+ ```
330
+
331
+ ### Example 2: Top Procedures by Revenue
332
+ ```typescript
333
+ const topProcedures = await analyticsService.getProcedureProfitability(
334
+ dateRange,
335
+ 10
336
+ );
337
+
338
+ topProcedures.forEach((procedure, index) => {
339
+ console.log(`${index + 1}. ${procedure.procedureName}`);
340
+ console.log(` Revenue: ${procedure.totalRevenue}`);
341
+ console.log(` Appointments: ${procedure.appointmentCount}`);
342
+ });
343
+ ```
344
+
345
+ ### Example 3: Clinic Comparison
346
+ ```typescript
347
+ const clinic1 = await analyticsService.getClinicAnalytics('clinic-1', dateRange);
348
+ const clinic2 = await analyticsService.getClinicAnalytics('clinic-2', dateRange);
349
+
350
+ console.log('Clinic Comparison:');
351
+ console.log(`Clinic 1 Revenue: ${clinic1.totalRevenue}`);
352
+ console.log(`Clinic 2 Revenue: ${clinic2.totalRevenue}`);
353
+ console.log(`Clinic 1 Cancellation Rate: ${clinic1.cancellationRate}%`);
354
+ console.log(`Clinic 2 Cancellation Rate: ${clinic2.cancellationRate}%`);
355
+ ```
356
+
357
+ ---
358
+
359
+ ## 🎛️ Configuration
360
+
361
+ ### Change Cache Freshness
362
+ ```typescript
363
+ // Use cache if data is less than 6 hours old
364
+ const metrics = await analyticsService.getPractitionerAnalytics(
365
+ practitionerId,
366
+ dateRange,
367
+ { maxCacheAgeHours: 6 }
368
+ );
369
+ ```
370
+
371
+ ### Force On-Demand Calculation
372
+ ```typescript
373
+ const metrics = await analyticsService.getPractitionerAnalytics(
374
+ practitionerId,
375
+ dateRange,
376
+ { useCache: false }
377
+ );
378
+ ```
379
+
380
+ ### Change Cloud Function Schedule
381
+ Edit `Cloud/functions/src/analytics/computeAnalytics.ts`:
382
+ ```typescript
383
+ schedule: "every 6 hours" // or "every 24 hours"
384
+ ```
385
+
386
+ ---
387
+
388
+ ## 📚 Full Documentation
389
+
390
+ - **Usage Guide**: `USAGE_GUIDE.md` - Complete guide with all use cases
391
+ - **Architecture**: `ARCHITECTURE.md` - How computed vs on-demand works
392
+ - **README**: `README.md` - API reference
393
+
@@ -0,0 +1,287 @@
1
+ # Analytics Service
2
+
3
+ Comprehensive financial and analytical intelligence service for the Clinic Admin app. Provides insights about doctors, procedures, appointments, patients, products, and clinic operations.
4
+
5
+ ## Overview
6
+
7
+ The `AnalyticsService` extends `BaseService` and provides methods to analyze appointment data, calculate metrics, and generate reports for clinic administration.
8
+
9
+ ## Usage
10
+
11
+ ### Initialization
12
+
13
+ ```typescript
14
+ import { AnalyticsService } from '@blackcode_sa/metaestetics-api';
15
+ import { AppointmentService } from '@blackcode_sa/metaestetics-api';
16
+
17
+ // Initialize dependencies
18
+ const appointmentService = new AppointmentService(db, auth, app, ...);
19
+
20
+ // Initialize analytics service
21
+ const analyticsService = new AnalyticsService(db, auth, app, appointmentService);
22
+ ```
23
+
24
+ ## Available Methods
25
+
26
+ ### ⚡ **Grouped Analytics** (NEW!)
27
+
28
+ All analytics now support **consistent grouping** by clinic, practitioner, procedure, patient, or technology:
29
+
30
+ - `getRevenueMetricsByEntity(groupBy, dateRange?, filters?)` - Revenue by entity
31
+ - `getProductUsageMetricsByEntity(groupBy, dateRange?, filters?)` - Product usage by entity
32
+ - `getTimeEfficiencyMetricsByEntity(groupBy, dateRange?, filters?)` - Time efficiency by entity
33
+ - `getPatientBehaviorMetricsByEntity(groupBy, dateRange?, filters?)` - Patient behavior by entity
34
+ - `getCancellationMetrics(groupBy, dateRange?)` - Cancellations by entity (existing)
35
+ - `getNoShowMetrics(groupBy, dateRange?)` - No-shows by entity (existing)
36
+
37
+ **Technology Grouping**: Groups all procedures using the same technology across all doctors (e.g., all Botox treatments regardless of which doctor performed them).
38
+
39
+ **See [GROUPED_ANALYTICS.md](./GROUPED_ANALYTICS.md) for complete guide and examples.**
40
+
41
+ ### Practitioner Analytics
42
+
43
+ #### `getPractitionerAnalytics(practitionerId, dateRange?)`
44
+
45
+ Get comprehensive performance metrics for a practitioner.
46
+
47
+ **Returns**: `PractitionerAnalytics`
48
+
49
+ **Example**:
50
+ ```typescript
51
+ const metrics = await analyticsService.getPractitionerAnalytics(
52
+ 'practitioner-id-123',
53
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
54
+ );
55
+
56
+ console.log(`Total appointments: ${metrics.totalAppointments}`);
57
+ console.log(`Cancellation rate: ${metrics.cancellationRate}%`);
58
+ console.log(`Total revenue: ${metrics.totalRevenue} ${metrics.currency}`);
59
+ ```
60
+
61
+ ### Procedure Analytics
62
+
63
+ #### `getProcedureAnalytics(procedureId?, dateRange?)`
64
+
65
+ Get performance metrics for one or all procedures.
66
+
67
+ **Returns**: `ProcedureAnalytics | ProcedureAnalytics[]`
68
+
69
+ **Example**:
70
+ ```typescript
71
+ // Get analytics for a specific procedure
72
+ const procedureMetrics = await analyticsService.getProcedureAnalytics(
73
+ 'procedure-id-123',
74
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
75
+ );
76
+
77
+ // Get analytics for all procedures
78
+ const allProcedures = await analyticsService.getProcedureAnalytics(
79
+ undefined,
80
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
81
+ );
82
+ ```
83
+
84
+ #### `getProcedurePopularity(dateRange?, limit?)`
85
+
86
+ Get the most popular procedures by appointment count.
87
+
88
+ **Returns**: `ProcedurePopularity[]`
89
+
90
+ **Example**:
91
+ ```typescript
92
+ const topProcedures = await analyticsService.getProcedurePopularity(
93
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') },
94
+ 10 // Top 10 procedures
95
+ );
96
+ ```
97
+
98
+ #### `getProcedureProfitability(dateRange?, limit?)`
99
+
100
+ Get the most profitable procedures by revenue.
101
+
102
+ **Returns**: `ProcedureProfitability[]`
103
+
104
+ **Example**:
105
+ ```typescript
106
+ const profitableProcedures = await analyticsService.getProcedureProfitability(
107
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') },
108
+ 10
109
+ );
110
+ ```
111
+
112
+ ### Time Efficiency Analytics
113
+
114
+ #### `getTimeEfficiencyMetrics(filters?, dateRange?)`
115
+
116
+ Analyze booked time vs actual time spent on appointments.
117
+
118
+ **Returns**: `TimeEfficiencyMetrics`
119
+
120
+ **Example**:
121
+ ```typescript
122
+ const timeMetrics = await analyticsService.getTimeEfficiencyMetrics(
123
+ { clinicBranchId: 'clinic-123' },
124
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
125
+ );
126
+
127
+ console.log(`Average efficiency: ${timeMetrics.averageEfficiency}%`);
128
+ console.log(`Average overrun: ${timeMetrics.averageOverrun} minutes`);
129
+ ```
130
+
131
+ ### Cancellation & No-Show Analytics
132
+
133
+ #### `getCancellationMetrics(groupBy, dateRange?)`
134
+
135
+ Get cancellation metrics grouped by clinic, practitioner, patient, or procedure.
136
+
137
+ **Returns**: `CancellationMetrics | CancellationMetrics[]`
138
+
139
+ **Example**:
140
+ ```typescript
141
+ // Get cancellations by clinic
142
+ const clinicCancellations = await analyticsService.getCancellationMetrics(
143
+ 'clinic',
144
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
145
+ );
146
+
147
+ // Get cancellations by practitioner
148
+ const practitionerCancellations = await analyticsService.getCancellationMetrics(
149
+ 'practitioner',
150
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
151
+ );
152
+ ```
153
+
154
+ #### `getNoShowMetrics(groupBy, dateRange?)`
155
+
156
+ Get no-show metrics grouped by clinic, practitioner, patient, or procedure.
157
+
158
+ **Returns**: `NoShowMetrics | NoShowMetrics[]`
159
+
160
+ **Example**:
161
+ ```typescript
162
+ const noShowMetrics = await analyticsService.getNoShowMetrics(
163
+ 'patient',
164
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
165
+ );
166
+ ```
167
+
168
+ ### Financial Analytics
169
+
170
+ #### `getRevenueMetrics(filters?, dateRange?)`
171
+
172
+ Get comprehensive revenue metrics.
173
+
174
+ **Returns**: `RevenueMetrics`
175
+
176
+ **Example**:
177
+ ```typescript
178
+ const revenue = await analyticsService.getRevenueMetrics(
179
+ { clinicBranchId: 'clinic-123' },
180
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
181
+ );
182
+
183
+ console.log(`Total revenue: ${revenue.totalRevenue} ${revenue.currency}`);
184
+ console.log(`Unpaid revenue: ${revenue.unpaidRevenue}`);
185
+ ```
186
+
187
+ ### Product Usage Analytics
188
+
189
+ #### `getProductUsageMetrics(productId?, dateRange?)`
190
+
191
+ Get product usage metrics for one or all products.
192
+
193
+ **Returns**: `ProductUsageMetrics | ProductUsageMetrics[]`
194
+
195
+ **Example**:
196
+ ```typescript
197
+ // Get usage for a specific product
198
+ const productMetrics = await analyticsService.getProductUsageMetrics(
199
+ 'product-id-123',
200
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
201
+ );
202
+
203
+ // Get usage for all products
204
+ const allProducts = await analyticsService.getProductUsageMetrics(
205
+ undefined,
206
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
207
+ );
208
+ ```
209
+
210
+ ### Patient Analytics
211
+
212
+ #### `getPatientAnalytics(patientId?, dateRange?)`
213
+
214
+ Get analytics for one or all patients.
215
+
216
+ **Returns**: `PatientAnalytics | PatientAnalytics[]`
217
+
218
+ **Example**:
219
+ ```typescript
220
+ const patientMetrics = await analyticsService.getPatientAnalytics(
221
+ 'patient-id-123',
222
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
223
+ );
224
+
225
+ console.log(`Lifetime value: ${patientMetrics.lifetimeValue}`);
226
+ console.log(`Average days between appointments: ${patientMetrics.averageDaysBetweenAppointments}`);
227
+ ```
228
+
229
+ ### Dashboard Analytics
230
+
231
+ #### `getDashboardData(filters?, dateRange?)`
232
+
233
+ Get comprehensive dashboard data aggregation.
234
+
235
+ **Returns**: `DashboardAnalytics`
236
+
237
+ **Example**:
238
+ ```typescript
239
+ const dashboard = await analyticsService.getDashboardData(
240
+ { clinicBranchId: 'clinic-123' },
241
+ { start: new Date('2024-01-01'), end: new Date('2024-12-31') }
242
+ );
243
+
244
+ console.log(`Total appointments: ${dashboard.overview.totalAppointments}`);
245
+ console.log(`Total revenue: ${dashboard.overview.totalRevenue}`);
246
+ console.log(`Top practitioners:`, dashboard.practitionerMetrics);
247
+ ```
248
+
249
+ ## Cost Calculation Priority
250
+
251
+ The service calculates appointment costs using the following priority:
252
+
253
+ 1. **`metadata.finalbilling.finalPrice`** - If available, uses the final billing price
254
+ 2. **`metadata.zonesData`** - Calculates from zone item subtotals
255
+ 3. **`appointment.cost`** - Falls back to base appointment cost
256
+
257
+ ## Time Efficiency Calculation
258
+
259
+ Time efficiency is calculated as:
260
+ - **Booked Duration**: `appointmentEndTime - appointmentStartTime` (minutes)
261
+ - **Actual Duration**: `actualDurationMinutes` or booked duration if not available
262
+ - **Efficiency**: `(actualDuration / bookedDuration) * 100` (percentage)
263
+ - **Overrun**: Positive difference if actual > booked
264
+ - **Underutilization**: Positive difference if booked > actual
265
+
266
+ ## Status Filtering
267
+
268
+ The service filters appointments by status:
269
+
270
+ - **Completed**: `AppointmentStatus.COMPLETED`
271
+ - **Canceled**: `CANCELED_PATIENT`, `CANCELED_CLINIC`, `CANCELED_PATIENT_RESCHEDULED`
272
+ - **No-Show**: `AppointmentStatus.NO_SHOW`
273
+ - **Active**: All statuses except canceled and no-show
274
+
275
+ ## Performance Considerations
276
+
277
+ - The service uses the `AppointmentService` to query appointments efficiently
278
+ - Large date ranges may require pagination (to be implemented)
279
+ - Consider caching dashboard data for frequently accessed metrics
280
+ - Use specific filters to reduce query scope when possible
281
+
282
+ ## Related Documentation
283
+
284
+ - [Analytics Types](../../types/analytics/analytics.types.ts)
285
+ - [Appointment Service](../appointment/README.md)
286
+ - [Full Proposal](../../backoffice/services/analytics.service.proposal.md)
287
+