Files
plantillas-proyectos/backend/api/v1/modules/a76/factura_cove/schemas.py
2026-04-26 18:13:20 -06:00

137 lines
4.7 KiB
Python

from __future__ import annotations
from datetime import datetime
from decimal import Decimal
from typing import List, Optional, Dict, Any
from pydantic import BaseModel, EmailStr, Field
class ConfiguracionVU(BaseModel):
"""
Configuración de Ventanilla Única / VUCEM para generación de COVE.
Nota: estos campos se pueden poblar desde CustomsBrokerVU (web_service_user,
web_service_access_key, fiel_access_key, query_tax_id, etc.) o desde el
propio request de la API pública, según el flujo que se implemente.
"""
rfc_usuario_vu: str = Field(..., max_length=30)
# Clave encriptada/token del webservice; puede ser larga (base64)
clave_webservice: str = Field(..., max_length=512)
archivo_cer_base64: str
archivo_key_base64: str
clave_fiel: str = Field(..., max_length=100)
class PersonaCove(BaseModel):
tipo_identificador: str = Field(..., max_length=10)
identificacion: str = Field(..., max_length=30)
apellido_paterno: str = Field(default="", max_length=80)
apellido_materno: str = Field(default="", max_length=80)
nombre: str = Field(default="", max_length=80)
calle: str = Field(default="", max_length=120)
numero_exterior: str = Field(default="", max_length=20)
numero_interior: str = Field(default="", max_length=20)
colonia: str = Field(default="", max_length=120)
localidad: str = Field(default="", max_length=120)
municipio: str = Field(default="", max_length=120)
entidad_federativa: str = Field(default="", max_length=120)
pais: str = Field(..., max_length=3, description="País en formato ISO o catálogo VU")
codigo_postal: str = Field(default="", max_length=15)
class DescripcionEspecifica(BaseModel):
marca: str = Field(default="", max_length=80)
modelo: str = Field(default="", max_length=80)
submodelo: str = Field(default="", max_length=80)
numero_serie: str = Field(default="", max_length=80)
class MercanciaCove(BaseModel):
descripcion_generica: str = Field(..., max_length=500)
clave_unidad_medida: str = Field(..., max_length=10)
tipo_moneda: str = Field(..., max_length=5)
cantidad: Decimal = Field(..., gt=0)
valor_unitario: Decimal = Field(..., ge=0)
valor_total: Decimal = Field(..., ge=0)
valor_dolares: Decimal = Field(default=Decimal("0"), ge=0)
descripcion_especifica: List[DescripcionEspecifica] = Field(default_factory=list)
class FacturaCoveRequest(BaseModel):
"""
Payload completo para generación de COVE.
Este modelo replica el contrato del servicio externo de COVE que se
mostró en la documentación compartida por el usuario.
"""
configuracion_vu: ConfiguracionVU
rfc_consulta: str = Field(..., max_length=30)
tipo_figura: str = Field(..., max_length=10)
numero_factura: str = Field(..., max_length=50)
tipo_operacion: str = Field(..., max_length=10)
patente_aduanal: str = Field(..., max_length=10)
fecha_expedicion: datetime
observaciones: Optional[str] = Field(None, max_length=500)
correo_electronico: Optional[EmailStr] = None
tiene_subdivision: bool = False
certificado_origen: bool = False
numero_exportador_autorizado: Optional[str] = Field(None, max_length=50)
emisor: PersonaCove
destinatario: PersonaCove
mercancias: List[MercanciaCove] = Field(default_factory=list, min_length=1)
class FacturaCoveResponse(BaseModel):
"""
Respuesta base del endpoint público de generación de COVE.
Para el flujo asíncrono interno, solo usamos task_id/status/message.
"""
task_id: Optional[str] = None
status: str
message: Optional[str] = None
class GenerateCoveFromInvoiceRequest(BaseModel):
"""
Request minimalista desde la vista de facturas.
Solo necesita el company_id porque el invoice_id viene en la URL y el
tenant_id se resuelve desde el token.
"""
company_id: int
force_regen: Optional[bool] = False
recipient_email: Optional[EmailStr] = None
class GenerateCoveResult(BaseModel):
"""
Resultado estándar que produce la tarea Celery factura_cove_generate.
"""
status: str
message: Optional[str] = None
invoice_id: Optional[int] = None
cove_number: Optional[str] = None
vucem_operation_num: Optional[str] = None
# ID de tarea devuelto por el servicio externo de COVE (si aplica)
external_task_id: Optional[str] = None
# Respuesta cruda devuelta por el servicio externo (POST generar-factura-cove)
external_response: Optional[Dict[str, Any]] = None
errors: Optional[list[dict]] = None
class CoveEligibilityIssue(BaseModel):
field: str
message: str
class CoveEligibilityResponse(BaseModel):
can_generate: bool
reasons: list[CoveEligibilityIssue] = Field(default_factory=list)