import { CloverClient } from '../clients/clover.js'; import { CloverOrder, CloverPayment, SalesSummary, RevenueByItem, RevenueByCategory, EmployeePerformance, } from '../types/index.js'; export function createReportsTools(client: CloverClient) { return { clover_sales_summary: { description: 'Get sales summary report for a date range', inputSchema: { type: 'object', properties: { startDate: { type: 'number', description: 'Start date (Unix timestamp in milliseconds)', }, endDate: { type: 'number', description: 'End date (Unix timestamp in milliseconds)', }, }, required: ['startDate', 'endDate'], }, handler: async (args: any) => { // Fetch orders and payments in date range const orders = await client.fetchPaginated('/orders', { filter: `createdTime>=${args.startDate} AND createdTime<=${args.endDate}`, expand: 'lineItems', }); const payments = await client.fetchPaginated('/payments', { filter: `createdTime>=${args.startDate} AND createdTime<=${args.endDate}`, }); const totalSales = payments .filter((p) => p.result === 'SUCCESS') .reduce((sum, p) => sum + p.amount, 0); const totalRefunds = payments .filter((p) => p.refunds && p.refunds.length > 0) .reduce( (sum, p) => sum + p.refunds!.reduce((rsum, r) => rsum + r.amount, 0), 0 ); const totalTax = payments .filter((p) => p.result === 'SUCCESS') .reduce((sum, p) => sum + (p.taxAmount || 0), 0); const totalTips = payments .filter((p) => p.result === 'SUCCESS') .reduce((sum, p) => sum + (p.tipAmount || 0), 0); const summary: SalesSummary = { totalSales, totalOrders: orders.length, averageOrderValue: orders.length > 0 ? totalSales / orders.length : 0, totalRefunds, netSales: totalSales - totalRefunds, totalTax, totalTips, }; return summary; }, }, clover_revenue_by_item: { description: 'Get revenue breakdown by item for a date range', inputSchema: { type: 'object', properties: { startDate: { type: 'number', description: 'Start date (Unix timestamp in milliseconds)', }, endDate: { type: 'number', description: 'End date (Unix timestamp in milliseconds)', }, }, required: ['startDate', 'endDate'], }, handler: async (args: any) => { const orders = await client.fetchPaginated('/orders', { filter: `createdTime>=${args.startDate} AND createdTime<=${args.endDate}`, expand: 'lineItems', }); const itemStats = new Map< string, { name: string; quantity: number; revenue: number } >(); orders.forEach((order) => { order.lineItems?.forEach((lineItem) => { const itemId = lineItem.item.id; const existing = itemStats.get(itemId) || { name: lineItem.name, quantity: 0, revenue: 0, }; existing.quantity += lineItem.unitQty || 1; existing.revenue += lineItem.price * (lineItem.unitQty || 1); itemStats.set(itemId, existing); }); }); const results: RevenueByItem[] = Array.from(itemStats.entries()).map( ([itemId, stats]) => ({ itemId, itemName: stats.name, quantitySold: stats.quantity, totalRevenue: stats.revenue, averagePrice: stats.revenue / stats.quantity, }) ); return { items: results, count: results.length }; }, }, clover_revenue_by_category: { description: 'Get revenue breakdown by category for a date range', inputSchema: { type: 'object', properties: { startDate: { type: 'number', description: 'Start date (Unix timestamp in milliseconds)', }, endDate: { type: 'number', description: 'End date (Unix timestamp in milliseconds)', }, }, required: ['startDate', 'endDate'], }, handler: async (args: any) => { // Fetch items with categories const items = await client.fetchPaginated('/items', { expand: 'categories', }); const orders = await client.fetchPaginated('/orders', { filter: `createdTime>=${args.startDate} AND createdTime<=${args.endDate}`, expand: 'lineItems', }); // Build item-to-category map const itemCategories = new Map(); items.forEach((item: any) => { if (item.categories) { itemCategories.set(item.id, item.categories); } }); const categoryStats = new Map< string, { name: string; itemCount: Set; revenue: number } >(); orders.forEach((order) => { order.lineItems?.forEach((lineItem) => { const categories = itemCategories.get(lineItem.item.id) || []; categories.forEach((cat: any) => { const existing = categoryStats.get(cat.id) || { name: cat.name, itemCount: new Set(), revenue: 0, }; existing.itemCount.add(lineItem.item.id); existing.revenue += lineItem.price * (lineItem.unitQty || 1); categoryStats.set(cat.id, existing); }); }); }); const results: RevenueByCategory[] = Array.from( categoryStats.entries() ).map(([categoryId, stats]) => ({ categoryId, categoryName: stats.name, itemCount: stats.itemCount.size, totalRevenue: stats.revenue, })); return { categories: results, count: results.length }; }, }, clover_employee_performance: { description: 'Get employee performance report for a date range', inputSchema: { type: 'object', properties: { startDate: { type: 'number', description: 'Start date (Unix timestamp in milliseconds)', }, endDate: { type: 'number', description: 'End date (Unix timestamp in milliseconds)', }, }, required: ['startDate', 'endDate'], }, handler: async (args: any) => { const payments = await client.fetchPaginated('/payments', { filter: `createdTime>=${args.startDate} AND createdTime<=${args.endDate}`, expand: 'employee', }); const employeeStats = new Map< string, { name: string; sales: number; orderCount: number } >(); payments .filter((p) => p.result === 'SUCCESS') .forEach((payment) => { const empId = payment.employee.id; const existing = employeeStats.get(empId) || { name: (payment.employee as any).name || 'Unknown', sales: 0, orderCount: 0, }; existing.sales += payment.amount; existing.orderCount += 1; employeeStats.set(empId, existing); }); const results: EmployeePerformance[] = Array.from( employeeStats.entries() ).map(([employeeId, stats]) => ({ employeeId, employeeName: stats.name, totalSales: stats.sales, orderCount: stats.orderCount, averageOrderValue: stats.sales / stats.orderCount, })); return { employees: results, count: results.length }; }, }, }; }