toga-ai 1.0.241 → 1.0.242
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/INDEX.md +1 -1
- package/knowledge/2.0/apps/_underscore/features/surface-resolver.md +39 -1
- package/knowledge/2.0/apps/api2/features/surface-meta-option.md +8 -1
- package/knowledge/2.0/apps/dbchanges2/INDEX.md +1 -1
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +43 -1
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga25-supply/features/surface-frontend.md +47 -15
- package/knowledge/clients/compass-canada/profile.md +1 -0
- package/knowledge/clients/compass-usa/profile.md +1 -0
- package/knowledge/clients/quad/profile.md +2 -1
- package/package.json +1 -1
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
| [NetSuite REST Client (_Component_Api_Netsuite) — record writes & SuiteQL](features/netsuite-rest-client.md) | `_Component_Api_Netsuite` is the **2.0 `_underscore` NetSuite REST client** — the shared primitive every worker2/api2 NetSuite caller uses for record GETs, Suit | _underscore/Component/Api/Netsuite/Netsuite.php |
|
|
14
14
|
| [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php |
|
|
15
15
|
| [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql |
|
|
16
|
-
| [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/Core/Surface.php, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.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 |
|
|
16
|
+
| [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/Core/Surface.php, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.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 |
|
|
17
17
|
| [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_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql |
|
|
18
18
|
| [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 | |
|
|
@@ -6,10 +6,12 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-30
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Core/Surface.php
|
|
13
|
+
- dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql
|
|
14
|
+
- dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql
|
|
13
15
|
- dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql
|
|
14
16
|
- dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql
|
|
15
17
|
- dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql
|
|
@@ -69,6 +71,29 @@ Inputs come from `$api` (the same accessor `_Model_Client_TableView::meta` uses)
|
|
|
69
71
|
A `metaDebug()` inspector returns the resolved bundle plus the raw cascade layers (shipped with the
|
|
70
72
|
resolver, not "later"). It is **not** named `debug()` — see gotchas.
|
|
71
73
|
|
|
74
|
+
## Grouped resolution — `metaGroup()`
|
|
75
|
+
|
|
76
|
+
`_Model_Core_Surface::metaGroup(&$api, string $slugs): object` — a scripted-API method exposed at
|
|
77
|
+
`GET /v2/surfaces/meta-group?slugs=a,b,c` for screens (e.g. a record-detail modal) that need many
|
|
78
|
+
section surfaces at once: **one round trip instead of N**. It splits the comma-separated `slugs`
|
|
79
|
+
list (capped at `const META_GROUP_MAX_SLUGS = 25`, logging truncation), resolves **each slug via the
|
|
80
|
+
existing per-slug `resolve()` cache path**, and returns `{group, surfaces: {slug => bundle|null}}`.
|
|
81
|
+
It is **composition only** — no new cache layer; an unknown slug yields the structured **empty
|
|
82
|
+
bundle** so one missing surface never fails the whole group. (Chosen over a `parentSurfaceId` GROUP
|
|
83
|
+
row after CTO review — a slug list keeps the model schema-stateless and avoids id drift.)
|
|
84
|
+
|
|
85
|
+
`resolve()` now declares an explicit `: object` return type.
|
|
86
|
+
|
|
87
|
+
### Message/label resolution fixes (latent SECTION/BUTTON_BAR bugs)
|
|
88
|
+
The grouped SalesOrders section migration is the first real consumer of the SECTION/BUTTON_BAR path,
|
|
89
|
+
which surfaced latent resolution gaps in `_buildBundle`/`_resolveElement`:
|
|
90
|
+
- Element message slots (`label`/`tooltip`/`placeholder`/`disabledTooltip`) are now remapped from
|
|
91
|
+
**message-id → messageKey** before return, because `bundle.messages` is keyed by `messageKey` —
|
|
92
|
+
previously labels silently fell back to the element key.
|
|
93
|
+
- `disabledTooltipMessageId` is collected into the batch and emitted as a `disabledTooltip` slot.
|
|
94
|
+
- `bundle.surface.titleMessageKey` is set from `titleMessageId`.
|
|
95
|
+
- The 2nd `_loadMessages` return is merged into `$messageKeyById`; dangling message ids are logged.
|
|
96
|
+
|
|
72
97
|
## Caching & invalidation
|
|
73
98
|
|
|
74
99
|
- Disk cache keyed `surface:meta:{app}:{slug}:{client}:{sortedPersonaIds}:{sortedRoleIds}:{lang}`.
|
|
@@ -137,7 +162,20 @@ resolver, not "later"). It is **not** named `debug()` — see gotchas.
|
|
|
137
162
|
`record-fields` already expose Public READ. Only the `surfaces` record gets Public read — the
|
|
138
163
|
admin-CRUD sibling records stay Super-User-only (the resolver reads their tables server-side).
|
|
139
164
|
|
|
165
|
+
- **The meta-group dispatch grant is per-client-DB too.** A 403 EZ-1 on `meta-group` on dev-sandbox
|
|
166
|
+
was the same mechanism as the original `meta` 403: the `meta-group` RecordScript needs its own
|
|
167
|
+
per-role `Client.AclRecordScripts` dispatch grant. Verified via toga-db — `meta` script id 25 was
|
|
168
|
+
granted roles 1/3/4 but `meta-group` (id 27) had **none** until the grant ran. Any new surfaces
|
|
169
|
+
scripted-API method repeats this: record READ never authorizes dispatch.
|
|
170
|
+
|
|
140
171
|
## Change history
|
|
172
|
+
- 2026-06-30 — Added `metaGroup()` (`GET /v2/surfaces/meta-group?slugs=a,b,c`): batched composition
|
|
173
|
+
over the existing per-slug `resolve()` cache, capped at `META_GROUP_MAX_SLUGS=25`, unknown slug →
|
|
174
|
+
empty bundle (chosen over a `parentSurfaceId` GROUP row post-CTO-review). Fixed latent
|
|
175
|
+
SECTION/BUTTON_BAR resolution (message-id→messageKey remap for label/tooltip/placeholder/
|
|
176
|
+
disabledTooltip, titleMessageKey, dangling-id logging); added `: object` to `resolve()`. The
|
|
177
|
+
`meta-group` script needed its own per-client `AclRecordScripts` dispatch grant (403 EZ-1 until
|
|
178
|
+
granted). (jcardinal)
|
|
141
179
|
- 2026-06-29 — Deploy fixes (round 2): grant the surfaces `meta`/`debug` RecordScripts in
|
|
142
180
|
`Client.AclRecordScripts` per role (Public/SuperUser/Base) — record-level `AclRecordPermissions`
|
|
143
181
|
alone left every meta call 403 EZ-1 because V2 dispatch authorizes via `AclRecordScripts`
|
|
@@ -6,7 +6,7 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-30
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
@@ -56,7 +56,14 @@ traffic), behavior is **byte-for-byte unchanged** — pure data, zero extra work
|
|
|
56
56
|
meta 403'd until the `meta`/`debug` scripts were granted per role in `AclRecordScripts`. See
|
|
57
57
|
[surface-resolver](../../_underscore/features/surface-resolver.md) gotchas.
|
|
58
58
|
|
|
59
|
+
- **Grouped meta endpoint `GET /v2/surfaces/meta-group?slugs=a,b,c`** (one round trip for a
|
|
60
|
+
multi-section screen) is a distinct RecordScript and needs its **own** per-role
|
|
61
|
+
`Client.AclRecordScripts` dispatch grant — it 403'd EZ-1 on dev-sandbox until granted, exactly like
|
|
62
|
+
`meta` did. See [surface-resolver](../../_underscore/features/surface-resolver.md).
|
|
63
|
+
|
|
59
64
|
## Change history
|
|
65
|
+
- 2026-06-30 — Noted the grouped `meta-group` endpoint (batched multi-slug resolve) and that it needs
|
|
66
|
+
its own per-role `AclRecordScripts` dispatch grant (403 EZ-1 until granted). (jcardinal)
|
|
60
67
|
- 2026-06-29 — Documented the second ACL layer: scripted-API dispatch is authorized via
|
|
61
68
|
`Client.AclRecordScripts` (per `recordScriptId`+`roleId`), not record `AclRecordPermissions` —
|
|
62
69
|
record READ alone still 403'd meta until the dispatch grants were seeded. (jcardinal)
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Database Changes (dbchanges2) Repository Architecture](architecture.md) | `dbchanges2` is the **schema-migration / SQL change-set repository** for the entire 2.0 platform. | Core/, Client/, Client_<Tenant>/, Logs/, Logs_Client/, _modules/ |
|
|
6
|
-
| [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql |
|
|
6
|
+
| [Surface Layer Schema (UI presentation/config tables)](features/surface-layer-schema.md) | The persistent schema for the platform-wide **Surface** UI presentation/configuration layer (see the `_underscore` [surface-resolver](../../_underscore/features | dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql, dbchanges2/Core/2026-06-25b - SurfaceRecordsAndFields.sql, dbchanges2/Core/2026-06-25c - SalesOrderLoginSurfaceSeed.sql, dbchanges2/Core/2026-06-29a - ItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29b - SurfaceMetaPublicReadAcl.sql, dbchanges2/Client/2026-06-29c - SurfaceRecordScriptAcl.sql, dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql, dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql, dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql, dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql, dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql, dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql, dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql, dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.sql, dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql, dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql, dbchanges2/Client/2026-06-03- BLANK_CLIENT_DATABASE.sql |
|
|
7
7
|
| [2.0 New-Client Onboarding (manual process)](workflows/client-onboarding.md) | How to manually stand up a new 2.0 client (tenant). | Client/, Client_<Tenant>/, Core/, Logs_Client/ |
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-30
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Core/2026-06-25a - SurfaceCoreTables.sql
|
|
@@ -18,6 +18,11 @@ files:
|
|
|
18
18
|
- dbchanges2/Core/2026-06-29c - SurfaceDebugPhpMethodFix.sql
|
|
19
19
|
- dbchanges2/Core/2026-06-29d - VendorItemsSurfaceSeed.sql
|
|
20
20
|
- dbchanges2/Core/2026-06-29e - InventorySurfaceSeed.sql
|
|
21
|
+
- dbchanges2/Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql
|
|
22
|
+
- dbchanges2/Client/2026-06-30a - SurfaceMetaGroupAcl.sql
|
|
23
|
+
- dbchanges2/Client_Compass/2026-06-30a - SalesOrderDisplaySectionManagerOverrides.sql
|
|
24
|
+
- dbchanges2/Client_CompassCanada/2026-06-30a - SalesOrderSurfaceManagerOverrides.sql
|
|
25
|
+
- dbchanges2/Client_Quad/2026-06-30a - SalesOrderSurfaceClientOverrides.sql
|
|
21
26
|
- dbchanges2/Client/2026-06-25a - SurfaceClientTables.sql
|
|
22
27
|
- dbchanges2/Client/2026-06-25b - SurfaceClientSeed.sql
|
|
23
28
|
- dbchanges2/Client/2026-06-25c - SurfaceClientAcl.sql
|
|
@@ -82,6 +87,19 @@ SurfaceElements`). Core migrations run first; Client after. The session built/se
|
|
|
82
87
|
purchase-orders/items/units via per-level fetchSlugs and has no single backing Record; carries
|
|
83
88
|
`groupByLabels` + `entityTags` in surface `config`) + `inventory-list-actions` (BUTTON_BAR:
|
|
84
89
|
Group by / Refresh). Actions anchored to `items(21)` because `Actions.recordId` is NOT NULL.
|
|
90
|
+
- `Core/2026-06-30a - SurfaceMetaGroupAndSalesOrderSections.sql` — registers the `meta-group`
|
|
91
|
+
RecordScript; seeds the previously-missing `order-recurring` SECTION surface (cardType
|
|
92
|
+
`recurringFields`, `isVisible 0`); seeds 5 display-toggle SECTION surfaces (invoices / shipments /
|
|
93
|
+
additional-order-details / notes / admin-notes), **each carrying a single `sectionVisibility`
|
|
94
|
+
marker element** (see gotcha — the override cascade only overrides ELEMENTS, never a surface's own
|
|
95
|
+
`isVisible`, so display-section visibility cannot be per-role-overridden without a marker element).
|
|
96
|
+
Core→Core only, so this file is prod-safe.
|
|
97
|
+
- `Client/2026-06-30a - SurfaceMetaGroupAcl.sql` — the `meta-group` `AclRecordScripts` dispatch grant
|
|
98
|
+
(roles 1/3/4), mirroring the `meta` grant.
|
|
99
|
+
- `Client_Compass/2026-06-30a` / `Client_CompassCanada/2026-06-30a` — per-client **MANAGER**-scope
|
|
100
|
+
SalesOrder section overrides; `Client_Quad/2026-06-30a` — Quad's **client-level** section deltas.
|
|
101
|
+
⚠ These three Client-DB seeds currently resolve **Core** ids via cross-DB subselect — see the
|
|
102
|
+
prod cross-cluster gotcha; they are paused before prod.
|
|
85
103
|
- `Client/2026-06-29c - SurfaceRecordScriptAcl.sql` — grants the surfaces `meta`/`debug` RecordScripts
|
|
86
104
|
in **`Client.AclRecordScripts`** per role (meta → Public(1)/SuperUser(3)/Base(4); debug →
|
|
87
105
|
SuperUser(3)). V2 authorizes scripted-API dispatch via `AclRecordScripts`, NOT record
|
|
@@ -122,7 +140,31 @@ SurfaceElements`). Core migrations run first; Client after. The session built/se
|
|
|
122
140
|
- **CLIENT-aclDatabase records** (`SurfaceOverrides`, `MessageTranslations`, `ThemeTokens`) need the
|
|
123
141
|
full 4-step ACL chain + field permissions in **each** client DB; resolve `roleId` by subselect.
|
|
124
142
|
|
|
143
|
+
- **⚠ PROD BLOCKER (ON HOLD) — Client-DB surface seeds must NOT resolve Core ids via cross-DB
|
|
144
|
+
subselect.** The `Client_Compass`/`Client_CompassCanada`/`Client_Quad` SalesOrder override seeds
|
|
145
|
+
resolve Core ids (`Core.RecordScripts`/`Core.Surfaces`/`Core.SurfaceElements`) with a cross-DB
|
|
146
|
+
subselect. This works only on the dev-sandbox **all-in-one** instance; in **production Core is a
|
|
147
|
+
separate cluster** and cross-DB queries require a single instance, so these seeds **break in prod**.
|
|
148
|
+
The established prod-safe pattern is `Client/2026-06-23b - ItemTranslationsAcl.sql`: **hardcode Core
|
|
149
|
+
ids as literals**, resolve only Client-local ids (e.g. `roleId` by name). The fix is paused; the
|
|
150
|
+
earlier `2026-06-25c` / `2026-06-29c` / `2026-06-25d` surface seeds **likely share the same
|
|
151
|
+
anti-pattern** and need auditing. Core→Core seeds are fine (same cluster). Also still open:
|
|
152
|
+
re-seeding NYCHH/Prudential/SPGlobal (deferred).
|
|
153
|
+
- **Display-section visibility cannot be per-role-overridden without an element.** The override
|
|
154
|
+
cascade overrides **ELEMENTS only**, never a surface's own `isVisible`. The 5 display-toggle SECTION
|
|
155
|
+
surfaces therefore each carry a single `sectionVisibility` marker element so a Client override can
|
|
156
|
+
flip a section per role/persona. Also discovered: `elementType` in `orderViewFields.json` is **dead
|
|
157
|
+
config** (no renderer reads it); and MANAGER/Quad summary tooltips can't be removed via the cascade
|
|
158
|
+
because `TOOLTIP_MESSAGE` is numeric-only (cosmetic, deferred).
|
|
159
|
+
|
|
125
160
|
## Change history
|
|
161
|
+
- 2026-06-30 — Added the SalesOrders SECTION-surface migration seeds: Core `meta-group` RecordScript +
|
|
162
|
+
`order-recurring` + 5 display-toggle SECTION surfaces (each with a `sectionVisibility` marker
|
|
163
|
+
element, since the cascade overrides elements not a surface's own isVisible), the `meta-group`
|
|
164
|
+
dispatch ACL grant, and per-client (Compass/CompassCanada MANAGER, Quad client-level) section
|
|
165
|
+
overrides. Recorded the ⚠ prod blocker: Client-DB seeds resolving Core ids via cross-DB subselect
|
|
166
|
+
break in prod (Core is a separate cluster) — must hardcode Core id literals per the
|
|
167
|
+
`ItemTranslationsAcl.sql` pattern; fix paused, earlier surface seeds likely share it. (jcardinal)
|
|
126
168
|
- 2026-06-29 — Added VendorItems + Inventory LIST seeds (`VendorItemsSurfaceSeed.sql` recordId 19 /
|
|
127
169
|
TableView 10; `InventorySurfaceSeed.sql` presentation-only, recordId+tableViewId NULL, Actions
|
|
128
170
|
anchored to items(21)). Added `SurfaceRecordScriptAcl.sql` (per-role `AclRecordScripts` dispatch
|
|
@@ -8,5 +8,5 @@
|
|
|
8
8
|
| [Column Visibility (URL-driven show/hide columns)](features/column-visibility.md) | A "Columns" header button that opens a modal listing every column from the table meta, lets the user show/hide columns, adjusts the table live, and persists the | toga25-supply/src/components/ColumnVisibilityModal/, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx |
|
|
9
9
|
| [Meta-Driven Page & Table Setup](features/meta-driven-table-data.md) | A page in this app is **meta-driven end to end**: the page view model fetches *page meta* (labels, sections, ACL) and *table meta* (the columns/fields + table s | toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableState.ts, toga25-supply/src/hooks/useTablePageMeta.ts, toga-blox-npm/dist/hooks/useFetchPageMeta.d.ts, toga-blox-npm/dist/hooks/useFetchTablePageMeta.d.ts, toga-blox-npm/dist/hooks/useAssignTableFieldLabels.d.ts, toga-blox-npm/dist/components/Table/hooks/useTableData.d.ts |
|
|
10
10
|
| [Record Modals & Nested Tables](features/record-modals-and-nested-tables.md) | The repo's family of modal + nested-table patterns layered over toga-blox `TableRecordModal` and `PrimaryTable*Layout`. | toga25-supply/src/layout/ItemRecordModalLayout/, toga25-supply/src/layout/SalesOrderRecordModalLayout/, toga25-supply/src/layout/SalesOrderItemsTableLayout/, toga25-supply/src/layout/ItemFulfillmentModal/, toga25-supply/src/layout/GenericNestedTables/, toga25-supply/src/hooks/useTableCellInteractions.ts |
|
|
11
|
-
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts |
|
|
11
|
+
| [Surface Frontend (DB-driven UI consumption, src/surface/)](features/surface-frontend.md) | The frontend consumer of the platform-wide Surface layer — DB-driven UI config fetched from `GET /v2/surfaces/meta?slug=<slug>` instead of statically-imported J | toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/SurfaceActionBar.tsx, toga25-supply/src/surface/SurfaceSection.tsx, toga25-supply/src/surface/resolve.ts, toga25-supply/src/surface/types.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/Login/LoginPage.tsx, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/Items/ItemsPage.tsx, toga25-supply/src/pages/Items/viewModel/useItemsPageViewModel.tsx, toga25-supply/src/pages/VendorItems/VendorItemsPage.tsx, toga25-supply/src/pages/VendorItems/viewModel/useVendorItemsPageViewModel.tsx, toga25-supply/src/pages/Inventory/Inventory.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/index.ts, toga25-supply/src/fieldsConfig/index.ts |
|
|
12
12
|
| [Cypress Testing Harness (component + e2e)](workflows/cypress-testing.md) | The Cypress test harness for the `toga25-supply` frontend, bootstrapped from scratch (`cypress` was already a dependency but there was no config, no `cypress/` | toga25-supply/cypress.config.ts, toga25-supply/cypress/tsconfig.json, toga25-supply/cypress/support/component.tsx, toga25-supply/cypress/support/component-index.html, toga25-supply/cypress/support/e2e.ts, toga25-supply/cypress/support/commands.ts, toga25-supply/cypress/support/fixtures.ts, toga25-supply/cypress/support/mocks/useApprovalModalViewModel.ts, toga25-supply/cypress/component/SalesOrderApprovalModalsLayout.cy.tsx, toga25-supply/cypress/component/RecordApprovalModalLayout.cy.tsx, toga25-supply/cypress/component/EnterPoNumberModal.cy.tsx, toga25-supply/cypress/e2e/salesOrderApproval.cy.ts |
|
|
@@ -6,10 +6,13 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-30
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/surface/useFetchSurfaceMeta.ts
|
|
13
|
+
- toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts
|
|
14
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx
|
|
15
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts
|
|
13
16
|
- toga25-supply/src/surface/evaluateSurfaceRule.ts
|
|
14
17
|
- toga25-supply/src/surface/actionRegistry.ts
|
|
15
18
|
- toga25-supply/src/surface/SurfaceActionBar.tsx
|
|
@@ -51,6 +54,12 @@ feature flag — see below). Backend: [surface-resolver](../../_underscore/featu
|
|
|
51
54
|
`raw` / `raw.data` / `raw.data.<route-key>` — because V2 nests a scripted-API return value one
|
|
52
55
|
level deeper, under a route-keyed slot of `data` (e.g. `data.meta`, `data.surfaces`). On a malformed
|
|
53
56
|
payload it falls back to an empty-elements bundle so the page never crashes.
|
|
57
|
+
- **`useFetchSurfaceMetaGroup(slugs[])`** — the grouped sibling of `useFetchSurfaceMeta`, for a screen
|
|
58
|
+
needing many section surfaces at once. **Order-insensitive** (the slug list is normalized so cache
|
|
59
|
+
hits don't depend on argument order), `staleTime: Infinity`, hits the grouped endpoint
|
|
60
|
+
`GET /v2/surfaces/meta-group?slugs=...`, and extracts the `{surfaces}` map via `extractGroup()` /
|
|
61
|
+
`looksLikeGroup()` (the same structural-fingerprint approach as `extractBundle`). Returns `{}` for
|
|
62
|
+
unmigrated screens (`enabled:false`), so a non-roster client does no fetch.
|
|
54
63
|
- **`evaluateSurfaceRule`** — the Tier-1 rule evaluator: a **frozen `all/any/none` + `{field,op,value}`
|
|
55
64
|
grammar** that **throws on an unknown op** (generalized from the SalesOrders
|
|
56
65
|
`buildPatchedTenantFields`/`evaluateEnableRule`/`resolveFlag` helpers). Evaluated client-side against
|
|
@@ -114,20 +123,35 @@ modal copy, visibility/enable) to Surface; **leave data-fetch wiring (fetch slug
|
|
|
114
123
|
nested-table topology) in JSON/code.** Not every screen reduces to a generic `SurfaceSection` swap —
|
|
115
124
|
see SalesOrders below.
|
|
116
125
|
|
|
117
|
-
## SalesOrders
|
|
118
|
-
|
|
119
|
-
The SalesOrders detail
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
code
|
|
130
|
-
|
|
126
|
+
## SalesOrders record-modal sections — migrated via the registered-renderer seam (roster-gated)
|
|
127
|
+
|
|
128
|
+
The SalesOrders detail sections are now migrated onto Surface for the **seeded roster clients only**,
|
|
129
|
+
via the registered-renderer approach (not a generic `SurfaceSection` swap — `SalesOrderSummaryGrid`'s
|
|
130
|
+
three bespoke renderers `detailSection`/`locationCard`/`totalsCard` are untouched).
|
|
131
|
+
|
|
132
|
+
- **`surfaceBundleToTenantFields()`** (`SalesOrders/helpers/`) adapts the grouped Surface bundle into
|
|
133
|
+
the existing **`TenantFields`** shape, so the existing `SalesOrderSummaryGrid` renderers and
|
|
134
|
+
`getDetailSections` are untouched — **only the SOURCE of section config moves JSON → Surface.**
|
|
135
|
+
Business logic stays in code: the recurring-lease `_totalLease` gate in the grid and approval
|
|
136
|
+
gating are **not** moved. (Registered-renderer seam: *config describes, code decides.*)
|
|
137
|
+
- **Roster gate — `SALES_ORDER_SURFACE_MIGRATED_CLIENTS = [COMPASS, COMPASSCANADA, QUAD]`** (an
|
|
138
|
+
in-code roster in `surfaceBundleToTenantFields.ts`; the view-model
|
|
139
|
+
`useSalesOrderRecordModalLayoutModel.tsx` consults it). The view-model resolves sections from
|
|
140
|
+
Surface **only** for roster clients; every other client (incl. never-migrated
|
|
141
|
+
NYCHH/Prudential/SPGlobal) skips the fetch and stays fully JSON-driven. Merge is **JSON-base +
|
|
142
|
+
Surface-wins.** ⚠ **Why a roster, not a flag:** the Core SECTION surfaces are **shared** and seeded
|
|
143
|
+
from Compass's shape — without the gate every client would inherit Compass's base and regress. A
|
|
144
|
+
**code** roster (not an env flag) is deliberate: a client is removed from the roster only once its
|
|
145
|
+
own seeds are verified.
|
|
146
|
+
- **JSON cleanup:** the migrated section blocks (orderDetails/shipTo/billTo/orderSummary/
|
|
147
|
+
orderRecurring + the 5 display sections) were **deleted** from the COMPASS / COMPASSCANADA / QUAD
|
|
148
|
+
`orderViewFields.json` files. Data-wiring (`salesOrderDetailsConfig`/`itemFulfillmentClickRule`/
|
|
149
|
+
`recordActionFields`/`recordApprovals`) was **kept**. The DEFAULT `orderViewFields.json` is left
|
|
150
|
+
**intact** — non-roster clients are still JSON-driven.
|
|
151
|
+
|
|
152
|
+
**Still JSON / deferred:** the SalesOrders **action-bar / approval-modal** migration remains on JSON;
|
|
153
|
+
seeding NYCHH/Prudential/SPGlobal is deferred; the Client-DB prod cross-cluster seed fix is on hold
|
|
154
|
+
(see [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md)).
|
|
131
155
|
|
|
132
156
|
## Gotchas
|
|
133
157
|
|
|
@@ -147,6 +171,14 @@ code/`_underscore`, plus reconciling those seeds and the Compass approval-modal
|
|
|
147
171
|
treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
|
|
148
172
|
|
|
149
173
|
## Change history
|
|
174
|
+
- 2026-06-30 — Migrated the SalesOrders record-modal sections onto Surface for the roster clients
|
|
175
|
+
[COMPASS, COMPASSCANADA, QUAD] via the registered-renderer seam: added `useFetchSurfaceMetaGroup`
|
|
176
|
+
(order-insensitive grouped `/surfaces/meta-group` fetch) + `surfaceBundleToTenantFields()` (adapts
|
|
177
|
+
the grouped bundle to the existing `TenantFields` shape; bespoke grid renderers + lease/approval
|
|
178
|
+
logic stay in code). Roster-gated (code roster, JSON-base + Surface-wins) so non-roster clients
|
|
179
|
+
don't inherit Compass's shared base. Deleted the migrated section blocks from COMPASS/COMPASSCANADA/
|
|
180
|
+
QUAD `orderViewFields.json` (data-wiring kept; DEFAULT intact). Action-bar/approval-modal still
|
|
181
|
+
JSON. (jcardinal)
|
|
150
182
|
- 2026-06-29 — Migrated VendorItems (screen 2, recipe-exact) and Inventory (screen 3,
|
|
151
183
|
**presentation-only by decision** — fetch topology stays JSON; only title/buttons/modal copy/entity
|
|
152
184
|
tags moved). Fixed the "s.elements is not iterable" crash: `extractBundle()` now finds the bundle by
|
|
@@ -5,6 +5,7 @@ apps:
|
|
|
5
5
|
- _underscore
|
|
6
6
|
- api2
|
|
7
7
|
- toga2-supply
|
|
8
|
+
- toga25-supply
|
|
8
9
|
- toga2-commerce
|
|
9
10
|
- worker2
|
|
10
11
|
- dbchanges2
|
|
@@ -12,7 +13,7 @@ project: _Underscore
|
|
|
12
13
|
client: quad
|
|
13
14
|
type: profile
|
|
14
15
|
status: active
|
|
15
|
-
updated: 2026-06-
|
|
16
|
+
updated: 2026-06-30
|
|
16
17
|
owners: ["jcardinal", "bala"]
|
|
17
18
|
files: []
|
|
18
19
|
related:
|
package/package.json
CHANGED