Files
CRM_AGENTES_CARGA/backend/api/v1/modules/crm/accounts/dto.py
Aduanasoft 3b8da8b4cc fix(crm): correcciones de revisión adversarial en servicios de dominio
- convert_lead valida embudo/etapa en el scope tenant/company (evita fuga multi-tenant y 500)
- oportunidades: estado/probabilidad/cierre se derivan de la etapa también en create/update (no solo move)
- oportunidades: valida que la etapa pertenezca al embudo indicado
- cuentas: country por defecto 'MX' (el server_default no aplicaba con NULL explícito)
- convert_lead: contact_name se normaliza (evita first_name vacío)
- +4 tests que fijan el comportamiento corregido (28 en verde)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 09:53:56 -06:00

70 lines
2.4 KiB
Python

from datetime import datetime
from pydantic import BaseModel, ConfigDict, EmailStr, Field
class AccountCreate(BaseModel):
name: str = Field(..., min_length=1, max_length=255)
trade_name: str | None = Field(None, max_length=255)
rfc: str | None = Field(None, max_length=13)
account_type: str | None = Field(None, max_length=40)
industry: str | None = Field(None, max_length=120)
email: EmailStr | None = None
phone: str | None = Field(None, max_length=40)
website: str | None = Field(None, max_length=255)
address: str | None = None
city: str | None = Field(None, max_length=120)
state: str | None = Field(None, max_length=120)
# País por defecto MX (CRM aduanero). Evita depender del server_default,
# que no aplica cuando model_dump envía la columna como NULL explícito.
country: str | None = Field("MX", max_length=2)
patente_aduanal: str | None = Field(None, max_length=20)
status: str = Field("active", max_length=20)
owner_user_id: str | None = Field(None, max_length=64)
notes: str | None = None
class AccountUpdate(BaseModel):
name: str | None = Field(None, min_length=1, max_length=255)
trade_name: str | None = Field(None, max_length=255)
rfc: str | None = Field(None, max_length=13)
account_type: str | None = Field(None, max_length=40)
industry: str | None = Field(None, max_length=120)
email: EmailStr | None = None
phone: str | None = Field(None, max_length=40)
website: str | None = Field(None, max_length=255)
address: str | None = None
city: str | None = Field(None, max_length=120)
state: str | None = Field(None, max_length=120)
country: str | None = Field(None, max_length=2)
patente_aduanal: str | None = Field(None, max_length=20)
status: str | None = Field(None, max_length=20)
owner_user_id: str | None = Field(None, max_length=64)
notes: str | None = None
class AccountResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
name: str
trade_name: str | None
rfc: str | None
account_type: str | None
industry: str | None
email: str | None
phone: str | None
website: str | None
address: str | None
city: str | None
state: str | None
country: str | None
patente_aduanal: str | None
status: str
owner_user_id: str | None
notes: str | None
tenant_id: int
company_id: int
created_at: datetime
updated_at: datetime