@blackcode_sa/metaestetics-api 1.15.10 → 1.15.14

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.js CHANGED
@@ -155,12 +155,9 @@ __export(index_exports, {
155
155
  USER_FORMS_SUBCOLLECTION: () => USER_FORMS_SUBCOLLECTION,
156
156
  UserRole: () => UserRole,
157
157
  UserService: () => UserService,
158
- enforceAppointmentLimit: () => enforceAppointmentLimit,
159
158
  enforceBranchLimit: () => enforceBranchLimit,
160
- enforceMessageLimit: () => enforceMessageLimit,
161
159
  enforceProcedureLimit: () => enforceProcedureLimit,
162
160
  enforceProviderLimit: () => enforceProviderLimit,
163
- enforceStaffLimit: () => enforceStaffLimit,
164
161
  getEffectiveTier: () => getEffectiveTier,
165
162
  getFirebaseApp: () => getFirebaseApp,
166
163
  getFirebaseAuth: () => getFirebaseAuth,
@@ -12737,11 +12734,8 @@ var TIER_CONFIG = {
12737
12734
  tier: "free",
12738
12735
  name: "Free",
12739
12736
  limits: {
12740
- maxProviders: 1,
12741
- maxProcedures: 3,
12742
- maxAppointmentsPerMonth: 3,
12743
- maxMessagesPerMonth: 10,
12744
- maxStaff: 1,
12737
+ maxProvidersPerBranch: 1,
12738
+ maxProceduresPerProvider: 3,
12745
12739
  maxBranches: 1
12746
12740
  }
12747
12741
  },
@@ -12749,11 +12743,8 @@ var TIER_CONFIG = {
12749
12743
  tier: "connect",
12750
12744
  name: "Connect",
12751
12745
  limits: {
12752
- maxProviders: -1,
12753
- maxProcedures: 15,
12754
- maxAppointmentsPerMonth: 100,
12755
- maxMessagesPerMonth: -1,
12756
- maxStaff: 5,
12746
+ maxProvidersPerBranch: 3,
12747
+ maxProceduresPerProvider: 10,
12757
12748
  maxBranches: 1
12758
12749
  }
12759
12750
  },
@@ -12761,12 +12752,9 @@ var TIER_CONFIG = {
12761
12752
  tier: "pro",
12762
12753
  name: "Pro",
12763
12754
  limits: {
12764
- maxProviders: -1,
12765
- maxProcedures: -1,
12766
- maxAppointmentsPerMonth: -1,
12767
- maxMessagesPerMonth: -1,
12768
- maxStaff: -1,
12769
- maxBranches: -1
12755
+ maxProvidersPerBranch: 10,
12756
+ maxProceduresPerProvider: 20,
12757
+ maxBranches: 3
12770
12758
  }
12771
12759
  }
12772
12760
  };
@@ -12897,8 +12885,10 @@ function resolveEffectiveTier(subscriptionModel) {
12897
12885
  var TierLimitError = class extends Error {
12898
12886
  constructor(resource, currentTier, currentCount, maxAllowed) {
12899
12887
  const tierLabel = currentTier.charAt(0).toUpperCase() + currentTier.slice(1);
12888
+ const canBuyAddons = currentTier === "connect" || currentTier === "pro";
12889
+ const actionHint = canBuyAddons ? "Please upgrade your plan or purchase an add-on." : "Please upgrade to Connect or Pro to add more.";
12900
12890
  super(
12901
- `Your ${tierLabel} plan allows a maximum of ${maxAllowed} ${resource}. You currently have ${currentCount}. Please upgrade your plan to add more.`
12891
+ `Your ${tierLabel} plan allows a maximum of ${maxAllowed} ${resource}. You currently have ${currentCount}. ${actionHint}`
12902
12892
  );
12903
12893
  this.code = "TIER_LIMIT_EXCEEDED";
12904
12894
  this.name = "TierLimitError";
@@ -12908,58 +12898,50 @@ var TierLimitError = class extends Error {
12908
12898
  this.maxAllowed = maxAllowed;
12909
12899
  }
12910
12900
  };
12911
- async function getEffectiveTier(db, clinicGroupId) {
12901
+ async function getClinicGroupTierData(db, clinicGroupId) {
12912
12902
  const groupRef = (0, import_firestore35.doc)(db, CLINIC_GROUPS_COLLECTION, clinicGroupId);
12913
12903
  const groupSnap = await (0, import_firestore35.getDoc)(groupRef);
12914
12904
  if (!groupSnap.exists()) {
12915
12905
  throw new Error(`Clinic group ${clinicGroupId} not found`);
12916
12906
  }
12917
- const subscriptionModel = groupSnap.data().subscriptionModel || "no_subscription";
12918
- return resolveEffectiveTier(subscriptionModel);
12907
+ const data = groupSnap.data();
12908
+ const subscriptionModel = data.subscriptionModel || "no_subscription";
12909
+ return {
12910
+ tier: resolveEffectiveTier(subscriptionModel),
12911
+ billing: data.billing
12912
+ };
12919
12913
  }
12920
- async function countProvidersInGroup(db, clinicGroupId) {
12921
- const clinicsQuery = (0, import_firestore35.query)(
12922
- (0, import_firestore35.collection)(db, CLINICS_COLLECTION),
12923
- (0, import_firestore35.where)("clinicGroupId", "==", clinicGroupId),
12924
- (0, import_firestore35.where)("isActive", "==", true)
12925
- );
12926
- const clinicsSnap = await (0, import_firestore35.getDocs)(clinicsQuery);
12927
- const clinicIds = clinicsSnap.docs.map((d) => d.id);
12928
- if (clinicIds.length === 0) return 0;
12914
+ async function getEffectiveTier(db, clinicGroupId) {
12915
+ const { tier } = await getClinicGroupTierData(db, clinicGroupId);
12916
+ return tier;
12917
+ }
12918
+ async function countProvidersInBranch(db, branchId) {
12929
12919
  const practitionersQuery = (0, import_firestore35.query)(
12930
12920
  (0, import_firestore35.collection)(db, PRACTITIONERS_COLLECTION),
12931
12921
  (0, import_firestore35.where)("isActive", "==", true)
12932
12922
  );
12933
12923
  const practitionersSnap = await (0, import_firestore35.getDocs)(practitionersQuery);
12934
- const clinicIdSet = new Set(clinicIds);
12935
- const uniqueProviders = practitionersSnap.docs.filter((d) => {
12936
- const data = d.data();
12937
- const clinics = data.clinics || [];
12938
- return clinics.some((c) => clinicIdSet.has(c));
12939
- });
12940
- return uniqueProviders.length;
12924
+ return practitionersSnap.docs.filter((d) => {
12925
+ const clinics = d.data().clinics || [];
12926
+ return clinics.includes(branchId);
12927
+ }).length;
12941
12928
  }
12942
- async function countProceduresInGroup(db, clinicGroupId) {
12943
- const clinicsQuery = (0, import_firestore35.query)(
12944
- (0, import_firestore35.collection)(db, CLINICS_COLLECTION),
12945
- (0, import_firestore35.where)("clinicGroupId", "==", clinicGroupId),
12929
+ async function countProceduresForProvider(db, branchId, providerId) {
12930
+ const proceduresQuery = (0, import_firestore35.query)(
12931
+ (0, import_firestore35.collection)(db, PROCEDURES_COLLECTION),
12932
+ (0, import_firestore35.where)("clinicBranchId", "==", branchId),
12933
+ (0, import_firestore35.where)("practitionerId", "==", providerId),
12946
12934
  (0, import_firestore35.where)("isActive", "==", true)
12947
12935
  );
12948
- const clinicsSnap = await (0, import_firestore35.getDocs)(clinicsQuery);
12949
- const clinicIds = clinicsSnap.docs.map((d) => d.id);
12950
- if (clinicIds.length === 0) return 0;
12951
- let totalProcedures = 0;
12952
- for (let i = 0; i < clinicIds.length; i += 30) {
12953
- const batch = clinicIds.slice(i, i + 30);
12954
- const proceduresQuery = (0, import_firestore35.query)(
12955
- (0, import_firestore35.collection)(db, PROCEDURES_COLLECTION),
12956
- (0, import_firestore35.where)("clinicBranchId", "in", batch),
12957
- (0, import_firestore35.where)("isActive", "==", true)
12958
- );
12959
- const proceduresSnap = await (0, import_firestore35.getDocs)(proceduresQuery);
12960
- totalProcedures += proceduresSnap.size;
12961
- }
12962
- return totalProcedures;
12936
+ const proceduresSnap = await (0, import_firestore35.getDocs)(proceduresQuery);
12937
+ const uniqueTechnologyIds = /* @__PURE__ */ new Set();
12938
+ proceduresSnap.docs.forEach((d) => {
12939
+ const technologyId = d.data().technologyId;
12940
+ if (technologyId) {
12941
+ uniqueTechnologyIds.add(technologyId);
12942
+ }
12943
+ });
12944
+ return uniqueTechnologyIds.size;
12963
12945
  }
12964
12946
  async function countBranchesInGroup(db, clinicGroupId) {
12965
12947
  const clinicsQuery = (0, import_firestore35.query)(
@@ -12970,92 +12952,47 @@ async function countBranchesInGroup(db, clinicGroupId) {
12970
12952
  const clinicsSnap = await (0, import_firestore35.getDocs)(clinicsQuery);
12971
12953
  return clinicsSnap.size;
12972
12954
  }
12973
- async function enforceProviderLimit(db, clinicGroupId) {
12974
- const tier = await getEffectiveTier(db, clinicGroupId);
12955
+ async function enforceProviderLimit(db, clinicGroupId, branchId) {
12956
+ var _a;
12957
+ const { tier, billing } = await getClinicGroupTierData(db, clinicGroupId);
12975
12958
  const config = TIER_CONFIG[tier];
12976
12959
  if (!config) return;
12977
- const max = config.limits.maxProviders;
12978
- if (max === -1) return;
12979
- const currentCount = await countProvidersInGroup(db, clinicGroupId);
12980
- if (currentCount + 1 > max) {
12981
- throw new TierLimitError("providers", tier, currentCount, max);
12960
+ const baseMax = config.limits.maxProvidersPerBranch;
12961
+ if (baseMax === -1) return;
12962
+ const addOns = (billing == null ? void 0 : billing.addOns) || {};
12963
+ const extraProviders = ((_a = addOns[branchId]) == null ? void 0 : _a.extraProviders) || 0;
12964
+ const effectiveMax = baseMax + extraProviders;
12965
+ const currentCount = await countProvidersInBranch(db, branchId);
12966
+ if (currentCount + 1 > effectiveMax) {
12967
+ throw new TierLimitError("providers in this branch", tier, currentCount, effectiveMax);
12982
12968
  }
12983
12969
  }
12984
- async function enforceProcedureLimit(db, clinicGroupId, count = 1) {
12985
- const tier = await getEffectiveTier(db, clinicGroupId);
12970
+ async function enforceProcedureLimit(db, clinicGroupId, branchId, providerId, count = 1) {
12971
+ var _a;
12972
+ const { tier, billing } = await getClinicGroupTierData(db, clinicGroupId);
12986
12973
  const config = TIER_CONFIG[tier];
12987
12974
  if (!config) return;
12988
- const max = config.limits.maxProcedures;
12989
- if (max === -1) return;
12990
- const currentCount = await countProceduresInGroup(db, clinicGroupId);
12991
- if (currentCount + count > max) {
12992
- throw new TierLimitError("procedures", tier, currentCount, max);
12975
+ const baseMax = config.limits.maxProceduresPerProvider;
12976
+ if (baseMax === -1) return;
12977
+ const addOns = (billing == null ? void 0 : billing.addOns) || {};
12978
+ const procedureBlocks = ((_a = addOns[branchId]) == null ? void 0 : _a.procedureBlocks) || 0;
12979
+ const effectiveMax = baseMax + procedureBlocks * 10;
12980
+ const currentCount = await countProceduresForProvider(db, branchId, providerId);
12981
+ if (currentCount + count > effectiveMax) {
12982
+ throw new TierLimitError("procedures for this provider", tier, currentCount, effectiveMax);
12993
12983
  }
12994
12984
  }
12995
12985
  async function enforceBranchLimit(db, clinicGroupId) {
12996
- const tier = await getEffectiveTier(db, clinicGroupId);
12986
+ const { tier, billing } = await getClinicGroupTierData(db, clinicGroupId);
12997
12987
  const config = TIER_CONFIG[tier];
12998
12988
  if (!config) return;
12999
- const max = config.limits.maxBranches;
13000
- if (max === -1) return;
12989
+ const baseMax = config.limits.maxBranches;
12990
+ if (baseMax === -1) return;
12991
+ const branchAddonCount = (billing == null ? void 0 : billing.branchAddonCount) || 0;
12992
+ const effectiveMax = baseMax + branchAddonCount;
13001
12993
  const currentCount = await countBranchesInGroup(db, clinicGroupId);
13002
- if (currentCount + 1 > max) {
13003
- throw new TierLimitError("clinic branches", tier, currentCount, max);
13004
- }
13005
- }
13006
- async function enforceStaffLimit(db, clinicGroupId) {
13007
- const tier = await getEffectiveTier(db, clinicGroupId);
13008
- const config = TIER_CONFIG[tier];
13009
- if (!config) return;
13010
- const max = config.limits.maxStaff;
13011
- if (max === -1) return;
13012
- const staffQuery = (0, import_firestore35.query)(
13013
- (0, import_firestore35.collection)(db, "clinic_staff_members"),
13014
- (0, import_firestore35.where)("clinicGroupId", "==", clinicGroupId),
13015
- (0, import_firestore35.where)("isActive", "==", true)
13016
- );
13017
- const staffSnap = await (0, import_firestore35.getDocs)(staffQuery);
13018
- const currentCount = staffSnap.size;
13019
- if (currentCount + 1 > max) {
13020
- throw new TierLimitError("staff members", tier, currentCount, max);
13021
- }
13022
- }
13023
- async function enforceAppointmentLimit(db, clinicGroupId) {
13024
- var _a;
13025
- const tier = await getEffectiveTier(db, clinicGroupId);
13026
- const config = TIER_CONFIG[tier];
13027
- if (!config) return;
13028
- const max = config.limits.maxAppointmentsPerMonth;
13029
- if (max === -1) return;
13030
- const now = /* @__PURE__ */ new Date();
13031
- const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
13032
- const counterRef = (0, import_firestore35.doc)(
13033
- db,
13034
- `${CLINIC_GROUPS_COLLECTION}/${clinicGroupId}/usage_counters/${yearMonth}`
13035
- );
13036
- const counterSnap = await (0, import_firestore35.getDoc)(counterRef);
13037
- const currentCount = counterSnap.exists() ? ((_a = counterSnap.data()) == null ? void 0 : _a.appointmentsCreated) || 0 : 0;
13038
- if (currentCount + 1 > max) {
13039
- throw new TierLimitError("appointments this month", tier, currentCount, max);
13040
- }
13041
- }
13042
- async function enforceMessageLimit(db, clinicGroupId) {
13043
- var _a;
13044
- const tier = await getEffectiveTier(db, clinicGroupId);
13045
- const config = TIER_CONFIG[tier];
13046
- if (!config) return;
13047
- const max = config.limits.maxMessagesPerMonth;
13048
- if (max === -1) return;
13049
- const now = /* @__PURE__ */ new Date();
13050
- const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
13051
- const counterRef = (0, import_firestore35.doc)(
13052
- db,
13053
- `${CLINIC_GROUPS_COLLECTION}/${clinicGroupId}/usage_counters/${yearMonth}`
13054
- );
13055
- const counterSnap = await (0, import_firestore35.getDoc)(counterRef);
13056
- const currentCount = counterSnap.exists() ? ((_a = counterSnap.data()) == null ? void 0 : _a.messagesCount) || 0 : 0;
13057
- if (currentCount + 1 > max) {
13058
- throw new TierLimitError("messages this month", tier, currentCount, max);
12994
+ if (currentCount + 1 > effectiveMax) {
12995
+ throw new TierLimitError("clinic branches", tier, currentCount, effectiveMax);
13059
12996
  }
13060
12997
  }
13061
12998
 
@@ -13311,7 +13248,7 @@ var PractitionerService = class extends BaseService {
13311
13248
  if (clinicSnap.exists()) {
13312
13249
  const clinicGroupId = clinicSnap.data().clinicGroupId;
13313
13250
  if (clinicGroupId) {
13314
- await enforceProviderLimit(this.db, clinicGroupId);
13251
+ await enforceProviderLimit(this.db, clinicGroupId, validData.clinics[0]);
13315
13252
  }
13316
13253
  }
13317
13254
  }
@@ -13391,7 +13328,7 @@ var PractitionerService = class extends BaseService {
13391
13328
  throw new Error(`Clinic ${clinicId} not found`);
13392
13329
  }
13393
13330
  if (clinic.clinicGroupId) {
13394
- await enforceProviderLimit(this.db, clinic.clinicGroupId);
13331
+ await enforceProviderLimit(this.db, clinic.clinicGroupId, clinicId);
13395
13332
  }
13396
13333
  const clinicsToAdd = /* @__PURE__ */ new Set([clinicId]);
13397
13334
  if (data.clinics && data.clinics.length > 0) {
@@ -23182,7 +23119,7 @@ var ProcedureService = class extends BaseService {
23182
23119
  throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
23183
23120
  }
23184
23121
  const clinic = clinicSnapshot.data();
23185
- await enforceProcedureLimit(this.db, clinic.clinicGroupId);
23122
+ await enforceProcedureLimit(this.db, clinic.clinicGroupId, validatedData.clinicBranchId, validatedData.practitionerId);
23186
23123
  const practitionerRef = (0, import_firestore63.doc)(this.db, PRACTITIONERS_COLLECTION, validatedData.practitionerId);
23187
23124
  const practitionerSnapshot = await (0, import_firestore63.getDoc)(practitionerRef);
23188
23125
  if (!practitionerSnapshot.exists()) {
@@ -23589,7 +23526,9 @@ var ProcedureService = class extends BaseService {
23589
23526
  throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
23590
23527
  }
23591
23528
  const clinic = clinicSnapshot.data();
23592
- await enforceProcedureLimit(this.db, clinic.clinicGroupId, practitionerIds.length);
23529
+ for (const practitionerId of practitionerIds) {
23530
+ await enforceProcedureLimit(this.db, clinic.clinicGroupId, validatedData.clinicBranchId, practitionerId);
23531
+ }
23593
23532
  let processedPhotos = [];
23594
23533
  if (validatedData.photos && validatedData.photos.length > 0) {
23595
23534
  const batchId = this.generateId();
@@ -28067,12 +28006,9 @@ var RequirementType = /* @__PURE__ */ ((RequirementType2) => {
28067
28006
  USER_FORMS_SUBCOLLECTION,
28068
28007
  UserRole,
28069
28008
  UserService,
28070
- enforceAppointmentLimit,
28071
28009
  enforceBranchLimit,
28072
- enforceMessageLimit,
28073
28010
  enforceProcedureLimit,
28074
28011
  enforceProviderLimit,
28075
- enforceStaffLimit,
28076
28012
  getEffectiveTier,
28077
28013
  getFirebaseApp,
28078
28014
  getFirebaseAuth,
package/dist/index.mjs CHANGED
@@ -12752,11 +12752,8 @@ var TIER_CONFIG = {
12752
12752
  tier: "free",
12753
12753
  name: "Free",
12754
12754
  limits: {
12755
- maxProviders: 1,
12756
- maxProcedures: 3,
12757
- maxAppointmentsPerMonth: 3,
12758
- maxMessagesPerMonth: 10,
12759
- maxStaff: 1,
12755
+ maxProvidersPerBranch: 1,
12756
+ maxProceduresPerProvider: 3,
12760
12757
  maxBranches: 1
12761
12758
  }
12762
12759
  },
@@ -12764,11 +12761,8 @@ var TIER_CONFIG = {
12764
12761
  tier: "connect",
12765
12762
  name: "Connect",
12766
12763
  limits: {
12767
- maxProviders: -1,
12768
- maxProcedures: 15,
12769
- maxAppointmentsPerMonth: 100,
12770
- maxMessagesPerMonth: -1,
12771
- maxStaff: 5,
12764
+ maxProvidersPerBranch: 3,
12765
+ maxProceduresPerProvider: 10,
12772
12766
  maxBranches: 1
12773
12767
  }
12774
12768
  },
@@ -12776,12 +12770,9 @@ var TIER_CONFIG = {
12776
12770
  tier: "pro",
12777
12771
  name: "Pro",
12778
12772
  limits: {
12779
- maxProviders: -1,
12780
- maxProcedures: -1,
12781
- maxAppointmentsPerMonth: -1,
12782
- maxMessagesPerMonth: -1,
12783
- maxStaff: -1,
12784
- maxBranches: -1
12773
+ maxProvidersPerBranch: 10,
12774
+ maxProceduresPerProvider: 20,
12775
+ maxBranches: 3
12785
12776
  }
12786
12777
  }
12787
12778
  };
@@ -12912,8 +12903,10 @@ function resolveEffectiveTier(subscriptionModel) {
12912
12903
  var TierLimitError = class extends Error {
12913
12904
  constructor(resource, currentTier, currentCount, maxAllowed) {
12914
12905
  const tierLabel = currentTier.charAt(0).toUpperCase() + currentTier.slice(1);
12906
+ const canBuyAddons = currentTier === "connect" || currentTier === "pro";
12907
+ const actionHint = canBuyAddons ? "Please upgrade your plan or purchase an add-on." : "Please upgrade to Connect or Pro to add more.";
12915
12908
  super(
12916
- `Your ${tierLabel} plan allows a maximum of ${maxAllowed} ${resource}. You currently have ${currentCount}. Please upgrade your plan to add more.`
12909
+ `Your ${tierLabel} plan allows a maximum of ${maxAllowed} ${resource}. You currently have ${currentCount}. ${actionHint}`
12917
12910
  );
12918
12911
  this.code = "TIER_LIMIT_EXCEEDED";
12919
12912
  this.name = "TierLimitError";
@@ -12923,58 +12916,50 @@ var TierLimitError = class extends Error {
12923
12916
  this.maxAllowed = maxAllowed;
12924
12917
  }
12925
12918
  };
12926
- async function getEffectiveTier(db, clinicGroupId) {
12919
+ async function getClinicGroupTierData(db, clinicGroupId) {
12927
12920
  const groupRef = doc24(db, CLINIC_GROUPS_COLLECTION, clinicGroupId);
12928
12921
  const groupSnap = await getDoc26(groupRef);
12929
12922
  if (!groupSnap.exists()) {
12930
12923
  throw new Error(`Clinic group ${clinicGroupId} not found`);
12931
12924
  }
12932
- const subscriptionModel = groupSnap.data().subscriptionModel || "no_subscription";
12933
- return resolveEffectiveTier(subscriptionModel);
12925
+ const data = groupSnap.data();
12926
+ const subscriptionModel = data.subscriptionModel || "no_subscription";
12927
+ return {
12928
+ tier: resolveEffectiveTier(subscriptionModel),
12929
+ billing: data.billing
12930
+ };
12934
12931
  }
12935
- async function countProvidersInGroup(db, clinicGroupId) {
12936
- const clinicsQuery = query13(
12937
- collection13(db, CLINICS_COLLECTION),
12938
- where13("clinicGroupId", "==", clinicGroupId),
12939
- where13("isActive", "==", true)
12940
- );
12941
- const clinicsSnap = await getDocs13(clinicsQuery);
12942
- const clinicIds = clinicsSnap.docs.map((d) => d.id);
12943
- if (clinicIds.length === 0) return 0;
12932
+ async function getEffectiveTier(db, clinicGroupId) {
12933
+ const { tier } = await getClinicGroupTierData(db, clinicGroupId);
12934
+ return tier;
12935
+ }
12936
+ async function countProvidersInBranch(db, branchId) {
12944
12937
  const practitionersQuery = query13(
12945
12938
  collection13(db, PRACTITIONERS_COLLECTION),
12946
12939
  where13("isActive", "==", true)
12947
12940
  );
12948
12941
  const practitionersSnap = await getDocs13(practitionersQuery);
12949
- const clinicIdSet = new Set(clinicIds);
12950
- const uniqueProviders = practitionersSnap.docs.filter((d) => {
12951
- const data = d.data();
12952
- const clinics = data.clinics || [];
12953
- return clinics.some((c) => clinicIdSet.has(c));
12954
- });
12955
- return uniqueProviders.length;
12942
+ return practitionersSnap.docs.filter((d) => {
12943
+ const clinics = d.data().clinics || [];
12944
+ return clinics.includes(branchId);
12945
+ }).length;
12956
12946
  }
12957
- async function countProceduresInGroup(db, clinicGroupId) {
12958
- const clinicsQuery = query13(
12959
- collection13(db, CLINICS_COLLECTION),
12960
- where13("clinicGroupId", "==", clinicGroupId),
12947
+ async function countProceduresForProvider(db, branchId, providerId) {
12948
+ const proceduresQuery = query13(
12949
+ collection13(db, PROCEDURES_COLLECTION),
12950
+ where13("clinicBranchId", "==", branchId),
12951
+ where13("practitionerId", "==", providerId),
12961
12952
  where13("isActive", "==", true)
12962
12953
  );
12963
- const clinicsSnap = await getDocs13(clinicsQuery);
12964
- const clinicIds = clinicsSnap.docs.map((d) => d.id);
12965
- if (clinicIds.length === 0) return 0;
12966
- let totalProcedures = 0;
12967
- for (let i = 0; i < clinicIds.length; i += 30) {
12968
- const batch = clinicIds.slice(i, i + 30);
12969
- const proceduresQuery = query13(
12970
- collection13(db, PROCEDURES_COLLECTION),
12971
- where13("clinicBranchId", "in", batch),
12972
- where13("isActive", "==", true)
12973
- );
12974
- const proceduresSnap = await getDocs13(proceduresQuery);
12975
- totalProcedures += proceduresSnap.size;
12976
- }
12977
- return totalProcedures;
12954
+ const proceduresSnap = await getDocs13(proceduresQuery);
12955
+ const uniqueTechnologyIds = /* @__PURE__ */ new Set();
12956
+ proceduresSnap.docs.forEach((d) => {
12957
+ const technologyId = d.data().technologyId;
12958
+ if (technologyId) {
12959
+ uniqueTechnologyIds.add(technologyId);
12960
+ }
12961
+ });
12962
+ return uniqueTechnologyIds.size;
12978
12963
  }
12979
12964
  async function countBranchesInGroup(db, clinicGroupId) {
12980
12965
  const clinicsQuery = query13(
@@ -12985,92 +12970,47 @@ async function countBranchesInGroup(db, clinicGroupId) {
12985
12970
  const clinicsSnap = await getDocs13(clinicsQuery);
12986
12971
  return clinicsSnap.size;
12987
12972
  }
12988
- async function enforceProviderLimit(db, clinicGroupId) {
12989
- const tier = await getEffectiveTier(db, clinicGroupId);
12973
+ async function enforceProviderLimit(db, clinicGroupId, branchId) {
12974
+ var _a;
12975
+ const { tier, billing } = await getClinicGroupTierData(db, clinicGroupId);
12990
12976
  const config = TIER_CONFIG[tier];
12991
12977
  if (!config) return;
12992
- const max = config.limits.maxProviders;
12993
- if (max === -1) return;
12994
- const currentCount = await countProvidersInGroup(db, clinicGroupId);
12995
- if (currentCount + 1 > max) {
12996
- throw new TierLimitError("providers", tier, currentCount, max);
12978
+ const baseMax = config.limits.maxProvidersPerBranch;
12979
+ if (baseMax === -1) return;
12980
+ const addOns = (billing == null ? void 0 : billing.addOns) || {};
12981
+ const extraProviders = ((_a = addOns[branchId]) == null ? void 0 : _a.extraProviders) || 0;
12982
+ const effectiveMax = baseMax + extraProviders;
12983
+ const currentCount = await countProvidersInBranch(db, branchId);
12984
+ if (currentCount + 1 > effectiveMax) {
12985
+ throw new TierLimitError("providers in this branch", tier, currentCount, effectiveMax);
12997
12986
  }
12998
12987
  }
12999
- async function enforceProcedureLimit(db, clinicGroupId, count = 1) {
13000
- const tier = await getEffectiveTier(db, clinicGroupId);
12988
+ async function enforceProcedureLimit(db, clinicGroupId, branchId, providerId, count = 1) {
12989
+ var _a;
12990
+ const { tier, billing } = await getClinicGroupTierData(db, clinicGroupId);
13001
12991
  const config = TIER_CONFIG[tier];
13002
12992
  if (!config) return;
13003
- const max = config.limits.maxProcedures;
13004
- if (max === -1) return;
13005
- const currentCount = await countProceduresInGroup(db, clinicGroupId);
13006
- if (currentCount + count > max) {
13007
- throw new TierLimitError("procedures", tier, currentCount, max);
12993
+ const baseMax = config.limits.maxProceduresPerProvider;
12994
+ if (baseMax === -1) return;
12995
+ const addOns = (billing == null ? void 0 : billing.addOns) || {};
12996
+ const procedureBlocks = ((_a = addOns[branchId]) == null ? void 0 : _a.procedureBlocks) || 0;
12997
+ const effectiveMax = baseMax + procedureBlocks * 10;
12998
+ const currentCount = await countProceduresForProvider(db, branchId, providerId);
12999
+ if (currentCount + count > effectiveMax) {
13000
+ throw new TierLimitError("procedures for this provider", tier, currentCount, effectiveMax);
13008
13001
  }
13009
13002
  }
13010
13003
  async function enforceBranchLimit(db, clinicGroupId) {
13011
- const tier = await getEffectiveTier(db, clinicGroupId);
13004
+ const { tier, billing } = await getClinicGroupTierData(db, clinicGroupId);
13012
13005
  const config = TIER_CONFIG[tier];
13013
13006
  if (!config) return;
13014
- const max = config.limits.maxBranches;
13015
- if (max === -1) return;
13007
+ const baseMax = config.limits.maxBranches;
13008
+ if (baseMax === -1) return;
13009
+ const branchAddonCount = (billing == null ? void 0 : billing.branchAddonCount) || 0;
13010
+ const effectiveMax = baseMax + branchAddonCount;
13016
13011
  const currentCount = await countBranchesInGroup(db, clinicGroupId);
13017
- if (currentCount + 1 > max) {
13018
- throw new TierLimitError("clinic branches", tier, currentCount, max);
13019
- }
13020
- }
13021
- async function enforceStaffLimit(db, clinicGroupId) {
13022
- const tier = await getEffectiveTier(db, clinicGroupId);
13023
- const config = TIER_CONFIG[tier];
13024
- if (!config) return;
13025
- const max = config.limits.maxStaff;
13026
- if (max === -1) return;
13027
- const staffQuery = query13(
13028
- collection13(db, "clinic_staff_members"),
13029
- where13("clinicGroupId", "==", clinicGroupId),
13030
- where13("isActive", "==", true)
13031
- );
13032
- const staffSnap = await getDocs13(staffQuery);
13033
- const currentCount = staffSnap.size;
13034
- if (currentCount + 1 > max) {
13035
- throw new TierLimitError("staff members", tier, currentCount, max);
13036
- }
13037
- }
13038
- async function enforceAppointmentLimit(db, clinicGroupId) {
13039
- var _a;
13040
- const tier = await getEffectiveTier(db, clinicGroupId);
13041
- const config = TIER_CONFIG[tier];
13042
- if (!config) return;
13043
- const max = config.limits.maxAppointmentsPerMonth;
13044
- if (max === -1) return;
13045
- const now = /* @__PURE__ */ new Date();
13046
- const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
13047
- const counterRef = doc24(
13048
- db,
13049
- `${CLINIC_GROUPS_COLLECTION}/${clinicGroupId}/usage_counters/${yearMonth}`
13050
- );
13051
- const counterSnap = await getDoc26(counterRef);
13052
- const currentCount = counterSnap.exists() ? ((_a = counterSnap.data()) == null ? void 0 : _a.appointmentsCreated) || 0 : 0;
13053
- if (currentCount + 1 > max) {
13054
- throw new TierLimitError("appointments this month", tier, currentCount, max);
13055
- }
13056
- }
13057
- async function enforceMessageLimit(db, clinicGroupId) {
13058
- var _a;
13059
- const tier = await getEffectiveTier(db, clinicGroupId);
13060
- const config = TIER_CONFIG[tier];
13061
- if (!config) return;
13062
- const max = config.limits.maxMessagesPerMonth;
13063
- if (max === -1) return;
13064
- const now = /* @__PURE__ */ new Date();
13065
- const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
13066
- const counterRef = doc24(
13067
- db,
13068
- `${CLINIC_GROUPS_COLLECTION}/${clinicGroupId}/usage_counters/${yearMonth}`
13069
- );
13070
- const counterSnap = await getDoc26(counterRef);
13071
- const currentCount = counterSnap.exists() ? ((_a = counterSnap.data()) == null ? void 0 : _a.messagesCount) || 0 : 0;
13072
- if (currentCount + 1 > max) {
13073
- throw new TierLimitError("messages this month", tier, currentCount, max);
13012
+ if (currentCount + 1 > effectiveMax) {
13013
+ throw new TierLimitError("clinic branches", tier, currentCount, effectiveMax);
13074
13014
  }
13075
13015
  }
13076
13016
 
@@ -13326,7 +13266,7 @@ var PractitionerService = class extends BaseService {
13326
13266
  if (clinicSnap.exists()) {
13327
13267
  const clinicGroupId = clinicSnap.data().clinicGroupId;
13328
13268
  if (clinicGroupId) {
13329
- await enforceProviderLimit(this.db, clinicGroupId);
13269
+ await enforceProviderLimit(this.db, clinicGroupId, validData.clinics[0]);
13330
13270
  }
13331
13271
  }
13332
13272
  }
@@ -13406,7 +13346,7 @@ var PractitionerService = class extends BaseService {
13406
13346
  throw new Error(`Clinic ${clinicId} not found`);
13407
13347
  }
13408
13348
  if (clinic.clinicGroupId) {
13409
- await enforceProviderLimit(this.db, clinic.clinicGroupId);
13349
+ await enforceProviderLimit(this.db, clinic.clinicGroupId, clinicId);
13410
13350
  }
13411
13351
  const clinicsToAdd = /* @__PURE__ */ new Set([clinicId]);
13412
13352
  if (data.clinics && data.clinics.length > 0) {
@@ -23411,7 +23351,7 @@ var ProcedureService = class extends BaseService {
23411
23351
  throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
23412
23352
  }
23413
23353
  const clinic = clinicSnapshot.data();
23414
- await enforceProcedureLimit(this.db, clinic.clinicGroupId);
23354
+ await enforceProcedureLimit(this.db, clinic.clinicGroupId, validatedData.clinicBranchId, validatedData.practitionerId);
23415
23355
  const practitionerRef = doc44(this.db, PRACTITIONERS_COLLECTION, validatedData.practitionerId);
23416
23356
  const practitionerSnapshot = await getDoc45(practitionerRef);
23417
23357
  if (!practitionerSnapshot.exists()) {
@@ -23818,7 +23758,9 @@ var ProcedureService = class extends BaseService {
23818
23758
  throw new Error(`Clinic with ID ${validatedData.clinicBranchId} not found`);
23819
23759
  }
23820
23760
  const clinic = clinicSnapshot.data();
23821
- await enforceProcedureLimit(this.db, clinic.clinicGroupId, practitionerIds.length);
23761
+ for (const practitionerId of practitionerIds) {
23762
+ await enforceProcedureLimit(this.db, clinic.clinicGroupId, validatedData.clinicBranchId, practitionerId);
23763
+ }
23822
23764
  let processedPhotos = [];
23823
23765
  if (validatedData.photos && validatedData.photos.length > 0) {
23824
23766
  const batchId = this.generateId();
@@ -28383,12 +28325,9 @@ export {
28383
28325
  USER_FORMS_SUBCOLLECTION,
28384
28326
  UserRole,
28385
28327
  UserService,
28386
- enforceAppointmentLimit,
28387
28328
  enforceBranchLimit,
28388
- enforceMessageLimit,
28389
28329
  enforceProcedureLimit,
28390
28330
  enforceProviderLimit,
28391
- enforceStaffLimit,
28392
28331
  getEffectiveTier,
28393
28332
  getFirebaseApp,
28394
28333
  getFirebaseAuth,
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.10",
4
+ "version": "1.15.14",
5
5
  "description": "Firebase authentication service with anonymous upgrade support",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",