platform-types 1.10.777 → 1.10.778

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/cjs/index.js CHANGED
@@ -121,6 +121,7 @@ __exportStar(require("./user.js"), exports);
121
121
  __exportStar(require("./userActivity.js"), exports);
122
122
  __exportStar(require("./valuation.js"), exports);
123
123
  __exportStar(require("./webhook.js"), exports);
124
+ __exportStar(require("./wholesaleRequest.js"), exports);
124
125
  __exportStar(require("./wire.js"), exports);
125
126
  __exportStar(require("./workItem.js"), exports);
126
127
  __exportStar(require("./zone.js"), exports);
@@ -6,7 +6,7 @@ exports.WEB_NOTIFICATIONS_MAX_LIMIT = exports.CUSTOMER_NOTIFICATION_TYPES = expo
6
6
  // legacy opt-ins and explicit channel maps remain source data on USER rows.
7
7
  // Addressed ERROR / ML_FACTURADOR_COLLISION / ML_SELLER_SWITCHED /
8
8
  // ML_CONNECTION_BROKEN / MENTION / CUSTOMER_PENDING_APPROVAL /
9
- // PAYMENT_ATTEMPT_RESOLUTION do not select a producer subscriber audience;
9
+ // PAYMENT_ATTEMPT_RESOLUTION / CUSTOMER_WHOLESALE_REQUEST do not select a producer subscriber audience;
10
10
  // the consumer still reads the live USER row for each delivery channel.
11
11
  // AFIP_CERT_EXPIRY is seeded on the founding user and uses the subscriber index.
12
12
  // Exported as a real enum consumed by api and app; adding an indexed type
@@ -199,6 +199,24 @@ var NotificationTypeEnum;
199
199
  * re-opens rings again.
200
200
  */
201
201
  NotificationTypeEnum["PAYMENT_ATTEMPT_RESOLUTION"] = "PAYMENT_ATTEMPT_RESOLUTION";
202
+ /**
203
+ * A signed-in storefront buyer asked for a wholesale account
204
+ * (`POST /user { mode: 'wholesale-request' }`) and the request waits in the
205
+ * operator queue (`GET /customers?mode=wholesale-requests`). Names the buyer
206
+ * by name only — never their email or CUIT.
207
+ *
208
+ * ⚠️ ADMIN-ONLY, and its own preference key. The producer fans out to every
209
+ * non-disabled ADMIN of the store (`listActiveAdmins`) — the tier that reads
210
+ * the queue and decides it — so a client offers the
211
+ * `CUSTOMER_WHOLESALE_REQUEST` toggle to ADMINs only. No legacy opt-in scan:
212
+ * the consumer enforces explicit type/channel preferences
213
+ * (`notificationPreferences.CUSTOMER_WHOLESALE_REQUEST`), default-on when
214
+ * absent. Additive — no backfill or consumer migration owed.
215
+ *
216
+ * ⚠️ Carries `wholesaleRequestRef` and NEVER a top-level `customerId` (an
217
+ * audience — the bell would reach the buyer's own storefront socket).
218
+ */
219
+ NotificationTypeEnum["CUSTOMER_WHOLESALE_REQUEST"] = "CUSTOMER_WHOLESALE_REQUEST";
202
220
  })(NotificationTypeEnum || (exports.NotificationTypeEnum = NotificationTypeEnum = {}));
203
221
  /**
204
222
  * The notification types a CUSTOMER bell can carry today; each links through
@@ -691,6 +691,17 @@ exports.SOCKET_ACTIONS = [
691
691
  * where the role and scope gates sit.
692
692
  */
693
693
  'workItems',
694
+ /**
695
+ * BE → the store's ADMIN-TIER operator sockets when a buyer's wholesale
696
+ * request is submitted or decided. Carries the `WholesaleRequest` row as it
697
+ * now stands (company data included), so the queue can patch in place.
698
+ *
699
+ * ⚠️ Admin tier only — `wsPostStore(..., { audience: 'operator', lowTierData:
700
+ * null })`: the queue is an admin-tier read, so a USER or PRINTER socket
701
+ * receives nothing. The buyer follows their own request on the `'customers'`
702
+ * action instead (`WholesaleRequestStatusFrame`).
703
+ */
704
+ 'wholesaleRequests',
694
705
  ];
695
706
  /** Runtime guard — narrows an untrusted string to a known action. */
696
707
  const isSocketAction = (value) => typeof value === 'string' && exports.SOCKET_ACTIONS.includes(value);
package/dist/cjs/store.js CHANGED
@@ -139,6 +139,13 @@ exports.STORE_MODULE_ERROR_CODES = [
139
139
  * entry naming no list of the store, with `data: { field, value }` where
140
140
  * `field` is `<catalog>[<row>].availability.priceLists[<entry>]`.
141
141
  *
142
+ * Request refusal, `409`:
143
+ * - `WHOLESALE_LIST_REQUIRED` — an `ecommerce` save turns
144
+ * `config.wholesaleRequests` on while the store holds no
145
+ * `audienceClass: 'wholesale'` list (`store/_ecommercePatch.ts`), with
146
+ * `data: { field: 'ecommerce.config.wholesaleRequests' }`. The platform PUT
147
+ * answers it too.
148
+ *
142
149
  * Maintenance mode (`{ mode: 'maintenance', level }`, `store/_patchMaintenance.ts`):
143
150
  * - `FORBIDDEN` (403) — the caller is neither an ADMIN nor a MANAGER.
144
151
  * - `STORE_NOT_FOUND` (404) — the store row is gone.
@@ -170,6 +177,7 @@ exports.STORE_PATCH_ERROR_CODES = [
170
177
  'DISPLAY_CURRENCY_NOT_INVOICEABLE',
171
178
  'PRODUCT_CURRENCY_NOT_INVOICEABLE',
172
179
  'PRICE_LIST_NOT_FOUND',
180
+ 'WHOLESALE_LIST_REQUIRED',
173
181
  'FORBIDDEN',
174
182
  'STORE_NOT_FOUND',
175
183
  ];
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * A signed-in storefront buyer's request for a wholesale account, and the
4
+ * operator queue that decides it.
5
+ *
6
+ * One row per customer (`WHOLESALEREQ#{storeId}` / `{customerId}`). A new
7
+ * request overwrites the previous one only once that one is decided, so a
8
+ * customer holds at most one pending request.
9
+ *
10
+ * The flow is offered only while the store's
11
+ * `ecommerce.config.wholesaleRequests` is `true` (absent = off).
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.WHOLESALE_REQUEST_REASON_MAX = exports.WHOLESALE_REQUEST_PHONE_MAX = exports.WHOLESALE_REQUEST_MESSAGE_MAX = exports.WHOLESALE_REQUEST_ACTIVITY_MAX = exports.WHOLESALE_REQUEST_RAZON_SOCIAL_MAX = exports.WHOLESALE_REQUESTS_PAGE_MAX = exports.WHOLESALE_REQUEST_STATES = void 0;
15
+ /** The three states a request moves through. */
16
+ exports.WHOLESALE_REQUEST_STATES = ['pending', 'approved', 'declined'];
17
+ /** `GET /customers?mode=wholesale-requests` page ceiling; the default page is 50. */
18
+ exports.WHOLESALE_REQUESTS_PAGE_MAX = 100;
19
+ /** Character ceilings on the buyer's free text, counted after trimming. */
20
+ exports.WHOLESALE_REQUEST_RAZON_SOCIAL_MAX = 200;
21
+ exports.WHOLESALE_REQUEST_ACTIVITY_MAX = 120;
22
+ exports.WHOLESALE_REQUEST_MESSAGE_MAX = 1000;
23
+ exports.WHOLESALE_REQUEST_PHONE_MAX = 30;
24
+ /** Character ceiling on the operator's decline reason, counted after trimming. */
25
+ exports.WHOLESALE_REQUEST_REASON_MAX = 500;
@@ -1238,6 +1238,15 @@ declare global {
1238
1238
  * On BOTH surfaces; `[]` when the subject has none.
1239
1239
  */
1240
1240
  recentPurchases: CustomerRecentPurchaseExportEntry[];
1241
+ /**
1242
+ * The subject's wholesale-account requests, on BOTH surfaces: the one filed
1243
+ * under their own id plus any filed under a merged-away id folded into them
1244
+ * (the same folded ids, and the same cap, as the financial collections) —
1245
+ * the company data submitted and its state, never the decline reason or the
1246
+ * deciding operator. Absent when there is none (and from servers predating
1247
+ * the request flow).
1248
+ */
1249
+ wholesaleRequests?: WholesaleRequestExportEntry[];
1241
1250
  /**
1242
1251
  * Per COLLECTION, and honest: `true` means the read stopped before the
1243
1252
  * end of a partition, folded financial IDs were omitted at the cap, or whole
@@ -1261,6 +1270,12 @@ declare global {
1261
1270
  favorites: boolean;
1262
1271
  /** `true` when folded ids past the cap were not read. */
1263
1272
  recentPurchases: boolean;
1273
+ /**
1274
+ * Present when `wholesaleRequests` is, or when merged-away ids were cut
1275
+ * at the folded-id cap — then `true`: a request filed under an omitted id
1276
+ * may be missing.
1277
+ */
1278
+ wholesaleRequests?: boolean;
1264
1279
  };
1265
1280
  }
1266
1281
  /**
package/dist/index.d.ts CHANGED
@@ -97,6 +97,7 @@ export * from './user.ts';
97
97
  export * from './userActivity.ts';
98
98
  export * from './valuation.ts';
99
99
  export * from './webhook.ts';
100
+ export * from './wholesaleRequest.ts';
100
101
  export * from './wire.ts';
101
102
  export * from './workItem.ts';
102
103
  export * from './zone.ts';
package/dist/index.js CHANGED
@@ -105,6 +105,7 @@ export * from "./user.js";
105
105
  export * from "./userActivity.js";
106
106
  export * from "./valuation.js";
107
107
  export * from "./webhook.js";
108
+ export * from "./wholesaleRequest.js";
108
109
  export * from "./wire.js";
109
110
  export * from "./workItem.js";
110
111
  export * from "./zone.js";
@@ -177,7 +177,25 @@ export declare enum NotificationTypeEnum {
177
177
  * nothing. An attempt an operator closed (`resolved`) and a later approval
178
178
  * re-opens rings again.
179
179
  */
180
- PAYMENT_ATTEMPT_RESOLUTION = "PAYMENT_ATTEMPT_RESOLUTION"
180
+ PAYMENT_ATTEMPT_RESOLUTION = "PAYMENT_ATTEMPT_RESOLUTION",
181
+ /**
182
+ * A signed-in storefront buyer asked for a wholesale account
183
+ * (`POST /user { mode: 'wholesale-request' }`) and the request waits in the
184
+ * operator queue (`GET /customers?mode=wholesale-requests`). Names the buyer
185
+ * by name only — never their email or CUIT.
186
+ *
187
+ * ⚠️ ADMIN-ONLY, and its own preference key. The producer fans out to every
188
+ * non-disabled ADMIN of the store (`listActiveAdmins`) — the tier that reads
189
+ * the queue and decides it — so a client offers the
190
+ * `CUSTOMER_WHOLESALE_REQUEST` toggle to ADMINs only. No legacy opt-in scan:
191
+ * the consumer enforces explicit type/channel preferences
192
+ * (`notificationPreferences.CUSTOMER_WHOLESALE_REQUEST`), default-on when
193
+ * absent. Additive — no backfill or consumer migration owed.
194
+ *
195
+ * ⚠️ Carries `wholesaleRequestRef` and NEVER a top-level `customerId` (an
196
+ * audience — the bell would reach the buyer's own storefront socket).
197
+ */
198
+ CUSTOMER_WHOLESALE_REQUEST = "CUSTOMER_WHOLESALE_REQUEST"
181
199
  }
182
200
  /**
183
201
  * The notification types a CUSTOMER bell can carry today; each links through
@@ -290,6 +308,11 @@ declare global {
290
308
  * POINTER, not an audience — see {@link PendingCustomerRef}.
291
309
  */
292
310
  pendingCustomerRef?: PendingCustomerRef;
311
+ /**
312
+ * `CUSTOMER_WHOLESALE_REQUEST` only: which request to open. A POINTER, not
313
+ * an audience — see {@link WholesaleRequestRef}.
314
+ */
315
+ wholesaleRequestRef?: WholesaleRequestRef;
293
316
  /** `MENTION` only: the colleague who wrote the mention (a snapshot). */
294
317
  actor?: NotificationActor;
295
318
  userId?: string;
@@ -353,7 +376,7 @@ declare global {
353
376
  * customer socket carry — the stored row minus the operator-only pointers.
354
377
  * A union keyed on `type`; `ORDER` is its only member today.
355
378
  */
356
- type CustomerNotification = Omit<NotificationInterface, 'cartRef' | 'pendingCustomerRef' | 'TableName' | 'type' | 'orderId'> & {
379
+ type CustomerNotification = Omit<NotificationInterface, 'cartRef' | 'pendingCustomerRef' | 'wholesaleRequestRef' | 'TableName' | 'type' | 'orderId'> & {
357
380
  type: NotificationTypeEnum.ORDER;
358
381
  /** Set on every customer row: the order the bell is about. */
359
382
  orderId: string;
@@ -3,7 +3,7 @@
3
3
  // legacy opt-ins and explicit channel maps remain source data on USER rows.
4
4
  // Addressed ERROR / ML_FACTURADOR_COLLISION / ML_SELLER_SWITCHED /
5
5
  // ML_CONNECTION_BROKEN / MENTION / CUSTOMER_PENDING_APPROVAL /
6
- // PAYMENT_ATTEMPT_RESOLUTION do not select a producer subscriber audience;
6
+ // PAYMENT_ATTEMPT_RESOLUTION / CUSTOMER_WHOLESALE_REQUEST do not select a producer subscriber audience;
7
7
  // the consumer still reads the live USER row for each delivery channel.
8
8
  // AFIP_CERT_EXPIRY is seeded on the founding user and uses the subscriber index.
9
9
  // Exported as a real enum consumed by api and app; adding an indexed type
@@ -196,6 +196,24 @@ export var NotificationTypeEnum;
196
196
  * re-opens rings again.
197
197
  */
198
198
  NotificationTypeEnum["PAYMENT_ATTEMPT_RESOLUTION"] = "PAYMENT_ATTEMPT_RESOLUTION";
199
+ /**
200
+ * A signed-in storefront buyer asked for a wholesale account
201
+ * (`POST /user { mode: 'wholesale-request' }`) and the request waits in the
202
+ * operator queue (`GET /customers?mode=wholesale-requests`). Names the buyer
203
+ * by name only — never their email or CUIT.
204
+ *
205
+ * ⚠️ ADMIN-ONLY, and its own preference key. The producer fans out to every
206
+ * non-disabled ADMIN of the store (`listActiveAdmins`) — the tier that reads
207
+ * the queue and decides it — so a client offers the
208
+ * `CUSTOMER_WHOLESALE_REQUEST` toggle to ADMINs only. No legacy opt-in scan:
209
+ * the consumer enforces explicit type/channel preferences
210
+ * (`notificationPreferences.CUSTOMER_WHOLESALE_REQUEST`), default-on when
211
+ * absent. Additive — no backfill or consumer migration owed.
212
+ *
213
+ * ⚠️ Carries `wholesaleRequestRef` and NEVER a top-level `customerId` (an
214
+ * audience — the bell would reach the buyer's own storefront socket).
215
+ */
216
+ NotificationTypeEnum["CUSTOMER_WHOLESALE_REQUEST"] = "CUSTOMER_WHOLESALE_REQUEST";
199
217
  })(NotificationTypeEnum || (NotificationTypeEnum = {}));
200
218
  /**
201
219
  * The notification types a CUSTOMER bell can carry today; each links through
package/dist/socket.d.ts CHANGED
@@ -26,7 +26,7 @@
26
26
  * A client must ignore actions it does not own rather than assume it
27
27
  * receives all of them.
28
28
  */
29
- export declare const SOCKET_ACTIONS: readonly ["account", "appointments", "appointmentWaitlist", "attendance", "attributes", "automation-rules", "baskets", "cart", "brands", "cash", "catalogOperation", "categories", "campaigns", "customers", "customerNote", "customerAddress", "customerContact", "deliveries", "globals", "inventory", "invoices", "literals", "loyalty", "orders", "presupuestos", "products", "promotions", "segments", "returns", "rma", "templates", "services", "service-templates", "shifts", "stores", "suppliers", "supplier-invoice", "support", "surveys", "users", "warehouses", "zones", "favorites", "webhooks", "account-delete", "baskets-delete", "cart-delete", "log-delete", "notifications", "support-message", "payment_received", "mp_payment_status_changed", "payment_linked", "payment_unlinked", "mercadopago", "mercadopago_dynamic_qr_created", "mercadopago_static_qr_created", "mp_hook_log_appended", "mp_ipn_log_appended", "orderPaymentAttempt", "orderDelivery", "paymentAttemptChanged", "stripe", "subscription", "caea", "print", "print-order", "print-invoice", "print-product", "print-tag", "print_job_failed", "request_printers", "printers_changed", "agent_status_changed", "print_rules_changed", "printers_active", "agent_command", "maintenance", "currency_auto_updated", "drain_progress", "integration_event_appended", "userActivityRecorded", "mercadolibre_settlement_period", "customDomain", "purchaseOrders", "cycleCounts", "stockTransfers", "workItems"];
29
+ export declare const SOCKET_ACTIONS: readonly ["account", "appointments", "appointmentWaitlist", "attendance", "attributes", "automation-rules", "baskets", "cart", "brands", "cash", "catalogOperation", "categories", "campaigns", "customers", "customerNote", "customerAddress", "customerContact", "deliveries", "globals", "inventory", "invoices", "literals", "loyalty", "orders", "presupuestos", "products", "promotions", "segments", "returns", "rma", "templates", "services", "service-templates", "shifts", "stores", "suppliers", "supplier-invoice", "support", "surveys", "users", "warehouses", "zones", "favorites", "webhooks", "account-delete", "baskets-delete", "cart-delete", "log-delete", "notifications", "support-message", "payment_received", "mp_payment_status_changed", "payment_linked", "payment_unlinked", "mercadopago", "mercadopago_dynamic_qr_created", "mercadopago_static_qr_created", "mp_hook_log_appended", "mp_ipn_log_appended", "orderPaymentAttempt", "orderDelivery", "paymentAttemptChanged", "stripe", "subscription", "caea", "print", "print-order", "print-invoice", "print-product", "print-tag", "print_job_failed", "request_printers", "printers_changed", "agent_status_changed", "print_rules_changed", "printers_active", "agent_command", "maintenance", "currency_auto_updated", "drain_progress", "integration_event_appended", "userActivityRecorded", "mercadolibre_settlement_period", "customDomain", "purchaseOrders", "cycleCounts", "stockTransfers", "workItems", "wholesaleRequests"];
30
30
  /** Union of every server→client data-frame action. */
31
31
  export type SocketAction = (typeof SOCKET_ACTIONS)[number];
32
32
  /**
package/dist/socket.js CHANGED
@@ -688,6 +688,17 @@ export const SOCKET_ACTIONS = [
688
688
  * where the role and scope gates sit.
689
689
  */
690
690
  'workItems',
691
+ /**
692
+ * BE → the store's ADMIN-TIER operator sockets when a buyer's wholesale
693
+ * request is submitted or decided. Carries the `WholesaleRequest` row as it
694
+ * now stands (company data included), so the queue can patch in place.
695
+ *
696
+ * ⚠️ Admin tier only — `wsPostStore(..., { audience: 'operator', lowTierData:
697
+ * null })`: the queue is an admin-tier read, so a USER or PRINTER socket
698
+ * receives nothing. The buyer follows their own request on the `'customers'`
699
+ * action instead (`WholesaleRequestStatusFrame`).
700
+ */
701
+ 'wholesaleRequests',
691
702
  ];
692
703
  /** Runtime guard — narrows an untrusted string to a known action. */
693
704
  export const isSocketAction = (value) => typeof value === 'string' && SOCKET_ACTIONS.includes(value);
package/dist/store.d.ts CHANGED
@@ -146,6 +146,18 @@ declare global {
146
146
  * frame), so the storefront can say the account needs approval.
147
147
  */
148
148
  registrationMode?: 'open' | 'approval';
149
+ /**
150
+ * Whether a signed-in storefront buyer may ask for a wholesale account
151
+ * (`POST /user { mode: 'wholesale-request' }`). Absent or `false` = off:
152
+ * the request route refuses `403 WHOLESALE_REQUESTS_DISABLED`. Explicit on
153
+ * purpose — a store may hold wholesale-class lists for customers it assigns
154
+ * itself without inviting requests. Turning it on requires at least one
155
+ * `audienceClass: 'wholesale'` list (`409 WHOLESALE_LIST_REQUIRED`).
156
+ * Written by `PATCH /store` (ADMIN) and `PUT /platform/stores/{storeId}`
157
+ * (MANAGER). SENT TO SHOPPERS (web `GET /store` and the customer `stores`
158
+ * frame), so the storefront can offer the request.
159
+ */
160
+ wholesaleRequests?: boolean;
149
161
  /**
150
162
  * How long a storefront online payment holds the order, in whole
151
163
  * minutes: the payment attempt's `expiresAt` and its Checkout Pro
@@ -198,9 +210,11 @@ declare global {
198
210
  /** Sparse tenant PATCH shape; live null values reset to registry defaults and are never stored.
199
211
  * An unknown newCustomerPriceList refuses 400 PRICE_LIST_NOT_FOUND with
200
212
  * data: { field: 'ecommerce.config.newCustomerPriceList', value: number }; nothing written.
213
+ * `wholesaleRequests: true` on a store with no `audienceClass: 'wholesale'` list
214
+ * refuses 409 WHOLESALE_LIST_REQUIRED; nothing written.
201
215
  */
202
216
  interface EcommercePatchInput {
203
- config?: Pick<NonNullable<Ecommerce['config']>, 'showDefaultPriceList' | 'newCustomerPriceList' | 'registrationMode' | 'paymentHoldMinutes'> & StorefrontLiveSettingsPatch;
217
+ config?: Pick<NonNullable<Ecommerce['config']>, 'showDefaultPriceList' | 'newCustomerPriceList' | 'registrationMode' | 'wholesaleRequests' | 'paymentHoldMinutes'> & StorefrontLiveSettingsPatch;
204
218
  sidebar?: Ecommerce['sidebar'];
205
219
  home?: Omit<NonNullable<Ecommerce['home']>, 'displayOrders' | 'displayProfile'>;
206
220
  themeColors?: Ecommerce['themeColors'];
@@ -2398,6 +2412,13 @@ export type StoreModuleRefusalBody = {
2398
2412
  * entry naming no list of the store, with `data: { field, value }` where
2399
2413
  * `field` is `<catalog>[<row>].availability.priceLists[<entry>]`.
2400
2414
  *
2415
+ * Request refusal, `409`:
2416
+ * - `WHOLESALE_LIST_REQUIRED` — an `ecommerce` save turns
2417
+ * `config.wholesaleRequests` on while the store holds no
2418
+ * `audienceClass: 'wholesale'` list (`store/_ecommercePatch.ts`), with
2419
+ * `data: { field: 'ecommerce.config.wholesaleRequests' }`. The platform PUT
2420
+ * answers it too.
2421
+ *
2401
2422
  * Maintenance mode (`{ mode: 'maintenance', level }`, `store/_patchMaintenance.ts`):
2402
2423
  * - `FORBIDDEN` (403) — the caller is neither an ADMIN nor a MANAGER.
2403
2424
  * - `STORE_NOT_FOUND` (404) — the store row is gone.
@@ -2411,5 +2432,5 @@ export type StoreModuleRefusalBody = {
2411
2432
  * checks, the display-currency lock, the storefront fence) are not listed
2412
2433
  * either. Cross-cutting refusals: check `GATING_ERROR_CODES` as well.
2413
2434
  */
2414
- 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"];
2435
+ 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", "WHOLESALE_LIST_REQUIRED", "FORBIDDEN", "STORE_NOT_FOUND"];
2415
2436
  export type StorePatchErrorCode = (typeof STORE_PATCH_ERROR_CODES)[number];
package/dist/store.js CHANGED
@@ -134,6 +134,13 @@ export const STORE_MODULE_ERROR_CODES = [
134
134
  * entry naming no list of the store, with `data: { field, value }` where
135
135
  * `field` is `<catalog>[<row>].availability.priceLists[<entry>]`.
136
136
  *
137
+ * Request refusal, `409`:
138
+ * - `WHOLESALE_LIST_REQUIRED` — an `ecommerce` save turns
139
+ * `config.wholesaleRequests` on while the store holds no
140
+ * `audienceClass: 'wholesale'` list (`store/_ecommercePatch.ts`), with
141
+ * `data: { field: 'ecommerce.config.wholesaleRequests' }`. The platform PUT
142
+ * answers it too.
143
+ *
137
144
  * Maintenance mode (`{ mode: 'maintenance', level }`, `store/_patchMaintenance.ts`):
138
145
  * - `FORBIDDEN` (403) — the caller is neither an ADMIN nor a MANAGER.
139
146
  * - `STORE_NOT_FOUND` (404) — the store row is gone.
@@ -165,6 +172,7 @@ export const STORE_PATCH_ERROR_CODES = [
165
172
  'DISPLAY_CURRENCY_NOT_INVOICEABLE',
166
173
  'PRODUCT_CURRENCY_NOT_INVOICEABLE',
167
174
  'PRICE_LIST_NOT_FOUND',
175
+ 'WHOLESALE_LIST_REQUIRED',
168
176
  'FORBIDDEN',
169
177
  'STORE_NOT_FOUND',
170
178
  ];
@@ -0,0 +1,161 @@
1
+ /**
2
+ * A signed-in storefront buyer's request for a wholesale account, and the
3
+ * operator queue that decides it.
4
+ *
5
+ * One row per customer (`WHOLESALEREQ#{storeId}` / `{customerId}`). A new
6
+ * request overwrites the previous one only once that one is decided, so a
7
+ * customer holds at most one pending request.
8
+ *
9
+ * The flow is offered only while the store's
10
+ * `ecommerce.config.wholesaleRequests` is `true` (absent = off).
11
+ */
12
+ /** The three states a request moves through. */
13
+ export declare const WHOLESALE_REQUEST_STATES: readonly ["pending", "approved", "declined"];
14
+ /** `GET /customers?mode=wholesale-requests` page ceiling; the default page is 50. */
15
+ export declare const WHOLESALE_REQUESTS_PAGE_MAX = 100;
16
+ /** Character ceilings on the buyer's free text, counted after trimming. */
17
+ export declare const WHOLESALE_REQUEST_RAZON_SOCIAL_MAX = 200;
18
+ export declare const WHOLESALE_REQUEST_ACTIVITY_MAX = 120;
19
+ export declare const WHOLESALE_REQUEST_MESSAGE_MAX = 1000;
20
+ export declare const WHOLESALE_REQUEST_PHONE_MAX = 30;
21
+ /** Character ceiling on the operator's decline reason, counted after trimming. */
22
+ export declare const WHOLESALE_REQUEST_REASON_MAX = 500;
23
+ declare global {
24
+ type WholesaleRequestState = (typeof WHOLESALE_REQUEST_STATES)[number];
25
+ /**
26
+ * The company data a buyer submits. `cuit`, `razonSocial` and `condFiscal`
27
+ * are required; the rest is optional.
28
+ *
29
+ * ⚠️ PII under Ley 25.326. It reaches the operator queue and the buyer's own
30
+ * data export, and never a bell, a log line or an ERROR row.
31
+ */
32
+ interface WholesaleRequestCompany {
33
+ /** 11 digits with a valid check digit, stored digits-only. */
34
+ cuit: string;
35
+ razonSocial: string;
36
+ /** A `condFiscal` code from `FISCAL_CONDITIONS`. */
37
+ condFiscal: number;
38
+ activity?: string;
39
+ phone?: string;
40
+ message?: string;
41
+ }
42
+ /** Web `POST /user` body that submits a request. */
43
+ interface WholesaleRequestInput extends WholesaleRequestCompany {
44
+ mode: 'wholesale-request';
45
+ }
46
+ /**
47
+ * What the BUYER sees of their request: web `GET /user?mode=wholesale-request`
48
+ * (`data`, or `data: null` when they never asked), the submit's answer and
49
+ * the status frame. Never the decline reason, the deciding operator or the
50
+ * list it moved them to.
51
+ */
52
+ interface WholesaleRequestStatus {
53
+ status: WholesaleRequestState;
54
+ /** Unix ms the request was submitted. */
55
+ createdAt: number;
56
+ /** Unix ms of the decision. Absent while pending. */
57
+ decidedAt?: number;
58
+ }
59
+ /** The operator's view of one request (admin tier only). */
60
+ interface WholesaleRequest extends WholesaleRequestCompany {
61
+ customerId: string;
62
+ status: WholesaleRequestState;
63
+ createdAt: number;
64
+ updatedAt?: number;
65
+ decidedAt?: number;
66
+ /** The deciding operator's user id. */
67
+ decidedBy?: string;
68
+ /** On approval: the wholesale-class list the customer was moved to. */
69
+ priceList?: number;
70
+ /** On decline: the operator's note. Operator-only, never sent to the buyer. */
71
+ reason?: string;
72
+ }
73
+ /**
74
+ * Where the requesting customer stands now, read off the CUSTOMER row when
75
+ * the queue page is served. A request whose customer is gone is listed with
76
+ * that state, never dropped.
77
+ */
78
+ type WholesaleRequestCustomerState = 'active' | 'disabled' | 'missing' | 'anonymized' | 'merged';
79
+ /** One `GET /customers?mode=wholesale-requests` item. */
80
+ interface WholesaleRequestQueueEntry extends WholesaleRequest {
81
+ customer: {
82
+ state: WholesaleRequestCustomerState;
83
+ fullName?: string;
84
+ /** The customer's CURRENT stored list. */
85
+ priceList?: number;
86
+ };
87
+ }
88
+ /**
89
+ * `GET /customers?mode=wholesale-requests` answer. `LastEvaluatedKey` is an
90
+ * opaque cursor, sent back as `?startKey=`; present (with `truncated: true`)
91
+ * only while more rows may follow.
92
+ */
93
+ interface WholesaleRequestsQueueResponse {
94
+ message: string;
95
+ data: WholesaleRequestQueueEntry[];
96
+ LastEvaluatedKey?: string;
97
+ truncated?: boolean;
98
+ }
99
+ /**
100
+ * App `POST /customers` body that decides a request. `'approved'` requires a
101
+ * wholesale-class `priceList` and refuses a `reason`; `'declined'` refuses a
102
+ * `priceList`. `createdAt` is the request's own, as the queue served it: a
103
+ * request declined and resubmitted since is a different request, refused
104
+ * `409 WHOLESALE_REQUEST_NOT_PENDING` rather than decided unseen.
105
+ */
106
+ type WholesaleDecisionInput = {
107
+ mode: 'wholesale-decision';
108
+ customerId: string;
109
+ createdAt: number;
110
+ decision: 'approved';
111
+ priceList: number;
112
+ } | {
113
+ mode: 'wholesale-decision';
114
+ customerId: string;
115
+ createdAt: number;
116
+ decision: 'declined';
117
+ reason?: string;
118
+ };
119
+ /**
120
+ * App `POST /customers { mode: 'wholesale-decision' }` answer: the request as
121
+ * it now stands. `fiscalIdentityApplied` rides an APPROVAL only — `true` when
122
+ * the submitted CUIT, razón social and condición fiscal were written into
123
+ * `Customer.afip`; `false` when they were not, because the customer already
124
+ * held an `afip` entry or a top-level `cuit` naming another taxpayer (then
125
+ * neither `afip` nor `cuit` is written, and the list still moves).
126
+ */
127
+ interface WholesaleDecisionResponse {
128
+ message: string;
129
+ data: WholesaleRequest;
130
+ fiscalIdentityApplied?: boolean;
131
+ }
132
+ /**
133
+ * The `customers` frame a buyer's own socket receives when their request is
134
+ * decided. Carries no `priceList`: the CUSTOMER row's own `customers` frame
135
+ * carries that on approval.
136
+ */
137
+ interface WholesaleRequestStatusFrame {
138
+ customerId: string;
139
+ wholesaleRequest: WholesaleRequestStatus;
140
+ }
141
+ /**
142
+ * Which request a `CUSTOMER_WHOLESALE_REQUEST` operator bell is about. A
143
+ * POINTER, never an audience — the top-level `customerId` would deliver the
144
+ * bell to the buyer. Operator surfaces only.
145
+ */
146
+ interface WholesaleRequestRef {
147
+ customerId: string;
148
+ }
149
+ /**
150
+ * The request as it appears in the customer's own data export (both
151
+ * surfaces): the company data they submitted and the state, never the
152
+ * decline reason or the deciding operator.
153
+ */
154
+ interface WholesaleRequestExportEntry extends WholesaleRequestCompany {
155
+ /** The id the request was filed under — the subject's, or a merged-away id folded into it. */
156
+ customerId: string;
157
+ status: WholesaleRequestState;
158
+ createdAt: number;
159
+ decidedAt?: number;
160
+ }
161
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A signed-in storefront buyer's request for a wholesale account, and the
3
+ * operator queue that decides it.
4
+ *
5
+ * One row per customer (`WHOLESALEREQ#{storeId}` / `{customerId}`). A new
6
+ * request overwrites the previous one only once that one is decided, so a
7
+ * customer holds at most one pending request.
8
+ *
9
+ * The flow is offered only while the store's
10
+ * `ecommerce.config.wholesaleRequests` is `true` (absent = off).
11
+ */
12
+ /** The three states a request moves through. */
13
+ export const WHOLESALE_REQUEST_STATES = ['pending', 'approved', 'declined'];
14
+ /** `GET /customers?mode=wholesale-requests` page ceiling; the default page is 50. */
15
+ export const WHOLESALE_REQUESTS_PAGE_MAX = 100;
16
+ /** Character ceilings on the buyer's free text, counted after trimming. */
17
+ export const WHOLESALE_REQUEST_RAZON_SOCIAL_MAX = 200;
18
+ export const WHOLESALE_REQUEST_ACTIVITY_MAX = 120;
19
+ export const WHOLESALE_REQUEST_MESSAGE_MAX = 1000;
20
+ export const WHOLESALE_REQUEST_PHONE_MAX = 30;
21
+ /** Character ceiling on the operator's decline reason, counted after trimming. */
22
+ export const WHOLESALE_REQUEST_REASON_MAX = 500;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "platform-types",
3
- "version": "1.10.777",
3
+ "version": "1.10.778",
4
4
  "description": "Shared wire and entity contracts for the platform.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",