plugin-financials/repositories/InvoiceRepository.js
2025-11-03 13:51:33 +02:00

224 lines
6.5 KiB
JavaScript

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>} 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>} 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>} 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<Object|null>} 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>} 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<Object>} 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<Object>} 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<Object>} 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<Object>} 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>} 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;