@blackcode_sa/metaestetics-api 1.7.47 → 1.8.1

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 (83) hide show
  1. package/dist/admin/index.d.mts +1815 -1667
  2. package/dist/admin/index.d.ts +1815 -1667
  3. package/dist/admin/index.js +6993 -6535
  4. package/dist/admin/index.mjs +6991 -6532
  5. package/dist/backoffice/index.d.mts +1418 -1418
  6. package/dist/backoffice/index.d.ts +1418 -1418
  7. package/dist/backoffice/index.js +1464 -1454
  8. package/dist/backoffice/index.mjs +1509 -1499
  9. package/dist/index.d.mts +16217 -14582
  10. package/dist/index.d.ts +16217 -14582
  11. package/dist/index.js +11419 -13806
  12. package/dist/index.mjs +12081 -14593
  13. package/package.json +5 -5
  14. package/src/admin/aggregation/appointment/index.ts +1 -0
  15. package/src/admin/aggregation/clinic/index.ts +1 -0
  16. package/src/admin/aggregation/forms/index.ts +1 -0
  17. package/src/admin/aggregation/index.ts +8 -0
  18. package/src/admin/aggregation/patient/index.ts +1 -0
  19. package/src/admin/aggregation/practitioner/index.ts +1 -0
  20. package/src/admin/aggregation/practitioner-invite/index.ts +1 -0
  21. package/src/admin/aggregation/procedure/index.ts +1 -0
  22. package/src/admin/aggregation/reviews/index.ts +1 -0
  23. package/src/admin/booking/index.ts +1 -1
  24. package/src/admin/calendar/index.ts +1 -0
  25. package/src/admin/documentation-templates/index.ts +1 -0
  26. package/src/admin/free-consultation/index.ts +1 -0
  27. package/src/admin/index.ts +23 -118
  28. package/src/admin/mailing/appointment/index.ts +1 -0
  29. package/src/admin/mailing/index.ts +1 -2
  30. package/src/admin/mailing/practitionerInvite/index.ts +1 -0
  31. package/src/admin/notifications/index.ts +1 -0
  32. package/src/admin/requirements/index.ts +1 -0
  33. package/src/admin/users/index.ts +1 -0
  34. package/src/admin/users/user-profile.admin.ts +1 -0
  35. package/src/backoffice/constants/index.ts +1 -0
  36. package/src/backoffice/errors/index.ts +1 -0
  37. package/src/backoffice/index.ts +5 -14
  38. package/src/backoffice/services/index.ts +7 -0
  39. package/src/backoffice/validations/index.ts +1 -0
  40. package/src/index.backup.ts +407 -0
  41. package/src/index.ts +5 -406
  42. package/src/services/PATIENTAUTH.MD +197 -0
  43. package/src/services/__tests__/auth/auth.setup.ts +2 -2
  44. package/src/services/__tests__/auth.service.test.ts +1 -1
  45. package/src/services/__tests__/user.service.test.ts +1 -1
  46. package/src/services/appointment/index.ts +1 -2
  47. package/src/services/{auth.service.ts → auth/auth.service.ts} +36 -22
  48. package/src/services/{auth.v2.service.ts → auth/auth.v2.service.ts} +17 -17
  49. package/src/services/auth/index.ts +2 -16
  50. package/src/services/calendar/calendar-refactored.service.ts +1 -1
  51. package/src/services/calendar/index.ts +5 -0
  52. package/src/services/clinic/index.ts +4 -0
  53. package/src/services/index.ts +12 -0
  54. package/src/services/media/index.ts +1 -0
  55. package/src/services/notifications/index.ts +1 -0
  56. package/src/services/patient/README.md +48 -0
  57. package/src/services/patient/To-Do.md +43 -0
  58. package/src/services/patient/index.ts +2 -0
  59. package/src/services/patient/patient.service.ts +289 -34
  60. package/src/services/patient/utils/index.ts +9 -0
  61. package/src/services/patient/utils/medical.utils.ts +114 -157
  62. package/src/services/patient/utils/profile.utils.ts +9 -0
  63. package/src/services/patient/utils/sensitive.utils.ts +79 -14
  64. package/src/services/patient/utils/token.utils.ts +211 -0
  65. package/src/services/practitioner/index.ts +1 -0
  66. package/src/services/procedure/index.ts +1 -0
  67. package/src/services/reviews/index.ts +1 -0
  68. package/src/services/user/index.ts +1 -0
  69. package/src/services/{user.service.ts → user/user.service.ts} +61 -12
  70. package/src/services/{user.v2.service.ts → user/user.v2.service.ts} +12 -12
  71. package/src/types/index.ts +42 -42
  72. package/src/types/patient/index.ts +33 -6
  73. package/src/types/patient/token.types.ts +61 -0
  74. package/src/types/user/index.ts +38 -0
  75. package/src/utils/index.ts +1 -0
  76. package/src/validations/calendar.schema.ts +6 -45
  77. package/src/validations/documentation-templates/index.ts +1 -0
  78. package/src/validations/documentation-templates.schema.ts +1 -1
  79. package/src/validations/index.ts +20 -0
  80. package/src/validations/patient/token.schema.ts +29 -0
  81. package/src/validations/patient.schema.ts +23 -6
  82. package/src/validations/profile-info.schema.ts +1 -1
  83. package/src/validations/schemas.ts +24 -24
@@ -45,64 +45,25 @@ import { AuthError } from "../../../errors/auth.errors";
45
45
  import { UserRole } from "../../../types";
46
46
  import { getMedicalInfoDocRef, getPatientDocRef } from "./docs.utils";
47
47
  import { getPractitionerProfileByUserRef } from "./practitioner.utils";
48
+ import { getClinicAdminByUserRef } from "../../clinic/utils/admin.utils";
48
49
 
49
50
  // Pomoćna funkcija za proveru i inicijalizaciju medical info dokumenta
50
51
  export const ensureMedicalInfoExists = async (
51
52
  db: Firestore,
52
53
  patientId: string,
53
- userRef: string
54
+ requesterId: string
54
55
  ): Promise<void> => {
55
- console.log(
56
- `[ensureMedicalInfoExists] Starting for patientId: ${patientId}, userRef: ${userRef}`
57
- );
58
-
59
- try {
60
- const medicalInfoRef = getMedicalInfoDocRef(db, patientId);
61
- console.log(
62
- `[ensureMedicalInfoExists] Got document reference: ${medicalInfoRef.path}`
63
- );
64
-
65
- const medicalInfoDoc = await getDoc(medicalInfoRef);
66
- console.log(
67
- `[ensureMedicalInfoExists] Document exists: ${medicalInfoDoc.exists()}`
68
- );
69
-
70
- if (!medicalInfoDoc.exists()) {
71
- const defaultData = {
72
- ...DEFAULT_MEDICAL_INFO,
73
- patientId,
74
- lastUpdated: serverTimestamp(),
75
- updatedBy: userRef,
76
- };
77
-
78
- console.log(
79
- `[ensureMedicalInfoExists] Creating new document with data:`,
80
- JSON.stringify(defaultData, (key, value) =>
81
- value &&
82
- typeof value === "object" &&
83
- value.constructor &&
84
- value.constructor.name === "Object"
85
- ? "[serverTimestamp]"
86
- : value
87
- )
88
- );
89
-
90
- await setDoc(medicalInfoRef, defaultData);
91
- console.log(`[ensureMedicalInfoExists] Document created successfully`);
92
-
93
- // Verify document was created
94
- const verifyDoc = await getDoc(medicalInfoRef);
95
- console.log(
96
- `[ensureMedicalInfoExists] Verification - document exists: ${verifyDoc.exists()}`
97
- );
98
- } else {
99
- console.log(
100
- `[ensureMedicalInfoExists] Document already exists, no action taken`
101
- );
102
- }
103
- } catch (error) {
104
- console.error(`[ensureMedicalInfoExists] Error:`, error);
105
- throw error;
56
+ const medicalInfoRef = getMedicalInfoDocRef(db, patientId);
57
+ const medicalInfoDoc = await getDoc(medicalInfoRef);
58
+
59
+ if (!medicalInfoDoc.exists()) {
60
+ const defaultData = {
61
+ ...DEFAULT_MEDICAL_INFO,
62
+ patientId,
63
+ lastUpdated: serverTimestamp(),
64
+ updatedBy: requesterId, // Koristimo ID onoga ko zahteva akciju
65
+ };
66
+ await setDoc(medicalInfoRef, defaultData);
106
67
  }
107
68
  };
108
69
 
@@ -110,48 +71,54 @@ export const ensureMedicalInfoExists = async (
110
71
  const checkMedicalAccessUtil = async (
111
72
  db: Firestore,
112
73
  patientId: string,
113
- userRef: string,
114
- userRoles: UserRole[]
74
+ requesterId: string,
75
+ requesterRoles: UserRole[]
115
76
  ): Promise<void> => {
116
- // Prvo pronađi profil pacijenta
117
77
  const patientDoc = await getDoc(getPatientDocRef(db, patientId));
118
78
  if (!patientDoc.exists()) {
119
79
  throw new Error("Patient profile not found");
120
80
  }
121
-
122
81
  const patientData = patientDoc.data() as PatientProfile;
123
82
 
124
- // Proveri da li je korisnik vlasnik profila
125
- if (patientData.userRef === userRef) return;
83
+ // 1. Provera da li je pacijent vlasnik profila (ako profil nije manuelan)
84
+ if (patientData.userRef && patientData.userRef === requesterId) {
85
+ return;
86
+ }
126
87
 
127
- // Ako je doktor, proveri da li je povezan sa pacijentom
128
- if (userRoles.includes(UserRole.PRACTITIONER)) {
88
+ // 2. Provera za doktora
89
+ if (requesterRoles.includes(UserRole.PRACTITIONER)) {
129
90
  const practitionerProfile = await getPractitionerProfileByUserRef(
130
91
  db,
131
- userRef
92
+ requesterId
132
93
  );
133
-
134
- if (!practitionerProfile) {
135
- throw new AuthError(
136
- "Practitioner profile not found",
137
- "AUTH/UNAUTHORIZED_ACCESS",
138
- 403
139
- );
94
+ if (
95
+ practitionerProfile &&
96
+ patientData.doctorIds?.includes(practitionerProfile.id)
97
+ ) {
98
+ return;
140
99
  }
100
+ }
141
101
 
142
- // Check if practitioner's ID is in the patient's doctorIds array
143
- const isAssignedDoctor = patientData.doctorIds?.includes(
144
- practitionerProfile.id
145
- );
146
-
147
- if (!isAssignedDoctor) {
148
- throw new AuthError(
149
- "Doktor nije povezan sa pacijentom",
150
- "AUTH/UNAUTHORIZED_ACCESS",
151
- 403
102
+ // 3. Provera za admina klinike
103
+ if (requesterRoles.includes(UserRole.CLINIC_ADMIN)) {
104
+ const adminProfile = await getClinicAdminByUserRef(db, requesterId);
105
+ if (adminProfile && adminProfile.clinicsManaged) {
106
+ // Check if any of the admin's managed clinics are in the patient's clinic list
107
+ const hasAccess = adminProfile.clinicsManaged.some((managedClinicId) =>
108
+ patientData.clinicIds?.includes(managedClinicId)
152
109
  );
110
+ if (hasAccess) {
111
+ return;
112
+ }
153
113
  }
154
114
  }
115
+
116
+ // Ako nijedan uslov nije zadovoljen
117
+ throw new AuthError(
118
+ "Unauthorized access to medical information.",
119
+ "AUTH/UNAUTHORIZED_ACCESS",
120
+ 403
121
+ );
155
122
  };
156
123
 
157
124
  // Osnovne metode za medicinske informacije
@@ -159,37 +126,35 @@ export const createMedicalInfoUtil = async (
159
126
  db: Firestore,
160
127
  patientId: string,
161
128
  data: CreatePatientMedicalInfoData,
162
- userRef: string,
163
- userRoles: UserRole[]
129
+ requesterId: string,
130
+ requesterRoles: UserRole[]
164
131
  ): Promise<void> => {
165
- await checkMedicalAccessUtil(db, patientId, userRef, userRoles);
166
-
132
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
167
133
  const validatedData = createPatientMedicalInfoSchema.parse(data);
168
134
 
169
135
  await setDoc(getMedicalInfoDocRef(db, patientId), {
170
136
  ...validatedData,
171
137
  patientId: patientId,
172
- lastUpdated: Timestamp.now(),
173
- updatedBy: userRef,
138
+ lastUpdated: serverTimestamp(),
139
+ updatedBy: requesterId,
174
140
  });
175
141
  };
176
142
 
177
143
  export const getMedicalInfoUtil = async (
178
144
  db: Firestore,
179
145
  patientId: string,
180
- userRef: string,
181
- userRoles: UserRole[]
146
+ requesterId: string,
147
+ requesterRoles: UserRole[]
182
148
  ): Promise<PatientMedicalInfo> => {
183
- await checkMedicalAccessUtil(db, patientId, userRef, userRoles);
184
-
149
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
185
150
  const docRef = getMedicalInfoDocRef(db, patientId);
186
151
  const snapshot = await getDoc(docRef);
187
-
188
152
  if (!snapshot.exists()) {
189
- throw new Error("Medicinske informacije nisu pronađene");
153
+ // Ako ne postoji, kreiraj prazan dokument
154
+ await ensureMedicalInfoExists(db, patientId, requesterId);
155
+ const newSnapshot = await getDoc(docRef);
156
+ return patientMedicalInfoSchema.parse(newSnapshot.data());
190
157
  }
191
-
192
- // The schema will transform raw timestamp objects to Timestamp instances
193
158
  return patientMedicalInfoSchema.parse(snapshot.data());
194
159
  };
195
160
 
@@ -198,15 +163,17 @@ export const updateVitalStatsUtil = async (
198
163
  db: Firestore,
199
164
  patientId: string,
200
165
  data: UpdateVitalStatsData,
201
- userRef: string
166
+ requesterId: string,
167
+ requesterRoles: UserRole[] // Dodajemo role za proveru pristupa
202
168
  ): Promise<void> => {
203
- await ensureMedicalInfoExists(db, patientId, userRef);
169
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
170
+ await ensureMedicalInfoExists(db, patientId, requesterId);
204
171
  const validatedData = updateVitalStatsSchema.parse(data);
205
172
 
206
173
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
207
174
  vitalStats: validatedData,
208
175
  lastUpdated: serverTimestamp(),
209
- updatedBy: userRef,
176
+ updatedBy: requesterId,
210
177
  });
211
178
  };
212
179
 
@@ -215,15 +182,16 @@ export const addAllergyUtil = async (
215
182
  db: Firestore,
216
183
  patientId: string,
217
184
  data: AddAllergyData,
218
- userRef: string
185
+ requesterId: string,
186
+ requesterRoles: UserRole[]
219
187
  ): Promise<void> => {
220
- await ensureMedicalInfoExists(db, patientId, userRef);
188
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
189
+ await ensureMedicalInfoExists(db, patientId, requesterId);
221
190
  const validatedData = addAllergySchema.parse(data);
222
-
223
191
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
224
192
  allergies: arrayUnion(validatedData),
225
193
  lastUpdated: serverTimestamp(),
226
- updatedBy: userRef,
194
+ updatedBy: requesterId,
227
195
  });
228
196
  };
229
197
 
@@ -231,31 +199,27 @@ export const updateAllergyUtil = async (
231
199
  db: Firestore,
232
200
  patientId: string,
233
201
  data: UpdateAllergyData,
234
- userRef: string
202
+ requesterId: string,
203
+ requesterRoles: UserRole[]
235
204
  ): Promise<void> => {
205
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
236
206
  const validatedData = updateAllergySchema.parse(data);
237
207
  const { allergyIndex, ...updateData } = validatedData;
238
-
239
208
  const docSnapshot = await getDoc(getMedicalInfoDocRef(db, patientId));
240
209
  if (!docSnapshot.exists()) throw new Error("Medical info not found");
241
-
242
- // Parse through schema to ensure proper Timestamp objects
243
210
  const medicalInfo = patientMedicalInfoSchema.parse(docSnapshot.data());
244
-
245
211
  if (allergyIndex >= medicalInfo.allergies.length) {
246
212
  throw new Error("Invalid allergy index");
247
213
  }
248
-
249
214
  const updatedAllergies = [...medicalInfo.allergies];
250
215
  updatedAllergies[allergyIndex] = {
251
216
  ...updatedAllergies[allergyIndex],
252
217
  ...updateData,
253
218
  };
254
-
255
219
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
256
220
  allergies: updatedAllergies,
257
221
  lastUpdated: serverTimestamp(),
258
- updatedBy: userRef,
222
+ updatedBy: requesterId,
259
223
  });
260
224
  };
261
225
 
@@ -263,24 +227,23 @@ export const removeAllergyUtil = async (
263
227
  db: Firestore,
264
228
  patientId: string,
265
229
  allergyIndex: number,
266
- userRef: string
230
+ requesterId: string,
231
+ requesterRoles: UserRole[]
267
232
  ): Promise<void> => {
233
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
268
234
  const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
269
235
  if (!doc.exists()) throw new Error("Medical info not found");
270
-
271
236
  const medicalInfo = doc.data() as PatientMedicalInfo;
272
237
  if (allergyIndex >= medicalInfo.allergies.length) {
273
238
  throw new Error("Invalid allergy index");
274
239
  }
275
-
276
240
  const updatedAllergies = medicalInfo.allergies.filter(
277
241
  (_, index) => index !== allergyIndex
278
242
  );
279
-
280
243
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
281
244
  allergies: updatedAllergies,
282
245
  lastUpdated: serverTimestamp(),
283
- updatedBy: userRef,
246
+ updatedBy: requesterId,
284
247
  });
285
248
  };
286
249
 
@@ -289,15 +252,16 @@ export const addBlockingConditionUtil = async (
289
252
  db: Firestore,
290
253
  patientId: string,
291
254
  data: AddBlockingConditionData,
292
- userRef: string
255
+ requesterId: string,
256
+ requesterRoles: UserRole[]
293
257
  ): Promise<void> => {
294
- await ensureMedicalInfoExists(db, patientId, userRef);
258
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
259
+ await ensureMedicalInfoExists(db, patientId, requesterId);
295
260
  const validatedData = addBlockingConditionSchema.parse(data);
296
-
297
261
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
298
262
  blockingConditions: arrayUnion(validatedData),
299
263
  lastUpdated: serverTimestamp(),
300
- updatedBy: userRef,
264
+ updatedBy: requesterId,
301
265
  });
302
266
  };
303
267
 
@@ -305,29 +269,27 @@ export const updateBlockingConditionUtil = async (
305
269
  db: Firestore,
306
270
  patientId: string,
307
271
  data: UpdateBlockingConditionData,
308
- userRef: string
272
+ requesterId: string,
273
+ requesterRoles: UserRole[]
309
274
  ): Promise<void> => {
275
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
310
276
  const validatedData = updateBlockingConditionSchema.parse(data);
311
277
  const { conditionIndex, ...updateData } = validatedData;
312
-
313
278
  const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
314
279
  if (!doc.exists()) throw new Error("Medical info not found");
315
-
316
280
  const medicalInfo = doc.data() as PatientMedicalInfo;
317
281
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
318
282
  throw new Error("Invalid blocking condition index");
319
283
  }
320
-
321
284
  const updatedConditions = [...medicalInfo.blockingConditions];
322
285
  updatedConditions[conditionIndex] = {
323
286
  ...updatedConditions[conditionIndex],
324
287
  ...updateData,
325
288
  };
326
-
327
289
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
328
290
  blockingConditions: updatedConditions,
329
291
  lastUpdated: serverTimestamp(),
330
- updatedBy: userRef,
292
+ updatedBy: requesterId,
331
293
  });
332
294
  };
333
295
 
@@ -335,24 +297,23 @@ export const removeBlockingConditionUtil = async (
335
297
  db: Firestore,
336
298
  patientId: string,
337
299
  conditionIndex: number,
338
- userRef: string
300
+ requesterId: string,
301
+ requesterRoles: UserRole[]
339
302
  ): Promise<void> => {
303
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
340
304
  const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
341
305
  if (!doc.exists()) throw new Error("Medical info not found");
342
-
343
306
  const medicalInfo = doc.data() as PatientMedicalInfo;
344
307
  if (conditionIndex >= medicalInfo.blockingConditions.length) {
345
308
  throw new Error("Invalid blocking condition index");
346
309
  }
347
-
348
310
  const updatedConditions = medicalInfo.blockingConditions.filter(
349
311
  (_, index) => index !== conditionIndex
350
312
  );
351
-
352
313
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
353
314
  blockingConditions: updatedConditions,
354
315
  lastUpdated: serverTimestamp(),
355
- updatedBy: userRef,
316
+ updatedBy: requesterId,
356
317
  });
357
318
  };
358
319
 
@@ -361,15 +322,16 @@ export const addContraindicationUtil = async (
361
322
  db: Firestore,
362
323
  patientId: string,
363
324
  data: AddContraindicationData,
364
- userRef: string
325
+ requesterId: string,
326
+ requesterRoles: UserRole[]
365
327
  ): Promise<void> => {
366
- await ensureMedicalInfoExists(db, patientId, userRef);
328
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
329
+ await ensureMedicalInfoExists(db, patientId, requesterId);
367
330
  const validatedData = addContraindicationSchema.parse(data);
368
-
369
331
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
370
332
  contraindications: arrayUnion(validatedData),
371
333
  lastUpdated: serverTimestamp(),
372
- updatedBy: userRef,
334
+ updatedBy: requesterId,
373
335
  });
374
336
  };
375
337
 
@@ -377,29 +339,27 @@ export const updateContraindicationUtil = async (
377
339
  db: Firestore,
378
340
  patientId: string,
379
341
  data: UpdateContraindicationData,
380
- userRef: string
342
+ requesterId: string,
343
+ requesterRoles: UserRole[]
381
344
  ): Promise<void> => {
345
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
382
346
  const validatedData = updateContraindicationSchema.parse(data);
383
347
  const { contraindicationIndex, ...updateData } = validatedData;
384
-
385
348
  const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
386
349
  if (!doc.exists()) throw new Error("Medical info not found");
387
-
388
350
  const medicalInfo = doc.data() as PatientMedicalInfo;
389
351
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
390
352
  throw new Error("Invalid contraindication index");
391
353
  }
392
-
393
354
  const updatedContraindications = [...medicalInfo.contraindications];
394
355
  updatedContraindications[contraindicationIndex] = {
395
356
  ...updatedContraindications[contraindicationIndex],
396
357
  ...updateData,
397
358
  };
398
-
399
359
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
400
360
  contraindications: updatedContraindications,
401
361
  lastUpdated: serverTimestamp(),
402
- updatedBy: userRef,
362
+ updatedBy: requesterId,
403
363
  });
404
364
  };
405
365
 
@@ -407,24 +367,23 @@ export const removeContraindicationUtil = async (
407
367
  db: Firestore,
408
368
  patientId: string,
409
369
  contraindicationIndex: number,
410
- userRef: string
370
+ requesterId: string,
371
+ requesterRoles: UserRole[]
411
372
  ): Promise<void> => {
373
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
412
374
  const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
413
375
  if (!doc.exists()) throw new Error("Medical info not found");
414
-
415
376
  const medicalInfo = doc.data() as PatientMedicalInfo;
416
377
  if (contraindicationIndex >= medicalInfo.contraindications.length) {
417
378
  throw new Error("Invalid contraindication index");
418
379
  }
419
-
420
380
  const updatedContraindications = medicalInfo.contraindications.filter(
421
381
  (_, index) => index !== contraindicationIndex
422
382
  );
423
-
424
383
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
425
384
  contraindications: updatedContraindications,
426
385
  lastUpdated: serverTimestamp(),
427
- updatedBy: userRef,
386
+ updatedBy: requesterId,
428
387
  });
429
388
  };
430
389
 
@@ -433,15 +392,16 @@ export const addMedicationUtil = async (
433
392
  db: Firestore,
434
393
  patientId: string,
435
394
  data: AddMedicationData,
436
- userRef: string
395
+ requesterId: string,
396
+ requesterRoles: UserRole[]
437
397
  ): Promise<void> => {
438
- await ensureMedicalInfoExists(db, patientId, userRef);
398
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
399
+ await ensureMedicalInfoExists(db, patientId, requesterId);
439
400
  const validatedData = addMedicationSchema.parse(data);
440
-
441
401
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
442
402
  currentMedications: arrayUnion(validatedData),
443
403
  lastUpdated: serverTimestamp(),
444
- updatedBy: userRef,
404
+ updatedBy: requesterId,
445
405
  });
446
406
  };
447
407
 
@@ -449,29 +409,27 @@ export const updateMedicationUtil = async (
449
409
  db: Firestore,
450
410
  patientId: string,
451
411
  data: UpdateMedicationData,
452
- userRef: string
412
+ requesterId: string,
413
+ requesterRoles: UserRole[]
453
414
  ): Promise<void> => {
415
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
454
416
  const validatedData = updateMedicationSchema.parse(data);
455
417
  const { medicationIndex, ...updateData } = validatedData;
456
-
457
418
  const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
458
419
  if (!doc.exists()) throw new Error("Medical info not found");
459
-
460
420
  const medicalInfo = doc.data() as PatientMedicalInfo;
461
421
  if (medicationIndex >= medicalInfo.currentMedications.length) {
462
422
  throw new Error("Invalid medication index");
463
423
  }
464
-
465
424
  const updatedMedications = [...medicalInfo.currentMedications];
466
425
  updatedMedications[medicationIndex] = {
467
426
  ...updatedMedications[medicationIndex],
468
427
  ...updateData,
469
428
  };
470
-
471
429
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
472
430
  currentMedications: updatedMedications,
473
431
  lastUpdated: serverTimestamp(),
474
- updatedBy: userRef,
432
+ updatedBy: requesterId,
475
433
  });
476
434
  };
477
435
 
@@ -479,23 +437,22 @@ export const removeMedicationUtil = async (
479
437
  db: Firestore,
480
438
  patientId: string,
481
439
  medicationIndex: number,
482
- userRef: string
440
+ requesterId: string,
441
+ requesterRoles: UserRole[]
483
442
  ): Promise<void> => {
443
+ await checkMedicalAccessUtil(db, patientId, requesterId, requesterRoles);
484
444
  const doc = await getDoc(getMedicalInfoDocRef(db, patientId));
485
445
  if (!doc.exists()) throw new Error("Medical info not found");
486
-
487
446
  const medicalInfo = doc.data() as PatientMedicalInfo;
488
447
  if (medicationIndex >= medicalInfo.currentMedications.length) {
489
448
  throw new Error("Invalid medication index");
490
449
  }
491
-
492
450
  const updatedMedications = medicalInfo.currentMedications.filter(
493
451
  (_, index) => index !== medicationIndex
494
452
  );
495
-
496
453
  await updateDoc(getMedicalInfoDocRef(db, patientId), {
497
454
  currentMedications: updatedMedications,
498
455
  lastUpdated: serverTimestamp(),
499
- updatedBy: userRef,
456
+ updatedBy: requesterId,
500
457
  });
501
458
  };
@@ -51,6 +51,14 @@ export const createPatientProfileUtil = async (
51
51
  try {
52
52
  console.log("[createPatientProfileUtil] Starting patient profile creation");
53
53
  const validatedData = createPatientProfileSchema.parse(data);
54
+
55
+ // This utility is for creating standard profiles, so userRef is required here.
56
+ if (!validatedData.userRef) {
57
+ throw new Error(
58
+ "userRef is required to create a standard patient profile."
59
+ );
60
+ }
61
+
54
62
  const patientId = generateId();
55
63
  console.log(`[createPatientProfileUtil] Generated patientId: ${patientId}`);
56
64
 
@@ -68,6 +76,7 @@ export const createPatientProfileUtil = async (
68
76
  },
69
77
  isActive: validatedData.isActive,
70
78
  isVerified: validatedData.isVerified,
79
+ isManual: validatedData.isManual,
71
80
  doctors: validatedData.doctors || [],
72
81
  clinics: validatedData.clinics || [],
73
82
  doctorIds: validatedData.doctors?.map((d) => d.userRef) || [],