[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 <noreply@anthropic.com>
This commit is contained in:
parent
dcdc15e162
commit
a9dcf86545
182
docs/ESTANDAR-CAMPOS-UBICACION.md
Normal file
182
docs/ESTANDAR-CAMPOS-UBICACION.md
Normal file
@ -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*
|
||||
203
docs/POLITICA-CASCADAS-FK.md
Normal file
203
docs/POLITICA-CASCADAS-FK.md
Normal file
@ -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*
|
||||
Loading…
Reference in New Issue
Block a user