Add trigger-demo-deploy job to cd-release workflow that POSTs to
Render deploy hook when TimeTrackerDemoRender org secret is set.
Runs after build-and-push; skips gracefully if secret is not
configured. Include demo deploy status in release summary.
Document in RENDER.md, CI_CD_DOCUMENTATION.md, and
GITHUB_ACTIONS_SETUP.md.
- Group form into sections: Project & task, Date & time, Details (with headings and icons)
- Upgrade main card to rounded-xl and shadow-lg; add section borders and spacing
- Unify form labels and helper text to app design tokens
- Style primary Log Time and secondary Clear buttons to match dashboard
- Apply rounded-xl to duplicate-entry info banner
- Document change in CHANGELOG under [Unreleased]
Reuse the existing notesEl variable when syncing Toast UI Editor notes
into the hidden textarea on form submit, fixing SyntaxError:
redeclaration of const notesEl in the start timer submit handler.
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.
- Add render.yaml Blueprint: PostgreSQL + Python web service, auto-deploy on push
- Add demo mode (DEMO_MODE, DEMO_USERNAME, DEMO_PASSWORD): single fixed user only
- Login page shows demo credentials when demo mode is active
- Disable self-registration, admin user creation, and OIDC user creation in demo mode
- DB init creates demo user with password when DEMO_MODE is true
- Add docs/deploy/RENDER.md with deployment and demo mode instructions
- Group Chat, Timer, and Help in header as round icon buttons
- Vertically aligned, evenly spaced (gap-2), consistent w-10 h-10
- Header timer: one-click start/stop from any page via floating-timer-bar.js
- Fix timer manual entry URL (use /timer/manual, not /timer/manual_entry)
- Add Help button linking to help page
- Update FEATURES_COMPLETE (Header Quick Access, One-Click Timers)
- Update help page Time Tracking section with header timer tip
- Update CHANGELOG
Replace the single-page setup (telemetry + optional Google Calendar) with
a guided wizard that collects all base settings before completion.
Wizard steps:
1. Welcome - intro and Next
2. Region & time - timezone, date/time format, currency (Settings)
3. Company - name, address, email, optional phone/website (Settings)
4. System - allow self-registration, rounding minutes, single active
timer, idle timeout (Settings)
5. Integrations (optional) - Google Calendar OAuth; can skip
6. Privacy & finish - telemetry opt-in; Complete Setup submits form
Backend (app/routes/setup.py):
- GET: pass settings and timezones to template for prefilling
- POST: validate timezone, date_format, currency, rounding_minutes,
idle_timeout_minutes; persist all fields to Settings and
mark_setup_complete(telemetry_enabled)
- Default timezone/currency to UTC/EUR when missing (keeps tests passing)
Frontend:
- initial_setup.html: 6 wizard steps, progress bar (Step X of 6),
Back/Next and submit on last step
- setup-wizard.js: step navigation, progress update, optional
client-side validation for step 2 (timezone, currency required)
Docs updated: TELEMETRY_QUICK_START.md, GETTING_STARTED.md,
TELEMETRY_IMPLEMENTATION_SUMMARY.md.
Decorative images now survive save/load and no longer cause a black PDF preview:
- Sync imageUrl onto groups before generateCode() so template_json has
correct source (invoice and quote layout editors).
- Inject name/imageUrl into design_json with position-based matching so
reordering does not swap or drop URLs.
- Restore name and imageUrl from saved JSON onto canvas on load
(synchronous) so Konva custom attrs are not required.
- Omit decorative image elements with empty source from template_json;
placeholders stay visible in the editor but are not sent to ReportLab.
- ReportLab: explicitly skip decorative images with empty source; validate
base64 data URI payload and decode in try/except to avoid bad PDF output.
Documentation: PDF_LAYOUT_CUSTOMIZATION.md and PDF_EDITOR_ENHANCED_FEATURES.md
updated with decorative image description, state persistence details, and
troubleshooting. CHANGELOG.md updated under [Unreleased] Fixed.
- 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).
- 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).
Add has_endpoint() helper to check if a Flask endpoint exists before
rendering navigation links. When stripe is not installed, the
payment_gateways blueprint fails to register but is_module_enabled()
can still return true, causing url_for('payment_gateways.list_gateways')
to raise BuildError when rendering base.html.
Only show the Payment Gateways nav link when the endpoint is actually
registered. This fixes inventory route tests that failed when following
redirects to the movements list page in environments without stripe.
Related to issue #385 (stock item devaluation).
Add user-date-input class to all date inputs in:
- Reports: index, export form, builder, and report pages (user, task,
project, time entries, unpaid hours)
- Expenses: form, dashboard, list
- Per diem: form, rate form, list
- Mileage: form, list
- Payments: create, list
- Invoices: edit (due date and linked expense dates)
Date fields now use the same Flatpickr-based picker as Time Tracking
and Log Entries, respecting user date format and first day of week
preferences (fixes inconsistency noted in #496).
- Fix#504: Save button no longer removes items/expenses table from PDF layout
- Add i18n-aware table name inference when Konva fails to serialize custom attrs
- Ensure table group names are set before generateCode on save
- Extend load-time defensive fix to support localized headers (DE, FR, IT, NL)
- Apply same fixes to Quote PDF layout for quote-items-table
- Fix#503: Invoice items (time entries, extra goods, expenses) now appear in PDF
- Root cause was #504; tables are now persisted correctly on save
- Add collapsible Help sections to invoice and quote PDF layout pages
- Update README with PDF Invoice Layout subsection and doc links
- Add troubleshooting for 'Items/expenses table disappears after save'
- Clarify invoice.all_line_items usage in PDF_LAYOUT_CUSTOMIZATION.md
- Document Items Table element in INVOICE_EXTRA_GOODS_PDF_EXPORT.md
Make https://timetracker.drytrix.com/support.html visible so users can purchase a key to hide donate/support UI (one key per instance, €25 one-time).
- Add SUPPORT_PURCHASE_URL config and support_purchase_url in template context
- Donate page: 'Remove Donation Messages' section and CTA, link in Other Ways to Help
- Admin Settings: Support visibility copy and 'Get key at Support & Purchase' button
- User Settings: line and link for admins to purchase a key
- Support banner: 'Purchase key to hide' link
- Dashboard widget: 'Want to hide this widget? Purchase a key'
- README: Support section bullet and intro line for support/purchase key
- SUPPORT_VISIBILITY.md: 'How to get a code' subsection, issuing codes note
- docs/README.md: Support visibility in Configuration with purchase link
- messages.po: add new translatable strings
- REST API v1: add deals, leads, contacts, time-entry-approvals (CRUD + approve/reject/cancel/bulk-approve). New scopes and /info entries.
- Standardize API errors: use error_response, forbidden_response, not_found_response in api_v1 (projects + new CRM/approval routes).
- Consolidate templates: move root templates/ into app/templates/, remove ChoiceLoader and legacy root files.
- Version: README/FEATURES_COMPLETE/CHANGELOG/mobile docs reference setup.py as single source (4.19.0); add [4.19.0] changelog entry.
- Docs: SERVICE_LAYER_AND_BASE_CRUD.md, RBAC_PERMISSION_MODEL.md; base_crud_service docstring points to service-layer doc.
- Mark projects_refactored_example, timer_refactored, invoices_refactored as REFERENCE ONLY in docstrings.
- Fix approve crash: make _mark_entry_approved a no-op (approval state from
TimeEntryApproval only; TimeEntry has no metadata column).
- Fix PostgreSQL enum: use values_callable on ApprovalStatus so DB receives
'pending' not 'PENDING' (matches approvalstatus enum).
- Fix approval templates: use requester/approver, request_comment,
time_entry.notes; reject form field name 'reason'; add can_approve to
view and pass from route.
- Filter get_pending_approvals by current approver (policy/project fallback).
- Add Time Approvals nav link under Work in base.html (module-enabled).
- Add Request approval in time entries list (icon) and view_timer (sidebar);
redirect to list_approvals after request.
- Fix empty_state calls in approvals/list.html (use icon_class positional
args to match components/ui.html macro).
- 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
- Fix Expense.date -> expense_date in custom_report_service, QuickBooks, Xero
- Report Builder: add expenses and invoices data sources in generate_report_data()
- Export Report button: require date range and show alert when missing
- Quick Actions: both CSV and Excel open export form (format selector on form)
- Finance & Reports: standardize date inputs (form-input on expenses list/dashboard)
- 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
- Use Project.client_obj in joinedload (fixes loader strategy error on export).
- Null-safe user/project/client in CSV rows; client column uses Project.client.
- Try/except and logger.exception in both CSV routes; SQLAlchemy handler
logs full traceback and writes to stderr for Docker logs.
Refs #496
Co-authored-by: Cursor <cursoragent@cursor.com>
- Load Flatpickr dark theme when app is in dark mode; sync on theme toggle.
- Apply form-input class to Flatpickr alt input so visible date field gets dark styles.
Refs #
Co-authored-by: Cursor <cursoragent@cursor.com>
- Admin PDF preview: build all_line_items on invoice wrapper and resolve
table data from element data source (invoice.all_line_items or
invoice.items) so preview matches exported PDF.
- ReportLab: when template uses invoice.items, append both extra_goods
and expenses to table data so all line types appear in PDF.
- Export PDF: explicitly load items, extra_goods, and expenses before
generation so data is in session.
- Docs: recommend invoice.all_line_items for custom templates; document
backward compatibility and preview behavior.
Refs #503
Co-authored-by: Cursor <cursoragent@cursor.com>
Persist items-table and expenses-table group names in design_json on save (Konva does not reliably serialize custom attrs on Groups). Add defensive restore of table group names from structure when loading saved layouts that lack names. Fix undefined hasForLoop in save handler.
Ref: #504
Co-authored-by: Cursor <cursoragent@cursor.com>
- Lead to Client: stop passing status into Client(); set after create (#514)
- Leads/Deals activity: parse datetime-local single string in timezone util (#513)
- Deal page: add audit tracking for Deal, entity history support, History section (#515)
Refs #513, #514, #515
Co-authored-by: Cursor <cursoragent@cursor.com>
Add client-side 1s ticker so elapsed time updates in real time on the dashboard when a timer is running. Also update Today's/Week's/Month's hours live and show the running time entry on the calendar in a distinct color.
- Timer section: display live elapsed (HH:MM:SS) using active_timer.start_time
- Recent Entries: live-updating duration for the row that is the active timer
- Stats cards: Today/Week/Month hours include running timer and update every second
- Calendar: include active timer in time_entries with is_running and cyan color
Ref: Issue #516
Co-authored-by: Cursor <cursoragent@cursor.com>
When the API returns a merged events list (single 'events' array with
item_type), the calendar module now splits by item_type so time entries
populate this.timeEntries and render with Time Entry category (clock
icon, time-entry color, correct modal and links).
- loadEvents(): detect merged response and split data.events by
extendedProps.item_type into events, tasks, time_entries
- renderDayEvents(), renderWeekDayBlocks(), renderMonthCellEvents():
use item_type for block/badge type and showEventDetails() so
time entries show as Time Entry, not Event
Refs #517
Co-authored-by: Cursor <cursoragent@cursor.com>
Add four templates that were referenced by routes but never created, which caused TemplateNotFound when using convert or activity flows:
- leads/convert_to_client.html: confirmation page for converting a lead to a client
- leads/convert_to_deal.html: form to convert a lead to a deal (name, client, stage, expected close date, probability)
- leads/activity_form.html: form to add an activity to a lead
- deals/activity_form.html: form to add an activity to a deal
In app/routes/leads.py, define PIPELINE_STAGES and pass pipeline_stages into the convert_to_deal template so the stage dropdown matches the deals module.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Respect user date format and week start: Flatpickr on user-date-input
fields, get_resolved_week_start_day for calendar/pickers, Week Starts On
from My Settings. Update CALENDAR_FEATURES_README.
- Fix time entry notes not saving on edit: sync Toast UI editor to hidden
textarea on all forms so the submitted form gets the current note (edit_timer).
- Time entries PDF export: wrap Task, Client, and Project columns like Notes
so long text breaks across lines (Issue #489).
Co-authored-by: Cursor <cursoragent@cursor.com>
Put the Start submit button in the modal header next to Close so it is always visible without scrolling on mobile and small viewports. Removes the footer Start button and associates the header button with the form via form=startTimerForm. Fixes#506 (comment: better use of real estate).
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add optional step before Docker build: write DONATE_HIDE_PUBLIC_KEY_PEM secret to donate_hide_public.pem
- Image then contains key at /app/donate_hide_public.pem when secret is set; build still succeeds if unset
Co-authored-by: Cursor <cursoragent@cursor.com>
- 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>
- Add overflow-y-auto to modal overlay so content can scroll when tall
- Add max-h-[90vh] and overflow-y-auto to modal content box so the
Start button is always reachable on mobile and short desktop windows
Fixes#506
Co-authored-by: Cursor <cursoragent@cursor.com>
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>