toga-ai 1.0.536 → 1.0.538
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/1.0/apps/library/features/startech-pcmaticb2b-sync.md +37 -15
- package/knowledge/1.0/apps/library/features/toga2-api-client-and-bridge.md +9 -1
- package/knowledge/1.0/standards/backend-php.md +40 -29
- package/knowledge/1.0/standards/framework-rules.md +9 -5
- package/knowledge/2.0/apps/_underscore/features/error-reporting-issue-event.md +26 -1
- package/knowledge/2.0/apps/_underscore/features/tracking-number-bridges.md +22 -2
- package/knowledge/2.0/apps/_underscore/features/units-for-items-for-purchase-orders.md +17 -3
- package/knowledge/2.0/apps/_underscore/workflows/local-db-refresh-from-beta.md +15 -2
- package/knowledge/2.0/apps/api2/features/v2-api-error-codes.md +57 -2
- package/knowledge/2.0/apps/toga2-commerce/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga2-commerce/features/client-fields.md +42 -2
- package/knowledge/2.0/apps/toga2-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga2-supply/workflows/client-host-scoping.md +82 -5
- package/knowledge/2.0/apps/worker2/features/creating-worker-actions.md +11 -0
- package/knowledge/INDEX.md +3 -3
- package/knowledge/clients/compass-canada/profile.md +15 -1
- package/knowledge/clients/compass-usa/INDEX.md +3 -0
- package/knowledge/clients/compass-usa/features/persona-model-and-levy-gating.md +132 -0
- package/knowledge/clients/compass-usa/profile.md +17 -1
- package/knowledge/clients/compass-usa/workflows/persona-population-env-comparison.md +82 -0
- package/knowledge/clients/compass-usa/workflows/persona-refactor-migration.md +138 -0
- package/knowledge/clients/elite/INDEX.md +2 -1
- package/knowledge/clients/elite/features/supply2-scope.md +65 -23
- package/knowledge/clients/elite/features/supply2-tableview-config-drift.md +107 -0
- package/knowledge/clients/elite/profile.md +19 -2
- package/knowledge/clients/nycdoe/features/servicenow-integration.md +75 -1
- package/knowledge/clients/quad/INDEX.md +1 -0
- package/knowledge/clients/quad/features/po-asn-email-import.md +177 -0
- package/knowledge/clients/quad/profile.md +16 -2
- package/knowledge/clients/rate/INDEX.md +1 -1
- package/knowledge/clients/rate/features/aig-contract-creation.md +53 -1
- package/knowledge/clients/rate/features/paypal-subscription-purchase-webhook.md +129 -17
- package/knowledge/sessions/2026-08-06-elite-inventory-fe-tcox.md +80 -0
- package/knowledge/sessions/2026-08-06-quad-asn-import-jam-ajean.md +256 -0
- package/knowledge/standalone/apps/claude/workflows/mcp-tool-usage.md +12 -2
- package/package.json +1 -1
|
@@ -6,8 +6,8 @@ project: Library
|
|
|
6
6
|
client: pcmaticb2b
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: [snaredla]
|
|
9
|
+
updated: 2026-08-10
|
|
10
|
+
owners: [snaredla, mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
13
13
|
- library/app/api/startechticket.php
|
|
@@ -59,6 +59,32 @@ $payload['ticketType'] = ['code' => $toga2TicketTypeCode];
|
|
|
59
59
|
|
|
60
60
|
Both `PCS Ticket` and `TOGaDesk-PCS` map to `PCS` in TOGA 2.0.
|
|
61
61
|
|
|
62
|
+
## Onboarding a new StarTech client — `STARTECH_TOGADESK_CLIENTS`
|
|
63
|
+
|
|
64
|
+
`App_Api_Toga2::STARTECH_TOGADESK_CLIENTS` (`library/app/api/toga2.php`, top of the class) is the
|
|
65
|
+
registry that identifies StarTech-integrated clients. It maps **TOGaDesk client id => the client's
|
|
66
|
+
TOGA 2.0 API credentials**:
|
|
67
|
+
|
|
68
|
+
```php
|
|
69
|
+
const STARTECH_TOGADESK_CLIENTS = [
|
|
70
|
+
App_Model_TogaDesk_Ticket::TOGADESK_CLIENT_ID__PCMATICB2B => [
|
|
71
|
+
'clientUuid' => self::CLIENT_UUID_PCMATICB2B,
|
|
72
|
+
'apiUuid' => self::API_UUID_PCMATICB2B,
|
|
73
|
+
'apiSecret' => self::API_SECRET_PCMATICB2B,
|
|
74
|
+
],
|
|
75
|
+
];
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- **A new row must be added per client onboard.** Consumers (e.g.
|
|
79
|
+
`getStartechSelectableTicketStageNames`) look the client up here and **short-circuit to an empty
|
|
80
|
+
result with no DB hit** when absent — so a missing row is a silent no-op (an empty status
|
|
81
|
+
dropdown), not an error. This is the operational step to remember.
|
|
82
|
+
- The row's values are references to the per-client `CLIENT_UUID_*` / `API_UUID_*` /
|
|
83
|
+
`API_SECRET_*` class constants declared at the top of the same file — that is **where** the
|
|
84
|
+
credentials live. Do not reproduce their values in the KB, tickets, or logs (see the hardcoded-credentials
|
|
85
|
+
gotcha in [toga2-api-client-and-bridge](./toga2-api-client-and-bridge.md)).
|
|
86
|
+
- Currently **one** row: TOGaDesk client **177** (PC Matic B2B).
|
|
87
|
+
|
|
62
88
|
## Gotchas
|
|
63
89
|
|
|
64
90
|
- **Do not add a NULL early-return guard in this function.** NULL `c_escalateToStartech` means the ticket was created in TOGaDesk — it must still sync to TOGA 2.0. The Startech gate lives in `_Trait_Startech_Ticket::postPost`, not here.
|
|
@@ -93,16 +119,12 @@ Imports Startech (Easeedesk V3) config into TOGA 2.0 — users, groups, devices,
|
|
|
93
119
|
the 1.0 → 2.0 payload. Both reference the client's 2.0 schema **by name** (from
|
|
94
120
|
`App_Model_Client::getClientDatabaseNames()`) so the JOIN stays inside one client schema — do not use
|
|
95
121
|
`qqJoinClientsTable(…, true)` here, its UNION-across-all-clients form would cross-match ids between
|
|
96
|
-
clients. StarTech clients are identified by `App_Api_Toga2::STARTECH_TOGADESK_CLIENTS
|
|
97
|
-
|
|
98
|
-
- 2026-
|
|
99
|
-
|
|
100
|
-
`
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
the
|
|
105
|
-
`App_Model_Client::getClientDatabaseNames()`) so the JOIN stays inside one client schema — do not use
|
|
106
|
-
`qqJoinClientsTable(…, true)` here, its UNION-across-all-clients form would cross-match ids between
|
|
107
|
-
clients. StarTech clients are identified by `App_Api_Toga2::STARTECH_TOGADESK_CLIENTS`
|
|
108
|
-
(TOGaDesk client id => TOGA 2.0 client id); non-StarTech clients short-circuit with no DB hit.
|
|
122
|
+
clients. StarTech clients are identified by `App_Api_Toga2::STARTECH_TOGADESK_CLIENTS`;
|
|
123
|
+
non-StarTech clients short-circuit with no DB hit.
|
|
124
|
+
- 2026-08-10: **Corrected** the description of `STARTECH_TOGADESK_CLIENTS` — it maps TOGaDesk client
|
|
125
|
+
id to the client's **TOGA 2.0 API credentials** (`clientUuid`/`apiUuid`/`apiSecret`, referencing the
|
|
126
|
+
`CLIENT_UUID_*`/`API_UUID_*`/`API_SECRET_*` class constants), **not** to a TOGA 2.0 client id as the
|
|
127
|
+
two 2026-07-29 entries stated. Added the onboarding section: a row must be added per client onboard
|
|
128
|
+
or the client silently short-circuits (empty stage list, no error). Also deduplicated the
|
|
129
|
+
accidentally-doubled 2026-07-29 entry. Observed while resolving a `_production` merge on TRUE-79401;
|
|
130
|
+
the const itself was authored by a teammate. (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-10
|
|
10
10
|
owners: [jcardinal, mhammontree, bala]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
@@ -213,6 +213,10 @@ tickets) was **explicitly rejected** — see the gotcha about the single-recipie
|
|
|
213
213
|
intact under a large merge. **Still open: whether it is deployed to the prod worker.**
|
|
214
214
|
Review technique worth reusing: `git diff _production...HEAD -- <file>` isolates a ticket's real
|
|
215
215
|
contribution from merge noise when an old branch is finally synced.
|
|
216
|
+
**Re-synced 2026-08-10** (`c64164fe`, pushed): `_production` merged into `TRUE-79401` again; the
|
|
217
|
+
only conflict was a positional collision in the class const block of `app/api/toga2.php` (this
|
|
218
|
+
ticket's `CUSTOMER_EMAIL_DELIMITER` landing next to incoming consts) — resolved by keeping both,
|
|
219
|
+
no logic change. Patch still intact; **deploy to the prod worker remains unverified.**
|
|
216
220
|
2. **Beta verification of the nested write is outstanding.** Assert real
|
|
217
221
|
`Client_Aig.ContactEmailAddresses` **row counts** (never the HTTP status), covering **both** a
|
|
218
222
|
brand-new contact (CREATE) **and** a second entitlement for an **existing** contact (UPDATE).
|
|
@@ -361,6 +365,10 @@ enable flags** and an optional `$monitorTogadeskDepartmentIds[]`:
|
|
|
361
365
|
|
|
362
366
|
## Change history
|
|
363
367
|
|
|
368
|
+
- 2026-08-10 — TRUE-79401 re-synced with `_production` (`c64164fe`, pushed); the sole conflict was a
|
|
369
|
+
positional const-block collision, resolved by keeping both sides. Patch intact; deploy still
|
|
370
|
+
unverified. `STARTECH_TOGADESK_CLIENTS` (credential registry, add a row per client onboard) is
|
|
371
|
+
documented on [startech-pcmaticb2b-sync](./startech-pcmaticb2b-sync.md). (mhammontree)
|
|
364
372
|
- 2026-08-05 — Recorded that a 2.0 GET **silently truncates a nested child collection** (observed
|
|
365
373
|
`itemFulfillmentItemUnits` 45 of 70) with no error — so verification/counts must read the child
|
|
366
374
|
resource **flat + fully paginated** and use `meta.totalRecordCount`, never a single nested
|
|
@@ -5,8 +5,8 @@ project: Library
|
|
|
5
5
|
client: shared
|
|
6
6
|
type: standard
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-08-
|
|
9
|
-
owners: [jcardinal, rgirish, mhammontree]
|
|
8
|
+
updated: 2026-08-06
|
|
9
|
+
owners: [jcardinal, rgirish, mhammontree, ajean]
|
|
10
10
|
files: []
|
|
11
11
|
related:
|
|
12
12
|
- ../apps/library/architecture.md
|
|
@@ -442,33 +442,44 @@ class Browser_Datagrid_Accounts extends Browser_Datagrid {
|
|
|
442
442
|
* The `@` error-suppression operator is acceptable only for defensive file I/O (e.g. reading an optional cache file); do not use it to hide real errors.
|
|
443
443
|
* Use structured logging to capture application behavior and errors.
|
|
444
444
|
|
|
445
|
-
###
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
445
|
+
### A notice/warning cannot be contained by `try/catch` — and it jams queues
|
|
446
|
+
|
|
447
|
+
**Canonical mechanics are above:** see
|
|
448
|
+
[*Any PHP warning terminates the request — how to opt out locally*](#any-php-warning-terminates-the-request--how-to-opt-out-locally).
|
|
449
|
+
`App_Error::handleError` does **not** throw; it calls `handleException`, which `exit`s. Two
|
|
450
|
+
sanctioned opt-outs (a local `set_error_handler`, or
|
|
451
|
+
`App_Error::setThrowExceptionsEnabled(false)` with a `finally` restore) are documented there.
|
|
452
|
+
This section records what that means for **job/queue code** specifically.
|
|
453
|
+
|
|
454
|
+
Additional verified detail:
|
|
455
|
+
|
|
456
|
+
- **Both exit paths.** `handleException` ends in `exit` whether Sentry is absent
|
|
457
|
+
(`library/app/error.php` ~L407) or present (~L671). Apps that call `\Sentry\init` in
|
|
458
|
+
`_/app/framework.php` — `worker` included — take the Sentry path, so "Sentry is configured"
|
|
459
|
+
does **not** mean execution continues.
|
|
460
|
+
- **The `return false;` after `exit;` in `handleError` is unreachable dead code.** Do not read
|
|
461
|
+
it as a signal that the handler returns control to the call site.
|
|
462
|
+
- **A notice jams a queue exactly like an uncaught exception would.** In an inbox- or
|
|
463
|
+
queue-driven loop, any statement *after* the failure point — an ack, an
|
|
464
|
+
`$imap->deleteMessage()`, a status stamp — never executes, so the same item is re-read and
|
|
465
|
+
re-fails on every run, forever. Confirmed against production evidence: after the notice
|
|
466
|
+
fired, the importer made **zero** further api2 calls in that run.
|
|
467
|
+
- **Diagnostic fingerprint:** an occurrence count that is an exact multiple of the job's
|
|
468
|
+
scheduled frequency (e.g. 20/hour for `*/3 * * * *`) means **one** poisoned item retried,
|
|
469
|
+
not many distinct failures.
|
|
470
|
+
- **In job code, prevention beats containment.** Guard optional data with `isset()` before
|
|
471
|
+
reading it, and consume/ack the item in a `finally` (or under a `catch (Throwable)`) so a
|
|
472
|
+
genuine exception cannot strand it. You cannot wrap your way out of a notice.
|
|
473
|
+
|
|
474
|
+
> **Existing guidance under review.** An earlier version of this section recommended
|
|
475
|
+
> wrapping fragile I/O in `try { ... } catch (\ErrorException $e)` relying on the GLOBAL
|
|
476
|
+
> handler to convert a warning. Given the `exit` above, that cannot work for a PHP
|
|
477
|
+
> warning or notice — the process ends before the `catch`. Uses of that pattern need
|
|
478
|
+
> **re-verifying** rather than assuming they are safe or assuming they are broken: the
|
|
479
|
+
> call may be throwing a genuine exception, or installing a local handler. Known
|
|
480
|
+
> occurrences to check: `clients/prudential/features/transmit-ordershipped-email.md`
|
|
481
|
+
> (~L112-123), `clients/prudential/profile.md` (~L75). Prefer the local-handler pattern
|
|
482
|
+
> from the canonical section above for anything new.
|
|
472
483
|
|
|
473
484
|
## Security Best Practices
|
|
474
485
|
|
|
@@ -5,8 +5,8 @@ project: Library
|
|
|
5
5
|
client: shared
|
|
6
6
|
type: standard
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-06
|
|
9
|
-
owners: [jcardinal]
|
|
8
|
+
updated: 2026-08-06
|
|
9
|
+
owners: [jcardinal, ajean]
|
|
10
10
|
files: []
|
|
11
11
|
related:
|
|
12
12
|
- ../apps/library/architecture.md
|
|
@@ -114,9 +114,13 @@ repos is only query strings embedded in PHP code — not standalone migration fi
|
|
|
114
114
|
|
|
115
115
|
## PHP 8.x deprecations abort the request (audit before running on 8.5)
|
|
116
116
|
|
|
117
|
-
The 1.0 framework's error handler
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
The 1.0 framework's error handler **terminates** on a PHP deprecation notice
|
|
118
|
+
(`App_Error::handleError` → `handleException` → `exit`; it does not throw a catchable
|
|
119
|
+
`ErrorException`), so the request/job dies where the deprecation fires. On PHP 8.5 a single
|
|
120
|
+
deprecated call in a live code path is therefore a hard, uncontainable failure — a
|
|
121
|
+
surrounding `try/catch` will not save it. See the back-end standard's
|
|
122
|
+
*"Any PHP warning terminates the request — how to opt out locally"* section for the two
|
|
123
|
+
sanctioned local opt-outs.
|
|
120
124
|
|
|
121
125
|
- When porting or running 1.0 code on PHP 8.x, **audit for deprecated-in-8.x calls** in every
|
|
122
126
|
reachable code path before deploy.
|
|
@@ -7,7 +7,7 @@ client: shared
|
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
9
|
updated: 2026-08-06
|
|
10
|
-
owners: ["dfranks", "jcardinal", "mhammontree"]
|
|
10
|
+
owners: ["dfranks", "jcardinal", "mhammontree", "ajean"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Error.php
|
|
13
13
|
- _underscore/Database.php
|
|
@@ -466,6 +466,23 @@ gracefully — it silently deletes all observability for the whole tier. Any cha
|
|
|
466
466
|
`varchar(255)` (`errorMessage`, `subject`) **throws** on `->save()`. Inside the handler's
|
|
467
467
|
`catch(Throwable)` the throw is swallowed and the row is silently dropped — precisely when a
|
|
468
468
|
long message matters most. Truncate to column width before `save()`.
|
|
469
|
+
- **A truncated `errorMessage` reads as "the API returned nothing" — it did not.** When a
|
|
470
|
+
captured message is a `print_r`-dumped api2 response envelope, the `varchar(255)` cut lands
|
|
471
|
+
**exactly where `status`, `error.code` and `messages[]` begin**, so the console shows an
|
|
472
|
+
envelope with no verdict in it and the field looks empty rather than severed. (Observed: a
|
|
473
|
+
pasted error cut at precisely 255 chars sent an investigation after a phantom empty field.)
|
|
474
|
+
Escalation path when the console message stops mid-envelope:
|
|
475
|
+
**(1)** the **full** request/response is in the **client-scoped** `Logs_<Client>.Api`
|
|
476
|
+
(e.g. `Logs_Quad.Api`) — **not** core `Logs.Api`; **(2)** the original inbound payload is in
|
|
477
|
+
`Logs.Event.context` (for an email importer, the attachment content verbatim — this is how a
|
|
478
|
+
poison file was recovered). Pull `context` before the **7-day** `Event` retention purges it.
|
|
479
|
+
- **`_Error::buildContext` redacts the DECODED token but not the RAW response string (OPEN
|
|
480
|
+
security note).** `access_token` is stripped from the decoded response object while the raw
|
|
481
|
+
`rawResponse` / `lastResponse` strings are stored intact, so live bearer JWTs reach
|
|
482
|
+
`Logs.Event.context` in **cleartext**. Two consequences: the raw strings need adding to the
|
|
483
|
+
redactor (in `_underscore/Error.php` **and** `library/app/error/capture.php` together, per the
|
|
484
|
+
gotcha above), and until then **never paste a `Logs.Event.context` or `Logs_<Client>.Api` row
|
|
485
|
+
into a ticket, chat, or knowledge doc** — those rows carry live tokens and customer PII.
|
|
469
486
|
- **Hand-written SQL is what a table rename actually breaks — and it fails *after* the model
|
|
470
487
|
succeeds.** The singular rename left plural literals in hand-written statements across four
|
|
471
488
|
files, so `$issue->save()` succeeded and then `UPDATE Issues …` threw **1146** before the
|
|
@@ -563,6 +580,14 @@ clientUserId). **Neither was built.** As built instead:
|
|
|
563
580
|
|
|
564
581
|
## Change history
|
|
565
582
|
|
|
583
|
+
- 2026-08-06 — Added two debugging/security notes found while unjamming the Quad PO/ASN importer:
|
|
584
|
+
(1) a `varchar(255)`-truncated `errorMessage` severs a dumped api2 envelope exactly at
|
|
585
|
+
`status`/`error.code`/`messages[]`, so escalate to the **client-scoped** `Logs_<Client>.Api`
|
|
586
|
+
for the full envelope and to `Logs.Event.context` (7-day purge) for the original payload;
|
|
587
|
+
(2) OPEN — `_Error::buildContext` redacts `access_token` in the decoded object but not in the
|
|
588
|
+
raw `rawResponse`/`lastResponse` strings, so `Event.context` holds cleartext JWTs; do not paste
|
|
589
|
+
those rows anywhere. (ajean)
|
|
590
|
+
|
|
566
591
|
- 2026-08-06 — Recorded an **open defect**: error capture is entirely dead on sandbox-dev/beta
|
|
567
592
|
because `Logs.Issue` there lacks the **`clickupPriority`** column, so the capture INSERT fails with
|
|
568
593
|
MySQL 1054 and **no** Issue row, Event row, or ClickUp task is written — the only trace is
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
10
|
-
owners: ["jcardinal", "mhammontree", "dfranks", "apeterson", "bala"]
|
|
9
|
+
updated: 2026-08-06
|
|
10
|
+
owners: ["jcardinal", "mhammontree", "dfranks", "apeterson", "bala", "tcox"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
13
13
|
- _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php
|
|
@@ -177,9 +177,29 @@ returnTrackingNumber: {...} }]`, and an IFIU's as `itemFulfillmentItemUnitTracki
|
|
|
177
177
|
`toga25-supply/sync_compasscanada_schema.sql` against `Client_CompassCanada` (local-only — prod
|
|
178
178
|
already has the tables). See the
|
|
179
179
|
[local-DB-refresh-from-beta workflow](../workflows/local-db-refresh-from-beta.md).
|
|
180
|
+
- **⚠ Clients that were not hand-migrated still carry `TableViewJoins` pointing at the DELETED
|
|
181
|
+
RecordFields — and nothing validates them until a user opens the view.** The per-client TableView
|
|
182
|
+
repointing was done one client at a time (Compass, NYCHH, Quad); a client onboarded before it, or
|
|
183
|
+
skipped, keeps joins on dropped ids and its views **500** — first
|
|
184
|
+
`table-views/meta` with *"Model build for primary key id '1431' for `_Model_Core_RecordField` … 0
|
|
185
|
+
rows"*, then the data query with *"Unknown column … in 'on clause'"* once the missing bridge join
|
|
186
|
+
record leaves the alias undefined. **`Client_Elite` has 11 such references across 9 views, in
|
|
187
|
+
production as well as sandbox** — see
|
|
188
|
+
[Elite supply2 TableView config drift](../../../clients/elite/features/supply2-tableview-config-drift.md)
|
|
189
|
+
for the full map and the NYCHH-mirroring fix. Before switching on a supply surface for a client
|
|
190
|
+
onboarded before 2026-06, diff its `TableViewJoins` against a migrated client. Note the same
|
|
191
|
+
audit found `sales-order-shipments` (join 10) referencing dead RecordField **211 in
|
|
192
|
+
`Client_Nychh` too** — that view is broken platform-wide and needs a backend decision, not a
|
|
193
|
+
copy.
|
|
180
194
|
- **Porting the item-fulfillment TableView fix to another client is two independent decisions, not a copy-paste.** The **re-root** (Units 31 → ItemFulfillmentItems 29 + OUTER unit chain) is universally portable and fixes the "0 records" bug everywhere. The **tracking source is client-specific**: Compass writes one tracking number per item fulfillment at the **item level (318)**, so its views read 318. Other clients populate different bridge levels — verify with `SELECT COUNT(*)` per bridge before choosing. As of 2026-06-19, **record 318 is empty in `Client_Nychh` and `Client_Quad`**; their tracking lives at the **IF/shipment level (317)** (covers IFIs: NYCHH 7955/10049 ≈ 79%, Quad 4878/4967 ≈ 98%) and the **unit level (319)** (≈ 7–9% — serialized only). Copying Compass's 318 join into a client that doesn't write 318 yields a structurally-correct view with permanently blank tracking until 318 is backfilled. (NYCHH's pre-fix view pointed at the now-deleted record 41 = old IF-level/package bridge, i.e. it originally intended IF-level 317.)
|
|
181
195
|
|
|
182
196
|
## Change history
|
|
197
|
+
- 2026-08-06 — Recorded that the per-client TableView repointing left **unmigrated clients broken**:
|
|
198
|
+
`Client_Elite` still has 11 references to deleted RecordFields (211/321/932/358/1431) across 9
|
|
199
|
+
views in **prod and sandbox**, so its views 500 on `table-views/meta` and then on the data query
|
|
200
|
+
once the bridge join record (319 / 289) is missing. Added the pre-go-live check (diff a new
|
|
201
|
+
client's `TableViewJoins` against a migrated one) and noted `sales-order-shipments` join 10
|
|
202
|
+
references dead 211 in `Client_Nychh` as well. (tcox)
|
|
183
203
|
- 2026-08-05 — Recorded that the 1.0 `App_Api_Toga2::syncItemFulfillmentFromNetsuite` full reconcile
|
|
184
204
|
now maintains **all three** IF bridges (317/318/319) and, when pruning units/items NetSuite dropped,
|
|
185
205
|
**deletes their bridge links first** because the bridge parent FKs are `RESTRICT` (no cascade) —
|
|
@@ -6,10 +6,12 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06
|
|
10
|
-
owners: ["bala"]
|
|
9
|
+
updated: 2026-08-06
|
|
10
|
+
owners: ["bala", "tcox"]
|
|
11
11
|
files: []
|
|
12
|
-
related:
|
|
12
|
+
related:
|
|
13
|
+
- ./tracking-number-bridges.md
|
|
14
|
+
- ../../../clients/elite/features/supply2-tableview-config-drift.md
|
|
13
15
|
---
|
|
14
16
|
|
|
15
17
|
## Summary
|
|
@@ -82,7 +84,19 @@ None — uniform. The linkage structure is framework-level and identical across
|
|
|
82
84
|
sales order (over/under-attribution), because attribution is by PO line, not by the
|
|
83
85
|
fulfillment that shipped the unit.
|
|
84
86
|
|
|
87
|
+
- **A 500 here is usually stale per-client view config, not missing data.** This view's joins were
|
|
88
|
+
repointed onto the bridge records (319 tracking, 289 `SalesOrderItems_PurchaseOrderItems`) per
|
|
89
|
+
client. A client that never got that migration 500s on `table-views/meta` (*"Model build for
|
|
90
|
+
primary key id '1431' for `_Model_Core_RecordField` … 0 rows"*) and then on the data query
|
|
91
|
+
(*"Unknown column `SalesOrderItems_PurchaseOrderItems.salesOrderItemId` in 'on clause'"*).
|
|
92
|
+
Confirmed on `Client_Elite` (view 17) — see
|
|
93
|
+
[Elite TableView config drift](../../../clients/elite/features/supply2-tableview-config-drift.md).
|
|
94
|
+
Check the view config before investigating the linkage paths above.
|
|
95
|
+
|
|
85
96
|
## Change history
|
|
97
|
+
- 2026-08-06 — Added the gotcha that a 500 on this view is usually **stale per-client
|
|
98
|
+
`TableViewJoins`** left behind by the bridge migrations (records 319 / 289), not a data-linkage
|
|
99
|
+
problem; confirmed on `Client_Elite` view 17. (tcox)
|
|
86
100
|
- 2026-06-22 — Documented the two-path units linkage model behind the
|
|
87
101
|
units-for-items-for-purchase-orders view and why the receipt-only traversal hides
|
|
88
102
|
fulfillment-only units. (bala)
|
|
@@ -6,8 +6,8 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["apeterson", "mhammontree"]
|
|
9
|
+
updated: 2026-08-06
|
|
10
|
+
owners: ["apeterson", "mhammontree", "tcox"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Config/
|
|
13
13
|
- _underscore/Loader.php
|
|
@@ -59,6 +59,15 @@ Order matters — dump first so a failed dump never wipes a local schema it can'
|
|
|
59
59
|
|
|
60
60
|
## Gotchas / known issues
|
|
61
61
|
|
|
62
|
+
- **"Beta" and "dev-sandbox" are the same cluster — confirm it in `Core.DatabaseHosts`, not in old
|
|
63
|
+
migrations.** `Core.DatabaseHosts` maps environment **`beta`** for `Client_*` schemas to the
|
|
64
|
+
**sandbox-dev** RDS cluster (`sandbox-dev.cluster-…us-east-1.rds.amazonaws.com`,
|
|
65
|
+
a.k.a. `dev.sandbox.database.togahub.com`). Practical consequences: a frontend running locally
|
|
66
|
+
against `VITE_API=https://api.beta.togahub.com/v2` authenticates and reads **dev-sandbox** data,
|
|
67
|
+
so seeding a test user there means inserting into the dev-sandbox `Client_<Id>`; and in the DB MCP
|
|
68
|
+
the environment to query is **`dev-sandbox`** (`client-beta` exposes no schemas). 2024-era
|
|
69
|
+
onboarding SQL that shows beta on a us-west-2 `client-cluster` is stale — that has been
|
|
70
|
+
consolidated.
|
|
62
71
|
- **Beta `admin` lacks `RELOAD` → `--single-transaction` fails.** The managed sandbox account
|
|
63
72
|
cannot `FLUSH TABLES WITH READ LOCK`, so the default `mysqldump --single-transaction` dies with
|
|
64
73
|
`Couldn't execute 'FLUSH TABLES WITH READ LOCK': Access denied ... (1045)`. **Fix:** drop
|
|
@@ -105,6 +114,10 @@ Order matters — dump first so a failed dump never wipes a local schema it can'
|
|
|
105
114
|
|
|
106
115
|
## Change history
|
|
107
116
|
|
|
117
|
+
- 2026-08-06 — Recorded that environment **`beta` resolves to the dev-sandbox cluster** in
|
|
118
|
+
`Core.DatabaseHosts` for `Client_*` schemas, so an app pointed at `api.beta.togahub.com` is
|
|
119
|
+
reading dev-sandbox data and the DB MCP environment to query is `dev-sandbox` (not
|
|
120
|
+
`client-beta`, which exposes no schemas). (tcox)
|
|
108
121
|
- 2026-07-27 — TRUE-79191: added the reverse-drift gotcha — pulling updated `_underscore` models from
|
|
109
122
|
prod **without** applying the matching `dbchanges2` migrations locally throws `1054 Unknown column`
|
|
110
123
|
(e.g. `Items.isFulfillable` from the 2026-07-17 migration); apply pending migrations or reset the
|
|
@@ -6,8 +6,8 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: [mhammontree, tcox, jcardinal]
|
|
9
|
+
updated: 2026-08-10
|
|
10
|
+
owners: [mhammontree, tcox, jcardinal, ajean]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
13
13
|
- api2/Component/Api/V2/Response/Response.php
|
|
@@ -97,6 +97,46 @@ migration instead of re-deriving it. All field/script ACL rows live in the **CLI
|
|
|
97
97
|
DB fully migrated. On a local dev box with PHP opcache disabled a branch switch takes effect
|
|
98
98
|
immediately (files are read fresh per request) — no Apache restart needed.
|
|
99
99
|
|
|
100
|
+
7. **`EV-12` can just be a mis-prefixed field name — check native vs. custom before anything
|
|
101
|
+
else.** The `c_` prefix is **not** cosmetic: `c_foo` and `foo` are different field names, and
|
|
102
|
+
a `c_`-prefixed name that has no custom-field registration simply does not exist, so api2
|
|
103
|
+
rejects **the entire write** (not just that key). This bites hardest on records where *some*
|
|
104
|
+
sibling keys genuinely are custom — e.g. `Client_Quad.Units` carries custom
|
|
105
|
+
`c_hostIdentifier` / `c_pkIdentifier` next to **native** `serialNumber`, `assetTag` and
|
|
106
|
+
`macAddress`, and sending `c_macAddress` failed the whole item fulfillment with `EV-12`.
|
|
107
|
+
Diagnose by running `SHOW COLUMNS` on the tenant table: a real column name → send it
|
|
108
|
+
**unprefixed**; no column → it must be a registered custom field to be sendable at all.
|
|
109
|
+
Verify **every** key in a nested payload, not just the one you changed.
|
|
110
|
+
|
|
111
|
+
8. **`EV-12` on a *lookup-by-name* means ambiguous, not missing.** A nested payload that
|
|
112
|
+
identifies a parent by a non-unique field (e.g. `shippingMethod: {name: 'Courier'}` where
|
|
113
|
+
the tenant's `ShippingMethods` holds two `Courier` rows under different carriers) cannot
|
|
114
|
+
resolve, and reads as a validation failure on the child (`trackingNumber`). Before hunting
|
|
115
|
+
for a missing record, check for **duplicates** — especially where the caller has its own
|
|
116
|
+
create-if-missing path, which is how the duplicate usually got there. Fix by narrowing the
|
|
117
|
+
identifier (nest the carrier), not by re-creating the parent.
|
|
118
|
+
|
|
119
|
+
9. **Turn an `EO-1` into a stack trace via `error.id`.** The 500 envelope carries
|
|
120
|
+
`error.id` like `"8K-6"`. The part **before the dash** is `Logs.Issue.reference` —
|
|
121
|
+
`SELECT * FROM Logs.Issue WHERE reference = '8K'` gives the full `errorMessage` and stack
|
|
122
|
+
trace. The envelope itself tells you nothing useful; always go to `Logs.Issue`.
|
|
123
|
+
Corollary: if `transactionId`, `authority` and `audience` are **all NULL** in the error
|
|
124
|
+
envelope, the request failed **before client context was established**.
|
|
125
|
+
|
|
126
|
+
10. **A "wrong client" bearer token looks like a payload bug.** POSTing a **Rate**-shaped
|
|
127
|
+
entitlement payload while holding an **AIG** token routes into
|
|
128
|
+
`_Model_Aig_Entitlement::prePost()` and dies with
|
|
129
|
+
`Undefined property: stdClass::$partNumber` — which reads as a malformed payload, not an
|
|
130
|
+
auth problem. **Switching Postman folders does NOT change the token**: it lives in an
|
|
131
|
+
environment variable and must be re-minted by re-running the auth request. Cheap
|
|
132
|
+
verification before debugging anything else — send any `GET` and check
|
|
133
|
+
**`audience.client`** in the response envelope.
|
|
134
|
+
|
|
135
|
+
11. **api2 matches nested objects by `uuid` ONLY.** Browser payloads often identify a related
|
|
136
|
+
record by a client-custom key (e.g. Rate's `c_borrowerId` for the customer). Replaying such
|
|
137
|
+
a payload server-side will **create a duplicate** rather than match the existing record.
|
|
138
|
+
When replaying or backfilling, rewrite every nested reference to use `uuid`.
|
|
139
|
+
|
|
100
140
|
## BREAKING (2026-07-30): `error` is now an object, not a bare string
|
|
101
141
|
|
|
102
142
|
The V2 envelope's `error` changed from a bare string (`"EO-1"`) to an **object**:
|
|
@@ -182,6 +222,21 @@ Full mechanics:
|
|
|
182
222
|
|
|
183
223
|
## Change history
|
|
184
224
|
|
|
225
|
+
- 2026-08-10 — TRUE-80575: added diagnosis notes 9-11 from debugging a Rate entitlement
|
|
226
|
+
replay — (9) `error.id`'s prefix before the dash **is** `Logs.Issue.reference`, so an `EO-1`
|
|
227
|
+
500 becomes a real stack trace via `SELECT * FROM Logs.Issue WHERE reference = '…'`, and
|
|
228
|
+
all-NULL `transactionId`/`authority`/`audience` means the failure preceded client context;
|
|
229
|
+
(10) a **wrong-client bearer token** masquerades as a payload bug (Rate payload + AIG token →
|
|
230
|
+
`Undefined property: stdClass::$partNumber` from `_Model_Aig_Entitlement::prePost()`), and
|
|
231
|
+
Postman folders do not switch tokens — verify `audience.client` on any GET first; (11) api2
|
|
232
|
+
matches nested objects by **`uuid` only**, so replaying a browser payload that references a
|
|
233
|
+
record by a custom key (`c_borrowerId`) creates a duplicate. (mhammontree)
|
|
234
|
+
- 2026-08-06 — Added two `EV-12` diagnosis notes from the Quad PO/ASN importer: (6) a
|
|
235
|
+
mis-prefixed field name (`c_macAddress` for the **native** `Units.macAddress` column) makes
|
|
236
|
+
api2 reject the whole write — verify every nested key against `SHOW COLUMNS`, since custom
|
|
237
|
+
and native keys sit side by side on the same record; (7) `EV-12` on a lookup-by-name means
|
|
238
|
+
the parent is **ambiguous** (duplicate `ShippingMethods` rows named `Courier`), not missing —
|
|
239
|
+
narrow the identifier rather than re-creating the parent. (ajean)
|
|
185
240
|
- 2026-08-04 — Recorded the catch-order trap from `_Exception_Business` now extending
|
|
186
241
|
`_Exception_Validation` (business conditions answer 4xx and are still captured, but any
|
|
187
242
|
`catch (_Exception_Validation …)` must test business **first** or they go unrecorded), and the
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
| [Cart Bundle Submission & the bundleUuid Identity Contract](features/cart-bundle-submission-and-identity.md) | How cart **bundles** (kits) are turned into `SalesOrderItems` when a cart is submitted or an existing order is edited, and the **identity-field contract** every | src/api/syncSalesOrderItemsFromLocalStorageCartToApi.ts, src/utils/formatSalesOrderBundlesFromApi.ts, src/stores/useCartStoreZu.ts, src/pages/OrderDetails/helpers/formatSalesOrderDataFromLocalStorage.ts, src/pages/OrderDetails/view/components/OrderItems.tsx |
|
|
7
7
|
| [Cart Notification Emails — duplicate prevention](features/cart-notification-emails.md) | On the cart "Notifications" section a user can add CC email addresses to an order. | src/pages/Cart/CartPage.tsx, src/pages/Cart/view/cartForm/CartForm.tsx, src/stores/useEmailOptionsStore.ts, src/stores/useCartSalesQuoteZu.ts, src/pages/Cart/viewModel/FIELDS/*/*/*/CARTPAGE.ts |
|
|
8
8
|
| [Cart Page — config-driven form architecture (current state + planned refactor)](features/cart-page-config-architecture.md) | The Cart page (`src/pages/Cart/`) is the most config-heavy page in `toga2-commerce`. | src/pages/Cart/CartPage.tsx, src/pages/Cart/view/cartForm/CartForm.tsx, src/pages/Cart/view/cartForm/CartFormSection.tsx, src/pages/Cart/view/cartForm/CartFormRenderer.tsx, src/pages/Cart/view/EditCart.tsx, src/pages/Cart/view/EditOrder.tsx, src/pages/Cart/viewModel/useEditOrderOrEditCartViewModel.ts, src/pages/Cart/viewModel/FIELDS/*/*/*/CARTPAGE.ts, src/hooks/useAssignClientFields.ts |
|
|
9
|
-
| [Client Fields — per-tenant / language / role content & config](features/client-fields.md) | Almost no user-facing text, field layout, or page config is hard-coded in `toga2-commerce`. | src/pages/Account/view/MySettingsView.tsx, src/fieldsConfig/index.ts, src/fieldsConfig/getClientLoginFields.ts, src/fieldsConfig/clientFields/COMPASS.json, src/fieldsConfig/clientFields/COMPASSCANADA.json, src/fieldsConfig/clientFields/QUAD.json, src/pages/Cart/api/CartApi.ts, src/hooks/useAuthenticationFlow.ts, src/contexts/AuthContext.tsx, src/pages/Login/viewModel/useLoginPageViewModel.ts, src/hooks/useAssignClientFields.ts, src/hooks/useDynamicConditionalFieldOptions.ts, src/stores/useFieldsStore.ts, src/components/BaseDetailField/BaseDetailField.tsx, src/components/NavIcons/NavIconItem.tsx, src/components/Submenus/AlertSubmenu.tsx, src/components/Submenus/types.ts, src/pages/Account/AccountPage.tsx, src/pages/Account/view/MyOrdersView.tsx, src/pages/GetSupport/GetSupportPage.tsx, src/pages/GetSupport/viewModel/useGetSupportViewModel.ts, src/queries/queries.ts, src/App.tsx, src/pages/Filter/FilterPage.tsx, src/pages/Filter/viewModel/FIELDS/COMPASS/ENGLISH/USER/FILTERPAGEFIELDS.json |
|
|
9
|
+
| [Client Fields — per-tenant / language / role content & config](features/client-fields.md) | Almost no user-facing text, field layout, or page config is hard-coded in `toga2-commerce`. | src/pages/Account/view/MySettingsView.tsx, src/contexts/helpers/getLoginSettings.ts, src/pages/Account/viewModel/useAccountViewModel.ts, src/fieldsConfig/index.ts, src/fieldsConfig/getClientLoginFields.ts, src/fieldsConfig/clientFields/COMPASS.json, src/fieldsConfig/clientFields/COMPASSCANADA.json, src/fieldsConfig/clientFields/QUAD.json, src/pages/Cart/api/CartApi.ts, src/hooks/useAuthenticationFlow.ts, src/contexts/AuthContext.tsx, src/pages/Login/viewModel/useLoginPageViewModel.ts, src/hooks/useAssignClientFields.ts, src/hooks/useDynamicConditionalFieldOptions.ts, src/stores/useFieldsStore.ts, src/components/BaseDetailField/BaseDetailField.tsx, src/components/NavIcons/NavIconItem.tsx, src/components/Submenus/AlertSubmenu.tsx, src/components/Submenus/types.ts, src/pages/Account/AccountPage.tsx, src/pages/Account/view/MyOrdersView.tsx, src/pages/GetSupport/GetSupportPage.tsx, src/pages/GetSupport/viewModel/useGetSupportViewModel.ts, src/queries/queries.ts, src/App.tsx, src/pages/Filter/FilterPage.tsx, src/pages/Filter/viewModel/FIELDS/COMPASS/ENGLISH/USER/FILTERPAGEFIELDS.json |
|
|
10
10
|
| [Config-Driven Expedited Shipping Gating (Cart)](features/expedited-shipping-gating.md) | On the toga2-commerce **Cart** page, expedited shipping options (**"2nd Day EOB"** and **"Next Day Air"**) are only offered in the *Shipping Method* dropdown wh | toga2-commerce/src/pages/Cart/helpers/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/viewModel/FIELDS/shared/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/view/cartForm/CartForm.tsx, toga2-commerce/src/pages/Cart/CartPage.tsx |
|
|
11
11
|
| [Filter / Search-Results Page & the Two Search Entry Points](features/filter-search-results-page.md) | The storefront has **two distinct search entry points that render the same card component through completely different code paths and different FIELDS files**. | src/pages/Filter/FilterPage.tsx, src/pages/Filter/viewModel/useFilterViewModel.ts, src/pages/Filter/viewModel/FIELDS/COMPASS/ENGLISH/USER/FILTERPAGEFIELDS.json, src/pages/Filter/viewModel/FIELDS/COMPASSCANADA/FRENCH/USER/FILTERPAGEFIELDS.json, src/pages/Filter/viewModel/FIELDS/QUAD/ENGLISH/BUYER/FILTERPAGEFIELDS.json, src/components/Header/Header.tsx, src/pages/Home/view/components/BundlesSection.tsx, src/pages/Home/viewModel/FIELDS/COMPASS/ENGLISH/USER/HOMEPAGEFIELDS.json, src/components/Cards/BundleViewCard.tsx, src/utils/renderBadge.tsx, src/hooks/useAssignClientFields.ts |
|
|
12
12
|
| [Multi-Tenant Resolution & Theming](features/multi-tenant-theming.md) | `toga2-commerce` serves multiple clients from one codebase. | src/themeConfig/themes.json, src/themeConfig/ThemeContext.tsx, src/themeConfig/types.ts, src/components/ThemeSwitcher/ThemeSwitcher.tsx, src/components/AuthLayout/AuthLayout.tsx, src/api/axiosInstance.ts, src/contexts/AuthContext.tsx, tailwind.config.js |
|
|
@@ -6,10 +6,12 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-08-06
|
|
10
10
|
owners: ["apeterson", "tcox", "bala"]
|
|
11
11
|
files:
|
|
12
12
|
- src/pages/Account/view/MySettingsView.tsx
|
|
13
|
+
- src/contexts/helpers/getLoginSettings.ts
|
|
14
|
+
- src/pages/Account/viewModel/useAccountViewModel.ts
|
|
13
15
|
- src/fieldsConfig/index.ts
|
|
14
16
|
- src/fieldsConfig/getClientLoginFields.ts
|
|
15
17
|
- src/fieldsConfig/clientFields/COMPASS.json
|
|
@@ -68,7 +70,10 @@ resolved per tenant, per language, and per role**. There are **two layers**:
|
|
|
68
70
|
```
|
|
69
71
|
|
|
70
72
|
- `languageSwitcher.isEnabled` — **only COMPASSCANADA is `true`** (English/French). COMPASS and
|
|
71
|
-
QUAD are `false`.
|
|
73
|
+
QUAD are `false`. This is a **hard branch-reachability fact**, not a preference: it gates the
|
|
74
|
+
*entire first branch* of `getLoginSettings.ts`, so that branch is **unreachable for Compass USA**
|
|
75
|
+
and QUAD. See "Login-settings branches" below before judging whether a `getLoginSettings` change
|
|
76
|
+
affects US or CA.
|
|
72
77
|
- `userApiFields` — the allow-list of user columns fetched at login; importantly it includes the
|
|
73
78
|
tenant's **role flags**: COMPASS/COMPASSCANADA carry `_isAdmin`, `_isSupervisor`, `_isSuperAdmin`;
|
|
74
79
|
QUAD carries `_isGlobalAdmin`, `_isItShopper`, `_isBuyer`. All include `_personaIds`. This key is
|
|
@@ -91,6 +96,33 @@ Resolved by **`src/fieldsConfig/getClientLoginFields.ts`** — a `switch(client)
|
|
|
91
96
|
**default is `COMPASS`** (not DEFAULT). Called from `AuthContext` as `getClientLoginFields(host)`.
|
|
92
97
|
The `languageSwitcher.isEnabled` value feeds the login-settings branch in `getLoginSettings.ts`.
|
|
93
98
|
|
|
99
|
+
### Login-settings branches (`src/contexts/helpers/getLoginSettings.ts`)
|
|
100
|
+
|
|
101
|
+
The top-level shape is `if (languageSwitcher.isEnabled) … else if (isPersonaSwitcherEnabled) … else …`,
|
|
102
|
+
and each leaf calls `setUser({ ...userData, contactUuid, _personaOptions })`. Two facts decide who is
|
|
103
|
+
affected by an edit here:
|
|
104
|
+
|
|
105
|
+
- **Branch 1 (language) is COMPASSCANADA-only** — COMPASS/QUAD `languageSwitcher.isEnabled` is
|
|
106
|
+
`false`, so a change confined to branch 1 **cannot** affect Compass USA.
|
|
107
|
+
- **Within Canada, the "no saved language" sub-branch is the common path.** Only **69 of 4,655**
|
|
108
|
+
active Canada users have a stored language preference (`UserGlobalSettings` page `language`), so
|
|
109
|
+
**~98% of Canada logins** take the `else` sub-branch that opens the settings modal.
|
|
110
|
+
|
|
111
|
+
> **Known defect (production) — Canada My Account shows a blank Persona field.** That
|
|
112
|
+
> "no saved language" sub-branch was the **only one of the seven `setUser` calls** in
|
|
113
|
+
> `getLoginSettings` not passing `_personaOptions`. All **8** Canada `MYACCOUNTFIELDS` configs
|
|
114
|
+
> (EN + FR × 4 roles) display a Persona field, and
|
|
115
|
+
> `src/pages/Account/viewModel/useAccountViewModel.ts` (~line 311) guards on `user._personaOptions`
|
|
116
|
+
> before populating it. **Nothing else sets it for those users** — the persona switcher is QUAD-only
|
|
117
|
+
> via `settingsKey`, and `NavIconList` only runs under View-As — so the field renders blank. This is
|
|
118
|
+
> a real defect, not cosmetic: Canada has 34 personas, 7,017 users with personas, 1,461 with more
|
|
119
|
+
> than one. A one-line fix exists on `TRUE-80672` / `_beta` (commit `5313a9b1`, "populate
|
|
120
|
+
> `_personaOptions` on the no-language login path") but is **not in `_production`** — verify the
|
|
121
|
+
> branch before assuming it is fixed. Related: the persisted-store caveat in
|
|
122
|
+
> [Language-sensitive React Query caches](#language-sensitive-react-query-caches-language_sensitive_query_keys)
|
|
123
|
+
> — `_personaOptions` lives in the persisted Zustand user store, so if it is never written at login,
|
|
124
|
+
> no cache invalidation will heal it.
|
|
125
|
+
|
|
94
126
|
### `fetchUsersApiFields` / `fetchUserApiFields` and the config-driven Cart fetches (C6)
|
|
95
127
|
|
|
96
128
|
`fetchUsers()` in `src/pages/Cart/api/CartApi.ts` no longer hardcodes its field list. It resolves it
|
|
@@ -378,6 +410,14 @@ on switch.
|
|
|
378
410
|
slip. Do not "reconcile" the two lists.
|
|
379
411
|
|
|
380
412
|
## Change history
|
|
413
|
+
- 2026-08-06 — Added a **Login-settings branches** section for `getLoginSettings.ts`: branch 1 is
|
|
414
|
+
gated by `languageSwitcher.isEnabled` and is therefore **unreachable for COMPASS/QUAD**, and within
|
|
415
|
+
Canada only **69 of 4,655** active users have a stored language, so ~98% take the "no saved
|
|
416
|
+
language" sub-branch. Recorded the resulting **production defect** — that sub-branch was the only
|
|
417
|
+
one of seven `setUser` calls omitting `_personaOptions`, so Canada My Account renders a blank
|
|
418
|
+
Persona field (all 8 COMPASSCANADA `MYACCOUNTFIELDS` show it; `useAccountViewModel` guards on
|
|
419
|
+
`user._personaOptions`; nothing else populates it). Fix `5313a9b1` is on `TRUE-80672`/`_beta` only,
|
|
420
|
+
**not `_production`**. (bala)
|
|
381
421
|
- 2026-07-28 — Documented two failure modes found while fixing the header-search kit card: (1) there is
|
|
382
422
|
**no fallback for an individual key** inside a present page JSON — a missing key silently yields
|
|
383
423
|
`undefined` (empty uncolored badge, `"3 undefineds"`), and key names differ per page
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
| [TOGa Supply (toga2-supply) Architecture](architecture.md) | `toga2-supply` is the **React + Vite frontend** for TOGa Supply — warehouse fulfillment tooling (shipment selection, fulfill & ship against carrier APIs, NetSui | toga2-supply/src/api/toga.ts, toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx |
|
|
6
6
|
| [Fulfill & Ship](features/fulfill-and-ship.md) | Fulfill & Ship lets a warehouse user select sales-order line items, enter serials, pick a carrier/method, and in one action: create the Item Fulfillment records | _underscore/Model/Client/Measure.php, _underscore/Model/Client/TrackingNumber.php, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/getEditShipmentFormOptions.ts, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/validateFormOnSubmit.ts, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/checkDimensions.ts, toga2-supply/src/components/ui/Tables/BasicTable/BasicTable.tsx, toga2-supply/src/components/ui/Tables/types.ts, toga2-supply/src/components/ui/GoogleMapsLink.tsx, toga2-supply/src/pages/ShipmentItems/view/forms/ShipmentItemsTable.tsx, toga2-supply/src/pages/ShipmentItems/api/ShipmentItemsApi.ts, toga2-supply/src/pages/ShipmentItems/types.ts, toga2-supply/src/pages/EditShipment/viewModel/FIELDS/RETURNLABELFIELDS.json, toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx, toga2-supply/src/components/ui/BaseInput/UnitSelect.tsx, toga2-supply/src/pages/EditShipment/view/modals/SerialNumbersModal.tsx, toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx, toga2-supply/src/pages/EditShipment/view/components/SelectedShipmentItemsTable.tsx, toga2-supply/src/pages/EditShipment/view/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/src/pages/EditShipment/viewModel/FIELDS/DUMMYUPDATESHIPMENTFIELDS.json, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/tailwind.config.cjs, toga2-supply/src/pages/EditShipment/view/modals/ReturnShippingModal.tsx, toga2-supply/src/styles/index.scss, toga2-supply/src/components/ui/BaseInput/BaseInput.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/EditShipment/viewModel/signatureTypes.ts, toga2-supply/src/pages/EditShipment/view/modals/SelectReturnAddressModal.tsx, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/formatShipmentData.ts, toga2-supply/src/pages/EditShipment/types.ts, toga2-supply/src/pages/Shipments/view/ShipmentsPage.tsx, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx, toga2-supply/src/pages/Shipments/api/ShipmentsApi.ts, toga2-supply/src/pages/Shipments/types.ts, toga2-supply/src/pages/FulfilledShipments/view/FulfilledShipmentsPage.tsx, toga2-supply/src/components/ui/CardTable/CardTable.tsx, toga2-supply/src/components/ui/CardTable/types.ts, toga2-supply/src/assets/pen-line.svg, _underscore/Model/Client/ItemFulfillment.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Component/Library/Carriers/Ups/Ups.php |
|
|
7
7
|
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-supply` (React + Vite) builds and deploys on **AWS Amplify**. | toga2-supply/amplify.yml, toga2-supply/.gitattributes, toga2-supply/.github/workflows/sync-stage-environments.yml, toga2-supply/.env.qc-security |
|
|
8
|
-
| [Onboarding a client to the supply2 frontend (host-scoping)](workflows/client-host-scoping.md) | How a new tenant becomes a "client" in the `toga2-supply` frontend. | toga2-supply/src/stores/useHostNameStore.ts, toga2-supply/src/hooks/useHostName.tsx, toga2-supply/src/hooks/usePageDetails.tsx, toga2-supply/src/hooks/usePageListDetails.tsx, toga2-supply/src/components/layout/SlideMenu/SlideMenu.tsx, toga2-supply/src/utils/handleClientAuthentication.ts, toga2-supply/src/App.tsx, toga2-supply/src/pages/Inventory/viewModel/FIELDS/DUMMYGROUPOPTIONS.ts, toga2-supply/src/pages/Inventory/listing/InventoryPage.tsx, toga2-supply/src/api/toga.ts |
|
|
8
|
+
| [Onboarding a client to the supply2 frontend (host-scoping)](workflows/client-host-scoping.md) | How a new tenant becomes a "client" in the `toga2-supply` frontend. | toga2-supply/src/stores/useHostNameStore.ts, toga2-supply/src/utils/resolveClientHostName.ts, toga2-supply/src/hooks/useHostName.tsx, toga2-supply/src/hooks/usePageDetails.tsx, toga2-supply/src/hooks/usePageListDetails.tsx, toga2-supply/src/components/layout/SlideMenu/SlideMenu.tsx, toga2-supply/src/utils/handleClientAuthentication.ts, toga2-supply/src/App.tsx, toga2-supply/src/pages/Inventory/viewModel/FIELDS/DUMMYGROUPOPTIONS.ts, toga2-supply/src/pages/Inventory/viewModel/FIELDS/INVENTORYPAGEFIELDS.ts, toga2-supply/src/pages/Inventory/listing/InventoryPage.tsx, toga2-supply/src/pages/Inventory/listing/InventorySubTablePage.tsx, toga2-supply/src/api/toga.ts, toga2-supply/package.json |
|