@sales-planner/http-client 0.11.0 → 0.13.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.
Files changed (49) hide show
  1. package/README.md +302 -277
  2. package/dist/clients/api-keys-client.d.ts +2 -2
  3. package/dist/clients/api-keys-client.d.ts.map +1 -1
  4. package/dist/clients/api-keys-client.js +2 -2
  5. package/dist/clients/brands-client.d.ts +12 -11
  6. package/dist/clients/brands-client.d.ts.map +1 -1
  7. package/dist/clients/brands-client.js +15 -12
  8. package/dist/clients/categories-client.d.ts +12 -11
  9. package/dist/clients/categories-client.d.ts.map +1 -1
  10. package/dist/clients/categories-client.js +15 -12
  11. package/dist/clients/groups-client.d.ts +12 -11
  12. package/dist/clients/groups-client.d.ts.map +1 -1
  13. package/dist/clients/groups-client.js +15 -12
  14. package/dist/clients/marketplaces-client.d.ts +12 -11
  15. package/dist/clients/marketplaces-client.d.ts.map +1 -1
  16. package/dist/clients/marketplaces-client.js +15 -12
  17. package/dist/clients/roles-client.d.ts +3 -3
  18. package/dist/clients/roles-client.d.ts.map +1 -1
  19. package/dist/clients/roles-client.js +4 -4
  20. package/dist/clients/sales-history-client.d.ts +11 -11
  21. package/dist/clients/sales-history-client.d.ts.map +1 -1
  22. package/dist/clients/sales-history-client.js +12 -12
  23. package/dist/clients/sales-planner-client.d.ts +0 -109
  24. package/dist/clients/sales-planner-client.d.ts.map +1 -1
  25. package/dist/clients/sales-planner-client.js +1 -109
  26. package/dist/clients/shops-client.d.ts +3 -3
  27. package/dist/clients/shops-client.d.ts.map +1 -1
  28. package/dist/clients/shops-client.js +4 -4
  29. package/dist/clients/skus-client.d.ts +12 -11
  30. package/dist/clients/skus-client.d.ts.map +1 -1
  31. package/dist/clients/skus-client.js +15 -12
  32. package/dist/clients/statuses-client.d.ts +12 -11
  33. package/dist/clients/statuses-client.d.ts.map +1 -1
  34. package/dist/clients/statuses-client.js +15 -12
  35. package/dist/clients/suppliers-client.d.ts +12 -36
  36. package/dist/clients/suppliers-client.d.ts.map +1 -1
  37. package/dist/clients/suppliers-client.js +16 -13
  38. package/dist/clients/tenants-client.d.ts +4 -4
  39. package/dist/clients/tenants-client.d.ts.map +1 -1
  40. package/dist/clients/tenants-client.js +6 -6
  41. package/dist/clients/user-roles-client.d.ts +4 -2
  42. package/dist/clients/user-roles-client.d.ts.map +1 -1
  43. package/dist/clients/user-roles-client.js +16 -2
  44. package/dist/clients/users-client.d.ts +2 -2
  45. package/dist/clients/users-client.d.ts.map +1 -1
  46. package/dist/clients/users-client.js +2 -2
  47. package/dist/index.d.ts +1 -1
  48. package/dist/index.d.ts.map +1 -1
  49. package/package.json +2 -2
package/README.md CHANGED
@@ -17,7 +17,7 @@ import { SalesPlannerClient } from '@sales-planner/http-client';
17
17
 
18
18
  const client = new SalesPlannerClient({
19
19
  baseUrl: 'https://sales-planner-back.vercel.app',
20
- getAuthToken: () => 'your-api-key',
20
+ apiKey: 'your-api-key',
21
21
  });
22
22
 
23
23
  // Check if API is healthy
@@ -25,339 +25,314 @@ const health = await client.getHealth();
25
25
  console.log(health); // { status: 'ok', version: '1.0.0' }
26
26
  ```
27
27
 
28
- ### API Styles
28
+ ### Shop Context
29
29
 
30
- The client supports two complementary API styles:
30
+ Most operations require a shop context (`ctx`) as the **first argument**:
31
31
 
32
- #### 1. **Namespaced API** (Recommended)
32
+ ```typescript
33
+ const ctx = { tenant_id: 1, shop_id: 1 };
34
+
35
+ // All entity methods follow: client.<entity>.<method>(ctx, ...args)
36
+ const skus = await client.skus.getSkus(ctx);
37
+ const sku = await client.skus.getSku(ctx, 123);
38
+ const brand = await client.brands.getBrandByCode(ctx, 'apple');
39
+ ```
40
+
41
+ ### Namespaced Sub-Clients
33
42
 
34
43
  Access resources through domain-specific sub-clients:
35
44
 
36
45
  ```typescript
37
- // Users
46
+ // Users (system-level, no ctx)
38
47
  const users = await client.users.getUsers();
39
48
  const user = await client.users.getUser(1);
40
49
 
41
- // Tenants & Shops
50
+ // Tenants (system-level, no ctx)
42
51
  const tenants = await client.tenants.getTenants();
43
- const shops = await client.shops.getShops(tenantId);
44
52
 
45
- // SKUs with import/export
46
- const skus = await client.skus.getSkus({ tenantId, shopId });
47
- await client.skus.importSkusJson(items, { tenantId, shopId });
48
- const csv = await client.skus.exportSkusCsv({ tenantId, shopId });
49
-
50
- // Brands with import/export
51
- const brands = await client.brands.getBrands({ tenantId, shopId });
52
- await client.brands.importBrandsJson(items, { tenantId, shopId });
53
- const brandsCsv = await client.brands.exportBrandsCsv({ tenantId, shopId });
53
+ // Shops (tenant-level)
54
+ const shops = await client.shops.getShops(tenantId);
54
55
 
55
- // Categories with import/export
56
- const categories = await client.categories.getCategories({ tenantId, shopId });
57
- await client.categories.importCategoriesJson(items, { tenantId, shopId });
56
+ // Shop-scoped entities (require ctx)
57
+ const ctx = { tenant_id: 1, shop_id: 1 };
58
58
 
59
- // Groups with import/export
60
- const groups = await client.groups.getGroups({ tenantId, shopId });
61
- await client.groups.importGroupsJson(items, { tenantId, shopId });
59
+ // SKUs
60
+ const skus = await client.skus.getSkus(ctx);
61
+ await client.skus.createSku(ctx, { code: 'SKU-001', title: 'Product 1' });
62
+ await client.skus.importJson(ctx, items);
63
+ const csv = await client.skus.exportCsv(ctx);
62
64
 
63
- // Statuses with import/export
64
- const statuses = await client.statuses.getStatuses({ tenantId, shopId });
65
- await client.statuses.importStatusesJson(items, { tenantId, shopId });
65
+ // Brands
66
+ const brands = await client.brands.getBrands(ctx);
67
+ await client.brands.importJson(ctx, [{ code: 'apple', title: 'Apple' }]);
66
68
 
67
- // Sales History
68
- const history = await client.salesHistory.getSalesHistory(
69
- { tenantId, shopId },
70
- { start: '2024-01', end: '2024-12' }
71
- );
69
+ // Categories, Groups, Statuses, Suppliers - same pattern
70
+ const categories = await client.categories.getCategories(ctx);
71
+ const groups = await client.groups.getGroups(ctx);
72
+ const statuses = await client.statuses.getStatuses(ctx);
73
+ const suppliers = await client.suppliers.getSuppliers(ctx);
72
74
 
73
75
  // Marketplaces
74
- const marketplaces = await client.marketplaces.getMarketplaces({ tenantId });
76
+ const marketplaces = await client.marketplaces.getMarketplaces(ctx);
75
77
 
76
- // Entity Metadata (for UI documentation)
78
+ // Sales History (with optional period filter)
79
+ const history = await client.salesHistory.getSalesHistory(ctx, {
80
+ start: '2024-01',
81
+ end: '2024-12',
82
+ });
83
+
84
+ // Entity Metadata (no auth required)
77
85
  const metadata = await client.metadata.getEntitiesMetadata();
78
- // Returns field definitions for brands, categories, groups, statuses, marketplaces, skus, sales history
79
86
  ```
80
87
 
81
- **Benefits:**
82
- - Clear domain separation
83
- - IDE autocomplete by domain
84
- - Easier to discover related methods
88
+ ## API Conventions
85
89
 
86
- #### 2. **Flat API** (Backward Compatible)
90
+ ### Argument Order
87
91
 
88
- Access all methods directly on the client:
92
+ All methods follow a consistent pattern: **context first, then other arguments**.
89
93
 
90
94
  ```typescript
91
- // Backward compatible with existing code
92
- const users = await client.getUsers();
93
- const user = await client.getUser(1);
94
- const skus = await client.getSkus({ tenantId, shopId });
95
- ```
95
+ // Get by ID
96
+ await client.skus.getSku(ctx, id);
97
+ await client.brands.getBrand(ctx, id);
96
98
 
97
- ## Import/Export Pattern
99
+ // Get by code
100
+ await client.skus.getSkuByCode(ctx, code);
101
+ await client.brands.getBrandByCode(ctx, code);
98
102
 
99
- Resources that support bulk operations (SKUs, Brands, Categories, Groups, Statuses, Sales History, Marketplaces) follow a consistent pattern:
103
+ // Create
104
+ await client.skus.createSku(ctx, request);
105
+ await client.brands.createBrand(ctx, request);
100
106
 
101
- ```typescript
102
- // Import from JSON
103
- const result = await client.skus.importSkusJson(
104
- [
105
- { code: 'SKU-001', title: 'Product 1' },
106
- { code: 'SKU-002', title: 'Product 2' },
107
- ],
108
- { tenantId, shopId }
109
- );
110
- // Returns: { inserted: 2, updated: 0, errors: [] }
111
-
112
- // Import from CSV file
113
- const csvContent = await fs.readFile('skus.csv', 'utf-8');
114
- const result = await client.skus.importSkusCsv(csvContent, { tenantId, shopId });
107
+ // Update
108
+ await client.skus.updateSku(ctx, id, request);
109
+ await client.brands.updateBrand(ctx, id, request);
115
110
 
116
- // Export to CSV
117
- const csv = await client.skus.exportSkusCsv({ tenantId, shopId });
111
+ // Delete
112
+ await client.skus.deleteSku(ctx, id);
113
+ await client.brands.deleteBrand(ctx, id);
118
114
 
119
- // Get example templates (no auth required)
120
- const exampleCsv = await client.skus.getSkusExampleCsv();
121
-
122
- // Same pattern works for brands, categories, groups, statuses
123
- const brandResult = await client.brands.importBrandsJson(
124
- [{ code: 'apple', title: 'Apple' }],
125
- { tenantId, shopId }
126
- );
127
-
128
- const categoryResult = await client.categories.importCategoriesJson(
129
- [{ code: 'electronics', title: 'Electronics' }],
130
- { tenantId, shopId }
131
- );
132
-
133
- const groupResult = await client.groups.importGroupsJson(
134
- [{ code: 'smartphones', title: 'Smartphones' }],
135
- { tenantId, shopId }
136
- );
137
-
138
- const statusResult = await client.statuses.importStatusesJson(
139
- [{ code: 'active', title: 'Active' }],
140
- { tenantId, shopId }
141
- );
115
+ // Import
116
+ await client.skus.importJson(ctx, items);
117
+ await client.skus.importCsv(ctx, csvContent);
118
+
119
+ // Export
120
+ await client.skus.exportJson(ctx);
121
+ await client.skus.exportCsv(ctx);
142
122
  ```
143
123
 
144
- ## Data Requirements
124
+ ### IDs vs Codes
145
125
 
146
- ### SKUs
126
+ - **Create/Update operations** use numeric IDs for references (e.g., `marketplace_id`)
127
+ - **Import/Export operations** use string codes for human readability (e.g., `marketplace: 'amazon'`)
128
+
129
+ ```typescript
130
+ // Create uses IDs
131
+ await client.salesHistory.createSalesHistory(ctx, {
132
+ sku_id: 123,
133
+ marketplace_id: 1,
134
+ period: '2026-01',
135
+ quantity: 100,
136
+ });
147
137
 
148
- **Create/Import:**
149
- - `code` (required): String, 1-100 characters, unique per shop
150
- - `title` (required): String, 1-200 characters
138
+ // Import uses codes (auto-resolves to IDs)
139
+ await client.salesHistory.importJson(ctx, [
140
+ { sku: 'SKU-001', marketplace: 'amazon', period: '2026-01', quantity: 100 },
141
+ ]);
151
142
 
152
- **Update:**
153
- - `code` (optional): String, 1-100 characters
154
- - `title` (optional): String, 1-200 characters
143
+ // Export returns codes
144
+ const exported = await client.salesHistory.exportJson(ctx);
145
+ // [{ sku: 'SKU-001', marketplace: 'amazon', period: '2026-01', quantity: 100 }]
146
+ ```
155
147
 
156
- ### Brands
148
+ ## Import/Export Pattern
157
149
 
158
- **Create/Import:**
159
- - `code` (required): String, 1-100 characters, unique per shop
160
- - `title` (required): String, 1-200 characters
150
+ All shop-scoped entities support bulk import/export:
161
151
 
162
- **Update:**
163
- - `code` (optional): String, 1-100 characters
164
- - `title` (optional): String, 1-200 characters
152
+ ```typescript
153
+ const ctx = { tenant_id: 1, shop_id: 1 };
165
154
 
166
- ### Categories
155
+ // Import from JSON array
156
+ const result = await client.skus.importJson(ctx, [
157
+ { code: 'SKU-001', title: 'Product 1' },
158
+ { code: 'SKU-002', title: 'Product 2' },
159
+ ]);
160
+ console.log(result); // { created: 2, updated: 0, errors: [] }
167
161
 
168
- **Create/Import:**
169
- - `code` (required): String, 1-100 characters, unique per shop
170
- - `title` (required): String, 1-200 characters
162
+ // Import from CSV string
163
+ const csvContent = `code,title
164
+ SKU-001,Product 1
165
+ SKU-002,Product 2`;
166
+ const result = await client.skus.importCsv(ctx, csvContent);
171
167
 
172
- **Update:**
173
- - `code` (optional): String, 1-100 characters
174
- - `title` (optional): String, 1-200 characters
168
+ // Export to JSON
169
+ const items = await client.skus.exportJson(ctx);
175
170
 
176
- ### Groups
171
+ // Export to CSV
172
+ const csv = await client.skus.exportCsv(ctx);
177
173
 
178
- **Create/Import:**
179
- - `code` (required): String, 1-100 characters, unique per shop
180
- - `title` (required): String, 1-200 characters
174
+ // Get example templates (no auth required)
175
+ const exampleJson = await client.skus.getExampleJson();
176
+ const exampleCsv = await client.skus.getExampleCsv();
177
+ ```
181
178
 
182
- **Update:**
183
- - `code` (optional): String, 1-100 characters
184
- - `title` (optional): String, 1-200 characters
179
+ **Supported entities:** SKUs, Brands, Categories, Groups, Statuses, Suppliers, Marketplaces, Sales History
185
180
 
186
- ### Statuses
181
+ ## Data Requirements
187
182
 
188
- **Create/Import:**
189
- - `code` (required): String, 1-100 characters, unique per shop
190
- - `title` (required): String, 1-200 characters
183
+ ### SKUs
191
184
 
192
- **Update:**
193
- - `code` (optional): String, 1-100 characters
194
- - `title` (optional): String, 1-200 characters
185
+ | Field | Create/Import | Update | Export |
186
+ |-------|---------------|--------|--------|
187
+ | `code` | Required, unique per shop | Optional | ✓ |
188
+ | `title` | Required | Optional | ✓ |
189
+ | `title2` | Optional | Optional | ✓ |
190
+ | `category` | Optional (code) | - | code |
191
+ | `group` | Optional (code) | - | code |
192
+ | `status` | Optional (code) | - | code |
193
+ | `supplier` | Optional (code) | - | code |
195
194
 
196
- ### Sales History
195
+ ### Brands, Categories, Groups, Statuses, Suppliers
197
196
 
198
- **Create:**
199
- - `sku_id` (required): Number, must reference existing SKU
200
- - `period` (required): String, format `YYYY-MM` (e.g., "2026-01")
201
- - `quantity` (required): Number, integer
202
- - `marketplace_id` (required): Number, must reference existing marketplace
197
+ | Field | Create/Import | Update | Export |
198
+ |-------|---------------|--------|--------|
199
+ | `code` | Required, unique per shop | Optional | ✓ |
200
+ | `title` | Required | Optional | ✓ |
203
201
 
204
- **Import (CSV/JSON):**
205
- - `sku_code` (required): String, must match existing SKU or will be auto-created
206
- - `period` (required): String, format `YYYY-MM` (e.g., "2026-01")
207
- - `quantity` (required): Number, integer
208
- - `marketplace` (required): String (marketplace code), must match existing marketplace or will be auto-created
202
+ ### Marketplaces
209
203
 
210
- Note: Import endpoints accept marketplace codes for convenience, but the API internally uses numeric marketplace IDs. Export also returns marketplace codes.
204
+ | Field | Create/Import | Update | Export |
205
+ |-------|---------------|--------|--------|
206
+ | `code` | Required, unique per shop | Optional | ✓ |
207
+ | `title` | Required | Optional | ✓ |
211
208
 
212
- **Update:**
213
- - `sku_id` (optional): Number
214
- - `period` (optional): String, format `YYYY-MM`
215
- - `quantity` (optional): Number
216
- - `marketplace_id` (optional): Number
209
+ ### Sales History
217
210
 
218
- ### Users
211
+ | Field | Create | Import | Update | Export |
212
+ |-------|--------|--------|--------|--------|
213
+ | `sku_id` | Required | - | Optional | - |
214
+ | `sku` | - | Required (code) | - | code |
215
+ | `marketplace_id` | Required | - | Optional | - |
216
+ | `marketplace` | - | Required (code) | - | code |
217
+ | `period` | Required (YYYY-MM) | Required | Optional | ✓ |
218
+ | `quantity` | Required | Required | Optional | ✓ |
219
219
 
220
- **Create:**
221
- - `email` (required): String, valid email format, unique
222
- - `name` (required): String, 1-200 characters
223
- - `default_shop_id` (optional): Number
220
+ ### Users
224
221
 
225
- **Update:**
226
- - `email` (optional): String, valid email format
227
- - `name` (optional): String, 1-200 characters
228
- - `default_shop_id` (optional): Number or null
222
+ | Field | Create | Update |
223
+ |-------|--------|--------|
224
+ | `email` | Required, unique | Optional |
225
+ | `name` | Required | Optional |
226
+ | `default_shop_id` | Optional | Optional (null to clear) |
229
227
 
230
228
  ### Tenants
231
229
 
232
- **Create:**
233
- - `title` (required): String, 1-200 characters
234
- - `owner_id` (optional): Number, user ID
235
-
236
- **Update:**
237
- - `title` (optional): String, 1-200 characters
238
- - `owner_id` (optional): Number or null
230
+ | Field | Create | Update |
231
+ |-------|--------|--------|
232
+ | `title` | Required | Optional |
233
+ | `owner_id` | Optional | Optional (null to clear) |
239
234
 
240
235
  ### Shops
241
236
 
242
- **Create:**
243
- - `title` (required): String, 1-200 characters
244
- - `tenant_id` (required): Number
245
-
246
- **Update:**
247
- - `title` (optional): String, 1-200 characters
248
-
249
- ### Marketplaces
250
-
251
- **Create:**
252
- - `code` (required): String, 1-100 characters, unique (e.g., "amazon", "ebay")
253
- - `title` (required): String, 1-200 characters
237
+ | Field | Create | Update |
238
+ |-------|--------|--------|
239
+ | `title` | Required | Optional |
240
+ | `tenant_id` | Required | - |
254
241
 
255
- **Update:**
256
- - `code` (optional): String, 1-100 characters
257
- - `title` (optional): String, 1-200 characters
242
+ ## Available Sub-Clients
258
243
 
259
- ## Error Handling
260
-
261
- ```typescript
262
- import { ApiError } from '@sales-planner/http-client';
263
-
264
- try {
265
- await client.users.getUser(999);
266
- } catch (error) {
267
- if (error instanceof ApiError) {
268
- console.error(`API Error ${error.status}: ${error.message}`);
269
- }
270
- }
271
- ```
272
-
273
- Common HTTP status codes:
274
- - `409 Conflict` - Duplicate resource (email, SKU code, sales period)
275
- - `404 Not Found` - Resource not found
276
- - `403 Forbidden` - Insufficient permissions
277
- - `400 Bad Request` - Validation error
278
-
279
- ```typescript
280
- try {
281
- await client.createUser({ email: 'user@example.com', name: 'John' });
282
- } catch (error) {
283
- if (error.response?.status === 409) {
284
- console.error('User with this email already exists');
285
- }
286
- }
287
- ```
288
-
289
- ## Available Methods
244
+ ### System-Level (no context required)
290
245
 
291
- ### Authentication
246
+ #### `client.me`
292
247
  - `getMe()` - Get current user with roles and tenants
293
248
 
294
- ### Users
295
- - `getUsers()`, `getUser(id)` - System admin or tenant admin (filtered by their tenants)
296
- - `createUser(dto)`, `deleteUser(id)` - Tenant admin or higher
297
-
298
- ### Tenants
299
- - `getTenants()`, `getTenant(id)` - All authenticated users
300
- - `createTenant(dto)` - System admin only
301
- - `updateTenant(id, dto)`, `deleteTenant(id)` - Authenticated users (validation happens server-side)
302
- - `createTenantWithShopAndUser(dto)` - System admin only
303
-
304
- ### Shops
305
- - `getShops(tenantId?)`, `getShop(id)`, `createShop(dto)`, `updateShop(id, dto)`, `deleteShop(id)` - Requires tenant access
249
+ #### `client.users`
250
+ - `getUsers()` - List users (system admin sees all, tenant admin sees their tenants)
251
+ - `getUser(id)` - Get user by ID
252
+ - `createUser(request)` - Create user
253
+ - `updateUser(id, request)` - Update user
254
+ - `deleteUser(id)` - Delete user
255
+
256
+ #### `client.tenants`
257
+ - `getTenants()` - List tenants
258
+ - `getTenant(id)` - Get tenant by ID
259
+ - `createTenant(request)` - Create tenant (system admin only)
260
+ - `updateTenant(id, request)` - Update tenant
261
+ - `deleteTenant(id)` - Delete tenant
262
+ - `createTenantWithShopAndUser(request)` - Create tenant with initial shop and user
263
+
264
+ #### `client.shops`
265
+ - `getShops(tenantId?)` - List shops (optionally filtered by tenant)
266
+ - `getShop(id)` - Get shop by ID
267
+ - `createShop(request)` - Create shop
268
+ - `updateShop(id, request)` - Update shop
269
+ - `deleteShop(id)` - Delete shop
306
270
  - `deleteShopData(id)` - Delete all data (SKUs, sales history) for a shop
307
271
 
308
- ### SKUs
309
- - `getSkus(ctx)`, `getSku(id, ctx)` - Requires read access (viewer or higher)
310
- - `createSku(dto, ctx)`, `updateSku(id, dto, ctx)`, `deleteSku(id, ctx)` - Requires write access (editor or higher)
311
- - `importSkusJson(items, ctx)`, `importSkusCsv(csvContent, ctx)` - Requires write access
312
- - `exportSkusJson(ctx)`, `exportSkusCsv(ctx)` - Requires read access
313
- - `getSkusExampleJson()`, `getSkusExampleCsv()` - Get import format examples (no auth required)
314
-
315
- ### Brands
316
- - `getBrands(ctx)`, `getBrand(id, ctx)` - Requires read access (viewer or higher)
317
- - `createBrand(dto, ctx)`, `updateBrand(id, dto, ctx)`, `deleteBrand(id, ctx)` - Requires write access (editor or higher)
318
- - `importBrandsJson(items, ctx)`, `importBrandsCsv(csvContent, ctx)` - Requires write access (bulk upsert by code)
319
- - `exportBrandsJson(ctx)`, `exportBrandsCsv(ctx)` - Requires read access
320
- - `getBrandsExampleJson()`, `getBrandsExampleCsv()` - Get import format examples (no auth required)
321
-
322
- ### Categories
323
- - `getCategories(ctx)`, `getCategory(id, ctx)` - Requires read access (viewer or higher)
324
- - `createCategory(dto, ctx)`, `updateCategory(id, dto, ctx)`, `deleteCategory(id, ctx)` - Requires write access (editor or higher)
325
- - `importCategoriesJson(items, ctx)`, `importCategoriesCsv(csvContent, ctx)` - Requires write access (bulk upsert by code)
326
- - `exportCategoriesJson(ctx)`, `exportCategoriesCsv(ctx)` - Requires read access
327
- - `getExampleCategoriesJson()`, `getExampleCategoriesCsv()` - Get import format examples (no auth required)
328
-
329
- ### Groups
330
- - `getGroups(ctx)`, `getGroup(id, ctx)` - Requires read access (viewer or higher)
331
- - `createGroup(dto, ctx)`, `updateGroup(id, dto, ctx)`, `deleteGroup(id, ctx)` - Requires write access (editor or higher)
332
- - `importGroupsJson(items, ctx)`, `importGroupsCsv(csvContent, ctx)` - Requires write access (bulk upsert by code)
333
- - `exportGroupsJson(ctx)`, `exportGroupsCsv(ctx)` - Requires read access
334
- - `getExampleGroupsJson()`, `getExampleGroupsCsv()` - Get import format examples (no auth required)
335
-
336
- ### Statuses
337
- - `getStatuses(ctx)`, `getStatus(id, ctx)` - Requires read access (viewer or higher)
338
- - `createStatus(dto, ctx)`, `updateStatus(id, dto, ctx)`, `deleteStatus(id, ctx)` - Requires write access (editor or higher)
339
- - `importStatusesJson(items, ctx)`, `importStatusesCsv(csvContent, ctx)` - Requires write access (bulk upsert by code)
340
- - `exportStatusesJson(ctx)`, `exportStatusesCsv(ctx)` - Requires read access
341
- - `getExampleStatusesJson()`, `getExampleStatusesCsv()` - Get import format examples (no auth required)
342
-
343
- ### Sales History
344
- - `getSalesHistory(ctx, query?)`, `getSalesHistoryItem(id, ctx)` - Requires read access (viewer or higher)
345
- - `createSalesHistory(dto, ctx)`, `updateSalesHistory(id, dto, ctx)`, `deleteSalesHistory(id, ctx)` - Requires write access (editor or higher)
346
- - `importSalesHistoryJson(items, ctx)`, `importSalesHistoryCsv(csvContent, ctx)` - Requires write access
347
- - `exportSalesHistoryJson(ctx, query?)`, `exportSalesHistoryCsv(ctx, query?)` - Requires read access
348
- - `getSalesHistoryExampleJson()`, `getSalesHistoryExampleCsv()` - Get import format examples (no auth required)
349
-
350
- ### Roles & User Roles
351
- - `getRoles()`, `getRole(id)` - System admin only
352
- - `createRole(dto)`, `updateRole(id, dto)`, `deleteRole(id)` - System admin only
353
- - `createUserRole(dto)`, `deleteUserRole(id)` - Tenant admin or higher for their tenant
354
-
355
- ### API Keys
356
- - `getApiKeys()`, `createApiKey(dto)`, `deleteApiKey(id)` - System admin only
357
-
358
- ### Marketplaces
359
- - `getMarketplaces()`, `getMarketplace(id)` - All authenticated users
360
- - `createMarketplace(dto)`, `updateMarketplace(id, dto)`, `deleteMarketplace(id)` - Requires write access (editor or higher)
272
+ #### `client.roles`
273
+ - `getRoles()` - List roles (system admin only)
274
+ - `getRole(id)` - Get role by ID
275
+
276
+ #### `client.userRoles`
277
+ - `getUserRoles(query)` - List user roles
278
+ - `getUserRole(id)` - Get user role by ID
279
+ - `createUserRole(request)` - Create user role
280
+ - `deleteUserRole(id)` - Delete user role
281
+
282
+ #### `client.apiKeys`
283
+ - `getApiKeys()` - List API keys (system admin only)
284
+ - `createApiKey(request)` - Create API key
285
+ - `deleteApiKey(id)` - Delete API key
286
+
287
+ #### `client.metadata`
288
+ - `getEntitiesMetadata()` - Get field definitions for all entities (no auth)
289
+
290
+ ### Shop-Scoped (context required)
291
+
292
+ All shop-scoped clients follow the same pattern:
293
+
294
+ #### `client.skus`
295
+ - `getSkus(ctx)` - List SKUs
296
+ - `getSku(ctx, id)` - Get SKU by ID
297
+ - `getSkuByCode(ctx, code)` - Get SKU by code
298
+ - `createSku(ctx, request)` - Create SKU
299
+ - `updateSku(ctx, id, request)` - Update SKU
300
+ - `deleteSku(ctx, id)` - Delete SKU
301
+ - `importJson(ctx, items)` - Import from JSON array
302
+ - `importCsv(ctx, csvContent)` - Import from CSV string
303
+ - `exportJson(ctx)` - Export to JSON
304
+ - `exportCsv(ctx)` - Export to CSV
305
+ - `getExampleJson()` - Get JSON import example (no auth)
306
+ - `getExampleCsv()` - Get CSV import example (no auth)
307
+
308
+ #### `client.brands`, `client.categories`, `client.groups`, `client.statuses`, `client.suppliers`
309
+
310
+ Same methods as `client.skus`:
311
+ - `get<Entity>s(ctx)`, `get<Entity>(ctx, id)`, `get<Entity>ByCode(ctx, code)`
312
+ - `create<Entity>(ctx, request)`, `update<Entity>(ctx, id, request)`, `delete<Entity>(ctx, id)`
313
+ - `importJson(ctx, items)`, `importCsv(ctx, csvContent)`
314
+ - `exportJson(ctx)`, `exportCsv(ctx)`
315
+ - `getExampleJson()`, `getExampleCsv()`
316
+
317
+ #### `client.marketplaces`
318
+ - `getMarketplaces(ctx)` - List marketplaces
319
+ - `getMarketplace(ctx, id)` - Get marketplace by ID
320
+ - `getMarketplaceByCode(ctx, code)` - Get marketplace by code
321
+ - `createMarketplace(ctx, request)` - Create marketplace
322
+ - `updateMarketplace(ctx, id, request)` - Update marketplace
323
+ - `deleteMarketplace(ctx, id)` - Delete marketplace
324
+ - `importJson(ctx, items)`, `importCsv(ctx, csvContent)` - Import
325
+ - `exportJson(ctx)`, `exportCsv(ctx)` - Export
326
+
327
+ #### `client.salesHistory`
328
+ - `getSalesHistory(ctx, query?)` - List sales history (optional period filter)
329
+ - `getSalesHistoryItem(ctx, id)` - Get sales history item by ID
330
+ - `createSalesHistory(ctx, request)` - Create sales history
331
+ - `updateSalesHistory(ctx, id, request)` - Update sales history
332
+ - `deleteSalesHistory(ctx, id)` - Delete sales history
333
+ - `importJson(ctx, items)`, `importCsv(ctx, csvContent)` - Import
334
+ - `exportJson(ctx, query?)`, `exportCsv(ctx, query?)` - Export (optional period filter)
335
+ - `getExampleJson()`, `getExampleCsv()` - Examples (no auth)
361
336
 
362
337
  ## Error Handling
363
338
 
@@ -365,7 +340,7 @@ try {
365
340
  import { SalesPlannerClient, ApiError } from '@sales-planner/http-client';
366
341
 
367
342
  try {
368
- const user = await client.getUser(999);
343
+ const user = await client.users.getUser(999);
369
344
  } catch (error) {
370
345
  if (error instanceof ApiError) {
371
346
  console.log(error.status); // 404
@@ -374,20 +349,70 @@ try {
374
349
  }
375
350
  ```
376
351
 
352
+ **Common HTTP status codes:**
353
+ - `400 Bad Request` - Validation error
354
+ - `401 Unauthorized` - Missing or invalid API key
355
+ - `403 Forbidden` - Insufficient permissions
356
+ - `404 Not Found` - Resource not found
357
+ - `409 Conflict` - Duplicate resource (email, code, period)
358
+
377
359
  ## Types
378
360
 
379
- All entity types, DTOs, and response types are exported:
361
+ Types are re-exported from `@sales-planner/shared`:
380
362
 
381
363
  ```typescript
382
- import type {
383
- User, Tenant, Shop, Sku, Brand, Category, Group, Status, SalesHistory,
384
- CreateUserDto, CreateTenantDto, CreateShopDto,
385
- CreateSkuRequest, CreateBrandRequest, CreateCategoryRequest,
386
- CreateGroupRequest, CreateStatusRequest,
387
- ShopContextParams, PeriodQuery,
388
- UserWithRolesAndTenants, ImportResult,
389
- SkuExportItem, BrandExportItem, CategoryExportItem,
390
- GroupExportItem, StatusExportItem
364
+ import type {
365
+ // Context
366
+ ShopContextParams,
367
+ PeriodQuery,
368
+
369
+ // Entities
370
+ User,
371
+ Tenant,
372
+ Shop,
373
+ Sku,
374
+ Brand,
375
+ Category,
376
+ Group,
377
+ Status,
378
+ Supplier,
379
+ Marketplace,
380
+ SalesHistory,
381
+ Role,
382
+ UserRole,
383
+ ApiKey,
384
+
385
+ // Request types (for create/update)
386
+ CreateUserRequest,
387
+ UpdateUserRequest,
388
+ CreateTenantRequest,
389
+ UpdateTenantRequest,
390
+ CreateShopRequest,
391
+ UpdateShopRequest,
392
+ CreateSkuRequest,
393
+ UpdateSkuRequest,
394
+ CreateBrandRequest,
395
+ UpdateBrandRequest,
396
+ CreateSalesHistoryRequest,
397
+ UpdateSalesHistoryRequest,
398
+ // ... etc
399
+
400
+ // Import types (for import operations)
401
+ ImportSkuItem,
402
+ ImportBrandItem,
403
+ ImportSalesHistoryItem,
404
+ // ... etc
405
+
406
+ // Response types
407
+ UserWithRolesAndTenants,
408
+ TenantWithShopAndApiKey,
409
+ ImportResult,
410
+ SkuImportResult,
411
+ DeleteDataResult,
412
+
413
+ // Export types
414
+ SkuExportItem,
415
+ SalesHistoryExportItem,
391
416
  } from '@sales-planner/http-client';
392
417
  ```
393
418