Merge remote-tracking branch 'ashok/MIFOSX-475' into develop
This commit is contained in:
@@ -0,0 +1 @@
|
||||
INSERT INTO `c_configuration` (`name`, `enabled`) VALUES ('reschedule-future-repayments', 1);
|
||||
+1
@@ -9,4 +9,5 @@ public interface ConfigurationDomainService {
|
||||
|
||||
boolean isMakerCheckerEnabledForTask(String taskPermissionCode);
|
||||
boolean isAmazonS3Enabled();
|
||||
boolean isRescheduleFutureRepaymentsEnabled();
|
||||
}
|
||||
+9
@@ -44,4 +44,13 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
public boolean isAmazonS3Enabled(){
|
||||
return this.globalConfigurationRepository.findOneByName("amazon-S3").isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRescheduleFutureRepaymentsEnabled() {
|
||||
final String rescheduleRepaymentsConfigurationProperty = "reschedule-future-repayments";
|
||||
final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByName(rescheduleRepaymentsConfigurationProperty);
|
||||
if (property == null) { throw new GlobalConfigurationPropertyNotFoundException(rescheduleRepaymentsConfigurationProperty); }
|
||||
return property.isEnabled();
|
||||
}
|
||||
|
||||
}
|
||||
+44
-7
@@ -21,6 +21,9 @@ import org.joda.time.LocalDate;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom;
|
||||
import org.mifosplatform.portfolio.calendar.CalendarConstants.CALENDAR_SUPPORTED_PARAMETERS;
|
||||
import org.mifosplatform.portfolio.calendar.exception.CalendarDateException;
|
||||
import org.mifosplatform.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarHelper;
|
||||
import org.mifosplatform.useradministration.domain.AppUser;
|
||||
|
||||
@Entity
|
||||
@@ -147,13 +150,19 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
|
||||
final String localeAsInput = command.locale();
|
||||
final String startDateParamName = CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue();
|
||||
if (command.isChangeInLocalDateParameterNamed(startDateParamName, getStartDateLocalDate())) {
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(startDateParamName);
|
||||
actualChanges.put(startDateParamName, valueAsInput);
|
||||
actualChanges.put("dateFormat", dateFormatAsInput);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
|
||||
final String valueAsInput = command.stringValueOfParameterNamed(startDateParamName);
|
||||
final LocalDate newValue = command.localDateValueOfParameterNamed(startDateParamName);
|
||||
this.startDate = newValue.toDate();
|
||||
if(isStartDateBefore(newValue)){
|
||||
actualChanges.put(startDateParamName, valueAsInput);
|
||||
actualChanges.put("dateFormat", dateFormatAsInput);
|
||||
actualChanges.put("locale", localeAsInput);
|
||||
this.startDate = newValue.toDate();
|
||||
}else{
|
||||
//new meeting start date should be greater than existing meeting start date
|
||||
final String defaultUserMessage = "New meeting start on or after date cannot be a date before existing meeting start date";
|
||||
throw new CalendarDateException("new.start.date.before.existing.date", defaultUserMessage, newValue, getStartDateLocalDate());
|
||||
}
|
||||
}
|
||||
|
||||
final String endDateParamName = CALENDAR_SUPPORTED_PARAMETERS.END_DATE.getValue();
|
||||
@@ -174,13 +183,21 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.duration = newValue;
|
||||
}
|
||||
|
||||
//Do not allow to change calendar type
|
||||
//TODO: AA Instead of throwing an exception, do not allow meeting calendar type to update.
|
||||
final String typeParamName = CALENDAR_SUPPORTED_PARAMETERS.TYPE_ID.getValue();
|
||||
if (command.isChangeInIntegerSansLocaleParameterNamed(typeParamName, this.typeId)) {
|
||||
final Integer newValue = command.integerValueSansLocaleOfParameterNamed(typeParamName);
|
||||
final String defaultUserMessage = "Meeting calendar type update is not supported";
|
||||
final String oldMeeingType = CalendarType.fromInt(this.typeId).name();
|
||||
final String newMeetingType = CalendarType.fromInt(newValue).name();
|
||||
|
||||
throw new CalendarParameterUpdateNotSupportedException("meeting.type", defaultUserMessage, newMeetingType, oldMeeingType);
|
||||
/*final Integer newValue = command.integerValueSansLocaleOfParameterNamed(typeParamName);
|
||||
actualChanges.put(typeParamName, newValue);
|
||||
this.typeId = newValue;
|
||||
this.typeId = newValue;*/
|
||||
}
|
||||
|
||||
|
||||
final String repeatingParamName = CALENDAR_SUPPORTED_PARAMETERS.REPEATING.getValue();
|
||||
if (command.isChangeInBooleanParameterNamed(repeatingParamName, this.repeating)) {
|
||||
final boolean newValue = command.booleanPrimitiveValueOfParameterNamed(repeatingParamName);
|
||||
@@ -188,9 +205,25 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
|
||||
this.repeating = newValue;
|
||||
}
|
||||
|
||||
//FIXME: AA Construct recurrence rule in API rather than getting it from UI
|
||||
final String recurrenceParamName = CALENDAR_SUPPORTED_PARAMETERS.RECURRENCE.getValue();
|
||||
if (command.isChangeInStringParameterNamed(recurrenceParamName, this.recurrence)) {
|
||||
final String newValue = command.stringValueOfParameterNamed(recurrenceParamName);
|
||||
|
||||
//FIXME: AA - Is this restriction required only for collection type meetings or for all?.
|
||||
//Do not allow to change meeting frequency
|
||||
|
||||
if(!CalendarHelper.isFrequencySame(this.recurrence, newValue)){
|
||||
final String defaultUserMessage = "Update of meeting frequency is not supported";
|
||||
throw new CalendarParameterUpdateNotSupportedException("meeting.frequency", defaultUserMessage);
|
||||
}
|
||||
|
||||
//Do not allow to change meeting interval
|
||||
if(!CalendarHelper.isIntervalSame(this.recurrence, newValue)){
|
||||
final String defaultUserMessage = "Update of meeting interval is not supported";
|
||||
throw new CalendarParameterUpdateNotSupportedException("meeting.interval", defaultUserMessage);
|
||||
}
|
||||
|
||||
actualChanges.put(recurrenceParamName, newValue);
|
||||
this.recurrence = StringUtils.defaultIfEmpty(newValue, null);
|
||||
}
|
||||
@@ -283,4 +316,8 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> {
|
||||
return endDateLocalDate;
|
||||
}
|
||||
|
||||
public boolean isStartDateBefore(final LocalDate newStartDate) {
|
||||
if (this.startDate != null && newStartDate != null && getStartDateLocalDate().isBefore(newStartDate)) { return true; }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -20,4 +20,6 @@ public interface CalendarInstanceRepository extends JpaRepository<CalendarInstan
|
||||
|
||||
@Query("from CalendarInstance ci where ci.entityId = :loanId and ci.entityTypeId = :entityTypeId")
|
||||
CalendarInstance findCalendarInstaneByLoanId(@Param("loanId") Long loanId, @Param("entityTypeId") Integer entityTypeId);
|
||||
|
||||
Collection<CalendarInstance> findByCalendarIdAndEntityTypeId(Long calendarId, Integer entityTypeId);
|
||||
}
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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.calendar.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeException} thrown when Calendar resources are not found.
|
||||
*/
|
||||
public class CalendarDateException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public CalendarDateException(String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg.calendar." + postFix , defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* 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.calendar.exception;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.exception.AbstractPlatformDomainRuleException;
|
||||
|
||||
/**
|
||||
* A {@link RuntimeException} thrown when Calendar resources are not found.
|
||||
*/
|
||||
public class CalendarParameterUpdateNotSupportedException extends AbstractPlatformDomainRuleException {
|
||||
|
||||
public CalendarParameterUpdateNotSupportedException(String postFix, String defaultUserMessage, Object... defaultUserMessageArgs) {
|
||||
super("error.msg.calendar.update.of." + postFix + ".is.not.supported", defaultUserMessage, defaultUserMessageArgs);
|
||||
}
|
||||
}
|
||||
+1
@@ -221,6 +221,7 @@ public class CalendarCommandFromApiJsonDeserializer extends AbstractFromApiJsonD
|
||||
CALENDAR_SUPPORTED_PARAMETERS.DURATION.getValue(), element);
|
||||
baseDataValidator.reset().parameter(CALENDAR_SUPPORTED_PARAMETERS.DURATION.getValue()).value(duration).ignoreIfNull();
|
||||
}
|
||||
//TODO: AA do not allow to change calendar type.
|
||||
if (this.fromApiJsonHelper.parameterExists(CALENDAR_SUPPORTED_PARAMETERS.TYPE_ID.getValue(), element)) {
|
||||
final Integer typeId = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(CALENDAR_SUPPORTED_PARAMETERS.TYPE_ID.getValue(),
|
||||
element);
|
||||
|
||||
+77
-40
@@ -33,27 +33,30 @@ import org.mifosplatform.portfolio.loanproduct.domain.PeriodFrequencyType;
|
||||
public class CalendarHelper {
|
||||
|
||||
public static LocalDate getNextRecurringDate(final String recurringRule, final LocalDate seedDate, final LocalDate startDate) {
|
||||
|
||||
final Recur recur = CalendarHelper.getICalRecur(recurringRule);
|
||||
|
||||
if (recur == null) { return null; }
|
||||
|
||||
return getNextRecurringDate(recur, seedDate, startDate);
|
||||
}
|
||||
|
||||
private static LocalDate getNextRecurringDate(final Recur recur, final LocalDate seedDate, final LocalDate startDate) {
|
||||
final DateTime periodStart = new DateTime(startDate.toDate());
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");//Date format in iCal4J is hard coded
|
||||
final String seedDateStr = df.format(seedDate.toDateTimeAtStartOfDay().toDate());
|
||||
|
||||
Date seed = null;
|
||||
try {
|
||||
seed = new Date(seedDateStr, "yyyy-MM-dd");
|
||||
} catch (ParseException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
final Date seed = convertToiCal4JCompatibleDate(seedDate);
|
||||
final Date nextRecDate = recur.getNextDate(seed, periodStart);
|
||||
return nextRecDate == null ? null : new LocalDate(nextRecDate);
|
||||
}
|
||||
|
||||
private static Date convertToiCal4JCompatibleDate(final LocalDate inputDate){
|
||||
// Date format in iCal4J is hard coded
|
||||
Date formattedDate = null;
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
final String seedDateStr = df.format(inputDate.toDateTimeAtStartOfDay().toDate());
|
||||
try {
|
||||
formattedDate = new Date(seedDateStr, "yyyy-MM-dd");
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return formattedDate;
|
||||
}
|
||||
|
||||
public static Collection<LocalDate> getRecurringDates(final String recurringRule, final LocalDate seedDate, final LocalDate endDate) {
|
||||
|
||||
@@ -79,16 +82,14 @@ public class CalendarHelper {
|
||||
|
||||
final Recur recur = CalendarHelper.getICalRecur(recurringRule);
|
||||
|
||||
return getRecurringDates(recur, seedDate, periodStartDate,
|
||||
periodEndDate, maxCount);
|
||||
}
|
||||
|
||||
private static Collection<LocalDate> getRecurringDates(final Recur recur, final LocalDate seedDate, final LocalDate periodStartDate,
|
||||
final LocalDate periodEndDate, final int maxCount) {
|
||||
if (recur == null) { return null; }
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
|
||||
final String seedDateStr = df.format(seedDate.toDateTimeAtStartOfDay().toDate());
|
||||
Date seed = null;
|
||||
try {
|
||||
seed = new Date(seedDateStr, "yyyy-MM-dd");
|
||||
} catch (ParseException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
final Date seed = convertToiCal4JCompatibleDate(seedDate);
|
||||
final DateTime periodStart = new DateTime(periodStartDate.toDate());
|
||||
final DateTime periodEnd = new DateTime(periodEndDate.toDate());
|
||||
|
||||
@@ -207,7 +208,15 @@ public class CalendarHelper {
|
||||
|
||||
public static boolean isValidRedurringDate(final String recurringRule, final LocalDate seedDate, final LocalDate date){
|
||||
|
||||
final Collection<LocalDate> recurDate = getRecurringDates(recurringRule, seedDate, date, date.plusDays(1), 1);
|
||||
final Recur recur = CalendarHelper.getICalRecur(recurringRule);
|
||||
if (recur == null) { return false; }
|
||||
|
||||
return isValidRedurringDate(recur, seedDate, date);
|
||||
}
|
||||
|
||||
public static boolean isValidRedurringDate(final Recur recur, final LocalDate seedDate, final LocalDate date){
|
||||
|
||||
final Collection<LocalDate> recurDate = getRecurringDates(recur, seedDate, date, date.plusDays(1), 1);
|
||||
return (recurDate == null || recurDate.isEmpty()) ? false : true;
|
||||
}
|
||||
|
||||
@@ -287,22 +296,50 @@ public class CalendarHelper {
|
||||
if(recur.getFrequency().equals(Recur.DAILY)){
|
||||
recur.setFrequency(frequency);
|
||||
}
|
||||
|
||||
final DateTime periodStart = new DateTime(startDate.toDate());
|
||||
final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");//Date format in iCal4J is hard coded
|
||||
final String seedDateStr = df.format(seedDate.toDateTimeAtStartOfDay().toDate());
|
||||
|
||||
Date seed = null;
|
||||
try {
|
||||
seed = new Date(seedDateStr, "yyyy-MM-dd");
|
||||
} catch (ParseException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
final Date nextRecDate = recur.getNextDate(seed, periodStart);
|
||||
final LocalDate firstRepaymentDate = nextRecDate == null ? null : new LocalDate(nextRecDate);
|
||||
|
||||
final LocalDate firstRepaymentDate = getNextRecurringDate(recur, seedDate, startDate);
|
||||
|
||||
return firstRepaymentDate;
|
||||
}
|
||||
|
||||
public static LocalDate getNewRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate, final LocalDate oldRepaymentDate, final Integer loanRepaymentInterval, final String frequency){
|
||||
final Recur recur = CalendarHelper.getICalRecur(recurringRule);
|
||||
if (recur == null) { return null; }
|
||||
|
||||
if(isValidRedurringDate(recur, seedDate, oldRepaymentDate)){
|
||||
return oldRepaymentDate;
|
||||
}
|
||||
/* Recurring dates should follow loanRepaymentInterval.
|
||||
e.g. The weekly meeting will have interval of 1, if the loan product with fortnightly frequency will have interval of 2,
|
||||
to generate right set of meeting dates reset interval same as loan repayment interval.
|
||||
*/
|
||||
recur.setInterval(loanRepaymentInterval);
|
||||
|
||||
/*Recurring dates should follow loanRepayment frequency.
|
||||
//e.g. daily meeting frequency should support all loan products with any type of frequency.
|
||||
to generate right set of meeting dates reset frequency same as loan repayment frequency.
|
||||
*/
|
||||
if(recur.getFrequency().equals(Recur.DAILY)){
|
||||
recur.setFrequency(frequency);
|
||||
}
|
||||
|
||||
final LocalDate newRepaymentDate = getNextRecurringDate(recur, seedDate, oldRepaymentDate);
|
||||
return newRepaymentDate;
|
||||
}
|
||||
|
||||
public static boolean isFrequencySame(final String oldRRule, final String newRRule) {
|
||||
Recur oldRecur = getICalRecur(oldRRule);
|
||||
Recur newRecur = getICalRecur(newRRule);
|
||||
|
||||
if (oldRecur == null || oldRecur.getFrequency() == null || newRecur == null || newRecur.getFrequency() == null) { return false; }
|
||||
return oldRecur.getFrequency().equals(newRecur.getFrequency());
|
||||
}
|
||||
|
||||
public static boolean isIntervalSame(final String oldRRule, final String newRRule) {
|
||||
Recur oldRecur = getICalRecur(oldRRule);
|
||||
Recur newRecur = getICalRecur(newRRule);
|
||||
|
||||
if (oldRecur == null || oldRecur.getFrequency() == null || newRecur == null || newRecur.getFrequency() == null) { return false; }
|
||||
return (oldRecur.getInterval() == newRecur.getInterval());
|
||||
}
|
||||
}
|
||||
|
||||
+22
-1
@@ -5,19 +5,24 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.calendar.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarRepository;
|
||||
import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException;
|
||||
import org.mifosplatform.portfolio.calendar.serialization.CalendarCommandFromApiJsonDeserializer;
|
||||
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
@Service
|
||||
public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWritePlatformService {
|
||||
@@ -25,14 +30,18 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
|
||||
private final CalendarRepository calendarRepository;
|
||||
private final CalendarCommandFromApiJsonDeserializer fromApiJsonDeserializer;
|
||||
private final CalendarInstanceRepository calendarInstanceRepository;
|
||||
private final LoanWritePlatformService loanWritePlatformService;
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
|
||||
@Autowired
|
||||
public CalendarWritePlatformServiceJpaRepositoryImpl(final CalendarRepository calendarRepository,
|
||||
final CalendarCommandFromApiJsonDeserializer fromApiJsonDeserializer,
|
||||
final CalendarInstanceRepository calendarInstanceRepository) {
|
||||
final CalendarInstanceRepository calendarInstanceRepository, final LoanWritePlatformService loanWritePlatformService, final ConfigurationDomainService configurationDomainService) {
|
||||
this.calendarRepository = calendarRepository;
|
||||
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
|
||||
this.calendarInstanceRepository = calendarInstanceRepository;
|
||||
this.loanWritePlatformService = loanWritePlatformService;
|
||||
this.configurationDomainService = configurationDomainService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -66,6 +75,18 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
this.calendarRepository.saveAndFlush(calendarForUpdate);
|
||||
|
||||
if(this.configurationDomainService.isRescheduleFutureRepaymentsEnabled()){
|
||||
//In this approach loans following this meeting calendar will be affected immediately.
|
||||
//get all calendar instances following this meeting calendar and calendar entity type is loan
|
||||
final Collection<CalendarInstance> loanCalendarInstances = this.calendarInstanceRepository.findByCalendarIdAndEntityTypeId(calendarId, CalendarEntityType.LOANS.getValue());
|
||||
|
||||
if (!CollectionUtils.isEmpty(loanCalendarInstances)) {
|
||||
// update all loans which are following this meeting calendar
|
||||
this.loanWritePlatformService.updateRepaymentsSchedule(calendarForUpdate, loanCalendarInstances);
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CommandProcessingResultBuilder() //
|
||||
|
||||
+25
-1
@@ -51,6 +51,7 @@ import org.mifosplatform.organisation.monetary.domain.ApplicationCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.mifosplatform.organisation.monetary.domain.Money;
|
||||
import org.mifosplatform.organisation.staff.domain.Staff;
|
||||
import org.mifosplatform.portfolio.calendar.service.CalendarHelper;
|
||||
import org.mifosplatform.portfolio.charge.exception.LoanChargeCannotBeAddedException;
|
||||
import org.mifosplatform.portfolio.client.domain.Client;
|
||||
import org.mifosplatform.portfolio.collateral.data.CollateralData;
|
||||
@@ -190,7 +191,6 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
@JoinColumn(name = "disbursedon_userid", nullable = true)
|
||||
private AppUser disbursedBy;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "closedon_date")
|
||||
private Date closedOnDate;
|
||||
@@ -2325,4 +2325,28 @@ public class Loan extends AbstractPersistable<Long> {
|
||||
return this.closedOnDate;
|
||||
}
|
||||
|
||||
public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule) {
|
||||
//update repayment dates of repayment schedule installaments
|
||||
LocalDate tmpFromDate = this.getDisbursementDate();//first repayment's from date is same as disbursement date.
|
||||
LocalDate newRepaymentDate = null;
|
||||
for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : this.repaymentScheduleInstallments) {
|
||||
final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
|
||||
//FIXME: AA this won't update repayment dates before current date.
|
||||
if (oldDueDate.isAfter(meetingStartDate) && oldDueDate.isAfter(DateUtils.getLocalDateOfTenant())) {
|
||||
final PeriodFrequencyType repaymentPeriodFrequencyType = this.loanRepaymentScheduleDetail.getRepaymentPeriodFrequencyType();
|
||||
final String frequency = CalendarHelper.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType);
|
||||
final Integer loanRepaymentInterval = this.loanRepaymentScheduleDetail.getRepayEvery();
|
||||
// reset repayment date with new meeting date
|
||||
newRepaymentDate = CalendarHelper.getNewRepaymentMeetingDate(recuringRule, meetingStartDate, oldDueDate, loanRepaymentInterval, frequency);
|
||||
loanRepaymentScheduleInstallment.updateDueDate(newRepaymentDate);
|
||||
//reset from date to get actual daysInPeriod
|
||||
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
|
||||
tmpFromDate = newRepaymentDate;//update with new repayment date
|
||||
}else{
|
||||
tmpFromDate = oldDueDate;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+14
-2
@@ -35,11 +35,11 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "fromdate", nullable = true)
|
||||
private final Date fromDate;
|
||||
private Date fromDate;
|
||||
|
||||
@Temporal(TemporalType.DATE)
|
||||
@Column(name = "duedate", nullable = false)
|
||||
private final Date dueDate;
|
||||
private Date dueDate;
|
||||
|
||||
@Column(name = "principal_amount", scale = 6, precision = 19, nullable = true)
|
||||
private BigDecimal principal;
|
||||
@@ -521,4 +521,16 @@ public final class LoanRepaymentScheduleInstallment extends AbstractAuditableCus
|
||||
this.obligationsMetOnDate = transactionDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateDueDate(final LocalDate newDueDate){
|
||||
if(newDueDate != null){
|
||||
this.dueDate = newDueDate.toDate();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateFromDate(final LocalDate newFromDate){
|
||||
if(newFromDate != null){
|
||||
this.fromDate = newFromDate.toDate();
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.domain;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
@@ -19,4 +20,7 @@ public interface LoanRepository extends JpaRepository<Loan, Long>,
|
||||
|
||||
@Query("from Loan loan where loan.client.id = :clientId")
|
||||
List<Loan> findLoanByClientId(@Param("clientId") Long clientId);
|
||||
|
||||
@Query("from Loan loan where loan.id IN :ids and loan.loanStatus IN :loanStatuses and loan.loanType IN :loanTypes")
|
||||
List<Loan> findByIdsAndLoanStatusAndLoanType(@Param("ids") Collection<Long> ids, @Param("loanStatuses") Collection<Integer> loanStatuses, @Param("loanTypes") Collection<Integer> loanTypes);
|
||||
}
|
||||
+5
@@ -5,10 +5,13 @@
|
||||
*/
|
||||
package org.mifosplatform.portfolio.loanaccount.service;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mifosplatform.infrastructure.core.api.JsonCommand;
|
||||
import org.mifosplatform.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.mifosplatform.portfolio.calendar.domain.Calendar;
|
||||
import org.mifosplatform.portfolio.calendar.domain.CalendarInstance;
|
||||
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkDisbursalCommand;
|
||||
import org.mifosplatform.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
|
||||
|
||||
@@ -47,4 +50,6 @@ public interface LoanWritePlatformService {
|
||||
CommandProcessingResult bulkLoanReassignment(JsonCommand command);
|
||||
|
||||
CommandProcessingResult removeLoanOfficer(Long loanId, JsonCommand command);
|
||||
|
||||
void updateRepaymentsSchedule(Calendar calendar, Collection<CalendarInstance> loanCalendarInstances);
|
||||
}
|
||||
+24
@@ -8,6 +8,7 @@ package org.mifosplatform.portfolio.loanaccount.service;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -55,6 +56,7 @@ import org.mifosplatform.portfolio.loanaccount.domain.LoanStatus;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanSummaryWrapper;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanTransactionRepository;
|
||||
import org.mifosplatform.portfolio.loanaccount.domain.LoanType;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanNotFoundException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerAssignmentException;
|
||||
import org.mifosplatform.portfolio.loanaccount.exception.LoanOfficerUnassignmentException;
|
||||
@@ -982,4 +984,26 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
existingTransactionIds, existingReversedTransactionIds);
|
||||
journalEntryWritePlatformService.createJournalEntriesForLoan(accountingBridgeData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRepaymentsSchedule(final Calendar calendar, Collection<CalendarInstance> loanCalendarInstances) {
|
||||
|
||||
final Collection<Integer> loanStatuses = new ArrayList<Integer>(Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(),
|
||||
LoanStatus.APPROVED.getValue(), LoanStatus.ACTIVE.getValue()));
|
||||
final Collection<Integer> loanTypes = new ArrayList<Integer>(Arrays.asList(LoanType.GROUP.getValue(), LoanType.JLG.getValue()));
|
||||
final Collection<Long> loanIds = new ArrayList<Long>(loanCalendarInstances.size());
|
||||
//loop through loanCalendarInstances to get loan ids
|
||||
for (CalendarInstance calendarInstance : loanCalendarInstances) {
|
||||
loanIds.add(calendarInstance.getEntityId());
|
||||
}
|
||||
|
||||
final List<Loan> loans = this.loanRepository.findByIdsAndLoanStatusAndLoanType(loanIds, loanStatuses, loanTypes);
|
||||
//loop through each loan to reschedule the repayment dates
|
||||
for (Loan loan : loans) {
|
||||
if(loan != null){
|
||||
loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence());
|
||||
this.loanRepository.save(loan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user