Files
reseller-platform/backend/app/routers/orders.py

114 lines
3.5 KiB
Python

"""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.order_router import route_order
from app.models import AuditLog, Customer, Order, Product
from app.schemas import 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=OrderOut)
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="new",
)
db.add(order)
db.flush()
result = route_order(db, order)
db.refresh(order)
return order
@router.get("", response_model=List[OrderOut])
def list_orders(status: Optional[str] = None, db: Session = Depends(get_db)):
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=OrderOut)
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)):
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