fix: busca de clientes restrita ao campo escolhido em ordenar_por
Antes, digitar qualquer coisa na busca comparava com CODIGO, CNPJ, RAZAO e FANTASIA ao mesmo tempo (OR), entao escolher 'Ordenar por: Codigo' e buscar '242' trazia tambem clientes cujo CNPJ ou Razao continham '242' em outro lugar. Agora a busca usa so a coluna selecionada no dropdown (mesma whitelist ja usada pra ordenacao). listar_clientes()/contar_clientes() e o template ganharam o ajuste; o dropdown mudou de rotulo pra 'Buscar/ordenar por' pra deixar claro que controla os dois. 93/93 testes passando.
This commit is contained in:
+10
-13
@@ -34,21 +34,22 @@ class ClienteComDependenciasError(Exception):
|
|||||||
def listar_clientes(
|
def listar_clientes(
|
||||||
busca: str | None = None, limite: int = 100, offset: int = 0, ordenar_por: str = ORDENACAO_PADRAO
|
busca: str | None = None, limite: int = 100, offset: int = 0, ordenar_por: str = ORDENACAO_PADRAO
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
coluna_ordenacao = ORDENACOES_VALIDAS.get(ordenar_por, ORDENACOES_VALIDAS[ORDENACAO_PADRAO])
|
coluna = ORDENACOES_VALIDAS.get(ordenar_por, ORDENACOES_VALIDAS[ORDENACAO_PADRAO])
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
if busca:
|
if busca:
|
||||||
|
# A busca é restrita à mesma coluna escolhida em "ordenar_por"
|
||||||
|
# — selecionar "Código" busca só em CODIGO, não em todos os
|
||||||
|
# campos de uma vez.
|
||||||
sql = (
|
sql = (
|
||||||
f"SELECT {_COLUNAS_SELECT} FROM CLIENTE "
|
f"SELECT {_COLUNAS_SELECT} FROM CLIENTE "
|
||||||
"WHERE CODIGO LIKE %s OR CNPJ LIKE %s OR RAZAO LIKE %s OR FANTASIA LIKE %s "
|
f"WHERE {coluna} LIKE %s ORDER BY {coluna} LIMIT %s OFFSET %s"
|
||||||
f"ORDER BY {coluna_ordenacao} LIMIT %s OFFSET %s"
|
|
||||||
)
|
)
|
||||||
termo = f"%{busca}%"
|
cur.execute(sql, (f"%{busca}%", limite, offset))
|
||||||
cur.execute(sql, (termo, termo, termo, termo, limite, offset))
|
|
||||||
else:
|
else:
|
||||||
cur.execute(
|
cur.execute(
|
||||||
f"SELECT {_COLUNAS_SELECT} FROM CLIENTE ORDER BY {coluna_ordenacao} LIMIT %s OFFSET %s",
|
f"SELECT {_COLUNAS_SELECT} FROM CLIENTE ORDER BY {coluna} LIMIT %s OFFSET %s",
|
||||||
(limite, offset),
|
(limite, offset),
|
||||||
)
|
)
|
||||||
return cur.fetchall()
|
return cur.fetchall()
|
||||||
@@ -80,17 +81,13 @@ def proximo_codigo_sugerido() -> str:
|
|||||||
return str(proximo).zfill(6)
|
return str(proximo).zfill(6)
|
||||||
|
|
||||||
|
|
||||||
def contar_clientes(busca: str | None = None) -> int:
|
def contar_clientes(busca: str | None = None, ordenar_por: str = ORDENACAO_PADRAO) -> int:
|
||||||
|
coluna = ORDENACOES_VALIDAS.get(ordenar_por, ORDENACOES_VALIDAS[ORDENACAO_PADRAO])
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
with conn.cursor() as cur:
|
with conn.cursor() as cur:
|
||||||
if busca:
|
if busca:
|
||||||
sql = (
|
cur.execute(f"SELECT COUNT(*) AS total FROM CLIENTE WHERE {coluna} LIKE %s", (f"%{busca}%",))
|
||||||
"SELECT COUNT(*) AS total FROM CLIENTE "
|
|
||||||
"WHERE CODIGO LIKE %s OR CNPJ LIKE %s OR RAZAO LIKE %s OR FANTASIA LIKE %s"
|
|
||||||
)
|
|
||||||
termo = f"%{busca}%"
|
|
||||||
cur.execute(sql, (termo, termo, termo, termo))
|
|
||||||
else:
|
else:
|
||||||
cur.execute("SELECT COUNT(*) AS total FROM CLIENTE")
|
cur.execute("SELECT COUNT(*) AS total FROM CLIENTE")
|
||||||
return cur.fetchone()["total"]
|
return cur.fetchone()["total"]
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ def listar(
|
|||||||
ordenar_por = db_clientes.ORDENACAO_PADRAO
|
ordenar_por = db_clientes.ORDENACAO_PADRAO
|
||||||
|
|
||||||
pagina = max(pagina, 1)
|
pagina = max(pagina, 1)
|
||||||
total = db_clientes.contar_clientes(busca=busca or None)
|
total = db_clientes.contar_clientes(busca=busca or None, ordenar_por=ordenar_por)
|
||||||
total_paginas = max(math.ceil(total / TAMANHO_PAGINA), 1)
|
total_paginas = max(math.ceil(total / TAMANHO_PAGINA), 1)
|
||||||
pagina = min(pagina, total_paginas)
|
pagina = min(pagina, total_paginas)
|
||||||
offset = (pagina - 1) * TAMANHO_PAGINA
|
offset = (pagina - 1) * TAMANHO_PAGINA
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
{% block conteudo %}
|
{% block conteudo %}
|
||||||
<h1>Clientes</h1>
|
<h1>Clientes</h1>
|
||||||
<form method="get" action="/clientes">
|
<form method="get" action="/clientes">
|
||||||
<input type="text" name="busca" value="{{ busca or '' }}" placeholder="Buscar por código, CNPJ, razão ou fantasia">
|
<label for="ordenar_por">Buscar/ordenar por</label>
|
||||||
<label for="ordenar_por">Ordenar por</label>
|
|
||||||
<select id="ordenar_por" name="ordenar_por">
|
<select id="ordenar_por" name="ordenar_por">
|
||||||
<option value="razao" {% if ordenar_por == "razao" %}selected{% endif %}>Razão social</option>
|
<option value="razao" {% if ordenar_por == "razao" %}selected{% endif %}>Razão social</option>
|
||||||
<option value="codigo" {% if ordenar_por == "codigo" %}selected{% endif %}>Código</option>
|
<option value="codigo" {% if ordenar_por == "codigo" %}selected{% endif %}>Código</option>
|
||||||
<option value="fantasia" {% if ordenar_por == "fantasia" %}selected{% endif %}>Fantasia</option>
|
<option value="fantasia" {% if ordenar_por == "fantasia" %}selected{% endif %}>Fantasia</option>
|
||||||
<option value="cnpj" {% if ordenar_por == "cnpj" %}selected{% endif %}>CNPJ</option>
|
<option value="cnpj" {% if ordenar_por == "cnpj" %}selected{% endif %}>CNPJ</option>
|
||||||
</select>
|
</select>
|
||||||
|
<input type="text" name="busca" value="{{ busca or '' }}" placeholder="Buscar só no campo escolhido acima">
|
||||||
<button type="submit">Buscar</button>
|
<button type="submit">Buscar</button>
|
||||||
</form>
|
</form>
|
||||||
<p><a class="botao" href="/clientes/novo">+ Novo cliente</a></p>
|
<p><a class="botao" href="/clientes/novo">+ Novo cliente</a></p>
|
||||||
|
|||||||
@@ -87,8 +87,15 @@ def test_excluir_cliente_sem_dependencias(cliente_id):
|
|||||||
assert db_clientes.obter_cliente(cliente_id) is None
|
assert db_clientes.obter_cliente(cliente_id) is None
|
||||||
|
|
||||||
|
|
||||||
def test_listar_clientes_ordena_por_codigo(cliente_id):
|
def test_listar_clientes_busca_e_restrita_a_coluna_escolhida(cliente_id):
|
||||||
|
# ordenar_por="codigo" restringe a busca à coluna CODIGO — o termo
|
||||||
|
# "TESTE PAINEL RAZAO" existe em RAZAO/FANTASIA, mas não deve aparecer
|
||||||
|
# aqui porque a busca não olha para essas colunas quando é "codigo".
|
||||||
resultado = db_clientes.listar_clientes(busca="TESTE PAINEL RAZAO", ordenar_por="codigo")
|
resultado = db_clientes.listar_clientes(busca="TESTE PAINEL RAZAO", ordenar_por="codigo")
|
||||||
|
assert len(resultado) == 0
|
||||||
|
|
||||||
|
# Buscando pelo próprio código, com ordenar_por="codigo", encontra.
|
||||||
|
resultado = db_clientes.listar_clientes(busca="999901", ordenar_por="codigo")
|
||||||
assert resultado[0]["CODIGO"] == "999901"
|
assert resultado[0]["CODIGO"] == "999901"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ def test_raiz_redireciona_para_clientes_com_login(monkeypatch):
|
|||||||
from app.routers import clientes as router_clientes
|
from app.routers import clientes as router_clientes
|
||||||
|
|
||||||
monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", lambda busca=None, limite=100, offset=0: [])
|
monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", lambda busca=None, limite=100, offset=0: [])
|
||||||
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None: 0)
|
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0)
|
||||||
resposta = client.get("/", headers=_AUTH_HEADERS, follow_redirects=False)
|
resposta = client.get("/", headers=_AUTH_HEADERS, follow_redirects=False)
|
||||||
assert resposta.status_code == 307
|
assert resposta.status_code == 307
|
||||||
assert resposta.headers["location"] == "/clientes"
|
assert resposta.headers["location"] == "/clientes"
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ def test_listar_clientes(monkeypatch):
|
|||||||
{"ID": 1, "CODIGO": "000001", "RAZAO": "Cliente Um", "FANTASIA": "Um", "CNPJ": "11222333000181"}
|
{"ID": 1, "CODIGO": "000001", "RAZAO": "Cliente Um", "FANTASIA": "Um", "CNPJ": "11222333000181"}
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None: 1)
|
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 1)
|
||||||
resposta = client.get("/clientes")
|
resposta = client.get("/clientes")
|
||||||
assert resposta.status_code == 200
|
assert resposta.status_code == 200
|
||||||
assert "Cliente Um" in resposta.text
|
assert "Cliente Um" in resposta.text
|
||||||
@@ -76,7 +76,7 @@ def test_listar_clientes_pagina_com_controles_de_paginacao(monkeypatch):
|
|||||||
for i in range(1, 6)
|
for i in range(1, 6)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None: 250)
|
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 250)
|
||||||
|
|
||||||
resposta = client.get("/clientes?pagina=2")
|
resposta = client.get("/clientes?pagina=2")
|
||||||
assert resposta.status_code == 200
|
assert resposta.status_code == 200
|
||||||
@@ -94,7 +94,7 @@ def test_listar_clientes_ordenar_por_invalido_cai_no_padrao(monkeypatch):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", _listar_clientes)
|
monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", _listar_clientes)
|
||||||
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None: 0)
|
monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0)
|
||||||
|
|
||||||
client.get("/clientes?ordenar_por=coisa_invalida_qualquer")
|
client.get("/clientes?ordenar_por=coisa_invalida_qualquer")
|
||||||
assert capturado["ordenar_por"] == "razao"
|
assert capturado["ordenar_por"] == "razao"
|
||||||
|
|||||||
Reference in New Issue
Block a user