diff --git a/.last-branch b/.last-branch index 0020da5..fabe856 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260116-02-runchecks-autotask-create-refresh +v20260116-03-autotask-ticket-linking-visibility diff --git a/containers/backupchecks/src/backend/app/main/routes_run_checks.py b/containers/backupchecks/src/backend/app/main/routes_run_checks.py index b66350c..7dc5b83 100644 --- a/containers/backupchecks/src/backend/app/main/routes_run_checks.py +++ b/containers/backupchecks/src/backend/app/main/routes_run_checks.py @@ -32,6 +32,9 @@ from ..models import ( JobRunReviewEvent, MailMessage, MailObject, + Ticket, + TicketJobRun, + TicketScope, Override, User, ) @@ -1008,23 +1011,84 @@ def api_run_checks_create_autotask_ticket(): except Exception as exc: return jsonify({"status": "error", "message": f"Autotask ticket creation failed: {exc}"}), 400 - ticket_id = created.get("id") if isinstance(created, dict) else None - ticket_number = None + ticket_id = None if isinstance(created, dict): - ticket_number = created.get("ticketNumber") or created.get("number") or created.get("ticket_number") + ticket_id = created.get("id") or created.get("itemId") or created.get("ticketId") + try: + # Some wrappers return {"item": {"id": ...}} + if not ticket_id and isinstance(created.get("item"), dict): + ticket_id = created.get("item", {}).get("id") + except Exception: + pass if not ticket_id: return jsonify({"status": "error", "message": "Autotask did not return a ticket id."}), 400 + # Autotask typically does not return the ticket number on create. + # Always fetch the created ticket so we can persist the ticketNumber for UI and internal linking. + ticket_number = None + try: + fetched = client.get_ticket(int(ticket_id)) + if isinstance(fetched, dict) and isinstance(fetched.get("item"), dict): + fetched = fetched.get("item") + if isinstance(fetched, dict): + ticket_number = fetched.get("ticketNumber") or fetched.get("number") or fetched.get("ticket_number") + except Exception: + ticket_number = None + try: run.autotask_ticket_id = int(ticket_id) except Exception: run.autotask_ticket_id = None + now = datetime.utcnow() run.autotask_ticket_number = (str(ticket_number).strip() if ticket_number is not None else "") or None - run.autotask_ticket_created_at = datetime.utcnow() + run.autotask_ticket_created_at = now run.autotask_ticket_created_by_user_id = current_user.id + # Also store an internal Ticket record and link it to the run. + # This keeps Tickets/Remarks, Job Details, and Run Checks indicators consistent with the existing manual workflow. + internal_ticket = None + if run.autotask_ticket_number: + ticket_code = (run.autotask_ticket_number or "").strip().upper() + internal_ticket = Ticket.query.filter_by(ticket_code=ticket_code).first() + if not internal_ticket: + internal_ticket = Ticket( + ticket_code=ticket_code, + title=subject, + description=description, + active_from_date=_to_amsterdam_date(run.run_at) or _to_amsterdam_date(now) or now.date(), + start_date=now, + resolved_at=None, + ) + db.session.add(internal_ticket) + db.session.flush() + + # Ensure a job scope exists (used by popups / job details / tickets page) + scope = None + if job and job.id and internal_ticket and internal_ticket.id: + scope = TicketScope.query.filter_by(ticket_id=internal_ticket.id, scope_type="job", job_id=job.id).first() + if not scope and internal_ticket and internal_ticket.id: + scope = TicketScope( + ticket_id=internal_ticket.id, + scope_type="job", + customer_id=job.customer_id if job else None, + backup_software=job.backup_software if job else None, + backup_type=job.backup_type if job else None, + job_id=job.id if job else None, + job_name_match=job.job_name if job else None, + job_name_match_mode="exact", + resolved_at=None, + ) + db.session.add(scope) + elif scope: + scope.resolved_at = None + + # Link ticket to this job run (idempotent) + if internal_ticket and internal_ticket.id and run.id: + if not TicketJobRun.query.filter_by(ticket_id=internal_ticket.id, job_run_id=run.id).first(): + db.session.add(TicketJobRun(ticket_id=internal_ticket.id, job_run_id=run.id, link_source="autotask")) + try: db.session.add(run) db.session.commit() diff --git a/docs/changelog.md b/docs/changelog.md index 2bd6da5..7c8a16a 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -139,11 +139,19 @@ Changes: ## v20260116-02-runchecks-autotask-create-refresh -Changes: +### Changes: - Fixed a JavaScript error in the Run Checks view where a non-existent renderModal() function was called after creating an Autotask ticket. - Replaced the renderModal() call with renderRun() to properly refresh the Run Checks modal state. - Ensured the Autotask ticket status is updated in the UI without throwing a frontend error. +## v20260116-03-autotask-ticket-linking-visibility + +### Changes: +- Ensured Autotask tickets created via Run Checks are stored as internal Ticket records instead of only external references. +- Linked created Autotask tickets to the corresponding Job Run so they appear in Tickets/Remarks. +- Added proper ticket association to Job Details, matching the behaviour of manually entered tickets. +- Updated the Run Checks view to show the ticket indicator when an Autotask ticket is linked to a run. + *** ## v0.1.21