toga-ai 1.0.234 → 1.0.235

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.
@@ -8,7 +8,7 @@
8
8
  | [Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment](features/carrier-shipping-labels.md) | Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label (UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracki | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillments/TrackingNumber.php, _underscore/Component/Library/LabelPdf/LabelPdf.php, _underscore/Component/Library/Carriers/Ups/Ups.php, _underscore/Component/Library/Carriers/Fedex/Fedex.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Component/Library/NetSuite/NetSuite.php, _underscore/Model/Client/TrackingNumber.php, _underscore/Model/Client/ShippingMethod.php, _underscore/Model.php, _underscore/Cloud.php |
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
  | [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 |
11
- | [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/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
11
+ | [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
12
  | [_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 |
13
13
  | [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 |
14
14
  | [Per-Client Database Connections & the Local Logs Trap](features/per-client-database-connections.md) | When `_underscore` serves a request for a client it opens **three distinct per-client database connections**, not one. | _underscore/Database.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php |
@@ -6,7 +6,7 @@ project: _Underscore
6
6
  client: shared
7
7
  type: feature
8
8
  status: active
9
- updated: 2026-06-26
9
+ updated: 2026-06-29
10
10
  owners: [dfranks]
11
11
  files:
12
12
  - _underscore/Component/Forecast/SaleImport/SaleImport.php
@@ -28,6 +28,7 @@ files:
28
28
  - test/@dave/test_fetchrecord_routes.php
29
29
  - test/@dave/verify_je_classification.php
30
30
  - test/@dave/probe_je_accounts.php
31
+ - test/@dave/probe_je_shape.php
31
32
  - test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js
32
33
  - test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js
33
34
  related:
@@ -161,11 +162,27 @@ location, memo). Handler `_Worker_Netsuite_JournalEntry`: `post`/`put` → `sync
161
162
  item-line upsert/reconcile machinery (`syncLines`/`guardedInsert`/`deleteRows`).
162
163
 
163
164
  **Mapping model (durable design):**
164
- - A JE line carries **no native item and no salesRep** (confirmed against the live account:
165
- 0 JEs carry an item on a revenue/cost line). The fields the business rule needs **do not
166
- exist in NetSuite yet** — they are stubbed behind constants `JE_LINE_ITEM_FIELD` /
167
- `JE_LINE_SALESREP_FIELD` (currently **null**), so until the fields are added every kept
168
- line groups under `(null, null)` into **one aggregate row**.
165
+ - **Sales rep is now LIVE on JE lines** as the custom column **`custcol_sales_rep_line`**
166
+ (`JE_LINE_SALESREP_FIELD = 'custcol_sales_rep_line'`, wired 2026-06-29 after live Apr–May
167
+ 2026 probing). The value is a NetSuite **employee REFERENCE object** `{links, id, refName}`;
168
+ `.id` is the employee internalId (a **string** in JSON). `journalLineLookup()` resolves it
169
+ via `lookupId('Employees','netsuiteInternalId', .id)` → local `salesRepEmployeeId`. The
170
+ per-run **account-number allowlist isolates exactly the lines that carry the rep** — offset/
171
+ balancing lines omit class/entity/`custcol_sales_rep_line`, so they group to null and are
172
+ dropped. No schema/migration (`salesRepEmployeeId` already on `Forecast.Sales`).
173
+ - **JE lines carry NO item field at all — item ingestion from NetSuite is not possible.**
174
+ Confirmed across multiple Apr–May 2026 JEs: the full union of line keys is
175
+ `{account, class, cleared, debit, credit, custcol_sales_rep_line, entity, line, memo, location}`.
176
+ There is **no native `item` and no custom item column**. `JE_LINE_ITEM_FIELD` stays **null
177
+ by design**; kept lines now group **per-salesRep with a null item**. The dimension that
178
+ exists *instead of* item is **`entity`** (the customer ref). Probe tool:
179
+ `test/@dave/probe_je_shape.php` (dumps line column shapes + custom columns).
180
+ - **No salesRep self-heal (unlike the item path).** `journalLineLookup()` for Employees has
181
+ **no `syncEmployee()` fallback** (the item path self-heals via
182
+ `_Worker_Netsuite_Item::syncItem()`). A rep missing from `Forecast.Employees` silently
183
+ resolves to **null** and re-aggregates under null. It relies on the **supporting-records
184
+ pull** keeping `Forecast.Employees` current. (Sample coverage was complete — rep IDs
185
+ 482, 569, 685, 30954, 34877 all present in prod `Forecast.Employees`.)
169
186
  - Lines hitting a **revenue** or **cost** GL account are grouped by **(salesRep, item)**:
170
187
  `revenue = Σ(credit − debit)` over revenue lines, `cost = Σ(debit − credit)` over cost
171
188
  lines, `profit = revenue − cost`.
@@ -411,6 +428,21 @@ record is deleted in NetSuite.)
411
428
  - The cron's sign handling is not portable here — see Sign convention.
412
429
 
413
430
  ## Change history
431
+ - 2026-06-29 — **Wired JE sales-rep ingestion live** (TRUE-79862): set
432
+ `JE_LINE_SALESREP_FIELD = 'custcol_sales_rep_line'` after live Apr–May 2026 probing
433
+ confirmed the rep is now present on JE lines as that custom column — a NetSuite employee
434
+ **reference** object `{links,id,refName}` whose `.id` (string) resolves via
435
+ `lookupId('Employees','netsuiteInternalId',.id)`. Kept lines now group per-salesRep with a
436
+ null item. One-line change, no schema/migration (`salesRepEmployeeId` already exists).
437
+ **Confirmed JE lines carry NO item field** (native or custom) — line key union is
438
+ `{account,class,cleared,debit,credit,custcol_sales_rep_line,entity,line,memo,location}`; the
439
+ dimension *instead of* item is `entity` (customer ref), so `JE_LINE_ITEM_FIELD` stays null
440
+ by design (item ingestion from NetSuite is not possible). **Gotcha: JE salesRep lookup has
441
+ no self-heal** (the item path does via `syncItem()`) — a rep missing from
442
+ `Forecast.Employees` resolves to null and aggregates under null; relies on the
443
+ supporting-records pull. Still dormant in prod (JE webhook fires only once the AMQ enqueuer
444
+ deploys on the Journal Entry record type). Probe tool: `test/@dave/probe_je_shape.php`.
445
+ (dfranks)
414
446
  - 2026-06-26 — **Changed JE revenue/cost classification from `accttype` buckets to an EXACT
415
447
  account-NUMBER allowlist** (sales-team-defined): `JE_REVENUE_ACCOUNT_NUMBERS =
416
448
  {41100,41300,41500}`, `JE_COST_ACCOUNT_NUMBERS = {51100,51200}`; everything else ignored.
@@ -16,7 +16,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
16
16
 
17
17
  ## 2.0 framework
18
18
 
19
- - **_underscore** (_Underscore) _(framework core)_ — 18 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
19
+ - **_underscore** (_Underscore) _(framework core)_ — 19 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
20
20
  - **worker2** (Worker) — 22 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)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.234",
3
+ "version": "1.0.235",
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",