react-shopwave-connect 0.4.1 → 0.4.2

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/README.md CHANGED
@@ -149,7 +149,7 @@ await deleteCategory(created.id);
149
149
  | product | `saveProduct`, `deleteProduct`, `fetchProduct` | `/api/products[/:id]` |
150
150
  | category | `saveCategory`, `deleteCategory`, `fetchCategory` | `/api/categories[/:id]` |
151
151
  | store | `saveStore`, `deleteStore`, `fetchStore` | `/api/stores[/:id]` |
152
- | promotion | `savePromotion`, `deletePromotion` (ends it), `fetchPromotion` | `/api/promotions[/:id]` |
152
+ | promotion | `savePromotion`, `deletePromotion` (expires it), `fetchPromotion` | `/api/promotions[/:id]` |
153
153
  | employee | `saveEmployee`, `deleteEmployee` (retires), `fetchEmployee` | `/api/employees[/:id]` |
154
154
  | consumer | `fetchConsumer` only — read-only in the API | `/api/consumer[/:id]` (GET) |
155
155
 
@@ -158,7 +158,7 @@ How they behave (matches what the Shopwave API does):
158
158
  - **Save** sends `{ <collection>: { "0": entity } }`. Shopwave upserts (an `id` means update), answers **201** and echoes the entity under the same ref with its `id` and server fields. The function checks `api.message.errors` and that the ref came back, and returns the saved entity (fields the echo leaves out are kept from your input). `saveEntities(kind, [a, b])` saves several at once and matches results back by ref.
159
159
  - **Delete** is a soft delete (read it back with `deleted: true`). Shopwave answers **205 with an empty body — also for ids that don't exist**, so a resolved promise means "accepted", not "a record was deleted".
160
160
  - **Read one** calls `GET /api/<route>/:id` and returns the record or `null`. List reads return `[]` when nothing matches (Shopwave answers with an empty body).
161
- - **Promotions:** Shopwave has no promotion DELETE; `deletePromotion` (and `DELETE /api/promotions/:id`) ends the promotion by setting `endDate` to now.
161
+ - **Promotions:** promotions are never deleted, only expired. `deletePromotion` (and `DELETE /api/promotions/:id`) reads the promotion and saves the whole record back with `endDate` set to now; one that hasn't started yet gets `startDate` = `endDate` = now. (The API rejects a partial `{ id, endDate }` save with error 899.) Dates go as UTC `YYYY-MM-DD HH:mm:ss`.
162
162
  - **Employees:** Shopwave has no employee DELETE, so `deleteEmployee` (and `DELETE /api/employees/:id`) retires the employee by setting `exitDate`. Updating an existing employee only changes `roleId`, `joinedDate` and `exitDate` — names and email are fixed — and the echo carries only those fields, so re-read with `fetchEmployee` if you need the stored record.
163
163
  - **Consumers** are read-only (`GET /consumer` by `ids`); their write/delete routes answer 405.
164
164
  - **Product prices (instances) can't be updated** — the API keeps them for history; a price change means a new instance with a new id. Build `instances` with `toProductInstancesForSave(formInstances, productAsLastRead)`: it keys them `"0"…"n"`, keeps an instance's `id` only when the original has that id with the same price and tax, and drops it otherwise (and drops server `timestamp`). `findRepricedInstances(formInstances, productAsLastRead)` lists the instances that will get new ids, so you can warn first. `saveProduct` without an `id` (a new product or a duplicate) always strips instance ids, so a copy can't point at the original's prices.
@@ -472,7 +472,7 @@ export const { POST } = shopwave.upload(); // or upload({ maxBytes,
472
472
  - `resource("merchant")`: `PUT`/`POST` take `{ merchant: {…} }` or the bare object and send every field on as `postBody`. The `id` is required (400 without it), because a POST without one would create a second merchant. `resource("user")` answers 405 to writes.
473
473
  - `upload()`: takes multipart `file` + `kind`, checks the size (default 10 MB → 413) and type (default `image/*` → 415), and `PUT`s only the file to `/uploader` with the `contentType` header. `extras` are ignored on uploads. Shopwave's reply (201 `{ fileName, path }`) is passed through.
474
474
 
475
- Entities: `product`, `category`, `store`, `promotion`, `employee` (DELETE retires via `exitDate`), `promotion` (DELETE ends it via `endDate`), `consumer` (mounted at `/api/consumer`; GET only, writes answer 405). For anything else, `shopwave.forward(request, { method, path, headers, postBody })` does the same token/refresh/encoding work for a custom handler.
475
+ Entities: `product`, `category`, `store`, `promotion`, `employee` (DELETE retires via `exitDate`), `promotion` (DELETE expires it: the whole record saved back with `endDate`, and a not-yet-started `startDate`, set to now), `consumer` (mounted at `/api/consumer`; GET only, writes answer 405). For anything else, `shopwave.forward(request, { method, path, headers, postBody })` does the same token/refresh/encoding work for a custom handler.
476
476
 
477
477
  Every handler:
478
478
 
@@ -484,7 +484,10 @@ async function submitEntity({ endpoint, method = "POST", payload }, options = {}
484
484
  * (https://developer.merchantstack.com/api-reference.html) and checked against
485
485
  * the live API where noted:
486
486
  * - category/product/store: GET filtered by `<x>Ids`, POST upsert, DELETE with `<x>Id` → 205.
487
- * - promotion: no DELETE; "deleting" a promotion ends it by setting `endDate` to now.
487
+ * - promotion: no DELETE; promotions are never deleted, only expired. Expiring
488
+ * reads the promotion and saves the whole record back with `endDate` = now (and
489
+ * `startDate` = now too if it hadn't started); the live API rejects a partial
490
+ * `{ id, endDate }` save with error 899. Dates are UTC `YYYY-MM-DD HH:mm:ss`.
488
491
  * - employee: POST updates `roleId`, `joinedDate`, `exitDate` (names can't be
489
492
  * changed); no DELETE (live API: "Cannot DELETE /employee") → retired via `exitDate`.
490
493
  * - consumer: read-only (`GET` with `ids`, comma-separated); live POST → 404.
@@ -525,7 +528,9 @@ const SHOPWAVE_ENTITIES = Object.freeze({
525
528
  idHeader: "promotionId",
526
529
  writable: true,
527
530
  deleteMode: "retire",
528
- retireField: "endDate"
531
+ retireField: "endDate",
532
+ retireWithRecord: true,
533
+ retireStartField: "startDate"
529
534
  },
530
535
  employee: {
531
536
  route: "employees",
@@ -600,7 +605,8 @@ async function saveEntity(kind, item, options = {}) {
600
605
  /**
601
606
  * Deletes one entity (a soft delete: it stays readable with `deleted: true`).
602
607
  * Employees and promotions can't be deleted in Shopwave; for them the route
603
- * sets `exitDate` / `endDate` to now instead (see `SHOPWAVE_ENTITIES`).
608
+ * sets `exitDate` / `endDate` to now instead (see `SHOPWAVE_ENTITIES`), and a
609
+ * promotion is saved back whole (see {@link deletePromotion}).
604
610
  *
605
611
  * Shopwave answers 205 with an empty body — also for ids that don't exist — so
606
612
  * a resolved promise means "the API accepted the request", not "a record was
@@ -641,7 +647,12 @@ const saveStore = (store, options) => saveEntity("store", store, options);
641
647
  const deleteStore = (id, options) => deleteEntityById("store", id, options);
642
648
  const fetchStore = (id, params, options) => fetchEntityById("store", id, params, options);
643
649
  const savePromotion = (promotion, options) => saveEntity("promotion", promotion, options);
644
- /** Ends a promotion: Shopwave has no promotion DELETE, so this sets `endDate` to now. */
650
+ /**
651
+ * Expires a promotion (Shopwave never deletes promotions). The route reads the
652
+ * promotion and saves it back with `endDate` set to now; a promotion that hasn't
653
+ * started yet gets `startDate` = `endDate` = now. Rejects if the promotion doesn't
654
+ * exist or the save fails.
655
+ */
645
656
  const deletePromotion = (id, options) => deleteEntityById("promotion", id, options);
646
657
  const fetchPromotion = (id, params, options) => fetchEntityById("promotion", id, params, options);
647
658
  /**