"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InvoicesService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const tax_config_entity_1 = require("./entities/tax-config.entity"); const invoice_entity_1 = require("./entities/invoice.entity"); const invoice_item_entity_1 = require("./entities/invoice-item.entity"); let InvoicesService = class InvoicesService { constructor(taxConfigRepo, invoiceRepo, itemRepo, dataSource) { this.taxConfigRepo = taxConfigRepo; this.invoiceRepo = invoiceRepo; this.itemRepo = itemRepo; this.dataSource = dataSource; } async getTaxConfig(tenantId) { return this.taxConfigRepo.findOne({ where: { tenantId } }); } async saveTaxConfig(tenantId, data) { let config = await this.getTaxConfig(tenantId); if (config) { Object.assign(config, data); } else { config = this.taxConfigRepo.create({ tenantId, ...data }); } return this.taxConfigRepo.save(config); } async createInvoice(tenantId, dto) { const taxConfig = await this.getTaxConfig(tenantId); if (!taxConfig || taxConfig.status !== tax_config_entity_1.TaxConfigStatus.ACTIVE) { throw new common_1.BadRequestException('Configuracion fiscal no activa'); } let subtotal = 0; let totalIva = 0; for (const item of dto.items) { const importe = item.cantidad * item.valorUnitario - (item.descuento || 0); subtotal += importe; totalIva += importe * 0.16; } const total = subtotal + totalIva; const folioResult = await this.dataSource.query(`SELECT get_next_invoice_folio($1, $2) as folio`, [tenantId, taxConfig.serie]); const folio = folioResult[0].folio; const invoice = this.invoiceRepo.create({ tenantId, saleId: dto.saleId, tipoComprobante: invoice_entity_1.InvoiceType.INGRESO, serie: taxConfig.serie, folio, receptorRfc: dto.receptorRfc.toUpperCase(), receptorNombre: dto.receptorNombre, receptorRegimenFiscal: dto.receptorRegimenFiscal, receptorCodigoPostal: dto.receptorCodigoPostal, receptorUsoCfdi: dto.receptorUsoCfdi, receptorEmail: dto.receptorEmail, subtotal, totalImpuestosTrasladados: totalIva, total, formaPago: dto.formaPago, metodoPago: dto.metodoPago, condicionesPago: dto.condicionesPago, status: invoice_entity_1.InvoiceStatus.DRAFT, notes: dto.notes, }); await this.invoiceRepo.save(invoice); for (const itemDto of dto.items) { const importe = itemDto.cantidad * itemDto.valorUnitario - (itemDto.descuento || 0); const item = this.itemRepo.create({ invoiceId: invoice.id, productId: itemDto.productId, claveProdServ: itemDto.claveProdServ, descripcion: itemDto.descripcion, cantidad: itemDto.cantidad, claveUnidad: itemDto.claveUnidad, unidad: itemDto.unidad, valorUnitario: itemDto.valorUnitario, descuento: itemDto.descuento || 0, importe, }); await this.itemRepo.save(item); } return this.getInvoice(invoice.id); } async getInvoice(id) { const invoice = await this.invoiceRepo.findOne({ where: { id }, relations: ['items'], }); if (!invoice) { throw new common_1.NotFoundException('Factura no encontrada'); } return invoice; } async getInvoices(tenantId, options) { const query = this.invoiceRepo.createQueryBuilder('invoice') .where('invoice.tenant_id = :tenantId', { tenantId }) .leftJoinAndSelect('invoice.items', 'items') .orderBy('invoice.created_at', 'DESC'); if (options?.status) { query.andWhere('invoice.status = :status', { status: options.status }); } if (options?.from) { query.andWhere('invoice.created_at >= :from', { from: options.from }); } if (options?.to) { query.andWhere('invoice.created_at <= :to', { to: options.to }); } if (options?.limit) { query.limit(options.limit); } return query.getMany(); } async stampInvoice(id) { const invoice = await this.getInvoice(id); if (invoice.status !== invoice_entity_1.InvoiceStatus.DRAFT && invoice.status !== invoice_entity_1.InvoiceStatus.PENDING) { throw new common_1.BadRequestException(`No se puede timbrar factura con estado: ${invoice.status}`); } const taxConfig = await this.getTaxConfig(invoice.tenantId); if (!taxConfig) { throw new common_1.BadRequestException('Configuracion fiscal no encontrada'); } const mockUuid = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}`.toUpperCase(); invoice.uuid = mockUuid; invoice.status = invoice_entity_1.InvoiceStatus.STAMPED; invoice.stampedAt = new Date(); invoice.pacResponse = { provider: taxConfig.pacProvider, sandbox: taxConfig.pacSandbox, timestamp: new Date().toISOString(), }; return this.invoiceRepo.save(invoice); } async cancelInvoice(id, reason, uuidReplacement) { const invoice = await this.getInvoice(id); if (invoice.status !== invoice_entity_1.InvoiceStatus.STAMPED && invoice.status !== invoice_entity_1.InvoiceStatus.SENT) { throw new common_1.BadRequestException(`No se puede cancelar factura con estado: ${invoice.status}`); } invoice.status = invoice_entity_1.InvoiceStatus.CANCELLED; invoice.cancelledAt = new Date(); invoice.cancelReason = reason; invoice.cancelUuidReplacement = uuidReplacement; return this.invoiceRepo.save(invoice); } async sendInvoice(id, email) { const invoice = await this.getInvoice(id); if (invoice.status !== invoice_entity_1.InvoiceStatus.STAMPED) { throw new common_1.BadRequestException('Solo se pueden enviar facturas timbradas'); } const targetEmail = email || invoice.receptorEmail; if (!targetEmail) { throw new common_1.BadRequestException('No hay email de destino'); } invoice.status = invoice_entity_1.InvoiceStatus.SENT; return this.invoiceRepo.save(invoice); } async getSummary(tenantId, month) { const targetMonth = month || new Date(); const monthStr = targetMonth.toISOString().split('T')[0]; const result = await this.dataSource.query(`SELECT * FROM get_invoice_summary($1, $2::date)`, [tenantId, monthStr]); return result[0] || { total_invoices: 0, total_amount: 0, total_cancelled: 0, by_status: {}, }; } }; exports.InvoicesService = InvoicesService; exports.InvoicesService = InvoicesService = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(tax_config_entity_1.TaxConfig)), __param(1, (0, typeorm_1.InjectRepository)(invoice_entity_1.Invoice)), __param(2, (0, typeorm_1.InjectRepository)(invoice_item_entity_1.InvoiceItem)), __metadata("design:paramtypes", [typeorm_2.Repository, typeorm_2.Repository, typeorm_2.Repository, typeorm_2.DataSource]) ], InvoicesService); //# sourceMappingURL=invoices.service.js.map