All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.brihaspathee.zeus.helper.impl.EnrollmentSpanHelperImpl Maven / Gradle / Ivy

The newest version!
package com.brihaspathee.zeus.helper.impl;

import com.brihaspathee.zeus.constants.EnrollmentSpanStatus;
import com.brihaspathee.zeus.domain.entity.Account;
import com.brihaspathee.zeus.domain.entity.EnrollmentSpan;
import com.brihaspathee.zeus.domain.entity.PremiumPayment;
import com.brihaspathee.zeus.domain.entity.PremiumSpan;
import com.brihaspathee.zeus.domain.repository.EnrollmentSpanRepository;
import com.brihaspathee.zeus.dto.account.*;
import com.brihaspathee.zeus.exception.EnrollmentSpanNotFoundException;
import com.brihaspathee.zeus.helper.interfaces.EnrollmentSpanHelper;
import com.brihaspathee.zeus.helper.interfaces.PremiumPaymentHelper;
import com.brihaspathee.zeus.helper.interfaces.PremiumSpanHelper;
import com.brihaspathee.zeus.mapper.interfaces.EnrollmentSpanMapper;
import com.brihaspathee.zeus.mapper.interfaces.PremiumPaymentMapper;
import com.brihaspathee.zeus.mapper.interfaces.PremiumSpanMapper;
import com.brihaspathee.zeus.service.TransactionProcessingContext;
import com.brihaspathee.zeus.service.interfaces.AccountProcessorService;
import com.brihaspathee.zeus.service.interfaces.MemberManagementService;
import com.brihaspathee.zeus.web.model.PremiumPeriodDto;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.swing.text.html.Option;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * Created in Intellij IDEA
 * User: Balaji Varadharajan
 * Date: 29, April 2024
 * Time: 5:16 PM
 * Project: Zeus
 * Package Name: com.brihaspathee.zeus.helper.impl
 * To change this template use File | Settings | File and Code Template
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class EnrollmentSpanHelperImpl implements EnrollmentSpanHelper {

    /**
     * Instance of enrollment span repository
     */
    private final EnrollmentSpanRepository enrollmentSpanRepository;

    /**
     * Instance of the enrollment span mapper
     */
    private final EnrollmentSpanMapper enrollmentSpanMapper;

    /**
     * The instance of the premium span helper
     */
    private final PremiumSpanHelper premiumSpanHelper;

    /**
     * The instance of the premium payment helper
     */
    private final PremiumPaymentHelper premiumPaymentHelper;

    /**
     * Instance of the member management service
     */
    private final MemberManagementService memberManagementService;

    /**
     * Instance of the account processor service
     */
    private final AccountProcessorService accountProcessorService;

    /**
     * Instance of the premium span mapper
     */
    private final PremiumSpanMapper premiumSpanMapper;


    /**
     * Update the enrollment spans
     * @param context - Context for the transaction
     * @param account - The account for which the enrollment span is to be updated
     * @param enrollmentSpanDtos - the enrollment spans that needs to be updated
     */
    @Override
    public void updateEnrollmentSpan(TransactionProcessingContext context, Account account, List enrollmentSpanDtos) {
        // return if the list is null or empty
        if(enrollmentSpanDtos == null || enrollmentSpanDtos.isEmpty()){
            return;
        }
        // iterate through the enrollment span to see if the enrollment span is changed
        enrollmentSpanDtos.forEach(enrollmentSpanDto -> {
            EnrollmentSpan enrollmentSpan = getEnrollmentSpan(enrollmentSpanDto.getEnrollmentSpanCode());
            if(enrollmentSpan == null){
                // enrollment span is not present with the account
                // create the enrollment span
                updateEnrollmentSpan(context, account, null, enrollmentSpanDto);
            }else{
                // The enrollment span exist
                // so set the enrollment span sk before updating it
                log.info("Updating enrollment span: {}", enrollmentSpanDto.getEnrollmentSpanCode());
                log.info("Status of the enrollment span: {}", enrollmentSpanDto.getEnrollmentSpanCode());
                enrollmentSpanDto.setEnrollmentSpanSK(enrollmentSpan.getEnrollmentSpanSK());
                updateEnrollmentSpan(context, account, enrollmentSpan, enrollmentSpanDto);
//                // The enrollment span exists, check if it has changed
//                if (enrollmentSpanDto.getChanged().get()){
//                    // The enrollment span exist
//                    // so set the enrollment span sk before updating it
//                    enrollmentSpanDto.setEnrollmentSpanSK(enrollmentSpan.getEnrollmentSpanSK());
//                    updateEnrollmentSpan(context, account, enrollmentSpan, enrollmentSpanDto);
//                }else{
//                    // enrollment span is not changed
//                    // check if premium spans have changed
//                    if(enrollmentSpanDto.getPremiumSpans() != null){
//                        premiumSpanHelper.updatePremiumSpans(enrollmentSpan, enrollmentSpanDto.getPremiumSpans().stream().toList());
//                    }
//                }

            }
//            if(enrollmentSpanDto.getChanged().get()){
//                log.info("About to update the enrollment span");
//                // update only if the enrollment span has changed
//                // Check if the enrollment span is present with the account
//
//                if(enrollmentSpan == null){
//                    // enrollment span is not present with the account
//                    // create the enrollment span
//                    updateEnrollmentSpan(context, account, enrollmentSpanDto, true);
//                }else{
//                    // The enrollment span exist
//                    // so set the enrollment span sk before updating it
//                    enrollmentSpanDto.setEnrollmentSpanSK(enrollmentSpan.getEnrollmentSpanSK());
//                    updateEnrollmentSpan(context, account, enrollmentSpanDto, false);
//                }
//            }else{
//                // enrollment span is not changed
//                // check if premium spans have changed
//                if(enrollmentSpanDto.getPremiumSpans() != null){
//                    premiumSpanHelper.updatePremiumSpans(enrollmentSpan, enrollmentSpanDto.getPremiumSpans().stream().toList());
//                }
//
//            }
        });

    }

    /**
     * Update enrollment span
     * @param enrollmentSpan
     * @param effectuationDate
     * @param paidThruDate
     * @param status
     */
    private void updateEnrollmentSpan(EnrollmentSpan enrollmentSpan,
                                     LocalDate effectuationDate,
                                     LocalDate paidThruDate,
                                     String status) {
        LocalDate currentPTD = enrollmentSpan.getPaidThroughDate();
        String currentStatus = enrollmentSpan.getStatusTypeCode();
        // Check if either the paid through date or the current status is different from what is passed in the input
        if(currentPTD == null || !paidThruDate.isEqual(currentPTD) || !currentStatus.equals(status)){
            enrollmentSpan.setPaidThroughDate(paidThruDate);
            enrollmentSpan.setClaimPaidThroughDate(paidThruDate);
            enrollmentSpan.setStatusTypeCode(status);
            enrollmentSpan.setEffectuationDate(effectuationDate);
            enrollmentSpan = enrollmentSpanRepository.save(enrollmentSpan);
            memberManagementService.updateEnrollmentSpan(EnrollmentSpanDto.builder()
                            .enrollmentSpanCode(enrollmentSpan.getEnrollmentSpanCode())
                            .effectuationDate(enrollmentSpan.getEffectuationDate())
                            .paidThroughDate(enrollmentSpan.getPaidThroughDate())
                            .statusTypeCode(enrollmentSpan.getStatusTypeCode())
                    .build());
        }
    }

    /**
     * Create the enrollment span dto from enrollment span and set it in the account dto
     * @param accountDto
     * @param enrollmentSpans
     */
    @Override
    public void setEnrollmentSpan(AccountDto accountDto, List enrollmentSpans) {
        Set enrollmentSpanDtos = new HashSet<>();
        enrollmentSpans.forEach(enrollmentSpan -> {
            EnrollmentSpanDto enrollmentSpanDto = enrollmentSpanMapper.enrollmentSpanToEnrollmentSpanDto(enrollmentSpan);
            premiumSpanHelper.setPremiumSpan(enrollmentSpanDto, enrollmentSpan.getPremiumSpans());
            premiumPaymentHelper.setPremiumPayment(enrollmentSpanDto, enrollmentSpan.getPremiumPayments());
            enrollmentSpanDtos.add(enrollmentSpanDto);
        });
        accountDto.setEnrollmentSpans(enrollmentSpanDtos);
    }

    /**
     * Manage premium payments for an enrollment span and
     * update the enrollment span appropriately
     * @param premiumPaymentDto
     */
    @Override
    public void createPremiumPayment(PremiumPaymentDto premiumPaymentDto) {
        String enrollmentSpanCode = premiumPaymentDto.getEnrollmentSpanCode();
        Optional optionalEnrollmentSpan = enrollmentSpanRepository.findEnrollmentSpanByEnrollmentSpanCode(enrollmentSpanCode);
        if (optionalEnrollmentSpan.isEmpty()){
            throw new EnrollmentSpanNotFoundException("Enrollment span with enrollment span code " +
                    enrollmentSpanCode + " not found");
        }
        EnrollmentSpan enrollmentSpan = optionalEnrollmentSpan.get();
        List payments = premiumPaymentHelper.createPremiumPayment(enrollmentSpan, premiumPaymentDto);
        LocalDate effectuationDate = null;
        if(enrollmentSpan.getEffectuationDate() == null){
            effectuationDate = determineEffectuationDate(payments);
        }else{
            effectuationDate = enrollmentSpan.getEffectuationDate();
        }
        LocalDate paidThroughDate = determinePaidThruDate(enrollmentSpan.getPremiumSpans(),
                payments);

        if (paidThroughDate != null){
            updateEnrollmentSpan(enrollmentSpan,
                    effectuationDate,
                    paidThroughDate,
                    EnrollmentSpanStatus.ENROLLED.name());
        }
    }

    /**
     * Determine effectuation date of an enrollment span from the list of payments
     * @param payments
     * @return
     */
    private LocalDate determineEffectuationDate(List payments) {
        LocalDate effectuationDate = null;
        if(!payments.isEmpty()){
            if(payments.size() == 1){
                PremiumPayment premiumPayment = payments.getFirst();
                effectuationDate = premiumPayment.getPaymentDate();
            }else{
                effectuationDate = payments.stream()
                        .map(PremiumPayment::getPaymentDate)
                        .filter(Objects::nonNull)
                        .min(LocalDate::compareTo)
                        .orElse(null);
            }
        }
        return effectuationDate;
    }

    /**
     * Determine the paid through date
     * @param premiumSpans
     * @param premiumPayments
     * @return
     */
    private LocalDate determinePaidThruDate(List premiumSpans,
                                            List premiumPayments) {
        if(premiumPayments == null || premiumPayments.isEmpty()){
            return null;
        }
        // filter our any canceled premium spans
        // The calculation of paid thru date has to be done only using active premium spans
        List nonCancelledPremiumSpans = new ArrayList<>(premiumSpans.stream()
                .filter(premiumSpan ->
                        premiumSpan.getStatusTypeCode().equals("ACTIVE")).toList());
        AtomicReference> premiumPeriods = new AtomicReference<>(new ArrayList<>());
        AtomicReference cumulativeTotalRestAmt = new AtomicReference<>(BigDecimal.ZERO);
        log.info("Non cancelled premium spans:{}", nonCancelledPremiumSpans.size());
        nonCancelledPremiumSpans.sort(Comparator.comparing(PremiumSpan::getStartDate));
        nonCancelledPremiumSpans.forEach(premiumSpan -> {
            BigDecimal totRestAmt = premiumSpan.getTotalResponsibleAmount();
            LocalDate startDate = premiumSpan.getStartDate();
            LocalDate endDate = premiumSpan.getEndDate();
            List localPremiumPeriods = premiumPeriods(startDate, endDate, totRestAmt);
            for (PremiumPeriodDto premiumPeriodDto : localPremiumPeriods) {
                cumulativeTotalRestAmt.set(setTotalPaidThruAmount(premiumPeriodDto, cumulativeTotalRestAmt.get()));
            }
            premiumPeriods.get().addAll(localPremiumPeriods);
        });
        premiumPeriods.get().sort(Comparator.comparing(PremiumPeriodDto::getStartPeriod));
        premiumPeriods.get().forEach(premiumPeriodDto -> {
            log.info("Premium period Start Date: {}", premiumPeriodDto.getStartPeriod());
            log.info("Premium period End Date: {}", premiumPeriodDto.getEndPeriod());
            log.info("Premium period responsibility amount: {}", premiumPeriodDto.getResponsibilityAmount());
            log.info("Premium period Premium Percent: {}", premiumPeriodDto.getPremiumPercent());
            log.info("Premium period Paid Through Amount: {}", premiumPeriodDto.getPaidThroughAmount());
        });
        BigDecimal premiumPaymentTotal = premiumPayments.stream().map(PremiumPayment::getPremiumPayment).reduce(BigDecimal.ZERO, BigDecimal::add);
        List paidThroughPeriods =
                premiumPeriods.get()
                        .stream()
                        .takeWhile(premiumPeriodDto ->
                                premiumPeriodDto.getPaidThroughAmount()
                                        .compareTo(premiumPaymentTotal) <= 0)
                        .toList();
        if(paidThroughPeriods.isEmpty()){
            log.info("Paid through periods is empty");
            return null;
        }else {
            return paidThroughPeriods.getLast().getEndPeriod();
        }
    }

    /**
     * Identify the list of end dates of each month between the two dates
     * @param startDate
     * @param endDate
     * @param totRestAmt
     * @return
     */
    private static List premiumPeriods(LocalDate startDate,
                                                  LocalDate endDate,
                                                  BigDecimal totRestAmt) {
        List endOfMonthDates = new ArrayList<>();
        List premiumPeriodDtos = new ArrayList<>();
        LocalDate monthEndDate = startDate.with(TemporalAdjusters.lastDayOfMonth());
        while (!monthEndDate.isAfter(endDate)) {
//            endOfMonthDates.add(monthEndDate);
            LocalDate monthStartDate = monthEndDate.with(TemporalAdjusters.firstDayOfMonth());
            if(monthStartDate.isBefore(startDate)){
                double premiumPercent = monthsBetween(startDate, monthEndDate);
                premiumPeriodDtos.add(PremiumPeriodDto.builder()
                        .startPeriod(startDate)
                        .endPeriod(monthEndDate)
                        .premiumPercent(premiumPercent)
                        .responsibilityAmount(totRestAmt.multiply(BigDecimal.valueOf(premiumPercent)))
                        .build());
            }else{
                double premiumPercent = monthsBetween(monthStartDate, monthEndDate);
                premiumPeriodDtos.add(PremiumPeriodDto.builder()
                        .startPeriod(monthStartDate)
                        .endPeriod(monthEndDate)
                        .premiumPercent(premiumPercent)
                        .responsibilityAmount(totRestAmt.multiply(BigDecimal.valueOf(premiumPercent)))
                        .build());
            }
            monthEndDate = monthEndDate.plusMonths(1).with(TemporalAdjusters.lastDayOfMonth());
        }
        if(!premiumPeriodDtos.isEmpty()){
            PremiumPeriodDto premiumPeriodDto = premiumPeriodDtos.getLast();
            if(!premiumPeriodDto.getEndPeriod().isEqual(endDate)){
                LocalDate monthStartDate = endDate.with(TemporalAdjusters.firstDayOfMonth());
                double premiumPercent = monthsBetween(monthStartDate, endDate);
                premiumPeriodDtos.add(PremiumPeriodDto.builder()
                        .startPeriod(monthStartDate)
                        .endPeriod(endDate)
                        .premiumPercent(premiumPercent)
                        .responsibilityAmount(totRestAmt.multiply(BigDecimal.valueOf(premiumPercent)))
                        .build());
            }
        }else{
            double premiumPercent = monthsBetween(startDate, endDate);
            premiumPeriodDtos.add(PremiumPeriodDto.builder()
                    .startPeriod(startDate)
                    .endPeriod(endDate)
                    .premiumPercent(premiumPercent)
                    .responsibilityAmount(totRestAmt.multiply(BigDecimal.valueOf(premiumPercent)))
                    .build());
        }
        premiumPeriodDtos.sort(Comparator.comparing(PremiumPeriodDto::getStartPeriod));
        return premiumPeriodDtos;
    }

    /**
     * Determines the number of months between the two given dates
     * @param startDate
     * @param endDate
     * @return
     */
    private static double monthsBetween(LocalDate startDate, LocalDate endDate) {

        // Get the number of full months between the two dates
        long fullMonthsBetween = ChronoUnit.MONTHS.between(startDate.withDayOfMonth(1), endDate.withDayOfMonth(1));

        // Calculate the remaining days if the end date is not the start of a new month
        LocalDate tempDate = startDate.plusMonths(fullMonthsBetween);
        if (tempDate.isAfter(endDate)) {
            fullMonthsBetween--;
            tempDate = startDate.plusMonths(fullMonthsBetween);
        }

        // Calculate the days remaining after the full months
        long remainingDays = ChronoUnit.DAYS.between(tempDate, endDate.plusDays(1)); // plusDays(1) to include end date

        // Check if remaining days complete a month
        double totalMonths = fullMonthsBetween;
        if (remainingDays == tempDate.lengthOfMonth()) {
            totalMonths += 1;
        } else if (remainingDays > 0) {
            totalMonths += (double) remainingDays / tempDate.lengthOfMonth();
        }
//        log.info("Total number of months between {} and {} is: {}", startDate, endDate, totalMonths);
        return totalMonths;
    }

    /**
     * Set the total paid through amount
     * @param premiumPeriodDto
     * @param totalPaidThruAmount
     * @return
     */
    private static BigDecimal setTotalPaidThruAmount(PremiumPeriodDto premiumPeriodDto,
                                              BigDecimal totalPaidThruAmount) {
//        log.info("Paid Through Amount before add {}", totalPaidThruAmount);
        totalPaidThruAmount = totalPaidThruAmount.add(premiumPeriodDto.getResponsibilityAmount());
//        log.info("Premium period dto:{}", premiumPeriodDto);
//        log.info("Paid Through Amount after add {}", totalPaidThruAmount);
        premiumPeriodDto.setPaidThroughAmount(totalPaidThruAmount.setScale(2, RoundingMode.HALF_UP));
        return totalPaidThruAmount;

    }

    /**
     * get the enrollment span matching the enrollment span code if present in the database
     * @param enrollmentSpanCode
     * @return
     */
    private EnrollmentSpan getEnrollmentSpan(String enrollmentSpanCode){
        Optional optional = enrollmentSpanRepository.findEnrollmentSpanByEnrollmentSpanCode(enrollmentSpanCode);
        return optional.orElse(null);
    }

    /**
     * Create the new enrollment span
     * @param context - context of the transaction
     * @param account - the account to which the enrollment span is to be associated
     * @param currentEnrollmentSpan - The current state of the enrollment span if it is present in the account
     * @param enrollmentSpanDto - the enrollment span to be created
     */
    private void updateEnrollmentSpan(TransactionProcessingContext context,
                                      Account account,
                                      EnrollmentSpan currentEnrollmentSpan,
                                      EnrollmentSpanDto enrollmentSpanDto){
        checkForEnrollmentSpanUpdates(context, account, currentEnrollmentSpan, enrollmentSpanDto);
        if(enrollmentSpanDto.getChanged().get() ||
        context.isSendUpdateToMMS()){
            // if either the enrollment span was updated based on the transaction that was received
            // or it was updated based on the paid through date determination
            // the enrollment span has to be updated
            EnrollmentSpan enrollmentSpan = enrollmentSpanMapper.enrollmentSpanDtoToEnrollmentSpan(enrollmentSpanDto);
            enrollmentSpan.setAccount(account);
            log.info("Enrollment span to be created is:{}", enrollmentSpan.getEnrollmentSpanCode());
            enrollmentSpan = enrollmentSpanRepository.save(enrollmentSpan);
            log.info("Enrollment span created is:{}", enrollmentSpan.getEnrollmentSpanSK());
            premiumSpanHelper.updatePremiumSpans(enrollmentSpan,
                    enrollmentSpanDto
                            .getPremiumSpans()
                            .stream()
                            .toList());
        }else{
            // enrollment span is not updated
            // check if any updates are necessary for premium spans
            premiumSpanHelper.updatePremiumSpans(currentEnrollmentSpan,
                    enrollmentSpanDto
                            .getPremiumSpans()
                            .stream()
                            .toList());
        }

        if(context.isSendUpdateToMMS()){
            // If an update about the enrollment span is to be sent to MMS then send the update
            EnrollmentSpanDto updatedEnrollmentSpan = EnrollmentSpanDto.builder()
                    .enrollmentSpanCode(enrollmentSpanDto.getEnrollmentSpanCode())
                    .statusTypeCode(enrollmentSpanDto.getStatusTypeCode())
                    .effectuationDate(enrollmentSpanDto.getEffectuationDate())
                    .paidThroughDate(enrollmentSpanDto.getPaidThroughDate())
                    .claimPaidThroughDate(enrollmentSpanDto.getClaimPaidThroughDate())
                    .build();
            memberManagementService.updateEnrollmentSpan(updatedEnrollmentSpan);
        }
    }

    /**
     * Determine the paid through date for an enrollment span the is either newly created
     * or has been updated by transaction processing
     * @param context - context of the transaction
     * @param account  - Account associated with the enrollment span
     * @param currentEnrollmentSpan - current state of the enrollment span if it exists
     * @param enrollmentSpanDto - the new enrollment span
     */
    private void checkForEnrollmentSpanUpdates(TransactionProcessingContext context,
                                       Account account,
                                       EnrollmentSpan currentEnrollmentSpan,
                                       EnrollmentSpanDto enrollmentSpanDto){
        String spanStatus = enrollmentSpanDto.getStatusTypeCode();
        log.info("Status of the span to be updated:{}", spanStatus);
        List premiumPayments = premiumPaymentHelper.getPremiumPayments(enrollmentSpanDto.getEnrollmentSpanCode());
        if(currentEnrollmentSpan == null){
            // Do these steps if a new enrollment span is to be created
            // If enrollment span status is enrolled and paid through date is not set
            // then set the paid through date to the end of the year
            if(spanStatus.equals(EnrollmentSpanStatus.ENROLLED.toString())){
                EnrollmentSpan priorEnrollmentSpan = getPriorEnrollmentSpan(account, enrollmentSpanDto.getStartDate());
                LocalDate paidThruDate = determinePaidThruDate(priorEnrollmentSpan, enrollmentSpanDto, premiumPayments);
                enrollmentSpanDto.setPaidThroughDate(paidThruDate);
                enrollmentSpanDto.setClaimPaidThroughDate(paidThruDate);
                context.setSendUpdateToMMS(true);
            }
        }else{
            // the enrollment span is not new
            LocalDate currentPaidThruDate = currentEnrollmentSpan.getPaidThroughDate();
            if(currentPaidThruDate == null &&
                    spanStatus.equals(EnrollmentSpanStatus.ENROLLED.toString())){
                // There is no paid through date in the current enrollment span and the new status is enrolled
                // Determine if a paid thru date needs to be set
                LocalDate paidThruDate = determinePaidThruDate(null, enrollmentSpanDto, premiumPayments);
                if(paidThruDate !=null){
                    enrollmentSpanDto.setPaidThroughDate(paidThruDate);
                    enrollmentSpanDto.setClaimPaidThroughDate(paidThruDate);
                    context.setSendUpdateToMMS(true);
                }
            }else if(currentPaidThruDate != null &&
                    !spanStatus.equals(EnrollmentSpanStatus.CANCELED.toString())){
                // the current enrollment span has a paid through date (which means the status cannot be premember)
                // and the new status is not canceled
                // check to see if the effectuation date needs to be removed and enrollment span status needs to be
                //       calculated - This will be done as below
                //                    Retrieve the previous enrollment span that is before this span
                //                    if no previous span exists or if there is a gap in coverage or if the previous span
                //                    is enrolled in a different plan then the effectuation date needs to be removed
                //                    CPTD and PTD needs to be removed, the span status needs to be recalculated
                EnrollmentSpan priorEnrollmentSpan = getPriorEnrollmentSpan(account, enrollmentSpanDto.getStartDate());
                LocalDate paidThruDate = determinePaidThruDate(priorEnrollmentSpan, enrollmentSpanDto, premiumPayments);
                if(paidThruDate!= null){
                    // If paid through date is not null and if it is different
                    // from the current paid through date
                    // then set the PTD and CPTD to the new value
                    if(!paidThruDate.isEqual(currentPaidThruDate)){
                        enrollmentSpanDto.setPaidThroughDate(paidThruDate);
                        enrollmentSpanDto.setClaimPaidThroughDate(paidThruDate);
                        context.setSendUpdateToMMS(true);
                    }
                    // if it is not null but is same as the current date then do not update
                }else{
                    // If new paid through date is null then remove
                    // PTD, CPTD and effectuation date from the span
                    enrollmentSpanDto.setPaidThroughDate(null);
                    enrollmentSpanDto.setClaimPaidThroughDate(null);
                    enrollmentSpanDto.setEffectuationDate(null);
                    context.setSendUpdateToMMS(true);
                }
            }else if(currentPaidThruDate != null &&
                    spanStatus.equals(EnrollmentSpanStatus.CANCELED.toString())){
                // The enrollment span is being canceled and contains a paid through date
                // this means the below dates needs to be set to null as they are no longer appropriate for the span
                // Effectuation Date, Paid through date and Claim Paid through date
                enrollmentSpanDto.setPaidThroughDate(null);
                enrollmentSpanDto.setClaimPaidThroughDate(null);
                enrollmentSpanDto.setEffectuationDate(null);
                context.setSendUpdateToMMS(true);

            }
        }
    }

    /**
     * Determine the paid through date of the enrollment span
     * @param priorEnrollmentSpan - Enrollment span prior to the span to which the paid through date is requested
     * @param enrollmentSpanDto - the enrollment span for which the paid through date is requested
     * @param premiumPayments - Any premium payments received for the enrollment span
     * @return
     */
    private LocalDate determinePaidThruDate(EnrollmentSpan priorEnrollmentSpan,
                                                   EnrollmentSpanDto enrollmentSpanDto,
                                                   List premiumPayments) {
        // paymentPTD is the paid through date determined based on the premium payments
        // made by the member
        LocalDate paymentPTD = determinePaidThruDate(premiumSpanMapper.premiumSpanDtosToPremiumSpans(
                enrollmentSpanDto.getPremiumSpans().stream().toList()),
                premiumPayments);
        log.info("PTD based on payment Date:{}", paymentPTD);
        // psPTD is the paid through date determined purely based on the premium spans total responsibility amount
        // or if the enrollment span has the same plan as the prior enrollment span and does not have any gap
        // in coverage
        LocalDate psPTD;
        // Take all the premium spans where the total responsibility amount is zero
        log.info("Premiums spans present in the dto:{}", enrollmentSpanDto.getPremiumSpans());
        List totRespZeroPremiumSpans = enrollmentSpanDto.getPremiumSpans()
                .stream()
                .sorted(Comparator.comparing(PremiumSpanDto::getSequence))
                .takeWhile(premiumSpanDto ->
                        BigDecimal.ZERO.compareTo(
                                premiumSpanDto.getTotalResponsibleAmount()) == 0)
                .toList();
//        List totRespZeroPremiumSpans = enrollmentSpanDto.getPremiumSpans()
//                .stream()
//                .filter(premiumSpanDto ->
//                        BigDecimal.ZERO.compareTo(
//                                premiumSpanDto.getTotalResponsibleAmount()) == 0)
//                .toList();
        log.info("Premiums spans where the TOTRESAMT is zero:{}", totRespZeroPremiumSpans);
        if(totRespZeroPremiumSpans.isEmpty()){
            log.info("There are no premium spans where TOTRESAMT is zero");
            log.info("Enrollment span prior to the current enrollment span:{}", priorEnrollmentSpan);
            // If total responsibility amount is not zero even for a single premium span
            // then check if prior enrollment span exist and the prior enrollment span has the
            // same plan and there is no gap in coverage
            // If not the paid through date will be null
            if(priorEnrollmentSpan!=null && isSamePlan(enrollmentSpanDto.getPlanId(), priorEnrollmentSpan.getPlanId()) &&
                    !isThereGapInCoverage(enrollmentSpanDto.getStartDate(), priorEnrollmentSpan)) {
                psPTD = enrollmentSpanDto.getStartDate().with(TemporalAdjusters.lastDayOfMonth());
            } else {
                psPTD = null;
            }
//            }else {
//                // This means either there is no prior enrollment span or
//                // the prior enrollment span does not have the same plan or
//                // there is gap in coverage
//                return null;
//            }

        }else{
            log.info("There are premium spans where TOTRESAMT is zero");
            // if there are premium spans where the total responsibility amount is zero
            // get the end date of the last premium span where the total responsibility amount is zero
            psPTD = totRespZeroPremiumSpans.stream()
                    .max(Comparator.comparing(PremiumSpanDto::getEndDate))
                    .map(PremiumSpanDto::getEndDate).orElse(null);
        }
        log.info("Premium Span PTD:{}", psPTD);
        // The logic is if paymentPTD and psPTD are both present, the final paid through date will be the greatest of the two
        // If only one present then it will be sent back as the paid through date
        // if both are absent then null should be returned
        //
        // 1. Optional.ofNullable(paymentPTD) - Wraps "paymentPTD" in an optional which will be empty if "paymentPTD" is null
        // 2. filter(paymentPaidThruDate -> Optional.ofNullable(psPTD)
        //       --- Applies the filter to paymentPTD is present
        // 3. map(paymentPaidThruDate::isAfter)
        //        --- Applies the mapping function to "psPTD" if present
        //        --- The mapping function checks if "paymentPaidThruDate" is after "psPTD". It returns a boolean indicating
        //            whether "paymentPaidThruDate" is after "psPTD"
        // 4. orElse(true)
        //         --- If psPTD is null, the mapping function cannot be applied, so the orElse provides a default value of true.
        //         --- This means if psPTD is null, the filter condition will be true, allowing paymentPTD to pass the filter.
        // 5. orElse(psPTD)
        //         --- If paymentPTD is present and passes the filter (meaning paymentPaidThruDate is after psPTD or psPTD is null), paymentPTD is returned.
        //         --- If paymentPTD is not present (i.e., it is null), or it does not pass the filter (i.e., paymentPTD is not after psPTD), psPTD is returned.
        return Optional.ofNullable(paymentPTD).filter(paymentPaidThruDate -> Optional.ofNullable(psPTD)
                .map(paymentPaidThruDate::isAfter)
                .orElse(true))
                .orElse(psPTD);
    }

    /**
     * Get the prior enrollment span
     * @param account
     * @param spanStartDate
     * @return
     */
    private EnrollmentSpan getPriorEnrollmentSpan(Account account, LocalDate spanStartDate){
        // get the list of all enrollment spans associated with the account currently
        List enrollmentSpans = enrollmentSpanRepository.findEnrollmentSpanByAccount_AccountNumber(
                account.getAccountNumber());
        enrollmentSpans =
                enrollmentSpans.stream()
                        .sorted(Comparator.comparing(EnrollmentSpan::getStartDate))
                        .collect(Collectors.toList());
        enrollmentSpans =
                enrollmentSpans.stream()
                        .takeWhile(
                                enrollmentSpanDto ->
                                        enrollmentSpanDto.getEndDate().isBefore(spanStartDate))
                        .collect(Collectors.toList());
        enrollmentSpans = enrollmentSpans.stream()
                .filter(
                        enrollmentSpan1 ->
                                !enrollmentSpan1.getStatusTypeCode()
                                        .equals(EnrollmentSpanStatus.CANCELED.toString()))
                .collect(Collectors.toList());
        if(!enrollmentSpans.isEmpty()){
            return enrollmentSpans.getLast();
        }else {
            return null;
        }
    }

    /**
     * Determine if the planIds are same
     * @param currentPlanId
     * @param priorPlanId
     * @return
     */
    private boolean isSamePlan(String currentPlanId,
                                      String priorPlanId){
        return currentPlanId.equals(priorPlanId);
    }

    /**
     * Determine if there is a gap between the current and the prior enrollment span
     * @param effectiveStartDate of the enrollment span to be created
     * @param priorEnrollmentSpan the prior year enrollment span
     * @return return true if there is a gap in coverage
     */
    private boolean isThereGapInCoverage(LocalDate effectiveStartDate,
                                                EnrollmentSpan priorEnrollmentSpan){
        if(priorEnrollmentSpan.getStatusTypeCode().equals(EnrollmentSpanStatus.CANCELED.toString())){
            return true;
        }else{
            long numOfDays = ChronoUnit.DAYS.between(priorEnrollmentSpan.getEndDate(),
                    effectiveStartDate);
            // numOfDays will be 1 if the end date of the prior enrollment span is the day prior to the
            // start of the current enrollment span

            // num of days will be greater than one if there is a gap between the end of the prior enrollment span
            // and start of the current enrollment span

            // num of days will be 0 if the end date of the prior enrollment span is same as that of the start date of the
            // current enrollment span

            // number of days will be -ve if the end date of the prior enrollment span is greater than the start date
            // of the current enrollment span
            if(numOfDays > 1){
                // This means that there is a gap between the end of the prior enrollment span
                // and start of the current enrollment span hence return tru
                return true;
            }else{
                // in all other cases there is no gap, hence return false
                return false;
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy