- 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>
204 lines
5.7 KiB
Markdown
204 lines
5.7 KiB
Markdown
# 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*
|