toga-ai 1.0.923 → 1.0.924
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/knowledge/2.0/apps/api2/features/nested-relationship-writes.md +3 -1
- package/knowledge/2.0/apps/worker2/features/service-request-sales-order-generation.md +18 -7
- package/knowledge/INDEX.md +1 -1
- package/knowledge/clients/adyen/INDEX.md +1 -0
- package/knowledge/clients/adyen/features/service-request-salesorder-netsuite.md +59 -0
- package/knowledge/clients/adyen/profile.md +8 -4
- package/knowledge/clients/elite/features/salesorder-netsuite-push.md +7 -2
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-10-01
|
|
10
10
|
owners: ["bala", "mhammontree", "tcox", "apeterson", "rgirish", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts
|
|
@@ -194,9 +194,11 @@ behaviour — it bites any write to a nested child, not one feature. Worked exam
|
|
|
194
194
|
- **Not every record is linkable by `uuid`** — read `searchableIdentifierFields` in the EV-12 body; fall back to `id` resolved by a natural key, never hardcoded.
|
|
195
195
|
- **Declaring a `c_` field on the model does not make it searchable** — the `CustomRecordFields.isIdentifier` flag is the gate; set the flag, not the child policy.
|
|
196
196
|
- **An identifier that is unique only inside a parent (e.g. `bundles.number` per catalog) links the WRONG record, silently.** The match uses only the identifier fields sent, so `bundle: {number: "001"}` hits another catalog's `001`. Send `uuid` (or add the scoping identifier) resolved in the caller. Seen on Prudential India REQs — see [bundle catalog resolution](../../../clients/prudential/features/service-request-bundle-catalog-resolution.md).
|
|
197
|
+
- **⚠ A nested `unit` with only an `item` (no serial/asset tag) REUSES one existing unit.** `ServiceRequests_Units.unitId` is `MATCH_UPSERT` and `Units.itemId` is an identifier, so `{unit:{item:{uuid}}}` matches the first unit of that item — every SR links the same unit. For serial-less requests attach a **bundle** instead (`ServiceRequests_Bundles.bundleId` is `MATCH`; send `{bundle:{uuid}}`). See [Adyen reclaim flow](../../../clients/adyen/features/service-request-salesorder-netsuite.md).
|
|
197
198
|
- **Singular-FK back-reference defects:** UPDATE path drops the parent's forward FK (echoes `null`); CREATE path re-parents a value-matched child to the newest writer (stolen child row). One defect, two symptoms.
|
|
198
199
|
|
|
199
200
|
## Change history
|
|
201
|
+
- 2026-09-30 — Gotcha: item-only nested unit matches an existing unit; use a bundle for serial-less SRs. (rgirish)
|
|
200
202
|
- 2026-09-29 — Gotcha: a per-parent identifier (`bundles.number`) matches across parents and links the wrong row. (rgirish)
|
|
201
203
|
- 2026-09-24 — Documented that the engine recurses into a nested inherent child with `POST`
|
|
202
204
|
hardcoded (`V2.php:5415`), so a child nested in a PUT is duplicated or fails 1062; update
|
|
@@ -6,11 +6,12 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["snaredla", "bala"]
|
|
9
|
+
updated: 2026-10-01
|
|
10
|
+
owners: ["snaredla", "bala", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Sync/ServiceRequest.php
|
|
13
13
|
- _underscore/Model/Elite/ServiceRequest.php
|
|
14
|
+
- _underscore/Model/Adyen/ServiceRequest.php
|
|
14
15
|
- dbchanges2/Core/2026-08-06a - Service request and sales order payload interceptors.sql
|
|
15
16
|
related:
|
|
16
17
|
- ./netsuite-salesorder-outbound-push.md
|
|
@@ -19,6 +20,8 @@ related:
|
|
|
19
20
|
- ../../_underscore/features/units-for-items-for-purchase-orders.md
|
|
20
21
|
- ../../../clients/elite/features/salesorder-netsuite-push.md
|
|
21
22
|
- ../../../clients/elite/features/togadesk-service-request-intake.md
|
|
23
|
+
- ../../../clients/adyen/features/service-request-salesorder-netsuite.md
|
|
24
|
+
- ../../api2/features/nested-relationship-writes.md
|
|
22
25
|
---
|
|
23
26
|
|
|
24
27
|
Worker `_Worker_Sync_ServiceRequest` that turns a Service Request → Sales Order → one PO per vendor for any tenant; open for the SR→SO→PO chain, its interceptor queueing, vendor resolution, or the `c_`-column probe.
|
|
@@ -34,7 +37,9 @@ _Worker_Sync_ServiceRequest::GenerateSalesOrderFromServiceRequest(
|
|
|
34
37
|
): string
|
|
35
38
|
```
|
|
36
39
|
|
|
37
|
-
Queued from a client's `service-requests` `postPost` interceptor
|
|
40
|
+
Queued from a client's `service-requests` `postPost` interceptor; invocable directly for testing like any worker action. Consumers: **Elite** ([desk intake](../../../clients/elite/features/togadesk-service-request-intake.md)) and **Adyen** ([reclaim flow](../../../clients/adyen/features/service-request-salesorder-netsuite.md), built 2026-09-30, not deployed).
|
|
41
|
+
|
|
42
|
+
**Per-client config = `SALES_ORDER_DEFAULTS_BY_CLIENT`**, keyed by `Core.Clients.clientIdentifier`: `locationUuid` (required — NetSuite rejects an order with no location) + `salesOrderTypeUuid` (nullable). Elite → location `3ca47b91-…`, type drop-ship `5994b81d-…`; Adyen → location `191c75ff-f02e-4dda-abc6-c8d26c35da62`, no type. **A client missing from the map throws** — onboarding a client to this worker = add a map entry. The uuids are the same in beta and prod.
|
|
38
43
|
|
|
39
44
|
**Proven end to end on dev-sandbox** (`https://api.dev.sandbox.togahub.com/v2`): one service request produced **SA100004** plus purchase order **P1**, both bridge rows linked, and a **rerun returned early** rather than duplicating.
|
|
40
45
|
|
|
@@ -50,8 +55,8 @@ Steps:
|
|
|
50
55
|
1. **Resolve the tenant.** `getClientContextFromCore()` looks up by `Core.Clients.clientIdentifier`, registers the client DB connection, returns Toga API credentials. (Deliberate local copy of the same private in `_Worker_Netsuite_SalesOrder`; lift to a shared parent when a third caller appears.)
|
|
51
56
|
2. **Guard.** `fetchServiceRequestFromToga2()` joins `SalesOrders` on `serviceRequestId`. If a sales order already exists, **return early** with a summary — makes the action idempotent under SQS redelivery and manual reruns. A request already in the **`Exception`** stage is skipped for the same reason.
|
|
52
57
|
3. **Reconcile the requester's address** (`reconcileRequesterAddressInToga2`) and **opt the requester into Order Updates** (`optInRequesterToOrderUpdatesInToga2`).
|
|
53
|
-
4. **Build and POST the sales order** (`buildSalesOrderPayloadForToga2`): customer by uuid, `serviceRequestId`, `ticketId`,
|
|
54
|
-
5. **Generate purchase orders** (`tryGeneratePurchaseOrdersInToga2` → `generatePurchaseOrdersInToga2`): re-read created lines with `fetchSalesOrderItemsFromToga2()`, resolve a vendor per item with `fetchVendorItemsFromToga2()`, group lines by vendor, POST **one PO per vendor** with line numbers restarted per order.
|
|
58
|
+
4. **Build and POST the sales order** (`buildSalesOrderPayloadForToga2`): customer by uuid, `serviceRequestId`, `ticketId`, `locationId` + type from the client's map, `shippingMethodId` = the SR's, else **`ShippingMethods` id 1**, and `shipToAddress` **only when the request has a deliverable address**. Lines come from `fetchServiceRequestLinesFromToga2()`: one line per SR **unit**; if the SR has no units, one line per **bundle item** (`ServiceRequests_Bundles` → `BundleItems` → `Items`). Each line is quantity 1, price 0, `lineNumber` assigned by the worker. No units and no bundle items → throws (no empty order).
|
|
59
|
+
5. **Generate purchase orders** (`tryGeneratePurchaseOrdersInToga2` → `generatePurchaseOrdersInToga2`): re-read created lines with `fetchSalesOrderItemsFromToga2()`, resolve a vendor per item with `fetchVendorItemsFromToga2()`, group lines by vendor, POST **one PO per vendor** with line numbers restarted per order. Items with no vendor are listed in the job result as `No vendor for: …` — the job does **not** fail (Adyen has no `VendorItems`, so it gets no PO).
|
|
55
60
|
6. **On a rejected sales-order POST**, `moveServiceRequestToExceptionInToga2()` parks the request in the `Exception` stage so the failure is visible *and* a redelivered job short-circuits on the step-2 stage guard.
|
|
56
61
|
|
|
57
62
|
### Vendor resolution replaces Prudential's hardcoded vendor
|
|
@@ -88,12 +93,14 @@ The three structural checks (string resolves → producer matches → intercepto
|
|
|
88
93
|
- **⚠ Before nesting a child collection in a payload, confirm the child is registered in `Core.InherentRecordChildren` under that parent record.** `salesOrderItemsCommittedUnits` was briefly nested under sales-order lines; it is **impossible** — record 152 has **zero** `Core.InherentRecordChildren` parent registrations under record 15, nothing writes that table via the API at all, and its model's `unitId` is a foreign key to `_Model_Client_User`, not to a unit. A nested key the API does not recognize is not obviously rejected; run the `InherentRecordChildren` check rather than assuming.
|
|
89
94
|
- **`shipToAddress` carries `childPolicy CREATE`** — posting an all-null address shape **creates a blank address row**. `hasDeliveryAddressForServiceRequest()` omits the key entirely instead.
|
|
90
95
|
- **Line numbers are assigned by the worker and re-checked.** After the POST the worker maps created lines back by `lineNumber`; a line it cannot map means the API renumbered them, and that is **reported** rather than silently dropped from the PO.
|
|
91
|
-
- **⚠
|
|
96
|
+
- **⚠ Do not add Prudential to the map without a review.** The worker now reads bundle items, but only as plain qty-1 lines — not Prudential's `BundleItems.preferredVendorId` / Dell flow. Prudential is not in `SALES_ORDER_DEFAULTS_BY_CLIENT`, so a run for it throws.
|
|
97
|
+
- **Fallback ship method `ShippingMethods` id 1 is a per-tenant row id**, not a known method. When the SR names none, verify id 1 is the right NetSuite-mapped method for that client (open).
|
|
98
|
+
- **⚠ Inbound NetSuite sync can duplicate an order this worker created (all clients, open).** `library/app/api/toga2.php:1002` matches sales orders only by `c_netsuiteInternalSalesOrderId`. If the inbound sync runs after NetSuite has the order but before the push writes the id back, it imports a second copy.
|
|
92
99
|
- **Placement:** cross-system sync workers live under `worker2/Worker/Sync/`. This worker does **not** belong in `Worker/Netsuite/` (which keeps the NetSuite push, `Worker/Netsuite/SalesOrder.php`) and not in `Worker/Client/`.
|
|
93
100
|
- **⚠ Renaming this action is a TWO-REPO, single-deploy change.** The method name here and the `runTask(action: 'Sync/ServiceRequest/GenerateSalesOrderFromServiceRequest', …)` **string** in `_underscore/Model/Elite/ServiceRequest.php:44` must land and deploy **together** — the rename from `GenerateSalesOrder` on 2026-08-12 spans both. Ship either alone and the dispatcher targets a name that no longer exists; because `postPost` wraps `runTask` in try/catch + `error_log`, Sales Order generation then stops **silently**. Same exposure for `Sync/SalesOrderStatus/PostSalesOrderStatusToTogadesk1`. See [creating worker actions](./creating-worker-actions.md).
|
|
94
101
|
- **⚠ The copied client-context helper has already DRIFTED.** `getClientContextFromCore()` here, the same private in `Worker/Sync/SalesOrderStatus.php`, and `getClientContext()` in `Worker/Netsuite/SalesOrder.php` are three copies — and only **this** one has `ORDER BY id ASC` on the `Apis` credential lookup, so the other two pick a **non-deterministic** API credential row (`Worker/Netsuite/SalesOrder.php` documents the flaw in a comment). Add the `ORDER BY` to all three if you touch any, and prefer extraction.
|
|
95
102
|
- **The NetSuite actions in this chain are `Netsuite/SalesOrder/CreateNetSuite` and `Netsuite/SalesOrder/UpdateNetSuite`.** There is **no** `Netsuite/SalesOrder/Push` — do not write that string into a migration, a test or a doc.
|
|
96
|
-
-
|
|
103
|
+
- **`locationId` was missing until 2026-09-30.** Orders created before then (e.g. prod Elite `SalesOrders` 279 / `SA100000`) have `locationId` NULL and fail `Netsuite/SalesOrder/CreateNetSuite` with *"no NetSuite location mapped…"*. The worker now sets it from the client's map (not deployed yet). Keep the fix here — `buildNetSuiteOrder()` must never guess a warehouse.
|
|
97
104
|
|
|
98
105
|
### Known limitations — business blockers, not code defects
|
|
99
106
|
|
|
@@ -104,6 +111,9 @@ Recorded so nobody re-debugs them as bugs. All four confirmed against Elite's da
|
|
|
104
111
|
- **Generated POs reach no vendor.** No `Integrations` rows and no `defaultPoSubmissionIntegrationId` configured, so nothing transmits them.
|
|
105
112
|
- The worker is otherwise complete; these are data/config gaps for the client to close.
|
|
106
113
|
|
|
114
|
+
## Change history
|
|
115
|
+
- 2026-09-30 — Per-client `SALES_ORDER_DEFAULTS_BY_CLIENT` (sets `locationId`, unknown client throws); bundle-item lines when an SR has no units; no-vendor items reported, not failed; Adyen added as second consumer. Not deployed. (rgirish)
|
|
116
|
+
|
|
107
117
|
## Related
|
|
108
118
|
|
|
109
119
|
- [Toga → NetSuite Sales-Order Push](./netsuite-salesorder-outbound-push.md)
|
|
@@ -112,3 +122,4 @@ Recorded so nobody re-debugs them as bugs. All four confirmed against Elite's da
|
|
|
112
122
|
- [Units for items for purchase orders](../../_underscore/features/units-for-items-for-purchase-orders.md)
|
|
113
123
|
- [Elite Sales-Order NetSuite push](../../../clients/elite/features/salesorder-netsuite-push.md)
|
|
114
124
|
- [Elite TOGaDesk service-request intake](../../../clients/elite/features/togadesk-service-request-intake.md)
|
|
125
|
+
- [Adyen reclaim SR → SO → NetSuite](../../../clients/adyen/features/service-request-salesorder-netsuite.md)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -19,7 +19,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
19
19
|
|
|
20
20
|
## 2.0 framework
|
|
21
21
|
|
|
22
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
22
|
+
- **_underscore** (_Underscore) _(framework core)_ — 94 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
23
23
|
- **worker2** (Worker) — 80 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
24
24
|
- **api2** (API) — 26 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
25
25
|
- **dbchanges2** (Database Changes) _(framework core)_ — 22 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
@@ -2,4 +2,5 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Framework | Summary |
|
|
4
4
|
|-----|-----------|---------|
|
|
5
|
+
| [Adyen — Reclaim Service Request → Sales Order → NetSuite (bundle-driven, no serials)](features/service-request-salesorder-netsuite.md) | 2.0 | Adyen's Reclaim SR → SO → NetSuite chain (same as Elite, but items come from a seeded bundle because Adyen sends no serials); open for the prePost bundle attach |
|
|
5
6
|
| [Adyen](profile.md) | 2.0 | Adyen live TOGa Supply client (`Client_Adyen`) on the 1.0 NetSuite→TOGa Supply sync, status draft; open for the prod tracking-API test orders + SR↔NetSuite matc |
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Adyen — Reclaim Service Request → Sales Order → NetSuite (bundle-driven, no serials)"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: adyen
|
|
7
|
+
type: client-feature
|
|
8
|
+
status: draft
|
|
9
|
+
updated: 2026-10-01
|
|
10
|
+
owners: ["rgirish"]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Model/Adyen/ServiceRequest.php
|
|
13
|
+
- _underscore/Model/Adyen/SalesOrder.php
|
|
14
|
+
- worker2/Worker/Sync/ServiceRequest.php
|
|
15
|
+
- dbchanges2/Client_Adyen/2026-09-30a - ServiceRequestSalesOrderSetup.sql
|
|
16
|
+
related:
|
|
17
|
+
- ../../../2.0/apps/worker2/features/service-request-sales-order-generation.md
|
|
18
|
+
- ../../../2.0/apps/worker2/features/netsuite-salesorder-outbound-push.md
|
|
19
|
+
- ../../../2.0/apps/api2/features/api-payload-interceptors.md
|
|
20
|
+
- ../../../2.0/apps/api2/features/nested-relationship-writes.md
|
|
21
|
+
- ../../elite/features/salesorder-netsuite-push.md
|
|
22
|
+
- ../profile.md
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
Adyen's Reclaim SR → SO → NetSuite chain (same as Elite, but items come from a seeded bundle because Adyen sends no serials); open for the prePost bundle attach, the 2026-09-30a setup migration, deploy order, or duplicate risks.
|
|
26
|
+
|
|
27
|
+
## Summary
|
|
28
|
+
|
|
29
|
+
Adyen's API (`Apis` id 4) posts Service Requests, all of type **"Reclaim"**, and **cannot send serial numbers**. The chain is the same as Elite's: SR `postPost` → worker2 builds the Sales Order → SO `postPost` → NetSuite push. The only Adyen difference: the SR's items come from a seeded **"Reclaim" bundle**, attached in `prePost`.
|
|
30
|
+
|
|
31
|
+
**Status (2026-10-01): code + migration written, NOT run or deployed anywhere, not tested end to end.**
|
|
32
|
+
|
|
33
|
+
## How it works
|
|
34
|
+
|
|
35
|
+
1. `_Model_Adyen_ServiceRequest::prePost` — when type is `Reclaim` and the payload has **no units and no bundles**, adds `serviceRequestBundles: [{bundle:{uuid:'23717330-7672-4590-9e3d-7f4097c18723'}}]` (`RECLAIM_BUNDLE_UUID`).
|
|
36
|
+
2. `_Model_Adyen_ServiceRequest::postPost` — same as Elite's: queues `Sync/ServiceRequest/GenerateSalesOrderFromServiceRequest`.
|
|
37
|
+
3. The worker turns the bundle's items into SO lines (qty 1 each) and sets location "Adyen" from its client map — see [SR → SO generation](../../../2.0/apps/worker2/features/service-request-sales-order-generation.md). No PO: Adyen has no `VendorItems` (job reports `No vendor for: …`, does not fail).
|
|
38
|
+
4. The existing `_Model_Adyen_SalesOrder::postPost` queues `Netsuite/SalesOrder/CreateNetSuite` ([shared push](../../../2.0/apps/worker2/features/netsuite-salesorder-outbound-push.md)).
|
|
39
|
+
|
|
40
|
+
**Why a bundle, not units:** a unit with only an item (no serial) matches an **existing** unit, so every SR would link the same unit — see [nested writes](../../../2.0/apps/api2/features/nested-relationship-writes.md). `ServiceRequests_Bundles.bundleId` is `MATCH` (links only), and Prudential already posts bundles daily with 201.
|
|
41
|
+
|
|
42
|
+
### Setup migration — `dbchanges2/Client_Adyen/2026-09-30a - ServiceRequestSalesOrderSetup.sql`
|
|
43
|
+
- `ApiPayloadInterceptors`: record 35 (`service-requests`) PRE+POST / POST, scoped to Adyen API; record 14 (`sales-orders`) POST/POST and POST/PUT, `apiId` NULL (the worker writes as a different API identity).
|
|
44
|
+
- Items `SVC-FS-OFFBOARD-ADN` (NS 1579821) + `Misc Laptop` (NS 1545437, SERIALIZED); "Reclaim" bundle + 2 `BundleItems`.
|
|
45
|
+
- Location "Adyen" (NS location 70); ShippingCarrier FedEx + ShippingMethod Ground (NS 318627).
|
|
46
|
+
- `Customers.c_netsuiteInternalCustomerId = 31054`.
|
|
47
|
+
|
|
48
|
+
These values copy Adyen's hand-made NetSuite reclaim orders (e.g. 290879, 288260, 279871): always `SVC-FS-OFFBOARD-ADN` + one device item, location 70, ship method 318627.
|
|
49
|
+
|
|
50
|
+
## Gotchas
|
|
51
|
+
|
|
52
|
+
- **⚠ Deploy order: worker2 → `_underscore` → SQL.** Running the SQL first registers a PRE/POST interceptor whose `prePost` method does not exist yet, so api2 fails **every** Adyen SR POST.
|
|
53
|
+
- **Before 2026-09-30a, `Client_Adyen` had 0 `ApiPayloadInterceptors` rows** — the Adyen model hooks committed 2026-08-24 never fired. "Nothing happened" on Adyen is a data check first.
|
|
54
|
+
- **Open — Adyen resending the same SR creates a duplicate.** `c_externalReferenceId` is not unique (SR100024 already duplicates SR100021 — see [profile](../profile.md)).
|
|
55
|
+
- **Open — NetSuite PO # (`otherrefnum`) will be the SR number**, not the employee name the hand-made orders carry. The profile's SR↔NetSuite matching reads that field.
|
|
56
|
+
- **Open — shipping method fallback** and the inbound-sync duplicate race are shared worker issues — see [SR → SO generation](../../../2.0/apps/worker2/features/service-request-sales-order-generation.md#gotchas).
|
|
57
|
+
|
|
58
|
+
## Change history
|
|
59
|
+
- 2026-09-30 — Built: Reclaim bundle attach in `prePost`, Elite-style `postPost`, setup migration 2026-09-30a. Not deployed. (rgirish)
|
|
@@ -7,11 +7,12 @@ apps:
|
|
|
7
7
|
- _underscore
|
|
8
8
|
- api2
|
|
9
9
|
- dbchanges2
|
|
10
|
+
- worker2
|
|
10
11
|
project: Worker
|
|
11
12
|
client: adyen
|
|
12
13
|
type: profile
|
|
13
14
|
status: draft
|
|
14
|
-
updated: 2026-
|
|
15
|
+
updated: 2026-10-01
|
|
15
16
|
owners: ["rgirish"]
|
|
16
17
|
files:
|
|
17
18
|
- worker/crons/toga2/netsuite/sync_togasupply_adyen.php
|
|
@@ -22,7 +23,10 @@ files:
|
|
|
22
23
|
- dbchanges2/Client_Adyen/2026-09-22a - AdyenApiTestSeed.sql
|
|
23
24
|
- dbchanges2/Client_Adyen/2026-09-28a - LinkTestOrdersToNetsuite.sql
|
|
24
25
|
- dbchanges2/Client_Adyen/2026-09-28b - RemoveUnmatchedTestOrders.sql
|
|
26
|
+
- _underscore/Model/Adyen/ServiceRequest.php
|
|
27
|
+
- dbchanges2/Client_Adyen/2026-09-30a - ServiceRequestSalesOrderSetup.sql
|
|
25
28
|
related:
|
|
29
|
+
- ./features/service-request-salesorder-netsuite.md
|
|
26
30
|
- ../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md
|
|
27
31
|
- ../../1.0/apps/library/features/netsuite-suiteql-api-reference.md
|
|
28
32
|
- ../growrk/features/transfer-order-flow.md
|
|
@@ -31,7 +35,7 @@ related:
|
|
|
31
35
|
- ../../2.0/apps/dbchanges2/workflows/nonprod-metadata-drift-repair.md
|
|
32
36
|
---
|
|
33
37
|
|
|
34
|
-
Adyen live TOGa Supply client (`Client_Adyen`) on the 1.0 NetSuite→TOGa Supply sync, status draft; open for the prod tracking-API test orders + SR↔NetSuite matching, the `isFulfillable` column gap, the beta=client-sandbox env trap, and the
|
|
38
|
+
Adyen live TOGa Supply client (`Client_Adyen`) on the 1.0 NetSuite→TOGa Supply sync, status draft; open for the prod tracking-API test orders + SR↔NetSuite matching, the `isFulfillable` column gap, the beta=client-sandbox env trap, the ACL findings, and the Reclaim SR → SO → NetSuite chain.
|
|
35
39
|
|
|
36
40
|
## Summary
|
|
37
41
|
|
|
@@ -42,7 +46,8 @@ Adyen live TOGa Supply client (`Client_Adyen`) on the 1.0 NetSuite→TOGa Supply
|
|
|
42
46
|
|
|
43
47
|
- **NetSuite → TOGa Supply sync.** Per-client thin wrapper over the shared engine — full mechanics on [per-client sync](../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md).
|
|
44
48
|
- **Transfer orders ENABLED** (`IS_ENABLED_INTEGRATION_TRANSFER_ORDERS = true`). Adyen's NetSuite transfer orders arrive **as NetSuite sales orders**, classified by `TRANSFER_ORDER_DETECTION_MODE__ZERO_DOLLAR_HOLD` — `holdInvoice === true` **AND** `total === 0`, same rule as GroWrk (see [GroWrk transfer order flow](../growrk/features/transfer-order-flow.md)). Mode is now declared explicitly in Adyen's launcher; `App_Api_Toga2::isTransferOrder()` throws if it is missing, so **`library` and `worker` must deploy together** for Adyen.
|
|
45
|
-
- **IDs:** `Core.Clients` id **48**; NetSuite customer **31054** ("4950 Adyen").
|
|
49
|
+
- **IDs:** `Core.Clients` id **48**; NetSuite customer **31054** ("4950 Adyen"); Adyen API = `Apis` id 4.
|
|
50
|
+
- **Reclaim SR → Sales Order → NetSuite** (built 2026-09-30, not deployed): same chain as Elite, items from a seeded "Reclaim" bundle because Adyen sends no serials — see [Adyen reclaim flow](./features/service-request-salesorder-netsuite.md).
|
|
46
51
|
- **`Client_Adyen`** is one of the client DBs with surface-layer / ACL drift noted on the [ACL permission chain](../../2.0/apps/_underscore/features/acl-permission-chain.md) and [surface-layer schema](../../2.0/apps/dbchanges2/features/surface-layer-schema.md) docs.
|
|
47
52
|
|
|
48
53
|
## Gotchas
|
|
@@ -123,5 +128,4 @@ The `AclRecordPermissions` prod-vs-dev-sandbox route-name diff from that session
|
|
|
123
128
|
|
|
124
129
|
- Formal/legal client name and account owner — recorded here simply as **Adyen**.
|
|
125
130
|
- Which repos beyond the `apps:` list are genuinely in Adyen's scope.
|
|
126
|
-
- Whether Adyen has client-specific models/features that deserve their own docs.
|
|
127
131
|
- Confirm with Adyen/ops: which FedEx package is outbound vs return; whether SR100024 should be closed; who sets up the NetSuite sync (customer id, Locations, cursors).
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: elite
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["bala", "snaredla"]
|
|
9
|
+
updated: 2026-10-01
|
|
10
|
+
owners: ["bala", "snaredla", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Elite/SalesOrder.php
|
|
13
13
|
- _underscore/Model/Elite/ServiceRequest.php
|
|
@@ -90,6 +90,8 @@ Every environment points at the **same live NetSuite account 1095849** — there
|
|
|
90
90
|
**Do not bulk-delete that range.** Two further NetSuite ids in it — **7369129** and **7374431** — were **not** created by this work. Any cleanup must be row by row. The general rule (testing against beta writes to production NetSuite **and** round-trips back into production Toga data) is recorded in [running worker2 locally](../../../2.0/apps/worker2/workflows/running-worker2-locally.md).
|
|
91
91
|
|
|
92
92
|
### The SR → SO worker does not set `locationId`, which blocks the push (hand-off)
|
|
93
|
+
> **Update 2026-09-30:** the worker now sets `locationId` from `SALES_ORDER_DEFAULTS_BY_CLIENT` (Elite → `3ca47b91-…`, type drop-ship). Written, **not deployed** — see [SR → SO generation](../../../2.0/apps/worker2/features/service-request-sales-order-generation.md).
|
|
94
|
+
|
|
93
95
|
prod `SalesOrders` **279** (`SA100000`, from `serviceRequestId 3`, 2026-08-13 11:11) was created by `Sync/ServiceRequest/GenerateSalesOrder` with **`locationId` NULL**, and `buildNetSuiteOrder()` then failed loudly: *"no NetSuite location mapped on line 1 or on the order — map `Locations.c_netsuiteInternalLocationId`"*.
|
|
94
96
|
|
|
95
97
|
**The mapping data is fine.** prod Elite `Locations`: id 1 *Elite* (uuid `3ca47b91-6d05-4e28-9f17-c0b82a6e1345`) → **196**, id 2 *Elite Eagan US HoldCo LLC* → **196**, id 3 *New York* → **5**. That location uuid is **identical in prod and dev-sandbox**, so the same payload value works in both.
|
|
@@ -116,6 +118,9 @@ The interceptor → `runTask` → SQS → worker2 chain **is** proven at volume
|
|
|
116
118
|
- **api2's `error_log()` output is not in CloudWatch.** worker2 ships `/var/log/php-fpm/www-error.log` to the `worker` log group via a postdeploy hook; **api2 has no equivalent hook**, so the interceptor's silent-skip messages must be pulled from the EB request/full logs.
|
|
117
119
|
- **Orders that arrived from NetSuite must not be pushed back** — the `c_netsuiteInternalSalesOrderId`-on-payload bail is what prevents an echo loop. Keep it in any new hook on this model.
|
|
118
120
|
|
|
121
|
+
## Change history
|
|
122
|
+
- 2026-09-30 — SR → SO worker now sets the order location (fixes the `locationId` hand-off); not deployed. (rgirish)
|
|
123
|
+
|
|
119
124
|
## Related
|
|
120
125
|
- [Toga → NetSuite Sales-Order Push (shared)](../../../2.0/apps/worker2/features/netsuite-salesorder-outbound-push.md)
|
|
121
126
|
- [SR → SO generation](../../../2.0/apps/worker2/features/service-request-sales-order-generation.md)
|
package/package.json
CHANGED