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.sql.Date;
19 import java.util.ArrayList;
20 import java.util.Collection;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.kuali.ole.coa.businessobject.Account;
27 import org.kuali.ole.coa.service.AccountService;
28 import org.kuali.ole.gl.GeneralLedgerConstants;
29 import org.kuali.ole.gl.batch.SufficientFundsAccountUpdateStep;
30 import org.kuali.ole.gl.batch.service.SufficientFundsAccountUpdateService;
31 import org.kuali.ole.gl.businessobject.Balance;
32 import org.kuali.ole.gl.businessobject.SufficientFundBalances;
33 import org.kuali.ole.gl.businessobject.SufficientFundRebuild;
34 import org.kuali.ole.gl.dataaccess.BalanceDao;
35 import org.kuali.ole.gl.dataaccess.SufficientFundBalancesDao;
36 import org.kuali.ole.gl.dataaccess.SufficientFundRebuildDao;
37 import org.kuali.ole.gl.service.SufficientFundsService;
38 import org.kuali.ole.sys.Message;
39 import org.kuali.ole.sys.OLEConstants;
40 import org.kuali.ole.sys.OLEKeyConstants;
41 import org.kuali.ole.sys.OLEPropertyConstants;
42 import org.kuali.ole.sys.businessobject.SystemOptions;
43 import org.kuali.ole.sys.context.SpringContext;
44 import org.kuali.ole.sys.service.ReportWriterService;
45 import org.kuali.rice.core.api.config.property.ConfigurationService;
46 import org.kuali.rice.core.api.datetime.DateTimeService;
47 import org.kuali.rice.core.api.util.type.KualiDecimal;
48 import org.kuali.rice.coreservice.framework.parameter.ParameterService;
49 import org.kuali.rice.krad.service.BusinessObjectService;
50 import org.springframework.transaction.annotation.Transactional;
51
52
53
54
55 @Transactional
56 public class SufficientFundsAccountUpdateServiceImpl implements SufficientFundsAccountUpdateService {
57 private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SufficientFundsAccountUpdateServiceImpl.class);
58
59 private DateTimeService dateTimeService;
60 private ConfigurationService kualiConfigurationService;
61 private BalanceDao balanceDao;
62 private SufficientFundBalancesDao sufficientFundBalancesDao;
63 private SufficientFundRebuildDao sufficientFundRebuildDao;
64 private SufficientFundsService sufficientFundsService;
65 private AccountService accountService;
66 private ReportWriterService reportWriterService;
67 private BusinessObjectService boService;
68
69 private Date runDate;
70 private SystemOptions options;
71
72 Map batchError;
73 List reportSummary;
74 List<Message> transactionErrors;
75
76 private Integer universityFiscalYear;
77 private int sfrbRecordsConvertedCount = 0;
78 private int sfrbRecordsReadCount = 0;
79 private int sfrbRecordsDeletedCount = 0;
80 private int sfrbNotDeletedCount = 0;
81 private int sfblDeletedCount = 0;
82 private int sfblInsertedCount = 0;
83 private int sfblUpdatedCount = 0;
84 private int warningCount = 0;
85
86
87 private SufficientFundBalances currentSfbl = null;
88
89
90
91
92 public SufficientFundsAccountUpdateServiceImpl() {
93 super();
94 }
95
96
97
98
99
100
101 protected Integer getFiscalYear() {
102 String val = SpringContext.getBean(ParameterService.class).getParameterValueAsString(SufficientFundsAccountUpdateStep.class, GeneralLedgerConstants.FISCAL_YEAR_PARM);
103 return Integer.parseInt(val);
104 }
105
106
107
108
109
110 @Override
111 public void rebuildSufficientFunds() {
112 List <SufficientFundRebuild> rebuildSfrbList = new ArrayList<SufficientFundRebuild>();
113
114 universityFiscalYear = getFiscalYear();
115 if ( LOG.isInfoEnabled() ) {
116 LOG.info( "Rebuilding sufficient funds for fiscal year: " + universityFiscalYear );
117 }
118 initService();
119
120
121 runDate = dateTimeService.getCurrentSqlDate();
122
123
124 LOG.info("rebuildSufficientFunds() Converting O types to A types");
125 Map criteria = new HashMap();
126 criteria.put(OLEPropertyConstants.ACCOUNT_FINANCIAL_OBJECT_TYPE_CODE, OLEConstants.SF_TYPE_OBJECT);
127
128 for (Iterator iter = boService.findMatching(SufficientFundRebuild.class, criteria).iterator(); iter.hasNext();) {
129 SufficientFundRebuild sfrb = (SufficientFundRebuild) iter.next();
130 ++sfrbRecordsReadCount;
131
132 transactionErrors = new ArrayList<Message>();
133
134 convertOtypeToAtypes(sfrb);
135
136 if (transactionErrors.size() > 0) {
137 reportWriterService.writeError(sfrb, transactionErrors);
138 rebuildSfrbList.add(sfrb);
139 }
140 }
141 criteria.clear();
142
143
144 LOG.info("rebuildSufficientFunds() Calculating SF balances for all A types");
145
146 criteria.put(OLEPropertyConstants.ACCOUNT_FINANCIAL_OBJECT_TYPE_CODE, OLEConstants.SF_TYPE_ACCOUNT);
147
148 for (Iterator iter = boService.findMatching(SufficientFundRebuild.class, criteria).iterator(); iter.hasNext();) {
149 SufficientFundRebuild sfrb = (SufficientFundRebuild) iter.next();
150 ++sfrbRecordsReadCount;
151
152 transactionErrors = new ArrayList<Message>();
153
154 calculateSufficientFundsByAccount(sfrb);
155
156 if (transactionErrors.size() > 0) {
157 reportWriterService.writeError(sfrb, transactionErrors);
158 rebuildSfrbList.add(sfrb);
159 }
160
161 }
162 sufficientFundRebuildDao.purgeSufficientFundRebuild();
163 boService.save( rebuildSfrbList);
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183 LOG.info("rebuildSufficientFunds() Create report");
184
185
186 reportWriterService.writeStatisticLine(" SFRB RECORDS CONVERTED FROM OBJECT TO ACCOUNT %,9d\n", sfrbRecordsConvertedCount);
187 reportWriterService.writeStatisticLine(" POST CONVERSION SFRB RECORDS READ %,9d\n", sfrbRecordsReadCount);
188 reportWriterService.writeStatisticLine(" SFRB RECORDS DELETED %,9d\n", sfrbRecordsDeletedCount);
189 reportWriterService.writeStatisticLine(" SFRB RECORDS KEPT DUE TO ERRORS %,9d\n", sfrbNotDeletedCount);
190 reportWriterService.writeStatisticLine(" SFBL RECORDS DELETED %,9d\n", sfblDeletedCount);
191 reportWriterService.writeStatisticLine(" SFBL RECORDS ADDED %,9d\n", sfblInsertedCount);
192 reportWriterService.writeStatisticLine(" SFBL RECORDS UDPATED %,9d\n", sfblUpdatedCount);
193 }
194
195
196
197
198 protected void initService() {
199 batchError = new HashMap();
200 reportSummary = new ArrayList();
201
202 runDate = new Date(dateTimeService.getCurrentDate().getTime());
203
204 options = (SystemOptions)boService.findBySinglePrimaryKey(SystemOptions.class, universityFiscalYear);
205
206 if (options == null) {
207 throw new IllegalStateException(kualiConfigurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_UNIV_DATE_NOT_FOUND));
208 }
209 }
210
211
212
213
214
215
216
217 @Override
218 public void convertOtypeToAtypes(SufficientFundRebuild sfrb) {
219 ++sfrbRecordsConvertedCount;
220 Collection fundBalances = sufficientFundBalancesDao.getByObjectCode(universityFiscalYear, sfrb.getChartOfAccountsCode(), sfrb.getAccountNumberFinancialObjectCode());
221 Map criteria = new HashMap();
222
223 for (Iterator fundBalancesIter = fundBalances.iterator(); fundBalancesIter.hasNext();) {
224 SufficientFundBalances sfbl = (SufficientFundBalances) fundBalancesIter.next();
225 criteria.put(OLEPropertyConstants.CHART_OF_ACCOUNTS_CODE, sfbl.getChartOfAccountsCode());
226 criteria.put(OLEPropertyConstants.ACCOUNT_FINANCIAL_OBJECT_TYPE_CODE, OLEConstants.SF_TYPE_ACCOUNT);
227 criteria.put(OLEPropertyConstants.ACCOUNT_NUMBER_FINANCIAL_OBJECT_CODE, sfbl.getAccountNumber());
228
229 SufficientFundRebuild altSfrb = (SufficientFundRebuild)boService.findByPrimaryKey(SufficientFundRebuild.class, criteria);
230 if (altSfrb == null) {
231 altSfrb = new SufficientFundRebuild();
232 altSfrb.setAccountFinancialObjectTypeCode(OLEConstants.SF_TYPE_ACCOUNT);
233 altSfrb.setAccountNumberFinancialObjectCode(sfbl.getAccountNumber());
234 altSfrb.setChartOfAccountsCode(sfbl.getChartOfAccountsCode());
235 boService.save(altSfrb);
236 }
237 criteria.clear();
238 }
239 }
240
241
242
243
244
245
246 @Override
247 public void calculateSufficientFundsByAccount(SufficientFundRebuild sfrb) {
248 Account sfrbAccount = accountService.getByPrimaryId(sfrb.getChartOfAccountsCode(), sfrb.getAccountNumberFinancialObjectCode());
249 if (sfrbAccount == null) {
250 String msg = "Account found in SufficientFundsRebuild table that is not in Accounts table [" + sfrb.getChartOfAccountsCode() + "-" + sfrb.getAccountNumberFinancialObjectCode() + "].";
251 LOG.error(msg);
252 throw new RuntimeException(msg);
253 }
254 if ((sfrbAccount.getAccountSufficientFundsCode() != null)
255 && (OLEConstants.SF_TYPE_ACCOUNT.equals(sfrbAccount.getAccountSufficientFundsCode())
256 || OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(sfrbAccount.getAccountSufficientFundsCode())
257 || OLEConstants.SF_TYPE_CONSOLIDATION.equals(sfrbAccount.getAccountSufficientFundsCode())
258 || OLEConstants.SF_TYPE_LEVEL.equals(sfrbAccount.getAccountSufficientFundsCode())
259 || OLEConstants.SF_TYPE_OBJECT.equals(sfrbAccount.getAccountSufficientFundsCode())
260 || OLEConstants.SF_TYPE_NO_CHECKING.equals(sfrbAccount.getAccountSufficientFundsCode()))) {
261 ++sfrbRecordsDeletedCount;
262 sfblDeletedCount += sufficientFundBalancesDao.deleteByAccountNumber(universityFiscalYear, sfrb.getChartOfAccountsCode(), sfrbAccount.getAccountNumber());
263
264 if (OLEConstants.SF_TYPE_NO_CHECKING.equalsIgnoreCase(sfrbAccount.getAccountSufficientFundsCode())) {
265
266 return;
267 }
268
269 Iterator balancesIterator = balanceDao.findAccountBalances(universityFiscalYear, sfrb.getChartOfAccountsCode(), sfrb.getAccountNumberFinancialObjectCode(), sfrbAccount.getAccountSufficientFundsCode());
270
271 if (balancesIterator == null) {
272 addTransactionError(kualiConfigurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_BALANCE_NOT_FOUND_FOR) + universityFiscalYear + ")");
273 ++warningCount;
274 ++sfrbNotDeletedCount;
275 return;
276 }
277
278 String currentFinObjectCd = "";
279 while (balancesIterator.hasNext()) {
280 Balance balance = (Balance) balancesIterator.next();
281 if ( LOG.isDebugEnabled() ) {
282 LOG.debug("Processing Balance for SF: " + balance.getChartOfAccountsCode() + " / " + balance.getAccountNumber() + " / " + balance.getObjectCode() );
283 }
284 String tempFinObjectCd = sufficientFundsService.getSufficientFundsObjectCode(balance.getFinancialObject(), sfrbAccount.getAccountSufficientFundsCode());
285 if ( LOG.isDebugEnabled() ) {
286 LOG.debug("SF Code: " + sfrbAccount.getAccountSufficientFundsCode() + " / Using SF Object Code: '" + tempFinObjectCd + "'" );
287 }
288 if (!tempFinObjectCd.equals(currentFinObjectCd)) {
289
290 currentFinObjectCd = tempFinObjectCd;
291
292 if (currentSfbl != null && amountsAreNonZero(currentSfbl)) {
293 boService.save(currentSfbl);
294 ++sfblInsertedCount;
295 }
296
297 currentSfbl = new SufficientFundBalances();
298 currentSfbl.setUniversityFiscalYear(universityFiscalYear);
299 currentSfbl.setChartOfAccountsCode(sfrb.getChartOfAccountsCode());
300 currentSfbl.setAccountNumber(sfrbAccount.getAccountNumber());
301 currentSfbl.setFinancialObjectCode(currentFinObjectCd);
302 currentSfbl.setAccountSufficientFundsCode(sfrbAccount.getAccountSufficientFundsCode());
303 currentSfbl.setAccountActualExpenditureAmt(KualiDecimal.ZERO);
304 currentSfbl.setAccountEncumbranceAmount(KualiDecimal.ZERO);
305 currentSfbl.setCurrentBudgetBalanceAmount(KualiDecimal.ZERO);
306 }
307
308 if (sfrbAccount.isForContractsAndGrants()) {
309 if ( LOG.isDebugEnabled() ) {
310 LOG.debug("Account is C&G. Adding C&G beginning balance amount (" + balance.getContractsGrantsBeginningBalanceAmount() + ") to the annual balance amount.");
311 }
312 balance.setAccountLineAnnualBalanceAmount(balance.getAccountLineAnnualBalanceAmount().add(balance.getContractsGrantsBeginningBalanceAmount()));
313 }
314
315 if (OLEConstants.SF_TYPE_CASH_AT_ACCOUNT.equals(sfrbAccount.getAccountSufficientFundsCode())) {
316 if ( LOG.isDebugEnabled() ) {
317 LOG.debug("SF type is Cash at account");
318 }
319 processCash(sfrbAccount, balance);
320 }
321 else {
322 processObjectOrAccount(sfrbAccount, balance);
323 }
324 }
325
326
327 if (currentSfbl != null && amountsAreNonZero(currentSfbl)) {
328 boService.save(currentSfbl);
329 ++sfblInsertedCount;
330 }
331 }
332 else {
333 addTransactionError(kualiConfigurationService.getPropertyValueAsString(OLEKeyConstants.ERROR_INVALID_ACCOUNT_SF_CODE_FOR));
334 ++warningCount;
335 ++sfrbNotDeletedCount;
336 return;
337 }
338 }
339
340
341
342
343
344
345
346 protected boolean amountsAreNonZero(SufficientFundBalances sfbl) {
347 boolean zero = true;
348 zero &= KualiDecimal.ZERO.equals(sfbl.getAccountActualExpenditureAmt());
349 zero &= KualiDecimal.ZERO.equals(sfbl.getAccountEncumbranceAmount());
350 zero &= KualiDecimal.ZERO.equals(sfbl.getCurrentBudgetBalanceAmount());
351 return !zero;
352 }
353
354
355
356
357
358
359
360 protected void processObjectOrAccount(Account sfrbAccount, Balance balance) {
361 if (options.getFinObjTypeExpenditureexpCd().equals(balance.getObjectTypeCode()) || options.getFinObjTypeExpendNotExpCode().equals(balance.getObjectTypeCode()) || options.getFinObjTypeExpNotExpendCode().equals(balance.getObjectTypeCode()) || options.getFinancialObjectTypeTransferExpenseCd().equals(balance.getObjectTypeCode())) {
362 if (options.getActualFinancialBalanceTypeCd().equals(balance.getBalanceTypeCode())) {
363 processObjtAcctActual(balance);
364 }
365 else if (options.getExtrnlEncumFinBalanceTypCd().equals(balance.getBalanceTypeCode()) || options.getIntrnlEncumFinBalanceTypCd().equals(balance.getBalanceTypeCode()) || options.getPreencumbranceFinBalTypeCd().equals(balance.getBalanceTypeCode()) || options.getCostShareEncumbranceBalanceTypeCd().equals(balance.getBalanceTypeCode())) {
366 processObjtAcctEncmbrnc(balance);
367 }
368 else if (options.getBudgetCheckingBalanceTypeCd().equals(balance.getBalanceTypeCode())) {
369 processObjtAcctBudget(balance);
370 }
371 } else {
372 if ( LOG.isDebugEnabled() ) {
373 LOG.debug("Skipped balance record. Object Type did not match.");
374 LOG.debug( balance.getObjectTypeCode() + " NOT IN ( " + options.getFinObjTypeExpenditureexpCd() + "," + options.getFinObjTypeExpendNotExpCode() + "," + options.getFinObjTypeExpNotExpendCode() + "," + options.getFinancialObjectTypeTransferExpenseCd() );
375 }
376 }
377 }
378
379
380
381
382
383
384 protected void processObjtAcctActual(Balance balance) {
385 currentSfbl.setAccountActualExpenditureAmt(currentSfbl.getAccountActualExpenditureAmt().add(balance.getAccountLineAnnualBalanceAmount()));
386 }
387
388
389
390
391
392
393 protected void processObjtAcctEncmbrnc(Balance balance) {
394 currentSfbl.setAccountEncumbranceAmount(currentSfbl.getAccountEncumbranceAmount().add(balance.getAccountLineAnnualBalanceAmount()));
395 currentSfbl.setAccountEncumbranceAmount(currentSfbl.getAccountEncumbranceAmount().add(balance.getBeginningBalanceLineAmount()));
396 }
397
398
399
400
401
402
403 protected void processObjtAcctBudget(Balance balance) {
404 currentSfbl.setCurrentBudgetBalanceAmount(currentSfbl.getCurrentBudgetBalanceAmount().add(balance.getAccountLineAnnualBalanceAmount()));
405 currentSfbl.setCurrentBudgetBalanceAmount(currentSfbl.getCurrentBudgetBalanceAmount().add(balance.getBeginningBalanceLineAmount()));
406 }
407
408
409
410
411
412
413
414 protected void processCash(Account sfrbAccount, Balance balance) {
415 if (balance.getBalanceTypeCode().equals(options.getActualFinancialBalanceTypeCd())) {
416 if (balance.getObjectCode().equals(sfrbAccount.getChartOfAccounts().getFinancialCashObjectCode()) || balance.getObjectCode().equals(sfrbAccount.getChartOfAccounts().getFinAccountsPayableObjectCode())) {
417 processCashActual(sfrbAccount, balance);
418 }
419 }
420 else if (balance.getBalanceTypeCode().equals(options.getExtrnlEncumFinBalanceTypCd()) || balance.getBalanceTypeCode().equals(options.getIntrnlEncumFinBalanceTypCd()) || balance.getBalanceTypeCode().equals(options.getPreencumbranceFinBalTypeCd()) || options.getCostShareEncumbranceBalanceTypeCd().equals(balance.getBalanceTypeCode())) {
421 if (balance.getObjectTypeCode().equals(options.getFinObjTypeExpenditureexpCd()) || balance.getObjectTypeCode().equals(options.getFinObjTypeExpendNotExpCode()) || options.getFinancialObjectTypeTransferExpenseCd().equals(balance.getObjectTypeCode()) || options.getFinObjTypeExpNotExpendCode().equals(balance.getObjectTypeCode())) {
422 processCashEncumbrance(balance);
423 }
424 }
425 }
426
427
428
429
430
431
432
433 protected void processCashActual(Account sfrbAccount, Balance balance) {
434 if (balance.getObjectCode().equals(sfrbAccount.getChartOfAccounts().getFinancialCashObjectCode())) {
435 currentSfbl.setCurrentBudgetBalanceAmount(currentSfbl.getCurrentBudgetBalanceAmount().add(balance.getAccountLineAnnualBalanceAmount()));
436 currentSfbl.setCurrentBudgetBalanceAmount(currentSfbl.getCurrentBudgetBalanceAmount().add(balance.getBeginningBalanceLineAmount()));
437 }
438 if (balance.getObjectCode().equals(sfrbAccount.getChartOfAccounts().getFinAccountsPayableObjectCode())) {
439 currentSfbl.setCurrentBudgetBalanceAmount(currentSfbl.getCurrentBudgetBalanceAmount().subtract(balance.getAccountLineAnnualBalanceAmount()));
440 currentSfbl.setCurrentBudgetBalanceAmount(currentSfbl.getCurrentBudgetBalanceAmount().subtract(balance.getBeginningBalanceLineAmount()));
441 }
442 }
443
444
445
446
447
448
449 protected void processCashEncumbrance(Balance balance) {
450 currentSfbl.setAccountEncumbranceAmount(currentSfbl.getAccountEncumbranceAmount().add(balance.getAccountLineAnnualBalanceAmount()));
451 currentSfbl.setAccountEncumbranceAmount(currentSfbl.getAccountEncumbranceAmount().add(balance.getBeginningBalanceLineAmount()));
452 }
453
454
455
456
457
458 protected void addTransactionError(String errorMessage) {
459 transactionErrors.add(new Message(errorMessage, Message.TYPE_WARNING));
460 }
461
462 public void setDateTimeService(DateTimeService dateTimeService) {
463 this.dateTimeService = dateTimeService;
464 }
465
466 public void setConfigurationService(ConfigurationService kualiConfigurationService) {
467 this.kualiConfigurationService = kualiConfigurationService;
468 }
469
470 public void setBalanceDao(BalanceDao balanceDao) {
471 this.balanceDao = balanceDao;
472 }
473
474 public void setSufficientFundBalancesDao(SufficientFundBalancesDao sufficientFundBalancesDao) {
475 this.sufficientFundBalancesDao = sufficientFundBalancesDao;
476 }
477
478 public void setReportWriterService(ReportWriterService sfrs) {
479 reportWriterService = sfrs;
480 }
481
482 public void setAccountService(AccountService accountService) {
483 this.accountService = accountService;
484 }
485
486 public void setSufficientFundsService(SufficientFundsService sfs) {
487 sufficientFundsService = sfs;
488 }
489
490 public void setBusinessObjectService(BusinessObjectService bos) {
491 boService = bos;
492 }
493
494 public void setSufficientFundRebuildDao(SufficientFundRebuildDao sufficientFundRebuildDao) {
495 this.sufficientFundRebuildDao = sufficientFundRebuildDao;
496 }
497 }