toga-ai 1.0.298 → 1.0.299

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.
@@ -6,8 +6,8 @@ project: Worker
6
6
  client: nycdoe
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-06-19
10
- owners: [mhammontree]
9
+ updated: 2026-07-09
10
+ owners: [mhammontree, sking]
11
11
  files:
12
12
  - worker/crons/sync/nycdoe/import_asn.php
13
13
  - worker/crons/sync/nycdoe/import_inc.php
@@ -17,6 +17,7 @@ files:
17
17
  - worker/crons/sync/nycdoe/1_send_asn_to_netsuite.php
18
18
  - worker/crons/sync/nycdoe/2_send_serials_to_netsuite.php
19
19
  - worker/crons/sync/nycdoe/3_create_installation_ticket.php
20
+ - worker/crons/sync/nycdoe/test_multi_po_receipt_resolution.php
20
21
  - worker/crons/sync/nycdoe/send_ticket_updates.php
21
22
  - worker/crons/sync/nycdoe/send_request_item_updates.php
22
23
  - worker/crons/sync/nycdoe/send_nycdoe_proof_of_delivery.php
@@ -34,6 +35,7 @@ files:
34
35
  - library/app/asnprocessor/lexmark.php
35
36
  - library/app/asnprocessor/acer.php
36
37
  - library/app/edi.php
38
+ - library/app/netsuite.php
37
39
  related:
38
40
  - ../profile.md
39
41
  - hold-status-sync.md
@@ -131,12 +133,27 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
131
133
  `qtyOrder` is immutable** — there is no update path to a sent SO line; growth must be a
132
134
  new item row → new SO line.
133
135
  - **Stage 4:** reconciles the NetSuite **PO** (`netSuiteInternalPurchaseOrderId` — not the
134
- SO id) and sends serial inventory assignments.
136
+ SO id) and sends serial inventory assignments. It is already multi-PO aware
137
+ (`listPurchaseOrdersCreatedFromSalesOrder`, groups items by PO, stamps each item's
138
+ `netSuiteInternalPurchaseOrderId`) **but only stamps items where the PO is NULL and never
139
+ re-stamps** — so an item stamped to PO-A but later *received* on PO-B is never corrected.
140
+ **TOGA does NOT create NetSuite POs anywhere in the DOE flow.** Stage 3 creates only the SO
141
+ (and, when needed, a special-order inventory Item); POs are generated inside NetSuite by
142
+ procurement/special-order. A single SO can therefore be fulfilled across **multiple POs**
143
+ (back-orders, multi-vendor, re-cut lines) — this is a legitimate NetSuite outcome, so the
144
+ durable fix lives downstream (resolve receipts SO-wide), not in preventing PO splits.
135
145
  - **Stage 5:** for units `WHERE togadeskRepairOrderId IS NULL`, reads serials from NetSuite
136
146
  item receipts (so TOGa Desk RO serials are **driven by NetSuite**, not the ASN unit),
137
147
  creates/reuses TOGa Desk manufacturers/models/assets/serial-numbers/MSO and creates **a
138
148
  new repair_orders row unconditionally per loop iteration** — one RO per Unit (a 50-cable
139
149
  line = 1 unit = 1 RO). Existing-serial lookup is scoped **per item row** (`$asnItemId`).
150
+ Item receipts are now resolved **Sales-Order-wide** via
151
+ `App_NetSuite::listItemReceiptsCreatedFromSalesOrder($soId)` (fans out across ALL POs from
152
+ `listPurchaseOrdersCreatedFromSalesOrder`, dedupes receipts by `internalId`), cached once
153
+ per ASN, with `getItemReceipt` cached per receipt id. The inner item query also requires
154
+ `netSuiteInternalSalesOrderId IS NOT NULL` so a null-SO first row cannot poison the per-ASN
155
+ receipt cache. Self-healing: units previously stuck under an unstamped PO clear on
156
+ subsequent 5-min cron runs.
140
157
  - **"#/N received" UI** (`togadesk/desk/template/pages/managedserviceorders/view.php`):
141
158
  denominator = `SUM(qtyOrder)` per part (committed + unsent); numerator = Units with
142
159
  `togadeskRepairOrderId IS NOT NULL`.
@@ -218,6 +235,36 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
218
235
  - **Stage 6 is not currently running** (`4_` paused with `active: 0`, `5_` unscheduled).
219
236
  - TRUE-77354: Stage 5 lookups changed `LIKE` → `=` to stop `_`/`%` wildcard matches in
220
237
  serials.
238
+ - **Multi-PO-per-SO silently drops install tickets (FIXED 2026-07-09).** Stage 5 used to
239
+ fetch item receipts only for the single stamped `netSuiteInternalPurchaseOrderId`. When a
240
+ NetSuite SO is fulfilled across more than one PO, units received under a PO *other* than the
241
+ stamped one never got a TogaDesk repair order, and the every-5-min cron silently re-queried
242
+ the wrong PO forever. No error was raised — the symptom was serials never syncing. Fixed by
243
+ resolving receipts SO-wide (see Stage 5). Before, no `App_NetSuite` method listed receipts
244
+ by SO — only by PO (`listItemReceiptsCreateFromPurchaseOrder`) or location
245
+ (`listAllItemReceiptsForLocation`); the new `listItemReceiptsCreatedFromSalesOrder`
246
+ (`library/app/netsuite.php`, after `listItemReceiptsCreateFromPurchaseOrder`) fills that gap.
247
+ Concrete case: customer PO `S202643596` (ASN 26339, SO internalId 6971858) split across PO
248
+ 164785 (internalId 6971859 → `38S0500` synced, 15 ROs) and PO 165751 (`50M7280` + `A3L980`
249
+ NOT synced). PRs: library#831 (merge first) then worker#1670.
250
+
251
+ ## Debugging & DB topology (DOE 1.0 sync)
252
+
253
+ Reference for future DOE investigations — the 1.0 worker cannot run on a dev/Windows box;
254
+ use the toga DB MCP + `Logs.API` instead of running prod code locally.
255
+
256
+ - **`db_core` alias → legacy (V1) environment, schema `Core`** — holds the FLAT tables
257
+ `AdvanceShippingNotices` / `AdvanceShippingNoticeItems` / `AdvanceShippingNoticeUnits`
258
+ (columns incl. `togadeskRepairOrderId`, `serialNumber`, `netSuiteInternalSalesOrderId`,
259
+ `netSuiteInternalPurchaseOrderId`, `customerPurchaseOrder`). The 1.0 cron reads **legacy/Core**.
260
+ - **Do NOT confuse with prod (V2) `Client_Nycdoe`** — a DIFFERENT, normalized schema
261
+ (`AdvanceShippingNoticeItemUnits` with a `unitId` FK). The 1.0 cron does not read V2.
262
+ - **NetSuite SOAP request/response payloads** are logged in legacy `Logs.API` — filter
263
+ `sourceJob LIKE '%3_create_installation_ticket%'`; `requestPayload` contains the PO
264
+ internalId. (The multi-PO case above was verified this way: the receipt search for PO
265
+ 6971859 returned `totalRecords=1`, only `38S0500`.)
266
+ - **`Bridge_NetSuite`** (legacy) has `SalesOrders` / `SalesOrderItems` / `SerialNumbers` but
267
+ **no `PurchaseOrders` table** — POs are pulled live from the NetSuite API.
221
268
 
222
269
  ## Operating rules when changing this integration
223
270
 
@@ -231,6 +278,14 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
231
278
  of the consumer query; `php -l` every touched file.
232
279
 
233
280
  ## Change history
281
+ - 2026-07-09 — Fixed silent install-ticket drop when a NetSuite SO is fulfilled across
282
+ multiple POs: Stage 5 now resolves item receipts Sales-Order-wide via new helper
283
+ `App_NetSuite::listItemReceiptsCreatedFromSalesOrder` (fans across all POs from the SO,
284
+ dedupes by internalId, cached per ASN), and its item query requires
285
+ `netSuiteInternalSalesOrderId IS NOT NULL`. Root cause: TOGA doesn't create DOE POs, so
286
+ multi-PO-per-SO is legitimate and units received on an unstamped PO were never ticketed.
287
+ Added multi-PO notes to Stage 4/5, a gotcha, and a DB-topology/debugging section.
288
+ PRs library#831 + worker#1670. (sking)
234
289
  - 2026-06-29 — Outbound status sync now keeps DOE holds authoritative (TRUE-79922). Hold
235
290
  detection moved to the SNOW native `state` field (not `u_status_task`); Assigned-force
236
291
  blocks guarded against local `HOLD_*` to stop the `state:3`↔`state:2` self-clobber; RITM
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.298",
3
+ "version": "1.0.299",
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",