toga-ai 1.0.314 → 1.0.316
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: elite
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-07-10
|
|
10
10
|
owners: ["snaredla"]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
@@ -165,5 +165,49 @@ Pass `true` for the dedup GET — errors must not silently empty the dedup array
|
|
|
165
165
|
Each ticket sync is wrapped in `try/catch` — exceptions are sent to Sentry and the run
|
|
166
166
|
continues with the next ticket. A single bad ticket does not abort the whole sync.
|
|
167
167
|
|
|
168
|
+
## Elite-specific sync behaviour
|
|
169
|
+
|
|
170
|
+
### Department assignment (TOGaDesk → TOGA 2)
|
|
171
|
+
Elite tickets use `c_togadeskDepartmentId` on `Client_Elite.TicketTeams` to reverse-lookup the correct Toga2 team from TOGaDesk's actual `departmentid`. CF84 (custom field storing team name) is **never** updated when an agent manually transfers a ticket between TOGaDesk departments via the UI — so it cannot be used as the sync source for team.
|
|
172
|
+
|
|
173
|
+
Lookup in `syncTogaDesk1TicketIntoToga2Ticket`:
|
|
174
|
+
```php
|
|
175
|
+
$eliteTeamLookupOptions = [
|
|
176
|
+
'where' => ['and' => [['c_togadeskDepartmentId' => ['=' => (int) $togadeskTicket->departmentid]]]]
|
|
177
|
+
];
|
|
178
|
+
$eliteTeamResult = App_Api_Toga2::send(..., 'GET', '/ticket-teams', null, $eliteTeamLookupOptions, ...);
|
|
179
|
+
if ($eliteTeamResult->meta->totalRecordCount == 1) {
|
|
180
|
+
$payload['ticketTeam'] = ['uuid' => $eliteTeamResult->data->ticketTeams[0]->uuid];
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Pending DB migration** — `c_togadeskDepartmentId INT NULL` must be added to `Client_Elite.TicketTeams` with team→dept mappings before this lookup works.
|
|
185
|
+
|
|
186
|
+
### Timestamp guard (prevents overwriting Freshservice changes)
|
|
187
|
+
Before pushing any field update (priority, status, category, group, type) to Freshservice, the sync compares timestamps:
|
|
188
|
+
```php
|
|
189
|
+
$eliteOriginalUpdatedAt = ... strtotime($eliteTicket->updated_at) ...; // captured BEFORE reply POSTs
|
|
190
|
+
$togadeskUpdatedAt = strtotime($togadeskLastModified);
|
|
191
|
+
$togadeskUpdateIsMoreRecent = ($togadeskUpdatedAt > $eliteOriginalUpdatedAt);
|
|
192
|
+
```
|
|
193
|
+
Nothing is written to Freshservice unless TOGaDesk is strictly newer. Safe to reset `TOGA_LAST_TICKET_INTEGRATION_DATETIME` for re-sync — Freshservice changes are protected.
|
|
194
|
+
|
|
195
|
+
### Reply status badge mapping
|
|
196
|
+
`tickets_replies.newStatus` is set from `$ticketNote->ticketStage->code` when a reply is synced in. Elite code → TOGaDesk status:
|
|
197
|
+
|
|
198
|
+
| Freshservice code | TOGaDesk status |
|
|
199
|
+
|---|---|
|
|
200
|
+
| 2 | Open |
|
|
201
|
+
| 3 | Awaiting User |
|
|
202
|
+
| 6 | In Progress |
|
|
203
|
+
| 4 | Resolved |
|
|
204
|
+
| 5 | Closed |
|
|
205
|
+
|
|
206
|
+
**Gotcha:** `case 2` was previously mapped to `STATUS__NEW_TICKET` (wrong) — fixed 2026-07-10. Replies from Freshservice with status "Open" were showing "New Ticket" badge in TOGaDesk.
|
|
207
|
+
|
|
208
|
+
### Variable name pollution gotcha
|
|
209
|
+
The Elite team lookup block inside `syncTogaDesk1TicketIntoToga2Ticket` must use its own dedicated options variable (`$eliteTeamLookupOptions`) — never reuse `$method`, `$route`, `$payload`, or `$options`. Those shared variables are consumed by the `send()` call immediately after the block. Reusing them causes the wrong endpoint to be called, resulting in a Sentry crash: `Undefined property: stdClass::$tickets`.
|
|
210
|
+
|
|
168
211
|
## Change history
|
|
169
212
|
- 2026-06-10 — Documented `App_Api_Toga2::syncWithTogadesk` orchestration (bidirectional TOGA 2 ↔ TOGaDesk ticket/note/file sync, watermarks, `ticketNoteFiles` gotcha). (snaredla)
|
|
213
|
+
- 2026-07-10 — Added Elite-specific section: department reverse-lookup via `c_togadeskDepartmentId`, timestamp guard, reply status badge mapping fix (`case 2` → Open), variable pollution gotcha. (snaredla)
|
|
@@ -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/fieldsConfig/index.ts, src/fieldsConfig/getClientLoginFields.ts, src/fieldsConfig/clientFields/COMPASS.json, src/fieldsConfig/clientFields/COMPASSCANADA.json, src/fieldsConfig/clientFields/QUAD.json, src/hooks/useAssignClientFields.ts, src/hooks/useDynamicConditionalFieldOptions.ts, src/stores/useFieldsStore.ts, src/components/BaseDetailField/BaseDetailField.tsx |
|
|
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/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/useAssignClientFields.ts, src/hooks/useDynamicConditionalFieldOptions.ts, src/stores/useFieldsStore.ts, src/components/BaseDetailField/BaseDetailField.tsx |
|
|
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
|
| [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 |
|
|
12
12
|
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-commerce` (React + Vite, "commerce2-react") builds and deploys on **AWS Amplify**. | toga2-commerce/amplify.yml, toga2-commerce/.gitattributes, toga2-commerce/package.json, toga2-commerce/.github/workflows/sync-stage-environments.yml |
|
|
@@ -6,14 +6,15 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["apeterson"]
|
|
9
|
+
updated: 2026-07-10
|
|
10
|
+
owners: ["apeterson", "tcox"]
|
|
11
11
|
files:
|
|
12
12
|
- src/fieldsConfig/index.ts
|
|
13
13
|
- src/fieldsConfig/getClientLoginFields.ts
|
|
14
14
|
- src/fieldsConfig/clientFields/COMPASS.json
|
|
15
15
|
- src/fieldsConfig/clientFields/COMPASSCANADA.json
|
|
16
16
|
- src/fieldsConfig/clientFields/QUAD.json
|
|
17
|
+
- src/pages/Cart/api/CartApi.ts
|
|
17
18
|
- src/hooks/useAssignClientFields.ts
|
|
18
19
|
- src/hooks/useDynamicConditionalFieldOptions.ts
|
|
19
20
|
- src/stores/useFieldsStore.ts
|
|
@@ -43,7 +44,8 @@ resolved per tenant, per language, and per role**. There are **two layers**:
|
|
|
43
44
|
```json
|
|
44
45
|
{
|
|
45
46
|
"languageSwitcher": { "uuid": "1", "isEnabled": true|false },
|
|
46
|
-
"userApiFields": ["firstName", "lastName", "email", "uuid", "_isAdmin", ...]
|
|
47
|
+
"userApiFields": ["firstName", "lastName", "email", "uuid", "_isAdmin", ...],
|
|
48
|
+
"fetchUsersApiFields": ["firstName", "lastName", "email", "uuid", ...]
|
|
47
49
|
}
|
|
48
50
|
```
|
|
49
51
|
|
|
@@ -52,11 +54,31 @@ resolved per tenant, per language, and per role**. There are **two layers**:
|
|
|
52
54
|
- `userApiFields` — the allow-list of user columns fetched at login; importantly it includes the
|
|
53
55
|
tenant's **role flags**: COMPASS/COMPASSCANADA carry `_isAdmin`, `_isSupervisor`, `_isSuperAdmin`;
|
|
54
56
|
QUAD carries `_isGlobalAdmin`, `_isItShopper`, `_isBuyer`. All include `_personaIds`.
|
|
57
|
+
- `fetchUsersApiFields` — the allow-list of user columns fetched by the **view-as user search**
|
|
58
|
+
(`fetchUsers()` in `src/pages/Cart/api/CartApi.ts`, driven from `NavIconList.tsx`). Distinct from
|
|
59
|
+
`userApiFields` (which is the login fetch). COMPASS/COMPASSCANADA use the 14-field default list;
|
|
60
|
+
**QUAD uses an 8-field list that intentionally omits `supervisorUser.*`** (see gotcha).
|
|
55
61
|
|
|
56
62
|
Resolved by **`src/fieldsConfig/getClientLoginFields.ts`** — a `switch(client)` returning the JSON;
|
|
57
63
|
**default is `COMPASS`** (not DEFAULT). Called from `AuthContext` as `getClientLoginFields(host)`.
|
|
58
64
|
The `languageSwitcher.isEnabled` value feeds the login-settings branch in `getLoginSettings.ts`.
|
|
59
65
|
|
|
66
|
+
### `fetchUsersApiFields` and the config-driven view-as search (C6)
|
|
67
|
+
|
|
68
|
+
`fetchUsers()` in `src/pages/Cart/api/CartApi.ts` no longer hardcodes its field list. It resolves it
|
|
69
|
+
via `getClientLoginFields(fieldKey ?? "").fetchUsersApiFields`, replacing a former
|
|
70
|
+
`if (fieldKey === "QUAD")` client-name branch — this honors the Cart "no `if (client === ...)`,
|
|
71
|
+
drive client behavior from config" rule (**C6** in
|
|
72
|
+
[cart-page-config-architecture](cart-page-config-architecture.md)). An unknown/missing `fieldKey`
|
|
73
|
+
falls back to COMPASS inside `getClientLoginFields` (same as the old `else` branch). Because the
|
|
74
|
+
tenant JSONs are **statically imported**, a tenant missing `fetchUsersApiFields` is a TypeScript
|
|
75
|
+
compile error — not a silent empty fetch — so the list is client-editable via config without
|
|
76
|
+
touching Cart code.
|
|
77
|
+
|
|
78
|
+
**Follow-up candidate (not yet done):** `fetchUser()` (singular, same `CartApi.ts`) still holds the
|
|
79
|
+
same hardcoded QUAD-vs-default field-list pattern and is a candidate for the identical config
|
|
80
|
+
treatment in a future ticket.
|
|
81
|
+
|
|
60
82
|
## Layer B — the per-page FIELDS registry
|
|
61
83
|
|
|
62
84
|
`src/fieldsConfig/index.ts` (~1100 lines) imports every page's per-tenant field constants (which
|
|
@@ -179,10 +201,22 @@ Common field attributes: `uuid`, `label`, `valueKey` (path into the data object)
|
|
|
179
201
|
- Role is derived from string `"1"` flags on the user (`user?._isAdmin === "1"`), not booleans.
|
|
180
202
|
- `getClientLoginFields` defaults to **COMPASS** for an unknown host, which can mask a
|
|
181
203
|
misconfigured tenant in local dev.
|
|
204
|
+
- **QUAD's `fetchUsersApiFields` omits `supervisorUser.*` on purpose — do not "fix" it to match
|
|
205
|
+
COMPASS.** A code-review bot flagged the mismatch, but it is correct: the original hardcoded QUAD
|
|
206
|
+
`fetchUsers` branch never requested supervisor fields; only `fetchUser()` (the detail fetch) does,
|
|
207
|
+
and the sole `fetchUsers` caller (view-as search in `NavIconList.tsx`) gets full detail from
|
|
208
|
+
`fetchUser()` after selection. QUAD also uses the `_isGlobalAdmin`/`_isBuyer`/`_isItShopper` role
|
|
209
|
+
model, not the supervisor hierarchy. Aligning the two lists would smuggle a behavior change into a
|
|
210
|
+
refactor.
|
|
182
211
|
|
|
183
212
|
## Change history
|
|
184
213
|
- 2026-06-23 — Initial: documented the two-layer field system (global `clientFields/<TENANT>.json`
|
|
185
214
|
+ the per-page `FIELDS` registry), the `tenant→language→role` resolution in `useAssignClientFields`,
|
|
186
215
|
the `en`/`fr` vs `ENGLISH`/`FRENCH` distinction, role derivation per tenant, and field JSON shapes
|
|
187
216
|
(apeterson)
|
|
217
|
+
- 2026-07-10 — Added the Layer A `fetchUsersApiFields` key: moved the hardcoded `fetchUsers()`
|
|
218
|
+
field lists out of `CartApi.ts` into each tenant JSON (COMPASS/COMPASSCANADA = 14-field default,
|
|
219
|
+
QUAD = 8-field), replacing the `if (fieldKey === "QUAD")` branch with a config lookup (C6). Noted
|
|
220
|
+
the deliberate QUAD `supervisorUser.*` omission and the `fetchUser()` follow-up candidate. Refactor
|
|
221
|
+
only, no behavior change; `tsc --noEmit` clean. (tcox)
|
|
188
222
|
</content>
|
package/package.json
CHANGED