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

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 (101) hide show
  1. package/dist/.build-stamp +1 -1
  2. package/dist/_entries.cjs +15561 -12478
  3. package/dist/admin/index.d.cts +4 -4
  4. package/dist/admin/index.d.ts +4 -4
  5. package/dist/admin/index.js +8 -5
  6. package/dist/{admin-resources.module-CWqdI9BV.d.ts → admin-resources.module-DtWAFI-G.d.ts} +1 -1
  7. package/dist/{admin-resources.module-B4XNvdH-.d.cts → admin-resources.module-pS7LbdbI.d.cts} +1 -1
  8. package/dist/{admin-stats.service-CsLNt19Z.d.ts → admin-stats.service--1sRNpUx.d.ts} +2 -2
  9. package/dist/{admin-stats.service-DrZrU8IJ.d.cts → admin-stats.service-ChATI9YK.d.cts} +2 -2
  10. package/dist/{aggregation-ChpZDh0g.d.ts → aggregation-B8iZBesJ.d.ts} +63 -4
  11. package/dist/{aggregation-OpnhwzF5.d.cts → aggregation-BRzgRXDb.d.cts} +63 -4
  12. package/dist/billing/index.d.cts +280 -13
  13. package/dist/billing/index.d.ts +280 -13
  14. package/dist/billing/index.js +32 -8
  15. package/dist/catalog/index.d.cts +2 -2
  16. package/dist/catalog/index.d.ts +2 -2
  17. package/dist/catalog/index.js +4 -2
  18. package/dist/{catalog.module-T-I5Ricz.d.cts → catalog.module-CeKslGHW.d.cts} +1 -1
  19. package/dist/{catalog.module-C9O7BxRq.d.ts → catalog.module-CiurcIjI.d.ts} +1 -1
  20. package/dist/checkout-offer/index.d.cts +4 -4
  21. package/dist/checkout-offer/index.d.ts +4 -4
  22. package/dist/checkout-offer/index.js +5 -5
  23. package/dist/{checkout-offer.module-x7SP_dX4.d.ts → checkout-offer.module-C761mcEv.d.ts} +1 -1
  24. package/dist/{checkout-offer.module-DFhGy6JO.d.cts → checkout-offer.module-CSHB4bB8.d.cts} +1 -1
  25. package/dist/{checkout-offer.service-BeHgYikI.d.ts → checkout-offer.service-Dux8EgEM.d.ts} +3 -3
  26. package/dist/{checkout-offer.service-CCVxG9b2.d.cts → checkout-offer.service-HKoTZ3JH.d.cts} +3 -3
  27. package/dist/{chunk-F5X66HF5.js → chunk-2ECX7BST.js} +1 -1
  28. package/dist/{chunk-G6EZWECL.js → chunk-2XBWYMOL.js} +141 -26
  29. package/dist/{chunk-R5YCJBHY.js → chunk-3HBP2HK3.js} +6 -2
  30. package/dist/{chunk-S33SO5XX.js → chunk-4OFJLVVH.js} +4 -0
  31. package/dist/{chunk-AOS2JPDD.js → chunk-5LL2554P.js} +757 -163
  32. package/dist/chunk-5MZH3VVU.js +292 -0
  33. package/dist/{chunk-WHJSYKNC.js → chunk-7LHUENAT.js} +2 -2
  34. package/dist/{chunk-M47BNEY2.js → chunk-7MLB6T5B.js} +2072 -407
  35. package/dist/{chunk-NDLC5GYK.js → chunk-CVCKXKGT.js} +138 -21
  36. package/dist/{chunk-VBRYPU7V.js → chunk-E224LKO2.js} +3 -1
  37. package/dist/{chunk-QPVDCKYS.js → chunk-ESEWSAOV.js} +4 -3
  38. package/dist/{chunk-CQ2ZZMTD.js → chunk-MU4O4EKX.js} +711 -828
  39. package/dist/{chunk-JBCC6C3M.js → chunk-NQIPQI2Q.js} +26 -7
  40. package/dist/{chunk-4SDSYV4A.js → chunk-PBL7N5J3.js} +18 -2
  41. package/dist/{chunk-LLYVYRGJ.js → chunk-QTJEZDKC.js} +15 -275
  42. package/dist/{chunk-3RLWML2S.js → chunk-QWKBOLBH.js} +35 -0
  43. package/dist/{chunk-XSSWYPP5.js → chunk-TR3L6FRF.js} +6 -2
  44. package/dist/chunk-TSLGMPGZ.js +21 -0
  45. package/dist/chunk-UJZCESIC.js +466 -0
  46. package/dist/{chunk-TOEFN7DN.js → chunk-W6VNCMOQ.js} +3 -3
  47. package/dist/{chunk-7NJKVC5X.js → chunk-WQNOCOI2.js} +53 -6
  48. package/dist/chunk-YZSILFW4.js +142 -0
  49. package/dist/{chunk-55QEDAMX.js → chunk-ZCXW24NH.js} +4 -9
  50. package/dist/discovery/index.js +2 -1
  51. package/dist/{enforce-quota.interceptor-BFyVdWe8.d.cts → enforce-quota.interceptor-Dc-1_zQf.d.cts} +2 -2
  52. package/dist/{enforce-quota.interceptor-GaBXboGx.d.ts → enforce-quota.interceptor-MXpwsOJ2.d.ts} +2 -2
  53. package/dist/entitlement/index.d.cts +1 -1
  54. package/dist/entitlement/index.d.ts +1 -1
  55. package/dist/entitlement/index.js +5 -3
  56. package/dist/index.d.cts +62 -25
  57. package/dist/index.d.ts +62 -25
  58. package/dist/index.js +81 -26
  59. package/dist/{issuer-identity.check-CBO0vcsq.d.cts → issuer-identity.check-BrZXesOF.d.cts} +3 -3
  60. package/dist/{issuer-identity.check-Cz9CEMkZ.d.ts → issuer-identity.check-BzlFSmSZ.d.ts} +3 -3
  61. package/dist/maintenance.service-CNMI3tSm.d.cts +133 -0
  62. package/dist/maintenance.service-yx6fxjUj.d.ts +133 -0
  63. package/dist/{module-options-DmQ3G4sZ.d.ts → module-options-BSYxlA38.d.ts} +50 -9
  64. package/dist/{module-options-qAOwinHR.d.cts → module-options-BkufuqlM.d.cts} +50 -9
  65. package/dist/payments/index.d.cts +2 -2
  66. package/dist/payments/index.d.ts +2 -2
  67. package/dist/payments/index.js +2 -2
  68. package/dist/{payments.module-BdUq9ot_.d.cts → payments.module-CwLeaflk.d.cts} +1 -1
  69. package/dist/{payments.module-CQGv_VG7.d.ts → payments.module-DmIN-HWx.d.ts} +1 -1
  70. package/dist/{plan-versions.service-DARLh-qI.d.cts → plan-versions.service-2mD0iEvY.d.cts} +1 -1
  71. package/dist/{plan-versions.service-C0N-Em2W.d.ts → plan-versions.service-C4a9aCEU.d.ts} +1 -1
  72. package/dist/platform/index.d.cts +34 -25
  73. package/dist/platform/index.d.ts +34 -25
  74. package/dist/platform/index.js +22 -17
  75. package/dist/promo/index.d.cts +15 -5
  76. package/dist/promo/index.d.ts +15 -5
  77. package/dist/promo/index.js +9 -3
  78. package/dist/{promo.module-CKkECmU8.d.cts → promo.module-Ddx-swUV.d.cts} +1 -1
  79. package/dist/{promo.module-DuIJdK3U.d.ts → promo.module-PsIvDcUp.d.ts} +1 -1
  80. package/dist/{promo.service-BYBu6dy3.d.cts → promo.service-BextPvx5.d.cts} +23 -1
  81. package/dist/{promo.service-CFjfF0Xm.d.ts → promo.service-Dqqh0JXn.d.ts} +23 -1
  82. package/dist/registration/index.d.cts +7 -4
  83. package/dist/registration/index.d.ts +7 -4
  84. package/dist/registration/index.js +11 -6
  85. package/dist/subscription-contract/index.d.cts +13 -3
  86. package/dist/subscription-contract/index.d.ts +13 -3
  87. package/dist/subscription-contract/index.js +5 -3
  88. package/dist/{subscription-contract.module-BfIRpc1o.d.ts → subscription-contract.module-B5wrmzkz.d.ts} +8 -1
  89. package/dist/{subscription-contract.module-DTMuUJYd.d.cts → subscription-contract.module-qaswPXwT.d.cts} +8 -1
  90. package/dist/{subscription-contract.service-CfNApLuP.d.cts → subscription-contract.service-CH2TKw-j.d.cts} +45 -3
  91. package/dist/{subscription-contract.service-CPZrTM9C.d.ts → subscription-contract.service-Dm2xEFZi.d.ts} +45 -3
  92. package/dist/{tenant-billing.controller-BLqbsONe.d.ts → tenant-billing.controller-BMH9YODT.d.ts} +92 -11
  93. package/dist/{tenant-billing.controller-DKbAqTbM.d.cts → tenant-billing.controller-BX3VExL3.d.cts} +92 -11
  94. package/dist/{tenant-billing.module-ClbjzocC.d.cts → tenant-billing.module-Do3QyKbr.d.cts} +33 -3
  95. package/dist/{tenant-billing.module-DohgJJ2m.d.ts → tenant-billing.module-W5_Y4ogN.d.ts} +33 -3
  96. package/dist/{tenant-billing.tokens-G-1xOlMr.d.cts → tenant-billing.tokens-D0vr1lfj.d.cts} +1 -1
  97. package/dist/{tenant-billing.tokens-G-1xOlMr.d.ts → tenant-billing.tokens-D0vr1lfj.d.ts} +1 -1
  98. package/dist/testing/index.d.cts +14 -13
  99. package/dist/testing/index.d.ts +14 -13
  100. package/dist/testing/index.js +54 -35
  101. package/package.json +5 -3
@@ -1,22 +1,35 @@
1
+ import {
2
+ AdminResourcesService
3
+ } from "./chunk-5MZH3VVU.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
+ SUCCESSOR_ATTEMPTS,
14
+ SubscriptionContractService,
15
+ contractChanged,
16
+ generatedDiscountLine
17
+ } from "./chunk-CVCKXKGT.js";
10
18
  import {
11
19
  resolveBundlePriceNet
12
20
  } from "./chunk-KPEMTBOP.js";
21
+ import {
22
+ AllowDuringMaintenance
23
+ } from "./chunk-TSLGMPGZ.js";
13
24
  import {
14
25
  EntitlementService,
15
26
  SUBSCRIPTION_BUNDLE_CONFIG_TOKEN,
16
27
  SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN,
17
28
  cancellationHasLanded,
29
+ contractBundleVersionIds,
30
+ contractLimits,
18
31
  toEffectiveLimitsSnapshot
19
- } from "./chunk-G6EZWECL.js";
32
+ } from "./chunk-2XBWYMOL.js";
20
33
  import {
21
34
  ENTITLEMENT_SERVICE_TOKEN
22
35
  } from "./chunk-OUDSQ3PU.js";
@@ -29,8 +42,9 @@ import {
29
42
  planNotSoldInCycle
30
43
  } from "./chunk-TBBZWZQT.js";
31
44
  import {
45
+ CONTRACT_TRANSACTION_RUNNER_TOKEN,
32
46
  SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN
33
- } from "./chunk-VBRYPU7V.js";
47
+ } from "./chunk-E224LKO2.js";
34
48
  import {
35
49
  assertContractWindow,
36
50
  assertNoNegativeDiscount,
@@ -44,6 +58,9 @@ import {
44
58
  CATALOG_ENTRY_REPOSITORY_TOKEN,
45
59
  PLAN_REPOSITORY_TOKEN
46
60
  } from "./chunk-3QVGA6JX.js";
61
+ import {
62
+ DISCOVERY_SNAPSHOT_TOKEN
63
+ } from "./chunk-QUZU2M33.js";
47
64
  import {
48
65
  ComposedTenantAuthGuard,
49
66
  holdsTenantAdminRole
@@ -61,9 +78,15 @@ import {
61
78
  USER_EMAIL_RESOLVER_TOKEN,
62
79
  USER_ID_RESOLVER_TOKEN
63
80
  } from "./chunk-GPQWGA6B.js";
81
+ import {
82
+ RLS_BYPASS_PORT_TOKEN
83
+ } from "./chunk-SZ7RFPXA.js";
64
84
  import {
65
85
  subscriberProviders
66
86
  } from "./chunk-WOEJ6K7M.js";
87
+ import {
88
+ SUBSCRIBER_REPOSITORY_TOKEN
89
+ } from "./chunk-LF6J4YYN.js";
67
90
  import {
68
91
  SaaSiCatPublicRoute
69
92
  } from "./chunk-YSC46BGM.js";
@@ -1028,23 +1051,52 @@ var SELF_SERVICE_BLOCKED_BUNDLES_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/ne
1028
1051
  var DAY_MS2 = 864e5;
1029
1052
  function computeProration(input) {
1030
1053
  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)));
1054
+ const { daysInPeriod, daysRemaining } = daysOf(periodStart, periodEnd, now);
1033
1055
  const rawDeltaNet = round2((targetPriceNet - currentPriceNet) * daysRemaining / daysInPeriod);
1034
- const prorataDeltaNet = Math.max(0, rawDeltaNet);
1035
1056
  return {
1057
+ basis: "difference",
1036
1058
  daysRemainingInPeriod: daysRemaining,
1037
1059
  daysInPeriod,
1038
1060
  periodStart,
1039
1061
  periodEnd,
1040
1062
  currentPriceNet,
1041
1063
  targetPriceNet,
1042
- prorataDeltaNet,
1064
+ prorataDeltaNet: Math.max(0, rawDeltaNet),
1043
1065
  rawDeltaNet,
1044
- isFree: rawDeltaNet < 0
1066
+ isFree: rawDeltaNet < 0,
1067
+ remainderNet: 0
1045
1068
  };
1046
1069
  }
1047
1070
  __name(computeProration, "computeProration");
1071
+ function computeNewPeriodCharge(input) {
1072
+ const { periodStart, periodEnd, now, currentPriceNet, targetPriceNet } = input;
1073
+ const { daysInPeriod, daysRemaining } = daysOf(periodStart, periodEnd, now);
1074
+ const remainderNet = round2(currentPriceNet * daysRemaining / daysInPeriod);
1075
+ const rawDeltaNet = round2(targetPriceNet - remainderNet);
1076
+ return {
1077
+ basis: "newPeriod",
1078
+ daysRemainingInPeriod: daysRemaining,
1079
+ daysInPeriod,
1080
+ periodStart,
1081
+ periodEnd,
1082
+ currentPriceNet,
1083
+ targetPriceNet,
1084
+ prorataDeltaNet: Math.max(0, rawDeltaNet),
1085
+ rawDeltaNet,
1086
+ isFree: rawDeltaNet < 0,
1087
+ remainderNet
1088
+ };
1089
+ }
1090
+ __name(computeNewPeriodCharge, "computeNewPeriodCharge");
1091
+ function daysOf(periodStart, periodEnd, now) {
1092
+ const daysInPeriod = Math.max(1, Math.round((periodEnd.getTime() - periodStart.getTime()) / DAY_MS2));
1093
+ const daysRemaining = Math.max(0, Math.min(daysInPeriod, Math.round((periodEnd.getTime() - now.getTime()) / DAY_MS2)));
1094
+ return {
1095
+ daysInPeriod,
1096
+ daysRemaining
1097
+ };
1098
+ }
1099
+ __name(daysOf, "daysOf");
1048
1100
  function round2(n) {
1049
1101
  return Math.round(n * 100) / 100;
1050
1102
  }
@@ -1081,7 +1133,8 @@ var PlanChangePreviewService = class {
1081
1133
  blockedPlans;
1082
1134
  trialProjection;
1083
1135
  subscriptionBundles;
1084
- constructor(catalogs, entitlements, subscriptions, usageSnapshot, blockedPlans = null, trialProjection = null, subscriptionBundles = null) {
1136
+ contracts;
1137
+ constructor(catalogs, entitlements, subscriptions, usageSnapshot, blockedPlans = null, trialProjection = null, subscriptionBundles = null, contracts = null) {
1085
1138
  this.catalogs = catalogs;
1086
1139
  this.entitlements = entitlements;
1087
1140
  this.subscriptions = subscriptions;
@@ -1089,6 +1142,7 @@ var PlanChangePreviewService = class {
1089
1142
  this.blockedPlans = blockedPlans;
1090
1143
  this.trialProjection = trialProjection;
1091
1144
  this.subscriptionBundles = subscriptionBundles;
1145
+ this.contracts = contracts;
1092
1146
  }
1093
1147
  async preview(tenantId, targetPlan, targetCycle, now = /* @__PURE__ */ new Date()) {
1094
1148
  const sub = await this.subscriptions.findForTenant(tenantId);
@@ -1173,7 +1227,7 @@ var PlanChangePreviewService = class {
1173
1227
  const commits = ctx.status !== "TRIAL";
1174
1228
  const isImmediate = planDirection === "UP" && (!commits || cycleDirection !== "SHORTER");
1175
1229
  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;
1230
+ 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
1231
  const blockers = [];
1178
1232
  const warnings = [];
1179
1233
  const landsAt = ctx.canceledEffectiveAt ?? ctx.canceledAt;
@@ -1360,17 +1414,41 @@ var PlanChangePreviewService = class {
1360
1414
  if (ctx.minimumTermUntil && ctx.minimumTermUntil > periodEnd) return ctx.minimumTermUntil;
1361
1415
  return periodEnd;
1362
1416
  }
1363
- computeProration(ctx, now, current, target, currentCycle, targetCycle) {
1417
+ computeProration(ctx, now, currentPriceNet, targetPriceNet, cycleDirection) {
1418
+ if (ctx.currentPeriodEnd === null) {
1419
+ return computeNewPeriodCharge({
1420
+ periodStart: now,
1421
+ periodEnd: now,
1422
+ now,
1423
+ currentPriceNet,
1424
+ targetPriceNet
1425
+ });
1426
+ }
1364
1427
  const periodStart = ctx.currentPeriodStart ?? ctx.startedAt ?? now;
1365
- const periodEnd = ctx.currentPeriodEnd ?? periodEndAfter(ctx.startedAt, ctx.currentBillingCycle, now);
1366
- return computeProration({
1428
+ const charge = cycleDirection === "SAME" ? computeProration : computeNewPeriodCharge;
1429
+ return charge({
1367
1430
  periodStart,
1368
- periodEnd,
1431
+ periodEnd: ctx.currentPeriodEnd,
1369
1432
  now,
1370
- currentPriceNet: priceForCycle(current, currentCycle) ?? 0,
1371
- targetPriceNet: priceForCycle(target, targetCycle) ?? 0
1433
+ currentPriceNet,
1434
+ targetPriceNet
1372
1435
  });
1373
1436
  }
1437
+ /**
1438
+ * The net price of the plan line in the contract in force, when it is for
1439
+ * `plan` billed in `cycle` — what the customer pays for the period they are
1440
+ * in. Null without contracts, or where the line is for another plan or
1441
+ * rhythm: a freeze is optional and does not stop the change it follows when
1442
+ * it fails, so the contract in force can still describe the plan before.
1443
+ * The caller then prices from the catalogue.
1444
+ */
1445
+ async planPricePaid(tenantId, plan, cycle, now) {
1446
+ const contract = await this.contracts?.findActiveByTenantId(tenantId, now);
1447
+ const planLine = contract?.lineItems.find((line) => line.kind === "plan");
1448
+ if (!planLine || planLine.sourceKey !== plan) return null;
1449
+ if (planLine.billingCycle !== cycle.toLowerCase()) return null;
1450
+ return planLine.priceNet;
1451
+ }
1374
1452
  };
1375
1453
  PlanChangePreviewService = _ts_decorate4([
1376
1454
  Injectable2(),
@@ -1384,6 +1462,8 @@ PlanChangePreviewService = _ts_decorate4([
1384
1462
  _ts_param2(5, Inject2(TRIAL_PROJECTION_PORT_TOKEN)),
1385
1463
  _ts_param2(6, Optional2()),
1386
1464
  _ts_param2(6, Inject2(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
1465
+ _ts_param2(7, Optional2()),
1466
+ _ts_param2(7, Inject2(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
1387
1467
  _ts_metadata2("design:type", Function),
1388
1468
  _ts_metadata2("design:paramtypes", [
1389
1469
  typeof PlanCatalogSource === "undefined" ? Object : PlanCatalogSource,
@@ -1392,6 +1472,7 @@ PlanChangePreviewService = _ts_decorate4([
1392
1472
  typeof UsageSnapshotPort === "undefined" ? Object : UsageSnapshotPort,
1393
1473
  Object,
1394
1474
  Object,
1475
+ Object,
1395
1476
  Object
1396
1477
  ])
1397
1478
  ], PlanChangePreviewService);
@@ -1509,183 +1590,1710 @@ PendingPlanMaterializationService = _ts_decorate5([
1509
1590
  ])
1510
1591
  ], PendingPlanMaterializationService);
1511
1592
 
1512
- // src/billing/subscription-contract-freeze.service.ts
1513
- import { Inject as Inject4, Injectable as Injectable4, Optional as Optional4, UnprocessableEntityException } from "@nestjs/common";
1514
- function _ts_decorate6(decorators, target, key, desc) {
1593
+ // src/billing/subscription-contract-freeze.service.ts
1594
+ import { Inject as Inject4, Injectable as Injectable4, Optional as Optional4, UnprocessableEntityException } from "@nestjs/common";
1595
+ function _ts_decorate6(decorators, target, key, desc) {
1596
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1597
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1598
+ 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;
1599
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1600
+ }
1601
+ __name(_ts_decorate6, "_ts_decorate");
1602
+ function _ts_metadata4(k, v) {
1603
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1604
+ }
1605
+ __name(_ts_metadata4, "_ts_metadata");
1606
+ function _ts_param4(paramIndex, decorator) {
1607
+ return function(target, key) {
1608
+ decorator(target, key, paramIndex);
1609
+ };
1610
+ }
1611
+ __name(_ts_param4, "_ts_param");
1612
+ var SubscriptionContractFreezeService = class {
1613
+ static {
1614
+ __name(this, "SubscriptionContractFreezeService");
1615
+ }
1616
+ catalogs;
1617
+ entitlements;
1618
+ contracts;
1619
+ source;
1620
+ subscriptions;
1621
+ promoCodes;
1622
+ constructor(catalogs, entitlements, contracts, source, writes = null, subscriptions = null, promoCodes = null) {
1623
+ this.catalogs = catalogs;
1624
+ this.entitlements = entitlements;
1625
+ this.contracts = contracts;
1626
+ this.source = source;
1627
+ this.subscriptions = subscriptions;
1628
+ this.promoCodes = promoCodes;
1629
+ if (writes?.bindsPlanVersion === false) {
1630
+ 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`.");
1631
+ }
1632
+ }
1633
+ assertPartyFor(tenantId) {
1634
+ return this.contracts.assertPartyFor(tenantId);
1635
+ }
1636
+ async endOnCancellation(tenantId, effectiveAt) {
1637
+ const active = await this.contracts.findActiveByTenantId(tenantId, effectiveAt);
1638
+ if (!active) return;
1639
+ await this.contracts.terminate(active.id, {
1640
+ effectiveUntil: effectiveAt,
1641
+ // Only when it is already over. An ordinary cancellation lands at
1642
+ // the term end, months out, and the customer is under this contract
1643
+ // until then — the invoice side has to keep finding it, and the
1644
+ // window in `findActiveByTenantId` is what stops it afterwards.
1645
+ status: effectiveAt <= /* @__PURE__ */ new Date() ? "terminated" : null
1646
+ });
1647
+ this.entitlements.invalidateTenant(tenantId);
1648
+ }
1649
+ async freezeOnPlanChange(tenantId, newPlan, billingCycle, effectiveFrom, endsAt = null) {
1650
+ const data = await this.composeOnPlanChange(tenantId, newPlan, billingCycle, effectiveFrom, endsAt);
1651
+ for (let attempt = 0; attempt < SUCCESSOR_ATTEMPTS; attempt++) {
1652
+ const previous = await this.contracts.findActiveByTenantId(tenantId, effectiveFrom);
1653
+ const written = await this.contracts.writeSuccessor(previous, data, effectiveFrom);
1654
+ if (written) {
1655
+ this.entitlements.invalidateTenant(tenantId);
1656
+ return;
1657
+ }
1658
+ }
1659
+ throw contractChanged(tenantId);
1660
+ }
1661
+ /**
1662
+ * The contract `freezeOnPlanChange` would write, and nothing written: the
1663
+ * plan version the subscription is bound to, the add-ons booked, the
1664
+ * catalogue's rate and currency, and a promotional code not yet recorded —
1665
+ * checked the way a contract is checked before it is written.
1666
+ *
1667
+ * Public so that an operator's refresh can show a full re-freeze before it
1668
+ * makes one, measured by the same rules a plan change is.
1669
+ */
1670
+ async composeOnPlanChange(tenantId, newPlan, billingCycle, effectiveFrom, endsAt = null) {
1671
+ const cycle = billingCycle === "YEARLY" ? "yearly" : "monthly";
1672
+ const catalog = await this.catalogs.current();
1673
+ const vatRate = catalog.vatRate;
1674
+ assertTaxRatePercent("catalog.vatRate", vatRate);
1675
+ assertContractWindow(effectiveFrom, endsAt);
1676
+ const bound = await this.source.findBoundPlanVersion(tenantId);
1677
+ if (!bound || bound.planId !== newPlan) {
1678
+ throw new Error(`The contract for tenant ${tenantId} was asked to record plan '${newPlan}', but the subscription is bound to ${bound ? `'${bound.planId}' (version ${bound.id})` : "no plan version"}. Bind the plan version on the subscription before the contract is frozen.`);
1679
+ }
1680
+ const stem = findPlan(catalog, newPlan);
1681
+ const planDef = planDefFromVersion({
1682
+ id: newPlan,
1683
+ name: stem?.name ?? newPlan,
1684
+ tagline: stem?.tagline
1685
+ }, bound);
1686
+ if (isPlanNotSoldInCycle(planDef, billingCycle)) {
1687
+ throw new UnprocessableEntityException(planNotSoldInCycle(planDef, billingCycle));
1688
+ }
1689
+ await this.contracts.assertPartyFor(tenantId);
1690
+ const bundles = await this.source.loadBookedBundles(tenantId, cycle);
1691
+ const planPriceNet = listPriceNet(planDef, billingCycle) ?? 0;
1692
+ const planLineItem = {
1693
+ kind: "plan",
1694
+ sourceKey: newPlan,
1695
+ sourceVersionId: bound.id,
1696
+ titleSnapshot: planDef.name ?? newPlan,
1697
+ descriptionSnapshot: planDef.tagline ?? null,
1698
+ quantity: 1,
1699
+ unit: null,
1700
+ priceNet: planPriceNet,
1701
+ billingCycle: cycle,
1702
+ minimumTermUntil: null,
1703
+ featuresSnapshot: planDef.features,
1704
+ quotaEffectsSnapshot: planDef.quotas,
1705
+ metadata: null
1706
+ };
1707
+ const redeemed = await this.redeemedCodeNotYetRecorded(tenantId, planPriceNet, billingCycle, vatRate);
1708
+ const lineItems = recordContractLinesMoney([
1709
+ planLineItem,
1710
+ ...bundles.lineItems,
1711
+ ...redeemed ? [
1712
+ redeemed.line
1713
+ ] : []
1714
+ ], {
1715
+ currency: catalog.currency,
1716
+ taxRate: vatRate
1717
+ });
1718
+ const totals = contractTotalsOf(lineItems, cycle);
1719
+ const priceSnapshot = {
1720
+ currency: catalog.currency,
1721
+ billingCycle: cycle,
1722
+ subtotalNet: totals.subtotalNet,
1723
+ discountNet: totals.discountNet,
1724
+ totalNet: totals.totalNet,
1725
+ vatRate,
1726
+ totalGross: totals.totalGross
1727
+ };
1728
+ assertOnePlanLine(lineItems);
1729
+ assertNoNegativeDiscount({
1730
+ priceSnapshot
1731
+ });
1732
+ const { limits, leftOutBundleVersionIds } = await this.entitlements.computeContractLimits(tenantId, effectiveFrom, catalog);
1733
+ const data = {
1734
+ tenantId,
1735
+ status: "active",
1736
+ effectiveFrom,
1737
+ // The successor inherits the ending. A cancellation capped the
1738
+ // contract that was running when it was declared; every contract
1739
+ // after it ends on the same date, or the repair lasts exactly until
1740
+ // the next plan change.
1741
+ effectiveUntil: endsAt,
1742
+ originalPlanVersionId: bound.id,
1743
+ originalBundleVersionIds: bundles.bundleVersionIds,
1744
+ entitlementSnapshot: {
1745
+ plan: limits.plan,
1746
+ quotas: {
1747
+ ...limits.quotas
1748
+ },
1749
+ features: [
1750
+ ...limits.features
1751
+ ],
1752
+ ...leftOutBundleVersionIds.length > 0 ? {
1753
+ leftOutBundleVersionIds
1754
+ } : {}
1755
+ },
1756
+ priceSnapshot,
1757
+ promoCodeSnapshots: redeemed ? [
1758
+ redeemed.snapshot
1759
+ ] : [],
1760
+ lineItems
1761
+ };
1762
+ return data;
1763
+ }
1764
+ /**
1765
+ * The code redeemed for the tenant's subscription, as a discount line, where
1766
+ * no contract of the tenant records it yet.
1767
+ *
1768
+ * The first contract written after a redemption records it — at onboarding,
1769
+ * or at the activation after a trial, where onboarding wrote none — and the
1770
+ * ones after it do not repeat it: the discount runs from where it was first
1771
+ * agreed, for the duration on its snapshot, counted from the first period
1772
+ * that is paid. A contract concluded from an offer that carried the code
1773
+ * already records it. Nothing is recorded without the promo module, for a
1774
+ * reversed redemption, or where a contract written since the redemption
1775
+ * does not record it: that was the first one, written before contracts
1776
+ * recorded codes or by the application itself, and a later contract does
1777
+ * not start the discount again.
1778
+ */
1779
+ async redeemedCodeNotYetRecorded(tenantId, planPriceNet, billingCycle, vatRate) {
1780
+ if (!this.promoCodes || !this.subscriptions) return null;
1781
+ const subscription = await this.subscriptions.findForTenant(tenantId);
1782
+ if (!subscription?.id) return null;
1783
+ const redeemed = await this.promoCodes.redeemedCodeFor(subscription.id);
1784
+ if (!redeemed) return null;
1785
+ const recorded = await this.contracts.list({
1786
+ tenantId
1787
+ });
1788
+ if (recorded.some((contract) => recordsPromoCode(contract, redeemed.code) || contract.createdAt > redeemed.redeemedAt)) {
1789
+ return null;
1790
+ }
1791
+ const cycle = billingCycle === "YEARLY" ? "yearly" : "monthly";
1792
+ const snapshot = {
1793
+ code: redeemed.code,
1794
+ label: buildLabel(redeemed, billingCycle),
1795
+ valueType: redeemed.valueType,
1796
+ value: Number(redeemed.value),
1797
+ resolvedAmountNet: promoCodeDiscountNet(planPriceNet, vatRate, redeemed),
1798
+ durationType: redeemed.durationType,
1799
+ durationValue: redeemed.durationValue
1800
+ };
1801
+ const line = generatedDiscountLine({
1802
+ billingCycle: cycle,
1803
+ promoCodeSnapshot: snapshot,
1804
+ promotionSnapshots: []
1805
+ }, snapshot.resolvedAmountNet);
1806
+ return {
1807
+ snapshot,
1808
+ line: {
1809
+ kind: line.kind,
1810
+ sourceKey: line.sourceKey,
1811
+ sourceVersionId: line.sourceVersionId ?? null,
1812
+ titleSnapshot: line.titleSnapshot,
1813
+ descriptionSnapshot: line.descriptionSnapshot ?? null,
1814
+ quantity: line.quantity,
1815
+ unit: line.unit ?? null,
1816
+ priceNet: line.priceNet,
1817
+ billingCycle: cycle,
1818
+ minimumTermUntil: null,
1819
+ featuresSnapshot: [],
1820
+ quotaEffectsSnapshot: {},
1821
+ metadata: line.metadata ?? null
1822
+ }
1823
+ };
1824
+ }
1825
+ };
1826
+ SubscriptionContractFreezeService = _ts_decorate6([
1827
+ Injectable4(),
1828
+ _ts_param4(0, Inject4(PLAN_CATALOG_SOURCE_TOKEN)),
1829
+ _ts_param4(1, Inject4(ENTITLEMENT_SERVICE_TOKEN)),
1830
+ _ts_param4(2, Inject4(SubscriptionContractService)),
1831
+ _ts_param4(3, Inject4(CONTRACT_FREEZE_SOURCE_PORT_TOKEN)),
1832
+ _ts_param4(4, Optional4()),
1833
+ _ts_param4(4, Inject4(SUBSCRIPTION_WRITE_PORT_TOKEN)),
1834
+ _ts_param4(5, Optional4()),
1835
+ _ts_param4(5, Inject4(SUBSCRIPTION_USAGE_PORT_TOKEN)),
1836
+ _ts_param4(6, Optional4()),
1837
+ _ts_param4(6, Inject4(PromoCodesService)),
1838
+ _ts_metadata4("design:type", Function),
1839
+ _ts_metadata4("design:paramtypes", [
1840
+ typeof PlanCatalogSource === "undefined" ? Object : PlanCatalogSource,
1841
+ typeof EntitlementService === "undefined" ? Object : EntitlementService,
1842
+ typeof SubscriptionContractService === "undefined" ? Object : SubscriptionContractService,
1843
+ typeof ContractFreezeSourcePort === "undefined" ? Object : ContractFreezeSourcePort,
1844
+ Object,
1845
+ Object,
1846
+ Object
1847
+ ])
1848
+ ], SubscriptionContractFreezeService);
1849
+ function recordsPromoCode(contract, code) {
1850
+ return contract.promoCodeSnapshots.some((snapshot) => snapshot !== null && typeof snapshot === "object" && snapshot.code === code);
1851
+ }
1852
+ __name(recordsPromoCode, "recordsPromoCode");
1853
+
1854
+ // src/billing/contract-refresh.ts
1855
+ var PRICE_FIELDS = [
1856
+ "currency",
1857
+ "billingCycle",
1858
+ "vatRate",
1859
+ "subtotalNet",
1860
+ "discountNet",
1861
+ "totalNet",
1862
+ "totalGross"
1863
+ ];
1864
+ var LINE_MONEY_FIELDS = [
1865
+ "quantity",
1866
+ "billingCycle",
1867
+ "currency",
1868
+ "taxRate",
1869
+ "priceNet",
1870
+ "priceGross",
1871
+ "taxAmount"
1872
+ ];
1873
+ function featureChangeOf(before, after) {
1874
+ const was = new Set(before);
1875
+ const is = new Set(after);
1876
+ return {
1877
+ added: [
1878
+ ...is
1879
+ ].filter((key) => !was.has(key)).sort(),
1880
+ removed: [
1881
+ ...was
1882
+ ].filter((key) => !is.has(key)).sort()
1883
+ };
1884
+ }
1885
+ __name(featureChangeOf, "featureChangeOf");
1886
+ function quotaChangesOf(before, after) {
1887
+ const keys = [
1888
+ .../* @__PURE__ */ new Set([
1889
+ ...Object.keys(before),
1890
+ ...Object.keys(after)
1891
+ ])
1892
+ ].sort();
1893
+ return keys.map((key) => ({
1894
+ key,
1895
+ before: before[key] ?? null,
1896
+ after: after[key] ?? null
1897
+ })).filter((change) => change.before !== change.after);
1898
+ }
1899
+ __name(quotaChangesOf, "quotaChangesOf");
1900
+ function moneyChangesOf(contract, successor) {
1901
+ const changes = [];
1902
+ for (const field of PRICE_FIELDS) {
1903
+ const before = contract.priceSnapshot[field];
1904
+ const after = successor.priceSnapshot[field];
1905
+ if (before !== after) changes.push({
1906
+ field: `priceSnapshot.${field}`,
1907
+ before,
1908
+ after
1909
+ });
1910
+ }
1911
+ const unmatched = [
1912
+ ...successor.lineItems
1913
+ ];
1914
+ for (const line of contract.lineItems) {
1915
+ const index = unmatched.findIndex((other2) => lineKeyOf(other2) === lineKeyOf(line));
1916
+ if (index === -1) {
1917
+ changes.push({
1918
+ field: `line ${lineKeyOf(line)}`,
1919
+ before: line.priceNet,
1920
+ after: null
1921
+ });
1922
+ continue;
1923
+ }
1924
+ const [other] = unmatched.splice(index, 1);
1925
+ changes.push(...lineMoneyChangesOf(line, other));
1926
+ }
1927
+ for (const other of unmatched) {
1928
+ changes.push({
1929
+ field: `line ${lineKeyOf(other)}`,
1930
+ before: null,
1931
+ after: other.priceNet
1932
+ });
1933
+ }
1934
+ return changes;
1935
+ }
1936
+ __name(moneyChangesOf, "moneyChangesOf");
1937
+ function lineMoneyChangesOf(before, after) {
1938
+ return LINE_MONEY_FIELDS.filter((field) => before[field] !== after[field]).map((field) => ({
1939
+ field: `line ${lineKeyOf(before)} ${field}`,
1940
+ before: before[field],
1941
+ after: after[field]
1942
+ }));
1943
+ }
1944
+ __name(lineMoneyChangesOf, "lineMoneyChangesOf");
1945
+ function lineKeyOf(line) {
1946
+ return `${line.kind}:${line.sourceKey}`;
1947
+ }
1948
+ __name(lineKeyOf, "lineKeyOf");
1949
+ function withAgreedTerms(contract, successor) {
1950
+ const unmatched = [
1951
+ ...contract.lineItems
1952
+ ];
1953
+ return {
1954
+ ...successor,
1955
+ originalOfferId: contract.originalOfferId,
1956
+ termsSnapshot: contract.termsSnapshot ? {
1957
+ ...contract.termsSnapshot
1958
+ } : null,
1959
+ promotionSnapshots: [
1960
+ ...contract.promotionSnapshots
1961
+ ],
1962
+ promoCodeSnapshots: [
1963
+ ...contract.promoCodeSnapshots
1964
+ ],
1965
+ lineItems: successor.lineItems.map((line) => {
1966
+ const index = unmatched.findIndex((agreed2) => lineKeyOf(agreed2) === lineKeyOf(line));
1967
+ if (index === -1) return line;
1968
+ const [agreed] = unmatched.splice(index, 1);
1969
+ return {
1970
+ ...line,
1971
+ minimumTermUntil: agreed.minimumTermUntil
1972
+ };
1973
+ })
1974
+ };
1975
+ }
1976
+ __name(withAgreedTerms, "withAgreedTerms");
1977
+ function vocabularyOf(input) {
1978
+ return {
1979
+ unknown: [
1980
+ ...input.frozen
1981
+ ].filter((key) => !input.known.has(key)).sort(),
1982
+ missing: [
1983
+ ...input.grantedToday
1984
+ ].filter((key) => !input.frozenWithReplacements.has(key)).sort()
1985
+ };
1986
+ }
1987
+ __name(vocabularyOf, "vocabularyOf");
1988
+
1989
+ // src/billing/contract-refresh.service.ts
1990
+ import { Inject as Inject5, Injectable as Injectable5, Logger as Logger2, Optional as Optional5 } from "@nestjs/common";
1991
+ import { ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES } from "@saasicat/core";
1992
+ function _ts_decorate7(decorators, target, key, desc) {
1993
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1994
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1995
+ 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;
1996
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1997
+ }
1998
+ __name(_ts_decorate7, "_ts_decorate");
1999
+ function _ts_metadata5(k, v) {
2000
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2001
+ }
2002
+ __name(_ts_metadata5, "_ts_metadata");
2003
+ function _ts_param5(paramIndex, decorator) {
2004
+ return function(target, key) {
2005
+ decorator(target, key, paramIndex);
2006
+ };
2007
+ }
2008
+ __name(_ts_param5, "_ts_param");
2009
+ var UNREADABLE = [
2010
+ "NO_SUBSCRIPTION",
2011
+ "REFUSED"
2012
+ ];
2013
+ var NO_VOCABULARY = {
2014
+ unknown: [],
2015
+ missing: []
2016
+ };
2017
+ var ContractRefreshService = class {
2018
+ static {
2019
+ __name(this, "ContractRefreshService");
2020
+ }
2021
+ contracts;
2022
+ entitlements;
2023
+ usage;
2024
+ catalogs;
2025
+ freeze;
2026
+ discovery;
2027
+ rlsBypass;
2028
+ audit;
2029
+ logger = new Logger2("SaaSiCat.ContractRefresh");
2030
+ constructor(contracts, entitlements, usage, catalogs, freeze, discovery = null, rlsBypass = null, audit = null) {
2031
+ this.contracts = contracts;
2032
+ this.entitlements = entitlements;
2033
+ this.usage = usage;
2034
+ this.catalogs = catalogs;
2035
+ this.freeze = freeze;
2036
+ this.discovery = discovery;
2037
+ this.rlsBypass = rlsBypass;
2038
+ this.audit = audit;
2039
+ }
2040
+ /** The contracts in force whose frozen features hold unknown keys or lack granted ones. */
2041
+ inspect(asOf = /* @__PURE__ */ new Date()) {
2042
+ return this.withBypass(async () => {
2043
+ const previews = await this.previewAll({}, "features", asOf);
2044
+ return {
2045
+ inForce: previews.length,
2046
+ stale: previews.filter(({ vocabulary }) => vocabulary.unknown.length > 0 || vocabulary.missing.length > 0),
2047
+ unreadable: previews.filter(({ refusal }) => refusal !== null && UNREADABLE.includes(refusal.code))
2048
+ };
2049
+ });
2050
+ }
2051
+ /** What a refresh would change on each contract selected. Writes nothing. */
2052
+ preview(selection, mode, asOf = /* @__PURE__ */ new Date()) {
2053
+ return this.withBypass(() => this.previewAll(selection, mode, asOf));
2054
+ }
2055
+ /**
2056
+ * Writes a successor for each contract selected that would change and is
2057
+ * not refused, and keeps the contract it replaces as superseded
2058
+ * (`SC-AUD-007`). What it writes is what `preview` shows at this moment.
2059
+ */
2060
+ apply(selection, mode, actor, asOf = /* @__PURE__ */ new Date()) {
2061
+ return this.withBypass(async () => {
2062
+ const catalog = await this.catalogs.current();
2063
+ const outcomes = [];
2064
+ for (const target of await this.targets(selection, asOf)) {
2065
+ const planned = await this.plan(target, mode, catalog, asOf);
2066
+ outcomes.push(await this.write(target, planned, mode, actor, asOf));
2067
+ }
2068
+ return outcomes;
2069
+ });
2070
+ }
2071
+ // -----------------------------------------------------------------
2072
+ async previewAll(selection, mode, asOf) {
2073
+ const catalog = await this.catalogs.current();
2074
+ const previews = [];
2075
+ for (const target of await this.targets(selection, asOf)) {
2076
+ previews.push((await this.plan(target, mode, catalog, asOf)).preview);
2077
+ }
2078
+ return previews;
2079
+ }
2080
+ async write(target, planned, mode, actor, asOf) {
2081
+ const { preview, successor } = planned;
2082
+ const unwritten = {
2083
+ ...preview,
2084
+ successorId: null,
2085
+ moved: false
2086
+ };
2087
+ if (!target.contract || !successor || preview.refusal || !preview.changes) {
2088
+ return unwritten;
2089
+ }
2090
+ let written;
2091
+ try {
2092
+ written = await this.contracts.writeSuccessor(target.contract, successor, asOf, {
2093
+ keepParties: mode === "features"
2094
+ });
2095
+ } catch (error) {
2096
+ return {
2097
+ ...unwritten,
2098
+ refusal: {
2099
+ code: "REFUSED",
2100
+ reason: messageOf(error)
2101
+ }
2102
+ };
2103
+ }
2104
+ if (!written) return {
2105
+ ...unwritten,
2106
+ moved: true
2107
+ };
2108
+ this.entitlements.invalidateTenant(written.tenantId);
2109
+ await this.record(actor, target.contract, written, mode, preview);
2110
+ return {
2111
+ ...preview,
2112
+ successorId: written.id,
2113
+ moved: false
2114
+ };
2115
+ }
2116
+ async plan(target, mode, catalog, asOf) {
2117
+ const contract = target.contract;
2118
+ if (!contract) {
2119
+ return refused(target.contractId, target.found?.tenantId ?? null, NO_VOCABULARY, {
2120
+ code: "NOT_IN_FORCE"
2121
+ });
2122
+ }
2123
+ const frozen = contractLimits(contract);
2124
+ const known = this.knownFeatures(catalog);
2125
+ const unknownOnly = vocabularyOf({
2126
+ frozen: frozen.features,
2127
+ frozenWithReplacements: frozen.features,
2128
+ grantedToday: /* @__PURE__ */ new Set(),
2129
+ known
2130
+ });
2131
+ let today;
2132
+ try {
2133
+ today = await this.entitlements.contractFeaturesFor(contract.tenantId, contractBundleVersionIds(contract), catalog);
2134
+ } catch (error) {
2135
+ return refused(contract.id, contract.tenantId, unknownOnly, {
2136
+ code: "REFUSED",
2137
+ reason: messageOf(error)
2138
+ });
2139
+ }
2140
+ if (!today) {
2141
+ return refused(contract.id, contract.tenantId, unknownOnly, {
2142
+ code: "NO_SUBSCRIPTION"
2143
+ });
2144
+ }
2145
+ const grantedToday = today.features;
2146
+ const vocabulary = vocabularyOf({
2147
+ frozen: frozen.features,
2148
+ frozenWithReplacements: this.entitlements.withReplacements(frozen.features),
2149
+ grantedToday,
2150
+ known
2151
+ });
2152
+ if (mode === "features") {
2153
+ const recorded = recordedPlanVersionOf(contract);
2154
+ if (!recorded) {
2155
+ return refused(contract.id, contract.tenantId, vocabulary, {
2156
+ code: "PLAN_VERSION_NOT_RECORDED"
2157
+ });
2158
+ }
2159
+ if (recorded !== today.planVersionId) {
2160
+ return refused(contract.id, contract.tenantId, vocabulary, {
2161
+ code: "PLAN_VERSION_MOVED",
2162
+ recorded,
2163
+ bound: today.planVersionId
2164
+ });
2165
+ }
2166
+ return this.compared(contract, frozen, vocabulary, {
2167
+ ...this.contracts.dataOf(contract),
2168
+ status: "active",
2169
+ effectiveFrom: asOf,
2170
+ entitlementSnapshot: {
2171
+ plan: frozen.plan,
2172
+ quotas: {
2173
+ ...frozen.quotas
2174
+ },
2175
+ features: [
2176
+ ...grantedToday
2177
+ ].sort(),
2178
+ ...leftOutOf(contract)
2179
+ }
2180
+ });
2181
+ }
2182
+ let successor;
2183
+ try {
2184
+ successor = await this.refrozen(contract, asOf);
2185
+ } catch (error) {
2186
+ return refused(contract.id, contract.tenantId, vocabulary, {
2187
+ code: "REFUSED",
2188
+ reason: messageOf(error)
2189
+ });
2190
+ }
2191
+ const planned = this.compared(contract, frozen, vocabulary, successor);
2192
+ if (planned.preview.money.length > 0) {
2193
+ planned.preview.refusal = {
2194
+ code: "MONEY_WOULD_CHANGE"
2195
+ };
2196
+ }
2197
+ return planned;
2198
+ }
2199
+ /**
2200
+ * The contract `freezeOnPlanChange` would write for the tenant now, with
2201
+ * the terms the contract it replaces agreed.
2202
+ */
2203
+ async refrozen(contract, asOf) {
2204
+ const subscription = await this.usage.findForTenant(contract.tenantId);
2205
+ if (!subscription) throw new Error(`Tenant '${contract.tenantId}' has no subscription.`);
2206
+ const composed = await this.freeze.composeOnPlanChange(
2207
+ contract.tenantId,
2208
+ subscription.plan,
2209
+ // The column behind it is the `BillingCycle` enum in the canonical
2210
+ // schema; the usage port types it as a string.
2211
+ subscription.billingCycle,
2212
+ asOf,
2213
+ contract.effectiveUntil
2214
+ );
2215
+ return withAgreedTerms(contract, composed);
2216
+ }
2217
+ compared(contract, frozen, vocabulary, successor) {
2218
+ const snapshot = successor.entitlementSnapshot;
2219
+ const features = featureChangeOf(frozen.features, snapshot?.features ?? []);
2220
+ const quotas = quotaChangesOf(frozen.quotas, snapshot?.quotas ?? {});
2221
+ const money = moneyChangesOf(contract, successor);
2222
+ const leftOutMoved = !sameMembers(contract.entitlementSnapshot?.leftOutBundleVersionIds ?? [], snapshot?.leftOutBundleVersionIds ?? []);
2223
+ return {
2224
+ preview: {
2225
+ contractId: contract.id,
2226
+ tenantId: contract.tenantId,
2227
+ vocabulary,
2228
+ features,
2229
+ quotas,
2230
+ money,
2231
+ changes: features.added.length > 0 || features.removed.length > 0 || quotas.length > 0 || money.length > 0 || frozen.plan !== snapshot?.plan || leftOutMoved,
2232
+ refusal: null
2233
+ },
2234
+ successor
2235
+ };
2236
+ }
2237
+ /**
2238
+ * Every feature key the application knows: the ones its code declares, the
2239
+ * old keys those declarations say they replace, and the catalogue's.
2240
+ */
2241
+ knownFeatures(catalog) {
2242
+ const known = /* @__PURE__ */ new Set();
2243
+ for (const feature of this.discovery?.features ?? []) {
2244
+ known.add(feature.featureKey);
2245
+ for (const old of feature.replaces ?? []) known.add(old);
2246
+ }
2247
+ for (const feature of catalog.features ?? []) known.add(feature.key);
2248
+ return known;
2249
+ }
2250
+ /** The contracts selected, each read as it stands, or `null` where it is not in force. */
2251
+ async targets(selection, asOf) {
2252
+ if (selection.contractIds === void 0) {
2253
+ const inForce = await this.contracts.list({
2254
+ asOf
2255
+ });
2256
+ return inForce.filter((contract) => isInForce(contract, asOf)).map((contract) => ({
2257
+ contractId: contract.id,
2258
+ found: contract,
2259
+ contract
2260
+ }));
2261
+ }
2262
+ const targets = [];
2263
+ for (const contractId of selection.contractIds) {
2264
+ const found = await this.contracts.findById(contractId);
2265
+ targets.push({
2266
+ contractId,
2267
+ found,
2268
+ contract: found && isInForce(found, asOf) ? found : null
2269
+ });
2270
+ }
2271
+ return targets;
2272
+ }
2273
+ /** Best effort: the successor stands whether or not its record could be written (`SC-AUD-004`). */
2274
+ async record(actor, previous, successor, mode, preview) {
2275
+ if (!this.audit) return;
2276
+ try {
2277
+ await this.audit.log({
2278
+ actor,
2279
+ entity: "SubscriptionContract",
2280
+ entityId: successor.id,
2281
+ action: "SUBSCRIPTION_CONTRACT_REFRESH",
2282
+ changes: {
2283
+ mode,
2284
+ supersedes: previous.id,
2285
+ tenantId: successor.tenantId,
2286
+ features: preview.features,
2287
+ quotas: preview.quotas
2288
+ }
2289
+ });
2290
+ } catch (error) {
2291
+ this.logger.error(`The audit record of the refresh that wrote contract ${successor.id} could not be written; the successor itself was.`, error instanceof Error ? error.stack : String(error));
2292
+ }
2293
+ }
2294
+ withBypass(read) {
2295
+ return this.rlsBypass ? this.rlsBypass.runWithBypass(read) : read();
2296
+ }
2297
+ };
2298
+ ContractRefreshService = _ts_decorate7([
2299
+ Injectable5(),
2300
+ _ts_param5(0, Inject5(SubscriptionContractService)),
2301
+ _ts_param5(1, Inject5(ENTITLEMENT_SERVICE_TOKEN)),
2302
+ _ts_param5(2, Inject5(SUBSCRIPTION_USAGE_PORT_TOKEN)),
2303
+ _ts_param5(3, Inject5(PLAN_CATALOG_SOURCE_TOKEN)),
2304
+ _ts_param5(4, Inject5(SubscriptionContractFreezeService)),
2305
+ _ts_param5(5, Optional5()),
2306
+ _ts_param5(5, Inject5(DISCOVERY_SNAPSHOT_TOKEN)),
2307
+ _ts_param5(6, Optional5()),
2308
+ _ts_param5(6, Inject5(RLS_BYPASS_PORT_TOKEN)),
2309
+ _ts_param5(7, Optional5()),
2310
+ _ts_param5(7, Inject5(AdminAuditService)),
2311
+ _ts_metadata5("design:type", Function),
2312
+ _ts_metadata5("design:paramtypes", [
2313
+ typeof SubscriptionContractService === "undefined" ? Object : SubscriptionContractService,
2314
+ typeof EntitlementService === "undefined" ? Object : EntitlementService,
2315
+ typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort,
2316
+ typeof PlanCatalogSource === "undefined" ? Object : PlanCatalogSource,
2317
+ typeof SubscriptionContractFreezeService === "undefined" ? Object : SubscriptionContractFreezeService,
2318
+ Object,
2319
+ Object,
2320
+ Object
2321
+ ])
2322
+ ], ContractRefreshService);
2323
+ function refused(contractId, tenantId, vocabulary, refusal) {
2324
+ return {
2325
+ preview: {
2326
+ contractId,
2327
+ tenantId,
2328
+ vocabulary,
2329
+ features: {
2330
+ added: [],
2331
+ removed: []
2332
+ },
2333
+ quotas: [],
2334
+ money: [],
2335
+ changes: false,
2336
+ refusal
2337
+ },
2338
+ successor: null
2339
+ };
2340
+ }
2341
+ __name(refused, "refused");
2342
+ function leftOutOf(contract) {
2343
+ const leftOut = contract.entitlementSnapshot?.leftOutBundleVersionIds;
2344
+ return leftOut && leftOut.length > 0 ? {
2345
+ leftOutBundleVersionIds: [
2346
+ ...leftOut
2347
+ ]
2348
+ } : {};
2349
+ }
2350
+ __name(leftOutOf, "leftOutOf");
2351
+ function recordedPlanVersionOf(contract) {
2352
+ return contract.originalPlanVersionId ?? contract.lineItems.find((line) => line.kind === "plan")?.sourceVersionId ?? null;
2353
+ }
2354
+ __name(recordedPlanVersionOf, "recordedPlanVersionOf");
2355
+ function isInForce(contract, asOf) {
2356
+ return ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES.includes(contract.status) && contract.effectiveFrom.getTime() <= asOf.getTime() && (contract.effectiveUntil === null || contract.effectiveUntil.getTime() > asOf.getTime());
2357
+ }
2358
+ __name(isInForce, "isInForce");
2359
+ function sameMembers(a, b) {
2360
+ const left = new Set(a);
2361
+ const right = new Set(b);
2362
+ return left.size === right.size && [
2363
+ ...left
2364
+ ].every((id) => right.has(id));
2365
+ }
2366
+ __name(sameMembers, "sameMembers");
2367
+ function messageOf(error) {
2368
+ if (error && typeof error === "object" && "getResponse" in error) {
2369
+ const response = error.getResponse();
2370
+ if (response && typeof response === "object" && "message" in response) {
2371
+ return String(response.message);
2372
+ }
2373
+ }
2374
+ return error instanceof Error ? error.message : String(error);
2375
+ }
2376
+ __name(messageOf, "messageOf");
2377
+
2378
+ // src/billing/charges/subscriber-charge.tokens.ts
2379
+ var SUBSCRIBER_LEDGER_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/SubscriberLedgerRepository");
2380
+
2381
+ // src/billing/charges/subscriber-account.service.ts
2382
+ import { Inject as Inject6, Injectable as Injectable6 } from "@nestjs/common";
2383
+
2384
+ // src/billing/charges/subscriber-account.ts
2385
+ var SOURCE_ORDER = {
2386
+ plan: 0,
2387
+ bundle: 1,
2388
+ discount: 2
2389
+ };
2390
+ function toWire(charge) {
2391
+ return {
2392
+ ...charge,
2393
+ periodStart: charge.periodStart.toISOString(),
2394
+ periodEnd: charge.periodEnd.toISOString(),
2395
+ bookedAt: charge.bookedAt.toISOString(),
2396
+ createdAt: charge.createdAt.toISOString()
2397
+ };
2398
+ }
2399
+ __name(toWire, "toWire");
2400
+ function newestFirst(a, b) {
2401
+ 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);
2402
+ }
2403
+ __name(newestFirst, "newestFirst");
2404
+ function accountViewOf(input) {
2405
+ const titles = /* @__PURE__ */ new Map();
2406
+ for (const contract of input.contracts) {
2407
+ for (const line of contract.lineItems) titles.set(line.id, line.titleSnapshot);
2408
+ }
2409
+ const entries = [
2410
+ ...input.charges
2411
+ ].sort(newestFirst).map((charge) => ({
2412
+ charge: toWire(charge),
2413
+ title: titles.get(charge.contractLineItemId) ?? null
2414
+ }));
2415
+ const subscriber = input.subscriber;
2416
+ return {
2417
+ holder: subscriber ? {
2418
+ id: subscriber.id,
2419
+ customerNumber: subscriber.customerNumber,
2420
+ legalName: subscriber.legalName
2421
+ } : null,
2422
+ entries
2423
+ };
2424
+ }
2425
+ __name(accountViewOf, "accountViewOf");
2426
+
2427
+ // src/billing/charges/subscriber-account.service.ts
2428
+ function _ts_decorate8(decorators, target, key, desc) {
2429
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2430
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2431
+ 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;
2432
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2433
+ }
2434
+ __name(_ts_decorate8, "_ts_decorate");
2435
+ function _ts_metadata6(k, v) {
2436
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2437
+ }
2438
+ __name(_ts_metadata6, "_ts_metadata");
2439
+ function _ts_param6(paramIndex, decorator) {
2440
+ return function(target, key) {
2441
+ decorator(target, key, paramIndex);
2442
+ };
2443
+ }
2444
+ __name(_ts_param6, "_ts_param");
2445
+ var SubscriberAccountService = class {
2446
+ static {
2447
+ __name(this, "SubscriberAccountService");
2448
+ }
2449
+ ledger;
2450
+ contracts;
2451
+ subscribers;
2452
+ subscriptions;
2453
+ constructor(ledger, contracts, subscribers, subscriptions) {
2454
+ this.ledger = ledger;
2455
+ this.contracts = contracts;
2456
+ this.subscribers = subscribers;
2457
+ this.subscriptions = subscriptions;
2458
+ }
2459
+ /** The account of the tenant's subscriber; empty while it has no subscription. */
2460
+ async accountOf(tenantId) {
2461
+ const [subscription, subscriber, contracts] = await Promise.all([
2462
+ this.subscriptions.findForTenant(tenantId),
2463
+ this.subscribers.findByTenantId(tenantId),
2464
+ this.contracts.list({
2465
+ tenantId
2466
+ })
2467
+ ]);
2468
+ const charges = subscription?.id ? await this.ledger.listBySubscription(subscription.id) : [];
2469
+ return accountViewOf({
2470
+ subscriber,
2471
+ charges,
2472
+ contracts
2473
+ });
2474
+ }
2475
+ };
2476
+ SubscriberAccountService = _ts_decorate8([
2477
+ Injectable6(),
2478
+ _ts_param6(0, Inject6(SUBSCRIBER_LEDGER_REPOSITORY_TOKEN)),
2479
+ _ts_param6(1, Inject6(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
2480
+ _ts_param6(2, Inject6(SUBSCRIBER_REPOSITORY_TOKEN)),
2481
+ _ts_param6(3, Inject6(SUBSCRIPTION_USAGE_PORT_TOKEN)),
2482
+ _ts_metadata6("design:type", Function),
2483
+ _ts_metadata6("design:paramtypes", [
2484
+ typeof SubscriberLedgerRepository === "undefined" ? Object : SubscriberLedgerRepository,
2485
+ typeof SubscriptionContractRepository === "undefined" ? Object : SubscriptionContractRepository,
2486
+ typeof SubscriberRepository === "undefined" ? Object : SubscriberRepository,
2487
+ typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort
2488
+ ])
2489
+ ], SubscriberAccountService);
2490
+
2491
+ // src/billing/charges/subscriber-account.module.ts
2492
+ import { Controller as Controller2, Get as Get2, Inject as Inject7, Module as Module2, Param, UseGuards } from "@nestjs/common";
2493
+ function _ts_decorate9(decorators, target, key, desc) {
2494
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2495
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2496
+ 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;
2497
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2498
+ }
2499
+ __name(_ts_decorate9, "_ts_decorate");
2500
+ function _ts_metadata7(k, v) {
2501
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2502
+ }
2503
+ __name(_ts_metadata7, "_ts_metadata");
2504
+ function _ts_param7(paramIndex, decorator) {
2505
+ return function(target, key) {
2506
+ decorator(target, key, paramIndex);
2507
+ };
2508
+ }
2509
+ __name(_ts_param7, "_ts_param");
2510
+ function buildSubscriberAccountController(guards) {
2511
+ let GeneratedSubscriberAccountController = class GeneratedSubscriberAccountController {
2512
+ static {
2513
+ __name(this, "GeneratedSubscriberAccountController");
2514
+ }
2515
+ tenants;
2516
+ accounts;
2517
+ rlsBypass;
2518
+ constructor(tenants, accounts, rlsBypass) {
2519
+ this.tenants = tenants;
2520
+ this.accounts = accounts;
2521
+ this.rlsBypass = rlsBypass;
2522
+ }
2523
+ /**
2524
+ * The charges of the tenant's subscriber. An operator's request is
2525
+ * scoped to no tenant, so finding the tenant and reading its account
2526
+ * both run outside any row-level policy the application keeps its
2527
+ * tenants apart with.
2528
+ */
2529
+ chargesOf(slug) {
2530
+ const read = /* @__PURE__ */ __name(async () => {
2531
+ const tenant = await this.tenants.getTenantDetail(slug);
2532
+ return this.accounts.accountOf(tenant.id);
2533
+ }, "read");
2534
+ return this.rlsBypass.runWithBypass(read);
2535
+ }
2536
+ };
2537
+ _ts_decorate9([
2538
+ Get2("tenants/:slug/charges"),
2539
+ _ts_param7(0, Param("slug")),
2540
+ _ts_metadata7("design:type", Function),
2541
+ _ts_metadata7("design:paramtypes", [
2542
+ String
2543
+ ]),
2544
+ _ts_metadata7("design:returntype", typeof Promise === "undefined" ? Object : Promise)
2545
+ ], GeneratedSubscriberAccountController.prototype, "chargesOf", null);
2546
+ GeneratedSubscriberAccountController = _ts_decorate9([
2547
+ Controller2("admin"),
2548
+ AllowDuringMaintenance(),
2549
+ UseGuards(...guards),
2550
+ _ts_param7(0, Inject7(AdminResourcesService)),
2551
+ _ts_param7(1, Inject7(SubscriberAccountService)),
2552
+ _ts_param7(2, Inject7(RLS_BYPASS_PORT_TOKEN)),
2553
+ _ts_metadata7("design:type", Function),
2554
+ _ts_metadata7("design:paramtypes", [
2555
+ typeof AdminResourcesService === "undefined" ? Object : AdminResourcesService,
2556
+ typeof SubscriberAccountService === "undefined" ? Object : SubscriberAccountService,
2557
+ typeof RlsBypassPort === "undefined" ? Object : RlsBypassPort
2558
+ ])
2559
+ ], GeneratedSubscriberAccountController);
2560
+ return GeneratedSubscriberAccountController;
2561
+ }
2562
+ __name(buildSubscriberAccountController, "buildSubscriberAccountController");
2563
+ var SubscriberAccountModule = class _SubscriberAccountModule {
2564
+ static {
2565
+ __name(this, "SubscriberAccountModule");
2566
+ }
2567
+ static forRoot(options) {
2568
+ return {
2569
+ module: _SubscriberAccountModule,
2570
+ imports: options.imports,
2571
+ controllers: [
2572
+ buildSubscriberAccountController(options.guards)
2573
+ ]
2574
+ };
2575
+ }
2576
+ };
2577
+ SubscriberAccountModule = _ts_decorate9([
2578
+ Module2({})
2579
+ ], SubscriberAccountModule);
2580
+
2581
+ // src/billing/charges/subscriber-charge.service.ts
2582
+ import { Inject as Inject8, Injectable as Injectable7, Logger as Logger3, Optional as Optional6 } from "@nestjs/common";
2583
+
2584
+ // src/billing/charges/charge-derivation.ts
2585
+ var PERIOD_ORIGINS = [
2586
+ "activation",
2587
+ "renewal",
2588
+ "bundleBooking",
2589
+ "planChange"
2590
+ ];
2591
+ var MAX_CYCLES = 600;
2592
+ var CENTS = 100;
2593
+ function deriveDueCharges(input) {
2594
+ const planCharges = derivePlanCharges(input);
2595
+ const periods = [
2596
+ ...writtenPlanPeriods(input),
2597
+ ...planCharges.map(({ period }) => period)
2598
+ ];
2599
+ return [
2600
+ ...planCharges.map(({ charge }) => charge),
2601
+ ...deriveUpgradeDifferences(input, periods),
2602
+ ...deriveDiscountCharges(input, planCharges, periods),
2603
+ ...deriveBundleCharges(input, accountStart(input))
2604
+ ];
2605
+ }
2606
+ __name(deriveDueCharges, "deriveDueCharges");
2607
+ function bookingsTheContractMisses(contracts, bookings, now) {
2608
+ const inForce = contractInForce(contracts, now);
2609
+ if (!inForce) return [];
2610
+ 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));
2611
+ }
2612
+ __name(bookingsTheContractMisses, "bookingsTheContractMisses");
2613
+ function derivePlanCharges(input) {
2614
+ const { subscription } = input;
2615
+ if (subscription.status === "TRIAL") return [];
2616
+ const window = windowOf(subscription.currentPeriodStart, subscription.currentPeriodEnd);
2617
+ if (!window) return [];
2618
+ const rhythm = rhythmOf(subscription.billingCycle);
2619
+ const isPlanLine = /* @__PURE__ */ __name((item) => item.kind === "plan" && item.billingCycle === rhythm, "isPlanLine");
2620
+ const replaced = periodReplacedBy(window, input);
2621
+ if (replaced) return newPeriodAfterChange(input, window, replaced, isPlanLine);
2622
+ const priced = periodsToCharge({
2623
+ window,
2624
+ from: lastPeriodEnd(input.written, "plan", subscription.id) ?? window.start,
2625
+ cycle: subscription.billingCycle,
2626
+ anchorDay: subscription.anchorDay,
2627
+ input,
2628
+ endsAt: subscription.endsAt
2629
+ }).flatMap((period) => {
2630
+ const found = lineFor(input.contracts, period, isPlanLine);
2631
+ return found ? [
2632
+ {
2633
+ period,
2634
+ ...found
2635
+ }
2636
+ ] : [];
2637
+ });
2638
+ const firstContract = earliest2(input.contracts);
2639
+ const opensSubscription = /* @__PURE__ */ __name((period) => holds(period, subscription.startedAt) || holds(period, firstContract?.effectiveFrom ?? null), "opensSubscription");
2640
+ return priced.map(({ period, contract, line }) => {
2641
+ const charge = chargeOf(input, contract, line, {
2642
+ origin: opensSubscription(period) ? "activation" : "renewal",
2643
+ source: "plan",
2644
+ sourceRef: subscription.id,
2645
+ period,
2646
+ amountNet: line.priceNet,
2647
+ bookedAt: period.start
2648
+ });
2649
+ return {
2650
+ charge,
2651
+ period: {
2652
+ charge,
2653
+ rhythm
2654
+ }
2655
+ };
2656
+ });
2657
+ }
2658
+ __name(derivePlanCharges, "derivePlanCharges");
2659
+ function periodReplacedBy(window, input) {
2660
+ const whole = input.written.filter((charge) => isWholePlanPeriod(charge, input.written, input.subscription.id));
2661
+ return whole.filter((charge) => charge.periodStart < window.start && window.start < charge.periodEnd).sort((a, b) => a.periodStart.getTime() - b.periodStart.getTime())[0] ?? null;
2662
+ }
2663
+ __name(periodReplacedBy, "periodReplacedBy");
2664
+ function newPeriodAfterChange(input, window, replaced, isPlanLine) {
2665
+ const { subscription } = input;
2666
+ if (!isChargeable(window, input, subscription.endsAt)) return [];
2667
+ const found = lineFor(input.contracts, window, isPlanLine);
2668
+ if (!found) return [];
2669
+ const charge = chargeOf(input, found.contract, found.line, {
2670
+ origin: "planChange",
2671
+ source: "plan",
2672
+ sourceRef: subscription.id,
2673
+ period: window,
2674
+ amountNet: computeNewPeriodCharge({
2675
+ periodStart: replaced.periodStart,
2676
+ periodEnd: replaced.periodEnd,
2677
+ now: window.start,
2678
+ currentPriceNet: pricePaidBefore(window.start, replaced, input.contracts),
2679
+ targetPriceNet: found.line.priceNet
2680
+ }).prorataDeltaNet,
2681
+ bookedAt: window.start
2682
+ });
2683
+ return [
2684
+ {
2685
+ charge,
2686
+ period: {
2687
+ charge,
2688
+ rhythm: rhythmOf(subscription.billingCycle)
2689
+ }
2690
+ }
2691
+ ];
2692
+ }
2693
+ __name(newPeriodAfterChange, "newPeriodAfterChange");
2694
+ function pricePaidBefore(at, replaced, contracts) {
2695
+ const chargedUnder = lineById(contracts, replaced.contractLineItemId);
2696
+ const rhythm = chargedUnder?.billingCycle;
2697
+ const before = contractInForce(contracts, new Date(at.getTime() - 1));
2698
+ const line = before?.lineItems.find((item) => item.kind === "plan" && item.billingCycle === rhythm);
2699
+ return line?.priceNet ?? chargedUnder?.priceNet ?? replaced.amountNet;
2700
+ }
2701
+ __name(pricePaidBefore, "pricePaidBefore");
2702
+ function deriveUpgradeDifferences(input, periods) {
2703
+ const { subscription } = input;
2704
+ const charges = [];
2705
+ for (const { charge: period, rhythm } of periods) {
2706
+ const isPlanLine = /* @__PURE__ */ __name((item) => item.kind === "plan" && item.billingCycle === rhythm, "isPlanLine");
2707
+ for (const contract of input.contracts) {
2708
+ const at = contract.effectiveFrom;
2709
+ if (contract.status === "scheduled") continue;
2710
+ if (!(period.periodStart < at && at < period.periodEnd)) continue;
2711
+ if (!isChargeable({
2712
+ start: at,
2713
+ end: period.periodEnd
2714
+ }, input, subscription.endsAt)) {
2715
+ continue;
2716
+ }
2717
+ const line = contract.lineItems.find(isPlanLine);
2718
+ const before = contractInForce(input.contracts, new Date(at.getTime() - 1));
2719
+ const lineBefore = before?.lineItems.find(isPlanLine);
2720
+ if (!line || !lineBefore) continue;
2721
+ const difference = computeProration({
2722
+ periodStart: period.periodStart,
2723
+ periodEnd: period.periodEnd,
2724
+ now: at,
2725
+ currentPriceNet: lineBefore.priceNet,
2726
+ targetPriceNet: line.priceNet
2727
+ }).prorataDeltaNet;
2728
+ if (difference <= 0) continue;
2729
+ charges.push(chargeOf(input, contract, line, {
2730
+ origin: "planChange",
2731
+ source: "plan",
2732
+ sourceRef: subscription.id,
2733
+ period: {
2734
+ start: at,
2735
+ end: period.periodEnd
2736
+ },
2737
+ amountNet: difference,
2738
+ bookedAt: at
2739
+ }));
2740
+ }
2741
+ }
2742
+ return charges;
2743
+ }
2744
+ __name(deriveUpgradeDifferences, "deriveUpgradeDifferences");
2745
+ function writtenPlanPeriods(input) {
2746
+ return input.written.flatMap((charge) => {
2747
+ if (!isWholePlanPeriod(charge, input.written, input.subscription.id)) return [];
2748
+ const line = lineById(input.contracts, charge.contractLineItemId);
2749
+ return line ? [
2750
+ {
2751
+ charge,
2752
+ rhythm: line.billingCycle
2753
+ }
2754
+ ] : [];
2755
+ });
2756
+ }
2757
+ __name(writtenPlanPeriods, "writtenPlanPeriods");
2758
+ function isWholePlanPeriod(charge, written, subscriptionId) {
2759
+ if (charge.source !== "plan" || charge.sourceRef !== subscriptionId) return false;
2760
+ if (!PERIOD_ORIGINS.includes(charge.origin)) return false;
2761
+ return charge.origin !== "planChange" || !isDifference(charge, written);
2762
+ }
2763
+ __name(isWholePlanPeriod, "isWholePlanPeriod");
2764
+ function isDifference(charge, written) {
2765
+ return written.some((other) => other.source === "plan" && other.sourceRef === charge.sourceRef && other.periodStart < charge.periodStart && sameInstant(other.periodEnd, charge.periodEnd));
2766
+ }
2767
+ __name(isDifference, "isDifference");
2768
+ function accountStart(input) {
2769
+ const { subscription } = input;
2770
+ const firstCharged = firstPlanPeriodStart(input.written, subscription.id);
2771
+ if (firstCharged) return firstCharged;
2772
+ if (subscription.status === "TRIAL") return null;
2773
+ return windowOf(subscription.currentPeriodStart, subscription.currentPeriodEnd)?.start ?? null;
2774
+ }
2775
+ __name(accountStart, "accountStart");
2776
+ function deriveBundleCharges(input, accountBegins) {
2777
+ if (!accountBegins) return [];
2778
+ const { subscription } = input;
2779
+ const anchorDay = subscription.anchorDay;
2780
+ const charges = [];
2781
+ for (const booking of input.bookings) {
2782
+ const window = windowOf(booking.currentPeriodStart, booking.currentPeriodEnd);
2783
+ if (!window) continue;
2784
+ const cycle = booking.billingCycle ?? subscription.billingCycle;
2785
+ const endsAt = earlierOf(booking.canceledEffectiveAt, subscription.endsAt);
2786
+ const lastEnd = lastPeriodEnd(input.written, "bundle", booking.id);
2787
+ const chainStart = laterOf(booking.startedAt, accountBegins);
2788
+ const periods = lastEnd ? periodsToCharge({
2789
+ window,
2790
+ from: lastEnd,
2791
+ cycle,
2792
+ anchorDay,
2793
+ input,
2794
+ endsAt
2795
+ }) : [
2796
+ ...firstBundlePeriod(window, chainStart, cycle, anchorDay),
2797
+ ...periodsToCharge({
2798
+ window,
2799
+ from: firstBoundaryAfter(window, chainStart, cycle, anchorDay),
2800
+ cycle,
2801
+ anchorDay,
2802
+ input,
2803
+ endsAt
2804
+ })
2805
+ ].filter((period) => isChargeable(period, input, endsAt));
2806
+ for (const period of periods) {
2807
+ const priced = lineFor(input.contracts, period, (item) => item.kind === "bundle" && item.sourceVersionId === booking.bundleVersionId);
2808
+ if (!priced) continue;
2809
+ const { contract, line } = priced;
2810
+ charges.push(chargeOf(input, contract, line, {
2811
+ origin: !lastEnd && sameInstant(period.start, chainStart) ? "bundleBooking" : "renewal",
2812
+ source: "bundle",
2813
+ sourceRef: booking.id,
2814
+ period,
2815
+ // Every period is charged for its share of the whole cycle
2816
+ // it ends, which is all of it except for the short first
2817
+ // one — the same arithmetic the preview quoted
2818
+ // (`SC-BUN-003`, `SC-PRIC-002`).
2819
+ amountNet: computeProration({
2820
+ periodStart: bundleFirstPeriodStart(period.end, cycle, anchorDay),
2821
+ periodEnd: period.end,
2822
+ now: period.start,
2823
+ currentPriceNet: 0,
2824
+ targetPriceNet: line.priceNet
2825
+ }).prorataDeltaNet,
2826
+ bookedAt: period.start
2827
+ }));
2828
+ }
2829
+ }
2830
+ return charges;
2831
+ }
2832
+ __name(deriveBundleCharges, "deriveBundleCharges");
2833
+ function deriveDiscountCharges(input, due, periods) {
2834
+ const { subscription } = input;
2835
+ const charges = [];
2836
+ for (const { contract, line } of discountsWhereAgreed(input.contracts)) {
2837
+ const rhythm = line.billingCycle;
2838
+ const cycle = rhythm === "yearly" ? "YEARLY" : "MONTHLY";
2839
+ const first = firstPeriodConcludedFor(contract, periods);
2840
+ if (!first) continue;
2841
+ const anchorDay = rhythm === rhythmOf(subscription.billingCycle) ? subscription.anchorDay : first.getUTCDate();
2842
+ const beganInItsRhythm = periods.some((period) => period.rhythm === rhythm && sameInstant(period.charge.periodStart, first));
2843
+ const endedAt = beganInItsRhythm ? firstPeriodStart(periods.filter((period) => period.rhythm !== rhythm && period.charge.periodStart > first)) : first;
2844
+ for (const { charge: planCharge, period } of due) {
2845
+ const amount = endedAt && planCharge.periodStart > endedAt ? 0 : period.rhythm === rhythm ? discountInPeriod(line, first, planCharge.periodStart, cycle, anchorDay) : sameInstant(planCharge.periodStart, endedAt) ? (
2846
+ // period, and no more than it costs.
2847
+ Math.min(discountLeftAt(line, first, planCharge.periodStart, cycle, anchorDay), planCharge.amountNet)
2848
+ ) : 0;
2849
+ if (amount <= 0) continue;
2850
+ charges.push(chargeOf(input, contract, line, {
2851
+ origin: planCharge.origin,
2852
+ source: "discount",
2853
+ sourceRef: line.sourceKey,
2854
+ period: {
2855
+ start: planCharge.periodStart,
2856
+ end: planCharge.periodEnd
2857
+ },
2858
+ amountNet: -amount,
2859
+ bookedAt: planCharge.bookedAt
2860
+ }));
2861
+ }
2862
+ }
2863
+ return charges;
2864
+ }
2865
+ __name(deriveDiscountCharges, "deriveDiscountCharges");
2866
+ function discountInPeriod(line, first, periodStart, cycle, anchorDay) {
2867
+ const index = cyclesBetween(first, periodStart, cycle, anchorDay);
2868
+ return index === null ? 0 : discountFor(line, index, first, periodStart, anchorDay);
2869
+ }
2870
+ __name(discountInPeriod, "discountInPeriod");
2871
+ function discountLeftAt(line, first, at, cycle, anchorDay) {
2872
+ let cents = 0;
2873
+ let periodStart = first;
2874
+ for (let index = 0; index < MAX_CYCLES; index++) {
2875
+ const amount = discountFor(line, index, first, periodStart, anchorDay);
2876
+ if (amount === 0) break;
2877
+ if (periodStart >= at) cents += toCents(amount);
2878
+ periodStart = advanceOneCycle(periodStart, cycle, anchorDay ?? void 0);
2879
+ }
2880
+ return cents / CENTS;
2881
+ }
2882
+ __name(discountLeftAt, "discountLeftAt");
2883
+ function discountsWhereAgreed(contracts) {
2884
+ const agreed = /* @__PURE__ */ new Map();
2885
+ for (const contract of [
2886
+ ...contracts
2887
+ ].sort(byEffectiveFrom)) {
2888
+ for (const line of contract.lineItems) {
2889
+ if (line.kind !== "discount" || agreed.has(line.sourceKey)) continue;
2890
+ agreed.set(line.sourceKey, {
2891
+ contract,
2892
+ line
2893
+ });
2894
+ }
2895
+ }
2896
+ return [
2897
+ ...agreed.values()
2898
+ ];
2899
+ }
2900
+ __name(discountsWhereAgreed, "discountsWhereAgreed");
2901
+ function discountFor(line, index, first, periodStart, anchorDay) {
2902
+ const snapshots = discountSnapshotsOf(line);
2903
+ if (!snapshots) {
2904
+ return index === 0 ? -line.priceNet : 0;
2905
+ }
2906
+ const withinMonths = /* @__PURE__ */ __name((months) => periodStart < monthsAfter(first, months, anchorDay), "withinMonths");
2907
+ let cents = 0;
2908
+ for (const promotion of snapshots.promotions) {
2909
+ const months = promotionMonths(promotion.value);
2910
+ const applies = promotion.type === "intro" || promotion.type === "freeMonths" ? months !== null && withinMonths(months) : index === 0;
2911
+ if (applies) cents += toCents(promotion.resolvedAmountNet);
2912
+ }
2913
+ const code = snapshots.promoCode;
2914
+ if (code) {
2915
+ const applies = code.durationType === "MONTHS" ? withinMonths(code.durationValue ?? 0) : code.durationType === "BILLING_CYCLES" ? index < (code.durationValue ?? 0) : index === 0;
2916
+ if (applies) cents += toCents(code.resolvedAmountNet);
2917
+ }
2918
+ return cents / CENTS;
2919
+ }
2920
+ __name(discountFor, "discountFor");
2921
+ function discountSnapshotsOf(line) {
2922
+ const metadata = line.metadata;
2923
+ if (!isRecord(metadata) || metadata.generated !== true) return null;
2924
+ const promotions = Array.isArray(metadata.promotionSnapshots) ? metadata.promotionSnapshots.filter(isRecord).map((snapshot) => ({
2925
+ type: typeof snapshot.type === "string" ? snapshot.type : "",
2926
+ value: snapshot.value,
2927
+ resolvedAmountNet: numberOr0(snapshot.resolvedAmountNet)
2928
+ })) : [];
2929
+ const code = isRecord(metadata.promoCodeSnapshot) ? metadata.promoCodeSnapshot : null;
2930
+ return {
2931
+ promotions,
2932
+ promoCode: code ? {
2933
+ durationType: typeof code.durationType === "string" ? code.durationType : null,
2934
+ durationValue: typeof code.durationValue === "number" ? code.durationValue : null,
2935
+ resolvedAmountNet: numberOr0(code.resolvedAmountNet)
2936
+ } : null
2937
+ };
2938
+ }
2939
+ __name(discountSnapshotsOf, "discountSnapshotsOf");
2940
+ function promotionMonths(value) {
2941
+ if (typeof value === "number") return value;
2942
+ if (isRecord(value) && typeof value.months === "number") return value.months;
2943
+ return null;
2944
+ }
2945
+ __name(promotionMonths, "promotionMonths");
2946
+ function periodsToCharge(args) {
2947
+ const { window, from, cycle, anchorDay, input, endsAt } = args;
2948
+ const periods = [];
2949
+ let start = from;
2950
+ for (let step = 0; step < MAX_CYCLES && start < window.start; step++) {
2951
+ const end = advanceOneCycle(start, cycle, anchorDay ?? void 0);
2952
+ if (end > window.start) break;
2953
+ periods.push({
2954
+ start,
2955
+ end
2956
+ });
2957
+ start = end;
2958
+ }
2959
+ if (from <= window.start) periods.push(window);
2960
+ return periods.filter((period) => isChargeable(period, input, endsAt));
2961
+ }
2962
+ __name(periodsToCharge, "periodsToCharge");
2963
+ function isChargeable(period, input, endsAt) {
2964
+ return period.start < period.end && period.start <= input.now && (!endsAt || period.start < endsAt);
2965
+ }
2966
+ __name(isChargeable, "isChargeable");
2967
+ function firstBoundaryAfter(window, start, cycle, anchorDay) {
2968
+ if (window.start <= start) return window.end;
2969
+ let boundary = window.start;
2970
+ for (let step = 0; step < MAX_CYCLES; step++) {
2971
+ const before = retreatOneCycle(boundary, cycle, anchorDay ?? void 0);
2972
+ if (before <= start) break;
2973
+ boundary = before;
2974
+ }
2975
+ return boundary;
2976
+ }
2977
+ __name(firstBoundaryAfter, "firstBoundaryAfter");
2978
+ function firstBundlePeriod(window, start, cycle, anchorDay) {
2979
+ return [
2980
+ {
2981
+ start,
2982
+ end: firstBoundaryAfter(window, start, cycle, anchorDay)
2983
+ }
2984
+ ];
2985
+ }
2986
+ __name(firstBundlePeriod, "firstBundlePeriod");
2987
+ function lastPeriodEnd(written, source, sourceRef) {
2988
+ let last = null;
2989
+ for (const charge of written) {
2990
+ if (charge.source !== source || charge.sourceRef !== sourceRef) continue;
2991
+ if (!PERIOD_ORIGINS.includes(charge.origin)) continue;
2992
+ if (!last || charge.periodEnd > last) last = charge.periodEnd;
2993
+ }
2994
+ return last;
2995
+ }
2996
+ __name(lastPeriodEnd, "lastPeriodEnd");
2997
+ function firstPlanPeriodStart(written, subscriptionId) {
2998
+ let first = null;
2999
+ for (const charge of written) {
3000
+ if (charge.source !== "plan" || charge.sourceRef !== subscriptionId) continue;
3001
+ if (!PERIOD_ORIGINS.includes(charge.origin)) continue;
3002
+ if (!first || charge.periodStart < first) first = charge.periodStart;
3003
+ }
3004
+ return first;
3005
+ }
3006
+ __name(firstPlanPeriodStart, "firstPlanPeriodStart");
3007
+ function firstPeriodConcludedFor(concluded, periods) {
3008
+ const at = concluded.effectiveFrom;
3009
+ let first = null;
3010
+ for (const { charge } of periods) {
3011
+ if (charge.periodEnd <= at) continue;
3012
+ if (charge.periodStart < at && charge.contractId !== concluded.id) continue;
3013
+ if (!first || charge.periodStart < first) first = charge.periodStart;
3014
+ }
3015
+ return first;
3016
+ }
3017
+ __name(firstPeriodConcludedFor, "firstPeriodConcludedFor");
3018
+ function cyclesBetween(from, to, cycle, anchorDay) {
3019
+ let at = from;
3020
+ for (let step = 0; step < MAX_CYCLES; step++) {
3021
+ if (at.getTime() === to.getTime()) return step;
3022
+ if (at > to) return null;
3023
+ at = advanceOneCycle(at, cycle, anchorDay ?? void 0);
3024
+ }
3025
+ return null;
3026
+ }
3027
+ __name(cyclesBetween, "cyclesBetween");
3028
+ function monthsAfter(from, months, anchorDay) {
3029
+ let at = from;
3030
+ for (let step = 0; step < months; step++) {
3031
+ at = advanceOneCycle(at, "MONTHLY", anchorDay ?? from.getUTCDate());
3032
+ }
3033
+ return at;
3034
+ }
3035
+ __name(monthsAfter, "monthsAfter");
3036
+ function lineFor(contracts, period, matches) {
3037
+ const inForce = contractInForce(contracts, period.start);
3038
+ const inForceLine = inForce?.lineItems.find(matches);
3039
+ if (inForce && inForceLine) return {
3040
+ contract: inForce,
3041
+ line: inForceLine
3042
+ };
3043
+ for (const contract of [
3044
+ ...contracts
3045
+ ].sort(byEffectiveFrom)) {
3046
+ if (contract.status === "scheduled") continue;
3047
+ if (contract.effectiveFrom < period.start || contract.effectiveFrom >= period.end) continue;
3048
+ const line = contract.lineItems.find(matches);
3049
+ if (line) return {
3050
+ contract,
3051
+ line
3052
+ };
3053
+ }
3054
+ return null;
3055
+ }
3056
+ __name(lineFor, "lineFor");
3057
+ function contractInForce(contracts, at) {
3058
+ return [
3059
+ ...contracts
3060
+ ].filter((contract) => contract.status !== "scheduled" && contract.effectiveFrom <= at && (contract.effectiveUntil === null || at < contract.effectiveUntil)).sort(byEffectiveFrom).at(-1) ?? null;
3061
+ }
3062
+ __name(contractInForce, "contractInForce");
3063
+ function earliest2(contracts) {
3064
+ return [
3065
+ ...contracts
3066
+ ].sort(byEffectiveFrom)[0] ?? null;
3067
+ }
3068
+ __name(earliest2, "earliest");
3069
+ function byEffectiveFrom(a, b) {
3070
+ return a.effectiveFrom.getTime() - b.effectiveFrom.getTime();
3071
+ }
3072
+ __name(byEffectiveFrom, "byEffectiveFrom");
3073
+ function chargeOf(input, contract, line, charge) {
3074
+ return {
3075
+ subscriberId: input.subscriberId,
3076
+ tenantId: input.subscription.tenantId,
3077
+ subscriptionId: input.subscription.id,
3078
+ contractId: contract.id,
3079
+ contractLineItemId: line.id,
3080
+ origin: charge.origin,
3081
+ source: charge.source,
3082
+ sourceRef: charge.sourceRef,
3083
+ periodStart: charge.period.start,
3084
+ periodEnd: charge.period.end,
3085
+ currency: line.currency,
3086
+ // Rounded once, here, where it is derived (`SC-PRIC-018`).
3087
+ amountNet: toCents(charge.amountNet) / CENTS,
3088
+ bookedAt: charge.bookedAt
3089
+ };
3090
+ }
3091
+ __name(chargeOf, "chargeOf");
3092
+ function windowOf(start, end) {
3093
+ return start && end && start < end ? {
3094
+ start,
3095
+ end
3096
+ } : null;
3097
+ }
3098
+ __name(windowOf, "windowOf");
3099
+ function laterOf(a, b) {
3100
+ return b > a ? b : a;
3101
+ }
3102
+ __name(laterOf, "laterOf");
3103
+ function sameInstant(a, b) {
3104
+ return b !== null && a.getTime() === b.getTime();
3105
+ }
3106
+ __name(sameInstant, "sameInstant");
3107
+ function firstPeriodStart(periods) {
3108
+ return periods.reduce((first, { charge }) => !first || charge.periodStart < first ? charge.periodStart : first, null);
3109
+ }
3110
+ __name(firstPeriodStart, "firstPeriodStart");
3111
+ function holds(period, at) {
3112
+ return at !== null && period.start <= at && at < period.end;
3113
+ }
3114
+ __name(holds, "holds");
3115
+ function earlierOf(a, b) {
3116
+ if (!a) return b;
3117
+ if (!b) return a;
3118
+ return a < b ? a : b;
3119
+ }
3120
+ __name(earlierOf, "earlierOf");
3121
+ function rhythmOf(cycle) {
3122
+ return cycle === "YEARLY" ? "yearly" : "monthly";
3123
+ }
3124
+ __name(rhythmOf, "rhythmOf");
3125
+ function lineById(contracts, id) {
3126
+ for (const contract of contracts) {
3127
+ const line = contract.lineItems.find((item) => item.id === id);
3128
+ if (line) return line;
3129
+ }
3130
+ return null;
3131
+ }
3132
+ __name(lineById, "lineById");
3133
+ function toCents(amount) {
3134
+ return Math.round(amount * CENTS);
3135
+ }
3136
+ __name(toCents, "toCents");
3137
+ function numberOr0(value) {
3138
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
3139
+ }
3140
+ __name(numberOr0, "numberOr0");
3141
+ function isRecord(value) {
3142
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3143
+ }
3144
+ __name(isRecord, "isRecord");
3145
+
3146
+ // src/billing/charges/subscriber-charge.service.ts
3147
+ function _ts_decorate10(decorators, target, key, desc) {
1515
3148
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1516
3149
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1517
3150
  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;
1518
3151
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1519
3152
  }
1520
- __name(_ts_decorate6, "_ts_decorate");
1521
- function _ts_metadata4(k, v) {
3153
+ __name(_ts_decorate10, "_ts_decorate");
3154
+ function _ts_metadata8(k, v) {
1522
3155
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1523
3156
  }
1524
- __name(_ts_metadata4, "_ts_metadata");
1525
- function _ts_param4(paramIndex, decorator) {
3157
+ __name(_ts_metadata8, "_ts_metadata");
3158
+ function _ts_param8(paramIndex, decorator) {
1526
3159
  return function(target, key) {
1527
3160
  decorator(target, key, paramIndex);
1528
3161
  };
1529
3162
  }
1530
- __name(_ts_param4, "_ts_param");
1531
- var SubscriptionContractFreezeService = class {
3163
+ __name(_ts_param8, "_ts_param");
3164
+ function planCycleOf(subscription) {
3165
+ return subscription.billingCycle;
3166
+ }
3167
+ __name(planCycleOf, "planCycleOf");
3168
+ function endOf(subscription) {
3169
+ return subscription.canceledEffectiveAt ?? subscription.canceledAt ?? null;
3170
+ }
3171
+ __name(endOf, "endOf");
3172
+ var SubscriberChargeService = class _SubscriberChargeService {
1532
3173
  static {
1533
- __name(this, "SubscriptionContractFreezeService");
3174
+ __name(this, "SubscriberChargeService");
1534
3175
  }
1535
- catalogs;
1536
- entitlements;
3176
+ ledger;
1537
3177
  contracts;
1538
- source;
1539
- constructor(catalogs, entitlements, contracts, source, writes = null) {
1540
- this.catalogs = catalogs;
1541
- this.entitlements = entitlements;
3178
+ subscribers;
3179
+ subscriptions;
3180
+ bookings;
3181
+ freeze;
3182
+ logger = new Logger3(_SubscriberChargeService.name);
3183
+ constructor(ledger, contracts, subscribers, subscriptions, bookings = null, freeze = null) {
3184
+ this.ledger = ledger;
1542
3185
  this.contracts = contracts;
1543
- this.source = source;
1544
- if (writes?.bindsPlanVersion === false) {
1545
- 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
- }
1547
- }
1548
- assertPartyFor(tenantId) {
1549
- return this.contracts.assertPartyFor(tenantId);
3186
+ this.subscribers = subscribers;
3187
+ this.subscriptions = subscriptions;
3188
+ this.bookings = bookings;
3189
+ this.freeze = freeze;
1550
3190
  }
1551
- async endOnCancellation(tenantId, effectiveAt) {
1552
- const active = await this.contracts.findActiveByTenantId(tenantId, effectiveAt);
1553
- if (!active) return;
1554
- await this.contracts.terminate(active.id, {
1555
- effectiveUntil: effectiveAt,
1556
- // Only when it is already over. An ordinary cancellation lands at
1557
- // the term end, months out, and the customer is under this contract
1558
- // until then — the invoice side has to keep finding it, and the
1559
- // window in `findActiveByTenantId` is what stops it afterwards.
1560
- status: effectiveAt <= /* @__PURE__ */ new Date() ? "terminated" : null
3191
+ /**
3192
+ * Writes every charge the tenant's records give rise to by `now` that is
3193
+ * not written yet, and returns those.
3194
+ *
3195
+ * Nothing is charged without a contract (a charge points at a contract
3196
+ * line), in a trial, before the plan's current window when the account is
3197
+ * still empty, for a period that starts after `now`, or for one that
3198
+ * starts on or after the date a cancellation takes effect.
3199
+ *
3200
+ * A window that moved on before anything charged it is not charged
3201
+ * afterwards, so a job that renews periods calls this before it moves a
3202
+ * window, and moves it only when the call succeeded.
3203
+ */
3204
+ async recordDueCharges(tenantId, now = /* @__PURE__ */ new Date()) {
3205
+ const subscription = await this.subscriptions.findForTenant(tenantId);
3206
+ if (!subscription?.id) return [];
3207
+ const subscriber = await this.subscribers.findByTenantId(tenantId);
3208
+ if (!subscriber) return [];
3209
+ const [bookings, written] = await Promise.all([
3210
+ this.bookings?.listBySubscription(subscription.id) ?? [],
3211
+ this.ledger.listBySubscription(subscription.id)
3212
+ ]);
3213
+ const contracts = await this.contractsNamingEveryBooking(tenantId, subscription, bookings, now);
3214
+ const due = deriveDueCharges({
3215
+ now,
3216
+ subscriberId: subscriber.id,
3217
+ subscription: {
3218
+ id: subscription.id,
3219
+ tenantId,
3220
+ status: subscription.status,
3221
+ billingCycle: planCycleOf(subscription),
3222
+ currentPeriodStart: subscription.currentPeriodStart,
3223
+ currentPeriodEnd: subscription.currentPeriodEnd,
3224
+ anchorDay: resolvePlanAnchorDay(subscription),
3225
+ startedAt: subscription.startedAt,
3226
+ endsAt: endOf(subscription)
3227
+ },
3228
+ contracts,
3229
+ bookings,
3230
+ written
1561
3231
  });
1562
- this.entitlements.invalidateTenant(tenantId);
3232
+ return this.ledger.recordCharges(due);
1563
3233
  }
1564
- async freezeOnPlanChange(tenantId, newPlan, billingCycle, effectiveFrom, endsAt = null) {
1565
- const cycle = billingCycle === "YEARLY" ? "yearly" : "monthly";
1566
- const catalog = await this.catalogs.current();
1567
- const vatRate = catalog.vatRate;
1568
- assertTaxRatePercent("catalog.vatRate", vatRate);
1569
- assertContractWindow(effectiveFrom, endsAt);
1570
- const bound = await this.source.findBoundPlanVersion(tenantId);
1571
- if (!bound || bound.planId !== newPlan) {
1572
- throw new Error(`The contract for tenant ${tenantId} was asked to record plan '${newPlan}', but the subscription is bound to ${bound ? `'${bound.planId}' (version ${bound.id})` : "no plan version"}. Bind the plan version on the subscription before the contract is frozen.`);
3234
+ /**
3235
+ * The tenant's contracts, written once more where the one in force misses
3236
+ * a running booking.
3237
+ *
3238
+ * A booking writes its contract after it is saved, and a failure there does
3239
+ * not undo the booking. The booking would then stay uncharged for good,
3240
+ * since a charge points at a contract line — so the journal writes the
3241
+ * contract the way the booking would have. A failure here is logged and the
3242
+ * rest is charged: the next call tries again.
3243
+ */
3244
+ async contractsNamingEveryBooking(tenantId, subscription, bookings, now) {
3245
+ const contracts = await this.contracts.list({
3246
+ tenantId
3247
+ });
3248
+ const missed = bookingsTheContractMisses(contracts, bookings, now);
3249
+ if (missed.length === 0 || !this.freeze || subscription.status === "TRIAL" || cancellationHasLanded(subscription, now)) {
3250
+ return contracts;
1573
3251
  }
1574
- const stem = findPlan(catalog, newPlan);
1575
- const planDef = planDefFromVersion({
1576
- id: newPlan,
1577
- name: stem?.name ?? newPlan,
1578
- tagline: stem?.tagline
1579
- }, bound);
1580
- if (isPlanNotSoldInCycle(planDef, billingCycle)) {
1581
- throw new UnprocessableEntityException(planNotSoldInCycle(planDef, billingCycle));
3252
+ try {
3253
+ await this.freeze.freezeOnPlanChange(tenantId, subscription.planVersion.planId, planCycleOf(subscription), now, endOf(subscription));
3254
+ } catch (err) {
3255
+ this.logger.error(`Writing the contract for ${missed.length} booking(s) it misses failed (tenant ${tenantId}): ${String(err)}`);
3256
+ return contracts;
1582
3257
  }
1583
- await this.contracts.assertPartyFor(tenantId);
1584
- const bundles = await this.source.loadBookedBundles(tenantId, cycle);
1585
- const planPriceNet = listPriceNet(planDef, billingCycle) ?? 0;
1586
- const planLineItem = {
1587
- kind: "plan",
1588
- sourceKey: newPlan,
1589
- sourceVersionId: bound.id,
1590
- titleSnapshot: planDef.name ?? newPlan,
1591
- descriptionSnapshot: planDef.tagline ?? null,
1592
- quantity: 1,
1593
- unit: null,
1594
- priceNet: planPriceNet,
1595
- billingCycle: cycle,
1596
- minimumTermUntil: null,
1597
- featuresSnapshot: planDef.features,
1598
- quotaEffectsSnapshot: planDef.quotas,
1599
- metadata: null
1600
- };
1601
- const lineItems = recordContractLinesMoney([
1602
- planLineItem,
1603
- ...bundles.lineItems
1604
- ], {
1605
- currency: catalog.currency,
1606
- taxRate: vatRate
1607
- });
1608
- const totals = contractTotalsOf(lineItems, cycle);
1609
- const priceSnapshot = {
1610
- currency: catalog.currency,
1611
- billingCycle: cycle,
1612
- subtotalNet: totals.subtotalNet,
1613
- discountNet: totals.discountNet,
1614
- totalNet: totals.totalNet,
1615
- vatRate,
1616
- totalGross: totals.totalGross
1617
- };
1618
- assertOnePlanLine(lineItems);
1619
- assertNoNegativeDiscount({
1620
- priceSnapshot
3258
+ return this.contracts.list({
3259
+ tenantId
1621
3260
  });
1622
- const previous = await this.contracts.findActiveByTenantId(tenantId, effectiveFrom);
1623
- if (previous) {
1624
- await this.contracts.terminate(previous.id, {
1625
- effectiveUntil: effectiveFrom,
1626
- status: "superseded"
1627
- });
1628
- }
1629
- this.entitlements.invalidateTenant(tenantId);
1630
- const limits = await this.entitlements.computeLimits(tenantId, effectiveFrom, catalog);
1631
- const data = {
1632
- tenantId,
1633
- status: "active",
1634
- effectiveFrom,
1635
- // The successor inherits the ending. A cancellation capped the
1636
- // contract that was running when it was declared; every contract
1637
- // after it ends on the same date, or the repair lasts exactly until
1638
- // the next plan change.
1639
- effectiveUntil: endsAt,
1640
- originalPlanVersionId: bound.id,
1641
- originalBundleVersionIds: bundles.bundleVersionIds,
1642
- entitlementSnapshot: {
1643
- plan: limits.plan,
1644
- quotas: {
1645
- ...limits.quotas
1646
- },
1647
- features: [
1648
- ...limits.features
1649
- ]
1650
- },
1651
- priceSnapshot,
1652
- lineItems
1653
- };
1654
- await this.contracts.create(data);
1655
- this.entitlements.invalidateTenant(tenantId);
1656
3261
  }
1657
3262
  };
1658
- SubscriptionContractFreezeService = _ts_decorate6([
1659
- Injectable4(),
1660
- _ts_param4(0, Inject4(PLAN_CATALOG_SOURCE_TOKEN)),
1661
- _ts_param4(1, Inject4(ENTITLEMENT_SERVICE_TOKEN)),
1662
- _ts_param4(2, Inject4(SubscriptionContractService)),
1663
- _ts_param4(3, Inject4(CONTRACT_FREEZE_SOURCE_PORT_TOKEN)),
1664
- _ts_param4(4, Optional4()),
1665
- _ts_param4(4, Inject4(SUBSCRIPTION_WRITE_PORT_TOKEN)),
1666
- _ts_metadata4("design:type", Function),
1667
- _ts_metadata4("design:paramtypes", [
1668
- typeof PlanCatalogSource === "undefined" ? Object : PlanCatalogSource,
1669
- typeof EntitlementService === "undefined" ? Object : EntitlementService,
1670
- typeof SubscriptionContractService === "undefined" ? Object : SubscriptionContractService,
1671
- typeof ContractFreezeSourcePort === "undefined" ? Object : ContractFreezeSourcePort,
3263
+ SubscriberChargeService = _ts_decorate10([
3264
+ Injectable7(),
3265
+ _ts_param8(0, Inject8(SUBSCRIBER_LEDGER_REPOSITORY_TOKEN)),
3266
+ _ts_param8(1, Inject8(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
3267
+ _ts_param8(2, Inject8(SUBSCRIBER_REPOSITORY_TOKEN)),
3268
+ _ts_param8(3, Inject8(SUBSCRIPTION_USAGE_PORT_TOKEN)),
3269
+ _ts_param8(4, Optional6()),
3270
+ _ts_param8(4, Inject8(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
3271
+ _ts_param8(5, Optional6()),
3272
+ _ts_param8(5, Inject8(CONTRACT_FREEZE_PORT_TOKEN)),
3273
+ _ts_metadata8("design:type", Function),
3274
+ _ts_metadata8("design:paramtypes", [
3275
+ typeof SubscriberLedgerRepository === "undefined" ? Object : SubscriberLedgerRepository,
3276
+ typeof SubscriptionContractRepository === "undefined" ? Object : SubscriptionContractRepository,
3277
+ typeof SubscriberRepository === "undefined" ? Object : SubscriberRepository,
3278
+ typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort,
3279
+ Object,
1672
3280
  Object
1673
3281
  ])
1674
- ], SubscriptionContractFreezeService);
3282
+ ], SubscriberChargeService);
1675
3283
 
1676
3284
  // src/billing/dto/tenant-billing.dto.ts
1677
3285
  import { IsISO8601, IsOptional, IsString, Matches } from "class-validator";
1678
- function _ts_decorate7(decorators, target, key, desc) {
3286
+ function _ts_decorate11(decorators, target, key, desc) {
1679
3287
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1680
3288
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1681
3289
  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;
1682
3290
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1683
3291
  }
1684
- __name(_ts_decorate7, "_ts_decorate");
1685
- function _ts_metadata5(k, v) {
3292
+ __name(_ts_decorate11, "_ts_decorate");
3293
+ function _ts_metadata9(k, v) {
1686
3294
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1687
3295
  }
1688
- __name(_ts_metadata5, "_ts_metadata");
3296
+ __name(_ts_metadata9, "_ts_metadata");
1689
3297
  var PLAN_OR_CYCLE_PATTERN = /^[A-Z][A-Z0-9_]*$/;
1690
3298
  var PreviewPlanChangeDto = class {
1691
3299
  static {
@@ -1694,19 +3302,19 @@ var PreviewPlanChangeDto = class {
1694
3302
  plan;
1695
3303
  billingCycle;
1696
3304
  };
1697
- _ts_decorate7([
3305
+ _ts_decorate11([
1698
3306
  IsString(),
1699
3307
  Matches(PLAN_OR_CYCLE_PATTERN, {
1700
3308
  message: "plan must be SCREAMING_SNAKE_CASE"
1701
3309
  }),
1702
- _ts_metadata5("design:type", String)
3310
+ _ts_metadata9("design:type", String)
1703
3311
  ], PreviewPlanChangeDto.prototype, "plan", void 0);
1704
- _ts_decorate7([
3312
+ _ts_decorate11([
1705
3313
  IsString(),
1706
3314
  Matches(PLAN_OR_CYCLE_PATTERN, {
1707
3315
  message: "billingCycle must be SCREAMING_SNAKE_CASE (e.g. MONTHLY, YEARLY)"
1708
3316
  }),
1709
- _ts_metadata5("design:type", String)
3317
+ _ts_metadata9("design:type", String)
1710
3318
  ], PreviewPlanChangeDto.prototype, "billingCycle", void 0);
1711
3319
  var ChangePlanDto = class extends PreviewPlanChangeDto {
1712
3320
  static {
@@ -1731,25 +3339,25 @@ var CancelSubscriptionDto = class {
1731
3339
  */
1732
3340
  expectedEffectiveAt;
1733
3341
  };
1734
- _ts_decorate7([
3342
+ _ts_decorate11([
1735
3343
  IsOptional(),
1736
3344
  IsISO8601(),
1737
- _ts_metadata5("design:type", String)
3345
+ _ts_metadata9("design:type", String)
1738
3346
  ], CancelSubscriptionDto.prototype, "expectedEffectiveAt", void 0);
1739
3347
 
1740
3348
  // src/billing/dto/onboarding-subscription.dto.ts
1741
3349
  import { ArrayMaxSize, IsArray, IsOptional as IsOptional2, IsString as IsString2, IsUUID, Matches as Matches2 } from "class-validator";
1742
- function _ts_decorate8(decorators, target, key, desc) {
3350
+ function _ts_decorate12(decorators, target, key, desc) {
1743
3351
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1744
3352
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1745
3353
  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
3354
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1747
3355
  }
1748
- __name(_ts_decorate8, "_ts_decorate");
1749
- function _ts_metadata6(k, v) {
3356
+ __name(_ts_decorate12, "_ts_decorate");
3357
+ function _ts_metadata10(k, v) {
1750
3358
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1751
3359
  }
1752
- __name(_ts_metadata6, "_ts_metadata");
3360
+ __name(_ts_metadata10, "_ts_metadata");
1753
3361
  var PLAN_OR_CYCLE_PATTERN2 = /^[A-Z][A-Z0-9_]*$/;
1754
3362
  var PROMO_CODE_PATTERN = /^[A-Z0-9_-]{4,32}$/i;
1755
3363
  var CompleteOnboardingSubscriptionDto = class {
@@ -1769,52 +3377,52 @@ var CompleteOnboardingSubscriptionDto = class {
1769
3377
  */
1770
3378
  bundleVersionIds;
1771
3379
  };
1772
- _ts_decorate8([
3380
+ _ts_decorate12([
1773
3381
  IsString2(),
1774
3382
  Matches2(PLAN_OR_CYCLE_PATTERN2, {
1775
3383
  message: "plan must be SCREAMING_SNAKE_CASE"
1776
3384
  }),
1777
- _ts_metadata6("design:type", String)
3385
+ _ts_metadata10("design:type", String)
1778
3386
  ], CompleteOnboardingSubscriptionDto.prototype, "plan", void 0);
1779
- _ts_decorate8([
3387
+ _ts_decorate12([
1780
3388
  IsString2(),
1781
3389
  Matches2(PLAN_OR_CYCLE_PATTERN2, {
1782
3390
  message: "billingCycle must be SCREAMING_SNAKE_CASE (e.g. MONTHLY, YEARLY)"
1783
3391
  }),
1784
- _ts_metadata6("design:type", String)
3392
+ _ts_metadata10("design:type", String)
1785
3393
  ], CompleteOnboardingSubscriptionDto.prototype, "billingCycle", void 0);
1786
- _ts_decorate8([
3394
+ _ts_decorate12([
1787
3395
  IsOptional2(),
1788
3396
  IsString2(),
1789
3397
  Matches2(PROMO_CODE_PATTERN, {
1790
3398
  message: 'promoCode must consist of A\u2013Z, 0\u20139, "-", "_" (4\u201332 characters)'
1791
3399
  }),
1792
- _ts_metadata6("design:type", String)
3400
+ _ts_metadata10("design:type", String)
1793
3401
  ], CompleteOnboardingSubscriptionDto.prototype, "promoCode", void 0);
1794
- _ts_decorate8([
3402
+ _ts_decorate12([
1795
3403
  IsOptional2(),
1796
3404
  IsArray(),
1797
3405
  ArrayMaxSize(10),
1798
3406
  IsUUID("all", {
1799
3407
  each: true
1800
3408
  }),
1801
- _ts_metadata6("design:type", Array)
3409
+ _ts_metadata10("design:type", Array)
1802
3410
  ], CompleteOnboardingSubscriptionDto.prototype, "bundleVersionIds", void 0);
1803
3411
 
1804
3412
  // src/billing/dto/subscription-bundles.dto.ts
1805
3413
  import { ArrayMaxSize as ArrayMaxSize2, IsArray as IsArray2, IsIn, IsInt, IsOptional as IsOptional3, IsUUID as IsUUID2, Matches as Matches3, Max, Min } from "class-validator";
1806
3414
  import { BUNDLE_PRICE_LOOKUP_LIMIT } from "@saasicat/core";
1807
- function _ts_decorate9(decorators, target, key, desc) {
3415
+ function _ts_decorate13(decorators, target, key, desc) {
1808
3416
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1809
3417
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1810
3418
  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
3419
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1812
3420
  }
1813
- __name(_ts_decorate9, "_ts_decorate");
1814
- function _ts_metadata7(k, v) {
3421
+ __name(_ts_decorate13, "_ts_decorate");
3422
+ function _ts_metadata11(k, v) {
1815
3423
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1816
3424
  }
1817
- __name(_ts_metadata7, "_ts_metadata");
3425
+ __name(_ts_metadata11, "_ts_metadata");
1818
3426
  var ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}(T.*)?$/;
1819
3427
  var AddSubscriptionBundleDto = class {
1820
3428
  static {
@@ -1839,24 +3447,24 @@ var AddSubscriptionBundleDto = class {
1839
3447
  */
1840
3448
  billingCycle;
1841
3449
  };
1842
- _ts_decorate9([
3450
+ _ts_decorate13([
1843
3451
  IsUUID2(),
1844
- _ts_metadata7("design:type", String)
3452
+ _ts_metadata11("design:type", String)
1845
3453
  ], AddSubscriptionBundleDto.prototype, "bundleVersionId", void 0);
1846
- _ts_decorate9([
3454
+ _ts_decorate13([
1847
3455
  IsOptional3(),
1848
3456
  IsInt(),
1849
3457
  Min(0),
1850
3458
  Max(120),
1851
- _ts_metadata7("design:type", Number)
3459
+ _ts_metadata11("design:type", Number)
1852
3460
  ], AddSubscriptionBundleDto.prototype, "minimumTermMonths", void 0);
1853
- _ts_decorate9([
3461
+ _ts_decorate13([
1854
3462
  IsOptional3(),
1855
3463
  IsIn([
1856
3464
  "MONTHLY",
1857
3465
  "YEARLY"
1858
3466
  ]),
1859
- _ts_metadata7("design:type", String)
3467
+ _ts_metadata11("design:type", String)
1860
3468
  ], AddSubscriptionBundleDto.prototype, "billingCycle", void 0);
1861
3469
  var PreviewSubscriptionBundleDto = class {
1862
3470
  static {
@@ -1880,30 +3488,30 @@ var PreviewSubscriptionBundleDto = class {
1880
3488
  */
1881
3489
  billingCycle;
1882
3490
  };
1883
- _ts_decorate9([
3491
+ _ts_decorate13([
1884
3492
  IsOptional3(),
1885
3493
  IsUUID2(),
1886
- _ts_metadata7("design:type", String)
3494
+ _ts_metadata11("design:type", String)
1887
3495
  ], PreviewSubscriptionBundleDto.prototype, "bundleVersionId", void 0);
1888
- _ts_decorate9([
3496
+ _ts_decorate13([
1889
3497
  IsOptional3(),
1890
3498
  IsUUID2(),
1891
- _ts_metadata7("design:type", String)
3499
+ _ts_metadata11("design:type", String)
1892
3500
  ], PreviewSubscriptionBundleDto.prototype, "subscriptionBundleId", void 0);
1893
- _ts_decorate9([
3501
+ _ts_decorate13([
1894
3502
  IsOptional3(),
1895
3503
  IsInt(),
1896
3504
  Min(0),
1897
3505
  Max(120),
1898
- _ts_metadata7("design:type", Number)
3506
+ _ts_metadata11("design:type", Number)
1899
3507
  ], PreviewSubscriptionBundleDto.prototype, "minimumTermMonths", void 0);
1900
- _ts_decorate9([
3508
+ _ts_decorate13([
1901
3509
  IsOptional3(),
1902
3510
  IsIn([
1903
3511
  "MONTHLY",
1904
3512
  "YEARLY"
1905
3513
  ]),
1906
- _ts_metadata7("design:type", String)
3514
+ _ts_metadata11("design:type", String)
1907
3515
  ], PreviewSubscriptionBundleDto.prototype, "billingCycle", void 0);
1908
3516
  var CancelSubscriptionBundleDto = class {
1909
3517
  static {
@@ -1916,12 +3524,12 @@ var CancelSubscriptionBundleDto = class {
1916
3524
  */
1917
3525
  canceledAt;
1918
3526
  };
1919
- _ts_decorate9([
3527
+ _ts_decorate13([
1920
3528
  IsOptional3(),
1921
3529
  Matches3(ISO_DATE_PATTERN, {
1922
3530
  message: "canceledAt must be an ISO date (YYYY-MM-DD)"
1923
3531
  }),
1924
- _ts_metadata7("design:type", String)
3532
+ _ts_metadata11("design:type", String)
1925
3533
  ], CancelSubscriptionBundleDto.prototype, "canceledAt", void 0);
1926
3534
  var BundlePriceLookupDto = class {
1927
3535
  static {
@@ -1933,35 +3541,35 @@ var BundlePriceLookupDto = class {
1933
3541
  */
1934
3542
  bundleVersionIds;
1935
3543
  };
1936
- _ts_decorate9([
3544
+ _ts_decorate13([
1937
3545
  IsArray2(),
1938
3546
  ArrayMaxSize2(BUNDLE_PRICE_LOOKUP_LIMIT),
1939
3547
  IsUUID2("4", {
1940
3548
  each: true
1941
3549
  }),
1942
- _ts_metadata7("design:type", Array)
3550
+ _ts_metadata11("design:type", Array)
1943
3551
  ], BundlePriceLookupDto.prototype, "bundleVersionIds", void 0);
1944
3552
 
1945
3553
  // 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";
3554
+ import { Inject as Inject9, Injectable as Injectable8, NotFoundException as NotFoundException2, Optional as Optional7, UnprocessableEntityException as UnprocessableEntityException2 } from "@nestjs/common";
1947
3555
  import { BILLING_ERROR_CODES as BILLING_ERROR_CODES2, CATALOG_ERROR_CODES } from "@saasicat/core";
1948
- function _ts_decorate10(decorators, target, key, desc) {
3556
+ function _ts_decorate14(decorators, target, key, desc) {
1949
3557
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1950
3558
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1951
3559
  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
3560
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1953
3561
  }
1954
- __name(_ts_decorate10, "_ts_decorate");
1955
- function _ts_metadata8(k, v) {
3562
+ __name(_ts_decorate14, "_ts_decorate");
3563
+ function _ts_metadata12(k, v) {
1956
3564
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1957
3565
  }
1958
- __name(_ts_metadata8, "_ts_metadata");
1959
- function _ts_param5(paramIndex, decorator) {
3566
+ __name(_ts_metadata12, "_ts_metadata");
3567
+ function _ts_param9(paramIndex, decorator) {
1960
3568
  return function(target, key) {
1961
3569
  decorator(target, key, paramIndex);
1962
3570
  };
1963
3571
  }
1964
- __name(_ts_param5, "_ts_param");
3572
+ __name(_ts_param9, "_ts_param");
1965
3573
  var SubscriptionBundlesService = class {
1966
3574
  static {
1967
3575
  __name(this, "SubscriptionBundlesService");
@@ -2202,16 +3810,16 @@ var SubscriptionBundlesService = class {
2202
3810
  return this.repo.reactivate(subscriptionBundleId);
2203
3811
  }
2204
3812
  };
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", [
3813
+ SubscriptionBundlesService = _ts_decorate14([
3814
+ Injectable8(),
3815
+ _ts_param9(0, Inject9(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
3816
+ _ts_param9(1, Inject9(BUNDLE_REPOSITORY_TOKEN)),
3817
+ _ts_param9(2, Optional7()),
3818
+ _ts_param9(2, Inject9(SUBSCRIPTION_BUNDLE_CONFIG_TOKEN)),
3819
+ _ts_param9(3, Optional7()),
3820
+ _ts_param9(3, Inject9(SELF_SERVICE_BLOCKED_BUNDLES_TOKEN)),
3821
+ _ts_metadata12("design:type", Function),
3822
+ _ts_metadata12("design:paramtypes", [
2215
3823
  typeof SubscriptionBundleRepository === "undefined" ? Object : SubscriptionBundleRepository,
2216
3824
  typeof BundleRepository === "undefined" ? Object : BundleRepository,
2217
3825
  typeof SubscriptionBundleConfig === "undefined" ? Object : SubscriptionBundleConfig,
@@ -2243,25 +3851,38 @@ function clampToParent(ownTermEndsAt, parentEndsAt) {
2243
3851
  __name(clampToParent, "clampToParent");
2244
3852
 
2245
3853
  // 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";
3854
+ import { BadRequestException, Body, Controller as Controller3, ForbiddenException as ForbiddenException2, Get as Get3, HttpCode, HttpStatus, Inject as Inject10, Logger as Logger4, ConflictException, NotFoundException as NotFoundException3, Optional as Optional8, Post, Req, UseGuards as UseGuards2 } from "@nestjs/common";
2247
3855
  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) {
3856
+
3857
+ // src/billing/charges/record-charges-after.ts
3858
+ async function recordChargesAfter(charges, tenantId, change, logger) {
3859
+ if (!charges) return;
3860
+ try {
3861
+ await charges.recordDueCharges(tenantId);
3862
+ } catch (err) {
3863
+ logger.error(`Recording charges after ${change} failed (tenant ${tenantId}): ${String(err)}`);
3864
+ }
3865
+ }
3866
+ __name(recordChargesAfter, "recordChargesAfter");
3867
+
3868
+ // src/billing/tenant-billing.controller.ts
3869
+ function _ts_decorate15(decorators, target, key, desc) {
2249
3870
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2250
3871
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2251
3872
  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
3873
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2253
3874
  }
2254
- __name(_ts_decorate11, "_ts_decorate");
2255
- function _ts_metadata9(k, v) {
3875
+ __name(_ts_decorate15, "_ts_decorate");
3876
+ function _ts_metadata13(k, v) {
2256
3877
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2257
3878
  }
2258
- __name(_ts_metadata9, "_ts_metadata");
2259
- function _ts_param6(paramIndex, decorator) {
3879
+ __name(_ts_metadata13, "_ts_metadata");
3880
+ function _ts_param10(paramIndex, decorator) {
2260
3881
  return function(target, key) {
2261
3882
  decorator(target, key, paramIndex);
2262
3883
  };
2263
3884
  }
2264
- __name(_ts_param6, "_ts_param");
3885
+ __name(_ts_param10, "_ts_param");
2265
3886
  var TenantBillingController = class _TenantBillingController {
2266
3887
  static {
2267
3888
  __name(this, "TenantBillingController");
@@ -2282,7 +3903,8 @@ var TenantBillingController = class _TenantBillingController {
2282
3903
  contractFreeze;
2283
3904
  trialProjection;
2284
3905
  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) {
3906
+ charges;
3907
+ 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
3908
  this.entitlements = entitlements;
2287
3909
  this.planPreview = planPreview;
2288
3910
  this.subscriptionUsage = subscriptionUsage;
@@ -2299,8 +3921,9 @@ var TenantBillingController = class _TenantBillingController {
2299
3921
  this.contractFreeze = contractFreeze;
2300
3922
  this.trialProjection = trialProjection;
2301
3923
  this.cancellationNoticeDays = cancellationNoticeDays;
3924
+ this.charges = charges;
2302
3925
  }
2303
- logger = new Logger2(_TenantBillingController.name);
3926
+ logger = new Logger4(_TenantBillingController.name);
2304
3927
  // ---------------------------------------------------------------------
2305
3928
  // Reads (Phase B) — all authenticated tenant users
2306
3929
  // ---------------------------------------------------------------------
@@ -2456,7 +4079,8 @@ var TenantBillingController = class _TenantBillingController {
2456
4079
  };
2457
4080
  if (decision.isImmediate) {
2458
4081
  const wasTrial = sub.status === "TRIAL";
2459
- const period = wasTrial || cancellationOutstanding ? null : initialPeriodWindow(/* @__PURE__ */ new Date(), dto.billingCycle);
4082
+ const opensPeriod = decision.proration?.basis === "newPeriod";
4083
+ const period = wasTrial || cancellationOutstanding || !opensPeriod ? null : initialPeriodWindow(/* @__PURE__ */ new Date(), dto.billingCycle);
2460
4084
  const trialEndsAt = wasTrial && this.trialProjection ? await this.trialProjection.projectTrialEndsAt({
2461
4085
  currentPlan: sub.plan,
2462
4086
  targetPlan: dto.plan,
@@ -2486,6 +4110,7 @@ var TenantBillingController = class _TenantBillingController {
2486
4110
  // contract it freezes still ends when the subscription does.
2487
4111
  sub.canceledEffectiveAt ?? sub.canceledAt ?? null
2488
4112
  );
4113
+ await recordChargesAfter(this.charges, tenantId, "a plan change", this.logger);
2489
4114
  await this.auditLog(req, userId, "Subscription", tenantId, "CHANGE_PLAN", {
2490
4115
  fromPlan: sub.plan,
2491
4116
  fromCycle: sub.billingCycle,
@@ -2658,7 +4283,6 @@ var TenantBillingController = class _TenantBillingController {
2658
4283
  }
2659
4284
  }
2660
4285
  }
2661
- await this.tryFreezeOnPlanChange(tenantId, dto.plan, dto.billingCycle, wasTrial);
2662
4286
  this.entitlements.invalidateTenant(tenantId);
2663
4287
  await this.auditLog(req, userId, "Subscription", tenantId, "COMPLETE_ONBOARDING_SUBSCRIPTION", {
2664
4288
  plan: planResult.plan,
@@ -2698,6 +4322,8 @@ var TenantBillingController = class _TenantBillingController {
2698
4322
  } else if (bundleVersionIds.length > 0 && !this.subscriptionBundles) {
2699
4323
  warnings.push("Bundle bookings were requested during onboarding, but SubscriptionBundleModule is not registered in the consumer. No bundles were created.");
2700
4324
  }
4325
+ await this.tryFreezeOnPlanChange(tenantId, dto.plan, dto.billingCycle, wasTrial);
4326
+ await recordChargesAfter(this.charges, tenantId, "onboarding", this.logger);
2701
4327
  return {
2702
4328
  plan: planResult.plan,
2703
4329
  // The adapter returns `string`; the UI expects `'MONTHLY' | 'YEARLY'`.
@@ -2976,115 +4602,117 @@ var TenantBillingController = class _TenantBillingController {
2976
4602
  }
2977
4603
  }
2978
4604
  };
2979
- _ts_decorate11([
2980
- Get2("entitlement"),
2981
- _ts_param6(0, Req()),
2982
- _ts_metadata9("design:type", Function),
2983
- _ts_metadata9("design:paramtypes", [
4605
+ _ts_decorate15([
4606
+ Get3("entitlement"),
4607
+ _ts_param10(0, Req()),
4608
+ _ts_metadata13("design:type", Function),
4609
+ _ts_metadata13("design:paramtypes", [
2984
4610
  typeof RequestLike === "undefined" ? Object : RequestLike
2985
4611
  ]),
2986
- _ts_metadata9("design:returntype", Promise)
4612
+ _ts_metadata13("design:returntype", Promise)
2987
4613
  ], 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", [
4614
+ _ts_decorate15([
4615
+ Get3("usage"),
4616
+ _ts_param10(0, Req()),
4617
+ _ts_metadata13("design:type", Function),
4618
+ _ts_metadata13("design:paramtypes", [
2993
4619
  typeof RequestLike === "undefined" ? Object : RequestLike
2994
4620
  ]),
2995
- _ts_metadata9("design:returntype", Promise)
4621
+ _ts_metadata13("design:returntype", Promise)
2996
4622
  ], TenantBillingController.prototype, "getUsage", null);
2997
- _ts_decorate11([
4623
+ _ts_decorate15([
2998
4624
  Post("plan/preview"),
2999
4625
  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", [
4626
+ UseGuards2(TenantAdminGuard),
4627
+ _ts_param10(0, Req()),
4628
+ _ts_param10(1, Body()),
4629
+ _ts_metadata13("design:type", Function),
4630
+ _ts_metadata13("design:paramtypes", [
3005
4631
  typeof RequestLike === "undefined" ? Object : RequestLike,
3006
4632
  typeof PreviewPlanChangeDto === "undefined" ? Object : PreviewPlanChangeDto
3007
4633
  ]),
3008
- _ts_metadata9("design:returntype", Promise)
4634
+ _ts_metadata13("design:returntype", Promise)
3009
4635
  ], TenantBillingController.prototype, "previewPlanChange", null);
3010
- _ts_decorate11([
4636
+ _ts_decorate15([
3011
4637
  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", [
4638
+ UseGuards2(TenantAdminGuard),
4639
+ _ts_param10(0, Req()),
4640
+ _ts_param10(1, Body()),
4641
+ _ts_metadata13("design:type", Function),
4642
+ _ts_metadata13("design:paramtypes", [
3017
4643
  typeof RequestLike === "undefined" ? Object : RequestLike,
3018
4644
  typeof ChangePlanDto === "undefined" ? Object : ChangePlanDto
3019
4645
  ]),
3020
- _ts_metadata9("design:returntype", Promise)
4646
+ _ts_metadata13("design:returntype", Promise)
3021
4647
  ], TenantBillingController.prototype, "changePlan", null);
3022
- _ts_decorate11([
4648
+ _ts_decorate15([
3023
4649
  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", [
4650
+ UseGuards2(TenantAdminGuard),
4651
+ _ts_param10(0, Req()),
4652
+ _ts_param10(1, Body()),
4653
+ _ts_metadata13("design:type", Function),
4654
+ _ts_metadata13("design:paramtypes", [
3029
4655
  typeof RequestLike === "undefined" ? Object : RequestLike,
3030
4656
  typeof CompleteOnboardingSubscriptionDto === "undefined" ? Object : CompleteOnboardingSubscriptionDto
3031
4657
  ]),
3032
- _ts_metadata9("design:returntype", Promise)
4658
+ _ts_metadata13("design:returntype", Promise)
3033
4659
  ], TenantBillingController.prototype, "completeOnboardingSubscription", null);
3034
- _ts_decorate11([
4660
+ _ts_decorate15([
3035
4661
  Post("subscription/accept-pending-version"),
3036
- UseGuards(TenantAdminGuard),
3037
- _ts_param6(0, Req()),
3038
- _ts_metadata9("design:type", Function),
3039
- _ts_metadata9("design:paramtypes", [
4662
+ UseGuards2(TenantAdminGuard),
4663
+ _ts_param10(0, Req()),
4664
+ _ts_metadata13("design:type", Function),
4665
+ _ts_metadata13("design:paramtypes", [
3040
4666
  typeof RequestLike === "undefined" ? Object : RequestLike
3041
4667
  ]),
3042
- _ts_metadata9("design:returntype", Promise)
4668
+ _ts_metadata13("design:returntype", Promise)
3043
4669
  ], TenantBillingController.prototype, "acceptPendingPlanVersion", null);
3044
- _ts_decorate11([
4670
+ _ts_decorate15([
3045
4671
  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", [
4672
+ UseGuards2(TenantAdminGuard),
4673
+ _ts_param10(0, Req()),
4674
+ _ts_param10(1, Body()),
4675
+ _ts_metadata13("design:type", Function),
4676
+ _ts_metadata13("design:paramtypes", [
3051
4677
  typeof RequestLike === "undefined" ? Object : RequestLike,
3052
4678
  typeof CancelSubscriptionDto === "undefined" ? Object : CancelSubscriptionDto
3053
4679
  ]),
3054
- _ts_metadata9("design:returntype", Promise)
4680
+ _ts_metadata13("design:returntype", Promise)
3055
4681
  ], 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", [
4682
+ TenantBillingController = _ts_decorate15([
4683
+ Controller3("billing"),
4684
+ UseGuards2(ComposedTenantAuthGuard),
4685
+ _ts_param10(0, Inject10(ENTITLEMENT_SERVICE_TOKEN)),
4686
+ _ts_param10(1, Inject10(PlanChangePreviewService)),
4687
+ _ts_param10(2, Inject10(SUBSCRIPTION_USAGE_PORT_TOKEN)),
4688
+ _ts_param10(3, Inject10(USAGE_SNAPSHOT_PORT_TOKEN)),
4689
+ _ts_param10(4, Inject10(SUBSCRIPTION_WRITE_PORT_TOKEN)),
4690
+ _ts_param10(5, Optional8()),
4691
+ _ts_param10(5, Inject10(TENANT_ID_RESOLVER_TOKEN)),
4692
+ _ts_param10(6, Optional8()),
4693
+ _ts_param10(6, Inject10(USER_ID_RESOLVER_TOKEN)),
4694
+ _ts_param10(7, Optional8()),
4695
+ _ts_param10(7, Inject10(SELF_SERVICE_BLOCKED_PLANS_TOKEN)),
4696
+ _ts_param10(8, Optional8()),
4697
+ _ts_param10(8, Inject10(PromoCodesService)),
4698
+ _ts_param10(9, Optional8()),
4699
+ _ts_param10(9, Inject10(AdminAuditService)),
4700
+ _ts_param10(10, Optional8()),
4701
+ _ts_param10(10, Inject10(USER_EMAIL_RESOLVER_TOKEN)),
4702
+ _ts_param10(11, Optional8()),
4703
+ _ts_param10(11, Inject10(AUDIT_CONTEXT_RESOLVER_TOKEN)),
4704
+ _ts_param10(12, Optional8()),
4705
+ _ts_param10(12, Inject10(SubscriptionBundlesService)),
4706
+ _ts_param10(13, Optional8()),
4707
+ _ts_param10(13, Inject10(CONTRACT_FREEZE_PORT_TOKEN)),
4708
+ _ts_param10(14, Optional8()),
4709
+ _ts_param10(14, Inject10(TRIAL_PROJECTION_PORT_TOKEN)),
4710
+ _ts_param10(15, Optional8()),
4711
+ _ts_param10(15, Inject10(CANCELLATION_NOTICE_DAYS_TOKEN)),
4712
+ _ts_param10(16, Optional8()),
4713
+ _ts_param10(16, Inject10(SubscriberChargeService)),
4714
+ _ts_metadata13("design:type", Function),
4715
+ _ts_metadata13("design:paramtypes", [
3088
4716
  typeof EntitlementService === "undefined" ? Object : EntitlementService,
3089
4717
  typeof PlanChangePreviewService === "undefined" ? Object : PlanChangePreviewService,
3090
4718
  typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort,
@@ -3100,20 +4728,21 @@ TenantBillingController = _ts_decorate11([
3100
4728
  Object,
3101
4729
  Object,
3102
4730
  Object,
3103
- typeof CancellationNoticePeriods === "undefined" ? Object : CancellationNoticePeriods
4731
+ typeof CancellationNoticePeriods === "undefined" ? Object : CancellationNoticePeriods,
4732
+ Object
3104
4733
  ])
3105
4734
  ], TenantBillingController);
3106
4735
 
3107
4736
  // src/billing/tenant-billing.module.ts
3108
- import { Module as Module2 } from "@nestjs/common";
4737
+ import { Module as Module3 } from "@nestjs/common";
3109
4738
  import { planCatalogSchema as planCatalogSchema2 } from "@saasicat/spec";
3110
- function _ts_decorate12(decorators, target, key, desc) {
4739
+ function _ts_decorate16(decorators, target, key, desc) {
3111
4740
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3112
4741
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3113
4742
  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
4743
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3115
4744
  }
3116
- __name(_ts_decorate12, "_ts_decorate");
4745
+ __name(_ts_decorate16, "_ts_decorate");
3117
4746
  function settingFromCatalog(catalog, key) {
3118
4747
  const settings = catalog.tenantBilling;
3119
4748
  const value = settings?.[key];
@@ -3178,10 +4807,20 @@ var TenantBillingModule = class _TenantBillingModule {
3178
4807
  if (!options.contractFreeze.subscriberRepository) {
3179
4808
  throw new Error("TenantBillingModule: `contractFreeze` needs `subscriberRepository` \u2014 a frozen contract names the subscriber it is concluded with, and a plan change for a tenant without one is refused before anything changes.");
3180
4809
  }
3181
- providers.push(asProvider(CONTRACT_FREEZE_SOURCE_PORT_TOKEN, options.contractFreeze.sourcePort), asProvider(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN, options.contractFreeze.subscriptionContractRepository), ...subscriberProviders(options.contractFreeze.subscriberRepository), SubscriptionContractService, {
4810
+ providers.push(asProvider(CONTRACT_FREEZE_SOURCE_PORT_TOKEN, options.contractFreeze.sourcePort), asProvider(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN, options.contractFreeze.subscriptionContractRepository), ...subscriberProviders(options.contractFreeze.subscriberRepository), SubscriptionContractService, SubscriptionContractFreezeService, {
3182
4811
  provide: CONTRACT_FREEZE_PORT_TOKEN,
3183
- useClass: SubscriptionContractFreezeService
3184
- });
4812
+ useExisting: SubscriptionContractFreezeService
4813
+ }, ContractRefreshService);
4814
+ if (options.contractFreeze.transactionRunner) {
4815
+ providers.push(asProvider(CONTRACT_TRANSACTION_RUNNER_TOKEN, options.contractFreeze.transactionRunner));
4816
+ }
4817
+ }
4818
+ const hasChargeJournal = Boolean(options.chargeJournal);
4819
+ if (options.chargeJournal) {
4820
+ if (!options.contractFreeze) {
4821
+ 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.");
4822
+ }
4823
+ providers.push(asProvider(SUBSCRIBER_LEDGER_REPOSITORY_TOKEN, options.chargeJournal.ledgerRepository), SubscriberChargeService, SubscriberAccountService);
3185
4824
  }
3186
4825
  if (options.tenantIdResolver) {
3187
4826
  providers.push({
@@ -3227,37 +4866,42 @@ var TenantBillingModule = class _TenantBillingModule {
3227
4866
  PendingPlanMaterializationService
3228
4867
  ] : [],
3229
4868
  ...hasContractFreeze ? [
3230
- CONTRACT_FREEZE_PORT_TOKEN
4869
+ CONTRACT_FREEZE_PORT_TOKEN,
4870
+ ContractRefreshService
4871
+ ] : [],
4872
+ ...hasChargeJournal ? [
4873
+ SubscriberChargeService,
4874
+ SubscriberAccountService
3231
4875
  ] : [],
3232
4876
  ...options.extraExports ?? []
3233
4877
  ]
3234
4878
  };
3235
4879
  }
3236
4880
  };
3237
- TenantBillingModule = _ts_decorate12([
3238
- Module2({})
4881
+ TenantBillingModule = _ts_decorate16([
4882
+ Module3({})
3239
4883
  ], TenantBillingModule);
3240
4884
 
3241
4885
  // src/billing/subscription-bundle-preview.service.ts
3242
- import { Inject as Inject7, Injectable as Injectable6, NotFoundException as NotFoundException4, Optional as Optional7 } from "@nestjs/common";
4886
+ import { Inject as Inject11, Injectable as Injectable9, NotFoundException as NotFoundException4, Optional as Optional9 } from "@nestjs/common";
3243
4887
  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) {
4888
+ function _ts_decorate17(decorators, target, key, desc) {
3245
4889
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3246
4890
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3247
4891
  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
4892
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3249
4893
  }
3250
- __name(_ts_decorate13, "_ts_decorate");
3251
- function _ts_metadata10(k, v) {
4894
+ __name(_ts_decorate17, "_ts_decorate");
4895
+ function _ts_metadata14(k, v) {
3252
4896
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
3253
4897
  }
3254
- __name(_ts_metadata10, "_ts_metadata");
3255
- function _ts_param7(paramIndex, decorator) {
4898
+ __name(_ts_metadata14, "_ts_metadata");
4899
+ function _ts_param11(paramIndex, decorator) {
3256
4900
  return function(target, key) {
3257
4901
  decorator(target, key, paramIndex);
3258
4902
  };
3259
4903
  }
3260
- __name(_ts_param7, "_ts_param");
4904
+ __name(_ts_param11, "_ts_param");
3261
4905
  var SubscriptionBundlePreviewService = class {
3262
4906
  static {
3263
4907
  __name(this, "SubscriptionBundlePreviewService");
@@ -3527,20 +5171,20 @@ var SubscriptionBundlePreviewService = class {
3527
5171
  return collectUnsatisfiedRequires2(bundleVersion.features ?? [], requiresIndex).filter((key) => !covered.has(key));
3528
5172
  }
3529
5173
  };
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", [
5174
+ SubscriptionBundlePreviewService = _ts_decorate17([
5175
+ Injectable9(),
5176
+ _ts_param11(0, Inject11(SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN)),
5177
+ _ts_param11(1, Inject11(BUNDLE_REPOSITORY_TOKEN)),
5178
+ _ts_param11(2, Optional9()),
5179
+ _ts_param11(2, Inject11(PLAN_REPOSITORY_TOKEN)),
5180
+ _ts_param11(3, Optional9()),
5181
+ _ts_param11(3, Inject11(CATALOG_ENTRY_REPOSITORY_TOKEN)),
5182
+ _ts_param11(4, Optional9()),
5183
+ _ts_param11(4, Inject11(SELF_SERVICE_BLOCKED_BUNDLES_TOKEN)),
5184
+ _ts_param11(5, Optional9()),
5185
+ _ts_param11(5, Inject11(SUBSCRIPTION_BUNDLE_CONFIG_TOKEN)),
5186
+ _ts_metadata14("design:type", Function),
5187
+ _ts_metadata14("design:paramtypes", [
3544
5188
  typeof SubscriptionBundleRepository === "undefined" ? Object : SubscriptionBundleRepository,
3545
5189
  typeof BundleRepository === "undefined" ? Object : BundleRepository,
3546
5190
  Object,
@@ -3565,25 +5209,29 @@ function toSnapshot(bundleVersion) {
3565
5209
  __name(toSnapshot, "toSnapshot");
3566
5210
 
3567
5211
  // 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";
5212
+ import { BadRequestException as BadRequestException2, Body as Body2, Controller as Controller4, Delete, Get as Get4, HttpCode as HttpCode2, HttpStatus as HttpStatus2, Inject as Inject12, Logger as Logger5, ConflictException as ConflictException2, NotFoundException as NotFoundException5, Optional as Optional10, Param as Param2, ParseUUIDPipe, Post as Post2, Req as Req2, UseGuards as UseGuards3 } from "@nestjs/common";
3569
5213
  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) {
5214
+ function _ts_decorate18(decorators, target, key, desc) {
3571
5215
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3572
5216
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3573
5217
  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
5218
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3575
5219
  }
3576
- __name(_ts_decorate14, "_ts_decorate");
3577
- function _ts_metadata11(k, v) {
5220
+ __name(_ts_decorate18, "_ts_decorate");
5221
+ function _ts_metadata15(k, v) {
3578
5222
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
3579
5223
  }
3580
- __name(_ts_metadata11, "_ts_metadata");
3581
- function _ts_param8(paramIndex, decorator) {
5224
+ __name(_ts_metadata15, "_ts_metadata");
5225
+ function _ts_param12(paramIndex, decorator) {
3582
5226
  return function(target, key) {
3583
5227
  decorator(target, key, paramIndex);
3584
5228
  };
3585
5229
  }
3586
- __name(_ts_param8, "_ts_param");
5230
+ __name(_ts_param12, "_ts_param");
5231
+ function planCycleOf2(sub) {
5232
+ return sub.billingCycle;
5233
+ }
5234
+ __name(planCycleOf2, "planCycleOf");
3587
5235
  function buildTenantSubscriptionBundlesController(extraGuards = []) {
3588
5236
  let GeneratedTenantSubscriptionBundlesController = class GeneratedTenantSubscriptionBundlesController2 {
3589
5237
  static {
@@ -3594,19 +5242,21 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3594
5242
  subscriptionUsage;
3595
5243
  tenantIdResolver;
3596
5244
  contractFreeze;
3597
- logger = new Logger3("TenantSubscriptionBundlesController");
5245
+ charges;
5246
+ logger = new Logger5("TenantSubscriptionBundlesController");
3598
5247
  /** Keeps the "no contractFreeze hook" warning to one line per process. */
3599
5248
  static warnedAboutMissingFreeze = false;
3600
- constructor(service, previewService, subscriptionUsage, tenantIdResolver, contractFreeze = null) {
5249
+ constructor(service, previewService, subscriptionUsage, tenantIdResolver, contractFreeze = null, charges = null) {
3601
5250
  this.service = service;
3602
5251
  this.previewService = previewService;
3603
5252
  this.subscriptionUsage = subscriptionUsage;
3604
5253
  this.tenantIdResolver = tenantIdResolver;
3605
5254
  this.contractFreeze = contractFreeze;
5255
+ this.charges = charges;
3606
5256
  }
3607
5257
  async list(req) {
3608
5258
  const sub = await this.requireSubscription(this.requireTenantId(req));
3609
- return this.service.listForSubscription(this.requireSubscriptionPk(sub), sub.plan, sub.billingCycle);
5259
+ return this.service.listForSubscription(this.requireSubscriptionPk(sub), sub.plan, planCycleOf2(sub));
3610
5260
  }
3611
5261
  /**
3612
5262
  * List prices for the bundles the store is showing, resolved for this
@@ -3636,7 +5286,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3636
5286
  // A bundle cannot commit past the subscription paying for it.
3637
5287
  parentEndsAt: sub.canceledEffectiveAt ?? sub.canceledAt ?? null,
3638
5288
  // And it runs in the plan's rhythm, on the plan's day.
3639
- planCycle: sub.billingCycle,
5289
+ planCycle: planCycleOf2(sub),
3640
5290
  planPeriodEnd: sub.currentPeriodEnd ?? null,
3641
5291
  // Resolved rather than passed through: a null here would let
3642
5292
  // the arithmetic read the period END, which a short month has
@@ -3646,6 +5296,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3646
5296
  billingCycle: dto.billingCycle
3647
5297
  });
3648
5298
  await this.refreezeContract(tenantId, sub);
5299
+ await recordChargesAfter(this.charges, tenantId, "an add-on booking", this.logger);
3649
5300
  return result;
3650
5301
  }
3651
5302
  async preview(req, dto) {
@@ -3662,7 +5313,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3662
5313
  subscriptionId: this.requireSubscriptionPk(sub),
3663
5314
  // Plan KEY (compatibility.planIds is key-based) — not the planVersion UUID.
3664
5315
  currentPlanKey: sub.plan,
3665
- billingCycle: sub.billingCycle,
5316
+ billingCycle: planCycleOf2(sub),
3666
5317
  status: sub.status,
3667
5318
  startedAt: sub.startedAt,
3668
5319
  currentPeriodStart: sub.currentPeriodStart,
@@ -3772,7 +5423,7 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3772
5423
  await this.contractFreeze.freezeOnPlanChange(
3773
5424
  tenantId,
3774
5425
  sub.planVersion.planId,
3775
- sub.billingCycle,
5426
+ planCycleOf2(sub),
3776
5427
  /* @__PURE__ */ new Date(),
3777
5428
  // Cancelling a bundle stays open on a cancelled
3778
5429
  // subscription, and it re-freezes: the replacement contract
@@ -3784,93 +5435,96 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3784
5435
  }
3785
5436
  }
3786
5437
  };
3787
- _ts_decorate14([
3788
- Get3(),
3789
- _ts_param8(0, Req2()),
3790
- _ts_metadata11("design:type", Function),
3791
- _ts_metadata11("design:paramtypes", [
5438
+ _ts_decorate18([
5439
+ Get4(),
5440
+ _ts_param12(0, Req2()),
5441
+ _ts_metadata15("design:type", Function),
5442
+ _ts_metadata15("design:paramtypes", [
3792
5443
  typeof RequestLike === "undefined" ? Object : RequestLike
3793
5444
  ]),
3794
- _ts_metadata11("design:returntype", Promise)
5445
+ _ts_metadata15("design:returntype", Promise)
3795
5446
  ], GeneratedTenantSubscriptionBundlesController.prototype, "list", null);
3796
- _ts_decorate14([
5447
+ _ts_decorate18([
3797
5448
  Post2("prices"),
3798
- _ts_param8(0, Req2()),
3799
- _ts_param8(1, Body2()),
3800
- _ts_metadata11("design:type", Function),
3801
- _ts_metadata11("design:paramtypes", [
5449
+ _ts_param12(0, Req2()),
5450
+ _ts_param12(1, Body2()),
5451
+ _ts_metadata15("design:type", Function),
5452
+ _ts_metadata15("design:paramtypes", [
3802
5453
  typeof RequestLike === "undefined" ? Object : RequestLike,
3803
5454
  typeof BundlePriceLookupDto === "undefined" ? Object : BundlePriceLookupDto
3804
5455
  ]),
3805
- _ts_metadata11("design:returntype", Promise)
5456
+ _ts_metadata15("design:returntype", Promise)
3806
5457
  ], GeneratedTenantSubscriptionBundlesController.prototype, "prices", null);
3807
- _ts_decorate14([
5458
+ _ts_decorate18([
3808
5459
  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", [
5460
+ UseGuards3(TenantAdminGuard),
5461
+ _ts_param12(0, Req2()),
5462
+ _ts_param12(1, Body2()),
5463
+ _ts_metadata15("design:type", Function),
5464
+ _ts_metadata15("design:paramtypes", [
3814
5465
  typeof RequestLike === "undefined" ? Object : RequestLike,
3815
5466
  typeof AddSubscriptionBundleDto === "undefined" ? Object : AddSubscriptionBundleDto
3816
5467
  ]),
3817
- _ts_metadata11("design:returntype", Promise)
5468
+ _ts_metadata15("design:returntype", Promise)
3818
5469
  ], GeneratedTenantSubscriptionBundlesController.prototype, "add", null);
3819
- _ts_decorate14([
5470
+ _ts_decorate18([
3820
5471
  Post2("preview"),
3821
5472
  HttpCode2(HttpStatus2.OK),
3822
- _ts_param8(0, Req2()),
3823
- _ts_param8(1, Body2()),
3824
- _ts_metadata11("design:type", Function),
3825
- _ts_metadata11("design:paramtypes", [
5473
+ _ts_param12(0, Req2()),
5474
+ _ts_param12(1, Body2()),
5475
+ _ts_metadata15("design:type", Function),
5476
+ _ts_metadata15("design:paramtypes", [
3826
5477
  typeof RequestLike === "undefined" ? Object : RequestLike,
3827
5478
  typeof PreviewSubscriptionBundleDto === "undefined" ? Object : PreviewSubscriptionBundleDto
3828
5479
  ]),
3829
- _ts_metadata11("design:returntype", Promise)
5480
+ _ts_metadata15("design:returntype", Promise)
3830
5481
  ], GeneratedTenantSubscriptionBundlesController.prototype, "preview", null);
3831
- _ts_decorate14([
5482
+ _ts_decorate18([
3832
5483
  Delete(":id"),
3833
- UseGuards2(TenantAdminGuard),
5484
+ UseGuards3(TenantAdminGuard),
3834
5485
  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", [
5486
+ _ts_param12(0, Req2()),
5487
+ _ts_param12(1, Param2("id", new ParseUUIDPipe())),
5488
+ _ts_param12(2, Body2()),
5489
+ _ts_metadata15("design:type", Function),
5490
+ _ts_metadata15("design:paramtypes", [
3840
5491
  typeof RequestLike === "undefined" ? Object : RequestLike,
3841
5492
  String,
3842
5493
  typeof CancelSubscriptionBundleDto === "undefined" ? Object : CancelSubscriptionBundleDto
3843
5494
  ]),
3844
- _ts_metadata11("design:returntype", Promise)
5495
+ _ts_metadata15("design:returntype", Promise)
3845
5496
  ], GeneratedTenantSubscriptionBundlesController.prototype, "cancel", null);
3846
- _ts_decorate14([
5497
+ _ts_decorate18([
3847
5498
  Post2(":id/reactivate"),
3848
- UseGuards2(TenantAdminGuard),
5499
+ UseGuards3(TenantAdminGuard),
3849
5500
  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", [
5501
+ _ts_param12(0, Req2()),
5502
+ _ts_param12(1, Param2("id", new ParseUUIDPipe())),
5503
+ _ts_metadata15("design:type", Function),
5504
+ _ts_metadata15("design:paramtypes", [
3854
5505
  typeof RequestLike === "undefined" ? Object : RequestLike,
3855
5506
  String
3856
5507
  ]),
3857
- _ts_metadata11("design:returntype", Promise)
5508
+ _ts_metadata15("design:returntype", Promise)
3858
5509
  ], 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", [
5510
+ GeneratedTenantSubscriptionBundlesController = _ts_decorate18([
5511
+ Controller4("billing/subscription-bundles"),
5512
+ UseGuards3(ComposedTenantAuthGuard, ...extraGuards),
5513
+ _ts_param12(0, Inject12(SubscriptionBundlesService)),
5514
+ _ts_param12(1, Inject12(SubscriptionBundlePreviewService)),
5515
+ _ts_param12(2, Inject12(SUBSCRIPTION_USAGE_PORT_TOKEN)),
5516
+ _ts_param12(3, Inject12(TENANT_ID_RESOLVER_TOKEN)),
5517
+ _ts_param12(4, Optional10()),
5518
+ _ts_param12(4, Inject12(CONTRACT_FREEZE_PORT_TOKEN)),
5519
+ _ts_param12(5, Optional10()),
5520
+ _ts_param12(5, Inject12(SubscriberChargeService)),
5521
+ _ts_metadata15("design:type", Function),
5522
+ _ts_metadata15("design:paramtypes", [
3870
5523
  typeof SubscriptionBundlesService === "undefined" ? Object : SubscriptionBundlesService,
3871
5524
  typeof SubscriptionBundlePreviewService === "undefined" ? Object : SubscriptionBundlePreviewService,
3872
5525
  typeof SubscriptionUsagePort === "undefined" ? Object : SubscriptionUsagePort,
3873
5526
  typeof TenantIdResolver === "undefined" ? Object : TenantIdResolver,
5527
+ Object,
3874
5528
  Object
3875
5529
  ])
3876
5530
  ], GeneratedTenantSubscriptionBundlesController);
@@ -3879,14 +5533,14 @@ function buildTenantSubscriptionBundlesController(extraGuards = []) {
3879
5533
  __name(buildTenantSubscriptionBundlesController, "buildTenantSubscriptionBundlesController");
3880
5534
 
3881
5535
  // src/billing/subscription-bundles.module.ts
3882
- import { Module as Module3 } from "@nestjs/common";
3883
- function _ts_decorate15(decorators, target, key, desc) {
5536
+ import { Module as Module4 } from "@nestjs/common";
5537
+ function _ts_decorate19(decorators, target, key, desc) {
3884
5538
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3885
5539
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3886
5540
  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
5541
  return c > 3 && r && Object.defineProperty(target, key, r), r;
3888
5542
  }
3889
- __name(_ts_decorate15, "_ts_decorate");
5543
+ __name(_ts_decorate19, "_ts_decorate");
3890
5544
  var SubscriptionBundleModule = class _SubscriptionBundleModule {
3891
5545
  static {
3892
5546
  __name(this, "SubscriptionBundleModule");
@@ -3948,8 +5602,8 @@ var SubscriptionBundleModule = class _SubscriptionBundleModule {
3948
5602
  };
3949
5603
  }
3950
5604
  };
3951
- SubscriptionBundleModule = _ts_decorate15([
3952
- Module3({})
5605
+ SubscriptionBundleModule = _ts_decorate19([
5606
+ Module4({})
3953
5607
  ], SubscriptionBundleModule);
3954
5608
 
3955
5609
  export {
@@ -3989,11 +5643,22 @@ export {
3989
5643
  SELF_SERVICE_BLOCKED_PLANS_TOKEN,
3990
5644
  SELF_SERVICE_BLOCKED_BUNDLES_TOKEN,
3991
5645
  computeProration,
5646
+ computeNewPeriodCharge,
3992
5647
  PlanChangePreviewService,
3993
5648
  CONTRACT_FREEZE_PORT_TOKEN,
3994
5649
  CONTRACT_FREEZE_SOURCE_PORT_TOKEN,
3995
5650
  PendingPlanMaterializationService,
3996
5651
  SubscriptionContractFreezeService,
5652
+ featureChangeOf,
5653
+ quotaChangesOf,
5654
+ moneyChangesOf,
5655
+ withAgreedTerms,
5656
+ vocabularyOf,
5657
+ ContractRefreshService,
5658
+ SUBSCRIBER_LEDGER_REPOSITORY_TOKEN,
5659
+ SubscriberAccountService,
5660
+ SubscriberAccountModule,
5661
+ SubscriberChargeService,
3997
5662
  PreviewPlanChangeDto,
3998
5663
  ChangePlanDto,
3999
5664
  CancelSubscriptionDto,