|
|
|
|
@ -204,43 +204,77 @@ def api_tickets():
|
|
|
|
|
if not re.match(r"^T\d{8}\.\d{4}$", ticket_code):
|
|
|
|
|
return jsonify({"status": "error", "message": "Invalid ticket_code format. Expected TYYYYMMDD.####."}), 400
|
|
|
|
|
|
|
|
|
|
# Ensure uniqueness
|
|
|
|
|
if Ticket.query.filter_by(ticket_code=ticket_code).first():
|
|
|
|
|
return jsonify({"status": "error", "message": "ticket_code already exists."}), 409
|
|
|
|
|
existing = Ticket.query.filter_by(ticket_code=ticket_code).first()
|
|
|
|
|
|
|
|
|
|
ticket = Ticket(
|
|
|
|
|
ticket_code=ticket_code,
|
|
|
|
|
title=None,
|
|
|
|
|
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,
|
|
|
|
|
)
|
|
|
|
|
# If the ticket already exists, link it to this job/run (a single ticket number can apply to multiple jobs).
|
|
|
|
|
if existing:
|
|
|
|
|
ticket = existing
|
|
|
|
|
try:
|
|
|
|
|
# Ensure there is at least one job scope for this ticket/job combination.
|
|
|
|
|
if job and job.id:
|
|
|
|
|
scope = TicketScope.query.filter_by(
|
|
|
|
|
ticket_id=ticket.id,
|
|
|
|
|
scope_type="job",
|
|
|
|
|
job_id=job.id,
|
|
|
|
|
).first()
|
|
|
|
|
if not scope:
|
|
|
|
|
scope = TicketScope(
|
|
|
|
|
ticket_id=ticket.id,
|
|
|
|
|
scope_type="job",
|
|
|
|
|
customer_id=job.customer_id,
|
|
|
|
|
backup_software=job.backup_software,
|
|
|
|
|
backup_type=job.backup_type,
|
|
|
|
|
job_id=job.id,
|
|
|
|
|
job_name_match=job.job_name,
|
|
|
|
|
job_name_match_mode="exact",
|
|
|
|
|
)
|
|
|
|
|
db.session.add(scope)
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
db.session.add(ticket)
|
|
|
|
|
db.session.flush()
|
|
|
|
|
# Link the ticket to this specific run (idempotent due to unique constraint).
|
|
|
|
|
existing_link = TicketJobRun.query.filter_by(ticket_id=ticket.id, job_run_id=run.id).first()
|
|
|
|
|
if not existing_link:
|
|
|
|
|
link = TicketJobRun(ticket_id=ticket.id, job_run_id=run.id, link_source="manual")
|
|
|
|
|
db.session.add(link)
|
|
|
|
|
|
|
|
|
|
# Minimal scope from job
|
|
|
|
|
scope = TicketScope(
|
|
|
|
|
ticket_id=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",
|
|
|
|
|
db.session.commit()
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
db.session.rollback()
|
|
|
|
|
return jsonify({"status": "error", "message": str(exc) or "Failed to link ticket."}), 500
|
|
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
ticket = Ticket(
|
|
|
|
|
ticket_code=ticket_code,
|
|
|
|
|
title=None,
|
|
|
|
|
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(scope)
|
|
|
|
|
|
|
|
|
|
link = TicketJobRun(ticket_id=ticket.id, job_run_id=run.id, link_source="manual")
|
|
|
|
|
db.session.add(link)
|
|
|
|
|
try:
|
|
|
|
|
db.session.add(ticket)
|
|
|
|
|
db.session.flush()
|
|
|
|
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
db.session.rollback()
|
|
|
|
|
return jsonify({"status": "error", "message": str(exc) or "Failed to create ticket."}), 500
|
|
|
|
|
# Minimal scope from job
|
|
|
|
|
scope = TicketScope(
|
|
|
|
|
ticket_id=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",
|
|
|
|
|
)
|
|
|
|
|
db.session.add(scope)
|
|
|
|
|
|
|
|
|
|
link = TicketJobRun(ticket_id=ticket.id, job_run_id=run.id, link_source="manual")
|
|
|
|
|
db.session.add(link)
|
|
|
|
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
db.session.rollback()
|
|
|
|
|
return jsonify({"status": "error", "message": str(exc) or "Failed to create ticket."}), 500
|
|
|
|
|
|
|
|
|
|
return jsonify(
|
|
|
|
|
{
|
|
|
|
|
@ -251,8 +285,8 @@ def api_tickets():
|
|
|
|
|
"description": ticket.description or "",
|
|
|
|
|
"start_date": _format_datetime(ticket.start_date),
|
|
|
|
|
"active_from_date": str(ticket.active_from_date) if getattr(ticket, "active_from_date", None) else "",
|
|
|
|
|
"resolved_at": "",
|
|
|
|
|
"active": True,
|
|
|
|
|
"resolved_at": _format_datetime(ticket.resolved_at) if getattr(ticket, "resolved_at", None) else "",
|
|
|
|
|
"active": getattr(ticket, "resolved_at", None) is None,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|