erp-construccion/CONTRIBUTING.md

8.6 KiB

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

# 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

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

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

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

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

# 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

# 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

# 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

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

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

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

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

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

Ejecutar Migraciones

# 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

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

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