@saasicat/core 1.0.0-rc.24 → 1.0.0-rc.26

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/index.d.ts CHANGED
@@ -859,6 +859,18 @@ interface PlanCatalogSubscribers {
859
859
  */
860
860
  customerNumberPrefix?: string;
861
861
  }
862
+ /** `config/saas.yaml#invoicing`: how the invoices issued from the charge journal are numbered and when they are due. */
863
+ interface PlanCatalogInvoicing {
864
+ /** Put in front of every invoice number: `AHP` gives `AHP-2026-000123`. */
865
+ numberPrefix: string;
866
+ /** Days from an invoice's issue date to the day it is due (`SC-PRIC-046`). */
867
+ paymentTermDays: number;
868
+ /**
869
+ * What a business letter of the issuer states beyond the invoice's own
870
+ * content, line by line — copied onto every invoice issued (`SC-PRIC-077`).
871
+ */
872
+ footer?: string[];
873
+ }
862
874
  /** One account at a payment gateway, by the name `PlanCatalogPayments.accounts` gives it. */
863
875
  interface PlanCatalogPaymentAccount {
864
876
  /** The provider its bound adapter names itself as, e.g. `stripe`. */
@@ -878,6 +890,13 @@ interface PlanCatalogPayments {
878
890
  /** Every account taking new payment methods or holding a reference in use. */
879
891
  accounts: Record<string, PlanCatalogPaymentAccount>;
880
892
  }
893
+ /** `config/saas.yaml#tax`: the adapter an installation names, and what the operator declares to it. */
894
+ interface PlanCatalogTax {
895
+ /** The adapter's package name, such as `@saasicat/tax-de`. */
896
+ adapter: string;
897
+ /** The options the adapter's factory builds it with; the factory checks them. */
898
+ options?: Record<string, unknown>;
899
+ }
881
900
  /**
882
901
  * The part of `config/saas.yaml` that is configuration rather than catalogue.
883
902
  *
@@ -890,8 +909,15 @@ interface PlanCatalogSettings {
890
909
  app: PlanCatalogApp;
891
910
  /** ISO-4217 currency code. */
892
911
  currency: string;
893
- /** VAT rate in percent. */
894
- vatRate: number;
912
+ /**
913
+ * VAT rate in percent. Every installation names it unless `tax` names a
914
+ * tax adapter, which is then the one source of every rate.
915
+ */
916
+ vatRate?: number;
917
+ /** The tax adapter that decides each charge's treatment, and its options. */
918
+ tax?: PlanCatalogTax;
919
+ /** The installation's time zone, an IANA name; required with `tax` (`SC-PRIC-045`). */
920
+ timeZone?: string;
895
921
  /** Commercial settings for the tenant self-service routes. */
896
922
  tenantBilling: PlanCatalogTenantBilling;
897
923
  /** App-wide marketing configuration. Optional. */
@@ -902,6 +928,8 @@ interface PlanCatalogSettings {
902
928
  issuer?: PlanCatalogIssuer;
903
929
  /** How subscribers are numbered. Optional. */
904
930
  subscribers?: PlanCatalogSubscribers;
931
+ /** How invoices are numbered and when they are due; named exactly where invoices are issued. */
932
+ invoicing?: PlanCatalogInvoicing;
905
933
  /** The payment gateway accounts. Optional until payment methods are taken. */
906
934
  payments?: PlanCatalogPayments;
907
935
  }
@@ -1008,46 +1036,572 @@ interface PromoCodeValidationLog {
1008
1036
  }
1009
1037
 
1010
1038
  /**
1011
- * A tenant's own limits beside its plan — a negotiated contract, a pilot.
1012
- * `quotas[key]` replaces the plan's value for that quota, `-1` meaning
1013
- * unlimited; `features` adds to the plan's. Stored as JSON in
1014
- * `subscriptions.customLimits`.
1039
+ * A tenant's own limits beside its plan — a negotiated contract, a pilot.
1040
+ * `quotas[key]` replaces the plan's value for that quota, `-1` meaning
1041
+ * unlimited; `features` adds to the plan's. Stored as JSON in
1042
+ * `subscriptions.customLimits`.
1043
+ */
1044
+ interface CustomLimits {
1045
+ quotas?: Record<QuotaKey, number>;
1046
+ features?: FeatureKey[];
1047
+ }
1048
+ /**
1049
+ * The keys a stored `customLimits` value is read by. The normative admin API
1050
+ * schema (`CustomLimits` in `@saasicat/spec`) is held to the same list.
1051
+ */
1052
+ declare const CUSTOM_LIMITS_KEYS: readonly ["quotas", "features"];
1053
+ /** What a stored `customLimits` value gave, and what in it went unread. */
1054
+ interface ReadCustomLimits {
1055
+ limits: CustomLimits | null;
1056
+ /**
1057
+ * The parts of the stored value the platform does not read — a key it does
1058
+ * not know, or a `quotas`/`features` of the wrong kind. Empty when every
1059
+ * part was read.
1060
+ */
1061
+ unread: string[];
1062
+ }
1063
+ /**
1064
+ * Reads a stored `customLimits` value the way the platform applies it.
1065
+ *
1066
+ * A shape the platform does not read is reported rather than refused: the
1067
+ * tenant keeps its plan's limits and goes on working, as a limit nothing can
1068
+ * count does not block anybody (`SC-ENTL-010`), and the caller says out loud
1069
+ * which limits were not applied. Each quota value goes through
1070
+ * `readQuotaValue`, the one reading of a quota in JSON.
1071
+ */
1072
+ declare function readCustomLimits(value: unknown): ReadCustomLimits;
1073
+ /**
1074
+ * Reads stored limits for an adapter, and reports what went unread once per
1075
+ * subscription — every entitlement read passes through here, so reporting on
1076
+ * each would bury the one line an operator needs under thousands.
1077
+ */
1078
+ declare function customLimitsReader(report: (line: string) => void): (subscriptionId: string, stored: unknown) => CustomLimits | null;
1079
+
1080
+ /** Alias for historical compatibility — equivalent to VersionChangeDirection. */
1081
+ type ChangeDirection = VersionChangeDirection;
1082
+ interface DiffResult {
1083
+ nonRegressive: boolean;
1084
+ changes: VersionChange[];
1085
+ }
1086
+ /**
1087
+ * `Decimal | string | number` — the three forms in which prices appear
1088
+ * in the platform. `Decimal` is the Prisma class (with
1089
+ * `.toNumber()`), which is not imported directly in order to keep the
1090
+ * platform Prisma-free — a structural view suffices.
1091
+ */
1092
+ type DecimalLike = number | string | {
1093
+ toNumber(): number;
1094
+ };
1095
+ interface PlanVersionFields {
1096
+ features: FeatureKey[];
1097
+ /**
1098
+ * Quotas of the version. -1 = unlimited; missing key = 0.
1099
+ *
1100
+ * Every key either side carries is compared. Which keys exist is the
1101
+ * installation's decision — they come from `@DefinesQuota` — so a fixed
1102
+ * set here would have compared the three the platform happened to know by
1103
+ * name and let every other one be lowered without the confirmation
1104
+ * publishing a regression asks for.
1105
+ */
1106
+ quotas: Record<QuotaKey, number>;
1107
+ monthlyNet: DecimalLike;
1108
+ yearlyNet: DecimalLike;
1109
+ }
1110
+ interface BundleVersionFields {
1111
+ features: FeatureKey[];
1112
+ /** Quota contributions of the bundle. -1 = unlimited; missing key = 0. */
1113
+ quotas: Record<QuotaKey, number>;
1114
+ /** Default pricing; null = only override pricing possible. */
1115
+ monthlyNet: DecimalLike | null;
1116
+ yearlyNet: DecimalLike | null;
1117
+ }
1118
+ declare function classifyPlanDiff(oldV: PlanVersionFields, newV: PlanVersionFields): DiffResult;
1119
+ /**
1120
+ * Classification of a BundleVersion diff for contract protection.
1121
+ *
1122
+ * Quotas are compared exactly as they are for a plan.
1123
+ *
1124
+ * Pricing can be `null` (the bundle only has override pricing); a switch
1125
+ * from value ↔ null is classified as REGRESSION (value dropped) or IMPROVEMENT
1126
+ * (value added, lowerIsBetter inverted). Both null
1127
+ * stay NEUTRAL.
1128
+ */
1129
+ declare function classifyBundleVersionDiff(oldV: BundleVersionFields, newV: BundleVersionFields): DiffResult;
1130
+
1131
+ /**
1132
+ * - `improvement` — nothing dearer in any rhythm, no feature missing, no quota
1133
+ * lower. Taken at once and free, the term kept.
1134
+ * - `more-for-more` — nothing missing or lower, but dearer in at least one
1135
+ * rhythm. Taken like an upgrade: at once, the difference prorated. The rule
1136
+ * reads every rhythm, so in the subscriber's own the difference can be zero
1137
+ * or below — monthly 49 to 45 with yearly 490 to 500 is more for more for a
1138
+ * monthly subscriber too.
1139
+ * - `takes-something-away` — a feature missing or a quota lower, whatever the
1140
+ * price. Taken like a downgrade: at the end of the running term, and only
1141
+ * when the subscriber asks for it.
1142
+ * - `same` — the two agree on everything compared: there is nothing to offer.
1143
+ */
1144
+ type VersionOfferClass = 'improvement' | 'more-for-more' | 'takes-something-away' | 'same';
1145
+ /**
1146
+ * A version as the comparison reads it. A price may be absent: that is a
1147
+ * rhythm the version is not sold in, and a rhythm that stops being sold counts
1148
+ * against the candidate like a price that went up.
1149
+ */
1150
+ type VersionOfferFields = BundleVersionFields;
1151
+ interface VersionOffer {
1152
+ readonly class: VersionOfferClass;
1153
+ /** Every difference, bound to candidate, as the catalogue's diff states it. */
1154
+ readonly changes: readonly VersionChange[];
1155
+ }
1156
+ /** One version as a subscriber compares it. A price of null is a rhythm it is not sold in. */
1157
+ interface VersionOfferSide {
1158
+ readonly planVersionId: string;
1159
+ readonly version: number;
1160
+ readonly features: readonly string[];
1161
+ readonly quotas: Readonly<Record<string, number>>;
1162
+ readonly monthlyNet: number | null;
1163
+ readonly yearlyNet: number | null;
1164
+ /** The last day it is sold on, where a successor closes its window — ISO, the whole day included. */
1165
+ readonly validUntil: string | null;
1166
+ /** When an operator ended it, where one did — ISO; from then it is not sold. */
1167
+ readonly endsAt: string | null;
1168
+ }
1169
+ /**
1170
+ * A newer version of the subscriber's plan, offered: the version bound and the
1171
+ * one offered side by side, what kind of offer it is, and when a switch taken
1172
+ * now would take effect — at once, or, for one that takes something away, at
1173
+ * the end of the running term. `GET billing/version-offer` answers with it.
1174
+ */
1175
+ interface VersionOfferView {
1176
+ readonly plan: string;
1177
+ readonly bound: VersionOfferSide;
1178
+ readonly offered: VersionOfferSide;
1179
+ readonly class: Exclude<VersionOfferClass, 'same'>;
1180
+ readonly changes: readonly VersionChange[];
1181
+ /** ISO instant. */
1182
+ readonly takesEffectAt: string;
1183
+ }
1184
+ /**
1185
+ * What taking an offer did: the version left and the one taken, and whether it
1186
+ * applied at once or waits for `takesEffectAt`. `POST billing/version-offer/accept`
1187
+ * answers with it.
1188
+ */
1189
+ interface VersionSwitchResult {
1190
+ readonly class: Exclude<VersionOfferClass, 'same'>;
1191
+ readonly fromPlanVersionId: string;
1192
+ readonly planVersionId: string;
1193
+ readonly immediate: boolean;
1194
+ /** ISO instant. */
1195
+ readonly takesEffectAt: string;
1196
+ }
1197
+ /**
1198
+ * Classifies `candidate` against `bound`. The comparison is the catalogue's
1199
+ * own diff — prices with a rhythm that may be absent, as a bundle version's
1200
+ * are — so an offer and a published version describe their changes alike.
1201
+ */
1202
+ declare function classifyVersionOffer(bound: VersionOfferFields, candidate: VersionOfferFields): VersionOffer;
1203
+
1204
+ /** A version, as an announcement names it. */
1205
+ interface RetiredVersionRef {
1206
+ readonly planVersionId: string;
1207
+ readonly planKey: string;
1208
+ readonly version: number;
1209
+ }
1210
+ /** An announcement as it is kept: one per retirement, whoever it reaches. */
1211
+ interface VersionRetirementRecord {
1212
+ readonly id: string;
1213
+ /** The version retired. */
1214
+ readonly retired: RetiredVersionRef;
1215
+ /** The version its subscriptions continue on, named by the operator. */
1216
+ readonly replacement: RetiredVersionRef;
1217
+ readonly announcedAt: Date;
1218
+ /** Who announced it — an actor tag, as the audit log writes it. */
1219
+ readonly announcedBy: string;
1220
+ }
1221
+ type NewVersionRetirement = Omit<VersionRetirementRecord, 'id'>;
1222
+ /**
1223
+ * The manifest capability that announces retiring a version. The platform sets
1224
+ * it where it serves the routes, and the admin UI offers the action only where
1225
+ * it is set. Whether the operator's terms allow it is a blocker the preview
1226
+ * reports, so an operator who has not confirmed them learns what is missing.
1227
+ */
1228
+ declare const VERSION_RETIREMENT_CAPABILITY = "planVersions.retire";
1229
+ /**
1230
+ * One of the two versions of a retirement, as the subscriber compares them:
1231
+ * price in each rhythm, quotas and features, with the plan it belongs to — the
1232
+ * replacement may be a version of another plan.
1233
+ */
1234
+ interface RetirementSide extends VersionOfferSide {
1235
+ readonly planKey: string;
1236
+ }
1237
+ /**
1238
+ * A plan version a subscription is on is being retired, and the subscription
1239
+ * continues on the named replacement from its effective date. Sent once per
1240
+ * subscription and retirement, when the retirement is announced, and kept as
1241
+ * it was sent: the record shows what the subscriber was told, prices included.
1242
+ */
1243
+ interface VersionRetiredNotice {
1244
+ readonly kind: 'version-retired';
1245
+ readonly tenantId: string;
1246
+ readonly subscriptionId: string;
1247
+ readonly retirementId: string;
1248
+ readonly retired: RetirementSide;
1249
+ readonly replacement: RetirementSide;
1250
+ /** Every difference, retired to replacement, as the catalogue's diff states it. */
1251
+ readonly changes: readonly VersionChange[];
1252
+ /**
1253
+ * The rhythm the subscription is billed in when the retirement takes
1254
+ * effect — a change of rhythm scheduled by then included — which says
1255
+ * which of the two prices applies.
1256
+ */
1257
+ readonly billingCycle: string;
1258
+ /** ISO 8601. */
1259
+ readonly effectiveAt: string;
1260
+ /** ISO date (`YYYY-MM-DD`), UTC. */
1261
+ readonly lastDayToCancel: string;
1262
+ }
1263
+ /**
1264
+ * The one reminder of a retirement, where staying put costs the subscription
1265
+ * something (`SC-SUB-034`): sent 14 days before the retirement takes effect,
1266
+ * once per subscription and retired version. It says again what the
1267
+ * retirement notice said, and what a switch taken now would cost. Its
1268
+ * `billingCycle` is still the rhythm billed at the date, read again from the
1269
+ * subscription as it stands when the reminder is sent: a change of rhythm
1270
+ * scheduled since the announcement is in it.
1271
+ */
1272
+ interface VersionRetirementReminder extends Omit<VersionRetiredNotice, 'kind'> {
1273
+ readonly kind: 'version-retirement-reminder';
1274
+ /**
1275
+ * What switching to the replacement now costs in the subscription's rhythm
1276
+ * (`SC-SUB-032`), or null where it cannot switch now: in a trial, with a
1277
+ * change scheduled, or where either plan is held for a special contract.
1278
+ */
1279
+ readonly switchTerms: RetirementSwitchTerms | null;
1280
+ }
1281
+ /**
1282
+ * Why a retirement does not reach a subscription on the version:
1283
+ *
1284
+ * - `ended` — the subscription is over.
1285
+ * - `cancelled-before` — its cancellation lands by the effective date.
1286
+ * - `changes-before` — a scheduled change moves it to another plan, or to
1287
+ * another version of this one, by then.
1288
+ * - `no-term` — it carries no date a term can be counted from.
1289
+ * - `already-told` — an earlier announcement of this version reached it, and
1290
+ * stands.
1291
+ */
1292
+ type RetirementSkipReason = 'ended' | 'cancelled-before' | 'changes-before' | 'no-term' | 'already-told';
1293
+ /** A refusal as the preview reports it and the announcement throws it. */
1294
+ interface RetirementBlocker {
1295
+ readonly code: string;
1296
+ readonly message: string;
1297
+ readonly params: Record<string, string | number>;
1298
+ }
1299
+ /** A subscription a retirement reaches, with what it means there. */
1300
+ interface RetirementReachedRow {
1301
+ readonly tenantId: string;
1302
+ readonly subscriptionId: string;
1303
+ readonly status: string;
1304
+ /** The rhythm it is billed in when the retirement takes effect. */
1305
+ readonly billingCycle: string;
1306
+ /** ISO 8601. */
1307
+ readonly effectiveAt: string;
1308
+ /** ISO date, UTC. */
1309
+ readonly lastDayToCancel: string;
1310
+ /** Reached by another retirement within the last twelve months: the announcement waits. */
1311
+ readonly reachedRecently: boolean;
1312
+ }
1313
+ /** A subscription on the version a retirement does not reach, and why. */
1314
+ interface RetirementSkippedRow {
1315
+ readonly tenantId: string;
1316
+ readonly subscriptionId: string;
1317
+ readonly reason: RetirementSkipReason;
1318
+ }
1319
+ /** What announcing a retirement now would do. */
1320
+ interface RetirementPreview {
1321
+ readonly retired: RetirementSide;
1322
+ readonly replacement: RetirementSide;
1323
+ /** Every difference, retired to replacement, as the subscribers will be told it. */
1324
+ readonly changes: readonly VersionChange[];
1325
+ /** ISO 8601: the moment it is computed for. */
1326
+ readonly asOf: string;
1327
+ readonly reached: readonly RetirementReachedRow[];
1328
+ readonly skipped: readonly RetirementSkippedRow[];
1329
+ /** Empty where it may be announced as it stands. */
1330
+ readonly blockers: readonly RetirementBlocker[];
1331
+ }
1332
+ /** What an announcement did. */
1333
+ interface RetirementAnnounced {
1334
+ readonly retirement: VersionRetirementRecord;
1335
+ /** Notices sent now; the rest are sent by the next run. */
1336
+ readonly told: number;
1337
+ readonly failed: number;
1338
+ }
1339
+ /**
1340
+ * Why a notice not told yet waits — for a subscription a plan version's
1341
+ * retirement reached, or a booking an add-on version's did. The run that sends
1342
+ * the notices asks the same questions in the same order.
1343
+ */
1344
+ interface NotToldReasons {
1345
+ /**
1346
+ * What the notice announces would not fit at its date: the replacement's
1347
+ * plan could not carry the add-ons held then, or the replacement add-on
1348
+ * could not run beside a plan the booking meets. It waits until it would.
1349
+ */
1350
+ readonly doesNotFit: number;
1351
+ /** Told of another retirement within the last twelve months: it waits until those are over. */
1352
+ readonly twelveMonths: number;
1353
+ /**
1354
+ * No longer reached: something of the subscriber's own — a change taking
1355
+ * it off the version, a cancellation landing first — came before the date.
1356
+ * It will not be sent.
1357
+ */
1358
+ readonly noLongerReached: number;
1359
+ /** Nothing holds it back: it has reached nobody yet, and the next run tries again. */
1360
+ readonly nobodyYet: number;
1361
+ }
1362
+ /**
1363
+ * How far a retirement has come, counted over the subscriptions — or, for an
1364
+ * add-on version, the bookings — it reached (`SC-SUB-033`, `SC-BUN-053`).
1365
+ */
1366
+ interface RetirementProgress {
1367
+ /** Off the retired version: moved at their date, switched early, or changed on their own. */
1368
+ readonly moved: number;
1369
+ /** Still on the retired version, before their date. */
1370
+ readonly waiting: number;
1371
+ /** Still on the retired version after their date: the run has not moved them yet, or cannot. */
1372
+ readonly overdue: number;
1373
+ /**
1374
+ * Still on the retired version, and ended by their date: there is nothing
1375
+ * to move. A booking that ended after its date, before a move came, counts
1376
+ * here too: nothing moves a booking that has ended.
1377
+ */
1378
+ readonly ended: number;
1379
+ /**
1380
+ * Still on the retired version, and not told yet: their notice has reached
1381
+ * nobody, so their date is not set and nothing moves them (`SC-SUB-038`).
1382
+ */
1383
+ readonly notTold: number;
1384
+ /**
1385
+ * Why those counted `notTold` are not told yet, each counted once. Absent
1386
+ * from a server that does not count them.
1387
+ */
1388
+ readonly notToldReasons?: NotToldReasons;
1389
+ /**
1390
+ * Reminded 14 days before their date (`SC-SUB-034`) — counted beside the
1391
+ * four above, not instead of one: a subscription reminded moves later.
1392
+ */
1393
+ readonly reminded: number;
1394
+ }
1395
+ /** An announcement as the administration lists it, with how far it has come. */
1396
+ interface VersionRetirementView extends VersionRetirementRecord {
1397
+ readonly progress: RetirementProgress;
1398
+ }
1399
+ /** What one run of the moves at the date did. */
1400
+ interface RetirementMoveRun {
1401
+ /**
1402
+ * Subscriptions, or bookings, moved onto their replacement, with the
1403
+ * contract the move writes.
1404
+ */
1405
+ readonly moved: number;
1406
+ /** Moves that could not be made, and are tried again by the next run. */
1407
+ readonly failed: number;
1408
+ }
1409
+ /** What switching to the replacement before the date did (`SC-SUB-032`). */
1410
+ interface RetirementSwitchResult {
1411
+ readonly fromPlanVersionId: string;
1412
+ readonly planVersionId: string;
1413
+ /** ISO date of the last day the price is held, or null where none is. */
1414
+ readonly heldUntilDay: string | null;
1415
+ }
1416
+
1417
+ /** What the switch costs, per period of the subscriber's rhythm. */
1418
+ interface RetirementSwitchTerms {
1419
+ /** The replacement's net price. */
1420
+ readonly priceNet: number;
1421
+ /**
1422
+ * Where the replacement costs more: the price the subscriber goes on paying,
1423
+ * the difference that is held, and the last day it is held — the day before
1424
+ * the date they were told. Null where it costs the same or less: its price
1425
+ * then applies from the subscriber's next period.
1426
+ */
1427
+ readonly held: {
1428
+ readonly priceNet: number;
1429
+ readonly amountNet: number;
1430
+ readonly lastDay: string;
1431
+ } | null;
1432
+ }
1433
+ /**
1434
+ * A side of a retirement as its prices are compared: what it costs in each
1435
+ * rhythm, for a plan version or for an add-on version beside a plan.
1015
1436
  */
1016
- interface CustomLimits {
1017
- quotas?: Record<QuotaKey, number>;
1018
- features?: FeatureKey[];
1437
+ type RetirementPricedSide = Pick<RetirementSide, 'monthlyNet' | 'yearlyNet'>;
1438
+ /**
1439
+ * What switching to the replacement now costs in `billingCycle`, or null where
1440
+ * the replacement has no price in it. A plan version's retirement and an add-on
1441
+ * version's are priced alike: each side by what it costs in each rhythm.
1442
+ */
1443
+ declare function retirementSwitchTerms(notice: {
1444
+ readonly retired: RetirementPricedSide;
1445
+ readonly replacement: RetirementPricedSide;
1446
+ readonly lastDayToCancel: string;
1447
+ }, billingCycle: string): RetirementSwitchTerms | null;
1448
+
1449
+ /** An add-on version, as an announcement names it. */
1450
+ interface RetiredBundleVersionRef {
1451
+ readonly bundleVersionId: string;
1452
+ readonly bundleKey: string;
1453
+ readonly version: number;
1454
+ }
1455
+ /** An add-on retirement as it is kept: one per announcement, whichever bookings it reaches. */
1456
+ interface BundleVersionRetirementRecord {
1457
+ readonly id: string;
1458
+ /** The version retired. */
1459
+ readonly retired: RetiredBundleVersionRef;
1460
+ /** The version of the same add-on its bookings continue on, named by the operator. */
1461
+ readonly replacement: RetiredBundleVersionRef;
1462
+ readonly announcedAt: Date;
1463
+ /** Who announced it — an actor tag, as the audit log writes it. */
1464
+ readonly announcedBy: string;
1019
1465
  }
1466
+ type NewBundleVersionRetirement = Omit<BundleVersionRetirementRecord, 'id'>;
1020
1467
  /**
1021
- * The keys a stored `customLimits` value is read by. The normative admin API
1022
- * schema (`CustomLimits` in `@saasicat/spec`) is held to the same list.
1468
+ * The manifest capability that announces retiring an add-on version. The
1469
+ * platform sets it where it serves the routes, and the admin UI offers the
1470
+ * action only where it is set.
1023
1471
  */
1024
- declare const CUSTOM_LIMITS_KEYS: readonly ["quotas", "features"];
1025
- /** What a stored `customLimits` value gave, and what in it went unread. */
1026
- interface ReadCustomLimits {
1027
- limits: CustomLimits | null;
1028
- /**
1029
- * The parts of the stored value the platform does not read — a key it does
1030
- * not know, or a `quotas`/`features` of the wrong kind. Empty when every
1031
- * part was read.
1032
- */
1033
- unread: string[];
1472
+ declare const BUNDLE_VERSION_RETIREMENT_CAPABILITY = "bundleVersions.retire";
1473
+ /**
1474
+ * An add-on version as the subscriber compares it with another — the one
1475
+ * retired and its replacement, or the one booked and the one offered: what it
1476
+ * grants, and its price beside the subscription's plan in each rhythm —
1477
+ * `pricingOverrides` included — or null where it has none there.
1478
+ */
1479
+ interface BundleVersionSide {
1480
+ readonly bundleVersionId: string;
1481
+ readonly bundleKey: string;
1482
+ readonly label: string;
1483
+ readonly version: number;
1484
+ readonly features: readonly string[];
1485
+ readonly quotas: Readonly<Record<string, number>>;
1486
+ readonly monthlyNet: number | null;
1487
+ readonly yearlyNet: number | null;
1034
1488
  }
1035
1489
  /**
1036
- * Reads a stored `customLimits` value the way the platform applies it.
1490
+ * An add-on version a booking is on is being retired, and the booking
1491
+ * continues on the named replacement from its effective date. Sent once per
1492
+ * booking and retirement, and kept as it was sent: the record shows what the
1493
+ * subscriber was told, prices included.
1494
+ */
1495
+ interface BundleVersionRetiredNotice {
1496
+ readonly kind: 'bundle-version-retired';
1497
+ readonly tenantId: string;
1498
+ readonly subscriptionId: string;
1499
+ readonly subscriptionBundleId: string;
1500
+ readonly retirementId: string;
1501
+ /** The plan the add-on runs beside at the date, which both sides are priced for. */
1502
+ readonly planKey: string;
1503
+ readonly retired: BundleVersionSide;
1504
+ readonly replacement: BundleVersionSide;
1505
+ /** Every difference, retired to replacement, at the prices for `planKey`. */
1506
+ readonly changes: readonly VersionChange[];
1507
+ /** The rhythm the booking is billed in, which says which of the two prices applies. */
1508
+ readonly billingCycle: string;
1509
+ /** ISO 8601. */
1510
+ readonly effectiveAt: string;
1511
+ /** ISO date (`YYYY-MM-DD`), UTC: the last day it may be cancelled without its minimum term. */
1512
+ readonly lastDayToCancel: string;
1513
+ }
1514
+ /**
1515
+ * Why an add-on retirement does not reach a booking of the version:
1037
1516
  *
1038
- * A shape the platform does not read is reported rather than refused: the
1039
- * tenant keeps its plan's limits and goes on working, as a limit nothing can
1040
- * count does not block anybody (`SC-ENTL-010`), and the caller says out loud
1041
- * which limits were not applied. Each quota value goes through
1042
- * `readQuotaValue`, the one reading of a quota in JSON.
1517
+ * - `ended` — the subscription is over, or the booking's cancellation has
1518
+ * already landed.
1519
+ * - `cancelled-before` — the booking's cancellation lands by the effective date.
1520
+ * - `changes-before` — the booking's switch to a newer version, taken for the
1521
+ * end of its term, lands by the effective date.
1522
+ * - `no-term` — it carries no period an end can be counted from.
1523
+ * - `already-told` — an earlier announcement of this version reached it, and
1524
+ * stands.
1043
1525
  */
1044
- declare function readCustomLimits(value: unknown): ReadCustomLimits;
1526
+ type BundleRetirementSkipReason = 'ended' | 'cancelled-before' | 'changes-before' | 'no-term' | 'already-told';
1045
1527
  /**
1046
- * Reads stored limits for an adapter, and reports what went unread once per
1047
- * subscription — every entitlement read passes through here, so reporting on
1048
- * each would bury the one line an operator needs under thousands.
1528
+ * What switching a booking to its retirement's replacement now would cost
1529
+ * (`SC-BUN-054`), in the rhythm the booking is billed in now — its own, or
1530
+ * else its plan's — which need not be the one its notice was told in.
1049
1531
  */
1050
- declare function customLimitsReader(report: (line: string) => void): (subscriptionId: string, stored: unknown) => CustomLimits | null;
1532
+ interface BundleRetirementSwitchTerms extends RetirementSwitchTerms {
1533
+ readonly billingCycle: string;
1534
+ }
1535
+ /**
1536
+ * The one reminder of an add-on retirement (`SC-BUN-056`): sent once, 14 days
1537
+ * before the booking's date, where staying put costs it something. It says
1538
+ * again what the booking was told, with both versions priced for the plan the
1539
+ * add-on runs beside at the date as the catalogue prices them when it is sent,
1540
+ * in the rhythm the booking is billed in then.
1541
+ */
1542
+ interface BundleVersionRetirementReminder extends Omit<BundleVersionRetiredNotice, 'kind'> {
1543
+ readonly kind: 'bundle-version-retirement-reminder';
1544
+ /** What switching to the replacement now costs (`SC-BUN-054`), or null where the booking cannot. */
1545
+ readonly switchTerms: BundleRetirementSwitchTerms | null;
1546
+ }
1547
+ /** What switching a booking to its retirement's replacement before the date did. */
1548
+ interface BundleRetirementSwitchResult {
1549
+ readonly subscriptionBundleId: string;
1550
+ readonly fromBundleVersionId: string;
1551
+ readonly bundleVersionId: string;
1552
+ /** ISO date of the last day the price is held, or null where none is. */
1553
+ readonly heldUntilDay: string | null;
1554
+ }
1555
+ /** A booking an add-on retirement reaches, with what it means there. */
1556
+ interface BundleRetirementReachedRow {
1557
+ readonly tenantId: string;
1558
+ readonly subscriptionId: string;
1559
+ readonly subscriptionBundleId: string;
1560
+ /** The plan the add-on runs beside when the retirement takes effect. */
1561
+ readonly planKey: string;
1562
+ /** The rhythm the plan is billed in then. */
1563
+ readonly planCycle: string;
1564
+ /** The rhythm the booking is billed in. */
1565
+ readonly billingCycle: string;
1566
+ /** ISO 8601. */
1567
+ readonly effectiveAt: string;
1568
+ /** ISO date, UTC. */
1569
+ readonly lastDayToCancel: string;
1570
+ /** Its subscription was reached by a retirement within the last twelve months: the announcement waits. */
1571
+ readonly reachedRecently: boolean;
1572
+ }
1573
+ /** A booking of the version an add-on retirement does not reach, and why. */
1574
+ interface BundleRetirementSkippedRow {
1575
+ readonly tenantId: string;
1576
+ readonly subscriptionId: string;
1577
+ readonly subscriptionBundleId: string;
1578
+ readonly reason: BundleRetirementSkipReason;
1579
+ }
1580
+ /** What announcing an add-on retirement now would do. */
1581
+ interface BundleRetirementPreview {
1582
+ /** The two versions at their own prices, before any plan's override. */
1583
+ readonly retired: BundleVersionSide;
1584
+ readonly replacement: BundleVersionSide;
1585
+ /** Every difference at those prices; each booking is told the one at its plan's. */
1586
+ readonly changes: readonly VersionChange[];
1587
+ /** ISO 8601: the moment it is computed for. */
1588
+ readonly asOf: string;
1589
+ readonly reached: readonly BundleRetirementReachedRow[];
1590
+ readonly skipped: readonly BundleRetirementSkippedRow[];
1591
+ /** Empty where it may be announced as it stands. */
1592
+ readonly blockers: readonly RetirementBlocker[];
1593
+ }
1594
+ /** What an add-on announcement did. */
1595
+ interface BundleRetirementAnnounced {
1596
+ readonly retirement: BundleVersionRetirementRecord;
1597
+ /** Notices sent now; the rest are sent by the next run. */
1598
+ readonly told: number;
1599
+ readonly failed: number;
1600
+ }
1601
+ /** An add-on announcement as the administration lists it, with how far it has come. */
1602
+ interface BundleVersionRetirementView extends BundleVersionRetirementRecord {
1603
+ readonly progress: RetirementProgress;
1604
+ }
1051
1605
 
1052
1606
  type SubscriptionStatus = 'TRIAL' | 'ACTIVE' | 'PAST_DUE' | 'CANCELED' | 'PENDING_SALES';
1053
1607
  interface Subscription {
@@ -1145,9 +1699,11 @@ interface VersionedEntityBase {
1145
1699
  * their own minimum term + their own cancellation (user requirement from
1146
1700
  * P11.7.3).
1147
1701
  *
1148
- * - `bundleVersionId` binds the booking to an *exact* BundleVersion
1149
- * (immutable; bundle updates only take effect after a new version with its
1150
- * own migration).
1702
+ * - `bundleVersionId` binds the booking to an *exact* BundleVersion. A newer
1703
+ * version of the add-on does not change it by being published: an add-on
1704
+ * retirement moves the booking onto its replacement at its date
1705
+ * (`SC-BUN-049`), and the subscriber moves it by taking a newer version
1706
+ * offered (`SC-BUN-058`).
1151
1707
  * - `startedAt` is the contract start of this booking.
1152
1708
  * - `minimumTermEndsAt` = end of the minimum term; `null` = no minimum term
1153
1709
  * (platform default = no commitment, set service-side).
@@ -1179,6 +1735,15 @@ interface SubscriptionBundleRecord {
1179
1735
  billingCycle: BillingCycle | null;
1180
1736
  currentPeriodStart: Date | null;
1181
1737
  currentPeriodEnd: Date | null;
1738
+ /**
1739
+ * A switch to another version of the add-on, taken for the end of the
1740
+ * booking's running term because it takes something away (`SC-BUN-058`):
1741
+ * the version the booking continues on, and from when. Both null where
1742
+ * none is scheduled. The run that makes the switch at that moment clears
1743
+ * them; until then the booking stays on `bundleVersionId`.
1744
+ */
1745
+ pendingBundleVersionId: string | null;
1746
+ pendingVersionEffectiveAt: Date | null;
1182
1747
  createdAt: Date;
1183
1748
  updatedAt: Date;
1184
1749
  }
@@ -1202,6 +1767,18 @@ interface SubscriptionBundleView extends SubscriptionBundleRecord {
1202
1767
  * defect: a field called `monthlyNet` on a yearly booking cannot be right.
1203
1768
  */
1204
1769
  priceNet: number | null;
1770
+ /**
1771
+ * The number of the version a switch scheduled for the end of the booking's
1772
+ * term moves it to (`pendingBundleVersionId`); null where none is
1773
+ * scheduled.
1774
+ */
1775
+ pendingVersion?: number | null;
1776
+ /**
1777
+ * The retirement of the add-on version this booking is on, as the
1778
+ * subscriber was told it (`SC-BUN-046`); absent or null where none was
1779
+ * told, or once the booking is on another version.
1780
+ */
1781
+ retirement?: BundleVersionRetiredNotice | null;
1205
1782
  }
1206
1783
  interface CreateSubscriptionBundleData {
1207
1784
  subscriptionId: string;
@@ -1580,7 +2157,7 @@ interface AdminManifest {
1580
2157
  actions?: AuditActionDef[];
1581
2158
  };
1582
2159
  }
1583
- type StandardPageKey = 'dashboard' | 'tenants' | 'subscriptions' | 'promoCodes' | 'plans' | 'audit' | 'users' | 'pilots' | 'discovery' | 'bundles' | 'marketingCatalog' | 'platformEmail' | 'platformEmailHistory' | 'settings' | 'maintenance';
2160
+ type StandardPageKey = 'dashboard' | 'tenants' | 'subscriptions' | 'promoCodes' | 'plans' | 'audit' | 'users' | 'pilots' | 'discovery' | 'bundles' | 'marketingCatalog' | 'platformEmail' | 'platformEmailHistory' | 'settings' | 'maintenance' | 'featureWithdrawals';
1584
2161
  interface StandardPageDef {
1585
2162
  enabled: boolean;
1586
2163
  requiredCapability?: CapabilityKey;
@@ -1868,6 +2445,35 @@ declare function grossFromNet(net: number, vatRate: number): number;
1868
2445
  declare function netFromGross(gross: number, vatRate: number): number;
1869
2446
  /** The tax a gross amount includes at a rate in per cent: gross × rate / (100 + rate). */
1870
2447
  declare function computeIncludedVat(gross: number, vatRate: number): number;
2448
+ /** One tax rate's part of an invoice: the net of its lines and the tax on that net. */
2449
+ interface TaxAtRate {
2450
+ /** In per cent. */
2451
+ rate: number;
2452
+ net: number;
2453
+ tax: number;
2454
+ }
2455
+ /** An invoice's tax, rate by rate, and the totals it states. */
2456
+ interface TaxPerRate {
2457
+ /** One entry per rate the lines carry, the lowest rate first. */
2458
+ rates: TaxAtRate[];
2459
+ net: number;
2460
+ tax: number;
2461
+ gross: number;
2462
+ }
2463
+ /**
2464
+ * An invoice's tax by the rule of EN 16931: the lines' net is summed per rate,
2465
+ * the rate is applied to each sum and rounded once, and the gross is the net
2466
+ * plus that tax.
2467
+ *
2468
+ * Never the sum of the lines' own rounded taxes, which can differ by cents —
2469
+ * ten lines of 12.34 at 19 % carry 2.34 each, 23.40 together, while 19 % of
2470
+ * 123.40 is 23.45 — and an electronic invoice whose stated tax differs from
2471
+ * the one its format computes is rejected.
2472
+ */
2473
+ declare function taxPerRate(lines: readonly {
2474
+ net: number;
2475
+ rate: number;
2476
+ }[]): TaxPerRate;
1871
2477
 
1872
2478
  type CheckoutOfferLineItemKind = 'plan' | 'bundle' | 'discount';
1873
2479
  /** Frozen billable line item in the offer. */
@@ -2176,6 +2782,12 @@ interface PublicMarketingCatalogResponse {
2176
2782
  currency: string;
2177
2783
  /** VAT rate in percent — for the CheckoutOffer price breakdown. */
2178
2784
  vatRate: number;
2785
+ /**
2786
+ * The country the rate applies to: the issuer's, where a tax adapter
2787
+ * decides and a subscriber elsewhere may be charged another — the page says
2788
+ * so (`SC-PRIC-037`). `null` where the one rate of the file applies to all.
2789
+ */
2790
+ vatRateShownFor: string | null;
2179
2791
  /** What a new payment method is taken with here, if one is taken. */
2180
2792
  newPaymentMethods: PublicNewPaymentMethods;
2181
2793
  /** Visible, marketed plans — sorted by `priority` DESC. */
@@ -2299,171 +2911,64 @@ interface DiscoverySnapshot {
2299
2911
  }
2300
2912
 
2301
2913
  interface EffectiveLimitsSnapshot {
2302
- plan: string;
2303
- quotas: Record<string, number>;
2304
- features: string[];
2305
- /**
2306
- * The add-ons a contract left out of these entitlements because their
2307
- * cancellation was already declared when it was written. Each is granted by
2308
- * its booking until its effective date, and by this snapshot never.
2309
- *
2310
- * Absent where nothing was left out — and on a snapshot written by an
2311
- * application, or before the platform left anything out, which then counts
2312
- * every add-on it names as its own.
2313
- */
2314
- leftOutBundleVersionIds?: string[];
2315
- }
2316
-
2317
- interface FeatureUiMeta {
2318
- /** Visible label for plan comparison tables, add-on lists. */
2319
- label: string;
2320
- /** Long description for tooltips, add-on cards. */
2321
- description: string;
2322
- /** Quasar icon name (e.g. 'directions_car', 'groups'). */
2323
- icon: string;
2324
- /** Mirror of `PlanCatalog.features[].plannedOnly` — cache for the UI without a catalog roundtrip. */
2325
- plannedOnly?: boolean;
2326
- /** true = base infrastructure, included in every plan (not bookable). */
2327
- core?: boolean;
2328
- }
2329
- /** Map FeatureKey → UI metadata. Consumer apps supply a complete table. */
2330
- type FeatureUiRegistry = Record<string, FeatureUiMeta>;
2331
-
2332
- /** A quota as a finite number, or `null` where the value cannot be read as one. */
2333
- declare function readQuotaValue(value: unknown): number | null;
2334
- /**
2335
- * Every quota in a JSON column, for a caller that computes with them.
2336
- *
2337
- * A key that is there stays there. Dropping an unreadable one made it *absent*,
2338
- * and absent means undeclared: `enforceLimit` answers an undeclared dimension
2339
- * with a 500, so every operation on that quota was refused — a fail-closed
2340
- * answer to somebody else's corrupt row.
2341
- */
2342
- declare function readQuotaRecord(value: unknown): Record<string, number>;
2343
-
2344
- /** Alias for historical compatibility — equivalent to VersionChangeDirection. */
2345
- type ChangeDirection = VersionChangeDirection;
2346
- interface DiffResult {
2347
- nonRegressive: boolean;
2348
- changes: VersionChange[];
2349
- }
2350
- /**
2351
- * `Decimal | string | number` — the three forms in which prices appear
2352
- * in the platform. `Decimal` is the Prisma class (with
2353
- * `.toNumber()`), which is not imported directly in order to keep the
2354
- * platform Prisma-free — a structural view suffices.
2355
- */
2356
- type DecimalLike = number | string | {
2357
- toNumber(): number;
2358
- };
2359
- interface PlanVersionFields {
2360
- features: FeatureKey[];
2361
- /**
2362
- * Quotas of the version. -1 = unlimited; missing key = 0.
2363
- *
2364
- * Every key either side carries is compared. Which keys exist is the
2365
- * installation's decision — they come from `@DefinesQuota` — so a fixed
2366
- * set here would have compared the three the platform happened to know by
2367
- * name and let every other one be lowered without the confirmation
2368
- * publishing a regression asks for.
2369
- */
2370
- quotas: Record<QuotaKey, number>;
2371
- monthlyNet: DecimalLike;
2372
- yearlyNet: DecimalLike;
2373
- }
2374
- interface BundleVersionFields {
2375
- features: FeatureKey[];
2376
- /** Quota contributions of the bundle. -1 = unlimited; missing key = 0. */
2377
- quotas: Record<QuotaKey, number>;
2378
- /** Default pricing; null = only override pricing possible. */
2379
- monthlyNet: DecimalLike | null;
2380
- yearlyNet: DecimalLike | null;
2381
- }
2382
- declare function classifyPlanDiff(oldV: PlanVersionFields, newV: PlanVersionFields): DiffResult;
2383
- /**
2384
- * Classification of a BundleVersion diff for contract protection.
2385
- *
2386
- * Quotas are compared exactly as they are for a plan.
2387
- *
2388
- * Pricing can be `null` (the bundle only has override pricing); a switch
2389
- * from value ↔ null is classified as REGRESSION (value dropped) or IMPROVEMENT
2390
- * (value added, lowerIsBetter inverted). Both null
2391
- * stay NEUTRAL.
2392
- */
2393
- declare function classifyBundleVersionDiff(oldV: BundleVersionFields, newV: BundleVersionFields): DiffResult;
2394
-
2395
- /**
2396
- * - `improvement` — nothing dearer in any rhythm, no feature missing, no quota
2397
- * lower. Taken at once and free, the term kept.
2398
- * - `more-for-more` — nothing missing or lower, but dearer in at least one
2399
- * rhythm. Taken like an upgrade: at once, the difference prorated. The rule
2400
- * reads every rhythm, so in the subscriber's own the difference can be zero
2401
- * or below — monthly 49 to 45 with yearly 490 to 500 is more for more for a
2402
- * monthly subscriber too.
2403
- * - `takes-something-away` — a feature missing or a quota lower, whatever the
2404
- * price. Taken like a downgrade: at the end of the running term, and only
2405
- * when the subscriber asks for it.
2406
- * - `same` — the two agree on everything compared: there is nothing to offer.
2407
- */
2408
- type VersionOfferClass = 'improvement' | 'more-for-more' | 'takes-something-away' | 'same';
2409
- /**
2410
- * A version as the comparison reads it. A price may be absent: that is a
2411
- * rhythm the version is not sold in, and a rhythm that stops being sold counts
2412
- * against the candidate like a price that went up.
2413
- */
2414
- type VersionOfferFields = BundleVersionFields;
2415
- interface VersionOffer {
2416
- readonly class: VersionOfferClass;
2417
- /** Every difference, bound to candidate, as the catalogue's diff states it. */
2418
- readonly changes: readonly VersionChange[];
2419
- }
2420
- /** One version as a subscriber compares it. A price of null is a rhythm it is not sold in. */
2421
- interface VersionOfferSide {
2422
- readonly planVersionId: string;
2423
- readonly version: number;
2424
- readonly features: readonly string[];
2425
- readonly quotas: Readonly<Record<string, number>>;
2426
- readonly monthlyNet: number | null;
2427
- readonly yearlyNet: number | null;
2428
- /** The last day it is sold on, where a successor closes its window — ISO, the whole day included. */
2429
- readonly validUntil: string | null;
2430
- /** When an operator ended it, where one did — ISO; from then it is not sold. */
2431
- readonly endsAt: string | null;
2914
+ plan: string;
2915
+ quotas: Record<string, number>;
2916
+ features: string[];
2917
+ /**
2918
+ * The add-ons a contract left out of these entitlements because their
2919
+ * cancellation was already declared when it was written. Each is granted by
2920
+ * its booking until its effective date, and by this snapshot never.
2921
+ *
2922
+ * Absent where nothing was left out — and on a snapshot written by an
2923
+ * application, or before the platform left anything out, which then counts
2924
+ * every add-on it names as its own.
2925
+ */
2926
+ leftOutBundleVersionIds?: string[];
2432
2927
  }
2433
- /**
2434
- * A newer version of the subscriber's plan, offered: the version bound and the
2435
- * one offered side by side, what kind of offer it is, and when a switch taken
2436
- * now would take effect — at once, or, for one that takes something away, at
2437
- * the end of the running term. `GET billing/version-offer` answers with it.
2438
- */
2439
- interface VersionOfferView {
2440
- readonly plan: string;
2441
- readonly bound: VersionOfferSide;
2442
- readonly offered: VersionOfferSide;
2443
- readonly class: Exclude<VersionOfferClass, 'same'>;
2444
- readonly changes: readonly VersionChange[];
2445
- /** ISO instant. */
2446
- readonly takesEffectAt: string;
2928
+
2929
+ interface FeatureUiMeta {
2930
+ /** Visible label for plan comparison tables, add-on lists. */
2931
+ label: string;
2932
+ /** Long description for tooltips, add-on cards. */
2933
+ description: string;
2934
+ /** Quasar icon name (e.g. 'directions_car', 'groups'). */
2935
+ icon: string;
2936
+ /** Mirror of `PlanCatalog.features[].plannedOnly` — cache for the UI without a catalog roundtrip. */
2937
+ plannedOnly?: boolean;
2938
+ /** true = base infrastructure, included in every plan (not bookable). */
2939
+ core?: boolean;
2940
+ /**
2941
+ * Set where the feature is withdrawn now or from a date ahead: each
2942
+ * withdrawal of it not over yet, the earliest first — why, from when, and
2943
+ * until when where it is lifted from a date ahead. A feature withdrawn
2944
+ * again from the day it returns carries both. A page that shows the
2945
+ * feature as part of a plan or an add-on shows this beside it, so whoever
2946
+ * concludes knows before they do.
2947
+ */
2948
+ withdrawn?: readonly FeatureWithdrawalMark[];
2447
2949
  }
2448
- /**
2449
- * What taking an offer did: the version left and the one taken, and whether it
2450
- * applied at once or waits for `takesEffectAt`. `POST billing/version-offer/accept`
2451
- * answers with it.
2452
- */
2453
- interface VersionSwitchResult {
2454
- readonly class: Exclude<VersionOfferClass, 'same'>;
2455
- readonly fromPlanVersionId: string;
2456
- readonly planVersionId: string;
2457
- readonly immediate: boolean;
2458
- /** ISO instant. */
2459
- readonly takesEffectAt: string;
2950
+ /** One withdrawal of a feature, as a catalogue marks it beside the feature. */
2951
+ interface FeatureWithdrawalMark {
2952
+ readonly reason: string;
2953
+ /** ISO 8601. */
2954
+ readonly effectiveFrom: string;
2955
+ /** ISO 8601, where it is lifted from a date ahead. */
2956
+ readonly liftedFrom: string | null;
2460
2957
  }
2958
+ /** Map FeatureKey → UI metadata. Consumer apps supply a complete table. */
2959
+ type FeatureUiRegistry = Record<string, FeatureUiMeta>;
2960
+
2961
+ /** A quota as a finite number, or `null` where the value cannot be read as one. */
2962
+ declare function readQuotaValue(value: unknown): number | null;
2461
2963
  /**
2462
- * Classifies `candidate` against `bound`. The comparison is the catalogue's
2463
- * own diff — prices with a rhythm that may be absent, as a bundle version's
2464
- * are — so an offer and a published version describe their changes alike.
2964
+ * Every quota in a JSON column, for a caller that computes with them.
2965
+ *
2966
+ * A key that is there stays there. Dropping an unreadable one made it *absent*,
2967
+ * and absent means undeclared: `enforceLimit` answers an undeclared dimension
2968
+ * with a 500, so every operation on that quota was refused — a fail-closed
2969
+ * answer to somebody else's corrupt row.
2465
2970
  */
2466
- declare function classifyVersionOffer(bound: VersionOfferFields, candidate: VersionOfferFields): VersionOffer;
2971
+ declare function readQuotaRecord(value: unknown): Record<string, number>;
2467
2972
 
2468
2973
  interface PromoPreviewRequest {
2469
2974
  /** Promo code (case-insensitive, normalized to uppercase in the service). */
@@ -2740,208 +3245,347 @@ interface MaintenanceUnlockView {
2740
3245
  wasLocked: boolean;
2741
3246
  }
2742
3247
 
2743
- /** What the switch costs, per period of the subscriber's rhythm. */
2744
- interface RetirementSwitchTerms {
2745
- /** The replacement's net price. */
2746
- readonly priceNet: number;
2747
- /**
2748
- * Where the replacement costs more: the price the subscriber goes on paying,
2749
- * the difference that is held, and the last day it is held — the day before
2750
- * the date they were told. Null where it costs the same or less: its price
2751
- * then applies from the subscriber's next period.
2752
- */
2753
- readonly held: {
2754
- readonly priceNet: number;
2755
- readonly amountNet: number;
2756
- readonly lastDay: string;
2757
- } | null;
3248
+ /**
3249
+ * A newer version of a booked add-on, offered beside the booking: the version
3250
+ * booked and the one on sale side by side, what kind of offer it is, and when
3251
+ * a switch taken now would take effect — at once, or, for one that takes
3252
+ * something away, at the end of the booking's running term. The list of the
3253
+ * tenant's bookings carries it on each booking, as `offer`.
3254
+ */
3255
+ interface BundleVersionOfferView {
3256
+ readonly subscriptionBundleId: string;
3257
+ /** The plan both versions are priced beside: the one the subscription is on. */
3258
+ readonly planKey: string;
3259
+ /** The rhythm the booking is billed in, which the kind of offer is judged in. */
3260
+ readonly billingCycle: BillingCycle;
3261
+ readonly bound: BundleVersionSide;
3262
+ readonly offered: BundleVersionSide;
3263
+ readonly class: Exclude<VersionOfferClass, 'same'>;
3264
+ /** Every difference, bound to offered, with the price in the booking's rhythm alone. */
3265
+ readonly changes: readonly VersionChange[];
3266
+ /** ISO instant. */
3267
+ readonly takesEffectAt: string;
2758
3268
  }
2759
3269
  /**
2760
- * What switching to the replacement now costs in `billingCycle`, or null where
2761
- * the replacement has no price in it.
3270
+ * What taking an add-on offer did: the booking, the version left and the one
3271
+ * taken, and whether it applied at once or waits for `takesEffectAt`.
2762
3272
  */
2763
- declare function retirementSwitchTerms(notice: Pick<VersionRetiredNotice, 'retired' | 'replacement' | 'lastDayToCancel'>, billingCycle: string): RetirementSwitchTerms | null;
3273
+ interface BundleVersionSwitchResult {
3274
+ readonly class: Exclude<VersionOfferClass, 'same'>;
3275
+ readonly subscriptionBundleId: string;
3276
+ readonly fromBundleVersionId: string;
3277
+ readonly bundleVersionId: string;
3278
+ readonly immediate: boolean;
3279
+ /** ISO instant. */
3280
+ readonly takesEffectAt: string;
3281
+ }
2764
3282
 
2765
- /** A version, as an announcement names it. */
2766
- interface RetiredVersionRef {
2767
- readonly planVersionId: string;
2768
- readonly planKey: string;
2769
- readonly version: number;
3283
+ /** What a reduction is named for: a plan, or an add-on, by its key. */
3284
+ type FeatureWithdrawalLineKind = 'plan' | 'bundle';
3285
+ /**
3286
+ * The net amount the price of a plan, or of an add-on, is reduced by for one
3287
+ * whole period of `billingCycle` without the feature. A plan or an add-on that
3288
+ * grants the feature and is not named is not reduced.
3289
+ */
3290
+ interface FeatureWithdrawalReduction {
3291
+ readonly kind: FeatureWithdrawalLineKind;
3292
+ /** The plan key, or the add-on's (bundle's) key. */
3293
+ readonly key: string;
3294
+ readonly billingCycle: BillingCycle;
3295
+ readonly amountNet: number;
2770
3296
  }
2771
- /** An announcement as it is kept: one per retirement, whoever it reaches. */
2772
- interface VersionRetirementRecord {
3297
+ /** The longest reason a withdrawal carries: the operator's words to the subscribers, not a document. */
3298
+ declare const FEATURE_WITHDRAWAL_REASON_MAX_LENGTH = 1000;
3299
+ /** A withdrawal as it is kept: one per announcement. */
3300
+ interface FeatureWithdrawalRecord {
2773
3301
  readonly id: string;
2774
- /** The version retired. */
2775
- readonly retired: RetiredVersionRef;
2776
- /** The version its subscriptions continue on, named by the operator. */
2777
- readonly replacement: RetiredVersionRef;
3302
+ readonly featureKey: string;
3303
+ /** Why, in the operator's words, as the subscribers are shown it. */
3304
+ readonly reason: string;
3305
+ /** From when the feature is granted to nobody. */
3306
+ readonly effectiveFrom: Date;
3307
+ /** From when it is granted again, once the operator has lifted the withdrawal. */
3308
+ readonly liftedFrom: Date | null;
3309
+ readonly reductions: readonly FeatureWithdrawalReduction[];
2778
3310
  readonly announcedAt: Date;
2779
3311
  /** Who announced it — an actor tag, as the audit log writes it. */
2780
3312
  readonly announcedBy: string;
3313
+ readonly liftedAt: Date | null;
3314
+ readonly liftedBy: string | null;
3315
+ /**
3316
+ * When the subscriptions it reaches were read and their notices recorded;
3317
+ * null until then. Announcing records the withdrawal first and reads who it
3318
+ * reaches once it is recorded, so that a change concluded while it was
3319
+ * being announced is not missed — the quarter-hourly run tells the ones
3320
+ * still null.
3321
+ */
3322
+ readonly toldAt: Date | null;
3323
+ }
3324
+ type NewFeatureWithdrawal = Pick<FeatureWithdrawalRecord, 'featureKey' | 'reason' | 'effectiveFrom' | 'reductions' | 'announcedAt' | 'announcedBy'>;
3325
+ /** What lifting a withdrawal records. */
3326
+ interface FeatureWithdrawalLift {
3327
+ readonly liftedFrom: Date;
3328
+ readonly liftedAt: Date;
3329
+ readonly liftedBy: string;
2781
3330
  }
2782
- type NewVersionRetirement = Omit<VersionRetirementRecord, 'id'>;
2783
3331
  /**
2784
- * The manifest capability that announces retiring a version. The platform sets
2785
- * it where it serves the routes, and the admin UI offers the action only where
2786
- * it is set. Whether the operator's terms allow it is a blocker the preview
2787
- * reports, so an operator who has not confirmed them learns what is missing.
3332
+ * Whether `withdrawal` takes its feature away at `at`. A withdrawal lifted
3333
+ * before its date never does.
2788
3334
  */
2789
- declare const VERSION_RETIREMENT_CAPABILITY = "planVersions.retire";
3335
+ declare function isFeatureWithdrawnAt(withdrawal: Pick<FeatureWithdrawalRecord, 'effectiveFrom' | 'liftedFrom'>, at: Date): boolean;
3336
+ /** The features `withdrawals` take away at `at`. */
3337
+ declare function featuresWithdrawnAt(withdrawals: readonly Pick<FeatureWithdrawalRecord, 'featureKey' | 'effectiveFrom' | 'liftedFrom'>[], at: Date): Set<string>;
2790
3338
  /**
2791
- * One of the two versions of a retirement, as the subscriber compares them:
2792
- * price in each rhythm, quotas and features, with the plan it belongs to — the
2793
- * replacement may be a version of another plan.
3339
+ * The first moment after `at` at which what `withdrawals` take away changes —
3340
+ * one takes effect, or is lifted — or null where none is still to come.
2794
3341
  */
2795
- interface RetirementSide extends VersionOfferSide {
2796
- readonly planKey: string;
3342
+ declare function nextWithdrawalChange(withdrawals: readonly Pick<FeatureWithdrawalRecord, 'effectiveFrom' | 'liftedFrom'>[], at: Date): Date | null;
3343
+ /**
3344
+ * The manifest capability that announces withdrawing a feature. The platform
3345
+ * sets it where it serves the routes, and the admin UI offers the action only
3346
+ * where it is set.
3347
+ */
3348
+ declare const FEATURE_WITHDRAWAL_CAPABILITY = "features.withdraw";
3349
+ /**
3350
+ * A line of a subscription a withdrawal reaches: the plan in the rhythm it is
3351
+ * billed in, or one booking of an add-on.
3352
+ */
3353
+ interface FeatureWithdrawalLine {
3354
+ readonly line: FeatureWithdrawalLineKind;
3355
+ /** The plan key, or the add-on's key. */
3356
+ readonly key: string;
3357
+ /** The plan's or the add-on's name, as the subscriber reads it. */
3358
+ readonly label: string;
3359
+ /** The booking, where the line is an add-on's; null for the plan. */
3360
+ readonly subscriptionBundleId: string | null;
3361
+ readonly billingCycle: BillingCycle;
3362
+ }
3363
+ /** A line as the subscriber was told of it: with the reduction it carries, or null for none. */
3364
+ interface FeatureWithdrawalNoticeLine extends FeatureWithdrawalLine {
3365
+ readonly reductionNet: number | null;
2797
3366
  }
2798
3367
  /**
2799
- * A plan version a subscription is on is being retired, and the subscription
2800
- * continues on the named replacement from its effective date. Sent once per
2801
- * subscription and retirement, when the retirement is announced, and kept as
2802
- * it was sent: the record shows what the subscriber was told, prices included.
3368
+ * A feature the subscription holds is withdrawn. Sent once per subscription and
3369
+ * withdrawal, at the announcement, and kept as it was sent: it is the record of
3370
+ * which lines the withdrawal reached and what each is reduced by.
2803
3371
  */
2804
- interface VersionRetiredNotice {
2805
- readonly kind: 'version-retired';
3372
+ interface FeatureWithdrawnNotice {
3373
+ readonly kind: 'feature-withdrawn';
2806
3374
  readonly tenantId: string;
2807
3375
  readonly subscriptionId: string;
2808
- readonly retirementId: string;
2809
- readonly retired: RetirementSide;
2810
- readonly replacement: RetirementSide;
2811
- /** Every difference, retired to replacement, as the catalogue's diff states it. */
2812
- readonly changes: readonly VersionChange[];
3376
+ readonly withdrawalId: string;
3377
+ readonly featureKey: string;
3378
+ readonly featureLabel: string;
3379
+ readonly reason: string;
3380
+ /** ISO 8601. */
3381
+ readonly effectiveFrom: string;
3382
+ readonly lines: readonly FeatureWithdrawalNoticeLine[];
2813
3383
  /**
2814
- * The rhythm the subscription is billed in when the retirement takes
2815
- * effect — a change of rhythm scheduled by then included — which says
2816
- * which of the two prices applies.
3384
+ * Whether the subscription holds the feature through special terms its
3385
+ * contract records and through no line: then it may end at once, and no
3386
+ * reduction applies by itself.
2817
3387
  */
2818
- readonly billingCycle: string;
3388
+ readonly specialTerms: boolean;
3389
+ }
3390
+ /** A withdrawn feature is granted again. Sent once to every subscription told of the withdrawal. */
3391
+ interface FeatureWithdrawalLiftedNotice {
3392
+ readonly kind: 'feature-withdrawal-lifted';
3393
+ readonly tenantId: string;
3394
+ readonly subscriptionId: string;
3395
+ readonly withdrawalId: string;
3396
+ readonly featureKey: string;
3397
+ readonly featureLabel: string;
2819
3398
  /** ISO 8601. */
2820
- readonly effectiveAt: string;
2821
- /** ISO date (`YYYY-MM-DD`), UTC. */
2822
- readonly lastDayToCancel: string;
3399
+ readonly liftedFrom: string;
2823
3400
  }
2824
3401
  /**
2825
- * The one reminder of a retirement, where staying put costs the subscription
2826
- * something (`SC-SUB-034`): sent 14 days before the retirement takes effect,
2827
- * once per subscription and retired version. It says again what the
2828
- * retirement notice said, and what a switch taken now would cost. Its
2829
- * `billingCycle` is still the rhythm billed at the date, read again from the
2830
- * subscription as it stands when the reminder is sent: a change of rhythm
2831
- * scheduled since the announcement is in it.
3402
+ * The subscription, or one booking of it, ended at once while a feature it
3403
+ * holds was withdrawn: what the account credits the unused rest of the period
3404
+ * already charged from. Recorded by each attempt before it ends anything, so
3405
+ * the credit never lacks its record; an attempt whose end did not happen leaves
3406
+ * one behind, and only the one whose `endedAt` the subscription or the booking
3407
+ * records is sent, counted or credited (`endedAtOnceSubjectOf`).
2832
3408
  */
2833
- interface VersionRetirementReminder extends Omit<VersionRetiredNotice, 'kind'> {
2834
- readonly kind: 'version-retirement-reminder';
2835
- /**
2836
- * What switching to the replacement now costs in the subscription's rhythm
2837
- * (`SC-SUB-032`), or null where it cannot switch now: in a trial, with a
2838
- * change scheduled, or where either plan is held for a special contract.
2839
- */
2840
- readonly switchTerms: RetirementSwitchTerms | null;
3409
+ interface EndedAtOnceNotice {
3410
+ readonly kind: 'ended-at-once';
3411
+ readonly tenantId: string;
3412
+ readonly subscriptionId: string;
3413
+ /** The booking that ended; null where the subscription did, and every booking with it. */
3414
+ readonly subscriptionBundleId: string | null;
3415
+ readonly withdrawalId: string;
3416
+ readonly featureKey: string;
3417
+ readonly featureLabel: string;
3418
+ /** ISO 8601. */
3419
+ readonly endedAt: string;
2841
3420
  }
2842
- /**
2843
- * Why a retirement does not reach a subscription on the version:
2844
- *
2845
- * - `ended` — the subscription is over.
2846
- * - `cancelled-before` — its cancellation lands by the effective date.
2847
- * - `changes-before` — a scheduled change moves it to another plan, or to
2848
- * another version of this one, by then.
2849
- * - `no-term` — it carries no date a term can be counted from.
2850
- * - `already-told` — an earlier announcement of this version reached it, and
2851
- * stands.
2852
- */
2853
- type RetirementSkipReason = 'ended' | 'cancelled-before' | 'changes-before' | 'no-term' | 'already-told';
2854
- /** A refusal as the preview reports it and the announcement throws it. */
2855
- interface RetirementBlocker {
3421
+ /** A blocker or a refusal a person reads: a stable code and the values its sentence names. */
3422
+ interface FeatureWithdrawalBlocker {
2856
3423
  readonly code: string;
2857
3424
  readonly message: string;
2858
3425
  readonly params: Record<string, string | number>;
2859
3426
  }
2860
- /** A subscription a retirement reaches, with what it means there. */
2861
- interface RetirementReachedRow {
3427
+ /** A subscription a withdrawal reaches, as the operator previews it. */
3428
+ interface FeatureWithdrawalReachedRow {
2862
3429
  readonly tenantId: string;
2863
3430
  readonly subscriptionId: string;
2864
3431
  readonly status: string;
2865
- /** The rhythm it is billed in when the retirement takes effect. */
2866
- readonly billingCycle: string;
2867
- /** ISO 8601. */
2868
- readonly effectiveAt: string;
2869
- /** ISO date, UTC. */
2870
- readonly lastDayToCancel: string;
2871
- /** Reached by another retirement within the last twelve months: the announcement waits. */
2872
- readonly reachedRecently: boolean;
2873
- }
2874
- /** A subscription on the version a retirement does not reach, and why. */
2875
- interface RetirementSkippedRow {
3432
+ /** Each line with its price per period in its rhythm, net, or null where none is known. */
3433
+ readonly lines: readonly (FeatureWithdrawalLine & {
3434
+ readonly priceNet: number | null;
3435
+ })[];
3436
+ /** Whether it holds the feature through special terms its contract records, and through no line. */
3437
+ readonly specialTerms: boolean;
3438
+ }
3439
+ /** A subscription holding the feature that the withdrawal does not reach: it ends by the date. */
3440
+ interface FeatureWithdrawalSkippedRow {
2876
3441
  readonly tenantId: string;
2877
3442
  readonly subscriptionId: string;
2878
- readonly reason: RetirementSkipReason;
3443
+ readonly reason: 'ends-before';
2879
3444
  }
2880
- /** What announcing a retirement now would do. */
2881
- interface RetirementPreview {
2882
- readonly retired: RetirementSide;
2883
- readonly replacement: RetirementSide;
2884
- /** Every difference, retired to replacement, as the subscribers will be told it. */
2885
- readonly changes: readonly VersionChange[];
3445
+ /**
3446
+ * A plan or an add-on in a rhythm the withdrawal reaches, which the operator
3447
+ * may name a reduction for: how many lines it reaches and the lowest price
3448
+ * among them, which a reduction may not exceed.
3449
+ */
3450
+ interface FeatureWithdrawalTarget {
3451
+ readonly kind: FeatureWithdrawalLineKind;
3452
+ readonly key: string;
3453
+ readonly label: string;
3454
+ readonly billingCycle: BillingCycle;
3455
+ readonly lines: number;
3456
+ readonly lowestPriceNet: number | null;
3457
+ }
3458
+ /** What announcing a withdrawal now would do. */
3459
+ interface FeatureWithdrawalPreview {
3460
+ readonly feature: {
3461
+ readonly key: string;
3462
+ readonly label: string;
3463
+ };
3464
+ /** ISO 8601: the date it would take effect. */
3465
+ readonly effectiveFrom: string;
2886
3466
  /** ISO 8601: the moment it is computed for. */
2887
3467
  readonly asOf: string;
2888
- readonly reached: readonly RetirementReachedRow[];
2889
- readonly skipped: readonly RetirementSkippedRow[];
3468
+ readonly reached: readonly FeatureWithdrawalReachedRow[];
3469
+ readonly skipped: readonly FeatureWithdrawalSkippedRow[];
3470
+ readonly targets: readonly FeatureWithdrawalTarget[];
2890
3471
  /** Empty where it may be announced as it stands. */
2891
- readonly blockers: readonly RetirementBlocker[];
3472
+ readonly blockers: readonly FeatureWithdrawalBlocker[];
3473
+ }
3474
+ /** A withdrawal as the administration reads it: the record, its dates as ISO 8601. */
3475
+ interface FeatureWithdrawalRow {
3476
+ readonly id: string;
3477
+ readonly featureKey: string;
3478
+ readonly reason: string;
3479
+ readonly effectiveFrom: string;
3480
+ readonly liftedFrom: string | null;
3481
+ readonly reductions: readonly FeatureWithdrawalReduction[];
3482
+ readonly announcedAt: string;
3483
+ readonly announcedBy: string;
3484
+ readonly liftedAt: string | null;
3485
+ readonly liftedBy: string | null;
3486
+ /** When the subscriptions it reaches were told; null until then. */
3487
+ readonly toldAt: string | null;
2892
3488
  }
3489
+ /** The row the administration reads for `record`. */
3490
+ declare function featureWithdrawalRowOf(record: FeatureWithdrawalRecord): FeatureWithdrawalRow;
2893
3491
  /** What an announcement did. */
2894
- interface RetirementAnnounced {
2895
- readonly retirement: VersionRetirementRecord;
3492
+ interface FeatureWithdrawalAnnounced {
3493
+ readonly withdrawal: FeatureWithdrawalRow;
2896
3494
  /** Notices sent now; the rest are sent by the next run. */
2897
3495
  readonly told: number;
2898
3496
  readonly failed: number;
2899
3497
  }
2900
- /**
2901
- * How far a retirement has come, counted over the subscriptions it reached
2902
- * (`SC-SUB-033`).
2903
- */
2904
- interface RetirementProgress {
2905
- /** Off the retired version: moved at their date, switched early, or changed on their own. */
2906
- readonly moved: number;
2907
- /** Still on the retired version, before their date. */
2908
- readonly waiting: number;
2909
- /** Still on the retired version after their date: the run has not moved them yet, or cannot. */
2910
- readonly overdue: number;
2911
- /** Still on the retired version, and ended by their date: there is nothing to move. */
2912
- readonly ended: number;
3498
+ /** What lifting a withdrawal did. */
3499
+ interface FeatureWithdrawalLifted {
3500
+ readonly withdrawal: FeatureWithdrawalRow;
3501
+ readonly told: number;
3502
+ readonly failed: number;
3503
+ }
3504
+ /** A withdrawal as the administration lists it, with how far it has come. */
3505
+ interface FeatureWithdrawalView extends FeatureWithdrawalRow {
3506
+ readonly featureLabel: string;
3507
+ readonly progress: {
3508
+ /** The subscriptions it reached. */
3509
+ readonly reached: number;
3510
+ /** Of those, the ones whose notice reached somebody. */
3511
+ readonly told: number;
3512
+ /** The subscriptions and bookings that ended at once under it. */
3513
+ readonly endedAtOnce: number;
3514
+ };
3515
+ }
3516
+ /** A line of the tenant's subscription a withdrawal reached, as the tenant sees it now. */
3517
+ interface TenantFeatureWithdrawalLine extends FeatureWithdrawalNoticeLine {
2913
3518
  /**
2914
- * Still on the retired version, and not told yet: their notice has reached
2915
- * nobody, so their date is not set and nothing moves them (`SC-SUB-036`).
3519
+ * Whether the reduction still applies: the line runs as it was reached.
3520
+ * A change of plan or rhythm the subscriber made ends it.
2916
3521
  */
2917
- readonly notTold: number;
3522
+ readonly reduced: boolean;
2918
3523
  /**
2919
- * Reminded 14 days before their date (`SC-SUB-034`) — counted beside the
2920
- * four above, not instead of one: a subscription reminded moves later.
3524
+ * Where a change scheduled before the announcement brings the line and has
3525
+ * not landed yet: when it lands, ISO 8601, from which the reduction
3526
+ * applies. Null otherwise.
2921
3527
  */
2922
- readonly reminded: number;
2923
- }
2924
- /** An announcement as the administration lists it, with how far it has come. */
2925
- interface VersionRetirementView extends VersionRetirementRecord {
2926
- readonly progress: RetirementProgress;
3528
+ readonly fromChange: string | null;
2927
3529
  }
2928
- /** What one run of the moves at the date did. */
2929
- interface RetirementMoveRun {
2930
- /** Subscriptions moved onto their replacement, with the contract the move writes. */
2931
- readonly moved: number;
2932
- /** Moves that could not be made, and are tried again by the next run. */
2933
- readonly failed: number;
3530
+ /** A withdrawal reaching the tenant's subscription, beside its plan and its add-ons. */
3531
+ interface TenantFeatureWithdrawal {
3532
+ readonly withdrawalId: string;
3533
+ readonly featureKey: string;
3534
+ readonly featureLabel: string;
3535
+ readonly reason: string;
3536
+ /** ISO 8601. */
3537
+ readonly effectiveFrom: string;
3538
+ /** ISO 8601, once the operator has lifted it. */
3539
+ readonly liftedFrom: string | null;
3540
+ /** Whether it takes the feature away now. */
3541
+ readonly inEffect: boolean;
3542
+ readonly lines: readonly TenantFeatureWithdrawalLine[];
3543
+ readonly specialTerms: boolean;
3544
+ /** What may be ended at once now: the subscription, and which bookings. */
3545
+ readonly endable: {
3546
+ readonly subscription: boolean;
3547
+ readonly subscriptionBundleIds: readonly string[];
3548
+ };
2934
3549
  }
2935
- /** What switching to the replacement before the date did (`SC-SUB-032`). */
2936
- interface RetirementSwitchResult {
2937
- readonly fromPlanVersionId: string;
2938
- readonly planVersionId: string;
2939
- /** ISO date of the last day the price is held, or null where none is. */
2940
- readonly heldUntilDay: string | null;
3550
+ /**
3551
+ * The features `withdrawals` take away at `now` or from a date ahead, and not
3552
+ * lifted by then, the earliest first: what a catalogue marks beside every plan
3553
+ * and add-on that grants them. A withdrawal lifted before its own date takes
3554
+ * nothing away. One feature can appear twice — withdrawn until a date, and
3555
+ * again from it.
3556
+ */
3557
+ declare function withdrawnFeaturesOf(withdrawals: readonly FeatureWithdrawalRecord[], now: Date): WithdrawnFeature[];
3558
+ /**
3559
+ * A feature withdrawn now or from a date ahead, as a catalogue marks it beside
3560
+ * every plan and add-on that grants it, so whoever concludes is shown it first.
3561
+ */
3562
+ interface WithdrawnFeature extends FeatureWithdrawalMark {
3563
+ readonly featureKey: string;
3564
+ }
3565
+ /** What ending at once now would credit, before the subscriber confirms it. */
3566
+ interface EndAtOncePreview {
3567
+ /** ISO 8601: the moment it ends. */
3568
+ readonly endsAt: string;
3569
+ /** The unused rest of what was charged, net, as a positive amount; 0 where nothing was charged. */
3570
+ readonly creditNet: number;
3571
+ /** ISO 4217, where anything is credited. */
3572
+ readonly currency: string | null;
2941
3573
  }
3574
+ /** What ending at once did. */
3575
+ interface EndedAtOnce extends EndAtOncePreview {
3576
+ readonly withdrawalId: string;
3577
+ /** The booking that ended; null where the subscription did. */
3578
+ readonly subscriptionBundleId: string | null;
3579
+ }
3580
+ /**
3581
+ * The subject an end at once is recorded under: the subscription or the
3582
+ * booking, and the moment the attempt names, so a later attempt records its
3583
+ * own moment rather than inheriting one whose end never happened.
3584
+ */
3585
+ declare function endedAtOnceSubjectOf(subscriptionOrBookingId: string, at: Date): string;
2942
3586
 
2943
3587
  /** What a notice is about. */
2944
- type SubscriptionNoticeKind = 'version-offered' | 'version-retired' | 'version-retirement-reminder';
3588
+ type SubscriptionNoticeKind = 'version-offered' | 'version-retired' | 'version-retirement-reminder' | 'bundle-version-retired' | 'bundle-version-retirement-reminder' | 'bundle-version-offered' | 'feature-withdrawn' | 'feature-withdrawal-lifted' | 'ended-at-once';
2945
3589
  /** Which notice: there is one per subscription, kind and subject. */
2946
3590
  interface SubscriptionNoticeKey {
2947
3591
  readonly tenantId: string;
@@ -2953,7 +3597,16 @@ interface SubscriptionNoticeKey {
2953
3597
  * is told of a version's retirement once, whichever announcement it came
2954
3598
  * with. The announcement is in the notice as `retirementId`. For
2955
3599
  * `version-retirement-reminder`, the id of the version retired as well: a
2956
- * subscription is reminded of a version's retirement once.
3600
+ * subscription is reminded of a version's retirement once. For
3601
+ * `bundle-version-retired`, the id of the add-on version retired: a
3602
+ * subscription holds one booking of an add-on, so that booking is told of
3603
+ * the retirement once. For `bundle-version-retirement-reminder`, the id of
3604
+ * the add-on version retired as well: that booking is reminded of it once.
3605
+ * For `bundle-version-offered`, the id of the add-on version offered: that
3606
+ * booking is told of the version once. For `feature-withdrawn` and
3607
+ * `feature-withdrawal-lifted`, the id of the withdrawal: a subscription is
3608
+ * told of each once. For `ended-at-once`, the id of what ended — the
3609
+ * subscription, or the booking — which ends once.
2957
3610
  */
2958
3611
  readonly subject: string;
2959
3612
  }
@@ -2992,8 +3645,21 @@ interface VersionOfferedNotice {
2992
3645
  /** The offer as `GET billing/version-offer` answers it at the moment the notice is due. */
2993
3646
  readonly offer: VersionOfferView;
2994
3647
  }
3648
+ /**
3649
+ * A newer version of an add-on a booking is on is now offered to it. Sent once
3650
+ * per booking and version, when the offer appears beside the add-on — the
3651
+ * notice and the offer say the same thing.
3652
+ */
3653
+ interface BundleVersionOfferedNotice {
3654
+ readonly kind: 'bundle-version-offered';
3655
+ readonly tenantId: string;
3656
+ readonly subscriptionId: string;
3657
+ readonly subscriptionBundleId: string;
3658
+ /** The offer as the list of the tenant's bookings shows it at the moment the notice is due. */
3659
+ readonly offer: BundleVersionOfferView;
3660
+ }
2995
3661
  /** A notice handed to the application to send. */
2996
- type SubscriptionNotice = VersionOfferedNotice | VersionRetiredNotice | VersionRetirementReminder;
3662
+ type SubscriptionNotice = VersionOfferedNotice | VersionRetiredNotice | VersionRetirementReminder | BundleVersionRetiredNotice | BundleVersionRetirementReminder | BundleVersionOfferedNotice | FeatureWithdrawnNotice | FeatureWithdrawalLiftedNotice | EndedAtOnceNotice;
2997
3663
 
2998
3664
  /**
2999
3665
  * A `subscription_notices` row as either adapter reads it back. The two JSON
@@ -3077,6 +3743,46 @@ interface CanonicalVersionRetirementRow {
3077
3743
  /** Reads a row back as a record, naming each column rather than spreading the row. */
3078
3744
  declare function toVersionRetirementRecord(row: CanonicalVersionRetirementRow): VersionRetirementRecord;
3079
3745
 
3746
+ /** A `bundle_version_retirements` row as either adapter reads it back. */
3747
+ interface CanonicalBundleVersionRetirementRow {
3748
+ id: string;
3749
+ retiredBundleVersionId: string;
3750
+ retiredBundleKey: string;
3751
+ retiredVersion: number;
3752
+ replacementBundleVersionId: string;
3753
+ replacementBundleKey: string;
3754
+ replacementVersion: number;
3755
+ announcedAt: Date;
3756
+ announcedBy: string;
3757
+ }
3758
+ /** Reads a row back as a record, naming each column rather than spreading the row. */
3759
+ declare function toBundleVersionRetirementRecord(row: CanonicalBundleVersionRetirementRow): BundleVersionRetirementRecord;
3760
+
3761
+ /**
3762
+ * A `feature_withdrawals` row as either adapter reads it back. `reductions` is
3763
+ * `unknown`: the database holds whatever was written to the column.
3764
+ */
3765
+ interface CanonicalFeatureWithdrawalRow {
3766
+ id: string;
3767
+ featureKey: string;
3768
+ reason: string;
3769
+ effectiveFrom: Date;
3770
+ liftedFrom: Date | null;
3771
+ reductions: unknown;
3772
+ announcedAt: Date;
3773
+ announcedBy: string;
3774
+ liftedAt: Date | null;
3775
+ liftedBy: string | null;
3776
+ toldAt: Date | null;
3777
+ }
3778
+ /**
3779
+ * Reads a row back as a record, naming each column rather than spreading the
3780
+ * row. The reductions are checked rather than cast: they are what every
3781
+ * subscription the withdrawal reached is charged less by, and a value nobody
3782
+ * can read must stop at the read and name its row.
3783
+ */
3784
+ declare function toFeatureWithdrawalRecord(row: CanonicalFeatureWithdrawalRow): FeatureWithdrawalRecord;
3785
+
3080
3786
  /** A scheduled change as a subscription row holds it. */
3081
3787
  interface ScheduledChangeColumns {
3082
3788
  readonly pendingPlan: string | null;
@@ -3119,9 +3825,15 @@ declare function retirementReminderIsDue(notice: Pick<VersionRetiredNotice, 'eff
3119
3825
  * Whether staying put costs the subscription something when the retirement
3120
3826
  * takes effect (D9): the replacement takes a feature or a quota away, or is
3121
3827
  * dearer in `billingCycle`, the rhythm it is billed in then — or not sold in
3122
- * it. A price that rises only in another rhythm costs it nothing.
3828
+ * it. A price that rises only in another rhythm costs it nothing. A plan
3829
+ * version's retirement and an add-on version's are judged alike.
3123
3830
  */
3124
- declare function retirementCostsTheSubscription(notice: Pick<VersionRetiredNotice, 'retired' | 'replacement' | 'changes' | 'lastDayToCancel'>, billingCycle: string): boolean;
3831
+ declare function retirementCostsTheSubscription(notice: {
3832
+ readonly retired: RetirementPricedSide;
3833
+ readonly replacement: RetirementPricedSide;
3834
+ readonly changes: readonly VersionChange[];
3835
+ readonly lastDayToCancel: string;
3836
+ }, billingCycle: string): boolean;
3125
3837
 
3126
3838
  /** A `maintenance_windows` row as either adapter reads it back. */
3127
3839
  type CanonicalMaintenanceWindowRow = MaintenanceWindowRecord;
@@ -3159,6 +3871,28 @@ declare function maintenanceRetryAfterSeconds(window: MaintenanceWindowRecord, n
3159
3871
  */
3160
3872
  declare function parseZonedInstant(text: string): Date | null;
3161
3873
 
3874
+ /** A calendar day, `YYYY-MM-DD`. */
3875
+ type CalendarDay = string;
3876
+ /**
3877
+ * The day `at` falls on in `timeZone`. Throws a `RangeError` for a zone the
3878
+ * runtime does not know, and for no zone at all — without one `Intl` would
3879
+ * read the host's, and the day would depend on the machine.
3880
+ */
3881
+ declare function dayInZone(at: Date, timeZone: string): CalendarDay;
3882
+ /**
3883
+ * The last day a period covers in `timeZone`. A period ends before `until`, so
3884
+ * its last moment is one millisecond earlier: a month from 1 October to
3885
+ * 1 November covers 31 October as its last day.
3886
+ */
3887
+ declare function lastDayInZone(period: {
3888
+ from: Date;
3889
+ until: Date;
3890
+ }, timeZone: string): CalendarDay;
3891
+ /** The day `days` calendar days after `day`; months and years roll over as the calendar does. */
3892
+ declare function addDaysToDay(day: CalendarDay, days: number): CalendarDay;
3893
+ /** The year a day belongs to. */
3894
+ declare function yearOfDay(day: CalendarDay): number;
3895
+
3162
3896
  /**
3163
3897
  * The top-level blocks of `config/saas.yaml` that are the catalogue rather than
3164
3898
  * the configuration, and the format marker.
@@ -3932,27 +4666,559 @@ interface PromoCodeValidationLogRepository {
3932
4666
  * The consumer implementation decides what "first time" means. Important:
3933
4667
  * unfinished onboarding drafts must not count as an existing customer.
3934
4668
  */
3935
- interface FirstTimeCustomerCheck {
3936
- /** Returns true if a completed/historical customer already exists for the email. */
3937
- hasExistingCustomerForEmail(email: string): Promise<boolean>;
4669
+ interface FirstTimeCustomerCheck {
4670
+ /** Returns true if a completed/historical customer already exists for the email. */
4671
+ hasExistingCustomerForEmail(email: string): Promise<boolean>;
4672
+ }
4673
+ /** Subscription lookup for `redeem()`. Sufficient for promo calculations. */
4674
+ interface PromoSubscriptionLookup {
4675
+ findById(subscriptionId: string, tx?: TransactionContext): Promise<{
4676
+ id: string;
4677
+ tenantId: string;
4678
+ plan: string;
4679
+ billingCycle: BillingCycle;
4680
+ startedAt: Date | null;
4681
+ } | null>;
4682
+ }
4683
+ /**
4684
+ * Aggregation adapter for the stats endpoint (`PromoCodesService.stats`).
4685
+ * Consumers without an `InvoiceDiscount` table return '0.00'.
4686
+ */
4687
+ interface PromoRevenueDeductionAggregator {
4688
+ /** Sum of the amountGross values for all redemptions of a promo code (Decimal-as-string). */
4689
+ sumGrossForPromoCode(promoCodeId: string): Promise<string>;
4690
+ }
4691
+
4692
+ /**
4693
+ * What made the charge arise. activation: the first period of a subscription. renewal: a later period. planChange: what an immediate plan change adds. bundleBooking: the first, short period of an add-on. bundleChange: what an add-on's switch to a dearer version adds for the rest of its period. credit: a decision that reduces what is owed. correction: a counter-entry reversing an earlier charge, which is how the append-only journal answers a mistake. reductionTakenBack: a reduction granted in advance, taken back for the days it no longer covers — the feature it was granted for came back, or what it reduced ended at once.
4694
+ */
4695
+ type SubscriberChargeOrigin = 'activation' | 'renewal' | 'planChange' | 'bundleBooking' | 'bundleChange' | 'credit' | 'correction' | 'reductionTakenBack';
4696
+ /**
4697
+ * Which kind of contract line the charge realises.
4698
+ */
4699
+ type SubscriberChargeSource = 'plan' | 'bundle' | 'discount';
4700
+ /**
4701
+ * One charge in a subscriber's account: an amount that became due for a period, derived from a line of the contract in force. The journal is append-only — a charge is never updated, and a mistake is answered with a counter-entry rather than an edit, so the account says what happened rather than what somebody thinks today. A charge is net: the tax is decided by the tax adapter when the charge is invoiced, from the subscriber's origin on that day, and computed once per rate on the invoice.
4702
+ */
4703
+ interface SubscriberCharge {
4704
+ /**
4705
+ * The charge's own identifier. Never shown to a customer as an invoice number: invoice numbering is sequential, gapless and legally constrained per country, and an identifier somebody has already seen on a screen cannot become one later without confusion.
4706
+ */
4707
+ id: string;
4708
+ /**
4709
+ * The party the contract is concluded with, whose account this is. It outlives the tenant.
4710
+ */
4711
+ subscriberId: string;
4712
+ /**
4713
+ * The tenant the subscription belonged to when the charge arose.
4714
+ */
4715
+ tenantId: string;
4716
+ /**
4717
+ * The subscription the charge arose on. Part of the natural key.
4718
+ */
4719
+ subscriptionId: string;
4720
+ /**
4721
+ * The contract whose line the charge realises.
4722
+ */
4723
+ contractId: string;
4724
+ /**
4725
+ * The contract line the charge came from, so that every invoice line can be traced back to what was agreed.
4726
+ */
4727
+ contractLineItemId: string;
4728
+ origin: SubscriberChargeOrigin;
4729
+ source: SubscriberChargeSource;
4730
+ /**
4731
+ * Which thing of its source the charge is for, stable across contracts: the subscription id for the plan, the booking id for an add-on, the discount's key for a discount. Part of the natural key (subscriptionId, source, sourceRef, periodStart, origin), which is unique — a charge derived twice is written once.
4732
+ */
4733
+ sourceRef: string;
4734
+ /**
4735
+ * The start of the period the charge belongs to — the grouping key for an invoice.
4736
+ */
4737
+ periodStart: string;
4738
+ periodEnd: string;
4739
+ /**
4740
+ * ISO 4217, as the contract line records it — recorded per charge so that a charge written in 2026 still means what it meant after the configured currency changes.
4741
+ */
4742
+ currency: string;
4743
+ /**
4744
+ * Net, rounded to the currency's decimal places once, when the charge is written; the written figure is the truth from then on. Negative for a discount.
4745
+ */
4746
+ amountNet: number;
4747
+ /**
4748
+ * When the amount became due. The account is ordered by this, not by when the row was written.
4749
+ */
4750
+ bookedAt: string;
4751
+ createdAt: string;
4752
+ }
4753
+
4754
+ /** A charge as a store reads it back: the wire format, with its dates as dates. */
4755
+ interface SubscriberChargeRecord {
4756
+ id: string;
4757
+ subscriberId: string;
4758
+ tenantId: string;
4759
+ subscriptionId: string;
4760
+ contractId: string;
4761
+ contractLineItemId: string;
4762
+ origin: SubscriberChargeOrigin;
4763
+ source: SubscriberChargeSource;
4764
+ /** The subscription id for the plan, the booking id for an add-on, the discount's key. */
4765
+ sourceRef: string;
4766
+ periodStart: Date;
4767
+ periodEnd: Date;
4768
+ currency: string;
4769
+ /** Net, rounded to the cent when written. Negative for a discount. */
4770
+ amountNet: number;
4771
+ /** When the amount became due. */
4772
+ bookedAt: Date;
4773
+ createdAt: Date;
4774
+ }
4775
+ /** A charge to write: everything but what the store assigns. */
4776
+ type NewSubscriberCharge = Omit<SubscriberChargeRecord, 'id' | 'createdAt'>;
4777
+ /**
4778
+ * The manifest capability that announces the operator's view of an account.
4779
+ * The platform sets it where it serves the view, and the admin UI shows the
4780
+ * view only where it is set.
4781
+ */
4782
+ declare const SUBSCRIBER_ACCOUNT_CAPABILITY = "charges.read";
4783
+ /** Whose account it is, as the operator is shown it. */
4784
+ interface AdminAccountHolder {
4785
+ id: string;
4786
+ /** What the customer quotes, and what an invoice will carry. */
4787
+ customerNumber: string;
4788
+ legalName: string;
4789
+ }
4790
+ /** One charge of an account, with the name its contract line gives it. */
4791
+ interface AdminAccountEntry {
4792
+ /** The charge as the journal holds it. */
4793
+ charge: SubscriberCharge;
4794
+ /**
4795
+ * The contract line's own title, as it was when the contract was
4796
+ * concluded, or `null` where that line cannot be read.
4797
+ */
4798
+ title: string | null;
4799
+ }
4800
+ /**
4801
+ * A tenant's account as the operator reads it: the charges of the subscription
4802
+ * it holds, newest first — by when each became due, then by its period, then
4803
+ * the plan before its add-ons before its discounts.
4804
+ *
4805
+ * No total: without invoices and payments, a sum would be what was charged so
4806
+ * far, and it would be read as what is owed.
4807
+ */
4808
+ interface AdminSubscriberAccount {
4809
+ /** `null` for a tenant that has no subscriber. */
4810
+ holder: AdminAccountHolder | null;
4811
+ entries: AdminAccountEntry[];
4812
+ }
4813
+
4814
+ /** The issuer an invoice names, as of its issue date. */
4815
+ interface SubscriptionInvoiceIssuer extends LegalIdentity, PartyAddress {
4816
+ }
4817
+ /** The subscriber an invoice names, copied from its record on the issue date. */
4818
+ interface SubscriptionInvoiceSubscriber extends LegalIdentity, PartyAddress {
4819
+ customerNumber: string;
4820
+ }
4821
+ /** One line of an invoice: one charge, as the invoice states it. */
4822
+ interface SubscriptionInvoiceLineRecord {
4823
+ id: string;
4824
+ invoiceId: string;
4825
+ /** From 1, in the order the invoice lists its lines. */
4826
+ position: number;
4827
+ /** The charge the line invoices; a charge stands on one invoice (`SC-PRIC-022`). */
4828
+ chargeId: string;
4829
+ /** The contract line the charge came from (`SC-AUD-013`). */
4830
+ contractLineItemId: string;
4831
+ /** What the line is for, as the contract line names it. */
4832
+ title: string;
4833
+ origin: SubscriberChargeOrigin;
4834
+ source: SubscriberChargeSource;
4835
+ /** The first day the charge covers, in the installation's time zone. */
4836
+ periodFrom: CalendarDay;
4837
+ /** The last day the charge covers, in the installation's time zone. */
4838
+ periodUntil: CalendarDay;
4839
+ /** Net, as the charge was written. Negative for a discount. */
4840
+ amountNet: number;
4841
+ /** In per cent: the rate of the invoice's treatment. */
4842
+ taxRate: number;
4843
+ }
4844
+ /** A line to write: everything but what the store assigns. */
4845
+ type NewSubscriptionInvoiceLine = Omit<SubscriptionInvoiceLineRecord, 'id' | 'invoiceId'>;
4846
+ /** An invoice as a store reads it back. */
4847
+ interface SubscriptionInvoiceRecord {
4848
+ id: string;
4849
+ /** `<prefix>-<year>-<sequence>`, e.g. `AHP-2026-000123` (`formatInvoiceNumber`). */
4850
+ number: string;
4851
+ numberPrefix: string;
4852
+ /** The year of the issue date, which the sequence restarts with. */
4853
+ numberYear: number;
4854
+ /** From 1 each year, without gaps. */
4855
+ numberSequence: number;
4856
+ tenantId: string;
4857
+ subscriberId: string;
4858
+ subscriptionId: string;
4859
+ /** The contract the invoiced charges belong to. */
4860
+ contractId: string;
4861
+ issuedAt: Date;
4862
+ /** The day it was issued, in the installation's time zone. */
4863
+ issueDate: CalendarDay;
4864
+ /** The day it is due: the issue date plus the payment term (`SC-PRIC-046`). */
4865
+ dueDate: CalendarDay;
4866
+ /** The first day its charges cover. */
4867
+ servicePeriodFrom: CalendarDay;
4868
+ /** The last day its charges cover. */
4869
+ servicePeriodUntil: CalendarDay;
4870
+ currency: string;
4871
+ issuer: SubscriptionInvoiceIssuer;
4872
+ subscriber: SubscriptionInvoiceSubscriber;
4873
+ /**
4874
+ * The lines `config/saas.yaml` named under `invoicing.footer` on the issue
4875
+ * date — what a business letter of the issuer states beyond the invoice's
4876
+ * own content (`SC-PRIC-077`); none where it named none.
4877
+ */
4878
+ footer: string[];
4879
+ /** The treatment the tax adapter decided when it was issued, with its name and version. */
4880
+ taxTreatment: TaxTreatment;
4881
+ /** The tax, rate by rate, computed once per rate (`SC-PRIC-041`). */
4882
+ taxes: TaxAtRate[];
4883
+ totalNet: number;
4884
+ totalTax: number;
4885
+ totalGross: number;
4886
+ lines: SubscriptionInvoiceLineRecord[];
4887
+ createdAt: Date;
4888
+ }
4889
+ /**
4890
+ * An invoice to issue: everything but what the store assigns — the id, and
4891
+ * the number, which the store draws when it writes the invoice.
4892
+ */
4893
+ interface NewSubscriptionInvoice extends Omit<SubscriptionInvoiceRecord, 'id' | 'number' | 'numberSequence' | 'lines' | 'createdAt'> {
4894
+ lines: NewSubscriptionInvoiceLine[];
4895
+ }
4896
+ /** How many digits an invoice number's sequence is written with, at least. */
4897
+ declare const INVOICE_SEQUENCE_DIGITS = 6;
4898
+ /**
4899
+ * The number an invoice carries: the prefix `config/saas.yaml` names, the year
4900
+ * the sequence counts in, and the sequence written with at least six digits —
4901
+ * `formatInvoiceNumber('AHP', 2026, 123)` is `AHP-2026-000123`.
4902
+ */
4903
+ declare function formatInvoiceNumber(prefix: string, year: number, sequence: number): string;
4904
+
4905
+ /**
4906
+ * The manifest capability that announces the operator's download of an
4907
+ * invoice's archived document. The platform sets it where it serves the route.
4908
+ */
4909
+ declare const INVOICE_DOCUMENT_CAPABILITY = "invoices.read";
4910
+ /** A rendered invoice: its bytes, as a file. */
4911
+ interface InvoiceDocument {
4912
+ content: Uint8Array;
4913
+ /** `application/pdf` for a ZUGFeRD invoice. */
4914
+ mediaType: string;
4915
+ /** What a person saving it would call it, such as `AHP-2026-000123.pdf`. */
4916
+ fileName: string;
4917
+ }
4918
+ /**
4919
+ * Renders an invoice into the file the tax adapter's law requires — the
4920
+ * application's own renderer, or the default one SaaSiCat ships.
4921
+ *
4922
+ * Given the invoice as it was issued, it renders the same bytes every time, so
4923
+ * a repeated attempt archives the file whose checksum was recorded. Whatever
4924
+ * it would take from the clock, such as a PDF's creation date, it takes from
4925
+ * the invoice's `issuedAt`. A renderer updated between two attempts renders
4926
+ * other bytes; while nothing is archived, those take the place of the ones
4927
+ * recorded, and the audit log says so (`SC-AUD-019`).
4928
+ */
4929
+ interface InvoiceRenderer {
4930
+ render(invoice: SubscriptionInvoiceRecord): Promise<InvoiceDocument>;
4931
+ }
4932
+ /** A document to archive: the file, under its invoice, with the checksum recorded for it. */
4933
+ interface InvoiceDocumentToArchive extends InvoiceDocument {
4934
+ invoiceId: string;
4935
+ /** SHA-256 of `content`, lower-case hex. */
4936
+ checksum: string;
4937
+ archivedAt: Date;
4938
+ }
4939
+ /** A document as the archive hands it back. */
4940
+ type ArchivedInvoiceDocument = InvoiceDocumentToArchive;
4941
+ /**
4942
+ * Keeps each invoice's document, unalterable, for as long as the law asks —
4943
+ * in Germany 8 years from the end of the calendar year it was issued in, in
4944
+ * the European Union. The shipped adapters keep it in the database
4945
+ * (`SubscriptionInvoiceArchive`, which refuses every update and delete);
4946
+ * object storage with a retention lock is another way to implement it.
4947
+ */
4948
+ interface InvoiceArchivePort {
4949
+ /**
4950
+ * Stores the document under its invoice. Storing it again with the same
4951
+ * checksum is the same write and changes nothing; another checksum for an
4952
+ * invoice already archived is refused with
4953
+ * `SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED`, and the first stays.
4954
+ */
4955
+ store(document: InvoiceDocumentToArchive): Promise<void>;
4956
+ /** The document archived under the invoice, or `null` where none is. */
4957
+ read(invoiceId: string): Promise<ArchivedInvoiceDocument | null>;
4958
+ /**
4959
+ * Why the archive would let a document it keeps be changed or removed, or
4960
+ * `null` where it would not. Asked when invoices start, which are refused
4961
+ * while it names a reason: an archive that is not unalterable keeps no
4962
+ * document as the law asks, and would only be found out at an audit.
4963
+ */
4964
+ protectionProblem(): Promise<string | null>;
4965
+ }
4966
+ /** An invoice on its way to the subscriber. */
4967
+ interface InvoiceMail {
4968
+ invoice: SubscriptionInvoiceRecord;
4969
+ /** The invoice email on the subscriber's record. */
4970
+ to: string;
4971
+ /** The archived file, to attach. */
4972
+ document: InvoiceDocument;
4973
+ }
4974
+ /**
4975
+ * Sends an invoice to the subscriber with its file attached (`SC-PRIC-047`).
4976
+ *
4977
+ * The application words the message — in the subscriber's language, with a
4978
+ * link to the tenant's sign-in rather than to the file itself — and sends it
4979
+ * with its own mail infrastructure. It rejects where the message could not be
4980
+ * handed on; the platform then shows the failure to the operator, with the
4981
+ * rejection's message as the reason and the recipient's address taken out of
4982
+ * it, and tries again on the next run. The invoice stays issued either way: delivery is not
4983
+ * what makes it an invoice.
4984
+ *
4985
+ * A run claims the invoice before it calls this, so of several instances one
4986
+ * sends it. One that does not answer within a minute holds the invoice for
4987
+ * its answer: a late success is recorded, a late failure tried again.
4988
+ */
4989
+ interface InvoiceMailPort {
4990
+ deliver(mail: InvoiceMail): Promise<void>;
4991
+ }
4992
+ /** Where an invoice's document stands, as the invoice records it. */
4993
+ interface SubscriptionInvoiceDocumentRecord {
4994
+ invoiceId: string;
4995
+ /** SHA-256 of the rendered file, lower-case hex, recorded before it is archived. */
4996
+ checksum: string;
4997
+ mediaType: string;
4998
+ fileName: string;
4999
+ /** In bytes. */
5000
+ size: number;
5001
+ renderedAt: Date;
5002
+ /** When the archive confirmed it; `null` while the invoice waits for its document. */
5003
+ archivedAt: Date | null;
5004
+ /**
5005
+ * When the run that is mailing it took it on; `null` while no run holds it.
5006
+ * A claim older than the lease is taken on by the next run.
5007
+ */
5008
+ deliveryClaimedAt: Date | null;
5009
+ /** When it was handed to the mail; `null` until then. */
5010
+ deliveredAt: Date | null;
5011
+ /** The address it was sent to. */
5012
+ deliveredTo: string | null;
5013
+ }
5014
+ /** What is recorded of a rendering before its file is archived. */
5015
+ type NewInvoiceRendering = Pick<SubscriptionInvoiceDocumentRecord, 'checksum' | 'mediaType' | 'fileName' | 'size' | 'renderedAt'>;
5016
+
5017
+ /** The adapter that gave an answer, recorded beside the answer. */
5018
+ interface TaxAdapterIdentity {
5019
+ /** The adapter's package name, such as `@saasicat/tax-de`. */
5020
+ name: string;
5021
+ /** The adapter's version as published, such as `1.0.0`. */
5022
+ version: string;
5023
+ }
5024
+ /** The kinds of tax treatment every adapter shares, in the order they are named. */
5025
+ declare const TAX_TREATMENT_KINDS: readonly ["standard", "reduced", "exempt", "reverse-charge", "not-taxable", "small-business"];
5026
+ /**
5027
+ * What a tax treatment is, in the words every adapter shares.
5028
+ *
5029
+ * - `standard` and `reduced`: the issuer's country taxes the charge at a rate.
5030
+ * - `exempt`: the charge is exempt in the issuer's country.
5031
+ * - `reverse-charge`: the subscriber, a business in another country, owes the
5032
+ * tax instead of the issuer.
5033
+ * - `not-taxable`: the charge is not taxable in the issuer's country at all,
5034
+ * such as a service to a business outside the European Union.
5035
+ * - `small-business`: the issuer charges no tax under a small business scheme.
5036
+ */
5037
+ type TaxTreatmentKind = (typeof TAX_TREATMENT_KINDS)[number];
5038
+ /** The treatment an adapter decided, as a contract and an invoice record it. */
5039
+ interface TaxTreatment {
5040
+ kind: TaxTreatmentKind;
5041
+ /** The rate in percent, such as `19`; `0` for every kind that charges no tax. */
5042
+ rate: number;
5043
+ /**
5044
+ * The sentence the document has to carry for this treatment, such as the
5045
+ * reference to the reverse charge; `null` where the law asks for none.
5046
+ */
5047
+ note: string | null;
5048
+ /** Who decided it. */
5049
+ adapter: TaxAdapterIdentity;
5050
+ }
5051
+ /**
5052
+ * Where a subscriber stands for tax purposes, as its record stands: what an
5053
+ * adapter decides a treatment from.
5054
+ */
5055
+ interface SubscriberTaxOrigin {
5056
+ /** The country of the billing address, ISO 3166-1 alpha-2; `null` while unknown. */
5057
+ country: string | null;
5058
+ /**
5059
+ * Whether the subscriber is a business, as sign-up or the operator recorded
5060
+ * it; `null` while nobody has said. Never derived from a tax identifier: a
5061
+ * business outside the European Union may have none.
5062
+ */
5063
+ business: boolean | null;
5064
+ /** The VAT identification number as entered, validated or not. */
5065
+ vatId: string | null;
5066
+ /**
5067
+ * The same number, only when its latest completed check found it valid;
5068
+ * otherwise `null`. An adapter decides as though a subscriber whose number
5069
+ * is not validated had none (`SC-PRIC-040`).
5070
+ */
5071
+ validatedVatId: string | null;
5072
+ }
5073
+ /** The issuer as an adapter needs it: the operator, as `config/saas.yaml` names it. */
5074
+ interface TaxIssuer {
5075
+ /** ISO 3166-1 alpha-2; `null` where the configuration names none. */
5076
+ country: string | null;
5077
+ vatId: string | null;
5078
+ }
5079
+ /** What an adapter decides a treatment for. */
5080
+ interface TaxDecisionRequest {
5081
+ issuer: TaxIssuer;
5082
+ origin: SubscriberTaxOrigin;
5083
+ /**
5084
+ * The period the charge covers, as a charge records it: from `from` up to
5085
+ * `until`, which is the first moment after it. A rate that changed in
5086
+ * between is the adapter's to see.
5087
+ */
5088
+ period: {
5089
+ from: Date;
5090
+ until: Date;
5091
+ };
5092
+ /**
5093
+ * The installation's time zone, an IANA name such as `Europe/Berlin`: the
5094
+ * calendar in which the days of the period count (`SC-PRIC-045`).
5095
+ */
5096
+ timeZone: string;
5097
+ }
5098
+ /**
5099
+ * An adapter's answer: a treatment, or a sentence saying the case is not
5100
+ * supported. An unsupported case is refused before a contract exists rather
5101
+ * than invoiced with a guessed tax (`SC-PRIC-039`).
5102
+ */
5103
+ type TaxDecision = {
5104
+ supported: true;
5105
+ treatment: TaxTreatment;
5106
+ } | {
5107
+ supported: false;
5108
+ reason: string;
5109
+ };
5110
+ /**
5111
+ * A completed check of a VAT identification number, with its date and what
5112
+ * the service returned (`SC-PRIC-040`). Every check is recorded for the
5113
+ * subscriber and never rewritten: a reverse charge rests on the confirmation
5114
+ * that held when the invoice was issued, and neither a later check nor a
5115
+ * corrected number may take it away.
5116
+ */
5117
+ interface VatIdCheck {
5118
+ /** The number checked, exactly as it was stored when the check ran. */
5119
+ vatId: string;
5120
+ /**
5121
+ * When the service's answer was received, by the platform's clock, to the
5122
+ * millisecond. `keepsVatIdCheck` makes the check with the latest one count,
5123
+ * so a date of day precision — such as the request date VIES returns —
5124
+ * would make two checks of one day tie, and a date in the future would hold
5125
+ * back every check after it. Such a date belongs in `confirmation`.
5126
+ */
5127
+ checkedAt: Date;
5128
+ /** Whether the service confirmed the number as valid. */
5129
+ valid: boolean;
5130
+ /** The service that answered, such as `VIES`. */
5131
+ service: string;
5132
+ /**
5133
+ * What the service returned as its confirmation — a request identifier, the
5134
+ * name and address it holds for the number — as text, as the service sent it.
5135
+ */
5136
+ confirmation: Record<string, string>;
5137
+ }
5138
+ /**
5139
+ * How a check ended. A check that could not complete, such as while the
5140
+ * service is unavailable, is not a result: it leaves the number unvalidated
5141
+ * and is never read as valid.
5142
+ */
5143
+ type VatIdCheckOutcome = {
5144
+ completed: true;
5145
+ check: VatIdCheck;
5146
+ } | {
5147
+ completed: false;
5148
+ reason: string;
5149
+ };
5150
+ /**
5151
+ * How an application binds a tax adapter, the way it binds a payment gateway:
5152
+ * the adapter's name, which `config/saas.yaml#tax.adapter` names, and how to
5153
+ * build it from the options the file gives it. The options come from the file,
5154
+ * so the factory checks them and throws for one the adapter cannot take.
5155
+ */
5156
+ interface TaxAdapterFactory {
5157
+ /** The adapter's package name, such as `@saasicat/tax-de`. */
5158
+ readonly adapterName: string;
5159
+ create(options: Readonly<Record<string, unknown>>): TaxAdapter;
5160
+ }
5161
+ /**
5162
+ * The tax adapter of an installation: a package for the country of its issuer,
5163
+ * named in `config/saas.yaml`. An adapter is a template, not tax advice; its
5164
+ * documentation says what it decides and on which basis.
5165
+ */
5166
+ interface TaxAdapter extends TaxAdapterIdentity {
5167
+ /** The treatment of a charge, or why the case is not supported. Decides only; writes nothing. */
5168
+ decide(request: TaxDecisionRequest): TaxDecision;
5169
+ /**
5170
+ * Checks a VAT identification number with the service the adapter names.
5171
+ * Resolves to an outcome whatever the service does: an error, a timeout and
5172
+ * an answer the adapter cannot read are each a check that did not complete.
5173
+ */
5174
+ checkVatId(vatId: string, issuer: TaxIssuer): Promise<VatIdCheckOutcome>;
5175
+ /**
5176
+ * An invoice's tax by the rule this adapter's law names (`SC-PRIC-041`):
5177
+ * per rate, with the totals the invoice states. The amount it states is
5178
+ * the amount collected and the amount a payment settles.
5179
+ */
5180
+ invoiceTax(lines: readonly InvoiceTaxLine[]): TaxPerRate;
5181
+ /**
5182
+ * What an invoice lacks of the content the law requires of it
5183
+ * (`SC-PRIC-027`), as the fields it misses — `issuer.taxIdentifier`,
5184
+ * `subscriber.postalCode` — or none where it is complete. Asked before the
5185
+ * number is drawn; an invoice that lacks anything is not issued.
5186
+ */
5187
+ invoiceContentGaps(draft: InvoiceContentDraft): readonly string[];
5188
+ /**
5189
+ * What a rendered document lacks of the format the law requires
5190
+ * (`SC-PRIC-042`), or disagrees with in the invoice it was rendered from —
5191
+ * `format.embeddedXml`, `xml.totalGross` — or none where it is the file to
5192
+ * issue. A document with anything here is not archived, sent or offered;
5193
+ * the invoice keeps its number and waits for a document that passes.
5194
+ */
5195
+ invoiceDocumentGaps(document: InvoiceDocument, invoice: SubscriptionInvoiceRecord): Promise<readonly string[]>;
3938
5196
  }
3939
- /** Subscription lookup for `redeem()`. Sufficient for promo calculations. */
3940
- interface PromoSubscriptionLookup {
3941
- findById(subscriptionId: string, tx?: TransactionContext): Promise<{
3942
- id: string;
3943
- tenantId: string;
3944
- plan: string;
3945
- billingCycle: BillingCycle;
3946
- startedAt: Date | null;
3947
- } | null>;
5197
+ /** A line as an invoice's tax is computed from it. */
5198
+ interface InvoiceTaxLine {
5199
+ net: number;
5200
+ /** In per cent. */
5201
+ rate: number;
3948
5202
  }
3949
5203
  /**
3950
- * Aggregation adapter for the stats endpoint (`PromoCodesService.stats`).
3951
- * Consumers without an `InvoiceDiscount` table return '0.00'.
5204
+ * What an invoice says, without its number: what the adapter checks against
5205
+ * the content the law requires before the number is drawn.
3952
5206
  */
3953
- interface PromoRevenueDeductionAggregator {
3954
- /** Sum of the amountGross values for all redemptions of a promo code (Decimal-as-string). */
3955
- sumGrossForPromoCode(promoCodeId: string): Promise<string>;
5207
+ interface InvoiceContentDraft {
5208
+ issuer: LegalIdentity & PartyAddress;
5209
+ subscriber: LegalIdentity & PartyAddress;
5210
+ treatment: TaxTreatment;
5211
+ issueDate: string;
5212
+ dueDate: string;
5213
+ servicePeriod: {
5214
+ from: string;
5215
+ until: string;
5216
+ };
5217
+ lines: readonly {
5218
+ title: string;
5219
+ net: number;
5220
+ rate: number;
5221
+ }[];
3956
5222
  }
3957
5223
 
3958
5224
  type ContractLineItemKind = 'plan' | 'bundle' | 'discount';
@@ -4070,6 +5336,14 @@ interface SubscriptionContractRecord extends SubscriptionContractParties {
4070
5336
  * never presented as what was agreed.
4071
5337
  */
4072
5338
  partiesMigrated: boolean;
5339
+ /**
5340
+ * The tax treatment the installation's tax adapter decided when the
5341
+ * contract was concluded, with the adapter's name and version
5342
+ * (`SC-PRIC-038`). `null` for a contract concluded without an adapter,
5343
+ * before one existed, or by a path that does not ask one yet; each invoice
5344
+ * then takes the adapter's answer when it is issued.
5345
+ */
5346
+ taxTreatment: TaxTreatment | null;
4073
5347
  status: SubscriptionContractStatus;
4074
5348
  effectiveFrom: Date;
4075
5349
  effectiveUntil: Date | null;
@@ -4116,6 +5390,11 @@ interface NewSubscriptionContractData extends CreateSubscriptionContractData {
4116
5390
  * means `false`.
4117
5391
  */
4118
5392
  partiesMigrated?: boolean;
5393
+ /**
5394
+ * The treatment the platform had the tax adapter decide, recorded as it
5395
+ * was answered and never rewritten. Absent means `null`.
5396
+ */
5397
+ taxTreatment?: TaxTreatment | null;
4119
5398
  }
4120
5399
  interface TerminateSubscriptionContractData {
4121
5400
  /**
@@ -4224,16 +5503,115 @@ interface SubscriberContact extends PartyAddress {
4224
5503
  /** Where invoices will be sent. */
4225
5504
  invoiceEmail: string | null;
4226
5505
  }
5506
+ /**
5507
+ * Whether the subscriber is a business: part of its tax origin beside the
5508
+ * country and the VAT identification number (ADR 0013).
5509
+ */
5510
+ interface SubscriberTaxStatus {
5511
+ /**
5512
+ * As sign-up or the operator recorded it; `null` while nobody has said.
5513
+ * Never derived from a tax identifier: a business outside the European
5514
+ * Union may have none, and is still a business.
5515
+ */
5516
+ business: boolean | null;
5517
+ }
5518
+ /**
5519
+ * The address an invoice names. Sign-up asks for every one of them, and once
5520
+ * given they can be changed but not cleared: without them nothing can be
5521
+ * invoiced (`SC-PRIC-032`).
5522
+ */
5523
+ declare const SUBSCRIBER_INVOICE_ADDRESS_FIELDS: readonly ["addressLine1", "postalCode", "city", "country"];
5524
+ type SubscriberInvoiceAddressField = (typeof SUBSCRIBER_INVOICE_ADDRESS_FIELDS)[number];
5525
+ /**
5526
+ * Whether a subscriber can be given its next contract where a tax adapter
5527
+ * decides (`SC-PRIC-032`, `SC-PRIC-039`): which fields of the address an
5528
+ * invoice names are empty, and the adapter's sentence where it supports no
5529
+ * treatment for the subscriber as it stands. Computed when it is read, from the
5530
+ * record and the adapter as they are then.
5531
+ */
5532
+ interface SubscriberReadiness {
5533
+ ready: boolean;
5534
+ missing: SubscriberInvoiceAddressField[];
5535
+ /** The adapter's sentence, or `null` where it treats the subscriber. */
5536
+ taxRefusal: string | null;
5537
+ }
5538
+ /**
5539
+ * A tenant's subscriber as the operator sees it beside the tenant: who the
5540
+ * contracts are concluded with, what a tax adapter decides from, and — where
5541
+ * one decides — what holds its next contract back (`SC-PRIC-070`).
5542
+ */
5543
+ interface AdminTenantSubscriber {
5544
+ subscriber: {
5545
+ id: string;
5546
+ customerNumber: string;
5547
+ legalName: string;
5548
+ addressLine1: string | null;
5549
+ addressLine2: string | null;
5550
+ postalCode: string | null;
5551
+ city: string | null;
5552
+ country: string | null;
5553
+ business: boolean | null;
5554
+ vatId: string | null;
5555
+ /** Whether the check that counts for the VAT id found it valid. */
5556
+ vatIdValidated: boolean;
5557
+ taxNumber: string | null;
5558
+ /** Created from the application's own tenant record, not from what a customer entered. */
5559
+ migrated: boolean;
5560
+ } | null;
5561
+ /** `null` where no tax adapter decides, or the tenant has no subscriber. */
5562
+ readiness: SubscriberReadiness | null;
5563
+ }
5564
+ /**
5565
+ * The manifest capability that announces the operator's view of a tenant's
5566
+ * subscriber. The platform sets it where it serves the view.
5567
+ */
5568
+ declare const SUBSCRIBER_STANDING_CAPABILITY = "subscribers.read";
5569
+ /**
5570
+ * The manifest capability that says a tax adapter decides, so a subscriber can
5571
+ * be held back from its next contract: the operator's lists ask which only
5572
+ * where it is set, and are spared the question everywhere else.
5573
+ */
5574
+ declare const SUBSCRIBER_ATTENTION_CAPABILITY = "subscribers.attention";
5575
+ /**
5576
+ * How many tenants one question about attention may name: a page of a list,
5577
+ * not the whole of it. A list longer than this asks in pages.
5578
+ */
5579
+ declare const SUBSCRIBER_ATTENTION_PAGE_SIZE = 200;
5580
+ /**
5581
+ * The manifest capability that announces the operator's corrections of a
5582
+ * subscriber — its legal identity, its business status, a check of its VAT
5583
+ * identification number — beside the subscriber's view.
5584
+ */
5585
+ declare const SUBSCRIBER_CORRECTION_CAPABILITY = "subscribers.correct";
5586
+ /** A tenant whose subscriber cannot be given its next contract, and why. */
5587
+ interface AdminSubscriberAttention {
5588
+ tenantId: string;
5589
+ readiness: SubscriberReadiness;
5590
+ }
5591
+ /** A live subscriber with the check of its VAT id that counts now. */
5592
+ interface SubscriberWithCurrentCheck {
5593
+ subscriber: SubscriberRecord;
5594
+ currentVatIdCheck: SubscriberVatIdCheckRecord | null;
5595
+ }
4227
5596
  /** A subscriber's master data, as it stands. */
4228
- type SubscriberDetails = LegalIdentity & SubscriberContact;
5597
+ type SubscriberDetails = LegalIdentity & SubscriberContact & SubscriberTaxStatus;
4229
5598
  /**
4230
5599
  * What a subscriber is created with.
4231
5600
  *
4232
- * Only the legal name is required. The address, the tax identifiers and the
4233
- * invoice email stay optional until sign-up asks for them and invoicing
4234
- * requires them; an absent or blank value is recorded as unknown.
5601
+ * Only the legal name is required. The address, the tax identifiers, the
5602
+ * business status and the invoice email stay optional until sign-up asks for
5603
+ * them and invoicing requires them; an absent or blank value is recorded as
5604
+ * unknown.
4235
5605
  */
4236
- type NewSubscriberDetails = Pick<SubscriberDetails, 'legalName'> & Partial<Omit<SubscriberDetails, 'legalName'>>;
5606
+ type NewSubscriberDetails = Pick<SubscriberDetails, 'legalName'> & Partial<Omit<SubscriberDetails, 'legalName'>> & {
5607
+ /**
5608
+ * A check of `vatId` made before the subscriber existed — in a
5609
+ * sign-up's step 4, or by `SubscriberService.assessNewSubscriber` —
5610
+ * recorded with it when it is created, so its first contract is
5611
+ * decided from it. A check of another number is refused.
5612
+ */
5613
+ vatIdCheck?: VatIdCheck | null;
5614
+ };
4237
5615
  interface SubscriberRecord extends SubscriberDetails {
4238
5616
  id: string;
4239
5617
  /**
@@ -4287,13 +5665,15 @@ interface SubscriberIdentityDelta {
4287
5665
  previous: SubscriberIdentityValues;
4288
5666
  corrected: SubscriberIdentityValues;
4289
5667
  }
4290
- /** What a repository writes for a correction the service has accepted. */
5668
+ /**
5669
+ * What a repository writes for a correction the service has accepted. The
5670
+ * repository dates it, while it holds the subscriber's row lock.
5671
+ */
4291
5672
  interface SubscriberCorrectionData {
4292
5673
  /** The new values; a field equal to what is stored is not recorded. */
4293
5674
  corrected: SubscriberIdentityValues;
4294
5675
  reason: string;
4295
5676
  correctedBy: string;
4296
- correctedAt: Date;
4297
5677
  }
4298
5678
  /** One correction of a subscriber's legal identity, as it was recorded. */
4299
5679
  interface SubscriberCorrectionRecord {
@@ -4305,6 +5685,12 @@ interface SubscriberCorrectionRecord {
4305
5685
  corrected: SubscriberIdentityValues;
4306
5686
  reason: string;
4307
5687
  correctedBy: string;
5688
+ /**
5689
+ * When the write that made it held the subscriber's row lock, by the
5690
+ * platform's clock. The list is in the order the database numbered the
5691
+ * writes; on one clock these dates follow that order, while two instances
5692
+ * whose clocks differ may date neighbouring corrections out of it.
5693
+ */
4308
5694
  correctedAt: Date;
4309
5695
  }
4310
5696
  /** The outcome of writing a correction: nothing is recorded when no value moved. */
@@ -4312,128 +5698,142 @@ interface SubscriberCorrectionResult {
4312
5698
  subscriber: SubscriberRecord;
4313
5699
  correction: SubscriberCorrectionRecord | null;
4314
5700
  }
4315
-
4316
5701
  /**
4317
- * What made the charge arise. activation: the first period of a subscription. renewal: a later period. planChange: what an immediate plan change adds. bundleBooking: the first, short period of an add-on. credit: a decision that reduces what is owed. correction: a counter-entry reversing an earlier charge, which is how the append-only journal answers a mistake.
5702
+ * The details a subscriber's tax origin consists of, and whose change is
5703
+ * recorded with its date — in the order they are named and shown.
4318
5704
  */
4319
- type SubscriberChargeOrigin = 'activation' | 'renewal' | 'planChange' | 'bundleBooking' | 'credit' | 'correction';
4320
- /**
4321
- * Which kind of contract line the charge realises.
4322
- */
4323
- type SubscriberChargeSource = 'plan' | 'bundle' | 'discount';
5705
+ declare const SUBSCRIBER_TAX_ORIGIN_FIELDS: readonly ["country", "business", "vatId"];
5706
+ type SubscriberTaxOriginField = (typeof SUBSCRIBER_TAX_ORIGIN_FIELDS)[number];
5707
+ /** Tax origin values by field, holding only the fields a change moved. */
5708
+ type SubscriberTaxOriginValues = Partial<Pick<SubscriberDetails, SubscriberTaxOriginField>>;
4324
5709
  /**
4325
- * One charge in a subscriber's account: an amount that became due for a period, derived from a line of the contract in force. The journal is append-only — a charge is never updated, and a mistake is answered with a counter-entry rather than an edit, so the account says what happened rather than what somebody thinks today. A charge is net: the tax is decided by the tax adapter when the charge is invoiced, from the subscriber's origin on that day, and computed once per rate on the invoice.
5710
+ * One change of a subscriber's tax origin, as it was recorded: written in the
5711
+ * same transaction as the change, whichever way it arrived — a contact change
5712
+ * of the country, a correction of the VAT identification number, a change of
5713
+ * the business status — and never rewritten. A change applies from the
5714
+ * subscriber's next invoice (`SC-PRIC-043`).
4326
5715
  */
4327
- interface SubscriberCharge {
4328
- /**
4329
- * The charge's own identifier. Never shown to a customer as an invoice number: invoice numbering is sequential, gapless and legally constrained per country, and an identifier somebody has already seen on a screen cannot become one later without confusion.
4330
- */
5716
+ interface SubscriberTaxOriginChangeRecord {
4331
5717
  id: string;
4332
- /**
4333
- * The party the contract is concluded with, whose account this is. It outlives the tenant.
4334
- */
4335
5718
  subscriberId: string;
5719
+ /** The values the change replaced. */
5720
+ previous: SubscriberTaxOriginValues;
5721
+ /** The values it wrote. */
5722
+ changed: SubscriberTaxOriginValues;
5723
+ /** Who changed it, as an actor tag the audit log would write. */
5724
+ changedBy: string;
4336
5725
  /**
4337
- * The tenant the subscription belonged to when the charge arose.
4338
- */
4339
- tenantId: string;
4340
- /**
4341
- * The subscription the charge arose on. Part of the natural key.
4342
- */
4343
- subscriptionId: string;
4344
- /**
4345
- * The contract whose line the charge realises.
4346
- */
4347
- contractId: string;
4348
- /**
4349
- * The contract line the charge came from, so that every invoice line can be traced back to what was agreed.
4350
- */
4351
- contractLineItemId: string;
4352
- origin: SubscriberChargeOrigin;
4353
- source: SubscriberChargeSource;
4354
- /**
4355
- * Which thing of its source the charge is for, stable across contracts: the subscription id for the plan, the booking id for an add-on, the discount's key for a discount. Part of the natural key (subscriptionId, source, sourceRef, periodStart, origin), which is unique — a charge derived twice is written once.
4356
- */
4357
- sourceRef: string;
4358
- /**
4359
- * The start of the period the charge belongs to — the grouping key for an invoice.
4360
- */
4361
- periodStart: string;
4362
- periodEnd: string;
4363
- /**
4364
- * ISO 4217, as the contract line records it — recorded per charge so that a charge written in 2026 still means what it meant after the configured currency changes.
4365
- */
4366
- currency: string;
4367
- /**
4368
- * Net, rounded to the currency's decimal places once, when the charge is written; the written figure is the truth from then on. Negative for a discount.
5726
+ * When the write that made it held the subscriber's row lock, by the
5727
+ * platform's clock. The list is in the order the database numbered the
5728
+ * writes; on one clock these dates follow that order, while two instances
5729
+ * whose clocks differ may date neighbouring changes out of it.
4369
5730
  */
4370
- amountNet: number;
5731
+ changedAt: Date;
4371
5732
  /**
4372
- * When the amount became due. The account is ordered by this, not by when the row was written.
5733
+ * Why it was changed: the reason of the correction or of the change of
5734
+ * business status that made it; `null` for a change of the country, which
5735
+ * comes with the contact details and states none.
4373
5736
  */
4374
- bookedAt: string;
4375
- createdAt: string;
5737
+ reason: string | null;
4376
5738
  }
4377
-
4378
- /** A charge as a store reads it back: the wire format, with its dates as dates. */
4379
- interface SubscriberChargeRecord {
4380
- id: string;
4381
- subscriberId: string;
4382
- tenantId: string;
4383
- subscriptionId: string;
4384
- contractId: string;
4385
- contractLineItemId: string;
4386
- origin: SubscriberChargeOrigin;
4387
- source: SubscriberChargeSource;
4388
- /** The subscription id for the plan, the booking id for an add-on, the discount's key. */
4389
- sourceRef: string;
4390
- periodStart: Date;
4391
- periodEnd: Date;
4392
- currency: string;
4393
- /** Net, rounded to the cent when written. Negative for a discount. */
4394
- amountNet: number;
4395
- /** When the amount became due. */
4396
- bookedAt: Date;
4397
- createdAt: Date;
5739
+ /** What a repository writes for a change of the business status the service has accepted. */
5740
+ interface SubscriberBusinessStatusData {
5741
+ business: boolean | null;
5742
+ /** Who changes it, as an actor tag the audit log would write. */
5743
+ changedBy: string;
5744
+ /** Why, in the operator's words: whether it is a business decides the tax treatment. */
5745
+ reason: string;
5746
+ }
5747
+ /** The outcome of writing a business status: nothing is recorded when it does not move. */
5748
+ interface SubscriberBusinessStatusResult {
5749
+ subscriber: SubscriberRecord;
5750
+ change: SubscriberTaxOriginChangeRecord | null;
4398
5751
  }
4399
- /** A charge to write: everything but what the store assigns. */
4400
- type NewSubscriberCharge = Omit<SubscriberChargeRecord, 'id' | 'createdAt'>;
4401
5752
  /**
4402
- * The manifest capability that announces the operator's view of an account.
4403
- * The platform sets it where it serves the view, and the admin UI shows the
4404
- * view only where it is set.
5753
+ * One completed check of a subscriber's VAT identification number, as it was
5754
+ * recorded: never rewritten, whether it counts now or not.
4405
5755
  */
4406
- declare const SUBSCRIBER_ACCOUNT_CAPABILITY = "charges.read";
4407
- /** Whose account it is, as the operator is shown it. */
4408
- interface AdminAccountHolder {
5756
+ interface SubscriberVatIdCheckRecord extends VatIdCheck {
4409
5757
  id: string;
4410
- /** What the customer quotes, and what an invoice will carry. */
4411
- customerNumber: string;
4412
- legalName: string;
5758
+ subscriberId: string;
5759
+ /** When the check was recorded, by the platform's clock. */
5760
+ recordedAt: Date;
4413
5761
  }
4414
- /** One charge of an account, with the name its contract line gives it. */
4415
- interface AdminAccountEntry {
4416
- /** The charge as the journal holds it. */
4417
- charge: SubscriberCharge;
5762
+ /** The outcome of recording a check: the check as recorded, and the one that counts now. */
5763
+ interface RecordedVatIdCheck {
5764
+ recorded: SubscriberVatIdCheckRecord;
4418
5765
  /**
4419
- * The contract line's own title, as it was when the contract was
4420
- * concluded, or `null` where that line cannot be read.
5766
+ * The check that counts for the subscriber's number from now on: the one
5767
+ * just recorded, one recorded earlier that completed later, or none — a
5768
+ * check of a number the subscriber no longer has counts for nothing.
4421
5769
  */
4422
- title: string | null;
5770
+ current: SubscriberVatIdCheckRecord | null;
4423
5771
  }
4424
5772
  /**
4425
- * A tenant's account as the operator reads it: the charges of the subscription
4426
- * it holds, newest first — by when each became due, then by its period, then
4427
- * the plan before its add-ons before its discounts.
4428
- *
4429
- * No total: without invoices and payments, a sum would be what was charged so
4430
- * far, and it would be read as what is owed.
5773
+ * What became of a check of a subscriber's VAT identification number: the
5774
+ * check as it was recorded, and whether it counts for the number held — or why
5775
+ * it did not complete, in which case nothing was recorded and the number stays
5776
+ * as validated as it was (`SC-PRIC-040`).
4431
5777
  */
4432
- interface AdminSubscriberAccount {
4433
- /** `null` for a tenant that has no subscriber. */
4434
- holder: AdminAccountHolder | null;
4435
- entries: AdminAccountEntry[];
5778
+ type SubscriberVatIdCheckResult = {
5779
+ completed: true;
5780
+ check: SubscriberVatIdCheckRecord;
5781
+ counts: boolean;
5782
+ } | {
5783
+ completed: false;
5784
+ reason: string;
5785
+ };
5786
+ /** A check of a subscriber's VAT identification number, as the operator is shown it. */
5787
+ type AdminVatIdCheckOutcome = {
5788
+ completed: true;
5789
+ valid: boolean;
5790
+ /** When the service answered, as an ISO string. */
5791
+ checkedAt: string;
5792
+ service: string;
5793
+ /** Whether it is the check that counts for the number held now. */
5794
+ counts: boolean;
5795
+ } | {
5796
+ completed: false;
5797
+ reason: string;
5798
+ };
5799
+ /**
5800
+ * The answer to an operator's correction or check: the subscriber as it now
5801
+ * stands, and the check a VAT identification number got, where one was made.
5802
+ */
5803
+ interface AdminSubscriberCorrected {
5804
+ subscriber: AdminTenantSubscriber;
5805
+ vatIdCheck: AdminVatIdCheckOutcome | null;
4436
5806
  }
5807
+ /**
5808
+ * One entry of a subscriber's history as the operator reads it, the latest
5809
+ * first: a correction of its legal identity, a change of its country or
5810
+ * business status, or a check of its VAT identification number. Dates are ISO
5811
+ * strings.
5812
+ */
5813
+ type AdminSubscriberHistoryEntry = {
5814
+ kind: 'identity-corrected';
5815
+ at: string;
5816
+ by: string;
5817
+ previous: SubscriberIdentityValues;
5818
+ corrected: SubscriberIdentityValues;
5819
+ reason: string;
5820
+ } | {
5821
+ kind: 'tax-origin-changed';
5822
+ at: string;
5823
+ by: string;
5824
+ previous: SubscriberTaxOriginValues;
5825
+ changed: SubscriberTaxOriginValues;
5826
+ /** `null` for a change of the country with the contact details. */
5827
+ reason: string | null;
5828
+ } | {
5829
+ kind: 'vat-id-checked';
5830
+ at: string;
5831
+ vatId: string;
5832
+ valid: boolean;
5833
+ service: string;
5834
+ /** Whether it is the check that counts for the number held now. */
5835
+ counts: boolean;
5836
+ };
4437
5837
 
4438
5838
  /**
4439
5839
  * Snapshot form of a `Subscription` row for the EntitlementService
@@ -4592,6 +5992,79 @@ interface SubscriptionBundleRepository {
4592
5992
  * implementations to avoid drift.
4593
5993
  */
4594
5994
  countActiveByBundleVersionId(bundleVersionId: string, asOf?: Date): Promise<number>;
5995
+ /**
5996
+ * Every booking of the add-on version `bundleVersionId`, in every tenant,
5997
+ * whatever its state. The platform reads it to find the bookings an add-on
5998
+ * retirement reaches, inside the RLS bypass, and decides itself which of
5999
+ * them are still running.
6000
+ *
6001
+ * Optional, so a repository written before it keeps working; retiring an
6002
+ * add-on version over one without it is refused at start-up.
6003
+ */
6004
+ listOfVersion?(bundleVersionId: string): Promise<SubscriptionBundleRecord[]>;
6005
+ /**
6006
+ * Moves the booking `subscriptionBundleId` from the add-on version `from`
6007
+ * onto `to` and answers it as it now stands, or null where it is not on
6008
+ * `from` any more, or gone. Nothing else changes: its period, its terms,
6009
+ * its rhythm and its cancellation stay as they are. Conditional on `from`,
6010
+ * so a move and the put-back of a move that could not write its contract
6011
+ * never write over each other, nor two runs over one booking.
6012
+ *
6013
+ * An add-on retirement moves a booking at its date, and a subscriber by
6014
+ * taking a newer version offered (`SC-BUN-058`). Optional, so a repository
6015
+ * written before it keeps working; retiring an add-on version over one
6016
+ * without it is refused at start-up, and no newer version is offered.
6017
+ */
6018
+ moveToVersion?(subscriptionBundleId: string, from: string, to: string): Promise<SubscriptionBundleRecord | null>;
6019
+ /**
6020
+ * Schedules the booking `subscriptionBundleId` to continue on the add-on
6021
+ * version `to` from `effectiveAt` — `pendingBundleVersionId` and
6022
+ * `pendingVersionEffectiveAt` — and answers it as it now stands, or null
6023
+ * where it is not on `from` any more, already has a switch scheduled, or is
6024
+ * gone. Nothing else changes, its version included: the switch is made at
6025
+ * that moment by the run (`listScheduledVersionsDue`).
6026
+ *
6027
+ * Optional, and so are the two methods after it: without all three, a
6028
+ * newer version that takes something away is not offered.
6029
+ */
6030
+ scheduleVersion?(subscriptionBundleId: string, switchTo: {
6031
+ from: string;
6032
+ to: string;
6033
+ effectiveAt: Date;
6034
+ }): Promise<SubscriptionBundleRecord | null>;
6035
+ /**
6036
+ * Clears the switch scheduled for the booking `subscriptionBundleId`, and
6037
+ * answers it as it now stands; null where no switch to `to` is scheduled
6038
+ * any more, or it is gone. Its version stays as it is.
6039
+ */
6040
+ unscheduleVersion?(subscriptionBundleId: string, to: string): Promise<SubscriptionBundleRecord | null>;
6041
+ /**
6042
+ * Every booking, in every tenant and whatever its state, with a switch
6043
+ * scheduled to take effect at or before `asOf` — oldest first. The
6044
+ * platform reads it inside the RLS bypass and decides itself which of
6045
+ * them still move.
6046
+ */
6047
+ listScheduledVersionsDue?(asOf: Date): Promise<SubscriptionBundleRecord[]>;
6048
+ /**
6049
+ * Ends at `input.at` the booking `subscriptionBundleId` whose cancellation
6050
+ * was declared for a later date, provided its effective date is still
6051
+ * `input.expectedCanceledEffectiveAt`, and answers it as it now stands;
6052
+ * null where its cancellation moved meanwhile, or it is gone. Both
6053
+ * cancellation fields become `input.at`. What a feature withdrawn for an
6054
+ * external reason lets a subscriber do, cancelled or not; a booking not
6055
+ * cancelled is ended through `cancel`.
6056
+ *
6057
+ * Optional, so a repository written before it keeps working; without it, a
6058
+ * booking already cancelled for a later date cannot be ended at once.
6059
+ */
6060
+ endNow?(subscriptionBundleId: string, input: EndNowInput): Promise<SubscriptionBundleRecord | null>;
6061
+ }
6062
+ /** What ending at once something cancelled for a later date is told to write. */
6063
+ interface EndNowInput {
6064
+ /** The moment it ends, and the moment that is declared. */
6065
+ readonly at: Date;
6066
+ /** The effective date of the cancellation the caller read: the write is conditional on it. */
6067
+ readonly expectedCanceledEffectiveAt: Date;
4595
6068
  }
4596
6069
  /**
4597
6070
  * Append-only repository for V3 SubscriptionContracts. Contracts are the
@@ -4694,19 +6167,66 @@ interface SubscriberRepository {
4694
6167
  findById(subscriberId: string, tx?: TransactionContext): Promise<SubscriberRecord | null>;
4695
6168
  /** The subscriber live for this tenant, or `null` when it has none. */
4696
6169
  findByTenantId(tenantId: string, tx?: TransactionContext): Promise<SubscriberRecord | null>;
4697
- /** Writes the members given and keeps the rest; `null` when no such subscriber exists. */
4698
- updateContact(subscriberId: string, change: SubscriberContactChange, tx?: TransactionContext): Promise<SubscriberRecord | null>;
6170
+ /**
6171
+ * Writes the members given and keeps the rest; `null` when no such
6172
+ * subscriber exists. Read and written under the lock a correction takes. A
6173
+ * change of the country is a change of the tax origin and is recorded as
6174
+ * one in the same step, by `changedBy` and dated while the lock is held.
6175
+ */
6176
+ updateContact(subscriberId: string, change: SubscriberContactChange, changedBy: string, tx?: TransactionContext): Promise<SubscriberRecord | null>;
4699
6177
  /**
4700
6178
  * Writes a correction of the legal identity and records it, in one step:
4701
6179
  * the subscriber is read and changed under a lock, so the values recorded
4702
6180
  * as replaced are the ones this write replaced even when two corrections
4703
- * arrive at once. A field whose stored value already equals the corrected
4704
- * one is left out of the record, and when none differs nothing is written
4705
- * and `correction` is `null`. `null` when no such subscriber exists.
6181
+ * arrive at once. The correction is dated while the lock is held and
6182
+ * numbered by the database as it is written. A field whose stored value
6183
+ * already equals the corrected one is left out of the record, and when none
6184
+ * differs nothing is written and `correction` is `null`. `null` when no
6185
+ * such subscriber exists.
6186
+ *
6187
+ * A corrected VAT identification number is a change of the tax origin and
6188
+ * is recorded as one too, by the same actor and with the same date.
6189
+ * The check that counted for the number it replaced counts no more — it
6190
+ * would otherwise validate that number again should it come back — and
6191
+ * stays recorded (`listVatIdChecks`); the number written is held from that
6192
+ * date (`vatIdSince`), so no check completed before it counts for it.
4706
6193
  */
4707
6194
  correctIdentity(subscriberId: string, data: SubscriberCorrectionData, tx?: TransactionContext): Promise<SubscriberCorrectionResult | null>;
4708
- /** Every correction of this subscriber, the latest first. */
6195
+ /** Every correction of this subscriber, the latest written first. */
4709
6196
  listCorrections(subscriberId: string): Promise<SubscriberCorrectionRecord[]>;
6197
+ /**
6198
+ * Writes whether the subscriber is a business and records the change of
6199
+ * its tax origin, in one step and under the same lock as a correction,
6200
+ * dated while the lock is held. When the stored status already equals the
6201
+ * one given nothing is written and `change` is `null`. `null` when no such
6202
+ * subscriber exists.
6203
+ */
6204
+ changeBusinessStatus(subscriberId: string, data: SubscriberBusinessStatusData, tx?: TransactionContext): Promise<SubscriberBusinessStatusResult | null>;
6205
+ /**
6206
+ * Records a completed check of the subscriber's VAT identification number.
6207
+ * Every check is recorded and none is rewritten or removed: it is the
6208
+ * evidence a reverse charge rests on. Under the lock a correction takes it
6209
+ * then decides whether the check counts from now on — only for the number
6210
+ * the subscriber holds, completed since it holds it, and never over a check
6211
+ * that completed later (`keepsVatIdCheck` from `@saasicat/core` decides).
6212
+ * `null` when no such subscriber exists.
6213
+ */
6214
+ recordVatIdCheck(subscriberId: string, check: VatIdCheck, tx?: TransactionContext): Promise<RecordedVatIdCheck | null>;
6215
+ /**
6216
+ * The check that counts for the subscriber's number now, or `null` while
6217
+ * none does: never checked, or corrected since.
6218
+ */
6219
+ findCurrentVatIdCheck(subscriberId: string, tx?: TransactionContext): Promise<SubscriberVatIdCheckRecord | null>;
6220
+ /** Every check recorded for this subscriber, counting or not, the latest checked first. */
6221
+ listVatIdChecks(subscriberId: string): Promise<SubscriberVatIdCheckRecord[]>;
6222
+ /**
6223
+ * The live subscribers of these tenants, each with the check of its VAT id
6224
+ * that counts now, in at most a few reads — what the operator's lists mark
6225
+ * a subscriber from. A tenant without a live subscriber is left out.
6226
+ */
6227
+ listForTenants(tenantIds: readonly string[], tx?: TransactionContext): Promise<SubscriberWithCurrentCheck[]>;
6228
+ /** Every recorded change of this subscriber's tax origin, the latest written first. */
6229
+ listTaxOriginChanges(subscriberId: string): Promise<SubscriberTaxOriginChangeRecord[]>;
4710
6230
  }
4711
6231
  /**
4712
6232
  * The subscriber's account: the charges that became due, one per contract line
@@ -4729,6 +6249,113 @@ interface SubscriberLedgerRepository {
4729
6249
  /** Every charge of one subscription, oldest period first. */
4730
6250
  listBySubscription(subscriptionId: string, tx?: TransactionContext): Promise<SubscriberChargeRecord[]>;
4731
6251
  }
6252
+ /**
6253
+ * The invoices issued from the charge journal, and the one number range they
6254
+ * are numbered in (`SC-PRIC-072`).
6255
+ *
6256
+ * An invoice is written once and never changed. Its number is drawn when it is
6257
+ * written, in the same transaction, so an invoice that fails to be written
6258
+ * leaves no gap; the range restarts at 1 each year.
6259
+ */
6260
+ interface SubscriptionInvoiceRepository {
6261
+ /**
6262
+ * The subscriptions with a charge to invoice: a charge on no invoice in a
6263
+ * group — the charges booked at the same moment under the same contract —
6264
+ * that carries an amount (`SC-PRIC-048`). By id, at most `limit`, after
6265
+ * `after` where it is given, so a caller reads them page by page and a
6266
+ * subscription held back on one run never keeps the others from the next.
6267
+ */
6268
+ listSubscriptionsWithUninvoicedCharges(page: {
6269
+ limit: number;
6270
+ after?: string;
6271
+ }, tx?: TransactionContext): Promise<string[]>;
6272
+ /** The charges of one subscription on no invoice, oldest period first. */
6273
+ listUninvoicedCharges(subscriptionId: string, tx?: TransactionContext): Promise<SubscriberChargeRecord[]>;
6274
+ /**
6275
+ * Draws the next number of the invoice's year and writes the invoice with
6276
+ * its lines, in one transaction — the caller's where it passes one. Two
6277
+ * calls at once draw two numbers one after the other, never the same one
6278
+ * and never with a gap between them.
6279
+ *
6280
+ * Refuses with `SUBSCRIPTION_INVOICE_CHARGE_INVOICED` (`moved`) where a
6281
+ * charge already stands on an invoice: nothing is written, and the number
6282
+ * is not drawn.
6283
+ */
6284
+ issue(invoice: NewSubscriptionInvoice, tx?: TransactionContext): Promise<SubscriptionInvoiceRecord>;
6285
+ findById(id: string, tx?: TransactionContext): Promise<SubscriptionInvoiceRecord | null>;
6286
+ /** Every invoice of one subscriber, the latest number first. */
6287
+ listBySubscriber(subscriberId: string, tx?: TransactionContext): Promise<SubscriptionInvoiceRecord[]>;
6288
+ /**
6289
+ * The prefixes the issued invoices carry, at most two: one is the range,
6290
+ * a second one says the prefix was changed under it (`SC-PRIC-024`).
6291
+ */
6292
+ listIssuedNumberPrefixes(tx?: TransactionContext): Promise<string[]>;
6293
+ /**
6294
+ * Records the rendering of an invoice's document before its file is
6295
+ * archived (`SC-AUD-019`), once. A rendering recorded again with the same
6296
+ * checksum is the same write and answers the record as it stands; another
6297
+ * checksum is refused with `SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED`
6298
+ * (`moved`), and the first stays: `replaceRendering` is the one write that
6299
+ * puts another in its place.
6300
+ */
6301
+ recordRendering(invoiceId: string, rendering: NewInvoiceRendering, tx?: TransactionContext): Promise<SubscriptionInvoiceDocumentRecord>;
6302
+ /**
6303
+ * Records a rendering in place of the one recorded with `replaced` as its
6304
+ * checksum, while the invoice is not archived (`SC-AUD-019`): until the
6305
+ * archive holds a file, nothing has been handed out. Answers the record as
6306
+ * it stands now, or `null` where the recorded checksum is another by now or
6307
+ * the invoice is archived — a run that read it before another one changed it.
6308
+ */
6309
+ replaceRendering(invoiceId: string, rendering: NewInvoiceRendering, replaced: string, tx?: TransactionContext): Promise<SubscriptionInvoiceDocumentRecord | null>;
6310
+ /**
6311
+ * Records that the archive confirmed the document whose checksum is
6312
+ * `checksum`; `false` where it was already recorded, or the recorded
6313
+ * checksum is another.
6314
+ */
6315
+ markArchived(invoiceId: string, at: Date, checksum: string, tx?: TransactionContext): Promise<boolean>;
6316
+ /**
6317
+ * Claims an archived invoice's mail for the run calling it at `at`: where
6318
+ * it is not delivered and no run holds it, or the run that holds it took it
6319
+ * on before `staleBefore`. `true` where this run holds it now — of two
6320
+ * concurrent calls exactly one does — so that an invoice is mailed once
6321
+ * however many instances run (`SC-PRIC-047`).
6322
+ */
6323
+ claimDelivery(invoiceId: string, at: Date, staleBefore: Date, tx?: TransactionContext): Promise<boolean>;
6324
+ /**
6325
+ * Records that the invoice was handed to the mail, provided it is still
6326
+ * held by the claim taken at `claimedAt` and not delivered. `false` where it
6327
+ * is not: the claim went stale and another run took the invoice on.
6328
+ */
6329
+ markDelivered(invoiceId: string, delivery: {
6330
+ claimedAt: Date;
6331
+ at: Date;
6332
+ to: string;
6333
+ }, tx?: TransactionContext): Promise<boolean>;
6334
+ /**
6335
+ * Lets go of the claim taken at `claimedAt`, so the next run tries again.
6336
+ * Does nothing where the invoice is delivered or held by another claim.
6337
+ */
6338
+ releaseDelivery(invoiceId: string, claimedAt: Date, tx?: TransactionContext): Promise<void>;
6339
+ /** Where the invoice's document stands, or `null` before it was rendered. */
6340
+ findDocument(invoiceId: string, tx?: TransactionContext): Promise<SubscriptionInvoiceDocumentRecord | null>;
6341
+ /**
6342
+ * The invoices whose document the archive has not confirmed yet —
6343
+ * not rendered, or rendered and not archived (`SC-PRIC-033`). By id, at
6344
+ * most `limit`, after `after` where it is given.
6345
+ */
6346
+ listAwaitingDocument(page: {
6347
+ limit: number;
6348
+ after?: string;
6349
+ }, tx?: TransactionContext): Promise<string[]>;
6350
+ /**
6351
+ * The invoices archived and not yet handed to the mail, page by page as
6352
+ * above — whether or not a run holds them, which `claimDelivery` decides.
6353
+ */
6354
+ listAwaitingDelivery(page: {
6355
+ limit: number;
6356
+ after?: string;
6357
+ }, tx?: TransactionContext): Promise<string[]>;
6358
+ }
4732
6359
  /**
4733
6360
  * Display form of a subscription for the tenant self-service UI.
4734
6361
  * Richer than `SubscriptionRecord` (which is only the aggregation form); contains
@@ -4863,6 +6490,16 @@ interface SubscriptionUsagePort {
4863
6490
  * port without it is refused at start-up.
4864
6491
  */
4865
6492
  listBoundToVersion?(planVersionId: string): Promise<TenantSubscriptionUsage[]>;
6493
+ /**
6494
+ * The subscriptions `subscriptionIds`, in whichever tenant, each with the
6495
+ * tenant it belongs to; an id that names none is left out. The platform
6496
+ * reads it inside the RLS bypass to find the subscription of each booking
6497
+ * an add-on retirement reaches.
6498
+ *
6499
+ * Optional, so a port written before it keeps working; retiring an add-on
6500
+ * version over a port without it is refused at start-up.
6501
+ */
6502
+ listByIds?(subscriptionIds: readonly string[]): Promise<TenantSubscriptionUsage[]>;
4866
6503
  }
4867
6504
  /**
4868
6505
  * Returns the current usage for all quotaKeys of a tenant declared via
@@ -5061,6 +6698,14 @@ interface CancelSubscriptionInput {
5061
6698
  terminateNow: boolean;
5062
6699
  minimumTermUntil?: Date;
5063
6700
  }
6701
+ /** What `endNow` answers with. */
6702
+ interface EndSubscriptionNowResult {
6703
+ /** False where the cancellation moved since it was read, and nothing was written. */
6704
+ ended: boolean;
6705
+ canceledAt: Date | null;
6706
+ canceledEffectiveAt: Date | null;
6707
+ status: string;
6708
+ }
5064
6709
  /** What `cancelSubscription` answers with. */
5065
6710
  interface CancelSubscriptionResult {
5066
6711
  canceledAt: Date | null;
@@ -5130,6 +6775,22 @@ interface TenantSubscriptionWritePort {
5130
6775
  * unchanged, which is the ordinary case.
5131
6776
  */
5132
6777
  cancelSubscription(tenantId: string, input: CancelSubscriptionInput): Promise<CancelSubscriptionResult>;
6778
+ /**
6779
+ * Ends at `input.at` a subscription whose cancellation was declared for a
6780
+ * later date, provided its effective date is still
6781
+ * `input.expectedCanceledEffectiveAt`: both cancellation fields become
6782
+ * `input.at` and the status `CANCELED`. Answers `ended: false`, with the
6783
+ * stored fields, where the cancellation moved meanwhile. A conditional
6784
+ * claim, like `cancelSubscription`, so an end written in between is not
6785
+ * written over.
6786
+ *
6787
+ * What a feature withdrawn for an external reason lets a subscriber do,
6788
+ * cancelled or not; a subscription not cancelled is ended through
6789
+ * `cancelSubscription` with `terminateNow`. Optional, so a port written
6790
+ * before it keeps working; without it, a subscription already cancelled
6791
+ * for a later date cannot be ended at once.
6792
+ */
6793
+ endNow?(tenantId: string, input: EndNowInput): Promise<EndSubscriptionNowResult>;
5133
6794
  /**
5134
6795
  * Atomic onboarding creation: sets plan + cycle + period window
5135
6796
  * AND optionally calls a promo-redeem callback — all in a
@@ -5336,7 +6997,9 @@ interface AdminUserListRow {
5336
6997
  }
5337
6998
  interface AdminSubscriptionListRow {
5338
6999
  id: string;
7000
+ /** The tenant by its id too: what the operator's lists ask a subscriber's standing by. */
5339
7001
  tenant: {
7002
+ id: string;
5340
7003
  slug: string;
5341
7004
  name: string;
5342
7005
  };
@@ -6221,11 +7884,26 @@ interface SubscriptionNoticeRepository {
6221
7884
  * Throw where it could not be sent and should be tried again: the claim is
6222
7885
  * released and the next run sends it. Resolving with no recipients records that
6223
7886
  * nobody could be told, and the notice is not tried again — except a
6224
- * retirement's notice (`version-retired`), which nobody was told of: it is tried
6225
- * again by every run until somebody is, and its date counts from then.
7887
+ * retirement's notice (`version-retired`, `bundle-version-retired`), which
7888
+ * nobody was told of: it is tried again by every run until somebody is, and its
7889
+ * date counts from then.
7890
+ */
7891
+ interface SubscriptionNoticePort {
7892
+ deliver(notice: SubscriptionNotice): Promise<SubscriptionNoticeDelivery>;
7893
+ }
7894
+
7895
+ /**
7896
+ * Keeps every add-on retirement announcement: which version, which
7897
+ * replacement, when and by whom. What it means for each booking it reaches is
7898
+ * kept beside it as a notice of kind `bundle-version-retired`, written in the
7899
+ * same transaction.
6226
7900
  */
6227
- interface SubscriptionNoticePort {
6228
- deliver(notice: SubscriptionNotice): Promise<SubscriptionNoticeDelivery>;
7901
+ interface BundleVersionRetirementRepository {
7902
+ /** Records an announcement, in the caller's transaction where one is given. */
7903
+ create(data: NewBundleVersionRetirement, tx?: TransactionContext): Promise<BundleVersionRetirementRecord>;
7904
+ /** Every announcement, the most recent first. */
7905
+ list(): Promise<BundleVersionRetirementRecord[]>;
7906
+ findById(id: string): Promise<BundleVersionRetirementRecord | null>;
6229
7907
  }
6230
7908
 
6231
7909
  /**
@@ -6241,6 +7919,45 @@ interface VersionRetirementRepository {
6241
7919
  findById(id: string): Promise<VersionRetirementRecord | null>;
6242
7920
  }
6243
7921
 
7922
+ /**
7923
+ * Keeps every feature withdrawal: which feature, why, from when, the
7924
+ * reductions the operator named, when it was lifted, and when the
7925
+ * subscriptions it reaches were told. What it means for each of them is kept
7926
+ * beside it as a notice of kind `feature-withdrawn`, written in the
7927
+ * transaction that marks it told.
7928
+ *
7929
+ * An adapter translates. It does not decide whether a feature may be
7930
+ * withdrawn; it holds a feature to one withdrawal not yet lifted, so that two
7931
+ * operators announcing at once cannot both succeed, and makes lifting and
7932
+ * marking it told conditional on neither having happened yet.
7933
+ */
7934
+ interface FeatureWithdrawalRepository {
7935
+ /**
7936
+ * Records a withdrawal, in the caller's transaction where one is given, and
7937
+ * returns it as stored — the id is the adapter's to assign. Returns null
7938
+ * where a withdrawal of the same feature is recorded and not lifted: the
7939
+ * database holds a feature to one, so of two concurrent calls exactly one
7940
+ * lands.
7941
+ */
7942
+ create(data: NewFeatureWithdrawal, tx?: TransactionContext): Promise<FeatureWithdrawalRecord | null>;
7943
+ /** Every withdrawal, the most recently announced first. */
7944
+ list(): Promise<FeatureWithdrawalRecord[]>;
7945
+ findById(id: string): Promise<FeatureWithdrawalRecord | null>;
7946
+ /**
7947
+ * Records that the withdrawal `id` is lifted, provided it is not lifted
7948
+ * yet, and returns it as stored; null where it was lifted meanwhile or no
7949
+ * withdrawal has that id.
7950
+ */
7951
+ lift(id: string, lift: FeatureWithdrawalLift, tx?: TransactionContext): Promise<FeatureWithdrawalRecord | null>;
7952
+ /**
7953
+ * Records that the subscriptions the withdrawal `id` reaches were told at
7954
+ * `at`, provided nobody recorded it yet, in the caller's transaction where
7955
+ * one is given. Returns whether this call recorded it: of an announcement
7956
+ * and a run telling the same withdrawal at once, exactly one does.
7957
+ */
7958
+ markTold(id: string, at: Date, tx?: TransactionContext): Promise<boolean>;
7959
+ }
7960
+
6244
7961
  /** Class reference usable as a DI token (e.g. the consumer's `PrismaService`). */
6245
7962
  type PersistenceClassRef = abstract new (...args: never[]) => unknown;
6246
7963
  /** DI token forms a persistence bundle may reference in `inject`. */
@@ -6328,6 +8045,10 @@ interface SaaSiCatPersistenceEntitlement {
6328
8045
  subscriberRepository?: PersistenceProvider<SubscriberRepository>;
6329
8046
  /** The charges a subscriber's contracts give rise to (`tenantBilling.chargeJournal`). */
6330
8047
  subscriberLedgerRepository?: PersistenceProvider<SubscriberLedgerRepository>;
8048
+ /** The invoices issued from those charges (`tenantBilling.chargeJournal.invoices`). */
8049
+ subscriptionInvoiceRepository?: PersistenceProvider<SubscriptionInvoiceRepository>;
8050
+ /** The invoices' documents, kept in the database (`chargeJournal.invoices.archive`). */
8051
+ subscriptionInvoiceArchive?: PersistenceProvider<InvoiceArchivePort>;
6331
8052
  subscriptionBundleRepository?: PersistenceProvider<SubscriptionBundleRepository>;
6332
8053
  bundleRepository?: PersistenceProvider<BundleRepository>;
6333
8054
  }
@@ -6365,6 +8086,17 @@ interface SaaSiCatPersistenceTenantBilling {
6365
8086
  * retire versions without one is refused at start-up.
6366
8087
  */
6367
8088
  versionRetirements?: PersistenceProvider<VersionRetirementRepository>;
8089
+ /**
8090
+ * The record of every add-on retirement announcement. Optional so an
8091
+ * adapter written before it keeps working; without it, retiring an add-on
8092
+ * version is off, and retiring a plan version is not affected.
8093
+ */
8094
+ bundleVersionRetirements?: PersistenceProvider<BundleVersionRetirementRepository>;
8095
+ /**
8096
+ * The record of every feature withdrawal. Optional so an adapter written
8097
+ * before it keeps working; without it, withdrawing a feature is off.
8098
+ */
8099
+ featureWithdrawals?: PersistenceProvider<FeatureWithdrawalRepository>;
6368
8100
  }
6369
8101
  /**
6370
8102
  * The record of payment gateway callbacks and the payment methods they confirm
@@ -6531,6 +8263,13 @@ declare const CATALOG_ERROR_CODES: {
6531
8263
  readonly PLAN_NOT_FOUND: "PLAN_NOT_FOUND";
6532
8264
  readonly PLAN_VERSION_NOT_FOUND: "PLAN_VERSION_NOT_FOUND";
6533
8265
  readonly BUNDLE_NOT_FOUND: "BUNDLE_NOT_FOUND";
8266
+ /**
8267
+ * Bookings told that their add-on version is being retired have still to
8268
+ * move onto a version of this add-on — waiting for their notice, for their
8269
+ * date, or past it — and a deleted add-on is booked by nothing, the move
8270
+ * included. Carries how many, and the add-on's key.
8271
+ */
8272
+ readonly BUNDLE_DELETE_WHILE_RETIREMENT_MOVES_PENDING: "BUNDLE_DELETE_WHILE_RETIREMENT_MOVES_PENDING";
6534
8273
  readonly BUNDLE_VERSION_NOT_FOUND: "BUNDLE_VERSION_NOT_FOUND";
6535
8274
  readonly FEATURE_NOT_FOUND: "FEATURE_NOT_FOUND";
6536
8275
  readonly QUOTA_NOT_FOUND: "QUOTA_NOT_FOUND";
@@ -6718,6 +8457,16 @@ declare const BILLING_ERROR_CODES: {
6718
8457
  * to pick, so the way past it is the add-on ending.
6719
8458
  */
6720
8459
  readonly RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW: "RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW";
8460
+ /**
8461
+ * As `RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW`, where only the version the
8462
+ * add-on continues on from a retirement's date cannot run beside the
8463
+ * replacement's plan. The version it is on can, so cancelling it — it then
8464
+ * ends before that date — lets the switch through. Given while that date
8465
+ * is ahead and the add-on is not cancelled yet; otherwise the earlier code
8466
+ * names the day it can end. Carries the add-on, that version, its date and
8467
+ * the plan.
8468
+ */
8469
+ readonly RETIREMENT_SWITCH_BUNDLE_REPLACEMENT_CANNOT_FOLLOW: "RETIREMENT_SWITCH_BUNDLE_REPLACEMENT_CANNOT_FOLLOW";
6721
8470
  /**
6722
8471
  * The subscription takes no switch now: a change is scheduled, it has
6723
8472
  * ended, or its plan is held for a special contract.
@@ -6728,6 +8477,142 @@ declare const BILLING_ERROR_CODES: {
6728
8477
  * the retirement as it now stands.
6729
8478
  */
6730
8479
  readonly RETIREMENT_SWITCH_CHANGED: "RETIREMENT_SWITCH_CHANGED";
8480
+ /**
8481
+ * The add-on version is still on sale: a booking made after the
8482
+ * announcement would be on it without having been told. Carries the
8483
+ * add-on key and the version.
8484
+ */
8485
+ readonly BUNDLE_RETIREMENT_VERSION_ON_SALE: "BUNDLE_RETIREMENT_VERSION_ON_SALE";
8486
+ /** The replacement is not on sale, so no booking can continue on it. Carries add-on key and version. */
8487
+ readonly BUNDLE_RETIREMENT_REPLACEMENT_NOT_ON_SALE: "BUNDLE_RETIREMENT_REPLACEMENT_NOT_ON_SALE";
8488
+ /**
8489
+ * The replacement is a version of another add-on. A booking continues on
8490
+ * a version of its own add-on, so it stays the same booking with its term.
8491
+ * Carries both add-on keys.
8492
+ */
8493
+ readonly BUNDLE_RETIREMENT_REPLACEMENT_OF_ANOTHER_BUNDLE: "BUNDLE_RETIREMENT_REPLACEMENT_OF_ANOTHER_BUNDLE";
8494
+ /**
8495
+ * Some of the bookings it reaches run, at their date, beside a plan the
8496
+ * replacement cannot run beside: not allowed there, without a price there
8497
+ * in the booking's rhythm, or in a longer rhythm than the plan's. Carries
8498
+ * how many, and the replacement's add-on key and version.
8499
+ */
8500
+ readonly BUNDLE_RETIREMENT_REPLACEMENT_CANNOT_RUN: "BUNDLE_RETIREMENT_REPLACEMENT_CANNOT_RUN";
8501
+ /** No running booking is on the version: there is nobody to tell. Carries add-on key and version. */
8502
+ readonly BUNDLE_RETIREMENT_NOTHING_AFFECTED: "BUNDLE_RETIREMENT_NOTHING_AFFECTED";
8503
+ /**
8504
+ * The booking is on an add-on version being retired, and was cancelled to
8505
+ * end before it would move, so it was never told and nothing moves it.
8506
+ * Reinstated, it would run on past the date on a version nobody sells;
8507
+ * booking the replacement once it has ended is the way. Carries the add-on
8508
+ * key, the version, the replacement's version and the day it can be booked
8509
+ * from.
8510
+ */
8511
+ readonly BUNDLE_RETIREMENT_REINSTATE_REFUSED: "BUNDLE_RETIREMENT_REINSTATE_REFUSED";
8512
+ /**
8513
+ * As `BUNDLE_RETIREMENT_REINSTATE_REFUSED`, where the replacement cannot
8514
+ * run beside the plan the subscription is on when the booking ends, or
8515
+ * beside one it is already set to move to after that, in any rhythm it
8516
+ * could be booked in, so there is no version of the add-on to book.
8517
+ * Carries the add-on key, the version and the replacement's version.
8518
+ */
8519
+ readonly BUNDLE_RETIREMENT_REINSTATE_REPLACEMENT_CANNOT_RUN: "BUNDLE_RETIREMENT_REINSTATE_REPLACEMENT_CANNOT_RUN";
8520
+ /**
8521
+ * As `BUNDLE_RETIREMENT_REINSTATE_REFUSED`, where the subscription ends by
8522
+ * the day the replacement could be booked from, so there is no day to
8523
+ * name. Carries the add-on key, the version and the replacement's version.
8524
+ */
8525
+ readonly BUNDLE_RETIREMENT_REINSTATE_SUBSCRIPTION_ENDS: "BUNDLE_RETIREMENT_REINSTATE_SUBSCRIPTION_ENDS";
8526
+ /**
8527
+ * The plan the add-on runs beside, or the rhythm it is billed in, changes
8528
+ * before the date the booking was told — by a change scheduled, or by a
8529
+ * retirement told that moves the subscription to another plan — so the
8530
+ * price a switch now would hold is not the one the booking pays until
8531
+ * then. Switching once the plan has changed, or waiting for the
8532
+ * date, is the way. Carries the add-on and the date.
8533
+ */
8534
+ readonly BUNDLE_RETIREMENT_SWITCH_PLAN_CHANGES: "BUNDLE_RETIREMENT_SWITCH_PLAN_CHANGES";
8535
+ /**
8536
+ * The add-on version the caller asked to switch a booking to is no longer
8537
+ * the one offered beside it — another is on sale, the booking moved, or
8538
+ * there is no offer. Nothing was switched. Carries the version named
8539
+ * (`bundleVersionId`) and the current `offer`, or `null`, so the page can
8540
+ * show it instead of guessing.
8541
+ */
8542
+ readonly BUNDLE_VERSION_OFFER_CHANGED: "BUNDLE_VERSION_OFFER_CHANGED";
8543
+ /**
8544
+ * Withdrawing a feature is not offered: the installation's stores cannot
8545
+ * list everybody a withdrawal would reach.
8546
+ */
8547
+ readonly FEATURE_WITHDRAWAL_UNAVAILABLE: "FEATURE_WITHDRAWAL_UNAVAILABLE";
8548
+ /** The catalogue knows no feature of that key, so there is nothing to withdraw. Carries `featureKey`. */
8549
+ readonly FEATURE_WITHDRAWAL_FEATURE_UNKNOWN: "FEATURE_WITHDRAWAL_FEATURE_UNKNOWN";
8550
+ /**
8551
+ * The date names a moment before now. A feature is withdrawn from the
8552
+ * moment it is announced or from a later date: the days before were
8553
+ * delivered.
8554
+ */
8555
+ readonly FEATURE_WITHDRAWAL_DATE_IN_PAST: "FEATURE_WITHDRAWAL_DATE_IN_PAST";
8556
+ /** A withdrawal of the feature is not lifted yet; one at a time. Carries `featureKey`. */
8557
+ readonly FEATURE_WITHDRAWAL_OPEN: "FEATURE_WITHDRAWAL_OPEN";
8558
+ /**
8559
+ * A withdrawal of the feature is lifted only from a date after the one
8560
+ * named, so the two would overlap. Carries `featureKey` and the `date` a
8561
+ * new one may begin on.
8562
+ */
8563
+ readonly FEATURE_WITHDRAWAL_OVERLAPS: "FEATURE_WITHDRAWAL_OVERLAPS";
8564
+ /**
8565
+ * A reduction names a plan or an add-on, in a rhythm, that no line the
8566
+ * withdrawal reaches is. Carries `kind`, `key` and `billingCycle`.
8567
+ */
8568
+ readonly FEATURE_WITHDRAWAL_REDUCTION_NOT_REACHED: "FEATURE_WITHDRAWAL_REDUCTION_NOT_REACHED";
8569
+ /** A reduction is named twice. Carries `kind`, `key` and `billingCycle`. */
8570
+ readonly FEATURE_WITHDRAWAL_REDUCTION_NAMED_TWICE: "FEATURE_WITHDRAWAL_REDUCTION_NAMED_TWICE";
8571
+ /**
8572
+ * A reduction is more than the lowest price among the lines it reduces:
8573
+ * a price is reduced to nothing at most. Carries `key`, `billingCycle` and
8574
+ * that `price`.
8575
+ */
8576
+ readonly FEATURE_WITHDRAWAL_REDUCTION_EXCEEDS_PRICE: "FEATURE_WITHDRAWAL_REDUCTION_EXCEEDS_PRICE";
8577
+ /**
8578
+ * The subscriptions the withdrawal reaches changed since the preview the
8579
+ * operator looked at. Nothing was announced; carries the current `preview`.
8580
+ */
8581
+ readonly FEATURE_WITHDRAWAL_PREVIEW_CHANGED: "FEATURE_WITHDRAWAL_PREVIEW_CHANGED";
8582
+ /** No withdrawal has that id. Carries `withdrawalId`. */
8583
+ readonly FEATURE_WITHDRAWAL_NOT_FOUND: "FEATURE_WITHDRAWAL_NOT_FOUND";
8584
+ /** The withdrawal was lifted already. Carries the `date` it is lifted from. */
8585
+ readonly FEATURE_WITHDRAWAL_ALREADY_LIFTED: "FEATURE_WITHDRAWAL_ALREADY_LIFTED";
8586
+ /**
8587
+ * Nobody the withdrawal reaches has been told of it yet, and telling them
8588
+ * failed just now: a withdrawal is lifted only once they were. Nothing was
8589
+ * lifted; the next try, or the quarter-hourly run, tells them first.
8590
+ */
8591
+ readonly FEATURE_WITHDRAWAL_NOT_TOLD_YET: "FEATURE_WITHDRAWAL_NOT_TOLD_YET";
8592
+ /**
8593
+ * The date a withdrawal would be lifted from lies before now: what was
8594
+ * withdrawn until then was withdrawn.
8595
+ */
8596
+ readonly FEATURE_WITHDRAWAL_LIFT_IN_PAST: "FEATURE_WITHDRAWAL_LIFT_IN_PAST";
8597
+ /**
8598
+ * The withdrawal does not take its feature away now — its date is ahead,
8599
+ * or it was lifted — so nothing ends at once because of it. Carries
8600
+ * `featureKey`.
8601
+ */
8602
+ readonly FEATURE_WITHDRAWAL_NOT_IN_EFFECT: "FEATURE_WITHDRAWAL_NOT_IN_EFFECT";
8603
+ /**
8604
+ * The withdrawal did not reach the subscription, or the line to end: it
8605
+ * was not running at the announcement, or no longer holds the feature.
8606
+ */
8607
+ readonly FEATURE_WITHDRAWAL_DOES_NOT_REACH: "FEATURE_WITHDRAWAL_DOES_NOT_REACH";
8608
+ /** What was to be ended at once has ended already. */
8609
+ readonly FEATURE_WITHDRAWAL_ALREADY_ENDED: "FEATURE_WITHDRAWAL_ALREADY_ENDED";
8610
+ /**
8611
+ * A cancellation is declared for a later date, and the installation's
8612
+ * subscription write or booking store cannot bring it forward (`endNow`).
8613
+ * It ends as declared. Carries that `date`.
8614
+ */
8615
+ readonly FEATURE_WITHDRAWAL_END_NOW_UNSUPPORTED: "FEATURE_WITHDRAWAL_END_NOW_UNSUPPORTED";
6731
8616
  /** The subscription has ended; its plan can no longer be changed. */
6732
8617
  readonly SUBSCRIPTION_ENDED: "SUBSCRIPTION_ENDED";
6733
8618
  /** An active special contract blocks self-service plan changes. */
@@ -6762,6 +8647,16 @@ declare const BILLING_ERROR_CODES: {
6762
8647
  * day the booking can end.
6763
8648
  */
6764
8649
  readonly BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN: "BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN";
8650
+ /**
8651
+ * A bundle the tenant already holds can run on the plan they are moving
8652
+ * to, but the version it continues on from a retirement's date cannot.
8653
+ * Cancelling it lets the change through at once: the booking then ends
8654
+ * before that date and never reaches the version. Given while that date is
8655
+ * ahead and the booking is not cancelled yet; otherwise
8656
+ * `BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN` names the day it can end.
8657
+ * Carries the bundle, that version, its date and the plan.
8658
+ */
8659
+ readonly BUNDLE_REPLACEMENT_DOES_NOT_FIT_TARGET_PLAN: "BUNDLE_REPLACEMENT_DOES_NOT_FIT_TARGET_PLAN";
6765
8660
  /**
6766
8661
  * Features of the previewed bundle are already covered by the plan or by
6767
8662
  * another booked bundle. A warning rather than a blocker: paying twice is
@@ -6831,6 +8726,21 @@ declare const CONTRACT_ERROR_CODES: {
6831
8726
  * describes. Nothing was written; load the offer and conclude it again.
6832
8727
  */
6833
8728
  readonly CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED";
8729
+ /**
8730
+ * An offer concludes a tenant's first contract, and this tenant has a
8731
+ * contract in force at the moment the offer's would take effect, or one
8732
+ * beginning after it. Concluded, the two would run side by side. Nothing
8733
+ * was written; a running subscription changes through its plan and its
8734
+ * add-ons. Carries the offer and the contract that runs.
8735
+ */
8736
+ readonly CHECKOUT_OFFER_CONTRACT_IN_FORCE: "CHECKOUT_OFFER_CONTRACT_IN_FORCE";
8737
+ /**
8738
+ * The offer names a version of an add-on the tenant has booked in another
8739
+ * version. Concluded, the contract would name a version the booking is not
8740
+ * on. Nothing was written; a booking changes its version through the offer
8741
+ * beside it. Carries the offer, the add-on, the booking and both versions.
8742
+ */
8743
+ readonly CHECKOUT_OFFER_ADD_ON_BOOKED_IN_ANOTHER_VERSION: "CHECKOUT_OFFER_ADD_ON_BOOKED_IN_ANOTHER_VERSION";
6834
8744
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
6835
8745
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
6836
8746
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
@@ -6842,8 +8752,64 @@ declare const CONTRACT_ERROR_CODES: {
6842
8752
  * would take effect. Nothing was written.
6843
8753
  */
6844
8754
  readonly SUBSCRIPTION_CONTRACT_CHANGED: "SUBSCRIPTION_CONTRACT_CHANGED";
8755
+ /**
8756
+ * A contract states a tax rate — `field` names where — other than the one
8757
+ * the installation's tax adapter decides for its subscriber.
8758
+ */
8759
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_DECIDED: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_DECIDED";
6845
8760
  };
6846
8761
  type ContractErrorCode = (typeof CONTRACT_ERROR_CODES)[keyof typeof CONTRACT_ERROR_CODES];
8762
+ /** Invoices issued from the charge journal (`SubscriptionInvoiceService`). */
8763
+ declare const INVOICE_ERROR_CODES: {
8764
+ /**
8765
+ * A charge already stands on an invoice: a second run issuing the same
8766
+ * charges lost the race. Nothing is written and no number is drawn.
8767
+ */
8768
+ readonly SUBSCRIPTION_INVOICE_CHARGE_INVOICED: "SUBSCRIPTION_INVOICE_CHARGE_INVOICED";
8769
+ /**
8770
+ * The invoice lacks content the tax adapter requires of it — `missing`
8771
+ * names the fields, `adapter` the adapter. It draws no number and waits
8772
+ * until what is missing is supplied (`SC-PRIC-027`).
8773
+ */
8774
+ readonly SUBSCRIPTION_INVOICE_CONTENT_INCOMPLETE: "SUBSCRIPTION_INVOICE_CONTENT_INCOMPLETE";
8775
+ /**
8776
+ * The contract's parties were copied by a migration and the operator has
8777
+ * not confirmed them yet; its charges are not invoiced until then
8778
+ * (`SC-AUD-012`).
8779
+ */
8780
+ readonly SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED: "SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED";
8781
+ /**
8782
+ * A file of the invoice's document has another checksum than the one
8783
+ * recorded or archived for it (`invoiceId`, `recorded`, `rendered`), and
8784
+ * the first stays. The run takes the archived file as the one issued, or —
8785
+ * where nothing is archived — records the new rendering in place of the
8786
+ * old (`SC-AUD-019`); the invoice waits only where another run changed the
8787
+ * record or the archive in between, and the next run settles it.
8788
+ */
8789
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED: "SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED";
8790
+ /**
8791
+ * The tax adapter refuses the rendered document — `missing` names what it
8792
+ * lacks or disagrees with, `adapter` the adapter. The invoice keeps its
8793
+ * number and waits for a document that passes (`SC-PRIC-042`).
8794
+ */
8795
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_REFUSED: "SUBSCRIPTION_INVOICE_DOCUMENT_REFUSED";
8796
+ /**
8797
+ * The archived document does not match the checksum recorded when it was
8798
+ * rendered (`invoiceId`). It is not handed out (`SC-AUD-019`).
8799
+ */
8800
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_MISMATCH: "SUBSCRIPTION_INVOICE_DOCUMENT_MISMATCH";
8801
+ /** The invoice's document is not archived yet (`invoiceId`); nothing is offered until it is (`SC-AUD-014`). */
8802
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_NOT_ARCHIVED: "SUBSCRIPTION_INVOICE_DOCUMENT_NOT_ARCHIVED";
8803
+ /** No invoice with this id (`invoiceId`). */
8804
+ readonly SUBSCRIPTION_INVOICE_NOT_FOUND: "SUBSCRIPTION_INVOICE_NOT_FOUND";
8805
+ /**
8806
+ * The subscriber's record holds no invoice email (`subscriberId`), so the
8807
+ * archived invoice cannot be sent (`SC-PRIC-047`); it is sent once one is
8808
+ * entered.
8809
+ */
8810
+ readonly SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL: "SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL";
8811
+ };
8812
+ type InvoiceErrorCode = (typeof INVOICE_ERROR_CODES)[keyof typeof INVOICE_ERROR_CODES];
6847
8813
  /** The parties contracts are concluded with (`SubscriberService`), and their absence. */
6848
8814
  declare const SUBSCRIBER_ERROR_CODES: {
6849
8815
  /**
@@ -6868,6 +8834,29 @@ declare const SUBSCRIBER_ERROR_CODES: {
6868
8834
  readonly SUBSCRIBER_CORRECTION_CHANGES_NOTHING: "SUBSCRIBER_CORRECTION_CHANGES_NOTHING";
6869
8835
  /** The operator declared another legal entity: that is a transfer, not an edit. */
6870
8836
  readonly SUBSCRIBER_TAKEOVER_IS_A_TRANSFER: "SUBSCRIBER_TAKEOVER_IS_A_TRANSFER";
8837
+ /**
8838
+ * A contact change named whether the subscriber is a business. That is part
8839
+ * of its tax origin and changes as a recorded change naming who made it.
8840
+ */
8841
+ readonly SUBSCRIBER_BUSINESS_STATUS_NOT_A_CONTACT: "SUBSCRIBER_BUSINESS_STATUS_NOT_A_CONTACT";
8842
+ /** A change of the subscriber's contact details or business status did not say who makes it. */
8843
+ readonly SUBSCRIBER_CHANGE_ACTOR_REQUIRED: "SUBSCRIBER_CHANGE_ACTOR_REQUIRED";
8844
+ /**
8845
+ * Where a tax adapter decides, a contract names its subscriber only once
8846
+ * the address an invoice names is complete (`SC-PRIC-032`): a party copied
8847
+ * onto a contract incomplete cannot be completed afterwards. Carries
8848
+ * `missing`, the empty fields, for a screen to name by their labels; the
8849
+ * shipped sentence names all four rather than the keys.
8850
+ */
8851
+ readonly SUBSCRIBER_IDENTITY_INCOMPLETE: "SUBSCRIBER_IDENTITY_INCOMPLETE";
8852
+ /**
8853
+ * A change of whether the subscriber is a business states no reason. It
8854
+ * decides the tax treatment of the next invoice, so the change log keeps
8855
+ * why it moved (`SC-PRIC-043`).
8856
+ */
8857
+ readonly SUBSCRIBER_BUSINESS_STATUS_REASON_REQUIRED: "SUBSCRIBER_BUSINESS_STATUS_REASON_REQUIRED";
8858
+ /** A check of the subscriber's VAT identification number was asked for, and it holds none. */
8859
+ readonly SUBSCRIBER_VAT_ID_MISSING: "SUBSCRIBER_VAT_ID_MISSING";
6871
8860
  };
6872
8861
  type SubscriberErrorCode = (typeof SUBSCRIBER_ERROR_CODES)[keyof typeof SUBSCRIBER_ERROR_CODES];
6873
8862
  /** Self-service registration funnel (`PendingRegistration`). */
@@ -6985,6 +8974,31 @@ declare const MAINTENANCE_ERROR_CODES: {
6985
8974
  readonly MAINTENANCE_MESSAGE_TOO_LONG: "MAINTENANCE_MESSAGE_TOO_LONG";
6986
8975
  };
6987
8976
  type MaintenanceErrorCode = (typeof MAINTENANCE_ERROR_CODES)[keyof typeof MAINTENANCE_ERROR_CODES];
8977
+ /** Codes of the installation's tax adapter (ADR 0013). */
8978
+ declare const TAX_ERROR_CODES: {
8979
+ /**
8980
+ * The tax adapter answers that it does not support the subscriber's case;
8981
+ * `adapter` names it and `reason` is its sentence. Nothing is concluded with
8982
+ * a guessed tax (`SC-PRIC-039`).
8983
+ */
8984
+ readonly TAX_TREATMENT_NOT_SUPPORTED: "TAX_TREATMENT_NOT_SUPPORTED";
8985
+ /**
8986
+ * The tax adapter needs the VAT identification number checked to treat the
8987
+ * case, and the check did not complete — the service is unavailable, timed
8988
+ * out, or answered in a way the adapter cannot read (`503`). Nothing is
8989
+ * decided in the subscriber's favour on a number not checked
8990
+ * (`SC-PRIC-040`): try again later. `adapter` names the adapter and
8991
+ * `reason` says why.
8992
+ */
8993
+ readonly TAX_VAT_ID_CHECK_NOT_COMPLETED: "TAX_VAT_ID_CHECK_NOT_COMPLETED";
8994
+ /**
8995
+ * A VAT identification number was to be checked, and `config/saas.yaml`
8996
+ * names no tax adapter, which is what names the service to check with
8997
+ * (`409`).
8998
+ */
8999
+ readonly TAX_VAT_ID_CHECK_NOT_AVAILABLE: "TAX_VAT_ID_CHECK_NOT_AVAILABLE";
9000
+ };
9001
+ type TaxErrorCode = (typeof TAX_ERROR_CODES)[keyof typeof TAX_ERROR_CODES];
6988
9002
  /**
6989
9003
  * Every exception code the platform emits, in one object.
6990
9004
  *
@@ -6993,6 +9007,27 @@ type MaintenanceErrorCode = (typeof MAINTENANCE_ERROR_CODES)[keyof typeof MAINTE
6993
9007
  * removing one may not.
6994
9008
  */
6995
9009
  declare const PLATFORM_ERROR_CODES: {
9010
+ /**
9011
+ * The tax adapter answers that it does not support the subscriber's case;
9012
+ * `adapter` names it and `reason` is its sentence. Nothing is concluded with
9013
+ * a guessed tax (`SC-PRIC-039`).
9014
+ */
9015
+ readonly TAX_TREATMENT_NOT_SUPPORTED: "TAX_TREATMENT_NOT_SUPPORTED";
9016
+ /**
9017
+ * The tax adapter needs the VAT identification number checked to treat the
9018
+ * case, and the check did not complete — the service is unavailable, timed
9019
+ * out, or answered in a way the adapter cannot read (`503`). Nothing is
9020
+ * decided in the subscriber's favour on a number not checked
9021
+ * (`SC-PRIC-040`): try again later. `adapter` names the adapter and
9022
+ * `reason` says why.
9023
+ */
9024
+ readonly TAX_VAT_ID_CHECK_NOT_COMPLETED: "TAX_VAT_ID_CHECK_NOT_COMPLETED";
9025
+ /**
9026
+ * A VAT identification number was to be checked, and `config/saas.yaml`
9027
+ * names no tax adapter, which is what names the service to check with
9028
+ * (`409`).
9029
+ */
9030
+ readonly TAX_VAT_ID_CHECK_NOT_AVAILABLE: "TAX_VAT_ID_CHECK_NOT_AVAILABLE";
6996
9031
  /** The application is locked for maintenance; the body carries the window. */
6997
9032
  readonly MAINTENANCE: "MAINTENANCE";
6998
9033
  /** A window is already open: move or cancel it before announcing another. */
@@ -7069,6 +9104,76 @@ declare const PLATFORM_ERROR_CODES: {
7069
9104
  readonly SUBSCRIBER_CORRECTION_CHANGES_NOTHING: "SUBSCRIBER_CORRECTION_CHANGES_NOTHING";
7070
9105
  /** The operator declared another legal entity: that is a transfer, not an edit. */
7071
9106
  readonly SUBSCRIBER_TAKEOVER_IS_A_TRANSFER: "SUBSCRIBER_TAKEOVER_IS_A_TRANSFER";
9107
+ /**
9108
+ * A contact change named whether the subscriber is a business. That is part
9109
+ * of its tax origin and changes as a recorded change naming who made it.
9110
+ */
9111
+ readonly SUBSCRIBER_BUSINESS_STATUS_NOT_A_CONTACT: "SUBSCRIBER_BUSINESS_STATUS_NOT_A_CONTACT";
9112
+ /** A change of the subscriber's contact details or business status did not say who makes it. */
9113
+ readonly SUBSCRIBER_CHANGE_ACTOR_REQUIRED: "SUBSCRIBER_CHANGE_ACTOR_REQUIRED";
9114
+ /**
9115
+ * Where a tax adapter decides, a contract names its subscriber only once
9116
+ * the address an invoice names is complete (`SC-PRIC-032`): a party copied
9117
+ * onto a contract incomplete cannot be completed afterwards. Carries
9118
+ * `missing`, the empty fields, for a screen to name by their labels; the
9119
+ * shipped sentence names all four rather than the keys.
9120
+ */
9121
+ readonly SUBSCRIBER_IDENTITY_INCOMPLETE: "SUBSCRIBER_IDENTITY_INCOMPLETE";
9122
+ /**
9123
+ * A change of whether the subscriber is a business states no reason. It
9124
+ * decides the tax treatment of the next invoice, so the change log keeps
9125
+ * why it moved (`SC-PRIC-043`).
9126
+ */
9127
+ readonly SUBSCRIBER_BUSINESS_STATUS_REASON_REQUIRED: "SUBSCRIBER_BUSINESS_STATUS_REASON_REQUIRED";
9128
+ /** A check of the subscriber's VAT identification number was asked for, and it holds none. */
9129
+ readonly SUBSCRIBER_VAT_ID_MISSING: "SUBSCRIBER_VAT_ID_MISSING";
9130
+ /**
9131
+ * A charge already stands on an invoice: a second run issuing the same
9132
+ * charges lost the race. Nothing is written and no number is drawn.
9133
+ */
9134
+ readonly SUBSCRIPTION_INVOICE_CHARGE_INVOICED: "SUBSCRIPTION_INVOICE_CHARGE_INVOICED";
9135
+ /**
9136
+ * The invoice lacks content the tax adapter requires of it — `missing`
9137
+ * names the fields, `adapter` the adapter. It draws no number and waits
9138
+ * until what is missing is supplied (`SC-PRIC-027`).
9139
+ */
9140
+ readonly SUBSCRIPTION_INVOICE_CONTENT_INCOMPLETE: "SUBSCRIPTION_INVOICE_CONTENT_INCOMPLETE";
9141
+ /**
9142
+ * The contract's parties were copied by a migration and the operator has
9143
+ * not confirmed them yet; its charges are not invoiced until then
9144
+ * (`SC-AUD-012`).
9145
+ */
9146
+ readonly SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED: "SUBSCRIPTION_INVOICE_PARTIES_UNCONFIRMED";
9147
+ /**
9148
+ * A file of the invoice's document has another checksum than the one
9149
+ * recorded or archived for it (`invoiceId`, `recorded`, `rendered`), and
9150
+ * the first stays. The run takes the archived file as the one issued, or —
9151
+ * where nothing is archived — records the new rendering in place of the
9152
+ * old (`SC-AUD-019`); the invoice waits only where another run changed the
9153
+ * record or the archive in between, and the next run settles it.
9154
+ */
9155
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED: "SUBSCRIPTION_INVOICE_DOCUMENT_CHANGED";
9156
+ /**
9157
+ * The tax adapter refuses the rendered document — `missing` names what it
9158
+ * lacks or disagrees with, `adapter` the adapter. The invoice keeps its
9159
+ * number and waits for a document that passes (`SC-PRIC-042`).
9160
+ */
9161
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_REFUSED: "SUBSCRIPTION_INVOICE_DOCUMENT_REFUSED";
9162
+ /**
9163
+ * The archived document does not match the checksum recorded when it was
9164
+ * rendered (`invoiceId`). It is not handed out (`SC-AUD-019`).
9165
+ */
9166
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_MISMATCH: "SUBSCRIPTION_INVOICE_DOCUMENT_MISMATCH";
9167
+ /** The invoice's document is not archived yet (`invoiceId`); nothing is offered until it is (`SC-AUD-014`). */
9168
+ readonly SUBSCRIPTION_INVOICE_DOCUMENT_NOT_ARCHIVED: "SUBSCRIPTION_INVOICE_DOCUMENT_NOT_ARCHIVED";
9169
+ /** No invoice with this id (`invoiceId`). */
9170
+ readonly SUBSCRIPTION_INVOICE_NOT_FOUND: "SUBSCRIPTION_INVOICE_NOT_FOUND";
9171
+ /**
9172
+ * The subscriber's record holds no invoice email (`subscriberId`), so the
9173
+ * archived invoice cannot be sent (`SC-PRIC-047`); it is sent once one is
9174
+ * entered.
9175
+ */
9176
+ readonly SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL: "SUBSCRIPTION_INVOICE_NO_INVOICE_EMAIL";
7072
9177
  readonly CHECKOUT_OFFER_LINE_ITEMS_REQUIRED: "CHECKOUT_OFFER_LINE_ITEMS_REQUIRED";
7073
9178
  readonly CHECKOUT_OFFER_PLAN_LINE_ITEM_REQUIRED: "CHECKOUT_OFFER_PLAN_LINE_ITEM_REQUIRED";
7074
9179
  readonly CHECKOUT_OFFER_BUNDLE_LINE_ITEMS_REQUIRED: "CHECKOUT_OFFER_BUNDLE_LINE_ITEMS_REQUIRED";
@@ -7103,6 +9208,21 @@ declare const PLATFORM_ERROR_CODES: {
7103
9208
  * describes. Nothing was written; load the offer and conclude it again.
7104
9209
  */
7105
9210
  readonly CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED";
9211
+ /**
9212
+ * An offer concludes a tenant's first contract, and this tenant has a
9213
+ * contract in force at the moment the offer's would take effect, or one
9214
+ * beginning after it. Concluded, the two would run side by side. Nothing
9215
+ * was written; a running subscription changes through its plan and its
9216
+ * add-ons. Carries the offer and the contract that runs.
9217
+ */
9218
+ readonly CHECKOUT_OFFER_CONTRACT_IN_FORCE: "CHECKOUT_OFFER_CONTRACT_IN_FORCE";
9219
+ /**
9220
+ * The offer names a version of an add-on the tenant has booked in another
9221
+ * version. Concluded, the contract would name a version the booking is not
9222
+ * on. Nothing was written; a booking changes its version through the offer
9223
+ * beside it. Carries the offer, the add-on, the booking and both versions.
9224
+ */
9225
+ readonly CHECKOUT_OFFER_ADD_ON_BOOKED_IN_ANOTHER_VERSION: "CHECKOUT_OFFER_ADD_ON_BOOKED_IN_ANOTHER_VERSION";
7106
9226
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
7107
9227
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
7108
9228
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
@@ -7114,6 +9234,11 @@ declare const PLATFORM_ERROR_CODES: {
7114
9234
  * would take effect. Nothing was written.
7115
9235
  */
7116
9236
  readonly SUBSCRIPTION_CONTRACT_CHANGED: "SUBSCRIPTION_CONTRACT_CHANGED";
9237
+ /**
9238
+ * A contract states a tax rate — `field` names where — other than the one
9239
+ * the installation's tax adapter decides for its subscriber.
9240
+ */
9241
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_DECIDED: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_DECIDED";
7117
9242
  /**
7118
9243
  * Feature not covered by the plan. Defined in `upsell.types.ts` because the
7119
9244
  * upsell body type is built around it; re-exported here so an exhaustive
@@ -7273,6 +9398,16 @@ declare const PLATFORM_ERROR_CODES: {
7273
9398
  * to pick, so the way past it is the add-on ending.
7274
9399
  */
7275
9400
  readonly RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW: "RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW";
9401
+ /**
9402
+ * As `RETIREMENT_SWITCH_BUNDLE_CANNOT_FOLLOW`, where only the version the
9403
+ * add-on continues on from a retirement's date cannot run beside the
9404
+ * replacement's plan. The version it is on can, so cancelling it — it then
9405
+ * ends before that date — lets the switch through. Given while that date
9406
+ * is ahead and the add-on is not cancelled yet; otherwise the earlier code
9407
+ * names the day it can end. Carries the add-on, that version, its date and
9408
+ * the plan.
9409
+ */
9410
+ readonly RETIREMENT_SWITCH_BUNDLE_REPLACEMENT_CANNOT_FOLLOW: "RETIREMENT_SWITCH_BUNDLE_REPLACEMENT_CANNOT_FOLLOW";
7276
9411
  /**
7277
9412
  * The subscription takes no switch now: a change is scheduled, it has
7278
9413
  * ended, or its plan is held for a special contract.
@@ -7283,6 +9418,142 @@ declare const PLATFORM_ERROR_CODES: {
7283
9418
  * the retirement as it now stands.
7284
9419
  */
7285
9420
  readonly RETIREMENT_SWITCH_CHANGED: "RETIREMENT_SWITCH_CHANGED";
9421
+ /**
9422
+ * The add-on version is still on sale: a booking made after the
9423
+ * announcement would be on it without having been told. Carries the
9424
+ * add-on key and the version.
9425
+ */
9426
+ readonly BUNDLE_RETIREMENT_VERSION_ON_SALE: "BUNDLE_RETIREMENT_VERSION_ON_SALE";
9427
+ /** The replacement is not on sale, so no booking can continue on it. Carries add-on key and version. */
9428
+ readonly BUNDLE_RETIREMENT_REPLACEMENT_NOT_ON_SALE: "BUNDLE_RETIREMENT_REPLACEMENT_NOT_ON_SALE";
9429
+ /**
9430
+ * The replacement is a version of another add-on. A booking continues on
9431
+ * a version of its own add-on, so it stays the same booking with its term.
9432
+ * Carries both add-on keys.
9433
+ */
9434
+ readonly BUNDLE_RETIREMENT_REPLACEMENT_OF_ANOTHER_BUNDLE: "BUNDLE_RETIREMENT_REPLACEMENT_OF_ANOTHER_BUNDLE";
9435
+ /**
9436
+ * Some of the bookings it reaches run, at their date, beside a plan the
9437
+ * replacement cannot run beside: not allowed there, without a price there
9438
+ * in the booking's rhythm, or in a longer rhythm than the plan's. Carries
9439
+ * how many, and the replacement's add-on key and version.
9440
+ */
9441
+ readonly BUNDLE_RETIREMENT_REPLACEMENT_CANNOT_RUN: "BUNDLE_RETIREMENT_REPLACEMENT_CANNOT_RUN";
9442
+ /** No running booking is on the version: there is nobody to tell. Carries add-on key and version. */
9443
+ readonly BUNDLE_RETIREMENT_NOTHING_AFFECTED: "BUNDLE_RETIREMENT_NOTHING_AFFECTED";
9444
+ /**
9445
+ * The booking is on an add-on version being retired, and was cancelled to
9446
+ * end before it would move, so it was never told and nothing moves it.
9447
+ * Reinstated, it would run on past the date on a version nobody sells;
9448
+ * booking the replacement once it has ended is the way. Carries the add-on
9449
+ * key, the version, the replacement's version and the day it can be booked
9450
+ * from.
9451
+ */
9452
+ readonly BUNDLE_RETIREMENT_REINSTATE_REFUSED: "BUNDLE_RETIREMENT_REINSTATE_REFUSED";
9453
+ /**
9454
+ * As `BUNDLE_RETIREMENT_REINSTATE_REFUSED`, where the replacement cannot
9455
+ * run beside the plan the subscription is on when the booking ends, or
9456
+ * beside one it is already set to move to after that, in any rhythm it
9457
+ * could be booked in, so there is no version of the add-on to book.
9458
+ * Carries the add-on key, the version and the replacement's version.
9459
+ */
9460
+ readonly BUNDLE_RETIREMENT_REINSTATE_REPLACEMENT_CANNOT_RUN: "BUNDLE_RETIREMENT_REINSTATE_REPLACEMENT_CANNOT_RUN";
9461
+ /**
9462
+ * As `BUNDLE_RETIREMENT_REINSTATE_REFUSED`, where the subscription ends by
9463
+ * the day the replacement could be booked from, so there is no day to
9464
+ * name. Carries the add-on key, the version and the replacement's version.
9465
+ */
9466
+ readonly BUNDLE_RETIREMENT_REINSTATE_SUBSCRIPTION_ENDS: "BUNDLE_RETIREMENT_REINSTATE_SUBSCRIPTION_ENDS";
9467
+ /**
9468
+ * The plan the add-on runs beside, or the rhythm it is billed in, changes
9469
+ * before the date the booking was told — by a change scheduled, or by a
9470
+ * retirement told that moves the subscription to another plan — so the
9471
+ * price a switch now would hold is not the one the booking pays until
9472
+ * then. Switching once the plan has changed, or waiting for the
9473
+ * date, is the way. Carries the add-on and the date.
9474
+ */
9475
+ readonly BUNDLE_RETIREMENT_SWITCH_PLAN_CHANGES: "BUNDLE_RETIREMENT_SWITCH_PLAN_CHANGES";
9476
+ /**
9477
+ * The add-on version the caller asked to switch a booking to is no longer
9478
+ * the one offered beside it — another is on sale, the booking moved, or
9479
+ * there is no offer. Nothing was switched. Carries the version named
9480
+ * (`bundleVersionId`) and the current `offer`, or `null`, so the page can
9481
+ * show it instead of guessing.
9482
+ */
9483
+ readonly BUNDLE_VERSION_OFFER_CHANGED: "BUNDLE_VERSION_OFFER_CHANGED";
9484
+ /**
9485
+ * Withdrawing a feature is not offered: the installation's stores cannot
9486
+ * list everybody a withdrawal would reach.
9487
+ */
9488
+ readonly FEATURE_WITHDRAWAL_UNAVAILABLE: "FEATURE_WITHDRAWAL_UNAVAILABLE";
9489
+ /** The catalogue knows no feature of that key, so there is nothing to withdraw. Carries `featureKey`. */
9490
+ readonly FEATURE_WITHDRAWAL_FEATURE_UNKNOWN: "FEATURE_WITHDRAWAL_FEATURE_UNKNOWN";
9491
+ /**
9492
+ * The date names a moment before now. A feature is withdrawn from the
9493
+ * moment it is announced or from a later date: the days before were
9494
+ * delivered.
9495
+ */
9496
+ readonly FEATURE_WITHDRAWAL_DATE_IN_PAST: "FEATURE_WITHDRAWAL_DATE_IN_PAST";
9497
+ /** A withdrawal of the feature is not lifted yet; one at a time. Carries `featureKey`. */
9498
+ readonly FEATURE_WITHDRAWAL_OPEN: "FEATURE_WITHDRAWAL_OPEN";
9499
+ /**
9500
+ * A withdrawal of the feature is lifted only from a date after the one
9501
+ * named, so the two would overlap. Carries `featureKey` and the `date` a
9502
+ * new one may begin on.
9503
+ */
9504
+ readonly FEATURE_WITHDRAWAL_OVERLAPS: "FEATURE_WITHDRAWAL_OVERLAPS";
9505
+ /**
9506
+ * A reduction names a plan or an add-on, in a rhythm, that no line the
9507
+ * withdrawal reaches is. Carries `kind`, `key` and `billingCycle`.
9508
+ */
9509
+ readonly FEATURE_WITHDRAWAL_REDUCTION_NOT_REACHED: "FEATURE_WITHDRAWAL_REDUCTION_NOT_REACHED";
9510
+ /** A reduction is named twice. Carries `kind`, `key` and `billingCycle`. */
9511
+ readonly FEATURE_WITHDRAWAL_REDUCTION_NAMED_TWICE: "FEATURE_WITHDRAWAL_REDUCTION_NAMED_TWICE";
9512
+ /**
9513
+ * A reduction is more than the lowest price among the lines it reduces:
9514
+ * a price is reduced to nothing at most. Carries `key`, `billingCycle` and
9515
+ * that `price`.
9516
+ */
9517
+ readonly FEATURE_WITHDRAWAL_REDUCTION_EXCEEDS_PRICE: "FEATURE_WITHDRAWAL_REDUCTION_EXCEEDS_PRICE";
9518
+ /**
9519
+ * The subscriptions the withdrawal reaches changed since the preview the
9520
+ * operator looked at. Nothing was announced; carries the current `preview`.
9521
+ */
9522
+ readonly FEATURE_WITHDRAWAL_PREVIEW_CHANGED: "FEATURE_WITHDRAWAL_PREVIEW_CHANGED";
9523
+ /** No withdrawal has that id. Carries `withdrawalId`. */
9524
+ readonly FEATURE_WITHDRAWAL_NOT_FOUND: "FEATURE_WITHDRAWAL_NOT_FOUND";
9525
+ /** The withdrawal was lifted already. Carries the `date` it is lifted from. */
9526
+ readonly FEATURE_WITHDRAWAL_ALREADY_LIFTED: "FEATURE_WITHDRAWAL_ALREADY_LIFTED";
9527
+ /**
9528
+ * Nobody the withdrawal reaches has been told of it yet, and telling them
9529
+ * failed just now: a withdrawal is lifted only once they were. Nothing was
9530
+ * lifted; the next try, or the quarter-hourly run, tells them first.
9531
+ */
9532
+ readonly FEATURE_WITHDRAWAL_NOT_TOLD_YET: "FEATURE_WITHDRAWAL_NOT_TOLD_YET";
9533
+ /**
9534
+ * The date a withdrawal would be lifted from lies before now: what was
9535
+ * withdrawn until then was withdrawn.
9536
+ */
9537
+ readonly FEATURE_WITHDRAWAL_LIFT_IN_PAST: "FEATURE_WITHDRAWAL_LIFT_IN_PAST";
9538
+ /**
9539
+ * The withdrawal does not take its feature away now — its date is ahead,
9540
+ * or it was lifted — so nothing ends at once because of it. Carries
9541
+ * `featureKey`.
9542
+ */
9543
+ readonly FEATURE_WITHDRAWAL_NOT_IN_EFFECT: "FEATURE_WITHDRAWAL_NOT_IN_EFFECT";
9544
+ /**
9545
+ * The withdrawal did not reach the subscription, or the line to end: it
9546
+ * was not running at the announcement, or no longer holds the feature.
9547
+ */
9548
+ readonly FEATURE_WITHDRAWAL_DOES_NOT_REACH: "FEATURE_WITHDRAWAL_DOES_NOT_REACH";
9549
+ /** What was to be ended at once has ended already. */
9550
+ readonly FEATURE_WITHDRAWAL_ALREADY_ENDED: "FEATURE_WITHDRAWAL_ALREADY_ENDED";
9551
+ /**
9552
+ * A cancellation is declared for a later date, and the installation's
9553
+ * subscription write or booking store cannot bring it forward (`endNow`).
9554
+ * It ends as declared. Carries that `date`.
9555
+ */
9556
+ readonly FEATURE_WITHDRAWAL_END_NOW_UNSUPPORTED: "FEATURE_WITHDRAWAL_END_NOW_UNSUPPORTED";
7286
9557
  /** The subscription has ended; its plan can no longer be changed. */
7287
9558
  readonly SUBSCRIPTION_ENDED: "SUBSCRIPTION_ENDED";
7288
9559
  /** An active special contract blocks self-service plan changes. */
@@ -7317,6 +9588,16 @@ declare const PLATFORM_ERROR_CODES: {
7317
9588
  * day the booking can end.
7318
9589
  */
7319
9590
  readonly BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN: "BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN";
9591
+ /**
9592
+ * A bundle the tenant already holds can run on the plan they are moving
9593
+ * to, but the version it continues on from a retirement's date cannot.
9594
+ * Cancelling it lets the change through at once: the booking then ends
9595
+ * before that date and never reaches the version. Given while that date is
9596
+ * ahead and the booking is not cancelled yet; otherwise
9597
+ * `BUNDLE_BOOKING_DOES_NOT_FIT_TARGET_PLAN` names the day it can end.
9598
+ * Carries the bundle, that version, its date and the plan.
9599
+ */
9600
+ readonly BUNDLE_REPLACEMENT_DOES_NOT_FIT_TARGET_PLAN: "BUNDLE_REPLACEMENT_DOES_NOT_FIT_TARGET_PLAN";
7320
9601
  /**
7321
9602
  * Features of the previewed bundle are already covered by the plan or by
7322
9603
  * another booked bundle. A warning rather than a blocker: paying twice is
@@ -7421,6 +9702,13 @@ declare const PLATFORM_ERROR_CODES: {
7421
9702
  readonly PLAN_NOT_FOUND: "PLAN_NOT_FOUND";
7422
9703
  readonly PLAN_VERSION_NOT_FOUND: "PLAN_VERSION_NOT_FOUND";
7423
9704
  readonly BUNDLE_NOT_FOUND: "BUNDLE_NOT_FOUND";
9705
+ /**
9706
+ * Bookings told that their add-on version is being retired have still to
9707
+ * move onto a version of this add-on — waiting for their notice, for their
9708
+ * date, or past it — and a deleted add-on is booked by nothing, the move
9709
+ * included. Carries how many, and the add-on's key.
9710
+ */
9711
+ readonly BUNDLE_DELETE_WHILE_RETIREMENT_MOVES_PENDING: "BUNDLE_DELETE_WHILE_RETIREMENT_MOVES_PENDING";
7424
9712
  readonly BUNDLE_VERSION_NOT_FOUND: "BUNDLE_VERSION_NOT_FOUND";
7425
9713
  readonly FEATURE_NOT_FOUND: "FEATURE_NOT_FOUND";
7426
9714
  readonly QUOTA_NOT_FOUND: "QUOTA_NOT_FOUND";
@@ -7493,7 +9781,7 @@ declare const PLATFORM_ERROR_CODES: {
7493
9781
  /** Email already taken (mapped from `PlatformUserExistsError`). */
7494
9782
  readonly EMAIL_EXISTS: "EMAIL_EXISTS";
7495
9783
  };
7496
- type PlatformErrorCode = SetupErrorCode | AuthErrorCode | PromoErrorCode | CatalogErrorCode | BillingErrorCode | ContractErrorCode | SubscriberErrorCode | RegistrationErrorCode | PaymentErrorCode | SettingsErrorCode | MaintenanceErrorCode;
9784
+ type PlatformErrorCode = SetupErrorCode | AuthErrorCode | PromoErrorCode | CatalogErrorCode | BillingErrorCode | ContractErrorCode | InvoiceErrorCode | SubscriberErrorCode | RegistrationErrorCode | PaymentErrorCode | SettingsErrorCode | MaintenanceErrorCode | TaxErrorCode;
7497
9785
  /**
7498
9786
  * Shape of a coded error response.
7499
9787
  *
@@ -7577,7 +9865,14 @@ declare const OPTIONAL_CANONICAL_MODELS: {
7577
9865
  readonly MaintenanceWindow: readonly ["core.maintenanceWindows"];
7578
9866
  readonly SubscriptionNotice: readonly ["tenantBilling.subscriptionNotices"];
7579
9867
  readonly VersionRetirement: readonly ["tenantBilling.versionRetirements"];
9868
+ readonly BundleVersionRetirement: readonly ["tenantBilling.bundleVersionRetirements"];
9869
+ readonly FeatureWithdrawal: readonly ["tenantBilling.featureWithdrawals"];
7580
9870
  readonly SubscriberLedgerEntry: readonly ["entitlement.subscriberLedgerRepository"];
9871
+ readonly SubscriptionInvoice: readonly ["entitlement.subscriptionInvoiceRepository"];
9872
+ readonly SubscriptionInvoiceLine: readonly ["entitlement.subscriptionInvoiceRepository"];
9873
+ readonly SubscriptionInvoiceNumber: readonly ["entitlement.subscriptionInvoiceRepository"];
9874
+ readonly SubscriptionInvoiceDocument: readonly ["entitlement.subscriptionInvoiceRepository"];
9875
+ readonly SubscriptionInvoiceArchive: readonly ["entitlement.subscriptionInvoiceArchive"];
7581
9876
  readonly PromoCodeHold: readonly ["promo.holdRepository"];
7582
9877
  };
7583
9878
  type OptionalCanonicalModel = keyof typeof OPTIONAL_CANONICAL_MODELS;
@@ -7881,6 +10176,14 @@ interface PendingRegistration {
7881
10176
  country: string | null;
7882
10177
  vatId: string | null;
7883
10178
  taxNumber: string | null;
10179
+ /** Whether the sign-up is a business, as step 4 says; `null` where it did not say. */
10180
+ business: boolean | null;
10181
+ /**
10182
+ * The check of `vatId` step 4 made, outside any transaction, where the tax
10183
+ * adapter needed a validated number to treat the sign-up; `null` where it
10184
+ * needed none. The subscriber takes it over when it is created.
10185
+ */
10186
+ vatIdCheck: VatIdCheck | null;
7884
10187
  /** The gateway's session for the payment method, unique within `checkoutGatewayAccount`. */
7885
10188
  checkoutSessionId: string | null;
7886
10189
  /** The account in `config/saas.yaml#payments.accounts` the session was opened at. */
@@ -7925,6 +10228,8 @@ interface PendingRegistrationUpdateInput {
7925
10228
  country?: string | null;
7926
10229
  vatId?: string | null;
7927
10230
  taxNumber?: string | null;
10231
+ business?: boolean | null;
10232
+ vatIdCheck?: VatIdCheck | null;
7928
10233
  checkoutSessionId?: string | null;
7929
10234
  checkoutGatewayAccount?: string | null;
7930
10235
  gatewayCustomerRef?: string | null;
@@ -8179,7 +10484,12 @@ interface ConfiguratorPlanMarketing {
8179
10484
  */
8180
10485
  interface ConfiguratorMarketingProvider {
8181
10486
  listPlanMarketing(): ConfiguratorPlanMarketing[];
8182
- getVatRate(): number;
10487
+ /**
10488
+ * The rate the configurator shows. Required where config/saas.yaml names no
10489
+ * tax adapter; left out where it names one, whose rate for a subscriber in
10490
+ * the issuer's country is shown instead.
10491
+ */
10492
+ getVatRate?(): number;
8183
10493
  getCurrency(): string;
8184
10494
  }
8185
10495
  /**
@@ -8360,6 +10670,13 @@ interface RegistrationBillingDetails {
8360
10670
  country: string;
8361
10671
  vatId?: string | null;
8362
10672
  taxNumber?: string | null;
10673
+ /**
10674
+ * Whether the sign-up is a business. A tax adapter decides from it — a
10675
+ * business elsewhere in the European Union, for instance, is charged under
10676
+ * the reverse charge — and refuses the step where it needs it and it is
10677
+ * missing.
10678
+ */
10679
+ business?: boolean | null;
8363
10680
  }
8364
10681
  interface StartCheckoutInput {
8365
10682
  pendingRegistrationId: string;
@@ -8561,6 +10878,10 @@ type IssuerIdentityChange =
8561
10878
  */
8562
10879
  declare function classifyIssuerChange(recorded: LegalIdentity | null, issuer: PlanCatalogIssuer | undefined): IssuerIdentityChange;
8563
10880
 
10881
+ /** Every origin a charge may name, as a record or a line that copies one is checked against. */
10882
+ declare const SUBSCRIBER_CHARGE_ORIGINS: readonly SubscriberChargeOrigin[];
10883
+ /** Every source a charge may name. */
10884
+ declare const SUBSCRIBER_CHARGE_SOURCES: readonly SubscriberChargeSource[];
8564
10885
  /**
8565
10886
  * A `subscriber_ledger_entries` row as either adapter reads it back. The amount
8566
10887
  * is `unknown` because a Prisma row carries a `Decimal` where a Drizzle row
@@ -8602,6 +10923,252 @@ declare function subscriberChargeColumns(charge: NewSubscriberCharge): Omit<Cano
8602
10923
  amountNet: string;
8603
10924
  };
8604
10925
 
10926
+ /**
10927
+ * A `subscription_invoices` row as either adapter reads it back. Amounts are
10928
+ * `unknown` because a Prisma row carries a `Decimal` where a Drizzle row carries
10929
+ * a numeric string, and days because one carries a `Date` at midnight UTC where
10930
+ * the other carries the text.
10931
+ */
10932
+ interface CanonicalSubscriptionInvoiceRow {
10933
+ id: string;
10934
+ number: string;
10935
+ numberPrefix: string;
10936
+ numberYear: number;
10937
+ numberSequence: number;
10938
+ tenantId: string;
10939
+ subscriberId: string;
10940
+ subscriptionId: string;
10941
+ contractId: string;
10942
+ issuedAt: Date;
10943
+ issueDate: unknown;
10944
+ dueDate: unknown;
10945
+ servicePeriodFrom: unknown;
10946
+ servicePeriodUntil: unknown;
10947
+ currency: string;
10948
+ issuerParty: unknown;
10949
+ subscriberParty: unknown;
10950
+ footer: unknown;
10951
+ taxTreatment: unknown;
10952
+ taxes: unknown;
10953
+ totalNet: unknown;
10954
+ totalTax: unknown;
10955
+ totalGross: unknown;
10956
+ createdAt: Date;
10957
+ }
10958
+ /** A `subscription_invoice_lines` row as either adapter reads it back. */
10959
+ interface CanonicalSubscriptionInvoiceLineRow {
10960
+ id: string;
10961
+ invoiceId: string;
10962
+ position: number;
10963
+ chargeId: string;
10964
+ contractLineItemId: string;
10965
+ title: string;
10966
+ origin: string;
10967
+ source: string;
10968
+ periodFrom: unknown;
10969
+ periodUntil: unknown;
10970
+ amountNet: unknown;
10971
+ taxRate: unknown;
10972
+ }
10973
+ /** Reads an invoice row and its lines back as a record, the lines in their order. */
10974
+ declare function toSubscriptionInvoiceRecord(row: CanonicalSubscriptionInvoiceRow, lines: readonly CanonicalSubscriptionInvoiceLineRow[]): SubscriptionInvoiceRecord;
10975
+ /**
10976
+ * The columns an invoice is written with: amounts as decimal strings of two
10977
+ * places, as a charge's are, and days as their text, which each adapter turns
10978
+ * into what its date column takes.
10979
+ */
10980
+ interface SubscriptionInvoiceColumns {
10981
+ number: string;
10982
+ numberPrefix: string;
10983
+ numberYear: number;
10984
+ numberSequence: number;
10985
+ tenantId: string;
10986
+ subscriberId: string;
10987
+ subscriptionId: string;
10988
+ contractId: string;
10989
+ issuedAt: Date;
10990
+ issueDate: CalendarDay;
10991
+ dueDate: CalendarDay;
10992
+ servicePeriodFrom: CalendarDay;
10993
+ servicePeriodUntil: CalendarDay;
10994
+ currency: string;
10995
+ issuerParty: SubscriptionInvoiceIssuer;
10996
+ subscriberParty: SubscriptionInvoiceSubscriber;
10997
+ footer: string[];
10998
+ taxTreatment: Record<string, unknown>;
10999
+ taxes: TaxAtRate[];
11000
+ totalNet: string;
11001
+ totalTax: string;
11002
+ totalGross: string;
11003
+ }
11004
+ /** The columns a line is written with, in the form `SubscriptionInvoiceColumns` describes. */
11005
+ interface SubscriptionInvoiceLineColumns {
11006
+ invoiceId: string;
11007
+ position: number;
11008
+ chargeId: string;
11009
+ contractLineItemId: string;
11010
+ title: string;
11011
+ origin: string;
11012
+ source: string;
11013
+ periodFrom: CalendarDay;
11014
+ periodUntil: CalendarDay;
11015
+ amountNet: string;
11016
+ taxRate: string;
11017
+ }
11018
+ /** The columns an invoice is written with, given the number the store drew. */
11019
+ declare function subscriptionInvoiceColumns(invoice: NewSubscriptionInvoice, drawn: {
11020
+ number: string;
11021
+ numberSequence: number;
11022
+ }): SubscriptionInvoiceColumns;
11023
+ /** The columns a line is written with. */
11024
+ declare function subscriptionInvoiceLineColumns(line: NewSubscriptionInvoiceLine, invoiceId: string): SubscriptionInvoiceLineColumns;
11025
+ /** A day as the `Date` at midnight UTC a Prisma `@db.Date` column takes. */
11026
+ declare function utcMidnightOf(day: CalendarDay): Date;
11027
+
11028
+ /**
11029
+ * A charge that already stands on an invoice: what the unique `chargeId` of
11030
+ * the invoice lines answers when two runs issue the same charges at once. The
11031
+ * invoice is not written and its number not drawn.
11032
+ */
11033
+ declare function subscriptionInvoiceChargeInvoiced(chargeId: string): PersistenceRefusal;
11034
+ /**
11035
+ * A rendering of an invoice's document whose checksum is not the one recorded
11036
+ * or archived for it, refused: the first stays. Only the run replaces a
11037
+ * recorded rendering, and only while nothing is archived (`SC-AUD-019`).
11038
+ */
11039
+ declare function subscriptionInvoiceDocumentChanged(invoiceId: string, recorded: string, rendered: string): PersistenceRefusal;
11040
+
11041
+ /** The charges one invoice issues: booked at the same moment under the same contract. */
11042
+ interface InvoiceGroup {
11043
+ contractId: string;
11044
+ bookedAt: Date;
11045
+ charges: SubscriberChargeRecord[];
11046
+ }
11047
+ /**
11048
+ * A subscription's charges left to invoice, as the invoices that issue them.
11049
+ *
11050
+ * The charges a billing period opens with are booked together and issued
11051
+ * together; a charge that arises later in the period is booked when it arises
11052
+ * and issued on an invoice of its own (`SC-PRIC-022`). A contract is the
11053
+ * agreement an invoice asks payment under, so charges of two contracts are
11054
+ * never on one invoice. The oldest come first, so numbers follow the charges.
11055
+ * A group whose charges are all zero issues no invoice (`SC-PRIC-048`) and is
11056
+ * left out; a charge of zero beside one that is not stays on its invoice.
11057
+ */
11058
+ declare function invoiceGroupsOf(charges: readonly SubscriberChargeRecord[]): InvoiceGroup[];
11059
+ /** What an invoice is drawn up from. */
11060
+ interface InvoiceDraftInput {
11061
+ group: InvoiceGroup;
11062
+ /** The contract the group's charges belong to: where each line takes its title and place. */
11063
+ contractLines: readonly Pick<ContractLineItemRecord, 'id' | 'titleSnapshot'>[];
11064
+ tenantId: string;
11065
+ subscriberId: string;
11066
+ subscriber: SubscriptionInvoiceSubscriber;
11067
+ issuer: SubscriptionInvoiceIssuer;
11068
+ /** The footer lines `config/saas.yaml` names now (`SC-PRIC-077`). */
11069
+ footer: readonly string[];
11070
+ /** The treatment the tax adapter decides for the subscriber now (`SC-PRIC-043`). */
11071
+ treatment: TaxTreatment;
11072
+ /** The adapter's rule for the tax of an invoice (`SC-PRIC-041`). */
11073
+ tax: (lines: readonly InvoiceTaxLine[]) => TaxPerRate;
11074
+ issuedAt: Date;
11075
+ /** The installation's time zone, which every day the invoice states is read in (`SC-PRIC-045`). */
11076
+ timeZone: string;
11077
+ numberPrefix: string;
11078
+ /** Days from the issue date to the due date (`SC-PRIC-046`). */
11079
+ paymentTermDays: number;
11080
+ }
11081
+ /**
11082
+ * The invoice `input.group` makes. Its lines follow the contract's lines, so
11083
+ * the invoice reads in the order the contract does, each naming its charge and
11084
+ * its contract line (`SC-AUD-013`) and taking the contract line's title. Every
11085
+ * line carries the treatment's rate, and the tax is the adapter's, computed
11086
+ * once per rate over the lines' net amounts.
11087
+ *
11088
+ * Throws where the charges contradict each other — a currency or a contract
11089
+ * line the group cannot have — which no invoice can be drawn up from.
11090
+ */
11091
+ declare function draftSubscriptionInvoice(input: InvoiceDraftInput): NewSubscriptionInvoice;
11092
+ /** What the tax adapter checks of an invoice before its number is drawn (`SC-PRIC-027`). */
11093
+ declare function invoiceContentDraftOf(invoice: NewSubscriptionInvoice): InvoiceContentDraft;
11094
+
11095
+ /** A recorded settings change, as far as the issuer's history needs it. */
11096
+ type IssuerHistoryStep = Pick<SettingsChangeRecord, 'previous' | 'current'>;
11097
+ /**
11098
+ * The issuer an invoice of a contract names.
11099
+ *
11100
+ * `copy` is the issuer the contract copied when it was concluded; `configured`
11101
+ * the issuer `config/saas.yaml` names on the issue date, as a contract would
11102
+ * copy it now (`contractPartiesOf`), or `null` where it names none; `history`
11103
+ * the recorded settings changes, the oldest first.
11104
+ */
11105
+ declare function invoiceIssuerOf(copy: ContractIssuerParty, configured: ContractIssuerParty | null, history: readonly IssuerHistoryStep[]): SubscriptionInvoiceIssuer;
11106
+ /**
11107
+ * The footer lines an invoice carries (`SC-PRIC-077`): those `config/saas.yaml`
11108
+ * names, where the invoice names the issuer the file names; none where it names
11109
+ * the contract's copy of another entity, whose register and account the lines
11110
+ * are not.
11111
+ */
11112
+ declare function invoiceFooterOf(issuer: SubscriptionInvoiceIssuer, configured: ContractIssuerParty | null, footer: readonly string[]): string[];
11113
+
11114
+ /** A `subscription_invoice_documents` row as either adapter reads it back. */
11115
+ interface CanonicalSubscriptionInvoiceDocumentRow {
11116
+ invoiceId: string;
11117
+ checksum: string;
11118
+ mediaType: string;
11119
+ fileName: string;
11120
+ size: unknown;
11121
+ renderedAt: Date;
11122
+ archivedAt: Date | null;
11123
+ deliveryClaimedAt: Date | null;
11124
+ deliveredAt: Date | null;
11125
+ deliveredTo: string | null;
11126
+ }
11127
+ /** A `subscription_invoice_archive` row as either adapter reads it back. */
11128
+ interface CanonicalSubscriptionInvoiceArchiveRow {
11129
+ invoiceId: string;
11130
+ checksum: string;
11131
+ mediaType: string;
11132
+ fileName: string;
11133
+ content: Uint8Array;
11134
+ archivedAt: Date;
11135
+ }
11136
+ /** Whether `value` is a checksum as the platform records one. */
11137
+ declare function isInvoiceDocumentChecksum(value: unknown): value is string;
11138
+ declare function toSubscriptionInvoiceDocumentRecord(row: CanonicalSubscriptionInvoiceDocumentRow): SubscriptionInvoiceDocumentRecord;
11139
+ declare function toArchivedInvoiceDocument(row: CanonicalSubscriptionInvoiceArchiveRow): ArchivedInvoiceDocument;
11140
+ /**
11141
+ * The triggers of `subscription_invoice_archive` that keep a document as it
11142
+ * was issued (`1.0-an-invoice-document-is-archived.postgres.sql`): one refuses
11143
+ * every update and delete of a row, the other every truncate of the table.
11144
+ */
11145
+ declare const INVOICE_ARCHIVE_TRIGGERS: readonly ["subscription_invoice_archive_is_kept", "subscription_invoice_archive_is_not_emptied"];
11146
+ /**
11147
+ * Why the database archive would let a document be changed, given the
11148
+ * triggers that fire on its table, or `null` where both of its own do. Shared
11149
+ * by both adapters, which read the enabled triggers from the catalogue.
11150
+ *
11151
+ * A database created from the fragments by `db push` alone, or a migration run
11152
+ * before the table existed, has none: the triggers are SQL the Prisma DSL
11153
+ * cannot express.
11154
+ */
11155
+ declare function invoiceArchiveProtectionProblem(enabledTriggers: readonly string[]): string | null;
11156
+
11157
+ /** The VAT category of EN 16931 (UNTDID 5305) a line and a breakdown name. */
11158
+ type VatCategory = 'S' | 'E' | 'AE' | 'O';
11159
+ /** The category EN 16931 names for a treatment an adapter decides. */
11160
+ declare function vatCategoryOf(kind: TaxTreatmentKind): VatCategory;
11161
+ /** Whether `text` can stand in an XML document as it is. */
11162
+ declare function holdsOnlyXmlCharacters(text: string): boolean;
11163
+ /**
11164
+ * `text` as an e-invoice's XML states it, which is what every XML parser reads
11165
+ * back from it: a character XML cannot hold becomes `?`, as the page shows a
11166
+ * character its font has no glyph for (`SC-PRIC-078`), and a carriage return
11167
+ * is the line feed XML 1.0 hands over in its place (§ 2.11). The renderer
11168
+ * writes this, and the adapter's check compares the record through it.
11169
+ */
11170
+ declare function asXmlReadsIt(text: string): string;
11171
+
8605
11172
  /** A `subscribers` row as either adapter reads it back. */
8606
11173
  interface CanonicalSubscriberRow {
8607
11174
  id: string;
@@ -8616,10 +11183,21 @@ interface CanonicalSubscriberRow {
8616
11183
  vatId: string | null;
8617
11184
  taxNumber: string | null;
8618
11185
  invoiceEmail: string | null;
11186
+ business: boolean | null;
8619
11187
  migrated: boolean;
8620
11188
  createdAt: Date;
8621
11189
  updatedAt: Date;
8622
11190
  }
11191
+ /** A `subscriber_tax_origin_changes` row as either adapter reads it back. */
11192
+ interface CanonicalSubscriberTaxOriginChangeRow {
11193
+ id: string;
11194
+ subscriberId: string;
11195
+ previous: unknown;
11196
+ changed: unknown;
11197
+ changedBy: string;
11198
+ changedAt: Date;
11199
+ reason: string | null;
11200
+ }
8623
11201
  /** A `subscriber_corrections` row as either adapter reads it back. */
8624
11202
  interface CanonicalSubscriberCorrectionRow {
8625
11203
  id: string;
@@ -8639,6 +11217,7 @@ declare function formatCustomerNumber(prefix: string, sequence: number): string;
8639
11217
  /** `tenantId` is the tenant the subscriber's live link names, read beside the row. */
8640
11218
  declare function toSubscriberRecord(row: CanonicalSubscriberRow, tenantId: string | null): SubscriberRecord;
8641
11219
  declare function toSubscriberCorrectionRecord(row: CanonicalSubscriberCorrectionRow): SubscriberCorrectionRecord;
11220
+ declare function toSubscriberTaxOriginChangeRecord(row: CanonicalSubscriberTaxOriginChangeRow): SubscriberTaxOriginChangeRecord;
8642
11221
  /**
8643
11222
  * The part of a correction that changes something: every corrected field whose
8644
11223
  * stored value differs, with the value it replaces. Both adapters decide this
@@ -8656,7 +11235,7 @@ declare function contractPartiesOf(subscriber: SubscriberRecord, issuer: PlanCat
8656
11235
  * verified with as its invoice email, and the billing address and tax
8657
11236
  * identifiers step 4 took.
8658
11237
  */
8659
- declare function subscriberFromRegistration(pending: Pick<PendingRegistration, 'tenantName' | 'email' | 'addressLine1' | 'addressLine2' | 'postalCode' | 'city' | 'country' | 'vatId' | 'taxNumber'>): NewSubscriberDetails;
11238
+ declare function subscriberFromRegistration(pending: Pick<PendingRegistration, 'tenantName' | 'email' | 'addressLine1' | 'addressLine2' | 'postalCode' | 'city' | 'country' | 'vatId' | 'taxNumber' | 'business' | 'vatIdCheck'>): NewSubscriberDetails;
8660
11239
 
8661
11240
  /** The payment method types, in the order a form offers them. */
8662
11241
  declare const PAYMENT_METHOD_TYPES: readonly PaymentMethodType[];
@@ -8733,9 +11312,10 @@ declare function refuseForeignPaymentMethodReference(recorded: Pick<CanonicalSub
8733
11312
  /**
8734
11313
  * A `subscription_bundles` row as either adapter reads it back.
8735
11314
  *
8736
- * The three billing columns are optional because a Prisma client generated from
8737
- * a schema that predates them returns a row without them; an absent column
8738
- * reads as null, the same as a booking made before they existed.
11315
+ * The three billing columns and the two of a scheduled switch are optional
11316
+ * because a Prisma client generated from a schema that predates them returns a
11317
+ * row without them; an absent column reads as null, the same as a booking made
11318
+ * before they existed.
8739
11319
  */
8740
11320
  interface CanonicalSubscriptionBundleRow {
8741
11321
  id: string;
@@ -8746,6 +11326,8 @@ interface CanonicalSubscriptionBundleRow {
8746
11326
  billingCycle?: string | null;
8747
11327
  currentPeriodStart?: Date | null;
8748
11328
  currentPeriodEnd?: Date | null;
11329
+ pendingBundleVersionId?: string | null;
11330
+ pendingVersionEffectiveAt?: Date | null;
8749
11331
  canceledAt: Date | null;
8750
11332
  canceledEffectiveAt: Date | null;
8751
11333
  createdAt: Date;
@@ -8769,6 +11351,8 @@ interface CanonicalContractRow {
8769
11351
  subscriberSnapshot: unknown;
8770
11352
  issuerSnapshot: unknown;
8771
11353
  partiesMigrated: boolean;
11354
+ /** JSON; see `toTaxTreatment`. */
11355
+ taxTreatment: unknown;
8772
11356
  status: string;
8773
11357
  effectiveFrom: Date;
8774
11358
  effectiveUntil: Date | null;
@@ -8823,6 +11407,100 @@ interface CanonicalRunningContractRow {
8823
11407
  /** One running contract, and the legal name on its issuer copy where it has one. */
8824
11408
  declare function toRunningContractIssuer(row: CanonicalRunningContractRow): RunningContractIssuer;
8825
11409
 
11410
+ /**
11411
+ * A VAT identification number in the one form it is stored, compared and
11412
+ * checked in: upper case, without the spaces, dots and hyphens it is often
11413
+ * written with. `atu 123.456-78` is `ATU12345678` — the same number, so
11414
+ * entering it in another spelling moves nothing. `null` where nothing is left.
11415
+ * The platform settles every number it is given with this, and a form that
11416
+ * asks whether a number changed compares with it too.
11417
+ */
11418
+ declare function canonicalVatId(written: string): string | null;
11419
+ /** A `subscriber_vat_id_checks` row as either adapter reads it back. */
11420
+ interface CanonicalSubscriberVatIdCheckRow {
11421
+ id: string;
11422
+ subscriberId: string;
11423
+ vatId: string;
11424
+ checkedAt: Date;
11425
+ valid: boolean;
11426
+ service: string;
11427
+ confirmation: unknown;
11428
+ recordedAt: Date;
11429
+ }
11430
+ /**
11431
+ * What an adapter decides a subscriber's treatment from: the record as it
11432
+ * stands, and the check that counts for its number now. The number counts as
11433
+ * validated only when that check is of the number the subscriber has and found
11434
+ * it valid.
11435
+ */
11436
+ declare function taxOriginOf(subscriber: Pick<SubscriberRecord, SubscriberTaxOriginField>, currentCheck: VatIdCheck | null): SubscriberTaxOrigin;
11437
+ /** The subscriber's number, and since when it holds it. */
11438
+ interface HeldVatId {
11439
+ vatId: string | null;
11440
+ /**
11441
+ * When the number was last set by a correction; `null` while it is the one
11442
+ * the subscriber was created with.
11443
+ */
11444
+ vatIdSince: Date | null;
11445
+ }
11446
+ /**
11447
+ * Whether a check just recorded is the one that counts from now on, given the
11448
+ * subscriber's number and the check that counts now. Only a check of the
11449
+ * number the subscriber holds, completed while it held it, and never over a
11450
+ * check that completed later:
11451
+ *
11452
+ * - A check completed before the number was last set says nothing about it:
11453
+ * the number was away in between, and may have been revoked meanwhile.
11454
+ * - Two checks of one number can run side by side, and the one written last
11455
+ * is not the latest — an older "valid" written after a newer "invalid" would
11456
+ * validate a number the latest check refused. Of two checks with the same
11457
+ * date, the one written last counts.
11458
+ *
11459
+ * `vatIdSince` and `checkedAt` are both the platform's clock; between two
11460
+ * instances, the window this leaves is the difference between their clocks.
11461
+ */
11462
+ declare function keepsVatIdCheck(held: HeldVatId, counting: VatIdCheck | null, check: VatIdCheck): boolean;
11463
+ /** A recorded check read back; a confirmation of another shape reads as empty. */
11464
+ declare function toSubscriberVatIdCheckRecord(row: CanonicalSubscriberVatIdCheckRow): SubscriberVatIdCheckRecord;
11465
+ /** A treatment as it is stored on a contract. */
11466
+ declare function taxTreatmentToJson(treatment: TaxTreatment): Record<string, unknown>;
11467
+ /** A stored treatment read back, or `null` where none is stored or it has another shape. */
11468
+ declare function toTaxTreatment(value: unknown): TaxTreatment | null;
11469
+ /** What a write moves of a subscriber's tax origin, and what follows from it. */
11470
+ interface TaxOriginWrite {
11471
+ /** The values the write replaces, holding only the fields it moves. */
11472
+ previous: SubscriberTaxOriginValues;
11473
+ /** The values it writes, holding only the fields it moves. */
11474
+ changed: SubscriberTaxOriginValues;
11475
+ /** Whether it moves anything, and so has a change to record. */
11476
+ moved: boolean;
11477
+ /**
11478
+ * Whether it moves the VAT identification number. The check that counted
11479
+ * for the old number then counts no more — it says nothing about the new
11480
+ * one, nor about the old one should it come back later — while the check
11481
+ * itself stays recorded; and the number held is held since this write
11482
+ * (`HeldVatId.vatIdSince`).
11483
+ */
11484
+ endsCountingVatIdCheck: boolean;
11485
+ }
11486
+ /**
11487
+ * What a write moves of the tax origin: every field named whose stored value
11488
+ * differs, with the value it replaces. Every adapter asks this under the lock
11489
+ * it read `current` with, and applies the answer: a change is recorded only
11490
+ * when it moved something.
11491
+ */
11492
+ declare function taxOriginWrite(current: Pick<SubscriberRecord, SubscriberTaxOriginField>, next: SubscriberTaxOriginValues): TaxOriginWrite;
11493
+ /** Stored tax origin values read back, holding only the fields that are there. */
11494
+ declare function toTaxOriginValues(value: unknown): SubscriberTaxOriginValues;
11495
+ /**
11496
+ * A check of a VAT identification number as a store gives it back. A sign-up
11497
+ * keeps its check where the application's repository puts it — often a JSON
11498
+ * column, which hands `checkedAt` back as text — so it is read again here,
11499
+ * once. Anything that is not a whole check reads as none: a number never
11500
+ * counts as validated on a record that cannot say so.
11501
+ */
11502
+ declare function vatIdCheckFromStore(value: unknown): VatIdCheck | null;
11503
+
8826
11504
  /** Why a version is editable (for UI badges + audit logs). */
8827
11505
  type VersionEditableReason = 'draft' | 'pre-active';
8828
11506
  interface VersionEditability {
@@ -8904,4 +11582,4 @@ declare function resolveErrorMessage(body: ResolvableErrorBody, overrides?: Part
8904
11582
 
8905
11583
  declare const ERROR_MESSAGES_DE: Record<PlatformErrorCode, string>;
8906
11584
 
8907
- export { ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES, AUTH_ERROR_CODES, type ActionKey, type ActivationOrchestrator, type ActivePlanVersionWhere, type ActivePlanVersionWhereWithEndsAt, type ActiveVersionWhere, type ActiveVersionWhereWithEndsAt, type ActorTag, type AdminAccountEntry, type AdminAccountHolder, type AdminActor, type AdminAuditListFilter, type AdminManifest, type AdminResourcesPort, type AdminSubscriberAccount, type AdminSubscriptionListRow, type AdminTenantDetail, type AdminTenantListFilter, type AdminTenantListRow, type AdminTenantStateResult, type AdminUserListFilter, type AdminUserListRow, type AppliedSettingsPort, type AppliedSettingsRecord, type AppliedSettingsValues, type ApplyOnboardingSelectionInput, type ApplyOnboardingSelectionResult, type ApprovedCatalogKeys, type AuditActionDef, type AuditActor, type AuditEntry, type AuditPort, type AuditQuery, type AuditQueryPort, type AuditStatsPort, type AuditStatsSnapshot, type AuthErrorCode, BILLING_ERROR_CODES, BUNDLE_PRICE_LOOKUP_LIMIT, type BillingCycle, type BillingErrorCode, type BundleAvailabilityState, type BundleCompatibility, type BundleFeatureShape, type BundleListFilter, type BundlePricingOverride, type BundleRepository, type BundleRow, type BundleVersionFields, type BundleVersionMutationResult, type BundleVersionRow, CATALOGUE_KEYS, CATALOG_ERROR_CODES, CONTRACT_ERROR_CODES, CUSTOM_LIMITS_KEYS, type CancelSubscriptionBundleData, type CancelSubscriptionInput, type CancelSubscriptionResult, type CancellationNoticePeriods, type CanonicalContractLineItemRow, type CanonicalContractRow, type CanonicalMaintenanceWindowRow, type CanonicalPlanRow, type CanonicalPlanVersionRow, type CanonicalRunningContractRow, type CanonicalSubscriberChargeRow, type CanonicalSubscriberCorrectionRow, type CanonicalSubscriberPaymentMethodRow, type CanonicalSubscriberRow, type CanonicalSubscriptionBundleRow, type CanonicalSubscriptionNoticeRow, type CanonicalVersionRetirementRow, type CapabilityCatalogEntryRow, type CapabilityCodeStatus, type CapabilityKey, type CapabilityKind, type CatalogEntryFilter, type CatalogEntryI18n, type CatalogEntryI18nFields, type CatalogEntryRepository, type CatalogErrorCode, type CatalogVersionKind, type ChangeDirection, type CheckoutOfferFilter, type CheckoutOfferLineItem, type CheckoutOfferLineItemKind, type CheckoutOfferPriceBreakdown, type CheckoutOfferPromoCodeSnapshot, type CheckoutOfferPromotionSnapshot, type CheckoutOfferRepository, type CheckoutOfferRow, type CheckoutOfferSelection, type CheckoutOfferSelectionUpdate, type CheckoutOfferStatus, type CleanupResult, type CliUserRow, type ComponentKey, type ConcurrencyGate, type ConfiguratorCatalog, type ConfiguratorMarketingProvider, type ConfiguratorModel, type ConfiguratorPlanMarketing, type ConfiguratorPlanVersionRow, type ConfiguratorPriceBreakdown, type ConfiguratorSourcesLookup, type ConfirmedPaymentMethod, type ContractErrorCode, type ContractIssuerParty, type ContractLineItemKind, type ContractLineItemRecord, type ContractSubscriberParty, type CreateBundleData, type CreateBundleVersionDraftData, type CreateCheckoutOfferData, type CreateMarketingProjectionData, type CreatePlanData, type CreatePlanVersionDraftData, type CreatePromoCodeData, type CreatePromoCodeRequest, type CreatePromotionData, type CreateSubscriberData, type CreateSubscriptionBundleData, type CreateSubscriptionContractData, type CreateSuperAdminCliInput, type CreateTenantInput, type CustomLimits, type DiffResult, type DiscoveredCapability, type DiscoveredFeature, type DiscoveredQuota, type DiscoveredQuotaPolicy, type DiscoveryCodeStatus, type DiscoverySnapshot, type DiscoveryStatus, ERROR_MESSAGES_DE, ERROR_MESSAGES_EN, type EffectiveLimitsSnapshot, type EmailPort, type ErrorMessageParams, FEATURE_NOT_LICENSED, type FeatureCatalogEntryRow, type FeatureDef, type FeatureKey, type FeatureNotLicensedBody, type FeatureRequiresIndex, type FeatureTier, type FeatureUiMeta, type FeatureUiRegistry, type FinalActivationResult, type FirstTimeCustomerCheck, ForeignPaymentMethodReferenceError, type ImmediatePlanChangeInput, type InvoiceLineItemSnapshot, type IssuerCorrectionFault, type IssuerIdentityChange, type KpiCardDef, type KpiDisplayHint, LEGAL_IDENTITY_FIELDS, type LegalIdentity, type LegalIdentityField, MAINTENANCE_CAPABILITY, MAINTENANCE_ERROR_CODES, MAINTENANCE_MESSAGE_MAX_LENGTH, MAINTENANCE_STATE_MAX_AGE_SECONDS, MARKETING_PRIORITY_MAX, MARKETING_PRIORITY_MIN, type MaintenanceErrorCode, type MaintenanceLockView, type MaintenanceNotificationPort, type MaintenanceOverview, type MaintenanceStatusView, type MaintenanceUnlockView, type MaintenanceWindowChanges, type MaintenanceWindowEvent, type MaintenanceWindowPort, type MaintenanceWindowRecord, type MaintenanceWindowStage, type MaintenanceWindowStatus, type MaintenanceWindowView, type ManifestAccessPort, type ManifestContribution, type MarketingProjectionFilter, type MarketingProjectionRepository, type MarketingProjectionRow, type MarketingSettingsRepository, type MarketingSettingsRow, type MarketingTargetType, type MarketingTopFeature, type MaskedPaymentMethod, type MfaPort, NO_SCHEDULED_CHANGE, type NewContractLineItemData, type NewMaintenanceWindow, type NewSettingsChange, type NewSubscriberCharge, type NewSubscriberDetails, type NewSubscriptionContractData, type NewVersionRetirement, type NoticeToRecord, OPTIONAL_CANONICAL_MODELS, OTP_RATE_LIMIT_MAX_SENDS, OTP_RATE_LIMIT_WINDOW_MINUTES, OTP_TTL_MINUTES, OTP_VERIFY_MAX_ATTEMPTS, type OnboardingPromoRedemption, type OnboardingSelectionRequest, type OnboardingSelectionResponse, type OptionalBundleMember, type OptionalCanonicalModel, type OrderlyRetirementSettings, PASSWORD_RESET_TTL_MINUTES, PAYMENT_ERROR_CODES, PAYMENT_METHOD_TYPES, PENDING_CHECKOUT_TTL_DAYS, PENDING_EMAIL_TTL_HOURS, PENDING_ONBOARDING_TTL_DAYS, PLATFORM_ERROR_CODES, PROMO_ERROR_CODES, type Paginated, type PartyAddress, type PasswordHasher, type PasswordResetCliResult, PaymentCallbackRejectedError, type PaymentErrorCode, type PaymentEventClaim, type PaymentEventLog, type PaymentGateway, type PaymentGatewayCallback, type PaymentGatewayEvent, type PaymentMethodHolder, type PaymentMethodSetupSession, type PaymentMethodSetupSubject, type PaymentMethodType, type PendingRegistration, type PendingRegistrationCreateInput, type PendingRegistrationRepository, type PendingRegistrationSnapshot, type PendingRegistrationUpdateInput, type PersistenceCapabilities, PersistenceCapabilityError, type PersistenceClassRef, type PersistenceInjectionToken, type PersistenceProvider, PersistenceRefusal, type PersistenceRefusalReason, type PlanCatalog, type PlanCatalogApp, type PlanCatalogImportReport, type PlanCatalogImportSink, type PlanCatalogIssuer, type PlanCatalogIssuerCorrection, type PlanCatalogLookup, type PlanCatalogMarketing, type PlanCatalogNotifications, type PlanCatalogPaymentAccount, type PlanCatalogPayments, type PlanCatalogReadSink, type PlanCatalogReadSnapshot, type PlanCatalogSettings, type PlanCatalogSubscribers, type PlanCatalogTenantBilling, type PlanDef, type PlanId, type PlanListFilter, type PlanRepository, type PlanRow, type PlanVersion, type PlanVersionFields, type PlanVersionMutationResult, type PlanVersionRecord, type PlanVersionRepository, type PlanVersionRow, type PlatformErrorBody, type PlatformErrorCode, type PlatformJobActor, type PlatformRole, type PlatformUserDto, PlatformUserExistsError, type ProjectPageDef, type PromoCode, type PromoCodeDurationType, type PromoCodeFilter, type PromoCodeHoldRecord, type PromoCodeHoldRepository, type PromoCodeHoldTaken, type PromoCodeRecord, type PromoCodeRedemption, type PromoCodeRedemptionListItem, type PromoCodeRedemptionRecord, type PromoCodeRedemptionRepository, type PromoCodeRedemptionStatus, type PromoCodeRepository, type PromoCodeStatsPort, type PromoCodeStatsSnapshot, type PromoCodeStatus, type PromoCodeValidationLog, type PromoCodeValidationLogRepository, type PromoCodeValidationResult, type PromoCodeValueType, type PromoErrorCode, type PromoPreviewInvalidReason, type PromoPreviewRequest, type PromoPreviewResponse, type PromoPreviewValidResponse, type PromoRevenueDeductionAggregator, type PromoSubscriptionLookup, type PromotionBillingCycle, type PromotionI18n, type PromotionI18nFields, type PromotionOnPrice, type PromotionRepository, type PromotionResult, type PromotionRow, type PromotionStatus, type PromotionTargetType, type PromotionType, type PromotionValue, type PublicBootResponse, type PublicComparisonRow, type PublicMarketingBundle, type PublicMarketingCatalogResponse, type PublicMarketingPlan, type PublicMarketingPromo, type PublicNewPaymentMethods, type PublicSignupPlan, type PublishBundleVersionData, type PublishBundleVersionMeta, type PublishPlanVersionData, type QuotaCatalogEntryRow, type QuotaEnforcementMode, type QuotaKey, type QuotaProvider, REGISTRATION_ERROR_CODES, REGISTRATION_RESUME_TTL_MINUTES, REGISTRATION_STEP_BY_STATUS, RETIREMENT_REMINDER_LEAD_DAYS, type ReadCustomLimits, type ReassignTenantAdminCliResult, type RecommendablePlan, type RecordSubscriberPaymentMethodData, type RecordSubscriberPaymentMethodOutcome, type RecordSubscriberPaymentMethodResult, type RedeemPromoInTransactionCallback, type RegistrationActivation, type RegistrationAuditContext, type RegistrationAuditEvent, type RegistrationAuditEventType, type RegistrationAuditLogger, type RegistrationBillingDetails, type RegistrationConfigSelection, type RegistrationConfiguratorLookup, type RegistrationErrorCode, type RegistrationOtpDelivery, type RegistrationPromoPreview, type RegistrationResumeDelivery, type RegistrationResumeTokenSigner, type RegistrationStatus, type RegistrationStep, type RequiredCapabilities, type ResolvableErrorBody, type ResumeRegistrationInput, type ResumeRegistrationResult, type RetiredVersionRef, type RetirementAnnounced, type RetirementBlocker, type RetirementMoveRun, type RetirementPreview, type RetirementProgress, type RetirementReachedRow, type RetirementSide, type RetirementSkipReason, type RetirementSkippedRow, type RetirementSwitchResult, type RetirementSwitchTerms, type ReviewCatalogEntryData, type RlsBypassPort, type RunningContractIssuer, type RunningContractIssuers, SETTINGS_ERROR_CODES, SETUP_ERROR_CODES, SUBSCRIBER_ACCOUNT_CAPABILITY, SUBSCRIBER_ERROR_CODES, type SaaSiCatPersistenceAdapter, type SaaSiCatPersistenceAdminResources, type SaaSiCatPersistenceCatalog, type SaaSiCatPersistenceCore, type SaaSiCatPersistenceEntitlement, type SaaSiCatPersistencePayments, type SaaSiCatPersistencePromo, type SaaSiCatPersistenceTenantBilling, type SaveRegistrationConfigInput, type SaveRegistrationConfigResult, type ScheduledChangeColumns, type ScheduledPlanChangeInput, type SecretSealer, type SelectPlanInput, type SelectPlanResult, type SelectableBundleShape, type SelfServiceBlockedPlans, type SetCatalogEntryReviewData, type SettingsChangeFilter, type SettingsChangeRecord, type SettingsDifference, type SettingsErrorCode, type SetupConfirmMfaRequest, type SetupConfirmMfaResponse, type SetupErrorCode, type SetupRequest, type SetupResult, type SetupStatusResponse, type SlugAvailabilityCheck, type StandardPageDef, type StandardPageKey, type StartCheckoutInput, type StartCheckoutResult, type StartPaymentMethodSetupInput, type StartRegistrationInput, type StartRegistrationResult, type StoredBundleStem, type StrictModeWarning, type StrictModeWarningCode, type SubscriberCharge, type SubscriberChargeOrigin, type SubscriberChargeRecord, type SubscriberChargeSource, type SubscriberContact, type SubscriberContactChange, type SubscriberCorrectionData, type SubscriberCorrectionRecord, type SubscriberCorrectionResult, type SubscriberDetails, type SubscriberErrorCode, type SubscriberIdentityCorrection, type SubscriberIdentityDelta, type SubscriberIdentityValues, type SubscriberLedgerRepository, type SubscriberPaymentMethodRecord, type SubscriberPaymentMethodReference, type SubscriberPaymentMethodRepository, type SubscriberPaymentMethodSetupData, type SubscriberPaymentMethodSetupMatch, type SubscriberPaymentMethodStatus, type SubscriberRecord, type SubscriberRepository, type Subscription, type SubscriptionBundleRecord, type SubscriptionBundleRepository, type SubscriptionBundleView, type SubscriptionContractFilter, type SubscriptionContractInvoiceSnapshot, type SubscriptionContractParties, type SubscriptionContractPriceSnapshot, type SubscriptionContractRecord, type SubscriptionContractRepository, type SubscriptionContractStatus, type SubscriptionNotice, type SubscriptionNoticeDelivery, type SubscriptionNoticeKey, type SubscriptionNoticeKind, type SubscriptionNoticePort, type SubscriptionNoticeRecord, type SubscriptionNoticeRepository, type SubscriptionRecord, type SubscriptionRepository, type SubscriptionStatsPort, type SubscriptionStatsSnapshot, type SubscriptionStatus, type SubscriptionUsagePort, type SubscriptionUsageRecord, type SuperAdminProvisioningPort, type SupersedeSubscriptionContractData, type SyncDiscoveryResult, type TenantActionDef, type TenantColumnDef, type TenantDto, type TenantListFilter, type TenantPort, type TenantSubscriptionUsage, type TenantSubscriptionWritePort, type TerminateSubscriptionContractData, type TopPromoCode, type TransactionContext, type TransactionRunner, type UpdateBundleData, type UpdateBundleVersionDraftData, type UpdateCatalogEntryBaseData, type UpdateCatalogEntryI18nData, type UpdateCheckoutOfferData, type UpdateMarketingProjectionData, type UpdateMarketingSettingsData, type UpdatePlanData, type UpdatePlanVersionDraftData, type UpdatePromoCodeData, type UpdatePromoCodeRequest, type UpdatePromotionData, type UpsellOffer, type UpsellOfferResolver, type UpsertCapabilityEntryData, type UpsertFeatureCatalogEntryInput, type UpsertFeatureEntryData, type UpsertPlanInput, type UpsertPlanVersionInput, type UpsertQuotaEntryData, type UpsertResult, type UsageSnapshotPort, type UserAccountLookup, type UserListFilter, type UserManagementPort, type UserPort, VERSION_RETIREMENT_CAPABILITY, type VerifyRegistrationOtpResult, type VersionChange, type VersionChangeDirection, type VersionEditability, type VersionEditableReason, type VersionOffer, type VersionOfferClass, type VersionOfferFields, type VersionOfferSide, type VersionOfferView, type VersionOfferedNotice, type VersionRetiredNotice, type VersionRetirementRecord, type VersionRetirementReminder, type VersionRetirementRepository, type VersionRetirementView, type VersionSale, type VersionSaleDates, type VersionSaleKind, type VersionSwitchResult, type VersionWindow, type VersionedEntityBase, applyPromo, assertPersistenceCapabilities, buildActivePlanVersionWhere, buildActiveVersionWhere, buildFeatureRequiresIndex, bundleDraftDefaults, bundleKeyTaken, bundleStemDefaults, canonicalJson, catalogDraftExists, catalogVersionAlreadyPublished, catalogVersionGone, classifyBundleVersionDiff, classifyIssuerChange, classifyPlanDiff, classifyVersionOffer, collectUnsatisfiedRequires, computeIncludedVat, concurrencyGate, concurrencyGateOf, contractPartiesOf, coverageExcludingSelf, customLimitsReader, definedFields, diffSettings, formatCustomerNumber, formatErrorMessage, grossFromNet, identityCorrectionDelta, isBundleRedundant, isForeignPaymentMethodReferenceError, isMaintenanceAnnouncementLapsed, isMaintenanceOverrun, isPaymentCallbackRejectedError, isPersistenceRefusal, isPlatformUserExistsError, isVersionActiveAt, isVersionEditable, issuerIdentityOf, keepOneRecommended, maintenanceRetryAfterSeconds, maintenanceStatusOf, maintenanceWindowStatusOf, maintenanceWindowViewOf, marketingProjectionTaken, membersLeftOut, missingRequiresFor, movedIdentityFields, netFromGross, noActivePlanVersion, parseZonedInstant, percentOf, pickActivePromo, planCatalogSettingsOf, planKeyTaken, planNotInCatalog, previousUtcDay, promoCodeTaken, promoStatus, promotionOnPrice, prorate, readCustomLimits, readQuotaRecord, readQuotaValue, recordedIssuerIdentity, refuseForeignPaymentMethodReference, resolveBundleAvailability, resolveErrorMessage, retirementCostsTheSubscription, retirementReminderDueAt, retirementReminderIsDue, retirementSwitchTerms, roundToCents, sameLegalIdentity, scheduledChangeAfterWrite, selectChargeableBundles, settingsSubtreeOf, startOfUtcDay, subscriberChargeColumns, subscriberFromRegistration, subscriberPaymentMethodColumns, subscriptionBundleAlreadyCancelled, subscriptionBundleGone, subscriptionContractGone, subscriptionGone, sumToCents, toBundleStemRow, toCents, toContractLineItemRecord, toDecimalString, toMaintenanceWindowRecord, toPlanRow, toPlanVersionRow, toRunningContractIssuer, toSubscriberChargeRecord, toSubscriberCorrectionRecord, toSubscriberPaymentMethodRecord, toSubscriberRecord, toSubscriptionBundleRecord, toSubscriptionContractRecord, toSubscriptionNoticeRecord, toVersionRetirementRecord, versionOnSale, versionOnSaleOrNext, versionSale };
11585
+ export { ACTIVE_SUBSCRIPTION_CONTRACT_STATUSES, AUTH_ERROR_CODES, type ActionKey, type ActivationOrchestrator, type ActivePlanVersionWhere, type ActivePlanVersionWhereWithEndsAt, type ActiveVersionWhere, type ActiveVersionWhereWithEndsAt, type ActorTag, type AdminAccountEntry, type AdminAccountHolder, type AdminActor, type AdminAuditListFilter, type AdminManifest, type AdminResourcesPort, type AdminSubscriberAccount, type AdminSubscriberAttention, type AdminSubscriberCorrected, type AdminSubscriberHistoryEntry, type AdminSubscriptionListRow, type AdminTenantDetail, type AdminTenantListFilter, type AdminTenantListRow, type AdminTenantStateResult, type AdminTenantSubscriber, type AdminUserListFilter, type AdminUserListRow, type AdminVatIdCheckOutcome, type AppliedSettingsPort, type AppliedSettingsRecord, type AppliedSettingsValues, type ApplyOnboardingSelectionInput, type ApplyOnboardingSelectionResult, type ApprovedCatalogKeys, type ArchivedInvoiceDocument, type AuditActionDef, type AuditActor, type AuditEntry, type AuditPort, type AuditQuery, type AuditQueryPort, type AuditStatsPort, type AuditStatsSnapshot, type AuthErrorCode, BILLING_ERROR_CODES, BUNDLE_PRICE_LOOKUP_LIMIT, BUNDLE_VERSION_RETIREMENT_CAPABILITY, type BillingCycle, type BillingErrorCode, type BundleAvailabilityState, type BundleCompatibility, type BundleFeatureShape, type BundleListFilter, type BundlePricingOverride, type BundleRepository, type BundleRetirementAnnounced, type BundleRetirementPreview, type BundleRetirementReachedRow, type BundleRetirementSkipReason, type BundleRetirementSkippedRow, type BundleRetirementSwitchResult, type BundleRetirementSwitchTerms, type BundleRow, type BundleVersionFields, type BundleVersionMutationResult, type BundleVersionOfferView, type BundleVersionOfferedNotice, type BundleVersionRetiredNotice, type BundleVersionRetirementRecord, type BundleVersionRetirementReminder, type BundleVersionRetirementRepository, type BundleVersionRetirementView, type BundleVersionRow, type BundleVersionSide, type BundleVersionSwitchResult, CATALOGUE_KEYS, CATALOG_ERROR_CODES, CONTRACT_ERROR_CODES, CUSTOM_LIMITS_KEYS, type CalendarDay, type CancelSubscriptionBundleData, type CancelSubscriptionInput, type CancelSubscriptionResult, type CancellationNoticePeriods, type CanonicalBundleVersionRetirementRow, type CanonicalContractLineItemRow, type CanonicalContractRow, type CanonicalFeatureWithdrawalRow, type CanonicalMaintenanceWindowRow, type CanonicalPlanRow, type CanonicalPlanVersionRow, type CanonicalRunningContractRow, type CanonicalSubscriberChargeRow, type CanonicalSubscriberCorrectionRow, type CanonicalSubscriberPaymentMethodRow, type CanonicalSubscriberRow, type CanonicalSubscriberTaxOriginChangeRow, type CanonicalSubscriberVatIdCheckRow, type CanonicalSubscriptionBundleRow, type CanonicalSubscriptionInvoiceArchiveRow, type CanonicalSubscriptionInvoiceDocumentRow, type CanonicalSubscriptionInvoiceLineRow, type CanonicalSubscriptionInvoiceRow, type CanonicalSubscriptionNoticeRow, type CanonicalVersionRetirementRow, type CapabilityCatalogEntryRow, type CapabilityCodeStatus, type CapabilityKey, type CapabilityKind, type CatalogEntryFilter, type CatalogEntryI18n, type CatalogEntryI18nFields, type CatalogEntryRepository, type CatalogErrorCode, type CatalogVersionKind, type ChangeDirection, type CheckoutOfferFilter, type CheckoutOfferLineItem, type CheckoutOfferLineItemKind, type CheckoutOfferPriceBreakdown, type CheckoutOfferPromoCodeSnapshot, type CheckoutOfferPromotionSnapshot, type CheckoutOfferRepository, type CheckoutOfferRow, type CheckoutOfferSelection, type CheckoutOfferSelectionUpdate, type CheckoutOfferStatus, type CleanupResult, type CliUserRow, type ComponentKey, type ConcurrencyGate, type ConfiguratorCatalog, type ConfiguratorMarketingProvider, type ConfiguratorModel, type ConfiguratorPlanMarketing, type ConfiguratorPlanVersionRow, type ConfiguratorPriceBreakdown, type ConfiguratorSourcesLookup, type ConfirmedPaymentMethod, type ContractErrorCode, type ContractIssuerParty, type ContractLineItemKind, type ContractLineItemRecord, type ContractSubscriberParty, type CreateBundleData, type CreateBundleVersionDraftData, type CreateCheckoutOfferData, type CreateMarketingProjectionData, type CreatePlanData, type CreatePlanVersionDraftData, type CreatePromoCodeData, type CreatePromoCodeRequest, type CreatePromotionData, type CreateSubscriberData, type CreateSubscriptionBundleData, type CreateSubscriptionContractData, type CreateSuperAdminCliInput, type CreateTenantInput, type CustomLimits, type DiffResult, type DiscoveredCapability, type DiscoveredFeature, type DiscoveredQuota, type DiscoveredQuotaPolicy, type DiscoveryCodeStatus, type DiscoverySnapshot, type DiscoveryStatus, ERROR_MESSAGES_DE, ERROR_MESSAGES_EN, type EffectiveLimitsSnapshot, type EmailPort, type EndAtOncePreview, type EndNowInput, type EndSubscriptionNowResult, type EndedAtOnce, type EndedAtOnceNotice, type ErrorMessageParams, FEATURE_NOT_LICENSED, FEATURE_WITHDRAWAL_CAPABILITY, FEATURE_WITHDRAWAL_REASON_MAX_LENGTH, type FeatureCatalogEntryRow, type FeatureDef, type FeatureKey, type FeatureNotLicensedBody, type FeatureRequiresIndex, type FeatureTier, type FeatureUiMeta, type FeatureUiRegistry, type FeatureWithdrawalAnnounced, type FeatureWithdrawalBlocker, type FeatureWithdrawalLift, type FeatureWithdrawalLifted, type FeatureWithdrawalLiftedNotice, type FeatureWithdrawalLine, type FeatureWithdrawalLineKind, type FeatureWithdrawalMark, type FeatureWithdrawalNoticeLine, type FeatureWithdrawalPreview, type FeatureWithdrawalReachedRow, type FeatureWithdrawalRecord, type FeatureWithdrawalReduction, type FeatureWithdrawalRepository, type FeatureWithdrawalRow, type FeatureWithdrawalSkippedRow, type FeatureWithdrawalTarget, type FeatureWithdrawalView, type FeatureWithdrawnNotice, type FinalActivationResult, type FirstTimeCustomerCheck, ForeignPaymentMethodReferenceError, type HeldVatId, INVOICE_ARCHIVE_TRIGGERS, INVOICE_DOCUMENT_CAPABILITY, INVOICE_ERROR_CODES, INVOICE_SEQUENCE_DIGITS, type ImmediatePlanChangeInput, type InvoiceArchivePort, type InvoiceContentDraft, type InvoiceDocument, type InvoiceDocumentToArchive, type InvoiceDraftInput, type InvoiceErrorCode, type InvoiceGroup, type InvoiceLineItemSnapshot, type InvoiceMail, type InvoiceMailPort, type InvoiceRenderer, type InvoiceTaxLine, type IssuerCorrectionFault, type IssuerHistoryStep, type IssuerIdentityChange, type KpiCardDef, type KpiDisplayHint, LEGAL_IDENTITY_FIELDS, type LegalIdentity, type LegalIdentityField, MAINTENANCE_CAPABILITY, MAINTENANCE_ERROR_CODES, MAINTENANCE_MESSAGE_MAX_LENGTH, MAINTENANCE_STATE_MAX_AGE_SECONDS, MARKETING_PRIORITY_MAX, MARKETING_PRIORITY_MIN, type MaintenanceErrorCode, type MaintenanceLockView, type MaintenanceNotificationPort, type MaintenanceOverview, type MaintenanceStatusView, type MaintenanceUnlockView, type MaintenanceWindowChanges, type MaintenanceWindowEvent, type MaintenanceWindowPort, type MaintenanceWindowRecord, type MaintenanceWindowStage, type MaintenanceWindowStatus, type MaintenanceWindowView, type ManifestAccessPort, type ManifestContribution, type MarketingProjectionFilter, type MarketingProjectionRepository, type MarketingProjectionRow, type MarketingSettingsRepository, type MarketingSettingsRow, type MarketingTargetType, type MarketingTopFeature, type MaskedPaymentMethod, type MfaPort, NO_SCHEDULED_CHANGE, type NewBundleVersionRetirement, type NewContractLineItemData, type NewFeatureWithdrawal, type NewInvoiceRendering, type NewMaintenanceWindow, type NewSettingsChange, type NewSubscriberCharge, type NewSubscriberDetails, type NewSubscriptionContractData, type NewSubscriptionInvoice, type NewSubscriptionInvoiceLine, type NewVersionRetirement, type NotToldReasons, type NoticeToRecord, OPTIONAL_CANONICAL_MODELS, OTP_RATE_LIMIT_MAX_SENDS, OTP_RATE_LIMIT_WINDOW_MINUTES, OTP_TTL_MINUTES, OTP_VERIFY_MAX_ATTEMPTS, type OnboardingPromoRedemption, type OnboardingSelectionRequest, type OnboardingSelectionResponse, type OptionalBundleMember, type OptionalCanonicalModel, type OrderlyRetirementSettings, PASSWORD_RESET_TTL_MINUTES, PAYMENT_ERROR_CODES, PAYMENT_METHOD_TYPES, PENDING_CHECKOUT_TTL_DAYS, PENDING_EMAIL_TTL_HOURS, PENDING_ONBOARDING_TTL_DAYS, PLATFORM_ERROR_CODES, PROMO_ERROR_CODES, type Paginated, type PartyAddress, type PasswordHasher, type PasswordResetCliResult, PaymentCallbackRejectedError, type PaymentErrorCode, type PaymentEventClaim, type PaymentEventLog, type PaymentGateway, type PaymentGatewayCallback, type PaymentGatewayEvent, type PaymentMethodHolder, type PaymentMethodSetupSession, type PaymentMethodSetupSubject, type PaymentMethodType, type PendingRegistration, type PendingRegistrationCreateInput, type PendingRegistrationRepository, type PendingRegistrationSnapshot, type PendingRegistrationUpdateInput, type PersistenceCapabilities, PersistenceCapabilityError, type PersistenceClassRef, type PersistenceInjectionToken, type PersistenceProvider, PersistenceRefusal, type PersistenceRefusalReason, type PlanCatalog, type PlanCatalogApp, type PlanCatalogImportReport, type PlanCatalogImportSink, type PlanCatalogInvoicing, type PlanCatalogIssuer, type PlanCatalogIssuerCorrection, type PlanCatalogLookup, type PlanCatalogMarketing, type PlanCatalogNotifications, type PlanCatalogPaymentAccount, type PlanCatalogPayments, type PlanCatalogReadSink, type PlanCatalogReadSnapshot, type PlanCatalogSettings, type PlanCatalogSubscribers, type PlanCatalogTax, type PlanCatalogTenantBilling, type PlanDef, type PlanId, type PlanListFilter, type PlanRepository, type PlanRow, type PlanVersion, type PlanVersionFields, type PlanVersionMutationResult, type PlanVersionRecord, type PlanVersionRepository, type PlanVersionRow, type PlatformErrorBody, type PlatformErrorCode, type PlatformJobActor, type PlatformRole, type PlatformUserDto, PlatformUserExistsError, type ProjectPageDef, type PromoCode, type PromoCodeDurationType, type PromoCodeFilter, type PromoCodeHoldRecord, type PromoCodeHoldRepository, type PromoCodeHoldTaken, type PromoCodeRecord, type PromoCodeRedemption, type PromoCodeRedemptionListItem, type PromoCodeRedemptionRecord, type PromoCodeRedemptionRepository, type PromoCodeRedemptionStatus, type PromoCodeRepository, type PromoCodeStatsPort, type PromoCodeStatsSnapshot, type PromoCodeStatus, type PromoCodeValidationLog, type PromoCodeValidationLogRepository, type PromoCodeValidationResult, type PromoCodeValueType, type PromoErrorCode, type PromoPreviewInvalidReason, type PromoPreviewRequest, type PromoPreviewResponse, type PromoPreviewValidResponse, type PromoRevenueDeductionAggregator, type PromoSubscriptionLookup, type PromotionBillingCycle, type PromotionI18n, type PromotionI18nFields, type PromotionOnPrice, type PromotionRepository, type PromotionResult, type PromotionRow, type PromotionStatus, type PromotionTargetType, type PromotionType, type PromotionValue, type PublicBootResponse, type PublicComparisonRow, type PublicMarketingBundle, type PublicMarketingCatalogResponse, type PublicMarketingPlan, type PublicMarketingPromo, type PublicNewPaymentMethods, type PublicSignupPlan, type PublishBundleVersionData, type PublishBundleVersionMeta, type PublishPlanVersionData, type QuotaCatalogEntryRow, type QuotaEnforcementMode, type QuotaKey, type QuotaProvider, REGISTRATION_ERROR_CODES, REGISTRATION_RESUME_TTL_MINUTES, REGISTRATION_STEP_BY_STATUS, RETIREMENT_REMINDER_LEAD_DAYS, type ReadCustomLimits, type ReassignTenantAdminCliResult, type RecommendablePlan, type RecordSubscriberPaymentMethodData, type RecordSubscriberPaymentMethodOutcome, type RecordSubscriberPaymentMethodResult, type RecordedVatIdCheck, type RedeemPromoInTransactionCallback, type RegistrationActivation, type RegistrationAuditContext, type RegistrationAuditEvent, type RegistrationAuditEventType, type RegistrationAuditLogger, type RegistrationBillingDetails, type RegistrationConfigSelection, type RegistrationConfiguratorLookup, type RegistrationErrorCode, type RegistrationOtpDelivery, type RegistrationPromoPreview, type RegistrationResumeDelivery, type RegistrationResumeTokenSigner, type RegistrationStatus, type RegistrationStep, type RequiredCapabilities, type ResolvableErrorBody, type ResumeRegistrationInput, type ResumeRegistrationResult, type RetiredBundleVersionRef, type RetiredVersionRef, type RetirementAnnounced, type RetirementBlocker, type RetirementMoveRun, type RetirementPreview, type RetirementPricedSide, type RetirementProgress, type RetirementReachedRow, type RetirementSide, type RetirementSkipReason, type RetirementSkippedRow, type RetirementSwitchResult, type RetirementSwitchTerms, type ReviewCatalogEntryData, type RlsBypassPort, type RunningContractIssuer, type RunningContractIssuers, SETTINGS_ERROR_CODES, SETUP_ERROR_CODES, SUBSCRIBER_ACCOUNT_CAPABILITY, SUBSCRIBER_ATTENTION_CAPABILITY, SUBSCRIBER_ATTENTION_PAGE_SIZE, SUBSCRIBER_CHARGE_ORIGINS, SUBSCRIBER_CHARGE_SOURCES, SUBSCRIBER_CORRECTION_CAPABILITY, SUBSCRIBER_ERROR_CODES, SUBSCRIBER_INVOICE_ADDRESS_FIELDS, SUBSCRIBER_STANDING_CAPABILITY, SUBSCRIBER_TAX_ORIGIN_FIELDS, type SaaSiCatPersistenceAdapter, type SaaSiCatPersistenceAdminResources, type SaaSiCatPersistenceCatalog, type SaaSiCatPersistenceCore, type SaaSiCatPersistenceEntitlement, type SaaSiCatPersistencePayments, type SaaSiCatPersistencePromo, type SaaSiCatPersistenceTenantBilling, type SaveRegistrationConfigInput, type SaveRegistrationConfigResult, type ScheduledChangeColumns, type ScheduledPlanChangeInput, type SecretSealer, type SelectPlanInput, type SelectPlanResult, type SelectableBundleShape, type SelfServiceBlockedPlans, type SetCatalogEntryReviewData, type SettingsChangeFilter, type SettingsChangeRecord, type SettingsDifference, type SettingsErrorCode, type SetupConfirmMfaRequest, type SetupConfirmMfaResponse, type SetupErrorCode, type SetupRequest, type SetupResult, type SetupStatusResponse, type SlugAvailabilityCheck, type StandardPageDef, type StandardPageKey, type StartCheckoutInput, type StartCheckoutResult, type StartPaymentMethodSetupInput, type StartRegistrationInput, type StartRegistrationResult, type StoredBundleStem, type StrictModeWarning, type StrictModeWarningCode, type SubscriberBusinessStatusData, type SubscriberBusinessStatusResult, type SubscriberCharge, type SubscriberChargeOrigin, type SubscriberChargeRecord, type SubscriberChargeSource, type SubscriberContact, type SubscriberContactChange, type SubscriberCorrectionData, type SubscriberCorrectionRecord, type SubscriberCorrectionResult, type SubscriberDetails, type SubscriberErrorCode, type SubscriberIdentityCorrection, type SubscriberIdentityDelta, type SubscriberIdentityValues, type SubscriberInvoiceAddressField, type SubscriberLedgerRepository, type SubscriberPaymentMethodRecord, type SubscriberPaymentMethodReference, type SubscriberPaymentMethodRepository, type SubscriberPaymentMethodSetupData, type SubscriberPaymentMethodSetupMatch, type SubscriberPaymentMethodStatus, type SubscriberReadiness, type SubscriberRecord, type SubscriberRepository, type SubscriberTaxOrigin, type SubscriberTaxOriginChangeRecord, type SubscriberTaxOriginField, type SubscriberTaxOriginValues, type SubscriberTaxStatus, type SubscriberVatIdCheckRecord, type SubscriberVatIdCheckResult, type SubscriberWithCurrentCheck, type Subscription, type SubscriptionBundleRecord, type SubscriptionBundleRepository, type SubscriptionBundleView, type SubscriptionContractFilter, type SubscriptionContractInvoiceSnapshot, type SubscriptionContractParties, type SubscriptionContractPriceSnapshot, type SubscriptionContractRecord, type SubscriptionContractRepository, type SubscriptionContractStatus, type SubscriptionInvoiceColumns, type SubscriptionInvoiceDocumentRecord, type SubscriptionInvoiceIssuer, type SubscriptionInvoiceLineColumns, type SubscriptionInvoiceLineRecord, type SubscriptionInvoiceRecord, type SubscriptionInvoiceRepository, type SubscriptionInvoiceSubscriber, type SubscriptionNotice, type SubscriptionNoticeDelivery, type SubscriptionNoticeKey, type SubscriptionNoticeKind, type SubscriptionNoticePort, type SubscriptionNoticeRecord, type SubscriptionNoticeRepository, type SubscriptionRecord, type SubscriptionRepository, type SubscriptionStatsPort, type SubscriptionStatsSnapshot, type SubscriptionStatus, type SubscriptionUsagePort, type SubscriptionUsageRecord, type SuperAdminProvisioningPort, type SupersedeSubscriptionContractData, type SyncDiscoveryResult, TAX_ERROR_CODES, TAX_TREATMENT_KINDS, type TaxAdapter, type TaxAdapterFactory, type TaxAdapterIdentity, type TaxAtRate, type TaxDecision, type TaxDecisionRequest, type TaxErrorCode, type TaxIssuer, type TaxOriginWrite, type TaxPerRate, type TaxTreatment, type TaxTreatmentKind, type TenantActionDef, type TenantColumnDef, type TenantDto, type TenantFeatureWithdrawal, type TenantFeatureWithdrawalLine, type TenantListFilter, type TenantPort, type TenantSubscriptionUsage, type TenantSubscriptionWritePort, type TerminateSubscriptionContractData, type TopPromoCode, type TransactionContext, type TransactionRunner, type UpdateBundleData, type UpdateBundleVersionDraftData, type UpdateCatalogEntryBaseData, type UpdateCatalogEntryI18nData, type UpdateCheckoutOfferData, type UpdateMarketingProjectionData, type UpdateMarketingSettingsData, type UpdatePlanData, type UpdatePlanVersionDraftData, type UpdatePromoCodeData, type UpdatePromoCodeRequest, type UpdatePromotionData, type UpsellOffer, type UpsellOfferResolver, type UpsertCapabilityEntryData, type UpsertFeatureCatalogEntryInput, type UpsertFeatureEntryData, type UpsertPlanInput, type UpsertPlanVersionInput, type UpsertQuotaEntryData, type UpsertResult, type UsageSnapshotPort, type UserAccountLookup, type UserListFilter, type UserManagementPort, type UserPort, VERSION_RETIREMENT_CAPABILITY, type VatCategory, type VatIdCheck, type VatIdCheckOutcome, type VerifyRegistrationOtpResult, type VersionChange, type VersionChangeDirection, type VersionEditability, type VersionEditableReason, type VersionOffer, type VersionOfferClass, type VersionOfferFields, type VersionOfferSide, type VersionOfferView, type VersionOfferedNotice, type VersionRetiredNotice, type VersionRetirementRecord, type VersionRetirementReminder, type VersionRetirementRepository, type VersionRetirementView, type VersionSale, type VersionSaleDates, type VersionSaleKind, type VersionSwitchResult, type VersionWindow, type VersionedEntityBase, type WithdrawnFeature, addDaysToDay, applyPromo, asXmlReadsIt, assertPersistenceCapabilities, buildActivePlanVersionWhere, buildActiveVersionWhere, buildFeatureRequiresIndex, bundleDraftDefaults, bundleKeyTaken, bundleStemDefaults, canonicalJson, canonicalVatId, catalogDraftExists, catalogVersionAlreadyPublished, catalogVersionGone, classifyBundleVersionDiff, classifyIssuerChange, classifyPlanDiff, classifyVersionOffer, collectUnsatisfiedRequires, computeIncludedVat, concurrencyGate, concurrencyGateOf, contractPartiesOf, coverageExcludingSelf, customLimitsReader, dayInZone, definedFields, diffSettings, draftSubscriptionInvoice, endedAtOnceSubjectOf, featureWithdrawalRowOf, featuresWithdrawnAt, formatCustomerNumber, formatErrorMessage, formatInvoiceNumber, grossFromNet, holdsOnlyXmlCharacters, identityCorrectionDelta, invoiceArchiveProtectionProblem, invoiceContentDraftOf, invoiceFooterOf, invoiceGroupsOf, invoiceIssuerOf, isBundleRedundant, isFeatureWithdrawnAt, isForeignPaymentMethodReferenceError, isInvoiceDocumentChecksum, isMaintenanceAnnouncementLapsed, isMaintenanceOverrun, isPaymentCallbackRejectedError, isPersistenceRefusal, isPlatformUserExistsError, isVersionActiveAt, isVersionEditable, issuerIdentityOf, keepOneRecommended, keepsVatIdCheck, lastDayInZone, maintenanceRetryAfterSeconds, maintenanceStatusOf, maintenanceWindowStatusOf, maintenanceWindowViewOf, marketingProjectionTaken, membersLeftOut, missingRequiresFor, movedIdentityFields, netFromGross, nextWithdrawalChange, noActivePlanVersion, parseZonedInstant, percentOf, pickActivePromo, planCatalogSettingsOf, planKeyTaken, planNotInCatalog, previousUtcDay, promoCodeTaken, promoStatus, promotionOnPrice, prorate, readCustomLimits, readQuotaRecord, readQuotaValue, recordedIssuerIdentity, refuseForeignPaymentMethodReference, resolveBundleAvailability, resolveErrorMessage, retirementCostsTheSubscription, retirementReminderDueAt, retirementReminderIsDue, retirementSwitchTerms, roundToCents, sameLegalIdentity, scheduledChangeAfterWrite, selectChargeableBundles, settingsSubtreeOf, startOfUtcDay, subscriberChargeColumns, subscriberFromRegistration, subscriberPaymentMethodColumns, subscriptionBundleAlreadyCancelled, subscriptionBundleGone, subscriptionContractGone, subscriptionGone, subscriptionInvoiceChargeInvoiced, subscriptionInvoiceColumns, subscriptionInvoiceDocumentChanged, subscriptionInvoiceLineColumns, sumToCents, taxOriginOf, taxOriginWrite, taxPerRate, taxTreatmentToJson, toArchivedInvoiceDocument, toBundleStemRow, toBundleVersionRetirementRecord, toCents, toContractLineItemRecord, toDecimalString, toFeatureWithdrawalRecord, toMaintenanceWindowRecord, toPlanRow, toPlanVersionRow, toRunningContractIssuer, toSubscriberChargeRecord, toSubscriberCorrectionRecord, toSubscriberPaymentMethodRecord, toSubscriberRecord, toSubscriberTaxOriginChangeRecord, toSubscriberVatIdCheckRecord, toSubscriptionBundleRecord, toSubscriptionContractRecord, toSubscriptionInvoiceDocumentRecord, toSubscriptionInvoiceRecord, toSubscriptionNoticeRecord, toTaxOriginValues, toTaxTreatment, toVersionRetirementRecord, utcMidnightOf, vatCategoryOf, vatIdCheckFromStore, versionOnSale, versionOnSaleOrNext, versionSale, withdrawnFeaturesOf, yearOfDay };