001 /* 002 * Copyright 2011 The Kuali Foundation. 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.kfs.module.ld.util; 017 018 import java.util.ArrayList; 019 import java.util.Collection; 020 import java.util.HashMap; 021 import java.util.HashSet; 022 import java.util.List; 023 import java.util.Map; 024 import java.util.Set; 025 026 import org.apache.commons.lang.StringUtils; 027 import org.kuali.kfs.module.ld.LaborConstants; 028 import org.kuali.kfs.module.ld.businessobject.ExpenseTransferAccountingLine; 029 import org.kuali.kfs.module.ld.businessobject.ExpenseTransferSourceAccountingLine; 030 import org.kuali.kfs.module.ld.businessobject.ExpenseTransferTargetAccountingLine; 031 import org.kuali.kfs.module.ld.businessobject.LaborLedgerPendingEntry; 032 import org.kuali.kfs.module.ld.businessobject.PositionObjectBenefit; 033 import org.kuali.kfs.module.ld.document.LaborLedgerPostingDocument; 034 import org.kuali.kfs.module.ld.document.service.LaborPendingEntryConverterService; 035 import org.kuali.kfs.module.ld.service.LaborBenefitsCalculationService; 036 import org.kuali.kfs.module.ld.service.LaborPositionObjectBenefitService; 037 import org.kuali.kfs.sys.KFSPropertyConstants; 038 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper; 039 import org.kuali.kfs.sys.context.SpringContext; 040 import org.kuali.rice.kns.util.KualiDecimal; 041 import org.kuali.rice.kns.util.ObjectUtils; 042 043 /** 044 * This class is used to help generating pending entries for the given labor documents 045 */ 046 public class LaborPendingEntryGenerator { 047 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(LaborPendingEntryGenerator.class); 048 049 /** 050 * generate the expense pending entries based on the given document and accouting line 051 * 052 * @param document the given accounting document 053 * @param accountingLine the given accounting line 054 * @param sequenceHelper the given squence helper 055 * @return a set of expense pending entries 056 */ 057 public static List<LaborLedgerPendingEntry> generateExpensePendingEntries(LaborLedgerPostingDocument document, ExpenseTransferAccountingLine accountingLine, GeneralLedgerPendingEntrySequenceHelper sequenceHelper) { 058 List<LaborLedgerPendingEntry> expensePendingEntries = new ArrayList<LaborLedgerPendingEntry>(); 059 LaborLedgerPendingEntry expensePendingEntry = SpringContext.getBean(LaborPendingEntryConverterService.class).getExpensePendingEntry(document, accountingLine, sequenceHelper); 060 expensePendingEntries.add(expensePendingEntry); 061 062 // if the AL's pay FY and period do not match the University fiscal year and period need to create a reversal entry for 063 // current period 064 if (!isAccountingLinePayFYPeriodMatchesUniversityPayFYPeriod(document, accountingLine)) { 065 LaborLedgerPendingEntry expenseA21PendingEntry = SpringContext.getBean(LaborPendingEntryConverterService.class).getExpenseA21PendingEntry(document, accountingLine, sequenceHelper); 066 expensePendingEntries.add(expenseA21PendingEntry); 067 068 LaborLedgerPendingEntry expenseA21ReversalPendingEntry = SpringContext.getBean(LaborPendingEntryConverterService.class).getExpenseA21ReversalPendingEntry(document, accountingLine, sequenceHelper); 069 expensePendingEntries.add(expenseA21ReversalPendingEntry); 070 } 071 072 return expensePendingEntries; 073 } 074 075 /** 076 * generate the benefit pending entries based on the given document and accounting line 077 * 078 * @param document the given accounting document 079 * @param accountingLine the given accounting line 080 * @param sequenceHelper the given squence helper 081 * @return a set of benefit pending entries 082 */ 083 public static List<LaborLedgerPendingEntry> generateBenefitPendingEntries(LaborLedgerPostingDocument document, ExpenseTransferAccountingLine accountingLine, GeneralLedgerPendingEntrySequenceHelper sequenceHelper) { 084 accountingLine.refreshReferenceObject(KFSPropertyConstants.LABOR_OBJECT); 085 if (ObjectUtils.isNull(accountingLine.getLaborObject())) { 086 return null; 087 } 088 089 String FringeOrSalaryCode = accountingLine.getLaborObject().getFinancialObjectFringeOrSalaryCode(); 090 if (!LaborConstants.SalaryExpenseTransfer.LABOR_LEDGER_SALARY_CODE.equals(FringeOrSalaryCode)) { 091 return null; 092 } 093 094 Integer payrollFiscalyear = accountingLine.getPayrollEndDateFiscalYear(); 095 String chartOfAccountsCode = accountingLine.getChartOfAccountsCode(); 096 String objectCode = accountingLine.getFinancialObjectCode(); 097 Collection<PositionObjectBenefit> positionObjectBenefits = SpringContext.getBean(LaborPositionObjectBenefitService.class).getPositionObjectBenefits(payrollFiscalyear, chartOfAccountsCode, objectCode); 098 099 List<LaborLedgerPendingEntry> benefitPendingEntries = new ArrayList<LaborLedgerPendingEntry>(); 100 for (PositionObjectBenefit positionObjectBenefit : positionObjectBenefits) { 101 String fringeBenefitObjectCode = positionObjectBenefit.getBenefitsCalculation().getPositionFringeBenefitObjectCode(); 102 103 KualiDecimal benefitAmount = SpringContext.getBean(LaborBenefitsCalculationService.class).calculateFringeBenefit(positionObjectBenefit, accountingLine.getAmount()); 104 if (benefitAmount.isNonZero()) { 105 List<LaborLedgerPendingEntry> pendingEntries = generateBenefitPendingEntries(document, accountingLine, sequenceHelper, benefitAmount, fringeBenefitObjectCode); 106 benefitPendingEntries.addAll(pendingEntries); 107 } 108 } 109 110 return benefitPendingEntries; 111 } 112 113 /** 114 * generate the benefit pending entries with the given benefit amount and finge benefit object code based on the given document 115 * and accouting line 116 * 117 * @param document the given accounting document 118 * @param accountingLine the given accounting line 119 * @param sequenceHelper the given squence helper 120 * @param benefitAmount the given benefit amount 121 * @param fringeBenefitObjectCode the given finge benefit object code 122 * @return a set of benefit pending entries with the given benefit amount and finge benefit object code 123 */ 124 public static List<LaborLedgerPendingEntry> generateBenefitPendingEntries(LaborLedgerPostingDocument document, ExpenseTransferAccountingLine accountingLine, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, KualiDecimal benefitAmount, String fringeBenefitObjectCode) { 125 List<LaborLedgerPendingEntry> benefitPendingEntries = new ArrayList<LaborLedgerPendingEntry>(); 126 LaborLedgerPendingEntry benefitPendingEntry = SpringContext.getBean(LaborPendingEntryConverterService.class).getBenefitPendingEntry(document, accountingLine, sequenceHelper, benefitAmount, fringeBenefitObjectCode); 127 benefitPendingEntries.add(benefitPendingEntry); 128 129 // if the AL's pay FY and period do not match the University fiscal year and period 130 if (!isAccountingLinePayFYPeriodMatchesUniversityPayFYPeriod(document, accountingLine)) { 131 LaborLedgerPendingEntry benefitA21PendingEntry = SpringContext.getBean(LaborPendingEntryConverterService.class).getBenefitA21PendingEntry(document, accountingLine, sequenceHelper, benefitAmount, fringeBenefitObjectCode); 132 benefitPendingEntries.add(benefitA21PendingEntry); 133 134 LaborLedgerPendingEntry benefitA21ReversalPendingEntry = SpringContext.getBean(LaborPendingEntryConverterService.class).getBenefitA21ReversalPendingEntry(document, accountingLine, sequenceHelper, benefitAmount, fringeBenefitObjectCode); 135 benefitPendingEntries.add(benefitA21ReversalPendingEntry); 136 } 137 138 return benefitPendingEntries; 139 } 140 141 /** 142 * generate the benefit clearing pending entries with the given benefit amount and fringe benefit object code based on the given 143 * document and accouting line 144 * 145 * @param document the given accounting document 146 * @param sequenceHelper the given squence helper 147 * @param accountNumber the given clearing account number 148 * @param chartOfAccountsCode the given clearing chart of accounts code 149 * @return a set of benefit clearing pending entries 150 */ 151 public static List<LaborLedgerPendingEntry> generateBenefitClearingPendingEntries(LaborLedgerPostingDocument document, GeneralLedgerPendingEntrySequenceHelper sequenceHelper, String accountNumber, String chartOfAccountsCode) { 152 List<LaborLedgerPendingEntry> benefitClearingPendingEntries = new ArrayList<LaborLedgerPendingEntry>(); 153 154 Map<String, KualiDecimal> sourceLineBenefitAmountSum = new HashMap<String, KualiDecimal>(); 155 List<ExpenseTransferSourceAccountingLine> sourceAccountingLines = document.getSourceAccountingLines(); 156 for (ExpenseTransferSourceAccountingLine accountingLine : sourceAccountingLines) { 157 updateBenefitAmountSum(sourceLineBenefitAmountSum, accountingLine); 158 } 159 160 Map<String, KualiDecimal> targetLineBenefitAmountSum = new HashMap<String, KualiDecimal>(); 161 List<ExpenseTransferTargetAccountingLine> targetAccountingLines = document.getTargetAccountingLines(); 162 for (ExpenseTransferTargetAccountingLine accountingLine : targetAccountingLines) { 163 updateBenefitAmountSum(targetLineBenefitAmountSum, accountingLine); 164 } 165 166 Set<String> benefitTypeCodes = new HashSet<String>(); 167 for (String key : targetLineBenefitAmountSum.keySet()) { 168 benefitTypeCodes.add(key); 169 } 170 171 for (String key : sourceLineBenefitAmountSum.keySet()) { 172 benefitTypeCodes.add(key); 173 } 174 175 for (String benefitTypeCode : benefitTypeCodes) { 176 KualiDecimal targetAmount = KualiDecimal.ZERO; 177 if (targetLineBenefitAmountSum.containsKey(benefitTypeCode)) { 178 targetAmount = targetLineBenefitAmountSum.get(benefitTypeCode); 179 } 180 181 KualiDecimal sourceAmount = KualiDecimal.ZERO; 182 if (sourceLineBenefitAmountSum.containsKey(benefitTypeCode)) { 183 sourceAmount = sourceLineBenefitAmountSum.get(benefitTypeCode); 184 } 185 186 KualiDecimal clearingAmount = sourceAmount.subtract(targetAmount); 187 if (clearingAmount.isNonZero()) { 188 benefitClearingPendingEntries.add(SpringContext.getBean(LaborPendingEntryConverterService.class).getBenefitClearingPendingEntry(document, sequenceHelper, accountNumber, chartOfAccountsCode, benefitTypeCode, clearingAmount)); 189 } 190 } 191 192 return benefitClearingPendingEntries; 193 } 194 195 /** 196 * update the benefit amount summary map based on the given accounting line 197 * 198 * @param benefitAmountSumByBenefitType the given benefit amount summary map 199 * @param accountingLine the given accounting line 200 */ 201 protected static void updateBenefitAmountSum(Map<String, KualiDecimal> benefitAmountSumByBenefitType, ExpenseTransferAccountingLine accountingLine) { 202 accountingLine.refreshReferenceObject(KFSPropertyConstants.LABOR_OBJECT); 203 if (ObjectUtils.isNull(accountingLine.getLaborObject())) { 204 return; 205 } 206 207 String FringeOrSalaryCode = accountingLine.getLaborObject().getFinancialObjectFringeOrSalaryCode(); 208 if (!LaborConstants.SalaryExpenseTransfer.LABOR_LEDGER_SALARY_CODE.equals(FringeOrSalaryCode)) { 209 return; 210 } 211 212 Integer payrollFiscalyear = accountingLine.getPayrollEndDateFiscalYear(); 213 String chartOfAccountsCode = accountingLine.getChartOfAccountsCode(); 214 String objectCode = accountingLine.getFinancialObjectCode(); 215 216 Collection<PositionObjectBenefit> positionObjectBenefits = SpringContext.getBean(LaborPositionObjectBenefitService.class).getPositionObjectBenefits(payrollFiscalyear, chartOfAccountsCode, objectCode); 217 for (PositionObjectBenefit positionObjectBenefit : positionObjectBenefits) { 218 String benefitTypeCode = positionObjectBenefit.getBenefitsCalculation().getPositionBenefitTypeCode(); 219 220 KualiDecimal benefitAmount = SpringContext.getBean(LaborBenefitsCalculationService.class).calculateFringeBenefit(positionObjectBenefit, accountingLine.getAmount()); 221 if (benefitAmountSumByBenefitType.containsKey(benefitTypeCode)) { 222 benefitAmount = benefitAmount.add(benefitAmountSumByBenefitType.get(benefitTypeCode)); 223 } 224 benefitAmountSumByBenefitType.put(benefitTypeCode, benefitAmount); 225 } 226 } 227 228 /** 229 * determine if the pay fiscal year and period from the accounting line match with its university fiscal year and period. 230 * 231 * @param document the given document 232 * @param accountingLine the given accounting line of the document 233 * @return true if the pay fiscal year and period from the accounting line match with its university fiscal year and period; 234 * otherwise, false 235 */ 236 protected static boolean isAccountingLinePayFYPeriodMatchesUniversityPayFYPeriod(LaborLedgerPostingDocument document, ExpenseTransferAccountingLine accountingLine) { 237 Integer fiscalYear = document.getPostingYear(); 238 Integer payFiscalYear = accountingLine.getPayrollEndDateFiscalYear(); 239 if (!fiscalYear.equals(payFiscalYear)) { 240 return false; 241 } 242 243 String periodCode = document.getPostingPeriodCode(); 244 String payPeriodCode = accountingLine.getPayrollEndDateFiscalPeriodCode(); 245 if (!StringUtils.equals(periodCode, payPeriodCode)) { 246 return false; 247 } 248 249 return true; 250 } 251 }