feat: funcoes puras de resolucao de tamanho de pagina e janela de paginas

This commit is contained in:
Gustavo
2026-08-20 08:48:35 -03:00
parent f940ff5768
commit 9d5702a01e
2 changed files with 83 additions and 0 deletions
+49
View File
@@ -34,6 +34,10 @@ templates = Jinja2Templates(directory="app/templates")
TAMANHO_PAGINA = 100
OPCOES_POR_PAGINA = [5, 10, 15, 20, 30, 40, 50, 100]
POR_PAGINA_PADRAO = 15
POR_PAGINA_COOKIE = "clientes_por_pagina"
# Únicos campos que o formulário exige preenchidos — os demais (contrato,
# valores, datas) podem ficar em branco e recebem um default sensato
# (ver _aplicar_defaults_opcionais) em vez de bloquear o cadastro.
@@ -86,6 +90,51 @@ def _campos_obrigatorios_faltando(dados: dict) -> list[str]:
return [campo for campo in CAMPOS_OBRIGATORIOS if not dados.get(campo)]
def _resolver_por_pagina(por_pagina_query: int | None, cookie_valor: str | None) -> int:
"""
Decide o tamanho de página efetivo: query string (se for um dos
valores de OPCOES_POR_PAGINA) -> cookie clientes_por_pagina (se for um
valor válido) -> padrão (15). Nunca deixa passar um valor fora de
OPCOES_POR_PAGINA, mesmo que venha adulterado na URL ou no cookie.
"""
if por_pagina_query in OPCOES_POR_PAGINA:
return por_pagina_query
try:
cookie_int = int(cookie_valor) if cookie_valor is not None else None
except ValueError:
cookie_int = None
if cookie_int in OPCOES_POR_PAGINA:
return cookie_int
return POR_PAGINA_PADRAO
def _janela_paginas(pagina: int, total_paginas: int) -> list[int | None]:
"""
Lista de números de página a mostrar na paginação, com None
representando uma reticência ("…"). Até 7 páginas, mostra todas. Acima
disso, mostra a primeira, a última, a página atual e uma vizinha de
cada lado, preenchendo os buracos com None. Ex.: página 5 de 20 ->
[1, None, 4, 5, 6, None, 20].
"""
if total_paginas <= 7:
return list(range(1, total_paginas + 1))
nucleo = {1, total_paginas, pagina}
for vizinha in (pagina - 1, pagina + 1):
if 1 <= vizinha <= total_paginas:
nucleo.add(vizinha)
ordenado = sorted(nucleo)
resultado: list[int | None] = []
anterior = None
for pag in ordenado:
if anterior is not None and pag - anterior > 1:
resultado.append(None)
resultado.append(pag)
anterior = pag
return resultado
def _validar_documento(dados: dict, confirmar_dv: bool) -> dict:
"""
Confere o dígito verificador do documento em CLIENTE.CNPJ (o mesmo
+34
View File
@@ -53,6 +53,40 @@ def _form_valido(codigo: str = "000001") -> dict:
return dados
def test_resolver_por_pagina_usa_query_quando_valido():
assert router_clientes._resolver_por_pagina(50, None) == 50
def test_resolver_por_pagina_ignora_query_invalida_usa_cookie():
assert router_clientes._resolver_por_pagina(999, "30") == 30
def test_resolver_por_pagina_ignora_cookie_invalido_usa_padrao():
assert router_clientes._resolver_por_pagina(None, "abc") == 15
assert router_clientes._resolver_por_pagina(None, "999") == 15
def test_resolver_por_pagina_sem_query_nem_cookie_usa_padrao():
assert router_clientes._resolver_por_pagina(None, None) == 15
def test_janela_paginas_poucas_paginas_mostra_todas():
assert router_clientes._janela_paginas(1, 5) == [1, 2, 3, 4, 5]
assert router_clientes._janela_paginas(3, 7) == [1, 2, 3, 4, 5, 6, 7]
def test_janela_paginas_no_meio_com_reticencias_dos_dois_lados():
assert router_clientes._janela_paginas(5, 20) == [1, None, 4, 5, 6, None, 20]
def test_janela_paginas_primeira_pagina():
assert router_clientes._janela_paginas(1, 20) == [1, 2, None, 20]
def test_janela_paginas_ultima_pagina():
assert router_clientes._janela_paginas(20, 20) == [1, None, 19, 20]
def test_listar_clientes(monkeypatch):
monkeypatch.setattr(
router_clientes.db_clientes,