# Guia de Contribucion - ERP Construccion

Esta guia describe las convenciones y procesos para contribuir al proyecto.

---

## Requisitos Previos

- Node.js 20 LTS
- Docker y Docker Compose
- PostgreSQL 15 (o usar docker-compose)
- Git

---

## Setup Inicial

```bash
# 1. Clonar repositorio
git clone <repo-url>
cd apps/verticales/construccion

# 2. Instalar dependencias
cd backend && npm install

# 3. Configurar variables de entorno
cp ../.env.example .env
# Editar .env con credenciales locales

# 4. Levantar servicios
docker-compose up -d postgres redis

# 5. Ejecutar migraciones (si aplica)
npm run migration:run

# 6. Iniciar en desarrollo
npm run dev
```

---

## Estructura de Ramas

| Rama | Proposito |
|------|-----------|
| `main` | Produccion estable |
| `develop` | Integracion de features |
| `feature/*` | Nuevas funcionalidades |
| `fix/*` | Correcciones de bugs |
| `refactor/*` | Refactorizaciones |
| `docs/*` | Documentacion |

### Flujo de Trabajo

```
1. Crear rama desde develop
   git checkout develop
   git pull origin develop
   git checkout -b feature/MAI-XXX-descripcion

2. Desarrollar y hacer commits
   git commit -m "feat(modulo): descripcion del cambio"

3. Push y crear Pull Request
   git push origin feature/MAI-XXX-descripcion
   # Crear PR en GitHub hacia develop

4. Code Review y merge
```

---

## Convenciones de Codigo

### Nomenclatura

| Tipo | Convencion | Ejemplo |
|------|------------|---------|
| Archivos | kebab-case.tipo.ts | `concepto.entity.ts` |
| Clases | PascalCase + sufijo | `ConceptoService` |
| Interfaces | PascalCase + prefijo I | `IConceptoRepository` |
| Variables | camelCase | `totalAmount` |
| Constantes | UPPER_SNAKE_CASE | `DB_SCHEMAS` |
| Metodos | camelCase + verbo | `findByContrato` |
| Enums | PascalCase | `EstimacionStatus` |
| Tablas DB | snake_case plural | `presupuestos` |
| Columnas DB | snake_case | `tenant_id` |

### Estructura de Archivos por Modulo

```
modules/
└── nombre-modulo/
    ├── entities/
    │   └── entidad.entity.ts
    ├── services/
    │   └── entidad.service.ts
    ├── controllers/
    │   └── entidad.controller.ts
    ├── dto/
    │   └── entidad.dto.ts
    └── index.ts
```

### Patron Entity

```typescript
import { Entity, Column, PrimaryGeneratedColumn, Index } from 'typeorm';
import { DB_SCHEMAS, DB_TABLES } from '@shared/constants';

@Entity({
  schema: DB_SCHEMAS.CONSTRUCTION,
  name: DB_TABLES.construction.CONCEPTOS,
})
@Index(['tenantId', 'code'], { unique: true })
export class Concepto {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ name: 'tenant_id', type: 'uuid' })
  tenantId: string;

  @Column({ name: 'code', length: 20 })
  code: string;

  // Usar snake_case en name: para mapear a DB
  @Column({ name: 'created_at', type: 'timestamptz', default: () => 'NOW()' })
  createdAt: Date;

  @Column({ name: 'deleted_at', type: 'timestamptz', nullable: true })
  deletedAt: Date | null;
}
```

### Patron Service

```typescript
import { Repository } from 'typeorm';
import { BaseService, ServiceContext } from '@shared/services/base.service';
import { Concepto } from '../entities/concepto.entity';

export class ConceptoService extends BaseService<Concepto> {
  constructor(repository: Repository<Concepto>) {
    super(repository);
  }

  // Metodos especificos del dominio
  async findByCode(ctx: ServiceContext, code: string): Promise<Concepto | null> {
    return this.findOne(ctx, { code });
  }
}
```

### Patron DTO

```typescript
import { IsString, IsOptional, IsUUID, MinLength, MaxLength } from 'class-validator';

export class CreateConceptoDto {
  @IsString()
  @MinLength(1)
  @MaxLength(20)
  code: string;

  @IsString()
  @MinLength(1)
  @MaxLength(200)
  name: string;

  @IsOptional()
  @IsUUID()
  parentId?: string;
}

export class UpdateConceptoDto {
  @IsOptional()
  @IsString()
  @MaxLength(200)
  name?: string;

  @IsOptional()
  @IsNumber()
  unitPrice?: number;
}
```

---

## SSOT - Constantes Centralizadas

### Regla Principal

**NUNCA** hardcodear:
- Nombres de schemas
- Nombres de tablas
- Rutas de API
- Valores de enums

### Uso Correcto

```typescript
// INCORRECTO
@Entity({ schema: 'construction', name: 'conceptos' })

// CORRECTO
import { DB_SCHEMAS, DB_TABLES } from '@shared/constants';

@Entity({
  schema: DB_SCHEMAS.CONSTRUCTION,
  name: DB_TABLES.construction.CONCEPTOS,
})
```

### Validacion Automatica

```bash
# Detecta hardcoding de constantes
npm run validate:constants

# Se ejecuta en pre-commit y CI
```

---

## Commits

### Formato de Mensaje

```
tipo(alcance): descripcion breve

[cuerpo opcional]

[footer opcional]
```

### Tipos de Commit

| Tipo | Descripcion |
|------|-------------|
| `feat` | Nueva funcionalidad |
| `fix` | Correccion de bug |
| `refactor` | Refactorizacion sin cambio funcional |
| `docs` | Documentacion |
| `test` | Tests |
| `chore` | Mantenimiento, dependencias |
| `style` | Formato, sin cambio de logica |
| `perf` | Mejora de performance |

### Ejemplos

```bash
# Feature
git commit -m "feat(budgets): agregar versionamiento de presupuestos"

# Fix
git commit -m "fix(estimates): corregir calculo de totales con decimales"

# Refactor
git commit -m "refactor(auth): simplificar validacion de tokens"

# Docs
git commit -m "docs(api): actualizar especificacion OpenAPI"
```

---

## Testing

### Ejecutar Tests

```bash
# Todos los tests
npm test

# Con cobertura
npm run test:coverage

# Watch mode
npm run test:watch

# Archivo especifico
npm test -- concepto.service.spec.ts
```

### Estructura de Test

```typescript
describe('ConceptoService', () => {
  let service: ConceptoService;
  let mockRepo: jest.Mocked<Repository<Concepto>>;

  const ctx: ServiceContext = {
    tenantId: 'test-tenant-uuid',
    userId: 'test-user-uuid',
  };

  beforeEach(() => {
    mockRepo = createMockRepository();
    service = new ConceptoService(mockRepo);
  });

  describe('createConcepto', () => {
    it('should create concepto with level 0 for root', async () => {
      // Arrange
      const dto = { code: '001', name: 'Test' };
      mockRepo.save.mockResolvedValue({ id: 'uuid', ...dto, level: 0 });

      // Act
      const result = await service.createConcepto(ctx, dto);

      // Assert
      expect(result.level).toBe(0);
    });

    it('should throw on duplicate code', async () => {
      // ...
    });
  });
});
```

### Cobertura Minima

- Statements: 80%
- Branches: 75%
- Functions: 80%
- Lines: 80%

---

## Linting y Formato

### Ejecutar Linting

```bash
# Verificar errores
npm run lint

# Corregir automaticamente
npm run lint:fix
```

### Configuracion ESLint

El proyecto usa ESLint con TypeScript. Reglas principales:

- No `any` implicito
- No variables no usadas
- Imports ordenados
- Espacios consistentes

### Pre-commit Hook

Se ejecuta automaticamente:

```bash
npm run precommit  # lint + validate:constants
```

---

## Pull Requests

### Checklist

Antes de crear un PR, verificar:

- [ ] Tests pasan: `npm test`
- [ ] Linting pasa: `npm run lint`
- [ ] Constantes validadas: `npm run validate:constants`
- [ ] Build funciona: `npm run build`
- [ ] Documentacion actualizada (si aplica)
- [ ] Commits con formato correcto

### Template de PR

```markdown
## Descripcion

Breve descripcion del cambio.

## Tipo de Cambio

- [ ] Feature
- [ ] Bug fix
- [ ] Refactor
- [ ] Docs
- [ ] Tests

## Modulo Afectado

- MAI-XXX: Nombre del modulo

## Testing

Describir como probar los cambios.

## Screenshots (si aplica)

## Checklist

- [ ] Tests agregados/actualizados
- [ ] Documentacion actualizada
- [ ] Sin breaking changes (o documentados)
```

---

## Migraciones de Base de Datos

### Generar Migracion

```bash
# Despues de modificar entidades
npm run migration:generate -- -n NombreDescriptivo
```

### Ejecutar Migraciones

```bash
# Aplicar pendientes
npm run migration:run

# Revertir ultima
npm run migration:revert
```

### Convenciones

- Una migracion por feature/fix
- Nombres descriptivos: `AddStatusToEstimaciones`
- Incluir rollback funcional
- No modificar migraciones ya aplicadas en produccion

---

## Debugging

### VS Code

```json
// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "launch",
      "name": "Debug Backend",
      "runtimeArgs": ["-r", "ts-node/register"],
      "args": ["${workspaceFolder}/backend/src/server.ts"],
      "env": {
        "NODE_ENV": "development",
        "LOG_LEVEL": "debug"
      }
    }
  ]
}
```

### Variables de Entorno para Debug

```bash
LOG_LEVEL=debug
TYPEORM_LOGGING=true
```

---

## Contacto

- **Issues:** Reportar bugs o sugerir features en GitHub Issues
- **PRs:** Siempre bienvenidos, seguir las convenciones

---

**Ultima actualizacion:** 2025-12-12
