const BaseFinancialRepository = require('./BaseFinancialRepository'); const logger = require('../../../src/utils/logger'); /** * Invoice Repository * * Manages invoice creation, tracking, and payments for units. */ class InvoiceRepository extends BaseFinancialRepository { constructor() { super('pg_fn_invoices'); } /** * Find invoices for a unit * @param {string} unitId - Unit ID * @param {Object} options - Query options * @returns {Promise} Array of invoices */ async findByUnitId(unitId, options = {}) { try { return await this.findAll({ unit_id: unitId }, { orderBy: 'issue_date', orderDirection: 'desc', ...options }); } catch (error) { logger.error('Error finding invoices by unit:', error); throw error; } } /** * Find invoices for a site * @param {string} siteId - Site ID * @param {Object} options - Query options * @returns {Promise} Array of invoices */ async findBySiteId(siteId, options = {}) { try { return await this.findAll({ site_id: siteId }, { orderBy: 'issue_date', orderDirection: 'desc', ...options }); } catch (error) { logger.error('Error finding invoices by site:', error); throw error; } } /** * Find invoices by status * @param {string} siteId - Site ID * @param {string} status - Invoice status (pending, paid, overdue, cancelled) * @returns {Promise} Array of invoices */ async findByStatus(siteId, status) { try { return await this.findAll({ site_id: siteId, status }, { orderBy: 'due_date' }); } catch (error) { logger.error('Error finding invoices by status:', error); throw error; } } /** * Find invoice by invoice number * @param {string} siteId - Site ID * @param {string} invoiceNumber - Invoice number * @returns {Promise} Invoice or null */ async findByInvoiceNumber(siteId, invoiceNumber) { try { return await this.findOne({ site_id: siteId, invoice_number: invoiceNumber }); } catch (error) { logger.error('Error finding invoice by number:', error); throw error; } } /** * Find overdue invoices * @param {string} siteId - Site ID * @returns {Promise} Array of overdue invoices */ async findOverdue(siteId) { try { const today = new Date().toISOString().split('T')[0]; return await this.findWhere({ site_id: { eq: siteId }, status: { eq: 'pending' }, due_date: { lt: today } }); } catch (error) { logger.error('Error finding overdue invoices:', error); throw error; } } /** * Create invoice with validation * @param {Object} invoiceData - Invoice data * @returns {Promise} Created invoice */ async createInvoice(invoiceData) { try { // Validate required fields if (!invoiceData.site_id || !invoiceData.unit_id || !invoiceData.invoice_number || !invoiceData.invoice_type || !invoiceData.description || !invoiceData.amount || !invoiceData.due_date) { throw new Error('Missing required fields for invoice creation'); } // Check if invoice number already exists for this site const existingInvoice = await this.findByInvoiceNumber(invoiceData.site_id, invoiceData.invoice_number); if (existingInvoice) { throw new Error('Invoice with this number already exists for this site'); } // Set defaults const invoice = { ...invoiceData, status: invoiceData.status || 'pending', issue_date: invoiceData.issue_date || new Date().toISOString().split('T')[0], created_at: new Date().toISOString(), updated_at: new Date().toISOString() }; return await this.create(invoice); } catch (error) { logger.error('Error creating invoice:', error); throw error; } } /** * Mark invoice as paid * @param {string} invoiceId - Invoice ID * @param {Object} paymentInfo - Payment information * @returns {Promise} Updated invoice */ async markAsPaid(invoiceId, paymentInfo = {}) { try { const update = { status: 'paid', payment_date: paymentInfo.payment_date || new Date().toISOString().split('T')[0], payment_method: paymentInfo.payment_method, payment_reference: paymentInfo.payment_reference, updated_at: new Date().toISOString() }; return await this.updateById(invoiceId, update); } catch (error) { logger.error('Error marking invoice as paid:', error); throw error; } } /** * Mark invoice as overdue * @param {string} invoiceId - Invoice ID * @returns {Promise} Updated invoice */ async markAsOverdue(invoiceId) { try { return await this.updateById(invoiceId, { status: 'overdue', updated_at: new Date().toISOString() }); } catch (error) { logger.error('Error marking invoice as overdue:', error); throw error; } } /** * Get invoice statistics for a unit * @param {string} unitId - Unit ID * @returns {Promise} Invoice statistics */ async getInvoiceStats(unitId) { try { const invoices = await this.findByUnitId(unitId); const stats = { total: invoices.length, pending: invoices.filter(i => i.status === 'pending').length, paid: invoices.filter(i => i.status === 'paid').length, overdue: invoices.filter(i => i.status === 'overdue').length, totalAmount: invoices.reduce((sum, inv) => sum + parseFloat(inv.amount), 0), paidAmount: invoices .filter(i => i.status === 'paid') .reduce((sum, inv) => sum + parseFloat(inv.amount), 0), pendingAmount: invoices .filter(i => i.status === 'pending' || i.status === 'overdue') .reduce((sum, inv) => sum + parseFloat(inv.amount), 0) }; return stats; } catch (error) { logger.error('Error getting invoice stats:', error); throw error; } } /** * Find invoices for work order * @param {string} workOrderId - Work order ID * @returns {Promise} Array of invoices */ async findByWorkOrderId(workOrderId) { try { return await this.findAll({ work_order_id: workOrderId }, { orderBy: 'created_at' }); } catch (error) { logger.error('Error finding invoices by work order:', error); throw error; } } } module.exports = InvoiceRepository;