[MMD-010] feat: Add FiadosService module for customer credit management
- Create fiados module with 3 entities: - CustomerCreditAccount: Credit accounts per customer - Fiado: Individual credit sales (fiados) - FiadoPayment: Payment/abono tracking - Implement FiadosService with full business logic: - Credit eligibility checking - Fiado creation with automatic due dates - Payment registration with FIFO allocation - Overdue tracking and account freezing - Connect FiadosToolsService to real FiadosService - Update MCP module registration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e26ab24aa5
commit
f74f713482
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Customer Credit Account Entity
|
||||
* Mecánicas Diesel - ERP Suite
|
||||
*
|
||||
* Represents a customer's credit account for fiado tracking.
|
||||
*/
|
||||
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
} from 'typeorm';
|
||||
import { Customer } from '../../customers/entities/customer.entity';
|
||||
|
||||
@Entity({ name: 'customer_credit_accounts', schema: 'service_management' })
|
||||
@Index('idx_credit_accounts_tenant', ['tenantId'])
|
||||
@Index('idx_credit_accounts_customer', ['customerId'])
|
||||
export class CustomerCreditAccount {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'tenant_id', type: 'uuid' })
|
||||
tenantId: string;
|
||||
|
||||
@Column({ name: 'customer_id', type: 'uuid' })
|
||||
customerId: string;
|
||||
|
||||
@ManyToOne(() => Customer)
|
||||
@JoinColumn({ name: 'customer_id' })
|
||||
customer: Customer;
|
||||
|
||||
// Credit limits
|
||||
@Column({ name: 'credit_limit', type: 'decimal', precision: 12, scale: 2, default: 0 })
|
||||
creditLimit: number;
|
||||
|
||||
@Column({ name: 'credit_days', type: 'integer', default: 30 })
|
||||
creditDays: number;
|
||||
|
||||
// Balances
|
||||
@Column({ name: 'current_balance', type: 'decimal', precision: 12, scale: 2, default: 0 })
|
||||
currentBalance: number;
|
||||
|
||||
// Statistics
|
||||
@Column({ name: 'total_credit_given', type: 'decimal', precision: 14, scale: 2, default: 0 })
|
||||
totalCreditGiven: number;
|
||||
|
||||
@Column({ name: 'total_payments_received', type: 'decimal', precision: 14, scale: 2, default: 0 })
|
||||
totalPaymentsReceived: number;
|
||||
|
||||
@Column({ name: 'overdue_amount', type: 'decimal', precision: 12, scale: 2, default: 0 })
|
||||
overdueAmount: number;
|
||||
|
||||
// Status
|
||||
@Column({ name: 'is_active', type: 'boolean', default: true })
|
||||
isActive: boolean;
|
||||
|
||||
@Column({ name: 'is_frozen', type: 'boolean', default: false })
|
||||
isFrozen: boolean;
|
||||
|
||||
@Column({ name: 'frozen_reason', type: 'text', nullable: true })
|
||||
frozenReason?: string;
|
||||
|
||||
@Column({ name: 'frozen_at', type: 'timestamptz', nullable: true })
|
||||
frozenAt?: Date;
|
||||
|
||||
@Column({ name: 'frozen_by', type: 'uuid', nullable: true })
|
||||
frozenBy?: string;
|
||||
|
||||
// Audit
|
||||
@Column({ name: 'created_by', type: 'uuid', nullable: true })
|
||||
createdBy?: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
|
||||
// Computed property
|
||||
get availableCredit(): number {
|
||||
return Number(this.creditLimit) - Number(this.currentBalance);
|
||||
}
|
||||
}
|
||||
87
src/modules/fiados/entities/fiado-payment.entity.ts
Normal file
87
src/modules/fiados/entities/fiado-payment.entity.ts
Normal file
@ -0,0 +1,87 @@
|
||||
/**
|
||||
* Fiado Payment Entity
|
||||
* Mecánicas Diesel - ERP Suite
|
||||
*
|
||||
* Represents a payment received for fiados.
|
||||
*/
|
||||
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { Customer } from '../../customers/entities/customer.entity';
|
||||
import { CustomerCreditAccount } from './customer-credit-account.entity';
|
||||
import { Fiado } from './fiado.entity';
|
||||
|
||||
export enum PaymentMethod {
|
||||
CASH = 'cash',
|
||||
CARD = 'card',
|
||||
TRANSFER = 'transfer',
|
||||
CHECK = 'check',
|
||||
OTHER = 'other',
|
||||
}
|
||||
|
||||
@Entity({ name: 'fiado_payments', schema: 'service_management' })
|
||||
@Index('idx_fiado_payments_tenant', ['tenantId'])
|
||||
@Index('idx_fiado_payments_customer', ['customerId'])
|
||||
@Index('idx_fiado_payments_account', ['creditAccountId'])
|
||||
@Index('idx_fiado_payments_fiado', ['fiadoId'])
|
||||
export class FiadoPayment {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'tenant_id', type: 'uuid' })
|
||||
tenantId: string;
|
||||
|
||||
@Column({ name: 'customer_id', type: 'uuid' })
|
||||
customerId: string;
|
||||
|
||||
@ManyToOne(() => Customer)
|
||||
@JoinColumn({ name: 'customer_id' })
|
||||
customer: Customer;
|
||||
|
||||
@Column({ name: 'credit_account_id', type: 'uuid' })
|
||||
creditAccountId: string;
|
||||
|
||||
@ManyToOne(() => CustomerCreditAccount)
|
||||
@JoinColumn({ name: 'credit_account_id' })
|
||||
creditAccount: CustomerCreditAccount;
|
||||
|
||||
@Column({ name: 'fiado_id', type: 'uuid', nullable: true })
|
||||
fiadoId?: string;
|
||||
|
||||
@ManyToOne(() => Fiado, { nullable: true })
|
||||
@JoinColumn({ name: 'fiado_id' })
|
||||
fiado?: Fiado;
|
||||
|
||||
// Identification
|
||||
@Column({ name: 'payment_number', type: 'varchar', length: 20 })
|
||||
paymentNumber: string;
|
||||
|
||||
// Amount
|
||||
@Column({ type: 'decimal', precision: 12, scale: 2 })
|
||||
amount: number;
|
||||
|
||||
// Payment method
|
||||
@Column({ name: 'payment_method', type: 'varchar', length: 20 })
|
||||
paymentMethod: PaymentMethod;
|
||||
|
||||
@Column({ name: 'payment_reference', type: 'varchar', length: 100, nullable: true })
|
||||
paymentReference?: string;
|
||||
|
||||
// Notes
|
||||
@Column({ type: 'text', nullable: true })
|
||||
notes?: string;
|
||||
|
||||
// Audit
|
||||
@Column({ name: 'received_by', type: 'uuid', nullable: true })
|
||||
receivedBy?: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
}
|
||||
115
src/modules/fiados/entities/fiado.entity.ts
Normal file
115
src/modules/fiados/entities/fiado.entity.ts
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Fiado Entity
|
||||
* Mecánicas Diesel - ERP Suite
|
||||
*
|
||||
* Represents an individual credit sale (fiado).
|
||||
*/
|
||||
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { Customer } from '../../customers/entities/customer.entity';
|
||||
import { CustomerCreditAccount } from './customer-credit-account.entity';
|
||||
|
||||
export enum FiadoStatus {
|
||||
PENDING = 'pending',
|
||||
PARTIAL = 'partial',
|
||||
PAID = 'paid',
|
||||
OVERDUE = 'overdue',
|
||||
CANCELLED = 'cancelled',
|
||||
WRITTEN_OFF = 'written_off',
|
||||
}
|
||||
|
||||
@Entity({ name: 'fiados', schema: 'service_management' })
|
||||
@Index('idx_fiados_tenant', ['tenantId'])
|
||||
@Index('idx_fiados_customer', ['customerId'])
|
||||
@Index('idx_fiados_account', ['creditAccountId'])
|
||||
@Index('idx_fiados_status', ['tenantId', 'status'])
|
||||
export class Fiado {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ name: 'tenant_id', type: 'uuid' })
|
||||
tenantId: string;
|
||||
|
||||
@Column({ name: 'customer_id', type: 'uuid' })
|
||||
customerId: string;
|
||||
|
||||
@ManyToOne(() => Customer)
|
||||
@JoinColumn({ name: 'customer_id' })
|
||||
customer: Customer;
|
||||
|
||||
@Column({ name: 'credit_account_id', type: 'uuid' })
|
||||
creditAccountId: string;
|
||||
|
||||
@ManyToOne(() => CustomerCreditAccount)
|
||||
@JoinColumn({ name: 'credit_account_id' })
|
||||
creditAccount: CustomerCreditAccount;
|
||||
|
||||
@Column({ name: 'order_id', type: 'uuid', nullable: true })
|
||||
orderId?: string;
|
||||
|
||||
// Identification
|
||||
@Column({ name: 'fiado_number', type: 'varchar', length: 20 })
|
||||
fiadoNumber: string;
|
||||
|
||||
// Amount
|
||||
@Column({ name: 'original_amount', type: 'decimal', precision: 12, scale: 2 })
|
||||
originalAmount: number;
|
||||
|
||||
@Column({ name: 'remaining_amount', type: 'decimal', precision: 12, scale: 2 })
|
||||
remainingAmount: number;
|
||||
|
||||
// Dates
|
||||
@Column({ name: 'issued_at', type: 'timestamptz', default: () => 'NOW()' })
|
||||
issuedAt: Date;
|
||||
|
||||
@Column({ name: 'due_at', type: 'timestamptz' })
|
||||
dueAt: Date;
|
||||
|
||||
@Column({ name: 'paid_at', type: 'timestamptz', nullable: true })
|
||||
paidAt?: Date;
|
||||
|
||||
// Status
|
||||
@Column({
|
||||
type: 'varchar',
|
||||
length: 20,
|
||||
default: FiadoStatus.PENDING,
|
||||
})
|
||||
status: FiadoStatus;
|
||||
|
||||
// Description
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description?: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
notes?: string;
|
||||
|
||||
// Audit
|
||||
@Column({ name: 'created_by', type: 'uuid', nullable: true })
|
||||
createdBy?: string;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at', type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at', type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
|
||||
// Computed
|
||||
get isOverdue(): boolean {
|
||||
return this.status !== FiadoStatus.PAID &&
|
||||
this.status !== FiadoStatus.CANCELLED &&
|
||||
new Date() > this.dueAt;
|
||||
}
|
||||
|
||||
get paidAmount(): number {
|
||||
return Number(this.originalAmount) - Number(this.remainingAmount);
|
||||
}
|
||||
}
|
||||
8
src/modules/fiados/entities/index.ts
Normal file
8
src/modules/fiados/entities/index.ts
Normal file
@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Fiados Entities Index
|
||||
* @module Fiados
|
||||
*/
|
||||
|
||||
export * from './customer-credit-account.entity';
|
||||
export * from './fiado.entity';
|
||||
export * from './fiado-payment.entity';
|
||||
9
src/modules/fiados/index.ts
Normal file
9
src/modules/fiados/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Fiados Module
|
||||
* Mecánicas Diesel - ERP Suite
|
||||
*
|
||||
* Credit (fiado) management for customers.
|
||||
*/
|
||||
|
||||
export * from './entities';
|
||||
export * from './services';
|
||||
640
src/modules/fiados/services/fiados.service.ts
Normal file
640
src/modules/fiados/services/fiados.service.ts
Normal file
@ -0,0 +1,640 @@
|
||||
/**
|
||||
* Fiados Service
|
||||
* Mecánicas Diesel - ERP Suite
|
||||
*
|
||||
* Business logic for credit (fiado) management.
|
||||
*/
|
||||
|
||||
import { DataSource, Repository, LessThan, In } from 'typeorm';
|
||||
import { CustomerCreditAccount } from '../entities/customer-credit-account.entity';
|
||||
import { Fiado, FiadoStatus } from '../entities/fiado.entity';
|
||||
import { FiadoPayment, PaymentMethod } from '../entities/fiado-payment.entity';
|
||||
import { Customer } from '../../customers/entities/customer.entity';
|
||||
|
||||
export interface FiadoBalance {
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
creditLimit: number;
|
||||
currentBalance: number;
|
||||
availableCredit: number;
|
||||
overdueAmount: number;
|
||||
isFrozen: boolean;
|
||||
pendingFiados: Array<{
|
||||
id: string;
|
||||
fiadoNumber: string;
|
||||
originalAmount: number;
|
||||
remainingAmount: number;
|
||||
dueAt: Date;
|
||||
status: string;
|
||||
isOverdue: boolean;
|
||||
}>;
|
||||
recentPayments: Array<{
|
||||
id: string;
|
||||
paymentNumber: string;
|
||||
amount: number;
|
||||
paymentMethod: string;
|
||||
createdAt: Date;
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface FiadoEligibility {
|
||||
eligible: boolean;
|
||||
reason: string;
|
||||
currentBalance: number;
|
||||
creditLimit: number;
|
||||
availableCredit: number;
|
||||
requestedAmount: number;
|
||||
hasOverdue: boolean;
|
||||
suggestions: string[];
|
||||
}
|
||||
|
||||
export interface CreateFiadoDto {
|
||||
customerId: string;
|
||||
amount: number;
|
||||
orderId?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export interface CreatePaymentDto {
|
||||
customerId: string;
|
||||
amount: number;
|
||||
paymentMethod: PaymentMethod;
|
||||
fiadoId?: string;
|
||||
paymentReference?: string;
|
||||
notes?: string;
|
||||
}
|
||||
|
||||
export class FiadosService {
|
||||
private creditAccountRepository: Repository<CustomerCreditAccount>;
|
||||
private fiadoRepository: Repository<Fiado>;
|
||||
private paymentRepository: Repository<FiadoPayment>;
|
||||
private customerRepository: Repository<Customer>;
|
||||
|
||||
constructor(private dataSource: DataSource) {
|
||||
this.creditAccountRepository = dataSource.getRepository(CustomerCreditAccount);
|
||||
this.fiadoRepository = dataSource.getRepository(Fiado);
|
||||
this.paymentRepository = dataSource.getRepository(FiadoPayment);
|
||||
this.customerRepository = dataSource.getRepository(Customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or create credit account for customer
|
||||
*/
|
||||
async getOrCreateCreditAccount(
|
||||
tenantId: string,
|
||||
customerId: string,
|
||||
userId?: string
|
||||
): Promise<CustomerCreditAccount> {
|
||||
let account = await this.creditAccountRepository.findOne({
|
||||
where: { tenantId, customerId },
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
// Get customer to copy credit settings
|
||||
const customer = await this.customerRepository.findOne({
|
||||
where: { id: customerId, tenantId },
|
||||
});
|
||||
|
||||
if (!customer) {
|
||||
throw new Error('Cliente no encontrado');
|
||||
}
|
||||
|
||||
account = this.creditAccountRepository.create({
|
||||
tenantId,
|
||||
customerId,
|
||||
creditLimit: customer.creditLimit || 0,
|
||||
creditDays: customer.creditDays || 30,
|
||||
createdBy: userId,
|
||||
});
|
||||
|
||||
account = await this.creditAccountRepository.save(account);
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fiado balance for a customer
|
||||
*/
|
||||
async getFiadoBalance(tenantId: string, customerId: string): Promise<FiadoBalance> {
|
||||
const account = await this.getOrCreateCreditAccount(tenantId, customerId);
|
||||
|
||||
// Get customer info
|
||||
const customer = await this.customerRepository.findOne({
|
||||
where: { id: customerId, tenantId },
|
||||
});
|
||||
|
||||
// Get pending fiados
|
||||
const pendingFiados = await this.fiadoRepository.find({
|
||||
where: {
|
||||
tenantId,
|
||||
customerId,
|
||||
status: In([FiadoStatus.PENDING, FiadoStatus.PARTIAL, FiadoStatus.OVERDUE]),
|
||||
},
|
||||
order: { dueAt: 'ASC' },
|
||||
take: 10,
|
||||
});
|
||||
|
||||
// Get recent payments
|
||||
const recentPayments = await this.paymentRepository.find({
|
||||
where: { tenantId, customerId },
|
||||
order: { createdAt: 'DESC' },
|
||||
take: 5,
|
||||
});
|
||||
|
||||
return {
|
||||
customerId,
|
||||
customerName: customer?.name || 'Desconocido',
|
||||
creditLimit: Number(account.creditLimit),
|
||||
currentBalance: Number(account.currentBalance),
|
||||
availableCredit: Number(account.creditLimit) - Number(account.currentBalance),
|
||||
overdueAmount: Number(account.overdueAmount),
|
||||
isFrozen: account.isFrozen,
|
||||
pendingFiados: pendingFiados.map(f => ({
|
||||
id: f.id,
|
||||
fiadoNumber: f.fiadoNumber,
|
||||
originalAmount: Number(f.originalAmount),
|
||||
remainingAmount: Number(f.remainingAmount),
|
||||
dueAt: f.dueAt,
|
||||
status: f.status,
|
||||
isOverdue: f.isOverdue,
|
||||
})),
|
||||
recentPayments: recentPayments.map(p => ({
|
||||
id: p.id,
|
||||
paymentNumber: p.paymentNumber,
|
||||
amount: Number(p.amount),
|
||||
paymentMethod: p.paymentMethod,
|
||||
createdAt: p.createdAt,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if customer is eligible for credit
|
||||
*/
|
||||
async checkEligibility(
|
||||
tenantId: string,
|
||||
customerId: string,
|
||||
amount: number
|
||||
): Promise<FiadoEligibility> {
|
||||
const account = await this.getOrCreateCreditAccount(tenantId, customerId);
|
||||
const suggestions: string[] = [];
|
||||
|
||||
const currentBalance = Number(account.currentBalance);
|
||||
const creditLimit = Number(account.creditLimit);
|
||||
const availableCredit = creditLimit - currentBalance;
|
||||
const overdueAmount = Number(account.overdueAmount);
|
||||
const hasOverdue = overdueAmount > 0;
|
||||
|
||||
// Check if account is frozen
|
||||
if (account.isFrozen) {
|
||||
return {
|
||||
eligible: false,
|
||||
reason: `Crédito congelado: ${account.frozenReason || 'Contacte administración'}`,
|
||||
currentBalance,
|
||||
creditLimit,
|
||||
availableCredit,
|
||||
requestedAmount: amount,
|
||||
hasOverdue,
|
||||
suggestions: ['Contactar al administrador para revisar el estado de la cuenta'],
|
||||
};
|
||||
}
|
||||
|
||||
// Check for overdue fiados
|
||||
if (hasOverdue) {
|
||||
suggestions.push('Solicitar pago del saldo vencido antes de continuar');
|
||||
return {
|
||||
eligible: false,
|
||||
reason: `Cliente tiene $${overdueAmount.toFixed(2)} vencido`,
|
||||
currentBalance,
|
||||
creditLimit,
|
||||
availableCredit,
|
||||
requestedAmount: amount,
|
||||
hasOverdue,
|
||||
suggestions,
|
||||
};
|
||||
}
|
||||
|
||||
// Check credit limit
|
||||
if (creditLimit === 0) {
|
||||
suggestions.push('Solicitar apertura de línea de crédito');
|
||||
return {
|
||||
eligible: false,
|
||||
reason: 'Cliente no tiene línea de crédito autorizada',
|
||||
currentBalance,
|
||||
creditLimit,
|
||||
availableCredit,
|
||||
requestedAmount: amount,
|
||||
hasOverdue,
|
||||
suggestions,
|
||||
};
|
||||
}
|
||||
|
||||
// Check available credit
|
||||
if (amount > availableCredit) {
|
||||
suggestions.push(`Reducir el monto a $${availableCredit.toFixed(2)}`);
|
||||
suggestions.push('Solicitar aumento de límite de crédito');
|
||||
suggestions.push('Solicitar pago parcial antes de continuar');
|
||||
return {
|
||||
eligible: false,
|
||||
reason: `Monto ($${amount.toFixed(2)}) excede crédito disponible ($${availableCredit.toFixed(2)})`,
|
||||
currentBalance,
|
||||
creditLimit,
|
||||
availableCredit,
|
||||
requestedAmount: amount,
|
||||
hasOverdue,
|
||||
suggestions,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
eligible: true,
|
||||
reason: 'Cliente con crédito disponible',
|
||||
currentBalance,
|
||||
creditLimit,
|
||||
availableCredit,
|
||||
requestedAmount: amount,
|
||||
hasOverdue,
|
||||
suggestions: [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new fiado (credit sale)
|
||||
*/
|
||||
async createFiado(
|
||||
tenantId: string,
|
||||
dto: CreateFiadoDto,
|
||||
userId?: string
|
||||
): Promise<Fiado> {
|
||||
// Check eligibility first
|
||||
const eligibility = await this.checkEligibility(tenantId, dto.customerId, dto.amount);
|
||||
if (!eligibility.eligible) {
|
||||
throw new Error(eligibility.reason);
|
||||
}
|
||||
|
||||
const account = await this.getOrCreateCreditAccount(tenantId, dto.customerId, userId);
|
||||
|
||||
// Generate fiado number
|
||||
const fiadoNumber = await this.generateFiadoNumber(tenantId);
|
||||
|
||||
// Calculate due date based on credit days
|
||||
const dueAt = new Date();
|
||||
dueAt.setDate(dueAt.getDate() + account.creditDays);
|
||||
|
||||
// Create fiado
|
||||
const fiado = this.fiadoRepository.create({
|
||||
tenantId,
|
||||
customerId: dto.customerId,
|
||||
creditAccountId: account.id,
|
||||
orderId: dto.orderId,
|
||||
fiadoNumber,
|
||||
originalAmount: dto.amount,
|
||||
remainingAmount: dto.amount,
|
||||
dueAt,
|
||||
status: FiadoStatus.PENDING,
|
||||
description: dto.description,
|
||||
createdBy: userId,
|
||||
});
|
||||
|
||||
const savedFiado = await this.fiadoRepository.save(fiado);
|
||||
|
||||
// Update account balance
|
||||
await this.creditAccountRepository.update(
|
||||
{ id: account.id },
|
||||
{
|
||||
currentBalance: () => `current_balance + ${dto.amount}`,
|
||||
totalCreditGiven: () => `total_credit_given + ${dto.amount}`,
|
||||
}
|
||||
);
|
||||
|
||||
return savedFiado;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a payment for fiados
|
||||
*/
|
||||
async registerPayment(
|
||||
tenantId: string,
|
||||
dto: CreatePaymentDto,
|
||||
userId?: string
|
||||
): Promise<{ payment: FiadoPayment; fiadosPaid: Fiado[] }> {
|
||||
const account = await this.getOrCreateCreditAccount(tenantId, dto.customerId, userId);
|
||||
|
||||
// Generate payment number
|
||||
const paymentNumber = await this.generatePaymentNumber(tenantId);
|
||||
|
||||
// Create payment record
|
||||
const payment = this.paymentRepository.create({
|
||||
tenantId,
|
||||
customerId: dto.customerId,
|
||||
creditAccountId: account.id,
|
||||
fiadoId: dto.fiadoId,
|
||||
paymentNumber,
|
||||
amount: dto.amount,
|
||||
paymentMethod: dto.paymentMethod,
|
||||
paymentReference: dto.paymentReference,
|
||||
notes: dto.notes,
|
||||
receivedBy: userId,
|
||||
});
|
||||
|
||||
const savedPayment = await this.paymentRepository.save(payment);
|
||||
|
||||
// Apply payment to fiados
|
||||
const fiadosPaid = await this.applyPaymentToFiados(
|
||||
tenantId,
|
||||
dto.customerId,
|
||||
dto.amount,
|
||||
dto.fiadoId
|
||||
);
|
||||
|
||||
// Update account balance
|
||||
await this.creditAccountRepository.update(
|
||||
{ id: account.id },
|
||||
{
|
||||
currentBalance: () => `current_balance - ${dto.amount}`,
|
||||
totalPaymentsReceived: () => `total_payments_received + ${dto.amount}`,
|
||||
}
|
||||
);
|
||||
|
||||
// Recalculate overdue amount
|
||||
await this.recalculateOverdueAmount(tenantId, account.id);
|
||||
|
||||
return { payment: savedPayment, fiadosPaid };
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply payment to fiados (FIFO - oldest first)
|
||||
*/
|
||||
private async applyPaymentToFiados(
|
||||
tenantId: string,
|
||||
customerId: string,
|
||||
amount: number,
|
||||
specificFiadoId?: string
|
||||
): Promise<Fiado[]> {
|
||||
let remainingPayment = amount;
|
||||
const paidFiados: Fiado[] = [];
|
||||
|
||||
// If specific fiado, apply to that first
|
||||
if (specificFiadoId) {
|
||||
const specificFiado = await this.fiadoRepository.findOne({
|
||||
where: { id: specificFiadoId, tenantId, customerId },
|
||||
});
|
||||
|
||||
if (specificFiado && Number(specificFiado.remainingAmount) > 0) {
|
||||
const toApply = Math.min(remainingPayment, Number(specificFiado.remainingAmount));
|
||||
specificFiado.remainingAmount = Number(specificFiado.remainingAmount) - toApply;
|
||||
remainingPayment -= toApply;
|
||||
|
||||
if (specificFiado.remainingAmount <= 0) {
|
||||
specificFiado.status = FiadoStatus.PAID;
|
||||
specificFiado.paidAt = new Date();
|
||||
} else {
|
||||
specificFiado.status = FiadoStatus.PARTIAL;
|
||||
}
|
||||
|
||||
await this.fiadoRepository.save(specificFiado);
|
||||
paidFiados.push(specificFiado);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply remaining to oldest fiados first
|
||||
if (remainingPayment > 0) {
|
||||
const pendingFiados = await this.fiadoRepository.find({
|
||||
where: {
|
||||
tenantId,
|
||||
customerId,
|
||||
status: In([FiadoStatus.PENDING, FiadoStatus.PARTIAL, FiadoStatus.OVERDUE]),
|
||||
},
|
||||
order: { dueAt: 'ASC' },
|
||||
});
|
||||
|
||||
for (const fiado of pendingFiados) {
|
||||
if (remainingPayment <= 0) break;
|
||||
if (specificFiadoId && fiado.id === specificFiadoId) continue; // Already processed
|
||||
|
||||
const toApply = Math.min(remainingPayment, Number(fiado.remainingAmount));
|
||||
fiado.remainingAmount = Number(fiado.remainingAmount) - toApply;
|
||||
remainingPayment -= toApply;
|
||||
|
||||
if (fiado.remainingAmount <= 0) {
|
||||
fiado.status = FiadoStatus.PAID;
|
||||
fiado.paidAt = new Date();
|
||||
} else {
|
||||
fiado.status = FiadoStatus.PARTIAL;
|
||||
}
|
||||
|
||||
await this.fiadoRepository.save(fiado);
|
||||
paidFiados.push(fiado);
|
||||
}
|
||||
}
|
||||
|
||||
return paidFiados;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update overdue fiados status
|
||||
*/
|
||||
async updateOverdueStatus(tenantId: string): Promise<number> {
|
||||
const now = new Date();
|
||||
const result = await this.fiadoRepository.update(
|
||||
{
|
||||
tenantId,
|
||||
status: In([FiadoStatus.PENDING, FiadoStatus.PARTIAL]),
|
||||
dueAt: LessThan(now),
|
||||
},
|
||||
{ status: FiadoStatus.OVERDUE }
|
||||
);
|
||||
|
||||
return result.affected || 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculate overdue amount for an account
|
||||
*/
|
||||
private async recalculateOverdueAmount(tenantId: string, accountId: string): Promise<void> {
|
||||
const result = await this.fiadoRepository
|
||||
.createQueryBuilder('f')
|
||||
.select('COALESCE(SUM(f.remaining_amount), 0)', 'total')
|
||||
.where('f.credit_account_id = :accountId', { accountId })
|
||||
.andWhere('f.status = :status', { status: FiadoStatus.OVERDUE })
|
||||
.getRawOne();
|
||||
|
||||
await this.creditAccountRepository.update(
|
||||
{ id: accountId },
|
||||
{ overdueAmount: result?.total || 0 }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get accounts receivable summary
|
||||
*/
|
||||
async getAccountsReceivable(
|
||||
tenantId: string,
|
||||
options: { status?: 'all' | 'current' | 'overdue'; minAmount?: number; limit?: number }
|
||||
): Promise<{
|
||||
total: number;
|
||||
count: number;
|
||||
overdueTotal: number;
|
||||
overdueCount: number;
|
||||
accounts: Array<{
|
||||
customerId: string;
|
||||
customerName: string;
|
||||
balance: number;
|
||||
overdueAmount: number;
|
||||
oldestDueDate: Date | null;
|
||||
}>;
|
||||
}> {
|
||||
const limit = options.limit || 50;
|
||||
|
||||
const queryBuilder = this.creditAccountRepository
|
||||
.createQueryBuilder('ca')
|
||||
.leftJoin('ca.customer', 'c')
|
||||
.select([
|
||||
'ca.customer_id AS "customerId"',
|
||||
'c.name AS "customerName"',
|
||||
'ca.current_balance AS balance',
|
||||
'ca.overdue_amount AS "overdueAmount"',
|
||||
])
|
||||
.where('ca.tenant_id = :tenantId', { tenantId })
|
||||
.andWhere('ca.current_balance > 0');
|
||||
|
||||
if (options.status === 'overdue') {
|
||||
queryBuilder.andWhere('ca.overdue_amount > 0');
|
||||
} else if (options.status === 'current') {
|
||||
queryBuilder.andWhere('ca.overdue_amount = 0');
|
||||
}
|
||||
|
||||
if (options.minAmount) {
|
||||
queryBuilder.andWhere('ca.current_balance >= :minAmount', { minAmount: options.minAmount });
|
||||
}
|
||||
|
||||
const accounts = await queryBuilder
|
||||
.orderBy('ca.current_balance', 'DESC')
|
||||
.limit(limit)
|
||||
.getRawMany();
|
||||
|
||||
// Get totals
|
||||
const totals = await this.creditAccountRepository
|
||||
.createQueryBuilder('ca')
|
||||
.select([
|
||||
'COALESCE(SUM(ca.current_balance), 0) AS total',
|
||||
'COUNT(*) AS count',
|
||||
'COALESCE(SUM(ca.overdue_amount), 0) AS "overdueTotal"',
|
||||
'COUNT(CASE WHEN ca.overdue_amount > 0 THEN 1 END) AS "overdueCount"',
|
||||
])
|
||||
.where('ca.tenant_id = :tenantId', { tenantId })
|
||||
.andWhere('ca.current_balance > 0')
|
||||
.getRawOne();
|
||||
|
||||
return {
|
||||
total: Number(totals?.total || 0),
|
||||
count: Number(totals?.count || 0),
|
||||
overdueTotal: Number(totals?.overdueTotal || 0),
|
||||
overdueCount: Number(totals?.overdueCount || 0),
|
||||
accounts: accounts.map(a => ({
|
||||
customerId: a.customerId,
|
||||
customerName: a.customerName,
|
||||
balance: Number(a.balance),
|
||||
overdueAmount: Number(a.overdueAmount),
|
||||
oldestDueDate: null, // Would need additional query
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique fiado number
|
||||
*/
|
||||
private async generateFiadoNumber(tenantId: string): Promise<string> {
|
||||
const year = new Date().getFullYear();
|
||||
const prefix = `F-${year}-`;
|
||||
|
||||
const lastFiado = await this.fiadoRepository
|
||||
.createQueryBuilder('f')
|
||||
.where('f.tenant_id = :tenantId', { tenantId })
|
||||
.andWhere('f.fiado_number LIKE :prefix', { prefix: `${prefix}%` })
|
||||
.orderBy('f.fiado_number', 'DESC')
|
||||
.getOne();
|
||||
|
||||
let nextNumber = 1;
|
||||
if (lastFiado) {
|
||||
const match = lastFiado.fiadoNumber.match(/F-\d{4}-(\d+)/);
|
||||
if (match) {
|
||||
nextNumber = parseInt(match[1], 10) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return `${prefix}${nextNumber.toString().padStart(5, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate unique payment number
|
||||
*/
|
||||
private async generatePaymentNumber(tenantId: string): Promise<string> {
|
||||
const year = new Date().getFullYear();
|
||||
const prefix = `P-${year}-`;
|
||||
|
||||
const lastPayment = await this.paymentRepository
|
||||
.createQueryBuilder('p')
|
||||
.where('p.tenant_id = :tenantId', { tenantId })
|
||||
.andWhere('p.payment_number LIKE :prefix', { prefix: `${prefix}%` })
|
||||
.orderBy('p.payment_number', 'DESC')
|
||||
.getOne();
|
||||
|
||||
let nextNumber = 1;
|
||||
if (lastPayment) {
|
||||
const match = lastPayment.paymentNumber.match(/P-\d{4}-(\d+)/);
|
||||
if (match) {
|
||||
nextNumber = parseInt(match[1], 10) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return `${prefix}${nextNumber.toString().padStart(5, '0')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Freeze a customer's credit account
|
||||
*/
|
||||
async freezeAccount(
|
||||
tenantId: string,
|
||||
customerId: string,
|
||||
reason: string,
|
||||
userId?: string
|
||||
): Promise<CustomerCreditAccount> {
|
||||
const account = await this.getOrCreateCreditAccount(tenantId, customerId);
|
||||
|
||||
account.isFrozen = true;
|
||||
account.frozenReason = reason;
|
||||
account.frozenAt = new Date();
|
||||
account.frozenBy = userId;
|
||||
|
||||
return this.creditAccountRepository.save(account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unfreeze a customer's credit account
|
||||
*/
|
||||
async unfreezeAccount(tenantId: string, customerId: string): Promise<CustomerCreditAccount> {
|
||||
const account = await this.getOrCreateCreditAccount(tenantId, customerId);
|
||||
|
||||
account.isFrozen = false;
|
||||
account.frozenReason = undefined;
|
||||
account.frozenAt = undefined;
|
||||
account.frozenBy = undefined;
|
||||
|
||||
return this.creditAccountRepository.save(account);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update credit limit for a customer
|
||||
*/
|
||||
async updateCreditLimit(
|
||||
tenantId: string,
|
||||
customerId: string,
|
||||
newLimit: number
|
||||
): Promise<CustomerCreditAccount> {
|
||||
const account = await this.getOrCreateCreditAccount(tenantId, customerId);
|
||||
account.creditLimit = newLimit;
|
||||
return this.creditAccountRepository.save(account);
|
||||
}
|
||||
}
|
||||
6
src/modules/fiados/services/index.ts
Normal file
6
src/modules/fiados/services/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Fiados Services Index
|
||||
* @module Fiados
|
||||
*/
|
||||
|
||||
export * from './fiados.service';
|
||||
@ -47,10 +47,10 @@ export class McpModule {
|
||||
|
||||
// Register tool providers (connected to real services)
|
||||
this.toolRegistry.registerProvider(new ProductsToolsService(this.dataSource));
|
||||
this.toolRegistry.registerProvider(new InventoryToolsService(this.dataSource));
|
||||
this.toolRegistry.registerProvider(new OrdersToolsService(this.dataSource));
|
||||
this.toolRegistry.registerProvider(new CustomersToolsService(this.dataSource));
|
||||
this.toolRegistry.registerProvider(new FiadosToolsService());
|
||||
this.toolRegistry.registerProvider(new InventoryToolsService()); // TODO: Connect to PartService
|
||||
this.toolRegistry.registerProvider(new OrdersToolsService()); // TODO: Connect to ServiceOrderService
|
||||
this.toolRegistry.registerProvider(new CustomersToolsService()); // TODO: Connect to CustomersService
|
||||
this.toolRegistry.registerProvider(new FiadosToolsService(this.dataSource));
|
||||
this.toolRegistry.registerProvider(new SalesToolsService(this.dataSource));
|
||||
this.toolRegistry.registerProvider(new FinancialToolsService(this.dataSource));
|
||||
this.toolRegistry.registerProvider(new BranchToolsService());
|
||||
|
||||
@ -1,17 +1,25 @@
|
||||
import { DataSource } from 'typeorm';
|
||||
import {
|
||||
McpToolProvider,
|
||||
McpToolDefinition,
|
||||
McpToolHandler,
|
||||
McpContext,
|
||||
} from '../interfaces';
|
||||
import { FiadosService, CreateFiadoDto, CreatePaymentDto } from '../../fiados/services/fiados.service';
|
||||
import { PaymentMethod } from '../../fiados/entities/fiado-payment.entity';
|
||||
|
||||
/**
|
||||
* Fiados (Credit) Tools Service
|
||||
* Provides MCP tools for credit/fiado management.
|
||||
*
|
||||
* TODO: Connect to actual FiadosService when available.
|
||||
* Connected to FiadosService for real data.
|
||||
*/
|
||||
export class FiadosToolsService implements McpToolProvider {
|
||||
private fiadosService: FiadosService;
|
||||
|
||||
constructor(dataSource: DataSource) {
|
||||
this.fiadosService = new FiadosService(dataSource);
|
||||
}
|
||||
|
||||
getTools(): McpToolDefinition[] {
|
||||
return [
|
||||
{
|
||||
@ -54,7 +62,10 @@ export class FiadosToolsService implements McpToolProvider {
|
||||
properties: {
|
||||
customer_id: { type: 'string', format: 'uuid' },
|
||||
amount: { type: 'number', minimum: 0.01 },
|
||||
payment_method: { type: 'string', enum: ['cash', 'card', 'transfer'] },
|
||||
payment_method: { type: 'string', enum: ['cash', 'card', 'transfer', 'check', 'other'] },
|
||||
fiado_id: { type: 'string', format: 'uuid' },
|
||||
payment_reference: { type: 'string' },
|
||||
notes: { type: 'string' },
|
||||
},
|
||||
required: ['customer_id', 'amount'],
|
||||
},
|
||||
@ -97,19 +108,35 @@ export class FiadosToolsService implements McpToolProvider {
|
||||
params: { customer_id: string },
|
||||
context: McpContext
|
||||
): Promise<any> {
|
||||
// TODO: Connect to actual fiados service
|
||||
const balance = await this.fiadosService.getFiadoBalance(
|
||||
context.tenantId,
|
||||
params.customer_id
|
||||
);
|
||||
|
||||
return {
|
||||
customer_id: params.customer_id,
|
||||
customer_name: 'Cliente ejemplo',
|
||||
balance: 1500.00,
|
||||
credit_limit: 5000.00,
|
||||
available_credit: 3500.00,
|
||||
pending_fiados: [
|
||||
{ id: 'fiado-1', amount: 500.00, date: '2026-01-10', status: 'pending' },
|
||||
{ id: 'fiado-2', amount: 1000.00, date: '2026-01-05', status: 'pending' },
|
||||
],
|
||||
recent_payments: [],
|
||||
message: 'Conectar a FiadosService real',
|
||||
customer_id: balance.customerId,
|
||||
customer_name: balance.customerName,
|
||||
balance: balance.currentBalance,
|
||||
credit_limit: balance.creditLimit,
|
||||
available_credit: balance.availableCredit,
|
||||
overdue_amount: balance.overdueAmount,
|
||||
is_frozen: balance.isFrozen,
|
||||
pending_fiados: balance.pendingFiados.map(f => ({
|
||||
id: f.id,
|
||||
fiado_number: f.fiadoNumber,
|
||||
original_amount: f.originalAmount,
|
||||
remaining_amount: f.remainingAmount,
|
||||
due_at: f.dueAt,
|
||||
status: f.status,
|
||||
is_overdue: f.isOverdue,
|
||||
})),
|
||||
recent_payments: balance.recentPayments.map(p => ({
|
||||
id: p.id,
|
||||
payment_number: p.paymentNumber,
|
||||
amount: p.amount,
|
||||
payment_method: p.paymentMethod,
|
||||
created_at: p.createdAt,
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
@ -117,48 +144,89 @@ export class FiadosToolsService implements McpToolProvider {
|
||||
params: { customer_id: string; amount: number; order_id?: string; description?: string },
|
||||
context: McpContext
|
||||
): Promise<any> {
|
||||
// TODO: Connect to actual fiados service
|
||||
// First check eligibility
|
||||
const eligibility = await this.checkFiadoEligibility(
|
||||
{ customer_id: params.customer_id, amount: params.amount },
|
||||
context
|
||||
const dto: CreateFiadoDto = {
|
||||
customerId: params.customer_id,
|
||||
amount: params.amount,
|
||||
orderId: params.order_id,
|
||||
description: params.description,
|
||||
};
|
||||
|
||||
const fiado = await this.fiadosService.createFiado(
|
||||
context.tenantId,
|
||||
dto,
|
||||
context.userId
|
||||
);
|
||||
|
||||
if (!eligibility.eligible) {
|
||||
throw new Error(eligibility.reason);
|
||||
}
|
||||
// Get updated balance
|
||||
const balance = await this.fiadosService.getFiadoBalance(
|
||||
context.tenantId,
|
||||
params.customer_id
|
||||
);
|
||||
|
||||
return {
|
||||
fiado_id: 'fiado-' + Date.now(),
|
||||
customer_id: params.customer_id,
|
||||
amount: params.amount,
|
||||
order_id: params.order_id,
|
||||
description: params.description,
|
||||
due_date: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), // 30 days
|
||||
new_balance: 1500.00 + params.amount,
|
||||
remaining_credit: 3500.00 - params.amount,
|
||||
created_by: context.userId,
|
||||
created_at: new Date().toISOString(),
|
||||
message: 'Conectar a FiadosService real',
|
||||
fiado_id: fiado.id,
|
||||
fiado_number: fiado.fiadoNumber,
|
||||
customer_id: fiado.customerId,
|
||||
amount: Number(fiado.originalAmount),
|
||||
order_id: fiado.orderId,
|
||||
description: fiado.description,
|
||||
due_date: fiado.dueAt.toISOString(),
|
||||
new_balance: balance.currentBalance,
|
||||
remaining_credit: balance.availableCredit,
|
||||
created_by: fiado.createdBy,
|
||||
created_at: fiado.createdAt.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
private async registerFiadoPayment(
|
||||
params: { customer_id: string; amount: number; payment_method?: string },
|
||||
params: {
|
||||
customer_id: string;
|
||||
amount: number;
|
||||
payment_method?: string;
|
||||
fiado_id?: string;
|
||||
payment_reference?: string;
|
||||
notes?: string;
|
||||
},
|
||||
context: McpContext
|
||||
): Promise<any> {
|
||||
// TODO: Connect to actual fiados service
|
||||
return {
|
||||
payment_id: 'payment-' + Date.now(),
|
||||
customer_id: params.customer_id,
|
||||
const dto: CreatePaymentDto = {
|
||||
customerId: params.customer_id,
|
||||
amount: params.amount,
|
||||
payment_method: params.payment_method || 'cash',
|
||||
previous_balance: 1500.00,
|
||||
new_balance: 1500.00 - params.amount,
|
||||
fiados_paid: [],
|
||||
created_by: context.userId,
|
||||
created_at: new Date().toISOString(),
|
||||
message: 'Conectar a FiadosService real',
|
||||
paymentMethod: (params.payment_method || 'cash') as PaymentMethod,
|
||||
fiadoId: params.fiado_id,
|
||||
paymentReference: params.payment_reference,
|
||||
notes: params.notes,
|
||||
};
|
||||
|
||||
const { payment, fiadosPaid } = await this.fiadosService.registerPayment(
|
||||
context.tenantId,
|
||||
dto,
|
||||
context.userId
|
||||
);
|
||||
|
||||
// Get updated balance
|
||||
const balance = await this.fiadosService.getFiadoBalance(
|
||||
context.tenantId,
|
||||
params.customer_id
|
||||
);
|
||||
|
||||
return {
|
||||
payment_id: payment.id,
|
||||
payment_number: payment.paymentNumber,
|
||||
customer_id: payment.customerId,
|
||||
amount: Number(payment.amount),
|
||||
payment_method: payment.paymentMethod,
|
||||
payment_reference: payment.paymentReference,
|
||||
previous_balance: balance.currentBalance + params.amount,
|
||||
new_balance: balance.currentBalance,
|
||||
fiados_paid: fiadosPaid.map(f => ({
|
||||
fiado_id: f.id,
|
||||
fiado_number: f.fiadoNumber,
|
||||
status: f.status,
|
||||
remaining_amount: Number(f.remainingAmount),
|
||||
})),
|
||||
created_by: payment.receivedBy,
|
||||
created_at: payment.createdAt.toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -166,51 +234,21 @@ export class FiadosToolsService implements McpToolProvider {
|
||||
params: { customer_id: string; amount: number },
|
||||
context: McpContext
|
||||
): Promise<any> {
|
||||
// TODO: Connect to actual fiados service
|
||||
const mockBalance = 1500.00;
|
||||
const mockCreditLimit = 5000.00;
|
||||
const mockAvailableCredit = mockCreditLimit - mockBalance;
|
||||
const hasOverdue = false;
|
||||
|
||||
if (hasOverdue) {
|
||||
return {
|
||||
eligible: false,
|
||||
reason: 'Cliente tiene saldo vencido',
|
||||
current_balance: mockBalance,
|
||||
credit_limit: mockCreditLimit,
|
||||
available_credit: mockAvailableCredit,
|
||||
requested_amount: params.amount,
|
||||
has_overdue: true,
|
||||
suggestions: ['Solicitar pago del saldo vencido antes de continuar'],
|
||||
};
|
||||
}
|
||||
|
||||
if (params.amount > mockAvailableCredit) {
|
||||
return {
|
||||
eligible: false,
|
||||
reason: 'Monto excede credito disponible',
|
||||
current_balance: mockBalance,
|
||||
credit_limit: mockCreditLimit,
|
||||
available_credit: mockAvailableCredit,
|
||||
requested_amount: params.amount,
|
||||
has_overdue: false,
|
||||
suggestions: [
|
||||
`Reducir el monto a $${mockAvailableCredit.toFixed(2)}`,
|
||||
'Solicitar aumento de limite de credito',
|
||||
],
|
||||
};
|
||||
}
|
||||
const eligibility = await this.fiadosService.checkEligibility(
|
||||
context.tenantId,
|
||||
params.customer_id,
|
||||
params.amount
|
||||
);
|
||||
|
||||
return {
|
||||
eligible: true,
|
||||
reason: 'Cliente con credito disponible',
|
||||
current_balance: mockBalance,
|
||||
credit_limit: mockCreditLimit,
|
||||
available_credit: mockAvailableCredit,
|
||||
requested_amount: params.amount,
|
||||
has_overdue: false,
|
||||
suggestions: [],
|
||||
message: 'Conectar a FiadosService real',
|
||||
eligible: eligibility.eligible,
|
||||
reason: eligibility.reason,
|
||||
current_balance: eligibility.currentBalance,
|
||||
credit_limit: eligibility.creditLimit,
|
||||
available_credit: eligibility.availableCredit,
|
||||
requested_amount: eligibility.requestedAmount,
|
||||
has_overdue: eligibility.hasOverdue,
|
||||
suggestions: eligibility.suggestions,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user