punchout-simulator 0.3.1 → 0.5.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.
package/README.md CHANGED
@@ -99,8 +99,8 @@ Document-specific:
99
99
  | Document | Checks |
100
100
  |---|---|
101
101
  | **PunchOutSetupResponse** | `Status` present & `200`; valid `StartPage/URL` |
102
- | **PunchOutOrderMessage** (punchback) | `BuyerCookie` matches the session; header `Total`/`Money`+currency; each `ItemIn` has quantity, `SupplierPartID`, `UnitPrice`+currency, `Description`, `UnitOfMeasure`, `Classification`@domain; **Total consistency** (sum of line totals) |
103
- | **OrderRequest** | `orderID`/`orderDate`/`Total`; `ShipTo`/`BillTo`; each `ItemOut`; **attachment `cid:` resolution** (see below) |
102
+ | **PunchOutOrderMessage** (punchback) | `BuyerCookie` matches the session; header `Total`/`Money`+currency; each `ItemIn` has quantity, `SupplierPartID`, `UnitPrice`+currency, `Description`, `UnitOfMeasure`, `Classification`@domain; **single currency** (mixed-currency is an error unless the supplier allows it); **Total consistency** (sum of line totals) |
103
+ | **OrderRequest** | `orderID`/`orderDate`/`Total`; `ShipTo`/`BillTo` present **and complete** (an `addressID` or a postal Street/City — an empty block is flagged); `Contact@role`; each `ItemOut`; **single currency** (as above); **attachment `cid:` resolution** (see below) |
104
104
  | **OrderResponse** | `Status` code present / not an error |
105
105
 
106
106
  > Note: full cXML DTD validation would require a native validating XML parser,
@@ -167,7 +167,7 @@ backend, so there is no CORS between them).
167
167
  | XML parsing | `fast-xml-parser` |
168
168
  | cXML building | template literals (full control over shape/ordering/`xml:lang`) |
169
169
  | multipart/related | hand-assembled (`Buffer` + boundary) |
170
- | Storage | `lowdb` (`config.json`) for buyers/suppliers/connections/profiles · append-only JSONL per session for logs · separate files for attachments |
170
+ | Storage | `lowdb` (`config.json`) for buyers/suppliers/connections/product-lists/profiles · append-only JSONL per session for logs · separate files for attachments |
171
171
 
172
172
  ```
173
173
  src/
@@ -178,15 +178,51 @@ src/
178
178
 
179
179
  ### Data model
180
180
 
181
- The config is normalized into four entities:
181
+ The config is normalized into five entities:
182
182
 
183
- - **Buyer** — a reusable party holding its own cXML identity (the `From` credential) and an optional **platform profile** reference (see below).
184
- - **Supplier** — a reusable party holding its cXML identity (`To`) plus its **endpoints** (PunchOut URL, Order URL) and an optional mock catalog. Endpoints are intrinsic to the supplier — defined once, not per relationship.
185
- - **Connection** — the edge pairing one Buyer with one Supplier. It holds only what is specific to that pair: which side the tool simulates (`mode`), the `sharedSecret`, an optional per-pair Sender identity override (defaults to the buyer's identity), `authStyle`, `deploymentMode`, and `attachmentEncoding`.
183
+ - **Buyer** — a reusable party holding its own cXML identity (the `From` credential), an optional **platform profile** reference (see below), and optional default **ShipTo / BillTo addresses** and an end-user **Contact** (see below).
184
+ - **Supplier** — a reusable party holding its cXML identity (`To`) plus its **endpoints** (PunchOut URL, Order URL) and the **product lists** it serves as its Mode-B catalog. Endpoints are intrinsic to the supplier — defined once, not per relationship. An optional `allowMixedCurrency` flag relaxes the multi-currency check for this supplier (see below).
185
+ - **Connection** — the edge pairing one Buyer with one Supplier. It holds only what is specific to that pair: which side the tool simulates (`mode`), the `sharedSecret`, an optional per-pair Sender identity override (defaults to the buyer's identity), `deploymentMode`, and `attachmentEncoding`.
186
+ - **Product List** — a reusable, named set of catalog products. Assigned to one or more Suppliers; each supplier serves the **union** of its lists as its mock catalog. See below.
186
187
  - **Profile** — a reusable **procurement-platform profile** (Ariba/Coupa/Jaggaer/…) referenced by a Buyer. See below.
187
188
 
188
189
  At send time: `From` = buyer identity, `To` = supplier identity, `Sender` = the connection's override (or the buyer), and the request targets the supplier's endpoints. The mock-supplier endpoints are keyed by supplier id (`/sim/<supplierId>/…`).
189
190
 
191
+ ### Product lists (Mode-B catalogs)
192
+
193
+ A **Product List** is a named set of catalog items, edited on its own and assigned to one or
194
+ more Suppliers from the **Products** tab. In Mode B a supplier serves the union of its assigned
195
+ lists; a supplier with no lists falls back to a small built-in demo catalog. A built-in
196
+ **Sample assortment** (~20 office/industrial items) ships out of the box and can be **loaded
197
+ into the editor** as a starting point. You can also **import a CSV** (a header row, any column
198
+ order; `SupplierPartID` required) to bulk-load a real catalog — a template is downloadable from
199
+ the editor. Recognized columns: `SupplierPartID`, `SupplierPartAuxiliaryID`, `Description`,
200
+ `UnitPrice`, `Currency`, `UoM`, `UNSPSC` (or a `Classifications` column as
201
+ `UNSPSC:31161500;eCl@ss:27-06`), `ManufacturerPartID`, `ManufacturerName`, `AllowFractional`.
202
+
203
+ Each product carries a `SupplierPartID`, an optional `SupplierPartAuxiliaryID`, description, unit
204
+ price + currency, unit of measure, manufacturer info, and **one or more `Classification`** entries
205
+ (each with its own `domain`, e.g. `UNSPSC` plus a supplier scheme like `eCl@ss`). A product may opt
206
+ into **fractional order quantities** (e.g. `1.5` m of cable) via its `allowFractional` flag — the
207
+ Mode-B catalog then accepts decimals for that item; all other items stay whole-number. These fields
208
+ flow through to the punchback (`ItemIn`) and the resulting OrderRequest (`ItemOut`).
209
+
210
+ **Currency.** A cXML `Total` is a single `Money`, so a document is expected to be single-currency.
211
+ If a cart/order spans more than one currency the mock supplier emits `Total` `0` (rather than a
212
+ meaningless cross-currency sum) and validation raises a `mixed-currency` **error**. A supplier that
213
+ genuinely handles multi-currency orders can set **`allowMixedCurrency`**, which downgrades that to a
214
+ non-blocking **warning** (the per-line `Money` currencies are always preserved either way).
215
+
216
+ ### Addresses & contact
217
+
218
+ A **Buyer** holds default **ShipTo** / **BillTo** addresses and an end-user **Contact** (name, email,
219
+ phone). They pre-fill the OrderRequest (which stays fully editable as cXML before sending) and, when the
220
+ buyer's profile enables it, are also carried in the **PunchOutSetupRequest** (Ariba-style). Each address
221
+ supports a postal block and/or an `addressID` (+ `addressIDDomain`) reference; the **profile's
222
+ `addressMode`** decides which is emitted (`full` / `id-only` / `both`) — matching how platforms differ
223
+ (Jaggaer/Workday send full postal, SAP is plant-code/`addressID`-centric, Ariba sends both and includes
224
+ ShipTo in setup). An emitted `ShipTo`/`BillTo` with neither an `addressID` nor a Street/City is flagged.
225
+
190
226
  ### Simulating different procurement platforms (Buyer profiles)
191
227
 
192
228
  Real procurement systems emit cXML differently. A **Profile** captures a platform's
@@ -201,6 +237,8 @@ Ariba, Coupa, Jaggaer, Oracle, SAP, or Workday:
201
237
  | `attachmentEncoding` | Default `Content-Transfer-Encoding` for OrderRequest attachments (`binary`/`base64`). |
202
238
  | `cartReturnTransport` | How the punchback is returned in Mode B: `cxml-urlencoded`, `cxml-base64`, or `raw`. |
203
239
  | `extrinsics` | `<Extrinsic>` templates injected into the setup/order documents (values may use `${buyerCookie}` / `${orderId}`). |
240
+ | `addressMode` | How `ShipTo`/`BillTo`/`Contact` are emitted: `full` (PostalAddress), `id-only` (just `addressID`), or `both`. |
241
+ | `shipToInSetup` / `contactInSetup` | Carry the buyer's `ShipTo` / `Contact` already in the **PunchOutSetupRequest** (Ariba-style), so the supplier can price/personalize per ship-to. |
204
242
 
205
243
  Built-in presets (Ariba, Coupa, Jaggaer, Oracle, SAP, Workday, Generic) ship out of the box
206
244
  and can be **loaded into the editor** as a starting point, then customized. A Profile holds
@@ -218,6 +256,7 @@ cxml-urlencoded) — i.e. the tool's original behavior.
218
256
 
219
257
  - `*/api/buyers`, `*/api/suppliers` — CRUD for the reusable parties
220
258
  - `*/api/profiles` — CRUD for procurement-platform profiles · `GET /api/profile-presets` — the built-in preset library
259
+ - `*/api/product-lists` — CRUD for product lists · `GET /api/product-list-presets` — the built-in sample library
221
260
  - `*/api/connections` — CRUD for connection edges (reference a buyer + supplier)
222
261
  - `GET /api/connections/:id/setup/preview` · `POST …/setup` — preview / send the SetupRequest
223
262
  - `POST /api/connections/:id/order/preview` · `POST …/order` — preview / send the OrderRequest (multipart if attachments)