@goweekdays/core 0.0.17 → 0.0.18

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.js CHANGED
@@ -16634,7 +16634,7 @@ function useXenditService() {
16634
16634
  if (error instanceof import_utils36.AppError) {
16635
16635
  throw error;
16636
16636
  }
16637
- throw new import_utils36.BadRequestError("Failed to initiate payment request.");
16637
+ throw new import_utils36.BadRequestError(error.response.data.message);
16638
16638
  }
16639
16639
  }
16640
16640
  async function getPaymentMethodById(id) {
@@ -20077,14 +20077,49 @@ function useSubscriptionRepo() {
20077
20077
  throw new import_utils55.BadRequestError("Failed to update subscription status.");
20078
20078
  }
20079
20079
  }
20080
- async function updateSeatsById({ _id, currentSeats, maxSeats, paidSeats, amount, promoCode } = {}, session) {
20080
+ async function updatePromoCodeById(_id, promoCode, session) {
20081
+ const schema3 = import_joi13.default.object({
20082
+ _id: import_joi13.default.string().hex().required(),
20083
+ promoCode: import_joi13.default.string().optional().allow("", null)
20084
+ });
20085
+ const { error } = schema3.validate({ _id, promoCode });
20086
+ if (error) {
20087
+ throw new import_utils55.BadRequestError(error.message);
20088
+ }
20089
+ try {
20090
+ _id = new import_mongodb24.ObjectId(_id);
20091
+ } catch (error2) {
20092
+ throw new import_utils55.BadRequestError("Invalid ID.");
20093
+ }
20094
+ try {
20095
+ return await collection.updateOne(
20096
+ { _id },
20097
+ {
20098
+ $set: { promoCode }
20099
+ },
20100
+ { session }
20101
+ );
20102
+ } catch (error2) {
20103
+ throw new import_utils55.BadRequestError("Failed to update subscription promo code.");
20104
+ }
20105
+ }
20106
+ async function updateSeatsById({
20107
+ _id,
20108
+ currentSeats,
20109
+ maxSeats,
20110
+ paidSeats,
20111
+ amount,
20112
+ status,
20113
+ nextBillingDate
20114
+ } = {}, session) {
20081
20115
  const schema3 = import_joi13.default.object({
20082
20116
  _id: import_joi13.default.string().hex().required(),
20083
20117
  currentSeats: import_joi13.default.number().required().min(1),
20084
20118
  maxSeats: import_joi13.default.number().required().min(0),
20085
20119
  paidSeats: import_joi13.default.number().optional().min(0),
20086
20120
  amount: import_joi13.default.number().required().min(0),
20087
- promoCode: import_joi13.default.string().optional().allow("", null)
20121
+ status: import_joi13.default.string().optional().allow("", null),
20122
+ nextBillingDate: import_joi13.default.date().optional().allow("", null)
20088
20123
  });
20089
20124
  const { error } = schema3.validate({
20090
20125
  _id,
@@ -20092,7 +20127,8 @@ function useSubscriptionRepo() {
20092
20127
  maxSeats,
20093
20128
  paidSeats,
20094
20129
  amount,
20095
- promoCode
20130
+ status,
20131
+ nextBillingDate
20096
20132
  });
20097
20133
  if (error) {
20098
20134
  throw new import_utils55.BadRequestError(error.message);
@@ -20111,8 +20147,11 @@ function useSubscriptionRepo() {
20111
20147
  if (paidSeats) {
20112
20148
  data.paidSeats = paidSeats;
20113
20149
  }
20114
- if (promoCode) {
20115
- data.promoCode = promoCode;
20150
+ if (status) {
20151
+ data.status = status;
20152
+ }
20153
+ if (nextBillingDate) {
20154
+ data.nextBillingDate = new Date(nextBillingDate);
20116
20155
  }
20117
20156
  try {
20118
20157
  return await collection.updateOne(
@@ -20170,7 +20209,8 @@ function useSubscriptionRepo() {
20170
20209
  markSubscriptionAsCanceled,
20171
20210
  updateSeatsById,
20172
20211
  updateMaxSeatsById,
20173
- updateStatusById
20212
+ updateStatusById,
20213
+ updatePromoCodeById
20174
20214
  };
20175
20215
  }
20176
20216
 
@@ -21269,6 +21309,7 @@ function usePriceRepo() {
21269
21309
  }
21270
21310
 
21271
21311
  // src/services/subscription.service.ts
21312
+ var import_mongodb35 = require("mongodb");
21272
21313
  function useSubscriptionService() {
21273
21314
  const {
21274
21315
  getByUserId: _getByUserId,
@@ -21287,7 +21328,8 @@ function useSubscriptionService() {
21287
21328
  getSubscription,
21288
21329
  pay,
21289
21330
  getCustomerById,
21290
- getPaymentMethodById
21331
+ getPaymentMethodById,
21332
+ createCustomer
21291
21333
  } = useXenditService();
21292
21334
  const { add: addOrg } = useOrgRepo();
21293
21335
  const { add: addAddress } = useAddressRepo();
@@ -21302,23 +21344,10 @@ function useSubscriptionService() {
21302
21344
  const { add: addPayment } = usePaymentRepo();
21303
21345
  const { getByType, incrementByType } = useCounterRepo();
21304
21346
  const { getByNameType } = usePriceRepo();
21305
- function computeTieredInvoice(seats, tiers, nextBillingDate) {
21347
+ function calculateTieredPricing(seats, tiers, prorationFactor = 1) {
21306
21348
  let totalCost = 0;
21349
+ let nonProratedCost = 0;
21307
21350
  let items = [];
21308
- let prorationFactor = 1;
21309
- if (nextBillingDate) {
21310
- const totalDaysInMonth = new Date(
21311
- nextBillingDate.getFullYear(),
21312
- nextBillingDate.getMonth() + 1,
21313
- 0
21314
- ).getDate();
21315
- const remainingDays = Math.floor(
21316
- (nextBillingDate.getTime() - (/* @__PURE__ */ new Date()).getTime()) / (1e3 * 60 * 60 * 24)
21317
- );
21318
- if (remainingDays > 1) {
21319
- prorationFactor = remainingDays / totalDaysInMonth;
21320
- }
21321
- }
21322
21351
  for (let i = 0; i < tiers.length; i++) {
21323
21352
  let { min, max, price } = tiers[i];
21324
21353
  if (max === 0)
@@ -21327,20 +21356,23 @@ function useSubscriptionService() {
21327
21356
  let seatsInTier = Math.min(seats, max) - min + 1;
21328
21357
  let tierCost = seatsInTier * price * prorationFactor;
21329
21358
  totalCost += tierCost;
21359
+ nonProratedCost += seatsInTier * price;
21330
21360
  items.push({
21331
21361
  description: `GoWeekdays Subscription - ${seatsInTier} seats @ ${price} per seat`,
21332
21362
  unitPrice: price,
21333
21363
  quantity: seatsInTier,
21334
21364
  seats: seatsInTier,
21335
- total: tierCost,
21336
- prorationFactor
21337
- // Include proration info for transparency
21365
+ total: formatAmount(tierCost)
21338
21366
  });
21339
21367
  }
21340
21368
  if (seats <= max)
21341
21369
  break;
21342
21370
  }
21343
- return { totalCost, items };
21371
+ return {
21372
+ totalCost: formatAmount(totalCost),
21373
+ items,
21374
+ nonProratedCost: formatAmount(nonProratedCost)
21375
+ };
21344
21376
  }
21345
21377
  async function createOrgSubscription(value) {
21346
21378
  const session = import_utils69.useAtlas.getClient()?.startSession();
@@ -21350,10 +21382,32 @@ function useSubscriptionService() {
21350
21382
  if (!_user) {
21351
21383
  throw new import_utils69.BadRequestError("User not found.");
21352
21384
  }
21353
- const org = await addOrg(value.organization, session);
21385
+ value.organization._id = new import_mongodb35.ObjectId();
21386
+ await addOrg(value.organization, session);
21387
+ console.log("value.organization._id", value.organization._id);
21388
+ const customerData = {
21389
+ reference_id: value.organization._id.toString(),
21390
+ type: value.organization.type === "business" ? "BUSINESS" : "INDIVIDUAL",
21391
+ email: value.organization.email
21392
+ };
21393
+ if (value.organization.type === "business") {
21394
+ customerData.business_detail = {
21395
+ business_name: value.organization.busInst ?? value.organization.name,
21396
+ business_type: "SOLE_PROPRIETOR"
21397
+ };
21398
+ }
21399
+ if (value.organization.type === "personal") {
21400
+ customerData.individual_detail = {
21401
+ given_names: value.organization.name
21402
+ };
21403
+ }
21404
+ const { id: customerId } = await createCustomer(customerData);
21405
+ if (!customerId) {
21406
+ throw new import_utils69.BadRequestError("Failed to create customer.");
21407
+ }
21354
21408
  const role = await addRole(
21355
21409
  {
21356
- org,
21410
+ org: value.organization._id.toString(),
21357
21411
  name: "owner",
21358
21412
  permissions: ["*"],
21359
21413
  type: "organization",
@@ -21363,7 +21417,7 @@ function useSubscriptionService() {
21363
21417
  );
21364
21418
  await addMember(
21365
21419
  {
21366
- org: org.toJSON(),
21420
+ org: value.organization._id.toString(),
21367
21421
  orgName: value.organization.name,
21368
21422
  user: value.user,
21369
21423
  name: `${_user.firstName} ${_user.lastName}`,
@@ -21374,15 +21428,19 @@ function useSubscriptionService() {
21374
21428
  );
21375
21429
  if (!_user.defaultOrg) {
21376
21430
  await updateUserFieldById(
21377
- { _id: value.user, field: "defaultOrg", value: org.toString() },
21431
+ {
21432
+ _id: value.user,
21433
+ field: "defaultOrg",
21434
+ value: value.organization._id.toString()
21435
+ },
21378
21436
  session
21379
21437
  );
21380
21438
  }
21381
- const customer = await getCustomerById(value.customer_id);
21439
+ const customer = await getCustomerById(customerId);
21382
21440
  if (!customer.id) {
21383
21441
  throw new import_utils69.BadRequestError("Xendit customer account required.");
21384
21442
  }
21385
- value.billingAddress.org = org;
21443
+ value.billingAddress.org = value.organization._id.toString();
21386
21444
  await addAddress(value.billingAddress, session);
21387
21445
  const description = "GoWeekdays Organization Monthly Subscription.";
21388
21446
  if (value.promoCode) {
@@ -21418,7 +21476,7 @@ function useSubscriptionService() {
21418
21476
  });
21419
21477
  }
21420
21478
  if (promoCode && promoCode.type === "tiered" && promoCode.tiers && promoCode.tiers.length) {
21421
- const computedTieredInvoice = computeTieredInvoice(
21479
+ const computedTieredInvoice = calculateTieredPricing(
21422
21480
  value.seats,
21423
21481
  promoCode.tiers
21424
21482
  );
@@ -21431,8 +21489,8 @@ function useSubscriptionService() {
21431
21489
  }
21432
21490
  const subscription = await add(
21433
21491
  {
21434
- org: org.toString(),
21435
- customerId: value.customer_id,
21492
+ org: value.organization._id.toString(),
21493
+ customerId,
21436
21494
  paymentMethodId: value.payment_method_id,
21437
21495
  amount,
21438
21496
  currency: value.currency,
@@ -21462,9 +21520,10 @@ function useSubscriptionService() {
21462
21520
  amount,
21463
21521
  dueDate: /* @__PURE__ */ new Date(),
21464
21522
  metadata: {
21465
- orgId: org.toString(),
21523
+ orgId: value.organization._id.toString(),
21466
21524
  currency: value.currency,
21467
- subscriptionId: subscription.toString()
21525
+ subscriptionId: subscription.toString(),
21526
+ description: `Initial invoice for ${value.seats.toLocaleString()} seats`
21468
21527
  },
21469
21528
  status: "paid",
21470
21529
  items: items && items.length ? items : [
@@ -21482,7 +21541,7 @@ function useSubscriptionService() {
21482
21541
  let payment = {};
21483
21542
  if (amount) {
21484
21543
  payment = await pay({
21485
- customer_id: value.customer_id,
21544
+ customer_id: customerId,
21486
21545
  payment_method_id: value.payment_method_id,
21487
21546
  amount,
21488
21547
  currency: value.currency,
@@ -21497,7 +21556,7 @@ function useSubscriptionService() {
21497
21556
  status: "completed",
21498
21557
  metadata: {
21499
21558
  userId: value.user,
21500
- orgId: org.toString(),
21559
+ orgId: value.organization._id.toString(),
21501
21560
  currency: value.currency,
21502
21561
  paymentMethod: value.payment_method_channel,
21503
21562
  paymentMethodType: value.payment_method_type,
@@ -21511,7 +21570,7 @@ function useSubscriptionService() {
21511
21570
  await session?.commitTransaction();
21512
21571
  return {
21513
21572
  message: "Subscription created successfully.",
21514
- data: { org: org.toString() }
21573
+ data: { org: value.organization._id.toString() }
21515
21574
  };
21516
21575
  } catch (error) {
21517
21576
  await session?.abortTransaction();
@@ -21717,7 +21776,7 @@ function useSubscriptionService() {
21717
21776
  });
21718
21777
  }
21719
21778
  if (code && promoCode && promoCode.type === "tiered" && promoCode.tiers && promoCode.tiers.length) {
21720
- const computedTieredInvoice = computeTieredInvoice(
21779
+ const computedTieredInvoice = calculateTieredPricing(
21721
21780
  chargeableSeats,
21722
21781
  promoCode.tiers
21723
21782
  );
@@ -21878,11 +21937,91 @@ function useSubscriptionService() {
21878
21937
  const prorationFactor = remainingDays / totalDaysInMonth;
21879
21938
  return remainingDays > 1 ? Number((seats * price * prorationFactor).toFixed(2)) : Number((seats * price).toFixed(2));
21880
21939
  }
21940
+ function formatAmount(amount) {
21941
+ const validation = import_joi17.default.object({
21942
+ amount: import_joi17.default.number().required()
21943
+ });
21944
+ const { error } = validation.validate({ amount });
21945
+ if (error) {
21946
+ throw new import_utils69.BadRequestError(error.message);
21947
+ }
21948
+ return Number(amount.toFixed(2));
21949
+ }
21950
+ function calculateAmountWithPromoCode(subscription, price, seats, prorationFactor = 1, promoCode) {
21951
+ let amount = 0;
21952
+ let newMonthSubscriptionFee = 0;
21953
+ let perSeatPrice = price.value;
21954
+ let items = [];
21955
+ if (!subscription.nextBillingDate) {
21956
+ throw new import_utils69.BadRequestError("Next billing date not found.");
21957
+ }
21958
+ if (promoCode) {
21959
+ if (promoCode.type === "fixed" && promoCode.fixed_rate !== void 0 && promoCode.fixed_rate >= 0) {
21960
+ perSeatPrice = promoCode.fixed_rate;
21961
+ amount = formatAmount(perSeatPrice * seats * prorationFactor);
21962
+ newMonthSubscriptionFee = formatAmount(perSeatPrice * seats);
21963
+ items = [
21964
+ {
21965
+ description: `GoWeekdays Subscription - ${seats} seats @ ${perSeatPrice} per seat`,
21966
+ unitPrice: perSeatPrice,
21967
+ quantity: 1,
21968
+ seats,
21969
+ total: amount
21970
+ }
21971
+ ];
21972
+ } else if (promoCode.type === "tiered" && promoCode.tiers && promoCode.tiers.length > 0) {
21973
+ const computedTieredInvoice = calculateTieredPricing(
21974
+ seats,
21975
+ promoCode.tiers,
21976
+ prorationFactor
21977
+ );
21978
+ amount = computedTieredInvoice.totalCost;
21979
+ newMonthSubscriptionFee = computedTieredInvoice.nonProratedCost;
21980
+ items = computedTieredInvoice.items;
21981
+ }
21982
+ }
21983
+ return {
21984
+ newMonthSubscriptionFee,
21985
+ amount,
21986
+ items
21987
+ };
21988
+ }
21989
+ function calculateProration(billingDate) {
21990
+ if (!billingDate) {
21991
+ throw new import_utils69.BadRequestError("Billing date is required.");
21992
+ }
21993
+ const nextBillingDate = billingDate;
21994
+ const totalDaysInMonth = new Date(
21995
+ nextBillingDate.getFullYear(),
21996
+ nextBillingDate.getMonth() + 1,
21997
+ 0
21998
+ ).getDate();
21999
+ const currentDate = /* @__PURE__ */ new Date();
22000
+ const remainingDays = Math.floor(
22001
+ (nextBillingDate.getTime() - currentDate.getTime()) / (1e3 * 60 * 60 * 24)
22002
+ );
22003
+ return remainingDays > 1 ? remainingDays / totalDaysInMonth : 1;
22004
+ }
22005
+ async function generateInvoiceNumber() {
22006
+ try {
22007
+ const invoiceCounter = await getByType("invoice");
22008
+ if (!invoiceCounter) {
22009
+ throw new import_utils69.BadRequestError("Failed to fetch invoice counter.");
22010
+ }
22011
+ const date = /* @__PURE__ */ new Date();
22012
+ const mm = String(date.getMonth() + 1).padStart(2, "0");
22013
+ const dd = String(date.getDate()).padStart(2, "0");
22014
+ const yyyy = date.getFullYear();
22015
+ const formattedDate = `${mm}-${dd}-${yyyy}`;
22016
+ return `inv-${formattedDate}-${invoiceCounter.count + 1}`;
22017
+ } catch (error) {
22018
+ throw error;
22019
+ }
22020
+ }
21881
22021
  async function updateSeatsById(value) {
21882
22022
  const schema3 = import_joi17.default.object({
21883
22023
  subscriptionId: import_joi17.default.string().hex().required(),
21884
- seats: import_joi17.default.number().required(),
21885
- promoCode: import_joi17.default.string().optional().allow("", null),
22024
+ seats: import_joi17.default.number().min(1).required(),
21886
22025
  amount: import_joi17.default.number().required()
21887
22026
  });
21888
22027
  const { error } = schema3.validate(value);
@@ -21892,9 +22031,6 @@ function useSubscriptionService() {
21892
22031
  const session = import_utils69.useAtlas.getClient()?.startSession();
21893
22032
  try {
21894
22033
  session?.startTransaction();
21895
- if (value.seats && value.seats <= 0) {
21896
- throw new import_utils69.BadRequestError("Seats must be greater than 0.");
21897
- }
21898
22034
  const subscription = await _getById(value.subscriptionId);
21899
22035
  if (!subscription) {
21900
22036
  throw new import_utils69.BadRequestError("Subscription not found.");
@@ -21909,9 +22045,20 @@ function useSubscriptionService() {
21909
22045
  if (!members) {
21910
22046
  throw new import_utils69.BadRequestError("Failed to fetch members.");
21911
22047
  }
21912
- const paidSeats = subscription.paidSeats ?? 0;
21913
- const addedSeats = paidSeats && paidSeats > value.seats ? 0 : value.seats - paidSeats;
21914
- if (addedSeats === 0 && members > value.seats) {
22048
+ let paidSeats = subscription.paidSeats ?? 0;
22049
+ let seats = paidSeats > value.seats ? 0 : value.seats - paidSeats;
22050
+ const dueInvoice = await getByDueDate(
22051
+ subscription.nextBillingDate,
22052
+ "pending"
22053
+ );
22054
+ const currentBillingDate = new Date(subscription?.nextBillingDate);
22055
+ const isCancelledManually = !dueInvoice && subscription.status === "cancelled" && subscription.failedAttempts === 0 && (/* @__PURE__ */ new Date()).getTime() > currentBillingDate.getTime();
22056
+ const activeWithOverdueInvoice = dueInvoice && subscription.status === "active" && subscription.failedAttempts && subscription.failedAttempts > 0 && (/* @__PURE__ */ new Date()).getTime() > currentBillingDate.getTime();
22057
+ const isInvoiced = dueInvoice && dueInvoice.status === "pending" && subscription.status === "active" && (/* @__PURE__ */ new Date()).getTime() > currentBillingDate.getTime();
22058
+ if (isCancelledManually || activeWithOverdueInvoice || isInvoiced) {
22059
+ seats = value.seats;
22060
+ }
22061
+ if (seats === 0 && members > value.seats) {
21915
22062
  throw new import_utils69.BadRequestError(
21916
22063
  "Cannot reduce seats to less than the number of members. Please remove members before reducing seats."
21917
22064
  );
@@ -21925,99 +22072,41 @@ function useSubscriptionService() {
21925
22072
  "Failed to fetch monthly subscription price."
21926
22073
  );
21927
22074
  }
21928
- let amount = monthlySubscriptionPrice.value;
21929
- let newMonthlySubscriptionPrice = monthlySubscriptionPrice.value;
22075
+ let perSeatPrice = monthlySubscriptionPrice.value;
21930
22076
  if (monthlySubscriptionPrice.saleValue && monthlySubscriptionPrice.saleExpiry && new Date(monthlySubscriptionPrice.saleExpiry) > /* @__PURE__ */ new Date()) {
21931
- amount = monthlySubscriptionPrice.saleValue;
21932
- newMonthlySubscriptionPrice = monthlySubscriptionPrice.saleValue;
22077
+ perSeatPrice = monthlySubscriptionPrice.saleValue;
21933
22078
  }
21934
- const items = [];
21935
- const dueInvoice = await getByDueDate(
21936
- subscription.nextBillingDate,
21937
- "pending"
22079
+ let prorationFactor = calculateProration(
22080
+ new Date(subscription.nextBillingDate)
21938
22081
  );
21939
- if (value.promoCode) {
21940
- const promoCode = await getByCode(value.promoCode);
21941
- if (!promoCode) {
21942
- throw new import_utils69.BadRequestError("Promo code not found.");
21943
- }
21944
- if (promoCode && promoCode.type === "fixed" && promoCode.fixed_rate !== void 0 && promoCode.fixed_rate >= 0) {
21945
- amount = Number(promoCode.fixed_rate * addedSeats);
21946
- newMonthlySubscriptionPrice = Number(
21947
- promoCode.fixed_rate * value.seats
22082
+ const isSubscriptionReactivation = subscription.status === "cancelled" && !dueInvoice;
22083
+ const isPassedBillingDate = (/* @__PURE__ */ new Date()).getTime() > new Date(subscription.nextBillingDate).getTime();
22084
+ if (isSubscriptionReactivation && isPassedBillingDate) {
22085
+ prorationFactor = 1;
22086
+ }
22087
+ let amount = perSeatPrice * seats * prorationFactor;
22088
+ let newMonthlySubscriptionPrice = perSeatPrice * seats;
22089
+ let items = [];
22090
+ if (subscription.promoCode) {
22091
+ const promoCode = await getByCode(subscription.promoCode);
22092
+ if (promoCode) {
22093
+ const {
22094
+ amount: amountWithPromoCode,
22095
+ newMonthSubscriptionFee,
22096
+ items: itemWithPromoCode
22097
+ } = calculateAmountWithPromoCode(
22098
+ subscription,
22099
+ monthlySubscriptionPrice,
22100
+ seats,
22101
+ prorationFactor,
22102
+ promoCode
21948
22103
  );
21949
- const invoiceItem = {
21950
- description: "GoWeekdays Monthly Subscription",
21951
- unitPrice: Number(promoCode.fixed_rate.toFixed(2)),
21952
- quantity: 1,
21953
- seats: addedSeats,
21954
- total: Number(
21955
- (newMonthlySubscriptionPrice * addedSeats).toFixed(2)
21956
- )
21957
- };
21958
- if (dueInvoice && dueInvoice.status === "pending") {
21959
- invoiceItem.total = Number(
21960
- (newMonthlySubscriptionPrice * value.seats).toFixed(2)
21961
- );
21962
- invoiceItem.seats = value.seats;
21963
- }
21964
- items.push(invoiceItem);
21965
- }
21966
- if (promoCode && promoCode.type === "tiered" && promoCode.tiers && promoCode.tiers.length) {
21967
- const computedTieredInvoice = computeTieredInvoice(
21968
- addedSeats,
21969
- promoCode.tiers,
21970
- new Date(subscription.nextBillingDate ?? "")
21971
- );
21972
- items.push(...computedTieredInvoice.items);
21973
- amount = Number(computedTieredInvoice.totalCost.toFixed(2));
21974
- const newMonthlySubscriptionComputedTiered = computeTieredInvoice(
21975
- value.seats,
21976
- promoCode.tiers
21977
- );
21978
- newMonthlySubscriptionPrice = Number(
21979
- newMonthlySubscriptionComputedTiered.totalCost.toFixed(2)
21980
- );
21981
- if (dueInvoice && dueInvoice.status === "pending") {
21982
- amount = newMonthlySubscriptionPrice;
21983
- items.length = 0;
21984
- items.push(...newMonthlySubscriptionComputedTiered.items);
21985
- }
22104
+ amount = amountWithPromoCode;
22105
+ newMonthlySubscriptionPrice = newMonthSubscriptionFee;
22106
+ items = itemWithPromoCode;
21986
22107
  }
21987
22108
  }
21988
- if (items.length === 0) {
21989
- const itemData = {
21990
- description: "GoWeekdays Monthly Subscription",
21991
- unitPrice: Number(amount.toFixed(2)),
21992
- quantity: 1,
21993
- seats: addedSeats,
21994
- total: prorateSeats(
21995
- addedSeats,
21996
- amount,
21997
- new Date(subscription.nextBillingDate)
21998
- )
21999
- };
22000
- if (dueInvoice && dueInvoice.status === "pending") {
22001
- itemData.total = prorateSeats(
22002
- value.seats,
22003
- amount,
22004
- new Date(subscription.nextBillingDate)
22005
- );
22006
- itemData.seats = value.seats;
22007
- }
22008
- items.push(itemData);
22009
- amount = itemData.total;
22010
- }
22011
- const invoiceCounter = await getByType("invoice");
22012
- if (!invoiceCounter) {
22013
- throw new import_utils69.BadRequestError("Failed to fetch invoice counter.");
22014
- }
22015
- const date = /* @__PURE__ */ new Date();
22016
- const mm = String(date.getMonth() + 1).padStart(2, "0");
22017
- const dd = String(date.getDate()).padStart(2, "0");
22018
- const yyyy = date.getFullYear();
22019
- const formattedDate = `${mm}-${dd}-${yyyy}`;
22020
- const invoiceNumber = `inv-${formattedDate}-${invoiceCounter.count + 1}`;
22109
+ const invoiceNumber = await generateInvoiceNumber();
22021
22110
  const invoiceData = {
22022
22111
  type: "organization-subscription",
22023
22112
  invoiceNumber,
@@ -22026,12 +22115,24 @@ function useSubscriptionService() {
22026
22115
  metadata: {
22027
22116
  orgId: String(subscription.org),
22028
22117
  currency: subscription.currency,
22029
- subscriptionId: value.subscriptionId
22118
+ subscriptionId: value.subscriptionId,
22119
+ description: `Charge for ${isSubscriptionReactivation ? "" : "additional"} ${seats.toLocaleString()} seats`
22030
22120
  },
22031
22121
  status: "paid",
22032
- items
22122
+ items: [
22123
+ {
22124
+ description: "GoWeekdays Monthly Subscription",
22125
+ unitPrice: formatAmount(perSeatPrice),
22126
+ quantity: 1,
22127
+ seats,
22128
+ total: formatAmount(amount)
22129
+ }
22130
+ ]
22033
22131
  };
22034
- if (addedSeats && !dueInvoice) {
22132
+ if (items.length) {
22133
+ invoiceData.items = items;
22134
+ }
22135
+ if (seats && !dueInvoice) {
22035
22136
  await addInvoice(invoiceData, session);
22036
22137
  await incrementByType("invoice", session);
22037
22138
  let payment = {};
@@ -22039,7 +22140,7 @@ function useSubscriptionService() {
22039
22140
  payment = await pay({
22040
22141
  customer_id: subscription.customerId,
22041
22142
  payment_method_id: subscription.paymentMethodId,
22042
- amount: Number(amount.toFixed(2)),
22143
+ amount: formatAmount(amount),
22043
22144
  currency: subscription.currency,
22044
22145
  description: "GoWeekdays Organization Monthly Subscription."
22045
22146
  });
@@ -22069,13 +22170,14 @@ function useSubscriptionService() {
22069
22170
  await addPayment(
22070
22171
  {
22071
22172
  invoiceId: invoiceNumber,
22072
- amount: Number(amount.toFixed(2)),
22173
+ amount: formatAmount(amount),
22073
22174
  paymentMethod: paymentMethod.type,
22074
22175
  status: "completed",
22075
22176
  metadata: paymentMetadata
22076
22177
  },
22077
22178
  session
22078
22179
  );
22180
+ paidSeats = value.seats;
22079
22181
  } else if (dueInvoice && dueInvoice.status === "pending") {
22080
22182
  await updateStatusByInvoiceNumber(
22081
22183
  dueInvoice.invoiceNumber,
@@ -22087,17 +22189,25 @@ function useSubscriptionService() {
22087
22189
  await addInvoice(invoiceData, session);
22088
22190
  await incrementByType("invoice", session);
22089
22191
  }
22090
- await _updateSeatsById(
22091
- {
22092
- _id: value.subscriptionId,
22093
- currentSeats: value.seats,
22094
- maxSeats: 0,
22095
- paidSeats: paidSeats > value.seats ? paidSeats : value.seats,
22096
- promoCode: value.promoCode ?? "",
22097
- amount: Number(newMonthlySubscriptionPrice.toFixed(2))
22098
- },
22099
- session
22100
- );
22192
+ const updateData = {
22193
+ _id: value.subscriptionId,
22194
+ currentSeats: value.seats,
22195
+ maxSeats: 0,
22196
+ paidSeats,
22197
+ amount: newMonthlySubscriptionPrice,
22198
+ status: "",
22199
+ nextBillingDate: ""
22200
+ };
22201
+ if (!dueInvoice && (/* @__PURE__ */ new Date()).getTime() > currentBillingDate.getTime()) {
22202
+ let date = /* @__PURE__ */ new Date();
22203
+ date.setMonth(date.getMonth() + 1);
22204
+ date.setDate(date.getDate() + 1);
22205
+ updateData.nextBillingDate = date.toISOString();
22206
+ }
22207
+ if (!dueInvoice && subscription.status === "cancelled") {
22208
+ updateData.status = "active";
22209
+ }
22210
+ await _updateSeatsById(updateData, session);
22101
22211
  await session?.commitTransaction();
22102
22212
  } catch (error2) {
22103
22213
  await session?.abortTransaction();
@@ -22174,7 +22284,9 @@ function useSubscriptionController() {
22174
22284
  getSubscriptions: _getSubscriptions,
22175
22285
  getByAffiliateUserId: _getByAffiliateUserId,
22176
22286
  getByOrgId: _getByOrgId,
22177
- getById: _getById
22287
+ getById: _getById,
22288
+ updatePromoCodeById: _updatePromoCodeById,
22289
+ updateStatusById: _updateStatusById
22178
22290
  } = useSubscriptionRepo();
22179
22291
  const {
22180
22292
  getByUserId: _getByUserId,
@@ -22356,6 +22468,46 @@ function useSubscriptionController() {
22356
22468
  return;
22357
22469
  }
22358
22470
  }
22471
+ async function updatePromoCodeById(req, res, next) {
22472
+ const subscriptionId = req.params.id ?? "";
22473
+ const promoCode = req.body.promoCode;
22474
+ const { error } = import_joi19.default.object({
22475
+ subscriptionId: import_joi19.default.string().required(),
22476
+ promoCode: import_joi19.default.string().optional().allow("", null)
22477
+ }).validate({ subscriptionId, promoCode });
22478
+ if (error) {
22479
+ next(new import_utils70.BadRequestError(error.message));
22480
+ return;
22481
+ }
22482
+ try {
22483
+ await _updatePromoCodeById(subscriptionId, promoCode);
22484
+ res.json({ message: "Promo code updated successfully." });
22485
+ return;
22486
+ } catch (error2) {
22487
+ next(error2);
22488
+ return;
22489
+ }
22490
+ }
22491
+ async function updateStatusById(req, res, next) {
22492
+ const subscriptionId = req.params.id ?? "";
22493
+ const status = req.body.status;
22494
+ const { error } = import_joi19.default.object({
22495
+ subscriptionId: import_joi19.default.string().required(),
22496
+ status: import_joi19.default.string().optional().allow("", null)
22497
+ }).validate({ subscriptionId, status });
22498
+ if (error) {
22499
+ next(new import_utils70.BadRequestError(error.message));
22500
+ return;
22501
+ }
22502
+ try {
22503
+ await _updateStatusById(subscriptionId, status);
22504
+ res.json({ message: "Promo code updated successfully." });
22505
+ return;
22506
+ } catch (error2) {
22507
+ next(error2);
22508
+ return;
22509
+ }
22510
+ }
22359
22511
  return {
22360
22512
  add,
22361
22513
  getByUserId,
@@ -22366,30 +22518,32 @@ function useSubscriptionController() {
22366
22518
  getSubscriptionStatus,
22367
22519
  createAffiliateSubscription,
22368
22520
  createOrgSubscription,
22369
- updateSubscriptionSeats
22521
+ updateSubscriptionSeats,
22522
+ updatePromoCodeById,
22523
+ updateStatusById
22370
22524
  };
22371
22525
  }
22372
22526
 
22373
22527
  // src/models/payment-method.model.ts
22374
22528
  var import_utils71 = require("@goweekdays/utils");
22375
- var import_mongodb35 = require("mongodb");
22529
+ var import_mongodb36 = require("mongodb");
22376
22530
  function MPaymentMethod(value) {
22377
22531
  if (value.user) {
22378
22532
  try {
22379
- value.user = new import_mongodb35.ObjectId(value.user);
22533
+ value.user = new import_mongodb36.ObjectId(value.user);
22380
22534
  } catch (error) {
22381
22535
  throw new import_utils71.BadRequestError("Invalid user ID.");
22382
22536
  }
22383
22537
  }
22384
22538
  if (value.org) {
22385
22539
  try {
22386
- value.org = new import_mongodb35.ObjectId(value.org);
22540
+ value.org = new import_mongodb36.ObjectId(value.org);
22387
22541
  } catch (error) {
22388
22542
  throw new import_utils71.BadRequestError("Invalid org ID.");
22389
22543
  }
22390
22544
  }
22391
22545
  return {
22392
- _id: value._id ?? new import_mongodb35.ObjectId(),
22546
+ _id: value._id ?? new import_mongodb36.ObjectId(),
22393
22547
  user: value.user ?? "",
22394
22548
  org: value.org ?? "",
22395
22549
  name: value.name,
@@ -22409,7 +22563,7 @@ function MPaymentMethod(value) {
22409
22563
 
22410
22564
  // src/repositories/payment-method.repository.ts
22411
22565
  var import_utils72 = require("@goweekdays/utils");
22412
- var import_mongodb36 = require("mongodb");
22566
+ var import_mongodb37 = require("mongodb");
22413
22567
  function usePaymentMethodRepo() {
22414
22568
  const db = import_utils72.useAtlas.getDb();
22415
22569
  if (!db) {
@@ -22462,7 +22616,7 @@ function usePaymentMethodRepo() {
22462
22616
  }
22463
22617
  function getByUser(user) {
22464
22618
  try {
22465
- user = new import_mongodb36.ObjectId(user);
22619
+ user = new import_mongodb37.ObjectId(user);
22466
22620
  } catch (error) {
22467
22621
  throw new import_utils72.BadRequestError("Invalid user ID.");
22468
22622
  }
@@ -22487,7 +22641,7 @@ function usePaymentMethodRepo() {
22487
22641
  }
22488
22642
  function getByOrg(org) {
22489
22643
  try {
22490
- org = new import_mongodb36.ObjectId(org);
22644
+ org = new import_mongodb37.ObjectId(org);
22491
22645
  } catch (error) {
22492
22646
  throw new import_utils72.BadRequestError("Invalid org ID.");
22493
22647
  }
@@ -22522,7 +22676,7 @@ function usePaymentMethodRepo() {
22522
22676
  }
22523
22677
  async function deleteById(_id) {
22524
22678
  try {
22525
- _id = new import_mongodb36.ObjectId(_id);
22679
+ _id = new import_mongodb37.ObjectId(_id);
22526
22680
  } catch (error) {
22527
22681
  throw new import_utils72.BadRequestError("Invalid payment method ID.");
22528
22682
  }
@@ -22684,7 +22838,12 @@ var import_joi20 = __toESM(require("joi"));
22684
22838
  var import_utils74 = require("@goweekdays/utils");
22685
22839
  function usePaymentMethodController() {
22686
22840
  const { linkEWallet: _linkEWallet, linkCard: _linkCard } = usePaymentMethodService();
22687
- const { cardLinkOnly, directDebitLinkOnly, eWalletLinkOnly } = useXenditService();
22841
+ const {
22842
+ cardLinkOnly,
22843
+ directDebitLinkOnly,
22844
+ eWalletLinkOnly,
22845
+ getPaymentMethodById: _getPaymentMethodById
22846
+ } = useXenditService();
22688
22847
  async function linkEWallet(req, res, next) {
22689
22848
  const type = req.body.type ?? "";
22690
22849
  const customer_id = req.body.customer_id ?? "";
@@ -22813,6 +22972,22 @@ function usePaymentMethodController() {
22813
22972
  next(error2);
22814
22973
  }
22815
22974
  }
22975
+ async function getPaymentMethodById(req, res, next) {
22976
+ const id = req.params.id ?? "";
22977
+ const validation = import_joi20.default.object({
22978
+ id: import_joi20.default.string().required()
22979
+ });
22980
+ const { error } = validation.validate({ id });
22981
+ if (error) {
22982
+ next(new import_utils74.BadRequestError(error.message));
22983
+ }
22984
+ try {
22985
+ const result = await _getPaymentMethodById(id);
22986
+ res.json(result);
22987
+ } catch (error2) {
22988
+ next(error2);
22989
+ }
22990
+ }
22816
22991
  async function linkOnly(req, res, next) {
22817
22992
  const type = req.body.type ?? "";
22818
22993
  if (type === "CARD") {
@@ -22871,7 +23046,8 @@ function usePaymentMethodController() {
22871
23046
  linkCard,
22872
23047
  getByUser,
22873
23048
  getByOrg,
22874
- linkOnly
23049
+ linkOnly,
23050
+ getPaymentMethodById
22875
23051
  };
22876
23052
  }
22877
23053
 
@@ -23316,7 +23492,7 @@ function usePromoCodeController() {
23316
23492
  }
23317
23493
 
23318
23494
  // src/models/order.model.ts
23319
- var import_mongodb37 = require("mongodb");
23495
+ var import_mongodb38 = require("mongodb");
23320
23496
 
23321
23497
  // src/validations/order.schema.ts
23322
23498
  var import_joi25 = __toESM(require("joi"));
@@ -23350,28 +23526,28 @@ function MOrder(value) {
23350
23526
  }
23351
23527
  if (value._id) {
23352
23528
  try {
23353
- value._id = new import_mongodb37.ObjectId(value._id);
23529
+ value._id = new import_mongodb38.ObjectId(value._id);
23354
23530
  } catch (error2) {
23355
23531
  throw new import_utils80.BadRequestError("Invalid ID.");
23356
23532
  }
23357
23533
  }
23358
23534
  if (value.user) {
23359
23535
  try {
23360
- value.user = new import_mongodb37.ObjectId(value.user);
23536
+ value.user = new import_mongodb38.ObjectId(value.user);
23361
23537
  } catch (error2) {
23362
23538
  throw new import_utils80.BadRequestError("Invalid user ID.");
23363
23539
  }
23364
23540
  }
23365
23541
  if (value.org) {
23366
23542
  try {
23367
- value.org = new import_mongodb37.ObjectId(value.org);
23543
+ value.org = new import_mongodb38.ObjectId(value.org);
23368
23544
  } catch (error2) {
23369
23545
  throw new import_utils80.BadRequestError("Invalid org ID.");
23370
23546
  }
23371
23547
  }
23372
23548
  if (value.metadata?.subscriptionId) {
23373
23549
  try {
23374
- value.metadata.subscriptionId = new import_mongodb37.ObjectId(
23550
+ value.metadata.subscriptionId = new import_mongodb38.ObjectId(
23375
23551
  value.metadata.subscriptionId
23376
23552
  );
23377
23553
  } catch (error2) {
@@ -23397,7 +23573,7 @@ function MOrder(value) {
23397
23573
 
23398
23574
  // src/repositories/order.repository.ts
23399
23575
  var import_utils81 = require("@goweekdays/utils");
23400
- var import_mongodb38 = require("mongodb");
23576
+ var import_mongodb39 = require("mongodb");
23401
23577
  function useOrderRepo() {
23402
23578
  const db = import_utils81.useAtlas.getDb();
23403
23579
  if (!db) {
@@ -23447,7 +23623,7 @@ function useOrderRepo() {
23447
23623
  }
23448
23624
  if (id) {
23449
23625
  try {
23450
- query["metadata.subscriptionId"] = new import_mongodb38.ObjectId(id);
23626
+ query["metadata.subscriptionId"] = new import_mongodb39.ObjectId(id);
23451
23627
  } catch (error) {
23452
23628
  throw new import_utils81.BadRequestError("Invalid subscription ID.");
23453
23629
  }
@@ -23702,7 +23878,8 @@ var import_joi27 = __toESM(require("joi"));
23702
23878
  function useInvoiceController() {
23703
23879
  const {
23704
23880
  getBySubscriptionId: _getBySubscriptionId,
23705
- getByNumber: _getByNumber
23881
+ getByNumber: _getByNumber,
23882
+ getByDueDate: _getByDueDate
23706
23883
  } = useInvoiceRepo();
23707
23884
  async function getBySubscriptionId(req, res, next) {
23708
23885
  const id = req.params.id;
@@ -23757,9 +23934,33 @@ function useInvoiceController() {
23757
23934
  next(error2);
23758
23935
  }
23759
23936
  }
23937
+ async function getByDueDateStatus(req, res, next) {
23938
+ const date = req.params.date;
23939
+ const status = req.params.status;
23940
+ const validation = import_joi27.default.object({
23941
+ date: import_joi27.default.string().required(),
23942
+ status: import_joi27.default.string().required()
23943
+ });
23944
+ const { error } = validation.validate({ date, status });
23945
+ if (error) {
23946
+ next(new import_utils84.BadRequestError(error.message));
23947
+ return;
23948
+ }
23949
+ try {
23950
+ const invoice = await _getByDueDate(new Date(date), status);
23951
+ if (!invoice) {
23952
+ throw new import_utils84.NotFoundError("Invoice not found.");
23953
+ }
23954
+ res.json(invoice);
23955
+ return;
23956
+ } catch (error2) {
23957
+ next(error2);
23958
+ }
23959
+ }
23760
23960
  return {
23761
23961
  getBySubscriptionId,
23762
- getByNumber
23962
+ getByNumber,
23963
+ getByDueDateStatus
23763
23964
  };
23764
23965
  }
23765
23966