# easy-invoice/2.3.3/assets/js/reports.js

Easy Invoice – Invoice Generator, PDF Quotes &amp; Payments, version 2.3.3. 654 lines.

- Page: https://pluginprobe.com/plugins/easy-invoice/2.3.3/code/assets/js/reports.js
- Raw: https://pluginprobe.com/plugins/easy-invoice/2.3.3/raw/assets/js/reports.js
- Modified: 2025-08-14T09:51:16+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/easy-invoice/2.3.3/code/assets/js/reports.js#L10-L20`.

```javascript
/**
 * Easy Invoice Reports JavaScript
 * 
 * Handles all reporting functionality including tab switching, 
 * chart rendering, and export features for the reports page.
 * 
 * @package EasyInvoice
 * @since 1.0.0
 */

(function($) {
    'use strict';

    const EasyInvoiceReports = {
        /**
         * Initialize the reporting functionality
         */
        init: function() {
            this.ensurePaymentTabVisibility();
            this.initCharts();
            this.setupTabSwitching();
            this.setupExportHandlers();
        },

        /**
         * Ensure payment tab is properly displayed by default
         */
        ensurePaymentTabVisibility: function() {
            const paymentReportTab = document.getElementById('payment-report-content');
            const invoiceReportTab = document.getElementById('invoice-report-content');
            
            if (paymentReportTab && invoiceReportTab) {
                // Set proper visibility immediately
                paymentReportTab.classList.remove('hidden');
                paymentReportTab.classList.add('block');
                paymentReportTab.style.display = 'block';
                
                invoiceReportTab.classList.add('hidden');
                invoiceReportTab.classList.remove('block');
                invoiceReportTab.style.display = 'none';
                
                // Set proper button state
                const paymentButton = document.getElementById('tab-payment-report');
                const invoiceButton = document.getElementById('tab-invoice-report');
                
                if (paymentButton && invoiceButton) {
                    paymentButton.classList.add('active-tab', 'border-indigo-500', 'text-indigo-600');
                    paymentButton.classList.remove('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300');
                    
                    invoiceButton.classList.remove('active-tab', 'border-indigo-500', 'text-indigo-600');
                    invoiceButton.classList.add('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300');
                }
                
                // Also set the visibility with a delay to override any other scripts
                setTimeout(function() {
                    paymentReportTab.classList.remove('hidden');
                    paymentReportTab.classList.add('block');
                    paymentReportTab.style.display = 'block !important';
                    
                    invoiceReportTab.classList.add('hidden');
                    invoiceReportTab.classList.remove('block');
                    invoiceReportTab.style.display = 'none';
                }, 100);
            }
        },
        
        /**
         * Initialize all chart visualizations
         */
        initCharts: function() {
            this.initRevenueChart();
            this.initStatusChart();
            this.initPaymentMethodsChart();
            this.initInvoiceStatusChart();
        },
        
        /**
         * Initialize monthly revenue chart
         */
        initRevenueChart: function() {
            try {
                if (!document.getElementById('revenue-chart')) {
                    return;
                }
                
                const monthlyRevenue = easy_invoice_reports.monthly_revenue || {};
                const monthlyRevenueLabels = Object.keys(monthlyRevenue);
                const monthlyRevenueData = Object.values(monthlyRevenue);
                
                const revenueCtx = document.getElementById('revenue-chart').getContext('2d');
                new Chart(revenueCtx, {
                    type: 'bar',
                    data: {
                        labels: monthlyRevenueLabels,
                        datasets: [{
                            label: 'Revenue',
                            data: monthlyRevenueData,
                            backgroundColor: 'rgba(79, 70, 229, 0.2)',
                            borderColor: 'rgba(79, 70, 229, 1)',
                            borderWidth: 1
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        scales: {
                            y: {
                                beginAtZero: true,
                                ticks: {
                                    callback: function(value) {
                                        return '$' + value.toLocaleString();
                                    }
                                }
                            }
                        },
                        plugins: {
                            tooltip: {
                                callbacks: {
                                    label: function(context) {
                                        return '$' + context.raw.toLocaleString();
                                    }
                                }
                            }
                        }
                    }
                });
            } catch (error) {
                // Error initializing revenue chart
                this.handleChartError('revenue-chart');
            }
        },
        
        /**
         * Initialize invoice status pie chart
         */
        initStatusChart: function() {
            try {
                if (!document.getElementById('status-chart')) {
                    return;
                }
                
                const invoiceStatus = easy_invoice_reports.invoice_status || {};
                
                // Filter out the counts key
                const statusLabels = Object.keys(invoiceStatus).filter(key => key !== 'counts');
                const statusData = statusLabels.map(label => invoiceStatus[label]);
                
                const statusColors = [
                    'rgba(34, 197, 94, 0.8)',  // Green for Paid
                    'rgba(234, 179, 8, 0.8)',   // Yellow for Unpaid
                    'rgba(239, 68, 68, 0.8)',   // Red for Overdue
                    'rgba(209, 213, 219, 0.8)'  // Gray for Draft
                ];
                
                const statusCtx = document.getElementById('status-chart').getContext('2d');
                new Chart(statusCtx, {
                    type: 'doughnut',
                    data: {
                        labels: statusLabels,
                        datasets: [{
                            data: statusData,
                            backgroundColor: statusColors,
                            borderWidth: 1
                        }]
                    },
                    options: {
                        responsive: true,
                        maintainAspectRatio: false,
                        plugins: {
                            legend: {
                                display: false
                            },
                            tooltip: {
                                callbacks: {
                                    label: function(context) {
                                        return context.label + ': ' + context.raw + '%';
                                    }
                                }
                            }
                        },
                        cutout: '70%'
                    }
                });
            } catch (error) {
                // Error initializing status chart
                this.handleChartError('status-chart');
            }
        },
        
        /**
         * Initialize payment methods chart
         */
        initPaymentMethodsChart: function() {
            try {
                if (!document.getElementById('payment-methods-chart')) {
                    return;
                }
                
                const paymentReport = easy_invoice_reports.payment_report || {};
                const paymentMethods = paymentReport.by_method || {};
                
                const methods = [];
                const counts = [];
                const methodColors = [];
                const colorPalette = [
                    'rgba(79, 70, 229, 0.8)', 
                    'rgba(59, 130, 246, 0.8)', 
                    'rgba(16, 185, 129, 0.8)', 
                    'rgba(245, 158, 11, 0.8)', 
                    'rgba(239, 68, 68, 0.8)', 
                    'rgba(107, 114, 128, 0.8)'
                ];
                
                let i = 0;
                for (const method in paymentMethods) {
                    methods.push(this.capitalizeFirst(method));
                    counts.push(paymentMethods[method].count);
                    methodColors.push(colorPalette[i % colorPalette.length]);
                    i++;
                }
                
                if (methods.length > 0) {
                    const paymentMethodsCtx = document.getElementById('payment-methods-chart').getContext('2d');
                    new Chart(paymentMethodsCtx, {
                        type: 'pie',
                        data: {
                            labels: methods,
                            datasets: [{
                                data: counts,
                                backgroundColor: methodColors,
                                borderWidth: 1
                            }]
                        },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            plugins: {
                                legend: {
                                    position: 'right'
                                },
                                tooltip: {
                                    callbacks: {
                                        label: function(context) {
                                            const total = context.dataset.data.reduce((a, b) => a + b, 0);
                                            const percentage = Math.round((context.raw / total) * 100);
                                            return context.label + ': ' + context.raw + ' (' + percentage + '%)';
                                        }
                                    }
                                }
                            }
                        }
                    });
                } else {
                    this.handleChartError('payment-methods-chart', 'No payment method data available');
                }
            } catch (error) {
                // Error initializing payment methods chart
                this.handleChartError('payment-methods-chart');
            }
        },
        
        /**
         * Initialize invoice status detailed chart
         */
        initInvoiceStatusChart: function() {
            try {
                if (!document.getElementById('invoice-status-chart')) {
                    return;
                }
                
                const invoiceReport = easy_invoice_reports.invoice_report || {};
                const statusData = invoiceReport.by_status || {};
                
                const statusList = {
                    'paid': 'Paid',
                    'unpaid': 'Unpaid',
                    'overdue': 'Overdue',
                    'draft': 'Draft',
                    'canceled': 'Canceled',
                    'other': 'Other'
                };
                
                const statusChartColors = [
                    'rgba(34, 197, 94, 0.8)',  // Green for Paid
                    'rgba(234, 179, 8, 0.8)',  // Yellow for Unpaid
                    'rgba(239, 68, 68, 0.8)',  // Red for Overdue
                    'rgba(209, 213, 219, 0.8)', // Gray for Draft
                    'rgba(107, 114, 128, 0.8)', // Dark Gray for Canceled
                    'rgba(156, 163, 175, 0.8)'  // Medium Gray for Other
                ];
                
                const statuses = [];
                const statusCounts = [];
                const colors = [];
                
                let i = 0;
                for (const statusKey in statusList) {
                    const data = statusData[statusKey] || { count: 0 };
                    if (data.count > 0) {
                        statuses.push(statusList[statusKey]);
                        statusCounts.push(data.count);
                        colors.push(statusChartColors[i]);
                    }
                    i++;
                }
                
                if (statuses.length > 0) {
                    const invoiceStatusCtx = document.getElementById('invoice-status-chart').getContext('2d');
                    new Chart(invoiceStatusCtx, {
                        type: 'pie',
                        data: {
                            labels: statuses,
                            datasets: [{
                                data: statusCounts,
                                backgroundColor: colors,
                                borderWidth: 1
                            }]
                        },
                        options: {
                            responsive: true,
                            maintainAspectRatio: false,
                            plugins: {
                                legend: {
                                    position: 'right'
                                },
                                tooltip: {
                                    callbacks: {
                                        label: function(context) {
                                            const total = context.dataset.data.reduce((a, b) => a + b, 0);
                                            const percentage = Math.round((context.raw / total) * 100);
                                            return context.label + ': ' + context.raw + ' (' + percentage + '%)';
                                        }
                                    }
                                }
                            }
                        }
                    });
                } else {
                    this.handleChartError('invoice-status-chart', 'No invoice status data available');
                }
            } catch (error) {
                // Error initializing invoice status chart
                this.handleChartError('invoice-status-chart'); 
            }
        },
        
        /**
         * Handle chart initialization errors
         */
        handleChartError: function(canvasId, message) {
            const canvas = document.getElementById(canvasId);
            if (!canvas) return;
            
            const ctx = canvas.getContext('2d');
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.font = '14px Arial';
            ctx.fillStyle = '#6B7280';
            ctx.textAlign = 'center';
            ctx.fillText(message || 'Error loading chart data', canvas.width / 2, canvas.height / 2);
        },
        
        /**
         * Setup tab switching functionality
         */
        setupTabSwitching: function() {
            const self = this;
            const tabButtons = document.querySelectorAll('.tab-button');
            
            tabButtons.forEach(button => {
                button.addEventListener('click', function(e) {
                    // Prevent default behavior that might cause page reload
                    e.preventDefault();
                    
                    // Get button ID safely
                    const buttonId = this.id || '';
                    
                    // Store the target tab ID before modifying classes
                    const targetId = buttonId.replace('tab-', '') + '-content';
                    
                    // Remove active class from all buttons first
                    tabButtons.forEach(btn => {
                        btn.classList.remove('active-tab', 'border-indigo-500', 'text-indigo-600');
                        btn.classList.add('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300');
                    });
                    
                    // Add active class to clicked button
                    this.classList.add('active-tab', 'border-indigo-500', 'text-indigo-600');
                    this.classList.remove('border-transparent', 'text-gray-500', 'hover:text-gray-700', 'hover:border-gray-300');
                    
                    // Get all tab contents
                    const paymentReportTab = document.getElementById('payment-report-content');
                    const invoiceReportTab = document.getElementById('invoice-report-content');
                    
                    // Hide both tabs first
                    if (paymentReportTab) {
                        paymentReportTab.classList.add('hidden');
                        paymentReportTab.classList.remove('block');
                        paymentReportTab.style.display = 'none';
                    }
                    
                    if (invoiceReportTab) {
                        invoiceReportTab.classList.add('hidden');
                        invoiceReportTab.classList.remove('block');
                        invoiceReportTab.style.display = 'none';
                    }
                    
                    // Show the target tab
                    const targetTab = document.getElementById(targetId);
                    if (targetTab) {
                        // Short delay helps ensure DOM updates properly
                        setTimeout(() => {
                            targetTab.classList.remove('hidden');
                            targetTab.classList.add('block');
                            targetTab.style.display = 'block';
                        }, 10);
                    } else {
                        // Fallback to payment report if target not found
                        if (paymentReportTab) {
                            setTimeout(() => {
                                paymentReportTab.classList.remove('hidden');
                                paymentReportTab.classList.add('block');
                                paymentReportTab.style.display = 'block';
                            }, 10);
                        }
                    }
                });
            });
        },
        
        /**
         * Setup report export functionality
         */
        setupExportHandlers: function() {
            this.setupMainReportExport();
            this.setupDetailedReportExport();
        },
        
        /**
         * Setup main report export
         */
        setupMainReportExport: function() {
            const self = this;
            const exportButton = document.getElementById('export-report');
            
            if (!exportButton) return;
            
            exportButton.addEventListener('click', function() {
                // Create a timestamp for the filename
                const timestamp = self.getFormattedDate();
                
                // Generate CSV content
                let csvContent = 'data:text/csv;charset=utf-8,';
                
                // Add report title and date range
                const title = document.querySelector('h1') ? document.querySelector('h1').textContent : 'Reports';
                csvContent += 'Easy Invoice Report: ' + title + '\r\n';
                csvContent += 'Date Range: ' + easy_invoice_reports.start_date + ' to ' + easy_invoice_reports.end_date + '\r\n\r\n';
                
                // Add summary stats from the global data
                const stats = easy_invoice_reports.summary_stats || {};
                csvContent += 'Summary Statistics\r\n';
                csvContent += 'Total Revenue,$' + self.formatNumber(stats.total_revenue) + '\r\n';
                csvContent += 'Total Invoices,' + stats.total_invoices + '\r\n';
                csvContent += 'Active Clients,' + stats.active_clients + '\r\n';
                csvContent += 'Average Payment Time,' + stats.avg_payment_time + ' days\r\n\r\n';
                
                // Add monthly revenue
                const monthlyRevenue = easy_invoice_reports.monthly_revenue || {};
                csvContent += 'Monthly Revenue\r\n';
                csvContent += 'Month,Revenue\r\n';
                
                for (const month in monthlyRevenue) {
                    csvContent += month + ',$' + self.formatNumber(monthlyRevenue[month]) + '\r\n';
                }
                csvContent += '\r\n';
                
                // Add invoice status
                const invoiceStatus = easy_invoice_reports.invoice_status || {};
                csvContent += 'Invoice Status\r\n';
                csvContent += 'Status,Percentage,Count\r\n';
                
                for (const status in invoiceStatus) {
                    if (status !== 'counts') {
                        const counts = invoiceStatus.counts || {};
                        const count = counts[status.toLowerCase()] || 0;
                        csvContent += status + ',' + invoiceStatus[status] + '%,' + count + '\r\n';
                    }
                }
                csvContent += '\r\n';
                
                // Add top clients
                const topClients = easy_invoice_reports.top_clients || [];
                csvContent += 'Top Clients by Revenue\r\n';
                csvContent += 'Client,Email,Total Amount,Invoices,Last Invoice\r\n';
                
                topClients.forEach(function(client) {
                    const lastInvoice = client.last_invoice ? self.formatDate(client.last_invoice) : 'N/A';
                    csvContent += self.escapeCsvValue(client.name) + ',' + 
                                  self.escapeCsvValue(client.email) + ',' + 
                                  '$' + self.formatNumber(client.total_amount) + ',' + 
                                  client.total_invoices + ',' + 
                                  lastInvoice + '\r\n';
                });
                
                self.triggerDownload(csvContent, 'easy-invoice-report-' + timestamp + '.csv');
            });
        },
        
        /**
         * Setup detailed report export
         */
        setupDetailedReportExport: function() {
            const self = this;
            const exportButton = document.getElementById('export-detailed-report');
            
            if (!exportButton) return;
            
            exportButton.addEventListener('click', function() {
                // Create a timestamp for the filename
                const timestamp = self.getFormattedDate();
                
                // Determine which tab is active
                const activeTab = document.querySelector('.tab-button.active-tab');
                if (!activeTab) return;
                
                const activeTabId = activeTab.id;
                let reportType = activeTabId === 'tab-payment-report' ? 'payment' : 'invoice';
                let reportData, fileName, headers, rows = [];
                
                if (reportType === 'payment') {
                    // Export payment report
                    const paymentReport = easy_invoice_reports.payment_report || {};
                    reportData = paymentReport.payments || [];
                    fileName = 'easy-invoice-payment-report-' + timestamp + '.csv';
                    headers = ['Date', 'Invoice', 'Amount', 'Method', 'Status', 'Transaction ID'];
                    
                    // Generate rows
                    reportData.forEach(function(payment) {
                        const date = self.formatDate(payment.date);
                        const row = [
                            date,
                            payment.invoice_number,
                            payment.currency_symbol + self.formatNumber(payment.amount),
                            self.capitalizeFirst(payment.payment_method),
                            self.capitalizeFirst(payment.status),
                            payment.transaction_id || '-'
                        ];
                        rows.push(row);
                    });
                } else {
                    // Export invoice report
                    const invoiceReport = easy_invoice_reports.invoice_report || {};
                    reportData = invoiceReport.invoices || [];
                    fileName = 'easy-invoice-invoice-report-' + timestamp + '.csv';
                    headers = ['Date', 'Due Date', 'Invoice #', 'Client', 'Amount', 'Status'];
                    
                    // Generate rows
                    reportData.forEach(function(invoice) {
                        const issueDate = self.formatDate(invoice.issue_date);
                        const dueDate = invoice.due_date ? self.formatDate(invoice.due_date) : '-';
                        const row = [
                            issueDate,
                            dueDate,
                            invoice.invoice_number,
                            self.escapeCsvValue(invoice.client_name),
                            '$' + self.formatNumber(invoice.total),
                            self.capitalizeFirst(invoice.status)
                        ];
                        rows.push(row);
                    });
                }
                
                // Generate CSV content
                let csvContent = 'data:text/csv;charset=utf-8,';
                csvContent += headers.join(',') + '\r\n';
                
                rows.forEach(function(row) {
                    csvContent += row.join(',') + '\r\n';
                });
                
                self.triggerDownload(csvContent, fileName);
            });
        },
        
        /**
         * Trigger CSV download
         */
        triggerDownload: function(csvContent, fileName) {
            const encodedUri = encodeURI(csvContent);
            const link = document.createElement('a');
            link.setAttribute('href', encodedUri);
            link.setAttribute('download', fileName);
            document.body.appendChild(link);
            
            // Trigger download
            link.click();
            document.body.removeChild(link);
        },
        
        /**
         * Format date for display
         */
        formatDate: function(dateString) {
            const date = new Date(dateString);
            return date.toLocaleDateString();
        },
        
        /**
         * Get formatted date for filenames
         */
        getFormattedDate: function() {
            const date = new Date();
            return date.getFullYear() + '-' + 
                String(date.getMonth() + 1).padStart(2, '0') + '-' + 
                String(date.getDate()).padStart(2, '0');
        },
        
        /**
         * Format number with commas for thousands
         */
        formatNumber: function(num) {
            return parseFloat(num).toLocaleString('en-US', {
                minimumFractionDigits: 2,
                maximumFractionDigits: 2
            });
        },
        
        /**
         * Capitalize first letter of a string
         */
        capitalizeFirst: function(str) {
            if (!str) return '';
            return str.charAt(0).toUpperCase() + str.slice(1);
        },
        
        /**
         * Escape values for CSV
         */
        escapeCsvValue: function(value) {
            if (!value) return '';
            // If the value contains a comma, quote, or newline, wrap in quotes and escape internal quotes
            if (/[",\n\r]/.test(value)) {
                return '"' + value.replace(/"/g, '""') + '"';
            }
            return value;
        }
    };

    // Initialize when document is ready
    $(document).ready(function() {
        EasyInvoiceReports.init();
    });

})(jQuery); 
```
