it0/packages/services/billing-service/src/infrastructure/repositories/invoice.repository.ts

79 lines
2.4 KiB
TypeScript

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<Invoice>;
private readonly itemRepo: Repository<InvoiceItem>;
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<Invoice | null> {
return this.repo.findOne({ where: { id }, relations: ['items'] });
}
async findByInvoiceNumber(invoiceNumber: string): Promise<Invoice | null> {
return this.repo.findOne({ where: { invoiceNumber } });
}
async findUnpaidByTenantId(tenantId: string): Promise<Invoice[]> {
return this.repo.find({
where: [
{ tenantId, status: InvoiceStatus.OPEN },
{ tenantId, status: InvoiceStatus.PAST_DUE },
],
});
}
async getNextInvoiceNumber(): Promise<string> {
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<Invoice> {
return this.repo.save(invoice);
}
async saveItems(items: InvoiceItem[]): Promise<InvoiceItem[]> {
return this.itemRepo.save(items);
}
async saveWithItems(invoice: Invoice, items: InvoiceItem[]): Promise<Invoice> {
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();
}
}
}