@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ónConstrucció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 productionPruebas
# 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
- Fork el repositorio
- Crea una rama para tu feature (
git checkout -b feature/nueva-funcionalidad) - Commit tus cambios (
git commit -am 'Agrega nueva funcionalidad') - Push a la rama (
git push origin feature/nueva-funcionalidad) - 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:
- 📧 Email: [email protected]
- 💬 Slack: #desarrollo-cbm
- 📋 Issues: GitHub Issues
🔄 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.
