/** * BranchPaymentTerminal Service * Servicio para gestión de terminales de pago por sucursal * * @module Branches */ import { Repository, FindOptionsWhere } from 'typeorm'; import { AppDataSource } from '../../../shared/database/typeorm.config'; import { BranchPaymentTerminal, TerminalProvider, HealthStatus, } from '../entities/branch-payment-terminal.entity'; import { ServiceContext } from './branch.service'; export interface CreateBranchPaymentTerminalDto { branchId: string; terminalId: string; terminalName?: string; terminalProvider: TerminalProvider; provider: string; credentials?: Record; isActive?: boolean; isPrimary?: boolean; dailyLimit?: number; transactionLimit?: number; config?: Record; } export interface UpdateBranchPaymentTerminalDto { terminalName?: string; terminalProvider?: TerminalProvider; provider?: string; credentials?: Record; isActive?: boolean; isPrimary?: boolean; healthStatus?: HealthStatus; dailyLimit?: number; transactionLimit?: number; config?: Record; } export interface BranchPaymentTerminalFilters { branchId?: string; terminalProvider?: TerminalProvider; isActive?: boolean; healthStatus?: HealthStatus; } export class BranchPaymentTerminalService { private repository: Repository; constructor() { this.repository = AppDataSource.getRepository(BranchPaymentTerminal); } async findAll( ctx: ServiceContext, filters?: BranchPaymentTerminalFilters ): Promise { const where: FindOptionsWhere = { tenantId: ctx.tenantId, }; if (filters?.branchId) { where.branchId = filters.branchId; } if (filters?.terminalProvider) { where.terminalProvider = filters.terminalProvider; } if (filters?.isActive !== undefined) { where.isActive = filters.isActive; } if (filters?.healthStatus) { where.healthStatus = filters.healthStatus; } return this.repository.find({ where, order: { isPrimary: 'DESC', terminalName: 'ASC' }, }); } async findById(ctx: ServiceContext, id: string): Promise { return this.repository.findOne({ where: { id, tenantId: ctx.tenantId }, }); } async findByBranch(ctx: ServiceContext, branchId: string): Promise { return this.repository.find({ where: { branchId, tenantId: ctx.tenantId, isActive: true }, order: { isPrimary: 'DESC', terminalName: 'ASC' }, }); } async findPrimaryTerminal( ctx: ServiceContext, branchId: string ): Promise { return this.repository.findOne({ where: { branchId, tenantId: ctx.tenantId, isPrimary: true, isActive: true }, }); } async findByTerminalId( ctx: ServiceContext, terminalId: string ): Promise { return this.repository.findOne({ where: { terminalId, tenantId: ctx.tenantId }, }); } async create( ctx: ServiceContext, data: CreateBranchPaymentTerminalDto ): Promise { // If this is the first terminal or marked as primary, ensure only one primary if (data.isPrimary) { await this.repository.update( { branchId: data.branchId, tenantId: ctx.tenantId, isPrimary: true }, { isPrimary: false } ); } const terminal = this.repository.create({ ...data, tenantId: ctx.tenantId, healthStatus: 'unknown', }); return this.repository.save(terminal); } async update( ctx: ServiceContext, id: string, data: UpdateBranchPaymentTerminalDto ): Promise { const terminal = await this.findById(ctx, id); if (!terminal) { return null; } // If setting as primary, unset other primaries for this branch if (data.isPrimary && !terminal.isPrimary) { await this.repository.update( { branchId: terminal.branchId, tenantId: ctx.tenantId, isPrimary: true }, { isPrimary: false } ); } Object.assign(terminal, data); return this.repository.save(terminal); } async delete(ctx: ServiceContext, id: string): Promise { const result = await this.repository.delete({ id, tenantId: ctx.tenantId }); return result.affected ? result.affected > 0 : false; } async setAsPrimary(ctx: ServiceContext, id: string): Promise { const terminal = await this.findById(ctx, id); if (!terminal) { return null; } // Unset other primaries await this.repository.update( { branchId: terminal.branchId, tenantId: ctx.tenantId, isPrimary: true }, { isPrimary: false } ); terminal.isPrimary = true; return this.repository.save(terminal); } async updateHealthStatus( ctx: ServiceContext, id: string, healthStatus: HealthStatus ): Promise { const terminal = await this.findById(ctx, id); if (!terminal) { return null; } terminal.healthStatus = healthStatus; terminal.lastHealthCheckAt = new Date(); return this.repository.save(terminal); } async recordTransaction(ctx: ServiceContext, id: string): Promise { const terminal = await this.findById(ctx, id); if (!terminal) { return null; } terminal.lastTransactionAt = new Date(); return this.repository.save(terminal); } async getHealthSummary(ctx: ServiceContext): Promise<{ total: number; healthy: number; degraded: number; offline: number; unknown: number; }> { const terminals = await this.repository.find({ where: { tenantId: ctx.tenantId, isActive: true }, }); return { total: terminals.length, healthy: terminals.filter(t => t.healthStatus === 'healthy').length, degraded: terminals.filter(t => t.healthStatus === 'degraded').length, offline: terminals.filter(t => t.healthStatus === 'offline').length, unknown: terminals.filter(t => t.healthStatus === 'unknown').length, }; } async getTerminalsNeedingHealthCheck( ctx: ServiceContext, maxAgeMinutes: number = 30 ): Promise { const cutoff = new Date(Date.now() - maxAgeMinutes * 60 * 1000); const terminals = await this.repository.find({ where: { tenantId: ctx.tenantId, isActive: true }, }); return terminals.filter(t => !t.lastHealthCheckAt || t.lastHealthCheckAt < cutoff); } }