@saasicat/nest 0.9.0 → 0.10.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 (86) hide show
  1. package/dist/_entries.cjs +17678 -0
  2. package/dist/admin/index.cjs +4 -1533
  3. package/dist/admin/index.d.cts +10 -173
  4. package/dist/admin/index.d.ts +10 -173
  5. package/dist/admin/index.js +3 -2
  6. package/dist/{admin-resources.module-BybEjZs-.d.cts → admin-resources.module-BZN_yBFj.d.cts} +1 -1
  7. package/dist/{admin-resources.module-CUCLlUAi.d.ts → admin-resources.module-Cms8MY6h.d.ts} +1 -1
  8. package/dist/admin-stats.module-15ac5KY3.d.ts +201 -0
  9. package/dist/admin-stats.module-v4SVvWzj.d.cts +201 -0
  10. package/dist/billing/index.cjs +4 -5130
  11. package/dist/billing/index.d.cts +8 -437
  12. package/dist/billing/index.d.ts +8 -437
  13. package/dist/billing/index.js +4 -4
  14. package/dist/catalog/index.cjs +4 -5624
  15. package/dist/catalog/index.d.cts +5 -328
  16. package/dist/catalog/index.d.ts +5 -328
  17. package/dist/catalog/index.js +4 -4
  18. package/dist/checkout-offer/index.cjs +4 -807
  19. package/dist/checkout-offer/index.d.cts +5 -84
  20. package/dist/checkout-offer/index.d.ts +5 -84
  21. package/dist/checkout-offer/index.js +3 -2
  22. package/dist/checkout-offer.module-Y0hDOVrq.d.ts +85 -0
  23. package/dist/checkout-offer.module-ZYF5IsDY.d.cts +85 -0
  24. package/dist/{chunk-PBQRPDO2.js → chunk-33TP26MR.js} +2 -2
  25. package/dist/{chunk-CK54YKVK.js → chunk-75JABGZK.js} +16 -16
  26. package/dist/{chunk-HRU3CP57.js → chunk-BOADBIGB.js} +1 -1
  27. package/dist/chunk-DBZGV3HC.js +0 -0
  28. package/dist/{chunk-ZLUGQE5D.js → chunk-E7AJ42AB.js} +8 -8
  29. package/dist/{chunk-HXAWPPFW.js → chunk-F3VV6EXG.js} +9 -9
  30. package/dist/{chunk-3M6VLT5N.js → chunk-FVT7KTUI.js} +2 -2
  31. package/dist/{chunk-GW2I3JZD.js → chunk-GBDUH6DO.js} +1 -1
  32. package/dist/{chunk-K3OPNVFR.js → chunk-HBFU5AN3.js} +5 -9
  33. package/dist/{chunk-OFBDEO45.js → chunk-IRWZC3GC.js} +1 -1
  34. package/dist/chunk-L4KTQ6CL.js +20 -0
  35. package/dist/{chunk-WGVTXSYW.js → chunk-LZHLGUQI.js} +1 -1
  36. package/dist/{chunk-ZZMAHX22.js → chunk-PUXLPUVH.js} +15 -12
  37. package/dist/{chunk-7K5PT2LX.js → chunk-TXDH4FUA.js} +1 -1
  38. package/dist/chunk-UF53DWWR.js +0 -0
  39. package/dist/{chunk-AJ3EPELS.js → chunk-V34AA7C7.js} +1 -1
  40. package/dist/chunk-WJ6BMDL3.js +313 -0
  41. package/dist/{chunk-M6CZQYPY.js → chunk-Z3FWN3HJ.js} +2 -2
  42. package/dist/discovery/index.cjs +4 -588
  43. package/dist/discovery/index.js +3 -3
  44. package/dist/{enforce-quota.interceptor-SqrQVF91.d.cts → enforce-quota.interceptor-BhkX5nq2.d.cts} +35 -2
  45. package/dist/{enforce-quota.interceptor-B3CL5MSw.d.ts → enforce-quota.interceptor-CcqZc0Yu.d.ts} +35 -2
  46. package/dist/entitlement/index.cjs +4 -781
  47. package/dist/entitlement/index.js +2 -2
  48. package/dist/index.cjs +4 -15671
  49. package/dist/index.d.cts +21 -56
  50. package/dist/index.d.ts +21 -56
  51. package/dist/index.js +39 -334
  52. package/dist/{module-DeH_Jkcq.d.cts → module-CSN8j8Tp.d.cts} +1 -1
  53. package/dist/{module-B5m080F0.d.ts → module-DgYJur6f.d.ts} +1 -1
  54. package/dist/plan-versions.service-BB36Q1bg.d.ts +329 -0
  55. package/dist/plan-versions.service-CUR2Whyr.d.cts +329 -0
  56. package/dist/platform/index.cjs +4 -12925
  57. package/dist/platform/index.d.cts +17 -9
  58. package/dist/platform/index.d.ts +17 -9
  59. package/dist/platform/index.js +98 -14
  60. package/dist/promo/index.cjs +4 -1520
  61. package/dist/promo/index.d.cts +6 -21
  62. package/dist/promo/index.d.ts +6 -21
  63. package/dist/promo/index.js +2 -2
  64. package/dist/rate-limit.guard-dntqAlfZ.d.cts +19 -0
  65. package/dist/rate-limit.guard-dntqAlfZ.d.ts +19 -0
  66. package/dist/registration/index.cjs +4 -1655
  67. package/dist/registration/index.js +1 -1
  68. package/dist/{service-DlhMnYjW.d.ts → service-DX8KbGXl.d.cts} +1 -1
  69. package/dist/{service-DlhMnYjW.d.cts → service-DX8KbGXl.d.ts} +1 -1
  70. package/dist/setup.service-BPwJ7My_.d.cts +45 -0
  71. package/dist/setup.service-D2mrdDDB.d.ts +45 -0
  72. package/dist/subscription-bundle-preview.service-BkJXHAeP.d.cts +438 -0
  73. package/dist/subscription-bundle-preview.service-DBbrE_cw.d.ts +438 -0
  74. package/dist/subscription-contract/index.cjs +4 -471
  75. package/dist/subscription-contract/index.d.cts +5 -13
  76. package/dist/subscription-contract/index.d.ts +5 -13
  77. package/dist/subscription-contract.module-CG9fPKAr.d.cts +14 -0
  78. package/dist/subscription-contract.module-CVebZzHR.d.ts +14 -0
  79. package/dist/testing/index.cjs +4 -13888
  80. package/dist/testing/index.d.cts +5 -5
  81. package/dist/testing/index.d.ts +5 -5
  82. package/dist/testing/index.js +10 -10
  83. package/package.json +4 -4
  84. package/dist/admin-manifest.service-BMeSLRW4.d.ts +0 -33
  85. package/dist/admin-manifest.service-StAQe6QO.d.cts +0 -33
  86. package/dist/chunk-N3WANKSA.js +0 -20
@@ -1,1520 +1,4 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
9
- var __export = (target, all) => {
10
- for (var name in all)
11
- __defProp(target, name, { get: all[name], enumerable: true });
12
- };
13
- var __copyProps = (to, from, except, desc) => {
14
- if (from && typeof from === "object" || typeof from === "function") {
15
- for (let key of __getOwnPropNames(from))
16
- if (!__hasOwnProp.call(to, key) && key !== except)
17
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
- }
19
- return to;
20
- };
21
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
- // If the importer is in node compatibility mode or this is not an ESM
23
- // file that has been converted to a CommonJS file using a Babel-
24
- // compatible transform (i.e. "__esModule" has not been set), then set
25
- // "default" to the CommonJS "module.exports" for node compatibility.
26
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
- mod
28
- ));
29
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
-
31
- // src/promo/index.ts
32
- var promo_exports = {};
33
- __export(promo_exports, {
34
- CreatePromoCodeDto: () => CreatePromoCodeDto,
35
- PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN: () => PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN,
36
- PROMO_CODE_REPOSITORY_TOKEN: () => PROMO_CODE_REPOSITORY_TOKEN,
37
- PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN: () => PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN,
38
- PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN: () => PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN,
39
- PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN: () => PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN,
40
- PROMO_SERVICE_CONFIG_TOKEN: () => PROMO_SERVICE_CONFIG_TOKEN,
41
- PROMO_SUBSCRIPTION_LOOKUP_TOKEN: () => PROMO_SUBSCRIPTION_LOOKUP_TOKEN,
42
- PROMO_TRANSACTION_RUNNER_TOKEN: () => PROMO_TRANSACTION_RUNNER_TOKEN,
43
- PreviewPromoCodeDto: () => PreviewPromoCodeDto,
44
- PromoCodeExpirer: () => PromoCodeExpirer,
45
- PromoCodePublicController: () => PromoCodePublicController,
46
- PromoCodeRateLimitGuard: () => PromoCodeRateLimitGuard,
47
- PromoCodesModule: () => PromoCodesModule,
48
- PromoCodesService: () => PromoCodesService,
49
- UpdatePromoCodeDto: () => UpdatePromoCodeDto,
50
- addCycles: () => addCycles,
51
- buildLabel: () => buildLabel,
52
- buildPromoCodeAdminController: () => buildPromoCodeAdminController,
53
- computeDiscountGross: () => computeDiscountGross,
54
- computeDiscountedGross: () => computeDiscountedGross,
55
- computeIncludedVat: () => computeIncludedVat,
56
- computeRegularStartsAt: () => computeRegularStartsAt,
57
- grossFromNet: () => grossFromNet,
58
- hashIp: () => hashIp,
59
- ipFingerprint: () => ipFingerprint,
60
- round2: () => round2
61
- });
62
- module.exports = __toCommonJS(promo_exports);
63
-
64
- // src/promo/math.ts
65
- function round2(n) {
66
- return Math.round(n * 100) / 100;
67
- }
68
- __name(round2, "round2");
69
- function grossFromNet(net, vatRate) {
70
- return round2(net * (1 + vatRate / 100));
71
- }
72
- __name(grossFromNet, "grossFromNet");
73
- function computeIncludedVat(grossAmount, vatRate) {
74
- return round2(grossAmount * vatRate / (100 + vatRate));
75
- }
76
- __name(computeIncludedVat, "computeIncludedVat");
77
-
78
- // src/promo/calculator.ts
79
- function computeDiscountGross(plan, code) {
80
- const v = Number(code.value);
81
- if (code.valueType === "PERCENT") {
82
- return round2(plan.gross * v / 100);
83
- }
84
- return round2(v);
85
- }
86
- __name(computeDiscountGross, "computeDiscountGross");
87
- function computeDiscountedGross(planGross, discountGross) {
88
- return round2(planGross - discountGross);
89
- }
90
- __name(computeDiscountedGross, "computeDiscountedGross");
91
- function computeRegularStartsAt(startsAt, cycle, durationType, durationValue) {
92
- const result = new Date(startsAt);
93
- if (durationType === "ONCE") {
94
- return addCycles(result, cycle, 1);
95
- }
96
- if (durationType === "MONTHS") {
97
- result.setMonth(result.getMonth() + (durationValue ?? 0));
98
- return result;
99
- }
100
- return addCycles(result, cycle, durationValue ?? 0);
101
- }
102
- __name(computeRegularStartsAt, "computeRegularStartsAt");
103
- function addCycles(date, cycle, n) {
104
- const r = new Date(date);
105
- if (cycle === "YEARLY") {
106
- r.setFullYear(r.getFullYear() + n);
107
- } else {
108
- r.setMonth(r.getMonth() + n);
109
- }
110
- return r;
111
- }
112
- __name(addCycles, "addCycles");
113
- function buildLabel(code, cycle) {
114
- const value = Number(code.value);
115
- const valueStr = code.valueType === "PERCENT" ? `${value.toLocaleString("de-DE")} %` : `${value.toLocaleString("de-DE", {
116
- minimumFractionDigits: 2,
117
- maximumFractionDigits: 2
118
- })} \u20AC`;
119
- if (code.durationType === "ONCE") {
120
- return `${valueStr} einmalig`;
121
- }
122
- if (code.durationType === "MONTHS") {
123
- const m = code.durationValue ?? 0;
124
- return m === 1 ? `${valueStr} im ersten Monat` : `${valueStr} f\xFCr ${m} Monate`;
125
- }
126
- const n = code.durationValue ?? 0;
127
- if (n === 1) return cycle === "YEARLY" ? `${valueStr} im ersten Jahr` : `${valueStr} im ersten Monat`;
128
- return cycle === "YEARLY" ? `${valueStr} in den ersten ${n} Jahren` : `${valueStr} in den ersten ${n} Monaten`;
129
- }
130
- __name(buildLabel, "buildLabel");
131
-
132
- // src/promo/service.ts
133
- var import_common2 = require("@nestjs/common");
134
-
135
- // src/billing/plan-catalog.module.ts
136
- var import_common = require("@nestjs/common");
137
-
138
- // src/core/di.ts
139
- function asProvider(token, impl) {
140
- if (typeof impl === "object" && impl !== null && "useFactory" in impl && typeof impl.useFactory === "function") {
141
- const factory = impl;
142
- return {
143
- provide: token,
144
- useFactory: factory.useFactory,
145
- inject: factory.inject
146
- };
147
- }
148
- return {
149
- provide: token,
150
- useValue: impl
151
- };
152
- }
153
- __name(asProvider, "asProvider");
154
-
155
- // src/billing/plan-catalog-from-snapshot.ts
156
- function buildPlanCatalogFromSnapshot(settings, snapshot) {
157
- const liveByPlanKey = new Map(snapshot.livePlanVersions.map((v) => [
158
- v.planId,
159
- v
160
- ]));
161
- const plans = snapshot.plans.filter((p) => p.deletedAt === null).sort((a, b) => a.sortOrder - b.sortOrder).map((stem) => {
162
- const live = liveByPlanKey.get(stem.planKey);
163
- if (!live) {
164
- return {
165
- id: stem.planKey,
166
- name: stem.label,
167
- tagline: stem.description ?? void 0,
168
- marketed: false,
169
- monthlyNet: null,
170
- yearlyNet: null,
171
- features: [],
172
- quotas: {}
173
- };
174
- }
175
- return {
176
- id: stem.planKey,
177
- name: stem.label,
178
- tagline: stem.description ?? void 0,
179
- marketed: live.marketed,
180
- monthlyNet: parseFloat(live.monthlyNet),
181
- yearlyNet: parseFloat(live.yearlyNet),
182
- features: live.features,
183
- quotas: live.quotas ?? {}
184
- };
185
- });
186
- const features = snapshot.featureEntries.filter((f) => f.deletedAt === null).sort((a, b) => a.sortOrder - b.sortOrder).map((row) => ({
187
- key: row.featureKey,
188
- label: row.label,
189
- icon: row.icon ?? void 0,
190
- tier: row.tier ?? void 0,
191
- plannedOnly: row.plannedOnly
192
- }));
193
- return {
194
- schemaVersion: 1,
195
- projectKey: settings.projectKey,
196
- ...settings.app ? {
197
- app: settings.app
198
- } : {},
199
- currency: settings.currency,
200
- vatRate: settings.vatRate,
201
- ...settings.marketing ? {
202
- marketing: settings.marketing
203
- } : {},
204
- features,
205
- plans
206
- };
207
- }
208
- __name(buildPlanCatalogFromSnapshot, "buildPlanCatalogFromSnapshot");
209
-
210
- // src/billing/plan-catalog.module.ts
211
- function _ts_decorate(decorators, target, key, desc) {
212
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
213
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
214
- 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;
215
- return c > 3 && r && Object.defineProperty(target, key, r), r;
216
- }
217
- __name(_ts_decorate, "_ts_decorate");
218
- var PLAN_CATALOG_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/PLAN_CATALOG");
219
- var PLAN_CATALOG_READ_SINK_TOKEN = /* @__PURE__ */ Symbol.for("saasicat/nest/PLAN_CATALOG_READ_SINK");
220
- var PlanCatalogModule = class _PlanCatalogModule {
221
- static {
222
- __name(this, "PlanCatalogModule");
223
- }
224
- static forRoot(options) {
225
- return {
226
- module: _PlanCatalogModule,
227
- global: options.global ?? true,
228
- imports: options.imports ?? [],
229
- providers: [
230
- ...options.extraProviders ?? [],
231
- asProvider(PLAN_CATALOG_READ_SINK_TOKEN, options.sink),
232
- {
233
- provide: PLAN_CATALOG_TOKEN,
234
- useFactory: /* @__PURE__ */ __name(async (sink) => {
235
- const snapshot = await sink.loadSnapshot(options.projectKey);
236
- return buildPlanCatalogFromSnapshot({
237
- projectKey: options.projectKey,
238
- app: options.app,
239
- currency: options.currency,
240
- vatRate: options.vatRate,
241
- marketing: options.marketing
242
- }, snapshot);
243
- }, "useFactory"),
244
- inject: [
245
- PLAN_CATALOG_READ_SINK_TOKEN
246
- ]
247
- }
248
- ],
249
- exports: [
250
- PLAN_CATALOG_TOKEN,
251
- PLAN_CATALOG_READ_SINK_TOKEN
252
- ]
253
- };
254
- }
255
- /**
256
- * Tests / in-memory setup: takes the catalog object directly, without a
257
- * sink/DB lookup. For unit tests + test bootstraps.
258
- */
259
- static forRootWithCatalog(catalog, opts = {}) {
260
- return {
261
- module: _PlanCatalogModule,
262
- providers: [
263
- {
264
- provide: PLAN_CATALOG_TOKEN,
265
- useValue: catalog
266
- }
267
- ],
268
- exports: [
269
- PLAN_CATALOG_TOKEN
270
- ],
271
- global: opts.global ?? true
272
- };
273
- }
274
- };
275
- PlanCatalogModule = _ts_decorate([
276
- (0, import_common.Module)({})
277
- ], PlanCatalogModule);
278
-
279
- // src/billing/plan-helpers.ts
280
- function findPlan(catalog, planId) {
281
- return (catalog.plans ?? []).find((p) => p.id === planId);
282
- }
283
- __name(findPlan, "findPlan");
284
- function getPlanPriceNet(catalog, planId, cycle) {
285
- const plan = findPlan(catalog, planId);
286
- if (!plan) return null;
287
- if (plan.marketed === false) return null;
288
- const net = cycle === "YEARLY" ? plan.yearlyNet : plan.monthlyNet;
289
- return net ?? null;
290
- }
291
- __name(getPlanPriceNet, "getPlanPriceNet");
292
- function getPlanPriceGross(catalog, planId, cycle, vatRate) {
293
- const net = getPlanPriceNet(catalog, planId, cycle);
294
- if (net === null) return null;
295
- const rate = vatRate ?? catalog.vatRate;
296
- return round2(net * (1 + rate / 100));
297
- }
298
- __name(getPlanPriceGross, "getPlanPriceGross");
299
-
300
- // src/promo/tokens.ts
301
- var PROMO_CODE_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("PROMO_CODE_REPOSITORY");
302
- var PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("PROMO_CODE_REDEMPTION_REPOSITORY");
303
- var PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN = /* @__PURE__ */ Symbol("PROMO_CODE_VALIDATION_LOG_REPOSITORY");
304
- var PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN = /* @__PURE__ */ Symbol("PROMO_FIRST_TIME_CUSTOMER_CHECK");
305
- var PROMO_SUBSCRIPTION_LOOKUP_TOKEN = /* @__PURE__ */ Symbol("PROMO_SUBSCRIPTION_LOOKUP");
306
- var PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN = /* @__PURE__ */ Symbol("PROMO_REVENUE_DEDUCTION_AGGREGATOR");
307
- var PROMO_TRANSACTION_RUNNER_TOKEN = /* @__PURE__ */ Symbol("PROMO_TRANSACTION_RUNNER");
308
- var PROMO_SERVICE_CONFIG_TOKEN = /* @__PURE__ */ Symbol("PROMO_SERVICE_CONFIG");
309
-
310
- // src/promo/service.ts
311
- function _ts_decorate2(decorators, target, key, desc) {
312
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
313
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
314
- 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;
315
- return c > 3 && r && Object.defineProperty(target, key, r), r;
316
- }
317
- __name(_ts_decorate2, "_ts_decorate");
318
- function _ts_metadata(k, v) {
319
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
320
- }
321
- __name(_ts_metadata, "_ts_metadata");
322
- function _ts_param(paramIndex, decorator) {
323
- return function(target, key) {
324
- decorator(target, key, paramIndex);
325
- };
326
- }
327
- __name(_ts_param, "_ts_param");
328
- var CODE_PATTERN = /^[A-Z0-9_-]{4,32}$/;
329
- var PromoCodesService = class {
330
- static {
331
- __name(this, "PromoCodesService");
332
- }
333
- promoRepo;
334
- redemptionRepo;
335
- validationLogRepo;
336
- firstTimeCheck;
337
- subscriptionLookup;
338
- revenueAggregator;
339
- transactionRunner;
340
- planCatalog;
341
- config;
342
- constructor(promoRepo, redemptionRepo, validationLogRepo, firstTimeCheck, subscriptionLookup, revenueAggregator, transactionRunner, planCatalog, config) {
343
- this.promoRepo = promoRepo;
344
- this.redemptionRepo = redemptionRepo;
345
- this.validationLogRepo = validationLogRepo;
346
- this.firstTimeCheck = firstTimeCheck;
347
- this.subscriptionLookup = subscriptionLookup;
348
- this.revenueAggregator = revenueAggregator;
349
- this.transactionRunner = transactionRunner;
350
- this.planCatalog = planCatalog;
351
- this.config = config;
352
- }
353
- // ─── ADMIN: Creation / Editing ─────────────────────────────────────────
354
- async create(input) {
355
- const code = input.code.trim().toUpperCase();
356
- if (!CODE_PATTERN.test(code)) {
357
- throw new import_common2.BadRequestException('Code darf nur Gro\xDFbuchstaben, Ziffern, \u201E-" und \u201E_" enthalten (4\u201332 Zeichen).');
358
- }
359
- if (input.valueType === "PERCENT") {
360
- if (!(input.value > 0 && input.value <= 100)) {
361
- throw new import_common2.BadRequestException("Prozentsatz muss zwischen 0 und 100 liegen.");
362
- }
363
- } else if (!(input.value > 0)) {
364
- throw new import_common2.BadRequestException("Betrag muss positiv sein.");
365
- }
366
- if (input.durationType === "ONCE") {
367
- if (input.durationValue != null) {
368
- throw new import_common2.BadRequestException("Bei einmaligem Rabatt darf keine Laufzeit gesetzt sein.");
369
- }
370
- } else {
371
- const v = input.durationValue;
372
- if (v == null || v < 1 || v > 24) {
373
- throw new import_common2.BadRequestException("Laufzeit ung\xFCltig (max. 24 Monate bzw. Abrechnungsperioden).");
374
- }
375
- }
376
- if (input.validFrom && input.validUntil && input.validFrom >= input.validUntil) {
377
- throw new import_common2.BadRequestException("G\xFCltigkeits-Fenster ung\xFCltig.");
378
- }
379
- const plans = input.appliesToPlans ?? [];
380
- const blocked = this.config.nonRedeemablePlans ?? [];
381
- for (const p of plans) {
382
- if (blocked.includes(p)) {
383
- throw new import_common2.BadRequestException(`${p}-Plan kann nicht rabattiert werden.`);
384
- }
385
- }
386
- if (input.minimumPlanAmountGross != null && input.minimumPlanAmountGross <= 0) {
387
- throw new import_common2.BadRequestException("Mindest-Plan-Brutto muss positiv sein.");
388
- }
389
- if (input.valueType === "ABSOLUTE" && !input.allowZeroInvoice) {
390
- const lowestApplicableGross = this.lowestApplicablePlanGross(plans);
391
- if (lowestApplicableGross != null && input.value >= lowestApplicableGross) {
392
- throw new import_common2.BadRequestException("Bei Absolutbetr\xE4gen muss der Rabatt unter dem niedrigsten anwendbaren Plan-Preis liegen oder allowZeroInvoice aktiviert sein.");
393
- }
394
- }
395
- const exists = await this.promoRepo.findByCode(code);
396
- if (exists) throw new import_common2.BadRequestException("Code existiert bereits.");
397
- return this.promoRepo.create({
398
- ...input,
399
- code
400
- });
401
- }
402
- async update(id, input) {
403
- const existing = await this.promoRepo.findById(id);
404
- if (!existing) throw new import_common2.NotFoundException("Code nicht gefunden");
405
- if (input.maxRedemptions != null && existing.maxRedemptions != null) {
406
- if (input.maxRedemptions < existing.maxRedemptions) {
407
- throw new import_common2.BadRequestException("maxRedemptions kann nicht gesenkt werden.");
408
- }
409
- }
410
- return this.promoRepo.update(id, input);
411
- }
412
- async softDelete(id) {
413
- const redemptions = await this.redemptionRepo.countByPromoCode(id);
414
- if (redemptions > 0) {
415
- throw new import_common2.BadRequestException("Code hat bereits Einl\xF6sungen \u2014 Soft-Delete nicht m\xF6glich. Stattdessen pausieren.");
416
- }
417
- await this.promoRepo.softDelete(id);
418
- }
419
- async findAll(filter = {}) {
420
- await this.lazyExpire();
421
- return this.promoRepo.findMany(filter);
422
- }
423
- async findOne(id) {
424
- await this.lazyExpire();
425
- const code = await this.promoRepo.findById(id);
426
- if (!code || code.deletedAt) throw new import_common2.NotFoundException("Code nicht gefunden");
427
- return code;
428
- }
429
- async stats(id) {
430
- const code = await this.findOne(id);
431
- const [active, total, reversed, expired, validations, revenueDeductionGross] = await Promise.all([
432
- this.redemptionRepo.countByPromoCode(id, "ACTIVE"),
433
- this.redemptionRepo.countByPromoCode(id),
434
- this.redemptionRepo.countByPromoCode(id, "REVERSED"),
435
- this.redemptionRepo.countByPromoCode(id, "EXPIRED"),
436
- this.validationLogRepo.countValid(id),
437
- this.revenueAggregator.sumGrossForPromoCode(id)
438
- ]);
439
- return {
440
- code,
441
- validations,
442
- redemptions: {
443
- active,
444
- total,
445
- reversed,
446
- expired
447
- },
448
- revenueDeductionGross
449
- };
450
- }
451
- async listRedemptions(promoCodeId) {
452
- return this.redemptionRepo.listByPromoCode(promoCodeId);
453
- }
454
- // ─── PUBLIC: Preview / Validation ──────────────────────────────────────
455
- async preview(input) {
456
- const code = input.code.trim().toUpperCase();
457
- if (!CODE_PATTERN.test(code)) {
458
- await this.validationLogRepo.log({
459
- promoCodeId: null,
460
- codeAttempt: code,
461
- result: "NOT_FOUND",
462
- ipHash: input.ipHash,
463
- sessionId: input.sessionId
464
- });
465
- return {
466
- valid: false,
467
- reason: "NOT_FOUND"
468
- };
469
- }
470
- await this.lazyExpire();
471
- const promo = await this.promoRepo.findByCode(code);
472
- const reason = this.checkEligibility(promo, input) ?? await this.checkFirstTimeCustomer(promo, input.email);
473
- if (reason) {
474
- await this.validationLogRepo.log({
475
- promoCodeId: promo?.id ?? null,
476
- codeAttempt: code,
477
- result: reason,
478
- ipHash: input.ipHash,
479
- sessionId: input.sessionId
480
- });
481
- return {
482
- valid: false,
483
- reason
484
- };
485
- }
486
- const planGross = getPlanPriceGross(this.planCatalog, input.planId, input.billingCycle);
487
- const discountGross = Math.min(computeDiscountGross({
488
- gross: planGross
489
- }, promo), planGross);
490
- const discountedGross = computeDiscountedGross(planGross, discountGross);
491
- const includedVat = computeIncludedVat(discountedGross, this.planCatalog.vatRate);
492
- if (!promo.allowZeroInvoice && discountedGross <= 0) {
493
- await this.validationLogRepo.log({
494
- promoCodeId: promo.id,
495
- codeAttempt: code,
496
- result: "WOULD_PRODUCE_ZERO_INVOICE",
497
- ipHash: input.ipHash,
498
- sessionId: input.sessionId
499
- });
500
- return {
501
- valid: false,
502
- reason: "WOULD_PRODUCE_ZERO_INVOICE"
503
- };
504
- }
505
- const startsAt = /* @__PURE__ */ new Date();
506
- const regularStartsAt = computeRegularStartsAt(startsAt, input.billingCycle, promo.durationType, promo.durationValue);
507
- await this.validationLogRepo.log({
508
- promoCodeId: promo.id,
509
- codeAttempt: code,
510
- result: "VALID",
511
- ipHash: input.ipHash,
512
- sessionId: input.sessionId
513
- });
514
- return {
515
- valid: true,
516
- code: promo.code,
517
- label: buildLabel(promo, input.billingCycle),
518
- discount: {
519
- valueType: promo.valueType,
520
- value: Number(promo.value).toFixed(2),
521
- durationType: promo.durationType,
522
- durationValue: promo.durationValue
523
- },
524
- price: {
525
- originalGross: planGross.toFixed(2),
526
- discountGross: discountGross.toFixed(2),
527
- discountedGross: discountedGross.toFixed(2),
528
- includedVat: includedVat.toFixed(2),
529
- nextRegularAmountGross: planGross.toFixed(2),
530
- regularStartsAt: regularStartsAt ? regularStartsAt.toISOString() : null
531
- }
532
- };
533
- }
534
- // ─── Onboarding path: atomic redemption ────────────────────────────────
535
- /**
536
- * Wraps `redeemInTransaction` in its own transaction-runner call —
537
- * the default path when the caller has no external tx context
538
- * (e.g. POST /billing/promo/redeem as a stand-alone endpoint).
539
- */
540
- async redeem(input) {
541
- return this.transactionRunner.run((tx) => this.redeemInTransaction(input, tx));
542
- }
543
- /**
544
- * Redeems a promo code within an EXTERNAL transaction — the caller
545
- * (typically `TenantSubscriptionWritePort.applyOnboardingSelection`)
546
- * already has `prisma.$transaction(...)` open and passes the `tx`
547
- * context through. This lands plan change, add-on insert, and redemption
548
- * insert in a single DB transaction (P10.1.1).
549
- */
550
- async redeemInTransaction(input, tx) {
551
- const code = input.code.trim().toUpperCase();
552
- const promo = await this.promoRepo.findByCode(code, tx);
553
- if (!promo || promo.deletedAt) {
554
- throw new import_common2.BadRequestException("Code nicht gefunden");
555
- }
556
- const sub = await this.subscriptionLookup.findById(input.subscriptionId, tx);
557
- if (!sub) throw new import_common2.NotFoundException("Subscription nicht gefunden");
558
- if (sub.tenantId !== input.tenantId) {
559
- throw new import_common2.BadRequestException("Subscription geh\xF6rt nicht zum Tenant");
560
- }
561
- const reason = this.checkEligibility(promo, {
562
- code,
563
- planId: sub.plan,
564
- billingCycle: sub.billingCycle
565
- });
566
- if (reason) throw new import_common2.BadRequestException(`Code nicht einl\xF6sbar: ${reason}`);
567
- const firstTimeReason = await this.checkFirstTimeCustomer(promo, input.email, {
568
- requireEmail: true
569
- });
570
- if (firstTimeReason) {
571
- throw new import_common2.BadRequestException(`Code nicht einl\xF6sbar: ${firstTimeReason}`);
572
- }
573
- const claimed = await this.promoRepo.claimSlot(promo.id, tx);
574
- if (!claimed) {
575
- throw new import_common2.BadRequestException("Code nicht einl\xF6sbar: EXHAUSTED");
576
- }
577
- await this.promoRepo.markExhaustedIfFull(promo.id, tx);
578
- const startsAt = sub.startedAt ?? /* @__PURE__ */ new Date();
579
- const endsAt = promo.durationType === "ONCE" ? null : computeRegularStartsAt(startsAt, sub.billingCycle, promo.durationType, promo.durationValue);
580
- return this.redemptionRepo.create({
581
- promoCodeId: promo.id,
582
- subscriptionId: sub.id,
583
- tenantId: sub.tenantId,
584
- appliedValueType: promo.valueType,
585
- appliedValue: promo.value,
586
- appliedDurationType: promo.durationType,
587
- appliedDurationValue: promo.durationValue,
588
- startsAt,
589
- endsAt
590
- }, tx);
591
- }
592
- async reverse(subscriptionId) {
593
- return this.transactionRunner.run(async (tx) => {
594
- const redemption = await this.redemptionRepo.findBySubscription(subscriptionId, tx);
595
- if (!redemption || redemption.status !== "ACTIVE") return redemption;
596
- const updated = await this.redemptionRepo.setReversed(redemption.id, tx);
597
- await this.promoRepo.releaseSlot(redemption.promoCodeId, tx);
598
- return updated;
599
- });
600
- }
601
- // ─── Helpers ───────────────────────────────────────────────────────────
602
- checkEligibility(promo, input) {
603
- if (!promo || promo.deletedAt) return "NOT_FOUND";
604
- if (promo.status === "EXPIRED") return "EXPIRED";
605
- if (promo.status === "EXHAUSTED") return "EXHAUSTED";
606
- if (promo.status === "PAUSED") return "PAUSED";
607
- const now = /* @__PURE__ */ new Date();
608
- if (promo.validFrom && promo.validFrom > now) return "EXPIRED";
609
- if (promo.validUntil && promo.validUntil < now) return "EXPIRED";
610
- const blocked = this.config.nonRedeemablePlans ?? [];
611
- if (blocked.includes(input.planId)) {
612
- return "PLAN_MISMATCH";
613
- }
614
- if (promo.appliesToPlans.length > 0 && !promo.appliesToPlans.includes(input.planId)) {
615
- return "PLAN_MISMATCH";
616
- }
617
- if (promo.appliesToBilling && promo.appliesToBilling !== input.billingCycle) {
618
- return "BILLING_MISMATCH";
619
- }
620
- const planGross = getPlanPriceGross(this.planCatalog, input.planId, input.billingCycle);
621
- if (planGross == null) return "PLAN_MISMATCH";
622
- if (promo.minimumPlanAmountGross && planGross < Number(promo.minimumPlanAmountGross)) {
623
- return "BELOW_MINIMUM_AMOUNT";
624
- }
625
- return null;
626
- }
627
- async checkFirstTimeCustomer(promo, email, options = {}) {
628
- if (!promo || !promo.firstTimeCustomersOnly) return null;
629
- if (!email) return options.requireEmail ? "NOT_FIRST_TIME_CUSTOMER" : null;
630
- const existing = await this.firstTimeCheck.hasExistingCustomerForEmail(email);
631
- return existing ? "NOT_FIRST_TIME_CUSTOMER" : null;
632
- }
633
- /**
634
- * Lazy expiry: sets codes whose validUntil has passed to EXPIRED.
635
- * Called before every find/preview — defense-in-depth alongside the cron.
636
- */
637
- async lazyExpire() {
638
- const now = /* @__PURE__ */ new Date();
639
- await this.promoRepo.expireDueCodes(now);
640
- await this.redemptionRepo.expireDueRedemptions(now);
641
- }
642
- /**
643
- * Lowest applicable plan price. With a whitelist it takes the minimum
644
- * from the whitelist, otherwise across all marketed plans of the catalog
645
- * (except non-redeemable).
646
- */
647
- lowestApplicablePlanGross(plans) {
648
- const blocked = new Set(this.config.nonRedeemablePlans ?? []);
649
- const candidates = plans.length > 0 ? plans : (this.planCatalog.plans ?? []).filter((p) => p.marketed !== false && !blocked.has(p.id)).map((p) => p.id);
650
- let min = null;
651
- for (const p of candidates) {
652
- const g = getPlanPriceGross(this.planCatalog, p, "MONTHLY");
653
- if (g == null) continue;
654
- if (min == null || g < min) min = g;
655
- }
656
- return min;
657
- }
658
- };
659
- PromoCodesService = _ts_decorate2([
660
- (0, import_common2.Injectable)(),
661
- _ts_param(0, (0, import_common2.Inject)(PROMO_CODE_REPOSITORY_TOKEN)),
662
- _ts_param(1, (0, import_common2.Inject)(PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN)),
663
- _ts_param(2, (0, import_common2.Inject)(PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN)),
664
- _ts_param(3, (0, import_common2.Inject)(PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN)),
665
- _ts_param(4, (0, import_common2.Inject)(PROMO_SUBSCRIPTION_LOOKUP_TOKEN)),
666
- _ts_param(5, (0, import_common2.Inject)(PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN)),
667
- _ts_param(6, (0, import_common2.Inject)(PROMO_TRANSACTION_RUNNER_TOKEN)),
668
- _ts_param(7, (0, import_common2.Inject)(PLAN_CATALOG_TOKEN)),
669
- _ts_param(8, (0, import_common2.Inject)(PROMO_SERVICE_CONFIG_TOKEN)),
670
- _ts_metadata("design:type", Function),
671
- _ts_metadata("design:paramtypes", [
672
- typeof PromoCodeRepository === "undefined" ? Object : PromoCodeRepository,
673
- typeof PromoCodeRedemptionRepository === "undefined" ? Object : PromoCodeRedemptionRepository,
674
- typeof PromoCodeValidationLogRepository === "undefined" ? Object : PromoCodeValidationLogRepository,
675
- typeof FirstTimeCustomerCheck === "undefined" ? Object : FirstTimeCustomerCheck,
676
- typeof PromoSubscriptionLookup === "undefined" ? Object : PromoSubscriptionLookup,
677
- typeof PromoRevenueDeductionAggregator === "undefined" ? Object : PromoRevenueDeductionAggregator,
678
- typeof TransactionRunner === "undefined" ? Object : TransactionRunner,
679
- typeof PlanCatalog === "undefined" ? Object : PlanCatalog,
680
- typeof PromoServiceConfig === "undefined" ? Object : PromoServiceConfig
681
- ])
682
- ], PromoCodesService);
683
-
684
- // src/promo/expirer.ts
685
- var import_common3 = require("@nestjs/common");
686
- var import_schedule = require("@nestjs/schedule");
687
- function _ts_decorate3(decorators, target, key, desc) {
688
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
689
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
690
- 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;
691
- return c > 3 && r && Object.defineProperty(target, key, r), r;
692
- }
693
- __name(_ts_decorate3, "_ts_decorate");
694
- function _ts_metadata2(k, v) {
695
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
696
- }
697
- __name(_ts_metadata2, "_ts_metadata");
698
- function _ts_param2(paramIndex, decorator) {
699
- return function(target, key) {
700
- decorator(target, key, paramIndex);
701
- };
702
- }
703
- __name(_ts_param2, "_ts_param");
704
- var PromoCodeExpirer = class _PromoCodeExpirer {
705
- static {
706
- __name(this, "PromoCodeExpirer");
707
- }
708
- promoRepo;
709
- redemptionRepo;
710
- logger = new import_common3.Logger(_PromoCodeExpirer.name);
711
- constructor(promoRepo, redemptionRepo) {
712
- this.promoRepo = promoRepo;
713
- this.redemptionRepo = redemptionRepo;
714
- }
715
- async expirePromoCodes() {
716
- const now = /* @__PURE__ */ new Date();
717
- const codes = await this.promoRepo.expireDueCodes(now);
718
- const redemptions = await this.redemptionRepo.expireDueRedemptions(now);
719
- if (codes > 0 || redemptions > 0) {
720
- this.logger.log(`PromoCodeExpirer: ${codes} Codes, ${redemptions} Redemptions abgelaufen.`);
721
- }
722
- }
723
- };
724
- _ts_decorate3([
725
- (0, import_schedule.Cron)(import_schedule.CronExpression.EVERY_DAY_AT_3AM, {
726
- name: "promoCodeExpirer",
727
- timeZone: "Europe/Berlin"
728
- }),
729
- _ts_metadata2("design:type", Function),
730
- _ts_metadata2("design:paramtypes", []),
731
- _ts_metadata2("design:returntype", Promise)
732
- ], PromoCodeExpirer.prototype, "expirePromoCodes", null);
733
- PromoCodeExpirer = _ts_decorate3([
734
- (0, import_common3.Injectable)(),
735
- _ts_param2(0, (0, import_common3.Inject)(PROMO_CODE_REPOSITORY_TOKEN)),
736
- _ts_param2(1, (0, import_common3.Inject)(PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN)),
737
- _ts_metadata2("design:type", Function),
738
- _ts_metadata2("design:paramtypes", [
739
- typeof PromoCodeRepository === "undefined" ? Object : PromoCodeRepository,
740
- typeof PromoCodeRedemptionRepository === "undefined" ? Object : PromoCodeRedemptionRepository
741
- ])
742
- ], PromoCodeExpirer);
743
-
744
- // src/promo/rate-limit.guard.ts
745
- var import_common4 = require("@nestjs/common");
746
- function _ts_decorate4(decorators, target, key, desc) {
747
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
748
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
749
- 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;
750
- return c > 3 && r && Object.defineProperty(target, key, r), r;
751
- }
752
- __name(_ts_decorate4, "_ts_decorate");
753
- var IP_WINDOW_MS = 6e4;
754
- var IP_LIMIT = 20;
755
- var SESSION_WINDOW_MS = 60 * 6e4;
756
- var SESSION_LIMIT = 50;
757
- var PromoCodeRateLimitGuard = class {
758
- static {
759
- __name(this, "PromoCodeRateLimitGuard");
760
- }
761
- ipBuckets = /* @__PURE__ */ new Map();
762
- sessionBuckets = /* @__PURE__ */ new Map();
763
- canActivate(context) {
764
- const req = context.switchToHttp().getRequest();
765
- const ipKey = ipFingerprint(req);
766
- const sessionKey = req.user?.id ?? null;
767
- if (this.exceeded(this.ipBuckets, ipKey, IP_WINDOW_MS, IP_LIMIT)) {
768
- throw new import_common4.HttpException({
769
- valid: false,
770
- reason: "RATE_LIMITED"
771
- }, import_common4.HttpStatus.TOO_MANY_REQUESTS);
772
- }
773
- if (sessionKey && this.exceeded(this.sessionBuckets, sessionKey, SESSION_WINDOW_MS, SESSION_LIMIT)) {
774
- throw new import_common4.HttpException({
775
- valid: false,
776
- reason: "RATE_LIMITED"
777
- }, import_common4.HttpStatus.TOO_MANY_REQUESTS);
778
- }
779
- return true;
780
- }
781
- exceeded(map, key, windowMs, limit) {
782
- const now = Date.now();
783
- const b = map.get(key);
784
- if (!b || now - b.windowStart > windowMs) {
785
- map.set(key, {
786
- count: 1,
787
- windowStart: now
788
- });
789
- return false;
790
- }
791
- b.count += 1;
792
- return b.count > limit;
793
- }
794
- };
795
- PromoCodeRateLimitGuard = _ts_decorate4([
796
- (0, import_common4.Injectable)()
797
- ], PromoCodeRateLimitGuard);
798
- function ipFingerprint(req) {
799
- const fwd = req.headers["x-forwarded-for"];
800
- const first = Array.isArray(fwd) ? fwd[0] : fwd?.split(",")[0]?.trim();
801
- return first || req.ip || "unknown";
802
- }
803
- __name(ipFingerprint, "ipFingerprint");
804
- function hashIp(req) {
805
- const fp = ipFingerprint(req);
806
- let h = 0;
807
- for (let i = 0; i < fp.length; i++) {
808
- h = h * 31 + fp.charCodeAt(i) | 0;
809
- }
810
- return `ip${(h >>> 0).toString(16)}`;
811
- }
812
- __name(hashIp, "hashIp");
813
-
814
- // src/promo/controller.ts
815
- var import_common5 = require("@nestjs/common");
816
-
817
- // src/promo/dto/promo-public.dto.ts
818
- var import_class_validator = require("class-validator");
819
- function _ts_decorate5(decorators, target, key, desc) {
820
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
821
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
822
- 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;
823
- return c > 3 && r && Object.defineProperty(target, key, r), r;
824
- }
825
- __name(_ts_decorate5, "_ts_decorate");
826
- function _ts_metadata3(k, v) {
827
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
828
- }
829
- __name(_ts_metadata3, "_ts_metadata");
830
- var PLAN_OR_CYCLE_PATTERN = /^[A-Z][A-Z0-9_]*$/;
831
- var CODE_PATTERN2 = /^[A-Z0-9_-]{4,32}$/i;
832
- var PreviewPromoCodeDto = class {
833
- static {
834
- __name(this, "PreviewPromoCodeDto");
835
- }
836
- code;
837
- plan;
838
- billingCycle;
839
- email;
840
- };
841
- _ts_decorate5([
842
- (0, import_class_validator.IsString)(),
843
- (0, import_class_validator.Matches)(CODE_PATTERN2, {
844
- message: 'code muss aus A\u2013Z, 0\u20139, "-" und "_" bestehen (4\u201332 Zeichen)'
845
- }),
846
- _ts_metadata3("design:type", String)
847
- ], PreviewPromoCodeDto.prototype, "code", void 0);
848
- _ts_decorate5([
849
- (0, import_class_validator.IsString)(),
850
- (0, import_class_validator.Matches)(PLAN_OR_CYCLE_PATTERN, {
851
- message: "plan muss SCREAMING_SNAKE_CASE sein"
852
- }),
853
- _ts_metadata3("design:type", String)
854
- ], PreviewPromoCodeDto.prototype, "plan", void 0);
855
- _ts_decorate5([
856
- (0, import_class_validator.IsString)(),
857
- (0, import_class_validator.Matches)(PLAN_OR_CYCLE_PATTERN, {
858
- message: "billingCycle muss SCREAMING_SNAKE_CASE sein (z. B. MONTHLY, YEARLY)"
859
- }),
860
- _ts_metadata3("design:type", String)
861
- ], PreviewPromoCodeDto.prototype, "billingCycle", void 0);
862
- _ts_decorate5([
863
- (0, import_class_validator.IsOptional)(),
864
- (0, import_class_validator.IsEmail)(),
865
- (0, import_class_validator.MaxLength)(254),
866
- _ts_metadata3("design:type", String)
867
- ], PreviewPromoCodeDto.prototype, "email", void 0);
868
-
869
- // src/promo/controller.ts
870
- function _ts_decorate6(decorators, target, key, desc) {
871
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
872
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
873
- 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;
874
- return c > 3 && r && Object.defineProperty(target, key, r), r;
875
- }
876
- __name(_ts_decorate6, "_ts_decorate");
877
- function _ts_metadata4(k, v) {
878
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
879
- }
880
- __name(_ts_metadata4, "_ts_metadata");
881
- function _ts_param3(paramIndex, decorator) {
882
- return function(target, key) {
883
- decorator(target, key, paramIndex);
884
- };
885
- }
886
- __name(_ts_param3, "_ts_param");
887
- var PromoCodePublicController = class {
888
- static {
889
- __name(this, "PromoCodePublicController");
890
- }
891
- promo;
892
- constructor(promo) {
893
- this.promo = promo;
894
- }
895
- async preview(dto, req) {
896
- return this.promo.preview({
897
- code: dto.code,
898
- planId: dto.plan,
899
- billingCycle: dto.billingCycle,
900
- email: dto.email,
901
- ipHash: hashIp(req),
902
- sessionId: req.user?.id ?? req.user?.sub
903
- });
904
- }
905
- };
906
- _ts_decorate6([
907
- (0, import_common5.Post)("preview"),
908
- (0, import_common5.HttpCode)(import_common5.HttpStatus.OK),
909
- (0, import_common5.UseGuards)(PromoCodeRateLimitGuard),
910
- _ts_param3(0, (0, import_common5.Body)()),
911
- _ts_param3(1, (0, import_common5.Req)()),
912
- _ts_metadata4("design:type", Function),
913
- _ts_metadata4("design:paramtypes", [
914
- typeof PreviewPromoCodeDto === "undefined" ? Object : PreviewPromoCodeDto,
915
- typeof RequestLike === "undefined" ? Object : RequestLike
916
- ]),
917
- _ts_metadata4("design:returntype", Promise)
918
- ], PromoCodePublicController.prototype, "preview", null);
919
- PromoCodePublicController = _ts_decorate6([
920
- (0, import_common5.Controller)("billing/promo"),
921
- _ts_param3(0, (0, import_common5.Inject)(PromoCodesService)),
922
- _ts_metadata4("design:type", Function),
923
- _ts_metadata4("design:paramtypes", [
924
- typeof PromoCodesService === "undefined" ? Object : PromoCodesService
925
- ])
926
- ], PromoCodePublicController);
927
-
928
- // src/promo/module.ts
929
- var import_common8 = require("@nestjs/common");
930
-
931
- // src/promo/admin-controller.ts
932
- var import_common7 = require("@nestjs/common");
933
-
934
- // src/admin/admin-audit.service.ts
935
- var os = __toESM(require("os"), 1);
936
- var import_common6 = require("@nestjs/common");
937
-
938
- // src/admin/tokens.ts
939
- var AUDIT_PORT_TOKEN = /* @__PURE__ */ Symbol.for("saas-platform/AuditPort");
940
-
941
- // src/admin/admin-audit.service.ts
942
- function _ts_decorate7(decorators, target, key, desc) {
943
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
944
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
945
- 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;
946
- return c > 3 && r && Object.defineProperty(target, key, r), r;
947
- }
948
- __name(_ts_decorate7, "_ts_decorate");
949
- function _ts_metadata5(k, v) {
950
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
951
- }
952
- __name(_ts_metadata5, "_ts_metadata");
953
- function _ts_param4(paramIndex, decorator) {
954
- return function(target, key) {
955
- decorator(target, key, paramIndex);
956
- };
957
- }
958
- __name(_ts_param4, "_ts_param");
959
- var AdminAuditService = class {
960
- static {
961
- __name(this, "AdminAuditService");
962
- }
963
- audit;
964
- constructor(audit) {
965
- this.audit = audit;
966
- }
967
- async log(input) {
968
- await this.audit.write({
969
- actor: input.actor,
970
- entity: input.entity,
971
- entityId: input.entityId,
972
- action: input.action,
973
- changes: {
974
- ...input.changes ?? {},
975
- actor: this.actorTag(input.actor)
976
- }
977
- });
978
- }
979
- /**
980
- * Standardized actor tag for the `changes.actor` column:
981
- * `<source>:<email>:<context>`.
982
- */
983
- actorTag(actor) {
984
- return `${actor.source}:${actor.email}:${actor.context}`;
985
- }
986
- /** Helper: build an AdminActor from an authenticated web request. */
987
- fromWebRequest(user, sessionId) {
988
- return {
989
- userId: user.id,
990
- email: user.email,
991
- source: "web",
992
- context: sessionId ?? "unknown"
993
- };
994
- }
995
- /** Helper: build an AdminActor from a CLI identity (hostname as context). */
996
- fromCli(user) {
997
- return {
998
- userId: user.id,
999
- email: user.email,
1000
- source: "cli",
1001
- context: os.hostname()
1002
- };
1003
- }
1004
- };
1005
- AdminAuditService = _ts_decorate7([
1006
- (0, import_common6.Injectable)(),
1007
- _ts_param4(0, (0, import_common6.Inject)(AUDIT_PORT_TOKEN)),
1008
- _ts_metadata5("design:type", Function),
1009
- _ts_metadata5("design:paramtypes", [
1010
- typeof AuditPort === "undefined" ? Object : AuditPort
1011
- ])
1012
- ], AdminAuditService);
1013
-
1014
- // src/promo/dto/promo-admin.dto.ts
1015
- var import_class_validator2 = require("class-validator");
1016
- function _ts_decorate8(decorators, target, key, desc) {
1017
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1018
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1019
- 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;
1020
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1021
- }
1022
- __name(_ts_decorate8, "_ts_decorate");
1023
- function _ts_metadata6(k, v) {
1024
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1025
- }
1026
- __name(_ts_metadata6, "_ts_metadata");
1027
- var VALUE_TYPES = [
1028
- "PERCENT",
1029
- "ABSOLUTE"
1030
- ];
1031
- var DURATION_TYPES = [
1032
- "ONCE",
1033
- "MONTHS",
1034
- "BILLING_CYCLES"
1035
- ];
1036
- var STATUSES = [
1037
- "ACTIVE",
1038
- "PAUSED"
1039
- ];
1040
- var BILLING_CYCLES = [
1041
- "MONTHLY",
1042
- "YEARLY"
1043
- ];
1044
- var CreatePromoCodeDto = class {
1045
- static {
1046
- __name(this, "CreatePromoCodeDto");
1047
- }
1048
- code;
1049
- valueType;
1050
- value;
1051
- durationType;
1052
- durationValue;
1053
- maxRedemptions;
1054
- validFrom;
1055
- validUntil;
1056
- appliesToPlans;
1057
- appliesToBilling;
1058
- firstTimeCustomersOnly;
1059
- minimumPlanAmountGross;
1060
- allowZeroInvoice;
1061
- revenueDeductionAccount;
1062
- campaignTag;
1063
- description;
1064
- };
1065
- _ts_decorate8([
1066
- (0, import_class_validator2.IsString)(),
1067
- (0, import_class_validator2.IsNotEmpty)(),
1068
- (0, import_class_validator2.MaxLength)(64),
1069
- _ts_metadata6("design:type", String)
1070
- ], CreatePromoCodeDto.prototype, "code", void 0);
1071
- _ts_decorate8([
1072
- (0, import_class_validator2.IsIn)(VALUE_TYPES),
1073
- _ts_metadata6("design:type", Object)
1074
- ], CreatePromoCodeDto.prototype, "valueType", void 0);
1075
- _ts_decorate8([
1076
- (0, import_class_validator2.IsNumber)(),
1077
- (0, import_class_validator2.Min)(0),
1078
- _ts_metadata6("design:type", Number)
1079
- ], CreatePromoCodeDto.prototype, "value", void 0);
1080
- _ts_decorate8([
1081
- (0, import_class_validator2.IsIn)(DURATION_TYPES),
1082
- _ts_metadata6("design:type", Object)
1083
- ], CreatePromoCodeDto.prototype, "durationType", void 0);
1084
- _ts_decorate8([
1085
- (0, import_class_validator2.IsOptional)(),
1086
- (0, import_class_validator2.IsInt)(),
1087
- (0, import_class_validator2.Min)(1),
1088
- _ts_metadata6("design:type", Object)
1089
- ], CreatePromoCodeDto.prototype, "durationValue", void 0);
1090
- _ts_decorate8([
1091
- (0, import_class_validator2.IsOptional)(),
1092
- (0, import_class_validator2.IsInt)(),
1093
- (0, import_class_validator2.Min)(1),
1094
- _ts_metadata6("design:type", Object)
1095
- ], CreatePromoCodeDto.prototype, "maxRedemptions", void 0);
1096
- _ts_decorate8([
1097
- (0, import_class_validator2.IsOptional)(),
1098
- (0, import_class_validator2.IsString)(),
1099
- _ts_metadata6("design:type", Object)
1100
- ], CreatePromoCodeDto.prototype, "validFrom", void 0);
1101
- _ts_decorate8([
1102
- (0, import_class_validator2.IsOptional)(),
1103
- (0, import_class_validator2.IsString)(),
1104
- _ts_metadata6("design:type", Object)
1105
- ], CreatePromoCodeDto.prototype, "validUntil", void 0);
1106
- _ts_decorate8([
1107
- (0, import_class_validator2.IsOptional)(),
1108
- (0, import_class_validator2.IsArray)(),
1109
- (0, import_class_validator2.IsString)({
1110
- each: true
1111
- }),
1112
- (0, import_class_validator2.ArrayUnique)(),
1113
- _ts_metadata6("design:type", Array)
1114
- ], CreatePromoCodeDto.prototype, "appliesToPlans", void 0);
1115
- _ts_decorate8([
1116
- (0, import_class_validator2.IsOptional)(),
1117
- (0, import_class_validator2.IsIn)(BILLING_CYCLES),
1118
- _ts_metadata6("design:type", Object)
1119
- ], CreatePromoCodeDto.prototype, "appliesToBilling", void 0);
1120
- _ts_decorate8([
1121
- (0, import_class_validator2.IsOptional)(),
1122
- (0, import_class_validator2.IsBoolean)(),
1123
- _ts_metadata6("design:type", Boolean)
1124
- ], CreatePromoCodeDto.prototype, "firstTimeCustomersOnly", void 0);
1125
- _ts_decorate8([
1126
- (0, import_class_validator2.IsOptional)(),
1127
- (0, import_class_validator2.IsNumber)(),
1128
- (0, import_class_validator2.Min)(0),
1129
- _ts_metadata6("design:type", Object)
1130
- ], CreatePromoCodeDto.prototype, "minimumPlanAmountGross", void 0);
1131
- _ts_decorate8([
1132
- (0, import_class_validator2.IsOptional)(),
1133
- (0, import_class_validator2.IsBoolean)(),
1134
- _ts_metadata6("design:type", Boolean)
1135
- ], CreatePromoCodeDto.prototype, "allowZeroInvoice", void 0);
1136
- _ts_decorate8([
1137
- (0, import_class_validator2.IsOptional)(),
1138
- (0, import_class_validator2.IsString)(),
1139
- (0, import_class_validator2.MaxLength)(32),
1140
- _ts_metadata6("design:type", Object)
1141
- ], CreatePromoCodeDto.prototype, "revenueDeductionAccount", void 0);
1142
- _ts_decorate8([
1143
- (0, import_class_validator2.IsOptional)(),
1144
- (0, import_class_validator2.IsString)(),
1145
- (0, import_class_validator2.MaxLength)(64),
1146
- _ts_metadata6("design:type", Object)
1147
- ], CreatePromoCodeDto.prototype, "campaignTag", void 0);
1148
- _ts_decorate8([
1149
- (0, import_class_validator2.IsOptional)(),
1150
- (0, import_class_validator2.IsString)(),
1151
- (0, import_class_validator2.MaxLength)(500),
1152
- _ts_metadata6("design:type", Object)
1153
- ], CreatePromoCodeDto.prototype, "description", void 0);
1154
- var UpdatePromoCodeDto = class {
1155
- static {
1156
- __name(this, "UpdatePromoCodeDto");
1157
- }
1158
- status;
1159
- valueType;
1160
- value;
1161
- durationType;
1162
- durationValue;
1163
- maxRedemptions;
1164
- validFrom;
1165
- validUntil;
1166
- appliesToPlans;
1167
- appliesToBilling;
1168
- firstTimeCustomersOnly;
1169
- minimumPlanAmountGross;
1170
- allowZeroInvoice;
1171
- description;
1172
- campaignTag;
1173
- revenueDeductionAccount;
1174
- };
1175
- _ts_decorate8([
1176
- (0, import_class_validator2.IsOptional)(),
1177
- (0, import_class_validator2.IsIn)(STATUSES),
1178
- _ts_metadata6("design:type", Object)
1179
- ], UpdatePromoCodeDto.prototype, "status", void 0);
1180
- _ts_decorate8([
1181
- (0, import_class_validator2.IsOptional)(),
1182
- (0, import_class_validator2.IsIn)(VALUE_TYPES),
1183
- _ts_metadata6("design:type", Object)
1184
- ], UpdatePromoCodeDto.prototype, "valueType", void 0);
1185
- _ts_decorate8([
1186
- (0, import_class_validator2.IsOptional)(),
1187
- (0, import_class_validator2.IsNumber)(),
1188
- (0, import_class_validator2.Min)(0),
1189
- _ts_metadata6("design:type", Number)
1190
- ], UpdatePromoCodeDto.prototype, "value", void 0);
1191
- _ts_decorate8([
1192
- (0, import_class_validator2.IsOptional)(),
1193
- (0, import_class_validator2.IsIn)(DURATION_TYPES),
1194
- _ts_metadata6("design:type", Object)
1195
- ], UpdatePromoCodeDto.prototype, "durationType", void 0);
1196
- _ts_decorate8([
1197
- (0, import_class_validator2.IsOptional)(),
1198
- (0, import_class_validator2.IsInt)(),
1199
- (0, import_class_validator2.Min)(1),
1200
- _ts_metadata6("design:type", Object)
1201
- ], UpdatePromoCodeDto.prototype, "durationValue", void 0);
1202
- _ts_decorate8([
1203
- (0, import_class_validator2.IsOptional)(),
1204
- (0, import_class_validator2.IsInt)(),
1205
- (0, import_class_validator2.Min)(1),
1206
- _ts_metadata6("design:type", Object)
1207
- ], UpdatePromoCodeDto.prototype, "maxRedemptions", void 0);
1208
- _ts_decorate8([
1209
- (0, import_class_validator2.IsOptional)(),
1210
- (0, import_class_validator2.IsString)(),
1211
- _ts_metadata6("design:type", Object)
1212
- ], UpdatePromoCodeDto.prototype, "validFrom", void 0);
1213
- _ts_decorate8([
1214
- (0, import_class_validator2.IsOptional)(),
1215
- (0, import_class_validator2.IsString)(),
1216
- _ts_metadata6("design:type", Object)
1217
- ], UpdatePromoCodeDto.prototype, "validUntil", void 0);
1218
- _ts_decorate8([
1219
- (0, import_class_validator2.IsOptional)(),
1220
- (0, import_class_validator2.IsArray)(),
1221
- (0, import_class_validator2.IsString)({
1222
- each: true
1223
- }),
1224
- (0, import_class_validator2.ArrayUnique)(),
1225
- _ts_metadata6("design:type", Array)
1226
- ], UpdatePromoCodeDto.prototype, "appliesToPlans", void 0);
1227
- _ts_decorate8([
1228
- (0, import_class_validator2.IsOptional)(),
1229
- (0, import_class_validator2.IsIn)(BILLING_CYCLES),
1230
- _ts_metadata6("design:type", Object)
1231
- ], UpdatePromoCodeDto.prototype, "appliesToBilling", void 0);
1232
- _ts_decorate8([
1233
- (0, import_class_validator2.IsOptional)(),
1234
- (0, import_class_validator2.IsBoolean)(),
1235
- _ts_metadata6("design:type", Boolean)
1236
- ], UpdatePromoCodeDto.prototype, "firstTimeCustomersOnly", void 0);
1237
- _ts_decorate8([
1238
- (0, import_class_validator2.IsOptional)(),
1239
- (0, import_class_validator2.IsNumber)(),
1240
- (0, import_class_validator2.Min)(0),
1241
- _ts_metadata6("design:type", Object)
1242
- ], UpdatePromoCodeDto.prototype, "minimumPlanAmountGross", void 0);
1243
- _ts_decorate8([
1244
- (0, import_class_validator2.IsOptional)(),
1245
- (0, import_class_validator2.IsBoolean)(),
1246
- _ts_metadata6("design:type", Boolean)
1247
- ], UpdatePromoCodeDto.prototype, "allowZeroInvoice", void 0);
1248
- _ts_decorate8([
1249
- (0, import_class_validator2.IsOptional)(),
1250
- (0, import_class_validator2.IsString)(),
1251
- (0, import_class_validator2.MaxLength)(500),
1252
- _ts_metadata6("design:type", Object)
1253
- ], UpdatePromoCodeDto.prototype, "description", void 0);
1254
- _ts_decorate8([
1255
- (0, import_class_validator2.IsOptional)(),
1256
- (0, import_class_validator2.IsString)(),
1257
- (0, import_class_validator2.MaxLength)(64),
1258
- _ts_metadata6("design:type", Object)
1259
- ], UpdatePromoCodeDto.prototype, "campaignTag", void 0);
1260
- _ts_decorate8([
1261
- (0, import_class_validator2.IsOptional)(),
1262
- (0, import_class_validator2.IsString)(),
1263
- (0, import_class_validator2.MaxLength)(32),
1264
- _ts_metadata6("design:type", Object)
1265
- ], UpdatePromoCodeDto.prototype, "revenueDeductionAccount", void 0);
1266
-
1267
- // src/promo/admin-controller.ts
1268
- function _ts_decorate9(decorators, target, key, desc) {
1269
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1270
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1271
- 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;
1272
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1273
- }
1274
- __name(_ts_decorate9, "_ts_decorate");
1275
- function _ts_metadata7(k, v) {
1276
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1277
- }
1278
- __name(_ts_metadata7, "_ts_metadata");
1279
- function _ts_param5(paramIndex, decorator) {
1280
- return function(target, key) {
1281
- decorator(target, key, paramIndex);
1282
- };
1283
- }
1284
- __name(_ts_param5, "_ts_param");
1285
- function buildPromoCodeAdminController(guards) {
1286
- let GeneratedPromoCodeAdminController = class GeneratedPromoCodeAdminController {
1287
- static {
1288
- __name(this, "GeneratedPromoCodeAdminController");
1289
- }
1290
- promos;
1291
- audit;
1292
- constructor(promos, audit) {
1293
- this.promos = promos;
1294
- this.audit = audit;
1295
- }
1296
- findAll(query) {
1297
- return this.promos.findAll(query);
1298
- }
1299
- async create(request, dto) {
1300
- const actor = actorFromRequest(request);
1301
- const created = await this.promos.create(toCreateData(dto, actor.userId));
1302
- await this.audit.log({
1303
- actor,
1304
- entity: "PromoCode",
1305
- entityId: created.id,
1306
- action: "PROMO_CODE_CREATE",
1307
- changes: {
1308
- code: created.code
1309
- }
1310
- });
1311
- return created;
1312
- }
1313
- async update(request, id, dto) {
1314
- const actor = actorFromRequest(request);
1315
- const updated = await this.promos.update(id, toUpdateData(dto));
1316
- await this.audit.log({
1317
- actor,
1318
- entity: "PromoCode",
1319
- entityId: id,
1320
- action: "PROMO_CODE_UPDATE",
1321
- changes: {
1322
- fields: Object.keys(dto)
1323
- }
1324
- });
1325
- return updated;
1326
- }
1327
- async remove(request, id) {
1328
- const actor = actorFromRequest(request);
1329
- await this.promos.softDelete(id);
1330
- await this.audit.log({
1331
- actor,
1332
- entity: "PromoCode",
1333
- entityId: id,
1334
- action: "PROMO_CODE_DELETE"
1335
- });
1336
- return {
1337
- ok: true
1338
- };
1339
- }
1340
- };
1341
- _ts_decorate9([
1342
- (0, import_common7.Get)(),
1343
- _ts_param5(0, (0, import_common7.Query)()),
1344
- _ts_metadata7("design:type", Function),
1345
- _ts_metadata7("design:paramtypes", [
1346
- Object
1347
- ]),
1348
- _ts_metadata7("design:returntype", typeof Promise === "undefined" ? Object : Promise)
1349
- ], GeneratedPromoCodeAdminController.prototype, "findAll", null);
1350
- _ts_decorate9([
1351
- (0, import_common7.Post)(),
1352
- _ts_param5(0, (0, import_common7.Req)()),
1353
- _ts_param5(1, (0, import_common7.Body)()),
1354
- _ts_metadata7("design:type", Function),
1355
- _ts_metadata7("design:paramtypes", [
1356
- typeof AdminRequest === "undefined" ? Object : AdminRequest,
1357
- typeof CreatePromoCodeDto === "undefined" ? Object : CreatePromoCodeDto
1358
- ]),
1359
- _ts_metadata7("design:returntype", Promise)
1360
- ], GeneratedPromoCodeAdminController.prototype, "create", null);
1361
- _ts_decorate9([
1362
- (0, import_common7.Patch)(":id"),
1363
- _ts_param5(0, (0, import_common7.Req)()),
1364
- _ts_param5(1, (0, import_common7.Param)("id")),
1365
- _ts_param5(2, (0, import_common7.Body)()),
1366
- _ts_metadata7("design:type", Function),
1367
- _ts_metadata7("design:paramtypes", [
1368
- typeof AdminRequest === "undefined" ? Object : AdminRequest,
1369
- String,
1370
- typeof UpdatePromoCodeDto === "undefined" ? Object : UpdatePromoCodeDto
1371
- ]),
1372
- _ts_metadata7("design:returntype", Promise)
1373
- ], GeneratedPromoCodeAdminController.prototype, "update", null);
1374
- _ts_decorate9([
1375
- (0, import_common7.Delete)(":id"),
1376
- _ts_param5(0, (0, import_common7.Req)()),
1377
- _ts_param5(1, (0, import_common7.Param)("id")),
1378
- _ts_metadata7("design:type", Function),
1379
- _ts_metadata7("design:paramtypes", [
1380
- typeof AdminRequest === "undefined" ? Object : AdminRequest,
1381
- String
1382
- ]),
1383
- _ts_metadata7("design:returntype", Promise)
1384
- ], GeneratedPromoCodeAdminController.prototype, "remove", null);
1385
- GeneratedPromoCodeAdminController = _ts_decorate9([
1386
- (0, import_common7.Controller)("admin/promo-codes"),
1387
- (0, import_common7.UseGuards)(...guards),
1388
- _ts_param5(0, (0, import_common7.Inject)(PromoCodesService)),
1389
- _ts_param5(1, (0, import_common7.Inject)(AdminAuditService)),
1390
- _ts_metadata7("design:type", Function),
1391
- _ts_metadata7("design:paramtypes", [
1392
- typeof PromoCodesService === "undefined" ? Object : PromoCodesService,
1393
- typeof AdminAuditService === "undefined" ? Object : AdminAuditService
1394
- ])
1395
- ], GeneratedPromoCodeAdminController);
1396
- return GeneratedPromoCodeAdminController;
1397
- }
1398
- __name(buildPromoCodeAdminController, "buildPromoCodeAdminController");
1399
- function actorFromRequest(request) {
1400
- const userId = request.user?.id ?? request.user?.userId ?? "unknown";
1401
- return {
1402
- userId,
1403
- email: request.user?.email ?? `${userId}@unknown`,
1404
- source: "web",
1405
- context: request.user?.sessionId ?? "unknown"
1406
- };
1407
- }
1408
- __name(actorFromRequest, "actorFromRequest");
1409
- function toCreateData(dto, createdById) {
1410
- return {
1411
- ...dto,
1412
- validFrom: parseDate(dto.validFrom),
1413
- validUntil: parseDate(dto.validUntil),
1414
- createdById
1415
- };
1416
- }
1417
- __name(toCreateData, "toCreateData");
1418
- function toUpdateData(dto) {
1419
- const { validFrom, validUntil, ...fields } = dto;
1420
- const data = fields;
1421
- if (validFrom !== void 0) data.validFrom = parseDate(validFrom);
1422
- if (validUntil !== void 0) data.validUntil = parseDate(validUntil);
1423
- return data;
1424
- }
1425
- __name(toUpdateData, "toUpdateData");
1426
- function parseDate(value) {
1427
- if (!value) return null;
1428
- const date = new Date(value);
1429
- return Number.isNaN(date.getTime()) ? null : date;
1430
- }
1431
- __name(parseDate, "parseDate");
1432
-
1433
- // src/promo/module.ts
1434
- function _ts_decorate10(decorators, target, key, desc) {
1435
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1436
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1437
- 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;
1438
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1439
- }
1440
- __name(_ts_decorate10, "_ts_decorate");
1441
- var PromoCodesModule = class _PromoCodesModule {
1442
- static {
1443
- __name(this, "PromoCodesModule");
1444
- }
1445
- static forRoot(options) {
1446
- const includeCron = options.includeExpirerCron ?? true;
1447
- const includePublic = options.includePublicController ?? true;
1448
- const controllers = includePublic ? [
1449
- PromoCodePublicController
1450
- ] : [];
1451
- if (options.adminController) {
1452
- controllers.push(buildPromoCodeAdminController(options.adminController.guards));
1453
- }
1454
- const providers = [
1455
- asProvider(PROMO_CODE_REPOSITORY_TOKEN, options.promoCodeRepository),
1456
- asProvider(PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN, options.redemptionRepository),
1457
- asProvider(PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN, options.validationLogRepository),
1458
- asProvider(PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN, options.firstTimeCustomerCheck),
1459
- asProvider(PROMO_SUBSCRIPTION_LOOKUP_TOKEN, options.subscriptionLookup),
1460
- asProvider(PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN, options.revenueAggregator),
1461
- asProvider(PROMO_TRANSACTION_RUNNER_TOKEN, options.transactionRunner),
1462
- {
1463
- provide: PROMO_SERVICE_CONFIG_TOKEN,
1464
- useValue: options.config ?? {}
1465
- },
1466
- PromoCodesService,
1467
- PromoCodeRateLimitGuard
1468
- ];
1469
- if (includeCron) {
1470
- providers.push(PromoCodeExpirer);
1471
- }
1472
- return {
1473
- module: _PromoCodesModule,
1474
- global: options.global ?? false,
1475
- imports: options.imports ?? [],
1476
- controllers,
1477
- providers,
1478
- exports: [
1479
- PromoCodesService,
1480
- PromoCodeRateLimitGuard,
1481
- PROMO_CODE_REPOSITORY_TOKEN,
1482
- PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN,
1483
- PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN
1484
- ]
1485
- };
1486
- }
1487
- };
1488
- PromoCodesModule = _ts_decorate10([
1489
- (0, import_common8.Module)({})
1490
- ], PromoCodesModule);
1491
- // Annotate the CommonJS export names for ESM import in node:
1492
- 0 && (module.exports = {
1493
- CreatePromoCodeDto,
1494
- PROMO_CODE_REDEMPTION_REPOSITORY_TOKEN,
1495
- PROMO_CODE_REPOSITORY_TOKEN,
1496
- PROMO_CODE_VALIDATION_LOG_REPOSITORY_TOKEN,
1497
- PROMO_FIRST_TIME_CUSTOMER_CHECK_TOKEN,
1498
- PROMO_REVENUE_DEDUCTION_AGGREGATOR_TOKEN,
1499
- PROMO_SERVICE_CONFIG_TOKEN,
1500
- PROMO_SUBSCRIPTION_LOOKUP_TOKEN,
1501
- PROMO_TRANSACTION_RUNNER_TOKEN,
1502
- PreviewPromoCodeDto,
1503
- PromoCodeExpirer,
1504
- PromoCodePublicController,
1505
- PromoCodeRateLimitGuard,
1506
- PromoCodesModule,
1507
- PromoCodesService,
1508
- UpdatePromoCodeDto,
1509
- addCycles,
1510
- buildLabel,
1511
- buildPromoCodeAdminController,
1512
- computeDiscountGross,
1513
- computeDiscountedGross,
1514
- computeIncludedVat,
1515
- computeRegularStartsAt,
1516
- grossFromNet,
1517
- hashIp,
1518
- ipFingerprint,
1519
- round2
1520
- });
1
+ 'use strict';
2
+ // Generated by scripts/build-cjs-stubs.mjs — do not edit.
3
+ // Thin re-export so every entry shares one set of class objects.
4
+ module.exports = require('../_entries.cjs').promoNs;