fix/AS-prosec-revertir-boolean (#482)

prosec revertir boolean y uso de catalogo

Reviewed-on: ADUANASOFT/anexo76#482
Co-authored-by: hreyes <hreyes@aduanasoft.com.mx>
Co-committed-by: hreyes <hreyes@aduanasoft.com.mx>
This commit is contained in:
2026-06-02 21:06:23 +00:00
committed by acazares
parent 96b3beed4f
commit 26c5a97c04
7 changed files with 147 additions and 78 deletions

View File

@@ -1,37 +0,0 @@
"""company prosec boolean to integer
Revision ID: 506969b0a256
Revises: a7b8c9d0e1f2
Create Date: 2026-06-02 00:00:00.000000
"""
from typing import Sequence, Union
from alembic import op
revision: str = "506969b0a256"
down_revision: Union[str, None] = "a7b8c9d0e1f2"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
# Convierte prosec de boolean a integer para almacenar el ID del sector PROSEC.
# El default 'false' debe eliminarse antes de cambiar el tipo.
op.execute("ALTER TABLE a76.company ALTER COLUMN prosec DROP DEFAULT")
op.execute("ALTER TABLE a76.company ALTER COLUMN prosec DROP NOT NULL")
op.execute(
"ALTER TABLE a76.company ALTER COLUMN prosec TYPE integer USING prosec::int"
)
def downgrade() -> None:
op.execute(
"""
ALTER TABLE a76.company
ALTER COLUMN prosec TYPE boolean
USING CASE WHEN prosec IS NULL OR prosec = 0 THEN false ELSE true END
"""
)
op.execute("ALTER TABLE a76.company ALTER COLUMN prosec SET DEFAULT false")
op.execute("ALTER TABLE a76.company ALTER COLUMN prosec SET NOT NULL")

View File

@@ -9,7 +9,7 @@ La migración mueve el valor de los extranjeros (type_nat_foreign='E') de `rfc`
`tax_id` y deja `rfc` en NULL para esos registros. Los nacionales no se tocan.
Revision ID: b3c4d5e6f7a8
Revises: 506969b0a256
Revises: a7b8c9d0e1f2
Create Date: 2026-06-02 10:00:00.000000
"""
@@ -20,7 +20,7 @@ import sqlalchemy as sa
from alembic import op
revision: str = "b3c4d5e6f7a8"
down_revision: Union[str, None] = "506969b0a256"
down_revision: Union[str, None] = "a7b8c9d0e1f2"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

View File

@@ -26,9 +26,9 @@ class CompanyCreateDTO(BaseModel):
program_number: Optional[str] = Field(
None, max_length=40, description="Program number"
)
prosec: Optional[int] = Field(None, description="PROSEC")
prosec: Optional[bool] = Field(False, description="Empresa con programa PROSEC")
prosec_authorization: Optional[str] = Field(
None, max_length=20, description="PROSEC authorization"
None, max_length=20, description="Clave del sector PROSEC"
)
# Identifiers
@@ -250,6 +250,22 @@ class CompanyCreateDTO(BaseModel):
raise ValueError("Date must be in DD/MM/YYYY format") from exc
raise ValueError("Date must be in DD/MM/YYYY format")
@field_validator("prosec", mode="before")
@classmethod
def coerce_prosec_to_bool(cls, value):
"""Coacciona prosec a booleano. Tolera valores heredados cuando la columna
era entero (ID de sector): null/0/vacío → False, cualquier otro → True.
Misma regla que el downgrade de la migración previa."""
if value in (None, ""):
return False
if isinstance(value, bool):
return value
if isinstance(value, (int, float)):
return value != 0
if isinstance(value, str):
return value.strip().lower() not in {"0", "false", "no", "off", ""}
return bool(value)
@field_validator("annex30_modality", mode="before")
@classmethod
def validate_annex30_modality(cls, value):
@@ -278,7 +294,7 @@ class CompanyResponseDTO(BaseModel):
# Program information
program: Optional[str] = None
program_number: Optional[str] = None
prosec: Optional[int] = None
prosec: bool = False
prosec_authorization: Optional[str] = None
# Identifiers
@@ -317,6 +333,21 @@ class CompanyResponseDTO(BaseModel):
created_at: datetime
updated_at: Optional[datetime] = None
@field_validator("prosec", mode="before")
@classmethod
def coerce_prosec_to_bool(cls, value):
"""Tolera datos heredados cuando la columna prosec era entero (ID de sector):
null/0/vacío → False, cualquier otro → True."""
if value in (None, ""):
return False
if isinstance(value, bool):
return value
if isinstance(value, (int, float)):
return value != 0
if isinstance(value, str):
return value.strip().lower() not in {"0", "false", "no", "off", ""}
return bool(value)
# --- Flattened Fields for Response ---
# Sectores
sector1: Optional[str] = None

View File

@@ -39,7 +39,11 @@ class Company(Base, TimestampMixin):
# Programa
program: Mapped[Optional[str]] = mapped_column(String(7))
program_number: Mapped[Optional[str]] = mapped_column(String(40))
prosec: Mapped[Optional[int]] = mapped_column(Integer, nullable=True, default=None)
# prosec: indica si la empresa cuenta con programa PROSEC (sí/no)
prosec: Mapped[bool] = mapped_column(
Boolean, nullable=False, default=False, server_default="false"
)
# prosec_authorization: clave del sector PROSEC (catálogo de sectores)
prosec_authorization: Mapped[Optional[str]] = mapped_column(String(20))
# Sectores