"""Orders router. POST /api/orders (body: {customer_email, items:[{product_id,qty}]}) -> creates order, routes to highest-scoring supplier, records profit GET /api/orders GET /api/orders/{id} POST /api/orders/{id}/tracking (simulate supplier tracking push) """ import uuid from datetime import datetime from decimal import Decimal from typing import List, Optional from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from app.database import get_db from app.engines.payments import btcpay from app.routers.auth import require_admin from app.engines.order_router import route_order from app.models import AuditLog, Customer, Order, Product from app.schemas import CheckoutResponse, CustomerOrderOut, OrderCreate, OrderOut, TrackingIn router = APIRouter() TRACKING_STATUSES = {"new", "paid", "fraud_check", "supplier_order", "confirmed"} def _get_order_or_404(db, order_id): order = db.query(Order).filter(Order.id == order_id).first() if order is None: raise HTTPException(status_code=404, detail="order not found") return order def _new_order_number() -> str: return f"POL-{datetime.utcnow():%Y%m%d}-{uuid.uuid4().hex[:6].upper()}" @router.post("", response_model=CheckoutResponse) def create_order(body: OrderCreate, db: Session = Depends(get_db)): email = body.customer_email.strip().lower() customer = db.query(Customer).filter(Customer.email == email).first() if customer is None: customer = Customer(email=email, name=email.split("@")[0]) db.add(customer) db.flush() items = [] retail_total = Decimal("0") for item in body.items: product = db.query(Product).filter(Product.id == item.product_id).first() if product is None: raise HTTPException(status_code=404, detail=f"product {item.product_id} not found") price = product.retail_price if price is None or Decimal(str(price)) <= 0: raise HTTPException(status_code=400, detail=f"product {product.sku} has no retail price") items.append( { "product_id": str(product.id), "sku": product.sku, "qty": item.qty, "unit_price": float(price), } ) retail_total += Decimal(str(price)) * item.qty order = Order( order_number=_new_order_number(), customer_id=customer.id, items=items, retail_total=retail_total, status="pending_payment", ) db.add(order) db.flush() result = route_order(db, order) if not result.get("routed"): raise HTTPException(status_code=400, detail="no eligible supplier for this order") checkout_url = None try: inv = btcpay.create_invoice(float(order.retail_total), order.order_number) order.btcpay_invoice_id = inv.get("invoice_id") checkout_url = inv.get("checkout_url") db.commit() except Exception as e: db.rollback() raise HTTPException(status_code=502, detail=f"payment provider unavailable: {e}") db.refresh(order) return CheckoutResponse(order=order, checkout_url=checkout_url) @router.get("", response_model=List[OrderOut]) def list_orders(status: Optional[str] = None, db: Session = Depends(get_db), _auth: dict = Depends(require_admin)): q = db.query(Order) if status: q = q.filter(Order.status == status) return q.order_by(Order.created_at.desc()).all() @router.get("/{order_id}", response_model=CustomerOrderOut) def get_order(order_id, db: Session = Depends(get_db)): return _get_order_or_404(db, order_id) @router.post("/{order_id}/tracking", response_model=OrderOut) def add_tracking(order_id, body: TrackingIn, db: Session = Depends(get_db), _auth: dict = Depends(require_admin)): order = _get_order_or_404(db, order_id) order.tracking = body.tracking if body.carrier: order.carrier = body.carrier if order.status in TRACKING_STATUSES: order.status = "shipped" db.add( AuditLog( actor="api", action="tracking_received", entity="order", entity_id=order.order_number, detail={"tracking": body.tracking, "carrier": body.carrier}, ) ) db.commit() db.refresh(order) return order