Auto-commit local changes before build (2026-01-16 13:44:34)
This commit is contained in:
parent
25d1962f7b
commit
ef8d12065b
@ -1 +1 @@
|
|||||||
v20260116-07-autotask-ticket-link-all-runs-ticketjobrun-fix
|
v20260116-08-autotask-ticket-backfill-ticketjobrun
|
||||||
|
|||||||
@ -1000,15 +1000,149 @@ def api_run_checks_create_autotask_ticket():
|
|||||||
run = JobRun.query.get(run_id)
|
run = JobRun.query.get(run_id)
|
||||||
if not run:
|
if not run:
|
||||||
return jsonify({"status": "error", "message": "Run not found."}), 404
|
return jsonify({"status": "error", "message": "Run not found."}), 404
|
||||||
|
# Idempotent: if already created, ensure internal Ticket/JobRun links exist and return existing linkage.
|
||||||
# Idempotent: if already created, return existing linkage.
|
|
||||||
if getattr(run, "autotask_ticket_id", None):
|
if getattr(run, "autotask_ticket_id", None):
|
||||||
|
linked_run_ids: list[int] = []
|
||||||
|
try:
|
||||||
|
rows = (
|
||||||
|
JobRun.query.filter(JobRun.job_id == run.job_id)
|
||||||
|
.filter(JobRun.reviewed_at.is_(None))
|
||||||
|
.with_entities(JobRun.id)
|
||||||
|
.order_by(JobRun.id.asc())
|
||||||
|
.all()
|
||||||
|
)
|
||||||
|
linked_run_ids = [int(rid) for (rid,) in rows if rid is not None]
|
||||||
|
except Exception:
|
||||||
|
linked_run_ids = []
|
||||||
|
|
||||||
|
# Safety: always include the explicitly selected run.
|
||||||
|
try:
|
||||||
|
if run.id and int(run.id) not in linked_run_ids:
|
||||||
|
linked_run_ids.append(int(run.id))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Best-effort: backfill internal Ticket + TicketScope + TicketJobRun links so Tickets/Remarks and Job Details stay consistent.
|
||||||
|
try:
|
||||||
|
ticket_code = (getattr(run, "autotask_ticket_number", None) or "").strip().upper()
|
||||||
|
if ticket_code:
|
||||||
|
job = Job.query.get(run.job_id)
|
||||||
|
customer = Customer.query.get(job.customer_id) if job and getattr(job, "customer_id", None) else None
|
||||||
|
|
||||||
|
# Compose a sensible title/description (best-effort).
|
||||||
|
subject = None
|
||||||
|
description = None
|
||||||
|
try:
|
||||||
|
status_display = run.status or "-"
|
||||||
|
try:
|
||||||
|
status_display, _, _, _ov_id, _ov_reason = _apply_overrides_to_run(job, run)
|
||||||
|
except Exception:
|
||||||
|
status_display = run.status or "-"
|
||||||
|
|
||||||
|
cname = getattr(customer, "name", None) or ""
|
||||||
|
jname = getattr(job, "job_name", None) or ""
|
||||||
|
subject = f"[Backupchecks] {cname} - {jname} - {status_display}"
|
||||||
|
|
||||||
|
settings = _get_or_create_settings()
|
||||||
|
msg = MailMessage.query.get(run.mail_message_id) if run.mail_message_id else None
|
||||||
|
overall_message = (getattr(msg, "overall_message", None) or "") if msg else ""
|
||||||
|
|
||||||
|
objects_payload: list[dict[str, str]] = []
|
||||||
|
try:
|
||||||
|
objs = run.objects.order_by(JobObject.object_name.asc()).all()
|
||||||
|
except Exception:
|
||||||
|
objs = list(run.objects or [])
|
||||||
|
for o in objs or []:
|
||||||
|
objects_payload.append(
|
||||||
|
{
|
||||||
|
"name": getattr(o, "object_name", "") or "",
|
||||||
|
"type": getattr(o, "object_type", "") or "",
|
||||||
|
"status": getattr(o, "status", "") or "",
|
||||||
|
"error_message": getattr(o, "error_message", "") or "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (not objects_payload) and msg:
|
||||||
|
try:
|
||||||
|
mos = MailObject.query.filter_by(mail_message_id=msg.id).order_by(MailObject.object_name.asc()).all()
|
||||||
|
except Exception:
|
||||||
|
mos = []
|
||||||
|
for mo in mos or []:
|
||||||
|
objects_payload.append(
|
||||||
|
{
|
||||||
|
"name": getattr(mo, "object_name", "") or "",
|
||||||
|
"type": getattr(mo, "object_type", "") or "",
|
||||||
|
"status": getattr(mo, "status", "") or "",
|
||||||
|
"error_message": getattr(mo, "error_message", "") or "",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
description = _compose_autotask_ticket_description(
|
||||||
|
settings=settings,
|
||||||
|
job=job,
|
||||||
|
run=run,
|
||||||
|
status_display=status_display,
|
||||||
|
overall_message=overall_message,
|
||||||
|
objects_payload=objects_payload,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
subject = subject or f"[Backupchecks] Autotask {ticket_code}"
|
||||||
|
description = description or ""
|
||||||
|
|
||||||
|
internal_ticket = Ticket.query.filter_by(ticket_code=ticket_code).first()
|
||||||
|
if not internal_ticket:
|
||||||
|
now = datetime.utcnow()
|
||||||
|
internal_ticket = Ticket(
|
||||||
|
ticket_code=ticket_code,
|
||||||
|
title=subject,
|
||||||
|
description=description or "",
|
||||||
|
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()
|
||||||
|
else:
|
||||||
|
# Keep it active if it was previously resolved globally.
|
||||||
|
if internal_ticket.resolved_at is not None:
|
||||||
|
internal_ticket.resolved_at = None
|
||||||
|
|
||||||
|
# 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 all relevant job runs (idempotent)
|
||||||
|
for rid in linked_run_ids or []:
|
||||||
|
if not TicketJobRun.query.filter_by(ticket_id=internal_ticket.id, job_run_id=rid).first():
|
||||||
|
db.session.add(TicketJobRun(ticket_id=internal_ticket.id, job_run_id=rid, link_source="autotask"))
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
except Exception:
|
||||||
|
db.session.rollback()
|
||||||
|
|
||||||
return jsonify(
|
return jsonify(
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"ticket_id": int(run.autotask_ticket_id),
|
"ticket_id": int(run.autotask_ticket_id),
|
||||||
"ticket_number": getattr(run, "autotask_ticket_number", None) or "",
|
"ticket_number": getattr(run, "autotask_ticket_number", None) or "",
|
||||||
"already_exists": True,
|
"already_exists": True,
|
||||||
|
"linked_run_ids": linked_run_ids or [],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -189,6 +189,14 @@ Changes:
|
|||||||
- Made the list of runs to link deterministic by collecting run IDs first, then applying both run field updates and internal ticket linking across that stable set
|
- Made the list of runs to link deterministic by collecting run IDs first, then applying both run field updates and internal ticket linking across that stable set
|
||||||
- No changes made to polling logic or PSA status interpretation
|
- No changes made to polling logic or PSA status interpretation
|
||||||
|
|
||||||
|
## v20260116-08-autotask-ticket-backfill-ticketjobrun
|
||||||
|
|
||||||
|
- Fixed inconsistent ticket linking when creating Autotask tickets from the Run Checks page.
|
||||||
|
- Ensured that newly created Autotask tickets are linked to all related job runs, not only the selected run.
|
||||||
|
- Backfilled ticket-to-run associations so tickets appear correctly in the Tickets overview.
|
||||||
|
- Corrected Job Details visibility so open runs linked to the same ticket now display the ticket number consistently.
|
||||||
|
- Aligned Run Checks, Tickets, and Job Details views to use the same ticket-jobrun linkage logic.
|
||||||
|
|
||||||
***
|
***
|
||||||
|
|
||||||
## v0.1.21
|
## v0.1.21
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user