bugfix: route stickness precedences (transaction in the same intent)

This commit is contained in:
2026-08-20 09:22:03 -03:00
parent bb0ef019bf
commit 9df2467deb
434 changed files with 7098 additions and 281 deletions

View File

@@ -0,0 +1,72 @@
from contas_mcp.servers.contas_mcp_server.main import (
_preflight_subject,
_recover_explicit_contestation_subject,
)
def _args(subject="TIM Fashion Mensal"):
return {
"msisdn": "11999999999",
"subject": subject,
"motivo": "não contratei TIM CTRL Redes Sociais 8.0",
"valor": 71.99,
"billing_analysis": {
"currentInvoice": [
{
"type": "plano",
"desc": "Plano",
"items": [
{"type": "plano", "desc": "TIM Black A 8.0", "value": "104.99", "contestable": False},
{"type": "plano", "desc": "TIM CTRL Redes Sociais 8.0", "value": "71.99", "contestable": False},
],
},
{
"type": "servicos_contratados_de_parceiros",
"desc": "Serviços de valor adicionado",
"items": [
{"type": "servicos_contratados_de_parceiros", "desc": "TIM Fashion Mensal", "value": "10.0", "contestable": True},
],
},
]
},
"invoice_detail": {
"parsed_content": {
"1199999999": {
"Planos": {
"TIM CTRL Redes Sociais 8.0": {"valor_final": 71.99}
},
"SVA Detalhe Total": [
{"desc": "Tim Fashion", "value": 10, "classe": "avulso"}
],
}
}
},
}
def test_recovers_explicit_contestation_subject_from_original_text():
item = _recover_explicit_contestation_subject(_args())
assert item is not None
assert item["name"] == "TIM CTRL Redes Sociais 8.0"
assert str(item["value"]) == "71.99"
assert item["category"] == "plano"
def test_preflight_does_not_replace_explicit_plan_with_vas():
args = _args()
result = _preflight_subject("contestar_cobranca", args)
assert result is not None
assert result["status"] == "OUT_OF_SCOPE"
assert result["subject"] == "TIM CTRL Redes Sociais 8.0"
assert result["resolved_value"] == "71.99"
assert result["metadata"]["subject_corrected_from"] == "TIM Fashion Mensal"
assert args["subject"] == "TIM CTRL Redes Sociais 8.0"
assert args["resolved_value"] == "71.99"
def test_correct_subject_is_preserved():
args = _args(subject="TIM CTRL Redes Sociais 8.0")
result = _preflight_subject("contestar_cobranca", args)
assert result is not None
assert result["subject"] == "TIM CTRL Redes Sociais 8.0"
assert args.get("_subject_corrected_from") is None

View File

@@ -446,3 +446,79 @@ def test_contestation_action_calculates_totals_only_for_items_actually_contested
assert [x["itemName"] for x in result["not_contested_items"]] == ["Outro VAS"]
assert result["contested_invoice_amount_open"] == "14.99"
assert result["contested_invoice_amount"] == "14.99"
def test_validate_contestation_items_reconhece_vas_por_type_do_billing_analysis() -> None:
items = [
{
"item_name": "TIM Fashion Mensal",
"claimed_amount": "10",
"validated_amount": "10",
}
]
invoice = {
"currentInvoice": [
{
"type": "servicos_contratados_de_parceiros",
"desc": "Serviços de valor adicionado",
"value": "76.98",
"items": [
{
"type": "servicos_contratados_de_parceiros",
"desc": "TIM Fashion Mensal",
"value": "10.0",
"contestable": True,
}
],
}
]
}
validated, validation_log, error = validate_contestation_items(items, invoice)
assert error is None
assert validation_log[-1]["status"] == "aprovado"
assert validation_log[-1]["secao_vas"] is True
assert validation_log[-1]["item_fatura_resolvido"] == "TIM Fashion Mensal"
assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros"
assert validated[0]["validated_amount"] == "10"
def test_validate_contestation_items_prefere_evidencia_vas_quando_item_aparece_em_multiplas_visoes() -> None:
items = [
{
"item_name": "TIM Fashion Mensal",
"claimed_amount": "10",
"validated_amount": "10",
}
]
invoice = {
"invoiceVariation": [
{
"desc": "TIM Fashion Mensal",
"value": "10.0",
}
],
"currentInvoice": [
{
"type": "servicos_contratados_de_parceiros",
"desc": "Serviços de valor adicionado",
"value": "76.98",
"items": [
{
"type": "servicos_contratados_de_parceiros",
"desc": "TIM Fashion Mensal",
"value": "10.0",
"contestable": True,
}
],
}
],
}
_, validation_log, error = validate_contestation_items(items, invoice)
assert error is None
assert validation_log[-1]["status"] == "aprovado"
assert validation_log[-1]["secao_vas"] is True
assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros"

View File

@@ -0,0 +1,24 @@
from app.presentation.tool_renderers import render_contas_vas
def test_vas_renderer_includes_available_values():
result = {
"products": [
{"name": "TIM Fashion Mensal", "can": {"cancel": True}, "details": {"valor": "10,00"}},
{"name": "Aya Audiobooks Premium", "can": {"cancel": True}, "details": {"valor": "9,99"}},
{"name": "Neymar Jr", "can": {"cancel": True}, "details": {"valor": "12,00"}},
{"name": "Tamboro Mensal", "can": {"cancel": True}, "details": {"valor": "14,99"}},
]
}
text = render_contas_vas(tool_name="consultar_vas", result=result, state={}, agent_label="VasAgent")
assert "TIM Fashion Mensal (avulso): R$ 10,00" in text
assert "Aya Audiobooks Premium (avulso): R$ 9,99" in text
assert "Neymar Jr (avulso): R$ 12,00" in text
assert "Tamboro Mensal (avulso): R$ 14,99" in text
def test_vas_renderer_does_not_invent_missing_value():
result = {"products": [{"name": "Sem Preço", "can": {"cancel": True}, "details": {}}]}
text = render_contas_vas(tool_name="consultar_vas", result=result, state={}, agent_label="VasAgent")
assert "Sem Preço (avulso)" in text
assert "R$" not in text