platform-types 1.10.733 → 1.10.735

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.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.INVOICE_DOCUMENT_REFUSAL_CODES = exports.INVOICE_ISSUANCE_REFUSAL_RECOVERY = exports.INVOICE_ISSUANCE_REFUSAL_CODES = exports.INVOICE_POST_VOUCHER_ERROR_CODES = void 0;
3
+ exports.INVOICE_NOT_AUTHORIZED_RECOVERIES = exports.INVOICE_DOCUMENT_REFUSAL_CODES = exports.INVOICE_ISSUANCE_REFUSAL_RECOVERY = exports.INVOICE_ISSUANCE_REFUSAL_CODES = exports.INVOICE_POST_VOUCHER_ERROR_CODES = void 0;
4
4
  /**
5
5
  * The codes `POST /invoices` answers AFTER a voucher number was spent — ARCA
6
6
  * was asked, or a CAEA number was claimed — as a runtime value the app maps.
@@ -87,13 +87,33 @@ exports.INVOICE_ISSUANCE_REFUSAL_RECOVERY = {
87
87
  * store. Answered before any store read, render, print stamp, broadcast,
88
88
  * staged PDF, print job or email.
89
89
  * - `INVOICE_NOT_AUTHORIZED` · 409 — the row's `fiscalStatus` is `pending_cae`
90
- * or `rejected`. `invoiceId` and that `fiscalStatus` ride beside `error`. A
90
+ * or `rejected`. `invoiceId`, that `fiscalStatus` and a `recovery`
91
+ * (`INVOICE_NOT_AUTHORIZED_RECOVERIES`) ride beside `error`. A
91
92
  * print refused here wrote nothing to the invoice and dispatched nothing.
92
- * `pending_cae` is wait-and-retry: the pending-invoice drain authorizes a
93
- * factura or full credit note with no client action (an FCE or partial
94
- * credit note waits for support). `rejected` is final — ARCA refused the
95
- * voucher and nothing retries it. The decision reads `fiscalStatus` only: a row with none (issued
96
- * before the field existed) is treated as authorized, whether or not it
97
- * carries a CAE.
93
+ * What the client tells the operator is `recovery`'s to say, never
94
+ * re-derived here: a `pending_cae` row answers `retry` or `support` (an FCE
95
+ * voucher, a partial credit note, or a submit whose recorded voucher triple
96
+ * is incomplete), and a `rejected` row answers `final`. Whether to refuse at
97
+ * all reads `fiscalStatus` only: a row with none (issued before the field
98
+ * existed) is treated as authorized, whether or not it carries a CAE.
98
99
  */
99
100
  exports.INVOICE_DOCUMENT_REFUSAL_CODES = ['INVOICE_NOT_FOUND', 'INVOICE_NOT_AUTHORIZED'];
101
+ /**
102
+ * What a client should tell the operator about a voucher the document routes
103
+ * refused with `INVOICE_NOT_AUTHORIZED` — the `recovery` beside `error` in that
104
+ * 409 body, decided server-side from the INVOICE row so a client never re-derives
105
+ * the drain's rules:
106
+ *
107
+ * - `retry` — `pending_cae`, and nothing on the row says the pending-invoice
108
+ * drain will hold it: try again later. The drain may still hold such a row
109
+ * when its order changed underneath it — that is decided against the order,
110
+ * not the row, and is not answered here.
111
+ * - `support` — `pending_cae`, and the row itself shows the drain will hold it
112
+ * rather than settle it: an FCE voucher (skipped), a PARTIAL credit note (its
113
+ * row carries `partialItems`, and the drain's full-order rebuild never
114
+ * reproduces it), or a submit whose recorded voucher triple is incomplete.
115
+ * Contact support. A return's own partial credit note can still be settled by
116
+ * that return's credit-note retry.
117
+ * - `final` — `rejected`. ARCA refused the voucher and nothing retries it.
118
+ */
119
+ exports.INVOICE_NOT_AUTHORIZED_RECOVERIES = ['retry', 'support', 'final'];
package/dist/cjs/store.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.STORE_MODULE_ERROR_CODES = exports.isStoreModuleOn = exports.isStoreModuleKey = exports.STORE_MODULE_KEYS = exports.CUSTOM_DOMAIN_ROW_LIMIT = exports.CUSTOM_DOMAIN_SLOT_LIMIT = void 0;
3
+ exports.STORE_PATCH_ERROR_CODES = exports.STORE_MODULE_ERROR_CODES = exports.isStoreModuleOn = exports.isStoreModuleKey = exports.STORE_MODULE_KEYS = exports.CUSTOM_DOMAIN_ROW_LIMIT = exports.CUSTOM_DOMAIN_SLOT_LIMIT = void 0;
4
4
  /**
5
5
  * How many custom-domain rows a store may hold that count: one active domain
6
6
  * and one spare, so a replacement can be verified and provisioned before the
@@ -52,9 +52,11 @@ exports.isStoreModuleKey = isStoreModuleKey;
52
52
  const isStoreModuleOn = (modules, module) => modules?.[module] !== false;
53
53
  exports.isStoreModuleOn = isStoreModuleOn;
54
54
  /**
55
- * Every refusal code the store-module switches put on the wire, beyond the
56
- * platform-wide set (`VALIDATION_FAILED`, and `STORE_CONFIG_FORBIDDEN` for a
57
- * plain USER writing the privileged leaf):
55
+ * Every refusal code the store-module switches put on the wire. Not listed
56
+ * here: `VALIDATION_FAILED` (a malformed body), and `STORE_CONFIG_FORBIDDEN` —
57
+ * the `PATCH /store` role refusal a plain USER gets for ANY privileged `config`
58
+ * leaf, `modules` included, judged before this module code runs. That one is in
59
+ * {@link STORE_PATCH_ERROR_CODES}:
58
60
  *
59
61
  * - `STORE_MODULE_UNKNOWN` (400) — `PATCH /store` named a key outside
60
62
  * `STORE_MODULE_KEYS`; `data.module` carries it.
@@ -91,3 +93,69 @@ exports.STORE_MODULE_ERROR_CODES = [
91
93
  'STORE_MODULE_SHIFTS_OPEN',
92
94
  'STORE_MODULE_DISABLED',
93
95
  ];
96
+ /**
97
+ * The bare refusal codes `PATCH /store` (`stacks/lambdas/store/_patch.ts`, and
98
+ * the route's own sibling modules it calls) answers itself — `{ error, message }`
99
+ * with no `data`, unless noted:
100
+ *
101
+ * Role refusals, `403` — a plain USER writing a privileged section. Each is
102
+ * judged before any other check on that section:
103
+ * - `STORE_CONFIG_FORBIDDEN` — any privileged `config` leaf (`modules`,
104
+ * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, …).
105
+ * - `STORE_ECOMMERCE_FORBIDDEN` — any `ecommerce` block.
106
+ * - `STORE_FISCAL_FIELD_FORBIDDEN` — a fiscal field of the store.
107
+ * - `STORE_JURISDICTION_FORBIDDEN` — `jurisdiction`.
108
+ * - `AFIP_RELACION_FLAG_FORBIDDEN` — `afip.wscdcEnabled` / `afip.fceEnabled`.
109
+ * - `FX_TABLE_FORBIDDEN` — the `currencies` exchange-rate table.
110
+ * - `FX_AUTO_UPDATE_FORBIDDEN` — `fxAutoUpdate`.
111
+ *
112
+ * Request refusals, `400`:
113
+ * - `FIELD_NOT_WRITABLE_HERE` — a field written through `POST /store`, not
114
+ * this route; a top-level `fields` array names them.
115
+ * - `AFIP_POINT_OF_SALE_NOT_CLEARABLE` — `null` sent for the punto de venta.
116
+ * - `CURRENCY_CATALOG_ID_UNKNOWN` — a `currencies` entry names an unknown or
117
+ * disabled catalog id.
118
+ * - `ARS_RATE_MUST_BE_ONE` — the peso entry's value is not exactly `1`.
119
+ * - `CURRENCY_RATE_ABOVE_CEILING` — a `currencies` value is above its ceiling.
120
+ * - `FX_CATALOG_ID_UNKNOWN` / `FX_SOURCE_ID_UNKNOWN` — an `fxAutoUpdate`
121
+ * binding names an unknown catalog id or rate source.
122
+ * - `DISPLAY_CURRENCY_NOT_INVOICEABLE` / `PRODUCT_CURRENCY_NOT_INVOICEABLE` — a
123
+ * store carrying an AFIP CUIT set `config.displayCurrency` /
124
+ * `config.defaultProductCurrency` to a non-oficial USD variant.
125
+ * - `PRICE_LIST_NOT_FOUND` — an `ecommerce` save names a
126
+ * `newCustomerPriceList` that does not exist (`store/_ecommercePatch.ts`).
127
+ *
128
+ * Maintenance mode (`{ mode: 'maintenance', level }`, `store/_patchMaintenance.ts`):
129
+ * - `FORBIDDEN` (403) — the caller is neither an ADMIN nor a MANAGER.
130
+ * - `STORE_NOT_FOUND` (404) — the store row is gone.
131
+ *
132
+ * ⚠️ Not repeated here because another tuple already publishes them:
133
+ * `VALIDATION_FAILED`, `PRODUCT_CURRENCY_NOT_SUBSCRIBED`
134
+ * (`PRODUCT_POST_ERROR_CODES`), `WAREHOUSE_NOT_FOUND` /
135
+ * `WAREHOUSE_INACTIVE` (a `channelWarehouses` target), and every
136
+ * `STORE_MODULE_*` code ({@link STORE_MODULE_ERROR_CODES}). Codes the route
137
+ * forwards from shared helpers (the write conflict, the CAEA punto-de-venta
138
+ * checks, the display-currency lock, the storefront fence) are not listed
139
+ * either. Cross-cutting refusals: check `GATING_ERROR_CODES` as well.
140
+ */
141
+ exports.STORE_PATCH_ERROR_CODES = [
142
+ 'STORE_CONFIG_FORBIDDEN',
143
+ 'STORE_ECOMMERCE_FORBIDDEN',
144
+ 'STORE_FISCAL_FIELD_FORBIDDEN',
145
+ 'STORE_JURISDICTION_FORBIDDEN',
146
+ 'AFIP_RELACION_FLAG_FORBIDDEN',
147
+ 'FX_TABLE_FORBIDDEN',
148
+ 'FX_AUTO_UPDATE_FORBIDDEN',
149
+ 'FIELD_NOT_WRITABLE_HERE',
150
+ 'AFIP_POINT_OF_SALE_NOT_CLEARABLE',
151
+ 'CURRENCY_CATALOG_ID_UNKNOWN',
152
+ 'ARS_RATE_MUST_BE_ONE',
153
+ 'CURRENCY_RATE_ABOVE_CEILING',
154
+ 'FX_CATALOG_ID_UNKNOWN',
155
+ 'FX_SOURCE_ID_UNKNOWN',
156
+ 'DISPLAY_CURRENCY_NOT_INVOICEABLE',
157
+ 'PRODUCT_CURRENCY_NOT_INVOICEABLE',
158
+ 'PRICE_LIST_NOT_FOUND',
159
+ 'FORBIDDEN',
160
+ 'STORE_NOT_FOUND',
161
+ ];
@@ -30,7 +30,7 @@
30
30
  * enabled:false until they do.
31
31
  */
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.FEATURE_KEYS = exports.isPaidPlanTier = exports.PLAN_CATALOGUE_ERROR_CODES = exports.PLAN_CATALOGUE_TEMPLATE_FIELDS = exports.PAID_PLAN_TIERS = exports.PLAN_TIERS = void 0;
33
+ exports.FEATURE_KEYS = exports.isPaidPlanTier = exports.PLAN_PRICE_SYNC_ERROR_CODES = exports.PLAN_CATALOGUE_ERROR_CODES = exports.PLAN_CATALOGUE_TEMPLATE_FIELDS = exports.PAID_PLAN_TIERS = exports.PLAN_TIERS = void 0;
34
34
  /*
35
35
  * Runtime tuples for the unions above.
36
36
  *
@@ -79,6 +79,10 @@ exports.PLAN_CATALOGUE_ERROR_CODES = exhaustiveTuple()([
79
79
  'PLAN_CATALOGUE_DRAFT_STALE',
80
80
  'IDEMPOTENCY_CONFLICT',
81
81
  ]);
82
+ /** Every `error` code `POST /subscription/checkout` adds for a price the provider has not caught up with. */
83
+ exports.PLAN_PRICE_SYNC_ERROR_CODES = exhaustiveTuple()([
84
+ 'PLAN_STRIPE_PRICE_STALE',
85
+ ]);
82
86
  /** Whether `value` names a paid tier — narrows an untyped read (a stored row, a global) to `PaidPlanTier`. */
83
87
  const isPaidPlanTier = (value) => typeof value === 'string' && exports.PAID_PLAN_TIERS.includes(value);
84
88
  exports.isPaidPlanTier = isPaidPlanTier;
package/dist/invoice.d.ts CHANGED
@@ -354,6 +354,26 @@ declare global {
354
354
  * Forward-only; nothing reads it as anything but a deadline.
355
355
  */
356
356
  drainClaimUntil?: number;
357
+ /**
358
+ * `true` while a REJECTED row's order still shows it unsettled — the
359
+ * rejection committed, but the order's `invoices[]` entry could not be set to
360
+ * `rejected` in the same write. Written in that same rejection write, only
361
+ * then; REMOVEd by the repair that sets the entry — tried at once, then by the
362
+ * `order-copy-repair` queue's consumer, and by a pending-invoice drain pass
363
+ * that reaches the store's page (AFIP not known down, its AFIP client up).
364
+ * ABSENT on every other row, and forward-only.
365
+ */
366
+ orderCopyStale?: true;
367
+ /**
368
+ * The rejecting caller's ORDER-trail origin, written beside `orderCopyStale`
369
+ * in the same write and REMOVEd with it, so whichever repairer reaches the
370
+ * row records the caller's settle rather than its own. Forward-only.
371
+ */
372
+ orderCopyStaleOrigin?: Extract<OrderAuditOrigin, 'platform' | 'operator' | 'worker'>;
373
+ /** The rejecting caller's settle reason — ids only, code-authored. Beside `orderCopyStale`, REMOVEd with it. */
374
+ orderCopyStaleReason?: string;
375
+ /** The rejecting caller's actor `userId` (`''` = the system actor). Beside `orderCopyStale`, REMOVEd with it. */
376
+ orderCopyStaleActorId?: string;
357
377
  invoicePrinted?: boolean;
358
378
  /**
359
379
  * Server-derived ms epoch, stamped by the WSS `ack` handler on an
@@ -1006,15 +1026,36 @@ export declare const INVOICE_ISSUANCE_REFUSAL_RECOVERY: {
1006
1026
  * store. Answered before any store read, render, print stamp, broadcast,
1007
1027
  * staged PDF, print job or email.
1008
1028
  * - `INVOICE_NOT_AUTHORIZED` · 409 — the row's `fiscalStatus` is `pending_cae`
1009
- * or `rejected`. `invoiceId` and that `fiscalStatus` ride beside `error`. A
1029
+ * or `rejected`. `invoiceId`, that `fiscalStatus` and a `recovery`
1030
+ * (`INVOICE_NOT_AUTHORIZED_RECOVERIES`) ride beside `error`. A
1010
1031
  * print refused here wrote nothing to the invoice and dispatched nothing.
1011
- * `pending_cae` is wait-and-retry: the pending-invoice drain authorizes a
1012
- * factura or full credit note with no client action (an FCE or partial
1013
- * credit note waits for support). `rejected` is final — ARCA refused the
1014
- * voucher and nothing retries it. The decision reads `fiscalStatus` only: a row with none (issued
1015
- * before the field existed) is treated as authorized, whether or not it
1016
- * carries a CAE.
1032
+ * What the client tells the operator is `recovery`'s to say, never
1033
+ * re-derived here: a `pending_cae` row answers `retry` or `support` (an FCE
1034
+ * voucher, a partial credit note, or a submit whose recorded voucher triple
1035
+ * is incomplete), and a `rejected` row answers `final`. Whether to refuse at
1036
+ * all reads `fiscalStatus` only: a row with none (issued before the field
1037
+ * existed) is treated as authorized, whether or not it carries a CAE.
1017
1038
  */
1018
1039
  export declare const INVOICE_DOCUMENT_REFUSAL_CODES: readonly ["INVOICE_NOT_FOUND", "INVOICE_NOT_AUTHORIZED"];
1019
1040
  export type InvoiceDocumentRefusalCode = (typeof INVOICE_DOCUMENT_REFUSAL_CODES)[number];
1041
+ /**
1042
+ * What a client should tell the operator about a voucher the document routes
1043
+ * refused with `INVOICE_NOT_AUTHORIZED` — the `recovery` beside `error` in that
1044
+ * 409 body, decided server-side from the INVOICE row so a client never re-derives
1045
+ * the drain's rules:
1046
+ *
1047
+ * - `retry` — `pending_cae`, and nothing on the row says the pending-invoice
1048
+ * drain will hold it: try again later. The drain may still hold such a row
1049
+ * when its order changed underneath it — that is decided against the order,
1050
+ * not the row, and is not answered here.
1051
+ * - `support` — `pending_cae`, and the row itself shows the drain will hold it
1052
+ * rather than settle it: an FCE voucher (skipped), a PARTIAL credit note (its
1053
+ * row carries `partialItems`, and the drain's full-order rebuild never
1054
+ * reproduces it), or a submit whose recorded voucher triple is incomplete.
1055
+ * Contact support. A return's own partial credit note can still be settled by
1056
+ * that return's credit-note retry.
1057
+ * - `final` — `rejected`. ARCA refused the voucher and nothing retries it.
1058
+ */
1059
+ export declare const INVOICE_NOT_AUTHORIZED_RECOVERIES: readonly ["retry", "support", "final"];
1060
+ export type InvoiceNotAuthorizedRecovery = (typeof INVOICE_NOT_AUTHORIZED_RECOVERIES)[number];
1020
1061
  export {};
package/dist/invoice.js CHANGED
@@ -84,13 +84,33 @@ export const INVOICE_ISSUANCE_REFUSAL_RECOVERY = {
84
84
  * store. Answered before any store read, render, print stamp, broadcast,
85
85
  * staged PDF, print job or email.
86
86
  * - `INVOICE_NOT_AUTHORIZED` · 409 — the row's `fiscalStatus` is `pending_cae`
87
- * or `rejected`. `invoiceId` and that `fiscalStatus` ride beside `error`. A
87
+ * or `rejected`. `invoiceId`, that `fiscalStatus` and a `recovery`
88
+ * (`INVOICE_NOT_AUTHORIZED_RECOVERIES`) ride beside `error`. A
88
89
  * print refused here wrote nothing to the invoice and dispatched nothing.
89
- * `pending_cae` is wait-and-retry: the pending-invoice drain authorizes a
90
- * factura or full credit note with no client action (an FCE or partial
91
- * credit note waits for support). `rejected` is final — ARCA refused the
92
- * voucher and nothing retries it. The decision reads `fiscalStatus` only: a row with none (issued
93
- * before the field existed) is treated as authorized, whether or not it
94
- * carries a CAE.
90
+ * What the client tells the operator is `recovery`'s to say, never
91
+ * re-derived here: a `pending_cae` row answers `retry` or `support` (an FCE
92
+ * voucher, a partial credit note, or a submit whose recorded voucher triple
93
+ * is incomplete), and a `rejected` row answers `final`. Whether to refuse at
94
+ * all reads `fiscalStatus` only: a row with none (issued before the field
95
+ * existed) is treated as authorized, whether or not it carries a CAE.
95
96
  */
96
97
  export const INVOICE_DOCUMENT_REFUSAL_CODES = ['INVOICE_NOT_FOUND', 'INVOICE_NOT_AUTHORIZED'];
98
+ /**
99
+ * What a client should tell the operator about a voucher the document routes
100
+ * refused with `INVOICE_NOT_AUTHORIZED` — the `recovery` beside `error` in that
101
+ * 409 body, decided server-side from the INVOICE row so a client never re-derives
102
+ * the drain's rules:
103
+ *
104
+ * - `retry` — `pending_cae`, and nothing on the row says the pending-invoice
105
+ * drain will hold it: try again later. The drain may still hold such a row
106
+ * when its order changed underneath it — that is decided against the order,
107
+ * not the row, and is not answered here.
108
+ * - `support` — `pending_cae`, and the row itself shows the drain will hold it
109
+ * rather than settle it: an FCE voucher (skipped), a PARTIAL credit note (its
110
+ * row carries `partialItems`, and the drain's full-order rebuild never
111
+ * reproduces it), or a submit whose recorded voucher triple is incomplete.
112
+ * Contact support. A return's own partial credit note can still be settled by
113
+ * that return's credit-note retry.
114
+ * - `final` — `rejected`. ARCA refused the voucher and nothing retries it.
115
+ */
116
+ export const INVOICE_NOT_AUTHORIZED_RECOVERIES = ['retry', 'support', 'final'];
package/dist/store.d.ts CHANGED
@@ -918,7 +918,7 @@ declare global {
918
918
  * not finish); `cash` while any cash shift is still open
919
919
  * (`409 STORE_MODULE_SHIFTS_OPEN` — `StoreModuleShiftsOpenData`).
920
920
  *
921
- * Enforced on the server for seven modules only:
921
+ * Enforced on the server for nine modules only:
922
922
  * - `promotions` — applying one is refused `PROMOTION_NOT_ACTIVE` (the
923
923
  * existing promotion refusal, not `STORE_MODULE_DISABLED`), a grant already
924
924
  * frozen on a cart stays on the row but derives no money (its
@@ -945,8 +945,22 @@ declare global {
945
945
  * settled `'skipped'` with reason `MODULE_OFF` at its send worker.
946
946
  * - `cash` — a POS terminal activates WITHOUT a shift (no `shiftId`,
947
947
  * living until its own expiry), and opening a shift (`POST /shifts`) is
948
- * refused `403 STORE_MODULE_DISABLED`; every other `/shifts` endpoint
949
- * (close, movements, reconcile, reads) stays reachable.
948
+ * refused `403 STORE_MODULE_DISABLED` — re-checked at commit, so an
949
+ * open that commits after the turn-off is refused (one committing
950
+ * inside the turn-off's own check-then-write window is accepted — see
951
+ * `docs/STORE.md` § Store module switches); every other `/shifts`
952
+ * endpoint (close, movements, reconcile, reads) stays reachable.
953
+ * - `customerAccounts` — the scheduled payment-reminder cron sends
954
+ * nothing for the store, and a scheduled reminder email already queued
955
+ * is dropped; the operator's per-customer send-reminder stays reachable.
956
+ * - `fiscalInvoicing` — "stop new, finish old": the CAEA request cron
957
+ * requests no new CAEA for the store and `AFIP_CERT_EXPIRY` is muted
958
+ * for it; a price-freezing seña over an order with no live factura
959
+ * freezes with no `invoiceId` (re-checked at commit) instead of being
960
+ * refused `DEPOSIT_FREEZE_REQUIRES_INVOICE`, and while that seña is held
961
+ * a re-price that moves the charged total is `409 ORDER_PRICE_FROZEN`,
962
+ * even after the module is back on. The CAEA inform cron, the return
963
+ * credit-note reconciler and operator-initiated invoicing are unchanged.
950
964
  * Every other module is hide-only: its endpoints stay reachable. Public
951
965
  * reads carry only `appointments`, `surveys`, `promotions` and
952
966
  * `customerAccounts`. `PUT /platform/stores/{storeId}` refuses the leaf.
@@ -2032,9 +2046,11 @@ export declare const isStoreModuleKey: (value: unknown) => value is StoreModuleK
2032
2046
  */
2033
2047
  export declare const isStoreModuleOn: (modules: StoreModules | null | undefined, module: StoreModuleKey) => boolean;
2034
2048
  /**
2035
- * Every refusal code the store-module switches put on the wire, beyond the
2036
- * platform-wide set (`VALIDATION_FAILED`, and `STORE_CONFIG_FORBIDDEN` for a
2037
- * plain USER writing the privileged leaf):
2049
+ * Every refusal code the store-module switches put on the wire. Not listed
2050
+ * here: `VALIDATION_FAILED` (a malformed body), and `STORE_CONFIG_FORBIDDEN` —
2051
+ * the `PATCH /store` role refusal a plain USER gets for ANY privileged `config`
2052
+ * leaf, `modules` included, judged before this module code runs. That one is in
2053
+ * {@link STORE_PATCH_ERROR_CODES}:
2038
2054
  *
2039
2055
  * - `STORE_MODULE_UNKNOWN` (400) — `PATCH /store` named a key outside
2040
2056
  * `STORE_MODULE_KEYS`; `data.module` carries it.
@@ -2147,3 +2163,50 @@ export type StoreModuleRefusalBody = {
2147
2163
  data: StoreModuleRefusalDataByCode[Code];
2148
2164
  };
2149
2165
  }[StoreModuleErrorCode];
2166
+ /**
2167
+ * The bare refusal codes `PATCH /store` (`stacks/lambdas/store/_patch.ts`, and
2168
+ * the route's own sibling modules it calls) answers itself — `{ error, message }`
2169
+ * with no `data`, unless noted:
2170
+ *
2171
+ * Role refusals, `403` — a plain USER writing a privileged section. Each is
2172
+ * judged before any other check on that section:
2173
+ * - `STORE_CONFIG_FORBIDDEN` — any privileged `config` leaf (`modules`,
2174
+ * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, …).
2175
+ * - `STORE_ECOMMERCE_FORBIDDEN` — any `ecommerce` block.
2176
+ * - `STORE_FISCAL_FIELD_FORBIDDEN` — a fiscal field of the store.
2177
+ * - `STORE_JURISDICTION_FORBIDDEN` — `jurisdiction`.
2178
+ * - `AFIP_RELACION_FLAG_FORBIDDEN` — `afip.wscdcEnabled` / `afip.fceEnabled`.
2179
+ * - `FX_TABLE_FORBIDDEN` — the `currencies` exchange-rate table.
2180
+ * - `FX_AUTO_UPDATE_FORBIDDEN` — `fxAutoUpdate`.
2181
+ *
2182
+ * Request refusals, `400`:
2183
+ * - `FIELD_NOT_WRITABLE_HERE` — a field written through `POST /store`, not
2184
+ * this route; a top-level `fields` array names them.
2185
+ * - `AFIP_POINT_OF_SALE_NOT_CLEARABLE` — `null` sent for the punto de venta.
2186
+ * - `CURRENCY_CATALOG_ID_UNKNOWN` — a `currencies` entry names an unknown or
2187
+ * disabled catalog id.
2188
+ * - `ARS_RATE_MUST_BE_ONE` — the peso entry's value is not exactly `1`.
2189
+ * - `CURRENCY_RATE_ABOVE_CEILING` — a `currencies` value is above its ceiling.
2190
+ * - `FX_CATALOG_ID_UNKNOWN` / `FX_SOURCE_ID_UNKNOWN` — an `fxAutoUpdate`
2191
+ * binding names an unknown catalog id or rate source.
2192
+ * - `DISPLAY_CURRENCY_NOT_INVOICEABLE` / `PRODUCT_CURRENCY_NOT_INVOICEABLE` — a
2193
+ * store carrying an AFIP CUIT set `config.displayCurrency` /
2194
+ * `config.defaultProductCurrency` to a non-oficial USD variant.
2195
+ * - `PRICE_LIST_NOT_FOUND` — an `ecommerce` save names a
2196
+ * `newCustomerPriceList` that does not exist (`store/_ecommercePatch.ts`).
2197
+ *
2198
+ * Maintenance mode (`{ mode: 'maintenance', level }`, `store/_patchMaintenance.ts`):
2199
+ * - `FORBIDDEN` (403) — the caller is neither an ADMIN nor a MANAGER.
2200
+ * - `STORE_NOT_FOUND` (404) — the store row is gone.
2201
+ *
2202
+ * ⚠️ Not repeated here because another tuple already publishes them:
2203
+ * `VALIDATION_FAILED`, `PRODUCT_CURRENCY_NOT_SUBSCRIBED`
2204
+ * (`PRODUCT_POST_ERROR_CODES`), `WAREHOUSE_NOT_FOUND` /
2205
+ * `WAREHOUSE_INACTIVE` (a `channelWarehouses` target), and every
2206
+ * `STORE_MODULE_*` code ({@link STORE_MODULE_ERROR_CODES}). Codes the route
2207
+ * forwards from shared helpers (the write conflict, the CAEA punto-de-venta
2208
+ * checks, the display-currency lock, the storefront fence) are not listed
2209
+ * either. Cross-cutting refusals: check `GATING_ERROR_CODES` as well.
2210
+ */
2211
+ export declare const STORE_PATCH_ERROR_CODES: readonly ["STORE_CONFIG_FORBIDDEN", "STORE_ECOMMERCE_FORBIDDEN", "STORE_FISCAL_FIELD_FORBIDDEN", "STORE_JURISDICTION_FORBIDDEN", "AFIP_RELACION_FLAG_FORBIDDEN", "FX_TABLE_FORBIDDEN", "FX_AUTO_UPDATE_FORBIDDEN", "FIELD_NOT_WRITABLE_HERE", "AFIP_POINT_OF_SALE_NOT_CLEARABLE", "CURRENCY_CATALOG_ID_UNKNOWN", "ARS_RATE_MUST_BE_ONE", "CURRENCY_RATE_ABOVE_CEILING", "FX_CATALOG_ID_UNKNOWN", "FX_SOURCE_ID_UNKNOWN", "DISPLAY_CURRENCY_NOT_INVOICEABLE", "PRODUCT_CURRENCY_NOT_INVOICEABLE", "PRICE_LIST_NOT_FOUND", "FORBIDDEN", "STORE_NOT_FOUND"];
2212
+ export type StorePatchErrorCode = (typeof STORE_PATCH_ERROR_CODES)[number];
package/dist/store.js CHANGED
@@ -47,9 +47,11 @@ export const isStoreModuleKey = (value) => typeof value === 'string' && STORE_MO
47
47
  */
48
48
  export const isStoreModuleOn = (modules, module) => modules?.[module] !== false;
49
49
  /**
50
- * Every refusal code the store-module switches put on the wire, beyond the
51
- * platform-wide set (`VALIDATION_FAILED`, and `STORE_CONFIG_FORBIDDEN` for a
52
- * plain USER writing the privileged leaf):
50
+ * Every refusal code the store-module switches put on the wire. Not listed
51
+ * here: `VALIDATION_FAILED` (a malformed body), and `STORE_CONFIG_FORBIDDEN` —
52
+ * the `PATCH /store` role refusal a plain USER gets for ANY privileged `config`
53
+ * leaf, `modules` included, judged before this module code runs. That one is in
54
+ * {@link STORE_PATCH_ERROR_CODES}:
53
55
  *
54
56
  * - `STORE_MODULE_UNKNOWN` (400) — `PATCH /store` named a key outside
55
57
  * `STORE_MODULE_KEYS`; `data.module` carries it.
@@ -86,3 +88,69 @@ export const STORE_MODULE_ERROR_CODES = [
86
88
  'STORE_MODULE_SHIFTS_OPEN',
87
89
  'STORE_MODULE_DISABLED',
88
90
  ];
91
+ /**
92
+ * The bare refusal codes `PATCH /store` (`stacks/lambdas/store/_patch.ts`, and
93
+ * the route's own sibling modules it calls) answers itself — `{ error, message }`
94
+ * with no `data`, unless noted:
95
+ *
96
+ * Role refusals, `403` — a plain USER writing a privileged section. Each is
97
+ * judged before any other check on that section:
98
+ * - `STORE_CONFIG_FORBIDDEN` — any privileged `config` leaf (`modules`,
99
+ * `stock`, `priceDecimals`, `changePrice`, `mandatory2fa`, …).
100
+ * - `STORE_ECOMMERCE_FORBIDDEN` — any `ecommerce` block.
101
+ * - `STORE_FISCAL_FIELD_FORBIDDEN` — a fiscal field of the store.
102
+ * - `STORE_JURISDICTION_FORBIDDEN` — `jurisdiction`.
103
+ * - `AFIP_RELACION_FLAG_FORBIDDEN` — `afip.wscdcEnabled` / `afip.fceEnabled`.
104
+ * - `FX_TABLE_FORBIDDEN` — the `currencies` exchange-rate table.
105
+ * - `FX_AUTO_UPDATE_FORBIDDEN` — `fxAutoUpdate`.
106
+ *
107
+ * Request refusals, `400`:
108
+ * - `FIELD_NOT_WRITABLE_HERE` — a field written through `POST /store`, not
109
+ * this route; a top-level `fields` array names them.
110
+ * - `AFIP_POINT_OF_SALE_NOT_CLEARABLE` — `null` sent for the punto de venta.
111
+ * - `CURRENCY_CATALOG_ID_UNKNOWN` — a `currencies` entry names an unknown or
112
+ * disabled catalog id.
113
+ * - `ARS_RATE_MUST_BE_ONE` — the peso entry's value is not exactly `1`.
114
+ * - `CURRENCY_RATE_ABOVE_CEILING` — a `currencies` value is above its ceiling.
115
+ * - `FX_CATALOG_ID_UNKNOWN` / `FX_SOURCE_ID_UNKNOWN` — an `fxAutoUpdate`
116
+ * binding names an unknown catalog id or rate source.
117
+ * - `DISPLAY_CURRENCY_NOT_INVOICEABLE` / `PRODUCT_CURRENCY_NOT_INVOICEABLE` — a
118
+ * store carrying an AFIP CUIT set `config.displayCurrency` /
119
+ * `config.defaultProductCurrency` to a non-oficial USD variant.
120
+ * - `PRICE_LIST_NOT_FOUND` — an `ecommerce` save names a
121
+ * `newCustomerPriceList` that does not exist (`store/_ecommercePatch.ts`).
122
+ *
123
+ * Maintenance mode (`{ mode: 'maintenance', level }`, `store/_patchMaintenance.ts`):
124
+ * - `FORBIDDEN` (403) — the caller is neither an ADMIN nor a MANAGER.
125
+ * - `STORE_NOT_FOUND` (404) — the store row is gone.
126
+ *
127
+ * ⚠️ Not repeated here because another tuple already publishes them:
128
+ * `VALIDATION_FAILED`, `PRODUCT_CURRENCY_NOT_SUBSCRIBED`
129
+ * (`PRODUCT_POST_ERROR_CODES`), `WAREHOUSE_NOT_FOUND` /
130
+ * `WAREHOUSE_INACTIVE` (a `channelWarehouses` target), and every
131
+ * `STORE_MODULE_*` code ({@link STORE_MODULE_ERROR_CODES}). Codes the route
132
+ * forwards from shared helpers (the write conflict, the CAEA punto-de-venta
133
+ * checks, the display-currency lock, the storefront fence) are not listed
134
+ * either. Cross-cutting refusals: check `GATING_ERROR_CODES` as well.
135
+ */
136
+ export const STORE_PATCH_ERROR_CODES = [
137
+ 'STORE_CONFIG_FORBIDDEN',
138
+ 'STORE_ECOMMERCE_FORBIDDEN',
139
+ 'STORE_FISCAL_FIELD_FORBIDDEN',
140
+ 'STORE_JURISDICTION_FORBIDDEN',
141
+ 'AFIP_RELACION_FLAG_FORBIDDEN',
142
+ 'FX_TABLE_FORBIDDEN',
143
+ 'FX_AUTO_UPDATE_FORBIDDEN',
144
+ 'FIELD_NOT_WRITABLE_HERE',
145
+ 'AFIP_POINT_OF_SALE_NOT_CLEARABLE',
146
+ 'CURRENCY_CATALOG_ID_UNKNOWN',
147
+ 'ARS_RATE_MUST_BE_ONE',
148
+ 'CURRENCY_RATE_ABOVE_CEILING',
149
+ 'FX_CATALOG_ID_UNKNOWN',
150
+ 'FX_SOURCE_ID_UNKNOWN',
151
+ 'DISPLAY_CURRENCY_NOT_INVOICEABLE',
152
+ 'PRODUCT_CURRENCY_NOT_INVOICEABLE',
153
+ 'PRICE_LIST_NOT_FOUND',
154
+ 'FORBIDDEN',
155
+ 'STORE_NOT_FOUND',
156
+ ];
@@ -780,11 +780,20 @@ declare global {
780
780
  * place to echo a catalogue's payment-provider IDs.
781
781
  */
782
782
  missingProviderIds: string[];
783
+ /**
784
+ * The charged billing cycles whose Stripe Price was minted at a different amount or currency
785
+ * than the row now publishes (its `stripePrice*Cents` / `stripePriceCurrency` stamp differs):
786
+ * Stripe checkout refuses them (`409 PLAN_STRIPE_PRICE_STALE`) until `seed-stripe-products`
787
+ * re-mints. A cycle with no stamp (a row seeded before the stamp existed) is never listed.
788
+ */
789
+ staleStripePriceCycles: ('monthly' | 'annual')[];
783
790
  /**
784
791
  * Keys in the template row's `grants` overlay that the api does not
785
792
  * declare (retired or misspelt), sorted. Inert — every reader ignores them
786
793
  * and the code default applies — but stored, so they are drift: a `null`
787
- * for that key on `PATCH /platform/billing/plans/grants` removes them.
794
+ * for that key on `PATCH /platform/billing/plans/grants`, then
795
+ * `POST /platform/billing/plans/publish`, removes them (the PATCH alone
796
+ * writes only the draft).
788
797
  */
789
798
  undeclaredGrantKeys: string[];
790
799
  /**
@@ -1093,10 +1102,24 @@ declare global {
1093
1102
  reconciledStores?: number;
1094
1103
  /**
1095
1104
  * A paid tier whose price, currency or name changed, with a currency set on the draft being
1096
- * published (never `basico`, never when that currency is absent): run the provider seeders
1097
- * to mint its new prices.
1105
+ * published (never `basico`, never when that currency is absent). A live publish mints the
1106
+ * new Stripe Prices itself when Stripe already bills the tier (see `stripePriceMinted`), and
1107
+ * that mint also converges the Stripe Product's name on the published `name`. The MercadoPago
1108
+ * PreApproval plans, and a Product rename after a `name`-only publish (which mints nothing),
1109
+ * stay the seeders'.
1098
1110
  */
1099
1111
  stripeSyncRequired: boolean;
1112
+ /**
1113
+ * Live publish only, and only on a tier whose price or currency moved while Stripe bills it
1114
+ * (its row carries a `stripeProductId`): `true` when the publish minted the new Stripe Prices
1115
+ * and stamped them on the row. `false` means this publish did NOT stamp the row, for one of
1116
+ * two reasons: the mint failed (an operator ERROR row `STRIPE_PLAN_PRICE_MINT_FAILED` is
1117
+ * written, and Stripe checkout refuses the tier with `409 PLAN_STRIPE_PRICE_STALE` until
1118
+ * `seed-stripe-products` re-mints), or a LATER publish moved the price before this mint's
1119
+ * stamp landed — superseded, so no ERROR row: that later publish mints its own. Absent
1120
+ * otherwise.
1121
+ */
1122
+ stripePriceMinted?: boolean;
1100
1123
  }
1101
1124
  /** `data` of `POST /platform/billing/plans/publish`. */
1102
1125
  interface PlanCataloguePublishResult {
@@ -1161,6 +1184,20 @@ declare global {
1161
1184
  * revision moved: send the same request again.
1162
1185
  */
1163
1186
  | 'PLAN_CATALOGUE_BUSY' | 'PLAN_CATALOGUE_NO_CHANGES' | 'PLAN_CATALOGUE_PUBLICATION_NOT_FOUND' | 'PLAN_CATALOGUE_REQUEST_EXPIRED' | 'PLAN_CATALOGUE_INVALID_CURSOR' | 'PLAN_CATALOGUE_DRAFT_STALE' | 'IDEMPOTENCY_CONFLICT';
1187
+ /**
1188
+ * The `error` codes `POST /subscription/checkout` adds when a published price
1189
+ * has not reached the provider that would bill it.
1190
+ */
1191
+ type PlanPriceSyncErrorCode =
1192
+ /**
1193
+ * 409 · the store checks out through Stripe, and the Stripe Price on that
1194
+ * plan cycle was minted at a different amount or currency than the plan
1195
+ * now publishes (its stamp, `stripePrice*Cents` / `stripePriceCurrency`,
1196
+ * differs) — a checkout would bill the old price. Nothing was created; the
1197
+ * operator re-mints (`seed-stripe-products`). Carries `planTier` and
1198
+ * `billingCycle` like `PLAN_NOT_PURCHASABLE`.
1199
+ */
1200
+ 'PLAN_STRIPE_PRICE_STALE';
1164
1201
  }
1165
1202
  export type BillingProviderName = SubscriptionProvider;
1166
1203
  export type InvoiceSummary = globalThis.InvoiceSummary;
@@ -1176,6 +1213,8 @@ export declare const PAID_PLAN_TIERS: readonly ["emprendedor", "profesional", "a
1176
1213
  export declare const PLAN_CATALOGUE_TEMPLATE_FIELDS: readonly ["name", "description", "displayOrder", "isActive", "isPopular", "color", "priceMonthlyCents", "priceAnnualCents", "currency"];
1177
1214
  /** Every `error` code the plan catalogue draft / publish routes add — the coverage test holds the handlers to it. */
1178
1215
  export declare const PLAN_CATALOGUE_ERROR_CODES: readonly ["PLAN_CATALOGUE_REVISION_CONFLICT", "PLAN_CATALOGUE_BUSY", "PLAN_CATALOGUE_NO_CHANGES", "PLAN_CATALOGUE_PUBLICATION_NOT_FOUND", "PLAN_CATALOGUE_REQUEST_EXPIRED", "PLAN_CATALOGUE_INVALID_CURSOR", "PLAN_CATALOGUE_DRAFT_STALE", "IDEMPOTENCY_CONFLICT"];
1216
+ /** Every `error` code `POST /subscription/checkout` adds for a price the provider has not caught up with. */
1217
+ export declare const PLAN_PRICE_SYNC_ERROR_CODES: readonly ["PLAN_STRIPE_PRICE_STALE"];
1179
1218
  /** Whether `value` names a paid tier — narrows an untyped read (a stored row, a global) to `PaidPlanTier`. */
1180
1219
  export declare const isPaidPlanTier: (value: unknown) => value is PaidPlanTier;
1181
1220
  /**
@@ -76,6 +76,10 @@ export const PLAN_CATALOGUE_ERROR_CODES = exhaustiveTuple()([
76
76
  'PLAN_CATALOGUE_DRAFT_STALE',
77
77
  'IDEMPOTENCY_CONFLICT',
78
78
  ]);
79
+ /** Every `error` code `POST /subscription/checkout` adds for a price the provider has not caught up with. */
80
+ export const PLAN_PRICE_SYNC_ERROR_CODES = exhaustiveTuple()([
81
+ 'PLAN_STRIPE_PRICE_STALE',
82
+ ]);
79
83
  /** Whether `value` names a paid tier — narrows an untyped read (a stored row, a global) to `PaidPlanTier`. */
80
84
  export const isPaidPlanTier = (value) => typeof value === 'string' && PAID_PLAN_TIERS.includes(value);
81
85
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "platform-types",
3
- "version": "1.10.733",
3
+ "version": "1.10.735",
4
4
  "description": "Shared wire and entity contracts for the platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",