Redesign changelog to use Python-based structure instead of Markdown

- Created app/changelog.py with structured changelog data (first 3 versions as example)
- Removed Gitea dependency for changelog rendering
- Added new changelog.html template with:
  - Sidebar navigation for version jumping
  - Bootstrap cards for each version
  - Color-coded type badges (feature/improvement/fixed/documentation)
  - Responsive design with sticky navigation
- Added changelog.css with modern styling and dark mode support
- Updated routes_changelog.py to use Python data structure
- No external dependencies, faster loading, always available

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Ivo Oskamp 2026-02-05 22:56:43 +01:00
parent 00372a0e99
commit d0811304c6
5 changed files with 726 additions and 60 deletions

View File

@ -0,0 +1,343 @@
"""
Changelog data structure for Backupchecks
"""
CHANGELOG = [
{
"version": "v0.1.22",
"date": "2026-02-05",
"summary": "This major release introduces comprehensive Autotask PSA integration, enabling seamless ticket management, customer company mapping, and automated ticket lifecycle handling directly from Backupchecks. The integration includes extensive settings configuration, robust API client implementation, intelligent ticket linking across job runs, and conditional ticket status updates based on time entries.",
"sections": [
{
"title": "Autotask Integration Core Features",
"type": "feature",
"subsections": [
{
"subtitle": "Settings and Configuration",
"items": [
"Complete Autotask integration settings in Settings → Integrations",
"Environment selection (Sandbox/Production) with automatic zone discovery",
"API authentication with fallback support for different tenant configurations",
"Tracking identifier (Integration Code) configuration for ticket attribution",
"Connection testing and diagnostics",
"Reference data synchronization (queues, sources, priorities, statuses)",
"Configurable ticket defaults (queue, source, status, priority)",
"Autotask integration and automatic mail import can now be properly disabled after being enabled (fixed unchecked checkbox processing)"
]
},
{
"subtitle": "Customer Company Mapping",
"items": [
"Explicit Autotask company mapping for customers using ID-based linkage",
"Company search with auto-suggestions when opening unmapped customers",
"Automatically populates search box with customer name and displays matching Autotask companies",
"Mapping status tracking (ok/renamed/missing/invalid)",
"Bulk mapping refresh for all customers",
"Clear search boxes when opening modals for better user experience"
]
},
{
"subtitle": "Ticket Creation and Management",
"items": [
"Create Autotask tickets directly from Run Checks page",
"Automatic ticket number assignment and storage",
"Link existing Autotask tickets to job runs",
"Cross-company ticket search for overarching infrastructure issues (search by ticket number finds tickets across all companies)",
"Ticket propagation to all active runs of the same job",
"Internal ticket registration for legacy compatibility (Tickets, Tickets/Remarks, Job Details)",
"Real-time ticket status polling and updates",
"Deleted ticket detection and audit tracking (deletion date/time and deleted-by resource information)"
]
},
{
"subtitle": "Ticket Resolution and Status Management",
"items": [
"Conditional ticket status updates based on time entries:",
" - Tickets without time entries: automatically closed (status 5 - Complete)",
" - Tickets with time entries: remain open for time tracking continuation",
"Dynamic confirmation messages indicating closure behavior based on time entry presence",
"Safe resolution updates preserving stabilizing fields (issueType, subIssueType, source)",
"Resolution field mirroring from internal ticket notes",
"Ticket notes created via `/Tickets/{id}/Notes` endpoint with timezone-aware timestamps",
"Deleted ticket handling with complete audit trail"
]
},
{
"subtitle": "Technical Implementation",
"items": [
"Full-featured Autotask REST API client (`integrations/autotask/client.py`)",
"Zone information discovery and endpoint resolution",
"Robust authentication handling with header-based fallback for sandbox environments",
"Picklist-based reference data retrieval (queues, sources, priorities, statuses)",
"Entity metadata parsing with tenant-specific field detection",
"Database migrations for Autotask linkage fields across SystemSettings, Customer, JobRun, and Ticket models",
"Ticketing utilities for internal/external ticket synchronization",
"Comprehensive API contract documentation (`docs/autotask_rest_api.md`)",
"Functional design living document for integration architecture"
]
}
]
},
{
"title": "User Interface Improvements",
"type": "improvement",
"items": [
"Search boxes now clear automatically when opening modals (Run Checks Link existing, Customer mapping)",
"Auto-search for similar company names when mapping unmapped customers",
"Cross-company ticket search when using ticket numbers (e.g., \"T20260205.0001\")",
"Dynamic confirmation messages for ticket resolution based on time entries",
"Improved visibility of Autotask ticket information in Run Checks",
"Status labels displayed instead of numeric codes in ticket lists",
"\"Deleted in PSA\" status display with deletion audit information",
"\"Resolved by PSA (Autotask)\" differentiation from Backupchecks-driven resolution"
]
},
{
"title": "Bug Fixes and Stability",
"type": "fixed",
"items": [
"Fixed Autotask REST API base URL casing (ATServicesRest/V1.0)",
"Fixed reference data retrieval using correct picklist endpoints",
"Fixed authentication fallback for sandbox-specific behavior",
"Fixed company name display from nested API responses",
"Fixed ticket ID normalization and response unwrapping (itemId handling)",
"Fixed TicketJobRun linkage for legacy ticket behavior",
"Fixed unchecked checkbox processing for enable/disable toggles (Autotask integration, automatic mail import)",
"Fixed ticket resolution updates to preserve exact field values from GET response",
"Fixed picklist field detection for tenant-specific metadata",
"Fixed migration stability with idempotent column checks",
"Fixed settings page crash with local helper functions",
"Fixed Run Checks modal stacking and Bootstrap 4/5 compatibility",
"Fixed JavaScript errors (renderModal → renderRun)",
"Fixed indentation errors preventing application startup",
"Fixed ticket propagation to ensure all active runs receive ticket linkage",
"Fixed polling to use read-only operations without state mutation"
]
},
{
"title": "Documentation",
"type": "documentation",
"items": [
"Added comprehensive Autotask REST API contract documentation (`docs/autotask_rest_api.md`)",
"Created functional design living document for integration architecture",
"Documented ticket lifecycle, status management, and time entry considerations",
"Added changelog tracking for Claude Code changes (`docs/changelog-claude.md`)"
]
}
]
},
{
"version": "v0.1.21",
"date": "2026-01-20",
"summary": "This release focuses on improving correctness, consistency, and access control across core application workflows, with particular attention to changelog rendering, browser-specific mail readability, Run Checks visibility, role-based access restrictions, override flexibility, and VSPC object linking reliability. The goal is to ensure predictable behavior, clearer diagnostics, and safer administration across both day-to-day operations and complex multi-entity reports.",
"sections": [
{
"title": "Changelog Rendering and Documentation Accuracy",
"type": "improvement",
"items": [
"Updated the Changelog route to render remote Markdown content instead of plain text",
"Enabled full Markdown parsing so headings, lists, links, and code blocks are displayed correctly",
"Ensured the changelog always fetches the latest version directly from the source repository at request time",
"Removed legacy plain-text rendering to prevent loss of structure and formatting"
]
},
{
"title": "Mail Rendering and Browser Compatibility",
"type": "improvement",
"items": [
"Forced a light color scheme for embedded mail content to prevent Microsoft Edge from applying automatic dark mode styling",
"Added explicit `color-scheme` and `forced-color-adjust` rules so original mail CSS is respected",
"Ensured consistent mail readability across Edge and Firefox",
"Applied these fixes consistently across Inbox, Deleted Inbox, Job Details, Run Checks, Daily Jobs, and Admin All Mail views"
]
},
{
"title": "Run Checks Visibility and Consistency",
"type": "improvement",
"items": [
"Added support for displaying the overall remark (overall_message) directly on the Run Checks page",
"Ensured consistency between Run Checks and Job Details, where the overall remark was already available",
"Improved operator visibility of high-level run context without requiring navigation to job details"
]
},
{
"title": "Initial Setup and User Existence Safeguards",
"type": "fixed",
"items": [
"Fixed an incorrect redirect to the \"Initial admin setup\" page when users already exist",
"Changed setup detection logic from \"admin user exists\" to \"any user exists\"",
"Ensured existing environments always show the login page instead of allowing a new initial admin to be created",
"Prevented direct access to the initial setup route when at least one user is present"
]
},
{
"title": "Role-Based Access Control and Menu Restrictions",
"type": "improvement",
"items": [
"Restricted the Reporter role to only access Dashboard, Reports, Changelog, and Feedback",
"Updated menu rendering to fully hide unauthorized menu items for Reporter users",
"Adjusted route access to ensure Feedback pages remain accessible for the Reporter role",
"Improved overall consistency between visible navigation and backend access rules"
]
},
{
"title": "Override Matching Flexibility and Maintainability",
"type": "feature",
"items": [
"Added configurable error text matching modes for overrides: contains, exact, starts with, and ends with",
"Updated override evaluation logic to apply the selected match mode across run remarks and object error messages",
"Extended the overrides UI with a match type selector and improved edit support for existing overrides",
"Added a database migration to create and backfill the `overrides.match_error_mode` field for existing records"
]
},
{
"title": "Job Deletion Stability",
"type": "fixed",
"items": [
"Fixed an error that occurred during job deletion",
"Corrected backend deletion logic to prevent runtime exceptions",
"Ensured related records are handled safely to avoid constraint or reference errors during removal"
]
},
{
"title": "VSPC Object Linking and Normalization",
"type": "fixed",
"items": [
"Fixed VSPC company name normalization so detection and object prefixing behave consistently",
"Ensured filtered object persistence respects the UNIQUE(customer_id, object_name) constraint",
"Correctly update `last_seen` timestamps for existing objects",
"Added automatic object persistence routing for VSPC per-company runs, ensuring objects are linked to the correct customer and job",
"Improved auto-approval for VSPC Active Alarms summaries with per-company run creation and case-insensitive object matching",
"Added best-effort retroactive processing to automatically link older inbox messages once company mappings are approved"
]
},
{
"title": "VSPC Normalization Bug Fixes and Backward Compatibility",
"type": "fixed",
"items": [
"Removed duplicate definitions of VSPC Active Alarms company extraction logic that caused inconsistent normalization",
"Ensured a single, consistent normalization path is used when creating jobs and linking objects",
"Improved object linking so real objects (e.g. HV01, USB Disk) are reliably associated with their jobs",
"Restored automatic re-linking for both new and historical VSPC mails",
"Added backward-compatible matching to prevent existing VSPC jobs from breaking due to earlier inconsistent company naming"
]
}
]
},
{
"version": "v0.1.20",
"date": "2026-01-15",
"summary": "This release delivers a comprehensive set of improvements focused on parser correctness, data consistency, and clearer operator workflows across Inbox handling, Run Checks, and administrative tooling. The main goal of these changes is to ensure that backup notifications are parsed reliably, presented consistently, and handled through predictable and auditable workflows, even for complex or multi-entity reports.",
"sections": [
{
"title": "Mail Parsing and Data Integrity",
"type": "improvement",
"items": [
"Fixed Veeam Backup for Microsoft 365 parsing where the overall summary message was not consistently stored",
"Improved extraction of overall detail messages so permission and role warnings are reliably captured",
"Ensured the extracted overall message is always available across Job Details, Run Checks, and reporting views",
"Added decoding of HTML entities in parsed object fields (name, type, status, error message) before storage, ensuring characters such as ampersands are displayed correctly",
"Improved robustness of parsing logic to prevent partial or misleading data from being stored when mails contain mixed or malformed content"
]
},
{
"title": "Object Classification and Sorting",
"type": "improvement",
"items": [
"Updated object list sorting to improve readability and prioritization",
"Objects are now grouped by severity in a fixed order: Errors first, then Warnings, followed by all other statuses",
"Within each severity group, objects are sorted alphabetically (AZ)",
"Applied the same sorting logic consistently across Inbox, Job Details, Run Checks, Daily Jobs, and the Admin All Mail view",
"Improved overall run status determination by reliably deriving the worst detected object state"
]
},
{
"title": "Parsers Overview and Maintainability",
"type": "improvement",
"items": [
"Refactored the Parsers overview page to use the central parser registry instead of a static, hardcoded list",
"All available parsers are now displayed automatically, ensuring the page stays in sync as parsers are added or removed",
"Removed hardcoded parser definitions from templates to improve long-term maintainability",
"Fixed a startup crash in the parsers route caused by an invalid absolute import by switching to a package-relative import",
"Prevented Gunicorn worker boot failures and Bad Gateway errors during application initialization"
]
},
{
"title": "User Management and Feedback Handling",
"type": "feature",
"items": [
"Added support for editing user roles directly from the User Management interface",
"Implemented backend logic to update existing role assignments without requiring user deletion",
"Ensured role changes are applied immediately and reflected correctly in permissions and access control",
"Updated feedback listings to show only Open items by default",
"Ensured Resolved items are always sorted to the bottom when viewing all feedback",
"Preserved existing filtering, searching, and user-controlled sorting behavior"
]
},
{
"title": "UI Improvements and Usability Enhancements",
"type": "improvement",
"items": [
"Introduced reusable ellipsis handling for long detail fields to prevent layout overlap",
"Added click-to-expand behavior for truncated fields, with double-click support to expand and select all text",
"Added automatic tooltips showing the full value when a field is truncated",
"Removed the redundant \"Objects\" heading above objects tables to reduce visual clutter",
"Applied truncation and expansion behavior consistently across Inbox, Deleted Mail, Run Checks, Daily Jobs, Job Detail views, and Admin All Mail",
"Reset expanded ellipsis fields when Bootstrap modals or offcanvas components are opened or closed to prevent state leakage",
"Fixed layout issues where the Objects table could overlap mail content in the Run Checks popup"
]
},
{
"title": "Veeam Cloud Connect and VSPC Parsing",
"type": "improvement",
"items": [
"Improved the Veeam Cloud Connect Report parser by combining User and Repository Name into a single object identifier",
"Excluded \"TOTAL\" rows from object processing",
"Correctly classified red rows as Errors and yellow/orange rows as Warnings",
"Ensured overall status is set to Error when one or more objects are in error state",
"Added support for Veeam Service Provider Console daily alarm summary emails",
"Implemented per-company object aggregation and derived overall status from the worst detected state",
"Improved detection of VSPC Active Alarms emails to prevent incorrect fallback to other Veeam parsers",
"Fixed a SyntaxError in the VSPC parser that caused application startup failures"
]
},
{
"title": "VSPC Company Mapping Workflow",
"type": "feature",
"items": [
"Introduced a dedicated company-mapping popup for VSPC Active Alarms summary reports",
"Enabled manual mapping of companies found in mails to existing customers",
"Implemented per-company job and run creation using the format \"Active alarms summary | <Company>\"",
"Disabled the standard approval flow for this report type and replaced it with a dedicated mapping workflow",
"Required all detected companies to be mapped before full approval, while still allowing partial approvals",
"Prevented duplicate run creation on repeated approvals",
"Improved visibility and usability of the mapping popup with scroll support for large company lists",
"Ensured only alarms belonging to the selected company are attached to the corresponding run"
]
},
{
"title": "NTFS Auditing and Synology ABB Enhancements",
"type": "improvement",
"items": [
"Added full parser support for NTFS Auditing reports",
"Improved hostname and FQDN extraction from subject lines, supporting multiple subject formats and prefixes",
"Ensured consistent job name generation as \"<hostname> file audits\"",
"Set overall status to Warning when detected change counts are greater than zero",
"Improved Synology Active Backup for Business parsing to detect partially completed jobs as Warning",
"Added support for localized completion messages and subject variants",
"Improved per-device object extraction and ensured specific device statuses take precedence over generic listings"
]
},
{
"title": "Workflow Simplification and Cleanup",
"type": "improvement",
"items": [
"Removed the \"Mark success (override)\" button from the Run Checks popup",
"Prevented creation of unintended overrides when marking individual runs as successful",
"Simplified override handling so Run Checks actions no longer affect override administration",
"Ensured firmware update notifications (QNAP) are treated as informational warnings and excluded from missing-run detection and reporting"
]
}
]
}
]

View File

@ -1,48 +1,13 @@
from .routes_shared import * # noqa: F401,F403
import markdown
GITEA_CHANGELOG_RAW_URL = (
"https://gitea.oskamp.info/ivooskamp/backupchecks/raw/branch/main/docs/changelog.md"
)
from app.changelog import CHANGELOG
@main_bp.route("/changelog")
@login_required
@roles_required("admin", "operator", "reporter", "viewer")
def changelog_page():
changelog_md = ""
changelog_html = ""
error = None
try:
resp = requests.get(
GITEA_CHANGELOG_RAW_URL,
timeout=10,
headers={"Accept": "text/plain, text/markdown; q=0.9, */*; q=0.1"},
)
if resp.status_code != 200:
raise RuntimeError(f"HTTP {resp.status_code}")
changelog_md = resp.text or ""
changelog_html = markdown.markdown(
changelog_md,
extensions=[
"fenced_code",
"tables",
"sane_lists",
"toc",
],
output_format="html5",
)
except Exception as exc: # pragma: no cover
error = f"Unable to load changelog from Gitea ({GITEA_CHANGELOG_RAW_URL}): {exc}"
return render_template(
"main/changelog.html",
changelog_md=changelog_md,
changelog_html=changelog_html,
changelog_error=error,
changelog_source_url=GITEA_CHANGELOG_RAW_URL,
changelog_versions=CHANGELOG,
)

View File

@ -0,0 +1,202 @@
/* Changelog specific styling */
/* Navigation sidebar */
.changelog-nav {
padding: 1rem;
background: var(--bs-body-bg);
border-radius: 0.5rem;
border: 1px solid var(--bs-border-color);
}
.changelog-nav-link {
padding: 0.5rem 0.75rem;
margin-bottom: 0.25rem;
border-radius: 0.375rem;
color: var(--bs-body-color);
text-decoration: none;
transition: all 0.15s ease-in-out;
font-size: 0.95rem;
}
.changelog-nav-link:hover {
background: var(--bs-tertiary-bg);
color: var(--bs-primary);
}
.changelog-nav-link:active,
.changelog-nav-link.active {
background: var(--bs-primary);
color: white;
}
/* Version cards */
.changelog-version-card {
border: 1px solid var(--bs-border-color);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
transition: box-shadow 0.2s ease-in-out;
scroll-margin-top: 80px;
}
.changelog-version-card:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.changelog-version-card .card-header {
padding: 1.25rem 1.5rem;
background: linear-gradient(135deg, var(--bs-primary) 0%, var(--bs-primary-dark, #0056b3) 100%);
}
.changelog-version-card .card-body {
padding: 1.5rem;
}
/* Summary section */
.changelog-summary {
padding: 1rem;
background: var(--bs-light);
border-left: 4px solid var(--bs-primary);
border-radius: 0.375rem;
}
[data-bs-theme="dark"] .changelog-summary {
background: var(--bs-dark);
}
.changelog-summary .lead {
margin-bottom: 0;
font-size: 1rem;
line-height: 1.6;
}
/* Section styling */
.changelog-section {
border-bottom: 1px solid var(--bs-border-color);
padding-bottom: 1.5rem;
}
.changelog-section:last-child {
border-bottom: none;
padding-bottom: 0;
}
/* Type badges */
.changelog-badge-feature {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%);
color: white;
font-weight: 600;
padding: 0.4rem 0.8rem;
font-size: 0.875rem;
}
.changelog-badge-improvement {
background: linear-gradient(135deg, #17a2b8 0%, #20c997 100%);
color: white;
font-weight: 600;
padding: 0.4rem 0.8rem;
font-size: 0.875rem;
}
.changelog-badge-fixed {
background: linear-gradient(135deg, #dc3545 0%, #fd7e14 100%);
color: white;
font-weight: 600;
padding: 0.4rem 0.8rem;
font-size: 0.875rem;
}
.changelog-badge-added {
background: linear-gradient(135deg, #007bff 0%, #6610f2 100%);
color: white;
font-weight: 600;
padding: 0.4rem 0.8rem;
font-size: 0.875rem;
}
.changelog-badge-removed {
background: linear-gradient(135deg, #6c757d 0%, #495057 100%);
color: white;
font-weight: 600;
padding: 0.4rem 0.8rem;
font-size: 0.875rem;
}
.changelog-badge-changed {
background: linear-gradient(135deg, #ffc107 0%, #ff9800 100%);
color: #212529;
font-weight: 600;
padding: 0.4rem 0.8rem;
font-size: 0.875rem;
}
.changelog-badge-documentation {
background: linear-gradient(135deg, #6f42c1 0%, #e83e8c 100%);
color: white;
font-weight: 600;
padding: 0.4rem 0.8rem;
font-size: 0.875rem;
}
/* Subsection styling */
.changelog-subsection {
margin-left: 0.5rem;
}
.changelog-subsection h4 {
font-weight: 600;
margin-bottom: 0.5rem;
}
/* List styling */
.changelog-list {
list-style-type: none;
padding-left: 0;
margin-bottom: 0;
}
.changelog-list li {
padding: 0.4rem 0 0.4rem 1.75rem;
position: relative;
line-height: 1.6;
}
.changelog-list li::before {
content: "●";
position: absolute;
left: 0.5rem;
color: var(--bs-primary);
font-weight: bold;
}
.changelog-list li:hover {
background: var(--bs-tertiary-bg);
border-radius: 0.25rem;
}
/* Nested lists (indented items) */
.changelog-list li:has(+ li) {
margin-bottom: 0.25rem;
}
/* Responsive adjustments */
@media (max-width: 767.98px) {
.changelog-version-card .card-header {
padding: 1rem;
}
.changelog-version-card .card-body {
padding: 1rem;
}
.changelog-summary {
padding: 0.75rem;
}
.changelog-list li {
font-size: 0.95rem;
}
}
/* Smooth scrolling */
html {
scroll-behavior: smooth;
}

View File

@ -1,32 +1,97 @@
{% extends 'layout/base.html' %}
{% block head %}
{{ super() }}
<link rel="stylesheet" href="{{ url_for('static', filename='css/changelog.css') }}" />
{% endblock %}
{% block content %}
<div class="d-flex align-items-center justify-content-between mb-3">
<div class="row">
<!-- Sidebar with version navigation -->
<div class="col-lg-3 col-md-4 d-none d-md-block">
<div class="changelog-nav sticky-top" style="top: 80px;">
<h6 class="text-body-secondary text-uppercase mb-3">Versions</h6>
<nav class="nav flex-column">
{% for version_data in changelog_versions %}
<a class="nav-link changelog-nav-link" href="#{{ version_data.version }}">
{{ version_data.version }}
<span class="text-body-secondary small d-block">{{ version_data.date }}</span>
</a>
{% endfor %}
</nav>
</div>
</div>
<!-- Main content -->
<div class="col-lg-9 col-md-8">
<div class="d-flex align-items-center justify-content-between mb-4">
<div>
<h1 class="h3 mb-1">Changelog</h1>
<div class="text-body-secondary">Loaded live from the repository.</div>
<div class="text-body-secondary">Release history and updates</div>
</div>
</div>
{% if changelog_versions %}
{% for version_data in changelog_versions %}
<div class="card changelog-version-card mb-4" id="{{ version_data.version }}">
<div class="card-header bg-primary text-white">
<div class="d-flex align-items-center justify-content-between">
<div>
<h2 class="h4 mb-0">{{ version_data.version }}</h2>
</div>
{% if changelog_source_url %}
<div class="text-end">
<a class="btn btn-sm btn-outline-secondary" href="{{ changelog_source_url }}" target="_blank" rel="noopener">
View source
</a>
<span class="badge bg-light text-dark">{{ version_data.date }}</span>
</div>
{% endif %}
</div>
{% if changelog_error %}
<div class="alert alert-warning" role="alert">
{{ changelog_error }}
</div>
{% endif %}
<div class="card">
<div class="card-body">
{% if changelog_html %}
<div class="markdown-content">{{ changelog_html | safe }}</div>
{% if version_data.summary %}
<div class="changelog-summary mb-4">
<p class="lead">{{ version_data.summary }}</p>
</div>
{% endif %}
{% for section in version_data.sections %}
<div class="changelog-section mb-4">
<h3 class="h5 mb-3">
{% if section.type %}
<span class="badge changelog-badge-{{ section.type }}">{{ section.title }}</span>
{% else %}
<div class="text-body-secondary">No changelog content available.</div>
{{ section.title }}
{% endif %}
</h3>
{% if section.subsections %}
{% for subsection in section.subsections %}
<div class="changelog-subsection mb-3">
{% if subsection.subtitle %}
<h4 class="h6 text-body-secondary mb-2">{{ subsection.subtitle }}</h4>
{% endif %}
{% if subsection.items %}
<ul class="changelog-list">
{% for item in subsection.items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
{% elif section.items %}
<ul class="changelog-list">
{% for item in section.items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endfor %}
{% else %}
<div class="alert alert-info" role="alert">
No changelog entries available.
</div>
{% endif %}
</div>
</div>

View File

@ -1,5 +1,96 @@
## v0.1.22
***
This major release introduces comprehensive Autotask PSA integration, enabling seamless ticket management, customer company mapping, and automated ticket lifecycle handling directly from Backupchecks. The integration includes extensive settings configuration, robust API client implementation, intelligent ticket linking across job runs, and conditional ticket status updates based on time entries.
### Autotask Integration Core Features
**Settings and Configuration:**
- Complete Autotask integration settings in Settings → Integrations
- Environment selection (Sandbox/Production) with automatic zone discovery
- API authentication with fallback support for different tenant configurations
- Tracking identifier (Integration Code) configuration for ticket attribution
- Connection testing and diagnostics
- Reference data synchronization (queues, sources, priorities, statuses)
- Configurable ticket defaults (queue, source, status, priority)
- Autotask integration and automatic mail import can now be properly disabled after being enabled (fixed unchecked checkbox processing)
**Customer Company Mapping:**
- Explicit Autotask company mapping for customers using ID-based linkage
- Company search with auto-suggestions when opening unmapped customers
- Automatically populates search box with customer name and displays matching Autotask companies
- Mapping status tracking (ok/renamed/missing/invalid)
- Bulk mapping refresh for all customers
- Clear search boxes when opening modals for better user experience
**Ticket Creation and Management:**
- Create Autotask tickets directly from Run Checks page
- Automatic ticket number assignment and storage
- Link existing Autotask tickets to job runs
- Cross-company ticket search for overarching infrastructure issues (search by ticket number finds tickets across all companies)
- Ticket propagation to all active runs of the same job
- Internal ticket registration for legacy compatibility (Tickets, Tickets/Remarks, Job Details)
- Real-time ticket status polling and updates
- Deleted ticket detection and audit tracking (deletion date/time and deleted-by resource information)
**Ticket Resolution and Status Management:**
- Conditional ticket status updates based on time entries:
- Tickets without time entries: automatically closed (status 5 - Complete)
- Tickets with time entries: remain open for time tracking continuation
- Dynamic confirmation messages indicating closure behavior based on time entry presence
- Safe resolution updates preserving stabilizing fields (issueType, subIssueType, source)
- Resolution field mirroring from internal ticket notes
- Ticket notes created via `/Tickets/{id}/Notes` endpoint with timezone-aware timestamps
- Deleted ticket handling with complete audit trail
**Technical Implementation:**
- Full-featured Autotask REST API client (`integrations/autotask/client.py`)
- Zone information discovery and endpoint resolution
- Robust authentication handling with header-based fallback for sandbox environments
- Picklist-based reference data retrieval (queues, sources, priorities, statuses)
- Entity metadata parsing with tenant-specific field detection
- Database migrations for Autotask linkage fields across SystemSettings, Customer, JobRun, and Ticket models
- Ticketing utilities for internal/external ticket synchronization
- Comprehensive API contract documentation (`docs/autotask_rest_api.md`)
- Functional design living document for integration architecture
### User Interface Improvements
- Search boxes now clear automatically when opening modals (Run Checks Link existing, Customer mapping)
- Auto-search for similar company names when mapping unmapped customers
- Cross-company ticket search when using ticket numbers (e.g., "T20260205.0001")
- Dynamic confirmation messages for ticket resolution based on time entries
- Improved visibility of Autotask ticket information in Run Checks
- Status labels displayed instead of numeric codes in ticket lists
- "Deleted in PSA" status display with deletion audit information
- "Resolved by PSA (Autotask)" differentiation from Backupchecks-driven resolution
### Bug Fixes and Stability
- Fixed Autotask REST API base URL casing (ATServicesRest/V1.0)
- Fixed reference data retrieval using correct picklist endpoints
- Fixed authentication fallback for sandbox-specific behavior
- Fixed company name display from nested API responses
- Fixed ticket ID normalization and response unwrapping (itemId handling)
- Fixed TicketJobRun linkage for legacy ticket behavior
- Fixed unchecked checkbox processing for enable/disable toggles (Autotask integration, automatic mail import)
- Fixed ticket resolution updates to preserve exact field values from GET response
- Fixed picklist field detection for tenant-specific metadata
- Fixed migration stability with idempotent column checks
- Fixed settings page crash with local helper functions
- Fixed Run Checks modal stacking and Bootstrap 4/5 compatibility
- Fixed JavaScript errors (renderModal → renderRun)
- Fixed indentation errors preventing application startup
- Fixed ticket propagation to ensure all active runs receive ticket linkage
- Fixed polling to use read-only operations without state mutation
### Documentation
- Added comprehensive Autotask REST API contract documentation (`docs/autotask_rest_api.md`)
- Created functional design living document for integration architecture
- Documented ticket lifecycle, status management, and time entry considerations
- Added changelog tracking for Claude Code changes (`docs/changelog-claude.md`)
---
## v0.1.21