@pelygo/janus 0.1.2 → 0.3.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 (5) hide show
  1. package/README.md +597 -170
  2. package/USAGE.md +376 -0
  3. package/dist/index.d.ts +1898 -11
  4. package/dist/index.js +593 -253
  5. package/package.json +3 -2
package/README.md CHANGED
@@ -1,263 +1,690 @@
1
1
  # @pelygo/janus
2
2
 
3
- TypeScript API client for JANUS (VCOMSUITE backend) endpoints with full type safety.
3
+ TypeScript API client for JANUS (VCOMSUITE warehouse management backend). Provides typed access to orders, returns, products, stock, invoices, ASNs, dispatches, and more.
4
4
 
5
- ## Installation
5
+ ## Setup
6
+
7
+ ### Install
6
8
 
7
9
  ```bash
8
10
  npm install @pelygo/janus @pelygo/auth
9
11
  ```
10
12
 
11
- ## Quick Start
13
+ `@pelygo/auth` is a required peer dependency. It handles token storage and refresh automatically.
14
+
15
+ ### Configure
16
+
17
+ The JANUS API server and the auth server are **different hosts**. You must provide both URLs.
12
18
 
13
19
  ```typescript
14
20
  import { createJanusApi } from '@pelygo/janus';
15
21
 
16
- // Create API instance
17
22
  const janus = createJanusApi({
18
- baseUrl: 'https://api.janus.pelygo.com',
23
+ baseUrl: 'https://api.janus.pelygo.com', // JANUS API server
24
+ authUrl: 'https://auth.pelygo.com', // Auth server (different host)
19
25
  onUnauthorized: () => {
20
- // Handle session expiry (redirect to login, etc.)
26
+ // IMPORTANT: use setTimeout in Base44/iframe environments
27
+ setTimeout(() => window.location.href = '/login', 0);
21
28
  },
22
29
  });
23
-
24
- // Authenticate
25
- await janus.auth.login('username', 'password');
26
-
27
- // Use typed resources
28
- const clients = await janus.clients.getAll();
29
- const returns = await janus.returns.query({ clientId: 5, pageSize: 50 });
30
30
  ```
31
31
 
32
- ## API Reference
33
-
34
- ### Creating the Client
32
+ **Dev environment:**
35
33
 
36
34
  ```typescript
37
35
  const janus = createJanusApi({
38
- baseUrl: string; // JANUS API base URL
39
- onUnauthorized?: () => void; // Called on 401 responses
36
+ baseUrl: 'https://api.janus.dev.pelygo.com',
37
+ authUrl: 'https://auth.dev.pelygo.com',
40
38
  });
41
39
  ```
42
40
 
43
- ### Authentication
44
-
45
- The client wraps `@pelygo/auth` for authentication:
41
+ ### Authenticate
46
42
 
47
43
  ```typescript
48
- // Login
49
- await janus.auth.login(username, password);
50
-
51
- // Get current user
44
+ await janus.auth.login('username', 'password');
52
45
  const user = await janus.auth.getUser();
53
-
54
- // Logout
55
- await janus.auth.logout();
56
-
57
- // Check if authenticated
58
46
  const isLoggedIn = janus.auth.isAuthenticated();
47
+ await janus.auth.logout();
59
48
  ```
60
49
 
61
- ### Clients Resource
50
+ ---
51
+
52
+ ## All Available Resources
53
+
54
+ | Resource | Accessor | Auth | Description |
55
+ |----------|----------|------|-------------|
56
+ | Clients | `janus.clients` | Yes | Client account CRUD, courier services per client |
57
+ | Returns | `janus.returns` | Yes | Full returns management: CRUD, items, comments, statuses, reasons, categories, sources |
58
+ | Orders | `janus.orders` | Yes | Query orders, stats, filter options, create/update |
59
+ | Products | `janus.products` | Yes | Product CRUD (preferred over `legacy.queryProducts`) |
60
+ | Product Categories | `janus.productCategories` | Yes | Product category CRUD |
61
+ | Stocks | `janus.stocks` | Yes | Stock record CRUD |
62
+ | Stock Transactions | `janus.stockTransactions` | Yes | Stock transaction history |
63
+ | Stock Allocations | `janus.stockAllocations` | Yes | Stock allocation records |
64
+ | Locations | `janus.locations` | Yes | Warehouse location CRUD |
65
+ | Dispatches | `janus.dispatches` | Yes | Dispatch CRUD |
66
+ | Consignments | `janus.consignments` | Yes | Consignment records |
67
+ | Invoices | `janus.invoices` | Yes | Invoice CRUD with line items |
68
+ | ASNs | `janus.asns` | Yes | Advance Shipping Notices with lines and receipts |
69
+ | Couriers | `janus.couriers` | Yes | Courier and service management |
70
+ | Reports | `janus.reports` | Yes | Returns summary, reasons, overview reports (JSON + CSV) |
71
+ | Integrations | `janus.integrations` | Yes | Integration management |
72
+ | Users | `janus.users` | Yes | User management |
73
+ | Logs | `janus.logs` | Yes | Activity and order issue logs |
74
+ | Tasks | `janus.tasks` | Yes | Task history |
75
+ | Audits | `janus.audits` | Yes | Audit trail (read-only) |
76
+ | Legacy | `janus.legacy` | Yes | Legacy couriers, post services, products |
77
+ | Helpers | `janus.helpers` | No | Public endpoints: return portal form submission, notification settings |
78
+ | Customer | `janus.customer` | No | Public customer tracking (order lookup) |
79
+ | Auth | `janus.auth` | -- | Underlying AuthApi instance for raw requests |
80
+
81
+ ---
82
+
83
+ ## Entity Field Reference
84
+
85
+ ### Return
86
+
87
+ | Field | Type | Description |
88
+ |-------|------|-------------|
89
+ | `id` | `number` | Unique ID |
90
+ | `return_reference` | `string?` | Human-readable return reference |
91
+ | `wms_order_ref` | `string?` | Original order reference |
92
+ | `client_id` | `number?` | Client ID |
93
+ | `status_id` | `number?` | Current status ID |
94
+ | `reason_id` | `number?` | Return reason ID |
95
+ | `customername` | `string?` | Full customer name |
96
+ | `forename` | `string?` | First name |
97
+ | `surname` | `string?` | Last name |
98
+ | `email` | `string?` | Customer email |
99
+ | `tracking_number` | `string?` | Courier tracking number |
100
+ | `tracking_url` | `string?` | Tracking page URL |
101
+ | `approval_status` | `'pending' \| 'approved' \| 'rejected'?` | Approval state |
102
+ | `approved` | `boolean?` | Whether approved |
103
+ | `approved_ts` | `string?` | Approval timestamp |
104
+ | `line_1` | `string?` | Address line 1 |
105
+ | `line_2` | `string?` | Address line 2 |
106
+ | `line_3` | `string?` | Address line 3 |
107
+ | `city` | `string?` | City |
108
+ | `county` | `string?` | County/state |
109
+ | `country` | `string?` | Country name |
110
+ | `country_code` | `string?` | ISO country code (e.g. `"GB"`) |
111
+ | `postcode` | `string?` | Postal code |
112
+ | `company` | `string?` | Company name |
113
+ | `wms_service_cost` | `number?` | WMS service cost |
114
+ | `courier_service_cost` | `number?` | Courier service cost |
115
+ | `courier_service_id` | `number?` | Assigned courier service ID |
116
+ | `items` | `ReturnItem[]?` | Line items (when included) |
117
+ | `statuses` | `ReturnStatusHistory[]?` | Status history (when included) |
118
+ | `comments` | `ReturnComment[]?` | Comments (when included) |
119
+ | `latestStatus` | `ReturnStatusHistory?` | Most recent status |
120
+ | `created_ts` | `string?` | ISO timestamp |
121
+ | `updated_ts` | `string?` | ISO timestamp |
122
+
123
+ ### LegacyOrder
124
+
125
+ | Field | Type | Description |
126
+ |-------|------|-------------|
127
+ | `id` | `number` | Primary key |
128
+ | `customerId` | `number?` | FK to customer |
129
+ | `reference` | `string?` | Order reference string |
130
+ | `source` | `number?` | Source ID |
131
+ | `orderDate` | `string?` | ISO date the order was placed |
132
+ | `dispatchCompletedDate` | `string?` | ISO date all dispatches completed |
133
+ | `orderStatus` | `number?` | Numeric status code |
134
+ | `postServiceId` | `number?` | FK to shipping service |
135
+ | `courierId` | `number?` | FK to courier |
136
+ | `actualWeight` | `number?` | Measured weight |
137
+ | `notes` | `string?` | Internal notes |
138
+ | `hold` | `unknown?` | Whether order is on hold |
139
+ | `releaseDate` | `string?` | ISO date when hold was released |
140
+ | `draft` | `unknown?` | Whether order is a draft |
141
+ | `giftMessage` | `string?` | Gift message text |
142
+ | `deliveryInstructions` | `string?` | Delivery instructions |
143
+ | `orderIssue` | `number?` | Issue flag |
144
+ | `deliveryTerms` | `string?` | Incoterms code (e.g. `"DAP"`) |
145
+ | `transaction_type` | `'b2c' \| 'b2b' \| 'internal_transfer' \| 'damage' \| 'expiry'` | **Required.** Transaction classification |
146
+ | `external_order_id` | `string?` | External system order ID |
147
+ | `channel_order_id` | `string?` | Sales channel order ID |
148
+ | `channel` | `string?` | Sales channel name |
149
+ | `created` | `string?` | ISO created timestamp |
150
+ | `modified` | `string?` | ISO modified timestamp |
151
+ | `status` | `number?` | Record status (0 = inactive, 1 = active) |
152
+ | `orderItems` | `LegacyOrderItem[]` | Line items |
153
+ | `orderMeta` | `LegacyOrderMeta[]` | Metadata key-value pairs |
154
+ | `shopperDeliveryAddress` | `LegacyShopperAddress` | Delivery address |
155
+ | `shopperBillingAddress` | `LegacyShopperAddress` | Billing address |
156
+ | `dispatches` | `LegacyDispatch[]` | All dispatches for this order |
157
+ | `dispatch` | `LegacyDispatch` | Primary dispatch |
158
+ | `orderSource` | `Source` | Source reference data |
159
+
160
+ ### LegacyDispatch
161
+
162
+ | Field | Type | Description |
163
+ |-------|------|-------------|
164
+ | `id` | `number` | Primary key |
165
+ | `orderId` | `number?` | FK to parent order |
166
+ | `reference` | `string?` | Client-facing reference code |
167
+ | `dispatchDate` | `string?` | ISO date shipped |
168
+ | `dispatchStatus` | `number?` | 0 = pending, 1 = dispatched, 2 = cancelled |
169
+ | `trackingNumber` | `string?` | Carrier tracking number |
170
+ | `actualWeight` | `number?` | Measured weight |
171
+ | `courierId` | `number?` | FK to courier |
172
+ | `postServiceId` | `number?` | FK to shipping service |
173
+ | `notes` | `string?` | Internal notes |
174
+ | `hold` | `unknown?` | 0 = not held, 1 = on hold |
175
+ | `draft` | `unknown?` | 0 = finalised, 1 = draft |
176
+ | `deliveryInstructions` | `string?` | Special delivery instructions |
177
+ | `dispatchActionId` | `number?` | Current action (pick, pack, ship) |
178
+ | `dispatchActionStatus` | `number?` | 0 = not started, 1 = in progress, 2 = completed |
179
+ | `stockStatus` | `number?` | 0 = unallocated, 1 = partial, 2 = fully allocated |
180
+ | `dispatchIssue` | `number?` | 0 = no issue, 1 = issue flagged |
181
+ | `priority` | `number?` | Higher = higher priority |
182
+ | `complete` | `number?` | 0 = incomplete, 1 = complete |
183
+ | `complete_date` | `string?` | ISO date marked complete |
184
+ | `deliveryTerms` | `string?` | Incoterms code |
185
+ | `created` | `string?` | ISO created timestamp |
186
+ | `modified` | `string?` | ISO modified timestamp |
187
+ | `status` | `number?` | 0 = inactive/deleted, 1 = active |
188
+ | `dispatchItems` | `LegacyDispatchItem[]` | Line items in this dispatch |
189
+ | `shopperDeliveryAddress` | `LegacyShopperAddress` | Delivery address |
190
+ | `consignment` | `Consignment` | Consignment record |
191
+ | `postService` | `LegacyPostService` | Shipping service details |
192
+
193
+ ### FormattedProduct
194
+
195
+ Returned by `janus.products.getAll()`. Field names are snake_case (different from the raw `LegacyProduct` entity).
196
+
197
+ | Field | Type | Description |
198
+ |-------|------|-------------|
199
+ | `product_id` | `number` | Primary key |
200
+ | `client_id` | `number` | FK to owning client |
201
+ | `sku_code` | `string?` | SKU -- unique product identifier within the client |
202
+ | `product_name` | `string?` | Human-readable name |
203
+ | `description` | `string?` | Extended description |
204
+ | `additional_skus` | `string?` | Comma-separated alternative SKUs |
205
+ | `asins` | `string?` | Amazon Standard Identification Numbers |
206
+ | `custom_fields` | `Record<string, unknown>?` | Client-defined key-value pairs |
207
+ | `hs_code` | `string?` | Harmonized System code for customs |
208
+ | `unit_of_measure` | `string?` | e.g. `"each"`, `"kg"`, `"litre"` |
209
+ | `eaches_per_pack` | `number?` | Units per case pack |
210
+ | `case_pack_sku_code` | `string?` | SKU of the case pack |
211
+ | `weight_grams` | `number?` | Unit weight in grams |
212
+ | `barcode` | `string?` | Primary EAN/UPC barcode |
213
+ | `case_pack_barcode` | `string?` | Case pack barcode |
214
+ | `product_category_id` | `number?` | FK to product category |
215
+ | `product_type` | `string?` | e.g. `"physical"`, `"digital"`, `"service"` |
216
+ | `sellable_format` | `string?` | e.g. `"single"`, `"pack"`, `"case"` |
217
+ | `commodity_description` | `string?` | Customs commodity description |
218
+ | `price` | `number?` | Unit sale price |
219
+ | `currency` | `string?` | ISO 4217 code (e.g. `"GBP"`) |
220
+ | `country_of_origin` | `string?` | ISO 3166-1 alpha-2 (e.g. `"CN"`) |
221
+ | `primary_material` | `string?` | Primary material |
222
+ | `secondary_material` | `string?` | Secondary material |
223
+ | `dangerous_goods` | `string?` | DG classification / UN number |
224
+ | `image_url` | `string?` | Product image URL |
225
+ | `low_stock_threshold` | `number?` | Low-stock alert threshold |
226
+ | `requires_batch_tracking` | `number?` | 0 = no, 1 = yes |
227
+ | `requires_serial_tracking` | `number?` | 0 = no, 1 = yes |
228
+ | `requires_bbe_tracking` | `number?` | 0 = no, 1 = yes (best-before/expiry) |
229
+ | `is_bundle` | `number?` | 0 = standalone, 1 = bundle |
230
+ | `bundle_components` | `string?` | Component SKUs and quantities |
231
+ | `default_stock_status` | `string?` | Status assigned on receipt |
232
+ | `product_status` | `string?` | e.g. `"active"`, `"discontinued"` |
233
+ | `status` | `number?` | 0 = inactive/deleted, 1 = active |
234
+ | `created_date` | `string?` | ISO created timestamp |
235
+ | `updated_date` | `string?` | ISO updated timestamp |
236
+
237
+ ### Stock
238
+
239
+ Extends `BaseEntity` (adds `id`, `created_by`, `updated_by`, `created_ts`, `updated_ts`, `partition_id`).
240
+
241
+ | Field | Type | Description |
242
+ |-------|------|-------------|
243
+ | `id` | `number` | Primary key (from BaseEntity) |
244
+ | `clientId` | `number?` | FK to owning client |
245
+ | `sku_code` | `string?` | SKU identifying the product |
246
+ | `location` | `string?` | Location code (e.g. `"A-01-03-B"`) |
247
+ | `quantity` | `number?` | Units at this location |
248
+ | `batch_number` | `string?` | Batch/lot number |
249
+ | `serial_number` | `string?` | Serial number |
250
+ | `best_before_date` | `string?` | ISO expiry date (FEFO rotation) |
251
+ | `received_date` | `string?` | ISO date received into warehouse |
252
+ | `stock_status` | `'available' \| 'quarantined' \| 'expired' \| 'damaged' \| 'reserved'` | **Required.** Current status |
253
+ | `fulfilment_location_id` | `number?` | FK to fulfilment centre |
254
+ | `fulfilment_location_name` | `string?` | Fulfilment centre name |
255
+ | `fulfilment_location_country` | `string?` | ISO country code |
256
+ | `notes` | `string?` | Free-text notes |
257
+ | `created_ts` | `string?` | ISO created timestamp (from BaseEntity) |
258
+ | `updated_ts` | `string?` | ISO updated timestamp (from BaseEntity) |
259
+
260
+ ### Location
261
+
262
+ Extends `BaseEntity`.
263
+
264
+ | Field | Type | Description |
265
+ |-------|------|-------------|
266
+ | `id` | `number` | Primary key |
267
+ | `clientId` | `number?` | FK to owning client |
268
+ | `location_code` | `string?` | Unique code (e.g. `"A-01-03-B"`) |
269
+ | `location_type` | `'pick' \| 'bulk' \| 'staging' \| 'receiving' \| 'shipping' \| 'quarantine' \| 'returns'` | **Required.** Location role |
270
+ | `zone` | `string?` | Warehouse zone (e.g. `"A"`) |
271
+ | `aisle` | `string?` | Aisle identifier |
272
+ | `bay` | `string?` | Bay/rack identifier |
273
+ | `level` | `string?` | Vertical level/shelf |
274
+ | `position` | `string?` | Horizontal position |
275
+ | `fulfilment_location_id` | `number?` | FK to fulfilment centre |
276
+ | `fulfilment_location_name` | `string?` | Fulfilment centre name |
277
+ | `current_units` | `number?` | Stock units currently held |
278
+ | `max_capacity_units` | `number?` | Maximum unit capacity |
279
+ | `max_capacity_weight_kg` | `number?` | Maximum weight (kg) |
280
+ | `is_mixed_sku_allowed` | `number?` | 0 = single SKU only, 1 = mixed |
281
+ | `is_active` | `number?` | 0 = decommissioned, 1 = in use |
282
+ | `is_pickable` | `number?` | 0 = no, 1 = yes |
283
+ | `is_receivable` | `number?` | 0 = no, 1 = yes |
284
+ | `temperature_zone` | `'ambient' \| 'chilled' \| 'frozen'` | **Required.** Temperature classification |
285
+ | `notes` | `string?` | Free-text notes |
286
+
287
+ ### Invoice
288
+
289
+ Extends `BaseEntity`.
290
+
291
+ | Field | Type | Description |
292
+ |-------|------|-------------|
293
+ | `id` | `number` | Primary key |
294
+ | `clientId` | `number?` | FK to client |
295
+ | `invoice_date` | `string` | **Required.** ISO invoice date |
296
+ | `xero_reference` | `string?` | Xero accounting reference |
297
+ | `total` | `number?` | Total invoice amount |
298
+ | `emailed` | `number?` | 0 = not emailed, 1 = emailed |
299
+ | `paid` | `number?` | 0 = unpaid, 1 = paid |
300
+ | `fuel_mode` | `number?` | 0 = flat rate, 1 = percentage |
301
+ | `status` | `number?` | 0 = inactive/deleted, 1 = active |
302
+ | `invoiceLines` | `InvoiceLine[]?` | Line items |
303
+
304
+ ### InvoiceLine
305
+
306
+ Extends `BaseEntity`.
307
+
308
+ | Field | Type | Description |
309
+ |-------|------|-------------|
310
+ | `id` | `number` | Primary key |
311
+ | `invoice_id` | `number?` | FK to parent invoice |
312
+ | `class_id` | `number?` | Charge classification (storage, fulfilment, shipping) |
313
+ | `object_id` | `number?` | FK to billable object (dispatch ID, etc.) |
314
+ | `reference` | `string` | **Required.** Client-facing reference |
315
+ | `description` | `string?` | Charge description |
316
+ | `dispatch_date` | `string?` | ISO dispatch date |
317
+ | `service_name` | `string?` | Shipping service name |
318
+ | `price` | `number?` | Unit price |
319
+ | `discount_rate` | `number?` | Discount percentage |
320
+ | `postcode` | `string?` | Destination postcode |
321
+ | `country_code` | `string?` | ISO destination country |
322
+ | `tax_class` | `number?` | 0 = zero-rated, 1 = standard, 2 = exempt |
323
+ | `consignment_price` | `number?` | Carrier cost before margin |
324
+ | `fuel` | `number?` | Fuel surcharge |
325
+ | `packaging_price` | `number?` | Packaging material cost |
326
+ | `discount` | `number?` | Calculated discount amount |
327
+ | `lines` | `number?` | Number of order lines |
328
+ | `picks` | `number?` | Number of picks |
329
+ | `weight` | `number?` | Total weight (kg) |
330
+ | `margin_charged` | `number?` | Margin on carrier cost |
331
+ | `total_override` | `number?` | Manual total override |
332
+ | `status` | `number?` | 0 = inactive/deleted, 1 = active |
333
+
334
+ ### ASN (Advance Shipping Notice)
335
+
336
+ Extends `BaseEntity`.
337
+
338
+ | Field | Type | Description |
339
+ |-------|------|-------------|
340
+ | `id` | `number` | Primary key |
341
+ | `clientId` | `number` | **Required.** FK to client |
342
+ | `asn_status` | `string` | **Required.** One of: `draft`, `awaiting_arrival`, `arrived`, `processing`, `receipted`, `receipted_with_discrepancies`, `temporarily_receipted`, `quarantined`, `cancelled` |
343
+ | `expected_arrival_date` | `string?` | ISO expected arrival date |
344
+ | `total_expected_units` | `number?` | Total expected units |
345
+ | `asnLines` | `AsnLine[]` | Line items |
346
+
347
+ ### AsnLine
348
+
349
+ Extends `BaseEntity`.
350
+
351
+ | Field | Type | Description |
352
+ |-------|------|-------------|
353
+ | `id` | `number` | Primary key |
354
+ | `asn_id` | `number` | **Required.** FK to parent ASN |
355
+ | `product_id` | `number?` | FK to product |
356
+ | `sku_code` | `string` | **Required.** SKU code |
357
+ | `expected_quantity` | `number` | **Required.** Expected unit count |
358
+ | `received_quantity` | `number?` | Actual received count |
359
+ | `asnReceipts` | `AsnReceipt[]` | Receipt records |
360
+
361
+ ### AsnReceipt
362
+
363
+ Extends `BaseEntity`.
364
+
365
+ | Field | Type | Description |
366
+ |-------|------|-------------|
367
+ | `id` | `number` | Primary key |
368
+ | `asn_id` | `number` | **Required.** FK to ASN |
369
+ | `asn_line_id` | `number` | **Required.** FK to ASN line |
370
+ | `received_quantity` | `number` | **Required.** Quantity received |
371
+ | `stock_status` | `'available' \| 'quarantined' \| 'expired' \| 'damaged' \| 'reserved'` | **Required.** Status of received stock |
372
+
373
+ ---
374
+
375
+ ## Query Filter Reference
376
+
377
+ ### PaginatedQueryFilters (v2 endpoints: products, stocks, locations, invoices, ASNs, dispatches)
378
+
379
+ | Param | Type | Required | Description |
380
+ |-------|------|----------|-------------|
381
+ | `clientId` | `number` | **Yes** | Client ID from `useClient()` |
382
+ | `pageSize` | `number` | No | Results per page (default: 25) |
383
+ | `pageNumber` | `number` | No | Page number, 1-based (default: 1) |
384
+ | `orderColumn` | `string` | No | Column to sort by (e.g. `'id'`, `'created'`) |
385
+ | `orderDirection` | `'ASC' \| 'DESC'` | No | Sort direction (default: DESC) |
386
+ | `query` | `string` | No | Free-text search across key fields |
387
+
388
+ ### ReturnsQueryFilters (janus.returns.query)
389
+
390
+ | Param | Type | Required | Description |
391
+ |-------|------|----------|-------------|
392
+ | `clientId` | `number` | **Yes** | Client ID |
393
+ | `page` | `number` | No | Page number |
394
+ | `pageSize` | `number` | No | Results per page |
395
+ | `created_at` | `string` | No | Start date (ISO, e.g. `'2025-01-01'`) |
396
+ | `created_to` | `string` | No | End date (ISO) |
397
+ | `last_status_ids` | `string` | No | Comma-separated status IDs to match current status |
398
+ | `include_status_ids` | `string` | No | Include returns with any of these status IDs |
399
+ | `exclude_status_ids` | `string` | No | Exclude returns with these status IDs |
400
+ | `processed_at` | `string` | No | Processed start date (ISO) |
401
+ | `processed_to` | `string` | No | Processed end date (ISO) |
402
+ | `include_associations` | `string` | No | Nested data: `'return_items'`, `'return_comments'`, or comma-separated |
403
+ | `csv` | `boolean` | No | Export as CSV instead of JSON |
404
+ | `group_by` | `string` | No | Group results by field |
405
+
406
+ ### OrdersQueryFilters (janus.orders.getAll)
407
+
408
+ | Param | Type | Required | Description |
409
+ |-------|------|----------|-------------|
410
+ | `clientId` | `number` | **Yes** | Client ID |
411
+ | `page` | `number` | No | Page number |
412
+ | `pageSize` | `number` | No | Results per page |
413
+ | `order_column` | `string` | No | Sort column |
414
+ | `order_direction` | `'ASC' \| 'DESC'` | No | Sort direction |
415
+ | `created_at` | `string` | No | Start date (ISO) |
416
+ | `created_to` | `string` | No | End date (ISO) |
417
+ | `channel` | `string` | No | Filter by sales channel |
418
+ | `destination` | `string` | No | Filter by destination |
419
+ | `courier` | `string` | No | Filter by courier name |
420
+ | `service` | `string` | No | Filter by courier service |
421
+ | `status` | `string` | No | Filter by order status |
422
+ | `query` | `string` | No | Free-text search |
423
+ | `include_all_stages` | `boolean` | No | Include all order stages |
424
+
425
+ ### ReturnsSummaryFilters (janus.reports.returnsSummary / returnsSummaryCsv)
426
+
427
+ | Param | Type | Required | Description |
428
+ |-------|------|----------|-------------|
429
+ | `clientId` | `number` | **Yes** | Client ID |
430
+ | `createdFrom` | `string` | No | Start date (ISO) |
431
+ | `createdTo` | `string` | No | End date (ISO) |
432
+ | `approvedFrom` | `string` | No | Approval start date |
433
+ | `approvedTo` | `string` | No | Approval end date |
434
+ | `courierServiceId` | `number` | No | Filter by courier service |
435
+ | `approved` | `boolean` | No | Only approved returns |
436
+ | `isClientsReport` | `boolean` | No | Format as client-level report |
437
+ | `page` | `number` | No | Page number |
438
+ | `pageSize` | `number` | No | Results per page |
439
+
440
+ ### ReturnsReasonsFilters (janus.reports.returnsReasons / returnsReasonsCsv)
441
+
442
+ | Param | Type | Required | Description |
443
+ |-------|------|----------|-------------|
444
+ | `clientId` | `number` | **Yes** | Client ID |
445
+ | `createdFrom` | `string` | No | Start date (ISO) |
446
+ | `createdTo` | `string` | No | End date (ISO) |
447
+
448
+ ### OrdersStatsFilters (janus.orders.getStats)
449
+
450
+ | Param | Type | Required | Description |
451
+ |-------|------|----------|-------------|
452
+ | `clientId` | `number` | **Yes** | Client ID |
453
+ | `created_at` | `string` | No | Start date (ISO) |
454
+ | `created_to` | `string` | No | End date (ISO) |
455
+ | `dashboard` | `boolean` | No | Return dashboard-formatted stats (recommended: `true`) |
456
+
457
+ ### Resource-specific extra filters
458
+
459
+ | Resource method | Extra param | Type | Description |
460
+ |----------------|-----------|------|-------------|
461
+ | `janus.stockTransactions.getAll` | `stockId` | `number` | Filter by stock record |
462
+ | `janus.stockAllocations.getAll` | `orderId` | `number` | Filter by order |
463
+ | `janus.dispatches.getAll` | `orderId` | `number` | Filter by order |
464
+ | `janus.audits.getAll` | `filter` | `string` | Additional filter |
465
+ | `janus.users.getAll` | `filterRole` | `string` | Filter by user role |
466
+ | `janus.users.getAll` | `filterType` | `string` | Filter by user type |
467
+ | `janus.users.getAll` | `filterClientId` | `number` | Filter by assigned client |
468
+
469
+ ---
470
+
471
+ ## Common Patterns
472
+
473
+ ### Querying with Filters and Pagination
474
+
475
+ **v2 endpoints** (products, stocks, locations, invoices, ASNs, dispatches) use `ClientScopedPaginatedQueryFilters`:
62
476
 
63
477
  ```typescript
64
- // Get all clients
65
- const clients = await janus.clients.getAll();
66
-
67
- // Get single client
68
- const client = await janus.clients.getById(5);
69
-
70
- // Update client
71
- const updated = await janus.clients.update(5, { name: 'New Name' });
478
+ const { data, meta } = await janus.products.getAll({
479
+ clientId: 5, // REQUIRED
480
+ pageSize: 50,
481
+ pageNumber: 1,
482
+ orderColumn: 'sku_code',
483
+ orderDirection: 'ASC',
484
+ query: 'widget', // free-text search
485
+ });
72
486
 
73
- // Get courier services for client
74
- const services = await janus.clients.getCourierServices(5);
75
- const service = await janus.clients.getCourierServiceById(5, serviceId);
76
- await janus.clients.updateCourierService(5, serviceId, { active: true });
487
+ // meta = { total_items, total_pages, current_page, page_size }
77
488
  ```
78
489
 
79
- ### Returns Resource
490
+ **Legacy endpoints** (orders, returns) use their own filter shapes:
80
491
 
81
492
  ```typescript
82
- // Query returns with filters
83
493
  const returns = await janus.returns.query({
84
494
  clientId: 5,
85
495
  last_status_ids: '1,2,3',
86
496
  created_at: '2024-01-01',
87
497
  created_to: '2024-12-31',
88
498
  pageSize: 100,
499
+ csv: false,
89
500
  });
90
501
 
91
- // Get single return with items
92
- const ret = await janus.returns.getById(123, { includeAssociations: true });
93
-
94
- // Create a return
95
- const newReturn = await janus.returns.create({
96
- client_id: 5,
97
- customer_name: 'John Doe',
98
- // ...
502
+ const orders = await janus.orders.getAll({
503
+ clientId: 5,
504
+ created_at: '2024-01-01',
505
+ created_to: '2024-12-31',
506
+ status: 'dispatched',
507
+ order_column: 'created',
508
+ order_direction: 'DESC',
99
509
  });
510
+ ```
100
511
 
101
- // Update a return
102
- await janus.returns.update(123, { customer_name: 'Jane Doe' });
103
-
104
- // Delete a return
105
- await janus.returns.delete(123);
106
-
107
- // Manage statuses
108
- await janus.returns.addStatus(123, statusId);
109
- await janus.returns.removeStatus(123, statusId);
110
- const statuses = await janus.returns.getStatuses(123);
111
-
112
- // Manage comments
113
- await janus.returns.addComment(123, 'Comment text');
114
- await janus.returns.removeComment(123, commentId);
115
- const comments = await janus.returns.getComments(123);
116
-
117
- // Manage items
118
- const items = await janus.returns.getItems(123);
119
- await janus.returns.addItem(123, { wms_product_sku: 'SKU123', qty: 2 });
120
- await janus.returns.updateItem(123, itemId, { qty: 3 });
512
+ ### CSV Export
121
513
 
122
- // Approval workflow
123
- await janus.returns.setApproval(123, true); // Approve
124
- await janus.returns.setApproval(123, false); // Reject
514
+ Reports have dedicated CSV methods:
125
515
 
126
- // Reference data
127
- const reasons = await janus.returns.getReasons();
128
- const allStatuses = await janus.returns.getAllStatuses();
129
- const filterOptions = await janus.returns.getFilterOptions(5);
516
+ ```typescript
517
+ const csvString = await janus.reports.returnsSummaryCsv({ clientId: 5 });
518
+ const reasonsCsv = await janus.reports.returnsReasonsCsv({ clientId: 5 });
519
+ const overviewCsv = await janus.reports.returnsOverviewCsv({ clientId: 5 });
130
520
  ```
131
521
 
132
- ### Reports Resource
522
+ Returns can also export as CSV via the query filter:
133
523
 
134
524
  ```typescript
135
- // Returns summary report
136
- const summary = await janus.reports.returnsSummary({
137
- clientId: 5,
138
- created_at: '2024-01-01',
139
- created_to: '2024-12-31',
140
- groupBy: 'month',
141
- });
142
-
143
- // CSV export
144
- const csvData = await janus.reports.returnsSummaryCsv({ clientId: 5 });
525
+ const csv = await janus.returns.query({ clientId: 5, csv: true });
526
+ ```
145
527
 
146
- // Returns by reason
147
- const reasonReport = await janus.reports.returnsReasons({ clientId: 5 });
148
- const reasonCsv = await janus.reports.returnsReasonsCsv({ clientId: 5 });
528
+ ### CRUD on Sub-Resources
149
529
 
150
- // Returns overview
151
- const overview = await janus.reports.returnsOverview({ clientId: 5 });
152
- const overviewCsv = await janus.reports.returnsOverviewCsv({ clientId: 5 });
530
+ Invoices, ASNs, and returns have nested sub-resources:
153
531
 
154
- // Statistics
155
- const stats = await janus.reports.returnsStats(5);
532
+ ```typescript
533
+ // Invoice lines
534
+ const { data: lines } = await janus.invoices.getLines(invoiceId, { clientId: 5 });
535
+ await janus.invoices.createLine(invoiceId, lineData);
536
+ await janus.invoices.updateLine(invoiceId, lineId, updatedData);
537
+
538
+ // ASN lines and receipts
539
+ const { data: asnLines } = await janus.asns.getLines(asnId, { clientId: 5 });
540
+ await janus.asns.createReceipt(asnId, lineId, receiptData);
541
+
542
+ // Return items, comments, statuses
543
+ const items = await janus.returns.getItems(returnId);
544
+ await janus.returns.addComment(returnId, 'Inspected, item damaged');
545
+ await janus.returns.addStatus(returnId, statusId);
156
546
  ```
157
547
 
158
- ### Helpers Resource (Public Endpoints)
548
+ ### Paginated Response Shape
159
549
 
160
- ```typescript
161
- // Submit return portal form (no auth required)
162
- const result = await janus.helpers.submitReturnPortalForm(formData);
550
+ v2 endpoints return:
163
551
 
164
- // Get notification settings
165
- const settings = await janus.helpers.getNotificationSettings(clientId);
552
+ ```typescript
553
+ {
554
+ data: T[],
555
+ meta: {
556
+ total_items: number,
557
+ total_pages: number,
558
+ current_page: number,
559
+ page_size: number,
560
+ }
561
+ }
166
562
  ```
167
563
 
168
- ## Types
564
+ ### Raw API Access
169
565
 
170
- All entity types are exported and can be imported:
566
+ For endpoints not yet wrapped:
171
567
 
172
568
  ```typescript
173
- import type {
174
- Client,
175
- Return,
176
- ReturnItem,
177
- ReturnStatus,
178
- ReturnReason,
179
- User,
180
- // ... more types
181
- } from '@pelygo/janus';
569
+ const result = await janus.auth.get('/some/endpoint?clientId=5');
570
+ const result = await janus.auth.post('/some/endpoint', { body: 'data' });
182
571
  ```
183
572
 
184
- ### Filter Types
573
+ ---
185
574
 
186
- ```typescript
187
- import type { ReturnsQueryParams, ReportFilters } from '@pelygo/janus';
575
+ ## Important Rules
188
576
 
189
- const filters: ReturnsQueryParams = {
190
- clientId: 5,
191
- last_status_ids: '1,2',
192
- pageSize: 50,
193
- };
194
- ```
577
+ 1. **Always pass `clientId`.**
578
+ Get it from `useClient()` or equivalent. Never omit it.
579
+ - New v2 endpoints (`products`, `stocks`, `locations`, `invoices`, `asns`, `dispatches`) **reject with 400** if `clientId` is missing.
580
+ - Older endpoints (`orders`, `returns`) silently return **all clients' data** if omitted -- this is a data leak, not a feature.
195
581
 
196
- ## Development
582
+ 2. **Use the exact field names from this README.**
583
+ Field names differ between entities (e.g. `sku_code` not `sku`, `product_name` not `name`, `price` not `value`). Do not guess -- refer to the tables above.
197
584
 
198
- ```bash
199
- # Install dependencies
200
- npm install
585
+ 3. **Navigation in Base44/iframe environments must use `setTimeout`.**
586
+ Direct `window.location` or router calls inside callbacks (like `onUnauthorized`) will fail silently in iframed environments:
587
+ ```typescript
588
+ onUnauthorized: () => {
589
+ setTimeout(() => window.location.href = '/login', 0);
590
+ },
591
+ ```
201
592
 
202
- # Run tests
203
- npm run test
593
+ 4. **`baseUrl` and `authUrl` are different servers.**
594
+ Do not pass the same URL for both. The JANUS API server handles data; the auth server handles tokens.
204
595
 
205
- # Run tests once
206
- npm run test:run
596
+ 5. **Use `janus.products` instead of `janus.legacy.queryProducts`.**
597
+ The `products` resource uses the newer v2 endpoint with proper pagination and typed responses.
207
598
 
208
- # Type check
209
- npm run typecheck
599
+ ---
210
600
 
211
- # Build
212
- npm run build
601
+ ## API Behaviour Notes
213
602
 
214
- # Generate entity types from v2 TypeORM entities
215
- npm run generate-types
603
+ ### Error responses
604
+ ```json
605
+ { "statusCode": 400, "message": "Error description" }
216
606
  ```
607
+ Common codes: 400 (bad request/validation), 401 (unauthorized), 404 (not found).
217
608
 
218
- ### Integration Tests
609
+ ### Date formats
610
+ - **Query params:** ISO 8601 format (`'2025-01-01'` or `'2025-01-01 12:00:00'`)
611
+ - **Response fields:** ISO 8601 timestamps (e.g. `created_ts`, `orderDate`, `created`)
612
+ - **CSV exports:** formatted as `DD-MM-YYYY HH:mm`
219
613
 
220
- To run integration tests against a real JANUS API, set environment variables:
614
+ ### Pagination defaults
615
+ - Default `pageSize`: 50 (v2 endpoints)
616
+ - Default `pageNumber`: 1 (1-based indexing)
617
+ - Default sort: DESC (newest first)
618
+ - Max sensible page size: 1,000
221
619
 
222
- ```bash
223
- export JANUS_API_URL="https://api.janus.dev.pelygo.com"
224
- export JANUS_TEST_USERNAME="testuser"
225
- export JANUS_TEST_PASSWORD="testpass"
226
- export JANUS_TEST_CLIENT_ID="5"
620
+ ### Search behaviour (`query` param)
621
+ Free-text search is case-insensitive LIKE matching across key text columns:
622
+ - **Products:** `sku`, `name`, `description`
623
+ - **Returns:** order references, tracking numbers
624
+ - **Clients:** client names
625
+ - **Orders:** order references, channel IDs
227
626
 
228
- npm run test:integration
229
- ```
627
+ ### Returns-specific behaviour
628
+ - **Archived returns** (status ID 10) are excluded by default — pass `include_archived: true` to include
629
+ - **Return types:** `0` = regular return, `1` = exchange — filter with `return_types: '0'` or `'1'`
630
+ - **Associations:** `include_associations: 'return_items'` loads line items; `'return_items,return_comments'` loads both
631
+ - **Approval statuses:** `'pending'`, `'approved'`, `'rejected'`
230
632
 
231
- ## Architecture
633
+ ### Stats responses
634
+ `getStats()` endpoints return flat objects with numeric values. **Keys vary by client data.** Always use `Object.entries(stats)` to iterate — never hardcode expected keys.
232
635
 
233
- ```
234
- src/
235
- ├── index.ts # Main exports
236
- ├── client.ts # createJanusApi() factory
237
- ├── types/
238
- │ ├── entities/ # Generated entity interfaces
239
- │ ├── filters.ts # Query filter types
240
- │ └── responses.ts # API response types
241
- ├── resources/
242
- │ ├── clients.ts # Clients API
243
- │ ├── returns.ts # Returns API
244
- │ ├── reports.ts # Reports API
245
- │ └── helpers.ts # Public endpoints
246
- └── utils/
247
- └── query-builder.ts # URL query string builder
636
+ ---
637
+
638
+ ## Type Imports
639
+
640
+ ```typescript
641
+ import type {
642
+ // Entities
643
+ Return, ReturnItem, ReturnStatus, ReturnReason, ReturnComment,
644
+ LegacyOrder, LegacyDispatch, LegacyOrderItem, LegacyDispatchItem,
645
+ LegacyShopperAddress,
646
+ FormattedProduct,
647
+ Stock,
648
+ Location,
649
+ Invoice, InvoiceLine,
650
+ Asn, AsnLine, AsnReceipt,
651
+ Client,
652
+
653
+ // Filters
654
+ ReturnsQueryFilters,
655
+ OrdersQueryFilters,
656
+ ClientScopedPaginatedQueryFilters,
657
+ PaginatedQueryFilters,
658
+ ReturnsSummaryFilters,
659
+
660
+ // Responses
661
+ PaginatedListResponse,
662
+ PaginatedResponse,
663
+ PaginationMeta,
664
+ ReturnsStatsResponse,
665
+ FilterOptionsResponse,
666
+
667
+ // API types
668
+ JanusApi,
669
+ JanusApiOptions,
670
+ } from '@pelygo/janus';
248
671
  ```
249
672
 
250
- ## Base44 Integration
673
+ ---
251
674
 
252
- For Base44 applications, see the **[Base44 Integration Guide](docs/BASE44_GUIDE.md)** which includes:
675
+ ## Development
253
676
 
254
- - Installation and setup for Base44
255
- - AI prompt examples for generating pages
256
- - Common patterns and code snippets
677
+ ```bash
678
+ npm run build # Build with Vite
679
+ npm run typecheck # TypeScript check
680
+ npm run test # Run tests with Vitest
681
+ npm run test:run # Run tests once
682
+ npm run generate-types # Generate entity types from v2 TypeORM entities
683
+ ```
257
684
 
258
685
  ## Related Packages
259
686
 
260
- - [@pelygo/auth](https://www.npmjs.com/package/@pelygo/auth) - Authentication client (peer dependency)
687
+ - [@pelygo/auth](https://www.npmjs.com/package/@pelygo/auth) -- Authentication client (peer dependency)
261
688
 
262
689
  ## License
263
690