michangarrito/apps/backend/dist/modules/orders/orders.service.js
rckrdmrd 48dea7a5d0 feat: Initial commit - michangarrito
Marketplace móvil para negocios locales mexicanos.

Estructura inicial:
- apps/backend (NestJS API)
- apps/frontend (React Web)
- apps/mobile (Expo/React Native)
- apps/mcp-server (Claude MCP Server)
- apps/whatsapp-service (WhatsApp Business API)
- database/ (PostgreSQL DDL)
- docs/ (Documentación)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 04:41:02 -06:00

205 lines
8.8 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.OrdersService = void 0;
const common_1 = require("@nestjs/common");
const typeorm_1 = require("@nestjs/typeorm");
const typeorm_2 = require("typeorm");
const order_entity_1 = require("./entities/order.entity");
const order_item_entity_1 = require("./entities/order-item.entity");
let OrdersService = class OrdersService {
constructor(orderRepo, orderItemRepo) {
this.orderRepo = orderRepo;
this.orderItemRepo = orderItemRepo;
}
generateOrderNumber() {
const now = new Date();
const dateStr = now.toISOString().slice(2, 10).replace(/-/g, '');
const random = Math.floor(Math.random() * 1000).toString().padStart(3, '0');
return `P${dateStr}-${random}`;
}
async create(tenantId, dto) {
let subtotal = 0;
const items = dto.items.map((item) => {
const itemSubtotal = item.quantity * item.unitPrice;
subtotal += itemSubtotal;
return {
...item,
subtotal: itemSubtotal,
};
});
const deliveryFee = dto.deliveryFee || 0;
const discountAmount = dto.discountAmount || 0;
const total = subtotal + deliveryFee - discountAmount;
const order = this.orderRepo.create({
tenantId,
orderNumber: this.generateOrderNumber(),
customerId: dto.customerId,
channel: dto.channel || order_entity_1.OrderChannel.WHATSAPP,
orderType: dto.orderType,
subtotal,
deliveryFee,
discountAmount,
total,
deliveryAddress: dto.deliveryAddress,
deliveryNotes: dto.deliveryNotes,
customerNotes: dto.customerNotes,
paymentMethod: dto.paymentMethod,
status: order_entity_1.OrderStatus.PENDING,
items: items.map((item) => this.orderItemRepo.create(item)),
});
return this.orderRepo.save(order);
}
async findAll(tenantId, status) {
const where = { tenantId };
if (status) {
where.status = status;
}
return this.orderRepo.find({
where,
relations: ['items', 'customer'],
order: { createdAt: 'DESC' },
});
}
async findOne(tenantId, id) {
const order = await this.orderRepo.findOne({
where: { id, tenantId },
relations: ['items', 'customer'],
});
if (!order) {
throw new common_1.NotFoundException('Pedido no encontrado');
}
return order;
}
async findByOrderNumber(tenantId, orderNumber) {
const order = await this.orderRepo.findOne({
where: { orderNumber, tenantId },
relations: ['items', 'customer'],
});
if (!order) {
throw new common_1.NotFoundException('Pedido no encontrado');
}
return order;
}
async getActiveOrders(tenantId) {
return this.orderRepo.find({
where: [
{ tenantId, status: order_entity_1.OrderStatus.PENDING },
{ tenantId, status: order_entity_1.OrderStatus.CONFIRMED },
{ tenantId, status: order_entity_1.OrderStatus.PREPARING },
{ tenantId, status: order_entity_1.OrderStatus.READY },
],
relations: ['items', 'customer'],
order: { createdAt: 'ASC' },
});
}
async getTodayOrders(tenantId) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
return this.orderRepo.find({
where: {
tenantId,
createdAt: (0, typeorm_2.Between)(today, tomorrow),
},
relations: ['items', 'customer'],
order: { createdAt: 'DESC' },
});
}
async updateStatus(tenantId, id, dto) {
const order = await this.findOne(tenantId, id);
this.validateStatusTransition(order.status, dto.status);
order.status = dto.status;
const now = new Date();
switch (dto.status) {
case order_entity_1.OrderStatus.CONFIRMED:
order.confirmedAt = now;
break;
case order_entity_1.OrderStatus.PREPARING:
order.preparingAt = now;
break;
case order_entity_1.OrderStatus.READY:
order.readyAt = now;
break;
case order_entity_1.OrderStatus.COMPLETED:
case order_entity_1.OrderStatus.DELIVERED:
order.completedAt = now;
break;
case order_entity_1.OrderStatus.CANCELLED:
order.cancelledAt = now;
order.cancelledReason = dto.reason;
break;
}
if (dto.internalNotes) {
order.internalNotes = dto.internalNotes;
}
return this.orderRepo.save(order);
}
validateStatusTransition(currentStatus, newStatus) {
const validTransitions = {
[order_entity_1.OrderStatus.PENDING]: [order_entity_1.OrderStatus.CONFIRMED, order_entity_1.OrderStatus.CANCELLED],
[order_entity_1.OrderStatus.CONFIRMED]: [order_entity_1.OrderStatus.PREPARING, order_entity_1.OrderStatus.CANCELLED],
[order_entity_1.OrderStatus.PREPARING]: [order_entity_1.OrderStatus.READY, order_entity_1.OrderStatus.CANCELLED],
[order_entity_1.OrderStatus.READY]: [order_entity_1.OrderStatus.DELIVERED, order_entity_1.OrderStatus.COMPLETED, order_entity_1.OrderStatus.CANCELLED],
[order_entity_1.OrderStatus.DELIVERED]: [order_entity_1.OrderStatus.COMPLETED],
[order_entity_1.OrderStatus.COMPLETED]: [],
[order_entity_1.OrderStatus.CANCELLED]: [],
};
if (!validTransitions[currentStatus].includes(newStatus)) {
throw new common_1.BadRequestException(`No se puede cambiar de ${currentStatus} a ${newStatus}`);
}
}
async getOrderStats(tenantId) {
const today = new Date();
today.setHours(0, 0, 0, 0);
const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);
const [todayOrders, pendingCount, preparingCount, readyCount,] = await Promise.all([
this.orderRepo.count({
where: { tenantId, createdAt: (0, typeorm_2.Between)(today, tomorrow) },
}),
this.orderRepo.count({ where: { tenantId, status: order_entity_1.OrderStatus.PENDING } }),
this.orderRepo.count({ where: { tenantId, status: order_entity_1.OrderStatus.PREPARING } }),
this.orderRepo.count({ where: { tenantId, status: order_entity_1.OrderStatus.READY } }),
]);
const todaySales = await this.orderRepo
.createQueryBuilder('order')
.select('SUM(order.total)', 'total')
.where('order.tenant_id = :tenantId', { tenantId })
.andWhere('order.created_at >= :today', { today })
.andWhere('order.created_at < :tomorrow', { tomorrow })
.andWhere('order.status NOT IN (:...statuses)', {
statuses: [order_entity_1.OrderStatus.CANCELLED],
})
.getRawOne();
return {
todayOrders,
todaySales: Number(todaySales?.total) || 0,
pending: pendingCount,
preparing: preparingCount,
ready: readyCount,
activeTotal: pendingCount + preparingCount + readyCount,
};
}
};
exports.OrdersService = OrdersService;
exports.OrdersService = OrdersService = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, typeorm_1.InjectRepository)(order_entity_1.Order)),
__param(1, (0, typeorm_1.InjectRepository)(order_item_entity_1.OrderItem)),
__metadata("design:paramtypes", [typeorm_2.Repository,
typeorm_2.Repository])
], OrdersService);
//# sourceMappingURL=orders.service.js.map