Add Create Test Shipment (Email SKU) for testing the return-label flow, plus test-mode/store_id investigation notes

Adds synthetic source="test" tickets (create_test_shipment_order/delete_test_shipments)
carrying a company's real emailed-return-label SKU, so Step 1/Step 2 can be exercised
against the sandbox without a real JIRA ticket or risking a real customer's. Gated to
Test Mode - the same flow against production would create a real paid shipment.

CLAUDE.md also captures this session's ShipStation sandbox findings: store_id is
schema-optional but empirically required for label visibility, the newer ship15 web UI's
URL is NOT the se- store ID (confirmed via direct probe - different failure shape than a
wrong-but-well-formed ID), and two separate test stores/carriers are needed per company,
mirroring production.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
2026-09-28 11:45:09 -05:00
co-authored by Claude Sonnet 5
parent 82e6148ca9
commit a7d2e7870c
3 changed files with 259 additions and 11 deletions
+88 -11
View File
@@ -29,7 +29,10 @@ app/
ticket_validation.py SKU validation rules (company mismatch, return/device mismatch)
tracking.py suggest_jira_status() from tracking numbers
external_links.py JIRA/Google Maps URL builders (View in JIRA, Look Up Address)
workers.py QThread workers + save_orders()/load_orders_by_view()/etc.
workers.py QThread workers + save_orders()/load_orders_by_view()/etc. -
also create_test_shipment_order()/delete_test_shipments()
(synthetic source="test" tickets for exercising the
Create Return Label flow without a real JIRA ticket)
services/
base.py NormalizedOrder TypedDict, OrderService ABC
jira_service.py JIRA REST API v3 /search/jql, paginated
@@ -182,6 +185,59 @@ and copy its store_id into `TEST_SHIPSTATION_SIGNIFY_STORE_ID` /
"Return label creation failed" / "invalid store" error on `store_id: 367672` (Oak
Street's *production* `se-367672`, prefix stripped in the echoed error per point 8
above) - happening because `TEST_SHIPSTATION_OAKSTREET_STORE_ID` was still blank.
The fix requires **two separate manual stores in the sandbox account** (one per
company), not one shared - mirrors production, which already has two independent
store IDs (`SHIPSTATION_SIGNIFY_STORE_ID`/`SHIPSTATION_OAKSTREET_STORE_ID`), and the
`TEST_*` settings already have two independent slots for exactly this. There was no
way to confirm via API whether one store could technically be shared across
companies in the sandbox (still no listing/inspection endpoint) - moot anyway, since
per-company stores is the already-intended design.
**`store_id` is schema-optional, but don't take that as license to drop it**: checked
ShipStation's own request schema for `POST /v2/labels` - `shipment.store_id` is NOT
in the list of required fields (unlike `shipment.ship_to`), and nothing in the schema
ties a store to a specific carrier/warehouse. But this app's own store_id check
exists for a real, previously-confirmed reason (see Return Labels point 1 above): a
label can come back `status: completed` from the API while being invisible anywhere
in ShipStation's own UI. The schema being lenient doesn't mean this account's actual
behavior is - keep requiring it. **Confirmed live, 2026-09-18** (three direct
`POST /v2/labels` probes against the sandbox key, harmless - sandbox labels, no real
cost): omitting `store_id` entirely succeeds cleanly (`200`, `status: "completed"`,
real label `se-201075505`) - so it genuinely is optional API-side, exactly as the
schema says. Whether that label is actually visible in ShipStation's own UI (the
thing that would tell us if the "invisible label" concern really extends to
store_id, or was specific to unlinked return labels) still needs a human to check the
sandbox account's Orders/Shipments list for `external_shipment_id:
STORE-PROBE-no-store-id-at-all`.
**A separate, unrelated test-mode gap found 2026-09-18**: `TEST_SIGNIFY_RETURN_CARRIER_ID`/
`TEST_OAKSTREET_RETURN_CARRIER_ID` were blank (only the `*_SERVICE_CODE` halves had
been filled in earlier), producing "No return-label Carrier ID/Service Code
configured" on Step 1. Not a new discovery - just the confirmed shared sandbox test
carrier (`se-6366092`, UPS) from earlier in this section, re-verified live and filled
into both settings. Worth knowing for next time: `TEST_SIGNIFY_RETURN_SERVICE_CODE`/
`TEST_OAKSTREET_RETURN_SERVICE_CODE` being set doesn't imply their carrier-ID
counterparts are - check both halves of a `TEST_*_RETURN_*` pair, not just one.
**The newer ShipStation web UI's URL is NOT the `se-` store ID - confirmed, don't
reuse it.** Creating a manual store at `ship15.shipstation.com/settings/stores/...`
shows a UUID in the URL (e.g. `081cf783-32b2-4b52-b9f8-767531d0ac47`), not an
`se-XXXXX` ID. This is ShipStation's newer/redesigned web UI - that UUID is its own
internal routing ID, a completely different ID space from the V2 API's store IDs, not
just a missing `se-` prefix (the `se-` prefix quirk documented above only ever
applies to genuinely `se-`-shaped IDs missing their prefix, not arbitrary UUIDs).
Confirmed by direct probe: a known-fake-but-correctly-shaped ID (`se-999999999`)
gets the expected clean `400 "invalid store"`, but that UUID (tried both raw and
with `se-` prepended) gets a `500 "An unexpected error occurred"` instead - a
different failure shape entirely, meaning the API doesn't even recognize it as a
candidate ID, let alone a wrong one. **Don't paste that URL UUID into
`TEST_SHIPSTATION_*_STORE_ID` and expect it to work.** The real `se-` ID for a
store created in this newer UI needs to come from somewhere else - check the store's
own settings *page content* (not the URL) for an API/Integration section that
displays it as text, or fall back to ShipStation support (their own help docs say
this explicitly: "contact our support team and tell them the name of the manual
store" is a valid way to get a store_id when the List Stores API isn't an option -
see the store_id dead-end note above).
**Production readiness check, 2026-09-16 (read-only, no labels created, no cost)**:
before a first real production test of the return-label flow, ran `GET /v2/carriers`,
@@ -201,6 +257,21 @@ production API key and diffed the results against `.env`:
check. If it fails, expect the same "invalid store" error shape as the test-mode one;
the fix then is re-checking the store_id in ShipStation's own UI, not the code.
**Testing the emailed-return-label SKU flow without a real ticket**: "Create Test
Shipment (Email SKU)..." (Data menu, Test Mode only) creates a synthetic
`source="test"` Order carrying that company's configured emailed-return-label SKU
(resolved from `EMAILED_LABEL_SKUS` + `COMPANY_SKU_MAP` at creation time, not
hardcoded to SH007/OK012) and a placeholder shipping address, then drops it on the
Active tab (`status="Created"`) so it can be selected and run through the *real*
Create Return Label flow - same code path a JIRA ticket uses, no separate test-only
logic to keep in sync. `source="test"` guarantees `save_orders()` (which only ever
matches on `source == "jira"`) can never touch or overwrite one of these, so a real
Import from JIRA is safe to run with test shipments still sitting on the board.
"Delete Test Shipments..." cleans them up by that same `source` marker, real tickets
untouched. **Deliberately gated to Test Mode** - the same flow against production
settings would create a real, paid UPS shipment to a fake address, not just a
sandbox test label.
**Real limitations of ShipStation's sandbox (confirmed via their own docs, not
assumed) that constrain what test mode can actually verify:**
- Branded Labels / Branded Tracking Pages are NOT available in sandbox. This directly
@@ -289,16 +360,22 @@ change without a deliberate separate conversation about it.
## Known-pending / not yet built
- **Immediate next step, as of the last working session**: the warehouse_id issue is
now fixed code-side ("Create Test Warehouse(s)..." in the Data menu, see ShipStation
Test Mode above) but hasn't been run/confirmed yet - run it, confirm the two
`TEST_SHIPSTATION_*_WAREHOUSE_ID` settings got saved. Then a *second*, separate
test-mode gap surfaced right after: an "invalid store" error on Oak Street's
production store_id, because `TEST_SHIPSTATION_*_STORE_ID` is blank and there is no
API-based fix for that one (see ShipStation Test Mode above) - both
`TEST_SHIPSTATION_SIGNIFY_STORE_ID` and `TEST_SHIPSTATION_OAKSTREET_STORE_ID` still
need to be filled in by hand from the sandbox account's own ShipStation UI before the
emailed-return-label flow can be tested end-to-end in test mode.
- **Immediate next step, as of the last working session**: the warehouse_id gap is
fixed and confirmed (both `TEST_SHIPSTATION_*_WAREHOUSE_ID` settings are populated).
The store_id gap is still open and turned out to be more involved than the
warehouse one: two manual stores were created in the sandbox account, one per
company (see ShipStation Test Mode above for why two, not one), but ShipStation's
newer web UI (`ship15.shipstation.com`) only exposes a UUID in the URL, not the
`se-XXXXX` ID this app's `TEST_SHIPSTATION_*_STORE_ID` settings need - confirmed via
direct API probe that this UUID is not a usable store_id at all (a different,
`500`-shaped failure than a genuinely wrong-but-well-formed ID gets). The real
`se-` ID for each new store still needs to be found (store settings page content,
or ShipStation support) before the emailed-return-label flow can be tested
end-to-end in test mode. Also still open: whether a label with NO store_id at all
is actually visible in ShipStation's UI - confirmed live that the API accepts a
request with the field omitted (see ShipStation Test Mode above), which, if it
turns out to be visible too, could make chasing the real store_id unnecessary for
test mode specifically.
- **EOD JIRA push**: deliberately deferred. `packed`, `serial_numbers`,
`tracking_numbers`, `shipping_method`, `assignee` are all captured and ready for it
whenever it's prioritized - would need the exact JIRA custom field IDs (outbound