diff --git a/app/routers/clientes.py b/app/routers/clientes.py index 6c08a89..3f5d162 100644 --- a/app/routers/clientes.py +++ b/app/routers/clientes.py @@ -43,6 +43,9 @@ CAMPOS_OBRIGATORIOS = ["CODIGO", "CNPJ", "RAZAO", "FANTASIA"] _CAMPOS_DECIMAIS_OPCIONAIS = ["IMPLANTACAO", "MENSALIDADE", "JUROS_MORA", "JUROS_MULTA"] _CAMPOS_DATA_OPCIONAIS = ["DATA_CONTRATO", "VENCIMENTO_CONTRATO"] +FILTROS_STATUS_VALIDOS = {"ativos": 1, "inativos": 0, "todos": None} +STATUS_PADRAO = "ativos" + def _redirect_com_mensagem(url: str, mensagem: str, tipo: str = "success") -> RedirectResponse: separador = "&" if "?" in url else "?" @@ -164,6 +167,7 @@ def listar( direcao: str = db_clientes.DIRECAO_PADRAO, pagina: int = 1, por_pagina: Optional[int] = None, + status: str = STATUS_PADRAO, mensagem: Optional[str] = None, mensagem_tipo: Optional[str] = None, usuario: str = Depends(exigir_login), @@ -172,17 +176,21 @@ def listar( ordenar_por = db_clientes.ORDENACAO_PADRAO if direcao not in db_clientes.DIRECOES_VALIDAS: direcao = db_clientes.DIRECAO_PADRAO + if status not in FILTROS_STATUS_VALIDOS: + status = STATUS_PADRAO + filtro_ativo = FILTROS_STATUS_VALIDOS[status] por_pagina_efetivo = _resolver_por_pagina(por_pagina, request.cookies.get(POR_PAGINA_COOKIE)) pagina = max(pagina, 1) - total = db_clientes.contar_clientes(busca=busca or None, ordenar_por=ordenar_por) + total = db_clientes.contar_clientes(busca=busca or None, ordenar_por=ordenar_por, filtro_ativo=filtro_ativo) total_paginas = max(math.ceil(total / por_pagina_efetivo), 1) pagina = min(pagina, total_paginas) offset = (pagina - 1) * por_pagina_efetivo clientes = db_clientes.listar_clientes( - busca=busca or None, limite=por_pagina_efetivo, offset=offset, ordenar_por=ordenar_por, direcao=direcao + busca=busca or None, limite=por_pagina_efetivo, offset=offset, ordenar_por=ordenar_por, + direcao=direcao, filtro_ativo=filtro_ativo, ) primeiro_item = offset + 1 if total > 0 else 0 @@ -193,7 +201,7 @@ def listar( "clientes_lista.html", { "clientes": clientes, "busca": busca, "ordenar_por": ordenar_por, "direcao": direcao, - "mensagem": mensagem, "mensagem_tipo": mensagem_tipo, + "mensagem": mensagem, "mensagem_tipo": mensagem_tipo, "status": status, "pagina": pagina, "total_paginas": total_paginas, "total": total, "por_pagina": por_pagina_efetivo, "opcoes_por_pagina": OPCOES_POR_PAGINA, "primeiro_item": primeiro_item, "ultimo_item": ultimo_item, @@ -216,6 +224,7 @@ def listar( def form_novo(request: Request, usuario: str = Depends(exigir_login)): dados = {c: "" for c in db_clientes.CAMPOS_CLIENTE} dados["CODIGO"] = db_clientes.proximo_codigo_sugerido() + dados["ATIVO"] = "1" return templates.TemplateResponse( request, "cliente_form.html", diff --git a/tests/test_main.py b/tests/test_main.py index 3602c87..8c293ea 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -25,8 +25,8 @@ def test_docs_desabilitado_retorna_404(): def test_raiz_redireciona_para_clientes_com_login(monkeypatch): from app.routers import clientes as router_clientes - monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": []) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) + monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: []) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1: 0) resposta = client.get("/", cookies=_cookie_sessao_admin(), follow_redirects=False) assert resposta.status_code == 307 assert resposta.headers["location"] == "/clientes" @@ -77,9 +77,9 @@ def test_pagina_clientes_tem_csrf_token_preenchido_no_botao_sair(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [], + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1: 0) client.cookies.clear() resposta = client.get("/clientes", cookies=_cookie_sessao_admin()) @@ -105,9 +105,9 @@ def test_logout_funciona_depois_de_visitar_clientes(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [], + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1: 0) client.cookies.clear() client.cookies.update(_cookie_sessao_admin()) diff --git a/tests/test_router_clientes.py b/tests/test_router_clientes.py index 1a5477a..c2d925c 100644 --- a/tests/test_router_clientes.py +++ b/tests/test_router_clientes.py @@ -58,6 +58,7 @@ def _form_valido(codigo: str = "000001") -> dict: "DATA_CONTRATO": "2026-01-01", "VENCIMENTO_CONTRATO": "2027-01-01", "DIA_VENCIMENTO": "10", + "ATIVO": "1", "csrf_token": CSRF_TESTE, } ) @@ -103,9 +104,9 @@ def test_listar_clientes_por_pagina_padrao_e_15(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": capturado.setdefault("limite", limite) and [], + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: capturado.setdefault("limite", limite) and [], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 3) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:3) resposta = client.get("/clientes") assert resposta.status_code == 200 @@ -117,9 +118,9 @@ def test_listar_clientes_por_pagina_da_query_e_usada_e_grava_cookie(monkeypatch) monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": capturado.setdefault("limite", limite) and [], + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: capturado.setdefault("limite", limite) and [], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 3) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:3) resposta = client.get("/clientes?por_pagina=50") assert resposta.status_code == 200 @@ -132,9 +133,9 @@ def test_listar_clientes_usa_cookie_quando_sem_por_pagina_na_query(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": capturado.setdefault("limite", limite) and [], + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: capturado.setdefault("limite", limite) and [], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 3) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:3) resposta = client.get("/clientes", cookies={router_clientes.POR_PAGINA_COOKIE: "30"}) assert resposta.status_code == 200 @@ -146,9 +147,9 @@ def test_listar_clientes_por_pagina_invalida_na_query_cai_no_padrao_e_nao_grava_ monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": capturado.setdefault("limite", limite) and [], + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: capturado.setdefault("limite", limite) and [], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 3) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:3) resposta = client.get("/clientes?por_pagina=999") assert resposta.status_code == 200 @@ -156,15 +157,75 @@ def test_listar_clientes_por_pagina_invalida_na_query_cai_no_padrao_e_nao_grava_ assert router_clientes.POR_PAGINA_COOKIE not in resposta.cookies +def test_listar_clientes_status_padrao_e_ativos(monkeypatch): + capturado = {} + monkeypatch.setattr( + router_clientes.db_clientes, + "listar_clientes", + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: (capturado.__setitem__("filtro_ativo", filtro_ativo), [])[1], + ) + monkeypatch.setattr( + router_clientes.db_clientes, + "contar_clientes", + lambda busca=None, ordenar_por="razao", filtro_ativo=1: 0, + ) + + resposta = client.get("/clientes") + assert resposta.status_code == 200 + assert capturado["filtro_ativo"] == 1 + + +def test_listar_clientes_status_inativos_usa_filtro_zero(monkeypatch): + capturado = {} + monkeypatch.setattr( + router_clientes.db_clientes, + "listar_clientes", + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: (capturado.__setitem__("filtro_ativo", filtro_ativo), [])[1], + ) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1: 0) + + resposta = client.get("/clientes?status=inativos") + assert resposta.status_code == 200 + assert capturado["filtro_ativo"] == 0 + + +def test_listar_clientes_status_todos_sem_filtro(monkeypatch): + capturado = {} + monkeypatch.setattr( + router_clientes.db_clientes, + "listar_clientes", + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: (capturado.__setitem__("filtro_ativo", filtro_ativo), [])[1], + ) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1: 0) + + resposta = client.get("/clientes?status=todos") + assert resposta.status_code == 200 + assert capturado["filtro_ativo"] is None + + +def test_listar_clientes_status_invalido_cai_no_padrao(monkeypatch): + capturado = {} + monkeypatch.setattr( + router_clientes.db_clientes, + "listar_clientes", + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: (capturado.__setitem__("filtro_ativo", filtro_ativo), [])[1], + ) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1: 0) + + resposta = client.get("/clientes?status=lixo_qualquer") + assert resposta.status_code == 200 + assert capturado["filtro_ativo"] == 1 + + def test_listar_clientes(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [ + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [ {"ID": 1, "CODIGO": "000001", "RAZAO": "Cliente Um", "FANTASIA": "Um", "CNPJ": "11222333000181"} ], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 1) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:1) resposta = client.get("/clientes") assert resposta.status_code == 200 assert "Cliente Um" in resposta.text @@ -174,9 +235,9 @@ def test_listar_clientes_inclui_csrf_token_no_contexto(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [], + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:0) resposta = client.get("/clientes") assert resposta.status_code == 200 @@ -191,11 +252,11 @@ def test_listar_clientes_tem_botao_de_gerar_chave_por_linha(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [ + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [ {"ID": 1, "CODIGO": "000042", "RAZAO": "Cliente Um", "FANTASIA": "Um", "CNPJ": "11222333000181"} ], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 1) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:1) resposta = client.get("/clientes") assert resposta.status_code == 200 @@ -208,12 +269,12 @@ def test_listar_clientes_pagina_com_controles_de_paginacao(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [ + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [ {"ID": i, "CODIGO": f"{i:06d}", "RAZAO": f"Cliente {i}", "FANTASIA": f"C{i}", "CNPJ": "11222333000181"} for i in range(1, 6) ], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 250) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:250) resposta = client.get("/clientes?pagina=2&por_pagina=100") assert resposta.status_code == 200 @@ -226,12 +287,12 @@ def test_listar_clientes_footer_com_selecao_de_por_pagina(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [ + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [ {"ID": i, "CODIGO": f"{i:06d}", "RAZAO": f"Cliente {i}", "FANTASIA": f"C{i}", "CNPJ": "1"} for i in range(1, 16) ], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 98) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:98) resposta = client.get("/clientes?pagina=1") assert resposta.status_code == 200 @@ -243,7 +304,7 @@ def test_listar_clientes_footer_com_selecao_de_por_pagina(monkeypatch): def test_listar_clientes_paginacao_com_reticencias_quando_muitas_paginas(monkeypatch): monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", lambda **kwargs: []) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 500) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:500) # 500 / 5 = 100 paginas, pedindo a pagina 10 (no meio) -> tem reticencias resposta = client.get("/clientes?pagina=10&por_pagina=5") @@ -254,12 +315,12 @@ def test_listar_clientes_paginacao_com_reticencias_quando_muitas_paginas(monkeyp def test_listar_clientes_ordenar_por_invalido_cai_no_padrao(monkeypatch): capturado = {} - def _listar_clientes(busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc"): + def _listar_clientes(busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1): capturado["ordenar_por"] = ordenar_por return [] monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", _listar_clientes) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:0) client.get("/clientes?ordenar_por=coisa_invalida_qualquer") assert capturado["ordenar_por"] == "razao" @@ -272,7 +333,7 @@ def test_cabecalhos_da_lista_alternam_direcao(monkeypatch): monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", lambda **kwargs: [] ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:0) # Sem ordenar_por/direcao na URL: padrão é razao/asc. Clicar de novo em # "Razão" deve levar pra desc; clicar em "Código" (coluna diferente da @@ -296,12 +357,12 @@ def test_busca_com_e_comercial_nao_quebra_links_de_paginacao_e_ordenacao(monkeyp monkeypatch.setattr( router_clientes.db_clientes, "listar_clientes", - lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc": [ + lambda busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1: [ {"ID": i, "CODIGO": f"{i:06d}", "RAZAO": f"Cliente {i}", "FANTASIA": f"C{i}", "CNPJ": "1"} for i in range(1, 6) ], ) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 250) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:250) resposta = client.get("/clientes?pagina=2&busca=A%26B") assert resposta.status_code == 200 @@ -312,12 +373,12 @@ def test_busca_com_e_comercial_nao_quebra_links_de_paginacao_e_ordenacao(monkeyp def test_listar_clientes_direcao_invalida_cai_no_padrao(monkeypatch): capturado = {} - def _listar_clientes(busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc"): + def _listar_clientes(busca=None, limite=100, offset=0, ordenar_por="razao", direcao="asc", filtro_ativo=1): capturado["direcao"] = direcao return [] monkeypatch.setattr(router_clientes.db_clientes, "listar_clientes", _listar_clientes) - monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao": 0) + monkeypatch.setattr(router_clientes.db_clientes, "contar_clientes", lambda busca=None, ordenar_por="razao", filtro_ativo=1:0) client.get("/clientes?direcao=coisa_invalida") assert capturado["direcao"] == "asc"