diff --git a/app/routes/order_routes.py b/app/routes/order_routes.py index 1507f85..44eb107 100644 --- a/app/routes/order_routes.py +++ b/app/routes/order_routes.py @@ -32,7 +32,8 @@ router = APIRouter(prefix="/orders") @router.get("/", response_model=List[TCGPlayerAPIOrderSummary]) async def get_orders( search_range: SearchRange = SearchRange.LAST_THREE_MONTHS, - open_only: bool = False + open_only: bool = False, + db: Session = Depends(get_db) ) -> List[TCGPlayerAPIOrderSummary]: """ Retrieve orders from TCGPlayer based on search criteria. @@ -47,6 +48,7 @@ async def get_orders( try: order_management = service_manager.get_service('order_management') orders = await order_management.get_orders(search_range, open_only) + orders = await order_management.add_item_quantity(db, orders) return orders except Exception as e: raise HTTPException(status_code=500, detail=f"Failed to fetch orders: {str(e)}") diff --git a/app/schemas/tcgplayer.py b/app/schemas/tcgplayer.py index b35a6e3..9e4c380 100644 --- a/app/schemas/tcgplayer.py +++ b/app/schemas/tcgplayer.py @@ -76,6 +76,7 @@ class TCGPlayerAPIOrderSummary(BaseModel): orderStatus: str buyerName: str shippingType: str + itemQuantity: int productAmount: float shippingAmount: float totalAmount: float diff --git a/app/services/external_api/tcgplayer/order_management_service.py b/app/services/external_api/tcgplayer/order_management_service.py index 5691a20..8594dda 100644 --- a/app/services/external_api/tcgplayer/order_management_service.py +++ b/app/services/external_api/tcgplayer/order_management_service.py @@ -24,6 +24,7 @@ import csv import io from app.schemas.file import FileInDB from datetime import datetime +from sqlalchemy import func logger = logging.getLogger(__name__) class OrderManagementService(BaseTCGPlayerService): @@ -40,7 +41,34 @@ class OrderManagementService(BaseTCGPlayerService): self.pull_sheet_endpoint = f"/pull-sheets/export{self.API_VERSION}" self.shipping_endpoint = f"/shipping/export{self.API_VERSION}" - + async def add_item_quantity(self, db: Session, orders: list[TCGPlayerAPIOrderSummary]) -> list[TCGPlayerAPIOrderSummary]: + """ + Add item quantity to orders using SQL aggregation for better performance + """ + # Get order numbers from the input orders + order_numbers = [order["orderNumber"] for order in orders] + + # Use SQL aggregation to get the sum of quantities directly from the database + quantity_sums = ( + db.query( + TCGPlayerOrderProduct.order_number, + func.sum(TCGPlayerOrderProduct.quantity).label('total_quantity') + ) + .filter(TCGPlayerOrderProduct.order_number.in_(order_numbers)) + .group_by(TCGPlayerOrderProduct.order_number) + .all() + ) + + # Create a lookup dictionary for faster access + quantity_lookup = {order_number: total_quantity for order_number, total_quantity in quantity_sums} + + # Update orders with quantities + for order in orders: + order["itemQuantity"] = quantity_lookup.get(order["orderNumber"], 0) + + return orders + + async def get_orders(self, search_range: str = "LastThreeMonths", open_only: bool = False, filter_out: list[str] = [], filter_in: list[str] = []) -> list[TCGPlayerAPIOrderSummary]: """ search range options: @@ -79,6 +107,9 @@ class OrderManagementService(BaseTCGPlayerService): orders = [order for order in orders if order.get("orderNumber") not in filter_out] if filter_in: orders = [order for order in orders if order.get("orderNumber") in filter_in] + # add item quantity to orders as none + for order in orders: + order["itemQuantity"] = 0 return orders async def get_order_ids(self, search_range: str = "LastThreeMonths", open_only: bool = False, filter_out: list[str] = [], filter_in: list[str] = []): diff --git a/app/services/scheduler/scheduler_service.py b/app/services/scheduler/scheduler_service.py index 3dda5a9..cc56290 100644 --- a/app/services/scheduler/scheduler_service.py +++ b/app/services/scheduler/scheduler_service.py @@ -75,7 +75,7 @@ class SchedulerService(BaseService): await self.scheduler.schedule_task( task_name="update_open_orders_hourly", func=self.update_open_orders_hourly, - cron_expression="10 * * * *", # Run at minute 10 of every hour + cron_expression="*/10 * * * *", # Run at minute 10 of every hour db=db ) # Schedule all orders update to run daily at 3 AM diff --git a/app/static/app.js b/app/static/app.js index 982e817..8d7777f 100644 --- a/app/static/app.js +++ b/app/static/app.js @@ -67,16 +67,30 @@ function displayOrders(orders) { } orders.forEach(order => { + const hasHighQuantity = order.itemQuantity > 9; + const hasHighAmount = order.productAmount > 40.00; + const orderCard = document.createElement('div'); orderCard.className = `bg-gray-700 rounded-lg shadow-sm p-4 border border-gray-600 hover:shadow-md transition-shadow cursor-pointer ${ selectedOrders.has(order.orderNumber) ? 'ring-2 ring-blue-500' : '' - }`; + } ${hasHighQuantity || hasHighAmount ? 'border-yellow-500' : ''}`; orderCard.dataset.orderId = order.orderNumber; orderCard.innerHTML = `
-

#${order.orderNumber || 'N/A'}

+
+

+ ${order.orderNumber || 'N/A'} +

+ ${(hasHighQuantity || hasHighAmount) ? ` + + + + + + ` : ''} +

${order.buyerName || 'N/A'}

${order.orderDate ? new Date(order.orderDate).toLocaleString() : 'N/A'}

-

$${order.totalAmount ? order.totalAmount.toFixed(2) : '0.00'}

+
+ ${hasHighAmount ? ` + ⚠️ + ` : ''} +

$${order.totalAmount ? order.totalAmount.toFixed(2) : '0.00'}

+
+ ${hasHighQuantity ? ` +
+ + + + + High quantity: ${order.itemQuantity} items + +
+ ` : ''}
`;