# 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-core/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<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:**
```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<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');
  }
}
```

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

```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-core/modules/partners';
import { ProductsModule } from '@erp-core/modules/products';
import { SalesModule } from '@erp-core/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
