From a9dcf86545121ff268c755323e2cc41c7990173e Mon Sep 17 00:00:00 2001 From: Adrian Flores Cortes Date: Tue, 3 Feb 2026 07:48:46 -0600 Subject: [PATCH] [ST-P3-001,ST-P3-003] docs: Add location fields and FK cascade policies - ESTANDAR-CAMPOS-UBICACION.md: Standards for location fields (GEOMETRY vs DECIMAL) - POLITICA-CASCADAS-FK.md: Foreign key cascade policy guide Co-Authored-By: Claude Opus 4.5 --- docs/ESTANDAR-CAMPOS-UBICACION.md | 182 +++++++++++++++++++++++++++ docs/POLITICA-CASCADAS-FK.md | 203 ++++++++++++++++++++++++++++++ 2 files changed, 385 insertions(+) create mode 100644 docs/ESTANDAR-CAMPOS-UBICACION.md create mode 100644 docs/POLITICA-CASCADAS-FK.md diff --git a/docs/ESTANDAR-CAMPOS-UBICACION.md b/docs/ESTANDAR-CAMPOS-UBICACION.md new file mode 100644 index 0000000..8d4529b --- /dev/null +++ b/docs/ESTANDAR-CAMPOS-UBICACION.md @@ -0,0 +1,182 @@ +# Estándar de Campos de Ubicación - ERP Construcción + +**Versión:** 1.0.0 +**Fecha:** 2026-02-03 +**Subtarea:** ST-P3-001 + +--- + +## Resumen + +Este documento define el estándar para campos de ubicación geográfica en la base de datos de ERP Construcción. + +--- + +## Tipos de Campos de Ubicación + +### 1. Ubicación Geográfica Precisa (RECOMENDADO) + +**Tipo:** `GEOMETRY(POINT, 4326)` + +**Uso:** Cuando se requiere almacenar coordenadas GPS para queries espaciales. + +```sql +location GEOMETRY(POINT, 4326), +``` + +**Ventajas:** +- Soporte nativo de PostGIS para queries espaciales +- Índices GiST eficientes +- Funciones ST_* para cálculos de distancia, área, etc. +- SRID 4326 = WGS84 (estándar GPS mundial) + +**Ejemplo de inserción:** +```sql +INSERT INTO tabla (location) +VALUES (ST_SetSRID(ST_MakePoint(-99.1332, 19.4326), 4326)); +``` + +**Ejemplo de query:** +```sql +SELECT * FROM tabla +WHERE ST_DWithin( + location, + ST_SetSRID(ST_MakePoint(-99.1332, 19.4326), 4326), + 1000 -- metros +); +``` + +--- + +### 2. Polígonos (Áreas) + +**Tipo:** `GEOMETRY(POLYGON, 4326)` + +**Uso:** Para delimitar áreas como fraccionamientos, etapas, manzanas. + +```sql +polygon GEOMETRY(POLYGON, 4326), +``` + +**Schemas que lo usan:** +- `construction.fraccionamientos` +- `construction.etapas` +- `construction.manzanas` + +--- + +### 3. Coordenadas Decimales (LEGACY) + +**Tipo:** `DECIMAL(10,8)` para latitud, `DECIMAL(11,8)` para longitud + +**Uso:** Compatibilidad con sistemas externos, APIs, tracking GPS simple. + +```sql +latitude DECIMAL(10,8), +longitude DECIMAL(11,8), +``` + +**Cuándo usar:** +- Integración con APIs externas que esperan lat/long separados +- Tracking de activos con alta frecuencia de escritura +- Cuando NO se requieren queries espaciales complejas + +**Schema actual:** `assets` (asset_locations, equipment, etc.) + +**Nota:** Considerar migración a GEOMETRY en futuras versiones. + +--- + +### 4. Descripción Textual + +**Tipo:** `VARCHAR(255)` o `TEXT` + +**Uso:** Ubicación descriptiva para humanos. + +```sql +location_description VARCHAR(255), +delivery_location VARCHAR(255), +``` + +**Ejemplos:** +- "Bodega principal, Rack A-3" +- "Frente 2, Nivel 1" +- "Entrada norte del fraccionamiento" + +--- + +## Convención de Nomenclatura + +| Tipo | Nombre de Campo | Ejemplo | +|------|-----------------|---------| +| GEOMETRY Point | `location` o `ubicacion_geo` | `location GEOMETRY(POINT, 4326)` | +| GEOMETRY Polygon | `polygon` o `area_geo` | `polygon GEOMETRY(POLYGON, 4326)` | +| DECIMAL latitud | `latitude` o `latitud` | `latitude DECIMAL(10,8)` | +| DECIMAL longitud | `longitude` o `longitud` | `longitude DECIMAL(11,8)` | +| Texto descriptivo | `location_description` o `ubicacion_desc` | `location_description VARCHAR(255)` | + +--- + +## Índices Recomendados + +### Para GEOMETRY + +```sql +CREATE INDEX idx_tabla_location_geo ON schema.tabla +USING GIST (location); +``` + +### Para DECIMAL (cuando se usa con PostGIS) + +```sql +CREATE INDEX idx_tabla_location_geo ON schema.tabla +USING GIST (ST_SetSRID(ST_MakePoint(longitude, latitude), 4326)) +WHERE longitude IS NOT NULL AND latitude IS NOT NULL; +``` + +--- + +## Estado Actual por Schema + +| Schema | Tipo Usado | Campos | Estado | +|--------|-----------|--------|--------| +| construction | GEOMETRY | location, polygon | ✅ Estándar | +| hse | GEOMETRY | ubicacion_geo | ✅ Estándar | +| inventory | GEOMETRY | location | ✅ Estándar | +| estimates | VARCHAR | location_description | ✅ OK (solo texto) | +| assets | DECIMAL | latitude, longitude | ⚠️ Legacy | +| purchase | VARCHAR | delivery_location | ✅ OK (solo texto) | + +--- + +## Recomendaciones + +1. **Nuevas tablas:** Usar `GEOMETRY(POINT, 4326)` para coordenadas +2. **Assets schema:** Considerar migración gradual a GEOMETRY +3. **Siempre incluir:** Campo `location_description` para contexto humano +4. **Índices:** Crear índice GiST para queries frecuentes + +--- + +## Migración Futura (Assets) + +Para migrar `assets.asset_locations` de DECIMAL a GEOMETRY: + +```sql +-- Agregar columna GEOMETRY +ALTER TABLE assets.asset_locations +ADD COLUMN location_geo GEOMETRY(POINT, 4326); + +-- Poblar desde lat/long +UPDATE assets.asset_locations +SET location_geo = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326) +WHERE latitude IS NOT NULL AND longitude IS NOT NULL; + +-- Crear índice +CREATE INDEX idx_asset_locations_geo ON assets.asset_locations +USING GIST (location_geo); +``` + +--- + +*Documentado: 2026-02-03 - ST-P3-001* diff --git a/docs/POLITICA-CASCADAS-FK.md b/docs/POLITICA-CASCADAS-FK.md new file mode 100644 index 0000000..58c1576 --- /dev/null +++ b/docs/POLITICA-CASCADAS-FK.md @@ -0,0 +1,203 @@ +# Política de Cascadas en Foreign Keys - ERP Construcción + +**Versión:** 1.0.0 +**Fecha:** 2026-02-03 +**Subtarea:** ST-P3-003 + +--- + +## Resumen + +Este documento define la política para configurar acciones de CASCADE, SET NULL, RESTRICT +y NO ACTION en foreign keys de la base de datos de ERP Construcción. + +--- + +## Opciones Disponibles + +| Acción | ON DELETE | ON UPDATE | Descripción | +|--------|-----------|-----------|-------------| +| CASCADE | ✅ | ✅ | Propaga la acción a registros relacionados | +| SET NULL | ✅ | ✅ | Establece NULL en la columna FK | +| SET DEFAULT | ✅ | ✅ | Establece el valor DEFAULT | +| RESTRICT | ✅ | ✅ | Bloquea si hay referencias (antes del commit) | +| NO ACTION | ✅ | ✅ | Bloquea si hay referencias (al commit) | + +--- + +## Estado Actual + +**Patrón dominante:** `ON DELETE CASCADE` + +El DDL actual usa CASCADE en todas las foreign keys. Esto es apropiado para: +- Relaciones padre-hijo donde el hijo no tiene sentido sin el padre +- Multi-tenancy (tenant_id → auth.tenants) +- Tablas de detalle (items de una orden, líneas de una póliza) + +--- + +## Guía de Decisión + +### Usar ON DELETE CASCADE cuando: + +1. **Relación de composición** - El hijo no existe sin el padre + ```sql + -- Items de una orden de compra + order_id UUID NOT NULL REFERENCES purchase.orders(id) ON DELETE CASCADE + ``` + +2. **Multi-tenancy** - Todos los datos del tenant se eliminan juntos + ```sql + tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE + ``` + +3. **Tablas de detalle/líneas** + - `estimacion_conceptos` → `estimaciones` + - `accounting_entry_lines` → `accounting_entries` + - `approval_steps` → `approval_instances` + +4. **Historial/Logs asociados** + - `asset_locations` → `assets` + - `document_versions` → `documents` + +--- + +### Usar ON DELETE SET NULL cuando: + +1. **Relación opcional** - El registro puede existir sin la referencia + ```sql + -- Empleado asignado puede irse pero el ticket persiste + assigned_to UUID REFERENCES hr.employees(id) ON DELETE SET NULL + ``` + +2. **Referencias a usuarios** - El usuario se va pero el registro queda + ```sql + created_by UUID REFERENCES auth.users(id) ON DELETE SET NULL + updated_by UUID REFERENCES auth.users(id) ON DELETE SET NULL + ``` + +3. **Referencias a catálogos opcionales** + ```sql + category_id UUID REFERENCES catalogs.categories(id) ON DELETE SET NULL + ``` + +--- + +### Usar ON DELETE RESTRICT cuando: + +1. **Proteger datos críticos** - No permitir borrar si hay referencias + ```sql + -- No borrar un proyecto si tiene estimaciones + proyecto_id UUID NOT NULL REFERENCES construction.proyectos(id) ON DELETE RESTRICT + ``` + +2. **Entidades maestras con dependencias de negocio** + ```sql + -- No borrar proveedor si tiene órdenes de compra + supplier_id UUID NOT NULL REFERENCES partners.suppliers(id) ON DELETE RESTRICT + ``` + +3. **Datos contables/legales** + ```sql + -- No borrar cuenta contable si tiene movimientos + account_id UUID NOT NULL REFERENCES finance.chart_of_accounts(id) ON DELETE RESTRICT + ``` + +--- + +### Usar NO ACTION cuando: + +Similar a RESTRICT, pero permite verificaciones diferidas dentro de una transacción. +Usar cuando se necesita reordenar operaciones dentro de una transacción. + +--- + +## ON UPDATE + +### Recomendación General: NO especificar + +Las primary keys son UUIDs inmutables. No hay necesidad de propagar cambios. + +```sql +-- UUID nunca cambia, no necesita ON UPDATE +id UUID PRIMARY KEY DEFAULT gen_random_uuid() +``` + +### Si se usa código natural como PK (no recomendado): + +```sql +-- Solo si el código puede cambiar +codigo VARCHAR(20) PRIMARY KEY, +-- FK con ON UPDATE CASCADE +producto_codigo VARCHAR(20) REFERENCES productos(codigo) ON UPDATE CASCADE +``` + +--- + +## Tabla de Referencia Rápida + +| Tipo de Relación | ON DELETE | Ejemplo | +|------------------|-----------|---------| +| Tenant → Datos | CASCADE | `tenant_id → auth.tenants` | +| Padre → Hijos (composición) | CASCADE | `order → order_items` | +| Referencia opcional | SET NULL | `assigned_to → employees` | +| Auditoría (created_by) | SET NULL | `created_by → users` | +| Entidad maestra protegida | RESTRICT | `proyecto → estimaciones` | +| Catálogo con dependencias | RESTRICT | `account → movements` | + +--- + +## Aplicación en Schemas Existentes + +### construction + +| FK | Actual | Recomendado | Razón | +|----|--------|-------------|-------| +| fraccionamientos.tenant_id | CASCADE | CASCADE ✅ | Multi-tenant | +| etapas.fraccionamiento_id | CASCADE | CASCADE ✅ | Composición | +| avances.responsable_id | CASCADE | SET NULL | Usuario puede irse | + +### estimates + +| FK | Actual | Recomendado | Razón | +|----|--------|-------------|-------| +| estimaciones.proyecto_id | CASCADE | RESTRICT | Proteger datos | +| conceptos_estimacion.estimacion_id | CASCADE | CASCADE ✅ | Composición | + +### finance + +| FK | Actual | Recomendado | Razón | +|----|--------|-------------|-------| +| accounting_entries.tenant_id | CASCADE | CASCADE ✅ | Multi-tenant | +| entry_lines.entry_id | CASCADE | CASCADE ✅ | Composición | +| entry_lines.account_id | - | RESTRICT | No borrar cuenta con movimientos | + +--- + +## Migración Sugerida + +Para FK que deberían usar SET NULL o RESTRICT en lugar de CASCADE: + +```sql +-- Ejemplo: Cambiar CASCADE a RESTRICT +ALTER TABLE estimates.estimaciones +DROP CONSTRAINT estimaciones_proyecto_id_fkey; + +ALTER TABLE estimates.estimaciones +ADD CONSTRAINT estimaciones_proyecto_id_fkey +FOREIGN KEY (proyecto_id) REFERENCES construction.proyectos(id) +ON DELETE RESTRICT; +``` + +**Nota:** Evaluar impacto en lógica de aplicación antes de cambiar. + +--- + +## Referencias + +- [PostgreSQL Foreign Key Constraints](https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-FK) +- ESTANDAR-DATABASE-PROFESIONAL.md + +--- + +*Documentado: 2026-02-03 - ST-P3-003*