@blackcode_sa/metaestetics-api 1.15.17-staging.7 → 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 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
@@ -25151,24 +25151,46 @@ var ResourceService = class extends BaseService {
25151
25151
  if (data.status !== void 0) updateData.status = data.status;
25152
25152
  if (data.quantity !== void 0 && data.quantity !== existing.quantity) {
25153
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;
25154
25158
  const batch = (0, import_firestore65.writeBatch)(this.db);
25155
25159
  const now = (0, import_firestore65.serverTimestamp)();
25156
25160
  const resourceName = data.name || existing.name;
25157
- for (let i = existing.quantity + 1; i <= data.quantity; i++) {
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;
25158
25165
  const instanceRef = (0, import_firestore65.doc)(
25159
- this.getInstancesRef(clinicBranchId, resourceId)
25166
+ this.getInstancesRef(clinicBranchId, resourceId),
25167
+ instance.id
25160
25168
  );
25161
- const instanceData = {
25162
- id: instanceRef.id,
25163
- resourceId,
25164
- clinicBranchId,
25165
- label: `${resourceName} #${i}`,
25166
- index: i,
25169
+ batch.update(instanceRef, {
25167
25170
  status: "active" /* ACTIVE */,
25168
- createdAt: now,
25169
25171
  updatedAt: now
25170
- };
25171
- batch.set(instanceRef, instanceData);
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
+ }
25172
25194
  }
25173
25195
  updateData.quantity = data.quantity;
25174
25196
  const resourceDocRef2 = this.getResourceDocRef(
@@ -25235,6 +25257,40 @@ var ResourceService = class extends BaseService {
25235
25257
  status: "inactive" /* INACTIVE */
25236
25258
  });
25237
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
+ }
25238
25294
  /**
25239
25295
  * Gets all instances for a resource
25240
25296
  */
package/dist/index.mjs CHANGED
@@ -25384,24 +25384,46 @@ var ResourceService = class extends BaseService {
25384
25384
  if (data.status !== void 0) updateData.status = data.status;
25385
25385
  if (data.quantity !== void 0 && data.quantity !== existing.quantity) {
25386
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;
25387
25391
  const batch = writeBatch7(this.db);
25388
25392
  const now = serverTimestamp36();
25389
25393
  const resourceName = data.name || existing.name;
25390
- for (let i = existing.quantity + 1; i <= data.quantity; i++) {
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;
25391
25398
  const instanceRef = doc46(
25392
- this.getInstancesRef(clinicBranchId, resourceId)
25399
+ this.getInstancesRef(clinicBranchId, resourceId),
25400
+ instance.id
25393
25401
  );
25394
- const instanceData = {
25395
- id: instanceRef.id,
25396
- resourceId,
25397
- clinicBranchId,
25398
- label: `${resourceName} #${i}`,
25399
- index: i,
25402
+ batch.update(instanceRef, {
25400
25403
  status: "active" /* ACTIVE */,
25401
- createdAt: now,
25402
25404
  updatedAt: now
25403
- };
25404
- batch.set(instanceRef, instanceData);
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
+ }
25405
25427
  }
25406
25428
  updateData.quantity = data.quantity;
25407
25429
  const resourceDocRef2 = this.getResourceDocRef(
@@ -25468,6 +25490,40 @@ var ResourceService = class extends BaseService {
25468
25490
  status: "inactive" /* INACTIVE */
25469
25491
  });
25470
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
+ }
25471
25527
  /**
25472
25528
  * Gets all instances for a resource
25473
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.7",
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",
@@ -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
- // Add new instances
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
- for (let i = existing.quantity + 1; i <= data.quantity; i++) {
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
- const instanceData: Omit<
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
- batch.set(instanceRef, instanceData);
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
  */