toga-ai 1.0.98 → 1.0.99

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.
@@ -5,3 +5,4 @@
5
5
  | [Worker (1.0 Framework) Architecture](architecture.md) | `worker` is the legacy (**1.0** `App_` framework) **background-job tier**. | worker/index.php, worker/_/app/framework.php, worker/crons/, worker/schedules/, worker/ebs/cron.worker.php, worker/.ebextensions/035_cron.worker.config |
6
6
  | [Forecast2 ↔ NetSuite Reconciliation & Trueup Tooling](features/forecast2-netsuite-reconciliation.md) | CLI tools to **audit** and **repair** drift between the production `Forecast` DB (core2) and NetSuite. | test/@dave/reconcile_netsuite_totals.php, test/@dave/analyze_netsuite_forecast_diff.php, test/@dave/trueup_sales.php, test/@dave/trueup_open_orders.php, test/@dave/trueup_opportunities.php, test/@dave/probe_sales_gap_direct.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
7
7
  | [Prudential: Send Shipments for the Day report (daily cron)](features/send-shipments-for-the-day.md) | Daily cron (9:00 PM) that emails Prudential and Dell stakeholders an Excel report of all devices shipped that day, including tracking number, serial number, emp | worker/crons/notifications/reports/send_shipments_for_the_day.php |
8
+ | [Onboarding a Client to the NetSuite TOGa Supply Sync](workflows/onboarding-client-to-netsuite-togasupply-sync.md) | How to add a new TOGa 2 client to the per-client NetSuite → TOGa Supply importer (`worker/crons/toga2/netsuite/`). | worker/crons/toga2/netsuite/sync_togasupply.php, worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/schedules/cron.worker.sync.json, dbchanges2/_modules/netsuite/2026-04-01 - Parameters.sql |
@@ -0,0 +1,116 @@
1
+ ---
2
+ title: Onboarding a Client to the NetSuite TOGa Supply Sync
3
+ framework: "1.0"
4
+ repo: worker
5
+ project: Worker
6
+ client: shared
7
+ type: workflow
8
+ status: active
9
+ updated: 2026-06-16
10
+ owners: ["dfranks"]
11
+ files:
12
+ - worker/crons/toga2/netsuite/sync_togasupply.php
13
+ - worker/crons/toga2/netsuite/common_sync_togasupply.php
14
+ - worker/schedules/cron.worker.sync.json
15
+ - dbchanges2/_modules/netsuite/2026-04-01 - Parameters.sql
16
+ related: []
17
+ ---
18
+
19
+ ## Summary
20
+ How to add a new TOGa 2 client to the per-client NetSuite → TOGa Supply importer
21
+ (`worker/crons/toga2/netsuite/`). Each client gets a thin wrapper cron that sets its
22
+ `CLIENT_CONFIGURATION` + `IS_ENABLED_*` flags and then `require_once`s the shared
23
+ `common_sync_togasupply.php`, which builds all lookups and runs the 6 sync sections
24
+ (Sales Orders, Purchase Orders, Invoices, Item Receipts, Item Fulfillments, Inventory
25
+ Adjustments). Reads come from production NetSuite (REST/SuiteQL); writes go to the TOGa2
26
+ API (`App_Api_Toga2::send`), which lands in that client's `Client_<Name>` database.
27
+
28
+ ## Steps
29
+ 1. **Create the wrapper** `worker/crons/toga2/netsuite/sync_togasupply_<client>.php`,
30
+ modeled exactly on an existing one (e.g. `sync_togasupply_compass_usa.php`). Set
31
+ `CLIENT_CONFIGURATION` (`client`, `isParentCustomer`, `api`, `secret`,
32
+ `importCustomerPurchaseOrdersOnVendor`) and the constants. Shared constants are the same
33
+ across clients: `NETSUITE_CUSTOM_FIELD_ID__END_USER_CUSTOMER = 3149`,
34
+ `SHIPPING_LOCATION_TYPE_UUID = 527a3995-…`, `WAREHOUSE_LOCATION_TYPE_UUID = 78c24f51-…`,
35
+ and `importCustomerPurchaseOrdersOnVendor = 10ed18ec-0999-53d9-f9db-5892d90f09d4`.
36
+ Get `client` from `Core.Clients` (by name); get `api`/`secret` from
37
+ `Client_<Name>.Apis`. Lint with `C:\xampp7\php\php.exe -l` (prod is PHP 7.2).
38
+ 2. **Determine `isParentCustomer` by probing NetSuite — do not assume.** It controls whether
39
+ the common body calls `listChildCustomers()` (parent) or `fetchCustomerById()` (flat).
40
+ `listChildCustomers(N)` runs `SELECT id FROM customer WHERE parent = N`. If the client's
41
+ NetSuite customer has **zero children**, `isParentCustomer` MUST be `false`, or the sync
42
+ builds an empty customer list, registers nothing, and imports nothing (silent no-op). See
43
+ Gotchas.
44
+ 3. **Seed the Parameters table** (see the dedicated section below) — REQUIRED or the sync
45
+ aborts on its first parameter read.
46
+ 4. **Add the schedule entry** in `worker/schedules/cron.worker.sync.json`: all
47
+ `sync_togasupply_*` jobs run `*/5 * * * *`, `active: 1`. The cron only runs once a
48
+ schedule entry references the file and the worker is redeployed.
49
+ 5. **Deploy order matters:** the Parameters seed must hit prod `Client_<Name>` **before or
50
+ with** the cron going live, or the sync 404-aborts (and throws a Sentry error) every 5
51
+ minutes.
52
+
53
+ ## Parameters seed (the required, easily-missed step)
54
+ The sync reads/writes per-client sync state via the TOGa2 API `/parameters` endpoint, which
55
+ is **update-only** — a `GET`/`PUT` on a non-existent key returns `404 EV-6`, and because
56
+ `App_Api_Toga2::send()` defaults `$throwExceptionOnApiError = true`, that 404 **throws and
57
+ aborts the entire run** before any record is processed. The `startModeIteration()` ternary
58
+ (`isSuccess ? value : MIN_DATETIME`) looks like it tolerates a missing key, but it never
59
+ gets there because `send()` throws first. Every live client (e.g. Compass) was seeded at
60
+ onboarding; a new client has none.
61
+
62
+ The `Parameters` *table* already exists in every `Client_<Name>` DB — you only seed **rows**.
63
+ Add a `dbchanges2/Client_<Name>/<date> - NetsuiteSyncParameters.sql` migration modeled on
64
+ `dbchanges2/_modules/netsuite/2026-04-01 - Parameters.sql`, inserting all **12** keys:
65
+ - 6 × `NETSUITE_LAST_SYNC_DATETIME_{SALES_ORDERS,PURCHASE_ORDERS,INVOICES,ITEM_RECEIPTS,ITEM_FULFILLMENTS,INVENTORY_ADJUSTMENTS}`
66
+ → a start datetime. This sets how far back the initial backfill pulls (5-day windows, every
67
+ 5 min). Pick deliberately: `2018-01-01` = full history; a recent date = go-forward only.
68
+ - 6 × `NETSUITE_EXECUTION_MODE_*` → `864000-IDLE` (the interval is clamped to
69
+ `MAX_TIME_WINDOW_TO_FETCH_FROM_NETSUITE_SECONDS = 432000` on first run, so the exact
70
+ seed interval is not critical; `IDLE` is what matters).
71
+
72
+ Use `(UUID(), '<key>', '<value>')` rows — `Parameters.uuid` is required and unique.
73
+ dbchanges2 files use the unqualified `Parameters` table name (deployer selects the DB from
74
+ the directory; no `USE`).
75
+
76
+ ## Systems involved
77
+ - `worker` cron tier (1.0) — the wrapper + `common_sync_togasupply.php`.
78
+ - Production NetSuite (account `1095849`) via `App_Api_Netsuite_Rest` (SuiteQL/REST) — reads.
79
+ - TOGa2 API (`App_Api_Toga2`) — writes to `Client_<Name>` (+ logs to `Logs_<Name>`).
80
+ - `dbchanges2/Client_<Name>/` — the Parameters seed migration.
81
+
82
+ ## Local testing
83
+ - The worker's `[api] _` and api2's `[database]/[databaseClient]/[database1]` on the dev
84
+ laptop point at local (`http://api2/v2` → local Apache/api2 → localhost MySQL). NetSuite is
85
+ the **production** account but reads are read-only.
86
+ - A local end-to-end run needs the client's schemas present locally: import both
87
+ `Client_<Name>` AND `Logs_<Name>` from the read replicas (client2 / logs clusters) into
88
+ local XAMPP. `Logs_<Name>` only needs the **schema** (`mysqldump --no-data`) — its API-log
89
+ history can be many GB; api2 connects to it (link `ClientLogs`) on every request and 500s
90
+ with "Unknown database 'logs_<name>'" if absent.
91
+ - Run the cron in place with `C:\xampp8\php\php.exe` from the `worker/` dir; the autoloader
92
+ walks up to `worker/_` for `__APPROOT__`. `App_Api_Netsuite_Rest::authenticate()` +
93
+ `App_ApiTransaction->setLogging(false)` is the laptop-safe NetSuite pattern (see
94
+ `test/@dave/nsq.php`, a read-only SuiteQL runner).
95
+ - Verified behavior: empty window → 0 writes, checkpoint advances (clean no-op); record
96
+ already present → `PUT` update (idempotent, keyed on `c_netsuiteInternalSalesOrderId`, no
97
+ dupes); record absent → `POST` insert. Checkpoints (`NETSUITE_LAST_SYNC_DATETIME_*`)
98
+ advance by one window and `NETSUITE_EXECUTION_MODE_*` flips back to `…-IDLE` when a section
99
+ finishes cleanly.
100
+
101
+ ## Edge cases & escalation
102
+ - **`isParentCustomer` wrong** → silent no-op (true on a childless customer) or missed
103
+ end-user matching. Always probe NetSuite first.
104
+ - **Parameters not seeded in prod** → sync throws every 5 min; surfaces as a `worker1` Sentry
105
+ error and zero imports.
106
+ - **Missing catalog item** on an incoming order may throw mid-run depending on the section;
107
+ the checkpoint does not advance past a throwing window, so it self-heals once the dependency
108
+ exists.
109
+
110
+ ## Change history
111
+ - 2026-06-16 — Documented the onboarding process after adding Quad (TRUE-79575): wrapper +
112
+ schedule + the required 12-key Parameters seed; captured the `isParentCustomer` NetSuite
113
+ probe and the local Client_/Logs_ schema requirement. (dfranks)
114
+
115
+ ## Related docs
116
+ - [Forecast2 NetSuite reconciliation](../features/forecast2-netsuite-reconciliation.md)
@@ -5,7 +5,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
5
5
  ## 1.0 framework
6
6
 
7
7
  - **library** (Library) _(framework core)_ — 4 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
- - **worker** (Worker) — 4 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
8
+ - **worker** (Worker) — 5 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
9
9
  - **togadesk** (TOGa Desk) — 7 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
10
10
  - **togaview** (TOGa View) — 5 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
11
11
  - **webhook** (Webhook) — 1 doc(s) → [1.0/apps/webhook/INDEX.md](1.0/apps/webhook/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.98",
3
+ "version": "1.0.99",
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",