User Guide

Administration

Users, roles, modules, audit log, backups.

6 user roles

👑

Super admin

Firm owner. Full access everywhere.
🧑‍💼

Manager

Scoped to assigned clients (approvals, reports, projects). Cross-client weeks blocked.
🧑

Employee

Own time entry + own records only.
🪪

1099 Contractor

SSN sole-prop paid directly by the firm. Surfaces under People, not Vendors.
🏢

Vendor admin

Agency-routed. Manages vendor's members + Team Week bulk entry.
👷

Vendor member

Agency-routed individual contractor. Time entry gated by members_can_log_time flag.

7 modules — per-tenant feature bundles

Toggle on/off per tenant in Settings → Modules:

coreonboardingcompliancecontractsvendor_staffingattorneybank_sync
  • ✓core — required, always on (time / clients / invoicing)
  • ✓onboarding — magic-link invites for hires + vendors
  • ✓compliance — HR docs + expiry + vendor matrix
  • ✓contracts — author + send + sign agreements
  • ✓vendor_staffing — vendors + outbound POs + invoices
  • ✓attorney — LEDES + UTBMS + matters (requires compliance)
  • ✓bank_sync — Plaid integration (separate cost)
Dual orthogonal gates
Every gated route asserts BOTH requireModule(auth.modules, "X") AND the role check. Disabled modules return 404 (not 403) — endpoints behave as if they don't exist for that tenant. Matches the sidebar treatment where the item is simply absent.
Live-data warning before disable
Before disabling a module, TTP runs six parallel COUNT queries: active outbound POs / pending vendor invoices / active contracts / user documents / tagged vendor attachments / pending onboarding invites. Any non-zero count fires a confirm dialog listing the in-flight rows. Prevents accidental disable when real work is mid-flight.

Audit log

Every mutation across the app writes an audit row capturing:

  • ✓Tenant ID + user ID + action + entity type + entity ID
  • ✓Metadata (before/after diff; type discriminator)
  • ✓Request ID (correlation with structured logs)
  • ✓IP address (redacted-prefix-only for privacy)
  • ✓Timestamp
  • ✓Searchable in Diagnostics → Audit Logs

Direct-edit escape hatch (super_admin)

When a time entry needs fixing after it's been submitted / approved / rejected, super_admin can PATCH it directly without the reject → auto-draft → edit → re-approve dance. The service accepts an adminOverride flag; the API route passes it when the caller is super_admin. Every override writes an audit row with metadata.type: "admin_direct_edit" + previousStatus so reviewers can find every one (and see what state the entry was in when the admin reached in). Status itself is NOT flipped by the override — admin edits the numbers on the approved record, doesn't reset workflow. Managers continue to use the standard reject flow; the escape hatch is super_admin-only.

Backups + diagnostics

💾

SQLite backup

Data lives in data/timetrackpro.sqlite. Stop service, copy file, restart — that's the backup. Schedule via Task Scheduler / cron.
🩺

Diagnostics page

5 health cards (App / DB / License / Disk / Logs) with auto- refresh. App Logs + Audit Logs tabs. Email Support bundles health snapshot.
Tip
For Docker installs, mount /app/data as a named volume. Backup the volume itself with docker volume backup or by tarring the mount point.