314 lines
12 KiB
Python
314 lines
12 KiB
Python
from pydantic import BaseModel, constr, field_validator
|
|
from fastapi import HTTPException, status
|
|
from typing import List
|
|
from typing import Optional
|
|
from datetime import datetime
|
|
from decimal import Decimal # Importar Decimal para campos monetários
|
|
|
|
# Funções para sanitização de entradas (evitar XSS, SQLi etc.)
|
|
# É importante que esta função seja mantida/implementada no seu ambiente
|
|
# from actions.validations.text import Text # Descomentar se for usar
|
|
|
|
# Schemas dos itens
|
|
from packages.v1.administrativo.schemas.ato_parte_schema import AtoParteSaveSchema
|
|
from packages.v1.administrativo.schemas.ato_documento_schema import (
|
|
AtoDocumentoSaveSchema,
|
|
)
|
|
|
|
|
|
# ----------------------------------------------------
|
|
# Schema Base: Usado para leitura (GET, SHOW, INDEX)
|
|
# Inclui todos os campos, incluindo os gerados pelo banco
|
|
# ----------------------------------------------------
|
|
class AtoPrincipalSchema(BaseModel):
|
|
# DDL: bigint NOT NULL AUTO_INCREMENT
|
|
ato_principal_id: Optional[int] = None
|
|
# DDL: bigint DEFAULT NULL
|
|
origem_ato_principal_id: Optional[int] = None
|
|
# DDL: bigint NOT NULL
|
|
identificacao_pedido_cgj: int
|
|
# DDL: int NOT NULL
|
|
tipo_ato: int
|
|
# DDL: varchar(50) NOT NULL (UNIQUE)
|
|
codigo_selo: constr(max_length=50)
|
|
# DDL: varchar(50) NOT NULL (UNIQUE)
|
|
codigo_ato: constr(max_length=50)
|
|
# DDL: varchar(255) NOT NULL (mas o banco pode conter valores NULL em registros antigos)
|
|
nome_civil_ato: Optional[constr(max_length=255)] = None
|
|
# DDL: varchar(255) NOT NULL (mas o banco pode conter valores NULL em registros antigos)
|
|
nome_serventuario_praticou_ato: Optional[constr(max_length=255)] = None
|
|
# DDL: datetime NOT NULL
|
|
data_solicitacao: datetime
|
|
# DDL: varchar(45) DEFAULT NULL
|
|
ip_maquina: Optional[constr(max_length=45)] = None
|
|
# DDL: text NOT NULL
|
|
inteiro_teor: Optional[str] = None # permite valor nulo
|
|
# DDL: decimal(12,2) DEFAULT NULL
|
|
valor_entrada: Optional[Decimal] = None
|
|
# DDL: decimal(12,2) NOT NULL
|
|
emolumento: Decimal
|
|
# DDL: decimal(12,2) NOT NULL
|
|
taxa_judiciaria: Decimal
|
|
# DDL: decimal(12,2) NOT NULL
|
|
fundos_estaduais: Decimal
|
|
# DDL: varchar(50) DEFAULT NULL
|
|
protocolo_protesto: Optional[constr(max_length=50)] = None
|
|
# DDL: varchar(50) DEFAULT NULL
|
|
protocolo_imovel: Optional[constr(max_length=50)] = None
|
|
# DDL: datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
created_at: Optional[datetime] = None
|
|
# DDL: datetime NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
updated_at: Optional[datetime] = None
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
# Permite a conversão de float/int para Decimal
|
|
json_encoders = {Decimal: lambda v: float(v)}
|
|
# Define a precisão para Decimal
|
|
decimal_places = 2
|
|
|
|
|
|
# ----------------------------------------------------
|
|
# Schema para Requisição de ID: Usado em SHOW e DELETE
|
|
# ----------------------------------------------------
|
|
class AtoPrincipalIdSchema(BaseModel):
|
|
ato_principal_id: int
|
|
|
|
@field_validator("ato_principal_id")
|
|
def validate_ato_principal_id(cls, v: int):
|
|
if v <= 0:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": "ato_principal_id",
|
|
"message": "ID do Ato Principal deve ser um valor positivo.",
|
|
}
|
|
],
|
|
)
|
|
return v
|
|
|
|
|
|
# ----------------------------------------------------
|
|
# Schema para Requisição de Código: Usado em SHOW e DELETE
|
|
# ----------------------------------------------------
|
|
class AtoPrincipalCodigoAtoSchema(BaseModel):
|
|
codigo_ato: str # Campo string equivalente ao varchar(50)
|
|
|
|
@field_validator("codigo_ato")
|
|
def validate_codigo_ato(cls, v: str):
|
|
# Remove espaços extras
|
|
v = v.strip()
|
|
|
|
# Verifica se está vazio
|
|
if not v:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": "codigo_ato",
|
|
"message": "O campo 'código do ato' é obrigatório.",
|
|
}
|
|
],
|
|
)
|
|
|
|
# Verifica o tamanho máximo (equivalente a varchar(50))
|
|
if len(v) > 50:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": "codigo_ato",
|
|
"message": "O campo 'código do ato' deve ter no máximo 50 caracteres.",
|
|
}
|
|
],
|
|
)
|
|
|
|
return v
|
|
|
|
|
|
# ----------------------------------------------------
|
|
# Schema para Criação (SAVE): Campos obrigatórios e sem ID
|
|
# ----------------------------------------------------
|
|
class AtoPrincipalSaveSchema(BaseModel):
|
|
ato_principal_id: Optional[int] = None # <<< tornar opcional
|
|
|
|
# Campos obrigatórios baseados na DDL (NOT NULL)
|
|
origem_ato_principal_id: Optional[int] = None # bigint DEFAULT NULL
|
|
identificacao_pedido_cgj: int # bigint NOT NULL
|
|
tipo_ato: int # int NOT NULL
|
|
codigo_selo: constr(max_length=50) # varchar(50) NOT NULL
|
|
codigo_ato: constr(max_length=50) # varchar(50) NOT NULL
|
|
nome_civil_ato: constr(max_length=255) # varchar(255) NOT NULL
|
|
nome_serventuario_praticou_ato: constr(max_length=255) # varchar(255) NOT NULL
|
|
data_solicitacao: datetime # datetime NOT NULL
|
|
ip_maquina: Optional[constr(max_length=45)] = None # varchar(45) DEFAULT NULL
|
|
inteiro_teor: str # text NOT NULL
|
|
valor_entrada: Optional[Decimal] = None # decimal(12,2) DEFAULT NULL
|
|
emolumento: Decimal # decimal(12,2) NOT NULL
|
|
taxa_judiciaria: Decimal # decimal(12,2) NOT NULL
|
|
fundos_estaduais: Decimal # decimal(12,2) NOT NULL
|
|
protocolo_protesto: Optional[constr(max_length=50)] = (
|
|
None # varchar(50) DEFAULT NULL
|
|
)
|
|
protocolo_imovel: Optional[constr(max_length=50)] = None # varchar(50) DEFAULT NULL
|
|
ato_partes: List[AtoParteSaveSchema] = [] # precisa existir
|
|
ato_documentos: List[AtoDocumentoSaveSchema] = [] # precisa existir
|
|
|
|
# NOVO CAMPO: Referência recursiva a outros atos principais
|
|
# Usando string para forward reference
|
|
atos_vinculados: Optional[List["AtoPrincipalSaveSchema"]] = None
|
|
|
|
# Validação e Sanitização de campos de texto obrigatórios (adaptado do código fonte)
|
|
@field_validator(
|
|
"codigo_selo",
|
|
"codigo_ato",
|
|
"nome_civil_ato",
|
|
"nome_serventuario_praticou_ato",
|
|
)
|
|
def validate_required_strings(cls, v: str):
|
|
v = v.strip()
|
|
if not v:
|
|
# Identifica o campo que falhou a validação
|
|
input_name = (
|
|
"Erro de campo" # Será substituído se a função for mais complexa
|
|
)
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": input_name,
|
|
"message": "Este campo obrigatório não pode ser vazio.",
|
|
}
|
|
],
|
|
)
|
|
# Adicionar aqui a sanitização de texto (ex: Text.sanitize(v)) se disponível
|
|
return v
|
|
|
|
# Validação dos campos monetários (baseado na CHECK CONSTRAINT da DDL)
|
|
@field_validator(
|
|
"valor_entrada", "emolumento", "taxa_judiciaria", "fundos_estaduais"
|
|
)
|
|
def validate_positive_values(cls, v: Optional[Decimal]):
|
|
if v is None and (
|
|
"valor_entrada" in cls.__annotations__
|
|
and cls.__annotations__["valor_entrada"] == Optional[Decimal]
|
|
):
|
|
# valor_entrada é o único campo DEFAULT NULL, aceita None
|
|
return v
|
|
if v is not None and v < 0:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": "campo_monetario",
|
|
"message": "Os valores monetários não podem ser negativos.",
|
|
}
|
|
],
|
|
)
|
|
return v
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
json_encoders = {Decimal: lambda v: float(v)}
|
|
decimal_places = 2
|
|
|
|
|
|
# ----------------------------------------------------
|
|
# Schema para Atualização (UPDATE): Todos opcionais (parciais)
|
|
# ----------------------------------------------------
|
|
class AtoPrincipalUpdateSchema(BaseModel):
|
|
# Todos os campos que podem ser atualizados são opcionais
|
|
origem_ato_principal_id: Optional[int] = None
|
|
identificacao_pedido_cgj: Optional[int] = None
|
|
tipo_ato: Optional[int] = None
|
|
codigo_selo: Optional[constr(max_length=50)] = None
|
|
codigo_ato: Optional[constr(max_length=50)] = None
|
|
nome_civil_ato: Optional[constr(max_length=255)] = None
|
|
nome_serventuario_praticou_ato: Optional[constr(max_length=255)] = None
|
|
data_solicitacao: Optional[datetime] = None
|
|
ip_maquina: Optional[constr(max_length=45)] = None
|
|
inteiro_teor: Optional[str] = None
|
|
valor_entrada: Optional[Decimal] = None
|
|
emolumento: Optional[Decimal] = None
|
|
taxa_judiciaria: Optional[Decimal] = None
|
|
fundos_estaduais: Optional[Decimal] = None
|
|
protocolo_protesto: Optional[constr(max_length=50)] = None
|
|
protocolo_imovel: Optional[constr(max_length=50)] = None
|
|
|
|
# Reutiliza a validação de strings obrigatórias (só valida se o campo for fornecido)
|
|
@field_validator(
|
|
"codigo_selo",
|
|
"codigo_ato",
|
|
"nome_civil_ato",
|
|
"nome_serventuario_praticou_ato",
|
|
"inteiro_teor",
|
|
)
|
|
def validate_non_empty_strings_on_update(cls, v: Optional[str]):
|
|
if v is not None:
|
|
v_stripped = v.strip()
|
|
if not v_stripped:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": "campo_de_texto",
|
|
"message": "Este campo não pode ser atualizado para um valor vazio.",
|
|
}
|
|
],
|
|
)
|
|
return v
|
|
|
|
# Reutiliza a validação de valores positivos
|
|
@field_validator(
|
|
"valor_entrada", "emolumento", "taxa_judiciaria", "fundos_estaduais"
|
|
)
|
|
def validate_positive_values_on_update(cls, v: Optional[Decimal]):
|
|
if v is not None and v < 0:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": "campo_monetario",
|
|
"message": "Os valores monetários não podem ser negativos.",
|
|
}
|
|
],
|
|
)
|
|
return v
|
|
|
|
class Config:
|
|
from_attributes = True
|
|
json_encoders = {Decimal: lambda v: float(v)}
|
|
decimal_places = 2
|
|
|
|
|
|
# ----------------------------------------------------
|
|
# Schema auxiliar para buscar por campos únicos (opcional, mas útil para o Save Service)
|
|
# ----------------------------------------------------
|
|
class AtoPrincipalCodigoSchema(BaseModel):
|
|
codigo: constr(max_length=50) # Pode ser codigo_selo ou codigo_ato
|
|
|
|
@field_validator("codigo")
|
|
def validate_codigo(cls, v: str):
|
|
v = v.strip()
|
|
if not v:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
|
detail=[
|
|
{
|
|
"input": "codigo",
|
|
"message": "O código de busca não pode ser vazio.",
|
|
}
|
|
],
|
|
)
|
|
return v
|
|
|
|
|
|
# --- ADIÇÃO CRÍTICA PARA RECURSIVIDADE (Resolução da forward reference) ---
|
|
# Necessário para resolver a referência da classe dentro de si mesma.
|
|
# Tenta Pydantic v2 (model_rebuild) e, se falhar, Pydantic v1 (update_forward_refs).
|
|
try:
|
|
AtoPrincipalSaveSchema.model_rebuild()
|
|
except AttributeError:
|
|
# Se estiver usando Pydantic v1
|
|
AtoPrincipalSaveSchema.update_forward_refs()
|