Migración desde erp-construccion/database - Estándar multi-repo v2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5744bc50ed
commit
bf97e26cdf
362
HERENCIA-ERP-CORE.md
Normal file
362
HERENCIA-ERP-CORE.md
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
# Referencia de Base de Datos - ERP Construcción
|
||||||
|
|
||||||
|
**Fecha:** 2025-12-09
|
||||||
|
**Versión:** 1.2
|
||||||
|
**Proyecto:** ERP Construcción
|
||||||
|
**Nivel:** 2B.2 (Proyecto Independiente)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RESUMEN
|
||||||
|
|
||||||
|
ERP Construcción es un **proyecto independiente** que implementa y adapta patrones del ERP-Core para el dominio de construcción de vivienda. No es una extensión del core, sino un sistema autónomo que:
|
||||||
|
|
||||||
|
1. **Implementa** schemas propios basados en patrones del core
|
||||||
|
2. **Adapta** estructuras de datos al dominio de construcción
|
||||||
|
3. **Reutiliza** código y patrones donde tiene sentido
|
||||||
|
4. **Opera independientemente** del ERP-Core
|
||||||
|
|
||||||
|
**DDL de Referencia (Core):** `apps/erp-core/database/ddl/`
|
||||||
|
**DDL Propio:** `database/schemas/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ARQUITECTURA DEL PROYECTO
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ERP CORE (Referencia) │
|
||||||
|
│ Patrones, specs y estructuras reutilizables │
|
||||||
|
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
|
||||||
|
│ │ auth │ │ core │ │inventory│ │ financial│ │
|
||||||
|
│ │ patrones│ │ patrones│ │ patrones│ │ patrones │ │
|
||||||
|
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
│ REFERENCIA / FORK
|
||||||
|
▼
|
||||||
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
|
│ ERP CONSTRUCCIÓN (Proyecto Independiente) │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||||
|
│ │construction │ │ hr │ │ hse │ │
|
||||||
|
│ │ 24 tbl │ │ 8 tbl │ │ 58 tbl │ │
|
||||||
|
│ │ (proyectos) │ │ (empleados) │ │ (seguridad) │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||||
|
│ │ estimates │ │ infonavit │ │ inventory │ │
|
||||||
|
│ │ 8 tbl │ │ 8 tbl │ │ 4 tbl │ │
|
||||||
|
│ │(estimación) │ │ (ruv) │ │ (ext) │ │
|
||||||
|
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||||
|
│ │
|
||||||
|
│ ┌─────────────┐ │
|
||||||
|
│ │ purchase │ Schemas propios: 7 │
|
||||||
|
│ │ 5 tbl │ Tablas propias: 110 │
|
||||||
|
│ │ (ext) │ Opera de forma INDEPENDIENTE │
|
||||||
|
│ └─────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PATRONES REUTILIZADOS DEL CORE
|
||||||
|
|
||||||
|
Los siguientes patrones del ERP-Core fueron **adaptados e implementados** en este proyecto:
|
||||||
|
|
||||||
|
| Patrón del Core | Adaptación en Construcción |
|
||||||
|
|-----------------|---------------------------|
|
||||||
|
| `auth.*` | Implementación propia de autenticación multi-tenant |
|
||||||
|
| `core.partners` | Contratistas, proveedores, clientes |
|
||||||
|
| `inventory.*` | Materiales de construcción, almacenes de obra |
|
||||||
|
| `projects.*` | Obras, fraccionamientos, etapas |
|
||||||
|
| `hr.*` | Personal de obra, cuadrillas, asistencias |
|
||||||
|
|
||||||
|
**Nota:** Este proyecto NO depende del ERP-Core para ejecutarse. Implementa sus propios schemas basados en los patrones de referencia.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SCHEMAS ESPECÍFICOS DE CONSTRUCCIÓN
|
||||||
|
|
||||||
|
### 1. Schema `construction` (24 tablas)
|
||||||
|
|
||||||
|
**Propósito:** Gestión de proyectos de obra, estructura y avances
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- DDL: 01-construction-schema-ddl.sql
|
||||||
|
-- Estructura de proyecto (8 tablas):
|
||||||
|
-- fraccionamientos, etapas, manzanas, lotes, torres, niveles, departamentos, prototipos
|
||||||
|
-- Presupuestos y Conceptos (3 tablas):
|
||||||
|
-- conceptos, presupuestos, presupuesto_partidas
|
||||||
|
-- Programación y Avances (5 tablas):
|
||||||
|
-- programa_obra, programa_actividades, avances_obra, fotos_avance, bitacora_obra
|
||||||
|
-- Calidad (5 tablas):
|
||||||
|
-- checklists, checklist_items, inspecciones, inspeccion_resultados, tickets_postventa
|
||||||
|
-- Contratos (3 tablas):
|
||||||
|
-- subcontratistas, contratos, contrato_partidas
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Schema `hr` extendido (8 tablas)
|
||||||
|
|
||||||
|
**Propósito:** Gestión de personal de obra, asistencias, destajo
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- DDL: 02-hr-schema-ddl.sql
|
||||||
|
-- Extiende: hr schema del core
|
||||||
|
|
||||||
|
hr.employee_construction -- Extensión empleados construcción
|
||||||
|
hr.asistencias -- Registro con GPS/biométrico
|
||||||
|
hr.asistencia_biometrico -- Datos biométricos
|
||||||
|
hr.geocercas -- Validación GPS (PostGIS)
|
||||||
|
hr.destajo -- Trabajo a destajo
|
||||||
|
hr.destajo_detalle -- Mediciones destajo
|
||||||
|
hr.cuadrillas -- Equipos de trabajo
|
||||||
|
hr.cuadrilla_miembros -- Miembros cuadrillas
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Schema `hse` (58 tablas)
|
||||||
|
|
||||||
|
**Propósito:** Health, Safety & Environment
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- DDL: 03-hse-schema-ddl.sql
|
||||||
|
-- Implementa 8 requerimientos funcionales (RF-MAA017-001 a 008)
|
||||||
|
|
||||||
|
Grupos de tablas:
|
||||||
|
- Gestión de Incidentes (5 tablas)
|
||||||
|
- Control de Capacitaciones (6 tablas)
|
||||||
|
- Inspecciones de Seguridad (7 tablas)
|
||||||
|
- Control de EPP (7 tablas)
|
||||||
|
- Cumplimiento STPS (11 tablas)
|
||||||
|
- Gestión Ambiental (9 tablas)
|
||||||
|
- Permisos de Trabajo (8 tablas)
|
||||||
|
- Indicadores HSE (7 tablas)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Schema `estimates` (8 tablas)
|
||||||
|
|
||||||
|
**Propósito:** Estimaciones, anticipos, retenciones
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- DDL: 04-estimates-schema-ddl.sql
|
||||||
|
-- Módulo: MAI-008 (Estimaciones y Facturación)
|
||||||
|
|
||||||
|
estimates.estimaciones -- Estimaciones de obra
|
||||||
|
estimates.estimacion_conceptos -- Conceptos estimados
|
||||||
|
estimates.generadores -- Números generadores
|
||||||
|
estimates.anticipos -- Anticipos de obra
|
||||||
|
estimates.amortizaciones -- Amortización de anticipos
|
||||||
|
estimates.retenciones -- Retenciones (garantía, IMSS, ISR)
|
||||||
|
estimates.fondo_garantia -- Fondo de garantía
|
||||||
|
estimates.estimacion_workflow -- Workflow de aprobación
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Schema `infonavit` (8 tablas)
|
||||||
|
|
||||||
|
**Propósito:** Integración INFONAVIT, RUV, derechohabientes
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- DDL: 05-infonavit-schema-ddl.sql
|
||||||
|
-- Módulos: MAI-010/011 (CRM Derechohabientes, Integración INFONAVIT)
|
||||||
|
|
||||||
|
infonavit.registro_infonavit -- Registro RUV
|
||||||
|
infonavit.oferta_vivienda -- Oferta registrada
|
||||||
|
infonavit.derechohabientes -- Derechohabientes
|
||||||
|
infonavit.asignacion_vivienda -- Asignaciones
|
||||||
|
infonavit.actas -- Actas de entrega
|
||||||
|
infonavit.acta_viviendas -- Viviendas en acta
|
||||||
|
infonavit.reportes_infonavit -- Reportes RUV
|
||||||
|
infonavit.historico_puntos -- Histórico puntos ecológicos
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Schema `inventory` extensión (4 tablas)
|
||||||
|
|
||||||
|
**Propósito:** Almacenes de proyecto, requisiciones de obra
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- DDL: 06-inventory-ext-schema-ddl.sql
|
||||||
|
-- Extiende: inventory schema del core
|
||||||
|
|
||||||
|
inventory.almacenes_proyecto -- Almacenes por obra
|
||||||
|
inventory.requisiciones_obra -- Requisiciones desde obra
|
||||||
|
inventory.requisicion_lineas -- Líneas de requisición
|
||||||
|
inventory.consumos_obra -- Consumos por lote/concepto
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Schema `purchase` extensión (5 tablas)
|
||||||
|
|
||||||
|
**Propósito:** Órdenes de compra construcción, comparativos
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- DDL: 07-purchase-ext-schema-ddl.sql
|
||||||
|
-- Extiende: purchase schema del core
|
||||||
|
|
||||||
|
purchase.purchase_order_construction -- Extensión OC
|
||||||
|
purchase.supplier_construction -- Extensión proveedores
|
||||||
|
purchase.comparativo_cotizaciones -- Cuadro comparativo
|
||||||
|
purchase.comparativo_proveedores -- Proveedores en comparativo
|
||||||
|
purchase.comparativo_productos -- Productos cotizados
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ORDEN DE EJECUCIÓN DDL
|
||||||
|
|
||||||
|
Para recrear la base de datos completa:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# PASO 1: Cargar ERP Core (base)
|
||||||
|
cd apps/erp-core/database
|
||||||
|
./scripts/reset-database.sh --force
|
||||||
|
|
||||||
|
# PASO 2: Cargar extensiones de Construcción (orden importante)
|
||||||
|
cd apps/verticales/construccion/database
|
||||||
|
psql $DATABASE_URL -f schemas/01-construction-schema-ddl.sql # 24 tablas
|
||||||
|
psql $DATABASE_URL -f schemas/02-hr-schema-ddl.sql # 8 tablas
|
||||||
|
psql $DATABASE_URL -f schemas/03-hse-schema-ddl.sql # 58 tablas
|
||||||
|
psql $DATABASE_URL -f schemas/04-estimates-schema-ddl.sql # 8 tablas
|
||||||
|
psql $DATABASE_URL -f schemas/05-infonavit-schema-ddl.sql # 8 tablas
|
||||||
|
psql $DATABASE_URL -f schemas/06-inventory-ext-schema-ddl.sql # 4 tablas
|
||||||
|
psql $DATABASE_URL -f schemas/07-purchase-ext-schema-ddl.sql # 5 tablas
|
||||||
|
```
|
||||||
|
|
||||||
|
**Nota:** Los archivos 06 y 07 dependen de que 01-construction esté instalado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DEPENDENCIAS CRUZADAS
|
||||||
|
|
||||||
|
### Tablas de Construcción que dependen del Core
|
||||||
|
|
||||||
|
| Tabla Construcción | Depende de (Core) |
|
||||||
|
|--------------------|-------------------|
|
||||||
|
| `construccion.proyectos` | `core.partners` (cliente) |
|
||||||
|
| `construccion.proyectos` | `auth.users` (created_by) |
|
||||||
|
| `construccion.fraccionamientos` | `construccion.proyectos` |
|
||||||
|
| `hr.employees` | `auth.users` |
|
||||||
|
| `hr.employee_fraccionamientos` | `construccion.fraccionamientos` |
|
||||||
|
| `hse.incidentes` | `construccion.fraccionamientos` |
|
||||||
|
| `hse.incidente_involucrados` | `hr.employees` |
|
||||||
|
| `hse.*` | `auth.users` (auditoria) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SPECS DEL CORE IMPLEMENTADAS
|
||||||
|
|
||||||
|
| Spec Core | Aplicación en Construcción | Estado |
|
||||||
|
|-----------|---------------------------|--------|
|
||||||
|
| SPEC-VALORACION-INVENTARIO | Materiales de construcción | ✅ DDL LISTO |
|
||||||
|
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes de concreto, acero | ✅ DDL LISTO |
|
||||||
|
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | Partidas de obra | PENDIENTE |
|
||||||
|
| SPEC-MAIL-THREAD-TRACKING | Historial de presupuestos | PENDIENTE |
|
||||||
|
| SPEC-WIZARD-TRANSIENT-MODEL | Asistentes de estimaciones | PENDIENTE |
|
||||||
|
|
||||||
|
### Correcciones de DDL Core (2025-12-08)
|
||||||
|
|
||||||
|
El DDL del ERP-Core fue corregido para resolver FK inválidas:
|
||||||
|
|
||||||
|
1. **stock_valuation_layers**: Campos `journal_entry_id` y `journal_entry_line_id` (antes `account_move_*`)
|
||||||
|
2. **stock_move_consume_rel**: Nueva tabla de trazabilidad (antes `move_line_consume_rel`)
|
||||||
|
3. **category_stock_accounts**: FK corregida a `core.product_categories`
|
||||||
|
4. **product_categories**: ALTERs ahora apuntan a schema `core`
|
||||||
|
|
||||||
|
Estas correcciones permiten que el DDL de inventory se ejecute correctamente.
|
||||||
|
|
||||||
|
### Correcciones de DDL Construcción (2025-12-08)
|
||||||
|
|
||||||
|
El DDL de la vertical Construcción fue corregido para alinearse con ERP-Core:
|
||||||
|
|
||||||
|
| Archivo | Correcciones | Detalle |
|
||||||
|
|---------|--------------|---------|
|
||||||
|
| `01-construction-schema-ddl.sql` | 4 FK | `core.tenants` → `auth.tenants`, `core.users` → `auth.users` |
|
||||||
|
| `02-hr-schema-ddl.sql` | 4 FK | Referencias corregidas a `auth.*` |
|
||||||
|
| `03-hse-schema-ddl.sql` | 42 FK | Todas las referencias corregidas |
|
||||||
|
| **Total** | **50 FK** | Ahora usa `auth.tenants` y `auth.users` correctamente |
|
||||||
|
|
||||||
|
**Verificaciones de prerequisitos actualizadas:**
|
||||||
|
- Los DDL ahora validan que `auth.tenants` y `auth.users` existan antes de crear tablas
|
||||||
|
- ERP-Core debe estar instalado antes de ejecutar DDL de Construcción
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MAPEO DE NOMENCLATURA
|
||||||
|
|
||||||
|
| Core | Construcción |
|
||||||
|
|------|--------------|
|
||||||
|
| `core.partners` | Contratistas, proveedores |
|
||||||
|
| `inventory.products` | Materiales de construcción |
|
||||||
|
| `inventory.locations` | Almacenes de obra |
|
||||||
|
| `projects.projects` | Base para `construccion.proyectos` |
|
||||||
|
| `hr.employees` | Personal de obra |
|
||||||
|
| `purchase.orders` | Órdenes de compra de materiales |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## VALIDACIÓN DE HERENCIA
|
||||||
|
|
||||||
|
### Verificar schemas heredados
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Verificar que existen los schemas del core
|
||||||
|
SELECT schema_name
|
||||||
|
FROM information_schema.schemata
|
||||||
|
WHERE schema_name IN ('auth', 'core', 'financial', 'inventory',
|
||||||
|
'purchase', 'projects', 'hr', 'analytics', 'system');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verificar extensiones de construcción
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Verificar schemas específicos
|
||||||
|
SELECT schema_name
|
||||||
|
FROM information_schema.schemata
|
||||||
|
WHERE schema_name IN ('construccion', 'hse');
|
||||||
|
|
||||||
|
-- Contar tablas por schema
|
||||||
|
SELECT schemaname, COUNT(*) as tables
|
||||||
|
FROM pg_tables
|
||||||
|
WHERE schemaname IN ('construccion', 'hr', 'hse')
|
||||||
|
GROUP BY schemaname;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SPECS DEL CORE APLICABLES
|
||||||
|
|
||||||
|
Según el [MAPEO-SPECS-VERTICALES.md](../../../../erp-core/docs/04-modelado/MAPEO-SPECS-VERTICALES.md):
|
||||||
|
|
||||||
|
| Categoría | Total | Obligatorias | Opcionales | No Aplican |
|
||||||
|
|-----------|-------|--------------|------------|------------|
|
||||||
|
| **Construcción** | 30 | 22 | 4 | 4 |
|
||||||
|
|
||||||
|
### SPECS Críticas para Construcción
|
||||||
|
|
||||||
|
| SPEC | Aplicación | Estado DDL |
|
||||||
|
|------|------------|------------|
|
||||||
|
| SPEC-VALORACION-INVENTARIO | Costeo de materiales | ✅ DDL LISTO |
|
||||||
|
| SPEC-TRAZABILIDAD-LOTES-SERIES | Lotes de concreto, acero | ✅ DDL LISTO |
|
||||||
|
| SPEC-PROYECTOS-DEPENDENCIAS-BURNDOWN | Partidas de obra | PENDIENTE |
|
||||||
|
| SPEC-PRESUPUESTOS-REVISIONES | Control presupuestal | PENDIENTE |
|
||||||
|
| SPEC-RRHH-EVALUACIONES-SKILLS | Personal de obra | PENDIENTE |
|
||||||
|
|
||||||
|
### SPECS No Aplicables
|
||||||
|
|
||||||
|
- `SPEC-INTEGRACION-CALENDAR` - Sin necesidad de calendario externo
|
||||||
|
- `SPEC-OAUTH2-SOCIAL-LOGIN` - Opcional, no crítico
|
||||||
|
- `SPEC-INVENTARIOS-CICLICOS` - Opcional para construcción
|
||||||
|
- `SPEC-CONSOLIDACION-FINANCIERA` - Opcional para construcción
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REFERENCIAS
|
||||||
|
|
||||||
|
- ERP Core DDL: `apps/erp-core/database/ddl/`
|
||||||
|
- ERP Core README: `apps/erp-core/database/README.md`
|
||||||
|
- MAPEO-SPECS-VERTICALES: `apps/erp-core/docs/04-modelado/MAPEO-SPECS-VERTICALES.md`
|
||||||
|
- DATABASE_INVENTORY.yml: `orchestration/inventarios/`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Documento de herencia oficial**
|
||||||
|
**Última actualización:** 2025-12-09
|
||||||
|
**Total schemas:** 7 | **Total tablas:** 110
|
||||||
186
README.md
186
README.md
@ -1,3 +1,185 @@
|
|||||||
# erp-construccion-database-v2
|
# Database - MVP Sistema Administración de Obra
|
||||||
|
|
||||||
Database de erp-construccion - Workspace V2
|
**Stack:** PostgreSQL 15+ con PostGIS
|
||||||
|
**Versión:** 1.0.0
|
||||||
|
**Fecha:** 2025-11-20
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 DESCRIPCIÓN
|
||||||
|
|
||||||
|
Base de datos del sistema de administración de obra e INFONAVIT.
|
||||||
|
|
||||||
|
**Schemas principales:**
|
||||||
|
- `auth_management` - Autenticación y usuarios
|
||||||
|
- `project_management` - Proyectos y estructura de obra
|
||||||
|
- `financial_management` - Presupuestos y control financiero
|
||||||
|
- `purchasing_management` - Compras e inventarios
|
||||||
|
- `construction_management` - Control de obra y avances
|
||||||
|
- `quality_management` - Calidad y postventa
|
||||||
|
- `infonavit_management` - Integración INFONAVIT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🏗️ ESTRUCTURA
|
||||||
|
|
||||||
|
```
|
||||||
|
ddl/
|
||||||
|
├── 00-init.sql # Inicialización + extensiones
|
||||||
|
└── schemas/ # Schemas por contexto
|
||||||
|
├── auth_management/
|
||||||
|
│ ├── tables/ # Tablas (01-users.sql, 02-roles.sql, ...)
|
||||||
|
│ ├── functions/ # Funciones SQL
|
||||||
|
│ ├── triggers/ # Triggers
|
||||||
|
│ └── views/ # Vistas
|
||||||
|
├── project_management/
|
||||||
|
│ └── ...
|
||||||
|
└── [otros schemas]/
|
||||||
|
|
||||||
|
seeds/
|
||||||
|
├── dev/ # Datos de desarrollo
|
||||||
|
└── prod/ # Datos de producción inicial
|
||||||
|
|
||||||
|
migrations/ # Migraciones versionadas
|
||||||
|
scripts/ # Scripts de utilidad
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 SETUP INICIAL
|
||||||
|
|
||||||
|
### 1. Crear Base de Datos
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ejecutar script de creación
|
||||||
|
cd scripts
|
||||||
|
./create-database.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Ejecutar DDL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Ejecutar inicialización
|
||||||
|
psql $DATABASE_URL -f ddl/00-init.sql
|
||||||
|
|
||||||
|
# Ejecutar schemas (en orden)
|
||||||
|
psql $DATABASE_URL -f ddl/schemas/auth_management/tables/01-users.sql
|
||||||
|
# ... etc
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Cargar Seeds (desarrollo)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
psql $DATABASE_URL -f seeds/dev/01-users.sql
|
||||||
|
psql $DATABASE_URL -f seeds/dev/02-projects.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 SCRIPTS DISPONIBLES
|
||||||
|
|
||||||
|
| Script | Descripción |
|
||||||
|
|--------|-------------|
|
||||||
|
| `create-database.sh` | Crea la base de datos desde cero |
|
||||||
|
| `reset-database.sh` | Elimina y recrea la base de datos |
|
||||||
|
| `run-migrations.sh` | Ejecuta migraciones pendientes |
|
||||||
|
| `backup-database.sh` | Crea backup de la base de datos |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📊 CONVENCIONES
|
||||||
|
|
||||||
|
### Nomenclatura
|
||||||
|
|
||||||
|
Seguir **ESTANDARES-NOMENCLATURA.md**:
|
||||||
|
- Schemas: `snake_case` + sufijo `_management`
|
||||||
|
- Tablas: `snake_case` plural
|
||||||
|
- Columnas: `snake_case` singular
|
||||||
|
- Índices: `idx_{tabla}_{columnas}`
|
||||||
|
- Foreign Keys: `fk_{origen}_to_{destino}`
|
||||||
|
- Constraints: `chk_{tabla}_{columna}`
|
||||||
|
|
||||||
|
### Estructura de Archivo DDL
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- ============================================================================
|
||||||
|
-- Tabla: nombre_tabla
|
||||||
|
-- Schema: nombre_schema
|
||||||
|
-- Descripción: [descripción]
|
||||||
|
-- Autor: Database-Agent
|
||||||
|
-- Fecha: YYYY-MM-DD
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS schema.tabla CASCADE;
|
||||||
|
|
||||||
|
CREATE TABLE schema.tabla (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
-- columnas...
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
|
||||||
|
COMMENT ON TABLE schema.tabla IS '[descripción]';
|
||||||
|
COMMENT ON COLUMN schema.tabla.columna IS '[descripción]';
|
||||||
|
|
||||||
|
CREATE INDEX idx_tabla_columna ON schema.tabla(columna);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔍 VALIDACIÓN
|
||||||
|
|
||||||
|
### Verificar Schemas
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT schema_name
|
||||||
|
FROM information_schema.schemata
|
||||||
|
WHERE schema_name LIKE '%_management';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verificar Tablas
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT table_schema, table_name,
|
||||||
|
(SELECT COUNT(*) FROM information_schema.columns
|
||||||
|
WHERE table_schema = t.table_schema
|
||||||
|
AND table_name = t.table_name) as column_count
|
||||||
|
FROM information_schema.tables t
|
||||||
|
WHERE table_schema LIKE '%_management'
|
||||||
|
ORDER BY table_schema, table_name;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verificar Índices
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT tablename, indexname
|
||||||
|
FROM pg_indexes
|
||||||
|
WHERE schemaname LIKE '%_management'
|
||||||
|
ORDER BY tablename;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 REFERENCIAS
|
||||||
|
|
||||||
|
- [DIRECTIVA-DISENO-BASE-DATOS.md](../../orchestration/directivas/DIRECTIVA-DISENO-BASE-DATOS.md)
|
||||||
|
- [ESTANDARES-NOMENCLATURA.md](../../orchestration/directivas/ESTANDARES-NOMENCLATURA.md)
|
||||||
|
- [MVP-APP.md](../../docs/00-overview/MVP-APP.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📝 VARIABLES DE ENTORNO
|
||||||
|
|
||||||
|
```bash
|
||||||
|
DATABASE_URL=postgresql://usuario:password@localhost:5432/nombre_db
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=5432
|
||||||
|
DB_NAME=erp_construccion
|
||||||
|
DB_USER=postgres
|
||||||
|
DB_PASSWORD=password
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Mantenido por:** Database-Agent
|
||||||
|
**Última actualización:** 2025-11-20
|
||||||
|
|||||||
323
VALIDACION-DDL-INVENTARIOS.md
Normal file
323
VALIDACION-DDL-INVENTARIOS.md
Normal file
@ -0,0 +1,323 @@
|
|||||||
|
# VALIDACION DDL vs INVENTARIOS - ERP CONSTRUCCION
|
||||||
|
**Fecha:** 2025-12-06
|
||||||
|
**Version:** 1.0.0
|
||||||
|
**Generado por:** Requirements-Analyst
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RESUMEN EJECUTIVO
|
||||||
|
|
||||||
|
| Metrica | Inventarios | DDL Real | Estado |
|
||||||
|
|---------|-------------|----------|--------|
|
||||||
|
| **Schemas** | 6 (+ 3 pendientes) | 7 implementados | DISCREPANCIA |
|
||||||
|
| **Tablas declaradas** | 57 | 65 | DISCREPANCIA |
|
||||||
|
| **HSE Schema** | "pendiente" | 58 tablas implementadas | DESACTUALIZADO |
|
||||||
|
| **ENUMs** | 22 | 89 (22 base + 67 HSE) | DESACTUALIZADO |
|
||||||
|
|
||||||
|
### Conclusion General
|
||||||
|
Los inventarios MASTER_INVENTORY.yml y DATABASE_INVENTORY.yml estan **DESACTUALIZADOS** respecto al DDL implementado. El schema HSE con 58 tablas y 67 ENUMs ya fue implementado pero los inventarios lo marcan como "pendiente".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ANALISIS DE OBJETOS DDL IMPLEMENTADOS
|
||||||
|
|
||||||
|
### 1.1 Schemas Creados (7 schemas de negocio)
|
||||||
|
|
||||||
|
| Schema | Origen | Tablas | ENUMs | Estado |
|
||||||
|
|--------|--------|--------|-------|--------|
|
||||||
|
| core | construccion | 2 | 0 | Implementado |
|
||||||
|
| core_shared | construccion | 0 | 0 | Implementado (funciones) |
|
||||||
|
| construction | construccion | 2 | 0 | Implementado |
|
||||||
|
| hr | construccion | 3 | 0 | Implementado |
|
||||||
|
| hse | construccion | 58 | 67 | **IMPLEMENTADO** |
|
||||||
|
| estimates | construccion | 0 | 0 | Schema vacio |
|
||||||
|
| infonavit | construccion | 0 | 0 | Schema vacio |
|
||||||
|
|
||||||
|
### 1.2 Tablas por Schema (Conteo Real del DDL)
|
||||||
|
|
||||||
|
#### core (2 tablas)
|
||||||
|
- `core.tenants` - Multi-tenancy base
|
||||||
|
- `core.users` - Usuarios base
|
||||||
|
|
||||||
|
#### construction (2 tablas)
|
||||||
|
- `construction.proyectos` - Proyectos de desarrollo
|
||||||
|
- `construction.fraccionamientos` - Obras/fraccionamientos
|
||||||
|
|
||||||
|
#### hr (3 tablas)
|
||||||
|
- `hr.employees` - Empleados
|
||||||
|
- `hr.puestos` - Catalogo de puestos
|
||||||
|
- `hr.employee_fraccionamientos` - Asignacion empleados a obras
|
||||||
|
|
||||||
|
#### hse (58 tablas) - RF-MAA017-001 a RF-MAA017-008
|
||||||
|
|
||||||
|
**RF-MAA017-001 Gestion de Incidentes (5 tablas):**
|
||||||
|
- `hse.incidentes`
|
||||||
|
- `hse.incidente_involucrados`
|
||||||
|
- `hse.incidente_investigacion`
|
||||||
|
- `hse.incidente_acciones`
|
||||||
|
- `hse.incidente_evidencias`
|
||||||
|
|
||||||
|
**RF-MAA017-002 Control de Capacitaciones (6 tablas):**
|
||||||
|
- `hse.capacitaciones`
|
||||||
|
- `hse.capacitacion_matriz`
|
||||||
|
- `hse.instructores`
|
||||||
|
- `hse.capacitacion_sesiones`
|
||||||
|
- `hse.capacitacion_asistentes`
|
||||||
|
- `hse.constancias_dc3`
|
||||||
|
|
||||||
|
**RF-MAA017-003 Inspecciones de Seguridad (7 tablas):**
|
||||||
|
- `hse.tipos_inspeccion`
|
||||||
|
- `hse.checklist_items`
|
||||||
|
- `hse.programa_inspecciones`
|
||||||
|
- `hse.inspecciones`
|
||||||
|
- `hse.inspeccion_evaluaciones`
|
||||||
|
- `hse.hallazgos`
|
||||||
|
- `hse.hallazgo_evidencias`
|
||||||
|
|
||||||
|
**RF-MAA017-004 Control de EPP (7 tablas):**
|
||||||
|
- `hse.epp_catalogo`
|
||||||
|
- `hse.epp_matriz_puesto`
|
||||||
|
- `hse.epp_asignaciones`
|
||||||
|
- `hse.epp_inspecciones`
|
||||||
|
- `hse.epp_bajas`
|
||||||
|
- `hse.epp_inventario`
|
||||||
|
- `hse.epp_movimientos`
|
||||||
|
|
||||||
|
**RF-MAA017-005 Cumplimiento STPS (11 tablas):**
|
||||||
|
- `hse.normas_stps`
|
||||||
|
- `hse.norma_requisitos`
|
||||||
|
- `hse.cumplimiento_obra`
|
||||||
|
- `hse.comision_seguridad`
|
||||||
|
- `hse.comision_integrantes`
|
||||||
|
- `hse.comision_recorridos`
|
||||||
|
- `hse.programa_seguridad`
|
||||||
|
- `hse.programa_actividades`
|
||||||
|
- `hse.documentos_stps`
|
||||||
|
- `hse.auditorias`
|
||||||
|
|
||||||
|
**RF-MAA017-006 Gestion Ambiental (9 tablas):**
|
||||||
|
- `hse.residuos_catalogo`
|
||||||
|
- `hse.residuos_generacion`
|
||||||
|
- `hse.almacen_temporal`
|
||||||
|
- `hse.proveedores_ambientales`
|
||||||
|
- `hse.manifiestos_residuos`
|
||||||
|
- `hse.manifiesto_detalle`
|
||||||
|
- `hse.impacto_ambiental`
|
||||||
|
- `hse.quejas_ambientales`
|
||||||
|
|
||||||
|
**RF-MAA017-007 Permisos de Trabajo (8 tablas):**
|
||||||
|
- `hse.tipos_permiso_trabajo`
|
||||||
|
- `hse.permisos_trabajo`
|
||||||
|
- `hse.permiso_personal`
|
||||||
|
- `hse.permiso_autorizaciones`
|
||||||
|
- `hse.permiso_checklist`
|
||||||
|
- `hse.permiso_monitoreos`
|
||||||
|
- `hse.permiso_eventos`
|
||||||
|
- `hse.permiso_documentos`
|
||||||
|
|
||||||
|
**RF-MAA017-008 Indicadores HSE (6 tablas):**
|
||||||
|
- `hse.indicadores_config`
|
||||||
|
- `hse.indicadores_meta_obra`
|
||||||
|
- `hse.indicadores_valores`
|
||||||
|
- `hse.horas_trabajadas`
|
||||||
|
- `hse.dias_sin_accidente`
|
||||||
|
- `hse.reportes_programados`
|
||||||
|
- `hse.alertas_indicadores`
|
||||||
|
|
||||||
|
### 1.3 ENUMs Implementados (89 total)
|
||||||
|
|
||||||
|
**ENUMs HSE (67):**
|
||||||
|
- Incidentes: `tipo_incidente`, `gravedad_incidente`, `estado_incidente`, `rol_involucrado`, `factor_causa`
|
||||||
|
- Capacitaciones: `tipo_capacitacion`, `estado_sesion`
|
||||||
|
- Inspecciones: `frecuencia`, `estado_inspeccion`, `resultado_evaluacion`, `gravedad_hallazgo`, `estado_hallazgo`, `tipo_evidencia`
|
||||||
|
- EPP: `categoria_epp`, `estado_epp`, `estado_inspeccion_epp`, `motivo_baja_epp`, `tipo_movimiento_epp`
|
||||||
|
- STPS: `estado_comision`, `rol_comision`, `representacion`, `estado_recorrido`, `estado_programa`, `tipo_actividad_programa`, `estado_actividad`, `tipo_documento_stps`, `tipo_auditoria`, `resultado_auditoria`, `estado_cumplimiento`
|
||||||
|
- Ambiental: `categoria_residuo`, `unidad_residuo`, `estado_residuo`, `estado_almacen`, `tipo_proveedor_ambiental`, `estado_manifiesto`, `tipo_impacto`, `severidad`, `probabilidad`, `nivel_riesgo`, `estado_impacto`, `origen_queja`, `tipo_queja`, `estado_queja`
|
||||||
|
- Permisos: `estado_permiso`, `rol_permiso`, `decision_autorizacion`, `momento_checklist`, `tipo_evento_permiso`
|
||||||
|
- Indicadores: `tipo_indicador`, `frecuencia_calculo`, `periodo_tipo`, `estado_semaforo`, `fuente_horas`, `tipo_reporte_hse`, `formato_reporte`, `tipo_alerta_indicador`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. DISCREPANCIAS DETECTADAS
|
||||||
|
|
||||||
|
### 2.1 MASTER_INVENTORY.yml
|
||||||
|
|
||||||
|
| Campo | Valor Actual | Valor Correcto | Accion |
|
||||||
|
|-------|--------------|----------------|--------|
|
||||||
|
| `metricas.database.schemas` | 6 | 7 | Actualizar a 7 |
|
||||||
|
| `metricas.database.tablas` | 57 | 65 | Actualizar a 65 |
|
||||||
|
| `metricas.database.enums` | 22 | 89 | Actualizar a 89 |
|
||||||
|
| `schemas.hse.estado` | "pendiente" | "implementado" | Actualizar |
|
||||||
|
| `schemas.hse.tablas` | 0 | 58 | Actualizar a 58 |
|
||||||
|
| `schemas.hse.ddl` | "pendiente" | "03-hse-schema-ddl.sql" | Actualizar |
|
||||||
|
| `modulos_fase_3.MAA-017.tablas` | 11 items | 58 items | Actualizar lista completa |
|
||||||
|
|
||||||
|
### 2.2 DATABASE_INVENTORY.yml
|
||||||
|
|
||||||
|
| Campo | Valor Actual | Valor Correcto | Accion |
|
||||||
|
|-------|--------------|----------------|--------|
|
||||||
|
| `resumen.schemas` | 6 | 7 | Actualizar |
|
||||||
|
| `resumen.tablas` | 57 | 65 | Actualizar |
|
||||||
|
| `resumen.enums` | 22 | 89 | Actualizar |
|
||||||
|
| Schema `hse` | No existe | Agregar seccion completa | FALTA |
|
||||||
|
| Tablas HSE | 0 | 58 | Agregar todas |
|
||||||
|
|
||||||
|
### 2.3 Tablas Faltantes en Inventarios
|
||||||
|
|
||||||
|
Las siguientes 58 tablas HSE + 2 core + 2 construction + 3 hr = 65 tablas existen en DDL pero el inventario solo declara 57:
|
||||||
|
|
||||||
|
**FALTANTES:**
|
||||||
|
- Todas las 58 tablas del schema `hse`
|
||||||
|
- Las 2 tablas minimas de `core` (tenants, users)
|
||||||
|
- La tabla `hr.puestos`
|
||||||
|
- La tabla `hr.employee_fraccionamientos`
|
||||||
|
- Las tablas de `construction` tienen nombres distintos en inventario vs DDL:
|
||||||
|
- Inventario: `fraccionamientos` (sin proyecto_id directo)
|
||||||
|
- DDL: `proyectos` + `fraccionamientos` (con proyecto_id)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. TRAZABILIDAD RF -> DDL
|
||||||
|
|
||||||
|
### 3.1 Modulo MAA-017 Seguridad HSE
|
||||||
|
|
||||||
|
| RF | Nombre | Tablas DDL | Trazabilidad |
|
||||||
|
|----|--------|------------|--------------|
|
||||||
|
| RF-MAA017-001 | Gestion de Incidentes | 5 tablas | COMPLETA |
|
||||||
|
| RF-MAA017-002 | Control de Capacitaciones | 6 tablas | COMPLETA |
|
||||||
|
| RF-MAA017-003 | Inspecciones de Seguridad | 7 tablas | COMPLETA |
|
||||||
|
| RF-MAA017-004 | Control de EPP | 7 tablas | COMPLETA |
|
||||||
|
| RF-MAA017-005 | Cumplimiento STPS | 11 tablas | COMPLETA |
|
||||||
|
| RF-MAA017-006 | Gestion Ambiental | 9 tablas | COMPLETA |
|
||||||
|
| RF-MAA017-007 | Permisos de Trabajo | 8 tablas | COMPLETA |
|
||||||
|
| RF-MAA017-008 | Indicadores HSE | 7 tablas | COMPLETA |
|
||||||
|
|
||||||
|
**Total:** 8 RFs -> 58 tablas + 67 ENUMs
|
||||||
|
|
||||||
|
### 3.2 Otros Modulos (Inventariados pero NO implementados en DDL)
|
||||||
|
|
||||||
|
| Modulo | Tablas Inventario | Tablas DDL | Estado |
|
||||||
|
|--------|-------------------|------------|--------|
|
||||||
|
| MAI-002 | 8 | 2 (proyectos, fraccionamientos) | PARCIAL |
|
||||||
|
| MAI-003 | 3 | 0 | SIN DDL |
|
||||||
|
| MAI-004 | 9 | 0 | SIN DDL |
|
||||||
|
| MAI-005 | 5 | 0 | SIN DDL |
|
||||||
|
| MAI-007 | 8 | 3 (employees, puestos, employee_fracc) | PARCIAL |
|
||||||
|
| MAI-008 | 8 | 0 | SIN DDL |
|
||||||
|
| MAI-009 | 5 | 0 | SIN DDL |
|
||||||
|
| MAI-010 | 1 | 0 | SIN DDL |
|
||||||
|
| MAI-011 | 7 | 0 | SIN DDL |
|
||||||
|
| MAI-012 | 3 | 0 | SIN DDL |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. POLITICA DE CARGA LIMPIA
|
||||||
|
|
||||||
|
### 4.1 Cumplimiento
|
||||||
|
|
||||||
|
| Check | Estado | Detalle |
|
||||||
|
|-------|--------|---------|
|
||||||
|
| No carpeta migrations/ | OK | No existe |
|
||||||
|
| No archivos fix-*.sql | OK | No existen |
|
||||||
|
| No archivos migration-*.sql | OK | No existen |
|
||||||
|
| Existe drop-and-recreate-database.sh | OK | Existe y es ejecutable |
|
||||||
|
| DDL en schemas/ | OK | 3 archivos SQL |
|
||||||
|
| Archivo init existe | OK | init-scripts/01-init-database.sql |
|
||||||
|
|
||||||
|
**Resultado:** POLITICA CUMPLIDA (6/6 checks)
|
||||||
|
|
||||||
|
### 4.2 Archivos DDL Actuales
|
||||||
|
|
||||||
|
```
|
||||||
|
database/
|
||||||
|
├── init-scripts/
|
||||||
|
│ └── 01-init-database.sql # Extensiones, schemas base, funciones core
|
||||||
|
├── schemas/
|
||||||
|
│ ├── 01-construction-schema-ddl.sql # proyectos, fraccionamientos
|
||||||
|
│ ├── 02-hr-schema-ddl.sql # employees, puestos, employee_fracc
|
||||||
|
│ └── 03-hse-schema-ddl.sql # 58 tablas HSE
|
||||||
|
├── drop-and-recreate-database.sh # Script carga limpia
|
||||||
|
└── validate-clean-load-policy.sh # Validador de politica
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. ACCIONES REQUERIDAS
|
||||||
|
|
||||||
|
### 5.1 Prioridad ALTA (Inventarios Desactualizados)
|
||||||
|
|
||||||
|
1. **Actualizar MASTER_INVENTORY.yml:**
|
||||||
|
- Cambiar `schemas.hse.estado` de "pendiente" a "implementado"
|
||||||
|
- Cambiar `schemas.hse.tablas` de 0 a 58
|
||||||
|
- Agregar `schemas.hse.ddl: 03-hse-schema-ddl.sql`
|
||||||
|
- Actualizar conteos globales
|
||||||
|
|
||||||
|
2. **Actualizar DATABASE_INVENTORY.yml:**
|
||||||
|
- Agregar seccion completa para schema `hse` con 58 tablas
|
||||||
|
- Agregar 67 ENUMs de HSE
|
||||||
|
- Actualizar conteos en resumen
|
||||||
|
|
||||||
|
### 5.2 Prioridad MEDIA (Completar DDL Faltante)
|
||||||
|
|
||||||
|
Los siguientes schemas tienen tablas inventariadas pero NO implementadas:
|
||||||
|
|
||||||
|
| Schema | Tablas Faltantes | DDL Requerido |
|
||||||
|
|--------|------------------|---------------|
|
||||||
|
| construction | 22 tablas | construction-schema-ddl.sql (expandir) |
|
||||||
|
| estimates | 8 tablas | estimates-schema-ddl.sql (crear) |
|
||||||
|
| infonavit | 8 tablas | infonavit-schema-ddl.sql (crear) |
|
||||||
|
| hr | 5 tablas | hr-schema-ddl.sql (expandir) |
|
||||||
|
| inventory | 4 tablas | inventory-ext-schema-ddl.sql (crear) |
|
||||||
|
| purchase | 5 tablas | purchase-ext-schema-ddl.sql (crear) |
|
||||||
|
|
||||||
|
### 5.3 Prioridad BAJA (Documentacion)
|
||||||
|
|
||||||
|
- Crear TRACEABILITY.yml por modulo cuando se implemente
|
||||||
|
- Actualizar README de database/ con estructura actual
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. RESUMEN FINAL
|
||||||
|
|
||||||
|
### Estado Actual
|
||||||
|
|
||||||
|
```
|
||||||
|
DDL Implementado:
|
||||||
|
├── core: 2 tablas (tenants, users)
|
||||||
|
├── construction: 2 tablas
|
||||||
|
├── hr: 3 tablas
|
||||||
|
├── hse: 58 tablas + 67 ENUMs ← IMPLEMENTADO (inventario dice "pendiente")
|
||||||
|
├── estimates: schema vacio
|
||||||
|
├── infonavit: schema vacio
|
||||||
|
├── inventory: schema vacio
|
||||||
|
└── purchase: schema vacio
|
||||||
|
|
||||||
|
Total: 65 tablas, 89 ENUMs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inventarios Declaran
|
||||||
|
|
||||||
|
```
|
||||||
|
MASTER + DATABASE_INVENTORY:
|
||||||
|
├── construction: 24 tablas (22 sin DDL)
|
||||||
|
├── estimates: 8 tablas (sin DDL)
|
||||||
|
├── infonavit: 8 tablas (sin DDL)
|
||||||
|
├── hr: 8 tablas (5 sin DDL)
|
||||||
|
├── inventory: 4 tablas (sin DDL)
|
||||||
|
├── purchase: 5 tablas (sin DDL)
|
||||||
|
└── hse: "pendiente" (INCORRECTO - tiene 58 tablas)
|
||||||
|
|
||||||
|
Total declarado: 57 tablas, 22 ENUMs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Gap Analysis
|
||||||
|
|
||||||
|
| Categoria | Inventario | DDL Real | Diferencia |
|
||||||
|
|-----------|------------|----------|------------|
|
||||||
|
| Tablas | 57 | 65 | +8 (HSE +58, otros -50) |
|
||||||
|
| ENUMs | 22 | 89 | +67 (todos HSE) |
|
||||||
|
| Schemas implementados | 6 | 7 | +1 (hse) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Documento generado automaticamente como parte de la validacion de Sprint 0.**
|
||||||
306
_MAP.md
Normal file
306
_MAP.md
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
# Database Map - ERP Construccion
|
||||||
|
|
||||||
|
**Proyecto:** ERP Construccion
|
||||||
|
**Version:** 1.0
|
||||||
|
**Ultima Actualizacion:** 2025-12-12
|
||||||
|
**Total Schemas:** 7
|
||||||
|
**Total Tablas:** 110
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NAVEGACION RAPIDA
|
||||||
|
|
||||||
|
```
|
||||||
|
database/
|
||||||
|
├── _MAP.md # Este archivo (indice maestro)
|
||||||
|
├── schemas/ # DDL por schema
|
||||||
|
│ ├── 01-construction-schema-ddl.sql # 24 tablas
|
||||||
|
│ ├── 02-hr-schema-ddl.sql # 8 tablas
|
||||||
|
│ ├── 03-hse-schema-ddl.sql # 58 tablas
|
||||||
|
│ ├── 04-estimates-schema-ddl.sql # 8 tablas
|
||||||
|
│ ├── 05-infonavit-schema-ddl.sql # 8 tablas
|
||||||
|
│ ├── 06-inventory-ext-schema-ddl.sql # 4 tablas
|
||||||
|
│ └── 07-purchase-ext-schema-ddl.sql # 5 tablas
|
||||||
|
├── init-scripts/ # Scripts de inicializacion
|
||||||
|
│ └── 01-init-database.sql
|
||||||
|
├── migrations/ # Migraciones TypeORM
|
||||||
|
├── seeds/ # Datos de prueba
|
||||||
|
└── HERENCIA-ERP-CORE.md # Referencia a ERP-Core
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SCHEMAS OVERVIEW
|
||||||
|
|
||||||
|
| # | Schema | Tablas | Descripcion | Estado |
|
||||||
|
|---|--------|--------|-------------|--------|
|
||||||
|
| 1 | `construction` | 24 | Proyectos, estructura, avances | ✅ DDL |
|
||||||
|
| 2 | `hr` | 8 | RRHH, asistencias, cuadrillas | ✅ DDL |
|
||||||
|
| 3 | `hse` | 58 | Seguridad, incidentes, EPP | ✅ DDL |
|
||||||
|
| 4 | `estimates` | 8 | Estimaciones, anticipos | ✅ DDL |
|
||||||
|
| 5 | `infonavit` | 8 | INFONAVIT, derechohabientes | ✅ DDL |
|
||||||
|
| 6 | `inventory` | 4 | Extension inventario obra | ✅ DDL |
|
||||||
|
| 7 | `purchase` | 5 | Extension compras obra | ✅ DDL |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## DETALLE POR SCHEMA
|
||||||
|
|
||||||
|
### 1. Schema: `construction` (24 tablas)
|
||||||
|
|
||||||
|
**DDL:** `schemas/01-construction-schema-ddl.sql`
|
||||||
|
|
||||||
|
#### Estructura de Proyecto (8 tablas)
|
||||||
|
|
||||||
|
| Tabla | Descripcion | FK Principales |
|
||||||
|
|-------|-------------|----------------|
|
||||||
|
| `proyectos` | Proyectos/obras | `auth.tenants`, `auth.users` |
|
||||||
|
| `fraccionamientos` | Fraccionamientos | `proyectos` |
|
||||||
|
| `etapas` | Etapas de construccion | `fraccionamientos` |
|
||||||
|
| `manzanas` | Manzanas | `etapas` |
|
||||||
|
| `lotes` | Lotes/unidades | `manzanas`, `prototipos` |
|
||||||
|
| `torres` | Torres (vertical) | `fraccionamientos` |
|
||||||
|
| `niveles` | Niveles/pisos | `torres` |
|
||||||
|
| `departamentos` | Departamentos | `niveles`, `prototipos` |
|
||||||
|
| `prototipos` | Tipos de vivienda | `fraccionamientos` |
|
||||||
|
|
||||||
|
#### Presupuestos (3 tablas)
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `conceptos` | Catalogo de conceptos de obra |
|
||||||
|
| `presupuestos` | Presupuestos maestros |
|
||||||
|
| `presupuesto_partidas` | Partidas presupuestales |
|
||||||
|
|
||||||
|
#### Programacion y Avances (5 tablas)
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `programa_obra` | Programa general de obra |
|
||||||
|
| `programa_actividades` | Actividades programadas |
|
||||||
|
| `avances_obra` | Registro de avances |
|
||||||
|
| `fotos_avance` | Evidencias fotograficas |
|
||||||
|
| `bitacora_obra` | Bitacora de obra |
|
||||||
|
|
||||||
|
#### Calidad (5 tablas)
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `checklists` | Checklists de calidad |
|
||||||
|
| `checklist_items` | Items de checklist |
|
||||||
|
| `inspecciones` | Inspecciones de calidad |
|
||||||
|
| `inspeccion_resultados` | Resultados |
|
||||||
|
| `tickets_postventa` | Tickets de postventa |
|
||||||
|
|
||||||
|
#### Contratos (3 tablas)
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `subcontratistas` | Catalogo subcontratistas |
|
||||||
|
| `contratos` | Contratos de obra |
|
||||||
|
| `contrato_partidas` | Partidas contratadas |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. Schema: `hr` (8 tablas)
|
||||||
|
|
||||||
|
**DDL:** `schemas/02-hr-schema-ddl.sql`
|
||||||
|
|
||||||
|
| Tabla | Descripcion | Caracteristicas |
|
||||||
|
|-------|-------------|-----------------|
|
||||||
|
| `employees` | Empleados base | Extension de core |
|
||||||
|
| `employee_construction` | Extension construccion | Campos especificos |
|
||||||
|
| `puestos` | Catalogo de puestos | - |
|
||||||
|
| `asistencias` | Registro asistencias | GPS, biometrico |
|
||||||
|
| `asistencia_biometrico` | Datos biometricos | - |
|
||||||
|
| `geocercas` | Geocercas para GPS | PostGIS |
|
||||||
|
| `destajo` | Trabajo a destajo | - |
|
||||||
|
| `destajo_detalle` | Mediciones destajo | - |
|
||||||
|
| `cuadrillas` | Cuadrillas de trabajo | - |
|
||||||
|
| `cuadrilla_miembros` | Miembros de cuadrilla | - |
|
||||||
|
| `employee_fraccionamientos` | Asignacion a fracc | - |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. Schema: `hse` (58 tablas)
|
||||||
|
|
||||||
|
**DDL:** `schemas/03-hse-schema-ddl.sql`
|
||||||
|
|
||||||
|
#### Gestion de Incidentes (5 tablas)
|
||||||
|
- `incidentes`, `incidente_involucrados`, `incidente_acciones`
|
||||||
|
- `incidente_evidencias`, `incidente_causas`
|
||||||
|
|
||||||
|
#### Control de Capacitaciones (6 tablas)
|
||||||
|
- `capacitaciones`, `capacitacion_participantes`, `capacitacion_materiales`
|
||||||
|
- `certificaciones`, `certificacion_empleados`, `plan_capacitacion`
|
||||||
|
|
||||||
|
#### Inspecciones de Seguridad (7 tablas)
|
||||||
|
- `inspecciones_seguridad`, `inspeccion_hallazgos`
|
||||||
|
- `checklist_seguridad`, `checklist_seguridad_items`
|
||||||
|
- `areas_riesgo`, `rondas_seguridad`, `ronda_puntos`
|
||||||
|
|
||||||
|
#### Control de EPP (7 tablas)
|
||||||
|
- `epp_catalogo`, `epp_asignaciones`, `epp_entregas`
|
||||||
|
- `epp_devoluciones`, `epp_inspecciones`
|
||||||
|
- `epp_vida_util`, `epp_stock`
|
||||||
|
|
||||||
|
#### Cumplimiento STPS (11 tablas)
|
||||||
|
- `normas_stps`, `requisitos_norma`, `cumplimiento_norma`
|
||||||
|
- `auditorias_stps`, `auditoria_hallazgos`
|
||||||
|
- `planes_accion`, `acciones_correctivas`
|
||||||
|
- `comision_seguridad`, `comision_miembros`
|
||||||
|
- `recorridos_comision`, `actas_comision`
|
||||||
|
|
||||||
|
#### Gestion Ambiental (9 tablas)
|
||||||
|
- `impactos_ambientales`, `residuos`, `residuo_movimientos`
|
||||||
|
- `manifiestos_residuos`, `monitoreo_ambiental`
|
||||||
|
- `permisos_ambientales`, `programas_ambientales`
|
||||||
|
- `indicadores_ambientales`, `eventos_ambientales`
|
||||||
|
|
||||||
|
#### Permisos de Trabajo (8 tablas)
|
||||||
|
- `permisos_trabajo`, `permiso_riesgos`, `permiso_autorizaciones`
|
||||||
|
- `permisos_altura`, `permisos_caliente`, `permisos_confinado`
|
||||||
|
- `permisos_electrico`, `permisos_excavacion`
|
||||||
|
|
||||||
|
#### Indicadores HSE (7 tablas)
|
||||||
|
- `kpi_configuracion`, `kpi_valores`, `kpi_metas`
|
||||||
|
- `dashboards_hse`, `alertas_hse`
|
||||||
|
- `reportes_hse`, `estadisticas_periodo`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. Schema: `estimates` (8 tablas)
|
||||||
|
|
||||||
|
**DDL:** `schemas/04-estimates-schema-ddl.sql`
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `estimaciones` | Estimaciones de obra |
|
||||||
|
| `estimacion_conceptos` | Conceptos estimados |
|
||||||
|
| `generadores` | Numeros generadores |
|
||||||
|
| `anticipos` | Anticipos de obra |
|
||||||
|
| `amortizaciones` | Amortizacion de anticipos |
|
||||||
|
| `retenciones` | Retenciones (garantia, IMSS) |
|
||||||
|
| `fondo_garantia` | Fondo de garantia |
|
||||||
|
| `estimacion_workflow` | Workflow de aprobacion |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. Schema: `infonavit` (8 tablas)
|
||||||
|
|
||||||
|
**DDL:** `schemas/05-infonavit-schema-ddl.sql`
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `registro_infonavit` | Registro RUV |
|
||||||
|
| `oferta_vivienda` | Oferta registrada |
|
||||||
|
| `derechohabientes` | Derechohabientes |
|
||||||
|
| `asignacion_vivienda` | Asignaciones |
|
||||||
|
| `actas` | Actas de entrega |
|
||||||
|
| `acta_viviendas` | Viviendas en acta |
|
||||||
|
| `reportes_infonavit` | Reportes RUV |
|
||||||
|
| `historico_puntos` | Historico puntos ecologicos |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. Schema: `inventory` Extension (4 tablas)
|
||||||
|
|
||||||
|
**DDL:** `schemas/06-inventory-ext-schema-ddl.sql`
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `almacenes_proyecto` | Almacenes por obra |
|
||||||
|
| `requisiciones_obra` | Requisiciones desde obra |
|
||||||
|
| `requisicion_lineas` | Lineas de requisicion |
|
||||||
|
| `consumos_obra` | Consumos por lote/concepto |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. Schema: `purchase` Extension (5 tablas)
|
||||||
|
|
||||||
|
**DDL:** `schemas/07-purchase-ext-schema-ddl.sql`
|
||||||
|
|
||||||
|
| Tabla | Descripcion |
|
||||||
|
|-------|-------------|
|
||||||
|
| `purchase_order_construction` | Extension OC |
|
||||||
|
| `supplier_construction` | Extension proveedores |
|
||||||
|
| `comparativo_cotizaciones` | Cuadro comparativo |
|
||||||
|
| `comparativo_proveedores` | Proveedores en comparativo |
|
||||||
|
| `comparativo_productos` | Productos cotizados |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ORDEN DE EJECUCION DDL
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Prerequisito: ERP-Core debe estar instalado
|
||||||
|
# Schema auth.* y core.* deben existir
|
||||||
|
|
||||||
|
# 1. Construction (base)
|
||||||
|
psql $DATABASE_URL -f schemas/01-construction-schema-ddl.sql
|
||||||
|
|
||||||
|
# 2. HR (depende de construction)
|
||||||
|
psql $DATABASE_URL -f schemas/02-hr-schema-ddl.sql
|
||||||
|
|
||||||
|
# 3. HSE (depende de construction y hr)
|
||||||
|
psql $DATABASE_URL -f schemas/03-hse-schema-ddl.sql
|
||||||
|
|
||||||
|
# 4. Estimates (depende de construction)
|
||||||
|
psql $DATABASE_URL -f schemas/04-estimates-schema-ddl.sql
|
||||||
|
|
||||||
|
# 5. INFONAVIT (depende de construction)
|
||||||
|
psql $DATABASE_URL -f schemas/05-infonavit-schema-ddl.sql
|
||||||
|
|
||||||
|
# 6. Inventory Extension (depende de construction)
|
||||||
|
psql $DATABASE_URL -f schemas/06-inventory-ext-schema-ddl.sql
|
||||||
|
|
||||||
|
# 7. Purchase Extension (depende de construction)
|
||||||
|
psql $DATABASE_URL -f schemas/07-purchase-ext-schema-ddl.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## RELACIONES PRINCIPALES
|
||||||
|
|
||||||
|
```
|
||||||
|
auth.tenants
|
||||||
|
└── construction.proyectos
|
||||||
|
└── construction.fraccionamientos
|
||||||
|
├── construction.etapas
|
||||||
|
│ └── construction.manzanas
|
||||||
|
│ └── construction.lotes
|
||||||
|
├── construction.torres (vertical)
|
||||||
|
│ └── construction.niveles
|
||||||
|
│ └── construction.departamentos
|
||||||
|
├── hr.employee_fraccionamientos
|
||||||
|
│ └── hr.employees
|
||||||
|
└── hse.incidentes
|
||||||
|
└── hse.incidente_involucrados
|
||||||
|
└── hr.employees
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ENUMS UTILIZADOS
|
||||||
|
|
||||||
|
Ver archivo: `backend/src/shared/constants/enums.constants.ts`
|
||||||
|
|
||||||
|
Los principales enums estan definidos en:
|
||||||
|
- `PROJECT_STATUS` - Estados de proyecto
|
||||||
|
- `LOT_STATUS` - Estados de lote
|
||||||
|
- `INCIDENT_SEVERITY` - Severidad de incidentes
|
||||||
|
- `ESTIMATION_STATUS` - Estados de estimacion
|
||||||
|
- `INFONAVIT_ASSIGNMENT_STATUS` - Estados INFONAVIT
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## REFERENCIAS
|
||||||
|
|
||||||
|
- **ERP-Core DDL:** `apps/erp-core/database/ddl/`
|
||||||
|
- **Herencia:** `HERENCIA-ERP-CORE.md`
|
||||||
|
- **Constantes SSOT:** `backend/src/shared/constants/database.constants.ts`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Mantenido por:** Architecture-Analyst
|
||||||
|
**Actualizacion:** Manual al agregar/modificar schemas
|
||||||
188
drop-and-recreate-database.sh
Executable file
188
drop-and-recreate-database.sh
Executable file
@ -0,0 +1,188 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =============================================================================
|
||||||
|
# DROP AND RECREATE DATABASE - ERP CONSTRUCCION
|
||||||
|
# =============================================================================
|
||||||
|
# Script de carga limpia segun DIRECTIVA-POLITICA-CARGA-LIMPIA.md
|
||||||
|
#
|
||||||
|
# Uso: ./drop-and-recreate-database.sh [DATABASE_URL]
|
||||||
|
# Ejemplo: ./drop-and-recreate-database.sh "postgresql://user:pass@localhost:5433/erp_construccion"
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colores
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Configuracion
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DDL_DIR="$SCRIPT_DIR/ddl"
|
||||||
|
SCHEMAS_DIR="$SCRIPT_DIR/schemas"
|
||||||
|
INIT_SCRIPTS_DIR="$SCRIPT_DIR/init-scripts"
|
||||||
|
|
||||||
|
# Database URL (por defecto desarrollo local)
|
||||||
|
DATABASE_URL="${1:-${DATABASE_URL:-postgresql://postgres:postgres@localhost:5433/erp_construccion}}"
|
||||||
|
|
||||||
|
# Extraer parametros de conexion
|
||||||
|
DB_HOST=$(echo "$DATABASE_URL" | sed -E 's|.*@([^:]+):.*|\1|')
|
||||||
|
DB_PORT=$(echo "$DATABASE_URL" | sed -E 's|.*:([0-9]+)/.*|\1|')
|
||||||
|
DB_NAME=$(echo "$DATABASE_URL" | sed -E 's|.*/([^?]+).*|\1|')
|
||||||
|
DB_USER=$(echo "$DATABASE_URL" | sed -E 's|.*://([^:]+):.*|\1|')
|
||||||
|
|
||||||
|
echo -e "${BLUE}=============================================================================${NC}"
|
||||||
|
echo -e "${BLUE} ERP CONSTRUCCION - Carga Limpia de Base de Datos${NC}"
|
||||||
|
echo -e "${BLUE}=============================================================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "Host: ${YELLOW}$DB_HOST:$DB_PORT${NC}"
|
||||||
|
echo -e "Database: ${YELLOW}$DB_NAME${NC}"
|
||||||
|
echo -e "Usuario: ${YELLOW}$DB_USER${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PASO 1: Verificar conexion
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[1/6] Verificando conexion a PostgreSQL...${NC}"
|
||||||
|
if ! psql "$DATABASE_URL" -c "SELECT 1" > /dev/null 2>&1; then
|
||||||
|
echo -e "${RED}ERROR: No se puede conectar a PostgreSQL${NC}"
|
||||||
|
echo -e "${YELLOW}Verificar que PostgreSQL esta corriendo y las credenciales son correctas${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo -e "${GREEN}OK - Conexion establecida${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PASO 2: DROP schemas existentes (carga limpia)
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[2/6] Eliminando schemas existentes (carga limpia)...${NC}"
|
||||||
|
|
||||||
|
# Lista de schemas a eliminar (orden inverso de dependencias)
|
||||||
|
SCHEMAS_TO_DROP=(
|
||||||
|
"hse"
|
||||||
|
"infonavit_management"
|
||||||
|
"safety_management"
|
||||||
|
"quality_management"
|
||||||
|
"construction_management"
|
||||||
|
"inventory_management"
|
||||||
|
"purchasing_management"
|
||||||
|
"financial_management"
|
||||||
|
"project_management"
|
||||||
|
"auth_management"
|
||||||
|
"core_shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
for schema in "${SCHEMAS_TO_DROP[@]}"; do
|
||||||
|
if psql "$DATABASE_URL" -t -c "SELECT 1 FROM pg_namespace WHERE nspname = '$schema'" 2>/dev/null | grep -q 1; then
|
||||||
|
psql "$DATABASE_URL" -c "DROP SCHEMA IF EXISTS $schema CASCADE" > /dev/null 2>&1
|
||||||
|
echo -e " - Schema ${YELLOW}$schema${NC} eliminado"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo -e "${GREEN}OK - Schemas eliminados${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PASO 3: Ejecutar DDL inicial
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[3/6] Ejecutando DDL inicial (extensiones, schemas base)...${NC}"
|
||||||
|
|
||||||
|
if [ -f "$INIT_SCRIPTS_DIR/01-init-database.sql" ]; then
|
||||||
|
psql "$DATABASE_URL" -f "$INIT_SCRIPTS_DIR/01-init-database.sql" > /dev/null 2>&1
|
||||||
|
echo -e "${GREEN}OK - DDL inicial ejecutado${NC}"
|
||||||
|
elif [ -f "$DDL_DIR/00-init.sql" ]; then
|
||||||
|
psql "$DATABASE_URL" -f "$DDL_DIR/00-init.sql" > /dev/null 2>&1
|
||||||
|
echo -e "${GREEN}OK - DDL inicial ejecutado (00-init.sql)${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}ERROR: No se encontro archivo de inicializacion${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PASO 4: Ejecutar DDL de schemas modulares
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[4/6] Ejecutando DDL de schemas modulares...${NC}"
|
||||||
|
|
||||||
|
# Buscar y ejecutar todos los archivos DDL en orden
|
||||||
|
DDL_FILES=$(find "$SCHEMAS_DIR" -name "*.sql" -type f 2>/dev/null | sort)
|
||||||
|
|
||||||
|
if [ -z "$DDL_FILES" ]; then
|
||||||
|
echo -e "${YELLOW} No hay archivos DDL modulares adicionales${NC}"
|
||||||
|
else
|
||||||
|
for ddl_file in $DDL_FILES; do
|
||||||
|
filename=$(basename "$ddl_file")
|
||||||
|
echo -ne " - Ejecutando ${YELLOW}$filename${NC}..."
|
||||||
|
if psql "$DATABASE_URL" -f "$ddl_file" > /dev/null 2>&1; then
|
||||||
|
echo -e " ${GREEN}OK${NC}"
|
||||||
|
else
|
||||||
|
echo -e " ${RED}ERROR${NC}"
|
||||||
|
echo -e "${RED}Fallo al ejecutar: $ddl_file${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PASO 5: Ejecutar DDL legacy (si existe)
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[5/6] Ejecutando DDL de schemas legacy (si existen)...${NC}"
|
||||||
|
|
||||||
|
LEGACY_DDL_DIR="$DDL_DIR/schemas"
|
||||||
|
if [ -d "$LEGACY_DDL_DIR" ]; then
|
||||||
|
LEGACY_FILES=$(find "$LEGACY_DDL_DIR" -name "*.sql" -type f 2>/dev/null | sort)
|
||||||
|
for ddl_file in $LEGACY_FILES; do
|
||||||
|
filename=$(basename "$ddl_file")
|
||||||
|
dirname=$(dirname "$ddl_file" | xargs basename)
|
||||||
|
echo -ne " - ${YELLOW}$dirname/$filename${NC}..."
|
||||||
|
if psql "$DATABASE_URL" -f "$ddl_file" > /dev/null 2>&1; then
|
||||||
|
echo -e " ${GREEN}OK${NC}"
|
||||||
|
else
|
||||||
|
echo -e " ${YELLOW}SKIP (puede requerir dependencias)${NC}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo -e " No hay DDL legacy"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PASO 6: Verificar resultado
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[6/6] Verificando resultado...${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Contar schemas creados
|
||||||
|
SCHEMA_COUNT=$(psql "$DATABASE_URL" -t -c "
|
||||||
|
SELECT COUNT(*) FROM pg_namespace
|
||||||
|
WHERE nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast', 'pg_temp_1', 'pg_toast_temp_1', 'public')
|
||||||
|
AND nspname NOT LIKE 'pg_%'
|
||||||
|
" | tr -d ' ')
|
||||||
|
|
||||||
|
# Contar tablas totales
|
||||||
|
TABLE_COUNT=$(psql "$DATABASE_URL" -t -c "
|
||||||
|
SELECT COUNT(*) FROM pg_tables
|
||||||
|
WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'public')
|
||||||
|
" | tr -d ' ')
|
||||||
|
|
||||||
|
# Mostrar resumen por schema
|
||||||
|
echo -e "${GREEN}=== RESUMEN DE CARGA LIMPIA ===${NC}"
|
||||||
|
echo ""
|
||||||
|
psql "$DATABASE_URL" -c "
|
||||||
|
SELECT
|
||||||
|
schemaname AS \"Schema\",
|
||||||
|
COUNT(*) AS \"Tablas\"
|
||||||
|
FROM pg_tables
|
||||||
|
WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'public')
|
||||||
|
GROUP BY schemaname
|
||||||
|
ORDER BY schemaname;
|
||||||
|
"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}=============================================================================${NC}"
|
||||||
|
echo -e "${GREEN} CARGA LIMPIA COMPLETADA EXITOSAMENTE${NC}"
|
||||||
|
echo -e "${GREEN}=============================================================================${NC}"
|
||||||
|
echo -e " Schemas creados: ${YELLOW}$SCHEMA_COUNT${NC}"
|
||||||
|
echo -e " Tablas creadas: ${YELLOW}$TABLE_COUNT${NC}"
|
||||||
|
echo -e "${GREEN}=============================================================================${NC}"
|
||||||
317
init-scripts/01-init-database.sql
Normal file
317
init-scripts/01-init-database.sql
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- Archivo: 01-init-database.sql
|
||||||
|
-- Descripcion: Inicializacion completa de base de datos - Carga Limpia
|
||||||
|
-- Proyecto: ERP Suite - Vertical Construccion
|
||||||
|
-- Version: 2.0.0
|
||||||
|
-- Fecha: 2025-12-06
|
||||||
|
-- ============================================================================
|
||||||
|
-- POLITICA: CARGA LIMPIA (ver DIRECTIVA-POLITICA-CARGA-LIMPIA.md)
|
||||||
|
-- Este archivo es parte de la fuente de verdad DDL.
|
||||||
|
-- NO usar migrations, fixes o patches incrementales.
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- EXTENSIONES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- Generacion de UUIDs
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- Funciones criptograficas
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "postgis"; -- Geolocalizacion
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- Busqueda fuzzy
|
||||||
|
CREATE EXTENSION IF NOT EXISTS "btree_gist"; -- Indices GiST avanzados
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- SCHEMA: core_shared (funciones compartidas)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE SCHEMA IF NOT EXISTS core_shared;
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA core_shared IS 'Funciones, tipos y utilidades compartidas entre modulos';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FUNCIONES DE AUDITORIA
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Funcion para actualizar updated_at automaticamente
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.set_updated_at()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = NOW();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION core_shared.set_updated_at() IS
|
||||||
|
'Trigger function para actualizar automaticamente el campo updated_at en cada UPDATE';
|
||||||
|
|
||||||
|
-- Funcion para establecer tenant_id desde contexto
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.set_tenant_id()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
IF NEW.tenant_id IS NULL THEN
|
||||||
|
NEW.tenant_id = current_setting('app.current_tenant_id', true)::uuid;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION core_shared.set_tenant_id() IS
|
||||||
|
'Trigger function para establecer tenant_id automaticamente desde el contexto de sesion';
|
||||||
|
|
||||||
|
-- Funcion para establecer created_by desde contexto
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.set_created_by()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
IF NEW.created_by IS NULL THEN
|
||||||
|
NEW.created_by = current_setting('app.current_user_id', true)::uuid;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION core_shared.set_created_by() IS
|
||||||
|
'Trigger function para establecer created_by automaticamente desde el contexto de sesion';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FUNCIONES DE CONTEXTO
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.get_current_tenant_id()
|
||||||
|
RETURNS UUID AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN NULLIF(current_setting('app.current_tenant_id', true), '')::UUID;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql STABLE;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION core_shared.get_current_tenant_id() IS
|
||||||
|
'Obtiene el ID del tenant actual desde el contexto de sesion';
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.get_current_user_id()
|
||||||
|
RETURNS UUID AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN NULLIF(current_setting('app.current_user_id', true), '')::UUID;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN OTHERS THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql STABLE;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION core_shared.get_current_user_id() IS
|
||||||
|
'Obtiene el ID del usuario actual desde el contexto de sesion';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FUNCIONES DE UTILIDAD
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Generar slug desde texto
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.generate_slug(input_text TEXT)
|
||||||
|
RETURNS TEXT AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN LOWER(
|
||||||
|
REGEXP_REPLACE(
|
||||||
|
REGEXP_REPLACE(
|
||||||
|
TRIM(input_text),
|
||||||
|
'[^a-zA-Z0-9\s-]', '', 'g'
|
||||||
|
),
|
||||||
|
'\s+', '-', 'g'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
||||||
|
|
||||||
|
-- Validar formato de email
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.is_valid_email(email TEXT)
|
||||||
|
RETURNS BOOLEAN AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$';
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
||||||
|
|
||||||
|
-- Validar formato de RFC mexicano
|
||||||
|
CREATE OR REPLACE FUNCTION core_shared.is_valid_rfc(rfc TEXT)
|
||||||
|
RETURNS BOOLEAN AS $$
|
||||||
|
BEGIN
|
||||||
|
RETURN rfc ~* '^[A-Z&Ñ]{3,4}[0-9]{6}[A-Z0-9]{3}$';
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql IMMUTABLE;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- PERMISOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
GRANT USAGE ON SCHEMA core_shared TO PUBLIC;
|
||||||
|
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA core_shared TO PUBLIC;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- SCHEMAS DE NEGOCIO - NOMENCLATURA UNIFICADA
|
||||||
|
-- ============================================================================
|
||||||
|
-- Segun NAMING-CONVENTIONS.md, usamos nombres cortos y descriptivos:
|
||||||
|
-- - construction (antes project_management, construction_management)
|
||||||
|
-- - estimates (antes financial_management)
|
||||||
|
-- - infonavit (antes infonavit_management)
|
||||||
|
-- - hr (extension de erp-core)
|
||||||
|
-- - inventory (extension de erp-core)
|
||||||
|
-- - purchase (extension de erp-core)
|
||||||
|
-- - hse (nuevo: seguridad, salud, medio ambiente)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Schemas propios de construccion
|
||||||
|
CREATE SCHEMA IF NOT EXISTS construction;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS estimates;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS infonavit;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS hse;
|
||||||
|
|
||||||
|
-- Schemas de extension (extendemos modulos de erp-core)
|
||||||
|
-- Estos pueden ya existir si erp-core esta instalado
|
||||||
|
CREATE SCHEMA IF NOT EXISTS hr;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS inventory;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS purchase;
|
||||||
|
|
||||||
|
-- Schemas legacy (compatibilidad temporal, marcar para deprecacion)
|
||||||
|
-- NOTA: Estos seran eliminados en version futura
|
||||||
|
CREATE SCHEMA IF NOT EXISTS auth_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS project_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS financial_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS purchasing_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS inventory_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS construction_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS quality_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS safety_management;
|
||||||
|
CREATE SCHEMA IF NOT EXISTS infonavit_management;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS EN SCHEMAS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Schemas principales (nuevos)
|
||||||
|
COMMENT ON SCHEMA construction IS
|
||||||
|
'Gestion de obras: proyectos, fraccionamientos, fases, viviendas, avances';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA estimates IS
|
||||||
|
'Presupuestos, partidas, estimaciones, control de costos';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA infonavit IS
|
||||||
|
'Integracion INFONAVIT: tramites, ECUVE, subsidios, normatividad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA hse IS
|
||||||
|
'Seguridad, Salud Ocupacional y Medio Ambiente (HSE/EHS)';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA hr IS
|
||||||
|
'Extension de RRHH para construccion: cuadrillas, destajo, asistencia obra';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA inventory IS
|
||||||
|
'Extension de inventarios: almacenes de obra, control de materiales';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA purchase IS
|
||||||
|
'Extension de compras: proveedores de construccion, requisiciones de obra';
|
||||||
|
|
||||||
|
-- Schemas legacy (deprecated)
|
||||||
|
COMMENT ON SCHEMA auth_management IS
|
||||||
|
'DEPRECATED: Usar core.auth. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA project_management IS
|
||||||
|
'DEPRECATED: Usar construction. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA financial_management IS
|
||||||
|
'DEPRECATED: Usar estimates. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA purchasing_management IS
|
||||||
|
'DEPRECATED: Usar purchase. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA inventory_management IS
|
||||||
|
'DEPRECATED: Usar inventory. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA construction_management IS
|
||||||
|
'DEPRECATED: Usar construction. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA quality_management IS
|
||||||
|
'DEPRECATED: Funcionalidad movida a hse (inspecciones) y construction (calidad)';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA safety_management IS
|
||||||
|
'DEPRECATED: Usar hse. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA infonavit_management IS
|
||||||
|
'DEPRECATED: Usar infonavit. Schema mantenido para compatibilidad';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FUNCION LEGACY (compatibilidad)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION update_updated_at_column()
|
||||||
|
RETURNS TRIGGER AS $$
|
||||||
|
BEGIN
|
||||||
|
NEW.updated_at = now();
|
||||||
|
RETURN NEW;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
COMMENT ON FUNCTION update_updated_at_column() IS
|
||||||
|
'LEGACY: Usar core_shared.set_updated_at() en nuevas tablas';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLAS CORE MINIMAS (si erp-core no esta instalado)
|
||||||
|
-- ============================================================================
|
||||||
|
-- Estas tablas son requeridas como FK por los modulos de construccion
|
||||||
|
-- Si erp-core esta instalado, estas ya existiran y el IF NOT EXISTS las omitira
|
||||||
|
|
||||||
|
-- Schema core para tablas compartidas
|
||||||
|
CREATE SCHEMA IF NOT EXISTS core;
|
||||||
|
|
||||||
|
-- Tabla de tenants (multi-tenancy)
|
||||||
|
CREATE TABLE IF NOT EXISTS core.tenants (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
code VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
name VARCHAR(200) NOT NULL,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
settings JSONB DEFAULT '{}',
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla de usuarios
|
||||||
|
CREATE TABLE IF NOT EXISTS core.users (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID REFERENCES core.tenants(id),
|
||||||
|
email VARCHAR(255) NOT NULL,
|
||||||
|
username VARCHAR(100),
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
UNIQUE(tenant_id, email)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- VERIFICACION
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
schema_count INTEGER;
|
||||||
|
ext_count INTEGER;
|
||||||
|
BEGIN
|
||||||
|
SELECT COUNT(*) INTO schema_count
|
||||||
|
FROM pg_namespace
|
||||||
|
WHERE nspname IN ('construction', 'estimates', 'infonavit', 'hse', 'hr', 'inventory', 'purchase', 'core', 'core_shared');
|
||||||
|
|
||||||
|
SELECT COUNT(*) INTO ext_count
|
||||||
|
FROM pg_extension
|
||||||
|
WHERE extname IN ('uuid-ossp', 'pgcrypto', 'postgis', 'pg_trgm', 'btree_gist');
|
||||||
|
|
||||||
|
RAISE NOTICE '============================================================';
|
||||||
|
RAISE NOTICE 'ERP CONSTRUCCION - Base de datos inicializada';
|
||||||
|
RAISE NOTICE '============================================================';
|
||||||
|
RAISE NOTICE 'Extensiones instaladas: %', ext_count;
|
||||||
|
RAISE NOTICE 'Schemas principales creados: %', schema_count;
|
||||||
|
RAISE NOTICE '============================================================';
|
||||||
|
RAISE NOTICE 'Schemas principales: construction, estimates, infonavit, hse';
|
||||||
|
RAISE NOTICE 'Schemas extension: hr, inventory, purchase';
|
||||||
|
RAISE NOTICE 'Schemas compartidos: core, core_shared';
|
||||||
|
RAISE NOTICE '============================================================';
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN
|
||||||
|
-- ============================================================================
|
||||||
903
schemas/01-construction-schema-ddl.sql
Normal file
903
schemas/01-construction-schema-ddl.sql
Normal file
@ -0,0 +1,903 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- CONSTRUCTION Schema DDL - Gestión de Obras (COMPLETO)
|
||||||
|
-- Modulos: MAI-002, MAI-003, MAI-005, MAI-009, MAI-012
|
||||||
|
-- Version: 2.0.0
|
||||||
|
-- Fecha: 2025-12-08
|
||||||
|
-- ============================================================================
|
||||||
|
-- POLITICA: CARGA LIMPIA (ver DIRECTIVA-POLITICA-CARGA-LIMPIA.md)
|
||||||
|
-- Este archivo es parte de la fuente de verdad DDL.
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Verificar que ERP-Core está instalado
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
||||||
|
RAISE EXCEPTION 'Schema auth no existe. Ejecutar primero ERP-Core DDL';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'auth' AND tablename = 'tenants') THEN
|
||||||
|
RAISE EXCEPTION 'Tabla auth.tenants no existe. ERP-Core debe estar instalado';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'auth' AND tablename = 'users') THEN
|
||||||
|
RAISE EXCEPTION 'Tabla auth.users no existe. ERP-Core debe estar instalado';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Crear schema si no existe
|
||||||
|
CREATE SCHEMA IF NOT EXISTS construction;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TYPES (ENUMs)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE construction.project_status AS ENUM (
|
||||||
|
'draft', 'planning', 'in_progress', 'paused', 'completed', 'cancelled'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE construction.lot_status AS ENUM (
|
||||||
|
'available', 'reserved', 'sold', 'under_construction', 'delivered', 'warranty'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE construction.prototype_type AS ENUM (
|
||||||
|
'horizontal', 'vertical', 'commercial', 'mixed'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE construction.advance_status AS ENUM (
|
||||||
|
'pending', 'captured', 'reviewed', 'approved', 'rejected'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE construction.quality_status AS ENUM (
|
||||||
|
'pending', 'in_review', 'approved', 'rejected', 'rework'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE construction.contract_type AS ENUM (
|
||||||
|
'fixed_price', 'unit_price', 'cost_plus', 'mixed'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE construction.contract_status AS ENUM (
|
||||||
|
'draft', 'pending_approval', 'active', 'suspended', 'terminated', 'closed'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - ESTRUCTURA DE PROYECTO
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: fraccionamientos (desarrollo inmobiliario)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.fraccionamientos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
address TEXT,
|
||||||
|
city VARCHAR(100),
|
||||||
|
state VARCHAR(100),
|
||||||
|
zip_code VARCHAR(10),
|
||||||
|
location GEOMETRY(POINT, 4326),
|
||||||
|
total_area_m2 DECIMAL(12,2),
|
||||||
|
buildable_area_m2 DECIMAL(12,2),
|
||||||
|
total_lots INTEGER DEFAULT 0,
|
||||||
|
status construction.project_status NOT NULL DEFAULT 'draft',
|
||||||
|
start_date DATE,
|
||||||
|
expected_end_date DATE,
|
||||||
|
actual_end_date DATE,
|
||||||
|
metadata JSONB DEFAULT '{}',
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_fraccionamientos_code_tenant UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: etapas (fases del fraccionamiento)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.etapas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id) ON DELETE CASCADE,
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
sequence INTEGER NOT NULL DEFAULT 1,
|
||||||
|
total_lots INTEGER DEFAULT 0,
|
||||||
|
status construction.project_status NOT NULL DEFAULT 'draft',
|
||||||
|
start_date DATE,
|
||||||
|
expected_end_date DATE,
|
||||||
|
actual_end_date DATE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_etapas_code_fracc UNIQUE (fraccionamiento_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: manzanas (agrupación de lotes)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.manzanas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
etapa_id UUID NOT NULL REFERENCES construction.etapas(id) ON DELETE CASCADE,
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(100),
|
||||||
|
total_lots INTEGER DEFAULT 0,
|
||||||
|
polygon GEOMETRY(POLYGON, 4326),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_manzanas_code_etapa UNIQUE (etapa_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: prototipos (tipos de vivienda) - definida antes de lotes
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.prototipos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
type construction.prototype_type NOT NULL DEFAULT 'horizontal',
|
||||||
|
area_construction_m2 DECIMAL(10,2),
|
||||||
|
area_terrain_m2 DECIMAL(10,2),
|
||||||
|
bedrooms INTEGER DEFAULT 0,
|
||||||
|
bathrooms DECIMAL(3,1) DEFAULT 0,
|
||||||
|
parking_spaces INTEGER DEFAULT 0,
|
||||||
|
floors INTEGER DEFAULT 1,
|
||||||
|
base_price DECIMAL(14,2),
|
||||||
|
blueprint_url VARCHAR(500),
|
||||||
|
render_url VARCHAR(500),
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
metadata JSONB DEFAULT '{}',
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_prototipos_code_tenant UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: lotes (unidades vendibles horizontal)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.lotes (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
manzana_id UUID NOT NULL REFERENCES construction.manzanas(id) ON DELETE CASCADE,
|
||||||
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
official_number VARCHAR(50),
|
||||||
|
area_m2 DECIMAL(10,2),
|
||||||
|
front_m DECIMAL(8,2),
|
||||||
|
depth_m DECIMAL(8,2),
|
||||||
|
status construction.lot_status NOT NULL DEFAULT 'available',
|
||||||
|
location GEOMETRY(POINT, 4326),
|
||||||
|
polygon GEOMETRY(POLYGON, 4326),
|
||||||
|
price_base DECIMAL(14,2),
|
||||||
|
price_final DECIMAL(14,2),
|
||||||
|
buyer_id UUID,
|
||||||
|
sale_date DATE,
|
||||||
|
delivery_date DATE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_lotes_code_manzana UNIQUE (manzana_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - ESTRUCTURA VERTICAL (TORRES)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: torres (edificios verticales)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.torres (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
etapa_id UUID NOT NULL REFERENCES construction.etapas(id) ON DELETE CASCADE,
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(100) NOT NULL,
|
||||||
|
total_floors INTEGER NOT NULL DEFAULT 1,
|
||||||
|
total_units INTEGER DEFAULT 0,
|
||||||
|
status construction.project_status NOT NULL DEFAULT 'draft',
|
||||||
|
location GEOMETRY(POINT, 4326),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_torres_code_etapa UNIQUE (etapa_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: niveles (pisos de torre)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.niveles (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
torre_id UUID NOT NULL REFERENCES construction.torres(id) ON DELETE CASCADE,
|
||||||
|
floor_number INTEGER NOT NULL,
|
||||||
|
name VARCHAR(50),
|
||||||
|
total_units INTEGER DEFAULT 0,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_niveles_floor_torre UNIQUE (torre_id, floor_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: departamentos (unidades en torre)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.departamentos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
nivel_id UUID NOT NULL REFERENCES construction.niveles(id) ON DELETE CASCADE,
|
||||||
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
unit_number VARCHAR(20) NOT NULL,
|
||||||
|
area_m2 DECIMAL(10,2),
|
||||||
|
status construction.lot_status NOT NULL DEFAULT 'available',
|
||||||
|
price_base DECIMAL(14,2),
|
||||||
|
price_final DECIMAL(14,2),
|
||||||
|
buyer_id UUID,
|
||||||
|
sale_date DATE,
|
||||||
|
delivery_date DATE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_departamentos_code_nivel UNIQUE (nivel_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - CONCEPTOS Y PRESUPUESTOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: conceptos (catálogo de conceptos de obra)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.conceptos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
parent_id UUID REFERENCES construction.conceptos(id),
|
||||||
|
code VARCHAR(50) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
unit_id UUID,
|
||||||
|
unit_price DECIMAL(12,4),
|
||||||
|
is_composite BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
level INTEGER NOT NULL DEFAULT 0,
|
||||||
|
path VARCHAR(500),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_conceptos_code_tenant UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: presupuestos (presupuesto por prototipo/obra)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.presupuestos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
fraccionamiento_id UUID REFERENCES construction.fraccionamientos(id),
|
||||||
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
version INTEGER NOT NULL DEFAULT 1,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
total_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
currency_id UUID,
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_presupuestos_code_version UNIQUE (tenant_id, code, version)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: presupuesto_partidas (líneas del presupuesto)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.presupuesto_partidas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
presupuesto_id UUID NOT NULL REFERENCES construction.presupuestos(id) ON DELETE CASCADE,
|
||||||
|
concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
|
||||||
|
sequence INTEGER NOT NULL DEFAULT 0,
|
||||||
|
quantity DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
unit_price DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
total_amount DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_partidas_presupuesto_concepto UNIQUE (presupuesto_id, concepto_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - AVANCES Y CONTROL DE OBRA
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: programa_obra (programa maestro)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.programa_obra (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
version INTEGER NOT NULL DEFAULT 1,
|
||||||
|
start_date DATE NOT NULL,
|
||||||
|
end_date DATE NOT NULL,
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_programa_code_version UNIQUE (tenant_id, code, version)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: programa_actividades (actividades del programa)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.programa_actividades (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
programa_id UUID NOT NULL REFERENCES construction.programa_obra(id) ON DELETE CASCADE,
|
||||||
|
concepto_id UUID REFERENCES construction.conceptos(id),
|
||||||
|
parent_id UUID REFERENCES construction.programa_actividades(id),
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
sequence INTEGER NOT NULL DEFAULT 0,
|
||||||
|
planned_start DATE,
|
||||||
|
planned_end DATE,
|
||||||
|
planned_quantity DECIMAL(12,4) DEFAULT 0,
|
||||||
|
planned_weight DECIMAL(8,4) DEFAULT 0,
|
||||||
|
wbs_code VARCHAR(50),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: avances_obra (captura de avances)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.avances_obra (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
||||||
|
concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
|
||||||
|
capture_date DATE NOT NULL,
|
||||||
|
quantity_executed DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
percentage_executed DECIMAL(5,2) DEFAULT 0,
|
||||||
|
status construction.advance_status NOT NULL DEFAULT 'pending',
|
||||||
|
notes TEXT,
|
||||||
|
captured_by UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
reviewed_by UUID REFERENCES auth.users(id),
|
||||||
|
reviewed_at TIMESTAMPTZ,
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT chk_avances_lote_or_depto CHECK (
|
||||||
|
(lote_id IS NOT NULL AND departamento_id IS NULL) OR
|
||||||
|
(lote_id IS NULL AND departamento_id IS NOT NULL)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: fotos_avance (evidencia fotográfica)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.fotos_avance (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
avance_id UUID NOT NULL REFERENCES construction.avances_obra(id) ON DELETE CASCADE,
|
||||||
|
file_url VARCHAR(500) NOT NULL,
|
||||||
|
file_name VARCHAR(255),
|
||||||
|
file_size INTEGER,
|
||||||
|
mime_type VARCHAR(50),
|
||||||
|
description TEXT,
|
||||||
|
location GEOMETRY(POINT, 4326),
|
||||||
|
captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: bitacora_obra (registro de bitácora)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.bitacora_obra (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
entry_date DATE NOT NULL,
|
||||||
|
entry_number INTEGER NOT NULL,
|
||||||
|
weather VARCHAR(50),
|
||||||
|
temperature_max DECIMAL(4,1),
|
||||||
|
temperature_min DECIMAL(4,1),
|
||||||
|
workers_count INTEGER DEFAULT 0,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
observations TEXT,
|
||||||
|
incidents TEXT,
|
||||||
|
registered_by UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_bitacora_fracc_number UNIQUE (fraccionamiento_id, entry_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - CALIDAD Y POSTVENTA (MAI-009)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: checklists (plantillas de verificación)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.checklists (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
prototipo_id UUID REFERENCES construction.prototipos(id),
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_checklists_code_tenant UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: checklist_items (items del checklist)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.checklist_items (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
checklist_id UUID NOT NULL REFERENCES construction.checklists(id) ON DELETE CASCADE,
|
||||||
|
sequence INTEGER NOT NULL DEFAULT 0,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
is_required BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: inspecciones (inspecciones de calidad)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.inspecciones (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
checklist_id UUID NOT NULL REFERENCES construction.checklists(id),
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
||||||
|
inspection_date DATE NOT NULL,
|
||||||
|
status construction.quality_status NOT NULL DEFAULT 'pending',
|
||||||
|
inspector_id UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
notes TEXT,
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: inspeccion_resultados (resultados por item)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.inspeccion_resultados (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
inspeccion_id UUID NOT NULL REFERENCES construction.inspecciones(id) ON DELETE CASCADE,
|
||||||
|
checklist_item_id UUID NOT NULL REFERENCES construction.checklist_items(id),
|
||||||
|
is_passed BOOLEAN,
|
||||||
|
notes TEXT,
|
||||||
|
photo_url VARCHAR(500),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: tickets_postventa (tickets de garantía)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.tickets_postventa (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
||||||
|
ticket_number VARCHAR(30) NOT NULL,
|
||||||
|
reported_date DATE NOT NULL,
|
||||||
|
category VARCHAR(50),
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
priority VARCHAR(20) DEFAULT 'medium',
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'open',
|
||||||
|
assigned_to UUID REFERENCES auth.users(id),
|
||||||
|
resolution TEXT,
|
||||||
|
resolved_at TIMESTAMPTZ,
|
||||||
|
resolved_by UUID REFERENCES auth.users(id),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_tickets_number_tenant UNIQUE (tenant_id, ticket_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - CONTRATOS Y SUBCONTRATOS (MAI-012)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: subcontratistas
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.subcontratistas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
partner_id UUID,
|
||||||
|
code VARCHAR(20) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
legal_name VARCHAR(255),
|
||||||
|
tax_id VARCHAR(20),
|
||||||
|
specialty VARCHAR(100),
|
||||||
|
contact_name VARCHAR(100),
|
||||||
|
contact_phone VARCHAR(20),
|
||||||
|
contact_email VARCHAR(100),
|
||||||
|
address TEXT,
|
||||||
|
rating DECIMAL(3,2),
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_subcontratistas_code_tenant UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: contratos (contratos con subcontratistas)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.contratos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
subcontratista_id UUID NOT NULL REFERENCES construction.subcontratistas(id),
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
contract_number VARCHAR(30) NOT NULL,
|
||||||
|
contract_type construction.contract_type NOT NULL DEFAULT 'unit_price',
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
start_date DATE NOT NULL,
|
||||||
|
end_date DATE,
|
||||||
|
total_amount DECIMAL(16,2),
|
||||||
|
advance_percentage DECIMAL(5,2) DEFAULT 0,
|
||||||
|
retention_percentage DECIMAL(5,2) DEFAULT 5,
|
||||||
|
status construction.contract_status NOT NULL DEFAULT 'draft',
|
||||||
|
signed_at TIMESTAMPTZ,
|
||||||
|
signed_by UUID REFERENCES auth.users(id),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_contratos_number_tenant UNIQUE (tenant_id, contract_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: contrato_partidas (líneas del contrato)
|
||||||
|
CREATE TABLE IF NOT EXISTS construction.contrato_partidas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
contrato_id UUID NOT NULL REFERENCES construction.contratos(id) ON DELETE CASCADE,
|
||||||
|
concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
|
||||||
|
quantity DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
unit_price DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
total_amount DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDICES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Fraccionamientos
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_fraccionamientos_tenant_id ON construction.fraccionamientos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_fraccionamientos_status ON construction.fraccionamientos(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_fraccionamientos_code ON construction.fraccionamientos(code);
|
||||||
|
|
||||||
|
-- Etapas
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_etapas_tenant_id ON construction.etapas(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_etapas_fraccionamiento_id ON construction.etapas(fraccionamiento_id);
|
||||||
|
|
||||||
|
-- Manzanas
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_manzanas_tenant_id ON construction.manzanas(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_manzanas_etapa_id ON construction.manzanas(etapa_id);
|
||||||
|
|
||||||
|
-- Lotes
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_lotes_tenant_id ON construction.lotes(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_lotes_manzana_id ON construction.lotes(manzana_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_lotes_prototipo_id ON construction.lotes(prototipo_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_lotes_status ON construction.lotes(status);
|
||||||
|
|
||||||
|
-- Torres
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_torres_tenant_id ON construction.torres(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_torres_etapa_id ON construction.torres(etapa_id);
|
||||||
|
|
||||||
|
-- Niveles
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_niveles_tenant_id ON construction.niveles(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_niveles_torre_id ON construction.niveles(torre_id);
|
||||||
|
|
||||||
|
-- Departamentos
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_departamentos_tenant_id ON construction.departamentos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_departamentos_nivel_id ON construction.departamentos(nivel_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_departamentos_status ON construction.departamentos(status);
|
||||||
|
|
||||||
|
-- Prototipos
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_prototipos_tenant_id ON construction.prototipos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_prototipos_type ON construction.prototipos(type);
|
||||||
|
|
||||||
|
-- Conceptos
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_conceptos_tenant_id ON construction.conceptos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_conceptos_parent_id ON construction.conceptos(parent_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_conceptos_code ON construction.conceptos(code);
|
||||||
|
|
||||||
|
-- Presupuestos
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_presupuestos_tenant_id ON construction.presupuestos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_presupuestos_fraccionamiento_id ON construction.presupuestos(fraccionamiento_id);
|
||||||
|
|
||||||
|
-- Avances
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_avances_tenant_id ON construction.avances_obra(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_avances_lote_id ON construction.avances_obra(lote_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_avances_concepto_id ON construction.avances_obra(concepto_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_avances_capture_date ON construction.avances_obra(capture_date);
|
||||||
|
|
||||||
|
-- Bitacora
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_bitacora_tenant_id ON construction.bitacora_obra(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_bitacora_fraccionamiento_id ON construction.bitacora_obra(fraccionamiento_id);
|
||||||
|
|
||||||
|
-- Inspecciones
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_inspecciones_tenant_id ON construction.inspecciones(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_inspecciones_status ON construction.inspecciones(status);
|
||||||
|
|
||||||
|
-- Tickets
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_tickets_tenant_id ON construction.tickets_postventa(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_tickets_status ON construction.tickets_postventa(status);
|
||||||
|
|
||||||
|
-- Subcontratistas
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_subcontratistas_tenant_id ON construction.subcontratistas(tenant_id);
|
||||||
|
|
||||||
|
-- Contratos
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_contratos_tenant_id ON construction.contratos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_contratos_subcontratista_id ON construction.contratos(subcontratista_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_contratos_fraccionamiento_id ON construction.contratos(fraccionamiento_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ROW LEVEL SECURITY (RLS)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE construction.fraccionamientos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.etapas ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.manzanas ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.lotes ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.torres ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.niveles ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.departamentos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.prototipos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.conceptos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.presupuestos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.presupuesto_partidas ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.programa_obra ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.programa_actividades ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.avances_obra ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.fotos_avance ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.bitacora_obra ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.checklists ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.checklist_items ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.inspecciones ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.inspeccion_resultados ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.tickets_postventa ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.subcontratistas ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.contratos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE construction.contrato_partidas ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
-- Policies de tenant isolation usando current_setting
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_fraccionamientos ON construction.fraccionamientos;
|
||||||
|
CREATE POLICY tenant_isolation_fraccionamientos ON construction.fraccionamientos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_etapas ON construction.etapas;
|
||||||
|
CREATE POLICY tenant_isolation_etapas ON construction.etapas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_manzanas ON construction.manzanas;
|
||||||
|
CREATE POLICY tenant_isolation_manzanas ON construction.manzanas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_lotes ON construction.lotes;
|
||||||
|
CREATE POLICY tenant_isolation_lotes ON construction.lotes
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_torres ON construction.torres;
|
||||||
|
CREATE POLICY tenant_isolation_torres ON construction.torres
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_niveles ON construction.niveles;
|
||||||
|
CREATE POLICY tenant_isolation_niveles ON construction.niveles
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_departamentos ON construction.departamentos;
|
||||||
|
CREATE POLICY tenant_isolation_departamentos ON construction.departamentos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_prototipos ON construction.prototipos;
|
||||||
|
CREATE POLICY tenant_isolation_prototipos ON construction.prototipos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_conceptos ON construction.conceptos;
|
||||||
|
CREATE POLICY tenant_isolation_conceptos ON construction.conceptos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_presupuestos ON construction.presupuestos;
|
||||||
|
CREATE POLICY tenant_isolation_presupuestos ON construction.presupuestos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_presupuesto_partidas ON construction.presupuesto_partidas;
|
||||||
|
CREATE POLICY tenant_isolation_presupuesto_partidas ON construction.presupuesto_partidas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_programa_obra ON construction.programa_obra;
|
||||||
|
CREATE POLICY tenant_isolation_programa_obra ON construction.programa_obra
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_programa_actividades ON construction.programa_actividades;
|
||||||
|
CREATE POLICY tenant_isolation_programa_actividades ON construction.programa_actividades
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_avances_obra ON construction.avances_obra;
|
||||||
|
CREATE POLICY tenant_isolation_avances_obra ON construction.avances_obra
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_fotos_avance ON construction.fotos_avance;
|
||||||
|
CREATE POLICY tenant_isolation_fotos_avance ON construction.fotos_avance
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_bitacora_obra ON construction.bitacora_obra;
|
||||||
|
CREATE POLICY tenant_isolation_bitacora_obra ON construction.bitacora_obra
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_checklists ON construction.checklists;
|
||||||
|
CREATE POLICY tenant_isolation_checklists ON construction.checklists
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_checklist_items ON construction.checklist_items;
|
||||||
|
CREATE POLICY tenant_isolation_checklist_items ON construction.checklist_items
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_inspecciones ON construction.inspecciones;
|
||||||
|
CREATE POLICY tenant_isolation_inspecciones ON construction.inspecciones
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_inspeccion_resultados ON construction.inspeccion_resultados;
|
||||||
|
CREATE POLICY tenant_isolation_inspeccion_resultados ON construction.inspeccion_resultados
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_tickets_postventa ON construction.tickets_postventa;
|
||||||
|
CREATE POLICY tenant_isolation_tickets_postventa ON construction.tickets_postventa
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_subcontratistas ON construction.subcontratistas;
|
||||||
|
CREATE POLICY tenant_isolation_subcontratistas ON construction.subcontratistas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_contratos ON construction.contratos;
|
||||||
|
CREATE POLICY tenant_isolation_contratos ON construction.contratos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_contrato_partidas ON construction.contrato_partidas;
|
||||||
|
CREATE POLICY tenant_isolation_contrato_partidas ON construction.contrato_partidas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA construction IS 'Schema de construcción: obras, lotes, avances, calidad, contratos';
|
||||||
|
COMMENT ON TABLE construction.fraccionamientos IS 'Desarrollos inmobiliarios/fraccionamientos';
|
||||||
|
COMMENT ON TABLE construction.etapas IS 'Etapas/fases de un fraccionamiento';
|
||||||
|
COMMENT ON TABLE construction.manzanas IS 'Manzanas dentro de una etapa';
|
||||||
|
COMMENT ON TABLE construction.lotes IS 'Lotes/terrenos vendibles (horizontal)';
|
||||||
|
COMMENT ON TABLE construction.torres IS 'Torres/edificios (vertical)';
|
||||||
|
COMMENT ON TABLE construction.niveles IS 'Pisos de una torre';
|
||||||
|
COMMENT ON TABLE construction.departamentos IS 'Departamentos/unidades en torre';
|
||||||
|
COMMENT ON TABLE construction.prototipos IS 'Tipos de vivienda/prototipos';
|
||||||
|
COMMENT ON TABLE construction.conceptos IS 'Catálogo de conceptos de obra';
|
||||||
|
COMMENT ON TABLE construction.presupuestos IS 'Presupuestos por prototipo u obra';
|
||||||
|
COMMENT ON TABLE construction.avances_obra IS 'Captura de avances físicos';
|
||||||
|
COMMENT ON TABLE construction.bitacora_obra IS 'Bitácora diaria de obra';
|
||||||
|
COMMENT ON TABLE construction.checklists IS 'Plantillas de verificación';
|
||||||
|
COMMENT ON TABLE construction.inspecciones IS 'Inspecciones de calidad';
|
||||||
|
COMMENT ON TABLE construction.tickets_postventa IS 'Tickets de garantía';
|
||||||
|
COMMENT ON TABLE construction.subcontratistas IS 'Catálogo de subcontratistas';
|
||||||
|
COMMENT ON TABLE construction.contratos IS 'Contratos con subcontratistas';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN DEL SCHEMA CONSTRUCTION
|
||||||
|
-- Total tablas: 24
|
||||||
|
-- ============================================================================
|
||||||
156
schemas/02-hr-schema-ddl.sql
Normal file
156
schemas/02-hr-schema-ddl.sql
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- HR Schema DDL - Extension de RRHH para Construccion
|
||||||
|
-- Modulo: MAI-007 RRHH y Asistencias
|
||||||
|
-- Version: 1.0.0
|
||||||
|
-- Fecha: 2025-12-06
|
||||||
|
-- ============================================================================
|
||||||
|
-- POLITICA: CARGA LIMPIA (ver DIRECTIVA-POLITICA-CARGA-LIMPIA.md)
|
||||||
|
-- Este archivo es parte de la fuente de verdad DDL.
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Verificar prerequisitos
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
||||||
|
RAISE EXCEPTION 'Schema auth no existe. ERP-Core debe estar instalado';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname = 'auth' AND tablename = 'tenants') THEN
|
||||||
|
RAISE EXCEPTION 'Tabla auth.tenants no existe. ERP-Core debe estar instalado';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'construction') THEN
|
||||||
|
RAISE EXCEPTION 'Schema construction no existe. Ejecutar primero 01-construction-schema-ddl.sql';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Crear schema si no existe
|
||||||
|
CREATE SCHEMA IF NOT EXISTS hr;
|
||||||
|
|
||||||
|
-- Configurar search_path
|
||||||
|
SET search_path TO hr, construction, core, core_shared, public;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLAS BASE (requeridas por HSE y otros modulos)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: Empleados
|
||||||
|
CREATE TABLE IF NOT EXISTS hr.employees (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
|
||||||
|
codigo VARCHAR(20) NOT NULL,
|
||||||
|
nombre VARCHAR(100) NOT NULL,
|
||||||
|
apellido_paterno VARCHAR(100) NOT NULL,
|
||||||
|
apellido_materno VARCHAR(100),
|
||||||
|
curp VARCHAR(18),
|
||||||
|
rfc VARCHAR(13),
|
||||||
|
nss VARCHAR(11),
|
||||||
|
fecha_nacimiento DATE,
|
||||||
|
genero VARCHAR(1),
|
||||||
|
email VARCHAR(255),
|
||||||
|
telefono VARCHAR(20),
|
||||||
|
direccion TEXT,
|
||||||
|
fecha_ingreso DATE NOT NULL,
|
||||||
|
fecha_baja DATE,
|
||||||
|
puesto_id UUID,
|
||||||
|
departamento VARCHAR(100),
|
||||||
|
tipo_contrato VARCHAR(50),
|
||||||
|
salario_diario DECIMAL(10,2),
|
||||||
|
estado VARCHAR(20) NOT NULL DEFAULT 'activo',
|
||||||
|
foto_url VARCHAR(500),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
|
||||||
|
CONSTRAINT uq_employees_codigo UNIQUE (tenant_id, codigo),
|
||||||
|
CONSTRAINT uq_employees_curp UNIQUE (tenant_id, curp)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: Puestos
|
||||||
|
CREATE TABLE IF NOT EXISTS hr.puestos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
|
||||||
|
codigo VARCHAR(20) NOT NULL,
|
||||||
|
nombre VARCHAR(100) NOT NULL,
|
||||||
|
descripcion TEXT,
|
||||||
|
nivel_riesgo VARCHAR(20),
|
||||||
|
requiere_capacitacion_especial BOOLEAN DEFAULT false,
|
||||||
|
activo BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT uq_puestos_codigo UNIQUE (tenant_id, codigo)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Agregar FK de puesto a empleados
|
||||||
|
ALTER TABLE hr.employees
|
||||||
|
ADD CONSTRAINT fk_employees_puesto
|
||||||
|
FOREIGN KEY (puesto_id) REFERENCES hr.puestos(id);
|
||||||
|
|
||||||
|
-- Tabla: Asignacion de empleados a obras
|
||||||
|
CREATE TABLE IF NOT EXISTS hr.employee_fraccionamientos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id),
|
||||||
|
employee_id UUID NOT NULL REFERENCES hr.employees(id),
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
fecha_inicio DATE NOT NULL,
|
||||||
|
fecha_fin DATE,
|
||||||
|
rol VARCHAR(50),
|
||||||
|
activo BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
|
||||||
|
CONSTRAINT uq_employee_fraccionamiento UNIQUE (employee_id, fraccionamiento_id, fecha_inicio)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDICES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_employees_tenant ON hr.employees(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_employees_estado ON hr.employees(estado);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_employees_puesto ON hr.employees(puesto_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_puestos_tenant ON hr.puestos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_employee_fraccionamientos_employee ON hr.employee_fraccionamientos(employee_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_employee_fraccionamientos_fraccionamiento ON hr.employee_fraccionamientos(fraccionamiento_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ROW LEVEL SECURITY
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE hr.employees ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE hr.puestos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE hr.employee_fraccionamientos ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
CREATE POLICY tenant_isolation_employees ON hr.employees
|
||||||
|
FOR ALL
|
||||||
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
|
||||||
|
CREATE POLICY tenant_isolation_puestos ON hr.puestos
|
||||||
|
FOR ALL
|
||||||
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
|
||||||
|
CREATE POLICY tenant_isolation_employee_fraccionamientos ON hr.employee_fraccionamientos
|
||||||
|
FOR ALL
|
||||||
|
USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TRIGGERS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE TRIGGER trg_employees_updated_at
|
||||||
|
BEFORE UPDATE ON hr.employees
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION core_shared.set_updated_at();
|
||||||
|
|
||||||
|
CREATE TRIGGER trg_puestos_updated_at
|
||||||
|
BEFORE UPDATE ON hr.puestos
|
||||||
|
FOR EACH ROW EXECUTE FUNCTION core_shared.set_updated_at();
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE hr.employees IS 'Empleados de la empresa';
|
||||||
|
COMMENT ON TABLE hr.puestos IS 'Catalogo de puestos de trabajo';
|
||||||
|
COMMENT ON TABLE hr.employee_fraccionamientos IS 'Asignacion de empleados a obras/fraccionamientos';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN
|
||||||
|
-- ============================================================================
|
||||||
1268
schemas/03-hse-schema-ddl.sql
Normal file
1268
schemas/03-hse-schema-ddl.sql
Normal file
File diff suppressed because it is too large
Load Diff
415
schemas/04-estimates-schema-ddl.sql
Normal file
415
schemas/04-estimates-schema-ddl.sql
Normal file
@ -0,0 +1,415 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- ESTIMATES Schema DDL - Estimaciones, Anticipos y Retenciones
|
||||||
|
-- Modulos: MAI-008 (Estimaciones y Facturación)
|
||||||
|
-- Version: 1.0.0
|
||||||
|
-- Fecha: 2025-12-08
|
||||||
|
-- ============================================================================
|
||||||
|
-- PREREQUISITOS:
|
||||||
|
-- 1. ERP-Core instalado (auth.tenants, auth.users)
|
||||||
|
-- 2. Schema construction instalado (fraccionamientos, contratos, conceptos, lotes, departamentos)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Verificar prerequisitos
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
||||||
|
RAISE EXCEPTION 'Schema auth no existe. Ejecutar primero ERP-Core DDL';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'construction') THEN
|
||||||
|
RAISE EXCEPTION 'Schema construction no existe. Ejecutar primero construction DDL';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Crear schema
|
||||||
|
CREATE SCHEMA IF NOT EXISTS estimates;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TYPES (ENUMs)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE estimates.estimate_status AS ENUM (
|
||||||
|
'draft', 'submitted', 'reviewed', 'approved', 'invoiced', 'paid', 'rejected', 'cancelled'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE estimates.advance_type AS ENUM (
|
||||||
|
'initial', 'progress', 'materials'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE estimates.retention_type AS ENUM (
|
||||||
|
'guarantee', 'tax', 'penalty', 'other'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE estimates.generator_status AS ENUM (
|
||||||
|
'draft', 'in_progress', 'completed', 'approved'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - ESTIMACIONES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: estimaciones (estimaciones de obra)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.estimaciones (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
contrato_id UUID NOT NULL REFERENCES construction.contratos(id),
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
estimate_number VARCHAR(30) NOT NULL,
|
||||||
|
period_start DATE NOT NULL,
|
||||||
|
period_end DATE NOT NULL,
|
||||||
|
sequence_number INTEGER NOT NULL,
|
||||||
|
status estimates.estimate_status NOT NULL DEFAULT 'draft',
|
||||||
|
subtotal DECIMAL(16,2) DEFAULT 0,
|
||||||
|
advance_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
retention_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
tax_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
total_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
submitted_at TIMESTAMPTZ,
|
||||||
|
submitted_by UUID REFERENCES auth.users(id),
|
||||||
|
reviewed_at TIMESTAMPTZ,
|
||||||
|
reviewed_by UUID REFERENCES auth.users(id),
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
invoice_id UUID,
|
||||||
|
invoiced_at TIMESTAMPTZ,
|
||||||
|
paid_at TIMESTAMPTZ,
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_estimaciones_number_tenant UNIQUE (tenant_id, estimate_number),
|
||||||
|
CONSTRAINT uq_estimaciones_sequence_contrato UNIQUE (contrato_id, sequence_number),
|
||||||
|
CONSTRAINT chk_estimaciones_period CHECK (period_end >= period_start)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: estimacion_conceptos (líneas de estimación)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.estimacion_conceptos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id) ON DELETE CASCADE,
|
||||||
|
concepto_id UUID NOT NULL REFERENCES construction.conceptos(id),
|
||||||
|
contrato_partida_id UUID REFERENCES construction.contrato_partidas(id),
|
||||||
|
quantity_contract DECIMAL(12,4) DEFAULT 0,
|
||||||
|
quantity_previous DECIMAL(12,4) DEFAULT 0,
|
||||||
|
quantity_current DECIMAL(12,4) DEFAULT 0,
|
||||||
|
quantity_accumulated DECIMAL(12,4) GENERATED ALWAYS AS (quantity_previous + quantity_current) STORED,
|
||||||
|
unit_price DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
amount_current DECIMAL(14,2) GENERATED ALWAYS AS (quantity_current * unit_price) STORED,
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_est_conceptos_estimacion_concepto UNIQUE (estimacion_id, concepto_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: generadores (soporte de cantidades)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.generadores (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
estimacion_concepto_id UUID NOT NULL REFERENCES estimates.estimacion_conceptos(id) ON DELETE CASCADE,
|
||||||
|
generator_number VARCHAR(30) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
status estimates.generator_status NOT NULL DEFAULT 'draft',
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
||||||
|
location_description VARCHAR(255),
|
||||||
|
quantity DECIMAL(12,4) NOT NULL DEFAULT 0,
|
||||||
|
formula TEXT,
|
||||||
|
photo_url VARCHAR(500),
|
||||||
|
sketch_url VARCHAR(500),
|
||||||
|
captured_by UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
captured_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - ANTICIPOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: anticipos (anticipos otorgados)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.anticipos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
contrato_id UUID NOT NULL REFERENCES construction.contratos(id),
|
||||||
|
advance_type estimates.advance_type NOT NULL DEFAULT 'initial',
|
||||||
|
advance_number VARCHAR(30) NOT NULL,
|
||||||
|
advance_date DATE NOT NULL,
|
||||||
|
gross_amount DECIMAL(16,2) NOT NULL,
|
||||||
|
tax_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
net_amount DECIMAL(16,2) NOT NULL,
|
||||||
|
amortization_percentage DECIMAL(5,2) DEFAULT 0,
|
||||||
|
amortized_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
pending_amount DECIMAL(16,2) GENERATED ALWAYS AS (net_amount - amortized_amount) STORED,
|
||||||
|
is_fully_amortized BOOLEAN DEFAULT FALSE,
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
paid_at TIMESTAMPTZ,
|
||||||
|
payment_reference VARCHAR(100),
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_anticipos_number_tenant UNIQUE (tenant_id, advance_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: amortizaciones (amortizaciones de anticipos)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.amortizaciones (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
anticipo_id UUID NOT NULL REFERENCES estimates.anticipos(id),
|
||||||
|
estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id),
|
||||||
|
amount DECIMAL(16,2) NOT NULL,
|
||||||
|
amortization_date DATE NOT NULL,
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_amortizaciones_anticipo_estimacion UNIQUE (anticipo_id, estimacion_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - RETENCIONES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: retenciones (retenciones aplicadas)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.retenciones (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id),
|
||||||
|
retention_type estimates.retention_type NOT NULL,
|
||||||
|
description VARCHAR(255) NOT NULL,
|
||||||
|
percentage DECIMAL(5,2),
|
||||||
|
amount DECIMAL(16,2) NOT NULL,
|
||||||
|
release_date DATE,
|
||||||
|
released_at TIMESTAMPTZ,
|
||||||
|
released_amount DECIMAL(16,2),
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: fondo_garantia (acumulado de fondo de garantía)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.fondo_garantia (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
contrato_id UUID NOT NULL REFERENCES construction.contratos(id),
|
||||||
|
accumulated_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
released_amount DECIMAL(16,2) DEFAULT 0,
|
||||||
|
pending_amount DECIMAL(16,2) GENERATED ALWAYS AS (accumulated_amount - released_amount) STORED,
|
||||||
|
release_date DATE,
|
||||||
|
released_at TIMESTAMPTZ,
|
||||||
|
released_by UUID REFERENCES auth.users(id),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_fondo_garantia_contrato UNIQUE (contrato_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - WORKFLOW
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: estimacion_workflow (historial de workflow)
|
||||||
|
CREATE TABLE IF NOT EXISTS estimates.estimacion_workflow (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
estimacion_id UUID NOT NULL REFERENCES estimates.estimaciones(id) ON DELETE CASCADE,
|
||||||
|
from_status estimates.estimate_status,
|
||||||
|
to_status estimates.estimate_status NOT NULL,
|
||||||
|
action VARCHAR(50) NOT NULL,
|
||||||
|
comments TEXT,
|
||||||
|
performed_by UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
performed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDICES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_estimaciones_tenant_id ON estimates.estimaciones(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_estimaciones_contrato_id ON estimates.estimaciones(contrato_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_estimaciones_fraccionamiento_id ON estimates.estimaciones(fraccionamiento_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_estimaciones_status ON estimates.estimaciones(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_estimaciones_period ON estimates.estimaciones(period_start, period_end);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_est_conceptos_tenant_id ON estimates.estimacion_conceptos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_est_conceptos_estimacion_id ON estimates.estimacion_conceptos(estimacion_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_est_conceptos_concepto_id ON estimates.estimacion_conceptos(concepto_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_generadores_tenant_id ON estimates.generadores(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_generadores_est_concepto_id ON estimates.generadores(estimacion_concepto_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_generadores_status ON estimates.generadores(status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_anticipos_tenant_id ON estimates.anticipos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_anticipos_contrato_id ON estimates.anticipos(contrato_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_anticipos_type ON estimates.anticipos(advance_type);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_amortizaciones_tenant_id ON estimates.amortizaciones(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_amortizaciones_anticipo_id ON estimates.amortizaciones(anticipo_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_amortizaciones_estimacion_id ON estimates.amortizaciones(estimacion_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_retenciones_tenant_id ON estimates.retenciones(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_retenciones_estimacion_id ON estimates.retenciones(estimacion_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_retenciones_type ON estimates.retenciones(retention_type);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_fondo_garantia_tenant_id ON estimates.fondo_garantia(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_fondo_garantia_contrato_id ON estimates.fondo_garantia(contrato_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_est_workflow_tenant_id ON estimates.estimacion_workflow(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_est_workflow_estimacion_id ON estimates.estimacion_workflow(estimacion_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ROW LEVEL SECURITY (RLS)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE estimates.estimaciones ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE estimates.estimacion_conceptos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE estimates.generadores ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE estimates.anticipos ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE estimates.amortizaciones ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE estimates.retenciones ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE estimates.fondo_garantia ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE estimates.estimacion_workflow ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_estimaciones ON estimates.estimaciones;
|
||||||
|
CREATE POLICY tenant_isolation_estimaciones ON estimates.estimaciones
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_est_conceptos ON estimates.estimacion_conceptos;
|
||||||
|
CREATE POLICY tenant_isolation_est_conceptos ON estimates.estimacion_conceptos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_generadores ON estimates.generadores;
|
||||||
|
CREATE POLICY tenant_isolation_generadores ON estimates.generadores
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_anticipos ON estimates.anticipos;
|
||||||
|
CREATE POLICY tenant_isolation_anticipos ON estimates.anticipos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_amortizaciones ON estimates.amortizaciones;
|
||||||
|
CREATE POLICY tenant_isolation_amortizaciones ON estimates.amortizaciones
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_retenciones ON estimates.retenciones;
|
||||||
|
CREATE POLICY tenant_isolation_retenciones ON estimates.retenciones
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_fondo_garantia ON estimates.fondo_garantia;
|
||||||
|
CREATE POLICY tenant_isolation_fondo_garantia ON estimates.fondo_garantia
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_est_workflow ON estimates.estimacion_workflow;
|
||||||
|
CREATE POLICY tenant_isolation_est_workflow ON estimates.estimacion_workflow
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FUNCIONES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Función: calcular totales de estimación
|
||||||
|
CREATE OR REPLACE FUNCTION estimates.calculate_estimate_totals(p_estimacion_id UUID)
|
||||||
|
RETURNS VOID AS $$
|
||||||
|
DECLARE
|
||||||
|
v_subtotal DECIMAL(16,2);
|
||||||
|
v_advance DECIMAL(16,2);
|
||||||
|
v_retention DECIMAL(16,2);
|
||||||
|
v_tax_rate DECIMAL(5,2) := 0.16;
|
||||||
|
v_tax DECIMAL(16,2);
|
||||||
|
v_total DECIMAL(16,2);
|
||||||
|
BEGIN
|
||||||
|
SELECT COALESCE(SUM(amount_current), 0) INTO v_subtotal
|
||||||
|
FROM estimates.estimacion_conceptos
|
||||||
|
WHERE estimacion_id = p_estimacion_id AND deleted_at IS NULL;
|
||||||
|
|
||||||
|
SELECT COALESCE(SUM(amount), 0) INTO v_advance
|
||||||
|
FROM estimates.amortizaciones
|
||||||
|
WHERE estimacion_id = p_estimacion_id AND deleted_at IS NULL;
|
||||||
|
|
||||||
|
SELECT COALESCE(SUM(amount), 0) INTO v_retention
|
||||||
|
FROM estimates.retenciones
|
||||||
|
WHERE estimacion_id = p_estimacion_id AND deleted_at IS NULL;
|
||||||
|
|
||||||
|
v_tax := v_subtotal * v_tax_rate;
|
||||||
|
v_total := v_subtotal + v_tax - v_advance - v_retention;
|
||||||
|
|
||||||
|
UPDATE estimates.estimaciones
|
||||||
|
SET subtotal = v_subtotal,
|
||||||
|
advance_amount = v_advance,
|
||||||
|
retention_amount = v_retention,
|
||||||
|
tax_amount = v_tax,
|
||||||
|
total_amount = v_total,
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = p_estimacion_id;
|
||||||
|
END;
|
||||||
|
$$ LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA estimates IS 'Schema de estimaciones, anticipos y retenciones de obra';
|
||||||
|
COMMENT ON TABLE estimates.estimaciones IS 'Estimaciones de obra periódicas';
|
||||||
|
COMMENT ON TABLE estimates.estimacion_conceptos IS 'Líneas de concepto por estimación';
|
||||||
|
COMMENT ON TABLE estimates.generadores IS 'Generadores de cantidades para estimaciones';
|
||||||
|
COMMENT ON TABLE estimates.anticipos IS 'Anticipos otorgados a subcontratistas';
|
||||||
|
COMMENT ON TABLE estimates.amortizaciones IS 'Amortizaciones de anticipos por estimación';
|
||||||
|
COMMENT ON TABLE estimates.retenciones IS 'Retenciones aplicadas a estimaciones';
|
||||||
|
COMMENT ON TABLE estimates.fondo_garantia IS 'Fondo de garantía acumulado por contrato';
|
||||||
|
COMMENT ON TABLE estimates.estimacion_workflow IS 'Historial de workflow de estimaciones';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN DEL SCHEMA ESTIMATES
|
||||||
|
-- Total tablas: 8
|
||||||
|
-- ============================================================================
|
||||||
413
schemas/05-infonavit-schema-ddl.sql
Normal file
413
schemas/05-infonavit-schema-ddl.sql
Normal file
@ -0,0 +1,413 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- INFONAVIT Schema DDL - Cumplimiento INFONAVIT y Derechohabientes
|
||||||
|
-- Modulos: MAI-010 (CRM Derechohabientes), MAI-011 (Integración INFONAVIT)
|
||||||
|
-- Version: 1.0.0
|
||||||
|
-- Fecha: 2025-12-08
|
||||||
|
-- ============================================================================
|
||||||
|
-- PREREQUISITOS:
|
||||||
|
-- 1. ERP-Core instalado (auth.tenants, auth.users, auth.companies)
|
||||||
|
-- 2. Schema construction instalado (fraccionamientos, lotes, departamentos)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Verificar prerequisitos
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
||||||
|
RAISE EXCEPTION 'Schema auth no existe. Ejecutar primero ERP-Core DDL';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'construction') THEN
|
||||||
|
RAISE EXCEPTION 'Schema construction no existe. Ejecutar primero construction DDL';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Crear schema
|
||||||
|
CREATE SCHEMA IF NOT EXISTS infonavit;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TYPES (ENUMs)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE infonavit.derechohabiente_status AS ENUM (
|
||||||
|
'prospect', 'pre_qualified', 'qualified', 'assigned', 'in_process', 'owner', 'cancelled'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE infonavit.credit_type AS ENUM (
|
||||||
|
'infonavit_tradicional', 'infonavit_total', 'cofinavit', 'mejoravit',
|
||||||
|
'fovissste', 'fovissste_infonavit', 'bank_credit', 'cash'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE infonavit.acta_type AS ENUM (
|
||||||
|
'inicio_obra', 'verificacion_avance', 'entrega_recepcion', 'conclusion_obra', 'liberacion_vivienda'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE infonavit.acta_status AS ENUM (
|
||||||
|
'draft', 'pending', 'signed', 'submitted', 'approved', 'rejected', 'cancelled'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE infonavit.report_type AS ENUM (
|
||||||
|
'avance_fisico', 'avance_financiero', 'inventario_viviendas', 'asignaciones', 'escrituraciones', 'cartera_vencida'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - REGISTRO INFONAVIT
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: registro_infonavit (registro del constructor ante INFONAVIT)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.registro_infonavit (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
company_id UUID NOT NULL,
|
||||||
|
registro_number VARCHAR(50) NOT NULL,
|
||||||
|
registro_date DATE NOT NULL,
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'active',
|
||||||
|
vigencia_start DATE,
|
||||||
|
vigencia_end DATE,
|
||||||
|
responsable_tecnico VARCHAR(255),
|
||||||
|
cedula_profesional VARCHAR(50),
|
||||||
|
metadata JSONB DEFAULT '{}',
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_registro_infonavit_tenant UNIQUE (tenant_id, registro_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: oferta_vivienda (oferta de viviendas ante INFONAVIT)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.oferta_vivienda (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
registro_id UUID NOT NULL REFERENCES infonavit.registro_infonavit(id),
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
oferta_number VARCHAR(50) NOT NULL,
|
||||||
|
submission_date DATE NOT NULL,
|
||||||
|
approval_date DATE,
|
||||||
|
total_units INTEGER NOT NULL DEFAULT 0,
|
||||||
|
approved_units INTEGER DEFAULT 0,
|
||||||
|
price_range_min DECIMAL(14,2),
|
||||||
|
price_range_max DECIMAL(14,2),
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||||
|
rejection_reason TEXT,
|
||||||
|
metadata JSONB DEFAULT '{}',
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_oferta_vivienda_tenant UNIQUE (tenant_id, oferta_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - DERECHOHABIENTES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: derechohabientes (compradores con crédito INFONAVIT)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.derechohabientes (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
partner_id UUID,
|
||||||
|
nss VARCHAR(15) NOT NULL,
|
||||||
|
curp VARCHAR(18),
|
||||||
|
rfc VARCHAR(13),
|
||||||
|
full_name VARCHAR(255) NOT NULL,
|
||||||
|
first_name VARCHAR(100),
|
||||||
|
last_name VARCHAR(100),
|
||||||
|
second_last_name VARCHAR(100),
|
||||||
|
birth_date DATE,
|
||||||
|
gender VARCHAR(10),
|
||||||
|
marital_status VARCHAR(20),
|
||||||
|
nationality VARCHAR(50) DEFAULT 'Mexicana',
|
||||||
|
email VARCHAR(255),
|
||||||
|
phone VARCHAR(20),
|
||||||
|
mobile VARCHAR(20),
|
||||||
|
address TEXT,
|
||||||
|
city VARCHAR(100),
|
||||||
|
state VARCHAR(100),
|
||||||
|
zip_code VARCHAR(10),
|
||||||
|
employer_name VARCHAR(255),
|
||||||
|
employer_rfc VARCHAR(13),
|
||||||
|
employment_start_date DATE,
|
||||||
|
salary DECIMAL(12,2),
|
||||||
|
cotization_weeks INTEGER,
|
||||||
|
credit_type infonavit.credit_type,
|
||||||
|
credit_number VARCHAR(50),
|
||||||
|
credit_amount DECIMAL(14,2),
|
||||||
|
puntos_infonavit DECIMAL(10,2),
|
||||||
|
subcuenta_vivienda DECIMAL(14,2),
|
||||||
|
precalificacion_date DATE,
|
||||||
|
precalificacion_amount DECIMAL(14,2),
|
||||||
|
status infonavit.derechohabiente_status NOT NULL DEFAULT 'prospect',
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_derechohabientes_nss_tenant UNIQUE (tenant_id, nss)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: asignacion_vivienda (asignación de vivienda a derechohabiente)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.asignacion_vivienda (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
derechohabiente_id UUID NOT NULL REFERENCES infonavit.derechohabientes(id),
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
||||||
|
oferta_id UUID REFERENCES infonavit.oferta_vivienda(id),
|
||||||
|
assignment_date DATE NOT NULL,
|
||||||
|
assignment_number VARCHAR(50),
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||||
|
sale_price DECIMAL(14,2) NOT NULL,
|
||||||
|
credit_amount DECIMAL(14,2),
|
||||||
|
down_payment DECIMAL(14,2),
|
||||||
|
subsidy_amount DECIMAL(14,2),
|
||||||
|
notary_name VARCHAR(255),
|
||||||
|
notary_number VARCHAR(50),
|
||||||
|
deed_date DATE,
|
||||||
|
deed_number VARCHAR(50),
|
||||||
|
public_registry_number VARCHAR(50),
|
||||||
|
public_registry_date DATE,
|
||||||
|
scheduled_delivery_date DATE,
|
||||||
|
actual_delivery_date DATE,
|
||||||
|
delivery_act_id UUID,
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT chk_asignacion_lote_or_depto CHECK (
|
||||||
|
(lote_id IS NOT NULL AND departamento_id IS NULL) OR
|
||||||
|
(lote_id IS NULL AND departamento_id IS NOT NULL)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - ACTAS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: actas (actas INFONAVIT)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.actas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
acta_type infonavit.acta_type NOT NULL,
|
||||||
|
acta_number VARCHAR(50) NOT NULL,
|
||||||
|
acta_date DATE NOT NULL,
|
||||||
|
status infonavit.acta_status NOT NULL DEFAULT 'draft',
|
||||||
|
infonavit_representative VARCHAR(255),
|
||||||
|
constructor_representative VARCHAR(255),
|
||||||
|
perito_name VARCHAR(255),
|
||||||
|
perito_cedula VARCHAR(50),
|
||||||
|
description TEXT,
|
||||||
|
observations TEXT,
|
||||||
|
agreements TEXT,
|
||||||
|
physical_advance_percentage DECIMAL(5,2),
|
||||||
|
financial_advance_percentage DECIMAL(5,2),
|
||||||
|
signed_at TIMESTAMPTZ,
|
||||||
|
submitted_to_infonavit_at TIMESTAMPTZ,
|
||||||
|
infonavit_response_at TIMESTAMPTZ,
|
||||||
|
infonavit_folio VARCHAR(50),
|
||||||
|
document_url VARCHAR(500),
|
||||||
|
signed_document_url VARCHAR(500),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_actas_number_tenant UNIQUE (tenant_id, acta_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: acta_viviendas (viviendas incluidas en acta)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.acta_viviendas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
acta_id UUID NOT NULL REFERENCES infonavit.actas(id) ON DELETE CASCADE,
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
||||||
|
advance_percentage DECIMAL(5,2),
|
||||||
|
observations TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - REPORTES INFONAVIT
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: reportes_infonavit (reportes enviados a INFONAVIT)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.reportes_infonavit (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
report_type infonavit.report_type NOT NULL,
|
||||||
|
report_number VARCHAR(50) NOT NULL,
|
||||||
|
period_start DATE NOT NULL,
|
||||||
|
period_end DATE NOT NULL,
|
||||||
|
submission_date DATE,
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'draft',
|
||||||
|
infonavit_folio VARCHAR(50),
|
||||||
|
total_units INTEGER,
|
||||||
|
units_in_progress INTEGER,
|
||||||
|
units_completed INTEGER,
|
||||||
|
units_delivered INTEGER,
|
||||||
|
physical_advance_percentage DECIMAL(5,2),
|
||||||
|
financial_advance_percentage DECIMAL(5,2),
|
||||||
|
document_url VARCHAR(500),
|
||||||
|
acknowledgment_url VARCHAR(500),
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_reportes_number_tenant UNIQUE (tenant_id, report_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: historico_puntos (histórico de puntos INFONAVIT)
|
||||||
|
CREATE TABLE IF NOT EXISTS infonavit.historico_puntos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
derechohabiente_id UUID NOT NULL REFERENCES infonavit.derechohabientes(id),
|
||||||
|
query_date DATE NOT NULL,
|
||||||
|
puntos DECIMAL(10,2),
|
||||||
|
subcuenta_vivienda DECIMAL(14,2),
|
||||||
|
cotization_weeks INTEGER,
|
||||||
|
credit_capacity DECIMAL(14,2),
|
||||||
|
source VARCHAR(50),
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDICES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_registro_infonavit_tenant_id ON infonavit.registro_infonavit(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_registro_infonavit_company_id ON infonavit.registro_infonavit(company_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_oferta_vivienda_tenant_id ON infonavit.oferta_vivienda(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_oferta_vivienda_registro_id ON infonavit.oferta_vivienda(registro_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_oferta_vivienda_fraccionamiento_id ON infonavit.oferta_vivienda(fraccionamiento_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_oferta_vivienda_status ON infonavit.oferta_vivienda(status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_derechohabientes_tenant_id ON infonavit.derechohabientes(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_derechohabientes_nss ON infonavit.derechohabientes(nss);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_derechohabientes_curp ON infonavit.derechohabientes(curp);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_derechohabientes_status ON infonavit.derechohabientes(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_derechohabientes_credit_type ON infonavit.derechohabientes(credit_type);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_asignacion_tenant_id ON infonavit.asignacion_vivienda(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_asignacion_derechohabiente_id ON infonavit.asignacion_vivienda(derechohabiente_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_asignacion_lote_id ON infonavit.asignacion_vivienda(lote_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_asignacion_status ON infonavit.asignacion_vivienda(status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_actas_tenant_id ON infonavit.actas(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_actas_fraccionamiento_id ON infonavit.actas(fraccionamiento_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_actas_type ON infonavit.actas(acta_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_actas_status ON infonavit.actas(status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_acta_viviendas_tenant_id ON infonavit.acta_viviendas(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_acta_viviendas_acta_id ON infonavit.acta_viviendas(acta_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_reportes_tenant_id ON infonavit.reportes_infonavit(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_reportes_fraccionamiento_id ON infonavit.reportes_infonavit(fraccionamiento_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_reportes_type ON infonavit.reportes_infonavit(report_type);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_historico_puntos_tenant_id ON infonavit.historico_puntos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_historico_puntos_derechohabiente_id ON infonavit.historico_puntos(derechohabiente_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ROW LEVEL SECURITY (RLS)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE infonavit.registro_infonavit ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE infonavit.oferta_vivienda ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE infonavit.derechohabientes ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE infonavit.asignacion_vivienda ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE infonavit.actas ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE infonavit.acta_viviendas ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE infonavit.reportes_infonavit ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE infonavit.historico_puntos ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_registro_infonavit ON infonavit.registro_infonavit;
|
||||||
|
CREATE POLICY tenant_isolation_registro_infonavit ON infonavit.registro_infonavit
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_oferta_vivienda ON infonavit.oferta_vivienda;
|
||||||
|
CREATE POLICY tenant_isolation_oferta_vivienda ON infonavit.oferta_vivienda
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_derechohabientes ON infonavit.derechohabientes;
|
||||||
|
CREATE POLICY tenant_isolation_derechohabientes ON infonavit.derechohabientes
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_asignacion_vivienda ON infonavit.asignacion_vivienda;
|
||||||
|
CREATE POLICY tenant_isolation_asignacion_vivienda ON infonavit.asignacion_vivienda
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_actas ON infonavit.actas;
|
||||||
|
CREATE POLICY tenant_isolation_actas ON infonavit.actas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_acta_viviendas ON infonavit.acta_viviendas;
|
||||||
|
CREATE POLICY tenant_isolation_acta_viviendas ON infonavit.acta_viviendas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_reportes_infonavit ON infonavit.reportes_infonavit;
|
||||||
|
CREATE POLICY tenant_isolation_reportes_infonavit ON infonavit.reportes_infonavit
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_historico_puntos ON infonavit.historico_puntos;
|
||||||
|
CREATE POLICY tenant_isolation_historico_puntos ON infonavit.historico_puntos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON SCHEMA infonavit IS 'Schema de cumplimiento INFONAVIT y gestión de derechohabientes';
|
||||||
|
COMMENT ON TABLE infonavit.registro_infonavit IS 'Registro del constructor ante INFONAVIT';
|
||||||
|
COMMENT ON TABLE infonavit.oferta_vivienda IS 'Oferta de viviendas registrada ante INFONAVIT';
|
||||||
|
COMMENT ON TABLE infonavit.derechohabientes IS 'Derechohabientes INFONAVIT/compradores';
|
||||||
|
COMMENT ON TABLE infonavit.asignacion_vivienda IS 'Asignación de vivienda a derechohabiente';
|
||||||
|
COMMENT ON TABLE infonavit.actas IS 'Actas oficiales INFONAVIT';
|
||||||
|
COMMENT ON TABLE infonavit.acta_viviendas IS 'Viviendas incluidas en cada acta';
|
||||||
|
COMMENT ON TABLE infonavit.reportes_infonavit IS 'Reportes periódicos enviados a INFONAVIT';
|
||||||
|
COMMENT ON TABLE infonavit.historico_puntos IS 'Histórico de consulta de puntos INFONAVIT';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN DEL SCHEMA INFONAVIT
|
||||||
|
-- Total tablas: 8
|
||||||
|
-- ============================================================================
|
||||||
213
schemas/06-inventory-ext-schema-ddl.sql
Normal file
213
schemas/06-inventory-ext-schema-ddl.sql
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- INVENTORY EXTENSION Schema DDL - Extensiones de Inventario para Construcción
|
||||||
|
-- Modulos: MAI-004 (Compras e Inventarios)
|
||||||
|
-- Version: 1.0.0
|
||||||
|
-- Fecha: 2025-12-08
|
||||||
|
-- ============================================================================
|
||||||
|
-- TIPO: Extensión del ERP Core (MGN-005 Inventory)
|
||||||
|
-- NOTA: Contiene SOLO extensiones específicas de construcción.
|
||||||
|
-- Las tablas base están en el ERP Core.
|
||||||
|
-- ============================================================================
|
||||||
|
-- PREREQUISITOS:
|
||||||
|
-- 1. ERP-Core instalado (auth.tenants, auth.users)
|
||||||
|
-- 2. Schema construction instalado (fraccionamientos, conceptos, lotes, departamentos)
|
||||||
|
-- 3. Schema inventory de ERP-Core instalado (opcional, para FKs)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Verificar prerequisitos
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
||||||
|
RAISE EXCEPTION 'Schema auth no existe. Ejecutar primero ERP-Core DDL';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'construction') THEN
|
||||||
|
RAISE EXCEPTION 'Schema construction no existe. Ejecutar primero construction DDL';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Crear schema si no existe (puede ya existir desde ERP-Core)
|
||||||
|
CREATE SCHEMA IF NOT EXISTS inventory;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TYPES (ENUMs) ADICIONALES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE inventory.warehouse_type_construction AS ENUM (
|
||||||
|
'central', 'obra', 'temporal', 'transito'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
CREATE TYPE inventory.requisition_status AS ENUM (
|
||||||
|
'draft', 'submitted', 'approved', 'partially_served', 'served', 'cancelled'
|
||||||
|
);
|
||||||
|
EXCEPTION WHEN duplicate_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - EXTENSIONES CONSTRUCCIÓN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: almacenes_proyecto (almacén por proyecto/obra)
|
||||||
|
-- Extiende: inventory.warehouses (ERP Core)
|
||||||
|
CREATE TABLE IF NOT EXISTS inventory.almacenes_proyecto (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
warehouse_id UUID NOT NULL, -- FK a inventory.warehouses (ERP Core)
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
warehouse_type inventory.warehouse_type_construction NOT NULL DEFAULT 'obra',
|
||||||
|
location_description TEXT,
|
||||||
|
location GEOMETRY(POINT, 4326),
|
||||||
|
responsible_id UUID REFERENCES auth.users(id),
|
||||||
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_almacenes_proyecto_warehouse UNIQUE (warehouse_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: requisiciones_obra (requisiciones desde obra)
|
||||||
|
CREATE TABLE IF NOT EXISTS inventory.requisiciones_obra (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
requisition_number VARCHAR(30) NOT NULL,
|
||||||
|
requisition_date DATE NOT NULL,
|
||||||
|
required_date DATE NOT NULL,
|
||||||
|
status inventory.requisition_status NOT NULL DEFAULT 'draft',
|
||||||
|
priority VARCHAR(20) DEFAULT 'medium',
|
||||||
|
requested_by UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
destination_warehouse_id UUID, -- FK a inventory.warehouses (ERP Core)
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
rejection_reason TEXT,
|
||||||
|
purchase_order_id UUID, -- FK a purchase.purchase_orders (ERP Core)
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_requisiciones_obra_number UNIQUE (tenant_id, requisition_number)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: requisicion_lineas (líneas de requisición)
|
||||||
|
CREATE TABLE IF NOT EXISTS inventory.requisicion_lineas (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
requisicion_id UUID NOT NULL REFERENCES inventory.requisiciones_obra(id) ON DELETE CASCADE,
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
concepto_id UUID REFERENCES construction.conceptos(id),
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
quantity_requested DECIMAL(12,4) NOT NULL,
|
||||||
|
quantity_approved DECIMAL(12,4),
|
||||||
|
quantity_served DECIMAL(12,4) DEFAULT 0,
|
||||||
|
unit_id UUID, -- FK a core.uom (ERP Core)
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: consumos_obra (consumos de materiales por obra/lote)
|
||||||
|
CREATE TABLE IF NOT EXISTS inventory.consumos_obra (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
stock_move_id UUID, -- FK a inventory.stock_moves (ERP Core)
|
||||||
|
fraccionamiento_id UUID NOT NULL REFERENCES construction.fraccionamientos(id),
|
||||||
|
lote_id UUID REFERENCES construction.lotes(id),
|
||||||
|
departamento_id UUID REFERENCES construction.departamentos(id),
|
||||||
|
concepto_id UUID REFERENCES construction.conceptos(id),
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
quantity DECIMAL(12,4) NOT NULL,
|
||||||
|
unit_cost DECIMAL(12,4),
|
||||||
|
total_cost DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_cost) STORED,
|
||||||
|
consumption_date DATE NOT NULL,
|
||||||
|
registered_by UUID NOT NULL REFERENCES auth.users(id),
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDICES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_almacenes_proyecto_tenant_id ON inventory.almacenes_proyecto(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_almacenes_proyecto_warehouse_id ON inventory.almacenes_proyecto(warehouse_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_almacenes_proyecto_fraccionamiento_id ON inventory.almacenes_proyecto(fraccionamiento_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisiciones_obra_tenant_id ON inventory.requisiciones_obra(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisiciones_obra_fraccionamiento_id ON inventory.requisiciones_obra(fraccionamiento_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisiciones_obra_status ON inventory.requisiciones_obra(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisiciones_obra_date ON inventory.requisiciones_obra(requisition_date);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisiciones_obra_required_date ON inventory.requisiciones_obra(required_date);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisicion_lineas_tenant_id ON inventory.requisicion_lineas(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisicion_lineas_requisicion_id ON inventory.requisicion_lineas(requisicion_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_requisicion_lineas_product_id ON inventory.requisicion_lineas(product_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_consumos_obra_tenant_id ON inventory.consumos_obra(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_consumos_obra_fraccionamiento_id ON inventory.consumos_obra(fraccionamiento_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_consumos_obra_lote_id ON inventory.consumos_obra(lote_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_consumos_obra_concepto_id ON inventory.consumos_obra(concepto_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_consumos_obra_product_id ON inventory.consumos_obra(product_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_consumos_obra_date ON inventory.consumos_obra(consumption_date);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ROW LEVEL SECURITY (RLS)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE inventory.almacenes_proyecto ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE inventory.requisiciones_obra ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE inventory.requisicion_lineas ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE inventory.consumos_obra ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_almacenes_proyecto ON inventory.almacenes_proyecto;
|
||||||
|
CREATE POLICY tenant_isolation_almacenes_proyecto ON inventory.almacenes_proyecto
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_requisiciones_obra ON inventory.requisiciones_obra;
|
||||||
|
CREATE POLICY tenant_isolation_requisiciones_obra ON inventory.requisiciones_obra
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_requisicion_lineas ON inventory.requisicion_lineas;
|
||||||
|
CREATE POLICY tenant_isolation_requisicion_lineas ON inventory.requisicion_lineas
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_consumos_obra ON inventory.consumos_obra;
|
||||||
|
CREATE POLICY tenant_isolation_consumos_obra ON inventory.consumos_obra
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE inventory.almacenes_proyecto IS 'Extensión: almacenes por proyecto de construcción';
|
||||||
|
COMMENT ON TABLE inventory.requisiciones_obra IS 'Extensión: requisiciones de material desde obra';
|
||||||
|
COMMENT ON TABLE inventory.requisicion_lineas IS 'Extensión: líneas de requisición de obra';
|
||||||
|
COMMENT ON TABLE inventory.consumos_obra IS 'Extensión: consumos de materiales por obra/lote';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN DE EXTENSIONES INVENTORY
|
||||||
|
-- Total tablas: 4
|
||||||
|
-- ============================================================================
|
||||||
227
schemas/07-purchase-ext-schema-ddl.sql
Normal file
227
schemas/07-purchase-ext-schema-ddl.sql
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- PURCHASE EXTENSION Schema DDL - Extensiones de Compras para Construcción
|
||||||
|
-- Modulos: MAI-004 (Compras e Inventarios)
|
||||||
|
-- Version: 1.0.0
|
||||||
|
-- Fecha: 2025-12-08
|
||||||
|
-- ============================================================================
|
||||||
|
-- TIPO: Extensión del ERP Core (MGN-006 Purchase)
|
||||||
|
-- NOTA: Contiene SOLO extensiones específicas de construcción.
|
||||||
|
-- Las tablas base están en el ERP Core.
|
||||||
|
-- ============================================================================
|
||||||
|
-- PREREQUISITOS:
|
||||||
|
-- 1. ERP-Core instalado (auth.tenants, auth.users)
|
||||||
|
-- 2. Schema construction instalado (fraccionamientos)
|
||||||
|
-- 3. Schema inventory extension instalado (requisiciones_obra)
|
||||||
|
-- 4. Schema purchase de ERP-Core instalado (opcional, para FKs)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Verificar prerequisitos
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'auth') THEN
|
||||||
|
RAISE EXCEPTION 'Schema auth no existe. Ejecutar primero ERP-Core DDL';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'construction') THEN
|
||||||
|
RAISE EXCEPTION 'Schema construction no existe. Ejecutar primero construction DDL';
|
||||||
|
END IF;
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = 'inventory') THEN
|
||||||
|
RAISE EXCEPTION 'Schema inventory no existe. Ejecutar primero inventory extension DDL';
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
-- Crear schema si no existe (puede ya existir desde ERP-Core)
|
||||||
|
CREATE SCHEMA IF NOT EXISTS purchase;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- TABLES - EXTENSIONES CONSTRUCCIÓN
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Tabla: purchase_order_construction (extensión de órdenes de compra)
|
||||||
|
-- Extiende: purchase.purchase_orders (ERP Core)
|
||||||
|
CREATE TABLE IF NOT EXISTS purchase.purchase_order_construction (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
purchase_order_id UUID NOT NULL, -- FK a purchase.purchase_orders (ERP Core)
|
||||||
|
fraccionamiento_id UUID REFERENCES construction.fraccionamientos(id),
|
||||||
|
requisicion_id UUID REFERENCES inventory.requisiciones_obra(id),
|
||||||
|
delivery_location VARCHAR(255),
|
||||||
|
delivery_contact VARCHAR(100),
|
||||||
|
delivery_phone VARCHAR(20),
|
||||||
|
received_by UUID REFERENCES auth.users(id),
|
||||||
|
received_at TIMESTAMPTZ,
|
||||||
|
quality_approved BOOLEAN,
|
||||||
|
quality_notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_po_construction_po_id UNIQUE (purchase_order_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: supplier_construction (extensión de proveedores)
|
||||||
|
-- Extiende: purchase.suppliers (ERP Core)
|
||||||
|
CREATE TABLE IF NOT EXISTS purchase.supplier_construction (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
supplier_id UUID NOT NULL, -- FK a purchase.suppliers (ERP Core)
|
||||||
|
is_materials_supplier BOOLEAN DEFAULT FALSE,
|
||||||
|
is_services_supplier BOOLEAN DEFAULT FALSE,
|
||||||
|
is_equipment_supplier BOOLEAN DEFAULT FALSE,
|
||||||
|
specialties TEXT[],
|
||||||
|
quality_rating DECIMAL(3,2),
|
||||||
|
delivery_rating DECIMAL(3,2),
|
||||||
|
price_rating DECIMAL(3,2),
|
||||||
|
overall_rating DECIMAL(3,2) GENERATED ALWAYS AS (
|
||||||
|
(COALESCE(quality_rating, 0) + COALESCE(delivery_rating, 0) + COALESCE(price_rating, 0)) / 3
|
||||||
|
) STORED,
|
||||||
|
last_evaluation_date DATE,
|
||||||
|
credit_limit DECIMAL(14,2),
|
||||||
|
payment_days INTEGER DEFAULT 30,
|
||||||
|
has_valid_documents BOOLEAN DEFAULT FALSE,
|
||||||
|
documents_expiry_date DATE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_supplier_construction_supplier_id UNIQUE (supplier_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: comparativo_cotizaciones (cuadro comparativo)
|
||||||
|
CREATE TABLE IF NOT EXISTS purchase.comparativo_cotizaciones (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
requisicion_id UUID REFERENCES inventory.requisiciones_obra(id),
|
||||||
|
code VARCHAR(30) NOT NULL,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
comparison_date DATE NOT NULL,
|
||||||
|
status VARCHAR(20) NOT NULL DEFAULT 'draft',
|
||||||
|
winner_supplier_id UUID, -- FK a purchase.suppliers (ERP Core)
|
||||||
|
approved_by UUID REFERENCES auth.users(id),
|
||||||
|
approved_at TIMESTAMPTZ,
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id),
|
||||||
|
deleted_at TIMESTAMPTZ,
|
||||||
|
deleted_by UUID REFERENCES auth.users(id),
|
||||||
|
CONSTRAINT uq_comparativo_code_tenant UNIQUE (tenant_id, code)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: comparativo_proveedores (proveedores en comparativo)
|
||||||
|
CREATE TABLE IF NOT EXISTS purchase.comparativo_proveedores (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
comparativo_id UUID NOT NULL REFERENCES purchase.comparativo_cotizaciones(id) ON DELETE CASCADE,
|
||||||
|
supplier_id UUID NOT NULL, -- FK a purchase.suppliers (ERP Core)
|
||||||
|
quotation_number VARCHAR(50),
|
||||||
|
quotation_date DATE,
|
||||||
|
delivery_days INTEGER,
|
||||||
|
payment_conditions VARCHAR(100),
|
||||||
|
total_amount DECIMAL(16,2),
|
||||||
|
is_selected BOOLEAN DEFAULT FALSE,
|
||||||
|
evaluation_notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Tabla: comparativo_productos (productos en comparativo)
|
||||||
|
CREATE TABLE IF NOT EXISTS purchase.comparativo_productos (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||||
|
comparativo_proveedor_id UUID NOT NULL REFERENCES purchase.comparativo_proveedores(id) ON DELETE CASCADE,
|
||||||
|
product_id UUID NOT NULL, -- FK a inventory.products (ERP Core)
|
||||||
|
quantity DECIMAL(12,4) NOT NULL,
|
||||||
|
unit_price DECIMAL(12,4) NOT NULL,
|
||||||
|
total_price DECIMAL(14,2) GENERATED ALWAYS AS (quantity * unit_price) STORED,
|
||||||
|
notes TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
created_by UUID REFERENCES auth.users(id),
|
||||||
|
updated_at TIMESTAMPTZ,
|
||||||
|
updated_by UUID REFERENCES auth.users(id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- INDICES
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_po_construction_tenant_id ON purchase.purchase_order_construction(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_po_construction_po_id ON purchase.purchase_order_construction(purchase_order_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_po_construction_fraccionamiento_id ON purchase.purchase_order_construction(fraccionamiento_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_po_construction_requisicion_id ON purchase.purchase_order_construction(requisicion_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_supplier_construction_tenant_id ON purchase.supplier_construction(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_supplier_construction_supplier_id ON purchase.supplier_construction(supplier_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_supplier_construction_rating ON purchase.supplier_construction(overall_rating);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_tenant_id ON purchase.comparativo_cotizaciones(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_requisicion_id ON purchase.comparativo_cotizaciones(requisicion_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_status ON purchase.comparativo_cotizaciones(status);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_prov_tenant_id ON purchase.comparativo_proveedores(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_prov_comparativo_id ON purchase.comparativo_proveedores(comparativo_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_prov_supplier_id ON purchase.comparativo_proveedores(supplier_id);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_prod_tenant_id ON purchase.comparativo_productos(tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_comparativo_prod_proveedor_id ON purchase.comparativo_productos(comparativo_proveedor_id);
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- ROW LEVEL SECURITY (RLS)
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
ALTER TABLE purchase.purchase_order_construction ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE purchase.supplier_construction ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE purchase.comparativo_cotizaciones ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE purchase.comparativo_proveedores ENABLE ROW LEVEL SECURITY;
|
||||||
|
ALTER TABLE purchase.comparativo_productos ENABLE ROW LEVEL SECURITY;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_po_construction ON purchase.purchase_order_construction;
|
||||||
|
CREATE POLICY tenant_isolation_po_construction ON purchase.purchase_order_construction
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_supplier_construction ON purchase.supplier_construction;
|
||||||
|
CREATE POLICY tenant_isolation_supplier_construction ON purchase.supplier_construction
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_comparativo ON purchase.comparativo_cotizaciones;
|
||||||
|
CREATE POLICY tenant_isolation_comparativo ON purchase.comparativo_cotizaciones
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_comparativo_prov ON purchase.comparativo_proveedores;
|
||||||
|
CREATE POLICY tenant_isolation_comparativo_prov ON purchase.comparativo_proveedores
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
DROP POLICY IF EXISTS tenant_isolation_comparativo_prod ON purchase.comparativo_productos;
|
||||||
|
CREATE POLICY tenant_isolation_comparativo_prod ON purchase.comparativo_productos
|
||||||
|
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);
|
||||||
|
EXCEPTION WHEN undefined_object THEN NULL; END $$;
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- COMENTARIOS
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
COMMENT ON TABLE purchase.purchase_order_construction IS 'Extensión: datos adicionales de OC para construcción';
|
||||||
|
COMMENT ON TABLE purchase.supplier_construction IS 'Extensión: datos adicionales de proveedores para construcción';
|
||||||
|
COMMENT ON TABLE purchase.comparativo_cotizaciones IS 'Extensión: cuadro comparativo de cotizaciones';
|
||||||
|
COMMENT ON TABLE purchase.comparativo_proveedores IS 'Extensión: proveedores participantes en comparativo';
|
||||||
|
COMMENT ON TABLE purchase.comparativo_productos IS 'Extensión: productos cotizados por proveedor';
|
||||||
|
|
||||||
|
-- ============================================================================
|
||||||
|
-- FIN DE EXTENSIONES PURCHASE
|
||||||
|
-- Total tablas: 5
|
||||||
|
-- ============================================================================
|
||||||
153
validate-clean-load-policy.sh
Executable file
153
validate-clean-load-policy.sh
Executable file
@ -0,0 +1,153 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# =============================================================================
|
||||||
|
# VALIDATE CLEAN LOAD POLICY
|
||||||
|
# =============================================================================
|
||||||
|
# Script de validacion de cumplimiento de DIRECTIVA-POLITICA-CARGA-LIMPIA.md
|
||||||
|
#
|
||||||
|
# Uso: ./validate-clean-load-policy.sh
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Colores
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# Configuracion
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
VIOLATIONS=0
|
||||||
|
|
||||||
|
echo -e "${BLUE}=============================================================================${NC}"
|
||||||
|
echo -e "${BLUE} VALIDACION DE POLITICA DE CARGA LIMPIA${NC}"
|
||||||
|
echo -e "${BLUE}=============================================================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 1: No debe existir carpeta migrations/
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[1/6] Verificando que NO existe carpeta migrations/...${NC}"
|
||||||
|
|
||||||
|
if [ -d "$SCRIPT_DIR/migrations" ]; then
|
||||||
|
echo -e "${RED}ERROR: Carpeta migrations/ detectada (PROHIBIDA)${NC}"
|
||||||
|
echo -e "${RED} Path: $SCRIPT_DIR/migrations${NC}"
|
||||||
|
echo -e "${YELLOW} Solucion: Eliminar carpeta y mover contenido a schemas/${NC}"
|
||||||
|
VIOLATIONS=$((VIOLATIONS + 1))
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}OK - No existe carpeta migrations/${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 2: No deben existir archivos fix-*.sql
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[2/6] Verificando que NO existen archivos fix-*.sql...${NC}"
|
||||||
|
|
||||||
|
FIX_FILES=$(find "$SCRIPT_DIR" -name "fix-*.sql" -o -name "patch-*.sql" -o -name "hotfix-*.sql" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$FIX_FILES" ]; then
|
||||||
|
echo -e "${RED}ERROR: Archivos fix/patch detectados (PROHIBIDOS):${NC}"
|
||||||
|
echo "$FIX_FILES" | while read -r file; do
|
||||||
|
echo -e "${RED} - $file${NC}"
|
||||||
|
done
|
||||||
|
echo -e "${YELLOW} Solucion: Incorporar cambios en DDL base y eliminar fixes${NC}"
|
||||||
|
VIOLATIONS=$((VIOLATIONS + 1))
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}OK - No existen archivos fix/patch${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 3: No deben existir archivos migration-*.sql o NNN-*.sql numerados
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[3/6] Verificando que NO existen archivos tipo migration...${NC}"
|
||||||
|
|
||||||
|
MIGRATION_FILES=$(find "$SCRIPT_DIR" -regex ".*[0-9][0-9][0-9][-_].*\.sql" ! -path "*/schemas/*" 2>/dev/null)
|
||||||
|
|
||||||
|
if [ -n "$MIGRATION_FILES" ]; then
|
||||||
|
echo -e "${RED}ERROR: Archivos tipo migration detectados (PROHIBIDOS):${NC}"
|
||||||
|
echo "$MIGRATION_FILES" | while read -r file; do
|
||||||
|
echo -e "${RED} - $file${NC}"
|
||||||
|
done
|
||||||
|
echo -e "${YELLOW} Solucion: Mover a schemas/ con nomenclatura correcta${NC}"
|
||||||
|
VIOLATIONS=$((VIOLATIONS + 1))
|
||||||
|
else
|
||||||
|
echo -e "${GREEN}OK - No existen archivos tipo migration fuera de schemas/${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 4: Debe existir script drop-and-recreate-database.sh
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[4/6] Verificando que existe drop-and-recreate-database.sh...${NC}"
|
||||||
|
|
||||||
|
if [ -f "$SCRIPT_DIR/drop-and-recreate-database.sh" ]; then
|
||||||
|
if [ -x "$SCRIPT_DIR/drop-and-recreate-database.sh" ]; then
|
||||||
|
echo -e "${GREEN}OK - Script existe y es ejecutable${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}WARN: Script existe pero no es ejecutable${NC}"
|
||||||
|
echo -e "${YELLOW} Solucion: chmod +x drop-and-recreate-database.sh${NC}"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED}ERROR: No existe drop-and-recreate-database.sh (REQUERIDO)${NC}"
|
||||||
|
echo -e "${YELLOW} Solucion: Crear script de recreacion limpia${NC}"
|
||||||
|
VIOLATIONS=$((VIOLATIONS + 1))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 5: Deben existir archivos DDL en schemas/
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[5/6] Verificando que existen archivos DDL en schemas/...${NC}"
|
||||||
|
|
||||||
|
DDL_COUNT=$(find "$SCRIPT_DIR/schemas" -name "*.sql" -type f 2>/dev/null | wc -l)
|
||||||
|
|
||||||
|
if [ "$DDL_COUNT" -gt 0 ]; then
|
||||||
|
echo -e "${GREEN}OK - Encontrados $DDL_COUNT archivos DDL en schemas/${NC}"
|
||||||
|
find "$SCRIPT_DIR/schemas" -name "*.sql" -type f | sort | while read -r file; do
|
||||||
|
echo -e " - $(basename "$file")"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}WARN: No hay archivos DDL en schemas/${NC}"
|
||||||
|
echo -e "${YELLOW} La base de datos puede quedar vacia${NC}"
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# CHECK 6: Debe existir archivo de inicializacion
|
||||||
|
# =============================================================================
|
||||||
|
echo -e "${YELLOW}[6/6] Verificando archivo de inicializacion...${NC}"
|
||||||
|
|
||||||
|
if [ -f "$SCRIPT_DIR/init-scripts/01-init-database.sql" ]; then
|
||||||
|
echo -e "${GREEN}OK - Existe init-scripts/01-init-database.sql${NC}"
|
||||||
|
elif [ -f "$SCRIPT_DIR/ddl/00-init.sql" ]; then
|
||||||
|
echo -e "${GREEN}OK - Existe ddl/00-init.sql${NC}"
|
||||||
|
else
|
||||||
|
echo -e "${RED}ERROR: No existe archivo de inicializacion${NC}"
|
||||||
|
echo -e "${YELLOW} Solucion: Crear init-scripts/01-init-database.sql${NC}"
|
||||||
|
VIOLATIONS=$((VIOLATIONS + 1))
|
||||||
|
fi
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# RESUMEN
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo -e "${BLUE}=============================================================================${NC}"
|
||||||
|
|
||||||
|
if [ "$VIOLATIONS" -eq 0 ]; then
|
||||||
|
echo -e "${GREEN} POLITICA DE CARGA LIMPIA: CUMPLIDA${NC}"
|
||||||
|
echo -e "${GREEN}=============================================================================${NC}"
|
||||||
|
echo -e "${GREEN} Todas las validaciones pasaron correctamente${NC}"
|
||||||
|
echo -e "${GREEN}=============================================================================${NC}"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo -e "${RED} POLITICA DE CARGA LIMPIA: VIOLADA${NC}"
|
||||||
|
echo -e "${RED}=============================================================================${NC}"
|
||||||
|
echo -e "${RED} Se encontraron $VIOLATIONS violacion(es)${NC}"
|
||||||
|
echo -e "${RED} Revisar DIRECTIVA-POLITICA-CARGA-LIMPIA.md para corregir${NC}"
|
||||||
|
echo -e "${RED}=============================================================================${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
Loading…
Reference in New Issue
Block a user