@goweekdays/core 2.11.10 → 2.11.11
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 +6 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +76 -138
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +76 -138
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -930,7 +930,7 @@ declare function useSubscriptionService(): {
|
|
|
930
930
|
promoCode?: string;
|
|
931
931
|
org: string;
|
|
932
932
|
plan: string;
|
|
933
|
-
}
|
|
933
|
+
}) => Promise<{
|
|
934
934
|
monthlyAmount: number;
|
|
935
935
|
proratedAmount: number;
|
|
936
936
|
subscriptionAmount: number;
|
package/dist/index.js
CHANGED
|
@@ -8417,147 +8417,85 @@ function useSubscriptionService() {
|
|
|
8417
8417
|
}
|
|
8418
8418
|
return total;
|
|
8419
8419
|
}
|
|
8420
|
-
|
|
8420
|
+
function computeMonthlyAmount(seats, planPrice, promo) {
|
|
8421
|
+
if (!promo)
|
|
8422
|
+
return planPrice * seats;
|
|
8423
|
+
const limit = promo.seats && promo.seats > 0 ? promo.seats : Infinity;
|
|
8424
|
+
switch (promo.type) {
|
|
8425
|
+
case "fixed":
|
|
8426
|
+
return Math.min(seats, limit) * (promo.fixedRate ?? 0) + Math.max(seats - limit, 0) * planPrice;
|
|
8427
|
+
case "flat":
|
|
8428
|
+
return (promo.flatRate ?? 0) + Math.max(seats - limit, 0) * planPrice;
|
|
8429
|
+
case "volume":
|
|
8430
|
+
return promo.tiers?.length ? calculateVolumeTierAmount(promo.tiers, 1, seats, planPrice) : planPrice * seats;
|
|
8431
|
+
default:
|
|
8432
|
+
return planPrice * seats;
|
|
8433
|
+
}
|
|
8434
|
+
}
|
|
8435
|
+
function computeProration(subscription, seats, plan, promo) {
|
|
8436
|
+
const additionalSeats = Math.max(0, seats - subscription.paidSeats);
|
|
8437
|
+
if (additionalSeats === 0)
|
|
8438
|
+
return 0;
|
|
8439
|
+
const today = /* @__PURE__ */ new Date();
|
|
8440
|
+
const nextBilling = new Date(subscription.nextBillingDate);
|
|
8441
|
+
const daysRemaining = Math.max(
|
|
8442
|
+
0,
|
|
8443
|
+
(nextBilling.getTime() - today.getTime()) / 864e5
|
|
8444
|
+
);
|
|
8445
|
+
const cycleDays = plan.billingCycle === "yearly" ? 365 : 30;
|
|
8446
|
+
if (daysRemaining === 0)
|
|
8447
|
+
return 0;
|
|
8448
|
+
const additionalAmount = computeMonthlyAmount(
|
|
8449
|
+
additionalSeats,
|
|
8450
|
+
plan.price,
|
|
8451
|
+
promo
|
|
8452
|
+
);
|
|
8453
|
+
return additionalAmount / cycleDays * daysRemaining;
|
|
8454
|
+
}
|
|
8455
|
+
async function computeFee(value) {
|
|
8421
8456
|
const { error } = schemaSubscriptionCompute.validate(value);
|
|
8422
8457
|
if (error) {
|
|
8423
8458
|
throw new import_utils44.BadRequestError(`Invalid subscription data: ${error.message}`);
|
|
8424
8459
|
}
|
|
8425
8460
|
const existingSubscription = await getByOrg(value.org);
|
|
8426
8461
|
const plan = await getPlanById(value.plan);
|
|
8427
|
-
if (!plan)
|
|
8462
|
+
if (!plan)
|
|
8428
8463
|
throw new import_utils44.BadRequestError("Plan not found.");
|
|
8464
|
+
const isNew = !existingSubscription;
|
|
8465
|
+
const isPromoChange = !!existingSubscription && value.promoCode !== void 0 && value.promoCode !== existingSubscription.promoCode;
|
|
8466
|
+
const isSeatIncrease = !!existingSubscription && value.seats > existingSubscription.seats;
|
|
8467
|
+
const isSeatDecrease = !!existingSubscription && value.seats < existingSubscription.seats;
|
|
8468
|
+
if (isPromoChange && isSeatIncrease) {
|
|
8469
|
+
throw new import_utils44.BadRequestError(
|
|
8470
|
+
"Cannot change promo code while increasing seats. Perform actions separately."
|
|
8471
|
+
);
|
|
8429
8472
|
}
|
|
8430
|
-
const
|
|
8431
|
-
|
|
8432
|
-
|
|
8433
|
-
}
|
|
8434
|
-
let promo = null;
|
|
8435
|
-
const promoCode = value.promoCode;
|
|
8436
|
-
if (promoCode) {
|
|
8437
|
-
promo = await getPromoByCode(promoCode);
|
|
8438
|
-
if (!promo) {
|
|
8439
|
-
throw new import_utils44.BadRequestError("Promo code not found.");
|
|
8440
|
-
}
|
|
8441
|
-
if (promo.usage && promo.usage > 0) {
|
|
8442
|
-
const currentUsageCount = await countByPromoId(
|
|
8443
|
-
promo._id?.toString() ?? ""
|
|
8444
|
-
);
|
|
8445
|
-
const isAlreadyUsingPromo = existingSubscription?.promoCode === promoCode;
|
|
8446
|
-
if (!isAlreadyUsingPromo && currentUsageCount >= promo.usage) {
|
|
8447
|
-
throw new import_utils44.BadRequestError(
|
|
8448
|
-
"Promo code has reached its maximum usage limit."
|
|
8449
|
-
);
|
|
8450
|
-
}
|
|
8451
|
-
}
|
|
8452
|
-
}
|
|
8453
|
-
let monthlyAmount = plan.price * value.seats;
|
|
8454
|
-
if (promo) {
|
|
8455
|
-
const promoSeatLimit = promo.seats && promo.seats > 0 ? promo.seats : Infinity;
|
|
8456
|
-
switch (promo.type) {
|
|
8457
|
-
case "fixed": {
|
|
8458
|
-
const promoSeats = Math.min(value.seats, promoSeatLimit);
|
|
8459
|
-
const standardSeats = Math.max(value.seats - promoSeatLimit, 0);
|
|
8460
|
-
monthlyAmount = Math.max(promo.fixedRate ?? 0, 0) * promoSeats + plan.price * standardSeats;
|
|
8461
|
-
break;
|
|
8462
|
-
}
|
|
8463
|
-
case "flat": {
|
|
8464
|
-
const standardSeats = Math.max(value.seats - promoSeatLimit, 0);
|
|
8465
|
-
monthlyAmount = (promo.flatRate ?? 0) + plan.price * standardSeats;
|
|
8466
|
-
break;
|
|
8467
|
-
}
|
|
8468
|
-
case "volume": {
|
|
8469
|
-
if (promo.tiers && promo.tiers.length > 0) {
|
|
8470
|
-
monthlyAmount = calculateVolumeTierAmount(
|
|
8471
|
-
promo.tiers,
|
|
8472
|
-
1,
|
|
8473
|
-
value.seats,
|
|
8474
|
-
plan.price
|
|
8475
|
-
);
|
|
8476
|
-
}
|
|
8477
|
-
break;
|
|
8478
|
-
}
|
|
8479
|
-
default:
|
|
8480
|
-
monthlyAmount = plan.price * value.seats;
|
|
8481
|
-
}
|
|
8482
|
-
}
|
|
8473
|
+
const effectivePromoCode = isPromoChange ? value.promoCode : existingSubscription?.promoCode;
|
|
8474
|
+
const promo = effectivePromoCode ? await getPromoByCode(effectivePromoCode) : null;
|
|
8475
|
+
const monthlyAmount = computeMonthlyAmount(value.seats, plan.price, promo);
|
|
8483
8476
|
let proratedAmount = 0;
|
|
8484
|
-
|
|
8485
|
-
|
|
8486
|
-
|
|
8487
|
-
|
|
8488
|
-
|
|
8489
|
-
|
|
8490
|
-
daysRemaining = Math.max(0, Math.ceil(timeDiff / (1e3 * 60 * 60 * 24)));
|
|
8491
|
-
if (plan.billingCycle === "yearly") {
|
|
8492
|
-
totalDaysInCycle = 365;
|
|
8493
|
-
}
|
|
8494
|
-
daysRemaining = Math.min(daysRemaining, totalDaysInCycle);
|
|
8495
|
-
const daysElapsed = totalDaysInCycle - daysRemaining;
|
|
8496
|
-
const additionalSeats = Math.max(
|
|
8497
|
-
0,
|
|
8498
|
-
value.seats - existingSubscription.paidSeats
|
|
8477
|
+
if (existingSubscription && isSeatIncrease && !isPromoChange) {
|
|
8478
|
+
proratedAmount = computeProration(
|
|
8479
|
+
existingSubscription,
|
|
8480
|
+
value.seats,
|
|
8481
|
+
plan,
|
|
8482
|
+
promo
|
|
8499
8483
|
);
|
|
8500
|
-
if (additionalSeats > 0 && daysRemaining > 0 && daysElapsed > 0 && !skipProration) {
|
|
8501
|
-
let additionalSeatsAmount = 0;
|
|
8502
|
-
if (promo?.type === "volume" && promo.tiers && promo.tiers.length > 0) {
|
|
8503
|
-
additionalSeatsAmount = calculateVolumeTierAmount(
|
|
8504
|
-
promo.tiers,
|
|
8505
|
-
existingSubscription.paidSeats + 1,
|
|
8506
|
-
additionalSeats,
|
|
8507
|
-
plan.price
|
|
8508
|
-
);
|
|
8509
|
-
} else if (promo?.type === "fixed") {
|
|
8510
|
-
const promoSeatLimit = promo.seats && promo.seats > 0 ? promo.seats : Infinity;
|
|
8511
|
-
const startSeat = existingSubscription.paidSeats + 1;
|
|
8512
|
-
const endSeat = value.seats;
|
|
8513
|
-
const promoSeatsInRange = Math.max(
|
|
8514
|
-
0,
|
|
8515
|
-
Math.min(promoSeatLimit, endSeat) - startSeat + 1
|
|
8516
|
-
);
|
|
8517
|
-
const standardSeatsInRange = Math.max(
|
|
8518
|
-
0,
|
|
8519
|
-
additionalSeats - promoSeatsInRange
|
|
8520
|
-
);
|
|
8521
|
-
additionalSeatsAmount = (promo.fixedRate ?? 0) * promoSeatsInRange + plan.price * standardSeatsInRange;
|
|
8522
|
-
} else if (promo?.type === "flat") {
|
|
8523
|
-
const promoSeatLimit = promo.seats && promo.seats > 0 ? promo.seats : Infinity;
|
|
8524
|
-
const startSeat = existingSubscription.paidSeats + 1;
|
|
8525
|
-
const endSeat = value.seats;
|
|
8526
|
-
if (startSeat > promoSeatLimit) {
|
|
8527
|
-
additionalSeatsAmount = plan.price * additionalSeats;
|
|
8528
|
-
} else {
|
|
8529
|
-
const seatsStillCoveredByFlat = Math.max(
|
|
8530
|
-
0,
|
|
8531
|
-
Math.min(promoSeatLimit, endSeat) - startSeat + 1
|
|
8532
|
-
);
|
|
8533
|
-
const seatsAtPlanPrice = additionalSeats - seatsStillCoveredByFlat;
|
|
8534
|
-
additionalSeatsAmount = plan.price * seatsAtPlanPrice;
|
|
8535
|
-
}
|
|
8536
|
-
} else {
|
|
8537
|
-
additionalSeatsAmount = plan.price * additionalSeats;
|
|
8538
|
-
}
|
|
8539
|
-
const dailyRate = additionalSeatsAmount / totalDaysInCycle;
|
|
8540
|
-
proratedAmount = dailyRate * daysRemaining;
|
|
8541
|
-
}
|
|
8542
8484
|
}
|
|
8543
|
-
const isIncrease = existingSubscription && value.seats > existingSubscription.paidSeats;
|
|
8544
8485
|
let subscriptionAmount;
|
|
8545
|
-
if (
|
|
8546
|
-
subscriptionAmount =
|
|
8547
|
-
} else if (
|
|
8548
|
-
subscriptionAmount =
|
|
8549
|
-
} else if (
|
|
8550
|
-
subscriptionAmount =
|
|
8486
|
+
if (isNew) {
|
|
8487
|
+
subscriptionAmount = monthlyAmount;
|
|
8488
|
+
} else if (isPromoChange) {
|
|
8489
|
+
subscriptionAmount = monthlyAmount;
|
|
8490
|
+
} else if (isSeatIncrease) {
|
|
8491
|
+
subscriptionAmount = existingSubscription.amount + proratedAmount;
|
|
8551
8492
|
} else {
|
|
8552
|
-
subscriptionAmount =
|
|
8493
|
+
subscriptionAmount = existingSubscription.amount;
|
|
8553
8494
|
}
|
|
8554
8495
|
return {
|
|
8555
|
-
// The monthly fee for the next billing cycle
|
|
8556
8496
|
monthlyAmount: Math.round(monthlyAmount * 100) / 100,
|
|
8557
|
-
// The prorated amount to charge now for mid-cycle seat additions
|
|
8558
8497
|
proratedAmount: Math.round(proratedAmount * 100) / 100,
|
|
8559
|
-
|
|
8560
|
-
subscriptionAmount,
|
|
8498
|
+
subscriptionAmount: Math.round(subscriptionAmount * 100) / 100,
|
|
8561
8499
|
currency: plan.currency
|
|
8562
8500
|
};
|
|
8563
8501
|
}
|
|
@@ -8699,20 +8637,23 @@ function useSubscriptionService() {
|
|
|
8699
8637
|
const { subscriptionAmount, proratedAmount, currency } = await computeFee(
|
|
8700
8638
|
{
|
|
8701
8639
|
seats: value.seats,
|
|
8702
|
-
promoCode: value.promoCode ?? "",
|
|
8703
8640
|
plan: value.plan ?? "",
|
|
8704
8641
|
org: value.org
|
|
8705
8642
|
}
|
|
8706
8643
|
);
|
|
8707
8644
|
const seatIncreased = value.seats > subscription.paidSeats;
|
|
8708
|
-
const additionalSeats = value.seats - subscription.paidSeats;
|
|
8709
|
-
const paidSeats = seatIncreased ?
|
|
8645
|
+
const additionalSeats = seatIncreased ? value.seats - subscription.paidSeats : 0;
|
|
8646
|
+
const paidSeats = seatIncreased ? subscription.paidSeats + additionalSeats : subscription.paidSeats;
|
|
8710
8647
|
await updateById(
|
|
8711
8648
|
subscription._id?.toString() ?? "",
|
|
8712
|
-
{
|
|
8649
|
+
{
|
|
8650
|
+
seats: value.seats,
|
|
8651
|
+
amount: subscriptionAmount,
|
|
8652
|
+
paidSeats
|
|
8653
|
+
},
|
|
8713
8654
|
session
|
|
8714
8655
|
);
|
|
8715
|
-
if (seatIncreased) {
|
|
8656
|
+
if (seatIncreased && proratedAmount > 0) {
|
|
8716
8657
|
await addTransaction(
|
|
8717
8658
|
{
|
|
8718
8659
|
type: "add-seat",
|
|
@@ -8778,15 +8719,12 @@ function useSubscriptionService() {
|
|
|
8778
8719
|
if (!plan) {
|
|
8779
8720
|
throw new import_utils44.BadRequestError("Plan not found.");
|
|
8780
8721
|
}
|
|
8781
|
-
const { subscriptionAmount, currency } = await computeFee(
|
|
8782
|
-
|
|
8783
|
-
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
|
|
8787
|
-
},
|
|
8788
|
-
true
|
|
8789
|
-
);
|
|
8722
|
+
const { subscriptionAmount, currency } = await computeFee({
|
|
8723
|
+
seats: subscription.seats,
|
|
8724
|
+
promoCode: value.promoCode ?? "",
|
|
8725
|
+
plan: plan._id?.toString() ?? "",
|
|
8726
|
+
org: value.org
|
|
8727
|
+
});
|
|
8790
8728
|
await updateById(
|
|
8791
8729
|
subscription._id?.toString() ?? "",
|
|
8792
8730
|
{ promoCode: value.promoCode ?? "", amount: subscriptionAmount },
|