toga-ai 1.0.133 → 1.0.135
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.
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -1
- package/knowledge/2.0/apps/_underscore/features/carrier-shipping-labels.md +57 -22
- package/knowledge/2.0/apps/api2/workflows/codepipeline-codeconnections-deploy.md +24 -2
- package/knowledge/2.0/apps/toga2-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga2-supply/features/fulfill-and-ship.md +30 -9
- package/knowledge/2.0/apps/worker2/features/netsuite-opportunity-sync.md +27 -2
- package/package.json +1 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [_underscore Framework Architecture](architecture.md) | `_underscore` is the shared PHP backend framework for **all 2.0 applications**. | _underscore/_underscore.php, _underscore/Loader.php, _underscore/Framework.php, _underscore/Model.php, _underscore/Database.php, _underscore/Query.php, _underscore/Route.php, _underscore/Component.php |
|
|
6
|
-
| [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/Carriers/Ups/Ups.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 |
|
|
6
|
+
| [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 |
|
|
7
7
|
| [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 |
|
|
8
8
|
| [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 |
|
|
9
9
|
| [Recursive Item Fulfillments (upstream mirroring)](features/recursive-item-fulfillments.md) | In a multi-tier supply chain a sales order (SO) spawns a purchase order (PO) that becomes another SO downstream, and so on. | _underscore/Model/Client/ItemFulfillment.php, _underscore/Model/Client/ItemFulfillmentItem.php, _underscore/Model/Client/ItemFulfillmentItemUnit.php, _underscore/Model/Client/ItemFulfillmentPackage.php, _underscore/Model/Compass/AdvanceShippingNotice.php, dbchanges2/Core/2026-02-13 - 75601 - RecursiveItemFulfillmentCreation.sql, dbchanges2/Core/2026-06-04 - RecursiveItemFulfillmentPut.sql |
|
|
@@ -6,12 +6,14 @@ project: _Underscore
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-18
|
|
10
10
|
owners: [mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- _underscore/Model/Client/ItemFulfillment.php
|
|
13
13
|
- _underscore/Model/Client/ItemFulfillments/TrackingNumber.php
|
|
14
|
+
- _underscore/Component/Library/LabelPdf/LabelPdf.php
|
|
14
15
|
- _underscore/Component/Library/Carriers/Ups/Ups.php
|
|
16
|
+
- _underscore/Component/Library/Carriers/Fedex/Fedex.php
|
|
15
17
|
- _underscore/Trait/Netsuite/ItemFulfillment.php
|
|
16
18
|
- _underscore/Trait/Netsuite/SalesOrder.php
|
|
17
19
|
- _underscore/Component/Library/NetSuite/NetSuite.php
|
|
@@ -29,21 +31,26 @@ related:
|
|
|
29
31
|
|
|
30
32
|
Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label
|
|
31
33
|
(UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracking
|
|
32
|
-
number and label attached.
|
|
33
|
-
|
|
34
|
+
number and label attached. As of 2026-06 the label is requested + stored as a raw
|
|
35
|
+
**PNG** and the printable PDF is **generated on demand** via `_Component_Library_LabelPdf`
|
|
36
|
+
(FPDF) — superseding the old Labelary ZPL→PDF approach.
|
|
34
37
|
|
|
35
38
|
## Key files / entry points
|
|
36
39
|
|
|
37
40
|
- `upsShipmentApi` / `fedexShipmentApi` (scripted APIs on
|
|
38
|
-
`Model/Client/ItemFulfillment.php`): build the carrier request
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
`{ error, trackingNumber }`. UPS path
|
|
42
|
-
fast-fails on an empty service code (it previously
|
|
43
|
-
|
|
44
|
-
`createNetsuiteItemFulfillment`'s package
|
|
41
|
+
`Model/Client/ItemFulfillment.php`): build the carrier request **requesting a PNG label**,
|
|
42
|
+
store the raw PNG on `TrackingNumbers.labelPdfFile`, generate a printable PDF via
|
|
43
|
+
`_Component_Library_LabelPdf`, save that PDF to the NetSuite File Cabinet, and return
|
|
44
|
+
`{ trackingNumber, pdfLabel, fileInternalId }` or `{ error, trackingNumber }`. UPS path
|
|
45
|
+
checks `$shipmentResponse->success` and fast-fails on an empty service code (it previously
|
|
46
|
+
swallowed UPS errors → misleading "Failed to save PDF to NetSuite"). These methods (plus
|
|
47
|
+
`fulfill` and `createNetsuiteItemFulfillment`'s package query) resolve the tracking record by
|
|
45
48
|
joining **`ItemFulfillments_TrackingNumbers`** (the bridge); they previously joined the
|
|
46
49
|
dropped `ItemFulfillmentPackages` table — see Gotchas.
|
|
50
|
+
- `_Component_Library_LabelPdf::buildFromPngLabels($labels)`
|
|
51
|
+
(`Component/Library/LabelPdf/LabelPdf.php`): builds a multi-page 4x6 PDF from raw PNG label
|
|
52
|
+
images (one per page, optional "Return Label" caption) via FPDF; returns the PDF as a binary
|
|
53
|
+
string. Built to take N pages so a combined shipping + return label is one print-once PDF.
|
|
47
54
|
- `_Component_Library_Carriers_Ups::submitShipmentRequest`
|
|
48
55
|
(`Component/Library/Carriers/Ups/Ups.php`): returns `{ success, errorMessage,
|
|
49
56
|
trackingNumber, encodedLabel }`.
|
|
@@ -58,21 +65,24 @@ decision** (dev lead Jeff, June 2026) that supersedes what the code currently do
|
|
|
58
65
|
(`_Model_Growrk_ItemFulfillment extends _Model_Client_ItemFulfillment`), not the base
|
|
59
66
|
model.
|
|
60
67
|
|
|
61
|
-
## Label-storage architecture (
|
|
68
|
+
## Label-storage architecture (implemented 2026-06; dev lead Jeff)
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
The label is **stored as the raw carrier PNG** (not a Labelary PDF) and the printable PDF is
|
|
71
|
+
**generated on demand** server-side:
|
|
64
72
|
|
|
65
|
-
1. **
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
73
|
+
1. **Carrier returns PNG.** UPS via `[ups] label_format = "PNG"` config (the client's
|
|
74
|
+
`getLabelFormat()` reads it; defaults ZPL); FedEx via `$shipmentRequest->labelImageType =
|
|
75
|
+
'PNG'` + `labelStockType = 'PAPER_4X6'`. Labelary is gone.
|
|
76
|
+
2. **Store the raw PNG** on `TrackingNumbers.labelPdfFile` (a `FIELD_STORAGE` field; the name is
|
|
77
|
+
retained though it now holds a PNG) via the model/S3 mechanism — never touch S3 by hand.
|
|
78
|
+
3. **Generate the PDF on demand** with `_Component_Library_LabelPdf::buildFromPngLabels([...])`
|
|
79
|
+
— FPDF, one label image per 4x6 page, optional "Return Label" caption — for the immediate
|
|
80
|
+
display + the NetSuite File Cabinet copy.
|
|
71
81
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
82
|
+
Jeff approved **PNG over GIF**: a PDF lib embeds PNG with **no extra extension** (FPDF parses
|
|
83
|
+
non-alpha PNG in pure PHP — no GD/Imagick). Remaining wiring: the reprint/return-label combine
|
|
84
|
+
(frontend calls a backend generate endpoint instead of the client-side `pdf-lib` merge), and
|
|
85
|
+
the return-label generation itself.
|
|
76
86
|
|
|
77
87
|
## `FIELD_STORAGE` mechanics (reference)
|
|
78
88
|
|
|
@@ -119,8 +129,33 @@ attempted.
|
|
|
119
129
|
bridge-linked `TrackingNumbers` row shows `number = NULL` after a "successful" ship. Backend
|
|
120
130
|
change — only effective once deployed to beta. Worth grepping `ItemFulfillmentPackage::TABLE`
|
|
121
131
|
across `_underscore`/`api2`/`library` for the same staleness elsewhere.
|
|
132
|
+
- **FPDF is a Composer dep, and `_Loader` throws on it.** `setasign/fpdf` lives in api2's
|
|
133
|
+
`vendor/` (not an `_underscore` Component library), so the framework autoloader throws
|
|
134
|
+
"could not find source file for 'FPDF'". `LabelPdf` wraps `new \FPDF()` with
|
|
135
|
+
`_Loader::setThrowExceptionInAutoloaderIfClassNotFound(false)` (restored after) so the chain
|
|
136
|
+
falls through to Composer's autoloader — same pattern as `Model/Client/TableView.php`.
|
|
137
|
+
- **Commit `composer.lock`.** EB runs `composer install` (installs strictly from the lock). If
|
|
138
|
+
`composer.json` declares `setasign/fpdf` but the lock wasn't committed, the dep never installs
|
|
139
|
+
→ runtime `Class "FPDF" not found`. Verify `vendor/setasign/fpdf/` exists after deploy.
|
|
140
|
+
- **FedEx tracking-cred config fallback.** `_Config::fedex('tracking_client_id')` **throws** when
|
|
141
|
+
the key is missing; the intended `?:` fallback to the ship `client_id`/`client_secret` only
|
|
142
|
+
works with the no-throw second arg: `_Config::fedex('tracking_client_id', false) ?: …` (same as
|
|
143
|
+
the UPS client). Without it, FedEx shipping dies with "Configuration parameter
|
|
144
|
+
'tracking_client_id' … has not been defined".
|
|
145
|
+
- **`labelPdfFile` (`FIELD_STORAGE`) write can silently skip** when set on a lazy
|
|
146
|
+
`new _Model_Client_TrackingNumber($id)` (never loaded): `number` (a real column) saves but the
|
|
147
|
+
storage write is skipped (no error, no S3 object — and `copyFileToS3` re-throws, so a skip ≠ a
|
|
148
|
+
failed write). Likely needs `->load()` before setting `labelPdfFile`. **Open issue** — the S3
|
|
149
|
+
label save was still not landing as of 2026-06-18.
|
|
150
|
+
- **FPDF can't embed alpha-channel or interlaced PNG** — carrier label PNGs must be flat raster.
|
|
151
|
+
If one isn't, `buildFromPngLabels` throws; fix = GD normalize (flatten/de-interlace) or TCPDF.
|
|
152
|
+
- **UPS shipper number must match the endpoint.** Beta hits the UPS **CIE test** endpoint
|
|
153
|
+
(`wwwcie.ups.com`); a production account number there returns `120100`/`120121`
|
|
154
|
+
("missing/invalid shipper number" / "cannot be used"). Use a CIE-enabled shipper number for
|
|
155
|
+
test, or point at prod UPS. `SHIPPER_NUMBER` is a hardcoded constant in the UPS client.
|
|
122
156
|
|
|
123
157
|
## Change history
|
|
158
|
+
- 2026-06-18 — Implemented the label-storage rework: request PNG from UPS/FedEx, store the raw PNG, generate the printable PDF on demand via the new `_Component_Library_LabelPdf` (FPDF); dropped Labelary. Added the FedEx tracking-cred config-fallback fix and the `_Loader`→Composer FPDF autoloader toggle. Open: the `labelPdfFile` S3 write still skips on a lazy-model save. (mhammontree)
|
|
124
159
|
- 2026-06-16 — Repointed the carrier-label/NS-IF tracking-number queries (`upsShipmentApi`, `fedexShipmentApi`, `fulfill`, `createNetsuiteItemFulfillment`) from the dropped `ItemFulfillmentPackages` table to the `ItemFulfillments_TrackingNumbers` bridge — they had been saving the label/`number` to the wrong/null record (reprint `labelPdfFile` null; NS IF missing tracking). Confirmed working on beta. (mhammontree)
|
|
125
160
|
- 2026-06-10 — Documented carrier shipping label mechanics (UPS/FedEx) + the authoritative GIF label-storage decision and NetSuite IF attachment. (mhammontree)
|
|
126
161
|
|
|
@@ -6,8 +6,8 @@ project: API
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: workflow
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
10
|
-
owners: ["jcardinal"]
|
|
9
|
+
updated: 2026-06-18
|
|
10
|
+
owners: ["jcardinal", "mhammontree"]
|
|
11
11
|
files: []
|
|
12
12
|
related: []
|
|
13
13
|
---
|
|
@@ -68,6 +68,27 @@ anything repo-specific. The AWS CLI cannot introspect the GitHub App installatio
|
|
|
68
68
|
account proves it is not a GitHub/repo/branch problem — connections and App installations are
|
|
69
69
|
per-account, so the fault is local to the failing account's connection.
|
|
70
70
|
|
|
71
|
+
### Gotcha: a terminated EB instance must be manually re-registered with the load balancer
|
|
72
|
+
We do **not** pay for EB-managed (auto) registration of instances on the load balancer. When you
|
|
73
|
+
terminate an instance — e.g. to force a clean redeploy / fresh `composer install` — the
|
|
74
|
+
**replacement instance is NOT automatically added as an LB target**. Until you register it by
|
|
75
|
+
hand (EC2 → the new instance → add it as a target in the environment's target group), the LB
|
|
76
|
+
keeps routing to the old/healthy target (or none), so your freshly-deployed code **never serves
|
|
77
|
+
traffic** and the deploy looks like it "didn't take" / the bug looks unfixed. After terminating:
|
|
78
|
+
grab the new instance ID, register it as a target, wait for it to go healthy, then test. This
|
|
79
|
+
masquerades as deploy-lag — confirm the LB is actually pointing at the new instance before
|
|
80
|
+
debugging the code. (Seen 2026-06-18 chasing a "missing FPDF / old code still running" loop on
|
|
81
|
+
beta that was really the LB still pointing at a terminated instance's replacement that was never
|
|
82
|
+
registered.)
|
|
83
|
+
|
|
84
|
+
### On-instance composer install (when a dep is missing post-deploy)
|
|
85
|
+
If a Composer dep is missing on the running instance (e.g. `Class "FPDF" not found` because
|
|
86
|
+
`composer.lock` wasn't committed), you can install it on the box over SSH/PuTTY — but
|
|
87
|
+
`composer require setasign/fpdf:^1.8` must have **no space** after the colon (`fpdf: ^1.8`
|
|
88
|
+
parses `^1.8` as a separate package and errors), and run it as the web app user / `chown` the
|
|
89
|
+
result so the webserver can read `vendor/`. The real fix is to **commit `composer.lock`** so EB's
|
|
90
|
+
`composer install` picks it up — the on-instance install is a stopgap that a redeploy wipes.
|
|
91
|
+
|
|
71
92
|
### CloudShell triage commands (read-only)
|
|
72
93
|
```bash
|
|
73
94
|
PIPELINE="<pipeline-name>"; REGION="us-east-1"
|
|
@@ -82,6 +103,7 @@ aws codeconnections get-connection --connection-arn "<CONN_ARN>" --region "$REGI
|
|
|
82
103
|
```
|
|
83
104
|
|
|
84
105
|
## Change history
|
|
106
|
+
- 2026-06-18 — Added two EB-instance gotchas surfaced during the TOGa Supply beta/prod label deploys: (1) terminated instances must be **manually re-registered** as LB targets (we don't pay for auto-registration) — until then the new code never serves traffic and looks like deploy-lag; (2) on-instance `composer require` stopgap syntax (no space after the colon, fix root cause by committing `composer.lock`). (mhammontree)
|
|
85
107
|
- 2026-06-16 — Documented after a pipeline (API-QC-Security, account 975050298201) failed every
|
|
86
108
|
run at Source with "[GitHub] GitHub returned an Internal Error"; connection was AVAILABLE and
|
|
87
109
|
IAM was correct — root cause was a pending GitHub App access-request approval in the
|
|
@@ -3,5 +3,5 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
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
|
-
| [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 |
|
|
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/ShipmentsPage.tsx, toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx, toga2-supply/src/pages/Shipments/api/ShipmentsApi.ts, toga2-supply/src/pages/FulfilledShipments/view/FulfilledShipmentsPage.tsx, _underscore/Model/Client/ItemFulfillment.php, _underscore/Trait/Netsuite/ItemFulfillment.php, _underscore/Component/Library/Carriers/Ups/Ups.php |
|
|
7
7
|
| [AWS Amplify Build & Deploy (non-prod environments)](workflows/amplify-build-and-deploy.md) | How `toga2-supply` (React + Vite) builds and deploys on **AWS Amplify**. | toga2-supply/amplify.yml, toga2-supply/.gitattributes, toga2-supply/.github/workflows/sync-stage-environments.yml, toga2-supply/.env.qc-security |
|
|
@@ -6,15 +6,17 @@ project: TOGa Supply
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-18
|
|
10
10
|
owners: [mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx
|
|
13
13
|
- toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx
|
|
14
14
|
- toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx
|
|
15
15
|
- toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts
|
|
16
|
+
- toga2-supply/src/pages/Shipments/view/ShipmentsPage.tsx
|
|
16
17
|
- toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx
|
|
17
18
|
- toga2-supply/src/pages/Shipments/api/ShipmentsApi.ts
|
|
19
|
+
- toga2-supply/src/pages/FulfilledShipments/view/FulfilledShipmentsPage.tsx
|
|
18
20
|
- _underscore/Model/Client/ItemFulfillment.php
|
|
19
21
|
- _underscore/Trait/Netsuite/ItemFulfillment.php
|
|
20
22
|
- _underscore/Component/Library/Carriers/Ups/Ups.php
|
|
@@ -57,13 +59,18 @@ GroWrk, June 2026); FedEx + UPS both need verification before prod.
|
|
|
57
59
|
`saveShipmentToNetsuite` → `createNetsuiteItemFulfillment`, which attaches the label
|
|
58
60
|
to the IF **after** creating it (the IF doesn't exist yet when the label is bought).
|
|
59
61
|
|
|
60
|
-
## Reprint (
|
|
62
|
+
## Reprint (being rewired to the backend)
|
|
61
63
|
|
|
62
|
-
Fulfilled-shipments view
|
|
63
|
-
multi-
|
|
64
|
-
(see the carrier-shipping-labels doc)
|
|
65
|
-
the
|
|
66
|
-
|
|
64
|
+
Fulfilled-shipments view (`FulfilledShipmentsPage` → `ShipmentsCardTableForm` with
|
|
65
|
+
`isFulfilledShipmentsView`): multi-select shipments, merge each stored label into one
|
|
66
|
+
multi-page PDF, print once. The label-storage rework (see the carrier-shipping-labels doc)
|
|
67
|
+
now stores the raw carrier **PNG** on `TrackingNumbers.labelPdfFile` and builds the PDF on the
|
|
68
|
+
**backend** (`_Component_Library_LabelPdf`/FPDF). So the current client-side `pdf-lib` merge —
|
|
69
|
+
which reads `labelPdfFile` expecting a base64 **PDF** — is now **stale**: it receives a PNG.
|
|
70
|
+
Reprint must be rewired to call a backend generate endpoint that returns the combined
|
|
71
|
+
(shipping + return) PDF; until then it won't produce a printable label even when one is stored.
|
|
72
|
+
The reprint reference was also corrected from `shipment.itemFulfillmentPackages` to
|
|
73
|
+
`itemFulfillmentTrackingNumbers` (bridge migration).
|
|
67
74
|
|
|
68
75
|
## Gotchas / known issues
|
|
69
76
|
|
|
@@ -117,11 +124,24 @@ likely moves to the backend.
|
|
|
117
124
|
`item-fulfillment-tracking-numbers` bridge requires the bridge record's ACL logic-group
|
|
118
125
|
chain to exist for the user's role, or it 403s and `itemFulfillmentTrackingNumbers` returns
|
|
119
126
|
`null` (so reprint has no label). See the tracking-number-bridges doc's ACL gotcha + fix.
|
|
127
|
+
- **Two separate page components + a scroll/clip trap.** `/shipments` renders `ShipmentsPage`;
|
|
128
|
+
`/fulfilled-shipments` renders `FulfilledShipmentsPage` — **separate components** (a fix to one
|
|
129
|
+
does NOT touch the other; the shared list is `ShipmentsCardTableForm`). Both pages used
|
|
130
|
+
`overflow-scroll` across nested containers — Tailwind `overflow-scroll` forces *always-visible*
|
|
131
|
+
scrollbars (the "scrollbars everywhere" look) — and `FulfilledShipmentsPage`'s outer had
|
|
132
|
+
`h-full min-h-full max-h-full` with **no overflow**, so its content (incl. the Reprint button
|
|
133
|
+
below the `min-h-[862px]` form) was clipped by `AuthLayout`'s `overflow-hidden` with no way to
|
|
134
|
+
scroll to it — the button was only reachable when zoomed out. Layout chain: `AuthLayout`'s
|
|
135
|
+
content area is `h-[calc(100vh-80px)]` inside `overflow-hidden`, so **each page's outer must own
|
|
136
|
+
its own scroll** (`h-full overflow-auto`). Fix (2026-06-18): `overflow-scroll` → `overflow-auto`
|
|
137
|
+
everywhere (and `overflow-y-auto` on the `max-h-[530px]` card list in `ShipmentsCardTableForm`),
|
|
138
|
+
and the fulfilled-page outer → `h-full overflow-auto`.
|
|
120
139
|
|
|
121
140
|
## Remaining work
|
|
122
141
|
|
|
123
|
-
-
|
|
124
|
-
|
|
142
|
+
- Reprint rewire: call the backend `LabelPdf` generate endpoint (PNG → combined PDF) instead
|
|
143
|
+
of the stale client-side `pdf-lib` merge (per the carrier-shipping-labels doc). The backend
|
|
144
|
+
half (PNG store + on-demand PDF) is done.
|
|
125
145
|
- Return-label flow: `returnTrackingNumberId` (nullable) on `ItemFulfillmentItemUnits`;
|
|
126
146
|
"needs return label" checkbox; return-address editable combo ("Rolodex pattern" of
|
|
127
147
|
client locations, overridable, validated); "return label" text on return pages of the
|
|
@@ -138,5 +158,6 @@ not the base `_Model_Client_ItemFulfillment`. Tested with GroWrk; UPS support wa
|
|
|
138
158
|
for Compass and is not yet in prod.
|
|
139
159
|
|
|
140
160
|
## Change history
|
|
161
|
+
- 2026-06-18 — PNG label-storage rework reflected on the frontend: labels now store as PNG and the PDF is built on the backend (`LabelPdf`), so the client-side `pdf-lib` reprint is stale and must be rewired to a backend generate endpoint. Fixed the Fulfilled Shipments responsiveness/clip bug (Reprint button unreachable at 100% zoom) — `overflow-scroll` → `overflow-auto` and gave `FulfilledShipmentsPage`'s outer its own scroll; documented the `/shipments` vs `/fulfilled-shipments` two-component trap and the `AuthLayout overflow-hidden` clip. (mhammontree)
|
|
141
162
|
- 2026-06-16 — Drove the full beta flow green for UPS/GroWrk. Updated the save step to the `/item-fulfillment-tracking-numbers` bridge (FE commit `b5c16592d`, key `itemFulfillmentTrackingNumbers`). Added gotchas: local-FE↔deployed-beta-BE skew, `dtSubmitted` fulfilled/pending gating, `createNetsuiteItemFulfillment` orderLine fragility on partial/already-fulfilled orders, and the bridge-ACL 403 that nulls tracking/labels (reprint). (mhammontree)
|
|
142
163
|
- 2026-06-10 — Documented the Fulfill & Ship flow (SO sync, label purchase, NetSuite IF creation, success gating, reprint). Driven green on beta for UPS/GroWrk; FedEx + prod verification pending. (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: Worker
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-06-
|
|
9
|
+
updated: 2026-06-18
|
|
10
10
|
owners: ["dfranks"]
|
|
11
11
|
files:
|
|
12
12
|
- worker2/Worker/Netsuite.php
|
|
@@ -90,7 +90,15 @@ second, independently-gated concern in the same handler.
|
|
|
90
90
|
|
|
91
91
|
1. `findClickupTaskByOpportunityNumber($tranId)` — GET the list filtered by the `Opportunity #`
|
|
92
92
|
custom field (`include_closed=true&include_archived=true&custom_fields=[{field_id,operator:'=',value}]`).
|
|
93
|
-
2. **Match → `updateTask()
|
|
93
|
+
2. **Match → `updateTask()` — but ONLY when `UPDATE_CLICKUP === true`.** `const UPDATE_CLICKUP = false`
|
|
94
|
+
([Opportunity.php](worker2/Worker/Netsuite/Opportunity.php)) is a **deliberate one-way guard, NOT a
|
|
95
|
+
temporary backstop to flip**: once a ClickUp opportunity task exists, ClickUp is the source of truth
|
|
96
|
+
for it, and a NetSuite edit must **not** clobber edits made on the ClickUp side. With the flag false
|
|
97
|
+
(its production value), a match returns `skipped update (update disabled)` and ClickUp is left
|
|
98
|
+
untouched — so the *designed, correct* behavior of a NetSuite edit to an opportunity that already has
|
|
99
|
+
a task is: **Forecast upserts, ClickUp unchanged.** Do not "enable updates" by flipping it. The
|
|
100
|
+
change-detection logic below only runs in the (currently-off) update path. When enabled, it:
|
|
101
|
+
**diffs first, writes only what changed** (change-detection backstop,
|
|
94
102
|
since 2026-06-17). PUT name/description **only if** one differs; POST **only** the custom fields
|
|
95
103
|
that differ (each to `/task/{id}/field/{fieldId}` — ClickUp has no bulk custom-field set). When
|
|
96
104
|
nothing differs it writes nothing and returns `task unchanged`. This kills the no-op-write echo: a
|
|
@@ -204,6 +212,13 @@ None — platform-wide Forecast sync.
|
|
|
204
212
|
(`https://webhook.togahub.com/netsuite`) with `debugWrap` off. Also flip the enqueuer deployment
|
|
205
213
|
`Testing → Released` (Testing fires only for the deploying user). Full checklist in
|
|
206
214
|
`DEPLOY_RUNBOOK.md` §6.
|
|
215
|
+
- **ClickUp's `=` custom-field filter is substring/prefix, NOT strict equality.** `findClickupTaskByOpportunityNumber($tranId)`
|
|
216
|
+
filters the `Opportunity #` field with `operator:'='`, but for a short-text custom field ClickUp matches
|
|
217
|
+
loosely: a lookup for `74266` still returns a task whose value is `74266--` (confirmed live 2026-06-18).
|
|
218
|
+
So **appending** to the dedup value does NOT defeat the match — to force a dedup *miss* (e.g. to exercise
|
|
219
|
+
the create branch) you must change the **leading** content or clear the field entirely. Verified both ways
|
|
220
|
+
on opp 74266/internalId 7161054: `74266--` → matched (update branch, skipped); field removed → missed
|
|
221
|
+
(create branch, new task `868k2rfj8`).
|
|
207
222
|
- **ClickUp dedup keys on the `Opportunity #` field, not a DB column.** We deliberately did NOT add
|
|
208
223
|
a `clickupTaskId` column — the dedup lookup queries ClickUp itself by `Opportunity #` (= tranId).
|
|
209
224
|
Consequences to know:
|
|
@@ -262,6 +277,16 @@ same **skip-if-unchanged** compare on the extracted values, and **actor-identity
|
|
|
262
277
|
trigger a CU→NS write. The NS→CU change-detection above is the complementary backstop, not a substitute.
|
|
263
278
|
|
|
264
279
|
## Change history
|
|
280
|
+
- 2026-06-18 — **Verified live in production** (no code change): real NetSuite SuiteScript webhooks
|
|
281
|
+
(`user-agent: NetSuite/2026.1`) arrive at `webhook.togahub.com/netsuite` and process successfully
|
|
282
|
+
(e.g. opp 6926224 PUT). Clarified that **`UPDATE_CLICKUP = false` is an intentional one-way guard**
|
|
283
|
+
(NS edits must not overwrite ClickUp-side edits), not a flag to flip — so "NS edit → Forecast upserts,
|
|
284
|
+
existing ClickUp task untouched" is the *designed* behavior. Documented that ClickUp's `=` custom-field
|
|
285
|
+
filter is **substring/prefix**, not exact (a `74266` lookup matched `74266--`); confirmed both dedup
|
|
286
|
+
branches on opp 74266/internalId 7161054. Testing note: the webhook needs the **internalId**, not the
|
|
287
|
+
opportunity number — resolve it via `Forecast.Opportunities.netsuiteOpportunityInternalId` for a given
|
|
288
|
+
`opportunityNumber` (tranId). Fire a manual test with
|
|
289
|
+
`curl -X POST https://webhook.togahub.com/netsuite -d '{"recordType":"opportunity","eventType":"edit","internalId":<id>}'`. (dfranks)
|
|
265
290
|
- 2026-06-17 — ClickUp **change-detection backstop** added to `updateTask()`: diff before write, skip
|
|
266
291
|
no-op syncs (`task unchanged`), PUT name/description only when changed, POST only changed custom
|
|
267
292
|
fields. `findClickupTaskByOpportunityNumber()` now returns the full task object; added
|
package/package.json
CHANGED