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:
@@ -9,11 +9,13 @@ pattern rather than blocking the UI.
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime as dt
|
||||
import uuid
|
||||
from typing import List, Tuple, TypedDict
|
||||
|
||||
from PyQt6.QtCore import QThread, pyqtSignal
|
||||
from sqlalchemy import select, delete
|
||||
|
||||
from app import config
|
||||
from app.database import get_session
|
||||
from app.models import Order
|
||||
from app.schedule import get_cutoff_time, is_past_cutoff_today
|
||||
@@ -301,6 +303,97 @@ def reset_local_database() -> int:
|
||||
session.close()
|
||||
|
||||
|
||||
def _emailed_label_sku_for_company(company: str) -> str:
|
||||
"""Picks whichever configured EMAILED_LABEL_SKUS entry actually
|
||||
resolves to the given company via COMPANY_SKU_MAP, rather than
|
||||
hardcoding SH007/OK012 - stays correct if either setting changes."""
|
||||
from app.companies import parse_mapping, resolve_company_by_sku
|
||||
from app.return_labels import get_emailed_label_skus
|
||||
|
||||
sku_map = parse_mapping(config.get("COMPANY_SKU_MAP", config.DEFAULT_COMPANY_SKU_MAP))
|
||||
for sku in sorted(get_emailed_label_skus()):
|
||||
if resolve_company_by_sku(sku, sku_map) == company:
|
||||
return sku.upper()
|
||||
return ""
|
||||
|
||||
|
||||
def create_test_shipment_order(company: str) -> Order:
|
||||
"""
|
||||
Creates a synthetic, non-JIRA Order row carrying that company's
|
||||
configured emailed-return-label SKU, purely so the existing "Create
|
||||
Return Label" flow (return_label_dialog.py + shipstation_send.py) can
|
||||
be exercised end-to-end - dummy shipment, then real return label -
|
||||
against a throwaway ticket instead of risking a real customer's.
|
||||
|
||||
source="test" keeps this completely separate from real JIRA rows:
|
||||
save_orders() only ever matches on source == "jira"/"shipstation", so
|
||||
Import from JIRA can never touch or overwrite one of these, and
|
||||
delete_test_shipments() cleans them up by that same marker.
|
||||
|
||||
status="Created" so it lands on the Active tab like a real open
|
||||
ticket would - that's where staff would naturally go to select it and
|
||||
run Create Return Label.
|
||||
"""
|
||||
sku = _emailed_label_sku_for_company(company)
|
||||
if not sku:
|
||||
raise ValueError(
|
||||
f"No EMAILED_LABEL_SKUS entry resolves to '{company}' via COMPANY_SKU_MAP - "
|
||||
"check both settings."
|
||||
)
|
||||
|
||||
ticket_number = f"TEST-EMAIL-{uuid.uuid4().hex[:8].upper()}"
|
||||
|
||||
order = Order(
|
||||
source="test",
|
||||
external_id=ticket_number,
|
||||
ticket_number=ticket_number,
|
||||
company=company,
|
||||
skus=[sku],
|
||||
line_items=[{"sku": sku, "item_name": "TEST - Emailed Return Label"}],
|
||||
shipping_info={
|
||||
"name": "TEST ORDER - DO NOT SHIP",
|
||||
"phone": "555-555-5555",
|
||||
"email": "",
|
||||
"address1": "123 Test St",
|
||||
"address2": "",
|
||||
"city": "Austin",
|
||||
"state": "TX",
|
||||
"zip": "78701",
|
||||
},
|
||||
creator="Test Shipment Generator",
|
||||
assignee="",
|
||||
description=(
|
||||
"Synthetic test order created via Data > Create Test Shipment (Email SKU) - "
|
||||
"not a real ticket. Safe to delete with Data > Delete Test Shipments."
|
||||
),
|
||||
summary=f"TEST - Emailed Return Label ({company})",
|
||||
status="Created",
|
||||
source_created_at=dt.datetime.now(),
|
||||
)
|
||||
|
||||
session = get_session()
|
||||
try:
|
||||
session.add(order)
|
||||
session.commit()
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
return order
|
||||
|
||||
|
||||
def delete_test_shipments() -> int:
|
||||
"""Removes every synthetic order created by create_test_shipment_order()
|
||||
(source == "test") - real JIRA-sourced rows are untouched, since those
|
||||
always have source == "jira"."""
|
||||
session = get_session()
|
||||
try:
|
||||
result = session.execute(delete(Order).where(Order.source == "test"))
|
||||
session.commit()
|
||||
return result.rowcount or 0
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
|
||||
def load_all_orders() -> List[Order]:
|
||||
session = get_session()
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user