# 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 ```bash # 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. ```typescript // 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. ```typescript // 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. ```typescript // 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. ```typescript // 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. ```typescript // Uso class MiService extends BaseService { constructor(repository: Repository) { super(repository); } } // Metodos disponibles BaseService ├── 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. ```typescript // 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 ```bash # 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 ```typescript @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 ```typescript export class MiService extends BaseService { constructor( repository: Repository, private readonly otroRepo: Repository ) { super(repository); } async miMetodo(ctx: ServiceContext, data: MiDto): Promise { // 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 ```bash # Ejecutar tests npm test # Con cobertura npm run test:coverage # Watch mode npm run test:watch ``` ```typescript // Ejemplo de test describe('ConceptoService', () => { let service: ConceptoService; let mockRepo: jest.Mocked>; 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 ```json { "type": "node", "request": "launch", "name": "Debug Backend", "runtimeArgs": ["-r", "ts-node/register"], "args": ["${workspaceFolder}/src/server.ts"], "env": { "NODE_ENV": "development" } } ``` ### Logs ```typescript // Configurar en .env LOG_LEVEL=debug LOG_FORMAT=dev ``` --- **Ultima actualizacion:** 2025-12-12