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)
|
package/knowledge/INDEX.md
CHANGED
|
@@ -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) —
|
|
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-
|
|
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