@saasicat/nest 1.0.0-rc.2 → 1.0.0-rc.21
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/README.md +44 -8
- package/dist/.build-stamp +1 -1
- package/dist/_entries.cjs +10214 -4796
- package/dist/admin/index.d.cts +5 -4
- package/dist/admin/index.d.ts +5 -4
- package/dist/admin/index.js +13 -9
- package/dist/{admin-resources.module-CpvzcfCc.d.cts → admin-resources.module-B4XNvdH-.d.cts} +1 -2
- package/dist/{admin-resources.module-DF5_Q7RD.d.ts → admin-resources.module-CWqdI9BV.d.ts} +1 -2
- package/dist/{admin-stats.service-TYiSUwIR.d.ts → admin-stats.service-CsLNt19Z.d.ts} +11 -7
- package/dist/{admin-stats.service-DfKupywR.d.cts → admin-stats.service-DrZrU8IJ.d.cts} +11 -7
- package/dist/{aggregation-B3CD0v_d.d.cts → aggregation-ChpZDh0g.d.ts} +39 -6
- package/dist/{aggregation-BFzW07DE.d.ts → aggregation-OpnhwzF5.d.cts} +39 -6
- package/dist/billing/index.d.cts +448 -108
- package/dist/billing/index.d.ts +448 -108
- package/dist/billing/index.js +108 -41
- package/dist/catalog/index.d.cts +74 -45
- package/dist/catalog/index.d.ts +74 -45
- package/dist/catalog/index.js +21 -13
- package/dist/catalog.module-C9O7BxRq.d.ts +180 -0
- package/dist/catalog.module-T-I5Ricz.d.cts +180 -0
- package/dist/checkout-offer/index.d.cts +17 -22
- package/dist/checkout-offer/index.d.ts +17 -22
- package/dist/checkout-offer/index.js +21 -7
- package/dist/checkout-offer.module-DFhGy6JO.d.cts +58 -0
- package/dist/checkout-offer.module-x7SP_dX4.d.ts +58 -0
- package/dist/checkout-offer.service-BeHgYikI.d.ts +242 -0
- package/dist/checkout-offer.service-CCVxG9b2.d.cts +242 -0
- package/dist/chunk-34XVPR6P.js +222 -0
- package/dist/{chunk-AOQJEYOL.js → chunk-3QVGA6JX.js} +3 -1
- package/dist/chunk-3RLWML2S.js +896 -0
- package/dist/{chunk-TXC3LHHB.js → chunk-4SDSYV4A.js} +21 -13
- package/dist/chunk-55QEDAMX.js +946 -0
- package/dist/chunk-5CKC7O52.js +140 -0
- package/dist/chunk-6PJE7EWO.js +185 -0
- package/dist/chunk-7NJKVC5X.js +353 -0
- package/dist/{chunk-RUNZ3X4C.js → chunk-AOS2JPDD.js} +1263 -185
- package/dist/{chunk-WOVQPXV4.js → chunk-CQ2ZZMTD.js} +702 -725
- package/dist/{chunk-NA6O63B7.js → chunk-F5X66HF5.js} +5 -5
- package/dist/chunk-FEQZCWUL.js +174 -0
- package/dist/{chunk-VIJ5NJWC.js → chunk-G6EZWECL.js} +107 -31
- package/dist/chunk-G7RQO2XO.js +0 -0
- package/dist/{chunk-N3L3AICU.js → chunk-GPQWGA6B.js} +5 -6
- package/dist/chunk-GZTL64QK.js +7 -0
- package/dist/{chunk-SABTXESR.js → chunk-J7NJRK3K.js} +17 -1
- package/dist/{chunk-SEPN52AD.js → chunk-JBCC6C3M.js} +566 -307
- package/dist/{chunk-I7GTA3RX.js → chunk-JQRA724W.js} +1 -1
- package/dist/{chunk-WXYJHZCN.js → chunk-JVAEKTJ4.js} +2 -14
- package/dist/chunk-KPEMTBOP.js +26 -0
- package/dist/chunk-LF6J4YYN.js +301 -0
- package/dist/{chunk-LTT736P3.js → chunk-LLYVYRGJ.js} +228 -387
- package/dist/{chunk-6Z7JR4EW.js → chunk-M47BNEY2.js} +1897 -633
- package/dist/{chunk-KFT5AIIH.js → chunk-NDLC5GYK.js} +206 -24
- package/dist/{chunk-O2J2HDXA.js → chunk-OOTEXP47.js} +3 -8
- package/dist/chunk-QPVDCKYS.js +702 -0
- package/dist/{chunk-NHVDCYK5.js → chunk-R5YCJBHY.js} +71 -106
- package/dist/{chunk-XBYAFEOR.js → chunk-S33SO5XX.js} +1 -1
- package/dist/chunk-SZ7RFPXA.js +10 -0
- package/dist/chunk-TBBZWZQT.js +98 -0
- package/dist/chunk-TOEFN7DN.js +295 -0
- package/dist/{chunk-7ZEGFL42.js → chunk-WHJSYKNC.js} +6 -6
- package/dist/chunk-WOEJ6K7M.js +55 -0
- package/dist/{chunk-AU3OOREM.js → chunk-XSSWYPP5.js} +16 -3
- package/dist/contract-line-item-money-B0Z3Ogur.d.cts +43 -0
- package/dist/contract-line-item-money-B0Z3Ogur.d.ts +43 -0
- package/dist/discovery/index.d.cts +2 -2
- package/dist/discovery/index.d.ts +2 -2
- package/dist/discovery/index.js +6 -6
- package/dist/{discovery.scanner-9cMqF95o.d.cts → discovery.scanner-DXKc6JkV.d.cts} +1 -1
- package/dist/{discovery.scanner-9cMqF95o.d.ts → discovery.scanner-DXKc6JkV.d.ts} +1 -1
- package/dist/{enforce-quota.interceptor-Df0e3OWe.d.cts → enforce-quota.interceptor-BFyVdWe8.d.cts} +6 -6
- package/dist/{enforce-quota.interceptor-DW3etVyI.d.ts → enforce-quota.interceptor-GaBXboGx.d.ts} +6 -6
- package/dist/entitlement/index.d.cts +27 -4
- package/dist/entitlement/index.d.ts +27 -4
- package/dist/entitlement/index.js +13 -8
- package/dist/index.d.cts +209 -28
- package/dist/index.d.ts +209 -28
- package/dist/index.js +285 -163
- package/dist/issuer-identity.check-CBO0vcsq.d.cts +197 -0
- package/dist/issuer-identity.check-Cz9CEMkZ.d.ts +197 -0
- package/dist/{module-options-COSEb0VW.d.ts → module-options-DmQ3G4sZ.d.ts} +135 -41
- package/dist/{module-options-BG3MzQva.d.cts → module-options-qAOwinHR.d.cts} +135 -41
- package/dist/payment-callback.service-CR84Xw1Z.d.cts +125 -0
- package/dist/payment-callback.service-CR84Xw1Z.d.ts +125 -0
- package/dist/payments/index.cjs +4 -0
- package/dist/payments/index.d.cts +187 -0
- package/dist/payments/index.d.ts +187 -0
- package/dist/payments/index.js +141 -0
- package/dist/payments.module-BdUq9ot_.d.cts +57 -0
- package/dist/payments.module-CQGv_VG7.d.ts +57 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.cts +21 -0
- package/dist/plan-catalog-source-DWe-BGY1.d.ts +21 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.cts → plan-resolution-CwUy_OqC.d.cts} +14 -0
- package/dist/{plan-resolution-Cgo_TR1H.d.ts → plan-resolution-CwUy_OqC.d.ts} +14 -0
- package/dist/{plan-versions.service-DChhbK6h.d.ts → plan-versions.service-C0N-Em2W.d.ts} +38 -64
- package/dist/{plan-versions.service-TPEtbN0r.d.cts → plan-versions.service-DARLh-qI.d.cts} +38 -64
- package/dist/platform/index.d.cts +52 -23
- package/dist/platform/index.d.ts +52 -23
- package/dist/platform/index.js +57 -36
- package/dist/promo/index.d.cts +23 -8
- package/dist/promo/index.d.ts +23 -8
- package/dist/promo/index.js +15 -7
- package/dist/{promo.module-DtvIycpt.d.cts → promo.module-CKkECmU8.d.cts} +9 -3
- package/dist/{promo.module-Z8h1PNX-.d.ts → promo.module-DuIJdK3U.d.ts} +9 -3
- package/dist/promo.service-BYBu6dy3.d.cts +215 -0
- package/dist/promo.service-CFjfF0Xm.d.ts +215 -0
- package/dist/registration/index.d.cts +104 -47
- package/dist/registration/index.d.ts +104 -47
- package/dist/registration/index.js +15 -9
- package/dist/subscriber/index.cjs +4 -0
- package/dist/subscriber/index.d.cts +28 -0
- package/dist/subscriber/index.d.ts +28 -0
- package/dist/subscriber/index.js +16 -0
- package/dist/subscriber.service-5W6Gi-1B.d.cts +68 -0
- package/dist/subscriber.service-5W6Gi-1B.d.ts +68 -0
- package/dist/subscription-contract/index.d.cts +4 -2
- package/dist/subscription-contract/index.d.ts +4 -2
- package/dist/subscription-contract/index.js +13 -5
- package/dist/{subscription-contract.module-fDFdQ0jm.d.ts → subscription-contract.module-BfIRpc1o.d.ts} +8 -2
- package/dist/{subscription-contract.module-CEK8HS5-.d.cts → subscription-contract.module-DTMuUJYd.d.cts} +8 -2
- package/dist/{subscription-contract.service-hD87MKyg.d.cts → subscription-contract.service-CPZrTM9C.d.ts} +32 -3
- package/dist/{subscription-contract.service-hD87MKyg.d.ts → subscription-contract.service-CfNApLuP.d.cts} +32 -3
- package/dist/tenant-billing.controller-BLqbsONe.d.ts +812 -0
- package/dist/tenant-billing.controller-DKbAqTbM.d.cts +812 -0
- package/dist/tenant-billing.module-ClbjzocC.d.cts +364 -0
- package/dist/tenant-billing.module-DohgJJ2m.d.ts +364 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.cts +129 -0
- package/dist/tenant-billing.tokens-G-1xOlMr.d.ts +129 -0
- package/dist/testing/index.d.cts +44 -17
- package/dist/testing/index.d.ts +44 -17
- package/dist/testing/index.js +189 -40
- package/package.json +24 -4
- package/dist/catalog.module-BDDO6iwq.d.cts +0 -103
- package/dist/catalog.module-BTnMsE6u.d.ts +0 -103
- package/dist/checkout-offer.module-CMtiQJTT.d.ts +0 -39
- package/dist/checkout-offer.module-CVpWrzbe.d.cts +0 -39
- package/dist/checkout-offer.service-BLOv2HOo.d.cts +0 -49
- package/dist/checkout-offer.service-BLOv2HOo.d.ts +0 -49
- package/dist/chunk-57V6ZTI6.js +0 -22
- package/dist/chunk-BSK6YBLI.js +0 -87
- package/dist/chunk-DQKCK7DX.js +0 -217
- package/dist/chunk-VXEYLNIB.js +0 -632
- package/dist/chunk-WUDYIPYH.js +0 -715
- package/dist/define-saasicat-DROBe-b1.d.ts +0 -69
- package/dist/define-saasicat-vGBCkFys.d.cts +0 -69
- package/dist/promo.service-BJbKAmw3.d.cts +0 -112
- package/dist/promo.service-BJbKAmw3.d.ts +0 -112
- package/dist/tenant-billing.controller-BQ67mlmI.d.ts +0 -438
- package/dist/tenant-billing.controller-DS8kB8a7.d.cts +0 -438
- package/dist/tenant-billing.module-BznC3XtW.d.cts +0 -299
- package/dist/tenant-billing.module-wUbsQSFG.d.ts +0 -299
- /package/dist/{chunk-2FR6ZL7R.js → chunk-BHZOH2DY.js} +0 -0
- /package/dist/{chunk-2SLTRKXC.js → chunk-DFOW3JVO.js} +0 -0
- /package/dist/{chunk-DBZGV3HC.js → chunk-DU56UKEH.js} +0 -0
- /package/dist/{chunk-OYNHOY45.js → chunk-GAUEVIAE.js} +0 -0
package/dist/billing/index.d.ts
CHANGED
|
@@ -1,25 +1,76 @@
|
|
|
1
|
-
import { BillingCycle, FeatureKey, UpsellOfferResolver, BundleRepository, CatalogEntryRepository, UpsellOffer,
|
|
2
|
-
export { BundleVersionFields, ChangeDirection, DiffResult, PlanVersionFields, VersionChange, VersionChangeDirection, classifyBundleVersionDiff, classifyPlanDiff } from '@saasicat/core';
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { BillingCycle, BundleVersionRow, FeatureKey, UpsellOfferResolver, BundleRepository, CatalogEntryRepository, UpsellOffer, PlanCatalogSettings, PlanCatalogReadSink, PlanCatalog, PlanCatalogReadSnapshot, PlanCatalogImportSink, PlanCatalogImportReport, PlanId, PlanDef, QuotaKey, MarketingTopFeature, FeatureUiRegistry, MarketingProjectionRepository, TenantSubscriptionWritePort, ConfiguratorSourcesLookup, ConfiguratorMarketingProvider, ConfiguratorCatalog } from '@saasicat/core';
|
|
2
|
+
export { BundleVersionFields, CancellationNoticePeriods, ChangeDirection, DiffResult, PlanVersionFields, SelfServiceBlockedPlans, VersionChange, VersionChangeDirection, classifyBundleVersionDiff, classifyPlanDiff } from '@saasicat/core';
|
|
3
|
+
export { A as AddBundleToSubscriptionInput, B as BundlePreviewSnapshot, C as CancelBundleFromSubscriptionInput, a as CancelSubscriptionDto, b as CancellableSubscription, c as CancellationDecision, d as CancellationInput, e as ChangePlanDto, f as CompleteOnboardingSubscriptionDto, g as ComposedTenantAuthGuard, h as CycleDirection, L as LimitsCheckRow, N as NO_NOTICE_PERIOD, P as PendingPlanMaterializationService, i as PlanChangeContext, j as PlanChangePreviewDto, k as PlanChangePreviewIssue, l as PlanChangePreviewService, m as PlanChangeType, n as PlanDirection, o as PlanSnapshotDto, p as PreviewPlanChangeDto, q as ProrationDto, r as ProrationInput, R as RedundantFeatureHint, S as SubscriptionBundleAddPreviewDto, s as SubscriptionBundleCancelPreviewDto, t as SubscriptionBundleConfig, u as SubscriptionBundlePreviewContext, v as SubscriptionBundlePreviewIssue, w as SubscriptionBundlePreviewService, x as SubscriptionBundlesService, T as TenantAdminGuard, y as TenantBillingController, U as UsageResponse, z as addMonths, D as clampToParent, E as computeProration, F as decideCancellation, G as decideCancellationFor, H as noticeDaysFor, I as resolveBundleCancelEffectiveAt } from '../tenant-billing.controller-BLqbsONe.js';
|
|
4
|
+
import { F as FEATURE_GUARD_MARKER, d as ContractFreezePort, e as ContractFreezeSourcePort } from '../tenant-billing.module-DohgJJ2m.js';
|
|
5
|
+
export { A as AjvErrorLike, C as CONTRACT_FREEZE_PORT_TOKEN, b as CONTRACT_FREEZE_SOURCE_PORT_TOKEN, c as ContractFreezeBundleSnapshot, E as EnvironmentVariables, f as LoadPlanCatalogFromStringOptions, L as LoadPlanCatalogOptions, M as MarkedFeatureGuard, P as PLAN_CATALOG_UNREADABLE_ERROR, g as PlanCatalogValidationError, h as SELF_SERVICE_BLOCKED_BUNDLES_TOKEN, i as SELF_SERVICE_BLOCKED_PLANS_TOKEN, j as SelfServiceBlockedBundles, a as SubscriptionBundleControllerOptions, k as SubscriptionBundleModule, S as SubscriptionBundleModuleOptions, l as TenantBillingModule, T as TenantBillingModuleOptions, m as catalogSource, n as isPlatformFeatureGuard, o as loadPlanCatalogFromFile, p as loadPlanCatalogFromString } from '../tenant-billing.module-DohgJJ2m.js';
|
|
5
6
|
import * as _nestjs_common from '@nestjs/common';
|
|
6
7
|
import { CanActivate, ExecutionContext, ArgumentsHost, Type, DynamicModule, ForwardReference, Provider } from '@nestjs/common';
|
|
7
8
|
export { Type as NestType } from '@nestjs/common';
|
|
8
9
|
import { Reflector, BaseExceptionFilter } from '@nestjs/core';
|
|
9
|
-
import { c as EntitlementService } from '../aggregation-
|
|
10
|
+
import { c as EntitlementService } from '../aggregation-ChpZDh0g.js';
|
|
10
11
|
import { P as ProviderSpec } from '../di-CcNeq9v-.js';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import '../
|
|
14
|
-
|
|
12
|
+
import { P as PlanCatalogSource } from '../plan-catalog-source-DWe-BGY1.js';
|
|
13
|
+
export { a as PlanCatalogOrigin, g as givenPlanCatalogSource } from '../plan-catalog-source-DWe-BGY1.js';
|
|
14
|
+
import { d as AuthGuardList } from '../tenant-billing.tokens-G-1xOlMr.js';
|
|
15
|
+
export { c as AUDIT_CONTEXT_RESOLVER_TOKEN, A as AuditContextResolver, B as BILLING_PERMISSION_GUARDS_TOKEN, C as CANCELLATION_NOTICE_DAYS_TOKEN, D as DuePendingPlanChange, e as PENDING_PLAN_QUERY_PORT_TOKEN, P as PendingPlanQueryPort, S as SUBSCRIPTION_USAGE_PORT_TOKEN, f as SUBSCRIPTION_WRITE_PORT_TOKEN, g as TENANT_AUTH_GUARDS_TOKEN, h as TENANT_ID_RESOLVER_TOKEN, i as TRIAL_PROJECTION_PORT_TOKEN, T as TenantIdResolver, j as TrialProjectionInput, a as TrialProjectionPort, k as USAGE_SNAPSHOT_PORT_TOKEN, l as USER_EMAIL_RESOLVER_TOKEN, m as USER_ID_RESOLVER_TOKEN, U as UserEmailResolver, b as UserIdResolver } from '../tenant-billing.tokens-G-1xOlMr.js';
|
|
16
|
+
import { S as SubscriptionContractService } from '../subscription-contract.service-CPZrTM9C.js';
|
|
17
|
+
import '../promo.service-CFjfF0Xm.js';
|
|
15
18
|
import '../admin-audit.service-4P3Djdxk.js';
|
|
19
|
+
import '../contract-line-item-money-B0Z3Ogur.js';
|
|
20
|
+
import '../plan-resolution-CwUy_OqC.js';
|
|
21
|
+
import '../subscriber.service-5W6Gi-1B.js';
|
|
16
22
|
|
|
23
|
+
/**
|
|
24
|
+
* One period onwards, on `anchorDay` where the month has one.
|
|
25
|
+
*
|
|
26
|
+
* The anchor is the day the subscription is billed on, and it survives months
|
|
27
|
+
* that are too short for it. Without one, this function has to read the day
|
|
28
|
+
* from its own input — and its own input is the previous clamped result, so a
|
|
29
|
+
* February eats the anchor and never gives it back: 31 January became 28
|
|
30
|
+
* February and then 28 March, 28 April, 28 May, for the rest of the
|
|
31
|
+
* subscription's life. Three days lost once, invisibly, and every later date
|
|
32
|
+
* measured from the wrong one.
|
|
33
|
+
*
|
|
34
|
+
* Clamping applies to the step's OUTPUT, never to the next step's input. A
|
|
35
|
+
* subscription anchored on the 31st is billed on the 28th in February and on
|
|
36
|
+
* the 31st again in March; one anchored on the 30th is billed on the 30th in
|
|
37
|
+
* October, not the 31st. The anchor is a day number, not "the end of the
|
|
38
|
+
* month".
|
|
39
|
+
*
|
|
40
|
+
* Omitting it keeps the old reading — the day of `d` — which is right wherever
|
|
41
|
+
* a caller has only one step to take and `d` is itself the anchor.
|
|
42
|
+
*/
|
|
43
|
+
declare function advanceOneCycle(d: Date, cycle: BillingCycle, anchorDay?: number): Date;
|
|
44
|
+
/**
|
|
45
|
+
* One period backwards, on `anchorDay` where the month has one.
|
|
46
|
+
*
|
|
47
|
+
* The mirror of `advanceOneCycle`, and it exists for one question: how long is
|
|
48
|
+
* the full period that ends here? A short first period is charged pro rata
|
|
49
|
+
* against a whole cycle, and a whole cycle is only measurable from both of its
|
|
50
|
+
* ends. Reading its length from the calendar month instead is wrong wherever
|
|
51
|
+
* the anchor is not the 1st — a period ending on 28 February with an anchor of
|
|
52
|
+
* 31 runs from 31 January, which is 28 days, not the 31 that February's
|
|
53
|
+
* neighbour would suggest.
|
|
54
|
+
*
|
|
55
|
+
* The same clamp rule applies, and applies to this step's output: from 31
|
|
56
|
+
* March backwards on anchor 31 is 28 February, and from there backwards again
|
|
57
|
+
* is 31 January, not 28 January. The anchor is never consumed.
|
|
58
|
+
*/
|
|
59
|
+
declare function retreatOneCycle(d: Date, cycle: BillingCycle, anchorDay?: number): Date;
|
|
60
|
+
/**
|
|
61
|
+
* The day of the month a subscription is billed on.
|
|
62
|
+
*
|
|
63
|
+
* Read from whichever date opened the current run of periods — the start of the
|
|
64
|
+
* window, not the end of the last one, because the end has already been through
|
|
65
|
+
* a clamp.
|
|
66
|
+
*/
|
|
67
|
+
declare function billingAnchorDay(windowStart: Date): number;
|
|
17
68
|
/**
|
|
18
69
|
* Finds the next period boundary that lies strictly **after** `after`.
|
|
19
70
|
* Iterates from `startedAt` (fallback: `after`) by +1 cycle each time, until
|
|
20
71
|
* the result is greater than `after`.
|
|
21
72
|
*/
|
|
22
|
-
declare function periodEndAfter(startedAt: Date | null, cycle: BillingCycle, after: Date): Date;
|
|
73
|
+
declare function periodEndAfter(startedAt: Date | null, cycle: BillingCycle, after: Date, anchorDay?: number): Date;
|
|
23
74
|
/**
|
|
24
75
|
* Returns the initial period window for a subscription
|
|
25
76
|
* (`currentPeriodStart`/`currentPeriodEnd`). `start` is `startedAt`,
|
|
@@ -42,7 +93,203 @@ declare function initialPeriodWindow(startedAt: Date, cycle: BillingCycle): {
|
|
|
42
93
|
*
|
|
43
94
|
* Spec: ROADMAP §2 no. 3 (advance-warning period), §6.1 (time-based selection).
|
|
44
95
|
*/
|
|
45
|
-
declare function periodEndWithMinLead(startedAt: Date | null, cycle: BillingCycle, now: Date, minLeadDays?: number): Date;
|
|
96
|
+
declare function periodEndWithMinLead(startedAt: Date | null, cycle: BillingCycle, now: Date, minLeadDays?: number, anchorDay?: number): Date;
|
|
97
|
+
|
|
98
|
+
/** What a caller knows about the plan's billing rhythm, before resolving it. */
|
|
99
|
+
interface PlanAnchorSource {
|
|
100
|
+
/** The stored anchor, where the subscription has one. */
|
|
101
|
+
billingAnchorDay?: number | null;
|
|
102
|
+
/** The date that opened the current run of periods. */
|
|
103
|
+
currentPeriodStart?: Date | null;
|
|
104
|
+
/** What opened it before the first window was written. */
|
|
105
|
+
startedAt?: Date | null;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The day of the month the plan is billed on, resolved once for everybody.
|
|
109
|
+
*
|
|
110
|
+
* Every caller that quotes a bundle and every caller that books one has to
|
|
111
|
+
* reach the same answer, or the preview describes a different contract from the
|
|
112
|
+
* one written — and this is the field that decides the difference. It happened:
|
|
113
|
+
* the preview derived the day from the window start while the booking left the
|
|
114
|
+
* value null and let the arithmetic read the window END, so for a 31 January to
|
|
115
|
+
* 28 February window the preview quoted a period ending on the 31st and the
|
|
116
|
+
* booking stored the 28th, after which the two renewed on different days
|
|
117
|
+
* forever.
|
|
118
|
+
*
|
|
119
|
+
* The order is authority, not convenience. The stored anchor is the answer
|
|
120
|
+
* where it exists. Failing that, the day that OPENED the current window —
|
|
121
|
+
* never the day that closed it, because a closing day has already been through
|
|
122
|
+
* a clamp and reading it is how a subscription billed on the 31st ends up
|
|
123
|
+
* billed on the 28th for the rest of its life.
|
|
124
|
+
*/
|
|
125
|
+
declare function resolvePlanAnchorDay(source: PlanAnchorSource): number | null;
|
|
126
|
+
interface BundleFirstPeriodInput {
|
|
127
|
+
/** When the bundle was booked. */
|
|
128
|
+
startedAt: Date;
|
|
129
|
+
/** The bundle's own cycle — never longer than the plan's. */
|
|
130
|
+
cycle: BillingCycle;
|
|
131
|
+
/** When the plan's current period ends, if it has one. */
|
|
132
|
+
planPeriodEnd: Date | null;
|
|
133
|
+
/** The day of the month the plan is billed on, if it is known. */
|
|
134
|
+
planAnchorDay: number | null;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The end of a bundle's first period: short, and landing on the plan's day.
|
|
138
|
+
*
|
|
139
|
+
* Null where the plan has no period to align to — a trial, or a subscription
|
|
140
|
+
* still waiting for sales. A bundle booked there has no period of its own
|
|
141
|
+
* either, rather than one invented from the booking date.
|
|
142
|
+
*/
|
|
143
|
+
declare function bundleFirstPeriodEnd(input: BundleFirstPeriodInput): Date | null;
|
|
144
|
+
/**
|
|
145
|
+
* The start of the full cycle that the first period is a part of.
|
|
146
|
+
*
|
|
147
|
+
* Not the booking date: the first period is short, and a short period is
|
|
148
|
+
* charged pro rata against a **whole** cycle. That whole cycle ends where the
|
|
149
|
+
* first period ends and begins one cycle before it — so a monthly bundle
|
|
150
|
+
* booked on 21 February against a plan anchored on the 31st is charged for
|
|
151
|
+
* 7 of the 28 days between 31 January and 28 February, not for 7 of 31, and
|
|
152
|
+
* not for the 160 days left of the plan's own yearly period.
|
|
153
|
+
*
|
|
154
|
+
* Prorating against the plan's period was the shape this used to have, and it
|
|
155
|
+
* is wrong by the width of the difference between the two cycles: a monthly
|
|
156
|
+
* bundle on a yearly plan was charged a fraction of a *year* at a monthly
|
|
157
|
+
* price.
|
|
158
|
+
*/
|
|
159
|
+
declare function bundleFirstPeriodStart(firstPeriodEnd: Date, cycle: BillingCycle, planAnchorDay: number | null): Date;
|
|
160
|
+
/**
|
|
161
|
+
* The end of every period after the first: one cycle on, still on the plan's
|
|
162
|
+
* day.
|
|
163
|
+
*/
|
|
164
|
+
declare function bundleNextPeriodEnd(currentPeriodEnd: Date, cycle: BillingCycle, planAnchorDay: number | null): Date;
|
|
165
|
+
/**
|
|
166
|
+
* Whether a bundle may be billed on `bundleCycle` beside a plan on `planCycle`.
|
|
167
|
+
*
|
|
168
|
+
* A bundle may run in a shorter rhythm than its plan, never a longer one. A
|
|
169
|
+
* yearly bundle on a monthly plan has no boundary to meet — the plan's periods
|
|
170
|
+
* end twelve times before the bundle's first one does, and every one of those
|
|
171
|
+
* ends is a moment the plan could stop and leave the bundle committed with
|
|
172
|
+
* nothing to grant.
|
|
173
|
+
*
|
|
174
|
+
* The shorter direction is fine and is the interesting case: a monthly bundle
|
|
175
|
+
* on a yearly plan simply lands on the plan's day every month, and on the
|
|
176
|
+
* plan's own boundary in the month the plan ends.
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* How long a booking commits the tenant when nobody says otherwise: **not at
|
|
180
|
+
* all**.
|
|
181
|
+
*
|
|
182
|
+
* An add-on can be cancelled at any time up to the moment its next period
|
|
183
|
+
* begins, and the cancellation takes effect at the end of the period it is in.
|
|
184
|
+
* The tenant pays for the period they are in, it ends normally, and nothing
|
|
185
|
+
* has to be paid back — which is the whole reason the rule is shaped this way.
|
|
186
|
+
*
|
|
187
|
+
* It was 12 until 2026-08-27, applied to every booking without an operator
|
|
188
|
+
* doing anything, and it made that rule impossible: a cancellation lands at
|
|
189
|
+
* `max(currentPeriodEnd, minimumTermEndsAt)`, so a monthly add-on booked today
|
|
190
|
+
* could not be cancelled to next month. On a yearly plan the term even
|
|
191
|
+
* outlasted the bundle's own last period. An operator who wants a commitment
|
|
192
|
+
* configures one; nobody gets one by default.
|
|
193
|
+
*/
|
|
194
|
+
declare const DEFAULT_BUNDLE_MINIMUM_TERM_MONTHS = 0;
|
|
195
|
+
declare function bundleCycleFitsPlan(bundleCycle: BillingCycle, planCycle: BillingCycle): boolean;
|
|
196
|
+
/** What a renewal job reads from a booking to decide whether to roll it on. */
|
|
197
|
+
interface BundlePeriodRollInput {
|
|
198
|
+
/**
|
|
199
|
+
* Null in two different situations, and the difference decides everything:
|
|
200
|
+
* a booking made before these columns existed, and one whose plan had no
|
|
201
|
+
* period to align to yet — a trial, or a subscription awaiting sales.
|
|
202
|
+
*
|
|
203
|
+
* `billingCycle` is what tells them apart. The booking route always writes
|
|
204
|
+
* it, so a row that has one but no window is the second case and is waiting
|
|
205
|
+
* for a window; a row with neither is the first and is billed with the plan.
|
|
206
|
+
*/
|
|
207
|
+
currentPeriodEnd: Date | null;
|
|
208
|
+
/** Null only on a booking made before bundles had a rhythm of their own. */
|
|
209
|
+
billingCycle: BillingCycle | null;
|
|
210
|
+
canceledAt: Date | null;
|
|
211
|
+
canceledEffectiveAt: Date | null;
|
|
212
|
+
}
|
|
213
|
+
/** The plan the booking hangs off, as the same job reads it. */
|
|
214
|
+
interface BundlePlanContext {
|
|
215
|
+
billingCycle: BillingCycle;
|
|
216
|
+
billingAnchorDay: number | null;
|
|
217
|
+
/** When the plan ends, or null while it runs on. */
|
|
218
|
+
endsAt: Date | null;
|
|
219
|
+
/**
|
|
220
|
+
* The plan's own window, which a booking still waiting for one joins.
|
|
221
|
+
*
|
|
222
|
+
* Null while the plan has no paid period either — then there is nothing to
|
|
223
|
+
* align to and the booking keeps waiting.
|
|
224
|
+
*/
|
|
225
|
+
currentPeriodStart?: Date | null;
|
|
226
|
+
currentPeriodEnd?: Date | null;
|
|
227
|
+
}
|
|
228
|
+
/** The window a roll opens. */
|
|
229
|
+
interface BundlePeriodWindow {
|
|
230
|
+
currentPeriodStart: Date;
|
|
231
|
+
currentPeriodEnd: Date;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* The window a booking should hold now, or `null` for nothing to do.
|
|
235
|
+
*
|
|
236
|
+
* The bundle counterpart of `computeNextPeriod`, and the same division of
|
|
237
|
+
* labour: the platform decides, the consumer's cron reads, writes and audits.
|
|
238
|
+
* Without it the columns written at booking would never move again, and "every
|
|
239
|
+
* period after the first runs anchor to anchor" would be a claim nothing keeps.
|
|
240
|
+
*
|
|
241
|
+
* It answers two questions the job cannot tell apart from the outside, because
|
|
242
|
+
* both look like a booking whose window is not current:
|
|
243
|
+
*
|
|
244
|
+
* - **Opening the first window.** A bundle booked while its plan had no period —
|
|
245
|
+
* during a trial, or before sales finished — was stored without one, because
|
|
246
|
+
* there was nothing to align to. Once the plan has a paid period the booking
|
|
247
|
+
* joins it. Left undone, a monthly bundle on a yearly trial kept granting its
|
|
248
|
+
* features and never acquired a window to bill them in.
|
|
249
|
+
* - **Rolling the next one.** The ordinary case, anchor to anchor.
|
|
250
|
+
*
|
|
251
|
+
* It declines in four:
|
|
252
|
+
*
|
|
253
|
+
* - **A booking billed with the plan** — written before these columns existed,
|
|
254
|
+
* recognisable by having no rhythm of its own either. Giving it a window would
|
|
255
|
+
* start billing it a second time.
|
|
256
|
+
* - **The plan has no paid period yet**, so there is still nothing to align to.
|
|
257
|
+
* - **The period is still running.** The job's filter should not have offered it.
|
|
258
|
+
* - **The booking's own cancellation has landed.** A declared one changes
|
|
259
|
+
* nothing, exactly as for a subscription.
|
|
260
|
+
* - **The plan has ended, or ends before the new period could open.** This is
|
|
261
|
+
* the rule the alignment exists for: the bundle ends with the plan, without a
|
|
262
|
+
* cancellation of its own.
|
|
263
|
+
*
|
|
264
|
+
* Where the plan ends *inside* the new period — which only happens when someone
|
|
265
|
+
* ends it off-anchor — the window is cut back to the plan's end rather than
|
|
266
|
+
* allowed to outlive it. A bundle committed past its plan is the one state this
|
|
267
|
+
* whole arrangement is built to prevent.
|
|
268
|
+
*/
|
|
269
|
+
declare function computeNextBundlePeriod(booking: BundlePeriodRollInput, plan: BundlePlanContext, now: Date): BundlePeriodWindow | null;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* List price (net) for the billing cycle, including a plan-specific pricing
|
|
273
|
+
* override (`BundlePricingOverride` with `planId`).
|
|
274
|
+
*
|
|
275
|
+
* `null` means no price is maintained for that combination — not that the
|
|
276
|
+
* bundle is free. A published bundle always resolves *some* price, which the
|
|
277
|
+
* publish gate checks; what it cannot check is the combination, since that
|
|
278
|
+
* depends on the plan the tenant is on and the rhythm they are billed in.
|
|
279
|
+
*/
|
|
280
|
+
declare function resolveBundlePriceNet(bundleVersion: BundleVersionRow, planKey: string, billingCycle: string): number | null;
|
|
281
|
+
/**
|
|
282
|
+
* The cycles a plan version is actually sold in.
|
|
283
|
+
*
|
|
284
|
+
* Derived from the prices it carries rather than from a list of cycles, so a
|
|
285
|
+
* plan priced monthly only is a monthly plan and nothing has to say so
|
|
286
|
+
* separately. A version with neither price is sold in no cycle at all — the
|
|
287
|
+
* plan's own publish gate refuses that, and here it simply asks nothing.
|
|
288
|
+
*/
|
|
289
|
+
declare function cyclesSoldFor(planVersion: {
|
|
290
|
+
monthlyNet?: string | null;
|
|
291
|
+
yearlyNet?: string | null;
|
|
292
|
+
}): BillingCycle[];
|
|
46
293
|
|
|
47
294
|
/** Generic shape of a PlanVersion snapshot for publish validation. */
|
|
48
295
|
interface PublishablePlanVersion {
|
|
@@ -113,6 +360,16 @@ interface RenewalSubInput {
|
|
|
113
360
|
pendingPlanVersionAccepted: boolean;
|
|
114
361
|
/** `nonRegressive` from the referenced PlanVersion. */
|
|
115
362
|
pendingPlanVersionNonRegressive: boolean;
|
|
363
|
+
/**
|
|
364
|
+
* The cancellation, read the same way `computeNextPeriod` reads it below.
|
|
365
|
+
*
|
|
366
|
+
* A version published before the customer cancelled still comes due
|
|
367
|
+
* afterwards, and rolling it forward rewrites the plan of a subscription
|
|
368
|
+
* whose term is over. Required, and required together, because a record
|
|
369
|
+
* that omits them answers "not cancelled" and goes ahead.
|
|
370
|
+
*/
|
|
371
|
+
canceledAt: Date | null;
|
|
372
|
+
canceledEffectiveAt: Date | null;
|
|
116
373
|
}
|
|
117
374
|
/**
|
|
118
375
|
* Decides what should happen to a subscription with a due pending version.
|
|
@@ -134,25 +391,66 @@ declare function clearPendingPlanVersionFields(): {
|
|
|
134
391
|
};
|
|
135
392
|
/** Input shape for `computeNextPeriod`. */
|
|
136
393
|
interface PeriodRollInput {
|
|
394
|
+
/**
|
|
395
|
+
* The day of the month this subscription is billed on, or null on a row
|
|
396
|
+
* that predates the column. Null falls back to the day of the period end,
|
|
397
|
+
* which is the reading that drifts — so a consumer that stores the anchor
|
|
398
|
+
* gets the correct date and one that does not keeps today's behaviour.
|
|
399
|
+
*/
|
|
400
|
+
billingAnchorDay?: number | null;
|
|
137
401
|
/** Subscription.currentPeriodEnd. NULL → no period active → SKIP. */
|
|
138
402
|
currentPeriodEnd: Date | null;
|
|
139
403
|
billingCycle: BillingCycle;
|
|
404
|
+
/**
|
|
405
|
+
* When a cancellation was declared — and, on a row written before the two
|
|
406
|
+
* fields parted company, also when it lands.
|
|
407
|
+
*
|
|
408
|
+
* It used to stop the renewal on its own, which was right while it carried
|
|
409
|
+
* both meanings. Since they separated it is normally only "the customer
|
|
410
|
+
* said so": a subscription cancelled in month three of a year still runs,
|
|
411
|
+
* and stopping its renewal would end it nine months early.
|
|
412
|
+
*
|
|
413
|
+
* The exception is the row that predates the split. There `canceledAt`
|
|
414
|
+
* holds the period end the old code computed and `canceledEffectiveAt` is
|
|
415
|
+
* null, so reading only the second would roll a cancelled subscription into
|
|
416
|
+
* another paid term, and the next one, forever.
|
|
417
|
+
*/
|
|
140
418
|
canceledAt: Date | null;
|
|
419
|
+
/**
|
|
420
|
+
* When that cancellation lands. This is what stops the renewal.
|
|
421
|
+
*
|
|
422
|
+
* Null while none was declared. A period may still roll onto it: with a
|
|
423
|
+
* notice period configured, a late cancellation lands at the end of the
|
|
424
|
+
* FOLLOWING period, and that period has to exist to end.
|
|
425
|
+
*/
|
|
426
|
+
canceledEffectiveAt: Date | null;
|
|
141
427
|
}
|
|
142
428
|
/** Result: the next period window or `null` (skip). */
|
|
143
429
|
interface NextPeriodWindow {
|
|
144
430
|
currentPeriodStart: Date;
|
|
145
431
|
currentPeriodEnd: Date;
|
|
432
|
+
/**
|
|
433
|
+
* The renewed commitment, which is the period itself.
|
|
434
|
+
*
|
|
435
|
+
* The minimum term IS the chosen billing period (rule a), it starts at
|
|
436
|
+
* activation (rule c) and it renews with the period unless a cancellation
|
|
437
|
+
* was declared first (rule d). Written on every roll so nothing has to
|
|
438
|
+
* reconstruct it later from a start date and a cycle.
|
|
439
|
+
*/
|
|
440
|
+
minimumTermUntil: Date;
|
|
146
441
|
}
|
|
147
442
|
/**
|
|
148
443
|
* Computes the next period window. `null` means: no action
|
|
149
444
|
* (either the period hasn't been reached yet, canceled, or NULL period).
|
|
150
445
|
*
|
|
151
446
|
* Logic (spec: SUPERADMIN_PLANS_DASHBOARD_TODO §2.2):
|
|
152
|
-
* - If
|
|
447
|
+
* - If a cancellation has LANDED → SKIP. A declared one has not, unless the
|
|
448
|
+
* row predates the split and carries its effective date in `canceledAt`.
|
|
153
449
|
* - If `currentPeriodEnd === null` → SKIP (trial / PENDING_SALES).
|
|
154
450
|
* - If `currentPeriodEnd > now` → SKIP (period still active).
|
|
155
|
-
* -
|
|
451
|
+
* - If a cancellation has LANDED → SKIP. A declared one has not.
|
|
452
|
+
* - Otherwise → start := old `currentPeriodEnd`, end := periodEndAfter(start),
|
|
453
|
+
* and the minimum term renews with it.
|
|
156
454
|
*/
|
|
157
455
|
declare function computeNextPeriod(sub: PeriodRollInput, now: Date): NextPeriodWindow | null;
|
|
158
456
|
|
|
@@ -230,10 +528,9 @@ declare const UPSELL_OFFER_CURRENCY_TOKEN: unique symbol;
|
|
|
230
528
|
|
|
231
529
|
declare class CatalogBundleUpsellResolver implements UpsellOfferResolver {
|
|
232
530
|
private readonly bundleRepo;
|
|
233
|
-
private readonly projectKey;
|
|
234
531
|
private readonly catalogEntryRepo;
|
|
235
532
|
private readonly currency;
|
|
236
|
-
constructor(bundleRepo: BundleRepository,
|
|
533
|
+
constructor(bundleRepo: BundleRepository, catalogEntryRepo?: CatalogEntryRepository | null, currency?: string | null);
|
|
237
534
|
resolveOffers(featureKeys: string[], tenantId: string): Promise<UpsellOffer[]>;
|
|
238
535
|
/**
|
|
239
536
|
* Uncovered dependencies of the missing features (#35) — feed into the
|
|
@@ -251,66 +548,18 @@ declare class LimitExceededFilter extends BaseExceptionFilter {
|
|
|
251
548
|
catch(exception: unknown, host: ArgumentsHost): void;
|
|
252
549
|
}
|
|
253
550
|
|
|
551
|
+
/** The settings of `config/saas.yaml` — a `PlanCatalogSettings`, fixed while the process runs. */
|
|
552
|
+
declare const PLAN_CATALOG_SETTINGS_TOKEN: unique symbol;
|
|
553
|
+
/** A `PlanCatalogSource`: the plans and features as they stand when an operation asks. */
|
|
554
|
+
declare const PLAN_CATALOG_SOURCE_TOKEN: unique symbol;
|
|
555
|
+
declare const PLAN_CATALOG_READ_SINK_TOKEN: unique symbol;
|
|
254
556
|
/**
|
|
255
|
-
*
|
|
256
|
-
* the
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
instancePath?: string;
|
|
260
|
-
message?: string;
|
|
261
|
-
schemaPath?: string;
|
|
262
|
-
}
|
|
263
|
-
declare class PlanCatalogValidationError extends Error {
|
|
264
|
-
readonly source: string;
|
|
265
|
-
readonly errors: AjvErrorLike[];
|
|
266
|
-
constructor(source: string, errors: AjvErrorLike[]);
|
|
267
|
-
}
|
|
268
|
-
interface LoadPlanCatalogOptions {
|
|
269
|
-
/**
|
|
270
|
-
* Absolute path or relative path (resolved against CWD).
|
|
271
|
-
*/
|
|
272
|
-
path: string;
|
|
273
|
-
/**
|
|
274
|
-
* Optional: additional cross-field validations that the JSON schema
|
|
275
|
-
* cannot cover. Default: enable all (see validateConsistency).
|
|
276
|
-
*/
|
|
277
|
-
crossFieldChecks?: boolean;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Loads + validates a saas.yaml file.
|
|
281
|
-
*
|
|
282
|
-
* Throws `PlanCatalogValidationError` on schema violations or
|
|
283
|
-
* cross-field violations. Throws `Error` on IO/YAML parse errors.
|
|
284
|
-
*/
|
|
285
|
-
declare function loadPlanCatalogFromFile(opts: LoadPlanCatalogOptions): PlanCatalog;
|
|
286
|
-
/**
|
|
287
|
-
* Variant for tests / in-memory loading: takes YAML content as a string,
|
|
288
|
-
* `source` is only for error logging.
|
|
557
|
+
* Every settings block of `config/saas.yaml` — app identity, currency, VAT rate,
|
|
558
|
+
* tenant billing and the rest — beside the read sink the plans and features
|
|
559
|
+
* come from. The settings are handed on whole, so a block the schema gains
|
|
560
|
+
* reaches `PLAN_CATALOG_SETTINGS_TOKEN` without this module learning its name.
|
|
289
561
|
*/
|
|
290
|
-
|
|
291
|
-
source: string;
|
|
292
|
-
crossFieldChecks?: boolean;
|
|
293
|
-
}): PlanCatalog;
|
|
294
|
-
|
|
295
|
-
declare const PLAN_CATALOG_TOKEN: unique symbol;
|
|
296
|
-
declare const PLAN_CATALOG_READ_SINK_TOKEN: unique symbol;
|
|
297
|
-
interface PlanCatalogModuleOptions {
|
|
298
|
-
/** Build-time identity of the app. */
|
|
299
|
-
projectKey: string;
|
|
300
|
-
/**
|
|
301
|
-
* App-identity block (branding + version) from `config/saas.yaml#app`.
|
|
302
|
-
* Flows into `PLAN_CATALOG_TOKEN.app` and from there into the
|
|
303
|
-
* AdminPublicBoot endpoint + the AdminManifestConfig.
|
|
304
|
-
*/
|
|
305
|
-
app?: PlanCatalog['app'];
|
|
306
|
-
currency: string;
|
|
307
|
-
vatRate: number;
|
|
308
|
-
/**
|
|
309
|
-
* App-wide marketing configuration — including the
|
|
310
|
-
* `availableLocales` pool. Flows into `PLAN_CATALOG_TOKEN.marketing`
|
|
311
|
-
* and from there into the admin manifest (`project.availableLocales`).
|
|
312
|
-
*/
|
|
313
|
-
marketing?: PlanCatalog['marketing'];
|
|
562
|
+
interface PlanCatalogModuleOptions extends PlanCatalogSettings {
|
|
314
563
|
/** App-specific adapter for DB reads. */
|
|
315
564
|
sink: ProviderSpec<PlanCatalogReadSink>;
|
|
316
565
|
/** Modules that must be visible in the DI scope (analogous to CatalogModule). */
|
|
@@ -330,15 +579,12 @@ declare class PlanCatalogModule {
|
|
|
330
579
|
}): DynamicModule;
|
|
331
580
|
}
|
|
332
581
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
/** App-wide marketing configuration. Optional. */
|
|
340
|
-
marketing?: PlanCatalog['marketing'];
|
|
341
|
-
}
|
|
582
|
+
/**
|
|
583
|
+
* The settings a database catalogue runs on. The database carries plans and
|
|
584
|
+
* features, never the settings, so they can only come from the file — every
|
|
585
|
+
* block of it, which is why this is the settings type rather than a list.
|
|
586
|
+
*/
|
|
587
|
+
type PlanCatalogBuildSettings = PlanCatalogSettings;
|
|
342
588
|
declare function buildPlanCatalogFromSnapshot(settings: PlanCatalogBuildSettings, snapshot: PlanCatalogReadSnapshot): PlanCatalog;
|
|
343
589
|
|
|
344
590
|
declare const PLAN_CATALOG_IMPORT_SINK_TOKEN: unique symbol;
|
|
@@ -362,8 +608,10 @@ declare class PlanCatalogImporterService {
|
|
|
362
608
|
interface PlanCatalogImporterControllerConfig {
|
|
363
609
|
/**
|
|
364
610
|
* Class-level guards for `/admin/billing/plan-catalog/import`. Required
|
|
365
|
-
* when `controller` is set
|
|
366
|
-
*
|
|
611
|
+
* when `controller` is set. The import publishes the plans it creates, so
|
|
612
|
+
* the handler requires the second factor whatever this list holds, and
|
|
613
|
+
* `MfaService` has to be resolvable through `AdminModule`; with `[]`
|
|
614
|
+
* nothing establishes a caller and the import is refused.
|
|
367
615
|
*/
|
|
368
616
|
guards: Array<Type<CanActivate>>;
|
|
369
617
|
}
|
|
@@ -397,7 +645,7 @@ declare function getPlanOrThrow(catalog: PlanCatalog, planId: PlanId): PlanDef;
|
|
|
397
645
|
/**
|
|
398
646
|
* Returns all marketed plans (`marketed: true` or undefined). Order as in the
|
|
399
647
|
* catalog. ENTERPRISE and other `marketed: false` plans are NOT included —
|
|
400
|
-
* these
|
|
648
|
+
* these are activated by a catalogue apply or a special contract, and do
|
|
401
649
|
* not belong in self-service onboarding lists.
|
|
402
650
|
*/
|
|
403
651
|
declare function getMarketedPlans(catalog: PlanCatalog): PlanDef[];
|
|
@@ -409,10 +657,31 @@ declare function getMarketedPlans(catalog: PlanCatalog): PlanDef[];
|
|
|
409
657
|
* - the plan has no price for the cycle (`monthlyNet`/`yearlyNet === null`)
|
|
410
658
|
*/
|
|
411
659
|
declare function getPlanPriceNet(catalog: PlanCatalog, planId: PlanId, cycle: BillingCycle): number | null;
|
|
660
|
+
/** The same rule for a plan already in hand, whichever version it describes. */
|
|
661
|
+
declare function listPriceNet(plan: PlanDef, cycle: BillingCycle): number | null;
|
|
662
|
+
/**
|
|
663
|
+
* Whether a plan on the list carries no price for a cycle, and so is not sold
|
|
664
|
+
* in it: a plan without a yearly price is a monthly plan, one without either is
|
|
665
|
+
* sold on request. A plan that is not marketed is sold under a special
|
|
666
|
+
* contract, whose price the catalogue does not hold, so this is never true of
|
|
667
|
+
* it.
|
|
668
|
+
*/
|
|
669
|
+
declare function isPlanNotSoldInCycle(plan: PlanDef, cycle: BillingCycle): boolean;
|
|
412
670
|
/**
|
|
413
|
-
*
|
|
671
|
+
* The refusal for a plan that is not sold in a cycle — the same body for the
|
|
672
|
+
* plan change's blocker and for the contract, which would otherwise record the
|
|
673
|
+
* plan at 0.00.
|
|
674
|
+
*/
|
|
675
|
+
declare function planNotSoldInCycle(plan: PlanDef, cycle: BillingCycle): {
|
|
676
|
+
code: string;
|
|
677
|
+
message: string;
|
|
678
|
+
params: Record<string, string>;
|
|
679
|
+
};
|
|
680
|
+
/**
|
|
681
|
+
* Gross list price from the catalog, through `grossFromNet`.
|
|
414
682
|
* `null` with the same rules as `getPlanPriceNet`. `vatRate` is optional;
|
|
415
|
-
* default: `catalog.vatRate`.
|
|
683
|
+
* default: `catalog.vatRate`. A rate that is not a percentage is refused, as it
|
|
684
|
+
* is wherever a rate is stated.
|
|
416
685
|
*/
|
|
417
686
|
declare function getPlanPriceGross(catalog: PlanCatalog, planId: PlanId, cycle: BillingCycle, vatRate?: number): number | null;
|
|
418
687
|
/**
|
|
@@ -492,13 +761,12 @@ interface PublicBundleEntry {
|
|
|
492
761
|
marketing?: MarketingFields;
|
|
493
762
|
}
|
|
494
763
|
declare class PublicCatalogController {
|
|
495
|
-
private readonly
|
|
764
|
+
private readonly planCatalogs;
|
|
496
765
|
private readonly featureRegistry;
|
|
497
|
-
private readonly projectKey;
|
|
498
766
|
private readonly marketingRepo;
|
|
499
767
|
private readonly bundleRepo;
|
|
500
768
|
private readonly catalogEntryRepo;
|
|
501
|
-
constructor(
|
|
769
|
+
constructor(planCatalogs: PlanCatalogSource, featureRegistry: FeatureUiRegistry, marketingRepo?: MarketingProjectionRepository | null, bundleRepo?: BundleRepository | null, catalogEntryRepo?: CatalogEntryRepository | null);
|
|
502
770
|
listPlans(lang?: string, localeParam?: string): Promise<PlanResponseEntry[]>;
|
|
503
771
|
listFeatureRegistry(): Promise<FeatureUiRegistry>;
|
|
504
772
|
/**
|
|
@@ -521,12 +789,6 @@ declare class PublicCatalogController {
|
|
|
521
789
|
interface PublicCatalogModuleOptions {
|
|
522
790
|
/** Required: consumer-specific FeatureUiRegistry. */
|
|
523
791
|
featureUiRegistry: FeatureUiRegistry;
|
|
524
|
-
/**
|
|
525
|
-
* — app identity (e.g. "clubapp"). Used
|
|
526
|
-
* for marketing lookups + bundle filters.
|
|
527
|
-
* Optional; if omitted, the new endpoints return empty lists.
|
|
528
|
-
*/
|
|
529
|
-
projectKey?: string;
|
|
530
792
|
/**
|
|
531
793
|
* Optional. When set, `/billing/bundles` is active.
|
|
532
794
|
*/
|
|
@@ -537,9 +799,9 @@ interface PublicCatalogModuleOptions {
|
|
|
537
799
|
*/
|
|
538
800
|
marketingRepository?: ProviderSpec<MarketingProjectionRepository>;
|
|
539
801
|
/**
|
|
540
|
-
* Optional (#13). When set
|
|
541
|
-
*
|
|
542
|
-
*
|
|
802
|
+
* Optional (#13). When set, `/billing/feature-registry` overlays the
|
|
803
|
+
* editable `FeatureCatalogEntry.icon` from the DB over the static
|
|
804
|
+
* registry.
|
|
543
805
|
*/
|
|
544
806
|
catalogEntryRepository?: ProviderSpec<CatalogEntryRepository>;
|
|
545
807
|
/**
|
|
@@ -553,22 +815,101 @@ declare class PublicCatalogModule {
|
|
|
553
815
|
static forRoot(options: PublicCatalogModuleOptions): DynamicModule;
|
|
554
816
|
}
|
|
555
817
|
|
|
556
|
-
declare const PUBLIC_CATALOG_PROJECT_KEY_TOKEN: unique symbol;
|
|
557
818
|
declare const PUBLIC_CATALOG_BUNDLE_REPOSITORY_TOKEN: unique symbol;
|
|
558
819
|
declare const PUBLIC_CATALOG_MARKETING_REPOSITORY_TOKEN: unique symbol;
|
|
559
820
|
|
|
821
|
+
/**
|
|
822
|
+
* The billing permission: who of a tenant's users may see and change what the
|
|
823
|
+
* subscriber pays with — and, as they arrive, its invoices and its account.
|
|
824
|
+
* The plan, the usage and a change's preview are not behind it.
|
|
825
|
+
*
|
|
826
|
+
* The application decides who holds it by passing guards
|
|
827
|
+
* (`payments.billingPermissionGuards`), so a role such as accounting can hold
|
|
828
|
+
* it without being the tenant's administrator. Without guards the tenant's
|
|
829
|
+
* administrator holds it. Runs after the tenant's authentication guards, so a
|
|
830
|
+
* user is on the request.
|
|
831
|
+
*/
|
|
832
|
+
declare class BillingPermissionGuard implements CanActivate {
|
|
833
|
+
private readonly guards;
|
|
834
|
+
constructor(guards?: AuthGuardList | null);
|
|
835
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
836
|
+
private holdsPermission;
|
|
837
|
+
}
|
|
838
|
+
|
|
560
839
|
declare class SubscriptionContractFreezeService implements ContractFreezePort {
|
|
561
|
-
private readonly
|
|
840
|
+
private readonly catalogs;
|
|
562
841
|
private readonly entitlements;
|
|
563
842
|
private readonly contracts;
|
|
564
|
-
private readonly projectKey;
|
|
565
843
|
private readonly source;
|
|
566
|
-
constructor(
|
|
567
|
-
|
|
844
|
+
constructor(catalogs: PlanCatalogSource, entitlements: EntitlementService, contracts: SubscriptionContractService, source: ContractFreezeSourcePort, writes?: TenantSubscriptionWritePort | null);
|
|
845
|
+
assertPartyFor(tenantId: string): Promise<void>;
|
|
846
|
+
endOnCancellation(tenantId: string, effectiveAt: Date): Promise<void>;
|
|
847
|
+
freezeOnPlanChange(tenantId: string, newPlan: string, billingCycle: BillingCycle, effectiveFrom: Date, endsAt?: Date | null): Promise<void>;
|
|
568
848
|
}
|
|
569
849
|
|
|
570
850
|
declare function computeCarriedTrialEndsAt(currentTrialDays: number, newTrialDays: number, currentTrialEndsAt: Date, now: Date): Date;
|
|
571
851
|
|
|
852
|
+
declare class AddSubscriptionBundleDto {
|
|
853
|
+
bundleVersionId: string;
|
|
854
|
+
/**
|
|
855
|
+
* Optional — override for the minimum term (months). `0` = no
|
|
856
|
+
* minimum term. Default comes from the `SubscriptionBundleConfig`
|
|
857
|
+
* (platform = no commitment).
|
|
858
|
+
*/
|
|
859
|
+
minimumTermMonths?: number;
|
|
860
|
+
/**
|
|
861
|
+
* The rhythm to bill this bundle in. Defaults to the plan's.
|
|
862
|
+
*
|
|
863
|
+
* A bundle may run in a shorter rhythm than its plan — monthly beside a
|
|
864
|
+
* yearly plan is the interesting case — but never a longer one. A yearly
|
|
865
|
+
* bundle on a monthly plan is refused rather than modelled: it has no
|
|
866
|
+
* boundary to meet, and every one of the plan's twelve period ends is a
|
|
867
|
+
* moment the plan could stop and leave the bundle committed with nothing to
|
|
868
|
+
* grant.
|
|
869
|
+
*/
|
|
870
|
+
billingCycle?: 'MONTHLY' | 'YEARLY';
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Body of `POST /billing/subscription-bundles/preview` (#37). Exactly one
|
|
874
|
+
* of `bundleVersionId` (add preview) or `subscriptionBundleId`
|
|
875
|
+
* (cancel preview) — the controller enforces this.
|
|
876
|
+
*/
|
|
877
|
+
declare class PreviewSubscriptionBundleDto {
|
|
878
|
+
/** Add preview: BundleVersion to be booked. */
|
|
879
|
+
bundleVersionId?: string;
|
|
880
|
+
/** Cancel preview: existing Bundle booking. */
|
|
881
|
+
subscriptionBundleId?: string;
|
|
882
|
+
/** Add preview only — override analogous to `AddSubscriptionBundleDto`. */
|
|
883
|
+
minimumTermMonths?: number;
|
|
884
|
+
/**
|
|
885
|
+
* Add preview only — the rhythm to quote, analogous to
|
|
886
|
+
* `AddSubscriptionBundleDto`. Defaults to the plan's.
|
|
887
|
+
*
|
|
888
|
+
* The booking has taken this since bundles gained a rhythm of their own.
|
|
889
|
+
* The preview not taking it meant a tenant asking for a monthly bundle on a
|
|
890
|
+
* yearly plan was quoted the yearly one and then charged the monthly one —
|
|
891
|
+
* a preview describing a different contract from the one written, which is
|
|
892
|
+
* the one thing a preview may never do.
|
|
893
|
+
*/
|
|
894
|
+
billingCycle?: 'MONTHLY' | 'YEARLY';
|
|
895
|
+
}
|
|
896
|
+
declare class CancelSubscriptionBundleDto {
|
|
897
|
+
/**
|
|
898
|
+
* Optional — default = `new Date()` server-side. Format: ISO-8601
|
|
899
|
+
* (`YYYY-MM-DD` or full timestamp). Usually not set by the tenant
|
|
900
|
+
* self-service.
|
|
901
|
+
*/
|
|
902
|
+
canceledAt?: string;
|
|
903
|
+
}
|
|
904
|
+
/** Which bundles the store is showing, so their prices can be resolved. */
|
|
905
|
+
declare class BundlePriceLookupDto {
|
|
906
|
+
/**
|
|
907
|
+
* Capped rather than unbounded: the caller names what it is displaying, and
|
|
908
|
+
* a page shows a catalogue, not a database. Each id costs a lookup.
|
|
909
|
+
*/
|
|
910
|
+
bundleVersionIds: string[];
|
|
911
|
+
}
|
|
912
|
+
|
|
572
913
|
/**
|
|
573
914
|
* Builds the `ConfiguratorCatalog` (for onboarding step 3) from the
|
|
574
915
|
* live `plan_versions` (SuperAdmin defines plans + prices) plus the
|
|
@@ -586,14 +927,13 @@ declare class ConfiguratorCatalogBuilder {
|
|
|
586
927
|
}
|
|
587
928
|
|
|
588
929
|
declare const SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN: unique symbol;
|
|
589
|
-
/** Optional config token;
|
|
930
|
+
/** Optional config token; without it a booking commits the tenant to nothing. */
|
|
590
931
|
declare const SUBSCRIPTION_BUNDLE_CONFIG_TOKEN: unique symbol;
|
|
591
932
|
|
|
592
933
|
/**
|
|
593
934
|
* Factory analogous to `buildBundlesController` etc. — the consumer can pass
|
|
594
|
-
* additional guards (e.g. RoleGuard) in addition to the platform
|
|
595
|
-
* `ComposedTenantAuthGuard`.
|
|
935
|
+
* additional guards (e.g. RoleGuard) in addition to the platform defaults.
|
|
596
936
|
*/
|
|
597
937
|
declare function buildTenantSubscriptionBundlesController(extraGuards?: Array<Type<CanActivate>>): Type;
|
|
598
938
|
|
|
599
|
-
export {
|
|
939
|
+
export { AddSubscriptionBundleDto, AuthGuardList, BILLING_FEATURE_UI_REGISTRY_TOKEN, BillingPermissionGuard, type BundleFirstPeriodInput, type BundlePeriodRollInput, type BundlePeriodWindow, type BundlePlanContext, BundlePriceLookupDto, CancelSubscriptionBundleDto, CatalogBundleUpsellResolver, ConfiguratorCatalogBuilder, ContractFreezePort, ContractFreezeSourcePort, DEFAULT_BUNDLE_MINIMUM_TERM_MONTHS, FEATURE_GUARD_CONFIG_TOKEN, FEATURE_GUARD_MARKER, FeatureGuard, type FeatureGuardConfig, LimitExceededFilter, type MarketingFields, type NextPeriodWindow, PLAN_CATALOG_IMPORT_SINK_TOKEN, PLAN_CATALOG_READ_SINK_TOKEN, PLAN_CATALOG_SETTINGS_TOKEN, PLAN_CATALOG_SOURCE_TOKEN, PUBLIC_CATALOG_BUNDLE_REPOSITORY_TOKEN, PUBLIC_CATALOG_MARKETING_REPOSITORY_TOKEN, type PeriodRollInput, type PlanAnchorSource, type PlanCatalogBuildSettings, PlanCatalogImportDto, type PlanCatalogImporterControllerConfig, PlanCatalogImporterModule, type PlanCatalogImporterModuleOptions, PlanCatalogImporterService, PlanCatalogModule, type PlanCatalogModuleOptions, PlanCatalogSource, type PlanResponseEntry, PreviewSubscriptionBundleDto, type PublicBundleEntry, PublicCatalogController, PublicCatalogModule, type PublicCatalogModuleOptions, PublishValidationError, type PublishablePlanVersion, REQUIRE_FEATURE_KEY, type RenewalDecision, type RenewalSubInput, RequireFeature, SUBSCRIPTION_BUNDLE_CONFIG_TOKEN, SUBSCRIPTION_BUNDLE_REPOSITORY_TOKEN, SubscriptionContractFreezeService, UPSELL_OFFER_CURRENCY_TOKEN, UPSELL_OFFER_RESOLVER_TOKEN, advanceOneCycle, assertBaseVersionFresh, assertChangeNote, assertDraftPublishable, assertOptimisticLockHeld, billingAnchorDay, buildPlanCatalogFromSnapshot, buildPlanCatalogImporterController, buildTenantSubscriptionBundlesController, bundleCycleFitsPlan, bundleFirstPeriodEnd, bundleFirstPeriodStart, bundleNextPeriodEnd, clearPendingPlanVersionFields, computeCarriedTrialEndsAt, computeNextBundlePeriod, computeNextPeriod, cyclesSoldFor, decideRenewal, findPlan, getActiveFeatureKeys, getMarketedPlans, getPlanOrThrow, getPlanPriceGross, getPlanPriceNet, getPlanQuota, initialPeriodWindow, isFeatureInPlan, isFeaturePlannedOnly, isPlanNotSoldInCycle, listPriceNet, periodEndAfter, periodEndWithMinLead, planNotSoldInCycle, resolveBundlePriceNet, resolvePlanAnchorDay, retreatOneCycle };
|