134 lines
4.5 KiB
Python
134 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class ServicioCotizadoInput(BaseModel):
|
|
catalogoId: str = Field(..., min_length=1, description="ID del servicio del catálogo")
|
|
nombre: str = Field(..., min_length=1, description="Nombre del servicio")
|
|
fase: int = Field(..., ge=0, le=3)
|
|
tipoPago: str = Field(..., pattern="^(unico|mensual)$")
|
|
precio: float = Field(..., ge=0, description="Precio (puede diferir del precioBase)")
|
|
tiempoEntrega: str
|
|
entregables: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class PlanBucefaloInput(BaseModel):
|
|
nivel: str = Field(..., description="Nivel: basico, estandar, premium, empresarial")
|
|
precio: float = Field(..., ge=0)
|
|
|
|
|
|
class ClienteInput(BaseModel):
|
|
nombre: str = Field(..., min_length=1)
|
|
empresa: str = ""
|
|
email: str = ""
|
|
telefono: str = ""
|
|
|
|
|
|
class CotizacionCreate(BaseModel):
|
|
numero: str = Field(..., min_length=1, description="Número de cotización (formato UJ{YY}{MM}{init}{seq})")
|
|
fecha: datetime
|
|
vigencia: datetime
|
|
moneda: str = Field("MXN", pattern="^(MXN|USD)$")
|
|
tipoCambio: str = "NA"
|
|
proyecto: str = "MKT Digital"
|
|
esquemaPago: str = Field("Pago Unico/Mensual", pattern="^(Pago Unico|Mensual|Pago Unico/Mensual)$")
|
|
incluirBonos: bool = False
|
|
incluirFinanciamiento: bool = False
|
|
observaciones: str = ""
|
|
asesorId: str = Field(..., min_length=1)
|
|
cliente: ClienteInput
|
|
servicios: list[ServicioCotizadoInput] = Field(..., min_length=1)
|
|
planBucefalo: PlanBucefaloInput | None = None
|
|
|
|
model_config = {
|
|
"json_schema_extra": {
|
|
"examples": [
|
|
{
|
|
"numero": "UJ2605AG001",
|
|
"fecha": "2026-05-03",
|
|
"vigencia": "2026-05-24",
|
|
"moneda": "MXN",
|
|
"tipoCambio": "NA",
|
|
"proyecto": "MKT Digital",
|
|
"esquemaPago": "Pago Unico/Mensual",
|
|
"incluirBonos": False,
|
|
"incluirFinanciamiento": False,
|
|
"observaciones": "",
|
|
"asesorId": "cuid-asesor",
|
|
"cliente": {
|
|
"nombre": "Juan Pérez",
|
|
"empresa": "ACME Corp",
|
|
"email": "[email protected]",
|
|
"telefono": "4421234567",
|
|
},
|
|
"servicios": [
|
|
{
|
|
"catalogoId": "cuid-servicio",
|
|
"nombre": "SEO On-Page",
|
|
"fase": 3,
|
|
"tipoPago": "mensual",
|
|
"precio": 2900,
|
|
"tiempoEntrega": "7 - 14 dias",
|
|
"entregables": ["Keyword research"],
|
|
}
|
|
],
|
|
"planBucefalo": {"nivel": "basico", "precio": 1000},
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
class CotizacionUpdate(BaseModel):
|
|
fecha: datetime | None = None
|
|
vigencia: datetime | None = None
|
|
moneda: str | None = Field(None, pattern="^(MXN|USD)$")
|
|
tipoCambio: str | None = None
|
|
proyecto: str | None = None
|
|
esquemaPago: str | None = Field(None, pattern="^(Pago Unico|Mensual|Pago Unico/Mensual)$")
|
|
incluirBonos: bool | None = None
|
|
incluirFinanciamiento: bool | None = None
|
|
observaciones: str | None = None
|
|
estado: str | None = Field(None, pattern="^(borrador|enviada|aprobada|rechazada)$")
|
|
cliente: ClienteInput | None = None
|
|
servicios: list[ServicioCotizadoInput] | None = None
|
|
planBucefalo: PlanBucefaloInput | None = None
|
|
|
|
|
|
class CambiarEstadoRequest(BaseModel):
|
|
estado: str = Field(..., pattern="^(borrador|enviada|aprobada|rechazada)$")
|
|
|
|
|
|
class ActualizarPrecioRequest(BaseModel):
|
|
servicioId: str = Field(..., min_length=1)
|
|
precio: float = Field(..., ge=0)
|
|
|
|
|
|
class CotizacionResponse(BaseModel):
|
|
id: str
|
|
numero: str
|
|
fecha: datetime
|
|
vigencia: datetime
|
|
moneda: str
|
|
tipoCambio: str
|
|
proyecto: str
|
|
esquemaPago: str
|
|
estado: str
|
|
incluirBonos: bool
|
|
incluirFinanciamiento: bool
|
|
observaciones: str | None
|
|
clienteId: str
|
|
asesorId: str
|
|
createdAt: datetime
|
|
updatedAt: datetime
|
|
cliente: dict[str, Any] | None = None
|
|
asesor: dict[str, Any] | None = None
|
|
servicios: list[dict[str, Any]] | None = None
|
|
planBucefalo: dict[str, Any] | None = None
|
|
|
|
model_config = {"from_attributes": True}
|