479 lines
8.6 KiB
Markdown
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
|