Add Polaris FastAPI backend: models/schemas, pricing+scoring+order router, importer, Celery tasks, API routers, seed script
This commit is contained in:
122
backend/app/engines/order_router.py
Normal file
122
backend/app/engines/order_router.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""Order router — select highest-scoring supplier and record profit.
|
||||
|
||||
select_supplier: eligible = active supplier, linked supplier_product, stock >= qty.
|
||||
route_order: assigns supplier, computes supplier_cost / shipping_cost / fees,
|
||||
records net profit = retail_total - supplier_cost - shipping_cost - fees.
|
||||
"""
|
||||
from decimal import ROUND_HALF_UP, Decimal
|
||||
|
||||
from app.engines import scoring
|
||||
from app.models import AuditLog, Product, Supplier, SupplierProduct
|
||||
|
||||
CENT = Decimal("0.01")
|
||||
FEE_RATE = Decimal("0.05") # payment processing estimate
|
||||
FEE_FLAT = Decimal("0.30")
|
||||
|
||||
|
||||
def _f(value) -> float:
|
||||
try:
|
||||
return float(value or 0)
|
||||
except (TypeError, ValueError):
|
||||
return 0.0
|
||||
|
||||
|
||||
def select_supplier(db, product_id, qty):
|
||||
"""Return (result_dict, error_str). result = {supplier, supplier_product, score}."""
|
||||
product = db.query(Product).filter(Product.id == product_id).first()
|
||||
if product is None:
|
||||
return None, "product not found"
|
||||
|
||||
rows = (
|
||||
db.query(SupplierProduct, Supplier)
|
||||
.join(Supplier, SupplierProduct.supplier_id == Supplier.id)
|
||||
.filter(
|
||||
SupplierProduct.product_id == product_id,
|
||||
Supplier.status == "active",
|
||||
SupplierProduct.supplier_inventory >= qty,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
if not rows:
|
||||
return None, "no eligible supplier"
|
||||
|
||||
pool = {"avg_cost": sum(_f(sp.supplier_cost) for sp, _ in rows) / len(rows)}
|
||||
scored = [
|
||||
(scoring.score_supplier(db, supplier, sp, qty, pool), sp, supplier)
|
||||
for sp, supplier in rows
|
||||
]
|
||||
best_score, best_sp, best_supplier = max(scored, key=lambda t: t[0])
|
||||
return {
|
||||
"supplier": best_supplier,
|
||||
"supplier_product": best_sp,
|
||||
"score": best_score,
|
||||
}, None
|
||||
|
||||
|
||||
def route_order(db, order):
|
||||
"""Route a newly created order to the best supplier(s) and record profit."""
|
||||
supplier_id = None
|
||||
supplier_cost = Decimal("0")
|
||||
shipping_cost = Decimal("0")
|
||||
routing_detail = []
|
||||
|
||||
for item in order.items or []:
|
||||
result, err = select_supplier(db, item["product_id"], int(item["qty"]))
|
||||
if err:
|
||||
routing_detail.append({"product_id": item["product_id"], "error": err})
|
||||
continue
|
||||
sp = result["supplier_product"]
|
||||
if supplier_id is None:
|
||||
supplier_id = sp.supplier_id
|
||||
supplier_cost += Decimal(str(sp.supplier_cost or 0)) * int(item["qty"])
|
||||
shipping_cost += Decimal(str(sp.shipping_cost or 0))
|
||||
routing_detail.append(
|
||||
{
|
||||
"product_id": item["product_id"],
|
||||
"supplier": result["supplier"].name,
|
||||
"supplier_score": result["score"],
|
||||
}
|
||||
)
|
||||
|
||||
order.supplier_id = supplier_id
|
||||
order.supplier_cost = supplier_cost.quantize(CENT, rounding=ROUND_HALF_UP)
|
||||
order.shipping_cost = shipping_cost.quantize(CENT, rounding=ROUND_HALF_UP)
|
||||
order.fees = (Decimal(str(order.retail_total or 0)) * FEE_RATE + FEE_FLAT).quantize(
|
||||
CENT, rounding=ROUND_HALF_UP
|
||||
)
|
||||
order.profit = (
|
||||
Decimal(str(order.retail_total or 0))
|
||||
- order.supplier_cost
|
||||
- order.shipping_cost
|
||||
- order.fees
|
||||
).quantize(CENT, rounding=ROUND_HALF_UP)
|
||||
|
||||
db.add(
|
||||
AuditLog(
|
||||
actor="order_router",
|
||||
action="order_routed",
|
||||
entity="order",
|
||||
entity_id=order.order_number,
|
||||
detail={
|
||||
"supplier_id": str(supplier_id) if supplier_id else None,
|
||||
"profit": float(order.profit),
|
||||
"routing": routing_detail,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
if supplier_id is None:
|
||||
order.status = "new" # stays unfulfilled; no eligible supplier
|
||||
db.commit()
|
||||
return {"routed": False, "reason": "no eligible supplier for any item"}
|
||||
|
||||
order.status = "confirmed"
|
||||
db.commit()
|
||||
return {
|
||||
"routed": True,
|
||||
"supplier_id": str(supplier_id),
|
||||
"profit": float(order.profit),
|
||||
"supplier_cost": float(order.supplier_cost),
|
||||
"shipping_cost": float(order.shipping_cost),
|
||||
"fees": float(order.fees),
|
||||
}
|
||||
Reference in New Issue
Block a user