"use strict"; /** * ComparativoService - Servicio de comparativos de cotizaciones * * Gestión de cuadros comparativos para selección de proveedores. * * @module Purchase */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ComparativoService = void 0; class ComparativoService { comparativoRepository; proveedorRepository; productoRepository; constructor(comparativoRepository, proveedorRepository, productoRepository) { this.comparativoRepository = comparativoRepository; this.proveedorRepository = proveedorRepository; this.productoRepository = productoRepository; } generateCode() { const now = new Date(); const year = now.getFullYear().toString().slice(-2); const month = (now.getMonth() + 1).toString().padStart(2, '0'); const random = Math.random().toString(36).substring(2, 6).toUpperCase(); return `CMP-${year}${month}-${random}`; } async findWithFilters(ctx, filters = {}, page = 1, limit = 20) { const skip = (page - 1) * limit; const queryBuilder = this.comparativoRepository .createQueryBuilder('comp') .leftJoinAndSelect('comp.requisicion', 'requisicion') .leftJoinAndSelect('comp.createdBy', 'createdBy') .where('comp.tenant_id = :tenantId', { tenantId: ctx.tenantId }) .andWhere('comp.deleted_at IS NULL'); if (filters.requisicionId) { queryBuilder.andWhere('comp.requisicion_id = :requisicionId', { requisicionId: filters.requisicionId, }); } if (filters.status) { queryBuilder.andWhere('comp.status = :status', { status: filters.status }); } if (filters.dateFrom) { queryBuilder.andWhere('comp.comparison_date >= :dateFrom', { dateFrom: filters.dateFrom }); } if (filters.dateTo) { queryBuilder.andWhere('comp.comparison_date <= :dateTo', { dateTo: filters.dateTo }); } queryBuilder .orderBy('comp.comparison_date', 'DESC') .skip(skip) .take(limit); const [data, total] = await queryBuilder.getManyAndCount(); return { data, meta: { total, page, limit, totalPages: Math.ceil(total / limit), }, }; } async findById(ctx, id) { return this.comparativoRepository.findOne({ where: { id, tenantId: ctx.tenantId, deletedAt: null, }, }); } async findWithDetails(ctx, id) { return this.comparativoRepository.findOne({ where: { id, tenantId: ctx.tenantId, deletedAt: null, }, relations: [ 'requisicion', 'createdBy', 'approvedBy', 'proveedores', 'proveedores.productos', ], }); } async create(ctx, dto) { const comparativo = this.comparativoRepository.create({ tenantId: ctx.tenantId, createdById: ctx.userId, code: this.generateCode(), requisicionId: dto.requisicionId, name: dto.name, comparisonDate: dto.comparisonDate, notes: dto.notes, status: 'draft', }); return this.comparativoRepository.save(comparativo); } async addProveedor(ctx, comparativoId, dto) { const comparativo = await this.findById(ctx, comparativoId); if (!comparativo) { throw new Error('Comparativo not found'); } if (comparativo.status !== 'draft' && comparativo.status !== 'in_evaluation') { throw new Error('Cannot add suppliers to approved or cancelled comparisons'); } const proveedor = this.proveedorRepository.create({ tenantId: ctx.tenantId, createdById: ctx.userId, comparativoId, supplierId: dto.supplierId, quotationNumber: dto.quotationNumber, quotationDate: dto.quotationDate, deliveryDays: dto.deliveryDays, paymentConditions: dto.paymentConditions, evaluationNotes: dto.evaluationNotes, }); return this.proveedorRepository.save(proveedor); } async addProducto(ctx, proveedorId, dto) { const proveedor = await this.proveedorRepository.findOne({ where: { id: proveedorId, tenantId: ctx.tenantId }, relations: ['comparativo'], }); if (!proveedor) { throw new Error('Supplier entry not found'); } if (proveedor.comparativo.status !== 'draft' && proveedor.comparativo.status !== 'in_evaluation') { throw new Error('Cannot add products to approved or cancelled comparisons'); } const producto = this.productoRepository.create({ tenantId: ctx.tenantId, createdById: ctx.userId, comparativoProveedorId: proveedorId, productId: dto.productId, quantity: dto.quantity, unitPrice: dto.unitPrice, notes: dto.notes, }); return this.productoRepository.save(producto); } async startEvaluation(ctx, id) { const comparativo = await this.findWithDetails(ctx, id); if (!comparativo) { return null; } if (comparativo.status !== 'draft') { throw new Error('Can only start evaluation on draft comparisons'); } if (!comparativo.proveedores || comparativo.proveedores.length < 2) { throw new Error('Need at least 2 suppliers to start evaluation'); } comparativo.status = 'in_evaluation'; return this.comparativoRepository.save(comparativo); } async selectWinner(ctx, id, supplierId) { const comparativo = await this.findWithDetails(ctx, id); if (!comparativo) { return null; } if (comparativo.status !== 'in_evaluation') { throw new Error('Can only select winner during evaluation'); } // Verify supplier is in the comparison const proveedor = comparativo.proveedores?.find((p) => p.supplierId === supplierId); if (!proveedor) { throw new Error('Supplier not found in this comparison'); } // Mark all as not selected, then mark winner for (const p of comparativo.proveedores || []) { p.isSelected = p.supplierId === supplierId; await this.proveedorRepository.save(p); } comparativo.winnerSupplierId = supplierId; comparativo.status = 'approved'; comparativo.approvedById = ctx.userId || ''; comparativo.approvedAt = new Date(); return this.comparativoRepository.save(comparativo); } async cancel(ctx, id) { const comparativo = await this.findById(ctx, id); if (!comparativo) { return null; } if (comparativo.status === 'approved') { throw new Error('Cannot cancel approved comparisons'); } comparativo.status = 'cancelled'; return this.comparativoRepository.save(comparativo); } async updateProveedorTotal(ctx, proveedorId) { const proveedor = await this.proveedorRepository.findOne({ where: { id: proveedorId, tenantId: ctx.tenantId }, relations: ['productos'], }); if (!proveedor) { return null; } const total = proveedor.productos?.reduce((sum, p) => sum + (p.quantity * p.unitPrice), 0) || 0; proveedor.totalAmount = total; return this.proveedorRepository.save(proveedor); } async softDelete(ctx, id) { const comparativo = await this.findById(ctx, id); if (!comparativo) { return false; } if (comparativo.status === 'approved') { throw new Error('Cannot delete approved comparisons'); } await this.comparativoRepository.update({ id, tenantId: ctx.tenantId }, { deletedAt: new Date(), deletedById: ctx.userId }); return true; } } exports.ComparativoService = ComparativoService; //# sourceMappingURL=comparativo.service.js.map