fix(core): leave employees whose work ended out of every member list

Merged
EV
18d ago
+896-15

Problem

#11506 removed employees whose work ended from project member lists. They still showed up in every other member list:

  • teams: the Teams page, and Ever Teams via GET /organization-team/me and GET /organization-team/:id;
  • sprints;
  • project modules;
  • departments;
  • clients (the employees assigned to a contact).

"End Work" keeps an employee's memberships so that "Back to Work" restores them, and none of these lists filtered them out. The edit forms only offer working employees, so these hidden members also could not be seen or managed there.

Fix

Reads: FormerEmployeesInterceptor. It is mounted on the team, sprint, project module, department and client controllers and resolvers. It removes employees whose work ended from every members list in the response, at any depth, in the shapes both ORMs and both protocols return:

  • Membership rows (team, sprint, module) are matched by employeeId. For MikroORM, where employeeId is not set, the employee relation or its key is used. One lookup per response: EmployeeService.findWorkingEmployeeIds (active, not archived, not deleted).
  • Employee rows in many-to-many lists (department, client) are judged by their own isActive / isArchived, so no lookup is needed.
  • Unidentifiable members are kept. A member is removed only when its employee is known not to be working.
  • Only the response is changed. Nothing is saved from it, so filtering can never remove a membership.
  • No new module dependency. EmployeeService is resolved at runtime (ModuleRef, non-strict), so the department and client modules, which don't import EmployeeModule, get the lookup too.

Edits keep what the form could not show:

  • Team, sprint and project-module member edits keep the memberships of employees whose work ended instead of deleting rows the form never sent back. A working member taken off is still removed.
  • Department and client edits keep their links to those employees (withFormerEmployeeLinks). An edit that does not mention members leaves them untouched.

Employees whose work ended are now listed only on the Employees page, when its filter asks for them. Historical records (task assignees, time logs, time-off requests, comments, approvals) are unchanged, because they record who actually did the work.

Tests

  • former-employees.spec.ts covers:
    • pages of teams filtered with one lookup;
    • a single team with nested lists;
    • a GraphQL connection (nodes and edges share rows);
    • MikroORM-shaped rows (relation object or key);
    • department/client employee rows judged by their flags (no lookup);
    • employee rows without flags (looked up);
    • unidentifiable members kept;
    • responses without member lists untouched;
    • withFormerEmployeeLinks.
  • former-employees.interceptor.spec.ts: the interceptor is mounted on all 10 controllers/resolvers; it filters through the application's EmployeeService; without that service it still filters rows that carry their own flags.
  • former-employees.edits.spec.ts:
    • sprint and project-module edits keep a former employee's membership and remove a working member taken off;
    • department and client edits keep links to former employees;
    • an edit that doesn't mention members leaves them alone.
  • organization-team-employee.service.spec.ts: a team edit keeps a former member's row (no delete, no unsubscribe) and still removes a working member taken off. organization-sprint.service.spec.ts: the harness now provides the working-employee lookup.
  • Results: 402 tests across the team, team-employee, sprint, module, department and contact suites pass. Core tsc is clean, the static gates pass, and ESLint reports no findings on the new files.

🤖 Generated with Claude Code