@cbm-common/service-outsourcing-repository
v0.0.1
Published
Biblioteca Angular especializada en la gestión integral de servicios de outsourcing. Proporciona una API completa para administrar servicios tercerizados con códigos únicos, categorías, listas de precios, control de estados y funcionalidades avanzadas de
Readme
Service Outsourcing Repository
Biblioteca Angular especializada en la gestión integral de servicios de outsourcing. Proporciona una API completa para administrar servicios tercerizados con códigos únicos, categorías, listas de precios, control de estados y funcionalidades avanzadas de importación/exportación.
📋 Características Principales
- ✅ Gestión Completa de Servicios: Administración de servicios de outsourcing con códigos únicos
- ✅ Múltiples Listas de Precios: Soporte para diferentes listas de precios por servicio
- ✅ Categorización Avanzada: Organización por categorías de outsourcing específicas
- ✅ Control de Estados: Habilitar/deshabilitar servicios con razones de desactivación
- ✅ Soft Delete y Restore: Eliminación lógica con posibilidad de restauración
- ✅ Búsqueda por Código: Obtener servicios rápidamente mediante códigos únicos
- ✅ Filtrado Avanzado: Búsqueda por múltiples criterios (estado, nombre, categoría, etc.)
- ✅ Importación/Exportación Excel: Plantillas y procesamiento masivo de datos
- ✅ Generación de PDFs: Documentos individuales por servicio
- ✅ 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
🚀 Instalación
npm install @cbm-common/service-outsourcing-repository⚙️ Configuración
1. Importar el Módulo
import { CbmServiceOutsourcingModule } from '@cbm-common/service-outsourcing-repository';
@NgModule({
imports: [
CbmServiceOutsourcingModule.forRoot({
baseUrl: 'https://api.cbm.com/service-outsourcing'
})
]
})
export class AppModule { }2. Configuración Standalone (Angular 20+)
import { CbmServiceOutsourcingModule } from '@cbm-common/service-outsourcing-repository';
@Component({
standalone: true,
imports: [CbmServiceOutsourcingModule.forRoot({
baseUrl: 'https://api.cbm.com/service-outsourcing'
})]
})
export class AppComponent { }📚 API Reference
Interfaz del Repositorio
interface ICbmServiceOutsourcingRepository {
// Listado paginado con filtros
list(params: CbmOutsourcingServiceModel.ListParams): Observable<CbmOutsourcingServiceModel.ListResponse>;
// Obtener servicio por ID
getOne(id: string): Observable<CbmOutsourcingServiceModel.GetOneResponse>;
// Obtener servicio por código único
getOneByCode(code: string): Observable<CbmOutsourcingServiceModel.GetOneByCodeResponse>;
// Crear nuevo servicio
save(data: CbmOutsourcingServiceModel.SaveBody): Observable<CbmOutsourcingServiceModel.ConfirmResponse>;
// Actualizar servicio existente
update(id: string, data: CbmOutsourcingServiceModel.UpdateBody): Observable<CbmOutsourcingServiceModel.ConfirmResponse>;
// Cambiar estado del servicio
changeStatus(id: string, data: CbmOutsourcingServiceModel.ChangeStatusBody): Observable<CbmOutsourcingServiceModel.ConfirmResponse>;
// Restaurar servicio eliminado
restore(id: string, data: CbmOutsourcingServiceModel.RestoreBody): Observable<CbmOutsourcingServiceModel.ConfirmResponse>;
// Eliminar servicio (soft delete)
delete(id: string): Observable<CbmOutsourcingServiceModel.ConfirmResponse>;
// Exportar a Excel
downloadExcel(params: CbmOutsourcingServiceModel.DownloadExcelParams): Observable<HttpResponse<Blob>>;
// Descargar plantilla Excel para carga
downloadExcelCreateTemplate(): Observable<HttpResponse<Blob>>;
// Importar desde Excel
importExcelCreate(data: FormData): Observable<HttpResponse<Blob>>;
// Descargar PDF individual
downloadIndividualPdf(id: string): Observable<HttpResponse<Blob>>;
}💡 Uso Básico
Inyección del Servicio
import { CbmServiceOutsourcingRepository } from '@cbm-common/service-outsourcing-repository';
@Component({
selector: 'app-outsourcing-manager'
})
export class OutsourcingManagerComponent {
services: CbmOutsourcingServiceModel.ListResponse.Item[] = [];
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {
this.loadServices();
}
loadServices() {
const params: CbmOutsourcingServiceModel.ListParams = {
page: 1,
size: 20,
enabled: true,
name: 'Desarrollo'
};
this.outsourcingRepository.list(params).subscribe({
next: (response) => {
if (response.success) {
this.services = response.items;
console.log('Servicios cargados:', this.services);
}
},
error: (error) => {
console.error('Error al cargar servicios:', error);
}
});
}
}Crear Nuevo Servicio
createNewService() {
const serviceData: CbmOutsourcingServiceModel.SaveBody = {
category_outsourcing_id: 'cat-123',
tax_iva_id: 'iva-456',
tax_iva_code: 'IVA12',
code: 'OUT-001',
barcode: '789456123',
name: 'Desarrollo de Software Personalizado',
comment: 'Servicio de desarrollo a medida',
iva: 12,
tax_ice: false,
automatic_code: false,
price_lists: [
{
price_list_id: 'price-001',
base_price: 5000.00,
tax_price: 5600.00
}
]
};
this.outsourcingRepository.save(serviceData).subscribe({
next: (response) => {
if (response.success) {
console.log('Servicio creado exitosamente');
this.loadServices(); // Recargar lista
}
},
error: (error) => console.error('Error al crear servicio:', error)
});
}Actualizar Servicio Existente
updateExistingService(serviceId: string) {
const updateData: CbmOutsourcingServiceModel.UpdateBody = {
name: 'Desarrollo de Software Empresarial',
comment: 'Servicio actualizado con nuevas funcionalidades',
price_lists: {
data: [
{
_id: 'price-list-id-123',
price_list_id: 'price-001',
base_price: 6500.00,
tax_price: 7280.00
}
],
deleted_records: ['old-price-id-456']
}
};
this.outsourcingRepository.update(serviceId, updateData).subscribe({
next: (response) => {
if (response.success) {
console.log('Servicio actualizado exitosamente');
this.loadServices();
}
},
error: (error) => console.error('Error al actualizar servicio:', error)
});
}Buscar por Código
findServiceByCode() {
const code = 'OUT-001';
this.outsourcingRepository.getOneByCode(code).subscribe({
next: (response) => {
if (response.success) {
console.log('Servicio encontrado:', response.data);
console.log('Nombre:', response.data.name);
console.log('Categoría:', response.data.category_name);
console.log('Listas de precios:', response.data.price_list_service_outsourcing);
}
},
error: (error) => console.error('Servicio no encontrado:', error)
});
}Cambiar Estado del Servicio
toggleServiceStatus(serviceId: string, enable: boolean) {
const statusData: CbmOutsourcingServiceModel.ChangeStatusBody = {
enabled: enable,
disabled_reason: enable ? undefined : 'Servicio temporalmente suspendido'
};
this.outsourcingRepository.changeStatus(serviceId, statusData).subscribe({
next: (response) => {
if (response.success) {
console.log(`Servicio ${enable ? 'habilitado' : 'deshabilitado'} exitosamente`);
this.loadServices();
}
},
error: (error) => console.error('Error al cambiar estado:', error)
});
}Restaurar Servicio Eliminado
restoreDeletedService(serviceId: string) {
const restoreData: CbmOutsourcingServiceModel.RestoreBody = {
reason: 'Restauración solicitada por el cliente'
};
this.outsourcingRepository.restore(serviceId, restoreData).subscribe({
next: (response) => {
if (response.success) {
console.log('Servicio restaurado exitosamente');
this.loadServices();
}
},
error: (error) => console.error('Error al restaurar servicio:', error)
});
}📊 Modelos de Datos
Parámetros de Búsqueda
interface ListParams {
page: number; // Número de página (1-based)
size: number; // Tamaño de página
deleted?: boolean; // Incluir servicios eliminados
enabled?: boolean; // Filtrar por estado activo
code_filter?: string; // Filtrar por código
name?: string; // Filtrar por nombre
company_branch_id?: string; // Filtrar por sucursal
category_id?: string; // Filtrar por categoría
tax_iva_id?: string; // Filtrar por impuesto IVA
price_list_id?: string; // Filtrar por lista de precios
}Servicio de Outsourcing
interface Item {
_id: string; // ID único del servicio
company_id: string; // ID de la empresa
company_branch_id: string; // ID de la sucursal
category_outsourcing_id: string; // ID de la categoría de outsourcing
tax_iva_id: string; // ID del impuesto IVA
tax_iva_code: string; // Código del IVA
automatic_code: boolean; // Generación automática de código
code: string; // Código único del servicio
barcode: string; // Código de barras
name: string; // Nombre del servicio
comment: string; // Comentarios adicionales
iva: number; // Porcentaje de IVA
tax_ice: boolean; // Aplica impuesto ICE
enabled: boolean; // Estado activo/inactivo
created_user: string; // Usuario que creó
created_at: number; // Fecha de creación (timestamp)
category_name: string; // Nombre de la categoría
tax_iva: TaxIva; // Información del impuesto IVA
price_list_service_outsourcing: PriceListOutsourcing[]; // Listas de precios
disabled_reason?: string; // Razón de desactivación
updated_at?: number; // Fecha de actualización
updated_user?: string; // Usuario que actualizó
loading?: boolean; // Estado de carga (UI)
cost?: number; // Costo del servicio
bulk_load?: boolean; // Carga masiva
deleted_at?: number; // Fecha de eliminación (soft delete)
deleted?: boolean; // Indicador de eliminación
deleted_user?: string; // Usuario que eliminó
company_branch_name: string; // Nombre de la sucursal
tax_iva_description: string; // Descripción del IVA
tax_iva_rate: number; // Tasa del IVA
}Impuesto IVA
interface TaxIva {
_id: string; // ID único del impuesto
country_id?: string; // ID del país
code?: string; // Código del impuesto
percentage?: number; // Porcentaje del impuesto
description?: string; // Descripción del impuesto
enabled?: boolean; // Estado activo
created_at?: number; // Fecha de creación
created_user?: string; // Usuario que creó
}Lista de Precios
interface PriceListOutsourcing {
_id: string; // ID único de la relación precio-servicio
price_list_id?: string; // ID de la lista de precios
price_list_name?: string; // Nombre de la lista de precios
base_price?: number; // Precio base
tax_price?: number; // Precio con impuestos
created_user?: string; // Usuario que creó
created_at?: number; // Fecha de creación
updated_at?: number; // Fecha de actualización
updated_user?: string; // Usuario que actualizó
company_id?: string; // ID de la empresa
company_branch_id?: string; // ID de la sucursal
country_id?: string; // ID del país
service_outsourcing_id?: string; // ID del servicio
}Datos para Crear Servicio
interface SaveBody {
category_outsourcing_id: string; // ID de categoría (requerido)
tax_iva_id: string; // ID del IVA (requerido)
tax_iva_code: string; // Código del IVA (requerido)
code: string; // Código único (requerido)
barcode: string; // Código de barras (requerido)
name: string; // Nombre (requerido)
comment: string; // Comentarios (requerido)
iva: number; // Porcentaje IVA (requerido)
tax_ice: boolean; // Aplica ICE (requerido)
automatic_code: boolean; // Código automático (requerido)
price_lists?: PriceListData[]; // Listas de precios (opcional)
}Ejemplo de Datos
{
"_id": "64f1a2b3c4d5e6f7g8h9i0j1",
"company_id": "company-123",
"company_branch_id": "branch-456",
"category_outsourcing_id": "cat-out-789",
"tax_iva_id": "iva-101",
"tax_iva_code": "IVA12",
"automatic_code": false,
"code": "OUT-001",
"barcode": "789456123",
"name": "Desarrollo de Software Personalizado",
"comment": "Servicio de desarrollo a medida",
"iva": 12,
"tax_ice": false,
"enabled": true,
"created_user": "admin",
"created_at": 1694000000000,
"category_name": "Desarrollo de Software",
"tax_iva": {
"_id": "iva-101",
"code": "IVA12",
"percentage": 12,
"description": "IVA 12%"
},
"price_list_service_outsourcing": [
{
"_id": "price-123",
"price_list_name": "Lista General",
"base_price": 5000.00,
"tax_price": 5600.00
}
],
"company_branch_name": "Sucursal Principal",
"tax_iva_description": "IVA 12%",
"tax_iva_rate": 12
}🔍 Casos de Uso Comunes
1. Sistema de Gestión de Servicios
@Component({
selector: 'app-service-management'
})
export class ServiceManagementComponent {
services: CbmOutsourcingServiceModel.ListResponse.Item[] = [];
filteredServices: CbmOutsourcingServiceModel.ListResponse.Item[] = [];
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {
this.loadAllServices();
}
loadAllServices() {
const params: CbmOutsourcingServiceModel.ListParams = {
page: 1,
size: 100,
enabled: true
};
this.outsourcingRepository.list(params).subscribe({
next: (response) => {
if (response.success) {
this.services = response.items;
this.filteredServices = response.items;
}
}
});
}
filterByCategory(categoryId: string) {
this.filteredServices = this.services.filter(
service => service.category_outsourcing_id === categoryId
);
}
getServicesByPriceRange(minPrice: number, maxPrice: number) {
return this.services.filter(service => {
const prices = service.price_list_service_outsourcing || [];
return prices.some(price =>
price.base_price && price.base_price >= minPrice && price.base_price <= maxPrice
);
});
}
}2. Dashboard de Servicios de Outsourcing
@Component({
selector: 'app-outsourcing-dashboard'
})
export class OutsourcingDashboardComponent {
stats = {
totalServices: 0,
activeServices: 0,
inactiveServices: 0,
deletedServices: 0,
servicesByCategory: new Map<string, number>(),
totalRevenue: 0
};
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {
this.loadDashboardStats();
}
loadDashboardStats() {
// Cargar servicios activos
const activeParams: CbmOutsourcingServiceModel.ListParams = {
page: 1,
size: 1000,
enabled: true,
deleted: false
};
this.outsourcingRepository.list(activeParams).subscribe({
next: (response) => {
if (response.success) {
this.stats.activeServices = response.total;
this.calculateRevenue(response.items);
this.calculateCategoryStats(response.items);
}
}
});
// Cargar servicios inactivos
const inactiveParams: CbmOutsourcingServiceModel.ListParams = {
page: 1,
size: 1000,
enabled: false,
deleted: false
};
this.outsourcingRepository.list(inactiveParams).subscribe({
next: (response) => {
if (response.success) {
this.stats.inactiveServices = response.total;
}
}
});
// Cargar servicios eliminados
const deletedParams: CbmOutsourcingServiceModel.ListParams = {
page: 1,
size: 1000,
deleted: true
};
this.outsourcingRepository.list(deletedParams).subscribe({
next: (response) => {
if (response.success) {
this.stats.deletedServices = response.total;
this.stats.totalServices = this.stats.activeServices +
this.stats.inactiveServices +
this.stats.deletedServices;
}
}
});
}
private calculateRevenue(services: CbmOutsourcingServiceModel.ListResponse.Item[]) {
this.stats.totalRevenue = services.reduce((total, service) => {
const prices = service.price_list_service_outsourcing || [];
const maxPrice = Math.max(...prices.map(p => p.tax_price || 0));
return total + maxPrice;
}, 0);
}
private calculateCategoryStats(services: CbmOutsourcingServiceModel.ListResponse.Item[]) {
services.forEach(service => {
const category = service.category_name;
const current = this.stats.servicesByCategory.get(category) || 0;
this.stats.servicesByCategory.set(category, current + 1);
});
}
}3. Generador de Catálogos de Servicios
@Component({
selector: 'app-service-catalog'
})
export class ServiceCatalogComponent {
catalogData: any[] = [];
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {
this.generateCatalog();
}
generateCatalog() {
const params: CbmOutsourcingServiceModel.ListParams = {
page: 1,
size: 500,
enabled: true
};
this.outsourcingRepository.list(params).subscribe({
next: (response) => {
if (response.success) {
this.catalogData = response.items.map(service => ({
codigo: service.code,
nombre: service.name,
categoria: service.category_name,
descripcion: service.comment,
precio_base: this.getMinPrice(service),
precio_impuestos: this.getMaxPrice(service),
iva: service.iva,
ice: service.tax_ice,
sucursal: service.company_branch_name
}));
}
}
});
}
private getMinPrice(service: CbmOutsourcingServiceModel.ListResponse.Item): number {
const prices = service.price_list_service_outsourcing || [];
return Math.min(...prices.map(p => p.base_price || 0));
}
private getMaxPrice(service: CbmOutsourcingServiceModel.ListResponse.Item): number {
const prices = service.price_list_service_outsourcing || [];
return Math.max(...prices.map(p => p.tax_price || 0));
}
}4. Sistema de Importación/Exportación
@Component({
selector: 'app-data-management'
})
export class DataManagementComponent {
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {}
exportServicesToExcel() {
const params: CbmOutsourcingServiceModel.DownloadExcelParams = {
enabled: true,
deleted: false
};
this.outsourcingRepository.downloadExcel(params).subscribe({
next: (response) => {
const blob = new Blob([response.body!], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `servicios-outsourcing-${new Date().toISOString().split('T')[0]}.xlsx`;
link.click();
window.URL.revokeObjectURL(url);
},
error: (error) => console.error('Error al exportar:', error)
});
}
downloadTemplate() {
this.outsourcingRepository.downloadExcelCreateTemplate().subscribe({
next: (response) => {
const blob = new Blob([response.body!], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'plantilla-servicios-outsourcing.xlsx';
link.click();
window.URL.revokeObjectURL(url);
}
});
}
importServicesFromExcel(file: File) {
const formData = new FormData();
formData.append('file', file);
this.outsourcingRepository.importExcelCreate(formData).subscribe({
next: (response) => {
console.log('Importación completada');
// Recargar lista de servicios
this.loadServices();
},
error: (error) => console.error('Error en importación:', error)
});
}
downloadServicePdf(serviceId: string) {
this.outsourcingRepository.downloadIndividualPdf(serviceId).subscribe({
next: (response) => {
const blob = new Blob([response.body!], { type: 'application/pdf' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `servicio-${serviceId}.pdf`;
link.click();
window.URL.revokeObjectURL(url);
}
});
}
}🏗️ 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 ICbmServiceOutsourcingRepository {
list(params: CbmOutsourcingServiceModel.ListParams): Observable<CbmOutsourcingServiceModel.ListResponse>;
getOne(id: string): Observable<CbmOutsourcingServiceModel.GetOneResponse>;
save(data: CbmOutsourcingServiceModel.SaveBody): Observable<CbmOutsourcingServiceModel.ConfirmResponse>;
// ... otros métodos
}
// Implementación del servicio
@Injectable({ providedIn: 'root' })
export class CbmServiceOutsourcingService implements ICbmServiceOutsourcingRepository {
constructor(
private readonly http: HttpClient,
@Inject(SERVICE_OUTSOURCING_MODULE_CONFIG) private readonly config: ICbmServiceOutsourcingModuleConfig
) {}
list(params: CbmOutsourcingServiceModel.ListParams): Observable<CbmOutsourcingServiceModel.ListResponse> {
return this.http.get<CbmOutsourcingServiceModel.ListResponse>(this.config.baseUrl, { params: { ...params } });
}
// ... implementación de métodos
}
// Wrapper del repositorio
@Injectable({ providedIn: 'root' })
export class CbmServiceOutsourcingRepository implements ICbmServiceOutsourcingRepository {
constructor(private service: CbmServiceOutsourcingService) {}
list(params: CbmOutsourcingServiceModel.ListParams): Observable<CbmOutsourcingServiceModel.ListResponse> {
return this.service.list(params);
}
// ... delegación a service
}Configuración Centralizada
// Configuración del módulo
@NgModule({
imports: [
CbmServiceOutsourcingModule.forRoot({
baseUrl: environment.apiUrl + '/service-outsourcing'
})
]
})
export class AppModule {}🔧 Mejores Prácticas
Manejo de Errores
@Component({
selector: 'app-safe-service-handler'
})
export class SafeServiceHandlerComponent {
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {}
safeLoadServices() {
const params: CbmOutsourcingServiceModel.ListParams = {
page: 1,
size: 20,
enabled: true
};
this.outsourcingRepository.list(params).subscribe({
next: (response) => {
if (response.success && response.items) {
this.processServices(response.items);
} else {
this.showErrorMessage('Respuesta inválida del servidor');
}
},
error: (error) => {
if (error.status === 404) {
this.showErrorMessage('Servicio de outsourcing no disponible');
} else if (error.status === 500) {
this.showErrorMessage('Error interno del servidor');
} else {
this.showErrorMessage('Error de conexión');
}
}
});
}
private processServices(services: CbmOutsourcingServiceModel.ListResponse.Item[]) {
// Procesar servicios de manera segura
services.forEach(service => {
if (service.code && service.name) {
// Procesar servicio válido
} else {
console.warn(`Servicio inválido: ${service._id}`);
}
});
}
private showErrorMessage(message: string) {
// Mostrar mensaje de error al usuario
}
}Validación de Datos
@Injectable({ providedIn: 'root' })
export class ServiceValidationService {
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {}
validateServiceData(data: CbmOutsourcingServiceModel.SaveBody): string[] {
const errors: string[] = [];
if (!data.code?.trim()) {
errors.push('El código es obligatorio');
}
if (!data.name?.trim()) {
errors.push('El nombre es obligatorio');
}
if (!data.category_outsourcing_id) {
errors.push('La categoría es obligatoria');
}
if (!data.tax_iva_id) {
errors.push('El impuesto IVA es obligatorio');
}
if (data.iva === undefined || data.iva < 0 || data.iva > 100) {
errors.push('El porcentaje de IVA debe estar entre 0 y 100');
}
if (data.price_lists && data.price_lists.length > 0) {
data.price_lists.forEach((price, index) => {
if (!price.price_list_id) {
errors.push(`La lista de precios ${index + 1} debe tener un ID`);
}
if (price.base_price === undefined || price.base_price < 0) {
errors.push(`El precio base ${index + 1} debe ser válido`);
}
});
}
return errors;
}
validateAndSave(data: CbmOutsourcingServiceModel.SaveBody): Observable<boolean> {
const errors = this.validateServiceData(data);
if (errors.length > 0) {
console.error('Errores de validación:', errors);
return of(false);
}
return this.outsourcingRepository.save(data).pipe(
map(response => response.success),
catchError(() => of(false))
);
}
}Caché de Servicios
@Injectable({ providedIn: 'root' })
export class ServiceCacheService {
private cache = new Map<string, CbmOutsourcingServiceModel.ListResponse.Item[]>();
private readonly CACHE_DURATION = 15 * 60 * 1000; // 15 minutos
constructor(private outsourcingRepository: CbmServiceOutsourcingRepository) {}
getServices(params: CbmOutsourcingServiceModel.ListParams, forceRefresh = false): Observable<CbmOutsourcingServiceModel.ListResponse.Item[]> {
const cacheKey = JSON.stringify(params);
const cached = this.cache.get(cacheKey);
if (cached && !forceRefresh && !this.isCacheExpired(cached)) {
return of(cached);
}
return this.outsourcingRepository.list(params).pipe(
map(response => {
if (response.success) {
this.cache.set(cacheKey, response.items);
return response.items;
}
throw new Error('Error al obtener servicios');
})
);
}
private isCacheExpired(services: CbmOutsourcingServiceModel.ListResponse.Item[]): boolean {
if (services.length === 0) return true;
const mostRecent = Math.max(...services.map(service => service.updated_at || service.created_at));
return Date.now() - mostRecent > this.CACHE_DURATION;
}
invalidateCache() {
this.cache.clear();
}
}📋 Lista de Verificación para Implementación
- [ ] ✅ Configurar el módulo
CbmServiceOutsourcingModule.forRoot()en el AppModule - [ ] ✅ Inyectar
CbmServiceOutsourcingRepositoryen 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 servicios (cambian con frecuencia moderada)
- [ ] ✅ Probar todas las operaciones CRUD
- [ ] ✅ Verificar funcionamiento de importación/exportación Excel
- [ ] ✅ Probar generación de PDFs individuales
- [ ] ✅ 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 servicios de outsourcing
- ✅ Soporte completo para operaciones CRUD
- ✅ Gestión de listas de precios múltiples
- ✅ Control de estados con razones de desactivación
- ✅ Soft delete y restore de servicios
- ✅ Búsqueda por código único
- ✅ Filtrado avanzado por múltiples criterios
- ✅ Funcionalidades de importación/exportación Excel
- ✅ Generación de PDFs individuales
- ✅ 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 Servicios de Outsourcing v0.0.1
