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/card-settlement-repository

v0.0.1

Published

Repositorio Angular para la gestión completa de liquidaciones de tarjetas de crédito en sistemas financieros. Implementa operaciones CRUD, consultas paginadas y generación de asientos contables con integración completa al sistema CBM.

Downloads

19

Readme

Card Settlement Repository

Repositorio Angular para la gestión completa de liquidaciones de tarjetas de crédito en sistemas financieros. Implementa operaciones CRUD, consultas paginadas y generación de asientos contables con integración completa al sistema CBM.

📦 Instalación

npm install @cbm-common/card-settlement-repository

⚙️ Configuración

Configuración del Módulo

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

import { CbmCardSettlementModule } from '@cbm-common/card-settlement-repository';

@NgModule({
  imports: [
    CbmCardSettlementModule.forRoot({
      baseUrl: 'https://api.cbm.com/card-settlements'
    })
  ]
})
export class AppModule {}

Configuración Standalone

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

import { CbmCardSettlementModule } from '@cbm-common/card-settlement-repository';

bootstrapApplication(AppComponent, {
  providers: [
    CbmCardSettlementModule.forRoot({
      baseUrl: 'https://api.cbm.com/card-settlements'
    })
  ]
});

🎯 Inyección de Dependencias

Inyección del Servicio

import { CbmCardSettlementService } from '@cbm-common/card-settlement-repository';

@Component({
  selector: 'app-card-settlement-manager',
  standalone: true,
  imports: [CbmCardSettlementService]
})
export class CardSettlementManagerComponent {
  constructor(private cardSettlementService: CbmCardSettlementService) {}
}

Inyección del Repositorio

import { CbmCardSettlementRepository } from '@cbm-common/card-settlement-repository';

@Component({
  selector: 'app-card-settlement-list',
  standalone: true,
  imports: [CbmCardSettlementRepository]
})
export class CardSettlementListComponent {
  constructor(private cardSettlementRepo: CbmCardSettlementRepository) {}
}

🏗️ Arquitectura del Repositorio

Patrón de Diseño

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

CbmCardSettlementModule
├── ICbmCardSettlementModuleConfig (configuración)
├── CbmCardSettlementService (implementa ICbmCardSettlementRepository)
├── CbmCardSettlementRepository (wrapper del service)
├── CbmCardSettlementModel (modelos de datos)
└── HttpClient (cliente HTTP)

Interfaz del Repositorio

export interface ICbmCardSettlementRepository {
  list(params: CbmCardSettlementModel.ListParams): Observable<CbmCardSettlementModel.ListResponse>;
  getOne(id: string): Observable<CbmCardSettlementModel.GetOneResponse>;
  save(data: CbmCardSettlementModel.SaveBody): Observable<CbmCardSettlementModel.ConfirmResponse>;
  generateOrRegenerateSeat(id: string): Observable<CbmCardSettlementModel.ConfirmResponse>;
}

📊 Operaciones Disponibles

Listado de Liquidaciones

// Listado paginado con filtros
list(params: {
  page: number;           // Página actual
  size: number;           // Tamaño de página
  movement_number?: string; // Número de movimiento
  reverse_status?: boolean; // Estado de reversión
  document_number?: string; // Número de documento
  date_begin?: number;    // Fecha inicio (timestamp)
  date_end?: number;      // Fecha fin (timestamp)
  cost_center_id?: string; // ID de centro de costo
}): Observable<ListResponse>

Obtener Liquidación Individual

// Obtener una liquidación específica por ID
getOne(id: string): Observable<GetOneResponse>

Crear Nueva Liquidación

// Crear una nueva liquidación de tarjeta
save(data: SaveBody): Observable<ConfirmResponse>

Generar/Regenerar Asiento

// Generar o regenerar asiento contable
generateOrRegenerateSeat(id: string): Observable<ConfirmResponse>

📋 Modelos de Datos

ListResponse.Item

Información completa de una liquidación de tarjeta:

interface Item {
  _id: string;
  company_id?: string;
  company_branch_id?: string;

  // Información del centro de costo
  cost_center_id?: string;
  cost_center_name?: string;
  cost_center_code?: string;

  // Información de la liquidación
  commentary?: string;
  date?: number;
  document_nomenclature?: string;
  document_number?: string;
  retention?: boolean;

  // Montos financieros
  total_card?: number;        // Total de tarjetas
  total_retention?: number;   // Total de retenciones
  total_bank_deposit?: number; // Total depósito bancario
  bank_fee?: number;          // Comisión bancaria

  // Información de la empresa
  company_NIF?: string;
  company_address?: string;
  company_trade_name?: string;
  company_business_name?: string;

  // Información de la sucursal
  company_branch_identification_number?: string;
  company_branch_trade_name?: string;
  company_branch_address?: string;
  company_branch_email?: string;
  company_branch_cellphone?: string;
  company_branch_phone?: string;
  company_branch_logo?: string;
}

SaveBody

Datos para crear una nueva liquidación:

interface SaveBody {
  company_branch_id: string;
  cost_center_id: string;
  document_number: string;
  total_card: number;
  total_retention: number;
  total_bank_deposit: number;
  bank_fee: number;
  date: number;
  commentary?: string;
  retention?: boolean;
  // ... otros campos según la API
}

🚀 Ejemplos de Uso

Ejemplo Básico: Listado de Liquidaciones

import { CbmCardSettlementService } from '@cbm-common/card-settlement-repository';
import { CbmCardSettlementModel } from '@cbm-common/card-settlement-repository';

@Component({
  selector: 'app-card-settlement-list',
  standalone: true,
  template: `
    <div class="settlement-list">
      <h2>Liquidaciones de Tarjetas</h2>

      <div class="filters">
        <input [(ngModel)]="filters.document_number" placeholder="Número de documento">
        <input [(ngModel)]="filters.movement_number" placeholder="Número de movimiento">
        <select [(ngModel)]="filters.reverse_status">
          <option [value]="undefined">Todos los estados</option>
          <option [value]="true">Revertidos</option>
          <option [value]="false">No revertidos</option>
        </select>
        <button (click)="loadSettlements()">Buscar</button>
      </div>

      <div class="table-container">
        <table>
          <thead>
            <tr>
              <th>Número Documento</th>
              <th>Centro de Costo</th>
              <th>Total Tarjetas</th>
              <th>Fecha</th>
              <th>Estado</th>
              <th>Acciones</th>
            </tr>
          </thead>
          <tbody>
            <tr *ngFor="let settlement of settlements">
              <td>{{ settlement.document_number }}</td>
              <td>{{ settlement.cost_center_name }}</td>
              <td>{{ settlement.total_card | currency }}</td>
              <td>{{ settlement.date | date:'dd/MM/yyyy' }}</td>
              <td>
                <span [class]="'status ' + (settlement.reverse_status ? 'reversed' : 'active')">
                  {{ settlement.reverse_status ? 'Revertido' : 'Activo' }}
                </span>
              </td>
              <td>
                <button (click)="viewSettlement(settlement._id)">Ver</button>
                <button (click)="generateSeat(settlement._id)">Generar Asiento</button>
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      <div class="pagination" *ngIf="totalPages > 1">
        <button [disabled]="currentPage === 1" (click)="changePage(currentPage - 1)">
          Anterior
        </button>
        <span>Página {{ currentPage }} de {{ totalPages }}</span>
        <button [disabled]="currentPage === totalPages" (click)="changePage(currentPage + 1)">
          Siguiente
        </button>
      </div>
    </div>
  `,
  styles: [`
    .settlement-list { padding: 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; }
    .status.active { color: green; }
    .status.reversed { color: red; }
    .pagination { display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 20px; }
  `]
})
export class CardSettlementListComponent implements OnInit {
  settlements: CbmCardSettlementModel.ListResponse.Item[] = [];
  currentPage = 1;
  pageSize = 10;
  totalPages = 0;

  filters: Partial<CbmCardSettlementModel.ListParams> = {
    document_number: '',
    movement_number: '',
    reverse_status: undefined
  };

  constructor(private cardSettlementService: CbmCardSettlementService) {}

  ngOnInit() {
    this.loadSettlements();
  }

  loadSettlements() {
    const params: CbmCardSettlementModel.ListParams = {
      page: this.currentPage,
      size: this.pageSize,
      ...this.filters
    };

    this.cardSettlementService.list(params).subscribe({
      next: (response) => {
        this.settlements = response.items;
        this.totalPages = response.pages;
      },
      error: (error) => {
        console.error('Error al cargar liquidaciones:', error);
      }
    });
  }

  changePage(page: number) {
    this.currentPage = page;
    this.loadSettlements();
  }

  viewSettlement(id: string) {
    this.cardSettlementService.getOne(id).subscribe({
      next: (settlement) => {
        console.log('Liquidación:', settlement);
        // Mostrar modal o navegar a detalle
      }
    });
  }

  generateSeat(id: string) {
    this.cardSettlementService.generateOrRegenerateSeat(id).subscribe({
      next: (response) => {
        console.log('Asiento generado:', response);
        // Mostrar mensaje de éxito
      },
      error: (error) => {
        console.error('Error al generar asiento:', error);
      }
    });
  }
}

Ejemplo con Formulario de Creación

import { ReactiveFormsModule } from '@angular/forms';
import { CbmCardSettlementService } from '@cbm-common/card-settlement-repository';

@Component({
  selector: 'app-card-settlement-form',
  standalone: true,
  imports: [ReactiveFormsModule, CbmCardSettlementService],
  template: `
    <form [formGroup]="settlementForm" (ngSubmit)="onSubmit()">
      <h2>Crear Nueva Liquidación de Tarjeta</h2>

      <div class="form-group">
        <label for="documentNumber">Número de Documento</label>
        <input id="documentNumber" type="text" formControlName="document_number">
      </div>

      <div class="form-group">
        <label for="costCenter">Centro de Costo</label>
        <select id="costCenter" formControlName="cost_center_id">
          <option *ngFor="let costCenter of costCenters" [value]="costCenter.id">
            {{ costCenter.name }}
          </option>
        </select>
      </div>

      <div class="form-group">
        <label for="totalCard">Total Tarjetas</label>
        <input id="totalCard" type="number" formControlName="total_card" step="0.01">
      </div>

      <div class="form-group">
        <label for="totalRetention">Total Retenciones</label>
        <input id="totalRetention" type="number" formControlName="total_retention" step="0.01">
      </div>

      <div class="form-group">
        <label for="totalBankDeposit">Total Depósito Bancario</label>
        <input id="totalBankDeposit" type="number" formControlName="total_bank_deposit" step="0.01">
      </div>

      <div class="form-group">
        <label for="bankFee">Comisión Bancaria</label>
        <input id="bankFee" type="number" formControlName="bank_fee" step="0.01">
      </div>

      <div class="form-group">
        <label for="settlementDate">Fecha de Liquidación</label>
        <input id="settlementDate" type="date" formControlName="date">
      </div>

      <div class="form-group">
        <label for="commentary">Comentario</label>
        <textarea id="commentary" formControlName="commentary" rows="3"></textarea>
      </div>

      <div class="form-group">
        <label>
          <input type="checkbox" formControlName="retention">
          Aplicar retención
        </label>
      </div>

      <div class="form-actions">
        <button type="button" (click)="onCancel()">Cancelar</button>
        <button type="submit" [disabled]="settlementForm.invalid">Crear Liquidación</button>
      </div>
    </form>
  `,
  styles: [`
    form { max-width: 500px; margin: 0 auto; }
    .form-group { margin-bottom: 15px; }
    label { display: block; margin-bottom: 5px; font-weight: bold; }
    input, select, textarea { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; }
    .form-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }
    button { padding: 10px 20px; border: none; border-radius: 4px; cursor: pointer; }
    button[type="submit"] { background: #007bff; color: white; }
    button[type="button"] { background: #6c757d; color: white; }
    button:disabled { opacity: 0.6; cursor: not-allowed; }
  `]
})
export class CardSettlementFormComponent implements OnInit {
  settlementForm!: FormGroup;
  costCenters: any[] = [];

  constructor(
    private fb: FormBuilder,
    private cardSettlementService: CbmCardSettlementService
  ) {}

  ngOnInit() {
    this.initForm();
    this.loadCostCenters();
  }

  initForm() {
    this.settlementForm = this.fb.group({
      company_branch_id: ['', Validators.required],
      cost_center_id: ['', Validators.required],
      document_number: ['', Validators.required],
      total_card: ['', [Validators.required, Validators.min(0)]],
      total_retention: ['', [Validators.required, Validators.min(0)]],
      total_bank_deposit: ['', [Validators.required, Validators.min(0)]],
      bank_fee: ['', [Validators.required, Validators.min(0)]],
      date: ['', Validators.required],
      commentary: [''],
      retention: [false]
    });
  }

  loadCostCenters() {
    // Cargar lista de centros de costo desde servicio
    this.costCenters = [
      { id: '1', name: 'Centro de Costo Principal' },
      { id: '2', name: 'Centro de Costo Secundario' }
    ];
  }

  onSubmit() {
    if (this.settlementForm.valid) {
      const formData = this.settlementForm.value;

      // Convertir fecha a timestamp
      formData.date = new Date(formData.date).getTime();

      this.cardSettlementService.save(formData).subscribe({
        next: (response) => {
          console.log('Liquidación creada:', response);
          // Resetear formulario y mostrar mensaje de éxito
          this.settlementForm.reset();
        },
        error: (error) => {
          console.error('Error al crear liquidación:', error);
        }
      });
    }
  }

  onCancel() {
    this.settlementForm.reset();
  }
}

Ejemplo con Gestión de Estados

import { BehaviorSubject, combineLatest } from 'rxjs';
import { map, startWith } from 'rxjs/operators';

@Component({
  selector: 'app-card-settlement-dashboard',
  standalone: true,
  template: `
    <div class="dashboard">
      <div class="stats">
        <div class="stat-card">
          <h3>Total Liquidaciones</h3>
          <span class="stat-number">{{ totalSettlements$ | async }}</span>
        </div>

        <div class="stat-card">
          <h3>Monto Total Tarjetas</h3>
          <span class="stat-number">{{ totalCardAmount$ | async | currency }}</span>
        </div>

        <div class="stat-card">
          <h3>Comisiones Totales</h3>
          <span class="stat-number">{{ totalFees$ | async | currency }}</span>
        </div>

        <div class="stat-card">
          <h3>Liquidaciones Activas</h3>
          <span class="stat-number">{{ activeSettlements$ | async }}</span>
        </div>
      </div>

      <div class="recent-settlements">
        <h3>Liquidaciones Recientes</h3>
        <div class="settlement-item" *ngFor="let settlement of recentSettlements$ | async">
          <div class="settlement-info">
            <span class="document">{{ settlement.document_number }}</span>
            <span class="cost-center">{{ settlement.cost_center_name }}</span>
          </div>
          <div class="settlement-amount">
            {{ settlement.total_card | currency }}
          </div>
        </div>
      </div>
    </div>
  `
})
export class CardSettlementDashboardComponent implements OnInit {
  private settlementsSubject = new BehaviorSubject<CbmCardSettlementModel.ListResponse.Item[]>([]);
  settlements$ = this.settlementsSubject.asObservable();

  totalSettlements$ = this.settlements$.pipe(
    map(settlements => settlements.length)
  );

  totalCardAmount$ = this.settlements$.pipe(
    map(settlements => settlements.reduce((sum, settlement) => sum + (settlement.total_card || 0), 0))
  );

  totalFees$ = this.settlements$.pipe(
    map(settlements => settlements.reduce((sum, settlement) => sum + (settlement.bank_fee || 0), 0))
  );

  activeSettlements$ = this.settlements$.pipe(
    map(settlements => settlements.filter(settlement => !settlement.reverse_status).length)
  );

  recentSettlements$ = this.settlements$.pipe(
    map(settlements => settlements.slice(0, 5))
  );

  constructor(private cardSettlementService: CbmCardSettlementService) {}

  ngOnInit() {
    this.loadSettlements();
  }

  loadSettlements() {
    const params: CbmCardSettlementModel.ListParams = {
      page: 1,
      size: 100
    };

    this.cardSettlementService.list(params).subscribe({
      next: (response) => {
        this.settlementsSubject.next(response.items);
      },
      error: (error) => {
        console.error('Error al cargar liquidaciones:', error);
      }
    });
  }
}

Ejemplo con Integración RxJS Avanzada

import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { Subject } from 'rxjs';

@Component({
  selector: 'app-card-settlement-search',
  standalone: true,
  template: `
    <div class="search-container">
      <input
        type="text"
        placeholder="Buscar por número de documento o movimiento..."
        [formControl]="searchControl"
        class="search-input">

      <div class="search-results" *ngIf="searchResults$ | async as results">
        <div class="result-item" *ngFor="let result of results" (click)="selectSettlement(result)">
          <div class="result-info">
            <span class="document">{{ result.document_number }}</span>
            <span class="movement">{{ result.movement_number }}</span>
          </div>
          <div class="result-amount">
            {{ result.total_card | currency }}
          </div>
        </div>

        <div class="no-results" *ngIf="results.length === 0">
          No se encontraron liquidaciones
        </div>
      </div>
    </div>
  `,
  styles: [`
    .search-container { position: relative; }
    .search-input { width: 100%; padding: 12px; border: 1px solid #ddd; border-radius: 4px; }
    .search-results { position: absolute; top: 100%; left: 0; right: 0; background: white; border: 1px solid #ddd; border-radius: 4px; max-height: 300px; overflow-y: auto; z-index: 1000; }
    .result-item { padding: 12px; border-bottom: 1px solid #eee; cursor: pointer; display: flex; justify-content: space-between; }
    .result-item:hover { background: #f5f5f5; }
    .no-results { padding: 12px; text-align: center; color: #666; }
  `]
})
export class CardSettlementSearchComponent implements OnInit, OnDestroy {
  searchControl = new FormControl('');
  private searchSubject = new Subject<string>();
  private destroy$ = new Subject<void>();

  searchResults$ = this.searchSubject.pipe(
    debounceTime(300),
    distinctUntilChanged(),
    switchMap(term => {
      if (term.length < 3) return of([]);

      const params: CbmCardSettlementModel.ListParams = {
        page: 1,
        size: 10,
        document_number: term,
        movement_number: term
      };

      return this.cardSettlementService.list(params).pipe(
        map(response => response.items),
        catchError(() => of([]))
      );
    })
  );

  constructor(private cardSettlementService: CbmCardSettlementService) {}

  ngOnInit() {
    this.searchControl.valueChanges.pipe(
      takeUntil(this.destroy$)
    ).subscribe(value => {
      this.searchSubject.next(value || '');
    });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  selectSettlement(settlement: CbmCardSettlementModel.ListResponse.Item) {
    console.log('Liquidación seleccionada:', settlement);
    // Emitir evento o navegar a detalle
  }
}

⚠️ Manejo de Errores

Errores de API

// Manejo de errores en operaciones
saveSettlement(settlementData: CbmCardSettlementModel.SaveBody) {
  this.cardSettlementService.save(settlementData).subscribe({
    next: (response) => {
      if (response.success) {
        console.log('Liquidación creada exitosamente');
        this.showSuccessMessage('Liquidación creada correctamente');
      } else {
        console.error('Error en la respuesta:', response);
        this.showErrorMessage('Error al crear la liquidación');
      }
    },
    error: (error) => {
      console.error('Error HTTP:', error);

      if (error.status === 400) {
        this.showErrorMessage('Datos inválidos. Verifique la información financiera.');
      } 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 gestionar liquidaciones.');
      } else {
        this.showErrorMessage('Error interno del servidor.');
      }
    }
  });
}

Estados de Carga

// Gestión de estados de carga
export class CardSettlementListComponent {
  isLoading = false;
  settlements: CbmCardSettlementModel.ListResponse.Item[] = [];

  loadSettlements() {
    this.isLoading = true;

    const params: CbmCardSettlementModel.ListParams = {
      page: this.currentPage,
      size: this.pageSize
    };

    this.cardSettlementService.list(params).subscribe({
      next: (response) => {
        this.settlements = response.items;
        this.isLoading = false;
      },
      error: (error) => {
        console.error('Error al cargar liquidaciones:', error);
        this.isLoading = false;
        this.showErrorMessage('Error al cargar la lista de liquidaciones');
      }
    });
  }
}

🔧 Configuración Avanzada

Configuración de Headers HTTP

// Configuración personalizada de HTTP
import { HTTP_INTERCEPTORS } from '@angular/common/http';

@NgModule({
  providers: [
    CbmCardSettlementModule.forRoot({
      baseUrl: 'https://api.cbm.com/card-settlements'
    }),
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ]
})
export class AppModule {}

Configuración de Timeouts

// Configuración de timeouts para operaciones largas
generateSeatWithTimeout(id: string) {
  return this.cardSettlementService.generateOrRegenerateSeat(id).pipe(
    timeout(30000), // 30 segundos timeout
    catchError(error => {
      if (error.name === 'TimeoutError') {
        return throwError(() => new Error('La operación tomó demasiado tiempo'));
      }
      return throwError(() => error);
    })
  );
}

Reintentos Automáticos

// Implementar reintentos para operaciones fallidas
saveSettlementWithRetry(settlementData: CbmCardSettlementModel.SaveBody, maxRetries = 3) {
  return this.cardSettlementService.save(settlementData).pipe(
    retry({
      count: maxRetries,
      delay: (error, retryCount) => timer(retryCount * 1000)
    }),
    catchError(error => {
      console.error(`Falló después de ${maxRetries} intentos:`, error);
      return throwError(() => error);
    })
  );
}

📋 Dependencias

Peer Dependencies (Requeridas)

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

Dependencias Internas

{
  "tslib": "^2.3.0"
}

🛠️ Desarrollo

Estructura del Proyecto

card-settlement-repository/
├── src/
│   ├── lib/
│   │   ├── card-settlement.model.ts       # Modelos de datos
│   │   ├── card-settlement.module.ts      # Configuración del módulo
│   │   ├── card-settlement.service.ts     # Servicio HTTP
│   │   ├── card-settlement.repository.ts  # Interfaz del repositorio
│   │   └── index.ts                      # Exportaciones
│   └── 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 card-settlement-repository

# Construir en modo watch
ng build card-settlement-repository --watch

# Construir para producción
ng build card-settlement-repository --configuration production

Pruebas

# Ejecutar pruebas unitarias
ng test card-settlement-repository

# Ejecutar pruebas con coverage
ng test card-settlement-repository --code-coverage

# Pruebas end-to-end
ng e2e card-settlement-repository

🎯 Mejores Prácticas

1. Gestión de Memoria

// Limpiar suscripciones para evitar memory leaks
export class CardSettlementListComponent implements OnDestroy {
  private destroy$ = new Subject<void>();

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
  }

  loadSettlements() {
    this.cardSettlementService.list(params).pipe(
      takeUntil(this.destroy$)
    ).subscribe({
      next: (response) => {
        this.settlements = response.items;
      }
    });
  }
}

2. Optimización de Rendimiento

// Usar OnPush change detection para mejor rendimiento
@Component({
  selector: 'app-card-settlement-list',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `...`
})
export class CardSettlementListComponent {
  settlements$ = this.cardSettlementService.list(params).pipe(
    shareReplay(1) // Compartir la respuesta entre múltiples suscriptores
  );
}

3. Validación de Datos Financieros

// Validaciones personalizadas para datos financieros
export class SettlementValidators {
  static validTotalCard(control: AbstractControl): ValidationErrors | null {
    const value = control.value;
    if (!value) return null;

    return value > 0 ? null : { invalidTotalCard: true };
  }

  static validBankFee(control: AbstractControl): ValidationErrors | null {
    const value = control.value;
    if (!value) return null;

    return value >= 0 ? null : { invalidBankFee: true };
  }

  static validRetentionAmount(form: FormGroup): ValidationErrors | null {
    const totalCard = form.get('total_card')?.value;
    const totalRetention = form.get('total_retention')?.value;

    if (!totalCard || !totalRetention) return null;

    return totalRetention <= totalCard ? null : { invalidRetentionAmount: true };
  }
}

// Uso en formularios reactivos
this.settlementForm = this.fb.group({
  total_card: ['', [Validators.required, SettlementValidators.validTotalCard]],
  total_retention: ['', [Validators.required, SettlementValidators.validBankFee]],
  bank_fee: ['', [Validators.required, SettlementValidators.validBankFee]]
}, { validators: SettlementValidators.validRetentionAmount });

4. Logging y Monitoreo

// Servicio de logging para operaciones financieras
@Injectable({ providedIn: 'root' })
export class SettlementLoggingService {
  logOperation(operation: string, data: any) {
    console.log(`[${new Date().toISOString()}] ${operation}:`, data);

    // Enviar a servicio de analytics
    this.analytics.trackEvent('settlement_operation', {
      operation,
      timestamp: Date.now(),
      data
    });
  }

  logError(operation: string, error: any) {
    console.error(`[${new Date().toISOString()}] Error in ${operation}:`, error);

    // Enviar a servicio de error tracking
    this.errorTracker.captureException(error, {
      tags: { operation, module: 'card-settlement' }
    });
  }
}

🤝 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 forRoot pattern
  • ✅ Servicio que implementa interfaz del repositorio
  • ✅ Operaciones CRUD completas (list, getOne, save)
  • ✅ Generación/regeneración de asientos contables
  • ✅ Modelos TypeScript bien tipados con datos financieros
  • ✅ Integración completa con HttpClient
  • ✅ Documentación completa en español

Nota: Este repositorio está optimizado para sistemas financieros que requieren gestión completa de liquidaciones de tarjetas con integración bancaria y contable. Las operaciones incluyen validaciones financieras y manejo robusto de datos monetarios.