erp-core/orchestration/directivas/DIRECTIVA-HERENCIA-MODULOS.md
rckrdmrd 4c4e27d9ba feat: Documentation and orchestration updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 05:35:20 -06:00

16 KiB

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

// 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

// 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
}
// 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:

-- 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:

// VERTICAL: construccion/backend/src/modules/partners/services/partner-construction.service.ts
@Injectable()
export class PartnerConstructionService {
  constructor(
    @InjectRepository(PartnerEntity)
    private partnerRepo: Repository<PartnerEntity>,
    @InjectRepository(PartnerConstructionExtEntity)
    private extRepo: Repository<PartnerConstructionExtEntity>,
  ) {}

  async createWithExtension(dto: CreatePartnerConstructionDto): Promise<PartnerWithExtension> {
    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<PartnerWithExtension> {
    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:

// 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

// CORE: erp-core/backend/src/modules/sales/services/sale-order.service.ts
@Injectable()
export class SaleOrderService {
  async create(dto: CreateSaleOrderDto): Promise<SaleOrderEntity> {
    // 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<void> {
    const partner = await this.partnerService.findOne(partnerId);
    if (!partner) throw new NotFoundException('Partner no encontrado');
  }
}
// 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<SaleOrderEntity> {
    // 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<void> {
    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<void> {
    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

-- 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

// 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