toga-ai 1.0.739 → 1.0.740
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/toga2-commerce/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga2-commerce/features/bundle-item-visibility-and-selectability.md +33 -2
- package/knowledge/clients/compass-usa/INDEX.md +1 -1
- package/knowledge/clients/compass-usa/features/item-catalogs-and-duplicate-items.md +29 -9
- package/knowledge/clients/compass-usa/features/mits-po-transmission-to-vendors.md +23 -2
- package/knowledge/clients/compass-usa/features/mits-sales-order-transmission-alerting.md +29 -3
- package/knowledge/clients/compass-usa/profile.md +7 -1
- package/knowledge/clients/compass-usa/workflows/catalog-fold-reversal.md +125 -37
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [TOGa Commerce (toga2-commerce / commerce2-react) Architecture](architecture.md) | `toga2-commerce` (npm package name **`commerce2-react`**, product name **TOGa Commerce**) is the customer-facing **B2B commerce storefront** of the 2.0 platform | src/main.tsx, src/App.tsx, src/routes.tsx, src/contexts/AuthContext.tsx, src/contexts/helpers/getLoginSettings.ts, src/api/axiosInstance.ts, src/stores/, src/themeConfig/ThemeContext.tsx, src/fieldsConfig/index.ts, src/hooks/useAssignClientFields.ts, vite.config.ts, package.json |
|
|
6
|
-
| [Bundle item visibility & selectability (the three flags, and why they are enforced nowhere but the client)](features/bundle-item-visibility-and-selectability.md) | Which components of a kit a shopper can see and choose is decided **entirely in the browser**. | toga2-commerce/src/pages/BundleView/api/BundleApi.tsx, toga2-commerce/src/pages/BundleView/helpers/formatOriginalBundleData.ts, toga2-commerce/src/pages/BundleView/helpers/formatBundleToAddToCart.ts, toga2-commerce/src/pages/BundleView/hooks/useBundleEditFromLocalStorage.ts, toga2-commerce/src/stores/useBundleBuilderZu.ts, _underscore/Model/Client/BundleItem.php, _underscore/Model/Client/SalesOrderItem.php, api2/Component/Api/V2/V2.php |
|
|
6
|
+
| [Bundle item visibility & selectability (the three flags, and why they are enforced nowhere but the client)](features/bundle-item-visibility-and-selectability.md) | Which components of a kit a shopper can see and choose is decided **entirely in the browser**. | toga2-commerce/src/pages/BundleView/api/BundleApi.tsx, toga2-commerce/src/pages/BundleView/helpers/formatOriginalBundleData.ts, toga2-commerce/src/pages/BundleView/helpers/formatBundleToAddToCart.ts, toga2-commerce/src/pages/BundleView/hooks/useBundleEditFromLocalStorage.ts, toga2-commerce/src/api/helpers/formatBundlesForSalesQuoteZuCartinEditCart.ts, toga2-commerce/src/api/genericApi.ts, toga2-commerce/src/api/FilterApi.ts, toga2-commerce/src/api/translatedSearchApi.ts, toga2-commerce/src/stores/useBundleBuilderZu.ts, _underscore/Model/Client/BundleItem.php, _underscore/Model/Client/SalesOrderItem.php, api2/Component/Api/V2/V2.php |
|
|
7
7
|
| [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 |
|
|
8
8
|
| [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 |
|
|
9
9
|
| [Cart Order-Total & Shipping Computation](features/cart-order-total-computation.md) | The Cart summary section (subtotal / shipping / tax / total) is **data-driven** from `cartData`. | toga2-commerce/src/pages/Cart/viewModel/useCartViewModel.ts, toga2-commerce/src/pages/Cart/CartPage.tsx, toga2-commerce/src/pages/Cart/view/cartForm/CartForm.tsx, toga2-commerce/src/pages/Cart/helpers/shippingOptionGates.ts, toga2-commerce/src/pages/Cart/api/CartApi.ts |
|
package/knowledge/2.0/apps/toga2-commerce/features/bundle-item-visibility-and-selectability.md
CHANGED
|
@@ -6,13 +6,17 @@ project: TOGa Commerce
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["apeterson"]
|
|
9
|
+
updated: 2026-09-02
|
|
10
|
+
owners: ["apeterson", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-commerce/src/pages/BundleView/api/BundleApi.tsx
|
|
13
13
|
- toga2-commerce/src/pages/BundleView/helpers/formatOriginalBundleData.ts
|
|
14
14
|
- toga2-commerce/src/pages/BundleView/helpers/formatBundleToAddToCart.ts
|
|
15
15
|
- toga2-commerce/src/pages/BundleView/hooks/useBundleEditFromLocalStorage.ts
|
|
16
|
+
- toga2-commerce/src/api/helpers/formatBundlesForSalesQuoteZuCartinEditCart.ts
|
|
17
|
+
- toga2-commerce/src/api/genericApi.ts
|
|
18
|
+
- toga2-commerce/src/api/FilterApi.ts
|
|
19
|
+
- toga2-commerce/src/api/translatedSearchApi.ts
|
|
16
20
|
- toga2-commerce/src/stores/useBundleBuilderZu.ts
|
|
17
21
|
- _underscore/Model/Client/BundleItem.php
|
|
18
22
|
- _underscore/Model/Client/SalesOrderItem.php
|
|
@@ -116,8 +120,28 @@ Also decided: **delete** the dead `where` clause in `BundleApi.tsx` rather than
|
|
|
116
120
|
engine cannot prune nested children via `where` at all, and the clause could not express the
|
|
117
121
|
fee / child-bundle exceptions even if it worked.
|
|
118
122
|
|
|
123
|
+
## The bundle path never filters by catalog — it relays whatever item the API returns
|
|
124
|
+
|
|
125
|
+
The storefront applies a `catalogId = 1` filter **only** on standalone item search
|
|
126
|
+
(`src/api/genericApi.ts`, `FilterApi.ts`, `translatedSearchApi.ts`). The **bundle** path does not:
|
|
127
|
+
`GET /v2/bundles/{uuid}` returns each kit line's `bundleItems.item.uuid`, and the cart submit relays
|
|
128
|
+
that uuid verbatim (`src/api/helpers/formatBundlesForSalesQuoteZuCartinEditCart.ts`,
|
|
129
|
+
`BundleView/api/BundleApi.tsx`). So the item a kit checkout POSTs is **whatever `BundleItems.itemId`
|
|
130
|
+
resolves to on the server** — the browser never re-picks a catalog for it.
|
|
131
|
+
|
|
132
|
+
**Consequence:** if a kit's `BundleItems.itemId` points at the wrong-catalog item, **every checkout
|
|
133
|
+
of that kit silently POSTs the wrong item, and no frontend change fixes it — the data must be
|
|
134
|
+
corrected.** This was the ongoing source of *new* bad orders after Compass USA's 2026-08-31 catalog
|
|
135
|
+
fold: `BundleItems.itemId` for 434 active rows across 110 bundles still pointed at the Agilant
|
|
136
|
+
catalog-2 twins, so each new kit checkout POSTed the Agilant item. Root cause + the
|
|
137
|
+
restore-`BundleItems` fix:
|
|
138
|
+
[Compass catalog-fold reversal](../../../clients/compass-usa/workflows/catalog-fold-reversal.md).
|
|
139
|
+
|
|
119
140
|
## Gotchas / known issues
|
|
120
141
|
|
|
142
|
+
- **⚠ A folded/wrong `BundleItems.itemId` is invisible to the storefront's catalog filter.** The
|
|
143
|
+
catalog filter guards standalone search only; bundles relay the server's item uuid. Fix wrong-item
|
|
144
|
+
kits in the data (`BundleItems`), not in the frontend.
|
|
121
145
|
- **⚠ Never read a flag on a `BundleItems` row without first knowing its row type.** Fee rows are
|
|
122
146
|
invisible-but-mandatory; child-bundle rows are inactive-by-convention.
|
|
123
147
|
- **⚠ Do not add a `where` to a uuid'd GET expecting nested filtering.** It is doubly inert (READ
|
|
@@ -145,6 +169,13 @@ direct API write (`Logs.Api` request payload for that order).
|
|
|
145
169
|
|
|
146
170
|
## Change history
|
|
147
171
|
|
|
172
|
+
- 2026-09-02 — Recorded that the storefront's `catalogId = 1` filter guards **standalone item
|
|
173
|
+
search only** (`genericApi.ts`, `FilterApi.ts`, `translatedSearchApi.ts`); the **bundle** path
|
|
174
|
+
relays whatever `bundleItems.item.uuid` `GET /v2/bundles/{uuid}` returns, so a kit checkout POSTs
|
|
175
|
+
whatever `BundleItems.itemId` resolves to server-side. A wrong-catalog `BundleItems.itemId`
|
|
176
|
+
therefore silently POSTs the wrong item and can only be fixed in data — this was the ongoing
|
|
177
|
+
source of new bad Compass orders after the 2026-08-31 catalog fold (434 rows / 110 bundles still
|
|
178
|
+
on the Agilant twins). (jcardinal)
|
|
148
179
|
- 2026-08-27 — Created from an investigation-only session (no code changed) into how part number
|
|
149
180
|
`S24D402GAN` reached Compass USA order `SA136441`. Recorded that the bundle fetch's
|
|
150
181
|
`BundlesItems.isActive` `where` clause in `BundleApi.tsx` has **never executed** (uuid'd GET
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
| [Compass Sales-Order Line-Number Renumbering (and why it hangs off the SO hooks)](features/sales-order-line-renumbering.md) | 2.0 | Compass sales-order lines must stay numbered **1..N with no gaps** after any add, edit, or delete — downstream MITS/PO linking reads `lineNumber` as an identity | _underscore/Model/Compass/SalesOrderItem.php, _underscore/Model/Compass/SalesOrder.php |
|
|
23
23
|
| [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 |
|
|
24
24
|
| [Compass USA](profile.md) | 2.0 | Compass USA is a TOGA client running a multi-tier supply-chain commerce operation. | |
|
|
25
|
-
| [Compass USA — reversing the 199-item catalog fold (MITS outage recovery)](workflows/catalog-fold-reversal.md) | 2.0 | The **root cause** of the Compass MITS transmission outage (the `assetType`-NULL and duplicate-vendor symptoms) was an ad-hoc cross-client **duplicate-items mer | test/team/2.0 deployment/duplicate_items_merge_ALL_CLIENTS.sql, test/team/2.0 deployment/duplicate_items_merge_analysis.sql, worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php, library/app/api/toga2.php, worker/crons/toga2/netsuite/common_sync_togasupply.php |
|
|
25
|
+
| [Compass USA — reversing the 199-item catalog fold (MITS outage recovery)](workflows/catalog-fold-reversal.md) | 2.0 | The **root cause** of the Compass MITS transmission outage (the `assetType`-NULL and duplicate-vendor symptoms) was an ad-hoc cross-client **duplicate-items mer | test/team/2.0 deployment/duplicate_items_merge_ALL_CLIENTS.sql, test/team/2.0 deployment/duplicate_items_merge_analysis.sql, worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php, worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php, library/app/api/toga2.php, worker/crons/toga2/netsuite/common_sync_togasupply.php |
|
|
26
26
|
| [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 |
|
|
27
27
|
| [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 |
|
|
28
28
|
| [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 |
|
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: compass-usa
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: ["bala", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
@@ -124,14 +124,18 @@ Plus `-1` / `-2` suffixed copies: items **2431, 2432, 2443, 2653**.
|
|
|
124
124
|
span more than one catalog.** It matters only as a thing to re-check, because a part-number lookup
|
|
125
125
|
with no catalog filter has **no ambiguity check on its exact-match branch**
|
|
126
126
|
([850 line-item resolution](odp-edi-850-item-resolution.md)).
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
reversal
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
`partNumber`
|
|
127
|
+
**Resolved: the twins carry DISTINCT part-number variants, so table-wide `partNumber` uniqueness
|
|
128
|
+
holds.** The Agilant `#ABA` form never equals the plain part number (e.g. `C40QYUC#ABA` vs
|
|
129
|
+
`C40QYUC`; `B7HJ2UC#ABA` vs `B7HJ2UC`), so any `UPPER(partNumber)` or partNumber-twin match
|
|
130
|
+
silently misses them. That is exactly why the
|
|
131
|
+
[catalog-fold reversal](../workflows/catalog-fold-reversal.md) ultimately mapped dupe→canon by
|
|
132
|
+
**primary key** from the pre-merge snapshot, never by part number, and why the `#ABA` PO-line
|
|
133
|
+
stragglers needed a manual VendorItem re-map. Only a history question remains (how the original
|
|
134
|
+
merge's `UPPER(partNumber)` partition folded pairs that do not share a part number); nothing
|
|
135
|
+
downstream depends on it. **⚠ Note:** the exact item ids ↔ catalog assignments in the
|
|
136
|
+
duplicate-pairs table above predate the 2026-08-31 fold and its reversal (which deleted then
|
|
137
|
+
restored catalog-1 items by their original snapshot ids), so re-read specific ids from current prod
|
|
138
|
+
before relying on them.
|
|
135
139
|
|
|
136
140
|
## Filtering by catalog: fine for *display*, never for *resolution*
|
|
137
141
|
|
|
@@ -196,6 +200,13 @@ children" rule:
|
|
|
196
200
|
- **⚠ A NULL `catalogId` is a real state** (27 items in prod, 22 ODP `VendorItems` point at such
|
|
197
201
|
items). Anything joining through `Catalogs` **drops those rows silently** — an inner join to
|
|
198
202
|
`Catalogs` is itself a filter.
|
|
203
|
+
- **⚠ `BundleItems.itemId` is a silent catalog-resolution consumer — the storefront never
|
|
204
|
+
re-catalogs it.** The `toga2-commerce` storefront relays whatever item `GET /v2/bundles/{uuid}`
|
|
205
|
+
returns; its `catalogId = 1` filter guards standalone item search only, never bundles. So a kit
|
|
206
|
+
whose `BundleItems.itemId` points at the wrong-catalog twin makes **every checkout POST the wrong
|
|
207
|
+
item** (434 rows / 110 bundles after the 2026-08-31 fold). Fix in data, not the frontend. See
|
|
208
|
+
[catalog-fold reversal](../workflows/catalog-fold-reversal.md) and
|
|
209
|
+
[bundle item visibility & selectability](../../../2.0/apps/toga2-commerce/features/bundle-item-visibility-and-selectability.md).
|
|
199
210
|
- **⚠ `isFulfillable` is born on the catalog-2 (Agilant) item** and propagates to the client-facing
|
|
200
211
|
copy; the flag's home catalog is not an accident. See
|
|
201
212
|
[isFulfillable data quality & the type rule](isfulfillable-data-quality-and-type-rule.md).
|
|
@@ -212,6 +223,15 @@ children" rule:
|
|
|
212
223
|
[catalog-fold reversal](../workflows/catalog-fold-reversal.md).
|
|
213
224
|
|
|
214
225
|
## Change history
|
|
226
|
+
- 2026-09-02 — **Resolved the flagged "distinct vs shared partNumber" tension**: the Agilant twins
|
|
227
|
+
carry **distinct** part-number variants (the `#ABA` form ≠ the plain part number, e.g.
|
|
228
|
+
`C40QYUC#ABA` vs `C40QYUC`), so table-wide `partNumber` uniqueness holds and the catalog-fold
|
|
229
|
+
reversal maps dupe→canon by **primary key**, never by part number (part-number matching misses the
|
|
230
|
+
`#ABA` stragglers). Flagged that exact item ids ↔ catalog assignments in the duplicate-pairs table
|
|
231
|
+
predate the fold+reversal and should be re-read from prod. Added `BundleItems.itemId` as a silent
|
|
232
|
+
catalog-resolution consumer — the storefront relays the bundle item uuid with no catalog filter, so
|
|
233
|
+
a folded `BundleItems.itemId` (434 rows / 110 bundles) POSTs the wrong item on every kit checkout
|
|
234
|
+
until the data is fixed. (jcardinal)
|
|
215
235
|
- 2026-09-01 — Folded in the **customer → catalog rule** (`customerId = 2` → catalog 1, everyone
|
|
216
236
|
else → catalog 2; verified 182,787 vs 1 pre-merge) and the mandatory `customerId` scope on any
|
|
217
237
|
catalog re-point (an unscoped one moved 687 Office-Depot lines). Added that the NetSuite sync
|
|
@@ -5,8 +5,8 @@ project: _Underscore
|
|
|
5
5
|
client: compass-usa
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-
|
|
9
|
-
owners: ["rgirish", "bala"]
|
|
8
|
+
updated: 2026-09-02
|
|
9
|
+
owners: ["rgirish", "bala", "jcardinal"]
|
|
10
10
|
files:
|
|
11
11
|
- worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php
|
|
12
12
|
- worker/crons/toga2/compasscanada/workflow/2_transmit_mits_purchase_orders_to_vendors.php
|
|
@@ -14,6 +14,8 @@ files:
|
|
|
14
14
|
related:
|
|
15
15
|
- mits-po-to-so-item-linking.md
|
|
16
16
|
- order-lifecycle-and-data-integrity.md
|
|
17
|
+
- item-catalogs-and-duplicate-items.md
|
|
18
|
+
- ../workflows/catalog-fold-reversal.md
|
|
17
19
|
---
|
|
18
20
|
|
|
19
21
|
## Summary
|
|
@@ -87,6 +89,19 @@ Compass Canada has a parallel `compasscanada/workflow/2_transmit_...` keyed on
|
|
|
87
89
|
`CUSTOMER_ID__COMPASS_CANADA`.
|
|
88
90
|
|
|
89
91
|
## Gotchas / known issues
|
|
92
|
+
- **⚠ Everything transmitted is sourced from the `VendorItem` the PO line points at — a
|
|
93
|
+
wrong-catalog `vendorItemId` sends the wrong SKU, wrong cost, AND the wrong channel.** The vendor
|
|
94
|
+
SKU and the cXML-vs-EMAIL routing come entirely from the `VendorItem` that
|
|
95
|
+
`PurchaseOrderItems.vendorItemId` references (with `PurchaseOrderItems.cost` for price). So a PO
|
|
96
|
+
line pointing at an **Agilant** VendorItem instead of the Office-Depot one transmits Agilant's
|
|
97
|
+
**internal SKU** and **wrong cost**, and — because that VendorItem carries **no
|
|
98
|
+
`overridePoSubmissionIntegrationId`** — falls back to the vendor's default **EMAIL** integration
|
|
99
|
+
instead of **cXML**. Verified in the legacy `Logs.API`: good order **SA136666** went **cXML** to
|
|
100
|
+
`b2b.odpbusiness.com` with `SupplierPartID 5775600` / `UnitPrice 945.17`; bad order **SA136846**
|
|
101
|
+
went **email** with `C40QYUC#ABA` / `$61`. This was a downstream symptom of the 2026-08-31 catalog
|
|
102
|
+
fold (a folded PO line resolved to the Agilant twin) — root cause and reversal:
|
|
103
|
+
[catalog-fold reversal](../workflows/catalog-fold-reversal.md);
|
|
104
|
+
[two-catalog item model](item-catalogs-and-duplicate-items.md).
|
|
90
105
|
- **Item-less PO is silently marked submitted without being sent (the "logs show not sent
|
|
91
106
|
but dtSubmitted is set" symptom).** The `dtSubmitted = NOW()` UPDATE runs **unconditionally
|
|
92
107
|
after** the per-integration `foreach`, not inside it. If a PO has **zero
|
|
@@ -109,6 +124,12 @@ Compass Canada has a parallel `compasscanada/workflow/2_transmit_...` keyed on
|
|
|
109
124
|
`dtSubmitted` stamped without a log (separate, previously-observed failure mode).
|
|
110
125
|
|
|
111
126
|
## Change history
|
|
127
|
+
- 2026-09-02 — Recorded that cron 2 sources the vendor **SKU, cost, and cXML-vs-EMAIL routing**
|
|
128
|
+
entirely from the `VendorItem` at `PurchaseOrderItems.vendorItemId` (plus `PurchaseOrderItems.cost`),
|
|
129
|
+
so a PO line folded onto an **Agilant** VendorItem transmits Agilant's internal SKU, the wrong
|
|
130
|
+
cost, and — lacking `overridePoSubmissionIntegrationId` — drops from cXML to the default EMAIL
|
|
131
|
+
channel. Evidence: good `SA136666` cXML `SupplierPartID 5775600` / `945.17`; bad `SA136846` email
|
|
132
|
+
`C40QYUC#ABA` / `$61`. Downstream symptom of the 2026-08-31 catalog fold. (jcardinal)
|
|
112
133
|
- 2026-07-14 — Fixed the emailed-PO PDF for both US Compass and Compass Canada: line-item
|
|
113
134
|
rows now advance by their measured wrapped height (long descriptions no longer overwritten),
|
|
114
135
|
and pagination is height-aware (a real 21-item PO went from a broken 17-page PDF to a clean
|
|
@@ -5,7 +5,7 @@ project: _Underscore
|
|
|
5
5
|
client: compass-usa
|
|
6
6
|
type: client-feature
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-
|
|
8
|
+
updated: 2026-09-02
|
|
9
9
|
owners: ["dfranks", "bala", "jcardinal"]
|
|
10
10
|
files:
|
|
11
11
|
- worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php
|
|
@@ -125,7 +125,12 @@ backlog (0 new, several >24h, one ~20 days old).
|
|
|
125
125
|
order**. This was the active blocker (e.g. item `4U555A-S`; ~10 distinct NULL-assetType
|
|
126
126
|
items blocked 9 of 11 pending orders).
|
|
127
127
|
2. **HTTP 403 `SalesOrder:Exists`** — MITS already created the order on a prior attempt, but
|
|
128
|
-
our side never recorded `c_dtTransmittedToCompass`, so the cron **retries it forever**.
|
|
128
|
+
our side never recorded `c_dtTransmittedToCompass`, so the cron **retries it forever**. **Now
|
|
129
|
+
handled (2026-09-02):** on a 403 `SalesOrder:Exists` the cron stamps
|
|
130
|
+
`SalesOrders.c_dtTransmittedToCompass`, writes a `Record`, and **continues** to the next order
|
|
131
|
+
instead of throwing — so an already-created order no longer starves the queue. Root cause of the
|
|
132
|
+
original loop (why we never stamped the first time) was the `App_ApiTransaction` decode fatal —
|
|
133
|
+
see the gotcha below.
|
|
129
134
|
|
|
130
135
|
**Deeper root cause of the 2026-08-31 outage — a wrong catalog merge, not just missing data.**
|
|
131
136
|
The NULL-assetType items were largely created by an ad-hoc cross-client dedup that folded
|
|
@@ -170,7 +175,19 @@ pages even when the count is under threshold. See
|
|
|
170
175
|
console, pushed to ClickUp, and emailed — so the payload was deliberately removed. Alerts here
|
|
171
176
|
name the order, not the person. (This is precisely what Canada still does wrong.)
|
|
172
177
|
- **The MITS crons hold their API credentials inline in the file.** Treat both files as
|
|
173
|
-
secret-bearing when copying snippets anywhere (tickets, PR bodies, knowledge docs).
|
|
178
|
+
secret-bearing when copying snippets anywhere (tickets, PR bodies, knowledge docs). The two
|
|
179
|
+
hardcoded MITS credentials live inline in
|
|
180
|
+
`1_transmit_compass_sales_orders_to_mits.php` — note their location, never paste the values.
|
|
181
|
+
- **⚠ 1.0 `App_ApiTransaction` built with `ENCODE_JSON` returns an ALREADY-DECODED object — do
|
|
182
|
+
NOT `json_decode()` it.** A transaction constructed with `App_ApiTransaction::ENCODE_JSON` hands
|
|
183
|
+
back a decoded object from `execute()`, not a JSON string. Calling `json_decode()` on it raises a
|
|
184
|
+
PHP 7.2 `"expects parameter 1 to be string, object given"` warning, which `App_Error` escalates to
|
|
185
|
+
a **FATAL**. In `1_transmit_compass_sales_orders_to_mits.php` that fatal fired **after** the order
|
|
186
|
+
transmitted to MITS but **before** stamping `c_dtTransmittedToCompass` — producing a ~42-order
|
|
187
|
+
backlog and the endless `SalesOrder:Exists` retry loop above (MITS kept the created order, we
|
|
188
|
+
never recorded it). Correct pattern:
|
|
189
|
+
`$decodedResp = is_string($resp) ? json_decode($resp) : $resp;`. This is a reusable 1.0 footgun,
|
|
190
|
+
not a Compass-only one — any `App_` code that `json_decode()`s an `ENCODE_JSON` response hits it.
|
|
174
191
|
- **The transmit item query's `GROUP BY SalesOrderItems.id` hides false "duplicates."** In
|
|
175
192
|
`1_transmit_compass_sales_orders_to_mits.php` (~L178-251, `GROUP BY` at L247) the
|
|
176
193
|
`GROUP BY SalesOrderItems.id` collapses the `LEFT JOIN VendorItems` / `LEFT JOIN
|
|
@@ -193,6 +210,15 @@ pages even when the count is under threshold. See
|
|
|
193
210
|
when this issue next fires. Watch that run rather than assuming it works.
|
|
194
211
|
|
|
195
212
|
## Change history
|
|
213
|
+
- 2026-09-02 — **Shipped HTTP 403 `SalesOrder:Exists` handling** in
|
|
214
|
+
`1_transmit_compass_sales_orders_to_mits.php`: on that response the cron now stamps
|
|
215
|
+
`c_dtTransmittedToCompass`, writes a `Record`, and continues instead of throwing forever (which
|
|
216
|
+
had starved the `ORDER BY RAND()` queue). Recorded the **root cause** of the loop — a 1.0
|
|
217
|
+
`App_ApiTransaction` built with `ENCODE_JSON` returns an **already-decoded object**, so a
|
|
218
|
+
`json_decode()` on it raises a PHP 7.2 warning that `App_Error` escalates to a **FATAL** *after*
|
|
219
|
+
transmit but *before* the stamp, producing a ~42-order backlog and the endless retry loop; the
|
|
220
|
+
fix is `is_string($resp) ? json_decode($resp) : $resp`. Flagged it as a reusable 1.0 footgun. Also
|
|
221
|
+
noted the two hardcoded MITS credentials' location. (jcardinal)
|
|
196
222
|
- 2026-09-01 — Traced the 2026-08-31 outage to its **deeper root cause**: an ad-hoc
|
|
197
223
|
cross-client dedup folded Compass's 199 catalog-1 store items into their Agilant canonical, so
|
|
198
224
|
order lines resolved to unclassified Agilant items (HTTP 400) and their fused VendorItems
|
|
@@ -19,7 +19,7 @@ project: _Underscore
|
|
|
19
19
|
client: compass-usa
|
|
20
20
|
type: profile
|
|
21
21
|
status: active
|
|
22
|
-
updated: 2026-09-
|
|
22
|
+
updated: 2026-09-02
|
|
23
23
|
owners: [jcardinal, bala, tcox, apeterson, dfranks, akhokhani]
|
|
24
24
|
files: []
|
|
25
25
|
related:
|
|
@@ -92,6 +92,12 @@ separate, related client (see its own profile).
|
|
|
92
92
|
corrupted in `Client_Compass.SalesOrderItems` (Canada and Quad: zero). The code fix is
|
|
93
93
|
preventive; existing rows still need a DB data-fix. Detection query + remediation plan:
|
|
94
94
|
[Cross-Kit Bundle Corruption](workflows/cross-kit-bundle-corruption.md).
|
|
95
|
+
- **⚠ A folded `BundleItems.itemId` makes the storefront POST the wrong-catalog item (2026-09-02).**
|
|
96
|
+
The storefront relays whatever item `GET /v2/bundles/{uuid}` returns and filters `catalogId = 1`
|
|
97
|
+
only on standalone search, never on bundles — so after the 2026-08-31 catalog fold, 434 kit-line
|
|
98
|
+
rows across 110 bundles still on the Agilant twins produced a fresh bad order on every kit
|
|
99
|
+
checkout. Fixed by restoring `BundleItems` in the complete catalog-fold reversal (fix is data, not
|
|
100
|
+
frontend): [catalog-fold reversal](workflows/catalog-fold-reversal.md).
|
|
95
101
|
|
|
96
102
|
## Vendors & integrations
|
|
97
103
|
- **Office Depot (ODP)** — vendor id 1. ASNs arrive via **cXML** (direct V2 API) and via the
|
|
@@ -5,17 +5,20 @@ project: _Underscore
|
|
|
5
5
|
client: compass-usa
|
|
6
6
|
type: workflow
|
|
7
7
|
status: active
|
|
8
|
-
updated: 2026-09-
|
|
8
|
+
updated: 2026-09-02
|
|
9
9
|
owners: ["jcardinal", "bala"]
|
|
10
10
|
files:
|
|
11
11
|
- test/team/2.0 deployment/duplicate_items_merge_ALL_CLIENTS.sql
|
|
12
12
|
- test/team/2.0 deployment/duplicate_items_merge_analysis.sql
|
|
13
13
|
- worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php
|
|
14
|
+
- worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php
|
|
14
15
|
- library/app/api/toga2.php
|
|
15
16
|
- worker/crons/toga2/netsuite/common_sync_togasupply.php
|
|
16
17
|
related:
|
|
17
18
|
- ../features/item-catalogs-and-duplicate-items.md
|
|
18
19
|
- ../features/mits-sales-order-transmission-alerting.md
|
|
20
|
+
- ../features/mits-po-transmission-to-vendors.md
|
|
21
|
+
- ../../../2.0/apps/toga2-commerce/features/bundle-item-visibility-and-selectability.md
|
|
19
22
|
- ../../../1.0/apps/worker/workflows/netsuite-duplicate-items-dedup.md
|
|
20
23
|
---
|
|
21
24
|
|
|
@@ -59,48 +62,116 @@ NULL-catalog junk items** and **199 duplicate-Agilant items** (from the NetSuite
|
|
|
59
62
|
inventory-adjustment item-create bug). **Only the 199 catalog-1 → catalog-2 folds were the
|
|
60
63
|
damage.**
|
|
61
64
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
65
|
+
**Part-number matching is unreliable here — the reversal maps dupe→canon by PRIMARY KEY, not
|
|
66
|
+
part number.** The Agilant twins carry **distinct** part-number variants, not shared ones: the
|
|
67
|
+
`#ABA` form does not equal the Compass part number (`C40QYUC#ABA` = item 2216 vs Compass
|
|
68
|
+
`C40QYUC` = 2157; `B7HJ2UC#ABA` = 2215 vs Compass `B7HJ2UC` = 2160). So any `UPPER(partNumber)`
|
|
69
|
+
or partNumber-twin re-point silently misses them, which is why the complete reversal keys off the
|
|
70
|
+
snapshot's own primary keys (below) and table-wide `partNumber` uniqueness still holds on prod.
|
|
71
|
+
(Left open only as history: how the original merge's `UPPER(partNumber)` partition folded pairs
|
|
72
|
+
that do not share a part number — the reversal does not depend on the answer, because it never
|
|
73
|
+
matches on part number.) Same note from the other side:
|
|
74
|
+
[two-catalog item model](../features/item-catalogs-and-duplicate-items.md).
|
|
75
|
+
|
|
76
|
+
## The first reversal was INCOMPLETE — it left 10 tables folded
|
|
77
|
+
|
|
78
|
+
The earlier reversal (`compass_twocatalog_fix.sql`) restored `Items` / `VendorItems` /
|
|
79
|
+
`VendorItemCosts` and re-pointed **`SalesOrderItems`** — but **only the `customerId = 2` slice**
|
|
80
|
+
of those. It **left folded** every other table the merge touched: `customerId = 1` and
|
|
81
|
+
`customerId = 3` `SalesOrderItems` (~50k rows), `ItemFeatures`, `ItemImages`, **`BundleItems`**,
|
|
82
|
+
`Personas_Items`, `Units`, `ItemPrices`, `ItemMedia`, `AssortmentItems`, and `InvoiceItems` — 10
|
|
83
|
+
tables still pointing at the Agilant canonical.
|
|
84
|
+
|
|
85
|
+
**Durable rule: a fold reversal MUST cover EVERY table the merge enumerates.** Restoring the
|
|
86
|
+
parent `Items` and re-pointing one child table is not a reversal. The
|
|
87
|
+
`duplicate_items_merge_ALL_CLIENTS.sql` block lists **38** reference points — read it as the
|
|
88
|
+
checklist:
|
|
89
|
+
|
|
90
|
+
- **24 `itemId` tables**,
|
|
91
|
+
- **`Entitlements.saleItemId`**,
|
|
92
|
+
- **3 `vendorItemId` tables** — `PurchaseOrderItems`, `VendorItemCosts`, `Personas_VendorItems`,
|
|
93
|
+
- **10 `unitId` tables**.
|
|
94
|
+
|
|
95
|
+
Anything on that list still resolving to a canonical is unreversed. The left-folded `BundleItems`
|
|
96
|
+
in particular was the ongoing source of *new* bad orders (see below).
|
|
97
|
+
|
|
98
|
+
## The complete reversal (durable method)
|
|
99
|
+
|
|
100
|
+
Restore **every** fold-affected row from the pre-merge snapshot
|
|
101
|
+
`Client_Compass_20260831_120000` (taken ~12:00, before the ~12:43 merge) **BY PRIMARY KEY** — not
|
|
102
|
+
by part-number twin matching (the `#ABA` variants make that unreliable, see above). The snapshot's
|
|
103
|
+
own primary keys are the only dependable dupe→canon mapping. **Validate the snapshot is pre-merge
|
|
104
|
+
first** (catalog-1 twins present, `customerId = 2` lines on catalog 1). Then, in a single
|
|
105
|
+
transaction, FK-checks-ON, **no `UPDATE IGNORE`**, COMMIT left commented (`SNAP_DB.` / `TARGET_DB.`
|
|
106
|
+
qualifiers):
|
|
107
|
+
|
|
108
|
+
1. **Parents — full-row restore.** `Items`, `VendorItems`, `VendorItemCosts`, `Units` restored
|
|
109
|
+
whole from the snapshot by id (`NOT EXISTS` guard).
|
|
110
|
+
2. **Children — FK-column-only restore.** For every child table on the merge's 38-point list,
|
|
111
|
+
restore **only the folded FK column** to its snapshot value, keyed by the child row's own PK.
|
|
112
|
+
(Verified zero non-FK drift, so nothing else is touched.)
|
|
113
|
+
3. **Post-snapshot "broken-window" rows** — rows created **after 2026-08-31 12:00** that are not
|
|
114
|
+
in the snapshot are re-pointed canon→dupe on **current** data, **scoped `customerId = 2`** (the
|
|
115
|
+
uuid-collision and customer-scope rules below still apply — the sync re-created order lines with
|
|
116
|
+
**new uuids**, so a uuid-restore collided 1062 on `UNIQUE(salesOrderId, lineNumber)`).
|
|
117
|
+
4. **Verify byte-for-byte.** This run: **0** rows still folded across all **38** tables and **0**
|
|
118
|
+
byte mismatches over **208,845** compared rows.
|
|
119
|
+
|
|
120
|
+
**Rehearse on a same-day full prod clone, then apply with a pre-run backup. CTO review gated the
|
|
121
|
+
approach** (rehearse-on-clone, fail-loud, customer-scoped, tenant-locked).
|
|
122
|
+
|
|
123
|
+
### Stragglers: `#ABA`-variant PO lines need a manual VendorItem re-map
|
|
124
|
+
Because the `#ABA` twins share no part number, neither the merge's part-number map nor a
|
|
125
|
+
partNumber-twin re-point catches PurchaseOrder lines sitting on them. These were fixed by an
|
|
126
|
+
**explicit VendorItem mapping**, scoped to **pending `customerId = 2` PO lines**, then clearing
|
|
127
|
+
`PurchaseOrders.dtSubmitted` so the PO re-transmits: VendorItem **3845 → 3597** @ `945.17`,
|
|
128
|
+
VendorItem **3884 → 3600** @ `1627.38`. Why a mis-mapped PO line transmits the wrong SKU + cost
|
|
129
|
+
and drops to EMAIL rather than cXML:
|
|
130
|
+
[MITS PO transmission to vendors](../features/mits-po-transmission-to-vendors.md).
|
|
131
|
+
|
|
132
|
+
## The folded `BundleItems` was the storefront's ongoing bad-order source
|
|
133
|
+
`BundleItems.itemId` (kit definitions) still pointed at the Agilant catalog-2 twins — **434 active
|
|
134
|
+
rows across 110 bundles**. The `toga2-commerce` storefront does **not** choose a catalog: it
|
|
135
|
+
relays whatever item uuid `GET /v2/bundles/{uuid}` returns (`bundleItems.item.uuid`). The
|
|
136
|
+
`catalogId = 1` filter runs **only** on standalone item search (`genericApi.ts`, `FilterApi.ts`,
|
|
137
|
+
`translatedSearchApi.ts`), **never** on bundles. So a folded `BundleItems.itemId` makes **every new
|
|
138
|
+
checkout of that kit POST the Agilant item** — a fresh wrong-catalog order each time. **The fix is
|
|
139
|
+
data (restore `BundleItems`); the frontend needs no change.** Storefront-side detail:
|
|
140
|
+
[bundle item visibility & selectability](../../../2.0/apps/toga2-commerce/features/bundle-item-visibility-and-selectability.md).
|
|
141
|
+
|
|
142
|
+
## The NetSuite→TOGa sync is NOT the source of new cust-2 bad orders (suspect ruled out)
|
|
143
|
+
The 1.0 NetSuite→TOGa TOGaSupply sync (`worker/crons/toga2/netsuite/`,
|
|
144
|
+
`App_Api_Toga2::syncSalesOrderFromNetsuite`, `library/app/api/toga2.php` ~L752) writes **only the
|
|
145
|
+
Agilant `customerId = 3` sales order** for Compass — it **pins** `locationId = 615286` and
|
|
146
|
+
`customerId = 3` (~L807-809) and cannot write or re-point a `customerId = 2` (Compass storefront)
|
|
147
|
+
order. **Stopping it fixed nothing.** The real ongoing source of new bad orders was the folded
|
|
148
|
+
`BundleItems` (above), not the sync — this corrects the earlier "the sync re-points cust-2 lines
|
|
149
|
+
back to Agilant / stop it first" concern. (The sync still *creates items* in catalog 2 — that is
|
|
150
|
+
the original duplicate-item source, see the two-catalog model.)
|
|
151
|
+
|
|
152
|
+
Context — the Compass order chain is up to **four** SalesOrders, linked by the
|
|
153
|
+
`SalesOrders_PurchaseOrders` / `PurchaseOrders_SalesOrders` bridge tables: Compass storefront SO
|
|
154
|
+
(`cust 2`) → MITS PurchaseOrder → ODP SO (`cust 1`) → Agilant/NetSuite SO (`cust 3`). The catalog
|
|
155
|
+
rule still holds: `cust 2` → catalog 1; `cust 1` and `cust 3` → catalog 2.
|
|
92
156
|
|
|
93
157
|
## Critical rules
|
|
94
158
|
|
|
159
|
+
- **Cover EVERY table the merge lists — restore by PRIMARY KEY, not by part number.** The merge
|
|
160
|
+
enumerates 38 reference points (24 `itemId` + `Entitlements.saleItemId` + 3 `vendorItemId` +
|
|
161
|
+
10 `unitId`); a reversal that touches only `Items` + one child table is incomplete (the first
|
|
162
|
+
attempt left 10 tables folded). `#ABA` twins share no part number, so partNumber matching misses
|
|
163
|
+
them — map dupe→canon from the snapshot's own PKs.
|
|
95
164
|
- **Scope every catalog re-point by `customerId`.** An unscoped
|
|
96
165
|
`UPDATE ... WHERE itemId = <agilantId>` wrongly moved **687 Office-Depot lines** onto the
|
|
97
166
|
Compass item and had to be corrected.
|
|
98
|
-
- **Re-point on CURRENT data, never restore order lines by uuid** — the sync
|
|
99
|
-
so a uuid-restore collides on `UNIQUE(salesOrderId, lineNumber)` (1062).
|
|
100
|
-
- **
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
167
|
+
- **Re-point post-snapshot rows on CURRENT data, never restore order lines by uuid** — the sync
|
|
168
|
+
minted new uuids, so a uuid-restore collides on `UNIQUE(salesOrderId, lineNumber)` (1062).
|
|
169
|
+
- **The NetSuite→TOGa sync is NOT the ongoing source of new cust-2 bad orders — the folded
|
|
170
|
+
`BundleItems` is.** `syncSalesOrderFromNetsuite` pins `customerId = 3` for Compass and writes
|
|
171
|
+
only the Agilant-tier SO, so it cannot re-point a cust-2 storefront line; stopping it changes
|
|
172
|
+
nothing. Restore `BundleItems` to stop new wrong-catalog checkouts. (The sync still *creates
|
|
173
|
+
items* in catalog 2 — that is the original duplicate-item source, see the
|
|
174
|
+
[two-catalog item model](../features/item-catalogs-and-duplicate-items.md).)
|
|
104
175
|
- **Never `UPDATE IGNORE` + delete-leftover for a catalog operation** — it permanently deletes
|
|
105
176
|
collision rows and cannot be reversed. Use FK-checks-ON, fail-loud, single transaction.
|
|
106
177
|
|
|
@@ -136,6 +207,23 @@ catalog-1 items were folded into catalog-2 **AND** storefront/transmit depends o
|
|
|
136
207
|
NULL-`assetType` order lines alone are **not** damage (Prudential and Quad show that as normal).
|
|
137
208
|
|
|
138
209
|
## Change history
|
|
210
|
+
- 2026-09-02 — **Completed the reversal and root-caused the rest of the incident.** Recorded that
|
|
211
|
+
the first reversal was **incomplete** — it restored `Items`/`VendorItems`/`VendorItemCosts` and
|
|
212
|
+
re-pointed only `customerId = 2` `SalesOrderItems`, leaving **10 tables** folded (cust-1/cust-3
|
|
213
|
+
`SalesOrderItems` ~50k, `ItemFeatures`, `ItemImages`, `BundleItems`, `Personas_Items`, `Units`,
|
|
214
|
+
`ItemPrices`, `ItemMedia`, `AssortmentItems`, `InvoiceItems`). Added the **cover-every-table**
|
|
215
|
+
rule (the merge lists 38 reference points: 24 `itemId` + `Entitlements.saleItemId` + 3
|
|
216
|
+
`vendorItemId` + 10 `unitId`) and the **complete method**: restore every fold-affected row from
|
|
217
|
+
the pre-merge snapshot **by PRIMARY KEY** (part-number matching fails because `#ABA` twins carry
|
|
218
|
+
distinct part numbers), parents full-row / children FK-column-only, post-snapshot rows re-pointed
|
|
219
|
+
cust-2 on current data; verified 0 folded across 38 tables / 0 byte mismatches over 208,845 rows.
|
|
220
|
+
Recorded the folded **`BundleItems`** (434 rows / 110 bundles) as the storefront's ongoing
|
|
221
|
+
bad-order source (toga2-commerce relays the bundle item uuid with no catalog filter — fix is
|
|
222
|
+
data), the **`#ABA` straggler** manual VendorItem re-map (3845→3597 @945.17, 3884→3600 @1627.38,
|
|
223
|
+
scoped cust-2 pending PO lines + clear `dtSubmitted`), and that the **NetSuite→TOGa sync is not**
|
|
224
|
+
the source of new cust-2 orders (it pins `customerId = 3` + `locationId = 615286`, writes only the
|
|
225
|
+
Agilant SO) — correcting the earlier "stop the sync first" rule. Resolved the part-number
|
|
226
|
+
contradiction for the reversal (maps by PK, never by partNumber). (jcardinal)
|
|
139
227
|
- 2026-09-01 — Flagged (did **not** resolve) the contradiction between this doc's 199
|
|
140
228
|
shared-`UPPER(partNumber)` catalog-1→catalog-2 folds and the same-day prod verification that zero
|
|
141
229
|
Compass part numbers span two catalogs; mirrored from the item-catalogs doc so it is visible from
|
package/package.json
CHANGED