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.
- package/knowledge/1.0/apps/library/features/netsuite-suiteql-rest-shim.md +22 -1
- package/knowledge/1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md +37 -0
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -1
- package/knowledge/2.0/apps/_underscore/features/tableview-joins.md +43 -1
- package/knowledge/clients/nychh/INDEX.md +1 -1
- package/knowledge/clients/nychh/profile.md +22 -0
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
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-
|
|
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