001: /*
002: * Copyright 2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.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/ecl1.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.module.labor.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.core.util.GeneralLedgerPendingEntrySequenceHelper;
028: import org.kuali.core.util.KualiDecimal;
029: import org.kuali.core.util.ObjectUtils;
030: import org.kuali.kfs.context.SpringContext;
031: import org.kuali.module.labor.LaborConstants;
032: import org.kuali.module.labor.LaborPropertyConstants;
033: import org.kuali.module.labor.bo.BenefitsCalculation;
034: import org.kuali.module.labor.bo.ExpenseTransferAccountingLine;
035: import org.kuali.module.labor.bo.ExpenseTransferSourceAccountingLine;
036: import org.kuali.module.labor.bo.ExpenseTransferTargetAccountingLine;
037: import org.kuali.module.labor.bo.LaborLedgerPendingEntry;
038: import org.kuali.module.labor.bo.PositionObjectBenefit;
039: import org.kuali.module.labor.document.LaborLedgerPostingDocument;
040: import org.kuali.module.labor.service.LaborPositionObjectBenefitService;
041:
042: /**
043: * This class is used to help generating pending entries for the given labor documents
044: */
045: public class LaborPendingEntryGenerator {
046: private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger
047: .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(
058: LaborLedgerPostingDocument document,
059: ExpenseTransferAccountingLine accountingLine,
060: GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
061: List<LaborLedgerPendingEntry> expensePendingEntries = new ArrayList<LaborLedgerPendingEntry>();
062: LaborLedgerPendingEntry expensePendingEntry = LaborPendingEntryConverter
063: .getExpensePendingEntry(document, accountingLine,
064: sequenceHelper);
065: expensePendingEntries.add(expensePendingEntry);
066:
067: // if the AL's pay FY and period do not match the University fiscal year and period need to create a reversal entry for
068: // current period
069: if (!isAccountingLinePayFYPeriodMatchesUniversityPayFYPeriod(
070: document, accountingLine)) {
071: LaborLedgerPendingEntry expenseA21PendingEntry = LaborPendingEntryConverter
072: .getExpenseA21PendingEntry(document,
073: accountingLine, sequenceHelper);
074: expensePendingEntries.add(expenseA21PendingEntry);
075:
076: LaborLedgerPendingEntry expenseA21ReversalPendingEntry = LaborPendingEntryConverter
077: .getExpenseA21ReversalPendingEntry(document,
078: accountingLine, sequenceHelper);
079: expensePendingEntries.add(expenseA21ReversalPendingEntry);
080: }
081:
082: return expensePendingEntries;
083: }
084:
085: /**
086: * generate the benefit pending entries based on the given document and accounting line
087: *
088: * @param document the given accounting document
089: * @param accountingLine the given accounting line
090: * @param sequenceHelper the given squence helper
091: * @return a set of benefit pending entries
092: */
093: public static List<LaborLedgerPendingEntry> generateBenefitPendingEntries(
094: LaborLedgerPostingDocument document,
095: ExpenseTransferAccountingLine accountingLine,
096: GeneralLedgerPendingEntrySequenceHelper sequenceHelper) {
097: accountingLine
098: .refreshReferenceObject(LaborPropertyConstants.LABOR_OBJECT);
099: if (ObjectUtils.isNull(accountingLine.getLaborObject())) {
100: return null;
101: }
102:
103: String FringeOrSalaryCode = accountingLine.getLaborObject()
104: .getFinancialObjectFringeOrSalaryCode();
105: if (!LaborConstants.SalaryExpenseTransfer.LABOR_LEDGER_SALARY_CODE
106: .equals(FringeOrSalaryCode)) {
107: return null;
108: }
109:
110: Integer payrollFiscalyear = accountingLine
111: .getPayrollEndDateFiscalYear();
112: String chartOfAccountsCode = accountingLine
113: .getChartOfAccountsCode();
114: String objectCode = accountingLine.getFinancialObjectCode();
115: Collection<PositionObjectBenefit> positionObjectBenefits = SpringContext
116: .getBean(LaborPositionObjectBenefitService.class)
117: .getPositionObjectBenefits(payrollFiscalyear,
118: chartOfAccountsCode, objectCode);
119:
120: List<LaborLedgerPendingEntry> benefitPendingEntries = new ArrayList<LaborLedgerPendingEntry>();
121: for (PositionObjectBenefit positionObjectBenefit : positionObjectBenefits) {
122: BenefitsCalculation benefitsCalculation = positionObjectBenefit
123: .getBenefitsCalculation();
124: String fringeBenefitObjectCode = benefitsCalculation
125: .getPositionFringeBenefitObjectCode();
126: String benefitTypeCode = benefitsCalculation
127: .getPositionBenefitTypeCode();
128:
129: // calculate the benefit amount (ledger amt * (benfit pct/100) )
130: KualiDecimal fringeBenefitPercent = benefitsCalculation
131: .getPositionFringeBenefitPercent();
132: KualiDecimal benefitAmount = fringeBenefitPercent.multiply(
133: accountingLine.getAmount()).divide(
134: new KualiDecimal(100));
135:
136: if (benefitAmount.isNonZero()) {
137: List<LaborLedgerPendingEntry> pendingEntries = generateBenefitPendingEntries(
138: document, accountingLine, sequenceHelper,
139: benefitAmount, fringeBenefitObjectCode);
140: benefitPendingEntries.addAll(pendingEntries);
141: }
142: }
143:
144: return benefitPendingEntries;
145: }
146:
147: /**
148: * generate the benefit pending entries with the given benefit amount and finge benefit object code based on the given document
149: * and accouting line
150: *
151: * @param document the given accounting document
152: * @param accountingLine the given accounting line
153: * @param sequenceHelper the given squence helper
154: * @param benefitAmount the given benefit amount
155: * @param fringeBenefitObjectCode the given finge benefit object code
156: * @return a set of benefit pending entries with the given benefit amount and finge benefit object code
157: */
158: public static List<LaborLedgerPendingEntry> generateBenefitPendingEntries(
159: LaborLedgerPostingDocument document,
160: ExpenseTransferAccountingLine accountingLine,
161: GeneralLedgerPendingEntrySequenceHelper sequenceHelper,
162: KualiDecimal benefitAmount, String fringeBenefitObjectCode) {
163: List<LaborLedgerPendingEntry> benefitPendingEntries = new ArrayList<LaborLedgerPendingEntry>();
164: LaborLedgerPendingEntry benefitPendingEntry = LaborPendingEntryConverter
165: .getBenefitPendingEntry(document, accountingLine,
166: sequenceHelper, benefitAmount,
167: fringeBenefitObjectCode);
168: benefitPendingEntries.add(benefitPendingEntry);
169:
170: // if the AL's pay FY and period do not match the University fiscal year and period
171: if (!isAccountingLinePayFYPeriodMatchesUniversityPayFYPeriod(
172: document, accountingLine)) {
173: LaborLedgerPendingEntry benefitA21PendingEntry = LaborPendingEntryConverter
174: .getBenefitA21PendingEntry(document,
175: accountingLine, sequenceHelper,
176: benefitAmount, fringeBenefitObjectCode);
177: benefitPendingEntries.add(benefitA21PendingEntry);
178:
179: LaborLedgerPendingEntry benefitA21ReversalPendingEntry = LaborPendingEntryConverter
180: .getBenefitA21ReversalPendingEntry(document,
181: accountingLine, sequenceHelper,
182: benefitAmount, fringeBenefitObjectCode);
183: benefitPendingEntries.add(benefitA21ReversalPendingEntry);
184: }
185:
186: return benefitPendingEntries;
187: }
188:
189: /**
190: * generate the benefit clearing pending entries with the given benefit amount and fringe benefit object code based on the given
191: * document and accouting line
192: *
193: * @param document the given accounting document
194: * @param sequenceHelper the given squence helper
195: * @param accountNumber the given clearing account number
196: * @param chartOfAccountsCode the given clearing chart of accounts code
197: * @return a set of benefit clearing pending entries
198: */
199: public static List<LaborLedgerPendingEntry> generateBenefitClearingPendingEntries(
200: LaborLedgerPostingDocument document,
201: GeneralLedgerPendingEntrySequenceHelper sequenceHelper,
202: String accountNumber, String chartOfAccountsCode) {
203: List<LaborLedgerPendingEntry> benefitClearingPendingEntries = new ArrayList<LaborLedgerPendingEntry>();
204:
205: Map<String, KualiDecimal> sourceLineBenefitAmountSum = new HashMap<String, KualiDecimal>();
206: List<ExpenseTransferSourceAccountingLine> sourceAccountingLines = document
207: .getSourceAccountingLines();
208: for (ExpenseTransferSourceAccountingLine accountingLine : sourceAccountingLines) {
209: updateBenefitAmountSum(sourceLineBenefitAmountSum,
210: accountingLine);
211: }
212:
213: Map<String, KualiDecimal> targetLineBenefitAmountSum = new HashMap<String, KualiDecimal>();
214: List<ExpenseTransferTargetAccountingLine> targetAccountingLines = document
215: .getTargetAccountingLines();
216: for (ExpenseTransferTargetAccountingLine accountingLine : targetAccountingLines) {
217: updateBenefitAmountSum(targetLineBenefitAmountSum,
218: accountingLine);
219: }
220:
221: Set<String> benefitTypeCodes = new HashSet<String>();
222: for (String key : targetLineBenefitAmountSum.keySet()) {
223: benefitTypeCodes.add(key);
224: }
225:
226: for (String key : sourceLineBenefitAmountSum.keySet()) {
227: benefitTypeCodes.add(key);
228: }
229:
230: for (String benefitTypeCode : benefitTypeCodes) {
231: KualiDecimal targetAmount = KualiDecimal.ZERO;
232: if (targetLineBenefitAmountSum.containsKey(benefitTypeCode)) {
233: targetAmount = targetLineBenefitAmountSum
234: .get(benefitTypeCode);
235: }
236:
237: KualiDecimal sourceAmount = KualiDecimal.ZERO;
238: if (sourceLineBenefitAmountSum.containsKey(benefitTypeCode)) {
239: sourceAmount = sourceLineBenefitAmountSum
240: .get(benefitTypeCode);
241: }
242:
243: KualiDecimal clearingAmount = sourceAmount
244: .subtract(targetAmount);
245: if (clearingAmount.isNonZero()) {
246: benefitClearingPendingEntries
247: .add(LaborPendingEntryConverter
248: .getBenefitClearingPendingEntry(
249: document, sequenceHelper,
250: accountNumber,
251: chartOfAccountsCode,
252: benefitTypeCode, clearingAmount));
253: }
254: }
255:
256: return benefitClearingPendingEntries;
257: }
258:
259: /**
260: * update the benefit amount summary map based on the given accounting line
261: *
262: * @param benefitAmountSumByBenefitType the given benefit amount summary map
263: * @param accountingLine the given accounting line
264: */
265: private static void updateBenefitAmountSum(
266: Map<String, KualiDecimal> benefitAmountSumByBenefitType,
267: ExpenseTransferAccountingLine accountingLine) {
268: accountingLine
269: .refreshReferenceObject(LaborPropertyConstants.LABOR_OBJECT);
270: if (ObjectUtils.isNull(accountingLine.getLaborObject())) {
271: return;
272: }
273:
274: String FringeOrSalaryCode = accountingLine.getLaborObject()
275: .getFinancialObjectFringeOrSalaryCode();
276: if (!LaborConstants.SalaryExpenseTransfer.LABOR_LEDGER_SALARY_CODE
277: .equals(FringeOrSalaryCode)) {
278: return;
279: }
280:
281: Integer payrollFiscalyear = accountingLine
282: .getPayrollEndDateFiscalYear();
283: String chartOfAccountsCode = accountingLine
284: .getChartOfAccountsCode();
285: String objectCode = accountingLine.getFinancialObjectCode();
286: Collection<PositionObjectBenefit> positionObjectBenefits = SpringContext
287: .getBean(LaborPositionObjectBenefitService.class)
288: .getPositionObjectBenefits(payrollFiscalyear,
289: chartOfAccountsCode, objectCode);
290:
291: for (PositionObjectBenefit positionObjectBenefit : positionObjectBenefits) {
292: BenefitsCalculation benefitsCalculation = positionObjectBenefit
293: .getBenefitsCalculation();
294: String fringeBenefitObjectCode = benefitsCalculation
295: .getPositionFringeBenefitObjectCode();
296: String benefitTypeCode = benefitsCalculation
297: .getPositionBenefitTypeCode();
298:
299: KualiDecimal fringeBenefitPercent = benefitsCalculation
300: .getPositionFringeBenefitPercent();
301: KualiDecimal benefitAmount = fringeBenefitPercent.multiply(
302: accountingLine.getAmount()).divide(
303: new KualiDecimal(100));
304: // KualiDecimal numericBenefitAmount = DebitCreditUtil.getNumericAmount(benefitAmount,
305: // accountingLine.getDebitCreditCode());
306:
307: if (benefitAmountSumByBenefitType
308: .containsKey(benefitTypeCode)) {
309: benefitAmount = benefitAmount
310: .add(benefitAmountSumByBenefitType
311: .get(benefitTypeCode));
312: }
313: benefitAmountSumByBenefitType.put(benefitTypeCode,
314: benefitAmount);
315: }
316: }
317:
318: /**
319: * determine if the pay fiscal year and period from the accounting line match with its university fiscal year and period.
320: *
321: * @param document the given document
322: * @param accountingLine the given accounting line of the document
323: * @return true if the pay fiscal year and period from the accounting line match with its university fiscal year and period;
324: * otherwise, false
325: */
326: private static boolean isAccountingLinePayFYPeriodMatchesUniversityPayFYPeriod(
327: LaborLedgerPostingDocument document,
328: ExpenseTransferAccountingLine accountingLine) {
329: Integer fiscalYear = document.getPostingYear();
330: Integer payFiscalYear = accountingLine
331: .getPayrollEndDateFiscalYear();
332: if (!payFiscalYear.equals(fiscalYear)) {
333: return false;
334: }
335:
336: String periodCode = document.getPostingPeriodCode();
337: String payPeriodCode = accountingLine
338: .getPayrollEndDateFiscalPeriodCode();
339: if (StringUtils.equals(periodCode, payPeriodCode)) {
340: return false;
341: }
342:
343: return true;
344: }
345: }
|