fix(core): leave employees whose work ended out of project members and counts
Problem
On app.gauzy.co the Projects page counted and listed employees who no longer work in the organization (e.g. Ever Works — "Members count 6", the project page's Team (6) panel). The project's edit form could not remove them: its employee picker offers working employees only, so those members were invisible there but still saved back.
Root cause. "End Work" makes an employee inactive and archived (EmployeeSubscriber: isActive = false, isArchived = true). Their organization_project_employee rows stay, by design, so that "Back to Work" restores them. But every project read returned those rows, and the stored membersCount counted them.
Fix
Employees whose work ended no longer appear in project member lists or counts. Their memberships are kept, so "Back to Work" brings them back to their projects.
- Reads (
OrganizationProjectService):GET /organization-projects(and/pagination),GET /organization-projects/:id,GET /organization-projects/employee/:employeeId, and the matching GraphQL fields now leave out ofmembers, and out of the members of the project'steams, any employee who is not active or is archived. This is one employee lookup per response. A project read without its members is returned unchanged. - Edits (
updateOrganizationProjectMembers): the edit form never sends those members back, so their rows are kept. Previously the edit would have deleted them once they stopped being listed. A working member taken off in the form is still removed. membersCount:- the project subscriber counts working members only;
EmployeeSubscriberrecounts an employee's projects after a write that starts or ends their work (End Work, Back to Work, a new start date);- migration
1791000003430-RecountProjectMembersOfWorkingEmployeesrecounts every existing project once. It is idempotent, anddownrestores the old count of all rows.
- Raw SQL uses
TRUE/FALSEliterals, not bound booleans, because better-sqlite3 refuses to bind a boolean. Postgres, MySQL and SQLite 3.23+ all accept the literals.
No frontend change is needed: the list, the project page's Team panel and the edit form all render what these endpoints return.
Tests
organization-project.service.former-employees.spec.ts: pagination, findAll, findById and findByEmployee leave out members whose work ended, from both the project's members and its teams' members, using one lookup. No lookup runs when members are not loaded. An edit keeps a former employee's membership and still removes a working member that the form dropped.organization-project-members-count.spec.tsruns on real SQLite:- the migration's up and down;
- a rerun of the migration is a no-op;
- the subscriber's count query;
- the recount after End Work and after Back to Work;
- the recount does not touch projects the employee is not a member of.
employee.subscriber.spec.ts: End Work and Back to Work trigger exactly one recount. An unrelated edit triggers none.- Existing
updateOrganizationProjectMembersharnesses: added the working-employee lookup.
🤖 Generated with Claude Code