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 }