"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.SalesService = void 0; const common_1 = require("@nestjs/common"); const typeorm_1 = require("@nestjs/typeorm"); const typeorm_2 = require("typeorm"); const sale_entity_1 = require("./entities/sale.entity"); const sale_item_entity_1 = require("./entities/sale-item.entity"); const product_entity_1 = require("../products/entities/product.entity"); const tenant_entity_1 = require("../auth/entities/tenant.entity"); let SalesService = class SalesService { constructor(saleRepository, saleItemRepository, productRepository, tenantRepository) { this.saleRepository = saleRepository; this.saleItemRepository = saleItemRepository; this.productRepository = productRepository; this.tenantRepository = tenantRepository; } async findAll(tenantId, filters) { const query = this.saleRepository .createQueryBuilder('sale') .leftJoinAndSelect('sale.items', 'items') .leftJoinAndSelect('sale.paymentMethod', 'paymentMethod') .where('sale.tenantId = :tenantId', { tenantId }); if (filters.startDate && filters.endDate) { query.andWhere('DATE(sale.createdAt) BETWEEN :startDate AND :endDate', { startDate: filters.startDate, endDate: filters.endDate, }); } else if (filters.startDate) { query.andWhere('DATE(sale.createdAt) >= :startDate', { startDate: filters.startDate, }); } else if (filters.endDate) { query.andWhere('DATE(sale.createdAt) <= :endDate', { endDate: filters.endDate, }); } if (filters.status) { query.andWhere('sale.status = :status', { status: filters.status }); } if (filters.ticketNumber) { query.andWhere('sale.ticketNumber ILIKE :ticketNumber', { ticketNumber: `%${filters.ticketNumber}%`, }); } query.orderBy('sale.createdAt', 'DESC'); if (filters.limit) { query.limit(filters.limit); } return query.getMany(); } async findOne(tenantId, id) { const sale = await this.saleRepository.findOne({ where: { id, tenantId }, relations: ['items', 'paymentMethod'], }); if (!sale) { throw new common_1.NotFoundException('Venta no encontrada'); } return sale; } async findByTicketNumber(tenantId, ticketNumber) { const sale = await this.saleRepository.findOne({ where: { ticketNumber, tenantId }, relations: ['items', 'paymentMethod'], }); if (!sale) { throw new common_1.NotFoundException('Venta no encontrada'); } return sale; } async create(tenantId, dto) { const tenant = await this.tenantRepository.findOne({ where: { id: tenantId }, }); if (!tenant) { throw new common_1.BadRequestException('Tenant no encontrado'); } let subtotal = 0; const saleItems = []; for (const item of dto.items) { const product = await this.productRepository.findOne({ where: { id: item.productId, tenantId, status: 'active' }, }); if (!product) { throw new common_1.BadRequestException(`Producto ${item.productId} no encontrado`); } if (product.trackInventory && Number(product.stockQuantity) < item.quantity) { throw new common_1.BadRequestException(`Stock insuficiente para ${product.name}. Disponible: ${product.stockQuantity}`); } const itemSubtotal = Number(product.price) * item.quantity * (1 - (item.discountPercent || 0) / 100); saleItems.push({ productId: product.id, productName: product.name, productSku: product.sku, quantity: item.quantity, unitPrice: Number(product.price), discountPercent: item.discountPercent || 0, subtotal: itemSubtotal, }); subtotal += itemSubtotal; } const taxRate = Number(tenant.taxRate) / 100; const taxAmount = subtotal - subtotal / (1 + taxRate); const total = subtotal; if (dto.amountReceived < total) { throw new common_1.BadRequestException(`Monto recibido ($${dto.amountReceived}) es menor al total ($${total.toFixed(2)})`); } const changeAmount = dto.amountReceived - total; const sale = this.saleRepository.create({ tenantId, subtotal, taxAmount, discountAmount: 0, total, paymentMethodId: dto.paymentMethodId, amountReceived: dto.amountReceived, changeAmount, customerName: dto.customerName, customerPhone: dto.customerPhone, notes: dto.notes, deviceInfo: dto.deviceInfo, status: sale_entity_1.SaleStatus.COMPLETED, }); const savedSale = await this.saleRepository.save(sale); for (const item of saleItems) { const saleItem = this.saleItemRepository.create({ ...item, saleId: savedSale.id, }); await this.saleItemRepository.save(saleItem); } return this.findOne(tenantId, savedSale.id); } async cancel(tenantId, id, dto) { const sale = await this.findOne(tenantId, id); if (sale.status !== sale_entity_1.SaleStatus.COMPLETED) { throw new common_1.BadRequestException('Solo se pueden cancelar ventas completadas'); } const today = new Date(); today.setHours(0, 0, 0, 0); const saleDate = new Date(sale.createdAt); saleDate.setHours(0, 0, 0, 0); if (saleDate.getTime() !== today.getTime()) { throw new common_1.BadRequestException('Solo se pueden cancelar ventas del día actual'); } sale.status = sale_entity_1.SaleStatus.CANCELLED; sale.cancelledAt = new Date(); sale.cancelReason = dto.reason; for (const item of sale.items) { if (item.productId) { const product = await this.productRepository.findOne({ where: { id: item.productId }, }); if (product?.trackInventory) { product.stockQuantity = Number(product.stockQuantity) + Number(item.quantity); await this.productRepository.save(product); } } } return this.saleRepository.save(sale); } async getTodaySummary(tenantId) { const today = new Date(); today.setHours(0, 0, 0, 0); const result = await this.saleRepository .createQueryBuilder('sale') .select([ 'COUNT(sale.id) as totalSales', 'COALESCE(SUM(sale.total), 0) as totalRevenue', 'COALESCE(SUM(sale.taxAmount), 0) as totalTax', 'COALESCE(AVG(sale.total), 0) as avgTicket', ]) .where('sale.tenantId = :tenantId', { tenantId }) .andWhere('DATE(sale.createdAt) = CURRENT_DATE') .andWhere('sale.status = :status', { status: sale_entity_1.SaleStatus.COMPLETED }) .getRawOne(); return { totalSales: parseInt(result.totalsales, 10) || 0, totalRevenue: parseFloat(result.totalrevenue) || 0, totalTax: parseFloat(result.totaltax) || 0, avgTicket: parseFloat(result.avgticket) || 0, }; } async getRecentSales(tenantId, limit = 10) { return this.saleRepository.find({ where: { tenantId }, relations: ['items', 'paymentMethod'], order: { createdAt: 'DESC' }, take: limit, }); } }; exports.SalesService = SalesService; exports.SalesService = SalesService = __decorate([ (0, common_1.Injectable)(), __param(0, (0, typeorm_1.InjectRepository)(sale_entity_1.Sale)), __param(1, (0, typeorm_1.InjectRepository)(sale_item_entity_1.SaleItem)), __param(2, (0, typeorm_1.InjectRepository)(product_entity_1.Product)), __param(3, (0, typeorm_1.InjectRepository)(tenant_entity_1.Tenant)), __metadata("design:paramtypes", [typeorm_2.Repository, typeorm_2.Repository, typeorm_2.Repository, typeorm_2.Repository]) ], SalesService); //# sourceMappingURL=sales.service.js.map