Commit Graph

1186 Commits

Author SHA1 Message Date
Dries Peeters cd0ccd61c7 feat(telemetry): add daily base heartbeat and trigger opt-in ping on enable
- Register send_base_telemetry_heartbeat_with_app cron at 03:00 UTC
- setup: call check_and_send_telemetry when user opts in during setup
- admin: call check_and_send_telemetry when toggling detailed analytics on
2026-03-16 13:01:09 +01:00
Dries Peeters 287020d30c feat(telemetry): gate product analytics on opt-in and send base first_seen at startup
- Delegate track_event, identify_user, track_page_view to telemetry service
- Only send detailed analytics when user has opted in (is_detailed_analytics_enabled)
- Call send_base_first_seen() once at app startup (idempotent per install)
- posthog_funnels: require telemetry_enabled for funnel tracking
- posthog_monitoring: require telemetry_enabled for error/performance events
2026-03-16 13:00:56 +01:00
Dries Peeters 5be0054157 feat(telemetry): add install_id UUID and consent-aware telemetry service
- Add get_install_id() and base_first_seen tracking in InstallationConfig
- Introduce app/telemetry package with TelemetryService abstraction
- Define minimal base telemetry schema (BASE_SCHEMA_KEYS)
- Implement send_base_telemetry, send_base_first_seen, send_base_heartbeat
- Implement send_analytics_event and identify_user gated by opt-in
- Unify install identity: get_installation_id() now returns get_install_id()
2026-03-16 13:00:49 +01:00
Dries Peeters fc9bd4c43b Version Bump 4.23.1 2026-03-15 10:58:19 +01:00
Dries Peeters b4486a627f fix: CI tests, code quality, and duplicate DB indexes
- Webhook models: remove duplicate index definitions so db.create_all()
  no longer raises 'index already exists' (columns already have index=True)
- ImportService: fix circular import by late-importing ClientService,
  ProjectService, TimeTrackingService in __init__
- reports: fix F823 by renaming unpack variable _ to _entry_count to avoid
  shadowing gettext _ in export_task_excel()
- Code quality: add .flake8 with extend-ignore so flake8 CI passes;
  simplify pyproject.toml isort config (drop unsupported options)
- Format: run black and isort on app/
- tests: restore minimal app fixture in test_import_export_models
2026-03-15 10:51:52 +01:00
Dries Peeters 5e45bbd801 chore: update root config, Dockerfile, README, and GitHub workflows
- Update .gitignore, Dockerfile, INSTALLATION.md, README.md
- Update cd-development, cd-release, and migration-check workflows
2026-03-15 10:17:19 +01:00
Dries Peeters d2cac0b3fa chore: move invoice template and tests; remove obsolete root files
- Move pdf_styles_default.css to app/templates/invoices/
- Move test_logo_pdf.py and test_quick_wins.py to tests/
- Remove fix_expense_schema.sql, temp_migration.sql, packages.txt,
  install_log.txt, temp_translated.txt
2026-03-15 10:16:49 +01:00
Dries Peeters 64b5fbe45d chore: move scripts to scripts/
- Move Python and shell scripts (apply_migration, check_routes, run_tests, etc.) to scripts/
- Move setup-https-mkcert and start-https (bat/sh) to scripts/
- Update start-local-test.bat and start-local-test.sh
2026-03-15 10:16:22 +01:00
Dries Peeters 55ae923636 chore: move Docker config to docker/
- Move docker-compose.* (analytics, https, local-test, remote) to docker/
- Move logrotate.conf.example to docker/
2026-03-15 10:16:09 +01:00
Dries Peeters 5fb49ad375 chore: move documentation and assets to docs/
- Move API, ARCHITECTURE, BUILD, DEVELOPMENT, and fix docs to docs/
- Move README variants and WINDOWS_BUILD to docs/
- Move assets and screenshots to docs/assets/
- Update GETTING_STARTED, README, CONTRIBUTOR_GUIDE, PROJECT_STRUCTURE,
  LOCAL_TESTING_WITH_SQLITE, VERSION_MANAGEMENT
2026-03-15 10:15:51 +01:00
Dries Peeters 5eea34a97d Version Bump 4.23.0 2026-03-15 09:39:28 +01:00
Dries Peeters 548de62dde test: extend fixtures and add scope, auth, recurring, reports tests
- Extend conftest and factories for API and scope tests
- Add test_auth, test_reports_scope, test_timer_scope
- Add test_recurring_invoice_service, test_scope_filter
- Add test_admin_dashboard_charts, test_api_contract, test_reports_task_report
- Update test_invoices, test_project_archiving_models, test_project_costs, test_time_entry_repository, test_utils
2026-03-15 09:37:15 +01:00
Dries Peeters 8bb42ddd02 feat(app): recurring invoices, gantt/reporting services, license UI
- Add RecurringInvoiceRepository and RecurringInvoiceService; refactor recurring_invoice model
- Add GanttService and move gantt logic from route to service
- Expand ReportingService and simplify reports route
- Add license_utils and user license template/settings
- Refactor routes to use scope_filter, api_responses, and services (API v1, timer, admin, invoices, etc.)
- Extend invoice_service for recurring; cache and scope_filter utils; base/template updates
2026-03-15 09:37:00 +01:00
Dries Peeters 9547937be2 docs: update README and guides, add audit and strategy docs
- Simplify README version section and point to CHANGELOG
- Update UI overview with Reports and installation reference
- Refresh CONTRIBUTING, DEVELOPMENT, API.md links/consistency
- Add ARCHITECTURE_AUDIT, DOCS_AUDIT, PRODUCT_UX_AUDIT, FRONTEND, PERFORMANCE
- Add API_CONSISTENCY_AUDIT, RESPONSE_FORMAT, CONTRIBUTOR_GUIDE, TESTING_STRATEGY
- Update GETTING_STARTED, REST_API, PROJECT_STRUCTURE, DEPLOYMENT_GUIDE
2026-03-15 09:36:37 +01:00
Dries Peeters e85b9ca596 Update setup.py 2026-03-12 21:39:24 +01:00
Dries Peeters 493ac2f17e docs: document manual entry worked time recalculation fix (#559)
- Add implementation note for deferred recalculation and input listeners
- Reference fix from Break Time feature doc (manual entry / worked time)
2026-03-12 21:38:27 +01:00
Dries Peeters e435e6ad31 docs(inventory): document return/waste devaluation usage (#385)
- In section 5.3 Stock Devaluation: note form message when item does not
  support devaluation.
- Add 'How to use (UI)' steps for return and waste with devaluation.
- Add API note for POST /api/v1/inventory/movements devaluation parameters.
- Enables closing GitHub issue #385 with a doc reference.
2026-03-12 21:38:24 +01:00
Dries Peeters 3b020e6c05 fix(timer): recalculate worked time after date/time commit (fixes #559)
Defer manual entry worked-time recalculation to the next microtask so
the DOM has the latest start/end date and time before reading. Add
input listeners so recalculation runs on every date/time change.

Fixes incorrect duration when end date is in the past (e.g. yesterday)
until the user reselected the end date.
2026-03-12 21:38:21 +01:00
Dries Peeters 39dd165efb feat(inventory): show devaluation requirements by stock item (#385)
- Add data-devaluation-supported on stock item options (trackable + default cost).
- When return/waste is selected and item cannot be devalued, disable 'Apply
  devaluation', show message: 'Devaluation requires a trackable item with a
  default cost.'
- Same message for standalone devaluation type when item is unsupported.
- Add en translation string for the new message.
2026-03-12 21:38:18 +01:00
Dries Peeters 9c0ac0f4ea Fix Xero integration for apps created after March 2026 (#567)
OAuth: Replace deprecated accounting.transactions scope with accounting.invoices and accounting.payments so new Xero Developer apps (on or after 2026-03-02) complete the authorization flow.

Expense sync: Use /api.xro/2.0/ExpenseClaims instead of non-existent /api.xro/2.0/Expenses; read ExpenseClaimID from response.

API: Add optional json_body to _api_request and send invoice/expense payloads (Invoices and ExpenseClaims wrappers) to Xero.

Docs: Add docs/integrations/XERO.md (setup, scopes, sync, troubleshooting) and CHANGELOG entry.
2026-03-12 21:33:11 +01:00
Dries Peeters 65f5b58638 docs(oidc): document JWE limitation and Authentik Encryption Key (fixes #566)
- Add Authentik to provider notes: leave Encryption Key empty; JWE not supported
- Add troubleshooting entry for 'SSO failed' with unsupported algorithm/encrypted token
- Mention reason=unsupported_algorithm_or_jwe in callback redirect log examples
2026-03-12 21:32:40 +01:00
Dries Peeters acdbab869f fix(oidc): surface clear error when IdP sends JWE-encrypted ID tokens (fixes #566)
When the IdP returns an encrypted ID token (e.g. Authentik with Encryption Key
set), Authlib raises UnsupportedAlgorithmError. Previously this was caught
generically and users saw a misleading message about session/cookie/proxy.

- Detect algorithm/JWE-related errors via exception type, module, or message
- Log reason=unsupported_algorithm_or_jwe and a specific warning
- Flash: disable ID token encryption (e.g. leave Authentik Encryption Key empty)
- Keep existing message for real session/state/code failures
2026-03-12 21:32:38 +01:00
Dries Peeters 8f1cfd2abd Update setup.py 2026-03-11 19:23:18 +01:00
Dries Peeters b50ce512fa fix: resolve duplicate timer.resume_timer endpoint on startup
The timer blueprint had two view functions named resume_timer, both
registering as endpoint 'timer.resume_timer' and causing Flask to raise
AssertionError on app load.

- Give the 'resume by id' route a unique endpoint: resume_timer_by_id
- Rename the view for GET /timer/resume/<timer_id> to resume_timer_by_id
- Update templates to use timer.resume_timer_by_id for links with timer_id
- Keep timer.resume_timer for POST (resume current paused timer)
2026-03-11 19:21:33 +01:00
Dries Peeters 1d3a1541e2 feat(mileage,per_diem): add CSV/PDF export and filter-aware export (Issue #564)
- Mileage: Add GET /mileage/export/csv and /mileage/export/pdf with same
  filters as list (status, project, client, date range, search). Export
  buttons in list header; JS builds export URL from current filter form.
- Mileage PDF: New app/utils/mileage_pdf.py (ReportLab, landscape A4,
  totals row for distance and amount).
- Per diem: Add Client filter to list (with client-lock/single-client
  handling). Add GET /per-diem/export/csv and /per-diem/export/pdf.
- Per diem PDF: New app/utils/per_diem_pdf.py (same style as mileage).
- Export links always use current filters (no need to submit first).
- CHANGELOG and docs/import_export/README updated.
2026-03-11 19:18:20 +01:00
Dries Peeters 1ad899834b fix(time-entries): apply date filter and export by current filters (Issue #555)
- Add visible Apply filters button in filter header so users can apply
  Start/End date and other filters without scrolling; expand panel if collapsed
- Keep CSV/PDF export links in sync with current filters: set href from URL
  on load and update on form change so export (including right-click Open in
  new tab / Save link as) always uses the filtered date range
- Document fix in CHANGELOG under [Unreleased]
2026-03-11 18:48:29 +01:00
Dries Peeters daf3236c37 feat(workforce): add delete for periods, time-off, leave types, and holidays (fixes #562)
- Backend: WorkforceGovernanceService.delete_period, delete_leave_request,
  delete_leave_type, delete_holiday with permission and state checks
- Web: POST delete routes in workforce blueprint; delete buttons in dashboard
  for periods (draft/rejected), time-off (draft/submitted/cancelled), leave
  types list, and company holidays (admin only)
- API v1: DELETE endpoints for timesheet-periods, time-off/requests,
  time-off/leave-types, time-off/holidays (scopes and admin where required)
- Desktop: deleteTimesheetPeriod/deleteTimeOffRequest in API client; Delete
  buttons and handlers in workforce view with confirmation and refresh
- Mobile: deleteTimesheetPeriod/deleteTimeOffRequest in API client; Delete
  in popup menus for periods and time-off requests
- Docs: WORKFORCE_DELETE.md, PROJECT_STRUCTURE and API_TOKEN_SCOPES updates
2026-03-11 18:44:53 +01:00
Dries Peeters a70285bfa9 docs: add break time feature documentation and changelog (Issue #561)
- Add docs/BREAK_TIME_FEATURE.md (timers pause/resume, manual break, API, settings)
- CHANGELOG: add entry under [Unreleased]
- FEATURES_COMPLETE: Timer Management and Manual Time Entry mention break time
- docs/README: link to Break Time feature doc
2026-03-11 17:59:12 +01:00
Dries Peeters c36736d063 feat(break-time): add Pause/Resume and break UI (Issue #561)
- Dashboard: Pause/Resume buttons, break and Paused badge, elapsed uses break-adjusted duration
- Timer page: Pause/Resume/Stop, break display
- Floating bar: paused state, Resume on click when paused; use server current_duration when paused
- Manual entry: Break field (HH:MM), Suggest button using default break rules
- Edit time entry: Break field (HH:MM) for admins
2026-03-11 17:59:03 +01:00
Dries Peeters cef83ff51d feat(break-time): add pause/resume routes, timer status, manual and edit break (Issue #561)
- Web: POST /timer/pause, POST /timer/resume; timer_status returns paused, break_seconds, break_formatted
- API v1: POST /api/v1/timer/pause, POST /api/v1/timer/resume
- manual_entry: parse break_time (HH:MM), pass break_seconds; prefill on duplicate
- edit_timer: parse break_time, pass break_seconds to update_entry; recalc duration
- API v1 time entry create/update accept break_seconds
2026-03-11 17:58:56 +01:00
Dries Peeters 7813f3f76a feat(break-time): add pause_timer/resume_timer and break_seconds to service (Issue #561)
- pause_timer(user_id), resume_timer(user_id) in TimeTrackingService
- create_manual_entry and update_entry accept break_seconds; duration = (end-start)-break
2026-03-11 17:58:46 +01:00
Dries Peeters 8752d3d6aa feat(break-time): add break_seconds and pause support to TimeEntry and schemas (Issue #561)
- TimeEntry: break_seconds, paused_at; pause_timer(), resume_timer(); current_duration_seconds and calculate_duration() account for break
- Settings: break_after_hours_1/2, break_minutes_1/2 for default break rules
- Repository: create_manual_entry accepts break_seconds
- Schemas: break_seconds on TimeEntrySchema, Create, Update
2026-03-11 17:58:37 +01:00
Dries Peeters f66d5b7547 feat(break-time): add migrations for break_seconds, paused_at, and default break rules (Issue #561)
- Migration 137: add time_entries.break_seconds, time_entries.paused_at
- Migration 138: add settings break_after_hours_1/2, break_minutes_1/2 (e.g. 6h->30min, 9h->45min)
2026-03-11 17:57:52 +01:00
Dries Peeters 1eadcd090b docs(overtime): accumulated YTD and take as paid leave (Issue #560)
- Document accumulated overtime (YTD) and where it appears (dashboard, analytics, workforce)
- Document take-overtime-as-paid-leave flow, API endpoints, and new helpers
- Add migration 136 and test_overtime_leave.py to Testing section; bump version to 1.2.0
2026-03-11 17:39:44 +01:00
Dries Peeters eefb529ef0 test(overtime): YTD helpers and overtime-as-leave validation (Issue #560)
- TestOvertimeYTD: get_overtime_ytd / get_overtime_last_12_months structure and values
- test_overtime_leave: request within YTD succeeds, exceeding YTD fails with validation
- Smoke test: assert get_overtime_ytd is available on overtime module
2026-03-11 17:39:37 +01:00
Dries Peeters 60551f3720 feat(migration): seed Overtime leave type for take-as-paid-leave (Issue #560)
- Migration 136: insert leave type code 'overtime' if not present
- Enables workforce 'Take as paid leave' flow without manual admin setup
2026-03-11 17:39:28 +01:00
Dries Peeters 251d41bc33 feat(workforce): overtime overview and take as paid leave (Issue #560)
- Workforce dashboard: show Accumulated overtime (YTD) next to Leave Balances
- Add get_overtime_leave_type() and validate requested_hours <= YTD for overtime leave
- Time-off form: 'Take as paid leave' link, overtime type preset, available hours hint
- create_leave_request rejects overtime requests exceeding YTD with clear error
2026-03-11 17:39:20 +01:00
Dries Peeters bd31609ea1 feat(overtime): show accumulated overtime (YTD) on dashboard and in API (Issue #560)
- Main dashboard: compute and display Overtime (YTD) in Month's Hours card
- Analytics: GET /api/analytics/overtime supports period=ytd and start_date/end_date
- API: dashboard stats endpoints include overtime_ytd_hours in response
2026-03-11 17:39:10 +01:00
Dries Peeters ca0c181dc3 feat(overtime): add get_overtime_ytd and get_overtime_last_12_months helpers (Issue #560)
- get_overtime_ytd(user): returns overtime from Jan 1 through today
- get_overtime_last_12_months(user): returns rolling 12-month overtime
- Reuses calculate_period_overtime; no new DB columns
2026-03-11 17:38:56 +01:00
Dries Peeters de2a7db026 fix: restrict subcontractors to assigned projects/clients when starting timers (fixes #558)
- Enforce scope in timer routes: start_timer (POST), start_timer_for_project (GET),
  and start_timer_from_template; deny with flash+redirect when project/client not allowed
- Add user_can_access_project check in api_start_timer (legacy API), API v1 timer/start,
  and kiosk start-timer; return 403 with clear error message
- Scope dashboard Start Timer modal: load active_projects and active_clients via
  apply_project_scope_to_model/apply_client_scope_to_model so subcontractors only see
  assigned options
- Document timer start scope in SUBCONTRACTOR_ROLE.md (web, API, kiosk, 403/redirect)
2026-03-11 16:49:26 +01:00
Dries Peeters 147da2949f Fix(web): prevent mobile browser freeze on Log Time page (Issue #557)
On viewports <=767px, skip loading Toast UI Editor for the notes field on manual entry and edit timer pages; use a plain textarea instead. Toast UI is heavy and was freezing/crashing mobile Safari and Chrome. Desktop behavior unchanged. Document in CHANGELOG and MOBILE_IMPROVEMENTS.md.
2026-03-11 16:37:31 +01:00
Dries Peeters ceae30ecb9 Fix #563: correct route for post-timer toast after Stop & Save
- Use timer.time_entries_overview instead of timer.time_entries when
  building the 'View time entries' URL in the dashboard. The invalid
  route name caused BuildError and an error page after stopping the
  timer, even though the time entry was saved.
- Document the fix in CHANGELOG under Unreleased / Fixed.
2026-03-11 16:23:58 +01:00
Dries Peeters 42c73871ab Update setup.py 2026-03-11 12:50:13 +01:00
Dries Peeters f93103c578 fix(time-entries): add Apply filters button and make export use current filters
Issue #555: Users could set start/end date but had no visible way to apply filters, and CSV/PDF export could ignore the date range if applied before the AJAX filter ran.

- Add explicit 'Apply filters' submit button so date and other filters are applied on click (and on Enter).

- Export CSV/PDF: on click, build URL from current form params so export always reflects the selected date range and filters.

- Initialize export links from form state on load so they match visible filters.

Fixes #555
2026-03-11 12:48:21 +01:00
Dries Peeters 0a4a1535c1 refactor: split API v1 into sub-blueprints, slim bootstrap, move dashboard to AnalyticsService
Architecture and maintainability improvements per production-readiness plan:

- API v1: Split monolithic api_v1.py into per-resource blueprints
  (api_v1_projects, api_v1_tasks, api_v1_clients, api_v1_invoices,
  api_v1_expenses, api_v1_payments, api_v1_mileage, api_v1_deals,
  api_v1_leads, api_v1_contacts). Register all in blueprint_registry;
  keep info, health, auth and remaining routes in api_v1.py.

- Bootstrap: Move setup_logging to app/utils/setup_logging.py and
  legacy migrations (task management, issues tables) to
  app/utils/legacy_migrations.py. Use SQLAlchemy 2-compatible
  db.engine.begin() in legacy_migrations.

- Dashboard: Add AnalyticsService.get_dashboard_top_projects and
  get_time_by_project_chart; thin main dashboard route to call
  services only and remove inline TimeEntry aggregation.

- Docs: Update ARCHITECTURE.md (module table, API structure, data
  flow, design decisions), DEVELOPMENT.md (workflow, build steps,
  test examples), CHANGELOG.md (Unreleased refactor entry).
2026-03-11 11:54:04 +01:00
Dries Peeters 1768ab8839 docs: add UI guidelines and update README, architecture, changelog
- Add docs/UI_GUIDELINES.md: design principles, component usage, layout,
  styling, keyboard/focus; file reference and a11y pointer
- README: add UI overview and Getting around (Dashboard, Timer, Time entries)
- ARCHITECTURE: add UI layer subsection (base, Tailwind, component macros)
- CHANGELOG: document UI/UX redesign under Unreleased
2026-03-11 10:21:03 +01:00
Dries Peeters ce331c6768 feat(ui): dashboard hero block and streamlined recent entries
- Move Timer card to top as hero (primary action: start/stop, quick start, repeat last)
- Replace four adjust-time forms with single form and JS-driven delta submit
- Show only last 5 recent entries with columns: Project, Duration, Date, Actions
- Add View all link to Time entries overview; fix post-stop toast URL to time_entries_overview
2026-03-11 10:20:53 +01:00
Dries Peeters 0e193ec3c1 feat(ui): base layout and sidebar navigation improvements
- Apply max-w-7xl mx-auto to main content for readable width on large screens
- Add first-class sidebar links for Timer and Time entries (above Calendar)
- Fix active state: only Time entries highlighted on time_entries_overview,
  only Timer highlighted on other timer.* routes
- Keep Time Tracking dropdown closed on Log time and Time entries pages
- Remove duplicate Time Entries link from Time Tracking submenu
2026-03-11 10:20:40 +01:00
Dries Peeters 9845a4c62c feat(ui): consolidate components and extend design tokens
- Add form-input-error and disabled state to form-input in input.css
- Add empty_state_compact and loading_overlay macros to components/ui.html
- Migrate tasks/overdue.html from Bootstrap (_components.html) to Tailwind
  (page_header, empty_state, alert from ui.html; consistent cards and grid)
2026-03-11 10:20:32 +01:00
Dries Peeters a3148d9021 docs: update README, CHANGELOG, ARCHITECTURE for product value release
README: document dashboard time-by-project chart, summary report charts and PDF export, post-timer toast, remind-to-log setting; add daily workflow note under dashboard screenshot. CHANGELOG: add Unreleased entries for new features and changes. ARCHITECTURE: mention remind-to-log job and summary report PDF in Data Flow.
2026-03-11 08:59:28 +01:00