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

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
+ ffbbf3de488fa65a264bc00b14097f9d84d9da3428b766d0c2c6b74ccac9b249
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 ──
@@ -1081,6 +1087,7 @@ var ERROR_MESSAGES_EN = {
1081
1087
  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
1088
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" is not in the catalog',
1083
1089
  PLAN_NOT_SELF_SERVICE: "{planName} is only activated via a special contract. Please contact the contract manager.",
1090
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} has no price for this billing rhythm and cannot be booked in it.",
1084
1091
  PLAN_CHANGE_BLOCKED: "Plan change during onboarding is blocked.",
1085
1092
  SUBSCRIPTION_CHANGED: "This subscription changed while the request was being decided. Reload it.",
1086
1093
  NO_SUBSCRIPTION: "This tenant has no subscription to cancel.",
@@ -1133,6 +1140,7 @@ var ERROR_MESSAGES_EN = {
1133
1140
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} must be a valid date.",
1134
1141
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil must be after effectiveFrom.",
1135
1142
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "A line item's taxAmount must be exactly priceGross minus priceNet.",
1143
+ 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
1144
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil must be after the effectiveFrom of the contract.",
1137
1145
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "A line item must be booked in the currency its contract was priced in.",
1138
1146
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' not found",
@@ -1268,6 +1276,7 @@ var ERROR_MESSAGES_DE = {
1268
1276
  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
1277
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" steht nicht im Katalog',
1270
1278
  PLAN_NOT_SELF_SERVICE: "{planName} wird nur \xFCber einen Sondervertrag freigeschaltet. Bitte wenden Sie sich an die Vertragsverwaltung.",
1279
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} hat f\xFCr diesen Abrechnungsrhythmus keinen Preis und kann darin nicht gebucht werden.",
1271
1280
  PLAN_CHANGE_BLOCKED: "W\xE4hrend des Onboardings ist ein Planwechsel gesperrt.",
1272
1281
  SUBSCRIPTION_CHANGED: "Dieses Abonnement hat sich ge\xE4ndert, w\xE4hrend die Anfrage entschieden wurde. Bitte lade es neu.",
1273
1282
  NO_SUBSCRIPTION: "Dieser Mandant hat kein Abonnement, das gek\xFCndigt werden kann.",
@@ -1320,6 +1329,7 @@ var ERROR_MESSAGES_DE = {
1320
1329
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} muss ein g\xFCltiges Datum sein.",
1321
1330
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil muss nach effectiveFrom liegen.",
1322
1331
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "taxAmount einer Position muss exakt priceGross minus priceNet sein.",
1332
+ 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
1333
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "Eine Position muss in der W\xE4hrung gebucht sein, in der ihr Vertrag bepreist ist.",
1324
1334
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil muss nach dem effectiveFrom des Vertrags liegen.",
1325
1335
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' nicht gefunden",
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;
@@ -3404,6 +3401,8 @@ interface PlanVersionRepository {
3404
3401
  *
3405
3402
  * Note: ignores `validFrom`/`validUntil`. For time-aware
3406
3403
  * resolution (onboarding, plan fallback for TRIAL) use `findActive`.
3404
+ *
3405
+ * A plan key no plan has finds `null`, not an error.
3407
3406
  */
3408
3407
  findLatestLive(planId: string, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3409
3408
  /**
@@ -3416,6 +3415,8 @@ interface PlanVersionRepository {
3416
3415
  * return the highest `validFrom`, explicitly ordering null start dates
3417
3416
  * last as a legacy fallback. Adapters without validity columns may omit
3418
3417
  * the method (consumers fall back to `findLatestLive`).
3418
+ *
3419
+ * A plan key no plan has finds `null`, not an error.
3419
3420
  */
3420
3421
  findActive?(planId: string, asOf?: Date, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3421
3422
  }
@@ -4351,6 +4352,11 @@ declare const BILLING_ERROR_CODES: {
4351
4352
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4352
4353
  /** Plan exists but cannot be booked via self-service. */
4353
4354
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4355
+ /**
4356
+ * Plan carries no price for the requested billing cycle, so it is not sold
4357
+ * in it: a plan without a yearly price is a monthly plan.
4358
+ */
4359
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4354
4360
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4355
4361
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4356
4362
  /**
@@ -4448,6 +4454,7 @@ declare const CONTRACT_ERROR_CODES: {
4448
4454
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4449
4455
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4450
4456
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4457
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4451
4458
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4452
4459
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4453
4460
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
@@ -4562,6 +4569,7 @@ declare const PLATFORM_ERROR_CODES: {
4562
4569
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4563
4570
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4564
4571
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4572
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4565
4573
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4566
4574
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4567
4575
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
@@ -4595,6 +4603,11 @@ declare const PLATFORM_ERROR_CODES: {
4595
4603
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4596
4604
  /** Plan exists but cannot be booked via self-service. */
4597
4605
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4606
+ /**
4607
+ * Plan carries no price for the requested billing cycle, so it is not sold
4608
+ * in it: a plan without a yearly price is a monthly plan.
4609
+ */
4610
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4598
4611
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4599
4612
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4600
4613
  /**
@@ -5186,8 +5199,6 @@ interface ConfiguratorModel {
5186
5199
  popular?: boolean;
5187
5200
  }
5188
5201
  interface ConfiguratorCatalog {
5189
- /** Factor `yearlyNet = monthlyNet * cycleDiscount` (typically 10 = 2 months free). */
5190
- cycleDiscount: number;
5191
5202
  currency: string;
5192
5203
  vatRate: number;
5193
5204
  models: ConfiguratorModel[];
@@ -5212,6 +5223,10 @@ interface ConfiguratorPriceBreakdown {
5212
5223
  modelMonthlyNet: number;
5213
5224
  subtotalMonthlyNet: number;
5214
5225
  subtotalNet: number;
5226
+ /**
5227
+ * Net amount taken off `subtotalNet`: the promo preview's gross discount
5228
+ * converted at `vatRate`.
5229
+ */
5215
5230
  discountAmount: number;
5216
5231
  totalNet: number;
5217
5232
  vatRate: number;
@@ -5288,8 +5303,6 @@ interface ConfiguratorPlanMarketing {
5288
5303
  */
5289
5304
  interface ConfiguratorMarketingProvider {
5290
5305
  listPlanMarketing(): ConfiguratorPlanMarketing[];
5291
- /** Factor `yearlyNet = monthlyNet * cycleDiscount`. Default `10`. */
5292
- getCycleDiscount(): number;
5293
5306
  getVatRate(): number;
5294
5307
  getCurrency(): string;
5295
5308
  }
@@ -5310,6 +5323,7 @@ interface RegistrationPromoPreview {
5310
5323
  reason?: string;
5311
5324
  percent?: number;
5312
5325
  label?: string;
5326
+ /** The discount in gross, reckoned against `subtotalGross`. */
5313
5327
  discountAmount?: number;
5314
5328
  }>;
5315
5329
  }
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;
@@ -3404,6 +3401,8 @@ interface PlanVersionRepository {
3404
3401
  *
3405
3402
  * Note: ignores `validFrom`/`validUntil`. For time-aware
3406
3403
  * resolution (onboarding, plan fallback for TRIAL) use `findActive`.
3404
+ *
3405
+ * A plan key no plan has finds `null`, not an error.
3407
3406
  */
3408
3407
  findLatestLive(planId: string, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3409
3408
  /**
@@ -3416,6 +3415,8 @@ interface PlanVersionRepository {
3416
3415
  * return the highest `validFrom`, explicitly ordering null start dates
3417
3416
  * last as a legacy fallback. Adapters without validity columns may omit
3418
3417
  * the method (consumers fall back to `findLatestLive`).
3418
+ *
3419
+ * A plan key no plan has finds `null`, not an error.
3419
3420
  */
3420
3421
  findActive?(planId: string, asOf?: Date, tx?: TransactionContext): Promise<PlanVersionRecord | null>;
3421
3422
  }
@@ -4351,6 +4352,11 @@ declare const BILLING_ERROR_CODES: {
4351
4352
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4352
4353
  /** Plan exists but cannot be booked via self-service. */
4353
4354
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4355
+ /**
4356
+ * Plan carries no price for the requested billing cycle, so it is not sold
4357
+ * in it: a plan without a yearly price is a monthly plan.
4358
+ */
4359
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4354
4360
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4355
4361
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4356
4362
  /**
@@ -4448,6 +4454,7 @@ declare const CONTRACT_ERROR_CODES: {
4448
4454
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4449
4455
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4450
4456
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4457
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4451
4458
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4452
4459
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4453
4460
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
@@ -4562,6 +4569,7 @@ declare const PLATFORM_ERROR_CODES: {
4562
4569
  readonly SUBSCRIPTION_CONTRACT_INVALID_DATE: "SUBSCRIPTION_CONTRACT_INVALID_DATE";
4563
4570
  readonly SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "SUBSCRIPTION_CONTRACT_INVALID_WINDOW";
4564
4571
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH";
4572
+ readonly SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT: "SUBSCRIPTION_CONTRACT_TAX_RATE_NOT_PERCENT";
4565
4573
  readonly SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH";
4566
4574
  readonly SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START";
4567
4575
  readonly CHECKOUT_OFFER_NOT_FOUND: "CHECKOUT_OFFER_NOT_FOUND";
@@ -4595,6 +4603,11 @@ declare const PLATFORM_ERROR_CODES: {
4595
4603
  readonly PLAN_NOT_IN_CATALOG: "PLAN_NOT_IN_CATALOG";
4596
4604
  /** Plan exists but cannot be booked via self-service. */
4597
4605
  readonly PLAN_NOT_SELF_SERVICE: "PLAN_NOT_SELF_SERVICE";
4606
+ /**
4607
+ * Plan carries no price for the requested billing cycle, so it is not sold
4608
+ * in it: a plan without a yearly price is a monthly plan.
4609
+ */
4610
+ readonly PLAN_NOT_SOLD_IN_CYCLE: "PLAN_NOT_SOLD_IN_CYCLE";
4598
4611
  /** Plan change refused. Carries `blockers[]` with their own codes. */
4599
4612
  readonly PLAN_CHANGE_BLOCKED: "PLAN_CHANGE_BLOCKED";
4600
4613
  /**
@@ -5186,8 +5199,6 @@ interface ConfiguratorModel {
5186
5199
  popular?: boolean;
5187
5200
  }
5188
5201
  interface ConfiguratorCatalog {
5189
- /** Factor `yearlyNet = monthlyNet * cycleDiscount` (typically 10 = 2 months free). */
5190
- cycleDiscount: number;
5191
5202
  currency: string;
5192
5203
  vatRate: number;
5193
5204
  models: ConfiguratorModel[];
@@ -5212,6 +5223,10 @@ interface ConfiguratorPriceBreakdown {
5212
5223
  modelMonthlyNet: number;
5213
5224
  subtotalMonthlyNet: number;
5214
5225
  subtotalNet: number;
5226
+ /**
5227
+ * Net amount taken off `subtotalNet`: the promo preview's gross discount
5228
+ * converted at `vatRate`.
5229
+ */
5215
5230
  discountAmount: number;
5216
5231
  totalNet: number;
5217
5232
  vatRate: number;
@@ -5288,8 +5303,6 @@ interface ConfiguratorPlanMarketing {
5288
5303
  */
5289
5304
  interface ConfiguratorMarketingProvider {
5290
5305
  listPlanMarketing(): ConfiguratorPlanMarketing[];
5291
- /** Factor `yearlyNet = monthlyNet * cycleDiscount`. Default `10`. */
5292
- getCycleDiscount(): number;
5293
5306
  getVatRate(): number;
5294
5307
  getCurrency(): string;
5295
5308
  }
@@ -5310,6 +5323,7 @@ interface RegistrationPromoPreview {
5310
5323
  reason?: string;
5311
5324
  percent?: number;
5312
5325
  label?: string;
5326
+ /** The discount in gross, reckoned against `subtotalGross`. */
5313
5327
  discountAmount?: number;
5314
5328
  }>;
5315
5329
  }
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 ──
@@ -992,6 +998,7 @@ var ERROR_MESSAGES_EN = {
992
998
  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
999
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" is not in the catalog',
994
1000
  PLAN_NOT_SELF_SERVICE: "{planName} is only activated via a special contract. Please contact the contract manager.",
1001
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} has no price for this billing rhythm and cannot be booked in it.",
995
1002
  PLAN_CHANGE_BLOCKED: "Plan change during onboarding is blocked.",
996
1003
  SUBSCRIPTION_CHANGED: "This subscription changed while the request was being decided. Reload it.",
997
1004
  NO_SUBSCRIPTION: "This tenant has no subscription to cancel.",
@@ -1044,6 +1051,7 @@ var ERROR_MESSAGES_EN = {
1044
1051
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} must be a valid date.",
1045
1052
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil must be after effectiveFrom.",
1046
1053
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "A line item's taxAmount must be exactly priceGross minus priceNet.",
1054
+ 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
1055
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil must be after the effectiveFrom of the contract.",
1048
1056
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "A line item must be booked in the currency its contract was priced in.",
1049
1057
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' not found",
@@ -1179,6 +1187,7 @@ var ERROR_MESSAGES_DE = {
1179
1187
  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
1188
  PLAN_NOT_IN_CATALOG: 'Plan "{planKey}" steht nicht im Katalog',
1181
1189
  PLAN_NOT_SELF_SERVICE: "{planName} wird nur \xFCber einen Sondervertrag freigeschaltet. Bitte wenden Sie sich an die Vertragsverwaltung.",
1190
+ PLAN_NOT_SOLD_IN_CYCLE: "{planName} hat f\xFCr diesen Abrechnungsrhythmus keinen Preis und kann darin nicht gebucht werden.",
1182
1191
  PLAN_CHANGE_BLOCKED: "W\xE4hrend des Onboardings ist ein Planwechsel gesperrt.",
1183
1192
  SUBSCRIPTION_CHANGED: "Dieses Abonnement hat sich ge\xE4ndert, w\xE4hrend die Anfrage entschieden wurde. Bitte lade es neu.",
1184
1193
  NO_SUBSCRIPTION: "Dieser Mandant hat kein Abonnement, das gek\xFCndigt werden kann.",
@@ -1231,6 +1240,7 @@ var ERROR_MESSAGES_DE = {
1231
1240
  SUBSCRIPTION_CONTRACT_INVALID_DATE: "{field} muss ein g\xFCltiges Datum sein.",
1232
1241
  SUBSCRIPTION_CONTRACT_INVALID_WINDOW: "effectiveUntil muss nach effectiveFrom liegen.",
1233
1242
  SUBSCRIPTION_CONTRACT_LINE_ITEM_TAX_MISMATCH: "taxAmount einer Position muss exakt priceGross minus priceNet sein.",
1243
+ 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
1244
  SUBSCRIPTION_CONTRACT_LINE_ITEM_CURRENCY_MISMATCH: "Eine Position muss in der W\xE4hrung gebucht sein, in der ihr Vertrag bepreist ist.",
1235
1245
  SUBSCRIPTION_CONTRACT_TERMINATION_BEFORE_START: "effectiveUntil muss nach dem effectiveFrom des Vertrags liegen.",
1236
1246
  CHECKOUT_OFFER_NOT_FOUND: "CheckoutOffer '{offerId}' nicht gefunden",
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.14",
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",