diff --git a/public/js/views/accounting-view.js b/public/js/views/accounting-view.js
index 8e3a7fe..52409e8 100644
--- a/public/js/views/accounting-view.js
+++ b/public/js/views/accounting-view.js
@@ -504,6 +504,10 @@ export function injectReportsControls() {
+
@@ -553,8 +557,10 @@ export async function loadTaxSummary() {
export async function loadCustomerRevenue() {
crStartDate = document.getElementById('cr-start').value;
crEndDate = document.getElementById('cr-end').value;
+ const anonymize = document.getElementById('cr-anonymize')?.checked || false;
if (!crStartDate || !crEndDate) return showError('cr-result', 'Please select both start and end dates.');
showLoading('cr-result', 'Loading customer revenue...');
+ const maskName = (name) => anonymize ? name.charAt(0) : name;
try {
const data = await window.API.accounting.getCustomerRevenue(crStartDate, crEndDate);
if (data.error) return showError('cr-result', data.error);
@@ -571,7 +577,7 @@ export async function loadCustomerRevenue() {
const rev = parseFloat(r.total_revenue) || 0;
const pct = grandTotal > 0 ? ((rev / grandTotal) * 100).toFixed(1) : '0.0';
rowsHtml += `
- | ${rank}. ${escapeHtml(r.customer_name)} |
+ ${rank}. ${escapeHtml(maskName(r.customer_name))} |
${r.invoice_count} |
${fmtMoney(rev)} |
${pct}% |
@@ -604,8 +610,10 @@ export async function loadCustomerRevenue() {
export function exportCustomerRevenuePdf() {
const startEl = document.getElementById('cr-start');
const endEl = document.getElementById('cr-end');
+ const anonymize = document.getElementById('cr-anonymize')?.checked || false;
if (!startEl?.value || !endEl?.value) return alert('Please select start and end dates first.');
- const url = `/api/accounting/reports/customer-revenue/pdf?startDate=${startEl.value}&endDate=${endEl.value}`;
+ let url = `/api/accounting/reports/customer-revenue/pdf?startDate=${startEl.value}&endDate=${endEl.value}`;
+ if (anonymize) url += '&anonymize=true';
window.open(url, '_blank');
}
diff --git a/src/routes/accounting.js b/src/routes/accounting.js
index b20fe7e..d37f75d 100644
--- a/src/routes/accounting.js
+++ b/src/routes/accounting.js
@@ -164,7 +164,7 @@ router.get('/reports/customer-revenue', async (req, res) => {
router.get('/reports/customer-revenue/pdf', async (req, res) => {
try {
- const { startDate, endDate } = req.query;
+ const { startDate, endDate, anonymize } = req.query;
if (!startDate || !endDate) {
return res.status(400).json({ error: 'startDate and endDate are required' });
}
@@ -189,6 +189,8 @@ router.get('/reports/customer-revenue/pdf', async (req, res) => {
const rows = result.rows;
const grandTotal = rows.length > 0 ? parseFloat(rows[0].grand_total) || 0 : 0;
const totalInvoices = rows.reduce((s, r) => s + parseInt(r.invoice_count), 0);
+ const doAnonymize = anonymize === 'true';
+ const maskName = (name) => doAnonymize ? name.charAt(0) : name;
let rowsHtml = '';
let rank = 0;
@@ -197,7 +199,7 @@ router.get('/reports/customer-revenue/pdf', async (req, res) => {
const rev = parseFloat(r.total_revenue) || 0;
const pct = grandTotal > 0 ? ((rev / grandTotal) * 100).toFixed(1) : '0.0';
rowsHtml += `
- | ${rank}. ${r.customer_name} |
+ ${rank}. ${maskName(r.customer_name)} |
${r.invoice_count} |
$${formatMoney(rev)} |
${pct}% |
@@ -223,6 +225,7 @@ router.get('/reports/customer-revenue/pdf', async (req, res) => {
.replace('{{SLOGAN}}', 'Providing IT Services and Support in South Texas Since 1996')
.replace('{{DATE_RANGE}}', dateRange)
.replace('{{GENERATED_DATE}}', generated)
+ .replace('{{ANONYMIZED_NOTE}}', doAnonymize ? ' (anonymized)' : '')
.replace('{{ROWS}}', rowsHtml);
const pdf = await generatePdfFromHtml(html);
diff --git a/templates/customer-revenue-template.html b/templates/customer-revenue-template.html
index 34de41f..447f862 100644
--- a/templates/customer-revenue-template.html
+++ b/templates/customer-revenue-template.html
@@ -43,7 +43,7 @@
- CUSTOMER REVENUE REPORT
+ CUSTOMER REVENUE REPORT{{ANONYMIZED_NOTE}}