Commit Graph

139 Commits

Author SHA1 Message Date
Dries Peeters 974f5cdd50 feat(quotes): invoice-style line items, costs, and extra goods (#585)
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.
2026-04-12 14:00:12 +02:00
Dries Peeters 70510a6622 fix: quote create 500, line order, and Factur-X PDF parity
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
2026-04-12 13:34:58 +02:00
Dries Peeters 98e6eb87b9 feat(db): add task custom_fields and api_idempotency_keys
- Add tasks.custom_fields (JSONB) for integration metadata (migration 143).
- Add api_idempotency_keys table keyed by token, scope, and key hash (migration 144).
- Register ApiIdempotencyKey model for safe replay of POST /api/v1/time-entries.
2026-04-05 08:39:11 +02:00
evilguy4000 1b5d019560 feat(admin): persist test recipient and send invoice email template tests
Add Settings.mail_test_recipient (migration 142) and wire Admin → Email Configuration to save, load, and prefill the SMTP test recipient field.

Extract invoice email PDF/HTML building into _build_invoice_email_payload for reuse. New send_invoice_template_test_email sends a real message with subject "Invoice template test: …", reusing production rendering and PDF attachment, without updating invoice status or creating InvoiceEmail records.

Add POST /admin/email-templates/<id>/send-test (optional recipient override, optional invoice_id) and Send test email UI on template view and edit pages. Extend smoke and unit tests.
2026-03-27 06:39:16 +01:00
Dries Peeters 60d4d55027 feat(invoices): add fully configurable invoice number patterns
Implement issue #575 by introducing token-based invoice number patterns in settings and unifying number generation across invoice creation paths. This removes hardcoded INV/date formatting and aligns export filenames and bootstrap schemas with stored invoice numbers.
2026-03-26 14:51:55 +01:00
Dries Peeters 5629254618 feat(db): add migrations for keyboard shortcuts overrides and client portal dashboard preferences
- Migration 139: add users.keyboard_shortcuts_overrides (JSON) for per-user shortcut customization
- Migration 140: add client_portal_dashboard_preferences table for widget layout and order
2026-03-16 15:15:16 +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 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 f150b73b94 feat: product value improvements (dashboard, reports, timer, reminders)
Dashboard:
- Add time-by-project chart (last 7 days) with Chart.js horizontal bar; link to Summary report

Summary report:
- Add time-by-project (last 30d) bar chart and daily trend (14d) line chart
- Add one-page PDF export (today/week/month hours + top projects table)

Post-timer flow:
- After stop, show toast "Logged Xh on [Project]" with action link "View time entries"
- Toast manager: optional actionLink/actionLabel for action links in toasts
- Session carries timer_stopped_toast to dashboard; no duplicate flash

Remind to log:
- User setting "Remind me to log time at end of day" + time picker (Settings)
- Hourly job: send one email per day if user has <0.5h logged that day (user TZ)
- Migration 135: notification_remind_to_log, reminder_to_log_time on users
2026-03-11 08:59:13 +01:00
Dries Peeters 55f2ef29fd feat(overtime): allow overtime calculation by weekly hours (Issue #551) 2026-03-09 20:33:48 +01:00
Dries Peeters 2c63f3ac78 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
2026-03-08 06:21:20 +01:00
Dries Peeters 531f16b597 feat(workforce): add timesheet governance, time-off, and workforce dashboard
- Add TimesheetPeriod, TimesheetPolicy, TimeOff models and migration 132
- Add workforce blueprint, routes, and workforce_governance_service
- Add workforce dashboard template; register blueprint via blueprint_registry
- Extend User model for time-off and policy associations
2026-03-06 15:44:35 +01:00
Dries Peeters 777d6ad3bf fix(invoicing): harden PEPPOL transport and PDF/A-3 export compliance
Implement native PEPPOL transport plumbing (identifier validation, SMP/SML discovery, and AS4 send path) and make ZUGFeRD/PDF export fail fast when embedding or PDF/A-3 normalization fails. Add settings, migrations, validators, tests, and docs so compliance issues are visible and verifiable.
2026-03-02 20:55:02 +01:00
Dries Peeters 3c5a937234 Add task tags and categorization (fixes #539)
Implement the advertised 'Task tags and categorization' feature that was
listed in docs but missing from the task edit form.

- Add tags column to tasks table (String 500, comma-separated)
- Add tags field to task create and edit forms with validation
- Display tags as badges on task view page and list pages
- Add tags filter to main tasks list and My Tasks with AJAX support
- Include tags in task export (CSV) and data export
- Add tags to Task API (create, update, list with filter)
- Add tag_list property to Task model for convenient parsing

Also: fix(oidc) use domain instead of IP for HTTPS metadata fetch
to fix TLS SNI (Fixes #540)
2026-02-28 17:28:54 +01:00
Dries Peeters 897c87dec8 fix: add merge migration for Alembic multiple heads (118 and 128)
Resolves 'Multiple head revisions' error during pre-deploy (flask db upgrade)
on Render. Two branches existed: 118_add_role_hidden_module_ids and
128_add_invoices_zugferd_pdf. New revision 129_merge_118_128_heads merges
them so 'alembic upgrade head' runs successfully.
2026-02-16 21:27:58 +01:00
Dries Peeters b0809e2f90 feat(invoicing): add ZugFerd/Factur-X support and document Peppol & ZugFerd
- Add optional embedding of EN 16931 UBL XML in invoice PDFs (ZugFerd/Factur-X)
  when 'Embed EN 16931 XML in invoice PDFs' is enabled in Admin > Peppol e-Invoicing.
  Exported PDFs then contain ZUGFeRD-invoice.xml for hybrid human- and machine-readable
  invoices; same UBL as Peppol, usable via Peppol or email.
- New setting invoices_zugferd_pdf (migration 128), pikepdf dependency, and
  app.utils.zugferd helper (best-effort supplier/customer from Settings and client).
- Wire embed in export_invoice_pdf (and fallback path); admin checkbox and persistence.
- Docs: PEPPOL_EINVOICING.md retitled to 'Peppol and ZugFerd', new section for
  ZugFerd embedding; README and CHANGELOG updated; migration 128 noted.
- Tests: test_zugferd.py (embed adds attachment with expected XML; invalid PDF
  returns original bytes and error).
2026-02-16 07:36:49 +01:00
Dries Peeters ae9ee9dec1 feat: add subcontractor role with assigned clients (scope-restricted access)
- Add user_clients table and UserClient model for many-to-many user-client assignment
- Add 'subcontractor' system role; users with this role see only assigned clients and their projects
- User helpers: is_scope_restricted, get_allowed_client_ids(), get_allowed_project_ids()
- Admin user form: assign clients when role is Subcontractor (multi-select, JS toggle)
- Scope filtering: clients, projects, time entries, reports, invoices, timer, API v1
- Direct access to out-of-scope client/project returns 403 (web and API)
- Migration 127_add_user_clients_table; scope_filter utility and ProjectService scope_client_ids
- Docs: SUBCONTRACTOR_ROLE.md, ADVANCED_PERMISSIONS.md, RBAC, CLIENT_PORTAL, README, CHANGELOG

Addresses GitHub Discussion #476 (user with limited clients/projects).
2026-02-16 07:12:57 +01:00
Dries Peeters ab14e4352a feat: time entry requirements in API and timer; import skips requirements
- API: include time_entry_requirements in GET /users/me (require_task, require_description, min length)
- Timer: validate task/description requirements when starting timer; pass time_approvals_enabled and can_request_approval to view_timer; pass entry_ids_with_pending_approval to time_entries_overview
- Import: pass skip_entry_requirements=True when creating entries from CSV import
2026-02-13 20:57:54 +01:00
Dries Peeters 54533ec95e feat(reports): add Time Entries report with Excel/CSV export (Discussion #463)
- Add /reports/time-entries page listing all time entries (billed and unbilled)
- Columns: Date, Start, Stop, Duration, Project, Task, Notes, Billed, Client
- Filters: date range, user, project, client, task, billed (all/yes/no)
- Export to Excel and CSV with same filters; add Billed column to excel export
- Resolve client from entry.client or project.client in export
- Add Time Entries Report card to Reports index
2026-02-13 20:56:07 +01:00
Dries Peeters 17b67cc578 feat(calendar): default view setting and remember last view
- Resolve calendar view from URL param, then user default, then session
- Add user preference calendar_default_view (day/week/month or unset)
- Persist last-used view in session when opening with ?view=
- Settings: Calendar default view dropdown (Remember last view / Day / Week / Month)
- PATCH /api/preferences supports calendar_default_view
- Migration 123: add calendar_default_view to users
- Tests for calendar view resolution and settings form/API

Ref #518

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 20:22:08 +01:00
Dries Peeters a7f2fec930 feat(support): system-wide support visibility (admin-only)
- Add Settings.donate_ui_hidden and migration 122; admin verify in Admin → Settings
- Support visibility section in admin settings: System ID, code input, Verify and hide for everyone
- New route POST /admin/settings/verify-donate-hide-code (Ed25519 or HMAC) sets donate_ui_hidden
- Templates (base, dashboard, about, help) hide donate UI when settings.donate_ui_hidden
- User settings: remove per-user code/System ID block; add note that admins configure in Admin → Settings
- Config: DONATE_HIDE_PUBLIC_KEY_PEM / _FILE and HMAC fallback; refuse private key PEM
- Dockerfile: set DONATE_HIDE_PUBLIC_KEY_FILE=/app/donate_hide_public.pem
- .gitignore: docs/internal/, scripts/generate_donate_hide_code.py, donate_hide_private.pem
- Update SUPPORT_VISIBILITY.md for system-wide flow and admin-only setup

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 14:00:30 +01:00
Dries Peeters 91685bb9c6 feat: allow users to hide donate/support UI via verified code
- Add Support visibility in Settings: users see a stable System ID and can
  enter a code to permanently hide donate buttons, support banner, and
  donate widgets.
- Verification supports two modes:
  - Ed25519: server stores only public key; codes are signatures generated
    offline with the private key (no secret on server).
  - HMAC: server stores a secret; code = HMAC(secret, system_id).
- Add User.ui_show_donate and Settings.system_instance_id (migration 121).
- Add donate_hide_code utility (HMAC + Ed25519 verify) and config for
  DONATE_HIDE_PUBLIC_KEY(_FILE) and DONATE_HIDE_UNLOCK_SECRET(_FILE).
- Wrap all donate UI in base, dashboard, about, help with conditional.
- Add admin doc SUPPORT_VISIBILITY.md; ignore docs/internal/ and
  code-generation script in .gitignore.
- Add translations for new strings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 11:11:03 +01:00
Dries Peeters 5bc637cc6b fix(invoices): include extra goods and expenses in PDF invoice exports
PDF invoices were missing extra goods (and expenses) because the ReportLab
template renderer only used invoice.items as the table data source.

- Add invoice.all_line_items to template context: merged list of items,
  extra_goods, and expenses with normalized description/quantity/price fields
- Update default template schema to use invoice.all_line_items instead of
  invoice.items for the items table
- Add migration to update existing saved templates with the new data source
- Update PDF layout designer: add all_line_items and extra_goods loop options,
  default items table to all_line_items
- Add expenses to fallback ReportLab generator for consistency with
  pdf_default.html

Fixes #503

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-07 22:24:45 +01:00
Dries Peeters bd00e01876 Add configurable date/time format and misc fixes
- Settings: add date_format and time_format (model, migration 119, admin UI)

- Use user date/time prefs in templates, calendar, and PDF export

- Expense: eager-load client instead of category in repo, service, and API

- Mobile: clarify server URL and certificate docs, bump to 4.18.0, improve connection diagnostics

- Ignore mobile/android/.gradle/

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-07 08:22:34 +01:00
Dries Peeters 8b6e61873b Use system date/time format by default with optional user override
Display formats for dates and times now follow the system settings (Admin
settings) by default. Users can override in their profile (User settings) or
choose "Use system default" so their view matches the rest of the system.

Backend:
- User.date_format and User.time_format are nullable; null means use system.
- Migration 120 makes these columns nullable (existing rows unchanged).
- get_resolved_date_format_key() and get_resolved_time_format_key() in
  timezone utils return the effective key (user or system) for templates and API.
- Context processor injects resolved_date_format_key and resolved_time_format_key
  so base.html and JS (window.userPrefs) always see the resolved format.
- User settings form: "Use system default" option and save logic for null.
- User.to_dict() includes resolved date_format, time_format, and timezone for
  API clients (e.g. mobile).

Web:
- base.html uses resolved keys for window.userPrefs (no hardcoded fallback).
- Replaced display-only strftime() in templates with |user_date, |user_datetime,
  |user_time, and |format_date so all visible dates/times respect settings.
  Left <input type="date"> values and URL/API params as YYYY-MM-DD where required.

Mobile:
- ApiClient.getCurrentUser() and user prefs provider load resolved prefs from
  /api/v1/users/me.
- date_format_utils maps API keys to intl patterns; formatDate, formatTime,
  formatDateTime, formatDateRange used for display.
- Time entries screen (filter dialog), time entry form, time entry card, and
  home dashboard use user prefs for formatting; API requests still send ISO dates.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-07 08:20:08 +01:00
Dries Peeters 3c01fb34c8 feat(modules): lock client selection and make Clients admin-only
- Add settings.locked_client_id and admin UI to select locked client
- Allow disabling Clients for non-admins while keeping admin access
- Gate Clients UI routes and API endpoints when module is disabled
- Auto-select and enforce the locked client across filters and form submissions

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 19:20:48 +01:00
Dries Peeters 807e6370ee feat(roles): add per-role module visibility (hide modules by role)
Admins can hide whole app modules (Analytics, Finance & Expenses, CRM, etc.)
per role so users in that role neither see them in the nav nor access them
by URL/API.

- Add Role.hidden_module_ids (JSON denylist) and migration
- Extend ModuleRegistry.is_enabled() with role-based hide check; module is
  hidden only if ALL of the user's roles hide it (super admins bypass)
- Add Module visibility section to role create/edit form with checkboxes
  by category; persist via hidden_modules form field
- Add tests for registry hide/allow semantics and route decorator 403

Closes #484

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-02 19:18:28 +01:00
Dries Peeters fe53c926ed feat(calendar): user-configurable colors for calendar item types
- Add User preferences: calendar_color_events, calendar_color_tasks,
  calendar_color_time_entries (nullable hex)
- Calendar API: attach color to each event/task/time_entry and return
  typeColors; view_calendar passes type_colors to template
- PATCH /api/preferences: accept and validate calendar color fields
- Migration 117: add the three user columns

Events, tasks, and time entries can now be distinguished by user-chosen
colors (defaults: blue, amber, green). Frontend color pickers and
legend are on the calendar page (already in previous commit).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-01-31 07:48:09 +01:00
Dries Peeters 5d4099422c fix: resolve migration multiple heads and flake8 F821/F823
- Add merge migration 116_merge_three_heads to join heads
  090_add_push_subscriptions, 100_gantt_colors_modules, and
  115_add_exclude_weekends so 'flask db upgrade' runs cleanly.

- Fix undefined-name and scope issues for CI code-quality:
  - api_v1: import InvalidOperation in amount_paid Decimal block
  - custom_reports, invoice_approvals, reports, scheduled_reports,
    tasks: add current_app to Flask imports
  - integrations: set user_integration before POST branches so
    update_config path can use it
  - invoices: use _ign for unpacking in export_invoice_ubl to avoid
    shadowing gettext _ (F823)
  - data_import, excel_export: add module logger (logging.getLogger)
2026-01-25 08:59:47 +01:00
Dries Peeters fca242033e feat: Add 5-day work week option for weekly time goals
Add exclude_weekends feature to allow users to set weekly goals for
Monday-Friday (5-day work week) instead of Monday-Sunday (7-day week).

This addresses client feedback requesting the ability to exclude weekends
when setting weekly goals, making it more realistic for standard work
schedules.

Changes:
- Add exclude_weekends boolean column to weekly_time_goals table
- Update WeeklyTimeGoal model to calculate week_end_date as Friday when
  exclude_weekends is True
- Filter weekend hours from actual_hours calculation when enabled
- Update days_remaining to count only weekdays for 5-day goals
- Add checkbox option in create/edit forms with helpful tooltips
- Update view template to show 5-day work week badge and exclude weekends
  from daily breakdown
- Update tips section to mention the 5-day option

The feature is backward compatible - existing goals default to 7-day weeks.
All calculations (hours, days remaining, averages) correctly account for
the selected work week type.
2026-01-22 20:17:04 +01:00
Dries Peeters 7dcd58608a feat: Enhance TimeEntry audit logging with comprehensive tracking
Add comprehensive audit logging for TimeEntry operations including:
- Client/project context and creation timestamps
- Full entity state before/after changes
- User-provided reasons for deletions and modifications
- Enhanced UI for entering reasons in delete/edit dialogs

Database Changes:
- Add migration 114: reason, entity_metadata, full_old_state, full_new_state columns
- Use JSON column type for entity_metadata for better type handling

Model Updates:
- Extend AuditLog model with new fields and helper methods
- Update log_change() to accept reason, metadata, and full states
- Add get_entity_metadata(), get_full_old_state(), get_full_new_state() methods
- Use JSON column for entity_metadata (returns dict/list directly)

Service Layer:
- Update TimeTrackingService to capture full TimeEntry state and metadata
- Accept reason parameter in delete_entry() and update_entry()
- Create comprehensive audit logs with all context

API Routes:
- Update api.py, api_v1.py, and timer.py routes to accept reason parameter
- Refactor routes to use service layer for consistent audit logging
- Add reason support to bulk delete operations

UI Enhancements:
- Add reason textarea to bulk delete confirmation dialog
- Add reason textarea to time entry edit forms (admin and regular users)
- Update JavaScript to handle reason submission

Audit Log Display:
- Show client/project information and creation timestamp in list view
- Display full old/new states, reason, and metadata in detail view
- Format JSON states for better readability

Bug Fixes:
- Fix duration_seconds reference in timer stop route
- Improve error handling in timer operations with proper exception handling
- Add dashboard cache invalidation after manual entry creation
2026-01-22 13:36:04 +01:00
Dries Peeters a15eb0c97a Add setting to make all invoices PEPPOL compliant
- Add invoices_peppol_compliant in Settings (Admin > Peppol e-Invoicing).
  When on: PDFs include seller/buyer PEPPOL identifiers; invoice view
  shows warnings for missing company/client PEPPOL data; UBL gets
  mandatory BIS Billing 3.0 elements.

- UBL: add InvoiceTypeCode 380 and BuyerReference (buyer_reference,
  project name, or invoice number).

- Optional buyer_reference on Invoice and create/edit forms (PEPPOL BT-10).

- Download UBL route and button when client is PEPPOL-ready and
  invoices_peppol_compliant or peppol_enabled.

- Migrations: 112 (settings.invoices_peppol_compliant),
  113 (invoices.buyer_reference).

- Update PEPPOL_EINVOICING.md; extend test_peppol_service UBL checks.
2026-01-21 15:13:26 +01:00
Dries Peeters f0b7e7a6df feat(reports): unpaid-by-salesman scheduled reports and report builder fixes
Scheduled reports (per-salesman, unpaid-only)
- Schedule form: add email distribution (single / SalesmanEmailMapping /
  template) and recipient template when "Split by custom field" is on.
- Support {value} and {value_lower} in recipient_email_template
  (e.g. {value_lower}@test.de).
- Add use_last_month_dates on ReportEmailSchedule: optional "Use previous
  calendar month" for monthly runs; migration 111.
- Override report date range in ScheduledReportService when
  cadence=monthly and use_last_month_dates=true.
- Wire use_last_month_dates through schedule form, API, and
  create_schedule.

Report Builder
- Add "Unpaid time entries" quick-start (Time Entries + unpaid only +
  last 30 days) and applyUnpaidPreset().
- Clarify "Unpaid only" help: "Unpaid = billable, not yet on any invoice."
- Define canvas at top of script so edit-mode IIFE and addDataSourceToCanvas
  can use it; fix applyUnpaidPreset/onclick when script failed to parse.
- Click-to-add for Data Sources and Components (in addition to drag-and-drop).
- Set dataTransfer.effectAllowed = 'copy' in dragstart for drop compatibility.
- Fix save-form handler: remove orphan try { with no catch that caused
  SyntaxError and prevented applyUnpaidPreset (and rest of script) from loading.

Documentation
- Add docs/reports/UNPAID_BY_SALESMAN_AND_SCHEDULED_REPORTS.md (unpaid
  definition, unpaid-by-salesman setup, SalesmanEmailMapping, template use).
2026-01-21 15:11:12 +01:00
Dries Peeters 8c070d08d5 feat(admin): restore admin-defined module visibility
- Add settings.disabled_module_ids (JSON) to store admin-disabled module IDs
- Migration 110: add disabled_module_ids column to settings
- ModuleRegistry.is_enabled() respects settings.disabled_module_ids
- Admin > System Settings: new 'Module visibility' section with toggles
  for all non-core modules; disabled modules are hidden from all users.
- Core modules stay always on; default empty list keeps current behavior.
2026-01-21 14:20:36 +01:00
Dries Peeters 2615fefa91 feat(gantt): project bar colors and Pickr color picker
- Add Project.color (hex) and migration for projects.color
- Projects create/edit: Gantt color field with Pickr (swatch + hex input),
  Pickr theme CSS and gantt-color-picker.js for init and sync
- Gantt API: include color in JSON for projects and tasks (tasks use project color)
- Gantt view: set custom_class from color, inject CSS for .bar and .bar-progress,
  fix selectors for .gantt .bar-wrapper and :hover/.active overrides; add
  fallback styles for gantt-project and gantt-task
2026-01-20 21:12:51 +01:00
Dries Peeters 0b76df53e2 feat: Add configurable date format for PDF templates
- Add date_format column to invoice_pdf_templates and quote_pdf_templates tables
- Default date format set to DD.MM.YYYY (%d.%m.%Y)
- Update PDF generators to use template-specific date format
- Add date format configuration in admin PDF template editor
- Replace Babel date formatting with strftime for consistent formatting
- Update template filters to use DD.MM.YYYY format by default

This allows users to customize date formatting per PDF template while
maintaining backward compatibility with existing templates.
2026-01-14 21:15:18 +01:00
Dries Peeters 62c34faf08 Fix: Add missing push_subscriptions table migration
- Create migration 090 to add push_subscriptions table
- Fixes database error when deleting users (relation 'push_subscriptions' does not exist)
- Table includes foreign key to users with CASCADE delete
- Adds index on user_id column
- Follows idempotent migration pattern for safe execution

Fixes #428
2026-01-13 21:57:37 +01:00
Dries Peeters ba9b789c51 feat: Add decorative images to PDF templates with full sync between invoice and quote editors
- Add decorative image element to PDF Layout Designer for both invoice and quote templates
- Implement template-level decorative image upload and management
- Add backend routes for template image upload and serving
- Update PDF generation (HTML preview and ReportLab) to handle template images with transparency preservation
- Sync all decorative image functionality between invoice and quote PDF layout editors

Fixes:
- Fix upload button not opening file picker in invoice template (use fresh DOM references)
- Fix element name matching to handle 'decorative-image element-overlap' format (use .includes() instead of strict equality)
- Fix image restoration after page reload with enhanced JSON searching and position matching
- Fix image persistence in Konva.js serialization/deserialization

Improvements:
- Enhanced image restoration logic with fallback mechanisms
- Improved error handling and console logging for debugging
- Better handling of transparent backgrounds in PDF export
- Consistent behavior between invoice and quote template editors
2026-01-13 13:33:55 +01:00
Dries Peeters 0178ef03ad feat: Increase invoice prefix length from 10 to 50 characters
- Add database migration to extend invoice_prefix column length
- Support longer invoice prefixes for better customization
- Migration includes SQLite compatibility handling
2026-01-11 08:37:59 +01:00
Dries Peeters 4a8607f400 feat: Add major feature updates - integrations, services, and utilities
- Add Google Calendar integration with OAuth 2.0 support
- Implement integration service and workflow engine
- Add new routes: auth, clients, custom_reports, integrations, invoices, team_chat
- Add utility modules: config_manager, email, excel_export, file_upload, permissions_seed
- Add integration view template
- Add Docker permission fixes and enhanced start scripts
- Add migration management utilities and legacy schema migration
- Add validation and version management scripts
- Update setup.py version to 4.9.16

This release significantly expands the application's integration capabilities,
adds new business logic services, and improves infrastructure tooling.
2026-01-09 22:42:53 +01:00
Dries Peeters 4eeaa2a842 feat: Migrate PDF templates to ReportLab JSON format
- Add ReportLab template renderer with JSON-based template system
- Implement template schema validation and helper functions
- Add database migration for template_json columns
- Update visual editor to generate ReportLab JSON alongside HTML/CSS
- Maintain backward compatibility with legacy templates
- Add comprehensive migration documentation

BREAKING CHANGE: Existing PDF templates need to be saved again through
the visual editor to generate the new template_json format. Templates
will continue to work using the legacy fallback generator until saved.
2026-01-09 11:43:42 +01:00
Dries Peeters 14d673a4a8 Allow auto-imported time entries without project or client
- Add migration to update check constraint allowing NULL project_id and client_id for source='auto' entries
- Update TimeEntry model validation to allow entries without project/client when source='auto'
- Update TimeEntryCreateSchema to allow entries without project/client when source='auto'
- Enables calendar integrations to import entries that don't have project/client mapping yet
2026-01-07 13:18:40 +01:00
Dries Peeters ad9bfbf1ed Fix client deletion errors and add invoice validation
This commit fixes multiple issues preventing client deletion and adds proper validation to prevent deletion when invoices exist.

Database Schema Fixes:

- Migration 103: Add missing quote_number column to quotes table

  - Handles migration from offer_number to quote_number

  - Generates quote numbers for existing quotes if needed

  - Creates required unique index

- Migration 104: Add all missing columns to quotes table

  - Adds subtotal, tax_amount, visible_to_client columns

  - Adds discount fields (discount_type, discount_amount, discount_reason, coupon_code)

  - Adds payment_terms column

  - Adds approval workflow columns (approval_status, approved_by, approved_at, rejected_by, rejection_reason)

  - Creates required indexes and foreign keys

- Migration 105: Fix client_notifications foreign key cascade

  - Updates client_notifications.client_id FK to ON DELETE CASCADE

  - Updates client_notification_preferences.client_id FK to ON DELETE CASCADE

  - Prevents NOT NULL constraint violations during client deletion

Model Updates:

- Add passive_deletes=True to ClientNotification.client relationship

- Add passive_deletes=True to ClientNotificationPreferences.client relationship

- Add passive_deletes=True to ClientAttachment.client relationship

- Update ClientNote.client relationship to use passive_deletes

Route Updates:

- Add invoice validation to delete_client() and bulk_delete_clients()

- Manually delete notifications before client deletion to prevent SQLAlchemy update issues

Fixes:

- Resolves IntegrityError when deleting clients with notifications

- Resolves missing quote_number column errors

- Resolves missing quotes table columns errors

- Prevents deletion of clients with invoices (data integrity)
2026-01-05 22:07:50 +01:00
Dries Peeters 596d2815f8 Create 102_add_missing_quotes_template_id.py 2026-01-05 20:50:24 +01:00
Dries Peeters 9beda467c9 Fix migration 088: Make salesman splitting migration idempotent
Add existence checks before adding columns to report_email_schedules table
to prevent DuplicateColumn errors when columns already exist in the database.

This ensures the migration can be safely rerun and handles cases where
columns were manually added or migration partially completed.

Fixes: split_by_salesman and salesman_field_name columns in
report_email_schedules table
2026-01-05 17:00:44 +01:00
Dries Peeters 4d336ac54a feat: improve inventory lot grouping and add issues table migration
- Inventory: Group stock lots by warehouse, unit cost, lot type, and date
  to prevent duplicate display entries
- Inventory: Add total value calculation and display per warehouse
- Migration: Make 082_add_global_integrations migration idempotent with
  existence checks for columns and indexes
- Migration: Add 101_add_issues_table migration for client-reported issues
  tracking with multi-database support (SQLite, PostgreSQL, MySQL)
- Version: Bump to 4.9.5
2026-01-05 16:23:30 +01:00
Dries Peeters f205904742 feat: Add comment attachments model and migration
- Create CommentAttachment model following ProjectAttachment pattern
- Add database migration for comment_attachments table
- Register CommentAttachment in models __init__.py
- Support file uploads (images, PDFs, documents, archives)
- Include file metadata (size, type, uploader, timestamp)
- Cascade delete attachments when comments are deleted

Enables file attachments to comments for better team collaboration.
2026-01-04 06:23:39 +01:00
Dries Peeters a24d515247 fix(migrations): add idempotency checks to additional migrations
Add existence checks to older migrations (002, 004, 013, 021, 031, 034, 044,
046, 071, 086, 087) to prevent errors when objects already exist.

Also fixes syntax errors in migrations 046 and 086 where index creation
code was incorrectly placed outside try/except blocks.

All affected migrations now:
- Check for table/column/index existence before creating
- Handle existing objects gracefully with informative messages
- Have safe downgrade functions
- Use proper try/except error handling
2026-01-04 04:34:56 +01:00
Dries Peeters fc6e1bb733 fix(migrations): add idempotency checks to recent migrations
Add existence checks to recent migrations (032, 049, 053, 055, 058, 064, 099)
to prevent DuplicateTable and DuplicateColumn errors. These migrations now:

- Check if tables/columns exist before creating them
- Handle cases where objects already exist gracefully
- Provide informative messages about operations
- Have safe downgrade functions with existence checks

This makes migrations more resilient to partial failures and manual
schema changes.
2026-01-04 04:34:52 +01:00
Dries Peeters 1fa86d7706 fix(migrations): add idempotency check for invoice_peppol_transmissions table
Fix migration 098 to check if invoice_peppol_transmissions table exists
before creating it. This prevents DuplicateTable errors when the migration
is run on databases where the table was already created manually or
during a previous partial migration attempt.

The migration now:
- Checks for table existence before creating
- Handles existing tables gracefully
- Provides informative messages
- Has safe downgrade function
2026-01-04 04:34:48 +01:00