Add cross-company ticket search for overarching issues

The "Link existing ticket" dialog now searches across all companies when
a ticket number is entered, enabling linking of overarching issues.

Changes:
- Added query_tickets_by_number() to Autotask client
- Route searches both customer's company and cross-company when ticket number detected
- Results are combined and deduplicated (customer tickets shown first)
- Enables linking multi-company infrastructure issues to any job

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Ivo Oskamp 2026-02-05 17:01:43 +01:00
parent e6847b8ec3
commit 077e1fb176
3 changed files with 87 additions and 2 deletions

View File

@ -970,6 +970,47 @@ class AutotaskClient:
return items[: int(limit)]
return items
def query_tickets_by_number(
self,
ticket_number: str,
*,
exclude_status_ids: Optional[List[int]] = None,
limit: int = 10,
) -> List[Dict[str, Any]]:
"""Query Tickets by ticket number across all companies.
Uses POST /Tickets/query.
This is useful for linking overarching issues that span multiple companies.
"""
tnum = (ticket_number or "").strip()
if not tnum:
return []
flt: List[Dict[str, Any]] = [
{"op": "eq", "field": "ticketNumber", "value": tnum},
]
ex: List[int] = []
for x in exclude_status_ids or []:
try:
v = int(x)
except Exception:
continue
if v > 0:
ex.append(v)
if ex:
flt.append({"op": "notIn", "field": "status", "value": ex})
data = self._request("POST", "Tickets/query", json_body={"filter": flt})
items = self._as_items_list(data)
# Respect limit if tenant returns more.
if limit and isinstance(limit, int) and limit > 0:
return items[: int(limit)]
return items
def query_time_entries_by_ticket_id(self, ticket_id: int) -> List[Dict[str, Any]]:
"""Query TimeEntries for a specific ticket.

View File

@ -1576,6 +1576,11 @@ def api_run_checks_autotask_existing_tickets():
"""List open (non-terminal) Autotask tickets for the selected run's customer.
Phase 2.2: used by the Run Checks modal to link an existing PSA ticket.
Search behaviour:
- Always searches tickets for the customer's company
- If search term looks like a ticket number (starts with T + digits), also searches
across all companies to enable linking overarching issues
"""
try:
@ -1640,20 +1645,43 @@ def api_run_checks_autotask_existing_tickets():
# Best-effort; list will still work without labels.
pass
# First: query tickets for this customer's company
tickets = client.query_tickets_for_company(
int(customer.autotask_company_id),
search=q,
exclude_status_ids=sorted(AUTOTASK_TERMINAL_STATUS_IDS),
limit=75,
)
# Second: if search looks like a ticket number, also search across all companies
# This allows linking overarching issues that span multiple companies
cross_company_tickets = []
if q and q.upper().startswith("T") and any(ch.isdigit() for ch in q):
try:
cross_company_tickets = client.query_tickets_by_number(
q,
exclude_status_ids=sorted(AUTOTASK_TERMINAL_STATUS_IDS),
limit=10,
)
except Exception:
# Best-effort; main company query already succeeded
pass
except Exception as exc:
return jsonify({"status": "error", "message": f"Autotask ticket lookup failed: {exc}"}), 400
# Combine and deduplicate results
seen_ids = set()
items = []
for t in tickets or []:
def add_ticket(t):
if not isinstance(t, dict):
continue
return
tid = t.get("id")
if tid in seen_ids:
return
seen_ids.add(tid)
tnum = (t.get("ticketNumber") or t.get("number") or "")
title = (t.get("title") or "")
st = t.get("status")
@ -1672,6 +1700,14 @@ def api_run_checks_autotask_existing_tickets():
}
)
# Add company tickets first (primary results)
for t in tickets or []:
add_ticket(t)
# Then add cross-company tickets (secondary results for ticket number search)
for t in cross_company_tickets or []:
add_ticket(t)
# Sort: newest-ish first. Autotask query ordering isn't guaranteed, so we provide a stable sort.
items.sort(key=lambda x: (x.get("ticketNumber") or ""), reverse=True)

View File

@ -4,6 +4,14 @@ This file documents all changes made to this project via Claude Code.
## [2026-02-05]
### Added
- Autotask "Link existing ticket" now supports cross-company ticket search:
- Added `query_tickets_by_number()` to search tickets by number across all companies
- When searching with a ticket number (e.g., "T20260205.0001"), results include:
- Tickets from the customer's company (primary results)
- Matching tickets from other companies (for overarching issues)
- Enables linking tickets for multi-company infrastructure issues
### Changed
- Autotask resolve confirmation and note messages now correctly indicate ticket closure status:
- Frontend confirmation dialog explains conditional closure based on time entries