toga-ai 1.0.913 → 1.0.915
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/api2/features/nested-relationship-writes.md +3 -1
- package/knowledge/2.0/apps/api2/features/tableview-apiwhereclause-row-filtering.md +34 -1
- package/knowledge/INDEX.md +1 -1
- package/knowledge/clients/elite/features/supply2-tableview-config-drift.md +21 -1
- package/knowledge/clients/prudential/INDEX.md +1 -0
- package/knowledge/clients/prudential/features/dell-lch-iop-transmissions.md +6 -1
- package/knowledge/clients/prudential/features/service-request-address-validation.md +6 -2
- package/knowledge/clients/prudential/features/service-request-bundle-catalog-resolution.md +56 -0
- package/knowledge/clients/prudential/profile.md +2 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-29
|
|
10
10
|
owners: ["bala", "mhammontree", "tcox", "apeterson", "rgirish", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- toga25-supply/src/pages/TransferOrders/view/CreateTransferOrderModal/hooks/useCreateTransferOrder.ts
|
|
@@ -193,9 +193,11 @@ behaviour — it bites any write to a nested child, not one feature. Worked exam
|
|
|
193
193
|
- **The UPDATE path returns 200 even when a child write failed** — write children directly against the child route and check each, if you need to know they landed.
|
|
194
194
|
- **Not every record is linkable by `uuid`** — read `searchableIdentifierFields` in the EV-12 body; fall back to `id` resolved by a natural key, never hardcoded.
|
|
195
195
|
- **Declaring a `c_` field on the model does not make it searchable** — the `CustomRecordFields.isIdentifier` flag is the gate; set the flag, not the child policy.
|
|
196
|
+
- **An identifier that is unique only inside a parent (e.g. `bundles.number` per catalog) links the WRONG record, silently.** The match uses only the identifier fields sent, so `bundle: {number: "001"}` hits another catalog's `001`. Send `uuid` (or add the scoping identifier) resolved in the caller. Seen on Prudential India REQs — see [bundle catalog resolution](../../../clients/prudential/features/service-request-bundle-catalog-resolution.md).
|
|
196
197
|
- **Singular-FK back-reference defects:** UPDATE path drops the parent's forward FK (echoes `null`); CREATE path re-parents a value-matched child to the newest writer (stolen child row). One defect, two symptoms.
|
|
197
198
|
|
|
198
199
|
## Change history
|
|
200
|
+
- 2026-09-29 — Gotcha: a per-parent identifier (`bundles.number`) matches across parents and links the wrong row. (rgirish)
|
|
199
201
|
- 2026-09-24 — Documented that the engine recurses into a nested inherent child with `POST`
|
|
200
202
|
hardcoded (`V2.php:5415`), so a child nested in a PUT is duplicated or fails 1062; update
|
|
201
203
|
children through their own route (TRUE-82138). (apeterson)
|
|
@@ -6,7 +6,7 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-29
|
|
10
10
|
owners: ["bala", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
@@ -17,6 +17,7 @@ related:
|
|
|
17
17
|
- ../../../clients/compass-usa/features/item-catalogs-and-duplicate-items.md
|
|
18
18
|
- ../../../clients/elite/features/supply2-tableview-config-drift.md
|
|
19
19
|
- tableview-field-metadata.md
|
|
20
|
+
- ../../_underscore/features/calculated-sql-fields.md
|
|
20
21
|
---
|
|
21
22
|
|
|
22
23
|
## Summary
|
|
@@ -167,8 +168,40 @@ None — engine behavior. A specific client's view may carry its own `apiWhereCl
|
|
|
167
168
|
- **Operator allowlist.** `parseOptionsWhere` whitelists the operators above and rejects unknown
|
|
168
169
|
ones (e.g. `regexp` returns a 500) — see the Compass cost-centers doc. Numeric/regex filtering
|
|
169
170
|
that the grammar can't express must be done client-side or by adding an operator to api2.
|
|
171
|
+
- **⚠ The FIELD name is never validated — an unknown one is a bare 500 with an EMPTY body.**
|
|
172
|
+
`parseOptionsWhere` maps only the operator token; the field slot passes straight into the SQL.
|
|
173
|
+
A name no model declares reaches MySQL as `Unknown column '<x>' in 'where clause'`, nothing
|
|
174
|
+
catches it, and the request dies as a 500 with **no message at all** (the 1.0 monitor then reports
|
|
175
|
+
its fallback literal *"No message found in response payload"* — see
|
|
176
|
+
[v2-api-error-codes](v2-api-error-codes.md) note 13). Live case: a `_isServiceClass` condition
|
|
177
|
+
stored on Elite's `inventory_items` clause 500'd `GET /v2/items` **91 times in 5 minutes across
|
|
178
|
+
7 prod api2 instances** (2026-09-17). **Open gap:** api2 has no guard that rejects an unknown
|
|
179
|
+
`_`-prefixed field with a 400 before it reaches MySQL.
|
|
180
|
+
- **⚠ Deploy order: the `_underscore` model field ships BEFORE the migration that references it.**
|
|
181
|
+
An `_`-prefixed name in a clause only resolves if the model declares it `FIELD_SQL` with a
|
|
182
|
+
matching same-name static (e.g. `_qtyOnHand` on `_Model_Client_Item`) — see
|
|
183
|
+
[calculated SQL fields](../../_underscore/features/calculated-sql-fields.md). A clause is **data**,
|
|
184
|
+
so it goes live the instant the `dbchanges2` file runs, with no deploy gate; applying it first
|
|
185
|
+
takes the listing endpoint down for that tenant. Check the client's own model too — a
|
|
186
|
+
`_Model_<Client>_Item` inherits from `_Model_Client_Item` and may declare neither.
|
|
187
|
+
- **An unexplained field in a `where=` lives in the DB, not the frontend.** Filters reaching api2
|
|
188
|
+
are stored in `Client_<Tenant>.TableViews.apiWhereClause`, not written in app code. When a field
|
|
189
|
+
name appears in **no** repo and **no** git history, query `TableViews` across the client DBs —
|
|
190
|
+
that is where it is.
|
|
170
191
|
|
|
171
192
|
## Change history
|
|
193
|
+
- 2026-09-29 — **Recorded that an unvalidated field name in a stored clause is a production
|
|
194
|
+
outage class, not a typo.** `parseOptionsWhere` maps only the operator and passes the field slot
|
|
195
|
+
straight to MySQL, so an undeclared `_`-prefixed name becomes `Unknown column` → an uncaught
|
|
196
|
+
500 with an **empty body**. Live case: a draft `_isServiceClass` condition on Elite's
|
|
197
|
+
`inventory_items` view 500'd `GET /v2/items` 91 times in 5 minutes across 7 prod api2 instances
|
|
198
|
+
(2026-09-17); `_isServiceClass` was declared on no model and existed in no repo or git history.
|
|
199
|
+
Added the deploy-ordering rule (the `_underscore` `FIELD_SQL` field must be deployed **before**
|
|
200
|
+
the `dbchanges2` file that references it, because a clause is data and needs no deploy), the
|
|
201
|
+
debugging path (an unexplained `where=` field lives in `TableViews.apiWhereClause`, not the
|
|
202
|
+
frontend repo), and the open gap that api2 rejects unknown operators but not unknown fields.
|
|
203
|
+
Diagnosis only, no code changed. Prod re-verified 2026-09-29: zero `TableViews` rows across all
|
|
204
|
+
37 `Client_*` databases reference the field. (bala)
|
|
172
205
|
|
|
173
206
|
- 2026-09-17 — **Recorded that subqueries ARE possible in a stored `apiWhereClause`**: the parser
|
|
174
207
|
splits on literal spaces, so writing `/**/` in place of every space (MySQL reads it as
|
package/knowledge/INDEX.md
CHANGED
|
@@ -19,7 +19,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
19
19
|
|
|
20
20
|
## 2.0 framework
|
|
21
21
|
|
|
22
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
22
|
+
- **_underscore** (_Underscore) _(framework core)_ — 93 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
23
23
|
- **worker2** (Worker) — 80 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
24
24
|
- **api2** (API) — 26 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
25
25
|
- **dbchanges2** (Database Changes) _(framework core)_ — 22 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: elite
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-29
|
|
10
10
|
owners: [tcox, bala, rgirish]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Client_Elite/
|
|
@@ -297,6 +297,17 @@ for the technique and the parser mechanics.
|
|
|
297
297
|
required a `_isServiceClass` model field on `_Model_Elite_Item`; **that field does not exist and is
|
|
298
298
|
not needed** — do not add one.
|
|
299
299
|
|
|
300
|
+
**⚠ That earlier draft reached PRODUCTION and took `/v2/items` down.** Before the rewrite, the
|
|
301
|
+
stored clause was `(Items.partNumber:excludes:SVC-,_isServiceClass:eq:0)`. `_isServiceClass` is
|
|
302
|
+
declared on neither `_Model_Client_Item` nor `_Model_Elite_Item`, so the raw name went into the SQL
|
|
303
|
+
and MySQL threw `Unknown column '_isServiceClass' in 'where clause'` — **91 empty-body 500s on
|
|
304
|
+
`GET /v2/items` in 5 minutes across 7 prod api2 instances (2026-09-17)**. A stored clause needs no
|
|
305
|
+
deploy, so it went live the moment the migration ran. Closed: `2026-09-15a` reverted the clause and
|
|
306
|
+
the current one uses only `_qtyOnHand` / `_qtyAvailable`, which **are** declared `FIELD_SQL`.
|
|
307
|
+
Re-verified 2026-09-29 — zero `TableViews` rows in any of the 37 prod `Client_*` databases mention
|
|
308
|
+
`ServiceClass`. The general rule and the debugging path are in
|
|
309
|
+
[apiWhereClause row filtering](../../../2.0/apps/api2/features/tableview-apiwhereclause-row-filtering.md#gotchas--known-issues).
|
|
310
|
+
|
|
300
311
|
### Why the class check replaced the `SVC-` prefix
|
|
301
312
|
|
|
302
313
|
The `SVC-` prefix was an unreliable signal. Verified on **prod `Client_Elite`** (2026-09-15):
|
|
@@ -388,6 +399,15 @@ the old `SVC-` prefix check back into the clause.
|
|
|
388
399
|
[error-reporting-issue-event](../../../2.0/apps/_underscore/features/error-reporting-issue-event.md).
|
|
389
400
|
|
|
390
401
|
## Change history
|
|
402
|
+
- 2026-09-29 — **Recorded that the abandoned `_isServiceClass` draft clause actually shipped to
|
|
403
|
+
production and caused an outage**, which this doc previously reported only as "does not exist and
|
|
404
|
+
is not needed". The stored clause `(Items.partNumber:excludes:SVC-,_isServiceClass:eq:0)` put an
|
|
405
|
+
undeclared field into the SQL, giving `Unknown column '_isServiceClass' in 'where clause'` and 91
|
|
406
|
+
empty-body 500s on `GET /v2/items` in 5 minutes across 7 prod api2 instances (2026-09-17).
|
|
407
|
+
Incident is closed — `2026-09-15a` reverted the clause, the live one uses only the declared
|
|
408
|
+
`FIELD_SQL` fields `_qtyOnHand` / `_qtyAvailable`, and a 2026-09-29 sweep of all 37 prod
|
|
409
|
+
`Client_*` databases found no `TableViews` row mentioning `ServiceClass`. Diagnosis only, no code
|
|
410
|
+
changed. (bala)
|
|
391
411
|
- 2026-09-17 — **Replaced the `SVC-` partNumber prefix filter with an ItemClass-hierarchy check**
|
|
392
412
|
on the `inventory_items` view (`2026-09-15a - InventoryHideServiceClassItems.sql`): the clause
|
|
393
413
|
now walks the `ItemClasses` parent chain and excludes items whose ROOT class id is 1, 22 or 40,
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
| [Prudential: order email PDF attachments (which guide goes on which email)](features/order-email-pdf-attachments.md) | 1.0 | Which S3 PDF guide attaches to which Prudential order email by service-request type/stage, and how to change an attachment safely; open before editing any Prude |
|
|
11
11
|
| [Prudential: Service Request Regional Address Validation](features/service-request-address-validation.md) | 2.0 | The prePost interceptor that validates a Prudential service request's deliverToAddress per region (USA/India/Ireland) and rejects a blank customer uuid; open fo |
|
|
12
12
|
| [Prudential: refused REQ alert (401/403 on POST /v2/service-requests)](features/service-request-auth-rejection-alert.md) | 1.0 | The 15-min cron that alerts on refused (401/403) inbound Prudential REQs, where each code is logged, and how to tell "we refused it" from "they never sent it"; |
|
|
13
|
+
| [Prudential: Service Request bundle lookup scoped to the customer's catalog (wrong Dell India provision profile)](features/service-request-bundle-catalog-resolution.md) | 2.0 | Why India REQs link to USA bundles (bundle numbers repeat across catalogs) and the prePost fix that resolves the bundle uuid inside the customer's catalog; open |
|
|
13
14
|
| [Prudential: Service Request rejection alert email](features/service-request-rejection-alert-email.md) | 2.0 | The real-time internal alert email fired when a Prudential SR POST is rejected by validation (400); open for SR rejection alerting or why a rejected REQ did/did |
|
|
14
15
|
| [Prudential: Sales Orders CSV export (TOGa 2.5 Supply)](features/supply-orders-csv-export.md) | 2.0 | The Prudential-only Sales Orders CSV export in toga25-supply: its two queries, the join fixes, and the fan-out/dedupe rule; open before touching the export or i |
|
|
15
16
|
| [Prudential Order Shipped Email — transmit_ordershipped_updates_prudential.php](features/transmit-ordershipped-email.md) | 1.0 | The two-phase Order Shipped transmit+email cron, its stranding flaws, the PDF-fetch-throws root cause, and the overloaded close column; open before touching Pru |
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: prudential
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-29
|
|
10
10
|
owners: ["rgirish", "bala"]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/delllch.php
|
|
@@ -18,6 +18,7 @@ files:
|
|
|
18
18
|
related:
|
|
19
19
|
- ../profile.md
|
|
20
20
|
- dell-asn-units-interceptor.md
|
|
21
|
+
- service-request-bundle-catalog-resolution.md
|
|
21
22
|
---
|
|
22
23
|
Outbound 1.0 worker transmissions to Dell's LCH ITSM via LCHRequestV2, which cron is live, and the two ways an order silently fails to reach Dell; open for Dell send debugging or the V2 payload schema.
|
|
23
24
|
|
|
@@ -101,6 +102,7 @@ Verification plan:
|
|
|
101
102
|
3. After beta passes a full order cycle (New → shipped), confirm the production script is live.
|
|
102
103
|
|
|
103
104
|
## Gotchas
|
|
105
|
+
- **Provision profile comes from the linked bundle (`Bundles.c_provisionProfile`).** If the REQ is linked to the wrong region's bundle, Dell gets the wrong profile — India REQs sent USA `10649405` instead of `74361088` until 2026-09. Cause and fix: [bundle catalog resolution](service-request-bundle-catalog-resolution.md).
|
|
104
106
|
- **⚠ NULL `ServiceRequests.customerId` = order silently never reaches Dell.** The selection query's `INNER JOIN Customers ON Customers.id = ServiceRequests.customerId` drops any REQ whose `customerId` is NULL, so it is never selected, never validated, never POSTed — and the Dell PO's `dtSubmitted` stays NULL forever (not sent, not marked, invisible). This is the sole differentiator behind "Dell has no trace of the REQ though Talos says we sent it": SO/PO/unit creation all succeeded, only transmission was skipped. At one investigation 79 Dell POs were stuck this way, all with `customerId` NULL. The 2.0-side guard in `service-request-address-validation.md` stops NEW customer-less REQs; already-stuck ones need a `customerId` backfill from ship-to country.
|
|
105
107
|
- **Root cause is a TOGa-side mapping gap, not missing SNOW data.** The inbound ServiceNow payload carries **no customer field** at all — `ServiceRequests.customerId` is assigned *inside TOGa* by `generate_sales_and_purchase_orders_from_service_requests.php`, so a NULL means the generation cron failed to derive/assign a region, not that SNOW omitted anything.
|
|
106
108
|
- **The country-split cutover (~2026-07-17) is when this started biting.** Before the split, `transmissions_to_dell_v1.php` had **no customer gate**, so every REQ transmitted regardless. The regional split files added `INNER JOIN Customers` + `WHERE Customers.uuid = '<region>'`, which drops NULL-`customerId` REQs. ~38 REQs stranded this way since the cutover.
|
|
@@ -110,6 +112,9 @@ Verification plan:
|
|
|
110
112
|
- Dell deprecated `LCHRequest`; any script still POSTing to the old route will fail. All four transmission scripts must point at `LCHRequestV2`.
|
|
111
113
|
- The removed fields (e.g. `deviceType`, `lob`, `imageType`) will be rejected by V2 validation — do not reintroduce them.
|
|
112
114
|
|
|
115
|
+
## Change history
|
|
116
|
+
- 2026-09-29 — Noted the provision profile is read from the linked bundle; wrong bundle link sent India REQs the USA profile. (rgirish)
|
|
117
|
+
|
|
113
118
|
## Related
|
|
114
119
|
- Prudential: [Dell ASN units interceptor](dell-asn-units-interceptor.md) (inbound 2.0 counterpart).
|
|
115
120
|
- [Prudential profile](../profile.md).
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: prudential
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-29
|
|
10
10
|
owners: ["rgirish", "bala"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Prudential/ServiceRequest.php
|
|
@@ -16,6 +16,7 @@ related:
|
|
|
16
16
|
- clients/prudential/features/dell-asn-units-interceptor.md
|
|
17
17
|
- clients/prudential/features/dell-lch-iop-transmissions.md
|
|
18
18
|
- clients/prudential/features/service-request-rejection-alert-email.md
|
|
19
|
+
- clients/prudential/features/service-request-bundle-catalog-resolution.md
|
|
19
20
|
- ../../../2.0/apps/_underscore/features/model-interceptor-unit-testing.md
|
|
20
21
|
---
|
|
21
22
|
The prePost interceptor that validates a Prudential service request's deliverToAddress per region (USA/India/Ireland) and rejects a blank customer uuid; open for SR address-validation rules or customer-less REQ stranding.
|
|
@@ -26,7 +27,7 @@ The `prePost` interceptor on `_Model_Prudential_ServiceRequest` validates `deliv
|
|
|
26
27
|
## How it works
|
|
27
28
|
|
|
28
29
|
### Key files / entry points
|
|
29
|
-
- `_underscore/Model/Prudential/ServiceRequest.php` — `prePost` interceptor; private methods `validateCustomer()`, `resolveRegion()`, `validateUsaAddress()`, `validateIndiaAddress()`, `validateIrelandAddress()`.
|
|
30
|
+
- `_underscore/Model/Prudential/ServiceRequest.php` — `prePost` interceptor; private methods `validateCustomer()`, `resolveRegion()`, `validateUsaAddress()`, `validateIndiaAddress()`, `validateIrelandAddress()`. After validation, `prePost` also calls `resolveBundlesInCustomerCatalog()` — see [bundle catalog resolution](service-request-bundle-catalog-resolution.md).
|
|
30
31
|
|
|
31
32
|
### Flow
|
|
32
33
|
1. `prePost` fires before each POST to `/v2/service-requests` for the Prudential client.
|
|
@@ -76,6 +77,9 @@ India optional fields (`mobileNumber`, `telephoneNumber`, `customerCode`, `origi
|
|
|
76
77
|
- **`line3` is India-only.** USA and Ireland validators do not check `line3` (it is not in their Dell spec). If Dell starts sending it for USA/Ireland it is silently ignored.
|
|
77
78
|
- **⚠ OPEN (pre-existing HIGH) — unguarded unit access after the validation block throws a 500.** Code that runs *after* the validation block reads `serviceRequestUnits[0]->unit->serialNumber` / `->assetTag` without guarding for a missing `serviceRequestUnits`. A payload with no units throws an **uncaught non-validation `_Exception` → HTTP 500** (which dumps globals in debug mode) and, because it is not an `_Exception_Validation`, it **skips the rejection-alert email** entirely. Fix: guard the unit access (or validate `serviceRequestUnits` presence inside the collected-error block) so it fails as a 400 with an alert instead of a silent 500. Tracked as a follow-up.
|
|
78
79
|
|
|
80
|
+
## Change history
|
|
81
|
+
- 2026-09-29 — prePost now also resolves bundle uuids inside the customer's catalog (separate doc). (rgirish)
|
|
82
|
+
|
|
79
83
|
## Related
|
|
80
84
|
- [Prudential profile](clients/prudential/profile.md)
|
|
81
85
|
- [Dell ASN units interceptor](clients/prudential/features/dell-asn-units-interceptor.md)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Prudential: Service Request bundle lookup scoped to the customer's catalog (wrong Dell India provision profile)"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: prudential
|
|
7
|
+
type: client-feature
|
|
8
|
+
status: draft
|
|
9
|
+
updated: 2026-09-29
|
|
10
|
+
owners: ["rgirish"]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Model/Prudential/ServiceRequest.php
|
|
13
|
+
- api2/Component/Api/V2/V2.php
|
|
14
|
+
- worker/crons/toga2/prudential/transmissions_to_dell_india.php
|
|
15
|
+
related:
|
|
16
|
+
- service-request-address-validation.md
|
|
17
|
+
- dell-lch-iop-transmissions.md
|
|
18
|
+
- ../profile.md
|
|
19
|
+
- ../../../2.0/apps/api2/features/nested-relationship-writes.md
|
|
20
|
+
---
|
|
21
|
+
Why India REQs link to USA bundles (bundle numbers repeat across catalogs) and the prePost fix that resolves the bundle uuid inside the customer's catalog; open for a wrong Dell provision profile or bundle mis-link.
|
|
22
|
+
|
|
23
|
+
## Summary
|
|
24
|
+
ServiceNow POSTs `/v2/service-requests` with `serviceRequestBundles:[{bundle:{number:"001"}}]` + `customer:{uuid}`. Bundle numbers are **not unique across catalogs**, and api2 matches the nested bundle on number alone, so every India REQ linked to a **USA** bundle. The Dell India cron then sent the USA provision profile **10649405** instead of India's **74361088** (reported by Dell India). Fix: `_Model_Prudential_ServiceRequest::prePost` adds the bundle `uuid` found inside the customer's own catalog. **Status: fix written, reviewed (php-reviewer + cso clean), uncommitted, NOT deployed, NOT tested end to end.**
|
|
25
|
+
|
|
26
|
+
## How it works
|
|
27
|
+
|
|
28
|
+
### Data (prod, 2026-09-29)
|
|
29
|
+
- Customer → catalog is `Customers_Catalogs`: USA customer 3 → catalog 2, India 4 → catalog 3, Ireland 5 → catalog 4 (Ireland catalog has no bundles).
|
|
30
|
+
- USA catalog 2 has bundles numbered `001` and `002`. India catalog 3 has `001` (id 105, Knowledge Worker) and `002` (id 106, Developer), both `c_provisionProfile = 74361088`.
|
|
31
|
+
- `transmissions_to_dell_india.php` reads the provision profile from `Bundles.c_provisionProfile` of the REQ's linked bundle. A wrong bundle link = a wrong profile sent to Dell.
|
|
32
|
+
|
|
33
|
+
### Fix — `resolveBundlesInCustomerCatalog()` (private, called in `prePost()` after validation)
|
|
34
|
+
1. For each `serviceRequestBundles[].bundle` that has a `number` and no `uuid`:
|
|
35
|
+
2. Look up `Bundles` by that number inside the customer's catalog (`Customers` → `Customers_Catalogs` → `Bundles`), by `customer.uuid`.
|
|
36
|
+
3. Exactly 1 match → **add** `bundle.uuid`, **keep** `bundle.number` (other scripts/interceptors may read the number).
|
|
37
|
+
4. 0 matches → leave the payload as sent. No reject (developer's choice).
|
|
38
|
+
|
|
39
|
+
### Why adding uuid (and keeping number) works
|
|
40
|
+
- `Core.RecordFields` for record 43 `bundles` has `uuid`, `name`, `number`, `catalogId` all `isIdentifier = 1`, and `Client_Prudential` has no `Apis_RecordFields` overrides for them. So `getForeignKeyValue` (`V2.php` ~7434) searches **number AND uuid together** — only the catalog-correct bundle matches.
|
|
41
|
+
- A nested object holding **only** `uuid` forces `CHILD_POLICY__MATCH` (`V2.php` ~7460). Keeping `number` means the object is not uuid-only; both shapes resolve to the same row here.
|
|
42
|
+
|
|
43
|
+
## Gotchas
|
|
44
|
+
- **Bundle `number` is unique only within a catalog.** Never resolve a bundle by number alone — scope it by the customer's catalog, or send `uuid`.
|
|
45
|
+
- **ServiceNow sends `"003"` for India Developer, but India Developer is `"002"`.** `003` has no match in the India catalog, so the fix leaves it unchanged and api2 still links it to USA bundle id 3. Action: ServiceNow must send `"002"` for India Developer.
|
|
46
|
+
- **The 129 India REQs already sent (2026-05-28 .. 2026-09-25, customer id 4) are NOT re-linked** (decided 2026-09-29). They sit on USA bundles 1 (127) and 3 (2) and were all transmitted to Dell with the USA bundle's profile. Do not "clean them up" without a new decision.
|
|
47
|
+
- No-match does not reject. A REQ with an unknown bundle number for its catalog is still created on whatever api2 matches by number.
|
|
48
|
+
|
|
49
|
+
## Change history
|
|
50
|
+
- 2026-09-29 — Root-caused the USA provision profile on India REQs; added catalog-scoped bundle uuid resolution in prePost (not deployed). (rgirish)
|
|
51
|
+
|
|
52
|
+
## Related
|
|
53
|
+
- [Service Request address validation](service-request-address-validation.md) — the same `prePost` interceptor.
|
|
54
|
+
- [Dell LCH IOP transmissions](dell-lch-iop-transmissions.md) — the India cron that sends the profile.
|
|
55
|
+
- [api2 nested relationship writes](../../../2.0/apps/api2/features/nested-relationship-writes.md) — identifier matching rules.
|
|
56
|
+
- [Prudential profile](../profile.md)
|
|
@@ -17,7 +17,7 @@ project: _Underscore
|
|
|
17
17
|
client: prudential
|
|
18
18
|
type: profile
|
|
19
19
|
status: active
|
|
20
|
-
updated: 2026-09-
|
|
20
|
+
updated: 2026-09-29
|
|
21
21
|
owners: ["jcardinal", "rgirish", "bala", "mhammontree"]
|
|
22
22
|
files: []
|
|
23
23
|
related:
|
|
@@ -35,6 +35,7 @@ related:
|
|
|
35
35
|
- features/order-email-pdf-attachments.md
|
|
36
36
|
- features/itemclasses-device-tier-data.md
|
|
37
37
|
- workflows/store-saml-deploy-topology.md
|
|
38
|
+
- features/service-request-bundle-catalog-resolution.md
|
|
38
39
|
---
|
|
39
40
|
|
|
40
41
|
## Summary
|
package/package.json
CHANGED