toga-ai 1.0.766 → 1.0.767

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.
@@ -3,6 +3,7 @@
3
3
  | Doc | Summary | Files |
4
4
  |-----|---------|-------|
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, worker/crons/infrastructure/execute_dbchanges.php, worker/.ebextensions/030_dbchanges.config |
6
+ | [Canon received-PO → NetSuite sales order sync](features/canon-received-po-netsuite-order-sync.md) | A 1.0 `worker` cron that reads Canon customer purchase orders out of the **Vision** databases and creates the matching **sales order in NetSuite**. | worker/crons/sync/sync_received_po.php, library/netsuitetoolkit/NSPHPClient.php |
6
7
  | [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, worker/schedules/cron.worker.sync.json |
7
8
  | [Compass Manager Approval Reminder Emails (1.0 worker crons)](features/compass-manager-approval-reminder-emails.md) | Two 1.0 worker crons nag approvers about sales orders still waiting on a decision — one per Compass tenant. | worker/crons/toga2/compass/compass_email_reminders.php, worker/crons/toga2/compasscanada/compass_email_reminders.php, worker/schedules/cron.worker.sync.json, worker1.5/crons/toga2/compass/compass_email_reminders.php, worker1.5/schedules/cron.worker.json |
8
9
  | [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/compass/send_delivered_email.php, worker/crons/toga2/compasscanada/update_salesorder_status_from_odp.php, worker/crons/toga2/compasscanada/workflow/3_update_salesorder_status_from_grand_and_toy.php, worker/crons/toga2/compasscanada/send_delivered_email.php, worker/crons/toga2/compasscanada/workflow/test_partial_in_transit_email.php, worker/crons/toga2/compasscanada/workflow/test_partial_delivered_email.php, library/app/client/compasscanada.php |
@@ -0,0 +1,112 @@
1
+ ---
2
+ title: Canon received-PO → NetSuite sales order sync
3
+ framework: "1.0"
4
+ repo: worker
5
+ project: Worker
6
+ client: canon
7
+ type: feature
8
+ status: active
9
+ updated: 2026-09-03
10
+ owners: ["mhammontree"]
11
+ files:
12
+ - worker/crons/sync/sync_received_po.php
13
+ - library/netsuitetoolkit/NSPHPClient.php
14
+ related:
15
+ - ./netsuite-togasupply-per-client-sync.md
16
+ - ../../../clients/canon/profile.md
17
+ ---
18
+
19
+ ## Summary
20
+ A 1.0 `worker` cron that reads Canon customer purchase orders out of the **Vision** databases and
21
+ creates the matching **sales order in NetSuite**. Before it can create the order it must turn every
22
+ Vision part number into a real NetSuite item — bundles are exploded into components and prefixes are
23
+ stripped. If any line cannot be matched, no order is created and operations gets a "not every part
24
+ number could be located" email.
25
+
26
+ **Critical rule:** the prefix strip list (`ASI-`, `/4YR`, `TOGA-`) appears **twice** in the file —
27
+ line ~54 and line ~233. A new SKU prefix must be added to **both** or the sync silently searches
28
+ NetSuite for the bundle name itself and fails.
29
+
30
+ ## Key files / entry points
31
+ - `worker/crons/sync/sync_received_po.php` — the whole sync (selection, bundle explode, NetSuite
32
+ item lookup, order create, all three emails).
33
+ - `library/netsuitetoolkit/NSPHPClient.php` line 200 — sets `SOAP_SINGLE_ELEMENT_ARRAYS`.
34
+
35
+ ## How it works
36
+ 1. **Selection.** Picks up rows from `Vision.CustomerPurchaseOrders` joined to
37
+ `CustomerPurchaseOrderItems` on `customerPurchaseOrderId`, where
38
+ `companyId = 13785 AND amiExportEnabled = 1 AND exportedToAmi = 0 AND IFNULL(netsuiteOrderNumber,'') LIKE ''`.
39
+ 2. **Bundle explode.** Custom-catalog bundles are expanded into their component part numbers
40
+ **before** any NetSuite search, using `Vision_Catalog.CustomProducts` joined through
41
+ `CustomCatalogCache` → `Products` for Canon companies **13785 / 18610**.
42
+ 3. **Prefix normalization.** Both the explode map (line ~54) and the per-line lookup (line ~233) run
43
+ `trim(str_replace(['ASI-', '/4YR', 'TOGA-'], '', $partNumber))`.
44
+ 4. **NetSuite item lookup** (~line 250) walks the search results and deliberately **skips
45
+ `ItemGroup` records**: `if (get_class($record) != 'ItemGroup') { $internalId = $record->internalId; break; }`.
46
+ If nothing is left, `$allItemsLocated` goes false and the failure email fires.
47
+ 5. **Quantity.** `billTo` selects the NetSuite customer and whether `$enforceQuantityOne` is set:
48
+ - `HPFS-LEASES` → 2839 — `enforceQuantityOne = true`
49
+ - `ACH-ASILEASE` → 28537 — `enforceQuantityOne = true`
50
+ - `ACH-HP-ACCESSOR` → 21908 if the order number starts with `9`, else 21055 — `false`
51
+ With `enforceQuantityOne = true` an exploded **bundle** line is written at quantity 1.
52
+ 6. **Price.** The exploded bundle's main line is priced from
53
+ `IFNULL(CustomProducts.listPrice, PriceProfilePartRules.rate)` — the Vision catalog price is
54
+ taken as-is; the sync does **not** compute cost + margin.
55
+ 7. **Email.** Three distinct bodies: SUCCESSFUL, a generic FAILED, and the "not every part number
56
+ could be located" FAILED that lists `$partNumbersCouldNotFind`. **Only the third names part
57
+ numbers** — that is the tell for a lookup miss vs. a NetSuite write failure. Recipients are
58
+ hardcoded near the top (`$emailOrdersTo` mfrye, cmullen; CC jblevins, sking).
59
+
60
+ ## Data model
61
+ - `Vision.CustomerPurchaseOrders`, `Vision.CustomerPurchaseOrderItems` (schema `Vision`,
62
+ environment `legacy`).
63
+ - `Vision_Catalog.CustomProducts`, `CustomCatalogCache`, `Products`, `PriceProfilePartRules`.
64
+ - Canon's catalog is 21 parts: 19 plain, 1 `ASI-` prefixed, and exactly one `TOGA-` prefixed
65
+ (`TOGA-MGEE4LL/A`) — the only entry with both `assetTagging` and `biosTagging` set.
66
+
67
+ ## Client variations
68
+ Canon-only cron (`companyId 13785`, catalog companies 13785 / 18610). No other client runs it.
69
+
70
+ ## Gotchas / known issues
71
+ - **A missing prefix makes the bundle name look like a part number.** `TOGA-MGEE4LL/A` was searched
72
+ literally; in NetSuite that name is an **Item Group** (internal id 1609121) whose components are
73
+ `MGEE4LL/A`, `SVC-TS-BIOSTAG`, `SVC-TS-ASSETTAG`. Because the lookup skips `ItemGroup`, the search
74
+ "found" the item and still returned nothing usable. The real item is `MGEE4LL/A`
75
+ (Serialized Inventory Item, internal id 1607214). Add any new prefix to **both** `str_replace`
76
+ arrays.
77
+ - **Quantity 12 on a lease PO means 12 monthly payments for ONE machine, not 12 machines.** Verified
78
+ against the manually keyed records for PO 9318575 (PO 169288 TD Synnex, SO 285071) — both show
79
+ `MGEE4LL/A` qty 1. Do **not** "fix" `$enforceQuantityOne` to pass the PO quantity through.
80
+ **Non-bundle** parts are not exploded and therefore DO keep the PO quantity (e.g. older
81
+ `ASI-MW1H3LL/A` orders imported at qty 12).
82
+ - **Catalog price can be stale, and that is accepted.** `TOGA-MGEE4LL/A` `listPrice` is $5,118.45;
83
+ the manually keyed SO 285071 used $5,106.45 (TD Synnex cost $4,749.00 + 7% margin = $357.45 GP).
84
+ Decision 2026-09-03: ship as-is — the sync should not compute margin; a reviewer adjusts the
85
+ auto-created order.
86
+ - **⚠ OPEN LATENT BUG (not fixed, needs its own ticket).** In the per-line NetSuite lookup, if
87
+ `$response->searchResult->status->isSuccess` is FALSE the code does **nothing** — the line is
88
+ silently skipped, `$allItemsLocated` stays TRUE, and the sales order is created in NetSuite
89
+ **missing that line**, with no email and no error. A NetSuite search outage or throttled call
90
+ produces a silently short order. This violates the fail-loud rule in `1.0/standards/backend-php.md`
91
+ via an implicit no-op rather than an explicit catch.
92
+ - **A fixed PO does not re-run.** Rows are flagged `exportedToAmi = 1` even when the export failed,
93
+ so deploying a fix does **not** replay them. POs 9318538, 9318575 and 9318670 (all
94
+ `TOGA-MGEE4LL/A`, qty 12, `netsuiteOrderNumber` NULL) were already flagged and need manual entry
95
+ or a flag reset. 9318575 is in NetSuite as SO 285071, keyed manually.
96
+ - **Ruled out — do not re-investigate.** (a) The single-result SOAP array trap does not apply: the
97
+ lookup is guarded by `is_array($response->searchResult->recordList->record)`, and
98
+ `library/netsuitetoolkit/NSPHPClient.php` line 200 sets `SOAP_SINGLE_ELEMENT_ARRAYS`, so a
99
+ one-record search is still an array. (b) Commit `a3e33776c` (TRUE-75559, 2026-03-17) did not
100
+ introduce the skip-ItemGroup behavior — it only deleted a commented-out block; the older
101
+ commented version actually *preferred* the ItemGroup, so the flip happened years earlier under a
102
+ different file path.
103
+
104
+ ## Change history
105
+ - 2026-09-03 — Added `TOGA-` to the prefix strip list at both line 54 and line 233 so Canon's new
106
+ bundle SKU `TOGA-MGEE4LL/A` resolves to `MGEE4LL/A` instead of the NetSuite Item Group; documented
107
+ the qty-1 lease rule, the accept-catalog-price decision, and the silent `isSuccess=false` skip
108
+ (TRUE-80811, mhammontree).
109
+
110
+ ## Related docs
111
+ - [NetSuite → TOGa Supply per-client sync](./netsuite-togasupply-per-client-sync.md)
112
+ - [Canon profile](../../../clients/canon/profile.md)
@@ -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)_ — 24 doc(s) → [1.0/apps/library/INDEX.md](1.0/apps/library/INDEX.md)
8
- - **worker** (Worker) — 32 doc(s) → [1.0/apps/worker/INDEX.md](1.0/apps/worker/INDEX.md)
8
+ - **worker** (Worker) — 33 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) — 13 doc(s) → [1.0/apps/togadesk/INDEX.md](1.0/apps/togadesk/INDEX.md)
@@ -8,11 +8,12 @@ project: Worker
8
8
  client: canon
9
9
  type: profile
10
10
  status: active
11
- updated: 2026-08-29
12
- owners: ["jcardinal"]
11
+ updated: 2026-09-03
12
+ owners: ["jcardinal", "mhammontree"]
13
13
  files: []
14
14
  related:
15
15
  - ../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md
16
+ - ../../1.0/apps/worker/features/canon-received-po-netsuite-order-sync.md
16
17
  ---
17
18
 
18
19
  ## Summary
@@ -32,3 +33,7 @@ full mechanics and the diagnosis playbook live in
32
33
  ## Integration touchpoints
33
34
 
34
35
  - **`worker` / `library` (1.0)** — the shared togasupply sync engine + `App_Api_Toga2` bridge.
36
+ - **`worker` (1.0) — received-PO → NetSuite sales orders.** `crons/sync/sync_received_po.php` reads
37
+ Canon POs from the **Vision** databases (`companyId 13785`, catalog companies 13785 / 18610),
38
+ explodes custom-catalog bundles into components, and creates the NetSuite sales order. See
39
+ [Canon received-PO → NetSuite sales order sync](../../1.0/apps/worker/features/canon-received-po-netsuite-order-sync.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.766",
3
+ "version": "1.0.767",
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",