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