toga-ai 1.0.643 → 1.0.644
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.
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
| [1.0 MVC Page Pattern & New-App Skeleton](features/mvc-page-pattern-and-app-skeleton.md) | This is the **reusable recipe for standing up a new 1.0 (`App_`) application** and for adding pages to one — the folder-based MVC routing, the page lifecycle, t | library/app/framework.php, library/app/frameworkindex.php, library/app/mvc.php, library/app/database.php, library/app/model.php, library/app/config.php |
|
|
17
17
|
| [isFulfillable from NetSuite during Item Sync (Phase 1)](features/netsuite-item-isfulfillable-sync.md) | This is the **1.0 (Phase 1)** half of the `isFulfillable` feature: reading the NetSuite `isfulfillable` flag during item sync and stamping it onto the **Agilant | library/app/netsuite.php, library/app/api/toga2.php, worker/crons/toga2/netsuite/common_sync_togasupply.php, worker/crons/toga2/netsuite/backfill_isfulfillable_jul5.php |
|
|
18
18
|
| [NetSuite SuiteQL/REST API Reference](features/netsuite-suiteql-api-reference.md) | General working reference for the Agilant NetSuite integration: how to authenticate, how SuiteQL behaves, and the confirmed schema of the tables/columns/codes w | library/app/api/netsuite/rest.php, library/ssl/netsuite_ec_key.pem, test/@dave/Junk Drawer/nsq.php |
|
|
19
|
-
| [NetSuite SuiteQL/REST Shim — Field Semantics](features/netsuite-suiteql-rest-shim.md) | `App_Api_Netsuite_Rest` is the REST/SuiteQL replacement for the deprecated NetSuite SOAP toolkit. | library/app/api/netsuite/rest.php |
|
|
19
|
+
| [NetSuite SuiteQL/REST Shim — Field Semantics](features/netsuite-suiteql-rest-shim.md) | `App_Api_Netsuite_Rest` is the REST/SuiteQL replacement for the deprecated NetSuite SOAP toolkit. | library/app/api/netsuite/rest.php, library/app/netsuite.php |
|
|
20
20
|
| [NetSuite Sync Alert Monitor (App_SystemMonitor_NetSuiteIntegration)](features/netsuite-sync-alert-monitor.md) | `App_SystemMonitor_NetSuiteIntegration` (`library/app/systemmonitor/netsuiteintegration.php`, title **"NetSuite Sync Alert"**) is a 1.0 system monitor that watc | library/app/systemmonitor/netsuiteintegration.php, worker/crons/infrastructure/system_monitors.php |
|
|
21
21
|
| [Startech PC Matic B2B Sync (library)](features/startech-pcmaticb2b-sync.md) | `library/app/api/toga2.php` handles bidirectional ticket sync for PC Matic B2B between TOGaDesk 1.0 and TOGA 2.0. | library/app/api/toga2.php, library/app/api/startechticket.php, worker/crons/toga2/startech/common_import_supporting_records.php |
|
|
22
22
|
| [App_Api_Toga2 — TOGa2 API Client & 1.0↔2.0 Sync Bridge](features/toga2-api-client-and-bridge.md) | `App_Api_Toga2` (`library/app/api/toga2.php`, ~8400 lines) is the **1.0-side client for the TOGa 2 (`_underscore`/api2) public API** *and* the home of the cross | library/app/api/toga2.php, worker/crons/toga2/aig/sync_togasupply_aig.php, worker/crons/toga2/wje/sync_togasupply_wje.php, test/@Mark/AIG/test_multi_email.php |
|
|
@@ -6,14 +6,16 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-
|
|
10
|
-
owners: [dfranks]
|
|
9
|
+
updated: 2026-08-25
|
|
10
|
+
owners: [dfranks, jcardinal]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/api/netsuite/rest.php
|
|
13
|
+
- library/app/netsuite.php
|
|
13
14
|
related:
|
|
14
15
|
- netsuite-suiteql-api-reference.md
|
|
15
16
|
- ../architecture.md
|
|
16
17
|
- ../../worker/features/forecast2-netsuite-reconciliation.md
|
|
18
|
+
- ../../../2.0/apps/_underscore/features/netsuite-rest-client.md
|
|
17
19
|
---
|
|
18
20
|
|
|
19
21
|
## Summary
|
|
@@ -90,6 +92,35 @@ Forecast2 tables already store.
|
|
|
90
92
|
- **TO_DATE rejects impossible dates** (e.g. `2026-06-31`) with an opaque HTTP 400 — validate
|
|
91
93
|
calendar dates before building SuiteQL.
|
|
92
94
|
|
|
95
|
+
## Transaction status sourcing (1.0) — and the 2026.2 REST `status.id` change
|
|
96
|
+
|
|
97
|
+
1.0 reaches NetSuite over **two transports, and neither reads status off a REST record GET
|
|
98
|
+
body**:
|
|
99
|
+
|
|
100
|
+
- **REST adapter (`App_Api_Netsuite_Rest`, `rest.php`).** It *does* issue `/record/v1/...`
|
|
101
|
+
GETs, but it sources `status` from the **SuiteQL header row** (`$h->status`, from the
|
|
102
|
+
`SELECT ... status ... FROM ...` at ~L381/574/754 — already a letter) and maps that letter
|
|
103
|
+
to the SOAP-era text label via an internal **`$statusMap`** (~L471-472, L625-626,
|
|
104
|
+
L853-854/863). The REST GET body (`$rec`) is only read for shipMethod / opportunity / terms /
|
|
105
|
+
createdFrom / country — **never status**. Downstream forecast2 crons
|
|
106
|
+
(`worker/crons/toga2/forecast2/*`) compare `$nsRecord->status` to text, but that text is
|
|
107
|
+
*adapter-produced* (from the SuiteQL letter via `$statusMap`), not from the changing REST
|
|
108
|
+
field.
|
|
109
|
+
- **SOAP (`App_NetSuite`, `library/app/netsuite.php`).** Always uses `new NetSuiteService()`
|
|
110
|
+
(SuiteTalk toolkit) and reads transaction status off the SOAP record as text (e.g.
|
|
111
|
+
`$items->status != 'Billed'/'Cancelled'/'Closed'` ~L1151/L1434; `$nsInvoice->status ==
|
|
112
|
+
'Paid In Full'` ~L2546). SOAP is a different API and is unaffected. (The many
|
|
113
|
+
`->status->isSuccess` / `statusDetail` reads elsewhere are the SOAP **response envelope**,
|
|
114
|
+
not the record's business status.)
|
|
115
|
+
|
|
116
|
+
> **NetSuite 2026.2 impact: none for 1.0.** 2026.2 standardizes the `status.id` returned by
|
|
117
|
+
> **REST record GETs** for transactions to single-letter codes across all types (some types
|
|
118
|
+
> return it as text like `"Open"` today); `status.refName` and SuiteQL are unchanged, and
|
|
119
|
+
> SOAP is a separate API. Since no 1.0 code reads `status.id` off a REST GET body, no patch is
|
|
120
|
+
> needed. NetSuite SuiteAnswers ID **89313**. The 2.0 side of the same conclusion (shared REST
|
|
121
|
+
> client + worker2 consumers) is in
|
|
122
|
+
> [`netsuite-rest-client.md`](../../../2.0/apps/_underscore/features/netsuite-rest-client.md).
|
|
123
|
+
|
|
93
124
|
## Data model
|
|
94
125
|
|
|
95
126
|
Reads NetSuite `transaction`, `transactionline`, `previoustransactionlinelink` via SuiteQL and
|
|
@@ -108,6 +139,12 @@ None — uniform across clients (NetSuite is a single shared account).
|
|
|
108
139
|
|
|
109
140
|
## Change history
|
|
110
141
|
|
|
142
|
+
- 2026-08-25 — Documented **1.0 transaction-status sourcing across both transports** and the
|
|
143
|
+
conclusion that the **NetSuite 2026.2 REST `status.id` text→letter standardization does not
|
|
144
|
+
affect 1.0** (SuiteAnswers 89313). `App_Api_Netsuite_Rest` derives status from the SuiteQL
|
|
145
|
+
letter and maps it via an internal `$statusMap` — never from the REST GET body; forecast2
|
|
146
|
+
crons see that adapter-produced text, not the changing field; `library/app/netsuite.php` is
|
|
147
|
+
pure SOAP (a different API). Discovery/impact-analysis only — no code changed. (jcardinal)
|
|
111
148
|
- 2026-06-11 — Documented bulk SuiteQL field semantics (tl.id==line, ShipItem NULL cost, iscogs
|
|
112
149
|
COGS filter, createdFrom non-determinism, foreignamountunpaid, sign conventions) surfaced while
|
|
113
150
|
moving the Forecast2 trueup tools off per-id REST GETs. (dfranks)
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
| [_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, _underscore/Model.php, _underscore/Model/Rate/Subscription.php |
|
|
31
31
|
| [_Model::save() parent FK cascade — stored-SQL-field recompute deadlocks](features/model-save-parent-cascade-stored-field-deadlock.md) | `_Model::save()` runs a **generic parent foreign-key cascade**: inserting (or saving) a child row that carries an FK to a parent causes `_Model` to **re-load an | _underscore/Model.php, _underscore/Model/Client/PurchaseOrder.php, _underscore/Model/Client/AdvanceShippingNotice.php |
|
|
32
32
|
| [_Model::save() vs raw _Query — no atomic conditional update](features/model-save-vs-query-atomic-update.md) | `_Model::save()` is a plain load-then-write ORM primitive and **cannot express an atomic conditional update** (an optimistic-concurrency / row-claim guard such | _underscore/Model.php, _underscore/Query.php, _underscore/Model/Rate/Subscription.php |
|
|
33
|
-
| [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 |
|
|
33
|
+
| [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, _underscore/Trait/Netsuite/SalesOrder.php, worker2/Worker/Netsuite/SalesOrder.php, worker2/Component/Forecast/SaleImport/SaleImport.php, worker2/Worker/Netsuite/Opportunity.php |
|
|
34
34
|
| [NetSuite Sales Order sync — ship-to address, phone, and PO reference sourcing](features/netsuite-salesorder-address-phone-sync.md) | `_Trait_Netsuite_SalesOrder` is the **shared** sales-order importer composed into **22 client models** (every client on the dbchanges2 `netsuite` module). | _underscore/Trait/Netsuite/SalesOrder.php, _underscore/Model.php, dbchanges2/_modules/netsuite/2026-08-10a - AddressPhoneNumberApiRoleAcl.sql |
|
|
35
35
|
| [Legacy page meta (Page::meta) & context-scoped ClientRecordFieldSettings](features/page-meta-context-field-settings.md) | `_Model_Core_Page::meta()` is the **legacy** page-meta resolver behind `GET /pages/meta?slug=<slug>` — still the live path for `toga2-supply` and other pre-Surf | _underscore/Model/Core/Page.php, _underscore/Model/Client/TableView.php, toga2-supply/src/components/ui/Tables/PrimaryTable/PrimaryTable.tsx |
|
|
36
36
|
| [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/Model.php, _underscore/Query.php, _underscore/ApiRequest.php, _underscore/Model/Client/Logs/Api.php, api2/Controller/Index.php |
|
|
@@ -6,14 +6,19 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-08-
|
|
9
|
+
updated: 2026-08-25
|
|
10
10
|
owners: ["dfranks", "jcardinal", "bala"]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Component/Api/Netsuite/Netsuite.php
|
|
13
|
+
- _underscore/Trait/Netsuite/SalesOrder.php
|
|
14
|
+
- worker2/Worker/Netsuite/SalesOrder.php
|
|
15
|
+
- worker2/Component/Forecast/SaleImport/SaleImport.php
|
|
16
|
+
- worker2/Worker/Netsuite/Opportunity.php
|
|
13
17
|
related:
|
|
14
18
|
- ../../worker2/features/netsuite-salesorder-open-orders-sync.md
|
|
15
19
|
- ../../worker2/features/netsuite-salesorder-outbound-push.md
|
|
16
20
|
- ../../worker2/features/netsuite-opportunity-sync.md
|
|
21
|
+
- ../../library/features/netsuite-suiteql-rest-shim.md
|
|
17
22
|
- ../architecture.md
|
|
18
23
|
---
|
|
19
24
|
|
|
@@ -167,6 +172,40 @@ rather than a bad query. Confirmed live 2026-07-28.
|
|
|
167
172
|
Surfaced building the NYCHH asset-tag backfill (see
|
|
168
173
|
[nychh-asset-tag-backfill](../../worker2/features/nychh-asset-tag-backfill.md)).
|
|
169
174
|
|
|
175
|
+
## NetSuite transaction status sourcing (2.0) — and the 2026.2 REST `status.id` change
|
|
176
|
+
|
|
177
|
+
**`fetchRecord()` returns the REST record UNTOUCHED — there is no central status
|
|
178
|
+
normalization in the shared client.** `fetchRecord()` (`Netsuite.php` ~L215-223) does
|
|
179
|
+
`send('GET', route)`, json-decodes, and hands the raw record back; it never reads or maps
|
|
180
|
+
`status`. So whether a 2.0 consumer is exposed to a change in the REST record's `status`
|
|
181
|
+
field depends entirely on *that consumer*, not on the client. Audited 2026-08-25 — every 2.0
|
|
182
|
+
consumer sources transaction status from a change-safe field:
|
|
183
|
+
|
|
184
|
+
- **`worker2/Worker/Netsuite/SalesOrder.php`** reads `status->refName` (the human-readable
|
|
185
|
+
label) and compares it to `OPEN_STATUSES` text — never `status.id`.
|
|
186
|
+
- **`worker2/Component/Forecast/SaleImport/SaleImport.php`** — `statusName()` (~L845-853) reads
|
|
187
|
+
`status->refName`, matched against `EXCLUDED_STATUSES` text. This is the shared engine that
|
|
188
|
+
Invoice / CashSale / CreditMemo / CashRefund import all delegate to.
|
|
189
|
+
- **`worker2/Worker/Netsuite/Opportunity.php`** — `fetchStatus()` (~L431-444) gets transaction
|
|
190
|
+
status via **SuiteQL** (`SELECT status FROM transaction`), which already returns the letter
|
|
191
|
+
and maps `A`/`C`/`D`. (Its `entityStatus.id` at ~L197 is a *different* field — an opaque
|
|
192
|
+
lookup key, not the transaction status.)
|
|
193
|
+
- **`_underscore/Trait/Netsuite/SalesOrder.php`** — the one text `switch ($nsOrder->status)`
|
|
194
|
+
(~L589-620) is driven by a **SOAP** `$service->get()` record, not REST.
|
|
195
|
+
- **`api2`** makes no NetSuite calls of its own — it only registers the SDK at boot and writes
|
|
196
|
+
one hardcoded outbound status literal (`PENDING_FULFILLMENT`); its `->status` reads are its
|
|
197
|
+
own HTTP response envelope. Not a NetSuite status consumer.
|
|
198
|
+
|
|
199
|
+
> **NetSuite 2026.2 impact: none for 2.0.** NetSuite 2026.2 standardizes the `status.id` field
|
|
200
|
+
> returned by **REST record GETs** for transaction records to single-letter codes (A/B/D/E/V)
|
|
201
|
+
> across all transaction types (today some types return `status.id` as text like `"Open"`);
|
|
202
|
+
> `status.refName` and SuiteQL are unchanged, and SOAP/SuiteTalk is a different API. Because no
|
|
203
|
+
> 2.0 code reads `status.id` off a REST GET body as text, no patch is needed. NetSuite
|
|
204
|
+
> SuiteAnswers ID **89313**. (Not audited: other 2.0 apps that may consume this client —
|
|
205
|
+
> `toga2-supply`, `toga25-supply`, `tools` — check them the same way if touched.) The 1.0 side
|
|
206
|
+
> of the same conclusion is in
|
|
207
|
+
> [`netsuite-suiteql-rest-shim.md`](../../library/features/netsuite-suiteql-rest-shim.md).
|
|
208
|
+
|
|
170
209
|
## Logging
|
|
171
210
|
|
|
172
211
|
`send()` calls `setLogging(false)`, so **NetSuite REST request/response bodies are NOT written to
|
|
@@ -192,6 +231,14 @@ doc.)
|
|
|
192
231
|
|
|
193
232
|
## Change history
|
|
194
233
|
|
|
234
|
+
- 2026-08-25 — Documented **2.0 NetSuite transaction-status sourcing** and the conclusion that
|
|
235
|
+
the **NetSuite 2026.2 REST `status.id` text→letter standardization does not affect 2.0**
|
|
236
|
+
(SuiteAnswers 89313). `fetchRecord()` returns the REST record raw (no central status
|
|
237
|
+
normalization), and every audited consumer reads status from `status.refName` or SuiteQL, or
|
|
238
|
+
over SOAP — none reads REST `status.id` as text: worker2 SalesOrder/SaleImport (`refName`),
|
|
239
|
+
worker2 Opportunity (`fetchStatus()` via SuiteQL), `_underscore` SalesOrder trait (SOAP),
|
|
240
|
+
api2 (no NetSuite calls). Discovery/impact-analysis only — no code changed. (jcardinal)
|
|
241
|
+
|
|
195
242
|
- 2026-08-20 — Added **idempotent creates via `externalId`**: `createRecord()` has no idempotency, so
|
|
196
243
|
stamp an `externalId` (NetSuite rejects a duplicate within the account) and read the record back
|
|
197
244
|
with `GET /record/v1/<record>/eid:<externalId>` — the non-SuiteQL fallback, which matters because
|
package/package.json
CHANGED