npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cbm-common/financial-bank-repository

v0.0.1

Published

Repositorio Angular para la gestión completa de bancos financieros y cuentas bancarias con integración contable, términos de pago y sistema de auditoría integral. Soporta operaciones de compras, ventas, secuencias de cheques y gestión de tarjetas de crédi

Downloads

8

Readme

Financial Bank Repository

Repositorio Angular para la gestión completa de bancos financieros y cuentas bancarias con integración contable, términos de pago y sistema de auditoría integral. Soporta operaciones de compras, ventas, secuencias de cheques y gestión de tarjetas de crédito/débito.

📦 Instalación

npm install @cbm-common/financial-bank-repository

⚙️ Configuración

Configuración del Módulo

El módulo debe configurarse en el módulo raíz de la aplicación:

import { CbmFinancialBankModule } from '@cbm-common/financial-bank-repository';

@NgModule({
  imports: [
    CbmFinancialBankModule.forRoot({
      baseUrl: 'https://api.cbm.com/financial-banks'
    })
  ]
})
export class AppModule {}

Configuración Standalone

Para aplicaciones standalone, configura el módulo en el bootstrap:

import { CbmFinancialBankModule } from '@cbm-common/financial-bank-repository';

bootstrapApplication(AppComponent, {
  providers: [
    CbmFinancialBankModule.forRoot({
      baseUrl: 'https://api.cbm.com/financial-banks'
    })
  ]
});

🎯 Inyección de Dependencias

Inyección del Servicio

import { CbmFinancialBankService } from '@cbm-common/financial-bank-repository';

@Component({
  selector: 'app-bank-manager',
  standalone: true,
  imports: [CbmFinancialBankService]
})
export class BankManagerComponent {
  constructor(private bankService: CbmFinancialBankService) {}
}

Inyección del Repositorio

import { CbmFinancialBankRepository } from '@cbm-common/financial-bank-repository';

@Component({
  selector: 'app-bank-list',
  standalone: true,
  imports: [CbmFinancialBankRepository]
})
export class BankListComponent {
  constructor(private bankRepo: CbmFinancialBankRepository) {}
}

🏗️ Arquitectura del Repositorio

Patrón de Diseño

El repositorio sigue el patrón Repository Pattern con Dependency Injection:

CbmFinancialBankModule
├── ICbmFinancialBankModuleConfig (configuración)
├── CbmFinancialBankService (implementa ICbmFinancialBankRepository)
├── CbmFinancialBankRepository (wrapper del service)
├── CbmFinancialBankModel (modelos de datos)
└── HttpClient (cliente HTTP)

Interfaz del Repositorio

export interface ICbmFinancialBankRepository {
  list(params: CbmFinancialBankModel.ListParams): Observable<CbmFinancialBankModel.ListResponse>;
  getOne(id: string): Observable<CbmFinancialBankModel.GetOneResponse>;
  save(data: CbmFinancialBankModel.SaveBody): Observable<CbmFinancialBankModel.ConfirmResponse>;
  update(id: string, data: CbmFinancialBankModel.UpdateBody): Observable<CbmFinancialBankModel.ConfirmResponse>;
  changeStatus(id: string, data: CbmFinancialBankModel.ChangeStatusBody): Observable<CbmFinancialBankModel.ConfirmResponse>;
  restore(id: string): Observable<CbmFinancialBankModel.ConfirmResponse>;
  delete(id: string): Observable<CbmFinancialBankModel.ConfirmResponse>;
}

📊 Operaciones Disponibles

Listado de Bancos Financieros

// Listado de bancos con filtros avanzados
list(params: {
  deleted?: boolean;        // Incluir eliminados (opcional)
  purchases?: boolean;      // Filtrar por bancos de compras
  sales?: boolean;          // Filtrar por bancos de ventas
  enabled?: boolean;        // Filtrar por estado habilitado
  name?: string;            // Filtrar por nombre del banco
  payment_term_code?: string; // Filtrar por código de plazo de pago
}): Observable<ListResponse>

Consulta Individual

// Obtener información detallada de un banco específico
getOne(id: string): Observable<GetOneResponse>

Creación de Banco Financiero

// Crear una nueva cuenta bancaria
save(data: {
  bank_id?: string;         // ID del banco (opcional)
  account_id?: string;      // ID de la cuenta (opcional)
  payment_term_id: string;  // ID del plazo de pago (requerido)
  name: string;             // Nombre de la cuenta (requerido)
  bank_name?: string;       // Nombre del banco (opcional)
  bank_cash?: string;       // Tipo de efectivo del banco (opcional)
  bank_account_type?: string; // Tipo de cuenta bancaria (opcional)
  account_number?: string;  // Número de cuenta (opcional)
  check_sequence?: number;  // Secuencia de cheques (opcional)
  sales: boolean;           // Habilitado para ventas (requerido)
  purchases: boolean;       // Habilitado para compras (requerido)
  card_number: string;      // Número de tarjeta (requerido)
}): Observable<ConfirmResponse>

Actualización de Banco Financiero

// Actualizar información de una cuenta bancaria
update(id: string, data: {
  bank_id?: string;         // ID del banco
  account_id?: string;      // ID de la cuenta
  payment_term_id?: string; // ID del plazo de pago
  name?: string;            // Nombre de la cuenta
  bank_name?: string;       // Nombre del banco
  bank_cash?: string;       // Tipo de efectivo del banco
  bank_account_type?: string; // Tipo de cuenta bancaria
  account_number?: string;  // Número de cuenta
  check_sequence?: number;  // Secuencia de cheques
  sales?: boolean;          // Habilitado para ventas
  purchases?: boolean;      // Habilitado para compras
  card_number?: string;     // Número de tarjeta
}): Observable<ConfirmResponse>

Cambio de Estado

// Cambiar el estado de una cuenta bancaria
changeStatus(id: string, data: {
  enabled: boolean;         // Nuevo estado (true/false)
  disabled_reason?: string; // Razón de deshabilitación (opcional)
}): Observable<ConfirmResponse>

Restauración de Banco

// Restaurar un banco previamente eliminado
restore(id: string): Observable<ConfirmResponse>

Eliminación de Banco

// Eliminar permanentemente un banco
delete(id: string): Observable<ConfirmResponse>

📋 Modelos de Datos

ListResponse.Data

Información completa de una cuenta bancaria con auditoría:

interface Data {
  _id: string;
  company_id: string;           // ID de la compañía
  bank_id: string;              // ID del banco
  account_id: string;           // ID de la cuenta
  payment_term_id: string;      // ID del plazo de pago
  name: string;                 // Nombre de la cuenta
  bank_name: string;            // Nombre del banco
  bank_account_type: string;    // Tipo de cuenta bancaria
  bank_cash: string;            // Tipo de efectivo del banco
  account_number: string;       // Número de cuenta
  check_sequence: number;       // Secuencia de cheques
  sales: boolean;               // Habilitado para ventas
  purchases: boolean;           // Habilitado para compras
  company_NIF: string;          // NIF de la compañía
  company_address: string;      // Dirección de la compañía
  company_trade_name: string;   // Nombre comercial
  company_business_name: string; // Razón social
  enabled: boolean;             // Estado de la cuenta
  deleted: boolean;             // Estado de eliminación
  created_user: string;         // Usuario que creó
  created_at: number;           // Timestamp de creación
  updated_at: number;           // Timestamp de actualización
  updated_user: string;         // Usuario que actualizó
  deleted_at: number;           // Timestamp de eliminación
  deleted_user: string;         // Usuario que eliminó
  payment_term_code: string;    // Código del plazo de pago
  card_number: string;          // Número de tarjeta
  detail_account: Data.DetailAccount[]; // Cuentas contables detalladas
  payment_term: Data.PaymentTerm; // Información del plazo de pago
  type: string;                 // Tipo de cuenta
}

Información de Plazo de Pago

interface PaymentTerm {
  name: string;  // Nombre del plazo de pago
  code: string;  // Código del plazo de pago
}

Cuentas Contables Detalladas

interface DetailAccount {
  _id: string;   // ID de la cuenta contable
  code: string;  // Código de la cuenta
  name: string;  // Nombre de la cuenta
}

🚀 Ejemplos de Uso

Ejemplo Básico: Gestión de Cuentas Bancarias

import { CbmFinancialBankService } from '@cbm-common/financial-bank-repository';
import { CbmFinancialBankModel } from '@cbm-common/financial-bank-repository';

@Component({
  selector: 'app-bank-management',
  standalone: true,
  template: `
    <div class="bank-management">
      <h2>Gestión de Cuentas Bancarias</h2>

      <!-- Formulario de creación/edición -->
      <div class="form-section">
        <h3>{{ isEditing ? 'Editar' : 'Crear' }} Cuenta Bancaria</h3>
        <form (ngSubmit)="onSubmit()" #bankForm="ngForm">
          <div class="form-group">
            <label for="name">Nombre de la Cuenta:</label>
            <input
              id="name"
              type="text"
              [(ngModel)]="bankFormData.name"
              name="name"
              required
              placeholder="Ej: Cuenta Principal">
          </div>

          <div class="form-group">
            <label for="bankName">Nombre del Banco:</label>
            <input
              id="bankName"
              type="text"
              [(ngModel)]="bankFormData.bank_name"
              name="bankName"
              placeholder="Ej: Banco Pichincha">
          </div>

          <div class="form-group">
            <label for="accountNumber">Número de Cuenta:</label>
            <input
              id="accountNumber"
              type="text"
              [(ngModel)]="bankFormData.account_number"
              name="accountNumber"
              placeholder="Ej: 1234567890">
          </div>

          <div class="form-group">
            <label for="cardNumber">Número de Tarjeta:</label>
            <input
              id="cardNumber"
              type="text"
              [(ngModel)]="bankFormData.card_number"
              name="cardNumber"
              required
              placeholder="Ej: **** **** **** 1234">
          </div>

          <div class="form-row">
            <div class="form-group">
              <label for="sales">Habilitado para Ventas:</label>
              <input
                id="sales"
                type="checkbox"
                [(ngModel)]="bankFormData.sales"
                name="sales">
            </div>

            <div class="form-group">
              <label for="purchases">Habilitado para Compras:</label>
              <input
                id="purchases"
                type="checkbox"
                [(ngModel)]="bankFormData.purchases"
                name="purchases">
            </div>
          </div>

          <div class="form-actions">
            <button type="submit" [disabled]="!bankForm.valid">
              {{ isEditing ? 'Actualizar' : 'Crear' }} Cuenta
            </button>
            <button type="button" (click)="cancelEdit()">Cancelar</button>
          </div>
        </form>
      </div>

      <!-- Tabla de cuentas bancarias -->
      <div class="table-section">
        <h3>Cuentas Bancarias Configuradas</h3>

        <!-- Filtros -->
        <div class="filters">
          <input
            [(ngModel)]="filters.name"
            placeholder="Buscar por nombre..."
            (input)="applyFilters()">
          <select [(ngModel)]="filters.enabled" (change)="applyFilters()">
            <option value="">Todos los estados</option>
            <option value="true">Habilitadas</option>
            <option value="false">Deshabilitadas</option>
          </select>
          <select [(ngModel)]="filters.sales" (change)="applyFilters()">
            <option value="">Todas las cuentas</option>
            <option value="true">Solo Ventas</option>
            <option value="false">Solo Compras</option>
          </select>
        </div>

        <!-- Tabla -->
        <div class="table-container">
          <table>
            <thead>
              <tr>
                <th>Nombre</th>
                <th>Banco</th>
                <th>Número de Cuenta</th>
                <th>Ventas</th>
                <th>Compras</th>
                <th>Estado</th>
                <th>Acciones</th>
              </tr>
            </thead>
            <tbody>
              <tr *ngFor="let bank of filteredBanks">
                <td>{{ bank.name }}</td>
                <td>{{ bank.bank_name || 'N/A' }}</td>
                <td>{{ bank.account_number || 'N/A' }}</td>
                <td>
                  <span class="indicator" [class.active]="bank.sales">
                    {{ bank.sales ? '✓' : '✗' }}
                  </span>
                </td>
                <td>
                  <span class="indicator" [class.active]="bank.purchases">
                    {{ bank.purchases ? '✓' : '✗' }}
                  </span>
                </td>
                <td>
                  <span class="status" [class.enabled]="bank.enabled" [class.disabled]="!bank.enabled">
                    {{ bank.enabled ? 'Habilitada' : 'Deshabilitada' }}
                  </span>
                </td>
                <td>
                  <button (click)="editBank(bank)">Editar</button>
                  <button (click)="toggleStatus(bank)" [disabled]="!bank.enabled">
                    {{ bank.enabled ? 'Deshabilitar' : 'Habilitar' }}
                  </button>
                  <button (click)="viewDetails(bank)">Ver Detalles</button>
                  <button (click)="deleteBank(bank)" class="danger" *ngIf="bank.deleted">
                    Eliminar
                  </button>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>

      <!-- Modal de detalles -->
      <div class="modal" *ngIf="selectedBank" (click)="closeModal()">
        <div class="modal-content" (click)="$event.stopPropagation()">
          <h3>Detalles de la Cuenta Bancaria</h3>
          <div class="bank-details">
            <div class="detail-row">
              <label>ID:</label>
              <span>{{ selectedBank._id }}</span>
            </div>
            <div class="detail-row">
              <label>Nombre:</label>
              <span>{{ selectedBank.name }}</span>
            </div>
            <div class="detail-row">
              <label>Banco:</label>
              <span>{{ selectedBank.bank_name || 'No especificado' }}</span>
            </div>
            <div class="detail-row">
              <label>Número de Cuenta:</label>
              <span>{{ selectedBank.account_number || 'No especificado' }}</span>
            </div>
            <div class="detail-row">
              <label>Tipo de Cuenta:</label>
              <span>{{ selectedBank.bank_account_type || 'No especificado' }}</span>
            </div>
            <div class="detail-row">
              <label>Secuencia de Cheques:</label>
              <span>{{ selectedBank.check_sequence || 'N/A' }}</span>
            </div>
            <div class="detail-row">
              <label>Número de Tarjeta:</label>
              <span>{{ selectedBank.card_number }}</span>
            </div>
            <div class="detail-row">
              <label>Plazo de Pago:</label>
              <span>{{ selectedBank.payment_term?.name }} ({{ selectedBank.payment_term?.code }})</span>
            </div>
            <div class="detail-row">
              <label>Estado:</label>
              <span class="status" [class.enabled]="selectedBank.enabled">
                {{ selectedBank.enabled ? 'Habilitada' : 'Deshabilitada' }}
              </span>
            </div>
            <div class="detail-row">
              <label>Creado por:</label>
              <span>{{ selectedBank.created_user }} ({{ formatDate(selectedBank.created_at) }})</span>
            </div>
            <div class="detail-row" *ngIf="selectedBank.updated_user">
              <label>Actualizado por:</label>
              <span>{{ selectedBank.updated_user }} ({{ formatDate(selectedBank.updated_at) }})</span>
            </div>
          </div>
          <div class="modal-actions">
            <button (click)="closeModal()">Cerrar</button>
          </div>
        </div>
      </div>
    </div>
  `,
  styles: [`
    .bank-management { padding: 20px; }
    .form-section, .table-section { margin-bottom: 30px; }
    .form-group { margin-bottom: 15px; }
    .form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
    .form-group input { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
    .form-row { display: flex; gap: 20px; }
    .form-row .form-group { flex: 1; }
    .form-actions { display: flex; gap: 10px; margin-top: 20px; }
    .filters { display: flex; gap: 10px; margin-bottom: 20px; }
    .table-container { overflow-x: auto; }
    table { width: 100%; border-collapse: collapse; }
    th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
    th { background-color: #f8f9fa; font-weight: bold; }
    .status.enabled { color: #28a745; font-weight: bold; }
    .status.disabled { color: #dc3545; font-weight: bold; }
    .indicator.active { color: #28a745; font-weight: bold; }
    .indicator:not(.active) { color: #dc3545; }
    button { padding: 6px 12px; border: none; border-radius: 4px; cursor: pointer; }
    button:hover { opacity: 0.8; }
    button:disabled { opacity: 0.5; cursor: not-allowed; }
    button.danger { background-color: #dc3545; color: white; }

    /* Modal styles */
    .modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 1000; }
    .modal-content { background: white; padding: 20px; border-radius: 8px; max-width: 600px; width: 90%; max-height: 80%; overflow-y: auto; }
    .bank-details { margin: 20px 0; }
    .detail-row { display: flex; margin-bottom: 10px; align-items: center; }
    .detail-row label { font-weight: bold; min-width: 140px; margin-right: 10px; }
    .detail-row span { flex: 1; }
    .modal-actions { display: flex; justify-content: flex-end; margin-top: 20px; }
  `]
})
export class BankManagementComponent implements OnInit {
  banks: CbmFinancialBankModel.ListResponse.Data[] = [];
  filteredBanks: CbmFinancialBankModel.ListResponse.Data[] = [];
  selectedBank: CbmFinancialBankModel.ListResponse.Data | null = null;
  isEditing = false;
  currentBankId = '';

  filters: Partial<CbmFinancialBankModel.ListParams> = {
    name: '',
    enabled: undefined,
    sales: undefined
  };

  bankFormData: Partial<CbmFinancialBankModel.SaveBody> = {
    name: '',
    sales: false,
    purchases: false,
    card_number: '',
    payment_term_id: ''
  };

  constructor(private bankService: CbmFinancialBankService) {}

  ngOnInit() {
    this.loadBanks();
  }

  loadBanks() {
    this.bankService.list({}).subscribe({
      next: (response) => {
        if (response.success) {
          this.banks = response.data || [];
          this.applyFilters();
        }
      },
      error: (error) => {
        console.error('Error al cargar bancos:', error);
      }
    });
  }

  applyFilters() {
    let filtered = [...this.banks];

    if (this.filters.name) {
      filtered = filtered.filter(bank =>
        bank.name.toLowerCase().includes(this.filters.name!.toLowerCase()) ||
        (bank.bank_name && bank.bank_name.toLowerCase().includes(this.filters.name!.toLowerCase()))
      );
    }

    if (this.filters.enabled !== undefined) {
      filtered = filtered.filter(bank => bank.enabled === (this.filters.enabled === 'true'));
    }

    if (this.filters.sales !== undefined) {
      filtered = filtered.filter(bank => bank.sales === (this.filters.sales === 'true'));
    }

    this.filteredBanks = filtered;
  }

  onSubmit() {
    if (this.isEditing) {
      this.updateBank();
    } else {
      this.createBank();
    }
  }

  createBank() {
    this.bankService.save(this.bankFormData as CbmFinancialBankModel.SaveBody).subscribe({
      next: (response) => {
        if (response.success) {
          this.resetForm();
          this.loadBanks();
          alert('Cuenta bancaria creada exitosamente');
        }
      },
      error: (error) => {
        console.error('Error al crear cuenta bancaria:', error);
      }
    });
  }

  updateBank() {
    this.bankService.update(this.currentBankId, this.bankFormData as CbmFinancialBankModel.UpdateBody).subscribe({
      next: (response) => {
        if (response.success) {
          this.resetForm();
          this.loadBanks();
          alert('Cuenta bancaria actualizada exitosamente');
        }
      },
      error: (error) => {
        console.error('Error al actualizar cuenta bancaria:', error);
      }
    });
  }

  editBank(bank: CbmFinancialBankModel.ListResponse.Data) {
    this.isEditing = true;
    this.currentBankId = bank._id;
    this.bankFormData = {
      name: bank.name,
      bank_name: bank.bank_name,
      account_number: bank.account_number,
      card_number: bank.card_number,
      sales: bank.sales,
      purchases: bank.purchases,
      payment_term_id: bank.payment_term_id
    };
  }

  toggleStatus(bank: CbmFinancialBankModel.ListResponse.Data) {
    const newStatus = !bank.enabled;
    const reason = newStatus ? '' : prompt('Razón de deshabilitación:') || '';

    this.bankService.changeStatus(bank._id, {
      enabled: newStatus,
      disabled_reason: reason
    }).subscribe({
      next: (response) => {
        if (response.success) {
          this.loadBanks();
          alert(`Cuenta bancaria ${newStatus ? 'habilitada' : 'deshabilitada'} exitosamente`);
        }
      },
      error: (error) => {
        console.error('Error al cambiar estado:', error);
      }
    });
  }

  deleteBank(bank: CbmFinancialBankModel.ListResponse.Data) {
    if (confirm('¿Está seguro de que desea eliminar permanentemente esta cuenta bancaria?')) {
      this.bankService.delete(bank._id).subscribe({
        next: (response) => {
          if (response.success) {
            this.loadBanks();
            alert('Cuenta bancaria eliminada exitosamente');
          }
        },
        error: (error) => {
          console.error('Error al eliminar cuenta bancaria:', error);
        }
      });
    }
  }

  viewDetails(bank: CbmFinancialBankModel.ListResponse.Data) {
    this.selectedBank = bank;
  }

  closeModal() {
    this.selectedBank = null;
  }

  cancelEdit() {
    this.resetForm();
  }

  resetForm() {
    this.isEditing = false;
    this.currentBankId = '';
    this.bankFormData = {
      name: '',
      sales: false,
      purchases: false,
      card_number: '',
      payment_term_id: ''
    };
  }

  formatDate(timestamp?: number): string {
    if (!timestamp) return 'N/A';
    return new Date(timestamp).toLocaleDateString();
  }
}

Ejemplo de Selector de Cuentas Bancarias

import { CbmFinancialBankService } from '@cbm-common/financial-bank-repository';

@Component({
  selector: 'app-bank-selector',
  standalone: true,
  template: `
    <div class="bank-selector">
      <h3>Seleccionar Cuenta Bancaria</h3>

      <div class="selector-container">
        <div class="search-section">
          <input
            [formControl]="searchControl"
            placeholder="Buscar por nombre o banco..."
            (input)="onSearchInput($event)">
          <button (click)="clearSearch()" *ngIf="searchControl.value">✕</button>
        </div>

        <div class="type-filters">
          <button
            [class.active]="activeFilter === 'all'"
            (click)="setFilter('all')">
            Todas
          </button>
          <button
            [class.active]="activeFilter === 'sales'"
            (click)="setFilter('sales')">
            Ventas
          </button>
          <button
            [class.active]="activeFilter === 'purchases'"
            (click)="setFilter('purchases')">
            Compras
          </button>
        </div>

        <div class="banks-list" *ngIf="filteredBanks.length > 0">
          <div
            *ngFor="let bank of filteredBanks"
            class="bank-option"
            [class.selected]="selectedBank?._id === bank._id"
            (click)="selectBank(bank)">
            <div class="bank-info">
              <div class="bank-main">
                <div class="bank-name">{{ bank.name }}</div>
                <div class="bank-details">
                  <span *ngIf="bank.bank_name">{{ bank.bank_name }}</span>
                  <span *ngIf="bank.account_number"> • {{ bank.account_number }}</span>
                </div>
              </div>
              <div class="bank-capabilities">
                <span class="capability" [class.enabled]="bank.sales">Ventas</span>
                <span class="capability" [class.enabled]="bank.purchases">Compras</span>
              </div>
            </div>
            <div class="bank-status" [class.enabled]="bank.enabled">
              {{ bank.enabled ? '●' : '○' }}
            </div>
          </div>
        </div>

        <div class="no-results" *ngIf="searchControl.value && filteredBanks.length === 0">
          <p>No se encontraron cuentas bancarias que coincidan con "{{ searchControl.value }}"</p>
        </div>

        <div class="loading" *ngIf="loading">
          <p>Cargando cuentas bancarias...</p>
        </div>
      </div>

      <div class="selected-bank" *ngIf="selectedBank">
        <h4>Cuenta Seleccionada:</h4>
        <div class="selected-info">
          <strong>{{ selectedBank.name }}</strong>
          <div class="bank-meta">
            <span *ngIf="selectedBank.bank_name">{{ selectedBank.bank_name }}</span>
            <span *ngIf="selectedBank.account_number"> • {{ selectedBank.account_number }}</span>
          </div>
          <div class="capabilities">
            <span class="capability" [class.enabled]="selectedBank.sales">Ventas</span>
            <span class="capability" [class.enabled]="selectedBank.purchases">Compras</span>
          </div>
        </div>
      </div>
    </div>
  `,
  styles: [`
    .bank-selector { max-width: 400px; }
    .selector-container { border: 1px solid #ddd; border-radius: 8px; overflow: hidden; }
    .search-section { padding: 15px; background: #f8f9fa; display: flex; align-items: center; }
    .search-section input { flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
    .search-section button { margin-left: 10px; padding: 8px; border: none; border-radius: 4px; cursor: pointer; }

    .type-filters { padding: 10px 15px; background: #f8f9fa; border-bottom: 1px solid #eee; display: flex; gap: 5px; }
    .type-filters button { padding: 6px 12px; border: 1px solid #ddd; background: white; border-radius: 4px; cursor: pointer; }
    .type-filters button.active { background: #007bff; color: white; border-color: #007bff; }

    .banks-list { max-height: 300px; overflow-y: auto; }
    .bank-option { display: flex; justify-content: space-between; align-items: center; padding: 12px 15px; cursor: pointer; border-bottom: 1px solid #eee; }
    .bank-option:hover { background: #f8f9fa; }
    .bank-option.selected { background: #e3f2fd; }
    .bank-info { flex: 1; }
    .bank-main { margin-bottom: 5px; }
    .bank-name { font-weight: bold; color: #1976d2; }
    .bank-details { font-size: 12px; color: #666; margin-top: 2px; }
    .bank-capabilities { display: flex; gap: 8px; }
    .capability.enabled { background: #28a745; color: white; padding: 2px 6px; border-radius: 10px; font-size: 10px; }
    .capability:not(.enabled) { background: #dc3545; color: white; padding: 2px 6px; border-radius: 10px; font-size: 10px; }
    .bank-status.enabled { color: #28a745; font-size: 18px; }
    .bank-status:not(.enabled) { color: #dc3545; font-size: 18px; }

    .no-results { padding: 20px; text-align: center; color: #666; }
    .loading { padding: 20px; text-align: center; color: #666; }
    .selected-bank { margin-top: 15px; padding: 10px; background: #e8f5e8; border-radius: 4px; }
    .selected-info { margin-top: 5px; }
    .bank-meta { font-size: 12px; color: #666; margin-top: 2px; }
    .capabilities { margin-top: 8px; }
  `]
})
export class BankSelectorComponent implements OnInit {
  searchControl = new FormControl('');
  allBanks: CbmFinancialBankModel.ListResponse.Data[] = [];
  filteredBanks: CbmFinancialBankModel.ListResponse.Data[] = [];
  selectedBank: CbmFinancialBankModel.ListResponse.Data | null = null;
  loading = false;
  activeFilter: 'all' | 'sales' | 'purchases' = 'all';

  constructor(private bankService: CbmFinancialBankService) {}

  ngOnInit() {
    this.loadAllBanks();
  }

  loadAllBanks() {
    this.loading = true;
    this.bankService.list({ enabled: true }).subscribe({
      next: (response) => {
        if (response.success) {
          this.allBanks = response.data || [];
          this.applyFilters();
        }
        this.loading = false;
      },
      error: (error) => {
        console.error('Error al cargar bancos:', error);
        this.loading = false;
      }
    });
  }

  onSearchInput(event: any) {
    const searchTerm = event.target.value.toLowerCase();
    this.filterBanks(searchTerm);
  }

  setFilter(filter: 'all' | 'sales' | 'purchases') {
    this.activeFilter = filter;
    this.applyFilters();
  }

  filterBanks(searchTerm: string) {
    let filtered = [...this.allBanks];

    // Aplicar filtro de búsqueda
    if (searchTerm) {
      filtered = filtered.filter(bank =>
        bank.name.toLowerCase().includes(searchTerm) ||
        (bank.bank_name && bank.bank_name.toLowerCase().includes(searchTerm))
      );
    }

    // Aplicar filtro de tipo
    if (this.activeFilter === 'sales') {
      filtered = filtered.filter(bank => bank.sales);
    } else if (this.activeFilter === 'purchases') {
      filtered = filtered.filter(bank => bank.purchases);
    }

    this.filteredBanks = filtered;
  }

  applyFilters() {
    this.filterBanks(this.searchControl.value || '');
  }

  selectBank(bank: CbmFinancialBankModel.ListResponse.Data) {
    this.selectedBank = bank;
    // Emitir evento de selección
    console.log('Cuenta bancaria seleccionada:', bank);
  }

  clearSearch() {
    this.searchControl.setValue('');
    this.applyFilters();
  }
}

Ejemplo de Integración con Sistema de Pagos

import { CbmFinancialBankService } from '@cbm-common/financial-bank-repository';

@Component({
  selector: 'app-payment-processor',
  standalone: true,
  template: `
    <div class="payment-processor">
      <h3>Procesador de Pagos</h3>

      <div class="payment-form">
        <div class="form-group">
          <label for="amount">Monto a Pagar:</label>
          <input
            id="amount"
            type="number"
            [(ngModel)]="paymentAmount"
            step="0.01"
            min="0.01"
            placeholder="0.00">
        </div>

        <div class="form-group">
          <label for="paymentType">Tipo de Pago:</label>
          <select id="paymentType" [(ngModel)]="paymentType">
            <option value="transfer">Transferencia Bancaria</option>
            <option value="check">Cheque</option>
            <option value="card">Tarjeta de Crédito/Débito</option>
          </select>
        </div>

        <div class="form-group" *ngIf="paymentType === 'transfer' || paymentType === 'check'">
          <label for="bankAccount">Cuenta Bancaria:</label>
          <select id="bankAccount" [(ngModel)]="selectedBankId" (change)="onBankChange()">
            <option value="">Seleccionar cuenta...</option>
            <option *ngFor="let bank of availableBanks" [value]="bank._id">
              {{ bank.name }} - {{ bank.bank_name }}
            </option>
          </select>
        </div>

        <div class="bank-details" *ngIf="selectedBank && (paymentType === 'transfer' || paymentType === 'check')">
          <div class="detail-card">
            <h4>Información de la Cuenta Seleccionada</h4>
            <div class="detail-grid">
              <div class="detail-item">
                <label>Banco:</label>
                <span>{{ selectedBank.bank_name }}</span>
              </div>
              <div class="detail-item">
                <label>Número de Cuenta:</label>
                <span>{{ selectedBank.account_number }}</span>
              </div>
              <div class="detail-item">
                <label>Tipo de Cuenta:</label>
                <span>{{ selectedBank.bank_account_type }}</span>
              </div>
              <div class="detail-item" *ngIf="paymentType === 'check'">
                <label>Próximo Cheque:</label>
                <span>#{{ selectedBank.check_sequence || 'N/A' }}</span>
              </div>
              <div class="detail-item">
                <label>Número de Tarjeta:</label>
                <span>{{ selectedBank.card_number }}</span>
              </div>
            </div>
          </div>
        </div>

        <div class="payment-actions">
          <button (click)="processPayment()" [disabled]="!canProcessPayment()">
            Procesar Pago
          </button>
          <button (click)="resetForm()">Limpiar</button>
        </div>
      </div>

      <div class="payment-history" *ngIf="paymentHistory.length > 0">
        <h4>Historial de Pagos Recientes</h4>
        <div class="history-list">
          <div *ngFor="let payment of paymentHistory" class="history-item">
            <div class="payment-info">
              <span class="amount">${{ payment.amount }}</span>
              <span class="type">{{ payment.type }}</span>
              <span class="date">{{ formatDate(payment.date) }}</span>
            </div>
            <div class="payment-status" [class.success]="payment.success">
              {{ payment.success ? 'Exitoso' : 'Fallido' }}
            </div>
          </div>
        </div>
      </div>
    </div>
  `,
  styles: [`
    .payment-processor { padding: 20px; max-width: 600px; }
    .payment-form { background: #f8f9fa; padding: 20px; border-radius: 8px; margin-bottom: 20px; }
    .form-group { margin-bottom: 15px; }
    .form-group label { display: block; margin-bottom: 5px; font-weight: bold; }
    .form-group input, .form-group select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 4px; }

    .bank-details { margin-top: 20px; }
    .detail-card { background: white; padding: 15px; border-radius: 8px; border: 1px solid #ddd; }
    .detail-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin-top: 10px; }
    .detail-item { display: flex; flex-direction: column; }
    .detail-item label { font-size: 12px; color: #666; margin-bottom: 2px; }
    .detail-item span { font-weight: bold; }

    .payment-actions { display: flex; gap: 10px; margin-top: 20px; }
    button { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
    button:hover { opacity: 0.8; }
    button:disabled { opacity: 0.5; cursor: not-allowed; }

    .payment-history { background: white; padding: 15px; border-radius: 8px; border: 1px solid #ddd; }
    .history-list { margin-top: 10px; }
    .history-item { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid #eee; }
    .history-item:last-child { border-bottom: none; }
    .payment-info { display: flex; flex-direction: column; }
    .amount { font-weight: bold; font-size: 16px; }
    .type { font-size: 12px; color: #666; }
    .date { font-size: 11px; color: #999; }
    .payment-status.success { color: #28a745; font-weight: bold; }
    .payment-status:not(.success) { color: #dc3545; font-weight: bold; }
  `]
})
export class PaymentProcessorComponent implements OnInit {
  availableBanks: CbmFinancialBankModel.ListResponse.Data[] = [];
  selectedBank: CbmFinancialBankModel.ListResponse.Data | null = null;
  selectedBankId = '';
  paymentAmount = 0;
  paymentType: 'transfer' | 'check' | 'card' = 'transfer';
  paymentHistory: any[] = [];

  constructor(private bankService: CbmFinancialBankService) {}

  ngOnInit() {
    this.loadAvailableBanks();
  }

  loadAvailableBanks() {
    // Cargar bancos habilitados para el tipo de pago seleccionado
    const params: Partial<CbmFinancialBankModel.ListParams> = { enabled: true };

    if (this.paymentType === 'transfer' || this.paymentType === 'check') {
      // Para transferencias y cheques, necesitamos bancos con cuentas
      params.purchases = true; // O sales dependiendo del contexto
    }

    this.bankService.list(params).subscribe({
      next: (response) => {
        if (response.success) {
          this.availableBanks = response.data || [];
        }
      },
      error: (error) => {
        console.error('Error al cargar bancos:', error);
      }
    });
  }

  onBankChange() {
    this.selectedBank = this.availableBanks.find(b => b._id === this.selectedBankId) || null;
  }

  canProcessPayment(): boolean {
    if (this.paymentAmount <= 0) return false;

    if (this.paymentType === 'transfer' || this.paymentType === 'check') {
      return !!this.selectedBank;
    }

    return true;
  }

  processPayment() {
    if (!this.canProcessPayment()) return;

    const paymentData = {
      amount: this.paymentAmount,
      type: this.paymentType,
      bank: this.selectedBank,
      date: new Date(),
      success: Math.random() > 0.1 // Simulación de éxito
    };

    // Aquí iría la lógica real de procesamiento de pago
    console.log('Procesando pago:', paymentData);

    // Agregar al historial
    this.paymentHistory.unshift(paymentData);

    // Limpiar formulario si fue exitoso
    if (paymentData.success) {
      this.resetForm();
      alert('Pago procesado exitosamente');
    } else {
      alert('Error al procesar el pago');
    }
  }

  resetForm() {
    this.paymentAmount = 0;
    this.selectedBankId = '';
    this.selectedBank = null;
    this.paymentType = 'transfer';
  }

  formatDate(date: Date): string {
    return date.toLocaleDateString();
  }
}

⚠️ Manejo de Errores

Validación de Cuentas Bancarias

// Servicio de validación para cuentas bancarias
@Injectable({ providedIn: 'root' })
export class BankValidator {
  validateBankData(bankData: Partial<CbmFinancialBankModel.SaveBody>): ValidationResult {
    const result: ValidationResult = { isValid: true, errors: [] };

    // Validar nombre
    if (!bankData.name || bankData.name.trim().length < 3) {
      result.errors.push('El nombre de la cuenta debe tener al menos 3 caracteres');
    }

    // Validar número de tarjeta
    if (!bankData.card_number || bankData.card_number.length < 4) {
      result.errors.push('El número de tarjeta es requerido y debe tener al menos 4 caracteres');
    }

    // Validar que al menos una opción esté habilitada
    if (!bankData.sales && !bankData.purchases) {
      result.errors.push('La cuenta debe estar habilitada para ventas o compras');
    }

    // Validar secuencia de cheques
    if (bankData.check_sequence && bankData.check_sequence < 1) {
      result.errors.push('La secuencia de cheques debe ser mayor a 0');
    }

    result.isValid = result.errors.length === 0;
    return result;
  }

  validateBankForPayment(bank: CbmFinancialBankModel.ListResponse.Data, paymentType: string): ValidationResult {
    const result: ValidationResult = { isValid: true, errors: [] };

    // Validar que el banco esté habilitado
    if (!bank.enabled) {
      result.errors.push('La cuenta bancaria no está habilitada');
    }

    // Validar tipo de pago
    if (paymentType === 'check' && !bank.check_sequence) {
      result.errors.push('La cuenta no tiene secuencia de cheques configurada');
    }

    if ((paymentType === 'transfer' || paymentType === 'check') && !bank.account_number) {
      result.errors.push('La cuenta no tiene número de cuenta configurado');
    }

    result.isValid = result.errors.length === 0;
    return result;
  }
}

// Uso en componentes
export class BankFormComponent {
  constructor(private validator: BankValidator) {}

  validateBank() {
    const validation = this.validator.validateBankData(this.bankForm.value);
    if (!validation.isValid) {
      this.showValidationErrors(validation.errors);
    }
  }
}

Manejo de Errores en Operaciones

// Manejo de errores específico para operaciones bancarias
loadBanksWithErrorHandling(params: CbmFinancialBankModel.ListParams) {
  this.bankService.list(params).subscribe({
    next: (response) => {
      if (response.success) {
        this.banks = response.data;
        this.showSuccessMessage(`Se encontraron ${response.data.length} cuentas bancarias`);
      } else {
        this.showErrorMessage('Error en la respuesta del servidor');
      }
    },
    error: (error) => {
      console.error('Error HTTP:', error);

      if (error.status === 400) {
        this.showErrorMessage('Parámetros de búsqueda inválidos. Verifique los filtros.');
      } else if (error.status === 401) {
        this.showErrorMessage('Sesión expirada. Inicie sesión nuevamente.');
      } else if (error.status === 403) {
        this.showErrorMessage('No tiene permisos para consultar cuentas bancarias.');
      } else if (error.status === 404) {
        this.showErrorMessage('No se encontraron cuentas bancarias.');
      } else if (error.status === 500) {
        this.showErrorMessage('Error interno del servidor. Intente nuevamente.');
      } else {
        this.showErrorMessage('Error de conexión. Verifique su conexión a internet.');
      }
    }
  });
}

🔧 Configuración Avanzada

Servicio de Caché para Cuentas Bancarias

// Servicio de caché para cuentas bancarias con invalidación automática
@Injectable({ providedIn: 'root' })
export class BankCacheService {
  private cache = new Map<string, { data: any; timestamp: number }>();
  private readonly CACHE_DURATION = 10 * 60 * 1000; // 10 minutos

  get(key: string): any | null {
    const cached = this.cache.get(key);
    if (cached && Date.now() - cached.timestamp < this.CACHE_DURATION) {
      return cached.data;
    }
    this.cache.delete(key);
    return null;
  }

  set(key: string, data: any) {
    this.cache.set(key, { data, timestamp: Date.now() });
  }

  invalidateBankCache() {
    // Invalidar caché cuando se modifica una cuenta bancaria
    this.cache.clear();
  }

  getEnabledBanks(): Observable<CbmFinancialBankModel.ListResponse> {
    const cacheKey = 'enabled_banks';
    const cached = this.get(cacheKey);

    if (cached) {
      return of(cached);
    }

    return this.bankService.list({ enabled: true }).pipe(
      tap(response => {
        if (response.success) {
          this.set(cacheKey, response);
        }
      })
    );
  }

  constructor(private bankService: CbmFinancialBankService) {}
}

Servicio de Auditoría para Operaciones Bancarias

// Servicio de auditoría para operaciones de cuentas bancarias
@Injectable({ providedIn: 'root' })
export class BankAuditService {
  logBankOperation(
    operation: string,
    bankData: Partial<CbmFinancialBankModel.ListResponse.Data>,
    userId: string,
    additionalData?: any
  ) {
    const auditEntry = {
      timestamp: new Date().toISOString(),
      operation: `BANK_${operation.toUpperCase()}`,
      bankId: bankData._id,
      bankName: bankData.name,
      accountNumber: bankData.account_number,
      userId,
      companyId: bankData.company_id,
      additionalData,
      severity: this.getOperationSeverity(operation)
    };

    console.log(`[BANK_AUDIT] ${JSON.stringify(auditEntry)}`);

    // Enviar a servicio de auditoría
    this.auditService.record('bank_operation', auditEntry);
  }

  logBankStatusChange(
    bankId: string,
    oldStatus: boolean,
    newStatus: boolean,
    reason: string,
    userId: string
  ) {
    const auditEntry = {
      timestamp: new Date().toISOString(),
      operation: 'BANK_STATUS_CHANGE',
      bankId,
      oldStatus,
      newStatus,
      reason,
      userId,
      severity: newStatus ? 'INFO' : 'WARNING'
    };

    console.log(`[BANK_STATUS_AUDIT] ${JSON.stringify(auditEntry)}`);

    // Alertar sobre cambios críticos
    if (!newStatus && reason.includes('fraude')) {
      this.alertService.notifyBankSecurityIssue(auditEntry);
    }
  }

  private getOperationSeverity(operation: string): string {
    const severityMap: { [key: string]: string } = {
      'create': 'INFO',
      'update': 'INFO',
      'delete': 'WARNING',
      'status_change': 'WARNING'
    };
    return severityMap[operation] || 'INFO';
  }

  constructor(
    private auditService: AuditService,
    private alertService: AlertService
  ) {}
}

📋 Dependencias

Peer Dependencies (Requeridas)

{
  "@angular/common": ">=20.1.5",
  "@angular/core": ">=20.1.5"
}

Dependencias Internas

{
  "tslib": "^2.3.0"
}

🛠️ Desarrollo

Estructura del Proyecto

financial-bank-repository/
├── src/
│   ├── lib/
│   │   ├── financial-bank.model.ts    # Modelos de datos para bancos
│   │   ├── financial-bank.module.ts   # Configuración del módulo
│   │   ├── financial-bank.service.ts  # Servicio HTTP para bancos
│   │   ├── financial-bank.repository.ts # Interfaz del repositorio
│   │   └── index.ts                   # Exportaciones públicas
│   └── public-api.ts                  # API pública
├── ng-package.json                    # Configuración empaquetado
├── package.json                       # Dependencias
└── README.md                          # Esta documentación

Construcción

# Construir la librería
ng build financial-bank-repository

# Construir en modo watch
ng build financial-bank-repository --watch

# Construir para producción
ng build financial-bank-repository --configuration production

Pruebas

# Ejecutar pruebas unitarias
ng test financial-bank-repository

# Ejecutar pruebas con coverage
ng test financial-bank-repository --code-coverage

# Pruebas end-to-end
ng e2e financial-bank-repository

🎯 Mejores Prácticas

1. Gestión de Secuencias de Cheques

// Servicio para gestión automática de secuencias de cheques
@Injectable({ providedIn: 'root' })
export class CheckSequenceManager {
  constructor(private bankService: CbmFinancialBankService) {}

  // Obtener próximo número de cheque
  getNextCheckNumber(bankId: string): Observable<number> {
    return this.bankService.getOne(bankId).pipe(
      map(response => {
        if (response.success && response.data) {
          return response.data.check_sequence || 1;
        }
        throw new Error('No se pudo obtener la secuencia de cheques');
      })
    );
  }

  // Actualizar secuencia de cheques después de usar uno
  updateCheckSequence(bankId: string, usedChecks: number = 1): Observable<any> {
    return this.bankService.getOne(bankId).pipe(
      switchMap(response => {
        if (response.success && response.data) {
          const currentSequence = response.data.check_sequence || 1;
          const newSequence = currentSequence + usedChecks;

          return this.bankService.update(bankId, {
            check_sequence: newSequence
          });
        }
        throw new Error('No se pudo actualizar la secuencia de cheques');
      })
    );
  }

  // Validar disponibilidad de cheques
  validateCheckAvailability(bankId: string, requiredChecks: number): Observable<boolean> {
    return this.getNextCheckNumber(bankId).pipe(
      map(sequence => {
        // Aquí iría lógica para validar contra inventario de cheques
        // Por simplicidad, asumimos que siempre hay cheques disponibles
        return true;
      })
    );
  }
}

2. Servicio de Integración Contable

// Servicio para integración con sistema contable
@Injectable({ providedIn: 'root' })
export class AccountingIntegrationService {
  constructor(private bankService: CbmFinancialBankService) {}

  // Sincronizar cuentas bancarias con plan contable
  syncBankAccountsWithChartOfAccounts(): Observable<any> {
    return this.bankService.list({ enabled: true }).pipe(
      switchMap(response => {
        if (response.success && response.data) {
          const syncOperations = response.data.map(bank => {
            return this.syncBankToAccounting(bank);
          });

          return forkJoin(syncOperations);
        }
        return of([]);
      })
    );
  }

  // Sincronizar cuenta bancaria individual
  private syncBankToAccounting(bank: CbmFinancialBankModel.ListResponse.Data): Observable<any> {
    const accountingEntry = {
      accountCode: this.generateAccountCode(bank),
      accountName: bank.name,
      bankName: bank.bank_name,
      accountNumber: bank.account_number,
      accountType: bank.bank_account_type,
      detailAccounts: bank.detail_account
    };

    // Aquí iría la integración con el sistema contable
    console.log('Sincronizando cuenta bancaria con contabilidad:', accountingEntry);
    return of({ success: true });
  }

  // Generar código de cuenta contable
  private generateAccountCode(bank: CbmFinancialBankModel.ListResponse.Data): string {
    // Lógica para generar códigos de cuenta contable
    // Ejemplo: 1101-001 (Banco Principal)
    const baseCode = '1101'; // Código base para bancos
    const bankSuffix = bank._id.slice(-3).toUpperCase();
    return `${baseCode}-${bankSuffix}`;
  }

  // Obtener cuentas contables relacionadas
  getRelatedAccountingAccounts(bankId: string): Observable<any[]> {
    return this.bankService.getOne(bankId).pipe(
      map(response => {
        if (response.success && response.data) {
          return response.data.detail_account || [];
        }
        return [];
      })
    );
  }
}

3. Servicio de Validación de Pagos

// Servicio para validación de pagos y transacciones
@Injectable({ providedIn: 'root' })
export class PaymentValidationService {
  constructor(private bankService: CbmFinancialBankService) {}

  // Validar capacidad de pago
  validatePaymentCapacity(
    bankId: string,
    paymentAmount: number,
    paymentType: string
  ): Observable<PaymentValidationResult> {
    return this.bankService.getOne(bankId).pipe(
      map(response => {
        if (!response.success || !response.data) {
          return { isValid: false, errors: ['Cuenta bancaria no encontrada'] };
        }

        const bank = response.data;
        const errors: string[] = [];

        // Validar estado de la cuenta
        if (!bank.enabled) {
          errors.push('La cuenta bancaria no está habilitada');
        }

        // Validar tipo de pago
        if (paymentType === 'check' && !bank.check_sequence) {
          errors.push('La cuenta no tiene secuencia de cheques configurada');
        }

        if ((paymentType === 'transfer' || paymentType === 'check') && !bank.account_number) {
          errors.push('La cuenta no tiene número de cuenta configurado');
        }

        // Validar límites de monto (simplificado)
        if (paymentAmount > 100000 && paymentType === 'check') {
          errors.push('Los montos superiores a $100,000 deben realizarse por transferencia');
        }

        return {
          isValid: errors.length === 0,
          errors,
          warnings: this.generatePaymentWarnings(bank, paymentAmount, paymentType)
        };
      })
    );
  }

  // Generar advertencias de pago
  private generatePaymentWarnings(
    bank: CbmFinancialBankModel.ListResponse.Data,
    amount: number,
    type: string
  ): string[] {
    const warnings: string[] = [];

    if (amount > 50000 && type === 'check') {
      warnings.push('Considere usar transferencia para montos superiores a $50,000');
    }

    if (type === 'check' && bank.check_sequence && bank.check_sequence > 990) {
      warnings.push('La secuencia de cheques está próxima a agotarse');
    }

    return warnings;
  }

  // Validar múltiples pagos
  validateBulkPayments(payments: PaymentRequest[]): Observable<BulkValidationResult> {
    const validations = payments.map(payment =>
      this.validatePaymentCapacity(payment.bankId, payment.amount, payment.type)
    );

    return forkJoin(validations).pipe(
      map(results => {
        const validPayments = results.filter(r => r.isValid);
        const invalidPayments = results.filter(r => !r.isValid);

        return {
          totalPayments: payments.length,
          validPayments: validPayments.length,
          invalidPayments: invalidPayments.length,
          results
        };
      })
    );
  }
}

// Interfaces auxiliares
export interface PaymentValidationResult {
  isValid: boolean;
  errors: string[];
  warnings: string[];
}

export interface BulkValidationResult {
  totalPayments: number;
  validPayments: number;
  invalidPayments: number;
  results: PaymentValidationResult[];
}

export interface PaymentRequest {
  bankId: string;
  amount: number;
  type: string;
}

🤝 Contribución

  1. Fork el repositorio
  2. Crea una rama para tu feature (git checkout -b feature/nueva-funcionalidad)
  3. Commit tus cambios (git commit -am 'Agrega nueva funcionalidad')
  4. Push a la rama (git push origin feature/nueva-funcionalidad)
  5. Abre un PullRequest

📄 Licencia

Este proyecto está bajo la Licencia MIT - ver el archivo LICENSE para más detalles.

📞 Soporte

Para soporte técnico o reportes de bugs, contacta al equipo de desarrollo de CBM:

🔄 Changelog

v0.0.1

  • ✅ Módulo de configuración con patrón forRoot simplificado
  • ✅ Servicio que implementa interfaz del repositorio directamente
  • ✅ Operaciones CRUD completas para gestión de cuentas bancarias
  • ✅ Modelo de datos completo con auditoría y integración contable
  • ✅ Soporte para secuencias de cheques y números de tarjeta
  • ✅ Integración completa con HttpClient
  • ✅ Documentación completa en español

Nota: Esta librería está optimizada para sistemas financieros que requieren gestión completa de cuentas bancarias, integración contable y procesamiento de pagos con soporte para múltiples tipos de transacciones.