toga-ai 1.0.149 → 1.0.151
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.
|
@@ -7,4 +7,4 @@
|
|
|
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
|
| [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php |
|
|
9
9
|
| [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 |
|
|
10
|
-
| [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, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql |
|
|
10
|
+
| [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, dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql, dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql, dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql |
|
|
@@ -6,7 +6,7 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-19
|
|
10
10
|
owners: ["jcardinal", "mhammontree", "dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- api2/Component/Api/V2/V2.php
|
|
@@ -22,6 +22,8 @@ files:
|
|
|
22
22
|
- dbchanges2/Core/2026-06-10 - TrackingNumberBridges.sql
|
|
23
23
|
- dbchanges2/Client_Prudential/2026-06-15 - ItemFulfillmentTrackingNumberAclLogicGroups.sql
|
|
24
24
|
- dbchanges2/Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql
|
|
25
|
+
- dbchanges2/Client_Nychh/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql
|
|
26
|
+
- dbchanges2/Client_Quad/2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql
|
|
25
27
|
related:
|
|
26
28
|
- recursive-item-fulfillments.md
|
|
27
29
|
---
|
|
@@ -98,6 +100,15 @@ returnTrackingNumber: {...} }]`, and an IFIU's as `itemFulfillmentItemUnitTracki
|
|
|
98
100
|
posts unit tracking + header tracking via the bridge routes; `Model/Compass/SalesOrder.php::_trackingNumbers`
|
|
99
101
|
reads through the bridges. The `item-fulfillments-for-sales-order-items` TableView was rebuilt
|
|
100
102
|
(see the compass-usa client doc).
|
|
103
|
+
- **NYCHH (Client_Nychh) & Quad (Client_Quad):** the same two item-fulfillment TableViews
|
|
104
|
+
(`item-fulfillments-for-sales-orders` id 12, `item-fulfillments-for-sales-order-items` id 13)
|
|
105
|
+
were rebuilt to mirror Compass's final state — re-rooted at `ItemFulfillmentItems` (29) with
|
|
106
|
+
OUTER unit/tracking chain and tracking via the item-level bridge **318** (`dbchanges2/Client_Nychh/2026-06-19a`,
|
|
107
|
+
`dbchanges2/Client_Quad/2026-06-19a`). NYCHH had no custom columns; Quad preserves three
|
|
108
|
+
Units-level customs (`c_hostIdentifier`/CustomRecordFields 1, `c_pkIdentifier`/2, `macAddress`/rf 1432),
|
|
109
|
+
re-attached to the Units join. **Caveat — record 318 is empty in both client DBs today** (see the
|
|
110
|
+
portability gotcha below), so the re-root immediately fixes the "0 records" bug but the
|
|
111
|
+
Outbound Tracking # / Carrier columns stay blank until item-level tracking is backfilled.
|
|
101
112
|
- **Prudential (Client_Prudential):** Dell posts the legacy `advanceShippingNoticeUnits` key with flat
|
|
102
113
|
tracking — a PRE/POST interceptor rewrites it (see the prudential client doc).
|
|
103
114
|
- **NetSuite-integrated clients:** `Trait/Netsuite/ItemFulfillment.php` now reads
|
|
@@ -143,7 +154,10 @@ returnTrackingNumber: {...} }]`, and an IFIU's as `itemFulfillmentItemUnitTracki
|
|
|
143
154
|
**Quad** (`Client_Quad/2026-06-18a - ...`, all of 317-322) so far. Each new client is whack-a-mole until
|
|
144
155
|
the chain is folded back into the base migration + blank-client template.
|
|
145
156
|
|
|
157
|
+
- **Porting the item-fulfillment TableView fix to another client is two independent decisions, not a copy-paste.** The **re-root** (Units 31 → ItemFulfillmentItems 29 + OUTER unit chain) is universally portable and fixes the "0 records" bug everywhere. The **tracking source is client-specific**: Compass writes one tracking number per item fulfillment at the **item level (318)**, so its views read 318. Other clients populate different bridge levels — verify with `SELECT COUNT(*)` per bridge before choosing. As of 2026-06-19, **record 318 is empty in `Client_Nychh` and `Client_Quad`**; their tracking lives at the **IF/shipment level (317)** (covers IFIs: NYCHH 7955/10049 ≈ 79%, Quad 4878/4967 ≈ 98%) and the **unit level (319)** (≈ 7–9% — serialized only). Copying Compass's 318 join into a client that doesn't write 318 yields a structurally-correct view with permanently blank tracking until 318 is backfilled. (NYCHH's pre-fix view pointed at the now-deleted record 41 = old IF-level/package bridge, i.e. it originally intended IF-level 317.)
|
|
158
|
+
|
|
146
159
|
## Change history
|
|
160
|
+
- 2026-06-19 — Applied the Compass item-fulfillment TableView fix to **NYCHH** and **Quad** (`Client_Nychh`/`Client_Quad` `2026-06-19a - FixItemFulfillmentTableViewTrackingAndRoot.sql`): re-rooted views 12 & 13 at ItemFulfillmentItems, item-level tracking via 318, preserving Quad's three Units-level custom columns. Confirmed read-only that record 318 is empty for both clients (tracking blank until backfilled) — their data is at 317 (IF-level, ~79%/~98% coverage) and 319 (unit-level); recorded the portability gotcha. (jcardinal)
|
|
147
161
|
- 2026-06-18 — Quad's NetSuite TOGa Supply sync (`sync_togasupply_quad.php`) hit the bridge ACL gap in prod: 403 EZ-1 on `item-fulfillment-tracking-numbers` (Sentry 7559037554, 46×, since the 2026-06-15 deploy). Confirmed `Client_Quad` perms 380-385 (recs 317-322, role 1) had 0 logic groups; added the chain via `Client_Quad/2026-06-18a - ItemFulfillmentItemReceiptTrackingNumberAclLogicGroups.sql` (all 6 bridges, idempotent, RANDOM_BYTES uuids). Noted the gap surfaces via the per-client sync, not only UI reads. (dfranks)
|
|
148
162
|
- 2026-06-16 — Caught a migration code-update miss: four queries in `Model/Client/ItemFulfillment.php` (`upsShipmentApi`, `fedexShipmentApi`, `fulfill`, `createNetsuiteItemFulfillment`) still joined the dropped `ItemFulfillmentPackages` table to resolve tracking — repointed to `ItemFulfillments_TrackingNumbers`. See the carrier-shipping-labels doc. (mhammontree)
|
|
149
163
|
- 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)
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Framework | Summary | Files |
|
|
4
4
|
|-----|-----------|---------|-------|
|
|
5
|
-
| [NYCDOE ServiceNow / ASN Integration](features/servicenow-integration.md) | 1.0 | The NYCDOE/ServiceNow integration mirrors DOE's ServiceNow tickets (Incidents + RITMs) into local tables, turns vendor shipment notices into NetSuite Sales Orde | worker/crons/sync/nycdoe/import_asn.php, worker/crons/sync/nycdoe/import_inc.php, worker/crons/sync/nycdoe/legacy_import_asn.php, worker/crons/sync/nycdoe/legacy_process_asn_queue.php, worker/crons/sync/nycdoe/process_tickets.php, worker/crons/sync/nycdoe/1_send_asn_to_netsuite.php, worker/crons/sync/nycdoe/2_send_serials_to_netsuite.php, worker/crons/sync/nycdoe/3_create_installation_ticket.php, worker/crons/sync/nycdoe/send_ticket_updates.php, worker/crons/sync/nycdoe/send_request_item_updates.php, worker/crons/sync/nycdoe/send_nycdoe_proof_of_delivery.php, worker/crons/sync/nycdoe/sync_nycdoe_locations.php, worker/crons/sync/nycdoe/receive_edi_purchase_orders.php, worker/crons/sync/nycdoe/send_edi_open_invoices.php, worker/crons/notifications/nycdoe/, worker/schedules/cron.worker.sync.json, worker/schedules/cron.worker.notification.json, library/app/api/nycdoe.php, library/app/api/nycdoev2.php, library/app/asnprocessor/manufacturer.php, library/app/edi.php |
|
|
5
|
+
| [NYCDOE ServiceNow / ASN Integration](features/servicenow-integration.md) | 1.0 | The NYCDOE/ServiceNow integration mirrors DOE's ServiceNow tickets (Incidents + RITMs) into local tables, turns vendor shipment notices into NetSuite Sales Orde | worker/crons/sync/nycdoe/import_asn.php, worker/crons/sync/nycdoe/import_inc.php, worker/crons/sync/nycdoe/legacy_import_asn.php, worker/crons/sync/nycdoe/legacy_process_asn_queue.php, worker/crons/sync/nycdoe/process_tickets.php, worker/crons/sync/nycdoe/1_send_asn_to_netsuite.php, worker/crons/sync/nycdoe/2_send_serials_to_netsuite.php, worker/crons/sync/nycdoe/3_create_installation_ticket.php, worker/crons/sync/nycdoe/send_ticket_updates.php, worker/crons/sync/nycdoe/send_request_item_updates.php, worker/crons/sync/nycdoe/send_nycdoe_proof_of_delivery.php, worker/crons/sync/nycdoe/sync_nycdoe_locations.php, worker/crons/sync/nycdoe/receive_edi_purchase_orders.php, worker/crons/sync/nycdoe/send_edi_open_invoices.php, worker/crons/notifications/nycdoe/, worker/schedules/cron.worker.sync.json, worker/schedules/cron.worker.notification.json, library/app/api/nycdoe.php, library/app/api/nycdoev2.php, library/app/asnprocessor/manufacturer.php, library/app/asnprocessor/apple.php, library/app/asnprocessor/lenovo.php, library/app/asnprocessor/lexmark.php, library/app/asnprocessor/acer.php, library/app/edi.php |
|
|
6
6
|
| [New York City Department of Education](profile.md) | 1.0 | NYC DOE (New York City Department of Education) is a TOGA client whose entire integration runs in the **1.0 worker tier** (~30 cron scripts under `worker/crons/ | |
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: nycdoe
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-19
|
|
10
10
|
owners: [mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/sync/nycdoe/import_asn.php
|
|
@@ -29,6 +29,10 @@ files:
|
|
|
29
29
|
- library/app/api/nycdoe.php
|
|
30
30
|
- library/app/api/nycdoev2.php
|
|
31
31
|
- library/app/asnprocessor/manufacturer.php
|
|
32
|
+
- library/app/asnprocessor/apple.php
|
|
33
|
+
- library/app/asnprocessor/lenovo.php
|
|
34
|
+
- library/app/asnprocessor/lexmark.php
|
|
35
|
+
- library/app/asnprocessor/acer.php
|
|
32
36
|
- library/app/edi.php
|
|
33
37
|
related:
|
|
34
38
|
- ../profile.md
|
|
@@ -147,10 +151,49 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
|
|
|
147
151
|
4. **The NetSuite freeze:** `netSuiteInternalSalesOrderId IS NOT NULL` ⇒ `qtyOrder` locked.
|
|
148
152
|
Growth = new item row; decrease = discrepancy alert, never a reduction.
|
|
149
153
|
5. **One unit per non-serialized item row** — a 50-cable line is 1 unit and 1 repair order.
|
|
150
|
-
6. **The
|
|
154
|
+
6. **The replacement PO suffix forces a brand-new ASN/MSO/RO chain** for replacements —
|
|
155
|
+
but the suffix is a **per-manufacturer convention with NO standard format**, and even a
|
|
156
|
+
single manufacturer is inconsistent. Observed in prod: `-RPL`, `-REPL`, `-RPLE`, `-N`,
|
|
157
|
+
`_N` (underscore), and a space variant (` RPL`). ServiceNow itself documents NO `-RPL`
|
|
158
|
+
convention — its native replacement model is field-based on the *original* RITM
|
|
159
|
+
(`device_replaced`/`replaced_serial_number`/`replaced_asset_tag`); the suffix comes from
|
|
160
|
+
the OEM/SFTP feeds. Replacement-of-replacement appears as `-RPL-RPL` and is structurally
|
|
161
|
+
fine **only if hyphenated consistently**. See the duplicate-fulfillment gotcha below.
|
|
151
162
|
|
|
152
163
|
## Gotchas / known issues
|
|
153
164
|
|
|
165
|
+
- **⚠ OPEN BUG (investigated 2026-06-19, not yet fixed) — manufacturer PO-suffix format
|
|
166
|
+
variance causes DUPLICATE fulfillment.** Stage 2's serialized dedupe is scoped to the
|
|
167
|
+
*exact* PO string (`lookupAsnUnitIdByAsnPartNumberAndSerialNumber[vendorId][purchaseOrder]
|
|
168
|
+
[part][serial]`, `legacy_process_asn_queue.php:272`), and the PO flows **verbatim** end to
|
|
169
|
+
end — never trimmed/normalized except Lenovo's `trim()` in `asnprocessor/lenovo.php`. So when
|
|
170
|
+
a manufacturer re-sends the *same physical unit* under a different suffix spelling
|
|
171
|
+
(e.g. Lenovo `WR260150152-RPL` then `WR260150152-RPLE`, same serial `V60095WK`), the two PO
|
|
172
|
+
strings differ → the serial guard misses → a full duplicate ASN→SO→MSO→RO chain is created
|
|
173
|
+
for one device (two NetSuite SOs, two repair orders, two onsite tickets).
|
|
174
|
+
- **Confirmed live case:** PO `WR260150152`, serial `V60095WK`, asset `DOE-LN1265478` →
|
|
175
|
+
repair orders **772308** (MSO 309909, via `-RPL`) and **801064** (MSO 318945, via `-RPLE`),
|
|
176
|
+
both `ORDER_ASSIGNED_AWAITING_SCHEDULING` (uncaught as of investigation), same TOGa Desk
|
|
177
|
+
`serialNumId 592519`.
|
|
178
|
+
- **Systemic, not a one-off:** ~14 same-base/same-serial duplicate pairs across **Lenovo AND
|
|
179
|
+
Lexmark**, spanning suffix forms `-RPL`/`-REPL`/`-RPLE`/`-N`/`_N`. A genuine replacement
|
|
180
|
+
carries a *new* serial, so identical-serial-across-base-and-suffix = true duplication.
|
|
181
|
+
- **The discriminator that would have caught it:** serial + assetTag + trackingNumber were
|
|
182
|
+
identical across copies. A serial-anchored dedupe (`vendorId + partNumber + serialNumber`,
|
|
183
|
+
ignoring PO suffix) collapses these; PO-scoped dedupe cannot.
|
|
184
|
+
- **Do NOT blindly canonicalize all suffixes to one token** — `-RPL-RPL` (legit
|
|
185
|
+
replacement-of-replacement) must stay distinct from `-RPL`. Confirm suffix semantics with
|
|
186
|
+
the SME before any fix.
|
|
187
|
+
- **Two ADJACENT anomalies surfaced by the same blast-radius query** (separate mechanisms,
|
|
188
|
+
not yet investigated): (a) a large Lexmark cluster pairing a **numeric order number with a
|
|
189
|
+
WR PO** for identical serials (e.g. `3100157`↔`WR240178018`) — same devices under two PO
|
|
190
|
+
identifiers; (b) **invalid/placeholder serials** becoming serialized units — serial `"NA"`
|
|
191
|
+
spans 25 POs→15 ROs (Lenovo `NA`→`''` normalization at `asnprocessor/lenovo.php:102` not
|
|
192
|
+
catching all), plus `"1"`, `"11"–"29"`.
|
|
193
|
+
- **Open thread for resume:** check RO active/cancelled status across the ~14 suffix-variance
|
|
194
|
+
pairs to size live double-truck-roll exposure; then trace the numeric-PO↔WR cluster.
|
|
195
|
+
Investigation was **read-only** — no code or data changes made.
|
|
196
|
+
|
|
154
197
|
- **The queue `dedupeKey` format is load-bearing.** A 2026 incident (WR260236464 duplicate
|
|
155
198
|
repair order) was caused by dedupeKey format drift (4-part keys with serial vs 3-part
|
|
156
199
|
without) letting old rows re-import; Stage 2 then created a sibling item row (frozen
|
|
@@ -187,6 +230,7 @@ Vendor SFTP ───(legacy_import_asn.php, ser+non-ser)─┘ [UNIQUE ded
|
|
|
187
230
|
of the consumer query; `php -l` every touched file.
|
|
188
231
|
|
|
189
232
|
## Change history
|
|
233
|
+
- 2026-06-19 — Investigated (read-only) the replacement-PO-suffix duplicate-fulfillment bug: PO-scoped serial dedupe defeated by per-manufacturer suffix-format variance (`-RPL`/`-REPL`/`-RPLE`/`-N`/`_N`); confirmed live case WR260150152/V60095WK (ROs 772308+801064) and ~14 systemic pairs across Lenovo+Lexmark. Refined business rule #6; added OPEN-BUG gotcha + adjacent anomalies (numeric-PO↔WR cluster, "NA"/integer serials). No code changes. (mhammontree)
|
|
190
234
|
- 2026-06-10 — Documented the NYC DOE ServiceNow/ASN integration (ticket mirror, dual ASN ingestion, NetSuite freeze pipeline, EDI surface, dedupeKey gotchas). (mhammontree)
|
|
191
235
|
|
|
192
236
|
## Related docs
|
package/package.json
CHANGED