From fd5ef0936009c8382e7a64ef9a675d3bec2b75a0 Mon Sep 17 00:00:00 2001 From: shahrahul1985 Date: Wed, 19 Aug 2015 15:16:00 +0530 Subject: [PATCH] MIFOSX-2148: Make Rounding Mode as configurable throughout Mifos --- .../data/GlobalConfigurationPropertyData.java | 7 +- .../domain/ConfigurationDomainService.java | 2 + .../domain/ConfigurationDomainServiceJpa.java | 12 ++ .../domain/GlobalConfigurationProperty.java | 34 +++-- ...lConfigurationPropertyCannotBeModfied.java | 17 +++ .../ConfigurationReadPlatformServiceImpl.java | 28 ++-- .../organisation/monetary/domain/Money.java | 6 +- .../monetary/domain/MoneyHelper.java | 36 +++++ .../portfolio/loanaccount/domain/Loan.java | 143 +++++++++--------- .../loanaccount/domain/LoanCharge.java | 49 +++--- .../service/GuarantorDomainServiceImpl.java | 32 ++-- .../domain/LoanApplicationTerms.java | 26 ++-- .../service/LoanScheduleAssembler.java | 67 ++++---- ...nReschedulePreviewPlatformServiceImpl.java | 15 +- ...heduleRequestWritePlatformServiceImpl.java | 27 ++-- .../LoanAccrualWritePlatformServiceImpl.java | 3 +- .../savings/domain/SavingsAccountCharge.java | 3 +- .../interest/AnnualCompoundingPeriod.java | 17 ++- .../interest/BiAnnualCompoundingPeriod.java | 17 ++- .../domain/interest/EndOfDayBalance.java | 19 ++- .../interest/MonthlyCompoundingPeriod.java | 17 ++- .../interest/QuarterlyCompoundingPeriod.java | 21 +-- ...WritePlatformServiceJpaRepositoryImpl.java | 7 +- ...WritePlatformServiceJpaRepositoryImpl.java | 10 +- .../V266__add_rounding_mode_configuration.sql | 4 + ...ymentScheduleTransactionProcessorTest.java | 7 +- ...ymentScheduleTransactionProcessorTest.java | 7 +- 27 files changed, 373 insertions(+), 260 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/exception/GlobalConfigurationPropertyCannotBeModfied.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MoneyHelper.java create mode 100644 mifosng-provider/src/main/resources/sql/migrations/core_db/V266__add_rounding_mode_configuration.sql diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/GlobalConfigurationPropertyData.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/GlobalConfigurationPropertyData.java index c7da31a5c..f71f7c804 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/GlobalConfigurationPropertyData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/data/GlobalConfigurationPropertyData.java @@ -21,8 +21,7 @@ public class GlobalConfigurationPropertyData { @SuppressWarnings("unused") private final String description; - public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, - final String description) { + public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, final String description) { this.name = name; this.enabled = enabled; this.value = value; @@ -30,8 +29,8 @@ public class GlobalConfigurationPropertyData { this.description = description; } - public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, - final Long id, final String description) { + public GlobalConfigurationPropertyData(final String name, final boolean enabled, final Long value, final Long id, + final String description) { this.name = name; this.enabled = enabled; this.value = value; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java index dd4f3a490..8149a6b4f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -47,4 +47,6 @@ public interface ConfigurationDomainService { boolean isMeetingMandatoryForJLGLoans(); + int getRoundingMode(); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index add99db69..c8309f37b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -180,4 +180,16 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService return property.isEnabled(); } + public int getRoundingMode() { + final String propertyName = "rounding-mode"; + int defaultValue = 6; // 6 Stands for HALF-EVEN + final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName); + if (property.isEnabled()) { + int value = property.getValue().intValue(); + if (value < 0 || value > 6) { return defaultValue; } + return value; + } + return defaultValue; + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java index d591e1b72..fb8de2be2 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/GlobalConfigurationProperty.java @@ -12,6 +12,8 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Table; +import org.mifosplatform.infrastructure.configuration.exception.GlobalConfigurationPropertyCannotBeModfied; +import org.mifosplatform.infrastructure.configuration.exception.GlobalConfigurationPropertyNotFoundException; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.security.exception.ForcePasswordResetException; import org.springframework.data.jpa.domain.AbstractPersistable; @@ -28,23 +30,28 @@ public class GlobalConfigurationProperty extends AbstractPersistable { @Column(name = "value", nullable = true) private Long value; - + @Column(name = "description", nullable = true) private final String description; + @Column(name = "is_trap_door", nullable = false) + private boolean isTrapDoor; + protected GlobalConfigurationProperty() { this.name = null; this.enabled = false; this.value = null; this.description = null; + this.isTrapDoor = false; } - public GlobalConfigurationProperty(final String name, final boolean enabled, final Long value, - final String description) { + public GlobalConfigurationProperty(final String name, final boolean enabled, final Long value, final String description, + final boolean isTrapDoor) { this.name = name; this.enabled = enabled; this.value = value; this.description = description; + this.isTrapDoor = isTrapDoor; } public boolean isEnabled() { @@ -65,6 +72,8 @@ public class GlobalConfigurationProperty extends AbstractPersistable { final Map actualChanges = new LinkedHashMap<>(7); + if (this.isTrapDoor == true) { throw new GlobalConfigurationPropertyCannotBeModfied(this.getId()); } + final String enabledParamName = "enabled"; if (command.isChangeInBooleanParameterNamed(enabledParamName, this.enabled)) { final Boolean newValue = command.booleanPrimitiveValueOfParameterNamed(enabledParamName); @@ -79,22 +88,19 @@ public class GlobalConfigurationProperty extends AbstractPersistable { actualChanges.put(valueParamName, newValue); this.value = newValue; } - + final String passwordPropertyName = "force-password-reset-days"; - if(this.name.equalsIgnoreCase(passwordPropertyName)){ - if(this.enabled == true && command.hasParameter(valueParamName) && this.value == 0 || - this.enabled == true && !command.hasParameter(valueParamName) && previousValue == 0){ - throw new ForcePasswordResetException(); - } - } - + if (this.name.equalsIgnoreCase(passwordPropertyName)) { + if (this.enabled == true && command.hasParameter(valueParamName) && this.value == 0 || this.enabled == true + && !command.hasParameter(valueParamName) && previousValue == 0) { throw new ForcePasswordResetException(); } + } + return actualChanges; } - public static GlobalConfigurationProperty newSurveyConfiguration(final String name) - { - return new GlobalConfigurationProperty(name, false, null, null); + public static GlobalConfigurationProperty newSurveyConfiguration(final String name) { + return new GlobalConfigurationProperty(name, false, null, null, false); } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/exception/GlobalConfigurationPropertyCannotBeModfied.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/exception/GlobalConfigurationPropertyCannotBeModfied.java new file mode 100644 index 000000000..f35a62c15 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/exception/GlobalConfigurationPropertyCannotBeModfied.java @@ -0,0 +1,17 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.configuration.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException; +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +public class GlobalConfigurationPropertyCannotBeModfied extends AbstractPlatformDomainRuleException{ + + public GlobalConfigurationPropertyCannotBeModfied(final Long configId) { + super("error.msg.configuration.id.not.modifiable", "Configuration identifier `" + configId + "` cannot be modified", new Object[] {configId}); + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java index d124443b6..ccf83f165 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/service/ConfigurationReadPlatformServiceImpl.java @@ -39,13 +39,14 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl this.context.authenticatedUser(); - String sql = "SELECT c.id, c.name, c.enabled, c.value, c.description FROM c_configuration c "; + String sql = "SELECT c.id, c.name, c.enabled, c.value, c.description, c.is_trap_door FROM c_configuration c "; - if(survey) - { - sql += " JOIN x_registered_table on x_registered_table.registered_table_name = c.name "; - sql += " WHERE x_registered_table.category ="+DataTableApiConstant.CATEGORY_PPI; + if (survey) { + sql += " JOIN x_registered_table on x_registered_table.registered_table_name = c.name "; + sql += " WHERE x_registered_table.category =" + DataTableApiConstant.CATEGORY_PPI + " and c.is_trap_door=0"; + } else { + sql += " WHERE c.is_trap_door=0"; } sql += " order by c.id"; @@ -53,33 +54,32 @@ public class ConfigurationReadPlatformServiceImpl implements ConfigurationReadPl return new GlobalConfigurationData(globalConfiguration); } - + @Override public GlobalConfigurationPropertyData retrieveGlobalConfiguration(Long configId) { this.context.authenticatedUser(); - final String sql = "SELECT c.id, c.name, c.enabled, c.value, c.description FROM " - + "c_configuration c where c.id=? order by c.id"; - final GlobalConfigurationPropertyData globalConfiguration = this.jdbcTemplate.queryForObject(sql, this.rm, new Object[] {configId}); + final String sql = "SELECT c.id, c.name, c.enabled, c.value, c.description, c.is_trap_door FROM " + + "c_configuration c where c.id=? and c.is_trap_door = 0 order by c.id"; + final GlobalConfigurationPropertyData globalConfiguration = this.jdbcTemplate.queryForObject(sql, this.rm, + new Object[] { configId }); return globalConfiguration; } - - private static final class GlobalConfigurationRowMapper implements RowMapper { - @Override - public GlobalConfigurationPropertyData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + public GlobalConfigurationPropertyData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) + throws SQLException { final String name = rs.getString("name"); final boolean enabled = rs.getBoolean("enabled"); final Long value = rs.getLong("value"); final String description = rs.getString("description"); final Long id = rs.getLong("id"); - + final boolean isTrapDoor = rs.getBoolean("is_trap_door"); return new GlobalConfigurationPropertyData(name, enabled, value, id, description); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java index 076201f05..39c3a6266 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/Money.java @@ -76,7 +76,7 @@ public class Money implements Comparable { final double existingVal = amountScaled.doubleValue(); amountScaled = BigDecimal.valueOf(roundToMultiplesOf(existingVal, inMultiplesOf)); } - this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, RoundingMode.HALF_EVEN); + this.amount = amountScaled.setScale(this.currencyDigitsAfterDecimal, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } public static double roundToMultiplesOf(final double existingVal, final Integer inMultiplesOf) { @@ -227,8 +227,8 @@ public class Money implements Comparable { @Override public int compareTo(final Money other) { final Money otherMoney = other; - if (this.currencyCode.equals(otherMoney.currencyCode) == false) { throw new UnsupportedOperationException( - "currencies arent different"); } + if (this.currencyCode + .equals(otherMoney.currencyCode) == false) { throw new UnsupportedOperationException("currencies arent different"); } return this.amount.compareTo(otherMoney.amount); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MoneyHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MoneyHelper.java new file mode 100644 index 000000000..fa4644aac --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/organisation/monetary/domain/MoneyHelper.java @@ -0,0 +1,36 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.organisation.monetary.domain; + +import javax.annotation.PostConstruct; + +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class MoneyHelper { + + private static int roundingModeIntValue = -1; + private static ConfigurationDomainService staticConfigurationDomainService; + + @Autowired + private ConfigurationDomainService configurationDomainService; + + @PostConstruct + public void someFunction () { + staticConfigurationDomainService = configurationDomainService; + } + + + public static int getRoundingMode() { + if (roundingModeIntValue == -1) { + roundingModeIntValue = staticConfigurationDomainService.getRoundingMode(); + } + return roundingModeIntValue; + } + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java index ccaeb06a7..18c996b75 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/Loan.java @@ -21,6 +21,7 @@ import org.joda.time.LocalDateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.infrastructure.codes.domain.CodeValue; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; @@ -33,6 +34,7 @@ import org.mifosplatform.organisation.monetary.data.CurrencyData; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; @@ -64,13 +66,16 @@ import org.mifosplatform.portfolio.loanproduct.LoanProductConstants; import org.mifosplatform.portfolio.loanproduct.domain.*; import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations; import org.mifosplatform.useradministration.domain.AppUser; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.domain.AbstractPersistable; +import org.springframework.stereotype.Component; import com.google.gson.JsonArray; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; @Entity +@Component @Table(name = "m_loan", uniqueConstraints = { @UniqueConstraint(columnNames = { "account_no" }, name = "loan_account_no_UNIQUE"), @UniqueConstraint(columnNames = { "external_id" }, name = "loan_externalid_UNIQUE") }) public class Loan extends AbstractPersistable { @@ -573,8 +578,8 @@ public class Loan extends AbstractPersistable { final LoanTransaction applyLoanChargeTransaction = LoanTransaction.accrueLoanCharge(this, getOffice(), chargeAmount, transactionDate, feeCharges, penaltyCharges, DateUtils.getLocalDateTimeOfTenant(), currentUser); Integer installmentNumber = null; - final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(applyLoanChargeTransaction, loanCharge, loanCharge.getAmount( - getCurrency()).getAmount(), installmentNumber); + final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(applyLoanChargeTransaction, loanCharge, + loanCharge.getAmount(getCurrency()).getAmount(), installmentNumber); applyLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy); this.loanTransactions.add(applyLoanChargeTransaction); return applyLoanChargeTransaction; @@ -583,8 +588,8 @@ public class Loan extends AbstractPersistable { private void handleChargePaidTransaction(final LoanCharge charge, final LoanTransaction chargesPayment, final LoanLifecycleStateMachine loanLifecycleStateMachine, final Integer installmentNumber) { chargesPayment.updateLoan(this); - final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, chargesPayment.getAmount(getCurrency()) - .getAmount(), installmentNumber); + final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(chargesPayment, charge, + chargesPayment.getAmount(getCurrency()).getAmount(), installmentNumber); chargesPayment.getLoanChargesPaid().add(loanChargePaidBy); this.loanTransactions.add(chargesPayment); final LoanStatus statusEnum = loanLifecycleStateMachine.transition(LoanEvent.LOAN_CHARGE_PAYMENT, @@ -622,7 +627,8 @@ public class Loan extends AbstractPersistable { private void validateLoanChargeIsNotWaived(final LoanCharge loanCharge) { if (loanCharge.isWaived()) { final String defaultUserMessage = "This loan charge cannot be removed as the charge as already been waived."; - throw new LoanChargeCannotBeAddedException("loanCharge", "loanCharge.is.waived", defaultUserMessage, getId(), loanCharge.name()); + throw new LoanChargeCannotBeAddedException("loanCharge", "loanCharge.is.waived", defaultUserMessage, getId(), + loanCharge.name()); } } @@ -862,11 +868,10 @@ public class Loan extends AbstractPersistable { existingTransactionIds.addAll(findExistingTransactionIds()); existingReversedTransactionIds.addAll(findExistingReversedTransactionIds()); - final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, getOffice(), amountWaived, - transactionDate, feeChargesWaived, penaltyChargesWaived, unrecognizedIncome, DateUtils.getLocalDateTimeOfTenant(), - currentUser); - final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge, waiveLoanChargeTransaction - .getAmount(getCurrency()).getAmount(), loanInstallmentNumber); + final LoanTransaction waiveLoanChargeTransaction = LoanTransaction.waiveLoanCharge(this, getOffice(), amountWaived, transactionDate, + feeChargesWaived, penaltyChargesWaived, unrecognizedIncome, DateUtils.getLocalDateTimeOfTenant(), currentUser); + final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(waiveLoanChargeTransaction, loanCharge, + waiveLoanChargeTransaction.getAmount(getCurrency()).getAmount(), loanInstallmentNumber); waiveLoanChargeTransaction.getLoanChargesPaid().add(loanChargePaidBy); this.loanTransactions.add(waiveLoanChargeTransaction); @@ -1409,13 +1414,13 @@ public class Loan extends AbstractPersistable { } if (this.loanInterestRecalculationDetails == null) { actualChanges.put(LoanProductConstants.isInterestRecalculationEnabledParameterName, true); - this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom(this.loanProduct - .getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), this.loanProduct - .getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), this.loanProduct - .getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), this.loanProduct - .getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate, this.loanProduct - .getProductInterestRecalculationDetails().getCompoundingFrequencyType().getValue(), this.loanProduct - .getProductInterestRecalculationDetails().getCompoundingInterval(), compoundingFrequencyDate); + this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom( + this.loanProduct.getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), + this.loanProduct.getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), + this.loanProduct.getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), + this.loanProduct.getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate, + this.loanProduct.getProductInterestRecalculationDetails().getCompoundingFrequencyType().getValue(), + this.loanProduct.getProductInterestRecalculationDetails().getCompoundingInterval(), compoundingFrequencyDate); this.loanInterestRecalculationDetails.updateLoan(this); } else { @@ -1549,9 +1554,8 @@ public class Loan extends AbstractPersistable { expectedDisbursementDate = date.toDate(); } - if (jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive() - && StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName) - .getAsString()))) { + if (jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive() && StringUtils + .isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) { principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName) .getAsBigDecimal(); } @@ -1694,7 +1698,8 @@ public class Loan extends AbstractPersistable { if (this.client != null && this.client.isActivatedAfter(submittedOn)) { final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date."; - throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, submittedOn); + throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, + submittedOn); } validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_CREATED, submittedOn); @@ -1737,13 +1742,13 @@ public class Loan extends AbstractPersistable { if (recalculationCompoundingFrequencyDate != null) { compoundingFrequencyDate = recalculationCompoundingFrequencyDate.toDate(); } - this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom(this.loanProduct - .getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), this.loanProduct - .getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), this.loanProduct - .getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), this.loanProduct - .getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate, this.loanProduct - .getProductInterestRecalculationDetails().getCompoundingFrequencyType().getValue(), this.loanProduct - .getProductInterestRecalculationDetails().getCompoundingInterval(), compoundingFrequencyDate); + this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom( + this.loanProduct.getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), + this.loanProduct.getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), + this.loanProduct.getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), + this.loanProduct.getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate, + this.loanProduct.getProductInterestRecalculationDetails().getCompoundingFrequencyType().getValue(), + this.loanProduct.getProductInterestRecalculationDetails().getCompoundingInterval(), compoundingFrequencyDate); this.loanInterestRecalculationDetails.updateLoan(this); } @@ -2228,7 +2233,8 @@ public class Loan extends AbstractPersistable { if (date != null) { for (LoanDisbursementDetails disbursementDetail : this.disbursementDetails) { if (disbursementDetail.actualDisbursementDate() != null) { - if (disbursementDetail.actualDisbursementDate().after(date) || disbursementDetail.actualDisbursementDate().equals(date)) { + if (disbursementDetail.actualDisbursementDate().after(date) + || disbursementDetail.actualDisbursementDate().equals(date)) { date = disbursementDetail.actualDisbursementDate(); details = disbursementDetail; } @@ -2317,7 +2323,7 @@ public class Loan extends AbstractPersistable { final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); final LoanScheduleGenerator loanScheduleGenerator = scheduleGeneratorDTO.getLoanScheduleFactory().create(interestMethod); - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(8, roundingMode); final Integer loanTermFrequency = this.termFrequency; @@ -2731,8 +2737,8 @@ public class Loan extends AbstractPersistable { final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory .determineProcessor(this.transactionProcessingStrategy); - final LoanRepaymentScheduleInstallment currentInstallment = fetchLoanRepaymentScheduleInstallment(loanTransaction - .getTransactionDate()); + final LoanRepaymentScheduleInstallment currentInstallment = fetchLoanRepaymentScheduleInstallment( + loanTransaction.getTransactionDate()); boolean reprocess = true; if (isTransactionChronologicallyLatest && adjustedTransaction == null @@ -2750,8 +2756,8 @@ public class Loan extends AbstractPersistable { if (currentInstallment == null || currentInstallment.isNotFullyPaidOff()) { reprocess = true; } else { - final LoanRepaymentScheduleInstallment nextInstallment = fetchRepaymentScheduleInstallment(currentInstallment - .getInstallmentNumber() + 1); + final LoanRepaymentScheduleInstallment nextInstallment = fetchRepaymentScheduleInstallment( + currentInstallment.getInstallmentNumber() + 1); if (nextInstallment != null && nextInstallment.getTotalPaidInAdvance(getCurrency()).isGreaterThanZero()) { reprocess = true; } @@ -2939,7 +2945,8 @@ public class Loan extends AbstractPersistable { return isAfterLatRepayment; } - private boolean isChronologicallyLatestTransaction(final LoanTransaction loanTransaction, final List loanTransactions) { + private boolean isChronologicallyLatestTransaction(final LoanTransaction loanTransaction, + final List loanTransactions) { boolean isChronologicallyLatestRepaymentOrWaiver = true; @@ -3066,8 +3073,8 @@ public class Loan extends AbstractPersistable { return changedTransactionDetail; } - public ChangedTransactionDetail undoWrittenOff(final List existingTransactionIds, - final List existingReversedTransactionIds, final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { + public ChangedTransactionDetail undoWrittenOff(final List existingTransactionIds, final List existingReversedTransactionIds, + final ScheduleGeneratorDTO scheduleGeneratorDTO, final AppUser currentUser) { validateAccountStatus(LoanEvent.WRITE_OFF_OUTSTANDING_UNDO); existingTransactionIds.addAll(findExistingTransactionIds()); @@ -3710,8 +3717,8 @@ public class Loan extends AbstractPersistable { final String errorMessage = "The Loan officer Unassign date(" + unassignDate + ") cannot be before its assignment date (" + latestHistoryRecord.getStartDate() + ")."; - throw new LoanOfficerUnassignmentDateException("cannot.be.before.assignment.date", errorMessage, getId(), getLoanOfficer() - .getId(), latestHistoryRecord.getStartDate(), unassignDate); + throw new LoanOfficerUnassignmentDateException("cannot.be.before.assignment.date", errorMessage, getId(), + getLoanOfficer().getId(), latestHistoryRecord.getStartDate(), unassignDate); } else if (unassignDate.isAfter(today)) { @@ -3986,8 +3993,8 @@ public class Loan extends AbstractPersistable { loanRepaymentInterval, tmpFromDate); if (newRepaymentDate.isAfter(maxDateLimitForNewRepayment)) { - newRepaymentDate = CalendarUtils.getNextRepaymentMeetingDate(recuringRule, seedDate, tmpFromDate, - loanRepaymentInterval, frequency, workingDays); + newRepaymentDate = CalendarUtils.getNextRepaymentMeetingDate(recuringRule, seedDate, tmpFromDate, loanRepaymentInterval, + frequency, workingDays); } if (isHolidayEnabled) { @@ -4240,7 +4247,8 @@ public class Loan extends AbstractPersistable { } private void validateActivityNotBeforeLastTransactionDate(final LoanEvent event, final LocalDate activityDate) { - if (!(this.repaymentScheduleDetail().isInterestRecalculationEnabled() || this.loanProduct().isHoldGuaranteeFundsEnabled())) { return; } + if (!(this.repaymentScheduleDetail().isInterestRecalculationEnabled() + || this.loanProduct().isHoldGuaranteeFundsEnabled())) { return; } LocalDate lastTransactionDate = getLastUserTransactionDate(); final LocalDate clientOfficeJoiningDate = this.client.getOfficeJoiningLocalDate(); if (lastTransactionDate.isAfter(activityDate)) { @@ -4330,8 +4338,8 @@ public class Loan extends AbstractPersistable { case LOAN_APPROVED: if (!isSubmittedAndPendingApproval()) { final String defaultUserMessage = "Loan Account Approval is not allowed. Loan Account is not in submitted and pending approval state."; - final ApiParameterError error = ApiParameterError.generalError( - "error.msg.loan.approve.account.is.not.submitted.and.pending.state", defaultUserMessage); + final ApiParameterError error = ApiParameterError + .generalError("error.msg.loan.approve.account.is.not.submitted.and.pending.state", defaultUserMessage); dataValidationErrors.add(error); } break; @@ -4347,8 +4355,8 @@ public class Loan extends AbstractPersistable { if ((!(isApproved() && isNotDisbursed()) && !this.loanProduct.isMultiDisburseLoan()) || (this.loanProduct.isMultiDisburseLoan() && !isAllTranchesNotDisbursed())) { final String defaultUserMessage = "Loan Disbursal is not allowed. Loan Account is not in approved and not disbursed state."; - final ApiParameterError error = ApiParameterError.generalError( - "error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage); + final ApiParameterError error = ApiParameterError + .generalError("error.msg.loan.disbursal.account.is.not.approve.not.disbursed.state", defaultUserMessage); dataValidationErrors.add(error); } break; @@ -4363,24 +4371,24 @@ public class Loan extends AbstractPersistable { case LOAN_REPAYMENT_OR_WAIVER: if (!isOpen()) { final String defaultUserMessage = "Loan Repayment or Waiver is not allowed. Loan Account is not active."; - final ApiParameterError error = ApiParameterError.generalError( - "error.msg.loan.repayment.or.waiver.account.is.not.active", defaultUserMessage); + final ApiParameterError error = ApiParameterError + .generalError("error.msg.loan.repayment.or.waiver.account.is.not.active", defaultUserMessage); dataValidationErrors.add(error); } break; case LOAN_REJECTED: if (!isSubmittedAndPendingApproval()) { final String defaultUserMessage = "Loan application cannot be rejected. Loan Account is not in Submitted and Pending approval state."; - final ApiParameterError error = ApiParameterError.generalError( - "error.msg.loan.reject.account.is.not.submitted.pending.approval.state", defaultUserMessage); + final ApiParameterError error = ApiParameterError + .generalError("error.msg.loan.reject.account.is.not.submitted.pending.approval.state", defaultUserMessage); dataValidationErrors.add(error); } break; case LOAN_WITHDRAWN: if (!isSubmittedAndPendingApproval()) { final String defaultUserMessage = "Loan application cannot be withdrawn. Loan Account is not in Submitted and Pending approval state."; - final ApiParameterError error = ApiParameterError.generalError( - "error.msg.loan.withdrawn.account.is.not.submitted.pending.approval.state", defaultUserMessage); + final ApiParameterError error = ApiParameterError + .generalError("error.msg.loan.withdrawn.account.is.not.submitted.pending.approval.state", defaultUserMessage); dataValidationErrors.add(error); } break; @@ -4395,8 +4403,8 @@ public class Loan extends AbstractPersistable { case WRITE_OFF_OUTSTANDING_UNDO: if (!isClosedWrittenOff()) { final String defaultUserMessage = "Loan Undo Written off is not allowed. Loan Account is not Written off."; - final ApiParameterError error = ApiParameterError.generalError( - "error.msg.loan.undo.writtenoff.account.is.not.written.off", defaultUserMessage); + final ApiParameterError error = ApiParameterError + .generalError("error.msg.loan.undo.writtenoff.account.is.not.written.off", defaultUserMessage); dataValidationErrors.add(error); } break; @@ -4421,8 +4429,8 @@ public class Loan extends AbstractPersistable { case LOAN_EDIT_MULTI_DISBURSE_DATE: if (isClosed()) { final String defaultUserMessage = "Edit disbursement is not allowed. Loan Account is not active."; - final ApiParameterError error = ApiParameterError.generalError( - "error.msg.loan.edit.disbursement.account.is.not.active", defaultUserMessage); + final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.edit.disbursement.account.is.not.active", + defaultUserMessage); dataValidationErrors.add(error); } break; @@ -4674,7 +4682,7 @@ public class Loan extends AbstractPersistable { final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); final LoanScheduleGenerator loanScheduleGenerator = generatorDTO.getLoanScheduleFactory().create(interestMethod); - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(8, roundingMode); final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO.getApplicationCurrency(), @@ -4719,12 +4727,11 @@ public class Loan extends AbstractPersistable { RecalculationFrequencyType compoundingFrequencyType = this.loanInterestRecalculationDetails().getCompoundingFrequencyType(); final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, - loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), - calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail, - this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, disbursementData, this.maxOutstandingLoanBalance, - loanVariationTermsData, getInterestChargedFromDate(), this.loanInterestRecalculationDetails, - calendarInstanceForInterestRecalculation, recalculationFrequencyType, compoundingCalendarInstance, - compoundingFrequencyType, this.loanProduct.getPrincipalThresholdForLastInstallment(), + loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), calculatedRepaymentsStartingFromDate, + getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount, + disbursementData, this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(), + this.loanInterestRecalculationDetails, calendarInstanceForInterestRecalculation, recalculationFrequencyType, + compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), this.loanProduct.preCloseInterestCalculationStrategy()); return loanApplicationTerms; } @@ -4738,7 +4745,7 @@ public class Loan extends AbstractPersistable { final InterestMethod interestMethod = this.loanRepaymentScheduleDetail.getInterestMethod(); final LoanScheduleGenerator loanScheduleGenerator = loanScheduleFactory.create(interestMethod); - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(8, roundingMode); final Integer loanTermFrequency = this.termFrequency; @@ -4988,8 +4995,8 @@ public class Loan extends AbstractPersistable { final List disbursementData = getDisbursmentData(); final List loanVariationTermsData = new ArrayList(); - LoanTermVariationsData data = new LoanTermVariationsData(null, - LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount); + LoanTermVariationsData data = new LoanTermVariationsData(null, LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), + expectedDisbursementDate, emiAmount); loanVariationTermsData.add(data); RecalculationFrequencyType recalculationFrequencyType = null; @@ -5131,8 +5138,8 @@ public class Loan extends AbstractPersistable { private void validateRefundDateIsAfterLastRepayment(final LocalDate refundTransactionDate) { final LocalDate possibleNextRefundDate = possibleNextRefundDate(); - if (possibleNextRefundDate == null || refundTransactionDate.isBefore(possibleNextRefundDate)) { throw new InvalidRefundDateException( - refundTransactionDate.toString()); } + if (possibleNextRefundDate == null || refundTransactionDate + .isBefore(possibleNextRefundDate)) { throw new InvalidRefundDateException(refundTransactionDate.toString()); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java index c2cbc4f67..f7a255e2f 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/domain/LoanCharge.java @@ -37,6 +37,7 @@ import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType; import org.mifosplatform.portfolio.charge.domain.ChargePaymentMode; @@ -143,8 +144,8 @@ public class LoanCharge extends AbstractPersistable { break; case PERCENT_OF_AMOUNT_AND_INTEREST: if (command.hasParameter("principal") && command.hasParameter("interest")) { - amountPercentageAppliedTo = command.bigDecimalValueOfParameterNamed("principal").add( - command.bigDecimalValueOfParameterNamed("interest")); + amountPercentageAppliedTo = command.bigDecimalValueOfParameterNamed("principal") + .add(command.bigDecimalValueOfParameterNamed("interest")); } else { amountPercentageAppliedTo = loan.getPrincpal().getAmount().add(loan.getTotalInterest()); } @@ -552,7 +553,7 @@ public class LoanCharge extends AbstractPersistable { BigDecimal percentageOf = BigDecimal.ZERO; if (isGreaterThanZero(value)) { - final MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(8, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); final BigDecimal multiplicand = percentage.divide(BigDecimal.valueOf(100l), mc); percentageOf = value.multiply(multiplicand, mc); } @@ -763,9 +764,8 @@ public class LoanCharge extends AbstractPersistable { public LoanInstallmentCharge getUnpaidInstallmentLoanCharge() { LoanInstallmentCharge unpaidChargePerInstallment = null; for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { - if (loanChargePerInstallment.isPending() - && (unpaidChargePerInstallment == null || unpaidChargePerInstallment.getRepaymentInstallment().getDueDate() - .isAfter(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { + if (loanChargePerInstallment.isPending() && (unpaidChargePerInstallment == null || unpaidChargePerInstallment + .getRepaymentInstallment().getDueDate().isAfter(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { unpaidChargePerInstallment = loanChargePerInstallment; } } @@ -781,7 +781,8 @@ public class LoanCharge extends AbstractPersistable { public LoanInstallmentCharge getInstallmentLoanCharge(final Integer installmentNumber) { for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { - if (installmentNumber.equals(loanChargePerInstallment.getRepaymentInstallment().getInstallmentNumber().intValue())) { return loanChargePerInstallment; } + if (installmentNumber.equals(loanChargePerInstallment.getRepaymentInstallment().getInstallmentNumber() + .intValue())) { return loanChargePerInstallment; } } return null; } @@ -883,52 +884,52 @@ public class LoanCharge extends AbstractPersistable { public LoanOverdueInstallmentCharge getOverdueInstallmentCharge() { return this.overdueInstallmentCharge; } - - public Money undoPaidOrPartiallyAmountBy(final Money incrementBy,final Integer installmentNumber, final Money feeAmount) { + + public Money undoPaidOrPartiallyAmountBy(final Money incrementBy, final Integer installmentNumber, final Money feeAmount) { Money processAmount = Money.zero(incrementBy.getCurrency()); - if(isInstalmentFee()){ - if(installmentNumber == null){ - processAmount = getLastPaidOrPartiallyPaidInstallmentLoanCharge(incrementBy.getCurrency()).undoPaidAmountBy(incrementBy, feeAmount); - }else{ + if (isInstalmentFee()) { + if (installmentNumber == null) { + processAmount = getLastPaidOrPartiallyPaidInstallmentLoanCharge(incrementBy.getCurrency()).undoPaidAmountBy(incrementBy, + feeAmount); + } else { processAmount = getInstallmentLoanCharge(installmentNumber).undoPaidAmountBy(incrementBy, feeAmount); } - }else{ + } else { processAmount = incrementBy; } Money amountPaidToDate = Money.of(processAmount.getCurrency(), this.amountPaid); - + Money amountDeductedOnThisCharge = Money.zero(processAmount.getCurrency()); if (processAmount.isGreaterThanOrEqualTo(amountPaidToDate)) { - amountDeductedOnThisCharge = amountPaidToDate; + amountDeductedOnThisCharge = amountPaidToDate; amountPaidToDate = Money.zero(processAmount.getCurrency()); this.amountPaid = amountPaidToDate.getAmount(); this.amountOutstanding = this.amount; this.paid = false; } else { - amountDeductedOnThisCharge = processAmount; + amountDeductedOnThisCharge = processAmount; amountPaidToDate = amountPaidToDate.minus(processAmount); this.amountPaid = amountPaidToDate.getAmount(); this.amountOutstanding = calculateAmountOutstanding(incrementBy.getCurrency()); } return amountDeductedOnThisCharge; } - + public LoanInstallmentCharge getLastPaidOrPartiallyPaidInstallmentLoanCharge(MonetaryCurrency currency) { LoanInstallmentCharge paidChargePerInstallment = null; for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { - Money outstanding = Money.of(currency, loanChargePerInstallment.getAmountOutstanding()); - final boolean partiallyPaid = outstanding.isGreaterThanZero() && outstanding.isLessThan(loanChargePerInstallment.getAmount(currency)); - if ((partiallyPaid || loanChargePerInstallment.isPaid()) - && (paidChargePerInstallment == null || paidChargePerInstallment.getRepaymentInstallment().getDueDate() - .isBefore(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { + Money outstanding = Money.of(currency, loanChargePerInstallment.getAmountOutstanding()); + final boolean partiallyPaid = outstanding.isGreaterThanZero() + && outstanding.isLessThan(loanChargePerInstallment.getAmount(currency)); + if ((partiallyPaid || loanChargePerInstallment.isPaid()) && (paidChargePerInstallment == null || paidChargePerInstallment + .getRepaymentInstallment().getDueDate().isBefore(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { paidChargePerInstallment = loanChargePerInstallment; } } return paidChargePerInstallment; } - public Loan getLoan() { return this.loan; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java index f56caa96d..53ff34026 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/guarantor/service/GuarantorDomainServiceImpl.java @@ -21,6 +21,7 @@ import org.joda.time.format.DateTimeFormatter; import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.account.PortfolioAccountType; import org.mifosplatform.portfolio.account.data.AccountTransferDTO; import org.mifosplatform.portfolio.account.domain.AccountTransferDetails; @@ -59,7 +60,7 @@ public class GuarantorDomainServiceImpl implements GuarantorDomainService { private final BusinessEventNotifierService businessEventNotifierService; private final DepositAccountOnHoldTransactionRepository depositAccountOnHoldTransactionRepository; private final Map releaseLoanIds = new HashMap<>(2); - private final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + @Autowired public GuarantorDomainServiceImpl(final GuarantorRepository guarantorRepository, @@ -101,8 +102,8 @@ public class GuarantorDomainServiceImpl implements GuarantorDomainService { final List existGuarantorList = this.guarantorRepository.findByLoan(loan); BigDecimal mandatoryAmount = principal.multiply(guaranteeData.getMandatoryGuarantee()).divide(BigDecimal.valueOf(100)); BigDecimal minSelfAmount = principal.multiply(guaranteeData.getMinimumGuaranteeFromOwnFunds()).divide(BigDecimal.valueOf(100)); - BigDecimal minExtGuarantee = principal.multiply(guaranteeData.getMinimumGuaranteeFromGuarantor()).divide( - BigDecimal.valueOf(100)); + BigDecimal minExtGuarantee = principal.multiply(guaranteeData.getMinimumGuaranteeFromGuarantor()) + .divide(BigDecimal.valueOf(100)); BigDecimal actualAmount = BigDecimal.ZERO; BigDecimal actualSelfAmount = BigDecimal.ZERO; @@ -113,11 +114,11 @@ public class GuarantorDomainServiceImpl implements GuarantorDomainService { if (guarantorFundingDetails.getStatus().isActive() || guarantorFundingDetails.getStatus().isWithdrawn() || guarantorFundingDetails.getStatus().isCompleted()) { if (guarantor.isSelfGuarantee()) { - actualSelfAmount = actualSelfAmount.add(guarantorFundingDetails.getAmount()).subtract( - guarantorFundingDetails.getAmountTransfered()); + actualSelfAmount = actualSelfAmount.add(guarantorFundingDetails.getAmount()) + .subtract(guarantorFundingDetails.getAmountTransfered()); } else { - actualExtGuarantee = actualExtGuarantee.add(guarantorFundingDetails.getAmount()).subtract( - guarantorFundingDetails.getAmountTransfered()); + actualExtGuarantee = actualExtGuarantee.add(guarantorFundingDetails.getAmount()) + .subtract(guarantorFundingDetails.getAmountTransfered()); } } } @@ -238,12 +239,13 @@ public class GuarantorDomainServiceImpl implements GuarantorDomainService { BigDecimal remainingAmount = guarantorFundingDetails.getAmountRemaining(); if (loan.getGuaranteeAmount().compareTo(loan.getPrincpal().getAmount()) == 1) { remainingAmount = remainingAmount.multiply(loan.getPrincpal().getAmount()).divide(loan.getGuaranteeAmount(), - roundingMode); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } AccountTransferDTO accountTransferDTO = new AccountTransferDTO(transactionDate, remainingAmount, fromAccountType, toAccountType, fromAccountId, toAccountId, description, locale, fmt, paymentDetail, fromTransferType, toTransferType, chargeId, loanInstallmentNumber, transferType, accountTransferDetails, noteText, - txnExternalId, loan, toSavingsAccount, fromSavingsAccount, isRegularTransaction, isExceptionForBalanceCheck); + txnExternalId, loan, toSavingsAccount, fromSavingsAccount, isRegularTransaction, + isExceptionForBalanceCheck); transferAmount(accountTransferDTO); } finally { releaseLoanIds.remove(loanId); @@ -378,16 +380,16 @@ public class GuarantorDomainServiceImpl implements GuarantorDomainService { BigDecimal amountForRelease = loanTransaction.getPrincipalPortion(); BigDecimal totalGuaranteeAmount = loan.getGuaranteeAmount(); BigDecimal principal = loan.getPrincpal().getAmount(); - if((amountForRelease!=null)&&(totalGuaranteeAmount!=null)) - { - amountForRelease = amountForRelease.multiply(totalGuaranteeAmount).divide(principal,this.roundingMode); + if ((amountForRelease != null) && (totalGuaranteeAmount != null)) { + amountForRelease = amountForRelease.multiply(totalGuaranteeAmount).divide(principal, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); List accountOnHoldTransactions = new ArrayList<>(); BigDecimal amountLeft = calculateAndRelaseGuarantorFunds(externalGuarantorList, guarantorGuarantee, amountForRelease, - loanTransaction, accountOnHoldTransactions); + loanTransaction, accountOnHoldTransactions); if (amountLeft.compareTo(BigDecimal.ZERO) == 1) { - calculateAndRelaseGuarantorFunds(selfGuarantorList, selfGuarantee, amountLeft, loanTransaction, accountOnHoldTransactions); + calculateAndRelaseGuarantorFunds(selfGuarantorList, selfGuarantee, amountLeft, loanTransaction, + accountOnHoldTransactions); externalGuarantorList.addAll(selfGuarantorList); } @@ -465,7 +467,7 @@ public class GuarantorDomainServiceImpl implements GuarantorDomainService { BigDecimal amountLeft = amountForRelease; for (GuarantorFundingDetails fundingDetails : guarantorList) { BigDecimal guarantorAmount = amountForRelease.multiply(fundingDetails.getAmountRemaining()).divide(totalGuaranteeAmount, - roundingMode); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); if (fundingDetails.getAmountRemaining().compareTo(guarantorAmount) < 1) { guarantorAmount = fundingDetails.getAmountRemaining(); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index 145e61cf1..b89ae080b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -18,6 +18,7 @@ import org.joda.time.PeriodType; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.common.domain.DayOfWeekType; @@ -402,7 +403,7 @@ public final class LoanApplicationTerms { } else if (this.actualFixedEmiAmount != null) { final Money difference = this.principal.minus(totalCumulativePrincipalToDate); final Money principalThreshold = principalForPeriod.multipliedBy(this.principalThresholdForLastInstalment).dividedBy(100, - RoundingMode.HALF_EVEN); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); if (difference.isLessThan(principalThreshold)) { adjusted = principalForPeriod.plus(difference.abs()); } @@ -486,7 +487,8 @@ public final class LoanApplicationTerms { case DECLINING_BALANCE: switch (this.amortizationMethod) { case EQUAL_INSTALLMENTS: - Money totalPmtForThisInstallment = pmtForInstallment(calculator, daysInPeriod, outstandingBalance, periodNumber, mc); + Money totalPmtForThisInstallment = pmtForInstallment(calculator, daysInPeriod, outstandingBalance, periodNumber, + mc); principalForInstallment = calculatePrincipalDueForInstallment(periodNumber, totalPmtForThisInstallment, interestForThisInstallment); break; @@ -551,9 +553,9 @@ public final class LoanApplicationTerms { final Money interestForThisInstallmentBeforeGrace = calculateDecliningInterestDueForInstallmentBeforeApplyingGrace( calculator, mc, daysInPeriodApplicableForInterest, outstandingBalance); - final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace( - calculator, interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriodApplicableForInterest, - outstandingBalance, periodNumber); + final Money interestForThisInstallmentAfterGrace = calculateDecliningInterestDueForInstallmentAfterApplyingGrace(calculator, + interestCalculationGraceOnRepaymentPeriodFraction, mc, daysInPeriodApplicableForInterest, outstandingBalance, + periodNumber); interestForInstallment = interestForThisInstallmentAfterGrace; if (interestForThisInstallmentAfterGrace.isGreaterThanZero()) { @@ -646,7 +648,8 @@ public final class LoanApplicationTerms { this.loanEndDate = loanEndDate; } - private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, final MathContext mc) { + private Money calculateTotalInterestPerInstallmentWithoutGrace(final PaymentPeriodsInOneYearCalculator calculator, + final MathContext mc) { final Money totalInterestForLoanTerm = calculateTotalFlatInterestDueWithoutGrace(calculator, mc); @@ -703,8 +706,8 @@ public final class LoanApplicationTerms { final Integer interestPaymentDuePeriods = calculateNumberOfRepaymentPeriodsWhereInterestPaymentIsDue(this.numberOfRepayments); - interestForInstallment = realTotalInterestForLoan - .dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), mc.getRoundingMode()); + interestForInstallment = realTotalInterestForLoan.dividedBy(BigDecimal.valueOf(interestPaymentDuePeriods), + mc.getRoundingMode()); } return interestForInstallment; @@ -786,8 +789,8 @@ public final class LoanApplicationTerms { final BigDecimal divisor = BigDecimal.valueOf(Double.valueOf("100.0")); final BigDecimal loanTermPeriodsInYearBigDecimal = BigDecimal.valueOf(loanTermPeriodsInOneYear); - final BigDecimal oneDayOfYearInterestRate = this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc).divide( - divisor, mc); + final BigDecimal oneDayOfYearInterestRate = this.annualNominalInterestRate.divide(loanTermPeriodsInYearBigDecimal, mc) + .divide(divisor, mc); BigDecimal interestRate = oneDayOfYearInterestRate.multiply(BigDecimal.valueOf(repaymentEvery), mc); return outstandingBalance.getAmount().multiply(interestRate, mc); } @@ -938,7 +941,8 @@ public final class LoanApplicationTerms { return principal; } - private Money calculatePrincipalDueForInstallment(final int periodNumber, final Money totalDuePerInstallment, final Money periodInterest) { + private Money calculatePrincipalDueForInstallment(final int periodNumber, final Money totalDuePerInstallment, + final Money periodInterest) { Money principal = totalDuePerInstallment.minus(periodInterest); if (isPrincipalGraceApplicableForThisPeriod(periodNumber)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index 60afc4008..5275f9beb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -25,6 +25,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.organisation.workingdays.service.WorkingDaysUtil; @@ -229,8 +230,8 @@ public class LoanScheduleAssembler { final Integer graceOnInterestCharged = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestCharged", element); final LocalDate interestChargedFromDate = this.fromApiJsonHelper.extractLocalDateNamed("interestChargedFromDate", element); - final Integer graceOnArrearsAgeing = this.fromApiJsonHelper.extractIntegerWithLocaleNamed( - LoanProductConstants.graceOnArrearsAgeingParameterName, element); + final Integer graceOnArrearsAgeing = this.fromApiJsonHelper + .extractIntegerWithLocaleNamed(LoanProductConstants.graceOnArrearsAgeingParameterName, element); // other final BigDecimal inArrearsTolerance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("inArrearsTolerance", element); @@ -238,14 +239,14 @@ public class LoanScheduleAssembler { final BigDecimal emiAmount = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.emiAmountParameterName, element); - final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed( - LoanApiConstants.maxOutstandingBalanceParameterName, element); + final BigDecimal maxOutstandingBalance = this.fromApiJsonHelper + .extractBigDecimalWithLocaleNamed(LoanApiConstants.maxOutstandingBalanceParameterName, element); final List disbursementDatas = fetchDisbursementData(element.getAsJsonObject()); final List loanVariationTermsData = new ArrayList<>(); - LoanTermVariationsData data = new LoanTermVariationsData(null, - LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), expectedDisbursementDate, emiAmount); + LoanTermVariationsData data = new LoanTermVariationsData(null, LoanEnumerations.loanvariationType(LoanTermVariationType.EMI_AMOUNT), + expectedDisbursementDate, emiAmount); loanVariationTermsData.add(data); /** @@ -265,8 +266,8 @@ public class LoanScheduleAssembler { .getProductInterestRecalculationDetails(); recalculationFrequencyType = loanProductInterestRecalculationDetails.getRestFrequencyType(); if (!recalculationFrequencyType.isSameAsRepayment()) { - LocalDate calendarStartDate = this.fromApiJsonHelper.extractLocalDateNamed( - LoanProductConstants.recalculationRestFrequencyDateParamName, element); + LocalDate calendarStartDate = this.fromApiJsonHelper + .extractLocalDateNamed(LoanProductConstants.recalculationRestFrequencyDateParamName, element); if (calendarStartDate == null) { calendarStartDate = expectedDisbursementDate; } @@ -278,8 +279,8 @@ public class LoanScheduleAssembler { if (compoundingMethod.isCompoundingEnabled()) { compoundingFrequencyType = loanProductInterestRecalculationDetails.getCompoundingFrequencyType(); if (!compoundingFrequencyType.isSameAsRepayment()) { - LocalDate calendarStartDate = this.fromApiJsonHelper.extractLocalDateNamed( - LoanProductConstants.recalculationCompoundingFrequencyDateParamName, element); + LocalDate calendarStartDate = this.fromApiJsonHelper + .extractLocalDateNamed(LoanProductConstants.recalculationCompoundingFrequencyDateParamName, element); if (calendarStartDate == null) { calendarStartDate = expectedDisbursementDate; } @@ -344,12 +345,12 @@ public class LoanScheduleAssembler { BigDecimal principal = null; if (jsonObject.has(LoanApiConstants.disbursementDateParameterName)) { - expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed( - LoanApiConstants.disbursementDateParameterName, jsonObject, dateFormat, locale); + expectedDisbursementDate = this.fromApiJsonHelper + .extractLocalDateNamed(LoanApiConstants.disbursementDateParameterName, jsonObject, dateFormat, locale); } if (jsonObject.has(LoanApiConstants.disbursementPrincipalParameterName) - && jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive() - && StringUtils.isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) { + && jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).isJsonPrimitive() && StringUtils + .isNotBlank((jsonObject.get(LoanApiConstants.disbursementPrincipalParameterName).getAsString()))) { principal = jsonObject.getAsJsonPrimitive(LoanApiConstants.disbursementPrincipalParameterName).getAsBigDecimal(); } disbursementDatas.add(new DisbursementData(null, expectedDisbursementDate, null, principal)); @@ -361,9 +362,8 @@ public class LoanScheduleAssembler { } private void validateRepaymentsStartDateWithMeetingDates(final LocalDate repaymentsStartingFromDate, final Calendar calendar) { - if (repaymentsStartingFromDate != null - && !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), - repaymentsStartingFromDate)) { + if (repaymentsStartingFromDate != null && !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), + calendar.getStartDateLocalDate(), repaymentsStartingFromDate)) { final String errorMessage = "First repayment date '" + repaymentsStartingFromDate + "' do not fall on a meeting date"; throw new LoanApplicationDateException("first.repayment.date.do.not.match.meeting.date", errorMessage, repaymentsStartingFromDate); @@ -444,7 +444,7 @@ public class LoanScheduleAssembler { final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod()); - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(8, roundingMode); HolidayDetailDTO detailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays); @@ -455,12 +455,12 @@ public class LoanScheduleAssembler { public LoanScheduleModel assembleForInterestRecalculation(final LoanApplicationTerms loanApplicationTerms, final Long officeId, List transactions, final Set loanCharges, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) { - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(8, roundingMode); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, loanApplicationTerms - .getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, + loanApplicationTerms.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod()); @@ -474,12 +474,12 @@ public class LoanScheduleAssembler { final Long officeId, List loanTransactions, final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor) { final LoanScheduleGenerator loanScheduleGenerator = this.loanScheduleFactory.create(loanApplicationTerms.getInterestMethod()); - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(8, roundingMode); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, loanApplicationTerms - .getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(officeId, + loanApplicationTerms.getExpectedDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays); @@ -521,7 +521,7 @@ public class LoanScheduleAssembler { refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType, minimumDaysBetweenDisbursalAndFirstRepayment, calendar); - }/*** Individual or group account, or JLG not linked to a meeting ***/ + } /*** Individual or group account, or JLG not linked to a meeting ***/ else { LocalDate dateBasedOnRepaymentFrequency; // Derive the first repayment date as greater date among @@ -534,12 +534,12 @@ public class LoanScheduleAssembler { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery); } else if (repaymentPeriodFrequencyType.isMonthly()) { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery); - }/** yearly loan **/ + } /** yearly loan **/ else { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery); } - derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment) ? dateBasedOnRepaymentFrequency - : dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment; + derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment) + ? dateBasedOnRepaymentFrequency : dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment; } return derivedFirstRepayment; @@ -553,16 +553,17 @@ public class LoanScheduleAssembler { final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar, refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency); final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment); - return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment : deriveFirstRepaymentDateForJLGLoans( - repaymentEvery, expectedDisbursementDate, derivedFirstRepayment, repaymentPeriodFrequencyType, - minimumDaysBetweenDisbursalAndFirstRepayment, calendar); + return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment + : deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate, derivedFirstRepayment, + repaymentPeriodFrequencyType, minimumDaysBetweenDisbursalAndFirstRepayment, calendar); } private void validateMinimumDaysBetweenDisbursalAndFirstRepayment(final LocalDate disbursalDate, final LocalDate firstRepaymentDate, final Integer minimumDaysBetweenDisbursalAndFirstRepayment) { final LocalDate minimumFirstRepaymentDate = disbursalDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment); - if (firstRepaymentDate.isBefore(minimumFirstRepaymentDate)) { throw new MinDaysBetweenDisbursalAndFirstRepaymentViolationException( - disbursalDate, firstRepaymentDate, minimumDaysBetweenDisbursalAndFirstRepayment); } + if (firstRepaymentDate + .isBefore(minimumFirstRepaymentDate)) { throw new MinDaysBetweenDisbursalAndFirstRepaymentViolationException(disbursalDate, + firstRepaymentDate, minimumDaysBetweenDisbursalAndFirstRepayment); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java index c7d1a9401..c4dd40514 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java @@ -16,6 +16,7 @@ import org.mifosplatform.organisation.holiday.domain.HolidayStatusType; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.portfolio.calendar.domain.Calendar; @@ -72,26 +73,26 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP Loan loan = loanRescheduleRequest.getLoan(); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan - .getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), + loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail(); final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency(); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod(); - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mathContext = new MathContext(8, roundingMode); - List oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive( - loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest); + List oldPeriods = this.loanScheduleHistoryWritePlatformService + .createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest); HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays); CalendarInstance restCalendarInstance = null; CalendarInstance compoundingCalendarInstance = null; if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue()); - compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId( - loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue()); + compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(), + CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue()); } final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java index 6efbff044..ddf0867f9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java @@ -28,6 +28,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.organisation.workingdays.domain.WorkingDays; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; import org.mifosplatform.portfolio.calendar.domain.Calendar; @@ -270,8 +271,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche final Map changes = new LinkedHashMap<>(); LocalDate approvedOnDate = jsonCommand.localDateValueOfParameterNamed("approvedOnDate"); - final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale( - jsonCommand.extractLocale()); + final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()) + .withLocale(jsonCommand.extractLocale()); changes.put("locale", jsonCommand.locale()); changes.put("dateFormat", jsonCommand.dateFormat()); @@ -283,15 +284,15 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche final LoanSummary loanSummary = loan.getSummary(); final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan - .getDisbursementDate().toDate()); + final List holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), + loan.getDisbursementDate().toDate()); final WorkingDays workingDays = this.workingDaysRepository.findOne(); final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail(); final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency(); final ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod(); - final RoundingMode roundingMode = RoundingMode.HALF_EVEN; + final RoundingMode roundingMode = RoundingMode.valueOf(MoneyHelper.getRoundingMode()); final MathContext mathContext = new MathContext(8, roundingMode); Collection loanRepaymentScheduleHistoryList = this.loanScheduleHistoryWritePlatformService @@ -454,12 +455,12 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency); final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); - calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate( - loan.getDisbursementDate(), loan, calendarInstance); + calculatedRepaymentsStartingFromDate = this.loanAccountDomainService + .getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance); isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled(); - holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate() - .toDate()); + holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), + loan.getDisbursementDate().toDate()); workingDays = this.workingDaysRepository.findOne(); overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod(); } @@ -471,8 +472,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche Money accruedCharge = Money.zero(loan.getCurrency()); if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) { - Collection chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy( - loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber); + Collection chargePaidByDatas = this.loanChargeReadPlatformService + .retriveLoanChargesPaidBy(loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber); for (LoanChargePaidByData chargePaidByData : chargePaidByDatas) { accruedCharge = accruedCharge.plus(chargePaidByData.getAmount()); } @@ -522,8 +523,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche final Map changes = new LinkedHashMap<>(); LocalDate rejectedOnDate = jsonCommand.localDateValueOfParameterNamed("rejectedOnDate"); - final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale( - jsonCommand.extractLocale()); + final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()) + .withLocale(jsonCommand.extractLocale()); changes.put("locale", jsonCommand.locale()); changes.put("dateFormat", jsonCommand.dateFormat()); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java index 6907955a0..f8edaabb6 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java @@ -22,6 +22,7 @@ import org.joda.time.LocalDate; import org.mifosplatform.accounting.journalentry.service.JournalEntryWritePlatformService; import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.loanaccount.data.LoanChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanInstallmentChargeData; import org.mifosplatform.portfolio.loanaccount.data.LoanScheduleAccrualData; @@ -140,7 +141,7 @@ public class LoanAccrualWritePlatformServiceImpl implements LoanAccrualWritePlat double iterest = interestPerDay * daysToBeAccrued; interestportion = BigDecimal.valueOf(iterest); } - interestportion = interestportion.setScale(accrualData.getCurrencyData().decimalPlaces(), RoundingMode.HALF_EVEN); + interestportion = interestportion.setScale(accrualData.getCurrencyData().decimalPlaces(), RoundingMode.valueOf(MoneyHelper.getRoundingMode())); BigDecimal totalAccInterest = accrualData.getAccruedInterestIncome(); BigDecimal totalAccPenalty = accrualData.getAccruedPenaltyIncome(); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java index fb3da312d..33421ac53 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/SavingsAccountCharge.java @@ -34,6 +34,7 @@ import org.joda.time.MonthDay; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.charge.domain.Charge; import org.mifosplatform.portfolio.charge.domain.ChargeCalculationType; import org.mifosplatform.portfolio.charge.domain.ChargeTimeType; @@ -527,7 +528,7 @@ public class SavingsAccountCharge extends AbstractPersistable { BigDecimal percentageOf = BigDecimal.ZERO; if (isGreaterThanZero(value)) { - final MathContext mc = new MathContext(8, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(8, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); final BigDecimal multiplicand = percentage.divide(BigDecimal.valueOf(100l), mc); percentageOf = value.multiply(multiplicand, mc); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java index 7189b9490..046c816b3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/AnnualCompoundingPeriod.java @@ -13,6 +13,7 @@ import java.util.List; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -25,8 +26,8 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { public static AnnualCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { - final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, - allEndOfDayBalances, upToInterestCalculationDate); + final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, + upToInterestCalculationDate); return new AnnualCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod); } @@ -40,8 +41,8 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { switch (interestCalculationType) { case DAILY_BALANCE: - interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, - interestRateAsFraction, daysInYear, minBalanceForInterestCalculation); + interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, + daysInYear, minBalanceForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, @@ -54,8 +55,8 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { return interestEarned; } - private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, + final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -71,14 +72,14 @@ public class AnnualCompoundingPeriod implements CompoundingPeriod { BigDecimal interestEarned = BigDecimal.ZERO; if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) - .setScale(9, RoundingMode.HALF_EVEN); + .setScale(9, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - RoundingMode.HALF_EVEN); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java index 1902b0eec..a6edf7641 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/BiAnnualCompoundingPeriod.java @@ -13,6 +13,7 @@ import java.util.List; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -25,8 +26,8 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { public static BiAnnualCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { - final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, - allEndOfDayBalances, upToInterestCalculationDate); + final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, + upToInterestCalculationDate); return new BiAnnualCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod); } @@ -40,8 +41,8 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { switch (interestCalculationType) { case DAILY_BALANCE: - interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, - interestRateAsFraction, daysInYear, minBalanceForInterestCalculation); + interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, + daysInYear, minBalanceForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, @@ -54,8 +55,8 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { return interestEarned; } - private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, + final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -71,14 +72,14 @@ public class BiAnnualCompoundingPeriod implements CompoundingPeriod { BigDecimal interestEarned = BigDecimal.ZERO; if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) - .setScale(9, RoundingMode.HALF_EVEN); + .setScale(9, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - RoundingMode.HALF_EVEN); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java index b00fe2acf..53ad518a3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/EndOfDayBalance.java @@ -12,6 +12,7 @@ import java.math.RoundingMode; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; public class EndOfDayBalance { @@ -20,7 +21,8 @@ public class EndOfDayBalance { private final Money endOfDayBalance; private final int numberOfDays; - public static EndOfDayBalance from(final LocalDate date, final Money openingBalance, final Money endOfDayBalance, final int numberOfDays) { + public static EndOfDayBalance from(final LocalDate date, final Money openingBalance, final Money endOfDayBalance, + final int numberOfDays) { return new EndOfDayBalance(date, openingBalance, endOfDayBalance, numberOfDays); } @@ -42,7 +44,8 @@ public class EndOfDayBalance { public BigDecimal cumulativeBalance(final BigDecimal interestToCompound) { final BigDecimal daysAsBigDecimal = BigDecimal.valueOf(this.numberOfDays); final BigDecimal realBalanceForInterestCalculation = this.endOfDayBalance.getAmount().add(interestToCompound); - return realBalanceForInterestCalculation.multiply(daysAsBigDecimal, MathContext.DECIMAL64).setScale(9, RoundingMode.HALF_EVEN); + return realBalanceForInterestCalculation.multiply(daysAsBigDecimal, MathContext.DECIMAL64).setScale(9, + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } public BigDecimal calculateInterestOnBalance(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, @@ -53,10 +56,10 @@ public class EndOfDayBalance { final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(this.numberOfDays), MathContext.DECIMAL64); final BigDecimal realBalanceForInterestCalculation = this.endOfDayBalance.getAmount().add(interestToCompound); - BigDecimal interest = BigDecimal.ZERO.setScale(9, RoundingMode.HALF_EVEN); + BigDecimal interest = BigDecimal.ZERO.setScale(9, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); if (realBalanceForInterestCalculation.compareTo(minBalanceForInterestCalculation) >= 0) { interest = realBalanceForInterestCalculation.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - RoundingMode.HALF_EVEN); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } return interest; } @@ -78,12 +81,12 @@ public class EndOfDayBalance { final BigDecimal interestRateForCompoundingPeriodPlusOne = BigDecimal.ONE.add(r); - final double interestRateForCompoundingPeriodPowered = Math.pow(interestRateForCompoundingPeriodPlusOne.doubleValue(), Integer - .valueOf(this.numberOfDays).doubleValue()); - BigDecimal futureValue = presentValue.setScale(9, RoundingMode.HALF_EVEN); + final double interestRateForCompoundingPeriodPowered = Math.pow(interestRateForCompoundingPeriodPlusOne.doubleValue(), + Integer.valueOf(this.numberOfDays).doubleValue()); + BigDecimal futureValue = presentValue.setScale(9, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); if (presentValue.compareTo(minBalanceForInterestCalculation) >= 0) { futureValue = presentValue.multiply(BigDecimal.valueOf(interestRateForCompoundingPeriodPowered), MathContext.DECIMAL64) - .setScale(9, RoundingMode.HALF_EVEN); + .setScale(9, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } return futureValue.subtract(presentValue); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java index 659bb692c..b93abc56a 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/MonthlyCompoundingPeriod.java @@ -13,6 +13,7 @@ import java.util.List; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -25,8 +26,8 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { public static MonthlyCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { - final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, - allEndOfDayBalances, upToInterestCalculationDate); + final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, + upToInterestCalculationDate); return new MonthlyCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod); } @@ -40,8 +41,8 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { switch (interestCalculationType) { case DAILY_BALANCE: - interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, - interestRateAsFraction, daysInYear, minBalanceForInterestCalculation); + interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, + daysInYear, minBalanceForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, @@ -54,8 +55,8 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { return interestEarned; } - private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, + final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -71,14 +72,14 @@ public class MonthlyCompoundingPeriod implements CompoundingPeriod { BigDecimal interestEarned = BigDecimal.ZERO; if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) - .setScale(9, RoundingMode.HALF_EVEN); + .setScale(9, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - RoundingMode.HALF_EVEN); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java index 112ad1cc8..1cd365bc7 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/domain/interest/QuarterlyCompoundingPeriod.java @@ -13,6 +13,7 @@ import java.util.List; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.domain.LocalDateInterval; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.savings.SavingsCompoundingInterestPeriodType; import org.mifosplatform.portfolio.savings.SavingsInterestCalculationType; @@ -22,11 +23,11 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { private final LocalDateInterval periodInterval; private final List endOfDayBalances; - public static QuarterlyCompoundingPeriod create(final LocalDateInterval periodInterval, - final List allEndOfDayBalances, final LocalDate upToInterestCalculationDate) { + public static QuarterlyCompoundingPeriod create(final LocalDateInterval periodInterval, final List allEndOfDayBalances, + final LocalDate upToInterestCalculationDate) { - final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, - allEndOfDayBalances, upToInterestCalculationDate); + final List endOfDayBalancesWithinPeriod = endOfDayBalancesWithinPeriodInterval(periodInterval, allEndOfDayBalances, + upToInterestCalculationDate); return new QuarterlyCompoundingPeriod(periodInterval, endOfDayBalancesWithinPeriod); } @@ -40,8 +41,8 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { switch (interestCalculationType) { case DAILY_BALANCE: - interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, - interestRateAsFraction, daysInYear, minBalanceForInterestCalculation); + interestEarned = calculateUsingDailyBalanceMethod(compoundingInterestPeriodType, interestToCompound, interestRateAsFraction, + daysInYear, minBalanceForInterestCalculation); break; case AVERAGE_DAILY_BALANCE: interestEarned = calculateUsingAverageDailyBalanceMethod(interestToCompound, interestRateAsFraction, daysInYear, @@ -54,8 +55,8 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { return interestEarned; } - private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, - final BigDecimal interestRateAsFraction, final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { + private BigDecimal calculateUsingAverageDailyBalanceMethod(final BigDecimal interestToCompound, final BigDecimal interestRateAsFraction, + final long daysInYear, final BigDecimal minBalanceForInterestCalculation) { BigDecimal cumulativeBalance = BigDecimal.ZERO; Integer numberOfDays = Integer.valueOf(0); @@ -71,14 +72,14 @@ public class QuarterlyCompoundingPeriod implements CompoundingPeriod { BigDecimal interestEarned = BigDecimal.ZERO; if (cumulativeBalance.compareTo(BigDecimal.ZERO) != 0 && numberOfDays > 0) { final BigDecimal averageDailyBalance = cumulativeBalance.divide(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64) - .setScale(9, RoundingMode.HALF_EVEN); + .setScale(9, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); final BigDecimal multiplicand = BigDecimal.ONE.divide(BigDecimal.valueOf(daysInYear), MathContext.DECIMAL64); final BigDecimal dailyInterestRate = interestRateAsFraction.multiply(multiplicand, MathContext.DECIMAL64); final BigDecimal periodicInterestRate = dailyInterestRate.multiply(BigDecimal.valueOf(numberOfDays), MathContext.DECIMAL64); if (averageDailyBalance.compareTo(minBalanceForInterestCalculation) >= 0) { interestEarned = averageDailyBalance.multiply(periodicInterestRate, MathContext.DECIMAL64).setScale(9, - RoundingMode.HALF_EVEN); + RoundingMode.valueOf(MoneyHelper.getRoundingMode())); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java index 52567e10e..858a01bfd 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/DepositAccountWritePlatformServiceJpaRepositoryImpl.java @@ -47,6 +47,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.workingdays.domain.WorkingDaysRepositoryWrapper; @@ -466,7 +467,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo checkClientOrGroupActive(account); final LocalDate today = DateUtils.getLocalDateOfTenant(); - final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(15, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); boolean isInterestTransfer = false; account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, @@ -510,7 +511,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final Set existingReversedTransactionIds = new HashSet<>(); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final LocalDate today = DateUtils.getLocalDateOfTenant(); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(10, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); boolean isInterestTransfer = false; account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); this.savingAccountRepository.save(account); @@ -659,7 +660,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo final Map changes = new LinkedHashMap<>(); final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(10, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); account.undoTransaction(transactionId); SavingsAccountTransaction transaction = null; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java index 3723894c2..b6e280f4d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/savings/service/SavingsAccountWritePlatformServiceJpaRepositoryImpl.java @@ -43,6 +43,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.organisation.office.domain.Office; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; @@ -321,7 +322,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi checkClientOrGroupActive(account); final LocalDate today = DateUtils.getLocalDateOfTenant(); - final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(15, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); boolean isInterestTransfer = false; account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); @@ -366,7 +367,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final Set existingReversedTransactionIds = new HashSet<>(); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); final LocalDate today = DateUtils.getLocalDateOfTenant(); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(10, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); boolean isInterestTransfer = false; account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); @@ -384,7 +385,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi } } - @Override public CommandProcessingResult undoTransaction(final Long savingsId, final Long transactionId, final boolean allowAccountTransferModification) { @@ -415,7 +415,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi "Savings account transaction:" + transactionId + " update not allowed for this savings type", transactionId); } final LocalDate today = DateUtils.getLocalDateOfTenant(); - final MathContext mc = new MathContext(15, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(15, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); if (account.isNotActive()) { throwValidationForActiveStatus(SavingsApiConstants.undoTransactionAction); @@ -497,7 +497,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi final Map changes = new LinkedHashMap<>(); final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes); - final MathContext mc = new MathContext(10, RoundingMode.HALF_EVEN); + final MathContext mc = new MathContext(10, RoundingMode.valueOf(MoneyHelper.getRoundingMode())); account.undoTransaction(transactionId); // for undo withdrawal fee diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V266__add_rounding_mode_configuration.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V266__add_rounding_mode_configuration.sql new file mode 100644 index 000000000..b7f919ee9 --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V266__add_rounding_mode_configuration.sql @@ -0,0 +1,4 @@ +ALTER TABLE `c_configuration` + ADD COLUMN `is_trap_door` boolean NOT NULL DEFAULT '0' AFTER `enabled`; + +insert into c_configuration(name, value, enabled, is_trap_door, description) values('rounding-mode', '6', '1', '1', '0 - UP, 1 - DOWN, 2- CEILING, 3- FLOOR, 4- HALF_UP, 5- HALF_DOWN, 6 - HALF_EVEN'); \ No newline at end of file diff --git a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/AdhikarLoanRepaymentScheduleTransactionProcessorTest.java b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/AdhikarLoanRepaymentScheduleTransactionProcessorTest.java index c2ad410d2..adba90b9a 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/AdhikarLoanRepaymentScheduleTransactionProcessorTest.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/AdhikarLoanRepaymentScheduleTransactionProcessorTest.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.loanaccount; +import java.lang.reflect.Field; import java.util.List; import org.joda.time.LocalDate; @@ -12,6 +13,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.RBILoanRepaymentScheduleTransactionProcessor; import org.mockito.runners.MockitoJUnitRunner; @@ -29,8 +31,11 @@ public class AdhikarLoanRepaymentScheduleTransactionProcessorTest { private List installments; @Before - public void setUpForEachTestCase() { + public void setUpForEachTestCase() throws Exception { + Field field = MoneyHelper.class.getDeclaredField("roundingModeIntValue"); + field.setAccessible(true); + field.setInt(null, 6); this.installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(this.july2nd, this.usDollars); this.processor = new RBILoanRepaymentScheduleTransactionProcessor(); diff --git a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/HeavensFamilyLoanRepaymentScheduleTransactionProcessorTest.java b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/HeavensFamilyLoanRepaymentScheduleTransactionProcessorTest.java index 2ca1ed866..d6e9e7213 100644 --- a/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/HeavensFamilyLoanRepaymentScheduleTransactionProcessorTest.java +++ b/mifosng-provider/src/test/java/org/mifosplatform/portfolio/loanaccount/HeavensFamilyLoanRepaymentScheduleTransactionProcessorTest.java @@ -5,6 +5,7 @@ */ package org.mifosplatform.portfolio.loanaccount; +import java.lang.reflect.Field; import java.util.List; import org.joda.time.LocalDate; @@ -12,6 +13,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; +import org.mifosplatform.organisation.monetary.domain.MoneyHelper; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment; import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.impl.HeavensFamilyLoanRepaymentScheduleTransactionProcessor; import org.mockito.runners.MockitoJUnitRunner; @@ -29,8 +31,11 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessorTest { private List installments; @Before - public void setUpForEachTestCase() { + public void setUpForEachTestCase() throws Exception { + Field field = MoneyHelper.class.getDeclaredField("roundingModeIntValue"); + field.setAccessible(true); + field.setInt(null, 6); this.installments = LoanScheduleTestDataHelper.createSimpleLoanSchedule(this.july2nd, this.usDollars); this.processor = new HeavensFamilyLoanRepaymentScheduleTransactionProcessor();