Add Employee vs Client user type and filter time/reports by employee (#14) (#15)
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
This commit was merged in pull request #15.
This commit is contained in:
2026-07-05 12:54:50 +00:00
parent 2fb5204614
commit 7dd5ec3be1
18 changed files with 696 additions and 71 deletions
+9 -1
View File
@@ -89,9 +89,11 @@
<li><a href="{% url 'planning:board_view' %}"
class="{% if 'planning' in request.path %}active{% endif %}"
data-tianji-event="nav_planning">Planning</a></li>
<li><a href="{% url 'financial_index' %}"
{% if has_financial_access %}
<li><a href="{% url 'financial_home' %}"
class="{% if 'financial' in request.path %}active{% endif %}"
data-tianji-event="nav_financials">Financials</a></li>
{% endif %}
<li class="dropdown" id="user-profile-dropdown">
<button type="button" class="profile-icon-link" aria-label="Account menu for {{ user.get_full_name|default:user.username }}"
aria-expanded="false" aria-haspopup="true" aria-controls="profile-menu">
@@ -103,6 +105,12 @@
</button>
<ul class="dropdown-content profile-dropdown-content" id="profile-menu" role="menu">
<li class="profile-name-item">{{ user.get_full_name|default:user.username }}</li>
{% if has_financial_access %}
<li><a href="{% url 'profile' %}">Profile</a></li>
{% endif %}
{% if is_financial_admin %}
<li><a href="{% url 'manage_users' %}">Manage Users</a></li>
{% endif %}
<li><a href="{% url 'change_password' %}">Change Password</a></li>
<li>
<form action="{% url 'logout' %}" method="post" style="margin: 0;">