Create a Weekly Sales Report That Emails You Every Monday

Mike Holownych
#n8n #automation #reporting #analytics #e-commerce

Quick win: Automate your weekly sales report in 20 minutes. Get emailed every Monday at 8am with last week’s key metrics.

No more manual spreadsheets.

What You’ll Build

Automated report includes:

  • Total revenue (last 7 days)
  • Number of orders
  • Average order value
  • Top 5 products
  • New vs returning customers
  • Week-over-week comparison

Delivered: Monday 8am to your inbox


n8n Workflow Structure

Schedule (Monday 8am)
→ Get orders (last 7 days)
→ Calculate metrics
→ Get previous week data
→ Calculate comparisons
→ Format HTML email
→ Send via SendGrid

Step-by-Step Setup

Node 1: Schedule Trigger

Cron: 0 8 * * 1
(Every Monday at 8am)

Node 2: Get Last Week’s Orders

For DashNex:

HTTP Request
Method: GET
URL: https://your-store.com/api/orders
Parameters:
  - start_date: `{{$now.minus({days: 7}).toISO()}}`
  - end_date: \{\{$now.toISO()\}\}
Headers:
  - Authorization: Bearer YOUR_API_KEY

For Shopify:

Shopify node
Operation: Get All Orders
Filters:
  - created_at_min: (7 days ago)

Node 3: Calculate Metrics (Function Node)

const orders = $input.all().map(item => item.json);

// Basic metrics
const totalRevenue = orders.reduce((sum, order) => sum + parseFloat(order.total_price), 0);
const orderCount = orders.length;
const avgOrderValue = totalRevenue / orderCount;

// Customer analysis
const newCustomers = orders.filter(o => o.customer.orders_count === 1).length;
const returningCustomers = orderCount - newCustomers;

// Top products
const productSales = {};
orders.forEach(order => {
  order.line_items.forEach(item => {
    if (!productSales[item.name]) {
      productSales[item.name] = { quantity: 0, revenue: 0 };
    }
    productSales[item.name].quantity += item.quantity;
    productSales[item.name].revenue += item.price * item.quantity;
  });
});

const topProducts = Object.entries(productSales)
  .sort((a, b) => b[1].revenue - a[1].revenue)
  .slice(0, 5)
  .map(([name, data]) => ({ name, ...data }));

return [{
  json: {
    period: 'Last 7 Days',
    totalRevenue: totalRevenue.toFixed(2),
    orderCount,
    avgOrderValue: avgOrderValue.toFixed(2),
    newCustomers,
    returningCustomers,
    topProducts
  }
}];

Node 4: Get Previous Week Data

Duplicate Node 2, change dates to 8-14 days ago.

Node 5: Calculate Week-over-Week Change

const thisWeek = $('Calculate This Week').first().json;
const lastWeek = $('Calculate Last Week').first().json;

const revenueChange = ((thisWeek.totalRevenue - lastWeek.totalRevenue) / lastWeek.totalRevenue * 100).toFixed(1);
const orderChange = ((thisWeek.orderCount - lastWeek.orderCount) / lastWeek.orderCount * 100).toFixed(1);

return [{
  json: {
    ...thisWeek,
    revenueChange,
    orderChange,
    revenueDirection: revenueChange > 0 ? '📈' : '📉',
    orderDirection: orderChange > 0 ? '📈' : '📉'
  }
}];

Node 6: Format HTML Email

const data = $json;

const html = `
<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: Arial, sans-serif; }
    .metric { background: #f5f5f5; padding: 20px; margin: 10px 0; border-radius: 8px; }
    .metric h2 { margin: 0 0 5px 0; font-size: 32px; }
    .metric p { margin: 0; color: #666; }
    .change { font-size: 14px; font-weight: bold; }
    .positive { color: #22c55e; }
    .negative { color: #ef4444; }
    table { width: 100%; border-collapse: collapse; margin: 20px 0; }
    th, td { text-align: left; padding: 12px; border-bottom: 1px solid #ddd; }
    th { background: #f5f5f5; }
  </style>
</head>
<body>
  <h1>Weekly Sales Report</h1>
  <p>${data.period}</p>
  
  <div class="metric">
    <h2>$${data.totalRevenue}</h2>
    <p>Total Revenue</p>
    <p class="change ${data.revenueChange > 0 ? 'positive' : 'negative'}">
      ${data.revenueDirection} ${Math.abs(data.revenueChange)}% vs last week
    </p>
  </div>
  
  <div class="metric">
    <h2>${data.orderCount}</h2>
    <p>Orders</p>
    <p class="change ${data.orderChange > 0 ? 'positive' : 'negative'}">
      ${data.orderDirection} ${Math.abs(data.orderChange)}% vs last week
    </p>
  </div>
  
  <div class="metric">
    <h2>$${data.avgOrderValue}</h2>
    <p>Average Order Value</p>
  </div>
  
  <h3>Customer Breakdown</h3>
  <table>
    <tr>
      <th>Type</th>
      <th>Count</th>
      <th>Percentage</th>
    </tr>
    <tr>
      <td>New Customers</td>
      <td>${data.newCustomers}</td>
      <td>${(data.newCustomers / data.orderCount * 100).toFixed(1)}%</td>
    </tr>
    <tr>
      <td>Returning Customers</td>
      <td>${data.returningCustomers}</td>
      <td>${(data.returningCustomers / data.orderCount * 100).toFixed(1)}%</td>
    </tr>
  </table>
  
  <h3>Top Products</h3>
  <table>
    <tr>
      <th>Product</th>
      <th>Units Sold</th>
      <th>Revenue</th>
    </tr>
    ${data.topProducts.map(p => `
      <tr>
        <td>${p.name}</td>
        <td>${p.quantity}</td>
        <td>$${p.revenue.toFixed(2)}</td>
      </tr>
    `).join('')}
  </table>
</body>
</html>
`;

return [{ json: { html, subject: `Weekly Sales Report - $${data.totalRevenue}` } }];

Node 7: Send Email (SendGrid)

To: [email protected]
Subject: \{\{$json.subject\}\}
Content Type: HTML
Message: \{\{$json.html\}\}

Example Report Output

Weekly Sales Report
Last 7 Days

$4,287.50
Total Revenue
📈 +12.3% vs last week

35
Orders
📈 +8.6% vs last week

$122.50
Average Order Value

Customer Breakdown
New Customers: 18 (51.4%)
Returning Customers: 17 (48.6%)

Top Products
1. Premium Course - 12 units - $1,188.00
2. Starter Bundle - 8 units - $632.00
3. Consulting Session - 5 units - $975.00
4. E-book Pack - 15 units - $447.00
5. Membership - 3 units - $297.00

Advanced Enhancements

Add Charts

Use Chart.js or Google Charts API to generate revenue trend chart.

Multi-Store Support

Clone workflow for each store, combine reports into one email.

Slack Integration

Also post summary to #sales Slack channel.

Google Sheets Logging

Log metrics to sheet for historical tracking.

Alert Thresholds

Send urgent Slack message if revenue drops >20% week-over-week.


Troubleshooting

No data in report:

  • Check API credentials
  • Verify date calculations
  • Ensure orders exist in date range

Email not sending:

  • Verify SendGrid API key
  • Check sender email is verified
  • Look for errors in execution log

Wrong timezone:

  • n8n uses UTC by default
  • Adjust cron or set VPS timezone

FAQ

Q: Can I customize metrics?

Yes! Modify the Function node to calculate any metrics you want.

Q: Can this work with WooCommerce/Magento?

Yes! Just change the API call to match your platform.

Q: Can I send to multiple recipients?

Yes! Add multiple emails in “To” field (comma-separated).

Q: How do I change the day/time?

Modify the cron in Schedule node. Use crontab.guru to create expression.


Related:


About: I’m Mike Holownych. I automate business reporting with n8n. Learn more →

MH

About Mike Holownych

I help entrepreneurs build self-running businesses with DashNex + automation. n8n automation expert specializing in e-commerce, affiliate marketing, and business systems.