diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/accrual/service/AccrualAccountingWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/accrual/service/AccrualAccountingWritePlatformServiceImpl.java index 7f42124a7..a7458408a 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/accrual/service/AccrualAccountingWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/accrual/service/AccrualAccountingWritePlatformServiceImpl.java @@ -29,6 +29,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; +import org.apache.fineract.infrastructure.core.exception.MultiException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService; import org.joda.time.LocalDate; @@ -52,12 +53,13 @@ public class AccrualAccountingWritePlatformServiceImpl implements AccrualAccount public CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command) { this.accountingDataValidator.validateLoanPeriodicAccrualData(command.json()); LocalDate tilldate = command.localDateValueOfParameterNamed(accrueTillParamName); - String errorlog = this.loanAccrualPlatformService.addPeriodicAccruals(tilldate); - if (errorlog.length() > 0) { + try { + this.loanAccrualPlatformService.addPeriodicAccruals(tilldate); + } catch (MultiException e) { final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) .resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME); - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE, errorlog); + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE, e.getMessage()); throw new PlatformApiDataValidationException(dataValidationErrors); } return CommandProcessingResult.empty(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/MultiException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/MultiException.java new file mode 100644 index 000000000..79ba28bef --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exception/MultiException.java @@ -0,0 +1,45 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.fineract.infrastructure.core.exception; + +/** + * Exception with multiple root causes. + * + * Intended to be used in places where N operations are performed in a loop over something, + * each of which could fail, but where we don't want to fail immediately but continue, and + * then fail at end. + * + *

The failures MUST each be logged within the loop, as they occur; this exception is + * only thrown to propagate the failure, but will not contain and re-log the details. + * + * @author Michael Vorburger.ch + */ +public class MultiException extends Exception { + + private final int n; + + public MultiException(int n) { + super(n + "x failures occured here (details have been previously logged"); + this.n = n; + } + + public int getCausesSize() { + return n; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/annotation/CronTarget.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/annotation/CronTarget.java index 1d5596dad..16aba6e8b 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/annotation/CronTarget.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/annotation/CronTarget.java @@ -26,9 +26,7 @@ import org.apache.fineract.infrastructure.jobs.service.JobName; /** * Annotation that marks a method to be picked while scheduling a cron jobs. - * */ - @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface CronTarget { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobExecutionException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobExecutionException.java index 3ebc87bd7..c66b9d612 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobExecutionException.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/exception/JobExecutionException.java @@ -18,9 +18,11 @@ */ package org.apache.fineract.infrastructure.jobs.exception; -public class JobExecutionException extends Exception { +import org.apache.fineract.infrastructure.core.exception.MultiException; - public JobExecutionException(final String msg) { - super(msg); +public class JobExecutionException extends MultiException { + + public JobExecutionException(int n) { + super(n); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java index 999e29613..cebba4559 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/service/JobName.java @@ -20,27 +20,27 @@ package org.apache.fineract.infrastructure.jobs.service; public enum JobName { - UPDATE_LOAN_SUMMARY("Update loan Summary"), // - UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), // - UPDATE_LOAN_PAID_IN_ADVANCE("Update Loan Paid In Advance"), // - APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), // - APPLY_HOLIDAYS_TO_LOANS("Apply Holidays To Loans"), // - POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"), // - TRANSFER_FEE_CHARGE_FOR_LOANS("Transfer Fee For Loans From Savings"), // - ACCOUNTING_RUNNING_BALANCE_UPDATE("Update Accounting Running Balances"), // - PAY_DUE_SAVINGS_CHARGES("Pay Due Savings Charges"), // - APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"), // - EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"), // - ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"), // - UPDATE_NPA("Update Non Performing Assets"), // - UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"), // - TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"), // - ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions"), // - RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"), // - GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule"), // - GENERATE_LOANLOSS_PROVISIONING("Generate Loan Loss Provisioning"), // - POST_DIVIDENTS_FOR_SHARES("Post Dividends For Shares"), // - UPDATE_SAVINGS_DORMANT_ACCOUNTS("Update Savings Dormant Accounts"), // + UPDATE_LOAN_SUMMARY("Update loan Summary"), + UPDATE_LOAN_ARREARS_AGEING("Update Loan Arrears Ageing"), + UPDATE_LOAN_PAID_IN_ADVANCE("Update Loan Paid In Advance"), + APPLY_ANNUAL_FEE_FOR_SAVINGS("Apply Annual Fee For Savings"), + APPLY_HOLIDAYS_TO_LOANS("Apply Holidays To Loans"), + POST_INTEREST_FOR_SAVINGS("Post Interest For Savings"), + TRANSFER_FEE_CHARGE_FOR_LOANS("Transfer Fee For Loans From Savings"), + ACCOUNTING_RUNNING_BALANCE_UPDATE("Update Accounting Running Balances"), + PAY_DUE_SAVINGS_CHARGES("Pay Due Savings Charges"), + APPLY_CHARGE_TO_OVERDUE_LOAN_INSTALLMENT("Apply penalty to overdue loans"), + EXECUTE_STANDING_INSTRUCTIONS("Execute Standing Instruction"), + ADD_ACCRUAL_ENTRIES("Add Accrual Transactions"), + UPDATE_NPA("Update Non Performing Assets"), + UPDATE_DEPOSITS_ACCOUNT_MATURITY_DETAILS("Update Deposit Accounts Maturity details"), + TRANSFER_INTEREST_TO_SAVINGS("Transfer Interest To Savings"), + ADD_PERIODIC_ACCRUAL_ENTRIES("Add Periodic Accrual Transactions"), + RECALCULATE_INTEREST_FOR_LOAN("Recalculate Interest For Loans"), + GENERATE_RD_SCEHDULE("Generate Mandatory Savings Schedule"), + GENERATE_LOANLOSS_PROVISIONING("Generate Loan Loss Provisioning"), + POST_DIVIDENTS_FOR_SHARES("Post Dividends For Shares"), + UPDATE_SAVINGS_DORMANT_ACCOUNTS("Update Savings Dormant Accounts"), ADD_PERIODIC_ACCRUAL_ENTRIES_FOR_LOANS_WITH_INCOME_POSTED_AS_TRANSACTIONS("Add Accrual Transactions For Loans With Income Posted As Transactions"), EXECUTE_REPORT_MAILING_JOBS("Execute Report Mailing Jobs"), UPDATE_SMS_OUTBOUND_WITH_CAMPAIGN_MESSAGE("Update SMS Outbound with Campaign Message"), diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java index 657e26d36..dac87a23e 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/StandingInstructionWritePlatformServiceImpl.java @@ -167,10 +167,10 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst AccountTransferStandingInstruction standingInstructionsForUpdate = this.standingInstructionRepository.findById(id) .orElseThrow(() -> new StandingInstructionNotFoundException(id)); final Map actualChanges = standingInstructionsForUpdate.update(command); - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(id) // - .with(actualChanges) // + return new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withEntityId(id) + .with(actualChanges) .build(); } @@ -182,9 +182,9 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst final Map actualChanges = new HashMap<>(); actualChanges.put(statusParamName, StandingInstructionStatus.DELETED.getValue()); - return new CommandProcessingResultBuilder() // - .withEntityId(id) // - .with(actualChanges) // + return new CommandProcessingResultBuilder() + .withEntityId(id) + .with(actualChanges) .build(); } @@ -237,8 +237,8 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst final boolean isExceptionForBalanceCheck = false; AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, transactionAmount, data.fromAccountType(), data.toAccountType(), data.fromAccount().accountId(), data.toAccount().accountId(), data.name() - + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data - .transferType().getValue(), null, null, null, null, null, fromSavingsAccount, + + " Standing instruction trasfer ", null, null, null, null, data.toTransferType(), null, null, data + .transferType().getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); final boolean transferCompleted = transferAmount(sb, accountTransferDTO, data.getId()); @@ -249,14 +249,13 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst } } - if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } - + if (sb.length() > 0) { + logger.error("executeStandingInstructions (transferAmount) encountered failure/s: {}", sb.toString()); + // This is a bit of a hack (related to https://issues.apache.org/jira/browse/FINERACT-858) and could be improved.. + throw new JobExecutionException(123456789); + } } - /** - * @param sb - * @param accountTransferDTO - */ private boolean transferAmount(final StringBuilder sb, final AccountTransferDTO accountTransferDTO, final Long instructionId) { boolean transferCompleted = true; StringBuffer errorLog = new StringBuffer(); @@ -266,23 +265,23 @@ public class StandingInstructionWritePlatformServiceImpl implements StandingInst this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } catch (final PlatformApiDataValidationException e) { sb.append("Validation exception while trasfering funds for standing Instruction id").append(instructionId).append(" from ") - .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) - .append("--------"); + .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) + .append("--------"); errorLog.append("Validation exception while trasfering funds " + e.getDefaultUserMessage()); } catch (final InsufficientAccountBalanceException e) { sb.append("InsufficientAccountBalance Exception while trasfering funds for standing Instruction id").append(instructionId) - .append(" from ").append(accountTransferDTO.getFromAccountId()).append(" to ") - .append(accountTransferDTO.getToAccountId()).append("--------"); + .append(" from ").append(accountTransferDTO.getFromAccountId()).append(" to ") + .append(accountTransferDTO.getToAccountId()).append("--------"); errorLog.append("InsufficientAccountBalance Exception "); } catch (final AbstractPlatformServiceUnavailableException e) { sb.append("Platform exception while trasfering funds for standing Instruction id").append(instructionId).append(" from ") - .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) - .append("--------"); + .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) + .append("--------"); errorLog.append("Platform exception while trasfering funds " + e.getDefaultUserMessage()); } catch (Exception e) { sb.append("Exception while trasfering funds for standing Instruction id").append(instructionId).append(" from ") - .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) - .append("--------"); + .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) + .append("--------"); errorLog.append("Exception while trasfering funds " + e.getMessage()); } 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 af4eb3260..f288b6d2c 100755 --- 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 @@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException; +import org.apache.fineract.infrastructure.core.exception.MultiException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; @@ -227,10 +228,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { // disable all active standing orders linked to this loan if status changes to closed disableStandingInstructionsLinkedToClosedLoan(loan); - builderResult.withEntityId(newRepaymentTransaction.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()); // + builderResult.withEntityId(newRepaymentTransaction.getId()) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()); return newRepaymentTransaction; } @@ -244,7 +245,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction"); if (realCause.getMessage().toLowerCase().contains("external_id_unique")) { baseDataValidator.reset().parameter("externalId").value(newRepaymentTransaction.getExternalId()) - .failWithCode("value.must.be.unique"); + .failWithCode("value.must.be.unique"); } if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.", dataValidationErrors); } @@ -412,10 +413,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_REFUND, constructEntityMap(BUSINESS_ENTITY.LOAN_TRANSACTION, newRefundTransaction)); - builderResult.withEntityId(newRefundTransaction.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()); // + builderResult.withEntityId(newRefundTransaction.getId()) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()); return newRefundTransaction; } @@ -509,10 +510,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { } if (!loanScheduleAccrualDatas.isEmpty()) { - String error = this.loanAccrualPlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualDatas); - if (error.length() > 0) { + try { + this.loanAccrualPlatformService.addPeriodicAccruals(accruedTill, loanScheduleAccrualDatas); + } catch (MultiException e) { String globalisationMessageCode = "error.msg.accrual.exception"; - throw new GeneralPlatformDomainRuleException(globalisationMessageCode, error, error); + throw new GeneralPlatformDomainRuleException(globalisationMessageCode, e.getMessage()); } } } @@ -543,10 +545,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { LoanScheduleAccrualData accrualData = new LoanScheduleAccrualData(loanId, officeId, installment.getInstallmentNumber(), accrualStartDate, repaymentFrequency, repayEvery, installment.getDueDate(), installment.getFromDate(), installment.getId(), loanProductId, installment.getInterestCharged(currency).getAmount(), installment - .getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(), + .getFeeChargesCharged(currency).getAmount(), installment.getPenaltyChargesCharged(currency).getAmount(), installment.getInterestAccrued(currency).getAmount(), installment.getFeeAccrued(currency).getAmount(), installment - .getPenaltyAccrued(currency).getAmount(), currencyData, interestCalculatedFrom, installment - .getInterestWaived(currency).getAmount()); + .getPenaltyAccrued(currency).getAmount(), currencyData, interestCalculatedFrom, installment + .getInterestWaived(currency).getAmount()); loanScheduleAccrualDatas.add(accrualData); } @@ -604,10 +606,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService { this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_REFUND, constructEntityMap(BUSINESS_ENTITY.LOAN_TRANSACTION, newRefundTransaction)); - builderResult.withEntityId(newRefundTransaction.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()); // + builderResult.withEntityId(newRefundTransaction.getId()) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()); return newRefundTransaction; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java index d3311f0c8..edcbf9402 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformService.java @@ -19,15 +19,16 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.util.Collection; +import org.apache.fineract.infrastructure.core.exception.MultiException; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData; import org.joda.time.LocalDate; public interface LoanAccrualPlatformService { - String addPeriodicAccruals(LocalDate tilldate); + void addPeriodicAccruals(LocalDate tilldate) throws MultiException; - String addPeriodicAccruals(LocalDate tilldate, Collection loanScheduleAccrualDatas); + void addPeriodicAccruals(LocalDate tilldate, Collection loanScheduleAccrualDatas) throws MultiException; void addAccrualAccounting() throws JobExecutionException; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java index af990caba..284b692c0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualPlatformServiceImpl.java @@ -22,17 +22,22 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; +import org.apache.fineract.infrastructure.core.exception.MultiException; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData; import org.joda.time.LocalDate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformService { + private final static Logger LOG = LoggerFactory.getLogger(LoanAccrualPlatformServiceImpl.class); + private final LoanReadPlatformService loanReadPlatformService; private final LoanAccrualWritePlatformService loanAccrualWritePlatformService; @@ -47,7 +52,6 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic @CronTarget(jobName = JobName.ADD_ACCRUAL_ENTRIES) public void addAccrualAccounting() throws JobExecutionException { Collection loanScheduleAccrualDatas = this.loanReadPlatformService.retriveScheduleAccrualData(); - StringBuilder sb = new StringBuilder(); Map> loanDataMap = new HashMap<>(); for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { if (loanDataMap.containsKey(accrualData.getLoanId())) { @@ -59,37 +63,36 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic } } + int errors = 0; for (Map.Entry> mapEntry : loanDataMap.entrySet()) { try { this.loanAccrualWritePlatformService.addAccrualAccounting(mapEntry.getKey(), mapEntry.getValue()); } catch (Exception e) { - Throwable realCause = e; - if (e.getCause() != null) { - realCause = e.getCause(); - } - sb.append("failed to add accural transaction for loan " + mapEntry.getKey() + " with message " + realCause.getMessage()); + LOG.error("Failed to add accural transaction for loan {}", mapEntry.getKey(), e); + ++errors; } } - - if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } + if (errors > 0) { throw new JobExecutionException(errors); } } @Override @CronTarget(jobName = JobName.ADD_PERIODIC_ACCRUAL_ENTRIES) public void addPeriodicAccruals() throws JobExecutionException { - String errors = addPeriodicAccruals(LocalDate.now()); - if (errors.length() > 0) { throw new JobExecutionException(errors); } + try { + addPeriodicAccruals(LocalDate.now()); + } catch (MultiException e) { + throw new JobExecutionException(e.getCausesSize()); + } } @Override - public String addPeriodicAccruals(final LocalDate tilldate) { + public void addPeriodicAccruals(final LocalDate tilldate) throws MultiException { Collection loanScheduleAccrualDatas = this.loanReadPlatformService.retrivePeriodicAccrualData(tilldate); - return addPeriodicAccruals(tilldate, loanScheduleAccrualDatas); + addPeriodicAccruals(tilldate, loanScheduleAccrualDatas); } @Override - public String addPeriodicAccruals(final LocalDate tilldate, Collection loanScheduleAccrualDatas) { - StringBuilder sb = new StringBuilder(); + public void addPeriodicAccruals(final LocalDate tilldate, Collection loanScheduleAccrualDatas) throws MultiException { Map> loanDataMap = new HashMap<>(); for (final LoanScheduleAccrualData accrualData : loanScheduleAccrualDatas) { if (loanDataMap.containsKey(accrualData.getLoanId())) { @@ -101,19 +104,16 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic } } + int errors = 0; for (Map.Entry> mapEntry : loanDataMap.entrySet()) { try { this.loanAccrualWritePlatformService.addPeriodicAccruals(tilldate, mapEntry.getKey(), mapEntry.getValue()); } catch (Exception e) { - Throwable realCause = e; - if (e.getCause() != null) { - realCause = e.getCause(); - } - sb.append("failed to add accural transaction for loan " + mapEntry.getKey() + " with message " + realCause.getMessage()); + LOG.error("Failed to add accural transaction for loan {}", mapEntry.getKey(), e); + ++errors; } } - - return sb.toString(); + if (errors > 0) { throw new MultiException(errors); } } @Override @@ -121,19 +121,16 @@ public class LoanAccrualPlatformServiceImpl implements LoanAccrualPlatformServic public void addPeriodicAccrualsForLoansWithIncomePostedAsTransactions() throws JobExecutionException { Collection loanIds = this.loanReadPlatformService.retrieveLoanIdsWithPendingIncomePostingTransactions(); if(loanIds != null && loanIds.size() > 0){ - StringBuilder sb = new StringBuilder(); + int errors = 0; for (Long loanId : loanIds) { try { this.loanAccrualWritePlatformService.addIncomeAndAccrualTransactions(loanId); } catch (Exception e) { - Throwable realCause = e; - if (e.getCause() != null) { - realCause = e.getCause(); - } - sb.append("failed to add income and accrual transaction for loan " + loanId + " with message " + realCause.getMessage()); + LOG.error("Failed to add income and accrual transaction for loan {}", loanId, e); + ++errors; } } - if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } + if (errors > 0) { throw new JobExecutionException(errors); } } } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularService.java index 25b1728cf..b6bfa2943 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularService.java @@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.util.Map; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; -import org.apache.fineract.organisation.office.data.OfficeData; public interface LoanSchedularService { @@ -29,8 +28,5 @@ public interface LoanSchedularService { void recalculateInterest() throws JobExecutionException; - void recalculateInterest(final OfficeData office, final int threadPoolSize, final int batchSize); - void recalculateInterest(@SuppressWarnings("unused") final Map jobParameters); - } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularServiceImpl.java index f56bdedc5..b8d36f619 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanSchedularServiceImpl.java @@ -55,6 +55,7 @@ import org.springframework.util.CollectionUtils; public class LoanSchedularServiceImpl implements LoanSchedularService { private final static Logger logger = LoggerFactory.getLogger(LoanSchedularServiceImpl.class); + private final ConfigurationDomainService configurationDomainService; private final LoanReadPlatformService loanReadPlatformService; private final LoanWritePlatformService loanWritePlatformService; @@ -63,9 +64,9 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { @Autowired public LoanSchedularServiceImpl(final ConfigurationDomainService configurationDomainService, - final LoanReadPlatformService loanReadPlatformService, final LoanWritePlatformService loanWritePlatformService, - final OfficeReadPlatformService officeReadPlatformService, - final ApplicationContext applicationContext) { + final LoanReadPlatformService loanReadPlatformService, final LoanWritePlatformService loanWritePlatformService, + final OfficeReadPlatformService officeReadPlatformService, + final ApplicationContext applicationContext) { this.configurationDomainService = configurationDomainService; this.loanReadPlatformService = loanReadPlatformService; this.loanWritePlatformService = loanWritePlatformService; @@ -83,7 +84,6 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { .retrieveAllLoansWithOverdueInstallments(penaltyWaitPeriodValue,backdatePenalties); if (!overdueLoanScheduledInstallments.isEmpty()) { - final StringBuilder sb = new StringBuilder(); final Map> overdueScheduleData = new HashMap<>(); for (final OverdueLoanScheduleData overdueInstallment : overdueLoanScheduledInstallments) { if (overdueScheduleData.containsKey(overdueInstallment.getLoanId())) { @@ -95,6 +95,7 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { } } + int numberOfErrors = 0; for (final Long loanId : overdueScheduleData.keySet()) { try { this.loanWritePlatformService.applyOverdueChargesForLoan(loanId, overdueScheduleData.get(loanId)); @@ -102,28 +103,18 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { } catch (final PlatformApiDataValidationException e) { final List errors = e.getErrors(); for (final ApiParameterError error : errors) { - logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message " - + error.getDeveloperMessage()); - sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ") - .append(error.getDeveloperMessage()); + logger.error("Apply Charges due for overdue loans failed for account {} with message: {}", loanId, error.getDeveloperMessage(), e); + ++numberOfErrors; } - } catch (final AbstractPlatformDomainRuleException ex) { - logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message " - + ex.getDefaultUserMessage()); - sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ") - .append(ex.getDefaultUserMessage()); + } catch (final AbstractPlatformDomainRuleException e) { + logger.error("Apply Charges due for overdue loans failed for account {} with message: {}", loanId, e.getDefaultUserMessage(), e); + ++numberOfErrors; } catch (Exception e) { - Throwable realCause = e; - if (e.getCause() != null) { - realCause = e.getCause(); - } - logger.error("Apply Charges due for overdue loans failed for account:" + loanId + " with message " - + realCause.getMessage()); - sb.append("Apply Charges due for overdue loans failed for account:").append(loanId).append(" with message ") - .append(realCause.getMessage()); + logger.error("Apply Charges due for overdue loans failed for account {}", loanId, e); + ++numberOfErrors; } } - if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } + if (numberOfErrors > 0) { throw new JobExecutionException(numberOfErrors); } } } @@ -138,64 +129,44 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { .fetchLoansForInterestRecalculation(); int i = 0; if (!loanIds.isEmpty()) { - final StringBuilder sb = new StringBuilder(); + int errors = 0; for (Long loanId : loanIds) { - logger.info("Loan ID " + loanId); + logger.info("recalculateInterest: Loan ID = {}", loanId); Integer numberOfRetries = 0; while (numberOfRetries <= maxNumberOfRetries) { try { - this.loanWritePlatformService - .recalculateInterest(loanId); + this.loanWritePlatformService.recalculateInterest(loanId); numberOfRetries = maxNumberOfRetries + 1; } catch (CannotAcquireLockException | ObjectOptimisticLockingFailureException exception) { - logger.info("Recalulate interest job has been retried " - + numberOfRetries + " time(s)"); - /*** - * Fail if the transaction has been retired for - * maxNumberOfRetries - **/ + logger.info("Recalulate interest job has been retried {} time(s)", numberOfRetries); + // Fail if the transaction has been retried for maxNumberOfRetries if (numberOfRetries >= maxNumberOfRetries) { - logger.warn("Recalulate interest job has been retried for the max allowed attempts of " - + numberOfRetries - + " and will be rolled back"); - sb.append("Recalulate interest job has been retried for the max allowed attempts of " - + numberOfRetries - + " and will be rolled back"); + logger.error("Recalulate interest job has been retried for the max allowed attempts of {} and will be rolled back", numberOfRetries); + ++errors; break; } - /*** - * Else sleep for a random time (between 1 to 10 - * seconds) and continue - **/ + // Else sleep for a random time (between 1 to 10 seconds) and continue try { Random random = new Random(); - int randomNum = random - .nextInt(maxIntervalBetweenRetries + 1); + int randomNum = random.nextInt(maxIntervalBetweenRetries + 1); Thread.sleep(1000 + (randomNum * 1000)); numberOfRetries = numberOfRetries + 1; } catch (InterruptedException e) { - sb.append("Interest recalculation for loans failed " + exception.getMessage()) ; + logger.error("Interest recalculation for loans retry failed due to InterruptedException", e) ; + ++errors; break; } } catch (Exception e) { - Throwable realCause = e; - if (e.getCause() != null) { - realCause = e.getCause(); - } - logger.error("Interest recalculation for loans failed for account:" + loanId + " with message " - + realCause.getMessage()); - sb.append("Interest recalculation for loans failed for account:").append(loanId).append(" with message ") - .append(realCause.getMessage()); + logger.error("Interest recalculation for loans failed for account {}", loanId, e); numberOfRetries = maxNumberOfRetries + 1; + ++errors; } i++; } - logger.info("Loans count " + i); - } - if (sb.length() > 0) { - throw new JobExecutionException(sb.toString()); + logger.info("recalculateInterest: Loans count {}", i); } + if (errors > 0) { throw new JobExecutionException(errors); } } } @@ -203,64 +174,63 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { @Override @CronTarget(jobName = JobName.RECALCULATE_INTEREST_FOR_LOAN) public void recalculateInterest(Map jobParameters) { - //gets the officeId + // gets the officeId final String officeId = jobParameters.get("officeId"); - logger.info(officeId); - Long officeIdLong=Long.valueOf(officeId); - //gets the Office object + logger.info("recalculateInterest: officeId={}", officeId); + Long officeIdLong = Long.valueOf(officeId); + + // gets the Office object final OfficeData office = this.officeReadPlatformService.retrieveOffice(officeIdLong); - if(office == null) + if(office == null) { throw new OfficeNotFoundException(officeIdLong); + } final int threadPoolSize=Integer.parseInt(jobParameters.get("thread-pool-size")); final int batchSize=Integer.parseInt(jobParameters.get("batch-size")); recalculateInterest(office,threadPoolSize,batchSize); } - @Override - public void recalculateInterest(OfficeData office, int threadPoolSize, int batchSize) { + private void recalculateInterest(OfficeData office, int threadPoolSize, int batchSize) { final int pageSize = batchSize * threadPoolSize; - //initialise the executor service with fetched configurations + // initialise the executor service with fetched configurations final ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize); Long maxLoanIdInList = 0L; final String officeHierarchy = office.getHierarchy() + "%"; - //Get the loanIds from service + // get the loanIds from service List loanIds = Collections.synchronizedList(this.loanReadPlatformService .fetchLoansForInterestRecalculation(pageSize, maxLoanIdInList, officeHierarchy)); - // gets the loanIds data set iteratively and call addAccuruals for that paginated dataset do { int totalFilteredRecords = loanIds.size(); logger.info("Starting accrual - total filtered records - " + totalFilteredRecords); - recalculateInterest(loanIds, threadPoolSize, batchSize, - executorService); + recalculateInterest(loanIds, threadPoolSize, batchSize, executorService); maxLoanIdInList+= pageSize+1; loanIds = Collections.synchronizedList(this.loanReadPlatformService .fetchLoansForInterestRecalculation(pageSize, maxLoanIdInList, officeHierarchy)); } while (!CollectionUtils.isEmpty(loanIds)); - //shutdown the executor when done + // shutdown the executor when done executorService.shutdownNow(); } private void recalculateInterest(List loanIds, - int threadPoolSize, int batchSize, final ExecutorService executorService) { - //StringBuilder sb = new StringBuilder(); + int threadPoolSize, int batchSize, final ExecutorService executorService) { - List> posters = new ArrayList>(); + List> posters = new ArrayList<>(); int fromIndex = 0; // get the size of current paginated dataset int size = loanIds.size(); - //calculate the batch size - double toGetCeilValue = (double) (size / threadPoolSize); + // calculate the batch size + double toGetCeilValue = size / threadPoolSize; batchSize = (int) Math.ceil(toGetCeilValue); - if(batchSize == 0) + if(batchSize == 0) { return; + } int toIndex = (batchSize > size - 1)? size : batchSize ; while(toIndex < size && loanIds.get(toIndex - 1).equals(loanIds.get(toIndex))) { @@ -274,27 +244,30 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { RecalculateInterestPoster poster = (RecalculateInterestPoster) this.applicationContext.getBean("recalculateInterestPoster"); poster.setLoanIds(subList); poster.setLoanWritePlatformService(loanWritePlatformService); - posters.add(Executors.callable(poster)); - if(lastBatch) + posters.add(poster); + if(lastBatch) { break; - if(toIndex + batchSize > size - 1) + } + if(toIndex + batchSize > size - 1) { lastBatch = true; + } fromIndex = fromIndex + (toIndex - fromIndex); toIndex = (toIndex + batchSize > size - 1)? size : toIndex + batchSize; while(toIndex < size && loanIds.get(toIndex - 1).equals(loanIds.get(toIndex))) { - toIndex ++; + toIndex++; } } try { - List> responses = executorService.invokeAll(posters); + List> responses = executorService.invokeAll(posters); checkCompletion(responses); } catch (InterruptedException e1) { logger.error("Interrupted while recalculateInterest", e1); } } - //break the lists into sub lists - public List safeSubList(List list, int fromIndex, int toIndex) { + + // break the lists into sub lists + private List safeSubList(List list, int fromIndex, int toIndex) { int size = list.size(); if (fromIndex >= size || toIndex <= 0 || fromIndex >= toIndex) { return Collections.emptyList(); @@ -305,30 +278,28 @@ public class LoanSchedularServiceImpl implements LoanSchedularService { return list.subList(fromIndex, toIndex); } - //checks the execution of task by each thread in the executor service - private void checkCompletion(List> responses) { + + // checks the execution of task by each thread in the executor service + private void checkCompletion(List> responses) { try { - for(Future f : responses) { + for(Future f : responses) { f.get(); } boolean allThreadsExecuted = false; int noOfThreadsExecuted = 0; - for (Future future : responses) { + for (Future future : responses) { if (future.isDone()) { noOfThreadsExecuted++; } } allThreadsExecuted = noOfThreadsExecuted == responses.size(); - if(!allThreadsExecuted) + if(!allThreadsExecuted) { logger.error("All threads could not execute."); + } } catch (InterruptedException e1) { logger.error("Interrupted while posting IR entries", e1); } catch (ExecutionException e2) { logger.error("Execution exception while posting IR entries", e2); } } - - - - } \ No newline at end of file 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 d8bf85c0a..94cb93b2b 100755 --- 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 @@ -194,7 +194,7 @@ import org.springframework.transaction.annotation.Transactional; @Service public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatformService { - private final static Logger logger = LoggerFactory.getLogger(LoanWritePlatformServiceJpaRepositoryImpl.class); + private final static Logger LOG = LoggerFactory.getLogger(LoanWritePlatformServiceJpaRepositoryImpl.class); private final PlatformSecurityContext context; private final LoanEventApiJsonValidator loanEventApiJsonValidator; @@ -393,11 +393,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf throw new GeneralPlatformDomainRuleException( "error.msg.loan.disbursal.date.should.be.after.last.transaction.date.of.loan.to.be.closed", "Disbursal date of this loan application "+loan.getDisbursementDate() - +" should be after last transaction date of loan to be closed "+ lastUserTransactionOnLoanToClose); + +" should be after last transaction date of loan to be closed "+ lastUserTransactionOnLoanToClose); } BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanIdToClose, - actualDisbursementDate).getAmount(); + actualDisbursementDate).getAmount(); final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount(); if(loanOutstanding.compareTo(firstDisbursalAmount) > 0){ throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed", @@ -621,9 +621,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final LocalDate nextPossibleRepaymentDate = null; final Date rescheduledRepaymentDate = null; - for (int i = 0; i < disbursalCommand.length; i++) { - final SingleDisbursalCommand singleLoanDisbursalCommand = disbursalCommand[i]; - + for (final SingleDisbursalCommand singleLoanDisbursalCommand : disbursalCommand) { final Loan loan = this.loanAssembler.assembleFrom(singleLoanDisbursalCommand.getLoanId()); final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); @@ -896,7 +894,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf constructEntityMap(BUSINESS_ENTITY.LOAN_ADJUSTED_TRANSACTION, transactionToAdjust)); if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)) { throw new PlatformServiceUnavailableException( "error.msg.loan.transfer.transaction.update.not.allowed", "Loan transaction:" + transactionId - + " update not allowed as it involves in account transfer", transactionId); } + + " update not allowed as it involves in account transfer", transactionId); } if (loan.isClosedWrittenOff()) { throw new PlatformServiceUnavailableException("error.msg.loan.written.off.update.not.allowed", "Loan transaction:" + transactionId + " update not allowed as loan status is written off", transactionId); } @@ -995,7 +993,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf if (!transactionIds.isEmpty()) { this.accountTransfersWritePlatformService - .reverseTransfersWithFromAccountTransactions(transactionIds, PortfolioAccountType.LOAN); + .reverseTransfersWithFromAccountTransactions(transactionIds, PortfolioAccountType.LOAN); loan.updateLoanSummarAndStatus(); } @@ -1008,14 +1006,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_ADJUST_TRANSACTION, entityMap); - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(transactionId) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()) // - .withLoanId(loanId) // - .with(changes) // + return new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withEntityId(transactionId) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()) + .withLoanId(loanId) + .with(changes) .build(); } @@ -1095,14 +1093,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanAccountDomainService.recalculateAccruals(loan); this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_WAIVE_INTEREST, constructEntityMap(BUSINESS_ENTITY.LOAN_TRANSACTION, waiveInterestTransaction)); - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(waiveInterestTransaction.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()) // - .withLoanId(loanId) // - .with(changes) // + return new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withEntityId(waiveInterestTransaction.getId()) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()) + .withLoanId(loanId) + .with(changes) .build(); } @@ -1165,14 +1163,14 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf this.loanAccountDomainService.recalculateAccruals(loan); this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_WRITTEN_OFF, constructEntityMap(BUSINESS_ENTITY.LOAN_TRANSACTION, writeoff)); - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(writeoff.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()) // - .withLoanId(loanId) // - .with(changes) // + return new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withEntityId(writeoff.getId()) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()) + .withLoanId(loanId) + .with(changes) .build(); } @@ -1237,24 +1235,24 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf CommandProcessingResult result = null; if (possibleClosingTransaction != null) { - result = new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(possibleClosingTransaction.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()) // - .withLoanId(loanId) // - .with(changes) // + result = new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withEntityId(possibleClosingTransaction.getId()) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()) + .withLoanId(loanId) + .with(changes) .build(); } else { - result = new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(loanId) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()) // - .withLoanId(loanId) // - .with(changes) // + result = new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withEntityId(loanId) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()) + .withLoanId(loanId) + .with(changes) .build(); } @@ -1332,14 +1330,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Charge chargeDefinition = this.chargeRepository.findOneWithNotFoundDetection(chargeDefinitionId); if (loan.isDisbursed() && chargeDefinition.isDisbursementCharge()) { - validateAddingNewChargeAllowed(loanDisburseDetails); // validates - // whether any - // pending - // disbursements - // are - // available to - // apply this - // charge + // validates whether any pending disbursements are available to apply this charge + validateAddingNewChargeAllowed(loanDisburseDetails); } final List existingTransactionIds = new ArrayList<>(loan.findExistingTransactionIds()); final List existingReversedTransactionIds = new ArrayList<>(loan.findExistingReversedTransactionIds()); @@ -1410,13 +1402,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } this.businessEventNotifierService.notifyBusinessEventWasExecuted(BUSINESS_EVENTS.LOAN_ADD_CHARGE, constructEntityMap(BUSINESS_ENTITY.LOAN_CHARGE, loanCharge)); - return new CommandProcessingResultBuilder() // - .withCommandId(command.commandId()) // - .withEntityId(loanCharge.getId()) // - .withOfficeId(loan.getOfficeId()) // - .withClientId(loan.getClientId()) // - .withGroupId(loan.getGroupId()) // - .withLoanId(loanId) // + return new CommandProcessingResultBuilder() + .withCommandId(command.commandId()) + .withEntityId(loanCharge.getId()) + .withOfficeId(loan.getOfficeId()) + .withClientId(loan.getClientId()) + .withGroupId(loan.getGroupId()) + .withLoanId(loanId) .build(); } @@ -1782,7 +1774,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf final Collection chargeDatas = this.loanChargeReadPlatformService.retrieveLoanChargesForFeePayment( ChargePaymentMode.ACCOUNT_TRANSFER.getValue(), LoanStatus.ACTIVE.getValue()); final boolean isRegularTransaction = true; - final StringBuilder sb = new StringBuilder(); + int errors = 0; if (chargeDatas != null) { for (final LoanChargeData chargeData : chargeDatas) { if (chargeData.isInstallmentFee()) { @@ -1803,7 +1795,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf null, LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), installmentChargeData.getInstallmentNumber(), AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); - transferFeeCharge(sb, accountTransferDTO); + if (!transferFeeCharge(accountTransferDTO)) { + ++errors; + } } } } else if (chargeData.getDueDate() != null && !chargeData.getDueDate().isAfter(DateUtils.getLocalDateOfTenant())) { @@ -1817,27 +1811,25 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf LoanTransactionType.CHARGE_PAYMENT.getValue(), chargeData.getId(), null, AccountTransferType.CHARGE_PAYMENT.getValue(), null, null, null, null, null, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); - transferFeeCharge(sb, accountTransferDTO); + if (!transferFeeCharge(accountTransferDTO)) { + ++errors; + } } } } - if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } + if (errors > 0) { throw new JobExecutionException(errors); } } - /** - * @param sb - * @param accountTransferDTO - */ - private void transferFeeCharge(final StringBuilder sb, final AccountTransferDTO accountTransferDTO) { + private boolean transferFeeCharge(final AccountTransferDTO accountTransferDTO) { try { this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); + return true; } catch (final PlatformApiDataValidationException e) { - sb.append("Validation exception while paying charge ").append(accountTransferDTO.getChargeId()).append(" for loan id:") - .append(accountTransferDTO.getToAccountId()).append("--------"); + LOG.error("Validation exception while paying charge {} for loan id {}", accountTransferDTO.getChargeId(), accountTransferDTO.getToAccountId(), e); + return false; } catch (final InsufficientAccountBalanceException e) { - sb.append("InsufficientAccountBalance Exception while paying charge ").append(accountTransferDTO.getChargeId()) - .append("for loan id:").append(accountTransferDTO.getToAccountId()).append("--------"); - + LOG.error("InsufficientAccountBalanceException while paying charge {} for loan id {}", accountTransferDTO.getChargeId(), accountTransferDTO.getToAccountId(), e); + return false; } } @@ -2517,7 +2509,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf 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); } + + " update not allowed as loan status is not written off", loanId); } LocalDate recalculateFrom = null; LoanTransaction writeOffTransaction = loan.findWriteOffTransaction(); this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_UNDO_WRITTEN_OFF, @@ -2883,7 +2875,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf BigDecimal overpaid = this.loanReadPlatformService.retrieveTotalPaidInAdvance(loanId).getPaidInAdvance(); if (overpaid == null || overpaid.equals(new BigDecimal(0)) || transactionAmount.floatValue() > overpaid.floatValue()) { - if (overpaid == null) overpaid = BigDecimal.ZERO; + if (overpaid == null) { + overpaid = BigDecimal.ZERO; + } throw new InvalidPaidInAdvanceAmountException(overpaid.toPlainString()); } } @@ -3005,12 +2999,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf } private void syncExpectedDateWithActualDisbursementDate(final Loan loan, LocalDate actualDisbursementDate){ - if(!loan.getExpectedDisbursedOnLocalDate().equals(actualDisbursementDate)){ - throw new DateMismatchException(actualDisbursementDate, - loan.getExpectedDisbursedOnLocalDate()); - } + if(!loan.getExpectedDisbursedOnLocalDate().equals(actualDisbursementDate)){ + throw new DateMismatchException(actualDisbursementDate, + loan.getExpectedDisbursedOnLocalDate()); + } - } + } private void validateTransactionsForTransfer(final Loan loan, final LocalDate transferDate) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/RecalculateInterestPoster.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/RecalculateInterestPoster.java index f3dfab875..b084c6060 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/RecalculateInterestPoster.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/RecalculateInterestPoster.java @@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.loanaccount.service; import java.util.Collection; import java.util.Random; +import java.util.concurrent.Callable; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException; import org.slf4j.Logger; @@ -29,41 +30,25 @@ import org.springframework.dao.CannotAcquireLockException; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.springframework.stereotype.Component; - @Component - @Scope("prototype") +public class RecalculateInterestPoster implements Callable { -public class RecalculateInterestPoster implements Runnable { - - - - private final static Logger logger = LoggerFactory.getLogger(" recalculate interest poster"); + private final static Logger logger = LoggerFactory.getLogger(RecalculateInterestPoster.class); private Collection loanIds; - private LoanWritePlatformService loanWritePlatformService; - public void setLoanIds(final Collection loanIds) { - this.loanIds = loanIds; - } - - public void setLoanWritePlatformService(final LoanWritePlatformService loanWritePlatformService) { - this.loanWritePlatformService = loanWritePlatformService; - } - - @Override - public void run() { - + public Void call() throws JobExecutionException { Integer maxNumberOfRetries = ThreadLocalContextUtil.getTenant() .getConnection().getMaxRetriesOnDeadlock(); Integer maxIntervalBetweenRetries = ThreadLocalContextUtil.getTenant() @@ -71,72 +56,45 @@ public class RecalculateInterestPoster implements Runnable { int i = 0; if (!loanIds.isEmpty()) { - final StringBuilder sb = new StringBuilder(); + int errors = 0; for (Long loanId : loanIds) { logger.info("Loan ID " + loanId); Integer numberOfRetries = 0; while (numberOfRetries <= maxNumberOfRetries) { try { - this.loanWritePlatformService - .recalculateInterest(loanId); + this.loanWritePlatformService.recalculateInterest(loanId); numberOfRetries = maxNumberOfRetries + 1; } catch (CannotAcquireLockException | ObjectOptimisticLockingFailureException exception) { - logger.info("Recalulate interest job has been retried " - + numberOfRetries + " time(s)"); - /*** - * Fail if the transaction has been retired for - * maxNumberOfRetries - **/ + logger.info("Recalulate interest job has been retried time(s)", numberOfRetries); + // Fail if the transaction has been retired for maxNumberOfRetries if (numberOfRetries >= maxNumberOfRetries) { - logger.warn("Recalulate interest job has been retried for the max allowed attempts of " - + numberOfRetries - + " and will be rolled back"); - sb.append("Recalulate interest job has been retried for the max allowed attempts of " - + numberOfRetries - + " and will be rolled back"); + logger.error("Recalulate interest job has been retried for the max allowed attempts of {} and will be rolled back", numberOfRetries); + ++errors; break; } - /*** - * Else sleep for a random time (between 1 to 10 - * seconds) and continue - **/ + // Else sleep for a random time (between 1 to 10 seconds) and continue try { Random random = new Random(); - int randomNum = random - .nextInt(maxIntervalBetweenRetries + 1); + int randomNum = random.nextInt(maxIntervalBetweenRetries + 1); Thread.sleep(1000 + (randomNum * 1000)); numberOfRetries = numberOfRetries + 1; } catch (InterruptedException e) { - sb.append("Interest recalculation for loans failed " + exception.getMessage()) ; + logger.error("Interest recalculation for loans retry failed due to InterruptedException", e) ; + ++errors; break; } } catch (Exception e) { - Throwable realCause = e; - if (e.getCause() != null) { - realCause = e.getCause(); - } - logger.error("Interest recalculation for loans failed for account:" + loanId + " with message " + realCause.getMessage(), e); - sb.append("Interest recalculation for loans failed for account:").append(loanId).append(" with message ") - .append(realCause.getMessage()); + logger.error("Interest recalculation for loans failed for account {}", loanId, e); numberOfRetries = maxNumberOfRetries + 1; + ++errors; } i++; } logger.info("Loans count " + i); } - if (sb.length() > 0) { - try { - throw new JobExecutionException(sb.toString()); - } catch (JobExecutionException e) { - logger.info("JobExecutionException occured :", e); - } - } + if (errors > 0) { throw new JobExecutionException(errors); } } - - + return null; } - - - } \ No newline at end of file diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 23841e2fa..d5c23eb19 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -112,6 +112,8 @@ import org.apache.fineract.useradministration.domain.AppUser; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -119,6 +121,8 @@ import org.springframework.transaction.annotation.Transactional; @Service public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements DepositAccountWritePlatformService { + private final static Logger LOG = LoggerFactory.getLogger(DepositAccountWritePlatformServiceJpaRepositoryImpl.class); + private final PlatformSecurityContext context; private final SavingsAccountRepositoryWrapper savingAccountRepositoryWrapper; private final SavingsAccountTransactionRepository savingsAccountTransactionRepository; @@ -571,21 +575,20 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo @Override @CronTarget(jobName = JobName.TRANSFER_INTEREST_TO_SAVINGS) public void transferInterestToSavings() throws JobExecutionException { + int errors = 0; Collection accountTrasferData = this.depositAccountReadPlatformService.retrieveDataForInterestTransfer(); - StringBuilder sb = new StringBuilder(200); for (AccountTransferDTO accountTransferDTO : accountTrasferData) { try { this.accountTransfersWritePlatformService.transferFunds(accountTransferDTO); } catch (final PlatformApiDataValidationException e) { - sb.append("Validation exception while trasfering Interest form ").append(accountTransferDTO.getFromAccountId()) - .append(" to ").append(accountTransferDTO.getToAccountId()).append("--------"); + LOG.error("Validation exception while trasfering Interest from {} to {}", accountTransferDTO.getFromAccountId(), accountTransferDTO.getToAccountId(), e); + ++errors; } catch (final InsufficientAccountBalanceException e) { - sb.append("InsufficientAccountBalance Exception while trasfering Interest form ") - .append(accountTransferDTO.getFromAccountId()).append(" to ").append(accountTransferDTO.getToAccountId()) - .append("--------"); + LOG.error("InsufficientAccountBalanceException while trasfering Interest from {} to {} ", accountTransferDTO.getFromAccountId(), accountTransferDTO.getToAccountId(), e); + ++errors; } } - if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } + if (errors > 0) { throw new JobExecutionException(errors); } } @Override @@ -615,8 +618,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo if (!allowAccountTransferModification && this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException( - "error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed", "Recurring deposit account transaction:" - + transactionId + " update not allowed as it involves in account transfer", transactionId); } + "error.msg.recurring.deposit.account.transfer.transaction.update.not.allowed", "Recurring deposit account transaction:" + + transactionId + " update not allowed as it involves in account transfer", transactionId); } final LocalDate today = DateUtils.getLocalDateOfTenant(); final MathContext mc = MathContext.DECIMAL64; @@ -689,12 +692,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo .findOneByIdAndSavingsAccountId(transactionId, savingsId); if (savingsAccountTransaction == null) { throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); } - if (!(savingsAccountTransaction.isDeposit() || savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException( + if ((!savingsAccountTransaction.isDeposit() && !savingsAccountTransaction.isWithdrawal()) || savingsAccountTransaction.isReversed()) { throw new TransactionUpdateNotAllowedException( savingsId, transactionId); } if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.SAVINGS)) { throw new PlatformServiceUnavailableException( "error.msg.saving.account.transfer.transaction.update.not.allowed", "Deposit account transaction:" + transactionId - + " update not allowed as it involves in account transfer", transactionId); } + + " update not allowed as it involves in account transfer", transactionId); } final LocalDate today = DateUtils.getLocalDateOfTenant(); @@ -1071,14 +1074,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() && this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() && !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } } @@ -1126,14 +1129,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() && this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() && !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); + .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } } @@ -1253,14 +1256,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() && this.holidayRepository.isHoliday(savingsAccountCharge.savingsAccount().officeId(), transactionDate)) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday"); + .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.on.holiday"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() && !this.workingDaysRepository.isWorkingDay(transactionDate)) { baseDataValidator.reset().parameter(dueAsOfDateParamName).value(transactionDate.toString(fmt)) - .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day"); + .failWithCodeNoParameterAddedToErrorCode("transaction.not.allowed.transaction.date.is.a.nonworking.day"); if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java index 6b624f112..0e3cc7f44 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsSchedularServiceImpl.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.portfolio.savings.service; +import static org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType.ACTIVE; + import java.util.List; import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.jobs.annotation.CronTarget; @@ -26,8 +28,9 @@ import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.savings.domain.SavingsAccountAssembler; import org.apache.fineract.portfolio.savings.domain.SavingsAccountRepositoryWrapper; -import org.apache.fineract.portfolio.savings.domain.SavingsAccountStatusType; import org.joda.time.LocalDate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; @@ -36,6 +39,8 @@ import org.springframework.stereotype.Service; @Service public class SavingsSchedularServiceImpl implements SavingsSchedularService { + private final static Logger LOG = LoggerFactory.getLogger(SavingsSchedularServiceImpl.class); + private final SavingsAccountAssembler savingAccountAssembler; private final SavingsAccountWritePlatformService savingsAccountWritePlatformService; private final SavingsAccountReadPlatformService savingAccountReadPlatformService; @@ -51,63 +56,54 @@ public class SavingsSchedularServiceImpl implements SavingsSchedularService { this.savingsAccountRepository = savingsAccountRepository; } - @CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS) @Override + @CronTarget(jobName = JobName.POST_INTEREST_FOR_SAVINGS) public void postInterestForAccounts() throws JobExecutionException { int page = 0; Integer initialSize = 500; Integer totalPageSize = 0; - StringBuffer sb = new StringBuffer(); + int errors = 0; do { PageRequest pageRequest = PageRequest.of(page, initialSize); - Page savingsAccounts = this.savingsAccountRepository.findByStatus(SavingsAccountStatusType.ACTIVE.getValue(), - pageRequest); + Page savingsAccounts = this.savingsAccountRepository.findByStatus(ACTIVE.getValue(), pageRequest); for (SavingsAccount savingsAccount : savingsAccounts.getContent()) { try { this.savingAccountAssembler.assignSavingAccountHelpers(savingsAccount); boolean postInterestAsOn = false; LocalDate transactionDate = null; - this.savingsAccountWritePlatformService.postInterest(savingsAccount, postInterestAsOn, - transactionDate); + this.savingsAccountWritePlatformService.postInterest(savingsAccount, postInterestAsOn, transactionDate); } catch (Exception e) { - Throwable realCause = e; - if (e.getCause() != null) { - realCause = e.getCause(); - } - sb.append("failed to post interest for Savings with id " + savingsAccount.getId() + " with message " - + realCause.getMessage()); + LOG.error("Failed to post interest for Savings with id {}", savingsAccount.getId(), e); + ++errors; } } page++; totalPageSize = savingsAccounts.getTotalPages(); } while (page < totalPageSize); - if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); } + if (errors > 0) { throw new JobExecutionException(errors); } } - @CronTarget(jobName = JobName.UPDATE_SAVINGS_DORMANT_ACCOUNTS) @Override + @CronTarget(jobName = JobName.UPDATE_SAVINGS_DORMANT_ACCOUNTS) public void updateSavingsDormancyStatus() throws JobExecutionException { - final LocalDate tenantLocalDate = DateUtils.getLocalDateOfTenant(); + LocalDate tenantLocalDate = DateUtils.getLocalDateOfTenant(); - final List savingsPendingInactive = this.savingAccountReadPlatformService - .retrieveSavingsIdsPendingInactive(tenantLocalDate); + List savingsPendingInactive = savingAccountReadPlatformService.retrieveSavingsIdsPendingInactive(tenantLocalDate); if(null != savingsPendingInactive && savingsPendingInactive.size() > 0){ for(Long savingsId : savingsPendingInactive){ this.savingsAccountWritePlatformService.setSubStatusInactive(savingsId); } } - final List savingsPendingDormant = this.savingAccountReadPlatformService - .retrieveSavingsIdsPendingDormant(tenantLocalDate); + List savingsPendingDormant = savingAccountReadPlatformService.retrieveSavingsIdsPendingDormant(tenantLocalDate); if(null != savingsPendingDormant && savingsPendingDormant.size() > 0){ for(Long savingsId : savingsPendingDormant){ this.savingsAccountWritePlatformService.setSubStatusDormant(savingsId); } } - final List savingsPendingEscheat = this.savingAccountReadPlatformService - .retrieveSavingsIdsPendingEscheat(tenantLocalDate); + List savingsPendingEscheat = savingAccountReadPlatformService.retrieveSavingsIdsPendingEscheat(tenantLocalDate); if(null != savingsPendingEscheat && savingsPendingEscheat.size() > 0){ for(Long savingsId : savingsPendingEscheat){ this.savingsAccountWritePlatformService.escheat(savingsId); diff --git a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java index 036ab7b65..3f9f60d12 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/scheduledjobs/service/ScheduledJobRunnerServiceImpl.java @@ -62,6 +62,7 @@ import org.springframework.util.CollectionUtils; public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService { private final static Logger logger = LoggerFactory.getLogger(ScheduledJobRunnerServiceImpl.class); + private final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd"); private final DateTimeFormatter formatterWithTime = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); @@ -111,9 +112,9 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService updateSqlBuilder.append("SUM(IFNULL(mr.interest_waived_derived,0)) as interest_waived_derived,"); updateSqlBuilder.append("SUM(IFNULL(mr.interest_writtenoff_derived,0)) as interest_writtenoff_derived,"); updateSqlBuilder - .append("SUM(IFNULL(mr.fee_charges_amount,0)) + IFNULL((select SUM(lc.amount) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_time_enum=1),0) as fee_charges_charged_derived,"); + .append("SUM(IFNULL(mr.fee_charges_amount,0)) + IFNULL((select SUM(lc.amount) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_time_enum=1),0) as fee_charges_charged_derived,"); updateSqlBuilder - .append("SUM(IFNULL(mr.fee_charges_completed_derived,0)) + IFNULL((select SUM(lc.amount_paid_derived) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_time_enum=1),0) as fee_charges_repaid_derived,"); + .append("SUM(IFNULL(mr.fee_charges_completed_derived,0)) + IFNULL((select SUM(lc.amount_paid_derived) from m_loan_charge lc where lc.loan_id=ml.id and lc.is_active=1 and lc.charge_time_enum=1),0) as fee_charges_repaid_derived,"); updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_waived_derived,0)) as fee_charges_waived_derived,"); updateSqlBuilder.append("SUM(IFNULL(mr.fee_charges_writtenoff_derived,0)) as fee_charges_writtenoff_derived,"); updateSqlBuilder.append("SUM(IFNULL(mr.penalty_charges_amount,0)) as penalty_charges_charged_derived,"); @@ -130,45 +131,45 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService updateSqlBuilder.append("m_loan.principal_repaid_derived = x.principal_repaid_derived,"); updateSqlBuilder.append("m_loan.principal_writtenoff_derived = x.principal_writtenoff_derived,"); updateSqlBuilder - .append("m_loan.principal_outstanding_derived = (x.principal_disbursed_derived - (x.principal_repaid_derived + x.principal_writtenoff_derived)),"); + .append("m_loan.principal_outstanding_derived = (x.principal_disbursed_derived - (x.principal_repaid_derived + x.principal_writtenoff_derived)),"); updateSqlBuilder.append("m_loan.interest_charged_derived = x.interest_charged_derived,"); updateSqlBuilder.append("m_loan.interest_repaid_derived = x.interest_repaid_derived,"); updateSqlBuilder.append("m_loan.interest_waived_derived = x.interest_waived_derived,"); updateSqlBuilder.append("m_loan.interest_writtenoff_derived = x.interest_writtenoff_derived,"); updateSqlBuilder - .append("m_loan.interest_outstanding_derived = (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)),"); + .append("m_loan.interest_outstanding_derived = (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)),"); updateSqlBuilder.append("m_loan.fee_charges_charged_derived = x.fee_charges_charged_derived,"); updateSqlBuilder.append("m_loan.fee_charges_repaid_derived = x.fee_charges_repaid_derived,"); updateSqlBuilder.append("m_loan.fee_charges_waived_derived = x.fee_charges_waived_derived,"); updateSqlBuilder.append("m_loan.fee_charges_writtenoff_derived = x.fee_charges_writtenoff_derived,"); updateSqlBuilder - .append("m_loan.fee_charges_outstanding_derived = (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)),"); + .append("m_loan.fee_charges_outstanding_derived = (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)),"); updateSqlBuilder.append("m_loan.penalty_charges_charged_derived = x.penalty_charges_charged_derived,"); updateSqlBuilder.append("m_loan.penalty_charges_repaid_derived = x.penalty_charges_repaid_derived,"); updateSqlBuilder.append("m_loan.penalty_charges_waived_derived = x.penalty_charges_waived_derived,"); updateSqlBuilder.append("m_loan.penalty_charges_writtenoff_derived = x.penalty_charges_writtenoff_derived,"); updateSqlBuilder - .append("m_loan.penalty_charges_outstanding_derived = (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived)),"); + .append("m_loan.penalty_charges_outstanding_derived = (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived)),"); updateSqlBuilder - .append("m_loan.total_expected_repayment_derived = (x.principal_disbursed_derived + x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); + .append("m_loan.total_expected_repayment_derived = (x.principal_disbursed_derived + x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); updateSqlBuilder - .append("m_loan.total_repayment_derived = (x.principal_repaid_derived + x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); + .append("m_loan.total_repayment_derived = (x.principal_repaid_derived + x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); updateSqlBuilder - .append("m_loan.total_expected_costofloan_derived = (x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); + .append("m_loan.total_expected_costofloan_derived = (x.interest_charged_derived + x.fee_charges_charged_derived + x.penalty_charges_charged_derived),"); updateSqlBuilder - .append("m_loan.total_costofloan_derived = (x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); + .append("m_loan.total_costofloan_derived = (x.interest_repaid_derived + x.fee_charges_repaid_derived + x.penalty_charges_repaid_derived),"); updateSqlBuilder - .append("m_loan.total_waived_derived = (x.interest_waived_derived + x.fee_charges_waived_derived + x.penalty_charges_waived_derived),"); + .append("m_loan.total_waived_derived = (x.interest_waived_derived + x.fee_charges_waived_derived + x.penalty_charges_waived_derived),"); updateSqlBuilder - .append("m_loan.total_writtenoff_derived = (x.interest_writtenoff_derived + x.fee_charges_writtenoff_derived + x.penalty_charges_writtenoff_derived),"); + .append("m_loan.total_writtenoff_derived = (x.interest_writtenoff_derived + x.fee_charges_writtenoff_derived + x.penalty_charges_writtenoff_derived),"); updateSqlBuilder.append("m_loan.total_outstanding_derived="); updateSqlBuilder.append(" (x.principal_disbursed_derived - (x.principal_repaid_derived + x.principal_writtenoff_derived)) + "); updateSqlBuilder - .append(" (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)) +"); + .append(" (x.interest_charged_derived - (x.interest_repaid_derived + x.interest_waived_derived + x.interest_writtenoff_derived)) +"); updateSqlBuilder - .append(" (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)) +"); + .append(" (x.fee_charges_charged_derived - (x.fee_charges_repaid_derived + x.fee_charges_waived_derived + x.fee_charges_writtenoff_derived)) +"); updateSqlBuilder - .append(" (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived))"); + .append(" (x.penalty_charges_charged_derived - (x.penalty_charges_repaid_derived + x.penalty_charges_waived_derived + x.penalty_charges_writtenoff_derived))"); final int result = jdbcTemplate.update(updateSqlBuilder.toString()); @@ -187,23 +188,23 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService final StringBuilder updateSqlBuilder = new StringBuilder(900); updateSqlBuilder - .append("INSERT INTO m_loan_paid_in_advance(loan_id, principal_in_advance_derived, interest_in_advance_derived, fee_charges_in_advance_derived, penalty_charges_in_advance_derived, total_in_advance_derived)"); + .append("INSERT INTO m_loan_paid_in_advance(loan_id, principal_in_advance_derived, interest_in_advance_derived, fee_charges_in_advance_derived, penalty_charges_in_advance_derived, total_in_advance_derived)"); updateSqlBuilder.append(" select ml.id as loanId,"); updateSqlBuilder.append(" SUM(ifnull(mr.principal_completed_derived, 0)) as principal_in_advance_derived,"); updateSqlBuilder.append(" SUM(ifnull(mr.interest_completed_derived, 0)) as interest_in_advance_derived,"); updateSqlBuilder.append(" SUM(ifnull(mr.fee_charges_completed_derived, 0)) as fee_charges_in_advance_derived,"); updateSqlBuilder.append(" SUM(ifnull(mr.penalty_charges_completed_derived, 0)) as penalty_charges_in_advance_derived,"); updateSqlBuilder - .append(" (SUM(ifnull(mr.principal_completed_derived, 0)) + SUM(ifnull(mr.interest_completed_derived, 0)) + SUM(ifnull(mr.fee_charges_completed_derived, 0)) + SUM(ifnull(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived"); + .append(" (SUM(ifnull(mr.principal_completed_derived, 0)) + SUM(ifnull(mr.interest_completed_derived, 0)) + SUM(ifnull(mr.fee_charges_completed_derived, 0)) + SUM(ifnull(mr.penalty_charges_completed_derived, 0))) as total_in_advance_derived"); updateSqlBuilder.append(" FROM m_loan ml "); updateSqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id "); updateSqlBuilder.append(" WHERE ml.loan_status_id = 300 "); updateSqlBuilder.append(" and mr.duedate >= CURDATE() "); updateSqlBuilder.append(" GROUP BY ml.id"); updateSqlBuilder - .append(" HAVING (SUM(ifnull(mr.principal_completed_derived, 0)) + SUM(ifnull(mr.interest_completed_derived, 0)) +"); + .append(" HAVING (SUM(ifnull(mr.principal_completed_derived, 0)) + SUM(ifnull(mr.interest_completed_derived, 0)) +"); updateSqlBuilder - .append(" SUM(ifnull(mr.fee_charges_completed_derived, 0)) + SUM(ifnull(mr.penalty_charges_completed_derived, 0))) > 0.0"); + .append(" SUM(ifnull(mr.fee_charges_completed_derived, 0)) + SUM(ifnull(mr.penalty_charges_completed_derived, 0))) > 0.0"); final int result = jdbcTemplate.update(updateSqlBuilder.toString()); @@ -238,31 +239,21 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService @Override @CronTarget(jobName = JobName.PAY_DUE_SAVINGS_CHARGES) public void applyDueChargesForSavings() throws JobExecutionException { - final Collection chargesDueData = this.savingsAccountChargeReadPlatformService - .retrieveChargesWithDue(); - final StringBuilder errorMsg = new StringBuilder(); - + final Collection chargesDueData = this.savingsAccountChargeReadPlatformService.retrieveChargesWithDue(); + int numberOfErrors = 0; for (final SavingsAccountAnnualFeeData savingsAccountReference : chargesDueData) { try { - this.savingsAccountWritePlatformService.applyChargeDue(savingsAccountReference.getId(), - savingsAccountReference.getAccountId()); + this.savingsAccountWritePlatformService.applyChargeDue(savingsAccountReference.getId(), savingsAccountReference.getAccountId()); } catch (final PlatformApiDataValidationException e) { final List errors = e.getErrors(); for (final ApiParameterError error : errors) { - logger.error("Apply Charges due for savings failed for account:" + savingsAccountReference.getAccountNo() - + " with message " + error.getDeveloperMessage()); - errorMsg.append("Apply Charges due for savings failed for account:").append(savingsAccountReference.getAccountNo()) - .append(" with message ").append(error.getDeveloperMessage()); + logger.error("Apply Charges due for savings failed for account {} with message: {}", savingsAccountReference.getAccountNo(), error.getDeveloperMessage(), e); + ++numberOfErrors; } } } - - logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Savings accounts affected by update: " + chargesDueData.size()); - - /* - * throw exception if any charge payment fails. - */ - if (errorMsg.length() > 0) { throw new JobExecutionException(errorMsg.toString()); } + logger.info("{}: Savings accounts affected by update: {}", ThreadLocalContextUtil.getTenant().getName(), chargesDueData.size()); + if (numberOfErrors > 0) { throw new JobExecutionException(numberOfErrors); } } @Transactional @@ -279,7 +270,7 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService resetNPASqlBuilder.append("set loan.is_npa = 0 "); resetNPASqlBuilder.append("where loan.loan_status_id = 300 and mpl.account_moves_out_of_npa_only_on_arrears_completion = 0 "); resetNPASqlBuilder - .append("or (mpl.account_moves_out_of_npa_only_on_arrears_completion = 1 and laa.overdue_since_date_derived is null)"); + .append("or (mpl.account_moves_out_of_npa_only_on_arrears_completion = 1 and laa.overdue_since_date_derived is null)"); jdbcTemplate.update(resetNPASqlBuilder.toString()); @@ -383,65 +374,62 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService @Override @CronTarget(jobName = JobName.POST_DIVIDENTS_FOR_SHARES) public void postDividends() throws JobExecutionException { + int numberOfErrors = 0; List> dividendDetails = this.shareAccountDividendReadPlatformService.retriveDividendDetailsForPostDividents(); - StringBuilder errorMsg = new StringBuilder(); for (Map dividendMap : dividendDetails) { Long id = null ; Long savingsId = null ; - if(dividendMap.get("id") instanceof BigInteger) { //Drizzle is returning BigInteger - id = ((BigInteger)dividendMap.get("id")).longValue() ; - savingsId = ((BigInteger)dividendMap.get("savingsAccountId")).longValue() ; - }else { //MySQL connector is returning Long - id = (Long) dividendMap.get("id") ; - savingsId = (Long) dividendMap.get("savingsAccountId") ; + if (dividendMap.get("id") instanceof BigInteger) { + // Drizzle is returningBigInteger + id = ((BigInteger) dividendMap.get("id")).longValue(); + savingsId = ((BigInteger) dividendMap.get("savingsAccountId")).longValue(); + } else { // MySQL connector is returning Long + id = (Long) dividendMap.get("id"); + savingsId = (Long) dividendMap.get("savingsAccountId"); } try { this.shareAccountSchedularService.postDividend(id, savingsId); } catch (final PlatformApiDataValidationException e) { final List errors = e.getErrors(); for (final ApiParameterError error : errors) { - logger.error("Post Dividends to savings failed for Divident detail Id:" + id + " and savings Id: " + savingsId - + " with message " + error.getDeveloperMessage()); - errorMsg.append("Post Dividends to savings failed for Divident detail Id:").append(id).append(" and savings Id:") - .append(savingsId).append(" with message ").append(error.getDeveloperMessage()); + logger.error("Post Dividends to savings failed due to ApiParameterError for Divident detail Id: {} and savings Id: {} with message: ", id, savingsId, error.getDeveloperMessage(), e); + ++numberOfErrors; } } catch (final Exception e) { - logger.error("Post Dividends to savings failed for Divident detail Id:" + id + " and savings Id: " + savingsId - + " with message " + e.getLocalizedMessage()); - errorMsg.append("Post Dividends to savings failed for Divident detail Id:").append(id).append(" and savings Id:") - .append(savingsId).append(" with message ").append(e.getLocalizedMessage()); + logger.error("Post Dividends to savings failed for Divident detail Id: {} and savings Id: {}", id, savingsId, e); + ++numberOfErrors; } } - if (errorMsg.length() > 0) { throw new JobExecutionException(errorMsg.toString()); } + if (numberOfErrors > 0) { throw new JobExecutionException(numberOfErrors); } } + @Override @CronTarget(jobName = JobName.UPDATE_TRAIL_BALANCE_DETAILS) public void updateTrialBalanceDetails() throws JobExecutionException { final JdbcTemplate jdbcTemplate = new JdbcTemplate(this.dataSourceServiceFactory.determineDataSourceService().retrieveDataSource()); final StringBuilder tbGapSqlBuilder = new StringBuilder(500); tbGapSqlBuilder.append("select distinct(je.transaction_date) ") - .append("from acc_gl_journal_entry je ") - .append("where je.transaction_date > (select IFNULL(MAX(created_date),'2010-01-01') from m_trial_balance)"); + .append("from acc_gl_journal_entry je ") + .append("where je.transaction_date > (select IFNULL(MAX(created_date),'2010-01-01') from m_trial_balance)"); final List tbGaps = jdbcTemplate.queryForList(tbGapSqlBuilder.toString(), Date.class); for(Date tbGap : tbGaps) { LocalDate convDate = new DateTime(tbGap).toLocalDate(); int days = Days.daysBetween(convDate, DateUtils.getLocalDateOfTenant()).getDays(); - if(days < 1) + if(days < 1) { continue; + } final String formattedDate = new SimpleDateFormat("yyyy-MM-dd").format(tbGap); final StringBuilder sqlBuilder = new StringBuilder(600); sqlBuilder.append("Insert Into m_trial_balance(office_id, account_id, Amount, entry_date, created_date,closing_balance) ") - .append("Select je.office_id, je.account_id, sum(if(je.type_enum=1, (-1) * je.amount, je.amount)) ") - .append("as Amount, Date(je.entry_date) as 'Entry_Date', je.transaction_date as 'Created_Date',sum(je.amount) as closing_balance ") - .append("from acc_gl_journal_entry je WHERE je.transaction_date = ? ") - .append("group by je.account_id, je.office_id, je.transaction_date, Date(je.entry_date)"); + .append("Select je.office_id, je.account_id, sum(if(je.type_enum=1, (-1) * je.amount, je.amount)) ") + .append("as Amount, Date(je.entry_date) as 'Entry_Date', je.transaction_date as 'Created_Date',sum(je.amount) as closing_balance ") + .append("from acc_gl_journal_entry je WHERE je.transaction_date = ? ") + .append("group by je.account_id, je.office_id, je.transaction_date, Date(je.entry_date)"); - final int result = jdbcTemplate.update(sqlBuilder.toString(), new Object[] { - formattedDate - }); + final int result = jdbcTemplate.update(sqlBuilder.toString(), formattedDate); logger.info(ThreadLocalContextUtil.getTenant().getName() + ": Results affected by update: " + result); } @@ -459,8 +447,9 @@ public class ScheduledJobRunnerServiceImpl implements ScheduledJobRunnerService List closingBalanceData = jdbcTemplate.queryForList(closingBalanceQuery, new Object[] {officeId, accountId}, BigDecimal.class); List tbRows = this.trialBalanceRepositoryWrapper.findNewByOfficeAndAccount(officeId, accountId); BigDecimal closingBalance = null; - if(!CollectionUtils.isEmpty(closingBalanceData)) + if(!CollectionUtils.isEmpty(closingBalanceData)) { closingBalance = closingBalanceData.get(0); + } if(CollectionUtils.isEmpty(closingBalanceData)) { closingBalance = BigDecimal.ZERO; for(TrialBalance row : tbRows) {