@blackcode_sa/metaestetics-api 1.15.17-staging.6 → 1.15.17-staging.8
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.
- package/dist/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +91 -37
- package/dist/index.mjs +91 -37
- package/package.json +1 -1
- package/src/services/clinic/utils/clinic-group.utils.ts +13 -1
- package/src/services/resource/resource.service.ts +85 -16
package/dist/index.d.mts
CHANGED
|
@@ -10113,6 +10113,11 @@ declare class ResourceService extends BaseService {
|
|
|
10113
10113
|
* Soft deletes a resource by setting status to INACTIVE
|
|
10114
10114
|
*/
|
|
10115
10115
|
deleteResource(clinicBranchId: string, resourceId: string): Promise<void>;
|
|
10116
|
+
/**
|
|
10117
|
+
* Hard deletes a specific instance and all its calendar events.
|
|
10118
|
+
* Only works on INACTIVE instances with no future bookings.
|
|
10119
|
+
*/
|
|
10120
|
+
hardDeleteInstance(clinicBranchId: string, resourceId: string, instanceId: string): Promise<void>;
|
|
10116
10121
|
/**
|
|
10117
10122
|
* Gets all instances for a resource
|
|
10118
10123
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -10113,6 +10113,11 @@ declare class ResourceService extends BaseService {
|
|
|
10113
10113
|
* Soft deletes a resource by setting status to INACTIVE
|
|
10114
10114
|
*/
|
|
10115
10115
|
deleteResource(clinicBranchId: string, resourceId: string): Promise<void>;
|
|
10116
|
+
/**
|
|
10117
|
+
* Hard deletes a specific instance and all its calendar events.
|
|
10118
|
+
* Only works on INACTIVE instances with no future bookings.
|
|
10119
|
+
*/
|
|
10120
|
+
hardDeleteInstance(clinicBranchId: string, resourceId: string, instanceId: string): Promise<void>;
|
|
10116
10121
|
/**
|
|
10117
10122
|
* Gets all instances for a resource
|
|
10118
10123
|
*/
|
package/dist/index.js
CHANGED
|
@@ -10542,7 +10542,7 @@ var removeMedicationUtil = async (db, patientId, medicationIndex, requesterId, r
|
|
|
10542
10542
|
// src/services/patient/utils/profile.utils.ts
|
|
10543
10543
|
var import_firestore26 = require("firebase/firestore");
|
|
10544
10544
|
var import_zod13 = require("zod");
|
|
10545
|
-
var createPatientProfileUtil = async (db, data,
|
|
10545
|
+
var createPatientProfileUtil = async (db, data, generateId) => {
|
|
10546
10546
|
var _a, _b;
|
|
10547
10547
|
try {
|
|
10548
10548
|
console.log("[createPatientProfileUtil] Starting patient profile creation");
|
|
@@ -10552,7 +10552,7 @@ var createPatientProfileUtil = async (db, data, generateId2) => {
|
|
|
10552
10552
|
"userRef is required to create a standard patient profile."
|
|
10553
10553
|
);
|
|
10554
10554
|
}
|
|
10555
|
-
const patientId =
|
|
10555
|
+
const patientId = generateId();
|
|
10556
10556
|
console.log(`[createPatientProfileUtil] Generated patientId: ${patientId}`);
|
|
10557
10557
|
const patientData = {
|
|
10558
10558
|
id: patientId,
|
|
@@ -10886,7 +10886,7 @@ var createPatientTokenSchema = import_zod14.z.object({
|
|
|
10886
10886
|
});
|
|
10887
10887
|
|
|
10888
10888
|
// src/services/patient/utils/token.utils.ts
|
|
10889
|
-
var createPatientTokenUtil = async (db, data, createdBy,
|
|
10889
|
+
var createPatientTokenUtil = async (db, data, createdBy, generateId) => {
|
|
10890
10890
|
var _a;
|
|
10891
10891
|
const validatedData = createPatientTokenSchema.parse(data);
|
|
10892
10892
|
const patientRef = getPatientDocRef(db, validatedData.patientId);
|
|
@@ -10897,9 +10897,9 @@ var createPatientTokenUtil = async (db, data, createdBy, generateId2) => {
|
|
|
10897
10897
|
);
|
|
10898
10898
|
}
|
|
10899
10899
|
const expiration = validatedData.expiresAt || new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3);
|
|
10900
|
-
const tokenString =
|
|
10900
|
+
const tokenString = generateId().slice(0, 6).toUpperCase();
|
|
10901
10901
|
const token = {
|
|
10902
|
-
id:
|
|
10902
|
+
id: generateId(),
|
|
10903
10903
|
token: tokenString,
|
|
10904
10904
|
patientId: validatedData.patientId,
|
|
10905
10905
|
email: validatedData.email,
|
|
@@ -15637,14 +15637,12 @@ async function uploadMultiplePhotos(photos, entityType, entityId, photoType, app
|
|
|
15637
15637
|
}
|
|
15638
15638
|
|
|
15639
15639
|
// src/services/clinic/utils/clinic-group.utils.ts
|
|
15640
|
-
function
|
|
15641
|
-
const chars = "
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
{ length: 12 },
|
|
15640
|
+
function generateTokenCode() {
|
|
15641
|
+
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
15642
|
+
return Array.from(
|
|
15643
|
+
{ length: 6 },
|
|
15645
15644
|
() => chars.charAt(Math.floor(Math.random() * chars.length))
|
|
15646
15645
|
).join("");
|
|
15647
|
-
return `${randomPart}-${timestamp}`;
|
|
15648
15646
|
}
|
|
15649
15647
|
async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdminService, app) {
|
|
15650
15648
|
console.log("[CLINIC_GROUP] Starting clinic group creation", {
|
|
@@ -15936,7 +15934,7 @@ async function createAdminToken(db, groupId, creatorAdminId, app, data) {
|
|
|
15936
15934
|
const tokenRef = (0, import_firestore41.doc)(adminTokensRef(db, groupId));
|
|
15937
15935
|
const token = {
|
|
15938
15936
|
id: tokenRef.id,
|
|
15939
|
-
token:
|
|
15937
|
+
token: generateTokenCode(),
|
|
15940
15938
|
status: "active" /* ACTIVE */,
|
|
15941
15939
|
email,
|
|
15942
15940
|
clinicGroupId: groupId,
|
|
@@ -18682,8 +18680,8 @@ function getClinicSyncedCalendarDocRef(db, clinicId, syncedCalendarId) {
|
|
|
18682
18680
|
}
|
|
18683
18681
|
|
|
18684
18682
|
// src/services/calendar/utils/clinic.utils.ts
|
|
18685
|
-
async function createClinicCalendarEventUtil(db, clinicId, eventData,
|
|
18686
|
-
const eventId =
|
|
18683
|
+
async function createClinicCalendarEventUtil(db, clinicId, eventData, generateId) {
|
|
18684
|
+
const eventId = generateId();
|
|
18687
18685
|
const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
|
|
18688
18686
|
const newEvent = {
|
|
18689
18687
|
id: eventId,
|
|
@@ -18733,8 +18731,8 @@ async function checkAutoConfirmAppointmentsUtil(db, clinicId) {
|
|
|
18733
18731
|
|
|
18734
18732
|
// src/services/calendar/utils/patient.utils.ts
|
|
18735
18733
|
var import_firestore49 = require("firebase/firestore");
|
|
18736
|
-
async function createPatientCalendarEventUtil(db, patientId, eventData,
|
|
18737
|
-
const eventId =
|
|
18734
|
+
async function createPatientCalendarEventUtil(db, patientId, eventData, generateId) {
|
|
18735
|
+
const eventId = generateId();
|
|
18738
18736
|
const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
|
|
18739
18737
|
const newEvent = {
|
|
18740
18738
|
id: eventId,
|
|
@@ -18765,8 +18763,8 @@ async function updatePatientCalendarEventUtil(db, patientId, eventId, updateData
|
|
|
18765
18763
|
|
|
18766
18764
|
// src/services/calendar/utils/practitioner.utils.ts
|
|
18767
18765
|
var import_firestore50 = require("firebase/firestore");
|
|
18768
|
-
async function createPractitionerCalendarEventUtil(db, practitionerId, eventData,
|
|
18769
|
-
const eventId =
|
|
18766
|
+
async function createPractitionerCalendarEventUtil(db, practitionerId, eventData, generateId) {
|
|
18767
|
+
const eventId = generateId();
|
|
18770
18768
|
const eventRef = getPractitionerCalendarEventDocRef(
|
|
18771
18769
|
db,
|
|
18772
18770
|
practitionerId,
|
|
@@ -18804,8 +18802,8 @@ async function updatePractitionerCalendarEventUtil(db, practitionerId, eventId,
|
|
|
18804
18802
|
}
|
|
18805
18803
|
|
|
18806
18804
|
// src/services/calendar/utils/appointment.utils.ts
|
|
18807
|
-
async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData,
|
|
18808
|
-
const eventId =
|
|
18805
|
+
async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData, generateId) {
|
|
18806
|
+
const eventId = generateId();
|
|
18809
18807
|
const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
|
|
18810
18808
|
const initialStatus = autoConfirm ? "confirmed" /* CONFIRMED */ : "pending" /* PENDING */;
|
|
18811
18809
|
const appointmentData = {
|
|
@@ -18971,8 +18969,8 @@ async function searchCalendarEventsUtil(db, params) {
|
|
|
18971
18969
|
|
|
18972
18970
|
// src/services/calendar/utils/synced-calendar.utils.ts
|
|
18973
18971
|
var import_firestore52 = require("firebase/firestore");
|
|
18974
|
-
async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendarData,
|
|
18975
|
-
const calendarId =
|
|
18972
|
+
async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendarData, generateId) {
|
|
18973
|
+
const calendarId = generateId();
|
|
18976
18974
|
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
18977
18975
|
db,
|
|
18978
18976
|
practitionerId,
|
|
@@ -18991,8 +18989,8 @@ async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendar
|
|
|
18991
18989
|
updatedAt: import_firestore52.Timestamp.now()
|
|
18992
18990
|
};
|
|
18993
18991
|
}
|
|
18994
|
-
async function createPatientSyncedCalendarUtil(db, patientId, calendarData,
|
|
18995
|
-
const calendarId =
|
|
18992
|
+
async function createPatientSyncedCalendarUtil(db, patientId, calendarData, generateId) {
|
|
18993
|
+
const calendarId = generateId();
|
|
18996
18994
|
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
18997
18995
|
const newCalendar = {
|
|
18998
18996
|
id: calendarId,
|
|
@@ -19007,8 +19005,8 @@ async function createPatientSyncedCalendarUtil(db, patientId, calendarData, gene
|
|
|
19007
19005
|
updatedAt: import_firestore52.Timestamp.now()
|
|
19008
19006
|
};
|
|
19009
19007
|
}
|
|
19010
|
-
async function createClinicSyncedCalendarUtil(db, clinicId, calendarData,
|
|
19011
|
-
const calendarId =
|
|
19008
|
+
async function createClinicSyncedCalendarUtil(db, clinicId, calendarData, generateId) {
|
|
19009
|
+
const calendarId = generateId();
|
|
19012
19010
|
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
19013
19011
|
const newCalendar = {
|
|
19014
19012
|
id: calendarId,
|
|
@@ -25153,24 +25151,46 @@ var ResourceService = class extends BaseService {
|
|
|
25153
25151
|
if (data.status !== void 0) updateData.status = data.status;
|
|
25154
25152
|
if (data.quantity !== void 0 && data.quantity !== existing.quantity) {
|
|
25155
25153
|
if (data.quantity > existing.quantity) {
|
|
25154
|
+
const allInstances = await this.getResourceInstances(clinicBranchId, resourceId);
|
|
25155
|
+
const activeInstances = allInstances.filter((i) => i.status === "active" /* ACTIVE */);
|
|
25156
|
+
const inactiveInstances = allInstances.filter((i) => i.status === "inactive" /* INACTIVE */);
|
|
25157
|
+
const needed = data.quantity - activeInstances.length;
|
|
25156
25158
|
const batch = (0, import_firestore65.writeBatch)(this.db);
|
|
25157
25159
|
const now = (0, import_firestore65.serverTimestamp)();
|
|
25158
25160
|
const resourceName = data.name || existing.name;
|
|
25159
|
-
|
|
25161
|
+
let reactivated = 0;
|
|
25162
|
+
const sortedInactive = [...inactiveInstances].sort((a, b) => a.index - b.index);
|
|
25163
|
+
for (const instance of sortedInactive) {
|
|
25164
|
+
if (reactivated >= needed) break;
|
|
25160
25165
|
const instanceRef = (0, import_firestore65.doc)(
|
|
25161
|
-
this.getInstancesRef(clinicBranchId, resourceId)
|
|
25166
|
+
this.getInstancesRef(clinicBranchId, resourceId),
|
|
25167
|
+
instance.id
|
|
25162
25168
|
);
|
|
25163
|
-
|
|
25164
|
-
id: instanceRef.id,
|
|
25165
|
-
resourceId,
|
|
25166
|
-
clinicBranchId,
|
|
25167
|
-
label: `${resourceName} #${i}`,
|
|
25168
|
-
index: i,
|
|
25169
|
+
batch.update(instanceRef, {
|
|
25169
25170
|
status: "active" /* ACTIVE */,
|
|
25170
|
-
createdAt: now,
|
|
25171
25171
|
updatedAt: now
|
|
25172
|
-
};
|
|
25173
|
-
|
|
25172
|
+
});
|
|
25173
|
+
reactivated++;
|
|
25174
|
+
}
|
|
25175
|
+
const remaining = needed - reactivated;
|
|
25176
|
+
if (remaining > 0) {
|
|
25177
|
+
const maxIndex = allInstances.reduce((max, i) => Math.max(max, i.index), 0);
|
|
25178
|
+
for (let i = 1; i <= remaining; i++) {
|
|
25179
|
+
const instanceRef = (0, import_firestore65.doc)(
|
|
25180
|
+
this.getInstancesRef(clinicBranchId, resourceId)
|
|
25181
|
+
);
|
|
25182
|
+
const instanceData = {
|
|
25183
|
+
id: instanceRef.id,
|
|
25184
|
+
resourceId,
|
|
25185
|
+
clinicBranchId,
|
|
25186
|
+
label: `${resourceName} #${maxIndex + i}`,
|
|
25187
|
+
index: maxIndex + i,
|
|
25188
|
+
status: "active" /* ACTIVE */,
|
|
25189
|
+
createdAt: now,
|
|
25190
|
+
updatedAt: now
|
|
25191
|
+
};
|
|
25192
|
+
batch.set(instanceRef, instanceData);
|
|
25193
|
+
}
|
|
25174
25194
|
}
|
|
25175
25195
|
updateData.quantity = data.quantity;
|
|
25176
25196
|
const resourceDocRef2 = this.getResourceDocRef(
|
|
@@ -25237,6 +25257,40 @@ var ResourceService = class extends BaseService {
|
|
|
25237
25257
|
status: "inactive" /* INACTIVE */
|
|
25238
25258
|
});
|
|
25239
25259
|
}
|
|
25260
|
+
/**
|
|
25261
|
+
* Hard deletes a specific instance and all its calendar events.
|
|
25262
|
+
* Only works on INACTIVE instances with no future bookings.
|
|
25263
|
+
*/
|
|
25264
|
+
async hardDeleteInstance(clinicBranchId, resourceId, instanceId) {
|
|
25265
|
+
const instanceRef = (0, import_firestore65.doc)(
|
|
25266
|
+
this.getInstancesRef(clinicBranchId, resourceId),
|
|
25267
|
+
instanceId
|
|
25268
|
+
);
|
|
25269
|
+
const instanceSnap = await (0, import_firestore65.getDoc)(instanceRef);
|
|
25270
|
+
if (!instanceSnap.exists()) throw new Error("Instance not found");
|
|
25271
|
+
const instance = instanceSnap.data();
|
|
25272
|
+
if (instance.status === "active" /* ACTIVE */) {
|
|
25273
|
+
throw new Error("Cannot hard-delete an active instance. Deactivate it first.");
|
|
25274
|
+
}
|
|
25275
|
+
const hasFutureBookings = await this.instanceHasFutureBookings(
|
|
25276
|
+
clinicBranchId,
|
|
25277
|
+
resourceId,
|
|
25278
|
+
instanceId
|
|
25279
|
+
);
|
|
25280
|
+
if (hasFutureBookings) {
|
|
25281
|
+
throw new Error("Cannot delete instance with future bookings.");
|
|
25282
|
+
}
|
|
25283
|
+
const calendarRef = this.getInstanceCalendarRef(
|
|
25284
|
+
clinicBranchId,
|
|
25285
|
+
resourceId,
|
|
25286
|
+
instanceId
|
|
25287
|
+
);
|
|
25288
|
+
const calendarSnap = await (0, import_firestore65.getDocs)(calendarRef);
|
|
25289
|
+
const batch = (0, import_firestore65.writeBatch)(this.db);
|
|
25290
|
+
calendarSnap.docs.forEach((d) => batch.delete(d.ref));
|
|
25291
|
+
batch.delete(instanceRef);
|
|
25292
|
+
await batch.commit();
|
|
25293
|
+
}
|
|
25240
25294
|
/**
|
|
25241
25295
|
* Gets all instances for a resource
|
|
25242
25296
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -10509,7 +10509,7 @@ import {
|
|
|
10509
10509
|
doc as doc16
|
|
10510
10510
|
} from "firebase/firestore";
|
|
10511
10511
|
import { z as z13 } from "zod";
|
|
10512
|
-
var createPatientProfileUtil = async (db, data,
|
|
10512
|
+
var createPatientProfileUtil = async (db, data, generateId) => {
|
|
10513
10513
|
var _a, _b;
|
|
10514
10514
|
try {
|
|
10515
10515
|
console.log("[createPatientProfileUtil] Starting patient profile creation");
|
|
@@ -10519,7 +10519,7 @@ var createPatientProfileUtil = async (db, data, generateId2) => {
|
|
|
10519
10519
|
"userRef is required to create a standard patient profile."
|
|
10520
10520
|
);
|
|
10521
10521
|
}
|
|
10522
|
-
const patientId =
|
|
10522
|
+
const patientId = generateId();
|
|
10523
10523
|
console.log(`[createPatientProfileUtil] Generated patientId: ${patientId}`);
|
|
10524
10524
|
const patientData = {
|
|
10525
10525
|
id: patientId,
|
|
@@ -10864,7 +10864,7 @@ var createPatientTokenSchema = z14.object({
|
|
|
10864
10864
|
});
|
|
10865
10865
|
|
|
10866
10866
|
// src/services/patient/utils/token.utils.ts
|
|
10867
|
-
var createPatientTokenUtil = async (db, data, createdBy,
|
|
10867
|
+
var createPatientTokenUtil = async (db, data, createdBy, generateId) => {
|
|
10868
10868
|
var _a;
|
|
10869
10869
|
const validatedData = createPatientTokenSchema.parse(data);
|
|
10870
10870
|
const patientRef = getPatientDocRef(db, validatedData.patientId);
|
|
@@ -10875,9 +10875,9 @@ var createPatientTokenUtil = async (db, data, createdBy, generateId2) => {
|
|
|
10875
10875
|
);
|
|
10876
10876
|
}
|
|
10877
10877
|
const expiration = validatedData.expiresAt || new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3);
|
|
10878
|
-
const tokenString =
|
|
10878
|
+
const tokenString = generateId().slice(0, 6).toUpperCase();
|
|
10879
10879
|
const token = {
|
|
10880
|
-
id:
|
|
10880
|
+
id: generateId(),
|
|
10881
10881
|
token: tokenString,
|
|
10882
10882
|
patientId: validatedData.patientId,
|
|
10883
10883
|
email: validatedData.email,
|
|
@@ -15666,14 +15666,12 @@ async function uploadMultiplePhotos(photos, entityType, entityId, photoType, app
|
|
|
15666
15666
|
}
|
|
15667
15667
|
|
|
15668
15668
|
// src/services/clinic/utils/clinic-group.utils.ts
|
|
15669
|
-
function
|
|
15670
|
-
const chars = "
|
|
15671
|
-
|
|
15672
|
-
|
|
15673
|
-
{ length: 12 },
|
|
15669
|
+
function generateTokenCode() {
|
|
15670
|
+
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
|
|
15671
|
+
return Array.from(
|
|
15672
|
+
{ length: 6 },
|
|
15674
15673
|
() => chars.charAt(Math.floor(Math.random() * chars.length))
|
|
15675
15674
|
).join("");
|
|
15676
|
-
return `${randomPart}-${timestamp}`;
|
|
15677
15675
|
}
|
|
15678
15676
|
async function createClinicGroup(db, data, ownerId, isDefault = false, clinicAdminService, app) {
|
|
15679
15677
|
console.log("[CLINIC_GROUP] Starting clinic group creation", {
|
|
@@ -15965,7 +15963,7 @@ async function createAdminToken(db, groupId, creatorAdminId, app, data) {
|
|
|
15965
15963
|
const tokenRef = doc28(adminTokensRef(db, groupId));
|
|
15966
15964
|
const token = {
|
|
15967
15965
|
id: tokenRef.id,
|
|
15968
|
-
token:
|
|
15966
|
+
token: generateTokenCode(),
|
|
15969
15967
|
status: "active" /* ACTIVE */,
|
|
15970
15968
|
email,
|
|
15971
15969
|
clinicGroupId: groupId,
|
|
@@ -18773,8 +18771,8 @@ function getClinicSyncedCalendarDocRef(db, clinicId, syncedCalendarId) {
|
|
|
18773
18771
|
}
|
|
18774
18772
|
|
|
18775
18773
|
// src/services/calendar/utils/clinic.utils.ts
|
|
18776
|
-
async function createClinicCalendarEventUtil(db, clinicId, eventData,
|
|
18777
|
-
const eventId =
|
|
18774
|
+
async function createClinicCalendarEventUtil(db, clinicId, eventData, generateId) {
|
|
18775
|
+
const eventId = generateId();
|
|
18778
18776
|
const eventRef = getClinicCalendarEventDocRef(db, clinicId, eventId);
|
|
18779
18777
|
const newEvent = {
|
|
18780
18778
|
id: eventId,
|
|
@@ -18836,8 +18834,8 @@ import {
|
|
|
18836
18834
|
Timestamp as Timestamp25,
|
|
18837
18835
|
serverTimestamp as serverTimestamp26
|
|
18838
18836
|
} from "firebase/firestore";
|
|
18839
|
-
async function createPatientCalendarEventUtil(db, patientId, eventData,
|
|
18840
|
-
const eventId =
|
|
18837
|
+
async function createPatientCalendarEventUtil(db, patientId, eventData, generateId) {
|
|
18838
|
+
const eventId = generateId();
|
|
18841
18839
|
const eventRef = getPatientCalendarEventDocRef(db, patientId, eventId);
|
|
18842
18840
|
const newEvent = {
|
|
18843
18841
|
id: eventId,
|
|
@@ -18880,8 +18878,8 @@ import {
|
|
|
18880
18878
|
Timestamp as Timestamp26,
|
|
18881
18879
|
serverTimestamp as serverTimestamp27
|
|
18882
18880
|
} from "firebase/firestore";
|
|
18883
|
-
async function createPractitionerCalendarEventUtil(db, practitionerId, eventData,
|
|
18884
|
-
const eventId =
|
|
18881
|
+
async function createPractitionerCalendarEventUtil(db, practitionerId, eventData, generateId) {
|
|
18882
|
+
const eventId = generateId();
|
|
18885
18883
|
const eventRef = getPractitionerCalendarEventDocRef(
|
|
18886
18884
|
db,
|
|
18887
18885
|
practitionerId,
|
|
@@ -18919,8 +18917,8 @@ async function updatePractitionerCalendarEventUtil(db, practitionerId, eventId,
|
|
|
18919
18917
|
}
|
|
18920
18918
|
|
|
18921
18919
|
// src/services/calendar/utils/appointment.utils.ts
|
|
18922
|
-
async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData,
|
|
18923
|
-
const eventId =
|
|
18920
|
+
async function createAppointmentUtil(db, clinicId, practitionerId, patientId, eventData, generateId) {
|
|
18921
|
+
const eventId = generateId();
|
|
18924
18922
|
const autoConfirm = await checkAutoConfirmAppointmentsUtil(db, clinicId);
|
|
18925
18923
|
const initialStatus = autoConfirm ? "confirmed" /* CONFIRMED */ : "pending" /* PENDING */;
|
|
18926
18924
|
const appointmentData = {
|
|
@@ -19110,8 +19108,8 @@ import {
|
|
|
19110
19108
|
Timestamp as Timestamp28,
|
|
19111
19109
|
serverTimestamp as serverTimestamp29
|
|
19112
19110
|
} from "firebase/firestore";
|
|
19113
|
-
async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendarData,
|
|
19114
|
-
const calendarId =
|
|
19111
|
+
async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendarData, generateId) {
|
|
19112
|
+
const calendarId = generateId();
|
|
19115
19113
|
const calendarRef = getPractitionerSyncedCalendarDocRef(
|
|
19116
19114
|
db,
|
|
19117
19115
|
practitionerId,
|
|
@@ -19130,8 +19128,8 @@ async function createPractitionerSyncedCalendarUtil(db, practitionerId, calendar
|
|
|
19130
19128
|
updatedAt: Timestamp28.now()
|
|
19131
19129
|
};
|
|
19132
19130
|
}
|
|
19133
|
-
async function createPatientSyncedCalendarUtil(db, patientId, calendarData,
|
|
19134
|
-
const calendarId =
|
|
19131
|
+
async function createPatientSyncedCalendarUtil(db, patientId, calendarData, generateId) {
|
|
19132
|
+
const calendarId = generateId();
|
|
19135
19133
|
const calendarRef = getPatientSyncedCalendarDocRef(db, patientId, calendarId);
|
|
19136
19134
|
const newCalendar = {
|
|
19137
19135
|
id: calendarId,
|
|
@@ -19146,8 +19144,8 @@ async function createPatientSyncedCalendarUtil(db, patientId, calendarData, gene
|
|
|
19146
19144
|
updatedAt: Timestamp28.now()
|
|
19147
19145
|
};
|
|
19148
19146
|
}
|
|
19149
|
-
async function createClinicSyncedCalendarUtil(db, clinicId, calendarData,
|
|
19150
|
-
const calendarId =
|
|
19147
|
+
async function createClinicSyncedCalendarUtil(db, clinicId, calendarData, generateId) {
|
|
19148
|
+
const calendarId = generateId();
|
|
19151
19149
|
const calendarRef = getClinicSyncedCalendarDocRef(db, clinicId, calendarId);
|
|
19152
19150
|
const newCalendar = {
|
|
19153
19151
|
id: calendarId,
|
|
@@ -25386,24 +25384,46 @@ var ResourceService = class extends BaseService {
|
|
|
25386
25384
|
if (data.status !== void 0) updateData.status = data.status;
|
|
25387
25385
|
if (data.quantity !== void 0 && data.quantity !== existing.quantity) {
|
|
25388
25386
|
if (data.quantity > existing.quantity) {
|
|
25387
|
+
const allInstances = await this.getResourceInstances(clinicBranchId, resourceId);
|
|
25388
|
+
const activeInstances = allInstances.filter((i) => i.status === "active" /* ACTIVE */);
|
|
25389
|
+
const inactiveInstances = allInstances.filter((i) => i.status === "inactive" /* INACTIVE */);
|
|
25390
|
+
const needed = data.quantity - activeInstances.length;
|
|
25389
25391
|
const batch = writeBatch7(this.db);
|
|
25390
25392
|
const now = serverTimestamp36();
|
|
25391
25393
|
const resourceName = data.name || existing.name;
|
|
25392
|
-
|
|
25394
|
+
let reactivated = 0;
|
|
25395
|
+
const sortedInactive = [...inactiveInstances].sort((a, b) => a.index - b.index);
|
|
25396
|
+
for (const instance of sortedInactive) {
|
|
25397
|
+
if (reactivated >= needed) break;
|
|
25393
25398
|
const instanceRef = doc46(
|
|
25394
|
-
this.getInstancesRef(clinicBranchId, resourceId)
|
|
25399
|
+
this.getInstancesRef(clinicBranchId, resourceId),
|
|
25400
|
+
instance.id
|
|
25395
25401
|
);
|
|
25396
|
-
|
|
25397
|
-
id: instanceRef.id,
|
|
25398
|
-
resourceId,
|
|
25399
|
-
clinicBranchId,
|
|
25400
|
-
label: `${resourceName} #${i}`,
|
|
25401
|
-
index: i,
|
|
25402
|
+
batch.update(instanceRef, {
|
|
25402
25403
|
status: "active" /* ACTIVE */,
|
|
25403
|
-
createdAt: now,
|
|
25404
25404
|
updatedAt: now
|
|
25405
|
-
};
|
|
25406
|
-
|
|
25405
|
+
});
|
|
25406
|
+
reactivated++;
|
|
25407
|
+
}
|
|
25408
|
+
const remaining = needed - reactivated;
|
|
25409
|
+
if (remaining > 0) {
|
|
25410
|
+
const maxIndex = allInstances.reduce((max, i) => Math.max(max, i.index), 0);
|
|
25411
|
+
for (let i = 1; i <= remaining; i++) {
|
|
25412
|
+
const instanceRef = doc46(
|
|
25413
|
+
this.getInstancesRef(clinicBranchId, resourceId)
|
|
25414
|
+
);
|
|
25415
|
+
const instanceData = {
|
|
25416
|
+
id: instanceRef.id,
|
|
25417
|
+
resourceId,
|
|
25418
|
+
clinicBranchId,
|
|
25419
|
+
label: `${resourceName} #${maxIndex + i}`,
|
|
25420
|
+
index: maxIndex + i,
|
|
25421
|
+
status: "active" /* ACTIVE */,
|
|
25422
|
+
createdAt: now,
|
|
25423
|
+
updatedAt: now
|
|
25424
|
+
};
|
|
25425
|
+
batch.set(instanceRef, instanceData);
|
|
25426
|
+
}
|
|
25407
25427
|
}
|
|
25408
25428
|
updateData.quantity = data.quantity;
|
|
25409
25429
|
const resourceDocRef2 = this.getResourceDocRef(
|
|
@@ -25470,6 +25490,40 @@ var ResourceService = class extends BaseService {
|
|
|
25470
25490
|
status: "inactive" /* INACTIVE */
|
|
25471
25491
|
});
|
|
25472
25492
|
}
|
|
25493
|
+
/**
|
|
25494
|
+
* Hard deletes a specific instance and all its calendar events.
|
|
25495
|
+
* Only works on INACTIVE instances with no future bookings.
|
|
25496
|
+
*/
|
|
25497
|
+
async hardDeleteInstance(clinicBranchId, resourceId, instanceId) {
|
|
25498
|
+
const instanceRef = doc46(
|
|
25499
|
+
this.getInstancesRef(clinicBranchId, resourceId),
|
|
25500
|
+
instanceId
|
|
25501
|
+
);
|
|
25502
|
+
const instanceSnap = await getDoc47(instanceRef);
|
|
25503
|
+
if (!instanceSnap.exists()) throw new Error("Instance not found");
|
|
25504
|
+
const instance = instanceSnap.data();
|
|
25505
|
+
if (instance.status === "active" /* ACTIVE */) {
|
|
25506
|
+
throw new Error("Cannot hard-delete an active instance. Deactivate it first.");
|
|
25507
|
+
}
|
|
25508
|
+
const hasFutureBookings = await this.instanceHasFutureBookings(
|
|
25509
|
+
clinicBranchId,
|
|
25510
|
+
resourceId,
|
|
25511
|
+
instanceId
|
|
25512
|
+
);
|
|
25513
|
+
if (hasFutureBookings) {
|
|
25514
|
+
throw new Error("Cannot delete instance with future bookings.");
|
|
25515
|
+
}
|
|
25516
|
+
const calendarRef = this.getInstanceCalendarRef(
|
|
25517
|
+
clinicBranchId,
|
|
25518
|
+
resourceId,
|
|
25519
|
+
instanceId
|
|
25520
|
+
);
|
|
25521
|
+
const calendarSnap = await getDocs35(calendarRef);
|
|
25522
|
+
const batch = writeBatch7(this.db);
|
|
25523
|
+
calendarSnap.docs.forEach((d) => batch.delete(d.ref));
|
|
25524
|
+
batch.delete(instanceRef);
|
|
25525
|
+
await batch.commit();
|
|
25526
|
+
}
|
|
25473
25527
|
/**
|
|
25474
25528
|
* Gets all instances for a resource
|
|
25475
25529
|
*/
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blackcode_sa/metaestetics-api",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.15.17-staging.
|
|
4
|
+
"version": "1.15.17-staging.8",
|
|
5
5
|
"description": "Firebase authentication service with anonymous upgrade support",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.mjs",
|
|
@@ -50,6 +50,18 @@ function generateId(): string {
|
|
|
50
50
|
return `${randomPart}-${timestamp}`;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Generates a short 6-character uppercase alphanumeric token code.
|
|
55
|
+
* Used for admin registration tokens — easy to read, type, and share.
|
|
56
|
+
* 36^6 = ~2.1 billion combinations, collision risk negligible for active tokens.
|
|
57
|
+
*/
|
|
58
|
+
function generateTokenCode(): string {
|
|
59
|
+
const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // no I/1/O/0 to avoid confusion
|
|
60
|
+
return Array.from({ length: 6 }, () =>
|
|
61
|
+
chars.charAt(Math.floor(Math.random() * chars.length))
|
|
62
|
+
).join("");
|
|
63
|
+
}
|
|
64
|
+
|
|
53
65
|
/**
|
|
54
66
|
* Creates a new clinic group
|
|
55
67
|
* @param db - Firestore database instance
|
|
@@ -502,7 +514,7 @@ export async function createAdminToken(
|
|
|
502
514
|
const tokenRef = doc(adminTokensRef(db, groupId));
|
|
503
515
|
const token: AdminToken = {
|
|
504
516
|
id: tokenRef.id,
|
|
505
|
-
token:
|
|
517
|
+
token: generateTokenCode(),
|
|
506
518
|
status: AdminTokenStatus.ACTIVE,
|
|
507
519
|
email,
|
|
508
520
|
clinicGroupId: groupId,
|
|
@@ -228,34 +228,61 @@ export class ResourceService extends BaseService {
|
|
|
228
228
|
// Handle quantity change
|
|
229
229
|
if (data.quantity !== undefined && data.quantity !== existing.quantity) {
|
|
230
230
|
if (data.quantity > existing.quantity) {
|
|
231
|
-
//
|
|
231
|
+
// Increasing quantity — first reactivate inactive instances, then create new ones if needed
|
|
232
|
+
const allInstances = await this.getResourceInstances(clinicBranchId, resourceId);
|
|
233
|
+
const activeInstances = allInstances.filter(i => i.status === ResourceStatus.ACTIVE);
|
|
234
|
+
const inactiveInstances = allInstances.filter(i => i.status === ResourceStatus.INACTIVE);
|
|
235
|
+
|
|
236
|
+
const needed = data.quantity - activeInstances.length;
|
|
232
237
|
const batch = writeBatch(this.db);
|
|
233
238
|
const now = serverTimestamp();
|
|
234
239
|
const resourceName = data.name || existing.name;
|
|
235
240
|
|
|
236
|
-
|
|
241
|
+
let reactivated = 0;
|
|
242
|
+
|
|
243
|
+
// Step 1: Reactivate inactive instances (sorted by index, lowest first)
|
|
244
|
+
const sortedInactive = [...inactiveInstances].sort((a, b) => a.index - b.index);
|
|
245
|
+
for (const instance of sortedInactive) {
|
|
246
|
+
if (reactivated >= needed) break;
|
|
237
247
|
const instanceRef = doc(
|
|
238
|
-
this.getInstancesRef(clinicBranchId, resourceId)
|
|
248
|
+
this.getInstancesRef(clinicBranchId, resourceId),
|
|
249
|
+
instance.id
|
|
239
250
|
);
|
|
240
|
-
|
|
241
|
-
ResourceInstance,
|
|
242
|
-
"createdAt" | "updatedAt"
|
|
243
|
-
> & { createdAt: any; updatedAt: any } = {
|
|
244
|
-
id: instanceRef.id,
|
|
245
|
-
resourceId,
|
|
246
|
-
clinicBranchId,
|
|
247
|
-
label: `${resourceName} #${i}`,
|
|
248
|
-
index: i,
|
|
251
|
+
batch.update(instanceRef, {
|
|
249
252
|
status: ResourceStatus.ACTIVE,
|
|
250
|
-
createdAt: now,
|
|
251
253
|
updatedAt: now,
|
|
252
|
-
};
|
|
253
|
-
|
|
254
|
+
});
|
|
255
|
+
reactivated++;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Step 2: Create new instances for any remaining needed
|
|
259
|
+
const remaining = needed - reactivated;
|
|
260
|
+
if (remaining > 0) {
|
|
261
|
+
// Find the highest existing index to continue numbering
|
|
262
|
+
const maxIndex = allInstances.reduce((max, i) => Math.max(max, i.index), 0);
|
|
263
|
+
for (let i = 1; i <= remaining; i++) {
|
|
264
|
+
const instanceRef = doc(
|
|
265
|
+
this.getInstancesRef(clinicBranchId, resourceId)
|
|
266
|
+
);
|
|
267
|
+
const instanceData: Omit<
|
|
268
|
+
ResourceInstance,
|
|
269
|
+
"createdAt" | "updatedAt"
|
|
270
|
+
> & { createdAt: any; updatedAt: any } = {
|
|
271
|
+
id: instanceRef.id,
|
|
272
|
+
resourceId,
|
|
273
|
+
clinicBranchId,
|
|
274
|
+
label: `${resourceName} #${maxIndex + i}`,
|
|
275
|
+
index: maxIndex + i,
|
|
276
|
+
status: ResourceStatus.ACTIVE,
|
|
277
|
+
createdAt: now,
|
|
278
|
+
updatedAt: now,
|
|
279
|
+
};
|
|
280
|
+
batch.set(instanceRef, instanceData);
|
|
281
|
+
}
|
|
254
282
|
}
|
|
255
283
|
|
|
256
284
|
updateData.quantity = data.quantity;
|
|
257
285
|
|
|
258
|
-
// Update resource doc in the batch too
|
|
259
286
|
const resourceDocRef = this.getResourceDocRef(
|
|
260
287
|
clinicBranchId,
|
|
261
288
|
resourceId
|
|
@@ -337,6 +364,48 @@ export class ResourceService extends BaseService {
|
|
|
337
364
|
});
|
|
338
365
|
}
|
|
339
366
|
|
|
367
|
+
/**
|
|
368
|
+
* Hard deletes a specific instance and all its calendar events.
|
|
369
|
+
* Only works on INACTIVE instances with no future bookings.
|
|
370
|
+
*/
|
|
371
|
+
async hardDeleteInstance(
|
|
372
|
+
clinicBranchId: string,
|
|
373
|
+
resourceId: string,
|
|
374
|
+
instanceId: string
|
|
375
|
+
): Promise<void> {
|
|
376
|
+
// Verify instance exists and is inactive
|
|
377
|
+
const instanceRef = doc(
|
|
378
|
+
this.getInstancesRef(clinicBranchId, resourceId),
|
|
379
|
+
instanceId
|
|
380
|
+
);
|
|
381
|
+
const instanceSnap = await getDoc(instanceRef);
|
|
382
|
+
if (!instanceSnap.exists()) throw new Error("Instance not found");
|
|
383
|
+
|
|
384
|
+
const instance = instanceSnap.data() as ResourceInstance;
|
|
385
|
+
if (instance.status === ResourceStatus.ACTIVE) {
|
|
386
|
+
throw new Error("Cannot hard-delete an active instance. Deactivate it first.");
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Check for future bookings
|
|
390
|
+
const hasFutureBookings = await this.instanceHasFutureBookings(
|
|
391
|
+
clinicBranchId, resourceId, instanceId
|
|
392
|
+
);
|
|
393
|
+
if (hasFutureBookings) {
|
|
394
|
+
throw new Error("Cannot delete instance with future bookings.");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Delete all calendar events for this instance
|
|
398
|
+
const calendarRef = this.getInstanceCalendarRef(
|
|
399
|
+
clinicBranchId, resourceId, instanceId
|
|
400
|
+
);
|
|
401
|
+
const calendarSnap = await getDocs(calendarRef);
|
|
402
|
+
|
|
403
|
+
const batch = writeBatch(this.db);
|
|
404
|
+
calendarSnap.docs.forEach(d => batch.delete(d.ref));
|
|
405
|
+
batch.delete(instanceRef);
|
|
406
|
+
await batch.commit();
|
|
407
|
+
}
|
|
408
|
+
|
|
340
409
|
/**
|
|
341
410
|
* Gets all instances for a resource
|
|
342
411
|
*/
|