|
7 | 7 |
|
8 | 8 | import { |
9 | 9 | changeOverride, |
10 | | - deleteAffiliationOverrides, |
11 | 10 | findMemberAffiliationOverrides, |
12 | 11 | findOrganizationAffiliationOverrides, |
13 | 12 | } from '../member_organization_affiliation_overrides' |
@@ -424,35 +423,51 @@ export async function findNonIntersectingRoles( |
424 | 423 | } |
425 | 424 |
|
426 | 425 | export async function removeMemberRole(qx: QueryExecutor, role: IMemberOrganization) { |
427 | | - if (role.id) { |
428 | | - await deleteAffiliationOverrides(qx, role.memberId, [role.id]) |
429 | | - } |
430 | | - |
431 | | - let deleteMemberRole = `DELETE FROM "memberOrganizations" |
432 | | - WHERE |
433 | | - "organizationId" = $(organizationId) and |
434 | | - "memberId" = $(memberId)` |
| 426 | + const conditions = ['"organizationId" = $(organizationId)', '"memberId" = $(memberId)'] |
435 | 427 |
|
436 | 428 | const replacements: Record<string, unknown> = { |
437 | 429 | organizationId: role.organizationId, |
438 | 430 | memberId: role.memberId, |
439 | 431 | } |
440 | 432 |
|
441 | 433 | if (role.dateStart === null) { |
442 | | - deleteMemberRole += ` and "dateStart" is null ` |
| 434 | + conditions.push('"dateStart" IS NULL') |
443 | 435 | } else { |
444 | | - deleteMemberRole += ` and "dateStart" = $(dateStart) ` |
| 436 | + conditions.push('"dateStart" = $(dateStart)') |
445 | 437 | replacements.dateStart = (role.dateStart as Date).toISOString() |
446 | 438 | } |
447 | 439 |
|
448 | 440 | if (role.dateEnd === null) { |
449 | | - deleteMemberRole += ` and "dateEnd" is null ` |
| 441 | + conditions.push('"dateEnd" IS NULL') |
450 | 442 | } else { |
451 | | - deleteMemberRole += ` and "dateEnd" = $(dateEnd) ` |
| 443 | + conditions.push('"dateEnd" = $(dateEnd)') |
452 | 444 | replacements.dateEnd = (role.dateEnd as Date).toISOString() |
453 | 445 | } |
454 | 446 |
|
455 | | - await qx.result(deleteMemberRole, replacements) |
| 447 | + const whereClause = conditions.join(' AND ') |
| 448 | + |
| 449 | + await qx.tx(async (tx) => { |
| 450 | + // Delete affiliation overrides first using subquery |
| 451 | + await tx.result( |
| 452 | + ` |
| 453 | + DELETE FROM "memberOrganizationAffiliationOverrides" |
| 454 | + WHERE "memberOrganizationId" IN ( |
| 455 | + SELECT id FROM "memberOrganizations" |
| 456 | + WHERE ${whereClause} |
| 457 | + ) |
| 458 | + `, |
| 459 | + replacements, |
| 460 | + ) |
| 461 | + |
| 462 | + // Then delete the role |
| 463 | + await tx.result( |
| 464 | + ` |
| 465 | + DELETE FROM "memberOrganizations" |
| 466 | + WHERE ${whereClause} |
| 467 | + `, |
| 468 | + replacements, |
| 469 | + ) |
| 470 | + }) |
456 | 471 | } |
457 | 472 |
|
458 | 473 | export async function addMemberRole( |
@@ -615,6 +630,22 @@ function transformRoleToTargetEntity( |
615 | 630 | } |
616 | 631 | } |
617 | 632 |
|
| 633 | +function areDatesEqual(dateA: Date | string | null, dateB: Date | string | null): boolean { |
| 634 | + if (dateA === null && dateB === null) return true |
| 635 | + if (dateA === null || dateB === null) return false |
| 636 | + return new Date(dateA).getTime() === new Date(dateB).getTime() |
| 637 | +} |
| 638 | + |
| 639 | +function isSamePrimaryRole(a: IMemberOrganization, b: IMemberOrganization): boolean { |
| 640 | + const isSameMember = a.memberId === b.memberId |
| 641 | + const isSameOrganization = a.organizationId === b.organizationId |
| 642 | + const isSameTitle = a.title === b.title |
| 643 | + const hasSameStartDate = areDatesEqual(a.dateStart, b.dateStart) |
| 644 | + const hasSameEndDate = areDatesEqual(a.dateEnd, b.dateEnd) |
| 645 | + |
| 646 | + return isSameMember && isSameOrganization && isSameTitle && hasSameStartDate && hasSameEndDate |
| 647 | +} |
| 648 | + |
618 | 649 | export async function mergeRoles( |
619 | 650 | qx: QueryExecutor, |
620 | 651 | primaryRoles: IMemberOrganization[], |
@@ -759,42 +790,28 @@ export async function mergeRoles( |
759 | 790 | return item.role.memberId === addRole.memberId && item.role.title === addRole.title |
760 | 791 | }) |
761 | 792 |
|
762 | | - let overrideToApply: IMemberOrganizationAffiliationOverride | undefined |
763 | 793 | if (relevantOverrides.length > 0) { |
764 | 794 | // Prefer the override from the primary role if it exists |
765 | 795 | const primaryOverride = relevantOverrides.find((item) => |
766 | 796 | primaryRoles.some((primaryRole) => primaryRole.id === item.role.id), |
767 | 797 | ) |
768 | 798 |
|
769 | 799 | // If we found a primary override, use it, otherwise, use the first one |
770 | | - overrideToApply = primaryOverride?.override || relevantOverrides[0]?.override |
771 | | - } |
| 800 | + const overrideToApply = primaryOverride?.override || relevantOverrides[0]?.override |
772 | 801 |
|
773 | | - if (overrideToApply) { |
774 | | - await changeOverride(qx, { |
775 | | - ...overrideToApply, |
776 | | - memberId: mergeStrat.targetMemberId(addRole), |
777 | | - memberOrganizationId: newRoleId, |
778 | | - }) |
| 802 | + if (overrideToApply) { |
| 803 | + await changeOverride(qx, { |
| 804 | + ...overrideToApply, |
| 805 | + memberId: mergeStrat.targetMemberId(addRole), |
| 806 | + memberOrganizationId: newRoleId, |
| 807 | + }) |
| 808 | + } |
779 | 809 | } |
780 | 810 | } else { |
781 | 811 | // Role already exists (duplicate), need to transfer override to existing role |
782 | 812 |
|
783 | 813 | // Find the existing role in primary that matches this addRole |
784 | | - const existingPrimaryRole = primaryRoles.find( |
785 | | - (pr) => |
786 | | - pr.memberId === addRole.memberId && |
787 | | - pr.organizationId === addRole.organizationId && |
788 | | - pr.title === addRole.title && |
789 | | - ((pr.dateStart === null && addRole.dateStart === null) || |
790 | | - (pr.dateStart && |
791 | | - addRole.dateStart && |
792 | | - new Date(pr.dateStart).getTime() === new Date(addRole.dateStart).getTime())) && |
793 | | - ((pr.dateEnd === null && addRole.dateEnd === null) || |
794 | | - (pr.dateEnd && |
795 | | - addRole.dateEnd && |
796 | | - new Date(pr.dateEnd).getTime() === new Date(addRole.dateEnd).getTime())), |
797 | | - ) |
| 814 | + const existingPrimaryRole = primaryRoles.find((pr) => isSamePrimaryRole(pr, addRole)) |
798 | 815 |
|
799 | 816 | if (existingPrimaryRole) { |
800 | 817 | // Find overrides from secondary roles that should be transferred |
|
0 commit comments