Retail Reporting Templates
50+ production-ready SQL and Python report templates covering daily sales, inventory turnover, sell-through rates, vendor performance, seasonal analysis, and executive KPI dashboards. Copy, customize, run.
Key Features
- Sales Reports — Daily/weekly/monthly revenue, AOV, units, by channel/category/region
- Inventory Reports — Turnover ratio, days-of-supply, dead stock, shrinkage tracking
- Vendor Reports — Fill rate, lead time, cost variance, quality scorecards
- Seasonal Analysis — Year-over-year comparisons, holiday lift, weather correlation
- Customer Reports — New vs returning, LTV distribution, acquisition cost
- Executive Dashboards — Aggregated KPIs with trend indicators and alerts
Quick Start
# 1. Extract the templates
unzip retail-reporting-templates.zip
cd retail-reporting-templates
# 2. Choose environment config
cp configs/development.yaml config.yaml # or production.yaml
# 3. Run a specific report
python -m retail_reports.runner --report daily_sales --date 2026-03-23
Architecture
├── configs/
│ ├── development.yaml # Dev database connection and settings
│ └── production.yaml # Production settings with caching
├── reports/
│ ├── sales/ # 12 sales report templates
│ ├── inventory/ # 10 inventory report templates
│ ├── vendor/ # 8 vendor performance templates
│ ├── customer/ # 8 customer analysis templates
│ ├── seasonal/ # 6 seasonal comparison templates
│ └── executive/ # 6 executive dashboard templates
└── src/retail_reports/
├── runner.py # Report execution engine
├── formatters.py # Output formatters (CSV, JSON, HTML)
└── utils.py # Date ranges, fiscal calendars, helpers
Usage Examples
Daily Sales Summary (SQL)
-- Report: daily_sales_summary
-- Frequency: Daily | Audience: Store managers
SELECT
DATE(o.order_date) AS sale_date,
COUNT(DISTINCT o.order_id) AS orders,
SUM(oi.quantity) AS units_sold,
SUM(o.order_total) AS gross_revenue,
SUM(o.discount_amount) AS total_discounts,
SUM(o.order_total) - SUM(o.discount_amount) AS net_revenue,
SUM(o.order_total) / COUNT(DISTINCT o.order_id) AS avg_order_value
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.order_date >= CURRENT_DATE - INTERVAL '30 days'
AND o.order_status = 'completed'
GROUP BY DATE(o.order_date)
ORDER BY sale_date DESC;
Inventory Turnover (SQL)
-- Report: inventory_turnover | Monthly | Inventory planners
SELECT p.category, p.sku, p.product_name,
s.avg_inventory, s.total_sold,
CASE WHEN s.avg_inventory > 0
THEN s.total_sold / s.avg_inventory ELSE 0 END AS turnover_ratio,
CASE WHEN s.total_sold > 0
THEN s.avg_inventory / (s.total_sold / 30.0) ELSE 999 END AS days_of_supply
FROM products p
JOIN (
SELECT sku, AVG(quantity_on_hand) AS avg_inventory, SUM(quantity_sold) AS total_sold
FROM daily_stock_snapshots
WHERE snapshot_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY sku
) s ON p.sku = s.sku
ORDER BY turnover_ratio DESC;
Run Report Programmatically
from retail_reports.runner import ReportRunner
runner = ReportRunner(config_path="config.yaml")
# Run a built-in report
result = runner.execute(
report="daily_sales",
params={"start_date": "2026-03-01", "end_date": "2026-03-23"},
output_format="csv"
)
print(f"Report generated: {result['output_path']}")
print(f"Rows: {result['row_count']}, Execution time: {result['elapsed_ms']}ms")
Vendor Scorecard
from retail_reports.runner import ReportRunner
runner = ReportRunner(config_path="config.yaml")
scorecard = runner.execute(
report="vendor_scorecard",
params={"vendor_id": "V-100", "period": "2026-Q1"}
)
for metric in scorecard["data"]:
print(f" {metric['name']}: {metric['value']} (target: {metric['target']})")
Report Catalog
| Category | Count | Key Reports |
|---|---|---|
| Sales | 12 | Daily summary, channel breakdown, category mix, hourly heatmap |
| Inventory | 10 | Turnover, days-of-supply, dead stock, reorder report, shrinkage |
| Vendor | 8 | Fill rate, lead time variance, cost analysis, quality scorecard |
| Customer | 8 | New vs returning, cohort LTV, acquisition cost, geography |
| Seasonal | 6 | YoY comparison, holiday lift, weather impact, promotional ROI |
| Executive | 6 | KPI dashboard, P&L summary, flash report, trend alerts |
Configuration
database:
type: "postgres" # postgres | mysql | sqlite
host: "localhost"
port: 5432
name: "retail_db"
user: "YOUR_DB_USER"
password: "YOUR_DB_PASSWORD"
reports:
output_dir: "./output"
default_format: "csv" # csv | json | html
cache_enabled: false
cache_ttl_minutes: 60
formatting:
currency: "USD"
date_format: "%Y-%m-%d"
Best Practices
- Schedule critical reports — Daily sales by 7 AM, inventory weekly on Monday
- Use production config for real data — Keep dev and prod configs separate
- Add date filters — Never run reports without a date range on large tables
- Cache expensive reports — Enable caching for reports that run hourly+
- Customize templates — These are starting points; adapt column names to your schema
Troubleshooting
| Issue | Cause | Fix |
|---|---|---|
| Report returns no data | Date range excludes all records | Widen start_date / end_date params |
| Slow execution | Missing indexes on date/status columns | Add indexes: CREATE INDEX idx_orders_date ON orders(order_date)
|
| Column name errors | Schema differs from template | Update SQL column names to match your tables |
| CSV encoding issues | Special characters in product names | Set encoding: "utf-8-sig" in config |
This is 1 of 11 resources in the Retail Automation Pro toolkit. Get the complete [Retail Reporting Templates] with all files, templates, and documentation for $29.
Or grab the entire Retail Automation Pro bundle (11 products) for $139 — save 30%.
Top comments (0)