FINERACT-1760: Raise transaction replayed business event

This commit is contained in:
Adam Saghy
2022-11-29 10:04:22 +01:00
committed by Arnold Gálovics
parent bbd1ca75a1
commit 4c597b61fb
13 changed files with 310 additions and 52 deletions
@@ -51,20 +51,27 @@ public class COBBusinessStepServiceImpl implements COBBusinessStepService {
if (executionMap == null || executionMap.isEmpty()) {
throw new BusinessStepException("Execution map is empty! COB Business step execution skipped!");
}
businessEventNotifierService.startExternalEventRecording();
for (String businessStep : executionMap.values()) {
try {
ThreadLocalContextUtil.setActionContext(ActionContext.COB);
COBBusinessStep<S> businessStepBean = (COBBusinessStep<S>) applicationContext.getBean(businessStep);
item = businessStepBean.execute(item);
} catch (Exception e) {
throw new BusinessStepException("Error happened during business step execution", e);
} finally {
// Fallback to COB action context after each business step
ThreadLocalContextUtil.setActionContext(ActionContext.COB);
// Extra safety net to avoid event leaking
try {
businessEventNotifierService.startExternalEventRecording();
for (String businessStep : executionMap.values()) {
try {
ThreadLocalContextUtil.setActionContext(ActionContext.COB);
COBBusinessStep<S> businessStepBean = (COBBusinessStep<S>) applicationContext.getBean(businessStep);
item = businessStepBean.execute(item);
} catch (Exception e) {
throw new BusinessStepException("Error happened during business step execution", e);
} finally {
// Fallback to COB action context after each business step
ThreadLocalContextUtil.setActionContext(ActionContext.COB);
}
}
businessEventNotifierService.stopExternalEventRecording();
} catch (Exception e) {
businessEventNotifierService.resetEventRecording();
throw e;
}
businessEventNotifierService.stopExternalEventRecording();
return item;
}
@@ -50,4 +50,6 @@ public interface BusinessEventNotifierService {
void startExternalEventRecording();
void stopExternalEventRecording();
void resetEventRecording();
}
@@ -155,4 +155,10 @@ public class BusinessEventNotifierServiceImpl implements BusinessEventNotifierSe
recordedEvents.remove();
}
}
@Override
public void resetEventRecording() {
eventRecordingEnabled.set(false);
recordedEvents.remove();
}
}
@@ -91,6 +91,7 @@ import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
import org.apache.fineract.portfolio.note.domain.Note;
import org.apache.fineract.portfolio.note.domain.NoteRepository;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
@@ -127,6 +128,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
private final DelinquencyWritePlatformService delinquencyWritePlatformService;
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
private final ExternalIdFactory externalIdFactory;
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
@Transactional
@Override
@@ -210,11 +212,14 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
if (changedTransactionDetail != null) {
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
saveLoanTransactionWithDataIntegrityViolationChecks(mapEntry.getValue());
// update loan with references to the newly created transactions
loan.addLoanTransaction(mapEntry.getValue());
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
if (StringUtils.isNotBlank(noteText)) {
@@ -788,6 +793,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
loan.getLoanTransactions().add(mapEntry.getValue());
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
@@ -30,6 +30,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper;
@@ -78,10 +79,10 @@ import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanResch
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException;
import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler;
import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService;
import org.apache.fineract.portfolio.loanaccount.service.ReplayedTransactionBusinessEventService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.orm.jpa.JpaSystemException;
@@ -89,6 +90,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanRescheduleRequestWritePlatformService {
private static final Logger LOG = LoggerFactory.getLogger(LoanRescheduleRequestWritePlatformServiceImpl.class);
@@ -111,45 +113,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
private static final DefaultScheduledDateGenerator DEFAULT_SCHEDULED_DATE_GENERATOR = new DefaultScheduledDateGenerator();
private final LoanAccountDomainService loanAccountDomainService;
private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository;
/**
* LoanRescheduleRequestWritePlatformServiceImpl constructor
*
*
**/
@Autowired
public LoanRescheduleRequestWritePlatformServiceImpl(final CodeValueRepositoryWrapper codeValueRepositoryWrapper,
final PlatformSecurityContext platformSecurityContext,
final LoanRescheduleRequestDataValidator loanRescheduleRequestDataValidator,
final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository,
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
final LoanTransactionRepository loanTransactionRepository,
final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanRepositoryWrapper loanRepositoryWrapper,
final LoanAssembler loanAssembler, final LoanUtilService loanUtilService,
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
final LoanScheduleGeneratorFactory loanScheduleFactory, final LoanSummaryWrapper loanSummaryWrapper,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final LoanAccountDomainService loanAccountDomainService,
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository) {
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
this.platformSecurityContext = platformSecurityContext;
this.loanRescheduleRequestDataValidator = loanRescheduleRequestDataValidator;
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
this.loanRepaymentScheduleHistoryRepository = loanRepaymentScheduleHistoryRepository;
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
this.loanTransactionRepository = loanTransactionRepository;
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.loanRepositoryWrapper = loanRepositoryWrapper;
this.loanAssembler = loanAssembler;
this.loanUtilService = loanUtilService;
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
this.loanScheduleFactory = loanScheduleFactory;
this.loanSummaryWrapper = loanSummaryWrapper;
this.accountTransfersWritePlatformService = accountTransfersWritePlatformService;
this.loanAccountDomainService = loanAccountDomainService;
this.repaymentScheduleInstallmentRepository = repaymentScheduleInstallmentRepository;
}
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
/**
* create a new instance of the LoanRescheduleRequest object from the JsonCommand object and persist
@@ -490,6 +454,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
@@ -155,6 +155,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
private final ExternalIdFactory externalIdFactory;
private final AccountTransferDetailRepository accountTransferDetailRepository;
private final LoanChargeAssembler loanChargeAssembler;
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) {
return e.getFromDate().isBefore(loanCharge.getDueDate()) && !loanCharge.getDueDate().isAfter(e.getDueDate());
@@ -246,6 +247,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
this.loanRepositoryWrapper.save(loan);
}
@@ -740,6 +743,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
}
@@ -1085,6 +1090,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
}
}
@@ -241,6 +241,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
private final LoanLifecycleStateMachine defaultLoanLifecycleStateMachine;
private final LoanAccountLockService loanAccountLockService;
private final ExternalIdFactory externalIdFactory;
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
@Transactional
@Override
@@ -457,6 +458,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanTransactionRepository.save(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
// auto create standing instruction
@@ -732,6 +735,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanTransactionRepository.save(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
}
@@ -1129,6 +1134,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
final String noteText = command.stringValueOfParameterNamed("note");
@@ -1360,6 +1367,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
final String noteText = command.stringValueOfParameterNamed("note");
@@ -2128,6 +2137,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
this.loanTransactionRepository.save(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
saveLoanWithDataIntegrityViolationChecks(loan);
@@ -2268,8 +2279,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
if (command.entityId() != null && changedTransactionDetail != null) {
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
this.loanTransactionRepository.save(mapEntry.getValue());
updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
@@ -2326,6 +2340,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
loan.addLoanTransaction(mapEntry.getValue());
this.accountTransfersWritePlatformService.updateLoanTransaction(mapEntry.getKey(), mapEntry.getValue());
}
// Trigger transaction replayed event
replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail);
}
postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
loanAccountDomainService.recalculateAccruals(loan);
@@ -0,0 +1,26 @@
/**
* 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.loanaccount.service;
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
public interface ReplayedTransactionBusinessEventService {
void raiseTransactionReplayedEvents(ChangedTransactionDetail changedTransactionDetail);
}
@@ -0,0 +1,59 @@
/**
* 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.loanaccount.service;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.portfolio.loanaccount.domain.ChangedTransactionDetail;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ReplayedTransactionBusinessEventServiceImpl implements ReplayedTransactionBusinessEventService {
private final BusinessEventNotifierService businessEventNotifierService;
private final LoanTransactionRepository loanTransactionRepository;
@Override
public void raiseTransactionReplayedEvents(final ChangedTransactionDetail changedTransactionDetail) {
if (changedTransactionDetail == null || changedTransactionDetail.getNewTransactionMappings().isEmpty()) {
return;
}
// Extra safety net to avoid event leaking
try {
businessEventNotifierService.startExternalEventRecording();
for (Map.Entry<Long, LoanTransaction> mapEntry : changedTransactionDetail.getNewTransactionMappings().entrySet()) {
LoanTransaction oldTransaction = loanTransactionRepository.findById(mapEntry.getKey())
.orElseThrow(() -> new LoanTransactionNotFoundException(mapEntry.getKey()));
LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(oldTransaction);
data.setNewTransactionDetail(mapEntry.getValue());
businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data));
}
businessEventNotifierService.stopExternalEventRecording();
} catch (Exception e) {
businessEventNotifierService.resetEventRecording();
throw e;
}
}
}