sinfactura-types 1.1.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -65,44 +65,123 @@ declare global {
65
65
  /** Resolved entitlements for a specific tenant (matrix + overrides applied). */
66
66
  type ResolvedEntitlements = Record<FeatureKey, Entitlement>;
67
67
  /**
68
- * A sellable plan in the catalog. Seeded into DynamoDB by api#626 and
69
- * served to the frontend via GET /subscription/plans.
68
+ * Implementation status of a feature on a plan row. Informational
69
+ * gating still happens via `enabled` / `limit`. `'service'` rows
70
+ * (e.g. prioritySupport) are human-delivered but still gated like
71
+ * booleans.
72
+ */
73
+ type PlanFeatureStatus = 'live' | 'planned' | 'future' | 'service';
74
+ /**
75
+ * A single feature row in a Plan's `features[]` array. Matches the
76
+ * BE wire format from `GET /subscription/plans` exactly — boolean
77
+ * features carry `enabled`, numeric/metered carry `limit` (-1 =
78
+ * unlimited).
79
+ */
80
+ interface PlanFeature {
81
+ key: FeatureKey;
82
+ type: EntitlementType;
83
+ status: PlanFeatureStatus;
84
+ /** User-facing Spanish description. */
85
+ description: string;
86
+ /** Set on `boolean` features; `null` on numeric/metered. */
87
+ enabled: boolean | null;
88
+ /** Set on `numeric`/`metered` features; `null` on boolean. -1 = unlimited. */
89
+ limit: number | null;
90
+ }
91
+ /**
92
+ * A sellable plan in the catalog. Aligned with the BE wire format from
93
+ * `GET /subscription/plans` (api#859). Source of truth lives in
94
+ * DynamoDB (`PLAN#{tier}` partition), administered via `POST /sa/plans`
95
+ * + `PATCH /sa/plans/{tier}` (api#859).
70
96
  *
71
- * Prices are integers in ARS cents (e.g. $40 000 = 4_000_000).
72
- * `null` = "Contactar ventas" (AVANZADO, pre-unlock).
97
+ * Prices are integers in the `currency` smallest unit (centavos for ARS,
98
+ * cents for USD). `null` = "Contactar ventas" (AVANZADO annual at launch
99
+ * is sales-led; basico/fundador are free) per spec §6.4.
100
+ *
101
+ * **Breaking change vs 1.1.x:** the catalog Plan interface was reshaped
102
+ * to match the BE wire format. Renames & removals:
103
+ * - `label` → `name`
104
+ * - `blurb` → `description`
105
+ * - `priceMonthly` → `priceMonthlyCents` (number | null)
106
+ * - `priceAnnual` → `priceAnnualCents` (number | null)
107
+ * - `currency` widened to `'ARS' | 'USD' | null`
108
+ * - `entitlements: Record<FeatureKey, Entitlement>` → `features: PlanFeature[]`
109
+ * - `isPublic` removed (use `isActive` for visibility gating)
110
+ * - `stripeMonthlyPriceId` / `stripeAnnualPriceId` /
111
+ * `mpPreApprovalPlanIdMonthly` / `mpPreApprovalPlanIdAnnual` removed
112
+ * (BE-internal — not on the public wire format)
113
+ * - `createdAt` / `updatedAt` removed (not on the public wire format)
114
+ *
115
+ * Added per api#859:
116
+ * - `displayOrder` (number)
117
+ * - `color` (single hex string, FE derives `soft`/`border` shades)
118
+ * - `isPopular` (now required, was optional)
73
119
  */
74
120
  interface Plan {
75
121
  tier: PlanTier;
76
- /** Display label "BÁSICO", "EMPRENDEDOR", "PROFESIONAL", "AVANZADO", "FUNDADOR". */
77
- label: string;
78
- /** Short marketing tagline (Spanish). */
79
- blurb?: string;
80
- /** Price in ARS cents for monthly billing. `null` = contactar. */
81
- priceMonthly: number | null;
82
- /** Price in ARS cents for annual billing (total / 12; ~20% discount). `null` = contactar. */
83
- priceAnnual: number | null;
84
- /** 'ARS' at launch. Reserved for future multi-currency. */
85
- currency: 'ARS';
122
+ /** Display name in Spanish (e.g. "Profesional"). */
123
+ name: string;
124
+ /** Short marketing one-liner in Spanish. */
125
+ description: string;
86
126
  /**
87
- * Whether the plan is accepting new subscribers. Closed-cohort plans
88
- * (e.g. Founders after 2026-05-31) set this to `false` without deleting
89
- * the plan row.
127
+ * Whether the plan is shown on the pricing page and accepting new
128
+ * subscribers. Closed-cohort plans (e.g. Founders after the cutoff)
129
+ * and pre-launch tiers (e.g. AVANZADO until ≥2 Planned features ship)
130
+ * set this to `false` without deleting the plan row.
90
131
  */
91
132
  isActive: boolean;
92
- /** Marked as the anchor / recommended tier on the pricing page. PROFESIONAL at launch. */
93
- isPopular?: boolean;
94
133
  /**
95
- * Visibility on the public pricing page. AVANZADO = `false` until
96
- * ≥2 Planned features ship (per SUBSCRIPTION_TIERS_BEST_PRACTICES §0).
134
+ * Anchor / recommended tier on the pricing page. The FE typically
135
+ * renders the "Más elegido" pill on the plan(s) flagged here. The
136
+ * BE does not enforce uniqueness — admins can flag any number of
137
+ * plans, but the canonical convention is exactly one.
138
+ */
139
+ isPopular: boolean;
140
+ /** Sort order on the pricing page (ascending). Ties allowed. */
141
+ displayOrder: number;
142
+ /** Monthly price in `currency` smallest units. `null` = sales-led / free. */
143
+ priceMonthlyCents: number | null;
144
+ /** Annual price in `currency` smallest units. `null` = sales-led / free. */
145
+ priceAnnualCents: number | null;
146
+ /**
147
+ * Currency the prices are denominated in. `'ARS'` at launch; `'USD'`
148
+ * is the migration target (api#841). `null` on free / off-billing
149
+ * tiers (basico, fundador).
150
+ */
151
+ currency: 'ARS' | 'USD' | null;
152
+ /**
153
+ * Single brand hex color (e.g. '#590d82'). The FE derives `soft`
154
+ * (light tint) and `border` shades via MUI's `alpha()` helper.
155
+ * `null` on plans created/seeded before the api#859 backfill.
97
156
  */
98
- isPublic: boolean;
99
- /** Stripe Price IDs once the plan is wired to Stripe Products (api#627). */
100
- stripeMonthlyPriceId?: string;
101
- stripeAnnualPriceId?: string;
102
- /** Per-tier entitlement configuration. */
103
- entitlements: Record<FeatureKey, Entitlement>;
157
+ color: string | null;
158
+ /**
159
+ * Per-feature configuration. Every `FeatureKey` appears exactly once.
160
+ * Boolean features carry `enabled`; numeric/metered carry `limit`.
161
+ */
162
+ features: PlanFeature[];
163
+ }
164
+ /**
165
+ * One audit row per SUPER_ADMIN-driven plan mutation. Returned by
166
+ * `GET /sa/plans/{tier}/audit` (api#859). The same row shape is used
167
+ * for the sibling `STORE` audit partition (api#827).
168
+ *
169
+ * `before` and `after` carry only the fields that changed (diff slice),
170
+ * not the full row blob.
171
+ */
172
+ interface PlanAuditEntry {
173
+ entity: 'PLAN';
174
+ entityId: string;
175
+ timestamp: number;
176
+ actor: {
177
+ userId: string;
178
+ fullName: string;
179
+ };
180
+ action: string;
181
+ before: Record<string, unknown>;
182
+ after: Record<string, unknown>;
183
+ reason: string;
104
184
  createdAt: number;
105
- updatedAt: number;
106
185
  }
107
186
  /**
108
187
  * A tenant's current subscription row. One per `tenantId`.
@@ -113,6 +192,15 @@ declare global {
113
192
  planTier: PlanTier;
114
193
  status: SubscriptionStatus;
115
194
  billingCycle: BillingCycle;
195
+ /**
196
+ * Currency this subscription was billed in at checkout time.
197
+ * Snapshotted from the Plan's `currency` field so it survives
198
+ * platform-wide currency switches (api#841 / api#842) — a customer
199
+ * who signed up on ARS keeps being charged in ARS even after the
200
+ * platform flips to USD for new signups. Required for accurate
201
+ * historical reporting + AFIP invoices.
202
+ */
203
+ currency?: 'ARS' | 'USD';
116
204
  /** Current period window (Unix ms). */
117
205
  currentPeriodStart: number;
118
206
  currentPeriodEnd: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sinfactura-types",
3
- "version": "1.1.1",
3
+ "version": "1.2.0",
4
4
  "main": "dist/index.js",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",