From 2d0f71447bc8fe5ab39c276ebb8a5072547d9b93 Mon Sep 17 00:00:00 2001 From: ashok-conflux Date: Sat, 9 Nov 2013 12:11:49 +0530 Subject: [PATCH] added support for capturing calendar history data and generate meeting dates accordingly --- .../core_db/V128__calendar-history-table.sql | 19 ++ .../portfolio/calendar/CalendarConstants.java | 1 + .../calendar/api/CalendarsApiResource.java | 10 +- .../portfolio/calendar/data/CalendarData.java | 54 ++++- .../portfolio/calendar/domain/Calendar.java | 119 ++++++++- .../calendar/domain/CalendarHistory.java | 134 ++++++++++ .../domain/CalendarHistoryRepository.java | 13 + .../calendar/domain/CalendarType.java | 4 + .../service/CalendarReadPlatformService.java | 9 +- .../CalendarReadPlatformServiceImpl.java | 228 +++++++++++++++--- ...WritePlatformServiceJpaRepositoryImpl.java | 111 +++++---- ...ollectionSheetReadPlatformServiceImpl.java | 8 +- .../group/api/CentersApiResource.java | 15 +- .../group/api/GroupsApiResource.java | 15 +- .../service/LoanScheduleAssembler.java | 4 +- .../LoanEventApiJsonValidator.java | 5 +- .../service/LoanReadPlatformServiceImpl.java | 2 +- .../meeting/api/MeetingsApiResource.java | 7 +- .../portfolio/meeting/data/MeetingData.java | 5 + .../portfolio/meeting/domain/Meeting.java | 3 +- .../service/MeetingReadPlatformService.java | 2 + .../MeetingReadPlatformServiceImpl.java | 16 +- 22 files changed, 675 insertions(+), 109 deletions(-) create mode 100644 mifosng-db/migrations/core_db/V128__calendar-history-table.sql create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistory.java create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistoryRepository.java diff --git a/mifosng-db/migrations/core_db/V128__calendar-history-table.sql b/mifosng-db/migrations/core_db/V128__calendar-history-table.sql new file mode 100644 index 000000000..459255918 --- /dev/null +++ b/mifosng-db/migrations/core_db/V128__calendar-history-table.sql @@ -0,0 +1,19 @@ +CREATE TABLE `m_calendar_history` ( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `calendar_id` BIGINT(20) NOT NULL, + `title` VARCHAR(50) NOT NULL, + `description` VARCHAR(100) NULL DEFAULT NULL, + `location` VARCHAR(50) NULL DEFAULT NULL, + `start_date` DATE NOT NULL, + `end_date` DATE NULL DEFAULT NULL, + `duration` SMALLINT(6) NULL DEFAULT NULL, + `calendar_type_enum` SMALLINT(5) NOT NULL, + `repeating` TINYINT(1) NOT NULL DEFAULT '0', + `recurrence` VARCHAR(100) NULL DEFAULT NULL, + `remind_by_enum` SMALLINT(5) NULL DEFAULT NULL, + `first_reminder` SMALLINT(11) NULL DEFAULT NULL, + `second_reminder` SMALLINT(11) NULL DEFAULT NULL, + PRIMARY KEY (`id`), + INDEX `FK_m_calendar_m_calendar_history` (`calendar_id`), + CONSTRAINT `FK_m_calendar_m_calendar_history` FOREIGN KEY (`calendar_id`) REFERENCES `m_calendar` (`id`) +); \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/CalendarConstants.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/CalendarConstants.java index 93c92d65b..7a1acedf3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/CalendarConstants.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/CalendarConstants.java @@ -10,6 +10,7 @@ import java.util.Set; public class CalendarConstants { + public static final String CALENDAR_RESOURCE_NAME = "calendar"; public static enum CALENDAR_SUPPORTED_PARAMETERS { CALENDAR_ID("id"), ENTITY_TYPE("entityType"), ENTITY_ID("entityId"), TITLE("title"), DESCRIPTION("description"), LOCATION( "location"), START_DATE("startDate"), END_DATE("endDate"), CREATED_DATE("createdDate"), DURATION("duration"), TYPE_ID( diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java index 097e4c845..6ed5872c3 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/api/CalendarsApiResource.java @@ -26,6 +26,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; +import org.joda.time.LocalDate; import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; @@ -94,7 +95,12 @@ public class CalendarsApiResource { CalendarData calendarData = this.readPlatformService.retrieveCalendar(calendarId, entityId, entityTypeId); // Include recurring date details - calendarData = this.readPlatformService.generateRecurringDate(calendarData); + final boolean withHistory = true; + final LocalDate tillDate = null; + final Collection recurringDates = this.readPlatformService.generateRecurringDates(calendarData, withHistory, tillDate); + final Collection nextTenRecurringDates = this.readPlatformService.generateNextTenRecurringDates(calendarData); + final LocalDate recentEligibleMeetingDate = null; + calendarData = CalendarData.withRecurringDates(calendarData, recurringDates, nextTenRecurringDates, recentEligibleMeetingDate); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); if (settings.isTemplate()) { @@ -135,7 +141,7 @@ public class CalendarsApiResource { CalendarEntityType.valueOf(entityType.toUpperCase()).getValue(), calendarTypeOptions)); // Add recurring dates - calendarsData = this.readPlatformService.generateRecurringDates(calendarsData); + calendarsData = this.readPlatformService.updateWithRecurringDates(calendarsData); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); return this.toApiJsonSerializer.serialize(settings, calendarsData, this.RESPONSE_DATA_PARAMETERS); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/data/CalendarData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/data/CalendarData.java index 2b0f4f799..bee189501 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/data/CalendarData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/data/CalendarData.java @@ -22,6 +22,7 @@ import org.mifosplatform.portfolio.calendar.service.CalendarEnumerations; public class CalendarData { private final Long id; + private final Long calendarInstanceId; private final Long entityId; private final EnumOptionData entityType; private final String title; @@ -58,7 +59,7 @@ public class CalendarData { final List frequencyOptions; final List repeatsOnDayOptions; - public static CalendarData instance(final Long id, final Long entityId, final EnumOptionData entityType, final String title, + public static CalendarData instance(final Long id, final Long calendarInstanceId, final Long entityId, final EnumOptionData entityType, final String title, final String description, final String location, final LocalDate startDate, final LocalDate endDate, final Integer duration, final EnumOptionData type, final boolean repeating, final String recurrence, final EnumOptionData frequency, final Integer interval, final EnumOptionData repeatsOnDay, final EnumOptionData remindBy, final Integer firstReminder, @@ -75,7 +76,7 @@ public class CalendarData { final List frequencyOptions = null; final List repeatsOnDayOptions = null; - return new CalendarData(id, entityId, entityType, title, description, location, startDate, endDate, duration, type, repeating, + return new CalendarData(id, calendarInstanceId, entityId, entityType, title, description, location, startDate, endDate, duration, type, repeating, recurrence, frequency, interval, repeatsOnDay, remindBy, firstReminder, secondReminder, recurringDates, nextTenRecurringDates, humanReadable, recentEligibleMeetingDate, createdDate, lastUpdatedDate, createdByUserId, createdByUsername, lastUpdatedByUserId, lastUpdatedByUsername, entityTypeOptions, calendarTypeOptions, remindByOptions, @@ -84,7 +85,7 @@ public class CalendarData { public static CalendarData withRecurringDates(final CalendarData calendarData, final Collection recurringDates, final Collection nextTenRecurringDates, final LocalDate recentEligibleMeetingDate) { - return new CalendarData(calendarData.id, calendarData.entityId, calendarData.entityType, calendarData.title, + return new CalendarData(calendarData.id, calendarData.calendarInstanceId, calendarData.entityId, calendarData.entityType, calendarData.title, calendarData.description, calendarData.location, calendarData.startDate, calendarData.endDate, calendarData.duration, calendarData.type, calendarData.repeating, calendarData.recurrence, calendarData.frequency, calendarData.interval, calendarData.repeatsOnDay, calendarData.remindBy, calendarData.firstReminder, calendarData.secondReminder, recurringDates, @@ -94,9 +95,22 @@ public class CalendarData { calendarData.calendarTypeOptions, calendarData.remindByOptions, calendarData.frequencyOptions, calendarData.repeatsOnDayOptions); } + + public static CalendarData withRecentEligibleMeetingDate(final CalendarData calendarData, final LocalDate recentEligibleMeetingDate) { + return new CalendarData(calendarData.id, calendarData.calendarInstanceId, calendarData.entityId, calendarData.entityType, calendarData.title, + calendarData.description, calendarData.location, calendarData.startDate, calendarData.endDate, calendarData.duration, + calendarData.type, calendarData.repeating, calendarData.recurrence, calendarData.frequency, calendarData.interval, + calendarData.repeatsOnDay, calendarData.remindBy, calendarData.firstReminder, calendarData.secondReminder, calendarData.recurringDates, + calendarData.nextTenRecurringDates, calendarData.humanReadable, recentEligibleMeetingDate, calendarData.createdDate, + calendarData.lastUpdatedDate, calendarData.createdByUserId, calendarData.createdByUsername, + calendarData.lastUpdatedByUserId, calendarData.lastUpdatedByUsername, calendarData.entityTypeOptions, + calendarData.calendarTypeOptions, calendarData.remindByOptions, calendarData.frequencyOptions, + calendarData.repeatsOnDayOptions); + } public static CalendarData sensibleDefaultsForNewCalendarCreation() { final Long id = null; + final Long calendarInstanceId = null; final Long entityId = null; final EnumOptionData entityType = null; final String title = null; @@ -132,7 +146,7 @@ public class CalendarData { final Long lastUpdatedByUserId = null; final String lastUpdatedByUsername = null; - return new CalendarData(id, entityId, entityType, title, description, location, startDate, endDate, duration, type, repeating, + return new CalendarData(id, calendarInstanceId, entityId, entityType, title, description, location, startDate, endDate, duration, type, repeating, recurrence, frequency, interval, repeatsOnDay, remindBy, firstReminder, secondReminder, recurringDates, nextTenRecurringDates, humanReadable, recentEligibleMeetingDate, createdDate, lastUpdatedDate, createdByUserId, createdByUsername, lastUpdatedByUserId, lastUpdatedByUsername, entityTypeOptions, calendarTypeOptions, remindByOptions, @@ -143,7 +157,7 @@ public class CalendarData { final List calendarTypeOptions, final List remindByOptions, final List repeatsOptions, final List repeatsOnDayOptions) { - return new CalendarData(calendarData.id, calendarData.entityId, calendarData.entityType, calendarData.title, + return new CalendarData(calendarData.id, calendarData.calendarInstanceId, calendarData.entityId, calendarData.entityType, calendarData.title, calendarData.description, calendarData.location, calendarData.startDate, calendarData.endDate, calendarData.duration, calendarData.type, calendarData.repeating, calendarData.recurrence, calendarData.frequency, calendarData.interval, calendarData.repeatsOnDay, calendarData.remindBy, calendarData.firstReminder, calendarData.secondReminder, @@ -153,7 +167,7 @@ public class CalendarData { entityTypeOptions, calendarTypeOptions, remindByOptions, repeatsOptions, repeatsOnDayOptions); } - private CalendarData(final Long id, final Long entityId, final EnumOptionData entityType, final String title, final String description, + private CalendarData(final Long id, final Long calendarInstanceId, final Long entityId, final EnumOptionData entityType, final String title, final String description, final String location, final LocalDate startDate, final LocalDate endDate, final Integer duration, final EnumOptionData type, final boolean repeating, final String recurrence, final EnumOptionData frequency, final Integer interval, final EnumOptionData repeatsOnDay, final EnumOptionData remindBy, final Integer firstReminder, final Integer secondReminder, @@ -164,6 +178,7 @@ public class CalendarData { final List remindByOptions, final List repeatsOptions, final List repeatsOnDayOptions) { this.id = id; + this.calendarInstanceId = calendarInstanceId; this.entityId = entityId; this.entityType = entityType; this.title = title; @@ -278,4 +293,31 @@ public class CalendarData { return this.humanReadable; } + + public Long getCalendarInstanceId() { + return this.calendarInstanceId; + } + + public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) { + if (this.startDate != null && compareDate != null) { + if (this.startDate.isBefore(compareDate) || this.startDate.equals(compareDate)) { return true; } + } + return false; + } + + public boolean isEndDateAfterOrEqual(final LocalDate compareDate) { + if (this.endDate != null && compareDate != null) { + if (this.endDate.isAfter(compareDate) || this.endDate.isEqual(compareDate)) { return true; } + } + return false; + } + + public boolean isBetweenStartAndEndDate(final LocalDate compareDate){ + if (isStartDateBeforeOrEqual(compareDate)){ + if (this.endDate == null || isEndDateAfterOrEqual(compareDate)){ + return true; + } + } + return false; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java index 35503566a..73f6b441b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/Calendar.java @@ -6,12 +6,21 @@ package org.mifosplatform.portfolio.calendar.domain; +import static org.mifosplatform.portfolio.calendar.CalendarConstants.CALENDAR_RESOURCE_NAME; + +import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.Set; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.JoinColumn; +import javax.persistence.OneToMany; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @@ -19,7 +28,11 @@ import javax.persistence.TemporalType; import org.apache.commons.lang.StringUtils; import org.joda.time.LocalDate; import org.mifosplatform.infrastructure.core.api.JsonCommand; +import org.mifosplatform.infrastructure.core.data.ApiParameterError; +import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.domain.AbstractAuditableCustom; +import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.portfolio.calendar.CalendarConstants.CALENDAR_SUPPORTED_PARAMETERS; import org.mifosplatform.portfolio.calendar.exception.CalendarDateException; import org.mifosplatform.portfolio.calendar.exception.CalendarParameterUpdateNotSupportedException; @@ -68,6 +81,10 @@ public class Calendar extends AbstractAuditableCustom { @Column(name = "second_reminder", nullable = true) private Integer secondReminder; + @OneToMany(fetch = FetchType.EAGER) + @JoinColumn(name = "calendar_id") + private Set calendarHistory = new HashSet(); + protected Calendar() { } @@ -75,6 +92,17 @@ public class Calendar extends AbstractAuditableCustom { public Calendar(final String title, final String description, final String location, final LocalDate startDate, final LocalDate endDate, final Integer duration, final Integer typeId, final boolean repeating, final String recurrence, final Integer remindById, final Integer firstReminder, final Integer secondReminder) { + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(CALENDAR_RESOURCE_NAME); + + final CalendarType calendarType = CalendarType.fromInt(typeId); + if (calendarType.isCollection() && !repeating) { + baseDataValidator.reset().parameter(CALENDAR_SUPPORTED_PARAMETERS.REPEATING.getValue()) + .failWithCodeNoParameterAddedToErrorCode("must.repeat.for.collection.calendar"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + this.title = StringUtils.defaultIfEmpty(title, null); this.description = StringUtils.defaultIfEmpty(description, null); this.location = StringUtils.defaultIfEmpty(location, null); @@ -153,17 +181,22 @@ public class Calendar extends AbstractAuditableCustom { final String valueAsInput = command.stringValueOfParameterNamed(startDateParamName); final LocalDate newValue = command.localDateValueOfParameterNamed(startDateParamName); - if (isStartDateBefore(newValue)) { + final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); + + if (newValue.isBefore(currentDate)) { + final String defaultUserMessage = "New meeting effective from date cannot be in past"; + throw new CalendarDateException("new.start.date.cannot.be.in.past", defaultUserMessage, newValue, + getStartDateLocalDate()); + }else if (isStartDateAfter(newValue) && isStartDateBeforeOrEqual(currentDate)) { + //new meeting date should be on or after start date or current date + final String defaultUserMessage = "New meeting effective from date cannot be a date before existing meeting start date"; + throw new CalendarDateException("new.start.date.before.existing.date", defaultUserMessage, newValue, + getStartDateLocalDate()); + } else { actualChanges.put(startDateParamName, valueAsInput); actualChanges.put("dateFormat", dateFormatAsInput); actualChanges.put("locale", localeAsInput); this.startDate = newValue.toDate(); - } else { - // new meeting start date should be greater than existing - // meeting start date - final String defaultUserMessage = "New meeting start on or after date cannot be a date before existing meeting start date"; - throw new CalendarDateException("new.start.date.before.existing.date", defaultUserMessage, newValue, - getStartDateLocalDate()); } } @@ -211,8 +244,21 @@ public class Calendar extends AbstractAuditableCustom { this.repeating = newValue; } + //if repeating is false then update recurrence to NULL + if(!this.repeating) this.recurrence = null; + + final List dataValidationErrors = new ArrayList(); + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource(CALENDAR_RESOURCE_NAME); + + final CalendarType calendarType = CalendarType.fromInt(this.typeId); + if (calendarType.isCollection() && !this.repeating) { + baseDataValidator.reset().parameter(CALENDAR_SUPPORTED_PARAMETERS.REPEATING.getValue()) + .failWithCodeNoParameterAddedToErrorCode("must.repeat.for.collection.calendar"); + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } + } + final String newRecurrence = Calendar.constructRecurrence(command, this); - if (this.recurrence != null && !newRecurrence.equalsIgnoreCase(this.recurrence)) { + if (!StringUtils.isBlank(this.recurrence) && !newRecurrence.equalsIgnoreCase(this.recurrence)) { // FIXME: AA - Is this restriction required only for collection type // meetings or for all?. @@ -319,9 +365,48 @@ public class Calendar extends AbstractAuditableCustom { } return endDateLocalDate; } + + public Set history() { + return this.calendarHistory; + } - public boolean isStartDateBefore(final LocalDate newStartDate) { - if (this.startDate != null && newStartDate != null && getStartDateLocalDate().isBefore(newStartDate)) { return true; } + public boolean isStartDateBefore(final LocalDate compareDate) { + if (this.startDate != null && compareDate != null && getStartDateLocalDate().isBefore(compareDate)) { return true; } + return false; + } + + public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) { + if (this.startDate != null && compareDate != null) { + if (getStartDateLocalDate().isBefore(compareDate) || getStartDateLocalDate().equals(compareDate)) { return true; } + } + return false; + } + + public boolean isStartDateAfter(final LocalDate compareDate) { + if (this.startDate != null && compareDate != null && getStartDateLocalDate().isAfter(compareDate)) { return true; } + return false; + } + + public boolean isStartDateAfterOrEqual(final LocalDate compareDate) { + if (this.startDate != null && compareDate != null) { + if (getStartDateLocalDate().isAfter(compareDate) || getStartDateLocalDate().isEqual(compareDate)) { return true; } + } + return false; + } + + public boolean isEndDateAfterOrEqual(final LocalDate compareDate) { + if (this.endDate != null && compareDate != null) { + if (getEndDateLocalDate().isAfter(compareDate) || getEndDateLocalDate().isEqual(compareDate)) { return true; } + } + return false; + } + + public boolean isBetweenStartAndEndDate(final LocalDate compareDate){ + if (isStartDateBeforeOrEqual(compareDate)){ + if (getEndDateLocalDate() == null || isEndDateAfterOrEqual(compareDate)){ + return true; + } + } return false; } @@ -358,4 +443,18 @@ public class Calendar extends AbstractAuditableCustom { } return ""; } + + public boolean isValidRecurringDate(final LocalDate compareDate) { + + if (isBetweenStartAndEndDate(compareDate)) { return CalendarUtils.isValidRedurringDate(getRecurrence(), getStartDateLocalDate(), + compareDate); } + + // validate with history details. + for (CalendarHistory history : history()) { + if (history.isBetweenStartAndEndDate(compareDate)) { return CalendarUtils.isValidRedurringDate(history.getRecurrence(), + history.getStartDateLocalDate(), compareDate); } + } + + return false; + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistory.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistory.java new file mode 100644 index 000000000..5f2d0bc53 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistory.java @@ -0,0 +1,134 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.mifosplatform.portfolio.calendar.domain; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; + +import org.joda.time.LocalDate; +import org.springframework.data.jpa.domain.AbstractPersistable; + +@Entity +@Table(name = "m_calendar_history") +public class CalendarHistory extends AbstractPersistable{ + + @ManyToOne(optional = false) + @JoinColumn(name = "calendar_id", referencedColumnName = "id", nullable = false) + private Calendar calendar; + + @Column(name = "title", length = 50, nullable = false) + private String title; + + @Column(name = "description", length = 100, nullable = true) + private String description; + + @Column(name = "location", length = 100, nullable = true) + private String location; + + @Column(name = "start_date", nullable = false) + @Temporal(TemporalType.DATE) + private Date startDate; + + @Column(name = "end_date", nullable = true) + @Temporal(TemporalType.DATE) + private Date endDate; + + @Column(name = "duration", nullable = true) + private Integer duration; + + @Column(name = "calendar_type_enum", nullable = false) + private Integer typeId; + + @Column(name = "repeating", nullable = false) + private boolean repeating = false; + + @Column(name = "recurrence", length = 100, nullable = true) + private String recurrence; + + @Column(name = "remind_by_enum", nullable = true) + private Integer remindById; + + @Column(name = "first_reminder", nullable = true) + private Integer firstReminder; + + @Column(name = "second_reminder", nullable = true) + private Integer secondReminder; + + protected CalendarHistory() { + + } + + public CalendarHistory(Calendar calendar, Date startDate) { + this.calendar = calendar; + this.title = calendar.getTitle(); + this.description = calendar.getDescription(); + this.location = calendar.getLocation(); + this.startDate = startDate; + this.endDate = calendar.getStartDate();//Calendar start date become end date for history data. + this.duration = calendar.getDuration(); + this.typeId = calendar.getTypeId(); + this.repeating = calendar.isRepeating(); + this.recurrence = calendar.getRecurrence(); + this.remindById = calendar.getRemindById(); + this.firstReminder = calendar.getFirstReminder(); + this.secondReminder = calendar.getSecondReminder(); + } + + public String getRecurrence() { + return this.recurrence; + } + + public LocalDate getStartDateLocalDate() { + LocalDate startDateLocalDate = null; + if (this.startDate != null) { + startDateLocalDate = LocalDate.fromDateFields(this.startDate); + } + return startDateLocalDate; + } + + public LocalDate getEndDateLocalDate() { + LocalDate endDateLocalDate = null; + if (this.endDate != null) { + endDateLocalDate = LocalDate.fromDateFields(this.endDate); + } + return endDateLocalDate; + } + + public boolean isEndDateAfterOrEqual(final LocalDate compareDate) { + if (this.endDate != null && compareDate != null) { + if (getEndDateLocalDate().isAfter(compareDate) || getEndDateLocalDate().isEqual(compareDate)) { return true; } + } + return false; + } + + public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) { + if (this.startDate != null && compareDate != null) { + if (getStartDateLocalDate().isBefore(compareDate) || getStartDateLocalDate().equals(compareDate)) { return true; } + } + return false; + } + + public boolean isBetweenStartAndEndDate(final LocalDate compareDate){ + if (isStartDateBeforeOrEqual(compareDate)){ + if (getEndDateLocalDate() == null || isEndDateAfterOrEqual(compareDate)){ + return true; + } + } + return false; + } + + public void updateEndDate(Date historyCalEndDate){ + this.endDate = historyCalEndDate; + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistoryRepository.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistoryRepository.java new file mode 100644 index 000000000..93fd98ec6 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarHistoryRepository.java @@ -0,0 +1,13 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.portfolio.calendar.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; + +public interface CalendarHistoryRepository extends JpaRepository, JpaSpecificationExecutor { + +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarType.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarType.java index e7932bc43..409e790fc 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarType.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/domain/CalendarType.java @@ -66,4 +66,8 @@ public enum CalendarType { public String toString() { return name().toString(); } + + public boolean isCollection(){ + return this.value.equals(CalendarType.COLLECTION.value); + } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java index 26f79549a..de901a4b0 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformService.java @@ -10,6 +10,7 @@ import java.util.List; import org.joda.time.LocalDate; import org.mifosplatform.portfolio.calendar.data.CalendarData; +import org.mifosplatform.portfolio.meeting.data.MeetingData; public interface CalendarReadPlatformService { @@ -24,14 +25,16 @@ public interface CalendarReadPlatformService { CalendarData retrieveNewCalendarDetails(); - CalendarData generateRecurringDate(final CalendarData calendarData); + Collection generateRecurringDates(final CalendarData calendarData, final boolean withHistory, final LocalDate tillDate); - CalendarData generateRecurringDate(final CalendarData calendarData, final LocalDate tillDate); + Collection generateNextTenRecurringDates(final CalendarData calendarData); - Collection generateRecurringDates(final Collection calendarsData); + Collection updateWithRecurringDates(final Collection calendarsData); CalendarData retrieveLoanCalendar(final Long loanId); CalendarData retrieveCollctionCalendarByEntity(final Long entityId, final Integer entityTypeId); + + LocalDate generateNextEligibleMeetingDateForCollection(CalendarData calendarData, MeetingData lastMeetingData); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java index 34e221e71..a91719d74 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarReadPlatformServiceImpl.java @@ -20,6 +20,7 @@ import org.mifosplatform.portfolio.calendar.data.CalendarData; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarType; import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException; +import org.mifosplatform.portfolio.meeting.data.MeetingData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; @@ -40,7 +41,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ private static final class CalendarDataMapper implements RowMapper { public String schema() { - return " select c.id as id, ci.entity_id as entityId, ci.entity_type_enum as entityTypeId, c.title as title, " + return " select c.id as id, ci.id as calendarInstanceId, ci.entity_id as entityId, ci.entity_type_enum as entityTypeId, c.title as title, " + " c.description as description, c.location as location, c.start_date as startDate, c.end_date as endDate, " + " c.duration as duration, c.calendar_type_enum as typeId, c.repeating as repeating, " + " c.recurrence as recurrence, c.remind_by_enum as remindById, c.first_reminder as firstReminder, c.second_reminder as secondReminder, " @@ -54,6 +55,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ public CalendarData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { final Long id = rs.getLong("id"); + final Long calendarInstanceId = rs.getLong("calendarInstanceId"); final Long entityId = rs.getLong("entityId"); final Integer entityTypeId = rs.getInt("entityTypeId"); final EnumOptionData entityType = CalendarEnumerations.calendarEntityType(entityTypeId); @@ -89,9 +91,10 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ final Long lastUpdatedByUserId = rs.getLong("updatingUserId"); final String lastUpdatedByUserName = rs.getString("updatingUserName"); - return CalendarData.instance(id, entityId, entityType, title, description, location, startDate, endDate, duration, type, - repeating, recurrence, frequency, interval, repeatsOnDay, remindBy, firstReminder, secondReminder, humanReadable, createdDate, - lastUpdatedDate, createdByUserId, createdByUserName, lastUpdatedByUserId, lastUpdatedByUserName); + return CalendarData.instance(id, calendarInstanceId, entityId, entityType, title, description, location, startDate, endDate, + duration, type, repeating, recurrence, frequency, interval, repeatsOnDay, remindBy, firstReminder, secondReminder, + humanReadable, createdDate, lastUpdatedDate, createdByUserId, createdByUserName, lastUpdatedByUserId, + lastUpdatedByUserName); } } @@ -184,49 +187,139 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ } @Override - public CalendarData generateRecurringDate(final CalendarData calendarData) { - return generateRecurringDate(calendarData, null); + public Collection generateRecurringDates(final CalendarData calendarData, final boolean withHistory, final LocalDate tillDate) { + final LocalDate fromDate = null; + Collection recurringDates = generateRecurringDate(calendarData, fromDate, tillDate, -1); + + if (withHistory) { + final Collection calendarHistorys = this.retrieveCalendarsFromHistory(calendarData.getId()); + for (CalendarData calendarHistory : calendarHistorys) { + recurringDates.addAll(generateRecurringDate(calendarHistory, fromDate, tillDate, -1)); + } + } + + return recurringDates; } @Override - public CalendarData generateRecurringDate(final CalendarData calendarData, final LocalDate tillDate) { - if (!calendarData.isRepeating()) { return calendarData; } + public Collection generateNextTenRecurringDates(CalendarData calendarData) { + final LocalDate tillDate = null; + return generateRecurringDate(calendarData, DateUtils.getLocalDateOfTenant(), tillDate, 10); + } + + private Collection generateRecurringDate(final CalendarData calendarData, final LocalDate fromDate, final LocalDate tillDate, final int maxCount) { + + if (!calendarData.isRepeating()) { return null; } final String rrule = calendarData.getRecurrence(); - final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); - final LocalDate seedDate = calendarData.getStartDate(); - LocalDate endDate = calendarData.getEndDate(); - LocalDate startDate = calendarData.getStartDate(); - if (seedDate.isBefore(currentDate.minusYears(1))) { - startDate = currentDate.minusYears(1); + /** + * Start date or effective from date of calendar recurrence. + */ + final LocalDate seedDate = this.getSeedDate(calendarData.getStartDate()); + /** + * periodStartDate date onwards recurring dates will be generated. + */ + final LocalDate periodStartDate = this.getPeriodStartDate(seedDate, calendarData.getStartDate(), fromDate); + /** + * till periodEndDate recurring dates will be generated. + */ + final LocalDate periodEndDate = this.getPeriodEndDate(calendarData.getEndDate(), tillDate); + + final Collection recurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, periodStartDate, periodEndDate, + maxCount); + return recurringDates; + } + + private LocalDate getSeedDate(LocalDate date) { + return date; + } + + private LocalDate getPeriodStartDate(final LocalDate seedDate, final LocalDate recurrenceStartDate, final LocalDate fromDate) { + LocalDate periodStartDate = null; + if(fromDate != null) { + periodStartDate = fromDate; + }else { + final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); + if (seedDate.isBefore(currentDate.minusYears(1))) { + periodStartDate = currentDate.minusYears(1); + }else{ + periodStartDate = recurrenceStartDate; + } } + return periodStartDate; + } + + private LocalDate getPeriodEndDate(LocalDate endDate, LocalDate tillDate) { + LocalDate periodEndDate = endDate; + final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); if (tillDate != null) { if (endDate != null) { if (endDate.isAfter(tillDate)) { - endDate = tillDate;// to retrieve meeting dates till - // specified date (tillDate) + // to retrieve meeting dates tillspecified date (tillDate) + periodEndDate = tillDate; } } else { - endDate = tillDate;// end date is null then fetch meeting dates - // tillDate + // end date is null then fetch meeting dates tillDate + periodEndDate = tillDate; } } else if (endDate == null || endDate.isAfter(currentDate.plusYears(1))) { - endDate = currentDate.plusYears(1); + periodEndDate = currentDate.plusYears(1); } - - final Collection recurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, startDate, endDate, -1); - final Collection nextTenRecurringDates = CalendarUtils.getRecurringDates(rrule, seedDate, endDate); - final LocalDate recentEligibleMeetingDate = CalendarUtils.getRecentEligibleMeetingDate(rrule, seedDate); - return CalendarData.withRecurringDates(calendarData, recurringDates, nextTenRecurringDates, recentEligibleMeetingDate); - + return periodEndDate; } @Override - public Collection generateRecurringDates(final Collection calendarsData) { - final Collection recuCalendarsData = new ArrayList(); + public LocalDate generateNextEligibleMeetingDateForCollection(final CalendarData calendarData, final MeetingData lastMeetingData) { + final LocalDate lastMeetingDate = (lastMeetingData == null) ? null : lastMeetingData.getMeetingDate(); + // get applicable calendar based on meeting date + CalendarData applicableCalendarData = calendarData; + LocalDate nextEligibleMeetingDate = null; + /** + * The next available meeting date for collection should be taken from + * application calendar for that time period. e.g. If the previous + * calendar details has weekly meeting starting from 1st of Oct 2013 on + * every Tuesday, then meeting dates for collection are 1,8,15,22,29.. + * + * If meeting schedule has changed from Tuesday to Friday with effective + * from 15th of Oct (calendar update has made on 2nd of Oct) , then + * application should allow to generate collection sheet on 8th of Oct + * 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; + } + + @Override + public Collection updateWithRecurringDates(final Collection calendarsData) { + final Collection recuCalendarsData = new ArrayList(); + final boolean withHistory = true; + final LocalDate tillDate = null; for (final CalendarData calendarData : calendarsData) { - recuCalendarsData.add(generateRecurringDate(calendarData)); + final Collection recurringDates = this.generateRecurringDates(calendarData, withHistory, tillDate); + final Collection nextTenRecurringDates = this.generateNextTenRecurringDates(calendarData); + final LocalDate recentEligibleMeetingDate = null; + final CalendarData updatedCalendarData = CalendarData.withRecurringDates(calendarData, recurringDates, nextTenRecurringDates, + recentEligibleMeetingDate); + recuCalendarsData.add(updatedCalendarData); } return recuCalendarsData; @@ -279,4 +372,83 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ return conditionSql; } + private CalendarData retrieveApplicableCalendarFromHistory(Long calendarId, LocalDate compareDate) { + try { + final CalendarDataFromHistoryMapper rm = new CalendarDataFromHistoryMapper(); + + final String sql = rm.schema() + " where c.calendar_id = ? and date(?) between c.start_date and c.end_date limit 1"; + + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { calendarId, compareDate.toDate() }); + } catch (final EmptyResultDataAccessException e) { + return null; + } + } + + private Collection retrieveCalendarsFromHistory(Long calendarId) { + try { + final CalendarDataFromHistoryMapper rm = new CalendarDataFromHistoryMapper(); + + final String sql = rm.schema() + " where c.calendar_id = ? "; + + final Collection calendars = this.jdbcTemplate.query(sql, rm, new Object[] { calendarId }); + return calendars; + } catch (final EmptyResultDataAccessException e) { + return null; + } + } + + private static final class CalendarDataFromHistoryMapper implements RowMapper { + + public String schema() { + return " select c.calendar_id as id, c.title as title, c.description as description, c.location as location, c.start_date as startDate, " + + " c.end_date as endDate, c.duration as duration, c.calendar_type_enum as typeId, c.repeating as repeating, " + + " c.recurrence as recurrence, c.remind_by_enum as remindById, c.first_reminder as firstReminder, c.second_reminder as secondReminder " + + " from m_calendar_history c "; + } + + @Override + public CalendarData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { + + final Long id = rs.getLong("id"); + final Long calendarInstanceId = null; + final Long entityId = null; + final EnumOptionData entityType = null; + final String title = rs.getString("title"); + final String description = rs.getString("description"); + final String location = rs.getString("location"); + final LocalDate startDate = JdbcSupport.getLocalDate(rs, "startDate"); + final LocalDate endDate = JdbcSupport.getLocalDate(rs, "endDate"); + final Integer duration = rs.getInt("duration"); + final Integer typeId = rs.getInt("typeId"); + final EnumOptionData type = CalendarEnumerations.calendarType(typeId); + final boolean repeating = rs.getBoolean("repeating"); + final String recurrence = rs.getString("recurrence"); + final EnumOptionData frequency = CalendarEnumerations.calendarFrequencyType(CalendarUtils.getFrequency(recurrence)); + final Integer interval = new Integer(CalendarUtils.getInterval(recurrence)); + final EnumOptionData repeatsOnDay = CalendarEnumerations.calendarWeekDaysType(CalendarUtils.getRepeatsOnDay(recurrence)); + final Integer remindById = rs.getInt("remindById"); + EnumOptionData remindBy = null; + if (remindById != null && remindById != 0) { + remindBy = CalendarEnumerations.calendarRemindBy(remindById); + } + final Integer firstReminder = rs.getInt("firstReminder"); + final Integer secondReminder = rs.getInt("secondReminder"); + String humanReadable = null; + if (startDate != null && recurrence != null) { + humanReadable = CalendarUtils.getRRuleReadable(startDate, recurrence); + } + + final LocalDate createdDate = null; + final LocalDate lastUpdatedDate = null; + final Long createdByUserId = null; + final String createdByUserName = null; + final Long lastUpdatedByUserId = null; + final String lastUpdatedByUserName = null; + + return CalendarData.instance(id, calendarInstanceId, entityId, entityType, title, description, location, startDate, endDate, + duration, type, repeating, recurrence, frequency, interval, repeatsOnDay, remindBy, firstReminder, secondReminder, + humanReadable, createdDate, lastUpdatedDate, createdByUserId, createdByUserName, lastUpdatedByUserId, + lastUpdatedByUserName); + } + } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java index 28fb0472b..4c50a6c28 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/calendar/service/CalendarWritePlatformServiceJpaRepositoryImpl.java @@ -7,6 +7,7 @@ package org.mifosplatform.portfolio.calendar.service; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.List; import java.util.Map; @@ -20,18 +21,22 @@ import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.data.CommandProcessingResultBuilder; import org.mifosplatform.infrastructure.core.data.DataValidatorBuilder; import org.mifosplatform.infrastructure.core.exception.PlatformApiDataValidationException; +import org.mifosplatform.infrastructure.core.service.DateUtils; import org.mifosplatform.portfolio.calendar.CalendarConstants.CALENDAR_SUPPORTED_PARAMETERS; import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; +import org.mifosplatform.portfolio.calendar.domain.CalendarHistory; +import org.mifosplatform.portfolio.calendar.domain.CalendarHistoryRepository; import org.mifosplatform.portfolio.calendar.domain.CalendarInstance; import org.mifosplatform.portfolio.calendar.domain.CalendarInstanceRepository; import org.mifosplatform.portfolio.calendar.domain.CalendarRepository; +import org.mifosplatform.portfolio.calendar.domain.CalendarType; import org.mifosplatform.portfolio.calendar.exception.CalendarNotFoundException; import org.mifosplatform.portfolio.calendar.serialization.CalendarCommandFromApiJsonDeserializer; import org.mifosplatform.portfolio.client.domain.Client; import org.mifosplatform.portfolio.client.domain.ClientRepositoryWrapper; import org.mifosplatform.portfolio.group.domain.Group; -import org.mifosplatform.portfolio.group.domain.GroupRepository; +import org.mifosplatform.portfolio.group.domain.GroupRepositoryWrapper; import org.mifosplatform.portfolio.loanaccount.domain.Loan; import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository; import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService; @@ -43,21 +48,24 @@ import org.springframework.util.CollectionUtils; public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWritePlatformService { private final CalendarRepository calendarRepository; + private final CalendarHistoryRepository calendarHistoryRepository; private final CalendarCommandFromApiJsonDeserializer fromApiJsonDeserializer; private final CalendarInstanceRepository calendarInstanceRepository; private final LoanWritePlatformService loanWritePlatformService; private final ConfigurationDomainService configurationDomainService; - private final GroupRepository groupRepository; + private final GroupRepositoryWrapper groupRepository; private final LoanRepository loanRepository; private final ClientRepositoryWrapper clientRepository; @Autowired public CalendarWritePlatformServiceJpaRepositoryImpl(final CalendarRepository calendarRepository, + final CalendarHistoryRepository calendarHistoryRepository, final CalendarCommandFromApiJsonDeserializer fromApiJsonDeserializer, final CalendarInstanceRepository calendarInstanceRepository, final LoanWritePlatformService loanWritePlatformService, - final ConfigurationDomainService configurationDomainService, final GroupRepository groupRepository, + final ConfigurationDomainService configurationDomainService, final GroupRepositoryWrapper groupRepository, final LoanRepository loanRepository, final ClientRepositoryWrapper clientRepository) { this.calendarRepository = calendarRepository; + this.calendarHistoryRepository = calendarHistoryRepository; this.fromApiJsonDeserializer = fromApiJsonDeserializer; this.calendarInstanceRepository = calendarInstanceRepository; this.loanWritePlatformService = loanWritePlatformService; @@ -71,25 +79,58 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr public CommandProcessingResult createCalendar(final JsonCommand command) { this.fromApiJsonDeserializer.validateForCreate(command.json()); - - final LocalDate entityActivationDate = getEntityActivationDate(command); + final Long entityId = command.getSupportedEntityId(); final CalendarEntityType entityType = CalendarEntityType.valueOf(command.getSupportedEntityType().toUpperCase()); - + LocalDate entityActivationDate = null; + Group centerOrGroup = null; + if (entityType.isCenter() || entityType.isGroup()) { + centerOrGroup = this.groupRepository.findOneWithNotFoundDetection(entityId); + entityActivationDate = centerOrGroup.getActivationLocalDate(); + } else if (entityType.isLoan()) { + final Loan loan = this.loanRepository.findOne(entityId); + entityActivationDate = (loan.getApprovedOnDate() == null) ? loan.getSubmittedOnDate() : loan.getApprovedOnDate(); + } else if (entityType.isLoan()) { + final Client client = this.clientRepository.findOneWithNotFoundDetection(entityId); + entityActivationDate = client.getActivationLocalDate(); + } + final Calendar newCalendar = Calendar.fromJson(command); - + final List dataValidationErrors = new ArrayList(); - final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) - .resource("calendar"); - if(entityActivationDate == null || newCalendar.getStartDateLocalDate().isBefore(entityActivationDate)){ + final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("calendar"); + if (entityActivationDate == null || newCalendar.getStartDateLocalDate().isBefore(entityActivationDate)) { final DateTimeFormatter formatter = DateTimeFormat.forPattern(command.dateFormat()).withLocale(command.extractLocale()); String dateAsString = ""; - if(entityActivationDate != null) dateAsString = formatter.print(entityActivationDate); - + if (entityActivationDate != null) dateAsString = formatter.print(entityActivationDate); + final String errorMessage = "cannot.be.before." + entityType.name().toLowerCase() + ".activation.date"; baseDataValidator.reset().parameter(CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue()).value(dateAsString) .failWithCodeNoParameterAddedToErrorCode(errorMessage); - if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } } + + if (entityType.isCenter() || entityType.isGroup()) { + Long centerOrGroupId = entityId; + Integer centerOrGroupEntityTypeId = entityType.getValue(); + + if (entityType.isGroup()) { + @SuppressWarnings("null") + final Group parent = centerOrGroup.getParent(); + if (parent != null) { + centerOrGroupId = parent.getId(); + centerOrGroupEntityTypeId = CalendarEntityType.CENTERS.getValue(); + } + } + + final CalendarInstance collectionCalendarInstance = this.calendarInstanceRepository + .findByEntityIdAndEntityTypeIdAndCalendarTypeId(centerOrGroupId, centerOrGroupEntityTypeId, + CalendarType.COLLECTION.getValue()); + if (collectionCalendarInstance != null) { + final String errorMessage = "multiple.collection.calendar.not.supported"; + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(errorMessage); + } + } + + if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); } this.calendarRepository.save(newCalendar); @@ -103,25 +144,6 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr } - private LocalDate getEntityActivationDate(JsonCommand command) { - final Long entityId = command.getSupportedEntityId(); - final CalendarEntityType entityType = CalendarEntityType.valueOf(command.getSupportedEntityType().toUpperCase()); - LocalDate entityActivationDate = null; - - if(entityType.isCenter() || entityType.isGroup()){ - final Group centerOrGroup = this.groupRepository.findOne(entityId); - entityActivationDate = centerOrGroup.getActivationLocalDate(); - } else if (entityType.isLoan()){ - final Loan loan = this.loanRepository.findOne(entityId); - entityActivationDate = (loan.getApprovedOnDate() == null) ? loan.getSubmittedOnDate() : loan.getApprovedOnDate(); - } else if (entityType.isLoan()){ - final Client client = this.clientRepository.findOneWithNotFoundDetection(entityId); - entityActivationDate = client.getActivationLocalDate(); - } - - return entityActivationDate; - } - @Override public CommandProcessingResult updateCalendar(final JsonCommand command) { @@ -130,25 +152,30 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr final Long calendarId = command.entityId(); final Calendar calendarForUpdate = this.calendarRepository.findOne(calendarId); if (calendarForUpdate == null) { throw new CalendarNotFoundException(calendarId); } - + final Date oldStartDate = calendarForUpdate.getStartDate(); + final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); + //create calendar history before updating calendar + final CalendarHistory calendarHistory = new CalendarHistory(calendarForUpdate, oldStartDate); final Map changes = calendarForUpdate.update(command); - + if (!changes.isEmpty()) { + //update calendar history table only if there is a change in calendar start date. + if(currentDate.isAfter(new LocalDate(oldStartDate))){ + final Date endDate = calendarForUpdate.getStartDateLocalDate().minusDays(1).toDate(); + calendarHistory.updateEndDate(endDate); + this.calendarHistoryRepository.save(calendarHistory); + } + this.calendarRepository.saveAndFlush(calendarForUpdate); - if (this.configurationDomainService.isRescheduleFutureRepaymentsEnabled()) { - // In this approach loans following this meeting calendar will - // be affected immediately. - // get all calendar instances following this meeting calendar - // and calendar entity type is loan + if (this.configurationDomainService.isRescheduleFutureRepaymentsEnabled() && calendarForUpdate.isRepeating()) { + //fetch all loan calendar instances associated with modifying calendar. final Collection loanCalendarInstances = this.calendarInstanceRepository.findByCalendarIdAndEntityTypeId( calendarId, CalendarEntityType.LOANS.getValue()); if (!CollectionUtils.isEmpty(loanCalendarInstances)) { - // update all loans which are following this meeting - // calendar + //update all loans associated with modifying calendar this.loanWritePlatformService.applyMeetingDateChanges(calendarForUpdate, loanCalendarInstances); - // } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java index 47414f955..64e35ec15 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/collectionsheet/service/CollectionSheetReadPlatformServiceImpl.java @@ -30,7 +30,6 @@ import org.mifosplatform.portfolio.calendar.domain.Calendar; import org.mifosplatform.portfolio.calendar.domain.CalendarEntityType; import org.mifosplatform.portfolio.calendar.domain.CalendarRepositoryWrapper; import org.mifosplatform.portfolio.calendar.exception.NotValidRecurringDateException; -import org.mifosplatform.portfolio.calendar.service.CalendarUtils; import org.mifosplatform.portfolio.collectionsheet.data.JLGClientData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetData; import org.mifosplatform.portfolio.collectionsheet.data.JLGCollectionSheetFlatData; @@ -304,9 +303,11 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe final String transactionDateStr = df.format(transactionDate.toDate()); final Calendar calendar = this.calendarRepositoryWrapper.findOneWithNotFoundDetection(calendarId); - if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), transactionDate)) { throw new NotValidRecurringDateException( - "collectionsheet", "The date '" + transactionDate + "' is not a valid meeting date.", transactionDate); } + // check if transaction against calendar effective from date + if (!calendar.isValidRecurringDate(transactionDate)) { 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 + "%"; @@ -360,5 +361,4 @@ public class CollectionSheetReadPlatformServiceImpl implements CollectionSheetRe return buildJLGCollectionSheet(transactionDate, collectionSheetFlatDatas); } - } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java index ea52302cd..1d2c99949 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/CentersApiResource.java @@ -24,6 +24,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; @@ -48,6 +49,8 @@ import org.mifosplatform.portfolio.group.data.CenterData; import org.mifosplatform.portfolio.group.data.GroupGeneralData; import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.meeting.data.MeetingData; +import org.mifosplatform.portfolio.meeting.service.MeetingReadPlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -70,6 +73,7 @@ public class CentersApiResource { private final FromJsonHelper fromJsonHelper; private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService; + private final MeetingReadPlatformService meetingReadPlatformService; @Autowired public CentersApiResource(final PlatformSecurityContext context, final CenterReadPlatformService centerReadPlatformService, @@ -79,7 +83,7 @@ public class CentersApiResource { final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService, final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper, final AccountDetailsReadPlatformService accountDetailsReadPlatformService, - final CalendarReadPlatformService calendarReadPlatformService) { + final CalendarReadPlatformService calendarReadPlatformService, final MeetingReadPlatformService meetingReadPlatformService) { this.context = context; this.centerReadPlatformService = centerReadPlatformService; this.centerApiJsonSerializer = centerApiJsonSerializer; @@ -91,6 +95,7 @@ public class CentersApiResource { this.fromJsonHelper = fromJsonHelper; this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; this.calendarReadPlatformService = calendarReadPlatformService; + this.meetingReadPlatformService = meetingReadPlatformService; } @GET @@ -161,7 +166,13 @@ public class CentersApiResource { collectionMeetingCalendar = this.calendarReadPlatformService.retrieveCollctionCalendarByEntity(centerId, CalendarEntityType.CENTERS.getValue()); if (collectionMeetingCalendar != null) { - collectionMeetingCalendar = this.calendarReadPlatformService.generateRecurringDate(collectionMeetingCalendar); + final boolean withHistory = true; + final LocalDate tillDate = null; + final Collection recurringDates = this.calendarReadPlatformService.generateRecurringDates(collectionMeetingCalendar, withHistory, tillDate); + final Collection nextTenRecurringDates = this.calendarReadPlatformService.generateNextTenRecurringDates(collectionMeetingCalendar); + final MeetingData lastMeeting = this.meetingReadPlatformService.retrieveLastMeeting(collectionMeetingCalendar.getCalendarInstanceId()); + final LocalDate recentEligibleMeetingDate = this.calendarReadPlatformService.generateNextEligibleMeetingDateForCollection(collectionMeetingCalendar, lastMeeting); + collectionMeetingCalendar = CalendarData.withRecurringDates(collectionMeetingCalendar, recurringDates, nextTenRecurringDates, recentEligibleMeetingDate); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java index 1faa23e43..161e8d10d 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/group/api/GroupsApiResource.java @@ -26,6 +26,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; @@ -55,6 +56,8 @@ import org.mifosplatform.portfolio.group.service.CenterReadPlatformService; import org.mifosplatform.portfolio.group.service.GroupReadPlatformService; import org.mifosplatform.portfolio.group.service.GroupRolesReadPlatformService; import org.mifosplatform.portfolio.group.service.SearchParameters; +import org.mifosplatform.portfolio.meeting.data.MeetingData; +import org.mifosplatform.portfolio.meeting.service.MeetingReadPlatformService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -81,6 +84,7 @@ public class GroupsApiResource { private final GroupRolesReadPlatformService groupRolesReadPlatformService; private final AccountDetailsReadPlatformService accountDetailsReadPlatformService; private final CalendarReadPlatformService calendarReadPlatformService; + private final MeetingReadPlatformService meetingReadPlatformService; @Autowired public GroupsApiResource(final PlatformSecurityContext context, final GroupReadPlatformService groupReadPlatformService, @@ -93,7 +97,7 @@ public class GroupsApiResource { final CollectionSheetReadPlatformService collectionSheetReadPlatformService, final FromJsonHelper fromJsonHelper, final GroupRolesReadPlatformService groupRolesReadPlatformService, final AccountDetailsReadPlatformService accountDetailsReadPlatformService, - final CalendarReadPlatformService calendarReadPlatformService) { + final CalendarReadPlatformService calendarReadPlatformService, final MeetingReadPlatformService meetingReadPlatformService) { this.context = context; this.groupReadPlatformService = groupReadPlatformService; @@ -109,6 +113,7 @@ public class GroupsApiResource { this.groupRolesReadPlatformService = groupRolesReadPlatformService; this.accountDetailsReadPlatformService = accountDetailsReadPlatformService; this.calendarReadPlatformService = calendarReadPlatformService; + this.meetingReadPlatformService = meetingReadPlatformService; } @GET @@ -216,7 +221,13 @@ public class GroupsApiResource { CalendarEntityType.GROUPS.getValue()); } if (collectionMeetingCalendar != null) { - collectionMeetingCalendar = this.calendarReadPlatformService.generateRecurringDate(collectionMeetingCalendar); + final boolean withHistory = true; + final LocalDate tillDate = null; + final Collection recurringDates = this.calendarReadPlatformService.generateRecurringDates(collectionMeetingCalendar, withHistory, tillDate); + final Collection nextTenRecurringDates = this.calendarReadPlatformService.generateNextTenRecurringDates(collectionMeetingCalendar); + final MeetingData lastMeeting = this.meetingReadPlatformService.retrieveLastMeeting(collectionMeetingCalendar.getCalendarInstanceId()); + final LocalDate recentEligibleMeetingDate = this.calendarReadPlatformService.generateNextEligibleMeetingDateForCollection(collectionMeetingCalendar, lastMeeting); + collectionMeetingCalendar = CalendarData.withRecurringDates(collectionMeetingCalendar, recurringDates, nextTenRecurringDates, recentEligibleMeetingDate); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index 344ae5dc6..1b01162e2 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -207,7 +207,7 @@ public class LoanScheduleAssembler { private void validateDisbursementDateWithMeetingDates(final LocalDate expectedDisbursementDate, final Calendar calendar) { // disbursement date should fall on a meeting date - if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), expectedDisbursementDate)) { + if (!calendar.isValidRecurringDate(expectedDisbursementDate)) { 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); } @@ -229,7 +229,7 @@ public class LoanScheduleAssembler { if (repaymentInterval % meetingInterval != 0) { // throw exception: Loan product frequency/interval throw new MeetingFrequencyMismatchException("loanapplication.repayment.interval", - "Loan repayment repaid every # must equal or multiple of meeting interval", repaymentInterval); + "Loan repayment repaid every # must equal or multiple of meeting interval " + meetingInterval, meetingInterval, repaymentInterval); } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java index e8c7d8ced..5b42d8e09 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java @@ -74,15 +74,12 @@ public final class LoanEventApiJsonValidator { public void validateDisbursementDateWithMeetingDate(final LocalDate actualDisbursementDate, final CalendarInstance calendarInstance) { if (null != calendarInstance) { final Calendar calendar = calendarInstance.getCalendar(); - if (calendar != null && actualDisbursementDate != null) { + if (!calendar.isValidRecurringDate(actualDisbursementDate)) { // Disbursement date should fall on a meeting date - if (!CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), actualDisbursementDate)) { final String errorMessage = "Expected disbursement date '" + actualDisbursementDate.toString() + "' does not fall on a meeting date."; throw new NotValidRecurringDateException("loan.actual.disbursement.date", errorMessage, actualDisbursementDate.toString(), calendar.getTitle()); - } - } } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index 2efe26092..b7e8373b4 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -1080,7 +1080,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { CalendarEntityType.GROUPS.getValue(), null)); calendarsData .addAll(this.calendarReadPlatformService.retrieveCalendarsByEntity(groupId, CalendarEntityType.GROUPS.getValue(), null)); - calendarsData = this.calendarReadPlatformService.generateRecurringDates(calendarsData); + calendarsData = this.calendarReadPlatformService.updateWithRecurringDates(calendarsData); return calendarsData; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java index cc6e831bf..f1d1cde1b 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/api/MeetingsApiResource.java @@ -24,6 +24,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.UriInfo; import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; import org.mifosplatform.commands.domain.CommandWrapper; import org.mifosplatform.commands.service.CommandWrapperBuilder; import org.mifosplatform.commands.service.PortfolioCommandSourceWritePlatformService; @@ -107,7 +108,11 @@ public class MeetingsApiResource { if (calendarId != null) { calendarData = this.calendarReadPlatformService.retrieveCalendar(calendarId, entityId, entityTypeId); - calendarData = this.calendarReadPlatformService.generateRecurringDate(calendarData, DateUtils.getLocalDateOfTenant()); + final boolean withHistory = true; + final Collection recurringDates = this.calendarReadPlatformService.generateRecurringDates(calendarData, withHistory, DateUtils.getLocalDateOfTenant()); + final Collection nextTenRecurringDates = this.calendarReadPlatformService.generateNextTenRecurringDates(calendarData); + final LocalDate recentEligibleMeetingDate = null; + calendarData = CalendarData.withRecurringDates(calendarData, recurringDates, nextTenRecurringDates, recentEligibleMeetingDate); } final MeetingData meetingData = MeetingData.template(clients, calendarData, diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java index 05420b548..022412809 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/data/MeetingData.java @@ -66,4 +66,9 @@ public class MeetingData { this.calendarData = calendarData; this.attendanceTypeOptions = attendanceTypeOptions; } + + public LocalDate getMeetingDate() { + return this.meetingDate; + } + } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java index 365fe0457..929fa7f94 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/domain/Meeting.java @@ -172,8 +172,9 @@ public class Meeting extends AbstractPersistable { if (meetingDate != null) { meetingDateLocalDate = LocalDate.fromDateFields(meetingDate); } + if (meetingDateLocalDate == null - || !CalendarUtils.isValidRedurringDate(calendar.getRecurrence(), calendar.getStartDateLocalDate(), meetingDateLocalDate)) { return false; } + || !calendar.isValidRecurringDate(meetingDateLocalDate)) { return false; } return true; } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java index 05350ccd5..962473094 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformService.java @@ -18,4 +18,6 @@ public interface MeetingReadPlatformService { Collection retrieveMeetingsByEntityByCalendarType(final Long entityId, final Integer entityTypeId, final List calendarTypeOptions); + + MeetingData retrieveLastMeeting(Long calendarInstanceId); } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java index 60dbadd79..b461fc652 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/portfolio/meeting/service/MeetingReadPlatformServiceImpl.java @@ -36,7 +36,8 @@ public class MeetingReadPlatformServiceImpl implements MeetingReadPlatformServic public String schema() { - return " select m.id as id, m.meeting_date as meetingDate from m_meeting m inner join m_calendar_instance ci on m.calendar_instance_id = ci.id "; + return " select m.id as id, m.meeting_date as meetingDate from m_meeting m " + + "inner join m_calendar_instance ci on m.calendar_instance_id = ci.id "; } @Override @@ -86,4 +87,17 @@ public class MeetingReadPlatformServiceImpl implements MeetingReadPlatformServic return this.jdbcTemplate.query(sql, rm, new Object[] { entityId, entityTypeId }); } + @Override + public MeetingData retrieveLastMeeting(Long calendarInstanceId) { + try { + final MeetingDataMapper rm = new MeetingDataMapper(); + + final String sql = rm.schema() + " where ci.id = ? order by m.meeting_date desc, m.id desc limit 1"; + + return this.jdbcTemplate.queryForObject(sql, rm, new Object[] { calendarInstanceId }); + } catch (final EmptyResultDataAccessException e) { + return null; + } + } + } \ No newline at end of file