From af6c9e2ed6d4b886f94b1469297f571d3d5467b1 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Sun, 7 Dec 2014 19:47:19 -0800 Subject: [PATCH] MIFOSX-1727 --- api-docs/apiLive.htm | 1 + .../domain/ConfigurationDomainService.java | 2 + .../domain/ConfigurationDomainServiceJpa.java | 7 ++ .../domain/LoanApplicationTerms.java | 79 ++++++++--------- .../service/LoanScheduleAssembler.java | 88 ++++++------------- ...cheduleCalculationPlatformServiceImpl.java | 15 +++- ...anApplicationCommandFromApiJsonHelper.java | 62 ++++++------- ...WritePlatformServiceJpaRepositoryImpl.java | 8 +- ..._for_enforcing_calendars_for_jlg_loans.sql | 1 + 9 files changed, 122 insertions(+), 141 deletions(-) create mode 100644 mifosng-provider/src/main/resources/sql/migrations/core_db/V226__configuration_for_enforcing_calendars_for_jlg_loans.sql diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 58962fb43..8600adf2d 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -13353,6 +13353,7 @@ Request Body:
  • allow-transactions-on-holiday - defaults to false - if true allows transactions on holidays
  • savings-interest-posting-current-period-end - Set it at the database level before any savings interest is posted. When set as false(default), interest will be posted on the first date of next period. If set as true, interest will be posted on last date of current period. There is no difference in the interest amount posted.
  • financial-year-beginning-month - Set it at the database level before any savings interest is posted. Allowed values 1 - 12 (January - December). Interest posting periods are evaluated based on this configuration.
  • +
  • meetings-mandatory-for-jlg-loans - if set to true, enforces all JLG loans to follow a meeting schedule belonging to either the parent group or Center.
  • diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java index e7146a302..bcbc2833e 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -43,4 +43,6 @@ public interface ConfigurationDomainService { public Integer retrieveMaxAllowedClientsInGroup(); + boolean isMeetingMandatoryForJLGLoans(); + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index 3b025b6dd..b8d873696 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -166,4 +166,11 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService return null; } + @Override + public boolean isMeetingMandatoryForJLGLoans() { + final String propertyName = "meetings-mandatory-for-jlg-loans"; + final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName); + return property.isEnabled(); + } + } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index 2daabc0d8..4aefa7775 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -19,11 +19,11 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency; import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency; import org.mifosplatform.organisation.monetary.domain.Money; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; +import org.mifosplatform.portfolio.common.domain.DayOfWeekType; import org.mifosplatform.portfolio.common.domain.DaysInMonthType; import org.mifosplatform.portfolio.common.domain.DaysInYearType; import org.mifosplatform.portfolio.common.domain.NthDayType; import org.mifosplatform.portfolio.common.domain.PeriodFrequencyType; -import org.mifosplatform.portfolio.common.domain.DayOfWeekType; import org.mifosplatform.portfolio.loanaccount.data.DisbursementData; import org.mifosplatform.portfolio.loanaccount.data.LoanTermVariationsData; import org.mifosplatform.portfolio.loanaccount.domain.LoanInterestRecalculationDetails; @@ -44,8 +44,8 @@ public final class LoanApplicationTerms { private final Integer repaymentEvery; private final PeriodFrequencyType repaymentPeriodFrequencyType; private final Integer nthDay; - - private final DayOfWeekType weekDayType; + + private final DayOfWeekType weekDayType; private final AmortizationMethod amortizationMethod; private final InterestMethod interestMethod; @@ -106,15 +106,15 @@ public final class LoanApplicationTerms { // added private LocalDate loanEndDate; - private List disbursementDatas; + private final List disbursementDatas; - private boolean multiDisburseLoan; + private final boolean multiDisburseLoan; private BigDecimal fixedEmiAmount; - private BigDecimal maxOutstandingBalance; + private final BigDecimal maxOutstandingBalance; - private List emiAmountVariations; + private final List emiAmountVariations; private Money totalInterestDue; @@ -134,38 +134,39 @@ public final class LoanApplicationTerms { public static LoanApplicationTerms assembleFrom(final ApplicationCurrency currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, - final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, - DayOfWeekType weekDayType, final AmortizationMethod amortizationMethod, - final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod, + final PeriodFrequencyType repaymentPeriodFrequencyType, Integer nthDay, DayOfWeekType weekDayType, + final AmortizationMethod amortizationMethod, final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualNominalInterestRate, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Money principalMoney, final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, - final LocalDate calculatedRepaymentsStartingFromDate, final Integer graceOnPrincipalPayment, final Integer graceOnInterestPayment, - final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, final Money inArrearsTolerance, - final boolean multiDisburseLoan, final BigDecimal emiAmount, + final LocalDate calculatedRepaymentsStartingFromDate, final Integer graceOnPrincipalPayment, + final Integer graceOnInterestPayment, final Integer graceOnInterestCharged, final LocalDate interestChargedFromDate, + final Money inArrearsTolerance, final boolean multiDisburseLoan, final BigDecimal emiAmount, final List disbursementDatas, final BigDecimal maxOutstandingBalance, - final List emiAmountVariations, final Integer graceOnArrearsAgeing, final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled) { + final List emiAmountVariations, final Integer graceOnArrearsAgeing, + final DaysInMonthType daysInMonthType, final DaysInYearType daysInYearType, final boolean isInterestRecalculationEnabled) { final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null; final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null; final CalendarInstance restCalendarInstance = null; final RecalculationFrequencyType recalculationFrequencyType = null; return new LoanApplicationTerms(currency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery, - repaymentPeriodFrequencyType,nthDay, weekDayType, amortizationMethod, interestMethod, interestRatePerPeriod, interestRatePeriodFrequencyType, - annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney, expectedDisbursementDate, - repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, graceOnInterestPayment, - graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount, disbursementDatas, - maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType, + repaymentPeriodFrequencyType, nthDay, weekDayType, amortizationMethod, interestMethod, interestRatePerPeriod, + interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney, + expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, + graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount, + disbursementDatas, maxOutstandingBalance, emiAmountVariations, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType); } public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, - final PeriodFrequencyType loanTermPeriodFrequencyType, NthDayType nthDay, - DayOfWeekType dayOfWeek, final LocalDate expectedDisbursementDate, - final LocalDate repaymentsStartingFromDate, final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance, + final PeriodFrequencyType loanTermPeriodFrequencyType, NthDayType nthDay, DayOfWeekType dayOfWeek, + final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, + final LocalDate calculatedRepaymentsStartingFromDate, final Money inArrearsTolerance, final LoanProductRelatedDetail loanProductRelatedDetail, final boolean multiDisburseLoan, final BigDecimal emiAmount, - final List disbursementDatas, final BigDecimal maxOutstandingBalance, final List emiAmountVariations, final LocalDate interestChargedFromDate) { + final List disbursementDatas, final BigDecimal maxOutstandingBalance, + final List emiAmountVariations, final LocalDate interestChargedFromDate) { final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments(); final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery(); @@ -194,13 +195,13 @@ public final class LoanApplicationTerms { final RecalculationFrequencyType recalculationFrequencyType = null; return new LoanApplicationTerms(applicationCurrency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, - repaymentEvery, repaymentPeriodFrequencyType, nthDay.getValue(), dayOfWeek, amortizationMethod, interestMethod, interestRatePerPeriod, - interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, principalMoney, - expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, graceOnPrincipalPayment, - graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, multiDisburseLoan, emiAmount, - disbursementDatas, maxOutstandingBalance, emiAmountVariations, loanProductRelatedDetail.getGraceOnDueDate(), - daysInMonthType, daysInYearType, isInterestRecalculationEnabled, rescheduleStrategyMethod, - interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType); + repaymentEvery, repaymentPeriodFrequencyType, nthDay.getValue(), dayOfWeek, amortizationMethod, interestMethod, + interestRatePerPeriod, interestRatePeriodFrequencyType, annualNominalInterestRate, interestCalculationPeriodMethod, + principalMoney, expectedDisbursementDate, repaymentsStartingFromDate, calculatedRepaymentsStartingFromDate, + graceOnPrincipalPayment, graceOnInterestPayment, graceOnInterestCharged, interestChargedFromDate, inArrearsTolerance, + multiDisburseLoan, emiAmount, disbursementDatas, maxOutstandingBalance, emiAmountVariations, + loanProductRelatedDetail.getGraceOnDueDate(), daysInMonthType, daysInYearType, isInterestRecalculationEnabled, + rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType); } public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, @@ -270,9 +271,8 @@ public final class LoanApplicationTerms { private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, - final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer nthDay, - final DayOfWeekType weekDayType, final AmortizationMethod amortizationMethod, - final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod, + final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer nthDay, final DayOfWeekType weekDayType, + final AmortizationMethod amortizationMethod, final InterestMethod interestMethod, final BigDecimal interestRatePerPeriod, final PeriodFrequencyType interestRatePeriodFrequencyType, final BigDecimal annualNominalInterestRate, final InterestCalculationPeriodMethod interestCalculationPeriodMethod, final Money principal, final LocalDate expectedDisbursementDate, final LocalDate repaymentsStartingFromDate, @@ -990,15 +990,14 @@ public final class LoanApplicationTerms { public BigDecimal getFixedEmiAmount() { return this.fixedEmiAmount; } - + public Integer getNthDay() { - return this.nthDay; - } - - public DayOfWeekType getWeekDayType() { - return this.weekDayType; - } + return this.nthDay; + } + public DayOfWeekType getWeekDayType() { + return this.weekDayType; + } public void setFixedEmiAmountForPeriod(LocalDate periodDate) { LocalDate startDate = expectedDisbursementDate; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index 9eff94d31..d877706dc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -164,7 +164,7 @@ public class LoanScheduleAssembler { final LocalDate expectedDisbursementDate = this.fromApiJsonHelper.extractLocalDateNamed("expectedDisbursementDate", element); final LocalDate repaymentsStartingFromDate = this.fromApiJsonHelper.extractLocalDateNamed("repaymentsStartingFromDate", element); - LocalDate calculatedRepaymentsStartingFromDate = null; + LocalDate calculatedRepaymentsStartingFromDate = repaymentsStartingFromDate; final Boolean synchDisbursement = this.fromApiJsonHelper.extractBooleanNamed("syncDisbursementWithMeeting", element); final Long calendarId = this.fromApiJsonHelper.extractLongNamed("calendarId", element); @@ -190,67 +190,31 @@ public class LoanScheduleAssembler { } /* - * If user has not passed the first repayments date then only system - * will derive it based on loan is individual/group loan or JLG loan. + * If user has not passed the first repayments date then then derive the + * same based on loan type. */ - Boolean isRepaymentsStartingFromDateDerived = false; - if (repaymentsStartingFromDate == null) { + if (calculatedRepaymentsStartingFromDate == null) { calculatedRepaymentsStartingFromDate = deriveFirstRepaymentDate(loanType, repaymentEvery, expectedDisbursementDate, repaymentPeriodFrequencyType, loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment(), calendar); - isRepaymentsStartingFromDateDerived = true; } /* - * If it is JLG loan, then make sure first repayment falls on meeting - * date + * If it is JLG loan synched with a meeting, then make sure first + * repayment falls on meeting date */ - if (loanType.isJLGAccount()) { - if (isRepaymentsStartingFromDateDerived) { - validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar); - } else { - validateRepaymentsStartDateWithMeetingDates(repaymentsStartingFromDate, calendar); - } - } - - /* - * If it is JLG loan and disbursement is synced on meeting, make sure - * disbursement date on meeting date - */ - if (loanType.isJLGAccount()) { + if (loanType.isJLGAccount() && calendar != null) { + validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar); + /* + * If disbursement is synced on meeting, make sure disbursement date + * is on a meeting date + */ if (synchDisbursement != null && synchDisbursement.booleanValue()) { validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar); } } - /* - * If minimum days between disbursal and first repayment is set, then - * validate the rule - */ - if (isRepaymentsStartingFromDateDerived) { - validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate, calculatedRepaymentsStartingFromDate, - loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment()); - } else { - validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate, repaymentsStartingFromDate, - loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment()); - - } - - /* - * If first repayment is not derived and instead directly taken from - * input then copy the value of repaymentsStartingFromDate to - * calculatedRepaymentsStartingFromDate - * - * 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; - } + validateMinimumDaysBetweenDisbursalAndFirstRepayment(expectedDisbursementDate, calculatedRepaymentsStartingFromDate, + loanProduct.getMinimumDaysBetweenDisbursalAndFirstRepayment()); // grace details final Integer graceOnPrincipalPayment = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("graceOnPrincipalPayment", element); @@ -469,17 +433,25 @@ public class LoanScheduleAssembler { } } - @SuppressWarnings("null") private LocalDate deriveFirstRepaymentDate(final AccountType loanType, final Integer repaymentEvery, final LocalDate expectedDisbursementDate, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar) { LocalDate derivedFirstRepayment = null; - LocalDate dateBasedOnRepaymentFrequency = null; + final LocalDate dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment = expectedDisbursementDate .plusDays(minimumDaysBetweenDisbursalAndFirstRepayment); - if (loanType.isIndividualAccount() || loanType.isGroupAccount()) { + if (loanType.isJLGAccount() && calendar != null) { + + final LocalDate refernceDateForCalculatingFirstRepaymentDate = expectedDisbursementDate; + derivedFirstRepayment = deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate, + refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType, + minimumDaysBetweenDisbursalAndFirstRepayment, calendar); + + }/*** Individual or group account, or JLG not linked to a meeting ***/ + else { + LocalDate dateBasedOnRepaymentFrequency; // Derive the first repayment date as greater date among // (disbursement date + plus frequency) or // (disbursement date + minimum between disbursal and first @@ -490,18 +462,12 @@ public class LoanScheduleAssembler { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusWeeks(repaymentEvery); } else if (repaymentPeriodFrequencyType.isMonthly()) { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusMonths(repaymentEvery); - } else if (repaymentPeriodFrequencyType.isYearly()) { + }/** yearly loan **/ + else { dateBasedOnRepaymentFrequency = expectedDisbursementDate.plusYears(repaymentEvery); } derivedFirstRepayment = dateBasedOnRepaymentFrequency.isAfter(dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment) ? dateBasedOnRepaymentFrequency : dateBasedOnMinimumDaysBetweenDisbursalAndFirstRepayment; - } else if (loanType.isJLGAccount()) { - - final LocalDate refernceDateForCalculatingFirstRepaymentDate = expectedDisbursementDate; - derivedFirstRepayment = deriveFirstRepaymentDateForJLGLoans(repaymentEvery, expectedDisbursementDate, - refernceDateForCalculatingFirstRepaymentDate, repaymentPeriodFrequencyType, - minimumDaysBetweenDisbursalAndFirstRepayment, calendar); - } return derivedFirstRepayment; diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java index 0c403ab16..f8942ddc1 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleCalculationPlatformServiceImpl.java @@ -11,6 +11,7 @@ import java.util.Collection; import java.util.List; import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; import org.mifosplatform.infrastructure.core.api.JsonQuery; import org.mifosplatform.infrastructure.core.data.ApiParameterError; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; @@ -61,6 +62,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC private final LoanAccountDomainService accountDomainService; private final CalendarInstanceRepository calendarInstanceRepository; private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory; + private final ConfigurationDomainService configurationDomainService; @Autowired public LoanScheduleCalculationPlatformServiceImpl(final CalculateLoanScheduleQueryFromApiJsonHelper fromApiJsonDeserializer, @@ -69,7 +71,8 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC final LoanReadPlatformService loanReadPlatformService, final LoanApplicationCommandFromApiJsonHelper loanApiJsonDeserializer, final LoanAssembler loanAssembler, final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, final LoanAccountDomainService accountDomainService, final CalendarInstanceRepository calendarInstanceRepository, - final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory) { + final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory, + final ConfigurationDomainService configurationDomainService) { this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.loanScheduleAssembler = loanScheduleAssembler; this.fromJsonHelper = fromJsonHelper; @@ -82,13 +85,19 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC this.accountDomainService = accountDomainService; this.calendarInstanceRepository = calendarInstanceRepository; this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory; + this.configurationDomainService = configurationDomainService; } @Override public LoanScheduleModel calculateLoanSchedule(final JsonQuery query, Boolean validateParams) { + /*** + * TODO: Vishwas, this is probably not required, test and remove the + * same + **/ if (validateParams) { - this.loanApiJsonDeserializer.validateForCreate(query.json()); + boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans(); + this.loanApiJsonDeserializer.validateForCreate(query.json(), isMeetingMandatoryForJLGLoans); } this.fromApiJsonDeserializer.validate(query.json()); @@ -171,7 +180,7 @@ public class LoanScheduleCalculationPlatformServiceImpl implements LoanScheduleC } if (isNewPaymentRequired) { LoanTransaction ondayPaymentTransaction = LoanTransaction.repayment(null, totalAmount, null, LocalDate.now(), null, - DateUtils.getLocalDateTimeOfTenant(), null); + DateUtils.getLocalDateTimeOfTenant(), null); modifiedTransactions.add(ondayPaymentTransaction); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java index e31b2177b..7ba21aed9 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanApplicationCommandFromApiJsonHelper.java @@ -46,9 +46,9 @@ public final class LoanApplicationCommandFromApiJsonHelper { */ final Set supportedParameters = new HashSet<>(Arrays.asList("dateFormat", "locale", "id", "clientId", "groupId", "loanType", "productId", "principal", "loanTermFrequency", "loanTermFrequencyType", "numberOfRepayments", "repaymentEvery", - "repaymentFrequencyType","repaymentFrequencyNthDayType", "repaymentFrequencyDayOfWeekType", "interestRatePerPeriod", "amortizationType", "interestType", "interestCalculationPeriodType", - "expectedDisbursementDate", "repaymentsStartingFromDate", "graceOnPrincipalPayment", - "graceOnInterestPayment", + "repaymentFrequencyType", "repaymentFrequencyNthDayType", "repaymentFrequencyDayOfWeekType", "interestRatePerPeriod", + "amortizationType", "interestType", "interestCalculationPeriodType", "expectedDisbursementDate", "repaymentsStartingFromDate", + "graceOnPrincipalPayment", "graceOnInterestPayment", "graceOnInterestCharged", "interestChargedFromDate", // "submittedOnDate", @@ -78,7 +78,7 @@ public final class LoanApplicationCommandFromApiJsonHelper { this.apiJsonHelper = apiJsonHelper; } - public void validateForCreate(final String json) { + public void validateForCreate(final String json, final boolean isMeetingMandatoryForJLGLoans) { if (StringUtils.isBlank(json)) { throw new InvalidJsonException(); } final Type typeOfMap = new TypeToken>() {}.getType(); @@ -112,32 +112,24 @@ 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 that must have meeting details + if (isMeetingMandatoryForJLGLoans) { + 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 value for + // syncDisbursement passed in + String syncDisbursementParameterName = "syncDisbursementWithMeeting"; + final Boolean syncDisbursement = this.fromApiJsonHelper.extractBooleanNamed(syncDisbursementParameterName, element); + + if (syncDisbursement == null) { + baseDataValidator.reset().parameter(syncDisbursementParameterName).value(syncDisbursement) + .trueOrFalseRequired(false); + } + } + } } @@ -289,14 +281,14 @@ public final class LoanApplicationCommandFromApiJsonHelper { final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element); baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).ignoreIfNull().longGreaterThanZero(); } - + final String createSiAtDisbursementParameterName = "createStandingInstructionAtDisbursement"; - if(this.fromApiJsonHelper.parameterExists(createSiAtDisbursementParameterName, element)) { - final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper - .extractBooleanNamed(createSiAtDisbursementParameterName, element); + if (this.fromApiJsonHelper.parameterExists(createSiAtDisbursementParameterName, element)) { + final Boolean createStandingInstructionAtDisbursement = this.fromApiJsonHelper.extractBooleanNamed( + createSiAtDisbursementParameterName, element); final Long linkAccountId = this.fromApiJsonHelper.extractLongNamed(linkAccountIdParameterName, element); - - if(createStandingInstructionAtDisbursement) { + + if (createStandingInstructionAtDisbursement) { baseDataValidator.reset().parameter(linkAccountIdParameterName).value(linkAccountId).notNull().longGreaterThanZero(); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index 1a312d96c..bac4afa4a 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -18,6 +18,7 @@ import org.mifosplatform.infrastructure.accountnumberformat.domain.AccountNumber import org.mifosplatform.infrastructure.accountnumberformat.domain.AccountNumberFormatRepositoryWrapper; import org.mifosplatform.infrastructure.accountnumberformat.domain.EntityAccountType; import org.mifosplatform.infrastructure.codes.domain.CodeValue; +import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService; import org.mifosplatform.infrastructure.core.api.JsonCommand; import org.mifosplatform.infrastructure.core.api.JsonQuery; import org.mifosplatform.infrastructure.core.data.ApiParameterError; @@ -131,6 +132,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa private final LoanAccountDomainService loanAccountDomainService; private final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository; private final BusinessEventNotifierService businessEventNotifierService; + private final ConfigurationDomainService configurationDomainService; @Autowired public LoanApplicationWritePlatformServiceJpaRepositoryImpl(final PlatformSecurityContext context, final FromJsonHelper fromJsonHelper, @@ -148,7 +150,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa final LoanRepaymentScheduleInstallmentRepository repaymentScheduleInstallmentRepository, final LoanReadPlatformService loanReadPlatformService, final LoanAccountDomainService loanAccountDomainService, final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, - final BusinessEventNotifierService businessEventNotifierService) { + final BusinessEventNotifierService businessEventNotifierService, final ConfigurationDomainService configurationDomainService) { this.context = context; this.fromJsonHelper = fromJsonHelper; this.loanApplicationTransitionApiJsonValidator = loanApplicationTransitionApiJsonValidator; @@ -176,6 +178,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa this.loanAccountDomainService = loanAccountDomainService; this.accountNumberFormatRepository = accountNumberFormatRepository; this.businessEventNotifierService = businessEventNotifierService; + this.configurationDomainService = configurationDomainService; } private LoanLifecycleStateMachine defaultLoanLifecycleStateMachine() { @@ -189,8 +192,9 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa try { final AppUser currentUser = getAppUserIfPresent(); + boolean isMeetingMandatoryForJLGLoans = configurationDomainService.isMeetingMandatoryForJLGLoans(); - this.fromApiJsonDeserializer.validateForCreate(command.json()); + this.fromApiJsonDeserializer.validateForCreate(command.json(), isMeetingMandatoryForJLGLoans); final List dataValidationErrors = new ArrayList<>(); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan"); diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V226__configuration_for_enforcing_calendars_for_jlg_loans.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V226__configuration_for_enforcing_calendars_for_jlg_loans.sql new file mode 100644 index 000000000..f1aea7346 --- /dev/null +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V226__configuration_for_enforcing_calendars_for_jlg_loans.sql @@ -0,0 +1 @@ +INSERT INTO c_configuration (`name`, `description`) VALUES ('meetings-mandatory-for-jlg-loans', 'Enforces all JLG loans to follow a meeting schedule belonging to parent group or Center'); \ No newline at end of file