Auto-commit local changes before build (2026-01-19 11:51:58)
This commit is contained in:
parent
36deb77806
commit
dabec03f91
@ -1 +1 @@
|
|||||||
v20260119-02-restoredto--v20260115-15-autotask-default-ticket-status-setting
|
v20260119-03-autotask-reference-data-auto-refresh
|
||||||
|
|||||||
@ -407,6 +407,8 @@ def settings():
|
|||||||
section = (request.args.get("section") or "general").strip().lower() or "general"
|
section = (request.args.get("section") or "general").strip().lower() or "general"
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
|
autotask_form_touched = any(str(k).startswith("autotask_") for k in (request.form or {}).keys())
|
||||||
|
|
||||||
# NOTE: The Settings UI has multiple tabs with separate forms.
|
# NOTE: The Settings UI has multiple tabs with separate forms.
|
||||||
# Only update values that are present in the submitted form, to avoid
|
# Only update values that are present in the submitted form, to avoid
|
||||||
# clearing unrelated settings when saving from another tab.
|
# clearing unrelated settings when saving from another tab.
|
||||||
@ -563,6 +565,48 @@ def settings():
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Settings have been saved.", "success")
|
flash("Settings have been saved.", "success")
|
||||||
|
|
||||||
|
# Autotask ticket defaults depend on reference data (queues, sources, statuses, priorities).
|
||||||
|
# When the Autotask integration is (re)configured, auto-refresh the cached reference data
|
||||||
|
# once so the dropdowns become usable immediately.
|
||||||
|
try:
|
||||||
|
if (
|
||||||
|
autotask_form_touched
|
||||||
|
and bool(getattr(settings, "autotask_enabled", False))
|
||||||
|
and bool(getattr(settings, "autotask_api_username", None))
|
||||||
|
and bool(getattr(settings, "autotask_api_password", None))
|
||||||
|
and bool(getattr(settings, "autotask_tracking_identifier", None))
|
||||||
|
):
|
||||||
|
missing_cache = (
|
||||||
|
not bool(getattr(settings, "autotask_cached_queues_json", None))
|
||||||
|
or not bool(getattr(settings, "autotask_cached_ticket_sources_json", None))
|
||||||
|
or not bool(getattr(settings, "autotask_cached_ticket_statuses_json", None))
|
||||||
|
or not bool(getattr(settings, "autotask_cached_priorities_json", None))
|
||||||
|
)
|
||||||
|
|
||||||
|
if missing_cache:
|
||||||
|
queues, sources, statuses, pr_out = _refresh_autotask_reference_data(settings)
|
||||||
|
db.session.commit()
|
||||||
|
flash(
|
||||||
|
f"Autotask reference data refreshed. Queues: {len(queues)}. Ticket Sources: {len(sources)}. Ticket Statuses: {len(statuses)}. Priorities: {len(pr_out)}.",
|
||||||
|
"success",
|
||||||
|
)
|
||||||
|
_log_admin_event(
|
||||||
|
"autotask_reference_data_auto_refreshed",
|
||||||
|
"Autotask reference data auto-refreshed after settings save.",
|
||||||
|
details=json.dumps({"queues": len(queues or []), "ticket_sources": len(sources or []), "ticket_statuses": len(statuses or []), "priorities": len(pr_out)}),
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
try:
|
||||||
|
db.session.rollback()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
flash(f"Autotask reference data refresh failed: {exc}", "warning")
|
||||||
|
_log_admin_event(
|
||||||
|
"autotask_reference_data_auto_refresh_failed",
|
||||||
|
"Autotask reference data auto-refresh failed after settings save.",
|
||||||
|
details=json.dumps({"error": str(exc)}),
|
||||||
|
)
|
||||||
|
|
||||||
# If EML storage has been turned off, clear any stored blobs immediately.
|
# If EML storage has been turned off, clear any stored blobs immediately.
|
||||||
try:
|
try:
|
||||||
if getattr(settings, "ingest_eml_retention_days", 7) == 0:
|
if getattr(settings, "ingest_eml_retention_days", 7) == 0:
|
||||||
@ -1308,6 +1352,77 @@ def settings_autotask_test_connection():
|
|||||||
return redirect(url_for("main.settings", section="integrations"))
|
return redirect(url_for("main.settings", section="integrations"))
|
||||||
|
|
||||||
|
|
||||||
|
def _refresh_autotask_reference_data(settings):
|
||||||
|
"""Refresh and persist Autotask reference data used for ticket default dropdowns."""
|
||||||
|
from ..integrations.autotask.client import AutotaskClient
|
||||||
|
|
||||||
|
client = AutotaskClient(
|
||||||
|
username=settings.autotask_api_username,
|
||||||
|
password=settings.autotask_api_password,
|
||||||
|
api_integration_code=settings.autotask_tracking_identifier,
|
||||||
|
environment=(settings.autotask_environment or "production"),
|
||||||
|
)
|
||||||
|
|
||||||
|
queues = client.get_queues()
|
||||||
|
sources = client.get_ticket_sources()
|
||||||
|
priorities = client.get_ticket_priorities()
|
||||||
|
statuses = client.get_ticket_statuses()
|
||||||
|
|
||||||
|
# Store a minimal subset for dropdowns (id + name/label)
|
||||||
|
# Note: Some "reference" values are exposed as picklists (value/label)
|
||||||
|
# instead of entity collections (id/name). We normalize both shapes.
|
||||||
|
def _norm(items):
|
||||||
|
out = []
|
||||||
|
for it in items or []:
|
||||||
|
if not isinstance(it, dict):
|
||||||
|
continue
|
||||||
|
_id = it.get("id")
|
||||||
|
if _id is None:
|
||||||
|
_id = it.get("value")
|
||||||
|
|
||||||
|
name = (
|
||||||
|
it.get("name")
|
||||||
|
or it.get("label")
|
||||||
|
or it.get("queueName")
|
||||||
|
or it.get("sourceName")
|
||||||
|
or it.get("description")
|
||||||
|
or ""
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
_id_int = int(_id)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
out.append({"id": _id_int, "name": str(name)})
|
||||||
|
# Sort by name for stable dropdowns
|
||||||
|
out.sort(key=lambda x: (x.get("name") or "").lower())
|
||||||
|
return out
|
||||||
|
|
||||||
|
settings.autotask_cached_queues_json = json.dumps(_norm(queues))
|
||||||
|
settings.autotask_cached_ticket_sources_json = json.dumps(_norm(sources))
|
||||||
|
settings.autotask_cached_ticket_statuses_json = json.dumps(_norm(statuses))
|
||||||
|
|
||||||
|
# Priorities are returned as picklist values (value/label)
|
||||||
|
pr_out = []
|
||||||
|
for it in priorities or []:
|
||||||
|
if not isinstance(it, dict):
|
||||||
|
continue
|
||||||
|
if it.get("isActive") is False:
|
||||||
|
continue
|
||||||
|
val = it.get("value")
|
||||||
|
label = it.get("label") or it.get("name") or ""
|
||||||
|
try:
|
||||||
|
val_int = int(val)
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
pr_out.append({"id": val_int, "name": str(label)})
|
||||||
|
pr_out.sort(key=lambda x: (x.get("name") or "").lower())
|
||||||
|
|
||||||
|
settings.autotask_cached_priorities_json = json.dumps(pr_out)
|
||||||
|
settings.autotask_reference_last_sync_at = datetime.utcnow()
|
||||||
|
|
||||||
|
return queues, sources, statuses, pr_out
|
||||||
|
|
||||||
|
|
||||||
@main_bp.route("/settings/autotask/refresh-reference-data", methods=["POST"])
|
@main_bp.route("/settings/autotask/refresh-reference-data", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@roles_required("admin")
|
@roles_required("admin")
|
||||||
@ -1319,71 +1434,7 @@ def settings_autotask_refresh_reference_data():
|
|||||||
return redirect(url_for("main.settings", section="integrations"))
|
return redirect(url_for("main.settings", section="integrations"))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from ..integrations.autotask.client import AutotaskClient
|
queues, sources, statuses, pr_out = _refresh_autotask_reference_data(settings)
|
||||||
client = AutotaskClient(
|
|
||||||
username=settings.autotask_api_username,
|
|
||||||
password=settings.autotask_api_password,
|
|
||||||
api_integration_code=settings.autotask_tracking_identifier,
|
|
||||||
environment=(settings.autotask_environment or "production"),
|
|
||||||
)
|
|
||||||
|
|
||||||
queues = client.get_queues()
|
|
||||||
sources = client.get_ticket_sources()
|
|
||||||
priorities = client.get_ticket_priorities()
|
|
||||||
statuses = client.get_ticket_statuses()
|
|
||||||
|
|
||||||
# Store a minimal subset for dropdowns (id + name/label)
|
|
||||||
# Note: Some "reference" values are exposed as picklists (value/label)
|
|
||||||
# instead of entity collections (id/name). We normalize both shapes.
|
|
||||||
def _norm(items):
|
|
||||||
out = []
|
|
||||||
for it in items or []:
|
|
||||||
if not isinstance(it, dict):
|
|
||||||
continue
|
|
||||||
_id = it.get("id")
|
|
||||||
if _id is None:
|
|
||||||
_id = it.get("value")
|
|
||||||
|
|
||||||
name = (
|
|
||||||
it.get("name")
|
|
||||||
or it.get("label")
|
|
||||||
or it.get("queueName")
|
|
||||||
or it.get("sourceName")
|
|
||||||
or it.get("description")
|
|
||||||
or ""
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
_id_int = int(_id)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
out.append({"id": _id_int, "name": str(name)})
|
|
||||||
# Sort by name for stable dropdowns
|
|
||||||
out.sort(key=lambda x: (x.get("name") or "").lower())
|
|
||||||
return out
|
|
||||||
|
|
||||||
settings.autotask_cached_queues_json = json.dumps(_norm(queues))
|
|
||||||
settings.autotask_cached_ticket_sources_json = json.dumps(_norm(sources))
|
|
||||||
settings.autotask_cached_ticket_statuses_json = json.dumps(_norm(statuses))
|
|
||||||
|
|
||||||
# Priorities are returned as picklist values (value/label)
|
|
||||||
pr_out = []
|
|
||||||
for it in priorities or []:
|
|
||||||
if not isinstance(it, dict):
|
|
||||||
continue
|
|
||||||
if it.get("isActive") is False:
|
|
||||||
continue
|
|
||||||
val = it.get("value")
|
|
||||||
label = it.get("label") or it.get("name") or ""
|
|
||||||
try:
|
|
||||||
val_int = int(val)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
pr_out.append({"id": val_int, "name": str(label)})
|
|
||||||
pr_out.sort(key=lambda x: (x.get("name") or "").lower())
|
|
||||||
|
|
||||||
settings.autotask_cached_priorities_json = json.dumps(pr_out)
|
|
||||||
settings.autotask_reference_last_sync_at = datetime.utcnow()
|
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
flash(
|
flash(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user