toga-ai 1.0.115 → 1.0.117
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.
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [TOGa Supply (toga2-supply) Architecture](architecture.md) | `toga2-supply` is the **React + Vite frontend** for TOGa Supply — warehouse fulfillment tooling (shipment selection, fulfill & ship against carrier APIs, NetSui | toga2-supply/src/api/toga.ts, toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx |
|
|
6
6
|
| [Fulfill & Ship](features/fulfill-and-ship.md) | Fulfill & Ship lets a warehouse user select sales-order line items, enter serials, pick a carrier/method, and in one action: create the Item Fulfillment records | toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx, toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx, toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx, toga2-supply/src/pages/Shipments/api/ShipmentsApi.ts, _underscore/Model/Client/ItemFulfillment.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Component/Library/Carriers/Ups/Ups.php |
|
|
7
|
-
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-supply` (React + Vite) builds and deploys to **AWS Amplify** for non-prod environments (
|
|
7
|
+
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-supply` (React + Vite) builds and deploys to **AWS Amplify** for non-prod environments (Stage, QC Security, QC Performance, …). | toga2-supply/amplify.yml, toga2-supply/.github/workflows/sync-stage-environments.yml, toga2-supply/.env.qc-security |
|
|
@@ -10,7 +10,7 @@ updated: 2026-06-17
|
|
|
10
10
|
owners: ["jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-supply/amplify.yml
|
|
13
|
-
- toga2-supply/
|
|
13
|
+
- toga2-supply/.github/workflows/sync-stage-environments.yml
|
|
14
14
|
- toga2-supply/.env.qc-security
|
|
15
15
|
related:
|
|
16
16
|
- ../architecture.md
|
|
@@ -19,34 +19,54 @@ related:
|
|
|
19
19
|
## Summary
|
|
20
20
|
|
|
21
21
|
How `toga2-supply` (React + Vite) builds and deploys to **AWS Amplify** for non-prod
|
|
22
|
-
environments (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
environments (Stage, QC Security, QC Performance, …). The **alpha/beta/gamma/production**
|
|
23
|
+
environments historically deploy via **GitHub Actions** (`.github/workflows/*.yml`) into the
|
|
24
|
+
`agilantsolutions/toga2-supply-build` repo — Amplify is the path used for the newer non-prod
|
|
25
|
+
environments. The first Amplify build failed with a JavaScript heap OOM; this doc records the
|
|
26
|
+
fix and the chosen **branch-per-environment** deployment model.
|
|
27
|
+
|
|
28
|
+
## Deployment model — branch per environment, mirrored from `_stage`
|
|
29
|
+
|
|
30
|
+
The unit of an environment in Amplify is a **branch** (idiomatic Amplify: branch = environment).
|
|
31
|
+
A single branch cannot fan out to multiple environments, and a branch can only be connected once
|
|
32
|
+
per Amplify app. So:
|
|
33
|
+
|
|
34
|
+
- **`_stage` is the integration branch** — where work lands and is reviewed. It is **not deployed
|
|
35
|
+
directly**.
|
|
36
|
+
- Each environment is its **own deployment branch** that mirrors `_stage` exactly:
|
|
37
|
+
`stage`, `qc-security`, `qc-performance`, …
|
|
38
|
+
- **Branch name == Vite mode == `.env.<branch>` filename.** `amplify.yml` builds with
|
|
39
|
+
`--mode "$AWS_BRANCH"` (Amplify injects `$AWS_BRANCH`), so branch `qc-security` builds
|
|
40
|
+
`--mode qc-security` and loads `.env.qc-security`. No per-branch env var needed.
|
|
41
|
+
- A GitHub Action (`sync-stage-environments.yml`) **fast-forwards every environment branch to
|
|
42
|
+
`_stage` on each push**, triggering each branch's Amplify build with its own mode.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
_stage (integration, not deployed)
|
|
46
|
+
│ push → sync-stage-environments.yml fast-forwards ↓
|
|
47
|
+
├──▶ stage ──▶ Amplify ──▶ build --mode stage
|
|
48
|
+
├──▶ qc-security ──▶ Amplify ──▶ build --mode qc-security
|
|
49
|
+
└──▶ qc-performance ──▶ Amplify ──▶ build --mode qc-performance
|
|
50
|
+
```
|
|
28
51
|
|
|
29
52
|
## Steps
|
|
30
53
|
|
|
31
|
-
1. **`amplify.yml`
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
3. **Each mode needs a matching `.env.<mode>` file** committed in the repo (Vite loads
|
|
42
|
-
`.env.<mode>` for `--mode <mode>`). e.g. `.env.qc-security` provides `VITE_API`. A new
|
|
43
|
-
environment requires creating its `.env.<mode>` file.
|
|
44
|
-
4. Commit `amplify.yml` + the env file, push the branch; Amplify rebuilds.
|
|
54
|
+
1. **`amplify.yml` (repo root) drives the build.** When present it **overrides the Amplify
|
|
55
|
+
console build settings entirely** — you cannot split "memory in the file, build command in the
|
|
56
|
+
console." It sets `NODE_OPTIONS=--max-old-space-size=4096` (the OOM fix) and runs
|
|
57
|
+
`npx tsc && npx vite build --mode "$AWS_BRANCH"`, publishing `dist/`.
|
|
58
|
+
2. **In the Amplify app, connect one branch per environment** (`stage`, `qc-security`,
|
|
59
|
+
`qc-performance`, …). Each branch is its own environment with its own hosting domain.
|
|
60
|
+
3. **Each environment needs a committed `.env.<branch>` file** (Vite loads `.env.<mode>`).
|
|
61
|
+
e.g. `.env.qc-security` provides `VITE_API`.
|
|
62
|
+
4. **Add the branch to the sync matrix.** Edit `sync-stage-environments.yml` → `matrix.branch`.
|
|
63
|
+
5. Push to `_stage`; the Action mirrors the env branches; Amplify rebuilds each.
|
|
45
64
|
|
|
46
65
|
## Systems involved
|
|
47
66
|
|
|
48
|
-
- AWS Amplify (build + hosting)
|
|
49
|
-
-
|
|
67
|
+
- AWS Amplify (build + hosting) — one branch per environment, branch name selects the mode.
|
|
68
|
+
- GitHub Actions — `sync-stage-environments.yml` mirrors `_stage` → env branches on push.
|
|
69
|
+
- Vite 4 build (`vite build --mode <branch>`), TypeScript `tsc` precompile.
|
|
50
70
|
- Sentry source-map upload via `@sentry/vite-plugin` (source maps are **on** —
|
|
51
71
|
`build.sourcemap: true` in `vite.config.ts`).
|
|
52
72
|
|
|
@@ -59,18 +79,26 @@ scalable per-environment build setup.
|
|
|
59
79
|
has ≥4 GB. Amplify **Standard** compute is 4 GB (tight with source maps on). If it OOMs
|
|
60
80
|
again, switch to the **Large build instance** (Amplify → App settings → Build settings)
|
|
61
81
|
and raise to `6144`/`7168`. Setting the heap above physical RAM makes it worse, not better.
|
|
62
|
-
- **
|
|
63
|
-
|
|
64
|
-
`
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
82
|
+
- **"Can't pick `_stage` again" in the UI:** within one Amplify app a branch connects only once.
|
|
83
|
+
This is why environments are separate *branches*, not multiple connections to `_stage`. (An
|
|
84
|
+
alternative — multiple Amplify *apps* all pointed at `_stage` — works but is a non-idiomatic
|
|
85
|
+
workaround: N apps to manage, no single dashboard. Branch-per-env is preferred.)
|
|
86
|
+
- **Env branches are disposable deployment pointers — never commit to them directly.** The sync
|
|
87
|
+
Action force-updates them (`--force-with-lease`) to match `_stage`; any direct commit is lost.
|
|
88
|
+
- **Branch name must match the mode/`.env` filename.** `--mode qc-security` loads
|
|
89
|
+
`.env.qc-security` exactly — a mismatch (e.g. a stray `.env.qc-security.beta`) is silently
|
|
90
|
+
ignored: the build succeeds but ships with no `VITE_API`.
|
|
91
|
+
- **Secret hygiene:** `.env.<mode>` files have historically carried a live `SENTRY_AUTH_TOKEN`.
|
|
92
|
+
Prefer rotating it and moving it to an Amplify env var rather than committing it.
|
|
68
93
|
|
|
69
94
|
## Change history
|
|
70
95
|
|
|
96
|
+
- 2026-06-17 — Switched to branch-per-environment: `amplify.yml` now builds `--mode "$AWS_BRANCH"`
|
|
97
|
+
and added `sync-stage-environments.yml` to mirror `_stage` → env branches. (Superseded the
|
|
98
|
+
earlier `VITE_BUILD_MODE` per-branch-env-var idea — unworkable because multiple environments
|
|
99
|
+
must deploy from the single `_stage` branch.) (jcardinal)
|
|
71
100
|
- 2026-06-17 — Created on first Amplify setup of a new QC environment: added root `amplify.yml`
|
|
72
|
-
with raised Node heap
|
|
73
|
-
`.env.qc-security` (jcardinal)
|
|
101
|
+
with raised Node heap (OOM fix); renamed `.env.qc-security.beta` → `.env.qc-security`. (jcardinal)
|
|
74
102
|
|
|
75
103
|
## Related docs
|
|
76
104
|
|
|
@@ -80,6 +80,14 @@ None — uniform (platform-wide Forecast2 sync).
|
|
|
80
80
|
- **REST shape ≠ SOAP shape.** The cron reads the SOAP-shim shape; this handler reads the REST record
|
|
81
81
|
(`status->refName`, line `quantityBilled`, `class->refName`, `entity->id`, `salesRep->id`,
|
|
82
82
|
`shippingCost`). Verified against live orders via `test/@dave/probe_salesorder_rest_shape.php`.
|
|
83
|
+
- **REST OMITS `rate` for a $0 line — treat missing/non-numeric rate as `0`, don't skip the line.**
|
|
84
|
+
A $0-priced line comes through SOAP as `rate = 0` (numeric); the cron computes `revenue = 0`,
|
|
85
|
+
`profit = −qtyOpen × unitCost`, and — since profit ≠ 0 — **inserts** the row (tracking open *cost* at
|
|
86
|
+
zero revenue). REST instead **leaves `rate` out entirely**, so an early `if (!is_numeric($rate)) continue;`
|
|
87
|
+
silently drops the whole class of rows. **Prod has 575 such rows** (`revenue = 0`, profit ≠ 0), so this
|
|
88
|
+
was real data loss. Fix (2026-06-17): `$unitPrice = is_numeric($line->rate ?? null) ? (float)$line->rate : 0.0;`
|
|
89
|
+
— the genuine filter is the both-zero check (`revenue == 0 && profit == 0`), not rate presence. Caught
|
|
90
|
+
live by editing a $0 test SO (7163257 → revenue 0, profit −105).
|
|
83
91
|
- **Three deliberate departures from the legacy cron (all intentional):**
|
|
84
92
|
1. **No date-window gate.** The cron flips `isOrderOpen=false` for tranDate outside −365d/+90d
|
|
85
93
|
(`common_import_…:1753`) to bound its windowed scan. Irrelevant to a single-id webhook — dropped.
|
|
@@ -99,6 +107,18 @@ None — uniform (platform-wide Forecast2 sync).
|
|
|
99
107
|
before *every* action — keeping it would couple the REST import to SOAP config. The push methods
|
|
100
108
|
(`Create`/`Update`/`Sync`) self-construct `NetSuiteService`, so push is unaffected and the Forecast
|
|
101
109
|
DB is registered globally in `_.php`.
|
|
110
|
+
- **Local testing — the webhook path hides errors; run the action directly to see them.** A real NS
|
|
111
|
+
edit reaches the local worker via the legacy `{action,parameters}` envelope, and `_Worker_NetSuite::Webhook`
|
|
112
|
+
dispatches the per-record action through `_Worker::runTask()`, whose **debug-mode self-POST swallows the
|
|
113
|
+
sub-action result** (you get `Successfully Executed: ok` from the *router* even when the import threw).
|
|
114
|
+
To see the truth, POST the sub-action straight to the worker and read the response:
|
|
115
|
+
`curl -X POST http://worker2/ -d '{"action":"Netsuite/SalesOrder/PUT","parameters":{"internalId":<id>}}'`.
|
|
116
|
+
- **Local `Forecast.Items` is a partial/stale copy → the "unknown item" guard throws.** The importer
|
|
117
|
+
(correctly) throws `Forecast.Items has no row for NetSuite item <id>` when a line's item isn't local;
|
|
118
|
+
prod keeps Items current (hourly import) so it won't, but local testing of an arbitrary SO often hits
|
|
119
|
+
this. Pre-seed the missing item locally (copy the row from the prod reader) or test that SO against prod.
|
|
120
|
+
- **Local worker2 writes to the DB its config points at — not prod.** With `[database] hostname = localhost`,
|
|
121
|
+
rows land in **local** `Forecast.OpenOrderItems`; verify there, not on the prod reader.
|
|
102
122
|
|
|
103
123
|
## Related work
|
|
104
124
|
|
|
@@ -111,6 +131,11 @@ and are a candidate to extract into a shared `_Component_Forecast_Db` before the
|
|
|
111
131
|
|
|
112
132
|
## Change history
|
|
113
133
|
|
|
134
|
+
- 2026-06-17 — **Fixed dropped zero-revenue/open-cost lines.** REST omits `rate` for $0 lines, and the
|
|
135
|
+
`if (!is_numeric($rate)) continue;` guard was skipping them — but the cron inserts these (profit =
|
|
136
|
+
−qtyOpen × unitCost; 575 such rows in prod). Now defaults missing/non-numeric rate to 0 so the both-zero
|
|
137
|
+
filter alone decides. Verified e2e from a real NS edit: 7162297 → $3,698, 7163257 → revenue 0/profit −105.
|
|
138
|
+
Added local-testing gotchas (webhook debug-swallow; stale local Items guard; local-DB write target). (dfranks)
|
|
114
139
|
- 2026-06-17 — Initial open-orders importer merged into `_Worker_Netsuite_SalesOrder` (TRUE-79142):
|
|
115
140
|
REST-only, no date-window gate, no shipping line, cascade bug fixed. (dfranks)
|
|
116
141
|
|
package/package.json
CHANGED