erp-construccion/docs/backend/MODULES.md

745 lines
20 KiB
Markdown

# Documentacion de Modulos Backend
Este documento detalla la implementacion de cada modulo del backend.
---
## Indice
1. [Auth Module](#auth-module)
2. [Budgets Module (MAI-003)](#budgets-module-mai-003)
3. [Progress Module (MAI-005)](#progress-module-mai-005)
4. [Estimates Module (MAI-008)](#estimates-module-mai-008)
5. [Construction Module (MAI-002)](#construction-module-mai-002)
6. [HR Module (MAI-007)](#hr-module-mai-007)
7. [HSE Module (MAA-017)](#hse-module-maa-017)
8. [Core Module](#core-module)
---
## Auth Module
**Ubicacion:** `backend/src/modules/auth/`
### Descripcion
Modulo de autenticacion JWT con refresh tokens y soporte multi-tenant.
### Estructura
```
auth/
├── dto/
│ └── auth.dto.ts # DTOs de autenticacion
├── services/
│ └── auth.service.ts # Logica de autenticacion
├── middleware/
│ └── auth.middleware.ts # Middleware JWT
└── index.ts # Exports
```
### DTOs
```typescript
// LoginDto
{
email: string; // Email del usuario
password: string; // Password
}
// RegisterDto
{
email: string;
password: string;
firstName: string;
lastName: string;
tenantId: string; // Tenant al que pertenece
}
// RefreshTokenDto
{
refreshToken: string;
}
// AuthResponse
{
user: UserDto;
accessToken: string;
refreshToken: string;
expiresIn: number;
}
```
### AuthService
| Metodo | Descripcion | Parametros | Retorna |
|--------|-------------|------------|---------|
| `login` | Autentica usuario | `LoginDto` | `AuthResponse` |
| `register` | Registra nuevo usuario | `RegisterDto` | `AuthResponse` |
| `refresh` | Renueva tokens | `RefreshTokenDto` | `AuthResponse` |
| `logout` | Revoca refresh token | `token: string` | `boolean` |
| `changePassword` | Cambia password | `ChangePasswordDto` | `boolean` |
| `validateToken` | Valida JWT | `token: string` | `JwtPayload` |
### AuthMiddleware
| Metodo | Descripcion |
|--------|-------------|
| `authenticate` | Valida JWT (requerido) |
| `optionalAuthenticate` | Valida JWT (opcional) |
| `authorize(...roles)` | Autoriza por roles |
| `requireAdmin` | Solo admin/super_admin |
| `requireSupervisor` | Solo supervisores+ |
| `setRLSContext` | Configura tenant en sesion PostgreSQL |
### Ejemplo de Uso
```typescript
// Login
const auth = await authService.login({
email: 'user@example.com',
password: 'securePassword123'
});
// Usar token en requests
headers: {
'Authorization': `Bearer ${auth.accessToken}`
}
// Refresh cuando expire
const newAuth = await authService.refresh({
refreshToken: auth.refreshToken
});
```
---
## Budgets Module (MAI-003)
**Ubicacion:** `backend/src/modules/budgets/`
### Descripcion
Gestion de catalogos de conceptos y presupuestos de obra.
### Estructura
```
budgets/
├── entities/
│ ├── concepto.entity.ts
│ ├── presupuesto.entity.ts
│ └── presupuesto-partida.entity.ts
├── services/
│ ├── concepto.service.ts
│ └── presupuesto.service.ts
└── index.ts
```
### Entidades
#### Concepto
Catalogo jerarquico de conceptos de obra.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador multi-tenant |
| `code` | string | Codigo unico (ej: "01.02.003") |
| `name` | string | Nombre del concepto |
| `description` | string | Descripcion detallada |
| `unit` | string | Unidad de medida (M2, ML, PZA) |
| `unitPrice` | decimal | Precio unitario |
| `parentId` | UUID | Concepto padre (null=raiz) |
| `level` | number | Nivel jerarquico (0=raiz) |
| `path` | string | Ruta completa (ej: "01/02/003") |
| `isActive` | boolean | Activo para uso |
**Relaciones:**
- `parent` → Concepto (auto-referencial)
- `children` → Concepto[] (hijos)
#### Presupuesto
Presupuesto versionado de obra.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `fraccionamientoId` | UUID | Proyecto asociado |
| `code` | string | Codigo del presupuesto |
| `name` | string | Nombre descriptivo |
| `version` | number | Version (1, 2, 3...) |
| `status` | enum | draft/submitted/approved |
| `totalAmount` | decimal | Total calculado |
| `approvedAt` | timestamp | Fecha de aprobacion |
| `approvedById` | UUID | Usuario que aprobo |
**Relaciones:**
- `fraccionamiento` → Fraccionamiento
- `partidas` → PresupuestoPartida[]
#### PresupuestoPartida
Lineas de presupuesto.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `presupuestoId` | UUID | Presupuesto padre |
| `conceptoId` | UUID | Concepto referenciado |
| `quantity` | decimal | Cantidad |
| `unitPrice` | decimal | Precio unitario |
| `totalAmount` | decimal | **GENERADO**: quantity * unitPrice |
### ConceptoService
| Metodo | Descripcion |
|--------|-------------|
| `createConcepto(ctx, dto)` | Crea concepto, calcula nivel/path |
| `findRootConceptos(ctx)` | Conceptos raiz (nivel 0) |
| `findChildren(ctx, parentId)` | Hijos de un concepto |
| `getConceptoTree(ctx, rootId?)` | Arbol completo/parcial |
| `search(ctx, term)` | Busqueda por codigo/nombre |
### PresupuestoService
| Metodo | Descripcion |
|--------|-------------|
| `createPresupuesto(ctx, dto)` | Crea presupuesto |
| `findByFraccionamiento(ctx, id)` | Por proyecto |
| `findWithPartidas(ctx, id)` | Con lineas cargadas |
| `addPartida(ctx, presupuestoId, dto)` | Agregar linea |
| `updatePartida(ctx, partidaId, dto)` | Modificar linea |
| `removePartida(ctx, partidaId)` | Eliminar linea |
| `recalculateTotal(ctx, presupuestoId)` | Recalcular total |
| `createNewVersion(ctx, presupuestoId)` | Crear version nueva |
| `approve(ctx, presupuestoId)` | Aprobar presupuesto |
---
## Progress Module (MAI-005)
**Ubicacion:** `backend/src/modules/progress/`
### Descripcion
Control de avances fisicos, bitacora y programacion de obra.
### Estructura
```
progress/
├── entities/
│ ├── avance-obra.entity.ts
│ ├── foto-avance.entity.ts
│ ├── bitacora-obra.entity.ts
│ ├── programa-obra.entity.ts
│ └── programa-actividad.entity.ts
├── services/
│ ├── avance-obra.service.ts
│ └── bitacora-obra.service.ts
└── index.ts
```
### Entidades
#### AvanceObra
Registro de avance fisico con workflow.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `loteId` | UUID | Lote donde se registro |
| `departamentoId` | UUID | Departamento (opcional) |
| `conceptoId` | UUID | Concepto de catalogo |
| `quantity` | decimal | Cantidad ejecutada |
| `progressDate` | date | Fecha de avance |
| `status` | enum | captured/reviewed/approved/rejected |
| `capturedById` | UUID | Usuario que capturo |
| `reviewedAt` | timestamp | Fecha de revision |
| `approvedAt` | timestamp | Fecha de aprobacion |
| `rejectionReason` | string | Motivo de rechazo |
| `notes` | string | Notas adicionales |
#### FotoAvance
Evidencia fotografica con geolocalización.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `avanceObraId` | UUID | Avance asociado |
| `fileName` | string | Nombre del archivo |
| `filePath` | string | Ruta en storage |
| `fileSize` | number | Tamano en bytes |
| `mimeType` | string | Tipo MIME |
| `latitude` | decimal | Latitud GPS |
| `longitude` | decimal | Longitud GPS |
| `takenAt` | timestamp | Fecha de captura |
| `description` | string | Descripcion |
#### BitacoraObra
Bitacora diaria de obra.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `fraccionamientoId` | UUID | Proyecto |
| `entryNumber` | number | Numero secuencial |
| `entryDate` | date | Fecha de entrada |
| `weather` | string | Clima del dia |
| `temperature` | decimal | Temperatura |
| `workersCount` | number | Personal en obra |
| `activities` | text | Actividades realizadas |
| `incidents` | text | Incidentes |
| `observations` | text | Observaciones |
| `createdById` | UUID | Usuario que creo |
### AvanceObraService
| Metodo | Descripcion |
|--------|-------------|
| `createAvance(ctx, dto)` | Crear avance en status captured |
| `findByLote(ctx, loteId)` | Avances de un lote |
| `findByDepartamento(ctx, deptoId)` | Avances de departamento |
| `findWithFilters(ctx, filters)` | Busqueda con filtros |
| `findWithFotos(ctx, id)` | Avance con fotos |
| `addFoto(ctx, avanceId, dto)` | Agregar foto |
| `review(ctx, id)` | Workflow: revisar |
| `approve(ctx, id)` | Workflow: aprobar |
| `reject(ctx, id, reason)` | Workflow: rechazar |
| `getAccumulatedProgress(ctx)` | Acumulado por concepto |
### BitacoraObraService
| Metodo | Descripcion |
|--------|-------------|
| `createEntry(ctx, dto)` | Crear entrada (numero automatico) |
| `findByFraccionamiento(ctx, id)` | Entradas de proyecto |
| `findWithFilters(ctx, id, filters)` | Con filtros |
| `findByDate(ctx, id, date)` | Por fecha especifica |
| `findLatest(ctx, id)` | Ultima entrada |
| `getStats(ctx, id)` | Estadisticas |
---
## Estimates Module (MAI-008)
**Ubicacion:** `backend/src/modules/estimates/`
### Descripcion
Estimaciones periodicas con workflow de aprobacion completo.
### Estructura
```
estimates/
├── entities/
│ ├── estimacion.entity.ts
│ ├── estimacion-concepto.entity.ts
│ ├── generador.entity.ts
│ ├── anticipo.entity.ts
│ ├── amortizacion.entity.ts
│ ├── retencion.entity.ts
│ ├── fondo-garantia.entity.ts
│ └── estimacion-workflow.entity.ts
├── services/
│ └── estimacion.service.ts
└── index.ts
```
### Entidades
#### Estimacion
Estimacion periodica principal.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `contratoId` | UUID | Contrato asociado |
| `number` | number | Numero secuencial |
| `periodStart` | date | Inicio del periodo |
| `periodEnd` | date | Fin del periodo |
| `status` | enum | draft/submitted/reviewed/approved/rejected |
| `subtotal` | decimal | Suma de conceptos |
| `iva` | decimal | IVA calculado |
| `retentions` | decimal | Retenciones |
| `amortization` | decimal | Amortizacion de anticipo |
| `totalAmount` | decimal | Total neto |
| `submittedAt` | timestamp | Fecha de envio |
| `reviewedAt` | timestamp | Fecha de revision |
| `approvedAt` | timestamp | Fecha de aprobacion |
| `rejectionReason` | string | Motivo de rechazo |
#### EstimacionConcepto
Lineas de estimacion con acumulados.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `estimacionId` | UUID | Estimacion padre |
| `conceptoId` | UUID | Concepto de catalogo |
| `contractQuantity` | decimal | Cantidad contratada |
| `previousQuantity` | decimal | Cantidad acumulada anterior |
| `currentQuantity` | decimal | Cantidad este periodo |
| `accumulatedQuantity` | decimal | **GENERADO**: previous + current |
| `pendingQuantity` | decimal | **GENERADO**: contract - accumulated |
| `unitPrice` | decimal | Precio unitario |
| `currentAmount` | decimal | **GENERADO**: current * price |
| `accumulatedAmount` | decimal | **GENERADO**: accumulated * price |
#### Generador
Numeros generadores (desglose de cantidades).
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `estimacionConceptoId` | UUID | Linea de estimacion |
| `description` | string | Descripcion del calculo |
| `length` | decimal | Largo |
| `width` | decimal | Ancho |
| `height` | decimal | Alto |
| `quantity` | decimal | Cantidad/Piezas |
| `partial` | decimal | **GENERADO**: formula |
| `formula` | string | Formula aplicada |
#### Anticipo
Anticipos de contrato.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `contratoId` | UUID | Contrato |
| `type` | enum | obra/materiales |
| `percentage` | decimal | Porcentaje del contrato |
| `amount` | decimal | Monto del anticipo |
| `grantedDate` | date | Fecha de otorgamiento |
| `amortizedAmount` | decimal | Monto ya amortizado |
| `pendingAmount` | decimal | **GENERADO** |
| `status` | enum | pending/partial/completed |
#### FondoGarantia
Fondo de garantia acumulado.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `contratoId` | UUID | Contrato |
| `percentage` | decimal | Porcentaje retenido |
| `accumulatedAmount` | decimal | Monto acumulado |
| `releasedAmount` | decimal | Monto liberado |
| `pendingAmount` | decimal | **GENERADO** |
### EstimacionService
| Metodo | Descripcion |
|--------|-------------|
| `createEstimacion(ctx, dto)` | Crear con numero automatico |
| `findByContrato(ctx, contratoId)` | Estimaciones de contrato |
| `findWithFilters(ctx, filters)` | Busqueda con filtros |
| `findWithDetails(ctx, id)` | Con todas las relaciones |
| `addConcepto(ctx, estimacionId, dto)` | Agregar linea |
| `addGenerador(ctx, conceptoId, dto)` | Agregar generador |
| `recalculateTotals(ctx, id)` | Recalcular totales |
| `submit(ctx, id)` | Workflow: enviar |
| `review(ctx, id)` | Workflow: revisar |
| `approve(ctx, id)` | Workflow: aprobar |
| `reject(ctx, id, reason)` | Workflow: rechazar |
| `getContractSummary(ctx, contratoId)` | Resumen financiero |
---
## Construction Module (MAI-002)
**Ubicacion:** `backend/src/modules/construction/`
### Descripcion
Gestion de proyectos y estructura organizacional.
### Entidades
#### Proyecto
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `code` | string | Codigo del proyecto |
| `name` | string | Nombre |
| `description` | text | Descripcion |
| `status` | enum | planning/in_progress/completed/cancelled |
| `startDate` | date | Fecha inicio |
| `endDate` | date | Fecha fin |
| `budget` | decimal | Presupuesto total |
| `location` | geography | Ubicacion GPS |
| `address` | string | Direccion |
| `city` | string | Ciudad |
| `state` | string | Estado |
#### Fraccionamiento
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `proyectoId` | UUID | Proyecto padre |
| `code` | string | Codigo |
| `name` | string | Nombre |
| `totalLots` | number | Total de lotes |
| `builtLots` | number | Lotes construidos |
| `soldLots` | number | Lotes vendidos |
---
## HR Module (MAI-007)
**Ubicacion:** `backend/src/modules/hr/`
### Descripcion
Gestion de recursos humanos y asignaciones.
### Entidades
#### Employee
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `employeeNumber` | string | Numero de empleado |
| `firstName` | string | Nombre |
| `lastName` | string | Apellidos |
| `email` | string | Email |
| `phone` | string | Telefono |
| `hireDate` | date | Fecha de contratacion |
| `puestoId` | UUID | Puesto |
| `status` | enum | active/inactive/terminated |
| `dailyRate` | decimal | Salario diario |
| `imssNumber` | string | Numero IMSS |
| `curp` | string | CURP |
| `rfc` | string | RFC |
#### Puesto
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `code` | string | Codigo |
| `name` | string | Nombre del puesto |
| `department` | string | Departamento |
| `level` | number | Nivel jerarquico |
| `baseSalary` | decimal | Salario base |
#### EmployeeFraccionamiento
Asignacion de empleados a proyectos.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `employeeId` | UUID | Empleado |
| `fraccionamientoId` | UUID | Proyecto |
| `role` | string | Rol en el proyecto |
| `startDate` | date | Fecha inicio |
| `endDate` | date | Fecha fin (null=activo) |
---
## HSE Module (MAA-017)
**Ubicacion:** `backend/src/modules/hse/`
### Descripcion
Seguridad, salud y medio ambiente.
### Entidades
#### Incidente
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `fraccionamientoId` | UUID | Proyecto |
| `incidentNumber` | string | Numero de reporte |
| `incidentDate` | timestamp | Fecha y hora |
| `type` | enum | accident/near_miss/incident |
| `severity` | enum | low/medium/high/critical |
| `description` | text | Descripcion |
| `location` | string | Ubicacion |
| `immediateActions` | text | Acciones inmediatas |
| `rootCause` | text | Causa raiz |
| `status` | enum | reported/investigating/closed |
| `reportedById` | UUID | Quien reporto |
#### IncidenteInvolucrado
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `incidenteId` | UUID | Incidente |
| `employeeId` | UUID | Empleado (opcional) |
| `name` | string | Nombre (si no es empleado) |
| `role` | enum | victim/witness/reporter |
| `injuryType` | string | Tipo de lesion |
| `treatmentRequired` | boolean | Requirio tratamiento |
#### IncidenteAccion
Acciones correctivas.
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `incidenteId` | UUID | Incidente |
| `description` | text | Descripcion de la accion |
| `responsibleId` | UUID | Responsable |
| `dueDate` | date | Fecha limite |
| `completedAt` | timestamp | Fecha de cierre |
| `status` | enum | pending/in_progress/completed |
#### Capacitacion
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `tenantId` | UUID | Discriminador |
| `name` | string | Nombre del curso |
| `type` | enum | induction/specific/recertification |
| `duration` | number | Duracion en horas |
| `validityMonths` | number | Vigencia en meses |
| `instructor` | string | Instructor |
| `scheduledDate` | date | Fecha programada |
| `status` | enum | scheduled/completed/cancelled |
---
## Core Module
**Ubicacion:** `backend/src/modules/core/`
### Descripcion
Entidades base del sistema (usuarios, tenants).
### Entidades
#### User
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `email` | string | Email unico |
| `passwordHash` | string | Password hasheado |
| `firstName` | string | Nombre |
| `lastName` | string | Apellidos |
| `role` | enum | Rol del sistema |
| `isActive` | boolean | Usuario activo |
| `lastLoginAt` | timestamp | Ultimo acceso |
| `emailVerifiedAt` | timestamp | Email verificado |
#### Tenant
| Campo | Tipo | Descripcion |
|-------|------|-------------|
| `id` | UUID | Primary key |
| `code` | string | Codigo unico |
| `name` | string | Nombre de la empresa |
| `subdomain` | string | Subdominio |
| `plan` | enum | basic/professional/enterprise |
| `isActive` | boolean | Tenant activo |
| `settings` | jsonb | Configuraciones |
| `createdAt` | timestamp | Fecha de creacion |
---
## Shared: BaseService
**Ubicacion:** `backend/src/shared/services/base.service.ts`
### Descripcion
Servicio base abstracto que proporciona operaciones CRUD multi-tenant.
### Interface ServiceContext
```typescript
interface ServiceContext {
tenantId: string;
userId: string;
}
```
### Interface PaginatedResult
```typescript
interface PaginatedResult<T> {
data: T[];
total: number;
page: number;
limit: number;
totalPages: number;
}
```
### Metodos
| Metodo | Descripcion | Retorna |
|--------|-------------|---------|
| `findAll(ctx, options?)` | Listado paginado | `PaginatedResult<T>` |
| `findById(ctx, id)` | Por ID | `T \| null` |
| `findOne(ctx, where)` | Por condicion | `T \| null` |
| `find(ctx, options)` | Por opciones | `T[]` |
| `create(ctx, data)` | Crear registro | `T` |
| `update(ctx, id, data)` | Actualizar | `T \| null` |
| `softDelete(ctx, id)` | Borrado logico | `boolean` |
| `hardDelete(ctx, id)` | Borrado fisico | `boolean` |
| `count(ctx, where?)` | Contar registros | `number` |
| `exists(ctx, where)` | Verificar existencia | `boolean` |
### Ejemplo de Uso
```typescript
class MiService extends BaseService<MiEntity> {
constructor(repository: Repository<MiEntity>) {
super(repository);
}
// Metodos personalizados
async findActive(ctx: ServiceContext): Promise<MiEntity[]> {
return this.find(ctx, {
where: { isActive: true },
order: { createdAt: 'DESC' },
});
}
}
// Uso
const ctx = { tenantId: 'uuid', userId: 'uuid' };
const items = await miService.findAll(ctx, { page: 1, limit: 10 });
const item = await miService.findById(ctx, 'item-uuid');
const created = await miService.create(ctx, { name: 'Nuevo' });
```
---
**Ultima actualizacion:** 2025-12-12