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