toga-ai 1.0.753 → 1.0.755

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: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-01
9
+ updated: 2026-09-02
10
10
  owners: [dfranks, jcardinal, kyalamarthi]
11
11
  files:
12
12
  - library/app/api/netsuite/rest.php
@@ -98,6 +98,21 @@ Forecast2 tables already store.
98
98
  - **TO_DATE rejects impossible dates** (e.g. `2026-06-31`) with an opaque HTTP 400 — validate
99
99
  calendar dates before building SuiteQL.
100
100
 
101
+ - **⚠ SuiteQL returns date/datetime columns in the account DISPLAY format (here `M/D/YYYY`,
102
+ date-only) unless you wrap them in `TO_CHAR` — losing the time is silent (2026-09-02).** A bare
103
+ `SELECT lastmodifieddate` came back `9/2/2026` (no time, locale-dependent order), while
104
+ `TO_CHAR(lastmodifieddate,'YYYY-MM-DD HH24:MI:SS')` gave `2026-09-02 17:39:21`. Any shim that
105
+ **stores or compares a NetSuite date STRING** must `TO_CHAR(col,'YYYY-MM-DD HH24:MI:SS')` it, or it
106
+ drops the time and inherits the account's locale format. This defeated the per-client sync's
107
+ per-record resume cursor: it stored `9/2/2026|<id>`, sat at midnight, and re-scanned the whole day
108
+ every run — fixed by `TO_CHAR`-ing `lastmodifieddate AS lastmodifieddate` in the 8 cursor-feeding
109
+ SELECTs (`listSalesOrders`, `fetchSalesOrderByIdFull`, `listSales`, `listPurchaseOrders`,
110
+ `fetchPurchaseOrderById`, `listItemReceipts`, `listItemFulfillments`, `listInventoryAdjustments`).
111
+ `WHERE`/`ORDER BY` are unaffected by the alias — bound via `TO_TIMESTAMP` — so they still use the base
112
+ column. **Still latent:** `listOpportunities` and `listItems` return raw date-only `lastmodifieddate`
113
+ into their shims; harmless only because neither feeds a resume cursor yet. Engine-side detail on the
114
+ [per-client-sync doc](../../worker/features/netsuite-togasupply-per-client-sync.md).
115
+
101
116
  - **The line SuiteQL must SELECT the line location or it is always NULL — the shim reads it but
102
117
  didn't select it.** `listSalesOrders()`'s line query now selects `tl.location` and
103
118
  `BUILTIN.DF(tl.location) AS location_name`. The line-shim assembly code already *read* those two
@@ -275,6 +290,12 @@ None — uniform across clients (NetSuite is a single shared account).
275
290
 
276
291
  ## Change history
277
292
 
293
+ - 2026-09-02 — Recorded that **SuiteQL returns date columns in the account DISPLAY format (date-only
294
+ `M/D/YYYY` here) unless wrapped in `TO_CHAR`** — a bare `lastmodifieddate` silently drops the time and
295
+ is locale-dependent. This defeated the per-client sync's per-record resume cursor (stored `9/2/2026|id`,
296
+ sat at midnight, re-scanned the whole day); fixed by `TO_CHAR(...,'YYYY-MM-DD HH24:MI:SS') AS
297
+ lastmodifieddate` in the 8 cursor-feeding SELECTs. `listOpportunities`/`listItems` still return raw
298
+ date-only but are latent (no cursor). (jcardinal)
278
299
  - 2026-09-01 — Recorded that **`listPurchaseOrders()` scoped POs by the VENDOR header `entity`** (via
279
300
  the reused `buildEntityInFilter()`), silently dropping every drop-ship / special-order PO for all
280
301
  togasupply clients since the SOAP→REST migration. A PO header `entity` is the vendor, not the
@@ -122,6 +122,35 @@ rename migration run on every client DB first (see the deploy-order gotcha below
122
122
  the same guarded `UPDATE ... WHERE key LIKE 'NETSUITE_LAST_SYNC_DATETIME_%'` so future new clients land on
123
123
  CURSOR keys. Still uncommitted in the working tree.
124
124
 
125
+ ### ⚠ The cursor was storing a date-ONLY, US-format value — the same-second tiebreak was dead (FIXED + DEPLOYED 2026-09-02)
126
+
127
+ The per-record cursor "advanced" but its datetime half was `M/D/YYYY` with **no time** (e.g.
128
+ `9/2/2026|6302323`), so it defeated its own same-second id tiebreak: the cursor sat at **midnight**,
129
+ which no record's real timestamp equals, so every run re-fetched the **whole day** — the resume was
130
+ defeated even while it looked healthy. Root cause: the SuiteQL SELECTs in
131
+ `library/app/api/netsuite/rest.php` returned `lastmodifieddate` **raw**, and NetSuite hands that column
132
+ back in the account **display** format (here `M/D/YYYY`, date-only). Proven: `SELECT lastmodifieddate`
133
+ → `9/2/2026`, but `TO_CHAR(lastmodifieddate,'YYYY-MM-DD HH24:MI:SS')` → `2026-09-02 17:39:21`. See the
134
+ [SuiteQL/REST shim doc](../../library/features/netsuite-suiteql-rest-shim.md) for the general rule.
135
+
136
+ - **Fix part 1 (`rest.php`):** wrapped the select as
137
+ `TO_CHAR(lastmodifieddate, 'YYYY-MM-DD HH24:MI:SS') AS lastmodifieddate` in the **8** SuiteQL SELECTs
138
+ that feed a cursor shim — `listSalesOrders`, `fetchSalesOrderByIdFull`, `listSales`,
139
+ `listPurchaseOrders`, `fetchPurchaseOrderById`, `listItemReceipts`, `listItemFulfillments`,
140
+ `listInventoryAdjustments`. `WHERE`/`ORDER BY` still use the **base** `lastmodifieddate` column (the
141
+ alias doesn't affect them) — the query bounds were already correct ISO via
142
+ `TO_TIMESTAMP('YYYY-MM-DD HH24:MI:SS')`.
143
+ - **Fix part 2 (`common_sync_togasupply.php`):** normalized the datetime half at the **6** per-record
144
+ cursor PUTs to canonical `Y-m-d H:i:s` via `strtotime()` + `date()` (falls back to the raw string if
145
+ `strtotime` fails). The empty-window `"<windowTop>|0"` PUT was already canonical
146
+ (`App_Date::convertToSQLDate`).
147
+ - **Old date-only cursors self-heal.** `strtotime('9/2/2026')` parses to midnight (one-time re-scan of
148
+ that day), then the next successful record overwrites with the full timestamp.
149
+ - **LATENT (noted, not fixed):** `listOpportunities` and `listItems` in `rest.php` also return raw
150
+ date-only `lastmodifieddate` into their shims — harmless today because neither feeds a resume cursor,
151
+ but the same trap if either ever becomes one.
152
+ - Deployed 2026-09-02 (worker + library re-deploy; both files had changed).
153
+
125
154
  ### ⚠ Per-record isolation is SECTION-SPECIFIC — and SALES_ORDERS has NONE (corrected 2026-08-17)
126
155
 
127
156
  Whether a bad record is "stepped over" or freezes the section depends entirely on whether **that
@@ -1095,6 +1124,14 @@ the code deploy).
1095
1124
 
1096
1125
  ## Change history
1097
1126
 
1127
+ - 2026-09-02 — **Per-record cursor was storing a date-ONLY, US-format value — FIXED + DEPLOYED (shared:
1128
+ `worker` + `library`).** The datetime half was `M/D/YYYY` with no time (e.g. `9/2/2026|6302323`), so the
1129
+ cursor sat at midnight and the same-second id tiebreak never matched — every run re-fetched the whole day.
1130
+ Root cause: `rest.php` SELECTed `lastmodifieddate` raw and NetSuite returns it in the account display format
1131
+ (date-only here). Fixed by `TO_CHAR(lastmodifieddate,'YYYY-MM-DD HH24:MI:SS') AS lastmodifieddate` in the 8
1132
+ cursor-feeding SuiteQL SELECTs, plus normalizing the 6 per-record cursor PUTs to canonical `Y-m-d H:i:s`
1133
+ (`strtotime`+`date`, raw fallback). Old cursors self-heal (midnight → one re-scan → full timestamp).
1134
+ `listOpportunities`/`listItems` share the raw-date trap but are latent (no cursor). (jcardinal)
1098
1135
  - 2026-09-02 — **Per-record resume cursor DEPLOYED to prod (shared: `worker` + `library` + `dbchanges2`).**
1099
1136
  Now live: worker commit `06e8f9ef` + library commit `3a70b59c` (tips of `_production`); rename migration
1100
1137
  `Client/2026-09-02b - NetsuiteSyncCursorRename.sql` run on all client DBs **before** the code. Recorded the
@@ -49,7 +49,7 @@
49
49
  | [Surface FIELD ↔ RecordField binding (maxLength / validationType / isWritable fold-in)](features/surface-field-recordfield-binding.md) | A resolver capability that makes a surface `FIELD` element carry its **input contract** — max length, validator name, and whether the caller may write it — inst | _underscore/Model/Core/Surface.php, _underscore/Model/Core/RecordField.php, _underscore/Model/Core/AclFieldPermission.php, _underscore/Model/Client/RecordFieldSetting.php, _underscore/Model/Client/ClientRecordFieldSetting.php, dbchanges2/Core/2026-08-14c - RecordFieldsValidationMetadata.sql, dbchanges2/Client/2026-08-14a - RecordFieldSettingsClientTable.sql, api2/Component/Api/V2/V2.php |
50
50
  | [Surface Resolver (_Model_Core_Surface::resolve — replaces Page::meta)](features/surface-resolver.md) | The runtime for the platform-wide **Surface** UI presentation layer: 9 `_underscore` models plus a cached resolver, `_Model_Core_Surface::resolve(&$api, string | _underscore/Model/Client/Language.php, dbchanges2/Core/2026-08-21 - SalesOrderDecisionSurfacesReseed.sql, dbchanges2/Core/2026-08-24 - RestoreApproveDenyRowActionsVisibility.sql, dbchanges2/Client_Quad/2026-08-24 - ProdPortApprovePoNumberEnabledRule.sql, dbchanges2/Client_Compass/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_CompassCanada/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, toga25-supply/src/App.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/contexts/AuthContext.tsx, dbchanges2/Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql, dbchanges2/Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql, dbchanges2/Core/2026-08-21a - SalesOrderDecisionSurfacesReseed.sql, dbchanges2/Client_Compass/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Client_Quad/2026-07-21c - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_CompassCanada/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Client_Compass/2026-08-04a - ApprovalDetailsAssignedManagerPreferredStage.sql, _underscore/Model/Core/Surface.php, _underscore/Model/Client/AclRecordScript.php, _underscore/Model/Core/RecordScript.php, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Quad/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Client_CompassCanada/2026-07-01a - GrantSurfacesMetaGroupScriptAcl.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Client_Compass/2026-07-15f - SalesOrderRecordActionsRemoveDeadConfigRuleOverrides.sql, dbchanges2/Core/2026-07-17h - Update - ClearApprovalsFilterButtonConfig.sql, dbchanges2/Client_Compass/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Compass/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, dbchanges2/Client_Quad/2026-07-17a - SalesOrderApprovalActionsOverride.sql, dbchanges2/Client_Quad/2026-07-17b - SalesOrderApprovalsFilterButtonOverride.sql, _underscore/Model/Core/SurfaceElement.php, _underscore/Model/Core/Action.php, _underscore/Model/Core/Vocabulary.php, _underscore/Model/Core/VocabularyTerm.php, _underscore/Model/Core/Message.php, _underscore/Model/Client/SurfaceOverride.php, _underscore/Model/Client/MessageTranslation.php, _underscore/Model/Client/ThemeToken.php, _underscore/Model/Core/Page.php, api2/Component/Api/V2/V2.php, dbchanges2/Core/2026-08-12a - NavigationSurfaceSeed.sql, dbchanges2/Client_Compass/2026-08-12a - NavigationRoleGrants.sql, toga25-supply/surface-layer-guide.md |
51
51
  | [Table-View Hyperlink Columns (meta → ACL → computed URL → render)](features/tableview-hyperlink-columns.md) | Any 2.0 table-view column can render its value as a clickable link instead of plain text. | _underscore/Model/Client/TableView.php, _underscore/Model/Client/TrackingNumber.php, api2/Component/Api/V2/V2.php, toga2-supply/src/api/toga.ts, toga2-supply/src/components/ui/Tables/PrimaryTable/helperFunctions/formatTableData.tsx, toga2-supply/src/components/ui/Tables/PrimaryTable/helperFunctions/convertData.tsx, toga2-supply/src/components/ui/Tables/hooks/useDataTableState.tsx, dbchanges2/Client/2026-07-20 - TrackingNumberHyperlinkAndFieldPermission.sql |
52
- | [TableView joins (TableViewJoins → SQL) — aliasing, chained multi-hop joins, ACL](features/tableview-joins.md) | `Client_*.TableViewJoins` rows are what let a table view show a column from a table other than its base record. | _underscore/Model/Client/TableView.php, _underscore/Model.php, api2/Component/Api/V2/V2.php, dbchanges2/Client/2026-08-11a - ServiceRequestsInventoryUnitsTableViews.sql, dbchanges2/Client_Elite/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_CompassCanada/2026-08-27a - ItemFulfillmentTableViewsRebuild.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql |
52
+ | [TableView joins (TableViewJoins → SQL) — aliasing, chained multi-hop joins, ACL](features/tableview-joins.md) | `Client_*.TableViewJoins` rows are what let a table view show a column from a table other than its base record. | _underscore/Model/Client/TableView.php, _underscore/Model.php, api2/Component/Api/V2/V2.php, dbchanges2/Client/2026-08-11a - ServiceRequestsInventoryUnitsTableViews.sql, dbchanges2/Client_Elite/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_CompassCanada/2026-08-27a - ItemFulfillmentTableViewsRebuild.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql, dbchanges2/Client_Nychh/2026-09-02a - PurchaseOrderItemsTableViewRebase.sql |
53
53
  | [TogaIQ Gateway Client (_Component_Api_Togaiq) — AI generate/translate from 2.0](features/togaiq-gateway-client.md) | `_Component_Api_Togaiq` is the 2.0 framework's client for the **TogaIQ** (Talos) AI gateway. | _underscore/Component/Api/Togaiq/Togaiq.php, _underscore/ApiRequest.php |
54
54
  | [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19b - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Growrk/2026-07-13a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Elite/2026-08-17 - FulfillmentTableViews.sql, dbchanges2/Client_Quad/2026-08-26a - BackfillQuadUnitLevelTrackingSingleTn.sql, dbchanges2/Client_Quad/2026-08-26b - BackfillQuadItemLevelTrackingSingleTn.sql, dbchanges2/Client_CompassCanada/2026-08-27a - ItemFulfillmentTableViewsRebuild.sql |
55
55
  | [Units for Items for Purchase Orders — Data Structure](features/units-for-items-for-purchase-orders.md) | Describes how unit (serialized inventory) data is linked to sales-order and purchase-order line items behind the `units-for-items-for-purchase-orders` TableView | _underscore/Model/Client/ServiceRequest.php, dbchanges2/Client/2026-08-11a - ServiceRequestsInventoryUnitsTableViews.sql |
@@ -6,7 +6,7 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-01
9
+ updated: 2026-09-02
10
10
  owners: [bala, apeterson, jcardinal]
11
11
  files:
12
12
  - _underscore/Model/Client/TableView.php
@@ -18,6 +18,7 @@ files:
18
18
  - dbchanges2/Client_CompassCanada/2026-08-27a - ItemFulfillmentTableViewsRebuild.sql
19
19
  - dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql
20
20
  - dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql
21
+ - dbchanges2/Client_Nychh/2026-09-02a - PurchaseOrderItemsTableViewRebase.sql
21
22
  related:
22
23
  - ../../api2/features/tableview-field-metadata.md
23
24
  - ./page-meta-context-field-settings.md
@@ -109,6 +110,37 @@ expansion + `calcDepth`), not out of a third `TableViewJoins` row. When a column
109
110
  past an already-duplicated table, the answer is usually the record fetch, not another join. See
110
111
  [Transfer Orders page](../../toga25-supply/features/transfer-orders-page.md).
111
112
 
113
+ ### Converting a sales-order-item view to purchase-order-items needs an extra VendorItems hop
114
+
115
+ `PurchaseOrderItems` has **no `itemId` column**. `SalesOrderItems` links to the catalog directly
116
+ (`SalesOrderItems.itemId → Items`), but `PurchaseOrderItems` reaches the catalog through a vendor row:
117
+ `PurchaseOrderItems.vendorItemId → VendorItems (record 19) → VendorItems.itemId → Items (record 21)`.
118
+ So rebasing a sales-order-item TableView onto purchase-order-items is **not** a straight recolumn — the
119
+ Items join gains a parent hop:
120
+
121
+ - Insert a **VendorItems** join **before** the Items join.
122
+ - Repoint the Items join's parent from `SalesOrderItems.itemId (66)` to `VendorItems.itemId (99)`.
123
+ - The VendorItems hop must sort first (same `sortOrder ASC, id ASC` rule as any chain) so the bare
124
+ `VendorItems` alias exists when the Items `onB` names it.
125
+
126
+ This is the line-item analog of the parent `purchase-orders` rebase (SalesOrders→PurchaseOrders).
127
+ Shipped for NYCHH as `Client_Nychh/2026-09-02a - PurchaseOrderItemsTableViewRebase.sql` on the
128
+ `items-for-purchase-orders` view (id 16): base record flipped 15 (sales-order-items) → 18
129
+ (purchase-order-items), the 7 quantity columns repointed to PurchaseOrderItems fields, the VendorItems
130
+ hop added, and the unused SalesOrders→bridge→PurchaseOrders INNER chain (join ids 123/124/141) dropped —
131
+ those joins displayed no column, they only filtered rows, which a PO-items base no longer needs.
132
+
133
+ For NYCHH both hops are **100% populated** (PO items 1129/1129, VendorItems 2414/2414), so both stay
134
+ **INNER** — no empty-grid risk (third failure mode above). Verify FK population per tenant first: a
135
+ tenant with sparse `vendorItemId` would need LEFT OUTER or would empty the grid.
136
+
137
+ Core ids for this conversion (verified prod 2026-09-02, identical across clients/envs): **Records** —
138
+ sales-order-items 15, purchase-order-items 18, vendor-items 19, items 21; item-level SO/PO bridges
139
+ 289 (downstream) / 290 (upstream). **RecordFields** — `PurchaseOrderItems.quantity` 94,
140
+ `_qtyFulfilled` 1500, `_qtyOnHand` 1501, `_qtyInvoiced` 1502, `_qtyAvailable` 1503, `_qtyBackordered`
141
+ 1504, `_qtyReceived` 1524, `_qtyOnOrder` 1525, `_qtyCommitted` 1526, `vendorItemId` 91;
142
+ `VendorItems.id` 96, `VendorItems.itemId` 99.
143
+
112
144
  ## Look for an existing unused join before adding one
113
145
 
114
146
  The `inventory_units` view already carried a Units → Items join
@@ -277,9 +309,19 @@ Core-level reference ids used (identical across all clients and environments): *
277
309
  - **An INNER join to an EMPTY FK empties the whole grid with NO error.** Third failure mode: an
278
310
  inner join on an FK that is unpopulated for the tenant drops every row (meta looks fine, data
279
311
  returns 0). Use LEFT OUTER for an optional FK, or verify FK population before choosing INNER.
312
+ - **`PurchaseOrderItems` has no `itemId` — it reaches the catalog via `vendorItemId → VendorItems
313
+ → Items`.** Converting a sales-order-item view (which joins Items directly) to purchase-order-items
314
+ needs an extra **VendorItems** hop inserted before Items, with the Items join repointed to
315
+ `VendorItems.itemId`. Don't just recolumn.
280
316
 
281
317
  ## Change history
282
318
 
319
+ - 2026-09-02 — Documented the **sales-order-item → purchase-order-item view conversion**: it needs an
320
+ extra **VendorItems** hop because `PurchaseOrderItems` has no `itemId` (catalog reached via
321
+ `vendorItemId → VendorItems (record 19) → itemId → Items`), unlike `SalesOrderItems` which joins
322
+ Items directly. Item-level analog of the parent `purchase-orders` rebase — shipped for NYCHH as
323
+ `Client_Nychh/2026-09-02a`, which also dropped the unused SalesOrders→bridge→PurchaseOrders chain
324
+ (join ids 123/124/141). Added the PO-item Core reference ids. (jcardinal)
283
325
  - 2026-09-01 — Added a **third `meta()` failure mode that is not an error**: an **INNER** join to a
284
326
  foreign key that is **unpopulated for the tenant** returns an **empty grid** (meta correct, data
285
327
  0 rows), because an inner join with no matching parent row drops everything. Found on NYCHH's
@@ -8,4 +8,4 @@
8
8
  | [NYCHH Talos AI Assistant Enablement (the only granted tenant)](features/talos-assistant-enablement.md) | 2.0 | **NYCHH is the only tenant granted the Talos AI assistant in TOGa 2.5 Supply, and it AUTHORS ITS OWN suggestion questions.** The feature is seeded **OFF in Core | dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql, toga25-supply/src/hooks/useTalosSurface.ts, toga25-supply/src/hooks/useTalosSurface.test.ts, toga25-supply/src/providers/TalosStreamProvider.tsx, toga25-supply/db-migrations/PLAYBOOK.md |
9
9
  | [NYCHH transfer-order 2.0 model — inventory quantities + V2 field enablement](features/transfer-order-inventory-quantities.md) | 2.0 | The 2.0 (`_underscore`) side of NYCHH transfer-order support: a **two-branch inventory quantity model** on the NYCHH `Item` override, plus the **client-override | _underscore/Model/Nychh/Item.php, _underscore/Model/Nychh/PurchaseOrderItem.php, _underscore/Model/Nychh/TransferOrder.php, _underscore/Model/Nychh/TransferOrderStage.php, _underscore/Model/Client/TransferOrder.php, _underscore/Model/Client/TransferOrderItem.php, dbchanges2/Client_Nychh/2026-08-27a - TransferOrderCustomFieldWritePermission.sql, dbchanges2/Client_Nychh/2026-08-27b - TransferOrderStageStatusIdentifier.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, _underscore/Model/Client/PurchaseOrderItem.php |
10
10
  | [NYCHH TransferOrder → NetSuite push (location roles, interceptor row 312, metadata state)](features/transfer-order-netsuite-push.md) | 2.0 | NYCHH is the **first and only** client on the outbound transfer-order push. | _underscore/Model/Client/TransferOrder.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql |
11
- | [NYC Health & Hospitals](profile.md) | 2.0 | NYC Health & Hospitals (NYCHH) is a TOGA 2.0 client on the `_underscore` platform, prod schema `Client_Nychh`. | dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql, dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql, dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql |
11
+ | [NYC Health & Hospitals](profile.md) | 2.0 | NYC Health & Hospitals (NYCHH) is a TOGA 2.0 client on the `_underscore` platform, prod schema `Client_Nychh`. | dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql, dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql, dbchanges2/Client_Nychh/2026-09-02a - PurchaseOrderItemsTableViewRebase.sql, dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql |
@@ -23,6 +23,7 @@ owners: ["jcardinal", "apeterson", "bala", "akhokhani"]
23
23
  files:
24
24
  - dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql
25
25
  - dbchanges2/Client_Nychh/2026-08-31a - PurchaseOrdersTableViewRebase.sql
26
+ - dbchanges2/Client_Nychh/2026-09-02a - PurchaseOrderItemsTableViewRebase.sql
26
27
  - dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql
27
28
  - dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql
28
29
  - dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql
@@ -84,6 +85,27 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
84
85
  `c7981164-1fe9-5878-b115-85c38dadad43` (`TOGa Supply`), and thread auto-titles are broken
85
86
  **server-side** on that assistant. See
86
87
  [NYCHH Talos enablement](./features/talos-assistant-enablement.md).
88
+ - **`items-for-purchase-orders` table view was rebased to purchase-order-items (2026-09-02).** The
89
+ item-level mirror of the 2026-08-31a parent PO rebase. `Client_Nychh/2026-09-02a -
90
+ PurchaseOrderItemsTableViewRebase.sql` flips view 16 (uuid `988ac251-...`) base record 15
91
+ (sales-order-items) → 18 (purchase-order-items), repoints the 7 quantity columns to
92
+ PurchaseOrderItems fields (quantity 64→94, `_qtyOnHand` 1581→1501, `_qtyAvailable` 1580→1503,
93
+ `_qtyInvoiced` 1583→1502, `_qtyFulfilled` 643→1500, `_qtyOnOrder` 1585→1525, `_qtyBackordered`
94
+ 1584→1504), drops the unused SalesOrders→bridge→PurchaseOrders INNER chain (join ids 123/124/141),
95
+ and adds a **VendorItems** join before Items because `PurchaseOrderItems` has no `itemId` (catalog
96
+ via `vendorItemId → VendorItems → itemId → Items`). Both FK hops are 100% populated for NYCHH (PO
97
+ items 1129/1129, VendorItems 2414/2414) so both stay INNER. Seeds Developer-role record grants for
98
+ records 18 and 19 (both had only Base+API) plus Developer read on `PurchaseOrderItems.quantity`
99
+ (field 94); roles resolved by name, uuid-keyed, idempotent. Mechanism:
100
+ [tableview-joins](../../2.0/apps/_underscore/features/tableview-joins.md). 🚨 **Applied to PROD
101
+ only so far** (view 16 rebased, chain returns 1131 rows) — **NOT run on client-sandbox** (still the
102
+ old sales-order-items version); the parent 2026-08-31a went to prod + client-sandbox, this one is
103
+ prod-only. **OPEN:** the parent `purchase-orders` view filters to vendor 172 (`apiWhereClause
104
+ (vendorId:eq:172)`); this item view has no `apiWhereClause`, so it lists items for all POs — left
105
+ unfiltered (mirrors prior state), flagged as a decision for the dev. A "3L-2250" error reported
106
+ right after this deploy was **unrelated** — prod `Logs.Issue` reference **3L** is a Prudential ASN
107
+ error (`Client_Prudential.AdvanceShippingNoticeUnits` doesn't exist); the suffix is the occurrence
108
+ count (3L, occurrence 2250).
87
109
  - **`inventory_units` table view carries item detail as of 2026-08-18** — Part Number, Manufacturer
88
110
  and Item Description sit directly after Asset Tag, reached through a **chained** Units → Items →
89
111
  Manufacturers join (`dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql`, identical
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.753",
3
+ "version": "1.0.755",
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",