toga-ai 1.0.752 → 1.0.754
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.
- package/knowledge/1.0/apps/library/features/netsuite-suiteql-rest-shim.md +22 -1
- package/knowledge/1.0/apps/library/features/toga2-api-client-and-bridge.md +8 -3
- package/knowledge/1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md +91 -17
- package/knowledge/clients/elite/features/netsuite-togasupply-sync.md +13 -3
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-02
|
|
10
10
|
owners: [dfranks, jcardinal, kyalamarthi]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/netsuite/rest.php
|
|
@@ -98,6 +98,21 @@ Forecast2 tables already store.
|
|
|
98
98
|
- **TO_DATE rejects impossible dates** (e.g. `2026-06-31`) with an opaque HTTP 400 — validate
|
|
99
99
|
calendar dates before building SuiteQL.
|
|
100
100
|
|
|
101
|
+
- **⚠ SuiteQL returns date/datetime columns in the account DISPLAY format (here `M/D/YYYY`,
|
|
102
|
+
date-only) unless you wrap them in `TO_CHAR` — losing the time is silent (2026-09-02).** A bare
|
|
103
|
+
`SELECT lastmodifieddate` came back `9/2/2026` (no time, locale-dependent order), while
|
|
104
|
+
`TO_CHAR(lastmodifieddate,'YYYY-MM-DD HH24:MI:SS')` gave `2026-09-02 17:39:21`. Any shim that
|
|
105
|
+
**stores or compares a NetSuite date STRING** must `TO_CHAR(col,'YYYY-MM-DD HH24:MI:SS')` it, or it
|
|
106
|
+
drops the time and inherits the account's locale format. This defeated the per-client sync's
|
|
107
|
+
per-record resume cursor: it stored `9/2/2026|<id>`, sat at midnight, and re-scanned the whole day
|
|
108
|
+
every run — fixed by `TO_CHAR`-ing `lastmodifieddate AS lastmodifieddate` in the 8 cursor-feeding
|
|
109
|
+
SELECTs (`listSalesOrders`, `fetchSalesOrderByIdFull`, `listSales`, `listPurchaseOrders`,
|
|
110
|
+
`fetchPurchaseOrderById`, `listItemReceipts`, `listItemFulfillments`, `listInventoryAdjustments`).
|
|
111
|
+
`WHERE`/`ORDER BY` are unaffected by the alias — bound via `TO_TIMESTAMP` — so they still use the base
|
|
112
|
+
column. **Still latent:** `listOpportunities` and `listItems` return raw date-only `lastmodifieddate`
|
|
113
|
+
into their shims; harmless only because neither feeds a resume cursor yet. Engine-side detail on the
|
|
114
|
+
[per-client-sync doc](../../worker/features/netsuite-togasupply-per-client-sync.md).
|
|
115
|
+
|
|
101
116
|
- **The line SuiteQL must SELECT the line location or it is always NULL — the shim reads it but
|
|
102
117
|
didn't select it.** `listSalesOrders()`'s line query now selects `tl.location` and
|
|
103
118
|
`BUILTIN.DF(tl.location) AS location_name`. The line-shim assembly code already *read* those two
|
|
@@ -275,6 +290,12 @@ None — uniform across clients (NetSuite is a single shared account).
|
|
|
275
290
|
|
|
276
291
|
## Change history
|
|
277
292
|
|
|
293
|
+
- 2026-09-02 — Recorded that **SuiteQL returns date columns in the account DISPLAY format (date-only
|
|
294
|
+
`M/D/YYYY` here) unless wrapped in `TO_CHAR`** — a bare `lastmodifieddate` silently drops the time and
|
|
295
|
+
is locale-dependent. This defeated the per-client sync's per-record resume cursor (stored `9/2/2026|id`,
|
|
296
|
+
sat at midnight, re-scanned the whole day); fixed by `TO_CHAR(...,'YYYY-MM-DD HH24:MI:SS') AS
|
|
297
|
+
lastmodifieddate` in the 8 cursor-feeding SELECTs. `listOpportunities`/`listItems` still return raw
|
|
298
|
+
date-only but are latent (no cursor). (jcardinal)
|
|
278
299
|
- 2026-09-01 — Recorded that **`listPurchaseOrders()` scoped POs by the VENDOR header `entity`** (via
|
|
279
300
|
the reused `buildEntityInFilter()`), silently dropping every drop-ship / special-order PO for all
|
|
280
301
|
togasupply clients since the SOAP→REST migration. A PO header `entity` is the vendor, not the
|
|
@@ -418,9 +418,14 @@ enable flags** and an optional `$monitorTogadeskDepartmentIds[]`:
|
|
|
418
418
|
still had a RESTRICT child (item receipt `ItemReceiptItems.purchaseOrderItemId`, or an SO/TO link).
|
|
419
419
|
Fixed to **unlink first, then delete**: delete the four PO bridge routes, unhook item receipts with
|
|
420
420
|
`PUT /item-receipt-items/{uuid}` `['purchaseOrderItem' => null]`, then DELETE the PO line kept
|
|
421
|
-
**throwing** as the fail-loud backstop — mirroring the SALES_ORDERS cleanup.
|
|
422
|
-
|
|
423
|
-
`ItemReceiptItems.purchaseOrderItemId`).
|
|
421
|
+
**throwing** as the fail-loud backstop — mirroring the SALES_ORDERS cleanup. **Now committed to
|
|
422
|
+
`_production` and confirmed live on Elite** (carried a schema deploy dependency: nullable
|
|
423
|
+
`ItemReceiptItems.purchaseOrderItemId`). **Follow-up fix same day:** the bridge-clear step's **GET**
|
|
424
|
+
was also a throwing `send()` — a client without an ACL grant on the transfer-order bridge routes
|
|
425
|
+
(Elite, Core records 329/330) got **403 EZ-2** and re-froze; the bridge/link GET loop now passes
|
|
426
|
+
`$throwExceptionOnApiError=false` and **skips** a link table on a no-access response (403 OR EZ-2),
|
|
427
|
+
while any other failure still throws. (Ops note: merging `library` to `_production` does not deploy
|
|
428
|
+
the worker boxes — a worker deploy does.) Full detail, the mirror-NetSuite decision, and the api2
|
|
424
429
|
bridge-route-key mechanic (kebab→camel, no pluralization) live on
|
|
425
430
|
[NetSuite → TOGa Supply per-client sync](../../worker/features/netsuite-togasupply-per-client-sync.md)
|
|
426
431
|
(2026-09-02). (jcardinal)
|
|
@@ -122,6 +122,35 @@ rename migration run on every client DB first (see the deploy-order gotcha below
|
|
|
122
122
|
the same guarded `UPDATE ... WHERE key LIKE 'NETSUITE_LAST_SYNC_DATETIME_%'` so future new clients land on
|
|
123
123
|
CURSOR keys. Still uncommitted in the working tree.
|
|
124
124
|
|
|
125
|
+
### ⚠ The cursor was storing a date-ONLY, US-format value — the same-second tiebreak was dead (FIXED + DEPLOYED 2026-09-02)
|
|
126
|
+
|
|
127
|
+
The per-record cursor "advanced" but its datetime half was `M/D/YYYY` with **no time** (e.g.
|
|
128
|
+
`9/2/2026|6302323`), so it defeated its own same-second id tiebreak: the cursor sat at **midnight**,
|
|
129
|
+
which no record's real timestamp equals, so every run re-fetched the **whole day** — the resume was
|
|
130
|
+
defeated even while it looked healthy. Root cause: the SuiteQL SELECTs in
|
|
131
|
+
`library/app/api/netsuite/rest.php` returned `lastmodifieddate` **raw**, and NetSuite hands that column
|
|
132
|
+
back in the account **display** format (here `M/D/YYYY`, date-only). Proven: `SELECT lastmodifieddate`
|
|
133
|
+
→ `9/2/2026`, but `TO_CHAR(lastmodifieddate,'YYYY-MM-DD HH24:MI:SS')` → `2026-09-02 17:39:21`. See the
|
|
134
|
+
[SuiteQL/REST shim doc](../../library/features/netsuite-suiteql-rest-shim.md) for the general rule.
|
|
135
|
+
|
|
136
|
+
- **Fix part 1 (`rest.php`):** wrapped the select as
|
|
137
|
+
`TO_CHAR(lastmodifieddate, 'YYYY-MM-DD HH24:MI:SS') AS lastmodifieddate` in the **8** SuiteQL SELECTs
|
|
138
|
+
that feed a cursor shim — `listSalesOrders`, `fetchSalesOrderByIdFull`, `listSales`,
|
|
139
|
+
`listPurchaseOrders`, `fetchPurchaseOrderById`, `listItemReceipts`, `listItemFulfillments`,
|
|
140
|
+
`listInventoryAdjustments`. `WHERE`/`ORDER BY` still use the **base** `lastmodifieddate` column (the
|
|
141
|
+
alias doesn't affect them) — the query bounds were already correct ISO via
|
|
142
|
+
`TO_TIMESTAMP('YYYY-MM-DD HH24:MI:SS')`.
|
|
143
|
+
- **Fix part 2 (`common_sync_togasupply.php`):** normalized the datetime half at the **6** per-record
|
|
144
|
+
cursor PUTs to canonical `Y-m-d H:i:s` via `strtotime()` + `date()` (falls back to the raw string if
|
|
145
|
+
`strtotime` fails). The empty-window `"<windowTop>|0"` PUT was already canonical
|
|
146
|
+
(`App_Date::convertToSQLDate`).
|
|
147
|
+
- **Old date-only cursors self-heal.** `strtotime('9/2/2026')` parses to midnight (one-time re-scan of
|
|
148
|
+
that day), then the next successful record overwrites with the full timestamp.
|
|
149
|
+
- **LATENT (noted, not fixed):** `listOpportunities` and `listItems` in `rest.php` also return raw
|
|
150
|
+
date-only `lastmodifieddate` into their shims — harmless today because neither feeds a resume cursor,
|
|
151
|
+
but the same trap if either ever becomes one.
|
|
152
|
+
- Deployed 2026-09-02 (worker + library re-deploy; both files had changed).
|
|
153
|
+
|
|
125
154
|
### ⚠ Per-record isolation is SECTION-SPECIFIC — and SALES_ORDERS has NONE (corrected 2026-08-17)
|
|
126
155
|
|
|
127
156
|
Whether a bad record is "stepped over" or freezes the section depends entirely on whether **that
|
|
@@ -649,8 +678,8 @@ the code deploy).
|
|
|
649
678
|
— for the throwing order.
|
|
650
679
|
|
|
651
680
|
- **⚠ The PURCHASE_ORDERS cleanup DELETE froze the whole section when NetSuite drops a PO line that
|
|
652
|
-
still has an item receipt or SO/TO link (fixed 2026-09-02, `library` only —
|
|
653
|
-
|
|
681
|
+
still has an item receipt or SO/TO link (fixed + DEPLOYED 2026-09-02, `library` only — committed to
|
|
682
|
+
`_production`, confirmed live on Elite).** Same freeze class as the SALES_ORDERS renumber-DELETE above, on a
|
|
654
683
|
different path. `syncPurchaseOrderFromNetsuite`'s cleanup loop ("remove remaining items if
|
|
655
684
|
updating", `library/app/api/toga2.php`) deleted a leftover Toga PO line with a **bare, throwing**
|
|
656
685
|
`send()` DELETE `/purchase-order-items/{uuid}`. When the dropped line still had an item receipt
|
|
@@ -681,6 +710,26 @@ the code deploy).
|
|
|
681
710
|
`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` (section 7, guarded/re-runnable).
|
|
682
711
|
**Confirm that migration has run on every client DB before deploying the code fix**, or the PUT
|
|
683
712
|
`purchaseOrderItem = null` fails.
|
|
713
|
+
- **⚠ Follow-up (deployed same day): the bridge-CLEAR GET must TOLERATE a 403 for a client that
|
|
714
|
+
lacks the bridge (transfer-order routes).** After the first fix deployed, Elite hit a NEW freeze.
|
|
715
|
+
The cleanup **GETs** each of the four bridge routes to find rows to remove, and that GET was a
|
|
716
|
+
**throwing** `send()`. Elite has **no ACL grant** on the transfer-order bridge Core records (**329**
|
|
717
|
+
`purchase-order-items-transfer-order-items`, **330** `transfer-order-items-purchase-order-items`)
|
|
718
|
+
because Elite does not use transfer orders, so `GET /purchase-order-items-transfer-order-items`
|
|
719
|
+
returned **403 EZ-2** ("no permission to read the specified fields", roleIds [1,3]) → the whole
|
|
720
|
+
section aborted, watermark still frozen at 2025-10-13. **Fix:** the bridge/link GET loop now calls
|
|
721
|
+
`App_Api_Toga2::send(..., $throwExceptionOnApiError = false)` and, on a **no-access** response
|
|
722
|
+
(status **403** OR error code **EZ-2**), **skips that link table** — no access means the client
|
|
723
|
+
cannot have rows there, so there is nothing to clear. **Any other failure still throws** (fail
|
|
724
|
+
loud). Covers both transfer-order bridge routes. Confirmed: Elite's PO import unfroze after
|
|
725
|
+
redeploy.
|
|
726
|
+
- **Confirming the new cleanup code is actually live (from `Logs_<Client>.Api`).** A run on the
|
|
727
|
+
**NEW** code shows, before the PO-line delete: `GET /purchase-order-item-sales-order-items`,
|
|
728
|
+
`GET /sales-order-item-purchase-order-items`, `GET /purchase-order-items-transfer-order-items`
|
|
729
|
+
(may be **403 and skipped**), `GET /item-receipt-items`, then `PUT /item-receipt-items/{uuid}`
|
|
730
|
+
(the unhook). The **OLD** code goes straight from the purchase-order PUT to a bare
|
|
731
|
+
`DELETE /purchase-order-items/{uuid}` with none of those. This call pattern is the ground truth
|
|
732
|
+
for whether the worker box is running the fix.
|
|
684
733
|
- **api2 route mechanics (verified against `api2/Component/Api/V2/V2.php`).** For these list/bridge
|
|
685
734
|
routes the response collection key is a **plain kebab→camel conversion of the route, no
|
|
686
735
|
pluralization** (`/purchase-order-item-sales-order-items` → `purchaseOrderItemSalesOrderItems`).
|
|
@@ -695,6 +744,14 @@ the code deploy).
|
|
|
695
744
|
"move the receipt to the surviving line" is **unsafe** — double-count / `UNIQUE(itemReceiptId,
|
|
696
745
|
purchaseOrderItemId)` collision. Only clean cases self-heal; tangled ones must throw for a human.
|
|
697
746
|
|
|
747
|
+
- **⚠ Merging `library` to `_production` on GitHub does NOT update the running worker EC2 boxes — a
|
|
748
|
+
worker DEPLOY does.** After a `library` merge to `_production`, the worker instances keep running the
|
|
749
|
+
**OLD** `library` code across cron cycles until an actual worker deploy pushes it onto the boxes. On
|
|
750
|
+
2026-09-02 the PO-cleanup fix merged at 14:48 but the worker ran old code for multiple 5-min cron
|
|
751
|
+
cycles (the api2 log still showed the old bare DELETE, none of the new calls) until it was deployed.
|
|
752
|
+
**Before concluding a `library` fix "didn't work," confirm the worker box is actually running it** —
|
|
753
|
+
the `Logs_<Client>.Api` call pattern is the ground truth (see the PO-cleanup call-pattern note above).
|
|
754
|
+
|
|
698
755
|
- **⚠ Inventory-adjustment sync: two shared-engine bugs + it is still SOAP (2026-08-28).** Both fixed
|
|
699
756
|
in `syncInventoryAdjustmentFromNetsuite` (`library/app/api/toga2.php`) and reachable by any client
|
|
700
757
|
running inventory adjustments: (a) the stale-item prune deleted an `InventoryAdjustmentItem` before
|
|
@@ -1067,6 +1124,14 @@ the code deploy).
|
|
|
1067
1124
|
|
|
1068
1125
|
## Change history
|
|
1069
1126
|
|
|
1127
|
+
- 2026-09-02 — **Per-record cursor was storing a date-ONLY, US-format value — FIXED + DEPLOYED (shared:
|
|
1128
|
+
`worker` + `library`).** The datetime half was `M/D/YYYY` with no time (e.g. `9/2/2026|6302323`), so the
|
|
1129
|
+
cursor sat at midnight and the same-second id tiebreak never matched — every run re-fetched the whole day.
|
|
1130
|
+
Root cause: `rest.php` SELECTed `lastmodifieddate` raw and NetSuite returns it in the account display format
|
|
1131
|
+
(date-only here). Fixed by `TO_CHAR(lastmodifieddate,'YYYY-MM-DD HH24:MI:SS') AS lastmodifieddate` in the 8
|
|
1132
|
+
cursor-feeding SuiteQL SELECTs, plus normalizing the 6 per-record cursor PUTs to canonical `Y-m-d H:i:s`
|
|
1133
|
+
(`strtotime`+`date`, raw fallback). Old cursors self-heal (midnight → one re-scan → full timestamp).
|
|
1134
|
+
`listOpportunities`/`listItems` share the raw-date trap but are latent (no cursor). (jcardinal)
|
|
1070
1135
|
- 2026-09-02 — **Per-record resume cursor DEPLOYED to prod (shared: `worker` + `library` + `dbchanges2`).**
|
|
1071
1136
|
Now live: worker commit `06e8f9ef` + library commit `3a70b59c` (tips of `_production`); rename migration
|
|
1072
1137
|
`Client/2026-09-02b - NetsuiteSyncCursorRename.sql` run on all client DBs **before** the code. Recorded the
|
|
@@ -1100,21 +1165,30 @@ the code deploy).
|
|
|
1100
1165
|
1048 "quantityAdjustBy cannot be null" on NetSuite serialized-detail sub-lines. The null-skip reached prod
|
|
1101
1166
|
~2026-09-01 afternoon; Issue 542 stopped 09-01 14:08. Recorded that `3-IDLE`-crawl = the same poison-record
|
|
1102
1167
|
shrink as `1-RUNNING`, not a slow section. (jcardinal)
|
|
1103
|
-
- 2026-09-02 — **PURCHASE_ORDERS cleanup DELETE freeze fixed
|
|
1104
|
-
|
|
1105
|
-
cleanup deleted a NetSuite-dropped PO line
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
poison PO re-tried every 5 min). Fix
|
|
1109
|
-
routes, unhook item receipts via
|
|
1110
|
-
DELETE the PO line kept **throwing**
|
|
1111
|
-
NetSuite — remove our line + its links but
|
|
1112
|
-
log-and-skip (a log-and-skip guard was tried this
|
|
1113
|
-
dependency:** `ItemReceiptItems.purchaseOrderItemId`
|
|
1114
|
-
(`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7) on every client
|
|
1115
|
-
Recorded the api2 bridge-route key mechanic (plain kebab→camel, no pluralization; PUT writes
|
|
1116
|
-
NULL) and Elite's 78 duplicate PO line-groups (4 with receipts, all tangled → must fail loud).
|
|
1117
|
-
|
|
1168
|
+
- 2026-09-02 — **PURCHASE_ORDERS cleanup DELETE freeze fixed + DEPLOYED, plus a follow-up 403 fix
|
|
1169
|
+
(shared, `library` only — committed to `_production`, confirmed live on Elite).**
|
|
1170
|
+
`syncPurchaseOrderFromNetsuite`'s "remove dropped items" cleanup deleted a NetSuite-dropped PO line
|
|
1171
|
+
with a bare throwing DELETE; a leftover item receipt (`ItemReceiptItems.purchaseOrderItemId`,
|
|
1172
|
+
RESTRICT) or SO/TO link → 1451 → EV-11 → threw → froze the whole PURCHASE_ORDERS section at
|
|
1173
|
+
`1-RUNNING` (Elite watermark stuck 2025-10-13, window ÷3 to 1s, poison PO re-tried every 5 min). Fix
|
|
1174
|
+
mirrors the SALES_ORDERS cleanup: unlink all four PO bridge routes, unhook item receipts via
|
|
1175
|
+
`PUT /item-receipt-items/{uuid}` `purchaseOrderItem = null`, then DELETE the PO line kept **throwing**
|
|
1176
|
+
as the fail-loud backstop. **Decision:** the sync mirrors NetSuite — remove our line + its links but
|
|
1177
|
+
keep the real records on the other side; never log-and-skip (a log-and-skip guard was tried this
|
|
1178
|
+
session and rejected as data damage). **Deploy dependency:** `ItemReceiptItems.purchaseOrderItemId`
|
|
1179
|
+
must be nullable (`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7) on every client
|
|
1180
|
+
DB first. Recorded the api2 bridge-route key mechanic (plain kebab→camel, no pluralization; PUT writes
|
|
1181
|
+
SQL NULL) and Elite's 78 duplicate PO line-groups (4 with receipts, all tangled → must fail loud).
|
|
1182
|
+
**Follow-up after deploy:** the cleanup's **bridge-clear GET** was itself a throwing `send()`, and a
|
|
1183
|
+
client without an ACL grant on the transfer-order bridge routes (Elite — no transfer orders — Core
|
|
1184
|
+
records 329/330) got **403 EZ-2** on `GET /purchase-order-items-transfer-order-items` and re-froze;
|
|
1185
|
+
the bridge/link GET loop now passes `$throwExceptionOnApiError=false` and **skips** a link table on a
|
|
1186
|
+
no-access response (403 OR EZ-2), while any other failure still throws. Elite PO import unfroze after
|
|
1187
|
+
redeploy. **Two ops lessons:** (1) merging `library` to `_production` does NOT deploy the worker EC2
|
|
1188
|
+
boxes — only a worker deploy does (old code ran for cron cycles after the 14:48 merge); (2) the
|
|
1189
|
+
`Logs_<Client>.Api` call pattern (bridge GETs + item-receipt unhook PUT vs. a bare PO-line DELETE) is
|
|
1190
|
+
the ground truth for whether the fix is live. No prod data was modified (read-only SELECTs + NetSuite
|
|
1191
|
+
reads). (jcardinal)
|
|
1118
1192
|
- 2026-09-01 — **Customer-PO `dateOrder`/`cost` fixes + NYCHH inventory-adjustment serial-case unfreeze
|
|
1119
1193
|
(shared, `library` only, working-tree — not yet committed/deployed).** `syncSalesOrderFromNetsuite`
|
|
1120
1194
|
now sets the customer PO's `dateOrder` from the SO `tranDate` (was always NULL) and its PO lines'
|
|
@@ -126,8 +126,8 @@ the trait declares must exist as a column, a `CustomRecordFields` row **and** an
|
|
|
126
126
|
## Change history
|
|
127
127
|
|
|
128
128
|
- 2026-09-02 — **Elite PURCHASE_ORDERS froze on a dropped PO line that still had a receipt** (shared
|
|
129
|
-
fix, `library` only —
|
|
130
|
-
case: `NETSUITE_EXECUTION_MODE_PURCHASE_ORDERS='1-RUNNING'`, watermark frozen at **2025-10-13**,
|
|
129
|
+
fix, `library` only — now DEPLOYED to `_production`, PO import confirmed unfrozen on Elite). Elite was
|
|
130
|
+
the confirmed prod case: `NETSUITE_EXECUTION_MODE_PURCHASE_ORDERS='1-RUNNING'`, watermark frozen at **2025-10-13**,
|
|
131
131
|
window collapsed to 1s, same poison PO re-tried every 5 min. Root cause is shared, not Elite —
|
|
132
132
|
`syncPurchaseOrderFromNetsuite`'s cleanup DELETE threw on a leftover item receipt / SO link. Elite
|
|
133
133
|
data behind it: **78 duplicate `(purchaseOrder, vendorItem)` PO line-groups**, only **4** carry a
|
|
@@ -138,7 +138,17 @@ the trait declares must exist as a column, a `CustomRecordFields` row **and** an
|
|
|
138
138
|
(double-count / `UNIQUE(itemReceiptId, purchaseOrderItemId)` collision) — tangled cases fail loud
|
|
139
139
|
for a human, only clean cases self-heal. **Before deploy:** confirm
|
|
140
140
|
`ItemReceiptItems.purchaseOrderItemId` is nullable on `Client_Elite`
|
|
141
|
-
(`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7).
|
|
141
|
+
(`dbchanges2/Client/2026-06-19a - TransferOrderSchemaFixes.sql` §7). **Follow-up after deploy:** a
|
|
142
|
+
second freeze appeared — the cleanup's **bridge-clear GET** was a throwing `send()`, and Elite has
|
|
143
|
+
**no ACL grant** on the transfer-order bridge Core records (**329**
|
|
144
|
+
`purchase-order-items-transfer-order-items`, **330** `transfer-order-items-purchase-order-items`)
|
|
145
|
+
because Elite doesn't use transfer orders, so `GET /purchase-order-items-transfer-order-items`
|
|
146
|
+
returned **403 EZ-2** (roleIds [1,3]) and aborted the section. Fixed by making the bridge GET loop
|
|
147
|
+
tolerate a no-access response (`$throwExceptionOnApiError=false`, skip that link table on 403/EZ-2,
|
|
148
|
+
else throw); Elite unfroze after redeploy. Note: merging `library` to `_production` did **not**
|
|
149
|
+
deploy the worker boxes — an actual worker deploy was required, and the `Logs_Elite.Api` call
|
|
150
|
+
pattern (bridge GETs + item-receipt unhook PUT vs. a bare PO-line DELETE) is how you confirm the fix
|
|
151
|
+
is live. Full mechanics on the
|
|
142
152
|
[engine doc](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md). (jcardinal)
|
|
143
153
|
- 2026-09-01 — **Drop-ship / special-order POs were never importing for Elite** (e.g. PO tranid
|
|
144
154
|
128238). Root cause was in shared code, not Elite: `listPurchaseOrders()` filtered the PO header by
|
package/package.json
CHANGED