erp-construccion/CONTRIBUTING.md

479 lines
8.6 KiB
Markdown

# 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