Analytics & Reports

Overview

Bambu aggregates all raw data (Sales, Expenses, Debts) and transforms it into actionable charts and CSV/Excel exports for business owners.

Why it exists

Data is useless without insight. Small shop owners need simple visual answers to questions like: "Am I profitable today?", "What products generate the most cash?", and "Are my debts spiraling out of control?".

User flow

  1. User clicks the Analytics tab.
  2. The UI renders Recharts.js line and bar graphs showing Gross Revenue vs. Net Profit over the last 30 days.
  3. The user cycles the time-series filters (Today, This Week, This Month, Last Year).
  4. The user clicks "Export to Excel" to download their raw ledger.

UI walkthrough

![Analytics Overview Graphs](../../assets/images/analytics/graphs.png) ![Top Products Table](../../assets/images/analytics/top-products.png)

Backend logic

Because querying calculating margin aggregates across tens of thousands of sales rows dynamically is slow:

  1. Redis is used heavily. GET /api/analytics/metrics hits the Redis layer first.
  2. If stale or missing, Prisma executes grouped summation queries (sum, avg) inside the specific tenant schema.
  3. The payload is cached for 15 minutes to reduce database load.
  4. Excel exporting uses exceljs library within Node.js to stream a buffer directly to the browser.

Database tables involved

Tenant Schema:

  • Sale, SaleItem, Expense: Queried via aggregations. No mutations occur here.

API endpoints

  • GET /api/analytics/metrics - High-level summation figures.
  • GET /api/analytics/reports - Export endpoint returning application headers (e.g., CSV/XLSX text blobs).

Permissions / roles

  • Owner: Full dashboard visibility.
  • Cashier: Access completely denied. An attempted route bypass throws a 403.

Edge cases

  • Changing time zones. The user's browser TZ is sent in headers so Next.js groups "Today's" sales based on their local time, not UTC.

Validation rules

  • Date range queries are restricted to a maximum 1-year spread to prevent out-of-memory crashes on Vercel Edge functions.

Error handling

  • If exceljs fails to stream, a generic 500 triggers the React Error Boundary, asking the user to try a smaller date window.

Screenshots placeholders

![Export Loading State](../../assets/images/analytics/export-loading.png)

Troubleshooting

  • "Why does my Gross Revenue not match the cash in my drawer?" -> Because Gross Revenue includes sales made on Credit/Debt. Focus on the "Cash Collected" metric instead.

Related features