fix: finalize Flow v2 authoritative cutover semantics

This commit is contained in:
plx
2026-08-16 01:58:12 +00:00
parent 213ba6b41b
commit f96b1b3d6e
6 changed files with 73 additions and 8 deletions

View File

@@ -77,6 +77,12 @@ def _active_obligations(obligations: Iterable[Mapping[str, Any]]) -> list[Mappin
and not row.get("resolved_at") and not row.get("superseded_by_task_id")]
def _unanswered_inbound(row: Mapping[str, Any]) -> bool:
inbound = _dt(row.get("latest_public_inbound"))
outbound = _dt(row.get("latest_public_outbound"))
return bool(inbound and (outbound is None or outbound <= inbound))
def _ref(row: Mapping[str, Any]) -> dict[str, Any]:
return {"source": "task", "id": str(row.get("id") or ""), "status": "pending",
"action_code": _code(row.get("action_code")),
@@ -149,7 +155,11 @@ def decide_authoritative_operation(
picked = choose(("REVIEW_MANUALLY", "REVIEW_REQUIRED", "REVIEW", "REVIEW_RECONSTRUCTED_PROCESS"), "review", "ACTIVE_REVIEW_OBLIGATION")
picked = picked or choose(("SUPPORT",), "do_now", "ACTIVE_SUPPORT_OBLIGATION")
if state == "INQUIRY" or business_action == "SEND_INFO":
valid_send_info = [row for row in by_code.get("SEND_INFO", []) if (
state != "AWAITING_CUSTOMER" or _unanswered_inbound(row)
)]
if valid_send_info and (state in {"INQUIRY", "AWAITING_CUSTOMER"} or business_action == "SEND_INFO"):
by_code["SEND_INFO"] = valid_send_info
picked = picked or choose(("SEND_INFO",), "do_now", "ACTIVE_RESPONSE_OBLIGATION")
picked = picked or choose(("CALL_CUSTOMER",), "do_now", "EXPLICIT_CALL_OBLIGATION")
if picked:

View File

@@ -730,7 +730,7 @@ def get_operations_summary(
seeded = {str(row.get("opportunity_id") or "") for row in seed_rows}
with (nullcontext(connection) if connection is not None else engine.connect()) as conn:
v2_seeds = conn.execute(text("""
SELECT p.opportunity_id::text, p.business_next_action, p.derived_at,
SELECT p.opportunity_id::text, p.business_state, p.business_next_action, p.derived_at,
o.title, o.value_amount, o.currency, o.lifecycle_state,
c.name AS customer_name
FROM opportunity_flow_state_v2 p
@@ -748,7 +748,10 @@ def get_operations_summary(
"source": "flow_v2", "id": oid, "opportunity_id": oid,
"created_at": projection_seed.get("derived_at"), "due_at": None,
"priority": "normal", "queue": "vendas",
"action_code": projection_seed.get("business_next_action"), "status": "projected",
"action_code": (projection_seed.get("business_next_action")
or ("WAIT_PAYMENT" if projection_seed.get("business_state") == "AWAITING_PAYMENT"
else "WAIT_CUSTOMER")),
"status": "projected",
"title": projection_seed.get("title") or "Oportunidade",
"opportunity_title": projection_seed.get("title") or "",
"customer_name": projection_seed.get("customer_name") or "",

View File

@@ -481,9 +481,16 @@ def _get_authoritative_opportunity_decisions(ids: list[str], *, connection: Any
FROM opportunity_flow_state_v2 WHERE opportunity_id::text IN :opportunity_ids
""", ids)
tasks = _bulk_rows(conn, """
SELECT id::text, opportunity_id::text, action_code, status, due_at, source_system,
resolved_at, resolution_code, superseded_by_task_id::text, created_at, metadata
FROM tasks WHERE opportunity_id::text IN :opportunity_ids AND status = 'pending'
SELECT t.id::text, t.opportunity_id::text, t.action_code, t.status, t.due_at,
t.source_system, t.conversation_id, t.resolved_at, t.resolution_code,
t.superseded_by_task_id::text, t.created_at, t.metadata,
(SELECT max(m.created_at) FROM messages m
WHERE m.source_system = 'chatwoot' AND m.conversation_id = t.conversation_id
AND m.direction = 'inbound') AS latest_public_inbound,
(SELECT max(m.created_at) FROM messages m
WHERE m.source_system = 'chatwoot' AND m.conversation_id = t.conversation_id
AND m.direction = 'outbound') AS latest_public_outbound
FROM tasks t WHERE t.opportunity_id::text IN :opportunity_ids AND t.status = 'pending'
""", ids)
customers = _bulk_rows(conn, """
SELECT o.id::text AS opportunity_id,