toga-ai 1.0.568 → 1.0.569

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 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
5
5
  ## 1.0 framework
6
6
 
7
7
  - **library** (Library) _(framework core)_ — 17 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
- - **worker** (Worker) — 22 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
8
+ - **worker** (Worker) — 23 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
9
9
  - **dbchanges** (Database Changes) _(framework core)_ — 1 doc(s) → [1.0/apps/dbchanges/INDEX.md](1.0/apps/dbchanges/INDEX.md)
10
10
  - **worker1.5** (Worker 1.5) — 0 doc(s) → [1.0/apps/worker1.5/INDEX.md](1.0/apps/worker1.5/INDEX.md)
11
11
  - **togadesk** (TOGa Desk) — 12 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
@@ -12,6 +12,7 @@
12
12
  | [Compass MITS PO rejection — zero line items (400 + Issue + PM alert email)](features/mits-po-zero-line-item-rejection.md) | 2.0 | MITS transmits Compass purchase orders that carry **`"purchaseOrderItems": []`** (real example: PO `50309196-1`, `mitsSalesOrder` `MR244630`). | _underscore/Model/Compass/PurchaseOrder.php, _underscore/Model/Compass/Usa/PurchaseOrder.php, _underscore/Model/Compass/Canada/PurchaseOrder.php, worker2/Worker/Client/Compass/reports/PurchaseOrderRejectionEmail.php |
13
13
  | [Compass MITS Sales-Order Transmission — Rejection Alerting (Issue/Event, not email-in-cron)](features/mits-sales-order-transmission-alerting.md) | 2.0 | When MITS **rejects** a Compass sales order transmitted by the 1.0 cron `1_transmit_compass_sales_orders_to_mits.php`, the alert is no longer an email built ins | worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php, worker2/Worker/Infrastructure/Errors.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/IssueEmailAddress.php, dbchanges2/Logs/2026-08-04a - MITS rejection business recipients.sql |
14
14
  | [Compass MR/MA Order Auto-Approval & Status Gate](features/mr-ma-order-approval-and-status.md) | 2.0 | Compass **MR** and **MA** sales orders are system-generated from the MITS / Office Depot EDI pipeline (they do not originate as user-entered SA orders) and must | _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/PurchaseOrder.php, worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php |
15
+ | [Compass Office Depot EDI 855 Acknowledgement + Over-Quantity PO Guard](features/odp-edi-855-acknowledgement-and-overquantity-guard.md) | 1.0 | How Compass acknowledges Office Depot (ODP) inbound **EDI 850** purchase orders with an **X12 855**, and the **over-quantity guard** that rejects a duplicate PO | worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php, worker/crons/toga2/compass/workflow/4_transmit_office_depot_po_acknowledgements.php, library/app/edi.php |
15
16
  | [Compass PEOPLE-File User Lifecycle (duplicate accounts, reactivation grace window, raw-SQL deactivation)](features/people-file-user-lifecycle.md) | 2.0 | The nightly **PEOPLE** file cron (`_Worker_Client_Compass_PeopleFile`, `worker2/Worker/Client/Compass/PeopleFile.php`) owns the whole `Users` row lifecycle for | worker2/Worker/Client/Compass/PeopleFile.php |
16
17
  | [Persona Model & Levy-Sector Gating (worker2 PEOPLE cron)](features/persona-model-and-levy-gating.md) | 2.0 | Compass USA catalogue visibility is driven by **personas** in `Client_Compass`. | worker2/Worker/Client/Compass/PeopleFile.php |
17
18
  | [Compass Sales-Order Line-Number Renumbering (and why it hangs off the SO hooks)](features/sales-order-line-renumbering.md) | 2.0 | Compass sales-order lines must stay numbered **1..N with no gaps** after any add, edit, or delete — downstream MITS/PO linking reads `lineNumber` as an identity | _underscore/Model/Compass/SalesOrderItem.php, _underscore/Model/Compass/SalesOrder.php |
@@ -19,7 +20,7 @@
19
20
  | [Compass Cross-Kit Bundle Corruption — Detection & Repair](workflows/cross-kit-bundle-corruption.md) | 2.0 | A frontend regression in `toga2-commerce`'s edit-order bundle submission mis-attributed bundle (kit) line items and **fees/warranties** to the **wrong kit**, pe | src/api/syncSalesOrderItemsFromLocalStorageCartToApi.ts |
20
21
  | [Compass Office Depot Duplicate PO-Line Cleanup (dual-catalog SKU)](workflows/odp-duplicate-po-line-cleanup.md) | 1.0 | The NetSuite fulfillment-sales-order importer duplicated Office Depot purchase-order lines on Compass USA because it reconciled the fulfillment SO against the e | library/app/api/toga2.php, dbchanges2/Client_Compass/2026-07-22a - CleanupOfficeDepotDuplicatePurchaseOrderItems.sql |
21
22
  | [Recovering a Lost Compass ODP EDI 850 Import (re-drop from Logs.FileLog)](workflows/odp-edi-import-recovery.md) | 1.0 | How to recover a Compass **Office Depot EDI 850** import that failed partway — the case where cron **3a** created the ODP SalesOrder header, the follow-up item | worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php, worker/crons/toga2/compass/workflow/5_create_netsuite_sales_orders_from_office_depot_purchase_orders.php, worker/schedules/cron.worker.sync.json |
22
- | [Compass ODP Order Pipeline to NetSuite (numbered worker crons)](workflows/odp-order-pipeline-to-netsuite.md) | 1.0 | The end-to-end **Compass Office Depot (ODP) order → NetSuite** pipeline as it actually runs through the 1.0 `worker` crons under `worker/crons/toga2/compass/`, | worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php, worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php, worker/crons/toga2/compass/edi/1_download_edi_s3_create_po_toga.php, worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php, worker/crons/toga2/compass/workflow/5_create_netsuite_sales_orders_from_office_depot_purchase_orders.php, worker/schedules/cron.worker.sync.json, library/app/client/compass.php |
23
+ | [Compass ODP Order Pipeline to NetSuite (numbered worker crons)](workflows/odp-order-pipeline-to-netsuite.md) | 1.0 | The end-to-end **Compass Office Depot (ODP) order → NetSuite** pipeline as it actually runs through the 1.0 `worker` crons under `worker/crons/toga2/compass/`, | worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php, worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php, worker/crons/toga2/compass/edi/1_download_edi_s3_create_po_toga.php, worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php, worker/crons/toga2/compass/workflow/4_transmit_office_depot_po_acknowledgements.php, worker/crons/toga2/compass/workflow/5_create_netsuite_sales_orders_from_office_depot_purchase_orders.php, worker/schedules/cron.worker.sync.json, library/app/client/compass.php |
23
24
  | [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. | |
24
25
  | [Comparing Compass Persona/Catalogue Effects across prod and dev-sandbox](workflows/persona-population-env-comparison.md) | 2.0 | How to prove what a persona/catalogue change actually does to **100% of Compass USA users**, by diffing production against dev-sandbox. | test/@Bala/generate_persona_test_data.py, test/@Bala/persona-refactor-full-user-export.sql, test/@Bala/TRUE-75705-persona-refactor-test-plan.md |
25
26
  | [Compass USA Persona Refactor — 8-Phase Migration & Deploy Order (TRUE-75705)](workflows/persona-refactor-migration.md) | 2.0 | Splits Compass USA's catch-all persona **1 ("All")** into **Base** (the six printer bundles, held by *everyone* including Levy) and a new **Non-Levy** persona ( | dbchanges2/Client_Compass/2026-08-06 - PersonaRefactor.sql, dbchanges2/Client_Compass/2026-08-06 - PersonaRefactor-Verification.sql, worker2/Worker/Client/Compass/PeopleFile.php, test/@Bala/TRUE-75705-persona-refactor-test-plan.md |
@@ -0,0 +1,116 @@
1
+ ---
2
+ title: Compass Office Depot EDI 855 Acknowledgement + Over-Quantity PO Guard
3
+ framework: "1.0"
4
+ repo: worker
5
+ project: Worker
6
+ client: compass-usa
7
+ type: client-feature
8
+ status: active
9
+ updated: 2026-08-13
10
+ owners: ["jcardinal"]
11
+ files:
12
+ - worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php
13
+ - worker/crons/toga2/compass/workflow/4_transmit_office_depot_po_acknowledgements.php
14
+ - library/app/edi.php
15
+ related:
16
+ - ../workflows/odp-order-pipeline-to-netsuite.md
17
+ - ../workflows/odp-duplicate-po-line-cleanup.md
18
+ - mits-po-transmission-to-vendors.md
19
+ - ../profile.md
20
+ ---
21
+
22
+ ## Summary
23
+ How Compass acknowledges Office Depot (ODP) inbound **EDI 850** purchase orders with an
24
+ **X12 855**, and the **over-quantity guard** that rejects a duplicate PO before it is ever
25
+ imported. Two paths share one builder:
26
+
27
+ - **Accept** — the normal case. Cron `4_transmit_office_depot_po_acknowledgements.php`
28
+ (every 15 min) picks up imported `PurchaseOrders` rows with `dtAcknowledged IS NULL` and
29
+ sends an accept 855, then stamps `dtAcknowledged`.
30
+ - **Reject** — cron `3a_import_office_depot_purchase_orders.php` (the 850 importer, every 5
31
+ min) detects a PO whose quantities exceed the Compass sales-order demand, **rejects the
32
+ entire PO (never imports it)**, and sends a reject 855 **inline from the importer**.
33
+
34
+ Both 855s are produced by the new shared 1.0-core method
35
+ `App_Edi::buildOfficeDepotPurchaseOrderAcknowledgement855()` (`library/app/edi.php`).
36
+
37
+ ## The problem this guards against
38
+ Office Depot began **re-sending an already-processed Compass sales-order line under a
39
+ BRAND-NEW ODP PO number** — same item, doubling the quantity. Example: one Compass SO
40
+ `MR244753` line was purchased twice, via ODP PO `41672697-1214` **and** `41665576-1214`.
41
+
42
+ The importer's only prior duplicate guard de-duped on **exact PO number**, so a
43
+ new-numbered duplicate sailed straight through and created a second downstream order —
44
+ the same failure family as the [dual-catalog duplicate PO-line
45
+ bug](../workflows/odp-duplicate-po-line-cleanup.md), but caused upstream by ODP re-sending,
46
+ not by our reconciliation.
47
+
48
+ ## The over-quantity guard (`3a`, before any SO/PO creation)
49
+ New helper **`officeDepotPurchaseOrderExceedsCompassDemand()`** runs **after** the existing
50
+ same-PO-number de-dupe and **before** any SalesOrder/PurchaseOrder creation:
51
+
52
+ - For each part number on the incoming 850, it sums the Compass SO's **ordered quantity
53
+ (ceiling)** and compares it against **all ODP PO quantity already chained to that Compass
54
+ SO plus this PO**.
55
+ - **If any matched part exceeds demand, the ENTIRE PO is rejected** — nothing is imported —
56
+ and a reject 855 is sent.
57
+ - **"First occurrence passes" rule** (uniform across MR / MA / SA order types): if the
58
+ Compass SO does not exist yet there is nothing to exceed, so the PO passes. This keeps the
59
+ guard order-type-agnostic.
60
+ - **Parts not on the Compass SO are ignored** (no false rejects on unrelated lines).
61
+ - The demand read uses a direct **read-only `db_prod_compass`** link (added this session),
62
+ with `App_Database::sqlEscape` on the SO number.
63
+
64
+ The demand relay it walks is the standard ODP chain — Compass SO (top, e.g. `MR244753`) →
65
+ Compass PO → ODP SO → ODP PO (leaf) — via the `SalesOrders_PurchaseOrders` and
66
+ `PurchaseOrders_SalesOrders` bridges (the same 6-table join the acknowledgement cron uses).
67
+
68
+ ## Why the reject is sent inline, not by cron 4
69
+ A rejected PO is **never persisted**, so cron 4 — which keys off `PurchaseOrders` rows with
70
+ `dtAcknowledged IS NULL` — can never see it. The importer therefore **builds and sends the
71
+ reject 855 itself** from the parsed EDI, through the same AS2 outbox the accept path uses
72
+ (`agilant-as2`, prefix `OfficeDepot/OUTBOX/`). No schema change, no new/changed cron
73
+ schedule.
74
+
75
+ ## The shared 855 builder (`App_Edi::buildOfficeDepotPurchaseOrderAcknowledgement855`)
76
+ Additive to the 1.0 core (`library/app/edi.php`). Renders the X12 855 with an
77
+ **accept/reject mode**:
78
+
79
+ - **Accept** reproduces the previous cron output **byte-for-byte**.
80
+ - **Reject** flips only two elements: **`BAK02` `AD` → `RD`** and **`ACK01` `IA` → `IR`**.
81
+ - Envelope values are taken from the live accept path: **sender `2127360115`, receiver
82
+ `ODEPCC1`, interchange `000042405`**.
83
+
84
+ Cron 4 was refactored to delegate accept-mode construction to this method
85
+ (behavior-preserving: same query, logging, AS2 send, and `dtAcknowledged` stamp). The
86
+ importer's reject path uses reject mode.
87
+
88
+ ## 850 REF round-trip (for tracing / builder inputs)
89
+ The 850 carries the identifiers the 855 echoes back:
90
+ - **`REF*CO`** — `productSpecificationDocumentNumber`
91
+ - **`REF*QC`** — `internalSalesOrder`
92
+
93
+ (See the [ODP order pipeline doc](../workflows/odp-order-pipeline-to-netsuite.md) for the
94
+ full 850 → TOGa identifier map, incl. `BEG` = ODP PO number and `REF*QC` = the value to
95
+ search on for the ODP SalesOrder.)
96
+
97
+ ## ⚠ Open item — reject codes vs ODP's 855 companion guide
98
+ The reject codes used (`BAK02 = RD`, `ACK01 = IR`) are the **standards-correct** X12 values.
99
+ They have **not** yet been confirmed against Office Depot's 855 companion guide. If ODP
100
+ expects different reject values it is a **one-line change** in the builder's reject branch.
101
+ A legacy, abandoned sketch (`compass/edi/3_send_edi_855(reject).php`) used **non-standard**
102
+ codes `BAK01 = A1` / `BAK02 = RE` — that path was **deliberately NOT followed**.
103
+
104
+ ## Change history
105
+ - 2026-08-13 — Built the over-quantity guard on the ODP 850 importer (cron 3a): ODP
106
+ re-sending an already-processed Compass SO line under a new PO number now rejects the whole
107
+ PO (new helper `officeDepotPurchaseOrderExceedsCompassDemand()`, "first occurrence passes",
108
+ parts-not-on-SO ignored, read-only `db_prod_compass` link). Added shared
109
+ `App_Edi::buildOfficeDepotPurchaseOrderAcknowledgement855()` (accept byte-for-byte, reject
110
+ flips `BAK02` AD→RD + `ACK01` IA→IR); refactored cron 4 to use it (accept mode);
111
+ reject 855 sent inline from the importer via the AS2 outbox since a rejected PO is never
112
+ persisted. No schema/schedule change; both repos deployed (library before worker). Tests
113
+ skipped per developer. Reject codes pending confirmation against ODP's 855 companion guide.
114
+ (jcardinal)
115
+ </content>
116
+ </invoke>
@@ -32,6 +32,7 @@ related:
32
32
  - features/approval-decision-flow.md
33
33
  - workflows/cross-kit-bundle-corruption.md
34
34
  - workflows/odp-duplicate-po-line-cleanup.md
35
+ - features/odp-edi-855-acknowledgement-and-overquantity-guard.md
35
36
  - ../../2.0/apps/worker2/features/compass-vip-support-importer.md
36
37
  - ../../2.0/apps/toga2-commerce/features/expedited-shipping-gating.md
37
38
  - ../../2.0/apps/toga2-commerce/features/category-tile-sort-order.md
@@ -90,6 +91,12 @@ separate, related client (see its own profile).
90
91
  duplicate Office Depot PO lines backed by shadow VendorItems (4,886 dup lines / 1,320 POs).
91
92
  Root cause, forward fix, and the `2026-07-22a` cleanup migration:
92
93
  [ODP Duplicate PO-Line Cleanup](workflows/odp-duplicate-po-line-cleanup.md).
94
+ - **ODP re-sends processed lines under new PO numbers (2026-08-13).** Office Depot began
95
+ re-sending an already-processed Compass SO line under a **brand-new** ODP PO number
96
+ (doubling quantity). The 850 importer (cron 3a) now runs an **over-quantity guard** that
97
+ rejects the whole PO and sends a reject **EDI 855**; ODP POs are acknowledged with an 855
98
+ (accept via cron 4, reject inline from 3a) built by a shared `App_Edi` method. See
99
+ [ODP EDI 855 Acknowledgement + Over-Quantity Guard](features/odp-edi-855-acknowledgement-and-overquantity-guard.md).
93
100
  - ASN ingestion entry points: cXML to the V2 API (logged in `Logs_Compass.Api`) and
94
101
  `worker/crons/toga2/compass/workflow/3b_import_strategic_systems_advance_shipping_notices.php`.
95
102
 
@@ -7,16 +7,18 @@ client: compass-usa
7
7
  type: workflow
8
8
  status: active
9
9
  updated: 2026-08-04
10
- owners: ["rgirish", "bala", "dfranks"]
10
+ owners: ["rgirish", "bala", "dfranks", "jcardinal"]
11
11
  files:
12
12
  - worker/crons/toga2/compass/workflow/1_transmit_compass_sales_orders_to_mits.php
13
13
  - worker/crons/toga2/compass/workflow/2_transmit_mits_purchase_orders_to_vendors.php
14
14
  - worker/crons/toga2/compass/edi/1_download_edi_s3_create_po_toga.php
15
15
  - worker/crons/toga2/compass/workflow/3a_import_office_depot_purchase_orders.php
16
+ - worker/crons/toga2/compass/workflow/4_transmit_office_depot_po_acknowledgements.php
16
17
  - worker/crons/toga2/compass/workflow/5_create_netsuite_sales_orders_from_office_depot_purchase_orders.php
17
18
  - worker/schedules/cron.worker.sync.json
18
19
  - library/app/client/compass.php
19
20
  related:
21
+ - clients/compass-usa/features/odp-edi-855-acknowledgement-and-overquantity-guard.md
20
22
  - clients/compass-usa/workflows/odp-edi-import-recovery.md
21
23
  - clients/compass-usa/workflows/order-lifecycle-and-data-integrity.md
22
24
  - clients/compass-usa/features/mits-po-transmission-to-vendors.md
@@ -122,6 +124,26 @@ duplicates and backfills whatever is missing**:
122
124
  a **zero-item ODP SalesOrder sits in this queue retrying every 10 minutes** until its lines are
123
125
  restored — the retry loop is the symptom, not the cause.
124
126
 
127
+ ## The active flow is `workflow/` — the other ODP folders are legacy
128
+ The **live** Office Depot EDI flow is entirely under
129
+ `worker/crons/toga2/compass/workflow/`: **3a** import (every 5 min), **4** transmit-PO-
130
+ acknowledgements (every 15 min), **5** create-NetSuite-SO, **6** generate-ASN. The folders
131
+ `compass/edi/`, `compass/DOA_edi_odp_flow/`, and `compass/DOA_Scripts/` are **legacy /
132
+ inactive** (this is why `edi/1` is dead code — see the correction above). In particular
133
+ `compass/edi/3_send_edi_855(reject).php` is an abandoned sketch using **non-standard** reject
134
+ codes (`BAK01=A1` / `BAK02=RE`) — **do not** follow it.
135
+
136
+ - **Cron 4 — `workflow/4_transmit_office_depot_po_acknowledgements.php`** (every 15 min) sends
137
+ the **accept** X12 **855** for imported ODP `PurchaseOrders` with `dtAcknowledged IS NULL`,
138
+ then stamps `dtAcknowledged`. It delegates 855 construction to
139
+ `App_Edi::buildOfficeDepotPurchaseOrderAcknowledgement855()` (accept mode). A **reject** 855
140
+ is instead sent **inline from cron 3a** when the over-quantity guard rejects a PO (a rejected
141
+ PO is never persisted, so cron 4 can never see it). See
142
+ [ODP EDI 855 Acknowledgement + Over-Quantity Guard](../features/odp-edi-855-acknowledgement-and-overquantity-guard.md).
143
+ - **Cron 3a now also runs an over-quantity guard** (`officeDepotPurchaseOrderExceedsCompassDemand()`)
144
+ after the same-PO-number de-dupe and before any SO/PO creation, rejecting a whole PO when ODP
145
+ re-sends an already-processed Compass SO line under a **new** PO number. Same doc.
146
+
125
147
  ## Cron 5 NetSuite SO creation — failure modes & the two ODP error emails
126
148
 
127
149
  Two different Compass ODP error emails exist, from **two different pipeline stages**. They look
@@ -193,6 +215,14 @@ wrong record makes every order look "stuck." Use the **join**, never a number ma
193
215
  transmission feature.)
194
216
 
195
217
  ## Change history
218
+ - 2026-08-13 — Added **cron 4** (transmit ODP PO acknowledgements, every 15 min) and noted
219
+ **cron 6** (generate ASN); flagged the active `workflow/` folder vs the legacy
220
+ `compass/edi/`, `compass/DOA_edi_odp_flow/`, `compass/DOA_Scripts/` folders (incl. the
221
+ abandoned non-standard-reject `edi/3` sketch). Recorded cron 3a's new over-quantity guard
222
+ (rejects a whole PO when ODP re-sends a Compass SO line under a new PO number) and the shared
223
+ reject/accept 855 builder; details in the new
224
+ [855 Acknowledgement + Over-Quantity Guard](../features/odp-edi-855-acknowledgement-and-overquantity-guard.md)
225
+ feature. (jcardinal)
196
226
  - 2026-08-04 — **Corrected the 850-import step**: `edi/1_download_edi_s3_create_po_toga.php` is
197
227
  **dead code in no schedule**; the schedule entry named "Download EDI From S3 Files & Create PO -
198
228
  EDI Office Depot" (`*/5`) actually runs `workflow/3a_import_office_depot_purchase_orders.php`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.568",
3
+ "version": "1.0.569",
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",