diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCloseBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCloseBusinessEvent.java new file mode 100644 index 00000000000..1c20dfaef25 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientCloseBusinessEvent.java @@ -0,0 +1,35 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.event.business.domain.client; + +import org.apache.fineract.portfolio.client.domain.Client; + +public class ClientCloseBusinessEvent extends ClientBusinessEvent { + + private static final String TYPE = "ClientCloseBusinessEvent"; + + public ClientCloseBusinessEvent(Client value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientReactivateBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientReactivateBusinessEvent.java new file mode 100644 index 00000000000..4d12d53ca6f --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientReactivateBusinessEvent.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.infrastructure.event.business.domain.client; + +import org.apache.fineract.portfolio.client.domain.Client; + +public class ClientReactivateBusinessEvent extends ClientBusinessEvent { + + private static final String TYPE = "ClientReactivateBusinessEvent"; + + public ClientReactivateBusinessEvent(Client value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientUndoRejectionBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientUndoRejectionBusinessEvent.java new file mode 100644 index 00000000000..65e070b1fb1 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientUndoRejectionBusinessEvent.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.infrastructure.event.business.domain.client; + +import org.apache.fineract.portfolio.client.domain.Client; + +public class ClientUndoRejectionBusinessEvent extends ClientBusinessEvent { + + private static final String TYPE = "ClientUndoRejectionBusinessEvent"; + + public ClientUndoRejectionBusinessEvent(Client value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientUndoWithdrawalBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientUndoWithdrawalBusinessEvent.java new file mode 100644 index 00000000000..9396918f307 --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientUndoWithdrawalBusinessEvent.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.infrastructure.event.business.domain.client; + +import org.apache.fineract.portfolio.client.domain.Client; + +public class ClientUndoWithdrawalBusinessEvent extends ClientBusinessEvent { + + private static final String TYPE = "ClientUndoWithdrawalBusinessEvent"; + + public ClientUndoWithdrawalBusinessEvent(Client value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientWithdrawBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientWithdrawBusinessEvent.java new file mode 100644 index 00000000000..873ae7a344d --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/client/ClientWithdrawBusinessEvent.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.infrastructure.event.business.domain.client; + +import org.apache.fineract.portfolio.client.domain.Client; + +public class ClientWithdrawBusinessEvent extends ClientBusinessEvent { + + private static final String TYPE = "ClientWithdrawBusinessEvent"; + + public ClientWithdrawBusinessEvent(Client value) { + super(value); + } + + @Override + public String getType() { + return TYPE; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java index 540ecedebf5..00d53f53182 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImpl.java @@ -26,7 +26,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -52,8 +51,13 @@ import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum; import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService; import org.apache.fineract.infrastructure.event.business.domain.client.ClientActivateBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.client.ClientCloseBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.client.ClientCreateBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.client.ClientReactivateBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.client.ClientRejectBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.client.ClientUndoRejectionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.client.ClientUndoWithdrawalBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.client.ClientWithdrawBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.organisation.office.domain.Office; @@ -91,12 +95,13 @@ import org.apache.fineract.portfolio.savings.exception.SavingsProductNotFoundException; import org.apache.fineract.portfolio.savings.service.SavingsApplicationProcessWritePlatformService; import org.apache.fineract.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.orm.jpa.JpaSystemException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -@AllArgsConstructor @Service @Slf4j public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWritePlatformService { @@ -125,6 +130,47 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP private final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService; private final ExternalIdFactory externalIdFactory; + @Autowired + public ClientWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, + final ClientRepositoryWrapper clientRepository, final ClientNonPersonRepositoryWrapper clientNonPersonRepository, + final OfficeRepositoryWrapper officeRepositoryWrapper, final NoteRepository noteRepository, + final GroupRepository groupRepository, final ClientDataValidator fromApiJsonDeserializer, + final AccountNumberGenerator accountNumberGenerator, final StaffRepositoryWrapper staffRepository, + final CodeValueRepositoryWrapper codeValueRepository, final LoanRepositoryWrapper loanRepositoryWrapper, + final SavingsAccountRepositoryWrapper savingsRepositoryWrapper, final SavingsProductRepository savingsProductRepository, + final SavingsApplicationProcessWritePlatformService savingsApplicationProcessWritePlatformService, + final CommandProcessingService commandProcessingService, final ConfigurationDomainService configurationDomainService, + final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, final FromJsonHelper fromApiJsonHelper, + final AddressWritePlatformService addressWritePlatformService, + final ClientFamilyMembersWritePlatformService clientFamilyMembersWritePlatformService, + @Lazy final BusinessEventNotifierService businessEventNotifierService, // BREAKS THE CYCLE + final EntityDatatableChecksWritePlatformService entityDatatableChecksWritePlatformService, + final ExternalIdFactory externalIdFactory) { + this.context = context; + this.clientRepository = clientRepository; + this.clientNonPersonRepository = clientNonPersonRepository; + this.officeRepositoryWrapper = officeRepositoryWrapper; + this.noteRepository = noteRepository; + this.groupRepository = groupRepository; + this.fromApiJsonDeserializer = fromApiJsonDeserializer; + this.accountNumberGenerator = accountNumberGenerator; + this.staffRepository = staffRepository; + this.codeValueRepository = codeValueRepository; + this.loanRepositoryWrapper = loanRepositoryWrapper; + this.savingsRepositoryWrapper = savingsRepositoryWrapper; + this.savingsProductRepository = savingsProductRepository; + this.savingsApplicationProcessWritePlatformService = savingsApplicationProcessWritePlatformService; + this.commandProcessingService = commandProcessingService; + this.configurationDomainService = configurationDomainService; + this.accountNumberFormatRepository = accountNumberFormatRepository; + this.fromApiJsonHelper = fromApiJsonHelper; + this.addressWritePlatformService = addressWritePlatformService; + this.clientFamilyMembersWritePlatformService = clientFamilyMembersWritePlatformService; + this.businessEventNotifierService = businessEventNotifierService; + this.entityDatatableChecksWritePlatformService = entityDatatableChecksWritePlatformService; + this.externalIdFactory = externalIdFactory; + } + @Transactional @Override public CommandProcessingResult deleteClient(final Long clientId) { @@ -885,6 +931,7 @@ public CommandProcessingResult closeClient(final Long clientId, final JsonComman client.close(currentUser, closureReason, closureDate); this.clientRepository.saveAndFlush(client); + businessEventNotifierService.notifyPostBusinessEvent(new ClientCloseBusinessEvent(client)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withClientId(clientId) // @@ -1014,6 +1061,7 @@ public CommandProcessingResult withdrawClient(Long entityId, JsonCommand command } client.withdraw(currentUser, withdrawalReason, withdrawalDate); this.clientRepository.saveAndFlush(client); + businessEventNotifierService.notifyPostBusinessEvent(new ClientWithdrawBusinessEvent(client)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withClientId(entityId) // @@ -1040,6 +1088,7 @@ public CommandProcessingResult reActivateClient(Long entityId, JsonCommand comma } client.reActivate(currentUser, reactivateDate); this.clientRepository.saveAndFlush(client); + businessEventNotifierService.notifyPostBusinessEvent(new ClientReactivateBusinessEvent(client)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withClientId(entityId) // @@ -1067,7 +1116,7 @@ public CommandProcessingResult undoRejection(Long entityId, JsonCommand command) client.reOpened(currentUser, undoRejectDate); this.clientRepository.saveAndFlush(client); - + businessEventNotifierService.notifyPostBusinessEvent(new ClientUndoRejectionBusinessEvent(client)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withClientId(entityId) // @@ -1094,7 +1143,7 @@ public CommandProcessingResult undoWithdrawal(Long entityId, JsonCommand command } client.reOpened(currentUser, undoWithdrawalDate); this.clientRepository.saveAndFlush(client); - + businessEventNotifierService.notifyPostBusinessEvent(new ClientUndoWithdrawalBusinessEvent(client)); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withClientId(entityId) // diff --git a/fineract-provider/src/main/resources/application.properties b/fineract-provider/src/main/resources/application.properties index 93c249625cd..a0d7a1ca73f 100644 --- a/fineract-provider/src/main/resources/application.properties +++ b/fineract-provider/src/main/resources/application.properties @@ -574,3 +574,4 @@ resilience4j.retry.instances.commandAuditFallback.wait-duration=${FINERACT_PROCE resilience4j.retry.instances.commandAuditFallback.enable-exponential-backoff=${FINERACT_PROCESS_COMMAND_AUDIT_FALLBACK_RETRY_ENABLE_EXPONENTIAL_BACKOFF:true} resilience4j.retry.instances.commandAuditFallback.exponential-backoff-multiplier=${FINERACT_PROCESS_COMMAND_AUDIT_FALLBACK_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2} resilience4j.retry.instances.commandAuditFallback.retryExceptions=${FINERACT_PROCESS_COMMAND_AUDIT_FALLBACK_RETRY_EXCEPTIONS:org.springframework.dao.ConcurrencyFailureException,org.eclipse.persistence.exceptions.OptimisticLockException,jakarta.persistence.OptimisticLockException,org.springframework.orm.jpa.JpaOptimisticLockingFailureException} +fineract.events.external.enabled=false diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImplTest.java new file mode 100644 index 00000000000..f5ebb99ed9a --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/client/service/ClientWritePlatformServiceJpaRepositoryImplTest.java @@ -0,0 +1,75 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.fineract.portfolio.client.service; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.LocalDate; +import org.apache.fineract.infrastructure.core.api.JsonCommand; +import org.apache.fineract.infrastructure.event.business.domain.client.ClientUndoWithdrawalBusinessEvent; +import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.client.data.ClientDataValidator; +import org.apache.fineract.portfolio.client.domain.Client; +import org.apache.fineract.portfolio.client.domain.ClientRepositoryWrapper; +import org.apache.fineract.useradministration.domain.AppUser; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class ClientWritePlatformServiceJpaRepositoryImplTest { + + @Mock + private ClientRepositoryWrapper clientRepository; + + @Mock + private BusinessEventNotifierService businessEventNotifierService; + + @Mock + private PlatformSecurityContext context; + + @Mock + private ClientDataValidator fromApiJsonDeserializer; + + @InjectMocks + private ClientWritePlatformServiceJpaRepositoryImpl underTest; + + @Test + public void testUndoWithdrawalFiresBusinessEvent() { + Long clientId = 1L; + Client client = mock(Client.class); + AppUser currentUser = mock(AppUser.class); + JsonCommand command = mock(JsonCommand.class); + LocalDate dummyDate = LocalDate.now(); + when(clientRepository.findOneWithNotFoundDetection(clientId)).thenReturn(client); + when(context.authenticatedUser()).thenReturn(currentUser); + when(command.localDateValueOfParameterNamed(any(String.class))).thenReturn(dummyDate); + when(client.isWithdrawn()).thenReturn(true); + this.underTest.undoWithdrawal(clientId, command); + verify(businessEventNotifierService).notifyPostBusinessEvent(any(ClientUndoWithdrawalBusinessEvent.class)); + verify(clientRepository).saveAndFlush(client); + } +}