Repository: incubator-fineract Updated Branches: refs/heads/develop 9c38078de -> bba8ca47b
http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java index e3d6e4e..64040af 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanReschedulePreviewPlatformServiceImpl.java @@ -50,6 +50,7 @@ import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanResch import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.exception.LoanRescheduleRequestNotFoundException; +import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService; import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.springframework.beans.factory.annotation.Autowired; @@ -66,6 +67,7 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP private final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService; private final CalendarInstanceRepository calendarInstanceRepository; private final FloatingRatesReadPlatformService floatingRatesReadPlatformService; + private final LoanUtilService loanUtilService; @Autowired public LoanReschedulePreviewPlatformServiceImpl(final LoanRescheduleRequestRepository loanRescheduleRequestRepository, @@ -74,7 +76,7 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanScheduleHistoryWritePlatformService loanScheduleHistoryWritePlatformService, final CalendarInstanceRepository calendarInstanceRepository, - final FloatingRatesReadPlatformService floatingRatesReadPlatformService) { + final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService) { this.loanRescheduleRequestRepository = loanRescheduleRequestRepository; this.applicationCurrencyRepository = applicationCurrencyRepository; this.configurationDomainService = configurationDomainService; @@ -83,6 +85,7 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP this.loanScheduleHistoryWritePlatformService = loanScheduleHistoryWritePlatformService; this.calendarInstanceRepository = calendarInstanceRepository; this.floatingRatesReadPlatformService = floatingRatesReadPlatformService; + this.loanUtilService = loanUtilService; } @Override @@ -122,9 +125,17 @@ public class LoanReschedulePreviewPlatformServiceImpl implements LoanRescheduleP loanCalendar = loanCalendarInstance.getCalendar(); } final FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan); + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar); + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + + } LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod, loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, - loanCalendar, floatingRateDTO); + loanCalendar, floatingRateDTO, isSkipRepaymentOnFirstMonth, numberOfDays); LoanRescheduleModel loanRescheduleModelWithOldPeriods = LoanRescheduleModel.createWithSchedulehistory(loanRescheduleModel, oldPeriods); return loanRescheduleModelWithOldPeriods; http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java index b1de9a9..9ab13ee 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/service/LoanRescheduleRequestWritePlatformServiceImpl.java @@ -355,9 +355,17 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche loanCalendar = loanCalendarInstance.getCalendar(); } FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan); + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar); + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + + } LoanRescheduleModel loanRescheduleModel = new DefaultLoanReschedulerFactory().reschedule(mathContext, interestMethod, loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, - loanCalendar, floatingRateDTO); + loanCalendar, floatingRateDTO, isSkipRepaymentOnFirstMonth, numberOfDays); final Collection<LoanRescheduleModelRepaymentPeriod> periods = loanRescheduleModel.getPeriods(); List<LoanRepaymentScheduleInstallment> repaymentScheduleInstallments = loan.getRepaymentScheduleInstallments(); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index fdda0de..3109a56 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -109,10 +109,11 @@ public final class LoanEventApiJsonValidator { throwExceptionIfValidationWarningsExist(dataValidationErrors); } - public void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance) { + public void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance, + Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) { if (null != calendarInstance) { final Calendar calendar = calendarInstance.getCalendar(); - if (!calendar.isValidRecurringDate(actualDisbursementDate)) { + if (!calendar.isValidRecurringDate(actualDisbursementDate, isSkipRepaymentOnFirstMonth, numberOfDays)) { // Disbursement date should fall on a meeting date final String errorMessage = "Expected disbursement date '" + actualDisbursementDate.toString() + "' does not fall on a meeting date."; http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java index e27edf6..572b559 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java @@ -889,13 +889,21 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa } checkClientOrGroupActive(loan); - + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; // validate expected disbursement date against meeting date if (loan.isSyncDisbursementWithMeeting() && (loan.isGroupLoan() || loan.isJLGLoan())) { final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); final Calendar calendar = calendarInstance.getCalendar(); - this.loanScheduleAssembler.validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar); + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if (isSkipRepaymentOnFirstMonthEnabled) { + isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar); + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + } + this.loanScheduleAssembler.validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar, + isSkipRepaymentOnFirstMonth, numberOfDays); + } final Map<String, Object> changes = loan.loanApplicationApproval(currentUser, command, disbursementDataArray, http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java index 9689baf..ce06ca4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java @@ -42,11 +42,13 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType; import org.apache.fineract.portfolio.calendar.domain.CalendarHistory; import org.apache.fineract.portfolio.calendar.domain.CalendarInstance; import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; +import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService; import org.apache.fineract.portfolio.calendar.service.CalendarUtils; import org.apache.fineract.portfolio.floatingrates.data.FloatingRateDTO; import org.apache.fineract.portfolio.floatingrates.data.FloatingRatePeriodData; import org.apache.fineract.portfolio.floatingrates.exception.FloatingRateNotFoundException; import org.apache.fineract.portfolio.floatingrates.service.FloatingRatesReadPlatformService; +import org.apache.fineract.portfolio.group.domain.Group; import org.apache.fineract.portfolio.loanaccount.api.LoanApiConstants; import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO; import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO; @@ -72,13 +74,14 @@ public class LoanUtilService { private final LoanScheduleGeneratorFactory loanScheduleFactory; private final FloatingRatesReadPlatformService floatingRatesReadPlatformService; private final FromJsonHelper fromApiJsonHelper; + private final CalendarReadPlatformService calendarReadPlatformService; @Autowired public LoanUtilService(final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository, final CalendarInstanceRepository calendarInstanceRepository, final ConfigurationDomainService configurationDomainService, final HolidayRepository holidayRepository, final WorkingDaysRepositoryWrapper workingDaysRepository, final LoanScheduleGeneratorFactory loanScheduleFactory, final FloatingRatesReadPlatformService floatingRatesReadPlatformService, - final FromJsonHelper fromApiJsonHelper) { + final FromJsonHelper fromApiJsonHelper, final CalendarReadPlatformService calendarReadPlatformService) { this.applicationCurrencyRepository = applicationCurrencyRepository; this.calendarInstanceRepository = calendarInstanceRepository; this.configurationDomainService = configurationDomainService; @@ -87,6 +90,7 @@ public class LoanUtilService { this.loanScheduleFactory = loanScheduleFactory; this.floatingRatesReadPlatformService = floatingRatesReadPlatformService; this.fromApiJsonHelper = fromApiJsonHelper; + this.calendarReadPlatformService = calendarReadPlatformService; } public ScheduleGeneratorDTO buildScheduleGeneratorDTO(final Loan loan, final LocalDate recalculateFrom) { @@ -125,13 +129,46 @@ public class LoanUtilService { } final Boolean isInterestChargedFromDateAsDisbursementDateEnabled = this.configurationDomainService.isInterestChargedFromDateSameAsDisbursementDate(); FloatingRateDTO floatingRateDTO = constructFloatingRateDTO(loan); + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), calendar); + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + } + ScheduleGeneratorDTO scheduleGeneratorDTO = new ScheduleGeneratorDTO(loanScheduleFactory, applicationCurrency, calculatedRepaymentsStartingFromDate, holidayDetails, restCalendarInstance, compoundingCalendarInstance, recalculateFrom, - overdurPenaltyWaitPeriod, floatingRateDTO, calendar, calendarHistoryDataWrapper, isInterestChargedFromDateAsDisbursementDateEnabled); + overdurPenaltyWaitPeriod, floatingRateDTO, calendar, calendarHistoryDataWrapper, isInterestChargedFromDateAsDisbursementDateEnabled, + numberOfDays, isSkipRepaymentOnFirstMonth); - return scheduleGeneratorDTO; + return scheduleGeneratorDTO; } + public Boolean isLoanRepaymentsSyncWithMeeting(final Group group, final Calendar calendar) { + Boolean isSkipRepaymentOnFirstMonth = false; + Long entityId = null; + Long entityTypeId = null; + + if (group != null) { + if (group.getParent() != null) { + entityId = group.getParent().getId(); + entityTypeId = CalendarEntityType.CENTERS.getValue().longValue(); + } else { + entityId = group.getId(); + entityTypeId = CalendarEntityType.GROUPS.getValue().longValue(); + } + } + + if (entityId == null || calendar == null) { + return isSkipRepaymentOnFirstMonth; + } + isSkipRepaymentOnFirstMonth = this.calendarReadPlatformService + .isCalendarAssociatedWithEntity(entityId, calendar.getId(), entityTypeId); + return isSkipRepaymentOnFirstMonth; + } + + public LocalDate getCalculatedRepaymentsStartingFromDate(final Loan loan) { final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); @@ -209,8 +246,15 @@ public class LoanUtilService { final Integer repayEvery = repaymentScheduleDetails.getRepayEvery(); final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails .getRepaymentPeriodFrequencyType()); + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); + isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), calendar); + } calculatedRepaymentsStartingFromDate = CalendarUtils.getFirstRepaymentMeetingDate(calendar, actualDisbursementDate, - repayEvery, frequency); + repayEvery, frequency, isSkipRepaymentOnFirstMonth, numberOfDays); } } } @@ -230,8 +274,15 @@ public class LoanUtilService { final Integer repayEvery = repaymentScheduleDetails.getRepayEvery(); final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentScheduleDetails .getRepaymentPeriodFrequencyType()); + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); + isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), historyList.get(0).getCalendar()); + } calculatedRepaymentsStartingFromDate = CalendarUtils.getNextRepaymentMeetingDate(historyList.get(0).getRecurrence(), historyList.get(0).getStartDateLocalDate(), - actualDisbursementDate, repayEvery, frequency, workingDays); + actualDisbursementDate, repayEvery, frequency, workingDays, isSkipRepaymentOnFirstMonth, numberOfDays); } } return calculatedRepaymentsStartingFromDate; http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java index ba1e9e6..639836a 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java @@ -310,14 +310,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // check for product mix validations checkForProductMixRestrictions(loan); + + LocalDate recalculateFrom = null; + ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); // validate actual disbursement date against meeting date final CalendarInstance calendarInstance = this.calendarInstanceRepository.findCalendarInstaneByEntityId(loan.getId(), CalendarEntityType.LOANS.getValue()); if (loan.isSyncDisbursementWithMeeting()) { - final LocalDate actualDisbursementDate = command.localDateValueOfParameterNamed("actualDisbursementDate"); - this.loanEventApiJsonValidator.validateDisbursementDateWithMeetingDate(actualDisbursementDate, calendarInstance); + this.loanEventApiJsonValidator.validateDisbursementDateWithMeetingDate(actualDisbursementDate, calendarInstance, + scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth(), scheduleGeneratorDTO.getNumberOfdays()); } this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BUSINESS_EVENTS.LOAN_DISBURSAL, @@ -356,9 +359,6 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan.getLoanTransactions().add(disbursementTransaction); } - LocalDate recalculateFrom = null; - ScheduleGeneratorDTO scheduleGeneratorDTO = this.loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); - regenerateScheduleOnDisbursement(command, loan, recalculateSchedule, scheduleGeneratorDTO, nextPossibleRepaymentDate, rescheduledRepaymentDate); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { this.loanScheduleHistoryWritePlatformService.createAndSaveLoanScheduleArchive(loan.fetchRepaymentScheduleInstallments(), @@ -1988,11 +1988,22 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf // loop through each loan to reschedule the repayment dates for (final Loan loan : loans) { if (loan != null) { + if(loan.getExpectedFirstRepaymentOnDate() != null && loan.getExpectedFirstRepaymentOnDate().equals(presentMeetingDate)){ final String defaultUserMessage = "Meeting calendar date update is not supported since its a first repayment date"; throw new CalendarParameterUpdateNotSupportedException("meeting.for.first.repayment.date", defaultUserMessage, loan.getExpectedFirstRepaymentOnDate(), presentMeetingDate); } + + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), calendar); + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + } + + holidays = this.holidayRepository.findByOfficeIdAndGreaterThanDate(loan.getOfficeId(), loan.getDisbursementDate().toDate()); if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) { ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom); @@ -2003,10 +2014,11 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf loan.fetchRepaymentScheduleInstallments(), loan, null); } else if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) { loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled, - holidays, workingDays, reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate); + holidays, workingDays, reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate, + isSkipRepaymentOnFirstMonth, numberOfDays); } else { loan.updateLoanRepaymentScheduleDates(calendar.getStartDateLocalDate(), calendar.getRecurrence(), isHolidayEnabled, - holidays, workingDays); + holidays, workingDays, isSkipRepaymentOnFirstMonth, numberOfDays); } saveLoanWithDataIntegrityViolationChecks(loan); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/domain/Meeting.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/domain/Meeting.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/domain/Meeting.java index be914ed..e33459e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/domain/Meeting.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/domain/Meeting.java @@ -77,9 +77,11 @@ public class Meeting extends AbstractPersistable<Long> { this.meetingDate = meetingDate; } - public static Meeting createNew(final CalendarInstance calendarInstance, final Date meetingDate, Boolean isTransactionDateOnNonMeetingDate) { + public static Meeting createNew(final CalendarInstance calendarInstance, final Date meetingDate, Boolean isTransactionDateOnNonMeetingDate, + final boolean isSkipRepaymentOnFirstMonth, final int numberOfDays) { - if (!isTransactionDateOnNonMeetingDate && !isValidMeetingDate(calendarInstance, meetingDate)) { throw new NotValidRecurringDateException("meeting", "The date '" + if (!isTransactionDateOnNonMeetingDate && !isValidMeetingDate(calendarInstance, meetingDate,isSkipRepaymentOnFirstMonth, numberOfDays)) + { throw new NotValidRecurringDateException("meeting", "The date '" + meetingDate + "' is not a valid meeting date.", meetingDate); } return new Meeting(calendarInstance, meetingDate); } @@ -93,7 +95,7 @@ public class Meeting extends AbstractPersistable<Long> { this.clientsAttendance = new HashSet<>(clientsAttendance); } - public Map<String, Object> update(final JsonCommand command) { + public Map<String, Object> update(final JsonCommand command, final boolean isSkipRepaymentOnFirstMonth, final int numberOfDays) { final Map<String, Object> actualChanges = new LinkedHashMap<>(9); final String dateFormatAsInput = command.dateFormat(); final String localeAsInput = command.locale(); @@ -106,7 +108,7 @@ public class Meeting extends AbstractPersistable<Long> { actualChanges.put("locale", localeAsInput); this.meetingDate = newValue.toDate(); - if (!isValidMeetingDate(this.calendarInstance, this.meetingDate)) { throw new NotValidRecurringDateException("meeting", + if (!isValidMeetingDate(this.calendarInstance, this.meetingDate, isSkipRepaymentOnFirstMonth, numberOfDays)) { throw new NotValidRecurringDateException("meeting", "Not a valid meeting date", this.meetingDate); } } @@ -177,14 +179,15 @@ public class Meeting extends AbstractPersistable<Long> { return this.meetingDate != null && newStartDate != null && getMeetingDateLocalDate().isBefore(newStartDate) ? true : false; } - private static boolean isValidMeetingDate(final CalendarInstance calendarInstance, final Date meetingDate) { + private static boolean isValidMeetingDate(final CalendarInstance calendarInstance, final Date meetingDate, + final boolean isSkipRepaymentOnFirstMonth, final int numberOfDays) { final Calendar calendar = calendarInstance.getCalendar(); LocalDate meetingDateLocalDate = null; if (meetingDate != null) { meetingDateLocalDate = LocalDate.fromDateFields(meetingDate); } - if (meetingDateLocalDate == null || !calendar.isValidRecurringDate(meetingDateLocalDate)) { return false; } + if (meetingDateLocalDate == null || !calendar.isValidRecurringDate(meetingDateLocalDate, isSkipRepaymentOnFirstMonth, numberOfDays)) { return false; } return true; } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java index a781129..5ed8e10 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/meeting/service/MeetingWritePlatformServiceJpaRepositoryImpl.java @@ -31,11 +31,13 @@ import java.util.Collection; import java.util.Date; import java.util.Map; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.apache.fineract.portfolio.calendar.data.CalendarData; import org.apache.fineract.portfolio.calendar.domain.Calendar; import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType; import org.apache.fineract.portfolio.calendar.domain.CalendarInstance; @@ -43,6 +45,7 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; import org.apache.fineract.portfolio.calendar.domain.CalendarRepository; import org.apache.fineract.portfolio.calendar.exception.CalendarInstanceNotFoundException; import org.apache.fineract.portfolio.calendar.exception.CalendarNotFoundException; +import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService; import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.ClientRepository; import org.apache.fineract.portfolio.group.domain.Group; @@ -73,12 +76,15 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit private final ClientRepository clientRepository; private final GroupRepository groupRepository; private final FromJsonHelper fromApiJsonHelper; + private final ConfigurationDomainService configurationDomainService; + private final CalendarReadPlatformService calendarReadPlatformService; @Autowired public MeetingWritePlatformServiceJpaRepositoryImpl(final MeetingRepositoryWrapper meetingRepositoryWrapper, final MeetingRepository meetingRepository, final MeetingDataValidator meetingDataValidator, final CalendarInstanceRepository calendarInstanceRepository, final CalendarRepository calendarRepository, - final ClientRepository clientRepository, final GroupRepository groupRepository, final FromJsonHelper fromApiJsonHelper) { + final ClientRepository clientRepository, final GroupRepository groupRepository, final FromJsonHelper fromApiJsonHelper, + final ConfigurationDomainService configurationDomainService, final CalendarReadPlatformService calendarReadPlatformService) { this.meetingRepositoryWrapper = meetingRepositoryWrapper; this.meetingRepository = meetingRepository; this.meetingDataValidator = meetingDataValidator; @@ -87,6 +93,8 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit this.clientRepository = clientRepository; this.groupRepository = groupRepository; this.fromApiJsonHelper = fromApiJsonHelper; + this.configurationDomainService = configurationDomainService; + this.calendarReadPlatformService = calendarReadPlatformService; } @Override @@ -96,11 +104,25 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit final Date meetingDate = command.DateValueOfParameterNamed(meetingDateParamName); final Boolean isTransactionDateOnNonMeetingDate = false; + /*Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + isSkipRepaymentOnFirstMonth = isLoanRepaymentsSyncWithMeeting(loan.group(), calendar); + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + }*/ try { final CalendarInstance calendarInstance = getCalendarInstance(command); // create new meeting - final Meeting newMeeting = Meeting.createNew(calendarInstance, meetingDate, isTransactionDateOnNonMeetingDate); + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + if(calendarInstance != null){isSkipRepaymentOnFirstMonth = true;} + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + } + final Meeting newMeeting = Meeting.createNew(calendarInstance, meetingDate, isTransactionDateOnNonMeetingDate, isSkipRepaymentOnFirstMonth, numberOfDays); final Collection<ClientAttendance> clientsAttendance = getClientsAttendance(newMeeting, command); if (clientsAttendance != null && !clientsAttendance.isEmpty()) { @@ -214,9 +236,23 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit @Override public CommandProcessingResult updateMeeting(final JsonCommand command) { this.meetingDataValidator.validateForUpdate(command); - + + final CalendarInstance calendarInstance = getCalendarInstance(command); + + // create new meeting + Boolean isSkipRepaymentOnFirstMonth = false; + + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService + .isSkippingMeetingOnFirstDayOfMonthEnabled(); + if (isSkipRepaymentOnFirstMonthEnabled) { + if (calendarInstance != null) { + isSkipRepaymentOnFirstMonth = true; + numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); + } + } final Meeting meetingForUpdate = this.meetingRepositoryWrapper.findOneWithNotFoundDetection(command.entityId()); - final Map<String, Object> changes = meetingForUpdate.update(command); + final Map<String, Object> changes = meetingForUpdate.update(command, isSkipRepaymentOnFirstMonth, numberOfDays); try { if (!changes.isEmpty()) { @@ -280,10 +316,23 @@ public class MeetingWritePlatformServiceJpaRepositoryImpl implements MeetingWrit try { final CalendarInstance calendarInstance = getCalendarInstance(command); - final Meeting meeting = this.meetingRepository.findByCalendarInstanceIdAndMeetingDate(calendarInstance.getId(), meetingDate); - + + final Meeting meeting = this.meetingRepository + .findByCalendarInstanceIdAndMeetingDate(calendarInstance.getId(), meetingDate); + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService + .isSkippingMeetingOnFirstDayOfMonthEnabled(); + if (isSkipRepaymentOnFirstMonthEnabled) { + isSkipRepaymentOnFirstMonth = true; + if (isSkipRepaymentOnFirstMonth) { + numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate() + .intValue(); + } + } // create new meeting - final Meeting newMeeting = (meeting != null) ? meeting : Meeting.createNew(calendarInstance, meetingDate, isTransactionDateOnNonMeetingDate); + final Meeting newMeeting = (meeting != null) ? meeting : Meeting.createNew(calendarInstance, meetingDate, isTransactionDateOnNonMeetingDate, + isSkipRepaymentOnFirstMonth, numberOfDays); final Collection<ClientAttendance> clientsAttendance = getClientsAttendance(newMeeting, command); if (clientsAttendance != null && !clientsAttendance.isEmpty()) { http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/resources/sql/migrations/core_db/V296__skip_repayment_on first-day_of_month.sql ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/resources/sql/migrations/core_db/V296__skip_repayment_on first-day_of_month.sql b/fineract-provider/src/main/resources/sql/migrations/core_db/V296__skip_repayment_on first-day_of_month.sql new file mode 100644 index 0000000..be3d7a2 --- /dev/null +++ b/fineract-provider/src/main/resources/sql/migrations/core_db/V296__skip_repayment_on first-day_of_month.sql @@ -0,0 +1 @@ +insert into c_configuration(name,value,description) values("skip-repayment-on-first-day-of-month",14,"skipping repayment on first day of month"); \ No newline at end of file