toga-ai 1.0.136 → 1.0.137
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.
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
| [Elite Freshservice Sync (worker2)](features/elite-freshservice-sync.md) | `_Worker_Elite` processes Freshservice webhook events and syncs them into TOGA 2. | worker2/Worker/Elite.php, worker2/Config/dev-kmaramreddy-laptop.ini |
|
|
9
9
|
| [Monitoring Framework (Orchestrator + Child Monitors)](features/monitoring-framework.md) | A unified, DB-driven monitoring framework for business-critical data flows (Compass POs, Prudential asset imports, AIG closed claims, …). | worker2/Worker/Monitor.php, worker2/Worker/Monitors/, worker2/Worker/Notification/Email.php, dbchanges2/Core/2026-05-21 - Monitors.sql |
|
|
10
10
|
| [NetSuite → TOGA Opportunity Sync (API Message Queue + worker2 webhook)](features/netsuite-opportunity-sync.md) | Outbound sync from NetSuite to TOGA for the record types the Forecast2 importer pulls (opportunities first; sales/items/etc. | worker2/Worker/Netsuite.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Controller/Index.php, _underscore/Worker.php, test/@dave/NetSuite/api-message-queue/lib_amq_queue.js, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/ue_amq_drain.js, test/@dave/NetSuite/api-message-queue/ss_amq_drain.js, test/@dave/NetSuite/api-message-queue/DEPLOY_RUNBOOK.md, test/@dave/clickup/backfill_opportunity_numbers.php, test/@dave/clickup/probe_opportunity_fields.php, test/@dave/probe_clickup_desc_match.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
|
|
11
|
-
| [NetSuite → Forecast Open-Orders Sync (salesOrder webhook → OpenOrderItems)](features/netsuite-salesorder-open-orders-sync.md) | Webhook-driven, single-record port of the legacy open-orders importer (TRUE-79142). | worker2/Worker/Netsuite/SalesOrder.php, worker2/Worker/Netsuite.php, test/@dave/probe_salesorder_rest_shape.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
|
|
11
|
+
| [NetSuite → Forecast Open-Orders Sync (salesOrder webhook → OpenOrderItems)](features/netsuite-salesorder-open-orders-sync.md) | Webhook-driven, single-record port of the legacy open-orders importer (TRUE-79142). | worker2/Worker/Netsuite/SalesOrder.php, worker2/Worker/Netsuite.php, test/@dave/probe_salesorder_rest_shape.php, test/@dave/probe_open_order_lines.php, test/@dave/check_so_status.php, test/@dave/check_so_history.php, test/@dave/probe_so_rest_lines.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
|
|
12
12
|
| [Teams Meeting Transcript Export](features/teams-transcript-export.md) | `_Worker_Team_Transcripts` (action `Team/Transcripts/Export`) polls Microsoft Graph for Teams meeting transcripts produced by a set of organizers, classifies ea | worker2/Worker/Team/Transcripts.php, worker2/Config/production.ini |
|
|
@@ -6,12 +6,16 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-18
|
|
10
10
|
owners: ["dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Netsuite/SalesOrder.php
|
|
13
13
|
- worker2/Worker/Netsuite.php
|
|
14
14
|
- test/@dave/probe_salesorder_rest_shape.php
|
|
15
|
+
- test/@dave/probe_open_order_lines.php
|
|
16
|
+
- test/@dave/check_so_status.php
|
|
17
|
+
- test/@dave/check_so_history.php
|
|
18
|
+
- test/@dave/probe_so_rest_lines.php
|
|
15
19
|
- worker/crons/toga2/forecast2/import_open_orders.php
|
|
16
20
|
- worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php
|
|
17
21
|
related:
|
|
@@ -77,6 +81,25 @@ None — uniform (platform-wide Forecast2 sync).
|
|
|
77
81
|
|
|
78
82
|
## Gotchas / known issues
|
|
79
83
|
|
|
84
|
+
- **Async-recalc race → a transiently-empty REST read PURGES a genuinely-open order (silent data loss).**
|
|
85
|
+
The open-status gate is not the only way rows get deleted: on an **open** order, if the REST GET
|
|
86
|
+
momentarily returns **zero importable open lines**, `syncOpenLines` runs its reconcile-delete with an
|
|
87
|
+
empty kept-set and wipes every existing row — there is no separate "removeAll", but the effect is the
|
|
88
|
+
same. This fires when NetSuite's own **asynchronous `-System-` line recompute** (e.g. the open
|
|
89
|
+
gross-profit pass, `CUSTCOL_OPEN_GP`) is still in flight at the instant the webhook's REST GET runs —
|
|
90
|
+
the record reads back open but with its open lines not yet materialized. The webhook is often *triggered
|
|
91
|
+
by that very recompute*, so the GET races it. **Confirmed live 2026-06-18 on SO 7151688** (tranId
|
|
92
|
+
279345): 4 edit webhooks at 14:02:35–48 CDT all succeeded with **0 rows**, while NetSuite's `-System-`
|
|
93
|
+
open-GP recompute wrote the line values at the same 14:02:30–45 window (NS Eastern = worker Central +1h).
|
|
94
|
+
A manual re-sync 22 min later imported all 4 lines correctly. Nothing self-heals it — there is no retry,
|
|
95
|
+
and the batch cron is being decommissioned. **Fix direction (not yet implemented):** never let "open
|
|
96
|
+
status + zero importable lines" trigger a destructive delete — re-fetch after a short delay, or treat it
|
|
97
|
+
as retryable (`throw` → `isSuccess=0` → re-runs) — and only let a **confidently-closed** status
|
|
98
|
+
(Billed / Closed / Cancelled) purge. Diagnose with `check_so_history.php` (systemnote status timeline)
|
|
99
|
+
+ `check_so_status.php` (current REST status) + `probe_so_rest_lines.php` (REST `item.items` as the
|
|
100
|
+
handler sees them). NB: `App_ApiTransaction::execute(false)` returns a raw JSON **string** for a record
|
|
101
|
+
GET — `json_decode` it directly; double-encoding it (`json_decode(json_encode($resp))`) silently yields
|
|
102
|
+
an empty object and a false "empty record" reading.
|
|
80
103
|
- **REST shape ≠ SOAP shape.** The cron reads the SOAP-shim shape; this handler reads the REST record
|
|
81
104
|
(`status->refName`, line `quantityBilled`, `class->refName`, `entity->id`, `salesRep->id`,
|
|
82
105
|
`shippingCost`). Verified against live orders via `test/@dave/probe_salesorder_rest_shape.php`.
|
|
@@ -131,6 +154,15 @@ and are a candidate to extract into a shared `_Component_Forecast_Db` before the
|
|
|
131
154
|
|
|
132
155
|
## Change history
|
|
133
156
|
|
|
157
|
+
- 2026-06-18 — **Verified live in production** after the SalesOrder enqueuer left Testing. 14 orders
|
|
158
|
+
reconciled against NetSuite SuiteQL (`probe_open_order_lines.php`); 13 at $0.00 delta. Found one
|
|
159
|
+
miss — SO 7151688 (open, ~$20.31, 0 rows) — traced to the **async-recalc race** above (see new
|
|
160
|
+
gotcha): the order was approved (Pending Approval → Pending Fulfillment) *before* the enqueuer was
|
|
161
|
+
released (so the approval itself never enqueued — one-time cutover artifact), then NetSuite's
|
|
162
|
+
`-System-` open-GP recompute fired the edit webhooks and the handler's REST GET raced that recompute,
|
|
163
|
+
reading zero open lines → reconcile-delete. A manual re-sync corrected it. Added diagnostics
|
|
164
|
+
`check_so_status.php`, `check_so_history.php`, `probe_so_rest_lines.php`. No code change yet — fix
|
|
165
|
+
direction recorded in the gotcha. (dfranks)
|
|
134
166
|
- 2026-06-17 — **Fixed dropped zero-revenue/open-cost lines.** REST omits `rate` for $0 lines, and the
|
|
135
167
|
`if (!is_numeric($rate)) continue;` guard was skipping them — but the cron inserts these (profit =
|
|
136
168
|
−qtyOpen × unitCost; 575 such rows in prod). Now defaults missing/non-numeric rate to 0 so the both-zero
|
package/package.json
CHANGED