toga-ai 1.0.883 → 1.0.885
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/2.0/apps/_underscore/features/page-meta-context-field-settings.md +18 -2
- package/knowledge/2.0/apps/toga25-supply/features/meta-driven-table-data.md +42 -11
- package/knowledge/2.0/apps/toga25-supply/features/transfer-orders-page.md +2 -2
- package/knowledge/clients/nychh/features/location-shipping-addresses.md +3 -3
- package/knowledge/clients/nychh/features/netsuite-transfer-order-import.md +12 -2
- package/knowledge/clients/nychh/profile.md +2 -2
- package/package.json +1 -1
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
10
|
-
owners: [tcox, bala, jcardinal]
|
|
9
|
+
updated: 2026-09-24
|
|
10
|
+
owners: [tcox, bala, jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Core/Page.php
|
|
13
13
|
- _underscore/Model/Client/TableView.php
|
|
@@ -62,6 +62,18 @@ For a **joined** column, a `ClientRecordFieldSettings` row with a null `contextR
|
|
|
62
62
|
|
|
63
63
|
**Check which page slug the frontend ACTUALLY requests.** Every label and ACL lookup is filtered on the page's `Core.PageRecords`, so the records must be attached to **the page the frontend asks for** — not always the page you built. Elite's `service-requests` view is requested as **`meta?page=sales-orders-listing`** (Elite reuses that page), so records 99 (Tickets) and 36 (ServiceRequestTypes) had to be attached to `sales-orders-listing`, **not** only to the new `service-requests-listing` page. Attaching them to the new page alone changed nothing. **Read the Network tab first** — mis-diagnosed twice before the actual request URL settled it. A missing header is a *page-attachment* question, and only the real request identifies the page.
|
|
64
64
|
|
|
65
|
+
**The Core default label chain (verified against production Core, 2026-09-24).** For a base (non-custom, context-free) field the whole chain is Core-only:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
Pages --< PageRecords >-- Records --< RecordFields
|
|
69
|
+
|
|
|
70
|
+
DefaultRecordFieldSettings (settingId = 'label-singular')
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
`Core.Settings` id **5** = `label-singular`; `Core.Apps` id **1** = `supply`. Client/Persona/User/Language `RecordFieldSettings` only *override* this; the Core default already labels the field for **every** tenant. So seeding headers for a new page is **one `dbchanges2` `Core/` file** — no per-tenant fan-out and no cross-cluster split. (Worked example: `Core/2026-09-24a - BundlesListingPageMeta.sql`.)
|
|
74
|
+
|
|
75
|
+
**`Sections` are a different path — and listing pages have none.** `Sections` / `SectionRecordFields` / `ClientSectionRecordFieldSettings` do not feed table column labels. In prod and sandbox **every** `*-listing` page has **0** `Sections` rows; only `*-view` pages (`items-view`, `sales-orders-view`) have any. "This page has no Sections" is normal and is never the reason a header is missing.
|
|
76
|
+
|
|
65
77
|
**The `Core.PageRecords` gate.** `Page.php` assembles `settings.fields` from an **INNER JOIN** on `Core.RecordFields` (~L652) gated by **`Core.PageRecords`**: only fields of records *attached to the page* are emitted. A correct settings row does nothing until **both** the labelled field's record **and** the context record are attached to the page. Sequencing:
|
|
66
78
|
1. **The `isset()` guard in `Page.php` (below) goes first.** Until it ships, adding context rows is actively dangerous — a context row fatals **every** page that contains the labelled field's record but not the context record.
|
|
67
79
|
2. Then the `Core.PageRecords` attachments.
|
|
@@ -77,6 +89,7 @@ For a **joined** column, a `ClientRecordFieldSettings` row with a null `contextR
|
|
|
77
89
|
|
|
78
90
|
## Gotchas
|
|
79
91
|
|
|
92
|
+
- **Missing headers: check `PageRecords` + `DefaultRecordFieldSettings`, never `Sections`.** Listing pages have zero Sections by design.
|
|
80
93
|
- **A page-meta 500 is not necessarily about that page.** Trace it to the offending `ClientRecordFieldSettings` row via the array key in the message (it is a `Core.RecordFields` id), not via the slug you requested.
|
|
81
94
|
- **Error-handler strictness is a deploy-time behavior change.** Warnings survivable for years become 500s the moment the strict handler ships; expect a cluster of "new" failures in old code after such a deploy.
|
|
82
95
|
- Do not "fix" this by deleting the client's settings row — that removes a real client customization. The guard belongs in the framework.
|
|
@@ -87,3 +100,6 @@ For a **joined** column, a `ClientRecordFieldSettings` row with a null `contextR
|
|
|
87
100
|
- [TableView field/column metadata](../../api2/features/tableview-field-metadata.md)
|
|
88
101
|
- [Error reporting (Issue/Event)](error-reporting-issue-event.md)
|
|
89
102
|
- [Elite supply2 TableView config drift](../../../clients/elite/features/supply2-tableview-config-drift.md)
|
|
103
|
+
|
|
104
|
+
## Change history
|
|
105
|
+
- 2026-09-24 — Documented the Core-only label chain (`Pages`→`PageRecords`→`RecordFields`→`DefaultRecordFieldSettings` `label-singular`, setting id 5) and that `Sections` are a separate path listing pages never have. (apeterson)
|
|
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-24
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx
|
|
@@ -16,7 +16,8 @@ files:
|
|
|
16
16
|
- toga25-supply/src/pages/Bundles/viewModel/useBundlesPageViewModel.tsx
|
|
17
17
|
- toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts
|
|
18
18
|
- toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts
|
|
19
|
-
- toga-blox-npm/dist/hooks/useAssignTableFieldLabels.
|
|
19
|
+
- toga-blox-npm/dist/components/Table/hooks/useAssignTableFieldLabels.js
|
|
20
|
+
- _underscore/Model/Core/Page.php
|
|
20
21
|
- toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts
|
|
21
22
|
related:
|
|
22
23
|
- ../architecture.md
|
|
@@ -79,17 +80,39 @@ The view model then returns `salesOrdersTableMeta` (after column-visibility adju
|
|
|
79
80
|
- **Table slug** (`tableState.slug`) identifies the *table* and is used for **both** the table-meta fetch (step 3) and the table-data fetch (step 5) — they must match so the columns and the rows describe the same table. In Sales Orders this slug is derived from the route pathname via `useServerTableUrlState`, not read out of `pageMeta`.
|
|
80
81
|
- `useAssignTableFieldLabels` is the bridge: it overlays the page meta's per-field labels (`pageMeta.settings.fields[table][field].label`) onto the table meta's fields, so page-scoped copy wins without re-fetching.
|
|
81
82
|
|
|
82
|
-
### 🚨 There is NO label in the table meta —
|
|
83
|
+
### 🚨 There is NO label in the table meta — labels come from Core page meta
|
|
83
84
|
|
|
84
|
-
`_Model_Client_TableView::meta()` emits, per field: `slug`, `settings{table, field, contextTable, contextField}`, `tableViewField`, `type`, `precision` and the `is*` flags — **and no `label`.** Every column header in this app is therefore supplied by the *page* meta and grafted on in step 4.
|
|
85
|
+
`_Model_Client_TableView::meta()` emits, per field: `slug`, `settings{table, field, contextTable, contextField}`, `tableViewField`, `type`, `precision` and the `is*` flags — **and no `label`.** `Core.TableViewFields` has no label column at all. Every column header in this app is therefore supplied by the *page* meta and grafted on in step 4.
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
**The label chain (verified against production Core, 2026-09-24):**
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
Pages --< PageRecords >-- Records --< RecordFields
|
|
91
|
+
|
|
|
92
|
+
DefaultRecordFieldSettings (settingId = 'label-singular')
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- `_Model_Core_Page::meta()` (`_underscore/Model/Core/Page.php`, the "fields settings" block ~L640-745) scopes which `RecordFields` a page exposes through **`PageRecords`** — the join table, **not** `Sections`.
|
|
96
|
+
- The label value is `Core.DefaultRecordFieldSettings` where `settingId` = the `label-singular` setting (`Core.Settings` id **5**; `Core.Apps` id **1** = `supply`). Per-tenant overrides layer on top via `Client/Persona/User/LanguageRecordFieldSettings` in the client DB, but the **Core default carries the label for every tenant**.
|
|
97
|
+
- **So seeding labels is a Core-only change** — one `dbchanges2` `Core/` file, no per-tenant fan-out, no cross-cluster split.
|
|
98
|
+
- `Sections` / `SectionRecordFields` / `ClientSectionRecordFieldSettings` are a **separate** settings path with nothing to do with table column labels.
|
|
99
|
+
|
|
100
|
+
**Trap — "the page has no Sections" is a dead end.** In prod (and sandbox) **every** `*-listing` page has **0 rows in `Sections`**; only the `*-view` pages (`items-view`, `sales-orders-view`) have any (5 each). Missing Sections is normal for a listing page and is never why labels are missing. Time was lost chasing this on 2026-09-24.
|
|
101
|
+
|
|
102
|
+
**Two failure shapes in `useAssignTableFieldLabels` that look identical on screen** (`toga-blox-npm/dist/components/Table/hooks/useAssignTableFieldLabels.js`):
|
|
103
|
+
|
|
104
|
+
| Cause | Code path | Result |
|
|
105
|
+
|---|---|---|
|
|
106
|
+
| `pageMeta` is null (no page-meta record) | `if (!pageFields) return table` | fields carry **no `label` key at all** |
|
|
107
|
+
| `pageMeta` present but the page slug is wrong / field unmatched | `const label = labelSingular \|\| fieldSlug` | label is stamped as the **raw slug** |
|
|
108
|
+
|
|
109
|
+
Pointing a page at the **wrong** page-meta slug is therefore not harmless — the merge runs, matches nothing, and writes slugs in as labels. Both render as camelCase headers, so the symptom does not tell you which one you have; check the `pageMeta` response.
|
|
87
110
|
|
|
88
111
|
**Interim workaround (Transfer Orders, 2026-08-28):** a slug humanizer applied to the **merged** meta — `sourceLocation` → "Source Location" — deliberately after the merge so headers and the Columns modal cannot drift. It cannot recover a label the slug does not contain: the design's "TO Number" is unreachable from slug `number`.
|
|
89
112
|
|
|
90
|
-
**
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
**Preferred fix for a new list page (decided 2026-09-24): seed the Core page meta.** One `dbchanges2` `Core/` migration with a `<slug>-listing` `Pages` row + `PageRecords` link + `DefaultRecordFieldSettings` `label-singular` rows. This was previously mis-estimated as expensive (assumed per-tenant `SectionRecordFields` fan-out across ~30 client DBs plus a cross-cluster split) — **it is not; it is Core-only, one file.** Prefer it over the `humanizeSlug` fallback, which stays valid only where the slug already reads correctly. A third option, extending the **list surface** with per-column `FIELD` elements carrying a `labelMessageId`, additionally makes headers translatable through `MessageTranslations` — something the page-meta route cannot do. See [surface-frontend](./surface-frontend.md).
|
|
114
|
+
|
|
115
|
+
Worked example (Bundles, TRUE-82137, 2026-09-24) — `dbchanges2` `Core/2026-09-24a - BundlesListingPageMeta.sql`: `Pages(bundles-listing, appId 1)`, `PageRecords` → `Records` id 43 (bundles), and 5 `DefaultRecordFieldSettings` `label-singular` rows (`number` 265 → Number, `name` 248 → Name, `description` 1446 → Description, `isActive` 1356 → Active, `sortOrder` 1314 → Sort Order). The `Records` and `RecordFields` ids already existed in prod, so no team-reserved ids were needed.
|
|
93
116
|
|
|
94
117
|
### 🚨 Never hardcode a table meta in a view model — it fakes the flags too
|
|
95
118
|
|
|
@@ -104,9 +127,14 @@ Inventory page), and called `useTablePageMeta` but **threw the fetched result aw
|
|
|
104
127
|
**masked** the fact that the real served meta had them all off (the flags were NULL in
|
|
105
128
|
`TableViewFields`). The page looked configured while the database said otherwise.
|
|
106
129
|
|
|
107
|
-
Use the fetched server meta
|
|
108
|
-
|
|
109
|
-
|
|
130
|
+
Use the fetched server meta. A hardcoded meta is never a workaround for missing data; it hides the
|
|
131
|
+
data problem.
|
|
132
|
+
|
|
133
|
+
The label half was closed on **2026-09-24**, and it takes **two changes that must land together**:
|
|
134
|
+
`useBundlesPageViewModel.tsx:40` `"inventory"` → `"bundles-listing"` (commit `87b4442`), **and** the
|
|
135
|
+
Core seed `dbchanges2 Core/2026-09-24a - BundlesListingPageMeta.sql` (commit `89d88d7`). Shipping the
|
|
136
|
+
slug alone leaves the page with no page-meta record; shipping the seed alone leaves it fetching
|
|
137
|
+
`inventory`.
|
|
110
138
|
|
|
111
139
|
### What's in the table meta (the columns)
|
|
112
140
|
|
|
@@ -157,7 +185,10 @@ App-side glue (NOT in toga-blox):
|
|
|
157
185
|
- **`additionalData` must be in the query key** (it is, inside `useTableData`) — required for WHERE-filtered modal tables or React Query serves a stale unfiltered first render.
|
|
158
186
|
- **Never hardcode a table meta object in a view model.** It silently fakes `isSortable`/`isFilterable`/`isCopyable` and hides a real `TableViewFields` gap; always render the fetched meta.
|
|
159
187
|
- **toga-blox edits need a rebuild** — the app consumes `dist/`; editing only `src` is a silent no-op.
|
|
188
|
+
- **Listing pages have zero `Sections` rows by design** — never diagnose missing headers by looking at `Sections`.
|
|
189
|
+
- **A wrong page-meta slug is worse than none** — the merge stamps raw slugs in as labels; it looks the same on screen as having no page meta.
|
|
160
190
|
- **A new page with no page-meta record has no column labels at all** — the table meta carries no `label` and the merge hook no-ops on a null `pageMeta`, so headers *and* the Columns modal render raw camelCase slugs. Plan the label source before building the screen (see above).
|
|
161
191
|
|
|
162
192
|
## Change history
|
|
193
|
+
- 2026-09-24 — Corrected the column-label source: labels come from `Core.DefaultRecordFieldSettings` (`label-singular`) scoped by `Pages`→`PageRecords`→`RecordFields`, **not** from `Sections`; listing pages legitimately have zero Sections. Recorded the two `useAssignTableFieldLabels` failure shapes (null pageMeta vs. wrong slug → raw slug stamped as label), corrected the cost of seeding page meta to Core-only/one-file, and closed the Bundles label gap (slug fix + `Core/2026-09-24a`, TRUE-82137). (apeterson)
|
|
163
194
|
- 2026-09-23 — Added the hardcoded-table-meta anti-pattern found on the Bundles page: it produced raw slug headers and, worse, faked `isSortable`/`isFilterable`/`isCopyable` as true, masking NULL flags in `TableViewFields` (TRUE-82137). (apeterson)
|
|
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-24
|
|
10
10
|
owners: [apeterson, bala, "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts
|
|
@@ -191,7 +191,7 @@ One `SurfaceOverrides` row, `IS_VISIBLE = '1'`, **client-wide** (`personaId`/`ro
|
|
|
191
191
|
|
|
192
192
|
`src/pages/TransferOrders/`, wired into `ROUTE_REGISTRY` in `src/routes.tsx` as `transfer-orders`.
|
|
193
193
|
- **The TableView slug comes from the surface** (`listSurfaceMeta.surface.tableViewSlug`, falling back to a local constant), so re-pointing the page at a different view is a data change. The page never derives its identity from the route.
|
|
194
|
-
- **Column labels are humanized from the slug.** There is no `transfer-orders-listing` page-meta record, so blox's label merge contributes nothing and headers would render raw camelCase. The humanizer is applied to the **merged** meta so headers and the Columns-visibility modal cannot drift. It cannot produce the design's "TO Number" from slug `number` — see [meta-driven-table-data](./meta-driven-table-data.md)
|
|
194
|
+
- **Column labels are humanized from the slug.** There is no `transfer-orders-listing` page-meta record, so blox's label merge contributes nothing and headers would render raw camelCase. The humanizer is applied to the **merged** meta so headers and the Columns-visibility modal cannot drift. It cannot produce the design's "TO Number" from slug `number`. Seeding a `transfer-orders-listing` page-meta record is the fix, and it is cheap (Core-only, one `dbchanges2` file) — see [meta-driven-table-data](./meta-driven-table-data.md).
|
|
195
195
|
|
|
196
196
|
### The record modal — two fetches, one modal element
|
|
197
197
|
|
|
@@ -6,8 +6,8 @@ project: Database Changes
|
|
|
6
6
|
client: nychh
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
10
|
-
owners: [bala]
|
|
9
|
+
updated: 2026-09-24
|
|
10
|
+
owners: [bala, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Client_Nychh/2026-09-09c - UpdateLocationShippingAddresses.sql
|
|
13
13
|
- dbchanges2/Client_Nychh/2026-09-09a - DeleteTestContacts.sql
|
|
@@ -59,7 +59,7 @@ The Create Transfer Order screen picks a **Target Contact** from `Contacts` thro
|
|
|
59
59
|
|
|
60
60
|
- **🚩 OPEN — two contacts have no location link.** **Erald Velmishi** (Central Office) and **Harry Ho** (Community Care) got no `Contacts_Locations` row because **neither facility exists in `Locations`**. They will not appear in the Target Contact dropdown anywhere until the locations are created.
|
|
61
61
|
- **🚩 OPEN — nothing reads the chosen contact.** `TransferOrders.contactId` is a real FK on `_Model_Client_TransferOrder`, but **no code in worker2 or toga25-supply reads it**. NetSuite ship-to addresses do carry a person on the first line (order **289193** shows *Frederick Roberts*, the Bellevue delivery contact), so wiring the contact into the NetSuite `attention` field is the obvious next step — **undecided, not implemented**. See [transfer-order → NetSuite push](../../../2.0/apps/worker2/features/netsuite-transferorder-outbound-push.md).
|
|
62
|
-
-
|
|
62
|
+
- **Location 2 "NYC Health + Hospitals" = client-level catch-all; address now 55 Water Street, 10041** (was Jacobi's 1400 Pelham). Its 1,832-of-1,939 TO count is largely an import bug (overwrote user destinations) + NetSuite-entered orders under 28908; fix + ship-to matching in [the NetSuite transfer-order import](./netsuite-transfer-order-import.md).
|
|
63
63
|
- **⚠ Two locations carry NetSuite customer ids that no longer exist.** **Bellevue Hospital Center → 29275** and **Jacobi Medical Center → 31917**; neither id is in the NetSuite `customer` table, so their addresses came from other sources rather than a `defaultshipping` row. The ids need correcting; until then any NetSuite lookup keyed on them returns nothing.
|
|
64
64
|
- **⚠ A location's address surfaces ONLY on transfer orders for this client.** Sales orders carry their own `shipToAddressId`, so "the address is fine on the order screens" says nothing about the location record.
|
|
65
65
|
- **⚠ An `Addresses` row can be shared by several locations** (ids 1 and 2 shared `Addresses.id = 1` here). Always check before an in-place UPDATE; insert + repoint the junction instead.
|
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: nychh
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-24
|
|
10
10
|
owners: [jcardinal, bala, rgirish]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
@@ -119,6 +119,14 @@ Verified in production 2026-09-02 (while building the **outbound** push): on a N
|
|
|
119
119
|
|
|
120
120
|
**Deploy:** the library change (the skip) is deployed and verified over many cron cycles; the worker change (the missing 3rd arg + two stale-comment fixes) is the current fix — **redeploy worker** to pick it up.
|
|
121
121
|
|
|
122
|
+
#### FIX written 2026-09-24 (NOT deployed) — import overwrote the user's destination with catch-all location 2
|
|
123
|
+
**Root cause (proven):** the outbound push sends every NYCHH TO under client-level customer **28908**; the real site is only on the NetSuite **ship-to**. The stocking-SO branch of `syncTransferOrderFromNetsuite()` resolves destination from `entity` → `Locations.c_netsuiteInternalCustomerId` → 28908 → **location 2**, and the change-check PUTs it over the user's choice on any later NetSuite update (e.g. status). Proof: TO **1962** / SO **290951** (internal 7520216) — `Logs_Nychh.Api` **3391680** front-end POST dest 23 (Segundo Ruiz Belvis), **3407179** import PUT (API "Agilant") dest 2.
|
|
124
|
+
- **Keep existing destination** when `entity == $clientConfiguration['netsuiteCustomerInternalId']` AND `$clientConfiguration['isParentCustomer'] === true` AND the TO already has one. `isParentCustomer` gate: for a single-customer client the client-level customer IS the real destination.
|
|
125
|
+
- `common_sync_togasupply.php` sets `$clientConfig['netsuiteCustomerInternalId']` from the fetched client record. Missing key → old behavior.
|
|
126
|
+
- **New NetSuite-entered TOs under the client customer** (same gates, no existing TO): `resolveTransferOrderDestinationFromShipTo()` (+ `normalizeShipToText`, `getShipToLocations` = `GET /locations` depth 3, static per-run cache per client uuid; failure/empty logged, not cached). Match = 5-digit zip equal AND location line1 == or word-prefix of NS addr1 (addr1 can carry extra text, e.g. Queens "8268 164th St Goethels Ave and Parsons Blvd"); street words abbreviated (Parkway→PKWY, South→S…). Shared buildings (Harlem/Renaissance 506 Lenox; Segundo Ruiz Belvis/Gotham Health Belvis 545 E 142nd) → tie broken by location name inside NS addressee. Client-level location never picked. No/ambiguous match → keep location 2 + `error_log` grep `resolveTransferOrderDestinationFromShipTo:` (no throw — would freeze the section). CTO AGREE; runner-up = NetSuite addressbook-id mapping (needs new field) — revisit if logs show many misses.
|
|
127
|
+
- **Unproven live:** depth=3 `/locations` returning `primaryLocationAddress.address` for the Agilant API role. Check the log grep after deploy.
|
|
128
|
+
- **Data repair SQL handed to dev (not run):** TO 1962→23, 1959→24, 1960→24, 1961→9, 1966→20 (NS 291004, ship-to Queens), guarded `AND destinationLocationId = 2`.
|
|
129
|
+
|
|
122
130
|
### Item-fulfillment transfer-order line link
|
|
123
131
|
`ItemFulfillmentItems.transferOrderItemId` must populate so the 2.0 stock-out quantity calc can tie a fulfillment back to its transfer-order line. In `syncItemFulfillmentFromNetsuite`:
|
|
124
132
|
- added `itemFulfillmentItems.transferOrderItem.uuid` to the GET `fields` allowlist,
|
|
@@ -209,7 +217,7 @@ Found by a fleet sweep of `NETSUITE_EXECUTION_MODE_*` across all 22 netsuite cli
|
|
|
209
217
|
|
|
210
218
|
## Gotchas / known issues
|
|
211
219
|
|
|
212
|
-
-
|
|
220
|
+
- **Location 2 count (1,832 of 1,939) is not a real ship pattern** — at least partly the 2026-09-24 overwrite bug + NetSuite-entered orders under 28908. Location 2 address is now 55 Water St 10041. See the destination fix above.
|
|
213
221
|
- **Real-TrnfrOrd item receipts are disabled — see the DEFERRED section above.** A receipt whose `createdFrom` is a real Transfer Order is skipped by design; do not read that as the importer being broken.
|
|
214
222
|
- **Order 128829 (no location anywhere) is unresolved** — see the origin-resolution section. Do not assume the importer is broken if this single order is stuck; every located order imports.
|
|
215
223
|
- **Routing mode is declared by the wrapper and there is NO fallback.** If a future client should route transfer orders by the stocking flag, add `const TRANSFER_ORDER_DETECTION_MODE = App_Api_Toga2::TRANSFER_ORDER_DETECTION_MODE__STOCKING_FLAG;` before its `require_once 'common_sync_togasupply.php';` — and confirm that client actually sets `custbody_stocking_order` in NetSuite. Omitting the mode no longer lands everything in SalesOrders; it **throws** (deliberate — see the shared engine doc).
|
|
@@ -219,6 +227,8 @@ Found by a fleet sweep of `NETSUITE_EXECUTION_MODE_*` across all 22 netsuite cli
|
|
|
219
227
|
## Change history
|
|
220
228
|
- 2026-09-21 — Fixed issue 587 (`ITEM_FULFILLMENTS` freeze): transfer-order fulfillment line was matched by `lineNumber` (= NetSuite stable `tl.id`) vs the shim's `orderLine` (= source `linesequencenumber`), which diverge on edited orders. Added `c_netsuiteLineUniqueKey` (new trait + Nychh model, `_modules/netsuite/` column/registration); match by it first, fall back to `lineNumber`. Needs a SALES_ORDERS cursor rewind on deploy. (jcardinal)
|
|
221
229
|
|
|
230
|
+
- 2026-09-24 — Keep user destination on client-customer TOs (parent clients); ship-to → Location match for new NS-entered TOs. Not deployed. (jcardinal)
|
|
231
|
+
|
|
222
232
|
## Related
|
|
223
233
|
- [NetSuite → TOGa Supply per-client sync (engine)](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md)
|
|
224
234
|
- [toga2 API client and bridge](../../../1.0/apps/library/features/toga2-api-client-and-bridge.md)
|
|
@@ -18,7 +18,7 @@ project: _Underscore
|
|
|
18
18
|
client: nychh
|
|
19
19
|
type: profile
|
|
20
20
|
status: active
|
|
21
|
-
updated: 2026-09-
|
|
21
|
+
updated: 2026-09-24
|
|
22
22
|
owners: ["jcardinal", "apeterson", "bala", "akhokhani"]
|
|
23
23
|
files:
|
|
24
24
|
- dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql
|
|
@@ -66,7 +66,7 @@ TOGA 2.0 client on `_underscore`, prod schema `Client_Nychh`. Supply-chain / ful
|
|
|
66
66
|
- **Worker tiers:** `worker2` (2.0) runs the NYCHH asset-tag backfill; `worker`/`library` (1.0) run the NetSuite→TOGa Supply sync and the asset-tag verification/backfill diagnostics.
|
|
67
67
|
|
|
68
68
|
## Key features (this client)
|
|
69
|
-
- **All 24 shipping locations shared ONE address until 2026-09-09 — the head-office Accounts Payable bill-to (50 Water St).** Each location had its own `Addresses` row (261-303) but they held identical values, and locations 1 + 2 shared `Addresses.id = 1`. `Client_Nychh/2026-09-09c - UpdateLocationShippingAddresses.sql` backfilled each from its NetSuite customer's `defaultshipping` address (applied + verified in prod: 22 distinct addresses over 24 locations). It only ever showed on the **transfer-order** screens, because `TransferOrders` has no ship-to column while `SalesOrders` carries its own `shipToAddressId`.
|
|
69
|
+
- **All 24 shipping locations shared ONE address until 2026-09-09 — the head-office Accounts Payable bill-to (50 Water St).** Each location had its own `Addresses` row (261-303) but they held identical values, and locations 1 + 2 shared `Addresses.id = 1`. `Client_Nychh/2026-09-09c - UpdateLocationShippingAddresses.sql` backfilled each from its NetSuite customer's `defaultshipping` address (applied + verified in prod: 22 distinct addresses over 24 locations). It only ever showed on the **transfer-order** screens, because `TransferOrders` has no ship-to column while `SalesOrders` carries its own `shipToAddressId`. Catch-all **location 2 "NYC Health + Hospitals"** now at 55 Water St 10041; its big TO count was mostly an import overwrite bug (fix 2026-09-24, see [NetSuite TO import](./features/netsuite-transfer-order-import.md)), and Bellevue/Jacobi carry NetSuite customer ids (29275 / 31917) that no longer exist. See [location shipping addresses](./features/location-shipping-addresses.md).
|
|
70
70
|
- 🚨 **sandbox-client (`Core` + `Client_Nychh`) was reset from PRODUCTION again on 2026-09-04** — second reset in two days (first, 2026-09-03, over the stale `purchase-orders` TableView). Every "verified on <date>" sandbox-client claim in this profile dated **before 2026-09-04 now describes whatever prod had**. The 2026-09-01 → 2026-09-03 `dbchanges2` migrations may need re-running on sandbox-client. Re-read the environment before trusting any sandbox state; procedure + re-run order in [Targeting the right database](../../2.0/apps/dbchanges2/workflows/targeting-the-right-database.md).
|
|
71
71
|
- **Transfer orders push OUT to NetSuite as $0 sales orders - LIVE and verified 2026-09-08.** NYCHH is the first and only client on that path; switched on purely by its `transfer-orders` (record 312) `POST`/`POST` interceptor row. NetSuite SO **289187** (id 7463537) was created from it. **The NetSuite customer is the CLIENT - 4490 / 28908 - on every transfer order, never the destination location** (a first cut sent the destination and put SO 7462031 on sub-customer 33674 "CHS"; `Locations` 1-26 are "Shipping" type mapping to sub-customers under 4490, and the bug hid because almost every order picks location 2, whose id *is* 28908). Origin location is still the NetSuite **warehouse**, and `TransferOrders.description` now goes out as the NetSuite `memo`, shown as **ORDER DESCRIPTION**. See [TransferOrder → NetSuite push](./features/transfer-order-netsuite-push.md).
|
|
72
72
|
- **Transfer orders closed in NetSuite never reached the Closed stage - 18 stuck on `Pending`, oldest 2022 (fix written 2026-09-08, NOT deployed).** The 1.0 sync skips a `Closed` order before the transfer-order branch, so the importer's `case 'Closed'` was dead code. Two NYCHH gates make this confusing to verify: the transfer-order status refresh is nested inside `IS_ENABLED_INTEGRATION_SALES_ORDERS` (Jeff enabled the feeds 2026-08-28 `b063f05f` and disabled sales orders, POs, invoices and item receipts on 2026-09-03 `2b321e9d` - where every NYCHH cursor stops), and the seek cursor never re-reads a passed order. 🚨 **Their `NETSUITE_LAST_SYNC_CURSOR_ITEM_RECEIPTS` is stale at 2024-08-06**, so re-enabling that feed replays ~2 years. See [NetSuite → TransferOrders import](./features/netsuite-transfer-order-import.md).
|
package/package.json
CHANGED