- Auto-fixed 4,496 lint issues (import sorting, modern syntax, etc.) - Added ignore rules for patterns intentional in this codebase: E402 (late imports), E712 (SQLAlchemy filters), B904 (raise from), SIM108/SIM105/SIM117 (readability preferences) - Added per-file ignores for tests and scripts - Excluded broken scripts/rename_terminology.py (has curly quotes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
204 lines
6.7 KiB
Python
204 lines
6.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Check shipment status directly from Letzshop API.
|
|
|
|
Usage:
|
|
python scripts/check_letzshop_shipment.py YOUR_API_KEY [SHIPMENT_ID_OR_ORDER_NUMBER]
|
|
|
|
Example:
|
|
python scripts/check_letzshop_shipment.py abc123 nvDv5RQEmCwbjo
|
|
python scripts/check_letzshop_shipment.py abc123 R532332163
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
|
|
import requests
|
|
|
|
ENDPOINT = "https://letzshop.lu/graphql"
|
|
|
|
# Query template - state is interpolated since Letzshop has enum issues
|
|
QUERY_SHIPMENTS_TEMPLATE = """
|
|
query GetShipmentsPaginated($first: Int!, $after: String) {{
|
|
shipments(state: {state}, first: $first, after: $after) {{
|
|
pageInfo {{
|
|
hasNextPage
|
|
endCursor
|
|
}}
|
|
nodes {{
|
|
id
|
|
number
|
|
state
|
|
order {{
|
|
id
|
|
number
|
|
email
|
|
total
|
|
completedAt
|
|
locale
|
|
shipAddress {{
|
|
firstName
|
|
lastName
|
|
merchant
|
|
streetName
|
|
streetNumber
|
|
city
|
|
zipCode
|
|
phone
|
|
country {{
|
|
iso
|
|
}}
|
|
}}
|
|
}}
|
|
inventoryUnits {{
|
|
id
|
|
state
|
|
variant {{
|
|
id
|
|
sku
|
|
mpn
|
|
price
|
|
tradeId {{
|
|
number
|
|
parser
|
|
}}
|
|
product {{
|
|
name {{
|
|
en
|
|
fr
|
|
de
|
|
}}
|
|
}}
|
|
}}
|
|
}}
|
|
}}
|
|
}}
|
|
}}
|
|
"""
|
|
|
|
|
|
def search_shipment(api_key: str, search_term: str):
|
|
"""Search for a shipment across all states."""
|
|
|
|
headers = {
|
|
"Content-Type": "application/json",
|
|
"Authorization": f"Bearer {api_key}",
|
|
}
|
|
|
|
# States to search through
|
|
states = ["shipped", "confirmed", "unconfirmed", "declined"]
|
|
|
|
print(f"Searching for: {search_term}")
|
|
print("=" * 60)
|
|
|
|
for state in states:
|
|
print(f"\nSearching in state: {state}...")
|
|
|
|
query = QUERY_SHIPMENTS_TEMPLATE.format(state=state)
|
|
|
|
# Paginate through results
|
|
has_next = True
|
|
after = None
|
|
page = 0
|
|
|
|
while has_next and page < 20: # Max 20 pages to avoid infinite loop
|
|
page += 1
|
|
variables = {"first": 50, "after": after}
|
|
|
|
response = requests.post(
|
|
ENDPOINT,
|
|
headers=headers,
|
|
json={"query": query, "variables": variables},
|
|
)
|
|
|
|
if response.status_code != 200:
|
|
print(f" Error: HTTP {response.status_code}")
|
|
break
|
|
|
|
data = response.json()
|
|
|
|
if "errors" in data:
|
|
print(f" GraphQL errors: {data['errors']}")
|
|
break
|
|
|
|
result = data.get("data", {}).get("shipments", {})
|
|
nodes = result.get("nodes", [])
|
|
page_info = result.get("pageInfo", {})
|
|
|
|
# Search for matching shipment
|
|
for shipment in nodes:
|
|
shipment_id = shipment.get("id", "")
|
|
shipment_number = shipment.get("number", "")
|
|
order = shipment.get("order", {})
|
|
order_number = order.get("number", "")
|
|
|
|
# Check if this matches our search term
|
|
if (search_term in (shipment_id, order_number) or search_term in shipment_id or search_term in shipment_number or search_term in order_number):
|
|
|
|
print(f"\n{'=' * 60}")
|
|
print("FOUND SHIPMENT!")
|
|
print(f"{'=' * 60}")
|
|
|
|
print("\n--- Shipment Info ---")
|
|
print(f" ID: {shipment.get('id')}")
|
|
print(f" Number: {shipment.get('number')}")
|
|
print(f" State: {shipment.get('state')}")
|
|
|
|
print("\n--- Order Info ---")
|
|
print(f" Order ID: {order.get('id')}")
|
|
print(f" Order Number: {order.get('number')}")
|
|
print(f" Email: {order.get('email')}")
|
|
print(f" Total: {order.get('total')}")
|
|
print(f" Completed At: {order.get('completedAt')}")
|
|
|
|
ship_addr = order.get("shipAddress", {})
|
|
if ship_addr:
|
|
print("\n--- Shipping Address ---")
|
|
print(f" Name: {ship_addr.get('firstName')} {ship_addr.get('lastName')}")
|
|
print(f" Street: {ship_addr.get('streetName')} {ship_addr.get('streetNumber')}")
|
|
print(f" City: {ship_addr.get('zipCode')} {ship_addr.get('city')}")
|
|
country = ship_addr.get("country", {})
|
|
print(f" Country: {country.get('iso')}")
|
|
|
|
print("\n--- Inventory Units ---")
|
|
units = shipment.get("inventoryUnits", [])
|
|
for i, unit in enumerate(units, 1):
|
|
print(f" Unit {i}:")
|
|
print(f" ID: {unit.get('id')}")
|
|
print(f" State: {unit.get('state')}")
|
|
variant = unit.get("variant", {})
|
|
print(f" SKU: {variant.get('sku')}")
|
|
trade_id = variant.get("tradeId", {})
|
|
print(f" GTIN: {trade_id.get('number')}")
|
|
product = variant.get("product", {})
|
|
name = product.get("name", {})
|
|
print(f" Product: {name.get('en')}")
|
|
|
|
print("\n--- Raw Response ---")
|
|
print(json.dumps(shipment, indent=2, default=str))
|
|
return shipment
|
|
|
|
has_next = page_info.get("hasNextPage", False)
|
|
after = page_info.get("endCursor")
|
|
|
|
if not has_next:
|
|
print(f" Searched {page} page(s), {len(nodes)} shipments in last page")
|
|
|
|
print(f"\nShipment not found for: {search_term}")
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python scripts/check_letzshop_shipment.py YOUR_API_KEY [SHIPMENT_ID_OR_ORDER_NUMBER]")
|
|
print("\nExample:")
|
|
print(" python scripts/check_letzshop_shipment.py abc123 nvDv5RQEmCwbjo")
|
|
print(" python scripts/check_letzshop_shipment.py abc123 R532332163")
|
|
sys.exit(1)
|
|
|
|
api_key = sys.argv[1]
|
|
# Default to the order number R532332163
|
|
search_term = sys.argv[2] if len(sys.argv) > 2 else "R532332163"
|
|
|
|
search_shipment(api_key, search_term)
|