1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.kuali.kfs.module.ec.service.impl;
20
21 import java.math.BigDecimal;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29
30 import org.apache.commons.lang.StringUtils;
31 import org.kuali.kfs.coa.businessobject.Account;
32 import org.kuali.kfs.integration.cg.ContractsAndGrantsModuleService;
33 import org.kuali.kfs.integration.ld.LaborLedgerExpenseTransferAccountingLine;
34 import org.kuali.kfs.integration.ld.LaborLedgerExpenseTransferSourceAccountingLine;
35 import org.kuali.kfs.integration.ld.LaborLedgerExpenseTransferTargetAccountingLine;
36 import org.kuali.kfs.integration.ld.LaborModuleService;
37 import org.kuali.kfs.module.ec.EffortConstants;
38 import org.kuali.kfs.module.ec.EffortKeyConstants;
39 import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetail;
40 import org.kuali.kfs.module.ec.businessobject.EffortCertificationDetailBuild;
41 import org.kuali.kfs.module.ec.businessobject.EffortCertificationDocumentBuild;
42 import org.kuali.kfs.module.ec.businessobject.EffortCertificationReportDefinition;
43 import org.kuali.kfs.module.ec.document.EffortCertificationDocument;
44 import org.kuali.kfs.module.ec.document.validation.impl.EffortCertificationDocumentRuleUtil;
45 import org.kuali.kfs.module.ec.service.EffortCertificationDocumentService;
46 import org.kuali.kfs.sys.KFSConstants;
47 import org.kuali.kfs.sys.KFSPropertyConstants;
48 import org.kuali.kfs.sys.MessageBuilder;
49 import org.kuali.kfs.sys.businessobject.AccountingLineOverride;
50 import org.kuali.kfs.sys.businessobject.AccountingLineOverride.COMPONENT;
51 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
52 import org.kuali.rice.core.api.util.type.KualiDecimal;
53 import org.kuali.rice.kew.api.WorkflowDocument;
54 import org.kuali.rice.kew.api.action.ActionRequestType;
55 import org.kuali.rice.kew.api.action.ActionTaken;
56 import org.kuali.rice.kew.api.action.ActionType;
57 import org.kuali.rice.kew.api.exception.WorkflowException;
58 import org.kuali.rice.kim.api.identity.Person;
59 import org.kuali.rice.kim.api.identity.PersonService;
60 import org.kuali.rice.kim.api.services.KimApiServiceLocator;
61 import org.kuali.rice.krad.UserSession;
62 import org.kuali.rice.krad.bo.AdHocRoutePerson;
63 import org.kuali.rice.krad.service.BusinessObjectService;
64 import org.kuali.rice.krad.service.DocumentService;
65 import org.kuali.rice.krad.service.KualiModuleService;
66 import org.kuali.rice.krad.util.GlobalVariables;
67 import org.springframework.transaction.annotation.Transactional;
68
69
70
71
72 @Transactional
73 public class EffortCertificationDocumentServiceImpl implements EffortCertificationDocumentService {
74 public static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(EffortCertificationDocumentServiceImpl.class);
75
76 private LaborModuleService laborModuleService;
77 private KualiModuleService kualiModuleService;
78 private ContractsAndGrantsModuleService contractsAndGrantsModuleService;
79
80 private DocumentService documentService;
81 private BusinessObjectService businessObjectService;
82
83
84
85
86 @Override
87 public void processApprovedEffortCertificationDocument(EffortCertificationDocument effortCertificationDocument) {
88 WorkflowDocument workflowDocument = effortCertificationDocument.getDocumentHeader().getWorkflowDocument();
89
90 if (workflowDocument.isProcessed()) {
91 GlobalVariables.setUserSession(new UserSession(KFSConstants.SYSTEM_USER));
92 generateSalaryExpenseTransferDocument(effortCertificationDocument);
93 }
94 }
95
96
97
98
99
100 @Override
101 public boolean createAndRouteEffortCertificationDocument(EffortCertificationDocumentBuild effortCertificationDocumentBuild) {
102 try {
103 EffortCertificationDocument effortCertificationDocument = (EffortCertificationDocument) documentService.getNewDocument(EffortConstants.EffortDocumentTypes.EFFORT_CERTIFICATION_DOCUMENT);
104 populateEffortCertificationDocument(effortCertificationDocument, effortCertificationDocumentBuild);
105 documentService.routeDocument(effortCertificationDocument, KFSConstants.EMPTY_STRING, null);
106 }
107 catch (WorkflowException we) {
108 LOG.error( "Unable to route ECD document: " + effortCertificationDocumentBuild, we);
109 throw new RuntimeException("Unable to route ECD document: " + effortCertificationDocumentBuild, we);
110 }
111
112 return true;
113 }
114
115
116
117
118
119
120 @Override
121 public boolean populateEffortCertificationDocument(EffortCertificationDocument effortCertificationDocument, EffortCertificationDocumentBuild effortCertificationDocumentBuild) {
122
123 effortCertificationDocument.setUniversityFiscalYear(effortCertificationDocumentBuild.getUniversityFiscalYear());
124 effortCertificationDocument.setEmplid(effortCertificationDocumentBuild.getEmplid());
125 effortCertificationDocument.setEffortCertificationReportNumber(effortCertificationDocumentBuild.getEffortCertificationReportNumber());
126 effortCertificationDocument.setEffortCertificationDocumentCode(effortCertificationDocumentBuild.getEffortCertificationDocumentCode());
127
128
129 List<EffortCertificationDetail> detailLines = effortCertificationDocument.getEffortCertificationDetailLines();
130 detailLines.clear();
131
132 List<EffortCertificationDetailBuild> detailLinesBuild = effortCertificationDocumentBuild.getEffortCertificationDetailLinesBuild();
133 for (EffortCertificationDetailBuild detailLineBuild : detailLinesBuild) {
134 detailLines.add(new EffortCertificationDetail(detailLineBuild));
135 }
136
137
138 FinancialSystemDocumentHeader documentHeader = effortCertificationDocument.getFinancialSystemDocumentHeader();
139 documentHeader.setDocumentDescription(effortCertificationDocumentBuild.getEmplid());
140 documentHeader.setFinancialDocumentTotalAmount(EffortCertificationDocument.getDocumentTotalAmount(effortCertificationDocument));
141
142 return true;
143 }
144
145
146
147
148
149 @Override
150 public void removeEffortCertificationDetailLines(EffortCertificationDocument effortCertificationDocument) {
151 Map<String, String> fieldValues = new HashMap<String, String>();
152 fieldValues.put(KFSPropertyConstants.DOCUMENT_NUMBER, effortCertificationDocument.getDocumentNumber());
153
154 businessObjectService.deleteMatching(EffortCertificationDetail.class, fieldValues);
155 }
156
157
158
159
160
161 @Override
162 public boolean generateSalaryExpenseTransferDocument(EffortCertificationDocument effortCertificationDocument) {
163 List<LaborLedgerExpenseTransferAccountingLine> sourceAccoutingLines = this.buildSourceAccountingLines(effortCertificationDocument);
164 List<LaborLedgerExpenseTransferAccountingLine> targetAccoutingLines = this.buildTargetAccountingLines(effortCertificationDocument);
165
166 if (sourceAccoutingLines.isEmpty() || targetAccoutingLines.isEmpty()) {
167 return true;
168 }
169
170 String description = effortCertificationDocument.getEmplid();
171 String explanation = MessageBuilder.buildMessageWithPlaceHolder(EffortKeyConstants.MESSAGE_CREATE_SET_DOCUMENT_DESCRIPTION, effortCertificationDocument.getDocumentNumber()).toString();
172
173 String annotation = KFSConstants.EMPTY_STRING;
174 List<String> adHocRecipients = new ArrayList<String>();
175 adHocRecipients.addAll(this.getFiscalOfficersIfAmountChanged(effortCertificationDocument));
176
177 try {
178 laborModuleService.createAndBlankApproveSalaryExpenseTransferDocument(description, explanation, annotation, adHocRecipients, sourceAccoutingLines, targetAccoutingLines);
179 }
180 catch (WorkflowException we) {
181 LOG.error( "Error while routing SET document created from ECD: " + effortCertificationDocument, we);
182 throw new RuntimeException("Error while routing SET document created from ECD: " + effortCertificationDocument, we);
183 }
184 return true;
185 }
186
187
188
189
190
191 @Override
192 public void addRouteLooping(EffortCertificationDocument effortCertificationDocument) {
193 WorkflowDocument workflowDocument = effortCertificationDocument.getDocumentHeader().getWorkflowDocument();
194 Set<Person> priorApprovers = getPriorApprovers(workflowDocument);
195
196 for (EffortCertificationDetail detailLine : effortCertificationDocument.getEffortCertificationDetailLines()) {
197 boolean hasBeenChanged = EffortCertificationDocumentRuleUtil.isPayrollAmountChangedFromPersisted(detailLine);
198 if (!hasBeenChanged) {
199 continue;
200 }
201 boolean isNewLine = detailLine.isNewLineIndicator();
202 if ( LOG.isInfoEnabled() ) {
203 LOG.info( "EC Detail Line has been changed: " + detailLine );
204 }
205
206 Account account = detailLine.getAccount();
207 Person fiscalOfficer = account.getAccountFiscalOfficerUser();
208 if ( fiscalOfficer != null && StringUtils.isNotBlank(fiscalOfficer.getPrincipalName())) {
209
210
211 AdHocRoutePerson adHocRoutePerson = buildAdHocRouteRecipient(fiscalOfficer.getPrincipalName(), ActionRequestType.APPROVE);
212
213 addAdHocRoutePerson(effortCertificationDocument.getAdHocRoutePersons(), priorApprovers, adHocRoutePerson, isNewLine);
214 } else {
215 LOG.warn( "Unable to obtain a fiscal officer for the detail line's account: " + account.getChartOfAccountsCode() + "-" + account.getAccountNumber() );
216 }
217
218 Person projectDirector = contractsAndGrantsModuleService.getProjectDirectorForAccount(account);
219 if (projectDirector != null) {
220 String accountProjectDirectorPersonUserId = projectDirector.getPrincipalName();
221
222
223
224 AdHocRoutePerson adHocRoutePerson = buildAdHocRouteRecipient(accountProjectDirectorPersonUserId, ActionRequestType.APPROVE);
225
226 addAdHocRoutePerson(effortCertificationDocument.getAdHocRoutePersons(), priorApprovers, adHocRoutePerson, isNewLine);
227 }
228 }
229 }
230
231
232
233
234
235
236
237
238 protected void addAdHocRoutePerson(Collection<AdHocRoutePerson> adHocRoutePersonList, Set<Person> priorApprovers, AdHocRoutePerson adHocRoutePerson) {
239 addAdHocRoutePerson(adHocRoutePersonList, priorApprovers, adHocRoutePerson, false);
240 }
241
242
243
244
245
246
247
248
249
250 protected void addAdHocRoutePerson(Collection<AdHocRoutePerson> adHocRoutePersonList, Set<Person> priorApprovers, AdHocRoutePerson adHocRoutePerson, boolean isNewLine) {
251 boolean canBeAdded = false;
252
253
254 if (priorApprovers == null || isNewLine) {
255 canBeAdded = true;
256 } else {
257
258 for (Person approver : priorApprovers) {
259 if (StringUtils.equals(approver.getPrincipalName(), adHocRoutePerson.getId())) {
260 canBeAdded = true;
261 break;
262 }
263 }
264 }
265
266 if (canBeAdded) {
267
268 for (AdHocRoutePerson person : adHocRoutePersonList) {
269 if (isSameAdHocRoutePerson(person, adHocRoutePerson)) {
270 canBeAdded = false;
271 break;
272 }
273 }
274 }
275
276 if (canBeAdded) {
277 adHocRoutePersonList.add(adHocRoutePerson);
278 }
279 }
280
281 protected boolean isSameAdHocRoutePerson(AdHocRoutePerson person1, AdHocRoutePerson person2) {
282 if (person1 == null || person2 == null) {
283 return false;
284 }
285
286 boolean isSameAdHocRoutePerson = StringUtils.equals(person1.getId(), person2.getId());
287 isSameAdHocRoutePerson &= person1.getType().equals(person2.getType());
288 isSameAdHocRoutePerson &= StringUtils.equals(person1.getActionRequested(), person2.getActionRequested());
289
290 return isSameAdHocRoutePerson;
291 }
292
293 protected Set<Person> getPriorApprovers(WorkflowDocument workflowDocument) {
294 PersonService personService = KimApiServiceLocator.getPersonService();
295 List<ActionTaken> actionsTaken = workflowDocument.getActionsTaken();
296 Set<String> principalIds = new HashSet<String>();
297 Set<Person> persons = new HashSet<Person>();
298
299 for (ActionTaken actionTaken : actionsTaken) {
300 if (ActionType.APPROVE.equals(actionTaken.getActionTaken())) {
301 String principalId = actionTaken.getPrincipalId();
302 if (!principalIds.contains(principalId)) {
303 principalIds.add(principalId);
304 persons.add(personService.getPerson(principalId));
305 }
306 }
307 }
308 return persons;
309 }
310
311
312
313
314
315
316
317
318 protected AdHocRoutePerson buildAdHocRouteRecipient(String personUserId, ActionRequestType actionRequest) {
319 AdHocRoutePerson adHocRoutePerson = new AdHocRoutePerson();
320 adHocRoutePerson.setActionRequested(actionRequest.getCode());
321 adHocRoutePerson.setId(personUserId);
322
323 return adHocRoutePerson;
324 }
325
326
327
328
329
330
331
332
333 protected List<LaborLedgerExpenseTransferAccountingLine> buildSourceAccountingLines(EffortCertificationDocument effortCertificationDocument) {
334 List<LaborLedgerExpenseTransferAccountingLine> sourceAccountingLines = new ArrayList<LaborLedgerExpenseTransferAccountingLine>();
335
336 List<EffortCertificationDetail> effortCertificationDetailLines = effortCertificationDocument.getEffortCertificationDetailLines();
337 for (EffortCertificationDetail detailLine : effortCertificationDetailLines) {
338 if (this.getDifference(detailLine).isPositive()) {
339 LaborLedgerExpenseTransferSourceAccountingLine sourceLine = kualiModuleService.getResponsibleModuleService(LaborLedgerExpenseTransferSourceAccountingLine.class).createNewObjectFromExternalizableClass(LaborLedgerExpenseTransferSourceAccountingLine.class);
340 this.addAccountingLineIntoList(sourceAccountingLines, sourceLine, effortCertificationDocument, detailLine);
341 }
342 }
343 return sourceAccountingLines;
344 }
345
346
347
348
349
350
351
352
353 protected List<LaborLedgerExpenseTransferAccountingLine> buildTargetAccountingLines(EffortCertificationDocument effortCertificationDocument) {
354 List<LaborLedgerExpenseTransferAccountingLine> targetAccountingLines = new ArrayList<LaborLedgerExpenseTransferAccountingLine>();
355
356 List<EffortCertificationDetail> effortCertificationDetailLines = effortCertificationDocument.getEffortCertificationDetailLines();
357 for (EffortCertificationDetail detailLine : effortCertificationDetailLines) {
358 if (this.getDifference(detailLine).isNegative()) {
359 LaborLedgerExpenseTransferTargetAccountingLine targetLine = kualiModuleService.getResponsibleModuleService(LaborLedgerExpenseTransferTargetAccountingLine.class).createNewObjectFromExternalizableClass(LaborLedgerExpenseTransferTargetAccountingLine.class);
360 this.addAccountingLineIntoList(targetAccountingLines, targetLine, effortCertificationDocument, detailLine);
361 }
362 }
363 return targetAccountingLines;
364 }
365
366
367
368
369
370
371
372 protected Set<String> getFiscalOfficersIfAmountChanged(EffortCertificationDocument effortCertificationDocument) {
373 Set<String> fiscalOfficers = new HashSet<String>();
374
375 List<EffortCertificationDetail> effortCertificationDetailLines = effortCertificationDocument.getEffortCertificationDetailLines();
376 for (EffortCertificationDetail detailLine : effortCertificationDetailLines) {
377 if (this.getDifference(detailLine).isNonZero()) {
378 Account account = detailLine.getAccount();
379 String accountFiscalOfficerPersonUserId = account.getAccountFiscalOfficerUser().getPrincipalName();
380
381 if (StringUtils.isEmpty(accountFiscalOfficerPersonUserId)) {
382 fiscalOfficers.add(accountFiscalOfficerPersonUserId);
383 }
384 }
385 }
386 return fiscalOfficers;
387 }
388
389
390
391
392
393
394
395
396
397 protected void addAccountingLineIntoList(List<LaborLedgerExpenseTransferAccountingLine> accountingLineList, LaborLedgerExpenseTransferAccountingLine accountingLine, EffortCertificationDocument effortCertificationDocument, EffortCertificationDetail detailLine) {
398 accountingLine.setSequenceNumber(accountingLineList.size() + 1);
399
400 this.populateAccountingLine(effortCertificationDocument, detailLine, accountingLine);
401 accountingLineList.add(accountingLine);
402 }
403
404
405
406
407
408
409
410
411 protected void populateAccountingLine(EffortCertificationDocument effortCertificationDocument, EffortCertificationDetail detailLine, LaborLedgerExpenseTransferAccountingLine accountingLine) {
412 accountingLine.setChartOfAccountsCode(detailLine.getChartOfAccountsCode());
413 accountingLine.setAccountNumber(detailLine.getAccountNumber());
414 accountingLine.setSubAccountNumber(detailLine.getSubAccountNumber());
415
416 accountingLine.setPostingYear(detailLine.getUniversityFiscalYear());
417 accountingLine.setFinancialObjectCode(detailLine.getFinancialObjectCode());
418 accountingLine.setBalanceTypeCode(KFSConstants.BALANCE_TYPE_ACTUAL);
419
420 accountingLine.setAmount(this.getDifference(detailLine).abs());
421
422 accountingLine.setFinancialSubObjectCode(null);
423 accountingLine.setProjectCode(null);
424 accountingLine.setOrganizationReferenceId(null);
425
426 accountingLine.setEmplid(effortCertificationDocument.getEmplid());
427 accountingLine.setPositionNumber(detailLine.getPositionNumber());
428 accountingLine.setPayrollTotalHours(BigDecimal.ZERO);
429
430 EffortCertificationReportDefinition reportDefinition = effortCertificationDocument.getEffortCertificationReportDefinition();
431 accountingLine.setPayrollEndDateFiscalYear(reportDefinition.getExpenseTransferFiscalYear());
432 accountingLine.setPayrollEndDateFiscalPeriodCode(reportDefinition.getExpenseTransferFiscalPeriodCode());
433
434 accountingLine.refreshNonUpdateableReferences();
435
436 AccountingLineOverride override = laborModuleService.determineNeededOverrides(null, accountingLine);
437
438
439 accountingLine.setAccountExpiredOverrideNeeded(override.hasComponent(COMPONENT.EXPIRED_ACCOUNT));
440 accountingLine.setAccountExpiredOverride(accountingLine.getAccountExpiredOverrideNeeded());
441
442
443 accountingLine.setObjectBudgetOverrideNeeded(override.hasComponent(COMPONENT.NON_BUDGETED_OBJECT));
444 accountingLine.setObjectBudgetOverride(accountingLine.isObjectBudgetOverrideNeeded());
445
446 if (accountingLine.getAccountExpiredOverrideNeeded() || accountingLine.isObjectBudgetOverrideNeeded()) {
447 accountingLine.setOverrideCode(override.getCode());
448 }
449
450 }
451
452
453
454
455
456
457
458 protected KualiDecimal getDifference(EffortCertificationDetail detailLine) {
459 return detailLine.getEffortCertificationOriginalPayrollAmount().subtract(detailLine.getEffortCertificationPayrollAmount());
460 }
461
462
463
464
465
466
467 public void setLaborModuleService(LaborModuleService laborModuleService) {
468 this.laborModuleService = laborModuleService;
469 }
470
471
472
473
474
475
476 public void setDocumentService(DocumentService documentService) {
477 this.documentService = documentService;
478 }
479
480
481
482
483
484
485 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
486 this.businessObjectService = businessObjectService;
487 }
488
489
490
491
492
493 public void setContractsAndGrantsModuleService(ContractsAndGrantsModuleService contractsAndGrantsModuleService) {
494 this.contractsAndGrantsModuleService = contractsAndGrantsModuleService;
495 }
496
497
498
499
500
501
502 public void setKualiModuleService(KualiModuleService kualiModuleService) {
503 this.kualiModuleService = kualiModuleService;
504 }
505 }