toga-ai 1.0.407 → 1.0.409
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/dbchanges2/INDEX.md +1 -1
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +41 -0
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga25-supply/features/surface-frontend.md +50 -0
- package/knowledge/sessions/2026-07-21-true-79533-wholehome-per-address-guard-mhammontree.md +66 -0
- package/package.json +1 -1
|
@@ -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/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, 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, 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, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql, dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql, dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Core/2026-07-20e - RestoreApproveDenyRowActions.sql, dbchanges2/Client_Compass/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Core/2026-07-17 - README - RUN ORDER.md, dbchanges2/Client_Compass/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Compass/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_CompassCanada/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_CompassCanada/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_Quad/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Quad/2026-07-21b - SalesOrderApproveDisabledTooltipTranslation.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/Client/2026-06-25a - SurfaceClientTables.sql, dbchanges2/Client_Compass/2026-06-25d - SalesOrderSurfaceClientSeed.sql, _underscore/Model/Client/ThemeToken.php, toga25-supply/src/themeConfig.json, 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, 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, dbchanges2/Core/2026-07-20a - Update - HideAdminNotesSectionByDefault.sql, dbchanges2/Core/2026-07-20b - Update - NotesSectionFieldElements.sql, dbchanges2/Client_Compass/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_Compass/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20a - AdminNotesSectionVisibilityOverride.sql, dbchanges2/Client_CompassCanada/2026-07-20b - NotesSectionFieldsOverride.sql, dbchanges2/Client_Quad/2026-07-20a - NotesSectionFieldsOverride.sql, dbchanges2/Core/2026-07-20c - Update - VendorItemsToggleSurfaceSeed.sql, dbchanges2/Client_Compass/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_Compass/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20c - ItemRecordEditButtonEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20d - ItemRecordVendorItemsEnable.sql, dbchanges2/Core/2026-07-20e - RestoreApproveDenyRowActions.sql, dbchanges2/Client_Compass/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Client_CompassCanada/2026-07-20e - RowActionsApprovalWorkflowAdminEnable.sql, dbchanges2/Core/2026-07-17 - README - RUN ORDER.md, dbchanges2/Client_Compass/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Compass/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_CompassCanada/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_CompassCanada/2026-07-21b - SalesOrderApprovalsGateEnable.sql, dbchanges2/Client_Quad/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql, dbchanges2/Client_Quad/2026-07-21b - SalesOrderApproveDisabledTooltipTranslation.sql, dbchanges2/Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql, dbchanges2/Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql |
|
|
7
7
|
| [2.0 New-Client Onboarding (manual process)](workflows/client-onboarding.md) | > **A local browser wizard now automates this.** Steps 2–9 below (create DBs, generate Core/API > inserts, append to `Clients_Db.txt`) — plus the dbchanges2 bla | Client/, Client_<Tenant>/, Core/, Logs_Client/ |
|
|
@@ -60,6 +60,8 @@ files:
|
|
|
60
60
|
- dbchanges2/Client_CompassCanada/2026-07-21b - SalesOrderApprovalsGateEnable.sql
|
|
61
61
|
- dbchanges2/Client_Quad/2026-07-21a - SalesOrderApproveEnabledRuleOverride.sql
|
|
62
62
|
- dbchanges2/Client_Quad/2026-07-21b - SalesOrderApproveDisabledTooltipTranslation.sql
|
|
63
|
+
- dbchanges2/Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql
|
|
64
|
+
- dbchanges2/Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql
|
|
63
65
|
related:
|
|
64
66
|
- ../../_underscore/features/surface-resolver.md
|
|
65
67
|
---
|
|
@@ -278,6 +280,35 @@ Both clients' `c`/`d` overrides and the Core seed were added to `Core/2026-07-17
|
|
|
278
280
|
ORDER.md`. Verified working in the UI for both clients. (Boolean toggle/badge labels in this modal
|
|
279
281
|
resolve through the resolver's new `trueLabel`/`falseLabel` emission — see the resolver doc.)
|
|
280
282
|
|
|
283
|
+
## SalesOrder decision-modal surfaces (approve + deny) — shared summary + per-action chrome
|
|
284
|
+
|
|
285
|
+
The approve/deny decision modal's display config was migrated off the per-client
|
|
286
|
+
`approvalActionFields.json` into metadata-driven surfaces (Core seed → resolved per role → overlaid
|
|
287
|
+
FE-side; see [surface-frontend](../../toga25-supply/features/surface-frontend.md)). The seed split is
|
|
288
|
+
**one shared summary surface + two per-action chrome surfaces** (the 8 order-summary rows are identical
|
|
289
|
+
across approve/deny; only the chrome diverges):
|
|
290
|
+
|
|
291
|
+
- `Core/2026-07-21a - SalesOrderDecisionSummarySurfaceSeed.sql` — surface `sales-order-decision-summary`
|
|
292
|
+
(type SECTION, `config.cardType='decisionSummary'`), **SHARED** by both modals. 8 read-only summary
|
|
293
|
+
`FIELD` elements (Assigned Manager / Order for / Submitted by / Order # / Order Date / Total / Cost
|
|
294
|
+
Center / Reason for Request) with **`recordFieldId` NULL + `config.valueKey`** binding; Total is
|
|
295
|
+
`CURRENCY`; all `isVisible=1` (a display field is the Core neutral default — visible). Messages keyed
|
|
296
|
+
`salesOrder.decision.field.*`. (Renamed from an earlier approval-only slug to
|
|
297
|
+
`sales-order-decision-summary` to reflect the shared use.)
|
|
298
|
+
- `Core/2026-07-21b - SalesOrderDecisionActionSurfaceSeed.sql` — TWO surfaces
|
|
299
|
+
`sales-order-approve-action` and `sales-order-deny-action` (`config.cardType='decisionAction'`,
|
|
300
|
+
`action='approve'`/`'deny'`). 7 role-tagged elements each (`config.role`: title / guardrail /
|
|
301
|
+
reasonInput / confirmButton / cancelButton / successToast / errorToast). **Approve reason OPTIONAL**
|
|
302
|
+
(charLimit 255, check icon, mint-700); **deny reason REQUIRED** (`showRequiredIndicator`, xmark icon).
|
|
303
|
+
Messages keyed `salesOrder.approve.*` / `salesOrder.deny.*` plus a shared `salesOrder.decision.cancel`.
|
|
304
|
+
|
|
305
|
+
Both seeds are **ADDITIVE, id-agnostic, and re-runnable**: `NOT EXISTS`-guarded per surface slug +
|
|
306
|
+
`config.role`/`valueKey`; Messages guarded by `messageKey`; the surfaces inherit `appId`/`recordId`
|
|
307
|
+
from the existing `order-details` sibling surface **by slug** (never a hardcoded id). Same authoring
|
|
308
|
+
pattern as the `2026-07-17g` seed. These are **Core→Core only** (prod-safe — no cross-DB Client
|
|
309
|
+
subselect). Per-client divergence (via `SurfaceOverride`) for Compass / Compass Canada / Quad is **not
|
|
310
|
+
yet seeded** — this session is the shared Core default + FE wiring only.
|
|
311
|
+
|
|
281
312
|
## ThemeTokens — per-tenant, physically isolated in each client DB
|
|
282
313
|
|
|
283
314
|
`ThemeTokens` lives **inside each tenant's own database** (`Client_Compass.ThemeTokens`,
|
|
@@ -442,6 +473,16 @@ rule resumes.
|
|
|
442
473
|
override is added). **Open follow-up.**
|
|
443
474
|
|
|
444
475
|
## Change history
|
|
476
|
+
- 2026-07-21 — Seeded the **SalesOrder decision-modal (approve + deny) surfaces**, migrating the
|
|
477
|
+
per-client `approvalActionFields.json` display config to metadata. `Core/2026-07-21a` = one SHARED
|
|
478
|
+
`sales-order-decision-summary` SECTION surface (cardType `decisionSummary`, 8 read-only FIELD summary
|
|
479
|
+
rows, `recordFieldId` NULL + `config.valueKey`, Total CURRENCY, all visible); `Core/2026-07-21b` = two
|
|
480
|
+
per-action chrome surfaces `sales-order-approve-action`/`sales-order-deny-action` (cardType
|
|
481
|
+
`decisionAction`, 7 role-tagged elements by `config.role`, approve reason optional / deny reason
|
|
482
|
+
required). Both additive, id-agnostic, re-runnable (NOT EXISTS by slug + role/valueKey; Messages by
|
|
483
|
+
messageKey; appId/recordId inherited from the `order-details` sibling by slug), Core→Core only
|
|
484
|
+
(prod-safe). Per-client SurfaceOverride divergence not yet seeded — shared Core default + FE wiring
|
|
485
|
+
only. (apeterson)
|
|
445
486
|
- 2026-07-21 — Added the sales-order **Approve enable-gate** per-client overrides (`Client_*/2026-07-21a`):
|
|
446
487
|
Compass/CC ADMIN `ENABLED_RULE` = `(pendingInitialApproval AND stepTwoAssigned) OR pendingApproval`
|
|
447
488
|
on surface 8 el 17 + surface 3 el 29 (rule JSON in `c_longValue`; Compass UPDATEs its existing
|
|
@@ -10,6 +10,6 @@
|
|
|
10
10
|
| [Force Logout on Deployment (useDeploymentGuard)](features/force-logout-on-deployment.md) | On large deployments the backend bumps the Core parameter `META_LAST_REFRESH_DATETIME`. | toga25-supply/src/hooks/useDeploymentGuard.tsx, toga25-supply/src/App.tsx |
|
|
11
11
|
| [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 |
|
|
12
12
|
| [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/GenericNestedTables.tsx, toga25-supply/src/layout/GenericNestedTables/GenericTableLayout.tsx, toga25-supply/src/pages/Inventory/viewModel/useInventoryPageViewModel.tsx, toga25-supply/src/pages/Inventory/viewModel/FIELDS/DEFAULT/inventoryGroupings.json, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts |
|
|
13
|
-
| [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/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, 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/SurfaceRowActions.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, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx |
|
|
13
|
+
| [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/helpers/buildPatchedTenantFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/SalesOrderView.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderSummaryGrid.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getDetailSections.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderNotesSection.tsx, toga25-supply/src/pages/SalesOrders/helpers/cleanOrder.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/surface/resolveElementState.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrderRowRecordState.ts, toga25-supply/src/surface/actionRegistry.ts, toga25-supply/src/surface/componentRegistry.tsx, 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/SurfaceRowActions.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, toga25-supply/src/layout/ItemRecordModalLayout/helpers/surfaceBundleToItemFields.ts, toga25-supply/src/layout/ItemRecordModalLayout/helpers/index.ts, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/ItemRecordModalLayout.tsx, toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx, toga25-supply/src/surface/useStatusColors.ts, toga25-supply/src/surface/SurfaceHeader.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
|
|
14
14
|
| [AWS Amplify Multi-Environment Deployment](workflows/amplify-deployment.md) | How `toga25-supply` deploys to **all** of its environments on AWS Amplify from a **single shared `amplify.yml`**. | toga25-supply/amplify.yml, toga25-supply/src/api/api.ts, toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/vite.config.ts, toga25-supply/package.json |
|
|
15
15
|
| [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 |
|
|
@@ -49,6 +49,8 @@ files:
|
|
|
49
49
|
- toga25-supply/src/layout/ItemRecordModalLayout/components/ItemRecordView.tsx
|
|
50
50
|
- toga25-supply/src/surface/useStatusColors.ts
|
|
51
51
|
- toga25-supply/src/surface/SurfaceHeader.tsx
|
|
52
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/helpers/surfaceBundlesToDecisionFields.ts
|
|
53
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx
|
|
52
54
|
related:
|
|
53
55
|
- ../../_underscore/features/surface-resolver.md
|
|
54
56
|
- meta-driven-table-data.md
|
|
@@ -262,6 +264,40 @@ in [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md):
|
|
|
262
264
|
> shows **`leasePrice`** for all clients, whereas the old DEFAULT JSON showed only unit price. Confirm
|
|
263
265
|
> that is the intended seed shape.
|
|
264
266
|
|
|
267
|
+
## SalesOrders approval-decision modal (approve + deny) — migrated via an OVERLAY adapter seam
|
|
268
|
+
|
|
269
|
+
The SalesOrder approve/deny decision modal's config now sources its display fields from Surface,
|
|
270
|
+
overlaid onto the existing per-client `approvalActionFields.json` — the same adapter-seam pattern as
|
|
271
|
+
`surfaceBundleToTenantFields` / `surfaceBundleToItemViewFields`, but with a **merge (overlay), not a
|
|
272
|
+
full replacement**, because the surfaces cover only part of `recordActionFields`.
|
|
273
|
+
|
|
274
|
+
- **`surfaceBundlesToDecisionFields.ts`** (new, `SalesOrderApprovalModalsLayout/helpers/`) adapts the
|
|
275
|
+
**3** resolved surface bundles into the decision-modal shape `{ orderContentDetails, approve, deny }`.
|
|
276
|
+
Exports `DECISION_SURFACE_SLUGS`, `surfaceBundlesToDecisionFields()`, `isDecisionSurfaceEnabled()`,
|
|
277
|
+
and `overlayDecisionSurfaceFields(jsonRecordActionFields, decision)`. Elements are found by
|
|
278
|
+
**`config.role`** — no ADMIN/MANAGER branching, since surfaces resolve per-role server-side. Labels
|
|
279
|
+
via `resolveMessage`; icon/color/weight from `el.tokens`.
|
|
280
|
+
- **`useApprovalModalViewModel.tsx`** fetches the 3 surfaces via
|
|
281
|
+
`useFetchSurfaceMetaGroup(DECISION_SURFACE_SLUGS)`, computes `decisionSurfacesEnabled` /
|
|
282
|
+
`decisionSurfaceFields`, and builds `resolvedTenantFields` that **overlays** the 3 Surface-migrated
|
|
283
|
+
`recordActionFields` keys (`orderContentDetails`, `approve`, `deny`) onto the patched JSON. Unseeded
|
|
284
|
+
clients fall back to pure JSON (no roster; enablement is detected from the resolved bundles).
|
|
285
|
+
|
|
286
|
+
**Overlay, not replacement (decision).** The 3 surfaces cover only 3 of the 7 `recordActionFields`
|
|
287
|
+
keys; keys NOT yet migrated stay JSON-sourced (`guardrailCopy`, `actionOptions` [the approve
|
|
288
|
+
step-two enable rule], `approvalWorkflow`, `orderContentDetailsWorkflow`). The JSON approve/deny
|
|
289
|
+
blocks also carry **functional** attributes the surface does not emit (`characterLimit 255`,
|
|
290
|
+
`inputContainerWidth`, submit `valueKeys`). An `overlayDefined()` helper copies only *defined* surface
|
|
291
|
+
values, so JSON-only functional fields survive: **Surface owns all display attributes** (labels,
|
|
292
|
+
visibility, icons, `isRequired`, placeholder, `textAreaHeight`); JSON fills gaps and functional
|
|
293
|
+
wiring. The per-client `approvalActionFields.json` files are kept on disk as reference (not deleted).
|
|
294
|
+
|
|
295
|
+
**Shared summary, separate per-action chrome (decision).** The 8 order-summary rows are identical for
|
|
296
|
+
approve and deny, so they come from **one** shared surface (`sales-order-decision-summary`); the action
|
|
297
|
+
chrome (title, reason-input required-ness, confirm button, icons, toasts) diverges, so it is **two**
|
|
298
|
+
surfaces (`sales-order-approve-action`, `sales-order-deny-action`). Seed side:
|
|
299
|
+
[surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md).
|
|
300
|
+
|
|
265
301
|
## Two FE rule engines (surface Tier-1 vs legacy named-rule) — do not conflate
|
|
266
302
|
|
|
267
303
|
There are **two** rule evaluators in this app. As of 2026-07-21 the surface evaluator now has a
|
|
@@ -424,6 +460,20 @@ now carry it (2026-07-21):
|
|
|
424
460
|
treat type-checking as pending. Runtime `GET /v2/surfaces/{slug}/meta` also not yet exercised.
|
|
425
461
|
|
|
426
462
|
## Change history
|
|
463
|
+
- 2026-07-21 — Migrated the **SalesOrders approve/deny decision modal** display config onto Surface via
|
|
464
|
+
an **overlay** adapter seam. New `surfaceBundlesToDecisionFields.ts` adapts the 3 resolved bundles
|
|
465
|
+
(`DECISION_SURFACE_SLUGS`) into `{orderContentDetails, approve, deny}`, finding elements by
|
|
466
|
+
`config.role` (no ADMIN/MANAGER branch — surfaces resolve per-role server-side); exports
|
|
467
|
+
`isDecisionSurfaceEnabled()` + `overlayDecisionSurfaceFields()`. `useApprovalModalViewModel.tsx`
|
|
468
|
+
fetches via `useFetchSurfaceMetaGroup` and OVERLAYS the 3 migrated `recordActionFields` keys onto the
|
|
469
|
+
patched JSON — merge, not replacement, because 4 keys (`guardrailCopy`/`actionOptions`/
|
|
470
|
+
`approvalWorkflow`/`orderContentDetailsWorkflow`) and functional attrs (`characterLimit`/
|
|
471
|
+
`inputContainerWidth`/submit `valueKeys`) stay JSON-sourced; `overlayDefined()` copies only defined
|
|
472
|
+
surface values so JSON functional fields survive (Surface owns display, JSON fills gaps). Summary rows
|
|
473
|
+
are one shared surface (`sales-order-decision-summary`); approve/deny chrome are two surfaces. Unseeded
|
|
474
|
+
clients fall back to pure JSON (enablement detected from bundles, no roster). tsc + eslint clean
|
|
475
|
+
(replaced `any` with `Record<string,unknown>` + casts at the JSON boundary in the overlay helpers).
|
|
476
|
+
(apeterson)
|
|
427
477
|
- 2026-07-21 — **Option B: moved the Approve enable-gate onto the surface FE path.** Added a
|
|
428
478
|
`SURFACE_NAMED_RULES` map + a `{type:SurfaceNamedRule}` branch to `evaluateSurfaceRule.ts`
|
|
429
479
|
(`SurfaceNamedRule` = `"stepTwoAssigned"|"poNumberEntered"` in `types.ts`) — the sanctioned FE-side
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: session
|
|
3
|
+
slug: true-79533-wholehome-per-address-guard
|
|
4
|
+
title: Rate Whole Home Warranty — one active warranty per validated address
|
|
5
|
+
author: mhammontree
|
|
6
|
+
repos: [_underscore, dbchanges2, test]
|
|
7
|
+
framework: "2.0"
|
|
8
|
+
client: rate
|
|
9
|
+
status: active
|
|
10
|
+
created: 2026-07-21
|
|
11
|
+
updated: 2026-07-21
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Session: true-79533-wholehome-per-address-guard
|
|
15
|
+
**Date:** 2026-07-21
|
|
16
|
+
**Project/Repo:** _underscore + dbchanges2 (2.0), client Rate
|
|
17
|
+
**Task:** TRUE-79533 — implement, verify, deploy-to-beta, and CodeRabbit-harden a backend guard so a Rate Whole Home Warranty (WH) can only be purchased for a carrier-validated address, with one active WH per distinct physical address, globally.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What WORKED
|
|
22
|
+
- **prePost guard** in `_underscore/Model/Rate/Entitlement.php` — WH-only (title contains "warranty"); requires a service address; hard-blocks if carrier validation (`_Model_Client_Address::validateAddress`, USPS→FedEx→UPS) returns `success:false`; adopts the carrier-normalized address; then hard-blocks a 2nd active WH on the same address via `hasActiveWarrantyAtAddress()`. Committed on `TRUE-79533`.
|
|
23
|
+
- **postPost `persistWarrantyServiceAddress()`** — after save, sets `Addresses.isValidated=1` and `Entitlements.c_serviceAddressId`; wrapped so it never blocks the AIG-contract/email flow.
|
|
24
|
+
- **Migration** `dbchanges2/Client_Rate/2026-07-07a - WholeHomeWarrantyPerAddressGuard.sql` — adds `c_serviceAddressId` + index, guarded (`NOT EXISTS`) `CustomRecordFields`(191,NUMBER) + `ApiPayloadInterceptors`(191, PRE/POST), and an idempotent backfill of existing WH from the contact primary-address chain. Run against local + **beta** `Client_Rate`.
|
|
25
|
+
- **Verification** `test/@Mark/Rate/verify_wholehome_per_address_guard.php` — **18/18 pass** against the real migrated local `Client_Rate` (helpers + dedup matrix + prePost branches).
|
|
26
|
+
- **CodeRabbit fixes** (committed on `TRUE-79533`): (1) `c_serviceAddressId` declared as FK array form `[FIELD_FOREIGNKEY, FIELDOPT_FOREIGNKEY_MODEL => '\_Model_Client_Address']`; (2) `hasActiveWarrantyAtAddress()` now resolves the address via `COALESCE(c_serviceAddressId, contact primary address)` so an unpinned WH is still deduped. `php -l` clean.
|
|
27
|
+
- **Beta deploy + integration:** `_underscore` `TRUE-79533` pushed to beta, migration run on beta `Client_Rate`. Front-end dev (Tanner) pointed his local `toga2-view` at beta and confirmed the flow "looks good."
|
|
28
|
+
- **Knowledge captured/pushed:** rate `whole-home-warranty-purchase-guard` + `_underscore/address-validation` features, `aig-contract-creation` update, `2.0/standards/backend-testing.md` update (lazy-transaction commit-before-read), and `_underscore/workflows/running-a-2.0-app-locally.md`.
|
|
29
|
+
|
|
30
|
+
## What did NOT work — DO NOT RETRY THESE
|
|
31
|
+
- **Running the full app login locally** never fully succeeded — do not sink more time into it; beta is the test path. The cascade encountered:
|
|
32
|
+
- Stale local `Core_2` (missing `Core/2026-06-10 - TrackingNumberBridges.sql`) still had Records row 41 (`ItemFulfillmentPackages`, model `\_Model_Client_ItemFulfillmentPackage`); V2 loops every `Core.Records` model (`$model::TABLE`, V2.php:3445) and **fatals every request** on the deleted model file. That migration is NON-IDEMPOTENT (fixed ids 317-322 / RecordFields 2171-2200).
|
|
33
|
+
- After refreshing `Core_2`, login returned the silent `Route.php:525` "Failed to determine how to render view" — the API handler returned no response. **Ruled OUT:** production env (confirmed `ENVIRONMENT=dev-markhammontree-laptop` via `?__env=1` die in api2/index.php). **Confirmed CORRECT locally:** `Core_2.Domains` row `http://rate.togaview` (port-stripped, id 580, appId 36, clientId 39); `clientId 39` = local Rate; `dev`→`localhost` DatabaseHosts for client+log+archive; login user `sa-qa-aug21-a@yopmail.com` exists, isActive=1, hasPassword.
|
|
34
|
+
- `VAR=x php ...` (bash env prefix) in **PowerShell** silently no-ops → script falls back to defaults. Use `$env:VAR='x'; php ...`.
|
|
35
|
+
- `_Query` has **no bind/params API** — use `_Database::escape()` (int-cast for ids); do NOT copy the raw-interpolation style in `_Model_Aig_Entitlement`.
|
|
36
|
+
- Standalone `test/` scripts run with **autocommit OFF + a separate read connection** — a write is invisible to a later read until `_Database::transactionCommit(DB_CLIENT)`. Seed → commit → read → assert → commit cleanup.
|
|
37
|
+
|
|
38
|
+
## Not tried yet (candidates for next session)
|
|
39
|
+
- **Local full-app login (if ever needed):** the two remaining unverified gaps are the base **`Logs`** database (DB_LOGS → `Logs.Api`, table `Api` must exist — separate from `Logs_Rate`) and **`Core_2.Apis`** being seeded (secret-bearing, excluded from backups). Provision those before re-attempting.
|
|
40
|
+
- **Optional — api2 relationship expansion for `serviceAddress`:** set `CustomRecordFields.foreignRecordId` → Addresses record (id 13) via a NEW `dbchanges2` migration (the `2026-07-07a` file already ran on beta). Not currently needed; the FK model declaration alone was the CodeRabbit ask.
|
|
41
|
+
- **Optional — extend the verify script** with an unpinned-entitlement case (c_serviceAddressId NULL + a contact primary address at the test address → expect blocked) to behaviorally cover the new fallback.
|
|
42
|
+
|
|
43
|
+
## Current file state
|
|
44
|
+
| File | Status | Notes |
|
|
45
|
+
|------|--------|-------|
|
|
46
|
+
| `_underscore/Model/Rate/Entitlement.php` | Committed (TRUE-79533) | prePost/postPost guard + CodeRabbit fixes (FK decl, COALESCE dedup fallback). NOTE: working tree may show the pre-guard TRUE-79191 version — the guard lives on branch `TRUE-79533`. |
|
|
47
|
+
| `_underscore/Model/Client/Address.php` | Committed (TRUE-79533) | Dead commented `validateAddress` block removed. |
|
|
48
|
+
| `dbchanges2/Client_Rate/2026-07-07a - WholeHomeWarrantyPerAddressGuard.sql` | Committed; run on beta | column+index+guarded registrations+backfill. |
|
|
49
|
+
| `test/@Mark/Rate/verify_wholehome_per_address_guard.php` | Committed | 18/18; does not yet cover the unpinned fallback. |
|
|
50
|
+
| `C:\WWW\true79533.md` | Local working ref | Logic-flow walkthrough with Mermaid diagrams (overlaps the captured guard doc). |
|
|
51
|
+
|
|
52
|
+
## Decisions made
|
|
53
|
+
- **Uniqueness = global per distinct address** (not per-borrower). Rationale: AIG one-plan-per-property; `line2` distinguishes apartment units so a landlord's 2A/2B/2C = 3 allowed WHs. Rejected: per-borrower scoping.
|
|
54
|
+
- **Invalid address → hard-block** the purchase (no entitlement). Rejected: allow-with-flag.
|
|
55
|
+
- **Persist `c_serviceAddressId` on the entitlement** rather than derive from the contact's (mutable) primary address; backfill existing WH. Rationale: reliable dedup + enables per-plan address display.
|
|
56
|
+
- **CodeRabbit #2 → fallback join, NOT pin-throw.** Rationale: making the pin required would reintroduce the block-the-save risk we deliberately designed out; COALESCE fallback closes the blind spot without that risk.
|
|
57
|
+
- **Deploy to beta + FE points local→beta** instead of fully rebuilding the local backend stack. Rationale: local full-app run needs base Logs + Apis seeding; beta has a complete stack; ticket verified independently (18/18).
|
|
58
|
+
|
|
59
|
+
## Blockers
|
|
60
|
+
None for the ticket — code committed on `TRUE-79533`, deployed to beta, integration-confirmed by the FE dev, CodeRabbit review resolved. (Local full-app login remains unresolved but is NOT blocking; beta is the test path.)
|
|
61
|
+
|
|
62
|
+
## Exact next step
|
|
63
|
+
> QA of TRUE-79533 on beta/QA: exercise the multi-unit landlord case (same building, different `line2` → multiple WHs allowed), the duplicate block (2nd active WH on same address rejected), invalid-address hard-block → toaster, tech-support unaffected, and the backfill against pre-existing WH. If QA files anything, use `C:\WWW\true79533.md` + the captured `clients/rate/features/whole-home-warranty-purchase-guard.md` as the reference.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
_Saved by /session-save on 2026-07-21_
|
package/package.json
CHANGED