@saasicat/nest 1.0.0-rc.21 → 1.0.0-rc.22

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.
Files changed (64) hide show
  1. package/dist/.build-stamp +1 -1
  2. package/dist/_entries.cjs +4887 -3609
  3. package/dist/admin/index.js +7 -5
  4. package/dist/{aggregation-ChpZDh0g.d.ts → aggregation-6fPkeV8i.d.ts} +39 -4
  5. package/dist/{aggregation-OpnhwzF5.d.cts → aggregation-DjQUc7yd.d.cts} +39 -4
  6. package/dist/billing/index.d.cts +68 -9
  7. package/dist/billing/index.d.ts +68 -9
  8. package/dist/billing/index.js +16 -5
  9. package/dist/checkout-offer/index.d.cts +2 -2
  10. package/dist/checkout-offer/index.d.ts +2 -2
  11. package/dist/checkout-offer/index.js +4 -4
  12. package/dist/{checkout-offer.service-CCVxG9b2.d.cts → checkout-offer.service-DmVGrT4v.d.cts} +1 -1
  13. package/dist/{checkout-offer.service-BeHgYikI.d.ts → checkout-offer.service-Yi30L4ry.d.ts} +1 -1
  14. package/dist/{chunk-WHJSYKNC.js → chunk-3U4N5UYD.js} +1 -1
  15. package/dist/{chunk-M47BNEY2.js → chunk-3YYLOYUT.js} +1373 -266
  16. package/dist/{chunk-G6EZWECL.js → chunk-B4YP7REK.js} +94 -26
  17. package/dist/{chunk-QPVDCKYS.js → chunk-ESEWSAOV.js} +4 -3
  18. package/dist/{chunk-NDLC5GYK.js → chunk-EU4N5LET.js} +4 -3
  19. package/dist/{chunk-LLYVYRGJ.js → chunk-F2PF4IXB.js} +1 -275
  20. package/dist/{chunk-AOS2JPDD.js → chunk-FJIOHAO5.js} +77 -41
  21. package/dist/{chunk-TOEFN7DN.js → chunk-FPMBQDW7.js} +2 -2
  22. package/dist/chunk-OSOP4KWL.js +288 -0
  23. package/dist/{chunk-3RLWML2S.js → chunk-QWKBOLBH.js} +35 -0
  24. package/dist/{chunk-JBCC6C3M.js → chunk-R3I5X262.js} +5 -4
  25. package/dist/{chunk-4SDSYV4A.js → chunk-T6WXDGIU.js} +1 -1
  26. package/dist/{chunk-R5YCJBHY.js → chunk-U5UPEFWC.js} +2 -2
  27. package/dist/{chunk-XSSWYPP5.js → chunk-UMZCOCT5.js} +1 -1
  28. package/dist/{chunk-7NJKVC5X.js → chunk-WQNOCOI2.js} +53 -6
  29. package/dist/{chunk-55QEDAMX.js → chunk-WTAS2CLM.js} +4 -9
  30. package/dist/{enforce-quota.interceptor-BFyVdWe8.d.cts → enforce-quota.interceptor-B9UhCBXx.d.cts} +2 -2
  31. package/dist/{enforce-quota.interceptor-GaBXboGx.d.ts → enforce-quota.interceptor-CR2waXNA.d.ts} +2 -2
  32. package/dist/entitlement/index.d.cts +1 -1
  33. package/dist/entitlement/index.d.ts +1 -1
  34. package/dist/entitlement/index.js +4 -2
  35. package/dist/index.d.cts +11 -11
  36. package/dist/index.d.ts +11 -11
  37. package/dist/index.js +36 -20
  38. package/dist/{issuer-identity.check-CBO0vcsq.d.cts → issuer-identity.check-BntcNdld.d.cts} +1 -1
  39. package/dist/{issuer-identity.check-Cz9CEMkZ.d.ts → issuer-identity.check-DFmS8whs.d.ts} +1 -1
  40. package/dist/{module-options-qAOwinHR.d.cts → module-options-C4S_StYF.d.cts} +2 -2
  41. package/dist/{module-options-DmQ3G4sZ.d.ts → module-options-DxJI-1iH.d.ts} +2 -2
  42. package/dist/payments/index.js +2 -2
  43. package/dist/platform/index.d.cts +25 -16
  44. package/dist/platform/index.d.ts +25 -16
  45. package/dist/platform/index.js +16 -14
  46. package/dist/promo/index.d.cts +11 -4
  47. package/dist/promo/index.d.ts +11 -4
  48. package/dist/promo/index.js +5 -3
  49. package/dist/{promo.module-CKkECmU8.d.cts → promo.module-Ddx-swUV.d.cts} +1 -1
  50. package/dist/{promo.module-DuIJdK3U.d.ts → promo.module-PsIvDcUp.d.ts} +1 -1
  51. package/dist/{promo.service-BYBu6dy3.d.cts → promo.service-BextPvx5.d.cts} +23 -1
  52. package/dist/{promo.service-CFjfF0Xm.d.ts → promo.service-Dqqh0JXn.d.ts} +23 -1
  53. package/dist/registration/index.d.cts +2 -2
  54. package/dist/registration/index.d.ts +2 -2
  55. package/dist/registration/index.js +5 -5
  56. package/dist/subscription-contract/index.js +2 -2
  57. package/dist/{tenant-billing.controller-DKbAqTbM.d.cts → tenant-billing.controller-BC6-S5cT.d.cts} +91 -10
  58. package/dist/{tenant-billing.controller-BLqbsONe.d.ts → tenant-billing.controller-BuQDQ76i.d.ts} +91 -10
  59. package/dist/{tenant-billing.module-ClbjzocC.d.cts → tenant-billing.module-C6MFfCKq.d.cts} +24 -1
  60. package/dist/{tenant-billing.module-DohgJJ2m.d.ts → tenant-billing.module-CUCQL0-k.d.ts} +24 -1
  61. package/dist/testing/index.d.cts +6 -6
  62. package/dist/testing/index.d.ts +6 -6
  63. package/dist/testing/index.js +33 -31
  64. package/package.json +3 -3
@@ -1,12 +1,18 @@
1
+ import {
2
+ AdminResourcesService
3
+ } from "./chunk-OSOP4KWL.js";
1
4
  import {
2
5
  AdminAuditService
3
6
  } from "./chunk-OOTEXP47.js";
4
7
  import {
5
- PromoCodesService
6
- } from "./chunk-3RLWML2S.js";
8
+ PromoCodesService,
9
+ buildLabel,
10
+ promoCodeDiscountNet
11
+ } from "./chunk-QWKBOLBH.js";
7
12
  import {
8
- SubscriptionContractService
9
- } from "./chunk-NDLC5GYK.js";
13
+ SubscriptionContractService,
14
+ generatedDiscountLine
15
+ } from "./chunk-EU4N5LET.js";
10
16
  import {
11
17
  resolveBundlePriceNet
12
18
  } from "./chunk-KPEMTBOP.js";
@@ -16,7 +22,7 @@ import {
16
22
  SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN,
17
23
  cancellationHasLanded,
18
24
  toEffectiveLimitsSnapshot
19
- } from "./chunk-G6EZWECL.js";
25
+ } from "./chunk-B4YP7REK.js";
20
26
  import {
21
27
  ENTITLEMENT_SERVICE_TOKEN
22
28
  } from "./chunk-OUDSQ3PU.js";
@@ -61,9 +67,15 @@ import {
61
67
  USER_EMAIL_RESOLVER_TOKEN,
62
68
  USER_ID_RESOLVER_TOKEN
63
69
  } from "./chunk-GPQWGA6B.js";
70
+ import {
71
+ RLS_BYPASS_PORT_TOKEN
72
+ } from "./chunk-SZ7RFPXA.js";
64
73
  import {
65
74
  subscriberProviders
66
75
  } from "./chunk-WOEJ6K7M.js";
76
+ import {
77
+ SUBSCRIBER_REPOSITORY_TOKEN
78
+ } from "./chunk-LF6J4YYN.js";
67
79
  import {
68
80
  SaaSiCatPublicRoute
69
81
  } from "./chunk-YSC46BGM.js";
@@ -1028,23 +1040,52 @@ var SELF_SERVICE_BLOCKED_BUNDLES_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/ne
1028
1040
  var DAY_MS2 = 864e5;
1029
1041
  function computeProration(input) {
1030
1042
  const { periodStart, periodEnd, now, currentPriceNet, targetPriceNet } = input;
1031
- const daysInPeriod = Math.max(1, Math.round((periodEnd.getTime() - periodStart.getTime()) / DAY_MS2));
1032
- const daysRemaining = Math.max(0, Math.min(daysInPeriod, Math.round((periodEnd.getTime() - now.getTime()) / DAY_MS2)));
1043
+ const { daysInPeriod, daysRemaining } = daysOf(periodStart, periodEnd, now);
1033
1044
  const rawDeltaNet = round2((targetPriceNet - currentPriceNet) * daysRemaining / daysInPeriod);
1034
- const prorataDeltaNet = Math.max(0, rawDeltaNet);
1035
1045
  return {
1046
+ basis: "difference",
1036
1047
  daysRemainingInPeriod: daysRemaining,
1037
1048
  daysInPeriod,
1038
1049
  periodStart,
1039
1050
  periodEnd,
1040
1051
  currentPriceNet,
1041
1052
  targetPriceNet,
1042
- prorataDeltaNet,
1053
+ prorataDeltaNet: Math.max(0, rawDeltaNet),
1043
1054
  rawDeltaNet,
1044
- isFree: rawDeltaNet < 0
1055
+ isFree: rawDeltaNet < 0,
1056
+ remainderNet: 0
1045
1057
  };
1046
1058
  }
1047
1059
  __name(computeProration, "computeProration");
1060
+ function computeNewPeriodCharge(input) {
1061
+ const { periodStart, periodEnd, now, currentPriceNet, targetPriceNet } = input;
1062
+ const { daysInPeriod, daysRemaining } = daysOf(periodStart, periodEnd, now);
1063
+ const remainderNet = round2(currentPriceNet * daysRemaining / daysInPeriod);
1064
+ const rawDeltaNet = round2(targetPriceNet - remainderNet);
1065
+ return {
1066
+ basis: "newPeriod",
1067
+ daysRemainingInPeriod: daysRemaining,
1068
+ daysInPeriod,
1069
+ periodStart,
1070
+ periodEnd,
1071
+ currentPriceNet,
1072
+ targetPriceNet,
1073
+ prorataDeltaNet: Math.max(0, rawDeltaNet),
1074
+ rawDeltaNet,
1075
+ isFree: rawDeltaNet < 0,
1076
+ remainderNet
1077
+ };
1078
+ }
1079
+ __name(computeNewPeriodCharge, "computeNewPeriodCharge");
1080
+ function daysOf(periodStart, periodEnd, now) {
1081
+ const daysInPeriod = Math.max(1, Math.round((periodEnd.getTime() - periodStart.getTime()) / DAY_MS2));
1082
+ const daysRemaining = Math.max(0, Math.min(daysInPeriod, Math.round((periodEnd.getTime() - now.getTime()) / DAY_MS2)));
1083
+ return {
1084
+ daysInPeriod,
1085
+ daysRemaining
1086
+ };
1087
+ }
1088
+ __name(daysOf, "daysOf");
1048
1089
  function round2(n) {
1049
1090
  return Math.round(n * 100) / 100;
1050
1091
  }
@@ -1081,7 +1122,8 @@ var PlanChangePreviewService = class {
1081
1122
  blockedPlans;
1082
1123
  trialProjection;
1083
1124
  subscriptionBundles;
1084
- constructor(catalogs, entitlements, subscriptions, usageSnapshot, blockedPlans = null, trialProjection = null, subscriptionBundles = null) {
1125
+ contracts;
1126
+ constructor(catalogs, entitlements, subscriptions, usageSnapshot, blockedPlans = null, trialProjection = null, subscriptionBundles = null, contracts = null) {
1085
1127
  this.catalogs = catalogs;
1086
1128
  this.entitlements = entitlements;
1087
1129
  this.subscriptions = subscriptions;
@@ -1089,6 +1131,7 @@ var PlanChangePreviewService = class {
1089
1131
  this.blockedPlans = blockedPlans;
1090
1132
  this.trialProjection = trialProjection;
1091
1133
  this.subscriptionBundles = subscriptionBundles;
1134
+ this.contracts = contracts;
1092
1135
  }
1093
1136
  async preview(tenantId, targetPlan, targetCycle, now = /* @__PURE__ */ new Date()) {
1094
1137
  const sub = await this.subscriptions.findForTenant(tenantId);
@@ -1173,7 +1216,7 @@ var PlanChangePreviewService = class {
1173
1216
  const commits = ctx.status !== "TRIAL";
1174
1217
  const isImmediate = planDirection === "UP" && (!commits || cycleDirection !== "SHORTER");
1175
1218
  const effectiveAt = isImmediate ? null : this.resolveEffectiveAt(ctx, now);
1176
- const proration = isImmediate && ctx.status !== "TRIAL" ? this.computeProration(ctx, now, currentSnap, targetSnap, sub.billingCycle, targetCycle) : null;
1219
+ const proration = isImmediate && ctx.status !== "TRIAL" ? this.computeProration(ctx, now, await this.planPricePaid(tenantId, sub.plan, sub.billingCycle, now) ?? priceForCycle(currentSnap, sub.billingCycle) ?? 0, priceForCycle(targetSnap, targetCycle) ?? 0, cycleDirection) : null;
1177
1220
  const blockers = [];
1178
1221
  const warnings = [];
1179
1222
  const landsAt = ctx.canceledEffectiveAt ?? ctx.canceledAt;
@@ -1360,17 +1403,41 @@ var PlanChangePreviewService = class {
1360
1403
  if (ctx.minimumTermUntil && ctx.minimumTermUntil > periodEnd) return ctx.minimumTermUntil;
1361
1404
  return periodEnd;
1362
1405
  }
1363
- computeProration(ctx, now, current, target, currentCycle, targetCycle) {
1406
+ computeProration(ctx, now, currentPriceNet, targetPriceNet, cycleDirection) {
1407
+ if (ctx.currentPeriodEnd === null) {
1408
+ return computeNewPeriodCharge({
1409
+ periodStart: now,
1410
+ periodEnd: now,
1411
+ now,
1412
+ currentPriceNet,
1413
+ targetPriceNet
1414
+ });
1415
+ }
1364
1416
  const periodStart = ctx.currentPeriodStart ?? ctx.startedAt ?? now;
1365
- const periodEnd = ctx.currentPeriodEnd ?? periodEndAfter(ctx.startedAt, ctx.currentBillingCycle, now);
1366
- return computeProration({
1417
+ const charge = cycleDirection === "SAME" ? computeProration : computeNewPeriodCharge;
1418
+ return charge({
1367
1419
  periodStart,
1368
- periodEnd,
1420
+ periodEnd: ctx.currentPeriodEnd,
1369
1421
  now,
1370
- currentPriceNet: priceForCycle(current, currentCycle) ?? 0,
1371
- targetPriceNet: priceForCycle(target, targetCycle) ?? 0
1422
+ currentPriceNet,
1423
+ targetPriceNet
1372
1424
  });
1373
1425
  }
1426
+ /**
1427
+ * The net price of the plan line in the contract in force, when it is for
1428
+ * `plan` billed in `cycle` — what the customer pays for the period they are
1429
+ * in. Null without contracts, or where the line is for another plan or
1430
+ * rhythm: a freeze is optional and does not stop the change it follows when
1431
+ * it fails, so the contract in force can still describe the plan before.
1432
+ * The caller then prices from the catalogue.
1433
+ */
1434
+ async planPricePaid(tenantId, plan, cycle, now) {
1435
+ const contract = await this.contracts?.findActiveByTenantId(tenantId, now);
1436
+ const planLine = contract?.lineItems.find((line) => line.kind === "plan");
1437
+ if (!planLine || planLine.sourceKey !== plan) return null;
1438
+ if (planLine.billingCycle !== cycle.toLowerCase()) return null;
1439
+ return planLine.priceNet;
1440
+ }
1374
1441
  };
1375
1442
  PlanChangePreviewService = _ts_decorate4([
1376
1443
  Injectable2(),
@@ -1384,6 +1451,8 @@ PlanChangePreviewService = _ts_decorate4([
1384
1451
  _ts_param2(5, Inject2(TRIAL_PROJECTION_PORT_TOKEN)),
1385
1452
  _ts_param2(6, Optional2()),
1386
1453
  _ts_param2(6, Inject2(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
1454
+ _ts_param2(7, Optional2()),
1455
+ _ts_param2(7, Inject2(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
1387
1456
  _ts_metadata2("design:type", Function),
1388
1457
  _ts_metadata2("design:paramtypes", [
1389
1458
  typeof PlanCatalogSource === "undefined" ? Object : PlanCatalogSource,
@@ -1392,6 +1461,7 @@ PlanChangePreviewService = _ts_decorate4([
1392
1461
  typeof UsageSnapshotPort === "undefined" ? Object : UsageSnapshotPort,
1393
1462
  Object,
1394
1463
  Object,
1464
+ Object,
1395
1465
  Object
1396
1466
  ])
1397
1467
  ], PlanChangePreviewService);
@@ -1536,11 +1606,15 @@ var SubscriptionContractFreezeService = class {
1536
1606
  entitlements;
1537
1607
  contracts;
1538
1608
  source;
1539
- constructor(catalogs, entitlements, contracts, source, writes = null) {
1609
+ subscriptions;
1610
+ promoCodes;
1611
+ constructor(catalogs, entitlements, contracts, source, writes = null, subscriptions = null, promoCodes = null) {
1540
1612
  this.catalogs = catalogs;
1541
1613
  this.entitlements = entitlements;
1542
1614
  this.contracts = contracts;
1543
1615
  this.source = source;
1616
+ this.subscriptions = subscriptions;
1617
+ this.promoCodes = promoCodes;
1544
1618
  if (writes?.bindsPlanVersion === false) {
1545
1619
  throw new Error("The contract freeze records the plan version a subscription is bound to, and this installation's subscription write does not bind it on a plan change. With @saasicat/adapter-prisma, leave `tenantSubscription.synchronizePlanVersion` at its default of true \u2014 or do not configure `contractFreeze`.");
1546
1620
  }
@@ -1598,9 +1672,13 @@ var SubscriptionContractFreezeService = class {
1598
1672
  quotaEffectsSnapshot: planDef.quotas,
1599
1673
  metadata: null
1600
1674
  };
1675
+ const redeemed = await this.redeemedCodeNotYetRecorded(tenantId, planPriceNet, billingCycle, vatRate);
1601
1676
  const lineItems = recordContractLinesMoney([
1602
1677
  planLineItem,
1603
- ...bundles.lineItems
1678
+ ...bundles.lineItems,
1679
+ ...redeemed ? [
1680
+ redeemed.line
1681
+ ] : []
1604
1682
  ], {
1605
1683
  currency: catalog.currency,
1606
1684
  taxRate: vatRate
@@ -1627,7 +1705,7 @@ var SubscriptionContractFreezeService = class {
1627
1705
  });
1628
1706
  }
1629
1707
  this.entitlements.invalidateTenant(tenantId);
1630
- const limits = await this.entitlements.computeLimits(tenantId, effectiveFrom, catalog);
1708
+ const { limits, leftOutBundleVersionIds } = await this.entitlements.computeContractLimits(tenantId, effectiveFrom, catalog);
1631
1709
  const data = {
1632
1710
  tenantId,
1633
1711
  status: "active",
@@ -1646,14 +1724,81 @@ var SubscriptionContractFreezeService = class {
1646
1724
  },
1647
1725
  features: [
1648
1726
  ...limits.features
1649
- ]
1727
+ ],
1728
+ ...leftOutBundleVersionIds.length > 0 ? {
1729
+ leftOutBundleVersionIds
1730
+ } : {}
1650
1731
  },
1651
1732
  priceSnapshot,
1733
+ promoCodeSnapshots: redeemed ? [
1734
+ redeemed.snapshot
1735
+ ] : [],
1652
1736
  lineItems
1653
1737
  };
1654
1738
  await this.contracts.create(data);
1655
1739
  this.entitlements.invalidateTenant(tenantId);
1656
1740
  }
1741
+ /**
1742
+ * The code redeemed for the tenant's subscription, as a discount line, where
1743
+ * no contract of the tenant records it yet.
1744
+ *
1745
+ * The first contract written after a redemption records it — at onboarding,
1746
+ * or at the activation after a trial, where onboarding wrote none — and the
1747
+ * ones after it do not repeat it: the discount runs from where it was first
1748
+ * agreed, for the duration on its snapshot, counted from the first period
1749
+ * that is paid. A contract concluded from an offer that carried the code
1750
+ * already records it. Nothing is recorded without the promo module, for a
1751
+ * reversed redemption, or where a contract written since the redemption
1752
+ * does not record it: that was the first one, written before contracts
1753
+ * recorded codes or by the application itself, and a later contract does
1754
+ * not start the discount again.
1755
+ */
1756
+ async redeemedCodeNotYetRecorded(tenantId, planPriceNet, billingCycle, vatRate) {
1757
+ if (!this.promoCodes || !this.subscriptions) return null;
1758
+ const subscription = await this.subscriptions.findForTenant(tenantId);
1759
+ if (!subscription?.id) return null;
1760
+ const redeemed = await this.promoCodes.redeemedCodeFor(subscription.id);
1761
+ if (!redeemed) return null;
1762
+ const recorded = await this.contracts.list({
1763
+ tenantId
1764
+ });
1765
+ if (recorded.some((contract) => recordsPromoCode(contract, redeemed.code) || contract.createdAt > redeemed.redeemedAt)) {
1766
+ return null;
1767
+ }
1768
+ const cycle = billingCycle === "YEARLY" ? "yearly" : "monthly";
1769
+ const snapshot = {
1770
+ code: redeemed.code,
1771
+ label: buildLabel(redeemed, billingCycle),
1772
+ valueType: redeemed.valueType,
1773
+ value: Number(redeemed.value),
1774
+ resolvedAmountNet: promoCodeDiscountNet(planPriceNet, vatRate, redeemed),
1775
+ durationType: redeemed.durationType,
1776
+ durationValue: redeemed.durationValue
1777
+ };
1778
+ const line = generatedDiscountLine({
1779
+ billingCycle: cycle,
1780
+ promoCodeSnapshot: snapshot,
1781
+ promotionSnapshots: []
1782
+ }, snapshot.resolvedAmountNet);
1783
+ return {
1784
+ snapshot,
1785
+ line: {
1786
+ kind: line.kind,
1787
+ sourceKey: line.sourceKey,
1788
+ sourceVersionId: line.sourceVersionId ?? null,
1789
+ titleSnapshot: line.titleSnapshot,
1790
+ descriptionSnapshot: line.descriptionSnapshot ?? null,
1791
+ quantity: line.quantity,
1792
+ unit: line.unit ?? null,
1793
+ priceNet: line.priceNet,
1794
+ billingCycle: cycle,
1795
+ minimumTermUntil: null,
1796
+ featuresSnapshot: [],
1797
+ quotaEffectsSnapshot: {},
1798
+ metadata: line.metadata ?? null
1799
+ }
1800
+ };
1801
+ }
1657
1802
  };
1658
1803
  SubscriptionContractFreezeService = _ts_decorate6([
1659
1804
  Injectable4(),
@@ -1663,18 +1808,76 @@ SubscriptionContractFreezeService = _ts_decorate6([
1663
1808
  _ts_param4(3, Inject4(CONTRACT_FREEZE_SOURCE_PORT_TOKEN)),
1664
1809
  _ts_param4(4, Optional4()),
1665
1810
  _ts_param4(4, Inject4(SUBSCRIPTION_WRITE_PORT_TOKEN)),
1811
+ _ts_param4(5, Optional4()),
1812
+ _ts_param4(5, Inject4(SUBSCRIPTION_USAGE_PORT_TOKEN)),
1813
+ _ts_param4(6, Optional4()),
1814
+ _ts_param4(6, Inject4(PromoCodesService)),
1666
1815
  _ts_metadata4("design:type", Function),
1667
1816
  _ts_metadata4("design:paramtypes", [
1668
1817
  typeof PlanCatalogSource === "undefined" ? Object : PlanCatalogSource,
1669
1818
  typeof EntitlementService === "undefined" ? Object : EntitlementService,
1670
1819
  typeof SubscriptionContractService === "undefined" ? Object : SubscriptionContractService,
1671
1820
  typeof ContractFreezeSourcePort === "undefined" ? Object : ContractFreezeSourcePort,
1821
+ Object,
1822
+ Object,
1672
1823
  Object
1673
1824
  ])
1674
1825
  ], SubscriptionContractFreezeService);
1826
+ function recordsPromoCode(contract, code) {
1827
+ return contract.promoCodeSnapshots.some((snapshot) => snapshot !== null && typeof snapshot === "object" && snapshot.code === code);
1828
+ }
1829
+ __name(recordsPromoCode, "recordsPromoCode");
1675
1830
 
1676
- // src/billing/dto/tenant-billing.dto.ts
1677
- import { IsISO8601, IsOptional, IsString, Matches } from "class-validator";
1831
+ // src/billing/charges/subscriber-charge.tokens.ts
1832
+ var SUBSCRIBER_LEDGER_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/SubscriberLedgerRepository");
1833
+
1834
+ // src/billing/charges/subscriber-account.service.ts
1835
+ import { Inject as Inject5, Injectable as Injectable5 } from "@nestjs/common";
1836
+
1837
+ // src/billing/charges/subscriber-account.ts
1838
+ var SOURCE_ORDER = {
1839
+ plan: 0,
1840
+ bundle: 1,
1841
+ discount: 2
1842
+ };
1843
+ function toWire(charge) {
1844
+ return {
1845
+ ...charge,
1846
+ periodStart: charge.periodStart.toISOString(),
1847
+ periodEnd: charge.periodEnd.toISOString(),
1848
+ bookedAt: charge.bookedAt.toISOString(),
1849
+ createdAt: charge.createdAt.toISOString()
1850
+ };
1851
+ }
1852
+ __name(toWire, "toWire");
1853
+ function newestFirst(a, b) {
1854
+ return b.bookedAt.getTime() - a.bookedAt.getTime() || b.periodStart.getTime() - a.periodStart.getTime() || SOURCE_ORDER[a.source] - SOURCE_ORDER[b.source] || a.id.localeCompare(b.id);
1855
+ }
1856
+ __name(newestFirst, "newestFirst");
1857
+ function accountViewOf(input) {
1858
+ const titles = /* @__PURE__ */ new Map();
1859
+ for (const contract of input.contracts) {
1860
+ for (const line of contract.lineItems) titles.set(line.id, line.titleSnapshot);
1861
+ }
1862
+ const entries = [
1863
+ ...input.charges
1864
+ ].sort(newestFirst).map((charge) => ({
1865
+ charge: toWire(charge),
1866
+ title: titles.get(charge.contractLineItemId) ?? null
1867
+ }));
1868
+ const subscriber = input.subscriber;
1869
+ return {
1870
+ holder: subscriber ? {
1871
+ id: subscriber.id,
1872
+ customerNumber: subscriber.customerNumber,
1873
+ legalName: subscriber.legalName
1874
+ } : null,
1875
+ entries
1876
+ };
1877
+ }
1878
+ __name(accountViewOf, "accountViewOf");
1879
+
1880
+ // src/billing/charges/subscriber-account.service.ts
1678
1881
  function _ts_decorate7(decorators, target, key, desc) {
1679
1882
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1680
1883
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1686,6 +1889,863 @@ function _ts_metadata5(k, v) {
1686
1889
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1687
1890
  }
1688
1891
  __name(_ts_metadata5, "_ts_metadata");
1892
+ function _ts_param5(paramIndex, decorator) {
1893
+ return function(target, key) {
1894
+ decorator(target, key, paramIndex);
1895
+ };
1896
+ }
1897
+ __name(_ts_param5, "_ts_param");
1898
+ var SubscriberAccountService = class {
1899
+ static {
1900
+ __name(this, "SubscriberAccountService");
1901
+ }
1902
+ ledger;
1903
+ contracts;
1904
+ subscribers;
1905
+ subscriptions;
1906
+ constructor(ledger, contracts, subscribers, subscriptions) {
1907
+ this.ledger = ledger;
1908
+ this.contracts = contracts;
1909
+ this.subscribers = subscribers;
1910
+ this.subscriptions = subscriptions;
1911
+ }
1912
+ /** The account of the tenant's subscriber; empty while it has no subscription. */
1913
+ async accountOf(tenantId) {
1914
+ const [subscription, subscriber, contracts] = await Promise.all([
1915
+ this.subscriptions.findForTenant(tenantId),
1916
+ this.subscribers.findByTenantId(tenantId),
1917
+ this.contracts.list({
1918
+ tenantId
1919
+ })
1920
+ ]);
1921
+ const charges = subscription?.id ? await this.ledger.listBySubscription(subscription.id) : [];
1922
+ return accountViewOf({
1923
+ subscriber,
1924
+ charges,
1925
+ contracts
1926
+ });
1927
+ }
1928
+ };
1929
+ SubscriberAccountService = _ts_decorate7([
1930
+ Injectable5(),
1931
+ _ts_param5(0, Inject5(SUBSCRIBER_LEDGER_REPOSITORY_TOKEN)),
1932
+ _ts_param5(1, Inject5(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
1933
+ _ts_param5(2, Inject5(SUBSCRIBER_REPOSITORY_TOKEN)),
1934
+ _ts_param5(3, Inject5(SUBSCRIPTION_USAGE_PORT_TOKEN)),
1935
+ _ts_metadata5("design:type", Function),
1936
+ _ts_metadata5("design:paramtypes", [
1937
+ typeof SubscriberLedgerRepository === "undefined" ? Object : SubscriberLedgerRepository,
1938
+ typeof SubscriptionContractRepository === "undefined" ? Object : SubscriptionContractRepository,
1939
+ typeof SubscriberRepository === "undefined" ? Object : SubscriberRepository,
1940
+ typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort
1941
+ ])
1942
+ ], SubscriberAccountService);
1943
+
1944
+ // src/billing/charges/subscriber-account.module.ts
1945
+ import { Controller as Controller2, Get as Get2, Inject as Inject6, Module as Module2, Param, UseGuards } from "@nestjs/common";
1946
+ function _ts_decorate8(decorators, target, key, desc) {
1947
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1948
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1949
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1950
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1951
+ }
1952
+ __name(_ts_decorate8, "_ts_decorate");
1953
+ function _ts_metadata6(k, v) {
1954
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1955
+ }
1956
+ __name(_ts_metadata6, "_ts_metadata");
1957
+ function _ts_param6(paramIndex, decorator) {
1958
+ return function(target, key) {
1959
+ decorator(target, key, paramIndex);
1960
+ };
1961
+ }
1962
+ __name(_ts_param6, "_ts_param");
1963
+ function buildSubscriberAccountController(guards) {
1964
+ let GeneratedSubscriberAccountController = class GeneratedSubscriberAccountController {
1965
+ static {
1966
+ __name(this, "GeneratedSubscriberAccountController");
1967
+ }
1968
+ tenants;
1969
+ accounts;
1970
+ rlsBypass;
1971
+ constructor(tenants, accounts, rlsBypass) {
1972
+ this.tenants = tenants;
1973
+ this.accounts = accounts;
1974
+ this.rlsBypass = rlsBypass;
1975
+ }
1976
+ /**
1977
+ * The charges of the tenant's subscriber. An operator's request is
1978
+ * scoped to no tenant, so finding the tenant and reading its account
1979
+ * both run outside any row-level policy the application keeps its
1980
+ * tenants apart with.
1981
+ */
1982
+ chargesOf(slug) {
1983
+ const read = /* @__PURE__ */ __name(async () => {
1984
+ const tenant = await this.tenants.getTenantDetail(slug);
1985
+ return this.accounts.accountOf(tenant.id);
1986
+ }, "read");
1987
+ return this.rlsBypass.runWithBypass(read);
1988
+ }
1989
+ };
1990
+ _ts_decorate8([
1991
+ Get2("tenants/:slug/charges"),
1992
+ _ts_param6(0, Param("slug")),
1993
+ _ts_metadata6("design:type", Function),
1994
+ _ts_metadata6("design:paramtypes", [
1995
+ String
1996
+ ]),
1997
+ _ts_metadata6("design:returntype", typeof Promise === "undefined" ? Object : Promise)
1998
+ ], GeneratedSubscriberAccountController.prototype, "chargesOf", null);
1999
+ GeneratedSubscriberAccountController = _ts_decorate8([
2000
+ Controller2("admin"),
2001
+ UseGuards(...guards),
2002
+ _ts_param6(0, Inject6(AdminResourcesService)),
2003
+ _ts_param6(1, Inject6(SubscriberAccountService)),
2004
+ _ts_param6(2, Inject6(RLS_BYPASS_PORT_TOKEN)),
2005
+ _ts_metadata6("design:type", Function),
2006
+ _ts_metadata6("design:paramtypes", [
2007
+ typeof AdminResourcesService === "undefined" ? Object : AdminResourcesService,
2008
+ typeof SubscriberAccountService === "undefined" ? Object : SubscriberAccountService,
2009
+ typeof RlsBypassPort === "undefined" ? Object : RlsBypassPort
2010
+ ])
2011
+ ], GeneratedSubscriberAccountController);
2012
+ return GeneratedSubscriberAccountController;
2013
+ }
2014
+ __name(buildSubscriberAccountController, "buildSubscriberAccountController");
2015
+ var SubscriberAccountModule = class _SubscriberAccountModule {
2016
+ static {
2017
+ __name(this, "SubscriberAccountModule");
2018
+ }
2019
+ static forRoot(options) {
2020
+ return {
2021
+ module: _SubscriberAccountModule,
2022
+ imports: options.imports,
2023
+ controllers: [
2024
+ buildSubscriberAccountController(options.guards)
2025
+ ]
2026
+ };
2027
+ }
2028
+ };
2029
+ SubscriberAccountModule = _ts_decorate8([
2030
+ Module2({})
2031
+ ], SubscriberAccountModule);
2032
+
2033
+ // src/billing/charges/subscriber-charge.service.ts
2034
+ import { Inject as Inject7, Injectable as Injectable6, Logger as Logger2, Optional as Optional5 } from "@nestjs/common";
2035
+
2036
+ // src/billing/charges/charge-derivation.ts
2037
+ var PERIOD_ORIGINS = [
2038
+ "activation",
2039
+ "renewal",
2040
+ "bundleBooking",
2041
+ "planChange"
2042
+ ];
2043
+ var MAX_CYCLES = 600;
2044
+ var CENTS = 100;
2045
+ function deriveDueCharges(input) {
2046
+ const planCharges = derivePlanCharges(input);
2047
+ const periods = [
2048
+ ...writtenPlanPeriods(input),
2049
+ ...planCharges.map(({ period }) => period)
2050
+ ];
2051
+ return [
2052
+ ...planCharges.map(({ charge }) => charge),
2053
+ ...deriveUpgradeDifferences(input, periods),
2054
+ ...deriveDiscountCharges(input, planCharges, periods),
2055
+ ...deriveBundleCharges(input, accountStart(input))
2056
+ ];
2057
+ }
2058
+ __name(deriveDueCharges, "deriveDueCharges");
2059
+ function bookingsTheContractMisses(contracts, bookings, now) {
2060
+ const inForce = contractInForce(contracts, now);
2061
+ if (!inForce) return [];
2062
+ return bookings.filter((booking) => inForce.effectiveFrom < booking.startedAt && booking.currentPeriodEnd !== null && (booking.canceledEffectiveAt === null || booking.canceledEffectiveAt > now) && !inForce.lineItems.some((item) => item.kind === "bundle" && item.sourceVersionId === booking.bundleVersionId));
2063
+ }
2064
+ __name(bookingsTheContractMisses, "bookingsTheContractMisses");
2065
+ function derivePlanCharges(input) {
2066
+ const { subscription } = input;
2067
+ if (subscription.status === "TRIAL") return [];
2068
+ const window = windowOf(subscription.currentPeriodStart, subscription.currentPeriodEnd);
2069
+ if (!window) return [];
2070
+ const rhythm = rhythmOf(subscription.billingCycle);
2071
+ const isPlanLine = /* @__PURE__ */ __name((item) => item.kind === "plan" && item.billingCycle === rhythm, "isPlanLine");
2072
+ const replaced = periodReplacedBy(window, input);
2073
+ if (replaced) return newPeriodAfterChange(input, window, replaced, isPlanLine);
2074
+ const priced = periodsToCharge({
2075
+ window,
2076
+ from: lastPeriodEnd(input.written, "plan", subscription.id) ?? window.start,
2077
+ cycle: subscription.billingCycle,
2078
+ anchorDay: subscription.anchorDay,
2079
+ input,
2080
+ endsAt: subscription.endsAt
2081
+ }).flatMap((period) => {
2082
+ const found = lineFor(input.contracts, period, isPlanLine);
2083
+ return found ? [
2084
+ {
2085
+ period,
2086
+ ...found
2087
+ }
2088
+ ] : [];
2089
+ });
2090
+ const firstContract = earliest2(input.contracts);
2091
+ const opensSubscription = /* @__PURE__ */ __name((period) => holds(period, subscription.startedAt) || holds(period, firstContract?.effectiveFrom ?? null), "opensSubscription");
2092
+ return priced.map(({ period, contract, line }) => {
2093
+ const charge = chargeOf(input, contract, line, {
2094
+ origin: opensSubscription(period) ? "activation" : "renewal",
2095
+ source: "plan",
2096
+ sourceRef: subscription.id,
2097
+ period,
2098
+ amountNet: line.priceNet,
2099
+ bookedAt: period.start
2100
+ });
2101
+ return {
2102
+ charge,
2103
+ period: {
2104
+ charge,
2105
+ rhythm
2106
+ }
2107
+ };
2108
+ });
2109
+ }
2110
+ __name(derivePlanCharges, "derivePlanCharges");
2111
+ function periodReplacedBy(window, input) {
2112
+ const whole = input.written.filter((charge) => isWholePlanPeriod(charge, input.written, input.subscription.id));
2113
+ return whole.filter((charge) => charge.periodStart < window.start && window.start < charge.periodEnd).sort((a, b) => a.periodStart.getTime() - b.periodStart.getTime())[0] ?? null;
2114
+ }
2115
+ __name(periodReplacedBy, "periodReplacedBy");
2116
+ function newPeriodAfterChange(input, window, replaced, isPlanLine) {
2117
+ const { subscription } = input;
2118
+ if (!isChargeable(window, input, subscription.endsAt)) return [];
2119
+ const found = lineFor(input.contracts, window, isPlanLine);
2120
+ if (!found) return [];
2121
+ const charge = chargeOf(input, found.contract, found.line, {
2122
+ origin: "planChange",
2123
+ source: "plan",
2124
+ sourceRef: subscription.id,
2125
+ period: window,
2126
+ amountNet: computeNewPeriodCharge({
2127
+ periodStart: replaced.periodStart,
2128
+ periodEnd: replaced.periodEnd,
2129
+ now: window.start,
2130
+ currentPriceNet: pricePaidBefore(window.start, replaced, input.contracts),
2131
+ targetPriceNet: found.line.priceNet
2132
+ }).prorataDeltaNet,
2133
+ bookedAt: window.start
2134
+ });
2135
+ return [
2136
+ {
2137
+ charge,
2138
+ period: {
2139
+ charge,
2140
+ rhythm: rhythmOf(subscription.billingCycle)
2141
+ }
2142
+ }
2143
+ ];
2144
+ }
2145
+ __name(newPeriodAfterChange, "newPeriodAfterChange");
2146
+ function pricePaidBefore(at, replaced, contracts) {
2147
+ const chargedUnder = lineById(contracts, replaced.contractLineItemId);
2148
+ const rhythm = chargedUnder?.billingCycle;
2149
+ const before = contractInForce(contracts, new Date(at.getTime() - 1));
2150
+ const line = before?.lineItems.find((item) => item.kind === "plan" && item.billingCycle === rhythm);
2151
+ return line?.priceNet ?? chargedUnder?.priceNet ?? replaced.amountNet;
2152
+ }
2153
+ __name(pricePaidBefore, "pricePaidBefore");
2154
+ function deriveUpgradeDifferences(input, periods) {
2155
+ const { subscription } = input;
2156
+ const charges = [];
2157
+ for (const { charge: period, rhythm } of periods) {
2158
+ const isPlanLine = /* @__PURE__ */ __name((item) => item.kind === "plan" && item.billingCycle === rhythm, "isPlanLine");
2159
+ for (const contract of input.contracts) {
2160
+ const at = contract.effectiveFrom;
2161
+ if (contract.status === "scheduled") continue;
2162
+ if (!(period.periodStart < at && at < period.periodEnd)) continue;
2163
+ if (!isChargeable({
2164
+ start: at,
2165
+ end: period.periodEnd
2166
+ }, input, subscription.endsAt)) {
2167
+ continue;
2168
+ }
2169
+ const line = contract.lineItems.find(isPlanLine);
2170
+ const before = contractInForce(input.contracts, new Date(at.getTime() - 1));
2171
+ const lineBefore = before?.lineItems.find(isPlanLine);
2172
+ if (!line || !lineBefore) continue;
2173
+ const difference = computeProration({
2174
+ periodStart: period.periodStart,
2175
+ periodEnd: period.periodEnd,
2176
+ now: at,
2177
+ currentPriceNet: lineBefore.priceNet,
2178
+ targetPriceNet: line.priceNet
2179
+ }).prorataDeltaNet;
2180
+ if (difference <= 0) continue;
2181
+ charges.push(chargeOf(input, contract, line, {
2182
+ origin: "planChange",
2183
+ source: "plan",
2184
+ sourceRef: subscription.id,
2185
+ period: {
2186
+ start: at,
2187
+ end: period.periodEnd
2188
+ },
2189
+ amountNet: difference,
2190
+ bookedAt: at
2191
+ }));
2192
+ }
2193
+ }
2194
+ return charges;
2195
+ }
2196
+ __name(deriveUpgradeDifferences, "deriveUpgradeDifferences");
2197
+ function writtenPlanPeriods(input) {
2198
+ return input.written.flatMap((charge) => {
2199
+ if (!isWholePlanPeriod(charge, input.written, input.subscription.id)) return [];
2200
+ const line = lineById(input.contracts, charge.contractLineItemId);
2201
+ return line ? [
2202
+ {
2203
+ charge,
2204
+ rhythm: line.billingCycle
2205
+ }
2206
+ ] : [];
2207
+ });
2208
+ }
2209
+ __name(writtenPlanPeriods, "writtenPlanPeriods");
2210
+ function isWholePlanPeriod(charge, written, subscriptionId) {
2211
+ if (charge.source !== "plan" || charge.sourceRef !== subscriptionId) return false;
2212
+ if (!PERIOD_ORIGINS.includes(charge.origin)) return false;
2213
+ return charge.origin !== "planChange" || !isDifference(charge, written);
2214
+ }
2215
+ __name(isWholePlanPeriod, "isWholePlanPeriod");
2216
+ function isDifference(charge, written) {
2217
+ return written.some((other) => other.source === "plan" && other.sourceRef === charge.sourceRef && other.periodStart < charge.periodStart && sameInstant(other.periodEnd, charge.periodEnd));
2218
+ }
2219
+ __name(isDifference, "isDifference");
2220
+ function accountStart(input) {
2221
+ const { subscription } = input;
2222
+ const firstCharged = firstPlanPeriodStart(input.written, subscription.id);
2223
+ if (firstCharged) return firstCharged;
2224
+ if (subscription.status === "TRIAL") return null;
2225
+ return windowOf(subscription.currentPeriodStart, subscription.currentPeriodEnd)?.start ?? null;
2226
+ }
2227
+ __name(accountStart, "accountStart");
2228
+ function deriveBundleCharges(input, accountBegins) {
2229
+ if (!accountBegins) return [];
2230
+ const { subscription } = input;
2231
+ const anchorDay = subscription.anchorDay;
2232
+ const charges = [];
2233
+ for (const booking of input.bookings) {
2234
+ const window = windowOf(booking.currentPeriodStart, booking.currentPeriodEnd);
2235
+ if (!window) continue;
2236
+ const cycle = booking.billingCycle ?? subscription.billingCycle;
2237
+ const endsAt = earlierOf(booking.canceledEffectiveAt, subscription.endsAt);
2238
+ const lastEnd = lastPeriodEnd(input.written, "bundle", booking.id);
2239
+ const chainStart = laterOf(booking.startedAt, accountBegins);
2240
+ const periods = lastEnd ? periodsToCharge({
2241
+ window,
2242
+ from: lastEnd,
2243
+ cycle,
2244
+ anchorDay,
2245
+ input,
2246
+ endsAt
2247
+ }) : [
2248
+ ...firstBundlePeriod(window, chainStart, cycle, anchorDay),
2249
+ ...periodsToCharge({
2250
+ window,
2251
+ from: firstBoundaryAfter(window, chainStart, cycle, anchorDay),
2252
+ cycle,
2253
+ anchorDay,
2254
+ input,
2255
+ endsAt
2256
+ })
2257
+ ].filter((period) => isChargeable(period, input, endsAt));
2258
+ for (const period of periods) {
2259
+ const priced = lineFor(input.contracts, period, (item) => item.kind === "bundle" && item.sourceVersionId === booking.bundleVersionId);
2260
+ if (!priced) continue;
2261
+ const { contract, line } = priced;
2262
+ charges.push(chargeOf(input, contract, line, {
2263
+ origin: !lastEnd && sameInstant(period.start, chainStart) ? "bundleBooking" : "renewal",
2264
+ source: "bundle",
2265
+ sourceRef: booking.id,
2266
+ period,
2267
+ // Every period is charged for its share of the whole cycle
2268
+ // it ends, which is all of it except for the short first
2269
+ // one — the same arithmetic the preview quoted
2270
+ // (`SC-BUN-003`, `SC-PRIC-002`).
2271
+ amountNet: computeProration({
2272
+ periodStart: bundleFirstPeriodStart(period.end, cycle, anchorDay),
2273
+ periodEnd: period.end,
2274
+ now: period.start,
2275
+ currentPriceNet: 0,
2276
+ targetPriceNet: line.priceNet
2277
+ }).prorataDeltaNet,
2278
+ bookedAt: period.start
2279
+ }));
2280
+ }
2281
+ }
2282
+ return charges;
2283
+ }
2284
+ __name(deriveBundleCharges, "deriveBundleCharges");
2285
+ function deriveDiscountCharges(input, due, periods) {
2286
+ const { subscription } = input;
2287
+ const charges = [];
2288
+ for (const { contract, line } of discountsWhereAgreed(input.contracts)) {
2289
+ const rhythm = line.billingCycle;
2290
+ const cycle = rhythm === "yearly" ? "YEARLY" : "MONTHLY";
2291
+ const first = firstPeriodConcludedFor(contract, periods);
2292
+ if (!first) continue;
2293
+ const anchorDay = rhythm === rhythmOf(subscription.billingCycle) ? subscription.anchorDay : first.getUTCDate();
2294
+ const beganInItsRhythm = periods.some((period) => period.rhythm === rhythm && sameInstant(period.charge.periodStart, first));
2295
+ const endedAt = beganInItsRhythm ? firstPeriodStart(periods.filter((period) => period.rhythm !== rhythm && period.charge.periodStart > first)) : first;
2296
+ for (const { charge: planCharge, period } of due) {
2297
+ const amount = endedAt && planCharge.periodStart > endedAt ? 0 : period.rhythm === rhythm ? discountInPeriod(line, first, planCharge.periodStart, cycle, anchorDay) : sameInstant(planCharge.periodStart, endedAt) ? (
2298
+ // period, and no more than it costs.
2299
+ Math.min(discountLeftAt(line, first, planCharge.periodStart, cycle, anchorDay), planCharge.amountNet)
2300
+ ) : 0;
2301
+ if (amount <= 0) continue;
2302
+ charges.push(chargeOf(input, contract, line, {
2303
+ origin: planCharge.origin,
2304
+ source: "discount",
2305
+ sourceRef: line.sourceKey,
2306
+ period: {
2307
+ start: planCharge.periodStart,
2308
+ end: planCharge.periodEnd
2309
+ },
2310
+ amountNet: -amount,
2311
+ bookedAt: planCharge.bookedAt
2312
+ }));
2313
+ }
2314
+ }
2315
+ return charges;
2316
+ }
2317
+ __name(deriveDiscountCharges, "deriveDiscountCharges");
2318
+ function discountInPeriod(line, first, periodStart, cycle, anchorDay) {
2319
+ const index = cyclesBetween(first, periodStart, cycle, anchorDay);
2320
+ return index === null ? 0 : discountFor(line, index, first, periodStart, anchorDay);
2321
+ }
2322
+ __name(discountInPeriod, "discountInPeriod");
2323
+ function discountLeftAt(line, first, at, cycle, anchorDay) {
2324
+ let cents = 0;
2325
+ let periodStart = first;
2326
+ for (let index = 0; index < MAX_CYCLES; index++) {
2327
+ const amount = discountFor(line, index, first, periodStart, anchorDay);
2328
+ if (amount === 0) break;
2329
+ if (periodStart >= at) cents += toCents(amount);
2330
+ periodStart = advanceOneCycle(periodStart, cycle, anchorDay ?? void 0);
2331
+ }
2332
+ return cents / CENTS;
2333
+ }
2334
+ __name(discountLeftAt, "discountLeftAt");
2335
+ function discountsWhereAgreed(contracts) {
2336
+ const agreed = /* @__PURE__ */ new Map();
2337
+ for (const contract of [
2338
+ ...contracts
2339
+ ].sort(byEffectiveFrom)) {
2340
+ for (const line of contract.lineItems) {
2341
+ if (line.kind !== "discount" || agreed.has(line.sourceKey)) continue;
2342
+ agreed.set(line.sourceKey, {
2343
+ contract,
2344
+ line
2345
+ });
2346
+ }
2347
+ }
2348
+ return [
2349
+ ...agreed.values()
2350
+ ];
2351
+ }
2352
+ __name(discountsWhereAgreed, "discountsWhereAgreed");
2353
+ function discountFor(line, index, first, periodStart, anchorDay) {
2354
+ const snapshots = discountSnapshotsOf(line);
2355
+ if (!snapshots) {
2356
+ return index === 0 ? -line.priceNet : 0;
2357
+ }
2358
+ const withinMonths = /* @__PURE__ */ __name((months) => periodStart < monthsAfter(first, months, anchorDay), "withinMonths");
2359
+ let cents = 0;
2360
+ for (const promotion of snapshots.promotions) {
2361
+ const months = promotionMonths(promotion.value);
2362
+ const applies = promotion.type === "intro" || promotion.type === "freeMonths" ? months !== null && withinMonths(months) : index === 0;
2363
+ if (applies) cents += toCents(promotion.resolvedAmountNet);
2364
+ }
2365
+ const code = snapshots.promoCode;
2366
+ if (code) {
2367
+ const applies = code.durationType === "MONTHS" ? withinMonths(code.durationValue ?? 0) : code.durationType === "BILLING_CYCLES" ? index < (code.durationValue ?? 0) : index === 0;
2368
+ if (applies) cents += toCents(code.resolvedAmountNet);
2369
+ }
2370
+ return cents / CENTS;
2371
+ }
2372
+ __name(discountFor, "discountFor");
2373
+ function discountSnapshotsOf(line) {
2374
+ const metadata = line.metadata;
2375
+ if (!isRecord(metadata) || metadata.generated !== true) return null;
2376
+ const promotions = Array.isArray(metadata.promotionSnapshots) ? metadata.promotionSnapshots.filter(isRecord).map((snapshot) => ({
2377
+ type: typeof snapshot.type === "string" ? snapshot.type : "",
2378
+ value: snapshot.value,
2379
+ resolvedAmountNet: numberOr0(snapshot.resolvedAmountNet)
2380
+ })) : [];
2381
+ const code = isRecord(metadata.promoCodeSnapshot) ? metadata.promoCodeSnapshot : null;
2382
+ return {
2383
+ promotions,
2384
+ promoCode: code ? {
2385
+ durationType: typeof code.durationType === "string" ? code.durationType : null,
2386
+ durationValue: typeof code.durationValue === "number" ? code.durationValue : null,
2387
+ resolvedAmountNet: numberOr0(code.resolvedAmountNet)
2388
+ } : null
2389
+ };
2390
+ }
2391
+ __name(discountSnapshotsOf, "discountSnapshotsOf");
2392
+ function promotionMonths(value) {
2393
+ if (typeof value === "number") return value;
2394
+ if (isRecord(value) && typeof value.months === "number") return value.months;
2395
+ return null;
2396
+ }
2397
+ __name(promotionMonths, "promotionMonths");
2398
+ function periodsToCharge(args) {
2399
+ const { window, from, cycle, anchorDay, input, endsAt } = args;
2400
+ const periods = [];
2401
+ let start = from;
2402
+ for (let step = 0; step < MAX_CYCLES && start < window.start; step++) {
2403
+ const end = advanceOneCycle(start, cycle, anchorDay ?? void 0);
2404
+ if (end > window.start) break;
2405
+ periods.push({
2406
+ start,
2407
+ end
2408
+ });
2409
+ start = end;
2410
+ }
2411
+ if (from <= window.start) periods.push(window);
2412
+ return periods.filter((period) => isChargeable(period, input, endsAt));
2413
+ }
2414
+ __name(periodsToCharge, "periodsToCharge");
2415
+ function isChargeable(period, input, endsAt) {
2416
+ return period.start < period.end && period.start <= input.now && (!endsAt || period.start < endsAt);
2417
+ }
2418
+ __name(isChargeable, "isChargeable");
2419
+ function firstBoundaryAfter(window, start, cycle, anchorDay) {
2420
+ if (window.start <= start) return window.end;
2421
+ let boundary = window.start;
2422
+ for (let step = 0; step < MAX_CYCLES; step++) {
2423
+ const before = retreatOneCycle(boundary, cycle, anchorDay ?? void 0);
2424
+ if (before <= start) break;
2425
+ boundary = before;
2426
+ }
2427
+ return boundary;
2428
+ }
2429
+ __name(firstBoundaryAfter, "firstBoundaryAfter");
2430
+ function firstBundlePeriod(window, start, cycle, anchorDay) {
2431
+ return [
2432
+ {
2433
+ start,
2434
+ end: firstBoundaryAfter(window, start, cycle, anchorDay)
2435
+ }
2436
+ ];
2437
+ }
2438
+ __name(firstBundlePeriod, "firstBundlePeriod");
2439
+ function lastPeriodEnd(written, source, sourceRef) {
2440
+ let last = null;
2441
+ for (const charge of written) {
2442
+ if (charge.source !== source || charge.sourceRef !== sourceRef) continue;
2443
+ if (!PERIOD_ORIGINS.includes(charge.origin)) continue;
2444
+ if (!last || charge.periodEnd > last) last = charge.periodEnd;
2445
+ }
2446
+ return last;
2447
+ }
2448
+ __name(lastPeriodEnd, "lastPeriodEnd");
2449
+ function firstPlanPeriodStart(written, subscriptionId) {
2450
+ let first = null;
2451
+ for (const charge of written) {
2452
+ if (charge.source !== "plan" || charge.sourceRef !== subscriptionId) continue;
2453
+ if (!PERIOD_ORIGINS.includes(charge.origin)) continue;
2454
+ if (!first || charge.periodStart < first) first = charge.periodStart;
2455
+ }
2456
+ return first;
2457
+ }
2458
+ __name(firstPlanPeriodStart, "firstPlanPeriodStart");
2459
+ function firstPeriodConcludedFor(concluded, periods) {
2460
+ const at = concluded.effectiveFrom;
2461
+ let first = null;
2462
+ for (const { charge } of periods) {
2463
+ if (charge.periodEnd <= at) continue;
2464
+ if (charge.periodStart < at && charge.contractId !== concluded.id) continue;
2465
+ if (!first || charge.periodStart < first) first = charge.periodStart;
2466
+ }
2467
+ return first;
2468
+ }
2469
+ __name(firstPeriodConcludedFor, "firstPeriodConcludedFor");
2470
+ function cyclesBetween(from, to, cycle, anchorDay) {
2471
+ let at = from;
2472
+ for (let step = 0; step < MAX_CYCLES; step++) {
2473
+ if (at.getTime() === to.getTime()) return step;
2474
+ if (at > to) return null;
2475
+ at = advanceOneCycle(at, cycle, anchorDay ?? void 0);
2476
+ }
2477
+ return null;
2478
+ }
2479
+ __name(cyclesBetween, "cyclesBetween");
2480
+ function monthsAfter(from, months, anchorDay) {
2481
+ let at = from;
2482
+ for (let step = 0; step < months; step++) {
2483
+ at = advanceOneCycle(at, "MONTHLY", anchorDay ?? from.getUTCDate());
2484
+ }
2485
+ return at;
2486
+ }
2487
+ __name(monthsAfter, "monthsAfter");
2488
+ function lineFor(contracts, period, matches) {
2489
+ const inForce = contractInForce(contracts, period.start);
2490
+ const inForceLine = inForce?.lineItems.find(matches);
2491
+ if (inForce && inForceLine) return {
2492
+ contract: inForce,
2493
+ line: inForceLine
2494
+ };
2495
+ for (const contract of [
2496
+ ...contracts
2497
+ ].sort(byEffectiveFrom)) {
2498
+ if (contract.status === "scheduled") continue;
2499
+ if (contract.effectiveFrom < period.start || contract.effectiveFrom >= period.end) continue;
2500
+ const line = contract.lineItems.find(matches);
2501
+ if (line) return {
2502
+ contract,
2503
+ line
2504
+ };
2505
+ }
2506
+ return null;
2507
+ }
2508
+ __name(lineFor, "lineFor");
2509
+ function contractInForce(contracts, at) {
2510
+ return [
2511
+ ...contracts
2512
+ ].filter((contract) => contract.status !== "scheduled" && contract.effectiveFrom <= at && (contract.effectiveUntil === null || at < contract.effectiveUntil)).sort(byEffectiveFrom).at(-1) ?? null;
2513
+ }
2514
+ __name(contractInForce, "contractInForce");
2515
+ function earliest2(contracts) {
2516
+ return [
2517
+ ...contracts
2518
+ ].sort(byEffectiveFrom)[0] ?? null;
2519
+ }
2520
+ __name(earliest2, "earliest");
2521
+ function byEffectiveFrom(a, b) {
2522
+ return a.effectiveFrom.getTime() - b.effectiveFrom.getTime();
2523
+ }
2524
+ __name(byEffectiveFrom, "byEffectiveFrom");
2525
+ function chargeOf(input, contract, line, charge) {
2526
+ return {
2527
+ subscriberId: input.subscriberId,
2528
+ tenantId: input.subscription.tenantId,
2529
+ subscriptionId: input.subscription.id,
2530
+ contractId: contract.id,
2531
+ contractLineItemId: line.id,
2532
+ origin: charge.origin,
2533
+ source: charge.source,
2534
+ sourceRef: charge.sourceRef,
2535
+ periodStart: charge.period.start,
2536
+ periodEnd: charge.period.end,
2537
+ currency: line.currency,
2538
+ // Rounded once, here, where it is derived (`SC-PRIC-018`).
2539
+ amountNet: toCents(charge.amountNet) / CENTS,
2540
+ bookedAt: charge.bookedAt
2541
+ };
2542
+ }
2543
+ __name(chargeOf, "chargeOf");
2544
+ function windowOf(start, end) {
2545
+ return start && end && start < end ? {
2546
+ start,
2547
+ end
2548
+ } : null;
2549
+ }
2550
+ __name(windowOf, "windowOf");
2551
+ function laterOf(a, b) {
2552
+ return b > a ? b : a;
2553
+ }
2554
+ __name(laterOf, "laterOf");
2555
+ function sameInstant(a, b) {
2556
+ return b !== null && a.getTime() === b.getTime();
2557
+ }
2558
+ __name(sameInstant, "sameInstant");
2559
+ function firstPeriodStart(periods) {
2560
+ return periods.reduce((first, { charge }) => !first || charge.periodStart < first ? charge.periodStart : first, null);
2561
+ }
2562
+ __name(firstPeriodStart, "firstPeriodStart");
2563
+ function holds(period, at) {
2564
+ return at !== null && period.start <= at && at < period.end;
2565
+ }
2566
+ __name(holds, "holds");
2567
+ function earlierOf(a, b) {
2568
+ if (!a) return b;
2569
+ if (!b) return a;
2570
+ return a < b ? a : b;
2571
+ }
2572
+ __name(earlierOf, "earlierOf");
2573
+ function rhythmOf(cycle) {
2574
+ return cycle === "YEARLY" ? "yearly" : "monthly";
2575
+ }
2576
+ __name(rhythmOf, "rhythmOf");
2577
+ function lineById(contracts, id) {
2578
+ for (const contract of contracts) {
2579
+ const line = contract.lineItems.find((item) => item.id === id);
2580
+ if (line) return line;
2581
+ }
2582
+ return null;
2583
+ }
2584
+ __name(lineById, "lineById");
2585
+ function toCents(amount) {
2586
+ return Math.round(amount * CENTS);
2587
+ }
2588
+ __name(toCents, "toCents");
2589
+ function numberOr0(value) {
2590
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
2591
+ }
2592
+ __name(numberOr0, "numberOr0");
2593
+ function isRecord(value) {
2594
+ return value !== null && typeof value === "object" && !Array.isArray(value);
2595
+ }
2596
+ __name(isRecord, "isRecord");
2597
+
2598
+ // src/billing/charges/subscriber-charge.service.ts
2599
+ function _ts_decorate9(decorators, target, key, desc) {
2600
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2601
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2602
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2603
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2604
+ }
2605
+ __name(_ts_decorate9, "_ts_decorate");
2606
+ function _ts_metadata7(k, v) {
2607
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2608
+ }
2609
+ __name(_ts_metadata7, "_ts_metadata");
2610
+ function _ts_param7(paramIndex, decorator) {
2611
+ return function(target, key) {
2612
+ decorator(target, key, paramIndex);
2613
+ };
2614
+ }
2615
+ __name(_ts_param7, "_ts_param");
2616
+ function planCycleOf(subscription) {
2617
+ return subscription.billingCycle;
2618
+ }
2619
+ __name(planCycleOf, "planCycleOf");
2620
+ function endOf(subscription) {
2621
+ return subscription.canceledEffectiveAt ?? subscription.canceledAt ?? null;
2622
+ }
2623
+ __name(endOf, "endOf");
2624
+ var SubscriberChargeService = class _SubscriberChargeService {
2625
+ static {
2626
+ __name(this, "SubscriberChargeService");
2627
+ }
2628
+ ledger;
2629
+ contracts;
2630
+ subscribers;
2631
+ subscriptions;
2632
+ bookings;
2633
+ freeze;
2634
+ logger = new Logger2(_SubscriberChargeService.name);
2635
+ constructor(ledger, contracts, subscribers, subscriptions, bookings = null, freeze = null) {
2636
+ this.ledger = ledger;
2637
+ this.contracts = contracts;
2638
+ this.subscribers = subscribers;
2639
+ this.subscriptions = subscriptions;
2640
+ this.bookings = bookings;
2641
+ this.freeze = freeze;
2642
+ }
2643
+ /**
2644
+ * Writes every charge the tenant's records give rise to by `now` that is
2645
+ * not written yet, and returns those.
2646
+ *
2647
+ * Nothing is charged without a contract (a charge points at a contract
2648
+ * line), in a trial, before the plan's current window when the account is
2649
+ * still empty, for a period that starts after `now`, or for one that
2650
+ * starts on or after the date a cancellation takes effect.
2651
+ *
2652
+ * A window that moved on before anything charged it is not charged
2653
+ * afterwards, so a job that renews periods calls this before it moves a
2654
+ * window, and moves it only when the call succeeded.
2655
+ */
2656
+ async recordDueCharges(tenantId, now = /* @__PURE__ */ new Date()) {
2657
+ const subscription = await this.subscriptions.findForTenant(tenantId);
2658
+ if (!subscription?.id) return [];
2659
+ const subscriber = await this.subscribers.findByTenantId(tenantId);
2660
+ if (!subscriber) return [];
2661
+ const [bookings, written] = await Promise.all([
2662
+ this.bookings?.listBySubscription(subscription.id) ?? [],
2663
+ this.ledger.listBySubscription(subscription.id)
2664
+ ]);
2665
+ const contracts = await this.contractsNamingEveryBooking(tenantId, subscription, bookings, now);
2666
+ const due = deriveDueCharges({
2667
+ now,
2668
+ subscriberId: subscriber.id,
2669
+ subscription: {
2670
+ id: subscription.id,
2671
+ tenantId,
2672
+ status: subscription.status,
2673
+ billingCycle: planCycleOf(subscription),
2674
+ currentPeriodStart: subscription.currentPeriodStart,
2675
+ currentPeriodEnd: subscription.currentPeriodEnd,
2676
+ anchorDay: resolvePlanAnchorDay(subscription),
2677
+ startedAt: subscription.startedAt,
2678
+ endsAt: endOf(subscription)
2679
+ },
2680
+ contracts,
2681
+ bookings,
2682
+ written
2683
+ });
2684
+ return this.ledger.recordCharges(due);
2685
+ }
2686
+ /**
2687
+ * The tenant's contracts, written once more where the one in force misses
2688
+ * a running booking.
2689
+ *
2690
+ * A booking writes its contract after it is saved, and a failure there does
2691
+ * not undo the booking. The booking would then stay uncharged for good,
2692
+ * since a charge points at a contract line — so the journal writes the
2693
+ * contract the way the booking would have. A failure here is logged and the
2694
+ * rest is charged: the next call tries again.
2695
+ */
2696
+ async contractsNamingEveryBooking(tenantId, subscription, bookings, now) {
2697
+ const contracts = await this.contracts.list({
2698
+ tenantId
2699
+ });
2700
+ const missed = bookingsTheContractMisses(contracts, bookings, now);
2701
+ if (missed.length === 0 || !this.freeze || subscription.status === "TRIAL" || cancellationHasLanded(subscription, now)) {
2702
+ return contracts;
2703
+ }
2704
+ try {
2705
+ await this.freeze.freezeOnPlanChange(tenantId, subscription.planVersion.planId, planCycleOf(subscription), now, endOf(subscription));
2706
+ } catch (err) {
2707
+ this.logger.error(`Writing the contract for ${missed.length} booking(s) it misses failed (tenant ${tenantId}): ${String(err)}`);
2708
+ return contracts;
2709
+ }
2710
+ return this.contracts.list({
2711
+ tenantId
2712
+ });
2713
+ }
2714
+ };
2715
+ SubscriberChargeService = _ts_decorate9([
2716
+ Injectable6(),
2717
+ _ts_param7(0, Inject7(SUBSCRIBER_LEDGER_REPOSITORY_TOKEN)),
2718
+ _ts_param7(1, Inject7(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
2719
+ _ts_param7(2, Inject7(SUBSCRIBER_REPOSITORY_TOKEN)),
2720
+ _ts_param7(3, Inject7(SUBSCRIPTION_USAGE_PORT_TOKEN)),
2721
+ _ts_param7(4, Optional5()),
2722
+ _ts_param7(4, Inject7(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
2723
+ _ts_param7(5, Optional5()),
2724
+ _ts_param7(5, Inject7(CONTRACT_FREEZE_PORT_TOKEN)),
2725
+ _ts_metadata7("design:type", Function),
2726
+ _ts_metadata7("design:paramtypes", [
2727
+ typeof SubscriberLedgerRepository === "undefined" ? Object : SubscriberLedgerRepository,
2728
+ typeof SubscriptionContractRepository === "undefined" ? Object : SubscriptionContractRepository,
2729
+ typeof SubscriberRepository === "undefined" ? Object : SubscriberRepository,
2730
+ typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort,
2731
+ Object,
2732
+ Object
2733
+ ])
2734
+ ], SubscriberChargeService);
2735
+
2736
+ // src/billing/dto/tenant-billing.dto.ts
2737
+ import { IsISO8601, IsOptional, IsString, Matches } from "class-validator";
2738
+ function _ts_decorate10(decorators, target, key, desc) {
2739
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2740
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2741
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2742
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2743
+ }
2744
+ __name(_ts_decorate10, "_ts_decorate");
2745
+ function _ts_metadata8(k, v) {
2746
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2747
+ }
2748
+ __name(_ts_metadata8, "_ts_metadata");
1689
2749
  var PLAN_OR_CYCLE_PATTERN = /^[A-Z][A-Z0-9_]*$/;
1690
2750
  var PreviewPlanChangeDto = class {
1691
2751
  static {
@@ -1694,19 +2754,19 @@ var PreviewPlanChangeDto = class {
1694
2754
  plan;
1695
2755
  billingCycle;
1696
2756
  };
1697
- _ts_decorate7([
2757
+ _ts_decorate10([
1698
2758
  IsString(),
1699
2759
  Matches(PLAN_OR_CYCLE_PATTERN, {
1700
2760
  message: "plan must be SCREAMING_SNAKE_CASE"
1701
2761
  }),
1702
- _ts_metadata5("design:type", String)
2762
+ _ts_metadata8("design:type", String)
1703
2763
  ], PreviewPlanChangeDto.prototype, "plan", void 0);
1704
- _ts_decorate7([
2764
+ _ts_decorate10([
1705
2765
  IsString(),
1706
2766
  Matches(PLAN_OR_CYCLE_PATTERN, {
1707
2767
  message: "billingCycle must be SCREAMING_SNAKE_CASE (e.g. MONTHLY, YEARLY)"
1708
2768
  }),
1709
- _ts_metadata5("design:type", String)
2769
+ _ts_metadata8("design:type", String)
1710
2770
  ], PreviewPlanChangeDto.prototype, "billingCycle", void 0);
1711
2771
  var ChangePlanDto = class extends PreviewPlanChangeDto {
1712
2772
  static {
@@ -1731,25 +2791,25 @@ var CancelSubscriptionDto = class {
1731
2791
  */
1732
2792
  expectedEffectiveAt;
1733
2793
  };
1734
- _ts_decorate7([
2794
+ _ts_decorate10([
1735
2795
  IsOptional(),
1736
2796
  IsISO8601(),
1737
- _ts_metadata5("design:type", String)
2797
+ _ts_metadata8("design:type", String)
1738
2798
  ], CancelSubscriptionDto.prototype, "expectedEffectiveAt", void 0);
1739
2799
 
1740
2800
  // src/billing/dto/onboarding-subscription.dto.ts
1741
2801
  import { ArrayMaxSize, IsArray, IsOptional as IsOptional2, IsString as IsString2, IsUUID, Matches as Matches2 } from "class-validator";
1742
- function _ts_decorate8(decorators, target, key, desc) {
2802
+ function _ts_decorate11(decorators, target, key, desc) {
1743
2803
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1744
2804
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1745
2805
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1746
2806
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1747
2807
  }
1748
- __name(_ts_decorate8, "_ts_decorate");
1749
- function _ts_metadata6(k, v) {
2808
+ __name(_ts_decorate11, "_ts_decorate");
2809
+ function _ts_metadata9(k, v) {
1750
2810
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1751
2811
  }
1752
- __name(_ts_metadata6, "_ts_metadata");
2812
+ __name(_ts_metadata9, "_ts_metadata");
1753
2813
  var PLAN_OR_CYCLE_PATTERN2 = /^[A-Z][A-Z0-9_]*$/;
1754
2814
  var PROMO_CODE_PATTERN = /^[A-Z0-9_-]{4,32}$/i;
1755
2815
  var CompleteOnboardingSubscriptionDto = class {
@@ -1769,52 +2829,52 @@ var CompleteOnboardingSubscriptionDto = class {
1769
2829
  */
1770
2830
  bundleVersionIds;
1771
2831
  };
1772
- _ts_decorate8([
2832
+ _ts_decorate11([
1773
2833
  IsString2(),
1774
2834
  Matches2(PLAN_OR_CYCLE_PATTERN2, {
1775
2835
  message: "plan must be SCREAMING_SNAKE_CASE"
1776
2836
  }),
1777
- _ts_metadata6("design:type", String)
2837
+ _ts_metadata9("design:type", String)
1778
2838
  ], CompleteOnboardingSubscriptionDto.prototype, "plan", void 0);
1779
- _ts_decorate8([
2839
+ _ts_decorate11([
1780
2840
  IsString2(),
1781
2841
  Matches2(PLAN_OR_CYCLE_PATTERN2, {
1782
2842
  message: "billingCycle must be SCREAMING_SNAKE_CASE (e.g. MONTHLY, YEARLY)"
1783
2843
  }),
1784
- _ts_metadata6("design:type", String)
2844
+ _ts_metadata9("design:type", String)
1785
2845
  ], CompleteOnboardingSubscriptionDto.prototype, "billingCycle", void 0);
1786
- _ts_decorate8([
2846
+ _ts_decorate11([
1787
2847
  IsOptional2(),
1788
2848
  IsString2(),
1789
2849
  Matches2(PROMO_CODE_PATTERN, {
1790
2850
  message: 'promoCode must consist of A\u2013Z, 0\u20139, "-", "_" (4\u201332 characters)'
1791
2851
  }),
1792
- _ts_metadata6("design:type", String)
2852
+ _ts_metadata9("design:type", String)
1793
2853
  ], CompleteOnboardingSubscriptionDto.prototype, "promoCode", void 0);
1794
- _ts_decorate8([
2854
+ _ts_decorate11([
1795
2855
  IsOptional2(),
1796
2856
  IsArray(),
1797
2857
  ArrayMaxSize(10),
1798
2858
  IsUUID("all", {
1799
2859
  each: true
1800
2860
  }),
1801
- _ts_metadata6("design:type", Array)
2861
+ _ts_metadata9("design:type", Array)
1802
2862
  ], CompleteOnboardingSubscriptionDto.prototype, "bundleVersionIds", void 0);
1803
2863
 
1804
2864
  // src/billing/dto/subscription-bundles.dto.ts
1805
2865
  import { ArrayMaxSize as ArrayMaxSize2, IsArray as IsArray2, IsIn, IsInt, IsOptional as IsOptional3, IsUUID as IsUUID2, Matches as Matches3, Max, Min } from "class-validator";
1806
2866
  import { BUNDLE_PRICE_LOOKUP_LIMIT } from "@saasicat/core";
1807
- function _ts_decorate9(decorators, target, key, desc) {
2867
+ function _ts_decorate12(decorators, target, key, desc) {
1808
2868
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1809
2869
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1810
2870
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1811
2871
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1812
2872
  }
1813
- __name(_ts_decorate9, "_ts_decorate");
1814
- function _ts_metadata7(k, v) {
2873
+ __name(_ts_decorate12, "_ts_decorate");
2874
+ function _ts_metadata10(k, v) {
1815
2875
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1816
2876
  }
1817
- __name(_ts_metadata7, "_ts_metadata");
2877
+ __name(_ts_metadata10, "_ts_metadata");
1818
2878
  var ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}(T.*)?$/;
1819
2879
  var AddSubscriptionBundleDto = class {
1820
2880
  static {
@@ -1839,24 +2899,24 @@ var AddSubscriptionBundleDto = class {
1839
2899
  */
1840
2900
  billingCycle;
1841
2901
  };
1842
- _ts_decorate9([
2902
+ _ts_decorate12([
1843
2903
  IsUUID2(),
1844
- _ts_metadata7("design:type", String)
2904
+ _ts_metadata10("design:type", String)
1845
2905
  ], AddSubscriptionBundleDto.prototype, "bundleVersionId", void 0);
1846
- _ts_decorate9([
2906
+ _ts_decorate12([
1847
2907
  IsOptional3(),
1848
2908
  IsInt(),
1849
2909
  Min(0),
1850
2910
  Max(120),
1851
- _ts_metadata7("design:type", Number)
2911
+ _ts_metadata10("design:type", Number)
1852
2912
  ], AddSubscriptionBundleDto.prototype, "minimumTermMonths", void 0);
1853
- _ts_decorate9([
2913
+ _ts_decorate12([
1854
2914
  IsOptional3(),
1855
2915
  IsIn([
1856
2916
  "MONTHLY",
1857
2917
  "YEARLY"
1858
2918
  ]),
1859
- _ts_metadata7("design:type", String)
2919
+ _ts_metadata10("design:type", String)
1860
2920
  ], AddSubscriptionBundleDto.prototype, "billingCycle", void 0);
1861
2921
  var PreviewSubscriptionBundleDto = class {
1862
2922
  static {
@@ -1880,30 +2940,30 @@ var PreviewSubscriptionBundleDto = class {
1880
2940
  */
1881
2941
  billingCycle;
1882
2942
  };
1883
- _ts_decorate9([
2943
+ _ts_decorate12([
1884
2944
  IsOptional3(),
1885
2945
  IsUUID2(),
1886
- _ts_metadata7("design:type", String)
2946
+ _ts_metadata10("design:type", String)
1887
2947
  ], PreviewSubscriptionBundleDto.prototype, "bundleVersionId", void 0);
1888
- _ts_decorate9([
2948
+ _ts_decorate12([
1889
2949
  IsOptional3(),
1890
2950
  IsUUID2(),
1891
- _ts_metadata7("design:type", String)
2951
+ _ts_metadata10("design:type", String)
1892
2952
  ], PreviewSubscriptionBundleDto.prototype, "subscriptionBundleId", void 0);
1893
- _ts_decorate9([
2953
+ _ts_decorate12([
1894
2954
  IsOptional3(),
1895
2955
  IsInt(),
1896
2956
  Min(0),
1897
2957
  Max(120),
1898
- _ts_metadata7("design:type", Number)
2958
+ _ts_metadata10("design:type", Number)
1899
2959
  ], PreviewSubscriptionBundleDto.prototype, "minimumTermMonths", void 0);
1900
- _ts_decorate9([
2960
+ _ts_decorate12([
1901
2961
  IsOptional3(),
1902
2962
  IsIn([
1903
2963
  "MONTHLY",
1904
2964
  "YEARLY"
1905
2965
  ]),
1906
- _ts_metadata7("design:type", String)
2966
+ _ts_metadata10("design:type", String)
1907
2967
  ], PreviewSubscriptionBundleDto.prototype, "billingCycle", void 0);
1908
2968
  var CancelSubscriptionBundleDto = class {
1909
2969
  static {
@@ -1916,12 +2976,12 @@ var CancelSubscriptionBundleDto = class {
1916
2976
  */
1917
2977
  canceledAt;
1918
2978
  };
1919
- _ts_decorate9([
2979
+ _ts_decorate12([
1920
2980
  IsOptional3(),
1921
2981
  Matches3(ISO_DATE_PATTERN, {
1922
2982
  message: "canceledAt must be an ISO date (YYYY-MM-DD)"
1923
2983
  }),
1924
- _ts_metadata7("design:type", String)
2984
+ _ts_metadata10("design:type", String)
1925
2985
  ], CancelSubscriptionBundleDto.prototype, "canceledAt", void 0);
1926
2986
  var BundlePriceLookupDto = class {
1927
2987
  static {
@@ -1933,35 +2993,35 @@ var BundlePriceLookupDto = class {
1933
2993
  */
1934
2994
  bundleVersionIds;
1935
2995
  };
1936
- _ts_decorate9([
2996
+ _ts_decorate12([
1937
2997
  IsArray2(),
1938
2998
  ArrayMaxSize2(BUNDLE_PRICE_LOOKUP_LIMIT),
1939
2999
  IsUUID2("4", {
1940
3000
  each: true
1941
3001
  }),
1942
- _ts_metadata7("design:type", Array)
3002
+ _ts_metadata10("design:type", Array)
1943
3003
  ], BundlePriceLookupDto.prototype, "bundleVersionIds", void 0);
1944
3004
 
1945
3005
  // src/billing/subscription-bundles.service.ts
1946
- import { Inject as Inject5, Injectable as Injectable5, NotFoundException as NotFoundException2, Optional as Optional5, UnprocessableEntityException as UnprocessableEntityException2 } from "@nestjs/common";
3006
+ import { Inject as Inject8, Injectable as Injectable7, NotFoundException as NotFoundException2, Optional as Optional6, UnprocessableEntityException as UnprocessableEntityException2 } from "@nestjs/common";
1947
3007
  import { BILLING_ERROR_CODES as BILLING_ERROR_CODES2, CATALOG_ERROR_CODES } from "@saasicat/core";
1948
- function _ts_decorate10(decorators, target, key, desc) {
3008
+ function _ts_decorate13(decorators, target, key, desc) {
1949
3009
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1950
3010
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1951
3011
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1952
3012
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1953
3013
  }
1954
- __name(_ts_decorate10, "_ts_decorate");
1955
- function _ts_metadata8(k, v) {
3014
+ __name(_ts_decorate13, "_ts_decorate");
3015
+ function _ts_metadata11(k, v) {
1956
3016
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1957
3017
  }
1958
- __name(_ts_metadata8, "_ts_metadata");
1959
- function _ts_param5(paramIndex, decorator) {
3018
+ __name(_ts_metadata11, "_ts_metadata");
3019
+ function _ts_param8(paramIndex, decorator) {
1960
3020
  return function(target, key) {
1961
3021
  decorator(target, key, paramIndex);
1962
3022
  };
1963
3023
  }
1964
- __name(_ts_param5, "_ts_param");
3024
+ __name(_ts_param8, "_ts_param");
1965
3025
  var SubscriptionBundlesService = class {
1966
3026
  static {
1967
3027
  __name(this, "SubscriptionBundlesService");
@@ -2202,16 +3262,16 @@ var SubscriptionBundlesService = class {
2202
3262
  return this.repo.reactivate(subscriptionBundleId);
2203
3263
  }
2204
3264
  };
2205
- SubscriptionBundlesService = _ts_decorate10([
2206
- Injectable5(),
2207
- _ts_param5(0, Inject5(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
2208
- _ts_param5(1, Inject5(BUNDLE_REPOSITORY_TOKEN)),
2209
- _ts_param5(2, Optional5()),
2210
- _ts_param5(2, Inject5(SUBSCRIPTION_BUNDLE_CONFIG_TOKEN)),
2211
- _ts_param5(3, Optional5()),
2212
- _ts_param5(3, Inject5(SELF_SERVICE_BLOCKED_BUNDLES_TOKEN)),
2213
- _ts_metadata8("design:type", Function),
2214
- _ts_metadata8("design:paramtypes", [
3265
+ SubscriptionBundlesService = _ts_decorate13([
3266
+ Injectable7(),
3267
+ _ts_param8(0, Inject8(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
3268
+ _ts_param8(1, Inject8(BUNDLE_REPOSITORY_TOKEN)),
3269
+ _ts_param8(2, Optional6()),
3270
+ _ts_param8(2, Inject8(SUBSCRIPTION_BUNDLE_CONFIG_TOKEN)),
3271
+ _ts_param8(3, Optional6()),
3272
+ _ts_param8(3, Inject8(SELF_SERVICE_BLOCKED_BUNDLES_TOKEN)),
3273
+ _ts_metadata11("design:type", Function),
3274
+ _ts_metadata11("design:paramtypes", [
2215
3275
  typeof SubscriptionBundleRepository === "undefined" ? Object : SubscriptionBundleRepository,
2216
3276
  typeof BundleRepository === "undefined" ? Object : BundleRepository,
2217
3277
  typeof SubscriptionBundleConfig === "undefined" ? Object : SubscriptionBundleConfig,
@@ -2243,25 +3303,38 @@ function clampToParent(ownTermEndsAt, parentEndsAt) {
2243
3303
  __name(clampToParent, "clampToParent");
2244
3304
 
2245
3305
  // src/billing/tenant-billing.controller.ts
2246
- import { BadRequestException, Body, Controller as Controller2, ForbiddenException as ForbiddenException2, Get as Get2, HttpCode, HttpStatus, Inject as Inject6, Logger as Logger2, ConflictException, NotFoundException as NotFoundException3, Optional as Optional6, Post, Req, UseGuards } from "@nestjs/common";
3306
+ import { BadRequestException, Body, Controller as Controller3, ForbiddenException as ForbiddenException2, Get as Get3, HttpCode, HttpStatus, Inject as Inject9, Logger as Logger3, ConflictException, NotFoundException as NotFoundException3, Optional as Optional7, Post, Req, UseGuards as UseGuards2 } from "@nestjs/common";
2247
3307
  import { AUTH_ERROR_CODES as AUTH_ERROR_CODES2, BILLING_ERROR_CODES as BILLING_ERROR_CODES3 } from "@saasicat/core";
2248
- function _ts_decorate11(decorators, target, key, desc) {
3308
+
3309
+ // src/billing/charges/record-charges-after.ts
3310
+ async function recordChargesAfter(charges, tenantId, change, logger) {
3311
+ if (!charges) return;
3312
+ try {
3313
+ await charges.recordDueCharges(tenantId);
3314
+ } catch (err) {
3315
+ logger.error(`Recording charges after ${change} failed (tenant ${tenantId}): ${String(err)}`);
3316
+ }
3317
+ }
3318
+ __name(recordChargesAfter, "recordChargesAfter");
3319
+
3320
+ // src/billing/tenant-billing.controller.ts
3321
+ function _ts_decorate14(decorators, target, key, desc) {
2249
3322
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2250
3323
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2251
3324
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2252
3325
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2253
3326
  }
2254
- __name(_ts_decorate11, "_ts_decorate");
2255
- function _ts_metadata9(k, v) {
3327
+ __name(_ts_decorate14, "_ts_decorate");
3328
+ function _ts_metadata12(k, v) {
2256
3329
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2257
3330
  }
2258
- __name(_ts_metadata9, "_ts_metadata");
2259
- function _ts_param6(paramIndex, decorator) {
3331
+ __name(_ts_metadata12, "_ts_metadata");
3332
+ function _ts_param9(paramIndex, decorator) {
2260
3333
  return function(target, key) {
2261
3334
  decorator(target, key, paramIndex);
2262
3335
  };
2263
3336
  }
2264
- __name(_ts_param6, "_ts_param");
3337
+ __name(_ts_param9, "_ts_param");
2265
3338
  var TenantBillingController = class _TenantBillingController {
2266
3339
  static {
2267
3340
  __name(this, "TenantBillingController");
@@ -2282,7 +3355,8 @@ var TenantBillingController = class _TenantBillingController {
2282
3355
  contractFreeze;
2283
3356
  trialProjection;
2284
3357
  cancellationNoticeDays;
2285
- constructor(entitlements, planPreview, subscriptionUsage, usageSnapshot, subscriptionWrite, tenantIdResolver = null, userIdResolver = null, blockedPlans = null, promoCodes = null, auditService = null, userEmailResolver = null, auditContextResolver = null, subscriptionBundles = null, contractFreeze = null, trialProjection = null, cancellationNoticeDays = NO_NOTICE_PERIOD) {
3358
+ charges;
3359
+ constructor(entitlements, planPreview, subscriptionUsage, usageSnapshot, subscriptionWrite, tenantIdResolver = null, userIdResolver = null, blockedPlans = null, promoCodes = null, auditService = null, userEmailResolver = null, auditContextResolver = null, subscriptionBundles = null, contractFreeze = null, trialProjection = null, cancellationNoticeDays = NO_NOTICE_PERIOD, charges = null) {
2286
3360
  this.entitlements = entitlements;
2287
3361
  this.planPreview = planPreview;
2288
3362
  this.subscriptionUsage = subscriptionUsage;
@@ -2299,8 +3373,9 @@ var TenantBillingController = class _TenantBillingController {
2299
3373
  this.contractFreeze = contractFreeze;
2300
3374
  this.trialProjection = trialProjection;
2301
3375
  this.cancellationNoticeDays = cancellationNoticeDays;
3376
+ this.charges = charges;
2302
3377
  }
2303
- logger = new Logger2(_TenantBillingController.name);
3378
+ logger = new Logger3(_TenantBillingController.name);
2304
3379
  // ---------------------------------------------------------------------
2305
3380
  // Reads (Phase B) — all authenticated tenant users
2306
3381
  // ---------------------------------------------------------------------
@@ -2456,7 +3531,8 @@ var TenantBillingController = class _TenantBillingController {
2456
3531
  };
2457
3532
  if (decision.isImmediate) {
2458
3533
  const wasTrial = sub.status === "TRIAL";
2459
- const period = wasTrial || cancellationOutstanding ? null : initialPeriodWindow(/* @__PURE__ */ new Date(), dto.billingCycle);
3534
+ const opensPeriod = decision.proration?.basis === "newPeriod";
3535
+ const period = wasTrial || cancellationOutstanding || !opensPeriod ? null : initialPeriodWindow(/* @__PURE__ */ new Date(), dto.billingCycle);
2460
3536
  const trialEndsAt = wasTrial && this.trialProjection ? await this.trialProjection.projectTrialEndsAt({
2461
3537
  currentPlan: sub.plan,
2462
3538
  targetPlan: dto.plan,
@@ -2486,6 +3562,7 @@ var TenantBillingController = class _TenantBillingController {
2486
3562
  // contract it freezes still ends when the subscription does.
2487
3563
  sub.canceledEffectiveAt ?? sub.canceledAt ?? null
2488
3564
  );
3565
+ await recordChargesAfter(this.charges, tenantId, "a plan change", this.logger);
2489
3566
  await this.auditLog(req, userId, "Subscription", tenantId, "CHANGE_PLAN", {
2490
3567
  fromPlan: sub.plan,
2491
3568
  fromCycle: sub.billingCycle,
@@ -2658,7 +3735,6 @@ var TenantBillingController = class _TenantBillingController {
2658
3735
  }
2659
3736
  }
2660
3737
  }
2661
- await this.tryFreezeOnPlanChange(tenantId, dto.plan, dto.billingCycle, wasTrial);
2662
3738
  this.entitlements.invalidateTenant(tenantId);
2663
3739
  await this.auditLog(req, userId, "Subscription", tenantId, "COMPLETE_ONBOARDING_SUBSCRIPTION", {
2664
3740
  plan: planResult.plan,
@@ -2698,6 +3774,8 @@ var TenantBillingController = class _TenantBillingController {
2698
3774
  } else if (bundleVersionIds.length > 0 && !this.subscriptionBundles) {
2699
3775
  warnings.push("Bundle bookings were requested during onboarding, but SubscriptionBundleModule is not registered in the consumer. No bundles were created.");
2700
3776
  }
3777
+ await this.tryFreezeOnPlanChange(tenantId, dto.plan, dto.billingCycle, wasTrial);
3778
+ await recordChargesAfter(this.charges, tenantId, "onboarding", this.logger);
2701
3779
  return {
2702
3780
  plan: planResult.plan,
2703
3781
  // The adapter returns `string`; the UI expects `'MONTHLY' | 'YEARLY'`.
@@ -2976,115 +4054,117 @@ var TenantBillingController = class _TenantBillingController {
2976
4054
  }
2977
4055
  }
2978
4056
  };
2979
- _ts_decorate11([
2980
- Get2("entitlement"),
2981
- _ts_param6(0, Req()),
2982
- _ts_metadata9("design:type", Function),
2983
- _ts_metadata9("design:paramtypes", [
4057
+ _ts_decorate14([
4058
+ Get3("entitlement"),
4059
+ _ts_param9(0, Req()),
4060
+ _ts_metadata12("design:type", Function),
4061
+ _ts_metadata12("design:paramtypes", [
2984
4062
  typeof RequestLike === "undefined" ? Object : RequestLike
2985
4063
  ]),
2986
- _ts_metadata9("design:returntype", Promise)
4064
+ _ts_metadata12("design:returntype", Promise)
2987
4065
  ], TenantBillingController.prototype, "getEntitlement", null);
2988
- _ts_decorate11([
2989
- Get2("usage"),
2990
- _ts_param6(0, Req()),
2991
- _ts_metadata9("design:type", Function),
2992
- _ts_metadata9("design:paramtypes", [
4066
+ _ts_decorate14([
4067
+ Get3("usage"),
4068
+ _ts_param9(0, Req()),
4069
+ _ts_metadata12("design:type", Function),
4070
+ _ts_metadata12("design:paramtypes", [
2993
4071
  typeof RequestLike === "undefined" ? Object : RequestLike
2994
4072
  ]),
2995
- _ts_metadata9("design:returntype", Promise)
4073
+ _ts_metadata12("design:returntype", Promise)
2996
4074
  ], TenantBillingController.prototype, "getUsage", null);
2997
- _ts_decorate11([
4075
+ _ts_decorate14([
2998
4076
  Post("plan/preview"),
2999
4077
  HttpCode(HttpStatus.OK),
3000
- UseGuards(TenantAdminGuard),
3001
- _ts_param6(0, Req()),
3002
- _ts_param6(1, Body()),
3003
- _ts_metadata9("design:type", Function),
3004
- _ts_metadata9("design:paramtypes", [
4078
+ UseGuards2(TenantAdminGuard),
4079
+ _ts_param9(0, Req()),
4080
+ _ts_param9(1, Body()),
4081
+ _ts_metadata12("design:type", Function),
4082
+ _ts_metadata12("design:paramtypes", [
3005
4083
  typeof RequestLike === "undefined" ? Object : RequestLike,
3006
4084
  typeof PreviewPlanChangeDto === "undefined" ? Object : PreviewPlanChangeDto
3007
4085
  ]),
3008
- _ts_metadata9("design:returntype", Promise)
4086
+ _ts_metadata12("design:returntype", Promise)
3009
4087
  ], TenantBillingController.prototype, "previewPlanChange", null);
3010
- _ts_decorate11([
4088
+ _ts_decorate14([
3011
4089
  Post("plan"),
3012
- UseGuards(TenantAdminGuard),
3013
- _ts_param6(0, Req()),
3014
- _ts_param6(1, Body()),
3015
- _ts_metadata9("design:type", Function),
3016
- _ts_metadata9("design:paramtypes", [
4090
+ UseGuards2(TenantAdminGuard),
4091
+ _ts_param9(0, Req()),
4092
+ _ts_param9(1, Body()),
4093
+ _ts_metadata12("design:type", Function),
4094
+ _ts_metadata12("design:paramtypes", [
3017
4095
  typeof RequestLike === "undefined" ? Object : RequestLike,
3018
4096
  typeof ChangePlanDto === "undefined" ? Object : ChangePlanDto
3019
4097
  ]),
3020
- _ts_metadata9("design:returntype", Promise)
4098
+ _ts_metadata12("design:returntype", Promise)
3021
4099
  ], TenantBillingController.prototype, "changePlan", null);
3022
- _ts_decorate11([
4100
+ _ts_decorate14([
3023
4101
  Post("onboarding/initial-subscription"),
3024
- UseGuards(TenantAdminGuard),
3025
- _ts_param6(0, Req()),
3026
- _ts_param6(1, Body()),
3027
- _ts_metadata9("design:type", Function),
3028
- _ts_metadata9("design:paramtypes", [
4102
+ UseGuards2(TenantAdminGuard),
4103
+ _ts_param9(0, Req()),
4104
+ _ts_param9(1, Body()),
4105
+ _ts_metadata12("design:type", Function),
4106
+ _ts_metadata12("design:paramtypes", [
3029
4107
  typeof RequestLike === "undefined" ? Object : RequestLike,
3030
4108
  typeof CompleteOnboardingSubscriptionDto === "undefined" ? Object : CompleteOnboardingSubscriptionDto
3031
4109
  ]),
3032
- _ts_metadata9("design:returntype", Promise)
4110
+ _ts_metadata12("design:returntype", Promise)
3033
4111
  ], TenantBillingController.prototype, "completeOnboardingSubscription", null);
3034
- _ts_decorate11([
4112
+ _ts_decorate14([
3035
4113
  Post("subscription/accept-pending-version"),
3036
- UseGuards(TenantAdminGuard),
3037
- _ts_param6(0, Req()),
3038
- _ts_metadata9("design:type", Function),
3039
- _ts_metadata9("design:paramtypes", [
4114
+ UseGuards2(TenantAdminGuard),
4115
+ _ts_param9(0, Req()),
4116
+ _ts_metadata12("design:type", Function),
4117
+ _ts_metadata12("design:paramtypes", [
3040
4118
  typeof RequestLike === "undefined" ? Object : RequestLike
3041
4119
  ]),
3042
- _ts_metadata9("design:returntype", Promise)
4120
+ _ts_metadata12("design:returntype", Promise)
3043
4121
  ], TenantBillingController.prototype, "acceptPendingPlanVersion", null);
3044
- _ts_decorate11([
4122
+ _ts_decorate14([
3045
4123
  Post("cancel"),
3046
- UseGuards(TenantAdminGuard),
3047
- _ts_param6(0, Req()),
3048
- _ts_param6(1, Body()),
3049
- _ts_metadata9("design:type", Function),
3050
- _ts_metadata9("design:paramtypes", [
4124
+ UseGuards2(TenantAdminGuard),
4125
+ _ts_param9(0, Req()),
4126
+ _ts_param9(1, Body()),
4127
+ _ts_metadata12("design:type", Function),
4128
+ _ts_metadata12("design:paramtypes", [
3051
4129
  typeof RequestLike === "undefined" ? Object : RequestLike,
3052
4130
  typeof CancelSubscriptionDto === "undefined" ? Object : CancelSubscriptionDto
3053
4131
  ]),
3054
- _ts_metadata9("design:returntype", Promise)
4132
+ _ts_metadata12("design:returntype", Promise)
3055
4133
  ], TenantBillingController.prototype, "cancelSubscription", null);
3056
- TenantBillingController = _ts_decorate11([
3057
- Controller2("billing"),
3058
- UseGuards(ComposedTenantAuthGuard),
3059
- _ts_param6(0, Inject6(ENTITLEMENT_SERVICE_TOKEN)),
3060
- _ts_param6(1, Inject6(PlanChangePreviewService)),
3061
- _ts_param6(2, Inject6(SUBSCRIPTION_USAGE_PORT_TOKEN)),
3062
- _ts_param6(3, Inject6(USAGE_SNAPSHOT_PORT_TOKEN)),
3063
- _ts_param6(4, Inject6(SUBSCRIPTION_WRITE_PORT_TOKEN)),
3064
- _ts_param6(5, Optional6()),
3065
- _ts_param6(5, Inject6(TENANT_ID_RESOLVER_TOKEN)),
3066
- _ts_param6(6, Optional6()),
3067
- _ts_param6(6, Inject6(USER_ID_RESOLVER_TOKEN)),
3068
- _ts_param6(7, Optional6()),
3069
- _ts_param6(7, Inject6(SELF_SERVICE_BLOCKED_PLANS_TOKEN)),
3070
- _ts_param6(8, Optional6()),
3071
- _ts_param6(8, Inject6(PromoCodesService)),
3072
- _ts_param6(9, Optional6()),
3073
- _ts_param6(9, Inject6(AdminAuditService)),
3074
- _ts_param6(10, Optional6()),
3075
- _ts_param6(10, Inject6(USER_EMAIL_RESOLVER_TOKEN)),
3076
- _ts_param6(11, Optional6()),
3077
- _ts_param6(11, Inject6(AUDIT_CONTEXT_RESOLVER_TOKEN)),
3078
- _ts_param6(12, Optional6()),
3079
- _ts_param6(12, Inject6(SubscriptionBundlesService)),
3080
- _ts_param6(13, Optional6()),
3081
- _ts_param6(13, Inject6(CONTRACT_FREEZE_PORT_TOKEN)),
3082
- _ts_param6(14, Optional6()),
3083
- _ts_param6(14, Inject6(TRIAL_PROJECTION_PORT_TOKEN)),
3084
- _ts_param6(15, Optional6()),
3085
- _ts_param6(15, Inject6(CANCELLATION_NOTICE_DAYS_TOKEN)),
3086
- _ts_metadata9("design:type", Function),
3087
- _ts_metadata9("design:paramtypes", [
4134
+ TenantBillingController = _ts_decorate14([
4135
+ Controller3("billing"),
4136
+ UseGuards2(ComposedTenantAuthGuard),
4137
+ _ts_param9(0, Inject9(ENTITLEMENT_SERVICE_TOKEN)),
4138
+ _ts_param9(1, Inject9(PlanChangePreviewService)),
4139
+ _ts_param9(2, Inject9(SUBSCRIPTION_USAGE_PORT_TOKEN)),
4140
+ _ts_param9(3, Inject9(USAGE_SNAPSHOT_PORT_TOKEN)),
4141
+ _ts_param9(4, Inject9(SUBSCRIPTION_WRITE_PORT_TOKEN)),
4142
+ _ts_param9(5, Optional7()),
4143
+ _ts_param9(5, Inject9(TENANT_ID_RESOLVER_TOKEN)),
4144
+ _ts_param9(6, Optional7()),
4145
+ _ts_param9(6, Inject9(USER_ID_RESOLVER_TOKEN)),
4146
+ _ts_param9(7, Optional7()),
4147
+ _ts_param9(7, Inject9(SELF_SERVICE_BLOCKED_PLANS_TOKEN)),
4148
+ _ts_param9(8, Optional7()),
4149
+ _ts_param9(8, Inject9(PromoCodesService)),
4150
+ _ts_param9(9, Optional7()),
4151
+ _ts_param9(9, Inject9(AdminAuditService)),
4152
+ _ts_param9(10, Optional7()),
4153
+ _ts_param9(10, Inject9(USER_EMAIL_RESOLVER_TOKEN)),
4154
+ _ts_param9(11, Optional7()),
4155
+ _ts_param9(11, Inject9(AUDIT_CONTEXT_RESOLVER_TOKEN)),
4156
+ _ts_param9(12, Optional7()),
4157
+ _ts_param9(12, Inject9(SubscriptionBundlesService)),
4158
+ _ts_param9(13, Optional7()),
4159
+ _ts_param9(13, Inject9(CONTRACT_FREEZE_PORT_TOKEN)),
4160
+ _ts_param9(14, Optional7()),
4161
+ _ts_param9(14, Inject9(TRIAL_PROJECTION_PORT_TOKEN)),
4162
+ _ts_param9(15, Optional7()),
4163
+ _ts_param9(15, Inject9(CANCELLATION_NOTICE_DAYS_TOKEN)),
4164
+ _ts_param9(16, Optional7()),
4165
+ _ts_param9(16, Inject9(SubscriberChargeService)),
4166
+ _ts_metadata12("design:type", Function),
4167
+ _ts_metadata12("design:paramtypes", [
3088
4168
  typeof EntitlementService === "undefined" ? Object : EntitlementService,
3089
4169
  typeof PlanChangePreviewService === "undefined" ? Object : PlanChangePreviewService,
3090
4170
  typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort,
@@ -3100,20 +4180,21 @@ TenantBillingController = _ts_decorate11([
3100
4180
  Object,
3101
4181
  Object,
3102
4182
  Object,
3103
- typeof CancellationNoticePeriods === "undefined" ? Object : CancellationNoticePeriods
4183
+ typeof CancellationNoticePeriods === "undefined" ? Object : CancellationNoticePeriods,
4184
+ Object
3104
4185
  ])
3105
4186
  ], TenantBillingController);
3106
4187
 
3107
4188
  // src/billing/tenant-billing.module.ts
3108
- import { Module as Module2 } from "@nestjs/common";
4189
+ import { Module as Module3 } from "@nestjs/common";
3109
4190
  import { planCatalogSchema as planCatalogSchema2 } from "@saasicat/spec";
3110
- function _ts_decorate12(decorators, target, key, desc) {
4191
+ function _ts_decorate15(decorators, target, key, desc) {
3111
4192
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3112
4193
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3113
4194
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3114
4195
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3115
4196
  }
3116
- __name(_ts_decorate12, "_ts_decorate");
4197
+ __name(_ts_decorate15, "_ts_decorate");
3117
4198
  function settingFromCatalog(catalog, key) {
3118
4199
  const settings = catalog.tenantBilling;
3119
4200
  const value = settings?.[key];
@@ -3183,6 +4264,13 @@ var TenantBillingModule = class _TenantBillingModule {
3183
4264
  useClass: SubscriptionContractFreezeService
3184
4265
  });
3185
4266
  }
4267
+ const hasChargeJournal = Boolean(options.chargeJournal);
4268
+ if (options.chargeJournal) {
4269
+ if (!options.contractFreeze) {
4270
+ throw new Error("TenantBillingModule: `chargeJournal` needs `contractFreeze` \u2014 a charge points at a line of a frozen contract, and the contracts and the subscribers they are concluded with come from there.");
4271
+ }
4272
+ providers.push(asProvider(SUBSCRIBER_LEDGER_REPOSITORY_TOKEN, options.chargeJournal.ledgerRepository), SubscriberChargeService, SubscriberAccountService);
4273
+ }
3186
4274
  if (options.tenantIdResolver) {
3187
4275
  providers.push({
3188
4276
  provide: TENANT_ID_RESOLVER_TOKEN,
@@ -3229,35 +4317,39 @@ var TenantBillingModule = class _TenantBillingModule {
3229
4317
  ...hasContractFreeze ? [
3230
4318
  CONTRACT_FREEZE_PORT_TOKEN
3231
4319
  ] : [],
4320
+ ...hasChargeJournal ? [
4321
+ SubscriberChargeService,
4322
+ SubscriberAccountService
4323
+ ] : [],
3232
4324
  ...options.extraExports ?? []
3233
4325
  ]
3234
4326
  };
3235
4327
  }
3236
4328
  };
3237
- TenantBillingModule = _ts_decorate12([
3238
- Module2({})
4329
+ TenantBillingModule = _ts_decorate15([
4330
+ Module3({})
3239
4331
  ], TenantBillingModule);
3240
4332
 
3241
4333
  // src/billing/subscription-bundle-preview.service.ts
3242
- import { Inject as Inject7, Injectable as Injectable6, NotFoundException as NotFoundException4, Optional as Optional7 } from "@nestjs/common";
4334
+ import { Inject as Inject10, Injectable as Injectable8, NotFoundException as NotFoundException4, Optional as Optional8 } from "@nestjs/common";
3243
4335
  import { BILLING_ERROR_CODES as BILLING_ERROR_CODES4, CATALOG_ERROR_CODES as CATALOG_ERROR_CODES2, buildFeatureRequiresIndex as buildFeatureRequiresIndex2, collectUnsatisfiedRequires as collectUnsatisfiedRequires2 } from "@saasicat/core";
3244
- function _ts_decorate13(decorators, target, key, desc) {
4336
+ function _ts_decorate16(decorators, target, key, desc) {
3245
4337
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3246
4338
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3247
4339
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3248
4340
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3249
4341
  }
3250
- __name(_ts_decorate13, "_ts_decorate");
3251
- function _ts_metadata10(k, v) {
4342
+ __name(_ts_decorate16, "_ts_decorate");
4343
+ function _ts_metadata13(k, v) {
3252
4344
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
3253
4345
  }
3254
- __name(_ts_metadata10, "_ts_metadata");
3255
- function _ts_param7(paramIndex, decorator) {
4346
+ __name(_ts_metadata13, "_ts_metadata");
4347
+ function _ts_param10(paramIndex, decorator) {
3256
4348
  return function(target, key) {
3257
4349
  decorator(target, key, paramIndex);
3258
4350
  };
3259
4351
  }
3260
- __name(_ts_param7, "_ts_param");
4352
+ __name(_ts_param10, "_ts_param");
3261
4353
  var SubscriptionBundlePreviewService = class {
3262
4354
  static {
3263
4355
  __name(this, "SubscriptionBundlePreviewService");
@@ -3527,20 +4619,20 @@ var SubscriptionBundlePreviewService = class {
3527
4619
  return collectUnsatisfiedRequires2(bundleVersion.features ?? [], requiresIndex).filter((key) => !covered.has(key));
3528
4620
  }
3529
4621
  };
3530
- SubscriptionBundlePreviewService = _ts_decorate13([
3531
- Injectable6(),
3532
- _ts_param7(0, Inject7(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
3533
- _ts_param7(1, Inject7(BUNDLE_REPOSITORY_TOKEN)),
3534
- _ts_param7(2, Optional7()),
3535
- _ts_param7(2, Inject7(PLAN_REPOSITORY_TOKEN)),
3536
- _ts_param7(3, Optional7()),
3537
- _ts_param7(3, Inject7(CATALOG_ENTRY_REPOSITORY_TOKEN)),
3538
- _ts_param7(4, Optional7()),
3539
- _ts_param7(4, Inject7(SELF_SERVICE_BLOCKED_BUNDLES_TOKEN)),
3540
- _ts_param7(5, Optional7()),
3541
- _ts_param7(5, Inject7(SUBSCRIPTION_BUNDLE_CONFIG_TOKEN)),
3542
- _ts_metadata10("design:type", Function),
3543
- _ts_metadata10("design:paramtypes", [
4622
+ SubscriptionBundlePreviewService = _ts_decorate16([
4623
+ Injectable8(),
4624
+ _ts_param10(0, Inject10(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
4625
+ _ts_param10(1, Inject10(BUNDLE_REPOSITORY_TOKEN)),
4626
+ _ts_param10(2, Optional8()),
4627
+ _ts_param10(2, Inject10(PLAN_REPOSITORY_TOKEN)),
4628
+ _ts_param10(3, Optional8()),
4629
+ _ts_param10(3, Inject10(CATALOG_ENTRY_REPOSITORY_TOKEN)),
4630
+ _ts_param10(4, Optional8()),
4631
+ _ts_param10(4, Inject10(SELF_SERVICE_BLOCKED_BUNDLES_TOKEN)),
4632
+ _ts_param10(5, Optional8()),
4633
+ _ts_param10(5, Inject10(SUBSCRIPTION_BUNDLE_CONFIG_TOKEN)),
4634
+ _ts_metadata13("design:type", Function),
4635
+ _ts_metadata13("design:paramtypes", [
3544
4636
  typeof SubscriptionBundleRepository === "undefined" ? Object : SubscriptionBundleRepository,
3545
4637
  typeof BundleRepository === "undefined" ? Object : BundleRepository,
3546
4638
  Object,
@@ -3565,25 +4657,29 @@ function toSnapshot(bundleVersion) {
3565
4657
  __name(toSnapshot, "toSnapshot");
3566
4658
 
3567
4659
  // src/billing/tenant-subscription-bundles.controller.ts
3568
- import { BadRequestException as BadRequestException2, Body as Body2, Controller as Controller3, Delete, Get as Get3, HttpCode as HttpCode2, HttpStatus as HttpStatus2, Inject as Inject8, Logger as Logger3, ConflictException as ConflictException2, NotFoundException as NotFoundException5, Optional as Optional8, Param, ParseUUIDPipe, Post as Post2, Req as Req2, UseGuards as UseGuards2 } from "@nestjs/common";
4660
+ import { BadRequestException as BadRequestException2, Body as Body2, Controller as Controller4, Delete, Get as Get4, HttpCode as HttpCode2, HttpStatus as HttpStatus2, Inject as Inject11, Logger as Logger4, ConflictException as ConflictException2, NotFoundException as NotFoundException5, Optional as Optional9, Param as Param2, ParseUUIDPipe, Post as Post2, Req as Req2, UseGuards as UseGuards3 } from "@nestjs/common";
3569
4661
  import { AUTH_ERROR_CODES as AUTH_ERROR_CODES3, BILLING_ERROR_CODES as BILLING_ERROR_CODES5 } from "@saasicat/core";
3570
- function _ts_decorate14(decorators, target, key, desc) {
4662
+ function _ts_decorate17(decorators, target, key, desc) {
3571
4663
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3572
4664
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3573
4665
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3574
4666
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3575
4667
  }
3576
- __name(_ts_decorate14, "_ts_decorate");
3577
- function _ts_metadata11(k, v) {
4668
+ __name(_ts_decorate17, "_ts_decorate");
4669
+ function _ts_metadata14(k, v) {
3578
4670
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
3579
4671
  }
3580
- __name(_ts_metadata11, "_ts_metadata");
3581
- function _ts_param8(paramIndex, decorator) {
4672
+ __name(_ts_metadata14, "_ts_metadata");
4673
+ function _ts_param11(paramIndex, decorator) {
3582
4674
  return function(target, key) {
3583
4675
  decorator(target, key, paramIndex);
3584
4676
  };
3585
4677
  }
3586
- __name(_ts_param8, "_ts_param");
4678
+ __name(_ts_param11, "_ts_param");
4679
+ function planCycleOf2(sub) {
4680
+ return sub.billingCycle;
4681
+ }
4682
+ __name(planCycleOf2, "planCycleOf");
3587
4683
  function buildTenantSubscriptionBundlesController(extraGuards = []) {
3588
4684
  let GeneratedTenantSubscriptionBundlesController = class GeneratedTenantSubscriptionBundlesController2 {
3589
4685
  static {
@@ -3594,19 +4690,21 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3594
4690
  subscriptionUsage;
3595
4691
  tenantIdResolver;
3596
4692
  contractFreeze;
3597
- logger = new Logger3("TenantSubscriptionBundlesController");
4693
+ charges;
4694
+ logger = new Logger4("TenantSubscriptionBundlesController");
3598
4695
  /** Keeps the "no contractFreeze hook" warning to one line per process. */
3599
4696
  static warnedAboutMissingFreeze = false;
3600
- constructor(service, previewService, subscriptionUsage, tenantIdResolver, contractFreeze = null) {
4697
+ constructor(service, previewService, subscriptionUsage, tenantIdResolver, contractFreeze = null, charges = null) {
3601
4698
  this.service = service;
3602
4699
  this.previewService = previewService;
3603
4700
  this.subscriptionUsage = subscriptionUsage;
3604
4701
  this.tenantIdResolver = tenantIdResolver;
3605
4702
  this.contractFreeze = contractFreeze;
4703
+ this.charges = charges;
3606
4704
  }
3607
4705
  async list(req) {
3608
4706
  const sub = await this.requireSubscription(this.requireTenantId(req));
3609
- return this.service.listForSubscription(this.requireSubscriptionPk(sub), sub.plan, sub.billingCycle);
4707
+ return this.service.listForSubscription(this.requireSubscriptionPk(sub), sub.plan, planCycleOf2(sub));
3610
4708
  }
3611
4709
  /**
3612
4710
  * List prices for the bundles the store is showing, resolved for this
@@ -3636,7 +4734,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3636
4734
  // A bundle cannot commit past the subscription paying for it.
3637
4735
  parentEndsAt: sub.canceledEffectiveAt ?? sub.canceledAt ?? null,
3638
4736
  // And it runs in the plan's rhythm, on the plan's day.
3639
- planCycle: sub.billingCycle,
4737
+ planCycle: planCycleOf2(sub),
3640
4738
  planPeriodEnd: sub.currentPeriodEnd ?? null,
3641
4739
  // Resolved rather than passed through: a null here would let
3642
4740
  // the arithmetic read the period END, which a short month has
@@ -3646,6 +4744,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3646
4744
  billingCycle: dto.billingCycle
3647
4745
  });
3648
4746
  await this.refreezeContract(tenantId, sub);
4747
+ await recordChargesAfter(this.charges, tenantId, "an add-on booking", this.logger);
3649
4748
  return result;
3650
4749
  }
3651
4750
  async preview(req, dto) {
@@ -3662,7 +4761,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3662
4761
  subscriptionId: this.requireSubscriptionPk(sub),
3663
4762
  // Plan KEY (compatibility.planIds is key-based) — not the planVersion UUID.
3664
4763
  currentPlanKey: sub.plan,
3665
- billingCycle: sub.billingCycle,
4764
+ billingCycle: planCycleOf2(sub),
3666
4765
  status: sub.status,
3667
4766
  startedAt: sub.startedAt,
3668
4767
  currentPeriodStart: sub.currentPeriodStart,
@@ -3772,7 +4871,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3772
4871
  await this.contractFreeze.freezeOnPlanChange(
3773
4872
  tenantId,
3774
4873
  sub.planVersion.planId,
3775
- sub.billingCycle,
4874
+ planCycleOf2(sub),
3776
4875
  /* @__PURE__ */ new Date(),
3777
4876
  // Cancelling a bundle stays open on a cancelled
3778
4877
  // subscription, and it re-freezes: the replacement contract
@@ -3784,93 +4883,96 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3784
4883
  }
3785
4884
  }
3786
4885
  };
3787
- _ts_decorate14([
3788
- Get3(),
3789
- _ts_param8(0, Req2()),
3790
- _ts_metadata11("design:type", Function),
3791
- _ts_metadata11("design:paramtypes", [
4886
+ _ts_decorate17([
4887
+ Get4(),
4888
+ _ts_param11(0, Req2()),
4889
+ _ts_metadata14("design:type", Function),
4890
+ _ts_metadata14("design:paramtypes", [
3792
4891
  typeof RequestLike === "undefined" ? Object : RequestLike
3793
4892
  ]),
3794
- _ts_metadata11("design:returntype", Promise)
4893
+ _ts_metadata14("design:returntype", Promise)
3795
4894
  ], GeneratedTenantSubscriptionBundlesController.prototype, "list", null);
3796
- _ts_decorate14([
4895
+ _ts_decorate17([
3797
4896
  Post2("prices"),
3798
- _ts_param8(0, Req2()),
3799
- _ts_param8(1, Body2()),
3800
- _ts_metadata11("design:type", Function),
3801
- _ts_metadata11("design:paramtypes", [
4897
+ _ts_param11(0, Req2()),
4898
+ _ts_param11(1, Body2()),
4899
+ _ts_metadata14("design:type", Function),
4900
+ _ts_metadata14("design:paramtypes", [
3802
4901
  typeof RequestLike === "undefined" ? Object : RequestLike,
3803
4902
  typeof BundlePriceLookupDto === "undefined" ? Object : BundlePriceLookupDto
3804
4903
  ]),
3805
- _ts_metadata11("design:returntype", Promise)
4904
+ _ts_metadata14("design:returntype", Promise)
3806
4905
  ], GeneratedTenantSubscriptionBundlesController.prototype, "prices", null);
3807
- _ts_decorate14([
4906
+ _ts_decorate17([
3808
4907
  Post2(),
3809
- UseGuards2(TenantAdminGuard),
3810
- _ts_param8(0, Req2()),
3811
- _ts_param8(1, Body2()),
3812
- _ts_metadata11("design:type", Function),
3813
- _ts_metadata11("design:paramtypes", [
4908
+ UseGuards3(TenantAdminGuard),
4909
+ _ts_param11(0, Req2()),
4910
+ _ts_param11(1, Body2()),
4911
+ _ts_metadata14("design:type", Function),
4912
+ _ts_metadata14("design:paramtypes", [
3814
4913
  typeof RequestLike === "undefined" ? Object : RequestLike,
3815
4914
  typeof AddSubscriptionBundleDto === "undefined" ? Object : AddSubscriptionBundleDto
3816
4915
  ]),
3817
- _ts_metadata11("design:returntype", Promise)
4916
+ _ts_metadata14("design:returntype", Promise)
3818
4917
  ], GeneratedTenantSubscriptionBundlesController.prototype, "add", null);
3819
- _ts_decorate14([
4918
+ _ts_decorate17([
3820
4919
  Post2("preview"),
3821
4920
  HttpCode2(HttpStatus2.OK),
3822
- _ts_param8(0, Req2()),
3823
- _ts_param8(1, Body2()),
3824
- _ts_metadata11("design:type", Function),
3825
- _ts_metadata11("design:paramtypes", [
4921
+ _ts_param11(0, Req2()),
4922
+ _ts_param11(1, Body2()),
4923
+ _ts_metadata14("design:type", Function),
4924
+ _ts_metadata14("design:paramtypes", [
3826
4925
  typeof RequestLike === "undefined" ? Object : RequestLike,
3827
4926
  typeof PreviewSubscriptionBundleDto === "undefined" ? Object : PreviewSubscriptionBundleDto
3828
4927
  ]),
3829
- _ts_metadata11("design:returntype", Promise)
4928
+ _ts_metadata14("design:returntype", Promise)
3830
4929
  ], GeneratedTenantSubscriptionBundlesController.prototype, "preview", null);
3831
- _ts_decorate14([
4930
+ _ts_decorate17([
3832
4931
  Delete(":id"),
3833
- UseGuards2(TenantAdminGuard),
4932
+ UseGuards3(TenantAdminGuard),
3834
4933
  HttpCode2(HttpStatus2.OK),
3835
- _ts_param8(0, Req2()),
3836
- _ts_param8(1, Param("id", new ParseUUIDPipe())),
3837
- _ts_param8(2, Body2()),
3838
- _ts_metadata11("design:type", Function),
3839
- _ts_metadata11("design:paramtypes", [
4934
+ _ts_param11(0, Req2()),
4935
+ _ts_param11(1, Param2("id", new ParseUUIDPipe())),
4936
+ _ts_param11(2, Body2()),
4937
+ _ts_metadata14("design:type", Function),
4938
+ _ts_metadata14("design:paramtypes", [
3840
4939
  typeof RequestLike === "undefined" ? Object : RequestLike,
3841
4940
  String,
3842
4941
  typeof CancelSubscriptionBundleDto === "undefined" ? Object : CancelSubscriptionBundleDto
3843
4942
  ]),
3844
- _ts_metadata11("design:returntype", Promise)
4943
+ _ts_metadata14("design:returntype", Promise)
3845
4944
  ], GeneratedTenantSubscriptionBundlesController.prototype, "cancel", null);
3846
- _ts_decorate14([
4945
+ _ts_decorate17([
3847
4946
  Post2(":id/reactivate"),
3848
- UseGuards2(TenantAdminGuard),
4947
+ UseGuards3(TenantAdminGuard),
3849
4948
  HttpCode2(HttpStatus2.OK),
3850
- _ts_param8(0, Req2()),
3851
- _ts_param8(1, Param("id", new ParseUUIDPipe())),
3852
- _ts_metadata11("design:type", Function),
3853
- _ts_metadata11("design:paramtypes", [
4949
+ _ts_param11(0, Req2()),
4950
+ _ts_param11(1, Param2("id", new ParseUUIDPipe())),
4951
+ _ts_metadata14("design:type", Function),
4952
+ _ts_metadata14("design:paramtypes", [
3854
4953
  typeof RequestLike === "undefined" ? Object : RequestLike,
3855
4954
  String
3856
4955
  ]),
3857
- _ts_metadata11("design:returntype", Promise)
4956
+ _ts_metadata14("design:returntype", Promise)
3858
4957
  ], GeneratedTenantSubscriptionBundlesController.prototype, "reactivate", null);
3859
- GeneratedTenantSubscriptionBundlesController = _ts_decorate14([
3860
- Controller3("billing/subscription-bundles"),
3861
- UseGuards2(ComposedTenantAuthGuard, ...extraGuards),
3862
- _ts_param8(0, Inject8(SubscriptionBundlesService)),
3863
- _ts_param8(1, Inject8(SubscriptionBundlePreviewService)),
3864
- _ts_param8(2, Inject8(SUBSCRIPTION_USAGE_PORT_TOKEN)),
3865
- _ts_param8(3, Inject8(TENANT_ID_RESOLVER_TOKEN)),
3866
- _ts_param8(4, Optional8()),
3867
- _ts_param8(4, Inject8(CONTRACT_FREEZE_PORT_TOKEN)),
3868
- _ts_metadata11("design:type", Function),
3869
- _ts_metadata11("design:paramtypes", [
4958
+ GeneratedTenantSubscriptionBundlesController = _ts_decorate17([
4959
+ Controller4("billing/subscription-bundles"),
4960
+ UseGuards3(ComposedTenantAuthGuard, ...extraGuards),
4961
+ _ts_param11(0, Inject11(SubscriptionBundlesService)),
4962
+ _ts_param11(1, Inject11(SubscriptionBundlePreviewService)),
4963
+ _ts_param11(2, Inject11(SUBSCRIPTION_USAGE_PORT_TOKEN)),
4964
+ _ts_param11(3, Inject11(TENANT_ID_RESOLVER_TOKEN)),
4965
+ _ts_param11(4, Optional9()),
4966
+ _ts_param11(4, Inject11(CONTRACT_FREEZE_PORT_TOKEN)),
4967
+ _ts_param11(5, Optional9()),
4968
+ _ts_param11(5, Inject11(SubscriberChargeService)),
4969
+ _ts_metadata14("design:type", Function),
4970
+ _ts_metadata14("design:paramtypes", [
3870
4971
  typeof SubscriptionBundlesService === "undefined" ? Object : SubscriptionBundlesService,
3871
4972
  typeof SubscriptionBundlePreviewService === "undefined" ? Object : SubscriptionBundlePreviewService,
3872
4973
  typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort,
3873
4974
  typeof TenantIdResolver === "undefined" ? Object : TenantIdResolver,
4975
+ Object,
3874
4976
  Object
3875
4977
  ])
3876
4978
  ], GeneratedTenantSubscriptionBundlesController);
@@ -3879,14 +4981,14 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3879
4981
  __name(buildTenantSubscriptionBundlesController, "buildTenantSubscriptionBundlesController");
3880
4982
 
3881
4983
  // src/billing/subscription-bundles.module.ts
3882
- import { Module as Module3 } from "@nestjs/common";
3883
- function _ts_decorate15(decorators, target, key, desc) {
4984
+ import { Module as Module4 } from "@nestjs/common";
4985
+ function _ts_decorate18(decorators, target, key, desc) {
3884
4986
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3885
4987
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3886
4988
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
3887
4989
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3888
4990
  }
3889
- __name(_ts_decorate15, "_ts_decorate");
4991
+ __name(_ts_decorate18, "_ts_decorate");
3890
4992
  var SubscriptionBundleModule = class _SubscriptionBundleModule {
3891
4993
  static {
3892
4994
  __name(this, "SubscriptionBundleModule");
@@ -3948,8 +5050,8 @@ var SubscriptionBundleModule = class _SubscriptionBundleModule {
3948
5050
  };
3949
5051
  }
3950
5052
  };
3951
- SubscriptionBundleModule = _ts_decorate15([
3952
- Module3({})
5053
+ SubscriptionBundleModule = _ts_decorate18([
5054
+ Module4({})
3953
5055
  ], SubscriptionBundleModule);
3954
5056
 
3955
5057
  export {
@@ -3989,11 +5091,16 @@ export {
3989
5091
  SELF_SERVICE_BLOCKED_PLANS_TOKEN,
3990
5092
  SELF_SERVICE_BLOCKED_BUNDLES_TOKEN,
3991
5093
  computeProration,
5094
+ computeNewPeriodCharge,
3992
5095
  PlanChangePreviewService,
3993
5096
  CONTRACT_FREEZE_PORT_TOKEN,
3994
5097
  CONTRACT_FREEZE_SOURCE_PORT_TOKEN,
3995
5098
  PendingPlanMaterializationService,
3996
5099
  SubscriptionContractFreezeService,
5100
+ SUBSCRIBER_LEDGER_REPOSITORY_TOKEN,
5101
+ SubscriberAccountService,
5102
+ SubscriberAccountModule,
5103
+ SubscriberChargeService,
3997
5104
  PreviewPlanChangeDto,
3998
5105
  ChangePlanDto,
3999
5106
  CancelSubscriptionDto,