com.gst.portfolio.calendar.service.CalendarWritePlatformServiceJpaRepositoryImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.gst.portfolio.calendar.service.CalendarWritePlatformServiceJpaRepositoryImpl.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.gst.portfolio.calendar.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.gst.infrastructure.configuration.domain.ConfigurationDomainService;
import com.gst.infrastructure.core.api.JsonCommand;
import com.gst.infrastructure.core.data.ApiParameterError;
import com.gst.infrastructure.core.data.CommandProcessingResult;
import com.gst.infrastructure.core.data.CommandProcessingResultBuilder;
import com.gst.infrastructure.core.data.DataValidatorBuilder;
import com.gst.infrastructure.core.exception.PlatformApiDataValidationException;
import com.gst.portfolio.calendar.CalendarConstants.CALENDAR_SUPPORTED_PARAMETERS;
import com.gst.portfolio.calendar.domain.Calendar;
import com.gst.portfolio.calendar.domain.CalendarEntityType;
import com.gst.portfolio.calendar.domain.CalendarHistory;
import com.gst.portfolio.calendar.domain.CalendarHistoryRepository;
import com.gst.portfolio.calendar.domain.CalendarInstance;
import com.gst.portfolio.calendar.domain.CalendarInstanceRepository;
import com.gst.portfolio.calendar.domain.CalendarRepository;
import com.gst.portfolio.calendar.domain.CalendarType;
import com.gst.portfolio.calendar.exception.CalendarNotFoundException;
import com.gst.portfolio.calendar.serialization.CalendarCommandFromApiJsonDeserializer;
import com.gst.portfolio.client.domain.Client;
import com.gst.portfolio.client.domain.ClientRepositoryWrapper;
import com.gst.portfolio.group.domain.Group;
import com.gst.portfolio.group.domain.GroupRepositoryWrapper;
import com.gst.portfolio.loanaccount.domain.Loan;
import com.gst.portfolio.loanaccount.domain.LoanRepositoryWrapper;
import com.gst.portfolio.loanaccount.domain.LoanStatus;
import com.gst.portfolio.loanaccount.service.LoanWritePlatformService;
import org.joda.time.LocalDate;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
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 LoanRepositoryWrapper loanRepositoryWrapper;
    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 LoanRepositoryWrapper loanRepositoryWrapper,
            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.loanRepositoryWrapper = loanRepositoryWrapper;
        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.loanRepositoryWrapper.findOneWithNotFoundDetection(command.getLoanId(), true);
            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();

    }

    public void validateIsEditMeetingAllowed(Long groupId) {

        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
        final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
                .resource("calendar");
        Group centerOrGroup = null;

        if (groupId != null) {
            centerOrGroup = this.groupRepository.findOneWithNotFoundDetection(groupId);
            final Group parent = centerOrGroup.getParent();
            /* Check if it is a Group and belongs to a center */
            if (centerOrGroup.isGroup() && parent != null) {

                Integer centerEntityTypeId = CalendarEntityType.CENTERS.getValue();
                /* Check if calendar is created at center */
                final CalendarInstance collectionCalendarInstance = this.calendarInstanceRepository
                        .findByEntityIdAndEntityTypeIdAndCalendarTypeId(parent.getId(), centerEntityTypeId,
                                CalendarType.COLLECTION.getValue());
                /* If calendar is created by parent group, then it cannot be edited by the child group */
                if (collectionCalendarInstance != null) {
                    final String errorMessage = "meeting.created.at.center.cannot.be.edited.at.group.level";
                    baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(errorMessage);
                }
            }

        }
        if (!dataValidationErrors.isEmpty()) {
            throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist",
                    "Validation errors exist.", dataValidationErrors);
        }

    }

    @Override
    public CommandProcessingResult updateCalendar(final JsonCommand command) {

        /** Validate to check if Edit is Allowed **/
        this.validateIsEditMeetingAllowed(command.getGroupId());
        /*
         * Validate all the data for updating the calendar
         */
        this.fromApiJsonDeserializer.validateForUpdate(command.json());

        Boolean areActiveEntitiesSynced = false;
        final Long calendarId = command.entityId();

        final Collection<Integer> loanStatuses = new ArrayList<>(
                Arrays.asList(LoanStatus.SUBMITTED_AND_PENDING_APPROVAL.getValue(), LoanStatus.APPROVED.getValue(),
                        LoanStatus.ACTIVE.getValue()));

        final Integer numberOfActiveLoansSyncedWithThisCalendar = this.calendarInstanceRepository
                .countOfLoansSyncedWithCalendar(calendarId, loanStatuses);

        /*
         * areActiveEntitiesSynced is set to true, if there are any active loans
         * synced to this calendar.
         */

        if (numberOfActiveLoansSyncedWithThisCalendar > 0) {
            areActiveEntitiesSynced = true;
        }

        final Calendar calendarForUpdate = this.calendarRepository.findOne(calendarId);
        if (calendarForUpdate == null) {
            throw new CalendarNotFoundException(calendarId);
        }

        final Date oldStartDate = calendarForUpdate.getStartDate();
        // create calendar history before updating calendar
        final CalendarHistory calendarHistory = new CalendarHistory(calendarForUpdate, oldStartDate);

        Map<String, Object> changes = null;

        final Boolean reschedulebasedOnMeetingDates = command.booleanObjectValueOfParameterNamed(
                CALENDAR_SUPPORTED_PARAMETERS.RESCHEDULE_BASED_ON_MEETING_DATES.getValue());

        /*
         * System allows to change the meeting date by two means,
         * 
         * Option 1: reschedulebasedOnMeetingDates = false or reschedulebasedOnMeetingDates is not passed 
         * By directly editing the recurring day with effective from
         * date and system decides the next meeting date based on some sensible
         * logic (i.e., number of minimum days between two repayments)
         * 
         * 
         * Option 2: reschedulebasedOnMeetingDates = true 
         * By providing alternative meeting date for one of future
         * meeting date and derive the day of recurrence from the new meeting
         * date. Ex: User proposes new meeting date say "14/Nov/2014" for
         * present meeting date "12/Nov/2014", based on this input other values
         * re derived and loans are rescheduled
         * 
         */

        LocalDate newMeetingDate = null;
        LocalDate presentMeetingDate = null;

        if (reschedulebasedOnMeetingDates != null && reschedulebasedOnMeetingDates) {

            newMeetingDate = command
                    .localDateValueOfParameterNamed(CALENDAR_SUPPORTED_PARAMETERS.NEW_MEETING_DATE.getValue());
            presentMeetingDate = command
                    .localDateValueOfParameterNamed(CALENDAR_SUPPORTED_PARAMETERS.PRESENT_MEETING_DATE.getValue());

            /*
             * New meeting date proposed will become the new start date for the
             * updated calendar
             */

            changes = calendarForUpdate.updateStartDateAndDerivedFeilds(newMeetingDate);

        } else {
            changes = calendarForUpdate.update(command, areActiveEntitiesSynced);
        }

        if (!changes.isEmpty()) {
            // update calendar history table only if there is a change in
            // calendar start date.
            if (reschedulebasedOnMeetingDates == null) {
                presentMeetingDate = command
                        .localDateValueOfParameterNamed(CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue());
            }
            if (null != newMeetingDate) {
                final Date endDate = presentMeetingDate.minusDays(1).toDate();
                calendarHistory.updateEndDate(endDate);
            }
            this.calendarHistoryRepository.save(calendarHistory);
            Set<CalendarHistory> history = calendarForUpdate.getCalendarHistory();
            history.add(calendarHistory);
            calendarForUpdate.updateCalendarHistory(history);
            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,
                            reschedulebasedOnMeetingDates, presentMeetingDate, newMeetingDate);

                }
            }
        }

        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();
    }
}