feat: bloqueia CNPJ/CPF duplicado ao cadastrar ou editar cliente
Mesmo padrão já usado pro CODIGO (db_clientes.codigo_em_uso): nova db_clientes.cnpj_em_uso(documento, ignorar_id=None) checada em criar()/editar() antes de gravar. A comparação ignora pontuação dos dois lados (não só normaliza o valor recebido) porque CLIENTE.CNPJ tem formatação inconsistente em registros legados (alguns com máscara, outros sem) — sem isso, um cadastro novo com o mesmo documento de um legado ainda mascarado passaria batido. app/templates/cliente_form.html: nova mensagem de erro 'erro_cnpj_duplicado', mesmo estilo visual do erro de código duplicado. 6 testes novos (unitário do db + integração do router pros dois fluxos), suíte completa (196 testes) passando. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
eed473c5f3
commit
f940ff5768
@@ -253,6 +253,7 @@ def test_criar_cliente_codigo_duplicado_bloqueia_sem_bypass(monkeypatch):
|
||||
|
||||
def test_criar_cliente_campo_obrigatorio_faltando_bloqueia(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
criado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: criado.setdefault("ok", True) and 42
|
||||
@@ -276,6 +277,7 @@ def test_criar_cliente_campos_administrativos_em_branco_usa_defaults(monkeypatch
|
||||
import datetime
|
||||
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
capturado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: capturado.setdefault("dados", dados) and 42
|
||||
@@ -299,6 +301,7 @@ def test_criar_cliente_campos_administrativos_em_branco_usa_defaults(monkeypatch
|
||||
|
||||
def test_criar_cliente_normaliza_cnpj_e_telefones(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
capturado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: capturado.setdefault("dados", dados) and 42
|
||||
@@ -323,6 +326,7 @@ def test_criar_cliente_normaliza_cnpj_e_telefones(monkeypatch):
|
||||
def test_editar_cliente_normaliza_cnpj_e_telefones(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "obter_cliente", lambda cliente_id: {"ID": cliente_id})
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
capturado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes,
|
||||
@@ -363,6 +367,37 @@ def test_editar_cliente_codigo_duplicado_bloqueia_sem_bypass(monkeypatch):
|
||||
assert "ok" not in atualizado
|
||||
|
||||
|
||||
def test_criar_cliente_cnpj_duplicado_bloqueia(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: True)
|
||||
criado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: criado.setdefault("ok", True) and 42
|
||||
)
|
||||
|
||||
resposta = client.post("/clientes/novo", data=_form_valido())
|
||||
assert resposta.status_code == 200
|
||||
assert "já está cadastrado" in resposta.text
|
||||
assert "ok" not in criado
|
||||
|
||||
|
||||
def test_editar_cliente_cnpj_duplicado_bloqueia(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "obter_cliente", lambda cliente_id: {"ID": cliente_id})
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: True)
|
||||
atualizado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes,
|
||||
"atualizar_cliente",
|
||||
lambda cliente_id, dados: atualizado.setdefault("ok", True),
|
||||
)
|
||||
|
||||
resposta = client.post("/clientes/1/editar", data=_form_valido())
|
||||
assert resposta.status_code == 200
|
||||
assert "já está cadastrado" in resposta.text
|
||||
assert "ok" not in atualizado
|
||||
|
||||
|
||||
def test_editar_cliente_inexistente_nao_finge_sucesso(monkeypatch):
|
||||
# Sem a checagem de existência, isso caía direto no UPDATE (0 linhas
|
||||
# afetadas, sem erro do MySQL) e ainda registrava "sucesso" no log.
|
||||
@@ -437,6 +472,7 @@ def test_criar_cliente_com_data_e_decimais_vazios_cria_com_defaults():
|
||||
|
||||
def test_criar_cliente_com_dv_invalido_exige_confirmacao(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
criado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: criado.setdefault("ok", True) and 42
|
||||
@@ -464,6 +500,7 @@ def test_criar_cliente_com_cnpj_dv_invalido_bloqueia_sempre(monkeypatch):
|
||||
"cadastrar mesmo assim" — nem com confirmar_dv_invalido=1.
|
||||
"""
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
criado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: criado.setdefault("ok", True) and 42
|
||||
@@ -487,6 +524,7 @@ def test_criar_cliente_com_cnpj_dv_invalido_bloqueia_sempre(monkeypatch):
|
||||
def test_editar_cliente_com_cnpj_dv_invalido_bloqueia(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "obter_cliente", lambda cliente_id: {"ID": cliente_id})
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
atualizado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes,
|
||||
@@ -506,6 +544,7 @@ def test_editar_cliente_com_cnpj_dv_invalido_bloqueia(monkeypatch):
|
||||
def test_criar_cliente_com_cpf_dv_invalido_continua_permitindo_confirmar(monkeypatch):
|
||||
"""Regressão: CPF com DV inválido não muda de comportamento."""
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
criado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: criado.setdefault("ok", True) and 42
|
||||
@@ -527,6 +566,7 @@ def test_criar_cliente_com_cpf_dv_invalido_continua_permitindo_confirmar(monkeyp
|
||||
|
||||
def test_criar_cliente_grava_telefone_com_ddi_55(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
capturado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: capturado.setdefault("dados", dados) and 42
|
||||
@@ -543,6 +583,7 @@ def test_criar_cliente_grava_telefone_com_ddi_55(monkeypatch):
|
||||
|
||||
def test_criar_cliente_telefone_ja_com_55_nao_duplica(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
capturado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: capturado.setdefault("dados", dados) and 42
|
||||
@@ -558,6 +599,7 @@ def test_criar_cliente_telefone_ja_com_55_nao_duplica(monkeypatch):
|
||||
|
||||
def test_criar_cliente_grava_uf_maiuscula_e_cep_sem_traco(monkeypatch):
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "codigo_em_uso", lambda codigo, ignorar_id=None: False)
|
||||
monkeypatch.setattr(router_clientes.db_clientes, "cnpj_em_uso", lambda documento, ignorar_id=None: False)
|
||||
capturado = {}
|
||||
monkeypatch.setattr(
|
||||
router_clientes.db_clientes, "criar_cliente", lambda dados: capturado.setdefault("dados", dados) and 42
|
||||
|
||||
Reference in New Issue
Block a user