MIFOSX-2148: Make Rounding Mode as configurable throughout Mifos

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