Backend de erp-construccion - Workspace V2
- Create 11 entities matching DDL: - DocumentCategory: Hierarchical categories - Document: Main document registry - DocumentVersion: Version control with files - DocumentPermission: Granular permissions - ApprovalWorkflow: Workflow definitions - ApprovalInstance: Active workflow instances - ApprovalStep: Steps per instance - ApprovalActionEntity: Actions taken - Annotation: Comments and markups - AccessLog: Access history - DocumentShare: External sharing links - Create DocumentService with full CRUD - Create DocumentVersionService for version management - All entities follow ERP-Construction patterns Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> |
||
|---|---|---|
| scripts | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| service.descriptor.yml | ||
| tsconfig.json | ||
Backend - ERP Construccion
API REST para sistema de administracion de obra e INFONAVIT.
| Campo | Valor |
|---|---|
| Stack | Node.js 20 + Express 4 + TypeScript 5 + TypeORM 0.3 |
| Version | 1.0.0 |
| Entidades | 30 |
| Services | 8 |
| Arquitectura | Multi-tenant con RLS |
Quick Start
# Instalar dependencias
npm install
# Configurar variables de entorno
cp ../.env.example .env
# Desarrollo con hot-reload
npm run dev
# El servidor estara en http://localhost:3000
Estructura del Proyecto
src/
├── modules/
│ ├── auth/ # Autenticacion JWT
│ │ ├── dto/
│ │ │ └── auth.dto.ts # DTOs tipados
│ │ ├── middleware/
│ │ │ └── auth.middleware.ts
│ │ ├── services/
│ │ │ └── auth.service.ts
│ │ └── index.ts
│ │
│ ├── budgets/ # MAI-003 Presupuestos
│ │ ├── entities/
│ │ │ ├── concepto.entity.ts
│ │ │ ├── presupuesto.entity.ts
│ │ │ └── presupuesto-partida.entity.ts
│ │ ├── services/
│ │ │ ├── concepto.service.ts
│ │ │ └── presupuesto.service.ts
│ │ └── index.ts
│ │
│ ├── progress/ # MAI-005 Control de Obra
│ │ ├── 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
│ │
│ ├── estimates/ # MAI-008 Estimaciones
│ │ ├── 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
│ │
│ ├── construction/ # MAI-002 Proyectos
│ │ └── entities/
│ │ ├── proyecto.entity.ts
│ │ └── fraccionamiento.entity.ts
│ │
│ ├── hr/ # MAI-007 RRHH
│ │ └── entities/
│ │ ├── employee.entity.ts
│ │ ├── puesto.entity.ts
│ │ └── employee-fraccionamiento.entity.ts
│ │
│ ├── hse/ # MAA-017 Seguridad HSE
│ │ └── entities/
│ │ ├── incidente.entity.ts
│ │ ├── incidente-involucrado.entity.ts
│ │ ├── incidente-accion.entity.ts
│ │ └── capacitacion.entity.ts
│ │
│ └── core/ # Entidades base
│ └── entities/
│ ├── user.entity.ts
│ └── tenant.entity.ts
│
└── shared/
├── constants/ # SSOT
│ ├── database.constants.ts
│ ├── api.constants.ts
│ ├── enums.constants.ts
│ └── index.ts
├── services/
│ └── base.service.ts # CRUD multi-tenant
└── database/
└── typeorm.config.ts
Modulos Implementados
Auth Module
Autenticacion JWT con refresh tokens y multi-tenancy.
// Services
AuthService
├── login(dto) // Login con email/password
├── register(dto) // Registro de usuarios
├── refresh(dto) // Renovar tokens
├── logout(token) // Revocar refresh token
└── changePassword(dto) // Cambiar password
// Middleware
AuthMiddleware
├── authenticate // Validar JWT (requerido)
├── optionalAuthenticate // Validar JWT (opcional)
├── authorize(...roles) // Autorizar por roles
├── requireAdmin // Solo admin/super_admin
└── requireSupervisor // Solo supervisores+
Budgets Module (MAI-003)
Catalogo de conceptos y presupuestos de obra.
// Entities
Concepto // Catalogo jerarquico (arbol)
Presupuesto // Presupuestos versionados
PresupuestoPartida // Lineas con calculo automatico
// Services
ConceptoService
├── createConcepto(ctx, dto) // Crear con nivel/path automatico
├── findRootConceptos(ctx) // Conceptos raiz
├── findChildren(ctx, parentId) // Hijos de un concepto
├── getConceptoTree(ctx, rootId) // Arbol completo
└── search(ctx, term) // Busqueda por codigo/nombre
PresupuestoService
├── createPresupuesto(ctx, dto)
├── findByFraccionamiento(ctx, id)
├── findWithPartidas(ctx, id)
├── addPartida(ctx, id, dto)
├── updatePartida(ctx, id, dto)
├── removePartida(ctx, id)
├── recalculateTotal(ctx, id)
├── createNewVersion(ctx, id) // Versionamiento
└── approve(ctx, id)
Progress Module (MAI-005)
Control de avances fisicos y bitacora de obra.
// Entities
AvanceObra // Avances con workflow
FotoAvance // Evidencias fotograficas con GPS
BitacoraObra // Bitacora diaria
ProgramaObra // Programa maestro
ProgramaActividad // Actividades WBS
// Services
AvanceObraService
├── createAvance(ctx, dto)
├── findByLote(ctx, loteId)
├── findByDepartamento(ctx, deptoId)
├── findWithFilters(ctx, filters)
├── findWithFotos(ctx, id)
├── addFoto(ctx, id, dto)
├── review(ctx, id) // Workflow: revisar
├── approve(ctx, id) // Workflow: aprobar
├── reject(ctx, id, reason) // Workflow: rechazar
└── getAccumulatedProgress(ctx) // Acumulado por concepto
BitacoraObraService
├── createEntry(ctx, dto) // Numero automatico
├── findByFraccionamiento(ctx, id)
├── findWithFilters(ctx, id, filters)
├── findByDate(ctx, id, date)
├── findLatest(ctx, id)
└── getStats(ctx, id) // Estadisticas
Estimates Module (MAI-008)
Estimaciones periodicas con workflow de aprobacion.
// Entities
Estimacion // Estimaciones con workflow
EstimacionConcepto // Lineas con acumulados
Generador // Numeros generadores
Anticipo // Anticipos
Amortizacion // Amortizaciones
Retencion // Retenciones
FondoGarantia // Fondo de garantia
EstimacionWorkflow // Historial de estados
// Services
EstimacionService
├── createEstimacion(ctx, dto) // Numero automatico
├── findByContrato(ctx, contratoId)
├── findWithFilters(ctx, filters)
├── findWithDetails(ctx, id) // Con relaciones
├── addConcepto(ctx, id, dto)
├── addGenerador(ctx, conceptoId, dto)
├── recalculateTotals(ctx, id) // Llama funcion PG
├── submit(ctx, id) // Workflow
├── review(ctx, id) // Workflow
├── approve(ctx, id) // Workflow
├── reject(ctx, id, reason) // Workflow
└── getContractSummary(ctx, id) // Resumen financiero
Base Service
Servicio base con CRUD multi-tenant.
// Uso
class MiService extends BaseService<MiEntity> {
constructor(repository: Repository<MiEntity>) {
super(repository);
}
}
// Metodos disponibles
BaseService<T>
├── findAll(ctx, options?) // Paginado
├── findById(ctx, id)
├── findOne(ctx, where)
├── find(ctx, options)
├── create(ctx, data)
├── update(ctx, id, data)
├── softDelete(ctx, id)
├── hardDelete(ctx, id)
├── count(ctx, where?)
└── exists(ctx, where)
// ServiceContext
interface ServiceContext {
tenantId: string;
userId: string;
}
SSOT Constants
Sistema de constantes centralizadas.
// database.constants.ts
import { DB_SCHEMAS, DB_TABLES, TABLE_REFS } from '@shared/constants';
DB_SCHEMAS.CONSTRUCTION // 'construction'
DB_TABLES.construction.CONCEPTOS // 'conceptos'
TABLE_REFS.FRACCIONAMIENTOS // 'construction.fraccionamientos'
// api.constants.ts
import { API_ROUTES } from '@shared/constants';
API_ROUTES.PRESUPUESTOS.BASE // '/api/v1/presupuestos'
API_ROUTES.ESTIMACIONES.BY_ID(id) // '/api/v1/estimaciones/:id'
// enums.constants.ts
import { ROLES, PROJECT_STATUS } from '@shared/constants';
ROLES.ADMIN // 'admin'
PROJECT_STATUS.IN_PROGRESS // 'in_progress'
Scripts NPM
# Desarrollo
npm run dev # Hot-reload con ts-node-dev
npm run build # Compilar TypeScript
npm run start # Produccion (dist/)
# Calidad
npm run lint # ESLint
npm run lint:fix # ESLint con autofix
npm run test # Jest
npm run test:watch # Jest watch mode
npm run test:coverage # Jest con cobertura
# Base de datos
npm run migration:generate # Generar migracion
npm run migration:run # Ejecutar migraciones
npm run migration:revert # Revertir ultima
# SSOT
npm run validate:constants # Validar no hardcoding
npm run sync:enums # Sincronizar a frontend
npm run precommit # lint + validate
Convenciones
Nomenclatura
| Tipo | Convencion | Ejemplo |
|---|---|---|
| Archivos | kebab-case.tipo.ts | concepto.entity.ts |
| Clases | PascalCase + sufijo | ConceptoService |
| Variables | camelCase | totalAmount |
| Constantes | UPPER_SNAKE_CASE | DB_SCHEMAS |
| Metodos | camelCase + verbo | findByContrato |
Entity Pattern
@Entity({ schema: 'construction', name: 'conceptos' })
@Index(['tenantId', 'code'], { unique: true })
export class Concepto {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column({ name: 'tenant_id', type: 'uuid' })
tenantId: string;
// ... columnas con name: 'snake_case'
// Soft delete
@Column({ name: 'deleted_at', type: 'timestamptz', nullable: true })
deletedAt: Date | null;
// Relations
@ManyToOne(() => Tenant)
@JoinColumn({ name: 'tenant_id' })
tenant: Tenant;
}
Service Pattern
export class MiService extends BaseService<MiEntity> {
constructor(
repository: Repository<MiEntity>,
private readonly otroRepo: Repository<OtroEntity>
) {
super(repository);
}
async miMetodo(ctx: ServiceContext, data: MiDto): Promise<MiEntity> {
// ctx tiene tenantId y userId
return this.create(ctx, data);
}
}
Seguridad
- Helmet para HTTP security headers
- CORS configurado por dominio
- Rate limiting por IP
- JWT con refresh tokens
- Bcrypt (12 rounds) para passwords
- class-validator para inputs
- RLS para aislamiento de tenants
Testing
# Ejecutar tests
npm test
# Con cobertura
npm run test:coverage
# Watch mode
npm run test:watch
// Ejemplo de test
describe('ConceptoService', () => {
let service: ConceptoService;
let mockRepo: jest.Mocked<Repository<Concepto>>;
beforeEach(() => {
mockRepo = createMockRepository();
service = new ConceptoService(mockRepo);
});
it('should create concepto with level', async () => {
const ctx = { tenantId: 'uuid', userId: 'uuid' };
const dto = { code: '001', name: 'Test' };
mockRepo.save.mockResolvedValue({ ...dto, level: 0 });
const result = await service.createConcepto(ctx, dto);
expect(result.level).toBe(0);
});
});
Debugging
VS Code
{
"type": "node",
"request": "launch",
"name": "Debug Backend",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/server.ts"],
"env": { "NODE_ENV": "development" }
}
Logs
// Configurar en .env
LOG_LEVEL=debug
LOG_FORMAT=dev
Ultima actualizacion: 2025-12-12