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:
parent
e6847b8ec3
commit
077e1fb176
@ -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.
|
||||
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user