Overview
The Sales API provides functions for recording sales transactions and querying sales history. All functions are asynchronous and return Promises.
Sale Object Structure
Sales records follow this structure:
{
id: number, // Auto-generated unique identifier
date: string, // ISO 8601 date string (e.g., '2026-03-07T10:30:00.000Z')
items: Array<Object>, // Array of items in the sale
subtotal: number, // Subtotal before discount
discount: number, // Discount amount applied
total: number // Final total after discount
}
Sale Item Structure
Each item in the items array has this structure:
{
id: number, // Product ID
name: string, // Product name
price: number, // Unit price
qty: number // Quantity sold
}
addSale()
Records a new sale transaction in the database.
The sale object containing date, items, and total
Resolves with the auto-generated ID of the new sale record
Implementation
export async function addSale(sale) {
return tx('sales', 'readwrite', (s) => s.add(sale));
}
Example Usage
import { addSale } from './js/db.js';
// Record a simple sale
const sale = {
date: new Date().toISOString(),
items: [
{ id: 1, name: 'Coffee', price: 3.99, qty: 2 },
{ id: 3, name: 'Sandwich', price: 5.50, qty: 1 }
],
subtotal: 13.48,
discount: 0,
total: 13.48
};
const saleId = await addSale(sale);
console.log(`Sale recorded with ID: ${saleId}`);
// Record a sale with discount
const discountedSale = {
date: new Date().toISOString(),
items: [
{ id: 1, name: 'Coffee', price: 3.99, qty: 5 },
{ id: 2, name: 'Tea', price: 2.99, qty: 3 },
{ id: 3, name: 'Sandwich', price: 5.50, qty: 2 }
],
subtotal: 39.92,
discount: 3.99, // 10% discount
total: 35.93
};
await addSale(discountedSale);
Error Handling
try {
const saleId = await addSale(sale);
console.log('Sale recorded successfully');
} catch (error) {
console.error('Failed to record sale:', error);
// Handle: database unavailable, invalid data, etc.
}
Important Notes:
- Always use ISO 8601 date strings for the
date field
- Ensure the
total equals subtotal - discount
- The
items array should not be empty
- Set
discount to 0 if no discount is applied
getSalesInRange()
Retrieves sales records within a specified date range.
Start date in ISO 8601 format (inclusive)
End date in ISO 8601 format (inclusive)
Resolves with an array of sale objects within the date range
Implementation
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);
};
});
});
}
Example Usage
import { getSalesInRange } from './js/db.js';
// Get sales for today
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const todaySales = await getSalesInRange(
today.toISOString(),
tomorrow.toISOString()
);
console.log(`Sales today: ${todaySales.length}`);
// Get sales for a specific week
const weekStart = new Date('2026-03-01T00:00:00.000Z');
const weekEnd = new Date('2026-03-07T23:59:59.999Z');
const weeklySales = await getSalesInRange(
weekStart.toISOString(),
weekEnd.toISOString()
);
// Calculate weekly total
const weeklyTotal = weeklySales.reduce((sum, sale) => sum + sale.total, 0);
console.log(`Weekly total: ₹${weeklyTotal.toFixed(2)}`);
// Get sales for the current month
const monthStart = new Date();
monthStart.setDate(1);
monthStart.setHours(0, 0, 0, 0);
const monthEnd = new Date();
monthEnd.setMonth(monthEnd.getMonth() + 1);
monthEnd.setDate(0);
monthEnd.setHours(23, 59, 59, 999);
const monthlySales = await getSalesInRange(
monthStart.toISOString(),
monthEnd.toISOString()
);
Error Handling
try {
const sales = await getSalesInRange(fromDate, toDate);
if (sales.length === 0) {
console.log('No sales in this period');
}
} catch (error) {
console.error('Failed to retrieve sales:', error);
}
Performance Notes:
- Uses the
date index for efficient range queries
- Returns results in date order (ascending)
- Both start and end dates are inclusive
getAllSales()
Retrieves all sales records from the database.
Resolves with an array of all sale objects
Implementation
export async function getAllSales() {
return tx('sales', 'readonly', (s) => {
return new Promise((resolve) => {
const items = [];
s.openCursor().onsuccess = (e) => {
const cur = e.target.result;
if (cur) { items.push(cur.value); cur.continue(); }
else resolve(items);
};
});
});
}
Example Usage
import { getAllSales } from './js/db.js';
// Get all sales
const allSales = await getAllSales();
console.log(`Total sales: ${allSales.length}`);
// Calculate total revenue
const totalRevenue = allSales.reduce((sum, sale) => sum + sale.total, 0);
console.log(`Total revenue: ₹${totalRevenue.toFixed(2)}`);
// Find best-selling products
const productSales = {};
allSales.forEach(sale => {
sale.items.forEach(item => {
if (!productSales[item.name]) {
productSales[item.name] = 0;
}
productSales[item.name] += item.qty;
});
});
console.log('Product sales:', productSales);
// Get average sale amount
if (allSales.length > 0) {
const average = totalRevenue / allSales.length;
console.log(`Average sale: ₹${average.toFixed(2)}`);
}
Error Handling
try {
const sales = await getAllSales();
console.log(`Retrieved ${sales.length} sales`);
} catch (error) {
console.error('Failed to retrieve sales:', error);
}
Use Cases:
- Generating complete sales reports
- Calculating lifetime revenue
- Analyzing sales patterns
- Exporting data for backup
Performance Considerations:
- Returns all records (can be memory-intensive for large datasets)
- Consider using
getSalesInRange() for filtered queries
- Results are not guaranteed to be in any specific order