toga-ai 1.0.73 → 1.0.74

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,7 +2,7 @@
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, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client_Compass/2026-06-11 - AsnItemTrackingNumberAcl.sql |
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, dbchanges2/Client_Compass/2026-06-15b - BackfillSA132781ItemFulfillmentTracking.sql |
6
6
  | [Compass: Item-Fulfillment TableViews (for-sales-order-items & for-sales-orders, tracking via bridge)](features/item-fulfillment-tracking-tableview.md) | 2.0 | Two sibling Compass TableViews in `Client_Compass` display fulfilled items in toga2-supply, both driven by `TableViews` / `TableViewJoins` / `TableViewFields` c | dbchanges2/Client_Compass/2026-06-10 - ItemFulfillmentsForSalesOrderItemsTableView.sql, dbchanges2/Client_Compass/2026-06-11 - ItemFulfillmentsForSalesOrdersTableView.sql, dbchanges2/Client_Compass/2026-06-15a - FixItemFulfillmentTrackingNumberJoins.sql |
7
7
  | [Compass MITS PO → SO Item Linking](features/mits-po-to-so-item-linking.md) | 2.0 | MITS sends Compass inbound Purchase Orders (`POST /v2/purchase-orders`) against a Sales Order (`mitsSalesOrder`). | _underscore/Model/Compass/PurchaseOrder.php |
8
8
  | [Compass USA](profile.md) | 2.0 | Compass USA is a TOGA client running a multi-tier supply-chain commerce operation. | |
@@ -5,12 +5,13 @@ project: _Underscore
5
5
  client: compass-usa
6
6
  type: client-feature
7
7
  status: active
8
- updated: 2026-06-11
8
+ updated: 2026-06-15
9
9
  owners: [jcardinal]
10
10
  files:
11
11
  - _underscore/Model/Compass/AdvanceShippingNotice.php
12
12
  - api2/Component/Api/Cxml/Cxml.php
13
13
  - dbchanges2/Client_Compass/2026-06-11 - AsnItemTrackingNumberAcl.sql
14
+ - dbchanges2/Client_Compass/2026-06-15b - BackfillSA132781ItemFulfillmentTracking.sql
14
15
  related:
15
16
  - ../../../2.0/apps/_underscore/features/recursive-item-fulfillments.md
16
17
  ---
@@ -36,16 +37,28 @@ unit level on both sides — not just on one unit.
36
37
  inherit `postPost` from `_Model_Compass_AdvanceShippingNotice` — edit the parent.
37
38
 
38
39
  ## How it works
40
+ As of **2026-06-15** the handler **reconciles tracking onto whatever fulfillment exists** —
41
+ it no longer only attaches tracking when it creates new fulfillment (see *Change history* and
42
+ the gotcha about already-fulfilled orders). Flow:
43
+
39
44
  1. Read ASN → resolve PO → SO via `SalesOrders_PurchaseOrders`; skip if no SO.
40
- 2. Select ASN items whose SOI still has unfulfilled qty (`SOI.quantity > SUM(IFI.quantity)`).
41
- 3. Reuse the IF if one already exists with the SO number, else POST a new IF.
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.
47
- 6. Create one IF package per unique tracking number (header-level tracking merged with
48
- tracking-only ASN-unit tracking, deduped by tracking-number uuid).
45
+ 2. Select **all** ASN items mapped to a SalesOrderItem (NO "unfulfilled only" filter), carrying
46
+ each SOI's `quantity` and already-`SUM`med fulfilled qty.
47
+ 3. Per ASN item, compute `remaining = SOI.quantity fulfilled` (rounded to kill float dust):
48
+ - **`remaining > 0`** (create path, unchanged): reuse the SO-numbered IF or POST a new one,
49
+ POST an IFI; that new IFI is the tracking target. Additional SO-numbered IFs are acceptable.
50
+ - **`remaining <= 0`** (reconcile path, new): the tracking targets are the **existing IFIs**
51
+ for that SOI (resolved via `ItemFulfillmentItems.salesOrderItemId`their `itemFulfillmentId`),
52
+ **not** an SO-number lookup NetSuite/toga2-supply IFs are `F`-numbered, not SO-numbered.
53
+ 4. Copy the ASN item's tracking (`AdvanceShippingNoticeItems_TrackingNumbers`) onto each target
54
+ IFI via `POST /item-fulfillment-item-tracking-numbers` → `ItemFulfillmentItems_TrackingNumbers`
55
+ — **idempotent** (skipped if the bridge row already exists; ASNs get re-POSTed).
56
+ 5. Per ASN unit: serialized (has a Unit) → attach unit tracking to the matching IFIU
57
+ (`POST /item-fulfillment-item-unit-tracking-numbers`; create the IFIU only on the create path);
58
+ tracking-only (`unitId IS NULL`) → collect its tracking for an IF package.
59
+ 6. Create one IF package per unique tracking number (header-level merged with tracking-only
60
+ ASN-unit tracking, deduped by tracking-number **id**) on **each involved IF** — i.e. only the
61
+ IF(s) that received this ASN's tracking — all idempotent.
49
62
 
50
63
  ## Tracking number propagation
51
64
  The cXML translator maps each `ShipControl` block's tracking number to a line via its
@@ -62,7 +75,10 @@ The cXML translator maps each `ShipControl` block's tracking number to a line vi
62
75
  | `ItemFulfillmentItemUnits_TrackingNumbers` | `postPost` — **only when serialized units exist** |
63
76
 
64
77
  Fix-forward only (decided 2026-06-11): applies to new ShipNotices; existing records are not
65
- backfilled.
78
+ backfilled (one-off backfills repair specific orders — e.g. `2026-06-15b` for SA132781).
79
+
80
+ As of 2026-06-15 propagation also fires when the IF **already exists** (reconcile path), so the
81
+ table above is populated even for SOs fulfilled out-of-band before the ASN arrived.
66
82
 
67
83
  ## Data model
68
84
  - `AdvanceShippingNoticeUnits.unitId` is nullable (tracking-only units have NULL).
@@ -80,6 +96,24 @@ Compass Canada (`Model/Compass/Canada/`) is a separate sub-client. The Compass c
80
96
  (Core `ClientApiIdentities` identity `153531108`, clientId 2) holds roles 1 and 3.
81
97
 
82
98
  ## Gotchas / known issues
99
+ - **ASN arriving AFTER the SO is already fulfilled (fixed 2026-06-15):** when an SO is fulfilled
100
+ out-of-band (NetSuite/toga2-supply creates `F`-numbered IFs) **before** its ASN arrives,
101
+ `postPost` previously found zero unfulfilled ASN items and skipped the **entire** propagation
102
+ block (the old `if ($asnItemsQuery->getRowCount())` gate wrapped even the header-package
103
+ creation) — so the ASN's tracking was lost at every level. Symptom: SO **SA132781** (id 105943)
104
+ had tracking on the ASN (header/item/unit) but none on its IFs (68063/68154). Fix: reconcile
105
+ onto existing fulfillment (see *How it works*). Forward-only; SA132781 repaired by the backfill
106
+ migration `2026-06-15b`.
107
+ - **A NULL `AdvanceShippingNoticeItemUnits.unitId` is CORRECT — do NOT add a NOT NULL
108
+ constraint.** Office Depot (vendorId 1) ships via cXML and **never sends serial numbers**;
109
+ the cXML→JSON translator deliberately creates a tracking-only ASN unit (no `unit`, NULL
110
+ `unitId`) purely to carry the unit-level tracking number. Verified against the raw cXML
111
+ (`Logs_Compass.Api`) for SA132781. A NOT NULL constraint would reject the majority of Compass
112
+ ASN volume (~56k of ~101k units are tracking-only) and break tracking ingestion. A serialized
113
+ `Unit` seen on the IF side (e.g. created by NetSuite) is unrelated to the tracking-only ASN unit.
114
+ - **Idempotency:** every tracking attach is guarded by a SELECT-before-POST on the bridge table,
115
+ so re-POSTing the same ASN does not duplicate `ItemFulfillment(s|Items|ItemUnits)_TrackingNumbers`
116
+ rows. New attach helpers depend on this — keep the guard if extending.
83
117
  - **Record 217 ACL gap (fixed 2026-06-11):** `AdvanceShippingNoticeItems_TrackingNumbers`
84
118
  (Record 217) had **zero** `AclRecordPermissions` and its `advanceShippingNoticeItemId`
85
119
  (RecordField 1440) / `trackingNumberId` (1441) fields had **zero** `AclFieldPermissions`,
@@ -101,5 +135,16 @@ Compass Canada (`Model/Compass/Canada/`) is a separate sub-client. The Compass c
101
135
  - Separate latent bug in the 1.0 worker: the Strategic Systems cron's no-serials branch
102
136
  builds `$itemLevelTrackingNumbers` but never attaches it to the ASN payload.
103
137
 
138
+ ## Change history
139
+ Dated one-liners, newest first.
140
+ - 2026-06-15 — `postPost` rewritten to reconcile ASN tracking onto **existing** fulfillment
141
+ (not just newly-created IFs), idempotently; fixes lost tracking when the SO was fulfilled
142
+ out-of-band before the ASN (e.g. SA132781). Confirmed NULL `unitId` is by-design for Office
143
+ Depot tracking-only units (no NOT NULL constraint). Backfill `2026-06-15b` repairs SA132781. (jcardinal)
144
+ - 2026-06-11 — Record 217 ACL grant so ASN-item-level tracking POSTs succeed; cXML SQL-injection
145
+ hardening; tracking propagated across all ASN/IF granularities. (jcardinal)
146
+ - 2026-06-08 — IFIU query switched to `LEFT OUTER JOIN Units`; tracking-only units routed to IF
147
+ packages (previously dropped by an INNER JOIN). (jcardinal)
148
+
104
149
  ## Related docs
105
150
  - Recursive Item Fulfillments (upstream mirroring) — the engine these IFs/packages feed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.73",
3
+ "version": "1.0.74",
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",