toga-ai 1.0.119 → 1.0.120

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.
@@ -57,6 +57,9 @@ the window's upper bound after each successful section.
57
57
 
58
58
  ### Adding a new client (the full recipe)
59
59
 
60
+ A wrapper + schedule alone is **not enough** — the client must also be provisioned in three
61
+ places or the sync runs but imports nothing (or 403s). All five steps are required:
62
+
60
63
  1. **Wrapper** — copy `sync_togasupply_canon.php` to `sync_togasupply_<client>.php`; set
61
64
  `CLIENT_CONFIGURATION` (literal UUIDs, or `App_Api_Toga2::*` consts if defined). Keep the
62
65
  rest identical.
@@ -64,8 +67,35 @@ the window's upper bound after each successful section.
64
67
  `"cron": "toga2/netsuite/sync_togasupply_<client>.php"`). Deploys regenerate the crontab.
65
68
  3. **Seed Parameters** — add `dbchanges2/Client_<Id>/<date> - NetsuiteSyncParameters.sql`
66
69
  inserting the 12 keys. **Without this the sync 404s on the first `/parameters` GET and the
67
- run aborts.** `Client_<Id>` directory selects the DB — no `USE` statement (dbchanges2 rule).
70
+ run aborts.** Seed start date = the canonical **`2026-03-04`** (from
71
+ `_modules/netsuite/2026-04-01 - Parameters.sql`) unless backfilling earlier history for that
72
+ client. `Client_<Id>` directory selects the DB — no `USE` statement (dbchanges2 rule).
68
73
  `lint`: `C:\xampp7\php\php.exe -l` (prod worker is PHP 7.2).
74
+ 4. **Client→NetSuite customer link** (`Core.Clients.netsuiteCustomerInternalId`) — **MUST be
75
+ set** to the client's NetSuite customer internal id. The lookup phase calls
76
+ `listChildCustomers((int)$client->netsuiteCustomerInternalId)`; if it's `NULL` the walk
77
+ seeds off `0`, the customer→client map is empty, and **no order ever matches → silent
78
+ zero-import** (the sync "runs" and even advances windows, but writes nothing). Find the id
79
+ with a SuiteQL `customer` lookup (`test/@dave/find_aig_netsuite_customer.php` is a template);
80
+ set it via a `dbchanges2/Core/` migration. Verify the client *has* supply transactions on
81
+ that customer before bothering (see the AIG gotcha).
82
+ 5. **Per-client custom fields on the synced records** — the shared engine hardcodes a fixed set
83
+ of `c_netsuite*` field names it reads/writes (see *Custom field contract* below). Each must
84
+ exist on the client's records as **column + `CustomRecordFields` row + `AclCustomFieldPermissions`
85
+ grant** (custom fields are per-client — see the 2.0 `_underscore` per-client-database doc). A
86
+ missing one → **403 EZ-2** ("not authorized to read the specified fields") aborting the run.
87
+ The most commonly-missing one is `c_netsuiteInternalCustomerId` on the **Customers** record
88
+ (`recordId 113`, `isIdentifier=1`).
89
+
90
+ ### Custom field contract (hardcoded in `common_sync_togasupply.php`)
91
+
92
+ The engine requests these `c_` fields by literal name per route — every synced client must have
93
+ them provisioned: `/customers`→`c_netsuiteInternalCustomerId`; `/countries`→`c_netsuiteCountry`;
94
+ `/locations`→`c_netsuiteInternalCustomerId` + `c_netsuiteInternalLocationId` + the
95
+ `c_dtNetsuiteLast*Integration` timestamps; `/subsidiaries`→`c_netsuiteInternalSubsidiaryId`;
96
+ `/vendors`→`c_netsuiteInternalVendorId`; `/payment-terms`→`c_netsuiteInternalTermsId`;
97
+ `/shipping-methods`→`c_netsuiteInternalShipMethodId`. The names are a **convention**, not
98
+ discovered per-client.
69
99
 
70
100
  ## Data model
71
101
 
@@ -74,7 +104,8 @@ Per-client `Parameters` table (2.0 client DB, e.g. `Client_Canon.Parameters`; co
74
104
  `/parameters`:
75
105
 
76
106
  - `NETSUITE_LAST_SYNC_DATETIME_{SALES_ORDERS,PURCHASE_ORDERS,INVOICES,ITEM_RECEIPTS,ITEM_FULFILLMENTS,INVENTORY_ADJUSTMENTS}`
77
- — seed `'2025-01-01 00:00:00'` (or chosen backfill start).
107
+ — seed `'2026-03-04 00:00:00'` (the canonical start; use an earlier date only to backfill a
108
+ client whose history predates it — e.g. AIG seeded `2023-01-01` to reach its 2023/2025 records).
78
109
  - `NETSUITE_EXECUTION_MODE_{...same six...}` — seed `'864000-IDLE'`.
79
110
 
80
111
  Parameters are stored **per client DB** but accessed **through the TOGa2 API**, not direct SQL.
@@ -102,13 +133,28 @@ Parameters are stored **per client DB** but accessed **through the TOGa2 API**,
102
133
  `Client_<Id>.Parameters`: a healthy client advances to ~now every 5 min. As of 2026-06-17,
103
134
  Quad was seeded but frozen at 2025-04/05 (not advancing) while the other 12 deployed clients
104
135
  were current.
136
+ - **Silent zero-import = missing `netsuiteCustomerInternalId`.** If a client "runs" (sections
137
+ flip RUNNING→IDLE, windows advance) but imports nothing, check
138
+ `Core.Clients.netsuiteCustomerInternalId` — a NULL there empties the customer map so no order
139
+ matches. Distinct from the EZ-2 *abort* caused by a missing `c_netsuite*` field.
140
+ - **"Listed ≠ functioning" — the AIG case (2026-06-17).** AIG was in the original combined
141
+ array but had **never** imported anything in prod (0 SalesOrders/POs/Invoices/Locations/Customers)
142
+ and *couldn't* have: its `netsuiteCustomerInternalId` was NULL and its Customers record lacked
143
+ `c_netsuiteInternalCustomerId`. After provisioning all three (custom field, Parameters, customer
144
+ link → NetSuite customer **34902**), it still imports ~nothing because AIG's entire NetSuite
145
+ supply footprint is **1 sales order (2025-09-11) + 15 opportunities** — it's an
146
+ opportunity/entitlement account, not a procurement/supply customer. Lesson: before onboarding a
147
+ client, confirm it actually transacts supply orders in NetSuite (SuiteQL the `customer`'s
148
+ transaction history) — being in the array doesn't mean it belongs on this engine.
105
149
 
106
150
  ## Change history
107
151
 
108
- - 2026-06-17 — Added AIG + Growrk thin wrappers (the two clients from the original array that
109
- never got one), registered both in `cron.worker.sync.json`, and added their dbchanges2
110
- Parameters seeds (`Client_Aig`, `Client_Growrk`). Verified the other 13 clients' Parameters
111
- against prod. (dfranks)
152
+ - 2026-06-17 — Documented the full onboarding prerequisites (client→NetSuite customer link +
153
+ per-client `c_netsuite*` custom fields) after AIG ran but imported nothing. Added AIG + Growrk
154
+ thin wrappers, schedule entries, and dbchanges2 Parameters seeds; built AIG's
155
+ `c_netsuiteInternalCustomerId` field migration + `Core.Clients` customer-link migration (id
156
+ 34902). Confirmed AIG is not a real supply client; Growrk ships, AIG held. Canonical seed start
157
+ is `2026-03-04` (not `2025-01-01`). (dfranks)
112
158
 
113
159
  ## Related docs
114
160
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.119",
3
+ "version": "1.0.120",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",