calculate_sales_totals
Calculate sales totals with filtering by date range, status, location, customer, and payment type. Returns revenue, tax, sale count, average value, and optional breakdowns for business analysis.
Instructions
Calculate sales totals with filtering by date range, status, location, customer, payment type, and amount. Returns total revenue, tax, sale count, average sale value, and optional breakdown by status, location, or date period.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| status | No | Filter by sale status | |
| location | No | Filter by location ID | |
| customer | No | Filter by customer account ID | |
| date_from | No | Filter sales created on or after this date (ISO 8601: YYYY-MM-DD) | |
| date_to | No | Filter sales created on or before this date (ISO 8601: YYYY-MM-DD) | |
| payment_type | No | Filter by payment type (cash, card, etc.) | |
| total_gte | No | Filter sales with total >= this value (in cents) | |
| total_lte | No | Filter sales with total <= this value (in cents) | |
| group_by | No | Group results by field for detailed breakdown | |
| date_interval | No | When group_by=date, aggregate by this interval (day, week, or month) |
Implementation Reference
- src/client.ts:580-719 (handler)The primary handler function executing the tool: fetches sales data with pagination, applies server-side and client-side filters (including dates), calculates aggregate totals (revenue, tax, count, average), supports grouping/breakdowns by status/location/date intervals, formats output with locale/currency awareness.async calculateSalesTotals(params?: { status?: string; location?: string; customer?: string; date_from?: string; date_to?: string; payment_type?: string; total_gte?: number; total_lte?: number; group_by?: 'status' | 'location' | 'date'; date_interval?: 'day' | 'week' | 'month'; }): Promise<SalesTotalsResult> { const { group_by, date_interval, ...filterParams } = params || {}; const filters: string[] = []; // Track applied filters if (filterParams.status) filters.push(`status=${filterParams.status}`); if (filterParams.location) filters.push(`location=${filterParams.location}`); if (filterParams.customer) filters.push(`customer=${filterParams.customer}`); if (filterParams.date_from) filters.push(`date_from=${filterParams.date_from}`); if (filterParams.date_to) filters.push(`date_to=${filterParams.date_to}`); if (filterParams.payment_type) filters.push(`payment_type=${filterParams.payment_type}`); if (filterParams.total_gte !== undefined) filters.push(`total>=${filterParams.total_gte}`); if (filterParams.total_lte !== undefined) filters.push(`total<=${filterParams.total_lte}`); let allSales: Sale[] = []; let cursor: string | null = null; // Build query params with only defined values (excluding date filters - not supported by API) const queryParams: Record<string, any> = { limit: 100 }; if (filterParams.status) queryParams.status = filterParams.status; if (filterParams.location) queryParams.location = filterParams.location; if (filterParams.customer) queryParams.customer = filterParams.customer; if (filterParams.payment_type) queryParams.payment_type = filterParams.payment_type; if (filterParams.total_gte !== undefined) queryParams.total_gte = filterParams.total_gte; if (filterParams.total_lte !== undefined) queryParams.total_lte = filterParams.total_lte; // NOTE: date_from/date_to NOT sent to API (not supported) - will filter client-side // Fetch all pages do { if (cursor) queryParams.cursor = cursor; const response = await this.listSales(queryParams); allSales = allSales.concat(response.data); cursor = response.next_cursor; } while (cursor); // Apply client-side date filtering (API doesn't support this) if (filterParams.date_from || filterParams.date_to) { allSales = allSales.filter(sale => { if (!sale.created) return false; const saleDate = new Date(sale.created); if (filterParams.date_from && saleDate < new Date(filterParams.date_from)) return false; if (filterParams.date_to && saleDate > new Date(filterParams.date_to)) return false; return true; }); } // Calculate totals let totalRevenue = 0; let totalTax = 0; let totalSales = 0; const breakdown: Record<string, { revenue: number; revenue_formatted: string; tax: number; tax_formatted: string; count: number }> = {}; for (const sale of allSales) { totalRevenue += sale.total || 0; totalTax += sale.tax || 0; totalSales += 1; // Group by if specified if (group_by) { let groupKey: string; switch (group_by) { case 'status': groupKey = sale.status; break; case 'location': groupKey = sale.location || 'no_location'; break; case 'date': if (date_interval) { const date = new Date(sale.created); if (date_interval === 'day') { groupKey = date.toISOString().split('T')[0]; } else if (date_interval === 'week') { const weekStart = new Date(date); weekStart.setDate(date.getDate() - date.getDay()); groupKey = weekStart.toISOString().split('T')[0]; } else if (date_interval === 'month') { groupKey = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; } else { groupKey = date.toISOString().split('T')[0]; } } else { groupKey = sale.created.split('T')[0]; } break; default: groupKey = 'all'; } if (!breakdown[groupKey]) { breakdown[groupKey] = { revenue: 0, revenue_formatted: '', tax: 0, tax_formatted: '', count: 0 }; } breakdown[groupKey].revenue += sale.total || 0; breakdown[groupKey].tax += sale.tax || 0; breakdown[groupKey].count += 1; } } // Format breakdown values const formattedBreakdown: Record<string, { revenue: number; revenue_formatted: string; tax: number; tax_formatted: string; count: number }> | undefined = group_by ? Object.fromEntries( Object.entries(breakdown).map(([key, data]) => [ key, { revenue: data.revenue, revenue_formatted: this.formatAmount(data.revenue), tax: data.tax, tax_formatted: this.formatAmount(data.tax), count: data.count } ]) ) : undefined; const avgSale = totalSales > 0 ? Math.round(totalRevenue / totalSales) : 0; return { total_revenue: totalRevenue, total_revenue_formatted: this.formatAmount(totalRevenue), total_tax: totalTax, total_tax_formatted: this.formatAmount(totalTax), total_sales: totalSales, average_sale: avgSale, average_sale_formatted: this.formatAmount(avgSale), breakdown: formattedBreakdown, filters_applied: filters, currency: this.currency, locale: this.locale, }; }
- src/server.ts:354-380 (schema)Input JSON Schema and tool metadata (name, description) registered with MCP server for parameter validation.{ name: 'calculate_sales_totals', description: 'Calculate sales totals with filtering by date range, status, location, customer, payment type, and amount. Returns total revenue, tax, sale count, average sale value, and optional breakdown by status, location, or date period.', inputSchema: { type: 'object', properties: { status: { type: 'string', enum: ['completed', 'voided', 'returned'], description: 'Filter by sale status' }, location: { type: 'string', description: 'Filter by location ID' }, customer: { type: 'string', description: 'Filter by customer account ID' }, date_from: { type: 'string', description: 'Filter sales created on or after this date (ISO 8601: YYYY-MM-DD)' }, date_to: { type: 'string', description: 'Filter sales created on or before this date (ISO 8601: YYYY-MM-DD)' }, payment_type: { type: 'string', description: 'Filter by payment type (cash, card, etc.)' }, total_gte: { type: 'number', description: 'Filter sales with total >= this value (in cents)' }, total_lte: { type: 'number', description: 'Filter sales with total <= this value (in cents)' }, group_by: { type: 'string', enum: ['status', 'location', 'date'], description: 'Group results by field for detailed breakdown' }, date_interval: { type: 'string', enum: ['day', 'week', 'month'], description: 'When group_by=date, aggregate by this interval (day, week, or month)' }, }, }, },
- src/server.ts:512-513 (registration)Dispatches tool calls in MCP CallToolRequestHandler to the client handler implementation.case 'calculate_sales_totals': return { content: [{ type: 'text', text: JSON.stringify(await client.calculateSalesTotals(args as any), null, 2) }] };
- src/types.ts:107-127 (schema)TypeScript interface defining the output structure returned by the handler (totals, breakdown, metadata).export interface SalesTotalsResult { total_revenue: number; // in cents total_revenue_formatted: string; // locale-formatted total_tax: number; // in cents total_tax_formatted: string; // locale-formatted total_sales: number; // count average_sale: number; // in cents average_sale_formatted: string; // locale-formatted breakdown?: { [key: string]: { revenue: number; revenue_formatted: string; tax: number; tax_formatted: string; count: number; }; }; filters_applied: string[]; currency: string; // ISO currency code locale: string; // BCP 47 locale }