Floating Rates Feature
This commit is contained in:
+16
@@ -2529,4 +2529,20 @@ public class CommandWrapperBuilder {
|
||||
this.href = "/provisioningentries/"+entryId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder createFloatingRate() {
|
||||
this.actionName = "CREATE";
|
||||
this.entityName = "FLOATINGRATE";
|
||||
this.entityId = null;
|
||||
this.href = "/floatingrates";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandWrapperBuilder updateFloatingRate(final Long floatingRateId) {
|
||||
this.actionName = "UPDATE";
|
||||
this.entityName = "FLOATINGRATE";
|
||||
this.entityId = floatingRateId;
|
||||
this.href = "/floatingrates/"+floatingRateId;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.api;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.mifosplatform.commands.domain.CommandWrapper;
|
||||
import org.mifosplatform.commands.service.CommandWrapperBuilder;
|
||||
import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService;
|
||||
import org.mifosplatform.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/floatingrates")
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
public class FloatingRatesApiResource {
|
||||
|
||||
private static final String RESOURCE_NAME = "FLOATINGRATE";
|
||||
private static final Set<String> LIST_FLOATING_RATES_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList("id", "name", "isBaseLendingRate", "isActive",
|
||||
"createdby", "createdOn", "modifiedBy", "modifiedOn"));
|
||||
private static final Set<String> INDIVIDUAL_FLOATING_RATES_PARAMETERS = new HashSet<>(
|
||||
Arrays.asList("id", "name", "isBaseLendingRate", "isActive",
|
||||
"createdBy", "createdOn", "modifiedBy", "modifiedOn",
|
||||
"ratePeriods"));
|
||||
private final PlatformSecurityContext context;
|
||||
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
|
||||
private final DefaultToApiJsonSerializer<FloatingRateData> toApiJsonSerializer;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
private final ApiRequestParameterHelper apiRequestParameterHelper;
|
||||
|
||||
@Autowired
|
||||
public FloatingRatesApiResource(
|
||||
final PlatformSecurityContext context,
|
||||
final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
|
||||
final DefaultToApiJsonSerializer<FloatingRateData> toApiJsonSerializer,
|
||||
final ApiRequestParameterHelper apiRequestParameterHelper,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.context = context;
|
||||
this.commandsSourceWritePlatformService = commandsSourceWritePlatformService;
|
||||
this.toApiJsonSerializer = toApiJsonSerializer;
|
||||
this.apiRequestParameterHelper = apiRequestParameterHelper;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String createFloatingRate(final String apiRequestBodyAsJson) {
|
||||
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.createFloatingRate().withJson(apiRequestBodyAsJson).build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService
|
||||
.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveAll(@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(
|
||||
RESOURCE_NAME);
|
||||
final List<FloatingRateData> floatingRates = this.floatingRatesReadPlatformService
|
||||
.retrieveAll();
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||
.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, floatingRates,
|
||||
FloatingRatesApiResource.LIST_FLOATING_RATES_PARAMETERS);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("{floatingRateId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String retrieveOne(
|
||||
@PathParam("floatingRateId") final Long floatingRateId,
|
||||
@Context final UriInfo uriInfo) {
|
||||
this.context.authenticatedUser().validateHasReadPermission(
|
||||
RESOURCE_NAME);
|
||||
final FloatingRateData floatingRates = this.floatingRatesReadPlatformService
|
||||
.retrieveOne(floatingRateId);
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper
|
||||
.process(uriInfo.getQueryParameters());
|
||||
return this.toApiJsonSerializer.serialize(settings, floatingRates,
|
||||
FloatingRatesApiResource.INDIVIDUAL_FLOATING_RATES_PARAMETERS);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("{floatingRateId}")
|
||||
@Consumes({ MediaType.APPLICATION_JSON })
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String updateFloatingRate(
|
||||
@PathParam("floatingRateId") final Long floatingRateId,
|
||||
final String apiRequestBodyAsJson) {
|
||||
final CommandWrapper commandRequest = new CommandWrapperBuilder()
|
||||
.updateFloatingRate(floatingRateId)
|
||||
.withJson(apiRequestBodyAsJson).build();
|
||||
final CommandProcessingResult result = this.commandsSourceWritePlatformService
|
||||
.logCommandSource(commandRequest);
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class FloatingRateDTO {
|
||||
|
||||
private final boolean isFloatingInterestRate;
|
||||
private final LocalDate startDate;
|
||||
private BigDecimal interestRateDiff;
|
||||
private final Collection<FloatingRatePeriodData> baseLendingRatePeriods;
|
||||
|
||||
public FloatingRateDTO(final boolean isFloatingInterestRate,
|
||||
final LocalDate startDate, final BigDecimal interestRateDiff,
|
||||
final Collection<FloatingRatePeriodData> baseLendingRatePeriods) {
|
||||
this.isFloatingInterestRate = isFloatingInterestRate;
|
||||
this.startDate = startDate;
|
||||
this.interestRateDiff = interestRateDiff;
|
||||
this.baseLendingRatePeriods = baseLendingRatePeriods;
|
||||
}
|
||||
|
||||
public BigDecimal fetchBaseRate(LocalDate date) {
|
||||
BigDecimal rate = null;
|
||||
for (FloatingRatePeriodData periodData : this.baseLendingRatePeriods) {
|
||||
if (date.isBefore(new LocalDate(periodData.getFromDate()))) {
|
||||
break;
|
||||
}
|
||||
rate = periodData.getInterestRate();
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
public void addInterestRateDiff(final BigDecimal diff) {
|
||||
this.interestRateDiff = this.interestRateDiff.add(diff);
|
||||
}
|
||||
|
||||
public boolean isFloatingInterestRate() {
|
||||
return this.isFloatingInterestRate;
|
||||
}
|
||||
|
||||
public LocalDate getStartDate() {
|
||||
return this.startDate;
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRateDiff() {
|
||||
return this.interestRateDiff;
|
||||
}
|
||||
|
||||
public Collection<FloatingRatePeriodData> getBaseLendingRatePeriods() {
|
||||
return this.baseLendingRatePeriods;
|
||||
}
|
||||
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.builder.CompareToBuilder;
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
|
||||
public class FloatingRateData implements Comparable<FloatingRateData> {
|
||||
|
||||
private final Long id;
|
||||
private final String name;
|
||||
private final boolean isBaseLendingRate;
|
||||
private final boolean isActive;
|
||||
private final String createdBy;
|
||||
private final Date createdOn;
|
||||
private final String modifiedBy;
|
||||
private final Date modifiedOn;
|
||||
private final List<FloatingRatePeriodData> ratePeriods;
|
||||
@SuppressWarnings("unused")
|
||||
private final List<EnumOptionData> interestRateFrequencyTypeOptions;
|
||||
|
||||
public FloatingRateData(Long id, String name, boolean isBaseLendingRate,
|
||||
boolean isActive, String createdBy, Date createdOn,
|
||||
String modifiedBy, Date modifiedOn,
|
||||
List<FloatingRatePeriodData> ratePeriods,
|
||||
List<EnumOptionData> interestRateFrequencyTypeOptions) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.isBaseLendingRate = isBaseLendingRate;
|
||||
this.isActive = isActive;
|
||||
this.createdBy = createdBy;
|
||||
this.createdOn = createdOn;
|
||||
this.modifiedBy = modifiedBy;
|
||||
this.modifiedOn = modifiedOn;
|
||||
this.ratePeriods = ratePeriods;
|
||||
this.interestRateFrequencyTypeOptions = interestRateFrequencyTypeOptions;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean isBaseLendingRate() {
|
||||
return this.isBaseLendingRate;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.isActive;
|
||||
}
|
||||
|
||||
public String getCreatedBy() {
|
||||
return this.createdBy;
|
||||
}
|
||||
|
||||
public Date getCreatedOn() {
|
||||
return this.createdOn;
|
||||
}
|
||||
|
||||
public String getModifiedBy() {
|
||||
return this.modifiedBy;
|
||||
}
|
||||
|
||||
public Date getModifiedOn() {
|
||||
return this.modifiedOn;
|
||||
}
|
||||
|
||||
public List<FloatingRatePeriodData> getRatePeriods() {
|
||||
return this.ratePeriods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final FloatingRateData obj) {
|
||||
if (obj == null) {
|
||||
return -1;
|
||||
}
|
||||
return new CompareToBuilder() //
|
||||
.append(this.id, obj.id) //
|
||||
.append(this.name, obj.name) //
|
||||
.append(this.isBaseLendingRate, obj.isBaseLendingRate) //
|
||||
.append(this.isActive, obj.isActive) //
|
||||
.toComparison();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
final FloatingRateData rhs = (FloatingRateData) obj;
|
||||
return new EqualsBuilder() //
|
||||
.append(this.id, rhs.id) //
|
||||
.append(this.name, rhs.name) //
|
||||
.append(this.isBaseLendingRate, rhs.isBaseLendingRate) //
|
||||
.append(this.isActive, rhs.isActive) //
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37) //
|
||||
.append(this.id) //
|
||||
.append(this.name) //
|
||||
.append(this.isBaseLendingRate) //
|
||||
.append(this.isActive) //
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
public static FloatingRateData toTemplate(
|
||||
List<EnumOptionData> interestRateFrequencyTypeOptions) {
|
||||
// TODO Auto-generated method stub
|
||||
return new FloatingRateData(null, null, false, true, null, null, null,
|
||||
null, null, interestRateFrequencyTypeOptions);
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.lang.builder.CompareToBuilder;
|
||||
import org.apache.commons.lang.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang.builder.HashCodeBuilder;
|
||||
import org.joda.time.LocalDate;
|
||||
|
||||
public class FloatingRatePeriodData implements
|
||||
Comparable<FloatingRatePeriodData> {
|
||||
|
||||
private Long id;
|
||||
private Date fromDate;
|
||||
private BigDecimal interestRate;
|
||||
private boolean isDifferentialToBaseLendingRate;
|
||||
private boolean isActive;
|
||||
private String createdBy;
|
||||
private Date createdOn;
|
||||
private String modifiedBy;
|
||||
private Date modifiedOn;
|
||||
|
||||
public FloatingRatePeriodData(Long id, Date fromDate,
|
||||
BigDecimal interestRate, boolean isDifferentialToBaseLendingRate,
|
||||
boolean isActive, String createdBy, Date createdOn,
|
||||
String modifiedBy, Date modifiedOn) {
|
||||
this.id = id;
|
||||
this.fromDate = fromDate;
|
||||
this.interestRate = interestRate;
|
||||
this.isDifferentialToBaseLendingRate = isDifferentialToBaseLendingRate;
|
||||
this.isActive = isActive;
|
||||
this.createdBy = createdBy;
|
||||
this.createdOn = createdOn;
|
||||
this.modifiedBy = modifiedBy;
|
||||
this.modifiedOn = modifiedOn;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Date getFromDate() {
|
||||
return this.fromDate;
|
||||
}
|
||||
|
||||
public LocalDate getFromDateAsLocalDate() {
|
||||
return new LocalDate(this.fromDate);
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRate() {
|
||||
return this.interestRate;
|
||||
}
|
||||
|
||||
public boolean isDifferentialToBaseLendingRate() {
|
||||
return this.isDifferentialToBaseLendingRate;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.isActive;
|
||||
}
|
||||
|
||||
public String getCreatedBy() {
|
||||
return this.createdBy;
|
||||
}
|
||||
|
||||
public Date getCreatedOn() {
|
||||
return this.createdOn;
|
||||
}
|
||||
|
||||
public String getModifiedBy() {
|
||||
return this.modifiedBy;
|
||||
}
|
||||
|
||||
public Date getModifiedOn() {
|
||||
return this.modifiedOn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final FloatingRatePeriodData obj) {
|
||||
if (obj == null) {
|
||||
return -1;
|
||||
}
|
||||
return new CompareToBuilder() //
|
||||
.append(this.id, obj.id) //
|
||||
.append(this.fromDate, obj.fromDate) //
|
||||
.append(this.isActive, obj.isActive) //
|
||||
.toComparison();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object obj) {
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (obj == this) {
|
||||
return true;
|
||||
}
|
||||
if (obj.getClass() != getClass()) {
|
||||
return false;
|
||||
}
|
||||
final FloatingRatePeriodData rhs = (FloatingRatePeriodData) obj;
|
||||
return new EqualsBuilder() //
|
||||
.append(this.id, rhs.id) //
|
||||
.append(this.fromDate, rhs.fromDate) //
|
||||
.append(this.isActive, rhs.isActive) //
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder(17, 37) //
|
||||
.append(this.id) //
|
||||
.append(this.fromDate) //
|
||||
.append(this.isActive) //
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
public class InterestRatePeriodData {
|
||||
|
||||
private Date fromDate;
|
||||
private final BigDecimal interestRate;
|
||||
private final boolean isDifferentialToBLR;
|
||||
private final Date blrFromDate;
|
||||
private final BigDecimal blrInterestRate;
|
||||
private BigDecimal loanDifferentialInterestRate;
|
||||
private BigDecimal loanProductDifferentialInterestRate;
|
||||
private BigDecimal effectiveInterestRate;
|
||||
|
||||
public InterestRatePeriodData(Date fromDate, BigDecimal interestRate,
|
||||
boolean isDifferentialToBLR, Date blrFromDate,
|
||||
BigDecimal blrInterestRate) {
|
||||
this.fromDate = fromDate;
|
||||
this.interestRate = interestRate;
|
||||
this.isDifferentialToBLR = isDifferentialToBLR;
|
||||
this.blrFromDate = blrFromDate;
|
||||
this.blrInterestRate = blrInterestRate;
|
||||
}
|
||||
|
||||
public Date getFromDate() {
|
||||
return this.fromDate;
|
||||
}
|
||||
|
||||
public void setFromDate(Date fromDate) {
|
||||
this.fromDate = fromDate;
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRate() {
|
||||
return this.interestRate;
|
||||
}
|
||||
|
||||
public boolean isDifferentialToBLR() {
|
||||
return this.isDifferentialToBLR;
|
||||
}
|
||||
|
||||
public Date getBlrFromDate() {
|
||||
return this.blrFromDate;
|
||||
}
|
||||
|
||||
public BigDecimal getBlrInterestRate() {
|
||||
return this.blrInterestRate;
|
||||
}
|
||||
|
||||
public BigDecimal getLoanDifferentialInterestRate() {
|
||||
return this.loanDifferentialInterestRate;
|
||||
}
|
||||
|
||||
public void setLoanDifferentialInterestRate(
|
||||
BigDecimal loanDifferentialInterestRate) {
|
||||
this.loanDifferentialInterestRate = loanDifferentialInterestRate;
|
||||
}
|
||||
|
||||
public BigDecimal getLoanProductDifferentialInterestRate() {
|
||||
return this.loanProductDifferentialInterestRate;
|
||||
}
|
||||
|
||||
public void setLoanProductDifferentialInterestRate(
|
||||
BigDecimal loanProductDifferentialInterestRate) {
|
||||
this.loanProductDifferentialInterestRate = loanProductDifferentialInterestRate;
|
||||
}
|
||||
|
||||
public BigDecimal getEffectiveInterestRate() {
|
||||
return this.effectiveInterestRate;
|
||||
}
|
||||
|
||||
public void setEffectiveInterestRate(BigDecimal effectiveInterestRate) {
|
||||
this.effectiveInterestRate = effectiveInterestRate;
|
||||
}
|
||||
}
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
import org.hibernate.annotations.LazyCollection;
|
||||
import org.hibernate.annotations.LazyCollectionOption;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_floating_rates", uniqueConstraints = { @UniqueConstraint(columnNames = { "name" }, name = "unq_name") })
|
||||
public class FloatingRate extends AbstractPersistable<Long> {
|
||||
|
||||
@Column(name = "name", length = 200, unique = true, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "is_base_lending_rate", nullable = false)
|
||||
private boolean isBaseLendingRate;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean isActive;
|
||||
|
||||
@OrderBy(value = "fromDate,id")
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "floatingRate", orphanRemoval = true)
|
||||
private Set<FloatingRatePeriod> floatingRatePeriods;
|
||||
|
||||
@ManyToOne(optional = true)
|
||||
@JoinColumn(name = "createdby_id", nullable = false)
|
||||
private AppUser createdBy;
|
||||
|
||||
@ManyToOne(optional = true)
|
||||
@JoinColumn(name = "lastmodifiedby_id", nullable = false)
|
||||
private AppUser modifiedBy;
|
||||
|
||||
@Column(name = "created_date", nullable = false)
|
||||
private Date createdOn;
|
||||
|
||||
@Column(name = "lastmodified_date", nullable = false)
|
||||
private Date modifiedOn;
|
||||
|
||||
public FloatingRate() {
|
||||
|
||||
}
|
||||
|
||||
public FloatingRate(String name, boolean isBaseLendingRate,
|
||||
boolean isActive, Set<FloatingRatePeriod> floatingRatePeriods,
|
||||
AppUser createdBy, AppUser modifiedBy, Date createdOn,
|
||||
Date modifiedOn) {
|
||||
this.name = name;
|
||||
this.isBaseLendingRate = isBaseLendingRate;
|
||||
this.isActive = isActive;
|
||||
this.floatingRatePeriods = floatingRatePeriods;
|
||||
this.createdBy = createdBy;
|
||||
this.createdOn = createdOn;
|
||||
this.modifiedBy = modifiedBy;
|
||||
this.modifiedOn = modifiedOn;
|
||||
if (floatingRatePeriods != null) {
|
||||
for (FloatingRatePeriod ratePeriod : floatingRatePeriods) {
|
||||
ratePeriod.updateFloatingRate(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static FloatingRate createNew(AppUser currentUser,
|
||||
JsonCommand command) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
final boolean isBaseLendingRate = command
|
||||
.parameterExists("isBaseLendingRate") ? command
|
||||
.booleanPrimitiveValueOfParameterNamed("isBaseLendingRate")
|
||||
: false;
|
||||
final boolean isActive = command.parameterExists("isActive") ? command
|
||||
.booleanPrimitiveValueOfParameterNamed("isActive") : true;
|
||||
final Set<FloatingRatePeriod> floatingRatePeriods = getRatePeriods(
|
||||
currentUser, command);
|
||||
final LocalDate currentDate = DateUtils.getLocalDateOfTenant();
|
||||
|
||||
return new FloatingRate(name, isBaseLendingRate, isActive,
|
||||
floatingRatePeriods, currentUser, currentUser,
|
||||
currentDate.toDate(), currentDate.toDate());
|
||||
}
|
||||
|
||||
private static Set<FloatingRatePeriod> getRatePeriods(
|
||||
final AppUser currentUser, final JsonCommand command) {
|
||||
if (!command.parameterExists("ratePeriods")) {
|
||||
return null;
|
||||
}
|
||||
Set<FloatingRatePeriod> ratePeriods = new HashSet<>();
|
||||
JsonArray arrayOfParameterNamed = command
|
||||
.arrayOfParameterNamed("ratePeriods");
|
||||
for (final JsonElement ratePeriod : arrayOfParameterNamed) {
|
||||
final JsonObject ratePeriodObject = ratePeriod.getAsJsonObject();
|
||||
final JsonParserHelper helper = new JsonParserHelper();
|
||||
final Date fromDate = helper.extractLocalDateNamed("fromDate",
|
||||
ratePeriod, new HashSet<String>()).toDate();
|
||||
final BigDecimal interestRate = ratePeriodObject
|
||||
.get("interestRate").getAsBigDecimal();
|
||||
final boolean isDifferentialToBaseLendingRate = helper
|
||||
.parameterExists("isDifferentialToBaseLendingRate",
|
||||
ratePeriod) ? ratePeriodObject.get(
|
||||
"isDifferentialToBaseLendingRate").getAsBoolean() : false;
|
||||
final boolean isActive = true;
|
||||
final Date currentDate = DateUtils.getDateOfTenant();
|
||||
ratePeriods.add(new FloatingRatePeriod(fromDate, interestRate,
|
||||
isDifferentialToBaseLendingRate, isActive, currentUser,
|
||||
currentUser, currentDate, currentDate));
|
||||
}
|
||||
|
||||
return ratePeriods;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public boolean isBaseLendingRate() {
|
||||
return this.isBaseLendingRate;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.isActive;
|
||||
}
|
||||
|
||||
public Set<FloatingRatePeriod> getFloatingRatePeriods() {
|
||||
return this.floatingRatePeriods;
|
||||
}
|
||||
|
||||
public AppUser getCreatedBy() {
|
||||
return this.createdBy;
|
||||
}
|
||||
|
||||
public AppUser getModifiedBy() {
|
||||
return this.modifiedBy;
|
||||
}
|
||||
|
||||
public Date getCreatedOn() {
|
||||
return this.createdOn;
|
||||
}
|
||||
|
||||
public Date getModifiedOn() {
|
||||
return this.modifiedOn;
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command,
|
||||
final AppUser appUser) {
|
||||
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
|
||||
|
||||
if (command.isChangeInStringParameterNamed("name", this.name)) {
|
||||
final String newValue = command.stringValueOfParameterNamed("name");
|
||||
actualChanges.put("name", newValue);
|
||||
this.name = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed("isBaseLendingRate",
|
||||
this.isBaseLendingRate)) {
|
||||
final boolean newValue = command
|
||||
.booleanPrimitiveValueOfParameterNamed("isBaseLendingRate");
|
||||
actualChanges.put("isBaseLendingRate", newValue);
|
||||
this.isBaseLendingRate = newValue;
|
||||
}
|
||||
|
||||
if (command.isChangeInBooleanParameterNamed("isActive", this.isActive)) {
|
||||
final boolean newValue = command
|
||||
.booleanPrimitiveValueOfParameterNamed("isActive");
|
||||
actualChanges.put("isActive", newValue);
|
||||
this.isActive = newValue;
|
||||
}
|
||||
|
||||
final Set<FloatingRatePeriod> newRatePeriods = getRatePeriods(appUser,
|
||||
command);
|
||||
if (newRatePeriods != null && !newRatePeriods.isEmpty()) {
|
||||
updateRatePeriods(newRatePeriods, appUser);
|
||||
actualChanges.put("ratePeriods",
|
||||
command.jsonFragment("ratePeriods"));
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private void updateRatePeriods(
|
||||
final Set<FloatingRatePeriod> newRatePeriods, final AppUser appUser) {
|
||||
final LocalDate today = DateUtils.getLocalDateOfTenant();
|
||||
if (this.floatingRatePeriods != null) {
|
||||
for (FloatingRatePeriod ratePeriod : this.floatingRatePeriods) {
|
||||
LocalDate fromDate = LocalDate.fromDateFields(ratePeriod
|
||||
.getFromDate());
|
||||
if (fromDate.isAfter(today)) {
|
||||
ratePeriod.setActive(false);
|
||||
ratePeriod.setModifiedBy(appUser);
|
||||
ratePeriod.setModifiedOn(today.toDate());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (FloatingRatePeriod newRatePeriod : newRatePeriods) {
|
||||
newRatePeriod.updateFloatingRate(this);
|
||||
this.floatingRatePeriods.add(newRatePeriod);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<FloatingRatePeriodData> fetchInterestRates(
|
||||
final FloatingRateDTO floatingRateDTO) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = new ArrayList<>();
|
||||
FloatingRatePeriod previousPeriod = null;
|
||||
boolean addPeriodData = false;
|
||||
for (FloatingRatePeriod floatingRatePeriod : this.floatingRatePeriods) {
|
||||
if (floatingRatePeriod.isActive()) {
|
||||
// will enter
|
||||
if (applicableRates.isEmpty()
|
||||
&& floatingRateDTO.getStartDate().isBefore(
|
||||
floatingRatePeriod.fetchFromDate())) {
|
||||
if (floatingRateDTO.isFloatingInterestRate()) {
|
||||
addPeriodData = true;
|
||||
}
|
||||
if (previousPeriod != null) {
|
||||
applicableRates.add(previousPeriod
|
||||
.toData(floatingRateDTO));
|
||||
} else if (!addPeriodData) {
|
||||
applicableRates.add(floatingRatePeriod
|
||||
.toData(floatingRateDTO));
|
||||
}
|
||||
}
|
||||
if (addPeriodData) {
|
||||
applicableRates.add(floatingRatePeriod
|
||||
.toData(floatingRateDTO));
|
||||
}
|
||||
previousPeriod = floatingRatePeriod;
|
||||
}
|
||||
}
|
||||
if (applicableRates.isEmpty() && previousPeriod != null) {
|
||||
applicableRates.add(previousPeriod.toData(floatingRateDTO));
|
||||
}
|
||||
return applicableRates;
|
||||
}
|
||||
|
||||
}
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_floating_rates_periods")
|
||||
public class FloatingRatePeriod extends AbstractPersistable<Long> {
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "floating_rates_id", nullable = false)
|
||||
private FloatingRate floatingRate;
|
||||
|
||||
@Column(name = "from_date", nullable = false)
|
||||
private Date fromDate;
|
||||
|
||||
@Column(name = "interest_rate", scale = 6, precision = 19, nullable = false)
|
||||
private BigDecimal interestRate;
|
||||
|
||||
@Column(name = "is_differential_to_base_lending_rate", nullable = false)
|
||||
private boolean isDifferentialToBaseLendingRate;
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean isActive;
|
||||
|
||||
@ManyToOne(optional = true)
|
||||
@JoinColumn(name = "createdby_id", nullable = false)
|
||||
private AppUser createdBy;
|
||||
|
||||
@ManyToOne(optional = true)
|
||||
@JoinColumn(name = "lastmodifiedby_id", nullable = false)
|
||||
private AppUser modifiedBy;
|
||||
|
||||
@Column(name = "created_date", nullable = false)
|
||||
private Date createdOn;
|
||||
|
||||
@Column(name = "lastmodified_date", nullable = false)
|
||||
private Date modifiedOn;
|
||||
|
||||
public FloatingRatePeriod() {
|
||||
|
||||
}
|
||||
|
||||
public FloatingRatePeriod(Date fromDate, BigDecimal interestRate,
|
||||
boolean isDifferentialToBaseLendingRate, boolean isActive,
|
||||
AppUser createdBy, AppUser modifiedBy, Date createdOn,
|
||||
Date modifiedOn) {
|
||||
this.fromDate = fromDate;
|
||||
this.interestRate = interestRate;
|
||||
this.isDifferentialToBaseLendingRate = isDifferentialToBaseLendingRate;
|
||||
this.isActive = isActive;
|
||||
this.createdBy = createdBy;
|
||||
this.modifiedBy = modifiedBy;
|
||||
this.createdOn = createdOn;
|
||||
this.modifiedOn = modifiedOn;
|
||||
}
|
||||
|
||||
public void updateFloatingRate(FloatingRate floatingRate) {
|
||||
this.floatingRate = floatingRate;
|
||||
}
|
||||
|
||||
public FloatingRate getFloatingRatesId() {
|
||||
return this.floatingRate;
|
||||
}
|
||||
|
||||
public Date getFromDate() {
|
||||
return this.fromDate;
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRate() {
|
||||
return this.interestRate;
|
||||
}
|
||||
|
||||
public boolean isDifferentialToBaseLendingRate() {
|
||||
return this.isDifferentialToBaseLendingRate;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return this.isActive;
|
||||
}
|
||||
|
||||
public AppUser getCreatedBy() {
|
||||
return this.createdBy;
|
||||
}
|
||||
|
||||
public AppUser getModifiedBy() {
|
||||
return this.modifiedBy;
|
||||
}
|
||||
|
||||
public Date getCreatedOn() {
|
||||
return this.createdOn;
|
||||
}
|
||||
|
||||
public Date getModifiedOn() {
|
||||
return this.modifiedOn;
|
||||
}
|
||||
|
||||
public void setModifiedBy(AppUser modifiedBy) {
|
||||
this.modifiedBy = modifiedBy;
|
||||
}
|
||||
|
||||
public void setModifiedOn(Date modifiedOn) {
|
||||
this.modifiedOn = modifiedOn;
|
||||
}
|
||||
|
||||
public void setActive(boolean b) {
|
||||
this.isActive = b;
|
||||
}
|
||||
|
||||
public LocalDate fetchFromDate() {
|
||||
return new LocalDate(this.fromDate);
|
||||
}
|
||||
|
||||
public FloatingRatePeriodData toData(final FloatingRateDTO floatingRateDTO) {
|
||||
|
||||
BigDecimal interest = getInterestRate().add(
|
||||
floatingRateDTO.getInterestRateDiff());
|
||||
if (isDifferentialToBaseLendingRate()) {
|
||||
interest = interest.add(floatingRateDTO
|
||||
.fetchBaseRate(fetchFromDate()));
|
||||
}
|
||||
|
||||
return new FloatingRatePeriodData(getId(), getFromDate(), interest,
|
||||
isDifferentialToBaseLendingRate(), isActive(), getCreatedBy()
|
||||
.getUsername(), getCreatedOn(), getModifiedBy()
|
||||
.getUsername(), getModifiedOn());
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.domain;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
|
||||
public interface FloatingRateRepository extends
|
||||
JpaRepository<FloatingRate, Long>,
|
||||
JpaSpecificationExecutor<FloatingRate> {
|
||||
|
||||
@Query("from FloatingRate floatingRate where floatingRate.isBaseLendingRate = 1 and floatingRate.isActive = 1")
|
||||
FloatingRate retrieveBaseLendingRate();
|
||||
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.domain;
|
||||
|
||||
import org.mifosplatform.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FloatingRateRepositoryWrapper {
|
||||
|
||||
private final FloatingRateRepository floatingRateRepository;
|
||||
|
||||
@Autowired
|
||||
public FloatingRateRepositoryWrapper(
|
||||
final FloatingRateRepository floatingRateRepository) {
|
||||
this.floatingRateRepository = floatingRateRepository;
|
||||
}
|
||||
|
||||
public FloatingRate retrieveBaseLendingRate() {
|
||||
return this.floatingRateRepository.retrieveBaseLendingRate();
|
||||
}
|
||||
|
||||
public FloatingRate findOneWithNotFoundDetection(final Long id) {
|
||||
final FloatingRate floatingRate = this.floatingRateRepository
|
||||
.findOne(id);
|
||||
if (floatingRate == null) {
|
||||
throw new FloatingRateNotFoundException(id);
|
||||
}
|
||||
return floatingRate;
|
||||
}
|
||||
|
||||
public void save(FloatingRate newFloatingRate) {
|
||||
this.floatingRateRepository.save(newFloatingRate);
|
||||
}
|
||||
|
||||
public void saveAndFlush(FloatingRate floatingRateForUpdate) {
|
||||
this.floatingRateRepository.saveAndFlush(floatingRateForUpdate);
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
|
||||
|
||||
public class FloatingRateNotFoundException extends
|
||||
AbstractPlatformResourceNotFoundException {
|
||||
|
||||
public FloatingRateNotFoundException(final Long id) {
|
||||
super("error.msg.floatingrate.id.invalid",
|
||||
"Floating Rate with identifier " + id + " does not exist", id);
|
||||
}
|
||||
|
||||
public FloatingRateNotFoundException(final String globalisationMessageCode) {
|
||||
super(globalisationMessageCode, "Floating Rate does not exist");
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.handler;
|
||||
|
||||
import org.mifosplatform.commands.annotation.CommandType;
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRateWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "FLOATINGRATE", action = "CREATE")
|
||||
public class CreateFloatingRateCommandHandler implements
|
||||
NewCommandSourceHandler {
|
||||
|
||||
private final FloatingRateWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public CreateFloatingRateCommandHandler(
|
||||
final FloatingRateWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.createFloatingRate(command);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.handler;
|
||||
|
||||
import org.mifosplatform.commands.annotation.CommandType;
|
||||
import org.mifosplatform.commands.handler.NewCommandSourceHandler;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRateWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@CommandType(entity = "FLOATINGRATE", action = "UPDATE")
|
||||
public class UpdateFloatingRateCommandHandler implements
|
||||
NewCommandSourceHandler {
|
||||
|
||||
private final FloatingRateWritePlatformService writePlatformService;
|
||||
|
||||
@Autowired
|
||||
public UpdateFloatingRateCommandHandler(
|
||||
final FloatingRateWritePlatformService writePlatformService) {
|
||||
this.writePlatformService = writePlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult processCommand(final JsonCommand command) {
|
||||
return this.writePlatformService.updateFloatingRate(command);
|
||||
}
|
||||
}
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.serialization;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
|
||||
import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRateRepository;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
@Component
|
||||
public class FloatingRateDataValidator {
|
||||
|
||||
private final Set<String> supportedParametersForFloatingRates = new HashSet<>(
|
||||
Arrays.asList("name", "isBaseLendingRate", "isActive",
|
||||
"ratePeriods"));
|
||||
private final Set<String> supportedParametersForFloatingRatePeriods = new HashSet<>(
|
||||
Arrays.asList("fromDate", "interestRate",
|
||||
"isDifferentialToBaseLendingRate", "locale", "dateFormat"));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final FloatingRateRepository floatingRateRepository;
|
||||
|
||||
@Autowired
|
||||
public FloatingRateDataValidator(final FromJsonHelper fromApiJsonHelper,
|
||||
final FloatingRateRepository floatingRateRepository) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.floatingRateRepository = floatingRateRepository;
|
||||
}
|
||||
|
||||
public void validateForCreate(String json) {
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
|
||||
this.supportedParametersForFloatingRates);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
|
||||
dataValidationErrors).resource("floatingrate");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed("name",
|
||||
element);
|
||||
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||
.notExceedingLengthOf(200);
|
||||
|
||||
if (this.fromApiJsonHelper
|
||||
.parameterExists("isBaseLendingRate", element)) {
|
||||
final Boolean isBaseLendingRate = this.fromApiJsonHelper
|
||||
.extractBooleanNamed("isBaseLendingRate", element);
|
||||
|
||||
baseDataValidator.reset().parameter("isBaseLendingRate")
|
||||
.value(isBaseLendingRate).notNull();
|
||||
if (isBaseLendingRate == null) {
|
||||
baseDataValidator.reset().parameter("isBaseLendingRate")
|
||||
.trueOrFalseRequired(false);
|
||||
} else if (isBaseLendingRate) {
|
||||
FloatingRate baseLendingRate = this.floatingRateRepository
|
||||
.retrieveBaseLendingRate();
|
||||
if (baseLendingRate != null) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("isBaseLendingRate")
|
||||
.value(isBaseLendingRate)
|
||||
.failWithCode("baselendingrate.duplicate",
|
||||
"Base Lending Rate already exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("isActive", element)) {
|
||||
final Boolean isActive = this.fromApiJsonHelper
|
||||
.extractBooleanNamed("isActive", element);
|
||||
if (isActive == null) {
|
||||
baseDataValidator.reset().parameter("isActive")
|
||||
.trueOrFalseRequired(false);
|
||||
}
|
||||
}
|
||||
|
||||
validateRatePeriods(baseDataValidator, element);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void validateRatePeriods(DataValidatorBuilder baseDataValidator,
|
||||
JsonElement element) {
|
||||
if (this.fromApiJsonHelper.parameterExists("ratePeriods", element)) {
|
||||
final JsonArray ratePeriods = this.fromApiJsonHelper
|
||||
.extractJsonArrayNamed("ratePeriods", element);
|
||||
baseDataValidator.reset().parameter("ratePeriods")
|
||||
.value(ratePeriods).notBlank().jsonArrayNotEmpty();
|
||||
|
||||
if (ratePeriods != null) {
|
||||
List<LocalDate> fromDates = new ArrayList<>();
|
||||
for (int i = 0; i < ratePeriods.size(); i++) {
|
||||
final JsonElement ratePeriod = ratePeriods.get(i);
|
||||
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(
|
||||
ratePeriod.getAsJsonObject(),
|
||||
this.supportedParametersForFloatingRatePeriods);
|
||||
|
||||
final LocalDate fromDate = this.fromApiJsonHelper
|
||||
.extractLocalDateNamed("fromDate", ratePeriod);
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("fromDate")
|
||||
.parameterAtIndexArray("fromDate", i + 1)
|
||||
.value(fromDate)
|
||||
.notBlank()
|
||||
.validateDateAfter(
|
||||
DateUtils.getLocalDateOfTenant()
|
||||
.plusDays(1));
|
||||
if (fromDate != null) {
|
||||
fromDates.add(fromDate);
|
||||
}
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper
|
||||
.extractBigDecimalWithLocaleNamed("interestRate",
|
||||
ratePeriod);
|
||||
baseDataValidator.reset().parameter("interestRate")
|
||||
.parameterAtIndexArray("interestRate", i + 1)
|
||||
.value(interestRatePerPeriod).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(
|
||||
"isDifferentialToBaseLendingRate", ratePeriod)) {
|
||||
final Boolean isDifferentialToBaseLendingRate = this.fromApiJsonHelper
|
||||
.extractBooleanNamed(
|
||||
"isDifferentialToBaseLendingRate",
|
||||
ratePeriod);
|
||||
if (isDifferentialToBaseLendingRate == null) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(
|
||||
"isDifferentialToBaseLendingRate")
|
||||
.parameterAtIndexArray(
|
||||
"isDifferentialToBaseLendingRate",
|
||||
i + 1).trueOrFalseRequired(false);
|
||||
} else if (isDifferentialToBaseLendingRate) {
|
||||
FloatingRate baseLendingRate = this.floatingRateRepository
|
||||
.retrieveBaseLendingRate();
|
||||
if (baseLendingRate == null) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(
|
||||
"isDifferentialToBaseLendingRate")
|
||||
.parameterAtIndexArray(
|
||||
"isDifferentialToBaseLendingRate",
|
||||
i + 1)
|
||||
.value(isDifferentialToBaseLendingRate)
|
||||
.failWithCode(
|
||||
"no.baselending.rate.defined",
|
||||
"Base Lending Rate doesn't exists");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<LocalDate> uniqueFromDates = new HashSet<>(fromDates);
|
||||
if (fromDates.size() != uniqueFromDates.size()) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("fromDate")
|
||||
.failWithCode("multiple.same.date",
|
||||
"More than one entry in ratePeriods have same fromDate.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void validateForUpdate(String json,
|
||||
FloatingRate floatingRateForUpdate) {
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {
|
||||
}.getType();
|
||||
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json,
|
||||
this.supportedParametersForFloatingRates);
|
||||
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(
|
||||
dataValidationErrors).resource("floatingrate");
|
||||
|
||||
final JsonElement element = this.fromApiJsonHelper.parse(json);
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("name", element)) {
|
||||
final String name = this.fromApiJsonHelper.extractStringNamed(
|
||||
"name", element);
|
||||
baseDataValidator.reset().parameter("name").value(name).notBlank()
|
||||
.notExceedingLengthOf(200);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper
|
||||
.parameterExists("isBaseLendingRate", element)) {
|
||||
final Boolean isBaseLendingRate = this.fromApiJsonHelper
|
||||
.extractBooleanNamed("isBaseLendingRate", element);
|
||||
|
||||
baseDataValidator.reset().parameter("isBaseLendingRate")
|
||||
.value(isBaseLendingRate).notNull();
|
||||
if (isBaseLendingRate == null) {
|
||||
baseDataValidator.reset().parameter("isBaseLendingRate")
|
||||
.trueOrFalseRequired(false);
|
||||
} else if (isBaseLendingRate) {
|
||||
FloatingRate baseLendingRate = this.floatingRateRepository
|
||||
.retrieveBaseLendingRate();
|
||||
if (baseLendingRate != null
|
||||
&& baseLendingRate.getId() != floatingRateForUpdate
|
||||
.getId()) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("isBaseLendingRate")
|
||||
.value(isBaseLendingRate)
|
||||
.failWithCode("baselendingrate.duplicate",
|
||||
"Base Lending Rate already exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("isActive", element)) {
|
||||
final Boolean isActive = this.fromApiJsonHelper
|
||||
.extractBooleanNamed("isActive", element);
|
||||
if (isActive == null) {
|
||||
baseDataValidator.reset().parameter("isActive")
|
||||
.trueOrFalseRequired(false);
|
||||
}
|
||||
}
|
||||
|
||||
validateRatePeriods(baseDataValidator, element);
|
||||
|
||||
throwExceptionIfValidationWarningsExist(dataValidationErrors);
|
||||
}
|
||||
|
||||
private void throwExceptionIfValidationWarningsExist(
|
||||
final List<ApiParameterError> dataValidationErrors) {
|
||||
if (!dataValidationErrors.isEmpty()) {
|
||||
throw new PlatformApiDataValidationException(
|
||||
"validation.msg.validation.errors.exist",
|
||||
"Validation errors exist.", dataValidationErrors);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.service;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
|
||||
public interface FloatingRateWritePlatformService {
|
||||
|
||||
public CommandProcessingResult createFloatingRate(JsonCommand command);
|
||||
|
||||
public CommandProcessingResult updateFloatingRate(JsonCommand command);
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRateRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.floatingrates.serialization.FloatingRateDataValidator;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class FloatingRateWritePlatformServiceImpl implements
|
||||
FloatingRateWritePlatformService {
|
||||
|
||||
private final static Logger logger = LoggerFactory
|
||||
.getLogger(FloatingRateWritePlatformServiceImpl.class);
|
||||
private final PlatformSecurityContext context;
|
||||
private final FloatingRateDataValidator fromApiJsonDeserializer;
|
||||
private final FloatingRateRepositoryWrapper floatingRateRepository;
|
||||
|
||||
@Autowired
|
||||
public FloatingRateWritePlatformServiceImpl(
|
||||
final PlatformSecurityContext context,
|
||||
final FloatingRateDataValidator fromApiJsonDeserializer,
|
||||
final FloatingRateRepositoryWrapper floatingRateRepository) {
|
||||
this.context = context;
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.floatingRateRepository = floatingRateRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult createFloatingRate(final JsonCommand command) {
|
||||
try {
|
||||
this.fromApiJsonDeserializer.validateForCreate(command.json());
|
||||
final AppUser currentUser = this.context.authenticatedUser();
|
||||
final FloatingRate newFloatingRate = FloatingRate.createNew(
|
||||
currentUser, command);
|
||||
this.floatingRateRepository.save(newFloatingRate);
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(newFloatingRate.getId()) //
|
||||
.build();
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult updateFloatingRate(final JsonCommand command) {
|
||||
try {
|
||||
final FloatingRate floatingRateForUpdate = this.floatingRateRepository
|
||||
.findOneWithNotFoundDetection(command.entityId());
|
||||
this.fromApiJsonDeserializer.validateForUpdate(command.json(),
|
||||
floatingRateForUpdate);
|
||||
final AppUser currentUser = this.context.authenticatedUser();
|
||||
final Map<String, Object> changes = floatingRateForUpdate.update(
|
||||
command, currentUser);
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
this.floatingRateRepository.save(floatingRateForUpdate);
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
.withCommandId(command.commandId()) //
|
||||
.withEntityId(command.entityId()) //
|
||||
.with(changes) //
|
||||
.build();
|
||||
} catch (final DataIntegrityViolationException dve) {
|
||||
handleDataIntegrityIssues(command, dve);
|
||||
return CommandProcessingResult.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleDataIntegrityIssues(final JsonCommand command,
|
||||
final DataIntegrityViolationException dve) {
|
||||
final Throwable realCause = dve.getMostSpecificCause();
|
||||
|
||||
if (realCause.getMessage().contains("unq_name")) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
throw new PlatformDataIntegrityException(
|
||||
"error.msg.floatingrates.duplicate.name",
|
||||
"Floating Rate with name `" + name + "` already exists",
|
||||
"name", name);
|
||||
}
|
||||
|
||||
if (realCause.getMessage().contains("unq_rate_period")) {
|
||||
throw new PlatformDataIntegrityException(
|
||||
"error.msg.floatingrates.duplicate.active.fromdate",
|
||||
"Attempt to add multiple floating rate periods with same fromdate",
|
||||
"fromdate", "");
|
||||
}
|
||||
|
||||
logAsErrorUnexpectedDataIntegrityException(dve);
|
||||
throw new PlatformDataIntegrityException(
|
||||
"error.msg.floatingrates.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
private void logAsErrorUnexpectedDataIntegrityException(
|
||||
DataIntegrityViolationException dve) {
|
||||
logger.error(dve.getMessage(), dve);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
|
||||
public interface FloatingRatesReadPlatformService {
|
||||
|
||||
List<FloatingRateData> retrieveAll();
|
||||
|
||||
List<FloatingRateData> retrieveLookupActive();
|
||||
|
||||
FloatingRateData retrieveOne(Long floatingRateId);
|
||||
|
||||
List<FloatingRateData> retrieveAllActive();
|
||||
|
||||
FloatingRateData retrieveBaseLendingRate();
|
||||
|
||||
List<InterestRatePeriodData> retrieveInterestRatePeriods(Long floatingRateId);
|
||||
|
||||
}
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
/**
|
||||
* 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.portfolio.floatingrates.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.service.RoutingDataSource;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.exception.FloatingRateNotFoundException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class FloatingRatesReadPlatformServiceImpl implements
|
||||
FloatingRatesReadPlatformService {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public FloatingRatesReadPlatformServiceImpl(
|
||||
final RoutingDataSource dataSource) {
|
||||
this.jdbcTemplate = new JdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FloatingRateData> retrieveAll() {
|
||||
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(false);
|
||||
final String sql = "select " + rateMapper.schema();
|
||||
return this.jdbcTemplate.query(sql, rateMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FloatingRateData> retrieveAllActive() {
|
||||
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(false);
|
||||
final String sql = "select " + rateMapper.schema()
|
||||
+ " where rate.is_active = 1 ";
|
||||
return this.jdbcTemplate.query(sql, rateMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FloatingRateData> retrieveLookupActive() {
|
||||
FloatingRateLookupMapper rateMapper = new FloatingRateLookupMapper();
|
||||
final String sql = "select " + rateMapper.schema()
|
||||
+ " where rate.is_active = 1 ";
|
||||
return this.jdbcTemplate.query(sql, rateMapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingRateData retrieveOne(final Long floatingRateId) {
|
||||
try {
|
||||
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(true);
|
||||
final String sql = "select " + rateMapper.schema()
|
||||
+ " where rate.id = ?";
|
||||
return this.jdbcTemplate.queryForObject(sql, rateMapper,
|
||||
new Object[] { floatingRateId });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new FloatingRateNotFoundException(floatingRateId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InterestRatePeriodData> retrieveInterestRatePeriods(
|
||||
final Long floatingRateId) {
|
||||
try {
|
||||
FloatingInterestRatePeriodRowMapper mapper = new FloatingInterestRatePeriodRowMapper();
|
||||
return this.jdbcTemplate.query(mapper.schema(), mapper,
|
||||
new Object[] { floatingRateId });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new FloatingRateNotFoundException(floatingRateId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingRateData retrieveBaseLendingRate() {
|
||||
try {
|
||||
FloatingRateRowMapper rateMapper = new FloatingRateRowMapper(true);
|
||||
final String sql = "select "
|
||||
+ rateMapper.schema()
|
||||
+ " where rate.is_base_lending_rate = 1 and rate.is_active = 1";
|
||||
return this.jdbcTemplate.queryForObject(sql, rateMapper);
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new FloatingRateNotFoundException(
|
||||
"error.msg.floatingrate.base.lending.rate.not.found");
|
||||
}
|
||||
}
|
||||
|
||||
private final class FloatingRateRowMapper implements
|
||||
RowMapper<FloatingRateData> {
|
||||
|
||||
private final boolean addRatePeriods;
|
||||
|
||||
private final StringBuilder sqlQuery = new StringBuilder()
|
||||
.append("rate.id as id, ")
|
||||
.append("rate.name as name, ")
|
||||
.append("rate.is_base_lending_rate as isBaseLendingRate, ")
|
||||
.append("rate.is_active as isActive, ")
|
||||
.append("crappu.username as createdBy, ")
|
||||
.append("rate.created_date as createdOn, ")
|
||||
.append("moappu.username as modifiedBy, ")
|
||||
.append("rate.lastmodified_date as modifiedOn ")
|
||||
.append("FROM m_floating_rates as rate ")
|
||||
.append("LEFT JOIN m_appuser as crappu on rate.createdby_id = crappu.id ")
|
||||
.append("LEFT JOIN m_appuser as moappu on rate.lastmodifiedby_id = moappu.id ");
|
||||
|
||||
public FloatingRateRowMapper(final boolean addRatePeriods) {
|
||||
this.addRatePeriods = addRatePeriods;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FloatingRateData mapRow(final ResultSet rs,
|
||||
@SuppressWarnings("unused") final int rowNum)
|
||||
throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final String name = rs.getString("name");
|
||||
final boolean isBaseLendingRate = rs
|
||||
.getBoolean("isBaseLendingRate");
|
||||
final boolean isActive = rs.getBoolean("isActive");
|
||||
final String createdBy = rs.getString("createdBy");
|
||||
final Date createdOn = rs.getDate("createdOn");
|
||||
final String modifiedBy = rs.getString("modifiedBy");
|
||||
final Date modifiedOn = rs.getDate("modifiedOn");
|
||||
List<FloatingRatePeriodData> ratePeriods = null;
|
||||
if (addRatePeriods) {
|
||||
FloatingRatePeriodRowMapper ratePeriodMapper = new FloatingRatePeriodRowMapper();
|
||||
final String sql = "select "
|
||||
+ ratePeriodMapper.schema()
|
||||
+ " where period.is_active = 1 and period.floating_rates_id = ? "
|
||||
+ " order by period.from_date desc ";
|
||||
ratePeriods = jdbcTemplate.query(sql, ratePeriodMapper,
|
||||
new Object[] { id });
|
||||
}
|
||||
return new FloatingRateData(id, name, isBaseLendingRate, isActive,
|
||||
createdBy, createdOn, modifiedBy, modifiedOn, ratePeriods,
|
||||
null);
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return sqlQuery.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private final class FloatingRatePeriodRowMapper implements
|
||||
RowMapper<FloatingRatePeriodData> {
|
||||
|
||||
private final StringBuilder sqlQuery = new StringBuilder()
|
||||
.append("period.id as id, ")
|
||||
.append("period.from_date as fromDate, ")
|
||||
.append("period.interest_rate as interestRate, ")
|
||||
.append("period.is_differential_to_base_lending_rate as isDifferentialToBaseLendingRate, ")
|
||||
.append("period.is_active as isActive, ")
|
||||
.append("crappu.username as createdBy, ")
|
||||
.append("period.created_date as createdOn, ")
|
||||
.append("moappu.username as modifiedBy, ")
|
||||
.append("period.lastmodified_date as modifiedOn ")
|
||||
.append("FROM m_floating_rates_periods as period ")
|
||||
.append("LEFT JOIN m_appuser as crappu on period.createdby_id = crappu.id ")
|
||||
.append("LEFT JOIN m_appuser as moappu on period.lastmodifiedby_id = moappu.id ");
|
||||
|
||||
@Override
|
||||
public FloatingRatePeriodData mapRow(final ResultSet rs,
|
||||
@SuppressWarnings("unused") final int rowNum)
|
||||
throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final Date fromDate = rs.getDate("fromDate");
|
||||
final BigDecimal interestRate = rs.getBigDecimal("interestRate");
|
||||
final boolean isDifferentialToBaseLendingRate = rs
|
||||
.getBoolean("isDifferentialToBaseLendingRate");
|
||||
final boolean isActive = rs.getBoolean("isActive");
|
||||
final String createdBy = rs.getString("createdBy");
|
||||
final Date createdOn = rs.getDate("createdOn");
|
||||
final String modifiedBy = rs.getString("modifiedBy");
|
||||
final Date modifiedOn = rs.getDate("modifiedOn");
|
||||
return new FloatingRatePeriodData(id, fromDate, interestRate,
|
||||
isDifferentialToBaseLendingRate, isActive, createdBy,
|
||||
createdOn, modifiedBy, modifiedOn);
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return sqlQuery.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private final class FloatingRateLookupMapper implements
|
||||
RowMapper<FloatingRateData> {
|
||||
|
||||
private final StringBuilder sqlQuery = new StringBuilder()
|
||||
.append("rate.id as id, ").append("rate.name as name, ")
|
||||
.append("rate.is_base_lending_rate as isBaseLendingRate ")
|
||||
.append("FROM m_floating_rates as rate ");
|
||||
|
||||
@Override
|
||||
public FloatingRateData mapRow(final ResultSet rs,
|
||||
@SuppressWarnings("unused") final int rowNum)
|
||||
throws SQLException {
|
||||
final Long id = rs.getLong("id");
|
||||
final String name = rs.getString("name");
|
||||
final boolean isBaseLendingRate = rs
|
||||
.getBoolean("isBaseLendingRate");
|
||||
return new FloatingRateData(id, name, isBaseLendingRate, true,
|
||||
null, null, null, null, null, null);
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return sqlQuery.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private final class FloatingInterestRatePeriodRowMapper implements
|
||||
RowMapper<InterestRatePeriodData> {
|
||||
|
||||
private final StringBuilder sqlQuery = new StringBuilder()
|
||||
.append("select ")
|
||||
.append(" linkedrateperiods.from_date as linkedrateperiods_from_date, ")
|
||||
.append(" linkedrateperiods.interest_rate as linkedrateperiods_interest_rate, ")
|
||||
.append(" linkedrateperiods.is_differential_to_base_lending_rate as linkedrateperiods_is_differential_to_base_lending_rate, ")
|
||||
.append(" baserate.from_date as baserate_from_date, ")
|
||||
.append(" baserate.interest_rate as baserate_interest_rate ")
|
||||
.append("from m_floating_rates as linkedrate ")
|
||||
.append("left join m_floating_rates_periods as linkedrateperiods on (linkedrate.id = linkedrateperiods.floating_rates_id and linkedrateperiods.is_active = 1) ")
|
||||
.append("left join ( ")
|
||||
.append(" select blr.name, ")
|
||||
.append(" blr.is_base_lending_rate, ")
|
||||
.append(" blr.is_active, ")
|
||||
.append(" blrperiods.from_date, ")
|
||||
.append(" blrperiods.interest_rate ")
|
||||
.append(" from m_floating_rates as blr ")
|
||||
.append(" left join m_floating_rates_periods as blrperiods on (blr.id = blrperiods.floating_rates_id and blrperiods.is_active = 1) ")
|
||||
.append(" where blr.is_base_lending_rate = 1 and blr.is_active = 1 ")
|
||||
.append(") as baserate on (linkedrateperiods.is_differential_to_base_lending_rate = 1 and linkedrate.is_base_lending_rate = 0) ")
|
||||
.append("where (baserate.from_date is null ")
|
||||
.append(" or baserate.from_date = (select MAX(b.from_date) ")
|
||||
.append(" from (select blr.name, ")
|
||||
.append(" blr.is_base_lending_rate, ")
|
||||
.append(" blr.is_active, ")
|
||||
.append(" blrperiods.from_date, ")
|
||||
.append(" blrperiods.interest_rate ")
|
||||
.append(" from m_floating_rates as blr ")
|
||||
.append(" left join m_floating_rates_periods as blrperiods on (blr.id = blrperiods.floating_rates_id and blrperiods.is_active = 1) ")
|
||||
.append(" where blr.is_base_lending_rate = 1 and blr.is_active = 1 ")
|
||||
.append(" ) as b ")
|
||||
.append(" where b.from_date <= linkedrateperiods.from_date)) ")
|
||||
.append("and linkedrate.id = ? ")
|
||||
.append("order by linkedratePeriods_from_date desc ");
|
||||
|
||||
@Override
|
||||
public InterestRatePeriodData mapRow(final ResultSet rs,
|
||||
@SuppressWarnings("unused") final int rowNum)
|
||||
throws SQLException {
|
||||
final Date fromDate = rs.getDate("linkedrateperiods_from_date");
|
||||
final BigDecimal interestRate = rs
|
||||
.getBigDecimal("linkedrateperiods_interest_rate");
|
||||
final boolean isDifferentialToBLR = rs
|
||||
.getBoolean("linkedrateperiods_is_differential_to_base_lending_rate");
|
||||
final Date blrFromDate = rs.getDate("baserate_from_date");
|
||||
final BigDecimal blrInterestRate = rs
|
||||
.getBigDecimal("baserate_interest_rate");
|
||||
|
||||
return new InterestRatePeriodData(fromDate, interestRate,
|
||||
isDifferentialToBLR, blrFromDate, blrInterestRate);
|
||||
}
|
||||
|
||||
public String schema() {
|
||||
return sqlQuery.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+7
-1
@@ -34,6 +34,10 @@ public interface LoanApiConstants {
|
||||
public static final String interestRecalculationCompoundingMethodParameterName = "interestRecalculationCompoundingMethod";
|
||||
public static final String rescheduleStrategyMethodParameterName = "rescheduleStrategyMethod";
|
||||
|
||||
// Floating interest rate related
|
||||
public static final String interestRateDifferentialParameterName = "interestRateDifferential";
|
||||
public static final String isFloatingInterestRateParameterName = "isFloatingInterestRate";
|
||||
|
||||
// Error codes
|
||||
public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_INTEREST_CALCULATION_TYPE= "loancharge.with.calculation.type.interest.not.allowed";
|
||||
public static final String LOAN_CHARGE_CAN_NOT_BE_ADDED_WITH_PRINCIPAL_CALCULATION_TYPE= "loancharge.with.calculation.type.principal.not.allowed";
|
||||
@@ -44,6 +48,8 @@ public interface LoanApiConstants {
|
||||
public static final String APPROVED_AMOUNT_IS_LESS_THAN_SUM_OF_TRANCHES = "sum.of.multi.disburse.amounts.must.be.equal.to.or.lesser.than.approved.principal";
|
||||
public static final String DISBURSEMENT_DATES_NOT_IN_ORDER = "disbursements.should.be.ordered.based.on.their.disbursement.dates";
|
||||
public static final String DISBURSEMENT_DATE_BEFORE_ERROR = "disbursement.date.of.tranche.cannot.be.before.expected.disbursement.date";
|
||||
|
||||
|
||||
public static final String isFloatingInterestRate = "isFloatingInterestRate";
|
||||
public static final String interestRateDifferential = "interestRateDifferential";
|
||||
|
||||
}
|
||||
|
||||
+14
-4
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.api;
|
||||
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestType;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -59,6 +61,7 @@ import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.client.data.ClientData;
|
||||
import org.mifosplatform.portfolio.collateral.data.CollateralData;
|
||||
import org.mifosplatform.portfolio.collateral.service.CollateralReadPlatformService;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.fund.service.FundReadPlatformService;
|
||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
@@ -85,6 +88,7 @@ import org.mifosplatform.portfolio.loanaccount.service.LoanChargeReadPlatformSer
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||
import org.mifosplatform.portfolio.note.data.NoteData;
|
||||
@@ -105,7 +109,7 @@ import com.google.gson.JsonElement;
|
||||
public class LoansApiResource {
|
||||
|
||||
private final Set<String> LOAN_DATA_PARAMETERS = new HashSet<>(Arrays.asList("id", "accountNo", "status", "externalId", "clientId",
|
||||
"group", "loanProductId", "loanProductName", "loanProductDescription", "fundId", "fundName", "loanPurposeId",
|
||||
"group", "loanProductId", "loanProductName", "loanProductDescription", "isLoanProductLinkedToFloatingRate", "fundId", "fundName", "loanPurposeId",
|
||||
"loanPurposeName", "loanOfficerId", "loanOfficerName", "currency", "principal", "totalOverpaid", "inArrearsTolerance",
|
||||
"termFrequency", "termPeriodFrequencyType", "numberOfRepayments", "repaymentEvery", "interestRatePerPeriod",
|
||||
"annualInterestRate", "repaymentFrequencyType", "transactionProcessingStrategyId", "transactionProcessingStrategyName",
|
||||
@@ -117,7 +121,7 @@ public class LoansApiResource {
|
||||
"repaymentFrequencyDaysOfWeekTypeOptions", "termFrequencyTypeOptions", "interestRateFrequencyTypeOptions", "fundOptions",
|
||||
"repaymentStrategyOptions", "chargeOptions", "loanOfficerOptions", "loanPurposeOptions", "loanCollateralOptions",
|
||||
"chargeTemplate", "calendarOptions", "syncDisbursementWithMeeting", "loanCounter", "loanProductCounter", "notes",
|
||||
"accountLinkingOptions", "linkedAccount"));
|
||||
"accountLinkingOptions", "linkedAccount", "interestRateDifferential", "isFloatingInterestRate", "interestRatesPeriods"));
|
||||
|
||||
private final Set<String> LOAN_APPROVAL_DATA_PARAMETERS = new HashSet<>(Arrays.asList("approvalDate", "approvalAmount"));
|
||||
private final String resourceNameForPermissions = "LOAN";
|
||||
@@ -354,6 +358,8 @@ public class LoansApiResource {
|
||||
compoundingCalendarData);
|
||||
}
|
||||
|
||||
Collection<InterestRatePeriodData> interestRatesPeriods = this.loanReadPlatformService.retrieveLoanInterestRatePeriodData(loanId);
|
||||
|
||||
Collection<LoanTransactionData> loanRepayments = null;
|
||||
LoanScheduleData repaymentSchedule = null;
|
||||
Collection<LoanChargeData> charges = null;
|
||||
@@ -488,7 +494,11 @@ public class LoansApiResource {
|
||||
interestRateFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
|
||||
|
||||
amortizationTypeOptions = this.dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
|
||||
interestTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
|
||||
if(product.isLinkedToFloatingInterestRates()){
|
||||
interestTypeOptions = Arrays.asList(interestType(InterestMethod.DECLINING_BALANCE));
|
||||
}else {
|
||||
interestTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestTypeOptions();
|
||||
}
|
||||
interestCalculationPeriodTypeOptions = this.dropdownReadPlatformService.retrieveLoanInterestRateCalculatedInPeriodOptions();
|
||||
|
||||
fundOptions = this.fundReadPlatformService.retrieveAllFunds();
|
||||
@@ -537,7 +547,7 @@ public class LoansApiResource {
|
||||
null, null, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
|
||||
interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
|
||||
loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
|
||||
overdueCharges, paidInAdvanceTemplate);
|
||||
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods);
|
||||
|
||||
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
|
||||
mandatoryResponseParameters);
|
||||
|
||||
+86
-42
@@ -27,6 +27,7 @@ import org.mifosplatform.portfolio.account.data.PortfolioAccountData;
|
||||
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.collateral.data.CollateralData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
@@ -64,6 +65,7 @@ public class LoanAccountData {
|
||||
private final Long loanProductId;
|
||||
private final String loanProductName;
|
||||
private final String loanProductDescription;
|
||||
private final boolean isLoanProductLinkedToFloatingRate;
|
||||
private final Long fundId;
|
||||
private final String fundName;
|
||||
private final Long loanPurposeId;
|
||||
@@ -88,6 +90,8 @@ public class LoanAccountData {
|
||||
private final BigDecimal interestRatePerPeriod;
|
||||
private final EnumOptionData interestRateFrequencyType;
|
||||
private final BigDecimal annualInterestRate;
|
||||
private final boolean isFloatingInterestRate;
|
||||
private final BigDecimal interestRateDifferential;
|
||||
|
||||
// settings
|
||||
private final EnumOptionData amortizationType;
|
||||
@@ -182,6 +186,8 @@ public class LoanAccountData {
|
||||
|
||||
// Paid In Advance
|
||||
private final PaidInAdvanceData paidInAdvance;
|
||||
|
||||
private final Collection<InterestRatePeriodData> interestRatesPeriods;
|
||||
|
||||
/**
|
||||
* Used to produce a {@link LoanAccountData} with only collateral options
|
||||
@@ -200,6 +206,7 @@ public class LoanAccountData {
|
||||
final Long loanProductId = null;
|
||||
final String loanProductName = null;
|
||||
final String loanProductDescription = null;
|
||||
final boolean isLoanProductLinkedToFloatingRate = false;
|
||||
final Long fundId = null;
|
||||
final String fundName = null;
|
||||
final Long loanPurposeId = null;
|
||||
@@ -225,6 +232,8 @@ public class LoanAccountData {
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final EnumOptionData interestType = null;
|
||||
final boolean isFloatingInterestRate = false;
|
||||
final BigDecimal interestRateDifferential = null;
|
||||
final EnumOptionData interestCalculationPeriodType = null;
|
||||
final LocalDate expectedFirstRepaymentOnDate = null;
|
||||
final Integer graceOnPrincipalPayment = null;
|
||||
@@ -283,13 +292,15 @@ public class LoanAccountData {
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
final Boolean createStandingInstructionAtDisbursement = null;
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, isFloatingInterestRate,
|
||||
interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
|
||||
collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
@@ -300,7 +311,7 @@ public class LoanAccountData {
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
|
||||
}
|
||||
|
||||
@@ -320,6 +331,7 @@ public class LoanAccountData {
|
||||
final Long loanProductId = null;
|
||||
final String loanProductName = null;
|
||||
final String loanProductDescription = null;
|
||||
final boolean isLoanProductLinkedToFloatingRate = false;
|
||||
final Long fundId = null;
|
||||
final String fundName = null;
|
||||
final Long loanPurposeId = null;
|
||||
@@ -345,6 +357,8 @@ public class LoanAccountData {
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final EnumOptionData interestType = null;
|
||||
final boolean isFloatingInterestRate = false;
|
||||
final BigDecimal interestRateDifferential = null;
|
||||
final EnumOptionData interestCalculationPeriodType = null;
|
||||
final LocalDate expectedFirstRepaymentOnDate = null;
|
||||
final Integer graceOnPrincipalPayment = null;
|
||||
@@ -404,13 +418,15 @@ public class LoanAccountData {
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
final Boolean createStandingInstructionAtDisbursement = null;
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
||||
isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
|
||||
collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
@@ -421,20 +437,21 @@ public class LoanAccountData {
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, maxOutstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
|
||||
}
|
||||
|
||||
public static LoanAccountData populateClientDefaults(final LoanAccountData acc, final LoanAccountData clientAcc) {
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, clientAcc.clientId, clientAcc.clientName,
|
||||
clientAcc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription,
|
||||
clientAcc.clientOfficeId, acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate,
|
||||
acc.fundId, acc.fundName, acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency,
|
||||
acc.proposedPrincipal, acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
clientAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
@@ -447,7 +464,7 @@ public class LoanAccountData {
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -467,6 +484,7 @@ public class LoanAccountData {
|
||||
final Long loanProductId = null;
|
||||
final String loanProductName = null;
|
||||
final String loanProductDescription = null;
|
||||
final boolean isLoanProductLinkedToFloatingRate = false;
|
||||
final Long fundId = null;
|
||||
final String fundName = null;
|
||||
final Long loanPurposeId = null;
|
||||
@@ -492,6 +510,8 @@ public class LoanAccountData {
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final EnumOptionData interestType = null;
|
||||
final boolean isFloatingInterestRate = false;
|
||||
final BigDecimal interestRateDifferential = null;
|
||||
final EnumOptionData interestCalculationPeriodType = null;
|
||||
final LocalDate expectedFirstRepaymentOnDate = null;
|
||||
final Integer graceOnPrincipalPayment = null;
|
||||
@@ -551,13 +571,15 @@ public class LoanAccountData {
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
final Boolean createStandingInstructionAtDisbursement = null;
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, principal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionProcessingStrategyId, transactionProcessingStrategyName, amortizationType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType,
|
||||
interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
||||
isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType,
|
||||
expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged,
|
||||
interestChargedFromDate, timeline, summary, feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges,
|
||||
collateral, guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
@@ -568,20 +590,21 @@ public class LoanAccountData {
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, maxOutstandingBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
|
||||
}
|
||||
|
||||
public static LoanAccountData populateGroupDefaults(final LoanAccountData acc, final LoanAccountData groupAcc) {
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
groupAcc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
groupAcc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
groupAcc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
@@ -594,7 +617,7 @@ public class LoanAccountData {
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
|
||||
}
|
||||
|
||||
@@ -712,14 +735,16 @@ public class LoanAccountData {
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
final Boolean createStandingInstructionAtDisbursement = null;
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType,
|
||||
product.getId(), product.getName(), product.getDescription(), product.getFundId(), product.getFundName(), loanPurposeId,
|
||||
product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(), product.getFundId(), product.getFundName(), loanPurposeId,
|
||||
loanPurposeName, loanOfficerId, loanOfficerName, product.getCurrency(), proposedPrincipal, principal, principal,
|
||||
totalOverpaid, product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, numberOfRepayments,
|
||||
product.getRepaymentEvery(), product.getRepaymentFrequencyType(), null, null, product.getTransactionProcessingStrategyId(),
|
||||
transactionProcessingStrategyName, product.getAmortizationType(), interestRatePerPeriod,
|
||||
product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
|
||||
product.isFloatingInterestRateCalculationAllowed(), product.getDefaultDifferentialLendingRate(),
|
||||
product.getInterestCalculationPeriodType(), expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(),
|
||||
product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), interestChargedFromDate, timeline, summary,
|
||||
feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
|
||||
@@ -732,7 +757,7 @@ public class LoanAccountData {
|
||||
product.getOutstandingLoanBalance(), emiAmountVariations, memberVariations, product, inArrears,
|
||||
product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), isNPA, product.getDaysInMonthType(),
|
||||
product.getDaysInYearType(), product.isInterestRecalculationEnabled(), product.toLoanInterestRecalculationData(),
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance);
|
||||
originalSchedule, createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
}
|
||||
|
||||
public static LoanAccountData populateLoanProductDefaults(final LoanAccountData acc, final LoanProductData product) {
|
||||
@@ -769,13 +794,14 @@ public class LoanAccountData {
|
||||
}
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(), product.getFundId(),
|
||||
acc.group, acc.loanType, product.getId(), product.getName(), product.getDescription(), product.isLinkedToFloatingInterestRates(), product.getFundId(),
|
||||
product.getFundName(), acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName,
|
||||
product.getCurrency(), product.getPrincipal(), product.getPrincipal(), product.getPrincipal(), acc.totalOverpaid,
|
||||
product.getInArrearsTolerance(), termFrequency, termPeriodFrequencyType, product.getNumberOfRepayments(),
|
||||
product.getRepaymentEvery(), product.getRepaymentFrequencyType(), null, null, product.getTransactionProcessingStrategyId(),
|
||||
product.getTransactionProcessingStrategyName(), product.getAmortizationType(), product.getInterestRatePerPeriod(),
|
||||
product.getInterestRateFrequencyType(), product.getAnnualInterestRate(), product.getInterestType(),
|
||||
product.isFloatingInterestRateCalculationAllowed(), product.getDefaultDifferentialLendingRate(),
|
||||
product.getInterestCalculationPeriodType(), acc.expectedFirstRepaymentOnDate, product.getGraceOnPrincipalPayment(),
|
||||
product.getGraceOnInterestPayment(), product.getGraceOnInterestCharged(), acc.interestChargedFromDate, acc.timeline,
|
||||
acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors,
|
||||
@@ -787,7 +813,8 @@ public class LoanAccountData {
|
||||
product.canDefineInstallmentAmount(), acc.fixedEmiAmount, product.getOutstandingLoanBalance(), acc.emiAmountVariations,
|
||||
acc.memberVariations, product, acc.inArrears, product.getGraceOnArrearsAgeing(), product.overdueFeeCharges(), acc.isNPA,
|
||||
product.getDaysInMonthType(), product.getDaysInYearType(), product.isInterestRecalculationEnabled(),
|
||||
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement, paidInAdvance);
|
||||
product.toLoanInterestRecalculationData(), acc.originalSchedule, acc.createStandingInstructionAtDisbursement,
|
||||
paidInAdvance, acc.interestRatesPeriods);
|
||||
|
||||
}
|
||||
|
||||
@@ -798,6 +825,7 @@ public class LoanAccountData {
|
||||
public static LoanAccountData basicLoanDetails(final Long id, final String accountNo, final LoanStatusEnumData status,
|
||||
final String externalId, final Long clientId, final String clientName, final Long clientOfficeId, final GroupGeneralData group,
|
||||
final EnumOptionData loanType, final Long loanProductId, final String loanProductName, final String loanProductDescription,
|
||||
final boolean isLoanProductLinkedToFloatingRate,
|
||||
final Long fundId, final String fundName, final Long loanPurposeId, final String loanPurposeName, final Long loanOfficerId,
|
||||
final String loanOfficerName, final CurrencyData currencyData, final BigDecimal proposedPrincipal, final BigDecimal principal,
|
||||
final BigDecimal approvedPrincipal, final BigDecimal totalOverpaid, final BigDecimal inArrearsTolerance,
|
||||
@@ -805,7 +833,8 @@ public class LoanAccountData {
|
||||
final Integer repaymentEvery, final EnumOptionData repaymentFrequencyType, EnumOptionData repaymentFrequencyNthDayType,
|
||||
EnumOptionData repaymentFrequencyDayOfWeekType, final Long transactionStrategyId, final String transactionStrategyName,
|
||||
final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod, final EnumOptionData interestRateFrequencyType,
|
||||
final BigDecimal annualInterestRate, final EnumOptionData interestType, final EnumOptionData interestCalculationPeriodType,
|
||||
final BigDecimal annualInterestRate, final EnumOptionData interestType, final boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final EnumOptionData interestCalculationPeriodType,
|
||||
final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment,
|
||||
final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline,
|
||||
final LoanSummaryData loanSummary, final BigDecimal feeChargesDueAtDisbursementCharged,
|
||||
@@ -848,13 +877,15 @@ public class LoanAccountData {
|
||||
final Collection<ChargeData> overdueCharges = null;
|
||||
final LoanScheduleData originalSchedule = null;
|
||||
final PaidInAdvanceData paidInAdvance = null;
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods = null;
|
||||
|
||||
return new LoanAccountData(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, group, loanType, loanProductId,
|
||||
loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName, loanOfficerId, loanOfficerName,
|
||||
currencyData, proposedPrincipal, principal, approvedPrincipal, totalOverpaid, inArrearsTolerance, termFrequency,
|
||||
termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType, repaymentFrequencyNthDayType,
|
||||
repaymentFrequencyDayOfWeekType, transactionStrategyId, transactionStrategyName, amortizationType, interestRatePerPeriod,
|
||||
interestRateFrequencyType, annualInterestRate, interestType, interestCalculationPeriodType, expectedFirstRepaymentOnDate,
|
||||
interestRateFrequencyType, annualInterestRate, interestType, isFloatingInterestRate, interestRateDifferential,
|
||||
interestCalculationPeriodType, expectedFirstRepaymentOnDate,
|
||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary,
|
||||
feeChargesDueAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral, guarantors, calendarData,
|
||||
productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions, repaymentFrequencyNthDayTypeOptions,
|
||||
@@ -865,7 +896,7 @@ public class LoanAccountData {
|
||||
disbursementData, multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmont, outstandingLoanBalance,
|
||||
emiAmountVariations, memberVariations, product, inArrears, graceOnArrearsAgeing, overdueCharges, isNPA, daysInMonthType,
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, originalSchedule,
|
||||
createStandingInstructionAtDisbursement, paidInAdvance);
|
||||
createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -888,20 +919,21 @@ public class LoanAccountData {
|
||||
final Collection<NoteData> notes, final Collection<PortfolioAccountData> accountLinkingOptions,
|
||||
final PortfolioAccountData linkedAccount, final Collection<DisbursementData> disbursementDetails,
|
||||
final Collection<LoanTermVariationsData> emiAmountVariations, final Collection<ChargeData> overdueCharges,
|
||||
final PaidInAdvanceData paidInAdvance) {
|
||||
final PaidInAdvanceData paidInAdvance, Collection<InterestRatePeriodData> interestRatesPeriods) {
|
||||
LoanProductConfigurableAttributes loanProductConfigurableAttributes = null;
|
||||
if (acc.product != null) {
|
||||
loanProductConfigurableAttributes = acc.product.getloanProductConfigurableAttributes();
|
||||
}
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, repaymentSchedule, transactions, charges, collateral,
|
||||
guarantors, calendarData, productOptions, termFrequencyTypeOptions, repaymentFrequencyTypeOptions,
|
||||
@@ -913,7 +945,7 @@ public class LoanAccountData {
|
||||
acc.maxOutstandingLoanBalance, emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, paidInAdvance);
|
||||
acc.createStandingInstructionAtDisbursement, paidInAdvance, interestRatesPeriods);
|
||||
}
|
||||
|
||||
public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection<LoanProductData> productOptions,
|
||||
@@ -925,18 +957,19 @@ public class LoanAccountData {
|
||||
acc.transactionProcessingStrategyOptions, acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions,
|
||||
acc.interestTypeOptions, acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null,
|
||||
allowedLoanOfficers, acc.loanPurposeOptions, acc.loanCollateralOptions, calendarOptions, acc.notes, accountLinkingOptions,
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance);
|
||||
acc.linkedAccount, acc.disbursementDetails, acc.emiAmountVariations, acc.overdueCharges, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateGroup(final LoanAccountData acc, final GroupGeneralData group) {
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
@@ -949,7 +982,7 @@ public class LoanAccountData {
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
}
|
||||
|
||||
public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map<Long, Integer> memberLoanCycle) {
|
||||
@@ -991,13 +1024,14 @@ public class LoanAccountData {
|
||||
}
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
@@ -1010,7 +1044,7 @@ public class LoanAccountData {
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
|
||||
}
|
||||
|
||||
@@ -1020,13 +1054,14 @@ public class LoanAccountData {
|
||||
final LoanInterestRecalculationData interestRecalculationData = LoanInterestRecalculationData.withCalendarData(
|
||||
acc.interestRecalculationData, calendarData, compoundingCalendarData);
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
@@ -1039,19 +1074,20 @@ public class LoanAccountData {
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, interestRecalculationData, acc.originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
}
|
||||
|
||||
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
|
||||
|
||||
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientName, acc.clientOfficeId,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.fundId, acc.fundName,
|
||||
acc.group, acc.loanType, acc.loanProductId, acc.loanProductName, acc.loanProductDescription, acc.isLoanProductLinkedToFloatingRate, acc.fundId, acc.fundName,
|
||||
acc.loanPurposeId, acc.loanPurposeName, acc.loanOfficerId, acc.loanOfficerName, acc.currency, acc.proposedPrincipal,
|
||||
acc.principal, acc.approvedPrincipal, acc.totalOverpaid, acc.inArrearsTolerance, acc.termFrequency,
|
||||
acc.termPeriodFrequencyType, acc.numberOfRepayments, acc.repaymentEvery, acc.repaymentFrequencyType,
|
||||
acc.repaymentFrequencyNthDayType, acc.repaymentFrequencyDayOfWeekType, acc.transactionProcessingStrategyId,
|
||||
acc.transactionProcessingStrategyName, acc.amortizationType, acc.interestRatePerPeriod, acc.interestRateFrequencyType,
|
||||
acc.annualInterestRate, acc.interestType, acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.annualInterestRate, acc.interestType, acc.isFloatingInterestRate, acc.interestRateDifferential,
|
||||
acc.interestCalculationPeriodType, acc.expectedFirstRepaymentOnDate,
|
||||
acc.graceOnPrincipalPayment, acc.graceOnInterestPayment, acc.graceOnInterestCharged, acc.interestChargedFromDate,
|
||||
acc.timeline, acc.summary, acc.feeChargesAtDisbursementCharged, acc.repaymentSchedule, acc.transactions, acc.charges,
|
||||
acc.collateral, acc.guarantors, acc.meeting, acc.productOptions, acc.termFrequencyTypeOptions,
|
||||
@@ -1064,7 +1100,7 @@ public class LoanAccountData {
|
||||
acc.maxOutstandingLoanBalance, acc.emiAmountVariations, acc.memberVariations, acc.product, acc.inArrears,
|
||||
acc.graceOnArrearsAgeing, acc.overdueCharges, acc.isNPA, acc.daysInMonthType, acc.daysInYearType,
|
||||
acc.isInterestRecalculationEnabled, acc.interestRecalculationData, originalSchedule,
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance);
|
||||
acc.createStandingInstructionAtDisbursement, acc.paidInAdvance, acc.interestRatesPeriods);
|
||||
}
|
||||
|
||||
private LoanAccountData(
|
||||
@@ -1080,6 +1116,7 @@ public class LoanAccountData {
|
||||
final Long loanProductId,
|
||||
final String loanProductName,
|
||||
final String loanProductDescription, //
|
||||
final boolean isLoanProductLinkedToFloatingRate,
|
||||
final Long fundId,
|
||||
final String fundName,
|
||||
final Long loanPurposeId,
|
||||
@@ -1100,7 +1137,8 @@ public class LoanAccountData {
|
||||
final EnumOptionData repaymentFrequencyNthDayType, final EnumOptionData repaymentFrequencyDayOfWeekType,
|
||||
final Long transactionProcessingStrategyId, final String transactionProcessingStrategyName,
|
||||
final EnumOptionData amortizationType, final BigDecimal interestRatePerPeriod, final EnumOptionData interestRateFrequencyType,
|
||||
final BigDecimal annualInterestRate, final EnumOptionData interestType, final EnumOptionData interestCalculationPeriodType,
|
||||
final BigDecimal annualInterestRate, final EnumOptionData interestType, final boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential, final EnumOptionData interestCalculationPeriodType,
|
||||
final LocalDate expectedFirstRepaymentOnDate, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment,
|
||||
final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final LoanApplicationTimelineData timeline,
|
||||
final LoanSummaryData summary, final BigDecimal feeChargesDueAtDisbursementCharged, final LoanScheduleData repaymentSchedule,
|
||||
@@ -1125,7 +1163,8 @@ public class LoanAccountData {
|
||||
final Integer graceOnArrearsAgeing, final Collection<ChargeData> overdueCharges, final Boolean isNPA,
|
||||
final EnumOptionData daysInMonthType, final EnumOptionData daysInYearType, final boolean isInterestRecalculationEnabled,
|
||||
final LoanInterestRecalculationData interestRecalculationData, final LoanScheduleData originalSchedule,
|
||||
final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance) {
|
||||
final Boolean createStandingInstructionAtDisbursement, final PaidInAdvanceData paidInAdvance,
|
||||
final Collection<InterestRatePeriodData> interestRatesPeriods) {
|
||||
|
||||
this.id = id;
|
||||
this.accountNo = accountNo;
|
||||
@@ -1139,6 +1178,7 @@ public class LoanAccountData {
|
||||
this.loanProductId = loanProductId;
|
||||
this.loanProductName = loanProductName;
|
||||
this.loanProductDescription = loanProductDescription;
|
||||
this.isLoanProductLinkedToFloatingRate = isLoanProductLinkedToFloatingRate;
|
||||
this.fundId = fundId;
|
||||
this.fundName = fundName;
|
||||
this.loanPurposeId = loanPurposeId;
|
||||
@@ -1165,6 +1205,8 @@ public class LoanAccountData {
|
||||
this.interestRateFrequencyType = interestRateFrequencyType;
|
||||
this.annualInterestRate = annualInterestRate;
|
||||
this.interestType = interestType;
|
||||
this.isFloatingInterestRate = isFloatingInterestRate;
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
this.interestCalculationPeriodType = interestCalculationPeriodType;
|
||||
this.expectedFirstRepaymentOnDate = expectedFirstRepaymentOnDate;
|
||||
this.graceOnPrincipalPayment = graceOnPrincipalPayment;
|
||||
@@ -1289,6 +1331,7 @@ public class LoanAccountData {
|
||||
if (this.product != null) {
|
||||
this.product.setloanProductConfigurableAttributes(product.getloanProductConfigurableAttributes());
|
||||
}
|
||||
this.interestRatesPeriods = interestRatesPeriods;
|
||||
}
|
||||
|
||||
public RepaymentScheduleRelatedLoanData repaymentScheduleRelatedData() {
|
||||
@@ -1395,4 +1438,5 @@ public class LoanAccountData {
|
||||
if(this.summary != null) return this.summary.getTotalPaidFeeCharges() ;
|
||||
return BigDecimal.ZERO ;
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
|
||||
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.data.EnumOptionData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
|
||||
|
||||
public class LoanTermVariationsData {
|
||||
|
||||
@@ -16,6 +17,7 @@ public class LoanTermVariationsData {
|
||||
private final Long id;
|
||||
private final EnumOptionData termType;
|
||||
private final LocalDate termVariationApplicableFrom;
|
||||
private final Integer termVariationApplicableFromInstallment;
|
||||
private final BigDecimal termValue;
|
||||
|
||||
public LoanTermVariationsData(final Long id, final EnumOptionData termType, final LocalDate termVariationApplicableFrom,
|
||||
@@ -24,11 +26,25 @@ public class LoanTermVariationsData {
|
||||
this.termType = termType;
|
||||
this.termVariationApplicableFrom = termVariationApplicableFrom;
|
||||
this.termValue = termValue;
|
||||
this.termVariationApplicableFromInstallment = null;
|
||||
}
|
||||
|
||||
public LoanTermVariationsData(final EnumOptionData termType, final LocalDate termVariationApplicableFrom,
|
||||
final BigDecimal termValue) {
|
||||
this.id = null;
|
||||
this.termType = termType;
|
||||
this.termVariationApplicableFrom = termVariationApplicableFrom;
|
||||
this.termValue = termValue;
|
||||
this.termVariationApplicableFromInstallment = null;
|
||||
}
|
||||
|
||||
public EnumOptionData getTermType() {
|
||||
return this.termType;
|
||||
}
|
||||
|
||||
public LoanTermVariationType getTermVariationType(){
|
||||
return LoanTermVariationType.fromInt(this.termType.getId().intValue());
|
||||
}
|
||||
|
||||
public LocalDate getTermApplicableFrom() {
|
||||
return this.termVariationApplicableFrom;
|
||||
@@ -37,4 +53,30 @@ public class LoanTermVariationsData {
|
||||
public BigDecimal getTermValue() {
|
||||
return this.termValue;
|
||||
}
|
||||
|
||||
public boolean isApplicable(final LocalDate fromDate, final LocalDate dueDate, int installmentNumber) {
|
||||
return occursOnDayFromAndUpTo(fromDate, dueDate, this.termVariationApplicableFrom)
|
||||
|| isIntegerEquals(installmentNumber, this.termVariationApplicableFromInstallment);
|
||||
}
|
||||
|
||||
private boolean occursOnDayFromAndUpTo(final LocalDate fromInclusive, final LocalDate upToNotInclusive, final LocalDate target) {
|
||||
return target != null && !target.isBefore(fromInclusive) && target.isBefore(upToNotInclusive);
|
||||
}
|
||||
|
||||
private boolean isIntegerEquals(final Integer installmentNumber, final Integer target) {
|
||||
return target != null && target.equals(installmentNumber);
|
||||
}
|
||||
|
||||
public boolean isApplicable(final LocalDate fromDate, int installmentNumber) {
|
||||
return occursBefore(fromDate, this.termVariationApplicableFrom)
|
||||
|| isIntegerGreterThan(installmentNumber, this.termVariationApplicableFromInstallment);
|
||||
}
|
||||
|
||||
private boolean occursBefore(final LocalDate date, final LocalDate target) {
|
||||
return target != null && target.isBefore(date);
|
||||
}
|
||||
|
||||
private boolean isIntegerGreterThan(final Integer installmentNumber, final Integer target) {
|
||||
return target != null && target < installmentNumber;
|
||||
}
|
||||
}
|
||||
|
||||
+12
-3
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.loanaccount.data;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
|
||||
public class ScheduleGeneratorDTO {
|
||||
@@ -20,10 +21,12 @@ public class ScheduleGeneratorDTO {
|
||||
final CalendarInstance compoundingCalendarInstance;
|
||||
LocalDate recalculateFrom;
|
||||
final Long overdurPenaltyWaitPeriod;
|
||||
final FloatingRateDTO floatingRateDTO;
|
||||
|
||||
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, final CalendarInstance compoundingCalendarInstance) {
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, final CalendarInstance compoundingCalendarInstance,
|
||||
final FloatingRateDTO floatingRateDTO) {
|
||||
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.applicationCurrency = applicationCurrency;
|
||||
@@ -33,13 +36,14 @@ public class ScheduleGeneratorDTO {
|
||||
this.recalculateFrom = null;
|
||||
this.overdurPenaltyWaitPeriod = null;
|
||||
this.holidayDetailDTO = holidayDetailDTO;
|
||||
this.floatingRateDTO = floatingRateDTO;
|
||||
|
||||
}
|
||||
|
||||
public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO,
|
||||
final CalendarInstance calendarInstanceForInterestRecalculation, final CalendarInstance compoundingCalendarInstance,
|
||||
final LocalDate recalculateFrom, final Long overdurPenaltyWaitPeriod) {
|
||||
final LocalDate recalculateFrom, final Long overdurPenaltyWaitPeriod, final FloatingRateDTO floatingRateDTO) {
|
||||
|
||||
this.loanScheduleFactory = loanScheduleFactory;
|
||||
this.applicationCurrency = applicationCurrency;
|
||||
@@ -49,7 +53,7 @@ public class ScheduleGeneratorDTO {
|
||||
this.recalculateFrom = recalculateFrom;
|
||||
this.overdurPenaltyWaitPeriod = overdurPenaltyWaitPeriod;
|
||||
this.holidayDetailDTO = holidayDetailDTO;
|
||||
|
||||
this.floatingRateDTO = floatingRateDTO;
|
||||
}
|
||||
|
||||
public LoanScheduleGeneratorFactory getLoanScheduleFactory() {
|
||||
@@ -96,4 +100,9 @@ public class ScheduleGeneratorDTO {
|
||||
return this.compoundingCalendarInstance;
|
||||
}
|
||||
|
||||
|
||||
public FloatingRateDTO getFloatingRateDTO() {
|
||||
return this.floatingRateDTO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+140
-17
@@ -77,6 +77,8 @@ import org.mifosplatform.portfolio.collateral.domain.LoanCollateral;
|
||||
import org.mifosplatform.portfolio.common.domain.DayOfWeekType;
|
||||
import org.mifosplatform.portfolio.common.domain.NthDayType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
@@ -369,18 +371,26 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
@Column(name = "interest_recalcualated_on")
|
||||
private Date interestRecalculatedOn;
|
||||
|
||||
@Column(name = "is_floating_interest_rate", nullable = true)
|
||||
private Boolean isFloatingInterestRate;
|
||||
|
||||
@Column(name = "interest_rate_differential", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal interestRateDifferential;
|
||||
|
||||
public static Loan newIndividualLoanApplication(final String accountNo, final Client client, final Integer loanType,
|
||||
final LoanProduct loanProduct, final Fund fund, final Staff officer, final CodeValue loanPurpose,
|
||||
final LoanTransactionProcessingStrategy transactionProcessingStrategy,
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
|
||||
final Set<LoanCollateral> collateral, final BigDecimal fixedEmiAmount, final Set<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement) {
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential) {
|
||||
final LoanStatus status = null;
|
||||
final Group group = null;
|
||||
final Boolean syncDisbursementWithMeeting = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement);
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential);
|
||||
}
|
||||
|
||||
public static Loan newGroupLoanApplication(final String accountNo, final Group group, final Integer loanType,
|
||||
@@ -389,12 +399,14 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
|
||||
final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final Set<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement) {
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential) {
|
||||
final LoanStatus status = null;
|
||||
final Client client = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement);
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential);
|
||||
}
|
||||
|
||||
public static Loan newIndividualLoanApplicationFromGroup(final String accountNo, final Client client, final Group group,
|
||||
@@ -403,11 +415,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final LoanProductRelatedDetail loanRepaymentScheduleDetail, final Set<LoanCharge> loanCharges,
|
||||
final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting, final BigDecimal fixedEmiAmount,
|
||||
final Set<LoanDisbursementDetails> disbursementDetails, final BigDecimal maxOutstandingLoanBalance,
|
||||
final Boolean createStandingInstructionAtDisbursement) {
|
||||
final Boolean createStandingInstructionAtDisbursement, final Boolean isFloatingInterestRate,
|
||||
final BigDecimal interestRateDifferential) {
|
||||
final LoanStatus status = null;
|
||||
return new Loan(accountNo, client, group, loanType, fund, officer, loanPurpose, transactionProcessingStrategy, loanProduct,
|
||||
loanRepaymentScheduleDetail, status, loanCharges, collateral, syncDisbursementWithMeeting, fixedEmiAmount,
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement);
|
||||
disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement, isFloatingInterestRate,
|
||||
interestRateDifferential);
|
||||
}
|
||||
|
||||
protected Loan() {
|
||||
@@ -419,11 +433,15 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final LoanProduct loanProduct, final LoanProductRelatedDetail loanRepaymentScheduleDetail, final LoanStatus loanStatus,
|
||||
final Set<LoanCharge> loanCharges, final Set<LoanCollateral> collateral, final Boolean syncDisbursementWithMeeting,
|
||||
final BigDecimal fixedEmiAmount, final Set<LoanDisbursementDetails> disbursementDetails,
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement) {
|
||||
final BigDecimal maxOutstandingLoanBalance, final Boolean createStandingInstructionAtDisbursement,
|
||||
final Boolean isFloatingInterestRate, final BigDecimal interestRateDifferential) {
|
||||
|
||||
this.loanRepaymentScheduleDetail = loanRepaymentScheduleDetail;
|
||||
this.loanRepaymentScheduleDetail.validateRepaymentPeriodWithGraceSettings();
|
||||
|
||||
this.isFloatingInterestRate = isFloatingInterestRate;
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
|
||||
if (StringUtils.isBlank(accountNo)) {
|
||||
this.accountNumber = new RandomPasswordGenerator(19).generate();
|
||||
this.accountNumberRequiresAutoGeneration = true;
|
||||
@@ -1288,6 +1306,20 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
actualChanges.put("recalculateLoanSchedule", true);
|
||||
}
|
||||
|
||||
final String isFloatingInterestRateParamName = "isFloatingInterestRate";
|
||||
if (command.isChangeInBooleanParameterNamed(isFloatingInterestRateParamName, this.isFloatingInterestRate)) {
|
||||
final Boolean newValue = command.booleanObjectValueOfParameterNamed(isFloatingInterestRateParamName);
|
||||
actualChanges.put(isFloatingInterestRateParamName, newValue);
|
||||
this.isFloatingInterestRate = newValue;
|
||||
}
|
||||
|
||||
final String interestRateDifferentialParamName = "interestRateDifferential";
|
||||
if (command.isChangeInBigDecimalParameterNamed(interestRateDifferentialParamName, this.interestRateDifferential)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(interestRateDifferentialParamName);
|
||||
actualChanges.put(interestRateDifferentialParamName, newValue);
|
||||
this.interestRateDifferential = newValue;
|
||||
}
|
||||
|
||||
Long existingFundId = null;
|
||||
if (this.fund != null) {
|
||||
existingFundId = this.fund.getId();
|
||||
@@ -1498,6 +1530,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
for (final LoanCharge loanCharge : charges) {
|
||||
recalculateLoanCharge(loanCharge, penaltyWaitPeriod);
|
||||
}
|
||||
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
|
||||
}
|
||||
|
||||
public boolean isInterestRecalculationEnabledForProduct() {
|
||||
@@ -2578,6 +2611,23 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
}
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(scheduleGeneratorDTO.getFloatingRateDTO());
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(scheduleGeneratorDTO.getApplicationCurrency(),
|
||||
loanTermFrequency, loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(),
|
||||
getExpectedFirstRepaymentOnDate(), scheduleGeneratorDTO.getCalculatedRepaymentsStartingFromDate(), getInArrearsTolerance(),
|
||||
@@ -2585,7 +2635,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
this.maxOutstandingLoanBalance, loanVariationTermsData, getInterestChargedFromDate(),
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, getApprovedPrincipal());
|
||||
this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, getApprovedPrincipal(),
|
||||
annualNominalInterestRate, loanTermVariations);
|
||||
|
||||
final LoanScheduleModel loanSchedule = loanScheduleGenerator.generate(mc, loanApplicationTerms, charges(),
|
||||
scheduleGeneratorDTO.getHolidayDetailDTO());
|
||||
@@ -4839,6 +4890,8 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
*/
|
||||
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
regenerateRepaymentScheduleWithInterestRecalculation(generatorDTO, currentUser);
|
||||
} else {
|
||||
regenerateRepaymentSchedule(generatorDTO, currentUser);
|
||||
}
|
||||
return processTransactions();
|
||||
|
||||
@@ -4929,7 +4982,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = constructLoanApplicationTerms(generatorDTO.getApplicationCurrency(),
|
||||
generatorDTO.getCalculatedRepaymentsStartingFromDate(), generatorDTO.getCalendarInstanceForInterestRecalculation(),
|
||||
generatorDTO.getCompoundingCalendarInstance());
|
||||
generatorDTO.getCompoundingCalendarInstance(), generatorDTO.getFloatingRateDTO());
|
||||
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
@@ -4941,7 +4994,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public LoanApplicationTerms constructLoanApplicationTerms(final ApplicationCurrency applicationCurrency,
|
||||
final LocalDate calculatedRepaymentsStartingFromDate, final CalendarInstance calendarInstanceForInterestRecalculation,
|
||||
final CalendarInstance compoundingCalendarInstance) {
|
||||
final CalendarInstance compoundingCalendarInstance, final FloatingRateDTO floatingRateDTO) {
|
||||
final Integer loanTermFrequency = this.termFrequency;
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
|
||||
final List<DisbursementData> disbursementData = new ArrayList<>();
|
||||
@@ -4967,6 +5020,23 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
RecalculationFrequencyType recalculationFrequencyType = this.loanInterestRecalculationDetails().getRestFrequencyType();
|
||||
RecalculationFrequencyType compoundingFrequencyType = this.loanInterestRecalculationDetails().getCompoundingFrequencyType();
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency,
|
||||
loanTermPeriodFrequencyType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(),
|
||||
calculatedRepaymentsStartingFromDate, getInArrearsTolerance(), this.loanRepaymentScheduleDetail,
|
||||
@@ -4975,13 +5045,13 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
calendarInstanceForInterestRecalculation, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, this.loanProduct.getPrincipalThresholdForLastInstallment(),
|
||||
this.loanProduct.getInstallmentAmountInMultiplesOf(), this.loanProduct.preCloseInterestCalculationStrategy(),
|
||||
getApprovedPrincipal());
|
||||
getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations);
|
||||
return loanApplicationTerms;
|
||||
}
|
||||
|
||||
public LoanRepaymentScheduleInstallment fetchPrepaymentDetail(final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final CalendarInstance restCalendarInstance, final CalendarInstance compoundingCalendarInstance,
|
||||
final HolidayDetailDTO holidayDetailDTO, final LocalDate onDate) {
|
||||
final HolidayDetailDTO holidayDetailDTO, final LocalDate onDate, FloatingRateDTO floatingRateDTO) {
|
||||
LoanRepaymentScheduleInstallment installment = null;
|
||||
|
||||
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
|
||||
@@ -5028,6 +5098,23 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
}
|
||||
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
|
||||
final LoanApplicationTerms loanApplicationTerms = LoanApplicationTerms.assembleFrom(null, loanTermFrequency,
|
||||
loanTermPeriodFrequencyType, nthDayType, dayOfWeekType, getDisbursementDate(), getExpectedFirstRepaymentOnDate(), null,
|
||||
getInArrearsTolerance(), this.loanRepaymentScheduleDetail, this.loanProduct.isMultiDisburseLoan(), this.fixedEmiAmount,
|
||||
@@ -5035,7 +5122,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod,
|
||||
getApprovedPrincipal());
|
||||
getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations);
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor = this.transactionProcessorFactory
|
||||
.determineProcessor(this.transactionProcessingStrategy);
|
||||
installment = loanScheduleGenerator.calculatePrepaymentAmount(getCurrency(), onDate, loanApplicationTerms, mc, charges(),
|
||||
@@ -5076,7 +5163,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
|
||||
public LocalDate fetchInterestRecalculateFromDate() {
|
||||
LocalDate interestRecalculatedOn = null;
|
||||
if (this.interestRecalculatedOn != null) {
|
||||
if (this.interestRecalculatedOn == null) {
|
||||
interestRecalculatedOn = getDisbursementDate();
|
||||
} else {
|
||||
interestRecalculatedOn = new LocalDate(this.interestRecalculatedOn);
|
||||
@@ -5189,13 +5276,16 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
* TODO
|
||||
* @param compoundingCalendarInstance
|
||||
* TODO
|
||||
* @param floatingRateDTO
|
||||
* TODO
|
||||
* @param loanCalendarInstance
|
||||
* Used for accessing the loan's calendar object
|
||||
* @return application terms of the Loan object
|
||||
**/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency,
|
||||
final CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar) {
|
||||
final CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar,
|
||||
final FloatingRateDTO floatingRateDTO) {
|
||||
LoanProduct loanProduct = loanProduct();
|
||||
// LoanProductRelatedDetail loanProductRelatedDetail =
|
||||
// getLoanRepaymentScheduleDetail();
|
||||
@@ -5219,7 +5309,7 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
final BigDecimal interestRatePerPeriod = this.loanRepaymentScheduleDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
|
||||
|
||||
final BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate();
|
||||
final Money principalMoney = this.loanRepaymentScheduleDetail.getPrincipal();
|
||||
|
||||
final LocalDate expectedDisbursementDate = getExpectedDisbursedOnLocalDate();
|
||||
@@ -5264,13 +5354,30 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
|
||||
}
|
||||
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDayType,
|
||||
dayOfWeekType, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate,
|
||||
inArrearsToleranceMoney, this.loanRepaymentScheduleDetail, loanProduct.isMultiDisburseLoan(), emiAmount, disbursementData,
|
||||
maxOutstandingBalance, loanVariationTermsData, interestChargedFromDate,
|
||||
this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(),
|
||||
recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal());
|
||||
this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(),
|
||||
annualNominalInterestRate, loanTermVariations);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5509,4 +5616,20 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean getIsFloatingInterestRate() {
|
||||
return this.isFloatingInterestRate;
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRateDifferential() {
|
||||
return this.interestRateDifferential;
|
||||
}
|
||||
|
||||
public void setIsFloatingInterestRate(Boolean isFloatingInterestRate) {
|
||||
this.isFloatingInterestRate = isFloatingInterestRate;
|
||||
}
|
||||
|
||||
public void setInterestRateDifferential(BigDecimal interestRateDifferential) {
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+25
-3
@@ -49,6 +49,9 @@ import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUS
|
||||
import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.service.BusinessEventNotifierService;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
@@ -88,6 +91,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
private final LoanAccrualPlatformService loanAccrualPlatformService;
|
||||
private final PlatformSecurityContext context;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanAccountDomainServiceJpa(final LoanAssembler loanAccountAssembler, final LoanRepository loanRepository,
|
||||
@@ -101,7 +105,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository,
|
||||
final LoanAccrualPlatformService loanAccrualPlatformService, final PlatformSecurityContext context,
|
||||
final BusinessEventNotifierService businessEventNotifierService) {
|
||||
final BusinessEventNotifierService businessEventNotifierService,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.loanAccountAssembler = loanAccountAssembler;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanTransactionRepository = loanTransactionRepository;
|
||||
@@ -119,6 +124,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
this.loanAccrualPlatformService = loanAccrualPlatformService;
|
||||
this.context = context;
|
||||
this.businessEventNotifierService = businessEventNotifierService;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -190,9 +196,11 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
}
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.makeRepayment(newRepaymentTransaction,
|
||||
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, isRecoveryRepayment,
|
||||
@@ -239,6 +247,19 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
return newRepaymentTransaction;
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
|
||||
private void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) {
|
||||
try {
|
||||
this.loanTransactionRepository.save(newRepaymentTransaction);
|
||||
@@ -557,8 +578,9 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance);
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, floatingRateDTO);
|
||||
|
||||
return scheduleGeneratorDTO;
|
||||
}
|
||||
|
||||
+5
@@ -54,6 +54,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
|
||||
public static final String DOES_CLIENT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.client.id = :clientId and loan.loanStatus in (100,200,300,303,304,700)";
|
||||
|
||||
public static final String DOES_PRODUCT_HAVE_NON_CLOSED_LOANS = "select case when (count (loan) > 0) then true else false end from Loan loan where loan.loanProduct.id = :productId and loan.loanStatus in (100,200,300,303,304,700)";
|
||||
|
||||
@Query(FIND_GROUP_LOANS_DISBURSED_AFTER)
|
||||
List<Loan> getGroupLoansDisbursedAfter(@Param("disbursementDate") Date disbursementDate, @Param("groupId") Long groupId,
|
||||
@Param("loanType") Integer loanType);
|
||||
@@ -126,5 +128,8 @@ public interface LoanRepository extends JpaRepository<Loan, Long>, JpaSpecificat
|
||||
|
||||
@Query(DOES_CLIENT_HAVE_NON_CLOSED_LOANS)
|
||||
boolean doNonClosedLoanAccountsExistForClient(@Param("clientId") Long clientId);
|
||||
|
||||
@Query(DOES_PRODUCT_HAVE_NON_CLOSED_LOANS)
|
||||
boolean doNonClosedLoanAccountsExistForProduct(@Param("productId") Long productId);
|
||||
|
||||
}
|
||||
+9
-1
@@ -8,7 +8,8 @@ package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
public enum LoanTermVariationType {
|
||||
|
||||
INVALID(0, "loanTermType.invalid"), //
|
||||
EMI_AMOUNT(1, "loanTermType.emiAmount"); //
|
||||
EMI_AMOUNT(1, "loanTermType.emiAmount"), //
|
||||
INTEREST_RATE(2, "loanTermType.interestRate");
|
||||
|
||||
private final Integer value;
|
||||
private final String code;
|
||||
@@ -33,6 +34,9 @@ public enum LoanTermVariationType {
|
||||
case 1:
|
||||
enumeration = LoanTermVariationType.EMI_AMOUNT;
|
||||
break;
|
||||
case 2:
|
||||
enumeration = LoanTermVariationType.INTEREST_RATE;
|
||||
break;
|
||||
}
|
||||
return enumeration;
|
||||
}
|
||||
@@ -40,4 +44,8 @@ public enum LoanTermVariationType {
|
||||
public boolean isEMIAmountVariation() {
|
||||
return this.value.equals(LoanTermVariationType.EMI_AMOUNT.getValue());
|
||||
}
|
||||
|
||||
public boolean isInterestRateVariation() {
|
||||
return this.value.equals(LoanTermVariationType.INTEREST_RATE.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
+16
-7
@@ -32,6 +32,7 @@ public class LoanScheduleRecalculationDTO {
|
||||
private final Map<LocalDate, Money> latePaymentMap;
|
||||
private final TreeMap<LocalDate, Money> compoundingMap;
|
||||
private final Map<LocalDate, Money> disburseDetailMap;
|
||||
private final Money principalToBeScheduled;
|
||||
private final Money outstandingBalance;
|
||||
private final Money outstandingBalanceAsPerRest;
|
||||
private final List<LoanRepaymentScheduleInstallment> installments;
|
||||
@@ -44,9 +45,9 @@ public class LoanScheduleRecalculationDTO {
|
||||
final LocalDate actualRepaymentDate, final Money totalCumulativePrincipal, final Money totalCumulativeInterest,
|
||||
final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged, final Money totalRepaymentExpected,
|
||||
final Money reducePrincipal, final Map<LocalDate, Money> principalPortionMap, final Map<LocalDate, Money> latePaymentMap,
|
||||
final TreeMap<LocalDate, Money> compoundingMap, final Map<LocalDate, Money> disburseDetailMap, final Money outstandingBalance,
|
||||
final Money outstandingBalanceAsPerRest, final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final Collection<RecalculationDetail> recalculationDetails,
|
||||
final TreeMap<LocalDate, Money> compoundingMap, final Map<LocalDate, Money> disburseDetailMap, Money principalToBeScheduled,
|
||||
final Money outstandingBalance, final Money outstandingBalanceAsPerRest,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate,
|
||||
final boolean partialUpdate) {
|
||||
this.periodNumber = periodNumber;
|
||||
@@ -63,6 +64,7 @@ public class LoanScheduleRecalculationDTO {
|
||||
this.latePaymentMap = latePaymentMap;
|
||||
this.compoundingMap = compoundingMap;
|
||||
this.disburseDetailMap = disburseDetailMap;
|
||||
this.principalToBeScheduled = principalToBeScheduled;
|
||||
this.outstandingBalance = outstandingBalance;
|
||||
this.outstandingBalanceAsPerRest = outstandingBalanceAsPerRest;
|
||||
this.installments = installments;
|
||||
@@ -77,14 +79,15 @@ public class LoanScheduleRecalculationDTO {
|
||||
final Money totalCumulativeInterest, final Money totalFeeChargesCharged, final Money totalPenaltyChargesCharged,
|
||||
final Money totalRepaymentExpected, final Money reducePrincipal, final Map<LocalDate, Money> principalPortionMap,
|
||||
final Map<LocalDate, Money> latePaymentMap, final TreeMap<LocalDate, Money> compoundingMap,
|
||||
final Map<LocalDate, Money> disburseDetailMap, final Money outstandingBalance, final Money outstandingBalanceAsPerRest,
|
||||
final List<LoanRepaymentScheduleInstallment> installments, final Collection<RecalculationDetail> recalculationDetails,
|
||||
final Map<LocalDate, Money> disburseDetailMap, Money principalToBeScheduled, final Money outstandingBalance,
|
||||
final Money outstandingBalanceAsPerRest, final List<LoanRepaymentScheduleInstallment> installments,
|
||||
final Collection<RecalculationDetail> recalculationDetails,
|
||||
final LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor, final LocalDate scheduleTillDate) {
|
||||
final boolean partialUpdate = true;
|
||||
return new LoanScheduleRecalculationDTO(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap,
|
||||
outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||
principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate);
|
||||
}
|
||||
|
||||
@@ -105,6 +108,7 @@ public class LoanScheduleRecalculationDTO {
|
||||
final Map<LocalDate, Money> latePaymentMap = null;
|
||||
final TreeMap<LocalDate, Money> compoundingMap = null;
|
||||
final Map<LocalDate, Money> disburseDetailMap = null;
|
||||
final Money principalToBeScheduled = null;
|
||||
final Money outstandingBalance = null;
|
||||
final Money outstandingBalanceAsPerRest = null;
|
||||
final List<LoanRepaymentScheduleInstallment> installments = null;
|
||||
@@ -112,7 +116,7 @@ public class LoanScheduleRecalculationDTO {
|
||||
return new LoanScheduleRecalculationDTO(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap, disburseDetailMap,
|
||||
outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||
principalToBeScheduled, outstandingBalance, outstandingBalanceAsPerRest, installments, recalculationDetails,
|
||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate, partialUpdate);
|
||||
}
|
||||
|
||||
@@ -200,4 +204,9 @@ public class LoanScheduleRecalculationDTO {
|
||||
return this.periodStartDate;
|
||||
}
|
||||
|
||||
|
||||
public Money getPrincipalToBeScheduled() {
|
||||
return this.principalToBeScheduled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+62
-19
@@ -28,8 +28,10 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarUtils;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
@@ -181,6 +183,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
extendTermForDailyRepayments = true;
|
||||
}
|
||||
|
||||
// for applying variations
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>(loanApplicationTerms.getLoanTermVariations());
|
||||
|
||||
// this block is to start the schedule generation from specified date
|
||||
if (loanScheduleRecalculationDTO != null && loanScheduleRecalculationDTO.isPartialUpdate()) {
|
||||
periodNumber = loanScheduleRecalculationDTO.getPeriodNumber();
|
||||
@@ -206,6 +211,16 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
outstandingBalanceAsPerRest = loanScheduleRecalculationDTO.getOutstandingBalanceAsPerRest();
|
||||
installments.clear();
|
||||
installments.addAll(loanScheduleRecalculationDTO.getInstallments());
|
||||
if (loanApplicationTerms.isMultiDisburseLoan()) {
|
||||
principalToBeScheduled = loanScheduleRecalculationDTO.getPrincipalToBeScheduled();
|
||||
loanApplicationTerms.setPrincipal(principalToBeScheduled);
|
||||
}
|
||||
for (LoanTermVariationsData variation : loanApplicationTerms.getLoanTermVariations()) {
|
||||
if (variation.getTermVariationType().isInterestRateVariation() && variation.isApplicable(periodStartDate, periodNumber)
|
||||
&& variation.getTermValue() != null) {
|
||||
loanApplicationTerms.updateAnnualNominalInterestRate(variation.getTermValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (!outstandingBalance.isZero() || !disburseDetailMap.isEmpty()) {
|
||||
@@ -241,6 +256,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
Collection<RecalculationDetail> applicableTransactions = getApplicableTransactionsForPeriod(loanApplicationTerms,
|
||||
scheduledDueDate, transactions);
|
||||
|
||||
Collection<LoanTermVariationsData> applicableVariations = getApplicableTermVariationsForPeriod(periodStartDate,
|
||||
scheduledDueDate, periodNumber, loanTermVariations);
|
||||
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = this.paymentPeriodsInOneYearCalculator
|
||||
.calculatePortionOfRepaymentPeriodInterestChargingGrace(periodStartDateApplicableForInterest, scheduledDueDate,
|
||||
loanApplicationTerms.getInterestChargedFromLocalDate(), loanApplicationTerms.getLoanTermPeriodFrequencyType(),
|
||||
@@ -320,7 +338,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc,
|
||||
mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap),
|
||||
compoundingMap, periodStartDateApplicableForInterest, detail.getTransactionDate(),
|
||||
daysInPeriodApplicableForInterest);
|
||||
daysInPeriodApplicableForInterest, applicableVariations);
|
||||
interestForThisinstallment = principalInterestForThisPeriod.interest();
|
||||
|
||||
totalOutstandingInterestPaymentDueToGrace = principalInterestForThisPeriod.interestPaymentDueToGrace();
|
||||
@@ -461,7 +479,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
mc,
|
||||
mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap),
|
||||
compoundingMap, periodStartDateApplicableForInterest, calculateTill,
|
||||
daysInPeriodApplicableForInterest);
|
||||
daysInPeriodApplicableForInterest, applicableVariations);
|
||||
if (!principalInterestForThisPeriod.interest()
|
||||
.plus(principalInterestForThisPeriod.interestPaymentDueToGrace()).plus(outstandingBalance)
|
||||
.isGreaterThan(unprocessed)) {
|
||||
@@ -528,7 +546,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
totalCumulativePrincipal.minus(reducePrincipal), totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
|
||||
totalOutstandingInterestPaymentDueToGrace, outstandingBalanceAsPerRest, loanApplicationTerms, periodNumber, mc,
|
||||
mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap), compoundingMap,
|
||||
periodStartDateApplicableForInterest, scheduledDueDate, daysInPeriodApplicableForInterest);
|
||||
periodStartDateApplicableForInterest, scheduledDueDate, daysInPeriodApplicableForInterest, applicableVariations);
|
||||
|
||||
if (loanApplicationTerms.getFixedEmiAmount() != null
|
||||
&& loanApplicationTerms.getFixedEmiAmount().compareTo(principalInterestForThisPeriod.interest().getAmount()) != 1) {
|
||||
@@ -648,7 +666,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
periodNumber, mc,
|
||||
mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailMap, compoundingMap),
|
||||
compoundingMap, periodStartDateApplicableForInterest, calculateTill,
|
||||
daysInPeriodApplicableForInterest);
|
||||
daysInPeriodApplicableForInterest, applicableVariations);
|
||||
Money diff = interestForThisinstallment.minus(interestTillDate.interest());
|
||||
if (!outstandingBalance.minus(diff).isGreaterThanZero()) {
|
||||
outstandingBalance = outstandingBalance.minus(diff);
|
||||
@@ -860,6 +878,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction = Double.valueOf(0);
|
||||
int periodNumberTemp = 1;
|
||||
LocalDate lastRestDate = getNextRestScheduleDate(currentDate.minusDays(1), loanApplicationTerms, holidayDetailDTO);
|
||||
Collection<LoanTermVariationsData> applicableVariations = loanApplicationTerms.getLoanTermVariations();
|
||||
|
||||
do {
|
||||
|
||||
actualRepaymentDate = this.scheduledDateGenerator.generateNextRepaymentDate(actualRepaymentDate, loanApplicationTerms,
|
||||
@@ -890,7 +910,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding,
|
||||
loanApplicationTerms, periodNumberTemp, mc,
|
||||
mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailsMap, compoundingMap), compoundingMap,
|
||||
periodStartDate, detail.getTransactionDate(), daysInPeriod);
|
||||
periodStartDate, detail.getTransactionDate(), daysInPeriod, applicableVariations);
|
||||
|
||||
Money interest = principalInterestForThisPeriod.interest();
|
||||
totalInterest = totalInterest.plus(interest);
|
||||
@@ -922,7 +942,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
totalInterest.zero(), totalInterest.zero(), totalInterest.zero(), outstanding, loanApplicationTerms,
|
||||
periodNumberTemp, mc,
|
||||
mergeVariationsToMap(principalPortionMap, latePaymentMap, disburseDetailsMap, compoundingMap), compoundingMap,
|
||||
periodStartDate, actualRepaymentDate, daysInPeriod);
|
||||
periodStartDate, actualRepaymentDate, daysInPeriod, applicableVariations);
|
||||
Money interest = principalInterestForThisPeriod.interest();
|
||||
totalInterest = totalInterest.plus(interest);
|
||||
if (loanApplicationTerms.getInterestRecalculationCompoundingMethod().isInterestCompoundingEnabled()) {
|
||||
@@ -959,6 +979,18 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
return applicableTransactions;
|
||||
}
|
||||
|
||||
private Collection<LoanTermVariationsData> getApplicableTermVariationsForPeriod(final LocalDate fromDate, final LocalDate dueDate,
|
||||
final int installmentNumber, final Collection<LoanTermVariationsData> variations) {
|
||||
Collection<LoanTermVariationsData> applicableVariations = new ArrayList<>();
|
||||
for (LoanTermVariationsData detail : variations) {
|
||||
if (detail.isApplicable(fromDate, dueDate, installmentNumber)) {
|
||||
applicableVariations.add(detail);
|
||||
}
|
||||
}
|
||||
variations.removeAll(applicableVariations);
|
||||
return applicableVariations;
|
||||
}
|
||||
|
||||
private List<LoanTransaction> createCurrentTransactionList(RecalculationDetail detail) {
|
||||
List<LoanTransaction> currentTransactions = new ArrayList<>(2);
|
||||
currentTransactions.add(detail.getTransaction());
|
||||
@@ -1340,7 +1372,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
@Override
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO,
|
||||
final CalendarInstance restCalendarInstance, final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar) {
|
||||
final CalendarInstance restCalendarInstance, final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar,
|
||||
final FloatingRateDTO floatingRateDTO) {
|
||||
|
||||
final Loan loan = loanRescheduleRequest.getLoan();
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
@@ -1482,7 +1515,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
// get the loan application terms from the Loan object
|
||||
final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency, restCalendarInstance,
|
||||
compoundingCalendarInstance, loanCalendar);
|
||||
compoundingCalendarInstance, loanCalendar, floatingRateDTO);
|
||||
|
||||
// for applying variations
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>(loanApplicationTerms.getLoanTermVariations());
|
||||
|
||||
// update the number of repayments
|
||||
loanApplicationTerms.updateNumberOfRepayments(numberOfRepayments);
|
||||
@@ -1551,12 +1587,16 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// principal outstanding
|
||||
loanApplicationTerms.setPrincipal(totalPrincipalOutstanding);
|
||||
|
||||
// for applying variations
|
||||
Collection<LoanTermVariationsData> applicableVariations = getApplicableTermVariationsForPeriod(installmentFromDate,
|
||||
adjustedInstallmentDueDate, periodNumber, loanTermVariations);
|
||||
|
||||
// determine the interest & principal for the period
|
||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||
this.paymentPeriodsInOneYearCalculator, interestCalculationGraceOnRepaymentPeriodFraction,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalInterestChargedForFullLoanTerm,
|
||||
totalOutstandingInterestPaymentDueToGrace, outstandingBalance, loanApplicationTerms, periodNumber, mathContext,
|
||||
null, null, installmentFromDate, adjustedInstallmentDueDate, daysInInstallment);
|
||||
null, null, installmentFromDate, adjustedInstallmentDueDate, daysInInstallment, applicableVariations);
|
||||
|
||||
// update the interest due for the period
|
||||
period.updateInterestDue(principalInterestForThisPeriod.interest());
|
||||
@@ -1575,7 +1615,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
interestCalculationGraceOnRepaymentPeriodFraction, totalCumulativePrincipal, totalCumulativeInterest,
|
||||
totalInterestChargedForFullLoanTerm, totalOutstandingInterestPaymentDueToGrace, outstandingBalance,
|
||||
loanApplicationTerms, periodNumber, mathContext, null, null, installmentFromDate, adjustedInstallmentDueDate,
|
||||
daysInInstallment);
|
||||
daysInInstallment, applicableVariations);
|
||||
|
||||
period.updatePrincipalDue(principalInterestForThisPeriod.principal());
|
||||
|
||||
@@ -1642,7 +1682,8 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
double interestCalculationGraceOnRepaymentPeriodFraction, Money totalCumulativePrincipal, Money totalCumulativeInterest,
|
||||
Money totalInterestDueForLoan, Money cumulatingInterestPaymentDueToGrace, Money outstandingBalance,
|
||||
LoanApplicationTerms loanApplicationTerms, int periodNumber, MathContext mc, TreeMap<LocalDate, Money> principalVariation,
|
||||
Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate, int daysForInterestInFullPeriod);
|
||||
Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate, int daysForInterestInFullPeriod,
|
||||
Collection<LoanTermVariationsData> termVariations);
|
||||
|
||||
protected final boolean isLastRepaymentPeriod(final int numberOfRepayments, final int periodNumber) {
|
||||
return periodNumber == numberOfRepayments;
|
||||
@@ -1891,8 +1932,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
periods, chargesDueAtTimeOfDisbursement, disburseDetailMap, true);
|
||||
outstandingBalance = outstandingBalance.zero().plus(disburseAmt);
|
||||
outstandingBalanceAsPerRest = outstandingBalance;
|
||||
principalToBeScheduled = principalToBeScheduled.zero().plus(disburseAmt);
|
||||
}
|
||||
|
||||
|
||||
// Block process the installment and creates the period if it falls
|
||||
// before reschedule from date
|
||||
// This will create the recalculation details by applying the
|
||||
@@ -1914,7 +1956,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
periodNumber++;
|
||||
}
|
||||
|
||||
|
||||
for (Map.Entry<LocalDate, Money> disburseDetail : disburseDetailMap.entrySet()) {
|
||||
if (disburseDetail.getKey().isAfter(installment.getFromDate())
|
||||
&& !disburseDetail.getKey().isAfter(installment.getDueDate())) {
|
||||
@@ -1926,6 +1968,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
// updates actual outstanding balance with new
|
||||
// disbursement detail
|
||||
outstandingBalance = outstandingBalance.plus(disburseDetail.getValue());
|
||||
principalToBeScheduled = principalToBeScheduled.plus(disburseDetail.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2001,12 +2044,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
|
||||
// for partial schedule generation
|
||||
if (!newRepaymentScheduleInstallments.isEmpty()) {
|
||||
loanScheduleRecalculationDTO = LoanScheduleRecalculationDTO
|
||||
.createLoanScheduleDTOForPartialUpdate(periodNumber, instalmentNumber, periodStartDate, actualRepaymentDate,
|
||||
totalCumulativePrincipal, totalCumulativeInterest, totalFeeChargesCharged, totalPenaltyChargesCharged,
|
||||
totalRepaymentExpected, reducePrincipal, principalPortionMap, latePaymentMap, compoundingMap,
|
||||
disburseDetailMap, outstandingBalance, outstandingBalanceAsPerRest, newRepaymentScheduleInstallments,
|
||||
recalculationDetails, loanRepaymentScheduleTransactionProcessor, scheduleTillDate);
|
||||
loanScheduleRecalculationDTO = LoanScheduleRecalculationDTO.createLoanScheduleDTOForPartialUpdate(periodNumber,
|
||||
instalmentNumber, periodStartDate, actualRepaymentDate, totalCumulativePrincipal, totalCumulativeInterest,
|
||||
totalFeeChargesCharged, totalPenaltyChargesCharged, totalRepaymentExpected, reducePrincipal, principalPortionMap,
|
||||
latePaymentMap, compoundingMap, disburseDetailMap, principalToBeScheduled, outstandingBalance,
|
||||
outstandingBalanceAsPerRest, newRepaymentScheduleInstallments, recalculationDetails,
|
||||
loanRepaymentScheduleTransactionProcessor, scheduleTillDate);
|
||||
retainedInstallments.addAll(newRepaymentScheduleInstallments);
|
||||
}
|
||||
|
||||
|
||||
+25
-4
@@ -5,7 +5,10 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -13,6 +16,7 @@ import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
|
||||
/**
|
||||
@@ -49,7 +53,8 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
@SuppressWarnings("unused") final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
|
||||
final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
|
||||
final TreeMap<LocalDate, Money> principalVariation, final Map<LocalDate, Money> compoundingMap,
|
||||
final LocalDate periodStartDate, final LocalDate periodEndDate, final int daysForInterestInFullPeriod) {
|
||||
final LocalDate periodStartDate, final LocalDate periodEndDate, final int daysForInterestInFullPeriod,
|
||||
final Collection<LoanTermVariationsData> termVariations) {
|
||||
|
||||
LocalDate interestStartDate = periodStartDate;
|
||||
Money interestForThisInstallment = totalCumulativePrincipal.zero();
|
||||
@@ -58,6 +63,20 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
Money balanceForInterestCalculation = outstandingBalance;
|
||||
Money cumulatingInterestDueToGrace = cumulatingInterestPaymentDueToGrace;
|
||||
final int daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDate, periodEndDate).getDays();
|
||||
Map<LocalDate, BigDecimal> interestRates = new HashMap<>(termVariations.size());
|
||||
for (LoanTermVariationsData loanTermVariation : termVariations) {
|
||||
if (loanTermVariation.getTermVariationType().isInterestRateVariation()
|
||||
&& loanTermVariation.isApplicable(periodStartDate, periodEndDate, periodNumber)) {
|
||||
LocalDate fromDate = loanTermVariation.getTermApplicableFrom();
|
||||
if (fromDate == null) {
|
||||
fromDate = periodStartDate;
|
||||
}
|
||||
interestRates.put(fromDate, loanTermVariation.getTermValue());
|
||||
if (!principalVariation.containsKey(fromDate)) {
|
||||
principalVariation.put(fromDate, balanceForInterestCalculation.zero());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (principalVariation != null) {
|
||||
// identifies rest date after current date for reducing all
|
||||
// compounding
|
||||
@@ -102,6 +121,9 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
compoundedMoney = compoundedMoney.plus(interestToBeCompounded).plus(compoundFee);
|
||||
}
|
||||
balanceForInterestCalculation = balanceForInterestCalculation.plus(principal.getValue()).plus(compoundFee);
|
||||
if (interestRates.containsKey(principal.getKey())) {
|
||||
loanApplicationTerms.updateAnnualNominalInterestRate(interestRates.get(principal.getKey()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -125,9 +147,8 @@ public class DecliningBalanceInterestLoanScheduleGenerator extends AbstractLoanS
|
||||
} else {
|
||||
interestForThisInstallment = interestForThisInstallment.plus(calculateInterestForDays(daysForInterestInFullPeriod, result
|
||||
.interest().getAmount(), interestForDays));
|
||||
cumulatingInterestDueToGrace = cumulatingInterestDueToGrace.plus(calculateInterestForDays(
|
||||
daysForInterestInFullPeriod, result.interestPaymentDueToGrace().minus(cumulatingInterestDueToGrace)
|
||||
.getAmount(), interestForDays));
|
||||
cumulatingInterestDueToGrace = cumulatingInterestDueToGrace.plus(calculateInterestForDays(daysForInterestInFullPeriod, result
|
||||
.interestPaymentDueToGrace().minus(cumulatingInterestDueToGrace).getAmount(), interestForDays));
|
||||
}
|
||||
|
||||
Money interestForPeriod = interestForThisInstallment;
|
||||
|
||||
+3
-1
@@ -6,12 +6,14 @@
|
||||
package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.MathContext;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.joda.time.Days;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData;
|
||||
|
||||
public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGenerator {
|
||||
|
||||
@@ -22,7 +24,7 @@ public class FlatInterestLoanScheduleGenerator extends AbstractLoanScheduleGener
|
||||
final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
|
||||
@SuppressWarnings("unused") TreeMap<LocalDate, Money> principalVariation,
|
||||
@SuppressWarnings("unused") Map<LocalDate, Money> compoundingMap, LocalDate periodStartDate, LocalDate periodEndDate,
|
||||
@SuppressWarnings("unused") int daysForInterestInFullPeriod) {
|
||||
@SuppressWarnings("unused") int daysForInterestInFullPeriod, @SuppressWarnings("unused") Collection<LoanTermVariationsData> termVariations) {
|
||||
final int daysInPeriodApplicableForInterest = Days.daysBetween(periodStartDate, periodEndDate).getDays();
|
||||
Money principalForThisInstallment = loanApplicationTerms.calculateTotalPrincipalForPeriod(calculator,
|
||||
daysInPeriodApplicableForInterest, outstandingBalance, periodNumber, mc, null);
|
||||
|
||||
+21
-37
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanaccount.loanschedule.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@@ -151,6 +152,8 @@ public final class LoanApplicationTerms {
|
||||
|
||||
private Money approvedPrincipal = null;
|
||||
|
||||
private final Collection<LoanTermVariationsData> loanTermVariations;
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery,
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType,
|
||||
@@ -168,7 +171,7 @@ public final class LoanApplicationTerms {
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
||||
BigDecimal approvedAmount) {
|
||||
BigDecimal approvedAmount, Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
|
||||
final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null;
|
||||
@@ -180,7 +183,7 @@ public final class LoanApplicationTerms {
|
||||
disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance,
|
||||
recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount);
|
||||
installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -195,7 +198,8 @@ public final class LoanApplicationTerms {
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount) {
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount, BigDecimal annualNominalInterestRate,
|
||||
Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
final Calendar loanCalendar = null;
|
||||
|
||||
return assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, nthDay, dayOfWeek,
|
||||
@@ -203,7 +207,8 @@ public final class LoanApplicationTerms {
|
||||
loanProductRelatedDetail, multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations,
|
||||
interestChargedFromDate, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType,
|
||||
restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType,
|
||||
loanPreClosureInterestCalculationStrategy, rescheduleStrategyMethod, loanCalendar, approvedAmount);
|
||||
loanPreClosureInterestCalculationStrategy, rescheduleStrategyMethod, loanCalendar, approvedAmount,
|
||||
annualNominalInterestRate, loanTermVariations);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -218,7 +223,8 @@ public final class LoanApplicationTerms {
|
||||
final InterestRecalculationCompoundingMethod compoundingMethod, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy,
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount) {
|
||||
final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount,
|
||||
BigDecimal annualNominalInterestRate, Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -227,7 +233,6 @@ public final class LoanApplicationTerms {
|
||||
final InterestMethod interestMethod = loanProductRelatedDetail.getInterestMethod();
|
||||
final BigDecimal interestRatePerPeriod = loanProductRelatedDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = loanProductRelatedDetail.getInterestPeriodFrequencyType();
|
||||
final BigDecimal annualNominalInterestRate = loanProductRelatedDetail.getAnnualNominalInterestRate();
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = loanProductRelatedDetail
|
||||
.getInterestCalculationPeriodMethod();
|
||||
final Money principalMoney = loanProductRelatedDetail.getPrincipal();
|
||||
@@ -250,7 +255,7 @@ public final class LoanApplicationTerms {
|
||||
loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount);
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency,
|
||||
@@ -263,7 +268,8 @@ public final class LoanApplicationTerms {
|
||||
final RecalculationFrequencyType recalculationFrequencyType, final CalendarInstance compoundingCalendarInstance,
|
||||
final RecalculationFrequencyType compoundingFrequencyType, final BigDecimal principalThresholdForLastInstalment,
|
||||
final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, BigDecimal approvedAmount) {
|
||||
final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, BigDecimal approvedAmount,
|
||||
final BigDecimal annualNominalInterestRate, final Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
|
||||
final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments();
|
||||
final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery();
|
||||
@@ -272,7 +278,6 @@ public final class LoanApplicationTerms {
|
||||
final InterestMethod interestMethod = loanProductRelatedDetail.getInterestMethod();
|
||||
final BigDecimal interestRatePerPeriod = loanProductRelatedDetail.getNominalInterestRatePerPeriod();
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = loanProductRelatedDetail.getInterestPeriodFrequencyType();
|
||||
final BigDecimal annualNominalInterestRate = loanProductRelatedDetail.getAnnualNominalInterestRate();
|
||||
final InterestCalculationPeriodMethod interestCalculationPeriodMethod = loanProductRelatedDetail
|
||||
.getInterestCalculationPeriodMethod();
|
||||
final Money principalMoney = loanProductRelatedDetail.getPrincipal();
|
||||
@@ -302,28 +307,7 @@ public final class LoanApplicationTerms {
|
||||
daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod,
|
||||
interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance,
|
||||
compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf,
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount);
|
||||
}
|
||||
|
||||
public static LoanApplicationTerms assembleFrom(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType,
|
||||
final LoanApplicationTerms applicationTerms, BigDecimal approvedAmount) {
|
||||
return new LoanApplicationTerms(applicationTerms.currency, applicationTerms.loanTermFrequency,
|
||||
applicationTerms.loanTermPeriodFrequencyType, applicationTerms.numberOfRepayments, repaymentEvery,
|
||||
repaymentPeriodFrequencyType, null, null, applicationTerms.amortizationMethod, applicationTerms.interestMethod,
|
||||
applicationTerms.interestRatePerPeriod, applicationTerms.interestRatePeriodFrequencyType,
|
||||
applicationTerms.annualNominalInterestRate, applicationTerms.interestCalculationPeriodMethod, applicationTerms.principal,
|
||||
applicationTerms.expectedDisbursementDate, applicationTerms.repaymentsStartingFromDate,
|
||||
applicationTerms.calculatedRepaymentsStartingFromDate, applicationTerms.principalGrace,
|
||||
applicationTerms.interestPaymentGrace, applicationTerms.interestChargingGrace, applicationTerms.interestChargedFromDate,
|
||||
applicationTerms.inArrearsTolerance, applicationTerms.multiDisburseLoan, applicationTerms.fixedEmiAmount,
|
||||
applicationTerms.disbursementDatas, applicationTerms.maxOutstandingBalance, applicationTerms.emiAmountVariations,
|
||||
applicationTerms.graceOnArrearsAgeing, applicationTerms.daysInMonthType, applicationTerms.daysInYearType,
|
||||
applicationTerms.interestRecalculationEnabled, applicationTerms.rescheduleStrategyMethod,
|
||||
applicationTerms.interestRecalculationCompoundingMethod, applicationTerms.restCalendarInstance,
|
||||
applicationTerms.recalculationFrequencyType, applicationTerms.compoundingCalendarInstance,
|
||||
applicationTerms.compoundingFrequencyType, applicationTerms.principalThresholdForLastInstalment,
|
||||
applicationTerms.installmentAmountInMultiplesOf, applicationTerms.preClosureInterestCalculationStrategy,
|
||||
applicationTerms.loanCalendar, approvedAmount);
|
||||
loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations);
|
||||
}
|
||||
|
||||
private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency,
|
||||
@@ -344,7 +328,7 @@ public final class LoanApplicationTerms {
|
||||
final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType,
|
||||
final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf,
|
||||
final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar,
|
||||
BigDecimal approvedAmount) {
|
||||
BigDecimal approvedAmount, Collection<LoanTermVariationsData> loanTermVariations) {
|
||||
this.currency = currency;
|
||||
this.loanTermFrequency = loanTermFrequency;
|
||||
this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType;
|
||||
@@ -394,6 +378,7 @@ public final class LoanApplicationTerms {
|
||||
|
||||
this.loanCalendar = loanCalendar;
|
||||
this.approvedPrincipal = Money.of(principal.getCurrency(), approvedAmount);
|
||||
this.loanTermVariations = loanTermVariations;
|
||||
}
|
||||
|
||||
public Money adjustPrincipalIfLastRepaymentPeriod(final Money principalForPeriod, final Money totalCumulativePrincipalToDate,
|
||||
@@ -1215,10 +1200,6 @@ public final class LoanApplicationTerms {
|
||||
}
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRatePerPeriod() {
|
||||
return this.interestRatePerPeriod;
|
||||
}
|
||||
|
||||
public BigDecimal getAnnualNominalInterestRate() {
|
||||
return this.annualNominalInterestRate;
|
||||
}
|
||||
@@ -1270,9 +1251,12 @@ public final class LoanApplicationTerms {
|
||||
return loanCalendar;
|
||||
}
|
||||
|
||||
|
||||
public BigDecimal getFixedPrincipalAmount() {
|
||||
return this.fixedPrincipalAmount;
|
||||
}
|
||||
|
||||
public Collection<LoanTermVariationsData> getLoanTermVariations() {
|
||||
return this.loanTermVariations;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -14,6 +14,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
@@ -38,5 +39,5 @@ public interface LoanScheduleGenerator {
|
||||
|
||||
LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest,
|
||||
final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance,
|
||||
CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar);
|
||||
CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, FloatingRateDTO floatingRateDTO);
|
||||
}
|
||||
+40
-4
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@@ -17,6 +18,7 @@ import org.apache.commons.lang.StringUtils;
|
||||
import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.core.serialization.FromJsonHelper;
|
||||
import org.mifosplatform.infrastructure.core.service.DateUtils;
|
||||
import org.mifosplatform.organisation.holiday.domain.Holiday;
|
||||
import org.mifosplatform.organisation.holiday.domain.HolidayRepository;
|
||||
import org.mifosplatform.organisation.holiday.domain.HolidayStatusType;
|
||||
@@ -45,6 +47,9 @@ import org.mifosplatform.portfolio.common.domain.DayOfWeekType;
|
||||
import org.mifosplatform.portfolio.common.domain.DaysInMonthType;
|
||||
import org.mifosplatform.portfolio.common.domain.DaysInYearType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
@@ -99,6 +104,7 @@ public class LoanScheduleAssembler {
|
||||
private final ClientRepositoryWrapper clientRepository;
|
||||
private final GroupRepositoryWrapper groupRepository;
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleAssembler(final FromJsonHelper fromApiJsonHelper, final LoanProductRepository loanProductRepository,
|
||||
@@ -107,7 +113,8 @@ public class LoanScheduleAssembler {
|
||||
final LoanChargeAssembler loanChargeAssembler, final CalendarRepository calendarRepository,
|
||||
final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService,
|
||||
final ClientRepositoryWrapper clientRepository, final GroupRepositoryWrapper groupRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository) {
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.fromApiJsonHelper = fromApiJsonHelper;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
@@ -120,6 +127,7 @@ public class LoanScheduleAssembler {
|
||||
this.clientRepository = clientRepository;
|
||||
this.groupRepository = groupRepository;
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
public LoanApplicationTerms assembleLoanTerms(final JsonElement element) {
|
||||
@@ -164,8 +172,10 @@ public class LoanScheduleAssembler {
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
|
||||
final PeriodFrequencyType interestRatePeriodFrequencyType = loanProduct.getInterestPeriodFrequencyType();
|
||||
|
||||
final BigDecimal annualNominalInterestRate = this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType,
|
||||
interestRatePerPeriod);
|
||||
BigDecimal annualNominalInterestRate = BigDecimal.ZERO;
|
||||
if (interestRatePerPeriod != null) {
|
||||
annualNominalInterestRate = this.aprCalculator.calculateFrom(interestRatePeriodFrequencyType, interestRatePerPeriod);
|
||||
}
|
||||
|
||||
// disbursement details
|
||||
final BigDecimal principal = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("principal", element);
|
||||
@@ -296,6 +306,31 @@ public class LoanScheduleAssembler {
|
||||
|
||||
final Integer installmentAmountInMultiplesOf = loanProduct.getInstallmentAmountInMultiplesOf();
|
||||
|
||||
Collection<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
final BigDecimal interestRateDiff = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
LoanApiConstants.interestRateDifferentialParameterName, element);
|
||||
final Boolean isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
LoanApiConstants.isFloatingInterestRateParameterName, element);
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
FloatingRateDTO floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, expectedDisbursementDate, interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
Collection<FloatingRatePeriodData> applicableRates = loanProduct.fetchInterestRates(floatingRateDTO);
|
||||
|
||||
LocalDate interestRateStartDate = DateUtils.getLocalDateOfTenant();
|
||||
for (FloatingRatePeriodData periodData : applicableRates) {
|
||||
LoanTermVariationsData loanTermVariation = new LoanTermVariationsData(
|
||||
LoanEnumerations.loanvariationType(LoanTermVariationType.INTEREST_RATE), periodData.getFromDateAsLocalDate(),
|
||||
periodData.getInterestRate());
|
||||
if (interestRateStartDate.isAfter(periodData.getFromDateAsLocalDate())) {
|
||||
interestRateStartDate = periodData.getFromDateAsLocalDate();
|
||||
annualNominalInterestRate = periodData.getInterestRate();
|
||||
}
|
||||
loanTermVariations.add(loanTermVariation);
|
||||
}
|
||||
}
|
||||
|
||||
return LoanApplicationTerms.assembleFrom(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments,
|
||||
repaymentEvery, repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod,
|
||||
interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod,
|
||||
@@ -304,7 +339,8 @@ public class LoanScheduleAssembler {
|
||||
loanProduct.isMultiDisburseLoan(), emiAmount, disbursementDatas, maxOutstandingBalance, loanVariationTermsData,
|
||||
graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType,
|
||||
restCalendarInstance, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment,
|
||||
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO);
|
||||
installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), calendar, BigDecimal.ZERO,
|
||||
loanTermVariations);
|
||||
}
|
||||
|
||||
private CalendarInstance createInterestRecalculationCalendarInstance(final LocalDate calendarStartDate,
|
||||
|
||||
+25
-5
@@ -26,6 +26,9 @@ import org.mifosplatform.portfolio.accountdetails.domain.AccountType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
@@ -63,6 +66,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanScheduleCalculationPlatformServiceImpl(final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer,
|
||||
@@ -72,7 +76,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
final LoanAssembler loanAssembler, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
|
||||
final LoanAccountDomainService accountDomainService, final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final ConfigurationDomainService configurationDomainService) {
|
||||
final ConfigurationDomainService configurationDomainService,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.loanScheduleAssembler = loanScheduleAssembler;
|
||||
this.fromJsonHelper = fromJsonHelper;
|
||||
@@ -86,6 +91,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,11 +170,12 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
LocalDate calculatedRepaymentsStartingFromDate = accountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
LoanApplicationTerms loanApplicationTerms = loan.constructLoanApplicationTerms(applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, restCalendarInstance, compoundingCalendarInstance);
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(
|
||||
currency, today, loanApplicationTerms, loan.charges(), loan.getOfficeId(), loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals(),
|
||||
loanRepaymentScheduleTransactionProcessor);
|
||||
calculatedRepaymentsStartingFromDate, restCalendarInstance, compoundingCalendarInstance, floatingRateDTO);
|
||||
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = this.loanScheduleAssembler.calculatePrepaymentAmount(currency,
|
||||
today, loanApplicationTerms, loan.charges(), loan.getOfficeId(),
|
||||
loan.retreiveListOfTransactionsPostDisbursementExcludeAccruals(), loanRepaymentScheduleTransactionProcessor);
|
||||
Money totalAmount = totalPrincipal.plus(loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency)).plus(
|
||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency));
|
||||
Money interestDue = Money.zero(currency);
|
||||
@@ -209,4 +216,17 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC
|
||||
}
|
||||
loanScheduleData.updateFuturePeriods(futureInstallments);
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -10,6 +10,7 @@ import java.math.MathContext;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.DecliningBalanceInterestLoanScheduleGenerator;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.FlatInterestLoanScheduleGenerator;
|
||||
@@ -21,19 +22,21 @@ public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory {
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||
final HolidayDetailDTO holidayDetailDTO, final CalendarInstance restCalendarInstance,
|
||||
final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar) {
|
||||
final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, final FloatingRateDTO floatingRateDTO) {
|
||||
|
||||
LoanRescheduleModel loanRescheduleModel = null;
|
||||
|
||||
switch (interestMethod) {
|
||||
case DECLINING_BALANCE:
|
||||
loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||
applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar);
|
||||
applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar,
|
||||
floatingRateDTO);
|
||||
break;
|
||||
|
||||
case FLAT:
|
||||
loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest,
|
||||
applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar);
|
||||
applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar,
|
||||
floatingRateDTO);
|
||||
break;
|
||||
|
||||
case INVALID:
|
||||
|
||||
+2
-1
@@ -10,6 +10,7 @@ import java.math.MathContext;
|
||||
import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
|
||||
@@ -18,5 +19,5 @@ public interface LoanReschedulerFactory {
|
||||
public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod,
|
||||
final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency,
|
||||
final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance,
|
||||
final Calendar loanCalendar);
|
||||
final Calendar loanCalendar, FloatingRateDTO floatingRateDTO);
|
||||
}
|
||||
|
||||
+29
-8
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.rescheduleloan.service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
@@ -23,6 +24,9 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanRepaymentScheduleHistory;
|
||||
@@ -47,6 +51,7 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository,
|
||||
@@ -54,7 +59,8 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository,
|
||||
final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService,
|
||||
final CalendarInstanceRepository calendarInstanceRepository) {
|
||||
final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.loanRescheduleRequestRepository = loanRescheduleRequestRepository;
|
||||
this.applicationCurrencyRepository = applicationCurrencyRepository;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
@@ -62,6 +68,7 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,8 +80,8 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
Loan loan = loanRescheduleRequest.getLoan();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
@@ -83,16 +90,16 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
final InterestMethod interestMethod = loan.getLoanRepaymentScheduleDetail().getInterestMethod();
|
||||
final RoundingMode roundingMode = MoneyHelper.getRoundingMode();
|
||||
final MathContext mathContext = new MathContext(8, roundingMode);
|
||||
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService
|
||||
.createLoanScheduleArchive(loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
List<LoanRepaymentScheduleHistory> oldPeriods = this.loanScheduleHistoryWritePlatformService.createLoanScheduleArchive(
|
||||
loan.getRepaymentScheduleInstallments(), loan, loanRescheduleRequest);
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
}
|
||||
final CalendarInstance loanCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
@@ -100,12 +107,26 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP
|
||||
if (loanCalendarInstance != null) {
|
||||
loanCalendar = loanCalendarInstance.getCalendar();
|
||||
}
|
||||
final FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
loanCalendar);
|
||||
loanCalendar, floatingRateDTO);
|
||||
LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel,
|
||||
oldPeriods);
|
||||
return loanRescheduleModelWithOldPeriods;
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-17
@@ -36,6 +36,9 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.HolidayDetailDTO;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanChargePaidByData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.ScheduleGeneratorDTO;
|
||||
@@ -84,6 +87,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
private final JournalEntryWritePlatformService journalEntryWritePlatformService;
|
||||
private final LoanRepository loanRepository;
|
||||
private final LoanAssembler loanAssembler;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
/**
|
||||
* LoanRescheduleRequestWritePlatformServiceImpl constructor
|
||||
@@ -103,7 +107,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
final LoanChargeReadPlatformService loanChargeReadPlatformService, final LoanScheduleGeneratorFactory loanScheduleFactory,
|
||||
final LoanTransactionRepository loanTransactionRepository,
|
||||
final JournalEntryWritePlatformService journalEntryWritePlatformService, final LoanRepository loanRepository,
|
||||
final LoanAssembler loanAssembler) {
|
||||
final LoanAssembler loanAssembler, final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.loanRepositoryWrapper = loanRepositoryWrapper;
|
||||
this.codeValueRepositoryWrapper = codeValueRepositoryWrapper;
|
||||
this.platformSecurityContext = platformSecurityContext;
|
||||
@@ -123,6 +127,7 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
this.journalEntryWritePlatformService = journalEntryWritePlatformService;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanAssembler = loanAssembler;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -272,8 +277,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
|
||||
LocalDate approvedOnDate = jsonCommand.localDateValueOfParameterNamed("approvedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat())
|
||||
.withLocale(jsonCommand.extractLocale());
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(
|
||||
jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
@@ -285,8 +290,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
final LoanSummary loanSummary = loan.getSummary();
|
||||
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate());
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getDisbursementDate().toDate());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
final LoanProductMinimumRepaymentScheduleRelatedDetail loanProductRelatedDetail = loan.getLoanRepaymentScheduleDetail();
|
||||
final MonetaryCurrency currency = loanProductRelatedDetail.getCurrency();
|
||||
@@ -314,9 +319,10 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
if (loanCalendarInstance != null) {
|
||||
loanCalendar = loanCalendarInstance.getCalendar();
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod,
|
||||
loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
loanCalendar);
|
||||
loanCalendar, floatingRateDTO);
|
||||
|
||||
final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods();
|
||||
List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments();
|
||||
@@ -373,7 +379,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
waiveLoanCharges(loan, waiveLoanCharges);
|
||||
|
||||
// update the Loan summary
|
||||
loanSummary.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true, null);
|
||||
loanSummary
|
||||
.updateSummary(currency, loan.getPrincpal(), repaymentScheduleInstallments, new LoanSummaryWrapper(), true, null);
|
||||
|
||||
// update the total number of schedule repayments
|
||||
loan.updateNumberOfRepayments(periods.size());
|
||||
@@ -458,26 +465,27 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
calculatedRepaymentsStartingFromDate = this.loanAccountDomainService
|
||||
.getCalculatedRepaymentsStartingFromDate(loan.getDisbursementDate(), loan, calendarInstance);
|
||||
calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
|
||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate());
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate()
|
||||
.toDate());
|
||||
workingDays = this.workingDaysRepository.findOne();
|
||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
recalculateFrom = DateUtils.getLocalDateOfTenant();
|
||||
recalculateFrom = DateUtils.getLocalDateOfTenant();
|
||||
}
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
recalculateFrom, overdurPenaltyWaitPeriod);
|
||||
recalculateFrom, overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
Money accruedCharge = Money.zero(loan.getCurrency());
|
||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService
|
||||
.retriveLoanChargesPaidBy(loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber);
|
||||
Collection<LoanChargePaidByData> chargePaidByDatas = this.loanChargeReadPlatformService.retriveLoanChargesPaidBy(
|
||||
loanCharge.getId(), LoanTransactionType.ACCRUAL, loanInstallmentNumber);
|
||||
for (LoanChargePaidByData chargePaidByData : chargePaidByDatas) {
|
||||
accruedCharge = accruedCharge.plus(chargePaidByData.getAmount());
|
||||
}
|
||||
@@ -527,8 +535,8 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
final Map<String, Object> changes = new LinkedHashMap<>();
|
||||
|
||||
LocalDate rejectedOnDate = jsonCommand.localDateValueOfParameterNamed("rejectedOnDate");
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat())
|
||||
.withLocale(jsonCommand.extractLocale());
|
||||
final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(jsonCommand.dateFormat()).withLocale(
|
||||
jsonCommand.extractLocale());
|
||||
|
||||
changes.put("locale", jsonCommand.locale());
|
||||
changes.put("dateFormat", jsonCommand.dateFormat());
|
||||
@@ -567,4 +575,17 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
||||
throw new PlatformDataIntegrityException("error.msg.loan.reschedule.unknown.data.integrity.issue",
|
||||
"Unknown data integrity issue with resource.");
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-6
@@ -36,12 +36,14 @@ public final class CalculateLoanScheduleQueryFromApiJsonHelper {
|
||||
final Set<String> supportedParameters = new HashSet<>(Arrays.asList("id", "clientId", "groupId", "loanType", "calendarId", "productId",
|
||||
"accountNo", "externalId", "fundId", "loanOfficerId", "loanPurposeId", "transactionProcessingStrategyId", "principal",
|
||||
"inArrearsTolerance", "interestRatePerPeriod", "repaymentEvery", "numberOfRepayments", "loanTermFrequency",
|
||||
"loanTermFrequencyType", "repaymentFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType", "interestRateFrequencyType",
|
||||
"expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment", "graceOnInterestPayment",
|
||||
"graceOnInterestCharged", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale", "dateFormat", "charges",
|
||||
"collateral", "syncDisbursementWithMeeting", "linkAccountId", LoanApiConstants.disbursementDataParameterName,
|
||||
LoanApiConstants.emiAmountParameterName, LoanApiConstants.maxOutstandingBalanceParameterName,
|
||||
LoanProductConstants.graceOnArrearsAgeingParameterName, LoanProductConstants.recalculationRestFrequencyDateParamName,"createStandingInstructionAtDisbursement"));
|
||||
"loanTermFrequencyType", "repaymentFrequencyType", "amortizationType", "interestType", "interestCalculationPeriodType",
|
||||
"interestRateFrequencyType", "expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment",
|
||||
"graceOnInterestPayment", "graceOnInterestCharged", "interestChargedFromDate", "submittedOnDate", "submittedOnNote", "locale",
|
||||
"dateFormat", "charges", "collateral", "syncDisbursementWithMeeting", "linkAccountId",
|
||||
LoanApiConstants.disbursementDataParameterName, LoanApiConstants.emiAmountParameterName,
|
||||
LoanApiConstants.maxOutstandingBalanceParameterName, LoanProductConstants.graceOnArrearsAgeingParameterName,
|
||||
LoanProductConstants.recalculationRestFrequencyDateParamName, "createStandingInstructionAtDisbursement",
|
||||
LoanApiConstants.isFloatingInterestRateParameterName, LoanApiConstants.interestRateDifferentialParameterName));
|
||||
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
|
||||
|
||||
+160
-29
@@ -48,10 +48,10 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
*/
|
||||
final Set<String> supportedParameters = new HashSet<>(Arrays.asList("dateFormat", "locale", "id", "clientId", "groupId", "loanType",
|
||||
"productId", "principal", "loanTermFrequency", "loanTermFrequencyType", "numberOfRepayments", "repaymentEvery",
|
||||
"repaymentFrequencyType", "repaymentFrequencyNthDayType", "repaymentFrequencyDayOfWeekType",
|
||||
"interestRatePerPeriod",
|
||||
"amortizationType",
|
||||
"interestType",
|
||||
"repaymentFrequencyType", "repaymentFrequencyNthDayType", "repaymentFrequencyDayOfWeekType", "interestRatePerPeriod",
|
||||
"amortizationType", "interestType",
|
||||
LoanApiConstants.isFloatingInterestRate,
|
||||
LoanApiConstants.interestRateDifferential,
|
||||
"interestCalculationPeriodType",
|
||||
"interestRateFrequencyType",
|
||||
"expectedDisbursementDate",
|
||||
@@ -203,12 +203,6 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
baseDataValidator.reset().parameter(repaymentEveryFrequencyTypeParameterName).value(repaymentEveryType).notNull()
|
||||
.inMinMaxRange(0, 3);
|
||||
|
||||
final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
interestRatePerPeriodParameterName, element);
|
||||
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String interestTypeParameterName = "interestType";
|
||||
final Integer interestType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestTypeParameterName, element);
|
||||
baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).notNull().inMinMaxRange(0, 1);
|
||||
@@ -219,6 +213,71 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
baseDataValidator.reset().parameter(interestCalculationPeriodTypeParameterName).value(interestCalculationPeriodType).notNull()
|
||||
.inMinMaxRange(0, 1);
|
||||
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRatePerPeriod")
|
||||
.failWithCode("not.supported.loanproduct.linked.to.floating.rate",
|
||||
"interestRatePerPeriod param is not supported, selected Loan Product is linked with floating interest rate.");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)){
|
||||
final Boolean isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
LoanApiConstants.isFloatingInterestRate, element);
|
||||
if(isFloatingInterestRate != null && isFloatingInterestRate && !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()){
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode("true.not.supported.for.selected.loanproduct",
|
||||
"isFloatingInterestRate value of true not supported for selected Loan Product.");
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).trueOrFalseRequired(false);
|
||||
}
|
||||
|
||||
if (interestType != null && interestType == InterestMethod.FLAT.getValue()) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(interestTypeParameterName)
|
||||
.failWithCode("should.be.0.for.selected.loan.product",
|
||||
"interestType should be DECLINING_BALANCE for selected Loan Product as it is linked to floating rates.");
|
||||
}
|
||||
|
||||
final String interestRateDifferentialParameterName = LoanApiConstants.interestRateDifferential;
|
||||
final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
interestRateDifferentialParameterName, element);
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(interestRateDifferentialParameterName)
|
||||
.value(interestRateDifferential)
|
||||
.notNull()
|
||||
.zeroOrPositiveAmount()
|
||||
.inMinAndMaxAmountRange(loanProduct.getFloatingRates().getMinDifferentialLendingRate(),
|
||||
loanProduct.getFloatingRates().getMaxDifferentialLendingRate());
|
||||
|
||||
} else {
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanApiConstants.isFloatingInterestRate)
|
||||
.failWithCode("not.supported.loanproduct.not.linked.to.floating.rate",
|
||||
"isFloatingInterestRate param is not supported, selected Loan Product is not linked with floating interest rate.");
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.interestRateDifferential, element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanApiConstants.interestRateDifferential)
|
||||
.failWithCode("not.supported.loanproduct.not.linked.to.floating.rate",
|
||||
"interestRateDifferential param is not supported, selected Loan Product is not linked with floating interest rate.");
|
||||
}
|
||||
|
||||
final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
interestRatePerPeriodParameterName, element);
|
||||
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
}
|
||||
|
||||
final String amortizationTypeParameterName = "amortizationType";
|
||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(amortizationTypeParameterName, element);
|
||||
baseDataValidator.reset().parameter(amortizationTypeParameterName).value(amortizationType).notNull().inMinMaxRange(0, 1);
|
||||
@@ -392,7 +451,7 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
public void validateForModify(final String json, final LoanProduct loanProduct) {
|
||||
public void validateForModify(final String json, final LoanProduct loanProduct, final Loan existingLoanApplication) {
|
||||
if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); }
|
||||
|
||||
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
|
||||
@@ -518,22 +577,93 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
baseDataValidator.reset().parameter(repaymentEveryTypeParameterName).value(repaymentEveryType).notNull().inMinMaxRange(0, 3);
|
||||
}
|
||||
|
||||
final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
|
||||
if (this.fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(
|
||||
interestRatePerPeriodParameterName, element);
|
||||
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String interestTypeParameterName = "interestType";
|
||||
Integer interestType = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(interestTypeParameterName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
final Integer interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(interestTypeParameterName, element);
|
||||
interestType = this.fromApiJsonHelper.extractIntegerWithLocaleNamed(interestTypeParameterName, element);
|
||||
baseDataValidator.reset().parameter(interestTypeParameterName).value(interestType).notNull().inMinMaxRange(0, 1);
|
||||
}
|
||||
|
||||
if (loanProduct.isLinkedToFloatingInterestRate()) {
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter("interestRatePerPeriod")
|
||||
.failWithCode("not.supported.loanproduct.linked.to.floating.rate",
|
||||
"interestRatePerPeriod param is not supported, selected Loan Product is linked with floating interest rate.");
|
||||
}
|
||||
|
||||
Boolean isFloatingInterestRate = existingLoanApplication.getIsFloatingInterestRate();
|
||||
if(this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)){
|
||||
isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isFloatingInterestRate, element);
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
if(isFloatingInterestRate != null){
|
||||
if(isFloatingInterestRate
|
||||
&& !loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed()){
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).failWithCode("true.not.supported.for.selected.loanproduct",
|
||||
"isFloatingInterestRate value of true not supported for selected Loan Product.");
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.isFloatingInterestRate).trueOrFalseRequired(false);
|
||||
}
|
||||
|
||||
if (interestType == null) {
|
||||
interestType = existingLoanApplication.getLoanProductRelatedDetail().getInterestMethod().getValue();
|
||||
}
|
||||
if (interestType != null && interestType == InterestMethod.FLAT.getValue()) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(interestTypeParameterName)
|
||||
.failWithCode("should.be.0.for.selected.loan.product",
|
||||
"interestType should be DECLINING_BALANCE for selected Loan Product as it is linked to floating rates.");
|
||||
}
|
||||
|
||||
final String interestRateDifferentialParameterName = LoanApiConstants.interestRateDifferential;
|
||||
BigDecimal interestRateDifferential = existingLoanApplication.getInterestRateDifferential();
|
||||
if (this.fromApiJsonHelper.parameterExists(interestRateDifferentialParameterName, element)) {
|
||||
interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(interestRateDifferentialParameterName,
|
||||
element);
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(interestRateDifferentialParameterName)
|
||||
.value(interestRateDifferential)
|
||||
.notNull()
|
||||
.zeroOrPositiveAmount()
|
||||
.inMinAndMaxAmountRange(loanProduct.getFloatingRates().getMinDifferentialLendingRate(),
|
||||
loanProduct.getFloatingRates().getMaxDifferentialLendingRate());
|
||||
|
||||
} else {
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.isFloatingInterestRate, element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanApiConstants.isFloatingInterestRate)
|
||||
.failWithCode("not.supported.loanproduct.not.linked.to.floating.rate",
|
||||
"isFloatingInterestRate param is not supported, selected Loan Product is not linked with floating interest rate.");
|
||||
}
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.interestRateDifferential, element)) {
|
||||
baseDataValidator
|
||||
.reset()
|
||||
.parameter(LoanApiConstants.interestRateDifferential)
|
||||
.failWithCode("not.supported.loanproduct.not.linked.to.floating.rate",
|
||||
"interestRateDifferential param is not supported, selected Loan Product is not linked with floating interest rate.");
|
||||
}
|
||||
|
||||
final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
|
||||
BigDecimal interestRatePerPeriod = existingLoanApplication.getLoanProductRelatedDetail().getNominalInterestRatePerPeriod();
|
||||
if (this.fromApiJsonHelper.parameterExists(interestRatePerPeriodParameterName, element)) {
|
||||
this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(interestRatePerPeriodParameterName, element);
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
}
|
||||
baseDataValidator.reset().parameter(interestRatePerPeriodParameterName).value(interestRatePerPeriod).notNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
}
|
||||
|
||||
final String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType";
|
||||
if (this.fromApiJsonHelper.parameterExists(interestCalculationPeriodTypeParameterName, element)) {
|
||||
atLeastOneParameterPassedForUpdate = true;
|
||||
@@ -849,23 +979,23 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
if (variationArray != null) {
|
||||
for (int i = 0; i < variationArray.size(); i++) {
|
||||
final JsonObject jsonObject1 = variationArray.get(i).getAsJsonObject();
|
||||
if(jsonObject1.has(LoanApiConstants.disbursementDateParameterName)){
|
||||
LocalDate date1 = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.disbursementDateParameterName, jsonObject1,
|
||||
dateFormat, locale);
|
||||
if (jsonObject1.has(LoanApiConstants.disbursementDateParameterName)) {
|
||||
LocalDate date1 = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.disbursementDateParameterName,
|
||||
jsonObject1, dateFormat, locale);
|
||||
|
||||
for (int j = i + 1; j < variationArray.size(); j++) {
|
||||
final JsonObject jsonObject2 = variationArray.get(j).getAsJsonObject();
|
||||
if(jsonObject2.has(LoanApiConstants.disbursementDateParameterName)){
|
||||
if (jsonObject2.has(LoanApiConstants.disbursementDateParameterName)) {
|
||||
LocalDate date2 = this.fromApiJsonHelper.extractLocalDateNamed(LoanApiConstants.disbursementDateParameterName,
|
||||
jsonObject2, dateFormat, locale);
|
||||
if (date1.isAfter(date2)) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDataParameterName)
|
||||
.failWithCode(LoanApiConstants.DISBURSEMENT_DATES_NOT_IN_ORDER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -918,8 +1048,9 @@ public final class LoanApplicationCommandFromApiJsonHelper {
|
||||
if (expectedDisbursement.equals(expectedDisbursementDate)) {
|
||||
isFirstinstallmentOnExpectedDisbursementDate = true;
|
||||
}
|
||||
|
||||
}i++;
|
||||
|
||||
}
|
||||
i++;
|
||||
} while (i < variationArray.size());
|
||||
if (!isFirstinstallmentOnExpectedDisbursementDate) {
|
||||
baseDataValidator.reset().parameter(LoanApiConstants.disbursementDateParameterName)
|
||||
|
||||
+27
-16
@@ -472,13 +472,23 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
try {
|
||||
AppUser currentUser = getAppUserIfPresent();
|
||||
final Loan existingLoanApplication = retrieveLoanBy(loanId);
|
||||
this.fromApiJsonDeserializer.validateForModify(command.json(), existingLoanApplication.loanProduct());
|
||||
|
||||
checkClientOrGroupActive(existingLoanApplication);
|
||||
|
||||
if (!existingLoanApplication.isSubmittedAndPendingApproval()) { throw new LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified(
|
||||
loanId); }
|
||||
|
||||
final String productIdParamName = "productId";
|
||||
LoanProduct newLoanProduct = null;
|
||||
if (command.isChangeInLongParameterNamed(productIdParamName, existingLoanApplication.loanProduct().getId())) {
|
||||
final Long productId = command.longValueOfParameterNamed(productIdParamName);
|
||||
newLoanProduct = this.loanProductRepository.findOne(productId);
|
||||
if (newLoanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||
}
|
||||
|
||||
LoanProduct loanProductForValidations = newLoanProduct == null? existingLoanApplication.loanProduct(): newLoanProduct;
|
||||
|
||||
this.fromApiJsonDeserializer.validateForModify(command.json(), loanProductForValidations, existingLoanApplication);
|
||||
|
||||
checkClientOrGroupActive(existingLoanApplication);
|
||||
|
||||
final Set<LoanCharge> existingCharges = existingLoanApplication.charges();
|
||||
Map<Long, LoanChargeData> chargesMap = new HashMap<>();
|
||||
for (LoanCharge charge : existingCharges) {
|
||||
@@ -554,33 +564,34 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
|
||||
existingLoanApplication.updateGroup(group);
|
||||
}
|
||||
|
||||
final String productIdParamName = "productId";
|
||||
if (changes.containsKey(productIdParamName)) {
|
||||
final Long productId = command.longValueOfParameterNamed(productIdParamName);
|
||||
final LoanProduct loanProduct = this.loanProductRepository.findOne(productId);
|
||||
if (loanProduct == null) { throw new LoanProductNotFoundException(productId); }
|
||||
|
||||
existingLoanApplication.updateLoanProduct(loanProduct);
|
||||
if (newLoanProduct != null) {
|
||||
existingLoanApplication.updateLoanProduct(newLoanProduct);
|
||||
if (!changes.containsKey("interestRateFrequencyType")) {
|
||||
existingLoanApplication.updateInterestRateFrequencyType();
|
||||
}
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan");
|
||||
if (loanProduct.useBorrowerCycle()) {
|
||||
if (newLoanProduct.useBorrowerCycle()) {
|
||||
final Long clientId = this.fromJsonHelper.extractLongNamed("clientId", command.parsedJson());
|
||||
final Long groupId = this.fromJsonHelper.extractLongNamed("groupId", command.parsedJson());
|
||||
Integer cycleNumber = 0;
|
||||
if (clientId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, loanProduct.getId());
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(clientId, newLoanProduct.getId());
|
||||
} else if (groupId != null) {
|
||||
cycleNumber = this.loanReadPlatformService.retriveLoanCounter(groupId, AccountType.GROUP.getValue(),
|
||||
loanProduct.getId());
|
||||
newLoanProduct.getId());
|
||||
}
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
loanProduct, cycleNumber);
|
||||
newLoanProduct, cycleNumber);
|
||||
} else {
|
||||
this.loanProductCommandFromApiJsonDeserializer.validateMinMaxConstraints(command.parsedJson(), baseDataValidator,
|
||||
loanProduct);
|
||||
newLoanProduct);
|
||||
}
|
||||
if(newLoanProduct.isLinkedToFloatingInterestRate()){
|
||||
existingLoanApplication.getLoanProductRelatedDetail().updateForFloatingInterestRates();
|
||||
}else{
|
||||
existingLoanApplication.setInterestRateDifferential(null);
|
||||
existingLoanApplication.setIsFloatingInterestRate(null);
|
||||
}
|
||||
if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
|
||||
}
|
||||
|
||||
+7
-3
@@ -215,6 +215,9 @@ public class LoanAssembler {
|
||||
Group group = null;
|
||||
|
||||
final LoanProductRelatedDetail loanProductRelatedDetail = this.loanScheduleAssembler.assembleLoanProductRelatedDetail(element);
|
||||
|
||||
final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(LoanApiConstants.interestRateDifferentialParameterName, element);
|
||||
final Boolean isFloatingInterestRate = this.fromApiJsonHelper.extractBooleanNamed(LoanApiConstants.isFloatingInterestRateParameterName, element);
|
||||
|
||||
final String loanTypeParameterName = "loanType";
|
||||
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
|
||||
@@ -239,20 +242,21 @@ public class LoanAssembler {
|
||||
loanApplication = Loan.newIndividualLoanApplicationFromGroup(accountNo, client, group, loanType.getId().intValue(),
|
||||
loanProduct, fund, loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges,
|
||||
collateral, syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement);
|
||||
createStandingInstructionAtDisbursement, isFloatingInterestRate, interestRateDifferential);
|
||||
|
||||
} else if (group != null) {
|
||||
|
||||
loanApplication = Loan.newGroupLoanApplication(accountNo, group, loanType.getId().intValue(), loanProduct, fund, loanOfficer,
|
||||
loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
|
||||
syncDisbursementWithMeeting, fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance,
|
||||
createStandingInstructionAtDisbursement);
|
||||
createStandingInstructionAtDisbursement,isFloatingInterestRate, interestRateDifferential);
|
||||
|
||||
} else if (client != null) {
|
||||
|
||||
loanApplication = Loan.newIndividualLoanApplication(accountNo, client, loanType.getId().intValue(), loanProduct, fund,
|
||||
loanOfficer, loanPurpose, loanTransactionProcessingStrategy, loanProductRelatedDetail, loanCharges, collateral,
|
||||
fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement);
|
||||
fixedEmiAmount, disbursementDetails, maxOutstandingLoanBalance, createStandingInstructionAtDisbursement,
|
||||
isFloatingInterestRate, interestRateDifferential);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -14,6 +14,7 @@ import org.mifosplatform.infrastructure.core.service.Page;
|
||||
import org.mifosplatform.infrastructure.core.service.SearchParameters;
|
||||
import org.mifosplatform.organisation.staff.data.StaffData;
|
||||
import org.mifosplatform.portfolio.calendar.data.CalendarData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.DisbursementData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanAccountData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanApprovalData;
|
||||
@@ -95,7 +96,7 @@ public interface LoanReadPlatformService {
|
||||
|
||||
Collection<LoanScheduleAccrualData> retrivePeriodicAccrualData(LocalDate tillDate);
|
||||
|
||||
Collection<Long> fetchArrearLoans();
|
||||
Collection<Long> fetchLoansForInterestRecalculation();
|
||||
|
||||
LoanTransactionData retrieveLoanPrePaymentTemplate(Long loanId, LocalDate onDate);
|
||||
|
||||
@@ -110,4 +111,6 @@ public interface LoanReadPlatformService {
|
||||
PaidInAdvanceData retrieveTotalPaidInAdvance(Long loanId);
|
||||
|
||||
LoanTransactionData retrieveRefundByCashTemplate(Long loanId);
|
||||
|
||||
Collection<InterestRatePeriodData> retrieveLoanInterestRatePeriodData(Long loanId);
|
||||
}
|
||||
+202
-78
@@ -5,6 +5,8 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.service;
|
||||
|
||||
import static org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations.interestType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
@@ -61,6 +63,10 @@ import org.mifosplatform.portfolio.client.domain.ClientEnumerations;
|
||||
import org.mifosplatform.portfolio.client.service.ClientReadPlatformService;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.service.CommonEnumerations;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.InterestRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.fund.service.FundReadPlatformService;
|
||||
import org.mifosplatform.portfolio.group.data.GroupGeneralData;
|
||||
@@ -97,6 +103,7 @@ import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanSche
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.TransactionProcessingStrategyData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.InterestMethod;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanDropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.mifosplatform.portfolio.loanproduct.service.LoanProductReadPlatformService;
|
||||
@@ -142,6 +149,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
private final WorkingDaysRepositoryWrapper workingDaysRepository;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanReadPlatformServiceImpl(final PlatformSecurityContext context, final LoanRepository loanRepository,
|
||||
@@ -155,7 +163,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final LoanScheduleGeneratorFactory loanScheduleFactory, final CalendarInstanceRepository calendarInstanceRepository,
|
||||
final HolidayRepository holidayRepository, final ConfigurationDomainService configurationDomainService,
|
||||
final WorkingDaysRepositoryWrapper workingDaysRepository, PaymentTypeReadPlatformService paymentTypeReadPlatformService,
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory) {
|
||||
final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanRepository = loanRepository;
|
||||
this.loanTransactionRepository = loanTransactionRepository;
|
||||
@@ -178,6 +187,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
this.workingDaysRepository = workingDaysRepository;
|
||||
this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
|
||||
this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,8 +207,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
sqlBuilder.append(" left join m_office transferToOffice on transferToOffice.id = c.transfer_to_office_id ");
|
||||
sqlBuilder.append(" where l.id=? and ( o.hierarchy like ? or transferToOffice.hierarchy like ?)");
|
||||
|
||||
return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), rm,
|
||||
new Object[] { loanId, hierarchySearchString, hierarchySearchString });
|
||||
return this.jdbcTemplate.queryForObject(sqlBuilder.toString(), rm, new Object[] { loanId, hierarchySearchString,
|
||||
hierarchySearchString });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
throw new LoanNotFoundException(loanId);
|
||||
}
|
||||
@@ -236,7 +246,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
/***
|
||||
* TODO Vishwas: Remove references to "Contra" from the codebase
|
||||
***/
|
||||
final String sql = "select " + rm.LoanPaymentsSchema()
|
||||
final String sql = "select "
|
||||
+ rm.LoanPaymentsSchema()
|
||||
+ " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and (tr.is_reversed=0 or tr.manually_adjusted_or_reversed = 1) order by tr.transaction_date ASC,id ";
|
||||
return this.jdbcTemplate.query(sql, rm, new Object[] { loanId });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
@@ -395,10 +406,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final BigDecimal outstandingLoanBalance = null;
|
||||
final BigDecimal unrecognizedIncomePortion = null;
|
||||
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
|
||||
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getInterestOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment
|
||||
.getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency)
|
||||
.getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, unrecognizedIncomePortion,
|
||||
paymentOptions, null, null, null, outstandingLoanBalance, false);
|
||||
}
|
||||
@@ -425,26 +435,26 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
restCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(loan.loanInterestRecalculationDetailId(),
|
||||
CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
final boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(),
|
||||
loan.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan
|
||||
.getDisbursementDate().toDate(), HolidayStatusType.ACTIVE.getValue());
|
||||
final WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
}
|
||||
|
||||
final FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loan.fetchPrepaymentDetail(this.loanScheduleFactory,
|
||||
restCalendarInstance, compoundingCalendarInstance, holidayDetailDTO, onDate);
|
||||
restCalendarInstance, compoundingCalendarInstance, holidayDetailDTO, onDate, floatingRateDTO);
|
||||
final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
|
||||
final Collection<PaymentTypeData> paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||
final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount();
|
||||
final BigDecimal unrecognizedIncomePortion = null;
|
||||
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
|
||||
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getInterestOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getTotalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment
|
||||
.getPrincipalOutstanding(currency).getAmount(), loanRepaymentScheduleInstallment.getInterestOutstanding(currency)
|
||||
.getAmount(), loanRepaymentScheduleInstallment.getFeeChargesOutstanding(currency).getAmount(),
|
||||
loanRepaymentScheduleInstallment.getPenaltyChargesOutstanding(currency).getAmount(), null, unrecognizedIncomePortion,
|
||||
paymentOptions, null, null, null, outstandingLoanBalance, false);
|
||||
}
|
||||
@@ -544,6 +554,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
return "l.id as id, l.account_no as accountNo, l.external_id as externalId, l.fund_id as fundId, f.name as fundName,"
|
||||
+ " l.loan_type_enum as loanType, l.loanpurpose_cv_id as loanPurposeId, cv.code_value as loanPurposeName,"
|
||||
+ " lp.id as loanProductId, lp.name as loanProductName, lp.description as loanProductDescription,"
|
||||
+ " lp.is_linked_to_floating_interest_rates as isLoanProductLinkedToFloatingRate, "
|
||||
+ " lp.allow_multiple_disbursals as multiDisburseLoan,"
|
||||
+ " lp.can_define_fixed_emi_amount as canDefineInstallmentAmount,"
|
||||
+ " c.id as clientId, c.display_name as clientName, c.office_id as clientOfficeId,"
|
||||
@@ -568,14 +579,19 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " lps.name as transactionStrategyName, "
|
||||
+ " l.currency_code as currencyCode, l.currency_digits as currencyDigits, l.currency_multiplesof as inMultiplesOf, rc.`name` as currencyName, rc.display_symbol as currencyDisplaySymbol, rc.internationalized_name_code as currencyNameCode, "
|
||||
+ " l.loan_officer_id as loanOfficerId, s.display_name as loanOfficerName, "
|
||||
+ " l.principal_disbursed_derived as principalDisbursed," + " l.principal_repaid_derived as principalPaid,"
|
||||
+ " l.principal_disbursed_derived as principalDisbursed,"
|
||||
+ " l.principal_repaid_derived as principalPaid,"
|
||||
+ " l.principal_writtenoff_derived as principalWrittenOff,"
|
||||
+ " l.principal_outstanding_derived as principalOutstanding," + " l.interest_charged_derived as interestCharged,"
|
||||
+ " l.interest_repaid_derived as interestPaid," + " l.interest_waived_derived as interestWaived,"
|
||||
+ " l.interest_writtenoff_derived as interestWrittenOff," + " l.interest_outstanding_derived as interestOutstanding,"
|
||||
+ " l.principal_outstanding_derived as principalOutstanding,"
|
||||
+ " l.interest_charged_derived as interestCharged,"
|
||||
+ " l.interest_repaid_derived as interestPaid,"
|
||||
+ " l.interest_waived_derived as interestWaived,"
|
||||
+ " l.interest_writtenoff_derived as interestWrittenOff,"
|
||||
+ " l.interest_outstanding_derived as interestOutstanding,"
|
||||
+ " l.fee_charges_charged_derived as feeChargesCharged,"
|
||||
+ " l.total_charges_due_at_disbursement_derived as feeChargesDueAtDisbursementCharged,"
|
||||
+ " l.fee_charges_repaid_derived as feeChargesPaid," + " l.fee_charges_waived_derived as feeChargesWaived,"
|
||||
+ " l.fee_charges_repaid_derived as feeChargesPaid,"
|
||||
+ " l.fee_charges_waived_derived as feeChargesWaived,"
|
||||
+ " l.fee_charges_writtenoff_derived as feeChargesWrittenOff,"
|
||||
+ " l.fee_charges_outstanding_derived as feeChargesOutstanding,"
|
||||
+ " l.penalty_charges_charged_derived as penaltyChargesCharged,"
|
||||
@@ -583,14 +599,21 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " l.penalty_charges_waived_derived as penaltyChargesWaived,"
|
||||
+ " l.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff,"
|
||||
+ " l.penalty_charges_outstanding_derived as penaltyChargesOutstanding,"
|
||||
+ " l.total_expected_repayment_derived as totalExpectedRepayment," + " l.total_repayment_derived as totalRepayment,"
|
||||
+ " l.total_expected_costofloan_derived as totalExpectedCostOfLoan," + " l.total_costofloan_derived as totalCostOfLoan,"
|
||||
+ " l.total_waived_derived as totalWaived," + " l.total_writtenoff_derived as totalWrittenOff,"
|
||||
+ " l.total_outstanding_derived as totalOutstanding," + " l.total_overpaid_derived as totalOverpaid,"
|
||||
+ " l.fixed_emi_amount as fixedEmiAmount," + " l.max_outstanding_loan_balance as outstandingLoanBalance,"
|
||||
+ " la.principal_overdue_derived as principalOverdue," + " la.interest_overdue_derived as interestOverdue,"
|
||||
+ " l.total_expected_repayment_derived as totalExpectedRepayment,"
|
||||
+ " l.total_repayment_derived as totalRepayment,"
|
||||
+ " l.total_expected_costofloan_derived as totalExpectedCostOfLoan,"
|
||||
+ " l.total_costofloan_derived as totalCostOfLoan,"
|
||||
+ " l.total_waived_derived as totalWaived,"
|
||||
+ " l.total_writtenoff_derived as totalWrittenOff,"
|
||||
+ " l.total_outstanding_derived as totalOutstanding,"
|
||||
+ " l.total_overpaid_derived as totalOverpaid,"
|
||||
+ " l.fixed_emi_amount as fixedEmiAmount,"
|
||||
+ " l.max_outstanding_loan_balance as outstandingLoanBalance,"
|
||||
+ " la.principal_overdue_derived as principalOverdue,"
|
||||
+ " la.interest_overdue_derived as interestOverdue,"
|
||||
+ " la.fee_charges_overdue_derived as feeChargesOverdue,"
|
||||
+ " la.penalty_charges_overdue_derived as penaltyChargesOverdue," + " la.total_overdue_derived as totalOverdue,"
|
||||
+ " la.penalty_charges_overdue_derived as penaltyChargesOverdue,"
|
||||
+ " la.total_overdue_derived as totalOverdue,"
|
||||
+ " la.overdue_since_date_derived as overdueSinceDate,"
|
||||
+ " l.sync_disbursement_with_meeting as syncDisbursementWithMeeting,"
|
||||
+ " l.loan_counter as loanCounter, l.loan_product_counter as loanProductCounter,"
|
||||
@@ -601,7 +624,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " lir.rest_freqency_date as restFrequencyDate, "
|
||||
+ " lir.compounding_frequency_type_enum as compoundingFrequencyEnum, lir.compounding_frequency_interval as compoundingInterval, "
|
||||
+ " lir.compounding_freqency_date as compoundingFrequencyDate, "
|
||||
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement " + " from m_loan l" //
|
||||
+ " l.is_floating_interest_rate as isFloatingInterestRate, "
|
||||
+ " l.interest_rate_differential as interestRateDifferential, "
|
||||
+ " l.create_standing_instruction_at_disbursement as createStandingInstructionAtDisbursement "
|
||||
+ " from m_loan l" //
|
||||
+ " join m_product_loan lp on lp.id = l.product_id" //
|
||||
+ " left join m_loan_recalculation_details lir on lir.loan_id = l.id "
|
||||
+ " join m_currency rc on rc.`code` = l.currency_code" //
|
||||
@@ -614,7 +640,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " left join m_appuser rbu on rbu.id = l.rejectedon_userid"
|
||||
+ " left join m_appuser wbu on wbu.id = l.withdrawnon_userid"
|
||||
+ " left join m_appuser abu on abu.id = l.approvedon_userid"
|
||||
+ " left join m_appuser dbu on dbu.id = l.disbursedon_userid" + " left join m_appuser cbu on cbu.id = l.closedon_userid"
|
||||
+ " left join m_appuser dbu on dbu.id = l.disbursedon_userid"
|
||||
+ " left join m_appuser cbu on cbu.id = l.closedon_userid"
|
||||
+ " left join m_code_value cv on cv.id = l.loanpurpose_cv_id"
|
||||
+ " left join ref_loan_transaction_processing_strategy lps on lps.id = l.loan_transaction_strategy_id";
|
||||
}
|
||||
@@ -664,6 +691,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final Long loanProductId = JdbcSupport.getLong(rs, "loanProductId");
|
||||
final String loanProductName = rs.getString("loanProductName");
|
||||
final String loanProductDescription = rs.getString("loanProductDescription");
|
||||
final boolean isLoanProductLinkedToFloatingRate = rs.getBoolean("isLoanProductLinkedToFloatingRate");
|
||||
final Boolean multiDisburseLoan = rs.getBoolean("multiDisburseLoan");
|
||||
final Boolean canDefineInstallmentAmount = rs.getBoolean("canDefineInstallmentAmount");
|
||||
final BigDecimal outstandingLoanBalance = rs.getBigDecimal("outstandingLoanBalance");
|
||||
@@ -720,6 +748,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final Integer repaymentEvery = JdbcSupport.getInteger(rs, "repaymentEvery");
|
||||
final BigDecimal interestRatePerPeriod = rs.getBigDecimal("interestRatePerPeriod");
|
||||
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
|
||||
final BigDecimal interestRateDifferential = rs.getBigDecimal("interestRateDifferential");
|
||||
final boolean isFloatingInterestRate = rs.getBoolean("isFloatingInterestRate");
|
||||
|
||||
final Integer graceOnPrincipalPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnPrincipalPayment");
|
||||
final Integer graceOnInterestPayment = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "graceOnInterestPayment");
|
||||
@@ -873,16 +903,17 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientName, clientOfficeId, groupData,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, fundId, fundName, loanPurposeId, loanPurposeName,
|
||||
loanType, loanProductId, loanProductName, loanProductDescription, isLoanProductLinkedToFloatingRate, fundId, fundName, loanPurposeId, loanPurposeName,
|
||||
loanOfficerId, loanOfficerName, currencyData, proposedPrincipal, principal, approvedPrincipal, totalOverpaid,
|
||||
inArrearsTolerance, termFrequency, termPeriodFrequencyType, numberOfRepayments, repaymentEvery, repaymentFrequencyType,
|
||||
repaymentFrequencyNthDayType, repaymentFrequencyDayOfWeekType, transactionStrategyId, transactionStrategyName,
|
||||
amortizationType, interestRatePerPeriod, interestRateFrequencyType, annualInterestRate, interestType,
|
||||
interestCalculationPeriodType, expectedFirstRepaymentOnDate, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
graceOnInterestCharged, interestChargedFromDate, timeline, loanSummary, feeChargesDueAtDisbursementCharged,
|
||||
syncDisbursementWithMeeting, loanCounter, loanProductCounter, multiDisburseLoan, canDefineInstallmentAmount,
|
||||
fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing, isNPA, daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, interestRecalculationData, createStandingInstructionAtDisbursement);
|
||||
isFloatingInterestRate, interestRateDifferential, interestCalculationPeriodType, expectedFirstRepaymentOnDate,
|
||||
graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, timeline,
|
||||
loanSummary, feeChargesDueAtDisbursementCharged, syncDisbursementWithMeeting, loanCounter, loanProductCounter,
|
||||
multiDisburseLoan, canDefineInstallmentAmount, fixedEmiAmount, outstandingLoanBalance, inArrears, graceOnArrearsAgeing,
|
||||
isNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, interestRecalculationData,
|
||||
createStandingInstructionAtDisbursement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -895,7 +926,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " ls.fee_charges_amount as feeChargesDue, ls.fee_charges_completed_derived as feeChargesPaid, ls.fee_charges_waived_derived as feeChargesWaived, ls.fee_charges_writtenoff_derived as feeChargesWrittenOff, "
|
||||
+ " ls.penalty_charges_amount as penaltyChargesDue, ls.penalty_charges_completed_derived as penaltyChargesPaid, ls.penalty_charges_waived_derived as penaltyChargesWaived, ls.penalty_charges_writtenoff_derived as penaltyChargesWrittenOff, "
|
||||
+ " ls.total_paid_in_advance_derived as totalPaidInAdvanceForPeriod, ls.total_paid_late_derived as totalPaidLateForPeriod, "
|
||||
+ " mc.amount,mc.id as chargeId " + " from m_loan_repayment_schedule ls "
|
||||
+ " mc.amount,mc.id as chargeId "
|
||||
+ " from m_loan_repayment_schedule ls "
|
||||
+ " inner join m_loan ml on ml.id = ls.loan_id "
|
||||
+ " join m_product_loan_charge plc on plc.product_loan_id = ml.product_id "
|
||||
+ " join m_charge mc on mc.id = plc.charge_id ";
|
||||
@@ -1032,8 +1064,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
this.outstandingLoanPrincipalBalance = this.outstandingLoanPrincipalBalance.add(data.amount());
|
||||
} else if (data.isDueForDisbursement(fromDate, dueDate)) {
|
||||
if (!excludePastUndisbursed || (excludePastUndisbursed
|
||||
&& (data.isDisbursed() || !data.disbursementDate().isBefore(LocalDate.now())))) {
|
||||
if (!excludePastUndisbursed
|
||||
|| (excludePastUndisbursed && (data.isDisbursed() || !data.disbursementDate().isBefore(LocalDate.now())))) {
|
||||
principal = principal.add(data.amount());
|
||||
if (data.getChargeAmount() == null) {
|
||||
final LoanSchedulePeriodData periodData = LoanSchedulePeriodData.disbursementOnlyPeriod(
|
||||
@@ -1093,12 +1125,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
"totalPaidInAdvanceForPeriod");
|
||||
final BigDecimal totalPaidLateForPeriod = JdbcSupport.getBigDecimalDefaultToZeroIfNull(rs, "totalPaidLateForPeriod");
|
||||
|
||||
final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived)
|
||||
.subtract(penaltyChargesWrittenOff);
|
||||
final BigDecimal penaltyChargesActualDue = penaltyChargesExpectedDue.subtract(penaltyChargesWaived).subtract(
|
||||
penaltyChargesWrittenOff);
|
||||
final BigDecimal penaltyChargesOutstanding = penaltyChargesActualDue.subtract(penaltyChargesPaid);
|
||||
|
||||
final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue)
|
||||
.add(penaltyChargesExpectedDue);
|
||||
final BigDecimal totalExpectedCostOfLoanForPeriod = interestExpectedDue.add(feeChargesExpectedDue).add(
|
||||
penaltyChargesExpectedDue);
|
||||
|
||||
final BigDecimal totalDueForPeriod = principalDue.add(totalExpectedCostOfLoanForPeriod);
|
||||
final BigDecimal totalPaidForPeriod = principalPaid.add(interestPaid).add(feeChargesPaid).add(penaltyChargesPaid);
|
||||
@@ -1139,11 +1171,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
periods.add(periodData);
|
||||
}
|
||||
|
||||
return new LoanScheduleData(this.currency, periods, loanTermInDays, totalPrincipalDisbursed, totalPrincipalExpected.getAmount(),
|
||||
totalPrincipalPaid.getAmount(), totalInterestCharged.getAmount(), totalFeeChargesCharged.getAmount(),
|
||||
totalPenaltyChargesCharged.getAmount(), totalWaived.getAmount(), totalWrittenOff.getAmount(),
|
||||
totalRepaymentExpected.getAmount(), totalRepayment.getAmount(), totalPaidInAdvance.getAmount(),
|
||||
totalPaidLate.getAmount(), totalOutstanding.getAmount());
|
||||
return new LoanScheduleData(this.currency, periods, loanTermInDays, totalPrincipalDisbursed,
|
||||
totalPrincipalExpected.getAmount(), totalPrincipalPaid.getAmount(), totalInterestCharged.getAmount(),
|
||||
totalFeeChargesCharged.getAmount(), totalPenaltyChargesCharged.getAmount(), totalWaived.getAmount(),
|
||||
totalWrittenOff.getAmount(), totalRepaymentExpected.getAmount(), totalRepayment.getAmount(),
|
||||
totalPaidInAdvance.getAmount(), totalPaidLate.getAmount(), totalOutstanding.getAmount());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1156,7 +1188,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
|
||||
+ " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, "
|
||||
+ " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, "
|
||||
+ " tr.unrecognized_income_portion as unrecognizedIncome," + " tr.submitted_on_date as submittedOnDate, "
|
||||
+ " tr.unrecognized_income_portion as unrecognizedIncome,"
|
||||
+ " tr.submitted_on_date as submittedOnDate, "
|
||||
+ " tr.manually_adjusted_or_reversed as manuallyReversed, "
|
||||
+ " pd.payment_type_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "
|
||||
+ " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, "
|
||||
@@ -1260,7 +1293,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
+ " fromtran.description as fromTransferDescription,"
|
||||
+ " totran.id as toTransferId, totran.is_reversed as toTransferReversed,"
|
||||
+ " totran.transaction_date as toTransferDate, totran.amount as toTransferAmount,"
|
||||
+ " totran.description as toTransferDescription " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id"
|
||||
+ " totran.description as toTransferDescription "
|
||||
+ " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id"
|
||||
+ " join m_currency rc on rc.`code` = l.currency_code "
|
||||
+ " left join m_account_transfer_transaction fromtran on fromtran.from_loan_transaction_id = tr.id "
|
||||
+ " left join m_account_transfer_transaction totran on totran.to_loan_transaction_id = tr.id ";
|
||||
@@ -1320,7 +1354,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
.retrieveInterestRateFrequencyTypeOptions();
|
||||
final Collection<EnumOptionData> amortizationTypeOptions = this.loanDropdownReadPlatformService
|
||||
.retrieveLoanAmortizationTypeOptions();
|
||||
final Collection<EnumOptionData> interestTypeOptions = this.loanDropdownReadPlatformService.retrieveLoanInterestTypeOptions();
|
||||
Collection<EnumOptionData> interestTypeOptions = null;
|
||||
if (loanProduct.isLinkedToFloatingInterestRates()) {
|
||||
interestTypeOptions = Arrays.asList(interestType(InterestMethod.DECLINING_BALANCE));
|
||||
} else {
|
||||
interestTypeOptions = this.loanDropdownReadPlatformService.retrieveLoanInterestTypeOptions();
|
||||
}
|
||||
final Collection<EnumOptionData> interestCalculationPeriodTypeOptions = this.loanDropdownReadPlatformService
|
||||
.retrieveLoanInterestRateCalculatedInPeriodOptions();
|
||||
final Collection<FundData> fundOptions = this.fundReadPlatformService.retrieveAllFunds();
|
||||
@@ -1334,8 +1373,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanProductApplicableCharges(productId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT });
|
||||
} else {
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanProductApplicableCharges(productId,
|
||||
new ChargeTimeType[] { ChargeTimeType.OVERDUE_INSTALLMENT, ChargeTimeType.TRANCHE_DISBURSEMENT });
|
||||
chargeOptions = this.chargeReadPlatformService.retrieveLoanProductApplicableCharges(productId, new ChargeTimeType[] {
|
||||
ChargeTimeType.OVERDUE_INSTALLMENT, ChargeTimeType.TRANCHE_DISBURSEMENT });
|
||||
}
|
||||
|
||||
Integer loanCycleCounter = null;
|
||||
@@ -1395,8 +1434,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
@Override
|
||||
public Collection<CalendarData> retrieveCalendars(final Long groupId) {
|
||||
Collection<CalendarData> calendarsData = new ArrayList<>();
|
||||
calendarsData.addAll(
|
||||
this.calendarReadPlatformService.retrieveParentCalendarsByEntity(groupId, CalendarEntityType.GROUPS.getValue(), null));
|
||||
calendarsData.addAll(this.calendarReadPlatformService.retrieveParentCalendarsByEntity(groupId,
|
||||
CalendarEntityType.GROUPS.getValue(), null));
|
||||
calendarsData
|
||||
.addAll(this.calendarReadPlatformService.retrieveCalendarsByEntity(groupId, CalendarEntityType.GROUPS.getValue(), null));
|
||||
calendarsData = this.calendarReadPlatformService.updateWithRecurringDates(calendarsData);
|
||||
@@ -1518,13 +1557,15 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
LoanScheduleAccrualMapper mapper = new LoanScheduleAccrualMapper();
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("select ").append(mapper.schema())
|
||||
sqlBuilder
|
||||
.append("select ")
|
||||
.append(mapper.schema())
|
||||
.append(" where ((ls.fee_charges_amount <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))")
|
||||
.append(" or ( ls.penalty_charges_amount <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))")
|
||||
.append(" or ( ls.interest_amount <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))")
|
||||
.append(" and loan.loan_status_id=? and mpl.accounting_type=? and loan.is_npa=0 and ls.duedate <= CURDATE() order by loan.id,ls.duedate");
|
||||
return this.jdbcTemplate.query(sqlBuilder.toString(), mapper,
|
||||
new Object[] { LoanStatus.ACTIVE.getValue(), AccountingRuleType.ACCRUAL_PERIODIC.getValue() });
|
||||
return this.jdbcTemplate.query(sqlBuilder.toString(), mapper, new Object[] { LoanStatus.ACTIVE.getValue(),
|
||||
AccountingRuleType.ACCRUAL_PERIODIC.getValue() });
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1532,7 +1573,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
LoanSchedulePeriodicAccrualMapper mapper = new LoanSchedulePeriodicAccrualMapper();
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("select ").append(mapper.schema())
|
||||
sqlBuilder
|
||||
.append("select ")
|
||||
.append(mapper.schema())
|
||||
.append(" where ((ls.fee_charges_amount <> if(ls.accrual_fee_charges_derived is null,0, ls.accrual_fee_charges_derived))")
|
||||
.append(" or (ls.penalty_charges_amount <> if(ls.accrual_penalty_charges_derived is null,0,ls.accrual_penalty_charges_derived))")
|
||||
.append(" or (ls.interest_amount <> if(ls.accrual_interest_derived is null,0,ls.accrual_interest_derived)))")
|
||||
@@ -1549,13 +1592,16 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
public String schema() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,")
|
||||
sqlBuilder
|
||||
.append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,")
|
||||
.append("loan.accrued_till as accruedTill, loan.repayment_period_frequency_enum as frequencyEnum, ")
|
||||
.append("loan.interest_calculated_from_date as interestCalculatedFrom, ").append("loan.repay_every as repayEvery,")
|
||||
.append("loan.interest_calculated_from_date as interestCalculatedFrom, ")
|
||||
.append("loan.repay_every as repayEvery,")
|
||||
.append("ls.installment as installmentNumber, ")
|
||||
.append("ls.duedate as duedate,ls.fromdate as fromdate ,ls.id as scheduleId,loan.product_id as productId,")
|
||||
.append("ls.interest_amount as interest, ls.interest_waived_derived as interestWaived,")
|
||||
.append("ls.penalty_charges_amount as penalty, ").append("ls.fee_charges_amount as charges, ")
|
||||
.append("ls.penalty_charges_amount as penalty, ")
|
||||
.append("ls.fee_charges_amount as charges, ")
|
||||
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
|
||||
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
|
||||
.append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode")
|
||||
@@ -1610,11 +1656,13 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
public String schema() {
|
||||
final StringBuilder sqlBuilder = new StringBuilder(400);
|
||||
sqlBuilder.append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,")
|
||||
sqlBuilder
|
||||
.append("loan.id as loanId ,if(loan.client_id is null,mg.office_id,mc.office_id) as officeId,")
|
||||
.append("ls.duedate as duedate,ls.fromdate as fromdate,ls.id as scheduleId,loan.product_id as productId,")
|
||||
.append("ls.installment as installmentNumber, ")
|
||||
.append("ls.interest_amount as interest, ls.interest_waived_derived as interestWaived,")
|
||||
.append("ls.penalty_charges_amount as penalty, ").append("ls.fee_charges_amount as charges, ")
|
||||
.append("ls.penalty_charges_amount as penalty, ")
|
||||
.append("ls.fee_charges_amount as charges, ")
|
||||
.append("ls.accrual_interest_derived as accinterest,ls.accrual_fee_charges_derived as accfeecharege,ls.accrual_penalty_charges_derived as accpenalty,")
|
||||
.append(" loan.currency_code as currencyCode,loan.currency_digits as currencyDigits,loan.currency_multiplesof as inMultiplesOf,")
|
||||
.append("curr.display_symbol as currencyDisplaySymbol,curr.name as currencyName,curr.internationalized_name_code as currencyNameCode")
|
||||
@@ -1687,24 +1735,42 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Long> fetchArrearLoans() {
|
||||
public Collection<Long> fetchLoansForInterestRecalculation() {
|
||||
StringBuilder sqlBuilder = new StringBuilder();
|
||||
sqlBuilder.append("SELECT ml.id FROM m_loan ml ");
|
||||
sqlBuilder.append(" INNER JOIN m_loan_repayment_schedule mr on mr.loan_id = ml.id ");
|
||||
sqlBuilder.append(" LEFT JOIN m_loan_disbursement_detail dd on dd.loan_id=ml.id and dd.disbursedon_date is null ");
|
||||
// For Floating rate changes
|
||||
sqlBuilder
|
||||
.append(" left join m_product_loan_floating_rates pfr on ml.product_id = pfr.loan_product_id and ml.is_floating_interest_rate = 1");
|
||||
sqlBuilder.append(" left join m_floating_rates fr on pfr.floating_rates_id = fr.id");
|
||||
sqlBuilder.append(" left join m_floating_rates_periods frp on fr.id = frp.floating_rates_id ");
|
||||
sqlBuilder.append(" left join m_loan_reschedule_request lrr on lrr.loan_id = ml.id");
|
||||
// this is to identify the applicable rates when base rate is changed
|
||||
sqlBuilder.append(" left join m_floating_rates bfr on bfr.is_base_lending_rate = 1");
|
||||
sqlBuilder.append(" left join m_floating_rates_periods bfrp on bfr.id = bfrp.floating_rates_id and bfrp.created_date >= ?");
|
||||
sqlBuilder.append(" WHERE ml.loan_status_id = ? ");
|
||||
sqlBuilder.append(" and ml.interest_recalculation_enabled = 1 ");
|
||||
sqlBuilder.append(" and ml.is_npa = 0 ");
|
||||
sqlBuilder.append(" and ((");
|
||||
sqlBuilder.append("ml.interest_recalculation_enabled = 1 ");
|
||||
sqlBuilder.append(" and (ml.interest_recalcualated_on is null or ml.interest_recalcualated_on <> ?)");
|
||||
sqlBuilder.append(" and ((");
|
||||
sqlBuilder.append(" mr.completed_derived is false ");
|
||||
sqlBuilder.append(" and mr.duedate < ? )");
|
||||
sqlBuilder.append(" or dd.expected_disburse_date < ? ) ");
|
||||
sqlBuilder.append(" or dd.expected_disburse_date < ? )) ");
|
||||
sqlBuilder.append(" or (");
|
||||
sqlBuilder.append(" fr.is_active = 1 and frp.is_active = 1");
|
||||
sqlBuilder.append(" and (frp.created_date >= ? or ");
|
||||
sqlBuilder.append("(bfrp.id is not null and frp.is_differential_to_base_lending_rate = 1 and frp.from_date >= bfrp.from_date)) ");
|
||||
sqlBuilder.append("and lrr.loan_id is null");
|
||||
sqlBuilder.append(" ))");
|
||||
sqlBuilder.append(" group by ml.id");
|
||||
try {
|
||||
String currentdate = formatter.print(DateUtils.getLocalDateOfTenant());
|
||||
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class,
|
||||
new Object[] { LoanStatus.ACTIVE.getValue(), currentdate, currentdate, currentdate });
|
||||
// will look only for yesterday modified rates
|
||||
String yesterday = formatter.print(DateUtils.getLocalDateOfTenant().minusDays(1));
|
||||
return this.jdbcTemplate.queryForList(sqlBuilder.toString(), Long.class, new Object[] { yesterday,
|
||||
LoanStatus.ACTIVE.getValue(), currentdate, currentdate, currentdate, yesterday });
|
||||
} catch (final EmptyResultDataAccessException e) {
|
||||
return null;
|
||||
}
|
||||
@@ -1835,10 +1901,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
|
||||
return LoanSchedulePeriodData.repaymentPeriodWithPayments(loanId, period, fromDate, dueDate, obligationsMetOnDate, complete,
|
||||
principalOriginalDue, principalPaid, principalWrittenOff, principalOutstanding, outstandingPrincipalBalanceOfLoan,
|
||||
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding, feeChargesDue,
|
||||
feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesDue, penaltyChargesPaid,
|
||||
penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod, totalPaid,
|
||||
totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, totalWaived, totalWrittenOff, totalOutstanding,
|
||||
interestDueOnPrincipalOutstanding, interestPaid, interestWaived, interestWrittenOff, interestOutstanding,
|
||||
feeChargesDue, feeChargesPaid, feeChargesWaived, feeChargesWrittenOff, feeChargesOutstanding, penaltyChargesDue,
|
||||
penaltyChargesPaid, penaltyChargesWaived, penaltyChargesWrittenOff, penaltyChargesOutstanding, totalDueForPeriod,
|
||||
totalPaid, totalPaidInAdvanceForPeriod, totalPaidLateForPeriod, totalWaived, totalWrittenOff, totalOutstanding,
|
||||
totalActualCostOfLoanForPeriod);
|
||||
}
|
||||
}
|
||||
@@ -1847,8 +1913,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
public Date retrieveMinimumDateOfRepaymentTransaction(Long loanId) {
|
||||
// TODO Auto-generated method stub
|
||||
Date date = this.jdbcTemplate.queryForObject(
|
||||
"select min(transaction_date) from m_loan_transaction where loan_id=? and transaction_type_enum=2", new Object[] { loanId },
|
||||
Date.class);
|
||||
"select min(transaction_date) from m_loan_transaction where loan_id=? and transaction_type_enum=2",
|
||||
new Object[] { loanId }, Date.class);
|
||||
|
||||
return date;
|
||||
}
|
||||
@@ -1894,8 +1960,66 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
|
||||
final Collection<PaymentTypeData> paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
|
||||
|
||||
return new LoanTransactionData(null, null, null, transactionType, null, currencyData, earliestUnpaidInstallmentDate,
|
||||
retrieveTotalPaidInAdvance(loan.getId()).getPaidInAdvance(), null, null, null, null, null, null, paymentOptions, null, null,
|
||||
null, null, false);
|
||||
retrieveTotalPaidInAdvance(loan.getId()).getPaidInAdvance(), null, null, null, null, null, null, paymentOptions, null,
|
||||
null, null, null, false);
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<InterestRatePeriodData> retrieveLoanInterestRatePeriodData(Long loanId) {
|
||||
this.context.authenticatedUser();
|
||||
|
||||
final Loan loan = this.loanRepository.findOne(loanId);
|
||||
if (loan == null) { throw new LoanNotFoundException(loanId); }
|
||||
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
final Collection<InterestRatePeriodData> intRatePeriodData = new ArrayList<>();
|
||||
final Collection<InterestRatePeriodData> intRates = this.floatingRatesReadPlatformService.retrieveInterestRatePeriods(loan
|
||||
.loanProduct().getFloatingRates().getFloatingRate().getId());
|
||||
for (final InterestRatePeriodData rate : intRates) {
|
||||
if (rate.getFromDate().compareTo(loan.getSubmittedOnDate().toDate()) > 0 && loan.getIsFloatingInterestRate()) {
|
||||
updateInterestRatePeriodData(rate, loan);
|
||||
intRatePeriodData.add(rate);
|
||||
} else if (rate.getFromDate().compareTo(loan.getSubmittedOnDate().toDate()) <= 0) {
|
||||
updateInterestRatePeriodData(rate, loan);
|
||||
intRatePeriodData.add(rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return intRatePeriodData;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void updateInterestRatePeriodData(InterestRatePeriodData rate, Loan loan) {
|
||||
rate.setLoanProductDifferentialInterestRate(loan.loanProduct().getFloatingRates().getInterestRateDifferential());
|
||||
rate.setLoanDifferentialInterestRate(loan.getInterestRateDifferential());
|
||||
|
||||
BigDecimal effectiveInterestRate = BigDecimal.ZERO;
|
||||
effectiveInterestRate = effectiveInterestRate.add(rate.getLoanDifferentialInterestRate());
|
||||
effectiveInterestRate = effectiveInterestRate.add(rate.getLoanProductDifferentialInterestRate());
|
||||
effectiveInterestRate = effectiveInterestRate.add(rate.getInterestRate());
|
||||
if (rate.getBlrInterestRate() != null && rate.isDifferentialToBLR()) {
|
||||
effectiveInterestRate = effectiveInterestRate.add(rate.getBlrInterestRate());
|
||||
}
|
||||
rate.setEffectiveInterestRate(effectiveInterestRate);
|
||||
|
||||
if (rate.getFromDate().compareTo(loan.getSubmittedOnDate().toDate()) < 0) {
|
||||
rate.setFromDate(loan.getSubmittedOnDate().toDate());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+7
-3
@@ -18,6 +18,7 @@ import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidation
|
||||
import org.mifosplatform.infrastructure.jobs.annotation.CronTarget;
|
||||
import org.mifosplatform.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.mifosplatform.infrastructure.jobs.service.JobName;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -31,13 +32,16 @@ public class LoanSchedularServiceImpl implements LoanSchedularService {
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final LoanReadPlatformService loanReadPlatformService;
|
||||
private final LoanWritePlatformService loanWritePlatformService;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanSchedularServiceImpl(final ConfigurationDomainService configurationDomainService,
|
||||
final LoanReadPlatformService loanReadPlatformService, final LoanWritePlatformService loanWritePlatformService) {
|
||||
final LoanReadPlatformService loanReadPlatformService, final LoanWritePlatformService loanWritePlatformService,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
this.loanReadPlatformService = loanReadPlatformService;
|
||||
this.loanWritePlatformService = loanWritePlatformService;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,11 +96,11 @@ public class LoanSchedularServiceImpl implements LoanSchedularService {
|
||||
if (sb.length() > 0) { throw new JobExecutionException(sb.toString()); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@CronTarget(jobName = JobName.RECALCULATE_INTEREST_FOR_LOAN)
|
||||
public void recalculateInterest() {
|
||||
Collection<Long> loanIds = this.loanReadPlatformService.fetchArrearLoans();
|
||||
Collection<Long> loanIds = this.loanReadPlatformService.fetchLoansForInterestRecalculation();
|
||||
for (Long loanId : loanIds) {
|
||||
this.loanWritePlatformService.recalculateInterest(loanId);
|
||||
}
|
||||
|
||||
+76
-37
@@ -97,6 +97,9 @@ import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUS
|
||||
import org.mifosplatform.portfolio.common.BusinessEventNotificationConstants.BUSINESS_EVENTS;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.service.BusinessEventNotifierService;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.group.domain.Group;
|
||||
import org.mifosplatform.portfolio.group.exception.GroupNotActiveException;
|
||||
import org.mifosplatform.portfolio.loanaccount.api.LoanApiConstants;
|
||||
@@ -206,6 +209,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final AccountTransferDetailRepository accountTransferDetailRepository;
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final GuarantorDomainService guarantorDomainService;
|
||||
private final FloatingRatesReadPlatformService floatingRatesReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -231,7 +235,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final LoanApplicationCommandFromApiJsonHelper loanApplicationCommandFromApiJsonHelper,
|
||||
final AccountAssociationsRepository accountAssociationRepository,
|
||||
final AccountTransferDetailRepository accountTransferDetailRepository,
|
||||
final BusinessEventNotifierService businessEventNotifierService, final GuarantorDomainService guarantorDomainService) {
|
||||
final BusinessEventNotifierService businessEventNotifierService, final GuarantorDomainService guarantorDomainService,
|
||||
final FloatingRatesReadPlatformService floatingRatesReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanEventApiJsonValidator = loanEventApiJsonValidator;
|
||||
this.loanAssembler = loanAssembler;
|
||||
@@ -266,6 +271,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
this.accountTransferDetailRepository = accountTransferDetailRepository;
|
||||
this.businessEventNotifierService = businessEventNotifierService;
|
||||
this.guarantorDomainService = guarantorDomainService;
|
||||
this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
|
||||
}
|
||||
|
||||
private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() {
|
||||
@@ -354,9 +360,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
recalculateFrom, overdurPenaltyWaitPeriod);
|
||||
recalculateFrom, overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
@@ -513,7 +521,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
for (LoanRepaymentScheduleInstallment installment : installments) {
|
||||
if (installment.getId() == null) {
|
||||
this.repaymentScheduleInstallmentRepository.save(installment);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.loanRepository.save(loan);
|
||||
} catch (final DataIntegrityViolationException e) {
|
||||
@@ -608,9 +616,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays,
|
||||
allowTransactionsOnHoliday, allowTransactionsOnNonWorkingDay);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
final ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
firstRepaymentOnDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO);
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(
|
||||
@@ -707,9 +717,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(this.loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance);
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, floatingRateDTO);
|
||||
|
||||
final Map<String, Object> changes = loan.undoDisbursal(scheduleGeneratorDTO, existingTransactionIds,
|
||||
existingReversedTransactionIds, currentUser);
|
||||
@@ -893,11 +904,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
.getTransactionDate();
|
||||
}
|
||||
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays, allowTransactionsOnHoliday,
|
||||
allowTransactionsOnNonWorkingDay);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.adjustExistingTransaction(newTransactionDetail,
|
||||
defaultLoanLifecycleStateMachine(), transactionToAdjust, existingTransactionIds, existingReversedTransactionIds,
|
||||
@@ -1042,9 +1055,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.waiveInterest(waiveInterestTransaction,
|
||||
defaultLoanLifecycleStateMachine(), existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO,
|
||||
currentUser);
|
||||
@@ -1137,9 +1152,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
recalculateFrom = command.localDateValueOfParameterNamed("transactionDate");
|
||||
}
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
final ChangedTransactionDetail changedTransactionDetail = loan.closeAsWrittenOff(command, defaultLoanLifecycleStateMachine(),
|
||||
changes, existingTransactionIds, existingReversedTransactionIds, currentUser, scheduleGeneratorDTO);
|
||||
@@ -1221,9 +1238,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(this.loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.close(command, defaultLoanLifecycleStateMachine(), changes,
|
||||
existingTransactionIds, existingReversedTransactionIds, scheduleGeneratorDTO, currentUser);
|
||||
final LoanTransaction possibleClosingTransaction = changedTransactionDetail.getNewTransactionMappings().remove(0L);
|
||||
@@ -1357,7 +1376,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
|
||||
boolean isAppliedOnBackDate = false;
|
||||
LoanCharge loanCharge = null;
|
||||
LocalDate recalculateFrom =loan.fetchInterestRecalculateFromDate();
|
||||
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
|
||||
if (chargeDefinition.isPercentageOfDisbursementAmount()) {
|
||||
LoanTrancheDisbursementCharge loanTrancheDisbursementCharge = null;
|
||||
for (LoanDisbursementDetails disbursementDetail : loanDisburseDetails) {
|
||||
@@ -1478,9 +1497,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
final Long overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO generatorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
recalculateFrom, overdurPenaltyWaitPeriod);
|
||||
recalculateFrom, overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.handleRegenerateRepaymentScheduleWithInterestRecalculation(
|
||||
generatorDTO, currentUser);
|
||||
saveLoanWithDataIntegrityViolationChecks(loan);
|
||||
@@ -1521,9 +1542,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
}
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance);
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance,
|
||||
floatingRateDTO);
|
||||
createLoanScheduleArchive(loan, scheduleGeneratorDTO);
|
||||
}
|
||||
|
||||
@@ -1678,9 +1701,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
Money accruedCharge = Money.zero(loan.getCurrency());
|
||||
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
|
||||
@@ -2440,7 +2465,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
if (loan != null) {
|
||||
boolean reprocessRequired = true;
|
||||
LocalDate recalculatedTill = loan.fetchInterestRecalculateFromDate();
|
||||
if(recalculateFrom.isAfter(recalculatedTill)){
|
||||
if (recalculateFrom.isAfter(recalculatedTill)) {
|
||||
recalculateFrom = recalculatedTill;
|
||||
}
|
||||
|
||||
@@ -2590,9 +2615,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
}
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.undoWrittenOff(existingTransactionIds, existingReversedTransactionIds,
|
||||
scheduleGeneratorDTO, currentUser);
|
||||
@@ -2717,10 +2744,12 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
}
|
||||
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(this.loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = null;
|
||||
AppUser currentUser = getAppUserIfPresent();
|
||||
@@ -2857,19 +2886,13 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@Transactional
|
||||
@Override
|
||||
public void recalculateInterest(final long loanId) {
|
||||
AppUser currentUser = getAppUserIfPresent();
|
||||
Loan loan = this.loanAssembler.assembleFrom(loanId);
|
||||
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
|
||||
AppUser currentUser = getAppUserIfPresent();
|
||||
this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_INTEREST_RECALCULATION,
|
||||
constructEntityMap(BUSINESS_ENTITY.LOAN, loan));
|
||||
CalendarInstance restCalendarInstance = null;
|
||||
CalendarInstance compoundingCalendarInstance = null;
|
||||
ApplicationCurrency applicationCurrency = null;
|
||||
LocalDate calculatedRepaymentsStartingFromDate = null;
|
||||
List<Holiday> holidays = null;
|
||||
boolean isHolidayEnabled = false;
|
||||
WorkingDays workingDays = null;
|
||||
LocalDate recalculateFrom = loan.fetchInterestRecalculateFromDate();
|
||||
Long overdurPenaltyWaitPeriod = null;
|
||||
final List<Long> existingTransactionIds = new ArrayList<>();
|
||||
final List<Long> existingReversedTransactionIds = new ArrayList<>();
|
||||
if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
|
||||
@@ -2877,23 +2900,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
CalendarEntityType.LOAN_RECALCULATION_REST_DETAIL.getValue());
|
||||
compoundingCalendarInstance = calendarInstanceRepository.findCalendarInstaneByEntityId(
|
||||
loan.loanInterestRecalculationDetailId(), CalendarEntityType.LOAN_RECALCULATION_COMPOUNDING_DETAIL.getValue());
|
||||
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
|
||||
isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate());
|
||||
workingDays = this.workingDaysRepository.findOne();
|
||||
overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
}
|
||||
|
||||
final MonetaryCurrency currency = loan.getCurrency();
|
||||
ApplicationCurrency applicationCurrency = this.applicationCurrencyRepository.findOneWithNotFoundDetection(currency);
|
||||
final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(),
|
||||
CalendarEntityType.LOANS.getValue());
|
||||
LocalDate calculatedRepaymentsStartingFromDate = this.loanAccountDomainService.getCalculatedRepaymentsStartingFromDate(
|
||||
loan.getDisbursementDate(), loan, calendarInstance);
|
||||
|
||||
boolean isHolidayEnabled = this.configurationDomainService.isRescheduleRepaymentsOnHolidaysEnabled();
|
||||
List<Holiday> holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate()
|
||||
.toDate());
|
||||
WorkingDays workingDays = this.workingDaysRepository.findOne();
|
||||
Long overdurPenaltyWaitPeriod = this.configurationDomainService.retrievePenaltyWaitPeriod();
|
||||
HolidayDetailDTO holidayDetailDTO = new HolidayDetailDTO(isHolidayEnabled, holidays, workingDays);
|
||||
FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan);
|
||||
|
||||
ScheduleGeneratorDTO generatorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency,
|
||||
calculatedRepaymentsStartingFromDate, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, recalculateFrom,
|
||||
overdurPenaltyWaitPeriod);
|
||||
overdurPenaltyWaitPeriod, floatingRateDTO);
|
||||
|
||||
ChangedTransactionDetail changedTransactionDetail = loan.recalculateScheduleFromLastTransaction(generatorDTO,
|
||||
existingTransactionIds, existingReversedTransactionIds, currentUser);
|
||||
@@ -3022,4 +3048,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
return map;
|
||||
}
|
||||
|
||||
private FloatingRateDTO constructFloatingRateDTO(final Loan loan) {
|
||||
FloatingRateDTO floatingRateDTO = null;
|
||||
if (loan.loanProduct().isLinkedToFloatingInterestRate()) {
|
||||
boolean isFloatingInterestRate = loan.getIsFloatingInterestRate();
|
||||
BigDecimal interestRateDiff = loan.getInterestRateDifferential();
|
||||
List<FloatingRatePeriodData> baseLendingRatePeriods = this.floatingRatesReadPlatformService.retrieveBaseLendingRate()
|
||||
.getRatePeriods();
|
||||
floatingRateDTO = new FloatingRateDTO(isFloatingInterestRate, loan.getDisbursementDate(), interestRateDiff,
|
||||
baseLendingRatePeriods);
|
||||
}
|
||||
return floatingRateDTO;
|
||||
}
|
||||
|
||||
}
|
||||
+11
-3
@@ -44,6 +44,8 @@ import org.mifosplatform.organisation.monetary.service.CurrencyReadPlatformServi
|
||||
import org.mifosplatform.portfolio.charge.data.ChargeData;
|
||||
import org.mifosplatform.portfolio.charge.service.ChargeReadPlatformService;
|
||||
import org.mifosplatform.portfolio.common.service.DropdownReadPlatformService;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateData;
|
||||
import org.mifosplatform.portfolio.floatingrates.service.FloatingRatesReadPlatformService;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.fund.service.FundReadPlatformService;
|
||||
import org.mifosplatform.portfolio.loanproduct.data.LoanProductData;
|
||||
@@ -73,7 +75,9 @@ public class LoanProductsApiResource {
|
||||
"paymentTypeOptions", "currencyOptions", "repaymentFrequencyTypeOptions", "interestRateFrequencyTypeOptions",
|
||||
"amortizationTypeOptions", "interestTypeOptions", "interestCalculationPeriodTypeOptions",
|
||||
"transactionProcessingStrategyOptions", "chargeOptions", "accountingOptions", "accountingRuleOptions",
|
||||
"accountingMappingOptions"));
|
||||
"accountingMappingOptions", "floatingRateOptions", "isLinkedToFloatingInterestRates", "floatingRatesId", "interestRateDifferential",
|
||||
"minDifferentialLendingRate", "defaultDifferentialLendingRate", "maxDifferentialLendingRate",
|
||||
"isFloatingInterestRateCalculationAllowed"));
|
||||
|
||||
private final Set<String> PRODUCT_MIX_DATA_PARAMETERS = new HashSet<>(Arrays.asList("productId", "productName", "restrictedProducts",
|
||||
"allowedProducts", "productOptions"));
|
||||
@@ -95,6 +99,7 @@ public class LoanProductsApiResource {
|
||||
private final ProductMixReadPlatformService productMixReadPlatformService;
|
||||
private final DropdownReadPlatformService commonDropdownReadPlatformService;
|
||||
private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
|
||||
private final FloatingRatesReadPlatformService floatingRateReadPlatformService;
|
||||
|
||||
@Autowired
|
||||
public LoanProductsApiResource(final PlatformSecurityContext context, final LoanProductReadPlatformService readPlatformService,
|
||||
@@ -108,7 +113,8 @@ public class LoanProductsApiResource {
|
||||
final DefaultToApiJsonSerializer<ProductMixData> productMixDataApiJsonSerializer,
|
||||
final ProductMixReadPlatformService productMixReadPlatformService,
|
||||
final DropdownReadPlatformService commonDropdownReadPlatformService,
|
||||
PaymentTypeReadPlatformService paymentTypeReadPlatformService) {
|
||||
PaymentTypeReadPlatformService paymentTypeReadPlatformService,
|
||||
final FloatingRatesReadPlatformService floatingRateReadPlatformService) {
|
||||
this.context = context;
|
||||
this.loanProductReadPlatformService = readPlatformService;
|
||||
this.chargeReadPlatformService = chargeReadPlatformService;
|
||||
@@ -124,6 +130,7 @@ public class LoanProductsApiResource {
|
||||
this.productMixReadPlatformService = productMixReadPlatformService;
|
||||
this.commonDropdownReadPlatformService = commonDropdownReadPlatformService;
|
||||
this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
|
||||
this.floatingRateReadPlatformService = floatingRateReadPlatformService;
|
||||
}
|
||||
|
||||
@POST
|
||||
@@ -279,13 +286,14 @@ public class LoanProductsApiResource {
|
||||
.retrieveInterestRecalculationFrequencyTypeOptions();
|
||||
final List<EnumOptionData> preCloseInterestCalculationStrategyOptions = dropdownReadPlatformService
|
||||
.retrivePreCloseInterestCalculationStrategyOptions();
|
||||
final List<FloatingRateData> floatingRateOptions = this.floatingRateReadPlatformService.retrieveLookupActive();
|
||||
|
||||
return new LoanProductData(productData, chargeOptions, penaltyOptions, paymentTypeOptions, currencyOptions,
|
||||
amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions, repaymentFrequencyTypeOptions,
|
||||
interestRateFrequencyTypeOptions, fundOptions, transactionProcessingStrategyOptions, accountOptions,
|
||||
accountingRuleTypeOptions, loanCycleValueConditionTypeOptions, daysInMonthTypeOptions, daysInYearTypeOptions,
|
||||
interestRecalculationCompoundingTypeOptions, rescheduleStrategyTypeOptions, interestRecalculationFrequencyTypeOptions,
|
||||
preCloseInterestCalculationStrategyOptions);
|
||||
preCloseInterestCalculationStrategyOptions, floatingRateOptions);
|
||||
}
|
||||
|
||||
}
|
||||
+90
-5
@@ -25,6 +25,7 @@ import org.mifosplatform.portfolio.common.domain.DaysInMonthType;
|
||||
import org.mifosplatform.portfolio.common.domain.DaysInYearType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.common.service.CommonEnumerations;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateData;
|
||||
import org.mifosplatform.portfolio.fund.data.FundData;
|
||||
import org.mifosplatform.portfolio.loanaccount.data.LoanInterestRecalculationData;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.AmortizationMethod;
|
||||
@@ -67,6 +68,15 @@ public class LoanProductData {
|
||||
private final BigDecimal maxInterestRatePerPeriod;
|
||||
private final EnumOptionData interestRateFrequencyType;
|
||||
private final BigDecimal annualInterestRate;
|
||||
|
||||
private final boolean isLinkedToFloatingInterestRates;
|
||||
private final Integer floatingRateId;
|
||||
private final String floatingRateName;
|
||||
private final BigDecimal interestRateDifferential;
|
||||
private final BigDecimal minDifferentialLendingRate;
|
||||
private final BigDecimal defaultDifferentialLendingRate;
|
||||
private final BigDecimal maxDifferentialLendingRate;
|
||||
private final boolean isFloatingInterestRateCalculationAllowed;
|
||||
|
||||
// settings
|
||||
private final EnumOptionData amortizationType;
|
||||
@@ -135,6 +145,8 @@ public class LoanProductData {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private final List<EnumOptionData> interestRecalculationFrequencyTypeOptions;
|
||||
@SuppressWarnings("unused")
|
||||
private final List<FloatingRateData> floatingRateOptions;
|
||||
|
||||
private final Boolean multiDisburseLoan;
|
||||
private final Integer maxTrancheCount;
|
||||
@@ -165,6 +177,14 @@ public class LoanProductData {
|
||||
final BigDecimal minInterestRatePerPeriod = null;
|
||||
final BigDecimal maxInterestRatePerPeriod = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final boolean isLinkedToFloatingInterestRates = false;
|
||||
final Integer floatingRateId = null;
|
||||
final String floatingRateName = null;
|
||||
final BigDecimal interestRateDifferential = null;
|
||||
final BigDecimal minDifferentialLendingRate = null;
|
||||
final BigDecimal defaultDifferentialLendingRate = null;
|
||||
final BigDecimal maxDifferentialLendingRate = null;
|
||||
final boolean isFloatingInterestRateCalculationAllowed = false;
|
||||
final EnumOptionData repaymentFrequencyType = null;
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final EnumOptionData amortizationType = null;
|
||||
@@ -217,7 +237,9 @@ public class LoanProductData {
|
||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
|
||||
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
|
||||
}
|
||||
|
||||
@@ -236,6 +258,14 @@ public class LoanProductData {
|
||||
final BigDecimal minInterestRatePerPeriod = null;
|
||||
final BigDecimal maxInterestRatePerPeriod = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final boolean isLinkedToFloatingInterestRates = false;
|
||||
final Integer floatingRateId = null;
|
||||
final String floatingRateName = null;
|
||||
final BigDecimal interestRateDifferential = null;
|
||||
final BigDecimal minDifferentialLendingRate = null;
|
||||
final BigDecimal defaultDifferentialLendingRate = null;
|
||||
final BigDecimal maxDifferentialLendingRate = null;
|
||||
final boolean isFloatingInterestRateCalculationAllowed = false;
|
||||
final EnumOptionData repaymentFrequencyType = null;
|
||||
final EnumOptionData interestRateFrequencyType = null;
|
||||
final EnumOptionData amortizationType = null;
|
||||
@@ -290,7 +320,9 @@ public class LoanProductData {
|
||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, productGuaranteeData,
|
||||
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes);
|
||||
installmentAmountInMultiplesOf, loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
|
||||
}
|
||||
|
||||
@@ -313,6 +345,14 @@ public class LoanProductData {
|
||||
final BigDecimal minInterestRatePerPeriod = null;
|
||||
final BigDecimal maxInterestRatePerPeriod = null;
|
||||
final BigDecimal annualInterestRate = null;
|
||||
final boolean isLinkedToFloatingInterestRates = false;
|
||||
final Integer floatingRateId = null;
|
||||
final String floatingRateName = null;
|
||||
final BigDecimal interestRateDifferential = null;
|
||||
final BigDecimal minDifferentialLendingRate = null;
|
||||
final BigDecimal defaultDifferentialLendingRate = null;
|
||||
final BigDecimal maxDifferentialLendingRate = null;
|
||||
final boolean isFloatingInterestRateCalculationAllowed = false;
|
||||
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(PeriodFrequencyType.MONTHS);
|
||||
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(PeriodFrequencyType.MONTHS);
|
||||
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(AmortizationMethod.EQUAL_INSTALLMENTS);
|
||||
@@ -371,7 +411,9 @@ public class LoanProductData {
|
||||
daysInYearType, isInterestRecalculationEnabled, interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment,
|
||||
holdGuaranteeFunds, productGuaranteeData, principalThresholdForLastInstallment,
|
||||
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount, installmentAmountInMultiplesOf,
|
||||
loanProductConfigurableAttributes);
|
||||
loanProductConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
|
||||
}
|
||||
|
||||
@@ -406,7 +448,10 @@ public class LoanProductData {
|
||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, boolean holdGuaranteeFunds,
|
||||
final LoanProductGuaranteeData loanProductGuaranteeData, final BigDecimal principalThresholdForLastInstallment,
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, boolean canDefineInstallmentAmount,
|
||||
Integer installmentAmountInMultiplesOf, LoanProductConfigurableAttributes allowAttributeOverrides) {
|
||||
Integer installmentAmountInMultiplesOf, LoanProductConfigurableAttributes allowAttributeOverrides,
|
||||
boolean isLinkedToFloatingInterestRates, Integer floatingRateId, String floatingRateName,
|
||||
BigDecimal interestRateDifferential, BigDecimal minDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||
BigDecimal maxDifferentialLendingRate, boolean isFloatingInterestRateCalculationAllowed) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.shortName = shortName;
|
||||
@@ -427,6 +472,14 @@ public class LoanProductData {
|
||||
this.minInterestRatePerPeriod = minInterestRatePerPeriod;
|
||||
this.maxInterestRatePerPeriod = maxInterestRatePerPeriod;
|
||||
this.annualInterestRate = annualInterestRate;
|
||||
this.isLinkedToFloatingInterestRates = isLinkedToFloatingInterestRates;
|
||||
this.floatingRateId = floatingRateId;
|
||||
this.floatingRateName = floatingRateName;
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
this.minDifferentialLendingRate = minDifferentialLendingRate;
|
||||
this.defaultDifferentialLendingRate = defaultDifferentialLendingRate;
|
||||
this.maxDifferentialLendingRate = maxDifferentialLendingRate;
|
||||
this.isFloatingInterestRateCalculationAllowed = isFloatingInterestRateCalculationAllowed;
|
||||
this.repaymentFrequencyType = repaymentFrequencyType;
|
||||
this.interestRateFrequencyType = interestRateFrequencyType;
|
||||
this.amortizationType = amortizationType;
|
||||
@@ -457,6 +510,7 @@ public class LoanProductData {
|
||||
this.interestCalculationPeriodTypeOptions = null;
|
||||
this.repaymentFrequencyTypeOptions = null;
|
||||
this.interestRateFrequencyTypeOptions = null;
|
||||
this.floatingRateOptions = null;
|
||||
|
||||
this.accountingMappingOptions = null;
|
||||
this.accountingRuleOptions = null;
|
||||
@@ -505,7 +559,7 @@ public class LoanProductData {
|
||||
final List<EnumOptionData> valueConditionTypeOptions, final List<EnumOptionData> daysInMonthTypeOptions,
|
||||
final List<EnumOptionData> daysInYearTypeOptions, final List<EnumOptionData> interestRecalculationCompoundingTypeOptions,
|
||||
final List<EnumOptionData> rescheduleStrategyTypeOptions, final List<EnumOptionData> interestRecalculationFrequencyTypeOptions,
|
||||
final List<EnumOptionData> preCloseInterestCalculationStrategyOptions) {
|
||||
final List<EnumOptionData> preCloseInterestCalculationStrategyOptions, final List<FloatingRateData> floatingRateOptions) {
|
||||
this.id = productData.id;
|
||||
this.name = productData.name;
|
||||
this.shortName = productData.shortName;
|
||||
@@ -525,6 +579,14 @@ public class LoanProductData {
|
||||
this.minInterestRatePerPeriod = productData.minInterestRatePerPeriod;
|
||||
this.maxInterestRatePerPeriod = productData.maxInterestRatePerPeriod;
|
||||
this.annualInterestRate = productData.annualInterestRate;
|
||||
this.isLinkedToFloatingInterestRates = productData.isLinkedToFloatingInterestRates;
|
||||
this.floatingRateId = productData.floatingRateId;
|
||||
this.floatingRateName = productData.floatingRateName;
|
||||
this.interestRateDifferential = productData.interestRateDifferential;
|
||||
this.minDifferentialLendingRate = productData.minDifferentialLendingRate;
|
||||
this.defaultDifferentialLendingRate = productData.defaultDifferentialLendingRate;
|
||||
this.maxDifferentialLendingRate = productData.maxDifferentialLendingRate;
|
||||
this.isFloatingInterestRateCalculationAllowed = productData.isFloatingInterestRateCalculationAllowed;
|
||||
this.repaymentFrequencyType = productData.repaymentFrequencyType;
|
||||
this.interestRateFrequencyType = productData.interestRateFrequencyType;
|
||||
this.amortizationType = productData.amortizationType;
|
||||
@@ -552,6 +614,7 @@ public class LoanProductData {
|
||||
this.currency = productData.currency;
|
||||
this.fundOptions = fundOptions;
|
||||
this.transactionProcessingStrategyOptions = transactionStrategyOptions;
|
||||
this.floatingRateOptions = floatingRateOptions;
|
||||
if (this.transactionProcessingStrategyOptions != null && this.transactionProcessingStrategyOptions.size() == 1) {
|
||||
final List<TransactionProcessingStrategyData> listOfOptions = new ArrayList<>(this.transactionProcessingStrategyOptions);
|
||||
|
||||
@@ -886,4 +949,26 @@ public class LoanProductData {
|
||||
public void setloanProductConfigurableAttributes(LoanProductConfigurableAttributes loanProductConfigurableAttributes) {
|
||||
this.allowAttributeOverrides = loanProductConfigurableAttributes;
|
||||
}
|
||||
|
||||
public boolean isLinkedToFloatingInterestRates() {
|
||||
return this.isLinkedToFloatingInterestRates;
|
||||
}
|
||||
|
||||
public BigDecimal getMinDifferentialLendingRate() {
|
||||
return this.minDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public BigDecimal getDefaultDifferentialLendingRate() {
|
||||
return this.defaultDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public BigDecimal getMaxDifferentialLendingRate() {
|
||||
return this.maxDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public boolean isFloatingInterestRateCalculationAllowed() {
|
||||
return this.isFloatingInterestRateCalculationAllowed;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+94
-13
@@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.loanproduct.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -43,6 +44,9 @@ import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.common.domain.DaysInMonthType;
|
||||
import org.mifosplatform.portfolio.common.domain.DaysInYearType;
|
||||
import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
import org.mifosplatform.portfolio.loanproduct.LoanProductConstants;
|
||||
@@ -153,8 +157,15 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
@Column(name = "instalment_amount_in_multiples_of", nullable = true)
|
||||
private Integer installmentAmountInMultiplesOf;
|
||||
|
||||
@Column(name = "is_linked_to_floating_interest_rates", nullable = false)
|
||||
private boolean isLinkedToFloatingInterestRate;
|
||||
|
||||
@LazyCollection(LazyCollectionOption.FALSE)
|
||||
@OneToOne(cascade = CascadeType.ALL, mappedBy = "loanProduct", optional = true, orphanRemoval = true)
|
||||
private LoanProductFloatingRates floatingRates;
|
||||
|
||||
public static LoanProduct assembleFromJson(final Fund fund, final LoanTransactionProcessingStrategy loanTransactionProcessingStrategy,
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
final List<Charge> productCharges, final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) {
|
||||
|
||||
final String name = command.stringValueOfParameterNamed("name");
|
||||
final String shortName = command.stringValueOfParameterNamed(LoanProductConstants.shortName);
|
||||
@@ -174,12 +185,31 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
final AmortizationMethod amortizationMethod = AmortizationMethod.fromInt(command.integerValueOfParameterNamed("amortizationType"));
|
||||
final PeriodFrequencyType repaymentFrequencyType = PeriodFrequencyType.fromInt(command
|
||||
.integerValueOfParameterNamed("repaymentFrequencyType"));
|
||||
final PeriodFrequencyType interestFrequencyType = PeriodFrequencyType.fromInt(command
|
||||
.integerValueOfParameterNamed("interestRateFrequencyType"));
|
||||
final BigDecimal interestRatePerPeriod = command.bigDecimalValueOfParameterNamed("interestRatePerPeriod");
|
||||
final BigDecimal minInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("minInterestRatePerPeriod");
|
||||
final BigDecimal maxInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("maxInterestRatePerPeriod");
|
||||
final BigDecimal annualInterestRate = aprCalculator.calculateFrom(interestFrequencyType, interestRatePerPeriod);
|
||||
PeriodFrequencyType interestFrequencyType = PeriodFrequencyType.INVALID;
|
||||
BigDecimal interestRatePerPeriod = null;
|
||||
BigDecimal minInterestRatePerPeriod = null;
|
||||
BigDecimal maxInterestRatePerPeriod = null;
|
||||
BigDecimal annualInterestRate = null;
|
||||
BigDecimal interestRateDifferential = null;
|
||||
BigDecimal minDifferentialLendingRate = null;
|
||||
BigDecimal maxDifferentialLendingRate = null;
|
||||
BigDecimal defaultDifferentialLendingRate = null;
|
||||
Boolean isFloatingInterestRateCalculationAllowed = null;
|
||||
final Boolean isLinkedToFloatingInterestRates = command.booleanObjectValueOfParameterNamed("isLinkedToFloatingInterestRates");
|
||||
if (isLinkedToFloatingInterestRates != null && isLinkedToFloatingInterestRates) {
|
||||
interestRateDifferential = command.bigDecimalValueOfParameterNamed("interestRateDifferential");
|
||||
minDifferentialLendingRate = command.bigDecimalValueOfParameterNamed("minDifferentialLendingRate");
|
||||
maxDifferentialLendingRate = command.bigDecimalValueOfParameterNamed("maxDifferentialLendingRate");
|
||||
defaultDifferentialLendingRate = command.bigDecimalValueOfParameterNamed("defaultDifferentialLendingRate");
|
||||
isFloatingInterestRateCalculationAllowed = command
|
||||
.booleanObjectValueOfParameterNamed("isFloatingInterestRateCalculationAllowed");
|
||||
} else {
|
||||
interestFrequencyType = PeriodFrequencyType.fromInt(command.integerValueOfParameterNamed("interestRateFrequencyType"));
|
||||
interestRatePerPeriod = command.bigDecimalValueOfParameterNamed("interestRatePerPeriod");
|
||||
minInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("minInterestRatePerPeriod");
|
||||
maxInterestRatePerPeriod = command.bigDecimalValueOfParameterNamed("maxInterestRatePerPeriod");
|
||||
annualInterestRate = aprCalculator.calculateFrom(interestFrequencyType, interestRatePerPeriod);
|
||||
}
|
||||
|
||||
final Integer repaymentEvery = command.integerValueOfParameterNamed("repaymentEvery");
|
||||
final Integer numberOfRepayments = command.integerValueOfParameterNamed("numberOfRepayments");
|
||||
@@ -271,7 +301,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
outstandingLoanBalance, graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType,
|
||||
isInterestRecalculationEnabled, interestRecalculationSettings, minimumDaysBetweenDisbursalAndFirstRepayment,
|
||||
holdGuarantorFunds, loanProductGuaranteeDetails, principalThresholdForLastInstallment,
|
||||
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineEmiAmount, installmentAmountInMultiplesOf, loanConfigurableAttributes);
|
||||
accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineEmiAmount, installmentAmountInMultiplesOf,
|
||||
loanConfigurableAttributes, isLinkedToFloatingInterestRates, floatingRate, interestRateDifferential,
|
||||
minDifferentialLendingRate, maxDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
isFloatingInterestRateCalculationAllowed);
|
||||
|
||||
}
|
||||
|
||||
@@ -498,7 +531,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final boolean holdGuarantorFunds,
|
||||
final LoanProductGuaranteeDetails loanProductGuaranteeDetails, final BigDecimal principalThresholdForLastInstallment,
|
||||
final boolean accountMovesOutOfNPAOnlyOnArrearsCompletion, final boolean canDefineEmiAmount,
|
||||
final Integer installmentAmountInMultiplesOf, final LoanProductConfigurableAttributes loanProductConfigurableAttributes) {
|
||||
final Integer installmentAmountInMultiplesOf, final LoanProductConfigurableAttributes loanProductConfigurableAttributes,
|
||||
Boolean isLinkedToFloatingInterestRates, FloatingRate floatingRate, BigDecimal interestRateDifferential,
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||
Boolean isFloatingInterestRateCalculationAllowed) {
|
||||
this.fund = fund;
|
||||
this.transactionProcessingStrategy = transactionProcessingStrategy;
|
||||
this.name = name.trim();
|
||||
@@ -513,6 +549,12 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
this.charges = charges;
|
||||
}
|
||||
|
||||
this.isLinkedToFloatingInterestRate = isLinkedToFloatingInterestRates == null ? false : isLinkedToFloatingInterestRates;
|
||||
if (isLinkedToFloatingInterestRate) {
|
||||
this.floatingRates = new LoanProductFloatingRates(floatingRate, this, interestRateDifferential, minDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, defaultDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
}
|
||||
|
||||
this.loanProductRelatedDetail = new LoanProductRelatedDetail(currency, defaultPrincipal, defaultNominalInterestRatePerPeriod,
|
||||
interestPeriodFrequencyType, defaultAnnualNominalInterestRate, interestMethod, interestCalculationPeriodMethod, repayEvery,
|
||||
repaymentFrequencyType, defaultNumberOfInstallments, graceOnPrincipalPayment, graceOnInterestPayment,
|
||||
@@ -603,9 +645,9 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
public Integer getAccountingType() {
|
||||
return this.accountingRule;
|
||||
}
|
||||
|
||||
public List<Charge> getLoanProductCharges(){
|
||||
return this.charges;
|
||||
|
||||
public List<Charge> getLoanProductCharges() {
|
||||
return this.charges;
|
||||
}
|
||||
|
||||
public void update(final LoanProductConfigurableAttributes loanConfigurableAttributes) {
|
||||
@@ -616,11 +658,26 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
return this.loanConfigurableAttributes;
|
||||
}
|
||||
|
||||
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator) {
|
||||
public Map<String, Object> update(final JsonCommand command, final AprCalculator aprCalculator, FloatingRate floatingRate) {
|
||||
|
||||
final Map<String, Object> actualChanges = this.loanProductRelatedDetail.update(command, aprCalculator);
|
||||
actualChanges.putAll(loanProductMinMaxConstraints().update(command));
|
||||
|
||||
final String isLinkedToFloatingInterestRates = "isLinkedToFloatingInterestRates";
|
||||
if (command.isChangeInBooleanParameterNamed(isLinkedToFloatingInterestRates, this.isLinkedToFloatingInterestRate)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isLinkedToFloatingInterestRates);
|
||||
actualChanges.put(isLinkedToFloatingInterestRates, newValue);
|
||||
this.isLinkedToFloatingInterestRate = newValue;
|
||||
}
|
||||
|
||||
if (this.isLinkedToFloatingInterestRate) {
|
||||
actualChanges.putAll(loanProductFloatingRates().update(command, floatingRate));
|
||||
this.loanProductRelatedDetail.updateForFloatingInterestRates();
|
||||
this.loanProductMinMaxConstraints.updateForFloatingInterestRates();
|
||||
}else{
|
||||
this.floatingRates = null;
|
||||
}
|
||||
|
||||
final String accountingTypeParamName = "accountingRule";
|
||||
if (command.isChangeInIntegerParameterNamed(accountingTypeParamName, this.accountingRule)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(accountingTypeParamName);
|
||||
@@ -907,6 +964,13 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
private LoanProductFloatingRates loanProductFloatingRates() {
|
||||
this.floatingRates = this.floatingRates == null
|
||||
? new LoanProductFloatingRates(null, this, null, null, null, null, false)
|
||||
: this.floatingRates;
|
||||
return this.floatingRates;
|
||||
}
|
||||
|
||||
public boolean isAccountingDisabled() {
|
||||
return AccountingRuleType.NONE.getValue().equals(this.accountingRule);
|
||||
}
|
||||
@@ -1187,4 +1251,21 @@ public class LoanProduct extends AbstractPersistable<Long> {
|
||||
public LoanProductRelatedDetail getLoanProductRelatedDetail() {
|
||||
return loanProductRelatedDetail;
|
||||
}
|
||||
|
||||
public boolean isLinkedToFloatingInterestRate() {
|
||||
return this.isLinkedToFloatingInterestRate;
|
||||
}
|
||||
|
||||
public LoanProductFloatingRates getFloatingRates() {
|
||||
return this.floatingRates;
|
||||
}
|
||||
|
||||
public Collection<FloatingRatePeriodData> fetchInterestRates(final FloatingRateDTO floatingRateDTO) {
|
||||
Collection<FloatingRatePeriodData> applicableRates = new ArrayList<>(1);
|
||||
if (isLinkedToFloatingInterestRate()) {
|
||||
applicableRates = getFloatingRates().fetchInterestRates(floatingRateDTO);
|
||||
}
|
||||
return applicableRates;
|
||||
}
|
||||
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/**
|
||||
* 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.portfolio.loanproduct.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRateDTO;
|
||||
import org.mifosplatform.portfolio.floatingrates.data.FloatingRatePeriodData;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_product_loan_floating_rates")
|
||||
public class LoanProductFloatingRates extends AbstractPersistable<Long> {
|
||||
|
||||
@OneToOne
|
||||
@JoinColumn(name = "loan_product_id", nullable = false)
|
||||
private LoanProduct loanProduct;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "floating_rates_id", nullable = false)
|
||||
private FloatingRate floatingRate;
|
||||
|
||||
@Column(name = "interest_rate_differential", nullable = false)
|
||||
private BigDecimal interestRateDifferential;
|
||||
|
||||
@Column(name = "min_differential_lending_rate", nullable = false)
|
||||
private BigDecimal minDifferentialLendingRate;
|
||||
|
||||
@Column(name = "default_differential_lending_rate", nullable = false)
|
||||
private BigDecimal defaultDifferentialLendingRate;
|
||||
|
||||
@Column(name = "max_differential_lending_rate", nullable = false)
|
||||
private BigDecimal maxDifferentialLendingRate;
|
||||
|
||||
@Column(name = "is_floating_interest_rate_calculation_allowed", nullable = false)
|
||||
private boolean isFloatingInterestRateCalculationAllowed;
|
||||
|
||||
public LoanProductFloatingRates(){
|
||||
|
||||
}
|
||||
public LoanProductFloatingRates(FloatingRate floatingRate, LoanProduct loanProduct, BigDecimal interestRateDifferential,
|
||||
BigDecimal minDifferentialLendingRate, BigDecimal maxDifferentialLendingRate, BigDecimal defaultDifferentialLendingRate,
|
||||
boolean isFloatingInterestRateCalculationAllowed){
|
||||
this.floatingRate = floatingRate;
|
||||
this.loanProduct = loanProduct;
|
||||
this.interestRateDifferential = interestRateDifferential;
|
||||
this.minDifferentialLendingRate = minDifferentialLendingRate;
|
||||
this.maxDifferentialLendingRate = maxDifferentialLendingRate;
|
||||
this.defaultDifferentialLendingRate = defaultDifferentialLendingRate;
|
||||
this.isFloatingInterestRateCalculationAllowed = isFloatingInterestRateCalculationAllowed;
|
||||
}
|
||||
|
||||
public LoanProduct getLoanProduct() {
|
||||
return this.loanProduct;
|
||||
}
|
||||
|
||||
public FloatingRate getFloatingRate() {
|
||||
return this.floatingRate;
|
||||
}
|
||||
|
||||
public BigDecimal getInterestRateDifferential() {
|
||||
return this.interestRateDifferential;
|
||||
}
|
||||
|
||||
public BigDecimal getMinDifferentialLendingRate() {
|
||||
return this.minDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public BigDecimal getDefaultDifferentialLendingRate() {
|
||||
return this.defaultDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public BigDecimal getMaxDifferentialLendingRate() {
|
||||
return this.maxDifferentialLendingRate;
|
||||
}
|
||||
|
||||
public boolean isFloatingInterestRateCalculationAllowed() {
|
||||
return this.isFloatingInterestRateCalculationAllowed;
|
||||
}
|
||||
|
||||
public Map<? extends String, ? extends Object> update(JsonCommand command, FloatingRate floatingRate) {
|
||||
final Map<String, Object> actualChanges = new LinkedHashMap<>(20);
|
||||
if (floatingRate != null) {
|
||||
final String floatingRatesId = "floatingRatesId";
|
||||
if (this.floatingRate == null || command.isChangeInLongParameterNamed(floatingRatesId, this.floatingRate.getId())) {
|
||||
final long newValue = command.longValueOfParameterNamed(floatingRatesId);
|
||||
actualChanges.put(floatingRatesId, newValue);
|
||||
this.floatingRate = floatingRate;
|
||||
}
|
||||
}
|
||||
|
||||
final String interestRateDifferential = "interestRateDifferential";
|
||||
if (command.isChangeInBigDecimalParameterNamed(interestRateDifferential, this.interestRateDifferential)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(interestRateDifferential);
|
||||
actualChanges.put(interestRateDifferential, newValue);
|
||||
this.interestRateDifferential = newValue;
|
||||
}
|
||||
final String minDifferentialLendingRate = "minDifferentialLendingRate";
|
||||
if (command.isChangeInBigDecimalParameterNamed(minDifferentialLendingRate, this.minDifferentialLendingRate)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(minDifferentialLendingRate);
|
||||
actualChanges.put(minDifferentialLendingRate, newValue);
|
||||
this.minDifferentialLendingRate = newValue;
|
||||
}
|
||||
final String defaultDifferentialLendingRate = "defaultDifferentialLendingRate";
|
||||
if (command.isChangeInBigDecimalParameterNamed(defaultDifferentialLendingRate, this.defaultDifferentialLendingRate)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(defaultDifferentialLendingRate);
|
||||
actualChanges.put(defaultDifferentialLendingRate, newValue);
|
||||
this.defaultDifferentialLendingRate = newValue;
|
||||
}
|
||||
final String maxDifferentialLendingRate = "maxDifferentialLendingRate";
|
||||
if (command.isChangeInBigDecimalParameterNamed(maxDifferentialLendingRate, this.maxDifferentialLendingRate)) {
|
||||
final BigDecimal newValue = command.bigDecimalValueOfParameterNamed(maxDifferentialLendingRate);
|
||||
actualChanges.put(maxDifferentialLendingRate, newValue);
|
||||
this.maxDifferentialLendingRate = newValue;
|
||||
}
|
||||
final String isFloatingInterestRateCalculationAllowed = "isFloatingInterestRateCalculationAllowed";
|
||||
if (command
|
||||
.isChangeInBooleanParameterNamed(isFloatingInterestRateCalculationAllowed, this.isFloatingInterestRateCalculationAllowed)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(isFloatingInterestRateCalculationAllowed);
|
||||
actualChanges.put(isFloatingInterestRateCalculationAllowed, newValue);
|
||||
this.isFloatingInterestRateCalculationAllowed = newValue;
|
||||
}
|
||||
|
||||
return actualChanges;
|
||||
}
|
||||
|
||||
public Collection<FloatingRatePeriodData> fetchInterestRates(final FloatingRateDTO floatingRateDTO) {
|
||||
floatingRateDTO.addInterestRateDiff(this.interestRateDifferential);
|
||||
return floatingRate.fetchInterestRates(floatingRateDTO);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+5
@@ -147,4 +147,9 @@ public class LoanProductMinMaxConstraints {
|
||||
return this.maxNumberOfRepayments;
|
||||
}
|
||||
|
||||
public void updateForFloatingInterestRates() {
|
||||
this.minNominalInterestRatePerPeriod = null;
|
||||
this.maxNominalInterestRatePerPeriod = null;
|
||||
}
|
||||
|
||||
}
|
||||
+18
-7
@@ -43,16 +43,16 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principal;
|
||||
|
||||
@Column(name = "nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = false)
|
||||
@Column(name = "nominal_interest_rate_per_period", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal nominalInterestRatePerPeriod;
|
||||
|
||||
// FIXME - move away form JPA ordinal use for enums using just integer -
|
||||
// requires sql patch for existing users of software.
|
||||
@Enumerated(EnumType.ORDINAL)
|
||||
@Column(name = "interest_period_frequency_enum", nullable = false)
|
||||
@Column(name = "interest_period_frequency_enum", nullable = true)
|
||||
private PeriodFrequencyType interestPeriodFrequencyType;
|
||||
|
||||
@Column(name = "annual_nominal_interest_rate", scale = 6, precision = 19, nullable = false)
|
||||
@Column(name = "annual_nominal_interest_rate", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal annualNominalInterestRate;
|
||||
|
||||
// FIXME - move away form JPA ordinal use for enums using just integer -
|
||||
@@ -205,17 +205,20 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
|
||||
@Override
|
||||
public BigDecimal getNominalInterestRatePerPeriod() {
|
||||
return BigDecimal.valueOf(Double.valueOf(this.nominalInterestRatePerPeriod.stripTrailingZeros().toString()));
|
||||
return this.nominalInterestRatePerPeriod == null? null
|
||||
: BigDecimal.valueOf(Double.valueOf(this.nominalInterestRatePerPeriod.stripTrailingZeros().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PeriodFrequencyType getInterestPeriodFrequencyType() {
|
||||
return this.interestPeriodFrequencyType;
|
||||
return this.interestPeriodFrequencyType == null? PeriodFrequencyType.INVALID
|
||||
: this.interestPeriodFrequencyType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getAnnualNominalInterestRate() {
|
||||
return BigDecimal.valueOf(Double.valueOf(this.annualNominalInterestRate.stripTrailingZeros().toString()));
|
||||
return this.annualNominalInterestRate == null? null
|
||||
:BigDecimal.valueOf(Double.valueOf(this.annualNominalInterestRate.stripTrailingZeros().toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -365,7 +368,9 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
}
|
||||
|
||||
final String interestRateFrequencyTypeParamName = "interestRateFrequencyType";
|
||||
if (command.isChangeInIntegerParameterNamed(interestRateFrequencyTypeParamName, this.interestPeriodFrequencyType.getValue())) {
|
||||
final int interestPeriodFrequencyType = this.interestPeriodFrequencyType == null?
|
||||
PeriodFrequencyType.INVALID.getValue() : this.interestPeriodFrequencyType.getValue();
|
||||
if (command.isChangeInIntegerParameterNamed(interestRateFrequencyTypeParamName, interestPeriodFrequencyType)) {
|
||||
final Integer newValue = command.integerValueOfParameterNamed(interestRateFrequencyTypeParamName);
|
||||
actualChanges.put(interestRateFrequencyTypeParamName, newValue);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
@@ -567,4 +572,10 @@ public class LoanProductRelatedDetail implements LoanProductMinimumRepaymentSche
|
||||
return this.inArrearsTolerance;
|
||||
}
|
||||
|
||||
public void updateForFloatingInterestRates() {
|
||||
this.nominalInterestRatePerPeriod = null;
|
||||
this.interestPeriodFrequencyType = PeriodFrequencyType.INVALID;
|
||||
this.annualNominalInterestRate = null;
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/**
|
||||
* 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.portfolio.loanproduct.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
public class LoanProductCannotBeModifiedDueToNonClosedLoansException extends AbstractPlatformDomainRuleException {
|
||||
public LoanProductCannotBeModifiedDueToNonClosedLoansException(final Long id) {
|
||||
super("error.msg.loanproduct.not.modifiable.due.to.non.closed.loans",
|
||||
"Loan product with identifier " + id + " cannot be modified due to non closed loans associated with it.", id);
|
||||
}
|
||||
}
|
||||
+340
-73
@@ -54,7 +54,9 @@ public final class LoanProductDataValidator {
|
||||
"minInterestRatePerPeriod", "maxInterestRatePerPeriod", "interestRateFrequencyType", "amortizationType", "interestType",
|
||||
"interestCalculationPeriodType", "inArrearsTolerance", "transactionProcessingStrategyId", "graceOnPrincipalPayment",
|
||||
"graceOnInterestPayment", "graceOnInterestCharged", "charges", "accountingRule", "includeInBorrowerCycle", "startDate",
|
||||
"closeDate", "externalId", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(),
|
||||
"closeDate", "externalId", "isLinkedToFloatingInterestRates", "floatingRatesId", "interestRateDifferential",
|
||||
"minDifferentialLendingRate", "defaultDifferentialLendingRate", "maxDifferentialLendingRate",
|
||||
"isFloatingInterestRateCalculationAllowed", LOAN_PRODUCT_ACCOUNTING_PARAMS.FEES_RECEIVABLE.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.FUND_SOURCE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_FEES.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INCOME_FROM_PENALTIES.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_ON_LOANS.getValue(),
|
||||
LOAN_PRODUCT_ACCOUNTING_PARAMS.INTEREST_RECEIVABLE.getValue(), LOAN_PRODUCT_ACCOUNTING_PARAMS.LOAN_PORTFOLIO.getValue(),
|
||||
@@ -214,48 +216,6 @@ public final class LoanProductDataValidator {
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("repaymentFrequencyType").value(repaymentFrequencyType).notNull().inMinMaxRange(0, 3);
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
|
||||
BigDecimal minInterestRatePerPeriod = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) {
|
||||
minInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
|
||||
BigDecimal maxInterestRatePerPeriod = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) {
|
||||
maxInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
if (maxInterestRatePerPeriod != null && maxInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minInterestRatePerPeriod != null && minInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod)
|
||||
.notLessThanMin(minInterestRatePerPeriod);
|
||||
if (minInterestRatePerPeriod.compareTo(maxInterestRatePerPeriod) <= 0) {
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod)
|
||||
.inMinAndMaxAmountRange(minInterestRatePerPeriod, maxInterestRatePerPeriod);
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod)
|
||||
.notGreaterThanMax(maxInterestRatePerPeriod);
|
||||
}
|
||||
} else if (minInterestRatePerPeriod != null && minInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod)
|
||||
.notLessThanMin(minInterestRatePerPeriod);
|
||||
}
|
||||
|
||||
final Integer interestRateFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
|
||||
|
||||
// settings
|
||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1);
|
||||
@@ -330,6 +290,168 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
}
|
||||
|
||||
//interest rates
|
||||
if(this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)
|
||||
&& this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true){
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("minInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("maxInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)){
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
if((interestType == null || interestType != InterestMethod.DECLINING_BALANCE.getValue())
|
||||
|| (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)){
|
||||
baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates").failWithCode("supported.only.for.declining.balance.interest.recalculation.enabled",
|
||||
"Floating interest rates are supported only for declining balance and interest recalculation enabled loan products");
|
||||
}
|
||||
|
||||
final Integer floatingRatesId = this.fromApiJsonHelper.extractIntegerNamed("floatingRatesId", element,
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("floatingRatesId").value(floatingRatesId).notNull();
|
||||
|
||||
final BigDecimal interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRateDifferential", element);
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String minDifferentialLendingRateParameterName = "minDifferentialLendingRate";
|
||||
BigDecimal minDifferentialLendingRate = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(minDifferentialLendingRateParameterName, element)) {
|
||||
minDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minDifferentialLendingRateParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(minDifferentialLendingRateParameterName).value(minDifferentialLendingRate).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String defaultDifferentialLendingRateParameterName = "defaultDifferentialLendingRate";
|
||||
BigDecimal defaultDifferentialLendingRate = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(defaultDifferentialLendingRateParameterName, element)) {
|
||||
defaultDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(defaultDifferentialLendingRateParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String maxDifferentialLendingRateParameterName = "maxDifferentialLendingRate";
|
||||
BigDecimal maxDifferentialLendingRate = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(maxDifferentialLendingRateParameterName, element)) {
|
||||
maxDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxDifferentialLendingRateParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(maxDifferentialLendingRateParameterName).value(maxDifferentialLendingRate).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("defaultDifferentialLendingRate").value(defaultDifferentialLendingRate)
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(defaultDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
final Boolean isFloatingInterestRateCalculationAllowed = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
"isFloatingInterestRateCalculationAllowed", element);
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed")
|
||||
.value(isFloatingInterestRateCalculationAllowed).notNull().isOneOfTheseValues(true, false);
|
||||
} else {
|
||||
if(this.fromApiJsonHelper.parameterExists("floatingRatesId", element)){
|
||||
baseDataValidator.reset().parameter("floatingRatesId").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)){
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("minDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("defaultDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)){
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element);
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
|
||||
BigDecimal minInterestRatePerPeriod = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) {
|
||||
minInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
|
||||
BigDecimal maxInterestRatePerPeriod = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) {
|
||||
maxInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
if (maxInterestRatePerPeriod != null && maxInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minInterestRatePerPeriod != null && minInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod)
|
||||
.notLessThanMin(minInterestRatePerPeriod);
|
||||
if (minInterestRatePerPeriod.compareTo(maxInterestRatePerPeriod) <= 0) {
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod)
|
||||
.inMinAndMaxAmountRange(minInterestRatePerPeriod, maxInterestRatePerPeriod);
|
||||
}
|
||||
} else {
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod)
|
||||
.notGreaterThanMax(maxInterestRatePerPeriod);
|
||||
}
|
||||
} else if (minInterestRatePerPeriod != null && minInterestRatePerPeriod.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod)
|
||||
.notLessThanMin(minInterestRatePerPeriod);
|
||||
}
|
||||
|
||||
final Integer interestRateFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
|
||||
}
|
||||
|
||||
|
||||
// Guarantee Funds
|
||||
Boolean holdGuaranteeFunds = false;
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.holdGuaranteeFundsParamName, element)) {
|
||||
@@ -746,30 +868,6 @@ public final class LoanProductDataValidator {
|
||||
baseDataValidator.reset().parameter("inArrearsTolerance").value(inArrearsTolerance).ignoreIfNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
|
||||
BigDecimal minInterestRatePerPeriod = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) {
|
||||
minInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
|
||||
BigDecimal maxInterestRatePerPeriod = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) {
|
||||
maxInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||
final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod",
|
||||
element);
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
|
||||
}
|
||||
|
||||
final String minNumberOfRepaymentsParameterName = "minNumberOfRepayments";
|
||||
Integer minNumberOfRepayments = null;
|
||||
if (this.fromApiJsonHelper.parameterExists(minNumberOfRepaymentsParameterName, element)) {
|
||||
@@ -841,12 +939,6 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
|
||||
//
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) {
|
||||
final Integer interestRateFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
|
||||
Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
|
||||
}
|
||||
|
||||
if (this.fromApiJsonHelper.parameterExists("amortizationType", element)) {
|
||||
final Integer amortizationType = this.fromApiJsonHelper.extractIntegerNamed("amortizationType", element, Locale.getDefault());
|
||||
baseDataValidator.reset().parameter("amortizationType").value(amortizationType).notNull().inMinMaxRange(0, 1);
|
||||
@@ -906,6 +998,176 @@ public final class LoanProductDataValidator {
|
||||
}
|
||||
}
|
||||
|
||||
//interest rates
|
||||
boolean isLinkedToFloatingInterestRates = loanProduct.isLinkedToFloatingInterestRate();
|
||||
if(this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)){
|
||||
isLinkedToFloatingInterestRates = this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element);
|
||||
}
|
||||
if(isLinkedToFloatingInterestRates){
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("minInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"minInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxInterestRatePerPeriod", element)){
|
||||
baseDataValidator.reset().parameter("maxInterestRatePerPeriod").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"maxInterestRatePerPeriod param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)){
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.true",
|
||||
"interestRateFrequencyType param is not supported when isLinkedToFloatingInterestRates is true");
|
||||
}
|
||||
|
||||
Integer interestType = this.fromApiJsonHelper.parameterExists("interestType", element)
|
||||
? this.fromApiJsonHelper.extractIntegerNamed("interestType", element, Locale.getDefault())
|
||||
: loanProduct.getLoanProductRelatedDetail().getInterestMethod().getValue();
|
||||
if((interestType == null || interestType != InterestMethod.DECLINING_BALANCE.getValue())
|
||||
|| (isInterestRecalculationEnabled == null || isInterestRecalculationEnabled == false)){
|
||||
baseDataValidator.reset().parameter("isLinkedToFloatingInterestRates").failWithCode("supported.only.for.declining.balance.interest.recalculation.enabled",
|
||||
"Floating interest rates are supported only for declining balance and interest recalculation enabled loan products");
|
||||
}
|
||||
|
||||
Long floatingRatesId = loanProduct.getFloatingRates() == null? null: loanProduct.getFloatingRates().getFloatingRate().getId();
|
||||
if(this.fromApiJsonHelper.parameterExists("floatingRatesId", element)){
|
||||
floatingRatesId = this.fromApiJsonHelper.extractLongNamed("floatingRatesId", element);
|
||||
}
|
||||
baseDataValidator.reset().parameter("floatingRatesId").value(floatingRatesId).notNull();
|
||||
|
||||
BigDecimal interestRateDifferential = loanProduct.getFloatingRates() == null? null: loanProduct.getFloatingRates().getInterestRateDifferential();
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)){
|
||||
interestRateDifferential = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRateDifferential", element);
|
||||
}
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").value(interestRateDifferential).notNull().zeroOrPositiveAmount();
|
||||
|
||||
final String minDifferentialLendingRateParameterName = "minDifferentialLendingRate";
|
||||
BigDecimal minDifferentialLendingRate = loanProduct.getFloatingRates() == null
|
||||
? null:loanProduct.getFloatingRates().getMinDifferentialLendingRate();
|
||||
if (this.fromApiJsonHelper.parameterExists(minDifferentialLendingRateParameterName, element)) {
|
||||
minDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minDifferentialLendingRateParameterName,
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(minDifferentialLendingRateParameterName).value(minDifferentialLendingRate).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String defaultDifferentialLendingRateParameterName = "defaultDifferentialLendingRate";
|
||||
BigDecimal defaultDifferentialLendingRate = loanProduct.getFloatingRates() == null
|
||||
? null:loanProduct.getFloatingRates().getDefaultDifferentialLendingRate();
|
||||
if (this.fromApiJsonHelper.parameterExists(defaultDifferentialLendingRateParameterName, element)) {
|
||||
defaultDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(defaultDifferentialLendingRateParameterName,
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(defaultDifferentialLendingRateParameterName).value(defaultDifferentialLendingRate).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String maxDifferentialLendingRateParameterName = "maxDifferentialLendingRate";
|
||||
BigDecimal maxDifferentialLendingRate = loanProduct.getFloatingRates() == null
|
||||
? null:loanProduct.getFloatingRates().getMaxDifferentialLendingRate();
|
||||
if (this.fromApiJsonHelper.parameterExists(maxDifferentialLendingRateParameterName, element)) {
|
||||
maxDifferentialLendingRate = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxDifferentialLendingRateParameterName,
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(maxDifferentialLendingRateParameterName).value(maxDifferentialLendingRate).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("defaultDifferentialLendingRate").value(defaultDifferentialLendingRate)
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (minDifferentialLendingRate != null && minDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(minDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxDifferentialLendingRate != null && maxDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
if (defaultDifferentialLendingRate != null && defaultDifferentialLendingRate.compareTo(BigDecimal.ZERO) != -1) {
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").value(maxDifferentialLendingRate)
|
||||
.notLessThanMin(defaultDifferentialLendingRate);
|
||||
}
|
||||
}
|
||||
|
||||
Boolean isFloatingInterestRateCalculationAllowed = loanProduct.getFloatingRates() == null ? null
|
||||
: loanProduct.getFloatingRates().isFloatingInterestRateCalculationAllowed();
|
||||
if(this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)){
|
||||
isFloatingInterestRateCalculationAllowed = this.fromApiJsonHelper.extractBooleanNamed(
|
||||
"isFloatingInterestRateCalculationAllowed", element);
|
||||
}
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed")
|
||||
.value(isFloatingInterestRateCalculationAllowed).notNull().isOneOfTheseValues(true, false);
|
||||
} else {
|
||||
if(this.fromApiJsonHelper.parameterExists("floatingRatesId", element)){
|
||||
baseDataValidator.reset().parameter("floatingRatesId").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"floatingRatesId param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("interestRateDifferential", element)){
|
||||
baseDataValidator.reset().parameter("interestRateDifferential").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"interestRateDifferential param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("minDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("minDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"minDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("defaultDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("defaultDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"defaultDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("maxDifferentialLendingRate", element)){
|
||||
baseDataValidator.reset().parameter("maxDifferentialLendingRate").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"maxDifferentialLendingRate param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
if(this.fromApiJsonHelper.parameterExists("isFloatingInterestRateCalculationAllowed", element)){
|
||||
baseDataValidator.reset().parameter("isFloatingInterestRateCalculationAllowed").failWithCode("not.supported.when.isLinkedToFloatingInterestRates.is.false",
|
||||
"isFloatingInterestRateCalculationAllowed param is not supported when isLinkedToFloatingInterestRates is not supplied or false");
|
||||
}
|
||||
|
||||
final String minInterestRatePerPeriodParameterName = "minInterestRatePerPeriod";
|
||||
BigDecimal minInterestRatePerPeriod = loanProduct.getMinNominalInterestRatePerPeriod();
|
||||
if (this.fromApiJsonHelper.parameterExists(minInterestRatePerPeriodParameterName, element)) {
|
||||
minInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(minInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(minInterestRatePerPeriodParameterName).value(minInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
final String maxInterestRatePerPeriodParameterName = "maxInterestRatePerPeriod";
|
||||
BigDecimal maxInterestRatePerPeriod = loanProduct.getMaxNominalInterestRatePerPeriod();
|
||||
if (this.fromApiJsonHelper.parameterExists(maxInterestRatePerPeriodParameterName, element)) {
|
||||
maxInterestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed(maxInterestRatePerPeriodParameterName,
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter(maxInterestRatePerPeriodParameterName).value(maxInterestRatePerPeriod).ignoreIfNull()
|
||||
.zeroOrPositiveAmount();
|
||||
|
||||
BigDecimal interestRatePerPeriod = loanProduct.getLoanProductRelatedDetail().getNominalInterestRatePerPeriod();
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRatePerPeriod", element)) {
|
||||
interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod",
|
||||
element);
|
||||
}
|
||||
baseDataValidator.reset().parameter("interestRatePerPeriod").value(interestRatePerPeriod).notNull().zeroOrPositiveAmount();
|
||||
|
||||
Integer interestRateFrequencyType = loanProduct.getLoanProductRelatedDetail().getInterestPeriodFrequencyType().getValue();
|
||||
if (this.fromApiJsonHelper.parameterExists("interestRateFrequencyType", element)) {
|
||||
interestRateFrequencyType = this.fromApiJsonHelper.extractIntegerNamed("interestRateFrequencyType", element,
|
||||
Locale.getDefault());
|
||||
}
|
||||
baseDataValidator.reset().parameter("interestRateFrequencyType").value(interestRateFrequencyType).notNull().inMinMaxRange(0, 3);
|
||||
}
|
||||
|
||||
// Guarantee Funds
|
||||
Boolean holdGuaranteeFunds = loanProduct.isHoldGuaranteeFundsEnabled();
|
||||
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.holdGuaranteeFundsParamName, element)) {
|
||||
@@ -1303,6 +1565,11 @@ public final class LoanProductDataValidator {
|
||||
private void validateNominalInterestRatePerPeriodMinMaxConstraint(final JsonElement element, final LoanProduct loanProduct,
|
||||
final DataValidatorBuilder baseDataValidator) {
|
||||
|
||||
if((this.fromApiJsonHelper.parameterExists("isLinkedToFloatingInterestRates", element)
|
||||
&& this.fromApiJsonHelper.extractBooleanNamed("isLinkedToFloatingInterestRates", element) == true)
|
||||
|| loanProduct.isLinkedToFloatingInterestRate()){
|
||||
return;
|
||||
}
|
||||
boolean iRPUpdated = false;
|
||||
boolean minIRPUpdated = false;
|
||||
boolean maxIRPUpdated = false;
|
||||
|
||||
+5
-1
@@ -199,7 +199,7 @@ public class LoanEnumerations {
|
||||
return optionData;
|
||||
}
|
||||
|
||||
public static EnumOptionData interestRateFrequencyType(final int id) {
|
||||
public static EnumOptionData interestRateFrequencyType(final Integer id) {
|
||||
return interestRateFrequencyType(PeriodFrequencyType.fromInt(id));
|
||||
}
|
||||
|
||||
@@ -503,6 +503,10 @@ public class LoanEnumerations {
|
||||
optionData = new EnumOptionData(LoanTermVariationType.EMI_AMOUNT.getValue().longValue(),
|
||||
LoanTermVariationType.EMI_AMOUNT.getCode(), "emiAmount");
|
||||
break;
|
||||
case INTEREST_RATE:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.INTEREST_RATE.getValue().longValue(),
|
||||
LoanTermVariationType.INTEREST_RATE.getCode(), "interestRate");
|
||||
break;
|
||||
default:
|
||||
optionData = new EnumOptionData(LoanTermVariationType.INVALID.getValue().longValue(),
|
||||
LoanTermVariationType.INVALID.getCode(), "Invalid");
|
||||
|
||||
+24
-3
@@ -192,13 +192,23 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
+ "curr.name as currencyName, curr.internationalized_name_code as currencyNameCode, curr.display_symbol as currencyDisplaySymbol, lp.external_id as externalId, "
|
||||
+ "lca.id as lcaId, lca.amortization_method_enum as amortizationBoolean, lca.interest_method_enum as interestMethodConfigBoolean, "
|
||||
+ "lca.loan_transaction_strategy_id as transactionProcessingStrategyBoolean,lca.interest_calculated_in_period_enum as interestCalcPeriodBoolean, lca.arrearstolerance_amount as arrearsToleranceBoolean, "
|
||||
+ "lca.repay_every as repaymentFrequencyBoolean, lca.moratorium as graceOnPrincipalAndInterestBoolean, lca.grace_on_arrears_ageing as graceOnArrearsAgingBoolean "
|
||||
+ "lca.repay_every as repaymentFrequencyBoolean, lca.moratorium as graceOnPrincipalAndInterestBoolean, lca.grace_on_arrears_ageing as graceOnArrearsAgingBoolean, "
|
||||
+ "lp.is_linked_to_floating_interest_rates as isLinkedToFloatingInterestRates, "
|
||||
+ "lfr.floating_rates_id as floatingRateId, "
|
||||
+ "fr.name as floatingRateName, "
|
||||
+ "lfr.interest_rate_differential as interestRateDifferential, "
|
||||
+ "lfr.min_differential_lending_rate as minDifferentialLendingRate, "
|
||||
+ "lfr.default_differential_lending_rate as defaultDifferentialLendingRate, "
|
||||
+ "lfr.max_differential_lending_rate as maxDifferentialLendingRate, "
|
||||
+ "lfr.is_floating_interest_rate_calculation_allowed as isFloatingInterestRateCalculationAllowed "
|
||||
+ " from m_product_loan lp "
|
||||
+ " left join m_fund f on f.id = lp.fund_id "
|
||||
+ " left join m_product_loan_recalculation_details lpr on lpr.product_id=lp.id "
|
||||
+ " left join m_product_loan_guarantee_details lpg on lpg.loan_product_id=lp.id "
|
||||
+ " left join ref_loan_transaction_processing_strategy ltps on ltps.id = lp.loan_transaction_strategy_id"
|
||||
+ " left join m_product_loan_configurable_attributes lca on lca.loan_product_id = lp.id "
|
||||
+ " left join m_product_loan_floating_rates as lfr on lfr.loan_product_id = lp.id "
|
||||
+ " left join m_floating_rates as fr on lfr.floating_rates_id = fr.id "
|
||||
+ " join m_currency curr on curr.code = lp.currency_code";
|
||||
}
|
||||
|
||||
@@ -250,13 +260,22 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
final BigDecimal maxInterestRatePerPeriod = rs.getBigDecimal("maxInterestRatePerPeriod");
|
||||
final BigDecimal annualInterestRate = rs.getBigDecimal("annualInterestRate");
|
||||
|
||||
final boolean isLinkedToFloatingInterestRates = rs.getBoolean("isLinkedToFloatingInterestRates");
|
||||
final Integer floatingRateId = JdbcSupport.getIntegerDefaultToNullIfZero(rs, "floatingRateId");
|
||||
final String floatingRateName = rs.getString("floatingRateName");
|
||||
final BigDecimal interestRateDifferential = rs.getBigDecimal("interestRateDifferential");
|
||||
final BigDecimal minDifferentialLendingRate = rs.getBigDecimal("minDifferentialLendingRate");
|
||||
final BigDecimal defaultDifferentialLendingRate = rs.getBigDecimal("defaultDifferentialLendingRate");
|
||||
final BigDecimal maxDifferentialLendingRate = rs.getBigDecimal("maxDifferentialLendingRate");
|
||||
final boolean isFloatingInterestRateCalculationAllowed = rs.getBoolean("isFloatingInterestRateCalculationAllowed");
|
||||
|
||||
final int repaymentFrequencyTypeId = JdbcSupport.getInteger(rs, "repaymentPeriodFrequency");
|
||||
final EnumOptionData repaymentFrequencyType = LoanEnumerations.repaymentFrequencyType(repaymentFrequencyTypeId);
|
||||
|
||||
final int amortizationTypeId = JdbcSupport.getInteger(rs, "amortizationMethod");
|
||||
final EnumOptionData amortizationType = LoanEnumerations.amortizationType(amortizationTypeId);
|
||||
|
||||
final int interestRateFrequencyTypeId = JdbcSupport.getInteger(rs, "interestRatePerPeriodFreq");
|
||||
final Integer interestRateFrequencyTypeId = JdbcSupport.getInteger(rs, "interestRatePerPeriodFreq");
|
||||
final EnumOptionData interestRateFrequencyType = LoanEnumerations.interestRateFrequencyType(interestRateFrequencyTypeId);
|
||||
|
||||
final int interestTypeId = JdbcSupport.getInteger(rs, "interestMethod");
|
||||
@@ -377,7 +396,9 @@ public class LoanProductReadPlatformServiceImpl implements LoanProductReadPlatfo
|
||||
graceOnArrearsAgeing, overdueDaysForNPA, daysInMonthType, daysInYearType, isInterestRecalculationEnabled,
|
||||
interestRecalculationData, minimumDaysBetweenDisbursalAndFirstRepayment, holdGuaranteeFunds, loanProductGuaranteeData,
|
||||
principalThresholdForLastInstallment, accountMovesOutOfNPAOnlyOnArrearsCompletion, canDefineInstallmentAmount,
|
||||
installmentAmountInMultiplesOf, allowAttributeOverrides);
|
||||
installmentAmountInMultiplesOf, allowAttributeOverrides, isLinkedToFloatingInterestRates, floatingRateId,
|
||||
floatingRateName, interestRateDifferential, minDifferentialLendingRate, defaultDifferentialLendingRate,
|
||||
maxDifferentialLendingRate, isFloatingInterestRateCalculationAllowed);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-4
@@ -21,9 +21,12 @@ import org.mifosplatform.infrastructure.entityaccess.service.MifosEntityAccessUt
|
||||
import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext;
|
||||
import org.mifosplatform.portfolio.charge.domain.Charge;
|
||||
import org.mifosplatform.portfolio.charge.domain.ChargeRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRate;
|
||||
import org.mifosplatform.portfolio.floatingrates.domain.FloatingRateRepositoryWrapper;
|
||||
import org.mifosplatform.portfolio.fund.domain.Fund;
|
||||
import org.mifosplatform.portfolio.fund.domain.FundRepository;
|
||||
import org.mifosplatform.portfolio.fund.exception.FundNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionProcessingStrategyRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanTransactionProcessingStrategyNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
|
||||
@@ -31,6 +34,7 @@ import org.mifosplatform.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.mifosplatform.portfolio.loanproduct.domain.LoanTransactionProcessingStrategy;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.InvalidCurrencyException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductCannotBeModifiedDueToNonClosedLoansException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductDateException;
|
||||
import org.mifosplatform.portfolio.loanproduct.exception.LoanProductNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanproduct.serialization.LoanProductDataValidator;
|
||||
@@ -57,6 +61,8 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
private final ChargeRepositoryWrapper chargeRepository;
|
||||
private final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService;
|
||||
private final MifosEntityAccessUtil mifosEntityAccessUtil;
|
||||
private final FloatingRateRepositoryWrapper floatingRateRepository;
|
||||
private final LoanRepository loanRepository;
|
||||
|
||||
@Autowired
|
||||
public LoanProductWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context,
|
||||
@@ -65,7 +71,9 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
final LoanTransactionProcessingStrategyRepository loanTransactionProcessingStrategyRepository,
|
||||
final ChargeRepositoryWrapper chargeRepository,
|
||||
final ProductToGLAccountMappingWritePlatformService accountMappingWritePlatformService,
|
||||
final MifosEntityAccessUtil mifosEntityAccessUtil) {
|
||||
final MifosEntityAccessUtil mifosEntityAccessUtil,
|
||||
final FloatingRateRepositoryWrapper floatingRateRepository,
|
||||
final LoanRepository loanRepository) {
|
||||
this.context = context;
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.loanProductRepository = loanProductRepository;
|
||||
@@ -75,6 +83,8 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
this.chargeRepository = chargeRepository;
|
||||
this.accountMappingWritePlatformService = accountMappingWritePlatformService;
|
||||
this.mifosEntityAccessUtil = mifosEntityAccessUtil;
|
||||
this.floatingRateRepository = floatingRateRepository;
|
||||
this.loanRepository = loanRepository;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -96,8 +106,13 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
final String currencyCode = command.stringValueOfParameterNamed("currencyCode");
|
||||
final List<Charge> charges = assembleListOfProductCharges(command, currencyCode);
|
||||
|
||||
FloatingRate floatingRate = null;
|
||||
if(command.parameterExists("floatingRatesId")){
|
||||
floatingRate = this.floatingRateRepository
|
||||
.findOneWithNotFoundDetection(command.longValueOfParameterNamed("floatingRatesId"));
|
||||
}
|
||||
final LoanProduct loanproduct = LoanProduct.assembleFromJson(fund, loanTransactionProcessingStrategy, charges, command,
|
||||
this.aprCalculator);
|
||||
this.aprCalculator, floatingRate);
|
||||
loanproduct.updateLoanProductInRelatedClasses();
|
||||
|
||||
this.loanProductRepository.save(loanproduct);
|
||||
@@ -154,7 +169,18 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
this.fromApiJsonDeserializer.validateForUpdate(command.json(), product);
|
||||
validateInputDates(command);
|
||||
|
||||
final Map<String, Object> changes = product.update(command, this.aprCalculator);
|
||||
if(anyChangeInCriticalFloatingRateLinkedParams(command, product)
|
||||
&& this.loanRepository.doNonClosedLoanAccountsExistForProduct(product.getId())){
|
||||
throw new LoanProductCannotBeModifiedDueToNonClosedLoansException(product.getId());
|
||||
}
|
||||
|
||||
FloatingRate floatingRate = null;
|
||||
if(command.parameterExists("floatingRatesId")){
|
||||
floatingRate = this.floatingRateRepository
|
||||
.findOneWithNotFoundDetection(command.longValueOfParameterNamed("floatingRatesId"));
|
||||
}
|
||||
|
||||
final Map<String, Object> changes = product.update(command, this.aprCalculator, floatingRate);
|
||||
|
||||
if (changes.containsKey("fundId")) {
|
||||
final Long fundId = (Long) changes.get("fundId");
|
||||
@@ -199,7 +225,15 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
|
||||
}
|
||||
|
||||
private List<Charge> assembleListOfProductCharges(final JsonCommand command, final String currencyCode) {
|
||||
private boolean anyChangeInCriticalFloatingRateLinkedParams(JsonCommand command, LoanProduct product) {
|
||||
final boolean isChangeFromFloatingToFlatOrViceVersa = command.isChangeInBooleanParameterNamed("isLinkedToFloatingInterestRates", product.isLinkedToFloatingInterestRate());
|
||||
final boolean isChangeInCriticalFloatingRateParams = product.getFloatingRates() != null
|
||||
&& (command.isChangeInLongParameterNamed("floatingRatesId", product.getFloatingRates().getFloatingRate().getId())
|
||||
|| command.isChangeInBigDecimalParameterNamed("interestRateDifferential", product.getFloatingRates().getInterestRateDifferential()));
|
||||
return isChangeFromFloatingToFlatOrViceVersa || isChangeInCriticalFloatingRateParams;
|
||||
}
|
||||
|
||||
private List<Charge> assembleListOfProductCharges(final JsonCommand command, final String currencyCode) {
|
||||
|
||||
final List<Charge> charges = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
CREATE TABLE `m_floating_rates` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`name` VARCHAR(200) NOT NULL,
|
||||
`is_base_lending_rate` BIT(1) NOT NULL DEFAULT 0,
|
||||
`is_active` BIT(1) NOT NULL DEFAULT 1,
|
||||
`createdby_id` BIGINT(20) NOT NULL,
|
||||
`created_date` DATETIME NOT NULL,
|
||||
`lastmodifiedby_id` BIGINT(20) NOT NULL,
|
||||
`lastmodified_date` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `unq_name` (`name`)
|
||||
);
|
||||
|
||||
CREATE TABLE `m_floating_rates_periods` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`floating_rates_id` BIGINT(20) NOT NULL,
|
||||
`from_date` DATETIME NOT NULL,
|
||||
`interest_rate` DECIMAL(19,6) NOT NULL,
|
||||
`is_differential_to_base_lending_rate` BIT(1) NOT NULL DEFAULT 0,
|
||||
`is_active` BIT(1) NOT NULL DEFAULT 1,
|
||||
`createdby_id` BIGINT(20) NOT NULL,
|
||||
`created_date` DATETIME NOT NULL,
|
||||
`lastmodifiedby_id` BIGINT(20) NOT NULL,
|
||||
`lastmodified_date` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `FK_mappings_m_floating_rates` FOREIGN KEY (`floating_rates_id`) REFERENCES `m_floating_rates` (`id`)
|
||||
);
|
||||
|
||||
ALTER TABLE `m_product_loan`
|
||||
ADD COLUMN `is_linked_to_floating_interest_rates` BIT(1) NOT NULL DEFAULT 0 AFTER `fund_id`,
|
||||
MODIFY COLUMN `nominal_interest_rate_per_period` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
MODIFY COLUMN `interest_period_frequency_enum` SMALLINT(5) NULL DEFAULT NULL,
|
||||
MODIFY COLUMN `annual_nominal_interest_rate` DECIMAL(19,6) NULL DEFAULT NULL;
|
||||
|
||||
CREATE TABLE `m_product_loan_floating_rates` (
|
||||
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
|
||||
`loan_product_id` BIGINT(20) NOT NULL,
|
||||
`floating_rates_id` BIGINT(20) NOT NULL,
|
||||
`interest_rate_differential` DECIMAL(19,6) NOT NULL DEFAULT 0,
|
||||
`min_differential_lending_rate` DECIMAL(19,6) NOT NULL DEFAULT 0,
|
||||
`default_differential_lending_rate` DECIMAL(19,6) NOT NULL DEFAULT 0,
|
||||
`max_differential_lending_rate` DECIMAL(19,6) NOT NULL DEFAULT 0,
|
||||
`is_floating_interest_rate_calculation_allowed` BIT(1) NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
CONSTRAINT `FK_mappings_m_product_loan_id` FOREIGN KEY (`loan_product_id`) REFERENCES `m_product_loan` (`id`),
|
||||
CONSTRAINT `FK_mappings_m_floating_rates_id` FOREIGN KEY (`floating_rates_id`) REFERENCES `m_floating_rates` (`id`)
|
||||
);
|
||||
|
||||
ALTER TABLE `m_loan`
|
||||
ADD COLUMN `is_floating_interest_rate` BIT(1) NULL DEFAULT 0 AFTER `arrearstolerance_amount`,
|
||||
ADD COLUMN `interest_rate_differential` DECIMAL(19,6) NULL DEFAULT 0 AFTER `is_floating_interest_rate`,
|
||||
MODIFY COLUMN `nominal_interest_rate_per_period` DECIMAL(19,6) NULL DEFAULT NULL,
|
||||
MODIFY COLUMN `interest_period_frequency_enum` SMALLINT(5) NULL DEFAULT NULL,
|
||||
MODIFY COLUMN `annual_nominal_interest_rate` DECIMAL(19,6) NULL DEFAULT NULL;
|
||||
|
||||
INSERT INTO `m_permission` (`grouping`, `code`, `entity_name`, `action_name`, `can_maker_checker`)
|
||||
VALUES ('portfolio', 'READ_FLOATINGRATE', 'FLOATINGRATE', 'READ', 0),
|
||||
('portfolio', 'CREATE_FLOATINGRATE', 'FLOATINGRATE', 'CREATE', 1),
|
||||
('portfolio', 'CREATE_FLOATINGRATE_CHECKER', 'FLOATINGRATE', 'CREATE_CHECKER', 0),
|
||||
('portfolio', 'UPDATE_FLOATINGRATE', 'FLOATINGRATE', 'UPDATE', 1),
|
||||
('portfolio', 'UPDATE_FLOATINGRATE_CHECKER', 'FLOATINGRATE', 'UPDATE_CHECKER', 0);
|
||||
Reference in New Issue
Block a user