Unit Tests / test (push) Successful in 16s
## Summary - Adds `UserProfile` model with mutually exclusive **Employee** / **Client** types - Replaces auto-Employee signal with auto-Client profile on user creation - Data migration: users with time log entries → Employee; others → Client (orphan Employee rows removed) - Admin UI at `/financial/manage_users` to set any user's type; profile page shows current type - **Employees** can log time; **Clients** get read-only access to reports and time logs - Time logs, reports, and dashboard filter to employees only - 14 new tests covering signals, type switching, access control, and filtering ## Design decisions (from issue Q&A) 1. Client login = read-only financial access (reports + time logs, no edit/log time) 2. Employee and Client are strictly mutually exclusive 3. Admins (superusers) can change type via Manage Users 4. Bulk migration applied for existing users ## Test plan - [x] `python manage.py test financial.tests` (14 tests pass) - [x] `python manage.py test public.tests` (21 tests pass) - [ ] Run migration on staging: `python manage.py migrate` - [ ] Verify admin can set user types at `/financial/manage_users` - [ ] Verify employee can log time at `/financial/timekeeping` - [ ] Verify client sees reports/time logs read-only, cannot log time - [ ] Verify employee filter dropdown excludes clients Closes #14 Reviewed-on: #15
24 lines
1.4 KiB
Python
24 lines
1.4 KiB
Python
from django.urls import path
|
|
|
|
from . import views
|
|
|
|
urlpatterns = [
|
|
path("", views.financial_home, name="financial_home"),
|
|
path("dashboard", views.index, name="financial_index"),
|
|
path("timekeeping", views.timekeeping, name="Timekeeping"),
|
|
path("time_logs", views.time_logs, name="time_logs"),
|
|
path("time_logs/<int:log_id>/edit", views.edit_time_log, name="edit_time_log"),
|
|
path("time_logs/<int:log_id>/delete", views.delete_time_log, name="delete_time_log"),
|
|
path("timeapproval", views.timeapproval, name="Timeapproval"),
|
|
path("contracts", views.contracts, name="contracts"),
|
|
path("<str:contract_slug>/contract_detail", views.contract_detail, name="contract_detail"),
|
|
path("new_contract", views.new_contract, name="new_contract"),
|
|
path("new_employee", views.new_employee, name="new_employee"),
|
|
path("<str:contract_slug>/new_charge_number", views.new_charge_number, name="new_charge_number"),
|
|
path("<str:charge_number_slug>/update_charge_number", views.update_charge_number, name="update_charge_number"),
|
|
#path("contracts/<int:contract_id>/", views.contract_detail, name="contract"),
|
|
path("procurements", views.procurement, name="procurements"),
|
|
path("profile", views.profile, name="profile"),
|
|
path("manage_users", views.manage_users, name="manage_users"),
|
|
path("client_reports", views.client_reports, name="client_reports"),
|
|
] |