Source code for app.orders
from itertools import count
from app.pydantic_models import Order, CartItem
from app.state_machine import StateMachine
from app.cart import CartManager
from app.inventory import InventoryManager
from app.config import discount_strategy, shipping_strategy
# In-memory storage for orders: order_id -> (Order, StateMachine)
_ORDERS: dict[int, tuple[Order, StateMachine]] = {}
_order_seq = count(start=1)
def _calculate_order_breakdown(items: list[CartItem]) -> dict[str, float]:
"""
Calculate pricing breakdown for a list of order items.
Args:
items: List of CartItem objects
Returns:
dict: Breakdown with subtotal, discount, shipping, and total
"""
# Calculate subtotal (price before discounts and shipping)
subtotal = 0.0
for it in items:
p = InventoryManager.get_instance().get_product(it.product_id)
assert p is not None
subtotal += p.price * it.quantity
# Prepare items list for shipping calculation
items_for_shipping = [(it.product_id, it.quantity) for it in items]
# Apply discount and shipping strategies
discount = discount_strategy.calculate_discount(subtotal)
shipping = shipping_strategy.calculate_shipping(subtotal, items_for_shipping)
# Calculate final total
total = subtotal - discount + shipping
return {
"subtotal": round(subtotal, 2),
"discount": round(discount, 2),
"shipping": round(shipping, 2),
"total": round(total, 2)
}
[docs]
def create_order(cart_id: str = "default") -> Order:
"""
Create an order from a shopping cart.
Validates stock availability for every item in the cart, decrements
the inventory, computes the total price, and clears the cart.
Args:
cart_id: The identifier of the cart to order from. Defaults to "default".
Returns:
Order: The newly created order.
Raises:
ValueError: If any product in the cart has insufficient stock.
"""
cart = CartManager.get_instance().get_cart(cart_id)
items: list[CartItem] = []
for pid, qty in cart.items.items():
if not InventoryManager.get_instance().has_stock(pid, qty):
raise ValueError(f"Insufficient stock for product {pid}")
items.append(CartItem(product_id=pid, quantity=qty))
for it in items:
InventoryManager.get_instance().decrement_stock(it.product_id, it.quantity)
# Calculate subtotal (price before discounts and shipping)
subtotal = 0.0
for it in items:
p = InventoryManager.get_instance().get_product(it.product_id)
assert p is not None
subtotal += p.price * it.quantity
# Prepare items list for shipping calculation
items_for_shipping = [(it.product_id, it.quantity) for it in items]
# Apply discount and shipping strategies
discount = discount_strategy.calculate_discount(subtotal)
shipping = shipping_strategy.calculate_shipping(subtotal, items_for_shipping)
# Calculate final total
total = subtotal - discount + shipping
oid = next(_order_seq)
order = Order(id=oid, cart_id=cart_id, items=items, total=round(total, 2), status="created")
_ORDERS[oid] = (order, StateMachine())
cart.clear()
return order
[docs]
def get_order_breakdown(order_id: int) -> dict[str, float]:
"""
Calculate the pricing breakdown for an order.
Args:
order_id: The ID of the order
Returns:
dict: Breakdown with subtotal, discount, shipping, and total
"""
order = get_order(order_id)
return _calculate_order_breakdown(order.items)
[docs]
def get_order(order_id: int) -> Order:
"""
Retrieves the order by its ID. Raises ValueError if the order is not found.
Args:
order_id: The unique identifier of the order to retrieve.
Returns:
The Order object corresponding to the given order_id.
Raises:
ValueError: If the order with the specified ID is not found in the system.
"""
if order_id not in _ORDERS:
raise ValueError("Order not found")
return _ORDERS[order_id][0]
[docs]
def get_machine(order_id: int) -> StateMachine:
"""
Retrieves the state machine for an order by its ID.
Raises ValueError if the order is not found.
Args:
order_id: The unique identifier of the state machine to retrieve.
Returns:
The StateMachine object corresponding to the given order_id.
Raises:
ValueError: If the state machine for the specified order ID is not found in the system.
"""
if order_id not in _ORDERS:
raise ValueError("Order not found")
return _ORDERS[order_id][1]
[docs]
def set_status(order_id: int, new_status: str) -> Order:
"""
Updates the status of an order and triggers the state machine transition.
Raises ValueError if the order is not found or if the status transition is invalid.
Args:
order_id: The unique identifier of the order to update.
new_status: The new status to set for the order.
Returns:
The updated Order object with the new status.
Raises:
ValueError: If the order with the specified ID is not found or if the status
transition is invalid according to the state machine rules.
"""
order = get_order(order_id)
machine = get_machine(order_id)
machine.update(new_status)
order.status = new_status
return order