Add conditional ticket status update based on time entries
Implements API contract section 9: ticket resolution workflow with conditional status updates based on time entry existence. - Added query_time_entries_by_ticket_id() for POST /TimeEntries/query - update_ticket_resolution_safe() now checks time entries: * No time entries: sets status to 5 (Complete) * Has time entries: keeps current status (ticket remains open) This ensures tickets are only auto-closed when appropriate per the validated Autotask API workflow. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
88421badbd
commit
9b905fa24f
@ -559,11 +559,16 @@ class AutotaskClient:
|
|||||||
|
|
||||||
|
|
||||||
def update_ticket_resolution_safe(self, ticket_id: int, resolution_text: str) -> Dict[str, Any]:
|
def update_ticket_resolution_safe(self, ticket_id: int, resolution_text: str) -> Dict[str, Any]:
|
||||||
"""Safely update the Ticket 'resolution' field without changing status.
|
"""Safely update the Ticket 'resolution' field with conditional status update.
|
||||||
|
|
||||||
Autotask Tickets require a full PUT update; therefore we must:
|
Autotask Tickets require a full PUT update; therefore we must:
|
||||||
- GET /Tickets/{id} to retrieve current stabilising fields (including classification/routing)
|
- GET /Tickets/{id} to retrieve current stabilising fields (including classification/routing)
|
||||||
- PUT /Tickets with those stabilising fields unchanged, and only update 'resolution'
|
- Query time entries for the ticket
|
||||||
|
- PUT /Tickets with stabilising fields and conditional status
|
||||||
|
|
||||||
|
Status logic (per API contract section 9):
|
||||||
|
- If NO time entries exist: set status to 5 (Complete)
|
||||||
|
- If time entries exist: keep current status unchanged
|
||||||
|
|
||||||
IMPORTANT:
|
IMPORTANT:
|
||||||
- GET /Tickets/{id} returns the ticket object under the 'item' envelope in most tenants.
|
- GET /Tickets/{id} returns the ticket object under the 'item' envelope in most tenants.
|
||||||
@ -639,14 +644,23 @@ class AutotaskClient:
|
|||||||
"Cannot safely update ticket resolution because required fields are missing: " + ", ".join(missing)
|
"Cannot safely update ticket resolution because required fields are missing: " + ", ".join(missing)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Check for time entries as per API contract section 9
|
||||||
|
# If no time entries exist, we can set status to 5 (Complete)
|
||||||
|
# If time entries exist, status remains unchanged
|
||||||
|
time_entries = self.query_time_entries_by_ticket_id(int(ticket_id))
|
||||||
|
has_time_entries = len(time_entries) > 0
|
||||||
|
|
||||||
|
# Determine final status based on time entry check
|
||||||
|
# Status 5 = Complete (sets completedDate and resolvedDateTime)
|
||||||
|
final_status = resolved_status if has_time_entries else 5
|
||||||
|
|
||||||
# Build payload with exact values from GET response (including null if that's what we got)
|
# Build payload with exact values from GET response (including null if that's what we got)
|
||||||
payload: Dict[str, Any] = {
|
payload: Dict[str, Any] = {
|
||||||
"id": int(ticket_id),
|
"id": int(ticket_id),
|
||||||
"issueType": resolved_issue_type,
|
"issueType": resolved_issue_type,
|
||||||
"subIssueType": resolved_sub_issue_type,
|
"subIssueType": resolved_sub_issue_type,
|
||||||
"source": resolved_source,
|
"source": resolved_source,
|
||||||
# Keep status unchanged
|
"status": final_status,
|
||||||
"status": resolved_status,
|
|
||||||
"resolution": str(resolution_text or ""),
|
"resolution": str(resolution_text or ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -955,3 +969,22 @@ class AutotaskClient:
|
|||||||
if limit and isinstance(limit, int) and limit > 0:
|
if limit and isinstance(limit, int) and limit > 0:
|
||||||
return items[: int(limit)]
|
return items[: int(limit)]
|
||||||
return items
|
return items
|
||||||
|
|
||||||
|
def query_time_entries_by_ticket_id(self, ticket_id: int) -> List[Dict[str, Any]]:
|
||||||
|
"""Query TimeEntries for a specific ticket.
|
||||||
|
|
||||||
|
Uses POST /TimeEntries/query as per API contract section 6.
|
||||||
|
|
||||||
|
Returns list of time entry items. Empty list if no time entries exist.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
tid = int(ticket_id)
|
||||||
|
except Exception:
|
||||||
|
tid = 0
|
||||||
|
if tid <= 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
payload = {"filter": [{"op": "eq", "field": "ticketID", "value": tid}]}
|
||||||
|
data = self._request("POST", "TimeEntries/query", json_body=payload)
|
||||||
|
return self._as_items_list(data)
|
||||||
|
|||||||
@ -4,6 +4,13 @@ This file documents all changes made to this project via Claude Code.
|
|||||||
|
|
||||||
## [2026-02-05]
|
## [2026-02-05]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Autotask conditional ticket status update based on time entries (API contract section 9):
|
||||||
|
- `query_time_entries_by_ticket_id()` - Query time entries for a ticket via POST /TimeEntries/query
|
||||||
|
- `update_ticket_resolution_safe()` now checks for time entries and conditionally sets status:
|
||||||
|
- If NO time entries exist: sets status to 5 (Complete) with completedDate and resolvedDateTime
|
||||||
|
- If time entries exist: keeps current status unchanged (ticket remains open)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Autotask ticket resolution update now correctly preserves exact field values from GET response in PUT payload.
|
- Autotask ticket resolution update now correctly preserves exact field values from GET response in PUT payload.
|
||||||
The `issueType`, `subIssueType`, and `source` fields are copied with their exact values (including null)
|
The `issueType`, `subIssueType`, and `source` fields are copied with their exact values (including null)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user