@saasicat/core 1.0.0-rc.13 → 1.0.0-rc.15

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/.build-stamp CHANGED
@@ -1 +1 @@
1
- 80e103efbcf415693ffe96c49f6aae0ba7201fb0aa4cac7b47004cd3d69fb3cc
1
+ 785f84b789c4fbc325a4d35f81dec979a2575832412363bbcf2fbbe87995e875
package/dist/index.cjs CHANGED
@@ -543,6 +543,11 @@ var BILLING_ERROR_CODES = {
543
543
  PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG",
544
544
  /** Plan exists but cannot be booked via self-service. */
545
545
  PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE",
546
+ /**
547
+ * Plan carries no price for the requested billing cycle, so it is not sold
548
+ * in it: a plan without a yearly price is a monthly plan.
549
+ */
550
+ PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE",
546
551
  /** Plan change refused. Carries `blockers[]` with their own codes. */
547
552
  PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED",
548
553
  /**
@@ -651,6 +656,7 @@ var CONTRACT_ERROR_CODES = {
651
656
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE",
652
657
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW",
653
658
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH",
659
+ SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT",
654
660
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH",
655
661
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START",
656
662
  // ── stage 3 ──
@@ -658,6 +664,12 @@ var CONTRACT_ERROR_CODES = {
658
664
  CHECKOUT_OFFER_EXPIRED: "CHECKOUT_OFFER_EXPIRED",
659
665
  CHECKOUT_OFFER_ALREADY_CONSUMED: "CHECKOUT_OFFER_ALREADY_CONSUMED",
660
666
  CHECKOUT_OFFER_NOT_CONSUMED: "CHECKOUT_OFFER_NOT_CONSUMED",
667
+ /**
668
+ * The offer changed between the checks of `conclude` and the transaction
669
+ * that consumed it, so the contract checked is not the one the offer now
670
+ * describes. Nothing was written; load the offer and conclude it again.
671
+ */
672
+ CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED",
661
673
  SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND",
662
674
  NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT",
663
675
  SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED"
@@ -1081,6 +1093,7 @@ var ERROR_MESSAGES_EN = {
1081
1093
  NO_ACTIVE_PLAN_VERSION: "No plan version for {planId} active as of {asOf} \u2014 neither the validFrom window is satisfied nor is a latest-live version available.",
1082
1094
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" is not in the catalog',
1083
1095
  PLAN_NOT_SELF_SERVICE: "{planName} is only activated via a special contract. Please contact the contract manager.",
1096
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} has no price for this billing rhythm and cannot be booked in it.",
1084
1097
  PLAN_CHANGE_BLOCKED: "Plan change during onboarding is blocked.",
1085
1098
  SUBSCRIPTION_CHANGED: "This subscription changed while the request was being decided. Reload it.",
1086
1099
  NO_SUBSCRIPTION: "This tenant has no subscription to cancel.",
@@ -1133,12 +1146,14 @@ var ERROR_MESSAGES_EN = {
1133
1146
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} must be a valid date.",
1134
1147
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil must be after effectiveFrom.",
1135
1148
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "A line item's taxAmount must be exactly priceGross minus priceNet.",
1149
+ SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "A subscription contract states a tax rate of {taxRate} at {field}, which is not a percentage: a rate lies from 0 to 100, and a value between 0 and 1 is refused as a fraction.",
1136
1150
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil must be after the effectiveFrom of the contract.",
1137
1151
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "A line item must be booked in the currency its contract was priced in.",
1138
1152
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' not found",
1139
1153
  CHECKOUT_OFFER_EXPIRED: "Checkout offer '{offerId}' has expired and cannot be {action}",
1140
1154
  CHECKOUT_OFFER_ALREADY_CONSUMED: "Checkout offer '{offerId}' has already been consumed and cannot be {action}",
1141
1155
  CHECKOUT_OFFER_NOT_CONSUMED: "CheckoutOffer '{offerId}' must be consumed before the contract is created",
1156
+ CHECKOUT_OFFER_CHANGED: "Checkout offer '{offerId}' changed while it was being concluded. Load it again.",
1142
1157
  SUBSCRIPTION_CONTRACT_NOT_FOUND: "SubscriptionContract '{contractId}' not found",
1143
1158
  NO_ACTIVE_SUBSCRIPTION_CONTRACT: "No active subscription contract for tenant {tenantId}",
1144
1159
  SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SubscriptionContract '{contractId}' is already closed",
@@ -1268,6 +1283,7 @@ var ERROR_MESSAGES_DE = {
1268
1283
  NO_ACTIVE_PLAN_VERSION: "Zum {asOf} ist keine Planversion f\xFCr {planId} aktiv \u2014 weder ist das validFrom-Fenster erf\xFCllt, noch steht eine j\xFCngste Live-Version bereit.",
1269
1284
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" steht nicht im Katalog',
1270
1285
  PLAN_NOT_SELF_SERVICE: "{planName} wird nur \xFCber einen Sondervertrag freigeschaltet. Bitte wenden Sie sich an die Vertragsverwaltung.",
1286
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} hat f\xFCr diesen Abrechnungsrhythmus keinen Preis und kann darin nicht gebucht werden.",
1271
1287
  PLAN_CHANGE_BLOCKED: "W\xE4hrend des Onboardings ist ein Planwechsel gesperrt.",
1272
1288
  SUBSCRIPTION_CHANGED: "Dieses Abonnement hat sich ge\xE4ndert, w\xE4hrend die Anfrage entschieden wurde. Bitte lade es neu.",
1273
1289
  NO_SUBSCRIPTION: "Dieser Mandant hat kein Abonnement, das gek\xFCndigt werden kann.",
@@ -1320,12 +1336,14 @@ var ERROR_MESSAGES_DE = {
1320
1336
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} muss ein g\xFCltiges Datum sein.",
1321
1337
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil muss nach effectiveFrom liegen.",
1322
1338
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "taxAmount einer Position muss exakt priceGross minus priceNet sein.",
1339
+ SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "Ein Abo-Vertrag nennt bei {field} einen Steuersatz von {taxRate}, der kein Prozentsatz ist: Ein Satz liegt zwischen 0 und 100, und ein Wert zwischen 0 und 1 wird als Bruch abgelehnt.",
1323
1340
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "Eine Position muss in der W\xE4hrung gebucht sein, in der ihr Vertrag bepreist ist.",
1324
1341
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil muss nach dem effectiveFrom des Vertrags liegen.",
1325
1342
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' nicht gefunden",
1326
1343
  CHECKOUT_OFFER_EXPIRED: "Checkout-Angebot '{offerId}' ist abgelaufen \u2014 '{action}' ist nicht mehr m\xF6glich",
1327
1344
  CHECKOUT_OFFER_ALREADY_CONSUMED: "Checkout-Angebot '{offerId}' wurde bereits eingel\xF6st \u2014 '{action}' ist nicht mehr m\xF6glich",
1328
1345
  CHECKOUT_OFFER_NOT_CONSUMED: "CheckoutOffer '{offerId}' muss eingel\xF6st sein, bevor der Vertrag entsteht",
1346
+ CHECKOUT_OFFER_CHANGED: "Checkout-Angebot '{offerId}' hat sich w\xE4hrend des Abschlusses ge\xE4ndert. Bitte neu laden.",
1329
1347
  SUBSCRIPTION_CONTRACT_NOT_FOUND: "SubscriptionContract '{contractId}' nicht gefunden",
1330
1348
  NO_ACTIVE_SUBSCRIPTION_CONTRACT: "Kein aktiver Abo-Vertrag f\xFCr Mandant {tenantId}",
1331
1349
  SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SubscriptionContract '{contractId}' ist bereits geschlossen",
package/dist/index.d.cts CHANGED
@@ -2003,6 +2003,11 @@ interface PromoPreviewValidResponse {
2003
2003
  /** Decimal-as-string, e.g. "199.00". */
2004
2004
  originalGross: string;
2005
2005
  discountGross: string;
2006
+ /**
2007
+ * `discountGross` in net, converted at the installation's VAT rate —
2008
+ * the figure a page showing net prices takes off the plan price.
2009
+ */
2010
+ discountNet: string;
2006
2011
  discountedGross: string;
2007
2012
  includedVat: string;
2008
2013
  nextRegularAmountGross: string;
@@ -2821,16 +2826,8 @@ interface SubscriptionContractPriceSnapshot {
2821
2826
  discountNet: number;
2822
2827
  totalNet: number;
2823
2828
  /**
2824
- * The rate this contract's total was computed at — in per cent where the
2825
- * contract was frozen from the catalogue, and as the offer stated it where
2826
- * it was concluded from one.
2827
- *
2828
- * A checkout offer prices its lines as `net * (1 + vatRate)`, so it states
2829
- * a fraction, and the value is copied here as it stands. The field
2830
- * therefore carries both units across a history and cannot be compared
2831
- * across contracts. `ContractLineItemRecord.taxRate` is always per cent and
2832
- * is the one to read; this is kept as written because it is the record of
2833
- * what the contract was concluded with.
2829
+ * The tax rate this contract's total was computed at, as a percentage:
2830
+ * 19 means 19 %, as every tax rate in SaaSiCat is.
2834
2831
  */
2835
2832
  vatRate: number;
2836
2833
  totalGross: number;
@@ -3088,7 +3085,15 @@ interface SubscriptionContractRepository {
3088
3085
  * instead of drawing an extra pool connection (starvation guard, #70).
3089
3086
  */
3090
3087
  findActiveByTenantId(tenantId: string, asOf?: Date, tx?: TransactionContext): Promise<SubscriptionContractRecord | null>;
3091
- create(data: CreateSubscriptionContractData): Promise<SubscriptionContractRecord>;
3088
+ /** With `tx`, the contract is written on that transaction and undone with it. */
3089
+ create(data: CreateSubscriptionContractData, tx?: TransactionContext): Promise<SubscriptionContractRecord>;
3090
+ /**
3091
+ * The contract concluded from a checkout offer (`originalOfferId`), or
3092
+ * `null` when none was. An offer is consumed once and so yields one
3093
+ * contract (`SC-MKT-017`); where an application's own path wrote two, the
3094
+ * earliest is returned, so the answer does not depend on read order.
3095
+ */
3096
+ findByOriginalOfferId(offerId: string, tx?: TransactionContext): Promise<SubscriptionContractRecord | null>;
3092
3097
  terminate(contractId: string, data: TerminateSubscriptionContractData): Promise<SubscriptionContractRecord>;
3093
3098
  }
3094
3099
  /**
@@ -3404,6 +3409,8 @@ interface PlanVersionRepository {
3404
3409
  *
3405
3410
  * Note: ignores `validFrom`/`validUntil`. For time-aware
3406
3411
  * resolution (onboarding, plan fallback for TRIAL) use `findActive`.
3412
+ *
3413
+ * A plan key no plan has finds `null`, not an error.
3407
3414
  */
3408
3415
  findLatestLive(planId: string, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3409
3416
  /**
@@ -3416,6 +3423,8 @@ interface PlanVersionRepository {
3416
3423
  * return the highest `validFrom`, explicitly ordering null start dates
3417
3424
  * last as a legacy fallback. Adapters without validity columns may omit
3418
3425
  * the method (consumers fall back to `findLatestLive`).
3426
+ *
3427
+ * A plan key no plan has finds `null`, not an error.
3419
3428
  */
3420
3429
  findActive?(planId: string, asOf?: Date, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3421
3430
  }
@@ -4029,8 +4038,19 @@ interface CheckoutOfferRepository {
4029
4038
  findById(id: string): Promise<CheckoutOfferRow | null>;
4030
4039
  create(data: CreateCheckoutOfferData): Promise<CheckoutOfferRow>;
4031
4040
  update(id: string, data: UpdateCheckoutOfferData): Promise<CheckoutOfferRow>;
4032
- /** Sets `status = 'consumed'` + `consumedAt = NOW()`. */
4033
- consume(id: string): Promise<CheckoutOfferRow>;
4041
+ /**
4042
+ * Sets `status = 'consumed'` + `consumedAt = NOW()`, and only while the
4043
+ * offer is still `open`: the write carries that condition, so of two
4044
+ * callers consuming at once one succeeds and the other is refused with
4045
+ * `CHECKOUT_OFFER_ALREADY_CONSUMED` (or `CHECKOUT_OFFER_EXPIRED`). A check
4046
+ * before the write cannot decide it, because the status can change in
4047
+ * between.
4048
+ *
4049
+ * With `tx`, the write runs on that transaction, so it is undone with
4050
+ * everything else the transaction wrote. `CheckoutOfferService.conclude`
4051
+ * depends on that; the persistence contract holds both.
4052
+ */
4053
+ consume(id: string, tx?: TransactionContext): Promise<CheckoutOfferRow>;
4034
4054
  }
4035
4055
 
4036
4056
  /** Which changes to list. */
@@ -4351,6 +4371,11 @@ declare const BILLING_ERROR_CODES: {
4351
4371
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4352
4372
  /** Plan exists but cannot be booked via self-service. */
4353
4373
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4374
+ /**
4375
+ * Plan carries no price for the requested billing cycle, so it is not sold
4376
+ * in it: a plan without a yearly price is a monthly plan.
4377
+ */
4378
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4354
4379
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4355
4380
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4356
4381
  /**
@@ -4448,12 +4473,19 @@ declare const CONTRACT_ERROR_CODES: {
4448
4473
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4449
4474
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4450
4475
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4476
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4451
4477
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4452
4478
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4453
4479
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
4454
4480
  readonly CHECKOUT_OFFER_EXPIRED: "CHECKOUT_OFFER_EXPIRED";
4455
4481
  readonly CHECKOUT_OFFER_ALREADY_CONSUMED: "CHECKOUT_OFFER_ALREADY_CONSUMED";
4456
4482
  readonly CHECKOUT_OFFER_NOT_CONSUMED: "CHECKOUT_OFFER_NOT_CONSUMED";
4483
+ /**
4484
+ * The offer changed between the checks of `conclude` and the transaction
4485
+ * that consumed it, so the contract checked is not the one the offer now
4486
+ * describes. Nothing was written; load the offer and conclude it again.
4487
+ */
4488
+ readonly CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED";
4457
4489
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
4458
4490
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
4459
4491
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
@@ -4562,12 +4594,19 @@ declare const PLATFORM_ERROR_CODES: {
4562
4594
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4563
4595
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4564
4596
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4597
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4565
4598
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4566
4599
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4567
4600
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
4568
4601
  readonly CHECKOUT_OFFER_EXPIRED: "CHECKOUT_OFFER_EXPIRED";
4569
4602
  readonly CHECKOUT_OFFER_ALREADY_CONSUMED: "CHECKOUT_OFFER_ALREADY_CONSUMED";
4570
4603
  readonly CHECKOUT_OFFER_NOT_CONSUMED: "CHECKOUT_OFFER_NOT_CONSUMED";
4604
+ /**
4605
+ * The offer changed between the checks of `conclude` and the transaction
4606
+ * that consumed it, so the contract checked is not the one the offer now
4607
+ * describes. Nothing was written; load the offer and conclude it again.
4608
+ */
4609
+ readonly CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED";
4571
4610
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
4572
4611
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
4573
4612
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
@@ -4595,6 +4634,11 @@ declare const PLATFORM_ERROR_CODES: {
4595
4634
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4596
4635
  /** Plan exists but cannot be booked via self-service. */
4597
4636
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4637
+ /**
4638
+ * Plan carries no price for the requested billing cycle, so it is not sold
4639
+ * in it: a plan without a yearly price is a monthly plan.
4640
+ */
4641
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4598
4642
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4599
4643
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4600
4644
  /**
@@ -5186,8 +5230,6 @@ interface ConfiguratorModel {
5186
5230
  popular?: boolean;
5187
5231
  }
5188
5232
  interface ConfiguratorCatalog {
5189
- /** Factor `yearlyNet = monthlyNet * cycleDiscount` (typically 10 = 2 months free). */
5190
- cycleDiscount: number;
5191
5233
  currency: string;
5192
5234
  vatRate: number;
5193
5235
  models: ConfiguratorModel[];
@@ -5212,6 +5254,10 @@ interface ConfiguratorPriceBreakdown {
5212
5254
  modelMonthlyNet: number;
5213
5255
  subtotalMonthlyNet: number;
5214
5256
  subtotalNet: number;
5257
+ /**
5258
+ * Net amount taken off `subtotalNet`: the promo preview's gross discount
5259
+ * converted at `vatRate`.
5260
+ */
5215
5261
  discountAmount: number;
5216
5262
  totalNet: number;
5217
5263
  vatRate: number;
@@ -5288,8 +5334,6 @@ interface ConfiguratorPlanMarketing {
5288
5334
  */
5289
5335
  interface ConfiguratorMarketingProvider {
5290
5336
  listPlanMarketing(): ConfiguratorPlanMarketing[];
5291
- /** Factor `yearlyNet = monthlyNet * cycleDiscount`. Default `10`. */
5292
- getCycleDiscount(): number;
5293
5337
  getVatRate(): number;
5294
5338
  getCurrency(): string;
5295
5339
  }
@@ -5310,6 +5354,7 @@ interface RegistrationPromoPreview {
5310
5354
  reason?: string;
5311
5355
  percent?: number;
5312
5356
  label?: string;
5357
+ /** The discount in gross, reckoned against `subtotalGross`. */
5313
5358
  discountAmount?: number;
5314
5359
  }>;
5315
5360
  }
package/dist/index.d.ts CHANGED
@@ -2003,6 +2003,11 @@ interface PromoPreviewValidResponse {
2003
2003
  /** Decimal-as-string, e.g. "199.00". */
2004
2004
  originalGross: string;
2005
2005
  discountGross: string;
2006
+ /**
2007
+ * `discountGross` in net, converted at the installation's VAT rate —
2008
+ * the figure a page showing net prices takes off the plan price.
2009
+ */
2010
+ discountNet: string;
2006
2011
  discountedGross: string;
2007
2012
  includedVat: string;
2008
2013
  nextRegularAmountGross: string;
@@ -2821,16 +2826,8 @@ interface SubscriptionContractPriceSnapshot {
2821
2826
  discountNet: number;
2822
2827
  totalNet: number;
2823
2828
  /**
2824
- * The rate this contract's total was computed at — in per cent where the
2825
- * contract was frozen from the catalogue, and as the offer stated it where
2826
- * it was concluded from one.
2827
- *
2828
- * A checkout offer prices its lines as `net * (1 + vatRate)`, so it states
2829
- * a fraction, and the value is copied here as it stands. The field
2830
- * therefore carries both units across a history and cannot be compared
2831
- * across contracts. `ContractLineItemRecord.taxRate` is always per cent and
2832
- * is the one to read; this is kept as written because it is the record of
2833
- * what the contract was concluded with.
2829
+ * The tax rate this contract's total was computed at, as a percentage:
2830
+ * 19 means 19 %, as every tax rate in SaaSiCat is.
2834
2831
  */
2835
2832
  vatRate: number;
2836
2833
  totalGross: number;
@@ -3088,7 +3085,15 @@ interface SubscriptionContractRepository {
3088
3085
  * instead of drawing an extra pool connection (starvation guard, #70).
3089
3086
  */
3090
3087
  findActiveByTenantId(tenantId: string, asOf?: Date, tx?: TransactionContext): Promise<SubscriptionContractRecord | null>;
3091
- create(data: CreateSubscriptionContractData): Promise<SubscriptionContractRecord>;
3088
+ /** With `tx`, the contract is written on that transaction and undone with it. */
3089
+ create(data: CreateSubscriptionContractData, tx?: TransactionContext): Promise<SubscriptionContractRecord>;
3090
+ /**
3091
+ * The contract concluded from a checkout offer (`originalOfferId`), or
3092
+ * `null` when none was. An offer is consumed once and so yields one
3093
+ * contract (`SC-MKT-017`); where an application's own path wrote two, the
3094
+ * earliest is returned, so the answer does not depend on read order.
3095
+ */
3096
+ findByOriginalOfferId(offerId: string, tx?: TransactionContext): Promise<SubscriptionContractRecord | null>;
3092
3097
  terminate(contractId: string, data: TerminateSubscriptionContractData): Promise<SubscriptionContractRecord>;
3093
3098
  }
3094
3099
  /**
@@ -3404,6 +3409,8 @@ interface PlanVersionRepository {
3404
3409
  *
3405
3410
  * Note: ignores `validFrom`/`validUntil`. For time-aware
3406
3411
  * resolution (onboarding, plan fallback for TRIAL) use `findActive`.
3412
+ *
3413
+ * A plan key no plan has finds `null`, not an error.
3407
3414
  */
3408
3415
  findLatestLive(planId: string, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3409
3416
  /**
@@ -3416,6 +3423,8 @@ interface PlanVersionRepository {
3416
3423
  * return the highest `validFrom`, explicitly ordering null start dates
3417
3424
  * last as a legacy fallback. Adapters without validity columns may omit
3418
3425
  * the method (consumers fall back to `findLatestLive`).
3426
+ *
3427
+ * A plan key no plan has finds `null`, not an error.
3419
3428
  */
3420
3429
  findActive?(planId: string, asOf?: Date, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3421
3430
  }
@@ -4029,8 +4038,19 @@ interface CheckoutOfferRepository {
4029
4038
  findById(id: string): Promise<CheckoutOfferRow | null>;
4030
4039
  create(data: CreateCheckoutOfferData): Promise<CheckoutOfferRow>;
4031
4040
  update(id: string, data: UpdateCheckoutOfferData): Promise<CheckoutOfferRow>;
4032
- /** Sets `status = 'consumed'` + `consumedAt = NOW()`. */
4033
- consume(id: string): Promise<CheckoutOfferRow>;
4041
+ /**
4042
+ * Sets `status = 'consumed'` + `consumedAt = NOW()`, and only while the
4043
+ * offer is still `open`: the write carries that condition, so of two
4044
+ * callers consuming at once one succeeds and the other is refused with
4045
+ * `CHECKOUT_OFFER_ALREADY_CONSUMED` (or `CHECKOUT_OFFER_EXPIRED`). A check
4046
+ * before the write cannot decide it, because the status can change in
4047
+ * between.
4048
+ *
4049
+ * With `tx`, the write runs on that transaction, so it is undone with
4050
+ * everything else the transaction wrote. `CheckoutOfferService.conclude`
4051
+ * depends on that; the persistence contract holds both.
4052
+ */
4053
+ consume(id: string, tx?: TransactionContext): Promise<CheckoutOfferRow>;
4034
4054
  }
4035
4055
 
4036
4056
  /** Which changes to list. */
@@ -4351,6 +4371,11 @@ declare const BILLING_ERROR_CODES: {
4351
4371
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4352
4372
  /** Plan exists but cannot be booked via self-service. */
4353
4373
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4374
+ /**
4375
+ * Plan carries no price for the requested billing cycle, so it is not sold
4376
+ * in it: a plan without a yearly price is a monthly plan.
4377
+ */
4378
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4354
4379
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4355
4380
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4356
4381
  /**
@@ -4448,12 +4473,19 @@ declare const CONTRACT_ERROR_CODES: {
4448
4473
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4449
4474
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4450
4475
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4476
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4451
4477
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4452
4478
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4453
4479
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
4454
4480
  readonly CHECKOUT_OFFER_EXPIRED: "CHECKOUT_OFFER_EXPIRED";
4455
4481
  readonly CHECKOUT_OFFER_ALREADY_CONSUMED: "CHECKOUT_OFFER_ALREADY_CONSUMED";
4456
4482
  readonly CHECKOUT_OFFER_NOT_CONSUMED: "CHECKOUT_OFFER_NOT_CONSUMED";
4483
+ /**
4484
+ * The offer changed between the checks of `conclude` and the transaction
4485
+ * that consumed it, so the contract checked is not the one the offer now
4486
+ * describes. Nothing was written; load the offer and conclude it again.
4487
+ */
4488
+ readonly CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED";
4457
4489
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
4458
4490
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
4459
4491
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
@@ -4562,12 +4594,19 @@ declare const PLATFORM_ERROR_CODES: {
4562
4594
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4563
4595
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4564
4596
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4597
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4565
4598
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4566
4599
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4567
4600
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
4568
4601
  readonly CHECKOUT_OFFER_EXPIRED: "CHECKOUT_OFFER_EXPIRED";
4569
4602
  readonly CHECKOUT_OFFER_ALREADY_CONSUMED: "CHECKOUT_OFFER_ALREADY_CONSUMED";
4570
4603
  readonly CHECKOUT_OFFER_NOT_CONSUMED: "CHECKOUT_OFFER_NOT_CONSUMED";
4604
+ /**
4605
+ * The offer changed between the checks of `conclude` and the transaction
4606
+ * that consumed it, so the contract checked is not the one the offer now
4607
+ * describes. Nothing was written; load the offer and conclude it again.
4608
+ */
4609
+ readonly CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED";
4571
4610
  readonly SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND";
4572
4611
  readonly NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT";
4573
4612
  readonly SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED";
@@ -4595,6 +4634,11 @@ declare const PLATFORM_ERROR_CODES: {
4595
4634
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4596
4635
  /** Plan exists but cannot be booked via self-service. */
4597
4636
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4637
+ /**
4638
+ * Plan carries no price for the requested billing cycle, so it is not sold
4639
+ * in it: a plan without a yearly price is a monthly plan.
4640
+ */
4641
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4598
4642
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4599
4643
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4600
4644
  /**
@@ -5186,8 +5230,6 @@ interface ConfiguratorModel {
5186
5230
  popular?: boolean;
5187
5231
  }
5188
5232
  interface ConfiguratorCatalog {
5189
- /** Factor `yearlyNet = monthlyNet * cycleDiscount` (typically 10 = 2 months free). */
5190
- cycleDiscount: number;
5191
5233
  currency: string;
5192
5234
  vatRate: number;
5193
5235
  models: ConfiguratorModel[];
@@ -5212,6 +5254,10 @@ interface ConfiguratorPriceBreakdown {
5212
5254
  modelMonthlyNet: number;
5213
5255
  subtotalMonthlyNet: number;
5214
5256
  subtotalNet: number;
5257
+ /**
5258
+ * Net amount taken off `subtotalNet`: the promo preview's gross discount
5259
+ * converted at `vatRate`.
5260
+ */
5215
5261
  discountAmount: number;
5216
5262
  totalNet: number;
5217
5263
  vatRate: number;
@@ -5288,8 +5334,6 @@ interface ConfiguratorPlanMarketing {
5288
5334
  */
5289
5335
  interface ConfiguratorMarketingProvider {
5290
5336
  listPlanMarketing(): ConfiguratorPlanMarketing[];
5291
- /** Factor `yearlyNet = monthlyNet * cycleDiscount`. Default `10`. */
5292
- getCycleDiscount(): number;
5293
5337
  getVatRate(): number;
5294
5338
  getCurrency(): string;
5295
5339
  }
@@ -5310,6 +5354,7 @@ interface RegistrationPromoPreview {
5310
5354
  reason?: string;
5311
5355
  percent?: number;
5312
5356
  label?: string;
5357
+ /** The discount in gross, reckoned against `subtotalGross`. */
5313
5358
  discountAmount?: number;
5314
5359
  }>;
5315
5360
  }
package/dist/index.js CHANGED
@@ -454,6 +454,11 @@ var BILLING_ERROR_CODES = {
454
454
  PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG",
455
455
  /** Plan exists but cannot be booked via self-service. */
456
456
  PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE",
457
+ /**
458
+ * Plan carries no price for the requested billing cycle, so it is not sold
459
+ * in it: a plan without a yearly price is a monthly plan.
460
+ */
461
+ PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE",
457
462
  /** Plan change refused. Carries `blockers[]` with their own codes. */
458
463
  PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED",
459
464
  /**
@@ -562,6 +567,7 @@ var CONTRACT_ERROR_CODES = {
562
567
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE",
563
568
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW",
564
569
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH",
570
+ SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT",
565
571
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH",
566
572
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START",
567
573
  // ── stage 3 ──
@@ -569,6 +575,12 @@ var CONTRACT_ERROR_CODES = {
569
575
  CHECKOUT_OFFER_EXPIRED: "CHECKOUT_OFFER_EXPIRED",
570
576
  CHECKOUT_OFFER_ALREADY_CONSUMED: "CHECKOUT_OFFER_ALREADY_CONSUMED",
571
577
  CHECKOUT_OFFER_NOT_CONSUMED: "CHECKOUT_OFFER_NOT_CONSUMED",
578
+ /**
579
+ * The offer changed between the checks of `conclude` and the transaction
580
+ * that consumed it, so the contract checked is not the one the offer now
581
+ * describes. Nothing was written; load the offer and conclude it again.
582
+ */
583
+ CHECKOUT_OFFER_CHANGED: "CHECKOUT_OFFER_CHANGED",
572
584
  SUBSCRIPTION_CONTRACT_NOT_FOUND: "SUBSCRIPTION_CONTRACT_NOT_FOUND",
573
585
  NO_ACTIVE_SUBSCRIPTION_CONTRACT: "NO_ACTIVE_SUBSCRIPTION_CONTRACT",
574
586
  SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SUBSCRIPTION_CONTRACT_ALREADY_CLOSED"
@@ -992,6 +1004,7 @@ var ERROR_MESSAGES_EN = {
992
1004
  NO_ACTIVE_PLAN_VERSION: "No plan version for {planId} active as of {asOf} \u2014 neither the validFrom window is satisfied nor is a latest-live version available.",
993
1005
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" is not in the catalog',
994
1006
  PLAN_NOT_SELF_SERVICE: "{planName} is only activated via a special contract. Please contact the contract manager.",
1007
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} has no price for this billing rhythm and cannot be booked in it.",
995
1008
  PLAN_CHANGE_BLOCKED: "Plan change during onboarding is blocked.",
996
1009
  SUBSCRIPTION_CHANGED: "This subscription changed while the request was being decided. Reload it.",
997
1010
  NO_SUBSCRIPTION: "This tenant has no subscription to cancel.",
@@ -1044,12 +1057,14 @@ var ERROR_MESSAGES_EN = {
1044
1057
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} must be a valid date.",
1045
1058
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil must be after effectiveFrom.",
1046
1059
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "A line item's taxAmount must be exactly priceGross minus priceNet.",
1060
+ SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "A subscription contract states a tax rate of {taxRate} at {field}, which is not a percentage: a rate lies from 0 to 100, and a value between 0 and 1 is refused as a fraction.",
1047
1061
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil must be after the effectiveFrom of the contract.",
1048
1062
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "A line item must be booked in the currency its contract was priced in.",
1049
1063
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' not found",
1050
1064
  CHECKOUT_OFFER_EXPIRED: "Checkout offer '{offerId}' has expired and cannot be {action}",
1051
1065
  CHECKOUT_OFFER_ALREADY_CONSUMED: "Checkout offer '{offerId}' has already been consumed and cannot be {action}",
1052
1066
  CHECKOUT_OFFER_NOT_CONSUMED: "CheckoutOffer '{offerId}' must be consumed before the contract is created",
1067
+ CHECKOUT_OFFER_CHANGED: "Checkout offer '{offerId}' changed while it was being concluded. Load it again.",
1053
1068
  SUBSCRIPTION_CONTRACT_NOT_FOUND: "SubscriptionContract '{contractId}' not found",
1054
1069
  NO_ACTIVE_SUBSCRIPTION_CONTRACT: "No active subscription contract for tenant {tenantId}",
1055
1070
  SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SubscriptionContract '{contractId}' is already closed",
@@ -1179,6 +1194,7 @@ var ERROR_MESSAGES_DE = {
1179
1194
  NO_ACTIVE_PLAN_VERSION: "Zum {asOf} ist keine Planversion f\xFCr {planId} aktiv \u2014 weder ist das validFrom-Fenster erf\xFCllt, noch steht eine j\xFCngste Live-Version bereit.",
1180
1195
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" steht nicht im Katalog',
1181
1196
  PLAN_NOT_SELF_SERVICE: "{planName} wird nur \xFCber einen Sondervertrag freigeschaltet. Bitte wenden Sie sich an die Vertragsverwaltung.",
1197
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} hat f\xFCr diesen Abrechnungsrhythmus keinen Preis und kann darin nicht gebucht werden.",
1182
1198
  PLAN_CHANGE_BLOCKED: "W\xE4hrend des Onboardings ist ein Planwechsel gesperrt.",
1183
1199
  SUBSCRIPTION_CHANGED: "Dieses Abonnement hat sich ge\xE4ndert, w\xE4hrend die Anfrage entschieden wurde. Bitte lade es neu.",
1184
1200
  NO_SUBSCRIPTION: "Dieser Mandant hat kein Abonnement, das gek\xFCndigt werden kann.",
@@ -1231,12 +1247,14 @@ var ERROR_MESSAGES_DE = {
1231
1247
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} muss ein g\xFCltiges Datum sein.",
1232
1248
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil muss nach effectiveFrom liegen.",
1233
1249
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "taxAmount einer Position muss exakt priceGross minus priceNet sein.",
1250
+ SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "Ein Abo-Vertrag nennt bei {field} einen Steuersatz von {taxRate}, der kein Prozentsatz ist: Ein Satz liegt zwischen 0 und 100, und ein Wert zwischen 0 und 1 wird als Bruch abgelehnt.",
1234
1251
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "Eine Position muss in der W\xE4hrung gebucht sein, in der ihr Vertrag bepreist ist.",
1235
1252
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil muss nach dem effectiveFrom des Vertrags liegen.",
1236
1253
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' nicht gefunden",
1237
1254
  CHECKOUT_OFFER_EXPIRED: "Checkout-Angebot '{offerId}' ist abgelaufen \u2014 '{action}' ist nicht mehr m\xF6glich",
1238
1255
  CHECKOUT_OFFER_ALREADY_CONSUMED: "Checkout-Angebot '{offerId}' wurde bereits eingel\xF6st \u2014 '{action}' ist nicht mehr m\xF6glich",
1239
1256
  CHECKOUT_OFFER_NOT_CONSUMED: "CheckoutOffer '{offerId}' muss eingel\xF6st sein, bevor der Vertrag entsteht",
1257
+ CHECKOUT_OFFER_CHANGED: "Checkout-Angebot '{offerId}' hat sich w\xE4hrend des Abschlusses ge\xE4ndert. Bitte neu laden.",
1240
1258
  SUBSCRIPTION_CONTRACT_NOT_FOUND: "SubscriptionContract '{contractId}' nicht gefunden",
1241
1259
  NO_ACTIVE_SUBSCRIPTION_CONTRACT: "Kein aktiver Abo-Vertrag f\xFCr Mandant {tenantId}",
1242
1260
  SUBSCRIPTION_CONTRACT_ALREADY_CLOSED: "SubscriptionContract '{contractId}' ist bereits geschlossen",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasicat/core",
3
- "version": "1.0.0-rc.13",
3
+ "version": "1.0.0-rc.15",
4
4
  "description": "TypeScript contracts for SaaSiCat — capabilities, catalogs, subscriptions, entitlements, admin manifests, persistence ports — and the pure domain logic both server and browser run: error catalogues, plan-diff classification, promo arithmetic, feature requirements.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",