fix(dashboard): remove cache to fix ORM detachment on second visit (Issue #549)

- Dashboard cached template data containing ORM objects (active_timer,
  recent_entries, top_projects, templates) that became detached when
  served in a different request, causing 'Instance not bound to a Session'
  and 'Database Error' on second visit
- Add migration 133 to merge heads 132 (timesheet governance) and 129
  (task tags) so flask db upgrade runs without conflicts
- Update CHANGELOG
This commit is contained in:
Dries Peeters
2026-03-08 06:21:20 +01:00
parent cac75d9e5e
commit 2c63f3ac78
3 changed files with 33 additions and 16 deletions
+2
View File
@@ -8,10 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Fixed
- **Dashboard cache (Issue #549)** — Removed dashboard caching that caused "Instance not bound to a Session" and "Database Error" on second visit. Cached template data contained ORM objects (active_timer, recent_entries, top_projects, templates, etc.) that become detached when served in a different request.
- **Task description field (Issue #535)** — When creating or editing a task, the description field could appear missing or broken if the Toast UI Editor (loaded from CDN) failed to load (e.g. reverse proxy, CSP, Firefox, or offline). A fallback now shows a plain textarea so users can always enter a description; Markdown is still supported when the rich editor loads.
- **ZUGFeRD / PDF/A-3 and PEPPOL (Discussion #433)** — ZUGFeRD embedding no longer silently succeeds without XML when the embed step fails; export is aborted with an actionable error. XMP metadata is created when missing so validators recognize the document. Optional PDF/A-3 normalization (XMP identification and output intent) and optional veraPDF validation gate added. Native PEPPOL transport (SML/SMP + AS4) and strict sender/recipient identifier validation added.
### Added
- **Migration merge 133** — Merge heads 132 (timesheet governance) and 129 (task tags) so `flask db upgrade` runs without conflicts.
- **PEPPOL native transport** — Transport mode can be set to **Native** (SML/SMP participant discovery + AS4 send) in addition to **Generic** (HTTP JSON access point). Sender and recipient identifiers are validated before send. New settings: `peppol_transport_mode`, `peppol_sml_url`, `peppol_native_cert_path`, `peppol_native_key_path` (Admin → Peppol e-Invoicing).
- **PDF/A-3 and validation** — Option **Normalize ZUGFeRD PDFs to PDF/A-3** and optional **Run veraPDF after export** with configurable path. Migration `130_add_peppol_transport_mode_and_native` adds the new columns.
- **Dashboard timer widget** — Pause and Stop buttons while a timer is running (Pause saves the segment so you can resume later). When no timer is active, a prominent "Resume (project name)" button restarts tracking with the same project/task/notes as your last entry. Quick time adjustment buttons (15 / 5 / +5 / +15 minutes) let you correct the current session without leaving the dashboard. New route `POST /timer/adjust` for start-time adjustment.
+4 -16
View File
@@ -25,19 +25,10 @@ def dashboard():
# Update user segments periodically (cached, not every request)
update_user_segments_if_needed(current_user.id, current_user)
# Use caching for dashboard data (5 minute TTL)
# Skip cache when testing: cached data can contain ORM objects that become detached
# when served in a different request, causing "Instance not bound to a Session" errors.
from app.utils.cache import get_cache, cached
cache = get_cache()
cache_key = f"dashboard:{current_user.id}"
use_cache = not current_app.testing
if use_cache:
cached_data = cache.get(cache_key)
if cached_data:
return render_template("main/dashboard.html", **cached_data)
# Do not cache dashboard template_data: it contains ORM objects (active_timer,
# recent_entries, top_projects, templates, etc.) that become detached when
# served in a different request, causing "Instance not bound to a Session"
# and "Database Error" on second visit (Issue #549).
# Get user's active timer
active_timer = current_user.active_timer
@@ -161,9 +152,6 @@ def dashboard():
"total_hours": total_hours, # For donation widget
}
if use_cache:
cache.set(cache_key, template_data, ttl=300)
return render_template("main/dashboard.html", **template_data)
@@ -0,0 +1,27 @@
"""Merge heads 132_add_timesheet_governance_and_time_off and 129_add_task_tags
Revision ID: 133_merge_132_129_heads
Revises: 132_add_timesheet_governance_and_time_off, 129_add_task_tags
Create Date: 2026-03-08
Resolves multiple heads so 'alembic upgrade head' / 'flask db upgrade' can run.
Branch from 117: 118_add_locked_client_id -> 119..128 -> 129_add_task_tags.
Branch from 129_merge_118_128_heads: 130 -> 131 -> 132.
"""
from alembic import op
revision = "133_merge_132_129_heads"
down_revision = ("132_add_timesheet_governance_and_time_off", "129_add_task_tags")
branch_labels = None
depends_on = None
def upgrade():
"""No schema changes - merge only."""
pass
def downgrade():
"""No schema changes - merge only."""
pass