@saasicat/nest 0.2.0

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 (96) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +53 -0
  3. package/dist/admin/index.cjs +1283 -0
  4. package/dist/admin/index.d.cts +225 -0
  5. package/dist/admin/index.d.ts +225 -0
  6. package/dist/admin/index.js +58 -0
  7. package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
  8. package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
  9. package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
  10. package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
  11. package/dist/aggregation-CJ3qQf92.d.cts +288 -0
  12. package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
  13. package/dist/billing/index.cjs +5205 -0
  14. package/dist/billing/index.d.cts +1326 -0
  15. package/dist/billing/index.d.ts +1326 -0
  16. package/dist/billing/index.js +197 -0
  17. package/dist/catalog/index.cjs +6449 -0
  18. package/dist/catalog/index.d.cts +1073 -0
  19. package/dist/catalog/index.d.ts +1073 -0
  20. package/dist/catalog/index.js +158 -0
  21. package/dist/checkout-offer/index.cjs +807 -0
  22. package/dist/checkout-offer/index.d.cts +120 -0
  23. package/dist/checkout-offer/index.d.ts +120 -0
  24. package/dist/checkout-offer/index.js +20 -0
  25. package/dist/chunk-2FR6ZL7R.js +0 -0
  26. package/dist/chunk-2PNX2QL2.js +347 -0
  27. package/dist/chunk-2VNZYRDV.js +326 -0
  28. package/dist/chunk-7MLGHWKO.js +12 -0
  29. package/dist/chunk-ABB2CRAS.js +31 -0
  30. package/dist/chunk-AJ3EPELS.js +157 -0
  31. package/dist/chunk-E56W4U2P.js +90 -0
  32. package/dist/chunk-HRU3CP57.js +71 -0
  33. package/dist/chunk-L4SUWH5B.js +14 -0
  34. package/dist/chunk-M6CZQYPY.js +314 -0
  35. package/dist/chunk-MDIZUVIK.js +87 -0
  36. package/dist/chunk-MV6LW5UB.js +217 -0
  37. package/dist/chunk-NYLON2VC.js +6 -0
  38. package/dist/chunk-P6MYZMXQ.js +951 -0
  39. package/dist/chunk-Q53N43LQ.js +13 -0
  40. package/dist/chunk-QJVPRD3R.js +573 -0
  41. package/dist/chunk-RIGLQ4BV.js +3367 -0
  42. package/dist/chunk-SABTXESR.js +24 -0
  43. package/dist/chunk-SHUYVCID.js +6 -0
  44. package/dist/chunk-SYEVGPFC.js +686 -0
  45. package/dist/chunk-V7HUZ6UQ.js +5923 -0
  46. package/dist/chunk-WPUVJRIN.js +29 -0
  47. package/dist/chunk-XCZ3TTFK.js +22 -0
  48. package/dist/chunk-XSGU2Y2O.js +1611 -0
  49. package/dist/chunk-YNEW3L63.js +190 -0
  50. package/dist/chunk-YXGHPILG.js +614 -0
  51. package/dist/chunk-ZL4LLE2Z.js +465 -0
  52. package/dist/di-CcNeq9v-.d.cts +16 -0
  53. package/dist/di-CcNeq9v-.d.ts +16 -0
  54. package/dist/discovery/index.cjs +588 -0
  55. package/dist/discovery/index.d.cts +299 -0
  56. package/dist/discovery/index.d.ts +299 -0
  57. package/dist/discovery/index.js +49 -0
  58. package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
  59. package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
  60. package/dist/entitlement/index.cjs +805 -0
  61. package/dist/entitlement/index.d.cts +60 -0
  62. package/dist/entitlement/index.d.ts +60 -0
  63. package/dist/entitlement/index.js +57 -0
  64. package/dist/index.cjs +15918 -0
  65. package/dist/index.d.cts +85 -0
  66. package/dist/index.d.ts +85 -0
  67. package/dist/index.js +956 -0
  68. package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
  69. package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
  70. package/dist/platform/index.cjs +2772 -0
  71. package/dist/platform/index.d.cts +118 -0
  72. package/dist/platform/index.d.ts +118 -0
  73. package/dist/platform/index.js +37 -0
  74. package/dist/promo/index.cjs +995 -0
  75. package/dist/promo/index.d.cts +121 -0
  76. package/dist/promo/index.d.ts +121 -0
  77. package/dist/promo/index.js +58 -0
  78. package/dist/registration/index.cjs +1687 -0
  79. package/dist/registration/index.d.cts +444 -0
  80. package/dist/registration/index.d.ts +444 -0
  81. package/dist/registration/index.js +82 -0
  82. package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
  83. package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
  84. package/dist/service-DX8KbGXl.d.cts +109 -0
  85. package/dist/service-DX8KbGXl.d.ts +109 -0
  86. package/dist/subscription-contract/index.cjs +471 -0
  87. package/dist/subscription-contract/index.d.cts +17 -0
  88. package/dist/subscription-contract/index.d.ts +17 -0
  89. package/dist/subscription-contract/index.js +21 -0
  90. package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
  91. package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
  92. package/dist/testing/index.cjs +3942 -0
  93. package/dist/testing/index.d.cts +279 -0
  94. package/dist/testing/index.d.ts +279 -0
  95. package/dist/testing/index.js +1195 -0
  96. package/package.json +185 -0
@@ -0,0 +1,807 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/checkout-offer/index.ts
22
+ var checkout_offer_exports = {};
23
+ __export(checkout_offer_exports, {
24
+ CHECKOUT_OFFER_REPOSITORY_TOKEN: () => CHECKOUT_OFFER_REPOSITORY_TOKEN,
25
+ CheckoutOfferModule: () => CheckoutOfferModule,
26
+ CheckoutOfferService: () => CheckoutOfferService,
27
+ CreateCheckoutOfferDto: () => CreateCheckoutOfferDto,
28
+ UpdateCheckoutOfferDto: () => UpdateCheckoutOfferDto,
29
+ buildCheckoutOfferController: () => buildCheckoutOfferController
30
+ });
31
+ module.exports = __toCommonJS(checkout_offer_exports);
32
+
33
+ // src/checkout-offer/checkout-offer.service.ts
34
+ var import_common = require("@nestjs/common");
35
+ var import_types = require("@saasicat/types");
36
+
37
+ // src/catalog/tokens.ts
38
+ var PLAN_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("PLAN_REPOSITORY");
39
+ var BUNDLE_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("BUNDLE_REPOSITORY");
40
+ var CATALOG_ENTRY_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("CATALOG_ENTRY_REPOSITORY");
41
+
42
+ // src/checkout-offer/discount-line-items.ts
43
+ function appendImplicitDiscountLineItem(input) {
44
+ const lineItems = input.lineItems.map((item) => cloneCheckoutOfferLineItem(item));
45
+ const discountNet = roundMoney(input.priceBreakdown.regularNet - input.priceBreakdown.effectiveNet);
46
+ if (lineItems.some((item) => item.kind === "discount" && !isGeneratedDiscount(item))) {
47
+ return lineItems;
48
+ }
49
+ const lineItemsWithoutGeneratedDiscount = lineItems.filter((item) => !isGeneratedDiscount(item));
50
+ if (discountNet <= 0) {
51
+ return lineItemsWithoutGeneratedDiscount;
52
+ }
53
+ return [
54
+ ...lineItemsWithoutGeneratedDiscount,
55
+ createDiscountLineItem(input, discountNet)
56
+ ];
57
+ }
58
+ __name(appendImplicitDiscountLineItem, "appendImplicitDiscountLineItem");
59
+ function cloneCheckoutOfferLineItem(item) {
60
+ return {
61
+ ...item,
62
+ featuresSnapshot: [
63
+ ...item.featuresSnapshot ?? []
64
+ ],
65
+ quotaEffectsSnapshot: {
66
+ ...item.quotaEffectsSnapshot ?? {}
67
+ },
68
+ metadata: cloneRecord(item.metadata)
69
+ };
70
+ }
71
+ __name(cloneCheckoutOfferLineItem, "cloneCheckoutOfferLineItem");
72
+ function createDiscountLineItem(input, discountNet) {
73
+ const promoCode = input.promoCodeSnapshot ?? null;
74
+ const firstPromotion = input.promotionSnapshots?.[0] ?? null;
75
+ const discountGross = roundMoney(discountNet * (1 + input.priceBreakdown.vatRate));
76
+ return {
77
+ kind: "discount",
78
+ sourceKey: promoCode?.code ?? firstPromotion?.id ?? "price-discount",
79
+ sourceVersionId: null,
80
+ titleSnapshot: promoCode?.label ?? firstPromotion?.label ?? "Rabatt",
81
+ descriptionSnapshot: promoCode ? `Promo-Code ${promoCode.code}` : null,
82
+ quantity: 1,
83
+ unit: null,
84
+ priceNet: -discountNet,
85
+ priceGross: -discountGross,
86
+ billingCycle: input.billingCycle,
87
+ featuresSnapshot: [],
88
+ quotaEffectsSnapshot: {},
89
+ metadata: {
90
+ generated: true,
91
+ source: promoCode ? "promo_code" : firstPromotion ? "promotion" : "price_breakdown",
92
+ discountNet,
93
+ discountGross,
94
+ promotionSnapshots: cloneArray(input.promotionSnapshots ?? []),
95
+ promoCodeSnapshot: cloneJsonValue(promoCode)
96
+ }
97
+ };
98
+ }
99
+ __name(createDiscountLineItem, "createDiscountLineItem");
100
+ function roundMoney(value) {
101
+ return Math.round((value + Number.EPSILON) * 100) / 100;
102
+ }
103
+ __name(roundMoney, "roundMoney");
104
+ function isGeneratedDiscount(item) {
105
+ return item.kind === "discount" && item.metadata?.generated === true;
106
+ }
107
+ __name(isGeneratedDiscount, "isGeneratedDiscount");
108
+ function cloneRecord(value) {
109
+ if (!value) return null;
110
+ return cloneJsonValue(value);
111
+ }
112
+ __name(cloneRecord, "cloneRecord");
113
+ function cloneArray(value) {
114
+ return cloneJsonValue(value);
115
+ }
116
+ __name(cloneArray, "cloneArray");
117
+ function cloneJsonValue(value) {
118
+ return value == null ? value : JSON.parse(JSON.stringify(value));
119
+ }
120
+ __name(cloneJsonValue, "cloneJsonValue");
121
+
122
+ // src/checkout-offer/tokens.ts
123
+ var CHECKOUT_OFFER_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("CHECKOUT_OFFER_REPOSITORY");
124
+
125
+ // src/checkout-offer/checkout-offer.service.ts
126
+ function _ts_decorate(decorators, target, key, desc) {
127
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
128
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
129
+ 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;
130
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
131
+ }
132
+ __name(_ts_decorate, "_ts_decorate");
133
+ function _ts_metadata(k, v) {
134
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
135
+ }
136
+ __name(_ts_metadata, "_ts_metadata");
137
+ function _ts_param(paramIndex, decorator) {
138
+ return function(target, key) {
139
+ decorator(target, key, paramIndex);
140
+ };
141
+ }
142
+ __name(_ts_param, "_ts_param");
143
+ var CheckoutOfferService = class {
144
+ static {
145
+ __name(this, "CheckoutOfferService");
146
+ }
147
+ repo;
148
+ bundles;
149
+ plans;
150
+ catalogEntries;
151
+ constructor(repo, bundles = null, plans = null, catalogEntries = null) {
152
+ this.repo = repo;
153
+ this.bundles = bundles;
154
+ this.plans = plans;
155
+ this.catalogEntries = catalogEntries;
156
+ }
157
+ list(filter) {
158
+ return this.repo.list(filter);
159
+ }
160
+ async getById(id) {
161
+ const row = await this.repo.findById(id);
162
+ if (!row) {
163
+ throw new import_common.NotFoundException(`CheckoutOffer '${id}' nicht gefunden`);
164
+ }
165
+ return row;
166
+ }
167
+ async create(data) {
168
+ const normalized = this.normalizeCreateData(data);
169
+ await this.assertFeatureRequiresSatisfied({
170
+ projectKey: normalized.projectKey,
171
+ planKey: normalized.planKey,
172
+ planVersionId: normalized.planVersionId ?? null,
173
+ bundleVersionIds: normalized.bundleVersionIds ?? [],
174
+ lineItems: normalized.lineItems ?? []
175
+ });
176
+ return this.repo.create(normalized);
177
+ }
178
+ /** Customization in onboarding — only while the offer is `open`. */
179
+ async update(id, data) {
180
+ const existing = await this.getById(id);
181
+ this.assertOpen(existing, "\xE4ndern");
182
+ const next = this.normalizeUpdateData(existing, data);
183
+ await this.assertFeatureRequiresSatisfied({
184
+ projectKey: existing.projectKey,
185
+ planKey: existing.planKey,
186
+ planVersionId: existing.planVersionId,
187
+ bundleVersionIds: next.bundleVersionIds ?? [],
188
+ lineItems: next.lineItems ?? []
189
+ });
190
+ return this.repo.update(id, next);
191
+ }
192
+ /**
193
+ * Freezes the offer (`status = 'consumed'`). Returns the final
194
+ * snapshot — the caller (registration/billing) writes it
195
+ * as `Subscription.packageSnapshot`.
196
+ */
197
+ async consume(id) {
198
+ const existing = await this.getById(id);
199
+ this.assertOpen(existing, "verbrauchen");
200
+ await this.assertBundleVersionsStillBookable(existing);
201
+ return this.repo.consume(id);
202
+ }
203
+ /**
204
+ * #35 P6 — server-side requires validation: the dependencies of all
205
+ * features (plan ∪ selected bundles) must be covered within the
206
+ * selection, otherwise an offer would be created whose features cannot
207
+ * work at the tenant (an app-specific validateModuleDependencies check is
208
+ * thereby replaced on the platform side). The requires source are the
209
+ * curated FeatureCatalogEntries; without CatalogEntryRepository it is
210
+ * skipped (graceful — no requires data available).
211
+ */
212
+ async assertFeatureRequiresSatisfied(input) {
213
+ if (!this.catalogEntries) return;
214
+ const entries = await this.catalogEntries.listFeatures({
215
+ projectKey: input.projectKey
216
+ });
217
+ const requiresIndex = (0, import_types.buildFeatureRequiresIndex)(entries);
218
+ if (requiresIndex.size === 0) return;
219
+ const selected = /* @__PURE__ */ new Set([
220
+ ...await this.resolvePlanFeatures(input),
221
+ ...await this.resolveBundleFeatures(input)
222
+ ]);
223
+ const missingRequires = (0, import_types.collectUnsatisfiedRequires)([
224
+ ...selected
225
+ ], requiresIndex);
226
+ if (missingRequires.length > 0) {
227
+ throw new import_common.UnprocessableEntityException({
228
+ code: "CHECKOUT_OFFER_FEATURE_DEPENDENCY_UNSATISFIED",
229
+ message: `Die Paket-Auswahl deckt nicht alle Feature-Abh\xE4ngigkeiten: [${missingRequires.join(", ")}] fehlen in Plan + gew\xE4hlten Bundles.`,
230
+ missingRequires
231
+ });
232
+ }
233
+ }
234
+ /**
235
+ * Plan features preferably from the server SSOT (PlanRepository); fallback
236
+ * is the frozen featuresSnapshot of the plan LineItem (adapter without
237
+ * PlanRepository wiring).
238
+ */
239
+ async resolvePlanFeatures(input) {
240
+ if (this.plans) {
241
+ const version = input.planVersionId ? await this.plans.findVersionById?.(input.planVersionId) : null;
242
+ const resolved = version ?? await this.plans.findActivePlanVersion?.(input.planKey) ?? await this.plans.findLatestLivePlanVersion?.(input.planKey);
243
+ if (resolved) return resolved.features ?? [];
244
+ }
245
+ return input.lineItems.filter((item) => item.kind === "plan").flatMap((item) => item.featuresSnapshot ?? []);
246
+ }
247
+ /** Bundle features from the BundleVersions; fallback featuresSnapshot. */
248
+ async resolveBundleFeatures(input) {
249
+ if (this.bundles) {
250
+ const features = [];
251
+ let allResolved = true;
252
+ for (const bundleVersionId of input.bundleVersionIds) {
253
+ const version = await this.bundles.findVersionById(bundleVersionId);
254
+ if (!version) {
255
+ allResolved = false;
256
+ break;
257
+ }
258
+ features.push(...version.features ?? []);
259
+ }
260
+ if (allResolved) return features;
261
+ }
262
+ return input.lineItems.filter((item) => item.kind === "bundle").flatMap((item) => item.featuresSnapshot ?? []);
263
+ }
264
+ async assertBundleVersionsStillBookable(offer) {
265
+ if (!this.bundles || !offer.bundleVersionIds || offer.bundleVersionIds.length === 0) {
266
+ return;
267
+ }
268
+ const now = Date.now();
269
+ const violations = [];
270
+ for (const bundleVersionId of offer.bundleVersionIds) {
271
+ const version = await this.bundles.findVersionById(bundleVersionId);
272
+ if (!version) {
273
+ violations.push({
274
+ bundleVersionId,
275
+ reason: "missing"
276
+ });
277
+ continue;
278
+ }
279
+ if (version.publishedAt === null) {
280
+ violations.push({
281
+ bundleVersionId,
282
+ reason: "not_published"
283
+ });
284
+ continue;
285
+ }
286
+ if (version.supersededAt !== null) {
287
+ violations.push({
288
+ bundleVersionId,
289
+ reason: "superseded"
290
+ });
291
+ continue;
292
+ }
293
+ if (this.dateIsAfterNow(version.validFrom, now)) {
294
+ violations.push({
295
+ bundleVersionId,
296
+ reason: "not_yet_valid"
297
+ });
298
+ continue;
299
+ }
300
+ if (this.isValidUntilExpired(version.validUntil, now)) {
301
+ violations.push({
302
+ bundleVersionId,
303
+ reason: "expired"
304
+ });
305
+ }
306
+ }
307
+ if (violations.length > 0) {
308
+ throw new import_common.UnprocessableEntityException({
309
+ code: "CHECKOUT_OFFER_BUNDLE_VERSION_NOT_BOOKABLE",
310
+ message: "Mindestens eine BundleVersion aus dem CheckoutOffer ist nicht mehr buchbar.",
311
+ violations
312
+ });
313
+ }
314
+ }
315
+ normalizeCreateData(data) {
316
+ const lineItems = this.resolveLineItems({
317
+ planKey: data.planKey,
318
+ billingCycle: data.billingCycle,
319
+ priceBreakdown: data.priceBreakdown,
320
+ lineItems: data.lineItems,
321
+ bundleVersionIds: data.bundleVersionIds ?? [],
322
+ promotionSnapshots: data.promotionSnapshots ?? [],
323
+ promoCodeSnapshot: data.promoCodeSnapshot ?? null
324
+ });
325
+ return {
326
+ ...data,
327
+ bundles: data.bundles ?? [],
328
+ bundleVersionIds: data.bundleVersionIds ?? [],
329
+ lineItems,
330
+ promotionSnapshots: data.promotionSnapshots ?? [],
331
+ promoCodeSnapshot: data.promoCodeSnapshot ?? null,
332
+ locale: data.locale ?? "de",
333
+ validUntil: data.validUntil ?? null
334
+ };
335
+ }
336
+ normalizeUpdateData(existing, data) {
337
+ const priceBreakdown = data.priceBreakdown ?? existing.priceBreakdown;
338
+ const billingCycle = data.billingCycle ?? existing.billingCycle;
339
+ const bundleVersionIds = data.bundleVersionIds ?? existing.bundleVersionIds ?? [];
340
+ const lineItems = this.resolveLineItems({
341
+ planKey: existing.planKey,
342
+ billingCycle,
343
+ priceBreakdown,
344
+ lineItems: data.lineItems ?? existing.lineItems,
345
+ bundleVersionIds,
346
+ promotionSnapshots: data.promotionSnapshots ?? existing.promotionSnapshots ?? [],
347
+ promoCodeSnapshot: data.promoCodeSnapshot !== void 0 ? data.promoCodeSnapshot : existing.promoCodeSnapshot ?? null
348
+ });
349
+ return {
350
+ ...data,
351
+ bundleVersionIds,
352
+ lineItems,
353
+ promotionSnapshots: data.promotionSnapshots ?? existing.promotionSnapshots ?? [],
354
+ promoCodeSnapshot: data.promoCodeSnapshot !== void 0 ? data.promoCodeSnapshot : existing.promoCodeSnapshot ?? null
355
+ };
356
+ }
357
+ resolveLineItems(input) {
358
+ const lineItems = input.lineItems && input.lineItems.length > 0 ? input.lineItems : [
359
+ this.defaultPlanLineItem(input.planKey, input.billingCycle, input.priceBreakdown)
360
+ ];
361
+ const hasPlan = lineItems.some((item) => item.kind === "plan");
362
+ if (!hasPlan) {
363
+ throw new import_common.UnprocessableEntityException({
364
+ code: "CHECKOUT_OFFER_PLAN_LINE_ITEM_REQUIRED",
365
+ message: "CheckoutOffer ben\xF6tigt eine eingefrorene Plan-LineItem."
366
+ });
367
+ }
368
+ const missingBundleVersionIds = input.bundleVersionIds.filter((versionId) => !lineItems.some((item) => item.kind === "bundle" && item.sourceVersionId === versionId));
369
+ if (missingBundleVersionIds.length > 0) {
370
+ throw new import_common.UnprocessableEntityException({
371
+ code: "CHECKOUT_OFFER_BUNDLE_LINE_ITEMS_REQUIRED",
372
+ message: "Jede ausgew\xE4hlte BundleVersion ben\xF6tigt eine eingefrorene Bundle-LineItem.",
373
+ bundleVersionIds: missingBundleVersionIds
374
+ });
375
+ }
376
+ return appendImplicitDiscountLineItem({
377
+ billingCycle: input.billingCycle,
378
+ priceBreakdown: input.priceBreakdown,
379
+ lineItems,
380
+ promotionSnapshots: input.promotionSnapshots ?? [],
381
+ promoCodeSnapshot: input.promoCodeSnapshot ?? null
382
+ });
383
+ }
384
+ defaultPlanLineItem(planKey, billingCycle, priceBreakdown) {
385
+ const priceNet = priceBreakdown.planNet;
386
+ return {
387
+ kind: "plan",
388
+ sourceKey: planKey,
389
+ sourceVersionId: null,
390
+ titleSnapshot: planKey,
391
+ descriptionSnapshot: null,
392
+ quantity: 1,
393
+ unit: null,
394
+ priceNet,
395
+ priceGross: Math.round(priceNet * (1 + priceBreakdown.vatRate) * 100) / 100,
396
+ billingCycle,
397
+ featuresSnapshot: [],
398
+ quotaEffectsSnapshot: {},
399
+ metadata: null
400
+ };
401
+ }
402
+ assertOpen(existing, action) {
403
+ if (existing.status === "consumed") {
404
+ throw new import_common.ConflictException(`CheckoutOffer '${existing.id}' ist bereits verbraucht und kann nicht ${action} werden`);
405
+ }
406
+ if (existing.status === "expired") {
407
+ throw new import_common.ConflictException(`CheckoutOffer '${existing.id}' ist abgelaufen und kann nicht ${action} werden`);
408
+ }
409
+ if (this.isExpired(existing)) {
410
+ throw new import_common.ConflictException(`CheckoutOffer '${existing.id}' ist abgelaufen und kann nicht ${action} werden`);
411
+ }
412
+ }
413
+ isExpired(row) {
414
+ return this.isValidUntilExpired(row.validUntil, Date.now());
415
+ }
416
+ dateIsAfterNow(value, now) {
417
+ if (!value) return false;
418
+ const time = new Date(value).getTime();
419
+ return Number.isNaN(time) || time > now;
420
+ }
421
+ // `validUntil` is day-inclusive (day date, UTC midnight): expired
422
+ // only from the following day, i.e. when validUntil < startOfDay(now). Symmetric
423
+ // to the catalog resolver (buildActivePlanVersionWhere).
424
+ isValidUntilExpired(value, nowMs) {
425
+ if (!value) return false;
426
+ const validUntil = new Date(value).getTime();
427
+ if (Number.isNaN(validUntil)) return true;
428
+ return validUntil < (0, import_types.startOfUtcDay)(new Date(nowMs)).getTime();
429
+ }
430
+ };
431
+ CheckoutOfferService = _ts_decorate([
432
+ (0, import_common.Injectable)(),
433
+ _ts_param(0, (0, import_common.Inject)(CHECKOUT_OFFER_REPOSITORY_TOKEN)),
434
+ _ts_param(1, (0, import_common.Optional)()),
435
+ _ts_param(1, (0, import_common.Inject)(BUNDLE_REPOSITORY_TOKEN)),
436
+ _ts_param(2, (0, import_common.Optional)()),
437
+ _ts_param(2, (0, import_common.Inject)(PLAN_REPOSITORY_TOKEN)),
438
+ _ts_param(3, (0, import_common.Optional)()),
439
+ _ts_param(3, (0, import_common.Inject)(CATALOG_ENTRY_REPOSITORY_TOKEN)),
440
+ _ts_metadata("design:type", Function),
441
+ _ts_metadata("design:paramtypes", [
442
+ typeof CheckoutOfferRepository === "undefined" ? Object : CheckoutOfferRepository,
443
+ Object,
444
+ Object,
445
+ Object
446
+ ])
447
+ ], CheckoutOfferService);
448
+
449
+ // src/checkout-offer/checkout-offer.controller.ts
450
+ var import_common2 = require("@nestjs/common");
451
+
452
+ // src/checkout-offer/dto/checkout-offer.dto.ts
453
+ var import_class_validator = require("class-validator");
454
+ function _ts_decorate2(decorators, target, key, desc) {
455
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
456
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
457
+ 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;
458
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
459
+ }
460
+ __name(_ts_decorate2, "_ts_decorate");
461
+ function _ts_metadata2(k, v) {
462
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
463
+ }
464
+ __name(_ts_metadata2, "_ts_metadata");
465
+ var PROJECT_KEY_PATTERN = /^[a-z][a-z0-9-]*$/;
466
+ var CYCLES = [
467
+ "monthly",
468
+ "yearly"
469
+ ];
470
+ var CreateCheckoutOfferDto = class {
471
+ static {
472
+ __name(this, "CreateCheckoutOfferDto");
473
+ }
474
+ projectKey;
475
+ planKey;
476
+ planVersionId;
477
+ billingCycle;
478
+ promotionId;
479
+ promoCode;
480
+ bundles;
481
+ bundleVersionIds;
482
+ priceBreakdown;
483
+ lineItems;
484
+ promotionSnapshots;
485
+ promoCodeSnapshot;
486
+ locale;
487
+ validUntil;
488
+ };
489
+ _ts_decorate2([
490
+ (0, import_class_validator.IsString)(),
491
+ (0, import_class_validator.Matches)(PROJECT_KEY_PATTERN, {
492
+ message: "projectKey muss kebab-case sein"
493
+ }),
494
+ (0, import_class_validator.MaxLength)(64),
495
+ _ts_metadata2("design:type", String)
496
+ ], CreateCheckoutOfferDto.prototype, "projectKey", void 0);
497
+ _ts_decorate2([
498
+ (0, import_class_validator.IsString)(),
499
+ (0, import_class_validator.MaxLength)(64),
500
+ _ts_metadata2("design:type", String)
501
+ ], CreateCheckoutOfferDto.prototype, "planKey", void 0);
502
+ _ts_decorate2([
503
+ (0, import_class_validator.IsOptional)(),
504
+ (0, import_class_validator.IsString)(),
505
+ _ts_metadata2("design:type", Object)
506
+ ], CreateCheckoutOfferDto.prototype, "planVersionId", void 0);
507
+ _ts_decorate2([
508
+ (0, import_class_validator.IsString)(),
509
+ (0, import_class_validator.IsIn)(CYCLES),
510
+ _ts_metadata2("design:type", String)
511
+ ], CreateCheckoutOfferDto.prototype, "billingCycle", void 0);
512
+ _ts_decorate2([
513
+ (0, import_class_validator.IsOptional)(),
514
+ (0, import_class_validator.IsString)(),
515
+ _ts_metadata2("design:type", Object)
516
+ ], CreateCheckoutOfferDto.prototype, "promotionId", void 0);
517
+ _ts_decorate2([
518
+ (0, import_class_validator.IsOptional)(),
519
+ (0, import_class_validator.IsString)(),
520
+ (0, import_class_validator.MaxLength)(64),
521
+ _ts_metadata2("design:type", Object)
522
+ ], CreateCheckoutOfferDto.prototype, "promoCode", void 0);
523
+ _ts_decorate2([
524
+ (0, import_class_validator.IsOptional)(),
525
+ (0, import_class_validator.IsArray)(),
526
+ (0, import_class_validator.IsString)({
527
+ each: true
528
+ }),
529
+ _ts_metadata2("design:type", Array)
530
+ ], CreateCheckoutOfferDto.prototype, "bundles", void 0);
531
+ _ts_decorate2([
532
+ (0, import_class_validator.IsOptional)(),
533
+ (0, import_class_validator.IsArray)(),
534
+ (0, import_class_validator.IsString)({
535
+ each: true
536
+ }),
537
+ _ts_metadata2("design:type", Array)
538
+ ], CreateCheckoutOfferDto.prototype, "bundleVersionIds", void 0);
539
+ _ts_decorate2([
540
+ (0, import_class_validator.IsDefined)(),
541
+ (0, import_class_validator.IsObject)(),
542
+ _ts_metadata2("design:type", typeof CheckoutOfferPriceBreakdown === "undefined" ? Object : CheckoutOfferPriceBreakdown)
543
+ ], CreateCheckoutOfferDto.prototype, "priceBreakdown", void 0);
544
+ _ts_decorate2([
545
+ (0, import_class_validator.IsOptional)(),
546
+ (0, import_class_validator.IsArray)(),
547
+ _ts_metadata2("design:type", Array)
548
+ ], CreateCheckoutOfferDto.prototype, "lineItems", void 0);
549
+ _ts_decorate2([
550
+ (0, import_class_validator.IsOptional)(),
551
+ (0, import_class_validator.IsArray)(),
552
+ _ts_metadata2("design:type", Array)
553
+ ], CreateCheckoutOfferDto.prototype, "promotionSnapshots", void 0);
554
+ _ts_decorate2([
555
+ (0, import_class_validator.IsOptional)(),
556
+ (0, import_class_validator.IsObject)(),
557
+ _ts_metadata2("design:type", Object)
558
+ ], CreateCheckoutOfferDto.prototype, "promoCodeSnapshot", void 0);
559
+ _ts_decorate2([
560
+ (0, import_class_validator.IsOptional)(),
561
+ (0, import_class_validator.IsString)(),
562
+ (0, import_class_validator.MaxLength)(8),
563
+ _ts_metadata2("design:type", String)
564
+ ], CreateCheckoutOfferDto.prototype, "locale", void 0);
565
+ _ts_decorate2([
566
+ (0, import_class_validator.IsOptional)(),
567
+ (0, import_class_validator.IsDateString)(),
568
+ _ts_metadata2("design:type", Object)
569
+ ], CreateCheckoutOfferDto.prototype, "validUntil", void 0);
570
+ var UpdateCheckoutOfferDto = class {
571
+ static {
572
+ __name(this, "UpdateCheckoutOfferDto");
573
+ }
574
+ billingCycle;
575
+ promotionId;
576
+ promoCode;
577
+ bundles;
578
+ bundleVersionIds;
579
+ priceBreakdown;
580
+ lineItems;
581
+ promotionSnapshots;
582
+ promoCodeSnapshot;
583
+ locale;
584
+ validUntil;
585
+ };
586
+ _ts_decorate2([
587
+ (0, import_class_validator.IsOptional)(),
588
+ (0, import_class_validator.IsString)(),
589
+ (0, import_class_validator.IsIn)(CYCLES),
590
+ _ts_metadata2("design:type", String)
591
+ ], UpdateCheckoutOfferDto.prototype, "billingCycle", void 0);
592
+ _ts_decorate2([
593
+ (0, import_class_validator.IsOptional)(),
594
+ (0, import_class_validator.IsString)(),
595
+ _ts_metadata2("design:type", Object)
596
+ ], UpdateCheckoutOfferDto.prototype, "promotionId", void 0);
597
+ _ts_decorate2([
598
+ (0, import_class_validator.IsOptional)(),
599
+ (0, import_class_validator.IsString)(),
600
+ (0, import_class_validator.MaxLength)(64),
601
+ _ts_metadata2("design:type", Object)
602
+ ], UpdateCheckoutOfferDto.prototype, "promoCode", void 0);
603
+ _ts_decorate2([
604
+ (0, import_class_validator.IsOptional)(),
605
+ (0, import_class_validator.IsArray)(),
606
+ (0, import_class_validator.IsString)({
607
+ each: true
608
+ }),
609
+ _ts_metadata2("design:type", Array)
610
+ ], UpdateCheckoutOfferDto.prototype, "bundles", void 0);
611
+ _ts_decorate2([
612
+ (0, import_class_validator.IsOptional)(),
613
+ (0, import_class_validator.IsArray)(),
614
+ (0, import_class_validator.IsString)({
615
+ each: true
616
+ }),
617
+ _ts_metadata2("design:type", Array)
618
+ ], UpdateCheckoutOfferDto.prototype, "bundleVersionIds", void 0);
619
+ _ts_decorate2([
620
+ (0, import_class_validator.IsOptional)(),
621
+ (0, import_class_validator.IsObject)(),
622
+ _ts_metadata2("design:type", typeof CheckoutOfferPriceBreakdown === "undefined" ? Object : CheckoutOfferPriceBreakdown)
623
+ ], UpdateCheckoutOfferDto.prototype, "priceBreakdown", void 0);
624
+ _ts_decorate2([
625
+ (0, import_class_validator.IsOptional)(),
626
+ (0, import_class_validator.IsArray)(),
627
+ _ts_metadata2("design:type", Array)
628
+ ], UpdateCheckoutOfferDto.prototype, "lineItems", void 0);
629
+ _ts_decorate2([
630
+ (0, import_class_validator.IsOptional)(),
631
+ (0, import_class_validator.IsArray)(),
632
+ _ts_metadata2("design:type", Array)
633
+ ], UpdateCheckoutOfferDto.prototype, "promotionSnapshots", void 0);
634
+ _ts_decorate2([
635
+ (0, import_class_validator.IsOptional)(),
636
+ (0, import_class_validator.IsObject)(),
637
+ _ts_metadata2("design:type", Object)
638
+ ], UpdateCheckoutOfferDto.prototype, "promoCodeSnapshot", void 0);
639
+ _ts_decorate2([
640
+ (0, import_class_validator.IsOptional)(),
641
+ (0, import_class_validator.IsString)(),
642
+ (0, import_class_validator.MaxLength)(8),
643
+ _ts_metadata2("design:type", String)
644
+ ], UpdateCheckoutOfferDto.prototype, "locale", void 0);
645
+ _ts_decorate2([
646
+ (0, import_class_validator.IsOptional)(),
647
+ (0, import_class_validator.IsDateString)(),
648
+ _ts_metadata2("design:type", Object)
649
+ ], UpdateCheckoutOfferDto.prototype, "validUntil", void 0);
650
+
651
+ // src/checkout-offer/checkout-offer.controller.ts
652
+ function _ts_decorate3(decorators, target, key, desc) {
653
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
654
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
655
+ 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;
656
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
657
+ }
658
+ __name(_ts_decorate3, "_ts_decorate");
659
+ function _ts_metadata3(k, v) {
660
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
661
+ }
662
+ __name(_ts_metadata3, "_ts_metadata");
663
+ function _ts_param2(paramIndex, decorator) {
664
+ return function(target, key) {
665
+ decorator(target, key, paramIndex);
666
+ };
667
+ }
668
+ __name(_ts_param2, "_ts_param");
669
+ function buildCheckoutOfferController(guards) {
670
+ let GeneratedCheckoutOfferController = class GeneratedCheckoutOfferController {
671
+ static {
672
+ __name(this, "GeneratedCheckoutOfferController");
673
+ }
674
+ service;
675
+ constructor(service) {
676
+ this.service = service;
677
+ }
678
+ /** The website creates an offer when a package is clicked. */
679
+ create(dto) {
680
+ return this.service.create(dto);
681
+ }
682
+ /** Onboarding reads the pre-selected offer (`?offer=<id>`). */
683
+ getById(id) {
684
+ return this.service.getById(id);
685
+ }
686
+ /** Onboarding customization — as long as the offer is `open`. */
687
+ update(id, dto) {
688
+ return this.service.update(id, dto);
689
+ }
690
+ };
691
+ _ts_decorate3([
692
+ (0, import_common2.Post)(),
693
+ _ts_param2(0, (0, import_common2.Body)()),
694
+ _ts_metadata3("design:type", Function),
695
+ _ts_metadata3("design:paramtypes", [
696
+ typeof CreateCheckoutOfferDto === "undefined" ? Object : CreateCheckoutOfferDto
697
+ ]),
698
+ _ts_metadata3("design:returntype", void 0)
699
+ ], GeneratedCheckoutOfferController.prototype, "create", null);
700
+ _ts_decorate3([
701
+ (0, import_common2.Get)(":id"),
702
+ _ts_param2(0, (0, import_common2.Param)("id", new import_common2.ParseUUIDPipe())),
703
+ _ts_metadata3("design:type", Function),
704
+ _ts_metadata3("design:paramtypes", [
705
+ String
706
+ ]),
707
+ _ts_metadata3("design:returntype", void 0)
708
+ ], GeneratedCheckoutOfferController.prototype, "getById", null);
709
+ _ts_decorate3([
710
+ (0, import_common2.Patch)(":id"),
711
+ _ts_param2(0, (0, import_common2.Param)("id", new import_common2.ParseUUIDPipe())),
712
+ _ts_param2(1, (0, import_common2.Body)()),
713
+ _ts_metadata3("design:type", Function),
714
+ _ts_metadata3("design:paramtypes", [
715
+ String,
716
+ typeof UpdateCheckoutOfferDto === "undefined" ? Object : UpdateCheckoutOfferDto
717
+ ]),
718
+ _ts_metadata3("design:returntype", void 0)
719
+ ], GeneratedCheckoutOfferController.prototype, "update", null);
720
+ GeneratedCheckoutOfferController = _ts_decorate3([
721
+ (0, import_common2.Controller)("public/checkout-offer"),
722
+ (0, import_common2.UseGuards)(...guards),
723
+ _ts_param2(0, (0, import_common2.Inject)(CheckoutOfferService)),
724
+ _ts_metadata3("design:type", Function),
725
+ _ts_metadata3("design:paramtypes", [
726
+ typeof CheckoutOfferService === "undefined" ? Object : CheckoutOfferService
727
+ ])
728
+ ], GeneratedCheckoutOfferController);
729
+ return GeneratedCheckoutOfferController;
730
+ }
731
+ __name(buildCheckoutOfferController, "buildCheckoutOfferController");
732
+
733
+ // src/checkout-offer/checkout-offer.module.ts
734
+ var import_common3 = require("@nestjs/common");
735
+
736
+ // src/core/di.ts
737
+ function asProvider(token, impl) {
738
+ if (typeof impl === "object" && impl !== null && "useFactory" in impl && typeof impl.useFactory === "function") {
739
+ const factory = impl;
740
+ return {
741
+ provide: token,
742
+ useFactory: factory.useFactory,
743
+ inject: factory.inject
744
+ };
745
+ }
746
+ return {
747
+ provide: token,
748
+ useValue: impl
749
+ };
750
+ }
751
+ __name(asProvider, "asProvider");
752
+
753
+ // src/checkout-offer/checkout-offer.module.ts
754
+ function _ts_decorate4(decorators, target, key, desc) {
755
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
756
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
757
+ 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;
758
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
759
+ }
760
+ __name(_ts_decorate4, "_ts_decorate");
761
+ var CheckoutOfferModule = class _CheckoutOfferModule {
762
+ static {
763
+ __name(this, "CheckoutOfferModule");
764
+ }
765
+ static forRoot(options) {
766
+ const controllers = [];
767
+ if (options.controller) {
768
+ controllers.push(buildCheckoutOfferController(options.controller.guards));
769
+ }
770
+ return {
771
+ module: _CheckoutOfferModule,
772
+ global: options.global ?? false,
773
+ imports: options.imports ?? [],
774
+ controllers,
775
+ providers: [
776
+ asProvider(CHECKOUT_OFFER_REPOSITORY_TOKEN, options.checkoutOfferRepository),
777
+ ...options.bundleRepository ? [
778
+ asProvider(BUNDLE_REPOSITORY_TOKEN, options.bundleRepository)
779
+ ] : [],
780
+ ...options.planRepository ? [
781
+ asProvider(PLAN_REPOSITORY_TOKEN, options.planRepository)
782
+ ] : [],
783
+ ...options.catalogEntryRepository ? [
784
+ asProvider(CATALOG_ENTRY_REPOSITORY_TOKEN, options.catalogEntryRepository)
785
+ ] : [],
786
+ CheckoutOfferService,
787
+ ...options.extraProviders ?? []
788
+ ],
789
+ exports: [
790
+ CheckoutOfferService,
791
+ CHECKOUT_OFFER_REPOSITORY_TOKEN
792
+ ]
793
+ };
794
+ }
795
+ };
796
+ CheckoutOfferModule = _ts_decorate4([
797
+ (0, import_common3.Module)({})
798
+ ], CheckoutOfferModule);
799
+ // Annotate the CommonJS export names for ESM import in node:
800
+ 0 && (module.exports = {
801
+ CHECKOUT_OFFER_REPOSITORY_TOKEN,
802
+ CheckoutOfferModule,
803
+ CheckoutOfferService,
804
+ CreateCheckoutOfferDto,
805
+ UpdateCheckoutOfferDto,
806
+ buildCheckoutOfferController
807
+ });