/**
* 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.service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.mifosplatform.infrastructure.configuration.domain.ConfigurationDomainService;
import org.mifosplatform.infrastructure.core.api.JsonCommand;
import org.mifosplatform.infrastructure.core.data.ApiParameterError;
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.GroupRepositoryWrapper;
import org.mifosplatform.portfolio.loanaccount.domain.Loan;
import org.mifosplatform.portfolio.loanaccount.domain.LoanRepository;
import org.mifosplatform.portfolio.loanaccount.service.LoanWritePlatformService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
@Service
public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWritePlatformService {
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 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 GroupRepositoryWrapper groupRepository,
final LoanRepository loanRepository, final ClientRepositoryWrapper clientRepository) {
this.calendarRepository = calendarRepository;
this.calendarHistoryRepository = calendarHistoryRepository;
this.fromApiJsonDeserializer = fromApiJsonDeserializer;
this.calendarInstanceRepository = calendarInstanceRepository;
this.loanWritePlatformService = loanWritePlatformService;
this.configurationDomainService = configurationDomainService;
this.groupRepository = groupRepository;
this.loanRepository = loanRepository;
this.clientRepository = clientRepository;
}
@Override
public CommandProcessingResult createCalendar(final JsonCommand command) {
this.fromApiJsonDeserializer.validateForCreate(command.json());
Long entityId = null;
CalendarEntityType entityType = CalendarEntityType.INVALID;
LocalDate entityActivationDate = null;
Group centerOrGroup = null;
if (command.getGroupId() != null) {
centerOrGroup = this.groupRepository.findOneWithNotFoundDetection(command.getGroupId());
entityActivationDate = centerOrGroup.getActivationLocalDate();
entityType = centerOrGroup.isCenter() ? CalendarEntityType.CENTERS : CalendarEntityType.GROUPS;
entityId = command.getGroupId();
} else if (command.getLoanId() != null) {
final Loan loan = this.loanRepository.findOne(command.getLoanId());
entityActivationDate = (loan.getApprovedOnDate() == null) ? loan.getSubmittedOnDate() : loan.getApprovedOnDate();
entityType = CalendarEntityType.LOANS;
entityId = command.getLoanId();
} else if (command.getClientId() != null) {
final Client client = this.clientRepository.findOneWithNotFoundDetection(command.getClientId());
entityActivationDate = client.getActivationLocalDate();
entityType = CalendarEntityType.CLIENTS;
entityId = command.getClientId();
}
final Integer entityTypeId = entityType.getValue();
final Calendar newCalendar = Calendar.fromJson(command);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
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);
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 (centerOrGroup != null) {
Long centerOrGroupId = centerOrGroup.getId();
Integer centerOrGroupEntityTypeId = entityType.getValue();
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);
final CalendarInstance newCalendarInstance = CalendarInstance.from(newCalendar, entityId, entityTypeId);
this.calendarInstanceRepository.save(newCalendarInstance);
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(newCalendar.getId()) //
.withClientId(command.getClientId()) //
.withGroupId(command.getGroupId()) //
.withLoanId(command.getLoanId()) //
.build();
}
@Override
public CommandProcessingResult updateCalendar(final JsonCommand command) {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
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<String, Object> 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() && calendarForUpdate.isRepeating()) {
// fetch all loan calendar instances associated with modifying
// calendar.
final Collection<CalendarInstance> loanCalendarInstances = this.calendarInstanceRepository.findByCalendarIdAndEntityTypeId(
calendarId, CalendarEntityType.LOANS.getValue());
if (!CollectionUtils.isEmpty(loanCalendarInstances)) {
// update all loans associated with modifying calendar
this.loanWritePlatformService.applyMeetingDateChanges(calendarForUpdate, loanCalendarInstances);
}
}
}
return new CommandProcessingResultBuilder() //
.withCommandId(command.commandId()) //
.withEntityId(calendarForUpdate.getId()) //
.with(changes) //
.build();
}
@Override
public CommandProcessingResult deleteCalendar(final Long calendarId) {
final Calendar calendarForDelete = this.calendarRepository.findOne(calendarId);
if (calendarForDelete == null) { throw new CalendarNotFoundException(calendarId); }
this.calendarRepository.delete(calendarForDelete);
return new CommandProcessingResultBuilder() //
.withCommandId(null) //
.withEntityId(calendarId) //
.build();
}
@Override
public CommandProcessingResult createCalendarInstance(final Long calendarId, final Long entityId, final Integer entityTypeId) {
final Calendar calendarForUpdate = this.calendarRepository.findOne(calendarId);
if (calendarForUpdate == null) { throw new CalendarNotFoundException(calendarId); }
final CalendarInstance newCalendarInstance = new CalendarInstance(calendarForUpdate, entityId, entityTypeId);
this.calendarInstanceRepository.save(newCalendarInstance);
return new CommandProcessingResultBuilder() //
.withCommandId(null) //
.withEntityId(calendarForUpdate.getId()) //
.build();
}
@Override
public CommandProcessingResult updateCalendarInstance(final Long calendarId, final Long entityId, final Integer entityTypeId) {
final Calendar calendarForUpdate = this.calendarRepository.findOne(calendarId);
if (calendarForUpdate == null) { throw new CalendarNotFoundException(calendarId); }
final CalendarInstance calendarInstanceForUpdate = this.calendarInstanceRepository.findByCalendarIdAndEntityIdAndEntityTypeId(
calendarId, entityId, entityTypeId);
this.calendarInstanceRepository.saveAndFlush(calendarInstanceForUpdate);
return new CommandProcessingResultBuilder() //
.withCommandId(null) //
.withEntityId(calendarForUpdate.getId()) //
.build();
}
}