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>
205 lines
8.8 KiB
JavaScript
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
|