Compare commits
4 Commits
a555dc9c61
...
f27e956b6f
| Author | SHA1 | Date | |
|---|---|---|---|
| f27e956b6f | |||
| 95b78157ad | |||
| 17b64d1f66 | |||
| a2895b6409 |
@ -482,7 +482,15 @@ def settings():
|
|||||||
|
|
||||||
if "autotask_default_ticket_status" in request.form:
|
if "autotask_default_ticket_status" in request.form:
|
||||||
try:
|
try:
|
||||||
settings.autotask_default_ticket_status = int(request.form.get("autotask_default_ticket_status") or 0) or None
|
form_value = request.form.get("autotask_default_ticket_status", "").strip()
|
||||||
|
if form_value: # Only update if a value was actually selected
|
||||||
|
settings.autotask_default_ticket_status = int(form_value)
|
||||||
|
elif form_value == "" and settings.autotask_default_ticket_status is not None:
|
||||||
|
# If explicitly cleared (empty string submitted) and was previously set,
|
||||||
|
# allow clearing only if reference data is loaded (dropdown has options)
|
||||||
|
if getattr(settings, "autotask_cached_ticket_statuses_json", None):
|
||||||
|
settings.autotask_default_ticket_status = None
|
||||||
|
# Otherwise: keep existing value (prevents accidental clearing when dropdown is empty)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -715,6 +723,34 @@ def settings():
|
|||||||
autotask_ticket_statuses = []
|
autotask_ticket_statuses = []
|
||||||
autotask_last_sync_at = getattr(settings, "autotask_reference_last_sync_at", None)
|
autotask_last_sync_at = getattr(settings, "autotask_reference_last_sync_at", None)
|
||||||
|
|
||||||
|
# Auto-load reference data on page load if Autotask is enabled but cache is empty
|
||||||
|
try:
|
||||||
|
if (
|
||||||
|
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 getattr(settings, "autotask_cached_queues_json", None)
|
||||||
|
or not getattr(settings, "autotask_cached_ticket_sources_json", None)
|
||||||
|
or not getattr(settings, "autotask_cached_ticket_statuses_json", None)
|
||||||
|
or not getattr(settings, "autotask_cached_priorities_json", None)
|
||||||
|
)
|
||||||
|
if missing_cache:
|
||||||
|
queues_loaded, sources_loaded, statuses_loaded, priorities_loaded = _refresh_autotask_reference_data(settings)
|
||||||
|
db.session.commit()
|
||||||
|
flash(
|
||||||
|
f"Autotask reference data auto-loaded. Queues: {len(queues_loaded)}. Ticket Sources: {len(sources_loaded)}. Ticket Statuses: {len(statuses_loaded)}. Priorities: {len(priorities_loaded)}.",
|
||||||
|
"info",
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
try:
|
||||||
|
db.session.rollback()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
flash(f"Failed to auto-load Autotask reference data: {exc}", "warning")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if getattr(settings, "autotask_cached_queues_json", None):
|
if getattr(settings, "autotask_cached_queues_json", None):
|
||||||
autotask_queues = json.loads(settings.autotask_cached_queues_json) or []
|
autotask_queues = json.loads(settings.autotask_cached_queues_json) or []
|
||||||
|
|||||||
@ -342,7 +342,7 @@
|
|||||||
|
|
||||||
|
|
||||||
{% if section == 'integrations' %}
|
{% if section == 'integrations' %}
|
||||||
<form method="post" class="mb-4">
|
<form method="post" class="mb-4" id="autotask-settings-form">
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">Autotask</div>
|
<div class="card-header">Autotask</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
@ -353,9 +353,9 @@
|
|||||||
|
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label for="autotask_environment" class="form-label">Environment</label>
|
<label for="autotask_environment" class="form-label">Environment <span class="text-danger">*</span></label>
|
||||||
<select class="form-select" id="autotask_environment" name="autotask_environment">
|
<select class="form-select" id="autotask_environment" name="autotask_environment" required>
|
||||||
<option value="" {% if not settings.autotask_environment %}selected{% endif %}>Select...</option>
|
<option value="">Select...</option>
|
||||||
<option value="sandbox" {% if settings.autotask_environment == 'sandbox' %}selected{% endif %}>Sandbox</option>
|
<option value="sandbox" {% if settings.autotask_environment == 'sandbox' %}selected{% endif %}>Sandbox</option>
|
||||||
<option value="production" {% if settings.autotask_environment == 'production' %}selected{% endif %}>Production</option>
|
<option value="production" {% if settings.autotask_environment == 'production' %}selected{% endif %}>Production</option>
|
||||||
</select>
|
</select>
|
||||||
@ -363,44 +363,51 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label for="autotask_api_username" class="form-label">API Username</label>
|
<label for="autotask_api_username" class="form-label">API Username <span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="autotask_api_username" name="autotask_api_username" value="{{ settings.autotask_api_username or '' }}" />
|
<input type="text" class="form-control" id="autotask_api_username" name="autotask_api_username" value="{{ settings.autotask_api_username or '' }}" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label for="autotask_api_password" class="form-label">API Password</label>
|
<label for="autotask_api_password" class="form-label">API Password {% if not has_autotask_password %}<span class="text-danger">*</span>{% endif %}</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
id="autotask_api_password"
|
id="autotask_api_password"
|
||||||
name="autotask_api_password"
|
name="autotask_api_password"
|
||||||
placeholder="{% if has_autotask_password %}******** (stored){% else %}enter password{% endif %}"
|
placeholder="{% if has_autotask_password %}******** (stored){% else %}enter password{% endif %}"
|
||||||
|
{% if not has_autotask_password %}required{% endif %}
|
||||||
/>
|
/>
|
||||||
<div class="form-text">Leave empty to keep the existing password.</div>
|
<div class="form-text">Leave empty to keep the existing password.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="autotask_tracking_identifier" class="form-label">Tracking Identifier (Integration Code)</label>
|
<label for="autotask_tracking_identifier" class="form-label">Tracking Identifier (Integration Code) <span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="autotask_tracking_identifier" name="autotask_tracking_identifier" value="{{ settings.autotask_tracking_identifier or '' }}" />
|
<input type="text" class="form-control" id="autotask_tracking_identifier" name="autotask_tracking_identifier" value="{{ settings.autotask_tracking_identifier or '' }}" required />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="autotask_base_url" class="form-label">Backupchecks Base URL</label>
|
<label for="autotask_base_url" class="form-label">Backupchecks Base URL <span class="text-danger">*</span></label>
|
||||||
<input type="text" class="form-control" id="autotask_base_url" name="autotask_base_url" value="{{ settings.autotask_base_url or '' }}" placeholder="https://backupchecks.example.com" />
|
<input type="url" class="form-control" id="autotask_base_url" name="autotask_base_url" value="{{ settings.autotask_base_url or '' }}" placeholder="https://backupchecks.example.com" required />
|
||||||
<div class="form-text">Required later for creating stable links to Job Details pages.</div>
|
<div class="form-text">Required later for creating stable links to Job Details pages.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end mt-3">
|
||||||
|
<button type="submit" class="btn btn-primary">Save Autotask Settings</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<form method="post" class="mb-4" id="ticket-defaults-form">
|
||||||
<div class="card mb-3">
|
<div class="card mb-3">
|
||||||
<div class="card-header">Ticket defaults</div>
|
<div class="card-header">Ticket defaults</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="autotask_default_queue_id" class="form-label">Default Queue</label>
|
<label for="autotask_default_queue_id" class="form-label">Default Queue <span class="text-danger">*</span></label>
|
||||||
<select class="form-select" id="autotask_default_queue_id" name="autotask_default_queue_id">
|
<select class="form-select" id="autotask_default_queue_id" name="autotask_default_queue_id" required>
|
||||||
<option value="" {% if not settings.autotask_default_queue_id %}selected{% endif %}>Select...</option>
|
<option value="">Select...</option>
|
||||||
{% for q in autotask_queues %}
|
{% for q in autotask_queues %}
|
||||||
<option value="{{ q.id }}" {% if settings.autotask_default_queue_id == q.id %}selected{% endif %}>{{ q.name }}</option>
|
<option value="{{ q.id }}" {% if settings.autotask_default_queue_id == q.id %}selected{% endif %}>{{ q.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -409,9 +416,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="autotask_default_ticket_source_id" class="form-label">Ticket Source</label>
|
<label for="autotask_default_ticket_source_id" class="form-label">Ticket Source <span class="text-danger">*</span></label>
|
||||||
<select class="form-select" id="autotask_default_ticket_source_id" name="autotask_default_ticket_source_id">
|
<select class="form-select" id="autotask_default_ticket_source_id" name="autotask_default_ticket_source_id" required>
|
||||||
<option value="" {% if not settings.autotask_default_ticket_source_id %}selected{% endif %}>Select...</option>
|
<option value="">Select...</option>
|
||||||
{% for s in autotask_ticket_sources %}
|
{% for s in autotask_ticket_sources %}
|
||||||
<option value="{{ s.id }}" {% if settings.autotask_default_ticket_source_id == s.id %}selected{% endif %}>{{ s.name }}</option>
|
<option value="{{ s.id }}" {% if settings.autotask_default_ticket_source_id == s.id %}selected{% endif %}>{{ s.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -420,9 +427,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="autotask_default_ticket_status" class="form-label">Default Ticket Status</label>
|
<label for="autotask_default_ticket_status" class="form-label">Default Ticket Status <span class="text-danger">*</span></label>
|
||||||
<select class="form-select" id="autotask_default_ticket_status" name="autotask_default_ticket_status">
|
<select class="form-select" id="autotask_default_ticket_status" name="autotask_default_ticket_status" required>
|
||||||
<option value="" {% if not settings.autotask_default_ticket_status %}selected{% endif %}>Select...</option>
|
<option value="">Select...</option>
|
||||||
{% for st in autotask_ticket_statuses %}
|
{% for st in autotask_ticket_statuses %}
|
||||||
<option value="{{ st.id }}" {% if settings.autotask_default_ticket_status == st.id %}selected{% endif %}>{{ st.name }}</option>
|
<option value="{{ st.id }}" {% if settings.autotask_default_ticket_status == st.id %}selected{% endif %}>{{ st.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -431,9 +438,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="autotask_priority_warning" class="form-label">Priority for Warning</label>
|
<label for="autotask_priority_warning" class="form-label">Priority for Warning <span class="text-danger">*</span></label>
|
||||||
<select class="form-select" id="autotask_priority_warning" name="autotask_priority_warning">
|
<select class="form-select" id="autotask_priority_warning" name="autotask_priority_warning" required>
|
||||||
<option value="" {% if not settings.autotask_priority_warning %}selected{% endif %}>Select...</option>
|
<option value="">Select...</option>
|
||||||
{% for p in autotask_priorities %}
|
{% for p in autotask_priorities %}
|
||||||
<option value="{{ p.id }}" {% if settings.autotask_priority_warning == p.id %}selected{% endif %}>{{ p.name }}</option>
|
<option value="{{ p.id }}" {% if settings.autotask_priority_warning == p.id %}selected{% endif %}>{{ p.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -442,9 +449,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="autotask_priority_error" class="form-label">Priority for Error</label>
|
<label for="autotask_priority_error" class="form-label">Priority for Error <span class="text-danger">*</span></label>
|
||||||
<select class="form-select" id="autotask_priority_error" name="autotask_priority_error">
|
<select class="form-select" id="autotask_priority_error" name="autotask_priority_error" required>
|
||||||
<option value="" {% if not settings.autotask_priority_error %}selected{% endif %}>Select...</option>
|
<option value="">Select...</option>
|
||||||
{% for p in autotask_priorities %}
|
{% for p in autotask_priorities %}
|
||||||
<option value="{{ p.id }}" {% if settings.autotask_priority_error == p.id %}selected{% endif %}>{{ p.name }}</option>
|
<option value="{{ p.id }}" {% if settings.autotask_priority_error == p.id %}selected{% endif %}>{{ p.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -453,11 +460,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-text mt-2">Priorities are loaded from Autotask to avoid manual ID mistakes.</div>
|
<div class="form-text mt-2">Priorities are loaded from Autotask to avoid manual ID mistakes.</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="d-flex justify-content-end mt-3">
|
<div class="d-flex justify-content-end mt-3">
|
||||||
<button type="submit" class="btn btn-primary">Save settings</button>
|
<button type="submit" class="btn btn-primary">Save Ticket Defaults</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
@ -27,6 +27,11 @@ This file documents all changes made to this project via Claude Code.
|
|||||||
- CSS include added to `<head>` section
|
- CSS include added to `<head>` section
|
||||||
- Banner placed directly after `<body>` tag, before navbar
|
- Banner placed directly after `<body>` tag, before navbar
|
||||||
- The banner displays "SANDBOX" in uppercase with letter-spacing for clear visibility across all pages
|
- The banner displays "SANDBOX" in uppercase with letter-spacing for clear visibility across all pages
|
||||||
|
- Auto-load Autotask reference data on Settings page load:
|
||||||
|
- Automatically fetches queues, ticket sources, statuses, and priorities when opening Settings
|
||||||
|
- Only triggers when Autotask is enabled, credentials are configured, and cache is empty
|
||||||
|
- Eliminates need to manually click "Refresh reference data" before selecting defaults
|
||||||
|
- Displays info message with loaded data counts
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Renamed "Refresh" button to "Search" in Link existing Autotask ticket modal for better clarity (the button performs a search operation)
|
- Renamed "Refresh" button to "Search" in Link existing Autotask ticket modal for better clarity (the button performs a search operation)
|
||||||
@ -36,6 +41,38 @@ This file documents all changes made to this project via Claude Code.
|
|||||||
- Button only appears when ticket is linked (autotask_ticket_id exists) and Autotask integration is enabled
|
- Button only appears when ticket is linked (autotask_ticket_id exists) and Autotask integration is enabled
|
||||||
- Opens in new tab with proper URL format including workspace and ticket ID parameters
|
- Opens in new tab with proper URL format including workspace and ticket ID parameters
|
||||||
- Styled as small outline button to maintain compact layout
|
- Styled as small outline button to maintain compact layout
|
||||||
|
- Merged all feature branches from v20260203-01 through v20260205-13 into main branch
|
||||||
|
- Consolidated 29 feature branches spanning three development cycles
|
||||||
|
- Used final state from v20260205-13-changelog-python-structure to preserve all functionality
|
||||||
|
- Successfully integrated Autotask PSA integration, changelog restructuring, and UI improvements
|
||||||
|
- All features from individual branches now available in main
|
||||||
|
- Reorganized Autotask settings into two separate forms with dedicated save buttons:
|
||||||
|
- **Autotask Settings** form with "Save Autotask Settings" button inside the card
|
||||||
|
- **Ticket Defaults** form with "Save Ticket Defaults" button inside the card
|
||||||
|
- All fields marked as required with red asterisks (*)
|
||||||
|
- HTML5 validation prevents saving incomplete configurations
|
||||||
|
- Clear visual separation improves UX and prevents accidental saves
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Jobs from archived jobs and inactive customers no longer appear on Daily Jobs, Run Checks, and Jobs list pages
|
||||||
|
- Added customer active status filtering to all job list queries
|
||||||
|
- Jobs now filtered where customer is NULL or active=True alongside existing archived=False filter
|
||||||
|
- Prevents showing jobs with "-" status from deleted customers or archived jobs
|
||||||
|
- Default Ticket Status dropdown no longer appears empty on first visit to Settings page
|
||||||
|
- Default Ticket Status value is now protected against accidental clearing:
|
||||||
|
- Only updates when a valid status is selected
|
||||||
|
- Only allows clearing if reference data is loaded (dropdown has options)
|
||||||
|
- Preserves existing value if dropdown is empty (prevents data loss)
|
||||||
|
- Fixes issue where "Create Autotask ticket" failed due to missing default status after saving settings with empty dropdown
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Cleaned up 66 merged feature branches from repository (both local and remote):
|
||||||
|
- Removed all v20260113-* branches (8 branches)
|
||||||
|
- Removed all v20260115-* branches (17 branches)
|
||||||
|
- Removed all v20260116-* branches (12 branches)
|
||||||
|
- Removed all v20260119-* branches (18 branches)
|
||||||
|
- Removed all v20260120-* branches (11 branches)
|
||||||
|
- Repository now contains only main branch and current working branches (v20260206-*)
|
||||||
|
|
||||||
## [2026-02-05]
|
## [2026-02-05]
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user