toga-ai 1.0.939 → 1.0.940
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.
|
@@ -6,8 +6,8 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: ["rgirish"]
|
|
9
|
+
updated: 2026-10-05
|
|
10
|
+
owners: ["rgirish", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
13
13
|
- library/app/api/netsuite/rest.php
|
|
@@ -35,7 +35,7 @@ When the NetSuite → TOGa Supply sync drops (skips + deletes) an order (all cli
|
|
|
35
35
|
- Every other case imports: some lines open (e.g. 9 open + 1 closed), all closed but shipped/invoiced/has a PO, all open, zero item lines.
|
|
36
36
|
- **Why the related-records part:** NetSuite had 132 SOs since 2025 with every line closed but real shipments (Staples 43, GroWrk 21, ODP Veyer 14, Smoke Stacks 11, NYCDOE 5, Endeavor 5, Elite 2 — e.g. Elite SO 269622 → shipment 304666). Deleting them drops real stock movements.
|
|
37
37
|
- Replaces 2026-09-29 "Closed status + related records = keep" and 2026-09-23 "delete every Closed".
|
|
38
|
-
- Status:
|
|
38
|
+
- Status: deployed; per-client SO scope fix deployed 2026-10-05.
|
|
39
39
|
|
|
40
40
|
## How it works
|
|
41
41
|
- **Item line** = `mainline = 'F'`, `item > 0`, not ShipItem/TaxItem/TaxGroup/Discount/EndGroup/BeginGroup/Subtotal/Description/Markup/Payment. One home: `App_Api_Netsuite_Rest::SALES_ORDER_ITEM_LINE_FILTER`.
|
|
@@ -44,6 +44,7 @@ When the NetSuite → TOGa Supply sync drops (skips + deletes) an order (all cli
|
|
|
44
44
|
- `deleteClosedOrderFromNetsuite()` **KEEP guard**: re-checks live and keeps (logs `KEEP`, returns false) if `hasOpenLines` OR `hasRelatedTransactions`. Live re-check is needed because the bulk tool `delete_closed_netsuite_orders.php` (dry run unless `--execute`) passes an order with no lines.
|
|
45
45
|
- The delete removes the SalesOrder/TransferOrder with its lines, invoices, fulfillments (units, tracking bridges) and bridge rows. Records on the other side of a bridge (PO, other SO, TrackingNumber, Unit) are never deleted — only the link row. Fails loud; a half-done delete is finished by the next run.
|
|
46
46
|
- **Line field `isOpen`** on `SalesOrderItems` (after `preferredVendorId`) and `PurchaseOrderItems` (after `vendorItemId`): `TINYINT UNSIGNED NOT NULL DEFAULT 1`; model `[self::FIELD_BOOLEAN, self::FIELDOPT_DEFAULT => true]`. Core.RecordFields 2634 (record 15) / 2635 (record 18), BOOLEAN. Client ACL: API role read+write, Base role read, roles by name. The sync writes 0/1 from the NS line (`property_exists` + `(int)` change check). Old rows stay 1 until their order re-syncs.
|
|
47
|
+
- **Per-client SO scope:** private `closedOrderSalesOrderScope()` returns `[joins, extraEquals]` for `readAllForClosedOrderDelete()` — Compass `SalesOrders.locationId=615286` + `customerId=3`; Prudential `Customers` join + `Customers.uuid = CLIENT_UUID_PRUDENTIAL_CUSTOMER`; other clients unscoped. Same scope as SO sync (~L1012), invoice sync (~L3879), ~L6941.
|
|
47
48
|
- Backfill shipped orders skipped under old rules: re-run their fulfillments with `process_netsuite_records.php` ([re-processing workflow](../../worker/workflows/reprocessing-one-netsuite-order.md)).
|
|
48
49
|
|
|
49
50
|
## Gotchas
|
|
@@ -51,8 +52,14 @@ When the NetSuite → TOGa Supply sync drops (skips + deletes) an order (all cli
|
|
|
51
52
|
- **`Client/2026-09-30a` ALTER is not guarded** — a second run fails on duplicate column. The INSERTs are guarded. Deploy order + per-DB check: [verifying a migration ran](../../../2.0/apps/dbchanges2/workflows/verifying-a-migration-ran.md).
|
|
52
53
|
- **The delete reads every route in `CLOSED_ORDER_DELETE_ROUTES` (toga2.php) — the client's API role (roleId 3) needs read on ALL of them:** record permission + an expression on each logic group + field permission (at least `uuid`). One gap throws in the SO loop and **SALES_ORDERS freezes**. Find gaps with `App_Api_Toga2::checkClosedOrderDeleteAccess()` (run by `delete_closed_netsuite_orders.php`; lists every gap in one run). ACL layers: [acl-permission-chain](../../../2.0/apps/_underscore/features/acl-permission-chain.md).
|
|
53
54
|
- Compass, 2026-09-29: records 312/313 had logic groups with no expression; 257 and 254 had no `AclRecordPermissions`, then no `AclFieldPermissions` (`EZ-2` on `uuid`). Fix: `dbchanges2/Client_Compass/2026-09-29a - TransferOrderAclExpressions.sql`, then reset `NETSUITE_EXECUTION_MODE_SALES_ORDERS` to `864000-IDLE`.
|
|
55
|
+
- **⚠ Compass dual-SO trap — SO lookup by NetSuite id MUST use the per-client scope.** Compass has 2 `SalesOrders` per `c_netsuiteInternalSalesOrderId`: Office Depot order (`customerId=1`, stamped by cron 5) + NetSuite-synced order (`customerId=3`, `locationId=615286`). 2026-10-02 17:00 → 2026-10-05 SALES_ORDERS froze at `1-RUNNING` (cursor `2026-10-02 17:56:01`, `Logs.Issue` 940, throw at toga2.php:688 `send()`): closing NS SO 7566413 (TOGa 292235) also tried to delete Office Depot SO 481506511001; its fulfillment line `ItemFulfillmentItems` 335334 DELETE hit MySQL 1451 `fk_ItemFulfillmentItems_upstreamItemFulfillmentItemId` (downstream 335333 points up to it) → api2 `EV-11` → whole section aborted each run. Fixed by the scope above. 4th hit of this trap (SO sync, IF sync, invoice sync, closed-order delete) — apply the scope on every new lookup.
|
|
56
|
+
- Damage: 1 `ItemFulfillmentItemUnits` row on Office Depot fulfillment F116294 deleted 2026-10-02 16:59:53; not recoverable (no audit row for api deletes). Only other closed-order delete (2026-09-30, SO 285524 / NS 7354035) was correct.
|
|
57
|
+
- **OPEN (dev decision):** after dropping 292235, mirror fulfillments of the NS-deleted shipment remain on higher tiers — F116294 (id 91650, Office Depot SO) and 91651 (Compass customer order); Compass order may still show shipped. Not touched — never touch the Compass customer order.
|
|
58
|
+
- **OPEN low-risk:** the TransferOrder read in `deleteClosedOrderFromNetsuite()` and `retireStaleSalesOrderForTransferOrder` (~L5698) are still unscoped by NS id; for Compass they throw (fail loud), not delete.
|
|
59
|
+
- **Diagnosis tips:** `Logs_Compass.Api` has no audit for deletes; toga-db MCP rejects any SQL containing the word `DELETE` — filter `method LIKE 'DEL%'`; unbounded `LIKE` on `Api.responsePayload` over days times out — use tight `dtStamp`/`id` ranges.
|
|
54
60
|
- Adding a route to `CLOSED_ORDER_DELETE_ROUTES` = a new ACL need on every client. Run the access check fleet-wide before deploy.
|
|
55
61
|
|
|
56
62
|
## Change history
|
|
57
63
|
- 2026-09-30 — Created: Closed SO with related records kept; only Closed with none deleted. KEEP guard + Compass ACL gotcha. (rgirish)
|
|
58
64
|
- 2026-09-30 — Rule v3: header status dropped; drop only when every item line `isclosed = 'T'` AND no related records. Added line `isOpen` field. (rgirish)
|
|
65
|
+
- 2026-10-05 — Per-client SO scope on the delete lookup (`closedOrderSalesOrderScope()`) after the Compass dual-SO freeze (Issue 940). Open items: higher-tier mirror fulfillments, unscoped TO lookups. (jcardinal)
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-05
|
|
10
10
|
owners: ["dfranks", "bala", "jcardinal", "mhammontree", "snaredla", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/toga2/netsuite/common_sync_togasupply.php
|
|
@@ -1966,6 +1966,7 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
|
|
|
1966
1966
|
`customerId=3`; Prudential adds the `Customers` join + `CLIENT_UUID_PRUDENTIAL_CUSTOMER`; no other client
|
|
1967
1967
|
changed, no else/default, and the guard still throws for a genuinely-missing SO. PHP 7.2-safe
|
|
1968
1968
|
(`$options['join'] ?? []`), signature unchanged.
|
|
1969
|
+
- Same trap hit the closed-order delete 2026-10-05 (SALES_ORDERS freeze, Issue 940) — 4th path; see [closed-order rule](../../library/features/netsuite-closed-order-rule.md). Every SO-by-NS-id lookup needs this scope.
|
|
1969
1970
|
- **⚠ OPEN follow-up (own ticket): six OTHER clients also keep two SalesOrders per NetSuite id, and the
|
|
1970
1971
|
fix does NOT scope them.** A read-only sweep found the same dual-SO pattern in **Growrk (2789), Canon
|
|
1971
1972
|
(2890), Endeavorhealth (4828), Spglobal (2893), Browardsheriff (160), Masonite (17)** (Nychh/Elite/Quad/Sbasite
|
|
@@ -2078,6 +2079,7 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
|
|
|
2078
2079
|
[NetSuite Sync Alert Monitor](../../library/features/netsuite-sync-alert-monitor.md).
|
|
2079
2080
|
|
|
2080
2081
|
## Change history
|
|
2082
|
+
- 2026-10-05 — Closed-order delete now per-client SO scoped (Compass dual-SO freeze, Issue 940); details in [closed-order rule](../../library/features/netsuite-closed-order-rule.md). Library only. (jcardinal)
|
|
2081
2083
|
- 2026-10-01 — `Items.description` now from the NetSuite item record, refreshed every touch ([doc](../../library/features/netsuite-item-description-sync.md); library + worker deploy together). Gotchas: invoice billing an SO older than the SO cursor (Adyen); receipt lines never rebuilt on an existing receipt (Elite). (rgirish)
|
|
2082
2084
|
- 2026-10-01 — NYCHH diagnosis traps recorded: the wrapper is gated to **once a day** by
|
|
2083
2085
|
`cronInitialization(86400)` despite its `*/5` schedule entry, and the live cursors/modes live in
|
|
@@ -21,7 +21,7 @@ project: _Underscore
|
|
|
21
21
|
client: compass-usa
|
|
22
22
|
type: profile
|
|
23
23
|
status: active
|
|
24
|
-
updated: 2026-
|
|
24
|
+
updated: 2026-10-05
|
|
25
25
|
owners: [jcardinal, bala, tcox, apeterson, dfranks, akhokhani, ajean, rgirish]
|
|
26
26
|
files: []
|
|
27
27
|
related:
|
|
@@ -219,6 +219,7 @@ separate, related client (see its own profile).
|
|
|
219
219
|
the ⚠ rule that **deactivation is raw bulk SQL, so no model hook ever fires on it**.
|
|
220
220
|
|
|
221
221
|
## Notes
|
|
222
|
+
- **⚠ SALES_ORDERS froze 2026-10-02 → 10-05 (Issue 940): closed-order delete matched the Office Depot twin SO** (dual-SO trap). Fixed in library with the per-client scope; open: higher-tier mirror fulfillments 91650/91651. Details: [closed-order rule](../../1.0/apps/library/features/netsuite-closed-order-rule.md).
|
|
222
223
|
- **⚠ SALES_ORDERS froze 2026-09-29 on ACL gaps the closed-order delete hit** (transfer-orders 312/313, entitlement-sales-orders 257, sales-order-payments 254). Fixed by `dbchanges2/Client_Compass/2026-09-29a - TransferOrderAclExpressions.sql`, then mode reset to `864000-IDLE`. Details: [closed-order rule](../../1.0/apps/library/features/netsuite-closed-order-rule.md).
|
|
223
224
|
- **⚠ The 1.0 NetSuite sync links item fulfillments ONLY to `customerId=3` / `locationId=615286`
|
|
224
225
|
SalesOrders (2026-09-16).** This Compass-specific hardcoded scope filter lives in
|
package/package.json
CHANGED