Skip Repayment Date Falling On First Day of Month
Project: http://git-wip-us.apache.org/repos/asf/incubator-fineract/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-fineract/commit/bba8ca47 Tree: http://git-wip-us.apache.org/repos/asf/incubator-fineract/tree/bba8ca47 Diff: http://git-wip-us.apache.org/repos/asf/incubator-fineract/diff/bba8ca47 Branch: refs/heads/develop Commit: bba8ca47be73d7663f8675a1fb061c434ef96797 Parents: 9c38078 Author: jinjurajan <[email protected]> Authored: Wed Mar 23 14:50:47 2016 +0530 Committer: jinjurajan <[email protected]> Committed: Wed Mar 23 14:50:47 2016 +0530 ---------------------------------------------------------------------- .../SkipRepaymentOnMonthFirstTest.java | 151 +++++++++++++++++++ .../domain/ConfigurationDomainService.java | 7 +- .../domain/ConfigurationDomainServiceJpa.java | 13 ++ .../portfolio/calendar/data/CalendarData.java | 4 +- .../portfolio/calendar/domain/Calendar.java | 6 +- .../calendar/domain/CalendarHistory.java | 5 + .../service/CalendarReadPlatformService.java | 2 + .../CalendarReadPlatformServiceImpl.java | 91 +++++++---- .../calendar/service/CalendarUtils.java | 93 +++++++++--- .../CollectionSheetReadPlatformServiceImpl.java | 40 ++++- .../service/CenterReadPlatformServiceImpl.java | 16 +- .../loanaccount/data/ScheduleGeneratorDTO.java | 18 ++- .../portfolio/loanaccount/domain/Loan.java | 23 +-- .../domain/AbstractLoanScheduleGenerator.java | 5 +- .../domain/DefaultScheduledDateGenerator.java | 4 +- .../domain/LoanApplicationTerms.java | 47 ++++-- .../domain/LoanScheduleGenerator.java | 3 +- .../service/LoanScheduleAssembler.java | 47 ++++-- .../domain/DefaultLoanReschedulerFactory.java | 7 +- .../domain/LoanReschedulerFactory.java | 3 +- ...oanReschedulePreviewPlatformServiceImpl.java | 15 +- ...scheduleRequestWritePlatformServiceImpl.java | 10 +- .../LoanEventApiJsonValidator.java | 5 +- ...onWritePlatformServiceJpaRepositoryImpl.java | 12 +- .../loanaccount/service/LoanUtilService.java | 61 +++++++- ...anWritePlatformServiceJpaRepositoryImpl.java | 26 +++- .../portfolio/meeting/domain/Meeting.java | 15 +- ...ngWritePlatformServiceJpaRepositoryImpl.java | 63 +++++++- ...96__skip_repayment_on first-day_of_month.sql | 1 + 29 files changed, 660 insertions(+), 133 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SkipRepaymentOnMonthFirstTest.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SkipRepaymentOnMonthFirstTest.java b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SkipRepaymentOnMonthFirstTest.java new file mode 100644 index 0000000..b863b03 --- /dev/null +++ b/fineract-provider/src/integrationTest/java/org/apache/fineract/integrationtests/SkipRepaymentOnMonthFirstTest.java @@ -0,0 +1,151 @@ +package org.apache.fineract.integrationtests; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +import org.apache.fineract.integrationtests.common.CalendarHelper; +import org.apache.fineract.integrationtests.common.ClientHelper; +import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper; +import org.apache.fineract.integrationtests.common.GroupHelper; +import org.apache.fineract.integrationtests.common.Utils; +import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.junit.Before; +import org.junit.Test; + +import com.jayway.restassured.builder.RequestSpecBuilder; +import com.jayway.restassured.builder.ResponseSpecBuilder; +import com.jayway.restassured.http.ContentType; +import com.jayway.restassured.specification.RequestSpecification; +import com.jayway.restassured.specification.ResponseSpecification; + +import junit.framework.Assert; + +@SuppressWarnings({ "static-access", "rawtypes", "unchecked", "deprecation" }) +public class SkipRepaymentOnMonthFirstTest { + + private ResponseSpecification responseSpec; + private RequestSpecification requestSpec; + private GlobalConfigurationHelper globalConfigurationHelper; + private LoanTransactionHelper loanTransactionHelper; + private CalendarHelper calendarHelper; + + @Before + public void setup() { + Utils.initializeRESTAssured(); + this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + this.requestSpec.header("Authorization", + "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + } + + @Test + public void testSkippingRepaymentOnFirstDayOfMonth() { + this.globalConfigurationHelper = new GlobalConfigurationHelper(this.requestSpec, this.responseSpec); + + // Retrieving All Global Configuration details + final ArrayList<HashMap> globalConfig = this.globalConfigurationHelper + .getAllGlobalConfigurations(this.requestSpec, this.responseSpec); + Assert.assertNotNull(globalConfig); + + String configName = "skip-repayment-on-first-day-of-month"; + boolean newBooleanValue = true; + + for (Integer configIndex = 0; configIndex < (globalConfig.size()); configIndex++) { + if (globalConfig.get(configIndex).get("name").equals(configName)) { + String configId = (globalConfig.get(configIndex).get("id")).toString(); + Integer updateConfigId = this.globalConfigurationHelper.updateEnabledFlagForGlobalConfiguration( + this.requestSpec, this.responseSpec, configId.toString(), newBooleanValue); + Assert.assertNotNull(updateConfigId); + break; + } + } + + } + + @Test + public void checkRepaymentSkipOnFirstDayOfMonth() { + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + Integer groupID = GroupHelper.createGroup(this.requestSpec, this.responseSpec, true); + groupID = GroupHelper.associateClient(this.requestSpec, this.responseSpec, groupID.toString(), + clientID.toString()); + final String startDate = "15 September 2011"; + final String frequency = "3"; // Monthly + final String interval = "1"; //Every One Moth + Integer calendarID = calendarHelper.createMeetingForGroup(requestSpec, responseSpec, groupID, startDate, frequency, + interval, null); + System.out.println("caladerId --------------------" + calendarID); + final Integer loanProductID = createLoanProduct(); + final Integer loanID = applyForLoanApplication(groupID, loanProductID, calendarID, clientID); + System.out.println("loanID----" + loanID); + final ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, + this.responseSpec, loanID); + verifyLoanRepaymentSchedule(loanSchedule); + + } + + private Integer createLoanProduct() { + System.out.println( + "------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------"); + final String loanProductJSON = new LoanProductTestBuilder() // + .withPrincipal("12,000.00") // + .withNumberOfRepayments("4") // + .withRepaymentAfterEvery("1") // + .withRepaymentTypeAsMonth() // + .withinterestRatePerPeriod("1") // + .withInterestRateFrequencyTypeAsMonths() // + .withAmortizationTypeAsEqualInstallments() // + .withInterestTypeAsDecliningBalance() // + .build(null); + return this.loanTransactionHelper.getLoanProductId(loanProductJSON); + } + + private Integer applyForLoanApplication(final Integer groupID, final Integer loanProductID, Integer calendarID, + Integer clientID) { + System.out.println( + "--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------"); + final String loanApplicationJSON = new LoanApplicationTestBuilder() // + .withPrincipal("12,000.00") // + .withLoanTermFrequency("4") // + .withLoanTermFrequencyAsMonths() // + .withNumberOfRepayments("4") // + .withRepaymentEveryAfter("1") // + .withRepaymentFrequencyTypeAsMonths() // + .withInterestRatePerPeriod("2") // + .withAmortizationTypeAsEqualInstallments() // + .withInterestTypeAsDecliningBalance() // + .withInterestCalculationPeriodTypeSameAsRepaymentPeriod() // + .withExpectedDisbursementDate("01 October 2011") // + .withCalendarID(calendarID.toString()) // + .withSubmittedOnDate("01 October 2011") // + .withLoanType("jlg").build(clientID.toString(), groupID.toString(), loanProductID.toString(), null); + System.out.println(loanApplicationJSON); + return this.loanTransactionHelper.getLoanId(loanApplicationJSON); + } + + private void verifyLoanRepaymentSchedule(final ArrayList<HashMap> loanSchedule) { + System.out.println("--------------------VERIFYING THE REPAYMENT DATE--------------------------"); + assertEquals("Checking for Repayment Date for 1st Month", new ArrayList<>(Arrays.asList(2011, 10, 15)), + loanSchedule.get(1).get("dueDate")); + System.out.println("Repayment Date for 1st Month--" + loanSchedule.get(1).get("dueDate")); + + assertEquals("Checking for Repayment Date for 2nd Month", new ArrayList<>(Arrays.asList(2011, 11, 15)), + loanSchedule.get(2).get("dueDate")); + System.out.println("Repayment Date for 2nd Month--" + loanSchedule.get(2).get("dueDate")); + + assertEquals("Checking for Repayment Date for 3rd Month", new ArrayList<>(Arrays.asList(2011, 12, 15)), + loanSchedule.get(3).get("dueDate")); + System.out.println("Repayment Date for 3rd Month--" + loanSchedule.get(3).get("dueDate")); + + assertEquals("Checking for Repayment Date for 4th Month", new ArrayList<>(Arrays.asList(2012, 1, 15)), + loanSchedule.get(4).get("dueDate")); + System.out.println("Repayment Date for 4th Month--" + loanSchedule.get(4).get("dueDate")); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java index a466a95..b62aaf4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainService.java @@ -70,6 +70,11 @@ public interface ConfigurationDomainService { Date retrieveOrganisationStartDate(); boolean isPaymnetypeApplicableforDisbursementCharge(); - + boolean isInterestChargedFromDateSameAsDisbursementDate(); + + boolean isSkippingMeetingOnFirstDayOfMonthEnabled(); + + Long retreivePeroidInNumberOfDaysForSkipMeetingDate(); + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java index c957f94..db853d4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/domain/ConfigurationDomainServiceJpa.java @@ -235,6 +235,19 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName); return property.isEnabled(); } + + @Override + public boolean isSkippingMeetingOnFirstDayOfMonthEnabled() { + return this.globalConfigurationRepository.findOneByNameWithNotFoundDetection("skip-repayment-on-first-day-of-month").isEnabled(); + } + + @Override + public Long retreivePeroidInNumberOfDaysForSkipMeetingDate() { + final String propertyName = "skip-repayment-on-first-day-of-month"; + final GlobalConfigurationProperty property = this.globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName); + return property.getValue(); + + } @Override public boolean isInterestChargedFromDateSameAsDisbursementDate() { http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/data/CalendarData.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/data/CalendarData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/data/CalendarData.java index 9368462..6151743 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/data/CalendarData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/data/CalendarData.java @@ -334,9 +334,9 @@ public class CalendarData { return false; } - public boolean isValidRecurringDate(final LocalDate compareDate) { + public boolean isValidRecurringDate(final LocalDate compareDate, final Boolean isSkipMeetingOnFirstDay, final Integer numberOfDays) { if (isBetweenStartAndEndDate(compareDate)) { return CalendarUtils.isValidRedurringDate(this.getRecurrence(), this.getStartDate(), - compareDate); } + compareDate, isSkipMeetingOnFirstDay, numberOfDays); } return false; } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java index 52d072d..8be58ec 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/Calendar.java @@ -560,15 +560,15 @@ public class Calendar extends AbstractAuditableCustom<AppUser, Long> { return recurrenceBuilder.toString(); } - public boolean isValidRecurringDate(final LocalDate compareDate) { + public boolean isValidRecurringDate(final LocalDate compareDate, Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) { if (isBetweenStartAndEndDate(compareDate)) { return CalendarUtils.isValidRedurringDate(getRecurrence(), getStartDateLocalDate(), - compareDate); } + compareDate, isSkipRepaymentOnFirstMonth, numberOfDays); } // validate with history details. for (CalendarHistory history : history()) { if (history.isBetweenStartAndEndDate(compareDate)) { return CalendarUtils.isValidRedurringDate(history.getRecurrence(), - history.getStartDateLocalDate(), compareDate); } + history.getStartDateLocalDate(), compareDate, isSkipRepaymentOnFirstMonth, numberOfDays); } } return false; http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/CalendarHistory.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/CalendarHistory.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/CalendarHistory.java index 4b193ab..93dc0bf 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/CalendarHistory.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/domain/CalendarHistory.java @@ -142,4 +142,9 @@ public class CalendarHistory extends AbstractPersistable<Long> { public void updateEndDate(Date historyCalEndDate) { this.endDate = historyCalEndDate; } + + public Calendar getCalendar() { + return this.calendar; + } + } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformService.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformService.java index 4584cc4..5b7b28f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformService.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformService.java @@ -50,4 +50,6 @@ public interface CalendarReadPlatformService { LocalDate generateNextEligibleMeetingDateForCollection(CalendarData calendarData, MeetingData lastMeetingData); + Boolean isCalendarAssociatedWithEntity(final Long entityId, final Long calendarId, Long entityTypeId); + } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java index 9c867c1..d86b7eb 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; import org.apache.fineract.infrastructure.core.service.DateUtils; @@ -45,10 +46,12 @@ import org.springframework.util.CollectionUtils; public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformService { private final JdbcTemplate jdbcTemplate; + private final ConfigurationDomainService configurationDomainService; @Autowired - public CalendarReadPlatformServiceImpl(final RoutingDataSource dataSource) { + public CalendarReadPlatformServiceImpl(final RoutingDataSource dataSource, final ConfigurationDomainService configurationDomainService) { this.jdbcTemplate = new JdbcTemplate(dataSource); + this.configurationDomainService = configurationDomainService; } private static final class CalendarDataMapper implements RowMapper<CalendarData> { @@ -237,11 +240,34 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ * till periodEndDate recurring dates will be generated. */ final LocalDate periodEndDate = this.getPeriodEndDate(calendarData.getEndDate(), tillDate); - - final Collection<LocalDate> recurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, periodStartDate, periodEndDate, - maxCount); - return recurringDates; - } + + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService + .isSkippingMeetingOnFirstDayOfMonthEnabled(); + if (isSkipRepaymentOnFirstMonthEnabled) { + numberOfDays = this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); + } + + final Collection<LocalDate> recurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, periodStartDate, + periodEndDate, maxCount, isSkipRepaymentOnFirstMonthEnabled, numberOfDays); + return recurringDates; + } + + @Override + public Boolean isCalendarAssociatedWithEntity(final Long entityId, final Long calendarId, final Long entityTypeId) { + String query = "Select COUNT(*) from m_calendar_instance ci where ci.entity_id = ? and ci.calendar_id = ? and " + + " ci.entity_type_enum = ?"; + try { + int calendarInstaneId = this.jdbcTemplate.queryForObject(query, + new Object[] { entityId, calendarId, entityTypeId }, Integer.class); + if (calendarInstaneId > 0) { + return true; + } + return false; + } catch (final EmptyResultDataAccessException e) { + return false; + } + } private LocalDate getSeedDate(LocalDate date) { return date; @@ -301,26 +327,37 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ * which is still on Tuesday and next collection sheet date should be on * 18th of Oct as per current calendar */ - if (lastMeetingDate != null && !calendarData.isBetweenStartAndEndDate(lastMeetingDate) - && !calendarData.isBetweenStartAndEndDate(DateUtils.getLocalDateOfTenant())) { - applicableCalendarData = this.retrieveApplicableCalendarFromHistory(calendarData.getId(), lastMeetingDate); - nextEligibleMeetingDate = CalendarUtils.getRecentEligibleMeetingDate(applicableCalendarData.getRecurrence(), lastMeetingDate); - } - - /** - * If nextEligibleMeetingDate is on or after current calendar startdate - * then regenerate the nextEligible meeting date based on - */ - if (nextEligibleMeetingDate == null) { - final LocalDate seedDate = (lastMeetingDate != null) ? lastMeetingDate : calendarData.getStartDate(); - nextEligibleMeetingDate = CalendarUtils.getRecentEligibleMeetingDate(applicableCalendarData.getRecurrence(), seedDate); - } else if (calendarData.isBetweenStartAndEndDate(nextEligibleMeetingDate)) { - nextEligibleMeetingDate = CalendarUtils.getRecentEligibleMeetingDate(applicableCalendarData.getRecurrence(), - calendarData.getStartDate()); - } - - return nextEligibleMeetingDate; - } + + + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService + .isSkippingMeetingOnFirstDayOfMonthEnabled(); + if (isSkipRepaymentOnFirstMonthEnabled) { + numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); + } + + if (lastMeetingDate != null && !calendarData.isBetweenStartAndEndDate(lastMeetingDate) + && !calendarData.isBetweenStartAndEndDate(DateUtils.getLocalDateOfTenant())) { + applicableCalendarData = this.retrieveApplicableCalendarFromHistory(calendarData.getId(), lastMeetingDate); + nextEligibleMeetingDate = CalendarUtils.getRecentEligibleMeetingDate(applicableCalendarData.getRecurrence(), + lastMeetingDate, isSkipRepaymentOnFirstMonthEnabled, numberOfDays); + } + + /** + * If nextEligibleMeetingDate is on or after current calendar startdate + * then regenerate the nextEligible meeting date based on + */ + if (nextEligibleMeetingDate == null) { + final LocalDate seedDate = (lastMeetingDate != null) ? lastMeetingDate : calendarData.getStartDate(); + nextEligibleMeetingDate = CalendarUtils.getRecentEligibleMeetingDate(applicableCalendarData.getRecurrence(), + seedDate, isSkipRepaymentOnFirstMonthEnabled, numberOfDays); + } else if (calendarData.isBetweenStartAndEndDate(nextEligibleMeetingDate)) { + nextEligibleMeetingDate = CalendarUtils.getRecentEligibleMeetingDate(applicableCalendarData.getRecurrence(), + calendarData.getStartDate(), isSkipRepaymentOnFirstMonthEnabled, numberOfDays); + } + + return nextEligibleMeetingDate; + } @Override public Collection<CalendarData> updateWithRecurringDates(final Collection<CalendarData> calendarsData) { @@ -465,4 +502,6 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ lastUpdatedByUserName); } } + + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java index ce42c0a..a525606 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/calendar/service/CalendarUtils.java @@ -133,19 +133,23 @@ public class CalendarUtils { public static Collection<LocalDate> getRecurringDates(final String recurringRule, final LocalDate seedDate, final LocalDate periodStartDate, final LocalDate periodEndDate) { final int maxCount = 10;// Default number of recurring dates - return getRecurringDates(recurringRule, seedDate, periodStartDate, periodEndDate, maxCount); + boolean isSkipRepaymentOnFirstdayofMonth = false; + final Integer numberofDays = 0; + return getRecurringDates(recurringRule, seedDate, periodStartDate, periodEndDate, maxCount, isSkipRepaymentOnFirstdayofMonth, + numberofDays); } public static Collection<LocalDate> getRecurringDates(final String recurringRule, final LocalDate seedDate, - final LocalDate periodStartDate, final LocalDate periodEndDate, final int maxCount) { + final LocalDate periodStartDate, final LocalDate periodEndDate, final int maxCount, boolean isSkippMeetingOnFirstDay, + final Integer numberOfDays) { final Recur recur = CalendarUtils.getICalRecur(recurringRule); - return getRecurringDates(recur, seedDate, periodStartDate, periodEndDate, maxCount); + return getRecurringDates(recur, seedDate, periodStartDate, periodEndDate, maxCount, isSkippMeetingOnFirstDay, numberOfDays); } private static Collection<LocalDate> getRecurringDates(final Recur recur, final LocalDate seedDate, final LocalDate periodStartDate, - final LocalDate periodEndDate, final int maxCount) { + final LocalDate periodEndDate, final int maxCount, boolean isSkippMeetingOnFirstDay, final Integer numberOfDays) { if (recur == null) { return null; } final Date seed = convertToiCal4JCompatibleDate(seedDate); final DateTime periodStart = new DateTime(periodStartDate.toDate()); @@ -153,11 +157,12 @@ public class CalendarUtils { final Value value = new Value(Value.DATE.getValue()); final DateList recurringDates = recur.getDates(seed, periodStart, periodEnd, value, maxCount); - return convertToLocalDateList(recurringDates, seedDate, getMeetingPeriodFrequencyType(recur)); + return convertToLocalDateList(recurringDates, seedDate, getMeetingPeriodFrequencyType(recur), isSkippMeetingOnFirstDay, + numberOfDays); } private static Collection<LocalDate> convertToLocalDateList(final DateList dates, final LocalDate seedDate, - final PeriodFrequencyType frequencyType) { + final PeriodFrequencyType frequencyType, boolean isSkippMeetingOnFirstDay, final Integer numberOfDays) { final Collection<LocalDate> recurringDates = new ArrayList<>(); @@ -167,9 +172,30 @@ public class CalendarUtils { recurringDates.add(adjustDate(new LocalDate(date), seedDate, frequencyType)); } + if (isSkippMeetingOnFirstDay) { return skipMeetingOnFirstdayOfMonth(recurringDates, numberOfDays); } + return recurringDates; } + private static Collection<LocalDate> skipMeetingOnFirstdayOfMonth(final Collection<LocalDate> recurringDates, final Integer numberOfDays) { + final Collection<LocalDate> adjustedRecurringDates = new ArrayList<>(); + + for (@SuppressWarnings("rawtypes") + final Iterator iterator = recurringDates.iterator(); iterator.hasNext();) { + LocalDate recuringDate = (LocalDate) iterator.next(); + adjustedRecurringDates.add(adjustRecurringDate(recuringDate, numberOfDays)); + } + return adjustedRecurringDates; + } + + public static LocalDate adjustRecurringDate(final LocalDate recuringDate, final Integer numberOfDays) { + if (recuringDate.getDayOfMonth() == 1) { + LocalDate adjustedRecurringDate = recuringDate.plusDays(numberOfDays); + return adjustedRecurringDate; + } + return recuringDate; + } + public static Recur getICalRecur(final String recurringRule) { // Construct RRule @@ -269,14 +295,30 @@ public class CalendarUtils { final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return false; } + final boolean isSkipRepaymentonFirstDayOfMonth = false; + final int numberOfDays = 0; + return isValidRecurringDate(recur, seedDate, date, isSkipRepaymentonFirstDayOfMonth, numberOfDays); + } + + public static boolean isValidRedurringDate(final String recurringRule, final LocalDate seedDate, final LocalDate date, + boolean isSkipRepaymentonFirstDayOfMonth, final Integer numberOfDays) { + + final Recur recur = CalendarUtils.getICalRecur(recurringRule); + if (recur == null) { return false; } - return isValidRecurringDate(recur, seedDate, date); + return isValidRecurringDate(recur, seedDate, date, isSkipRepaymentonFirstDayOfMonth, numberOfDays); } - public static boolean isValidRecurringDate(final Recur recur, final LocalDate seedDate, final LocalDate date) { + public static boolean isValidRecurringDate(final Recur recur, final LocalDate seedDate, final LocalDate date, + boolean isSkipRepaymentonFirstDayOfMonth, final int numberOfDays) { + LocalDate startDate = date; + if (isSkipRepaymentonFirstDayOfMonth && date.getDayOfMonth() == (numberOfDays + 1)) { + startDate = startDate.minusDays(numberOfDays); + } + final Collection<LocalDate> recurDate = getRecurringDates(recur, seedDate, startDate, date.plusDays(1), 1, + isSkipRepaymentonFirstDayOfMonth, numberOfDays); - final Collection<LocalDate> recurDate = getRecurringDates(recur, seedDate, date, date.plusDays(1), 1); - return (recurDate == null || recurDate.isEmpty()) ? false : true; + return (recurDate == null || recurDate.isEmpty()) ? false : recurDate.contains(date); } public static enum DayNameEnum { @@ -359,12 +401,13 @@ public class CalendarUtils { } public static LocalDate getFirstRepaymentMeetingDate(final Calendar calendar, final LocalDate disbursementDate, - final Integer loanRepaymentInterval, final String frequency) { + final Integer loanRepaymentInterval, final String frequency, boolean isSkipRepaymentOnFirstDayOfMonth, + final Integer numberOfDays) { final Recur recur = CalendarUtils.getICalRecur(calendar.getRecurrence()); if (recur == null) { return null; } LocalDate startDate = disbursementDate; final LocalDate seedDate = calendar.getStartDateLocalDate(); - if (isValidRedurringDate(calendar.getRecurrence(), seedDate, startDate)) { + if (isValidRedurringDate(calendar.getRecurrence(), seedDate, startDate, isSkipRepaymentOnFirstDayOfMonth, numberOfDays)) { startDate = startDate.plusDays(1); } // Recurring dates should follow loanRepaymentInterval. @@ -386,25 +429,32 @@ public class CalendarUtils { } final LocalDate firstRepaymentDate = getNextRecurringDate(recur, seedDate, startDate); + if (isSkipRepaymentOnFirstDayOfMonth && firstRepaymentDate.getDayOfMonth() == 1) { return adjustRecurringDate(firstRepaymentDate, + numberOfDays); } return firstRepaymentDate; } public static LocalDate getNewRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate, - final LocalDate oldRepaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays) { + final LocalDate oldRepaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays, + final boolean isSkipRepaymentOnFirstDayOfMonth, final Integer numberOfDays) { final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return null; } - if (isValidRecurringDate(recur, seedDate, oldRepaymentDate)) { return oldRepaymentDate; } - return getNextRepaymentMeetingDate(recurringRule, seedDate, oldRepaymentDate, loanRepaymentInterval, frequency, workingDays); + if (isValidRecurringDate(recur, seedDate, oldRepaymentDate, isSkipRepaymentOnFirstDayOfMonth, numberOfDays)) { return oldRepaymentDate; } + LocalDate nextRapaymentDate = getNextRepaymentMeetingDate(recurringRule, seedDate, oldRepaymentDate, loanRepaymentInterval, + frequency, workingDays, isSkipRepaymentOnFirstDayOfMonth, numberOfDays); + + return nextRapaymentDate; } public static LocalDate getNextRepaymentMeetingDate(final String recurringRule, final LocalDate seedDate, - final LocalDate repaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays) { + final LocalDate repaymentDate, final Integer loanRepaymentInterval, final String frequency, final WorkingDays workingDays, + boolean isSkipRepaymentOnFirstDayOfMonth, final Integer numberOfDays) { final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return null; } LocalDate tmpDate = repaymentDate; - if (isValidRecurringDate(recur, seedDate, repaymentDate)) { + if (isValidRecurringDate(recur, seedDate, repaymentDate, isSkipRepaymentOnFirstDayOfMonth, numberOfDays)) { tmpDate = repaymentDate.plusDays(1); } /* @@ -430,6 +480,10 @@ public class CalendarUtils { final LocalDate nextRepaymentDate = getNextRecurringDate(recur, seedDate, newRepaymentDate); newRepaymentDate = WorkingDaysUtil.getOffSetDateIfNonWorkingDay(newRepaymentDate, nextRepaymentDate, workingDays); + if (isSkipRepaymentOnFirstDayOfMonth) { + LocalDate newRepaymentDateTemp = adjustRecurringDate(newRepaymentDate, numberOfDays); + return WorkingDaysUtil.getOffSetDateIfNonWorkingDay(newRepaymentDateTemp, nextRepaymentDate, workingDays); + } return newRepaymentDate; } @@ -500,12 +554,13 @@ public class CalendarUtils { return sqlCalendarTypeOptions; } - public static LocalDate getRecentEligibleMeetingDate(final String recurringRule, final LocalDate seedDate) { + public static LocalDate getRecentEligibleMeetingDate(final String recurringRule, final LocalDate seedDate, + final boolean isSkipMeetingOnFirstDay, final Integer numberOfDays) { LocalDate currentDate = DateUtils.getLocalDateOfTenant(); final Recur recur = CalendarUtils.getICalRecur(recurringRule); if (recur == null) { return null; } - if (isValidRecurringDate(recur, seedDate, currentDate)) { return currentDate; } + if (isValidRecurringDate(recur, seedDate, currentDate, isSkipMeetingOnFirstDay, numberOfDays)) { return currentDate; } if (recur.getFrequency().equals(Recur.DAILY)) { currentDate = currentDate.plusDays(recur.getInterval()); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index 76d0c09..247d426 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -36,6 +36,7 @@ import java.util.Set; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.JsonQuery; import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.domain.JdbcSupport; @@ -44,8 +45,10 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte import org.apache.fineract.organisation.monetary.data.CurrencyData; import org.apache.fineract.portfolio.calendar.domain.Calendar; import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType; +import org.apache.fineract.portfolio.calendar.domain.CalendarInstanceRepository; import org.apache.fineract.portfolio.calendar.domain.CalendarRepositoryWrapper; import org.apache.fineract.portfolio.calendar.exception.NotValidRecurringDateException; +import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService; import org.apache.fineract.portfolio.collectionsheet.data.IndividualClientData; import org.apache.fineract.portfolio.collectionsheet.data.IndividualCollectionSheetData; import org.apache.fineract.portfolio.collectionsheet.data.IndividualCollectionSheetLoanFlatData; @@ -91,6 +94,9 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe private final MandatorySavingsCollectionsheetExtractor mandatorySavingsExtractor = new MandatorySavingsCollectionsheetExtractor(); private final CodeValueReadPlatformService codeValueReadPlatformService; private final PaymentTypeReadPlatformService paymentTypeReadPlatformService; + private final CalendarReadPlatformService calendarReadPlatformService; + private final ConfigurationDomainService configurationDomainService; + private final CalendarInstanceRepository calendarInstanceRepository; @Autowired public CollectionSheetReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, @@ -98,7 +104,9 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final CollectionSheetGenerateCommandFromApiJsonDeserializer collectionSheetGenerateCommandFromApiJsonDeserializer, final CalendarRepositoryWrapper calendarRepositoryWrapper, final AttendanceDropdownReadPlatformService attendanceDropdownReadPlatformService, - final CodeValueReadPlatformService codeValueReadPlatformService, final PaymentTypeReadPlatformService paymentTypeReadPlatformService) { + final CodeValueReadPlatformService codeValueReadPlatformService, final PaymentTypeReadPlatformService paymentTypeReadPlatformService, + final CalendarReadPlatformService calendarReadPlatformService, final ConfigurationDomainService configurationDomainService, + final CalendarInstanceRepository calendarInstanceRepository) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.namedParameterjdbcTemplate = new NamedParameterJdbcTemplate(dataSource); @@ -108,6 +116,9 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe this.attendanceDropdownReadPlatformService = attendanceDropdownReadPlatformService; this.codeValueReadPlatformService = codeValueReadPlatformService; this.paymentTypeReadPlatformService = paymentTypeReadPlatformService; + this.calendarReadPlatformService = calendarReadPlatformService; + this.configurationDomainService = configurationDomainService; + this.calendarInstanceRepository = calendarInstanceRepository; } /* @@ -320,20 +331,39 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final Calendar calendar = this.calendarRepositoryWrapper.findOneWithNotFoundDetection(calendarId); // check if transaction against calendar effective from date + + final GroupGeneralData group = this.groupReadPlatformService.retrieveOne(groupId); + + // entityType should be center if it's within a center + final CalendarEntityType entityType = (group.isChildGroup()) ? CalendarEntityType.CENTERS : CalendarEntityType.GROUPS; + + Long entityId = null; + if(group.isChildGroup()){ + entityId = group.getParentId(); + }else{ + entityId = group.getId(); + } - if (!calendar.isValidRecurringDate(transactionDate)) { throw new NotValidRecurringDateException("collectionsheet", "The date '" + Boolean isSkipMeetingOnFirstDay = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + numberOfDays = this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); + isSkipMeetingOnFirstDay = this.calendarReadPlatformService.isCalendarAssociatedWithEntity(entityId, calendar.getId(), + entityType.getValue().longValue()); + } + + if (!calendar.isValidRecurringDate(transactionDate, isSkipMeetingOnFirstDay, numberOfDays)) { throw new NotValidRecurringDateException("collectionsheet", "The date '" + transactionDate + "' is not a valid meeting date.", transactionDate); } final AppUser currentUser = this.context.authenticatedUser(); final String hierarchy = currentUser.getOffice().getHierarchy(); final String officeHierarchy = hierarchy + "%"; - final GroupGeneralData group = this.groupReadPlatformService.retrieveOne(groupId); + final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper(); - // entityType should be center if it's within a center - final CalendarEntityType entityType = (group.isChildGroup()) ? CalendarEntityType.CENTERS : CalendarEntityType.GROUPS; final SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("dueDate", transactionDateStr) .addValue("groupId", group.getId()).addValue("officeHierarchy", officeHierarchy) http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java index 716d5ec..f19148f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/group/service/CenterReadPlatformServiceImpl.java @@ -31,6 +31,7 @@ import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService; +import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.core.api.ApiParameterHelper; import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; @@ -50,6 +51,7 @@ import org.apache.fineract.organisation.staff.data.StaffData; import org.apache.fineract.organisation.staff.service.StaffReadPlatformService; import org.apache.fineract.portfolio.calendar.data.CalendarData; import org.apache.fineract.portfolio.calendar.service.CalendarEnumerations; +import org.apache.fineract.portfolio.calendar.service.CalendarReadPlatformService; import org.apache.fineract.portfolio.client.data.ClientData; import org.apache.fineract.portfolio.client.domain.ClientEnumerations; import org.apache.fineract.portfolio.client.service.ClientReadPlatformService; @@ -79,6 +81,8 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService private final OfficeReadPlatformService officeReadPlatformService; private final StaffReadPlatformService staffReadPlatformService; private final CodeValueReadPlatformService codeValueReadPlatformService; + private final ConfigurationDomainService configurationDomainService; + private final CalendarReadPlatformService calendarReadPlatformService; // data mappers private final CenterDataMapper centerMapper = new CenterDataMapper(); @@ -92,7 +96,8 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService public CenterReadPlatformServiceImpl(final PlatformSecurityContext context, final RoutingDataSource dataSource, final ClientReadPlatformService clientReadPlatformService, final OfficeReadPlatformService officeReadPlatformService, final StaffReadPlatformService staffReadPlatformService, final CodeValueReadPlatformService codeValueReadPlatformService, - final PaginationParametersDataValidator paginationParametersDataValidator) { + final PaginationParametersDataValidator paginationParametersDataValidator, final ConfigurationDomainService configurationDomainService, + final CalendarReadPlatformService calendarReadPlatformService) { this.context = context; this.clientReadPlatformService = clientReadPlatformService; this.jdbcTemplate = new JdbcTemplate(dataSource); @@ -100,6 +105,8 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService this.staffReadPlatformService = staffReadPlatformService; this.codeValueReadPlatformService = codeValueReadPlatformService; this.paginationParametersDataValidator = paginationParametersDataValidator; + this.configurationDomainService = configurationDomainService; + this.calendarReadPlatformService = calendarReadPlatformService; } // 'g.' preffix because of ERROR 1052 (23000): Column 'column_name' in where @@ -521,8 +528,13 @@ public class CenterReadPlatformServiceImpl implements CenterReadPlatformService Collection<StaffCenterData> staffCenterDataArray = new ArrayList<>(); Boolean flag = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = this.configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if (isSkipRepaymentOnFirstMonthEnabled) { + numberOfDays = this.configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); + } for (CenterData centerData : centerDataArray) { - if (centerData.getCollectionMeetingCalendar().isValidRecurringDate(new LocalDate(meetingDate))) { + if (centerData.getCollectionMeetingCalendar().isValidRecurringDate(new LocalDate(meetingDate), isSkipRepaymentOnFirstMonthEnabled, numberOfDays)) { if (staffCenterDataArray.size() <= 0) { Collection<CenterData> meetingFallCenter = new ArrayList<>(); meetingFallCenter.add(centerData); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java index 7ffe6bd..ee6461a 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/ScheduleGeneratorDTO.java @@ -40,14 +40,18 @@ public class ScheduleGeneratorDTO { final Calendar calendar; final CalendarHistoryDataWrapper calendarHistoryDataWrapper; final Boolean isInterestChargedFromDateAsDisbursementDateEnabled; + final Integer numberOfdays; + final boolean isSkipRepaymentOnFirstDayofMonth; + public ScheduleGeneratorDTO(final LoanScheduleGeneratorFactory loanScheduleFactory, final ApplicationCurrency applicationCurrency, final LocalDate calculatedRepaymentsStartingFromDate, final HolidayDetailDTO holidayDetailDTO, final CalendarInstance calendarInstanceForInterestRecalculation, final CalendarInstance compoundingCalendarInstance, final LocalDate recalculateFrom, final Long overdurPenaltyWaitPeriod, final FloatingRateDTO floatingRateDTO, - final Calendar calendar, final CalendarHistoryDataWrapper calendarHistoryDataWrapper, - final Boolean isInterestChargedFromDateAsDisbursementDateEnabled) { + final Calendar calendar, final CalendarHistoryDataWrapper calendarHistoryDataWrapper, + final Boolean isInterestChargedFromDateAsDisbursementDateEnabled, final Integer numberOfdays, final boolean isSkipRepaymentOnFirstDayofMonth) { + this.loanScheduleFactory = loanScheduleFactory; this.applicationCurrency = applicationCurrency; this.calculatedRepaymentsStartingFromDate = calculatedRepaymentsStartingFromDate; @@ -61,6 +65,8 @@ public class ScheduleGeneratorDTO { this.calendarHistoryDataWrapper = calendarHistoryDataWrapper; this.isInterestChargedFromDateAsDisbursementDateEnabled = isInterestChargedFromDateAsDisbursementDateEnabled; + this.numberOfdays = numberOfdays; + this.isSkipRepaymentOnFirstDayofMonth = isSkipRepaymentOnFirstDayofMonth; } public LoanScheduleGeneratorFactory getLoanScheduleFactory() { @@ -123,4 +129,12 @@ public class ScheduleGeneratorDTO { return this.isInterestChargedFromDateAsDisbursementDateEnabled; } + public Integer getNumberOfdays() { + return numberOfdays; + } + + public boolean isSkipRepaymentOnFirstDayofMonth() { + return isSkipRepaymentOnFirstDayofMonth; + } + } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index ebc2b6c..9f235ba 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -4170,7 +4170,8 @@ public class Loan extends AbstractPersistable<Long> { public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays, - final Boolean reschedulebasedOnMeetingDates, final LocalDate presentMeetingDate, final LocalDate newMeetingDate) { + final Boolean reschedulebasedOnMeetingDates, final LocalDate presentMeetingDate, final LocalDate newMeetingDate, + final boolean isSkipRepaymentonfirstdayofmonth, final Integer numberofDays) { // first repayment's from date is same as disbursement date. /* @@ -4202,7 +4203,7 @@ public class Loan extends AbstractPersistable<Long> { // getNewRepaymentMeetingDate method returns next meeting // date and not the same as tmpFromDate newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, tmpFromDate, tmpFromDate.plusDays(1), - loanRepaymentInterval, frequency, workingDays); + loanRepaymentInterval, frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays); } if (isHolidayEnabled) { @@ -4230,7 +4231,8 @@ public class Loan extends AbstractPersistable<Long> { } public void updateLoanRepaymentScheduleDates(final LocalDate meetingStartDate, final String recuringRule, - final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays) { + final boolean isHolidayEnabled, final List<Holiday> holidays, final WorkingDays workingDays, + final boolean isSkipRepaymentonfirstdayofmonth, final Integer numberofDays) { // first repayment's from date is same as disbursement date. LocalDate tmpFromDate = getDisbursementDate(); @@ -4250,14 +4252,14 @@ public class Loan extends AbstractPersistable<Long> { if (oldDueDate.isAfter(seedDate) && oldDueDate.isAfter(DateUtils.getLocalDateOfTenant())) { newRepaymentDate = CalendarUtils.getNewRepaymentMeetingDate(recuringRule, seedDate, oldDueDate, loanRepaymentInterval, - frequency, workingDays); + frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays); final LocalDate maxDateLimitForNewRepayment = getMaxDateLimitForNewRepayment(repaymentPeriodFrequencyType, loanRepaymentInterval, tmpFromDate); if (newRepaymentDate.isAfter(maxDateLimitForNewRepayment)) { newRepaymentDate = CalendarUtils.getNextRepaymentMeetingDate(recuringRule, seedDate, tmpFromDate, - loanRepaymentInterval, frequency, workingDays); + loanRepaymentInterval, frequency, workingDays, isSkipRepaymentonfirstdayofmonth, numberofDays); } if (isHolidayEnabled) { @@ -5046,9 +5048,9 @@ public class Loan extends AbstractPersistable<Long> { compoundingMethod = this.loanInterestRecalculationDetails.getInterestRecalculationCompoundingMethod(); compoundingFrequencyType = this.loanInterestRecalculationDetails.getCompoundingFrequencyType(); rescheduleStrategyMethod = this.loanInterestRecalculationDetails.getRescheduleStrategyMethod(); - calendar = scheduleGeneratorDTO.getCalendar(); - calendarHistoryDataWrapper = scheduleGeneratorDTO.getCalendarHistoryDataWrapper(); } + calendar = scheduleGeneratorDTO.getCalendar(); + calendarHistoryDataWrapper = scheduleGeneratorDTO.getCalendarHistoryDataWrapper(); BigDecimal annualNominalInterestRate = this.loanRepaymentScheduleDetail.getAnnualNominalInterestRate(); FloatingRateDTO floatingRateDTO = scheduleGeneratorDTO.getFloatingRateDTO(); @@ -5066,7 +5068,8 @@ public class Loan extends AbstractPersistable<Long> { this.maxOutstandingLoanBalance, interestChargedFromDate, this.loanProduct.getPrincipalThresholdForLastInstallment(), this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(), - rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations, calendarHistoryDataWrapper); + rescheduleStrategyMethod, calendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations, calendarHistoryDataWrapper, + scheduleGeneratorDTO.getNumberOfdays(), scheduleGeneratorDTO.isSkipRepaymentOnFirstDayofMonth()); return loanApplicationTerms; } @@ -5230,7 +5233,7 @@ public class Loan extends AbstractPersistable<Long> { @SuppressWarnings({ "unused" }) public LoanApplicationTerms getLoanApplicationTerms(final ApplicationCurrency applicationCurrency, final CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, - final FloatingRateDTO floatingRateDTO) { + final FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays) { LoanProduct loanProduct = loanProduct(); // LoanProductRelatedDetail loanProductRelatedDetail = // getLoanRepaymentScheduleDetail(); @@ -5310,7 +5313,7 @@ public class Loan extends AbstractPersistable<Long> { this.loanProduct.getInstallmentAmountInMultiplesOf(), recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, this.loanProduct.preCloseInterestCalculationStrategy(), rescheduleStrategyMethod, loanCalendar, getApprovedPrincipal(), annualNominalInterestRate, loanTermVariations, - calendarHistoryDataWrapper); + calendarHistoryDataWrapper, numberofdays, isSkipRepaymentonmonthFirst); } /** http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index e60a749..8024652 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -57,6 +57,7 @@ import org.apache.fineract.portfolio.loanaccount.loanschedule.exception.Schedule import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModel; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleModelRepaymentPeriod; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequest; +import org.apache.fineract.portfolio.loanaccount.service.LoanUtilService; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductMinimumRepaymentScheduleRelatedDetail; import org.joda.time.Days; import org.joda.time.LocalDate; @@ -1581,7 +1582,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener public LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, final CalendarInstance restCalendarInstance, final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, - final FloatingRateDTO floatingRateDTO) { + final FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays) { final Loan loan = loanRescheduleRequest.getLoan(); final LoanSummary loanSummary = loan.getSummary(); @@ -1723,7 +1724,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener // get the loan application terms from the Loan object final LoanApplicationTerms loanApplicationTerms = loan.getLoanApplicationTerms(applicationCurrency, restCalendarInstance, - compoundingCalendarInstance, loanCalendar, floatingRateDTO); + compoundingCalendarInstance, loanCalendar, floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays); // for applying variations Collection<LoanTermVariationsData> loanTermVariations = loanApplicationTerms.getLoanTermVariations().getInterestRateChanges(); http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java index a23f1e4..7afc002 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java @@ -92,9 +92,11 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator { dueRepaymentPeriodDate = CalendarUtils.getNewRepaymentMeetingDate(reccuringString, seedDate, lastRepaymentDate.plusDays(1), loanApplicationTerms.getRepaymentEvery(), CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(loanApplicationTerms.getLoanTermPeriodFrequencyType()), - holidayDetailDTO.getWorkingDays()); + holidayDetailDTO.getWorkingDays(), loanApplicationTerms.isSkipRepaymentOnFirstDayofMonth(), + loanApplicationTerms.getNumberOfdays()); } } + return dueRepaymentPeriodDate; } http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java index e4f1555..d2341ba 100755 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java @@ -183,6 +183,10 @@ public final class LoanApplicationTerms { private final Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled; + private final Integer numberOfDays; + + private final boolean isSkipRepaymentOnFirstDayOfMonth; + 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, @@ -199,9 +203,11 @@ public final class LoanApplicationTerms { final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType, final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf, final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar, - BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled) { + BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, + final Integer numberOfdays,boolean isSkipRepaymentOnFirstDayofMonth) { - final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null; + + final LoanRescheduleStrategyMethod rescheduleStrategyMethod = null; final InterestRecalculationCompoundingMethod interestRecalculationCompoundingMethod = null; final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null; return new LoanApplicationTerms(currency, loanTermFrequency, loanTermPeriodFrequencyType, numberOfRepayments, repaymentEvery, @@ -214,7 +220,8 @@ public final class LoanApplicationTerms { interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, preClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper, - isInterestChargedFromDateSameAsDisbursalDateEnabled); + isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth); + } public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, @@ -229,7 +236,7 @@ public final class LoanApplicationTerms { final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType, final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, final LoanRescheduleStrategyMethod rescheduleStrategyMethod, BigDecimal approvedAmount, BigDecimal annualNominalInterestRate, - List<LoanTermVariationsData> loanTermVariations) { + List<LoanTermVariationsData> loanTermVariations, final Integer numberOfdays, final boolean isSkipRepaymentOnFirstDayofMonth) { final Calendar loanCalendar = null; final CalendarHistoryDataWrapper calendarHistoryDataWrapper = null; @@ -239,7 +246,7 @@ public final class LoanApplicationTerms { principalThresholdForLastInstalment, installmentAmountInMultiplesOf, recalculationFrequencyType, restCalendarInstance, compoundingMethod, compoundingCalendarInstance, compoundingFrequencyType, loanPreClosureInterestCalculationStrategy, rescheduleStrategyMethod, loanCalendar, approvedAmount, annualNominalInterestRate, loanTermVariations, - calendarHistoryDataWrapper); + calendarHistoryDataWrapper, numberOfdays, isSkipRepaymentOnFirstDayofMonth); } public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, @@ -255,7 +262,8 @@ public final class LoanApplicationTerms { final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, final LoanRescheduleStrategyMethod rescheduleStrategyMethod, final Calendar loanCalendar, BigDecimal approvedAmount, BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations, - final CalendarHistoryDataWrapper calendarHistoryDataWrapper) { + final CalendarHistoryDataWrapper calendarHistoryDataWrapper, final Integer numberOfdays, + final boolean isSkipRepaymentOnFirstDayofMonth) { final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments(); final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery(); @@ -289,7 +297,7 @@ public final class LoanApplicationTerms { rescheduleStrategyMethod, compoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper, - isInterestChargedFromDateSameAsDisbursalDateEnabled); + isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth); } public static LoanApplicationTerms assembleFrom(final ApplicationCurrency applicationCurrency, final Integer loanTermFrequency, @@ -302,7 +310,8 @@ public final class LoanApplicationTerms { final CalendarInstance compoundingCalendarInstance, final RecalculationFrequencyType compoundingFrequencyType, final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf, final LoanPreClosureInterestCalculationStrategy loanPreClosureInterestCalculationStrategy, final Calendar loanCalendar, - BigDecimal approvedAmount, final BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations) { + BigDecimal approvedAmount, final BigDecimal annualNominalInterestRate, final List<LoanTermVariationsData> loanTermVariations, + Integer numberOfdays, boolean isSkipRepaymentOnFirstDayofMonth) { final Integer numberOfRepayments = loanProductRelatedDetail.getNumberOfRepayments(); final Integer repaymentEvery = loanProductRelatedDetail.getRepayEvery(); @@ -344,7 +353,8 @@ public final class LoanApplicationTerms { rescheduleStrategyMethod, interestRecalculationCompoundingMethod, restCalendarInstance, recalculationFrequencyType, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanPreClosureInterestCalculationStrategy, loanCalendar, approvedAmount, loanTermVariations, calendarHistoryDataWrapper, - isInterestChargedFromDateSameAsDisbursalDateEnabled); + isInterestChargedFromDateSameAsDisbursalDateEnabled, numberOfdays, isSkipRepaymentOnFirstDayofMonth); + } public static LoanApplicationTerms assembleFrom(final LoanApplicationTerms applicationTerms, @@ -367,8 +377,9 @@ public final class LoanApplicationTerms { applicationTerms.principalThresholdForLastInstalment, applicationTerms.installmentAmountInMultiplesOf, applicationTerms.preClosureInterestCalculationStrategy, applicationTerms.loanCalendar, applicationTerms.approvedPrincipal.getAmount(), loanTermVariations, applicationTerms.calendarHistoryDataWrapper, - applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled); - } + applicationTerms.isInterestChargedFromDateSameAsDisbursalDateEnabled, applicationTerms.numberOfDays, + applicationTerms.isSkipRepaymentOnFirstDayOfMonth); + } private LoanApplicationTerms(final ApplicationCurrency currency, final Integer loanTermFrequency, final PeriodFrequencyType loanTermPeriodFrequencyType, final Integer numberOfRepayments, final Integer repaymentEvery, @@ -389,7 +400,9 @@ public final class LoanApplicationTerms { final BigDecimal principalThresholdForLastInstalment, final Integer installmentAmountInMultiplesOf, final LoanPreClosureInterestCalculationStrategy preClosureInterestCalculationStrategy, final Calendar loanCalendar, BigDecimal approvedAmount, List<LoanTermVariationsData> loanTermVariations, - final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled) { + final CalendarHistoryDataWrapper calendarHistoryDataWrapper, Boolean isInterestChargedFromDateSameAsDisbursalDateEnabled, + final Integer numberOfdays, final boolean isSkipRepaymentOnFirstDayofMonth) { + this.currency = currency; this.loanTermFrequency = loanTermFrequency; this.loanTermPeriodFrequencyType = loanTermPeriodFrequencyType; @@ -436,6 +449,8 @@ public final class LoanApplicationTerms { this.principalThresholdForLastInstalment = principalThresholdForLastInstalment; this.installmentAmountInMultiplesOf = installmentAmountInMultiplesOf; this.preClosureInterestCalculationStrategy = preClosureInterestCalculationStrategy; + this.isSkipRepaymentOnFirstDayOfMonth = isSkipRepaymentOnFirstDayofMonth; + this.numberOfDays = numberOfdays; this.loanCalendar = loanCalendar; this.approvedPrincipal = Money.of(principal.getCurrency(), approvedAmount); @@ -1454,4 +1469,12 @@ public final class LoanApplicationTerms { return this.isInterestChargedFromDateSameAsDisbursalDateEnabled; } + public Integer getNumberOfdays() { + return numberOfDays; + } + + public boolean isSkipRepaymentOnFirstDayofMonth() { + return isSkipRepaymentOnFirstDayOfMonth; + } + } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java index 822632d..ae67451 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanScheduleGenerator.java @@ -54,5 +54,6 @@ public interface LoanScheduleGenerator { LoanRescheduleModel reschedule(final MathContext mathContext, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance, - CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, FloatingRateDTO floatingRateDTO); + CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, FloatingRateDTO floatingRateDTO, + final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays); } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index b5486f7..fa29f9e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -270,14 +270,29 @@ public class LoanScheduleAssembler { * If it is JLG loan/Group Loan synched with a meeting, then make sure * first repayment falls on meeting date */ + final Long groupId = this.fromApiJsonHelper.extractLongNamed("groupId", element); + Group group = null; + if(groupId != null){ + group = this.groupRepository.findOneWithNotFoundDetection(groupId); + } + + Boolean isSkipMeetingOnFirstDay = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + isSkipMeetingOnFirstDay = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(group, calendar); + if(isSkipMeetingOnFirstDay) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + } if ((loanType.isJLGAccount() || loanType.isGroupAccount()) && calendar != null) { - validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar); + validateRepaymentsStartDateWithMeetingDates(calculatedRepaymentsStartingFromDate, calendar, isSkipMeetingOnFirstDay, + numberOfDays); + /* * If disbursement is synced on meeting, make sure disbursement date * is on a meeting date */ if (synchDisbursement != null && synchDisbursement.booleanValue()) { - validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar); + validateDisbursementDateWithMeetingDates(expectedDisbursementDate, calendar, isSkipMeetingOnFirstDay, numberOfDays); } } @@ -397,8 +412,8 @@ public class LoanScheduleAssembler { maxOutstandingBalance, graceOnArrearsAgeing, daysInMonthType, daysInYearType, isInterestRecalculationEnabled, recalculationFrequencyType, restCalendarInstance, compoundingCalendarInstance, compoundingFrequencyType, principalThresholdForLastInstalment, installmentAmountInMultiplesOf, loanProduct.preCloseInterestCalculationStrategy(), - calendar, BigDecimal.ZERO, loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled); - } + calendar, BigDecimal.ZERO, loanTermVariations, isInterestChargedFromDateSameAsDisbursalDateEnabled,numberOfDays, isSkipMeetingOnFirstDay); +} private CalendarInstance createCalendarForSameAsRepayment(final Integer repaymentEvery, final PeriodFrequencyType repaymentPeriodFrequencyType, final LocalDate expectedDisbursementDate) { @@ -468,19 +483,20 @@ public class LoanScheduleAssembler { return disbursementDatas; } - private void validateRepaymentsStartDateWithMeetingDates(final LocalDate repaymentsStartingFromDate, final Calendar calendar) { + private void validateRepaymentsStartDateWithMeetingDates(final LocalDate repaymentsStartingFromDate, final Calendar calendar, + boolean isSkipRepaymentOnFirstDayOfMonth, final Integer numberOfDays) { if (repaymentsStartingFromDate != null && !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), - repaymentsStartingFromDate)) { + repaymentsStartingFromDate, isSkipRepaymentOnFirstDayOfMonth, numberOfDays)) { final String errorMessage = "First repayment date '" + repaymentsStartingFromDate + "' do not fall on a meeting date"; throw new LoanApplicationDateException("first.repayment.date.do.not.match.meeting.date", errorMessage, repaymentsStartingFromDate); } } - public void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar) { + public void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar, Boolean isSkipRepaymentOnFirstMonth, Integer numberOfDays) { // disbursement date should fall on a meeting date - if (!calendar.isValidRecurringDate(expectedDisbursementDate)) { + if (!calendar.isValidRecurringDate(expectedDisbursementDate, isSkipRepaymentOnFirstMonth, numberOfDays)) { final String errorMessage = "Expected disbursement date '" + expectedDisbursementDate + "' do not fall on a meeting date"; throw new LoanApplicationDateException("disbursement.date.do.not.match.meeting.date", errorMessage, expectedDisbursementDate); } @@ -747,6 +763,13 @@ public class LoanScheduleAssembler { if (loanCalendarInstance != null) { loanCalendar = loanCalendarInstance.getCalendar(); } + Boolean isSkipRepaymentOnFirstMonth = false; + Integer numberOfDays = 0; + boolean isSkipRepaymentOnFirstMonthEnabled = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + if(isSkipRepaymentOnFirstMonthEnabled){ + isSkipRepaymentOnFirstMonth = this.loanUtilService.isLoanRepaymentsSyncWithMeeting(loan.group(), loanCalendar); + if(isSkipRepaymentOnFirstMonth) { numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); } + } final Integer minGap = installmentConfig.getMinimumGap(); final Integer maxGap = installmentConfig.getMaximumGap(); @@ -760,7 +783,8 @@ public class LoanScheduleAssembler { .value(duedate) .failWithCodeNoParameterAddedToErrorCode("variable.schedule.date.must.be.in.min.max.range", "Loan schedule date invalid"); - } else if (loanCalendar != null && !actualDueDates.contains(duedate) && !loanCalendar.isValidRecurringDate(duedate)) { + } else if (loanCalendar != null && !actualDueDates.contains(duedate) && !loanCalendar.isValidRecurringDate(duedate, + isSkipRepaymentOnFirstMonth, numberOfDays)) { baseDataValidator .reset() .value(duedate) @@ -1004,10 +1028,11 @@ public class LoanScheduleAssembler { private LocalDate deriveFirstRepaymentDateForJLGLoans(final Integer repaymentEvery, final LocalDate expectedDisbursementDate, final LocalDate refernceDateForCalculatingFirstRepaymentDate, final PeriodFrequencyType repaymentPeriodFrequencyType, final Integer minimumDaysBetweenDisbursalAndFirstRepayment, final Calendar calendar) { - + boolean isMeetingSkipOnFirstDayOfMonth = configurationDomainService.isSkippingMeetingOnFirstDayOfMonthEnabled(); + int numberOfDays = configurationDomainService.retreivePeroidInNumberOfDaysForSkipMeetingDate().intValue(); final String frequency = CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType); final LocalDate derivedFirstRepayment = CalendarUtils.getFirstRepaymentMeetingDate(calendar, - refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency); + refernceDateForCalculatingFirstRepaymentDate, repaymentEvery, frequency, isMeetingSkipOnFirstDayOfMonth, numberOfDays); final LocalDate minimumFirstRepaymentDate = expectedDisbursementDate.plusDays(minimumDaysBetweenDisbursalAndFirstRepayment); return minimumFirstRepaymentDate.isBefore(derivedFirstRepayment) ? derivedFirstRepayment : deriveFirstRepaymentDateForJLGLoans( repaymentEvery, expectedDisbursementDate, derivedFirstRepayment, repaymentPeriodFrequencyType, http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java index d04a8a1..bcb7bc4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/DefaultLoanReschedulerFactory.java @@ -35,7 +35,8 @@ public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory { public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, final CalendarInstance restCalendarInstance, - final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, final FloatingRateDTO floatingRateDTO) { + final CalendarInstance compoundingCalendarInstance, final Calendar loanCalendar, final FloatingRateDTO floatingRateDTO, + final boolean isSkipRepaymentonmonthFirst, final Integer numberofdays) { LoanRescheduleModel loanRescheduleModel = null; @@ -43,13 +44,13 @@ public class DefaultLoanReschedulerFactory implements LoanReschedulerFactory { case DECLINING_BALANCE: loanRescheduleModel = new DecliningBalanceInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar, - floatingRateDTO); + floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays); break; case FLAT: loanRescheduleModel = new FlatInterestLoanScheduleGenerator().reschedule(mathContext, loanRescheduleRequest, applicationCurrency, holidayDetailDTO, restCalendarInstance, compoundingCalendarInstance, loanCalendar, - floatingRateDTO); + floatingRateDTO, isSkipRepaymentonmonthFirst, numberofdays); break; case INVALID: http://git-wip-us.apache.org/repos/asf/incubator-fineract/blob/bba8ca47/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java ---------------------------------------------------------------------- diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java index 8356e1b..f2e8bc7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/rescheduleloan/domain/LoanReschedulerFactory.java @@ -32,5 +32,6 @@ public interface LoanReschedulerFactory { public LoanRescheduleModel reschedule(final MathContext mathContext, final InterestMethod interestMethod, final LoanRescheduleRequest loanRescheduleRequest, final ApplicationCurrency applicationCurrency, final HolidayDetailDTO holidayDetailDTO, CalendarInstance restCalendarInstance, CalendarInstance compoundingCalendarInstance, - final Calendar loanCalendar, FloatingRateDTO floatingRateDTO); + final Calendar loanCalendar, FloatingRateDTO floatingRateDTO, final boolean isSkipRepaymentonmonthFirst, + final Integer numberofdays); }
