Home / Help Center / pos-app / Reports / Sales Reports
📈 Reports

Sales Reports

Sales Reports is a dedicated report workbench with 15 different sales breakdowns in a left-hand menu — from daily/hourly trends to by-product, by-customer, by-salesman, discount and profit-margin analysis — each with its own filters and one-click export.

📍 Menu path: Reports → Sales Reports
👤 Who uses it: Roles with Reports permission (export buttons need Export permission)
🏷️ Plan tier: advanced+

Overview

Unlike the tabbed Key Reports page, Sales Reports uses a left-hand navigation list of 15 report types. Clicking a report loads it in the panel on the right; the selection is stored in the URL as ?r= (e.g. ?r=by-product) so any report can be bookmarked directly.

Every individual report has its own summary cards, its own date range or filter controls, and (where you have Export permission) its own Export button that downloads exactly what is on screen — not the whole report set.

The 15 reports are: Daily Sales Report, Hourly Sales Report, Monthly/Yearly Sales, Sales by Product, Sales by Category, Sales by Brand, Sales by Customer, Sales by Salesman/Cashier, Sales by Payment Mode, Invoice-wise Sales Report, Cancelled Bills Report, Void Bills Report, Discount Report, Profit Margin Report and Return/Refund Report.

Before You Start

Step-by-Step Guide

1 Read time-based sales trends

Covers Daily Sales Report, Hourly Sales Report and Monthly/Yearly Sales.

  1. Open Daily Sales Report and pick a date; the four summary cards (Total Sales, Revenue, Discount, Tax) and payment-method chips update, followed by a full bill-by-bill table you can export.
  2. Open Hourly Sales Report for the same idea broken into 24 hourly buckets for one day; the busiest hour is highlighted with a PEAK badge and a distribution bar per hour.
  3. Open Monthly/Yearly Sales and pick a year from the dropdown; the current month row is highlighted with a NOW badge, and each month shows count, revenue, tax, discount and a trend bar.

2 Break sales down by product, category and brand

  1. Open Sales by Product, set a From/To date range, and read products ranked by revenue with quantity sold and a share-of-total bar.
  2. Open Sales by Category — this reuses the profit engine, so each category shows Revenue, COGS, Gross Profit and a colour-coded Margin % (green ≥30%, amber ≥15%, red below).
  3. Open Sales by Brand for the same ranking idea grouped by product brand instead of category.

3 See who bought and who sold

  1. Open Sales by Customer to rank your customers by revenue and invoice count for the selected date range, with average order value per customer.
  2. Open Sales by Salesman / Cashier to rank staff by revenue, transaction count, average order value and discount given — useful for reviewing cashier performance.

4 Analyse payment modes and the full invoice list

  1. Open Sales by Payment Mode to see Cash/Card/UPI/Wallet/Credit/Split broken down by transaction count, amount and % of total, each with a distribution bar.
  2. Open Invoice-wise Sales Report for a paginated (25 per page) list of every bill in the date range; filter by payment status (Completed/Pending/Refunded) with the dropdown, and page through results with the Prev/Next controls at the bottom.

5 Investigate cancelled, void, discounted and returned bills

  1. Open Cancelled Bills Report or Void Bills Report — both currently pull bills with REFUNDED status; the Void Bills view shows a note explaining that void and cancelled bills share the same status in this system.
  2. Open Discount Report to see every bill that had a discount applied, with gross amount, discount amount, net amount and a colour-coded discount % (red if over 20%).
  3. Open Return / Refund Report for a dedicated list of refunded bills with total refund amount and average refund per bill.

6 Check overall profitability

  1. Open Profit Margin Report, set a date range (defaults to year-to-date), and review the six-card P&L summary (Revenue, COGS, Gross Profit, Expenses, Net Profit, Net Margin).
  2. Scroll to Top Products by Profit and By Category tables, each ranked by gross profit with a colour-coded margin %.

Every Field & Button, Explained

Field / ButtonWhat it does
Left-hand report menuLists all 15 report types; the active one is stored in the URL as ?r= so any single report can be linked to directly.
Daily Sales ReportSingle-date bill list with Total Sales, Revenue, Discount and Tax cards plus a payment-method chip row.
Hourly Sales ReportSame day broken into 24 hourly buckets with a PEAK badge on the busiest hour.
Monthly / Yearly SalesTwelve-month table for a chosen year with a NOW badge on the current month and a revenue trend bar per row.
Sales by ProductProducts ranked by revenue in a date range, with quantity sold and share-of-total bar.
Sales by CategoryCategory breakdown using the profit engine — Revenue, COGS, Gross Profit and colour-coded Margin %.
Sales by BrandSame ranking as Sales by Product but grouped by product brand.
Sales by CustomerCustomers ranked by revenue with invoice count and average order value.
Sales by Salesman / CashierStaff ranked by revenue, transaction count, average order value and discount given.
Sales by Payment ModeCash/Card/UPI/Wallet/Credit/Split breakdown with transaction count, amount and % share.
Invoice-wise Sales ReportPaginated (25/page) full bill list with a payment-status filter dropdown.
Cancelled Bills Report / Void Bills ReportBoth currently query bills with REFUNDED status; Void view shows an explanatory note that void and cancelled share one status today.
Discount ReportBills with discount > 0, showing gross/discount/net amounts and a colour-coded discount %.
Profit Margin ReportYear-to-date-by-default P&L summary plus Top Products by Profit and By Category tables.
Return / Refund ReportDedicated refunded-bills list with total and average refund amount.
Date range picker + This Month/Last Month/This Year quick buttonsShared filter control used by most of the 15 reports (Daily/Hourly use a single date instead).
Export buttonDownloads exactly the table currently on screen for that one report; only shown with Export permission.

Tips & Best Practices

Troubleshooting & FAQ

Sales by Salesman / Cashier is empty or missing staff.
This report only includes sales that have a salesman/cashier recorded on the bill. Check that your billing flow captures that field.
Cancelled Bills Report and Void Bills Report show identical data.
In this system both report types currently query bills with REFUNDED status — a note on the Void Bills view explains this. Enable separate void tracking in Settings if you need them to differ.
Sales by Category or Profit Margin Report shows zero gross profit.
Gross profit needs a Cost Price on the product. Set Cost Price under Products, then reload the report.
Export button is missing on a report.
Export is a separate permission from viewing reports — ask your Admin to grant Export permission under Settings → Staff Permissions.

🧑‍💻 Developer Notes

Source component(s):

  • frontend-app/packages/pos-react-lib/src/pages/SalesReportsPage.jsx

Backend endpoints used:

  • GET /reports/sales/daily (params: date) — Daily & Hourly reports
  • GET /reports/sales (params: from, to, groupBy) — Monthly/Yearly, Sales by Customer, Sales by Payment Mode
  • GET /reports/sales/top-products (params: startDate, endDate, limit) — Sales by Product
  • GET /reports/profit (params: from, to) — Sales by Category, Profit Margin Report
  • GET /reports/sales/by-brand (params: from, to) — Sales by Brand
  • GET /reports/sales/by-salesman (params: from, to) — Sales by Salesman / Cashier
  • GET /sales (params: startDate, endDate, paymentStatus, page, limit) — Invoice-wise, Cancelled/Void (paymentStatus=REFUNDED), Discount Report (client-filtered), Return/Refund Report

Related tables (db-core repositories):

  • Sale
  • SaleItem
  • Product
  • Customer

Redux slices:

  • scope (selectCurrentScope / getScopeQueryParams)

Feature flag key: reports (requires advanced plan tier or above)

Each of the 15 reports is its own React component mapped by id in a COMPONENTS lookup table, dispatched via the ?r= query param — this is why every report can be deep-linked and why adding a new report is just adding a new map entry. Every data-fetching report component owns an AbortController so navigating away mid-request never sets state on an unmounted view.