toga-ai 1.0.30 → 1.0.32
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/README.md +40 -0
- package/knowledge/2.0/apps/_underscore/INDEX.md +1 -0
- package/knowledge/2.0/apps/_underscore/features/carrier-shipping-labels.md +113 -0
- package/knowledge/2.0/apps/toga2-supply/INDEX.md +6 -0
- package/knowledge/2.0/apps/toga2-supply/architecture.md +73 -0
- package/knowledge/2.0/apps/toga2-supply/features/fulfill-and-ship.md +112 -0
- package/knowledge/INDEX.md +2 -1
- package/knowledge/registry.json +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,6 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
Shared Claude Code skills, configuration, and the team **knowledge base** for TOGA Technology projects.
|
|
4
4
|
|
|
5
|
+
## Onboarding — install on a new machine
|
|
6
|
+
|
|
7
|
+
Do this once per developer. The harness has two independent parts: the **ECC plugin**
|
|
8
|
+
(provides the `ecc:*` reviewer agents the TOGA agents delegate to) and the **TOGA harness**
|
|
9
|
+
(`npx toga-ai`, which installs the skills, agents, rules, and knowledge base). Install order
|
|
10
|
+
between the two does not matter.
|
|
11
|
+
|
|
12
|
+
**Prerequisites** (must already be installed):
|
|
13
|
+
- **Node.js ≥ 18** — required by `npx` and the installer.
|
|
14
|
+
- **Git** — required for `/capture` to push and for knowledge auto-updates.
|
|
15
|
+
- **Claude Code** — the `claude` CLI.
|
|
16
|
+
|
|
17
|
+
**Steps:**
|
|
18
|
+
|
|
19
|
+
1. Open a terminal in VS Code and launch Claude Code:
|
|
20
|
+
```
|
|
21
|
+
claude
|
|
22
|
+
```
|
|
23
|
+
2. Add the ECC marketplace and install the plugin, then reload:
|
|
24
|
+
```
|
|
25
|
+
/plugin marketplace add https://github.com/affaan-m/ECC
|
|
26
|
+
/plugin install ecc@ecc
|
|
27
|
+
/reload-plugins
|
|
28
|
+
```
|
|
29
|
+
3. Open a **new terminal**, and **from inside the project repo you'll work in**, run:
|
|
30
|
+
```
|
|
31
|
+
npx toga-ai
|
|
32
|
+
```
|
|
33
|
+
This installs the harness into that project's `.claude/` and clones the shared knowledge
|
|
34
|
+
repo to `~/toga-tech`. **Run it once in each project folder** you work in.
|
|
35
|
+
4. Start a new Claude Code session. Begin every session with `/kickoff`, and run `/capture`
|
|
36
|
+
whenever you have something to add to the team knowledge base.
|
|
37
|
+
|
|
38
|
+
> **Push access is required to contribute.** `/capture` pushes to `_main` on
|
|
39
|
+
> `agilantsolutions/claude`. Each developer must be a **collaborator on that repo with git
|
|
40
|
+
> credentials configured** (HTTPS token / credential manager or SSH). Without it, `/capture`
|
|
41
|
+
> still writes docs locally but the push fails silently — so the developer won't be
|
|
42
|
+
> contributing knowledge back. `npx toga-ai` then delivers everyone's captured knowledge to
|
|
43
|
+
> the whole team on each run.
|
|
44
|
+
|
|
5
45
|
## Skills
|
|
6
46
|
|
|
7
47
|
| Skill | Description |
|
|
@@ -3,4 +3,5 @@
|
|
|
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/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
7
|
| [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 |
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Carrier Shipping Labels (UPS/FedEx) & NetSuite Item Fulfillment
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: _underscore
|
|
5
|
+
project: _Underscore
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-10
|
|
10
|
+
owners: [mhammontree]
|
|
11
|
+
files:
|
|
12
|
+
- _underscore/Model/Client/ItemFulfillment.php
|
|
13
|
+
- _underscore/Component/Library/Carriers/Ups/Ups.php
|
|
14
|
+
- _underscore/Trait/Netsuite/ItemFulfillment.php
|
|
15
|
+
- _underscore/Trait/Netsuite/SalesOrder.php
|
|
16
|
+
- _underscore/Component/Library/NetSuite/NetSuite.php
|
|
17
|
+
- _underscore/Model/Client/TrackingNumber.php
|
|
18
|
+
- _underscore/Model/Client/ShippingMethod.php
|
|
19
|
+
- _underscore/Model.php
|
|
20
|
+
- _underscore/Cloud.php
|
|
21
|
+
related:
|
|
22
|
+
- ../architecture.md
|
|
23
|
+
- recursive-item-fulfillments.md
|
|
24
|
+
- ../../toga2-supply/features/fulfill-and-ship.md
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Summary
|
|
28
|
+
|
|
29
|
+
Backend mechanics behind TOGa Supply's Fulfill & Ship: buying a carrier label
|
|
30
|
+
(UPS/FedEx), persisting it, and creating the NetSuite Item Fulfillment with tracking
|
|
31
|
+
number and label attached. Includes the **authoritative label-storage architecture
|
|
32
|
+
decision** (dev lead Jeff, June 2026) that supersedes what the code currently does.
|
|
33
|
+
|
|
34
|
+
## Key files / entry points
|
|
35
|
+
|
|
36
|
+
- `upsShipmentApi` / `fedexShipmentApi` (scripted APIs on
|
|
37
|
+
`Model/Client/ItemFulfillment.php`): build the carrier request, call the carrier,
|
|
38
|
+
(currently) convert ZPL→PDF via Labelary, save the PDF to NetSuite File Cabinet +
|
|
39
|
+
the DB storage field, return `{ trackingNumber, pdfLabel, fileInternalId }` or
|
|
40
|
+
`{ error, trackingNumber }`. UPS path now checks `$shipmentResponse->success` and
|
|
41
|
+
fast-fails on an empty service code (it previously swallowed UPS errors and surfaced
|
|
42
|
+
a misleading "Failed to save PDF to NetSuite").
|
|
43
|
+
- `_Component_Library_Carriers_Ups::submitShipmentRequest`
|
|
44
|
+
(`Component/Library/Carriers/Ups/Ups.php`): returns `{ success, errorMessage,
|
|
45
|
+
trackingNumber, encodedLabel }`.
|
|
46
|
+
- `createNetsuiteItemFulfillment` (`Trait/Netsuite/ItemFulfillment.php`): builds the NS
|
|
47
|
+
IF from the SO, assigns serialized inventory, sets packages/tracking numbers.
|
|
48
|
+
**Returns the numeric NS internalId on success, an error string on failure**
|
|
49
|
+
(ambiguous return — callers must check `/^\d+$/`). Takes
|
|
50
|
+
`$labelFileInternalId = null` and attaches the label to the IF after creation
|
|
51
|
+
(`attachFileToRecord` was widened `private` → `protected` so the trait can call it
|
|
52
|
+
from client subclasses).
|
|
53
|
+
- The trait is composed into **client-specific** subclasses
|
|
54
|
+
(`_Model_Growrk_ItemFulfillment extends _Model_Client_ItemFulfillment`), not the base
|
|
55
|
+
model.
|
|
56
|
+
|
|
57
|
+
## Label-storage architecture (AUTHORITATIVE — dev lead Jeff, 2026-06)
|
|
58
|
+
|
|
59
|
+
Supersedes the Labelary-PDF + manual-S3 approach:
|
|
60
|
+
|
|
61
|
+
1. **Do NOT touch S3 by hand.** The model's `FIELD_STORAGE` abstraction, accessed via
|
|
62
|
+
the 2.0 API, already handles label fetch + storage.
|
|
63
|
+
2. **Store the raw carrier response as a GIF**, not a Labelary-converted PDF.
|
|
64
|
+
3. **Generate the printable PDF on demand**, combining the shipping label AND the
|
|
65
|
+
return label into one PDF (the browser print dialog handles one file at a time).
|
|
66
|
+
Leaning **backend** generation — `pdf-lib` (client-side) cannot embed GIF.
|
|
67
|
+
|
|
68
|
+
Not yet implemented. Open questions before building: backend vs frontend for the
|
|
69
|
+
GIF→PDF step; which existing 2.0 label-fetch/storage calls to build on. Combining
|
|
70
|
+
shipping + return also depends on the return-label flow existing — outbound-only can
|
|
71
|
+
come first.
|
|
72
|
+
|
|
73
|
+
## `FIELD_STORAGE` mechanics (reference)
|
|
74
|
+
|
|
75
|
+
`Model.php` handles storage fields separately from regular columns (write loop ~line
|
|
76
|
+
393; read ~line 730). Config keys in `[_underscore]`: `model_storage_folder` → local
|
|
77
|
+
folder mode (if set, S3 is never used); `model_storage_s3_bucket` +
|
|
78
|
+
`model_storage_s3_folder` → S3 mode. Storage key/path (both modes):
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
<folder-or-s3-prefix>/<env (_Environment::$name)>/<database>/<TABLE>/<field>/<primaryKeyId>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
e.g. `<prefix>/beta/Client_Growrk/TrackingNumbers/labelPdfFile/<id>` — named by numeric
|
|
85
|
+
PK, **no extension**. Reads return the raw bytes verbatim (no base64/data-uri); writes
|
|
86
|
+
store whatever value was assigned. `_Cloud::copyFileToS3` **re-throws** on AWS errors —
|
|
87
|
+
if no exception reached the caller and execution continued, the write was never
|
|
88
|
+
attempted.
|
|
89
|
+
|
|
90
|
+
## Gotchas / known issues
|
|
91
|
+
|
|
92
|
+
- UPS **test** endpoint (`wwwcie.ups.com`, debug mode) returns a constant tracking
|
|
93
|
+
number `1ZXXXXXXXXXXXXXXXX` + SAMPLE labels → `TrackingNumbers.number` UNIQUE
|
|
94
|
+
collision and File Cabinet duplicate-filename failures on repeat tests (see the
|
|
95
|
+
fulfill-and-ship doc for the cleanup SQL). Never in prod.
|
|
96
|
+
- `upsShipmentApi` reads ship-to from the **Sales Order** (`shipToAddressId`), not the
|
|
97
|
+
Item Fulfillment — form-edited addresses never reach UPS.
|
|
98
|
+
- UPS `AddressLine` max 35 chars/line; send an array of trimmed lines (FedEx already
|
|
99
|
+
does), not `line1 . ' ' . line2`.
|
|
100
|
+
- `ShippingMethods.code` is carrier-scoped: UPS numeric (`03` Ground, `01` Next Day
|
|
101
|
+
Air), FedEx strings (`FEDEX_GROUND`). Empty code → UPS error 120500.
|
|
102
|
+
- Serialized-inventory assignment fails ("Invalid issueinventorynumber reference key")
|
|
103
|
+
when the lookup isn't location-scoped and the SO has no Warehouse Location —
|
|
104
|
+
recommended: always scope `getInventoryNumberFromSerialNumber` by location.
|
|
105
|
+
- `createNetsuiteItemFulfillment` doesn't set carrier/method on the NS IF — it defaults
|
|
106
|
+
(can show FedEx for a UPS shipment). Open item.
|
|
107
|
+
|
|
108
|
+
## Related docs
|
|
109
|
+
|
|
110
|
+
- [Fulfill & Ship (toga2-supply)](../../toga2-supply/features/fulfill-and-ship.md) —
|
|
111
|
+
frontend flow and success gating.
|
|
112
|
+
- [Recursive Item Fulfillments](recursive-item-fulfillments.md) — interceptor-driven
|
|
113
|
+
upstream mirroring on the same models.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# toga2-supply (TOGa Supply) — 2.0 knowledge
|
|
2
|
+
|
|
3
|
+
| Doc | Summary | Files |
|
|
4
|
+
|-----|---------|-------|
|
|
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 |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: TOGa Supply (toga2-supply) Architecture
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: toga2-supply
|
|
5
|
+
project: TOGa Supply
|
|
6
|
+
client: shared
|
|
7
|
+
type: architecture
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-10
|
|
10
|
+
owners: [mhammontree]
|
|
11
|
+
files:
|
|
12
|
+
- toga2-supply/src/api/toga.ts
|
|
13
|
+
- toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx
|
|
14
|
+
- toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx
|
|
15
|
+
- toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts
|
|
16
|
+
- toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx
|
|
17
|
+
related:
|
|
18
|
+
- ../_underscore/architecture.md
|
|
19
|
+
- ../api2/architecture.md
|
|
20
|
+
- features/fulfill-and-ship.md
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Summary
|
|
24
|
+
|
|
25
|
+
`toga2-supply` is the **React + Vite frontend** for TOGa Supply — warehouse fulfillment
|
|
26
|
+
tooling (shipment selection, fulfill & ship against carrier APIs, NetSuite Item
|
|
27
|
+
Fulfillment creation, label printing/reprinting). It is a pure frontend: all business
|
|
28
|
+
logic lives in the 2.0 API (`api2` engine over `_underscore` models). It has **no
|
|
29
|
+
backend code of its own** — `dependsOn: [api2]`.
|
|
30
|
+
|
|
31
|
+
## Topology (dev/test setup)
|
|
32
|
+
|
|
33
|
+
- The frontend runs **locally** on the developer machine (e.g.
|
|
34
|
+
`http://growrk.togasupply:5173`); it is not deployed to beta in this setup. Local
|
|
35
|
+
code edits take effect on reload.
|
|
36
|
+
- It calls the **beta** API at `https://api.beta.togahub.com`. Backend (`_underscore`)
|
|
37
|
+
changes therefore require a **deploy to beta** before they are observable — a symptom
|
|
38
|
+
can look "unfixed" simply because the backend fix isn't deployed yet.
|
|
39
|
+
- Client subdomain selects the tenant (e.g. `growrk.togasupply` → client GroWrk,
|
|
40
|
+
DB `Client_Growrk`).
|
|
41
|
+
|
|
42
|
+
## Shared API client — `src/api/toga.ts`
|
|
43
|
+
|
|
44
|
+
`togaApiRequest` resolves the API base from the hostname, auto-bootstraps auth
|
|
45
|
+
(`/auth/public` / `/auth/refresh`), and **retries once on 401**. The recurring 401s
|
|
46
|
+
seen at page load are this retry/refresh pattern (usually transient) — the real failure
|
|
47
|
+
mode is a non-401 status (400/business error) carrying a message.
|
|
48
|
+
|
|
49
|
+
Response envelope (2.0 API): `{ isSuccess, status, error, messages, data: { <route>:
|
|
50
|
+
{ <method>: ... } } }`. Scripted-API results nest as `data.<routeCamel>.<methodName>`.
|
|
51
|
+
|
|
52
|
+
## Route map
|
|
53
|
+
|
|
54
|
+
| Route | Page | Purpose |
|
|
55
|
+
|---|---|---|
|
|
56
|
+
| `/shipment-items?internalId=<NS SO id>` | `ShipmentItemsPage` | select SO line items; the **only** place the SO syncs from NetSuite into Toga; stores selection in `localStorage("selectedItems")` |
|
|
57
|
+
| `/edit-shipment?internalId=<id>` | `EditShipmentPage` → `EditShipmentForm` | serials, carrier, method → **Fulfill & Ship** |
|
|
58
|
+
| `/shipments`, `/fulfilled-shipments` | `ShipmentsCardTableForm` | pending vs fulfilled views; reprint lives here |
|
|
59
|
+
|
|
60
|
+
## Key decisions
|
|
61
|
+
|
|
62
|
+
- Carrier dropdown filters to FEDEX/UPS carriers that have ≥1 account number.
|
|
63
|
+
- Success/failure gating: a fulfillment is only "green" when the carrier returned a real
|
|
64
|
+
tracking number AND NetSuite returned a numeric internalId (`/^\d+$/`) — see the
|
|
65
|
+
fulfill-and-ship feature doc.
|
|
66
|
+
- Browser print: the print dialog handles one file at a time and `window.print()` fires
|
|
67
|
+
once per session — hence the combined-PDF reprint design. Popups must be allowed
|
|
68
|
+
(label window is `window.open`).
|
|
69
|
+
|
|
70
|
+
## Related docs
|
|
71
|
+
|
|
72
|
+
- [Fulfill & Ship feature](features/fulfill-and-ship.md) — the end-to-end flow, bugs, gotchas.
|
|
73
|
+
- [Carrier shipping labels (_underscore)](../_underscore/features/carrier-shipping-labels.md) — backend label + NetSuite mechanics.
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Fulfill & Ship
|
|
3
|
+
framework: "2.0"
|
|
4
|
+
repo: toga2-supply
|
|
5
|
+
project: TOGa Supply
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-06-10
|
|
10
|
+
owners: [mhammontree]
|
|
11
|
+
files:
|
|
12
|
+
- toga2-supply/src/pages/ShipmentItems/view/ShipmentItemsPage.tsx
|
|
13
|
+
- toga2-supply/src/pages/EditShipment/view/EditShipmentPage.tsx
|
|
14
|
+
- toga2-supply/src/pages/EditShipment/view/components/forms/EditShipmentForm.tsx
|
|
15
|
+
- toga2-supply/src/pages/EditShipment/api/UpdateShipmentApi.ts
|
|
16
|
+
- toga2-supply/src/pages/Shipments/view/components/ShipmentsCardTableForm/ShipmentsCardTableForm.tsx
|
|
17
|
+
- toga2-supply/src/pages/Shipments/api/ShipmentsApi.ts
|
|
18
|
+
- _underscore/Model/Client/ItemFulfillment.php
|
|
19
|
+
- _underscore/Trait/Netsuite/ItemFulfillment.php
|
|
20
|
+
- _underscore/Component/Library/Carriers/Ups/Ups.php
|
|
21
|
+
related:
|
|
22
|
+
- ../architecture.md
|
|
23
|
+
- ../../_underscore/features/carrier-shipping-labels.md
|
|
24
|
+
- ../../_underscore/features/recursive-item-fulfillments.md
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Summary
|
|
28
|
+
|
|
29
|
+
Fulfill & Ship lets a warehouse user select sales-order line items, enter serials,
|
|
30
|
+
pick a carrier/method, and in one action: create the Item Fulfillment records in Toga,
|
|
31
|
+
buy a carrier label (UPS/FedEx), and create the NetSuite Item Fulfillment with the
|
|
32
|
+
tracking number and label attached. Driven green end-to-end on beta (UPS, client
|
|
33
|
+
GroWrk, June 2026); FedEx + UPS both need verification before prod.
|
|
34
|
+
|
|
35
|
+
## How it works
|
|
36
|
+
|
|
37
|
+
1. `/shipment-items` load: `checkIfSalesOrderExists(internalId)` (GET `/sales-orders`
|
|
38
|
+
by `c_netsuiteInternalSalesOrderId`); if missing →
|
|
39
|
+
`GET /sales-orders/syncNetsuiteSalesOrder` then `syncNetsuiteItemFulfillmentWithToga`.
|
|
40
|
+
**This is the only place the SO syncs from NetSuite into Toga.**
|
|
41
|
+
2. `/edit-shipment` submit (`onFormSubmit("fulfillAndShip")` in `EditShipmentForm.tsx`):
|
|
42
|
+
- `saveShipment` → POST `/item-fulfillments`, `/tracking-numbers`,
|
|
43
|
+
`/item-fulfillment-packages`.
|
|
44
|
+
- `fulfillShipment` → GET `/item-fulfillments/upsShipmentApi` (or `fedexShipmentApi`)
|
|
45
|
+
for the label, then `saveShipmentToNetsuite` →
|
|
46
|
+
GET `/item-fulfillments/createNetsuiteItemFulfillment`.
|
|
47
|
+
- `handleShipmentDownload` opens the label PDF and triggers print.
|
|
48
|
+
3. **Success gating**: `fulfillShipment` returns `{ success, error, data }` and requires
|
|
49
|
+
BOTH a real tracking number (no `error`) AND a numeric NetSuite internalId
|
|
50
|
+
(`/^\d+$/` — `createNetsuiteItemFulfillment` returns the numeric internalId on
|
|
51
|
+
success but an **error string** on failure). Both callers gate the success toaster
|
|
52
|
+
on `success`.
|
|
53
|
+
4. The label `fileInternalId` is passed through `fulfillShipment` →
|
|
54
|
+
`saveShipmentToNetsuite` → `createNetsuiteItemFulfillment`, which attaches the label
|
|
55
|
+
to the IF **after** creating it (the IF doesn't exist yet when the label is bought).
|
|
56
|
+
|
|
57
|
+
## Reprint (first pass — being reworked)
|
|
58
|
+
|
|
59
|
+
Fulfilled-shipments view: multi-select shipments, merge each stored label into one
|
|
60
|
+
multi-page PDF with `pdf-lib`, print once. **Superseded by the label-storage decision**
|
|
61
|
+
(see the carrier-shipping-labels doc): labels will be stored as raw carrier GIFs and
|
|
62
|
+
the combined PDF generated on demand — note `pdf-lib` cannot embed GIF, so generation
|
|
63
|
+
likely moves to the backend.
|
|
64
|
+
|
|
65
|
+
## Gotchas / known issues
|
|
66
|
+
|
|
67
|
+
- **UPS test endpoint** (`wwwcie.ups.com`, debug mode) returns a constant tracking
|
|
68
|
+
number `1ZXXXXXXXXXXXXXXXX` + SAMPLE labels. On 2nd+ test runs this collides with the
|
|
69
|
+
`TrackingNumbers.number` UNIQUE index (surfaces mislabeled as "Labelary API error")
|
|
70
|
+
AND NetSuite File Cabinet's unique filename (`ShippingLabel_<tracking>.pdf`). Free
|
|
71
|
+
both between runs:
|
|
72
|
+
`UPDATE TrackingNumbers SET number = CONCAT(number,'-',id) WHERE number='1ZXXXXXXXXXXXXXXXX'`
|
|
73
|
+
and delete/rename the prior File Cabinet file. Never happens in prod.
|
|
74
|
+
- **Address source mismatch**: `upsShipmentApi` reads ship-to from the **Sales Order**
|
|
75
|
+
(`SalesOrder.shipToAddressId`), NOT the address edited in the form (the IF's) — form
|
|
76
|
+
address edits never reach the carrier. Open decision: read the IF address instead.
|
|
77
|
+
- **UPS address limits**: `AddressLine` max 35 chars/line; code currently sends
|
|
78
|
+
`line1 + ' ' + line2` as one line — should send a trimmed array (FedEx path already
|
|
79
|
+
does).
|
|
80
|
+
- **Service codes are carrier-scoped**: UPS numeric (`03`=Ground, `01`=Next Day Air),
|
|
81
|
+
FedEx strings (`FEDEX_GROUND`), in `ShippingMethods.code`. Empty code → UPS 120500;
|
|
82
|
+
there is now a fast-fail guard.
|
|
83
|
+
- **Serialized inventory needs a location**: a SO with no Warehouse Location makes
|
|
84
|
+
NetSuite reject the serial issue ("Invalid issueinventorynumber reference key").
|
|
85
|
+
Recommended hardening: always scope `getInventoryNumberFromSerialNumber` by location.
|
|
86
|
+
- **EV-12 on POST /item-fulfillments** means the SO isn't synced into Toga yet — fire
|
|
87
|
+
`GET /sales-orders/syncNetsuiteSalesOrder?netsuiteInternalSalesOrderId=<id>`.
|
|
88
|
+
- **Carrier on the NS IF defaults wrong**: `createNetsuiteItemFulfillment` doesn't set
|
|
89
|
+
carrier/method, so the IF can show FedEx while the tracking number is UPS. Open item.
|
|
90
|
+
- Real carrier errors live in the client-logs `Api` table (DIRECTION `OUT`,
|
|
91
|
+
`hostname LIKE '%ups.com%'`, `responsePayload`).
|
|
92
|
+
- React warning: `setState`-in-render in `ShipmentItemsTable.tsx` (~line 149,
|
|
93
|
+
`clearErrors` inside a `setRowsClicked` updater) — move out of the updater.
|
|
94
|
+
|
|
95
|
+
## Remaining work
|
|
96
|
+
|
|
97
|
+
- Label storage + reprint rework per the carrier-shipping-labels doc (raw GIF +
|
|
98
|
+
on-demand combined PDF).
|
|
99
|
+
- Return-label flow: `returnTrackingNumberId` (nullable) on `ItemFulfillmentItemUnits`;
|
|
100
|
+
"needs return label" checkbox; return-address editable combo ("Rolodex pattern" of
|
|
101
|
+
client locations, overridable, validated); "return label" text on return pages of the
|
|
102
|
+
combined PDF.
|
|
103
|
+
- Address-source decision, UPS 35-char hardening, location-scoped inventory lookup,
|
|
104
|
+
carrier/method on the NS IF, responsiveness per Figma, FedEx + UPS end-to-end
|
|
105
|
+
verification before prod.
|
|
106
|
+
|
|
107
|
+
## Client variations
|
|
108
|
+
|
|
109
|
+
None in the flow itself — but the NetSuite trait is composed into **client-specific**
|
|
110
|
+
model subclasses (e.g. `_Model_Growrk_ItemFulfillment uses _Trait_Netsuite_ItemFulfillment`),
|
|
111
|
+
not the base `_Model_Client_ItemFulfillment`. Tested with GroWrk; UPS support was built
|
|
112
|
+
for Compass and is not yet in prod.
|
package/knowledge/INDEX.md
CHANGED
|
@@ -9,10 +9,11 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
9
9
|
|
|
10
10
|
## 2.0 framework
|
|
11
11
|
|
|
12
|
-
- **_underscore** (_Underscore) _(framework core)_ —
|
|
12
|
+
- **_underscore** (_Underscore) _(framework core)_ — 3 doc(s) → [2.0/apps/_underscore/INDEX.md](2.0/apps/_underscore/INDEX.md)
|
|
13
13
|
- **worker2** (Worker) — 3 doc(s) → [2.0/apps/worker2/INDEX.md](2.0/apps/worker2/INDEX.md)
|
|
14
14
|
- **api2** (API) — 1 doc(s) → [2.0/apps/api2/INDEX.md](2.0/apps/api2/INDEX.md)
|
|
15
15
|
- **dbchanges2** (Database Changes) _(framework core)_ — 1 doc(s) → [2.0/apps/dbchanges2/INDEX.md](2.0/apps/dbchanges2/INDEX.md)
|
|
16
|
+
- **toga2-supply** (TOGa Supply) — 2 doc(s) → [2.0/apps/toga2-supply/INDEX.md](2.0/apps/toga2-supply/INDEX.md)
|
|
16
17
|
|
|
17
18
|
## Clients
|
|
18
19
|
|
package/knowledge/registry.json
CHANGED
|
@@ -4,5 +4,6 @@
|
|
|
4
4
|
{ "repo": "api2", "project": "API", "framework": "2.0", "role": "app", "dependsOn": [] },
|
|
5
5
|
{ "repo": "dbchanges2", "project": "Database Changes", "framework": "2.0", "role": "core", "dependsOn": [] },
|
|
6
6
|
{ "repo": "library", "project": "Library", "framework": "1.0", "role": "core", "dependsOn": [] },
|
|
7
|
-
{ "repo": "worker", "project": "Worker", "framework": "1.0", "role": "app", "dependsOn": [] }
|
|
7
|
+
{ "repo": "worker", "project": "Worker", "framework": "1.0", "role": "app", "dependsOn": [] },
|
|
8
|
+
{ "repo": "toga2-supply", "project": "TOGa Supply", "framework": "2.0", "role": "app", "dependsOn": ["api2"] }
|
|
8
9
|
]
|
package/package.json
CHANGED