michangarrito/apps/backend/dist/modules/marketplace/marketplace.service.js
rckrdmrd 97f407c661 [MIGRATION-V2] feat: Migrar michangarrito a estructura v2
- Prefijo v2: MCH
- TRACEABILITY-MASTER.yml creado
- Listo para integracion como submodulo

Workspace: v2.0.0 | SIMCO: v4.0.0
2026-01-10 11:28:54 -06:00

318 lines
15 KiB
JavaScript

"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.MarketplaceService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const supplier_entity_1 = require("./entities/supplier.entity");
const supplier_product_entity_1 = require("./entities/supplier-product.entity");
const supplier_order_entity_1 = require("./entities/supplier-order.entity");
const supplier_order_item_entity_1 = require("./entities/supplier-order-item.entity");
const supplier_review_entity_1 = require("./entities/supplier-review.entity");
let MarketplaceService = class MarketplaceService {
constructor(supplierRepo, productRepo, orderRepo, orderItemRepo, reviewRepo, dataSource) {
this.supplierRepo = supplierRepo;
this.productRepo = productRepo;
this.orderRepo = orderRepo;
this.orderItemRepo = orderItemRepo;
this.reviewRepo = reviewRepo;
this.dataSource = dataSource;
}
async findSuppliers(options) {
const query = this.supplierRepo.createQueryBuilder('supplier')
.where('supplier.status = :status', { status: supplier_entity_1.SupplierStatus.ACTIVE })
.orderBy('supplier.rating', 'DESC')
.addOrderBy('supplier.total_orders', 'DESC');
if (options?.category) {
query.andWhere(':category = ANY(supplier.categories)', {
category: options.category,
});
}
if (options?.zipCode) {
query.andWhere('(supplier.coverage_zones = \'{}\' OR :zipCode = ANY(supplier.coverage_zones))', { zipCode: options.zipCode });
}
if (options?.search) {
query.andWhere('(supplier.name ILIKE :search OR supplier.description ILIKE :search)', { search: `%${options.search}%` });
}
if (options?.limit) {
query.limit(options.limit);
}
return query.getMany();
}
async getSupplier(id) {
const supplier = await this.supplierRepo.findOne({
where: { id },
relations: ['products', 'reviews'],
});
if (!supplier) {
throw new common_1.NotFoundException('Proveedor no encontrado');
}
return supplier;
}
async getSupplierProducts(supplierId, options) {
const query = this.productRepo.createQueryBuilder('product')
.where('product.supplier_id = :supplierId', { supplierId })
.andWhere('product.active = true')
.orderBy('product.category', 'ASC')
.addOrderBy('product.name', 'ASC');
if (options?.category) {
query.andWhere('product.category = :category', { category: options.category });
}
if (options?.search) {
query.andWhere('(product.name ILIKE :search OR product.description ILIKE :search)', { search: `%${options.search}%` });
}
if (options?.inStock !== undefined) {
query.andWhere('product.in_stock = :inStock', { inStock: options.inStock });
}
return query.getMany();
}
async createOrder(tenantId, dto) {
const supplier = await this.supplierRepo.findOne({
where: { id: dto.supplierId, status: supplier_entity_1.SupplierStatus.ACTIVE },
});
if (!supplier) {
throw new common_1.NotFoundException('Proveedor no encontrado o no activo');
}
const productIds = dto.items.map((item) => item.productId);
const products = await this.productRepo.findByIds(productIds);
if (products.length !== productIds.length) {
throw new common_1.BadRequestException('Algunos productos no fueron encontrados');
}
const productMap = new Map(products.map((p) => [p.id, p]));
let subtotal = 0;
for (const item of dto.items) {
const product = productMap.get(item.productId);
if (!product) {
throw new common_1.BadRequestException(`Producto ${item.productId} no encontrado`);
}
if (item.quantity < product.minQuantity) {
throw new common_1.BadRequestException(`Cantidad minima para ${product.name} es ${product.minQuantity}`);
}
if (!product.inStock) {
throw new common_1.BadRequestException(`${product.name} no esta disponible`);
}
let unitPrice = Number(product.unitPrice);
if (product.tieredPricing && product.tieredPricing.length > 0) {
for (const tier of product.tieredPricing.sort((a, b) => b.min - a.min)) {
if (item.quantity >= tier.min) {
unitPrice = tier.price;
break;
}
}
}
subtotal += unitPrice * item.quantity;
}
let deliveryFee = Number(supplier.deliveryFee);
if (supplier.freeDeliveryMin && subtotal >= Number(supplier.freeDeliveryMin)) {
deliveryFee = 0;
}
if (subtotal < Number(supplier.minOrderAmount)) {
throw new common_1.BadRequestException(`Pedido minimo es $${supplier.minOrderAmount}`);
}
const total = subtotal + deliveryFee;
const order = this.orderRepo.create({
tenantId,
supplierId: dto.supplierId,
status: supplier_order_entity_1.SupplierOrderStatus.PENDING,
subtotal,
deliveryFee,
total,
deliveryAddress: dto.deliveryAddress,
deliveryCity: dto.deliveryCity,
deliveryZip: dto.deliveryZip,
deliveryPhone: dto.deliveryPhone,
deliveryContact: dto.deliveryContact,
requestedDate: dto.requestedDate ? new Date(dto.requestedDate) : null,
notes: dto.notes,
});
await this.orderRepo.save(order);
for (const item of dto.items) {
const product = productMap.get(item.productId);
let unitPrice = Number(product.unitPrice);
if (product.tieredPricing && product.tieredPricing.length > 0) {
for (const tier of product.tieredPricing.sort((a, b) => b.min - a.min)) {
if (item.quantity >= tier.min) {
unitPrice = tier.price;
break;
}
}
}
const orderItem = this.orderItemRepo.create({
orderId: order.id,
productId: item.productId,
productName: product.name,
productSku: product.sku,
quantity: item.quantity,
unitPrice,
total: unitPrice * item.quantity,
notes: item.notes,
});
await this.orderItemRepo.save(orderItem);
}
return this.getOrder(order.id);
}
async getOrder(id) {
const order = await this.orderRepo.findOne({
where: { id },
relations: ['items', 'supplier'],
});
if (!order) {
throw new common_1.NotFoundException('Pedido no encontrado');
}
return order;
}
async getOrders(tenantId, options) {
const query = this.orderRepo.createQueryBuilder('order')
.where('order.tenant_id = :tenantId', { tenantId })
.leftJoinAndSelect('order.supplier', 'supplier')
.leftJoinAndSelect('order.items', 'items')
.orderBy('order.created_at', 'DESC');
if (options?.status) {
query.andWhere('order.status = :status', { status: options.status });
}
if (options?.supplierId) {
query.andWhere('order.supplier_id = :supplierId', { supplierId: options.supplierId });
}
if (options?.limit) {
query.limit(options.limit);
}
return query.getMany();
}
async updateOrderStatus(id, status, notes) {
const order = await this.getOrder(id);
const validTransitions = {
[supplier_order_entity_1.SupplierOrderStatus.PENDING]: [supplier_order_entity_1.SupplierOrderStatus.CONFIRMED, supplier_order_entity_1.SupplierOrderStatus.CANCELLED, supplier_order_entity_1.SupplierOrderStatus.REJECTED],
[supplier_order_entity_1.SupplierOrderStatus.CONFIRMED]: [supplier_order_entity_1.SupplierOrderStatus.PREPARING, supplier_order_entity_1.SupplierOrderStatus.CANCELLED],
[supplier_order_entity_1.SupplierOrderStatus.PREPARING]: [supplier_order_entity_1.SupplierOrderStatus.SHIPPED, supplier_order_entity_1.SupplierOrderStatus.CANCELLED],
[supplier_order_entity_1.SupplierOrderStatus.SHIPPED]: [supplier_order_entity_1.SupplierOrderStatus.DELIVERED, supplier_order_entity_1.SupplierOrderStatus.CANCELLED],
[supplier_order_entity_1.SupplierOrderStatus.DELIVERED]: [],
[supplier_order_entity_1.SupplierOrderStatus.CANCELLED]: [],
[supplier_order_entity_1.SupplierOrderStatus.REJECTED]: [],
};
if (!validTransitions[order.status].includes(status)) {
throw new common_1.BadRequestException(`No se puede cambiar estado de ${order.status} a ${status}`);
}
order.status = status;
if (status === supplier_order_entity_1.SupplierOrderStatus.CONFIRMED) {
order.confirmedDate = new Date();
}
if (status === supplier_order_entity_1.SupplierOrderStatus.DELIVERED) {
order.deliveredAt = new Date();
}
if (status === supplier_order_entity_1.SupplierOrderStatus.CANCELLED || status === supplier_order_entity_1.SupplierOrderStatus.REJECTED) {
order.cancelledAt = new Date();
order.cancelReason = notes;
}
if (notes) {
order.supplierNotes = notes;
}
return this.orderRepo.save(order);
}
async cancelOrder(id, tenantId, reason) {
const order = await this.getOrder(id);
if (order.tenantId !== tenantId) {
throw new common_1.BadRequestException('No autorizado');
}
if (![supplier_order_entity_1.SupplierOrderStatus.PENDING, supplier_order_entity_1.SupplierOrderStatus.CONFIRMED].includes(order.status)) {
throw new common_1.BadRequestException('No se puede cancelar el pedido en este estado');
}
order.status = supplier_order_entity_1.SupplierOrderStatus.CANCELLED;
order.cancelledAt = new Date();
order.cancelReason = reason;
order.cancelledBy = 'tenant';
return this.orderRepo.save(order);
}
async createReview(tenantId, dto) {
const supplier = await this.supplierRepo.findOne({
where: { id: dto.supplierId },
});
if (!supplier) {
throw new common_1.NotFoundException('Proveedor no encontrado');
}
let verified = false;
if (dto.orderId) {
const order = await this.orderRepo.findOne({
where: { id: dto.orderId, tenantId, supplierId: dto.supplierId },
});
if (!order) {
throw new common_1.BadRequestException('Orden no encontrada');
}
if (order.status === supplier_order_entity_1.SupplierOrderStatus.DELIVERED) {
verified = true;
}
}
const review = this.reviewRepo.create({
tenantId,
supplierId: dto.supplierId,
orderId: dto.orderId,
rating: dto.rating,
title: dto.title,
comment: dto.comment,
ratingQuality: dto.ratingQuality,
ratingDelivery: dto.ratingDelivery,
ratingPrice: dto.ratingPrice,
verified,
});
return this.reviewRepo.save(review);
}
async getReviews(supplierId, options) {
return this.reviewRepo.find({
where: { supplierId, status: 'active' },
order: { createdAt: 'DESC' },
take: options?.limit || 20,
skip: options?.offset || 0,
});
}
async addFavorite(tenantId, supplierId) {
await this.dataSource.query(`INSERT INTO marketplace.supplier_favorites (tenant_id, supplier_id)
VALUES ($1, $2) ON CONFLICT DO NOTHING`, [tenantId, supplierId]);
}
async removeFavorite(tenantId, supplierId) {
await this.dataSource.query(`DELETE FROM marketplace.supplier_favorites WHERE tenant_id = $1 AND supplier_id = $2`, [tenantId, supplierId]);
}
async getFavorites(tenantId) {
const result = await this.dataSource.query(`SELECT s.* FROM marketplace.suppliers s
JOIN marketplace.supplier_favorites f ON s.id = f.supplier_id
WHERE f.tenant_id = $1`, [tenantId]);
return result;
}
async getMarketplaceStats() {
const result = await this.dataSource.query(`SELECT * FROM marketplace.get_marketplace_stats()`);
return result[0] || {
total_suppliers: 0,
active_suppliers: 0,
total_products: 0,
total_orders: 0,
total_gmv: 0,
avg_rating: 0,
};
}
};
exports.MarketplaceService = MarketplaceService;
exports.MarketplaceService = MarketplaceService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(supplier_entity_1.Supplier)),
__param(1, (0, typeorm_1.InjectRepository)(supplier_product_entity_1.SupplierProduct)),
__param(2, (0, typeorm_1.InjectRepository)(supplier_order_entity_1.SupplierOrder)),
__param(3, (0, typeorm_1.InjectRepository)(supplier_order_item_entity_1.SupplierOrderItem)),
__param(4, (0, typeorm_1.InjectRepository)(supplier_review_entity_1.SupplierReview)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.Repository,
typeorm_2.DataSource])
], MarketplaceService);
//# sourceMappingURL=marketplace.service.js.map