@saasicat/nest 0.8.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.
- package/dist/_entries.cjs +17678 -0
- package/dist/admin/index.cjs +4 -1274
- package/dist/admin/index.d.cts +10 -171
- package/dist/admin/index.d.ts +10 -171
- package/dist/admin/index.js +13 -4
- package/dist/admin-resources.module-BZN_yBFj.d.cts +40 -0
- package/dist/admin-resources.module-Cms8MY6h.d.ts +40 -0
- package/dist/admin-stats.module-15ac5KY3.d.ts +201 -0
- package/dist/admin-stats.module-v4SVvWzj.d.cts +201 -0
- package/dist/billing/index.cjs +4 -5128
- package/dist/billing/index.d.cts +8 -719
- package/dist/billing/index.d.ts +8 -719
- package/dist/billing/index.js +7 -10
- package/dist/catalog/index.cjs +4 -5624
- package/dist/catalog/index.d.cts +7 -427
- package/dist/catalog/index.d.ts +7 -427
- package/dist/catalog/index.js +14 -12
- package/dist/catalog.module-BKObceo0.d.ts +103 -0
- package/dist/catalog.module-Dw2Hrbyu.d.cts +103 -0
- package/dist/checkout-offer/index.cjs +4 -807
- package/dist/checkout-offer/index.d.cts +5 -84
- package/dist/checkout-offer/index.d.ts +5 -84
- package/dist/checkout-offer/index.js +3 -2
- package/dist/checkout-offer.module-Y0hDOVrq.d.ts +85 -0
- package/dist/checkout-offer.module-ZYF5IsDY.d.cts +85 -0
- package/dist/{chunk-5DDCSGZC.js → chunk-33TP26MR.js} +144 -356
- package/dist/{chunk-CK54YKVK.js → chunk-75JABGZK.js} +16 -16
- package/dist/{chunk-HRU3CP57.js → chunk-BOADBIGB.js} +1 -1
- package/dist/{chunk-ZLUGQE5D.js → chunk-E7AJ42AB.js} +8 -8
- package/dist/{chunk-6LP7CDS2.js → chunk-F3VV6EXG.js} +18 -13
- package/dist/{chunk-PLDHI6NA.js → chunk-FVT7KTUI.js} +258 -3
- package/dist/{chunk-RZU4HIAN.js → chunk-GBDUH6DO.js} +26 -23
- package/dist/{chunk-K3OPNVFR.js → chunk-HBFU5AN3.js} +5 -9
- package/dist/chunk-HWGIMNWR.js +0 -0
- package/dist/chunk-IRWZC3GC.js +755 -0
- package/dist/chunk-L4KTQ6CL.js +20 -0
- package/dist/{chunk-T5SXXAKG.js → chunk-LZHLGUQI.js} +1 -1
- package/dist/{chunk-YHHE4KDA.js → chunk-PUXLPUVH.js} +351 -21
- package/dist/chunk-TXDH4FUA.js +211 -0
- package/dist/chunk-UF53DWWR.js +0 -0
- package/dist/{chunk-AJ3EPELS.js → chunk-V34AA7C7.js} +1 -1
- package/dist/chunk-WJ6BMDL3.js +313 -0
- package/dist/{chunk-M6CZQYPY.js → chunk-Z3FWN3HJ.js} +2 -2
- package/dist/discovery/index.cjs +4 -588
- package/dist/discovery/index.js +3 -3
- package/dist/{enforce-quota.interceptor-cv7r80VM.d.cts → enforce-quota.interceptor-BhkX5nq2.d.cts} +127 -12
- package/dist/{enforce-quota.interceptor-Dj1Kusr5.d.ts → enforce-quota.interceptor-CcqZc0Yu.d.ts} +127 -12
- package/dist/entitlement/index.cjs +4 -780
- package/dist/entitlement/index.d.cts +3 -1
- package/dist/entitlement/index.d.ts +3 -1
- package/dist/entitlement/index.js +2 -3
- package/dist/index.cjs +4 -14975
- package/dist/index.d.cts +22 -54
- package/dist/index.d.ts +22 -54
- package/dist/index.js +88 -369
- package/dist/module-CSN8j8Tp.d.cts +39 -0
- package/dist/module-DgYJur6f.d.ts +39 -0
- package/dist/plan-versions.service-BB36Q1bg.d.ts +329 -0
- package/dist/plan-versions.service-CUR2Whyr.d.cts +329 -0
- package/dist/platform/index.cjs +4 -2779
- package/dist/platform/index.d.cts +50 -8
- package/dist/platform/index.d.ts +50 -8
- package/dist/platform/index.js +116 -12
- package/dist/promo/index.cjs +4 -995
- package/dist/promo/index.d.cts +48 -46
- package/dist/promo/index.d.ts +48 -46
- package/dist/promo/index.js +10 -2
- package/dist/rate-limit.guard-dntqAlfZ.d.cts +19 -0
- package/dist/rate-limit.guard-dntqAlfZ.d.ts +19 -0
- package/dist/registration/index.cjs +4 -1655
- package/dist/registration/index.js +1 -1
- package/dist/setup.service-BPwJ7My_.d.cts +45 -0
- package/dist/setup.service-D2mrdDDB.d.ts +45 -0
- package/dist/subscription-bundle-preview.service-BkJXHAeP.d.cts +438 -0
- package/dist/subscription-bundle-preview.service-DBbrE_cw.d.ts +438 -0
- package/dist/subscription-bundles.module-BQmoozm5.d.ts +291 -0
- package/dist/subscription-bundles.module-DCpV-Pb5.d.cts +291 -0
- package/dist/subscription-contract/index.cjs +4 -471
- package/dist/subscription-contract/index.d.cts +5 -13
- package/dist/subscription-contract/index.d.ts +5 -13
- package/dist/subscription-contract.module-CG9fPKAr.d.cts +14 -0
- package/dist/subscription-contract.module-CVebZzHR.d.ts +14 -0
- package/dist/testing/index.cjs +4 -3756
- package/dist/testing/index.d.cts +9 -4
- package/dist/testing/index.d.ts +9 -4
- package/dist/testing/index.js +13 -7
- package/package.json +5 -5
- package/dist/admin-manifest.config-DyrQNT7M.d.cts +0 -10
- package/dist/admin-manifest.config-DyrQNT7M.d.ts +0 -10
- package/dist/admin-manifest.service-Cp6uwvmt.d.cts +0 -33
- package/dist/admin-manifest.service-UGgzR1x-.d.ts +0 -33
- package/dist/chunk-52UTQRD3.js +0 -326
- package/dist/chunk-N3WANKSA.js +0 -20
- package/dist/chunk-Q53N43LQ.js +0 -13
- /package/dist/{chunk-2FR6ZL7R.js → chunk-DBZGV3HC.js} +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { SuperAdminProvisioningPort, SetupStatusResponse, SetupRequest, SetupResult, SetupConfirmMfaRequest, SetupConfirmMfaResponse } from '@saasicat/types';
|
|
3
|
+
import { P as ProviderSpec } from './di-CcNeq9v-.cjs';
|
|
4
|
+
import { j as MfaService } from './admin-stats.module-v4SVvWzj.cjs';
|
|
5
|
+
|
|
6
|
+
interface SetupModuleOptions {
|
|
7
|
+
/**
|
|
8
|
+
* App adapter for the existence check + creation of the first SUPER_ADMIN. A
|
|
9
|
+
* full `UserManagementPort` adapter (CLI) satisfies this contract as well, so
|
|
10
|
+
* it can be reused.
|
|
11
|
+
*/
|
|
12
|
+
provisioningPort: ProviderSpec<SuperAdminProvisioningPort>;
|
|
13
|
+
/** Env-var name of the setup token. Default `SETUP_TOKEN`. */
|
|
14
|
+
setupTokenEnvVar?: string;
|
|
15
|
+
/** Authenticator issuer for MFA enrollment. Default `SuperAdmin`. */
|
|
16
|
+
mfaIssuer?: string;
|
|
17
|
+
/** Register the module globally — default `false`. */
|
|
18
|
+
global?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare class SetupModule {
|
|
21
|
+
static forRoot(options: SetupModuleOptions): DynamicModule;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const SETUP_PROVISIONING_PORT_TOKEN: unique symbol;
|
|
25
|
+
declare const SETUP_CONFIG_TOKEN: unique symbol;
|
|
26
|
+
interface SetupConfig {
|
|
27
|
+
/** Env-var name of the setup token. Without the var set, setup is disabled. */
|
|
28
|
+
setupTokenEnvVar: string;
|
|
29
|
+
/** Authenticator issuer for the MFA enrollment in the wizard. */
|
|
30
|
+
mfaIssuer: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare class SetupService {
|
|
34
|
+
private readonly users;
|
|
35
|
+
private readonly config;
|
|
36
|
+
private readonly mfa;
|
|
37
|
+
constructor(users: SuperAdminProvisioningPort, config: SetupConfig, mfa: MfaService);
|
|
38
|
+
status(): Promise<SetupStatusResponse>;
|
|
39
|
+
setup(req: SetupRequest): Promise<SetupResult>;
|
|
40
|
+
confirmMfa(req: SetupConfirmMfaRequest): Promise<SetupConfirmMfaResponse>;
|
|
41
|
+
private assertToken;
|
|
42
|
+
private assertNoSuperAdmin;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { SetupService as S, SETUP_CONFIG_TOKEN as a, SETUP_PROVISIONING_PORT_TOKEN as b, type SetupConfig as c, SetupModule as d, type SetupModuleOptions as e };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { DynamicModule } from '@nestjs/common';
|
|
2
|
+
import { SuperAdminProvisioningPort, SetupStatusResponse, SetupRequest, SetupResult, SetupConfirmMfaRequest, SetupConfirmMfaResponse } from '@saasicat/types';
|
|
3
|
+
import { P as ProviderSpec } from './di-CcNeq9v-.js';
|
|
4
|
+
import { j as MfaService } from './admin-stats.module-15ac5KY3.js';
|
|
5
|
+
|
|
6
|
+
interface SetupModuleOptions {
|
|
7
|
+
/**
|
|
8
|
+
* App adapter for the existence check + creation of the first SUPER_ADMIN. A
|
|
9
|
+
* full `UserManagementPort` adapter (CLI) satisfies this contract as well, so
|
|
10
|
+
* it can be reused.
|
|
11
|
+
*/
|
|
12
|
+
provisioningPort: ProviderSpec<SuperAdminProvisioningPort>;
|
|
13
|
+
/** Env-var name of the setup token. Default `SETUP_TOKEN`. */
|
|
14
|
+
setupTokenEnvVar?: string;
|
|
15
|
+
/** Authenticator issuer for MFA enrollment. Default `SuperAdmin`. */
|
|
16
|
+
mfaIssuer?: string;
|
|
17
|
+
/** Register the module globally — default `false`. */
|
|
18
|
+
global?: boolean;
|
|
19
|
+
}
|
|
20
|
+
declare class SetupModule {
|
|
21
|
+
static forRoot(options: SetupModuleOptions): DynamicModule;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
declare const SETUP_PROVISIONING_PORT_TOKEN: unique symbol;
|
|
25
|
+
declare const SETUP_CONFIG_TOKEN: unique symbol;
|
|
26
|
+
interface SetupConfig {
|
|
27
|
+
/** Env-var name of the setup token. Without the var set, setup is disabled. */
|
|
28
|
+
setupTokenEnvVar: string;
|
|
29
|
+
/** Authenticator issuer for the MFA enrollment in the wizard. */
|
|
30
|
+
mfaIssuer: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare class SetupService {
|
|
34
|
+
private readonly users;
|
|
35
|
+
private readonly config;
|
|
36
|
+
private readonly mfa;
|
|
37
|
+
constructor(users: SuperAdminProvisioningPort, config: SetupConfig, mfa: MfaService);
|
|
38
|
+
status(): Promise<SetupStatusResponse>;
|
|
39
|
+
setup(req: SetupRequest): Promise<SetupResult>;
|
|
40
|
+
confirmMfa(req: SetupConfirmMfaRequest): Promise<SetupConfirmMfaResponse>;
|
|
41
|
+
private assertToken;
|
|
42
|
+
private assertNoSuperAdmin;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { SetupService as S, SETUP_CONFIG_TOKEN as a, SETUP_PROVISIONING_PORT_TOKEN as b, type SetupConfig as c, SetupModule as d, type SetupModuleOptions as e };
|
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
|
2
|
+
import { b as AuthGuardList, m as SelfServiceBlockedPlans, w as TrialProjectionPort, h as PendingPlanQueryPort, f as ContractFreezePort, l as SelfServiceBlockedBundles, u as TenantIdResolver, B as UserIdResolver, z as UserEmailResolver, a as AuditContextResolver } from './subscription-bundles.module-DCpV-Pb5.cjs';
|
|
3
|
+
import { PlanCatalog, SubscriptionUsagePort, UsageSnapshotPort, TenantSubscriptionWritePort, SubscriptionBundleRepository, BundleRepository, SubscriptionBundleView, SubscriptionBundleRecord, SubscriptionUsageRecord, OnboardingSelectionResponse, PlanRepository, CatalogEntryRepository, BundleVersionRow } from '@saasicat/types';
|
|
4
|
+
import { c as EntitlementService, a as EffectiveLimitsSnapshot, t as toEffectiveLimitsSnapshot } from './aggregation-CfI0bOsQ.cjs';
|
|
5
|
+
import { f as PromoCodesService } from './service-DX8KbGXl.cjs';
|
|
6
|
+
import { A as AdminAuditService } from './admin-audit.service-9IqXMlZm.cjs';
|
|
7
|
+
|
|
8
|
+
declare class ComposedTenantAuthGuard implements CanActivate {
|
|
9
|
+
private readonly guards;
|
|
10
|
+
constructor(guards?: AuthGuardList | null);
|
|
11
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare class TenantAdminGuard implements CanActivate {
|
|
15
|
+
canActivate(context: ExecutionContext): boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ProrationDto {
|
|
19
|
+
daysRemainingInPeriod: number;
|
|
20
|
+
daysInPeriod: number;
|
|
21
|
+
periodStart: Date;
|
|
22
|
+
periodEnd: Date;
|
|
23
|
+
currentPriceNet: number;
|
|
24
|
+
targetPriceNet: number;
|
|
25
|
+
/** Prorated surcharge/credit until end of period. Negative = credit. */
|
|
26
|
+
prorataDeltaNet: number;
|
|
27
|
+
}
|
|
28
|
+
interface ProrationInput {
|
|
29
|
+
periodStart: Date;
|
|
30
|
+
periodEnd: Date;
|
|
31
|
+
now: Date;
|
|
32
|
+
/** Previous period price (bundle add: 0 — something is only added). */
|
|
33
|
+
currentPriceNet: number;
|
|
34
|
+
targetPriceNet: number;
|
|
35
|
+
}
|
|
36
|
+
declare function computeProration(input: ProrationInput): ProrationDto;
|
|
37
|
+
|
|
38
|
+
type PlanChangeType = 'UPGRADE' | 'DOWNGRADE' | 'CYCLE_CHANGE' | 'NOOP';
|
|
39
|
+
interface PlanSnapshotDto {
|
|
40
|
+
id: string;
|
|
41
|
+
name: string;
|
|
42
|
+
monthlyNet: number | null;
|
|
43
|
+
yearlyNet: number | null;
|
|
44
|
+
quotas: Record<string, number>;
|
|
45
|
+
features: string[];
|
|
46
|
+
}
|
|
47
|
+
interface LimitsCheckRow {
|
|
48
|
+
used: number;
|
|
49
|
+
currentMax: number;
|
|
50
|
+
targetMax: number;
|
|
51
|
+
exceeded: boolean;
|
|
52
|
+
}
|
|
53
|
+
interface PlanChangePreviewIssue {
|
|
54
|
+
code: string;
|
|
55
|
+
message: string;
|
|
56
|
+
}
|
|
57
|
+
interface PlanChangePreviewDto {
|
|
58
|
+
changeType: PlanChangeType;
|
|
59
|
+
current: {
|
|
60
|
+
plan: PlanSnapshotDto;
|
|
61
|
+
billingCycle: string;
|
|
62
|
+
};
|
|
63
|
+
target: {
|
|
64
|
+
plan: PlanSnapshotDto;
|
|
65
|
+
billingCycle: string;
|
|
66
|
+
};
|
|
67
|
+
/** For upgrade/NOOP: immediately (null). Otherwise period end. */
|
|
68
|
+
effectiveAt: Date | null;
|
|
69
|
+
isImmediate: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Projected new trial end after the change (app trial logic, e.g.
|
|
72
|
+
* carry-over of the remaining time). `null` if no TrialProjectionPort is
|
|
73
|
+
* configured, the subscription is not in a trial, or nothing changes.
|
|
74
|
+
* The wizard uses this to show "regulär ab Ende der Testphase".
|
|
75
|
+
*/
|
|
76
|
+
projectedTrialEndsAt: Date | null;
|
|
77
|
+
proration: ProrationDto | null;
|
|
78
|
+
/** Map quotaKey → LimitsCheckRow across all quota dimensions from the current limit, target plan and usage. */
|
|
79
|
+
limitsCheck: Record<string, LimitsCheckRow>;
|
|
80
|
+
featuresLost: string[];
|
|
81
|
+
featuresGained: string[];
|
|
82
|
+
/** Hard prevention reasons — e.g. usage > target limit. */
|
|
83
|
+
blockers: PlanChangePreviewIssue[];
|
|
84
|
+
/** Non-blocking hints — e.g. feature loss. */
|
|
85
|
+
warnings: PlanChangePreviewIssue[];
|
|
86
|
+
}
|
|
87
|
+
interface PlanChangeContext {
|
|
88
|
+
/** Current period start time from the subscription, if present. */
|
|
89
|
+
currentPeriodStart: Date | null;
|
|
90
|
+
/** Current period end from the subscription, if present. */
|
|
91
|
+
currentPeriodEnd: Date | null;
|
|
92
|
+
/** TRIAL end, if status === 'TRIAL'. */
|
|
93
|
+
trialEndsAt: Date | null;
|
|
94
|
+
/** Subscription status (TRIAL/ACTIVE/...). */
|
|
95
|
+
status: string;
|
|
96
|
+
/** Current cycle of the subscription (for cycle-change classification). */
|
|
97
|
+
currentBillingCycle: string;
|
|
98
|
+
/** Current plan of the subscription. */
|
|
99
|
+
currentPlan: string;
|
|
100
|
+
/** Subscription start, if present (for the periodEndAfter fallback). */
|
|
101
|
+
startedAt: Date | null;
|
|
102
|
+
}
|
|
103
|
+
declare class PlanChangePreviewService {
|
|
104
|
+
private readonly catalog;
|
|
105
|
+
private readonly entitlements;
|
|
106
|
+
private readonly subscriptions;
|
|
107
|
+
private readonly usageSnapshot;
|
|
108
|
+
private readonly blockedPlans;
|
|
109
|
+
private readonly trialProjection;
|
|
110
|
+
constructor(catalog: PlanCatalog, entitlements: EntitlementService, subscriptions: SubscriptionUsagePort, usageSnapshot: UsageSnapshotPort, blockedPlans?: SelfServiceBlockedPlans | null, trialProjection?: TrialProjectionPort | null);
|
|
111
|
+
preview(tenantId: string, targetPlan: string, targetCycle: string, now?: Date): Promise<PlanChangePreviewDto>;
|
|
112
|
+
/** Like `preview`, but only the blocker list — for a server-side
|
|
113
|
+
* pre-check before the `changePlan` mutation (defense-in-depth). */
|
|
114
|
+
assertChangeAllowed(tenantId: string, targetPlan: string, targetCycle: string, now?: Date): Promise<PlanChangePreviewIssue[]>;
|
|
115
|
+
private classify;
|
|
116
|
+
/** Catalog order = rank. Non-marketed plans go to the end. */
|
|
117
|
+
private planRank;
|
|
118
|
+
private resolveEffectiveAt;
|
|
119
|
+
private computeProration;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
declare class PendingPlanMaterializationService {
|
|
123
|
+
private readonly query;
|
|
124
|
+
private readonly subscriptionWrite;
|
|
125
|
+
private readonly entitlements;
|
|
126
|
+
private readonly contractFreeze;
|
|
127
|
+
private readonly logger;
|
|
128
|
+
constructor(query: PendingPlanQueryPort, subscriptionWrite: TenantSubscriptionWritePort, entitlements: EntitlementService, contractFreeze?: ContractFreezePort | null);
|
|
129
|
+
materializeDuePlanChanges(now?: Date): Promise<{
|
|
130
|
+
applied: number;
|
|
131
|
+
}>;
|
|
132
|
+
private tryFreeze;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
declare class PreviewPlanChangeDto {
|
|
136
|
+
plan: string;
|
|
137
|
+
billingCycle: string;
|
|
138
|
+
}
|
|
139
|
+
declare class ChangePlanDto extends PreviewPlanChangeDto {
|
|
140
|
+
/** Immediate change (true) vs. change at period end (false/undefined). */
|
|
141
|
+
effectiveImmediately?: boolean;
|
|
142
|
+
}
|
|
143
|
+
declare class CancelSubscriptionDto {
|
|
144
|
+
/** true = cancel immediately (status CANCELED). false/undefined = at period end. */
|
|
145
|
+
immediately?: boolean;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
declare class CompleteOnboardingSubscriptionDto {
|
|
149
|
+
plan: string;
|
|
150
|
+
billingCycle: string;
|
|
151
|
+
promoCode?: string;
|
|
152
|
+
/**
|
|
153
|
+
* Optional — UUIDs of the BundleVersions that should be booked
|
|
154
|
+
* together with the plan (P11.7.3). Per bundle, the platform
|
|
155
|
+
* default minimum term (12 months) is set. Bundles are added
|
|
156
|
+
* best-effort **after** the plan change — an error on an individual
|
|
157
|
+
* bundle (e.g. incompatible with the chosen plan) lands as a
|
|
158
|
+
* warning in the response, without rolling back the plan change.
|
|
159
|
+
*/
|
|
160
|
+
bundleVersionIds?: string[];
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
interface SubscriptionBundleConfig {
|
|
164
|
+
/** Default minimum term in months on `add`. Default = 12. */
|
|
165
|
+
defaultMinimumTermMonths?: number;
|
|
166
|
+
}
|
|
167
|
+
interface AddBundleToSubscriptionInput {
|
|
168
|
+
subscriptionId: string;
|
|
169
|
+
bundleVersionId: string;
|
|
170
|
+
/** PlanKey of the current subscription for the plan-compatibility check. */
|
|
171
|
+
currentPlanKey: string;
|
|
172
|
+
/** Default = now (service time). */
|
|
173
|
+
startedAt?: Date;
|
|
174
|
+
/**
|
|
175
|
+
* Override for the minimum term (months). Default = config or
|
|
176
|
+
* 12. `0` explicitly means "no minimum term"
|
|
177
|
+
* (`minimumTermEndsAt = null`).
|
|
178
|
+
*/
|
|
179
|
+
minimumTermMonths?: number;
|
|
180
|
+
}
|
|
181
|
+
interface CancelBundleFromSubscriptionInput {
|
|
182
|
+
subscriptionBundleId: string;
|
|
183
|
+
/** Default = now. */
|
|
184
|
+
canceledAt?: Date;
|
|
185
|
+
/**
|
|
186
|
+
* Period end of the subscription from which the cancellation could take effect.
|
|
187
|
+
* Effective date = `max(currentPeriodEnd, minimumTermEndsAt)`.
|
|
188
|
+
* If not set, `canceledAt` is interpreted as the period end
|
|
189
|
+
* (= immediate effect, provided the minimum term has already expired).
|
|
190
|
+
*/
|
|
191
|
+
currentPeriodEnd?: Date;
|
|
192
|
+
}
|
|
193
|
+
declare class SubscriptionBundlesService {
|
|
194
|
+
private readonly repo;
|
|
195
|
+
private readonly bundles;
|
|
196
|
+
private readonly blockedBundles;
|
|
197
|
+
private readonly defaultMinTermMonths;
|
|
198
|
+
constructor(repo: SubscriptionBundleRepository, bundles: BundleRepository, config?: SubscriptionBundleConfig, blockedBundles?: SelfServiceBlockedBundles | null);
|
|
199
|
+
/** All bundle bookings of a subscription (for the "My Bundles" page). */
|
|
200
|
+
listForSubscription(subscriptionId: string): Promise<SubscriptionBundleView[]>;
|
|
201
|
+
addBundleToSubscription(input: AddBundleToSubscriptionInput): Promise<SubscriptionBundleRecord>;
|
|
202
|
+
cancelBundleFromSubscription(input: CancelBundleFromSubscriptionInput): Promise<SubscriptionBundleRecord>;
|
|
203
|
+
/**
|
|
204
|
+
* "Undo cancellation" — only as long as the cancellation is not yet effective
|
|
205
|
+
* (the bundle runs until `canceledEffectiveAt`). After that, re-booking is the way.
|
|
206
|
+
*/
|
|
207
|
+
reactivateBundle(subscriptionBundleId: string): Promise<SubscriptionBundleRecord>;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Effective date of a bundle cancellation:
|
|
211
|
+
* `max(currentPeriodEnd, minimumTermEndsAt)` — missing values fall back to
|
|
212
|
+
* `canceledAt` (= immediate effect). Shared between the
|
|
213
|
+
* cancellation mutation and the preview (#37).
|
|
214
|
+
*/
|
|
215
|
+
declare function resolveBundleCancelEffectiveAt(input: {
|
|
216
|
+
canceledAt: Date;
|
|
217
|
+
currentPeriodEnd: Date | null;
|
|
218
|
+
minimumTermEndsAt: Date | null;
|
|
219
|
+
}): Date;
|
|
220
|
+
/**
|
|
221
|
+
* Adds `months` to `date` and keeps the UTC day. Edge case
|
|
222
|
+
* 31.01 + 1 month → 28/29.02 (JS Date does this automatically by
|
|
223
|
+
* setMonth normalizing the day).
|
|
224
|
+
*/
|
|
225
|
+
declare function addMonths(date: Date, months: number): Date;
|
|
226
|
+
|
|
227
|
+
interface RequestLike {
|
|
228
|
+
user?: {
|
|
229
|
+
tenantId?: string;
|
|
230
|
+
sub?: string;
|
|
231
|
+
id?: string;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
interface UsageResponse {
|
|
235
|
+
plan: string;
|
|
236
|
+
effectivePlan: string;
|
|
237
|
+
billingCycle: string;
|
|
238
|
+
status: string;
|
|
239
|
+
isPilot: boolean;
|
|
240
|
+
pilotEndsAt: Date | null;
|
|
241
|
+
trialEndsAt: Date | null;
|
|
242
|
+
startedAt: Date | null;
|
|
243
|
+
currentPeriodStart: Date | null;
|
|
244
|
+
currentPeriodEnd: Date | null;
|
|
245
|
+
pendingPlan: string | null;
|
|
246
|
+
pendingBillingCycle: string | null;
|
|
247
|
+
pendingEffectiveAt: Date | null;
|
|
248
|
+
planVersion: SubscriptionUsageRecord['planVersion'];
|
|
249
|
+
pendingPlanVersion: SubscriptionUsageRecord['pendingPlanVersion'];
|
|
250
|
+
pendingPlanVersionEffectiveAt: Date | null;
|
|
251
|
+
pendingPlanVersionAccepted: boolean;
|
|
252
|
+
pendingPlanVersionAcceptedAt: Date | null;
|
|
253
|
+
limits: ReturnType<typeof toEffectiveLimitsSnapshot>;
|
|
254
|
+
usage: Record<string, number>;
|
|
255
|
+
/**
|
|
256
|
+
* P11.4 (METAMODELL §17a): Frozen package snapshot from the
|
|
257
|
+
* `CheckoutOffer` that was activated during onboarding. Read-only
|
|
258
|
+
* for the tenant self-service UI. `null` for subscriptions without
|
|
259
|
+
* a CheckoutOffer origin.
|
|
260
|
+
*/
|
|
261
|
+
packageSnapshot: unknown | null;
|
|
262
|
+
/** P11.4: Optional reference to the originating CheckoutOffer. */
|
|
263
|
+
checkoutOfferId: string | null;
|
|
264
|
+
}
|
|
265
|
+
declare class TenantBillingController {
|
|
266
|
+
private readonly entitlements;
|
|
267
|
+
private readonly planPreview;
|
|
268
|
+
private readonly subscriptionUsage;
|
|
269
|
+
private readonly usageSnapshot;
|
|
270
|
+
private readonly subscriptionWrite;
|
|
271
|
+
private readonly tenantIdResolver;
|
|
272
|
+
private readonly userIdResolver;
|
|
273
|
+
private readonly blockedPlans;
|
|
274
|
+
private readonly promoCodes;
|
|
275
|
+
private readonly auditService;
|
|
276
|
+
private readonly userEmailResolver;
|
|
277
|
+
private readonly auditContextResolver;
|
|
278
|
+
private readonly subscriptionBundles;
|
|
279
|
+
private readonly contractFreeze;
|
|
280
|
+
private readonly trialProjection;
|
|
281
|
+
constructor(entitlements: EntitlementService, planPreview: PlanChangePreviewService, subscriptionUsage: SubscriptionUsagePort, usageSnapshot: UsageSnapshotPort, subscriptionWrite: TenantSubscriptionWritePort, tenantIdResolver?: TenantIdResolver | null, userIdResolver?: UserIdResolver | null, blockedPlans?: SelfServiceBlockedPlans | null, promoCodes?: PromoCodesService | null, auditService?: AdminAuditService | null, userEmailResolver?: UserEmailResolver | null, auditContextResolver?: AuditContextResolver | null, subscriptionBundles?: SubscriptionBundlesService | null, contractFreeze?: ContractFreezePort | null, trialProjection?: TrialProjectionPort | null);
|
|
282
|
+
private readonly logger;
|
|
283
|
+
getEntitlement(req: RequestLike): Promise<EffectiveLimitsSnapshot>;
|
|
284
|
+
getUsage(req: RequestLike): Promise<UsageResponse>;
|
|
285
|
+
previewPlanChange(req: RequestLike, dto: PreviewPlanChangeDto): Promise<PlanChangePreviewDto>;
|
|
286
|
+
changePlan(req: RequestLike, dto: ChangePlanDto): Promise<{
|
|
287
|
+
plan: string;
|
|
288
|
+
billingCycle: string;
|
|
289
|
+
immediate: boolean;
|
|
290
|
+
pendingPlan?: undefined;
|
|
291
|
+
pendingBillingCycle?: undefined;
|
|
292
|
+
pendingEffectiveAt?: undefined;
|
|
293
|
+
} | {
|
|
294
|
+
plan: string;
|
|
295
|
+
billingCycle: string;
|
|
296
|
+
pendingPlan: string;
|
|
297
|
+
pendingBillingCycle: string;
|
|
298
|
+
pendingEffectiveAt: Date;
|
|
299
|
+
immediate: boolean;
|
|
300
|
+
}>;
|
|
301
|
+
completeOnboardingSubscription(req: RequestLike, dto: CompleteOnboardingSubscriptionDto): Promise<OnboardingSelectionResponse>;
|
|
302
|
+
acceptPendingPlanVersion(req: RequestLike): Promise<{
|
|
303
|
+
accepted: boolean;
|
|
304
|
+
acceptedAt: Date | null;
|
|
305
|
+
effectiveAt: Date | null;
|
|
306
|
+
idempotent: boolean;
|
|
307
|
+
}>;
|
|
308
|
+
cancelSubscription(req: RequestLike, dto: CancelSubscriptionDto): Promise<{
|
|
309
|
+
canceledAt: Date | null;
|
|
310
|
+
status: string;
|
|
311
|
+
immediate: boolean;
|
|
312
|
+
}>;
|
|
313
|
+
private requireTenantId;
|
|
314
|
+
private requireUserId;
|
|
315
|
+
private buildActor;
|
|
316
|
+
private resolveUserEmail;
|
|
317
|
+
private toResponseRedemption;
|
|
318
|
+
private collectPromoSkipReasons;
|
|
319
|
+
/**
|
|
320
|
+
* #18: contract freeze after the plan change — non-fatal, only outside of
|
|
321
|
+
* the trial (during a trial the trial entitlements apply, not the booked plan;
|
|
322
|
+
* the freeze happens on the transition to ACTIVE, i.e. on materialization).
|
|
323
|
+
* Without a configured `contractFreeze` hook the call is a no-op.
|
|
324
|
+
*/
|
|
325
|
+
private tryFreezeOnPlanChange;
|
|
326
|
+
/**
|
|
327
|
+
* Audit-log helper — writes best-effort, does not block the response path.
|
|
328
|
+
* If `AdminAuditService` is not injected (e.g. a minimal deploy without
|
|
329
|
+
* AdminModule), the call is silently discarded.
|
|
330
|
+
*/
|
|
331
|
+
private auditLog;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
interface SubscriptionBundlePreviewIssue {
|
|
335
|
+
code: string;
|
|
336
|
+
message: string;
|
|
337
|
+
}
|
|
338
|
+
/** Subscription context — the controller reads it from the SubscriptionUsagePort. */
|
|
339
|
+
interface SubscriptionBundlePreviewContext {
|
|
340
|
+
subscriptionId: string;
|
|
341
|
+
/** PlanKey of the current subscription (plan compatibility + redundancy source). */
|
|
342
|
+
currentPlanKey: string;
|
|
343
|
+
/** 'MONTHLY' | 'YEARLY' (port convention). */
|
|
344
|
+
billingCycle: string;
|
|
345
|
+
/** Subscription status (TRIAL/ACTIVE/...). No proration during TRIAL. */
|
|
346
|
+
status: string;
|
|
347
|
+
startedAt: Date | null;
|
|
348
|
+
currentPeriodStart: Date | null;
|
|
349
|
+
currentPeriodEnd: Date | null;
|
|
350
|
+
}
|
|
351
|
+
interface BundlePreviewSnapshot {
|
|
352
|
+
bundleKey: string;
|
|
353
|
+
label: string;
|
|
354
|
+
bundleVersionId: string;
|
|
355
|
+
features: string[];
|
|
356
|
+
quotas: Record<string, number>;
|
|
357
|
+
}
|
|
358
|
+
/** AK-13: feature is already paid for elsewhere — double-payment hint. */
|
|
359
|
+
interface RedundantFeatureHint {
|
|
360
|
+
featureKey: string;
|
|
361
|
+
coveredBy: 'PLAN' | 'BUNDLE';
|
|
362
|
+
/** planKey or bundleKey of the covering source. */
|
|
363
|
+
coveredByKey: string;
|
|
364
|
+
}
|
|
365
|
+
interface SubscriptionBundleAddPreviewDto {
|
|
366
|
+
action: 'add';
|
|
367
|
+
bundle: BundlePreviewSnapshot;
|
|
368
|
+
billingCycle: string;
|
|
369
|
+
/**
|
|
370
|
+
* Prorated amount until period end. `null` during TRIAL (no paid
|
|
371
|
+
* period yet) or without a list price for the cycle.
|
|
372
|
+
*/
|
|
373
|
+
proration: ProrationDto | null;
|
|
374
|
+
/** List price per follow-up period in the current cycle; null = no price maintained. */
|
|
375
|
+
nextPeriodPriceNet: number | null;
|
|
376
|
+
minimumTermMonths: number;
|
|
377
|
+
/** Projected minimum-term end from `now`; null = no minimum term. */
|
|
378
|
+
minimumTermEndsAt: Date | null;
|
|
379
|
+
redundantFeatures: RedundantFeatureHint[];
|
|
380
|
+
/**
|
|
381
|
+
* requires-features (#35) that neither the plan nor active bundles nor the
|
|
382
|
+
* bundle itself cover. Non-empty ⇒ blocker
|
|
383
|
+
* BUNDLE_FEATURE_DEPENDENCY_UNSATISFIED.
|
|
384
|
+
*/
|
|
385
|
+
missingRequires: string[];
|
|
386
|
+
blockers: SubscriptionBundlePreviewIssue[];
|
|
387
|
+
warnings: SubscriptionBundlePreviewIssue[];
|
|
388
|
+
}
|
|
389
|
+
interface SubscriptionBundleCancelPreviewDto {
|
|
390
|
+
action: 'cancel';
|
|
391
|
+
subscriptionBundleId: string;
|
|
392
|
+
bundle: BundlePreviewSnapshot;
|
|
393
|
+
billingCycle: string;
|
|
394
|
+
/** Effective date = max(currentPeriodEnd, minimumTermEndsAt). */
|
|
395
|
+
effectiveAt: Date;
|
|
396
|
+
/** Savings per period from the effective date; null = no price maintained. */
|
|
397
|
+
nextPeriodSavingsNet: number | null;
|
|
398
|
+
blockers: SubscriptionBundlePreviewIssue[];
|
|
399
|
+
warnings: SubscriptionBundlePreviewIssue[];
|
|
400
|
+
}
|
|
401
|
+
declare class SubscriptionBundlePreviewService {
|
|
402
|
+
private readonly subscriptionBundles;
|
|
403
|
+
private readonly bundles;
|
|
404
|
+
private readonly plans;
|
|
405
|
+
private readonly catalogEntries;
|
|
406
|
+
private readonly blockedBundles;
|
|
407
|
+
private readonly defaultMinTermMonths;
|
|
408
|
+
constructor(subscriptionBundles: SubscriptionBundleRepository, bundles: BundleRepository, plans?: PlanRepository | null, catalogEntries?: CatalogEntryRepository | null, blockedBundles?: SelfServiceBlockedBundles | null, config?: SubscriptionBundleConfig);
|
|
409
|
+
previewAdd(ctx: SubscriptionBundlePreviewContext, input: {
|
|
410
|
+
bundleVersionId: string;
|
|
411
|
+
minimumTermMonths?: number;
|
|
412
|
+
}, now?: Date): Promise<SubscriptionBundleAddPreviewDto>;
|
|
413
|
+
previewCancel(ctx: SubscriptionBundlePreviewContext, input: {
|
|
414
|
+
subscriptionBundleId: string;
|
|
415
|
+
}, now?: Date): Promise<SubscriptionBundleCancelPreviewDto>;
|
|
416
|
+
/** Bookability checks — same codes as `addBundleToSubscription` (422 path). */
|
|
417
|
+
private collectBookabilityBlockers;
|
|
418
|
+
/** Versions of the active bundle bookings (for redundancy + requires coverage). */
|
|
419
|
+
private loadActiveBundleVersions;
|
|
420
|
+
/** Features of the currently live PlanVersion state; empty without PlanRepository. */
|
|
421
|
+
private resolvePlanFeatures;
|
|
422
|
+
private collectRedundantFeatures;
|
|
423
|
+
/**
|
|
424
|
+
* requires of the new bundle that neither the bundle itself nor plan ∪
|
|
425
|
+
* active bundles cover (#35). Empty without CatalogEntryRepository.
|
|
426
|
+
*/
|
|
427
|
+
private collectMissingRequires;
|
|
428
|
+
/** `projectKey` lives on the bundle stem, not on the version. */
|
|
429
|
+
private resolveProjectKey;
|
|
430
|
+
}
|
|
431
|
+
/**
|
|
432
|
+
* List price (net) for the billing cycle including plan-specific
|
|
433
|
+
* pricing override (BundlePricingOverride with `planId`). null = no price
|
|
434
|
+
* maintained for the cycle.
|
|
435
|
+
*/
|
|
436
|
+
declare function resolveBundlePriceNet(bundleVersion: BundleVersionRow, planKey: string, billingCycle: string): number | null;
|
|
437
|
+
|
|
438
|
+
export { type AddBundleToSubscriptionInput as A, type BundlePreviewSnapshot as B, type CancelBundleFromSubscriptionInput as C, type LimitsCheckRow as L, PendingPlanMaterializationService as P, type RedundantFeatureHint as R, type SubscriptionBundleAddPreviewDto as S, TenantAdminGuard as T, type UsageResponse as U, CancelSubscriptionDto as a, ChangePlanDto as b, CompleteOnboardingSubscriptionDto as c, ComposedTenantAuthGuard as d, type PlanChangeContext as e, type PlanChangePreviewDto as f, type PlanChangePreviewIssue as g, PlanChangePreviewService as h, type PlanChangeType as i, type PlanSnapshotDto as j, PreviewPlanChangeDto as k, type ProrationDto as l, type ProrationInput as m, type SubscriptionBundleCancelPreviewDto as n, type SubscriptionBundleConfig as o, type SubscriptionBundlePreviewContext as p, type SubscriptionBundlePreviewIssue as q, SubscriptionBundlePreviewService as r, SubscriptionBundlesService as s, TenantBillingController as t, addMonths as u, computeProration as v, resolveBundleCancelEffectiveAt as w, resolveBundlePriceNet as x };
|