toga-ai 1.0.362 → 1.0.363
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/features/acl-permission-chain.md +28 -0
- package/knowledge/2.0/apps/_underscore/features/address-validation.md +23 -0
- package/knowledge/2.0/apps/_underscore/features/carrier-shipping-labels.md +31 -0
- package/knowledge/2.0/apps/api2/INDEX.md +1 -0
- package/knowledge/2.0/apps/api2/features/v2-api-error-codes.md +66 -0
- package/knowledge/2.0/apps/toga2-supply/INDEX.md +1 -1
- package/knowledge/2.0/apps/toga2-supply/features/fulfill-and-ship.md +51 -0
- package/knowledge/INDEX.md +1 -1
- package/package.json +1 -1
|
@@ -112,6 +112,30 @@ Worked example: the reprint API `GET /v2/tracking-numbers/reprint`
|
|
|
112
112
|
Client-DB `AclRecordScripts` grant (migrations in `dbchanges2`) before it stopped 403ing — see the
|
|
113
113
|
carrier-shipping-labels feature doc.
|
|
114
114
|
|
|
115
|
+
## Add a writable field to a V2 record (3-file migration)
|
|
116
|
+
|
|
117
|
+
Adding a persisted, API-writable field to an existing V2 record is a **three-file migration set**
|
|
118
|
+
— miss any one and the API rejects the write with a specific code (see the
|
|
119
|
+
[V2 API error codes](../../api2/features/v2-api-error-codes.md) map):
|
|
120
|
+
|
|
121
|
+
1. **Core `RecordFields` registration** (`dbchanges2/Core/`) — register the field name + type
|
|
122
|
+
(e.g. `'STRING'`) against the record. Resolve the `recordId` by **route**, not a hardcoded id.
|
|
123
|
+
**Without it the API returns `EV-8`** ("field does not exist").
|
|
124
|
+
2. **Client column** (`dbchanges2/Client/`) — `ALTER TABLE <Table> ADD <field> <type>`, guarded
|
|
125
|
+
by an `INFORMATION_SCHEMA` existence check so it is safe to replay and safe against the
|
|
126
|
+
`BLANK_CLIENT_DATABASE` template.
|
|
127
|
+
3. **Client `AclFieldPermissions` grant** (`dbchanges2/Client/`) — grant the caller's role(s)
|
|
128
|
+
read/write on the new field. **Without it the API returns `EV-9`** ("no permission to write
|
|
129
|
+
field"). The reliable way to get the right rows is to **clone the grants of an existing
|
|
130
|
+
writable sibling field** on the same record — pick a sibling that actually has explicit
|
|
131
|
+
`AclFieldPermissions` rows (e.g. on `TrackingNumbers`, `containsBattery` has them, whereas
|
|
132
|
+
`requiresSignature` had none), and resolve `roleId` by subselect.
|
|
133
|
+
|
|
134
|
+
Worked example (TRUE-79191, `TrackingNumbers.signatureType`): `Core/... - TrackingNumberSignatureTypeRecordField.sql`
|
|
135
|
+
(RecordField, `STRING`), `Client/... - TrackingNumberSignatureType.sql` (guarded `ALTER TABLE`),
|
|
136
|
+
`Client/... - TrackingNumberSignatureTypeFieldPermission.sql` (cloned `containsBattery`'s grants).
|
|
137
|
+
This mirrors the earlier `needsReturnLabel` / `returnAddressId` 3-file sets.
|
|
138
|
+
|
|
115
139
|
## Checklist (so the chain is never half-built)
|
|
116
140
|
|
|
117
141
|
- [ ] `AclRecordPermissions` row for the role × record with the right CRUD flags.
|
|
@@ -123,6 +147,10 @@ carrier-shipping-labels feature doc.
|
|
|
123
147
|
|
|
124
148
|
## Change history
|
|
125
149
|
|
|
150
|
+
- **2026-07-17** — TRUE-79191: added the **"add a writable field to a V2 record" 3-file migration**
|
|
151
|
+
recipe (Core `RecordFields` → `EV-8` if missing; guarded Client column; Client `AclFieldPermissions`
|
|
152
|
+
→ `EV-9` if missing, cloned from a writable sibling like `containsBattery`), worked example
|
|
153
|
+
`TrackingNumbers.signatureType`. Cross-linked the new V2 API error-codes map. (mhammontree)
|
|
126
154
|
- **2026-07-02** — Added the record-script dispatch grant (`AclRecordScripts`) as a gate distinct
|
|
127
155
|
from the record-CRUD chain, with the full recipe for exposing a scripted API on a `_Model`
|
|
128
156
|
(Core.RecordScripts route + Client `AclRecordScripts` grant + `(&$api, …)` method), discovered
|
|
@@ -53,6 +53,24 @@ scripted endpoint in **`Core.RecordScripts`** (id 17: `recordId` 13 = Addresses,
|
|
|
53
53
|
route = validateAddress) returns to **`data.addresses.validateAddress`**, which is exactly
|
|
54
54
|
what the frontend reads.
|
|
55
55
|
|
|
56
|
+
## `GET /addresses/validate` — the migration-registered route (2026-07)
|
|
57
|
+
|
|
58
|
+
TRUE-79191 added a **second** route to the same `validateAddress` PHP method, this time **registered
|
|
59
|
+
in `dbchanges2` source** (closing the parity gap noted below) rather than only in the live DB:
|
|
60
|
+
|
|
61
|
+
- **Route** — `Core.RecordScripts` row: `recordId` = Addresses, `method` `GET`, route **`validate`**,
|
|
62
|
+
`phpMethod` `validateAddress` (`dbchanges2/Core/2026-07-16b - AddressValidateRecordScript.sql`).
|
|
63
|
+
- **Dispatch grant** — Client `AclRecordScripts` grant to roles **Public (1) / Super User (3) /
|
|
64
|
+
Base (4)** (`dbchanges2/Client/2026-07-16c - AddressValidateScriptAcl.sql`). Without it the call
|
|
65
|
+
403s with `EZ-1`.
|
|
66
|
+
- **Response path** — because the route is `validate`, the result lands at
|
|
67
|
+
**`data.addresses.validate`** (the Fulfill & Ship return-address modal reads exactly this;
|
|
68
|
+
distinct from the older global `validateAddress` route's `data.addresses.validateAddress`).
|
|
69
|
+
|
|
70
|
+
The PHP method (`_Model_Client_Address::validateAddress`) already existed and was unchanged — it was
|
|
71
|
+
simply **unrouted in source** until this migration set exposed it. New scripted routes should follow
|
|
72
|
+
this pattern (route + `AclRecordScripts` both in `dbchanges2`).
|
|
73
|
+
|
|
56
74
|
## Client variations
|
|
57
75
|
|
|
58
76
|
None — this is shared engine + framework behavior. The carrier credentials are resolved
|
|
@@ -68,6 +86,11 @@ per the standard client carrier config.
|
|
|
68
86
|
|
|
69
87
|
## Change history
|
|
70
88
|
|
|
89
|
+
- 2026-07-17 — TRUE-79191: added a **`dbchanges2`-registered** `GET /addresses/validate` route to the
|
|
90
|
+
same `validateAddress` method (Core `RecordScripts` route `validate` + Client `AclRecordScripts`
|
|
91
|
+
grant to Public/SuperUser/Base) so the Fulfill & Ship return-address modal can validate on save;
|
|
92
|
+
response at `data.addresses.validate`. Method was pre-existing but unrouted. Closes the source/DB
|
|
93
|
+
parity gap going forward. (mhammontree)
|
|
71
94
|
- 2026-07-07 — Documented `_Model_Client_Address::validateAddress` (USPS→FedEx→UPS waterfall
|
|
72
95
|
+ normalized success shape) and the global `GET /addresses/validateAddress`
|
|
73
96
|
(`Core.RecordScripts` id 17) endpoint, including the dispatch precedence, the
|
|
@@ -145,6 +145,29 @@ NetSuite** at all. It now iterates the `itemFulfillmentTrackingNumbers` bridge a
|
|
|
145
145
|
(`packageDescr = 'Return Label'`). (The 2026-06-16 bridge repoint fixed the four queries in
|
|
146
146
|
`Model/Client/ItemFulfillment.php`; this trait method was a separate straggler on the same table.)
|
|
147
147
|
|
|
148
|
+
## Signature type on the label (implemented 2026-07)
|
|
149
|
+
|
|
150
|
+
Signature was **never applied to labels before** — the carrier libs ignored it. Fulfill & Ship now
|
|
151
|
+
carries a **4-level signature type** (None Specified / Indirect / Direct / Adult) end-to-end onto the
|
|
152
|
+
outbound label:
|
|
153
|
+
|
|
154
|
+
- **Storage & field.** `TrackingNumbers.signatureType` (`VARCHAR(255)`), exposed as
|
|
155
|
+
`_Model_Client_TrackingNumber::$signatureType = self::FIELD_CHAR` and registered as a Core
|
|
156
|
+
RecordField + Client `AclFieldPermissions` grant (the 3-file migration — see the ACL doc).
|
|
157
|
+
- **Threading.** A **generic `$signatureType`** field was added to
|
|
158
|
+
`Component/Library/Carriers/ShipmentRequest/ShipmentRequest.php`. Both outbound paths
|
|
159
|
+
(`fedexShipmentApi`, `upsShipmentApi` in `Model/Client/ItemFulfillment.php`) now **SELECT
|
|
160
|
+
`TrackingNumbers.signatureType`** and pass it into the request. **Return-label flows are
|
|
161
|
+
untouched — no signature on returns.**
|
|
162
|
+
- **FedEx mapping** (`Component/Library/Carriers/Fedex/Fedex.php`): maps the level to per-package
|
|
163
|
+
`packageSpecialServices.signatureOptionType` → `INDIRECT` / `DIRECT` / `ADULT`. **None Specified
|
|
164
|
+
is sent explicitly as `NO_SIGNATURE_REQUIRED`.**
|
|
165
|
+
- **UPS mapping** (`Component/Library/Carriers/Ups/Ups.php`): maps to
|
|
166
|
+
`Package.PackageServiceOptions.DeliveryConfirmation.DCISType` (`2` = Signature Required, `3` =
|
|
167
|
+
Adult) on **both the OAuth and the legacy payloads**. **⚠ UPS has no Indirect tier — Indirect
|
|
168
|
+
falls back to Signature Required (`2`).** None Specified is **omitted** (UPS default, no
|
|
169
|
+
confirmation).
|
|
170
|
+
|
|
148
171
|
## `FIELD_STORAGE` mechanics (reference)
|
|
149
172
|
|
|
150
173
|
`Model.php` handles storage fields separately from regular columns (write loop ~line
|
|
@@ -259,6 +282,14 @@ still discards the unsaved in-memory value.
|
|
|
259
282
|
bill) — expect a hard error rather than a silently-wrong bill in those cases.
|
|
260
283
|
|
|
261
284
|
## Change history
|
|
285
|
+
- 2026-07-17 — TRUE-79191: applied **signature type** to outbound labels for the first time (carrier
|
|
286
|
+
libs previously ignored it). Added a generic `$signatureType` on `ShipmentRequest`;
|
|
287
|
+
`fedexShipmentApi`/`upsShipmentApi` now SELECT + pass `TrackingNumbers.signatureType`; FedEx maps to
|
|
288
|
+
`packageSpecialServices.signatureOptionType` (INDIRECT/DIRECT/ADULT, None → explicit
|
|
289
|
+
`NO_SIGNATURE_REQUIRED`); UPS maps to `DeliveryConfirmation.DCISType` (2=Signature, 3=Adult) on both
|
|
290
|
+
OAuth + legacy payloads, **Indirect falls back to 2 (UPS has no Indirect tier)**, None omitted.
|
|
291
|
+
Return-label flows untouched. `TrackingNumbers.signatureType` column + `$signatureType=FIELD_CHAR`
|
|
292
|
+
added. (mhammontree)
|
|
262
293
|
- 2026-07-14 — TRUE-79191 return-label increment: `generateReturnLabel` ships from the **per-fulfillment** return address (`returnAddressId` → Addresses/States, falling back to `RETURN_ADDRESS_*`; supersedes the fixed `RETURN_ADDRESSES` set) and returns `returnTrackingNumberUuid`; documented the return-leg reuse semantic (return reuses outbound weight/carrier/method/account — only the address is independent). Fixed `Trait/Netsuite/ItemFulfillment.php::createNetsuiteItemFulfillment`, which read the retired `itemFulfillmentPackages` so no tracking reached NetSuite — now iterates the `itemFulfillmentTrackingNumbers` bridge and pushes outbound + return (2nd package, `packageDescr='Return Label'`). `reprintLabelsApi` `$caption` now accepts a position-aligned CSV for per-page captions. Added the local live-UPS testing setup gotcha (`[ups]` OAuth config, CIE endpoint/shipper, `ShippingMethods.code`). (mhammontree)
|
|
263
294
|
- 2026-07-02 — Reprint combine moved to the backend: new `_Model_Client_TrackingNumber::reprintLabelsApi` scripted API (RecordScript `GET /v2/tracking-numbers/reprint`) returns a combined base64 PDF from the stored PNGs (frontend `pdf-lib` merge removed). Added return-label generation (`generateReturnLabel`; UPS ReturnService `9` / FedEx `PRINT_RETURN_LABEL`; Ground/customer-drops-off; `RETURN_ADDRESSES`; links `returnTrackingNumberId`). Carrier billing account threaded from the tracking record (`shippingCarrierAccountNumber`) — removed `SHIPPER_NUMBER`/`ACCOUNT_NUMBER` constants, fail-fast on empty account, error on >1 tracking number per IF. Ship-to now `COALESCE(IF, SO)` address. `getLabelFormat()` defaults PNG + allowlist. Fixed two SQL injections (`postPost` `$payload->uuid`, `fulfill` NetSuite `$internalId`) and removed leftover debug `echo`/`print_r` from `fulfill()`. Found + fixed the framework-core root cause of storage writes silently vanishing (two `Model.php` `FIELD_STORAGE` bugs — see FIELD_STORAGE mechanics). (mhammontree)
|
|
264
295
|
- 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)
|
|
@@ -11,5 +11,6 @@
|
|
|
11
11
|
| [Surface action-state via the surface=<slug> request option (M2M-safe)](features/surface-meta-option.md) | An opt-in V2 engine request option, `surface=<slug>`, that attaches per-record UI action state (`isVisible`/`isEnabled`) to a GET response **under `meta.surface | api2/Component/Api/V2/V2.php, _underscore/Model/Core/Surface.php |
|
|
12
12
|
| [TableView row-filtering via apiWhereClause (options.where grammar, end to end)](features/tableview-apiwhereclause-row-filtering.md) | `TableViews.apiWhereClause` (TEXT, nullable) is the sanctioned, code-free way to restrict or exclude rows from a 2.0 table view. | api2/Component/Api/V2/V2.php, _underscore/Model/Client/TableView.php, toga2-supply/src/api/toga.ts |
|
|
13
13
|
| [Tickets API (/v2/tickets)](features/tickets-api.md) | The generic ticket endpoint of the 2.0 REST API. | Component/Api/V2/V2.php |
|
|
14
|
+
| [V2 API error/message codes (EV/EZ troubleshooting map)](features/v2-api-error-codes.md) | The V2 JSON engine (`Component/Api/V2/V2.php`) returns short **message codes** in the response `error` field, grouped by family: `EN-*` authentication, `EZ-*` a | api2/Component/Api/V2/V2.php |
|
|
14
15
|
| [AWS CodePipeline Deployment via CodeConnections (GitHub → Elastic Beanstalk)](workflows/codepipeline-codeconnections-deploy.md) | 2.0 apps (`api2`, `_underscore`) are deployed through **AWS CodePipeline**. | |
|
|
15
16
|
| [New Environment Configuration & Provisioning (api2)](workflows/environment-configuration-and-provisioning.md) | What it takes for a 2.0 API environment (e.g. | api2/Config/<environment>.ini, api2/Controller/Index.php, dbchanges2/Core/2026-06-16a - DatabaseHosts for new QA QC stage demo environments.sql, _underscore/Route.php |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "V2 API error/message codes (EV/EZ troubleshooting map)"
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: api2
|
|
5
|
+
project: API
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-17
|
|
10
|
+
owners: [mhammontree]
|
|
11
|
+
files:
|
|
12
|
+
- api2/Component/Api/V2/V2.php
|
|
13
|
+
related:
|
|
14
|
+
- ../../_underscore/features/acl-permission-chain.md
|
|
15
|
+
- scripted-api-post-body-args.md
|
|
16
|
+
- ../architecture.md
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Summary
|
|
20
|
+
|
|
21
|
+
The V2 JSON engine (`Component/Api/V2/V2.php`) returns short **message codes** in the
|
|
22
|
+
response `error` field, grouped by family: `EN-*` authentication, `EZ-*` authorization,
|
|
23
|
+
`EV-*` validation, `EO-*` operation, plus `W*`/`D*`. This doc is the **troubleshooting map**
|
|
24
|
+
for the codes you hit when adding a field or a scripted API to a V2 record — each with its
|
|
25
|
+
root cause and the exact fix — so a teammate can go straight from the code to the missing
|
|
26
|
+
migration instead of re-deriving it. All field/script ACL rows live in the **CLIENT** DB
|
|
27
|
+
(`Records.aclDatabase = 'CLIENT'`), keyed by the caller's `roleId`.
|
|
28
|
+
|
|
29
|
+
## The codes (cause → fix)
|
|
30
|
+
|
|
31
|
+
| Code | Meaning | Root cause | Fix |
|
|
32
|
+
|------|---------|------------|-----|
|
|
33
|
+
| **EV-6** | Resource / route not found | A **scripted-API** route segment is being treated as a record uuid because no Record Script matches — i.e. the `Core.RecordScripts` route row is missing | Register the `Core.RecordScripts` row (`recordId`, `method`, `route`, `phpMethod`) |
|
|
34
|
+
| **EV-8** | Request field does not exist | A field sent in the payload isn't registered as a **`Core.RecordFields`** row for that record | Register the field in `Core.RecordFields` (see the "add a field to a V2 record" recipe in the ACL doc) |
|
|
35
|
+
| **EV-9** | No permission to write field | The field exists but the caller's role has no **`AclFieldPermissions`** grant (`isWritable=1`) in the CLIENT DB | Add the `AclFieldPermissions` row for the role (clone a writable sibling field's grant) |
|
|
36
|
+
| **EZ-1** | Unauthorized record/script dispatch | Missing **`AclRecordScripts`** (scripted APIs) or the **`AclRecordPermissions`** four-table chain (records) for the caller's role | Grant `AclRecordScripts` (scripts) or complete the record-CRUD chain |
|
|
37
|
+
| **EV-5** | Duplicate `transactionId` | The globally-unique `transactionId` was reused | Send a fresh unique `transactionId` per request |
|
|
38
|
+
| **EV-12** | Record's parent not synced | (Fulfill & Ship) POST `/item-fulfillments` when the Sales Order isn't synced into Toga yet | `GET /sales-orders/syncNetsuiteSalesOrder?netsuiteInternalSalesOrderId=<id>` first |
|
|
39
|
+
|
|
40
|
+
## How to diagnose
|
|
41
|
+
|
|
42
|
+
1. **Resolve the role.** Field/script ACLs are keyed by `roleId` **in the CLIENT DB**, and role
|
|
43
|
+
ids differ per client — resolve by subselect (`SELECT id FROM Roles WHERE name='Base'`), never
|
|
44
|
+
hardcode. To find the right grant shape, inspect a **working sibling** field/script's rows.
|
|
45
|
+
2. **EV-8 vs EV-9** are the two halves of adding a writable field: EV-8 = the Core `RecordFields`
|
|
46
|
+
registration is missing; EV-9 = the field exists but the Client `AclFieldPermissions` grant is
|
|
47
|
+
missing. You will typically hit EV-8 first, fix it, then hit EV-9.
|
|
48
|
+
3. **EV-6 vs EZ-1** are the two halves of exposing a scripted API: EV-6 = no `Core.RecordScripts`
|
|
49
|
+
route (the segment falls through to record lookup); EZ-1 = the route exists but there's no
|
|
50
|
+
`AclRecordScripts` dispatch grant for the caller's role.
|
|
51
|
+
|
|
52
|
+
## Related recipes
|
|
53
|
+
|
|
54
|
+
- **Add a writable field to a V2 record (3-file migration)** and **expose a scripted API**
|
|
55
|
+
(RecordScript + AclRecordScripts): see
|
|
56
|
+
[ACL Permission Chain](../../_underscore/features/acl-permission-chain.md).
|
|
57
|
+
- The response-envelope shape and code families: see [api2 architecture](../architecture.md).
|
|
58
|
+
|
|
59
|
+
## Change history
|
|
60
|
+
|
|
61
|
+
- 2026-07-17 — TRUE-79191: documented the EV-6/EV-8/EV-9/EZ-1 troubleshooting map (plus EV-5/EV-12)
|
|
62
|
+
discovered wiring the Signature Type field and the `addresses/validate` scripted route on Fulfill
|
|
63
|
+
& Ship — each code mapped to the missing migration (RecordField / AclFieldPermissions /
|
|
64
|
+
RecordScripts / AclRecordScripts) and the resolve-role-by-sibling technique. (mhammontree)
|
|
65
|
+
</content>
|
|
66
|
+
</invoke>
|
|
@@ -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/view/components/SelectedShipmentItemsTable.tsx, toga2-supply/src/pages/EditShipment/view/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/src/pages/EditShipment/viewModel/FIELDS/DUMMYUPDATESHIPMENTFIELDS.json, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/tailwind.config.cjs, toga2-supply/src/pages/EditShipment/view/modals/ReturnShippingModal.tsx, toga2-supply/src/styles/index.scss, toga2-supply/src/components/ui/BaseInput/BaseInput.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/formatShipmentData.ts, toga2-supply/src/pages/EditShipment/types.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/Shipments/types.ts, toga2-supply/src/pages/FulfilledShipments/view/FulfilledShipmentsPage.tsx, toga2-supply/src/components/ui/CardTable/CardTable.tsx, toga2-supply/src/components/ui/CardTable/types.ts, toga2-supply/src/assets/pen-line.svg, _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/view/components/SelectedShipmentItemsTable.tsx, toga2-supply/src/pages/EditShipment/view/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/src/pages/EditShipment/viewModel/FIELDS/DUMMYUPDATESHIPMENTFIELDS.json, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/renderEditShipmentFormInput.tsx, toga2-supply/tailwind.config.cjs, toga2-supply/src/pages/EditShipment/view/modals/ReturnShippingModal.tsx, toga2-supply/src/styles/index.scss, toga2-supply/src/components/ui/BaseInput/BaseInput.tsx, toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts, toga2-supply/src/pages/EditShipment/viewModel/signatureTypes.ts, toga2-supply/src/pages/EditShipment/view/modals/SelectReturnAddressModal.tsx, toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/formatShipmentData.ts, toga2-supply/src/pages/EditShipment/types.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/Shipments/types.ts, toga2-supply/src/pages/FulfilledShipments/view/FulfilledShipmentsPage.tsx, toga2-supply/src/components/ui/CardTable/CardTable.tsx, toga2-supply/src/components/ui/CardTable/types.ts, toga2-supply/src/assets/pen-line.svg, _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 |
|
|
@@ -21,6 +21,8 @@ files:
|
|
|
21
21
|
- toga2-supply/src/styles/index.scss
|
|
22
22
|
- toga2-supply/src/components/ui/BaseInput/BaseInput.tsx
|
|
23
23
|
- toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts
|
|
24
|
+
- toga2-supply/src/pages/EditShipment/viewModel/signatureTypes.ts
|
|
25
|
+
- toga2-supply/src/pages/EditShipment/view/modals/SelectReturnAddressModal.tsx
|
|
24
26
|
- toga2-supply/src/pages/EditShipment/helpers/ShipmentDetailsForm/formatShipmentData.ts
|
|
25
27
|
- toga2-supply/src/pages/EditShipment/types.ts
|
|
26
28
|
- toga2-supply/src/pages/Shipments/view/ShipmentsPage.tsx
|
|
@@ -209,6 +211,42 @@ bare string** — see the gotcha below before wiring a new select field.
|
|
|
209
211
|
12px check glyph. This is the **shared** checkbox, so the change applies **app-wide**, not just to
|
|
210
212
|
the shipment forms — audit other consumers when touching it.
|
|
211
213
|
|
|
214
|
+
## Signature Type — 4-level enum (2026-07)
|
|
215
|
+
|
|
216
|
+
Signature moved from a **boolean `requiresSignature`** to a **4-level `signatureType` enum**:
|
|
217
|
+
**None Specified / Indirect / Direct / Adult** (cards read "No / Indirect / Direct / Adult Signature
|
|
218
|
+
Required"). It persists on `TrackingNumbers.signatureType` and is now applied to the outbound label
|
|
219
|
+
(see the carrier-shipping-labels doc's signature-type mapping + the UPS "no Indirect tier" rule).
|
|
220
|
+
|
|
221
|
+
- **Single source of truth:** `src/pages/EditShipment/viewModel/signatureTypes.ts` holds the dropdown
|
|
222
|
+
options, the card-label helper `signatureCardLabel`, the legacy-boolean derivation
|
|
223
|
+
`requiresSignatureFromLevel`, and `SIGNATURE_CARRIER_MAP`. Change signature behavior here, not in
|
|
224
|
+
the components.
|
|
225
|
+
- **Back-compat:** create + edit save paths send `trackingNumber.signatureType` **and still derive
|
|
226
|
+
`requiresSignature`** (via `requiresSignatureFromLevel`) so older consumers keep working. The API
|
|
227
|
+
read field lists request `signatureType`; edit rehydrates from it; shipment cards render a computed
|
|
228
|
+
`signatureLabel` with a legacy fallback for rows saved before the enum.
|
|
229
|
+
- Backend: `TrackingNumbers.signatureType` column + `_Model_Client_TrackingNumber::$signatureType`
|
|
230
|
+
(registered via the 3-file V2-field migration — see the ACL doc).
|
|
231
|
+
- **The whole-object react-select rule still applies** — `signatureType`'s form value is the option
|
|
232
|
+
OBJECT `{uuid,name}`; map on `.uuid`, rehydrate the object (see the react-select gotcha below).
|
|
233
|
+
|
|
234
|
+
## Return Address validation modal — "Select Return Address" (2026-07)
|
|
235
|
+
|
|
236
|
+
Saving a return address now runs a **carrier validation step** (Figma node 3037-17080). On Save the
|
|
237
|
+
`ReturnShippingModal` calls `validateReturnAddress()` and **always** opens `SelectReturnAddressModal`,
|
|
238
|
+
which shows the carrier-normalized **Suggested Address** vs the **Original** as radios (the Suggested
|
|
239
|
+
heading always shows, with an empty state when the carriers return none), Cancel / Save; the chosen
|
|
240
|
+
address is applied.
|
|
241
|
+
|
|
242
|
+
- **Backend:** `validateReturnAddress()` (`UpdateShipmentApi.ts`) does `GET /addresses/validate`
|
|
243
|
+
(result at `response.data.addresses.validate`) — the migration-registered scripted route on the
|
|
244
|
+
existing USPS→FedEx→UPS `validateAddress` cascade (see the `_underscore` address-validation doc).
|
|
245
|
+
- **⚠ State-code gotcha:** the stored state form value is only `{uuid, name}`, but USPS needs the
|
|
246
|
+
**2-letter code**. `getStates` already fetches `code`, but the `EditShipmentPage` mapping was
|
|
247
|
+
**dropping** it — carry `code` through the state options and resolve it at validation time, or
|
|
248
|
+
validation fails on a missing/blank state.
|
|
249
|
+
|
|
212
250
|
## Planned direction — "Fulfill & Ship" from an existing Item Fulfillment (future, Eric)
|
|
213
251
|
|
|
214
252
|
A **third entry mode** is planned (distinct from Select-Items-create and the pencil-edit): a
|
|
@@ -459,6 +497,19 @@ not the base `_Model_Client_ItemFulfillment`. Tested with GroWrk; UPS support wa
|
|
|
459
497
|
for Compass and is not yet in prod.
|
|
460
498
|
|
|
461
499
|
## Change history
|
|
500
|
+
- 2026-07-17 — TRUE-79191: replaced the boolean `requiresSignature` with a **4-level `signatureType`
|
|
501
|
+
enum** (None Specified / Indirect / Direct / Adult) — `viewModel/signatureTypes.ts` is the single
|
|
502
|
+
source of truth (options, `signatureCardLabel`, `requiresSignatureFromLevel` back-compat,
|
|
503
|
+
`SIGNATURE_CARRIER_MAP`); save paths send `signatureType` and still derive `requiresSignature`; the
|
|
504
|
+
level is now applied to the outbound label (carrier mapping in the carrier-shipping-labels doc).
|
|
505
|
+
Added the **"Select Return Address" validation modal** (`SelectReturnAddressModal.tsx`;
|
|
506
|
+
`validateReturnAddress()` GET `/addresses/validate` → `data.addresses.validate`; Save always
|
|
507
|
+
validates then shows Suggested-vs-Original) and documented the **state-code gotcha** (carry `code`
|
|
508
|
+
through state options; USPS needs the 2-letter code the `{uuid,name}` value drops). Also: app-wide
|
|
509
|
+
`BaseInput` select-focus now uses `border-1` + inset ring (was `border-2`, which shifted text on
|
|
510
|
+
focus) and `BasicTable` gained `stickyHeader`/`headerCellPadding`/`tableContainerMarginBottom` props;
|
|
511
|
+
the rest of the layout/overflow pass (AuthLayout gap rebalance, page top-gap removals, device-list
|
|
512
|
+
grow-to-cap, shipment-card `w-full`/`min-h-full`) was cosmetic. (mhammontree)
|
|
462
513
|
- 2026-07-15 — TRUE-79191: made the Edit Return Label **Addressee a searchable combobox** (native
|
|
463
514
|
`<input list>` + `<datalist>`, filters + allows custom text, prefills via `applyWarehouse`); added a
|
|
464
515
|
**backdrop-click discard guard** (guarded `BaseModal onClose` on `formState.isDirty`); restyled the
|
package/knowledge/INDEX.md
CHANGED
|
@@ -19,7 +19,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
19
19
|
|
|
20
20
|
- **_underscore** (_Underscore) _(framework core)_ — 33 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
21
21
|
- **worker2** (Worker) — 30 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
22
|
-
- **api2** (API) —
|
|
22
|
+
- **api2** (API) — 12 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
23
23
|
- **dbchanges2** (Database Changes) _(framework core)_ — 3 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
24
24
|
- **toga2-supply** (TOGa Supply) — 3 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
|
25
25
|
- **saml** (SAML SSO Gateway) — 3 doc(s) → [2.0/apps/saml/INDEX.md](2.0/apps/saml/INDEX.md)
|
package/package.json
CHANGED