The Sales Reports module provides comprehensive transaction analysis with date filtering and export options for accounting and business intelligence.
Overview
Sales reports allow you to:
Filter transactions by date range
View detailed transaction history
Calculate totals and transaction counts
Export data to CSV for spreadsheet analysis
Print formatted reports as PDF
Date Range Filtering View sales for any date range with instant updates
CSV Export Download transaction data for Excel or Google Sheets
PDF Printing Generate printable reports for physical records
Real-time Totals Automatic calculation of totals and item counts
Date Range Selection
Reports default to today’s date but can be adjusted to any range:
import { getSalesInRange } from './db.js' ;
import { money } from './shared.js' ;
const fromDate = document . getElementById ( 'fromDate' );
const toDate = document . getElementById ( 'toDate' );
function setDefaultDates () {
const today = new Date (). toISOString (). slice ( 0 , 10 );
fromDate . value = today ;
toDate . value = today ;
}
// Auto-load on date change
fromDate . addEventListener ( 'change' , loadReport );
toDate . addEventListener ( 'change' , loadReport );
Reports update automatically when you change either date field, providing instant feedback without clicking a submit button.
Loading Report Data
The report loader queries IndexedDB with the selected date range:
async function loadReport () {
const from = new Date ( fromDate . value || '1970-01-01' );
const to = new Date ( toDate . value || '2999-12-31' );
to . setHours ( 23 , 59 , 59 , 999 );
const rows = await getSalesInRange ( from . toISOString (), to . toISOString ());
const total = rows . reduce (( a , r ) => a + r . total , 0 );
reportSummary . textContent = ` ${ rows . length } transactions, total ₹ ${ money ( total ) } ` ;
reportTable . innerHTML = tableHTML ( rows );
return rows ;
}
The end date is automatically adjusted to include all transactions up to 11:59:59 PM on the selected day.
Report Table
Transactions are displayed in a structured table with totals:
function tableHTML ( rows ) {
if ( rows . length === 0 ) {
return '<div class="text-center text-gray-500 py-8">No sales in selected range</div>' ;
}
return `
<table class="min-w-full border-collapse border border-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="border border-gray-200 px-4 py-2 text-left font-semibold">ID</th>
<th class="border border-gray-200 px-4 py-2 text-left font-semibold">Date</th>
<th class="border border-gray-200 px-4 py-2 text-right font-semibold">Items</th>
<th class="border border-gray-200 px-4 py-2 text-right font-semibold">Total</th>
</tr>
</thead>
<tbody>
${ rows . map ( r => `
<tr class="hover:bg-gray-50">
<td class="border border-gray-200 px-4 py-2"> ${ r . id } </td>
<td class="border border-gray-200 px-4 py-2"> ${ new Date ( r . date ). toLocaleString () } </td>
<td class="border border-gray-200 px-4 py-2 text-right"> ${ r . items . reduce (( a , b ) => a + b . qty , 0 ) } </td>
<td class="border border-gray-200 px-4 py-2 text-right font-semibold">₹ ${ money ( r . total ) } </td>
</tr>
` ). join ( '' ) }
<tr class="bg-gray-50 font-bold">
<td colspan="3" class="border border-gray-200 px-4 py-2 text-right">Grand Total:</td>
<td class="border border-gray-200 px-4 py-2 text-right">₹ ${ money ( rows . reduce (( a , r ) => a + r . total , 0 )) } </td>
</tr>
</tbody>
</table>
` ;
}
Table Columns
ID : Unique transaction identifier
Date : Transaction timestamp in local format
Items : Total quantity of items sold in the transaction
Total : Final transaction amount after discounts
CSV Export
Generate downloadable CSV files for external analysis:
exportCsvBtn . addEventListener ( 'click' , async () => {
const rows = await loadReport ();
if ( rows . length === 0 ) return ;
const header = [ 'ID' , 'Date' , 'Items' , 'Total' ];
const lines = [ header . join ( ',' )];
rows . forEach ( r => {
const items = r . items . reduce (( a , b ) => a + b . qty , 0 );
lines . push ([ r . id , r . date , items , money ( r . total )]. join ( ',' ));
});
const csv = lines . join ( ' \n ' );
const blob = new Blob ([ csv ], { type: 'text/csv' });
const url = URL . createObjectURL ( blob );
const a = document . createElement ( 'a' );
a . href = url ;
a . download = `sales-report- ${ fromDate . value } -to- ${ toDate . value } .csv` ;
document . body . appendChild ( a );
a . click ();
document . body . removeChild ( a );
URL . revokeObjectURL ( url );
});
CSV files are automatically named with the date range, making it easy to organize exported reports.
Exported CSV files contain:
Header row with column names
One row per transaction
Comma-separated values
Compatible with Excel, Google Sheets, and accounting software
PDF Printing
Generate printable reports for physical archiving:
printPdfBtn . addEventListener ( 'click' , async () => {
const rows = await loadReport ();
if ( rows . length === 0 ) return ;
printArea . style . display = 'block' ;
printArea . innerHTML = `
<div>
<h1 class="text-2xl font-bold mb-2">Sales Report</h1>
<p class="text-sm mb-4">Period: ${ fromDate . value } to ${ toDate . value } </p>
${ tableHTML ( rows ) }
</div>
` ;
window . print ();
setTimeout (() => { printArea . style . display = 'none' ; }, 100 );
});
The print dialog uses a special hidden area that becomes visible only during printing, keeping the interface clean.
Database Query
Reports use an indexed query for fast date range filtering:
export async function getSalesInRange ( fromISO , toISO ) {
return tx ( 'sales' , 'readonly' , ( s ) => {
const idx = s . index ( 'date' );
const range = IDBKeyRange . bound ( fromISO , toISO );
return new Promise (( resolve ) => {
const rows = [];
idx . openCursor ( range ). onsuccess = ( e ) => {
const cur = e . target . result ;
if ( cur ) { rows . push ( cur . value ); cur . continue (); }
else resolve ( rows );
};
});
});
}
The date index on the sales store makes range queries extremely fast, even with thousands of transactions.
User Workflow
Navigate to Reports page
Reports default to today’s date
Adjust “From Date” if needed
Adjust “To Date” if needed
Report updates automatically
Review summary (transaction count, total)
Review detailed table
Export options:
Click “Export CSV” for spreadsheet analysis
Click “Print PDF” for physical copy
Files are named with the date range
Set date range to a week or month
Note the total transactions and revenue
Export CSV for deeper analysis
Use spreadsheet software to:
Create charts and graphs
Calculate averages
Identify peak sales periods
Compare different time periods
Report Features
Real-time Calculation
Transaction Count : Automatically counted from filtered results
Grand Total : Sum of all transaction totals in the range
Item Totals : Calculated per transaction by summing item quantities
Empty State
When no transactions exist in the selected range:
if ( rows . length === 0 ) {
return '<div class="text-center text-gray-500 py-8">No sales in selected range</div>' ;
}
Best Practices
Regular Exports : Export reports weekly or monthly for backup
Date Accuracy : Double-check date ranges before exporting
File Organization : Use the auto-generated filenames to organize exports
Trend Analysis : Compare different date ranges to identify patterns
Accounting Integration : Import CSV files into accounting software
Print Backups : Keep physical copies of monthly reports for compliance
Indexed Queries : Date-based filtering uses IndexedDB indexes for speed
Client-side Processing : All calculations happen in the browser
No Server Required : Reports work completely offline
Instant Updates : Date changes trigger immediate report refresh
Use Case : Data analysis, accounting software, spreadsheetsFormat : Comma-separated valuesFile Extension : .csvOpens With : Excel, Google Sheets, LibreOffice CalcBenefits : Editable, can create charts, calculate custom metrics
Use Case : Physical records, compliance, sharingFormat : Printable HTML rendered via browser printFile Extension : .pdf (when saving from print dialog)Opens With : Any PDF reader or printerBenefits : Fixed format, professional appearance, archival quality
Related Pages