Departments — Developer Guide
Phase 7 HR module. Slug departments, middleware module:departments, permissions departments.*.
Domain
| Model / table | Notes |
|---|---|
Department / departments | Soft deletes; UUID; unique (tenant_id, slug); manager_id → users |
department_user | User membership pivot (+ tenant_id) |
department_employee | Employee membership pivot (+ tenant_id) |
Enum: DepartmentStatusEnum (active | inactive).
Service: DepartmentService (CRUD, membership sync, manager assign, performance aggregates). Events → DepartmentEventSubscriber → PlatformAuditService + Spatie LogsActivity (log name departments).
Visibility (policy + query):
superadmin/admin— org-wide- Department manager (
manager_id) — managed departments + performance - Others with
departments.view— memberships only
Performance eligibility: union of attached user IDs, linked employee user_ids, and manager_id. Unlinked employees appear on the roster with performance_eligible: false.
Manager/member pickers: GET /users excludes the authenticated user (Users admin list). The Departments form merges the signed-in user into picker options so a solo owner can assign themselves.
Authz hardening: manager_id on create/update and PUT …/manager require org-wide assign_manager (admin/superadmin). Membership arrays on create/update require manage_members. Exists rules for managers/members/department_ids are tenant-scoped (tenant_id + not soft-deleted).
Employee create/update accepts department_ids[] when the Departments module is installed; syncs the pivot and sets legacy employees.department to the primary department name.
Backend layout
| Piece | Path |
|---|---|
| Model | app/Models/Department.php |
| Service | app/Services/Tenant/DepartmentService.php |
| Controller | app/Http/Controllers/Tenant/Api/V1/DepartmentController.php |
| Requests | app/Http/Requests/Tenant/Api/V1/Department/* |
| Policy | app/Policies/DepartmentPolicy.php |
| Tests | tests/Feature/Tenant/Department/ |
Permissions
departments.view | create | update | delete | restore | force.delete
departments.manage_members | assign_manager | view_performanceDefault role map: admin (all), manager (view/create/update/manage_members/assign_manager/view_performance), staff (view).
API
See tenant-v1-departments.md and tenant-v1-reports.md (GET /reports/department-performance).
Reports & digest
| Piece | Path |
|---|---|
| Report service | app/Services/Tenant/DepartmentPerformanceReportService.php |
| Controller | app/Http/Controllers/Tenant/Api/V1/DepartmentPerformanceReportController.php |
| Request | app/Http/Requests/Tenant/Api/V1/Report/DepartmentPerformanceReportRequest.php |
| Notification | app/Notifications/Tenant/Report/DepartmentPerformanceDigestNotification.php |
| Command | app/Console/Commands/SendDepartmentPerformanceDigestCommand.php (reports:send-department-digest) |
| Tests | tests/Feature/Tenant/Report/, tests/Feature/Tenant/Notification/DepartmentPerformanceDigestNotificationTest.php |
Auth: dashboard.view middleware + owner or User::isDepartmentManager() in the Form Request (no new Spatie permission). Period filter applies to created_at on leads/tasks assigned to Department::performanceUserIds().
Digest delivery tracking reuses DailySummaryDelivery with kind = department_weekly; mail send/fail tracked by TrackDailySummaryDelivery.
Frontend
- Page:
src/pages/departments/(list, dedicated create/edit form pages, record view with Details / Users / Employees / Performance sections) - Reports page:
src/pages/reports/department-reports-page.tsxat/reports/departments - Service / keys / permissions:
departmentService,departmentReportService,QUERY_KEYS.departments*,QUERY_KEYS.departmentPerformanceReport,PERMISSIONS.dashboard.view+ owner/manager gate - Nav group HR, dual-gated
module: departments+ owner/manager (requiresDepartmentReportAccess)
Tests
php artisan test --compact tests/Feature/Tenant/DepartmentNon-goals (v1)
- Expanding Lead/Task list assignee scoping for department managers
- Hard catalog dependency between
departmentsandemployees - Dropping the free-text
employees.departmentcolumn