@cbm-common/declaration-code-configuration-repository
v0.0.1
Published
Biblioteca Angular especializada en la gestión de configuraciones de códigos de declaración fiscal. Proporciona una API completa para administrar códigos de declaración bruta, neta e impositiva, organizados por país y tipo de declaración.
Readme
Declaration Code Configuration Repository
Biblioteca Angular especializada en la gestión de configuraciones de códigos de declaración fiscal. Proporciona una API completa para administrar códigos de declaración bruta, neta e impositiva, organizados por país y tipo de declaración.
📋 Características Principales
- ✅ Gestión Completa de Códigos: Administración de códigos brutos, netos e impositivos
- ✅ Soporte Multi-País: Configuraciones específicas por jurisdicción territorial
- ✅ Múltiples Tipos: Diferentes categorías de códigos de declaración
- ✅ Operaciones CRUD Completas: Crear, leer, actualizar y eliminar configuraciones
- ✅ Filtrado Avanzado: Búsqueda por estado, nombre y otros criterios
- ✅ Arquitectura Moderna: Basada en Angular 20.1.5 con inyección de dependencias
- ✅ Patrón Repository: Separación clara entre lógica de negocio y acceso a datos
- ✅ TypeScript Nativo: Interfaces tipadas para mayor seguridad y mantenibilidad
🚀 Instalación
npm install @cbm-common/declaration-code-configuration-repository⚙️ Configuración
1. Importar el Módulo
import { CbmDeclarationCodeConfigurationModule } from '@cbm-common/declaration-code-configuration-repository';
@NgModule({
imports: [
CbmDeclarationCodeConfigurationModule.forRoot({
baseUrl: 'https://api.cbm.com/declaration-codes'
})
]
})
export class AppModule { }2. Configuración Standalone (Angular 20+)
import { CbmDeclarationCodeConfigurationModule } from '@cbm-common/declaration-code-configuration-repository';
@Component({
standalone: true,
imports: [CbmDeclarationCodeConfigurationModule.forRoot({
baseUrl: 'https://api.cbm.com/declaration-codes'
})]
})
export class AppComponent { }📚 API Reference
Interfaz del Repositorio
interface ICbmDeclarationCodeConfigurationRepository {
// Listado con filtros
list(params: CbmDeclarationCodeConfigurationModel.ListParams): Observable<CbmDeclarationCodeConfigurationModel.ListResponse[]>;
// Crear nueva configuración
save(data: CbmDeclarationCodeConfigurationModel.SaveBody): Observable<CbmDeclarationCodeConfigurationModel.ConfirmResponse>;
// Actualizar configuración existente
update(id: string, data: CbmDeclarationCodeConfigurationModel.UpdateBody): Observable<CbmDeclarationCodeConfigurationModel.ConfirmResponse>;
// Obtener configuración por ID
getOne(id: string): Observable<CbmDeclarationCodeConfigurationModel.GetOneResponse>;
// Eliminar configuración
delete(id: string): Observable<CbmDeclarationCodeConfigurationModel.ConfirmResponse>;
}💡 Uso Básico
Inyección del Servicio
import { CbmDeclarationCodeConfigurationRepository } from '@cbm-common/declaration-code-configuration-repository';
@Component({
selector: 'app-declaration-code-manager'
})
export class DeclarationCodeManagerComponent {
codes: CbmDeclarationCodeConfigurationModel.ListResponse[] = [];
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {
this.loadCodes();
}
loadCodes() {
const params: CbmDeclarationCodeConfigurationModel.ListParams = {
enabled: true,
name: 'IVA'
};
this.declarationCodeRepository.list(params).subscribe({
next: (response) => {
this.codes = response;
console.log('Códigos de declaración cargados:', this.codes);
},
error: (error) => {
console.error('Error al cargar códigos:', error);
}
});
}
}Crear Nueva Configuración
createNewCode() {
const codeData: CbmDeclarationCodeConfigurationModel.SaveBody = {
raw_code: 'IVA-001',
net_code: 'IVA-NET-001',
tax_code: 'IVA-TAX-001',
description: 'Código de declaración para IVA general',
type: 'TAX'
};
this.declarationCodeRepository.save(codeData).subscribe({
next: (response) => {
if (response.success) {
console.log('Código creado exitosamente');
this.loadCodes(); // Recargar lista
}
},
error: (error) => console.error('Error al crear código:', error)
});
}Actualizar Configuración Existente
updateExistingCode(codeId: string) {
const updateData: CbmDeclarationCodeConfigurationModel.UpdateBody = {
description: 'Código de declaración para IVA general - Actualizado',
raw_code: 'IVA-001-UPDATED',
net_code: 'IVA-NET-001-UPDATED'
};
this.declarationCodeRepository.update(codeId, updateData).subscribe({
next: (response) => {
if (response.success) {
console.log('Código actualizado exitosamente');
this.loadCodes();
}
},
error: (error) => console.error('Error al actualizar código:', error)
});
}Obtener Configuración Específica
loadCodeDetails(codeId: string) {
this.declarationCodeRepository.getOne(codeId).subscribe({
next: (response) => {
console.log('Detalles del código:', response);
console.log('Código bruto:', response.raw_code);
console.log('Código neto:', response.net_code);
console.log('Código impositivo:', response.tax_code);
console.log('Tipo:', response.type);
console.log('País:', response.country_id);
},
error: (error) => console.error('Código no encontrado:', error)
});
}📊 Modelos de Datos
Parámetros de Búsqueda
interface ListParams {
enabled?: boolean; // Filtrar por estado activo/inactivo
name?: string; // Filtrar por nombre o descripción
}Configuración de Código de Declaración
interface ListResponse {
_id: string; // ID único de la configuración
country_id: string; // ID del país
raw_code: string; // Código bruto de declaración
net_code: string; // Código neto de declaración
tax_code?: string; // Código impositivo (opcional)
description: string; // Descripción detallada
type: string; // Tipo de código (ej: 'TAX', 'INCOME', etc.)
enabled: boolean; // Estado activo/inactivo
deleted: boolean; // Indicador de eliminación lógica
created_at: number; // Fecha de creación (timestamp)
created_user: string; // Usuario que creó
updated_at?: number; // Fecha de actualización (timestamp)
updated_user?: string; // Usuario que actualizó
}Datos para Crear Nueva Configuración
interface SaveBody {
raw_code?: string; // Código bruto (opcional)
net_code?: string; // Código neto (opcional)
tax_code?: string; // Código impositivo (opcional)
description: string; // Descripción (requerido)
type: string; // Tipo de código (requerido)
}Datos para Actualizar Configuración
interface UpdateBody {
raw_code?: string; // Código bruto (opcional)
net_code?: string; // Código neto (opcional)
tax_code?: string; // Código impositivo (opcional)
description?: string; // Descripción (opcional)
type?: string; // Tipo de código (opcional)
}Respuesta de Confirmación
interface ConfirmResponse {
success: boolean; // Indicador de éxito
message: string; // Mensaje descriptivo
data?: any; // Datos adicionales (opcional)
}Ejemplo de Datos
{
"_id": "64f1a2b3c4d5e6f7g8h9i0j1",
"country_id": "ecuador",
"raw_code": "IVA-001",
"net_code": "IVA-NET-001",
"tax_code": "IVA-TAX-001",
"description": "Código de declaración para IVA general",
"type": "TAX",
"enabled": true,
"deleted": false,
"created_at": 1694000000000,
"created_user": "admin",
"updated_at": 1694000000000,
"updated_user": "admin"
}🔍 Casos de Uso Comunes
1. Sistema de Gestión Fiscal
@Component({
selector: 'app-tax-code-manager'
})
export class TaxCodeManagerComponent {
taxCodes: CbmDeclarationCodeConfigurationModel.ListResponse[] = [];
selectedCountry = 'ecuador';
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {
this.loadTaxCodes();
}
loadTaxCodes() {
const params: CbmDeclarationCodeConfigurationModel.ListParams = {
enabled: true
};
this.declarationCodeRepository.list(params).subscribe({
next: (response) => {
// Filtrar por país seleccionado
this.taxCodes = response.filter(code =>
code.country_id === this.selectedCountry && code.type === 'TAX'
);
}
});
}
getCodesByType(type: string): CbmDeclarationCodeConfigurationModel.ListResponse[] {
return this.taxCodes.filter(code => code.type === type);
}
}2. Generador de Reportes Fiscales
@Component({
selector: 'app-fiscal-report-generator'
})
export class FiscalReportGeneratorComponent {
reportData: any[] = [];
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {}
generateFiscalReport(countryId: string, type: string) {
const params: CbmDeclarationCodeConfigurationModel.ListParams = {
enabled: true
};
this.declarationCodeRepository.list(params).subscribe({
next: (response) => {
const filteredCodes = response.filter(code =>
code.country_id === countryId &&
code.type === type &&
!code.deleted
);
this.reportData = filteredCodes.map(code => ({
codigo_bruto: code.raw_code,
codigo_neto: code.net_code,
codigo_impositivo: code.tax_code,
descripcion: code.description,
tipo: code.type,
pais: code.country_id
}));
this.exportReport();
}
});
}
private exportReport() {
// Lógica para exportar el reporte
console.log('Datos del reporte:', this.reportData);
}
}3. Validador de Códigos de Declaración
@Injectable({ providedIn: 'root' })
export class DeclarationCodeValidatorService {
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {}
validateCode(code: string, countryId: string, type: string): Observable<boolean> {
const params: CbmDeclarationCodeConfigurationModel.ListParams = {
enabled: true
};
return this.declarationCodeRepository.list(params).pipe(
map(response => {
return response.some(item =>
(item.raw_code === code || item.net_code === code || item.tax_code === code) &&
item.country_id === countryId &&
item.type === type &&
!item.deleted
);
}),
catchError(() => of(false))
);
}
getCodeByRawCode(rawCode: string): Observable<CbmDeclarationCodeConfigurationModel.ListResponse | null> {
const params: CbmDeclarationCodeConfigurationModel.ListParams = {
enabled: true
};
return this.declarationCodeRepository.list(params).pipe(
map(response => {
return response.find(item =>
item.raw_code === rawCode && !item.deleted
) || null;
})
);
}
}4. Dashboard de Configuraciones
@Component({
selector: 'app-configuration-dashboard'
})
export class ConfigurationDashboardComponent {
stats = {
totalCodes: 0,
activeCodes: 0,
inactiveCodes: 0,
codesByType: new Map<string, number>(),
codesByCountry: new Map<string, number>()
};
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {
this.loadDashboardStats();
}
loadDashboardStats() {
const params: CbmDeclarationCodeConfigurationModel.ListParams = {};
this.declarationCodeRepository.list(params).subscribe({
next: (response) => {
this.calculateStats(response);
}
});
}
private calculateStats(codes: CbmDeclarationCodeConfigurationModel.ListResponse[]) {
this.stats.totalCodes = codes.length;
this.stats.activeCodes = codes.filter(code => code.enabled && !code.deleted).length;
this.stats.inactiveCodes = codes.filter(code => !code.enabled || code.deleted).length;
// Estadísticas por tipo
codes.forEach(code => {
const current = this.stats.codesByType.get(code.type) || 0;
this.stats.codesByType.set(code.type, current + 1);
});
// Estadísticas por país
codes.forEach(code => {
const current = this.stats.codesByCountry.get(code.country_id) || 0;
this.stats.codesByCountry.set(code.country_id, current + 1);
});
}
}🏗️ Arquitectura y Patrones
Patrón Repository
La biblioteca implementa el patrón Repository para mantener la separación entre la lógica de negocio y el acceso a datos:
// Interfaz del repositorio
export interface ICbmDeclarationCodeConfigurationRepository {
list(params: CbmDeclarationCodeConfigurationModel.ListParams): Observable<CbmDeclarationCodeConfigurationModel.ListResponse[]>;
save(data: CbmDeclarationCodeConfigurationModel.SaveBody): Observable<CbmDeclarationCodeConfigurationModel.ConfirmResponse>;
update(id: string, data: CbmDeclarationCodeConfigurationModel.UpdateBody): Observable<CbmDeclarationCodeConfigurationModel.ConfirmResponse>;
getOne(id: string): Observable<CbmDeclarationCodeConfigurationModel.GetOneResponse>;
delete(id: string): Observable<CbmDeclarationCodeConfigurationModel.ConfirmResponse>;
}
// Implementación del servicio
@Injectable({ providedIn: 'root' })
export class CbmDeclarationCodeConfigurationService implements ICbmDeclarationCodeConfigurationRepository {
constructor(
private readonly http: HttpClient,
@Inject(DECLARATION_CODE_CONFIGURATION_MODULE_CONFIG) private readonly config: ICbmDeclarationCodeConfigurationModuleConfig
) {}
list(params: CbmDeclarationCodeConfigurationModel.ListParams): Observable<CbmDeclarationCodeConfigurationModel.ListResponse[]> {
return this.http.get<CbmDeclarationCodeConfigurationModel.ListResponse[]>(this.config.baseUrl, { params: { ...params } });
}
// ... otros métodos
}
// Wrapper del repositorio
@Injectable({ providedIn: 'root' })
export class CbmDeclarationCodeConfigurationRepository implements ICbmDeclarationCodeConfigurationRepository {
constructor(private service: CbmDeclarationCodeConfigurationService) {}
list(params: CbmDeclarationCodeConfigurationModel.ListParams): Observable<CbmDeclarationCodeConfigurationModel.ListResponse[]> {
return this.service.list(params);
}
// ... delegación a service
}Configuración Centralizada
// Configuración del módulo
@NgModule({
imports: [
CbmDeclarationCodeConfigurationModule.forRoot({
baseUrl: environment.apiUrl + '/declaration-codes'
})
]
})
export class AppModule {}🔧 Mejores Prácticas
Manejo de Errores
@Component({
selector: 'app-safe-code-handler'
})
export class SafeCodeHandlerComponent {
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {}
safeLoadCodes() {
const params: CbmDeclarationCodeConfigurationModel.ListParams = {
enabled: true
};
this.declarationCodeRepository.list(params).subscribe({
next: (response) => {
if (Array.isArray(response)) {
this.processCodes(response);
} else {
this.showErrorMessage('Respuesta inválida del servidor');
}
},
error: (error) => {
if (error.status === 404) {
this.showErrorMessage('Servicio de códigos no disponible');
} else if (error.status === 500) {
this.showErrorMessage('Error interno del servidor');
} else {
this.showErrorMessage('Error de conexión');
}
}
});
}
private processCodes(codes: CbmDeclarationCodeConfigurationModel.ListResponse[]) {
// Procesar códigos de manera segura
codes.forEach(code => {
if (code.raw_code && code.type) {
// Procesar código válido
} else {
console.warn(`Código inválido: ${code._id}`);
}
});
}
private showErrorMessage(message: string) {
// Mostrar mensaje de error al usuario
}
}Validación de Datos
@Injectable({ providedIn: 'root' })
export class CodeValidationService {
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {}
validateCodeData(data: CbmDeclarationCodeConfigurationModel.SaveBody): string[] {
const errors: string[] = [];
if (!data.description?.trim()) {
errors.push('La descripción es obligatoria');
}
if (!data.type?.trim()) {
errors.push('El tipo es obligatorio');
}
if (data.raw_code && !this.isValidCodeFormat(data.raw_code)) {
errors.push('El formato del código bruto no es válido');
}
if (data.net_code && !this.isValidCodeFormat(data.net_code)) {
errors.push('El formato del código neto no es válido');
}
return errors;
}
private isValidCodeFormat(code: string): boolean {
// Validar formato de código (ejemplo: XXX-999)
const codePattern = /^[A-Z]{3}-\d{3}$/;
return codePattern.test(code);
}
validateAndSave(data: CbmDeclarationCodeConfigurationModel.SaveBody): Observable<boolean> {
const errors = this.validateCodeData(data);
if (errors.length > 0) {
console.error('Errores de validación:', errors);
return of(false);
}
return this.declarationCodeRepository.save(data).pipe(
map(response => response.success),
catchError(() => of(false))
);
}
}Caché de Configuraciones
@Injectable({ providedIn: 'root' })
export class CodeCacheService {
private cache = new Map<string, CbmDeclarationCodeConfigurationModel.ListResponse[]>();
private readonly CACHE_DURATION = 30 * 60 * 1000; // 30 minutos
constructor(private declarationCodeRepository: CbmDeclarationCodeConfigurationRepository) {}
getCodes(params: CbmDeclarationCodeConfigurationModel.ListParams, forceRefresh = false): Observable<CbmDeclarationCodeConfigurationModel.ListResponse[]> {
const cacheKey = JSON.stringify(params);
const cached = this.cache.get(cacheKey);
if (cached && !forceRefresh && !this.isCacheExpired(cached)) {
return of(cached);
}
return this.declarationCodeRepository.list(params).pipe(
tap(response => {
this.cache.set(cacheKey, response);
})
);
}
private isCacheExpired(codes: CbmDeclarationCodeConfigurationModel.ListResponse[]): boolean {
if (codes.length === 0) return true;
const mostRecent = Math.max(...codes.map(code => code.updated_at || code.created_at));
return Date.now() - mostRecent > this.CACHE_DURATION;
}
invalidateCache() {
this.cache.clear();
}
}📋 Lista de Verificación para Implementación
- [ ] ✅ Configurar el módulo
CbmDeclarationCodeConfigurationModule.forRoot()en el AppModule - [ ] ✅ Inyectar
CbmDeclarationCodeConfigurationRepositoryen los componentes que lo necesiten - [ ] ✅ Implementar manejo de errores para todas las operaciones CRUD
- [ ] ✅ Validar datos antes de enviar al servidor
- [ ] ✅ Implementar indicadores de carga durante las operaciones
- [ ] ✅ Manejar estados de error y respuestas no exitosas
- [ ] ✅ Considerar implementar caché para configuraciones (cambian infrequentemente)
- [ ] ✅ Probar todas las operaciones CRUD
- [ ] ✅ Verificar funcionamiento con diferentes países y tipos
- [ ] ✅ Documentar casos de uso específicos de la aplicación
🔗 Dependencias
{
"peerDependencies": {
"@angular/common": "20.1.5",
"@angular/core": "20.1.5",
"@angular/common/http": "20.1.5"
},
"dependencies": {
"tslib": "2.3.0"
}
}📝 Notas de Versión
v0.0.1
- ✅ Implementación inicial del repositorio de códigos de declaración
- ✅ Soporte completo para operaciones CRUD
- ✅ Gestión de códigos brutos, netos e impositivos
- ✅ Filtrado por estado y nombre
- ✅ Arquitectura basada en patrón Repository
- ✅ Configuración simplificada con
forRoot() - ✅ Compatibilidad con Angular 20.1.5
- ✅ Interfaces TypeScript tipadas
- ✅ Documentación completa en español
🤝 Contribución
Para contribuir a esta biblioteca:
- Fork el repositorio
- Crear una rama para la nueva funcionalidad
- Implementar los cambios siguiendo los patrones establecidos
- Agregar pruebas unitarias si es necesario
- Enviar un Pull Request con descripción detallada
📞 Soporte
Para soporte técnico o consultas sobre el uso de la biblioteca, contactar al equipo de desarrollo de CBM.
CBM (Contabilidad y Facturación Moderna) - Repositorio de Códigos de Declaración v0.0.1
