1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package org.kuali.ole.gl.batch.service.impl;
17  
18  import java.util.ArrayList;
19  import java.util.Date;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.kuali.ole.coa.businessobject.A21SubAccount;
25  import org.kuali.ole.coa.businessobject.Account;
26  import org.kuali.ole.coa.businessobject.IndirectCostRecoveryExclusionAccount;
27  import org.kuali.ole.coa.businessobject.IndirectCostRecoveryExclusionType;
28  import org.kuali.ole.coa.businessobject.ObjectCode;
29  import org.kuali.ole.coa.dataaccess.IndirectCostRecoveryExclusionAccountDao;
30  import org.kuali.ole.coa.dataaccess.IndirectCostRecoveryExclusionTypeDao;
31  import org.kuali.ole.gl.GeneralLedgerConstants;
32  import org.kuali.ole.gl.batch.PosterIndirectCostRecoveryEntriesStep;
33  import org.kuali.ole.gl.batch.service.AccountingCycleCachingService;
34  import org.kuali.ole.gl.batch.service.IndirectCostRecoveryService;
35  import org.kuali.ole.gl.batch.service.PostTransaction;
36  import org.kuali.ole.gl.businessobject.ExpenditureTransaction;
37  import org.kuali.ole.gl.businessobject.Transaction;
38  import org.kuali.ole.sys.Message;
39  import org.kuali.ole.sys.OLEConstants;
40  import org.kuali.ole.sys.OLEPropertyConstants;
41  import org.kuali.ole.sys.context.SpringContext;
42  import org.kuali.ole.sys.service.ReportWriterService;
43  import org.kuali.rice.core.api.parameter.ParameterEvaluatorService;
44  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
45  import org.kuali.rice.krad.service.BusinessObjectService;
46  import org.kuali.rice.krad.service.PersistenceStructureService;
47  import org.kuali.rice.krad.util.ObjectUtils;
48  import org.springframework.transaction.annotation.Transactional;
49  import org.springframework.util.StringUtils;
50  
51  
52  
53  
54  
55  @Transactional
56  public class PostExpenditureTransaction implements IndirectCostRecoveryService, PostTransaction {
57      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PostExpenditureTransaction.class);
58  
59      private static final String INDIRECT_COST_TYPES_PARAMETER = "INDIRECT_COST_TYPES";
60      private static final String INDIRECT_COST_FISCAL_PERIODS_PARAMETER = "INDIRECT_COST_FISCAL_PERIODS";
61      private static final String ICR_EXCLUSIONS_AT_TRANSACTION_AND_TOP_LEVEL_ONLY_PARAMETER_NAME = "ICR_EXCLUSIONS_AT_TRANSACTION_AND_TOP_LEVEL_ONLY_IND";
62  
63      private IndirectCostRecoveryExclusionAccountDao indirectCostRecoveryExclusionAccountDao;
64      private IndirectCostRecoveryExclusionTypeDao indirectCostRecoveryExclusionTypeDao;
65      private AccountingCycleCachingService accountingCycleCachingService;
66      private PersistenceStructureService persistenceStructureService;
67      private ParameterService parameterService;
68  
69      public void setIndirectCostRecoveryExclusionAccountDao(IndirectCostRecoveryExclusionAccountDao icrea) {
70          indirectCostRecoveryExclusionAccountDao = icrea;
71      }
72  
73      public void setIndirectCostRecoveryExclusionTypeDao(IndirectCostRecoveryExclusionTypeDao icrea) {
74          indirectCostRecoveryExclusionTypeDao = icrea;
75      }
76  
77      
78  
79  
80      public PostExpenditureTransaction() {
81          super();
82      }
83  
84      
85  
86  
87  
88  
89  
90  
91  
92  
93      @Override
94      public boolean isIcrTransaction(Transaction transaction, ReportWriterService reportWriterService) {
95          if (LOG.isDebugEnabled()) {
96              LOG.debug("isIcrTransaction() started");
97          }
98  
99          
100         
101         if (transaction.getObjectType().isFinObjectTypeIcrSelectionIndicator() && SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(PosterIndirectCostRecoveryEntriesStep.class, PostExpenditureTransaction.INDIRECT_COST_FISCAL_PERIODS_PARAMETER, transaction.getUniversityFiscalPeriodCode()).evaluationSucceeds()) {
102             
103 
104             
105             A21SubAccount a21SubAccount = accountingCycleCachingService.getA21SubAccount(transaction.getAccount().getChartOfAccountsCode(), transaction.getAccount().getAccountNumber(), transaction.getSubAccountNumber());
106             String financialIcrSeriesIdentifier;
107             String indirectCostRecoveryTypeCode;
108 
109             
110             if (a21SubAccount != null) {
111                 if (StringUtils.hasText(a21SubAccount.getFinancialIcrSeriesIdentifier()) && StringUtils.hasText(a21SubAccount.getIndirectCostRecoveryTypeCode())) {
112                     
113                     if (!StringUtils.hasText(transaction.getAccount().getFinancialIcrSeriesIdentifier()) || !StringUtils.hasText(transaction.getAccount().getAcctIndirectCostRcvyTypeCd())) {
114                         List<Message> warnings = new ArrayList<Message>();
115                         warnings.add(new Message("Warning - excluding transaction from Indirect Cost Recovery because Sub-Account is set up for ICR, but Account is not.", Message.TYPE_WARNING));
116                         reportWriterService.writeError(transaction, warnings);
117                     }
118                 }
119 
120                 if (StringUtils.hasText(a21SubAccount.getFinancialIcrSeriesIdentifier()) && StringUtils.hasText(a21SubAccount.getIndirectCostRecoveryTypeCode())) {
121                     
122                     financialIcrSeriesIdentifier = a21SubAccount.getFinancialIcrSeriesIdentifier();
123                     indirectCostRecoveryTypeCode = a21SubAccount.getIndirectCostRecoveryTypeCode();
124                 }
125                 else {
126                     
127                     financialIcrSeriesIdentifier = transaction.getAccount().getFinancialIcrSeriesIdentifier();
128                     indirectCostRecoveryTypeCode = transaction.getAccount().getAcctIndirectCostRcvyTypeCd();
129                 }
130             }
131             else {
132                 
133                 financialIcrSeriesIdentifier = transaction.getAccount().getFinancialIcrSeriesIdentifier();
134                 indirectCostRecoveryTypeCode = transaction.getAccount().getAcctIndirectCostRcvyTypeCd();
135             }
136 
137             
138             if (!StringUtils.hasText(financialIcrSeriesIdentifier)) {
139                 LOG.debug("isIcrTransaction() Not ICR Account");
140                 return false;
141             }
142 
143             if ((a21SubAccount != null) && OLEConstants.SubAccountType.COST_SHARE.equals(a21SubAccount.getSubAccountTypeCode())) {
144                 
145                 LOG.debug("isIcrTransaction() A21 subaccounts with type of CS - not posted");
146                 return false;
147             }
148 
149             
150             final boolean selfAndTopLevelOnly = getParameterService().getParameterValueAsBoolean(PosterIndirectCostRecoveryEntriesStep.class, PostExpenditureTransaction.ICR_EXCLUSIONS_AT_TRANSACTION_AND_TOP_LEVEL_ONLY_PARAMETER_NAME);
151             if (excludedByType(indirectCostRecoveryTypeCode, transaction.getFinancialObject(), selfAndTopLevelOnly)) {
152                 return false;
153             }
154             if (excludedByAccount(transaction.getAccount(), transaction.getFinancialObject(), selfAndTopLevelOnly)) {
155                 return false;
156             }
157 
158             return true;  
159         }
160         else {
161             
162             LOG.debug("isIcrTransaction() invalid period code - not posted");
163             return false;
164         }
165     }
166 
167     
168 
169 
170 
171 
172 
173 
174     protected boolean excludedByType(String indirectCostRecoveryTypeCode, ObjectCode objectCode, boolean selfAndTopLevelOnly) {
175         
176         if ((!StringUtils.hasText(indirectCostRecoveryTypeCode)) || !SpringContext.getBean(ParameterEvaluatorService.class).getParameterEvaluator(PosterIndirectCostRecoveryEntriesStep.class, PostExpenditureTransaction.INDIRECT_COST_TYPES_PARAMETER, indirectCostRecoveryTypeCode).evaluationSucceeds()) {
177             
178             if (LOG.isDebugEnabled()) {
179                 LOG.debug("isIcrTransaction() ICR type is null or excluded by the OLE-GL / Poster Indirect Cost Recoveries Step / INDIRECT_COST_TYPES parameter - not posted");
180             }
181             return true;
182         }
183 
184         if (hasExclusionByType(indirectCostRecoveryTypeCode, objectCode)) {
185             return true;
186         }
187 
188         ObjectCode currentObjectCode = getReportsToObjectCode(objectCode);
189         while (currentObjectCode != null && !currentObjectCode.isReportingToSelf()) {
190             if (!selfAndTopLevelOnly && hasExclusionByType(indirectCostRecoveryTypeCode, currentObjectCode)) {
191                 return true;
192             }
193 
194             currentObjectCode = getReportsToObjectCode(currentObjectCode);
195         }
196         if (currentObjectCode != null && hasExclusionByType(indirectCostRecoveryTypeCode, currentObjectCode))
197          {
198             return true; 
199         }
200 
201         return false;
202     }
203 
204     
205 
206 
207 
208 
209 
210     protected boolean hasExclusionByType(String indirectCostRecoveryTypeCode, ObjectCode objectCode) {
211         Map<String, Object> keys = new HashMap<String, Object>();
212         keys.put(OLEPropertyConstants.ACCOUNT_INDIRECT_COST_RECOVERY_TYPE_CODE, indirectCostRecoveryTypeCode);
213         keys.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, objectCode.getChartOfAccountsCode());
214         keys.put(OLEPropertyConstants.FINANCIAL_OBJECT_CODE, objectCode.getFinancialObjectCode());
215         final IndirectCostRecoveryExclusionType excType = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(IndirectCostRecoveryExclusionType.class, keys);
216         return !ObjectUtils.isNull(excType) && excType.isActive();
217     }
218 
219     
220 
221 
222 
223 
224 
225 
226     protected boolean excludedByAccount(Account account, ObjectCode objectCode, boolean selfAndTopLevelOnly) {
227         if (hasExclusionByAccount(account, objectCode)) {
228             return true;
229         }
230 
231         ObjectCode currentObjectCode = getReportsToObjectCode(objectCode);
232         while (currentObjectCode != null && !currentObjectCode.isReportingToSelf()) {
233             if (!selfAndTopLevelOnly && hasExclusionByAccount(account, currentObjectCode)) {
234                 return true;
235             }
236 
237             currentObjectCode = getReportsToObjectCode(currentObjectCode);
238         }
239         if (currentObjectCode != null && hasExclusionByAccount(account, currentObjectCode))
240          {
241             return true; 
242         }
243 
244         return false;
245     }
246 
247     
248 
249 
250 
251 
252 
253     protected boolean hasExclusionByAccount(Account account, ObjectCode objectCode) {
254         Map<String, Object> keys = new HashMap<String, Object>();
255         keys.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, account.getChartOfAccountsCode());
256         keys.put(OLEPropertyConstants.ACCOUNT_NUMBER, account.getAccountNumber());
257         keys.put(OLEPropertyConstants.FINANCIAL_OBJECT_CHART_OF_ACCOUNT_CODE, objectCode.getChartOfAccountsCode());
258         keys.put(OLEPropertyConstants.FINANCIAL_OBJECT_CODE, objectCode.getFinancialObjectCode());
259         final IndirectCostRecoveryExclusionAccount excAccount = SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(IndirectCostRecoveryExclusionAccount.class, keys);
260 
261         return !ObjectUtils.isNull(excAccount);
262     }
263 
264     
265 
266 
267 
268 
269     protected boolean hasValidObjectCodeReportingHierarchy(ObjectCode objectCode) {
270         ObjectCode currentObjectCode = objectCode;
271         while (hasValidReportsToFields(currentObjectCode) && !currentObjectCode.isReportingToSelf()) {
272             currentObjectCode = getReportsToObjectCode(currentObjectCode);
273             if (ObjectUtils.isNull(currentObjectCode) || !currentObjectCode.isActive()) {
274                 return false;
275             }
276         }
277         if (!hasValidReportsToFields(currentObjectCode)) {
278             return false;
279         }
280         return true;
281     }
282 
283     
284 
285 
286 
287 
288     protected boolean hasValidReportsToFields(ObjectCode objectCode) {
289         return !org.apache.commons.lang.StringUtils.isBlank(objectCode.getReportsToChartOfAccountsCode()) && !org.apache.commons.lang.StringUtils.isBlank(objectCode.getReportsToFinancialObjectCode());
290     }
291 
292     
293 
294 
295 
296 
297     protected ObjectCode getReportsToObjectCode(ObjectCode objectCode) {
298        return accountingCycleCachingService.getObjectCode(objectCode.getUniversityFiscalYear(), objectCode.getReportsToChartOfAccountsCode(), objectCode.getReportsToFinancialObjectCode());
299     }
300 
301     
302 
303 
304 
305 
306 
307 
308 
309 
310 
311     @Override
312     public String post(Transaction t, int mode, Date postDate, ReportWriterService posterReportWriterService) {
313         LOG.debug("post() started");
314 
315         if (ObjectUtils.isNull(t.getFinancialObject()) || !hasValidObjectCodeReportingHierarchy(t.getFinancialObject())) {
316             
317             return GeneralLedgerConstants.ERROR_CODE + ": Warning - excluding transaction from Indirect Cost Recovery because "+t.getUniversityFiscalYear().toString()+"-"+t.getChartOfAccountsCode()+"-"+t.getFinancialObjectCode()+" has an invalid reports to hierarchy (either has an non-existent object or an inactive object)";
318         }
319         else if (isIcrTransaction(t, posterReportWriterService)) {
320             return postTransaction(t, mode);
321         }
322         return GeneralLedgerConstants.EMPTY_CODE;
323     }
324 
325     
326 
327 
328 
329 
330 
331 
332     protected String postTransaction(Transaction t, int mode) {
333         LOG.debug("postTransaction() started");
334 
335         String returnCode = GeneralLedgerConstants.UPDATE_CODE;
336         ExpenditureTransaction et = accountingCycleCachingService.getExpenditureTransaction(t);
337         if (et == null) {
338             LOG.debug("Posting expenditure transation");
339             et = new ExpenditureTransaction(t);
340             returnCode = GeneralLedgerConstants.INSERT_CODE;
341         }
342 
343         if (org.apache.commons.lang.StringUtils.isBlank(t.getOrganizationReferenceId())) {
344             et.setOrganizationReferenceId(GeneralLedgerConstants.getDashOrganizationReferenceId());
345         }
346 
347         if (OLEConstants.GL_DEBIT_CODE.equals(t.getTransactionDebitCreditCode()) || OLEConstants.GL_BUDGET_CODE.equals(t.getTransactionDebitCreditCode())) {
348             et.setAccountObjectDirectCostAmount(et.getAccountObjectDirectCostAmount().add(t.getTransactionLedgerEntryAmount()));
349         }
350         else {
351             et.setAccountObjectDirectCostAmount(et.getAccountObjectDirectCostAmount().subtract(t.getTransactionLedgerEntryAmount()));
352         }
353 
354         if (returnCode.equals(GeneralLedgerConstants.INSERT_CODE)) {
355             
356             LOG.info("Inserting a GLEX record. Transaction:"+t);
357             accountingCycleCachingService.insertExpenditureTransaction(et);
358         } else {
359             
360             LOG.info("Updating a GLEX record. Transaction:"+t);
361             accountingCycleCachingService.updateExpenditureTransaction(et);
362         }
363 
364         return returnCode;
365     }
366 
367     
368 
369 
370     @Override
371     public String getDestinationName() {
372         return persistenceStructureService.getTableName(ExpenditureTransaction.class);
373     }
374 
375     public void setAccountingCycleCachingService(AccountingCycleCachingService accountingCycleCachingService) {
376         this.accountingCycleCachingService = accountingCycleCachingService;
377     }
378 
379     public void setPersistenceStructureService(PersistenceStructureService persistenceStructureService) {
380         this.persistenceStructureService = persistenceStructureService;
381     }
382 
383     
384 
385 
386 
387     public ParameterService getParameterService() {
388         return parameterService;
389     }
390 
391     
392 
393 
394 
395     public void setParameterService(ParameterService parameterService) {
396         this.parameterService = parameterService;
397     }
398 
399 }