cleaning up MIFOSX-1584

This commit is contained in:
Vishwas Babu A J
2014-11-05 23:58:03 +05:30
parent a8c733100b
commit f1e9c2800b
7 changed files with 115 additions and 104 deletions
@@ -0,0 +1,26 @@
/**
* 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.loanaccount.exception;
import org.joda.time.LocalDate;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when a number of days
* between disbursal date and firstRepayment is less than
* minimumDaysBetweenDisbursalAndFirstRepayment
*
*/
public class MinDaysBetweenDisbursalAndFirstRepaymentViolationException extends AbstractPlatformDomainRuleException {
public MinDaysBetweenDisbursalAndFirstRepaymentViolationException(final LocalDate disbursalDate, final LocalDate firstRepaymentDate,
Integer minimumDaysBetweenDisbursalAndFirstRepayment) {
super("error.msg.loan.days.between.first.repayment.and.disbursal.are.less.than.minimum.allowed.numbers",
"Number of days between loan disbursal (" + disbursalDate + ") and first repayment (" + firstRepaymentDate
+ ") can't be less than (" + minimumDaysBetweenDisbursalAndFirstRepayment + ").", disbursalDate,
firstRepaymentDate, minimumDaysBetweenDisbursalAndFirstRepayment);
}
}
@@ -1,22 +0,0 @@
/**
* 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.loanaccount.exception;
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* {@link AbstractPlatformDomainRuleException} thrown when a number of days between disbursal date and firstRepayment is less than
* minimumDaysBetweenDisbursalAndFirstRepayment
*
*/
public class MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException extends AbstractPlatformDomainRuleException {
public MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException(final String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super("validation.msg.loan.days.between.first.repayment.and.disbursal.are.less than.minimum.number.of.days.required",
defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -51,7 +51,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanTermVariationType;
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
import org.mifosplatform.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
import org.mifosplatform.portfolio.loanaccount.exception.LoanApplicationDateException;
import org.mifosplatform.portfolio.loanaccount.exception.MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException;
import org.mifosplatform.portfolio.loanaccount.exception.MinDaysBetweenDisbursalAndFirstRepaymentViolationException;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.AprCalculator;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanApplicationTerms;
import org.mifosplatform.portfolio.loanaccount.loanschedule.domain.LoanScheduleGenerator;
@@ -161,16 +161,15 @@ public class LoanScheduleAssembler {
final LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element);
final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element);
LocalDate calculatedRepaymentsStartingFromDate = null;
final Boolean synchDisbursement = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element);
final Long calendarId = this.fromApiJsonHelper.extractLongNamed("calendarId", element);
Calendar calendar = null;
final String loanTypeParameterName = "loanType";
final String loanTypeStr = this.fromApiJsonHelper.extractStringNamed(loanTypeParameterName, element);
final AccountType loanType = AccountType.fromName(loanTypeStr);
/*
* If it is JLG loan then make sure loan frequency is same as
@@ -237,20 +236,20 @@ public class LoanScheduleAssembler {
/*
* If first repayment is not derived and instead directly taken from
* input then copy the value of repaymentsStartingFromDate to
* calculatedRepaymentsStartingFromDate
* calculatedRepaymentsStartingFromDate
*
* TODO: The reason for this bad code is, LoanApplicationTerms.assembleFrom
* takes both repaymentsStartingFromDate and calculatedRepaymentsStartingFromDate
* and derived data has precedence over input date,
* TODO: The reason for this bad code is,
* LoanApplicationTerms.assembleFrom takes both
* repaymentsStartingFromDate and calculatedRepaymentsStartingFromDate
* and derived data has precedence over input date,
*
* FIXME:LoanApplicationTerms.assembleFrom should take only one
* "first repayment date" than taking two dates with same purpose.
*
*/
if (!isRepaymentsStartingFromDateDerived) {
calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate;
}
// grace details
final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element);
final Integer graceOnInterestPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnInterestPayment", element);
@@ -467,66 +466,62 @@ public class LoanScheduleAssembler {
}
}
}
private LocalDate deriveFirstRepaymentDate(final AccountType loanType, final Integer repaymentEvery,
private LocalDate deriveFirstRepaymentDate(final AccountType loanType, final Integer repaymentEvery,
final LocalDate expectedDisbursementDate, final PeriodFrequencyType repaymentPeriodFrequencyType,
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar){
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar) {
LocalDate derivedFirstRepayment = null;
LocalDate dateBasedOnRepaymentFrequency = null;
final LocalDate dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
LocalDate dateBasedOnRepaymentFrequency;
final LocalDate dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = expectedDisbursementDate
.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
if (loanType.isIndividualAccount() || loanType.isGroupAccount()) {
// Derive the first repayment date as greater date among (disbursement date + plus frequency) or
// (disbursement date + minimum between disbursal and first repayment )
// Derive the first repayment date as greater date among
// (disbursement date + plus frequency) or
// (disbursement date + minimum between disbursal and first
// repayment )
if (repaymentPeriodFrequencyType.isDaily()) {
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusDays(repaymentEvery);
}
else if(repaymentPeriodFrequencyType.isWeekly()){
} else if (repaymentPeriodFrequencyType.isWeekly()) {
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery);
}
else if (repaymentPeriodFrequencyType.isMonthly()) {
} else if (repaymentPeriodFrequencyType.isMonthly()) {
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery);
}
else if (repaymentPeriodFrequencyType.isYearly()){
} else if (repaymentPeriodFrequencyType.isYearly()) {
dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery);
}
derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment)?dateBasedOnRepaymentFrequency:dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment;
}
else if(loanType.isJLGAccount()){
derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment) ? dateBasedOnRepaymentFrequency
: dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment;
} else if (loanType.isJLGAccount()) {
final LocalDate refernceDateForCalculatingFirstRepaymentDate = expectedDisbursementDate;
derivedFirstRepayment = deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate,refernceDateForCalculatingFirstRepaymentDate,
repaymentPeriodFrequencyType,minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
final LocalDate refernceDateForCalculatingFirstRepaymentDate = expectedDisbursementDate;
derivedFirstRepayment = deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate,
refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType,
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
}
return derivedFirstRepayment;
}
private LocalDate deriveFirstRepaymentDateForJLGLoans(final Integer repaymentEvery,
final LocalDate expectedDisbursementDate, final LocalDate refernceDateForCalculatingFirstRepaymentDate,
final PeriodFrequencyType repaymentPeriodFrequencyType,final Integer minimumDaysBetweenDisbursalAndFirstRepayment,
final Calendar calendar){
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar, refernceDateForCalculatingFirstRepaymentDate,
repaymentEvery, frequency);
final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment
:deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate,derivedFirstRepayment, repaymentPeriodFrequencyType,
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
private LocalDate deriveFirstRepaymentDateForJLGLoans(final Integer repaymentEvery, final LocalDate expectedDisbursementDate,
final LocalDate refernceDateForCalculatingFirstRepaymentDate, final PeriodFrequencyType repaymentPeriodFrequencyType,
final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar) {
final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar,
refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency);
final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment : deriveFirstRepaymentDateForJLGLoans(
repaymentEvery, expectedDisbursementDate, derivedFirstRepayment, repaymentPeriodFrequencyType,
minimumDaysBetweenDisbursalAndFirstRepayment, calendar);
}
private void validateMinimumDaysBetweenDisbursalAndFirstRepayment(final LocalDate disbursalDate,
final LocalDate firstRepaymentDate, final Integer minimumDaysBetweenDisbursalAndFirstRepayment){
final LocalDate minimumFirstRepaymentDate = disbursalDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
if(firstRepaymentDate.isBefore(minimumFirstRepaymentDate)){
final String errorMessage = "Number of days between disbursal date (" + disbursalDate + ") and first repayment date ("
+ firstRepaymentDate + ") can't be less than minimumDaysBetweenDisbursalAndFirstRepayment ("
+ minimumDaysBetweenDisbursalAndFirstRepayment + ")." ;
throw new MinimumDaysBetweenDisbursalAndFirstRepaymentRuleException(errorMessage);
}
private void validateMinimumDaysBetweenDisbursalAndFirstRepayment(final LocalDate disbursalDate, final LocalDate firstRepaymentDate,
final Integer minimumDaysBetweenDisbursalAndFirstRepayment) {
final LocalDate minimumFirstRepaymentDate = disbursalDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment);
if (firstRepaymentDate.isBefore(minimumFirstRepaymentDate)) { throw new MinDaysBetweenDisbursalAndFirstRepaymentViolationException(
disbursalDate, firstRepaymentDate, minimumDaysBetweenDisbursalAndFirstRepayment); }
}
}
@@ -112,19 +112,32 @@ public final class LoanApplicationCommandFromApiJsonHelper {
if (loanType.isJLGAccount()) {
baseDataValidator.reset().parameter("clientId").value(clientId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter("groupId").value(groupId).notNull().longGreaterThanZero();
/**
* these validations should be carried out based on Global
* configurations, commenting them for now
**/
// if it is JLG loan then must have a meeting details (calendar)
final String calendarIdParameterName = "calendarId";
final Long calendarId = this.fromApiJsonHelper.extractLongNamed(calendarIdParameterName, element);
baseDataValidator.reset().parameter(calendarIdParameterName).value(calendarId).notNull().integerGreaterThanZero();
// if it is JLG loan then must have a syncDisbursement
final String syncDisbursementParameterName = "syncDisbursementWithMeeting";
final Boolean syncDisbursement = this.fromApiJsonHelper.extractBooleanNamed(syncDisbursementParameterName, element);
if (syncDisbursement == null) {
baseDataValidator.reset().parameter(syncDisbursementParameterName).value(syncDisbursement).trueOrFalseRequired(false);
}
/**
* // if it is JLG loan then must have a meeting details
* (calendar) final String calendarIdParameterName =
* "calendarId"; final Long calendarId =
* this.fromApiJsonHelper.extractLongNamed
* (calendarIdParameterName, element);
* baseDataValidator.reset().
* parameter(calendarIdParameterName).value
* (calendarId).notNull().integerGreaterThanZero();
*
* // if it is JLG loan then must have a syncDisbursement final
* String syncDisbursementParameterName =
* "syncDisbursementWithMeeting"; final Boolean syncDisbursement
* = this.fromApiJsonHelper.extractBooleanNamed(
* syncDisbursementParameterName, element);
*
* if (syncDisbursement == null) {
* baseDataValidator.reset().parameter
* (syncDisbursementParameterName
* ).value(syncDisbursement).trueOrFalseRequired(false); }
**/
}
}
@@ -121,7 +121,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
@Column(name = "overdue_days_for_npa", nullable = true)
private Integer overdueDaysForNPA;
@Column(name = "min_days_between_disbursal_and_first_repayment", nullable = true)
private Integer minimumDaysBetweenDisbursalAndFirstRepayment;
@@ -168,7 +168,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
final Integer graceOnInterestPayment = command.integerValueOfParameterNamed("graceOnInterestPayment");
final Integer graceOnInterestCharged = command.integerValueOfParameterNamed("graceOnInterestCharged");
final Integer minimumDaysBetweenDisbursalAndFirstRepayment = command
.integerValueOfParameterNamed("minimumDaysBetweenDisbursalAndFirstRepayment");
.integerValueOfParameterNamed(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment);
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(command.integerValueOfParameterNamed("accountingRule"));
final boolean includeInBorrowerCycle = command.booleanPrimitiveValueOfParameterNamed("includeInBorrowerCycle");
@@ -435,7 +435,8 @@ public class LoanProduct extends AbstractPersistable<Long> {
final Set<LoanProductBorrowerCycleVariations> loanProductBorrowerCycleVariations, final boolean multiDisburseLoan,
final Integer maxTrancheCount, final BigDecimal outstandingLoanBalance, final Integer graceOnArrearsAgeing,
final Integer overdueDaysForNPA, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType,
final boolean isInterestRecalculationEnabled, final LoanProductInterestRecalculationDetails productInterestRecalculationDetails,
final boolean isInterestRecalculationEnabled,
final LoanProductInterestRecalculationDetails productInterestRecalculationDetails,
final Integer minimumDaysBetweenDisbursalAndFirstRepayment) {
this.fund = fund;
this.transactionProcessingStrategy = transactionProcessingStrategy;
@@ -665,7 +666,7 @@ public class LoanProduct extends AbstractPersistable<Long> {
actualChanges.put("locale", localeAsInput);
this.minimumDaysBetweenDisbursalAndFirstRepayment = newValue;
}
/**
* Update interest recalculation settings
*/
@@ -809,10 +810,10 @@ public class LoanProduct extends AbstractPersistable<Long> {
return this.loanProductRelatedDetail.isInterestRecalculationEnabled();
}
public Integer getMinimumDaysBetweenDisbursalAndFirstRepayment(){
return this.minimumDaysBetweenDisbursalAndFirstRepayment == null?0:this.minimumDaysBetweenDisbursalAndFirstRepayment;
public Integer getMinimumDaysBetweenDisbursalAndFirstRepayment() {
return this.minimumDaysBetweenDisbursalAndFirstRepayment == null ? 0 : this.minimumDaysBetweenDisbursalAndFirstRepayment;
}
public LoanProductBorrowerCycleVariations fetchLoanProductBorrowerCycleVariationById(Long id) {
LoanProductBorrowerCycleVariations borrowerCycleVariation = null;
for (LoanProductBorrowerCycleVariations cycleVariation : this.borrowerCycleVariations) {
@@ -104,14 +104,14 @@ public final class LoanProductDataValidator {
final Long fundId = this.fromApiJsonHelper.extractLongNamed("fundId", element);
baseDataValidator.reset().parameter("fundId").value(fundId).ignoreIfNull().integerGreaterThanZero();
}
if (this.fromApiJsonHelper.parameterExists("minimumDaysBetweenDisbursalAndFirstRepayment", element)) {
if (this.fromApiJsonHelper.parameterExists(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, element)) {
final Long minimumDaysBetweenDisbursalAndFirstRepayment = this.fromApiJsonHelper.extractLongNamed(
"minimumDaysBetweenDisbursalAndFirstRepayment", element);
baseDataValidator.reset().parameter("minimumDaysBetweenDisbursalAndFirstRepayment")
LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment, element);
baseDataValidator.reset().parameter(LoanProductConstants.minimumDaysBetweenDisbursalAndFirstRepayment)
.value(minimumDaysBetweenDisbursalAndFirstRepayment).ignoreIfNull().integerGreaterThanZero();
}
final Boolean includeInBorrowerCycle = this.fromApiJsonHelper.extractBooleanNamed("includeInBorrowerCycle", element);
baseDataValidator.reset().parameter("includeInBorrowerCycle").value(includeInBorrowerCycle).ignoreIfNull()
.validateForBooleanValue();
@@ -1,2 +0,0 @@
ALTER TABLE `m_product_loan`
ADD COLUMN `min_days_between_disbursal_and_first_repayment` INT(3) NULL AFTER `interest_recalculation_enabled`;