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>
202 lines
8.4 KiB
JavaScript
202 lines
8.4 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.CustomersService = void 0;
|
|
const common_1 = require("@nestjs/common");
|
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
const typeorm_2 = require("typeorm");
|
|
const customer_entity_1 = require("./entities/customer.entity");
|
|
const fiado_entity_1 = require("./entities/fiado.entity");
|
|
const fiado_payment_entity_1 = require("./entities/fiado-payment.entity");
|
|
let CustomersService = class CustomersService {
|
|
constructor(customerRepo, fiadoRepo, fiadoPaymentRepo) {
|
|
this.customerRepo = customerRepo;
|
|
this.fiadoRepo = fiadoRepo;
|
|
this.fiadoPaymentRepo = fiadoPaymentRepo;
|
|
}
|
|
async findAll(tenantId) {
|
|
return this.customerRepo.find({
|
|
where: { tenantId, status: 'active' },
|
|
order: { name: 'ASC' },
|
|
});
|
|
}
|
|
async findOne(tenantId, id) {
|
|
const customer = await this.customerRepo.findOne({
|
|
where: { id, tenantId },
|
|
relations: ['fiados'],
|
|
});
|
|
if (!customer) {
|
|
throw new common_1.NotFoundException('Cliente no encontrado');
|
|
}
|
|
return customer;
|
|
}
|
|
async findByPhone(tenantId, phone) {
|
|
return this.customerRepo.findOne({
|
|
where: { tenantId, phone },
|
|
});
|
|
}
|
|
async create(tenantId, dto) {
|
|
const customer = this.customerRepo.create({
|
|
...dto,
|
|
tenantId,
|
|
});
|
|
return this.customerRepo.save(customer);
|
|
}
|
|
async update(tenantId, id, dto) {
|
|
const customer = await this.findOne(tenantId, id);
|
|
Object.assign(customer, dto);
|
|
return this.customerRepo.save(customer);
|
|
}
|
|
async toggleActive(tenantId, id) {
|
|
const customer = await this.findOne(tenantId, id);
|
|
customer.status = customer.status === 'active' ? 'inactive' : 'active';
|
|
return this.customerRepo.save(customer);
|
|
}
|
|
async getWithFiados(tenantId) {
|
|
return this.customerRepo.find({
|
|
where: { tenantId, fiadoEnabled: true },
|
|
order: { currentFiadoBalance: 'DESC' },
|
|
});
|
|
}
|
|
async createFiado(tenantId, dto) {
|
|
const customer = await this.findOne(tenantId, dto.customerId);
|
|
if (!customer.fiadoEnabled) {
|
|
throw new common_1.BadRequestException('El cliente no tiene habilitado el fiado');
|
|
}
|
|
const newBalance = Number(customer.currentFiadoBalance) + dto.amount;
|
|
if (customer.fiadoLimit > 0 && newBalance > customer.fiadoLimit) {
|
|
throw new common_1.BadRequestException(`El fiado excede el límite. Límite: $${customer.fiadoLimit}, Balance actual: $${customer.currentFiadoBalance}`);
|
|
}
|
|
const fiado = this.fiadoRepo.create({
|
|
tenantId,
|
|
customerId: dto.customerId,
|
|
saleId: dto.saleId,
|
|
amount: dto.amount,
|
|
remainingAmount: dto.amount,
|
|
description: dto.description,
|
|
dueDate: dto.dueDate ? new Date(dto.dueDate) : null,
|
|
status: fiado_entity_1.FiadoStatus.PENDING,
|
|
});
|
|
await this.fiadoRepo.save(fiado);
|
|
customer.currentFiadoBalance = newBalance;
|
|
await this.customerRepo.save(customer);
|
|
return fiado;
|
|
}
|
|
async getFiados(tenantId, customerId) {
|
|
const where = { tenantId };
|
|
if (customerId) {
|
|
where.customerId = customerId;
|
|
}
|
|
return this.fiadoRepo.find({
|
|
where,
|
|
relations: ['customer', 'payments'],
|
|
order: { createdAt: 'DESC' },
|
|
});
|
|
}
|
|
async getPendingFiados(tenantId) {
|
|
return this.fiadoRepo.find({
|
|
where: [
|
|
{ tenantId, status: fiado_entity_1.FiadoStatus.PENDING },
|
|
{ tenantId, status: fiado_entity_1.FiadoStatus.PARTIAL },
|
|
],
|
|
relations: ['customer'],
|
|
order: { createdAt: 'ASC' },
|
|
});
|
|
}
|
|
async payFiado(tenantId, fiadoId, dto, userId) {
|
|
const fiado = await this.fiadoRepo.findOne({
|
|
where: { id: fiadoId, tenantId },
|
|
relations: ['customer'],
|
|
});
|
|
if (!fiado) {
|
|
throw new common_1.NotFoundException('Fiado no encontrado');
|
|
}
|
|
if (fiado.status === fiado_entity_1.FiadoStatus.PAID) {
|
|
throw new common_1.BadRequestException('Este fiado ya está pagado');
|
|
}
|
|
if (dto.amount > Number(fiado.remainingAmount)) {
|
|
throw new common_1.BadRequestException(`El monto excede el saldo pendiente: $${fiado.remainingAmount}`);
|
|
}
|
|
const payment = this.fiadoPaymentRepo.create({
|
|
fiadoId,
|
|
amount: dto.amount,
|
|
paymentMethod: dto.paymentMethod || 'cash',
|
|
notes: dto.notes,
|
|
receivedBy: userId,
|
|
});
|
|
await this.fiadoPaymentRepo.save(payment);
|
|
fiado.paidAmount = Number(fiado.paidAmount) + dto.amount;
|
|
fiado.remainingAmount = Number(fiado.remainingAmount) - dto.amount;
|
|
if (fiado.remainingAmount <= 0) {
|
|
fiado.status = fiado_entity_1.FiadoStatus.PAID;
|
|
fiado.paidAt = new Date();
|
|
}
|
|
else {
|
|
fiado.status = fiado_entity_1.FiadoStatus.PARTIAL;
|
|
}
|
|
await this.fiadoRepo.save(fiado);
|
|
const customer = fiado.customer;
|
|
customer.currentFiadoBalance = Number(customer.currentFiadoBalance) - dto.amount;
|
|
await this.customerRepo.save(customer);
|
|
return fiado;
|
|
}
|
|
async cancelFiado(tenantId, fiadoId) {
|
|
const fiado = await this.fiadoRepo.findOne({
|
|
where: { id: fiadoId, tenantId },
|
|
relations: ['customer'],
|
|
});
|
|
if (!fiado) {
|
|
throw new common_1.NotFoundException('Fiado no encontrado');
|
|
}
|
|
if (fiado.status === fiado_entity_1.FiadoStatus.PAID) {
|
|
throw new common_1.BadRequestException('No se puede cancelar un fiado pagado');
|
|
}
|
|
const customer = fiado.customer;
|
|
customer.currentFiadoBalance = Number(customer.currentFiadoBalance) - Number(fiado.remainingAmount);
|
|
await this.customerRepo.save(customer);
|
|
fiado.status = fiado_entity_1.FiadoStatus.CANCELLED;
|
|
return this.fiadoRepo.save(fiado);
|
|
}
|
|
async getCustomerStats(tenantId, customerId) {
|
|
const customer = await this.findOne(tenantId, customerId);
|
|
const pendingFiados = await this.fiadoRepo.count({
|
|
where: [
|
|
{ customerId, status: fiado_entity_1.FiadoStatus.PENDING },
|
|
{ customerId, status: fiado_entity_1.FiadoStatus.PARTIAL },
|
|
],
|
|
});
|
|
return {
|
|
customer,
|
|
stats: {
|
|
totalPurchases: customer.totalPurchases,
|
|
purchaseCount: customer.purchaseCount,
|
|
fiadoBalance: customer.currentFiadoBalance,
|
|
fiadoLimit: customer.fiadoLimit,
|
|
fiadoAvailable: Math.max(0, Number(customer.fiadoLimit) - Number(customer.currentFiadoBalance)),
|
|
pendingFiados,
|
|
},
|
|
};
|
|
}
|
|
};
|
|
exports.CustomersService = CustomersService;
|
|
exports.CustomersService = CustomersService = __decorate([
|
|
(0, common_1.Injectable)(),
|
|
__param(0, (0, typeorm_1.InjectRepository)(customer_entity_1.Customer)),
|
|
__param(1, (0, typeorm_1.InjectRepository)(fiado_entity_1.Fiado)),
|
|
__param(2, (0, typeorm_1.InjectRepository)(fiado_payment_entity_1.FiadoPayment)),
|
|
__metadata("design:paramtypes", [typeorm_2.Repository,
|
|
typeorm_2.Repository,
|
|
typeorm_2.Repository])
|
|
], CustomersService);
|
|
//# sourceMappingURL=customers.service.js.map
|