Saltar a contenido

Especificación Técnica: mae_personas

Esquema: core.identidad
Base de Datos: core
Servicio: svc-identidad
Tabla Legacy Origen: core_sigfa.mae_personas
Propósito: Maestro de personas naturales (cédula) del núcleo. Hereda de mae_entidades (patrón party).


1. Justificación y Mejoras de Arquitectura

  • Hereda de mae_entidades (joined table inheritance): su id es el mismo de la entidad padre y no genera UUID propio (sin DEFAULT gen_random_uuid()).
  • Cada fila de mae_personas tiene su fila en mae_entidades con tipo_entidad = 'PERSONA_NATURAL'.
  • Identificación y género referenciados por UUID al modelo de catálogos.
  • Registro de nacimiento y contacto en un único maestro.
  • Evolución del legacy core_sigfa.mae_personas en core.identidad.

2. Definiciones de Implementación

Table core.identidad.mae_personas {
    id                         uuid          [pk, ref: > mae_entidades.id]
    tipo_identificacion_id     uuid          [ref: > cat_tipos_identificacion.id]
    identificacion             varchar(20)   [not null]
    nombres                    varchar(100)  [not null]
    apellidos                  varchar(100)  [not null]
    fecha_nacimiento           date         
    genero_id                  uuid          [ref: > cat_generos.id]
    email                      varchar(150) 
    telefono                   varchar(20)  
    direccion                  varchar(300) 

    // Auditoría General
    is_activo               boolean       [not null, default: `true`, note: 'Estado lógico del registro']
    created_at              timestamptz   [not null, default: `now()`, note: 'Fecha de creación']
    updated_at              timestamptz   [not null, default: `now()`, note: 'Fecha de última actualización']
    deleted_at              timestamptz   [note: 'Fecha de eliminación lógica']
    created_by              uuid          [note: 'Usuario creador']
    updated_by              uuid          [note: 'Usuario modificador']

    Indexes {
        (identificacion) [unique, name: 'uq_mae_personas_identificacion']
    }
}
CREATE TABLE IF NOT EXISTS core.identidad.mae_personas (
    id UUID NOT NULL PRIMARY KEY,
    tipo_identificacion_id UUID,
    identificacion VARCHAR(20) NOT NULL,
    nombres VARCHAR(100) NOT NULL,
    apellidos VARCHAR(100) NOT NULL,
    fecha_nacimiento DATE,
    genero_id UUID,
    email VARCHAR(150),
    telefono VARCHAR(20),
    direccion VARCHAR(300),

    -- Auditoría General
    is_activo BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at TIMESTAMPTZ,
    created_by UUID,
    updated_by UUID,
    CONSTRAINT uq_mae_personas_identificacion UNIQUE (identificacion),
    CONSTRAINT fk_mae_personas_entidad FOREIGN KEY (id) REFERENCES core.identidad.mae_entidades (id),
    CONSTRAINT fk_mae_personas_tipo_identificacion FOREIGN KEY (tipo_identificacion_id) REFERENCES core.identidad.cat_tipos_identificacion (id),
    CONSTRAINT fk_mae_personas_genero FOREIGN KEY (genero_id) REFERENCES core.identidad.cat_generos (id)
);

-- Comentarios
COMMENT ON COLUMN core.identidad.mae_personas.id IS 'Mismo id de mae_entidades (herencia de tabla)';
COMMENT ON COLUMN core.identidad.mae_personas.tipo_identificacion_id IS 'Tipo de identificación';
COMMENT ON COLUMN core.identidad.mae_personas.identificacion IS 'Número de identificación';
COMMENT ON COLUMN core.identidad.mae_personas.nombres IS 'Nombres de la persona';
COMMENT ON COLUMN core.identidad.mae_personas.apellidos IS 'Apellidos de la persona';
COMMENT ON COLUMN core.identidad.mae_personas.fecha_nacimiento IS 'Fecha de nacimiento';
COMMENT ON COLUMN core.identidad.mae_personas.genero_id IS 'Género de la persona';
COMMENT ON COLUMN core.identidad.mae_personas.email IS 'Correo electrónico';
COMMENT ON COLUMN core.identidad.mae_personas.telefono IS 'Teléfono';
COMMENT ON COLUMN core.identidad.mae_personas.direccion IS 'Dirección';
from datetime import datetime, date
from typing import Optional
import uuid

from sqlalchemy import (
    BigInteger,
    Boolean,
    CheckConstraint,
    Date,
    DateTime,
    ForeignKey,
    Identity,
    Index,
    Integer,
    SmallInteger,
    String,
    Text,
    UniqueConstraint,
    text,
)
from sqlalchemy.dialects import postgresql
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column


class Base(DeclarativeBase):
    pass

class MaePersona(MaeEntidad):
    __tablename__ = 'mae_personas'
    __table_args__ = (
        UniqueConstraint('identificacion', name='uq_mae_personas_identificacion'),
        {"schema": 'core.identidad'},
    )
    __mapper_args__ = {"polymorphic_identity": "PERSONA_NATURAL"}

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("core.identidad.mae_entidades.id"),
        primary_key=True,
        comment='Mismo id de mae_entidades (herencia de tabla)',
    )
    tipo_identificacion_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("cat_tipos_identificacion.id"),
        comment='Tipo de identificación',
    )
    identificacion: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        comment='Número de identificación',
    )
    nombres: Mapped[str] = mapped_column(
        String(100),
        nullable=False,
        comment='Nombres de la persona',
    )
    apellidos: Mapped[str] = mapped_column(
        String(100),
        nullable=False,
        comment='Apellidos de la persona',
    )
    fecha_nacimiento: Mapped[date | None] = mapped_column(
        Date,
        comment='Fecha de nacimiento',
    )
    genero_id: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        ForeignKey("cat_generos.id"),
        comment='Género de la persona',
    )
    email: Mapped[str | None] = mapped_column(
        String(150),
        comment='Correo electrónico',
    )
    telefono: Mapped[str | None] = mapped_column(
        String(20),
        comment='Teléfono',
    )
    direccion: Mapped[str | None] = mapped_column(
        String(300),
        comment='Dirección',
    )

    # Auditoría General
    is_activo: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        server_default=text("true"),
        comment='Estado lógico del registro',
    )
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=text("now()"),
        comment='Fecha de creación',
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        server_default=text("now()"),
        comment='Fecha de última actualización',
    )
    deleted_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True),
        comment='Fecha de eliminación lógica',
    )
    created_by: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        comment='Usuario creador',
    )
    updated_by: Mapped[uuid.UUID | None] = mapped_column(
        UUID(as_uuid=True),
        comment='Usuario modificador',
    )
"""revision: core_id_0008
create table core.identidad.mae_personas

Nota: la herencia party (FK a mae_entidades, sin DEFAULT en id) la aplica
la migración 0005_entidades de svc-identidad.
"""

from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql


revision: str = 'core_id_0008'
down_revision: str | None = 'core_id_0007'
branch_labels: str | None = None
depends_on: str | None = None


def upgrade() -> None:
    op.create_table(
        'mae_personas',
        sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
        sa.Column("tipo_identificacion_id", postgresql.UUID(as_uuid=True)),
        sa.Column("identificacion", sa.String(length=20), nullable=False),
        sa.Column("nombres", sa.String(length=100), nullable=False),
        sa.Column("apellidos", sa.String(length=100), nullable=False),
        sa.Column("fecha_nacimiento", sa.Date()),
        sa.Column("genero_id", postgresql.UUID(as_uuid=True)),
        sa.Column("email", sa.String(length=150)),
        sa.Column("telefono", sa.String(length=20)),
        sa.Column("direccion", sa.String(length=300)),
        sa.Column("is_activo", sa.Boolean(), nullable=False, server_default=sa.text("true")),
        sa.Column("created_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
        sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False, server_default=sa.text("now()")),
        sa.Column("deleted_at", sa.DateTime(timezone=True)),
        sa.Column("created_by", postgresql.UUID(as_uuid=True)),
        sa.Column("updated_by", postgresql.UUID(as_uuid=True)),
        sa.UniqueConstraint('identificacion', name='uq_mae_personas_identificacion'),
        sa.ForeignKeyConstraint(['id'], ['core.identidad.mae_entidades.id'], name='fk_mae_personas_entidad'),
        sa.ForeignKeyConstraint(['tipo_identificacion_id'], ['core.identidad.cat_tipos_identificacion.id'], name='fk_mae_personas_tipo_identificacion'),
        sa.ForeignKeyConstraint(['genero_id'], ['core.identidad.cat_generos.id'], name='fk_mae_personas_genero'),
        schema='core.identidad',
    )


def downgrade() -> None:
    op.drop_table('mae_personas', schema='core.identidad')