import { Repository, FindOptionsWhere } from 'typeorm'; import { Invoice, Payment } from '../entities'; import { CreateInvoiceDto, UpdateInvoiceDto, CreatePaymentDto } from '../dto'; export interface InvoiceSearchParams { tenantId: string; invoiceType?: string; partnerId?: string; status?: string; limit?: number; offset?: number; } export class InvoicesService { constructor( private readonly invoiceRepository: Repository, private readonly paymentRepository: Repository ) {} async findAllInvoices(params: InvoiceSearchParams): Promise<{ data: Invoice[]; total: number }> { const { tenantId, invoiceType, partnerId, status, limit = 50, offset = 0 } = params; const where: FindOptionsWhere = { tenantId }; if (invoiceType) where.invoiceType = invoiceType as any; if (partnerId) where.partnerId = partnerId; if (status) where.status = status as any; const [data, total] = await this.invoiceRepository.findAndCount({ where, take: limit, skip: offset, order: { createdAt: 'DESC' } }); return { data, total }; } async findInvoice(id: string, tenantId: string): Promise { return this.invoiceRepository.findOne({ where: { id, tenantId } }); } async createInvoice(tenantId: string, dto: CreateInvoiceDto, createdBy?: string): Promise { const count = await this.invoiceRepository.count({ where: { tenantId, invoiceType: dto.invoiceType } }); const prefix = dto.invoiceType === 'sale' ? 'FAC' : dto.invoiceType === 'purchase' ? 'FP' : dto.invoiceType === 'credit_note' ? 'NC' : 'ND'; const invoiceNumber = `${prefix}-${String(count + 1).padStart(6, '0')}`; const invoice = this.invoiceRepository.create({ ...dto, tenantId, invoiceNumber, createdBy, invoiceDate: dto.invoiceDate ? new Date(dto.invoiceDate) : new Date(), dueDate: dto.dueDate ? new Date(dto.dueDate) : undefined }); return this.invoiceRepository.save(invoice); } async updateInvoice(id: string, tenantId: string, dto: UpdateInvoiceDto, updatedBy?: string): Promise { const invoice = await this.findInvoice(id, tenantId); if (!invoice) return null; Object.assign(invoice, { ...dto, updatedBy }); return this.invoiceRepository.save(invoice); } async deleteInvoice(id: string, tenantId: string): Promise { const result = await this.invoiceRepository.softDelete({ id, tenantId }); return (result.affected ?? 0) > 0; } async validateInvoice(id: string, tenantId: string, userId?: string): Promise { const invoice = await this.findInvoice(id, tenantId); if (!invoice || invoice.status !== 'draft') return null; invoice.status = 'validated'; invoice.updatedBy = userId ?? null; return this.invoiceRepository.save(invoice); } async findAllPayments(tenantId: string, partnerId?: string, limit = 50, offset = 0): Promise<{ data: Payment[]; total: number }> { const where: FindOptionsWhere = { tenantId }; if (partnerId) where.partnerId = partnerId; const [data, total] = await this.paymentRepository.findAndCount({ where, take: limit, skip: offset, order: { createdAt: 'DESC' } }); return { data, total }; } async findPayment(id: string, tenantId: string): Promise { return this.paymentRepository.findOne({ where: { id, tenantId } }); } async createPayment(tenantId: string, dto: CreatePaymentDto, createdBy?: string): Promise { const count = await this.paymentRepository.count({ where: { tenantId } }); const paymentNumber = `PAG-${String(count + 1).padStart(6, '0')}`; const payment = this.paymentRepository.create({ ...dto, tenantId, paymentNumber, createdBy, paymentDate: dto.paymentDate ? new Date(dto.paymentDate) : new Date() }); return this.paymentRepository.save(payment); } async confirmPayment(id: string, tenantId: string, userId?: string): Promise { const payment = await this.findPayment(id, tenantId); if (!payment || payment.status !== 'draft') return null; payment.status = 'confirmed'; return this.paymentRepository.save(payment); } }