# DIRECTIVA: Herencia y Extension de Modulos **Proyecto:** ERP Core - Base Generica Reutilizable **Version:** 1.0.0 **Fecha:** 2025-12-05 **Aplicable a:** ERP Core y todas las verticales **Estado:** OBLIGATORIO --- ## PRINCIPIO FUNDAMENTAL > **"Las verticales EXTIENDEN el core, nunca lo modifican"** El ERP Core proporciona el 60-70% de funcionalidad comun. Las verticales agregan el 30-40% restante especifico de su giro. --- ## ARQUITECTURA DE HERENCIA ``` ┌─────────────────────────────────────────────────────────────────┐ │ ERP CORE │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Modulos Genericos (MGN-*) │ │ │ │ • Auth, Users, Roles, Tenants │ │ │ │ • Partners, Products, Catalogs │ │ │ │ • Sales, Purchases, Inventory │ │ │ │ • Financial, HR, CRM, Projects │ │ │ └─────────────────────────────────────────────────────────┘ │ └────────────────────────────┬────────────────────────────────────┘ │ ┌────────────────────┼────────────────────┐ ↓ ↓ ↓ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ CONSTRUCCION │ │VIDRIO TEMPLADO│ │MECANICAS DIESEL│ │ (MAI-*) │ │ (MVT-*) │ │ (MMD-*) │ ├───────────────┤ ├───────────────┤ ├───────────────┤ │ +Proyectos │ │ +Produccion │ │ +Ordenes Srv │ │ +Presupuestos │ │ +Corte Optimo │ │ +Diagnostico │ │ +Control Obra │ │ +Templado │ │ +Refacciones │ │ +INFONAVIT │ │ +Instalacion │ │ +Flotillas │ └───────────────┘ └───────────────┘ └───────────────┘ ``` --- ## TIPOS DE HERENCIA ### 1. Uso Directo (Sin Modificacion) La vertical usa el modulo del core tal cual. **Ejemplo:** Modulo de autenticacion ```typescript // En vertical construccion // No se crea nada nuevo, se usa directamente import { AuthModule } from '@erp-shared/modules/auth'; @Module({ imports: [AuthModule], // Usa directo del core }) export class ConstruccionModule {} ``` **Cuando usar:** - Autenticacion (MGN-001) - Usuarios base (MGN-002) - Roles y permisos (MGN-003) - Multi-tenancy (MGN-004) - Catalogos maestros (MGN-005) - Configuracion sistema (MGN-006) - Auditoria (MGN-007) --- ### 2. Extension de Entidad (Campos Adicionales) La vertical agrega campos a una entidad del core sin modificarla. **Ejemplo:** Partner con campos de construccion ```typescript // CORE: apps/erp-core/backend/src/modules/partners/entities/partner.entity.ts @Entity({ schema: 'core_partners', name: 'partners' }) export class PartnerEntity extends BaseEntity { @Column({ length: 200 }) name: string; @Column({ name: 'is_company', default: false }) isCompany: boolean; @Column({ name: 'tax_id', length: 20, nullable: true }) taxId: string; // ... campos base } ``` ```typescript // VERTICAL: apps/verticales/construccion/backend/src/modules/partners/entities/partner-extension.entity.ts @Entity({ schema: 'vertical_construccion', name: 'partner_extensions' }) export class PartnerConstructionExtEntity { @PrimaryColumn('uuid') id: string; // Mismo ID que partner del core @OneToOne(() => PartnerEntity) @JoinColumn({ name: 'id' }) partner: PartnerEntity; // Campos especificos de construccion @Column({ name: 'contractor_license', length: 50, nullable: true }) contractorLicense: string; @Column({ name: 'infonavit_number', length: 20, nullable: true }) infonavitNumber: string; @Column({ name: 'specialty', type: 'enum', enum: ContractorSpecialty, nullable: true }) specialty: ContractorSpecialty; @Column({ name: 'insurance_policy', length: 50, nullable: true }) insurancePolicy: string; @Column({ name: 'insurance_expiry', type: 'date', nullable: true }) insuranceExpiry: Date; } ``` **DDL Extension:** ```sql -- Schema de vertical CREATE SCHEMA IF NOT EXISTS vertical_construccion; -- Tabla de extension (1:1 con core) CREATE TABLE vertical_construccion.partner_extensions ( id UUID PRIMARY KEY REFERENCES core_partners.partners(id) ON DELETE CASCADE, contractor_license VARCHAR(50), infonavit_number VARCHAR(20), specialty VARCHAR(50), insurance_policy VARCHAR(50), insurance_expiry DATE, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Vista unificada para consultas CREATE VIEW vertical_construccion.v_partners_full AS SELECT p.*, ext.contractor_license, ext.infonavit_number, ext.specialty, ext.insurance_policy, ext.insurance_expiry FROM core_partners.partners p LEFT JOIN vertical_construccion.partner_extensions ext ON p.id = ext.id; ``` **Service de Extension:** ```typescript // VERTICAL: construccion/backend/src/modules/partners/services/partner-construction.service.ts @Injectable() export class PartnerConstructionService { constructor( @InjectRepository(PartnerEntity) private partnerRepo: Repository, @InjectRepository(PartnerConstructionExtEntity) private extRepo: Repository, ) {} async createWithExtension(dto: CreatePartnerConstructionDto): Promise { return this.dataSource.transaction(async (manager) => { // 1. Crear partner base (core) const partner = manager.create(PartnerEntity, { name: dto.name, isCompany: dto.isCompany, taxId: dto.taxId, tenantId: dto.tenantId, }); await manager.save(partner); // 2. Crear extension (vertical) const extension = manager.create(PartnerConstructionExtEntity, { id: partner.id, // Mismo ID contractorLicense: dto.contractorLicense, infonavitNumber: dto.infonavitNumber, specialty: dto.specialty, }); await manager.save(extension); return { ...partner, ...extension }; }); } async findWithExtension(id: string): Promise { const partner = await this.partnerRepo.findOne({ where: { id } }); const extension = await this.extRepo.findOne({ where: { id } }); return { ...partner, ...extension }; } } ``` --- ### 3. Modulo Nuevo (Especifico de Vertical) La vertical crea modulos completamente nuevos que no existen en el core. **Ejemplo:** Control de Obra (solo construccion) ``` verticales/construccion/backend/src/modules/control-obra/ ├── control-obra.module.ts ├── control-obra.controller.ts ├── control-obra.service.ts ├── entities/ │ ├── avance-obra.entity.ts │ ├── reporte-diario.entity.ts │ └── partida-presupuesto.entity.ts ├── dto/ │ ├── create-avance.dto.ts │ └── update-avance.dto.ts └── __tests__/ ``` **Relaciones con Core:** ```typescript // verticales/construccion/backend/src/modules/control-obra/entities/avance-obra.entity.ts @Entity({ schema: 'vertical_construccion', name: 'avances_obra' }) export class AvanceObraEntity extends BaseEntity { // FK a proyecto (del core projects o extension) @ManyToOne(() => ProjectEntity) @JoinColumn({ name: 'project_id' }) project: ProjectEntity; // FK a usuario del core @ManyToOne(() => UserEntity) @JoinColumn({ name: 'supervisor_id' }) supervisor: UserEntity; // Campos especificos de control de obra @Column({ type: 'date' }) fecha: Date; @Column({ name: 'porcentaje_avance', type: 'decimal', precision: 5, scale: 2 }) porcentajeAvance: number; @Column({ type: 'text', nullable: true }) observaciones: string; @OneToMany(() => ReporteDiarioEntity, r => r.avance) reportes: ReporteDiarioEntity[]; } ``` --- ### 4. Override de Comportamiento (Herencia de Service) La vertical modifica el comportamiento de un service del core. **Ejemplo:** Validacion adicional en ventas ```typescript // CORE: erp-core/backend/src/modules/sales/services/sale-order.service.ts @Injectable() export class SaleOrderService { async create(dto: CreateSaleOrderDto): Promise { // Validaciones base await this.validatePartner(dto.partnerId); await this.validateProducts(dto.lines); // Crear orden return this.repository.save(dto); } protected async validatePartner(partnerId: string): Promise { const partner = await this.partnerService.findOne(partnerId); if (!partner) throw new NotFoundException('Partner no encontrado'); } } ``` ```typescript // VERTICAL: construccion/backend/src/modules/sales/services/sale-order-construction.service.ts @Injectable() export class SaleOrderConstructionService extends SaleOrderService { constructor( // Inyectar dependencias del core partnerService: PartnerService, // Agregar dependencias de vertical private projectService: ProjectService, private budgetService: BudgetService, ) { super(partnerService); } // Override del metodo create async create(dto: CreateSaleOrderConstructionDto): Promise { // Validaciones adicionales de construccion await this.validateProject(dto.projectId); await this.validateBudget(dto.projectId, dto.lines); // Llamar al metodo base const order = await super.create(dto); // Crear extension await this.createExtension(order.id, dto); return order; } private async validateProject(projectId: string): Promise { const project = await this.projectService.findOne(projectId); if (!project) throw new NotFoundException('Proyecto no encontrado'); if (project.status === 'cancelled') { throw new BadRequestException('No se puede vender a proyecto cancelado'); } } private async validateBudget(projectId: string, lines: SaleOrderLineDto[]): Promise { const budget = await this.budgetService.getAvailable(projectId); const total = lines.reduce((sum, l) => sum + l.quantity * l.priceUnit, 0); if (total > budget) { throw new BadRequestException('Monto excede presupuesto disponible'); } } } ``` --- ## REGLAS DE HERENCIA ### PERMITIDO | Accion | Descripcion | Ejemplo | |--------|-------------|---------| | Usar directo | Importar modulo sin cambios | `import { AuthModule }` | | Extender entidad | Agregar campos via tabla 1:1 | `partner_extensions` | | Crear modulo nuevo | Modulo especifico de vertical | `control-obra` | | Override service | Heredar y agregar logica | `extends SaleOrderService` | | Agregar endpoints | Nuevos endpoints en vertical | `POST /api/construccion/avances` | | Crear vistas | Vistas que combinan core + vertical | `v_partners_full` | ### PROHIBIDO | Accion | Razon | Alternativa | |--------|-------|-------------| | Modificar entidad core | Rompe otras verticales | Crear tabla extension | | Eliminar campos core | Rompe funcionalidad base | Usar `is_active` | | Modificar DDL core | Afecta todas las verticales | Crear schema vertical | | Fork de modulo core | Duplicacion, mantenimiento | Heredar/extender | | Modificar API core | Rompe contratos | Crear endpoints propios | --- ## ESTRUCTURA DE SCHEMAS ```sql -- Schemas del CORE (no modificar) core_system -- Tenants, sequences, config core_auth -- Users, sessions, tokens core_partners -- Partners, addresses, contacts core_products -- Products, variants, categories core_sales -- Sale orders, lines core_purchases -- Purchase orders, lines core_inventory -- Stock, moves, locations core_financial -- Accounts, invoices, payments core_hr -- Employees, contracts core_crm -- Leads, opportunities core_projects -- Projects genericos -- Schemas de VERTICALES (extensiones) vertical_construccion -- Extension para construccion vertical_vidrio -- Extension para vidrio templado vertical_mecanicas -- Extension para mecanicas diesel vertical_clinicas -- Extension para clinicas vertical_retail -- Extension para punto de venta ``` --- ## PATRON DE MODULO VERTICAL ```typescript // verticales/{vertical}/backend/src/modules/{modulo}/{modulo}.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; // Importar del CORE (uso directo) import { PartnersModule } from '@erp-shared/modules/partners'; import { ProductsModule } from '@erp-shared/modules/products'; import { SalesModule } from '@erp-shared/modules/sales'; // Entidades locales (extensiones + nuevas) import { PartnerConstructionExtEntity } from './entities/partner-extension.entity'; import { ProjectObraEntity } from './entities/project-obra.entity'; import { AvanceObraEntity } from './entities/avance-obra.entity'; // Services locales import { PartnerConstructionService } from './services/partner-construction.service'; import { ProjectObraService } from './services/project-obra.service'; import { ControlObraService } from './services/control-obra.service'; // Controllers locales import { PartnerConstructionController } from './controllers/partner-construction.controller'; import { ProjectObraController } from './controllers/project-obra.controller'; import { ControlObraController } from './controllers/control-obra.controller'; @Module({ imports: [ // Modulos del core (uso directo) PartnersModule, ProductsModule, SalesModule, // Entidades locales TypeOrmModule.forFeature([ PartnerConstructionExtEntity, ProjectObraEntity, AvanceObraEntity, ]), ], controllers: [ PartnerConstructionController, ProjectObraController, ControlObraController, ], providers: [ PartnerConstructionService, ProjectObraService, ControlObraService, ], exports: [ PartnerConstructionService, ProjectObraService, ControlObraService, ], }) export class ConstruccionModule {} ``` --- ## CHECKLIST DE EXTENSION Antes de extender un modulo del core, verificar: ### Analisis - [ ] Identificar que modulo del core se extiende - [ ] Documentar campos adicionales requeridos - [ ] Identificar relaciones con otros modulos - [ ] Verificar que no existe ya una extension similar ### Diseno - [ ] Crear DDL-SPEC para tabla de extension - [ ] Disenar vistas unificadas - [ ] Especificar indices necesarios - [ ] Definir FK a tablas del core ### Implementacion - [ ] Crear entidad de extension (1:1 con core) - [ ] Crear/heredar service - [ ] Crear controller con endpoints propios - [ ] Integrar en modulo de vertical ### Validacion - [ ] Funcionalidad core no afectada - [ ] Extension funciona correctamente - [ ] Tests de integracion pasan - [ ] Documentacion actualizada --- ## REFERENCIAS ### Directivas Relacionadas - `DIRECTIVA-EXTENSION-VERTICALES.md` - Como se estructuran verticales - `DIRECTIVA-MULTI-TENANT.md` - Aislamiento por tenant - `DIRECTIVA-PATRONES-ODOO.md` - Patrones de herencia Odoo ### Codigo de Referencia - Odoo module inheritance: `knowledge-base/patterns/odoo/inheritance.md` - Gamilit modules: `projects/gamilit/apps/backend/src/modules/` --- **Version:** 1.0.0 **Ultima actualizacion:** 2025-12-05 **Estado:** ACTIVA Y OBLIGATORIA