diff --git a/src/modules/orders/controllers/orders.controller.ts b/src/modules/orders/controllers/orders.controller.ts index c5a7df8..9140795 100644 --- a/src/modules/orders/controllers/orders.controller.ts +++ b/src/modules/orders/controllers/orders.controller.ts @@ -14,7 +14,7 @@ import { UpdateOrderStatusDto } from '../dto/update-order-status.dto'; @ApiBearerAuth() @Controller() export class OrdersController { - constructor(private readonly ordersService: OrdersService) {} + constructor(private readonly ordersService: OrdersService) { } @UseGuards(AuthGuard) @Post('public/checkout') @@ -98,8 +98,16 @@ export class OrdersController { @UseGuards(AdminAuthGuard) @ApiOperation({ summary: 'Get Stats for report page' }) - @Get('admin/orders/stats') - findStats( @RestId() restId: string) { + @Get('admin/orders/stats') + findStats(@RestId() restId: string) { return this.ordersService.getStats(restId); } + + @UseGuards(AdminAuthGuard) + @ApiOperation({ summary: 'Get food sales pie chart data for last month' }) + @ApiHeader(API_HEADER_SLUG) + @Get('admin/orders/food-sales-pie-chart') + getFoodSalesPieChart(@RestId() restId: string) { + return this.ordersService.getFoodSalesPieChart(restId); + } } diff --git a/src/modules/orders/providers/orders.service.ts b/src/modules/orders/providers/orders.service.ts index 6a043ad..7111dab 100644 --- a/src/modules/orders/providers/orders.service.ts +++ b/src/modules/orders/providers/orders.service.ts @@ -507,4 +507,58 @@ export class OrdersService { }; }); } + + async getFoodSalesPieChart(restId: string) { + return this.em.transactional(async em => { + // Calculate last month date range + const now = new Date(); + const firstDayOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1); + const lastDayOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0, 23, 59, 59, 999); + + // Query order items from orders in the last month + // Only include completed orders (excluding canceled and pending) + const result = await em.execute( + ` + SELECT + f.id as food_id, + f.title as food_title, + COALESCE(SUM(oi.quantity), 0)::int as total_quantity, + COALESCE(SUM(oi.total_price), 0)::numeric as total_revenue + FROM order_items oi + INNER JOIN orders o ON oi.order_id = o.id + INNER JOIN foods f ON oi.food_id = f.id + WHERE o.restaurant_id = ? + AND o.created_at >= ? + AND o.created_at <= ? + AND o.status NOT IN ('pendingPayment', 'canceled') + GROUP BY f.id, f.title + ORDER BY total_revenue DESC + `, + [restId, firstDayOfLastMonth, lastDayOfLastMonth], + ); + + // Calculate total revenue for percentage calculation + const totalRevenue = result.reduce((sum: number, item: any) => { + return sum + Number(item.total_revenue || 0); + }, 0); + + // Format data for pie chart and calculate percentages + const chartData = result.map((item: any) => ({ + foodId: item.food_id, + foodTitle: item.food_title || 'Unknown', + quantity: Number(item.total_quantity || 0), + revenue: Number(item.total_revenue || 0), + percentage: totalRevenue > 0 ? Number(((Number(item.total_revenue || 0) / totalRevenue) * 100).toFixed(2)) : 0, + })); + + return { + period: { + startDate: firstDayOfLastMonth.toISOString(), + endDate: lastDayOfLastMonth.toISOString(), + }, + totalRevenue, + data: chartData, + }; + }); + } }