diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembersRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembersRepository.java index 4b43f73cc..e9614d5bd 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembersRepository.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/domain/ClientFamilyMembersRepository.java @@ -28,16 +28,27 @@ public interface ClientFamilyMembersRepository extends JpaRepository, JpaSpecificationExecutor { /** - * Counts non-deleted family members for a client that hold a given relationship code, optionally - * ignoring one specific family member (used at update time so a row doesn't conflict with itself). - * Backs the single-use relationship rule for Mother / Father / Spouse. + * Counts non-deleted family members for a client that hold a given relationship code. + * Backs the single-use relationship rule for Mother / Father / Spouse on create. */ @Query("SELECT COUNT(fm) FROM ClientFamilyMembers fm " + "WHERE fm.client.id = :clientId " + "AND fm.relationship.id = :relationshipId " - + "AND fm.isDeleted = false " - + "AND (:excludeFamilyMemberId IS NULL OR fm.id <> :excludeFamilyMemberId)") + + "AND fm.isDeleted = false") long countActiveByClientAndRelationship(@Param("clientId") Long clientId, + @Param("relationshipId") Long relationshipId); + + // Update variant: excludes one specific row so it doesn't conflict with itself. + // Why: PostgreSQL parses parameter types at parse-time; a nullable Long parameter compared with + // <> against a bigint column gets bound as varchar when null, producing + // "operator does not exist: bigint <> character varying". Splitting create vs. update keeps + // both queries non-nullable. + @Query("SELECT COUNT(fm) FROM ClientFamilyMembers fm " + + "WHERE fm.client.id = :clientId " + + "AND fm.relationship.id = :relationshipId " + + "AND fm.isDeleted = false " + + "AND fm.id <> :excludeFamilyMemberId") + long countActiveByClientAndRelationshipExcluding(@Param("clientId") Long clientId, @Param("relationshipId") Long relationshipId, @Param("excludeFamilyMemberId") Long excludeFamilyMemberId); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java index 62f941f83..eca15dc08 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientFamilyMembersWritePlatformServiceImpl.java @@ -99,8 +99,9 @@ public class ClientFamilyMembersWritePlatformServiceImpl implements ClientFamily if (!SINGLE_USE_RELATIONSHIPS.contains(label)) { return; } - final long existing = clientFamilyRepository.countActiveByClientAndRelationship( - clientId, relationship.getId(), excludeMemberId); + final long existing = (excludeMemberId == null) + ? clientFamilyRepository.countActiveByClientAndRelationship(clientId, relationship.getId()) + : clientFamilyRepository.countActiveByClientAndRelationshipExcluding(clientId, relationship.getId(), excludeMemberId); if (existing > 0) { final String displayName = relationship.getLabel(); throw new PlatformApiDataValidationException(java.util.List.of(ApiParameterError.parameterError(