@cbm-common/deposit-cheque-repository
v0.0.1
Published
Repositorio Angular para la gestión completa de depósitos de cheques en sistemas financieros. Implementa operaciones CRUD, consultas paginadas y generación de asientos contables con integración completa al sistema CBM.
Readme
Deposit Cheque Repository
Repositorio Angular para la gestión completa de depósitos de cheques 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/deposit-cheque-repository⚙️ Configuración
Configuración del Módulo
El módulo debe configurarse en el módulo raíz de la aplicación:
import { CbmDepositChequeModule } from '@cbm-common/deposit-cheque-repository';
@NgModule({
imports: [
CbmDepositChequeModule.forRoot({
baseUrl: 'https://api.cbm.com/deposit-cheques'
})
]
})
export class AppModule {}Configuración Standalone
Para aplicaciones standalone, configura el módulo en el bootstrap:
import { CbmDepositChequeModule } from '@cbm-common/deposit-cheque-repository';
bootstrapApplication(AppComponent, {
providers: [
CbmDepositChequeModule.forRoot({
baseUrl: 'https://api.cbm.com/deposit-cheques'
})
]
});🎯 Inyección de Dependencias
Inyección del Servicio
import { CbmDepositChequeService } from '@cbm-common/deposit-cheque-repository';
@Component({
selector: 'app-deposit-cheque-manager',
standalone: true,
imports: [CbmDepositChequeService]
})
export class DepositChequeManagerComponent {
constructor(private depositChequeService: CbmDepositChequeService) {}
}Inyección del Repositorio
import { CbmDepositChequeRepository } from '@cbm-common/deposit-cheque-repository';
@Component({
selector: 'app-deposit-cheque-list',
standalone: true,
imports: [CbmDepositChequeRepository]
})
export class DepositChequeListComponent {
constructor(private depositChequeRepo: CbmDepositChequeRepository) {}
}🏗️ Arquitectura del Repositorio
Patrón de Diseño
El repositorio sigue el patrón Repository Pattern con Dependency Injection:
CbmDepositChequeModule
├── ICbmDepositChequeModuleConfig (configuración)
├── CbmDepositChequeService (implementa ICbmDepositChequeRepository)
├── CbmDepositChequeRepository (wrapper del service)
├── CbmDepositChequeModel (modelos de datos)
└── HttpClient (cliente HTTP)Interfaz del Repositorio
export interface ICbmDepositChequeRepository {
list(params: CbmDepositChequeModel.ListParams): Observable<CbmDepositChequeModel.ListResponse>;
getOne(id: string): Observable<CbmDepositChequeModel.GetOneResponse>;
save(data: CbmDepositChequeModel.SaveBody): Observable<CbmDepositChequeModel.ConfirmResponse>;
generateOrRegenerateSeat(id: string): Observable<CbmDepositChequeModel.ConfirmResponse>;
}📊 Operaciones Disponibles
Listado de Depósitos
// Listado paginado con filtros
list(params: {
page: number; // Página actual
size: number; // Tamaño de página
document_number?: string; // Número de documento
bank_name?: string; // Nombre del banco
operation_number?: string; // Número de operación
category_id?: string; // ID de categoría
date_begin?: number; // Fecha inicio (timestamp)
date_end?: number; // Fecha fin (timestamp)
enabled?: boolean; // Estado activo/inactivo
}): Observable<ListResponse>Obtener Depósito Individual
// Obtener un depósito específico por ID
getOne(id: string): Observable<GetOneResponse>Crear Nuevo Depósito
// Crear un nuevo depósito de cheque
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 un depósito de cheque:
interface Item {
_id: string;
company_id: string;
company_branch_id: string;
// 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_logo: string;
company_branch_address: string;
company_branch_email: string;
company_branch_cellphone: string;
company_branch_phone: string;
// Información bancaria
financials_bank_id: string;
financials_bank_name: string;
financials_bank_account_number: string;
// Información del depósito
cost_center_id: string;
document_nomenclature: string;
document_number: string;
total_payment: number;
deposit_date: number;
date: number;
operation_number: string;
enabled: boolean;
}SaveBody
Datos para crear un nuevo depósito:
interface SaveBody {
company_branch_id: string;
financials_bank_id: string;
cost_center_id: string;
document_number: string;
total_payment: number;
deposit_date: number;
operation_number: string;
// ... otros campos según la API
}🚀 Ejemplos de Uso
Ejemplo Básico: Listado de Depósitos
import { CbmDepositChequeService } from '@cbm-common/deposit-cheque-repository';
import { CbmDepositChequeModel } from '@cbm-common/deposit-cheque-repository';
@Component({
selector: 'app-deposit-cheque-list',
standalone: true,
template: `
<div class="deposit-list">
<h2>Depósitos de Cheques</h2>
<div class="filters">
<input [(ngModel)]="filters.document_number" placeholder="Número de documento">
<input [(ngModel)]="filters.bank_name" placeholder="Nombre del banco">
<button (click)="loadDeposits()">Buscar</button>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>Número Documento</th>
<th>Banco</th>
<th>Monto Total</th>
<th>Fecha Depósito</th>
<th>Estado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let deposit of deposits">
<td>{{ deposit.document_number }}</td>
<td>{{ deposit.financials_bank_name }}</td>
<td>{{ deposit.total_payment | currency }}</td>
<td>{{ deposit.deposit_date | date:'dd/MM/yyyy' }}</td>
<td>
<span [class]="'status ' + (deposit.enabled ? 'active' : 'inactive')">
{{ deposit.enabled ? 'Activo' : 'Inactivo' }}
</span>
</td>
<td>
<button (click)="viewDeposit(deposit._id)">Ver</button>
<button (click)="generateSeat(deposit._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: [`
.deposit-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.inactive { color: red; }
.pagination { display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 20px; }
`]
})
export class DepositChequeListComponent implements OnInit {
deposits: CbmDepositChequeModel.ListResponse.Item[] = [];
currentPage = 1;
pageSize = 10;
totalPages = 0;
filters: Partial<CbmDepositChequeModel.ListParams> = {
document_number: '',
bank_name: ''
};
constructor(private depositChequeService: CbmDepositChequeService) {}
ngOnInit() {
this.loadDeposits();
}
loadDeposits() {
const params: CbmDepositChequeModel.ListParams = {
page: this.currentPage,
size: this.pageSize,
...this.filters
};
this.depositChequeService.list(params).subscribe({
next: (response) => {
this.deposits = response.items;
this.totalPages = response.pages;
},
error: (error) => {
console.error('Error al cargar depósitos:', error);
}
});
}
changePage(page: number) {
this.currentPage = page;
this.loadDeposits();
}
viewDeposit(id: string) {
this.depositChequeService.getOne(id).subscribe({
next: (deposit) => {
console.log('Depósito:', deposit);
// Mostrar modal o navegar a detalle
}
});
}
generateSeat(id: string) {
this.depositChequeService.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 { CbmDepositChequeService } from '@cbm-common/deposit-cheque-repository';
@Component({
selector: 'app-deposit-cheque-form',
standalone: true,
imports: [ReactiveFormsModule, CbmDepositChequeService],
template: `
<form [formGroup]="depositForm" (ngSubmit)="onSubmit()">
<h2>Crear Nuevo Depósito de Cheque</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="bankId">Banco</label>
<select id="bankId" formControlName="financials_bank_id">
<option *ngFor="let bank of banks" [value]="bank.id">
{{ bank.name }}
</option>
</select>
</div>
<div class="form-group">
<label for="totalPayment">Monto Total</label>
<input id="totalPayment" type="number" formControlName="total_payment" step="0.01">
</div>
<div class="form-group">
<label for="depositDate">Fecha de Depósito</label>
<input id="depositDate" type="date" formControlName="deposit_date">
</div>
<div class="form-group">
<label for="operationNumber">Número de Operación</label>
<input id="operationNumber" type="text" formControlName="operation_number">
</div>
<div class="form-actions">
<button type="button" (click)="onCancel()">Cancelar</button>
<button type="submit" [disabled]="depositForm.invalid">Crear Depósito</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 { 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 DepositChequeFormComponent implements OnInit {
depositForm!: FormGroup;
banks: any[] = [];
constructor(
private fb: FormBuilder,
private depositChequeService: CbmDepositChequeService
) {}
ngOnInit() {
this.initForm();
this.loadBanks();
}
initForm() {
this.depositForm = this.fb.group({
company_branch_id: ['', Validators.required],
financials_bank_id: ['', Validators.required],
cost_center_id: ['', Validators.required],
document_number: ['', Validators.required],
total_payment: ['', [Validators.required, Validators.min(0)]],
deposit_date: ['', Validators.required],
operation_number: ['', Validators.required]
});
}
loadBanks() {
// Cargar lista de bancos desde servicio
this.banks = [
{ id: '1', name: 'Banco Nacional' },
{ id: '2', name: 'Banco Internacional' }
];
}
onSubmit() {
if (this.depositForm.valid) {
const formData = this.depositForm.value;
// Convertir fecha a timestamp
formData.deposit_date = new Date(formData.deposit_date).getTime();
this.depositChequeService.save(formData).subscribe({
next: (response) => {
console.log('Depósito creado:', response);
// Resetear formulario y mostrar mensaje de éxito
this.depositForm.reset();
},
error: (error) => {
console.error('Error al crear depósito:', error);
}
});
}
}
onCancel() {
this.depositForm.reset();
}
}Ejemplo con Gestión de Estados
import { BehaviorSubject, combineLatest } from 'rxjs';
import { map, startWith } from 'rxjs/operators';
@Component({
selector: 'app-deposit-cheque-dashboard',
standalone: true,
template: `
<div class="dashboard">
<div class="stats">
<div class="stat-card">
<h3>Total Depósitos</h3>
<span class="stat-number">{{ totalDeposits$ | async }}</span>
</div>
<div class="stat-card">
<h3>Monto Total</h3>
<span class="stat-number">{{ totalAmount$ | async | currency }}</span>
</div>
<div class="stat-card">
<h3>Depósitos Activos</h3>
<span class="stat-number">{{ activeDeposits$ | async }}</span>
</div>
</div>
<div class="recent-deposits">
<h3>Depósitos Recientes</h3>
<div class="deposit-item" *ngFor="let deposit of recentDeposits$ | async">
<div class="deposit-info">
<span class="document">{{ deposit.document_number }}</span>
<span class="bank">{{ deposit.financials_bank_name }}</span>
</div>
<div class="deposit-amount">
{{ deposit.total_payment | currency }}
</div>
</div>
</div>
</div>
`
})
export class DepositChequeDashboardComponent implements OnInit {
private depositsSubject = new BehaviorSubject<CbmDepositChequeModel.ListResponse.Item[]>([]);
deposits$ = this.depositsSubject.asObservable();
totalDeposits$ = this.deposits$.pipe(
map(deposits => deposits.length)
);
totalAmount$ = this.deposits$.pipe(
map(deposits => deposits.reduce((sum, deposit) => sum + deposit.total_payment, 0))
);
activeDeposits$ = this.deposits$.pipe(
map(deposits => deposits.filter(deposit => deposit.enabled).length)
);
recentDeposits$ = this.deposits$.pipe(
map(deposits => deposits.slice(0, 5))
);
constructor(private depositChequeService: CbmDepositChequeService) {}
ngOnInit() {
this.loadDeposits();
}
loadDeposits() {
const params: CbmDepositChequeModel.ListParams = {
page: 1,
size: 100,
enabled: true
};
this.depositChequeService.list(params).subscribe({
next: (response) => {
this.depositsSubject.next(response.items);
},
error: (error) => {
console.error('Error al cargar depósitos:', error);
}
});
}
}Ejemplo con Integración RxJS Avanzada
import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
import { Subject } from 'rxjs';
@Component({
selector: 'app-deposit-cheque-search',
standalone: true,
template: `
<div class="search-container">
<input
type="text"
placeholder="Buscar por número de documento o banco..."
[formControl]="searchControl"
class="search-input">
<div class="search-results" *ngIf="searchResults$ | async as results">
<div class="result-item" *ngFor="let result of results" (click)="selectDeposit(result)">
<div class="result-info">
<span class="document">{{ result.document_number }}</span>
<span class="bank">{{ result.financials_bank_name }}</span>
</div>
<div class="result-amount">
{{ result.total_payment | currency }}
</div>
</div>
<div class="no-results" *ngIf="results.length === 0">
No se encontraron depósitos
</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 DepositChequeSearchComponent 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: CbmDepositChequeModel.ListParams = {
page: 1,
size: 10,
document_number: term,
bank_name: term
};
return this.depositChequeService.list(params).pipe(
map(response => response.items),
catchError(() => of([]))
);
})
);
constructor(private depositChequeService: CbmDepositChequeService) {}
ngOnInit() {
this.searchControl.valueChanges.pipe(
takeUntil(this.destroy$)
).subscribe(value => {
this.searchSubject.next(value || '');
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
selectDeposit(deposit: CbmDepositChequeModel.ListResponse.Item) {
console.log('Depósito seleccionado:', deposit);
// Emitir evento o navegar a detalle
}
}⚠️ Manejo de Errores
Errores de API
// Manejo de errores en operaciones
saveDeposit(depositData: CbmDepositChequeModel.SaveBody) {
this.depositChequeService.save(depositData).subscribe({
next: (response) => {
if (response.success) {
console.log('Depósito creado exitosamente');
this.showSuccessMessage('Depósito creado correctamente');
} else {
console.error('Error en la respuesta:', response);
this.showErrorMessage('Error al crear el depósito');
}
},
error: (error) => {
console.error('Error HTTP:', error);
if (error.status === 400) {
this.showErrorMessage('Datos inválidos. Verifique la información.');
} 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 esta operación.');
} else {
this.showErrorMessage('Error interno del servidor.');
}
}
});
}Estados de Carga
// Gestión de estados de carga
export class DepositChequeListComponent {
isLoading = false;
deposits: CbmDepositChequeModel.ListResponse.Item[] = [];
loadDeposits() {
this.isLoading = true;
const params: CbmDepositChequeModel.ListParams = {
page: this.currentPage,
size: this.pageSize
};
this.depositChequeService.list(params).subscribe({
next: (response) => {
this.deposits = response.items;
this.isLoading = false;
},
error: (error) => {
console.error('Error al cargar depósitos:', error);
this.isLoading = false;
this.showErrorMessage('Error al cargar la lista de depósitos');
}
});
}
}🔧 Configuración Avanzada
Configuración de Headers HTTP
// Configuración personalizada de HTTP
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@NgModule({
providers: [
CbmDepositChequeModule.forRoot({
baseUrl: 'https://api.cbm.com/deposit-cheques'
}),
{
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.depositChequeService.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
saveDepositWithRetry(depositData: CbmDepositChequeModel.SaveBody, maxRetries = 3) {
return this.depositChequeService.save(depositData).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
deposit-cheque-repository/
├── src/
│ ├── lib/
│ │ ├── deposit-cheque.model.ts # Modelos de datos
│ │ ├── deposit-cheque.module.ts # Configuración del módulo
│ │ ├── deposit-cheque.service.ts # Servicio HTTP
│ │ ├── deposit-cheque.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 deposit-cheque-repository
# Construir en modo watch
ng build deposit-cheque-repository --watch
# Construir para producción
ng build deposit-cheque-repository --configuration productionPruebas
# Ejecutar pruebas unitarias
ng test deposit-cheque-repository
# Ejecutar pruebas con coverage
ng test deposit-cheque-repository --code-coverage
# Pruebas end-to-end
ng e2e deposit-cheque-repository🎯 Mejores Prácticas
1. Gestión de Memoria
// Limpiar suscripciones para evitar memory leaks
export class DepositChequeListComponent implements OnDestroy {
private destroy$ = new Subject<void>();
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
loadDeposits() {
this.depositChequeService.list(params).pipe(
takeUntil(this.destroy$)
).subscribe({
next: (response) => {
this.deposits = response.items;
}
});
}
}2. Optimización de Rendimiento
// Usar OnPush change detection para mejor rendimiento
@Component({
selector: 'app-deposit-cheque-list',
changeDetection: ChangeDetectionStrategy.OnPush,
template: `...`
})
export class DepositChequeListComponent {
deposits$ = this.depositChequeService.list(params).pipe(
shareReplay(1) // Compartir la respuesta entre múltiples suscriptores
);
}3. Validación de Datos
// Validaciones personalizadas para depósitos
export class DepositValidators {
static validDocumentNumber(control: AbstractControl): ValidationErrors | null {
const value = control.value;
if (!value) return null;
// Validar formato de número de documento
const documentPattern = /^[A-Z]{2}\d{8}$/;
return documentPattern.test(value) ? null : { invalidDocumentNumber: true };
}
static validTotalPayment(control: AbstractControl): ValidationErrors | null {
const value = control.value;
if (!value) return null;
return value > 0 ? null : { invalidTotalPayment: true };
}
}
// Uso en formularios reactivos
this.depositForm = this.fb.group({
document_number: ['', [Validators.required, DepositValidators.validDocumentNumber]],
total_payment: ['', [Validators.required, DepositValidators.validTotalPayment]]
});4. Logging y Monitoreo
// Servicio de logging para operaciones de depósitos
@Injectable({ providedIn: 'root' })
export class DepositLoggingService {
logOperation(operation: string, data: any) {
console.log(`[${new Date().toISOString()}] ${operation}:`, data);
// Enviar a servicio de analytics
this.analytics.trackEvent('deposit_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: 'deposit-cheque' }
});
}
}🤝 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
- ✅ 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 depósitos de cheques con integración bancaria y contable. Las operaciones incluyen validaciones de negocio y manejo robusto de errores.
