@goweekdays/core 2.11.11 → 2.11.13

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.mjs CHANGED
@@ -5435,6 +5435,7 @@ var schemaSubscriptionCompute = Joi20.object({
5435
5435
  seats: Joi20.number().integer().min(1).required(),
5436
5436
  plan: Joi20.string().hex().length(24).required(),
5437
5437
  promoCode: Joi20.string().optional().allow("", null),
5438
+ nextPromoCode: Joi20.string().optional().allow("", null),
5438
5439
  org: Joi20.string().hex().length(24).required()
5439
5440
  });
5440
5441
  var schemaSubscribe = Joi20.object({
@@ -5453,6 +5454,7 @@ var schema2 = {
5453
5454
  };
5454
5455
  var schemaSubscription = Joi20.object({
5455
5456
  ...schema2,
5457
+ billingPeriodStart: Joi20.date().optional().allow("", null),
5456
5458
  org: Joi20.string().hex().length(24).required(),
5457
5459
  orgName: Joi20.string().optional().allow("", null),
5458
5460
  currency: Joi20.string().length(3).required(),
@@ -5499,9 +5501,11 @@ function modelSubscription(data) {
5499
5501
  amount: data.amount,
5500
5502
  currency: data.currency,
5501
5503
  billingCycle: data.billingCycle,
5502
- promoCode: data.promoCode,
5504
+ promoCode: data.promoCode ?? "",
5505
+ nextPromoCode: data.nextPromoCode ?? "",
5503
5506
  status: data.status ?? "active",
5504
5507
  retry: data.retry ?? 0,
5508
+ billingPeriodStart: data.billingPeriodStart,
5505
5509
  nextBillingDate: data.nextBillingDate,
5506
5510
  createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
5507
5511
  updatedAt: data.updatedAt ?? ""
@@ -5822,7 +5826,9 @@ function useSubscriptionRepo() {
5822
5826
  paidSeats: Joi21.number().integer().min(0).optional(),
5823
5827
  amount: Joi21.number().positive().optional().allow(0),
5824
5828
  promoCode: Joi21.string().max(50).optional().allow("", null),
5829
+ nextPromoCode: Joi21.string().max(50).optional().allow("", null),
5825
5830
  status: Joi21.string().valid("active", "due", "overdue", "suspended").optional().allow("", null),
5831
+ billingPeriodStart: Joi21.date().optional().allow("", null),
5826
5832
  nextBillingDate: Joi21.date().optional().allow("", null)
5827
5833
  });
5828
5834
  const { error } = validation.validate(options);
@@ -5992,7 +5998,17 @@ var schemaSubscriptionTransaction = Joi22.object({
5992
5998
  "promo-expired",
5993
5999
  "promo-updated"
5994
6000
  ).required(),
5995
- description: Joi22.string().max(255).optional().allow("", null),
6001
+ metadata: Joi22.object({
6002
+ additionalSeats: Joi22.number().integer().min(1).optional(),
6003
+ seats: Joi22.number().integer().min(0).optional(),
6004
+ paidSeats: Joi22.number().integer().min(0).optional(),
6005
+ plan: Joi22.string().hex().length(24).optional(),
6006
+ promoCode: Joi22.string().optional().allow("", null),
6007
+ nextPromoCode: Joi22.string().optional().allow("", null),
6008
+ billingPeriodStart: Joi22.date().optional().allow("", null),
6009
+ nextBillingDate: Joi22.date().optional().allow("", null)
6010
+ }).optional(),
6011
+ description: Joi22.string().optional().allow("", null),
5996
6012
  createdBy: Joi22.string().hex().length(24).required(),
5997
6013
  createdByName: Joi22.string().optional().allow("", null)
5998
6014
  });
@@ -6030,6 +6046,7 @@ function modelSubscriptionTransaction(data) {
6030
6046
  amount: data.amount,
6031
6047
  currency: data.currency,
6032
6048
  type: data.type,
6049
+ metadata: data.metadata ?? {},
6033
6050
  description: data.description ?? "",
6034
6051
  createdBy: data.createdBy,
6035
6052
  createdByName: data.createdByName,
@@ -8444,13 +8461,42 @@ function useSubscriptionService() {
8444
8461
  case "fixed":
8445
8462
  return Math.min(seats, limit) * (promo.fixedRate ?? 0) + Math.max(seats - limit, 0) * planPrice;
8446
8463
  case "flat":
8447
- return (promo.flatRate ?? 0) + Math.max(seats - limit, 0) * planPrice;
8464
+ return (promo.flatRate ?? 0) + Math.max(0, seats - limit) * planPrice;
8448
8465
  case "volume":
8449
8466
  return promo.tiers?.length ? calculateVolumeTierAmount(promo.tiers, 1, seats, planPrice) : planPrice * seats;
8450
8467
  default:
8451
8468
  return planPrice * seats;
8452
8469
  }
8453
8470
  }
8471
+ function computeAdditionalSeatAmount(additionalSeats, planPrice, promo, paidSeats) {
8472
+ if (additionalSeats <= 0)
8473
+ return 0;
8474
+ if (!promo) {
8475
+ return additionalSeats * planPrice;
8476
+ }
8477
+ const limit = promo.seats && promo.seats > 0 ? promo.seats : Infinity;
8478
+ switch (promo.type) {
8479
+ case "flat": {
8480
+ const chargeableSeats = Math.max(0, paidSeats + additionalSeats - limit) - Math.max(0, paidSeats - limit);
8481
+ return chargeableSeats * planPrice;
8482
+ }
8483
+ case "fixed": {
8484
+ const promoSeatsLeft = Math.max(0, limit - paidSeats);
8485
+ const promoSeats = Math.min(additionalSeats, promoSeatsLeft);
8486
+ const normalSeats = additionalSeats - promoSeats;
8487
+ return promoSeats * (promo.fixedRate ?? 0) + normalSeats * planPrice;
8488
+ }
8489
+ case "volume":
8490
+ return promo.tiers?.length ? calculateVolumeTierAmount(
8491
+ promo.tiers,
8492
+ paidSeats + 1,
8493
+ additionalSeats,
8494
+ planPrice
8495
+ ) : additionalSeats * planPrice;
8496
+ default:
8497
+ return additionalSeats * planPrice;
8498
+ }
8499
+ }
8454
8500
  function computeProration(subscription, seats, plan, promo) {
8455
8501
  const additionalSeats = Math.max(0, seats - subscription.paidSeats);
8456
8502
  if (additionalSeats === 0)
@@ -8464,10 +8510,11 @@ function useSubscriptionService() {
8464
8510
  const cycleDays = plan.billingCycle === "yearly" ? 365 : 30;
8465
8511
  if (daysRemaining === 0)
8466
8512
  return 0;
8467
- const additionalAmount = computeMonthlyAmount(
8513
+ const additionalAmount = computeAdditionalSeatAmount(
8468
8514
  additionalSeats,
8469
8515
  plan.price,
8470
- promo
8516
+ promo,
8517
+ subscription.paidSeats
8471
8518
  );
8472
8519
  return additionalAmount / cycleDays * daysRemaining;
8473
8520
  }
@@ -8483,7 +8530,6 @@ function useSubscriptionService() {
8483
8530
  const isNew = !existingSubscription;
8484
8531
  const isPromoChange = !!existingSubscription && value.promoCode !== void 0 && value.promoCode !== existingSubscription.promoCode;
8485
8532
  const isSeatIncrease = !!existingSubscription && value.seats > existingSubscription.seats;
8486
- const isSeatDecrease = !!existingSubscription && value.seats < existingSubscription.seats;
8487
8533
  if (isPromoChange && isSeatIncrease) {
8488
8534
  throw new BadRequestError41(
8489
8535
  "Cannot change promo code while increasing seats. Perform actions separately."
@@ -8553,7 +8599,8 @@ function useSubscriptionService() {
8553
8599
  if (!membership) {
8554
8600
  throw new BadRequestError41("User is not a member of the organization.");
8555
8601
  }
8556
- const nextBillingDate = /* @__PURE__ */ new Date();
8602
+ const billingPeriodStart = /* @__PURE__ */ new Date();
8603
+ const nextBillingDate = new Date(billingPeriodStart);
8557
8604
  nextBillingDate.setDate(nextBillingDate.getDate() + 30);
8558
8605
  const { subscriptionAmount, currency } = await computeFee({
8559
8606
  seats: value.seats,
@@ -8578,6 +8625,7 @@ function useSubscriptionService() {
8578
8625
  amount: subscriptionAmount,
8579
8626
  currency,
8580
8627
  billingCycle: plan.billingCycle,
8628
+ billingPeriodStart,
8581
8629
  nextBillingDate,
8582
8630
  promoCode: value.promoCode
8583
8631
  },
@@ -8681,7 +8729,17 @@ function useSubscriptionService() {
8681
8729
  currency,
8682
8730
  subscription: subscription._id?.toString() ?? "",
8683
8731
  createdBy: value.user,
8684
- createdByName: `${userData.firstName} ${userData.lastName}`
8732
+ createdByName: `${userData.firstName} ${userData.lastName}`,
8733
+ metadata: {
8734
+ additionalSeats,
8735
+ seats: value.seats,
8736
+ paidSeats,
8737
+ plan: value.plan ?? "",
8738
+ promoCode: subscription.promoCode ?? "",
8739
+ nextPromoCode: subscription.nextPromoCode ?? "",
8740
+ billingPeriodStart: subscription.billingPeriodStart,
8741
+ nextBillingDate: subscription.nextBillingDate
8742
+ }
8685
8743
  },
8686
8744
  session
8687
8745
  );
@@ -8746,7 +8804,7 @@ function useSubscriptionService() {
8746
8804
  });
8747
8805
  await updateById(
8748
8806
  subscription._id?.toString() ?? "",
8749
- { promoCode: value.promoCode ?? "", amount: subscriptionAmount },
8807
+ { nextPromoCode: value.promoCode ?? "" },
8750
8808
  session
8751
8809
  );
8752
8810
  if (subscription.promoCode) {
@@ -8757,7 +8815,7 @@ function useSubscriptionService() {
8757
8815
  {
8758
8816
  promo: promo._id,
8759
8817
  org: value.org,
8760
- usedBy: userData.email
8818
+ usedBy: value.user
8761
8819
  },
8762
8820
  session
8763
8821
  );
@@ -8765,12 +8823,19 @@ function useSubscriptionService() {
8765
8823
  await addTransaction(
8766
8824
  {
8767
8825
  type: "promo-updated",
8768
- description: `Updated promo code to ${value.promoCode || "none"}.`,
8826
+ description: `Promo code ${value.promoCode || "removed"} scheduled to take effect on the next billing cycle.`,
8769
8827
  amount: 0,
8770
- currency,
8828
+ currency: subscription.currency,
8771
8829
  subscription: subscription._id?.toString() ?? "",
8772
8830
  createdBy: value.user,
8773
- createdByName: `${userData.firstName} ${userData.lastName}`
8831
+ metadata: {
8832
+ seats: subscription.seats,
8833
+ paidSeats: subscription.paidSeats,
8834
+ promoCode: subscription.promoCode,
8835
+ nextPromoCode: value.promoCode ?? "",
8836
+ billingPeriodStart: subscription.billingPeriodStart,
8837
+ nextBillingDate: subscription.nextBillingDate
8838
+ }
8774
8839
  },
8775
8840
  session
8776
8841
  );
@@ -8930,39 +8995,43 @@ function useSubscriptionService() {
8930
8995
  }
8931
8996
  const ledgerBill = await getByInvoice(invoiceId);
8932
8997
  if (!ledgerBill) {
8933
- throw new BadRequestError41(
8934
- "Ledger bill not found for the given invoice ID."
8935
- );
8998
+ throw new BadRequestError41("Ledger bill not found.");
8936
8999
  }
8937
9000
  const orgId = String(ledgerBill.org);
8938
- const org = await getOrgById(orgId);
8939
- if (!org) {
8940
- throw new BadRequestError41("Organization not found for the ledger bill.");
8941
- }
8942
9001
  const subscription = await getByOrg(orgId);
8943
9002
  if (!subscription) {
8944
- throw new BadRequestError41("Subscription not found for the organization.");
9003
+ throw new BadRequestError41("Subscription not found.");
8945
9004
  }
8946
9005
  const plan = await getDefaultPlan();
8947
9006
  if (!plan) {
8948
- throw new BadRequestError41("Default plan not found.");
9007
+ throw new BadRequestError41("Plan not found.");
8949
9008
  }
8950
9009
  const session = useAtlas18.getClient()?.startSession();
8951
9010
  if (!session) {
8952
- throw new Error("Unable to start database session.");
9011
+ throw new InternalServerError22("Unable to start database session.");
8953
9012
  }
8954
9013
  try {
8955
9014
  session.startTransaction();
8956
- const subscriptionId = String(subscription._id);
8957
- const nextBillingDate = /* @__PURE__ */ new Date();
9015
+ const billingPeriodStart = /* @__PURE__ */ new Date();
9016
+ const nextBillingDate = new Date(billingPeriodStart);
8958
9017
  nextBillingDate.setMonth(nextBillingDate.getMonth() + 1);
9018
+ const effectivePromoCode = subscription.nextPromoCode || subscription.promoCode;
9019
+ const promo = effectivePromoCode ? await getPromoByCode(effectivePromoCode) : null;
9020
+ const monthlyAmount = computeMonthlyAmount(
9021
+ subscription.seats,
9022
+ plan.price,
9023
+ promo
9024
+ );
8959
9025
  await updateById(
8960
- subscriptionId,
9026
+ subscription._id?.toString() ?? "",
8961
9027
  {
8962
9028
  status: "active",
9029
+ billingPeriodStart,
8963
9030
  nextBillingDate,
8964
9031
  paidSeats: subscription.seats,
8965
- amount: plan.price * subscription.seats
9032
+ amount: Math.round(monthlyAmount * 100) / 100,
9033
+ promoCode: effectivePromoCode,
9034
+ nextPromoCode: ""
8966
9035
  },
8967
9036
  session
8968
9037
  );
@@ -8973,14 +9042,7 @@ function useSubscriptionService() {
8973
9042
  return "Successfully processed paid invoice.";
8974
9043
  } catch (error2) {
8975
9044
  await session.abortTransaction();
8976
- logger23.log({
8977
- level: "error",
8978
- message: `Failed to process paid invoice ${invoiceId}: ${error2 instanceof Error ? error2.message : String(error2)}`
8979
- });
8980
- if (error2 instanceof AppError20) {
8981
- throw error2;
8982
- }
8983
- throw new InternalServerError22("Failed to process paid invoice.");
9045
+ throw error2;
8984
9046
  } finally {
8985
9047
  session.endSession();
8986
9048
  }
@@ -9520,6 +9582,7 @@ function useOrgService() {
9520
9582
  paidSeats: value.seats,
9521
9583
  currency: plan.currency,
9522
9584
  billingCycle: plan.billingCycle,
9585
+ billingPeriodStart: currentDate,
9523
9586
  nextBillingDate
9524
9587
  },
9525
9588
  session