toga-ai 1.0.827 → 1.0.828

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,7 +6,7 @@ project: Library
6
6
  client: nychh
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-09-14
9
+ updated: 2026-09-16
10
10
  owners: [jcardinal, bala, rgirish]
11
11
  files:
12
12
  - library/app/api/toga2.php
@@ -15,11 +15,11 @@ files:
15
15
  - worker/crons/toga2/netsuite/sync_togasupply_hh.php
16
16
  - worker/crons/toga2/netsuite/common_sync_togasupply.php
17
17
  - worker/crons/toga2/netsuite/diagnose_hh_stuck_transfer_order.php
18
+ - worker/crons/toga2/netsuite/probe_import_single_order_hh.php
19
+ - worker/crons/toga2/netsuite/relink_transfer_order_pos_hh.php
18
20
  - dbchanges2/Core/2026-08-27a - TransferOrderUuidSearchableIdentifier.sql
19
21
  - dbchanges2/Client/2026-08-28a - TransferOrderItemsTimestampColumns.sql
20
- - dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestampColumnsReposition.sql
21
- - dbchanges2/Client/2026-08-29 - ItemReceiptTransferOrderIdFieldPermission.sql
22
- - dbchanges2/Client/2026-08-29b - ItemReceiptItemTransferOrderItemIdFieldPermission.sql
22
+ - dbchanges2/Client_Nychh/2026-09-16a - TransferOrderBridgeAcl.sql
23
23
  related:
24
24
  - ../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md
25
25
  - ../../../1.0/apps/library/features/toga2-api-client-and-bridge.md
@@ -92,6 +92,80 @@ sales-order linker. See the shared
92
92
  [SO↔PO bridge direction](../../../2.0/apps/_underscore/features/sales-order-purchase-order-bridge-direction.md)
93
93
  doc for the bridge naming convention.
94
94
 
95
+ ### ⚠ The bridge links were never being created — three stacked causes, fixed + prod-verified 2026-09-16
96
+
97
+ The PO↔TO bridge links (header **and** line) were silently never forming for NYCHH. Three
98
+ independent faults, peeled in order; the last was the real blocker:
99
+
100
+ 1. **Linking only ever ran from the `PURCHASE_ORDERS` section, which never runs for NYCHH.** The
101
+ links depended on the PO section, so a transfer-order import created none. **Fix:** a new **public**
102
+ `App_Api_Toga2::linkTransferOrderPurchaseOrders()` now runs on **every** transfer-order
103
+ import/retry from inside `syncTransferOrderFromNetsuite()`. It is **idempotent** — re-running an
104
+ import repairs any missing link. In `TRANSFER_ORDER_DETECTION_MODE__STOCKING_FLAG` mode it finds
105
+ the customer PO by the NetSuite **PO #** (`otherRefNum`, split on `' + '`) under the
106
+ `clientConfiguration['importCustomerPurchaseOrdersOnVendor']` vendor (the synthetic **Agilant**
107
+ vendor), then calls `linkPurchaseOrderToTransferOrder` for the header
108
+ (`PurchaseOrders_TransferOrders`) and line (`PurchaseOrderItems_TransferOrderItems`) links.
109
+ 2. **`linkPurchaseOrderToTransferOrder` read the TO line items from the wrong place, so line links
110
+ never formed.** It read `$transferOrder->transferOrderItems`, but **api2 NEVER inlines that
111
+ collection at any depth** (`transfer-orders` has no `InherentRecordChildren` row — same root cause
112
+ as the idempotency bug), so it was always empty. **Fix:** read TO items via
113
+ `getTransferOrderItemsByUuid` (the reliable direct `GET /transfer-order-items` query). Also lowered
114
+ the TO existence GET to `depth=1` and added `recordsPerPage=1000` to the PO-items GET.
115
+ 3. **ROOT CAUSE — an ACL gap, not code.** The NYCHH **API** role had **no write ACL** on the two
116
+ bridge records **325** (`PurchaseOrders_TransferOrders`) and **329**
117
+ (`PurchaseOrderItems_TransferOrderItems`), so the link POST failed api2 **EZ-3 / WZ-2** ("no
118
+ fields in which you are authorized to write"). The working **sales-order** bridge (record **276**,
119
+ `PurchaseOrders_SalesOrders`) already had the grant, which is why SO links worked and TO links did
120
+ not. **Also key: on CREATE, api2 `isPermitted()` DENIES a record permission that has no
121
+ `AclLogicGroups` chain — a bare `AclRecordPermissions` row is inert** (the standard four-table
122
+ chain rule — see [ACL permission chain](../../../2.0/apps/_underscore/features/acl-permission-chain.md)).
123
+
124
+ **Fix migration:** `dbchanges2/Client_Nychh/2026-09-16a - TransferOrderBridgeAcl.sql` seeds the full
125
+ four-table chain (`AclRecordExpressions` `'all'` → `AclRecordPermissions` full CRUD → `AclLogicGroups`
126
+ AND → `AclLogicGroupExpressions`) **plus** `AclFieldPermissions isWritable = 1`, for roles **Base**
127
+ and **API**, on records **325** and **329**, mirroring the working record 276. Guarded/re-runnable,
128
+ roles resolved by name, fresh random v4 uuids, `Core` ids hardcoded (no `Client_*`→`Core` JOIN — the
129
+ cross-cluster rule). The only writable fields a bridge POST touches are the row `uuid` + the two FK
130
+ columns. **Data facts:** Core bridge record ids **276**=`PurchaseOrders_SalesOrders`,
131
+ **325**=`PurchaseOrders_TransferOrders`, **329**=`PurchaseOrderItems_TransferOrderItems`; Core
132
+ `RecordFields` **2210**=uuid/**2211**=purchaseOrderId/**2212**=transferOrderId (rec 325),
133
+ **2226**=uuid/**2227**=purchaseOrderItemId/**2228**=transferOrderItemId (rec 329); `Client_Nychh`
134
+ roles **1**=Base, **2**=Developer, **3**=API, **4**=Public. Reviews: `cto` AGREE (four-table chain),
135
+ `sql-reviewer` SAFE TO MERGE.
136
+
137
+ **ACL failures are now LOUD in the linker.** Both bridge existence checks **THROW** when their GET
138
+ returns no `meta->totalRecordCount` (an ACL **read** gap) instead of silently skipping the create;
139
+ the create POST already throws on a write-fail (EZ-3/WZ-2). So any future ACL gap stops the run
140
+ rather than quietly producing zero links.
141
+
142
+ **Verified in prod:** link `PurchaseOrderItems.id = 589` ↔ `TransferOrderItems.id = 7155` created for
143
+ PO **EIT0012548** ↔ TO **284624** (NetSuite SO **7326440**).
144
+
145
+ ### PO discovery for a NYCHH transfer order — `otherRefNum` + Agilant vendor, NEVER `createdPo`
146
+
147
+ For NYCHH the SO **line-level `createdPo` is NULL**; the PurchaseOrder is the **customer PO**
148
+ (`otherRefNum`, the NetSuite "PO #" field) imported against the synthetic **Agilant** vendor. So PO
149
+ discovery from a transfer order must use `otherRefNum` + the Agilant vendor — never `createdPo`.
150
+ NetSuite proof: SO **7326440** `tranid` 284624, `otherrefnum` EIT0012548; its lines' `createdpo` are
151
+ all null; in NetSuite it links only to an invoice + item fulfillment (there is **no** PurchaseOrder
152
+ record on the SO itself).
153
+
154
+ ### Operator tools for this path (manual CLI, NOT scheduled)
155
+
156
+ - **`worker/crons/toga2/netsuite/probe_import_single_order_hh.php`** — imports/relinks **one**
157
+ NetSuite order by internal id for NYCHH. Builds lookups by requiring `common_sync_togasupply.php`
158
+ with all `IS_ENABLED_INTEGRATION_*` false, fetches via `fetchSalesOrderByIdFull`, routes via
159
+ `isTransferOrder`, runs `syncTransferOrderFromNetsuite`, then prints diagnostics (`[diag]` PO
160
+ lookup, `[trace]` header-link POST response) and verifies header + line links. No cursor writes /
161
+ no process lock.
162
+ - **`worker/crons/toga2/netsuite/relink_transfer_order_pos_hh.php`** — backfills links for **all**
163
+ existing `Client_Nychh` transfer orders. Pages every `TransferOrders` row, fetches each NetSuite
164
+ order for its PO #, runs only the link step (`App_Api_Toga2::linkTransferOrderPurchaseOrders` — no
165
+ re-import), idempotent, per-order `try/catch` that logs + collects failures (a sanctioned
166
+ operator-tool exception to fail-loud), with a live progress bar + counts + end summary. Optional
167
+ first arg = limit.
168
+
95
169
  ## Transfer-order origin/destination location resolution
96
170
 
97
171
  `TransferOrders.originLocationId` is **NOT NULL**, so a missing origin fails the V2 insert with
@@ -384,6 +458,25 @@ item receipts replays about two years unless it is rewound forward first.
384
458
 
385
459
  ## Change history
386
460
 
461
+ - 2026-09-16 - **RESOLVED: the PO↔TO bridge links were never being created** (fixed end-to-end,
462
+ prod-verified: PO EIT0012548 ↔ TO 284624 / SO 7326440, link `PurchaseOrderItems.id=589` ↔
463
+ `TransferOrderItems.id=7155`). Three stacked causes: (1) linking only ran from the `PURCHASE_ORDERS`
464
+ section, which never runs for NYCHH → new **public** `linkTransferOrderPurchaseOrders()` now runs on
465
+ every TO import/retry, idempotent, finds the customer PO by `otherRefNum` (split on `' + '`) on the
466
+ Agilant vendor (`clientConfiguration['importCustomerPurchaseOrdersOnVendor']`); (2)
467
+ `linkPurchaseOrderToTransferOrder` read TO items from `$transferOrder->transferOrderItems`, which
468
+ api2 NEVER inlines → now reads via `getTransferOrderItemsByUuid` (also lowered the TO GET to depth 1,
469
+ added `recordsPerPage=1000` to the PO-items GET); (3) **ROOT CAUSE** — the NYCHH API role had no
470
+ write ACL on bridge records **325** (`PurchaseOrders_TransferOrders`) / **329**
471
+ (`PurchaseOrderItems_TransferOrderItems`), so the POST failed **EZ-3/WZ-2**; the working SO bridge
472
+ (rec 276) had it. Fix migration `dbchanges2/Client_Nychh/2026-09-16a - TransferOrderBridgeAcl.sql`
473
+ seeds the full four-table chain + `AclFieldPermissions` for roles Base + API on 325/329, mirroring
474
+ 276 (guarded, roles by name, random v4 uuids, Core ids hardcoded). Also made ACL failures loud in
475
+ the linker (both existence GETs THROW on no `totalRecordCount`). Recorded the NYCHH PO data model:
476
+ `createdPo` is NULL, the PO is the customer PO (`otherRefNum`) on the Agilant vendor. Added two
477
+ manual operator tools (`probe_import_single_order_hh.php`, `relink_transfer_order_pos_hh.php`).
478
+ `cto` AGREE, `sql-reviewer` SAFE TO MERGE. **Deploy: library + worker together (via a WORKER
479
+ deploy — the worker box runs its own `/var/www/library` clone).** (jcardinal)
387
480
  - 2026-09-14 - **RESOLVED: the INVENTORY_ADJUSTMENTS destination wedge.** Root cause (prod): TO 277871
388
481
  (SO 7102750) has customer **3997 "1896 Laurel Hill Management, LLC"** — not a NYCHH customer and with
389
482
  **no `Client_Nychh.Locations` row**, so the destination never resolved and the NOT NULL insert 1048'd
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.827",
3
+ "version": "1.0.828",
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",