diff --git a/.last-branch b/.last-branch index 76e3ecb..a30bfd0 100644 --- a/.last-branch +++ b/.last-branch @@ -1 +1 @@ -v20260115-14-autotask-runchecks-ticket-migration-fix +v20260115-15-autotask-default-ticket-status-setting diff --git a/containers/backupchecks/src/backend/app/integrations/autotask/client.py b/containers/backupchecks/src/backend/app/integrations/autotask/client.py index 4989ec1..59a62f0 100644 --- a/containers/backupchecks/src/backend/app/integrations/autotask/client.py +++ b/containers/backupchecks/src/backend/app/integrations/autotask/client.py @@ -397,6 +397,13 @@ class AutotaskClient: return self._call_picklist_values(picklist_values) + def get_ticket_statuses(self) -> List[Dict[str, Any]]: + """Return Ticket Status picklist values. + + We retrieve this from Tickets field metadata to avoid hardcoded status IDs. + """ + return self._get_ticket_picklist_values(field_names=["status", "statusid"]) + def create_ticket(self, payload: Dict[str, Any]) -> Dict[str, Any]: """Create a Ticket in Autotask. diff --git a/containers/backupchecks/src/backend/app/main/routes_settings.py b/containers/backupchecks/src/backend/app/main/routes_settings.py index 5897121..2160fb5 100644 --- a/containers/backupchecks/src/backend/app/main/routes_settings.py +++ b/containers/backupchecks/src/backend/app/main/routes_settings.py @@ -657,6 +657,7 @@ def settings(): autotask_queues = [] autotask_ticket_sources = [] autotask_priorities = [] + autotask_ticket_statuses = [] autotask_last_sync_at = getattr(settings, "autotask_reference_last_sync_at", None) try: @@ -677,6 +678,12 @@ def settings(): except Exception: autotask_priorities = [] + try: + if getattr(settings, "autotask_cached_ticket_statuses_json", None): + autotask_ticket_statuses = json.loads(settings.autotask_cached_ticket_statuses_json) or [] + except Exception: + autotask_ticket_statuses = [] + return render_template( "main/settings.html", settings=settings, @@ -692,6 +699,7 @@ def settings(): autotask_queues=autotask_queues, autotask_ticket_sources=autotask_ticket_sources, autotask_priorities=autotask_priorities, + autotask_ticket_statuses=autotask_ticket_statuses, autotask_last_sync_at=autotask_last_sync_at, news_admin_items=news_admin_items, news_admin_stats=news_admin_stats, @@ -1322,6 +1330,7 @@ def settings_autotask_refresh_reference_data(): 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) @@ -1354,6 +1363,7 @@ def settings_autotask_refresh_reference_data(): 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 = [] @@ -1377,13 +1387,13 @@ def settings_autotask_refresh_reference_data(): db.session.commit() flash( - f"Autotask reference data refreshed. Queues: {len(queues)}. Ticket Sources: {len(sources)}. Priorities: {len(pr_out)}.", + 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_refresh_reference_data", "Autotask reference data refreshed.", - details=json.dumps({"queues": len(queues or []), "ticket_sources": len(sources or []), "priorities": len(pr_out)}), + 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: flash(f"Failed to refresh Autotask reference data: {exc}", "danger") diff --git a/containers/backupchecks/src/backend/app/migrations.py b/containers/backupchecks/src/backend/app/migrations.py index f7708dc..b1d1405 100644 --- a/containers/backupchecks/src/backend/app/migrations.py +++ b/containers/backupchecks/src/backend/app/migrations.py @@ -3,31 +3,6 @@ from sqlalchemy import inspect, text from .database import db -def _get_table_columns(conn, table_name: str) -> set[str]: - """Return a set of column names for a table using the provided connection. - - Returns an empty set when the table does not exist or cannot be inspected. - - Using information_schema keeps this helper stable across SQLAlchemy - versions and avoids creating nested connections while inside begin() blocks. - """ - - try: - result = conn.execute( - text( - """ - SELECT column_name - FROM information_schema.columns - WHERE table_name = :table - """ - ), - {"table": table_name}, - ) - return {row[0] for row in result.fetchall()} - except Exception: - return set() - - def _column_exists(table_name: str, column_name: str) -> bool: """Return True if the given column exists on the given table.""" engine = db.get_engine() @@ -193,6 +168,7 @@ def migrate_system_settings_autotask_integration() -> None: ("autotask_cached_queues_json", "TEXT NULL"), ("autotask_cached_ticket_sources_json", "TEXT NULL"), ("autotask_cached_priorities_json", "TEXT NULL"), + ("autotask_cached_ticket_statuses_json", "TEXT NULL"), ("autotask_reference_last_sync_at", "TIMESTAMP NULL"), ] @@ -948,10 +924,9 @@ def migrate_job_runs_autotask_ticket_fields() -> None: return try: - with engine.begin() as conn: + with engine.connect() as conn: cols = _get_table_columns(conn, table) if not cols: - print("[migrations] job_runs table not found; skipping migrate_job_runs_autotask_ticket_fields") return if "autotask_ticket_id" not in cols: @@ -981,14 +956,12 @@ def migrate_job_runs_autotask_ticket_fields() -> None: ) except Exception as exc: print( - f"[migrations] Could not add FK job_runs_autotask_ticket_created_by_user_id -> users.id (continuing): {exc}" + f"[migrations] Could not add FK job_runs.autotask_ticket_created_by_user_id -> users.id (continuing): {exc}" ) - conn.execute( - text('CREATE INDEX IF NOT EXISTS idx_job_runs_autotask_ticket_id ON "job_runs" (autotask_ticket_id)') - ) + conn.execute(text('CREATE INDEX IF NOT EXISTS idx_job_runs_autotask_ticket_id ON "job_runs" (autotask_ticket_id)')) except Exception as exc: - print(f"[migrations] migrate_job_runs_autotask_ticket_fields failed (continuing): {exc}") + print(f"[migrations] job_runs table not found; skipping migrate_job_runs_autotask_ticket_fields: {exc}") return print("[migrations] migrate_job_runs_autotask_ticket_fields completed.") diff --git a/containers/backupchecks/src/backend/app/models.py b/containers/backupchecks/src/backend/app/models.py index 3b3d41b..4ecba7d 100644 --- a/containers/backupchecks/src/backend/app/models.py +++ b/containers/backupchecks/src/backend/app/models.py @@ -127,6 +127,7 @@ class SystemSettings(db.Model): autotask_cached_queues_json = db.Column(db.Text, nullable=True) autotask_cached_ticket_sources_json = db.Column(db.Text, nullable=True) autotask_cached_priorities_json = db.Column(db.Text, nullable=True) + autotask_cached_ticket_statuses_json = db.Column(db.Text, nullable=True) autotask_reference_last_sync_at = db.Column(db.DateTime, nullable=True) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) updated_at = db.Column( diff --git a/containers/backupchecks/src/templates/main/settings.html b/containers/backupchecks/src/templates/main/settings.html index 6c52acb..3b848e0 100644 --- a/containers/backupchecks/src/templates/main/settings.html +++ b/containers/backupchecks/src/templates/main/settings.html @@ -397,6 +397,17 @@