@goweekdays/core 2.11.12 → 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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @goweekdays/core
2
2
 
3
+ ## 2.11.13
4
+
5
+ ### Patch Changes
6
+
7
+ - 0458614: Add support for next promo code and billing period start
8
+
3
9
  ## 2.11.12
4
10
 
5
11
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -866,8 +866,10 @@ type TSubscription = {
866
866
  currency: string;
867
867
  billingCycle: "monthly" | "yearly";
868
868
  promoCode?: string;
869
+ nextPromoCode?: string;
869
870
  retry?: number;
870
871
  status?: string;
872
+ billingPeriodStart: Date | string;
871
873
  nextBillingDate: Date | string;
872
874
  createdAt?: Date | string;
873
875
  updatedAt?: Date | string;
@@ -904,7 +906,9 @@ declare function useSubscriptionRepo(): {
904
906
  paidSeats?: number;
905
907
  amount?: number;
906
908
  promoCode?: string;
909
+ nextPromoCode?: string;
907
910
  status?: string;
911
+ billingPeriodStart?: Date;
908
912
  nextBillingDate?: Date;
909
913
  }, session?: ClientSession) => Promise<string>;
910
914
  getByStatus: (status?: string, limit?: number, retry?: number) => Promise<TSubscription[]>;
@@ -949,12 +953,23 @@ declare function useSubscriptionService(): {
949
953
  processPaidInvoice: (invoiceId: string) => Promise<string>;
950
954
  };
951
955
 
956
+ type TSubscriptionTransactionMetadata = {
957
+ additionalSeats?: number;
958
+ seats?: number;
959
+ paidSeats?: number;
960
+ plan?: string;
961
+ promoCode?: string;
962
+ nextPromoCode?: string;
963
+ billingPeriodStart?: Date | string;
964
+ nextBillingDate?: Date | string;
965
+ };
952
966
  type TSubscriptionTransaction = {
953
967
  _id?: ObjectId;
954
968
  subscription: string | ObjectId;
955
969
  amount: number;
956
970
  currency: string;
957
971
  type: "initiate" | "add-seat" | "remove-seat" | "renewal" | "promo-applied" | "promo-removed" | "promo-expired" | "promo-updated";
972
+ metadata?: TSubscriptionTransactionMetadata;
958
973
  description?: string;
959
974
  createdBy: string | ObjectId;
960
975
  createdByName?: string;
package/dist/index.js CHANGED
@@ -5486,6 +5486,7 @@ var schemaSubscriptionCompute = import_joi20.default.object({
5486
5486
  seats: import_joi20.default.number().integer().min(1).required(),
5487
5487
  plan: import_joi20.default.string().hex().length(24).required(),
5488
5488
  promoCode: import_joi20.default.string().optional().allow("", null),
5489
+ nextPromoCode: import_joi20.default.string().optional().allow("", null),
5489
5490
  org: import_joi20.default.string().hex().length(24).required()
5490
5491
  });
5491
5492
  var schemaSubscribe = import_joi20.default.object({
@@ -5504,6 +5505,7 @@ var schema2 = {
5504
5505
  };
5505
5506
  var schemaSubscription = import_joi20.default.object({
5506
5507
  ...schema2,
5508
+ billingPeriodStart: import_joi20.default.date().optional().allow("", null),
5507
5509
  org: import_joi20.default.string().hex().length(24).required(),
5508
5510
  orgName: import_joi20.default.string().optional().allow("", null),
5509
5511
  currency: import_joi20.default.string().length(3).required(),
@@ -5550,9 +5552,11 @@ function modelSubscription(data) {
5550
5552
  amount: data.amount,
5551
5553
  currency: data.currency,
5552
5554
  billingCycle: data.billingCycle,
5553
- promoCode: data.promoCode,
5555
+ promoCode: data.promoCode ?? "",
5556
+ nextPromoCode: data.nextPromoCode ?? "",
5554
5557
  status: data.status ?? "active",
5555
5558
  retry: data.retry ?? 0,
5559
+ billingPeriodStart: data.billingPeriodStart,
5556
5560
  nextBillingDate: data.nextBillingDate,
5557
5561
  createdAt: data.createdAt ?? /* @__PURE__ */ new Date(),
5558
5562
  updatedAt: data.updatedAt ?? ""
@@ -5864,7 +5868,9 @@ function useSubscriptionRepo() {
5864
5868
  paidSeats: import_joi21.default.number().integer().min(0).optional(),
5865
5869
  amount: import_joi21.default.number().positive().optional().allow(0),
5866
5870
  promoCode: import_joi21.default.string().max(50).optional().allow("", null),
5871
+ nextPromoCode: import_joi21.default.string().max(50).optional().allow("", null),
5867
5872
  status: import_joi21.default.string().valid("active", "due", "overdue", "suspended").optional().allow("", null),
5873
+ billingPeriodStart: import_joi21.default.date().optional().allow("", null),
5868
5874
  nextBillingDate: import_joi21.default.date().optional().allow("", null)
5869
5875
  });
5870
5876
  const { error } = validation.validate(options);
@@ -6020,6 +6026,16 @@ var schemaSubscriptionTransaction = import_joi22.default.object({
6020
6026
  "promo-expired",
6021
6027
  "promo-updated"
6022
6028
  ).required(),
6029
+ metadata: import_joi22.default.object({
6030
+ additionalSeats: import_joi22.default.number().integer().min(1).optional(),
6031
+ seats: import_joi22.default.number().integer().min(0).optional(),
6032
+ paidSeats: import_joi22.default.number().integer().min(0).optional(),
6033
+ plan: import_joi22.default.string().hex().length(24).optional(),
6034
+ promoCode: import_joi22.default.string().optional().allow("", null),
6035
+ nextPromoCode: import_joi22.default.string().optional().allow("", null),
6036
+ billingPeriodStart: import_joi22.default.date().optional().allow("", null),
6037
+ nextBillingDate: import_joi22.default.date().optional().allow("", null)
6038
+ }).optional(),
6023
6039
  description: import_joi22.default.string().optional().allow("", null),
6024
6040
  createdBy: import_joi22.default.string().hex().length(24).required(),
6025
6041
  createdByName: import_joi22.default.string().optional().allow("", null)
@@ -6058,6 +6074,7 @@ function modelSubscriptionTransaction(data) {
6058
6074
  amount: data.amount,
6059
6075
  currency: data.currency,
6060
6076
  type: data.type,
6077
+ metadata: data.metadata ?? {},
6061
6078
  description: data.description ?? "",
6062
6079
  createdBy: data.createdBy,
6063
6080
  createdByName: data.createdByName,
@@ -8563,7 +8580,8 @@ function useSubscriptionService() {
8563
8580
  if (!membership) {
8564
8581
  throw new import_utils44.BadRequestError("User is not a member of the organization.");
8565
8582
  }
8566
- const nextBillingDate = /* @__PURE__ */ new Date();
8583
+ const billingPeriodStart = /* @__PURE__ */ new Date();
8584
+ const nextBillingDate = new Date(billingPeriodStart);
8567
8585
  nextBillingDate.setDate(nextBillingDate.getDate() + 30);
8568
8586
  const { subscriptionAmount, currency } = await computeFee({
8569
8587
  seats: value.seats,
@@ -8588,6 +8606,7 @@ function useSubscriptionService() {
8588
8606
  amount: subscriptionAmount,
8589
8607
  currency,
8590
8608
  billingCycle: plan.billingCycle,
8609
+ billingPeriodStart,
8591
8610
  nextBillingDate,
8592
8611
  promoCode: value.promoCode
8593
8612
  },
@@ -8691,7 +8710,17 @@ function useSubscriptionService() {
8691
8710
  currency,
8692
8711
  subscription: subscription._id?.toString() ?? "",
8693
8712
  createdBy: value.user,
8694
- createdByName: `${userData.firstName} ${userData.lastName}`
8713
+ createdByName: `${userData.firstName} ${userData.lastName}`,
8714
+ metadata: {
8715
+ additionalSeats,
8716
+ seats: value.seats,
8717
+ paidSeats,
8718
+ plan: value.plan ?? "",
8719
+ promoCode: subscription.promoCode ?? "",
8720
+ nextPromoCode: subscription.nextPromoCode ?? "",
8721
+ billingPeriodStart: subscription.billingPeriodStart,
8722
+ nextBillingDate: subscription.nextBillingDate
8723
+ }
8695
8724
  },
8696
8725
  session
8697
8726
  );
@@ -8756,67 +8785,38 @@ function useSubscriptionService() {
8756
8785
  });
8757
8786
  await updateById(
8758
8787
  subscription._id?.toString() ?? "",
8759
- { promoCode: value.promoCode ?? "", amount: subscriptionAmount },
8788
+ { nextPromoCode: value.promoCode ?? "" },
8760
8789
  session
8761
8790
  );
8762
8791
  if (subscription.promoCode) {
8763
8792
  await updatePromoUsageStatusByOrgId(value.org, "inactive", session);
8764
8793
  }
8765
- let description = `Promo code updated to ${value.promoCode ?? "none"}. At the time of update, ${(0, import_utils44.formatNumber)(subscription.paidSeats, {
8766
- decimalPlaces: 0
8767
- })} seat(s) were already included in the subscription.`;
8768
8794
  if (promo && promo._id) {
8769
8795
  await addPromoUsage(
8770
8796
  {
8771
8797
  promo: promo._id,
8772
8798
  org: value.org,
8773
- usedBy: userData.email
8799
+ usedBy: value.user
8774
8800
  },
8775
8801
  session
8776
8802
  );
8777
- const additionalDescription = `${promo.seats ? `Seats beyond ${(0, import_utils44.formatNumber)(promo.seats, {
8778
- decimalPlaces: 0
8779
- })} are charged at the standard rate.` : ""}`;
8780
- if (promo.type === "flat") {
8781
- description += ` ${(0, import_utils44.formatNumber)(promo.flatRate ?? 0, {
8782
- currency,
8783
- useSymbol: true
8784
- })} ${promo.seats ? `limited to ${(0, import_utils44.formatNumber)(promo.seats, {
8785
- decimalPlaces: 0
8786
- })} seat(s)` : " for all seats"}. ${additionalDescription}`;
8787
- }
8788
- if (promo.type === "fixed") {
8789
- description += ` ${(0, import_utils44.formatNumber)(promo.fixedRate ?? 0, {
8790
- currency,
8791
- useSymbol: true
8792
- })} per seat${promo.seats ? `, limited to ${(0, import_utils44.formatNumber)(promo.seats, {
8793
- decimalPlaces: 0
8794
- })} seat(s)` : ""}. ${additionalDescription}`;
8795
- }
8796
- if (promo.type === "volume") {
8797
- if (promo.tiers && promo.tiers.length > 0) {
8798
- const tierDescriptions = promo.tiers.map((tier) => {
8799
- const maxSeatsDesc = tier.maxSeats === 0 ? "and above" : `to ${(0, import_utils44.formatNumber)(tier.maxSeats, { decimalPlaces: 0 })}`;
8800
- return `${(0, import_utils44.formatNumber)(tier.minSeats, {
8801
- decimalPlaces: 0
8802
- })} ${maxSeatsDesc} ${(0, import_utils44.formatNumber)(tier.rate, {
8803
- currency,
8804
- useSymbol: true
8805
- })} per seat`;
8806
- }).join(", ");
8807
- description += ` Volume tiers, ${tierDescriptions}.`;
8808
- }
8809
- }
8810
8803
  }
8811
8804
  await addTransaction(
8812
8805
  {
8813
8806
  type: "promo-updated",
8814
- description,
8807
+ description: `Promo code ${value.promoCode || "removed"} scheduled to take effect on the next billing cycle.`,
8815
8808
  amount: 0,
8816
- currency,
8809
+ currency: subscription.currency,
8817
8810
  subscription: subscription._id?.toString() ?? "",
8818
8811
  createdBy: value.user,
8819
- createdByName: `${userData.firstName} ${userData.lastName}`
8812
+ metadata: {
8813
+ seats: subscription.seats,
8814
+ paidSeats: subscription.paidSeats,
8815
+ promoCode: subscription.promoCode,
8816
+ nextPromoCode: value.promoCode ?? "",
8817
+ billingPeriodStart: subscription.billingPeriodStart,
8818
+ nextBillingDate: subscription.nextBillingDate
8819
+ }
8820
8820
  },
8821
8821
  session
8822
8822
  );
@@ -8976,39 +8976,43 @@ function useSubscriptionService() {
8976
8976
  }
8977
8977
  const ledgerBill = await getByInvoice(invoiceId);
8978
8978
  if (!ledgerBill) {
8979
- throw new import_utils44.BadRequestError(
8980
- "Ledger bill not found for the given invoice ID."
8981
- );
8979
+ throw new import_utils44.BadRequestError("Ledger bill not found.");
8982
8980
  }
8983
8981
  const orgId = String(ledgerBill.org);
8984
- const org = await getOrgById(orgId);
8985
- if (!org) {
8986
- throw new import_utils44.BadRequestError("Organization not found for the ledger bill.");
8987
- }
8988
8982
  const subscription = await getByOrg(orgId);
8989
8983
  if (!subscription) {
8990
- throw new import_utils44.BadRequestError("Subscription not found for the organization.");
8984
+ throw new import_utils44.BadRequestError("Subscription not found.");
8991
8985
  }
8992
8986
  const plan = await getDefaultPlan();
8993
8987
  if (!plan) {
8994
- throw new import_utils44.BadRequestError("Default plan not found.");
8988
+ throw new import_utils44.BadRequestError("Plan not found.");
8995
8989
  }
8996
8990
  const session = import_utils44.useAtlas.getClient()?.startSession();
8997
8991
  if (!session) {
8998
- throw new Error("Unable to start database session.");
8992
+ throw new import_utils44.InternalServerError("Unable to start database session.");
8999
8993
  }
9000
8994
  try {
9001
8995
  session.startTransaction();
9002
- const subscriptionId = String(subscription._id);
9003
- const nextBillingDate = /* @__PURE__ */ new Date();
8996
+ const billingPeriodStart = /* @__PURE__ */ new Date();
8997
+ const nextBillingDate = new Date(billingPeriodStart);
9004
8998
  nextBillingDate.setMonth(nextBillingDate.getMonth() + 1);
8999
+ const effectivePromoCode = subscription.nextPromoCode || subscription.promoCode;
9000
+ const promo = effectivePromoCode ? await getPromoByCode(effectivePromoCode) : null;
9001
+ const monthlyAmount = computeMonthlyAmount(
9002
+ subscription.seats,
9003
+ plan.price,
9004
+ promo
9005
+ );
9005
9006
  await updateById(
9006
- subscriptionId,
9007
+ subscription._id?.toString() ?? "",
9007
9008
  {
9008
9009
  status: "active",
9010
+ billingPeriodStart,
9009
9011
  nextBillingDate,
9010
9012
  paidSeats: subscription.seats,
9011
- amount: plan.price * subscription.seats
9013
+ amount: Math.round(monthlyAmount * 100) / 100,
9014
+ promoCode: effectivePromoCode,
9015
+ nextPromoCode: ""
9012
9016
  },
9013
9017
  session
9014
9018
  );
@@ -9019,14 +9023,7 @@ function useSubscriptionService() {
9019
9023
  return "Successfully processed paid invoice.";
9020
9024
  } catch (error2) {
9021
9025
  await session.abortTransaction();
9022
- import_utils44.logger.log({
9023
- level: "error",
9024
- message: `Failed to process paid invoice ${invoiceId}: ${error2 instanceof Error ? error2.message : String(error2)}`
9025
- });
9026
- if (error2 instanceof import_utils44.AppError) {
9027
- throw error2;
9028
- }
9029
- throw new import_utils44.InternalServerError("Failed to process paid invoice.");
9026
+ throw error2;
9030
9027
  } finally {
9031
9028
  session.endSession();
9032
9029
  }
@@ -9562,6 +9559,7 @@ function useOrgService() {
9562
9559
  paidSeats: value.seats,
9563
9560
  currency: plan.currency,
9564
9561
  billingCycle: plan.billingCycle,
9562
+ billingPeriodStart: currentDate,
9565
9563
  nextBillingDate
9566
9564
  },
9567
9565
  session