From ace59352fa995024acc4dce857e01ee3e09c86d2 Mon Sep 17 00:00:00 2001 From: mariiaKraievska Date: Tue, 12 Aug 2025 15:41:42 +0300 Subject: [PATCH] FINERACT-2233: Rework journal entry handling logic in Loan module --- .../LoanAccountOwnerTransferBusinessStep.java | 8 +- .../LoanAccountOwnerTransferServiceImpl.java | 5 +- ...nAccountOwnerTransferBusinessStepTest.java | 30 +- .../LoanAccountOwnerTransferServiceTest.java | 3 + .../mapper/LoanAccountingBridgeMapper.java | 274 ---------------- .../LoanAccrualsProcessingService.java | 4 +- .../service/LoanChargeService.java | 8 +- .../LoanDownPaymentHandlerServiceImpl.java | 2 + .../service/LoanJournalEntryPoster.java | 27 +- .../service/LoanRefundService.java | 22 +- ...epaymentScheduleProcessingWrapperTest.java | 3 +- .../BuyDownFeeWritePlatformServiceImpl.java | 13 +- ...talizedIncomeWritePlatformServiceImpl.java | 14 +- .../JournalEntryWritePlatformService.java | 29 ++ ...WritePlatformServiceJpaRepositoryImpl.java | 165 ++++++++++ .../domain/LoanAccountDomainServiceJpa.java | 131 ++------ .../service/LoanScheduleAssembler.java | 4 +- .../LoanScheduleWritePlatformServiceImpl.java | 2 +- ...heduleRequestWritePlatformServiceImpl.java | 25 +- ...nAccrualActivityProcessingServiceImpl.java | 3 + .../service/LoanAccrualEventService.java | 2 +- .../LoanAccrualsProcessingServiceImpl.java | 145 +++++---- ...WritePlatformServiceJpaRepositoryImpl.java | 2 +- .../service/LoanAssemblerImpl.java | 4 +- ...nFeeAmortizationProcessingServiceImpl.java | 31 +- ...comeAmortizationProcessingServiceImpl.java | 31 +- .../LoanChargeWritePlatformServiceImpl.java | 77 ++--- .../service/LoanDisbursementService.java | 12 +- .../service/LoanJournalEntryPosterImpl.java | 36 +-- ...WritePlatformServiceJpaRepositoryImpl.java | 299 ++++++------------ .../ReprocessLoanTransactionsServiceImpl.java | 17 + .../adjustment/LoanAdjustmentServiceImpl.java | 25 +- .../starter/LoanAccountConfiguration.java | 82 +++-- ...ntDelinquencyRangeEventSerializerTest.java | 3 +- .../LoanAdjustmentServiceImplTest.java | 21 +- ...oanChargeWritePlatformServiceImplTest.java | 16 +- ...LoanDownPaymentHandlerServiceImplTest.java | 5 +- 37 files changed, 609 insertions(+), 971 deletions(-) delete mode 100644 fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java index 3ba0963ee..523a0f6c7 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStep.java @@ -39,11 +39,11 @@ import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent; -import org.apache.fineract.investor.service.AccountingService; import org.apache.fineract.investor.service.DelayedSettlementAttributeService; import org.apache.fineract.investor.service.ExternalAssetOwnerTransferOutstandingInterestCalculation; import org.apache.fineract.investor.service.LoanTransferabilityService; import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster; import org.springframework.context.annotation.Conditional; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Component; @@ -61,7 +61,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep ExternalTransferStatus.BUYBACK); private final ExternalAssetOwnerTransferRepository externalAssetOwnerTransferRepository; private final ExternalAssetOwnerTransferLoanMappingRepository externalAssetOwnerTransferLoanMappingRepository; - private final AccountingService accountingService; + private final LoanJournalEntryPoster loanJournalEntryPoster; private final BusinessEventNotifierService businessEventNotifierService; private final LoanTransferabilityService loanTransferabilityService; private final DelayedSettlementAttributeService delayedSettlementAttributeService; @@ -148,7 +148,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep externalAssetOwnerTransferRepository.save(activeExternalAssetOwnerTransfer); buybackExternalAssetOwnerTransfer = externalAssetOwnerTransferRepository.save(buybackExternalAssetOwnerTransfer); externalAssetOwnerTransferLoanMappingRepository.deleteByLoanIdAndOwnerTransfer(loan.getId(), activeExternalAssetOwnerTransfer); - accountingService.createJournalEntriesForBuybackAssetTransfer(loan, buybackExternalAssetOwnerTransfer); + loanJournalEntryPoster.postJournalEntriesForExternalOwnerTransfer(loan, buybackExternalAssetOwnerTransfer, null); return buybackExternalAssetOwnerTransfer; } @@ -174,7 +174,7 @@ public class LoanAccountOwnerTransferBusinessStep implements LoanCOBBusinessStep ExternalTransferStatus activeStatus = determineActiveStatus(externalAssetOwnerTransfer); ExternalAssetOwnerTransfer newTransfer = activatePendingEntry(settlementDate, externalAssetOwnerTransfer, activeStatus); - accountingService.createJournalEntriesForSaleAssetTransfer(loan, newTransfer, previousOwner); + loanJournalEntryPoster.postJournalEntriesForExternalOwnerTransfer(loan, newTransfer, previousOwner); return newTransfer; } diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java b/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java index 28876ff79..5d397a003 100644 --- a/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java +++ b/fineract-investor/src/main/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceImpl.java @@ -47,6 +47,7 @@ import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent; import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -60,7 +61,7 @@ public class LoanAccountOwnerTransferServiceImpl implements LoanAccountOwnerTran public static final LocalDate FUTURE_DATE_9999_12_31 = LocalDate.of(9999, 12, 31); private final ExternalAssetOwnerTransferRepository externalAssetOwnerTransferRepository; private final ExternalAssetOwnerTransferLoanMappingRepository externalAssetOwnerTransferLoanMappingRepository; - private final AccountingService accountingService; + private final LoanJournalEntryPoster loanJournalEntryPoster; private final BusinessEventNotifierService businessEventNotifierService; private final ExternalAssetOwnerTransferOutstandingInterestCalculation externalAssetOwnerTransferOutstandingInterestCalculation; @@ -107,7 +108,7 @@ public class LoanAccountOwnerTransferServiceImpl implements LoanAccountOwnerTran buybackTransfer = updatePendingBuybackTransfer(loan, buybackTransfer); externalAssetOwnerTransferLoanMappingRepository.deleteByLoanIdAndOwnerTransfer(loan.getId(), activeTransfer); - accountingService.createJournalEntriesForBuybackAssetTransfer(loan, buybackTransfer); + loanJournalEntryPoster.postJournalEntriesForExternalOwnerTransfer(loan, buybackTransfer, null); businessEventNotifierService.notifyPostBusinessEvent(new LoanOwnershipTransferBusinessEvent(buybackTransfer, loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanAccountSnapshotBusinessEvent(loan)); diff --git a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java index 39c5db446..03b503080 100644 --- a/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java +++ b/fineract-investor/src/test/java/org/apache/fineract/investor/cob/loan/LoanAccountOwnerTransferBusinessStepTest.java @@ -56,13 +56,13 @@ import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMapping import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferLoanMappingRepository; import org.apache.fineract.investor.domain.ExternalAssetOwnerTransferRepository; import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent; -import org.apache.fineract.investor.service.AccountingService; import org.apache.fineract.investor.service.DelayedSettlementAttributeService; import org.apache.fineract.investor.service.ExternalAssetOwnerTransferOutstandingInterestCalculation; import org.apache.fineract.investor.service.LoanTransferabilityService; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary; +import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster; import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; @@ -97,7 +97,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { private ExternalAssetOwnerTransferLoanMappingRepository externalAssetOwnerTransferLoanMappingRepository; @Mock - private AccountingService accountingService; + private LoanJournalEntryPoster loanJournalEntryPoster; @Mock private BusinessEventNotifierService businessEventNotifierService; @@ -130,7 +130,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT); ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, actualDate))); underTest = new LoanAccountOwnerTransferBusinessStep(externalAssetOwnerTransferRepository, - externalAssetOwnerTransferLoanMappingRepository, accountingService, businessEventNotifierService, + externalAssetOwnerTransferLoanMappingRepository, loanJournalEntryPoster, businessEventNotifierService, loanTransferabilityService, delayedSettlementAttributeService, externalAssetOwnerTransferOutstandingInterestCalculation); } @@ -149,7 +149,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { final Loan processedLoan = underTest.execute(loanForProcessing); // then verify(externalAssetOwnerTransferRepository, times(1)).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id"))); - verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, accountingService); + verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, loanJournalEntryPoster); assertEquals(processedLoan, loanForProcessing); } @@ -176,7 +176,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { // then assertEquals("Illegal transfer found. Expected PENDING and BUYBACK, found: PENDING and ACTIVE", exception.getMessage()); verify(externalAssetOwnerTransferRepository, times(1)).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id"))); - verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, accountingService); + verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, loanJournalEntryPoster); } @Test @@ -202,7 +202,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { // then assertEquals("Delayed Settlement enabled, but found 2 transfers of statuses: PENDING and BUYBACK", exception.getMessage()); verify(externalAssetOwnerTransferRepository, times(1)).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id"))); - verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, accountingService); + verifyNoInteractions(businessEventNotifierService, loanTransferabilityService, loanJournalEntryPoster); } @Test @@ -273,7 +273,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { assertEquals(processedLoan, loanForProcessing); - verifyNoInteractions(loanTransferabilityService, accountingService); + verifyNoInteractions(loanTransferabilityService, loanJournalEntryPoster); ArgumentCaptor> businessEventArgumentCaptor = verifyBusinessEvents(2); verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, secondSaveResult); @@ -316,8 +316,8 @@ public class LoanAccountOwnerTransferBusinessStepTest { assertEquals(processedLoan, loanForProcessing); - verify(accountingService).createJournalEntriesForBuybackAssetTransfer(loanForProcessing, firstResponseItem); - verifyNoMoreInteractions(accountingService); + verify(loanJournalEntryPoster).postJournalEntriesForExternalOwnerTransfer(loanForProcessing, firstResponseItem, null); + verifyNoMoreInteractions(loanJournalEntryPoster); ArgumentCaptor> businessEventArgumentCaptor = verifyBusinessEvents(2); verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, firstResponseItem); @@ -387,8 +387,8 @@ public class LoanAccountOwnerTransferBusinessStepTest { verify(externalAssetOwnerTransferLoanMappingRepository).save(externalAssetOwnerTransferLoanMappingArgumentCaptor.capture()); - verify(accountingService).createJournalEntriesForSaleAssetTransfer(loanForProcessing, savedNewTransfer, null); - verifyNoMoreInteractions(accountingService); + verify(loanJournalEntryPoster).postJournalEntriesForExternalOwnerTransfer(loanForProcessing, savedNewTransfer, null); + verifyNoMoreInteractions(loanJournalEntryPoster); ArgumentCaptor> businessEventArgumentCaptor = verifyBusinessEvents(2); verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, savedNewTransfer); @@ -445,7 +445,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { assertEquals(actualDate, capturedActiveTransfer.getEffectiveDateTo()); assertEquals(processedLoan, loanForProcessing); - verifyNoInteractions(accountingService); + verifyNoInteractions(loanJournalEntryPoster); ArgumentCaptor> businessEventArgumentCaptor = verifyBusinessEvents(1); verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, savedNewTransfer); @@ -516,8 +516,8 @@ public class LoanAccountOwnerTransferBusinessStepTest { assertEquals(savedNewTransfer, externalAssetOwnerTransferLoanMappingArgumentCaptor.getValue().getOwnerTransfer()); assertEquals(processedLoan, loanForProcessing); - verify(accountingService).createJournalEntriesForSaleAssetTransfer(loanForProcessing, savedNewTransfer, previousOwner); - verifyNoMoreInteractions(accountingService); + verify(loanJournalEntryPoster).postJournalEntriesForExternalOwnerTransfer(loanForProcessing, savedNewTransfer, previousOwner); + verifyNoMoreInteractions(loanJournalEntryPoster); ArgumentCaptor> businessEventArgumentCaptor = verifyBusinessEvents(2); verifyLoanTransferBusinessEvent(businessEventArgumentCaptor, 0, loanForProcessing, savedNewTransfer); @@ -552,7 +552,7 @@ public class LoanAccountOwnerTransferBusinessStepTest { verify(loanTransferabilityService).isTransferable(loanForProcessing, pendingTransfer); verifyNoMoreInteractions(loanTransferabilityService); - verifyNoInteractions(accountingService); + verifyNoInteractions(loanJournalEntryPoster); verify(externalAssetOwnerTransferRepository).findAll(any(Specification.class), eq(Sort.by(Sort.Direction.ASC, "id"))); verify(externalAssetOwnerTransferRepository).findOne(any(Specification.class)); verify(externalAssetOwnerTransferRepository, never()).save(any(ExternalAssetOwnerTransfer.class)); diff --git a/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java b/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java index ac409b363..bbd251ec5 100644 --- a/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java +++ b/fineract-investor/src/test/java/org/apache/fineract/investor/service/LoanAccountOwnerTransferServiceTest.java @@ -50,6 +50,7 @@ import org.apache.fineract.investor.domain.LoanOwnershipTransferBusinessEvent; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary; +import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -82,6 +83,8 @@ public class LoanAccountOwnerTransferServiceTest { private BusinessEventNotifierService businessEventNotifierService; @Mock private ExternalAssetOwnerTransferOutstandingInterestCalculation externalAssetOwnerTransferOutstandingInterestCalculation; + @Mock + private LoanJournalEntryPoster loanJournalEntryPoster; @InjectMocks private LoanAccountOwnerTransferServiceImpl underTest; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java deleted file mode 100644 index c764ccff9..000000000 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanAccountingBridgeMapper.java +++ /dev/null @@ -1,274 +0,0 @@ -/** - * 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.mapper; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO; -import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByDTO; -import org.apache.fineract.portfolio.loanaccount.domain.Loan; -import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; -import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; -import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class LoanAccountingBridgeMapper { - - private final LoanTransactionRepository loanTransactionRepository; - - public List deriveAccountingBridgeDataForChargeOff(final String currencyCode, - final List existingTransactionIds, final List existingReversedTransactionIds, final boolean isAccountTransfer, - final Loan loan) { - final List newLoanTransactionsBeforeChargeOff = new ArrayList<>(); - final List newLoanTransactionsAfterChargeOff = new ArrayList<>(); - - // split the transactions according charge-off date - classifyTransactionsBasedOnChargeOffDate(newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, - existingTransactionIds, existingReversedTransactionIds, currencyCode, loan); - - AccountingBridgeDataDTO beforeChargeOff = new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), - currencyCode, loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(), - loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), - isAccountTransfer, false, loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(), - newLoanTransactionsBeforeChargeOff, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); - - AccountingBridgeDataDTO afterChargeOff = new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), - currencyCode, loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(), - loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), - isAccountTransfer, true, loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(), - newLoanTransactionsAfterChargeOff, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); - - List result = new ArrayList<>(); - result.add(beforeChargeOff); - result.add(afterChargeOff); - return result; - } - - public AccountingBridgeDataDTO deriveAccountingBridgeData(final String currencyCode, final List existingTransactionIds, - final List existingReversedTransactionIds, final boolean isAccountTransfer, final Loan loan) { - final List transactions = findTransactionsForAccountingBridge(existingTransactionIds, - existingReversedTransactionIds, loan); - - final List newLoanTransactions = transactions.stream() // - .map(transaction -> mapToLoanTransactionData(transaction, currencyCode)) // - .toList(); - - return new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), currencyCode, - loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(), - loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), - isAccountTransfer, loan.isChargedOff(), loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(), - newLoanTransactions, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); - } - - public AccountingBridgeLoanTransactionDTO mapToLoanTransactionData(final LoanTransaction transaction, final String currencyCode) { - final AccountingBridgeLoanTransactionDTO transactionDTO = new AccountingBridgeLoanTransactionDTO(); - - transactionDTO.setId(transaction.getId()); - transactionDTO.setOfficeId(transaction.getOffice().getId()); - transactionDTO.setType(LoanEnumerations.transactionType(transaction.getTypeOf())); - transactionDTO.setReversed(transaction.isReversed()); - transactionDTO.setDate(transaction.getTransactionDate()); - transactionDTO.setCurrencyCode(currencyCode); - transactionDTO.setAmount(transaction.getAmount()); - transactionDTO.setNetDisbursalAmount(transaction.getLoan().getNetDisbursalAmount()); - - if (transactionDTO.getType().isChargeback() && (transaction.getLoan().getCreditAllocationRules() == null - || transaction.getLoan().getCreditAllocationRules().isEmpty())) { - transactionDTO.setPrincipalPortion(transaction.getAmount()); - } else { - transactionDTO.setPrincipalPortion(transaction.getPrincipalPortion()); - } - - transactionDTO.setInterestPortion(transaction.getInterestPortion()); - transactionDTO.setFeeChargesPortion(transaction.getFeeChargesPortion()); - transactionDTO.setPenaltyChargesPortion(transaction.getPenaltyChargesPortion()); - transactionDTO.setOverPaymentPortion(transaction.getOverPaymentPortion()); - - if (transactionDTO.getType().isChargeRefund()) { - transactionDTO.setChargeRefundChargeType(transaction.getChargeRefundChargeType()); - } - - if (transaction.getPaymentDetail() != null) { - transactionDTO.setPaymentTypeId(transaction.getPaymentDetail().getPaymentType().getId()); - } - - if (!transaction.getLoanChargesPaid().isEmpty()) { - List loanChargesPaidData = new ArrayList<>(); - for (final LoanChargePaidBy chargePaidBy : transaction.getLoanChargesPaid()) { - final LoanChargePaidByDTO loanChargePaidData = new LoanChargePaidByDTO(); - loanChargePaidData.setChargeId(chargePaidBy.getLoanCharge().getCharge().getId()); - loanChargePaidData.setIsPenalty(chargePaidBy.getLoanCharge().isPenaltyCharge()); - loanChargePaidData.setLoanChargeId(chargePaidBy.getLoanCharge().getId()); - loanChargePaidData.setAmount(chargePaidBy.getAmount()); - loanChargePaidData.setInstallmentNumber(chargePaidBy.getInstallmentNumber()); - - loanChargesPaidData.add(loanChargePaidData); - } - transactionDTO.setLoanChargesPaid(loanChargesPaidData); - } - - if (transactionDTO.getType().isChargeback() && transaction.getOverPaymentPortion() != null - && transaction.getOverPaymentPortion().compareTo(BigDecimal.ZERO) > 0) { - BigDecimal principalPaid = transaction.getOverPaymentPortion(); - BigDecimal feePaid = BigDecimal.ZERO; - BigDecimal penaltyPaid = BigDecimal.ZERO; - if (!transaction.getLoanTransactionToRepaymentScheduleMappings().isEmpty()) { - principalPaid = transaction.getLoanTransactionToRepaymentScheduleMappings().stream() - .map(mapping -> Optional.ofNullable(mapping.getPrincipalPortion()).orElse(BigDecimal.ZERO)) - .reduce(BigDecimal.ZERO, BigDecimal::add); - feePaid = transaction.getLoanTransactionToRepaymentScheduleMappings().stream() - .map(mapping -> Optional.ofNullable(mapping.getFeeChargesPortion()).orElse(BigDecimal.ZERO)) - .reduce(BigDecimal.ZERO, BigDecimal::add); - penaltyPaid = transaction.getLoanTransactionToRepaymentScheduleMappings().stream() - .map(mapping -> Optional.ofNullable(mapping.getPenaltyChargesPortion()).orElse(BigDecimal.ZERO)) - .reduce(BigDecimal.ZERO, BigDecimal::add); - } - transactionDTO.setPrincipalPaid(principalPaid); - transactionDTO.setFeePaid(feePaid); - transactionDTO.setPenaltyPaid(penaltyPaid); - } - - LoanTransactionRelation loanTransactionRelation = transaction.getLoanTransactionRelations().stream() - .filter(e -> LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT.equals(e.getRelationType())).findAny().orElse(null); - if (loanTransactionRelation != null) { - LoanCharge loanCharge = loanTransactionRelation.getToCharge(); - transactionDTO.setLoanChargeData(loanCharge.toData()); - } - - transactionDTO.setLoanToLoanTransfer(false); - - return transactionDTO; - } - - private void classifyTransactionsBasedOnChargeOffDate(final List newLoanTransactionsBeforeChargeOff, - final List newLoanTransactionsAfterChargeOff, final List existingTransactionIds, - final List existingReversedTransactionIds, final String currencyCode, final Loan loan) { - - final LocalDate chargeOffDate = loan.getChargedOffOnDate(); - if (chargeOffDate == null) { - return; - } - - // Before charge-off - final List beforeTransactions = findTransactionsForChargeOffClassification(loan, chargeOffDate, "BEFORE", - existingTransactionIds, existingReversedTransactionIds); - processTransactionsForChargeOff(beforeTransactions, newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, - currencyCode, loan); - - // On charge-off date - final List onTransactions = findTransactionsForChargeOffClassification(loan, chargeOffDate, "EQUAL", - existingTransactionIds, existingReversedTransactionIds); - processTransactionsForChargeOff(onTransactions, newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, currencyCode, - loan); - - // After charge-off - final List afterTransactions = findTransactionsForChargeOffClassification(loan, chargeOffDate, "AFTER", - existingTransactionIds, existingReversedTransactionIds); - processTransactionsForChargeOff(afterTransactions, newLoanTransactionsBeforeChargeOff, newLoanTransactionsAfterChargeOff, - currencyCode, loan); - } - - private List findTransactionsForChargeOffClassification(final Loan loan, final LocalDate chargeOffDate, - final String dateComparison, final List existingTransactionIds, final List existingReversedTransactionIds) { - - final boolean hasExistingIds = existingTransactionIds != null && !existingTransactionIds.isEmpty(); - final boolean hasExistingReversedIds = existingReversedTransactionIds != null && !existingReversedTransactionIds.isEmpty(); - - if (hasExistingIds && hasExistingReversedIds) { - return loanTransactionRepository.findTransactionsForChargeOffClassification(loan, chargeOffDate, dateComparison, - existingTransactionIds, existingReversedTransactionIds); - } else if (hasExistingIds) { - return loanTransactionRepository.findTransactionsForChargeOffClassification(loan, chargeOffDate, dateComparison, - existingTransactionIds); - } else { - return loanTransactionRepository.findTransactionsForChargeOffClassification(loan, chargeOffDate, dateComparison); - } - } - - private void processTransactionsForChargeOff(final List transactions, - final List newLoanTransactionsBeforeChargeOff, - final List newLoanTransactionsAfterChargeOff, final String currencyCode, final Loan loan) { - - final Optional chargeOffTransactionOptional = findChargeOffTransaction(loan); - if (chargeOffTransactionOptional.isEmpty()) { - return; - } - - final LoanTransaction chargeOffTransaction = chargeOffTransactionOptional.get(); - final LoanTransaction originalChargeOffTransaction = getOriginalTransactionIfReverseReplayed(chargeOffTransaction); - - transactions.forEach(transaction -> { - List targetList; - - if (transaction.isReversed()) { - final LoanTransaction originalTransaction = getOriginalTransactionIfReverseReplayed(transaction); - targetList = originalTransaction.happenedBefore(originalChargeOffTransaction) ? newLoanTransactionsBeforeChargeOff - : newLoanTransactionsAfterChargeOff; - } else { - targetList = transaction.happenedBefore(chargeOffTransaction) ? newLoanTransactionsBeforeChargeOff - : newLoanTransactionsAfterChargeOff; - } - - targetList.add(mapToLoanTransactionData(transaction, currencyCode)); - }); - } - - private Optional findChargeOffTransaction(final Loan loan) { - return loanTransactionRepository.findNonReversedByLoanAndType(loan, LoanTransactionType.CHARGE_OFF).stream() // - .findFirst(); - } - - private List findTransactionsForAccountingBridge(final List existingTransactionIds, - final List existingReversedTransactionIds, final Loan loan) { - if (existingTransactionIds == null || existingTransactionIds.isEmpty()) { - return loanTransactionRepository.findNonReversedByLoan(loan); - } else if (existingReversedTransactionIds == null || existingReversedTransactionIds.isEmpty()) { - return loanTransactionRepository.findTransactionsForAccountingBridge(loan, existingTransactionIds); - } else { - return loanTransactionRepository.findTransactionsForAccountingBridge(loan, existingTransactionIds, - existingReversedTransactionIds); - } - } - - private LoanTransaction getOriginalTransactionIfReverseReplayed(final LoanTransaction loanTransaction) { - if (loanTransaction.getLoanTransactionRelations().isEmpty()) { - return loanTransaction; - } - - return loanTransaction.getLoanTransactionRelations().stream() // - .filter(tr -> LoanTransactionRelationTypeEnum.REPLAYED.equals(tr.getRelationType())) // - .map(LoanTransactionRelation::getToTransaction) // - .min(Comparator.comparingLong(LoanTransaction::getId)) // - .orElse(loanTransaction); - } - -} diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java index d5b89f8bc..d92505c6d 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingService.java @@ -34,13 +34,13 @@ public interface LoanAccrualsProcessingService { void addAccruals(@NotNull LocalDate tillDate) throws MultiException; - void reprocessExistingAccruals(@NotNull Loan loan); + void reprocessExistingAccruals(@NotNull Loan loan, boolean addEvent); void processAccrualsOnInterestRecalculation(@NotNull Loan loan, boolean isInterestRecalculationEnabled, boolean addJournal); void addIncomePostingAndAccruals(Long loanId) throws Exception; - void processIncomePostingAndAccruals(@NotNull Loan loan); + void processIncomePostingAndAccruals(@NotNull Loan loan, boolean addEvent); void processAccrualsOnLoanClosure(@NonNull Loan loan, boolean addJournal); diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java index 129744034..faaf18c0d 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeService.java @@ -51,7 +51,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleIns import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleProcessingWrapper; import org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; @@ -64,7 +63,6 @@ public class LoanChargeService { private final LoanTransactionProcessingService loanTransactionProcessingService; private final LoanLifecycleStateMachine loanLifecycleStateMachine; private final LoanBalanceService loanBalanceService; - private final LoanTransactionRepository loanTransactionRepository; public void recalculateAllCharges(final Loan loan) { Set charges = loan.getActiveCharges(); @@ -134,11 +132,9 @@ public class LoanChargeService { } } - public void makeChargePayment(final Loan loan, final Long chargeId, final List existingTransactionIds, - final List existingReversedTransactionIds, final LoanTransaction paymentTransaction, final Integer installmentNumber) { + public void makeChargePayment(final Loan loan, final Long chargeId, final LoanTransaction paymentTransaction, + final Integer installmentNumber) { loanChargeValidator.validateChargePaymentNotInFuture(paymentTransaction); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); LoanCharge charge = null; for (final LoanCharge loanCharge : loan.getCharges()) { if (loanCharge.isActive() && chargeId.equals(loanCharge.getId())) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java index 467b8dbaf..a2bb34a7c 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java @@ -63,6 +63,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler private final LoanLifecycleStateMachine loanLifecycleStateMachine; private final LoanBalanceService loanBalanceService; private final LoanTransactionService loanTransactionService; + private final LoanJournalEntryPoster journalEntryPoster; @Override public LoanTransaction handleDownPayment(ScheduleGeneratorDTO scheduleGeneratorDTO, JsonCommand command, @@ -71,6 +72,7 @@ public class LoanDownPaymentHandlerServiceImpl implements LoanDownPaymentHandler LoanTransaction downPaymentTransaction = handleDownPayment(loan, disbursementTransaction, command, scheduleGeneratorDTO); if (downPaymentTransaction != null) { downPaymentTransaction = loanTransactionRepository.saveAndFlush(downPaymentTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(downPaymentTransaction, false, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanTransactionDownPaymentPostBusinessEvent(downPaymentTransaction)); } return downPaymentTransaction; diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPoster.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPoster.java index bcc4b0fa8..65c41474f 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPoster.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPoster.java @@ -18,10 +18,33 @@ */ package org.apache.fineract.portfolio.loanaccount.service; -import java.util.List; import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; public interface LoanJournalEntryPoster { - void postJournalEntries(Loan loan, List existingTransactionIds, List existingReversedTransactionIds); + /** + * Create journal entries immediately for a single loan transaction This replaces the old 2-step process of + * collecting transaction IDs and then creating journal entries + * + * @param loanTransaction + * the loan transaction to create journal entries for + * @param isAccountTransfer + * whether this is an account transfer transaction + * @param isLoanToLoanTransfer + * whether this is a loan-to-loan transfer transaction + */ + void postJournalEntriesForLoanTransaction(LoanTransaction loanTransaction, boolean isAccountTransfer, boolean isLoanToLoanTransfer); + + /** + * Create journal entries immediately for an external owner transfer + * + * @param loan + * the loan being transferred + * @param externalAssetOwnerTransfer + * the external owner transfer details + * @param previousOwner + * the previous owner (can be null for initial transfers) + */ + void postJournalEntriesForExternalOwnerTransfer(Loan loan, Object externalAssetOwnerTransfer, Object previousOwner); } diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java index 1799373aa..a7c83d476 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanRefundService.java @@ -19,13 +19,11 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.time.LocalDate; -import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent; import org.apache.fineract.portfolio.loanaccount.domain.LoanLifecycleStateMachine; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; -import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; import org.apache.fineract.portfolio.loanaccount.serialization.LoanRefundValidator; @@ -36,13 +34,8 @@ public class LoanRefundService { private final LoanRefundValidator loanRefundValidator; private final LoanTransactionProcessingService loadTransactionProcessingService; private final LoanLifecycleStateMachine loanLifecycleStateMachine; - private final LoanTransactionRepository loanTransactionRepository; - - public void makeRefund(final Loan loan, final LoanTransaction loanTransaction, final List existingTransactionIds, - final List existingReversedTransactionIds) { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); + public void makeRefund(final Loan loan, final LoanTransaction loanTransaction) { loanRefundValidator.validateTransferRefund(loan, loanTransaction); loanTransaction.updateLoan(loan); @@ -59,21 +52,12 @@ public class LoanRefundService { return loanTransactionDate; } - public void makeRefundForActiveLoan(final Loan loan, final LoanTransaction loanTransaction, final List existingTransactionIds, - final List existingReversedTransactionIds) { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - + public void makeRefundForActiveLoan(final Loan loan, final LoanTransaction loanTransaction) { handleRefundTransaction(loan, loanTransaction); } - public void creditBalanceRefund(final Loan loan, final LoanTransaction newCreditBalanceRefundTransaction, - final List existingTransactionIds, final List existingReversedTransactionIds) { + public void creditBalanceRefund(final Loan loan, final LoanTransaction newCreditBalanceRefundTransaction) { loanRefundValidator.validateCreditBalanceRefund(loan, newCreditBalanceRefundTransaction); - - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - loan.getLoanTransactions().add(newCreditBalanceRefundTransaction); loanLifecycleStateMachine.determineAndTransition(loan, newCreditBalanceRefundTransaction.getTransactionDate()); diff --git a/fineract-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapperTest.java b/fineract-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapperTest.java index 4fdaaca1b..290b3e733 100644 --- a/fineract-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapperTest.java +++ b/fineract-loan/src/test/java/org/apache/fineract/portfolio/loanaccount/domain/SingleLoanChargeRepaymentScheduleProcessingWrapperTest.java @@ -69,8 +69,7 @@ public class SingleLoanChargeRepaymentScheduleProcessingWrapperTest { private ArgumentCaptor penaltyChargesWrittenOff = ArgumentCaptor.forClass(Money.class); private final LoanChargeService loanChargeService = new LoanChargeService(mock(LoanChargeValidator.class), - mock(LoanTransactionProcessingService.class), mock(LoanLifecycleStateMachine.class), mock(LoanBalanceService.class), - mock(LoanTransactionRepository.class)); + mock(LoanTransactionProcessingService.class), mock(LoanLifecycleStateMachine.class), mock(LoanBalanceService.class)); @BeforeAll public static void init() { diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeWritePlatformServiceImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeWritePlatformServiceImpl.java index c0a77b414..2a623ae50 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeWritePlatformServiceImpl.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/BuyDownFeeWritePlatformServiceImpl.java @@ -20,9 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.math.BigDecimal; import java.time.LocalDate; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -77,8 +75,6 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); final Map changes = new LinkedHashMap<>(); // Create payment details @@ -109,8 +105,7 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer noteWritePlatformService.createLoanTransactionNote(buyDownFeeTransaction.getId(), noteText); } - // Post journal entries - loanJournalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(buyDownFeeTransaction, false, false); // Notify business events businessEventNotifierService.notifyPostBusinessEvent(new LoanBuyDownFeeTransactionCreatedBusinessEvent(buyDownFeeTransaction)); @@ -131,8 +126,6 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer final Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); final Map changes = new LinkedHashMap<>(); // Create payment details @@ -178,8 +171,8 @@ public class BuyDownFeeWritePlatformServiceImpl implements BuyDownFeePlatformSer if (StringUtils.isNotBlank(noteText)) { noteWritePlatformService.createLoanTransactionNote(savedBuyDownFeeAdjustment.getId(), noteText); } - // Post journal entries - loanJournalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(savedBuyDownFeeAdjustment, false, false); // Notify business events businessEventNotifierService diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java index 9a34fe4b9..fe4112a1c 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/CapitalizedIncomeWritePlatformServiceImpl.java @@ -20,9 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.math.BigDecimal; import java.time.LocalDate; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -74,8 +72,6 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc public CommandProcessingResult addCapitalizedIncome(final Long loanId, final JsonCommand command) { loanTransactionValidator.validateCapitalizedIncome(command, loanId); final Loan loan = loanAssembler.assembleFrom(loanId); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); final Map changes = new LinkedHashMap<>(); // Create payment details final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); @@ -106,8 +102,8 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc noteWritePlatformService.createLoanTransactionNote(capitalizedIncomeTransaction.getId(), noteText); } - // Post journal entries - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + // Create journal entries immediately for this transaction + journalEntryPoster.postJournalEntriesForLoanTransaction(capitalizedIncomeTransaction, false, false); loanLifecycleStateMachine.determineAndTransition(loan, transactionDate); @@ -128,8 +124,6 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc final JsonCommand command) { loanTransactionValidator.validateCapitalizedIncomeAdjustment(command, loanId, capitalizedIncomeTransactionId); final Loan loan = loanAssembler.assembleFrom(loanId); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); final Map changes = new LinkedHashMap<>(); // Create payment details final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); @@ -155,8 +149,8 @@ public class CapitalizedIncomeWritePlatformServiceImpl implements CapitalizedInc if (noteText != null && !noteText.isEmpty()) { noteWritePlatformService.createLoanTransactionNote(savedCapitalizedIncomeAdjustment.getId(), noteText); } - // Post journal entries - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + // Create journal entries immediately for this transaction + journalEntryPoster.postJournalEntriesForLoanTransaction(savedCapitalizedIncomeAdjustment, false, false); LoanCapitalizedIncomeBalance capitalizedIncomeBalance = capitalizedIncomeBalanceRepository.findByLoanIdAndLoanTransactionId(loanId, capitalizedIncomeTransactionId); diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java index 6f3c96fed..c01e543e9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformService.java @@ -24,7 +24,11 @@ import java.util.Map; import org.apache.fineract.accounting.provisioning.domain.ProvisioningEntry; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; +import org.apache.fineract.investor.domain.ExternalAssetOwner; +import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer; import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; public interface JournalEntryWritePlatformService { @@ -50,4 +54,29 @@ public interface JournalEntryWritePlatformService { void revertShareAccountJournalEntries(ArrayList transactionId, LocalDate transactionDate); + /** + * Create journal entries immediately for a single loan transaction + * + * @param loanTransaction + * the loan transaction to create journal entries for + * @param isAccountTransfer + * whether this is an account transfer transaction + * @param isLoanToLoanTransfer + * whether this is a loan-to-loan transfer transaction + */ + void createJournalEntriesForLoanTransaction(LoanTransaction loanTransaction, boolean isAccountTransfer, boolean isLoanToLoanTransfer); + + /** + * Create journal entries immediately for an external owner transfer + * + * @param loan + * the loan being transferred + * @param externalAssetOwnerTransfer + * the external owner transfer details + * @param previousOwner + * the previous owner (can be null for initial transfers) + */ + void createJournalEntriesForExternalOwnerTransfer(Loan loan, ExternalAssetOwnerTransfer externalAssetOwnerTransfer, + ExternalAssetOwner previousOwner); + } diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java index 9aeb95364..95d3ecc5a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/JournalEntryWritePlatformServiceJpaRepositoryImpl.java @@ -78,13 +78,24 @@ import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.investor.domain.ExternalAssetOwner; import org.apache.fineract.investor.domain.ExternalAssetOwnerRepository; +import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer; import org.apache.fineract.investor.exception.ExternalAssetOwnerNotFoundException; import org.apache.fineract.investor.service.AccountingService; +import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.OrganisationCurrencyRepositoryWrapper; import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper; import org.apache.fineract.portfolio.PortfolioProductType; import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; +import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO; +import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByDTO; +import org.apache.fineract.portfolio.loanaccount.domain.Loan; +import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; +import org.apache.fineract.portfolio.loanaccount.domain.LoanChargePaidBy; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum; +import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.service.PaymentDetailWritePlatformService; import org.apache.fineract.useradministration.domain.AppUser; @@ -789,6 +800,160 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa accountingProcessorForClientTransactions.createJournalEntriesForClientTransaction(clientTransactionDTO); } + @Transactional + @Override + public void createJournalEntriesForLoanTransaction(final LoanTransaction loanTransaction, final boolean isAccountTransfer, + final boolean isLoanToLoanTransfer) { + final Loan loan = loanTransaction.getLoan(); + + // Check if accounting is enabled for this loan + if (!loan.isCashBasedAccountingEnabledOnLoanProduct() && !loan.isUpfrontAccrualAccountingEnabledOnLoanProduct() + && !loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) { + return; // No accounting enabled, skip journal entry creation + } + + final AccountingBridgeDataDTO accountingBridgeData = createAccountingBridgeDataForSingleTransaction(loanTransaction, + isAccountTransfer); + + if (isLoanToLoanTransfer) { + accountingBridgeData.getNewLoanTransactions().forEach(tx -> tx.setLoanToLoanTransfer(true)); + } + + this.createJournalEntriesForLoan(accountingBridgeData); + } + + @Transactional + @Override + public void createJournalEntriesForExternalOwnerTransfer(final Loan loan, final ExternalAssetOwnerTransfer externalAssetOwnerTransfer, + final ExternalAssetOwner previousOwner) { + final boolean isBuyback = externalAssetOwnerTransfer.getStatus().name().contains("BUYBACK"); + + if (isBuyback) { + this.accountingService.createJournalEntriesForBuybackAssetTransfer(loan, externalAssetOwnerTransfer); + } else { + this.accountingService.createJournalEntriesForSaleAssetTransfer(loan, externalAssetOwnerTransfer, previousOwner); + } + } + + /** + * Create AccountingBridgeDataDTO for a single loan transaction This converts a single LoanTransaction to the format + * expected by existing journal entry logic + */ + private AccountingBridgeDataDTO createAccountingBridgeDataForSingleTransaction(final LoanTransaction loanTransaction, + final boolean isAccountTransfer) { + final Loan loan = loanTransaction.getLoan(); + final String currencyCode = loan.getCurrencyCode(); + + final AccountingBridgeLoanTransactionDTO transactionDTO = convertToAccountingBridgeTransaction(loanTransaction); + + final List transactions = new ArrayList<>(); + transactions.add(transactionDTO); + + boolean wasChargedOffAtTransactionTime = loan.isChargedOff(); + if (loan.isChargedOff() && loan.getChargedOffOnDate() != null) { + // If transaction date is before charge-off date, treat as non-charged-off + if (loanTransaction.getTransactionDate().isBefore(loan.getChargedOffOnDate())) { + wasChargedOffAtTransactionTime = false; + } + } + + return new AccountingBridgeDataDTO(loan.getId(), loan.productId(), loan.getOfficeId(), currencyCode, + loan.getSummary().getTotalInterestCharged(), loan.isCashBasedAccountingEnabledOnLoanProduct(), + loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), + isAccountTransfer, wasChargedOffAtTransactionTime, loan.isFraud(), loan.fetchChargeOffReasonId(), loan.isClosedWrittenOff(), + transactions, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); + } + + /** + * Convert LoanTransaction to AccountingBridgeLoanTransactionDTO + */ + private AccountingBridgeLoanTransactionDTO convertToAccountingBridgeTransaction(LoanTransaction loanTransaction) { + final MonetaryCurrency currency = loanTransaction.getLoan().getCurrency(); + final AccountingBridgeLoanTransactionDTO transactionDTO = new AccountingBridgeLoanTransactionDTO(); + + transactionDTO.setId(loanTransaction.getId()); + transactionDTO.setOfficeId(loanTransaction.getOffice().getId()); + transactionDTO.setType(LoanEnumerations.transactionType(loanTransaction.getTypeOf())); + transactionDTO.setReversed(loanTransaction.isReversed()); + transactionDTO.setDate(loanTransaction.getTransactionDate()); + transactionDTO.setCurrencyCode(currency.getCode()); + transactionDTO.setAmount(loanTransaction.getAmount()); + transactionDTO.setNetDisbursalAmount(loanTransaction.getLoan().getNetDisbursalAmount()); + + // Handle principalPortion for chargeback + if (transactionDTO.getType().isChargeback() && (loanTransaction.getLoan().getCreditAllocationRules() == null + || loanTransaction.getLoan().getCreditAllocationRules().isEmpty())) { + transactionDTO.setPrincipalPortion(loanTransaction.getAmount()); + } else { + transactionDTO.setPrincipalPortion(loanTransaction.getPrincipalPortion()); + } + + transactionDTO.setInterestPortion(loanTransaction.getInterestPortion()); + transactionDTO.setFeeChargesPortion(loanTransaction.getFeeChargesPortion()); + transactionDTO.setPenaltyChargesPortion(loanTransaction.getPenaltyChargesPortion()); + transactionDTO.setOverPaymentPortion(loanTransaction.getOverPaymentPortion()); + + // Handle ChargeRefund transactions + if (transactionDTO.getType().isChargeRefund()) { + transactionDTO.setChargeRefundChargeType(loanTransaction.getChargeRefundChargeType()); + } + + if (loanTransaction.getPaymentDetail() != null) { + transactionDTO.setPaymentTypeId(loanTransaction.getPaymentDetail().getPaymentType().getId()); + } + + // Populate loanChargesPaid from the transaction + if (!loanTransaction.getLoanChargesPaid().isEmpty()) { + List loanChargesPaidData = new ArrayList<>(); + for (final LoanChargePaidBy chargePaidBy : loanTransaction.getLoanChargesPaid()) { + final LoanChargePaidByDTO loanChargePaidData = new LoanChargePaidByDTO(); + loanChargePaidData.setChargeId(chargePaidBy.getLoanCharge().getCharge().getId()); + loanChargePaidData.setIsPenalty(chargePaidBy.getLoanCharge().isPenaltyCharge()); + loanChargePaidData.setLoanChargeId(chargePaidBy.getLoanCharge().getId()); + loanChargePaidData.setAmount(chargePaidBy.getAmount()); + loanChargePaidData.setInstallmentNumber(chargePaidBy.getInstallmentNumber()); + + loanChargesPaidData.add(loanChargePaidData); + } + transactionDTO.setLoanChargesPaid(loanChargesPaidData); + } + + // Handle chargeback principalPaid/feePaid/penaltyPaid + if (transactionDTO.getType().isChargeback() && loanTransaction.getOverPaymentPortion() != null + && loanTransaction.getOverPaymentPortion().compareTo(BigDecimal.ZERO) > 0) { + BigDecimal principalPaid = loanTransaction.getOverPaymentPortion(); + BigDecimal feePaid = BigDecimal.ZERO; + BigDecimal penaltyPaid = BigDecimal.ZERO; + if (!loanTransaction.getLoanTransactionToRepaymentScheduleMappings().isEmpty()) { + principalPaid = loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() + .map(mapping -> Optional.ofNullable(mapping.getPrincipalPortion()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + feePaid = loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() + .map(mapping -> Optional.ofNullable(mapping.getFeeChargesPortion()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + penaltyPaid = loanTransaction.getLoanTransactionToRepaymentScheduleMappings().stream() + .map(mapping -> Optional.ofNullable(mapping.getPenaltyChargesPortion()).orElse(BigDecimal.ZERO)) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + transactionDTO.setPrincipalPaid(principalPaid); + transactionDTO.setFeePaid(feePaid); + transactionDTO.setPenaltyPaid(penaltyPaid); + } + + // Populate loanChargeData for CHARGE_ADJUSTMENT transactions + LoanTransactionRelation loanTransactionRelation = loanTransaction.getLoanTransactionRelations().stream() + .filter(e -> LoanTransactionRelationTypeEnum.CHARGE_ADJUSTMENT.equals(e.getRelationType())).findAny().orElse(null); + if (loanTransactionRelation != null) { + LoanCharge loanCharge = loanTransactionRelation.getToCharge(); + transactionDTO.setLoanChargeData(loanCharge.toData()); + } + + // Set loanToLoanTransfer + transactionDTO.setLoanToLoanTransfer(false); + + return transactionDTO; + } + private static class OfficeCurrencyKey { final Office office; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index f528c505c..e263a8c62 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -31,7 +31,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; @@ -88,26 +87,24 @@ import org.apache.fineract.portfolio.delinquency.service.DelinquencyWritePlatfor import org.apache.fineract.portfolio.delinquency.validator.LoanDelinquencyActionData; import org.apache.fineract.portfolio.group.domain.Group; import org.apache.fineract.portfolio.group.exception.GroupNotActiveException; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.LoanRefundRequestData; import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.MoneyHolder; import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.TransactionCtx; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanForeclosureValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator; import org.apache.fineract.portfolio.loanaccount.service.InterestRefundService; import org.apache.fineract.portfolio.loanaccount.service.InterestRefundServiceDelegate; -import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService; import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService; import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService; import org.apache.fineract.portfolio.loanaccount.service.LoanChargeService; import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerService; +import org.apache.fineract.portfolio.loanaccount.service.LoanJournalEntryPoster; import org.apache.fineract.portfolio.loanaccount.service.LoanRefundService; import org.apache.fineract.portfolio.loanaccount.service.LoanScheduleService; import org.apache.fineract.portfolio.loanaccount.service.LoanTransactionProcessingService; @@ -135,7 +132,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { private final ConfigurationDomainService configurationDomainService; private final HolidayRepository holidayRepository; private final WorkingDaysRepositoryWrapper workingDaysRepository; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final NoteRepository noteRepository; private final BusinessEventNotifierService businessEventNotifierService; private final LoanUtilService loanUtilService; @@ -145,7 +141,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { private final DelinquencyWritePlatformService delinquencyWritePlatformService; private final LoanLifecycleStateMachine loanLifecycleStateMachine; private final ExternalIdFactory externalIdFactory; - private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService; private final DelinquencyEffectivePauseHelper delinquencyEffectivePauseHelper; private final DelinquencyReadPlatformService delinquencyReadPlatformService; private final LoanAccrualsProcessingService loanAccrualsProcessingService; @@ -160,11 +155,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { private final LoanRefundService loanRefundService; private final LoanAccountService loanAccountService; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; - private final LoanAccountingBridgeMapper loanAccountingBridgeMapper; private final LoanTransactionProcessingService loanTransactionProcessingService; private final LoanBalanceService loanBalanceService; private final LoanTransactionService loanTransactionService; private final LoanAccountDomainServiceJpaHelper loanAccountDomainServiceJpaHelper; + private final LoanJournalEntryPoster journalEntryPoster; @Transactional @Override @@ -235,9 +230,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { * .validateRepaymentDateWithMeetingDate(transactionDate, calendarInstance); } */ - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - final Money repaymentAmount = Money.of(loan.getCurrency(), transactionAmount); LoanTransaction newRepaymentTransaction; if (isRecoveryRepayment) { @@ -273,11 +265,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { loanDownPaymentTransactionValidator.validateRepaymentTypeAccountStatus(loan, newRepaymentTransaction, event); loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, event, newRepaymentTransaction.getTransactionDate()); - makeRepayment(loan, newRepaymentTransaction, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO); + makeRepayment(loan, newRepaymentTransaction, scheduleGeneratorDTO); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newRepaymentTransaction); @@ -289,12 +281,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); setLoanDelinquencyTag(loan, transactionDate); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(newRepaymentTransaction, isAccountTransfer, isLoanToLoanTransfer); if (!repaymentTransactionType.isChargeRefund()) { final LoanTransactionBusinessEvent transactionRepaymentEvent = getTransactionRepaymentTypeBusinessEvent( repaymentTransactionType, isRecoveryRepayment, newRepaymentTransaction); @@ -393,7 +384,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { public LoanTransaction makeChargePayment(final Loan loan, final Long chargeId, final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, final ExternalId txnExternalId, final Integer transactionType, Integer installmentNumber) { - boolean isAccountTransfer = true; checkClientOrGroupActive(loan); if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) { throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " @@ -403,9 +393,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } businessEventNotifierService.notifyPreBusinessEvent(new LoanChargePaymentPreBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - final Money paymentAmout = Money.of(loan.getCurrency(), transactionAmount); final LoanTransactionType loanTransactionType = LoanTransactionType.fromInt(transactionType); @@ -413,7 +400,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { transactionDate, txnExternalId, loanTransactionType); if (loanTransactionType.isRepaymentAtDisbursement()) { - handlePayDisbursementTransaction(loan, chargeId, newPaymentTransaction, existingTransactionIds, existingReversedTransactionIds); + handlePayDisbursementTransaction(loan, chargeId, newPaymentTransaction); } else { final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), transactionDate, @@ -433,8 +420,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { LoanEvent.LOAN_CHARGE_PAYMENT); loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_CHARGE_PAYMENT, newPaymentTransaction.getTransactionDate()); - loanChargeService.makeChargePayment(loan, chargeId, existingTransactionIds, existingReversedTransactionIds, - newPaymentTransaction, installmentNumber); + loanChargeService.makeChargePayment(loan, chargeId, newPaymentTransaction, installmentNumber); } loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newPaymentTransaction); loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -445,19 +431,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(newPaymentTransaction, true, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanChargePaymentPostBusinessEvent(newPaymentTransaction)); return newPaymentTransaction; } - private void handlePayDisbursementTransaction(final Loan loan, final Long chargeId, final LoanTransaction chargesPayment, - final List existingTransactionIds, final List existingReversedTransactionIds) { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); + private void handlePayDisbursementTransaction(final Loan loan, final Long chargeId, final LoanTransaction chargesPayment) { LoanCharge charge = null; for (final LoanCharge loanCharge : loan.getCharges()) { if (loanCharge.isActive() && chargeId.equals(loanCharge.getId())) { @@ -474,38 +456,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { charge.markAsFullyPaid(); } - private void postJournalEntries(final Loan loanAccount, final List existingTransactionIds, - final List existingReversedTransactionIds, boolean isAccountTransfer) { - postJournalEntries(loanAccount, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, false); - } - - private void postJournalEntries(final Loan loanAccount, final List existingTransactionIds, - final List existingReversedTransactionIds, boolean isAccountTransfer, boolean isLoanToLoanTransfer) { - - final MonetaryCurrency currency = loanAccount.getCurrency(); - - if (loanAccount.isChargedOff()) { - final List accountingBridgeDataList = loanAccountingBridgeMapper - .deriveAccountingBridgeDataForChargeOff(currency.getCode(), existingTransactionIds, existingReversedTransactionIds, - isAccountTransfer, loanAccount); - if (isLoanToLoanTransfer) { - accountingBridgeDataList.forEach(dto -> dto.getNewLoanTransactions().forEach(tx -> tx.setLoanToLoanTransfer(true))); - } - - for (AccountingBridgeDataDTO accountingBridgeData : accountingBridgeDataList) { - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - } - } else { - final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(), - existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loanAccount); - if (isLoanToLoanTransfer) { - accountingBridgeData.getNewLoanTransactions().forEach(tx -> tx.setLoanToLoanTransfer(true)); - } - - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - } - } - private void checkClientOrGroupActive(final Loan loan) { final Client client = loan.client(); if (client != null) { @@ -525,7 +475,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { public LoanTransaction makeRefund(final Long accountId, final CommandProcessingResultBuilder builderResult, final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText, final ExternalId txnExternalId) { - boolean isAccountTransfer = true; final Loan loan = this.loanAccountAssembler.assembleFrom(accountId); checkClientOrGroupActive(loan); if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) { @@ -535,8 +484,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { loan.getId()); } businessEventNotifierService.notifyPreBusinessEvent(new LoanRefundPreBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount); final LoanTransaction newRefundTransaction = LoanTransaction.refund(loan.getOffice(), refundAmount, paymentDetail, transactionDate, @@ -552,7 +499,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newRefundTransaction.getTransactionDate(), workingDays, allowTransactionsOnNonWorkingDay); - loanRefundService.makeRefund(loan, newRefundTransaction, existingTransactionIds, existingReversedTransactionIds); + loanRefundService.makeRefund(loan, newRefundTransaction); loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newRefundTransaction); this.loanRepositoryWrapper.saveAndFlush(loan); @@ -562,8 +509,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { this.noteRepository.save(note); } - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(newRefundTransaction, true, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction)); builderResult.withEntityId(newRefundTransaction.getId()).withOfficeId(loan.getOfficeId()).withClientId(loan.getClientId()) @@ -591,9 +537,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan", loan.getId()); } - boolean isAccountTransfer = true; - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); final Money amount = Money.of(loan.getCurrency(), transactionAmount); LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan, amount, paymentDetail, transactionDate, txnExternalId, loan.getTotalOverpaidAsMoney()); @@ -611,7 +554,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { this.noteRepository.save(note); } - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, isLoanToLoanTransfer); + journalEntryPoster.postJournalEntriesForLoanTransaction(disbursementTransaction, true, isLoanToLoanTransfer); return disbursementTransaction; } @@ -676,8 +619,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } businessEventNotifierService.notifyPreBusinessEvent(new LoanCreditBalanceRefundPreBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount); LoanTransaction newCreditBalanceRefundTransaction = LoanTransaction.creditBalanceRefund(loan, loan.getOffice(), refundAmount, @@ -686,8 +627,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CREDIT_BALANCE_REFUND); loanTransactionValidator.validateRefundDateIsAfterLastRepayment(loan, newCreditBalanceRefundTransaction.getTransactionDate()); - loanRefundService.creditBalanceRefund(loan, newCreditBalanceRefundTransaction, existingTransactionIds, - existingReversedTransactionIds); + loanRefundService.creditBalanceRefund(loan, newCreditBalanceRefundTransaction); newCreditBalanceRefundTransaction = this.loanTransactionRepository.saveAndFlush(newCreditBalanceRefundTransaction); @@ -697,10 +637,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(newCreditBalanceRefundTransaction, false, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService .notifyPostBusinessEvent(new LoanCreditBalanceRefundPostBusinessEvent(newCreditBalanceRefundTransaction)); @@ -714,8 +653,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { final Loan loan = this.loanAccountAssembler.assembleFrom(accountId); checkClientOrGroupActive(loan); businessEventNotifierService.notifyPreBusinessEvent(new LoanRefundPreBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount); if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) { @@ -740,7 +677,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { allowTransactionsOnNonWorkingDay); loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REFUND, newRefundTransaction.getTransactionDate()); - loanRefundService.makeRefundForActiveLoan(loan, newRefundTransaction, existingTransactionIds, existingReversedTransactionIds); + loanRefundService.makeRefundForActiveLoan(loan, newRefundTransaction); this.loanTransactionRepository.saveAndFlush(newRefundTransaction); @@ -750,10 +687,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(newRefundTransaction, false, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanRefundPostBusinessEvent(newRefundTransaction)); @@ -777,10 +713,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { MonetaryCurrency currency = loan.getCurrency(); List newTransactions = new ArrayList<>(); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); final ScheduleGeneratorDTO scheduleGeneratorDTO = null; final LoanRepaymentScheduleInstallment foreCloseDetail = loanBalanceService.fetchLoanForeclosureDetail(loan, foreClosureDate); @@ -809,14 +741,15 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_FORECLOSURE); handleForeClosureTransactions(loan, payment, scheduleGeneratorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } for (LoanTransaction newTransaction : newTransactions) { LoanTransaction savedNewTransaction = loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newTransaction); loan.addLoanTransaction(savedNewTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(newTransaction, false, false); transactionIds.add(savedNewTransaction.getId()); } changes.put("transactions", transactionIds); @@ -830,8 +763,6 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { this.noteRepository.save(note); } - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanForeClosurePostBusinessEvent(payment)); return payment; @@ -1002,18 +933,13 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { final LocalDate transactionDate = DateUtils.getBusinessLocalDate(); final BigDecimal refundAmount = loanRefundRequest.getTotalAmount(); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - final LoanTransaction interestRefundTransaction = LoanTransaction.interestRefund(loan, loan.getOffice(), refundAmount, loanRefundRequest.getPrincipal(), loanRefundRequest.getInterest(), loanRefundRequest.getFeeCharges(), loanRefundRequest.getPenaltyCharges(), paymentDetail, transactionDate, externalIdFactory.create()); interestRefundTransaction.updateLoan(loan); loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(interestRefundTransaction); loan.addLoanTransaction(interestRefundTransaction); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, false); + journalEntryPoster.postJournalEntriesForLoanTransaction(interestRefundTransaction, false, false); return interestRefundTransaction; } @@ -1089,12 +1015,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } @SuppressWarnings("null") - private void makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction, final List existingTransactionIds, - final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) { + private void makeRepayment(final Loan loan, final LoanTransaction repaymentTransaction, + final ScheduleGeneratorDTO scheduleGeneratorDTO) { loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(loan, repaymentTransaction, "created"); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, repaymentTransaction, null, scheduleGeneratorDTO); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index 51cc48183..a865fe843 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -974,7 +974,7 @@ public class LoanScheduleAssembler { final LocalDate recalculateFrom = null; ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, false); } @@ -1553,7 +1553,7 @@ public class LoanScheduleAssembler { if (actualChanges.containsKey(LoanApiConstants.approvedLoanAmountParameterName) || actualChanges.containsKey("recalculateLoanSchedule") || actualChanges.containsKey("expectedDisbursementDate")) { loanScheduleService.regenerateRepaymentSchedule(loan, loanUtilService.buildScheduleGeneratorDTO(loan, null)); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, false); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java index 2ba6e2349..5cb2e6455 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleWritePlatformServiceImpl.java @@ -101,7 +101,7 @@ public class LoanScheduleWritePlatformServiceImpl implements LoanScheduleWritePl final LocalDate recalculateFrom = null; ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, false); loanAccountService.saveLoanWithDataIntegrityViolationChecks(loan); businessEventNotifierService.notifyPostBusinessEvent(new LoanScheduleVariationsDeletedBusinessEvent(loan)); return new CommandProcessingResultBuilder() // diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java index 0ec88814a..173e3f486 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java @@ -32,7 +32,6 @@ import java.util.Optional; import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.codes.domain.CodeValue; import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper; import org.apache.fineract.infrastructure.core.api.JsonCommand; @@ -47,9 +46,7 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalance import org.apache.fineract.infrastructure.event.business.domain.loan.LoanRescheduledDueAdjustScheduleBusinessEvent; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MoneyHelper; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -72,14 +69,12 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanRepayme import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory; import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanTermVariationsMapper; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.RescheduleLoansApiConstants; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.data.LoanRescheduleRequestDataValidator; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException; -import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService; import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService; import org.apache.fineract.portfolio.loanaccount.service.LoanAssembler; import org.apache.fineract.portfolio.loanaccount.service.LoanChargeService; @@ -108,20 +103,17 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche private final LoanRescheduleRequestRepository loanRescheduleRequestRepository; private final LoanRepaymentScheduleHistoryRepository loanRepaymentScheduleHistoryRepository; private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final LoanRepositoryWrapper loanRepositoryWrapper; private final LoanAssembler loanAssembler; private final LoanUtilService loanUtilService; private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory; private final LoanScheduleGeneratorFactory loanScheduleFactory; private final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository; - private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService; private final BusinessEventNotifierService businessEventNotifierService; private final LoanAccrualsProcessingService loanAccrualsProcessingService; private final LoanChargeService loanChargeService; private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; private final LoanTermVariationsMapper loanTermVariationsMapper; - private final LoanAccountingBridgeMapper loanAccountingBridgeMapper; private final LoanScheduleComponent loanSchedule; private final LoanTransactionRepository loanTransactionRepository; private final LoanLifecycleStateMachine loanLifecycleStateMachine; @@ -355,9 +347,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche changes.put("approvedByUserId", appUser.getId()); Loan loan = loanRescheduleRequest.getLoan(); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>( - loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, loanRescheduleRequest.getRescheduleFromDate()); @@ -442,7 +431,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche } else { loanSchedule.updateLoanSchedule(loan, loanScheduleDTO.getLoanScheduleModel()); } - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); loanChargeService.recalculateAllCharges(loan); reprocessLoanTransactionsService.reprocessTransactions(loan); @@ -460,11 +449,9 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche } loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false); + loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, true); businessEventNotifierService.notifyPostBusinessEvent(new LoanRescheduledDueAdjustScheduleBusinessEvent(loan)); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); if (hasInterestRateChange) { businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); } @@ -507,14 +494,6 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche } } - private void postJournalEntries(Loan loan, List existingTransactionIds, List existingReversedTransactionIds) { - final MonetaryCurrency currency = loan.getCurrency(); - boolean isAccountTransfer = false; - final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(), - existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan); - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - } - @Override @Transactional public CommandProcessingResult reject(JsonCommand jsonCommand) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java index 3e28eb854..f4a3026ed 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualActivityProcessingServiceImpl.java @@ -63,6 +63,7 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi private final LoanAccountService loanAccountService; private final LoanBalanceService loanBalanceService; private final LoanTransactionRepository loanTransactionRepository; + private final LoanJournalEntryPoster journalEntryPoster; @Override @Transactional(propagation = Propagation.REQUIRES_NEW) @@ -245,6 +246,7 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newLoanTransaction); loan.addLoanTransaction(newLoanTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(newLoanTransaction, false, false); LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction); data.setNewTransactionDetail(newLoanTransaction); @@ -277,6 +279,7 @@ public class LoanAccrualActivityProcessingServiceImpl implements LoanAccrualActi if (newAccrualActivityTransaction != null) { LoanTransaction savedNewTransaction = makeAccrualActivityTransaction(loan, newAccrualActivityTransaction); loan.addLoanTransaction(savedNewTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(savedNewTransaction, false, false); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java index 80f446f8f..e17f5ee76 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualEventService.java @@ -50,7 +50,7 @@ public class LoanAccrualEventService { LoanStatus status = loan.getStatus(); if (status.isClosedObligationsMet() || status.isOverpaid()) { log.debug("Loan closure on accrual for loan {}", loan.getId()); - loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan, false); + loanAccrualsProcessingService.processAccrualsOnLoanClosure(loan, true); loanAccrualActivityProcessingService.processAccrualActivityForLoanClosure(loan); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java index a1c09c258..f6fe4cb99 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualsProcessingServiceImpl.java @@ -45,7 +45,6 @@ import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.fineract.accounting.common.AccountingRuleType; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.config.TaskExecutorConstant; import org.apache.fineract.infrastructure.core.domain.ExternalId; @@ -55,7 +54,6 @@ import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; -import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualAdjustmentTransactionBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent; @@ -63,8 +61,6 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeLoanTransactionDTO; import org.apache.fineract.portfolio.loanaccount.data.AccrualBalances; import org.apache.fineract.portfolio.loanaccount.data.AccrualChargeData; import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodData; @@ -88,7 +84,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; import org.apache.fineract.portfolio.loanproduct.domain.InterestRecalculationCompoundingMethod; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.springframework.beans.factory.annotation.Qualifier; @@ -110,18 +105,16 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing private final BusinessEventNotifierService businessEventNotifierService; private final ConfigurationDomainService configurationDomainService; private final LoanRepositoryWrapper loanRepositoryWrapper; - private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final LoanTransactionRepository loanTransactionRepository; private final LoanScheduleGeneratorFactory loanScheduleFactory; @Qualifier(TaskExecutorConstant.CONFIGURABLE_TASK_EXECUTOR_BEAN_NAME) private final ThreadPoolTaskExecutor taskExecutor; private final TransactionTemplate transactionTemplate; - private final LoanAccountingBridgeMapper loanAccountingBridgeMapper; private final LoanChargeService loanChargeService; private final LoanBalanceService loanBalanceService; private final LoanChargePaidByRepository loanChargePaidByRepository; + private final LoanJournalEntryPoster journalEntryPoster; /** * method adds accrual for batch job "Add Periodic Accrual Transactions" and add accruals api for Loan @@ -210,13 +203,13 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing * reschedule */ @Override - public void reprocessExistingAccruals(@NotNull Loan loan) { + public void reprocessExistingAccruals(@NotNull final Loan loan, final boolean addEvent) { List accrualTransactions = retrieveListOfAccrualTransactions(loan); if (!accrualTransactions.isEmpty()) { if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) { - reprocessPeriodicAccruals(loan, accrualTransactions); + reprocessPeriodicAccruals(loan, accrualTransactions, addEvent); } else if (loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) { - reprocessNonPeriodicAccruals(loan, accrualTransactions); + reprocessNonPeriodicAccruals(loan, accrualTransactions, addEvent); } } } @@ -253,40 +246,37 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing if (isProgressiveAccrual(loan)) { return; } - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - processIncomePostingAndAccruals(loan); + processIncomePostingAndAccruals(loan, true); this.loanRepositoryWrapper.saveAndFlush(loan); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); } /** * method calculates accruals for loan with interest recalculation and compounding to be posted as income */ @Override - public void processIncomePostingAndAccruals(@NotNull Loan loan) { + public void processIncomePostingAndAccruals(@NotNull final Loan loan, final boolean addEvent) { if (isProgressiveAccrual(loan)) { return; } - LoanInterestRecalculationDetails recalculationDetails = loan.getLoanInterestRecalculationDetails(); + final LoanInterestRecalculationDetails recalculationDetails = loan.getLoanInterestRecalculationDetails(); if (recalculationDetails == null || !recalculationDetails.isCompoundingToBePostedAsTransaction()) { return; } LocalDate lastCompoundingDate = loan.getDisbursementDate(); - List compoundingDetails = extractInterestRecalculationAdditionalDetails(loan); + final List compoundingDetails = extractInterestRecalculationAdditionalDetails(loan); for (LoanInterestRecalcualtionAdditionalDetails compoundingDetail : compoundingDetails) { if (!DateUtils.isBeforeBusinessDate(compoundingDetail.getEffectiveDate())) { break; } - addUpdateIncomeAndAccrualTransaction(loan, compoundingDetail, lastCompoundingDate); + addUpdateIncomeAndAccrualTransaction(loan, compoundingDetail, lastCompoundingDate, addEvent); lastCompoundingDate = compoundingDetail.getEffectiveDate(); } - List installments = loan.getRepaymentScheduleInstallments(); - LoanRepaymentScheduleInstallment lastInstallment = LoanRepaymentScheduleInstallment.getLastNonDownPaymentInstallment(installments); + final List installments = loan.getRepaymentScheduleInstallments(); + final LoanRepaymentScheduleInstallment lastInstallment = LoanRepaymentScheduleInstallment + .getLastNonDownPaymentInstallment(installments); - reverseTransactionsAfter(loan, Set.of(ACCRUAL, ACCRUAL_ADJUSTMENT, INCOME_POSTING), lastInstallment.getDueDate(), false); + reverseTransactionsAfter(loan, Set.of(ACCRUAL, ACCRUAL_ADJUSTMENT, INCOME_POSTING), lastInstallment.getDueDate(), addEvent); } /** @@ -431,22 +421,14 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing loanTransactionRepository.flush(); if (addJournal) { - final List newTransactionDTOs = new ArrayList<>(); for (LoanTransaction accrualTransaction : accrualTransactions) { final LoanTransactionBusinessEvent businessEvent = accrualTransaction.isAccrual() ? new LoanAccrualTransactionCreatedBusinessEvent(accrualTransaction) : new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTransaction); businessEventNotifierService.notifyPostBusinessEvent(businessEvent); - final AccountingBridgeLoanTransactionDTO transactionDTO = loanAccountingBridgeMapper - .mapToLoanTransactionData(accrualTransaction, currency.getCode()); - newTransactionDTOs.add(transactionDTO); + // Create journal entries immediately for this transaction + journalEntryPoster.postJournalEntriesForLoanTransaction(accrualTransaction, false, false); } - final AccountingBridgeDataDTO accountingBridgeData = new AccountingBridgeDataDTO(loan.getId(), loan.getLoanProduct().getId(), - loan.getOfficeId(), loan.getCurrencyCode(), loan.getSummary().getTotalInterestCharged(), - loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct(), - loan.isUpfrontAccrualAccountingEnabledOnLoanProduct(), loan.isPeriodicAccrualAccountingEnabledOnLoanProduct(), false, - false, false, null, false, newTransactionDTOs, loan.getLoanProductRelatedDetail().isMerchantBuyDownFee()); - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); } } @@ -749,7 +731,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing // ReprocessAccruals - private void reprocessPeriodicAccruals(Loan loan, final List accrualTransactions) { + private void reprocessPeriodicAccruals(Loan loan, final List accrualTransactions, final boolean addEvent) { if (loan.isChargedOff()) { return; } @@ -809,14 +791,15 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing List installments = loan.getRepaymentScheduleInstallments(); boolean isBasedOnSubmittedOnDate = !isChargeOnDueDate; for (LoanRepaymentScheduleInstallment installment : installments) { - checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment); + checkAndUpdateAccrualsForInstallment(loan, accrualTransactions, installments, isBasedOnSubmittedOnDate, installment, + addEvent); } } // reverse accruals after last installment - reverseTransactionsAfter(loan, ACCRUAL_TYPES, lastDueDate, false); + reverseTransactionsAfter(loan, ACCRUAL_TYPES, lastDueDate, addEvent); } - private void reprocessNonPeriodicAccruals(Loan loan, final List accrualTransactions) { + private void reprocessNonPeriodicAccruals(Loan loan, final List accrualTransactions, final boolean addEvent) { if (isProgressiveAccrual(loan)) { return; } @@ -828,6 +811,12 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing if (accrualTransaction.getInterestPortion(loan.getCurrency()).isGreaterThanZero()) { if (accrualTransaction.getInterestPortion(loan.getCurrency()).isNotEqualTo(interestApplied)) { accrualTransaction.reverse(); + if (addEvent) { + journalEntryPoster.postJournalEntriesForLoanTransaction(accrualTransaction, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualAdjustmentTransactionBusinessEvent( + accrualTransaction); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } if (isExternalIdAutoGenerationEnabled) { externalId = ExternalId.generate(); } @@ -835,6 +824,12 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing interestApplied, loan.getDisbursementDate(), externalId); LoanTransaction savedInterestAccrualTransaction = loanTransactionRepository.save(interestAccrualTransaction); loan.addLoanTransaction(savedInterestAccrualTransaction); + if (addEvent) { + journalEntryPoster.postJournalEntriesForLoanTransaction(savedInterestAccrualTransaction, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualTransactionCreatedBusinessEvent( + savedInterestAccrualTransaction); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } } } else { Set chargePaidBies = accrualTransaction.getLoanChargesPaid(); @@ -843,7 +838,23 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing Money chargeAmount = loanCharge.getAmount(loan.getCurrency()); if (chargeAmount.isNotEqualTo(accrualTransaction.getAmount(loan.getCurrency()))) { accrualTransaction.reverse(); - loanChargeService.handleChargeAppliedTransaction(loan, loanCharge, accrualTransaction.getTransactionDate()); + if (addEvent) { + journalEntryPoster.postJournalEntriesForLoanTransaction(accrualTransaction, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualAdjustmentTransactionBusinessEvent( + accrualTransaction); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } + final LoanTransaction applyLoanChargeTransaction = loanChargeService.handleChargeAppliedTransaction(loan, + loanCharge, accrualTransaction.getTransactionDate()); + if (applyLoanChargeTransaction != null) { + LoanTransaction savedApplyLoanChargeTransaction = loanTransactionRepository.save(applyLoanChargeTransaction); + if (addEvent) { + journalEntryPoster.postJournalEntriesForLoanTransaction(savedApplyLoanChargeTransaction, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualTransactionCreatedBusinessEvent( + savedApplyLoanChargeTransaction); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } + } } } } @@ -852,7 +863,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing private void checkAndUpdateAccrualsForInstallment(Loan loan, List accrualTransactions, List installments, boolean isBasedOnSubmittedOnDate, - LoanRepaymentScheduleInstallment installment) { + LoanRepaymentScheduleInstallment installment, final boolean addEvent) { MonetaryCurrency currency = loan.getCurrency(); Money zero = Money.zero(currency); Money interest = zero; @@ -870,6 +881,12 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing fee = fee.minus(accrualTransaction.getFeeChargesPortion(currency)); penalty = penalty.minus(accrualTransaction.getPenaltyChargesPortion(currency)); accrualTransaction.reverse(); + if (addEvent) { + journalEntryPoster.postJournalEntriesForLoanTransaction(accrualTransaction, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualAdjustmentTransactionBusinessEvent( + accrualTransaction); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } } } } @@ -910,7 +927,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } private void addUpdateIncomeAndAccrualTransaction(Loan loan, LoanInterestRecalcualtionAdditionalDetails compoundingDetail, - LocalDate lastCompoundingDate) { + LocalDate lastCompoundingDate, boolean addEvent) { BigDecimal interest = BigDecimal.ZERO; BigDecimal fee = BigDecimal.ZERO; BigDecimal penalties = BigDecimal.ZERO; @@ -938,7 +955,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } createUpdateIncomePostingTransaction(loan, compoundingDetail, interest, fee, penalties, externalId); - createUpdateAccrualTransaction(loan, compoundingDetail, interest, fee, penalties, feeDetails, externalId); + createUpdateAccrualTransaction(loan, compoundingDetail, interest, fee, penalties, feeDetails, externalId, addEvent); loanBalanceService.updateLoanOutstandingBalances(loan); } @@ -947,21 +964,25 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing final Optional incomeTransaction = loanTransactionRepository.findNonReversedByLoanAndTypesAndDate(loan, Set.of(INCOME_POSTING), compoundingDetail.getEffectiveDate()); if (incomeTransaction.isEmpty()) { - LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(), + final LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(), compoundingDetail.getAmount(), interest, fee, penalties, externalId); - LoanTransaction savedTransaction = loanTransactionRepository.save(transaction); + final LoanTransaction savedTransaction = loanTransactionRepository.save(transaction); loan.addLoanTransaction(savedTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(savedTransaction, false, false); } else if (incomeTransaction.get().getAmount(loan.getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) { incomeTransaction.get().reverse(); - LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(), + journalEntryPoster.postJournalEntriesForLoanTransaction(incomeTransaction.get(), false, false); + final LoanTransaction transaction = LoanTransaction.incomePosting(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(), compoundingDetail.getAmount(), interest, fee, penalties, externalId); - LoanTransaction savedTransaction = loanTransactionRepository.save(transaction); + final LoanTransaction savedTransaction = loanTransactionRepository.save(transaction); loan.addLoanTransaction(savedTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(savedTransaction, false, false); } } private void createUpdateAccrualTransaction(Loan loan, LoanInterestRecalcualtionAdditionalDetails compoundingDetail, - BigDecimal interest, BigDecimal fee, BigDecimal penalties, HashMap feeDetails, ExternalId externalId) { + BigDecimal interest, BigDecimal fee, BigDecimal penalties, HashMap feeDetails, ExternalId externalId, + boolean addEvent) { if (configurationDomainService.isExternalIdAutoGenerationEnabled()) { externalId = ExternalId.generate(); } @@ -971,12 +992,24 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing Set.of(LoanTransactionType.ACCRUAL, LoanTransactionType.ACCRUAL_ADJUSTMENT), compoundingDetail.getEffectiveDate()); if (accrualTransaction.isEmpty() || !MathUtil.isEqualTo(accrualTransaction.get().getAmount(), compoundingDetail.getAmount())) { - accrualTransaction.ifPresent(LoanTransaction::reverse); + accrualTransaction.ifPresent(accrualTrans -> { + accrualTrans.reverse(); + if (addEvent) { + journalEntryPoster.postJournalEntriesForLoanTransaction(accrualTrans, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualAdjustmentTransactionBusinessEvent(accrualTrans); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } + }); LoanTransaction accrual = LoanTransaction.accrueTransaction(loan, loan.getOffice(), compoundingDetail.getEffectiveDate(), compoundingDetail.getAmount(), interest, fee, penalties, externalId); updateLoanChargesPaidBy(loan, accrual, feeDetails, null); LoanTransaction savedAccrual = loanTransactionRepository.save(accrual); loan.addLoanTransaction(savedAccrual); + if (addEvent) { + journalEntryPoster.postJournalEntriesForLoanTransaction(savedAccrual, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualTransactionCreatedBusinessEvent(savedAccrual); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } } } } @@ -1039,6 +1072,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing interestToPost, feeToPost, penaltyToPost, externalId); LoanTransaction savedFinalIncomeTransaction = loanTransactionRepository.save(finalIncomeTransaction); loan.addLoanTransaction(savedFinalIncomeTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(savedFinalIncomeTransaction, false, false); if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) { final LocalDate lastAccruedDate = loanTransactionRepository @@ -1055,6 +1089,7 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing updateLoanChargesPaidBy(loan, finalAccrual, feeDetails, null); LoanTransaction savedFinalAccrual = loanTransactionRepository.save(finalAccrual); loan.addLoanTransaction(savedFinalAccrual); + journalEntryPoster.postJournalEntriesForLoanTransaction(savedFinalAccrual, false, false); } } @@ -1120,15 +1155,6 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing } } - private void postJournalEntries(final Loan loan, final List existingTransactionIds, - final List existingReversedTransactionIds) { - final MonetaryCurrency currency = loan.getCurrency(); - boolean isAccountTransfer = false; - final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(), - existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan); - journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - } - private void ensureAccrualTransactionMappings(final Loan loan, final boolean chargeOnDueDate) { final List entriesToProcess = loanChargePaidByRepository.findChargePaidByMappingsWithoutInstallmentNumber(loan); @@ -1234,14 +1260,15 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing private void reverseTransactionsOnOrAfter(final Loan loan, final Set types, final LocalDate date) { loanTransactionRepository.findNonReversedByLoanAndTypesAndOnOrAfterDate(loan, types, date) - .forEach(transaction -> reverseAccrual(transaction, false)); + .forEach(transaction -> reverseAccrual(transaction, true)); } private void reverseAccrual(final LoanTransaction transaction, final boolean addEvent) { transaction.reverse(); if (addEvent) { - final LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction); - businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data)); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); + final LoanTransactionBusinessEvent businessEvent = new LoanAccrualAdjustmentTransactionBusinessEvent(transaction); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 937cb0a27..a27ea347f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -625,7 +625,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa LocalDate recalculateFrom = null; ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, false); } loan.adjustNetDisbursalAmount(loan.getProposedPrincipal()); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java index af94f04d3..0f251096b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAssemblerImpl.java @@ -285,7 +285,7 @@ public class LoanAssemblerImpl implements LoanAssembler { // TODO: review loanChargeService.recalculateAllCharges(loanApplication); topUpLoanConfiguration(element, loanApplication); - loanAccrualsProcessingService.reprocessExistingAccruals(loanApplication); + loanAccrualsProcessingService.reprocessExistingAccruals(loanApplication, false); return loanApplication; } @@ -851,7 +851,7 @@ public class LoanAssemblerImpl implements LoanAssembler { final LoanScheduleModel loanScheduleModel = this.calculationPlatformService.calculateLoanSchedule(query, false); loanSchedule.updateLoanSchedule(loan, loanScheduleModel); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, false); loanChargeService.recalculateAllCharges(loan); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java index 4e3f6d0a7..5a2d3f304 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanBuyDownFeeAmortizationProcessingServiceImpl.java @@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.math.BigDecimal; import java.time.LocalDate; -import java.util.Collections; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -59,9 +58,6 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD @Override @Transactional public void processBuyDownFeeAmortizationTillDate(@NonNull Loan loan, @NonNull LocalDate tillDate, boolean addJournal) { - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - List balances = loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId()); LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() @@ -99,7 +95,7 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD loanTransactionRepository.flush(); if (addJournal) { - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); } BusinessEvent event = MathUtil.isGreaterThanZero(totalAmortizationAmount) @@ -112,13 +108,6 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD @Override @Transactional public void processBuyDownFeeAmortizationOnLoanClosure(@NonNull final Loan loan, final boolean addJournal) { - List existingTransactionIds = Collections.emptyList(); - List existingReversedTransactionIds = Collections.emptyList(); - if (addJournal) { - existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - } - final LocalDate transactionDate = getFinalBuyDownFeeAmortizationTransactionDate(loan); final Optional amortizationTransaction = createBuyDownFeeAmortizationTransaction(loan, transactionDate, false, null); @@ -130,20 +119,16 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD businessEventNotifierService .notifyPostBusinessEvent(new LoanBuyDownFeeAmortizationAdjustmentTransactionCreatedBusinessEvent(loanTransaction)); } + if (addJournal) { + journalEntryPoster.postJournalEntriesForLoanTransaction(amortizationTransaction.get(), false, false); + } }); - - if (addJournal) { - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - } } @Override @Transactional public void processBuyDownFeeAmortizationOnLoanChargeOff(@NonNull final Loan loan, @NonNull final LoanTransaction chargeOffTransaction) { - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - LocalDate transactionDate = loan.getChargedOffOnDate(); if (transactionDate == null) { transactionDate = DateUtils.getBusinessLocalDate(); @@ -152,7 +137,7 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD final Optional amortizationTransaction = createBuyDownFeeAmortizationTransaction(loan, transactionDate, true, chargeOffTransaction); if (amortizationTransaction.isPresent()) { - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(amortizationTransaction.get(), false, false); if (amortizationTransaction.get().isBuyDownFeeAmortization()) { businessEventNotifierService.notifyPostBusinessEvent( new LoanBuyDownFeeAmortizationTransactionCreatedBusinessEvent(amortizationTransaction.get())); @@ -167,8 +152,6 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD @Transactional public void processBuyDownFeeAmortizationOnLoanUndoChargeOff(@NonNull final LoanTransaction loanTransaction) { final Loan loan = loanTransaction.getLoan(); - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); loan.getLoanTransactions().stream().filter(LoanTransaction::isBuyDownFeeAmortization) .filter(transaction -> transaction.getTransactionDate().equals(loanTransaction.getTransactionDate()) @@ -177,17 +160,15 @@ public class LoanBuyDownFeeAmortizationProcessingServiceImpl implements LoanBuyD && rel.getToTransaction().equals(loanTransaction))) .forEach(transaction -> { transaction.reverse(); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); final LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction); businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data)); - }); for (LoanBuyDownFeeBalance balance : loanBuyDownFeeBalanceRepository.findAllByLoanId(loan.getId())) { balance.setUnrecognizedAmount(balance.getChargedOffAmount()); balance.setChargedOffAmount(BigDecimal.ZERO); } - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } private Optional createBuyDownFeeAmortizationTransaction(final Loan loan, final LocalDate transactionDate, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanCapitalizedIncomeAmortizationProcessingServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanCapitalizedIncomeAmortizationProcessingServiceImpl.java index e8bf678e1..1fd36d238 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanCapitalizedIncomeAmortizationProcessingServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanCapitalizedIncomeAmortizationProcessingServiceImpl.java @@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.math.BigDecimal; import java.time.LocalDate; -import java.util.Collections; import java.util.List; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -62,13 +61,6 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L @Override @Transactional public void processCapitalizedIncomeAmortizationOnLoanClosure(@NotNull final Loan loan, final boolean addJournal) { - List existingTransactionIds = Collections.emptyList(); - List existingReversedTransactionIds = Collections.emptyList(); - if (addJournal) { - existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - } - final LocalDate transactionDate = getFinalCapitalizedIncomeAmortizationTransactionDate(loan); final Optional amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate, false, null); @@ -80,20 +72,16 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L businessEventNotifierService.notifyPostBusinessEvent( new LoanCapitalizedIncomeAmortizationAdjustmentTransactionCreatedBusinessEvent(loanTransaction)); } + if (addJournal) { + journalEntryPoster.postJournalEntriesForLoanTransaction(amortizationTransaction.get(), false, false); + } }); - - if (addJournal) { - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - } } @Override @Transactional public void processCapitalizedIncomeAmortizationOnLoanChargeOff(@NotNull final Loan loan, @NonNull final LoanTransaction chargeOffTransaction) { - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - LocalDate transactionDate = loan.getChargedOffOnDate(); if (transactionDate == null) { transactionDate = DateUtils.getBusinessLocalDate(); @@ -102,7 +90,7 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L final Optional amortizationTransaction = createCapitalizedIncomeAmortizationTransaction(loan, transactionDate, true, chargeOffTransaction); if (amortizationTransaction.isPresent()) { - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(amortizationTransaction.get(), false, false); if (amortizationTransaction.get().isCapitalizedIncomeAmortization()) { businessEventNotifierService.notifyPostBusinessEvent( new LoanCapitalizedIncomeAmortizationTransactionCreatedBusinessEvent(amortizationTransaction.get())); @@ -162,8 +150,6 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L @Transactional public void processCapitalizedIncomeAmortizationOnLoanUndoChargeOff(@NotNull final LoanTransaction loanTransaction) { final Loan loan = loanTransaction.getLoan(); - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); loan.getLoanTransactions().stream().filter(LoanTransaction::isCapitalizedIncomeAmortization) .filter(transaction -> transaction.getTransactionDate().equals(loanTransaction.getTransactionDate()) @@ -174,24 +160,19 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L transaction.reverse(); final LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction); businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data)); - + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); }); for (LoanCapitalizedIncomeBalance balance : loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId())) { balance.setUnrecognizedAmount(balance.getChargedOffAmount()); balance.setChargedOffAmount(BigDecimal.ZERO); } - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); } @Override @Transactional public void processCapitalizedIncomeAmortizationTillDate(@NonNull final Loan loan, @NonNull final LocalDate tillDate, final boolean addJournal) { - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - List balances = loanCapitalizedIncomeBalanceRepository.findAllByLoanId(loan.getId()); LocalDate maturityDate = loan.getMaturityDate() != null ? loan.getMaturityDate() @@ -229,7 +210,7 @@ public class LoanCapitalizedIncomeAmortizationProcessingServiceImpl implements L loanTransactionRepository.flush(); if (addJournal) { - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); } BusinessEvent event = MathUtil.isGreaterThanZero(totalAmortizationAmount) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java index 9595ba1d9..b255354ee 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImpl.java @@ -36,7 +36,6 @@ import java.util.Set; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.ApiParameterError; @@ -90,7 +89,6 @@ import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType; import org.apache.fineract.portfolio.group.domain.Group; import org.apache.fineract.portfolio.group.exception.GroupNotActiveException; import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; import org.apache.fineract.portfolio.loanaccount.data.LoanChargePaidByData; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.domain.Loan; @@ -129,7 +127,6 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanSc import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.DefaultScheduledDateGenerator; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.ScheduledDateGenerator; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanDownPaymentTransactionValidator; @@ -156,7 +153,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo private final LoanTransactionRepository loanTransactionRepository; private final AccountTransfersWritePlatformService accountTransfersWritePlatformService; private final LoanRepositoryWrapper loanRepositoryWrapper; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final LoanAccountDomainService loanAccountDomainService; private final LoanChargeRepository loanChargeRepository; private final LoanWritePlatformService loanWritePlatformService; @@ -172,7 +168,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo private final LoanChargeAssembler loanChargeAssembler; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; private final NoteRepository noteRepository; - private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService; private final LoanAccrualsProcessingService loanAccrualsProcessingService; private final LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator; private final LoanChargeValidator loanChargeValidator; @@ -180,8 +175,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; private final LoanAccountService loanAccountService; private final LoanAdjustmentService loanAdjustmentService; - private final LoanAccountingBridgeMapper loanAccountingBridgeMapper; private final LoanChargeService loanChargeService; + private final LoanJournalEntryPoster loanJournalEntryPoster; private static boolean isPartOfThisInstallment(LoanCharge loanCharge, LoanRepaymentScheduleInstallment e) { return DateUtils.isAfter(loanCharge.getDueDate(), e.getFromDate()) && !DateUtils.isAfter(loanCharge.getDueDate(), e.getDueDate()); @@ -207,8 +202,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo // apply this charge validateAddingNewChargeAllowed(loanDisburseDetails); } - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); boolean isAppliedOnBackDate = false; LoanCharge loanCharge = null; @@ -301,14 +294,11 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo if (loan.isInterestBearingAndInterestRecalculationEnabled() && isAppliedOnBackDate && loan.isFeeCompoundingEnabledForInterestRecalculation()) { - loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false); + loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, true); } this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); businessEventNotifierService.notifyPostBusinessEvent(new LoanAddChargeBusinessEvent(loanCharge)); - - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); return new CommandProcessingResultBuilder().withCommandId(command.commandId()) // @@ -546,8 +536,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo final Map changes = new LinkedHashMap<>(); changes.put(LoanApiConstants.externalIdParameterName, externalId); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); LocalDate recalculateFrom = null; ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); @@ -561,21 +549,21 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo } loanChargeValidator.validateLoanIsNotClosed(loan, loanCharge); - final LoanTransaction waiveTransaction = waiveLoanCharge(loan, loanCharge, changes, existingTransactionIds, - existingReversedTransactionIds, loanInstallmentNumber, scheduleGeneratorDTO, accruedCharge, externalId); + final LoanTransaction waiveTransaction = waiveLoanCharge(loan, loanCharge, changes, loanInstallmentNumber, scheduleGeneratorDTO, + accruedCharge, externalId); if (loan.isInterestBearingAndInterestRecalculationEnabled() && DateUtils.isBefore(loanCharge.getDueLocalDate(), DateUtils.getBusinessLocalDate())) { - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } this.loanTransactionRepository.saveAndFlush(waiveTransaction); this.loanRepositoryWrapper.save(loan); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(waiveTransaction, false, false); + this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanWaiveChargeBusinessEvent(loanCharge)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); return new CommandProcessingResultBuilder() // @@ -830,8 +818,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo if (optPenaltyCharge.isEmpty()) { return; } - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); boolean runInterestRecalculation = false; LocalDate recalculateFrom = DateUtils.getBusinessLocalDate(); LocalDate lastChargeDate = null; @@ -881,20 +867,15 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo if (loan.isInterestBearingAndInterestRecalculationEnabled() && runInterestRecalculation && loan.isFeeCompoundingEnabledForInterestRecalculation()) { - loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, false); + loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, true, true); } this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); - - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); } } private LoanTransaction applyChargeAdjustment(final Loan loan, final LoanCharge loanCharge, final BigDecimal transactionAmount, final LocalDate transactionDate, final ExternalId txnExternalId, PaymentDetail paymentDetail) { businessEventNotifierService.notifyPreBusinessEvent(new LoanChargeAdjustmentPreBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); LoanTransaction loanChargeAdjustmentTransaction = LoanTransaction.chargeAdjustment(loan, transactionAmount, transactionDate, txnExternalId, paymentDetail); @@ -921,11 +902,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate()); loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - - final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData( - loan.getCurrency().getCode(), existingTransactionIds, existingReversedTransactionIds, false, loan); - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(loanChargeAdjustmentTransaction, false, false); loanAccountDomainService.setLoanDelinquencyTag(loan, transactionDate); return loanChargeAdjustmentTransaction; @@ -943,8 +920,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo private void undoInstalmentFee(Map changes, Loan loan, LoanTransaction loanTransaction, LoanChargePaidBy loanChargePaidBy) { - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); LoanCharge loanCharge = loanChargePaidBy.getLoanCharge(); final Integer installmentNumber = loanChargePaidBy.getInstallmentNumber(); LoanInstallmentCharge chargePerInstallment; @@ -989,7 +964,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo loanCharge.undoWaived(); } loan.updateLoanSummaryForUndoWaiveCharge(amountWaived, loanCharge.isPenaltyCharge()); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(loanTransaction, false, false); changes.put(AMOUNT, amountWaived); } else { throw new InstallmentNotFoundException(loanTransaction.getId()); @@ -998,9 +973,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo private void undoSpecifiedDueDateCharge(final Map changes, final Loan loan, final LoanTransaction loanTransaction, final LoanChargePaidBy loanChargePaidBy) { - - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); LoanCharge loanCharge = loanChargePaidBy.getLoanCharge(); BigDecimal amountWaived = loanCharge.getAmountWaived(loan.getCurrency()).getAmount(); if (!loanCharge.isWaived() || amountWaived == null) { @@ -1017,7 +989,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo .filter(e -> isPartOfThisInstallment(loanCharge, e)).findFirst().orElseThrow(); updateRepaymentInstalmentWithWaivedAmount(loanCharge, installment, amountWaived); loan.updateLoanSummaryForUndoWaiveCharge(amountWaived, loanCharge.isPenaltyCharge()); - postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(loanTransaction, false, false); changes.put(AMOUNT, amountWaived); } @@ -1104,6 +1076,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo final LoanTransaction applyLoanChargeTransaction = loanChargeService.handleChargeAppliedTransaction(loan, loanCharge, null); if (applyLoanChargeTransaction != null) { this.loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(applyLoanChargeTransaction, false, false); businessEventNotifierService .notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(applyLoanChargeTransaction)); } @@ -1111,6 +1084,7 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo && DateUtils.getBusinessLocalDate().isAfter(loan.getMaturityDate())) { final LoanTransaction loanTransaction = loanChargeService.createChargeAppliedTransaction(loan, loanCharge, null); this.loanTransactionRepository.saveAndFlush(loanTransaction); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(loanTransaction, false, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanAccrualTransactionCreatedBusinessEvent(loanTransaction)); } @@ -1212,13 +1186,11 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo public Loan runScheduleRecalculation(Loan loan, final LocalDate recalculateFrom) { if (loan.isInterestBearingAndInterestRecalculationEnabled() && !loan.isChargedOff()) { - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); loanScheduleService.handleRegenerateRepaymentScheduleWithInterestRecalculation(loan, generatorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); loan = loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); } return loan; } @@ -1289,16 +1261,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo return refundableAmount; } - private void postJournalEntries(final Loan loan, final List existingTransactionIds, - final List existingReversedTransactionIds) { - - final MonetaryCurrency currency = loan.getCurrency(); - boolean isAccountTransfer = false; - final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(), - existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan); - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - } - private LoanCharge retrieveLoanChargeBy(final Long loanId, final Long loanChargeId) { final LoanCharge loanCharge = this.loanChargeRepository.findById(loanChargeId) .orElseThrow(() -> new LoanChargeNotFoundException(loanChargeId)); @@ -1423,8 +1385,8 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo } public LoanTransaction waiveLoanCharge(final Loan loan, final LoanCharge loanCharge, final Map changes, - final List existingTransactionIds, final List existingReversedTransactionIds, final Integer loanInstallmentNumber, - final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge, final ExternalId externalId) { + final Integer loanInstallmentNumber, final ScheduleGeneratorDTO scheduleGeneratorDTO, final Money accruedCharge, + final ExternalId externalId) { final Money amountWaived = loanCharge.waive(loan.getCurrency(), loanInstallmentNumber); changes.put("amount", amountWaived.getAmount()); @@ -1474,9 +1436,6 @@ public class LoanChargeWritePlatformServiceImpl implements LoanChargeWritePlatfo loan.updateSummaryWithTotalFeeChargesDueAtDisbursement(loan.deriveSumTotalOfChargesDueAtDisbursement()); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(loan, loan.getOffice(), amountWaived, transactionDate, feeChargesWaived, penaltyChargesWaived, unrecognizedIncome, externalId); final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge, diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java index c7fe4e457..a23e07185 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDisbursementService.java @@ -52,6 +52,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanDisbursementValidator; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; @@ -64,6 +65,8 @@ public class LoanDisbursementService { private final ReprocessLoanTransactionsService reprocessLoanTransactionsService; private final LoanChargeService loanChargeService; private final LoanBalanceService loanBalanceService; + private final LoanJournalEntryPoster loanJournalEntryPoster; + private final LoanTransactionRepository loanTransactionRepository; public void updateDisbursementDetails(final Loan loan, final JsonCommand jsonCommand, final Map actualChanges) { final List disbursementList = loan.fetchDisbursementIds(); @@ -210,7 +213,12 @@ public class LoanDisbursementService { } } else if (disbursedOn.equals(loan.getActualDisbursementDate()) && loan.isNoneOrCashOrUpfrontAccrualAccountingEnabledOnLoanProduct()) { - loanChargeService.handleChargeAppliedTransaction(loan, charge, disbursedOn); + final LoanTransaction applyLoanChargeTransaction = loanChargeService.handleChargeAppliedTransaction(loan, charge, + disbursedOn); + if (applyLoanChargeTransaction != null) { + loanTransactionRepository.saveAndFlush(applyLoanChargeTransaction); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(applyLoanChargeTransaction, false, false); + } } } @@ -219,6 +227,8 @@ public class LoanDisbursementService { chargesPayment.updateComponentsAndTotal(zero, zero, disbursentMoney, zero); chargesPayment.updateLoan(loan); loan.addLoanTransaction(chargesPayment); + loanTransactionRepository.saveAndFlush(chargesPayment); + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(chargesPayment, false, false); loanBalanceService.updateLoanOutstandingBalances(loan); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPosterImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPosterImpl.java index 72c87dee3..fc2a096e0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPosterImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanJournalEntryPosterImpl.java @@ -18,13 +18,12 @@ */ package org.apache.fineract.portfolio.loanaccount.service; -import java.util.List; import lombok.RequiredArgsConstructor; import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; +import org.apache.fineract.investor.domain.ExternalAssetOwner; +import org.apache.fineract.investor.domain.ExternalAssetOwnerTransfer; import org.apache.fineract.portfolio.loanaccount.domain.Loan; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; +import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.springframework.stereotype.Component; @Component @@ -32,25 +31,20 @@ import org.springframework.stereotype.Component; public class LoanJournalEntryPosterImpl implements LoanJournalEntryPoster { private final JournalEntryWritePlatformService journalEntryWritePlatformService; - private final LoanAccountingBridgeMapper loanAccountingBridgeMapper; @Override - public void postJournalEntries(final Loan loan, final List existingTransactionIds, - final List existingReversedTransactionIds) { - final MonetaryCurrency currency = loan.getCurrency(); - boolean isAccountTransfer = false; - - if (loan.isChargedOff()) { - List accountingBridgeDataList = loanAccountingBridgeMapper.deriveAccountingBridgeDataForChargeOff( - currency.getCode(), existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan); - for (AccountingBridgeDataDTO accountingBridgeData : accountingBridgeDataList) { - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - } - } else { - AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(), - existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan); - this.journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - } + public void postJournalEntriesForLoanTransaction(final LoanTransaction loanTransaction, final boolean isAccountTransfer, + final boolean isLoanToLoanTransfer) { + this.journalEntryWritePlatformService.createJournalEntriesForLoanTransaction(loanTransaction, isAccountTransfer, + isLoanToLoanTransfer); } + @Override + public void postJournalEntriesForExternalOwnerTransfer(final Loan loan, final Object externalAssetOwnerTransfer, + final Object previousOwner) { + // Cast to proper types + final ExternalAssetOwnerTransfer transfer = (ExternalAssetOwnerTransfer) externalAssetOwnerTransfer; + final ExternalAssetOwner prevOwner = (ExternalAssetOwner) previousOwner; + this.journalEntryWritePlatformService.createJournalEntriesForExternalOwnerTransfer(loan, transfer, prevOwner); + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index cd17636d5..6594caad5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -51,7 +51,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.cob.exceptions.AccountLockCannotBeOverruledException; import org.apache.fineract.cob.service.LoanAccountLockService; import org.apache.fineract.infrastructure.codes.domain.CodeValue; @@ -109,7 +108,6 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte import org.apache.fineract.organisation.holiday.domain.Holiday; import org.apache.fineract.organisation.holiday.domain.HolidayRepositoryWrapper; import org.apache.fineract.organisation.holiday.service.HolidayUtil; -import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.staff.domain.Staff; @@ -153,7 +151,6 @@ import org.apache.fineract.portfolio.group.domain.Group; import org.apache.fineract.portfolio.group.exception.GroupNotActiveException; import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; import org.apache.fineract.portfolio.loanaccount.command.LoanUpdateCommand; -import org.apache.fineract.portfolio.loanaccount.data.AccountingBridgeDataDTO; import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository; @@ -200,7 +197,6 @@ import org.apache.fineract.portfolio.loanaccount.guarantor.service.GuarantorDoma import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod; import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; import org.apache.fineract.portfolio.loanaccount.serialization.LoanApplicationValidator; @@ -242,7 +238,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionRelationRepository loanTransactionRelationRepository; private final LoanAssembler loanAssembler; - private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final CalendarInstanceRepository calendarInstanceRepository; private final PaymentDetailWritePlatformService paymentDetailWritePlatformService; private final HolidayRepositoryWrapper holidayRepository; @@ -287,7 +282,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private final LoanAccountService loanAccountService; private final LoanJournalEntryPoster journalEntryPoster; private final LoanAdjustmentService loanAdjustmentService; - private final LoanAccountingBridgeMapper loanAccountingBridgeMapper; private final LoanMapper loanMapper; private final LoanTransactionProcessingService loanTransactionProcessingService; private final LoanBalanceService loanBalanceService; @@ -356,9 +350,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf businessEventNotifierService.notifyPreBusinessEvent(new LoanDisbursalBusinessEvent(loan)); - List existingTransactionIds = new ArrayList<>(); - List existingReversedTransactionIds = new ArrayList<>(); - final AppUser currentUser = getAppUserIfPresent(); final Map changes = new LinkedHashMap<>(); @@ -398,16 +389,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf LoanTransaction disbursementTransaction = null; if (isAccountTransfer) { disburseLoanToSavings(loan, command, amountToDisburse, paymentDetail); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); } else { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); disbursementTransaction = LoanTransaction.disbursement(loan, amountToDisburse, paymentDetail, actualDisbursementDate, txnExternalId, loan.getTotalOverpaidAsMoney()); disbursementTransaction.updateLoan(loan); loan.addLoanTransaction(disbursementTransaction); loanTransactionRepository.saveAndFlush(disbursementTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(disbursementTransaction, false, false); } if (loan.getRepaymentScheduleInstallments().isEmpty()) { /* @@ -426,12 +414,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf scheduleGeneratorDTO); loan.adjustNetDisbursalAmount(amountToDisburse.getAmount()); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate(); if (loan.isInterestBearingAndInterestRecalculationEnabled() && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || loan.isDisbursementMissed())) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } if (loan.isAutoRepaymentForDownPaymentEnabled() && !isWithoutAutoPayment) { @@ -462,9 +450,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } else { loanDownPaymentHandlerService.handleDownPayment(scheduleGeneratorDTO, command, disbursementTransaction, loan); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } } } @@ -501,7 +489,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } updateRecurringCalendarDatesForInterestRecalculation(loan); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); // Post Dated Checks @@ -525,8 +513,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf businessEventNotifierService.notifyPostBusinessEvent(new LoanDisbursalTransactionBusinessEvent(disbursalTransaction)); } - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); return new CommandProcessingResultBuilder() // @@ -594,6 +580,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LoanTransaction interestAppliedTransaction = LoanTransaction.accrueInterest(loan.getOffice(), loan, interestApplied, actualDisbursementDate1, externalId); loan.addLoanTransaction(interestAppliedTransaction); + loanTransactionRepository.saveAndFlush(interestAppliedTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(interestAppliedTransaction, false, false); } if (loan.getLoanProduct().isMultiDisburseLoan() || loan.isProgressiveSchedule()) { @@ -753,9 +741,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf checkClientOrGroupActive(loan); businessEventNotifierService.notifyPreBusinessEvent(new LoanDisbursalBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); // Bulk disbursement should happen on meeting date (mostly from @@ -773,16 +758,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final ExternalId txnExternalId = externalIdFactory.createFromCommand(command, LoanApiConstants.externalIdParameterName); if (isAccountTransfer) { disburseLoanToSavings(loan, command, disburseAmount, paymentDetail); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - } else { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); LoanTransaction disbursementTransaction = LoanTransaction.disbursement(loan, disburseAmount, paymentDetail, actualDisbursementDate, txnExternalId, loan.getTotalOverpaidAsMoney()); disbursementTransaction.updateLoan(loan); loan.addLoanTransaction(disbursementTransaction); + loanTransactionRepository.saveAndFlush(disbursementTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(disbursementTransaction, false, false); businessEventNotifierService .notifyPostBusinessEvent(new LoanDisbursalTransactionBusinessEvent(disbursementTransaction)); } @@ -797,19 +779,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf disburseLoan(command, configurationDomainService.isPaymentTypeApplicableForDisbursementCharge(), paymentDetail, loan, currentUser, changes, scheduleGeneratorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); LocalDate firstInstallmentDueDate = loan.fetchRepaymentScheduleInstallment(1).getDueDate(); if (loan.isInterestBearingAndInterestRecalculationEnabled() && (DateUtils.isBeforeBusinessDate(firstInstallmentDueDate) || loan.isDisbursementMissed())) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } } if (!changes.isEmpty()) { createNote(loan, command, changes); loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); } final Set loanCharges = loan.getActiveCharges(); final Map disBuLoanCharges = new HashMap<>(); @@ -879,10 +859,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoDisbursalBusinessEvent(loan)); removeLoanCycle(loan); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - // - final MonetaryCurrency currency = loan.getCurrency(); final LocalDate recalculateFrom = null; loan.setActualDisbursementDate(null); @@ -894,10 +870,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_DISBURSAL_UNDO); loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_DISBURSAL_UNDO, loan.getDisbursementDate()); - final Map changes = undoDisbursal(loan, scheduleGeneratorDTO, existingTransactionIds, - existingReversedTransactionIds); + final Map changes = undoDisbursal(loan, scheduleGeneratorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (!changes.isEmpty()) { if (loan.isTopup() && loan.getClientId() != null) { @@ -912,11 +887,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); this.accountTransfersWritePlatformService.reverseAllTransactions(loanId, PortfolioAccountType.LOAN); createNote(loan, command, changes); - boolean isAccountTransfer = false; - final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(), - existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan); - journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoDisbursalBusinessEvent(loan)); } @@ -1031,8 +1001,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf reprocessLoanTransactionsService.reprocessTransactions(loan); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } } @@ -1046,10 +1016,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf businessEventNotifierService.notifyPreBusinessEvent(new LoanTransactionInterestPaymentWaiverPreBusinessEvent(loan)); - // save already existing transaction ids - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - final String noteText = command.stringValueOfParameterNamed("note"); final Map changes = new LinkedHashMap<>(); @@ -1061,13 +1027,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loanTransactionValidator.validateLoanTransactionInterestPaymentWaiverAfterRecalculation(loan); loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newInterestPaymentWaiverTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(newInterestPaymentWaiverTransaction, false, false); loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); saveNote(noteText, loan, newInterestPaymentWaiverTransaction); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); loanAccountDomainService.setLoanDelinquencyTag(loan, newInterestPaymentWaiverTransaction.getTransactionDate()); // disable all active standing orders linked to this loan if status changes to closed @@ -1079,9 +1046,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // Finished Notification final LoanTransactionBusinessEvent transactionRepaymentEvent = new LoanTransactionInterestPaymentWaiverPostBusinessEvent( newInterestPaymentWaiverTransaction); - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(transactionRepaymentEvent); @@ -1170,7 +1134,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final boolean allowTransactionsOnHoliday = this.configurationDomainService.allowTransactionsOnHolidayEnabled(); for (final SingleRepaymentCommand singleLoanRepaymentCommand : repaymentCommand) { if (singleLoanRepaymentCommand != null) { - Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(singleLoanRepaymentCommand.getLoanId()); + Loan loan = this.loanAssembler.assembleFrom(singleLoanRepaymentCommand.getLoanId()); final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), singleLoanRepaymentCommand.getTransactionDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); @@ -1318,9 +1282,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } checkClientOrGroupActive(loan); - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - businessEventNotifierService.notifyPreBusinessEvent(new LoanChargebackTransactionBusinessEvent(loanTransaction)); final LocalDate transactionDate = DateUtils.getBusinessLocalDate(); @@ -1351,6 +1312,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf handleChargebackTransaction(loan, newTransaction); newTransaction = this.loanTransactionRepository.saveAndFlush(newTransaction); + // Create journal entries immediately for this transaction + journalEntryPoster.postJournalEntriesForLoanTransaction(newTransaction, false, false); loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -1361,9 +1324,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.noteRepository.save(note); } - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); this.loanAccountDomainService.setLoanDelinquencyTag(loan, transactionDate); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanChargebackTransactionBusinessEvent(newTransaction)); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); @@ -1412,9 +1373,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); Money unrecognizedIncome = transactionAmountAsMoney.zero(); Money interestComponent = transactionAmountAsMoney; @@ -1439,14 +1397,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf LoanEvent.LOAN_REPAYMENT_OR_WAIVER); loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REPAYMENT_OR_WAIVER, waiveInterestTransaction.getTransactionDate()); - waiveInterest(loan, waiveInterestTransaction, existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO); + waiveInterest(loan, waiveInterestTransaction, scheduleGeneratorDTO); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } this.loanTransactionRepository.saveAndFlush(waiveInterestTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(waiveInterestTransaction, false, false); loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); final String noteText = command.stringValueOfParameterNamed("note"); @@ -1457,11 +1416,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanWaiveInterestBusinessEvent(waiveInterestTransaction)); return new CommandProcessingResultBuilder() // @@ -1508,10 +1464,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf StatusEnum.WRITE_OFF.getValue(), EntityTables.LOAN.getForeignKeyColumnNameOnDatatable(), loan.productId()); removeLoanCycle(loan); - - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - updateLoanCounters(loan, loan.getDisbursementDate()); LocalDate recalculateFrom = null; @@ -1533,16 +1485,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .withLoanId(loanId) // .with(changes); - final Optional loanTransactionOptional = closeAsWrittenOff(loan, command, changes, existingTransactionIds, - existingReversedTransactionIds, currentUser, scheduleGeneratorDTO); + final Optional loanTransactionOptional = closeAsWrittenOff(loan, command, changes, currentUser, + scheduleGeneratorDTO); if (loanTransactionOptional.isPresent()) { final LoanTransaction loanTransaction = loanTransactionOptional.get(); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } this.loanTransactionRepository.saveAndFlush(loanTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(loanTransaction, false, false); saveLoanWithDataIntegrityViolationChecks(loan); final String noteText = command.stringValueOfParameterNamed("note"); if (StringUtils.isNotBlank(noteText)) { @@ -1551,11 +1504,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.noteRepository.save(note); } loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, - loan.isInterestBearingAndInterestRecalculationEnabled(), false); + loan.isInterestBearingAndInterestRecalculationEnabled(), true); loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanWrittenOffPostBusinessEvent(loanTransaction)); @@ -1587,9 +1537,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf changes.put("locale", command.locale()); changes.put("dateFormat", command.dateFormat()); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - updateLoanCounters(loan, loan.getDisbursementDate()); LocalDate recalculateFrom = null; @@ -1601,15 +1548,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate closureDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE); loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_CLOSED); loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.REPAID_IN_FULL, closureDate); - final Optional loanTransactionOptional = close(loan, command, changes, existingTransactionIds, - existingReversedTransactionIds, scheduleGeneratorDTO); + final Optional loanTransactionOptional = close(loan, command, changes, scheduleGeneratorDTO); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } - loanTransactionOptional.ifPresent(this.loanTransactionRepository::saveAndFlush); + loanTransactionOptional.ifPresent(loanTransaction -> { + this.loanTransactionRepository.saveAndFlush(loanTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(loanTransaction, false, false); + }); saveLoanWithDataIntegrityViolationChecks(loan); final String noteText = command.stringValueOfParameterNamed("note"); @@ -1619,9 +1568,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.noteRepository.save(note); } - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); @@ -1633,9 +1581,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // disable all active standing instructions linked to the loan this.loanAccountDomainService.disableStandingInstructionsLinkedToClosedLoan(loan); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); - CommandProcessingResult result; if (loanTransactionOptional.isPresent()) { result = new CommandProcessingResultBuilder() // @@ -1756,17 +1701,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf businessEventNotifierService.notifyPreBusinessEvent(new LoanInitiateTransferBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); ExternalId externalId = externalIdFactory.create(); final LoanTransaction newTransferTransaction = LoanTransaction.initiateTransfer(loan.getOffice(), loan, transferDate, externalId); loan.addLoanTransaction(newTransferTransaction); loanLifecycleStateMachine.transition(LoanEvent.LOAN_INITIATE_TRANSFER, loan); this.loanTransactionRepository.saveAndFlush(newTransferTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(newTransferTransaction, false, false); saveLoanWithDataIntegrityViolationChecks(loan); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanInitiateTransferBusinessEvent(loan)); return newTransferTransaction; } @@ -1776,8 +1719,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public LoanTransaction acceptLoanTransfer(final Loan loan, final LocalDate transferDate, final Office acceptedInOffice, final Staff loanOfficer) { businessEventNotifierService.notifyPreBusinessEvent(new LoanAcceptTransferBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); ExternalId externalId = externalIdFactory.create(); final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.approveTransfer(acceptedInOffice, loan, transferDate, externalId); @@ -1792,9 +1733,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(newTransferAcceptanceTransaction, false, false); saveLoanWithDataIntegrityViolationChecks(loan); - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanAcceptTransferBusinessEvent(loan)); return newTransferAcceptanceTransaction; @@ -1805,9 +1745,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf public LoanTransaction withdrawLoanTransfer(final Loan loan, final LocalDate transferDate) { businessEventNotifierService.notifyPreBusinessEvent(new LoanWithdrawTransferBusinessEvent(loan)); - final List existingTransactionIds = new ArrayList<>(loanTransactionRepository.findTransactionIdsByLoan(loan)); - final List existingReversedTransactionIds = new ArrayList<>(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - ExternalId externalId = externalIdFactory.create(); final LoanTransaction newTransferAcceptanceTransaction = LoanTransaction.withdrawTransfer(loan.getOffice(), loan, transferDate, @@ -1816,9 +1753,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loanLifecycleStateMachine.transition(LoanEvent.LOAN_WITHDRAW_TRANSFER, loan); this.loanTransactionRepository.saveAndFlush(newTransferAcceptanceTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(newTransferAcceptanceTransaction, false, false); saveLoanWithDataIntegrityViolationChecks(loan); - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanWithdrawTransferBusinessEvent(loan)); return newTransferAcceptanceTransaction; @@ -2019,9 +1955,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf holidays, workingDays, isSkipRepaymentOnFirstMonth, numberOfDays); } - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, false); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, false); } saveLoanWithDataIntegrityViolationChecks(loan); businessEventNotifierService.notifyPostBusinessEvent(new LoanRescheduledDueCalendarChangeBusinessEvent(loan)); @@ -2193,8 +2129,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf Loan loan = this.loanAssembler.assembleFrom(loanId); checkClientOrGroupActive(loan); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); if (!loan.isClosedWrittenOff()) { throw new PlatformServiceUnavailableException("error.msg.loan.status.not.written.off.update.not.allowed", "Loan :" + loanId + " update not allowed as loan status is not written off", loanId); @@ -2207,20 +2141,19 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction)); loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.WRITE_OFF_OUTSTANDING_UNDO); - undoWrittenOff(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + undoWrittenOff(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(writeOffTransaction, false, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoWrittenOffBusinessEvent(writeOffTransaction)); @@ -2331,8 +2264,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf private CommandProcessingResult processLoanDisbursementDetail(Loan loan, Long loanId, JsonCommand command, LoanDisbursementDetails loanDisbursementDetails) { - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); final Map changes = new LinkedHashMap<>(); LocalDate recalculateFrom = null; ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); @@ -2352,9 +2283,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -2362,12 +2293,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf createLoanScheduleArchive(loan, scheduleGeneratorDTO); } loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); - return new CommandProcessingResultBuilder() // .withOfficeId(loan.getOfficeId()) // .withClientId(loan.getClientId()) // @@ -2416,18 +2344,15 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf ScheduleGeneratorDTO generatorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); loanScheduleService.recalculateScheduleFromLastTransaction(loan, generatorDTO, existingTransactionIds, existingReversedTransactionIds); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, - loan.isInterestBearingAndInterestRecalculationEnabled(), false); + loan.isInterestBearingAndInterestRecalculationEnabled(), true); businessEventNotifierService.notifyPostBusinessEvent(new LoanInterestRecalculationBusinessEvent(loan)); - - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); } else { loanScheduleService.recalculateScheduleFromLastTransaction(loan, null, existingTransactionIds, existingReversedTransactionIds, true); @@ -2511,10 +2436,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO); } } - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } } @@ -2681,31 +2606,20 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } businessEventNotifierService.notifyPreBusinessEvent(new LoanUndoLastDisbursalBusinessEvent(loan)); - final MonetaryCurrency currency = loan.getCurrency(); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFromDate); loanDownPaymentTransactionValidator.validateAccountStatus(loan, LoanEvent.LOAN_DISBURSAL_UNDO_LAST); loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_DISBURSAL_UNDO_LAST, loan.getDisbursementDate()); - final Map changes = undoLastDisbursal(scheduleGeneratorDTO, existingTransactionIds, existingReversedTransactionIds, - loan); - - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); - } + final Map changes = undoLastDisbursal(scheduleGeneratorDTO, loan); if (!changes.isEmpty()) { loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + if (loan.isInterestBearingAndInterestRecalculationEnabled()) { + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); + } createNote(loan, command, changes); - boolean isAccountTransfer = false; - final AccountingBridgeDataDTO accountingBridgeData = loanAccountingBridgeMapper.deriveAccountingBridgeData(currency.getCode(), - existingTransactionIds, existingReversedTransactionIds, isAccountTransfer, loan); - journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoLastDisbursalBusinessEvent(loan)); } @@ -2813,10 +2727,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } else { loan.markAsChargedOff(transactionDate, currentUser, null); } - - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - final LoanTransaction chargeOffTransaction = LoanTransaction.chargeOff(loan, transactionDate, txnExternalId); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { @@ -2833,6 +2743,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan.addLoanTransaction(chargeOffTransaction); } loanTransactionRepository.saveAndFlush(chargeOffTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(chargeOffTransaction, false, false); saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -2849,13 +2760,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf : DateUtils.isAfter(transaction.getTransactionDate(), transactionDate)) .forEach(transaction -> { transaction.reverse(); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); final LoanAdjustTransactionBusinessEvent.Data data = new LoanAdjustTransactionBusinessEvent.Data(transaction); businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(data)); }); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanChargeOffPostBusinessEvent(chargeOffTransaction)); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); return new CommandProcessingResultBuilder() // .withCommandId(command.commandId()) // .withEntityId(chargeOffTransaction.getId()) // @@ -2873,8 +2783,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanTransactionValidator.validateUndoChargeOff(command.json()); final Long loanId = command.getLoanId(); final Loan loan = this.loanAssembler.assembleFrom(loanId); - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); checkClientOrGroupActive(loan); if (!loan.isOpen()) { throw new GeneralPlatformDomainRuleException("error.msg.loan.is.not.active", @@ -2906,6 +2814,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan.liftChargeOff(); loanTransactionRepository.saveAndFlush(chargedOffTransaction); + journalEntryPoster.postJournalEntriesForLoanTransaction(chargedOffTransaction, false, false); final ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, null, null); if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) { @@ -2917,7 +2826,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf reprocessLoanTransactionsService.reprocessTransactions(loan); saveLoanWithDataIntegrityViolationChecks(loan); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); businessEventNotifierService.notifyPostBusinessEvent(new LoanUndoChargeOffBusinessEvent(chargedOffTransaction)); return new CommandProcessingResultBuilder() // @@ -2956,9 +2864,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); // Create note createNote(loan, command, changes); - // Initial transaction ids for journal entry generation - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); // Create refund transaction(s) final Boolean interestRefundCalculation = command.booleanObjectValueOfParameterNamed("interestRefundCalculation"); Pair refundTransactions = loanAccountDomainService.makeRefund(loan, scheduleGeneratorDTO, @@ -2967,8 +2872,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf LoanTransaction interestRefundTransaction = refundTransactions.getRight(); // Accrual reprocessing if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -2980,20 +2885,21 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf //// Mark Post Dated Check as paid., Do we need this for refund? loanAccountDomainService.updateAndSavePostDatedChecksForIndividualAccount(loan, refundTransaction); if (interestRefundTransaction != null) { - loanAccountDomainService.updateAndSavePostDatedChecksForIndividualAccount(loan, refundTransaction); + loanAccountDomainService.updateAndSavePostDatedChecksForIndividualAccount(loan, interestRefundTransaction); } // Collateral management loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, refundTransaction); if (interestRefundTransaction != null) { - loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, refundTransaction); + loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, interestRefundTransaction); } // Raise business events loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); - // Create journal entries for the new transaction(s) - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(refundTransaction, false, false); + if (interestRefundTransaction != null) { + journalEntryPoster.postJournalEntriesForLoanTransaction(interestRefundTransaction, false, false); + } businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); Long entityId = refundTransaction.getId(); @@ -3070,9 +2976,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf createNote(loan, command, changes); - final List existingTransactionIds = loanTransactionRepository.findTransactionIdsByLoan(loan); - final List existingReversedTransactionIds = loanTransactionRepository.findReversedTransactionIdsByLoan(loan); - final LoanTransaction interestRefundTxn = loanAccountDomainService.createManualInterestRefundWithAmount(loan, targetTransaction, amount, paymentDetail, txnExternalId); @@ -3111,8 +3014,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // Accrual reprocessing if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.reprocessExistingAccruals(loan); - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -3123,10 +3026,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loanAccountDomainService.updateAndSaveLoanCollateralTransactionsForIndividualAccounts(loan, interestRefundTxn); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(interestRefundTxn, false, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); return new CommandProcessingResultBuilder() // @@ -3194,13 +3096,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } } - private Map undoDisbursal(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO, - final List existingTransactionIds, final List existingReversedTransactionIds) { + private Map undoDisbursal(final Loan loan, final ScheduleGeneratorDTO scheduleGeneratorDTO) { final Map actualChanges = new LinkedHashMap<>(); final LoanStatus currentStatus = loan.getStatus(); final LoanStatus statusEnum = this.loanLifecycleStateMachine.dryTransition(LoanEvent.LOAN_DISBURSAL_UNDO, loan); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); if (!statusEnum.hasStateOf(currentStatus)) { this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSAL_UNDO, loan); // tis will update the loan // status @@ -3293,6 +3192,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf for (final LoanTransaction transaction : loan.getLoanTransactions()) { loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(transaction.getLoan(), transaction, "reversed"); transaction.reverse(); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); if (transaction.getId() != null) { retainTransactions.add(transaction); } @@ -3301,8 +3201,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private Optional closeAsWrittenOff(final Loan loan, final JsonCommand command, final Map changes, - final List existingTransactionIds, final List existingReversedTransactionIds, final AppUser currentUser, - final ScheduleGeneratorDTO scheduleGeneratorDTO) { + final AppUser currentUser, final ScheduleGeneratorDTO scheduleGeneratorDTO) { closeDisbursements(loan, scheduleGeneratorDTO); final LocalDate writtenOffOnLocalDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE); @@ -3315,9 +3214,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return Optional.empty(); } - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - final String txnExternalId = command.stringValueOfParameterNamedAllowingNull(EXTERNAL_ID); ExternalId externalId = ExternalIdFactory.produce(txnExternalId); @@ -3379,11 +3275,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private Optional close(final Loan loan, final JsonCommand command, final Map changes, - final List existingTransactionIds, final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - final LocalDate closureDate = command.localDateValueOfParameterNamed(TRANSACTION_DATE); final String txnExternalId = command.stringValueOfParameterNamedAllowingNull(EXTERNAL_ID); @@ -3489,17 +3381,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf reprocessLoanTransactionsService.reprocessTransactions(loan); } - private Map undoLastDisbursal(final ScheduleGeneratorDTO scheduleGeneratorDTO, final List existingTransactionIds, - final List existingReversedTransactionIds, final Loan loan) { + private Map undoLastDisbursal(final ScheduleGeneratorDTO scheduleGeneratorDTO, final Loan loan) { final Map actualChanges = new LinkedHashMap<>(); List loanTransactions = loan.retrieveListOfTransactionsByType(LoanTransactionType.DISBURSEMENT); loanTransactions.sort(Comparator.comparing(LoanTransaction::getId)); final LoanTransaction lastDisbursalTransaction = loanTransactions.getLast(); final LocalDate lastTransactionDate = lastDisbursalTransaction.getTransactionDate(); - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - loanTransactions = loanTransactionRepository.findNonReversedMonetaryTransactionsByLoan(loan); Collections.reverse(loanTransactions); for (final LoanTransaction previousTransaction : loanTransactions) { @@ -3530,18 +3418,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf return actualChanges; } - private void waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction, final List existingTransactionIds, - final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO) { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - + private void waiveInterest(final Loan loan, final LoanTransaction waiveInterestTransaction, + final ScheduleGeneratorDTO scheduleGeneratorDTO) { loanDownPaymentHandlerService.handleRepaymentOrRecoveryOrWaiverTransaction(loan, waiveInterestTransaction, null, scheduleGeneratorDTO); } - private void undoWrittenOff(final Loan loan, final List existingTransactionIds, final List existingReversedTransactionIds) { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); + private void undoWrittenOff(final Loan loan) { final LoanTransaction writeOffTransaction = loan.findWriteOffTransaction(); loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(writeOffTransaction.getLoan(), writeOffTransaction, "reversed"); @@ -3558,7 +3441,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf /** * Reverse only disbursement, accruals, and repayments at disbursal transactions */ - public void reverseExistingTransactionsTillLastDisbursal(final Loan loan, final LoanTransaction lastDisbursalTransaction) { + public void reverseExistingTransactionsTillLastDisbursal(Loan loan, final LoanTransaction lastDisbursalTransaction) { LoanCharge chargeToDeactivate = null; for (final LoanCharge charge : loan.getCharges()) { @@ -3590,6 +3473,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf && transaction.isAllowTypeTransactionAtTheTimeOfLastUndo()) { loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(transaction.getLoan(), transaction, "reversed"); transaction.reverse(); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); } } @@ -3609,9 +3493,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf .max(Comparator.comparing(LoanTransaction::getId)); // reverse the down-payment transaction - downPaymentTransaction.ifPresent(tr -> { - loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(tr.getLoan(), tr, "reversed"); - tr.reverse(); + downPaymentTransaction.ifPresent(transaction -> { + loanChargeValidator.validateRepaymentTypeTransactionNotBeforeAChargeRefund(transaction.getLoan(), transaction, "reversed"); + transaction.reverse(); + journalEntryPoster.postJournalEntriesForLoanTransaction(transaction, false, false); }); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java index 1043fc284..de8fe0a83 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ReprocessLoanTransactionsServiceImpl.java @@ -24,6 +24,10 @@ import java.util.Optional; import java.util.Set; import lombok.RequiredArgsConstructor; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; +import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualAdjustmentTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanAccrualTransactionCreatedBusinessEvent; +import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent; +import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.portfolio.interestpauses.service.LoanAccountTransfersService; @@ -56,6 +60,8 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa private final LoanBalanceService loanBalanceService; private final LoanTransactionRepository loanTransactionRepository; private final LoanTransactionService loanTransactionService; + private final LoanJournalEntryPoster loanJournalEntryPoster; + private final BusinessEventNotifierService businessEventNotifierService; @Override public void reprocessTransactions(final Loan loan) { @@ -191,8 +197,19 @@ public class ReprocessLoanTransactionsServiceImpl implements ReprocessLoanTransa loanAccountService.saveLoanTransactionWithDataIntegrityViolationChecks(newTransaction); + // Create journal entries for new transaction + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(newTransaction, false, false); + if (oldTransaction == null && (newTransaction.isAccrual() || newTransaction.isAccrualAdjustment())) { + final LoanTransactionBusinessEvent businessEvent = newTransaction.isAccrual() + ? new LoanAccrualTransactionCreatedBusinessEvent(newTransaction) + : new LoanAccrualAdjustmentTransactionBusinessEvent(newTransaction); + businessEventNotifierService.notifyPostBusinessEvent(businessEvent); + } + if (oldTransaction != null) { loanAccountTransfersService.updateLoanTransaction(oldTransaction.getId(), newTransaction); + // Create reversal journal entries for old transaction if it exists (reverse-replay scenario) + loanJournalEntryPoster.postJournalEntriesForLoanTransaction(oldTransaction, false, false); } } replayedTransactionBusinessEventService.raiseTransactionReplayedEvents(changedTransactionDetail); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java index 7267d59bd..14c0c21f1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/adjustment/LoanAdjustmentServiceImpl.java @@ -64,7 +64,6 @@ import org.apache.fineract.portfolio.loanaccount.repository.LoanCapitalizedIncom import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanTransactionValidator; import org.apache.fineract.portfolio.loanaccount.service.BuyDownFeePlatformService; -import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualTransactionBusinessEventService; import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualsProcessingService; import org.apache.fineract.portfolio.loanaccount.service.LoanBalanceService; import org.apache.fineract.portfolio.loanaccount.service.LoanDownPaymentHandlerService; @@ -94,7 +93,6 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { private final LoanUtilService loanUtilService; private final LoanRepaymentScheduleInstallmentRepository loanRepaymentScheduleInstallmentRepository; private final LoanLifecycleStateMachine loanLifecycleStateMachine; - private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService; private final LoanDownPaymentHandlerService loanDownPaymentHandlerService; private final LoanAccrualsProcessingService loanAccrualsProcessingService; private final LoanChargeValidator loanChargeValidator; @@ -115,9 +113,6 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { ExternalId reversalTxnExternalId = parameter.getReversalTxnExternalId(); String noteText = parameter.getNoteText(); - final List existingTransactionIds = new ArrayList<>(); - final List existingReversedTransactionIds = new ArrayList<>(); - final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount); LoanTransaction newTransactionDetail = LoanTransaction.repaymentType(transactionToAdjust.getTypeOf(), loan.getOffice(), transactionAmountAsMoney, paymentDetail, transactionDate, txnExternalId, transactionToAdjust.getChargeRefundChargeType()); @@ -220,12 +215,11 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { loanTransactionValidator.validateRepaymentDateIsOnNonWorkingDay(newTransactionDetail.getTransactionDate(), holidayDetailDTO.getWorkingDays(), holidayDetailDTO.isAllowTransactionsOnNonWorkingDay()); - adjustExistingTransaction(loan, newTransactionDetail, transactionToAdjust, existingTransactionIds, existingReversedTransactionIds, - scheduleGeneratorDTO, reversalTxnExternalId); + adjustExistingTransaction(loan, newTransactionDetail, transactionToAdjust, scheduleGeneratorDTO, reversalTxnExternalId); - loanAccrualsProcessingService.reprocessExistingAccruals(loan); + loanAccrualsProcessingService.reprocessExistingAccruals(loan, true); if (loan.isInterestBearingAndInterestRecalculationEnabled()) { - loanAccrualsProcessingService.processIncomePostingAndAccruals(loan); + loanAccrualsProcessingService.processIncomePostingAndAccruals(loan, true); } boolean thereIsNewTransaction = newTransactionDetail.isGreaterThanZero(); @@ -234,6 +228,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail); } this.loanTransactionRepository.saveAndFlush(newTransactionDetail); + journalEntryPoster.postJournalEntriesForLoanTransaction(newTransactionDetail, false, false); } loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan); @@ -267,7 +262,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { loanLifecycleStateMachine.determineAndTransition(loan, loan.getLastUserTransactionDate()); loanAccrualsProcessingService.processAccrualsOnInterestRecalculation(loan, loan.isInterestBearingAndInterestRecalculationEnabled(), - false); + true); this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate()); @@ -283,8 +278,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { entityExternalId = newTransactionDetail.getExternalId(); } - journalEntryPoster.postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds); - loanAccrualTransactionBusinessEventService.raiseBusinessEventForAccrualTransactions(loan, existingTransactionIds); + journalEntryPoster.postJournalEntriesForLoanTransaction(transactionToAdjust, false, false); businessEventNotifierService.notifyPostBusinessEvent(new LoanBalanceChangedBusinessEvent(loan)); businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(eventData)); @@ -300,12 +294,8 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { } public void adjustExistingTransaction(final Loan loan, final LoanTransaction newTransactionDetail, - final LoanTransaction transactionForAdjustment, final List existingTransactionIds, - final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, + final LoanTransaction transactionForAdjustment, final ScheduleGeneratorDTO scheduleGeneratorDTO, final ExternalId reversalExternalId) { - existingTransactionIds.addAll(loanTransactionRepository.findTransactionIdsByLoan(loan)); - existingReversedTransactionIds.addAll(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)); - loanTransactionValidator.validateActivityNotBeforeClientOrGroupTransferDate(loan, LoanEvent.LOAN_REPAYMENT_OR_WAIVER, transactionForAdjustment.getTransactionDate()); @@ -336,6 +326,7 @@ public class LoanAdjustmentServiceImpl implements LoanAdjustmentService { loanTransaction, "reversed"); loanTransaction.reverse(); loanTransaction.manuallyAdjustedOrReversed(); + journalEntryPoster.postJournalEntriesForLoanTransaction(loanTransaction, false, false); LoanAdjustTransactionBusinessEvent.Data eventData = new LoanAdjustTransactionBusinessEvent.Data(loanTransaction); businessEventNotifierService.notifyPostBusinessEvent(new LoanAdjustTransactionBusinessEvent(eventData)); }); diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java index c3ae2bbad..7a1e2f263 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/starter/LoanAccountConfiguration.java @@ -18,7 +18,6 @@ */ package org.apache.fineract.portfolio.loanaccount.starter; -import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.cob.service.LoanAccountLockService; import org.apache.fineract.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper; import org.apache.fineract.infrastructure.codes.domain.CodeValueRepositoryWrapper; @@ -83,7 +82,6 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanSchedul import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleAssembler; import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleCalculationPlatformService; import org.apache.fineract.portfolio.loanaccount.loanschedule.service.LoanScheduleHistoryWritePlatformService; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanChargeMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanCollateralManagementMapper; import org.apache.fineract.portfolio.loanaccount.mapper.LoanMapper; @@ -306,29 +304,27 @@ public class LoanAccountConfiguration { LoanAssembler loanAssembler, ChargeRepositoryWrapper chargeRepository, BusinessEventNotifierService businessEventNotifierService, LoanTransactionRepository loanTransactionRepository, AccountTransfersWritePlatformService accountTransfersWritePlatformService, LoanRepositoryWrapper loanRepositoryWrapper, - JournalEntryWritePlatformService journalEntryWritePlatformService, LoanAccountDomainService loanAccountDomainService, - LoanChargeRepository loanChargeRepository, LoanWritePlatformService loanWritePlatformService, LoanUtilService loanUtilService, + LoanAccountDomainService loanAccountDomainService, LoanChargeRepository loanChargeRepository, + LoanWritePlatformService loanWritePlatformService, LoanUtilService loanUtilService, LoanChargeReadPlatformService loanChargeReadPlatformService, LoanLifecycleStateMachine loanLifecycleStateMachine, AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, FromJsonHelper fromApiJsonHelper, ConfigurationDomainService configurationDomainService, LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, ExternalIdFactory externalIdFactory, AccountTransferDetailRepository accountTransferDetailRepository, LoanChargeAssembler loanChargeAssembler, PaymentDetailWritePlatformService paymentDetailWritePlatformService, - NoteRepository noteRepository, LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService, - LoanAccrualsProcessingService loanAccrualsProcessingService, + NoteRepository noteRepository, LoanAccrualsProcessingService loanAccrualsProcessingService, LoanDownPaymentTransactionValidator loanDownPaymentTransactionValidator, LoanChargeValidator loanChargeValidator, LoanScheduleService loanScheduleService, ReprocessLoanTransactionsService reprocessLoanTransactionsService, - LoanAccountService loanAccountService, LoanAdjustmentService loanAdjustmentService, - LoanAccountingBridgeMapper loanAccountingBridgeMapper, LoanChargeService loanChargeService) { + LoanAccountService loanAccountService, LoanAdjustmentService loanAdjustmentService, LoanChargeService loanChargeService, + LoanJournalEntryPoster loanJournalEntryPoster) { return new LoanChargeWritePlatformServiceImpl(loanChargeApiJsonValidator, loanAssembler, chargeRepository, businessEventNotifierService, loanTransactionRepository, accountTransfersWritePlatformService, loanRepositoryWrapper, - journalEntryWritePlatformService, loanAccountDomainService, loanChargeRepository, loanWritePlatformService, loanUtilService, - loanChargeReadPlatformService, loanLifecycleStateMachine, accountAssociationsReadPlatformService, fromApiJsonHelper, - configurationDomainService, loanRepaymentScheduleTransactionProcessorFactory, externalIdFactory, - accountTransferDetailRepository, loanChargeAssembler, paymentDetailWritePlatformService, noteRepository, - loanAccrualTransactionBusinessEventService, loanAccrualsProcessingService, loanDownPaymentTransactionValidator, + loanAccountDomainService, loanChargeRepository, loanWritePlatformService, loanUtilService, loanChargeReadPlatformService, + loanLifecycleStateMachine, accountAssociationsReadPlatformService, fromApiJsonHelper, configurationDomainService, + loanRepaymentScheduleTransactionProcessorFactory, externalIdFactory, accountTransferDetailRepository, loanChargeAssembler, + paymentDetailWritePlatformService, noteRepository, loanAccrualsProcessingService, loanDownPaymentTransactionValidator, loanChargeValidator, loanScheduleService, reprocessLoanTransactionsService, loanAccountService, loanAdjustmentService, - loanAccountingBridgeMapper, loanChargeService); + loanChargeService, loanJournalEntryPoster); } @Bean @@ -415,10 +411,10 @@ public class LoanAccountConfiguration { LoanUpdateCommandFromApiJsonDeserializer loanUpdateCommandFromApiJsonDeserializer, LoanRepositoryWrapper loanRepositoryWrapper, LoanAccountDomainService loanAccountDomainService, NoteRepository noteRepository, LoanTransactionRepository loanTransactionRepository, LoanTransactionRelationRepository loanTransactionRelationRepository, - LoanAssembler loanAssembler, JournalEntryWritePlatformService journalEntryWritePlatformService, - CalendarInstanceRepository calendarInstanceRepository, PaymentDetailWritePlatformService paymentDetailWritePlatformService, - HolidayRepositoryWrapper holidayRepository, ConfigurationDomainService configurationDomainService, - WorkingDaysRepositoryWrapper workingDaysRepository, AccountTransfersWritePlatformService accountTransfersWritePlatformService, + LoanAssembler loanAssembler, CalendarInstanceRepository calendarInstanceRepository, + PaymentDetailWritePlatformService paymentDetailWritePlatformService, HolidayRepositoryWrapper holidayRepository, + ConfigurationDomainService configurationDomainService, WorkingDaysRepositoryWrapper workingDaysRepository, + AccountTransfersWritePlatformService accountTransfersWritePlatformService, AccountTransfersReadPlatformService accountTransfersReadPlatformService, AccountAssociationsReadPlatformService accountAssociationsReadPlatformService, LoanReadPlatformService loanReadPlatformService, FromJsonHelper fromApiJsonHelper, CalendarRepository calendarRepository, @@ -440,25 +436,24 @@ public class LoanAccountConfiguration { LoanDisbursementService loanDisbursementService, LoanScheduleService loanScheduleService, LoanChargeValidator loanChargeValidator, LoanOfficerService loanOfficerService, ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanAccountService loanAccountService, - LoanJournalEntryPoster journalEntryPoster, LoanAdjustmentService loanAdjustmentService, - LoanAccountingBridgeMapper loanAccountingBridgeMapper, LoanMapper loanMapper, + LoanJournalEntryPoster journalEntryPoster, LoanAdjustmentService loanAdjustmentService, LoanMapper loanMapper, LoanTransactionProcessingService loanTransactionProcessingService, final LoanBalanceService loanBalanceService, LoanTransactionService loanTransactionService) { return new LoanWritePlatformServiceJpaRepositoryImpl(context, loanTransactionValidator, loanUpdateCommandFromApiJsonDeserializer, loanRepositoryWrapper, loanAccountDomainService, noteRepository, loanTransactionRepository, - loanTransactionRelationRepository, loanAssembler, journalEntryWritePlatformService, calendarInstanceRepository, - paymentDetailWritePlatformService, holidayRepository, configurationDomainService, workingDaysRepository, - accountTransfersWritePlatformService, accountTransfersReadPlatformService, accountAssociationsReadPlatformService, - loanReadPlatformService, fromApiJsonHelper, calendarRepository, loanScheduleHistoryWritePlatformService, - loanApplicationValidator, accountAssociationRepository, accountTransferDetailRepository, businessEventNotifierService, - guarantorDomainService, loanUtilService, entityDatatableChecksWritePlatformService, codeValueRepository, - cashierTransactionDataValidator, glimRepository, loanRepository, repaymentWithPostDatedChecksAssembler, - postDatedChecksRepository, loanRepaymentScheduleInstallmentRepository, loanLifecycleStateMachine, loanAccountLockService, - externalIdFactory, loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, - loanTransactionAssembler, loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator, - loanDisbursementService, loanScheduleService, loanChargeValidator, loanOfficerService, reprocessLoanTransactionsService, - loanAccountService, journalEntryPoster, loanAdjustmentService, loanAccountingBridgeMapper, loanMapper, - loanTransactionProcessingService, loanBalanceService, loanTransactionService); + loanTransactionRelationRepository, loanAssembler, calendarInstanceRepository, paymentDetailWritePlatformService, + holidayRepository, configurationDomainService, workingDaysRepository, accountTransfersWritePlatformService, + accountTransfersReadPlatformService, accountAssociationsReadPlatformService, loanReadPlatformService, fromApiJsonHelper, + calendarRepository, loanScheduleHistoryWritePlatformService, loanApplicationValidator, accountAssociationRepository, + accountTransferDetailRepository, businessEventNotifierService, guarantorDomainService, loanUtilService, + entityDatatableChecksWritePlatformService, codeValueRepository, cashierTransactionDataValidator, glimRepository, + loanRepository, repaymentWithPostDatedChecksAssembler, postDatedChecksRepository, + loanRepaymentScheduleInstallmentRepository, loanLifecycleStateMachine, loanAccountLockService, externalIdFactory, + loanAccrualTransactionBusinessEventService, errorHandler, loanDownPaymentHandlerService, loanTransactionAssembler, + loanAccrualsProcessingService, loanOfficerValidator, loanDownPaymentTransactionValidator, loanDisbursementService, + loanScheduleService, loanChargeValidator, loanOfficerService, reprocessLoanTransactionsService, loanAccountService, + journalEntryPoster, loanAdjustmentService, loanMapper, loanTransactionProcessingService, loanBalanceService, + loanTransactionService); } @Bean @@ -476,11 +471,12 @@ public class LoanAccountConfiguration { LoanRefundService loanRefundService, LoanRefundValidator loanRefundValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService, LoanTransactionProcessingService loanTransactionProcessingService, LoanLifecycleStateMachine loanLifecycleStateMachine, - LoanBalanceService loanBalanceService, LoanTransactionService loanTransactionService) { + LoanBalanceService loanBalanceService, LoanTransactionService loanTransactionService, + LoanJournalEntryPoster journalEntryPoster) { return new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService, loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator, reprocessLoanTransactionsService, loanTransactionProcessingService, loanLifecycleStateMachine, loanBalanceService, - loanTransactionService); + loanTransactionService, journalEntryPoster); } @Bean @@ -493,19 +489,18 @@ public class LoanAccountConfiguration { @ConditionalOnMissingBean(LoanDisbursementService.class) public LoanDisbursementService loanDisbursementService(LoanChargeValidator loanChargeValidator, LoanDisbursementValidator loanDisbursementValidator, ReprocessLoanTransactionsService reprocessLoanTransactionsService, - LoanChargeService loanChargeService, LoanBalanceService loanBalanceService) { + LoanChargeService loanChargeService, LoanBalanceService loanBalanceService, LoanJournalEntryPoster journalEntryPoster, + LoanTransactionRepository loanTransactionRepository) { return new LoanDisbursementService(loanChargeValidator, loanDisbursementValidator, reprocessLoanTransactionsService, - loanChargeService, loanBalanceService); + loanChargeService, loanBalanceService, journalEntryPoster, loanTransactionRepository); } @Bean @ConditionalOnMissingBean(LoanChargeService.class) public LoanChargeService loanChargeService(final LoanChargeValidator loanChargeValidator, final LoanTransactionProcessingService loanTransactionProcessingService, - final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanBalanceService loanBalanceService, - final LoanTransactionRepository loanTransactionRepository) { - return new LoanChargeService(loanChargeValidator, loanTransactionProcessingService, loanLifecycleStateMachine, loanBalanceService, - loanTransactionRepository); + final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanBalanceService loanBalanceService) { + return new LoanChargeService(loanChargeValidator, loanTransactionProcessingService, loanLifecycleStateMachine, loanBalanceService); } @Bean @@ -528,9 +523,8 @@ public class LoanAccountConfiguration { @ConditionalOnMissingBean(LoanRefundService.class) public LoanRefundService loanRefundService(final LoanRefundValidator loanRefundValidator, final LoanTransactionProcessingService loanTransactionProcessingService, - final LoanLifecycleStateMachine loanLifecycleStateMachine, final LoanTransactionRepository loanTransactionRepository) { - return new LoanRefundService(loanRefundValidator, loanTransactionProcessingService, loanLifecycleStateMachine, - loanTransactionRepository); + final LoanLifecycleStateMachine loanLifecycleStateMachine) { + return new LoanRefundService(loanRefundValidator, loanTransactionProcessingService, loanLifecycleStateMachine); } @Bean diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java index af2da0746..5b7405027 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/event/external/service/serialization/serializer/loan/LoanAccountDelinquencyRangeEventSerializerTest.java @@ -131,8 +131,7 @@ public class LoanAccountDelinquencyRangeEventSerializerTest { private AvroDateTimeMapper mapper; private final LoanChargeService loanChargeService = new LoanChargeService(mock(LoanChargeValidator.class), - mock(LoanTransactionProcessingService.class), mock(LoanLifecycleStateMachine.class), mock(LoanBalanceService.class), - mock(LoanTransactionRepository.class)); + mock(LoanTransactionProcessingService.class), mock(LoanLifecycleStateMachine.class), mock(LoanBalanceService.class)); private MockedStatic moneyHelper = Mockito.mockStatic(MoneyHelper.class); diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanAdjustmentServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanAdjustmentServiceImplTest.java index e4ec718d4..8c1afd70e 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanAdjustmentServiceImplTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanAdjustmentServiceImplTest.java @@ -28,9 +28,7 @@ import static org.mockito.Mockito.when; import java.time.LocalDate; import java.time.ZoneId; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -194,6 +192,8 @@ class LoanAdjustmentServiceImplTest { private LoanAccrualsProcessingService loanAccrualsProcessingService; @Mock private LoanChargeValidator loanChargeValidator; + @Mock + private LoanJournalEntryPoster journalEntryPoster; @Test void givenMerchantIssuedRefundTransactionWithRelatedTransactions_whenAdjustExistingTransaction_thenRelatedTransactionsAreReversedAndEventsTriggered() { @@ -204,9 +204,6 @@ class LoanAdjustmentServiceImplTest { ScheduleGeneratorDTO scheduleGeneratorDTO = mock(ScheduleGeneratorDTO.class); ExternalId reversalExternalId = ExternalId.generate(); - List existingTransactionIds = new ArrayList<>(); - List existingReversedTransactionIds = new ArrayList<>(); - // Mock transaction type when(transactionForAdjustment.getTypeOf()).thenReturn(LoanTransactionType.MERCHANT_ISSUED_REFUND); when(transactionForAdjustment.isNotRepaymentLikeType()).thenReturn(false); @@ -233,16 +230,12 @@ class LoanAdjustmentServiceImplTest { List loanTransactions = Arrays.asList(transactionForAdjustment, relatedTransaction); when(loan.getLoanTransactions()).thenReturn(loanTransactions); - // Mock methods called inside adjustExistingTransaction - when(loanTransactionRepository.findTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList()); - when(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList()); doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any()); when(loan.isClosedWrittenOff()).thenReturn(false); when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true); // Act - underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds, - existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId); + underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, scheduleGeneratorDTO, reversalExternalId); // Assert // Verify that related transaction is reversed and event is triggered @@ -265,9 +258,6 @@ class LoanAdjustmentServiceImplTest { ScheduleGeneratorDTO scheduleGeneratorDTO = mock(ScheduleGeneratorDTO.class); ExternalId reversalExternalId = ExternalId.generate(); - List existingTransactionIds = new ArrayList<>(); - List existingReversedTransactionIds = new ArrayList<>(); - // Mock transaction type when(transactionForAdjustment.getTypeOf()).thenReturn(LoanTransactionType.REPAYMENT); when(transactionForAdjustment.isNotRepaymentLikeType()).thenReturn(false); @@ -279,15 +269,12 @@ class LoanAdjustmentServiceImplTest { LoanTransaction unrelatedTransaction = mock(LoanTransaction.class); // Mock methods called inside adjustExistingTransaction - when(loanTransactionRepository.findTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList()); - when(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)).thenReturn(Collections.emptyList()); doNothing().when(loanTransactionValidator).validateActivityNotBeforeClientOrGroupTransferDate(any(), any(), any()); when(loan.isClosedWrittenOff()).thenReturn(false); when(newTransactionDetail.isRepaymentLikeType()).thenReturn(true); // Act - underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, existingTransactionIds, - existingReversedTransactionIds, scheduleGeneratorDTO, reversalExternalId); + underTest.adjustExistingTransaction(loan, newTransactionDetail, transactionForAdjustment, scheduleGeneratorDTO, reversalExternalId); // Assert // Verify that no related transactions are reversed diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java index cb090499c..7ef287f53 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanChargeWritePlatformServiceImplTest.java @@ -19,10 +19,7 @@ package org.apache.fineract.portfolio.loanaccount.service; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.never; @@ -35,7 +32,6 @@ import java.time.LocalDate; import java.time.ZoneId; import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.stream.Stream; import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; @@ -61,7 +57,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.SingleLoanChargeRepaymentScheduleProcessingWrapper; import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; -import org.apache.fineract.portfolio.loanaccount.mapper.LoanAccountingBridgeMapper; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeApiJsonValidator; import org.apache.fineract.portfolio.loanaccount.serialization.LoanChargeValidator; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; @@ -155,9 +150,6 @@ class LoanChargeWritePlatformServiceImplTest { @Mock private LoanAccountService loanAccountService; - @Mock - private LoanAccountingBridgeMapper loanAccountingBridgeMapper; - @Mock private LoanChargeService loanChargeService; @@ -170,6 +162,9 @@ class LoanChargeWritePlatformServiceImplTest { @Mock private LoanLifecycleStateMachine loanLifecycleStateMachine; + @Mock + private LoanJournalEntryPoster journalEntryPoster; + @BeforeEach void setUp() { when(loanAssembler.assembleFrom(LOAN_ID)).thenReturn(loan); @@ -190,10 +185,6 @@ class LoanChargeWritePlatformServiceImplTest { when(loan.getCurrency()).thenReturn(monetaryCurrency); when(monetaryCurrency.getCode()).thenReturn(CURRENCY_CODE); when(loanAccountService.saveAndFlushLoanWithDataIntegrityViolationChecks(any())).thenReturn(loan); - List existingTransactionIds = new ArrayList<>(); - List existingReversedTransactionIds = new ArrayList<>(); - when(loanTransactionRepository.findTransactionIdsByLoan(loan)).thenReturn(existingTransactionIds); - when(loanTransactionRepository.findReversedTransactionIdsByLoan(loan)).thenReturn(existingReversedTransactionIds); when(loan.getLoanCharges()).thenReturn(new HashSet<>()); when(loan.getDisbursementDate()).thenReturn(LocalDate.now(ZoneId.systemDefault())); @@ -209,7 +200,6 @@ class LoanChargeWritePlatformServiceImplTest { when(loan.isCashBasedAccountingEnabledOnLoanProduct()).thenReturn(false); when(loan.isUpfrontAccrualAccountingEnabledOnLoanProduct()).thenReturn(false); - when(loanAccountingBridgeMapper.deriveAccountingBridgeData(anyString(), anyList(), anyList(), anyBoolean(), any(Loan.class))).thenReturn(new AccountingBridgeDataDTO()); doNothing().when(journalEntryWritePlatformService).createJournalEntriesForLoan(any(AccountingBridgeDataDTO.class)); doNothing().when(loanChargeService).addLoanCharge(any(Loan.class), any(LoanCharge.class)); } diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java index e2fad522e..42d04ff43 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImplTest.java @@ -117,6 +117,9 @@ public class LoanDownPaymentHandlerServiceImplTest { @Mock private LoanTransactionService loanTransactionService; + @Mock + private LoanJournalEntryPoster loanJournalEntryPoster; + private LoanDownPaymentHandlerServiceImpl underTest; @BeforeEach @@ -124,7 +127,7 @@ public class LoanDownPaymentHandlerServiceImplTest { underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService, loanDownPaymentTransactionValidator, loanScheduleService, loanRefundService, loanRefundValidator, reprocessLoanTransactionsService, loanTransactionProcessingService, loanLifecycleStateMachine, loanBalanceService, - loanTransactionService); + loanTransactionService, loanJournalEntryPoster); moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP)); moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP); tempConfigServiceMock.when(TemporaryConfigurationServiceContainer::isExternalIdAutoGenerationEnabled).thenReturn(true);