feat: add operational eligibility and scheduled follow-up
This commit is contained in:
@@ -13,6 +13,7 @@ from typing import Any, Iterable, Mapping
|
||||
|
||||
from app.operation_noise import is_low_value_no_opportunity_item, is_noise_operation_item
|
||||
from app.work_center_action_policy import canonical_action_code
|
||||
from app.operational_eligibility import apply_operational_eligibility
|
||||
|
||||
|
||||
WAITING_ACTION_CODES = {
|
||||
@@ -173,15 +174,22 @@ def partition_canonical_items(
|
||||
items: Iterable[Mapping[str, Any]], *, display_limit: int
|
||||
) -> dict[str, Any]:
|
||||
all_items = [dict(item) for item in items]
|
||||
actionable = [item for item in all_items if item.get("operational_queue") != "waiting"]
|
||||
actionable_queues = {"do_now", "review", "blocked", "exception"}
|
||||
actionable = [item for item in all_items if item.get("operational_queue") in actionable_queues]
|
||||
waiting = [item for item in all_items if item.get("operational_queue") == "waiting"]
|
||||
backlog = [item for item in all_items if item.get("operational_queue") == "backlog"]
|
||||
not_current = [item for item in all_items if item.get("operational_queue") == "not_current"]
|
||||
return {
|
||||
"actionable_items": actionable,
|
||||
"waiting_items": waiting,
|
||||
"visible_actionable_items": actionable[:display_limit],
|
||||
"visible_waiting_items": waiting[:display_limit],
|
||||
"backlog_items": backlog,
|
||||
"not_current_items": not_current,
|
||||
"work_queue_total": len(actionable),
|
||||
"waiting_total": len(waiting),
|
||||
"backlog_total": len(backlog),
|
||||
"not_current_total": len(not_current),
|
||||
}
|
||||
|
||||
|
||||
@@ -313,6 +321,22 @@ def _opportunity_item(
|
||||
rows: list[Mapping[str, Any]],
|
||||
decision: Mapping[str, Any],
|
||||
) -> tuple[dict[str, Any] | None, list[dict[str, Any]]]:
|
||||
scheduled_call = next((
|
||||
row for row in rows
|
||||
if row.get("source") == "task"
|
||||
and _s(row.get("status")).lower() == "pending"
|
||||
and _canonical_code(row.get("action_code")) == "CALL_CUSTOMER"
|
||||
), None)
|
||||
if scheduled_call:
|
||||
decision = {
|
||||
**dict(decision),
|
||||
"action_code": "CALL_CUSTOMER",
|
||||
"label": "Ligar ao cliente",
|
||||
"description": _s(scheduled_call.get("detail")) or "Contacto telefónico agendado pelo operador.",
|
||||
"reason": "Existe um contacto telefónico explicitamente agendado.",
|
||||
"priority": _s(scheduled_call.get("priority")) or "normal",
|
||||
"target_url": _s(scheduled_call.get("href")),
|
||||
}
|
||||
action_code = _canonical_code(decision.get("action_code")) or "REVIEW"
|
||||
independent_exceptions: list[dict[str, Any]] = []
|
||||
evidence_rows: list[Mapping[str, Any]] = []
|
||||
@@ -338,7 +362,7 @@ def _opportunity_item(
|
||||
"target_url": f"/opportunities/{opportunity_id}",
|
||||
}
|
||||
|
||||
matching_task = _matching_task(evidence_rows, action_code)
|
||||
matching_task = scheduled_call or _matching_task(evidence_rows, action_code)
|
||||
primary = matching_task or next((row for row in evidence_rows if row.get("source") == "task"), None)
|
||||
primary = primary or (evidence_rows[0] if evidence_rows else {})
|
||||
process_key = f"opportunity:{opportunity_id}"
|
||||
@@ -455,6 +479,7 @@ def canonicalize_operations(
|
||||
# intermediate groups. Exact process/action identity is nevertheless the
|
||||
# final safe uniqueness boundary.
|
||||
items = _merge_exact_work_items(items)
|
||||
items = apply_operational_eligibility(items)
|
||||
|
||||
# Preserve the existing Operations ordering: newest first inside each
|
||||
# priority band, without allowing supporting/stale rows to set the band.
|
||||
|
||||
Reference in New Issue
Block a user