@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/CHANGELOG.md +12 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +98 -35
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +98 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
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,7 +6026,17 @@ var schemaSubscriptionTransaction = import_joi22.default.object({
|
|
|
6020
6026
|
"promo-expired",
|
|
6021
6027
|
"promo-updated"
|
|
6022
6028
|
).required(),
|
|
6023
|
-
|
|
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(),
|
|
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)
|
|
6026
6042
|
});
|
|
@@ -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,
|
|
@@ -8425,13 +8442,42 @@ function useSubscriptionService() {
|
|
|
8425
8442
|
case "fixed":
|
|
8426
8443
|
return Math.min(seats, limit) * (promo.fixedRate ?? 0) + Math.max(seats - limit, 0) * planPrice;
|
|
8427
8444
|
case "flat":
|
|
8428
|
-
return (promo.flatRate ?? 0) + Math.max(seats - limit
|
|
8445
|
+
return (promo.flatRate ?? 0) + Math.max(0, seats - limit) * planPrice;
|
|
8429
8446
|
case "volume":
|
|
8430
8447
|
return promo.tiers?.length ? calculateVolumeTierAmount(promo.tiers, 1, seats, planPrice) : planPrice * seats;
|
|
8431
8448
|
default:
|
|
8432
8449
|
return planPrice * seats;
|
|
8433
8450
|
}
|
|
8434
8451
|
}
|
|
8452
|
+
function computeAdditionalSeatAmount(additionalSeats, planPrice, promo, paidSeats) {
|
|
8453
|
+
if (additionalSeats <= 0)
|
|
8454
|
+
return 0;
|
|
8455
|
+
if (!promo) {
|
|
8456
|
+
return additionalSeats * planPrice;
|
|
8457
|
+
}
|
|
8458
|
+
const limit = promo.seats && promo.seats > 0 ? promo.seats : Infinity;
|
|
8459
|
+
switch (promo.type) {
|
|
8460
|
+
case "flat": {
|
|
8461
|
+
const chargeableSeats = Math.max(0, paidSeats + additionalSeats - limit) - Math.max(0, paidSeats - limit);
|
|
8462
|
+
return chargeableSeats * planPrice;
|
|
8463
|
+
}
|
|
8464
|
+
case "fixed": {
|
|
8465
|
+
const promoSeatsLeft = Math.max(0, limit - paidSeats);
|
|
8466
|
+
const promoSeats = Math.min(additionalSeats, promoSeatsLeft);
|
|
8467
|
+
const normalSeats = additionalSeats - promoSeats;
|
|
8468
|
+
return promoSeats * (promo.fixedRate ?? 0) + normalSeats * planPrice;
|
|
8469
|
+
}
|
|
8470
|
+
case "volume":
|
|
8471
|
+
return promo.tiers?.length ? calculateVolumeTierAmount(
|
|
8472
|
+
promo.tiers,
|
|
8473
|
+
paidSeats + 1,
|
|
8474
|
+
additionalSeats,
|
|
8475
|
+
planPrice
|
|
8476
|
+
) : additionalSeats * planPrice;
|
|
8477
|
+
default:
|
|
8478
|
+
return additionalSeats * planPrice;
|
|
8479
|
+
}
|
|
8480
|
+
}
|
|
8435
8481
|
function computeProration(subscription, seats, plan, promo) {
|
|
8436
8482
|
const additionalSeats = Math.max(0, seats - subscription.paidSeats);
|
|
8437
8483
|
if (additionalSeats === 0)
|
|
@@ -8445,10 +8491,11 @@ function useSubscriptionService() {
|
|
|
8445
8491
|
const cycleDays = plan.billingCycle === "yearly" ? 365 : 30;
|
|
8446
8492
|
if (daysRemaining === 0)
|
|
8447
8493
|
return 0;
|
|
8448
|
-
const additionalAmount =
|
|
8494
|
+
const additionalAmount = computeAdditionalSeatAmount(
|
|
8449
8495
|
additionalSeats,
|
|
8450
8496
|
plan.price,
|
|
8451
|
-
promo
|
|
8497
|
+
promo,
|
|
8498
|
+
subscription.paidSeats
|
|
8452
8499
|
);
|
|
8453
8500
|
return additionalAmount / cycleDays * daysRemaining;
|
|
8454
8501
|
}
|
|
@@ -8464,7 +8511,6 @@ function useSubscriptionService() {
|
|
|
8464
8511
|
const isNew = !existingSubscription;
|
|
8465
8512
|
const isPromoChange = !!existingSubscription && value.promoCode !== void 0 && value.promoCode !== existingSubscription.promoCode;
|
|
8466
8513
|
const isSeatIncrease = !!existingSubscription && value.seats > existingSubscription.seats;
|
|
8467
|
-
const isSeatDecrease = !!existingSubscription && value.seats < existingSubscription.seats;
|
|
8468
8514
|
if (isPromoChange && isSeatIncrease) {
|
|
8469
8515
|
throw new import_utils44.BadRequestError(
|
|
8470
8516
|
"Cannot change promo code while increasing seats. Perform actions separately."
|
|
@@ -8534,7 +8580,8 @@ function useSubscriptionService() {
|
|
|
8534
8580
|
if (!membership) {
|
|
8535
8581
|
throw new import_utils44.BadRequestError("User is not a member of the organization.");
|
|
8536
8582
|
}
|
|
8537
|
-
const
|
|
8583
|
+
const billingPeriodStart = /* @__PURE__ */ new Date();
|
|
8584
|
+
const nextBillingDate = new Date(billingPeriodStart);
|
|
8538
8585
|
nextBillingDate.setDate(nextBillingDate.getDate() + 30);
|
|
8539
8586
|
const { subscriptionAmount, currency } = await computeFee({
|
|
8540
8587
|
seats: value.seats,
|
|
@@ -8559,6 +8606,7 @@ function useSubscriptionService() {
|
|
|
8559
8606
|
amount: subscriptionAmount,
|
|
8560
8607
|
currency,
|
|
8561
8608
|
billingCycle: plan.billingCycle,
|
|
8609
|
+
billingPeriodStart,
|
|
8562
8610
|
nextBillingDate,
|
|
8563
8611
|
promoCode: value.promoCode
|
|
8564
8612
|
},
|
|
@@ -8662,7 +8710,17 @@ function useSubscriptionService() {
|
|
|
8662
8710
|
currency,
|
|
8663
8711
|
subscription: subscription._id?.toString() ?? "",
|
|
8664
8712
|
createdBy: value.user,
|
|
8665
|
-
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
|
+
}
|
|
8666
8724
|
},
|
|
8667
8725
|
session
|
|
8668
8726
|
);
|
|
@@ -8727,7 +8785,7 @@ function useSubscriptionService() {
|
|
|
8727
8785
|
});
|
|
8728
8786
|
await updateById(
|
|
8729
8787
|
subscription._id?.toString() ?? "",
|
|
8730
|
-
{
|
|
8788
|
+
{ nextPromoCode: value.promoCode ?? "" },
|
|
8731
8789
|
session
|
|
8732
8790
|
);
|
|
8733
8791
|
if (subscription.promoCode) {
|
|
@@ -8738,7 +8796,7 @@ function useSubscriptionService() {
|
|
|
8738
8796
|
{
|
|
8739
8797
|
promo: promo._id,
|
|
8740
8798
|
org: value.org,
|
|
8741
|
-
usedBy:
|
|
8799
|
+
usedBy: value.user
|
|
8742
8800
|
},
|
|
8743
8801
|
session
|
|
8744
8802
|
);
|
|
@@ -8746,12 +8804,19 @@ function useSubscriptionService() {
|
|
|
8746
8804
|
await addTransaction(
|
|
8747
8805
|
{
|
|
8748
8806
|
type: "promo-updated",
|
|
8749
|
-
description: `
|
|
8807
|
+
description: `Promo code ${value.promoCode || "removed"} scheduled to take effect on the next billing cycle.`,
|
|
8750
8808
|
amount: 0,
|
|
8751
|
-
currency,
|
|
8809
|
+
currency: subscription.currency,
|
|
8752
8810
|
subscription: subscription._id?.toString() ?? "",
|
|
8753
8811
|
createdBy: value.user,
|
|
8754
|
-
|
|
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
|
+
}
|
|
8755
8820
|
},
|
|
8756
8821
|
session
|
|
8757
8822
|
);
|
|
@@ -8911,39 +8976,43 @@ function useSubscriptionService() {
|
|
|
8911
8976
|
}
|
|
8912
8977
|
const ledgerBill = await getByInvoice(invoiceId);
|
|
8913
8978
|
if (!ledgerBill) {
|
|
8914
|
-
throw new import_utils44.BadRequestError(
|
|
8915
|
-
"Ledger bill not found for the given invoice ID."
|
|
8916
|
-
);
|
|
8979
|
+
throw new import_utils44.BadRequestError("Ledger bill not found.");
|
|
8917
8980
|
}
|
|
8918
8981
|
const orgId = String(ledgerBill.org);
|
|
8919
|
-
const org = await getOrgById(orgId);
|
|
8920
|
-
if (!org) {
|
|
8921
|
-
throw new import_utils44.BadRequestError("Organization not found for the ledger bill.");
|
|
8922
|
-
}
|
|
8923
8982
|
const subscription = await getByOrg(orgId);
|
|
8924
8983
|
if (!subscription) {
|
|
8925
|
-
throw new import_utils44.BadRequestError("Subscription not found
|
|
8984
|
+
throw new import_utils44.BadRequestError("Subscription not found.");
|
|
8926
8985
|
}
|
|
8927
8986
|
const plan = await getDefaultPlan();
|
|
8928
8987
|
if (!plan) {
|
|
8929
|
-
throw new import_utils44.BadRequestError("
|
|
8988
|
+
throw new import_utils44.BadRequestError("Plan not found.");
|
|
8930
8989
|
}
|
|
8931
8990
|
const session = import_utils44.useAtlas.getClient()?.startSession();
|
|
8932
8991
|
if (!session) {
|
|
8933
|
-
throw new
|
|
8992
|
+
throw new import_utils44.InternalServerError("Unable to start database session.");
|
|
8934
8993
|
}
|
|
8935
8994
|
try {
|
|
8936
8995
|
session.startTransaction();
|
|
8937
|
-
const
|
|
8938
|
-
const nextBillingDate =
|
|
8996
|
+
const billingPeriodStart = /* @__PURE__ */ new Date();
|
|
8997
|
+
const nextBillingDate = new Date(billingPeriodStart);
|
|
8939
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
|
+
);
|
|
8940
9006
|
await updateById(
|
|
8941
|
-
|
|
9007
|
+
subscription._id?.toString() ?? "",
|
|
8942
9008
|
{
|
|
8943
9009
|
status: "active",
|
|
9010
|
+
billingPeriodStart,
|
|
8944
9011
|
nextBillingDate,
|
|
8945
9012
|
paidSeats: subscription.seats,
|
|
8946
|
-
amount:
|
|
9013
|
+
amount: Math.round(monthlyAmount * 100) / 100,
|
|
9014
|
+
promoCode: effectivePromoCode,
|
|
9015
|
+
nextPromoCode: ""
|
|
8947
9016
|
},
|
|
8948
9017
|
session
|
|
8949
9018
|
);
|
|
@@ -8954,14 +9023,7 @@ function useSubscriptionService() {
|
|
|
8954
9023
|
return "Successfully processed paid invoice.";
|
|
8955
9024
|
} catch (error2) {
|
|
8956
9025
|
await session.abortTransaction();
|
|
8957
|
-
|
|
8958
|
-
level: "error",
|
|
8959
|
-
message: `Failed to process paid invoice ${invoiceId}: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
8960
|
-
});
|
|
8961
|
-
if (error2 instanceof import_utils44.AppError) {
|
|
8962
|
-
throw error2;
|
|
8963
|
-
}
|
|
8964
|
-
throw new import_utils44.InternalServerError("Failed to process paid invoice.");
|
|
9026
|
+
throw error2;
|
|
8965
9027
|
} finally {
|
|
8966
9028
|
session.endSession();
|
|
8967
9029
|
}
|
|
@@ -9497,6 +9559,7 @@ function useOrgService() {
|
|
|
9497
9559
|
paidSeats: value.seats,
|
|
9498
9560
|
currency: plan.currency,
|
|
9499
9561
|
billingCycle: plan.billingCycle,
|
|
9562
|
+
billingPeriodStart: currentDate,
|
|
9500
9563
|
nextBillingDate
|
|
9501
9564
|
},
|
|
9502
9565
|
session
|