From 7ac769afd7c7db0a89688c743e9e4ffc974762e2 Mon Sep 17 00:00:00 2001 From: Yeganathan S <63534555+skwowet@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:36:38 +0100 Subject: [PATCH 1/2] fix: stop unmerge from copying ui work history onto both members Signed-off-by: Yeganathan S <63534555+skwowet@users.noreply.github.com> --- backend/src/services/organizationService.ts | 1 - .../src/services/member-organization.ts | 17 +++++++++++------ .../src/services/member/unmerge.ts | 16 +++++++--------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/backend/src/services/organizationService.ts b/backend/src/services/organizationService.ts index 0db9800dda..0ae4fb04a4 100644 --- a/backend/src/services/organizationService.ts +++ b/backend/src/services/organizationService.ts @@ -416,7 +416,6 @@ export default class OrganizationService extends LoggerBase { // check if anything to delete in primary const rolesToDelete = memberOrganizations.filter( (r) => - r.source !== 'ui' && !primaryUnmergedRoles.some( (pr) => pr.memberId === r.memberId && diff --git a/services/libs/common_services/src/services/member-organization.ts b/services/libs/common_services/src/services/member-organization.ts index 7e53b4302b..134f433c61 100644 --- a/services/libs/common_services/src/services/member-organization.ts +++ b/services/libs/common_services/src/services/member-organization.ts @@ -4,6 +4,7 @@ import { MemberOrgDate, MemberOrgStintChange, MemberRoleUnmergeStrategy, + OrganizationSource, } from '@crowd/types' function roleKey( @@ -16,7 +17,7 @@ function roleKey( return role.memberId } -function roleExistsInArray( +export function roleExistsInArray( role: IMemberOrganization, roles: IMemberOrganization[], strategy: MemberRoleUnmergeStrategy, @@ -59,15 +60,19 @@ export function unmergeRoles( secondaryBackupRoles: IMemberOrganization[], strategy: MemberRoleUnmergeStrategy, ): IMemberOrganization[] { - const unmergedRoles: IMemberOrganization[] = mergedRoles.filter( - (role) => - role.source === 'ui' || - !secondaryBackupRoles.some((r) => roleKey(r, strategy) === roleKey(role, strategy)), + const cameFromSecondary = (role: IMemberOrganization) => + roleExistsInArray(role, secondaryBackupRoles, strategy) && + !roleExistsInArray(role, primaryBackupRoles, strategy) + + const unmergedRoles: IMemberOrganization[] = mergedRoles.filter((role) => + role.source === OrganizationSource.UI + ? !cameFromSecondary(role) + : !secondaryBackupRoles.some((r) => roleKey(r, strategy) === roleKey(role, strategy)), ) const editableRoles = mergedRoles.filter( (role) => - role.source !== 'ui' && + role.source !== OrganizationSource.UI && secondaryBackupRoles.some((r) => roleKey(r, strategy) === roleKey(role, strategy)), ) diff --git a/services/libs/common_services/src/services/member/unmerge.ts b/services/libs/common_services/src/services/member/unmerge.ts index 43f4baa3b5..81c3c9c9a4 100644 --- a/services/libs/common_services/src/services/member/unmerge.ts +++ b/services/libs/common_services/src/services/member/unmerge.ts @@ -59,10 +59,11 @@ import { MergeActionState, MergeActionStep, MergeActionType, + OrganizationSource, } from '@crowd/types' import { BotDetectionService } from '../bot.service' -import { unmergeRoles } from '../member-organization' +import { roleExistsInArray, unmergeRoles } from '../member-organization' const logger = getServiceLogger() @@ -591,16 +592,13 @@ export async function unmergeMember( const memberOrganizationsMap = await fetchManyMemberOrgsWithOrgData(tx, [memberId]) const memberOrganizations = memberOrganizationsMap.get(memberId) ?? [] + const isRoleIn = (role: IMemberRoleWithOrganization, roles: IMemberRoleWithOrganization[]) => + roleExistsInArray(role, roles, MemberRoleUnmergeStrategy.SAME_MEMBER) + rolesToDelete = memberOrganizations.filter( (r) => - r.source !== 'ui' && - !primary.memberOrganizations.some( - (pr) => - pr.organizationId === r.organizationId && - pr.title === r.title && - pr.dateStart === r.dateStart && - pr.dateEnd === r.dateEnd, - ), + !isRoleIn(r, primary.memberOrganizations) && + (r.source !== OrganizationSource.UI || isRoleIn(r, secondary.memberOrganizations)), ) for (const role of rolesToDelete) { From 659a0e2925d05c1d720c0ff429fc3714272a8ed4 Mon Sep 17 00:00:00 2001 From: Yeganathan S <63534555+skwowet@users.noreply.github.com> Date: Mon, 2 Nov 2026 15:45:24 +0100 Subject: [PATCH 2/2] fix: compare unmerge role dates by value Signed-off-by: Yeganathan S <63534555+skwowet@users.noreply.github.com> --- backend/src/services/organizationService.ts | 12 +++++------- .../src/services/member-organization.ts | 9 +++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/backend/src/services/organizationService.ts b/backend/src/services/organizationService.ts index 0ae4fb04a4..e25227a79d 100644 --- a/backend/src/services/organizationService.ts +++ b/backend/src/services/organizationService.ts @@ -19,7 +19,7 @@ import { mergeObjects, normalizeHostname, } from '@crowd/common' -import { signalMemberUpdate, unmergeRoles } from '@crowd/common_services' +import { roleExistsInArray, signalMemberUpdate, unmergeRoles } from '@crowd/common_services' import { addMemberRole, moveMembersBetweenOrganizations, @@ -416,12 +416,10 @@ export default class OrganizationService extends LoggerBase { // check if anything to delete in primary const rolesToDelete = memberOrganizations.filter( (r) => - !primaryUnmergedRoles.some( - (pr) => - pr.memberId === r.memberId && - pr.title === r.title && - pr.dateStart === r.dateStart && - pr.dateEnd === r.dateEnd, + !roleExistsInArray( + r, + primaryUnmergedRoles, + MemberRoleUnmergeStrategy.SAME_ORGANIZATION, ), ) diff --git a/services/libs/common_services/src/services/member-organization.ts b/services/libs/common_services/src/services/member-organization.ts index 134f433c61..07ba071984 100644 --- a/services/libs/common_services/src/services/member-organization.ts +++ b/services/libs/common_services/src/services/member-organization.ts @@ -17,6 +17,11 @@ function roleKey( return role.memberId } +function isSameDate(a: Date | string | null, b: Date | string | null): boolean { + if (!a || !b) return !a && !b + return new Date(a).getTime() === new Date(b).getTime() +} + export function roleExistsInArray( role: IMemberOrganization, roles: IMemberOrganization[], @@ -27,8 +32,8 @@ export function roleExistsInArray( (r) => roleKey(r, strategy) === key && r.title === role.title && - r.dateStart === role.dateStart && - r.dateEnd === role.dateEnd, + isSameDate(r.dateStart, role.dateStart) && + isSameDate(r.dateEnd, role.dateEnd), ) }