toga-ai 1.0.970 → 1.0.971
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.
|
@@ -5,10 +5,11 @@ project: _Underscore
|
|
|
5
5
|
client: compass-usa
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-10-
|
|
9
|
-
owners: [bala, ajean]
|
|
8
|
+
updated: 2026-10-08
|
|
9
|
+
owners: [bala, ajean, rgirish]
|
|
10
10
|
files:
|
|
11
11
|
- toga2-commerce/src/pages/Cart/api/CartApi.ts
|
|
12
|
+
- toga2-commerce/src/pages/Cart/viewModel/useCartViewModel.ts
|
|
12
13
|
- worker1.5/crons/toga2/compass/import_locations.php
|
|
13
14
|
- _underscore/Model/Compass/SalesOrder.php
|
|
14
15
|
- api2/Component/Api/V2/V2.php
|
|
@@ -33,17 +34,20 @@ US Compass only (`Client_Compass`). Compass Canada is explicitly out of scope. S
|
|
|
33
34
|
|
|
34
35
|
### What a cost center actually is
|
|
35
36
|
- A cost center = a **Unit Location** in `Client_Compass.Locations` (`locationTypeId = 1`), keyed by custom field `c_erpSystemEntityId`.
|
|
36
|
-
- `c_erpEntityId` is a **different** field
|
|
37
|
+
- `Locations.c_erpEntityId` is a **different** field: the full `"<entity>-<CC>"` unit id (e.g. `1001-20180`). At checkout only the **CC number** (e.g. `20180`) is written to `SalesOrders.c_erpEntityId` as a **plain string, not a foreign key** — the entity (`1001` / `1019`) is not stored. `_underscore/Model/Compass/SalesOrder.php` exposes a `_costCenter` computed field.
|
|
38
|
+
- Entity `1001` = US cost centers. Entity `1019` rows look like old / reused CCs (feed active flag `U`, not `Y`).
|
|
37
39
|
- The `CostCenters` table is empty for Compass; nothing reads `Users_CostCenters` / `Locations_CostCenters` / `SalesOrders.costCenterId` / `Users.defaultCostCenterId`.
|
|
38
40
|
|
|
39
41
|
### The commerce dropdown
|
|
40
|
-
- Built by `fetchCostCenter()` in `toga2-commerce/src/pages/Cart/api/CartApi.ts`, which calls `GET /locations` filtered on `c_erpSystemEntityId LIKE %search% AND isActive = 1`,
|
|
42
|
+
- Built by `fetchCostCenter()` in `toga2-commerce/src/pages/Cart/api/CartApi.ts`, which calls `GET /locations` filtered on `c_erpSystemEntityId LIKE %search% AND isActive = 1`, fields `c_erpSystemEntityId,name`, distinct (exact `=` match on the display path).
|
|
43
|
+
- `useCartViewModel.ts` (~426-460) labels options `"<CC> - <name>"` and dedupes by `c_erpSystemEntityId` — **first row wins**. So when one CC number exists under several entities, the shown name can belong to the wrong entity.
|
|
41
44
|
- Funnels both the cart checkout and the New-User form, so a single filter in `fetchCostCenter` covers both entry points.
|
|
42
45
|
- **api2 does no cost-center validation** — the FE is the only selection gate.
|
|
43
46
|
|
|
44
47
|
### Where the values come from (import)
|
|
45
|
-
- `worker1.5/crons/toga2/compass/import_locations.php` reads
|
|
46
|
-
-
|
|
48
|
+
- `worker1.5/crons/toga2/compass/import_locations.php` (1.0, daily) reads pipe-delimited `UNIT*.txt` from SFTP. Col 0 = `<entity>-<CC>` → `c_erpEntityId`; col 16 = CC → `c_erpSystemEntityId`; cols 17-20 = address; col 23 = active flag (`Y` → 1, else 0).
|
|
49
|
+
- Units are looked up by `c_erpEntityId` with an **INNER JOIN** to `Locations_Addresses` / `Addresses` on `primaryLocationAddressId` (~line 210-220). A unit with no address is never found, so a **new `Locations` row is inserted every run** — the source of the duplicates (see gotcha).
|
|
50
|
+
- The update branch sets a new `uuid` on every run, so Location uuids are not stable.
|
|
47
51
|
|
|
48
52
|
### Numeric-only enforcement (2026-07-06) — three layers
|
|
49
53
|
1. **Front-end selection block** — in `fetchCostCenter`, on the **search path** (`useSearchOptions === true`), returned locations are filtered to numeric-only with `/^[0-9]+$/` on `c_erpSystemEntityId.trim()`. The non-search / display path is left untouched (an already-saved value still renders). Done client-side because api2 rejects a server-side numeric filter (see gotcha) and api2 was out of scope.
|
|
@@ -61,12 +65,15 @@ Deactivation proven safe against prod data:
|
|
|
61
65
|
- Proven end-to-end in dev-sandbox: after the migration, active non-numeric rows = **0** (all 1,185 codes incl. the 725 number+alphabet combos) while pure-numeric stayed untouched at **440,005** active rows.
|
|
62
66
|
|
|
63
67
|
## Gotchas
|
|
68
|
+
- **⚠ "TechHub shows the wrong location name for CC X" is display-only.** The CC number is shared by more than one entity (176 numeric CCs among active units, prod 2026-10-08); the dropdown keeps the first row. The order stores only the CC number, so the order data is correct. Example: `20180` → `1001-20180` "General Overhead" (id 58192, RA B&I sector) vs `1019-20180` "Williamsburg Pottery Main" (548 active rows). Quick fix per CC: set `isActive = 0` on the stale entity's rows — check `Users.locationId` first (`1019-20180` had 0 users).
|
|
69
|
+
- **⚠ BUG (found 2026-10-08, NOT fixed) — the import duplicates units with no address, forever.** Cause: the INNER JOIN unit lookup above. Prod 2026-10-08: **874,887** active unit rows with no address; many units have ~1,200 rows (e.g. `1019-20180`: 1,038 rows, 859 active). Duplicates made before the active-flag logic stay active forever — the cron can never find them to deactivate. Fix: LEFT JOIN the unit lookup, then clean up the duplicates (soft delete, `isActive = 0`).
|
|
70
|
+
- **⚠ SECURITY — `import_locations.php` has the `cgsftp` SFTP password hardcoded in source.** Move it to config and rotate it. Never copy the value anywhere.
|
|
64
71
|
- **⚠ A cost-center number is NOT unique across sectors — never match on the number alone.** Scope
|
|
65
72
|
every match to a sector / hierarchy path. Measured 2026-10-05: 10 of the client's 634 "corporate"
|
|
66
73
|
cost centers also exist under other sectors — `4018` / `45781` / `62937` under B&I Overhead
|
|
67
74
|
(`M00000`), `22772` / `26963` / `28184` / `54188` under Corporate Income (`H00000`), `47258` /
|
|
68
75
|
`62230` under CCL Hospitality (`Y00000`), `31335` under ESFM (`W00000`). Three carry real users
|
|
69
|
-
(`4018` → 10, `31335` → 9, `47258` → 9), so the wrong match hits live people. See
|
|
76
|
+
(`4018` → 10, `31335` → 9, `47258` → 9), so the wrong match hits live people. Same number can also repeat across entities (`1001` / `1019`) — see the display gotcha above. See
|
|
70
77
|
[Location Hierarchy & Sectors](./location-hierarchy-and-sectors.md) for the walk and the
|
|
71
78
|
dedupe-before-counting recipe.
|
|
72
79
|
- **⚠ `Users.c_erpSystemEntityId` has a DOUBLE meaning — never overwrite it to persist a cost-center change.** In `worker2/Worker/Client/Compass/PeopleFile.php` it is (a) copied straight into `c_hrEmpDirectoryKey` and treated as **unique per employee** (`:544-546`, e.g. `"1001-1003"`), and (b) used as the **primary key for resolving `Users.locationId`** (`:566-571`). Writing a location code into it corrupts employee identity to buy one night of persistence. This is why the [Tools Compass admin](../../../1.0/apps/tools/features/compass-user-persona-admin.md) writes only `Users.locationId` + `Users.c_erpEntityId` and accepts that a cost-center edit is **undone that night** for anyone in the PEOPLE file.
|
|
@@ -74,7 +81,6 @@ Deactivation proven safe against prod data:
|
|
|
74
81
|
- **⚠ There is no usable index for a cost-center search, so never wire one to a type-ahead.** `Client_Compass` has ~909,700 active `locationTypeId = 1` (Unit) rows carrying only ~36,352 distinct `c_erpSystemEntityId` values (~730 duplicate rows per code); **442,549** of the active rows have a numeric short code. Per the `dbchanges2` schema the only key on `Locations` is `locationTypeId`, which covers nearly the whole table — so there is no index on `c_erpSystemEntityId`, on `isActive`, or any composite. Every cost-center search reads the table then groups. **Recommended follow-up (not done): index `Locations (locationTypeId, isActive, c_erpSystemEntityId)` in BOTH Compass tenants** (dbchanges2).
|
|
75
82
|
- **⚠ BUG (found 2026-09-04, NOT fixed) — `_Model_Compass_SalesOrder::_costCenter()` joins the wrong column.** `_underscore/Model/Compass/SalesOrder.php:287-305` does `INNER JOIN Contacts ON Contacts.id = Users.id`; it should be `Users.contactId`. The user-level cost-center fallback therefore resolves an **unrelated user's** contact. Needs an `_underscore` change.
|
|
76
83
|
- **api2 cannot do a server-side numeric filter.** The V2 query-string where-parser (`api2/Component/Api/V2/V2.php` → `parseOptionsWhere`) whitelists operators and **rejects `regexp` with a 500**. Any numeric/regex filtering must happen client-side (or via a new operator in api2, which was out of scope here).
|
|
77
|
-
- **Two fields, easy to swap.** `c_erpSystemEntityId` is the cost center (what users pick); `c_erpEntityId` is the unit id and the value orders record on `SalesOrders.c_erpEntityId`.
|
|
78
84
|
- **Second, possibly-live copy of the import cron not yet guarded.** A second copy exists at `worker/crons/toga2/compass/DOA_Scripts/import_locations.php` (appears disabled / "DOA"). It did **not** receive the numeric guard — confirm whether it is live before trusting the guard to be complete.
|
|
79
85
|
|
|
80
86
|
## Related
|
|
@@ -83,3 +89,6 @@ Deactivation proven safe against prod data:
|
|
|
83
89
|
- [Cart page config architecture](../../../2.0/apps/toga2-commerce/features/cart-page-config-architecture.md)
|
|
84
90
|
- [Tools Compass user/persona admin](../../../1.0/apps/tools/features/compass-user-persona-admin.md)
|
|
85
91
|
- [PEOPLE-file user lifecycle](./people-file-user-lifecycle.md)
|
|
92
|
+
|
|
93
|
+
## Change history
|
|
94
|
+
- 2026-10-08 — Documented dropdown first-row-wins dedupe (wrong name = display only), entity 1001 vs 1019, the INNER JOIN import bug that inserts duplicate address-less units every run, uuid churn, and the hardcoded SFTP credential. (rgirish)
|
|
@@ -5,8 +5,8 @@ project: _Underscore
|
|
|
5
5
|
client: compass-usa
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-10-
|
|
9
|
-
owners: [bala]
|
|
8
|
+
updated: 2026-10-08
|
|
9
|
+
owners: [bala, rgirish]
|
|
10
10
|
files:
|
|
11
11
|
- worker/crons/toga2/compass/DOA_Scripts/import_locations.php
|
|
12
12
|
- worker1.5/crons/toga2/compass/import_locations.php
|
|
@@ -69,6 +69,8 @@ not re-derive it. Ids are set in `import_locations.php:37-45`.
|
|
|
69
69
|
| 16 | cost center entity id → `Locations.c_erpSystemEntityId` |
|
|
70
70
|
| 17-20 | street / city / state / zip |
|
|
71
71
|
|
|
72
|
+
The live daily copy, `worker1.5/crons/toga2/compass/import_locations.php`, also reads **col 23 = unit active flag** (`Y` → active). Its duplicate-unit bug is in [Cost Centers](./cost-centers.md).
|
|
73
|
+
|
|
72
74
|
Because new corporate cost centers arrive on this feed on their own, **anything scoped to "corporate"
|
|
73
75
|
must key off the hierarchy, never a client-maintained spreadsheet.**
|
|
74
76
|
|
|
@@ -120,6 +122,7 @@ All of them are flagged Active and all have **zero users**. Drop them before any
|
|
|
120
122
|
- [Effective persona resolution](../../../2.0/apps/_underscore/features/effective-persona-resolution.md)
|
|
121
123
|
|
|
122
124
|
## Change history
|
|
125
|
+
- 2026-10-08 — Noted the live worker1.5 import copy reads col 23 as the active flag. (rgirish)
|
|
123
126
|
- 2026-10-05 — Created from the field-laptop research. Recorded that corporate is identified only by
|
|
124
127
|
sector `J00000` (`Locations.id = 52`) and the ERP-id letter prefix (no flag, no column in the
|
|
125
128
|
21-column feed), that Levy's corporate office is the **district** `K9902` not a sector, that the
|
package/package.json
CHANGED