# Vertical Development Guide

## Overview

Este documento explica cómo crear una **nueva vertical** de negocio que extiende el **erp-core** genérico. El patrón sigue el modelo de Odoo ERP: core reutilizable + extensiones especializadas por industria.

**Verticales Existentes:**
- Construcción (INFONAVIT) - 35% completado
- Vidrio Templado - 0%
- Mecánicas Diesel - 30%
- Retail (POS) - 0%
- Clínicas - 0%

## Architecture Pattern

### Core + Vertical Model

```
┌─────────────────────────────────────┐
│          ERP CORE (60-70%)          │
│  Generic modules: auth, inventory,  │
│  sales, purchases, financial, etc.  │
└────────────┬────────────────────────┘
             │ extends
             ▼
┌─────────────────────────────────────┐
│     VERTICAL (30-40%)               │
│  Industry-specific extensions       │
│  - Override methods                 │
│  - Add new modules                  │
│  - Extend database schemas          │
└─────────────────────────────────────┘
```

### Key Principles

1. **Don't modify core** - Core stays generic and reusable
2. **Extend, don't replace** - Vertical extends core modules
3. **Inheritance over duplication** - Use TypeScript class inheritance
4. **Additive database changes** - Add schemas, don't modify core schemas
5. **Separate documentation** - Each vertical has its own docs/

## Step-by-Step: Create New Vertical

### 1. Project Structure

```bash
# Create vertical directory
mkdir -p apps/verticales/my-vertical

cd apps/verticales/my-vertical

# Create standard structure
mkdir -p backend/src/{modules,shared}
mkdir -p frontend/src/{modules,shared}
mkdir -p database/ddl/schemas
mkdir -p docs
mkdir -p orchestration/{00-guidelines,trazas,estados}
```

**Result:**
```
apps/verticales/my-vertical/
├── backend/
│   └── src/
│       ├── modules/          # Industry-specific modules
│       ├── shared/           # Shared utilities
│       ├── routes/           # API routes
│       └── index.ts          # Entry point
├── frontend/
│   └── src/
│       ├── modules/          # UI modules
│       └── shared/           # Shared components
├── database/
│   ├── ddl/
│   │   └── schemas/          # Vertical schemas
│   ├── migrations/           # Database migrations
│   └── seeds/                # Test data
├── docs/                     # Vertical documentation
└── orchestration/            # Agent orchestration
    ├── 00-guidelines/
    │   └── CONTEXTO-PROYECTO.md
    ├── trazas/
    ├── estados/
    └── PROXIMA-ACCION.md
```

### 2. Define Vertical Context

Create `orchestration/00-guidelines/CONTEXTO-PROYECTO.md`:

```markdown
# Contexto del Proyecto: [Vertical Name]

## Descripción
[What is this vertical? What industry problem does it solve?]

## Módulos Específicos
1. [Module 1] - [Description]
2. [Module 2] - [Description]

## Dependencias del Core
- auth: Authentication & authorization
- inventory: Product management (extended)
- sales: Sales management (extended)
- [List core modules used]

## Schemas de Base de Datos
1. [schema_name] - [Purpose]
2. [schema_name] - [Purpose]

## Estado Actual
[Current development status]
```

### 3. Database Schema Design

#### Create Vertical Schema

```bash
# Create schema DDL
touch database/ddl/schemas/my_vertical_management/schema.sql
```

**Example:** `database/ddl/schemas/my_vertical_management/schema.sql`

```sql
-- ============================================
-- SCHEMA: my_vertical_management
-- PURPOSE: Industry-specific data for my vertical
-- DEPENDS ON: auth, core, inventory (from erp-core)
-- ============================================

CREATE SCHEMA IF NOT EXISTS my_vertical_management;

-- ============================================
-- TABLE: my_vertical_management.custom_entities
-- ============================================
CREATE TABLE my_vertical_management.custom_entities (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id),

    -- Link to core entities
    product_id UUID REFERENCES products.products(id),
    partner_id UUID REFERENCES core.partners(id),

    -- Vertical-specific fields
    industry_code VARCHAR(50) NOT NULL,
    certification_date DATE,
    compliance_status VARCHAR(20),

    -- Standard audit fields
    created_by UUID REFERENCES auth.users(id),
    updated_by UUID REFERENCES auth.users(id),
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    deleted_at TIMESTAMPTZ
);

-- Indexes
CREATE INDEX idx_custom_entities_tenant_id
ON my_vertical_management.custom_entities(tenant_id);

CREATE INDEX idx_custom_entities_industry_code
ON my_vertical_management.custom_entities(tenant_id, industry_code)
WHERE deleted_at IS NULL;

-- RLS Policy
ALTER TABLE my_vertical_management.custom_entities ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation ON my_vertical_management.custom_entities
    USING (tenant_id = current_setting('app.current_tenant_id')::uuid);

-- Permissions
GRANT SELECT, INSERT, UPDATE, DELETE
ON my_vertical_management.custom_entities TO erp_app_user;

-- Comments
COMMENT ON TABLE my_vertical_management.custom_entities IS
    'Stores industry-specific entity data for my vertical';
```

### 4. Backend Module Structure

#### Extend Core Module

**Example:** Extending Projects module

```typescript
// backend/src/modules/projects/vertical-project.service.ts
import { ProjectService } from '@erp-core/modules/projects/project.service';
import { CreateProjectDto } from '@erp-core/modules/projects/dto';

interface CreateVerticalProjectDto extends CreateProjectDto {
  // Add vertical-specific fields
  industryCode: string;
  certificationDate?: Date;
  complianceStatus: string;
}

export class VerticalProjectService extends ProjectService {
  /**
   * Override create method to add vertical logic
   */
  async create(
    data: CreateVerticalProjectDto,
    tenantId: string,
    userId: string
  ): Promise<Project> {
    const client = await this.pool.connect();

    try {
      await client.query('BEGIN');

      // 1. Call parent method (creates core project)
      const project = await super.create(data, tenantId, userId);

      // 2. Create vertical-specific data
      await client.query(`
        INSERT INTO my_vertical_management.custom_entities (
          tenant_id, product_id, industry_code,
          certification_date, compliance_status,
          created_by
        ) VALUES ($1, $2, $3, $4, $5, $6)
      `, [
        tenantId,
        project.id,
        data.industryCode,
        data.certificationDate,
        data.complianceStatus,
        userId
      ]);

      await client.query('COMMIT');
      return project;

    } catch (error) {
      await client.query('ROLLBACK');
      throw error;
    } finally {
      client.release();
    }
  }

  /**
   * Add vertical-specific method
   */
  async findByCertificationDate(
    tenantId: string,
    startDate: Date,
    endDate: Date
  ): Promise<any[]> {
    const query = `
      SELECT p.*, ce.industry_code, ce.certification_date
      FROM projects.projects p
      INNER JOIN my_vertical_management.custom_entities ce
        ON p.id = ce.product_id
      WHERE p.tenant_id = $1
        AND ce.certification_date BETWEEN $2 AND $3
        AND p.deleted_at IS NULL
    `;

    const result = await this.pool.query(query, [tenantId, startDate, endDate]);
    return result.rows;
  }

  /**
   * Override validation
   */
  protected async validateProjectData(data: CreateVerticalProjectDto): Promise<void> {
    // Call parent validation
    await super.validateProjectData(data);

    // Add vertical-specific validation
    if (!data.industryCode) {
      throw new Error('Industry code is required for this vertical');
    }

    if (data.complianceStatus && !['pending', 'approved', 'rejected'].includes(data.complianceStatus)) {
      throw new Error('Invalid compliance status');
    }
  }
}
```

#### Create New Module

**Example:** Vertical-specific module

```typescript
// backend/src/modules/certifications/certification.service.ts
import { BaseService } from '@erp-core/shared/services/base.service';

interface Certification {
  id: string;
  tenantId: string;
  entityId: string;
  certificationNumber: string;
  issueDate: Date;
  expiryDate: Date;
  status: string;
}

interface CreateCertificationDto {
  entityId: string;
  certificationNumber: string;
  issueDate: Date;
  expiryDate: Date;
}

export class CertificationService extends BaseService<
  Certification,
  CreateCertificationDto,
  Partial<CreateCertificationDto>
> {
  constructor() {
    super('certifications', 'my_vertical_management');
  }

  /**
   * Find certifications expiring soon
   */
  async findExpiringSoon(
    tenantId: string,
    daysAhead: number = 30
  ): Promise<Certification[]> {
    const query = `
      SELECT *
      FROM my_vertical_management.certifications
      WHERE tenant_id = $1
        AND expiry_date <= NOW() + INTERVAL '${daysAhead} days'
        AND expiry_date >= NOW()
        AND deleted_at IS NULL
      ORDER BY expiry_date ASC
    `;

    const result = await this.pool.query(query, [tenantId]);
    return result.rows;
  }

  /**
   * Renew certification
   */
  async renew(
    certificationId: string,
    tenantId: string,
    userId: string,
    newExpiryDate: Date
  ): Promise<Certification> {
    const query = `
      UPDATE my_vertical_management.certifications
      SET
        expiry_date = $1,
        status = 'active',
        updated_by = $2,
        updated_at = NOW()
      WHERE id = $3 AND tenant_id = $4
      RETURNING *
    `;

    const result = await this.pool.query(query, [
      newExpiryDate,
      userId,
      certificationId,
      tenantId
    ]);

    return result.rows[0];
  }
}
```

### 5. API Routes

```typescript
// backend/src/routes/index.ts
import express from 'express';
import { VerticalProjectService } from '../modules/projects/vertical-project.service';
import { CertificationService } from '../modules/certifications/certification.service';
import { authenticateJWT } from '@erp-core/middleware/auth.middleware';

const router = express.Router();

const projectService = new VerticalProjectService();
const certificationService = new CertificationService();

// Extend core projects endpoint
router.post('/projects', authenticateJWT, async (req, res) => {
  try {
    const project = await projectService.create(
      req.body,
      req.user.tenantId,
      req.user.id
    );
    res.status(201).json(project);
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
});

// Vertical-specific endpoint
router.get('/certifications/expiring', authenticateJWT, async (req, res) => {
  try {
    const daysAhead = parseInt(req.query.days as string) || 30;
    const certs = await certificationService.findExpiringSoon(
      req.user.tenantId,
      daysAhead
    );
    res.json(certs);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

export default router;
```

### 6. Frontend Module

```tsx
// frontend/src/modules/certifications/CertificationList.tsx
import React, { useEffect, useState } from 'react';
import { api } from '../../shared/utils/api';

interface Certification {
  id: string;
  certificationNumber: string;
  issueDate: string;
  expiryDate: string;
  status: string;
}

export const CertificationList: React.FC = () => {
  const [certifications, setCertifications] = useState<Certification[]>([]);

  useEffect(() => {
    fetchCertifications();
  }, []);

  const fetchCertifications = async () => {
    const response = await api.get('/certifications/expiring?days=30');
    setCertifications(response.data);
  };

  return (
    <div className="p-4">
      <h2 className="text-2xl font-bold mb-4">Certifications Expiring Soon</h2>

      <table className="w-full border-collapse border">
        <thead>
          <tr className="bg-gray-100">
            <th className="border p-2">Certification Number</th>
            <th className="border p-2">Issue Date</th>
            <th className="border p-2">Expiry Date</th>
            <th className="border p-2">Status</th>
          </tr>
        </thead>
        <tbody>
          {certifications.map((cert) => (
            <tr key={cert.id}>
              <td className="border p-2">{cert.certificationNumber}</td>
              <td className="border p-2">{new Date(cert.issueDate).toLocaleDateString()}</td>
              <td className="border p-2">{new Date(cert.expiryDate).toLocaleDateString()}</td>
              <td className="border p-2">
                <span className={`px-2 py-1 rounded ${
                  cert.status === 'active' ? 'bg-green-200' : 'bg-red-200'
                }`}>
                  {cert.status}
                </span>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};
```

### 7. Documentation

Create documentation for your vertical:

#### Required Docs

1. **CONTEXTO-PROYECTO.md** - Project overview
2. **REQUERIMIENTOS.md** - Functional requirements
3. **MODELO-DATOS.md** - Database schema documentation
4. **API.md** - API endpoints
5. **GUIA-USUARIO.md** - User guide

#### Example: MODELO-DATOS.md

```markdown
# Modelo de Datos: [Vertical Name]

## Schemas

### my_vertical_management

#### Tablas

##### custom_entities
**Propósito:** Almacena datos específicos de la industria

| Campo | Tipo | Descripción |
|-------|------|-------------|
| id | UUID | Primary key |
| tenant_id | UUID | Tenant isolation |
| product_id | UUID | Link to core product |
| industry_code | VARCHAR(50) | Industry classification code |
| certification_date | DATE | Date of certification |
| compliance_status | VARCHAR(20) | Compliance status |

**Índices:**
- `idx_custom_entities_tenant_id` - Performance
- `idx_custom_entities_industry_code` - Queries by code

**RLS:** Enabled (tenant_isolation policy)
```

### 8. Integration with Core

#### Import Core Modules

```typescript
// backend/src/modules/projects/vertical-project.service.ts

// Option 1: Direct import (if monorepo)
import { ProjectService } from '../../../erp-core/backend/src/modules/projects/project.service';

// Option 2: Package import (if separate packages)
import { ProjectService } from '@erp-core/modules/projects';
```

#### Share Types

```typescript
// shared-libs/core/types/index.ts
export interface BaseEntity {
  id: string;
  tenantId: string;
  createdBy: string;
  updatedBy?: string;
  createdAt: Date;
  updatedAt: Date;
  deletedAt?: Date;
}

export interface Project extends BaseEntity {
  name: string;
  description?: string;
  status: 'draft' | 'active' | 'completed';
}
```

## Best Practices

### 1. Follow Naming Conventions

**Schemas:**
```
{vertical}_management
example: construction_management, clinic_management
```

**Tables:**
```
{vertical}_management.{entity_plural}
example: construction_management.phases
```

**Services:**
```
{Vertical}{Entity}Service
example: ConstructionProjectService
```

### 2. Always Use Multi-Tenancy

```sql
-- ✅ Good
CREATE TABLE my_vertical_management.entities (
    id UUID PRIMARY KEY,
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
    -- ...
);

-- ❌ Bad (missing tenant_id)
CREATE TABLE my_vertical_management.entities (
    id UUID PRIMARY KEY,
    -- missing tenant_id!
);
```

### 3. Extend, Don't Duplicate

```typescript
// ✅ Good - Extend core service
class VerticalProjectService extends ProjectService {
  async create(...) {
    const project = await super.create(...);
    // Add vertical logic
    return project;
  }
}

// ❌ Bad - Duplicate core logic
class VerticalProjectService {
  async create(...) {
    // Copy-pasted from ProjectService
    // Now you have duplicated code!
  }
}
```

### 4. Document Dependencies

```markdown
## Dependencias del Core

Este vertical extiende los siguientes módulos del core:

- **projects** - Gestión de proyectos (override create, findAll)
- **inventory** - Productos (agrega campos custom)
- **sales** - Ventas (validación adicional)
- **financial** - Contabilidad (reportes específicos)
```

### 5. Use Transactions

```typescript
async create(data: any, tenantId: string, userId: string) {
  const client = await this.pool.connect();

  try {
    await client.query('BEGIN');

    // 1. Core operation
    const entity = await super.create(data, tenantId, userId);

    // 2. Vertical operation
    await this.createVerticalData(client, entity.id, data);

    await client.query('COMMIT');
    return entity;

  } catch (error) {
    await client.query('ROLLBACK');
    throw error;
  } finally {
    client.release();
  }
}
```

## Testing Your Vertical

### Unit Tests

```typescript
// __tests__/vertical-project.service.test.ts
import { VerticalProjectService } from '../modules/projects/vertical-project.service';

describe('VerticalProjectService', () => {
  let service: VerticalProjectService;

  beforeEach(() => {
    service = new VerticalProjectService();
  });

  it('should create project with vertical data', async () => {
    const data = {
      name: 'Test Project',
      industryCode: 'IND-001',
      complianceStatus: 'pending'
    };

    const project = await service.create(data, 'tenant-id', 'user-id');

    expect(project).toBeDefined();
    expect(project.name).toBe('Test Project');
    // Verify vertical data was created
  });
});
```

### Integration Tests

Test interaction with core modules and database.

## Deployment

### Database Migration

```bash
# Run core migrations first
cd apps/erp-core/database
psql -U erp_user -d erp_db -f ddl/schemas/auth/schema.sql
psql -U erp_user -d erp_db -f ddl/schemas/core/schema.sql
# ... all core schemas

# Then run vertical migrations
cd apps/verticales/my-vertical/database
psql -U erp_user -d erp_db -f ddl/schemas/my_vertical_management/schema.sql
```

### Environment Variables

```bash
# .env for vertical
CORE_API_URL=http://localhost:3000
VERTICAL_NAME=my-vertical
VERTICAL_DB_SCHEMA=my_vertical_management
```

## Examples from Existing Verticals

### Construcción Vertical

**Extends:**
- Projects → Construction Projects (adds phases, developments)
- Partners → Derechohabientes (adds INFONAVIT data)
- Financial → Presupuestos (construction budgets)

**New Modules:**
- Quality Management
- INFONAVIT Integration
- Construction Control

### Mecánicas Diesel Vertical

**Extends:**
- Inventory → Vehicle Parts (adds vehicle compatibility)
- Sales → Work Orders (service orders)
- Partners → Vehicle Owners

**New Modules:**
- Diagnostics
- Maintenance Schedules
- Vehicle Registry

## Checklist: Create New Vertical

- [ ] Create directory structure
- [ ] Write CONTEXTO-PROYECTO.md
- [ ] Design database schemas
- [ ] Create DDL files with RLS
- [ ] Identify core modules to extend
- [ ] Create service classes (extend BaseService)
- [ ] Implement API routes
- [ ] Create frontend modules
- [ ] Write documentation
- [ ] Write unit tests
- [ ] Integration testing
- [ ] Deploy database schemas

## References

- [Architecture Documentation](./ARCHITECTURE.md)
- [Multi-Tenancy Guide](./MULTI-TENANCY.md)
- [Core Modules Documentation](../apps/erp-core/docs/)
- [Odoo Development Patterns](../../../workspace/core/knowledge-base/patterns/PATRON-CORE-ODOO.md)
