toga-ai 1.0.712 → 1.0.713
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 +2 -1
- package/knowledge/2.0/apps/dbchanges2/features/rerunnable-additive-inserts.md +2 -3
- package/knowledge/2.0/apps/dbchanges2/features/surface-layer-schema.md +49 -2
- package/knowledge/2.0/apps/dbchanges2/workflows/targeting-the-right-database.md +99 -0
- package/knowledge/2.0/apps/toga-blox/workflows/dynamic-publish-pipeline.md +13 -1
- package/knowledge/2.0/apps/toga-blox/workflows/local-link-into-a-consumer-app.md +32 -1
- package/knowledge/2.0/apps/toga25-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga25-supply/features/action-button-rule-engine.md +52 -3
- package/knowledge/2.0/apps/toga25-supply/features/record-modals-and-nested-tables.md +13 -0
- package/knowledge/2.0/apps/toga25-supply/features/transfer-orders-page.md +23 -4
- package/knowledge/2.0/apps/toga25-supply/workflows/amplify-deployment.md +85 -1
- package/knowledge/INDEX.md +1 -1
- package/knowledge/clients/compass-usa/INDEX.md +1 -1
- package/knowledge/clients/compass-usa/features/approval-decision-flow.md +51 -1
- package/knowledge/clients/compass-usa/workflows/granting-navigation-access.md +10 -3
- package/knowledge/clients/nychh/INDEX.md +1 -1
- package/knowledge/clients/nychh/profile.md +26 -6
- package/package.json +1 -1
|
@@ -3,11 +3,12 @@
|
|
|
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
|
-
| [Re-runnable additive migrations (uuid4 in SQL, INSERT guards, the DISTINCT trap, conditional ALTERs)](features/rerunnable-additive-inserts.md) | Most `dbchanges2` files are **additive data grants** run by hand against production, often more than once (once per environment, or twice because someone was no | dbchanges2/Client_Compass/2026-08-26a - CompassCreativeStudioPersona.sql, dbchanges2/
|
|
6
|
+
| [Re-runnable additive migrations (uuid4 in SQL, INSERT guards, the DISTINCT trap, conditional ALTERs)](features/rerunnable-additive-inserts.md) | Most `dbchanges2` files are **additive data grants** run by hand against production, often more than once (once per environment, or twice because someone was no | dbchanges2/Client_Compass/2026-08-26a - CompassCreativeStudioPersona.sql, dbchanges2/Core/2026-08-27a - Insert - Netsuite Location SyncAll CronJob.sql |
|
|
7
7
|
| [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 | toga25-supply/db-migrations/PLAYBOOK.md, toga25-supply/db-migrations/SURFACE-FEATURE-RUNBOOK.md, dbchanges2/Client_Compass/, dbchanges2/Client_CompassCanada/, dbchanges2/Client_Quad/, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql, dbchanges2/Core/2026-08-24b - SalesOrderStatusFilterSurfaceSeed.sql, dbchanges2/Client_Compass/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_CompassCanada/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_Quad/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Client_Nychh/2026-08-24b - SalesOrderStatusFilterHides.sql, dbchanges2/Core/2026-08-21 - SalesOrderDecisionSurfacesReseed.sql, dbchanges2/Core/2026-08-24 - RestoreApproveDenyRowActionsVisibility.sql, dbchanges2/Client_Quad/2026-08-24 - ProdPortApprovePoNumberEnabledRule.sql, dbchanges2/Client_Quad/2026-07-21b - ApproveDisabledTooltipPoNumber.sql, dbchanges2/Client_Quad/2026-08-21 - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_CompassCanada/2026-08-24 - ProdPortApprovalsGateAndApproveStepTwoRule.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, 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, dbchanges2/Client_Quad/2026-07-21c - SalesOrderDecisionSummaryOverride.sql, dbchanges2/Client_Compass/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Client_CompassCanada/2026-07-21c - SalesOrderDecisionSummaryTotalConcat.sql, dbchanges2/Core/2026-07-23a - PoNumberDetailFieldValueKey.sql, dbchanges2/Core/2026-07-29a - SalesOrderApprovalDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-05b - ApprovalDetailsShippingMethodConcatCharge.sql, dbchanges2/Core/2026-08-14a - NoteBadgesVocabularySurface.sql, dbchanges2/Client/2026-08-14a - NoteBadgeThemeTokens.sql, dbchanges2/Client/2026-08-14b - NoteBadgeUserColorFix.sql, dbchanges2/Client_Compass/2026-08-14a - NoteBadgeDelegateThemeTokens.sql, dbchanges2/Client_CompassCanada/2026-08-14a - NoteBadgeDelegateThemeTokens.sql, dbchanges2/Core/2026-08-26a - ServiceRequestRecordHeaderSurfaceSeed.sql, dbchanges2/Core/2026-08-26b - ServiceRequestDetailsSurfaceSeed.sql, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql, dbchanges2/Client/2026-08-28a - TransferOrderStageThemeTokens.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql |
|
|
8
8
|
| [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/ |
|
|
9
9
|
| [Auditing a client DB that drifted from its models (partially applied module migration)](workflows/client-schema-drift-audit.md) | A recurring 2.0 failure mode: **one client's database drifts from what the PHP models declare**, usually because a `_modules/<module>/` migration was applied to | dbchanges2/Client_Growrk/2026-05-28.sql, dbchanges2/Client_Growrk/2026-08-10c - GrowrkServiceRequestCustomFieldsCatchUp.sql, dbchanges2/Client_Growrk/2026-08-10d - GrowrkServiceRequestTypeAndDispositionSeeds.sql, dbchanges2/_modules/netsuite/2026-07-10a - UnitInventoryFields.sql, dbchanges2/Client_Growrk/2026-08-10 - GrowrkUnitInventoryFieldsCatchUp.sql, dbchanges2/Client_Growrk/2026-08-10b - GrowrkUnitItemDescriptionAcl.sql, dbchanges2/Client_Growrk/_modules.txt |
|
|
10
10
|
| [Framework branch running AHEAD of schema — environment-wide 1054/EO-1 after a deploy](workflows/framework-branch-ahead-of-schema.md) | A **third** kind of 2.0 schema drift, distinct from the two already documented: nobody's database went backwards — **the code went forwards**. | _underscore/Model/Core/RecordField.php, _underscore/Model/Client/TableViewField.php, _underscore/Model/Client/TableView.php, _underscore/Model/Core/Surface.php, _underscore/Model/Client/Urgency.php, api2/Component/Api/V2/V2.php, dbchanges2/Core/2026-08-14c - RecordFieldsValidationMetadata.sql, dbchanges2/Client/2026-08-14a - RecordFieldSettingsClientTable.sql, dbchanges2/Client/2026-08-13a - UserNavPreferencesClientTable.sql, dbchanges2/Client/2026-08-13c - PersonaNavVisibilityClientTable.sql |
|
|
11
11
|
| [Local vs prod MySQL config parity — why “it passed locally” is not evidence](workflows/local-vs-prod-mysql-config-parity.md) | Several migration failures that look like "prod-only bugs" are actually **per-machine MySQL server-configuration differences**. | |
|
|
12
12
|
| [Repairing non-prod metadata drift (works in prod, broken in beta/dev-sandbox)](workflows/nonprod-metadata-drift-repair.md) | Almost all 2.0 platform behavior is **metadata** — `Core.Records`/`RecordFields`, `Core.RecordScripts`, `Core.ApiPayloadInterceptors`, and per-client `Acl*` row | dbchanges2/Core/2026-06-30a - ItemFulfillmentStageDefaultInterceptor.sql, dbchanges2/Client_Compass/2026-08-06 - RemoveBrokenSalesOrderItemPostPostInterceptor.sql, dbchanges2/Core/2026-07-16a - TrackingNumberSignatureTypeRecordField.sql, dbchanges2/_modules/netsuite/2026-07-10a - UnitInventoryFields.sql, dbchanges2/Client_Aig/_modules.txt, dbchanges2/Client/2026-07-22c - TrackingNumberMeasureIdsFieldPermission.sql, api2/Config/beta.ini, api2/Config/sandbox-dev.ini, dbchanges2/Client/2026-07-22a - EntitlementServiceAddressId.sql, dbchanges2/Client/2026-07-23a - EntitlementServiceAddressIdFieldPermission.sql |
|
|
13
|
+
| [Targeting the right database (tenant restores, and running a change file against the wrong DB)](workflows/targeting-the-right-database.md) | Two ways to point at the wrong database — a dump taken from the wrong host, and a change file piped into the wrong schema. | api2/Config/production.ini, dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql |
|
|
13
14
|
| [Verifying whether a dbchanges2 migration actually ran in an environment](workflows/verifying-a-migration-ran.md) | dbchanges2 has no execution ledger you can query — "did this file run here?" has to be answered from the **rows the file would have produced**. | Client/2026-08-11b - SalesOrderPurchaseOrdersField.sql, Client/2026-08-13 - UnitsForItemsPO_SalesOrderItemsJoin.sql |
|
|
@@ -6,11 +6,10 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
10
|
-
owners: [bala, kyalamarthi]
|
|
9
|
+
updated: 2026-08-31
|
|
10
|
+
owners: [bala, kyalamarthi, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Client_Compass/2026-08-26a - CompassCreativeStudioPersona.sql
|
|
13
|
-
- dbchanges2/Client_Compass/2026-08-28 - GrantViewOrdersRoleSalesOrdersNavigation.sql
|
|
14
13
|
- dbchanges2/Core/2026-08-27a - Insert - Netsuite Location SyncAll CronJob.sql
|
|
15
14
|
related:
|
|
16
15
|
- ../architecture.md
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: [jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/db-migrations/PLAYBOOK.md
|
|
@@ -563,7 +563,10 @@ All four gated buttons (17/18/19/21) carry the **Core** rule `order._status in [
|
|
|
563
563
|
so on a **canceled** order the approval-workflow button is **hidden**.
|
|
564
564
|
|
|
565
565
|
**Next free Core ids in PROD, 2026-08-28:** `Surfaces` **47**, `SurfaceElements` **170**, `Messages`
|
|
566
|
-
**227**, `Actions` **17**.
|
|
566
|
+
**227**, `Actions` **17**. **Superseded — on 2026-08-31 (sandbox-client, freshly reset from prod) the
|
|
567
|
+
MAX ids were `Surfaces` 52 / `SurfaceElements` 189 / `Messages` 260, and `Core/2026-08-28a`+`b` then
|
|
568
|
+
claimed `Surfaces` 53–57 / `SurfaceElements` 190–206 / `Messages` 261–284.** (Re-verify per the 🚨
|
|
569
|
+
rule below before writing a seed — "next free" vs
|
|
567
570
|
"highest used" is exactly what caused the 2026-08-24 collision.)
|
|
568
571
|
|
|
569
572
|
**Per-tenant overrides, as they actually exist in prod:**
|
|
@@ -605,6 +608,41 @@ the LIST surface + 4 record-modal SECTION surfaces + the `transfer-order-stage`
|
|
|
605
608
|
verified free on **local AND production** before authoring. See
|
|
606
609
|
[Transfer Orders page](../../toga25-supply/features/transfer-orders-page.md).
|
|
607
610
|
|
|
611
|
+
#### ⚠ Those blocks were RENUMBERED on 2026-08-31 — a reservation expires when someone else ships
|
|
612
|
+
|
|
613
|
+
The 2026-08-28 blocks above were reserved from a **2026-08-28 snapshot** of production, and were
|
|
614
|
+
**claimed by another feature before the transfer-order files shipped**: `sales-order-denial-details`
|
|
615
|
+
took `Surfaces` **47**, `SurfaceElements` **170–176** and `Messages` **227–232**. Re-verified against
|
|
616
|
+
sandbox-client (freshly reset from production) on 2026-08-31, where MAX ids were `Surfaces` **52** /
|
|
617
|
+
`SurfaceElements` **189** / `Messages` **260**, the two transfer-order files were renumbered to a
|
|
618
|
+
contiguous block above MAX:
|
|
619
|
+
|
|
620
|
+
| | Reserved 2026-08-28 (stale) | **Live, as committed 2026-08-31** |
|
|
621
|
+
|---|---|---|
|
|
622
|
+
| `Core.Messages` | 227–260 | **261–284** |
|
|
623
|
+
| `Core.Surfaces` | 47–52 | **53–57** |
|
|
624
|
+
| `Core.SurfaceElements` | 170–189 | **190–206** |
|
|
625
|
+
|
|
626
|
+
`Surfaces` 48–51, `SurfaceElements` 177–186 and `Messages` 233–250 are deliberately left as **unused
|
|
627
|
+
gaps** rather than back-filled — a contiguous block above MAX is trivially verifiable and trivially
|
|
628
|
+
reversible; hunting free holes is neither.
|
|
629
|
+
|
|
630
|
+
Two durable rules:
|
|
631
|
+
|
|
632
|
+
1. **A reserved block is a snapshot, not a lock.** Nothing in Core enforces it. **Re-verify the block
|
|
633
|
+
is still free immediately before the file runs**, not just when it is written, and say so in the
|
|
634
|
+
file header. The longer a seed sits unmerged, the more likely its block is gone.
|
|
635
|
+
2. **Renumbering is cheap; colliding is not.** Because these seeds use literal ids with plain
|
|
636
|
+
`INSERT`, a stale block fails loudly on the primary key — which is the designed behavior and the
|
|
637
|
+
reason `INSERT IGNORE` is banned here (see the 2026-08-24 incident below). Bump to a fresh block
|
|
638
|
+
above MAX rather than trying to reuse the gap.
|
|
639
|
+
|
|
640
|
+
> These explicit ids are **correct and required**, not a portability bug. `Core.Surfaces` /
|
|
641
|
+
> `Core.SurfaceElements` / `Core.Messages` are reserved-literal tables precisely so a `Client_*`
|
|
642
|
+
> override file can inline them without a cross-cluster `Core.*` read. Do not "fix" a surface seed
|
|
643
|
+
> into a `messageKey`-resolving `INSERT … SELECT`: the client-side references have no way to resolve
|
|
644
|
+
> a Core id at runtime.
|
|
645
|
+
|
|
608
646
|
**⚠ The reserved blocks are not an inventory of every surface id — pre-2026-08-21 surfaces sit
|
|
609
647
|
outside them.** The reserved-id discipline begins at the reseed, so surfaces seeded before it took
|
|
610
648
|
plain `AUTO_INCREMENT` ids: the **`navigation`** TAB_STRIP is `Core.SurfaceElements` **109–114**
|
|
@@ -978,6 +1016,15 @@ hits `Duplicate entry '251' for key 'messages.PRIMARY'`. The resolution that kee
|
|
|
978
1016
|
`config.valueKey` when it lives in the page.
|
|
979
1017
|
|
|
980
1018
|
## Change history
|
|
1019
|
+
- 2026-08-31 — **Renumbered the transfer-order reserved blocks** to `Messages` **261–284**,
|
|
1020
|
+
`Surfaces` **53–57**, `SurfaceElements` **190–206**: the 2026-08-28 blocks were reserved from a
|
|
1021
|
+
snapshot and `sales-order-denial-details` claimed `Surfaces` 47 / `SurfaceElements` 170–176 /
|
|
1022
|
+
`Messages` 227–232 first. New rule — **a reserved block is a snapshot, not a lock; re-verify it is
|
|
1023
|
+
free immediately before the file RUNS, not just when it is written**, and bump to a contiguous
|
|
1024
|
+
block above MAX (leaving gaps) rather than back-filling. Also recorded explicitly that literal
|
|
1025
|
+
Core ids in a surface seed are **required, not a portability bug**, so nobody "fixes" one into a
|
|
1026
|
+
`messageKey`-resolving `INSERT … SELECT`. From `Core/2026-08-28a`/`b`, committed in `dbchanges2`
|
|
1027
|
+
`1c3fe2e`. (apeterson)
|
|
981
1028
|
- 2026-08-28 — **Extended the transfer-order reserved blocks** to `Messages` **227–260**, `Surfaces`
|
|
982
1029
|
**47–52**, `SurfaceElements` **170–189** (`Core/2026-08-28b`, the `transfer-orders-list-actions`
|
|
983
1030
|
BUTTON_BAR; all ids verified free on local **and** production first). Three authoring rules added:
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Targeting the right database (tenant restores, and running a change file against the wrong DB)"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: dbchanges2
|
|
5
|
+
project: Database Changes
|
|
6
|
+
client: shared
|
|
7
|
+
type: workflow
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-08-31
|
|
10
|
+
owners: [apeterson]
|
|
11
|
+
files:
|
|
12
|
+
- api2/Config/production.ini
|
|
13
|
+
- dbchanges2/Core/2026-08-28a - TransferOrderSurfaceSeed.sql
|
|
14
|
+
related:
|
|
15
|
+
- ../architecture.md
|
|
16
|
+
- ./verifying-a-migration-ran.md
|
|
17
|
+
- ../features/surface-layer-schema.md
|
|
18
|
+
- ../../../clients/nychh/profile.md
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Summary
|
|
22
|
+
|
|
23
|
+
Two ways to point at the wrong database — a dump taken from the wrong host, and a change file piped
|
|
24
|
+
into the wrong schema. **Both "succeed."** One of them silently overwrites a shared environment with
|
|
25
|
+
your laptop's data; the other fails with an error that names the wrong problem. This is the
|
|
26
|
+
procedure that catches each before it costs a day.
|
|
27
|
+
|
|
28
|
+
## Where the databases are
|
|
29
|
+
|
|
30
|
+
Connection **locations** only — passwords live in the config file, never here.
|
|
31
|
+
|
|
32
|
+
| Cluster | Host | Config section (`api2/Config/production.ini`) |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Core | `reader1.core.database.togahub.com` | `[database]` |
|
|
35
|
+
| Client (tenant) — read | `reader1.client.database.togahub.com` | `[databaseClient]` |
|
|
36
|
+
| Client (tenant) — write | `writer.client.database.togahub.com` | `[databaseClient]` |
|
|
37
|
+
| Logs | — | `[databaseLogs]` |
|
|
38
|
+
| Client sandbox | `client.sandbox.database.togahub.com` | (sandbox config) |
|
|
39
|
+
|
|
40
|
+
Username is `admin`. **The password is in the matching section of
|
|
41
|
+
`api2/Config/production.ini` — read it from there; never paste it anywhere, including here.**
|
|
42
|
+
|
|
43
|
+
**Keep the password out of your shell history and out of `ps`** by building a throwaway
|
|
44
|
+
credentials file from the ini instead of passing `-p` on the command line:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# awk the password out of the [databaseClient] section into a 0600 defaults file
|
|
48
|
+
umask 077
|
|
49
|
+
awk '/^\[databaseClient\]/{s=1} s&&/^password/{print; exit}' Config/production.ini \
|
|
50
|
+
| sed 's/ *= */=/' > /tmp/my.cnf.$$
|
|
51
|
+
printf '[client]\nhost=reader1.client.database.togahub.com\nuser=admin\n' | cat - /tmp/my.cnf.$$ > /tmp/creds.cnf
|
|
52
|
+
mysql --defaults-extra-file=/tmp/creds.cnf Client_Nychh -e 'SELECT COUNT(*) FROM TransferOrders'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Delete the file when you are done.
|
|
56
|
+
|
|
57
|
+
## Steps — before you trust ANY tenant dump
|
|
58
|
+
|
|
59
|
+
1. **Prove which environment you actually reached, with a row count known to DIFFER between
|
|
60
|
+
environments.** Not a table list, not a schema diff — a *count*. Worked example (2026-08-31):
|
|
61
|
+
`Client_Nychh.TransferOrders` = **1,927 rows in production** and **24 `TO-SEED-*` rows locally**.
|
|
62
|
+
One `SELECT COUNT(*)` settles it.
|
|
63
|
+
2. **Re-check the host you typed.** `-h127.0.0.1` / `-hlocalhost` in a command you believed was
|
|
64
|
+
pointed at prod is the whole failure mode; a defaults file (above) removes the chance to fumble it.
|
|
65
|
+
3. **Restore.** Drop + restore will run perfectly whatever the source was — success proves nothing.
|
|
66
|
+
4. **Verify the restored copy against the SOURCE you intended**, repeating step 1 against the
|
|
67
|
+
restored schema.
|
|
68
|
+
|
|
69
|
+
## Gotchas
|
|
70
|
+
|
|
71
|
+
- 🚨 **A wrong-source restore is silent and total.** A dump taken from `127.0.0.1`, believed to be
|
|
72
|
+
production, was restored onto sandbox-client `Client_Nychh`. Both the drop and the restore
|
|
73
|
+
succeeded; sandbox simply became a copy of the developer's laptop, carrying local `TO-SEED-*`
|
|
74
|
+
transfer orders into a shared environment. **The tell was that sandbox matched local EXACTLY** —
|
|
75
|
+
353 tables, identical row counts. An exact match with your local DB is evidence of a bad restore,
|
|
76
|
+
not of a good one.
|
|
77
|
+
- **A small post-restore drift vs. live prod is EXPECTED, not an error.** `--single-transaction`
|
|
78
|
+
makes the dump a point-in-time snapshot, so a live table keeps moving after it: `Units` read 8,627
|
|
79
|
+
on prod and 8,618 on the restored sandbox. Do not chase that; only a *category* difference
|
|
80
|
+
(missing tables, order-of-magnitude counts) means the restore was wrong.
|
|
81
|
+
- 🚨 **The `dbchanges2` folder name IS the target database — pipe each folder to its own DB.**
|
|
82
|
+
`Core/` → `Core`, `Client_<Name>/` → that one tenant, `Client/` → **every** tenant. Files reference
|
|
83
|
+
tables **unqualified** by design (the cluster-isolation rule), so running a `Core/` file against a
|
|
84
|
+
client DB does not complain about the database — it fails as
|
|
85
|
+
`Table 'Client_Nychh.Messages' doesn't exist`, which names a missing table rather than the real
|
|
86
|
+
mistake. If you see "table doesn't exist" for a table you know is in Core, check which schema you
|
|
87
|
+
piped the file into before you touch the SQL.
|
|
88
|
+
- **The repo is evidence of intent, never of what an environment contains.** Confirming a file ran
|
|
89
|
+
is its own procedure — see
|
|
90
|
+
[Verifying whether a migration actually ran](./verifying-a-migration-ran.md).
|
|
91
|
+
|
|
92
|
+
## Change history
|
|
93
|
+
- 2026-08-31 — Initial. Captured after a dump taken from `127.0.0.1` was restored onto sandbox-client
|
|
94
|
+
`Client_Nychh` believing it was production (the tell: sandbox matched local exactly). Records the
|
|
95
|
+
verify-by-differing-row-count check, the `--single-transaction` snapshot-drift caveat, the
|
|
96
|
+
prod/sandbox cluster hostnames + which config section holds each password, the
|
|
97
|
+
`--defaults-extra-file` recipe that keeps the password out of history and `ps`, and the
|
|
98
|
+
folder-name-is-the-target-database rule with its misleading "table doesn't exist" error.
|
|
99
|
+
(apeterson)
|
|
@@ -6,7 +6,7 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: [jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/.github/workflows/publish.yml
|
|
@@ -85,6 +85,14 @@ environment needs its own maintained `_<mode>` branch in `toga-blox-npm`.
|
|
|
85
85
|
|
|
86
86
|
## Gotchas
|
|
87
87
|
|
|
88
|
+
- **⚠ There is a STALE UNDERSCORED dist-tag on the registry — `_sandbox-client` (with the
|
|
89
|
+
underscore) is NOT the channel.** Verified 2026-08-31, the registry carries **both**
|
|
90
|
+
`_sandbox-client` = `1.0.316-sandbox-client.1` (vestigial, predates the current pipeline) **and**
|
|
91
|
+
`sandbox-client` = `1.1.1-sandbox-client.140` (the live channel head). Branch `_<mode>` maps to
|
|
92
|
+
channel `<mode>` with the underscore **stripped** (same convention as `amplify.yml`), so the
|
|
93
|
+
**branch** `_sandbox-client` tracks the **tag** `sandbox-client`. Asking npm for "the latest
|
|
94
|
+
`_sandbox-client` tag" literally installs something **139 publishes behind**. Always resolve the
|
|
95
|
+
head with `npm view @agilant/toga-blox dist-tags` and install the underscore-less name.
|
|
88
96
|
- **Publish before build.** `npm install @agilant/toga-blox@<mode>` fails `ETARGET No matching
|
|
89
97
|
version` in the app build until this repo has published that channel. GitHub Actions runs the
|
|
90
98
|
workflow version *on the pushed branch*, so the branch must carry the current `publish.yml`.
|
|
@@ -192,6 +200,10 @@ environment needs its own maintained `_<mode>` branch in `toga-blox-npm`.
|
|
|
192
200
|
you push to a release branch.
|
|
193
201
|
|
|
194
202
|
## Change history
|
|
203
|
+
- 2026-08-31 — Recorded the **stale underscored dist-tag trap**: the registry carries both
|
|
204
|
+
`_sandbox-client` (`1.0.316-sandbox-client.1`, vestigial) and `sandbox-client`
|
|
205
|
+
(`1.1.1-sandbox-client.140`, live head). Installing the underscored name silently pins 139
|
|
206
|
+
publishes back. (apeterson)
|
|
195
207
|
- 2026-08-27 — Released blox **1.1.0** on `_production` (published `1.1.1-production.139`) and fixed
|
|
196
208
|
a shipped-broken tarball: the `feature-new-table` → `_production` merge left `global.css` at the
|
|
197
209
|
repo root with `import "../global.css"`, so the emitted `dist/index.js` referenced a path outside
|
|
@@ -6,7 +6,7 @@ project: TOGa Blox
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: [tcox, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga-blox/package.json
|
|
@@ -88,6 +88,29 @@ dedupe: [
|
|
|
88
88
|
- **This only reproduces on machines using the local checkout**, so across a team it reads as an
|
|
89
89
|
*intermittent* bug that "works for everyone else." A `QueryClient`/context error with the provider
|
|
90
90
|
visibly mounted is almost always this.
|
|
91
|
+
- **Version parity is a red herring — module IDENTITY is what matters.** In the reproduction both
|
|
92
|
+
copies were `@tanstack/react-query` **5.101.4**. Identical versions still mean two physical
|
|
93
|
+
directories, two module instances, and two React contexts. Do not clear the theory because
|
|
94
|
+
`npm ls` shows one version.
|
|
95
|
+
- **`dedupe` alone was not sufficient here — alias too.** Follow-up 2026-08-31: pin every
|
|
96
|
+
**context-bearing** peer with **both** `resolve.alias` (absolute path into the app's
|
|
97
|
+
`node_modules`, the same treatment `react`/`react-dom` already had) **and** `resolve.dedupe`:
|
|
98
|
+
`@tanstack/react-query`, `@tanstack/react-query-persist-client`, `@tanstack/react-table`,
|
|
99
|
+
`react-hook-form`. `react-hook-form` was nested under blox as well — the same latent bug for
|
|
100
|
+
`BaseInput` / `useFormContext`, just not throwing yet.
|
|
101
|
+
- **`optimizeDeps.include` is why the throw appears to come from inside blox.** With blox in
|
|
102
|
+
`optimizeDeps.include`, esbuild pre-bundles blox **together with its nested copy**, so the error
|
|
103
|
+
surfaces from `@agilant_toga-blox.js` rather than from app code, and the `?v=<hash>` in the error
|
|
104
|
+
URL is the pre-bundle cache key (which is why a config change with a stale
|
|
105
|
+
`node_modules/.vite` looks like it did nothing).
|
|
106
|
+
- **Verification technique — prove there is exactly one copy.** After rebuilding deps, confirm that
|
|
107
|
+
**exactly one** chunk under `node_modules/.vite/deps` contains the string `No QueryClient set`,
|
|
108
|
+
then confirm `@agilant_toga-blox.js`, `@tanstack_react-query.js` and
|
|
109
|
+
`@tanstack_react-query-persist-client.js` all import **that same** chunk. Before the fix blox
|
|
110
|
+
imported its own.
|
|
111
|
+
- **Installing blox from the registry replaces the symlink with a real directory**, which removes
|
|
112
|
+
this root cause outright — the alias/dedupe belt-and-braces is what keeps the *linked* dev loop
|
|
113
|
+
safe.
|
|
91
114
|
|
|
92
115
|
### 3. Disable blox's PostCSS config while linked
|
|
93
116
|
|
|
@@ -119,6 +142,14 @@ The consumer reads blox's `dist`, so rebuild blox after every change.
|
|
|
119
142
|
drags every component's CSS into the consumer bundle.
|
|
120
143
|
|
|
121
144
|
## Change history
|
|
145
|
+
- 2026-08-31 — Closed out the duplicate-`react-query` case with the parts that made it hard:
|
|
146
|
+
**both copies were the SAME version (5.101.4)** — identity, not version, is what breaks context;
|
|
147
|
+
`optimizeDeps.include` pre-bundles blox with its nested copy so the throw appears to originate
|
|
148
|
+
inside `@agilant_toga-blox.js`; the fix needs `resolve.alias` (absolute path) **as well as**
|
|
149
|
+
`resolve.dedupe` for every context-bearing peer (react-query, react-query-persist-client,
|
|
150
|
+
react-table, **react-hook-form** — also nested, same latent bug for `BaseInput`); and a concrete
|
|
151
|
+
one-copy verification (exactly one `.vite/deps` chunk containing `No QueryClient set`, imported by
|
|
152
|
+
blox and both react-query chunks). (apeterson)
|
|
122
153
|
- 2026-08-28 — Added the Vite half of the one-copy rule: **every peer carrying a React context must
|
|
123
154
|
be in `resolve.dedupe`**, not just React. `toga25-supply` threw "No QueryClient set" **with
|
|
124
155
|
`QueryClientProvider` visibly in the stack** because `@tanstack/react-query` was missing from
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [TOGa 2.5 Supply — Architecture](architecture.md) | `toga25-supply` ("TOGa 2.5 Supply") is the **React/TypeScript frontend** for the 2.0 Supply application — an iteration and improvement of `toga2-supply`. | toga25-supply/src/main.tsx, toga25-supply/src/App.tsx, toga25-supply/src/routes.tsx, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/fieldsConfig/useClientFields.ts, toga25-supply/src/layout/, toga25-supply/src/pages/, toga25-supply/src/hooks/useTableCellInteractions.ts |
|
|
6
|
-
| [Action-Button Rule Engine (Flag / Rule grammar)](features/action-button-rule-engine.md) | A declarative, fully config-driven rule engine that resolves the boolean-ish flags (`isEnabled`, `isVisible`, `isComplete`) on SalesOrder action-button options. | toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.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/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
|
|
6
|
+
| [Action-Button Rule Engine (Flag / Rule grammar)](features/action-button-rule-engine.md) | A declarative, fully config-driven rule engine that resolves the boolean-ish flags (`isEnabled`, `isVisible`, `isComplete`) on SalesOrder action-button options. | toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts, toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.test.ts, toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts, toga25-supply/src/layout/RecordApprovalModal/view/ApprovalFlowDetailInputs.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/FIELDS/COMPASS/approvalActionFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/FIELDS/COMPASSCANADA/approvalActionFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx |
|
|
7
7
|
| [Client API-Fetch Fields (useClientApiFields / apiFields.json)](features/client-api-fetch-fields.md) | The mechanism that resolves a client's **API-fetch projection** — which `fields` / `ojoin` / `join` / `where` to request from the TOGa API for a given page — fr | toga25-supply/src/fieldsConfig/resolveApiConfig.ts, toga25-supply/src/fieldsConfig/useClientApiFields.ts, toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/apiFields.json, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/useItemRecordModalViewModel.tsx |
|
|
8
8
|
| [Client-Configurable Fields (useClientFields / fieldsConfig)](features/client-configurable-fields.md) | The mechanism for config that **varies by client** (or client × role) — field overrides, filter buttons, group-by options, column pickers, layout toggles — with | toga25-supply/src/fieldsConfig/index.ts, toga25-supply/src/fieldsConfig/useClientFields.ts, toga25-supply/src/pages/SalesOrders/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/viewModel/FIELDS/, toga25-supply/src/pages/Inventory/README.md, toga25-supply/src/layout/ItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/layout/VendorItemRecordModalLayout/viewModel/FIELDS/, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/view/sections/SalesOrderTopBar.tsx, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/FIELDS/ |
|
|
9
9
|
| [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/surface/useColumnVisibilityModalConfig.ts, toga25-supply/src/surface/index.ts, toga25-supply/src/pages/SalesOrders/SalesOrders.tsx, toga25-supply/src/pages/SalesOrders/viewModel/useSalesOrdersPageViewModel.tsx, toga25-supply/src/pages/SalesOrders/hooks/useSalesOrdersTableData.tsx, dbchanges2/Core/2026-08-28b - TransferOrderListActionsSurfaceSeed.sql |
|
|
@@ -6,16 +6,22 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.ts
|
|
13
|
+
- toga25-supply/src/pages/SalesOrders/helpers/evaluateEnableRule.test.ts
|
|
13
14
|
- toga25-supply/src/pages/SalesOrders/helpers/buildPatchedTenantFields.ts
|
|
15
|
+
- toga25-supply/src/layout/RecordApprovalModal/view/ApprovalFlowDetailInputs.tsx
|
|
16
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/FIELDS/COMPASS/approvalActionFields.json
|
|
17
|
+
- toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/FIELDS/COMPASSCANADA/approvalActionFields.json
|
|
14
18
|
- toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/viewModel/useSalesOrderRecordModalLayoutModel.tsx
|
|
15
19
|
- toga25-supply/src/pages/SalesOrders/view/SalesOrderApprovalModalsLayout/viewModel/useApprovalModalViewModel.tsx
|
|
16
20
|
related:
|
|
17
21
|
- client-configurable-fields.md
|
|
22
|
+
- record-modals-and-nested-tables.md
|
|
18
23
|
- ../architecture.md
|
|
24
|
+
- ../../../clients/compass-usa/features/approval-decision-flow.md
|
|
19
25
|
---
|
|
20
26
|
|
|
21
27
|
## What it is
|
|
@@ -55,8 +61,11 @@ type FieldOp = "eq" | "ne" | "gt" | "gte" | "lt" | "lte"
|
|
|
55
61
|
- **`RuleContext`** is `{ order, currentStage?, stages? }`. `currentStage`/`stages` carry
|
|
56
62
|
approval-workflow state when available.
|
|
57
63
|
- **Named rules** (`{ type }`) are bespoke leaf checks that a field predicate cannot express
|
|
58
|
-
— they live in the `NAMED_RULES` registry.
|
|
59
|
-
|
|
64
|
+
— they live in the `NAMED_RULES` registry. Members (both walk the approval stages, which a
|
|
65
|
+
field predicate cannot express):
|
|
66
|
+
- `stepTwoAssigned` — a step-2 assignee exists.
|
|
67
|
+
- `stepOneUndecided` — step 1 carries **no** decision yet.
|
|
68
|
+
An unknown `type` resolves to `true`.
|
|
60
69
|
|
|
61
70
|
### Resolution primitives
|
|
62
71
|
|
|
@@ -78,6 +87,25 @@ type FieldOp = "eq" | "ne" | "gt" | "gte" | "lt" | "lte"
|
|
|
78
87
|
It returns a new `tenantFields` with a patched `recordActionFields.actionOptions`. Callers:
|
|
79
88
|
`useSalesOrderRecordModalLayoutModel.tsx` and `useApprovalModalViewModel.tsx`.
|
|
80
89
|
|
|
90
|
+
### It also resolves flags on approval-workflow STAGE INPUTS
|
|
91
|
+
|
|
92
|
+
`buildPatchedTenantFields` additionally walks
|
|
93
|
+
`recordActionFields.approvalWorkflow.stages[].inputs[]` and resolves each input's flags the same
|
|
94
|
+
way. This placement is deliberate and is the layering rule to preserve: **rule evaluation stays in
|
|
95
|
+
the `pages/SalesOrders` layer, so the shared `layout/RecordApprovalModal` receives only plain
|
|
96
|
+
booleans and holds no tenant logic.** A shared-layout component must never import the rule engine
|
|
97
|
+
or read tenant config; if it needs a new gate, resolve the flag here and pass it down.
|
|
98
|
+
|
|
99
|
+
`ApprovalFlowDetailInputs.tsx` consumes one such resolved boolean: a resolved `isEnabled === true`
|
|
100
|
+
on a stage's `assignedTo` input **overrides the decided-stage lock** —
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
isAssignedToDisabled = areInputsDisabled && !isAssignedToUnlocked
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The note textarea and the approve/deny controls stay locked regardless, so a recorded decision can
|
|
107
|
+
never be flipped — only the assignee is reopened.
|
|
108
|
+
|
|
81
109
|
### Back-compat shim
|
|
82
110
|
|
|
83
111
|
The previous single-purpose `EnableRule` grammar (`alwaysEnabled`, `requireStepTwoAssigned`,
|
|
@@ -103,6 +131,14 @@ Retire the shim once all configs express flags as `Rule`.
|
|
|
103
131
|
|
|
104
132
|
## Gotchas
|
|
105
133
|
|
|
134
|
+
- **⚠ Both Compass `approvalActionFields.json` files repeat the heading string
|
|
135
|
+
`"Approval: Manager Approval"` on step 1 AND step 2** (in the `MANAGER` role block). A
|
|
136
|
+
text/heading-anchored edit therefore silently patches the **wrong stage**. Target stages by
|
|
137
|
+
**structure** (`stage.valueKey === "2"`) and assert placement afterwards.
|
|
138
|
+
- **A tenant opts into a stage-input gate by config only.** Adding
|
|
139
|
+
`"isEnabled": { "type": "stepOneUndecided" }` to a step-2 `assignedTo` input changes that tenant
|
|
140
|
+
and nothing else: tenants that omit the key hit `resolveFlag`'s `false` fallback and keep their
|
|
141
|
+
existing behaviour exactly (DEFAULT and QUAD were left untouched for this reason).
|
|
106
142
|
- **`recordActionFields` is the current key** (renamed from `approvalActionFields` /
|
|
107
143
|
`orderViewFields`-era naming). `buildPatchedTenantFields` reads
|
|
108
144
|
`recordActionFields.actionOptions`; missing config falls back to `[]`.
|
|
@@ -116,4 +152,17 @@ Retire the shim once all configs express flags as `Rule`.
|
|
|
116
152
|
default.
|
|
117
153
|
|
|
118
154
|
## Change history
|
|
155
|
+
- 2026-08-31 — **Added named rule `stepOneUndecided` and extended `buildPatchedTenantFields` to
|
|
156
|
+
resolve flags on `approvalWorkflow.stages[].inputs[]`**, so a VIP-pre-approved step-2 manager can
|
|
157
|
+
be reassigned (Compass USA + Compass Canada). `ApprovalFlowDetailInputs` now treats a resolved
|
|
158
|
+
`isEnabled === true` on a stage's `assignedTo` as an override of the decided-stage lock while the
|
|
159
|
+
note and approve/deny controls stay locked. Both tenants opt in via config only (step-2
|
|
160
|
+
`assignedTo`, ADMIN + MANAGER blocks); DEFAULT and QUAD unchanged. **The rule deliberately does
|
|
161
|
+
NOT key on `isPreApproved`** — blox derives that solely from
|
|
162
|
+
`!!stage.ApprovalDecisionTypes.name`, which is NULL on precisely the broken orders (see
|
|
163
|
+
[Compass Approval-Decision Flow](../../../clients/compass-usa/features/approval-decision-flow.md));
|
|
164
|
+
"step 1 undecided" is the safe equivalent because an order cannot legitimately be past step 2
|
|
165
|
+
while step 1 is pending. Recorded the duplicate-heading trap in both Compass config files and the
|
|
166
|
+
keep-rules-out-of-`RecordApprovalModal` layering rule. Verified: `tsc -b` clean, 44/44 vitest
|
|
167
|
+
(10 new in `evaluateEnableRule.test.ts`), `vite build --mode sandbox-client` clean. (apeterson)
|
|
119
168
|
- 2026-06-25 — Documented the generic Flag/Rule engine: combinators, field predicates, named-rule registry, `resolveFlag`/`buildPatchedTenantFields`, and the legacy `EnableRule` back-compat shim. (apeterson)
|
|
@@ -238,6 +238,16 @@ Scaffold per the `ItemRecordModalLayout` pattern (`src/layout/ItemRecordModalLay
|
|
|
238
238
|
join is not feasible for a list, so that column needs the backend field (promote `_purchaseOrders`
|
|
239
239
|
to base `_Model_Client_SalesOrder`) or be hidden for Quad. **Not addressed** — open follow-up.
|
|
240
240
|
|
|
241
|
+
- **A decided stage's `assignedTo` can be selectively UNLOCKED — and the emitted PUT must stay
|
|
242
|
+
assignee-only.** `ApprovalFlowDetailInputs` unlocks just the assignee when the stage input's
|
|
243
|
+
resolved `isEnabled` is `true` (Compass USA/Canada step 2; see
|
|
244
|
+
[Action-Button Rule Engine](action-button-rule-engine.md)), leaving note + approve/deny locked.
|
|
245
|
+
The payload consequence is load-bearing: the request carries **only `assignedToUserId` and never
|
|
246
|
+
`isApproved`**, because `_Model_Compass_ApprovalDecision::_evaluateManagerVip` **early-returns
|
|
247
|
+
when `isset($api->httpPayload->isApproved)`**. Keeping `isApproved` out is what lets `postPut` →
|
|
248
|
+
`handleManagerReassignment` run and revoke a VIP auto-approval for a non-VIP / over-limit new
|
|
249
|
+
manager. Adding `isApproved` "for completeness" would silently disable that backend rule.
|
|
250
|
+
|
|
241
251
|
- **ONE `TableRecordModal` must own every state — loading, error and loaded.** Rendering a second
|
|
242
252
|
`TableRecordModal` for the loaded state puts a **different element type at the same position in the
|
|
243
253
|
tree**, so React unmounts the first and mounts the second: the CSS keyframe restarts and the user
|
|
@@ -252,6 +262,9 @@ Scaffold per the `ItemRecordModalLayout` pattern (`src/layout/ItemRecordModalLay
|
|
|
252
262
|
[Transfer Orders page](transfer-orders-page.md).
|
|
253
263
|
|
|
254
264
|
## Change history
|
|
265
|
+
- 2026-08-31 — Recorded the **selectively-unlocked `assignedTo` on a decided approval stage** and
|
|
266
|
+
the rule that its PUT must remain assignee-only (`isApproved` present makes
|
|
267
|
+
`_evaluateManagerVip` early-return, killing the reassignment revoke path). (apeterson)
|
|
255
268
|
- 2026-08-28 — Added two modal rules confirmed on the Transfer Orders build: **one
|
|
256
269
|
`TableRecordModal` owns loading/error/loaded** (a second one at the same tree position swaps
|
|
257
270
|
element types and replays the slide-up animation), and a record whose children are **not** declared
|
|
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: [apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/pages/TransferOrders/TransferOrders.tsx
|
|
@@ -115,9 +115,22 @@ bind to whichever Locations join happened to be first.
|
|
|
115
115
|
|
|
116
116
|
## The Core surface vertical
|
|
117
117
|
|
|
118
|
-
Reserved id blocks
|
|
119
|
-
**`Messages`
|
|
120
|
-
`INSERT` — never `INSERT IGNORE`.
|
|
118
|
+
Reserved id blocks, **as committed** (`dbchanges2` `1c3fe2e`, 2026-08-31): `Core/2026-08-28a` +
|
|
119
|
+
`2026-08-28b` together claim **`Messages` 261–284, `Surfaces` 53–57, `SurfaceElements` 190–206**, all
|
|
120
|
+
literal ids with plain `INSERT` — never `INSERT IGNORE`.
|
|
121
|
+
|
|
122
|
+
⚠ **These are NOT the 227–250 / 47–51 / 170–186 blocks the first draft reserved.** Those were taken
|
|
123
|
+
from a 2026-08-28 prod snapshot and `sales-order-denial-details` shipped into them first
|
|
124
|
+
(`Surfaces` 47, `SurfaceElements` 170–176, `Messages` 227–232). Re-verified on sandbox-client
|
|
125
|
+
(freshly reset from prod) 2026-08-31 — MAX was `Surfaces` 52 / `SurfaceElements` 189 / `Messages`
|
|
126
|
+
260 — and renumbered to a contiguous block above MAX, leaving the old range as unused gaps. Any
|
|
127
|
+
surface/element id quoted below is from the original draft numbering; **read the file, not this
|
|
128
|
+
table, for the live id**. Re-verify the block is free again immediately before running. See
|
|
129
|
+
[Surface Layer Schema](../../dbchanges2/features/surface-layer-schema.md).
|
|
130
|
+
|
|
131
|
+
**Run order: `Core/` → `Client/` (fan-out) → `Client_Nychh/`.** The client files reference Core
|
|
132
|
+
record ids, and the NYCHH TableView + ACL depend on columns the fan-out files add. Within `Client/`,
|
|
133
|
+
the stages table must precede the file that adds the stage-id FK column.
|
|
121
134
|
|
|
122
135
|
| Surface | id | Type | Notes |
|
|
123
136
|
|---|---|---|---|
|
|
@@ -359,6 +372,12 @@ visible in Core (neutral default; other tenants populate `TransferOrders.purchas
|
|
|
359
372
|
earlier migration without names, so a name-based lookup on them finds nothing.
|
|
360
373
|
|
|
361
374
|
## Change history
|
|
375
|
+
- 2026-08-31 — **Committed the seven-file migration set** (`dbchanges2` `1c3fe2e`, branch
|
|
376
|
+
`TRUE-80852`, pushed) and **renumbered the Core reserved blocks** to `Messages` 261–284 /
|
|
377
|
+
`Surfaces` 53–57 / `SurfaceElements` 190–206 — the 2026-08-28 blocks had been claimed by
|
|
378
|
+
`sales-order-denial-details` before this shipped. Recorded the **`Core/` → `Client/` →
|
|
379
|
+
`Client_Nychh/` run order**. Also corrected the prod picture: `Client_Nychh.TransferOrders` holds
|
|
380
|
+
**1,927 production rows** — only the UI vertical is new, the data is long-live. (apeterson)
|
|
362
381
|
- 2026-08-28 — Second pass, same day. Discovered the vertical sits on **four untracked layers** of
|
|
363
382
|
prod/sandbox state (record + field ACL for records 312/313 and their logic-group chain, plus the
|
|
364
383
|
`TransferOrderItems.dtCreated/dtUpdated` columns) — a fresh environment cannot reproduce
|
|
@@ -6,7 +6,7 @@ project: TOGa 2.5 Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: [jcardinal, apeterson]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/amplify.yml
|
|
@@ -17,6 +17,7 @@ files:
|
|
|
17
17
|
- toga25-supply/.env.development
|
|
18
18
|
related:
|
|
19
19
|
- ../architecture.md
|
|
20
|
+
- ../../toga-blox/workflows/dynamic-publish-pipeline.md
|
|
20
21
|
- ../features/transfer-orders-page.md
|
|
21
22
|
- ../../toga-blox/workflows/local-link-into-a-consumer-app.md
|
|
22
23
|
---
|
|
@@ -109,6 +110,56 @@ developer override in a **gitignored `.env.development.local`** (Vite loads `.lo
|
|
|
109
110
|
the intended per-developer layer). Until that lands, check `git status` before committing on any
|
|
110
111
|
branch where you pointed the dev server at localhost.
|
|
111
112
|
|
|
113
|
+
## ⚠ The build spec and the `.env` set are NOT reliably present on a branch
|
|
114
|
+
|
|
115
|
+
Verified 2026-08-31 on `_sandbox-client`, and this is the failure mode to check **first** when a
|
|
116
|
+
deploy misbehaves:
|
|
117
|
+
|
|
118
|
+
- **`amplify.yml` existed in exactly ONE commit (`5a276be`) and was absent from the root of EVERY
|
|
119
|
+
remote branch — including `_production`.** So Amplify had been building from a **console-stored**
|
|
120
|
+
build spec, which means the fail-loud `_<mode>` guardrail above was **not actually running**. It
|
|
121
|
+
was recreated on `_sandbox-client`. Note the recreated spec runs **`npx tsc -b` before
|
|
122
|
+
`vite build`**, which the previously documented version did not: **a type error now fails the
|
|
123
|
+
deploy.**
|
|
124
|
+
- **TWO competing `.env` conventions coexist across branches.** The `_<mode>` convention needs
|
|
125
|
+
`sandbox-client`, `sandbox-dev`, `client-*`, `qa-*`, `qc-*`, `production`, `demo`, `stage`. But
|
|
126
|
+
`_sandbox-client`, `_production`, `_beta` and `_main` carry an **OLD** set
|
|
127
|
+
(`.env.alpha` / `.beta` / `.gamma` / `.sprint` / `.test`); only `_stage`, `_sandbox-dev` and the
|
|
128
|
+
snapshot `_sandbox-client-backup820` carry the new 21-mode set. Dry-running the derivation across
|
|
129
|
+
all **27** remote `_*` branches, only **`_beta` and `_stage`** would have built. A "reset from
|
|
130
|
+
prod" is how a branch loses the new set.
|
|
131
|
+
- **The audit — dry-run the guardrail locally before landing the spec.** For every remote `_*`
|
|
132
|
+
branch: `VITE_MODE="${b#_}"; [ -f ".env.$VITE_MODE" ]`. That tells you exactly which branches
|
|
133
|
+
would fail, in seconds, without a deploy.
|
|
134
|
+
- Restoration is a verbatim copy, not a rewrite: `git checkout <known-good-ref> -- .env.<mode> …`
|
|
135
|
+
(19 files restored from `origin/_sandbox-client-backup820`, comments intact). Do **not** overwrite
|
|
136
|
+
files that already exist (`.env`, `.env.development`, `.env.stage` were left alone). Env-file
|
|
137
|
+
format is `VITE_API = 'https://...'` — spaces around `=`, single-quoted — and they contain only
|
|
138
|
+
public base URLs, no secrets.
|
|
139
|
+
- **⚠ Open data issue, needs a human:** on `_sandbox-client`, `.env.stage` points at
|
|
140
|
+
`https://api.dev.sandbox.togahub.com/v2` (**dev-sandbox**) while the backup has
|
|
141
|
+
`https://api.stage.togahub.com/v2`. As-is, `_stage` would deploy pointing at dev-sandbox. Also 4
|
|
142
|
+
orphan old-convention files remain with no matching `_<mode>` branch
|
|
143
|
+
(`.env.alpha`/`.gamma`/`.sprint`/`.test`; `.env.beta` still has one).
|
|
144
|
+
|
|
145
|
+
## Deleting a remote branch silently breaks its Amplify deploy trigger
|
|
146
|
+
|
|
147
|
+
An Amplify branch is **its own resource with its own webhook association** — it is **not** inferred
|
|
148
|
+
from the repo. Deleting the git branch either deletes the Amplify branch resource (when automatic
|
|
149
|
+
branch deletion is enabled) or leaves it with a stale association. Either way **re-creating the git
|
|
150
|
+
branch does NOT reconnect it**: pushes land in GitHub and Amplify ignores them entirely.
|
|
151
|
+
|
|
152
|
+
- **A missing `amplify.yml` is NOT the cause of a missing trigger** — a build would still start and
|
|
153
|
+
fall back to the console spec. Don't conflate the two.
|
|
154
|
+
- **Diagnose:** GitHub → *Settings → Webhooks* → the Amplify webhook → **Recent Deliveries**. A
|
|
155
|
+
delivery with `200` and no Amplify job = the branch is not connected; **no delivery at all** = the
|
|
156
|
+
webhook is gone. Or `aws amplify list-apps` then `aws amplify list-branches --app-id <id>`.
|
|
157
|
+
- **Fix:** reconnect the branch in the Amplify console **and re-add its domain association** — a
|
|
158
|
+
deleted branch loses its subdomain mapping, so the build can go **green while the URL still
|
|
159
|
+
404s**.
|
|
160
|
+
- Amplify also **dedupes by commit SHA**, so a recreated branch pointing at an already-built SHA is
|
|
161
|
+
**skipped** — push a new commit to force a build.
|
|
162
|
+
|
|
112
163
|
## Adding a new environment
|
|
113
164
|
|
|
114
165
|
1. Create `.env.<mode>` with the public `VITE_API` base URL.
|
|
@@ -144,12 +195,45 @@ branch where you pointed the dev server at localhost.
|
|
|
144
195
|
(the legacy v7 table stack). All three `react-table` v7 packages were **already** declared at
|
|
145
196
|
identical versions on `_sandbox-client` — `_production` was the outlier. Fixed in `92644c8`
|
|
146
197
|
(pushed), verified with a clean vite build (1761 modules).
|
|
198
|
+
- **⚠ The missing-blox-declaration defect RECURS per branch — it is not fixed once.** Verified
|
|
199
|
+
2026-08-31: `_sandbox-client` **also** had no `@agilant/toga-blox` dependency entry and no lock
|
|
200
|
+
entry despite ~200 imports, exactly as `_production` had (fixed there in `92644c8`). The symlink
|
|
201
|
+
is what hides it, so **re-run the audit on every branch you deploy**. Fixed by installing the
|
|
202
|
+
**exact** pin `1.1.1-sandbox-client.140` (never a caret — some published versions ship an empty
|
|
203
|
+
`dist`). That install surfaced one more masked peer: **`react-day-picker`** (blox peer `^9.5.1`)
|
|
204
|
+
was neither declared nor present in the app's own `node_modules`; added at `^9.14.0`. All 19 blox
|
|
205
|
+
peers are now declared. Installing from the registry also replaced the symlink with a real
|
|
206
|
+
directory, which independently removed the duplicate-`@tanstack/react-query` root cause (see
|
|
207
|
+
[local-link-into-a-consumer-app](../../toga-blox/workflows/local-link-into-a-consumer-app.md)).
|
|
208
|
+
Verified after install: `tsc -b` clean, 44/44 tests, `vite build --mode sandbox-client` clean
|
|
209
|
+
(1773 modules), exactly one react-query copy.
|
|
210
|
+
- **⚠ Latent and NOT fixed — `framer-motion` peer mismatch.** The app declares `^12.35.0`; blox's
|
|
211
|
+
peer range is `^11.0.8`; `legacy-peer-deps=true` silenced the warning, so blox installed a
|
|
212
|
+
**nested framer-motion v11**. framer-motion provides React context (`AnimatePresence`,
|
|
213
|
+
`MotionConfig`) — i.e. the **same duplicate-instance bug class**, just not throwing today. Fix
|
|
214
|
+
direction: widen blox's peer to `^11 || ^12`, **or** add `framer-motion` to the app's
|
|
215
|
+
dedupe/alias list (which forces blox onto v12 — needs testing).
|
|
147
216
|
- **The audit that catches the above — reusable for any blox consumer.** Enumerate the shared lib's
|
|
148
217
|
`peerDependencies` from `node_modules/<pkg>/package.json`, then check each one for a **real
|
|
149
218
|
directory** (not a symlink passthrough) in the *app's* `node_modules`. Do this whenever a repo
|
|
150
219
|
has ever been developed against a linked local checkout.
|
|
151
220
|
|
|
152
221
|
## Change history
|
|
222
|
+
- 2026-08-31 — Restored the branch's deploy inputs and recorded three systemic findings.
|
|
223
|
+
(1) **`amplify.yml` was absent from the root of every remote branch** (it lived in a single commit
|
|
224
|
+
`5a276be`), so Amplify had been building from a **console-stored spec** and the fail-loud
|
|
225
|
+
`_<mode>` guardrail was never actually running; recreated it, and the new spec adds `npx tsc -b`
|
|
226
|
+
before `vite build` so a type error now fails the deploy. (2) **Two competing `.env` conventions
|
|
227
|
+
coexist across branches** — a dry-run of the derivation across all 27 remote `_*` branches showed
|
|
228
|
+
only `_beta` and `_stage` would build; restored 19 `.env.<mode>` files verbatim from
|
|
229
|
+
`origin/_sandbox-client-backup820`, and recorded the local dry-run audit. (3) **Deleting a remote
|
|
230
|
+
branch silently breaks its Amplify trigger** — the branch resource/webhook association is not
|
|
231
|
+
inferred from the repo, re-creating the git branch does not reconnect it, the domain association
|
|
232
|
+
must be re-added (green build, 404 URL), and Amplify dedupes by commit SHA. Also: the
|
|
233
|
+
missing-`@agilant/toga-blox`-declaration defect **recurred** on `_sandbox-client` (fixed with the
|
|
234
|
+
exact pin `1.1.1-sandbox-client.140`, plus the masked peer `react-day-picker`), and logged the
|
|
235
|
+
**latent, unfixed `framer-motion` v11-vs-v12 duplicate-context risk**. Flagged an open data issue:
|
|
236
|
+
`_sandbox-client`'s `.env.stage` points at dev-sandbox. (apeterson)
|
|
153
237
|
- 2026-08-28 — Recorded that **`npm run dev` does not point at a local API**: mode `development`
|
|
154
238
|
loads the tracked `.env.development`, which ships
|
|
155
239
|
`VITE_API = https://api.dev.sandbox.togahub.com/v2` — so "dev" reads/writes dev-sandbox data. The
|
package/knowledge/INDEX.md
CHANGED
|
@@ -21,7 +21,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
21
21
|
- **_underscore** (_Underscore) _(framework core)_ — 71 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
22
22
|
- **worker2** (Worker) — 58 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
23
23
|
- **api2** (API) — 25 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
24
|
-
- **dbchanges2** (Database Changes) _(framework core)_ —
|
|
24
|
+
- **dbchanges2** (Database Changes) _(framework core)_ — 15 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
25
25
|
- **toga2-supply** (TOGa Supply) — 9 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
|
26
26
|
- **saml** (SAML SSO Gateway) — 5 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
|
|
27
27
|
- **toga2-view** (TOGa View Frontend) — 12 doc(s) → [2.0/apps/toga2-view/INDEX.md](2.0/apps/toga2-view/INDEX.md)
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
| [Stranded Approval Reassignment (repointing approvals off dead duplicate Compass Users rows)](features/stranded-approval-reassignment.md) | 2.0 | A Compass employee who leaves and comes back after more than `CONTACT_UNLINK_GRACE_DAYS` (5) is **inserted as a brand-new `Users` row** by the PEOPLE importer i | _underscore/Model/Compass/ApprovalDecision.php, worker2/Worker/Client/Compass/ApprovalReassignment.php, worker2/Worker/Client/Compass/PeopleFile.php |
|
|
23
23
|
| [Compass USA](profile.md) | 2.0 | Compass USA is a TOGA client running a multi-tier supply-chain commerce operation. | |
|
|
24
24
|
| [Compass Cross-Kit Bundle Corruption — Detection & Repair](workflows/cross-kit-bundle-corruption.md) | 2.0 | A frontend regression in `toga2-commerce`'s edit-order bundle submission mis-attributed bundle (kit) line items and **fees/warranties** to the **wrong kit**, pe | src/api/syncSalesOrderItemsFromLocalStorageCartToApi.ts |
|
|
25
|
-
| [Granting a Compass user the same toga25-supply navigation as another user (role vs. SurfaceOverride)](workflows/granting-navigation-access.md) | 2.0 | "Give user X the same menu items user Y has" is the **navigation analog** of the recurring [bundle/persona grant](./granting-persona-bundle-access.md) request. | dbchanges2/Client_Compass/2026-08-
|
|
25
|
+
| [Granting a Compass user the same toga25-supply navigation as another user (role vs. SurfaceOverride)](workflows/granting-navigation-access.md) | 2.0 | "Give user X the same menu items user Y has" is the **navigation analog** of the recurring [bundle/persona grant](./granting-persona-bundle-access.md) request. | dbchanges2/Client_Compass/2026-08-12a - NavigationRoleGrants.sql, dbchanges2/Client_Compass/2026-02-25 - AgilantUsersViewAccess.sql, dbchanges2/Client_Compass/2026-08-11 - CompassODPViewVendorItemsAccess.sql, dbchanges2/Core/2026-08-12a - NavigationSurfaceSeed.sql |
|
|
26
26
|
| [Granting a Compass user access to a bundle (persona grant) and the SuperUser role](workflows/granting-persona-bundle-access.md) | 2.0 | "Give user X sight of kit N" is a **recurring** Compass request, usually paired with "and make them a super user". | dbchanges2/Client_Compass/2026-08-26a - CompassCreativeStudioPersona.sql |
|
|
27
27
|
| [Compass Office Depot Duplicate PO-Line Cleanup (dual-catalog SKU)](workflows/odp-duplicate-po-line-cleanup.md) | 1.0 | The NetSuite fulfillment-sales-order importer duplicated Office Depot purchase-order lines on Compass USA because it reconciled the fulfillment SO against the e | library/app/api/toga2.php, dbchanges2/Client_Compass/2026-07-22a - CleanupOfficeDepotDuplicatePurchaseOrderItems.sql |
|
|
28
28
|
| [Recovering a Lost Compass ODP EDI 850 Import (re-drop from Logs.FileLog)](workflows/odp-edi-import-recovery.md) | 1.0 | How to recover a Compass **Office Depot EDI 850** import that failed partway — the case where cron **3a** created the ODP SalesOrder header, the follow-up item | worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php, worker/crons/toga2/compass/workflow/5_create_netsuite_sales_orders_from_office_depot_purchase_orders.php, worker/schedules/cron.worker.sync.json |
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: compass-usa
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: ["apeterson", "dfranks", "bala"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Compass/ApprovalDecision.php
|
|
@@ -17,6 +17,7 @@ files:
|
|
|
17
17
|
- _underscore/Model/Quad/SalesOrder.php
|
|
18
18
|
related:
|
|
19
19
|
- mr-ma-order-approval-and-status.md
|
|
20
|
+
- ../../../2.0/apps/toga25-supply/features/action-button-rule-engine.md
|
|
20
21
|
- ../../../2.0/apps/_underscore/features/sales-order-denial-reason.md
|
|
21
22
|
- people-file-user-lifecycle.md
|
|
22
23
|
- stranded-approval-reassignment.md
|
|
@@ -342,6 +343,45 @@ requester**. All of these comparisons are case-insensitive (`strcasecmp()`), mat
|
|
|
342
343
|
[Record Change Audit Log](../../../2.0/apps/_underscore/features/record-change-audit-log.md).
|
|
343
344
|
- Real incident: order SA134461 — VIP manager Spyros Gravas, subtotal $926.47 (both auto-approve
|
|
344
345
|
conditions met) still required manual override because the requester had no supervisor.
|
|
346
|
+
- **⚠ The `!$hasManager` branch strands orders in an unrecoverable decided-but-untyped state — the
|
|
347
|
+
worst of the no-supervisor family.** When the "Order for" contact is changed to a user with **no**
|
|
348
|
+
`Users.supervisorUserId`, `_Model_Compass_SalesOrder::postPut`'s `!$hasManager` branch
|
|
349
|
+
(`Model/Compass/SalesOrder.php` ~L1018) PUTs `assignedToUser`, `decidedByUser`, `isApproved`,
|
|
350
|
+
`approvalDecisionType`, `note` **and** `dtDecision` all to `null` — deliberately wiping an existing
|
|
351
|
+
VIP auto-approval. A later **manual** approval then sets `isApproved = 1` + `decidedByUserId` but
|
|
352
|
+
never stamps `approvalDecisionTypeId` (correct — it is not a *system* decision). End state:
|
|
353
|
+
`isApproved = 1`, `approvalDecisionTypeId = NULL`, and frequently a **stale** "Auto-approved:
|
|
354
|
+
Manager is VIP and order total <= 5000" note left behind.
|
|
355
|
+
- **Frontend consequence (the part that makes it unrecoverable):** blox derives `isPreApproved`
|
|
356
|
+
from `!!stage.ApprovalDecisionTypes.name`, so it is `false`. The approval modal therefore locks
|
|
357
|
+
the stage's inputs **and hides the Stage Actions block that contains the only "Reset to Pending"
|
|
358
|
+
control** — no admin action can recover the order. This is why the 2026-08-31 frontend fix gates
|
|
359
|
+
on `stepOneUndecided` rather than on `isPreApproved` (see
|
|
360
|
+
[Action-Button Rule Engine](../../../2.0/apps/toga25-supply/features/action-button-rule-engine.md)).
|
|
361
|
+
- **Diagnostic signature in the order activity log**, in order: `"Manager approval skipped due to
|
|
362
|
+
VIP logic"` (creation-time VIP auto-approve), then `"Manager assignment cleared: new user has no
|
|
363
|
+
supervisor"` **and** `"VIP auto-approval removed: new user has no supervisor"` (written **only**
|
|
364
|
+
by that `!$hasManager` branch), then `"Manager Approved by <admin>"` (the manual approval).
|
|
365
|
+
- **Prod evidence 2026-08-31** (`Client_Compass`, orders **SA136701 / SA136702**): step-1 decision
|
|
366
|
+
NULL; step-2 `isApproved = 1`, `approvalDecisionTypeId = NULL`, `decidedByUserId = 152887` (the
|
|
367
|
+
admin, **not** the assigned manager), assignee `JEFF.KRAUSE@COMPASS-USA.COM` with `c_isVip = 1`,
|
|
368
|
+
and the VIP auto-approve text still in `note`. **Caveat: for those two specific orders the writer
|
|
369
|
+
was NOT positively identified** (no log access; prod RDS is unreachable from a laptop). The
|
|
370
|
+
mechanism above is proven from a **third** order's activity log — treat it as the mechanism, not
|
|
371
|
+
as confirmed provenance of those two rows.
|
|
372
|
+
- **Still open (not addressed):** nothing stops **new** orders reaching this state — the
|
|
373
|
+
`!$hasManager` branch should either re-derive a manager for the new contact or leave a clean
|
|
374
|
+
*pending* stage instead of a half-cleared one. And once step 1 **is** approved, a decided stage
|
|
375
|
+
still hides its own "Reset to Pending" control.
|
|
376
|
+
- **Two dead theories — do not re-derive them.** Both were chased against api2's write path
|
|
377
|
+
(`Component/Api/V2/V2.php` ~L4858-5040) and are false:
|
|
378
|
+
1. api2 **does** accept a bare numeric FK id on write (it falls through to `$model->$key = $val`),
|
|
379
|
+
so passing `'approvalDecisionType' => 1` as a scalar through `internalApiRequest` is **not**
|
|
380
|
+
silently dropped.
|
|
381
|
+
2. In api2's write path an **unknown** field or a **non-writable** field is a **hard error**
|
|
382
|
+
(`unknownFields` / `fieldsNotWritable` → an `EZ`/`EV` message, whole request fails), never a
|
|
383
|
+
silent strip. So a row that saved its other fields **cannot** have had one field quietly
|
|
384
|
+
discarded by ACL.
|
|
345
385
|
- **Never drop the order requester from notifications on manager reassignment.** If the outgoing
|
|
346
386
|
manager happens to also be the order requester (same email), deleting the old-manager address
|
|
347
387
|
from `SalesOrderEmailAddresses` would silently remove the requester from all order notifications.
|
|
@@ -397,6 +437,16 @@ requester**. All of these comparisons are case-insensitive (`strcasecmp()`), mat
|
|
|
397
437
|
approve as manager on his behalf. (Fixed 2026-07-23.)
|
|
398
438
|
|
|
399
439
|
## Change history
|
|
440
|
+
- 2026-08-31 — **Root-caused the "decided but untyped" step-2 approvals that no admin can recover**
|
|
441
|
+
(both tenants, via the shared parent). `_Model_Compass_SalesOrder::postPut`'s `!$hasManager` branch
|
|
442
|
+
nulls the entire step-2 decision when the "Order for" contact has no `supervisorUserId`; a
|
|
443
|
+
subsequent manual approval sets `isApproved = 1` without `approvalDecisionTypeId`, so the frontend
|
|
444
|
+
reads `isPreApproved = false`, locks the stage **and** hides the only "Reset to Pending" control.
|
|
445
|
+
Recorded the three-note activity-log signature, prod evidence SA136701/SA136702 (with the caveat
|
|
446
|
+
that the writer of those two rows was not positively identified), the still-open prevention gap,
|
|
447
|
+
and two **dead** api2 theories (a bare numeric FK id **is** accepted on write; an unknown or
|
|
448
|
+
non-writable field is a hard error, never a silent strip). Investigation only — no backend change;
|
|
449
|
+
the frontend mitigation shipped in `toga25-supply`. (apeterson)
|
|
400
450
|
- 2026-08-28 — Recorded that this flow's `buildRejectionNoteHtml()` path is the **only** consumer of
|
|
401
451
|
`ApprovalDecisions.note` anywhere in the platform — no supply or commerce frontend renders the
|
|
402
452
|
denial reason (and Quad reaches it through its **own** `_Model_Quad_ApprovalDecision`, not this
|
|
@@ -6,11 +6,9 @@ project: Database Changes
|
|
|
6
6
|
client: compass-usa
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-31
|
|
10
10
|
owners: [apeterson, bala]
|
|
11
11
|
files:
|
|
12
|
-
- dbchanges2/Client_Compass/2026-08-27a - GrantAgilantAdministratorsRoleToInternalUser.sql
|
|
13
|
-
- dbchanges2/Client_Compass/2026-08-28 - GrantViewOrdersRoleSalesOrdersNavigation.sql
|
|
14
12
|
- dbchanges2/Client_Compass/2026-08-12a - NavigationRoleGrants.sql
|
|
15
13
|
- dbchanges2/Client_Compass/2026-02-25 - AgilantUsersViewAccess.sql
|
|
16
14
|
- dbchanges2/Client_Compass/2026-08-11 - CompassODPViewVendorItemsAccess.sql
|
|
@@ -253,6 +251,15 @@ tenant-wide behavior change and needs its own ticket and sign-off.
|
|
|
253
251
|
`AclActionPermissions` when a menu item is missing. It is always a `SurfaceOverrides` row.
|
|
254
252
|
|
|
255
253
|
## Change history
|
|
254
|
+
- 2026-08-31 — 🚨 **Both migration files this doc describes are GONE.** Verified with
|
|
255
|
+
`git log --all`: neither `Client_Compass/2026-08-27a - GrantAgilantAdministratorsRoleToInternalUser.sql`
|
|
256
|
+
nor `Client_Compass/2026-08-28 - GrantViewOrdersRoleSalesOrdersNavigation.sql` was ever committed on
|
|
257
|
+
any branch, and neither is in the working tree — the 08-27a file was deleted this session at the
|
|
258
|
+
developer's request (local backup only), and a third untracked file,
|
|
259
|
+
`2026-08-28a - ViewOrdersRoleReadOnlySurface.sql`, was deleted with it. Removed both from `files:`.
|
|
260
|
+
**The recipes and the role/element analysis below still stand — the SQL must be rewritten before it
|
|
261
|
+
can ship.** General lesson: an uncommitted migration is not a deliverable; a doc that cites one is
|
|
262
|
+
citing something that may not exist tomorrow. (apeterson)
|
|
256
263
|
- 2026-08-28 — Added the **reverse case**: a role with no `SurfaceOverrides` nav row leaves the user
|
|
257
264
|
with zero menu items, which `toga25-supply` renders as a **blank page with no error** and users
|
|
258
265
|
report as *"cannot log in"* (a fresh `Users.dtLastLogin` proves auth succeeded). Identified the
|
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
| [NYCHH NetSuite → TransferOrders import (stocking-flag routing, PO bridges, origin location)](features/netsuite-transfer-order-import.md) | 1.0 | NYCHH's NetSuite **transfer orders are represented as NetSuite sales orders** (same as the GroWrk pattern), so the shared NetSuite → TOGa Supply importer (`App_ | library/app/api/toga2.php, library/app/api/netsuite/rest.php, library/app/netsuite.php, worker/crons/toga2/netsuite/sync_togasupply_hh.php, worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/crons/toga2/netsuite/diagnose_hh_stuck_transfer_order.php, dbchanges2/Core/2026-08-27a - TransferOrderUuidSearchableIdentifier.sql, dbchanges2/Client/2026-08-28a - TransferOrderItemsTimestampColumns.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestampColumnsReposition.sql, dbchanges2/Client/2026-08-29 - ItemReceiptTransferOrderIdFieldPermission.sql, dbchanges2/Client/2026-08-29b - ItemReceiptItemTransferOrderItemIdFieldPermission.sql |
|
|
7
7
|
| [NYCHH PO links are UPSTREAM — the downstream SO→PO route returns empty](features/po-number-upstream-direction.md) | 2.0 | NYCHH's sales orders are created **from the customer's purchase order**, so their SO↔PO links live in the **upstream** table `PurchaseOrders_SalesOrders` (route | _underscore/Model/Client/SalesOrder.php, _underscore/Trait/Netsuite/SalesOrder.php, toga25-supply/src/pages/SalesOrders/view/SalesOrderRecordModalLayout/hooks/usePurchaseOrderDetails.ts, dbchanges2/Client/2026-08-11b - SalesOrderPurchaseOrdersField.sql, dbchanges2/Client/2026-08-25 - SalesOrderPurchaseOrdersFieldAllClients.sql |
|
|
8
8
|
| [NYCHH transfer-order 2.0 model — inventory quantities + V2 field enablement](features/transfer-order-inventory-quantities.md) | 2.0 | The 2.0 (`_underscore`) side of NYCHH transfer-order support: a **two-branch inventory quantity model** on the NYCHH `Item` override, plus the **client-override | _underscore/Model/Nychh/Item.php, _underscore/Model/Nychh/PurchaseOrderItem.php, _underscore/Model/Nychh/TransferOrder.php, _underscore/Model/Nychh/TransferOrderStage.php, _underscore/Model/Client/TransferOrder.php, _underscore/Model/Client/TransferOrderItem.php, dbchanges2/Client_Nychh/2026-08-27a - TransferOrderCustomFieldWritePermission.sql, dbchanges2/Client_Nychh/2026-08-27b - TransferOrderStageStatusIdentifier.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql |
|
|
9
|
-
| [NYC Health & Hospitals](profile.md) | 2.0 | NYC Health & Hospitals (NYCHH) is a TOGA 2.0 client on the `_underscore` platform, prod schema `Client_Nychh`. | dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, dbchanges2/Client_Nychh/2026-08-
|
|
9
|
+
| [NYC Health & Hospitals](profile.md) | 2.0 | NYC Health & Hospitals (NYCHH) is a TOGA 2.0 client on the `_underscore` platform, prod schema `Client_Nychh`. | dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql, dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql, dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql, dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql, dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql, dbchanges2/Client/2026-08-28b - TransferOrderItemsTimestamps.sql |
|
|
@@ -16,13 +16,11 @@ project: _Underscore
|
|
|
16
16
|
client: nychh
|
|
17
17
|
type: profile
|
|
18
18
|
status: active
|
|
19
|
-
updated: 2026-08-
|
|
19
|
+
updated: 2026-08-31
|
|
20
20
|
owners: ["jcardinal", "apeterson", "bala", "akhokhani"]
|
|
21
21
|
files:
|
|
22
22
|
- dbchanges2/Client_Nychh/2026-08-18 - InventoryUnitsItemColumns.sql
|
|
23
23
|
- dbchanges2/Client_Nychh/2026-08-24 - FixInventoryGroupingsUnitsTopologyOverrideIds.sql
|
|
24
|
-
- dbchanges2/Client_Nychh/2026-08-27a - TransferOrderCustomFieldWritePermission.sql
|
|
25
|
-
- dbchanges2/Client_Nychh/2026-08-27b - TransferOrderStageStatusIdentifier.sql
|
|
26
24
|
- dbchanges2/Client_Nychh/2026-08-28a - TransferOrdersTableView.sql
|
|
27
25
|
- dbchanges2/Client_Nychh/2026-08-28b - TransferOrdersNavigationEnable.sql
|
|
28
26
|
- dbchanges2/Client_Nychh/2026-08-28c - TransferOrderRecordAcl.sql
|
|
@@ -68,14 +66,26 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
68
66
|
arrive as sales orders; the 1.0 importer routes them by the `custbody_stocking_order` flag (not the
|
|
69
67
|
dollar amount), bridges the PO, and resolves origin/destination locations. See
|
|
70
68
|
[NYCHH NetSuite → TransferOrders import](./features/netsuite-transfer-order-import.md).
|
|
71
|
-
-
|
|
69
|
+
- 🚨 **`Client_Nychh.TransferOrders` HAS production data — 1,927 rows (verified 2026-08-31 on
|
|
70
|
+
`reader1.client.database.togahub.com`).** The transfer-order *tables and data* have been live in
|
|
71
|
+
prod for a while (the 1.0 NetSuite importer writes them); only the **2026-08-28 UI vertical**
|
|
72
|
+
(TableView, Core surfaces, nav override) is new. An earlier version of this profile said transfer
|
|
73
|
+
orders were "local only … not yet run on sandbox-client or production", and that stale line
|
|
74
|
+
produced a wrong recommendation this session — that a prod-to-local restore would *wipe* the
|
|
75
|
+
transfer-orders feature. **Never infer prod state from this repo or from a migration's run
|
|
76
|
+
status; read prod.** Restoring or overwriting `Client_Nychh` destroys real transfer-order rows.
|
|
77
|
+
- **The Transfer Orders SCREEN is NYCHH-only and new (2026-08-28).** NYCHH is the first — and
|
|
72
78
|
today the only — tenant with a `transfer-orders` TableView (`Client_Nychh/2026-08-28a`, which also
|
|
73
79
|
seeds the ACL chain for record 351 that the Status column silently depends on) and the one
|
|
74
80
|
`SurfaceOverrides` row that reveals the nav item (`2026-08-28b`, client-wide, matching how this
|
|
75
81
|
tenant already grants Sales Orders and Inventory — NYCHH does not scope navigation by role). The
|
|
76
82
|
Core surfaces and the stage colour tokens are **shared/neutral**, hidden for every other client
|
|
77
|
-
until they opt in.
|
|
78
|
-
|
|
83
|
+
until they opt in. The seven migration files were **committed 2026-08-31** (`dbchanges2` commit
|
|
84
|
+
`1c3fe2e`, branch `TRUE-80852`, pushed); run order is **`Core/` → `Client/` fan-out →
|
|
85
|
+
`Client_Nychh/`**, because the client files reference Core record ids and the NYCHH TableView/ACL
|
|
86
|
+
need the columns the fan-out files add. Run state per environment must be **verified against that
|
|
87
|
+
environment**, never assumed from the repo. **beta is blocked** because it lacks the
|
|
88
|
+
`TransferOrderStages` table. A **Create Transfer Order** modal is started (UI built, data
|
|
79
89
|
layer flagged off pending three open data-model questions). See
|
|
80
90
|
[Transfer Orders page](../../2.0/apps/toga25-supply/features/transfer-orders-page.md).
|
|
81
91
|
- 🚨 **The transfer-order ACL + schema on prod/sandbox are UNTRACKED (2026-08-28).** Record and field
|
|
@@ -122,4 +132,14 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
122
132
|
ship in the same release as that Core file.** NYCHH was the only drifted client (Client_Elite's
|
|
123
133
|
equivalents are correct). See
|
|
124
134
|
[Surface Layer Schema](../../2.0/apps/dbchanges2/features/surface-layer-schema.md).
|
|
135
|
+
**Where that file actually lives (checked 2026-08-31):** `Client_Nychh/2026-08-24 -
|
|
136
|
+
FixInventoryGroupingsUnitsTopologyOverrideIds.sql` is committed on branch **`TRUE-81190`** only —
|
|
137
|
+
it is **not on `_main`**, so shipping `Core/2026-08-21 - SalesOrderDecisionSurfacesReseed.sql`
|
|
138
|
+
without merging `TRUE-81190` is the exact break described above.
|
|
139
|
+
- 🚨 **Two NYCHH migrations named in earlier notes were never committed anywhere** (`git log --all`,
|
|
140
|
+
2026-08-31): `Client_Nychh/2026-08-27a - TransferOrderCustomFieldWritePermission.sql` and
|
|
141
|
+
`Client_Nychh/2026-08-27b - TransferOrderStageStatusIdentifier.sql`. Zero commits touch either
|
|
142
|
+
path on any branch, and neither is in the working tree — so the transfer-order **custom-field
|
|
143
|
+
write permission** and the **stage status identifier** are, like the ACL layers above, prod/local
|
|
144
|
+
state with no file behind them. Rewrite them before anyone builds a fresh environment.
|
|
125
145
|
- This profile is a starting point; expand as more NYCHH-specific behavior is captured.
|
package/package.json
CHANGED