feat(OnlyOffice): Ajustes diversos no editor de texto
This commit is contained in:
parent
6ccf74e687
commit
1e34ce9e9a
12 changed files with 304 additions and 77 deletions
|
|
@ -278,4 +278,4 @@ gunicorn main:app \
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
uvicorn main:app --host 0.0.0.0 --port 8000
|
uvicorn main:app --host 0.0.0.0 --port 8000 --reload
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
# Importa a biblioteca nativa 'zlib' usada para descompressão de dados binários.
|
# Importa a biblioteca nativa 'zlib' usada para compressão/descompressão de dados binários.
|
||||||
import base64
|
|
||||||
import zlib
|
import zlib
|
||||||
|
|
||||||
# Importa a função 'rtf_to_text' da biblioteca 'striprtf',
|
# Importa a função 'rtf_to_text' da biblioteca 'striprtf',
|
||||||
|
|
@ -7,7 +6,7 @@ import zlib
|
||||||
from striprtf.striprtf import rtf_to_text
|
from striprtf.striprtf import rtf_to_text
|
||||||
|
|
||||||
|
|
||||||
# Define uma classe utilitária chamada 'String', contendo apenas métodos estáticos.
|
# Define uma classe utilitária chamada 'Text', contendo apenas métodos estáticos.
|
||||||
# Essa abordagem permite o uso direto sem necessidade de instanciar a classe.
|
# Essa abordagem permite o uso direto sem necessidade de instanciar a classe.
|
||||||
class Text:
|
class Text:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|
@ -23,71 +22,56 @@ class Text:
|
||||||
- Representado como bytes puros
|
- Representado como bytes puros
|
||||||
"""
|
"""
|
||||||
# Verifica se o valor recebido é nulo, vazio ou None.
|
# Verifica se o valor recebido é nulo, vazio ou None.
|
||||||
# Se for, retorna string vazia para evitar erros de processamento.
|
|
||||||
if not vf_string:
|
if not vf_string:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Caso seja um objeto tipo stream (ex: campo BLOB do Firebird)
|
# Caso seja um objeto tipo stream (ex: campo BLOB do Firebird)
|
||||||
# Campos BLOB geralmente possuem o método `.read()` para leitura de bytes.
|
|
||||||
if hasattr(vf_string, "read"):
|
if hasattr(vf_string, "read"):
|
||||||
vf_string = vf_string.read() # Lê o conteúdo completo do stream
|
vf_string = vf_string.read()
|
||||||
|
|
||||||
# Garante que o valor trabalhado é uma sequência de bytes (não string)
|
# Garante que o valor trabalhado é uma sequência de bytes
|
||||||
# Se o dado já for texto (str), converte para bytes em codificação Latin-1,
|
|
||||||
# que é compatível com ISO-8859-1 usado por sistemas Delphi/Firebird.
|
|
||||||
if isinstance(vf_string, str):
|
if isinstance(vf_string, str):
|
||||||
vf_bytes = vf_string.encode("latin1", errors="ignore")
|
vf_bytes = vf_string.encode("latin1", errors="ignore")
|
||||||
else:
|
else:
|
||||||
vf_bytes = vf_string # Já está em bytes, então apenas reaproveita
|
vf_bytes = vf_string
|
||||||
|
|
||||||
# Detecta se o conteúdo foi compactado com zlib.
|
# Detecta assinatura zlib (0x78 0x9C ou 0x78 0xDA)
|
||||||
# A assinatura padrão do formato zlib começa com bytes: 0x78 0x9C ou 0x78 0xDA.
|
|
||||||
is_zlib = (
|
is_zlib = (
|
||||||
len(vf_bytes) > 2 and vf_bytes[0] == 0x78 and vf_bytes[1] in (0x9C, 0xDA)
|
len(vf_bytes) > 2 and vf_bytes[0] == 0x78 and vf_bytes[1] in (0x9C, 0xDA)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Se a detecção confirmar que o conteúdo é zlib, tenta descompactar.
|
# Se for zlib, tenta descompactar
|
||||||
if is_zlib:
|
if is_zlib:
|
||||||
try:
|
try:
|
||||||
# Descompacta os bytes e decodifica o texto usando ISO-8859-1 (ANSI),
|
return zlib.decompress(vf_bytes).decode("iso-8859-1", errors="ignore")
|
||||||
# que preserva corretamente acentuação e caracteres especiais.
|
|
||||||
text = zlib.decompress(vf_bytes).decode("iso-8859-1", errors="ignore")
|
|
||||||
return text
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Caso falhe (por dados corrompidos ou não comprimidos de fato),
|
|
||||||
# o fluxo continua normalmente sem interromper a execução.
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Se não for zlib, tenta tratar o conteúdo como texto puro (não compactado)
|
# Caso não seja zlib, trata como texto puro
|
||||||
try:
|
try:
|
||||||
# Decodifica os bytes diretamente de ISO-8859-1 (padrão usado pelo Delphi)
|
|
||||||
return vf_bytes.decode("iso-8859-1", errors="ignore")
|
return vf_bytes.decode("iso-8859-1", errors="ignore")
|
||||||
except Exception:
|
except Exception:
|
||||||
# Como fallback, converte para string bruta para evitar falhas.
|
|
||||||
return str(vf_string)
|
return str(vf_string)
|
||||||
|
|
||||||
# >>> NOVO MÉTODO <<<
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def compress(text, *, encoding: str = "iso-8859-1", as_base64: bool = True):
|
def compress(text, *, encoding: str = "iso-8859-1"):
|
||||||
"""
|
"""
|
||||||
Comprime texto/dados com zlib.
|
Comprime texto/dados com zlib SEM Base64.
|
||||||
|
|
||||||
Parâmetros:
|
Parâmetros:
|
||||||
text: str | bytes | stream (com .read())
|
text: str | bytes | stream (com .read())
|
||||||
encoding: encoding usado quando 'text' for str (padrão: ISO-8859-1)
|
encoding: encoding usado quando 'text' for str (padrão: ISO-8859-1)
|
||||||
as_base64: se True, retorna string Base64 do conteúdo comprimido;
|
|
||||||
caso False, retorna bytes comprimidos.
|
|
||||||
|
|
||||||
Retorno:
|
Retorno:
|
||||||
- bytes (zlib) quando as_base64=False
|
- bytes comprimidos (zlib)
|
||||||
- str (Base64) quando as_base64=True
|
|
||||||
|
|
||||||
Observações:
|
Observações:
|
||||||
- Use o mesmo 'encoding' ao descomprimir para simetria.
|
- Ideal para armazenamento direto em BLOB (Firebird, PostgreSQL, etc.)
|
||||||
- Ideal para armazenar em BLOB ou trafegar seguro (Base64).
|
- Evita overhead e custo do Base64
|
||||||
|
- Totalmente compatível com 'decompress'
|
||||||
"""
|
"""
|
||||||
if text is None or text == "":
|
if text is None or text == "":
|
||||||
return "" if as_base64 else b""
|
return b""
|
||||||
|
|
||||||
# Se for stream (ex.: BLOB do Firebird)
|
# Se for stream (ex.: BLOB do Firebird)
|
||||||
if hasattr(text, "read"):
|
if hasattr(text, "read"):
|
||||||
|
|
@ -101,14 +85,8 @@ class Text:
|
||||||
else:
|
else:
|
||||||
raw_bytes = bytes(raw)
|
raw_bytes = bytes(raw)
|
||||||
|
|
||||||
# Comprime com zlib
|
# Comprime com zlib e retorna bytes
|
||||||
comp = zlib.compress(raw_bytes)
|
return zlib.compress(raw_bytes)
|
||||||
|
|
||||||
# Opcional: codifica em Base64 para transporte/JSON
|
|
||||||
if as_base64:
|
|
||||||
return base64.b64encode(comp).decode("ascii")
|
|
||||||
|
|
||||||
return comp
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def to_text(raw_text: str) -> str:
|
def to_text(raw_text: str) -> str:
|
||||||
|
|
@ -117,25 +95,17 @@ class Text:
|
||||||
|
|
||||||
Finalidade:
|
Finalidade:
|
||||||
- Detectar automaticamente se o conteúdo está em formato RTF.
|
- Detectar automaticamente se o conteúdo está em formato RTF.
|
||||||
- Converter para texto plano usando a função 'rtf_to_text' (da striprtf).
|
- Converter para texto plano usando a função 'rtf_to_text'.
|
||||||
- Retornar uma string limpa e pronta para ser usada em APIs, logs, etc.
|
- Retornar uma string limpa e pronta para uso.
|
||||||
"""
|
"""
|
||||||
# Verifica se o texto recebido está vazio ou None — retorna vazio se sim.
|
|
||||||
if not raw_text:
|
if not raw_text:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
# Verifica se o texto começa com o cabeçalho padrão de arquivos RTF.
|
# Detecta cabeçalho RTF
|
||||||
# Exemplo: "{\\rtf1\\ansi..." indica conteúdo em formato RTF.
|
|
||||||
if raw_text.strip().startswith("{\\rtf"):
|
if raw_text.strip().startswith("{\\rtf"):
|
||||||
try:
|
try:
|
||||||
# Converte o RTF em texto simples, preservando acentuação e quebras de linha.
|
return rtf_to_text(raw_text).strip()
|
||||||
text = rtf_to_text(raw_text)
|
|
||||||
# Remove espaços em branco extras nas extremidades.
|
|
||||||
return text.strip()
|
|
||||||
except Exception:
|
except Exception:
|
||||||
# Se ocorrer erro na conversão (ex: RTF inválido),
|
|
||||||
# retorna o conteúdo original sem alterações.
|
|
||||||
return raw_text
|
return raw_text
|
||||||
|
|
||||||
# Caso o texto não seja RTF, apenas remove espaços em branco extras e retorna.
|
|
||||||
return raw_text.strip()
|
return raw_text.strip()
|
||||||
|
|
|
||||||
|
|
@ -5,32 +5,30 @@ from pytz import timezone
|
||||||
from abstracts.action import BaseAction
|
from abstracts.action import BaseAction
|
||||||
from actions.config.config import Config
|
from actions.config.config import Config
|
||||||
|
|
||||||
|
|
||||||
class CreateToken(BaseAction):
|
class CreateToken(BaseAction):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
# Busca as configurações da aplicação
|
# Busca as configurações da aplicação
|
||||||
self.config = Config.get('app.json')
|
self.config = Config.get("app.json")
|
||||||
|
|
||||||
# Cria o timedelta com base na config
|
# Cria o timedelta com base na config
|
||||||
self.access_token_expire = timedelta(
|
self.access_token_expire = timedelta(
|
||||||
minutes=self.config.jwt.expire.minute,
|
minutes=self.config.jwt.expire.minute,
|
||||||
hours=self.config.jwt.expire.hours,
|
hours=self.config.jwt.expire.hours,
|
||||||
days=self.config.jwt.expire.days
|
days=self.config.jwt.expire.days,
|
||||||
)
|
)
|
||||||
|
|
||||||
def execute(self, tipo_token: str, data : str) -> str:
|
def execute(self, tipo_token: str, data: str) -> str:
|
||||||
|
|
||||||
sp = timezone('America/Sao_Paulo')
|
sp = timezone("America/Sao_Paulo")
|
||||||
agora = datetime.now(tz=sp)
|
agora = datetime.now(tz=sp)
|
||||||
expira = agora + self.access_token_expire
|
expira = agora + self.access_token_expire
|
||||||
|
|
||||||
# Define os dados do token
|
# Define os dados do token
|
||||||
payload = {
|
payload = {"type": tipo_token, "exp": expira, "iat": agora, "data": str(data)}
|
||||||
'type' : tipo_token,
|
|
||||||
'exp' : expira,
|
|
||||||
'iat' : agora,
|
|
||||||
'data' : str(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Retorna os dados codificados
|
# Retorna os dados codificados
|
||||||
return jwt.encode(payload, self.config.jwt.token, algorithm=self.config.jwt.algorithm)
|
return jwt.encode(
|
||||||
|
payload, self.config.jwt.token, algorithm=self.config.jwt.algorithm
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
from abstracts.action import BaseAction
|
||||||
|
from packages.v1.servicos.balcao.repositories.t_servico_itempedido.t_servico_itempedido_certidao_save_repository import (
|
||||||
|
TServicoItemPedidoCertidaoSaveRepository,
|
||||||
|
)
|
||||||
|
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
||||||
|
TServicoItemPedidoCertidaoSaveSchema,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TServicoItemPedidoCertidaSaveAction(BaseAction):
|
||||||
|
"""
|
||||||
|
Serviço responsável por encapsular a lógica de negócio para a operação
|
||||||
|
de salvamento de um novo registro na tabela T_SERVICO_ITEMPEDIDO.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def execute(self, data: TServicoItemPedidoCertidaoSaveSchema):
|
||||||
|
"""
|
||||||
|
Executa a operação de salvamento.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t_servico_itempedido_schema (TServicoItemPedidoSchema):
|
||||||
|
O esquema com os dados a serem persistidos.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
O resultado da operação de salvamento.
|
||||||
|
"""
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Instanciamento do repositório
|
||||||
|
# ----------------------------------------------------
|
||||||
|
save_repository = TServicoItemPedidoCertidaoSaveRepository()
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Execução do repositório
|
||||||
|
# ----------------------------------------------------
|
||||||
|
response = save_repository.execute(data)
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Retorno da informação
|
||||||
|
# ----------------------------------------------------
|
||||||
|
return response
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
from docx import Document
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
from abstracts.action import BaseAction
|
from abstracts.action import BaseAction
|
||||||
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
||||||
|
|
@ -18,8 +19,13 @@ class TServicoItemPedidoCreateCertidaoAction(BaseAction):
|
||||||
|
|
||||||
def execute(self, data: TServicoItemPedidoCreateCertidaoSchema):
|
def execute(self, data: TServicoItemPedidoCreateCertidaoSchema):
|
||||||
|
|
||||||
# 1. Decodifica o texto
|
texto = None
|
||||||
texto = Text.decompress(data.certidao_texto)
|
|
||||||
|
# Verifica se existe conteudo a ser escrito
|
||||||
|
if data.certidao_texto:
|
||||||
|
|
||||||
|
# 1. Decodifica o texto
|
||||||
|
texto = Text.decompress(data.certidao_texto)
|
||||||
|
|
||||||
# 2. Configuração do caminho e nome
|
# 2. Configuração do caminho e nome
|
||||||
diretorio = "./storage/temp/"
|
diretorio = "./storage/temp/"
|
||||||
|
|
@ -31,9 +37,18 @@ class TServicoItemPedidoCreateCertidaoAction(BaseAction):
|
||||||
# 3. Garante que a pasta existe
|
# 3. Garante que a pasta existe
|
||||||
os.makedirs(diretorio, exist_ok=True)
|
os.makedirs(diretorio, exist_ok=True)
|
||||||
|
|
||||||
# 4. Escreve o texto em disco
|
if texto:
|
||||||
with open(caminho_completo, "wb") as f:
|
# 4. Escreve o texto em disco
|
||||||
f.write(texto.encode("utf-8"))
|
with open(caminho_completo, "wb") as f:
|
||||||
|
f.write(texto.encode("utf-8"))
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# 1. Instancia o objeto (cria a estrutura XML em memória)
|
||||||
|
document = Document()
|
||||||
|
|
||||||
|
# 2. Salva o arquivo fisicamente
|
||||||
|
document.save(caminho_completo)
|
||||||
|
|
||||||
# 5. VALIDAÇÃO: Verifica se o arquivo existe e se tem conteúdo
|
# 5. VALIDAÇÃO: Verifica se o arquivo existe e se tem conteúdo
|
||||||
if not os.path.exists(caminho_completo):
|
if not os.path.exists(caminho_completo):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
from actions.dynamic_import.dynamic_import import DynamicImport
|
from actions.dynamic_import.dynamic_import import DynamicImport
|
||||||
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
||||||
TServicoItemIndexSchema,
|
TServicoItemIndexSchema,
|
||||||
|
TServicoItemPedidoCertidaoSaveSchema,
|
||||||
TServicoItemPedidoSaveSchema,
|
TServicoItemPedidoSaveSchema,
|
||||||
TServicoItemPedidoSituacaoSchema,
|
TServicoItemPedidoSituacaoSchema,
|
||||||
TServicoItemPedidoUpdateSchema,
|
TServicoItemPedidoUpdateSchema,
|
||||||
|
|
@ -99,6 +100,23 @@ class TServicoItemPedidoController:
|
||||||
"data": self.show_service.execute(t_servico_itempedido_id_schema),
|
"data": self.show_service.execute(t_servico_itempedido_id_schema),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Busca um registro específico de T_SERVICO_ITEMPEDIDO pelo ID
|
||||||
|
# ----------------------------------------------------
|
||||||
|
def certidao_save(self, data: TServicoItemPedidoCertidaoSaveSchema):
|
||||||
|
|
||||||
|
# Importação da classe desejada
|
||||||
|
save_service = self.dynamic_import.service(
|
||||||
|
"t_servico_itempedido_certidao_save_service",
|
||||||
|
"TServicoItemPedidoCertidaoSaveService",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Instância da classe service
|
||||||
|
self.save_service = save_service()
|
||||||
|
|
||||||
|
# Execução da busca
|
||||||
|
return self.save_service.execute(data)
|
||||||
|
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# Cadastra um novo registro em T_SERVICO_ITEMPEDIDO
|
# Cadastra um novo registro em T_SERVICO_ITEMPEDIDO
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Importação de bibliotecas
|
# Importação de bibliotecas
|
||||||
from fastapi import APIRouter, Depends, status
|
from fastapi import APIRouter, Depends, status, Request
|
||||||
from actions.data.dict_to_namespace import dict_to_namespace
|
from actions.data.dict_to_namespace import dict_to_namespace
|
||||||
from actions.jwt.get_current_user import get_current_user
|
from actions.jwt.get_current_user import get_current_user
|
||||||
from packages.v1.servicos.balcao.controllers.t_servico_itempedido_controller import (
|
from packages.v1.servicos.balcao.controllers.t_servico_itempedido_controller import (
|
||||||
|
|
@ -7,6 +7,7 @@ from packages.v1.servicos.balcao.controllers.t_servico_itempedido_controller imp
|
||||||
)
|
)
|
||||||
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
||||||
TServicoItemIndexSchema,
|
TServicoItemIndexSchema,
|
||||||
|
TServicoItemPedidoCertidaoSaveSchema,
|
||||||
TServicoItemPedidoSaveSchema,
|
TServicoItemPedidoSaveSchema,
|
||||||
TServicoItemPedidoSituacaoSchema,
|
TServicoItemPedidoSituacaoSchema,
|
||||||
TServicoItemPedidoUpdateSchema,
|
TServicoItemPedidoUpdateSchema,
|
||||||
|
|
@ -210,6 +211,30 @@ async def ativar(
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Gera o arquivo em disco para Manipulação
|
||||||
|
# ----------------------------------------------------
|
||||||
|
@router.post(
|
||||||
|
"/{servico_itempedido_id}/certidao/salvar",
|
||||||
|
status_code=status.HTTP_200_OK,
|
||||||
|
summary="Atualiza o texto da certidão",
|
||||||
|
response_description="Atualiza o texto da certidão",
|
||||||
|
)
|
||||||
|
async def certidao_save(servico_itempedido_id: int, request: Request):
|
||||||
|
|
||||||
|
# Obtem os dados enviados
|
||||||
|
data = await request.json()
|
||||||
|
|
||||||
|
response = t_servico_itempedido_controller.certidao_save(
|
||||||
|
TServicoItemPedidoCertidaoSaveSchema(
|
||||||
|
servico_itempedido_id=servico_itempedido_id, data=data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# return response
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
# Exclui um registro de T_SERVICO_ITEMPEDIDO
|
# Exclui um registro de T_SERVICO_ITEMPEDIDO
|
||||||
# ----------------------------------------------------
|
# ----------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -18,16 +18,19 @@ class TAtoIndexRepository(BaseRepository):
|
||||||
# Montagem do SQL
|
# Montagem do SQL
|
||||||
sql = """ SELECT
|
sql = """ SELECT
|
||||||
TA.ATO_ID,
|
TA.ATO_ID,
|
||||||
TA.PROTOCOLO ,
|
TA.PROTOCOLO,
|
||||||
|
TAT.DESCRICAO,
|
||||||
TA.DATA_LAVRATURA,
|
TA.DATA_LAVRATURA,
|
||||||
|
TA.DATA_ABERTURA,
|
||||||
TLA.NUMERO_LIVRO,
|
TLA.NUMERO_LIVRO,
|
||||||
TA.FOLHA_INICIAL ,
|
TA.FOLHA_INICIAL,
|
||||||
TA.FOLHA_FINAL
|
TA.FOLHA_FINAL
|
||||||
FROM
|
FROM
|
||||||
T_ATO TA
|
T_ATO TA
|
||||||
JOIN T_LIVRO_ANDAMENTO tla ON TA.LIVRO_ANDAMENTO_ID = TLA.LIVRO_ANDAMENTO_ID
|
JOIN T_LIVRO_ANDAMENTO tla ON TA.LIVRO_ANDAMENTO_ID = TLA.LIVRO_ANDAMENTO_ID
|
||||||
|
JOIN T_ATO_TIPO TAT ON TA.ATO_TIPO_ID = TAT.ATO_TIPO_ID
|
||||||
ORDER BY
|
ORDER BY
|
||||||
TA.ATO_ID ASC """
|
TA.ATO_ID DESC """
|
||||||
|
|
||||||
# Execução do sql
|
# Execução do sql
|
||||||
response = self.fetch_all(sql)
|
response = self.fetch_all(sql)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
from abstracts.repository import BaseRepository
|
||||||
|
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
||||||
|
TServicoItemPedidoCertidaoSaveSchema,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TServicoItemPedidoCertidaoSaveRepository(BaseRepository):
|
||||||
|
"""
|
||||||
|
Repositório responsável pela operação de salvamento de um novo registro
|
||||||
|
na tabela T_SERVICO_ITEMPEDIDO.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def execute(self, data: TServicoItemPedidoCertidaoSaveSchema):
|
||||||
|
"""
|
||||||
|
Executa a operação de salvamento no banco de dados.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
t_servico_itempedido_save_schema (TServicoItemPedidoSchema): O esquema com os dados a serem salvos.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
O registro recém-criado.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPException: Caso ocorra um erro na execução da query.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Preenchimento dos parâmetros
|
||||||
|
# ----------------------------------------------------
|
||||||
|
params = data.model_dump(exclude_unset=True)
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Montagem do SQL dinâmico
|
||||||
|
# ----------------------------------------------------
|
||||||
|
sql = "UPDATE T_SERVICO_ITEMPEDIDO TSI SET TSI.CERTIDAO_TEXTO = :certidao_texto WHERE TSI.SERVICO_ITEMPEDIDO_ID = :servico_itempedido_id RETURNING *"
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Execução do SQL e retorno do registro
|
||||||
|
# ----------------------------------------------------
|
||||||
|
return self.run_and_return(sql, params)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# Tratamento de erros e lançamento de exceção HTTP
|
||||||
|
# ----------------------------------------------------
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail=f"Erro ao salvar registro em T_SERVICO_ITEMPEDIDO: {e}",
|
||||||
|
)
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from typing import Optional
|
from typing import Any, Optional
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
@ -109,6 +109,16 @@ class TServicoItemPedidoSaveSituacaoSchema(BaseModel):
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
|
class TServicoItemPedidoCertidaoSaveSchema(BaseModel):
|
||||||
|
|
||||||
|
servico_itempedido_id: int = None
|
||||||
|
data: object
|
||||||
|
certidao_texto: Optional[bytes] = None
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
from_attributes = True
|
||||||
|
|
||||||
|
|
||||||
class TServicoItemPedidoSaveSchema(TServicoItemPedidoSchema):
|
class TServicoItemPedidoSaveSchema(TServicoItemPedidoSchema):
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
|
|
@ -140,7 +150,7 @@ class TServicoItemPedidoSaveSchema_(TServicoItemPedidoSchema):
|
||||||
class TServicoItemPedidoCreateCertidaoSchema(BaseModel):
|
class TServicoItemPedidoCreateCertidaoSchema(BaseModel):
|
||||||
|
|
||||||
servico_itempedido_id: int
|
servico_itempedido_id: int
|
||||||
certidao_texto: bytes
|
certidao_texto: Optional[Any] = None
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
from_attributes = True
|
from_attributes = True
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
from fastapi import HTTPException, status
|
||||||
|
from actions.data.text import Text
|
||||||
|
from packages.v1.servicos.balcao.actions.t_servico_itempedido.t_servico_itempedido_certidao_save_action import (
|
||||||
|
TServicoItemPedidoCertidaSaveAction,
|
||||||
|
)
|
||||||
|
from packages.v1.servicos.balcao.schemas.t_servico_itempedido_schema import (
|
||||||
|
TServicoItemPedidoCertidaoSaveSchema,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class TServicoItemPedidoCertidaoSaveService:
|
||||||
|
|
||||||
|
def execute(self, data: TServicoItemPedidoCertidaoSaveSchema):
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# 1. Apenas status FINAL COM SAVE
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if data.data.get("status") != 2:
|
||||||
|
return {"error": 0}
|
||||||
|
|
||||||
|
file_url = data.data.get("url")
|
||||||
|
if not file_url:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
|
detail="URL do arquivo não informada.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# 2. Download binário
|
||||||
|
# ----------------------------------------------------
|
||||||
|
response = requests.get(file_url, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
arquivo_bytes = response.content
|
||||||
|
|
||||||
|
if not arquivo_bytes:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Arquivo retornado está vazio.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# 3. Validação RTF mínima
|
||||||
|
# ----------------------------------------------------
|
||||||
|
if not arquivo_bytes.lstrip().startswith(b"{\\rtf"):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
|
||||||
|
detail="Conteúdo retornado não é um RTF válido.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# 4. Persistência no banco
|
||||||
|
# ----------------------------------------------------
|
||||||
|
data.certidao_texto = Text.compress(arquivo_bytes)
|
||||||
|
|
||||||
|
save_action = TServicoItemPedidoCertidaSaveAction()
|
||||||
|
|
||||||
|
if not save_action.execute(data):
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Erro ao salvar certidão no banco.",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# 5. Escrita em disco (atomic)
|
||||||
|
# ----------------------------------------------------
|
||||||
|
destino = Path("./storage/temp")
|
||||||
|
destino.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
arquivo_final = destino / data.data["key"]
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False, dir=destino) as tmp:
|
||||||
|
tmp.write(arquivo_bytes)
|
||||||
|
tmp.flush()
|
||||||
|
temp_name = tmp.name
|
||||||
|
|
||||||
|
os.replace(temp_name, arquivo_final)
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# 6. Retorno esperado pelo OnlyOffice
|
||||||
|
# ----------------------------------------------------
|
||||||
|
return {"error": 0}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
|
from docx import Document
|
||||||
from fastapi import HTTPException, status
|
from fastapi import HTTPException, status
|
||||||
from packages.v1.servicos.balcao.actions.t_servico_itempedido.t_servico_itempedido_create_certidao_action import (
|
from packages.v1.servicos.balcao.actions.t_servico_itempedido.t_servico_itempedido_create_certidao_action import (
|
||||||
TServicoItemPedidoCreateCertidaoAction,
|
TServicoItemPedidoCreateCertidaoAction,
|
||||||
|
|
@ -92,6 +93,16 @@ class TServicoItemPedidoIndexService:
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# Cria o documento editavel em disco
|
||||||
|
item.certidao_texto = create_certidao_action.execute(
|
||||||
|
TServicoItemPedidoCreateCertidaoSchema(
|
||||||
|
servico_itempedido_id=item.servico_itempedido_id,
|
||||||
|
certidao_texto=None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Adiciona o objeto processado na lista final
|
# Adiciona o objeto processado na lista final
|
||||||
data.append(item)
|
data.append(item)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue