toga-ai 1.0.273 → 1.0.274

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.
@@ -9,7 +9,7 @@
9
9
  | [Client Email Template Sending](features/email-template-sending.md) | `_Model_Client_EmailTemplate` sends a stored, client-defined email template by UUID. | _underscore/Model/Client/EmailTemplate.php, _underscore/Model/Client/EmailTemplateOutgoingEmailAddress.php, _underscore/Email.php |
10
10
  | [Error Reporting — Issue/Event Aggregation (exceptionHandler)](features/error-reporting-issue-event.md) | `_underscore`'s global exception handler persists every uncaught exception into a two-table **Issue / Event** model in the **shared Core Logs DB** (`_underscore | _underscore/Error.php, _underscore/Model/Core/Logs/Issue.php, _underscore/Model/Core/Logs/Event.php, dbchanges2/Logs/2026-07-06 - Issue and Event tables.sql |
11
11
  | [Record-Changed Event Publishing (_Event::publish to SQS)](features/event-publish-sqs.md) | `_Event::publish()` (in `_underscore/Event.php`) is the PHP side of the real-time event pipeline. | _underscore/Event.php |
12
- | [Forecast.Sales NetSuite import engine (real-time webhook)](features/forecast-sale-import.md) | Real-time importer that takes a NetSuite **sale** record and writes its lines into `Forecast.Sales` (the Forecast2 revenue table). | _underscore/Component/Forecast/SaleImport/SaleImport.php, _underscore/Component/Forecast/Db/Db.php, _underscore/Component/Api/Netsuite/Netsuite.php, worker2/Worker/Netsuite/Invoice.php, worker2/Worker/Netsuite/CashSale.php, worker2/Worker/Netsuite/CreditMemo.php, worker2/Worker/Netsuite/CashRefund.php, worker2/Worker/Netsuite/JournalEntry.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Forecast/2026-06-26a - Add journalEntry to Sales transaction type enum.sql, test/@dave/test_invoice_lifecycle.php, test/@dave/test_je_lifecycle.php, test/@dave/test_creditmemo_lifecycle.php, test/@dave/test_cashsale_lifecycle.php, test/@dave/test_cashrefund_lifecycle.php, test/@dave/test_fetchrecord_routes.php, test/@dave/verify_je_classification.php, test/@dave/probe_je_accounts.php, test/@dave/probe_je_shape.php, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
12
+ | [Forecast.Sales NetSuite import engine (real-time webhook)](features/forecast-sale-import.md) | Real-time importer that takes a NetSuite **sale** record and writes its lines into `Forecast.Sales` (the Forecast2 revenue table). | _underscore/Component/Forecast/SaleImport/SaleImport.php, _underscore/Component/Forecast/Db/Db.php, _underscore/Component/Api/Netsuite/Netsuite.php, worker2/Worker/Netsuite/Invoice.php, worker2/Worker/Netsuite/CashSale.php, worker2/Worker/Netsuite/CreditMemo.php, worker2/Worker/Netsuite/CashRefund.php, worker2/Worker/Netsuite/JournalEntry.php, worker2/Worker/Netsuite/Opportunity.php, worker2/Worker/Netsuite/SalesOrder.php, dbchanges2/Forecast/2026-06-26a - Add journalEntry to Sales transaction type enum.sql, test/@dave/test_invoice_lifecycle.php, test/@dave/test_je_lifecycle.php, test/@dave/test_creditmemo_lifecycle.php, test/@dave/test_cashsale_lifecycle.php, test/@dave/test_cashrefund_lifecycle.php, test/@dave/test_fetchrecord_routes.php, test/@dave/verify_je_classification.php, test/@dave/probe_je_accounts.php, test/@dave/probe_je_shape.php, test/@dave/fixer.php, test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
13
13
  | [Item-Fulfillment Stage Lifecycle (picked/packed/shipped) & Order Status](features/item-fulfillment-stage-lifecycle-and-order-status.md) | Every ItemFulfillment (IF) now carries an explicit **stage** — picked → packed → shipped — resolved through `ItemFulfillmentStages → ItemFulfillmentStatuses` (m | _underscore/Model/Client/SalesOrder.php, _underscore/Model/Quad/SalesOrder.php, _underscore/Model/Compass/SalesOrder.php, _underscore/Model/Compass/SalesOrderStatus.php, _underscore/Model/Client/SalesOrderItem.php, _underscore/Model/Client/Item.php, _underscore/Model/Client/PurchaseOrderItem.php, library/app/api/toga2.php, dbchanges2/Client/2026-06-30a - BackfillNullStageItemFulfillmentsToShipped.sql, dbchanges2/Client/2026-06-30b - SalesOrderStatusesPickedPacked.sql, dbchanges2/Client/2026-06-30c - ItemFulfillmentStageIdNotNull.sql, dbchanges2/Client_CompassCanada/2026-06-30a - ItemFulfillmentLifecycleAndShippedBackfill.sql |
14
14
  | [_Model magic-field access (__get without __isset)](features/model-magic-field-access.md) | `_Model` exposes DB columns as "magic" properties via `__get()`, but it defines **no** `__isset()`. | _underscore/Model/Core/Model.php |
15
15
  | [NetSuite REST Client (_Component_Api_Netsuite) — record writes & SuiteQL](features/netsuite-rest-client.md) | `_Component_Api_Netsuite` is the **2.0 `_underscore` NetSuite REST client** — the shared primitive every worker2/api2 NetSuite caller uses for record GETs, Suit | _underscore/Component/Api/Netsuite/Netsuite.php |
@@ -6,7 +6,7 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-30
9
+ updated: 2026-07-07
10
10
  owners: [dfranks]
11
11
  files:
12
12
  - _underscore/Component/Forecast/SaleImport/SaleImport.php
@@ -29,6 +29,7 @@ files:
29
29
  - test/@dave/verify_je_classification.php
30
30
  - test/@dave/probe_je_accounts.php
31
31
  - test/@dave/probe_je_shape.php
32
+ - test/@dave/fixer.php
32
33
  - test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js
33
34
  - test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js
34
35
  related:
@@ -184,12 +185,21 @@ item-line upsert/reconcile machinery (`syncLines`/`guardedInsert`/`deleteRows`).
184
185
  per-run **account-number allowlist isolates exactly the lines that carry the rep** — offset/
185
186
  balancing lines omit class/entity/`custcol_sales_rep_line`, so they group to null and are
186
187
  dropped. No schema/migration (`salesRepEmployeeId` already on `Forecast.Sales`).
187
- - **JE lines carry NO item field at all — item ingestion from NetSuite is not possible.**
188
- Confirmed across multiple Apr–May 2026 JEs: the full union of line keys is
189
- `{account, class, cleared, debit, credit, custcol_sales_rep_line, entity, line, memo, location}`.
190
- There is **no native `item` and no custom item column**. `JE_LINE_ITEM_FIELD` stays **null
191
- by design**; kept lines now group **per-salesRep with a null item**. The dimension that
192
- exists *instead of* item is **`entity`** (the customer ref). Probe tool:
188
+ - **JE lines carry their item in a CUSTOM column `custcol_item_sku` — NOT the native item
189
+ field (corrected 2026-07-07, TRUE-79973).** The **native** `transactionline.item` /
190
+ REST `line.item` is **empty on every JE rev/cost line** (live probe 2026-07-07: empty on
191
+ all 12,293 2026 JE revenue/cost lines) — which is why earlier probing concluded "no item
192
+ column exists." The SKU actually lives **line-level** on each rev/cost GL line in
193
+ **`custcol_item_sku`**, shaped **identically to `custcol_sales_rep_line`**: a NetSuite
194
+ **reference object** `line.custcol_item_sku = {id, refName, links}` where `.id` (a JSON
195
+ string) is the item **internalId** (e.g. `"1103457"`). It maps to Forecast via
196
+ `lookupId('Items','netsuiteItemInternalId', .id)`. In SuiteQL it is `tl.custcol_item_sku`
197
+ returning the raw internal id — **filter/group with `> 0`, never `IS NOT NULL`** (same
198
+ ~15x IS-NOT-NULL perf trap as `custcol_sales_rep_line`; see the SuiteQL custom-column doc).
199
+ This is the plug-in point for `JE_LINE_ITEM_FIELD` (previously null "no item column
200
+ exists"). Consequence: the earlier TRUE-79968/79970 design that grouped every kept JE line
201
+ under `itemId=NULL` is now **wrong** — kept lines carry a real item. The `entity`
202
+ (customer ref) dimension still exists in addition to item. Probe tool:
193
203
  `test/@dave/probe_je_shape.php` (dumps line column shapes + custom columns).
194
204
  - **No salesRep self-heal (unlike the item path).** `journalLineLookup()` for Employees has
195
205
  **no `syncEmployee()` fallback** (the item path self-heals via
@@ -442,6 +452,20 @@ record is deleted in NetSuite.)
442
452
  - The cron's sign handling is not portable here — see Sign convention.
443
453
 
444
454
  ## Change history
455
+ - 2026-07-07 — **CORRECTION: JE lines DO carry an item — in the custom column
456
+ `custcol_item_sku`, not the native item field** (TRUE-79973, dfranks). Live probe
457
+ 2026-07-07 confirmed native `transactionline.item` / REST `line.item` is empty on **all
458
+ 12,293** 2026 JE rev/cost lines, while the SKU is populated **line-level** on each rev/cost
459
+ GL line in `custcol_item_sku` as a reference object `{id, refName, links}` (`.id` = item
460
+ internalId, e.g. `"1103457"`), identical in shape to `custcol_sales_rep_line`. Maps via
461
+ `lookupId('Items','netsuiteItemInternalId', .id)`; SuiteQL exposes `tl.custcol_item_sku`
462
+ (raw id) — filter/group with `> 0`, not `IS NOT NULL` (same perf trap). This is the plug-in
463
+ point for `JE_LINE_ITEM_FIELD` (previously null "no item column exists") and **overturns**
464
+ the earlier TRUE-79968/79970 assumption that JE lines carry no item and group under
465
+ `itemId=NULL`. Touch-points for the backfill/import work:
466
+ `_underscore/Component/Forecast/SaleImport/SaleImport.php` (`JE_LINE_ITEM_FIELD`,
467
+ `journalLineLookup`, `buildJournalEntryRows`) and `test/@dave/fixer.php` (JE line SuiteQL).
468
+ (dfranks)
445
469
  - 2026-06-30 — **JE import now keeps ONLY rep-bearing lines** (TRUE-79968, dfranks).
446
470
  `buildJournalEntryRows` skips any GL line whose **raw** `custcol_sales_rep_line` is empty,
447
471
  applied **per-line before** the (salesRep,item) bucketing: a no-rep JE → 0 Sales rows, a
@@ -17,7 +17,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
17
17
  ## 2.0 framework
18
18
 
19
19
  - **_underscore** (_Underscore) _(framework core)_ — 22 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
20
- - **worker2** (Worker) — 26 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
20
+ - **worker2** (Worker) — 27 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
21
21
  - **api2** (API) — 7 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
22
22
  - **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
23
23
  - **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.273",
3
+ "version": "1.0.274",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",