toga-ai 1.0.764 → 1.0.765

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@ project: Database Changes
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-02
9
+ updated: 2026-09-03
10
10
  owners: [jcardinal, apeterson, tcox]
11
11
  files:
12
12
  - dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql
@@ -399,6 +399,11 @@ and `NOT EXISTS`-guarded.
399
399
  `config.valueKey='_totalPurchase'`. Requires the small FE adapter extension to emit `isConcatenated`
400
400
  (see [surface-frontend](../../toga25-supply/features/surface-frontend.md)). Authored this session;
401
401
  only Quad confirmed applied locally.
402
+ **Do NOT add `"suffix":"+"` to this element's config to get the "$4,737.00 +" joiner (2026-09-03).**
403
+ blox renders a field-level `suffix` **unconditionally**, but the stacked lease line auto-hides when
404
+ `_totalLease` is null/0 — a seeded `+` would dangle on every non-lease order. The joiner is FE-side
405
+ and conditional (`withStackedCurrencyJoiner`, see
406
+ [surface-frontend](../../toga25-supply/features/surface-frontend.md)).
402
407
 
403
408
  ## ThemeTokens — per-tenant, physically isolated in each client DB
404
409
 
@@ -1288,6 +1293,11 @@ uncommitted at the time. See
1288
1293
  [Transfer Orders page](../../toga25-supply/features/transfer-orders-page.md).
1289
1294
 
1290
1295
  ## Change history
1296
+ - 2026-09-03 — Flagged that the Compass / Compass Canada stacked-Total `CONFIG` override must **not**
1297
+ gain a `"suffix":"+"`: blox renders a field `suffix` unconditionally while the stacked lease line
1298
+ auto-hides on null/0, so a seeded `+` dangles on non-lease orders. The joiner is conditional FE code
1299
+ (`withStackedCurrencyJoiner`) — see
1300
+ [surface-frontend](../../toga25-supply/features/surface-frontend.md). (tcox)
1291
1301
  - 2026-09-02 — Wrote down the **`Core.Records` / `Core.RecordFields` id protocol**, the counterpart to
1292
1302
  this doc's reserved-block discipline: they are the only two hand-maintained-id tables platform-wide,
1293
1303
  so **ask the developer for the next free id**, never `AUTO_INCREMENT` them, and **never infer the
@@ -12,7 +12,7 @@
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/pages/SalesOrders/view/SalesOrderRecordModalLayout/hooks/usePurchaseOrderDetails.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/layout/PrimaryTableServerLayout/PrimaryTableServerLayout.tsx, toga25-supply/src/layout/PrimaryTableServerLayout/types.ts, 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/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/hooks/useTableCellInteractions.ts, toga25-supply/src/hooks/useServerTableUrlState.ts, toga25-supply/src/layout/RecordApprovalModal/helpers/handleFormatApprovalWorkflowPayload.ts, toga25-supply/src/layout/RecordApprovalModal/api/approvalDecisionsApi.ts, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css |
13
13
  | [Side navigation & default route — an empty nav renders a BLANK PAGE and gets reported as "cannot log in"](features/side-navigation-and-default-route.md) | The 2.5 side nav is **100% backend-driven** by the `navigation` surface bundle, and the same list also decides **which routes exist** and **where `/` lands**. | toga25-supply/src/routes.tsx, toga25-supply/src/layout/AppLayout/viewModel/useAppLayoutViewModel.ts, api2/Component/Api/V2/V2.php, _underscore/Model/Core/Surface.php |
14
14
  | [SSO redirect & public-vs-user session gating (useAuthenticationFlow)](features/sso-redirect-and-session-gating.md) | How 2.5 Supply decides, on every navigation, whether an anonymous visitor should be bounced to their client's SSO IdP instead of the local `/login` form. | toga25-supply/src/hooks/useAuthenticationFlow.ts, toga25-supply/src/routes.tsx, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/api/api.ts |
15
- | [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/App.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/DenialBanner.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/SalesOrderRecordModal.module.css, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/SalesOrderApprovalModalsLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/RecordApprovalModalLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/view/ApprovalTimelineView.tsx, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css, toga25-supply/src/utils/formatDateTime.ts, toga25-supply/src/utils/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/layout/RecordApprovalModal/, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/surface/applyColSpan.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getVisibleSections.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderApprovalSummaryGrid.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/ServiceRequestRecordModalLayout.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/view/ServiceRequestsView.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/useServiceRequestRecordModalLayoutModel.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, 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/view/sections/AdminNotesSection.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getAdminNotes.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/hooks/useSalesOrderVip.ts, 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 |
15
+ | [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/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.ts, toga25-supply/src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.test.ts, toga25-supply/src/App.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/DenialBanner.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/SalesOrderRecordModal.module.css, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/SalesOrderApprovalModalsLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/RecordApprovalModalLayout.tsx, toga25-supply/src/layout/RecordApprovalModal/view/ApprovalTimelineView.tsx, toga25-supply/src/layout/RecordApprovalModal/ApprovalModal.module.css, toga25-supply/src/utils/formatDateTime.ts, toga25-supply/src/utils/index.ts, toga25-supply/src/surface/evaluateSurfaceRule.ts, toga25-supply/src/layout/RecordApprovalModal/, toga25-supply/src/contexts/AuthContext.tsx, toga25-supply/src/surface/applyColSpan.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/sectionRenderers.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getVisibleSections.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/SalesOrderApprovalSummaryGrid.tsx, toga25-supply/src/surface/useFetchSurfaceMeta.ts, toga25-supply/src/pages/SalesOrders/helpers/surfaceBundleToTenantFields.ts, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/ServiceRequestRecordModalLayout.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/view/ServiceRequestsView.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/useServiceRequestRecordModalLayoutModel.tsx, toga25-supply/src/pages/ServiceRequests/view/ServiceRequestRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, 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/view/sections/AdminNotesSection.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/helpers/getAdminNotes.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/hooks/useSalesOrderVip.ts, 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 |
16
16
  | [Talos Integration (AppLayout host, live Aegra streaming, LangGraph→blox mapper)](features/talos-integration.md) | toga25-supply is the first host of the shared blox [Talos assistant](../../toga-blox/features/talos-assistant.md). | toga25-supply/src/api/talos.ts, toga25-supply/src/hooks/useTalosSession.ts, toga25-supply/src/hooks/useTalosThreads.ts, toga25-supply/src/hooks/useTalosThreads.test.ts, toga25-supply/src/hooks/useTalosSurface.ts, toga25-supply/src/hooks/useTalosSurface.test.ts, toga25-supply/src/App.tsx, toga25-supply/db-migrations/PLAYBOOK.md, dbchanges2/Core/2026-09-02a - TalosAssistantSurfaceSeed.sql, dbchanges2/Client_Nychh/2026-09-02a - TalosAssistantEnable.sql, toga25-supply/src/providers/TalosStreamProvider.tsx, toga25-supply/src/utils/talosMessages.ts, toga25-supply/src/utils/talosMessages.test.ts, toga25-supply/src/layout/AppLayout/AppLayout.tsx, toga25-supply/src/components/Header/Header.tsx, toga25-supply/src/components/Header/Header.module.css, toga25-supply/src/index.css, toga25-supply/src/assets/talos-owl.png |
17
17
  | [Transfer Orders page (TableView → Core surfaces → React page + record modal)](features/transfer-orders-page.md) | The Transfer Orders screen — list + read-only record modal — built end to end on 2026-08-28 from a Claude Design prototype. | toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/PanelSelect.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/ContactSelect.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/SelectTargetLocationModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/TransferItemPickerTable.tsx, _underscore/Model/Client/TransferOrder.php, _underscore/Model/Client/TransferOrderNote.php, _underscore/Model/Client/TransferOrderNoteType.php, dbchanges2/Client/2026-09-01b - TransferOrderNotesTables.sql, dbchanges2/Client/2026-09-01c - TransferOrderContactCreatedByAndDescription.sql, dbchanges2/Core/2026-09-01c - TransferOrderNotesRecords.sql, dbchanges2/Core/2026-09-01d - TransferOrderContactCreatedByAndDescriptionRecordFields.sql, toga25-supply/src/pages/TransferOrders/TransferOrders.tsx, toga25-supply/src/pages/TransferOrders/hooks/useTransferOrdersTableState.ts, toga25-supply/src/pages/TransferOrders/viewModel/useTransferOrdersPageViewModel.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrdersTableLayout/TransferOrdersTableLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/TransferOrderRecordModalLayout.tsx, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/hooks/useTransferOrderRecord.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/viewModel/useTransferOrderRecordModalLayoutModel.ts, toga25-supply/src/pages/TransferOrders/view/TransferOrderRecordModalLayout/view/TransferOrderRecordView.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/CreateTransferOrderModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/index.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/usePurchaseOrderItemRows.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTargetLocationOptions.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useTransferSourceLocation.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useLocationContacts.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateLocationContact.ts, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/AddContactModal.tsx, toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/view/TransferNotesField.tsx, dbchanges2/Client/2026-09-01a - PurchaseOrderItemQtyFieldsApiRoleRead.sql, toga25-supply/src/surface/useColumnVisibilityModalConfig.ts, toga25-supply/src/routes.tsx, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql |
18
18
  | [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, toga25-supply/.env.development |
@@ -6,9 +6,11 @@ project: TOGa 2.5 Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-09-02
9
+ updated: 2026-09-03
10
10
  owners: [jcardinal, apeterson, tcox]
11
11
  files:
12
+ - toga25-supply/src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.ts
13
+ - toga25-supply/src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.test.ts
12
14
  - toga25-supply/src/App.tsx
13
15
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/layoutComponents/DenialBanner.tsx
14
16
  - toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/SalesOrderRecordModal.module.css
@@ -354,13 +356,23 @@ full replacement**, because the surfaces cover only part of `recordActionFields`
354
356
 
355
357
  **Overlay, not replacement (decision).** The 3 surfaces cover only 3 of the 7 `recordActionFields`
356
358
  keys; keys NOT yet migrated stay JSON-sourced (`guardrailCopy`, `actionOptions` [the approve
357
- step-two enable rule], `approvalWorkflow`, `orderContentDetailsWorkflow`). The JSON approve/deny
359
+ step-two enable rule], `approvalWorkflow`). The JSON approve/deny
358
360
  blocks also carry **functional** attributes the surface does not emit (`characterLimit 255`,
359
361
  `inputContainerWidth`, submit `valueKeys`). An `overlayDefined()` helper copies only *defined* surface
360
362
  values, so JSON-only functional fields survive: **Surface owns all display attributes** (labels,
361
363
  visibility, icons, `isRequired`, placeholder, `textAreaHeight`); JSON fills gaps and functional
362
364
  wiring. The per-client `approvalActionFields.json` files are kept on disk as reference (not deleted).
363
365
 
366
+ **`orderContentDetailsWorkflow` IS surface-driven — it is the SAME array as approve/deny (verified
367
+ 2026-09-03).** `overlayDecisionSurfaceFields` points **both** `orderContentDetails` and
368
+ `orderContentDetailsWorkflow` at `decision.orderContentDetails`
369
+ (`surfaceBundlesToDecisionFields.ts` ~L458), so the Approval Workflow modal's summary rows come from
370
+ `sales-order-decision-summary` exactly like the approve/deny modals. The header comment in
371
+ `useApprovalModalViewModel.tsx` (~L155) still lists `orderContentDetailsWorkflow` among the
372
+ "unmigrated keys" — that comment is **stale**. Practical effect: any seed or adapter change to the
373
+ decision summary lands in **all three** modal modes at once, and the two grids share one `colSpan`
374
+ value (see the colSpan caveat below).
375
+
364
376
  **Concatenated summary values (`isConcatenated`).** `surfaceBundlesToDecisionFields.ts` now passes an
365
377
  element's `config.isConcatenated` through **verbatim** onto the decision-summary row: it exports a
366
378
  `DecisionSummaryConcat` type (string shorthand | descriptor `{valueKey, prefix?, suffix?, valueType?,
@@ -372,6 +384,46 @@ side in [surface-layer-schema](../../dbchanges2/features/surface-layer-schema.md
372
384
  the FE ignored. blox `BaseDetailField` (`renderBaseDetailFieldValue.tsx`) already supports
373
385
  `valueKey`+`suffix`+`layout:'stacked'`+`valueType:'currency'`. tsc + eslint clean.
374
386
 
387
+ ### The stacked-Total `+` joiner is FE code, NOT a seeded `suffix` (2026-09-03)
388
+
389
+ Design wants the two stacked Total lines to read as one sum, so the primary line gets a trailing
390
+ `+` — but **only when the lease line is actually there**:
391
+
392
+ ```
393
+ $4,737.00 +
394
+ $107.00/mo
395
+ ```
396
+
397
+ `withStackedCurrencyJoiner(field, data)` (`src/layout/RecordApprovalModal/helpers/stackedCurrencyJoiner.ts`)
398
+ returns the field with `suffix: "+"` (appended after any existing suffix) **only** when all of this
399
+ holds: `field.valueType === 'currency'`, `field.isConcatenated` is a **descriptor** (not the string
400
+ shorthand) with `layout: 'stacked'` and `valueType: 'currency'`, **and** the add-on value resolves to
401
+ something non-null / non-empty / non-zero. Otherwise it returns the **same field reference**. The
402
+ visibility check deliberately mirrors blox `renderBaseDetailFieldValue.resolveSecondValue` — dotted
403
+ `valueKey` walk plus the optional `getValue` resolver — so the `+` and the second line appear and
404
+ disappear together.
405
+
406
+ Wired in `RecordApprovalModalLayout.tsx` `renderField` as
407
+ `field={isWorkflow ? withStackedCurrencyJoiner(field, data) : field}`, covering the workflow modal's
408
+ editable **and** read-only views. **Approve/deny modals are deliberately unchanged** — extending to
409
+ them is a one-line change (drop the `isWorkflow` check).
410
+
411
+ **Why this is app code and not blox or a seed (decision).** blox `BaseDetailField` renders a
412
+ field-level `suffix` **unconditionally** after a currency primary (`base = "$ X" + " " + suffix`),
413
+ while the stacked add-on line **auto-hides** on null/empty/0. There is no surface or blox option for
414
+ "suffix only when the second line shows", so a seed-side `suffix:"+"` on the Total element would
415
+ leave a **dangling `+`** on every order with no monthly lease. Doing it in blox would need a new
416
+ concat-descriptor option plus a publish, a pin bump in toga25-supply, and SQL for Compass and Compass
417
+ Canada — a lot of machinery for a presentation flourish. This is the same line as the grid-geometry
418
+ boundary below: **config describes, code decides.** (blox pin at the time: `1.1.1-production.139`.)
419
+
420
+ Covered by `stackedCurrencyJoiner.test.ts` (13 vitest cases) — the joiner is pure, so it is testable
421
+ without rendering the modal.
422
+
423
+ **Not implemented:** the mockup also showed `(36 months)` after the lease line. Nothing in the
424
+ current surface config, seeds, or FE code resolves a lease **term**, so it was left out — it needs a
425
+ data source before it can be rendered.
426
+
375
427
  **Shared summary, separate per-action chrome (decision).** The 8 order-summary rows are identical for
376
428
  approve and deny, so they come from **one** shared surface (`sales-order-decision-summary`); the action
377
429
  chrome (title, reason-input required-ness, confirm button, icons, toasts) diverges, so it is **two**
@@ -914,6 +966,22 @@ button except **View Log** renders and does nothing on click. Whether the SR mod
914
966
  actions surface is **still an open decision**, not an oversight.
915
967
 
916
968
  ## Change history
969
+ - 2026-09-03 — **Built the stacked-Total `+` joiner in the Approval Workflow modal**
970
+ (`withStackedCurrencyJoiner`, new `RecordApprovalModal/helpers/stackedCurrencyJoiner.ts` + 13
971
+ vitest cases; one-line wiring in `RecordApprovalModalLayout` `renderField`, workflow modal only —
972
+ approve/deny untouched). It adds `suffix:"+"` to a currency primary **only** when its
973
+ `isConcatenated` descriptor is `layout:'stacked'` + `valueType:'currency'` **and** the add-on value
974
+ will actually render (mirrors blox `resolveSecondValue`, incl. `getValue` + dotted `valueKey`), so
975
+ the `+` never dangles on an order with no monthly lease. **DECISION: this stays app code** — blox
976
+ renders a field `suffix` unconditionally and there is no "suffix only if the second line shows"
977
+ option, so a seeded `suffix:"+"` would be wrong for non-lease orders, and a blox option would cost
978
+ a publish + pin bump + SQL for Compass and Compass Canada. Same boundary as grid geometry: config
979
+ describes, code decides. Also **corrected** the doc's claim that `orderContentDetailsWorkflow` is
980
+ unmigrated/JSON-sourced — `overlayDecisionSurfaceFields` points it at the **same**
981
+ `sales-order-decision-summary` array as approve/deny (the stale claim also survives as a comment in
982
+ `useApprovalModalViewModel.tsx` ~L155). Mockup's `(36 months)` lease term is **not** implemented —
983
+ no data source for it. tsc + eslint clean, vitest 18/18 in `src/layout/RecordApprovalModal`; blox
984
+ pin `1.1.1-production.139`; uncommitted on `TRUE-80850`. (tcox)
917
985
  - 2026-09-02 — **FIXED the surface bundle being cached in `localStorage` for up to 24h, so a
918
986
  permission or feature change could not take effect.** `App.tsx`'s `PersistQueryClientProvider` had
919
987
  **no `dehydrateOptions`**, so every query — including the `staleTime: Infinity` surface bundle —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.764",
3
+ "version": "1.0.765",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",