- Honor AI_ENABLED across session AI, REST v1, LLM service, templates, and
context; add regression tests for the AI helper gate.
- Docker Compose: optional Ollama behind the ai profile; align env.example
and example compose with safe defaults.
- Add UNINSTALL.md with a dedicated AI teardown section; cross-link from
README, INSTALLATION, Getting Started, docs index, and Docker setup guide.
- Record 5.5.6 in CHANGELOG and sync version examples in BUILD_CONFIGURATION;
bump setup.py to 5.5.6.
Add multi-select with Shift/Ctrl toggles and marquee selection on the invoice
and quote PDF template editors, plus a shared transformer, group nudge with
snap-aware arrow keys, multi copy/paste/duplicate, align-to-page or
align-to-selection, horizontal/vertical distribute, and Ctrl/Cmd+G group /
Ctrl/Cmd+Shift+G ungroup.
Expose horizontal (left/center/right/justify) and vertical (top/middle/bottom)
text controls in the properties panel; export verticalAlign, group_id,
locked, and hidden in template JSON where set. Skip hidden elements in
ReportLab rendering; draw justified text on canvas via Paragraph; honor
verticalAlign when a text box height is present.
Introduce app/static/js/pdf_editor helpers (loaded with dynamic import from
the templates), pdf_editor.css, a layers panel with visibility and lock toggles,
smart-guide snapping, and optional ruler chrome. Document behaviour and manual
QA in docs/PDF_LAYOUT_EDITOR.md; add tests/test_pdf_template_schema.py for
optional template fields.
Closes#619.
Move shell layout fixes from [Unreleased] to CHANGELOG [5.5.5].
Sync BUILD_CONFIGURATION.md version code example and setup.py snippet
with setup.py (single source of truth).
Bump package version in setup.py to 5.5.4.
CHANGELOG: document 5.5.4 (full-database restore hardening and backup/restore documentation).
docs/BUILD_CONFIGURATION.md: refresh version-code example and setup.py snippet to 5.5.4 (50504).
Admin restore runs in a background thread; the finally block must not use current_app.logger outside an application context. Use the captured Flask app instance for safe_file_remove logging instead.
While restore_backup runs (extract through Alembic upgrade), set a per-app _database_restore_in_progress flag and expose is_database_restore_in_progress(). The client portal blueprint registers a global app_context_processor; get_current_client() now skips database access during restore and catches SQLAlchemy errors with session rollback so error pages and login can still render when the schema is briefly torn on PostgreSQL.
Documentation: add docs/admin/BACKUP_AND_RESTORE.md, link it from the admin index and import/export docs, cross-reference from DATABASE_RECOVERY.md, and extend IMPORT_EXPORT_GUIDE.md with concurrent-restore guidance.
Fix stale build-guide links, document the implemented quotes API scopes/endpoints, and clarify quote access plus permission-denial behavior so docs match route and test-backed behavior.
Ensure quote list/detail access uses shared quote scope resolution so users with quote-management permissions can view records they can edit, including post-edit redirects in web and API flows. Add regression coverage for non-admin edit_quotes behavior and document the scope-alignment requirement in advanced permissions docs.
Argos and similar MT often corrupt %(name)s (e.g. "% (horas)") or swap in
positional %s, causing ValueError during dashboard render.
- Add scripts/sanitize_po_format_strings.py to clear invalid msgstr / plural
strings so gettext falls back to English msgids.
- Run sanitizer on translations/pt; msgfmt --check-format now passes.
- Document sanitizer + msgfmt after bulk fill in TRANSLATION_SYSTEM and
fill_po_argos header.
- Fix babel.cfg with [extractors] so pybabel resolves jinja2 templates on
toolchains where babel.extractors entry points are not loaded.
- Regenerate messages from source: extract POT, update all locales, drop
obsolete entries (--ignore-obsolete). Portuguese msgstr filled with
offline Argos en→pt (machine output; human QA still recommended).
- Add scripts/fill_po_argos.py for optional first-pass locale fills.
- Gitignore root messages.pot; document extract/update/Argos in
TRANSLATION_SYSTEM and CONTRIBUTING_TRANSLATIONS.
Register Português in LANGUAGES and normalize pt-BR/pt-PT (and similar)
to pt in _normalize_locale so Accept-Language and stored preferences resolve
to translations/pt/.
Add translations/pt/LC_MESSAGES/messages.po seeded from English msgids;
translators can fill msgstr incrementally.
Extend i18n tests for pt presence and catalog file. Update translation
docs (TRANSLATION_SYSTEM, CONTRIBUTING_TRANSLATIONS, implementation note).
- Fix setup.py version string (missing quote).
- Promote CHANGELOG [Unreleased] entries to [5.5.0] (2026-04-27); leave empty [Unreleased].
- Update BUILD_CONFIGURATION.md example version to match setup.py.
Replace the separate plus and bolt floating controls with a single Actions menu inside #fabDock, driven by app/static/floating-actions.js. The dock stacks Actions, optional team chat, and AI Helper using shared CSS variables for spacing; the AI control is a circular FAB matching the other buttons.
Move the chat widget panel to a fixed viewport overlay so dock z-index no longer paints controls over the open panel, and lift the panel bottom when the admin version banner or mobile bottom nav applies. Fade non-actions dock children while the actions menu is open (fab-dock--menu-open).
Update README.md, docs/UI_GUIDELINES.md, and the advanced-features implementation summaries so contributors describe the floating hub instead of global-fab.js. Keep app/static/quick-actions.js aligned with the retired mount pattern for any remaining references.
- Add ollama and ollama-init services with ollama_data volume; app waits for the model pull and receives AI_* defaults (AI_BASE_URL=http://ollama:11434).
- Document bundled stack, env vars, and Ollama vs host base URL in README and DOCKER_COMPOSE_SETUP.
- Align env.example AI defaults with the compose stack.
- fix(ai): include api_key_set on AIProviderConfig so from_settings(**get_ai_config()) matches the settings dict.
Timer starts always blocked a second running entry and never read the\nadmin-controlled Settings flag.\n\n- Add TimeTrackingService.can_start_timer() using Settings.get_settings()\n and wire it into start_timer, web timer routes, kiosk start, and\n legacy POST /api/timer/resume.\n- POST /api/v1/timer/start returns 409 with error_code\n timer_already_running when single-active mode is on and a timer\n is already running.\n- Deduplicate start_timer template handling in the service.\n\nTests: tests/test_single_active_timer_setting.py.\nDocs: REST_API (responses), GETTING_STARTED, REQUIREMENTS, Docker env\nnotes, TESTING_STRATEGY, env.example comment; CHANGELOG entry.
Introduce AUTH_METHOD values ldap and all, with LDAP_* environment settings, ldap3-based LDAPService (search, optional groupOfNames checks, user bind, DB sync), and users.auth_provider (local|oidc|ldap) via migration 153_add_user_auth_provider.
Login supports LDAP-only and combined all (local then LDAP where appropriate); OIDC callback sets auth_provider. Forgot/reset/change password flows skip LDAP-managed accounts. Admin System Settings gains a read-only LDAP summary and POST /admin/ldap/test. Production env validation requires core LDAP variables when LDAP is enabled; OIDC registration and docs recognize all.
Documentation: new docs/admin/configuration/LDAP_SETUP.md; updates to OIDC_SETUP, GETTING_STARTED, Docker guides, Render deploy notes, docs README, and CHANGELOG. Tests: tests/test_ldap_auth.py; test_oidc_logout allows auth_method all.
Add app/static/manifest.json (TimeTracker / Tracker, indigo theme) and PNG install icons via scripts/generate_pwa_icons.py.
Replace inline Flask service worker with app/static/js/sw.js served at /service-worker.js for full-site scope. Cache name timetracker-v1: cache-first for /static, network-first for HTML and non-v1 /api, no interception of /api/v1/* (preserves Authorization).
Add public GET /offline and offline.html for SW navigation fallback; redirect /manifest.webmanifest to the static manifest.
Wire base.html (manifest link, theme-color #4F46E5, SW registration) and pwa-enhancements.js (ready/update/push without duplicate registration). Remove legacy app/static/service-worker.js and manifest.webmanifest.
Tests: service worker and offline routes, manifest redirect, TestPWA expectations; drop duplicate test_enhanced_ui app/client fixtures in favor of conftest.
Docs: ASSETS.md, BUILD_CONFIGURATION.md, implementation notes, and incomplete-features analysis updated for new paths.
Add POST /api/v1/clients/{client_id}/invoice-unbilled to build one draft
invoice from completed billable time not yet on any invoice line, grouped
by project with RateOverride-based rates. Supports API tokens
(write:invoices) or a logged-in user with create_invoices; enforces client
and module access.
InvoiceService gains shared preview/state helpers and
create_client_unbilled_invoice with safe_commit and invoice webhook.
Client detail page adds a confirmation flow and redirect to the new
invoice. Document read:invoices / write:invoices and the new route in
docs/api/API_TOKEN_SCOPES.md; expose the path on the v1 discovery payload.
Tests: two-project client creates two line items; second call returns
no_unbilled_entries.
Describe the session JSON endpoint used by the main dashboard week
comparison chart: partial Monday-to-today window, parallel prior week,
dense by_day series, and null change_percent when last week has no hours.
Note the path with other internal dashboard routes in API_VERSIONING.md.
Global quick actions (FAB):
- Add a fixed primary FAB with an animated popover (Start Timer, Log Time,
New Task) for authenticated users in base.html.
- Start Timer uses the dashboard control when present, otherwise opens the
dashboard with #start-timer so the existing start modal appears.
- Hide the FAB from the md breakpoint up while the header floating timer is
active (floating-timer-bar.js toggles body.fab-hide-desktop-timer-active).
Time entries overview:
- Make Notes and Duration editable in the table where permissions allow;
save via same-origin PUT/PATCH /api/entry/<id> (time-entries-inline-edit.js).
- Register PATCH on the session update_entry route alongside PUT.
Dashboard:
- Add a this-week vs last-week hours chart fed by GET /api/reports/week-comparison
and dashboard-enhancements.js.
Documentation:
- Extend UI_GUIDELINES (FAB, inline edit, file reference) and ARCHITECTURE
(session JSON endpoints) to match the new behavior.
Add an authenticated-only bottom bar below the md breakpoint with
Heroicon-style tabs for Dashboard, Timer, Time entries, Projects, and
More. More opens a slide-up sheet (backdrop, close, Escape) for
Invoices, Clients, Reports, and user Settings, gated by module flags
where applicable.
Align shell layout to Tailwind md (768px): sidebar hidden md:flex,
main md:ml-64 / md:ml-16 when collapsed, mobile hamburger md:hidden,
RTL mainContent margin reset at 767px. Main column uses pb-16 on
small screens so content clears the bar; bar and sheet use pb-safe
(env safe-area) with a Tailwind safelist and @layer utilities rule.
Remove the legacy six-slot FAB bottom nav from base.html.
Docs: README UI overview, CHANGELOG [Unreleased], UI_GUIDELINES layout
and file reference.
Adds a Tailwind-styled global command palette (Ctrl/Cmd+K) with fuzzy search and a Start Timer flow that picks a project then POSTs /api/timer/start with CSRF.
Updates docs to reflect the new shortcut.
Adopt an indigo brand palette with slate neutrals, add semantic colors, and introduce base/component layers (buttons, cards, badges). Move Inter loading to the Tailwind input CSS and update brand guidelines accordingly.
Introduce a web-first AI helper with admin-configurable providers (Ollama or hosted OpenAI-compatible), server-side context building, and confirmed write actions. Expose the feature via session /api/ai/* endpoints and scoped /api/v1/ai/* endpoints.
Harden security by requiring a strong SECRET_KEY for Docker Compose, adding optional settings encryption-at-rest (Fernet), and introducing TOTP-based 2FA plus password reset flows. Update admin UI, API docs, and install documentation.
Create the auto-provisioned demo user with legacy role "user" and the RBAC
user role so public demos cannot reach settings or the PDF layout editor.
On startup, downgrade an existing demo user that still has admin or
super_admin roles left over from older releases.
Document behavior in docs/deploy/RENDER.md and README.md.
Desktop (Electron):
- Add two-step first-run wizard: test TimeTracker via GET /api/v1/info, then log in with API token
- Replace bogus token check with validateSession (users/me, fallback to timer/status for narrow scopes)
- Normalize base URLs; classify TLS/DNS/timeout errors; periodic 401 forces re-login
- Settings save/test use public + authenticated checks; prebuild/prestart and npm test
Server:
- Exempt /api/v1/info, /api/v1/health, and POST /api/v1/auth/login from HTML setup redirect
- Include setup_required on GET /api/v1/info for unfinished installs
Mobile (Flutter):
- Validate saved token against new server URL before persisting settings change
- Remove unused lib/core/config.dart; point BUILD_CONFIGURATION at app_config.dart
Docs: DESKTOP_SETTINGS, desktop README, mobile-desktop-apps README, REST_API /info
Delete app/utils/posthog_features.py. It was unused by routes, and
is_posthog_enabled() always returned False, so flags never activated and
the API was misleading.
Document the real model: deployment behavior uses environment variables
and app/config.py; per-user UI preferences stay on the user record.
Refresh PostHog monitoring guides and README accordingly, update the
incomplete-implementations note, and soften posthog_segmentation wording
so it does not imply an in-app PostHog flag layer.
Register optional blueprints and the optional audit_logs module with full tracebacks (logger.exception and stable extra fields). Re-raise registration errors when FLASK_ENV is development or DEBUG is enabled so local misconfiguration fails fast; production and testing keep skipping optional modules after logging.
Update REST API, API versioning, architecture, project structure, contributor guide, and CONTRIBUTING for global search responses (partial and per-domain errors), shared run_global_search in app/services/global_search_service.py, and blueprint registry observability.
Document the dual HTTP surface everywhere integrators look: OpenAPI intro and servers, ARCHITECTURE, REST_API, API_VERSIONING (deprecated vs internal routes, shared modules), and CONTRIBUTING (v1-first rule).
Session JSON routes in app/routes/api.py that overlap REST v1 now return X-API-Deprecated and a Link header with rel successor-version, implemented via app/utils/api_deprecation.py.
Extract shared global search into app/services/global_search_service.py for both GET /api/search and GET /api/v1/search while preserving legacy short-query 200 empty responses and v1 400 validation.
Refactor legacy POST /api/timer/start, /api/timer/stop, and the start path of /api/timer/resume to use TimeTrackingService; keep existing socketio emits for the web UI.
Add tests/test_api_deprecation_headers.py and adjust search partial-failure tests to patch Project.query on the service module.
Describe how GET /api/openapi.json sets info.version via get_version_from_setup()
(setup.py at runtime, with TIMETRACKER_VERSION or APP_VERSION overrides and Flask
APP_VERSION fallback), and point contributors to the same rules in version
management and project structure docs.
Update the documentation audit entry for the former hardcoded OpenAPI version
gap, document tests/test_api_route_contract.py and the refined test_api_v1
isolation guidance in the testing strategy, add a quick-reference pytest
invocation for the contract suite, and note info.version in the API consistency
audit next to the OpenAPI references.
Document session-auth GET /api/stats/value-dashboard in REST_API.md
(response shape, last_7_days, estimated value fields, 10-minute Redis
cache). Link dashboard session JSON routes from docs/API.md and note
that /api/v1 scopes do not apply to those legacy paths.
Update the comprehensive API test so GET /api/projects/<id>/tasks is
expected to return every task status, including done and cancelled,
which matches the time-entry UI.
Changelog: record the documentation update and the test correction.
Smart notifications (opt-in under user settings): NotificationService builds candidates from the user's local day and active timers; GET /api/notifications and POST /api/notifications/dismiss; migration 150 adds user columns and user_smart_notification_dismissals. /api/summary/today uses the same local-day totals. Client polls from smart-notifications.js; toastManager.show gains onDismiss for server dismiss sync. Config and env.example document SMART_NOTIFY_* variables.
Value dashboard: StatsService with Redis-backed caching, GET /api/stats/value-dashboard, dashboard template and dashboard-enhancements polling alongside existing widgets.
API v1 token search now uses apply_project_scope and apply_client_scope on queries; scope_filter adds apply_project_scope; tests extended for the new helper.
Add a support modal with usage stats, tier and license links, share control, and offline-safe outbound CTAs. Surface support from the header, sidebar, user menu, dashboard card, and settings "Support & Community" section without hiding entry points when a supporter license is active.
Introduce UsageStatsService and a persisted users.support_stats_reports_generated counter incremented on key report exports and custom report views. Add SupportPromptService for session-scoped soft toasts (after export, dashboard milestones, long session via POST /donate/request-soft-prompt).
Wire consent-aware track_event names support.* and mirror funnel rows in DonationInteraction; fix has_recent_donation_click to treat link_clicked as a recent click. Document events and SUPPORT_* / migration notes in docs.
Tests: tests/test_support_services.py for prompt and usage stats behavior.
Add VersionService to fetch and cache the latest GitHub release, compare it to the installed semver (APP_VERSION when valid, else setup.py), and expose admin-only GET /api/version/check and POST /api/version/dismiss on the legacy /api blueprint (session or Bearer token).
Persist per-user dismissal in users.dismissed_release_version (Alembic 148) and show a non-blocking update card in base.html for administrators. Add packaging for semver parsing and tests for comparison, service, and routes.
Document configuration in docs/admin/deployment/VERSION_MANAGEMENT.md and endpoints in docs/api/REST_API.md and docs/API.md.
Document https://crowdin.com/project/drytrix-timetracker in CONTRIBUTING.md, docs/TRANSLATION_SYSTEM.md, and contributor guides. Update CONTRIBUTING_TRANSLATIONS.md with the public project URL, clearer translator vs maintainer onboarding, and an optional "Further Crowdin integration" section (GitHub app, automation, QA, glossary, notifications).
Refresh crowdin.yml header comments (project URL, secrets pointers), normalize preserve_hierarchy to a boolean, and keep the nb→no languages_mapping quoted for YAML 1.1 compatibility.
Add crowdin.yml mapping the English gettext catalog to per-locale messages.po paths, with an explicit nb→no mapping so Norwegian matches app/config.py.
Add a manual GitHub Action (Crowdin sync) to upload sources and download translations when CROWDIN_PROJECT_ID and CROWDIN_PERSONAL_TOKEN are configured.
Extend CONTRIBUTING_TRANSLATIONS with maintainer steps, cross-link TRANSLATION_SYSTEM and contributor guides, refresh TRANSLATION_SYSTEM metadata, and note the integration under [Unreleased] in CHANGELOG.md.
Add Move up / Move down controls and an Order column for quote line items, Costs, and Extra goods on create and edit pages (Issue #584). DOM row order matches POST field order so existing QuoteItem.position handling stays correct.
Fix the quote detail Valid until row by using quote.is_expired instead of an undefined Jinja now() (Issue #583). Submit a past valid_until in the web regression test so the view path is exercised.
Document translation contributions without Git: add docs/CONTRIBUTING_TRANSLATIONS.md, a Translation improvement issue template, links from CONTRIBUTING.md and TRANSLATION_SYSTEM.md, and a Translations subsection in docs/development/CONTRIBUTING.md. Refresh CHANGELOG [Unreleased] for these items.
The will-navigate guard compared file URL origins to the literal string
file://, but the URL API reports an opaque origin for file pages, so
legitimate file navigations were blocked and the window could fail to
load reliably on Windows.
Import utils/helpers from the renderer entry so esbuild includes
window.Helpers in the bundle, restoring formatters and isValidUrl after
build:renderer.
Documentation: desktop README explains the renderer bundle workflow;
Windows desktop troubleshooting covers stuck loading; frontend quality
gates table notes the app.js entry and rebuild step.
Add quote_items.line_kind (item | expense | good) plus display_name,
category, line_date, and sku so one table still drives totals, PDFs,
and acceptance stock logic.
- Migration 147: new columns with backfill line_kind=item
- Quote create/edit: three sections; stock and warehouse only when a
line item is sourced from inventory; shared JS partial
- POST parsing, positions, and duplicate-quote copying for all fields
- API v1 quote items accept the new fields with defaults for old clients
- View, client portal, and PDF/fallback rendering use display_name and
line metadata where relevant
- Integration tests: stock POST shape; expense and good line creation
Docs: extend INVENTORY_MANAGEMENT_PLAN QuoteItem and migration notes.
Quotes (#583):
- Add requires_approval, approval_level, and can_be_sent; wire create form
- Migrations 145 (approval columns) and 146 (quote_items.position)
- Order Quote.items by position; set positions on create/edit/duplicate/API
- Fix view template approval branch (not_required); add web regression test
Invoices / PEPPOL:
- Use the same Factur-X embed and PDF/A-3 normalization for export and
email attachments; Associated File Data + text/xml metadata
- CII/UBL validators, pdfa3, zugferd, and invoice_pdf_postprocess helper
- Bundle compact sRGB ICC (app/resources/icc/); INVOICE_SRGB_ICC_PATH override
- Package data in setup.py; extend PEPPOL_EINVOICING.md and tests
Switch product and installation telemetry to OTLP/Grafana across runtime config, CI injection, docs, and tests to unify telemetry transport and simplify privacy-focused opt-in behavior.
- New scopes read:inventory and write:inventory; existing read/write:projects
still grant same inventory access for backward compatibility
- require_api_token() accepts tuple of scopes (any one required); inventory
endpoints accept (read:inventory | read:projects) and (write:inventory | write:projects)
- ApiTokenService: add new scopes to allowed list; document in API_TOKEN_SCOPES.md
- Add tests for inventory report endpoints with scope checks
- Add keyboard_shortcuts_defaults utility for default bindings and overrides
- Update Settings keyboard shortcuts template for customization UI
- Add KEYBOARD_SHORTCUTS_DEVELOPER.md for implementation and extension
- Add telemetry-architecture.md: base vs detailed layers, schema, consent, retention
- ARCHITECTURE: link to telemetry doc and two-layer overview
- analytics.md: two-layer telemetry section and link to architecture
- privacy.md: update base/detailed analytics and retention wording
- all_tracked_events.md: document base_telemetry.first_seen/heartbeat, opt-in events