toga-ai 1.0.732 → 1.0.733
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.
|
@@ -530,6 +530,20 @@ Parameters are stored **per client DB** but accessed **through the TOGa2 API**,
|
|
|
530
530
|
a new line is actually created. Full root cause, the prod cleanup migration, and the
|
|
531
531
|
deploy-fix-before-cleanup sequencing live in the
|
|
532
532
|
[Compass ODP Duplicate PO-Line Cleanup](../../../clients/compass-usa/workflows/odp-duplicate-po-line-cleanup.md).
|
|
533
|
+
- **Customer-PO created from a sales order was missing `dateOrder` and had `cost` hardcoded 0 (both
|
|
534
|
+
fixed 2026-09-01, shared, `library` only).** In `syncSalesOrderFromNetsuite`
|
|
535
|
+
(`library/app/api/toga2.php`), the "create the customer PO" `POST /purchase-orders` branch
|
|
536
|
+
(~L1961) never set `dateOrder`, so every customer PO built from a sales order had
|
|
537
|
+
`PurchaseOrders.dateOrder` NULL. Fix: add
|
|
538
|
+
`'dateOrder' => App_Date::convertToTimezone($nsOrder->tranDate, 'Y-m-d', 'America/New_York')`
|
|
539
|
+
(the same expression that sets the SO's own `dateOrder` at ~L1394). Separately, all **three**
|
|
540
|
+
PO-item creation sites (`POST /purchase-order-item-sales-order-items`, ~L1863/1937/2051) sent
|
|
541
|
+
`'cost' => 0`; because this PO is the customer PO **for** the sales order, the PO line's cost is
|
|
542
|
+
the SO line's price — changed to `'cost' => $salesOrderItem['price']` (`$itemPrice`, from the
|
|
543
|
+
NetSuite rate or amount/qty fallback built ~L1223, always present). **Both are INSERT-ONLY:** the
|
|
544
|
+
create branches run only when the PO / line does not already exist and nothing PUTs `dateOrder` or
|
|
545
|
+
`cost`, so re-syncs and a second SO sharing the same customer-PO number never overwrite them.
|
|
546
|
+
**Existing NULL/`cost=0` rows are NOT backfilled** — new records only.
|
|
533
547
|
- **⚠ A single un-deletable renumbered line could freeze the entire SALES_ORDERS section (fixed
|
|
534
548
|
2026-08-17).** When NetSuite renumbers a line, `syncSalesOrderFromNetsuite` renumbers by
|
|
535
549
|
**DELETE `/sales-order-items/{uuid}` then re-create in the per-item loop**. Its "pinned row" guard
|
|
@@ -881,9 +895,43 @@ Parameters are stored **per client DB** but accessed **through the TOGa2 API**,
|
|
|
881
895
|
`'catalog' => ['name' => 'Agilant']` on the inventory-adjustment item-create. Stops all new duplicates.
|
|
882
896
|
The one-time cleanup of the accumulated ~4,300 duplicate part numbers across all 17 clients is the
|
|
883
897
|
[NetSuite duplicate-Items dedup workflow](../workflows/netsuite-duplicate-items-dedup.md).
|
|
898
|
+
- **⚠ Inventory-adjustment `/units` reconciliation missed an existing unit on a serial-CASE mismatch →
|
|
899
|
+
1062 dup-key freeze (fixed 2026-09-01, shared, `library` only).** In
|
|
900
|
+
`syncInventoryAdjustmentFromNetsuite` (`library/app/api/toga2.php` ~L5985) the PHP lookup of existing
|
|
901
|
+
units was keyed by the **returned** unit serial (`$thisLookupUnitsBySerialNumber[$unit->serialNumber]`,
|
|
902
|
+
stored `'n/a'`) but looked up with the NetSuite serial **uppercased** via `strtoupper` (`'N/A'`). **PHP
|
|
903
|
+
array keys are case-sensitive; the `Units.itemId_serialNumber` unique key is case-insensitive** — so
|
|
904
|
+
the existing unit was missed, re-INSERTed, and collided MySQL **1062** ("Duplicate entry '560-N/A'")
|
|
905
|
+
→ `EV-10` → the section aborted (fail-loud, no per-record try/catch) and froze at `1-RUNNING`. **Fix:**
|
|
906
|
+
key the lookup by `strtoupper($unit->serialNumber)` so it matches the uppercased NetSuite serial;
|
|
907
|
+
section self-heals on deploy (mode→IDLE, watermark advances). The `Items.uuid=$uuidItem` scoping was
|
|
908
|
+
left intact deliberately — copying the fulfillment "serial-only" lookup would be wrong for the shared
|
|
909
|
+
`'N/A'` placeholder serial (it would reuse another item's placeholder unit). **Durable lesson: when a
|
|
910
|
+
PHP lookup mirrors a case-insensitive DB unique key, normalize the KEY the same way you normalize the
|
|
911
|
+
probe — case-fold both, not one.**
|
|
912
|
+
- **Diagnosis playbook (durable) — localize a frozen inventory-adjustment section without NetSuite
|
|
913
|
+
access.** The 2026-09-01 NYCHH freeze above was diagnosed purely from DB logs, and item-matching was
|
|
914
|
+
*ruled out* (item 560 / partNumber B11B242201 is the only Agilant-catalog item for that part — no
|
|
915
|
+
duplicate second-catalog item, `$uuidItem` resolved correctly; the inventory-adjustment item-create
|
|
916
|
+
already stamps catalog `Agilant`). Chain: **`Client_<Id>.Parameters`** (localize the stuck section:
|
|
917
|
+
`1-RUNNING` + frozen watermark) → **`Logs.Event` filtered by `clientId`** (crash `file:line`, the
|
|
918
|
+
chain `toga2.php → common_sync_togasupply.php → sync*FromNetsuite`, plus the `transactionId`) →
|
|
919
|
+
**`Logs_<Client>.Api` on that `transactionId`** (the untruncated `EV-*`/MySQL rejection + payload).
|
|
920
|
+
A "silent freeze" here is a serial/data collision, not necessarily a catalog/item-matching problem —
|
|
921
|
+
read the untruncated Api-log rejection before assuming item matching.
|
|
884
922
|
|
|
885
923
|
## Change history
|
|
886
924
|
|
|
925
|
+
- 2026-09-01 — **Customer-PO `dateOrder`/`cost` fixes + NYCHH inventory-adjustment serial-case unfreeze
|
|
926
|
+
(shared, `library` only, working-tree — not yet committed/deployed).** `syncSalesOrderFromNetsuite`
|
|
927
|
+
now sets the customer PO's `dateOrder` from the SO `tranDate` (was always NULL) and its PO lines'
|
|
928
|
+
`cost` from the SO line `price` (was hardcoded 0) across all three PO-item create sites — both
|
|
929
|
+
INSERT-only, no backfill of existing rows. `syncInventoryAdjustmentFromNetsuite` now keys the `/units`
|
|
930
|
+
reconciliation lookup by `strtoupper($unit->serialNumber)` so a case-mismatched serial (`'n/a'` vs
|
|
931
|
+
`'N/A'`) matches the case-insensitive `Units.itemId_serialNumber` unique key — was missing the
|
|
932
|
+
existing unit and 1062-colliding, freezing NYCHH `INVENTORY_ADJUSTMENTS` at `1-RUNNING` (watermark
|
|
933
|
+
frozen 2022-12-28). Ruled out item-matching as the cause (single Agilant-catalog item, `$uuidItem`
|
|
934
|
+
correct). All three benefit every togasupply client. (jcardinal)
|
|
887
935
|
- 2026-09-01 — **TRUE-81576: stopped runaway duplicate `ShippingCarriers` creation (worker + library).**
|
|
888
936
|
`common_sync_togasupply.php` now seeds the carrier lookup from its own paged `GET /shipping-carriers`
|
|
889
937
|
loop instead of harvesting carriers out of the `/shipping-methods` loop — a carrier with zero
|
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: nychh
|
|
7
7
|
type: client-feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
9
|
+
updated: 2026-09-01
|
|
10
10
|
owners: [jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/toga2.php
|
|
@@ -134,11 +134,22 @@ Both are framework-level NetSuite facts — full detail on
|
|
|
134
134
|
**`mainline='F'` is the WRONG filter** — NetSuite sometimes marks the first real fulfilled line
|
|
135
135
|
`mainline='T'`.
|
|
136
136
|
|
|
137
|
-
##
|
|
137
|
+
## Three shared-engine fixes in `syncInventoryAdjustmentFromNetsuite` (surfaced here, affect any client)
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
All in the shared library method — see also the
|
|
140
140
|
[per-client sync](../../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md):
|
|
141
141
|
|
|
142
|
+
- **(c) Serial-CASE mismatch missed the existing unit → 1062 freeze (fixed 2026-09-01).** The `/units`
|
|
143
|
+
reconciliation lookup was keyed by the **returned** unit serial (`$unit->serialNumber`, stored `'n/a'`)
|
|
144
|
+
but probed with the NetSuite serial **uppercased** via `strtoupper` (`'N/A'`). PHP array keys are
|
|
145
|
+
case-sensitive but `Units.itemId_serialNumber` is a **case-insensitive** unique key, so the existing
|
|
146
|
+
unit (id 546, itemId 560, serial `'n/a'`) was missed and re-INSERTed → MySQL **1062**
|
|
147
|
+
("Duplicate entry '560-N/A'") → `EV-10` → section aborted (no per-record try/catch). This froze NYCHH
|
|
148
|
+
`INVENTORY_ADJUSTMENTS` at `1-RUNNING`, watermark stuck 2022-12-28 (integration was recently
|
|
149
|
+
restarted). **Fix:** key the lookup by `strtoupper($unit->serialNumber)`. The `Items.uuid=$uuidItem`
|
|
150
|
+
scoping was **kept deliberately** — copying the fulfillment "serial-only" lookup would be wrong for the
|
|
151
|
+
shared `'N/A'` placeholder serial (would reuse another item's placeholder unit). Section self-heals on
|
|
152
|
+
deploy. `library` only, working-tree, not yet committed/deployed.
|
|
142
153
|
- **(a) Prune-order FK violation.** The stale-item prune deleted an `InventoryAdjustmentItem` while
|
|
143
154
|
its child `InventoryAdjustmentItemUnits` still referenced it (FK **RESTRICT**, no cascade) → MySQL
|
|
144
155
|
**1451** → `EV-11`. **Fix:** delete the child units (`/inventory-adjustment-item-units/{uuid}`)
|
|
@@ -197,9 +208,29 @@ this feature on the scheduled cron.
|
|
|
197
208
|
- **Empty lookups drop or duplicate items.** The item loop filters to known warehouses and creates
|
|
198
209
|
items on lookup-miss, so any manual/diagnostic invocation must seed the warehouse/item/manufacturer
|
|
199
210
|
lookups (the harness does).
|
|
211
|
+
- **A frozen NYCHH `INVENTORY_ADJUSTMENTS` section is NOT necessarily an item-matching / two-catalog
|
|
212
|
+
problem (verified 2026-09-01).** When the section stuck at `1-RUNNING`, item matching was suspected
|
|
213
|
+
(wrong of two intentional catalogs). Verified in prod it was fine: item **560**
|
|
214
|
+
(uuid `4c7b93d2-…`, partNumber **B11B242201**, HYBRID) is the Agilant-catalog item and the **only**
|
|
215
|
+
item with that part number — no duplicate second-catalog item, `$uuidItem` resolved correctly, and the
|
|
216
|
+
inventory-adjustment item-create already stamps catalog `Agilant`. The freeze was purely the serial-CASE
|
|
217
|
+
bug (fix (c) above). **Diagnosis chain (no NetSuite access):** `Client_Nychh.Parameters` (localize the
|
|
218
|
+
stuck section) → `Logs.Event` `clientId=3` (crash `toga2.php:6080` via
|
|
219
|
+
`common_sync_togasupply.php:1180` `syncInventoryAdjustmentFromNetsuite`, + `transactionId`) →
|
|
220
|
+
`Logs_Nychh.Api` on that `transactionId` (untruncated `EV-10`/1062 rejection + payload).
|
|
200
221
|
|
|
201
222
|
## Change history
|
|
202
223
|
|
|
224
|
+
- 2026-09-01 — **Unfroze NYCHH `INVENTORY_ADJUSTMENTS` — serial-CASE mismatch in the `/units` lookup
|
|
225
|
+
(shared `library`, working-tree, not yet committed/deployed).** `syncInventoryAdjustmentFromNetsuite`
|
|
226
|
+
keyed the units lookup by the returned serial (`'n/a'`) but probed with the uppercased NetSuite serial
|
|
227
|
+
(`'N/A'`); PHP array keys are case-sensitive while `Units.itemId_serialNumber` is case-insensitive, so
|
|
228
|
+
the existing unit was missed, re-INSERTed, and 1062-collided → `EV-10` → section aborted at
|
|
229
|
+
`1-RUNNING` (watermark frozen 2022-12-28). Fixed by keying on `strtoupper($unit->serialNumber)`; kept
|
|
230
|
+
the `Items.uuid=$uuidItem` scoping (the shared `'N/A'` placeholder makes serial-only wrong here). Ruled
|
|
231
|
+
out item-matching / two-catalog as the cause (item 560 / B11B242201 is the only Agilant-catalog item,
|
|
232
|
+
`$uuidItem` correct). Diagnosed entirely from `Parameters` → `Logs.Event` (clientId 3) → `Logs_Nychh.Api`
|
|
233
|
+
by `transactionId`, no NetSuite access. (jcardinal)
|
|
203
234
|
- 2026-08-30 — Verified that NYCHH adjustments with a NULL `itemFulfillmentId`/`itemFulfillmentItemId`
|
|
204
235
|
are **correct, not a bug**: 0 of 27 NYCHH adjustments populate NetSuite `custbody_stock_adjustment`
|
|
205
236
|
(~1 in 40 account-wide), so most carry no link. Confirmed the shim's scalar SuiteQL read of the field
|
|
@@ -187,4 +187,9 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
187
187
|
path on any branch, and neither is in the working tree — so the transfer-order **custom-field
|
|
188
188
|
write permission** and the **stage status identifier** are, like the ACL layers above, prod/local
|
|
189
189
|
state with no file behind them. Rewrite them before anyone builds a fresh environment.
|
|
190
|
+
- 🚨 **OPEN, not investigated (found 2026-09-01): recurring api2 `1054` errors for NYCHH.**
|
|
191
|
+
`Logs.Event` **issueId 64**, `clientId 3`: *"Database Error Occurred Error #: 1054 Unknown column
|
|
192
|
+
'vendor.uuid' in 'where clause'"* on a `PurchaseOrders` GET (`_Query->getFoundRows` via
|
|
193
|
+
`V2.php processRoutePairs`), several occurrences ~2026-09-01 12:29. Flagged for a future look; no
|
|
194
|
+
change made. Likely a TableView/query join referencing `vendor.uuid` that isn't in scope.
|
|
190
195
|
- This profile is a starting point; expand as more NYCHH-specific behavior is captured.
|
package/package.json
CHANGED