toga-ai 1.0.52 → 1.0.53

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.
@@ -2,6 +2,6 @@
2
2
 
3
3
  | Doc | Framework | Summary | Files |
4
4
  |-----|-----------|---------|-------|
5
- | [Compass ASN → ItemFulfillment Auto-Creation](features/asn-to-item-fulfillment.md) | 2.0 | For Compass USA, posting an AdvanceShippingNotice (ASN) auto-creates the ItemFulfillment (IF) on the upstream SalesOrder. | _underscore/Model/Compass/AdvanceShippingNotice.php |
5
+ | [Compass ASN → ItemFulfillment Auto-Creation](features/asn-to-item-fulfillment.md) | 2.0 | For Compass USA, posting an AdvanceShippingNotice (ASN) auto-creates the ItemFulfillment (IF) on the upstream SalesOrder. | _underscore/Model/Compass/AdvanceShippingNotice.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client_Compass/2026-06-11 - AsnItemTrackingNumberAcl.sql |
6
6
  | [Compass: Item Fulfillments for Sales Order Items TableView (tracking via bridge)](features/item-fulfillment-tracking-tableview.md) | 2.0 | The Compass TableView `item-fulfillments-for-sales-order-items` (`TableViews.id = 13` in Client_Compass) was rebuilt as part of the tracking-number bridge migra | dbchanges2/Client_Compass/2026-06-10 - ItemFulfillmentsForSalesOrderItemsTableView.sql |
7
7
  | [Compass USA](profile.md) | 2.0 | Compass USA is a TOGA client running a multi-tier supply-chain commerce operation. | |
@@ -5,10 +5,12 @@ project: _Underscore
5
5
  client: compass-usa
6
6
  type: client-feature
7
7
  status: active
8
- updated: 2026-06-08
8
+ updated: 2026-06-11
9
9
  owners: [jcardinal]
10
10
  files:
11
11
  - _underscore/Model/Compass/AdvanceShippingNotice.php
12
+ - api2/Component/Api/Cxml/Cxml.php
13
+ - dbchanges2/Client_Compass/2026-06-11 - AsnItemTrackingNumberAcl.sql
12
14
  related:
13
15
  - ../../../2.0/apps/_underscore/features/recursive-item-fulfillments.md
14
16
  ---
@@ -21,41 +23,81 @@ IFIUs for serialized units, and IF packages for tracking. ASNs arrive two ways:
21
23
  **cXML** (direct V2 API) and the worker email cron
22
24
  `3b_import_strategic_systems_advance_shipping_notices.php`.
23
25
 
26
+ Tracking numbers are propagated across **every** ASN and IF granularity (see *Tracking number
27
+ propagation* below), so a single ShipNotice tracking number lands at the header, item, and
28
+ unit level on both sides — not just on one unit.
29
+
24
30
  ## Key files / entry points
31
+ - `api2/Component/Api/Cxml/Cxml.php` — the cXML `ShipNoticeRequest` translator that builds
32
+ the `/advance-shipping-notices` POST payload.
25
33
  - `_underscore/Model/Compass/AdvanceShippingNotice.php` — `postPost(&$api, &$payload)`.
26
34
  - Fires only when the Compass ASN `ApiPayloadInterceptors` rows exist (interceptor is DB-driven).
35
+ - `Model/Compass/Usa/` and `Model/Compass/Canada/` ASN classes are **empty subclasses** that
36
+ inherit `postPost` from `_Model_Compass_AdvanceShippingNotice` — edit the parent.
27
37
 
28
38
  ## How it works
29
39
  1. Read ASN → resolve PO → SO via `SalesOrders_PurchaseOrders`; skip if no SO.
30
40
  2. Select ASN items whose SOI still has unfulfilled qty (`SOI.quantity > SUM(IFI.quantity)`).
31
41
  3. Reuse the IF if one already exists with the SO number, else POST a new IF.
32
- 4. Per ASN item: POST an IFI (qty = ASN item qty), then read its ASN units.
33
- 5. Per ASN unit: if it has a Unit (serialized) POST an IFIU; if it is tracking-only
34
- (`unitId IS NULL`)collect its tracking number for an IF package instead.
42
+ 4. Per ASN item: POST an IFI (qty = ASN item qty); then copy that ASN item's tracking numbers
43
+ (`AdvanceShippingNoticeItems_TrackingNumbers`) onto the IF item via
44
+ `POST /item-fulfillment-item-tracking-numbers` → `ItemFulfillmentItems_TrackingNumbers`.
45
+ 5. Per ASN unit: if it has a Unit (serialized) → POST an IFIU (carrying unit tracking); if it
46
+ is tracking-only (`unitId IS NULL`) → collect its tracking number for an IF package instead.
35
47
  6. Create one IF package per unique tracking number (header-level tracking merged with
36
48
  tracking-only ASN-unit tracking, deduped by tracking-number uuid).
37
49
 
50
+ ## Tracking number propagation
51
+ The cXML translator maps each `ShipControl` block's tracking number to a line via its
52
+ `LineNumber` Extrinsic (it iterates **all** ShipControls and **all** ShipNoticeItems, not just
53
+ `[0]`), then attaches the number at every level. End state for a cXML ShipNotice:
54
+
55
+ | Table | Populated by |
56
+ |---|---|
57
+ | `AdvanceShippingNotices_TrackingNumbers` | cXML payload (ASN header) |
58
+ | `AdvanceShippingNoticeItems_TrackingNumbers` | cXML payload (matched line's item) |
59
+ | `AdvanceShippingNoticeItemUnits_TrackingNumbers` | cXML payload (tracking-only unit) |
60
+ | `ItemFulfillments_TrackingNumbers` | `postPost` IF package |
61
+ | `ItemFulfillmentItems_TrackingNumbers` | `postPost` (copies ASN-item tracking) |
62
+ | `ItemFulfillmentItemUnits_TrackingNumbers` | `postPost` — **only when serialized units exist** |
63
+
64
+ Fix-forward only (decided 2026-06-11): applies to new ShipNotices; existing records are not
65
+ backfilled.
66
+
38
67
  ## Data model
39
68
  - `AdvanceShippingNoticeUnits.unitId` is nullable (tracking-only units have NULL).
40
69
  - `ItemFulfillmentItemUnits.unitId` is **NOT NULL** — a tracking-only unit can never be an
41
- IFIU; its tracking belongs on an `ItemFulfillmentPackage`.
42
- - Compass tracking lives in `ItemFulfillmentPackages` (header-level); IFIUs are only for
43
- genuinely serialized units.
70
+ IFIU; its tracking belongs on an `ItemFulfillmentPackage` / `ItemFulfillments_TrackingNumbers`.
71
+ So for a tracking-only ShipNotice (no serialized units, the Office Depot norm) the IFIU
72
+ bridge legitimately stays empty — there are no IF item units to attach to.
73
+ - Tracking bridge Records (Core): ASN = 216 (unit) / 217 (item) / 218 (header);
74
+ IF = 317 (header) / 318 (item) / 319 (unit). All six are registered inherent children of
75
+ their parents, so the nested payload keys are accepted with no metadata migration.
44
76
 
45
77
  ## Client variations
46
78
  Compass USA handler (`Model/Compass/`), extending `_Model_Client_AdvanceShippingNotice`.
47
- Compass Canada (`Model/Compass/Canada/`) is a separate sub-client.
79
+ Compass Canada (`Model/Compass/Canada/`) is a separate sub-client. The Compass cXML API
80
+ (Core `ClientApiIdentities` identity `153531108`, clientId 2) holds roles 1 and 3.
48
81
 
49
82
  ## Gotchas / known issues
83
+ - **Record 217 ACL gap (fixed 2026-06-11):** `AdvanceShippingNoticeItems_TrackingNumbers`
84
+ (Record 217) had **zero** `AclRecordPermissions` and its `advanceShippingNoticeItemId`
85
+ (RecordField 1440) / `trackingNumberId` (1441) fields had **zero** `AclFieldPermissions`,
86
+ so ASN-item-level tracking POSTs were rejected (`EZ-1`). `dbchanges2/Client_Compass/2026-06-11
87
+ - AsnItemTrackingNumberAcl.sql` grants role 3 CRUD + role 7 read at the record level and
88
+ roles 1/3/4 writable at the field level, mirroring sibling Record 216. **This SQL must be
89
+ executed against the Compass DB before the cXML change works end-to-end.**
50
90
  - **Tracking-only ASN units (the majority — ~56k of ~101k in prod):** before 2026-06-08 the
51
91
  IFIU query used `INNER JOIN Units`, silently dropping NULL-`unitId` rows, so cXML shipments
52
92
  produced an IF + IFI with **no IFIU and no package — tracking was lost**. Fixed: `LEFT
53
93
  OUTER JOIN Units` + route tracking-only units to IF packages. Historical records (e.g.
54
- SO SA132502 / IF 66997) are **not backfilled** — fix-forward only; a one-time repair is
55
- still an open decision.
94
+ SO SA132502 / IF 66997) are **not backfilled** — fix-forward only.
56
95
  - Interceptor is **DB-driven**: `postPost` does nothing without the Compass
57
96
  `ApiPayloadInterceptors` rows in that env.
58
97
  - The IF is named after the SO number; an already-existing IF with that number is reused.
98
+ - **cXML SQL-injection hardening (2026-06-11):** the `ShipNoticeRequest` path interpolated the
99
+ cXML `Sender` Identity / SharedSecret and the OrderReference `orderID` (purchase order
100
+ number) straight into queries. These are now passed through `_Database::escape()`.
59
101
  - Separate latent bug in the 1.0 worker: the Strategic Systems cron's no-serials branch
60
102
  builds `$itemLevelTrackingNumbers` but never attaches it to the ASN payload.
61
103
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.52",
3
+ "version": "1.0.53",
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",