@saasicat/nest 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +53 -0
  3. package/dist/admin/index.cjs +1283 -0
  4. package/dist/admin/index.d.cts +225 -0
  5. package/dist/admin/index.d.ts +225 -0
  6. package/dist/admin/index.js +58 -0
  7. package/dist/admin-audit.service-9IqXMlZm.d.cts +30 -0
  8. package/dist/admin-audit.service-9IqXMlZm.d.ts +30 -0
  9. package/dist/admin-manifest.config-DyrQNT7M.d.cts +10 -0
  10. package/dist/admin-manifest.config-DyrQNT7M.d.ts +10 -0
  11. package/dist/aggregation-CJ3qQf92.d.cts +288 -0
  12. package/dist/aggregation-Dvz9e8X2.d.ts +288 -0
  13. package/dist/billing/index.cjs +5205 -0
  14. package/dist/billing/index.d.cts +1326 -0
  15. package/dist/billing/index.d.ts +1326 -0
  16. package/dist/billing/index.js +197 -0
  17. package/dist/catalog/index.cjs +6449 -0
  18. package/dist/catalog/index.d.cts +1073 -0
  19. package/dist/catalog/index.d.ts +1073 -0
  20. package/dist/catalog/index.js +158 -0
  21. package/dist/checkout-offer/index.cjs +807 -0
  22. package/dist/checkout-offer/index.d.cts +120 -0
  23. package/dist/checkout-offer/index.d.ts +120 -0
  24. package/dist/checkout-offer/index.js +20 -0
  25. package/dist/chunk-2FR6ZL7R.js +0 -0
  26. package/dist/chunk-2PNX2QL2.js +347 -0
  27. package/dist/chunk-2VNZYRDV.js +326 -0
  28. package/dist/chunk-7MLGHWKO.js +12 -0
  29. package/dist/chunk-ABB2CRAS.js +31 -0
  30. package/dist/chunk-AJ3EPELS.js +157 -0
  31. package/dist/chunk-E56W4U2P.js +90 -0
  32. package/dist/chunk-HRU3CP57.js +71 -0
  33. package/dist/chunk-L4SUWH5B.js +14 -0
  34. package/dist/chunk-M6CZQYPY.js +314 -0
  35. package/dist/chunk-MDIZUVIK.js +87 -0
  36. package/dist/chunk-MV6LW5UB.js +217 -0
  37. package/dist/chunk-NYLON2VC.js +6 -0
  38. package/dist/chunk-P6MYZMXQ.js +951 -0
  39. package/dist/chunk-Q53N43LQ.js +13 -0
  40. package/dist/chunk-QJVPRD3R.js +573 -0
  41. package/dist/chunk-RIGLQ4BV.js +3367 -0
  42. package/dist/chunk-SABTXESR.js +24 -0
  43. package/dist/chunk-SHUYVCID.js +6 -0
  44. package/dist/chunk-SYEVGPFC.js +686 -0
  45. package/dist/chunk-V7HUZ6UQ.js +5923 -0
  46. package/dist/chunk-WPUVJRIN.js +29 -0
  47. package/dist/chunk-XCZ3TTFK.js +22 -0
  48. package/dist/chunk-XSGU2Y2O.js +1611 -0
  49. package/dist/chunk-YNEW3L63.js +190 -0
  50. package/dist/chunk-YXGHPILG.js +614 -0
  51. package/dist/chunk-ZL4LLE2Z.js +465 -0
  52. package/dist/di-CcNeq9v-.d.cts +16 -0
  53. package/dist/di-CcNeq9v-.d.ts +16 -0
  54. package/dist/discovery/index.cjs +588 -0
  55. package/dist/discovery/index.d.cts +299 -0
  56. package/dist/discovery/index.d.ts +299 -0
  57. package/dist/discovery/index.js +49 -0
  58. package/dist/discovery.scanner-CUYLKlYT.d.cts +59 -0
  59. package/dist/discovery.scanner-CUYLKlYT.d.ts +59 -0
  60. package/dist/entitlement/index.cjs +805 -0
  61. package/dist/entitlement/index.d.cts +60 -0
  62. package/dist/entitlement/index.d.ts +60 -0
  63. package/dist/entitlement/index.js +57 -0
  64. package/dist/index.cjs +15918 -0
  65. package/dist/index.d.cts +85 -0
  66. package/dist/index.d.ts +85 -0
  67. package/dist/index.js +956 -0
  68. package/dist/plan-resolution-CFCoUkrE.d.cts +41 -0
  69. package/dist/plan-resolution-CFCoUkrE.d.ts +41 -0
  70. package/dist/platform/index.cjs +2772 -0
  71. package/dist/platform/index.d.cts +118 -0
  72. package/dist/platform/index.d.ts +118 -0
  73. package/dist/platform/index.js +37 -0
  74. package/dist/promo/index.cjs +995 -0
  75. package/dist/promo/index.d.cts +121 -0
  76. package/dist/promo/index.d.ts +121 -0
  77. package/dist/promo/index.js +58 -0
  78. package/dist/registration/index.cjs +1687 -0
  79. package/dist/registration/index.d.cts +444 -0
  80. package/dist/registration/index.d.ts +444 -0
  81. package/dist/registration/index.js +82 -0
  82. package/dist/saas-platform.module-CgZ2omgg.d.cts +180 -0
  83. package/dist/saas-platform.module-DT9TnCzk.d.ts +180 -0
  84. package/dist/service-DX8KbGXl.d.cts +109 -0
  85. package/dist/service-DX8KbGXl.d.ts +109 -0
  86. package/dist/subscription-contract/index.cjs +471 -0
  87. package/dist/subscription-contract/index.d.cts +17 -0
  88. package/dist/subscription-contract/index.d.ts +17 -0
  89. package/dist/subscription-contract/index.js +21 -0
  90. package/dist/subscription-contract.service--cm47ZJJ.d.cts +39 -0
  91. package/dist/subscription-contract.service--cm47ZJJ.d.ts +39 -0
  92. package/dist/testing/index.cjs +3942 -0
  93. package/dist/testing/index.d.cts +279 -0
  94. package/dist/testing/index.d.ts +279 -0
  95. package/dist/testing/index.js +1195 -0
  96. package/package.json +185 -0
@@ -0,0 +1,120 @@
1
+ import { CheckoutOfferRepository, BundleRepository, PlanRepository, CatalogEntryRepository, CheckoutOfferFilter, CheckoutOfferRow, CreateCheckoutOfferData, UpdateCheckoutOfferData, CheckoutOfferPriceBreakdown, CheckoutOfferLineItem, CheckoutOfferPromotionSnapshot, CheckoutOfferPromoCodeSnapshot } from '@saasicat/types';
2
+ import { Type, CanActivate, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
3
+ import { P as ProviderSpec } from '../di-CcNeq9v-.cjs';
4
+
5
+ declare class CheckoutOfferService {
6
+ private readonly repo;
7
+ private readonly bundles;
8
+ private readonly plans;
9
+ private readonly catalogEntries;
10
+ constructor(repo: CheckoutOfferRepository, bundles?: BundleRepository | null, plans?: PlanRepository | null, catalogEntries?: CatalogEntryRepository | null);
11
+ list(filter: CheckoutOfferFilter): Promise<CheckoutOfferRow[]>;
12
+ getById(id: string): Promise<CheckoutOfferRow>;
13
+ create(data: CreateCheckoutOfferData): Promise<CheckoutOfferRow>;
14
+ /** Customization in onboarding — only while the offer is `open`. */
15
+ update(id: string, data: UpdateCheckoutOfferData): Promise<CheckoutOfferRow>;
16
+ /**
17
+ * Freezes the offer (`status = 'consumed'`). Returns the final
18
+ * snapshot — the caller (registration/billing) writes it
19
+ * as `Subscription.packageSnapshot`.
20
+ */
21
+ consume(id: string): Promise<CheckoutOfferRow>;
22
+ /**
23
+ * #35 P6 — server-side requires validation: the dependencies of all
24
+ * features (plan ∪ selected bundles) must be covered within the
25
+ * selection, otherwise an offer would be created whose features cannot
26
+ * work at the tenant (an app-specific validateModuleDependencies check is
27
+ * thereby replaced on the platform side). The requires source are the
28
+ * curated FeatureCatalogEntries; without CatalogEntryRepository it is
29
+ * skipped (graceful — no requires data available).
30
+ */
31
+ private assertFeatureRequiresSatisfied;
32
+ /**
33
+ * Plan features preferably from the server SSOT (PlanRepository); fallback
34
+ * is the frozen featuresSnapshot of the plan LineItem (adapter without
35
+ * PlanRepository wiring).
36
+ */
37
+ private resolvePlanFeatures;
38
+ /** Bundle features from the BundleVersions; fallback featuresSnapshot. */
39
+ private resolveBundleFeatures;
40
+ private assertBundleVersionsStillBookable;
41
+ private normalizeCreateData;
42
+ private normalizeUpdateData;
43
+ private resolveLineItems;
44
+ private defaultPlanLineItem;
45
+ private assertOpen;
46
+ private isExpired;
47
+ private dateIsAfterNow;
48
+ private isValidUntilExpired;
49
+ }
50
+
51
+ declare function buildCheckoutOfferController(guards: Array<Type<CanActivate>>): Type;
52
+
53
+ interface CheckoutOfferControllerConfig {
54
+ /** Class-level guards; `[]` for auth-free public endpoints. */
55
+ guards: Array<Type<CanActivate>>;
56
+ }
57
+ interface CheckoutOfferModuleOptions {
58
+ checkoutOfferRepository: ProviderSpec<CheckoutOfferRepository>;
59
+ /**
60
+ * Optional for V3 revalidation on consume: if set, the service checks
61
+ * whether referenced bundle versions are still bookable. Also provides
62
+ * the bundle features for the requires validation (#35 P6).
63
+ */
64
+ bundleRepository?: ProviderSpec<BundleRepository>;
65
+ /**
66
+ * Optional for the requires validation (#35 P6): plan features of the
67
+ * chosen plan version. Without wiring, falls back to the featuresSnapshot
68
+ * of the plan line item.
69
+ */
70
+ planRepository?: ProviderSpec<PlanRepository>;
71
+ /**
72
+ * Optional for the requires validation (#35 P6): requires source =
73
+ * curated FeatureCatalogEntries. Without wiring, the validation is
74
+ * skipped (graceful).
75
+ */
76
+ catalogEntryRepository?: ProviderSpec<CatalogEntryRepository>;
77
+ /** Controller mount for `/public/checkout-offer`. Omitted = service only. */
78
+ controller?: CheckoutOfferControllerConfig;
79
+ imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
80
+ extraProviders?: Provider[];
81
+ global?: boolean;
82
+ }
83
+ declare class CheckoutOfferModule {
84
+ static forRoot(options: CheckoutOfferModuleOptions): DynamicModule;
85
+ }
86
+
87
+ /** Repository for `checkout_offers`. */
88
+ declare const CHECKOUT_OFFER_REPOSITORY_TOKEN: unique symbol;
89
+
90
+ declare class CreateCheckoutOfferDto {
91
+ projectKey: string;
92
+ planKey: string;
93
+ planVersionId?: string | null;
94
+ billingCycle: 'monthly' | 'yearly';
95
+ promotionId?: string | null;
96
+ promoCode?: string | null;
97
+ bundles?: string[];
98
+ bundleVersionIds?: string[];
99
+ priceBreakdown: CheckoutOfferPriceBreakdown;
100
+ lineItems?: CheckoutOfferLineItem[];
101
+ promotionSnapshots?: CheckoutOfferPromotionSnapshot[];
102
+ promoCodeSnapshot?: CheckoutOfferPromoCodeSnapshot | null;
103
+ locale?: string;
104
+ validUntil?: string | null;
105
+ }
106
+ declare class UpdateCheckoutOfferDto {
107
+ billingCycle?: 'monthly' | 'yearly';
108
+ promotionId?: string | null;
109
+ promoCode?: string | null;
110
+ bundles?: string[];
111
+ bundleVersionIds?: string[];
112
+ priceBreakdown?: CheckoutOfferPriceBreakdown;
113
+ lineItems?: CheckoutOfferLineItem[];
114
+ promotionSnapshots?: CheckoutOfferPromotionSnapshot[];
115
+ promoCodeSnapshot?: CheckoutOfferPromoCodeSnapshot | null;
116
+ locale?: string;
117
+ validUntil?: string | null;
118
+ }
119
+
120
+ export { CHECKOUT_OFFER_REPOSITORY_TOKEN, type CheckoutOfferControllerConfig, CheckoutOfferModule, type CheckoutOfferModuleOptions, CheckoutOfferService, CreateCheckoutOfferDto, UpdateCheckoutOfferDto, buildCheckoutOfferController };
@@ -0,0 +1,120 @@
1
+ import { CheckoutOfferRepository, BundleRepository, PlanRepository, CatalogEntryRepository, CheckoutOfferFilter, CheckoutOfferRow, CreateCheckoutOfferData, UpdateCheckoutOfferData, CheckoutOfferPriceBreakdown, CheckoutOfferLineItem, CheckoutOfferPromotionSnapshot, CheckoutOfferPromoCodeSnapshot } from '@saasicat/types';
2
+ import { Type, CanActivate, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
3
+ import { P as ProviderSpec } from '../di-CcNeq9v-.js';
4
+
5
+ declare class CheckoutOfferService {
6
+ private readonly repo;
7
+ private readonly bundles;
8
+ private readonly plans;
9
+ private readonly catalogEntries;
10
+ constructor(repo: CheckoutOfferRepository, bundles?: BundleRepository | null, plans?: PlanRepository | null, catalogEntries?: CatalogEntryRepository | null);
11
+ list(filter: CheckoutOfferFilter): Promise<CheckoutOfferRow[]>;
12
+ getById(id: string): Promise<CheckoutOfferRow>;
13
+ create(data: CreateCheckoutOfferData): Promise<CheckoutOfferRow>;
14
+ /** Customization in onboarding — only while the offer is `open`. */
15
+ update(id: string, data: UpdateCheckoutOfferData): Promise<CheckoutOfferRow>;
16
+ /**
17
+ * Freezes the offer (`status = 'consumed'`). Returns the final
18
+ * snapshot — the caller (registration/billing) writes it
19
+ * as `Subscription.packageSnapshot`.
20
+ */
21
+ consume(id: string): Promise<CheckoutOfferRow>;
22
+ /**
23
+ * #35 P6 — server-side requires validation: the dependencies of all
24
+ * features (plan ∪ selected bundles) must be covered within the
25
+ * selection, otherwise an offer would be created whose features cannot
26
+ * work at the tenant (an app-specific validateModuleDependencies check is
27
+ * thereby replaced on the platform side). The requires source are the
28
+ * curated FeatureCatalogEntries; without CatalogEntryRepository it is
29
+ * skipped (graceful — no requires data available).
30
+ */
31
+ private assertFeatureRequiresSatisfied;
32
+ /**
33
+ * Plan features preferably from the server SSOT (PlanRepository); fallback
34
+ * is the frozen featuresSnapshot of the plan LineItem (adapter without
35
+ * PlanRepository wiring).
36
+ */
37
+ private resolvePlanFeatures;
38
+ /** Bundle features from the BundleVersions; fallback featuresSnapshot. */
39
+ private resolveBundleFeatures;
40
+ private assertBundleVersionsStillBookable;
41
+ private normalizeCreateData;
42
+ private normalizeUpdateData;
43
+ private resolveLineItems;
44
+ private defaultPlanLineItem;
45
+ private assertOpen;
46
+ private isExpired;
47
+ private dateIsAfterNow;
48
+ private isValidUntilExpired;
49
+ }
50
+
51
+ declare function buildCheckoutOfferController(guards: Array<Type<CanActivate>>): Type;
52
+
53
+ interface CheckoutOfferControllerConfig {
54
+ /** Class-level guards; `[]` for auth-free public endpoints. */
55
+ guards: Array<Type<CanActivate>>;
56
+ }
57
+ interface CheckoutOfferModuleOptions {
58
+ checkoutOfferRepository: ProviderSpec<CheckoutOfferRepository>;
59
+ /**
60
+ * Optional for V3 revalidation on consume: if set, the service checks
61
+ * whether referenced bundle versions are still bookable. Also provides
62
+ * the bundle features for the requires validation (#35 P6).
63
+ */
64
+ bundleRepository?: ProviderSpec<BundleRepository>;
65
+ /**
66
+ * Optional for the requires validation (#35 P6): plan features of the
67
+ * chosen plan version. Without wiring, falls back to the featuresSnapshot
68
+ * of the plan line item.
69
+ */
70
+ planRepository?: ProviderSpec<PlanRepository>;
71
+ /**
72
+ * Optional for the requires validation (#35 P6): requires source =
73
+ * curated FeatureCatalogEntries. Without wiring, the validation is
74
+ * skipped (graceful).
75
+ */
76
+ catalogEntryRepository?: ProviderSpec<CatalogEntryRepository>;
77
+ /** Controller mount for `/public/checkout-offer`. Omitted = service only. */
78
+ controller?: CheckoutOfferControllerConfig;
79
+ imports?: Array<Type<unknown> | DynamicModule | Promise<DynamicModule> | ForwardReference>;
80
+ extraProviders?: Provider[];
81
+ global?: boolean;
82
+ }
83
+ declare class CheckoutOfferModule {
84
+ static forRoot(options: CheckoutOfferModuleOptions): DynamicModule;
85
+ }
86
+
87
+ /** Repository for `checkout_offers`. */
88
+ declare const CHECKOUT_OFFER_REPOSITORY_TOKEN: unique symbol;
89
+
90
+ declare class CreateCheckoutOfferDto {
91
+ projectKey: string;
92
+ planKey: string;
93
+ planVersionId?: string | null;
94
+ billingCycle: 'monthly' | 'yearly';
95
+ promotionId?: string | null;
96
+ promoCode?: string | null;
97
+ bundles?: string[];
98
+ bundleVersionIds?: string[];
99
+ priceBreakdown: CheckoutOfferPriceBreakdown;
100
+ lineItems?: CheckoutOfferLineItem[];
101
+ promotionSnapshots?: CheckoutOfferPromotionSnapshot[];
102
+ promoCodeSnapshot?: CheckoutOfferPromoCodeSnapshot | null;
103
+ locale?: string;
104
+ validUntil?: string | null;
105
+ }
106
+ declare class UpdateCheckoutOfferDto {
107
+ billingCycle?: 'monthly' | 'yearly';
108
+ promotionId?: string | null;
109
+ promoCode?: string | null;
110
+ bundles?: string[];
111
+ bundleVersionIds?: string[];
112
+ priceBreakdown?: CheckoutOfferPriceBreakdown;
113
+ lineItems?: CheckoutOfferLineItem[];
114
+ promotionSnapshots?: CheckoutOfferPromotionSnapshot[];
115
+ promoCodeSnapshot?: CheckoutOfferPromoCodeSnapshot | null;
116
+ locale?: string;
117
+ validUntil?: string | null;
118
+ }
119
+
120
+ export { CHECKOUT_OFFER_REPOSITORY_TOKEN, type CheckoutOfferControllerConfig, CheckoutOfferModule, type CheckoutOfferModuleOptions, CheckoutOfferService, CreateCheckoutOfferDto, UpdateCheckoutOfferDto, buildCheckoutOfferController };
@@ -0,0 +1,20 @@
1
+ import {
2
+ CHECKOUT_OFFER_REPOSITORY_TOKEN,
3
+ CheckoutOfferModule,
4
+ CheckoutOfferService,
5
+ CreateCheckoutOfferDto,
6
+ UpdateCheckoutOfferDto,
7
+ buildCheckoutOfferController
8
+ } from "../chunk-SYEVGPFC.js";
9
+ import "../chunk-MDIZUVIK.js";
10
+ import "../chunk-XCZ3TTFK.js";
11
+ import "../chunk-SABTXESR.js";
12
+ import "../chunk-SHUYVCID.js";
13
+ export {
14
+ CHECKOUT_OFFER_REPOSITORY_TOKEN,
15
+ CheckoutOfferModule,
16
+ CheckoutOfferService,
17
+ CreateCheckoutOfferDto,
18
+ UpdateCheckoutOfferDto,
19
+ buildCheckoutOfferController
20
+ };
File without changes
@@ -0,0 +1,347 @@
1
+ import {
2
+ appendImplicitDiscountLineItem
3
+ } from "./chunk-MDIZUVIK.js";
4
+ import {
5
+ SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN
6
+ } from "./chunk-NYLON2VC.js";
7
+ import {
8
+ asProvider
9
+ } from "./chunk-SABTXESR.js";
10
+ import {
11
+ __name
12
+ } from "./chunk-SHUYVCID.js";
13
+
14
+ // src/subscription-contract/subscription-contract.service.ts
15
+ import { ConflictException, Inject, Injectable, NotFoundException, UnprocessableEntityException } from "@nestjs/common";
16
+ function _ts_decorate(decorators, target, key, desc) {
17
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
18
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
19
+ 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;
20
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
21
+ }
22
+ __name(_ts_decorate, "_ts_decorate");
23
+ function _ts_metadata(k, v) {
24
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
25
+ }
26
+ __name(_ts_metadata, "_ts_metadata");
27
+ function _ts_param(paramIndex, decorator) {
28
+ return function(target, key) {
29
+ decorator(target, key, paramIndex);
30
+ };
31
+ }
32
+ __name(_ts_param, "_ts_param");
33
+ var SubscriptionContractService = class {
34
+ static {
35
+ __name(this, "SubscriptionContractService");
36
+ }
37
+ repo;
38
+ constructor(repo) {
39
+ this.repo = repo;
40
+ }
41
+ list(filter) {
42
+ return this.repo.list(filter);
43
+ }
44
+ async getById(contractId) {
45
+ const row = await this.repo.findById(contractId);
46
+ if (!row) {
47
+ throw new NotFoundException(`SubscriptionContract '${contractId}' nicht gefunden`);
48
+ }
49
+ return row;
50
+ }
51
+ async findActiveByTenantId(tenantId, asOf = /* @__PURE__ */ new Date()) {
52
+ return this.repo.findActiveByTenantId(tenantId, asOf);
53
+ }
54
+ async getActiveInvoiceSnapshotForTenant(tenantId, asOf = /* @__PURE__ */ new Date()) {
55
+ const contract = await this.repo.findActiveByTenantId(tenantId, asOf);
56
+ if (!contract) {
57
+ throw new NotFoundException(`Kein aktiver SubscriptionContract f\xFCr Tenant ${tenantId}`);
58
+ }
59
+ return subscriptionContractToInvoiceSnapshot(contract);
60
+ }
61
+ async create(data) {
62
+ this.assertCreateData(data);
63
+ return this.repo.create(this.cloneCreateData(data));
64
+ }
65
+ async terminate(contractId, data) {
66
+ const existing = await this.getById(contractId);
67
+ this.assertTerminable(existing, data);
68
+ return this.repo.terminate(contractId, data);
69
+ }
70
+ async replaceActiveContract(tenantId, data, terminateAt) {
71
+ const previous = await this.repo.findActiveByTenantId(tenantId, terminateAt);
72
+ if (previous) {
73
+ await this.terminate(previous.id, {
74
+ effectiveUntil: terminateAt,
75
+ status: "superseded"
76
+ });
77
+ }
78
+ const next = await this.create({
79
+ ...data,
80
+ tenantId,
81
+ effectiveFrom: data.effectiveFrom ?? terminateAt
82
+ });
83
+ return {
84
+ previous,
85
+ next
86
+ };
87
+ }
88
+ createDataFromOffer(offer, options) {
89
+ if (offer.status !== "consumed") {
90
+ throw new ConflictException(`CheckoutOffer '${offer.id}' muss vor Contract-Erstellung consumed sein`);
91
+ }
92
+ const lineItems = this.lineItemsFromOffer(offer);
93
+ return {
94
+ projectKey: offer.projectKey,
95
+ tenantId: options.tenantId,
96
+ status: options.status ?? "active",
97
+ effectiveFrom: options.effectiveFrom,
98
+ effectiveUntil: options.effectiveUntil ?? null,
99
+ originalOfferId: offer.id,
100
+ originalPlanVersionId: offer.planVersionId,
101
+ originalBundleVersionIds: [
102
+ ...offer.bundleVersionIds ?? []
103
+ ],
104
+ entitlementSnapshot: options.entitlementSnapshot ?? null,
105
+ priceSnapshot: this.priceSnapshotFromOffer(offer),
106
+ promotionSnapshots: [
107
+ ...offer.promotionSnapshots ?? []
108
+ ],
109
+ promoCodeSnapshots: offer.promoCodeSnapshot ? [
110
+ offer.promoCodeSnapshot
111
+ ] : [],
112
+ termsSnapshot: options.termsSnapshot ?? null,
113
+ lineItems
114
+ };
115
+ }
116
+ async createFromOffer(offer, options) {
117
+ return this.create(this.createDataFromOffer(offer, options));
118
+ }
119
+ lineItemsFromOffer(offer) {
120
+ const source = offer.lineItems ?? [];
121
+ if (source.length === 0) {
122
+ throw new UnprocessableEntityException({
123
+ code: "CHECKOUT_OFFER_LINE_ITEMS_REQUIRED",
124
+ message: "Aus einem CheckoutOffer kann nur ein Contract entstehen, wenn LineItems eingefroren sind."
125
+ });
126
+ }
127
+ return appendImplicitDiscountLineItem({
128
+ billingCycle: offer.billingCycle,
129
+ priceBreakdown: offer.priceBreakdown,
130
+ lineItems: source,
131
+ promotionSnapshots: offer.promotionSnapshots ?? [],
132
+ promoCodeSnapshot: offer.promoCodeSnapshot ?? null
133
+ }).map((item) => this.offerLineItemToContractLineItem(item));
134
+ }
135
+ offerLineItemToContractLineItem(item) {
136
+ return {
137
+ kind: item.kind,
138
+ sourceKey: item.sourceKey,
139
+ sourceVersionId: item.sourceVersionId ?? null,
140
+ titleSnapshot: item.titleSnapshot,
141
+ descriptionSnapshot: item.descriptionSnapshot ?? null,
142
+ quantity: item.quantity,
143
+ unit: item.unit ?? null,
144
+ priceNet: item.priceNet,
145
+ priceGross: item.priceGross,
146
+ billingCycle: item.billingCycle,
147
+ minimumTermUntil: this.parseOptionalDate(item.minimumTermUntil, "lineItems.minimumTermUntil"),
148
+ featuresSnapshot: [
149
+ ...item.featuresSnapshot ?? []
150
+ ],
151
+ quotaEffectsSnapshot: {
152
+ ...item.quotaEffectsSnapshot ?? {}
153
+ },
154
+ metadata: item.metadata ?? null
155
+ };
156
+ }
157
+ parseOptionalDate(value, field) {
158
+ if (value === null || value === void 0) return null;
159
+ const date = value instanceof Date ? new Date(value) : new Date(value);
160
+ if (Number.isNaN(date.getTime())) {
161
+ throw new UnprocessableEntityException({
162
+ code: "SUBSCRIPTION_CONTRACT_INVALID_DATE",
163
+ message: `${field} muss ein g\xFCltiges Datum sein.`
164
+ });
165
+ }
166
+ return date;
167
+ }
168
+ priceSnapshotFromOffer(offer) {
169
+ const breakdown = offer.priceBreakdown;
170
+ return {
171
+ currency: breakdown.currency,
172
+ billingCycle: breakdown.billingCycle,
173
+ subtotalNet: breakdown.regularNet,
174
+ discountNet: Math.max(0, breakdown.regularNet - breakdown.effectiveNet),
175
+ totalNet: breakdown.effectiveNet,
176
+ vatRate: breakdown.vatRate,
177
+ totalGross: breakdown.effectiveGross
178
+ };
179
+ }
180
+ assertCreateData(data) {
181
+ if (data.lineItems.length === 0) {
182
+ throw new UnprocessableEntityException({
183
+ code: "SUBSCRIPTION_CONTRACT_LINE_ITEMS_REQUIRED",
184
+ message: "SubscriptionContract ben\xF6tigt mindestens eine LineItem."
185
+ });
186
+ }
187
+ if (data.effectiveUntil && data.effectiveUntil <= data.effectiveFrom) {
188
+ throw new UnprocessableEntityException({
189
+ code: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW",
190
+ message: "effectiveUntil muss nach effectiveFrom liegen."
191
+ });
192
+ }
193
+ const planLineItems = data.lineItems.filter((item) => item.kind === "plan");
194
+ if (planLineItems.length !== 1) {
195
+ throw new UnprocessableEntityException({
196
+ code: "SUBSCRIPTION_CONTRACT_PLAN_LINE_ITEM_REQUIRED",
197
+ message: "SubscriptionContract ben\xF6tigt genau eine Plan-Grundposition."
198
+ });
199
+ }
200
+ }
201
+ assertTerminable(existing, data) {
202
+ if (existing.status === "terminated" || existing.status === "superseded") {
203
+ throw new ConflictException(`SubscriptionContract '${existing.id}' ist bereits geschlossen`);
204
+ }
205
+ if (data.effectiveUntil <= existing.effectiveFrom) {
206
+ throw new UnprocessableEntityException({
207
+ code: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START",
208
+ message: "effectiveUntil muss nach effectiveFrom des Contracts liegen."
209
+ });
210
+ }
211
+ }
212
+ cloneCreateData(data) {
213
+ return {
214
+ ...data,
215
+ originalBundleVersionIds: [
216
+ ...data.originalBundleVersionIds ?? []
217
+ ],
218
+ promotionSnapshots: [
219
+ ...data.promotionSnapshots ?? []
220
+ ],
221
+ promoCodeSnapshots: [
222
+ ...data.promoCodeSnapshots ?? []
223
+ ],
224
+ termsSnapshot: data.termsSnapshot ? {
225
+ ...data.termsSnapshot
226
+ } : null,
227
+ lineItems: data.lineItems.map((item) => this.cloneLineItemData(item))
228
+ };
229
+ }
230
+ cloneLineItemData(item) {
231
+ return {
232
+ ...item,
233
+ featuresSnapshot: [
234
+ ...item.featuresSnapshot
235
+ ],
236
+ quotaEffectsSnapshot: {
237
+ ...item.quotaEffectsSnapshot
238
+ },
239
+ metadata: item.metadata ? {
240
+ ...item.metadata
241
+ } : null
242
+ };
243
+ }
244
+ };
245
+ SubscriptionContractService = _ts_decorate([
246
+ Injectable(),
247
+ _ts_param(0, Inject(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN)),
248
+ _ts_metadata("design:type", Function),
249
+ _ts_metadata("design:paramtypes", [
250
+ typeof SubscriptionContractRepository === "undefined" ? Object : SubscriptionContractRepository
251
+ ])
252
+ ], SubscriptionContractService);
253
+ function contractLineItemToInvoiceLineItem(item) {
254
+ return {
255
+ sourceContractLineItemId: item.id,
256
+ sourceKey: item.sourceKey,
257
+ sourceVersionId: item.sourceVersionId,
258
+ kind: item.kind,
259
+ title: item.titleSnapshot,
260
+ description: item.descriptionSnapshot,
261
+ quantity: item.quantity,
262
+ unit: item.unit,
263
+ priceNet: item.priceNet,
264
+ priceGross: item.priceGross,
265
+ billingCycle: item.billingCycle,
266
+ minimumTermUntil: item.minimumTermUntil,
267
+ metadata: item.metadata
268
+ };
269
+ }
270
+ __name(contractLineItemToInvoiceLineItem, "contractLineItemToInvoiceLineItem");
271
+ function subscriptionContractToInvoiceSnapshot(contract) {
272
+ return {
273
+ contractId: contract.id,
274
+ projectKey: contract.projectKey,
275
+ tenantId: contract.tenantId,
276
+ originalOfferId: contract.originalOfferId,
277
+ currency: contract.priceSnapshot.currency,
278
+ billingCycle: contract.priceSnapshot.billingCycle,
279
+ effectiveFrom: contract.effectiveFrom,
280
+ effectiveUntil: contract.effectiveUntil,
281
+ subtotalNet: contract.priceSnapshot.subtotalNet,
282
+ discountNet: contract.priceSnapshot.discountNet,
283
+ totalNet: contract.priceSnapshot.totalNet,
284
+ vatRate: contract.priceSnapshot.vatRate,
285
+ totalGross: contract.priceSnapshot.totalGross,
286
+ lineItems: sortContractLineItemsForInvoice(contract.lineItems).map(contractLineItemToInvoiceLineItem)
287
+ };
288
+ }
289
+ __name(subscriptionContractToInvoiceSnapshot, "subscriptionContractToInvoiceSnapshot");
290
+ function sortContractLineItemsForInvoice(lineItems) {
291
+ return [
292
+ ...lineItems
293
+ ].sort((a, b) => lineItemKindPriority(a.kind) - lineItemKindPriority(b.kind) || a.createdAt.getTime() - b.createdAt.getTime() || a.id.localeCompare(b.id));
294
+ }
295
+ __name(sortContractLineItemsForInvoice, "sortContractLineItemsForInvoice");
296
+ function lineItemKindPriority(kind) {
297
+ switch (kind) {
298
+ case "plan":
299
+ return 10;
300
+ case "bundle":
301
+ return 20;
302
+ case "discount":
303
+ return 90;
304
+ }
305
+ }
306
+ __name(lineItemKindPriority, "lineItemKindPriority");
307
+
308
+ // src/subscription-contract/subscription-contract.module.ts
309
+ import { Module } from "@nestjs/common";
310
+ function _ts_decorate2(decorators, target, key, desc) {
311
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
312
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
313
+ 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;
314
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
315
+ }
316
+ __name(_ts_decorate2, "_ts_decorate");
317
+ var SubscriptionContractModule = class _SubscriptionContractModule {
318
+ static {
319
+ __name(this, "SubscriptionContractModule");
320
+ }
321
+ static forRoot(options) {
322
+ return {
323
+ module: _SubscriptionContractModule,
324
+ global: options.global ?? false,
325
+ providers: [
326
+ asProvider(SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN, options.subscriptionContractRepository),
327
+ SubscriptionContractService,
328
+ ...options.extraProviders ?? []
329
+ ],
330
+ exports: [
331
+ SubscriptionContractService,
332
+ SUBSCRIPTION_CONTRACT_REPOSITORY_TOKEN
333
+ ]
334
+ };
335
+ }
336
+ };
337
+ SubscriptionContractModule = _ts_decorate2([
338
+ Module({})
339
+ ], SubscriptionContractModule);
340
+
341
+ export {
342
+ SubscriptionContractService,
343
+ contractLineItemToInvoiceLineItem,
344
+ subscriptionContractToInvoiceSnapshot,
345
+ sortContractLineItemsForInvoice,
346
+ SubscriptionContractModule
347
+ };