toga-ai 1.0.570 → 1.0.571

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.
@@ -94,6 +94,31 @@ The 850 carries the identifiers the 855 echoes back:
94
94
  full 850 → TOGa identifier map, incl. `BEG` = ODP PO number and `REF*QC` = the value to
95
95
  search on for the ODP SalesOrder.)
96
96
 
97
+ ## Rejections raise a business alert (Logs.Issue)
98
+ A reject used to be **silent** — the PO was skipped and no human was notified. Cron `3a` now
99
+ surfaces every over-quantity/duplicate rejection as a curated **Logs.Issue** alert by throwing
100
+ `App_Exception_Business` (issue key **`COMPASS_OFFICE_DEPOT_PO_IMPORT_REJECTED_OVER_QUANTITY`**,
101
+ `URGENCY_HIGH`, message listing the rejected PO numbers). The reject 855 is still built and
102
+ acknowledged back to Office Depot first; the alert is purely additional notification. The issue
103
+ key follows the sibling cron `1_transmit_compass_sales_orders_to_mits.php` naming convention.
104
+
105
+ ### ⚠ Gotcha — the throw is DEFERRED to end-of-run, never inline
106
+ The exception is **not** thrown in the reject branch. It is deferred to the very end of the run:
107
+ after the full `$ediFiles['Contents']` loop, after each S3 850 file is logged + deleted, and
108
+ after `App_Framework::cronFinished()`. A single collector array
109
+ (`$rejectedPurchaseOrderNumbers`) accumulates rejected PO numbers across all files/POs, and one
110
+ exception is thrown at end-of-run.
111
+
112
+ **Why it must be deferred:** an inline throw unwinds the loop *before* the per-file S3 delete, so
113
+ the same 850 is re-fetched next run and a **duplicate reject 855 is re-sent to Office Depot every
114
+ run — an "855 storm."** Deferring guarantees each 850 is deleted first, and the stable issue key
115
+ collapses multiple rejects into one Logs.Issue.
116
+
117
+ Throwing **after** `cronFinished()` is safe: the overlap guard
118
+ (`App_Framework::exitIfProcessRunning`, a `ps -ef` process-table scan) has no dependency on
119
+ cronFinished's `CronJobExecutions` checkout row, and on CLI an uncaught `App_Exception_Business`
120
+ just echoes `ERROR [ref]:` and exits.
121
+
97
122
  ## ⚠ Open item — reject codes vs ODP's 855 companion guide
98
123
  The reject codes used (`BAK02 = RD`, `ACK01 = IR`) are the **standards-correct** X12 values.
99
124
  They have **not** yet been confirmed against Office Depot's 855 companion guide. If ODP
@@ -102,6 +127,13 @@ A legacy, abandoned sketch (`compass/edi/3_send_edi_855(reject).php`) used **non
102
127
  codes `BAK01 = A1` / `BAK02 = RE` — that path was **deliberately NOT followed**.
103
128
 
104
129
  ## Change history
130
+ - 2026-08-13 — Rejections now raise a business alert: cron `3a` throws
131
+ `App_Exception_Business` (`COMPASS_OFFICE_DEPOT_PO_IMPORT_REJECTED_OVER_QUANTITY`,
132
+ `URGENCY_HIGH`) so every over-quantity/duplicate reject surfaces as a Logs.Issue instead of
133
+ being silent. The throw is **deferred to end-of-run** (after the file loop, per-file S3
134
+ delete, and `cronFinished()`) via a `$rejectedPurchaseOrderNumbers` collector — an inline
135
+ throw would skip the S3 delete and cause an "855 storm" (same 850 re-fetched, duplicate reject
136
+ 855 re-sent every run). `php -l` clean; php-reviewer 0/0. (jcardinal)
105
137
  - 2026-08-13 — Built the over-quantity guard on the ODP 850 importer (cron 3a): ODP
106
138
  re-sending an already-processed Compass SO line under a new PO number now rejects the whole
107
139
  PO (new helper `officeDepotPurchaseOrderExceedsCompassDemand()`, "first occurrence passes",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.570",
3
+ "version": "1.0.571",
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",