toga-ai 1.0.102 → 1.0.104

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,4 +6,4 @@
6
6
  | [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
7
7
  | [Client Email Template Sending](features/email-template-sending.md) | `_Model_Client_EmailTemplate` sends a stored, client-defined email template by UUID. | _underscore/Model/Client/EmailTemplate.php, _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php, _underscore/Email.php |
8
8
  | [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql |
9
- | [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql |
9
+ | [Tracking-Number Bridge Migration (ASN / Item Fulfillment / Item Receipt)](features/tracking-number-bridges.md) | Shipment tracking numbers used to live as **scalar FK columns** (`trackingNumberId`, `returnTrackingNumberId`) directly on the lowest-level "unit"/"item" tables | api2/Component/Api/V2/V2.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php, _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php, _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Prudential/AdvanceShippingNotice.php, _underscore/Model/Compass/AdvanceShippingNotice.php, _underscore/Trait/Netsuite/ItemFulfillment.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client/2026-06-10 - TrackingNumberBridges.sql, dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql |
@@ -6,9 +6,10 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-10
10
- owners: ["jcardinal"]
9
+ updated: 2026-06-16
10
+ owners: ["jcardinal", "mhammontree"]
11
11
  files:
12
+ - api2/Component/Api/V2/V2.php
12
13
  - _underscore/Model/Client/AdvanceShippingNoticeItemUnit.php
13
14
  - _underscore/Model/Client/AdvanceShippingNoticeItemUnits/TrackingNumber.php
14
15
  - _underscore/Model/Client/ItemFulfillmentItemUnits/TrackingNumber.php
@@ -116,8 +117,25 @@ returnTrackingNumber: {...} }]`, and an IFIU's as `itemFulfillmentItemUnitTracki
116
117
  - **Deploy coupling:** renames are NOT backward compatible — apply the DB migration and deploy
117
118
  `_underscore` + `api2` + `worker` + `library` together. Run the additions/migrations DB file first;
118
119
  run the companion `..._DROPS.sql` (and the Core deletes) only after the bridges + app are confirmed.
120
+ - **⚠ Bridge ACL is incomplete — non-Super-User reads get 403 (prod + every client).** The migration
121
+ registers `AclRecordPermissions` for the new bridge records 317–322 (Core §7 = Super User; Client §6b
122
+ = per-client copies of the parent record's perms) but **never creates the
123
+ `AclLogicGroups` → `AclLogicGroupExpressions` → `AclRecordExpressions` chain beneath them.** A 2.0
124
+ record read requires that full chain (`api2/Component/Api/V2/V2.php`, `!$aclLogicGroupsDefined`), so a
125
+ role hitting a bridge route (e.g. `item-fulfillment-tracking-numbers`) gets **403 EZ-1** with DEBUG
126
+ "No ACL Logic Groups defined for ACL Record Permissions". Confirmed in **prod and every client** —
127
+ working analogs (parent 28, ASN bridges 216/217/218) have 1 logic group + 1 expression each; the IF/IR
128
+ bridges 317–322 have **0**. Downstream symptom: nested bridge fields return `null` (TOGa Supply's
129
+ fulfilled-shipments lists shipments but `itemFulfillmentTrackingNumbers` is null → reprint has no
130
+ label). **Fix:** per bridge `AclRecordPermission`, add one `AclLogicGroup` + `AclLogicGroupExpression` +
131
+ `AclRecordExpression` (`slug='all'`, `sqlExpression='1'`) — mirroring how 28 / 216 work. Run in **both**
132
+ `Core` (Super User) **and** each `Client_*` (per-role; `Client_Growrk` fixed on beta this way).
133
+ **Client-DB caveat:** `Records` is a Core-only table, so the client-side `AclRecordExpressions` insert
134
+ must derive record ids from `AclRecordPermissions` (`SELECT DISTINCT recordId ... WHERE recordId IN (...)`),
135
+ **not** join `Records`. This gap belongs back in the migration (Core §7 + Client §6b + blank-client template).
119
136
 
120
137
  ## Change history
138
+ - 2026-06-16 — Found + documented the bridge ACL gap: the migration creates bridge `AclRecordPermissions` (317–322) but not their `AclLogicGroups`/`AclLogicGroupExpressions`/`AclRecordExpressions` chain → 403 "No ACL Logic Groups defined" for non-Super-User reads (confirmed prod + all clients). Recorded the `slug='all'`/`sqlExpression='1'` fix for Core + per-client, plus the Client-DB `Records`-table caveat. (mhammontree)
121
139
  - 2026-06-10 — Documented the tracking-number bridge migration: scalar FK columns → `*_TrackingNumbers` bridge tables across ASN/IF/IR, ASN unit table rename, packages-table consolidation. (jcardinal)
122
140
 
123
141
  ## Related docs
@@ -6,7 +6,7 @@ project: TOGa Supply
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-10
9
+ updated: 2026-06-16
10
10
  owners: [mhammontree]
11
11
  files:
12
12
  - toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx
@@ -40,7 +40,10 @@ GroWrk, June 2026); FedEx + UPS both need verification before prod.
40
40
  **This is the only place the SO syncs from NetSuite into Toga.**
41
41
  2. `/edit-shipment` submit (`onFormSubmit("fulfillAndShip")` in `EditShipmentForm.tsx`):
42
42
  - `saveShipment` → POST `/item-fulfillments`, `/tracking-numbers`,
43
- `/item-fulfillment-packages`.
43
+ `/item-fulfillment-tracking-numbers` (the bridge — payload key
44
+ `itemFulfillmentTrackingNumbers`, **not** the old `itemFulfillmentPackages`; see the
45
+ `_underscore` tracking-number-bridges doc). The frontend migration to the bridge is prod
46
+ commit `b5c16592d`.
44
47
  - `fulfillShipment` → GET `/item-fulfillments/upsShipmentApi` (or `fedexShipmentApi`)
45
48
  for the label, then `saveShipmentToNetsuite` →
46
49
  GET `/item-fulfillments/createNetsuiteItemFulfillment`.
@@ -91,6 +94,29 @@ likely moves to the backend.
91
94
  `hostname LIKE '%ups.com%'`, `responsePayload`).
92
95
  - React warning: `setState`-in-render in `ShipmentItemsTable.tsx` (~line 149,
93
96
  `clearErrors` inside a `setRowsClicked` updater) — move out of the updater.
97
+ - **Local frontend runs against the *deployed* beta backend.** The dev frontend
98
+ (`growrk.togasupply:5173`) calls `api.beta.togahub.com`, so **any `_underscore` change
99
+ only takes effect after a deploy to beta** — a fix you made locally looks "unfixed" until
100
+ deployed (and a local frontend ahead of the deployed backend produces skew errors, e.g.
101
+ `Unknown named parameter $labelFileInternalId` when the FE passes a param the deployed
102
+ method lacks). When chasing a beta error, confirm what commit is deployed before assuming
103
+ the local code is what ran.
104
+ - **Fulfilled vs pending is driven by `ItemFulfillments.dtSubmitted`** (`ShipmentsApi.ts`
105
+ `getShipments`: fulfilled = `dtSubmitted ne null`, pending = `eq null`). `dtSubmitted` is
106
+ set **only** by `createNetsuiteItemFulfillment` on a successful NetSuite IF creation
107
+ (`Trait/Netsuite/ItemFulfillment.php`). So if the NetSuite step fails, the shipment stays
108
+ on the **pending** page, never the fulfilled one — "no fulfilled shipments" usually means
109
+ the NS step didn't complete, not a UI bug.
110
+ - **`createNetsuiteItemFulfillment` line mapping is fragile on partial/already-fulfilled
111
+ orders.** It `get`s the NetSuite SO and sets the fulfillment `orderLine` = the SO's `line`.
112
+ If a line is already fulfilled (or NetSuite re-sequences the fulfillable sublist because a
113
+ line isn't committed), NetSuite returns **"Unable to find a matching line for sublist item
114
+ with key: [orderLine] and value: [N]"**. Test against an SO line that's genuinely open;
115
+ robust fix = NetSuite `initialize`/`transform` (only exposes fulfillable lines).
116
+ - **Bridge ACL blocks the tracking/label read (reprint).** Reading tracking via the
117
+ `item-fulfillment-tracking-numbers` bridge requires the bridge record's ACL logic-group
118
+ chain to exist for the user's role, or it 403s and `itemFulfillmentTrackingNumbers` returns
119
+ `null` (so reprint has no label). See the tracking-number-bridges doc's ACL gotcha + fix.
94
120
 
95
121
  ## Remaining work
96
122
 
@@ -112,4 +138,5 @@ not the base `_Model_Client_ItemFulfillment`. Tested with GroWrk; UPS support wa
112
138
  for Compass and is not yet in prod.
113
139
 
114
140
  ## Change history
141
+ - 2026-06-16 — Drove the full beta flow green for UPS/GroWrk. Updated the save step to the `/item-fulfillment-tracking-numbers` bridge (FE commit `b5c16592d`, key `itemFulfillmentTrackingNumbers`). Added gotchas: local-FE↔deployed-beta-BE skew, `dtSubmitted` fulfilled/pending gating, `createNetsuiteItemFulfillment` orderLine fragility on partial/already-fulfilled orders, and the bridge-ACL 403 that nulls tracking/labels (reprint). (mhammontree)
115
142
  - 2026-06-10 — Documented the Fulfill & Ship flow (SO sync, label purchase, NetSuite IF creation, success gating, reprint). Driven green on beta for UPS/GroWrk; FedEx + prod verification pending. (mhammontree)
@@ -5,7 +5,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
5
5
  ## 1.0 framework
6
6
 
7
7
  - **library** (Library) _(framework core)_ — 4 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
- - **worker** (Worker) — 5 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
8
+ - **worker** (Worker) — 6 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
9
9
  - **togadesk** (TOGa Desk) — 7 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
10
10
  - **togaview** (TOGa View) — 5 doc(s) → [1.0/apps/togaview/INDEX.md](1.0/apps/togaview/INDEX.md)
11
11
  - **webhook** (Webhook) — 1 doc(s) → [1.0/apps/webhook/INDEX.md](1.0/apps/webhook/INDEX.md)
@@ -14,7 +14,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
14
14
 
15
15
  ## 2.0 framework
16
16
 
17
- - **_underscore** (_Underscore) _(framework core)_ — 6 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
17
+ - **_underscore** (_Underscore) _(framework core)_ — 7 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
18
18
  - **worker2** (Worker) — 6 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
19
19
  - **api2** (API) — 1 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
20
20
  - **dbchanges2** (Database Changes) _(framework core)_ — 1 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
@@ -3,5 +3,6 @@
3
3
  | Doc | Framework | Summary | Files |
4
4
  |-----|-----------|---------|-------|
5
5
  | [Prudential: Dell ASN units PRE/POST interceptor (legacy key + flat tracking)](features/dell-asn-units-interceptor.md) | 2.0 | After the tracking-number bridge migration, the ASN unit route was renamed (`advance-shipping-notice-units` → `advance-shipping-notice-item-units`), so the inhe | _underscore/Model/Prudential/AdvanceShippingNotice.php, dbchanges2/Client_Prudential/2026-06-10 - AsnUnitsInterceptor.sql |
6
+ | [Prudential: Service Request Regional Address Validation](features/service-request-address-validation.md) | 2.0 | The `prePost` interceptor on `_Model_Prudential_ServiceRequest` validates `deliverToAddress` fields differently depending on which Prudential regional customer | _underscore/Model/Prudential/ServiceRequest.php |
6
7
  | [Prudential Financial](profile.md) | 2.0 | Prudential is a TOGA client whose device-fulfillment flow is driven by **Dell** via the Dell API (`Client_Prudential.Apis.id = 2`). | |
7
8
  | [Prudential: Dell ASN failed POST backfill replay](workflows/dell-asn-backfill-replay.md) | 2.0 | When Dell ASN POSTs fail in bulk (e.g. | |
@@ -0,0 +1,101 @@
1
+ ---
2
+ title: "Prudential: Service Request Regional Address Validation"
3
+ framework: "2.0"
4
+ repo: _underscore
5
+ project: _Underscore
6
+ client: prudential
7
+ type: client-feature
8
+ status: active
9
+ updated: 2026-06-16
10
+ owners: ["rgirish"]
11
+ files:
12
+ - _underscore/Model/Prudential/ServiceRequest.php
13
+ related:
14
+ - clients/prudential/profile.md
15
+ - clients/prudential/features/dell-asn-units-interceptor.md
16
+ ---
17
+
18
+ ## Summary
19
+
20
+ The `prePost` interceptor on `_Model_Prudential_ServiceRequest` validates `deliverToAddress`
21
+ fields differently depending on which Prudential regional customer is placing the request.
22
+ The region is determined at runtime by looking up `customer.uuid` (sent in the POST payload)
23
+ against the `Client_Prudential.Customers` table, whose `name` column holds the region label.
24
+ Three regions are supported: **USA**, **India**, **Ireland**. Unknown or absent UUIDs fall
25
+ back to USA rules.
26
+
27
+ ## Key files / entry points
28
+
29
+ - `_underscore/Model/Prudential/ServiceRequest.php` — `prePost` interceptor; private methods
30
+ `resolveRegion()`, `validateUsaAddress()`, `validateIndiaAddress()`, `validateIrelandAddress()`
31
+
32
+ ## How it works
33
+
34
+ 1. `prePost` fires before each POST to `/v2/service-requests` for the Prudential client.
35
+ 2. `resolveRegion()` reads `payload->customer->uuid`, escapes it, and queries
36
+ `Client_Prudential.Customers` for the matching `name`. Returns `'India'`, `'Ireland'`, or
37
+ `'USA'` (default for anything unrecognised or when uuid is null).
38
+ 3. A `match` expression dispatches to the correct validator.
39
+ 4. On any validation failure, an `Exception` is thrown with a semicolon-delimited list of all
40
+ errors — the API engine catches it and returns a 4xx to Dell.
41
+
42
+ ## Data model
43
+
44
+ `Client_Prudential.Customers` — the region lookup table:
45
+
46
+ | Column | Type | Notes |
47
+ |--------|-----------|----------------------------------------------------|
48
+ | `id` | int PK | |
49
+ | `uuid` | char(36) | Sent by Dell in `customer.uuid` on every POST |
50
+ | `name` | varchar | Region label: `'USA'`, `'India'`, or `'Ireland'` |
51
+
52
+ ## Validation rules by region
53
+
54
+ ### USA (default)
55
+ | Field | Rule |
56
+ |------------------------|-------------------------------------------|
57
+ | `line1` | Required, max 35 chars |
58
+ | `line2` | Optional, max 35 chars |
59
+ | `city` | Required |
60
+ | `zip` | Required (any characters) |
61
+ | `state.code` | Required, exactly 2 alphabetic characters |
62
+
63
+ ### India (Dell India primary field spec)
64
+ | Field | Rule |
65
+ |-------------------|-------------------------------------------------------|
66
+ | `line1` | Required, max 30 chars (Customer/Consignee Name) |
67
+ | `line2` | Optional, max 30 chars (Address Line 1) |
68
+ | `line3` | Optional, max 30 chars (Address Line 2) |
69
+ | `city` | Required, max 30 chars (Address Line 3 / city) |
70
+ | `zip` | Required, max 6 chars, strictly numeric (0–9) |
71
+ | `mobileNumber` | Optional, 10–15 chars, numeric |
72
+ | `telephoneNumber` | Optional, 6–15 chars, numeric |
73
+ | `customerCode` | Optional, max 6 chars, alphanumeric |
74
+ | `originAreaCode` | Optional, max 3 chars, alphabetic (A–Z) |
75
+
76
+ India optional fields (`mobileNumber`, `telephoneNumber`, `customerCode`, `originAreaCode`)
77
+ are silently skipped when absent — Prudential has not yet started sending them.
78
+
79
+ ### Ireland
80
+ Same as USA except `zip` accepts any characters — Irish Eircode format is alphanumeric
81
+ with a space (e.g. `F92 FP83`). No numeric restriction.
82
+
83
+ ## Gotchas / known issues
84
+
85
+ - **Null UUID = USA fallback.** All requests sent before Prudential added `customer.uuid` to
86
+ their payload lacked the field entirely; those are treated as USA and pass through unchanged.
87
+ This is intentional backward compatibility.
88
+ - **Region check is by `Customers.name` string, not by id.** If the customer name is ever
89
+ changed in the DB (e.g. `'USA'` → `'United States'`), the validator will silently fall
90
+ back to USA rules for all regions. The match arms are `'India'` and `'Ireland'` only;
91
+ everything else maps to USA.
92
+ - **India optional fields are forward-compatible.** The Dell India field spec includes
93
+ `mobileNumber`, `telephoneNumber`, `customerCode`, and `originAreaCode` but Prudential has
94
+ not started sending them yet. The validators already handle them — no code change needed
95
+ when Prudential starts including them.
96
+ - **`line3` is India-only.** USA and Ireland validators do not check `line3` (it is not in
97
+ their Dell spec). If Dell starts sending it for USA/Ireland it is silently ignored.
98
+
99
+ ## Change history
100
+ - 2026-06-16 — Created: regional address validation (USA/India/Ireland) replacing the single
101
+ USA-only `validateDeliverToAddress` method; region resolved from `customer.uuid` lookup (rgirish)
@@ -9,11 +9,12 @@ project: _Underscore
9
9
  client: prudential
10
10
  type: profile
11
11
  status: active
12
- updated: 2026-06-10
13
- owners: ["jcardinal"]
12
+ updated: 2026-06-16
13
+ owners: ["jcardinal", "rgirish"]
14
14
  files: []
15
15
  related:
16
16
  - features/dell-asn-units-interceptor.md
17
+ - features/service-request-address-validation.md
17
18
  ---
18
19
 
19
20
  ## Summary
@@ -24,7 +25,10 @@ return-tracking per unit) into the 2.0 platform (DB `Client_Prudential`); the 1.
24
25
  order-status transmissions.
25
26
 
26
27
  ## Integrations
27
- - **Inbound:** Dell API → POST `/v2/advance-shipping-notices` (creates ASNs + units + tracking).
28
+ - **Inbound (ASNs):** Dell API → POST `/v2/advance-shipping-notices` (creates ASNs + units + tracking).
29
+ - **Inbound (Service Requests):** Dell API → POST `/v2/service-requests`. Payload includes
30
+ `customer.uuid` which maps to a regional customer record (`USA`, `India`, or `Ireland`) in
31
+ `Client_Prudential.Customers` — used to apply region-specific `deliverToAddress` validation.
28
32
  - **Outbound (worker crons):** `transmissions_to_netsuite.php`, `transmit_ordershipped_updates_prudential.php`,
29
33
  `transmit_closecomplete_updates_prudential.php`, `transmit_order_delivered_updates_from_fedex_toga2.php`,
30
34
  `update_tracking_number_from_netsuite_to_toga.php`, `manually_insert_ASN.php`.
@@ -32,6 +36,8 @@ order-status transmissions.
32
36
  ## Gotchas
33
37
  - Dell will not change their payload shape — see the Dell ASN units interceptor feature doc for the
34
38
  PRE/POST translation that keeps their feed working after the tracking-number bridge migration.
39
+ - Service request region is resolved from `Customers.name` string (`'USA'`/`'India'`/`'Ireland'`).
40
+ Renaming those customer records in the DB would silently break regional validation routing.
35
41
 
36
42
  ## Related docs
37
43
  - Dell ASN units interceptor.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.102",
3
+ "version": "1.0.104",
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",