toga-ai 1.0.219 → 1.0.220
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.
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
| [Assortment Name Translation (AssortmentTranslations sidecar)](features/assortment-name-translation.md) | Serves Assortment (product-grouping) **names** in multiple languages by adding a per-language **sidecar** table `AssortmentTranslations`, reusing the platform's | _underscore/Model/Client/AssortmentTranslation.php, dbchanges2/Client/2026-06-26a - AssortmentTranslations.sql, dbchanges2/Core/2026-06-26a - AssortmentTranslationsRecord.sql, dbchanges2/Client/2026-06-26b - AssortmentTranslationsAcl.sql |
|
|
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
|
-
| [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/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js, test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js |
|
|
10
|
+
| [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
11
|
| [_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 |
|
|
12
12
|
| [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 |
|
|
13
13
|
| [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 |
|
|
@@ -26,6 +26,8 @@ files:
|
|
|
26
26
|
- test/@dave/test_cashsale_lifecycle.php
|
|
27
27
|
- test/@dave/test_cashrefund_lifecycle.php
|
|
28
28
|
- test/@dave/test_fetchrecord_routes.php
|
|
29
|
+
- test/@dave/verify_je_classification.php
|
|
30
|
+
- test/@dave/probe_je_accounts.php
|
|
29
31
|
- test/@dave/NetSuite/api-message-queue/ue_api_msg_queue_enqueue.js
|
|
30
32
|
- test/@dave/NetSuite/api-message-queue/dev_ue_api_msg_queue_enqueue.js
|
|
31
33
|
related:
|
|
@@ -167,11 +169,25 @@ item-line upsert/reconcile machinery (`syncLines`/`guardedInsert`/`deleteRows`).
|
|
|
167
169
|
- Lines hitting a **revenue** or **cost** GL account are grouped by **(salesRep, item)**:
|
|
168
170
|
`revenue = Σ(credit − debit)` over revenue lines, `cost = Σ(debit − credit)` over cost
|
|
169
171
|
lines, `profit = revenue − cost`.
|
|
170
|
-
- **Account classification is
|
|
171
|
-
|
|
172
|
-
`
|
|
173
|
-
`
|
|
174
|
-
|
|
172
|
+
- **Account classification is an EXACT account-NUMBER allowlist** (sales-team-defined; this
|
|
173
|
+
*replaced* the old provisional `accttype`-bucket rule). Constants in
|
|
174
|
+
`_Component_Forecast_SaleImport`: `JE_REVENUE_ACCOUNT_NUMBERS = ['41100','41300','41500']`,
|
|
175
|
+
`JE_COST_ACCOUNT_NUMBERS = ['51100','51200']`. Any line whose account number is in neither
|
|
176
|
+
list is **ignored**. The helper `accountNumbers(accountIds)` (which **replaced**
|
|
177
|
+
`accountTypes()`) resolves each line account's `acctnumber` via SuiteQL
|
|
178
|
+
`SELECT id, acctnumber FROM account WHERE id IN (...)`, cached per run; matching is
|
|
179
|
+
**string membership of `acctnumber`**, not `accttype`.
|
|
180
|
+
- **Sub-accounts are EXCLUDED by design — exact match, never prefix.** This chart of accounts
|
|
181
|
+
has real traps that both a `accttype` rule AND a prefix match would mis-bucket: under **41300**
|
|
182
|
+
"Service Revenue" sit `41300.01` (Service Revenue-Agent, `accttype` Income), `41300.02`/`.03`
|
|
183
|
+
(Service Cost-Agent, `accttype` **Bank**), and `41300.04` (Service **Costs**-Agent, `accttype`
|
|
184
|
+
**Income** despite the cost name); **51200** has `51200.01` (COGS Service-Agent). Exact-number
|
|
185
|
+
matching sidesteps all of them.
|
|
186
|
+
- **Resolved account map** (number → internalId / name): `41100→245` Product Revenue,
|
|
187
|
+
`41300→339` Service Revenue, `41500→355` Pre-Sales Consulting; `51100→248` COGS Product,
|
|
188
|
+
`51200→249` COGS Service. Read-only resolver tool: `test/@dave/probe_je_accounts.php`.
|
|
189
|
+
- **Likely future one-line tweak:** if the Agent sub-accounts should count, add `41300.01` to
|
|
190
|
+
the revenue list and `51200.01` to the cost list.
|
|
175
191
|
- Group key uses `json_encode([salesRep, item])` (not a string-join) to stay collision-safe
|
|
176
192
|
once the fields go live — an empty-string separator would let `"5|" + null` collide.
|
|
177
193
|
|
|
@@ -281,6 +297,12 @@ live `_Component_Api_Netsuite::createRecord()` `#`-delimiter bug (still unfixed;
|
|
|
281
297
|
5. **delete** the record.
|
|
282
298
|
6. **verify** the local row is gone (`removeAll`).
|
|
283
299
|
|
|
300
|
+
> **Scope of the update step (what it does and does NOT cover):** the update step changes
|
|
301
|
+
> **only `tranDate`** — a tracked column, so it forces a real change-detection UPDATE while
|
|
302
|
+
> the amount stays unchanged. It does **NOT** exercise updates to revenue / customer /
|
|
303
|
+
> salesRep / item, nor line add/remove. A future tester verifying those must extend the
|
|
304
|
+
> harness; the current update-step assertion only proves the tracked-column UPDATE path.
|
|
305
|
+
|
|
284
306
|
Poll the **local** Forecast DB between steps:
|
|
285
307
|
`C:\xampp8\mysql\bin\mysql.exe -u root` → db **`forecast`**, table **`Sales`** (filter on the
|
|
286
308
|
test internalId, or customerId `2` / itemId `25`).
|
|
@@ -301,9 +323,16 @@ test internalId, or customerId `2` / itemId `25`).
|
|
|
301
323
|
- Item **103741** ("Test Other Charge for Sale") → local `forecast.Items` id **25**.
|
|
302
324
|
- Location **5** ("Main") — required as a **header-level** field on creditMemo/cashSale/cashRefund
|
|
303
325
|
and **line-level** on invoice.
|
|
304
|
-
- **JE fixture:** subsidiary **1**; accounts **245** (Product Revenue
|
|
305
|
-
(COGS Product
|
|
306
|
-
`reversalDate` so NetSuite auto-creates the reversal.
|
|
326
|
+
- **JE fixture:** subsidiary **1**; accounts **245** (Product Revenue, acctnumber **41100**),
|
|
327
|
+
**248** (COGS Product, **51100**), **215** (Clearing - CSS — ignored, neither revenue nor
|
|
328
|
+
cost); set a `reversalDate` so NetSuite auto-creates the reversal. Classification is now by
|
|
329
|
+
**account number** (allowlist), so any clearing/balance line lands outside both lists and is
|
|
330
|
+
ignored automatically.
|
|
331
|
+
- **Discriminating classification test — `test/@dave/verify_je_classification.php`:** posts one
|
|
332
|
+
balanced JE `+10/+20/+30` to `41100/41300/41500`, `−5/−7` to `51100/51200`, **plus `+100` to
|
|
333
|
+
the excluded sub-account `41300.01`**, plus a clearing balance line → local `Forecast.Sales`
|
|
334
|
+
aggregates to **revenue=60.00, profit=48.00** (NOT 160 — proving `41300.01` is excluded and the
|
|
335
|
+
exact-number rule is in force).
|
|
307
336
|
|
|
308
337
|
### Expected local revenue signs / row outcomes
|
|
309
338
|
- **invoice** → `+` (a **$0** invoice still writes **one** row, revenue `0.00` — the engine does
|
|
@@ -382,6 +411,19 @@ record is deleted in NetSuite.)
|
|
|
382
411
|
- The cron's sign handling is not portable here — see Sign convention.
|
|
383
412
|
|
|
384
413
|
## Change history
|
|
414
|
+
- 2026-06-26 — **Changed JE revenue/cost classification from `accttype` buckets to an EXACT
|
|
415
|
+
account-NUMBER allowlist** (sales-team-defined): `JE_REVENUE_ACCOUNT_NUMBERS =
|
|
416
|
+
{41100,41300,41500}`, `JE_COST_ACCOUNT_NUMBERS = {51100,51200}`; everything else ignored.
|
|
417
|
+
`accountNumbers()` (replaced `accountTypes()`) resolves each account's `acctnumber` via
|
|
418
|
+
SuiteQL, cached per run; match is exact-string, **never prefix** — sub-accounts are excluded
|
|
419
|
+
by design to dodge real chart-of-accounts traps (`41300.04` "Service Costs-Agent" is
|
|
420
|
+
`accttype` Income; `41300.02/.03` are `accttype` Bank). Resolved map: 41100→245, 41300→339,
|
|
421
|
+
41500→355, 51100→248, 51200→249. Verified with a discriminating balanced-JE test
|
|
422
|
+
(`test/@dave/verify_je_classification.php` → revenue=60.00/profit=48.00, proving `41300.01`
|
|
423
|
+
excluded); read-only resolver `test/@dave/probe_je_accounts.php`. Future one-liner: add
|
|
424
|
+
`41300.01`/`51200.01` if Agent sub-accounts should count. Also clarified the e2e harness
|
|
425
|
+
update step exercises **only `tranDate`** (a tracked column) — not revenue/customer/salesRep/
|
|
426
|
+
item or line add/remove. (dfranks)
|
|
385
427
|
- 2026-06-26 — **Documented the full e2e testing playbook + harness inventory to run cold**
|
|
386
428
|
(6-step create→verify→update→verify→delete→verify lifecycle; per-type harness action sets;
|
|
387
429
|
the two execution modes — WEBHOOK vs LOCAL-ONLY `sync` — fixtures, expected signs, prod
|
package/package.json
CHANGED