@goweekdays/core 2.11.1 → 2.11.3

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.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 2bb4eb2: Add orgName to subscription and refactor service logic
8
+
9
+ ## 2.11.2
10
+
11
+ ### Patch Changes
12
+
13
+ - d27a91f: Fix seat decrease billing logic in subscription service
14
+ - 69837e4: Allow zero amount in subscription schema and fix promo calc
15
+
3
16
  ## 2.11.1
4
17
 
5
18
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -831,6 +831,7 @@ declare function usePlanController(): {
831
831
  type TSubscription = {
832
832
  _id?: ObjectId;
833
833
  org: string | ObjectId;
834
+ orgName?: string;
834
835
  seats: number;
835
836
  paidSeats: number;
836
837
  amount: number;
package/dist/index.js CHANGED
@@ -5446,13 +5446,14 @@ 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
  };
5453
5453
  var schemaSubscription = import_joi20.default.object({
5454
5454
  ...schema2,
5455
5455
  org: import_joi20.default.string().hex().length(24).required(),
5456
+ orgName: import_joi20.default.string().optional().allow("", null),
5456
5457
  currency: import_joi20.default.string().length(3).required(),
5457
5458
  billingCycle: import_joi20.default.string().valid("monthly", "yearly").required()
5458
5459
  });
@@ -5491,6 +5492,7 @@ function modelSubscription(data) {
5491
5492
  return {
5492
5493
  _id: data._id,
5493
5494
  org: data.org,
5495
+ orgName: data.orgName ?? "",
5494
5496
  seats: data.seats,
5495
5497
  paidSeats: data.paidSeats,
5496
5498
  amount: data.amount,
@@ -8286,7 +8288,6 @@ function usePromoController() {
8286
8288
  // src/resources/subscription/subscription.service.ts
8287
8289
  function useSubscriptionService() {
8288
8290
  const {
8289
- getById,
8290
8291
  updateById,
8291
8292
  getByStatus,
8292
8293
  updateStatusById,
@@ -8354,10 +8355,10 @@ function useSubscriptionService() {
8354
8355
  if (promo) {
8355
8356
  switch (promo.type) {
8356
8357
  case "fixed":
8357
- monthlyAmount = Math.max(plan.price - (promo.fixedRate ?? 0), 0) * value.seats;
8358
+ monthlyAmount = Math.max(promo.fixedRate ?? 0, 0) * value.seats;
8358
8359
  break;
8359
8360
  case "flat":
8360
- monthlyAmount = Math.max(plan.price - (promo.flatRate ?? 0) / value.seats, 0) * value.seats;
8361
+ monthlyAmount = Math.max((promo.flatRate ?? 0) / value.seats, 0) * value.seats;
8361
8362
  break;
8362
8363
  case "volume": {
8363
8364
  if (promo.tiers && promo.tiers.length > 0) {
@@ -8419,14 +8420,17 @@ function useSubscriptionService() {
8419
8420
  proratedAmount = dailyRate * daysRemaining;
8420
8421
  }
8421
8422
  }
8423
+ const isDecrease = existingSubscription && value.seats < existingSubscription.paidSeats;
8422
8424
  return {
8423
8425
  // The monthly fee for the next billing cycle
8424
8426
  monthlyAmount: Math.round(monthlyAmount * 100) / 100,
8425
8427
  // The prorated amount to charge now for mid-cycle seat additions
8426
8428
  proratedAmount: Math.round(proratedAmount * 100) / 100,
8427
8429
  // 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,
8430
+ // - Seat increase with proration: existing amount + prorated
8431
+ // - Seat decrease: keep existing amount (decrease takes effect next cycle)
8432
+ // - Same day change or no proration: use monthlyAmount
8433
+ 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
8434
  currency: plan.currency
8431
8435
  };
8432
8436
  }
@@ -8435,34 +8439,38 @@ function useSubscriptionService() {
8435
8439
  if (error) {
8436
8440
  throw new import_utils44.BadRequestError(`Invalid subscription data: ${error.message}`);
8437
8441
  }
8438
- const existingSubscription = await getByOrg(value.org);
8439
- if (existingSubscription) {
8440
- throw new import_utils44.BadRequestError("Organization already has a subscription.");
8441
- }
8442
- const plan = await getPlanById(value.plan);
8443
- if (!plan) {
8444
- throw new import_utils44.BadRequestError("Plan not found.");
8445
- }
8446
- const userData = await getUserById(value.user);
8447
- if (!userData) {
8448
- throw new import_utils44.BadRequestError("User not found.");
8449
- }
8450
- const membership = await getMembershipByApp({
8451
- user: value.user,
8452
- org: value.org,
8453
- app: "org"
8454
- });
8455
- if (!membership) {
8456
- throw new import_utils44.BadRequestError("User is not a member of the organization.");
8457
- }
8458
- const nextBillingDate = /* @__PURE__ */ new Date();
8459
- nextBillingDate.setMonth(nextBillingDate.getMonth() + 1);
8460
8442
  const session = import_utils44.useAtlas.getClient()?.startSession();
8461
8443
  if (!session) {
8462
8444
  throw new import_utils44.InternalServerError("Unable to start database session.");
8463
8445
  }
8464
8446
  try {
8465
8447
  session.startTransaction();
8448
+ const org = await getOrgById(value.org);
8449
+ if (!org) {
8450
+ throw new import_utils44.BadRequestError("Organization not found.");
8451
+ }
8452
+ const existingSubscription = await getByOrg(value.org);
8453
+ if (existingSubscription) {
8454
+ throw new import_utils44.BadRequestError("Organization already has a subscription.");
8455
+ }
8456
+ const plan = await getPlanById(value.plan);
8457
+ if (!plan) {
8458
+ throw new import_utils44.BadRequestError("Plan not found.");
8459
+ }
8460
+ const userData = await getUserById(value.user);
8461
+ if (!userData) {
8462
+ throw new import_utils44.BadRequestError("User not found.");
8463
+ }
8464
+ const membership = await getMembershipByApp({
8465
+ user: value.user,
8466
+ org: value.org,
8467
+ app: "org"
8468
+ });
8469
+ if (!membership) {
8470
+ throw new import_utils44.BadRequestError("User is not a member of the organization.");
8471
+ }
8472
+ const nextBillingDate = /* @__PURE__ */ new Date();
8473
+ nextBillingDate.setMonth(nextBillingDate.getMonth() + 1);
8466
8474
  const { subscriptionAmount, currency } = await computeFee({
8467
8475
  seats: value.seats,
8468
8476
  promoCode: value.promoCode,
@@ -8472,6 +8480,7 @@ function useSubscriptionService() {
8472
8480
  const subId = await _add(
8473
8481
  {
8474
8482
  org: value.org,
8483
+ orgName: org.name,
8475
8484
  seats: value.seats,
8476
8485
  paidSeats: value.seats,
8477
8486
  // Initial seats are considered "paid" for proration purposes
@@ -8516,39 +8525,44 @@ function useSubscriptionService() {
8516
8525
  if (error) {
8517
8526
  throw new import_utils44.BadRequestError(error.message);
8518
8527
  }
8519
- const subscription = await getByOrg(value.org);
8520
- if (!subscription) {
8521
- throw new import_utils44.BadRequestError("Subscription not found");
8522
- }
8523
- if (subscription.seats === value.seats) {
8524
- throw new import_utils44.BadRequestError(
8525
- "Failed to update subscription, no changes detected."
8526
- );
8527
- }
8528
- const userData = await getUserById(value.user);
8529
- if (!userData) {
8530
- throw new import_utils44.BadRequestError("User not found.");
8531
- }
8532
- const membership = await getMembershipByApp({
8533
- user: value.user,
8534
- org: value.org,
8535
- app: "org"
8536
- });
8537
- if (!membership) {
8538
- throw new import_utils44.BadRequestError("User is not a member of the organization.");
8539
- }
8540
- const { subscriptionAmount, proratedAmount, currency } = await computeFee(
8541
- value
8542
- );
8543
8528
  const session = import_utils44.useAtlas.getClient()?.startSession();
8544
8529
  if (!session) {
8545
8530
  throw new import_utils44.InternalServerError("Unable to start database session.");
8546
8531
  }
8547
- const seatIncreased = value.seats > subscription.paidSeats;
8548
- const additionalSeats = value.seats - subscription.paidSeats;
8549
- const paidSeats = seatIncreased ? value.seats : subscription.paidSeats;
8550
8532
  try {
8551
8533
  session.startTransaction();
8534
+ const subscription = await getByOrg(value.org);
8535
+ if (!subscription) {
8536
+ throw new import_utils44.BadRequestError("Subscription not found");
8537
+ }
8538
+ if (subscription.seats === value.seats) {
8539
+ throw new import_utils44.BadRequestError(
8540
+ "Failed to update subscription, no changes detected."
8541
+ );
8542
+ }
8543
+ const userData = await getUserById(value.user);
8544
+ if (!userData) {
8545
+ throw new import_utils44.BadRequestError("User not found.");
8546
+ }
8547
+ const membership = await getMembershipByApp({
8548
+ user: value.user,
8549
+ org: value.org,
8550
+ app: "org"
8551
+ });
8552
+ if (!membership) {
8553
+ throw new import_utils44.BadRequestError("User is not a member of the organization.");
8554
+ }
8555
+ const { subscriptionAmount, proratedAmount, currency } = await computeFee(
8556
+ {
8557
+ seats: value.seats,
8558
+ promoCode: value.promoCode ?? "",
8559
+ plan: value.plan ?? "",
8560
+ org: value.org
8561
+ }
8562
+ );
8563
+ const seatIncreased = value.seats > subscription.paidSeats;
8564
+ const additionalSeats = value.seats - subscription.paidSeats;
8565
+ const paidSeats = seatIncreased ? value.seats : subscription.paidSeats;
8552
8566
  await updateById(
8553
8567
  subscription._id?.toString() ?? "",
8554
8568
  { seats: value.seats, amount: subscriptionAmount, paidSeats },
@@ -8582,53 +8596,53 @@ function useSubscriptionService() {
8582
8596
  if (error) {
8583
8597
  throw new import_utils44.BadRequestError(error.message);
8584
8598
  }
8585
- const subscription = await getByOrg(value.org);
8586
- if (!subscription) {
8587
- throw new import_utils44.BadRequestError("Subscription not found");
8588
- }
8589
- if (subscription.promoCode === value.promoCode) {
8590
- throw new import_utils44.BadRequestError(
8591
- "Failed to update subscription, no changes detected."
8592
- );
8593
- }
8594
- let promo = null;
8595
- if (value.promoCode) {
8596
- promo = await getPromoByCode(value.promoCode);
8597
- if (!promo) {
8598
- throw new import_utils44.BadRequestError("Promo code not found.");
8599
- }
8600
- }
8601
- const userData = await getUserById(value.user);
8602
- if (!userData) {
8603
- throw new import_utils44.BadRequestError("User not found.");
8604
- }
8605
- const membership = await getMembershipByApp({
8606
- user: value.user,
8607
- org: value.org,
8608
- app: "org"
8609
- });
8610
- if (!membership) {
8611
- throw new import_utils44.BadRequestError("User is not a member of the organization.");
8612
- }
8613
- const plan = await getDefaultPlan();
8614
- if (!plan) {
8615
- throw new import_utils44.BadRequestError("Plan not found.");
8616
- }
8617
- const { subscriptionAmount, currency } = await computeFee(
8618
- {
8619
- seats: subscription.seats,
8620
- promoCode: value.promoCode ?? "",
8621
- plan: plan._id?.toString() ?? "",
8622
- org: value.org
8623
- },
8624
- true
8625
- );
8626
8599
  const session = import_utils44.useAtlas.getClient()?.startSession();
8627
8600
  if (!session) {
8628
8601
  throw new import_utils44.InternalServerError("Unable to start database session.");
8629
8602
  }
8630
8603
  try {
8631
8604
  session.startTransaction();
8605
+ const subscription = await getByOrg(value.org);
8606
+ if (!subscription) {
8607
+ throw new import_utils44.BadRequestError("Subscription not found");
8608
+ }
8609
+ if (subscription.promoCode === value.promoCode) {
8610
+ throw new import_utils44.BadRequestError(
8611
+ "Failed to update subscription, no changes detected."
8612
+ );
8613
+ }
8614
+ let promo = null;
8615
+ if (value.promoCode) {
8616
+ promo = await getPromoByCode(value.promoCode);
8617
+ if (!promo) {
8618
+ throw new import_utils44.BadRequestError("Promo code not found.");
8619
+ }
8620
+ }
8621
+ const userData = await getUserById(value.user);
8622
+ if (!userData) {
8623
+ throw new import_utils44.BadRequestError("User not found.");
8624
+ }
8625
+ const membership = await getMembershipByApp({
8626
+ user: value.user,
8627
+ org: value.org,
8628
+ app: "org"
8629
+ });
8630
+ if (!membership) {
8631
+ throw new import_utils44.BadRequestError("User is not a member of the organization.");
8632
+ }
8633
+ const plan = await getDefaultPlan();
8634
+ if (!plan) {
8635
+ throw new import_utils44.BadRequestError("Plan not found.");
8636
+ }
8637
+ const { subscriptionAmount, currency } = await computeFee(
8638
+ {
8639
+ seats: subscription.seats,
8640
+ promoCode: value.promoCode ?? "",
8641
+ plan: plan._id?.toString() ?? "",
8642
+ org: value.org
8643
+ },
8644
+ true
8645
+ );
8632
8646
  await updateById(
8633
8647
  subscription._id?.toString() ?? "",
8634
8648
  { promoCode: value.promoCode ?? "", amount: subscriptionAmount },