# Sprint 6.11 Identity / Employee Cleanup Review ## Summary This pass moves the system closer to the target architecture where: - `users` is login identity - `employees` is employee master - training ownership is based on `employeeId` - user actions remain tracked separately The work completed in this pass focuses on the highest-risk operational paths first: - schema support for user-to-employee mapping - stopping employee import from auto-creating users - employee-owned training records - employee-based employee directory - employee-based training matrix compliance ## Migration Changes Migration file: - `drizzle/0010_romantic_marauders.sql` Changes included: - create `user_employee_maps` - make `training_records.user_id` nullable - make `employee_training_targets.user_id` nullable - add `training_records.submitted_by_user_id` - add `training_records.reviewed_by_user_id` - add unique index `employee_training_targets_org_employee_year_idx` - backfill `user_employee_maps` from existing `users.employee_id` - backfill `submitted_by_user_id` from `created_by` - backfill `reviewed_by_user_id` from `reviewed_by` ## Code Changes ### Identity / Mapping - `src/db/schema.ts` - `src/features/employees/server/employee-identity-linking.ts` Added: - `user_employee_maps` - helper to resolve employee ids for a user - helper to maintain mapping during import or login linking ### Employee Import - `src/app/api/import-employees/route.ts` Changed behavior: - import upserts into `employees` - import upserts `employee_training_targets` by `employeeId` - import links existing users when employee code matches - import no longer auto-creates login users ### Employees API - `src/app/api/employees/route.ts` - `src/features/employees/server/employee-data.ts` Changed behavior: - employees API is active again - `mode=options` returns active employee options for HRD workflows ### Training Records - `src/features/training-records/api/types.ts` - `src/features/training-records/api/service.ts` - `src/features/training-records/schemas/training-record.ts` - `src/features/training-records/components/training-record-form.tsx` - `src/features/training-records/components/training-record-view-page.tsx` - `src/features/training-records/server/training-record-data.ts` - `src/app/api/training-records/route.ts` - `src/app/api/training-records/[id]/route.ts` - `src/app/api/training-records/[id]/review/route.ts` - `src/app/dashboard/training-records/[trainingRecordId]/page.tsx` Changed behavior: - training record create/update now uses `employeeId` as owner - `submittedByUserId` is written on create - `reviewedByUserId` is written on review - employee self-scope uses mapped employee ids - HRD employee selector now reads from `employees` ### Employee Directory - `src/features/employee-directory/server/employee-directory-data.ts` - `src/features/employee-directory/components/employee-directory-detail-page.tsx` Changed behavior: - directory detail reads from `employees` - training summary and transcript use `training_records.employee_id` ### Training Targets - `src/features/training-policy/server/employee-training-target-data.ts` Changed behavior: - added employee-based target resolution - user-based target resolution now falls through employee mapping ### Training Matrix - `src/features/training-matrix/server/training-matrix-data.ts` Changed behavior: - compliance calculation now uses `employees` - approved-course ownership now uses `training_records.employee_id` ## Regression Checklist 1. Run `npm run migrate` 2. Sign in with an existing credentials user 3. Import employee Excel with no pre-existing user account 4. Verify employee rows are created/updated in `employees` 5. Verify no new `users` row is auto-created from import 6. Verify `employee_training_targets` rows are created using `employee_id` 7. Verify existing users with matching employee code are linked in `user_employee_maps` 8. As employee user with a linked employee profile, create a training record 9. Verify `training_records.employee_id` is set 10. Verify `training_records.submitted_by_user_id` is set 11. Review a pending record as HRD 12. Verify `training_records.reviewed_by_user_id` is set 13. Open employee directory detail and verify totals still load 14. Open training matrix compliance and verify rows still load ## Verification Verified in this pass: - `npx tsc --noEmit` ## Known Remaining Work This pass does **not** fully migrate every aggregation path yet. Still recommended for the next pass: - move `overview` fully to `employees` + `training_records.employee_id` - move `reports` fully to `employees` + `training_records.employee_id` - reduce remaining dependency on `users.employeeCode` as compatibility fallback - add Keycloak runtime mapping using `user_employee_maps` ## Risk Notes - some runtime paths still keep compatibility writes to legacy columns - `users.employeeId` still exists as fallback and compatibility support - notifications still depend on login users, so employees without user accounts will not receive login-bound notifications