You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ra...@apache.org on 2016/05/04 14:26:19 UTC
[07/10] incubator-fineract git commit: FINERACT-60 : Interest
compounding, nth day rest frequency and meeting calendar date changes
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java
index a525606..84c53b1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java
@@ -30,6 +30,7 @@ import java.util.StringTokenizer;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateList;
import net.fortuna.ical4j.model.DateTime;
+import net.fortuna.ical4j.model.NumberList;
import net.fortuna.ical4j.model.Recur;
import net.fortuna.ical4j.model.ValidationException;
import net.fortuna.ical4j.model.WeekDay;
@@ -37,18 +38,23 @@ import net.fortuna.ical4j.model.WeekDayList;
import net.fortuna.ical4j.model.parameter.Value;
import net.fortuna.ical4j.model.property.RRule;
+import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
+import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.workingdays.domain.WorkingDays;
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType;
import org.apache.fineract.portfolio.calendar.domain.CalendarWeekDaysType;
+import org.apache.fineract.portfolio.common.domain.NthDayType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
+import com.google.gson.JsonElement;
+
public class CalendarUtils {
static {
@@ -260,10 +266,48 @@ public class CalendarUtils {
}
} else if (recur.getFrequency().equals(Recur.MONTHLY)) {
- if (recur.getInterval() == 1) {
+ NumberList nthDays = recur.getSetPosList();
+ Integer nthDay = null;
+ if (!nthDays.isEmpty())
+ nthDay = (Integer) nthDays.get(0);
+ NumberList monthDays = recur.getMonthDayList();
+ Integer monthDay = null;
+ if (!monthDays.isEmpty())
+ monthDay = (Integer) monthDays.get(0);
+ WeekDayList weekdays = recur.getDayList();
+ WeekDay weekDay = null;
+ if (!weekdays.isEmpty())
+ weekDay = (WeekDay) weekdays.get(0);
+ if (nthDay != null && weekDay != null) {
+ NthDayType nthDayType = NthDayType.fromInt(nthDay);
+ NthDayNameEnum nthDayName = NthDayNameEnum.from(nthDayType.toString());
+ DayNameEnum weekdayType = DayNameEnum.from(weekDay.getDay());
+ if (recur.getInterval() == 1 || recur.getInterval() == -1) {
+ humanReadable = "Monthly on " + nthDayName.getCode().toLowerCase() + " " + weekdayType.getCode().toLowerCase();
+ } else {
+ humanReadable = "Every " + recur.getInterval() + " months on " + nthDayName.getCode().toLowerCase() + " "
+ + weekdayType.getCode().toLowerCase();
+ }
+ } else if (monthDay != null) {
+ if (monthDay == -1) {
+ if (recur.getInterval() == 1 || recur.getInterval() == -1) {
+ humanReadable = "Monthly on last day";
+ } else {
+ humanReadable = "Every " + recur.getInterval() + " months on last day";
+ }
+ } else {
+ if (recur.getInterval() == 1 || recur.getInterval() == -1) {
+ humanReadable = "Monthly on day " + monthDay;
+ } else {
+ humanReadable = "Every " + recur.getInterval() + " months on day " + monthDay;
+ }
+ }
+ } else {
+ if (recur.getInterval() == 1 || recur.getInterval() == -1) {
humanReadable = "Monthly on day " + startDate.getDayOfMonth();
} else {
humanReadable = "Every " + recur.getInterval() + " months on day " + startDate.getDayOfMonth();
+ }
}
} else if (recur.getFrequency().equals(Recur.YEARLY)) {
if (recur.getInterval() == 1) {
@@ -347,6 +391,28 @@ public class CalendarUtils {
return DayNameEnum.MO;// Default it to Monday
}
}
+ public static enum NthDayNameEnum {
+ ONE(1, "First"), TWO(2, "Second"), THREE(3, "Third"), FOUR(4, "Fourth"), FIVE(5, "Fifth"), LAST(-1, "Last"), INVALID(0, "Invalid");
+ private final String code;
+ private final Integer value;
+
+ private NthDayNameEnum(final Integer value, final String code) {
+ this.value = value;
+ this.code = code;
+ }
+ public String getCode() {
+ return this.code;
+ }
+ public int getValue() {
+ return this.value;
+ }
+ public static NthDayNameEnum from(final String name) {
+ for (final NthDayNameEnum nthDayName : NthDayNameEnum.values()) {
+ if (nthDayName.toString().equals(name)) { return nthDayName; }
+ }
+ return NthDayNameEnum.INVALID;
+ }
+ }
public static PeriodFrequencyType getMeetingPeriodFrequencyType(final String recurringRule) {
final Recur recur = CalendarUtils.getICalRecur(recurringRule);
@@ -399,6 +465,18 @@ public class CalendarUtils {
WeekDay weekDay = (WeekDay) weekDays.get(0);
return CalendarWeekDaysType.fromString(weekDay.getDay());
}
+ public static NthDayType getRepeatsOnNthDayOfMonth(final String recurringRule) {
+ final Recur recur = CalendarUtils.getICalRecur(recurringRule);
+ NumberList monthDays = null;
+ if(recur.getDayList().isEmpty())
+ monthDays = recur.getMonthDayList();
+ else
+ monthDays = recur.getSetPosList();
+ if (monthDays.isEmpty()) return NthDayType.INVALID;
+ if (!recur.getMonthDayList().isEmpty() && recur.getSetPosList().isEmpty()) return NthDayType.ONDAY;
+ Integer monthDay = (Integer) monthDays.get(0);
+ return NthDayType.fromInt(monthDay);
+ }
public static LocalDate getFirstRepaymentMeetingDate(final Calendar calendar, final LocalDate disbursementDate,
final Integer loanRepaymentInterval, final String frequency, boolean isSkipRepaymentOnFirstDayOfMonth,
@@ -589,4 +667,37 @@ public class CalendarUtils {
return scheduleDate;
}
+ public static void validateNthDayOfMonthFrequency(DataValidatorBuilder baseDataValidator, final String repeatsOnNthDayOfMonthParamName,
+ final String repeatsOnDayParamName, final JsonElement element, final FromJsonHelper fromApiJsonHelper) {
+ final Integer repeatsOnNthDayOfMonth = fromApiJsonHelper.extractIntegerSansLocaleNamed(repeatsOnNthDayOfMonthParamName, element);
+ baseDataValidator.reset().parameter(repeatsOnNthDayOfMonthParamName).value(repeatsOnNthDayOfMonth).ignoreIfNull()
+ .isOneOfTheseValues(NthDayType.ONE.getValue(), NthDayType.TWO.getValue(), NthDayType.THREE.getValue(),
+ NthDayType.FOUR.getValue(), NthDayType.LAST.getValue(), NthDayType.ONDAY.getValue());
+ final Integer repeatsOnDay = fromApiJsonHelper.extractIntegerSansLocaleNamed(repeatsOnDayParamName, element);
+ baseDataValidator.reset().parameter(repeatsOnDayParamName).value(repeatsOnDay).ignoreIfNull()
+ .inMinMaxRange(CalendarWeekDaysType.getMinValue(), CalendarWeekDaysType.getMaxValue());
+ NthDayType nthDayType = null;
+ if (repeatsOnNthDayOfMonth != null) {
+ nthDayType = NthDayType.fromInt(repeatsOnNthDayOfMonth);
+ }
+ if (nthDayType != null && nthDayType != NthDayType.INVALID) {
+ if (nthDayType == NthDayType.ONE || nthDayType == NthDayType.TWO || nthDayType == NthDayType.THREE
+ || nthDayType == NthDayType.FOUR) {
+ baseDataValidator.reset().parameter(repeatsOnDayParamName).value(repeatsOnDay).cantBeBlankWhenParameterProvidedIs(
+ repeatsOnNthDayOfMonthParamName, NthDayNameEnum.from(nthDayType.toString()).getCode().toLowerCase());
+ }
+ }
+ }
+ public static Integer getMonthOnDay(String recurringRule) {
+ final Recur recur = CalendarUtils.getICalRecur(recurringRule);
+ NumberList monthDayList = null;
+ Integer monthOnDay = null;
+ if (getMeetingPeriodFrequencyType(recur).isMonthly()) {
+ monthDayList = recur.getMonthDayList();
+ if (!monthDayList.isEmpty()) {
+ monthOnDay = (Integer) monthDayList.get(0);
+ }
+ }
+ return monthOnDay;
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java
index c987bd7..be01e2b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java
@@ -33,7 +33,6 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
-import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.calendar.CalendarConstants.CALENDAR_SUPPORTED_PARAMETERS;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
@@ -291,8 +290,10 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
if (reschedulebasedOnMeetingDates == null){
presentMeetingDate = command.localDateValueOfParameterNamed(CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue());
}
- final Date endDate = presentMeetingDate.minusDays(1).toDate();
- calendarHistory.updateEndDate(endDate);
+ if (null != newMeetingDate) {
+ final Date endDate = presentMeetingDate.minusDays(1).toDate();
+ calendarHistory.updateEndDate(endDate);
+ }
this.calendarHistoryRepository.save(calendarHistory);
Set<CalendarHistory> history = calendarForUpdate.getCalendarHistory();
history.add(calendarHistory);
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/domain/NthDayType.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/domain/NthDayType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/domain/NthDayType.java
index d32b218..7abb445 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/domain/NthDayType.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/domain/NthDayType.java
@@ -25,6 +25,8 @@ public enum NthDayType {
THREE(3,"nthDayType.three"),
FOUR(4,"nthDayType.four"),
FIVE(5,"nthDayType.five"),
+ LAST(-1,"nthDayType.last"),
+ ONDAY(-2,"nthDayType.onday"),
INVALID(0,"nthDayType.invalid");
private final Integer value;
@@ -62,6 +64,12 @@ public enum NthDayType {
case 5:
repaymentFrequencyNthDayType = NthDayType.FIVE;
break;
+ case -1:
+ repaymentFrequencyNthDayType = NthDayType.LAST;
+ break;
+ case -2:
+ repaymentFrequencyNthDayType = NthDayType.ONDAY;
+ break;
default:
break;
}
@@ -69,4 +77,13 @@ public enum NthDayType {
return repaymentFrequencyNthDayType;
}
+ public boolean isInvalid() {
+ return this.value.equals(NthDayType.INVALID.value);
+ }
+ public boolean isLastDay() {
+ return this.value.equals(NthDayType.LAST.value);
+ }
+ public boolean isOnDay() {
+ return this.value.equals(NthDayType.ONDAY.value);
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
index c44b8e4..fc503b7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java
@@ -52,6 +52,7 @@ import org.apache.fineract.organisation.staff.service.StaffReadPlatformService;
import org.apache.fineract.portfolio.calendar.data.CalendarData;
import org.apache.fineract.portfolio.calendar.service.CalendarEnumerations;
import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService;
+import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.client.data.ClientData;
import org.apache.fineract.portfolio.client.domain.ClientEnumerations;
import org.apache.fineract.portfolio.client.service.ClientReadPlatformService;
@@ -271,10 +272,12 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService
final LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate");
final String recurrence = rs.getString("recurrence");
final LocalTime meetingTime = JdbcSupport.getLocalTime(rs,"meetingTime");
+ Integer monthOnDay = CalendarUtils.getMonthOnDay(recurrence);
CalendarData calendarData = CalendarData.instance(calendarId, calendarInstanceId, entityId, entityType, title, description,
location, startDate, endDate, null, null, false, recurrence, null, null, null, null, null, null, null, null, null,
- null, null, null, null,meetingTime);
+ null, null, null, null, null, meetingTime, monthOnDay);
+
return CenterData.instance(id, accountNo, name, externalId, status, activationDate, officeId, null, staffId, staffName, hierarchy, null,
calendarData);
}
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
index 61ea803..41d6c0b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
@@ -40,12 +40,49 @@ public interface LoanApiConstants {
public static final String rejectedOnDateParameterName = "rejectedOnDate";
public static final String withdrawnOnDateParameterName = "withdrawnOnDate";
- // Interest recalculation related
- public static final String isInterestRecalculationEnabledParameterName = "isInterestRecalculationEnabled";
+ public static final String transactionProcessingStrategyIdParameterName = "transactionProcessingStrategyId";
+ public static final String loanPurposeIdParameterName = "loanPurposeId";
+ public static final String loanOfficerIdParameterName = "loanOfficerId";
+ public static final String fundIdParameterName = "fundId";
+ public static final String externalIdParameterName = "externalId";
+ public static final String accountNoParameterName = "accountNo";
+ public static final String productIdParameterName = "productId";
+ public static final String calendarIdParameterName = "calendarId";
+ public static final String loanTypeParameterName = "loanType";
+ public static final String groupIdParameterName = "groupId";
+ public static final String clientIdParameterName = "clientId";
+ public static final String idParameterName = "id";
+ public static final String graceOnInterestChargedParameterName = "graceOnInterestCharged";
+ public static final String graceOnInterestPaymentParameterName = "graceOnInterestPayment";
+ public static final String graceOnPrincipalPaymentParameterName = "graceOnPrincipalPayment";
+ public static final String repaymentsStartingFromDateParameterName = "repaymentsStartingFromDate";
+ public static final String interestRateFrequencyTypeParameterName = "interestRateFrequencyType";
+ public static final String interestCalculationPeriodTypeParameterName = "interestCalculationPeriodType";
+ public static final String interestTypeParameterName = "interestType";
+ public static final String amortizationTypeParameterName = "amortizationType";
+ public static final String repaymentFrequencyTypeParameterName = "repaymentFrequencyType";
+ public static final String loanTermFrequencyTypeParameterName = "loanTermFrequencyType";
+ public static final String loanTermFrequencyParameterName = "loanTermFrequency";
+ public static final String numberOfRepaymentsParameterName = "numberOfRepayments";
+ public static final String repaymentEveryParameterName = "repaymentEvery";
+ public static final String interestRatePerPeriodParameterName = "interestRatePerPeriod";
+ public static final String inArrearsToleranceParameterName = "inArrearsTolerance";
+ public static final String interestChargedFromDateParameterName = "interestChargedFromDate";
+ public static final String submittedOnDateParameterName = "submittedOnDate";
+ public static final String submittedOnNoteParameterName = "interestChargedFromDate";
+ public static final String collateralParameterName = "collateral";
+ public static final String syncDisbursementWithMeetingParameterName = "syncDisbursementWithMeeting";
+ public static final String linkAccountIdParameterName = "linkAccountId";
+ public static final String createStandingInstructionAtDisbursementParameterName = "createStandingInstructionAtDisbursement";
public static final String daysInYearTypeParameterName = "daysInYearType";
public static final String daysInMonthTypeParameterName = "daysInMonthType";
+
+ // Interest recalculation related
+ public static final String isInterestRecalculationEnabledParameterName = "isInterestRecalculationEnabled";
public static final String interestRecalculationCompoundingMethodParameterName = "interestRecalculationCompoundingMethod";
public static final String rescheduleStrategyMethodParameterName = "rescheduleStrategyMethod";
+ public static final String repaymentFrequencyNthDayTypeParameterName = "repaymentFrequencyNthDayType";
+ public static final String repaymentFrequencyDayOfWeekTypeParameterName = "repaymentFrequencyDayOfWeekType";
// Floating interest rate related
public static final String interestRateDifferentialParameterName = "interestRateDifferential";
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
index 929a5ae..5e71e03 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResource.java
@@ -372,6 +372,17 @@ public class LoansApiResource {
loanBasicDetails = LoanAccountData.withInterestRecalculationCalendarData(loanBasicDetails, calendarData,
compoundingCalendarData);
}
+ if (loanBasicDetails.isMonthlyRepaymentFrequencyType()) {
+ Collection<CalendarData> loanCalendarDatas = this.calendarReadPlatformService
+ .retrieveCalendarsByEntity(loanId,
+ CalendarEntityType.LOANS.getValue(), null);
+ CalendarData calendarData = null;
+ if (!CollectionUtils.isEmpty(loanCalendarDatas)) {
+ calendarData = loanCalendarDatas.iterator().next();
+ }
+ if(calendarData != null)
+ loanBasicDetails = LoanAccountData.withLoanCalendarData(loanBasicDetails, calendarData);
+ }
Collection<InterestRatePeriodData> interestRatesPeriods = this.loanReadPlatformService.retrieveLoanInterestRatePeriodData(loanId);
@@ -484,6 +495,8 @@ public class LoansApiResource {
LoanProductData product = null;
Collection<EnumOptionData> loanTermFrequencyTypeOptions = null;
Collection<EnumOptionData> repaymentFrequencyTypeOptions = null;
+ Collection<EnumOptionData> repaymentFrequencyNthDayTypeOptions = null;
+ Collection<EnumOptionData> repaymentFrequencyDayOfWeekTypeOptions = null;
Collection<TransactionProcessingStrategyData> repaymentStrategyOptions = null;
Collection<EnumOptionData> interestRateFrequencyTypeOptions = null;
Collection<EnumOptionData> amortizationTypeOptions = null;
@@ -506,6 +519,8 @@ public class LoansApiResource {
loanBasicDetails.setProduct(product);
loanTermFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveLoanTermFrequencyTypeOptions();
repaymentFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyTypeOptions();
+ repaymentFrequencyNthDayTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyOptionsForNthDayOfMonth();
+ repaymentFrequencyDayOfWeekTypeOptions = this.dropdownReadPlatformService.retrieveRepaymentFrequencyOptionsForDaysOfWeek();
interestRateFrequencyTypeOptions = this.dropdownReadPlatformService.retrieveInterestRateFrequencyTypeOptions();
amortizationTypeOptions = this.dropdownReadPlatformService.retrieveLoanAmortizationTypeOptions();
@@ -559,9 +574,10 @@ public class LoansApiResource {
final LoanAccountData loanAccount = LoanAccountData.associationsAndTemplate(loanBasicDetails, repaymentSchedule, loanRepayments,
charges, collateral, guarantors, meeting, productOptions, loanTermFrequencyTypeOptions, repaymentFrequencyTypeOptions,
- null, null, repaymentStrategyOptions, interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions,
- interestCalculationPeriodTypeOptions, fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions,
- loanCollateralOptions, calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
+ repaymentFrequencyNthDayTypeOptions, repaymentFrequencyDayOfWeekTypeOptions, repaymentStrategyOptions,
+ interestRateFrequencyTypeOptions, amortizationTypeOptions, interestTypeOptions, interestCalculationPeriodTypeOptions,
+ fundOptions, chargeOptions, chargeTemplate, allowedLoanOfficers, loanPurposeOptions, loanCollateralOptions,
+ calendarOptions, notes, accountLinkingOptions, linkedAccount, disbursementData, emiAmountVariations,
overdueCharges, paidInAdvanceTemplate, interestRatesPeriods);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters(),
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
index cc7fc87..5975ec3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java
@@ -34,6 +34,7 @@ import org.apache.fineract.portfolio.account.data.PortfolioAccountData;
import org.apache.fineract.portfolio.calendar.data.CalendarData;
import org.apache.fineract.portfolio.charge.data.ChargeData;
import org.apache.fineract.portfolio.collateral.data.CollateralData;
+import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.floatingrates.data.InterestRatePeriodData;
import org.apache.fineract.portfolio.fund.data.FundData;
import org.apache.fineract.portfolio.group.data.GroupGeneralData;
@@ -1143,6 +1144,33 @@ public class LoanAccountData {
acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
}
+ public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) {
+ return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientAccountNo, acc.clientName,
+ acc.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, calendarData.getRepeatsOnNthDayOfMonth(), calendarData.getRepeatsOnDay(),
+ acc.transactionProcessingStrategyId, acc.transactionProcessingStrategyName, acc.amortizationType,
+ acc.interestRatePerPeriod, acc.interestRateFrequencyType, acc.annualInterestRate, acc.interestType,
+ acc.isFloatingInterestRate, acc.interestRateDifferential, acc.interestCalculationPeriodType,
+ acc.allowPartialPeriodInterestCalcualtion, acc.expectedFirstRepaymentOnDate, acc.graceOnPrincipalPayment,
+ acc.recurringMoratoriumOnPrincipalPeriods, 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, acc.repaymentFrequencyTypeOptions, acc.repaymentFrequencyNthDayTypeOptions,
+ acc.repaymentFrequencyDaysOfWeekTypeOptions, acc.transactionProcessingStrategyOptions,
+ acc.interestRateFrequencyTypeOptions, acc.amortizationTypeOptions, acc.interestTypeOptions,
+ acc.interestCalculationPeriodTypeOptions, acc.fundOptions, acc.chargeOptions, null, acc.loanOfficerOptions,
+ acc.loanPurposeOptions, acc.loanCollateralOptions, acc.calendarOptions, acc.syncDisbursementWithMeeting, acc.loanCounter,
+ acc.loanProductCounter, acc.notes, acc.accountLinkingOptions, acc.linkedAccount, acc.disbursementDetails,
+ acc.multiDisburseLoan, acc.canDefineInstallmentAmount, acc.fixedEmiAmount, 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.interestRatesPeriods,
+ acc.isVariableInstallmentsAllowed, acc.minimumGap, acc.maximumGap);
+ }
+
public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) {
return new LoanAccountData(acc.id, acc.accountNo, acc.status, acc.externalId, acc.clientId, acc.clientAccountNo, acc.clientName,
@@ -1518,4 +1546,8 @@ public class LoanAccountData {
return BigDecimal.ZERO;
}
+ public boolean isMonthlyRepaymentFrequencyType() {
+ return (this.repaymentFrequencyType.getId().intValue() == PeriodFrequencyType.MONTHS.getValue());
+ }
+
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanInterestRecalculationData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanInterestRecalculationData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanInterestRecalculationData.java
index 4b432e6..3cc4541 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanInterestRecalculationData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanInterestRecalculationData.java
@@ -20,7 +20,6 @@ package org.apache.fineract.portfolio.loanaccount.data;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.portfolio.calendar.data.CalendarData;
-import org.joda.time.LocalDate;
public class LoanInterestRecalculationData {
@@ -32,19 +31,29 @@ public class LoanInterestRecalculationData {
private final CalendarData calendarData;
private final EnumOptionData recalculationRestFrequencyType;
private final Integer recalculationRestFrequencyInterval;
- private final LocalDate recalculationRestFrequencyDate;
+ /* private final LocalDate recalculationRestFrequencyDate; */
+ private final EnumOptionData recalculationRestFrequencyNthDay;
+ private final EnumOptionData recalculationRestFrequencyWeekday;
+ private final Integer recalculationRestFrequencyOnDay;
private final EnumOptionData recalculationCompoundingFrequencyType;
private final Integer recalculationCompoundingFrequencyInterval;
- private final LocalDate recalculationCompoundingFrequencyDate;
+ /* private final LocalDate recalculationCompoundingFrequencyDate; */
+ private final EnumOptionData recalculationCompoundingFrequencyNthDay;
+ private final EnumOptionData recalculationCompoundingFrequencyWeekday;
+ private final Integer recalculationCompoundingFrequencyOnDay;
+ private final Boolean isCompoundingToBePostedAsTransaction;
@SuppressWarnings("unused")
private final CalendarData compoundingCalendarData;
+ private final Boolean allowCompoundingOnEod;
public LoanInterestRecalculationData(final Long id, final Long loanId, final EnumOptionData interestRecalculationCompoundingType,
final EnumOptionData rescheduleStrategyType, final CalendarData calendarData,
final EnumOptionData recalculationRestFrequencyType, final Integer recalculationRestFrequencyInterval,
- final LocalDate recalculationRestFrequencyDate, final CalendarData compoundingCalendarData,
+ final EnumOptionData recalculationRestFrequencyNthDay, final EnumOptionData recalculationRestFrequencyWeekday,
+ final Integer recalculationRestFrequencyOnDay, final CalendarData compoundingCalendarData,
final EnumOptionData recalculationCompoundingFrequencyType, final Integer recalculationCompoundingFrequencyInterval,
- final LocalDate recalculationCompoundingFrequencyDate) {
+ final EnumOptionData recalculationCompoundingFrequencyNthDay, final EnumOptionData recalculationCompoundingFrequencyWeekday,
+ final Integer recalculationCompoundingFrequencyOnDay, final Boolean isCompoundingToBePostedAsTransaction, final Boolean allowCompoundingOnEod) {
this.id = id;
this.loanId = loanId;
this.interestRecalculationCompoundingType = interestRecalculationCompoundingType;
@@ -52,11 +61,17 @@ public class LoanInterestRecalculationData {
this.calendarData = calendarData;
this.recalculationRestFrequencyType = recalculationRestFrequencyType;
this.recalculationRestFrequencyInterval = recalculationRestFrequencyInterval;
- this.recalculationRestFrequencyDate = recalculationRestFrequencyDate;
+ this.recalculationRestFrequencyNthDay = recalculationRestFrequencyNthDay;
+ this.recalculationRestFrequencyWeekday = recalculationRestFrequencyWeekday;
+ this.recalculationRestFrequencyOnDay = recalculationRestFrequencyOnDay;
this.recalculationCompoundingFrequencyType = recalculationCompoundingFrequencyType;
this.recalculationCompoundingFrequencyInterval = recalculationCompoundingFrequencyInterval;
- this.recalculationCompoundingFrequencyDate = recalculationCompoundingFrequencyDate;
+ this.recalculationCompoundingFrequencyNthDay = recalculationCompoundingFrequencyNthDay;
+ this.recalculationCompoundingFrequencyWeekday = recalculationCompoundingFrequencyWeekday;
+ this.recalculationCompoundingFrequencyOnDay = recalculationCompoundingFrequencyOnDay;
this.compoundingCalendarData = compoundingCalendarData;
+ this.isCompoundingToBePostedAsTransaction = isCompoundingToBePostedAsTransaction;
+ this.allowCompoundingOnEod = allowCompoundingOnEod;
}
public static LoanInterestRecalculationData withCalendarData(final LoanInterestRecalculationData recalculationData,
@@ -64,9 +79,12 @@ public class LoanInterestRecalculationData {
return new LoanInterestRecalculationData(recalculationData.id, recalculationData.loanId,
recalculationData.interestRecalculationCompoundingType, recalculationData.rescheduleStrategyType, calendarData,
recalculationData.recalculationRestFrequencyType, recalculationData.recalculationRestFrequencyInterval,
- recalculationData.recalculationRestFrequencyDate, compoundingCalendarData,
+ recalculationData.recalculationRestFrequencyNthDay, recalculationData.recalculationRestFrequencyWeekday,
+ recalculationData.recalculationRestFrequencyOnDay, compoundingCalendarData,
recalculationData.recalculationCompoundingFrequencyType, recalculationData.recalculationCompoundingFrequencyInterval,
- recalculationData.recalculationCompoundingFrequencyDate);
+ recalculationData.recalculationCompoundingFrequencyNthDay, recalculationData.recalculationCompoundingFrequencyWeekday,
+ recalculationData.recalculationCompoundingFrequencyOnDay, recalculationData.isCompoundingToBePostedAsTransaction,
+ recalculationData.allowCompoundingOnEod);
}
public Long getId() {
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 13fd9ce..822b575 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -24,6 +24,7 @@ import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -76,6 +77,7 @@ import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
import org.apache.fineract.portfolio.calendar.domain.CalendarInstance;
+import org.apache.fineract.portfolio.calendar.domain.CalendarWeekDaysType;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.charge.domain.Charge;
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
@@ -115,7 +117,6 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanApplica
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModel;
import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleModelPeriod;
-import org.apache.fineract.portfolio.loanproduct.LoanProductConstants;
import org.apache.fineract.portfolio.loanproduct.domain.AmortizationMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestCalculationPeriodMethod;
import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
@@ -192,12 +193,6 @@ public class Loan extends AbstractPersistable<Long> {
@Embedded
private LoanProductRelatedDetail loanRepaymentScheduleDetail;
- @Column(name = "repayment_frequency_nth_day_enum", nullable = true)
- private Integer repaymentFrequencyNthDayType;
-
- @Column(name = "repayment_frequency_day_of_week_enum", nullable = true)
- private Integer repaymentFrequencyDayOfWeekType;
-
@Column(name = "term_frequency", nullable = false)
private Integer termFrequency;
@@ -858,7 +853,7 @@ public class Loan extends AbstractPersistable<Long> {
if (loanCharge.isOverdueInstallmentCharge()) { return loanCharge.getAmountPercentageAppliedTo(); }
switch (loanCharge.getChargeCalculation()) {
case PERCENT_OF_AMOUNT:
- amount = getDerivedAmountForCharge(loanCharge);
+ amount = getDerivedAmountForCharge(loanCharge);
break;
case PERCENT_OF_AMOUNT_AND_INTEREST:
final BigDecimal totalInterestCharged = getTotalInterest();
@@ -1135,7 +1130,8 @@ public class Loan extends AbstractPersistable<Long> {
scheduledLoanInstallment.periodNumber(), scheduledLoanInstallment.periodFromDate(),
scheduledLoanInstallment.periodDueDate(), scheduledLoanInstallment.principalDue(),
scheduledLoanInstallment.interestDue(), scheduledLoanInstallment.feeChargesDue(),
- scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent());
+ scheduledLoanInstallment.penaltyChargesDue(), scheduledLoanInstallment.isRecalculatedInterestComponent(),
+ scheduledLoanInstallment.getLoanCompoundingDetails());
addRepaymentScheduleInstallment(installment);
}
}
@@ -1194,6 +1190,13 @@ public class Loan extends AbstractPersistable<Long> {
}
installment.updateAccrualPortion(interest, fee, penality);
}
+ LoanRepaymentScheduleInstallment lastInstallment = this.repaymentScheduleInstallments
+ .get(this.repaymentScheduleInstallments.size() - 1);
+ for (LoanTransaction loanTransaction : accruals) {
+ if (loanTransaction.getTransactionDate().isAfter(lastInstallment.getDueDate()) && !loanTransaction.isReversed()) {
+ loanTransaction.reverse();
+ }
+ }
}
private void updateAccrualsForNonPeriodicAccruals(final Collection<LoanTransaction> accruals, final AppUser currentUser) {
@@ -1265,12 +1268,6 @@ public class Loan extends AbstractPersistable<Long> {
final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale();
- final LocalDate recalculationRestFrequencyDate = command
- .localDateValueOfParameterNamed(LoanProductConstants.recalculationRestFrequencyDateParamName);
- final LocalDate recalculationCompoundingFrequencyDate = command
- .localDateValueOfParameterNamed(LoanProductConstants.recalculationCompoundingFrequencyDateParamName);
- updateLoanInterestRecalculationSettings(recalculationRestFrequencyDate, recalculationCompoundingFrequencyDate, command,
- actualChanges);
final String accountNoParamName = "accountNo";
if (command.isChangeInStringParameterNamed(accountNoParamName, this.accountNumber)) {
@@ -1556,37 +1553,6 @@ public class Loan extends AbstractPersistable<Long> {
/**
* Update interest recalculation settings if product configuration changes
*/
- public void updateLoanInterestRecalculationSettings(final LocalDate recalculationRestFrequencyDate,
- final LocalDate recalculationCompoundingFrequencyDate, final JsonCommand command, final Map<String, Object> actualChanges) {
-
- if (isInterestRecalculationEnabledForProduct()) {
- Date restFrequencyDate = null;
- if (recalculationRestFrequencyDate != null) {
- restFrequencyDate = recalculationRestFrequencyDate.toDate();
- }
-
- Date compoundingFrequencyDate = null;
- if (recalculationCompoundingFrequencyDate != null) {
- compoundingFrequencyDate = recalculationCompoundingFrequencyDate.toDate();
- }
- if (this.loanInterestRecalculationDetails == null) {
- actualChanges.put(LoanProductConstants.isInterestRecalculationEnabledParameterName, true);
- this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom(this.loanProduct
- .getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), this.loanProduct
- .getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), this.loanProduct
- .getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), this.loanProduct
- .getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate, this.loanProduct
- .getProductInterestRecalculationDetails().getCompoundingFrequencyType().getValue(), this.loanProduct
- .getProductInterestRecalculationDetails().getCompoundingInterval(), compoundingFrequencyDate);
- this.loanInterestRecalculationDetails.updateLoan(this);
- } else {
-
- this.loanInterestRecalculationDetails.update(command, actualChanges);
- }
- } else {
- this.loanInterestRecalculationDetails = null;
- }
- }
private void updateOverdueScheduleInstallment(final LoanCharge loanCharge) {
if (loanCharge.isOverdueInstallmentCharge() && loanCharge.isActive()) {
@@ -1911,8 +1877,7 @@ public class Loan extends AbstractPersistable<Long> {
public void loanApplicationSubmittal(final AppUser currentUser, final LoanScheduleModel loanSchedule,
final LoanApplicationTerms loanApplicationTerms, final LoanLifecycleStateMachine lifecycleStateMachine,
final LocalDate submittedOn, final String externalId, final boolean allowTransactionsOnHoliday, final List<Holiday> holidays,
- final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay, final LocalDate recalculationRestFrequencyDate,
- final LocalDate recalculationCompoundingFrequencyDate) {
+ final WorkingDays workingDays, final boolean allowTransactionsOnNonWorkingDay) {
updateLoanSchedule(loanSchedule, currentUser);
@@ -1933,14 +1898,6 @@ public class Loan extends AbstractPersistable<Long> {
this.expectedFirstRepaymentOnDate = loanApplicationTerms.getRepaymentStartFromDate();
this.interestChargedFromDate = loanApplicationTerms.getInterestChargedFromDate();
- if (loanApplicationTerms.getRepaymentPeriodFrequencyType() == PeriodFrequencyType.MONTHS) {
- this.repaymentFrequencyNthDayType = loanApplicationTerms.getNthDay();
- this.repaymentFrequencyDayOfWeekType = loanApplicationTerms.getWeekDayType().getValue();
- } else {
- this.repaymentFrequencyNthDayType = NthDayType.INVALID.getValue();
- this.repaymentFrequencyDayOfWeekType = DayOfWeekType.INVALID.getValue();
- }
-
updateLoanScheduleDependentDerivedFields();
if (submittedOn.isAfter(DateUtils.getLocalDateOfTenant())) {
@@ -1984,22 +1941,9 @@ public class Loan extends AbstractPersistable<Long> {
* enabled
*/
if (this.loanRepaymentScheduleDetail.isInterestRecalculationEnabled()) {
- Date restFrequencyDate = null;
- if (recalculationRestFrequencyDate != null) {
- restFrequencyDate = recalculationRestFrequencyDate.toDate();
- }
- Date compoundingFrequencyDate = null;
- if (recalculationCompoundingFrequencyDate != null) {
- compoundingFrequencyDate = recalculationCompoundingFrequencyDate.toDate();
- }
this.loanInterestRecalculationDetails = LoanInterestRecalculationDetails.createFrom(this.loanProduct
- .getProductInterestRecalculationDetails().getInterestRecalculationCompoundingMethod(), this.loanProduct
- .getProductInterestRecalculationDetails().getRescheduleStrategyMethod(), this.loanProduct
- .getProductInterestRecalculationDetails().getRestFrequencyType().getValue(), this.loanProduct
- .getProductInterestRecalculationDetails().getRestInterval(), restFrequencyDate, this.loanProduct
- .getProductInterestRecalculationDetails().getCompoundingFrequencyType().getValue(), this.loanProduct
- .getProductInterestRecalculationDetails().getCompoundingInterval(), compoundingFrequencyDate);
+ .getProductInterestRecalculationDetails());
this.loanInterestRecalculationDetails.updateLoan(this);
}
@@ -2299,7 +2243,7 @@ public class Loan extends AbstractPersistable<Long> {
}
public ChangedTransactionDetail disburse(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges,
- final ScheduleGeneratorDTO scheduleGeneratorDTO,final PaymentDetail paymentDetail) {
+ final ScheduleGeneratorDTO scheduleGeneratorDTO, final PaymentDetail paymentDetail) {
final LoanStatus statusEnum = this.loanLifecycleStateMachine.transition(LoanEvent.LOAN_DISBURSED,
LoanStatus.fromInt(this.loanStatus));
@@ -2330,7 +2274,7 @@ public class Loan extends AbstractPersistable<Long> {
updateSummaryWithTotalFeeChargesDueAtDisbursement(deriveSumTotalOfChargesDueAtDisbursement());
updateLoanRepaymentPeriodsDerivedFields(actualDisbursementDate);
LocalDateTime createdDate = DateUtils.getLocalDateTimeOfTenant();
- handleDisbursementTransaction(actualDisbursementDate, createdDate, currentUser,paymentDetail);
+ handleDisbursementTransaction(actualDisbursementDate, createdDate, currentUser, paymentDetail);
updateLoanSummaryDerivedFields();
final Money interestApplied = Money.of(getCurrency(), this.summary.getTotalInterestCharged());
@@ -2380,8 +2324,13 @@ public class Loan extends AbstractPersistable<Long> {
this.loanTermVariations.add(loanVariationTerms);
}
- if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged || rescheduledRepaymentDate != null) {
- regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
+ if (isRepaymentScheduleRegenerationRequiredForDisbursement(actualDisbursementDate) || recalculateSchedule || isEmiAmountChanged
+ || rescheduledRepaymentDate != null) {
+ if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
+ regenerateRepaymentScheduleWithInterestRecalculation(scheduleGeneratorDTO, currentUser);
+ } else {
+ regenerateRepaymentSchedule(scheduleGeneratorDTO, currentUser);
+ }
}
}
@@ -2592,7 +2541,7 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanSchedule(loanSchedule, currentUser);
Set<LoanCharge> charges = this.charges();
for (LoanCharge loanCharge : charges) {
- recalculateLoanCharge(loanCharge, scheduleGeneratorDTO.getPenaltyWaitPeriod());
+ recalculateLoanCharge(loanCharge, scheduleGeneratorDTO.getPenaltyWaitPeriod());
}
}
@@ -2633,7 +2582,8 @@ public class Loan extends AbstractPersistable<Long> {
return interestRate;
}
- private void handleDisbursementTransaction(final LocalDate disbursedOn, final LocalDateTime createdDate, final AppUser currentUser, final PaymentDetail paymentDetail) {
+ private void handleDisbursementTransaction(final LocalDate disbursedOn, final LocalDateTime createdDate, final AppUser currentUser,
+ final PaymentDetail paymentDetail) {
// add repayment transaction to track incoming money from client to mfi
// for (charges due at time of disbursement)
@@ -2651,14 +2601,14 @@ public class Loan extends AbstractPersistable<Long> {
**/
Money disbursentMoney = Money.zero(getCurrency());
- final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, paymentDetail, disbursedOn,
- null, createdDate, currentUser);
+ final LoanTransaction chargesPayment = LoanTransaction.repaymentAtDisbursement(getOffice(), disbursentMoney, paymentDetail,
+ disbursedOn, null, createdDate, currentUser);
final Integer installmentNumber = null;
for (final LoanCharge charge : charges()) {
Date actualDisbursementDate = getActualDisbursementDate(charge);
- if ((charge.getCharge().getChargeTimeType() == ChargeTimeType.DISBURSEMENT.getValue()
- && disbursedOn.equals(new LocalDate(actualDisbursementDate)) && actualDisbursementDate != null
- && !charge.isWaived() && !charge.isFullyPaid())
+ if ((charge.getCharge().getChargeTimeType() == ChargeTimeType.DISBURSEMENT.getValue()
+ && disbursedOn.equals(new LocalDate(actualDisbursementDate)) && actualDisbursementDate != null && !charge.isWaived() && !charge
+ .isFullyPaid())
|| (charge.getCharge().getChargeTimeType() == ChargeTimeType.TRANCHE_DISBURSEMENT.getValue()
&& disbursedOn.equals(new LocalDate(actualDisbursementDate)) && actualDisbursementDate != null
&& !charge.isWaived() && !charge.isFullyPaid())) {
@@ -3088,6 +3038,18 @@ public class Loan extends AbstractPersistable<Long> {
return installment;
}
+ private List<LoanTransaction> retreiveListOfIncomePostingTransactions() {
+ final List<LoanTransaction> incomePostTransactions = new ArrayList<>();
+ for (final LoanTransaction transaction : this.loanTransactions) {
+ if (transaction.isNotReversed() && transaction.isIncomePosting()) {
+ incomePostTransactions.add(transaction);
+ }
+ }
+ final LoanTransactionComparator transactionComparator = new LoanTransactionComparator();
+ Collections.sort(incomePostTransactions, transactionComparator);
+ return incomePostTransactions;
+ }
+
private List<LoanTransaction> retreiveListOfTransactionsPostDisbursement() {
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
for (final LoanTransaction transaction : this.loanTransactions) {
@@ -3104,8 +3066,8 @@ public class Loan extends AbstractPersistable<Long> {
final List<LoanTransaction> repaymentsOrWaivers = new ArrayList<>();
for (final LoanTransaction transaction : this.loanTransactions) {
if (transaction.isNotReversed()
- && !(transaction.isDisbursement() || transaction.isAccrual() || transaction.isRepaymentAtDisbursement() || transaction
- .isNonMonetaryTransaction())) {
+ && !(transaction.isDisbursement() || transaction.isAccrual() || transaction.isRepaymentAtDisbursement()
+ || transaction.isNonMonetaryTransaction() || transaction.isIncomePosting())) {
repaymentsOrWaivers.add(transaction);
}
}
@@ -3169,6 +3131,71 @@ public class Loan extends AbstractPersistable<Long> {
LoanStatus.fromInt(this.loanStatus));
this.loanStatus = statusEnum.getValue();
}
+ processIncomeAccrualTransactionOnLoanClosure();
+ }
+
+ private void processIncomeAccrualTransactionOnLoanClosure() {
+ if (this.loanInterestRecalculationDetails != null && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
+ && this.status().isClosedObligationsMet()) {
+ Date closedDate = this.getClosedOnDate();
+ LocalDate closedLocalDate = new LocalDate(closedDate);
+ reverseTransactionsOnOrAfter(retreiveListOfIncomePostingTransactions(), closedDate);
+ reverseTransactionsOnOrAfter(retreiveListOfAccrualTransactions(), closedDate);
+ HashMap<String, BigDecimal> cumulativeIncomeFromInstallments = new HashMap<>();
+ determineCumulativeIncomeFromInstallments(cumulativeIncomeFromInstallments);
+ HashMap<String, BigDecimal> cumulativeIncomeFromIncomePosting = new HashMap<>();
+ determineCumulativeIncomeDetails(retreiveListOfIncomePostingTransactions(), cumulativeIncomeFromIncomePosting);
+ BigDecimal interestToPost = cumulativeIncomeFromInstallments.get("interest").subtract(
+ cumulativeIncomeFromIncomePosting.get("interest"));
+ BigDecimal feeToPost = cumulativeIncomeFromInstallments.get("fee").subtract(cumulativeIncomeFromIncomePosting.get("fee"));
+ BigDecimal penaltyToPost = cumulativeIncomeFromInstallments.get("penalty").subtract(
+ cumulativeIncomeFromIncomePosting.get("penalty"));
+ BigDecimal amountToPost = interestToPost.add(feeToPost).add(penaltyToPost);
+ LoanTransaction finalIncomeTransaction = LoanTransaction.incomePosting(this, this.getOffice(), closedDate, amountToPost,
+ interestToPost, feeToPost, penaltyToPost, null);
+ this.loanTransactions.add(finalIncomeTransaction);
+ if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
+ List<LoanTransaction> updatedAccrualTransactions = retreiveListOfAccrualTransactions();
+ LocalDate lastAccruedDate = this.getDisbursementDate();
+ if (updatedAccrualTransactions != null && updatedAccrualTransactions.size() > 0) {
+ lastAccruedDate = updatedAccrualTransactions.get(updatedAccrualTransactions.size() - 1).getTransactionDate();
+ }
+ HashMap<String, Object> feeDetails = new HashMap<>();
+ determineFeeDetails(lastAccruedDate, closedLocalDate, feeDetails);
+ LoanTransaction finalAccrual = LoanTransaction.accrueTransaction(this, this.getOffice(), closedLocalDate, amountToPost,
+ interestToPost, feeToPost, penaltyToPost, null);
+ updateLoanChargesPaidBy(finalAccrual, feeDetails, null);
+ this.loanTransactions.add(finalAccrual);
+ }
+ }
+ }
+
+ private void determineCumulativeIncomeFromInstallments(HashMap<String, BigDecimal> cumulativeIncomeFromInstallments) {
+ BigDecimal interest = BigDecimal.ZERO;
+ BigDecimal fee = BigDecimal.ZERO;
+ BigDecimal penalty = BigDecimal.ZERO;
+ for (LoanRepaymentScheduleInstallment installment : this.repaymentScheduleInstallments) {
+ interest = interest.add(installment.getInterestCharged(getCurrency()).getAmount());
+ fee = fee.add(installment.getFeeChargesCharged(getCurrency()).getAmount());
+ penalty = penalty.add(installment.getPenaltyChargesCharged(getCurrency()).getAmount());
+ }
+ cumulativeIncomeFromInstallments.put("interest", interest);
+ cumulativeIncomeFromInstallments.put("fee", fee);
+ cumulativeIncomeFromInstallments.put("penalty", penalty);
+ }
+
+ private void determineCumulativeIncomeDetails(Collection<LoanTransaction> transactions, HashMap<String, BigDecimal> incomeDetailsMap) {
+ BigDecimal interest = BigDecimal.ZERO;
+ BigDecimal fee = BigDecimal.ZERO;
+ BigDecimal penalty = BigDecimal.ZERO;
+ for (LoanTransaction transaction : transactions) {
+ interest = interest.add(transaction.getInterestPortion(getCurrency()).getAmount());
+ fee = fee.add(transaction.getFeeChargesPortion(getCurrency()).getAmount());
+ penalty = penalty.add(transaction.getPenaltyChargesPortion(getCurrency()).getAmount());
+ }
+ incomeDetailsMap.put("interest", interest);
+ incomeDetailsMap.put("fee", fee);
+ incomeDetailsMap.put("penalty", penalty);
}
private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) {
@@ -4566,7 +4593,7 @@ public class Loan extends AbstractPersistable<Long> {
private LocalDate getLastUserTransactionDate() {
LocalDate currentTransactionDate = getDisbursementDate();
for (final LoanTransaction previousTransaction : this.loanTransactions) {
- if (!(previousTransaction.isReversed() || previousTransaction.isAccrual())) {
+ if (!(previousTransaction.isReversed() || previousTransaction.isAccrual() || previousTransaction.isIncomePosting())) {
if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())) {
currentTransactionDate = previousTransaction.getTransactionDate();
}
@@ -4982,6 +5009,179 @@ public class Loan extends AbstractPersistable<Long> {
}
processPostDisbursementTransactions();
+ processIncomeTransactions(currentUser);
+ }
+
+ private void updateLoanChargesPaidBy(LoanTransaction accrual, HashMap<String, Object> feeDetails,
+ LoanRepaymentScheduleInstallment installment) {
+ @SuppressWarnings("unchecked")
+ List<LoanCharge> loanCharges = (List<LoanCharge>) feeDetails.get("loanCharges");
+ @SuppressWarnings("unchecked")
+ List<LoanInstallmentCharge> loanInstallmentCharges = (List<LoanInstallmentCharge>) feeDetails.get("loanInstallmentCharges");
+ if (loanCharges != null) {
+ for (LoanCharge loanCharge : loanCharges) {
+ Integer installmentNumber = null == installment ? null : installment.getInstallmentNumber();
+ final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(accrual, loanCharge, loanCharge.getAmount(getCurrency())
+ .getAmount(), installmentNumber);
+ accrual.getLoanChargesPaid().add(loanChargePaidBy);
+ }
+ }
+ if (loanInstallmentCharges != null) {
+ for (LoanInstallmentCharge loanInstallmentCharge : loanInstallmentCharges) {
+ Integer installmentNumber = null == loanInstallmentCharge.getInstallment() ? null : loanInstallmentCharge.getInstallment()
+ .getInstallmentNumber();
+ final LoanChargePaidBy loanChargePaidBy = new LoanChargePaidBy(accrual, loanInstallmentCharge.getLoancharge(),
+ loanInstallmentCharge.getAmount(getCurrency()).getAmount(), installmentNumber);
+ accrual.getLoanChargesPaid().add(loanChargePaidBy);
+ }
+ }
+ }
+
+ public void processIncomeTransactions(AppUser currentUser) {
+ if (this.loanInterestRecalculationDetails != null && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()) {
+ LocalDate lastCompoundingDate = this.getDisbursementDate();
+ List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = extractInterestRecalculationAdditionalDetails();
+ List<LoanTransaction> incomeTransactions = retreiveListOfIncomePostingTransactions();
+ List<LoanTransaction> accrualTransactions = retreiveListOfAccrualTransactions();
+ for (LoanInterestRecalcualtionAdditionalDetails compoundingDetail : compoundingDetails) {
+ if (!compoundingDetail.getEffectiveDate().isBefore(DateUtils.getLocalDateOfTenant())) {
+ break;
+ }
+ LoanTransaction incomeTransaction = getTransactionForDate(incomeTransactions, compoundingDetail.getEffectiveDate());
+ LoanTransaction accrualTransaction = getTransactionForDate(accrualTransactions, compoundingDetail.getEffectiveDate());
+ addUpdateIncomeAndAccrualTransaction(compoundingDetail, lastCompoundingDate, currentUser, incomeTransaction,
+ accrualTransaction);
+ lastCompoundingDate = compoundingDetail.getEffectiveDate();
+ }
+ }
+ }
+
+ private void reverseTransactionsOnOrAfter(List<LoanTransaction> transactions, Date date) {
+ LocalDate refDate = new LocalDate(date);
+ for (LoanTransaction loanTransaction : transactions) {
+ if (!loanTransaction.getTransactionDate().isBefore(refDate)) {
+ loanTransaction.reverse();
+ }
+ }
+ }
+
+ private void addUpdateIncomeAndAccrualTransaction(LoanInterestRecalcualtionAdditionalDetails compoundingDetail,
+ LocalDate lastCompoundingDate, AppUser currentUser, LoanTransaction existingIncomeTransaction,
+ LoanTransaction existingAccrualTransaction) {
+ BigDecimal interest = BigDecimal.ZERO;
+ BigDecimal fee = BigDecimal.ZERO;
+ BigDecimal penalties = BigDecimal.ZERO;
+ HashMap<String, Object> feeDetails = new HashMap<>();
+
+ if (this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod().equals(
+ InterestRecalculationCompoundingMethod.INTEREST)) {
+ interest = compoundingDetail.getAmount();
+ } else if (this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod().equals(
+ InterestRecalculationCompoundingMethod.FEE)) {
+ determineFeeDetails(lastCompoundingDate, compoundingDetail.getEffectiveDate(), feeDetails);
+ fee = (BigDecimal) feeDetails.get("fee");
+ penalties = (BigDecimal) feeDetails.get("penalties");
+ } else if (this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod().equals(
+ InterestRecalculationCompoundingMethod.INTEREST_AND_FEE)) {
+ determineFeeDetails(lastCompoundingDate, compoundingDetail.getEffectiveDate(), feeDetails);
+ fee = (BigDecimal) feeDetails.get("fee");
+ penalties = (BigDecimal) feeDetails.get("penalties");
+ interest = compoundingDetail.getAmount().subtract(fee).subtract(penalties);
+ }
+
+ if (existingIncomeTransaction == null) {
+ LoanTransaction transaction = LoanTransaction.incomePosting(this, this.getOffice(), compoundingDetail.getEffectiveDate()
+ .toDate(), compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
+ this.loanTransactions.add(transaction);
+ } else if (existingIncomeTransaction.getAmount(getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
+ existingIncomeTransaction.reverse();
+ LoanTransaction transaction = LoanTransaction.incomePosting(this, this.getOffice(), compoundingDetail.getEffectiveDate()
+ .toDate(), compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
+ this.loanTransactions.add(transaction);
+ }
+
+ if (isPeriodicAccrualAccountingEnabledOnLoanProduct()) {
+ if (existingAccrualTransaction == null) {
+ LoanTransaction accrual = LoanTransaction.accrueTransaction(this, this.getOffice(), compoundingDetail.getEffectiveDate(),
+ compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
+ updateLoanChargesPaidBy(accrual, feeDetails, null);
+ this.loanTransactions.add(accrual);
+ } else if (existingAccrualTransaction.getAmount(getCurrency()).getAmount().compareTo(compoundingDetail.getAmount()) != 0) {
+ existingAccrualTransaction.reverse();
+ LoanTransaction accrual = LoanTransaction.accrueTransaction(this, this.getOffice(), compoundingDetail.getEffectiveDate(),
+ compoundingDetail.getAmount(), interest, fee, penalties, currentUser);
+ updateLoanChargesPaidBy(accrual, feeDetails, null);
+ this.loanTransactions.add(accrual);
+ }
+ }
+ updateLoanOutstandingBalaces();
+ }
+
+ private void determineFeeDetails(LocalDate fromDate, LocalDate toDate, HashMap<String, Object> feeDetails) {
+ BigDecimal fee = BigDecimal.ZERO;
+ BigDecimal penalties = BigDecimal.ZERO;
+
+ List<Integer> installments = new ArrayList<>();
+ for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) {
+ if (loanRepaymentScheduleInstallment.getDueDate().isAfter(fromDate)
+ && !loanRepaymentScheduleInstallment.getDueDate().isAfter(toDate)) {
+ installments.add(loanRepaymentScheduleInstallment.getInstallmentNumber());
+ }
+ }
+
+ List<LoanCharge> loanCharges = new ArrayList<>();
+ List<LoanInstallmentCharge> loanInstallmentCharges = new ArrayList<>();
+ for (LoanCharge loanCharge : this.charges()) {
+ if (loanCharge.isDueForCollectionFromAndUpToAndIncluding(fromDate, toDate)) {
+ if (loanCharge.isPenaltyCharge() && !loanCharge.isInstalmentFee()) {
+ penalties = penalties.add(loanCharge.amount());
+ loanCharges.add(loanCharge);
+ } else if (!loanCharge.isInstalmentFee()) {
+ fee = fee.add(loanCharge.amount());
+ loanCharges.add(loanCharge);
+ }
+ } else if (loanCharge.isInstalmentFee()) {
+ for (LoanInstallmentCharge installmentCharge : loanCharge.installmentCharges()) {
+ if (installments.contains(installmentCharge.getRepaymentInstallment().getInstallmentNumber())) {
+ fee = fee.add(installmentCharge.getAmount());
+ loanInstallmentCharges.add(installmentCharge);
+ }
+ }
+ }
+ }
+
+ feeDetails.put("fee", fee);
+ feeDetails.put("penalties", penalties);
+ feeDetails.put("loanCharges", loanCharges);
+ feeDetails.put("loanInstallmentCharges", loanInstallmentCharges);
+ }
+
+ private LoanTransaction getTransactionForDate(List<LoanTransaction> transactions, LocalDate effectiveDate) {
+ for (LoanTransaction loanTransaction : transactions) {
+ if (loanTransaction.getTransactionDate().isEqual(effectiveDate)) { return loanTransaction; }
+ }
+ return null;
+ }
+
+ private List<LoanInterestRecalcualtionAdditionalDetails> extractInterestRecalculationAdditionalDetails() {
+ List<LoanInterestRecalcualtionAdditionalDetails> retDetails = new ArrayList<>();
+ if (null != this.repaymentScheduleInstallments && this.repaymentScheduleInstallments.size() > 0) {
+ Iterator<LoanRepaymentScheduleInstallment> installmentsItr = this.repaymentScheduleInstallments.iterator();
+ while (installmentsItr.hasNext()) {
+ LoanRepaymentScheduleInstallment installment = installmentsItr.next();
+ if (null != installment.getLoanCompoundingDetails()) {
+ retDetails.addAll(installment.getLoanCompoundingDetails());
+ }
+ }
+ }
+ Collections.sort(retDetails, new Comparator<LoanInterestRecalcualtionAdditionalDetails>() {
+
+ @Override
+ public int compare(LoanInterestRecalcualtionAdditionalDetails first, LoanInterestRecalcualtionAdditionalDetails second) {
+ return first.getEffectiveDate().compareTo(second.getEffectiveDate());
+ }
+ });
+ return retDetails;
}
public void processPostDisbursementTransactions() {
@@ -5044,21 +5244,27 @@ public class Loan extends AbstractPersistable<Long> {
public LoanApplicationTerms constructLoanApplicationTerms(final ScheduleGeneratorDTO scheduleGeneratorDTO) {
final Integer loanTermFrequency = this.termFrequency;
final PeriodFrequencyType loanTermPeriodFrequencyType = PeriodFrequencyType.fromInt(this.termPeriodFrequencyType);
- final NthDayType nthDayType = NthDayType.fromInt(this.repaymentFrequencyNthDayType);
- final DayOfWeekType dayOfWeekType = DayOfWeekType.fromInt(this.repaymentFrequencyDayOfWeekType);
+ NthDayType nthDayType = null;
+ DayOfWeekType dayOfWeekType = null;
final List<DisbursementData> disbursementData = new ArrayList<>();
for (LoanDisbursementDetails disbursementDetails : this.disbursementDetails) {
disbursementData.add(disbursementDetails.toData());
}
+ Calendar calendar = scheduleGeneratorDTO.getCalendar();
+ if (calendar != null) {
+ nthDayType = CalendarUtils.getRepeatsOnNthDayOfMonth(calendar.getRecurrence());
+ dayOfWeekType = DayOfWeekType.fromInt(CalendarUtils.getRepeatsOnDay(calendar.getRecurrence()).getValue());
+ }
+ HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
CalendarInstance restCalendarInstance = null;
CalendarInstance compoundingCalendarInstance = null;
RecalculationFrequencyType recalculationFrequencyType = null;
InterestRecalculationCompoundingMethod compoundingMethod = null;
RecalculationFrequencyType compoundingFrequencyType = null;
LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
- Calendar calendar = null;
CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
+ boolean allowCompoundingOnEod = false;
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
restCalendarInstance = scheduleGeneratorDTO.getCalendarInstanceForInterestRecalculation();
compoundingCalendarInstance = scheduleGeneratorDTO.getCompoundingCalendarInstance();
@@ -5066,6 +5272,8 @@ public class Loan extends AbstractPersistable<Long> {
compoundingMethod = this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod();
compoundingFrequencyType = this.loanInterestRecalculationDetails.getCompoundingFrequencyType();
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
+ allowCompoundingOnEod = this.loanInterestRecalculationDetails.allowCompoundingOnEod();
+ calendarHistoryDataWrapper = scheduleGeneratorDTO.getCalendarHistoryDataWrapper();
}
calendar = scheduleGeneratorDTO.getCalendar();
calendarHistoryDataWrapper = scheduleGeneratorDTO.getCalendarHistoryDataWrapper();
@@ -5075,7 +5283,7 @@ public class Loan extends AbstractPersistable<Long> {
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
annualNominalInterestRate = constructLoanTermVariations(floatingRateDTO, annualNominalInterestRate, loanTermVariations);
LocalDate interestChargedFromDate = getInterestChargedFromDate();
- if(interestChargedFromDate == null && scheduleGeneratorDTO.isInterestChargedFromDateAsDisbursementDateEnabled()){
+ if (interestChargedFromDate == null && scheduleGeneratorDTO.isInterestChargedFromDateAsDisbursementDateEnabled()) {
interestChargedFromDate = getDisbursementDate();
}
@@ -5087,7 +5295,7 @@ public class Loan extends AbstractPersistable<Long> {
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations, calendarHistoryDataWrapper,
- scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth());
+ scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), holidayDetailDTO, allowCompoundingOnEod);
return loanApplicationTerms;
}
@@ -5105,6 +5313,7 @@ public class Loan extends AbstractPersistable<Long> {
Money penaltyCharges = Money.zero(loanCurrency());
Money totalPrincipal = Money.zero(loanCurrency());
Money totalInterest = Money.zero(loanCurrency());
+ final List<LoanInterestRecalcualtionAdditionalDetails> compoundingDetails = null;
for (final LoanRepaymentScheduleInstallment scheduledRepayment : this.repaymentScheduleInstallments) {
totalPrincipal = totalPrincipal.plus(scheduledRepayment.getPrincipalOutstanding(loanCurrency()));
totalInterest = totalInterest.plus(scheduledRepayment.getInterestOutstanding(loanCurrency()));
@@ -5112,7 +5321,7 @@ public class Loan extends AbstractPersistable<Long> {
penaltyCharges = penaltyCharges.plus(scheduledRepayment.getPenaltyChargesOutstanding(loanCurrency()));
}
return new LoanRepaymentScheduleInstallment(null, 0, LocalDate.now(), LocalDate.now(), totalPrincipal.getAmount(),
- totalInterest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount(), false);
+ totalInterest.getAmount(), feeCharges.getAmount(), penaltyCharges.getAmount(), false, compoundingDetails);
}
public List<LoanRepaymentScheduleInstallment> fetchRepaymentScheduleInstallments() {
@@ -5141,11 +5350,17 @@ public class Loan extends AbstractPersistable<Long> {
Money outstanding = Money.zero(getCurrency());
List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
for (LoanTransaction loanTransaction : loanTransactions) {
- if (loanTransaction.isDisbursement()) {
+ if (loanTransaction.isDisbursement() || loanTransaction.isIncomePosting()) {
outstanding = outstanding.plus(loanTransaction.getAmount(getCurrency()));
loanTransaction.updateOutstandingLoanBalance(outstanding.getAmount());
} else {
- outstanding = outstanding.minus(loanTransaction.getPrincipalPortion(getCurrency()));
+ if (this.loanInterestRecalculationDetails != null
+ && this.loanInterestRecalculationDetails.isCompoundingToBePostedAsTransaction()
+ && !loanTransaction.isRepaymentAtDisbursement()) {
+ outstanding = outstanding.minus(loanTransaction.getAmount(getCurrency()));
+ } else {
+ outstanding = outstanding.minus(loanTransaction.getPrincipalPortion(getCurrency()));
+ }
loanTransaction.updateOutstandingLoanBalance(outstanding.getAmount());
}
}
@@ -5252,7 +5467,8 @@ public class Loan extends AbstractPersistable<Long> {
@SuppressWarnings({ "unused" })
public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency,
final CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar,
- final FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays) {
+ final FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays,
+ final HolidayDetailDTO holidayDetailDTO) {
LoanProduct loanProduct = loanProduct();
// LoanProductRelatedDetail loanProductRelatedDetail =
// getLoanRepaymentScheduleDetail();
@@ -5260,8 +5476,15 @@ public class Loan extends AbstractPersistable<Long> {
final Integer loanTermFrequency = getTermFrequency();
final PeriodFrequencyType loanTermPeriodFrequencyType = this.loanRepaymentScheduleDetail.getInterestPeriodFrequencyType();
- final NthDayType nthDayType = NthDayType.fromInt(this.repaymentFrequencyNthDayType);
- final DayOfWeekType dayOfWeekType = DayOfWeekType.fromInt(this.repaymentFrequencyDayOfWeekType);
+ NthDayType nthDayType = null;
+ DayOfWeekType dayOfWeekType = null;
+ if (loanCalendar != null) {
+ nthDayType = CalendarUtils.getRepeatsOnNthDayOfMonth(loanCalendar.getRecurrence());
+ CalendarWeekDaysType getRepeatsOnDay = CalendarUtils.getRepeatsOnDay(loanCalendar.getRecurrence());
+ Integer getRepeatsOnDayValue = null;
+ if (getRepeatsOnDay != null) getRepeatsOnDayValue = getRepeatsOnDay.getValue();
+ if (getRepeatsOnDayValue != null) dayOfWeekType = DayOfWeekType.fromInt(getRepeatsOnDayValue);
+ }
final Integer numberOfRepayments = this.loanRepaymentScheduleDetail.getNumberOfRepayments();
final Integer repaymentEvery = this.loanRepaymentScheduleDetail.getRepayEvery();
@@ -5304,7 +5527,7 @@ public class Loan extends AbstractPersistable<Long> {
final BigDecimal maxOutstandingBalance = getMaxOutstandingLoanBalance();
final List<DisbursementData> disbursementData = getDisbursmentData();
-
+
CalendarHistoryDataWrapper calendarHistoryDataWrapper = null;
if (loanCalendar != null) {
Set<CalendarHistory> calendarHistory = loanCalendar.getCalendarHistory();
@@ -5315,11 +5538,13 @@ public class Loan extends AbstractPersistable<Long> {
InterestRecalculationCompoundingMethod compoundingMethod = null;
RecalculationFrequencyType compoundingFrequencyType = null;
LoanRescheduleStrategyMethod rescheduleStrategyMethod = null;
+ boolean allowCompoundingOnEod = false;
if (this.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
recalculationFrequencyType = this.loanInterestRecalculationDetails.getRestFrequencyType();
compoundingMethod = this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod();
compoundingFrequencyType = this.loanInterestRecalculationDetails.getCompoundingFrequencyType();
rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod();
+ allowCompoundingOnEod = this.loanInterestRecalculationDetails.allowCompoundingOnEod();
}
List<LoanTermVariationsData> loanTermVariations = new ArrayList<>();
@@ -5332,7 +5557,7 @@ public class Loan extends AbstractPersistable<Long> {
this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod,
compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(),
rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations,
- calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst);
+ calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst, holidayDetailDTO, allowCompoundingOnEod);
}
/**
@@ -5376,22 +5601,6 @@ public class Loan extends AbstractPersistable<Long> {
return isEnabled;
}
- public Integer getRepaymentFrequencyNthDayType() {
- return this.repaymentFrequencyNthDayType;
- }
-
- public void setRepaymentFrequencyNthDayType(Integer repaymentFrequencyNthDayType) {
- this.repaymentFrequencyNthDayType = repaymentFrequencyNthDayType;
- }
-
- public Integer getRepaymentFrequencyDayOfWeekType() {
- return this.repaymentFrequencyDayOfWeekType;
- }
-
- public void setRepaymentFrequencyDayOfWeekType(Integer repaymentFrequencyDayOfWeekType) {
- this.repaymentFrequencyDayOfWeekType = repaymentFrequencyDayOfWeekType;
- }
-
public String getAccountNumber() {
return this.accountNumber;
}
@@ -5597,8 +5806,10 @@ public class Loan extends AbstractPersistable<Long> {
updateLoanToLastDisbursalState(actualDisbursementDate);
for (Iterator<LoanTermVariations> iterator = this.loanTermVariations.iterator(); iterator.hasNext();) {
LoanTermVariations loanTermVariations = iterator.next();
- if (loanTermVariations.getTermType().isDueDateVariation() && loanTermVariations.fetchDateValue().isAfter(actualDisbursementDate) ||
- loanTermVariations.getTermType().isEMIAmountVariation() && loanTermVariations.getTermApplicableFrom().equals(actualDisbursementDate.toDate())
+ if (loanTermVariations.getTermType().isDueDateVariation()
+ && loanTermVariations.fetchDateValue().isAfter(actualDisbursementDate)
+ || loanTermVariations.getTermType().isEMIAmountVariation()
+ && loanTermVariations.getTermApplicableFrom().equals(actualDisbursementDate.toDate())
|| loanTermVariations.getTermApplicableFrom().after(actualDisbursementDate.toDate())) {
iterator.remove();
}
@@ -5722,7 +5933,7 @@ public class Loan extends AbstractPersistable<Long> {
}
return nextRepaymentDate;
}
-
+
public BigDecimal getDerivedAmountForCharge(LoanCharge loanCharge) {
BigDecimal amount = BigDecimal.ZERO;
if (isMultiDisburmentLoan() && (loanCharge.getCharge().getChargeTimeType() == ChargeTimeType.DISBURSEMENT.getValue())) {
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
index 5a2241b..f1f8c4e 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInstallmentCharge.java
@@ -288,4 +288,10 @@ public class LoanInstallmentCharge extends AbstractPersistable<Long> {
return amountToDeductOnThisCharge;
}
+ public LoanCharge getLoancharge() {
+ return this.loancharge;
+ }
+ public LoanRepaymentScheduleInstallment getInstallment() {
+ return this.installment;
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/763cf18b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalcualtionAdditionalDetails.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalcualtionAdditionalDetails.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalcualtionAdditionalDetails.java
new file mode 100644
index 0000000..f374760
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanInterestRecalcualtionAdditionalDetails.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.domain;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.joda.time.LocalDate;
+import org.springframework.data.jpa.domain.AbstractPersistable;
+
+@Entity
+@Table(name = "m_loan_interest_recalculation_additional_details")
+public class LoanInterestRecalcualtionAdditionalDetails extends AbstractPersistable<Long> {
+
+ @Temporal(TemporalType.DATE)
+ @Column(name = "effective_date")
+ private Date effectiveDate;
+
+ @Column(name = "amount", scale = 6, precision = 19, nullable = false)
+ private BigDecimal amount;
+
+ protected LoanInterestRecalcualtionAdditionalDetails() {
+
+ }
+
+ public LoanInterestRecalcualtionAdditionalDetails(final LocalDate effectiveDate, final BigDecimal amount) {
+ if (effectiveDate != null) {
+ this.effectiveDate = effectiveDate.toDate();
+ }
+ this.amount = amount;
+ }
+
+ public LocalDate getEffectiveDate() {
+ return new LocalDate(this.effectiveDate);
+ }
+
+ public BigDecimal getAmount() {
+ return this.amount;
+ }
+}