toga-ai 1.0.417 → 1.0.418

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,8 +6,8 @@ project: _Underscore
6
6
  client: compass-usa
7
7
  type: client-feature
8
8
  status: active
9
- updated: 2026-07-17
10
- owners: ["apeterson", "dfranks"]
9
+ updated: 2026-07-23
10
+ owners: ["apeterson", "dfranks", "bala"]
11
11
  files:
12
12
  - _underscore/Model/Compass/ApprovalDecision.php
13
13
  - _underscore/Model/Compass/SalesOrder.php
@@ -50,11 +50,35 @@ tenants differ, the **parent** branches on `$api->client->clientIdentifier === '
50
50
  - **Email-template resolution** — `resolveEmailTemplateUuid()` picks the tenant's template UUID.
51
51
  - **EN/FR localization** — user language is read from `UserGlobalSettings` (`settingId = 2`;
52
52
  `en` / `fr-CA`) so Canadian notifications go out in the recipient's language.
53
+ - **Web-link host resolution** — `_Model_Compass_SalesOrder::resolveSupplyHost()` /
54
+ `resolveCommerceHost()` pick the tenant's supply/commerce domain for the links embedded in
55
+ approval and order-status emails (see below).
53
56
 
54
57
  Because the divergence is a runtime branch and not an override, do **not** add per-client logic to
55
58
  the empty subclasses — extend the shared parent and branch there if a genuine tenant difference is
56
59
  needed.
57
60
 
61
+ ### Email-link host resolution (US vs Canada domains)
62
+ The "Review Order" link (togasupply) and the user order-details link (togacommerce) embedded in
63
+ Compass approval and order-status emails are built from a **client-aware host resolver** on the
64
+ shared parent `_Model_Compass_SalesOrder`, so the link always points at the domain that actually
65
+ holds the order:
66
+ - Four host constants: `HOST_SUPPLY__COMPASS_US = 'compass.togasupply.com'`,
67
+ `HOST_SUPPLY__COMPASS_CANADA = 'compasscanada.togasupply.com'`,
68
+ `HOST_COMMERCE__COMPASS_US = 'compass.togacommerce.com'`,
69
+ `HOST_COMMERCE__COMPASS_CANADA = 'compasscanada.togacommerce.com'`.
70
+ - Public static `resolveSupplyHost(&$api)` / `resolveCommerceHost(&$api)` return the correct host;
71
+ private static `isCompassCanadaClient(&$api)` returns
72
+ `($api->client->clientIdentifier ?? '') === 'Compass_Canada'`. Anything not Canada falls back to
73
+ the **US** host, so US behavior is unchanged.
74
+ - All 8 link-building sites build the URL as `"https://" . resolveSupplyHost($api) . "/?..."` (only
75
+ the host varies; the query string / uuid is identical). Sites wired: `ApprovalDecision.php` —
76
+ manager-approval-request block and VIP auto-approve block (`orderUrl` + `userOrderUrl` in each);
77
+ `SalesOrder.php` — admin-approved block (`postPut`), in-transit/delivered block, and
78
+ `_sendVipManagerNotification`.
79
+ - This reuses the **same `$api->client->clientIdentifier` signal** as the email-template language
80
+ resolver, so the link host and the email template can never disagree.
81
+
58
82
  ### VIP manager auto-approve — the rule and its three (all supervisor-derived) triggers
59
83
  A single rule governs VIP auto-approve: a **manager-stage (step 2)** approval auto-approves iff the
60
84
  assigned manager's `Users.c_isVip = 1` **AND** the order subtotal
@@ -114,8 +138,25 @@ requester**. All of these comparisons are case-insensitive (`strcasecmp()`), mat
114
138
  requester/new-manager guards.
115
139
  - **Don't put tenant behavior in the empty subclasses.** `Usa`/`Canada` `ApprovalDecision` are
116
140
  intentionally empty; the tenant branch lives in the parent on `clientIdentifier`.
141
+ - **Never hardcode the web host in email links.** The email links previously used string literals
142
+ `compass.togasupply.com` / `compass.togacommerce.com` in the parent classes. Because the tenant
143
+ subclasses are empty, **Compass Canada orders inherited the US links** and sent Canada
144
+ recipients to the US supply/commerce site, which does not contain their order — so the link never
145
+ opened. Always build the host via `_Model_Compass_SalesOrder::resolveSupplyHost()` /
146
+ `resolveCommerceHost()`, never a literal. Real incident: order SAC100650 — manager Gary Berenz
147
+ could not open the order from the "Manager Approval Needed" email, so admin Kai Wong had to
148
+ approve as manager on his behalf. (Fixed 2026-07-23.)
117
149
 
118
150
  ## Change history
151
+ - 2026-07-23 — Fixed Compass approval/status **email links** being hardcoded to the US domains
152
+ (`compass.togasupply.com` / `compass.togacommerce.com`) in the parent classes, so Compass Canada
153
+ orders (empty `Canada` subclass) inherited US links and Canada recipients were sent to a site
154
+ without their order. Added a client-aware host resolver to `_Model_Compass_SalesOrder` (four
155
+ `HOST_*` constants, `resolveSupplyHost()`/`resolveCommerceHost()`, `isCompassCanadaClient()`
156
+ branching on `clientIdentifier === 'Compass_Canada'`, US fallback), and rewired all 8 link sites
157
+ in `ApprovalDecision.php` and `SalesOrder.php` to build the host through it — reusing the same
158
+ `clientIdentifier` signal as the email-template resolver so host and template always agree.
159
+ Verified both client contexts resolve correctly; `php -l` clean. Prod incident SAC100650. (bala)
119
160
  - 2026-07-17 — Documented the VIP manager auto-approve rule (VIP manager + subtotal ≤ $5000) and
120
161
  its three supervisor-derived triggers (`postPost` creation, `postPut` contact change,
121
162
  `handleManagerReassignment` on `postPut`), plus the **POST-path gap**: a requester with no
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.417",
3
+ "version": "1.0.418",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",