import { Injectable } from '@nestjs/common'; import { DataSource, Repository } from 'typeorm'; import { Invoice, InvoiceStatus } from '../../domain/entities/invoice.entity'; import { InvoiceItem } from '../../domain/entities/invoice-item.entity'; @Injectable() export class InvoiceRepository { private readonly repo: Repository; private readonly itemRepo: Repository; constructor(private readonly dataSource: DataSource) { this.repo = this.dataSource.getRepository(Invoice); this.itemRepo = this.dataSource.getRepository(InvoiceItem); } async findByTenantId(tenantId: string, limit = 20, offset = 0): Promise<[Invoice[], number]> { return this.repo.findAndCount({ where: { tenantId }, order: { createdAt: 'DESC' }, take: limit, skip: offset, }); } async findById(id: string): Promise { return this.repo.findOne({ where: { id }, relations: ['items'] }); } async findByInvoiceNumber(invoiceNumber: string): Promise { return this.repo.findOne({ where: { invoiceNumber } }); } async findUnpaidByTenantId(tenantId: string): Promise { return this.repo.find({ where: [ { tenantId, status: InvoiceStatus.OPEN }, { tenantId, status: InvoiceStatus.PAST_DUE }, ], }); } async getNextInvoiceNumber(): Promise { const result = await this.dataSource.query( `SELECT nextval('billing_invoice_number_seq') AS seq`, ); const seq = result[0]?.seq ?? Date.now(); const year = new Date().getFullYear(); return `INV-${year}-${String(seq).padStart(6, '0')}`; } async save(invoice: Invoice): Promise { return this.repo.save(invoice); } async saveItems(items: InvoiceItem[]): Promise { return this.itemRepo.save(items); } async saveWithItems(invoice: Invoice, items: InvoiceItem[]): Promise { const runner = this.dataSource.createQueryRunner(); await runner.connect(); await runner.startTransaction(); try { const savedInvoice = await runner.manager.save(Invoice, invoice); for (const item of items) { item.invoiceId = savedInvoice.id; } await runner.manager.save(InvoiceItem, items); await runner.commitTransaction(); return savedInvoice; } catch (err) { await runner.rollbackTransaction(); throw err; } finally { await runner.release(); } } }