@goweekdays/core 2.11.0 → 2.11.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @goweekdays/core
2
2
 
3
+ ## 2.11.2
4
+
5
+ ### Patch Changes
6
+
7
+ - d27a91f: Fix seat decrease billing logic in subscription service
8
+ - 69837e4: Allow zero amount in subscription schema and fix promo calc
9
+
10
+ ## 2.11.1
11
+
12
+ ### Patch Changes
13
+
14
+ - 86a2d45: Add skipProration option to computeFee function
15
+
3
16
  ## 2.11.0
4
17
 
5
18
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -901,7 +901,7 @@ declare function useSubscriptionService(): {
901
901
  promoCode?: string;
902
902
  org: string;
903
903
  plan: string;
904
- }) => Promise<{
904
+ }, skipProration?: boolean) => Promise<{
905
905
  monthlyAmount: number;
906
906
  proratedAmount: number;
907
907
  subscriptionAmount: number;
package/dist/index.js CHANGED
@@ -5446,7 +5446,7 @@ var schemaSubscribe = import_joi20.default.object({
5446
5446
  var schema2 = {
5447
5447
  seats: import_joi20.default.number().integer().min(1).required(),
5448
5448
  paidSeats: import_joi20.default.number().integer().min(0).required(),
5449
- amount: import_joi20.default.number().positive().required(),
5449
+ amount: import_joi20.default.number().positive().required().allow(0),
5450
5450
  promoCode: import_joi20.default.string().optional().allow("", null),
5451
5451
  nextBillingDate: import_joi20.default.date().optional().allow("", null)
5452
5452
  };
@@ -8328,7 +8328,7 @@ function useSubscriptionService() {
8328
8328
  }
8329
8329
  return total;
8330
8330
  }
8331
- async function computeFee(value) {
8331
+ async function computeFee(value, skipProration) {
8332
8332
  const { error } = schemaSubscriptionCompute.validate(value);
8333
8333
  if (error) {
8334
8334
  throw new import_utils44.BadRequestError(`Invalid subscription data: ${error.message}`);
@@ -8354,10 +8354,10 @@ function useSubscriptionService() {
8354
8354
  if (promo) {
8355
8355
  switch (promo.type) {
8356
8356
  case "fixed":
8357
- monthlyAmount = Math.max(plan.price - (promo.fixedRate ?? 0), 0) * value.seats;
8357
+ monthlyAmount = Math.max(promo.fixedRate ?? 0, 0) * value.seats;
8358
8358
  break;
8359
8359
  case "flat":
8360
- monthlyAmount = Math.max(plan.price - (promo.flatRate ?? 0) / value.seats, 0) * value.seats;
8360
+ monthlyAmount = Math.max((promo.flatRate ?? 0) / value.seats, 0) * value.seats;
8361
8361
  break;
8362
8362
  case "volume": {
8363
8363
  if (promo.tiers && promo.tiers.length > 0) {
@@ -8391,7 +8391,7 @@ function useSubscriptionService() {
8391
8391
  0,
8392
8392
  value.seats - existingSubscription.paidSeats
8393
8393
  );
8394
- if (additionalSeats > 0 && daysRemaining > 0 && daysElapsed > 0) {
8394
+ if (additionalSeats > 0 && daysRemaining > 0 && daysElapsed > 0 && !skipProration) {
8395
8395
  let additionalSeatsAmount = 0;
8396
8396
  if (promo?.type === "volume" && promo.tiers && promo.tiers.length > 0) {
8397
8397
  additionalSeatsAmount = calculateVolumeTierAmount(
@@ -8419,14 +8419,17 @@ function useSubscriptionService() {
8419
8419
  proratedAmount = dailyRate * daysRemaining;
8420
8420
  }
8421
8421
  }
8422
+ const isDecrease = existingSubscription && value.seats < existingSubscription.paidSeats;
8422
8423
  return {
8423
8424
  // The monthly fee for the next billing cycle
8424
8425
  monthlyAmount: Math.round(monthlyAmount * 100) / 100,
8425
8426
  // The prorated amount to charge now for mid-cycle seat additions
8426
8427
  proratedAmount: Math.round(proratedAmount * 100) / 100,
8427
8428
  // The new subscription.amount for updates
8428
- // If no proration (same day change), use monthlyAmount; otherwise add prorated to existing
8429
- subscriptionAmount: existingSubscription ? proratedAmount > 0 ? Math.round((existingSubscription.amount + proratedAmount) * 100) / 100 : Math.round(monthlyAmount * 100) / 100 : Math.round(monthlyAmount * 100) / 100,
8429
+ // - Seat increase with proration: existing amount + prorated
8430
+ // - Seat decrease: keep existing amount (decrease takes effect next cycle)
8431
+ // - Same day change or no proration: use monthlyAmount
8432
+ subscriptionAmount: existingSubscription ? proratedAmount > 0 ? Math.round((existingSubscription.amount + proratedAmount) * 100) / 100 : isDecrease ? Math.round(existingSubscription.amount * 100) / 100 : Math.round(monthlyAmount * 100) / 100 : Math.round(monthlyAmount * 100) / 100,
8430
8433
  currency: plan.currency
8431
8434
  };
8432
8435
  }
@@ -8537,9 +8540,12 @@ function useSubscriptionService() {
8537
8540
  if (!membership) {
8538
8541
  throw new import_utils44.BadRequestError("User is not a member of the organization.");
8539
8542
  }
8540
- const { subscriptionAmount, proratedAmount, currency } = await computeFee(
8541
- value
8542
- );
8543
+ const { subscriptionAmount, proratedAmount, currency } = await computeFee({
8544
+ seats: value.seats,
8545
+ promoCode: value.promoCode ?? "",
8546
+ plan: value.plan ?? "",
8547
+ org: value.org
8548
+ });
8543
8549
  const session = import_utils44.useAtlas.getClient()?.startSession();
8544
8550
  if (!session) {
8545
8551
  throw new import_utils44.InternalServerError("Unable to start database session.");
@@ -8614,12 +8620,15 @@ function useSubscriptionService() {
8614
8620
  if (!plan) {
8615
8621
  throw new import_utils44.BadRequestError("Plan not found.");
8616
8622
  }
8617
- const { subscriptionAmount, currency } = await computeFee({
8618
- seats: subscription.seats,
8619
- promoCode: value.promoCode ?? "",
8620
- plan: plan._id?.toString() ?? "",
8621
- org: value.org
8622
- });
8623
+ const { subscriptionAmount, currency } = await computeFee(
8624
+ {
8625
+ seats: subscription.seats,
8626
+ promoCode: value.promoCode ?? "",
8627
+ plan: plan._id?.toString() ?? "",
8628
+ org: value.org
8629
+ },
8630
+ true
8631
+ );
8623
8632
  const session = import_utils44.useAtlas.getClient()?.startSession();
8624
8633
  if (!session) {
8625
8634
  throw new import_utils44.InternalServerError("Unable to start database session.");