Add Polaris FastAPI backend: models/schemas, pricing+scoring+order router, importer, Celery tasks, API routers, seed script
This commit is contained in:
113
backend/app/routers/orders.py
Normal file
113
backend/app/routers/orders.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user