toga-ai 1.0.153 → 1.0.155

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.
@@ -5,7 +5,7 @@
5
5
  | [Worker (1.0 Framework) Architecture](architecture.md) | `worker` is the legacy (**1.0** `App_` framework) **background-job tier**. | worker/index.php, worker/_/app/framework.php, worker/crons/, worker/schedules/, worker/ebs/cron.worker.php, worker/.ebextensions/035_cron.worker.config |
6
6
  | [Compass MA Sales Order Exception Report](features/compass-ma-sales-order-exception-report.md) | A worker cron that emails operations the "Compass Refresh Exception Report" — Compass `MA%` sales orders whose corresponding Office Depot (ODP) sales order has | worker/crons/toga2/compass/workflow/7_generate_ma_sales_order_exception_report.php |
7
7
  | [Compass Partial In-Transit & Delivered Emails (per package)](features/compass-partial-in-transit-delivered-emails.md) | Compass USA and Compass Canada send a **per-package** in-transit email (and a matching delivered email) instead of one email listing the whole order. | worker/crons/toga2/compass/update_salesorder_status_from_odp.php, worker/crons/toga2/compasscanada/workflow/3_update_salesorder_status_from_grand_and_toy.php |
8
- | [Forecast2 ↔ NetSuite Reconciliation & Trueup Tooling](features/forecast2-netsuite-reconciliation.md) | CLI tools to **audit** and **repair** drift between the production `Forecast` DB (core2) and NetSuite. | test/@dave/reconcile_netsuite_totals.php, test/@dave/analyze_netsuite_forecast_diff.php, test/@dave/trueup_sales.php, test/@dave/trueup_open_orders.php, test/@dave/trueup_opportunities.php, test/@dave/probe_sales_gap_direct.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
8
+ | [Forecast2 ↔ NetSuite Reconciliation & Trueup Tooling](features/forecast2-netsuite-reconciliation.md) | CLI tools to **audit** and **repair** drift between the production `Forecast` DB (core2) and NetSuite. | test/@dave/reconcile_netsuite_totals.php, test/@dave/analyze_netsuite_forecast_diff.php, test/@dave/trueup_sales.php, test/@dave/trueup_open_orders.php, test/@dave/trueup_opportunities.php, test/@dave/probe_sales_gap_direct.php, test/@dave/probe_missing_oo_timing.php, test/@dave/probe_missing_oo_createdby.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
9
9
  | [NetSuite → TOGa Supply Per-Client Sync (thin wrappers)](features/netsuite-togasupply-per-client-sync.md) | Syncs NetSuite transactions (sales orders, purchase orders, invoices, item receipts, item fulfillments, inventory adjustments) into each TOGa Supply (2.0) clien | worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/crons/toga2/netsuite/sync_togasupply_canon.php, worker/schedules/cron.worker.sync.json, dbchanges2/_modules/netsuite/2026-04-01 - Parameters.sql |
10
10
  | [Prudential: Send Shipments for the Day report (daily cron)](features/send-shipments-for-the-day.md) | Daily cron (9:00 PM) that emails Prudential and Dell stakeholders an Excel report of all devices shipped that day, including tracking number, serial number, emp | worker/crons/notifications/reports/send_shipments_for_the_day.php |
11
11
  | [Onboarding a Client to the NetSuite TOGa Supply Sync](workflows/onboarding-client-to-netsuite-togasupply-sync.md) | How to add a new TOGa 2 client to the per-client NetSuite → TOGa Supply importer (`worker/crons/toga2/netsuite/`). | worker/crons/toga2/netsuite/sync_togasupply.php, worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/schedules/cron.worker.sync.json, dbchanges2/_modules/netsuite/2026-04-01 - Parameters.sql |
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-11
9
+ updated: 2026-06-22
10
10
  owners: [dfranks]
11
11
  files:
12
12
  - test/@dave/reconcile_netsuite_totals.php
@@ -15,6 +15,8 @@ files:
15
15
  - test/@dave/trueup_open_orders.php
16
16
  - test/@dave/trueup_opportunities.php
17
17
  - test/@dave/probe_sales_gap_direct.php
18
+ - test/@dave/probe_missing_oo_timing.php
19
+ - test/@dave/probe_missing_oo_createdby.php
18
20
  - worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php
19
21
  related:
20
22
  - ../architecture.md
@@ -116,6 +118,27 @@ None — Forecast2 is a single shared dataset.
116
118
  - **Pre-2024 legacy open SOs** — ordered before the sync window.
117
119
  When a user asks about a date range (e.g. 2026 YTD), **filter the open-orders comparison to that
118
120
  trandate window manually** — within the normal window (2024→present) the delta is rounding-only.
121
+ - **`reconcile`'s Open Orders headline is product-only; shipping is NS-only and NOT a delta.**
122
+ Forecast2 never stores the synthetic shipping line (the `OpenOrderItems` writer drops it — see the
123
+ open-orders sync doc), so its shipping column is structurally `$0`. Summing NS product+shipping in
124
+ the headline therefore manufactures a phantom delta (e.g. a clean book reported a fake `$7,547`
125
+ "gap" that was 100% the NS shipping line). Fixed 2026-06-22: the **Open Orders** row compares
126
+ **product-only on both sides** (reconciles to ~$0; the residual is float rounding across orders),
127
+ and shipping prints as a separate `+ shipping` info row marked `(NS-only)` via `reportInfoRow()`.
128
+ Compare the **product** figure for sync health.
129
+ - **The per-order presence diff is the real sync-health signal — and it splits misses into two
130
+ buckets.** `reconcile` calls `openOrderPerOrderDiff()` after the totals: for the window it lists
131
+ open SOs *missing from Forecast2* (open in NS, zero rows synced), *missing from NetSuite* (FC rows
132
+ whose SO is no longer open — benign, `trueup_open_orders` cleanup deletes them), and product-revenue
133
+ mismatches. A non-zero "missing from Forecast2" count is a **real** gap (distinct from the benign
134
+ shipping/rounding deltas above). Root-causing those misses (2026-06-22) showed **two independent
135
+ causes** needing different fixes (detailed in the open-orders sync doc): (A) orders the AMQ enqueuer
136
+ never fired for — **no `Core.WorkerJobs` row at all** for the SO id; and (B) orders that processed
137
+ `isSuccess=1` but wrote zero rows via the read-lag→destructive-delete. Diagnose by bucket:
138
+ `probe_missing_oo_timing.php` (NS status/timestamps confirm the orders are genuinely open) +
139
+ `probe_missing_oo_createdby.php` (creator/context — integration/Web-Services-origin orders cluster
140
+ here) + a `Core.WorkerJobs` lookup by `JSON_EXTRACT(parameters,'$.internalId')` (no row → bucket A;
141
+ rows all `isSuccess=1` yet zero OOI rows → bucket B).
119
142
  - **Legacy credit-memo wrong-sign rows.** Some `Forecast.Sales` credit-memo/cash-refund rows were
120
143
  stored with **flipped signs** by a prior sync version (positive instead of negative), inflating
121
144
  revenue. `trueup_sales.php` repairs them: it compares against NS by trandate, detects the sign
@@ -126,6 +149,12 @@ None — Forecast2 is a single shared dataset.
126
149
 
127
150
  ## Change history
128
151
 
152
+ - 2026-06-22 — **`reconcile` Open Orders made apples-to-apples.** Headline row now compares
153
+ product-only on both sides (shipping is NS-only, prints as a separate `(NS-only)` info row) — kills
154
+ a phantom shipping-only "delta". Documented the per-order presence diff as the real sync-health
155
+ signal and the **two missing-from-Forecast2 buckets** (enqueuer-never-fired vs read-lag
156
+ destructive-delete) with a bucket-based diagnostic recipe; added `probe_missing_oo_timing.php` +
157
+ `probe_missing_oo_createdby.php`. (dfranks)
129
158
  - 2026-06-11 — Documented the `App_Database` sandbox-dev read pitfall (bypass with direct core2
130
159
  mysqli), `reconcile`'s open-orders live-snapshot semantics (future-dated + pre-2024 legacy SOs
131
160
  explain apparent gaps), the legacy credit-memo wrong-sign repair, and added `trueup_opportunities`
@@ -8,5 +8,5 @@
8
8
  | [Elite Freshservice Sync (worker2)](features/elite-freshservice-sync.md) | `_Worker_Elite` processes Freshservice webhook events and syncs them into TOGA 2. | worker2/Worker/Elite.php, worker2/Config/dev-kmaramreddy-laptop.ini |
9
9
  | [Monitoring Framework (Orchestrator + Child Monitors)](features/monitoring-framework.md) | A unified, DB-driven monitoring framework for business-critical data flows (Compass POs, Prudential asset imports, AIG closed claims, …). | worker2/Worker/Monitor.php, worker2/Worker/Monitors/, worker2/Worker/Notification/Email.php, dbchanges2/Core/2026-05-21 - Monitors.sql |
10
10
  | [NetSuite → TOGA Opportunity Sync (API Message Queue + worker2 webhook)](features/netsuite-opportunity-sync.md) | Outbound sync from NetSuite to TOGA for the record types the Forecast2 importer pulls (opportunities first; sales/items/etc. | worker2/Worker/Netsuite.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Controller/Index.php, _underscore/Worker.php, test/@dave/NetSuite/api-message-queue/lib_amq_queue.js, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/ue_amq_drain.js, test/@dave/NetSuite/api-message-queue/ss_amq_drain.js, test/@dave/NetSuite/api-message-queue/DEPLOY_RUNBOOK.md, test/@dave/clickup/backfill_opportunity_numbers.php, test/@dave/clickup/probe_opportunity_fields.php, test/@dave/probe_clickup_desc_match.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
11
- | [NetSuite → Forecast Open-Orders Sync (salesOrder webhook → OpenOrderItems)](features/netsuite-salesorder-open-orders-sync.md) | Webhook-driven, single-record port of the legacy open-orders importer (TRUE-79142). | worker2/Worker/Netsuite/SalesOrder.php, worker2/Worker/Netsuite.php, test/@dave/probe_salesorder_rest_shape.php, test/@dave/probe_open_order_lines.php, test/@dave/check_so_status.php, test/@dave/check_so_history.php, test/@dave/probe_so_rest_lines.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
11
+ | [NetSuite → Forecast Open-Orders Sync (salesOrder webhook → OpenOrderItems)](features/netsuite-salesorder-open-orders-sync.md) | Webhook-driven, single-record port of the legacy open-orders importer (TRUE-79142). | worker2/Worker/Netsuite/SalesOrder.php, worker2/Worker/Netsuite.php, test/@dave/probe_salesorder_rest_shape.php, test/@dave/probe_open_order_lines.php, test/@dave/check_so_status.php, test/@dave/check_so_history.php, test/@dave/probe_so_rest_lines.php, test/@dave/probe_missing_oo_timing.php, test/@dave/probe_missing_oo_createdby.php, worker/crons/toga2/forecast2/import_open_orders.php, worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php |
12
12
  | [Teams Meeting Transcript Export](features/teams-transcript-export.md) | `_Worker_Team_Transcripts` (action `Team/Transcripts/Export`) polls Microsoft Graph for Teams meeting transcripts produced by a set of organizers, classifies ea | worker2/Worker/Team/Transcripts.php, worker2/Config/production.ini, worker2/Database/TeamsTranscriptExports.sql, dbchanges2/Core/2026-06-18a - Teams Transcript Export schedule.sql |
@@ -6,7 +6,7 @@ project: Worker
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-18
9
+ updated: 2026-06-22
10
10
  owners: ["dfranks"]
11
11
  files:
12
12
  - worker2/Worker/Netsuite/SalesOrder.php
@@ -16,6 +16,8 @@ files:
16
16
  - test/@dave/check_so_status.php
17
17
  - test/@dave/check_so_history.php
18
18
  - test/@dave/probe_so_rest_lines.php
19
+ - test/@dave/probe_missing_oo_timing.php
20
+ - test/@dave/probe_missing_oo_createdby.php
19
21
  - worker/crons/toga2/forecast2/import_open_orders.php
20
22
  - worker/crons/toga2/forecast2/common_import_sales_from_netsuite.php
21
23
  related:
@@ -120,6 +122,26 @@ None — uniform (platform-wide Forecast2 sync).
120
122
  `project_amq_realtime_requirement` in dev memory). Diagnose with `probe_open_order_lines.php` (SuiteQL vs
121
123
  Forecast) + `probe_so_rest_lines.php` (live REST sublist count) + the `WorkerJobs` `dtStarted`/`instanceId`
122
124
  timing (to confirm serial-vs-concurrent before blaming ordering).
125
+ - **A missing open order has TWO distinct causes — diagnose which before "fixing" it.** When
126
+ `reconcile_netsuite_totals.php`'s per-order diff reports an open SO with **zero `OpenOrderItems`
127
+ rows**, it's one of two unrelated failures, told apart by `Core.WorkerJobs` (lookup by
128
+ `JSON_EXTRACT(parameters,'$.internalId')`):
129
+ - **Bucket A — the webhook never fired (no `WorkerJobs` row at all).** Nothing enqueued → the
130
+ handler never ran. The enqueuer (NetSuite-side AMQ User Event) doesn't fire for every context —
131
+ **integration/Web-Services-origin orders and backend/scheduled mass-edits cluster here** (a UE
132
+ only fires in the execution contexts its deployment enables). Confirm the order is genuinely open
133
+ + see its creator/context with `probe_missing_oo_timing.php` + `probe_missing_oo_createdby.php`.
134
+ This is **NetSuite-side** (enqueuer deployment/context), *not* a worker2 handler bug — the
135
+ read-lag fixes below do nothing for it. Backstop: the scheduled drainer / `trueup_open_orders`.
136
+ - **Bucket B — the handler ran and returned `isSuccess=1` but wrote zero rows** (the read-lag →
137
+ destructive-delete below). All items present in `Forecast.Items` (no throw), order genuinely open.
138
+ **Diagnostic for whether the self-heal path is even active:** `deferReimport` re-enqueues with an
139
+ incremented `attempt` parameter, and `logImportBreadcrumb` writes a per-order row to **`Logs.Api`
140
+ `source = 'OPEN_ORDER_IMPORT'`** whose `responseCode` encodes the outcome (**200** synced / **202**
141
+ deferred-reenqueued / **204** removeAll / **409** wrote-but-short = silent zero-rows / **500** gave
142
+ up after `MAX_IMPORT_ATTEMPTS`). If every `WorkerJobs` row for the id has a **NULL `attempt`** and
143
+ there are **no `OPEN_ORDER_IMPORT` breadcrumbs** for it, the self-heal/defer path isn't executing
144
+ for that order — so a bucket-B miss has no retry and freezes at zero rows.
123
145
  - **REST shape ≠ SOAP shape.** The cron reads the SOAP-shim shape; this handler reads the REST record
124
146
  (`status->refName`, line `quantityBilled`, `class->refName`, `entity->id`, `salesRep->id`,
125
147
  `shippingCost`). Verified against live orders via `test/@dave/probe_salesorder_rest_shape.php`.
@@ -174,6 +196,15 @@ and are a candidate to extract into a shared `_Component_Forecast_Db` before the
174
196
 
175
197
  ## Change history
176
198
 
199
+ - 2026-06-22 — **Separated "missing open order" into two distinct buckets** with a discriminating
200
+ diagnostic (`Core.WorkerJobs` lookup by `parameters.internalId`): **(A) webhook never enqueued** (no
201
+ `WorkerJobs` row — NetSuite-side enqueuer not firing for integration/Web-Services/backend-mass-edit
202
+ contexts; not a handler bug) vs **(B) processed `isSuccess=1`, zero rows** (the read-lag destructive
203
+ delete). Recorded the self-heal signal-reading (NULL `attempt` on every job + no `OPEN_ORDER_IMPORT`
204
+ Logs.Api breadcrumb ⇒ self-heal/defer path not active for that order; breadcrumb `responseCode`
205
+ legend 200/202/204/409/500). Reconfirmed every sampled miss carries a NetSuite **item group**, and
206
+ all group/member items resolved in `Forecast.Items` (so isSuccess=1 = no unknown-item throw). Added
207
+ `probe_missing_oo_timing.php` + `probe_missing_oo_createdby.php`. (dfranks)
177
208
  - 2026-06-18 — **Root-caused the open-order delta to REST `item.items` read-after-write lag** (not the
178
209
  drainer/enqueue fixes, not ordering). SO 7170669 committed 4 of 15 open lines despite 14 *strictly
179
210
  sequential, single-instance, in-order* PUTs — the last authoritative read returned a lagged partial
@@ -5,5 +5,6 @@
5
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, _underscore/Model/Compass/PurchaseOrder.php, api2/Component/Api/Cxml/Cxml.php, dbchanges2/Client_Compass/2026-06-11 - AsnItemTrackingNumberAcl.sql, dbchanges2/Client_Compass/2026-06-15b - BackfillSA132781ItemFulfillmentTracking.sql, dbchanges2/Client_Compass/2026-06-16 - CleanupSA132763CrossLineTracking.sql, dbchanges2/Client_Compass/2026-06-16b - CleanupSA132743CrossLineTracking.sql, dbchanges2/Client_Compass/2026-06-16c - BackfillSA132763C40QYUCTracking.sql, dbchanges2/Client_Compass/2026-06-18a - CleanupSA132898DuplicateTracking.sql, dbchanges2/Client_Compass/2026-06-18b - CleanupSA132881DuplicateTracking.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, worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php |
8
+ | [Compass MITS PO Transmission to Vendors](features/mits-po-transmission-to-vendors.md) | 2.0 | The 1.0 worker cron `2_transmit_mits_purchase_orders_to_vendors.php` transmits Compass PurchaseOrders to their vendors (Office Depot, Strategic Systems, Compass | worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php, library/app/client/compass.php |
8
9
  | [Compass USA](profile.md) | 2.0 | Compass USA is a TOGA client running a multi-tier supply-chain commerce operation. | |
9
10
  | [Compass Order Lifecycle & Data-Integrity Invariants](workflows/order-lifecycle-and-data-integrity.md) | 2.0 | End-to-end map of how a Compass order flows through the `Client_Compass` (2.0) database and the **expected raw-data shape** at each link/ASN/IF level. | |
@@ -13,6 +13,7 @@ files:
13
13
  - worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php
14
14
  related:
15
15
  - asn-to-item-fulfillment.md
16
+ - mits-po-transmission-to-vendors.md
16
17
  ---
17
18
 
18
19
  ## Summary
@@ -0,0 +1,89 @@
1
+ ---
2
+ title: Compass MITS PO Transmission to Vendors
3
+ framework: "2.0"
4
+ project: _Underscore
5
+ client: compass-usa
6
+ type: client-feature
7
+ status: active
8
+ updated: 2026-06-22
9
+ owners: ["rgirish"]
10
+ files:
11
+ - worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php
12
+ - library/app/client/compass.php
13
+ related:
14
+ - mits-po-to-so-item-linking.md
15
+ - order-lifecycle-and-data-integrity.md
16
+ ---
17
+
18
+ ## Summary
19
+ The 1.0 worker cron `2_transmit_mits_purchase_orders_to_vendors.php` transmits Compass
20
+ PurchaseOrders to their vendors (Office Depot, Strategic Systems, Compass, Presidio). Each
21
+ vendor's `Integration` decides the channel: **CXML** (HTTP POST of a cXML OrderRequest) or
22
+ **EMAIL** (a generated PDF emailed via `App_Email`). On success it writes "transmitted to
23
+ Vendor" log rows and stamps `PurchaseOrders.dtSubmitted`.
24
+
25
+ ## Key files / entry points
26
+ - `worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php` — the cron.
27
+ - `library/app/client/compass.php` — `App_Client_Compass` constants:
28
+ `CUSTOMER_ID__COMPASS = 2`, `CUSTOMER_ID__OFFICE_DEPOT = 1`, `CUSTOMER_ID__AGILANT = 3`;
29
+ `VENDOR_ID__OFFICE_DEPOT = 1`, `VENDOR_ID__STRATEGIC_SYSTEMS = 25`, `VENDOR_ID__COMPASS = 26`,
30
+ `VENDOR_ID__PRESIDIO = 4`.
31
+
32
+ ## How it works
33
+ 1. **Outer query** selects POs to send: `customerId = CUSTOMER_ID__COMPASS (2)`,
34
+ `dtSubmitted IS NULL`, and `vendorId IN (1, 25, 26, 4)`. (So it only ever transmits
35
+ `customerId = 2` POs — `customerId = 1` ODP-as-customer POs are out of scope here.)
36
+ 2. **Per PO**, an inner query loads `PurchaseOrderItems` and groups them by integration
37
+ (`VendorItems.overridePoSubmissionIntegrationId` else the vendor's
38
+ `defaultPoSubmissionIntegrationId`).
39
+ 3. **Per integration group** (the `foreach`): build + send the cXML POST or the PDF email,
40
+ then INSERT two `Logs_Compass.Record` rows
41
+ (`"Purchase Order <number> transmitted to Vendor via <CXML|EMAIL>. Part numbers: ..."`,
42
+ recordId 17 = PO and 14 = SO).
43
+ 4. **After the integration loop**, `UPDATE PurchaseOrders SET dtSubmitted = NOW()` and `sleep(10)`.
44
+
45
+ Routing note: Office Depot (vendor 1) `defaultPoSubmissionIntegrationId = 2`, which is an
46
+ **EMAIL** integration ("Office Depot / Yes") — so ODP POs are emailed PDFs by default, not
47
+ cXML, unless a `VendorItem` overrides it. There is also a CXML integration (id 1,
48
+ "Office Depot / No") that is NOT the default.
49
+
50
+ ## Data model
51
+ - `Client_Compass.PurchaseOrders.dtSubmitted` — set by step 4; the outer query's `IS NULL`
52
+ guard means a PO is only ever picked up once.
53
+ - `Logs_Compass.Record` — `recordId` 17 (PurchaseOrder) / 14 (SalesOrder), `primaryKeyId` =
54
+ the PO/SO `id`. The "transmitted to Vendor" note is the success marker.
55
+
56
+ ## Client variations
57
+ Compass Canada has a parallel `compasscanada/workflow/2_transmit_...` keyed on
58
+ `CUSTOMER_ID__COMPASS_CANADA`.
59
+
60
+ ## Gotchas / known issues
61
+ - **Item-less PO is silently marked submitted without being sent (the "logs show not sent
62
+ but dtSubmitted is set" symptom).** The `dtSubmitted = NOW()` UPDATE runs **unconditionally
63
+ after** the per-integration `foreach`, not inside it. If a PO has **zero
64
+ `PurchaseOrderItems`**, the inner items query returns nothing, `$itemsToSubmitByIntegration`
65
+ stays empty, the `foreach` body never runs — so **nothing is transmitted and no
66
+ "transmitted to Vendor" `Record` row is written** — yet `dtSubmitted` still gets stamped.
67
+ The PO then never re-qualifies for transmission (`dtSubmitted IS NULL` no longer matches).
68
+ **Diagnostic signature:** PO has `dtSubmitted` set but there is NO
69
+ `Logs_Compass.Record` "transmitted to Vendor" note (only "received from MITS") → the PO had
70
+ no line items. Confirmed on MR241298 / PO 50298575-1 (2026-06-21): `dtSubmitted` 23:20:04,
71
+ zero PurchaseOrderItems, no transmit log, no outbound Api log.
72
+ - **Root cause is upstream at MITS, not this cron.** MITS created the order header-only:
73
+ `POST /v2/sales-orders` and `POST /v2/purchase-orders` were both received with
74
+ `"salesOrderItems":[]` / `"purchaseOrderItems":[]` (both 201). The V2 API accepts an order
75
+ with no items (no empty-items rejection), TOGA stored exactly that, and the cron then masked
76
+ it. To confirm what MITS actually sent, read the inbound `requestPayload` in `Logs_Compass.Api`
77
+ (`direction = 'IN'`, `route` `/v2/purchase-orders` or `/v2/sales-orders`, `apiId = 2`).
78
+ - **No try/catch around the log INSERTs or the dtSubmitted UPDATE**, and they write to
79
+ different DBs (`Logs_Compass` vs `Client_Compass`) — a `Logs_Compass` failure can still leave
80
+ `dtSubmitted` stamped without a log (separate, previously-observed failure mode).
81
+
82
+ ## Change history
83
+ - 2026-06-22 — Documented the cron and the unconditional-`dtSubmitted` gotcha: item-less POs
84
+ get marked submitted without transmitting or logging; diagnosed via MR241298 inbound MITS
85
+ payload showing empty item arrays. No code change made (diagnosis only). (rgirish)
86
+
87
+ ## Related docs
88
+ - [Compass MITS PO → SO Item Linking](mits-po-to-so-item-linking.md)
89
+ - [Compass Order Lifecycle & Data-Integrity Invariants](../workflows/order-lifecycle-and-data-integrity.md)
@@ -13,6 +13,7 @@ related:
13
13
  - clients/compass-usa/features/asn-to-item-fulfillment.md
14
14
  - clients/compass-usa/features/mits-po-to-so-item-linking.md
15
15
  - clients/compass-usa/features/item-fulfillment-tracking-tableview.md
16
+ - clients/compass-usa/features/mits-po-transmission-to-vendors.md
16
17
  ---
17
18
 
18
19
  ## Summary
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.153",
3
+ "version": "1.0.155",
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",