toga-ai 1.0.787 → 1.0.789
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/worker/features/netsuite-togasupply-per-client-sync.md +77 -1
- package/knowledge/2.0/apps/dbchanges2/architecture.md +42 -19
- package/knowledge/2.0/apps/dbchanges2/features/rerunnable-additive-inserts.md +14 -1
- package/knowledge/clients/nychh/profile.md +22 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-09
|
|
10
10
|
owners: ["dfranks", "bala", "jcardinal", "mhammontree", "snaredla", "rgirish"]
|
|
11
11
|
files:
|
|
12
12
|
- worker/crons/toga2/netsuite/common_sync_togasupply.php
|
|
@@ -257,6 +257,45 @@ again, the sections still could **not advance** — the value was in the **wrong
|
|
|
257
257
|
This is the same SuiteQL-vs-REST clock trap as the date-only-format bug above — see the
|
|
258
258
|
[SuiteQL/REST shim doc](../../library/features/netsuite-suiteql-rest-shim.md).
|
|
259
259
|
|
|
260
|
+
### ⚠ …and the cursor was READ on the wrong clock too — `strtotime()` (Chicago) shifted the fetch window +1h → SILENT record skips across ALL 6 sections + ALL clients (found + fixed 2026-09-09, NYCHH prod)
|
|
261
|
+
|
|
262
|
+
The deepest of the cursor-clock bugs, and the one that actually **dropped records that never came
|
|
263
|
+
back**. The two clock bugs above are on the **write** side of the cursor; this one is on the
|
|
264
|
+
**read** side of the same value, and it was silent — no crash, no alert, no frozen section. It
|
|
265
|
+
hit **every section and every client**, not just the by-reference ones.
|
|
266
|
+
|
|
267
|
+
- **Root cause — read clock ≠ fetch clock.** The cursor value `"<lastmoddate>|<netsuiteId>"` stores
|
|
268
|
+
its datetime in the NetSuite account timezone = **America/New_York (Eastern)** — the same clock the
|
|
269
|
+
list-fetch `WHERE` bounds render in (`App_Api_Netsuite_Rest::listItemFulfillments` et al. call
|
|
270
|
+
`->setTimezone('America/New_York')`). But `startModeIteration()`
|
|
271
|
+
(`common_sync_togasupply.php` ~L622) read it back with **`strtotime()`**, which parses in the
|
|
272
|
+
**script timezone America/Chicago** (set in library `_.php`). Chicago is 1h behind Eastern, so the
|
|
273
|
+
same digits produced an epoch **1 hour later**; the list fetch then re-rendered that bound in
|
|
274
|
+
Eastern, shifting the window's **lower bound +1 hour**.
|
|
275
|
+
- **Effect — a silent fetch-level skip.** Any record whose Eastern lastmod fell in that **1-hour
|
|
276
|
+
shadow** right after the previous checkpoint was **excluded from the fetch**; the window read empty,
|
|
277
|
+
and the `|0` empty-window jump advanced the cursor **past** it. Nothing threw — a throw still
|
|
278
|
+
freezes the section loudly; this is the quiet path that loses data. Combined with the giant-record
|
|
279
|
+
900s timeouts (below) that historically froze sections and were then manually stepped over, the
|
|
280
|
+
gaps compounded over time.
|
|
281
|
+
- **Proven on NYCHH prod** (item fulfillment `6138135`, lastmod **2025-09-04 17:26:58 Eastern**): the
|
|
282
|
+
+1h bound (`> 17:29:03`) returned **0** records; the correct bound (`> 16:29:03`) returned **1**
|
|
283
|
+
(that record). The cursor trace showed the `|0` empty-window jump right over its time slot.
|
|
284
|
+
- **Fix (worker only, deployed + verified):** parse the cursor as Eastern in `startModeIteration` —
|
|
285
|
+
`$epochLastIntegration = (new \DateTime($dtLastIntegration, new \DateTimeZone('America/New_York')))->getTimestamp();`
|
|
286
|
+
— and change all **6** empty-window `|0` jump writes (~L831/938/1024/1193/1281/1369) from
|
|
287
|
+
`App_Date::convertToSQLDate(...)` (renders Chicago) to
|
|
288
|
+
`App_Date::convertToTimezone($epoch, 'Y-m-d H:i:s', 'America/New_York')`, so the **whole** cursor
|
|
289
|
+
pipeline — read + per-record write + empty-window write — runs on **one** clock (Eastern). DST-safe
|
|
290
|
+
(named IANA zone). php-reviewer + cto both **AGREE**; cto flagged (and we applied) the
|
|
291
|
+
empty-window-write hardening to stop a permanent 1h backward bias building up on idle runs. Verified
|
|
292
|
+
after setting NYCHH's fulfillment cursor to `2025-09-04 00:00:00|0`: `6138135` imported.
|
|
293
|
+
- **The rule (now proven THREE times — this bug + the two write-clock bugs above):** every touch of
|
|
294
|
+
the cursor value — the **read**, the per-record **write**, and the empty-window **write** — must use
|
|
295
|
+
the NetSuite account timezone (**Eastern**), never the script's Chicago clock and never the REST
|
|
296
|
+
GET's UTC. `strtotime()` and `App_Date::convertToSQLDate()` both use the script tz (Chicago) and are
|
|
297
|
+
the trap. Any manual cursor rewind value you set by hand must also be **Eastern**.
|
|
298
|
+
|
|
260
299
|
### ⚠ Per-record isolation is SECTION-SPECIFIC — and SALES_ORDERS has NONE (corrected 2026-08-17)
|
|
261
300
|
|
|
262
301
|
Whether a bad record is "stepped over" or freezes the section depends entirely on whether **that
|
|
@@ -454,6 +493,19 @@ See the [tracking-number bridge doc](../../../2.0/apps/_underscore/features/trac
|
|
|
454
493
|
0 of their fulfillments resolve an upstream sales order). `/units` writes and all three
|
|
455
494
|
tracking-bridge models have **no** interceptor, and **DELETE fires none**.
|
|
456
495
|
|
|
496
|
+
- **The two `GET /sales-orders` calls use a narrow `fields` projection, NOT `depth` (2026-09-09).**
|
|
497
|
+
Both the sales-order lookup and the missing-SO refetch in `syncItemFulfillmentFromNetsuite`
|
|
498
|
+
(`toga2.php` ~L4644-4706) request only `uuid`,
|
|
499
|
+
`salesOrderItems.{uuid,lineNumber,quantity,c_netsuiteLineUniqueKey}`, and
|
|
500
|
+
`salesOrderItems.item.{uuid,partNumber}` — the only fields the line-matcher reads. They previously
|
|
501
|
+
sent `depth=5`, whose huge nested read (units, tracking, PO links) made api2 exceed the worker's
|
|
502
|
+
**900s cURL** limit and threw **before** the `ItemFulfillments` row was written, freezing the
|
|
503
|
+
section (`Logs.Issue` 666, `apitransaction.php:418`). SO GETs dropped 100s+ → ~1s. Same class of fix
|
|
504
|
+
as the 2026-09-08 inventory-adjustments `depth=5→4` change; a `fields` projection is the proven
|
|
505
|
+
pattern here (already used by the `/item-fulfillments` GET in this same method) and dodges the api2
|
|
506
|
+
max-depth FK omission. Keep new fields explicit in the projection — a needed field left out of the
|
|
507
|
+
list silently reads `null`, not an error.
|
|
508
|
+
|
|
457
509
|
### Single-tracking-number fan-out to items (318) and units (319) — corrected 2026-08-26
|
|
458
510
|
|
|
459
511
|
The earlier "maintains all three bridges" claim was **only true for 317 (header)**: in prod
|
|
@@ -1387,6 +1439,30 @@ library (or vice versa) crashes GroWrk and Adyen on their next sync run.
|
|
|
1387
1439
|
|
|
1388
1440
|
## Change history
|
|
1389
1441
|
|
|
1442
|
+
- 2026-09-09 — **The resume cursor was READ on the wrong clock — a silent +1h window skip dropped
|
|
1443
|
+
records across ALL 6 sections and ALL clients (worker, deployed + verified).** `startModeIteration`
|
|
1444
|
+
(`common_sync_togasupply.php` ~L622) parsed the Eastern-stored cursor with `strtotime()` (script tz
|
|
1445
|
+
America/Chicago), so the fetch's lower bound rendered **1 hour high** and any record in that 1-hour
|
|
1446
|
+
shadow was excluded; the empty window then `|0`-jumped past it — no crash, no alert. Fixed by parsing
|
|
1447
|
+
the cursor as `America/New_York` and rewriting the 6 empty-window `|0` jump writes with
|
|
1448
|
+
`App_Date::convertToTimezone(..., 'America/New_York')`, so read + per-record write + empty-window
|
|
1449
|
+
write all run on one clock (Eastern). Proven on NYCHH IF `6138135` (Eastern 2025-09-04 17:26:58): the
|
|
1450
|
+
+1h bound returned 0 records, the correct bound returned 1. php-reviewer + cto AGREE; cto's
|
|
1451
|
+
empty-window-write hardening applied. Now the THIRD cursor-clock bug — the standing rule is every
|
|
1452
|
+
touch of the cursor value uses Eastern, never Chicago (`strtotime`/`convertToSQLDate`) and never the
|
|
1453
|
+
REST GET's UTC. See the read-clock gotcha in *How it works*. (jcardinal)
|
|
1454
|
+
- 2026-09-09 — **Fulfillment sales-order GET at `depth=5` hit the worker's 900s cURL timeout and froze
|
|
1455
|
+
the section (library, deployed + verified).** Both `GET /sales-orders` calls in
|
|
1456
|
+
`App_Api_Toga2::syncItemFulfillmentFromNetsuite` (`toga2.php` ~L4644-4706 — the lookup + the
|
|
1457
|
+
missing-SO refetch) changed from `depth=5` to a narrow `fields` projection (uuid;
|
|
1458
|
+
`salesOrderItems.{uuid,lineNumber,quantity,c_netsuiteLineUniqueKey}`;
|
|
1459
|
+
`salesOrderItems.item.{uuid,partNumber}` — the only fields the matcher reads), dropping the huge
|
|
1460
|
+
nested read (units, tracking, PO links) that pushed api2 past 900s and threw before the
|
|
1461
|
+
`ItemFulfillments` row was written (`Logs.Issue` 666, `apitransaction.php:418`). Same class as the
|
|
1462
|
+
2026-09-08 inventory-adjustments depth fix; the `fields` projection is the proven pattern (already
|
|
1463
|
+
used by the `/item-fulfillments` GET in this method). Fulfillment SO GETs dropped 100s+ → ~1s. Both
|
|
1464
|
+
fixes address the NYCHH missing-fulfillment investigation; library + worker still deploy together for
|
|
1465
|
+
this sync path. (jcardinal)
|
|
1390
1466
|
- 2026-09-08 - **A `Closed` NetSuite order never reaches the transfer-order sync** - the
|
|
1391
1467
|
`status === 'Closed'` `continue` at `common_sync_togasupply.php` ~L746 sits **before** the
|
|
1392
1468
|
`isTransferOrder()` branch, so `syncTransferOrderFromNetsuite`'s `case 'Closed'` was dead code and
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-09
|
|
10
10
|
owners: [jcardinal, mhammontree, bala, ajean]
|
|
11
11
|
files:
|
|
12
12
|
- Core/
|
|
@@ -72,19 +72,21 @@ team-maintained `id`s** — ask the developer for the next value before insertin
|
|
|
72
72
|
> trailing-space quirk like `2026-06-03- BLANK_CLIENT_DATABASE.sql`). The **only** hard rule
|
|
73
73
|
> is the leading `YYYY-MM-DD<letter>` so ordering holds.
|
|
74
74
|
|
|
75
|
-
## One file per
|
|
75
|
+
## One file per realm per turn (keep change files consolidated)
|
|
76
76
|
|
|
77
|
-
**Prefer a single `.sql` file per
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
files
|
|
81
|
-
|
|
82
|
-
must
|
|
77
|
+
**Prefer a single `.sql` file per realm (folder) per turn.** A *realm* is the target database a
|
|
78
|
+
folder maps to — `Core`, the shared `Client`, a single tenant like `Client_True`, `Logs`, a module,
|
|
79
|
+
and so on. When one response produces several changes for the **same** realm — e.g. three separate
|
|
80
|
+
files under `Core/`, or four under `Client/` — **consolidate them into one dated file** for that
|
|
81
|
+
folder instead of writing several. This is a **strong preference**, not an absolute rule: multiple
|
|
82
|
+
same-realm files in one turn are allowed when they genuinely must be separate (for example, an
|
|
83
|
+
ordering gap where another folder's file must run **between** them).
|
|
83
84
|
|
|
84
|
-
- Combine same-
|
|
85
|
-
- Different
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
- Combine same-realm statements into one `YYYY-MM-DD<letter> - <Description>.sql`.
|
|
86
|
+
- Different realms (folders) stay in their own files — that split is **required** (see isolation);
|
|
87
|
+
never mix two realms in one file.
|
|
88
|
+
- The `dbchanges2-file-sprawl` hook warns when a second file for the same realm is created in a
|
|
89
|
+
turn, as a reminder to consolidate.
|
|
88
90
|
|
|
89
91
|
## Folder → database mapping
|
|
90
92
|
|
|
@@ -362,12 +364,23 @@ its own header.)
|
|
|
362
364
|
the relevant clients list `<module>` in their `_modules.txt`.
|
|
363
365
|
5. Never edit or re-date an already-applied file — add a new dated file instead. Never put new
|
|
364
366
|
work in a `HISTORIC` folder.
|
|
365
|
-
6. **Never seed a `uuid` column with MySQL `UUID()
|
|
366
|
-
**v4** UUID; `UUID()` is time/MAC-based (v1) and violates it. Since a
|
|
367
|
-
`_String::generateUuid()
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
367
|
+
6. **Never seed a `uuid` column with MySQL `UUID()` — not even in a bulk insert.** The 2.0 standard
|
|
368
|
+
requires a fully-random **v4** UUID; `UUID()` is time/MAC-based (v1) and violates it. Since a
|
|
369
|
+
migration can't call `_String::generateUuid()`:
|
|
370
|
+
- **Single-row insert:** **pre-generate a v4 UUID literal** (a real v4 generator) and hardcode
|
|
371
|
+
the literal string in the `VALUES` clause.
|
|
372
|
+
- **Multi-row / bulk `INSERT … SELECT`:** use the inline `RANDOM_BYTES()` v4 expression (see
|
|
373
|
+
`features/rerunnable-additive-inserts.md` → *Generating a real UUID4 inline*), never `UUID()`.
|
|
374
|
+
|
|
375
|
+
**Every UUID must be freshly, fully random — never derive one from another.** Do **not** take a
|
|
376
|
+
known uuid and bump a digit (`…-0001`, `…-0002`, …), copy-and-tweak, or otherwise pattern them.
|
|
377
|
+
When a file needs several literals, generate each one independently at random. (Patterned uuids
|
|
378
|
+
are guessable and defeat the uniqueness guarantee.) The one intended exception is a fan-out
|
|
379
|
+
`Client/` grant, which **reuses the same** literal per row across tenants on purpose for safe
|
|
380
|
+
rollback — that is deliberate reuse, not incrementing (see the feature doc).
|
|
381
|
+
|
|
382
|
+
One earlier migration (`Core/2026-05-08 … CronJobs_WorkerCleanup_insert`) used `UUID()` — that is
|
|
383
|
+
a known violation, not a precedent to copy. First applied correctly in
|
|
371
384
|
`Core/2026-07-21b - Insert - SprintLockScheduled CronJob.sql`.
|
|
372
385
|
7. **Guard every registration/seed insert with `NOT EXISTS` (or `INSERT IGNORE`).** Rows that
|
|
373
386
|
register platform behavior — `ApiPayloadInterceptors`, `Core.RecordFields`/`CustomRecordFields`,
|
|
@@ -562,8 +575,9 @@ Wrap the existing-set subquery in a **derived table** (with `DISTINCT` or `LIMIT
|
|
|
562
575
|
|
|
563
576
|
```sql
|
|
564
577
|
-- CORRECT — the derived table snapshots the "already present" set before any insert
|
|
578
|
+
-- (uuid: inline RANDOM_BYTES() v4 expression, never UUID() — see rule #6 / the feature doc)
|
|
565
579
|
INSERT INTO TranscriptPromptTerms (uuid, category, term, isActive, c_trueAiModelId)
|
|
566
|
-
SELECT
|
|
580
|
+
SELECT LOWER(CONCAT(HEX(RANDOM_BYTES(4)), '-', …)), t.category, t.term, t.isActive, m.mid
|
|
567
581
|
FROM <base rows> t
|
|
568
582
|
CROSS JOIN <active model ids> m
|
|
569
583
|
WHERE m.mid NOT IN (
|
|
@@ -591,6 +605,15 @@ tables that `_Model_*` classes map to.
|
|
|
591
605
|
|
|
592
606
|
## Change history
|
|
593
607
|
|
|
608
|
+
- 2026-09-09 — **Two team-wide authoring changes (jcardinal).** (1) **UUIDs must be freshly, fully
|
|
609
|
+
random — never derived from another.** Sharpened rule #6: do not bump a digit / copy-and-tweak /
|
|
610
|
+
pattern uuid literals; generate each independently at random. Reaffirmed **never `UUID()` — not
|
|
611
|
+
even in bulk** (bulk uses the `RANDOM_BYTES()` v4 expression); fixed the doc's own
|
|
612
|
+
`TranscriptPromptTerms` example, which still used `UUID()`. The fan-out `Client/` same-literal
|
|
613
|
+
reuse is the one intended exception (deliberate reuse for safe rollback, not incrementing).
|
|
614
|
+
(2) **One file per realm per turn** — renamed/strengthened the consolidation section: prefer a
|
|
615
|
+
single `.sql` file per realm (`Core`, `Client`, `Client_<Name>`, …) per turn; a strong preference,
|
|
616
|
+
not absolute; different realms still split (never mix two realms in one file). (jcardinal)
|
|
594
617
|
- 2026-08-18 - Added **rule 10**: a fan-out `Client/` file must never reference a
|
|
595
618
|
`customRecordFieldId` or a client-specific label, because `CustomRecordFields` ids are per-client
|
|
596
619
|
`AUTO_INCREMENT` - the same numeric id names a different field in every tenant and mis-wires
|
|
@@ -6,7 +6,7 @@ project: Database Changes
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-09-
|
|
9
|
+
updated: 2026-09-09
|
|
10
10
|
owners: [bala, kyalamarthi, apeterson, ajean, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- dbchanges2/Client_Compass/2026-08-26a - CompassCreativeStudioPersona.sql
|
|
@@ -52,6 +52,14 @@ and `SUBSTRING('89ab', …)` picks the variant nibble. Verified working on prod
|
|
|
52
52
|
that is *not* v4-shaped (older migrations concatenated `RANDOM_BYTES` without the version/variant
|
|
53
53
|
nibbles). They are fine and must not be "fixed" — but do not use them as the template for new SQL.
|
|
54
54
|
|
|
55
|
+
**⚠ When a file needs several hardcoded literals, generate each one independently at random — never
|
|
56
|
+
derive one from another.** Do not paste one uuid and bump a digit (`…-0001`, `…-0002`, …) or
|
|
57
|
+
copy-and-tweak to make the next. Patterned uuids are guessable and undercut the uniqueness the column
|
|
58
|
+
exists to give. Generate each literal fresh from a real v4 generator. (The **one** intended exception
|
|
59
|
+
is the fan-out rule just below, which reuses the **same** literal per row across tenants on purpose —
|
|
60
|
+
deliberate reuse for a safe rollback, not incrementing.) `UUID()` is never allowed, in single-row or
|
|
61
|
+
bulk inserts — bulk uses the `RANDOM_BYTES()` expression above.
|
|
62
|
+
|
|
55
63
|
### ⚠ In a FAN-OUT grant migration, use HARDCODED uuid literals — generated uuids make rollback unsafe
|
|
56
64
|
|
|
57
65
|
The generator above is right for a normal additive insert. It is **wrong for a `Client/` fan-out
|
|
@@ -310,6 +318,11 @@ concluding a migration is production-safe.
|
|
|
310
318
|
[FIELD_SQL calculated fields](../../_underscore/features/calculated-sql-fields.md).
|
|
311
319
|
|
|
312
320
|
## Change history
|
|
321
|
+
- 2026-09-09 — Added the rule that **every hardcoded uuid literal must be generated independently at
|
|
322
|
+
random** — never bump a digit / copy-and-tweak / pattern them (guessable, defeats uniqueness); the
|
|
323
|
+
fan-out same-literal reuse is the one intended exception. Restated **never `UUID()`, single-row or
|
|
324
|
+
bulk** (bulk uses the `RANDOM_BYTES()` v4 expression). Team-wide authoring change alongside
|
|
325
|
+
architecture rule #6. (jcardinal)
|
|
313
326
|
- 2026-09-08 — Extended the **`Core.CronJobs` registration** shape with three mechanics confirmed on
|
|
314
327
|
`Core/2026-09-08e - Insert QcBatch CronJob.sql`: reference `CronJobs` **unqualified** in a `Core/`
|
|
315
328
|
file (a `Core.` prefix is a cross-database reference that breaks in prod); **always set
|
|
@@ -18,7 +18,7 @@ project: _Underscore
|
|
|
18
18
|
client: nychh
|
|
19
19
|
type: profile
|
|
20
20
|
status: active
|
|
21
|
-
updated: 2026-09-
|
|
21
|
+
updated: 2026-09-09
|
|
22
22
|
owners: ["jcardinal", "apeterson", "bala", "akhokhani"]
|
|
23
23
|
files:
|
|
24
24
|
- dbchanges2/Client_Nychh/2026-09-02a - TransferOrderNetsuitePushInterceptor.sql
|
|
@@ -85,6 +85,27 @@ table views. Client-specific DB change-sets live in `dbchanges2/Client_Nychh/`.
|
|
|
85
85
|
`NETSUITE_LAST_SYNC_CURSOR_ITEM_RECEIPTS` is stale at 2024-08-06**, so re-enabling that feed
|
|
86
86
|
replays ~2 years. See
|
|
87
87
|
[NYCHH NetSuite → TransferOrders import](./features/netsuite-transfer-order-import.md).
|
|
88
|
+
- **Missing NetSuite records (item fulfillments + others) root-caused and fixed (2026-09-09).** Two
|
|
89
|
+
separate bugs in the 1.0 NetSuite→TOGa Supply sync starved NYCHH imports, both deployed and verified
|
|
90
|
+
in prod: (1) the resume cursor was **read on the wrong clock** (`strtotime()` in Chicago vs the
|
|
91
|
+
Eastern-stored value), shifting the fetch window +1h and **silently skipping** any record in that
|
|
92
|
+
1-hour shadow — this hit all 6 sections and all clients; and (2) the fulfillment `GET /sales-orders`
|
|
93
|
+
ran at `depth=5` and **timed out at 900s**, freezing the section before the fulfillment row was
|
|
94
|
+
written. Proven on IF `6138135` (Eastern 2025-09-04 17:26:58). Fixes and the standing "cursor is
|
|
95
|
+
always Eastern" rule live in the
|
|
96
|
+
[per-client sync feature doc](../../1.0/apps/worker/features/netsuite-togasupply-per-client-sync.md).
|
|
97
|
+
- **NYCHH sync scope + data-gap scale + backfill approach (2026-09-09).**
|
|
98
|
+
- **Scope = 22 NetSuite customers:** parent **28908** "NYC Health + Hospitals" + 21 hospital child
|
|
99
|
+
customers (`24145, 29273, 29276, 31584, 31910-31916, 31918-31925, 32229, 33674`).
|
|
100
|
+
- **Gap report (NetSuite scope vs `Client_Nychh` imported):** Item Fulfillments **2986 vs 2472**
|
|
101
|
+
(~514 missing, 17%); Sales+Transfer Orders 2121 NS vs 2098 TOGa (159 SalesOrders + 1939
|
|
102
|
+
TransferOrders — roughly complete); Invoices **2128 vs 213** (~1915 missing, 90% — but that is
|
|
103
|
+
mostly because the **invoices feed was toggled OFF on 2026-09-03**, not the timezone bug).
|
|
104
|
+
POs/Receipts/Adjustments are vendor/location-scoped (not measured) and several are toggled off.
|
|
105
|
+
- **Backfill approach:** prefer a **targeted re-import of the specific missing NetSuite ids** (diff
|
|
106
|
+
NetSuite scope vs TOGa) over a **blind cursor rewind** — a full rescan re-pulls thousands of heavy
|
|
107
|
+
serial records and stresses the shared NetSuite auth quota. Re-import is safe (matches on the
|
|
108
|
+
NetSuite internal id). Any manual cursor rewind value must be written in **Eastern**.
|
|
88
109
|
- 🚩 **OPEN, not root-caused (2026-09-08): a duplicate item INSERT aborts the whole NYCHH sales-order
|
|
89
110
|
sync run.** `Items` id 5429 (658-BFZH) was created and then re-inserted in the same run —
|
|
90
111
|
`Duplicate entry '26-658-BFZH-1' for key 'Items.manufacturerId_partNumber_catalogId'` (EV-10). The
|
package/package.json
CHANGED