org.kuali.kpme.tklm.leave.payout.service.LeavePayoutServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kpme.tklm.leave.payout.service.LeavePayoutServiceImpl.java

Source

/**
 * Copyright 2004-2014 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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 org.kuali.kpme.tklm.leave.payout.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.joda.time.LocalDate;
import org.kuali.kpme.core.api.accrualcategory.AccrualCategory;
import org.kuali.kpme.core.api.accrualcategory.rule.AccrualCategoryRule;
import org.kuali.kpme.core.service.HrServiceLocator;
import org.kuali.kpme.core.util.HrConstants;
import org.kuali.kpme.core.util.HrContext;
import org.kuali.kpme.core.util.TKUtils;
import org.kuali.kpme.tklm.api.leave.block.LeaveBlock;
import org.kuali.kpme.tklm.api.leave.override.EmployeeOverrideContract;
import org.kuali.kpme.tklm.common.LMConstants;
import org.kuali.kpme.tklm.leave.block.LeaveBlockBo;
import org.kuali.kpme.tklm.leave.override.EmployeeOverride;
import org.kuali.kpme.tklm.leave.payout.LeavePayout;
import org.kuali.kpme.tklm.leave.payout.dao.LeavePayoutDao;
import org.kuali.kpme.tklm.leave.service.LmServiceLocator;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kns.service.KNSServiceLocator;
import org.kuali.rice.krad.maintenance.MaintenanceDocument;
import org.kuali.rice.krad.service.KRADServiceLocator;
import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krad.util.ObjectUtils;

public class LeavePayoutServiceImpl implements LeavePayoutService {

    private LeavePayoutDao leavePayoutDao;
    private static final Logger LOG = Logger.getLogger(LeavePayoutServiceImpl.class);

    @Override
    public List<LeavePayout> getAllLeavePayoutsForPrincipalId(String principalId) {
        return leavePayoutDao.getAllLeavePayoutsForPrincipalId(principalId);
    }

    @Override
    public List<LeavePayout> getAllLeavePayoutsForPrincipalIdAsOfDate(String principalId, LocalDate effectiveDate) {
        return leavePayoutDao.getAllLeavePayoutsForPrincipalIdAsOfDate(principalId, effectiveDate);
    }

    @Override
    public List<LeavePayout> getAllLeavePayoutsByEffectiveDate(LocalDate effectiveDate) {
        return leavePayoutDao.getAllLeavePayoutsByEffectiveDate(effectiveDate);
    }

    @Override
    public LeavePayout getLeavePayoutById(String lmLeavePayoutId) {
        return leavePayoutDao.getLeavePayoutById(lmLeavePayoutId);
    }

    public LeavePayoutDao getLeavePayoutDao() {
        return leavePayoutDao;
    }

    public void setLeavePayoutDao(LeavePayoutDao leavePayoutDao) {
        this.leavePayoutDao = leavePayoutDao;
    }

    @Override
    public LeavePayout initializePayout(String principalId, String accrualCategoryRule, BigDecimal accruedBalance,
            LocalDate effectiveDate) {
        //Initially, principals may be allowed to edit the transfer amount when prompted to submit this balance transfer, however,
        //a base transfer amount together with a forfeited amount is calculated to bring the balance back to its limit in accordance
        //with transfer limits.
        LeavePayout leavePayout = null;
        AccrualCategoryRule accrualRule = HrServiceLocator.getAccrualCategoryRuleService()
                .getAccrualCategoryRule(accrualCategoryRule);

        if (ObjectUtils.isNotNull(accrualRule) && ObjectUtils.isNotNull(accruedBalance)) {
            leavePayout = new LeavePayout();
            //Leave summary is not a requirement, per se, but the information it contains is.
            //The only thing that is obtained from leave summary is the accrued balance of the leave summary row matching the
            //passed accrualCategoryRules accrual category.
            //These two objects are essential to balance transfers triggered when the employee submits their leave calendar for approval.
            //Neither of these objects should be null, otherwise this method could not have been called.
            AccrualCategory fromAccrualCategory = HrServiceLocator.getAccrualCategoryService()
                    .getAccrualCategory(accrualRule.getLmAccrualCategoryId());
            BigDecimal fullTimeEngagement = HrServiceLocator.getJobService()
                    .getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate);

            // AccrualRule.maxBalance == null -> no balance limit. No balance limit -> no accrual triggered transfer / payout / lose.
            // execution point should not be here if max balance on accrualRule is null, unless there exists an employee override.
            BigDecimal maxBalance = accrualRule.getMaxBalance();
            BigDecimal adjustedMaxBalance = maxBalance.multiply(fullTimeEngagement).setScale(2);

            BigDecimal maxPayoutAmount = null;
            BigDecimal adjustedMaxPayoutAmount = null;
            if (ObjectUtils.isNotNull(accrualRule.getMaxPayoutAmount())) {
                maxPayoutAmount = new BigDecimal(accrualRule.getMaxPayoutAmount());
                adjustedMaxPayoutAmount = maxPayoutAmount.multiply(fullTimeEngagement).setScale(2);
            } else {
                // no limit on transfer amount
                maxPayoutAmount = new BigDecimal(Long.MAX_VALUE);
                adjustedMaxPayoutAmount = maxPayoutAmount;
            }

            BigDecimal maxCarryOver = null;
            BigDecimal adjustedMaxCarryOver = null;
            if (ObjectUtils.isNotNull(accrualRule.getMaxCarryOver())) {
                maxCarryOver = new BigDecimal(accrualRule.getMaxCarryOver());
                adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2);
            } else {
                //no limit to carry over.
                maxCarryOver = new BigDecimal(Long.MAX_VALUE);
                adjustedMaxCarryOver = maxCarryOver;
            }

            EmployeeOverrideContract maxBalanceOverride = LmServiceLocator.getEmployeeOverrideService()
                    .getEmployeeOverride(principalId, fromAccrualCategory.getLeavePlan(),
                            fromAccrualCategory.getAccrualCategory(), "MB", effectiveDate);
            EmployeeOverrideContract maxPayoutAmountOverride = LmServiceLocator.getEmployeeOverrideService()
                    .getEmployeeOverride(principalId, fromAccrualCategory.getLeavePlan(),
                            fromAccrualCategory.getAccrualCategory(), "MPA", effectiveDate);
            EmployeeOverrideContract maxAnnualCarryOverOverride = LmServiceLocator.getEmployeeOverrideService()
                    .getEmployeeOverride(principalId, fromAccrualCategory.getLeavePlan(),
                            fromAccrualCategory.getAccrualCategory(), "MAC", effectiveDate);
            //Do not pro-rate override values for FTE.
            if (maxBalanceOverride != null)
                adjustedMaxBalance = new BigDecimal(maxBalanceOverride.getOverrideValue());
            if (maxPayoutAmountOverride != null)
                adjustedMaxPayoutAmount = new BigDecimal(maxPayoutAmountOverride.getOverrideValue());
            if (maxAnnualCarryOverOverride != null)
                adjustedMaxCarryOver = new BigDecimal(maxAnnualCarryOverOverride.getOverrideValue());

            BigDecimal transferAmount = accruedBalance.subtract(adjustedMaxBalance);
            if (transferAmount.compareTo(adjustedMaxPayoutAmount) > 0) {
                //there's forfeiture.
                //bring transfer amount down to the adjusted maximum transfer amount, and place excess in forfeiture.
                //accruedBalance - adjustedMaxPayoutAmount - adjustedMaxBalance = forfeiture.
                //transferAmount = accruedBalance - adjustedMaxBalance; forfeiture = transferAmount - adjustedMaxPayoutAmount.
                BigDecimal forfeiture = transferAmount.subtract(adjustedMaxPayoutAmount).setScale(2);
                forfeiture = forfeiture.stripTrailingZeros();
                leavePayout.setForfeitedAmount(forfeiture);
                leavePayout.setPayoutAmount(adjustedMaxPayoutAmount);
            } else {
                leavePayout.setPayoutAmount(transferAmount);
                leavePayout.setForfeitedAmount(BigDecimal.ZERO);
            }

            assert (adjustedMaxBalance.compareTo(accruedBalance
                    .subtract(leavePayout.getPayoutAmount().add(leavePayout.getForfeitedAmount()))) == 0);

            // Max Carry Over logic for Year End transfers.
            if (StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),
                    HrConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) {
                if (ObjectUtils.isNotNull(maxCarryOver)) {

                    if (ObjectUtils.isNull(adjustedMaxCarryOver))
                        adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2);
                    //otherwise, adjustedMaxCarryOver has an employee override value, which trumps accrual rule defined MAC.
                    //At this point, transfer amount and forfeiture have been set so that the new accrued balance will be the
                    //adjusted max balance, so this amount is used to check against carry over.
                    if (adjustedMaxBalance.compareTo(adjustedMaxCarryOver) > 0) {
                        BigDecimal carryOverDiff = adjustedMaxBalance.subtract(adjustedMaxCarryOver);

                        if (StringUtils.equals(accrualRule.getActionAtMaxBalance(),
                                HrConstants.ACTION_AT_MAX_BALANCE.LOSE)) {
                            //add carry over excess to forfeiture.
                            leavePayout.setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverDiff));
                        } else {
                            //maximize the transfer amount.
                            BigDecimal potentialPayoutAmount = leavePayout.getPayoutAmount().add(carryOverDiff);

                            //Can this entire amount be added to the transfer amount??
                            if (potentialPayoutAmount.compareTo(adjustedMaxPayoutAmount) <= 0) {
                                //yes
                                leavePayout.setPayoutAmount(leavePayout.getPayoutAmount().add(carryOverDiff));
                            } else {
                                //no
                                BigDecimal carryOverExcess = potentialPayoutAmount
                                        .subtract(adjustedMaxPayoutAmount);
                                //move excess to forfeiture
                                leavePayout
                                        .setForfeitedAmount(leavePayout.getForfeitedAmount().add(carryOverExcess));
                                //the remainder (if any) can be added to the transfer amount ( unless action is LOSE ).
                                leavePayout.setPayoutAmount(
                                        leavePayout.getPayoutAmount().add(carryOverDiff.subtract(carryOverExcess)));

                                assert (adjustedMaxCarryOver.compareTo(accruedBalance.subtract(
                                        leavePayout.getPayoutAmount().add(leavePayout.getForfeitedAmount()))) == 0);
                            }
                        }
                    }
                    //otherwise, given balance will be at or under the max annual carry over.
                }
            }

            leavePayout.setEffectiveLocalDate(effectiveDate);
            leavePayout.setAccrualCategoryRule(accrualCategoryRule);
            leavePayout.setFromAccrualCategory(fromAccrualCategory.getAccrualCategory());
            leavePayout.setPrincipalId(principalId);
            leavePayout.setUserPrincipalId(HrContext.getPrincipalId());
            leavePayout.setEarnCode(accrualRule.getMaxPayoutEarnCode());

        }
        return leavePayout;
    }

    @Override
    public LeavePayout payout(LeavePayout leavePayout) {
        if (ObjectUtils.isNull(leavePayout)) {
            //         throw new RuntimeException("did not supply a valid LeavePayout object.");
            LOG.error("did not supply a valid LeavePayout object.");
            return null;
        } else {
            List<LeaveBlockBo> leaveBlocks = new ArrayList<LeaveBlockBo>();
            BigDecimal transferAmount = leavePayout.getPayoutAmount();
            LeaveBlockBo aLeaveBlock = null;

            if (ObjectUtils.isNotNull(transferAmount)) {
                if (transferAmount.compareTo(BigDecimal.ZERO) > 0) {

                    aLeaveBlock = new LeaveBlockBo();
                    //Create a leave block that adds the adjusted transfer amount to the "transfer to" accrual category.
                    aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
                    aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
                    aLeaveBlock.setEarnCode(leavePayout.getEarnCode());
                    aLeaveBlock.setAccrualCategory(leavePayout.getEarnCodeObj().getAccrualCategory());
                    aLeaveBlock.setDescription("Amount payed out");
                    aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount());
                    aLeaveBlock.setAccrualGenerated(true);
                    aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
                    aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
                    aLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.REQUESTED);
                    aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
                    aLeaveBlock.setBlockId(0L);

                    //Want to store the newly created leave block id on this maintainable object
                    //when the status of the maintenance document encapsulating this maintainable changes
                    //the id will be used to fetch and update the leave block statuses.
                    LeaveBlock lb = LmServiceLocator.getLeaveBlockService().saveLeaveBlock(
                            LeaveBlockBo.to(aLeaveBlock), GlobalVariables.getUserSession().getPrincipalId());

                    leavePayout.setPayoutLeaveBlockId(lb.getLmLeaveBlockId());

                    //Create leave block that removes the correct transfer amount from the originating accrual category.
                    aLeaveBlock = new LeaveBlockBo();
                    aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
                    aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
                    aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode());
                    aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory());
                    aLeaveBlock.setDescription("Payout amount");
                    aLeaveBlock.setLeaveAmount(leavePayout.getPayoutAmount().negate());
                    aLeaveBlock.setAccrualGenerated(true);
                    aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
                    aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
                    aLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.REQUESTED);
                    aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
                    aLeaveBlock.setBlockId(0L);

                    //Want to store the newly created leave block id on this maintainable object.
                    //when the status of the maintenance document encapsulating this maintainable changes
                    //the id will be used to fetch and update the leave block statuses.
                    lb = LmServiceLocator.getLeaveBlockService().saveLeaveBlock(LeaveBlockBo.to(aLeaveBlock),
                            GlobalVariables.getUserSession().getPrincipalId());

                    leavePayout.setPayoutFromLeaveBlockId(lb.getLmLeaveBlockId());
                }
            }

            BigDecimal forfeitedAmount = leavePayout.getForfeitedAmount();
            if (ObjectUtils.isNotNull(forfeitedAmount)) {
                //Any amount forfeited must come out of the originating accrual category in order to bring balance back to max.
                if (forfeitedAmount.compareTo(BigDecimal.ZERO) > 0) {
                    //for balance transfers with action = lose, transfer amount must be moved to forfeitedAmount
                    aLeaveBlock = new LeaveBlockBo();
                    aLeaveBlock.setPrincipalId(leavePayout.getPrincipalId());
                    aLeaveBlock.setLeaveDate(leavePayout.getEffectiveDate());
                    aLeaveBlock.setEarnCode(leavePayout.getFromAccrualCategoryObj().getEarnCode());
                    aLeaveBlock.setAccrualCategory(leavePayout.getFromAccrualCategory());
                    aLeaveBlock.setDescription(LMConstants.PAYOUT_FORFEIT_LB_DESCRIPTION);
                    aLeaveBlock.setLeaveAmount(forfeitedAmount.negate());
                    aLeaveBlock.setAccrualGenerated(true);
                    aLeaveBlock.setTransactionDocId(leavePayout.getDocumentHeaderId());
                    aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.LEAVE_PAYOUT);
                    aLeaveBlock.setRequestStatus(HrConstants.REQUEST_STATUS.REQUESTED);
                    aLeaveBlock.setDocumentId(leavePayout.getLeaveCalendarDocumentId());
                    aLeaveBlock.setBlockId(0L);

                    //Want to store the newly created leave block id on this maintainable object
                    //when the status of the maintenance document encapsulating this maintainable changes
                    //the id will be used to fetch and update the leave block statuses.
                    LeaveBlock lb = LmServiceLocator.getLeaveBlockService().saveLeaveBlock(
                            LeaveBlockBo.to(aLeaveBlock), GlobalVariables.getUserSession().getPrincipalId());

                    leavePayout.setForfeitedLeaveBlockId(lb.getLmLeaveBlockId());
                }
            }
            return leavePayout;
        }
    }

    @Override
    public void submitToWorkflow(LeavePayout leavePayout) throws WorkflowException {

        //leavePayout.setStatus(HrConstants.ROUTE_STATUS.ENROUTE);

        /*MaintenanceDocument document = KRADServiceLocatorWeb.getMaintenanceDocumentService().setupNewMaintenanceDocument(LeavePayout.class.getName(),
        "LeavePayoutDocumentType",KRADConstants.MAINTENANCE_NEW_ACTION);*/
        MaintenanceDocument document = (MaintenanceDocument) KRADServiceLocatorWeb.getDocumentService()
                .getNewDocument("LeavePayoutDocumentType");
        document.getDocumentHeader().setDocumentDescription(
                TKUtils.getDocumentDescription(leavePayout.getPrincipalId(), leavePayout.getEffectiveLocalDate()));
        Map<String, String[]> params = new HashMap<String, String[]>();

        KRADServiceLocatorWeb.getMaintenanceDocumentService().setupMaintenanceObject(document,
                KRADConstants.MAINTENANCE_NEW_ACTION, params);

        LeavePayout lpObj = (LeavePayout) document.getNewMaintainableObject().getDataObject();

        lpObj.setAccrualCategoryRule(leavePayout.getAccrualCategoryRule());
        lpObj.setEffectiveDate(leavePayout.getEffectiveDate());
        lpObj.setLeaveCalendarDocumentId(leavePayout.getLeaveCalendarDocumentId());
        lpObj.setForfeitedAmount(leavePayout.getForfeitedAmount());
        lpObj.setFromAccrualCategory(leavePayout.getFromAccrualCategory());
        lpObj.setPrincipalId(leavePayout.getPrincipalId());
        lpObj.setEarnCode(leavePayout.getEarnCode());
        lpObj.setPayoutAmount(leavePayout.getPayoutAmount());
        lpObj.setDocumentHeaderId(document.getDocumentHeader().getWorkflowDocument().getDocumentId());

        //document.getNewMaintainableObject().setDataObject(lpObj);
        KRADServiceLocatorWeb.getDocumentService().saveDocument(document);
        document.getDocumentHeader().getWorkflowDocument().saveDocument("");

        document.getDocumentHeader().getWorkflowDocument().route("");
    }

    @Override
    public List<LeavePayout> getLeavePayouts(String viewPrincipal, LocalDate beginPeriodDate,
            LocalDate endPeriodDate) {
        return leavePayoutDao.getLeavePayouts(viewPrincipal, beginPeriodDate, endPeriodDate);
    }

    @Override
    public void saveOrUpdate(LeavePayout payout) {
        KNSServiceLocator.getBusinessObjectService().save(payout);
    }

    @Override
    public List<LeavePayout> getLeavePayouts(String principalId, String fromAccrualCategory, String payoutAmount,
            String earnCode, String forfeitedAmount, LocalDate fromEffdt, LocalDate toEffdt) {
        return leavePayoutDao.getLeavePayouts(principalId, fromAccrualCategory, payoutAmount, earnCode,
                forfeitedAmount, fromEffdt, toEffdt);
    }
}