feat: API v1 CRM/approvals, api_responses, templates, version & RBAC docs

- 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.
This commit is contained in:
Dries Peeters
2026-02-13 21:43:09 +01:00
parent 115db52b2b
commit e68f231b91
22 changed files with 673 additions and 129 deletions
+45
View File
@@ -0,0 +1,45 @@
# RBAC Permission Model (Route-Level)
This document describes how route-level access control is applied across the application. For the full role and permission system (roles, permissions, categories), see [ADVANCED_PERMISSIONS.md](../ADVANCED_PERMISSIONS.md).
## Two patterns
### 1. Permission-scoped routes
These blueprints protect routes with `@admin_or_permission_required("permission_name")` in addition to `@login_required`. Only users who are admins or have the given permission can access the route.
**Blueprints using permission decorators:**
- **admin** `access_admin`, `view_users`, `create_users`, `edit_users`, `delete_users`, `manage_telemetry`, `manage_settings`, `manage_backups`, `view_system_info`, `manage_oidc`, `manage_api_tokens`, `manage_integrations`
- **audit_logs** `view_audit_logs`
- **per_diem** `per_diem_rates.view`, `per_diem_rates.create`, `per_diem_rates.edit`, `per_diem_rates.delete`
- **inventory** `view_inventory`, `manage_stock_items`, `manage_warehouses`, `view_stock_levels`, `view_stock_history`, `manage_stock_movements`, `transfer_stock`, `manage_stock_reservations`, `manage_suppliers`, `manage_purchase_orders`, `view_inventory_reports`
- **clients** permission checks where applicable
- **projects** `create_projects` (and others where applied)
- **kanban** permission checks where applied
- **webhooks** permission checks where applied
- **project_templates** permission checks where applied
- **quotes** permission checks where applied
- **custom_field_definitions** permission checks where applied
- **invoice_approvals** permission checks where applied
- **payment_gateways** permission checks where applied
- **kiosk** permission checks where applied
- **offers** permission checks where applied
- **link_templates** permission checks where applied
- **expense_categories** permission checks where applied
### 2. All authenticated users
These blueprints use only `@login_required`. Any logged-in user can access the routes. This is intentional for areas where the default roles (e.g. User, Manager) are expected to have full access within the app.
**Examples:** deals, leads, invoices (main routes), timer, reports, calendar, expenses (main routes), main dashboard, time_approvals, contacts, tasks, client_notes, budget_alerts, payments, recurring_invoices, etc.
## When to add permission decorators
- **New admin-only or sensitive feature:** Use `@admin_or_permission_required("appropriate_permission")` and define the permission in the permission system if it does not exist.
- **New feature for all users:** Use only `@login_required`.
- **Existing “login only” route:** Leave as-is unless you are explicitly tightening access; then add a permission and document it in ADVANCED_PERMISSIONS.md.
## API v1 (REST)
REST API v1 uses API token scopes (e.g. `read:deals`, `write:time_entries`) rather than web permission names. See [API Token Scopes](../api/API_TOKEN_SCOPES.md) and [REST_API.md](../api/REST_API.md).
@@ -0,0 +1,28 @@
# Service Layer and Base CRUD Pattern
## Chosen pattern
TimeTracker uses a **domain service layer**: route handlers call service classes (e.g. `ProjectService`, `InvoiceService`, `TimeApprovalService`) that encapsulate business logic and data access. Routes are kept thin; validation, permissions, and orchestration live in services or in route-level decorators.
- **Services** live in `app/services/`. Each domain (projects, clients, time entries, invoices, approvals, etc.) has one or more service classes.
- **Repositories** exist for some domains (`app/repositories/`, e.g. `TimeEntryRepository`, `ProjectRepository`, `ClientRepository`, `TaskRepository`) and are used by services or routes to avoid N+1 queries and centralize queries.
- **Routes** use `db.session` and model queries where a dedicated service or repository is not yet introduced; new features and refactors should prefer the service (and optionally repository) pattern.
## BaseCRUDService
`app/services/base_crud_service.py` defines **BaseCRUDService**, a generic base class that provides standard CRUD (get_by_id, create, update, delete, list_all) with consistent `{ "success", "message", "data" / "error" }` result dicts.
- **Current use**: BaseCRUDService is **not** extended by any service today. Domain services implement their own methods and return shapes (e.g. `ProjectService.create()` returns a result dict used by the API).
- **When to use it**: Prefer BaseCRUDService when:
- You introduce a **new** domain that has a **repository** with `get_by_id`, `create`, `update`, `delete`, and `query()`.
- The resource is mostly simple CRUD with minimal extra logic.
- **When not to use it**: Existing domain services (projects, clients, invoices, time entries, etc.) have custom logic, validation, and return shapes. Migrating them to BaseCRUDService would require repository implementations and possible API response changes; it is optional and can be done incrementally.
## Summary
| Aspect | Approach |
|---------------------|--------------------------------------------------------------------------|
| New features | Prefer service class + optional repository; use BaseCRUDService only if CRUD is simple and a repository exists. |
| Existing services | Keep current pattern; no requirement to extend BaseCRUDService. |
| Route layer | Prefer calling services; direct `db.session` / model queries are acceptable where services are not yet used. |
| API response shape | Use `app.utils.api_responses` (e.g. `error_response`, `success_response`) for consistent JSON error/success format. |