feat(crm): formularios Clientes/Prospectos y Proveedores por catálogo — T2026-07-081/082

Frontend de los catálogos de referencia en BD:
- Store crm-catalogs + cliente API; selects poblados desde /v1/crm/catalogs
  (fiscal: régimen, uso CFDI, forma/método de pago SAT, moneda ISO; comercial;
  país/estado/tipo de domicilio/área). Muestran la descripción, no la clave.
- "Otro → especificar" (medio de contacto y clasificación), observaciones
  generales, y datos de auditoría (ID, fechas, usuarios) en la vista de edición.
- Botón "Editar" explícito en los listados.
- Pantalla de administración de catálogos (insertar/editar/borrar): globales
  solo activar/desactivar; los de la empresa con alta/edición/borrado.
- addresses.country ampliado a 3 (país ISO alfa-3) + migración con down().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ernesto Herrera
2026-07-22 10:31:59 -06:00
parent a2a474f8c8
commit de8557dec2
16 changed files with 1057 additions and 2179 deletions

View File

@@ -0,0 +1,44 @@
"""ampliar crm.addresses.country a 3 (país ISO alfa-3 del catálogo)
Revision ID: e7f8a9b0c1d2
Revises: e6f7a8b9c0d1
Create Date: 2026-07-22 00:30:00.000000
El catálogo de País usa códigos ISO 3166 alfa-3 (MEX, USA, …). La columna
addresses.country era String(2); se amplía a String(3) para almacenarlos.
"""
from typing import Sequence, Union
import sqlalchemy as sa
from alembic import op
revision: str = "e7f8a9b0c1d2"
down_revision: Union[str, None] = "e6f7a8b9c0d1"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
SCHEMA = "crm"
def upgrade() -> None:
op.alter_column(
"addresses", "country",
type_=sa.String(length=3),
existing_type=sa.String(length=2),
existing_nullable=True,
server_default=sa.text("'MEX'"),
schema=SCHEMA,
)
def downgrade() -> None:
# Trunca a 2 chars por si hay códigos alfa-3 guardados (rollback de dev).
op.execute("UPDATE crm.addresses SET country = left(country, 2) WHERE length(country) > 2")
op.alter_column(
"addresses", "country",
type_=sa.String(length=2),
existing_type=sa.String(length=3),
existing_nullable=True,
server_default=sa.text("'MX'"),
schema=SCHEMA,
)

View File

@@ -29,7 +29,8 @@ class Address(Base, TenantScopedMixin, TimestampMixin):
neighborhood: Mapped[str | None] = mapped_column(String(120), nullable=True) # colonia
postal_code: Mapped[str | None] = mapped_column(String(10), nullable=True) # código postal
city: Mapped[str | None] = mapped_column(String(120), nullable=True) # municipio
state: Mapped[str | None] = mapped_column(String(120), nullable=True) # estado
country: Mapped[str | None] = mapped_column(String(2), nullable=True, server_default=text("'MX'"))
state: Mapped[str | None] = mapped_column(String(120), nullable=True) # estado (código catálogo)
# País como código ISO 3166 alfa-3 del catálogo (p. ej. MEX). Ampliado de 2→3.
country: Mapped[str | None] = mapped_column(String(3), nullable=True, server_default=text("'MEX'"))
reference_notes: Mapped[str | None] = mapped_column(Text, nullable=True) # referencias
is_primary: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default=text("false"))

File diff suppressed because it is too large Load Diff