# 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
