From d0c29ca3744e8fca326145900d8573ebf34884a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vitor=20Hugo=20Belorio=20Sim=C3=A3o?= Date: Tue, 17 Mar 2026 09:49:05 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(sales):=20priorizar=20listag?= =?UTF-8?q?em=20de=20pedidos=20sobre=20follow-up=20de=20compra=20ativo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - impede que mensagens como 'Liste os meus pedidos' sejam consumidas pelo atalho de order_create quando ainda existe draft de compra aberto - corrige o reaproveitamento indevido da ultima falha de credito em fluxos de vendas ativos, permitindo que a listagem siga para o handler correto - adiciona regressao cobrindo o cenario de order_list com pending_order_drafts para evitar que a resposta fique presa no erro anterior --- .../orchestration/orquestrador_service.py | 2 + tests/test_turn_decision_contract.py | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/app/services/orchestration/orquestrador_service.py b/app/services/orchestration/orquestrador_service.py index 945f576..05b13df 100644 --- a/app/services/orchestration/orquestrador_service.py +++ b/app/services/orchestration/orquestrador_service.py @@ -632,6 +632,8 @@ class OrquestradorService(ReviewFlowMixin, OrderFlowMixin): normalized_message = self.normalizer.normalize_text(message).strip() if self._looks_like_explicit_domain_shift_request(normalized_message): return None + if self._has_order_listing_request(message): + return None pending_order_draft = self.state.get_entry("pending_order_drafts", user_id, expire=True) if pending_order_draft: diff --git a/tests/test_turn_decision_contract.py b/tests/test_turn_decision_contract.py index b986415..fcc8be1 100644 --- a/tests/test_turn_decision_contract.py +++ b/tests/test_turn_decision_contract.py @@ -2515,6 +2515,59 @@ class TurnDecisionContractTests(unittest.IsolatedAsyncioTestCase): self.assertEqual(state.get_user_context(1)["active_domain"], "general") self.assertEqual(state.get_user_context(1)["generic_memory"], {}) + async def test_active_sales_follow_up_ignores_order_listing_request_with_open_order_draft(self): + state = FakeState( + entries={ + "pending_order_drafts": { + 1: { + "payload": { + "cpf": "12345678909", + "vehicle_id": 15, + "modelo_veiculo": "Volkswagen T-Cross 2022", + "valor_veiculo": 73224.0, + }, + "expires_at": utc_now() + timedelta(minutes=15), + } + } + }, + contexts={ + 1: { + "active_domain": "sales", + "generic_memory": {"cpf": "12345678909"}, + "shared_memory": {"cpf": "12345678909"}, + "order_queue": [], + "pending_order_selection": None, + "pending_switch": None, + "last_stock_results": [ + {"id": 15, "modelo": "Volkswagen T-Cross 2022", "categoria": "suv", "preco": 73224.0, "budget_relaxed": True}, + ], + "selected_vehicle": {"id": 15, "modelo": "Volkswagen T-Cross 2022", "categoria": "suv", "preco": 73224.0, "budget_relaxed": True}, + } + } + ) + service = OrquestradorService.__new__(OrquestradorService) + service.state = state + service.normalizer = EntityNormalizer() + service._get_user_context = lambda user_id: state.get_user_context(user_id) + service._save_user_context = lambda user_id, context: state.save_user_context(user_id, context) + + async def fake_try_collect_and_create_order(**kwargs): + raise AssertionError("nao deveria consumir listagem de pedidos como continuacao de order_create") + + service._try_collect_and_create_order = fake_try_collect_and_create_order + + async def finish(response: str, queue_notice: str | None = None): + return response + + response = await service._try_handle_active_sales_follow_up( + message="Liste os meus pedidos", + user_id=1, + finish=finish, + ) + + self.assertIsNone(response) + self.assertIsNotNone(state.get_entry("pending_order_drafts", 1)) + async def test_active_sales_follow_up_allows_new_budget_search_to_reset_open_order_draft(self): state = FakeState( entries={