/*
* The Kuali Financial System, a comprehensive financial management system for higher education.
*
* Copyright 2005-2014 The Kuali Foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.kuali.kfs.module.bc.document.service.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.coa.businessobject.A21SubAccount;
import org.kuali.kfs.coa.businessobject.Account;
import org.kuali.kfs.coa.businessobject.SubAccount;
import org.kuali.kfs.coa.businessobject.SubFundGroup;
import org.kuali.kfs.coa.service.OrganizationService;
import org.kuali.kfs.fp.service.FiscalYearFunctionControlService;
import org.kuali.kfs.integration.ld.LaborLedgerBenefitsCalculation;
import org.kuali.kfs.integration.ld.LaborLedgerObject;
import org.kuali.kfs.module.bc.BCConstants;
import org.kuali.kfs.module.bc.BCKeyConstants;
import org.kuali.kfs.module.bc.BCPropertyConstants;
import org.kuali.kfs.module.bc.BCConstants.MonthSpreadDeleteType;
import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAccountOrganizationHierarchy;
import org.kuali.kfs.module.bc.businessobject.BudgetConstructionAccountReports;
import org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader;
import org.kuali.kfs.module.bc.businessobject.BudgetConstructionMonthly;
import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding;
import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger;
import org.kuali.kfs.module.bc.businessobject.SalarySettingExpansion;
import org.kuali.kfs.module.bc.document.BudgetConstructionDocument;
import org.kuali.kfs.module.bc.document.dataaccess.BudgetConstructionDao;
import org.kuali.kfs.module.bc.document.service.BenefitsCalculationService;
import org.kuali.kfs.module.bc.document.service.BudgetDocumentService;
import org.kuali.kfs.module.bc.document.service.BudgetParameterService;
import org.kuali.kfs.module.bc.document.validation.event.DeleteMonthlySpreadEvent;
import org.kuali.kfs.module.bc.document.validation.impl.BudgetConstructionRuleUtil;
import org.kuali.kfs.module.bc.document.web.struts.BudgetConstructionForm;
import org.kuali.kfs.module.bc.document.web.struts.MonthlyBudgetForm;
import org.kuali.kfs.module.bc.util.BudgetParameterFinder;
import org.kuali.kfs.sys.KFSConstants;
import org.kuali.kfs.sys.KFSParameterKeyConstants;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.service.NonTransactional;
import org.kuali.kfs.sys.service.OptionsService;
import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
import org.kuali.rice.core.api.util.type.KualiDecimal;
import org.kuali.rice.core.api.util.type.KualiInteger;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kew.api.document.WorkflowDocumentService;
import org.kuali.rice.kew.api.exception.WorkflowException;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kns.util.KNSGlobalVariables;
import org.kuali.rice.kns.util.MessageList;
import org.kuali.rice.krad.bo.AdHocRouteRecipient;
import org.kuali.rice.krad.dao.DocumentDao;
import org.kuali.rice.krad.document.Document;
import org.kuali.rice.krad.exception.ValidationException;
import org.kuali.rice.krad.rules.rule.event.KualiDocumentEvent;
import org.kuali.rice.krad.rules.rule.event.SaveDocumentEvent;
import org.kuali.rice.krad.service.BusinessObjectService;
import org.kuali.rice.krad.service.DocumentService;
import org.kuali.rice.krad.service.KualiModuleService;
import org.kuali.rice.krad.service.PersistenceService;
import org.kuali.rice.krad.service.SessionDocumentService;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.ObjectUtils;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.transaction.annotation.Transactional;
/**
* Implements the BudgetDocumentService interface. Methods here operate on objects associated with the Budget Construction document
* such as BudgetConstructionHeader
*/
public class BudgetDocumentServiceImpl implements BudgetDocumentService {
private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(BudgetDocumentServiceImpl.class);
protected BudgetConstructionDao budgetConstructionDao;
protected DocumentDao documentDao;
protected DocumentService documentService;
protected WorkflowDocumentService workflowDocumentService;
protected BenefitsCalculationService benefitsCalculationService;
protected BusinessObjectService businessObjectService;
protected KualiModuleService kualiModuleService;
protected ParameterService parameterService;
protected BudgetParameterService budgetParameterService;
protected FiscalYearFunctionControlService fiscalYearFunctionControlService;
protected OptionsService optionsService;
protected PersistenceService persistenceService;
protected OrganizationService organizationService;
protected String defaultLaborBenefitRateCategoryCode;
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getByCandidateKey(java.lang.String, java.lang.String,
* java.lang.String, java.lang.Integer)
*/
@Transactional
public BudgetConstructionHeader getByCandidateKey(String chartOfAccountsCode, String accountNumber, String subAccountNumber, Integer fiscalYear) {
return budgetConstructionDao.getByCandidateKey(chartOfAccountsCode, accountNumber, subAccountNumber, fiscalYear);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#saveDocument(org.kuali.rice.krad.document.Document)
* similar to DocumentService.saveDocument()
*/
@Transactional
public Document saveDocument(BudgetConstructionDocument budgetConstructionDocument) throws WorkflowException, ValidationException {
// user did explicit save here so mark as touched
budgetConstructionDocument.getFinancialSystemDocumentHeader().setFinancialDocumentStatusCode(KFSConstants.DocumentStatusCodes.ENROUTE);
this.saveDocumentNoWorkflow(budgetConstructionDocument);
SpringContext.getBean(SessionDocumentService.class).addDocumentToUserSession(GlobalVariables.getUserSession(), budgetConstructionDocument.getDocumentHeader().getWorkflowDocument());
// save any messages up to this point and put them back in after logDocumentAction()
// this is a hack to get around the problem where messageLists gets cleared
// that is PostProcessorServiceImpl.doActionTaken(ActionTakenEventDTO), establishGlobalVariables(), which does
// GlobalVariables.clear()
// not sure why this doesn't trash the GlobalVariables.getMessageMap()
MessageList messagesSoFar = KNSGlobalVariables.getMessageList();
budgetConstructionDocument.getDocumentHeader().getWorkflowDocument().logAnnotation("Document Updated");
// putting messages back in
KNSGlobalVariables.getMessageList().addAll(messagesSoFar);
return budgetConstructionDocument;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#saveDocumentNoWorkflow(org.kuali.rice.krad.document.Document)
*/
@Transactional
public Document saveDocumentNoWorkflow(BudgetConstructionDocument bcDoc) throws ValidationException {
return this.saveDocumentNoWorkFlow(bcDoc, MonthSpreadDeleteType.NONE, true);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#saveDocumentNoWorkFlow(org.kuali.kfs.module.bc.document.BudgetConstructionDocument,
* org.kuali.kfs.module.bc.BCConstants.MonthSpreadDeleteType, boolean)
*/
@Transactional
public Document saveDocumentNoWorkFlow(BudgetConstructionDocument bcDoc, MonthSpreadDeleteType monthSpreadDeleteType, boolean doMonthRICheck) throws ValidationException {
checkForNulls(bcDoc);
bcDoc.prepareForSave();
// validate and save the local objects not workflow objects
// this eventually calls BudgetConstructionRules.processSaveDocument() which overrides the method in DocumentRuleBase
if (doMonthRICheck) {
validateAndPersistDocument(bcDoc, new SaveDocumentEvent(bcDoc));
}
else {
validateAndPersistDocument(bcDoc, new DeleteMonthlySpreadEvent(bcDoc, monthSpreadDeleteType));
}
return bcDoc;
}
@Transactional
public void saveMonthlyBudget(MonthlyBudgetForm monthlyBudgetForm, BudgetConstructionMonthly budgetConstructionMonthly) {
BudgetConstructionForm budgetConstructionForm = (BudgetConstructionForm) GlobalVariables.getUserSession().retrieveObject(monthlyBudgetForm.getReturnFormKey());
BudgetConstructionDocument bcDoc = budgetConstructionForm.getBudgetConstructionDocument();
// handle any override situation
// getting here assumes that the line is not a salary detail line and that overrides are allowed
KualiInteger changeAmount = KualiInteger.ZERO;
KualiInteger monthTotalAmount = budgetConstructionMonthly.getFinancialDocumentMonthTotalLineAmount();
KualiInteger pbglRequestAmount = budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger().getAccountLineAnnualBalanceAmount();
if (!monthTotalAmount.equals(pbglRequestAmount)) {
changeAmount = monthTotalAmount.subtract(pbglRequestAmount);
// change the pbgl request amount store it and sync the object in session
budgetConstructionMonthly.refreshReferenceObject("pendingBudgetConstructionGeneralLedger");
PendingBudgetConstructionGeneralLedger sourceRow = (PendingBudgetConstructionGeneralLedger) businessObjectService.retrieve(budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger());
sourceRow.setAccountLineAnnualBalanceAmount(monthTotalAmount);
businessObjectService.save(sourceRow);
this.addOrUpdatePBGLRow(bcDoc, sourceRow, monthlyBudgetForm.isRevenue());
if (monthlyBudgetForm.isRevenue()){
bcDoc.setRevenueAccountLineAnnualBalanceAmountTotal(bcDoc.getRevenueAccountLineAnnualBalanceAmountTotal().add(changeAmount));
} else {
bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(changeAmount));
}
}
businessObjectService.save(budgetConstructionMonthly);
this.callForBenefitsCalcIfNeeded(bcDoc, budgetConstructionMonthly, changeAmount);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#callForBenefitsCalcIfNeeded(org.kuali.kfs.module.bc.document.BudgetConstructionDocument,
* org.kuali.kfs.module.bc.businessobject.BudgetConstructionMonthly, org.kuali.rice.core.api.util.type.KualiInteger)
*/
@Transactional
public void callForBenefitsCalcIfNeeded(BudgetConstructionDocument bcDoc, BudgetConstructionMonthly budgetConstructionMonthly, KualiInteger pbglChangeAmount) {
if (!benefitsCalculationService.isBenefitsCalculationDisabled()) {
if (budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger().getPositionObjectBenefit() != null && !budgetConstructionMonthly.getPendingBudgetConstructionGeneralLedger().getPositionObjectBenefit().isEmpty()) {
bcDoc.setMonthlyBenefitsCalcNeeded(true);
if (pbglChangeAmount.isNonZero()) {
bcDoc.setBenefitsCalcNeeded(true);
}
}
}
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateBenefitsIfNeeded(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@Transactional
public void calculateBenefitsIfNeeded(BudgetConstructionDocument bcDoc) {
if (bcDoc.isBenefitsCalcNeeded() || bcDoc.isMonthlyBenefitsCalcNeeded()) {
if (bcDoc.isBenefitsCalcNeeded()) {
this.calculateAnnualBenefits(bcDoc);
}
if (bcDoc.isMonthlyBenefitsCalcNeeded()) {
this.calculateMonthlyBenefits(bcDoc);
}
// reload from the DB and refresh refs
this.reloadBenefitsLines(bcDoc);
}
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateBenefits(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@Transactional
public void calculateBenefits(BudgetConstructionDocument bcDoc) {
this.calculateAnnualBenefits(bcDoc);
this.calculateMonthlyBenefits(bcDoc);
// reload from the DB and refresh refs
this.reloadBenefitsLines(bcDoc);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateAnnualBenefits(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@Transactional
protected void calculateAnnualBenefits(BudgetConstructionDocument bcDoc) {
// allow benefits calculation if document's account is not salary setting only lines
bcDoc.setBenefitsCalcNeeded(false);
if (!bcDoc.isSalarySettingOnly()) {
// pbgl lines are saved at this point, calc benefits
String sysParam = SpringContext.getBean(ParameterService.class).getParameterValueAsString(KfsParameterConstants.FINANCIAL_SYSTEM_ALL.class, KFSParameterKeyConstants.LdParameterConstants.ENABLE_FRINGE_BENEFIT_CALC_BY_BENEFIT_RATE_CATEGORY_IND);
LOG.debug("sysParam: " + sysParam);
// if sysParam == Y then Labor Benefit Rate Category Code must be used
if (sysParam.equalsIgnoreCase("Y")) {
benefitsCalculationService.calculateAnnualBudgetConstructionGeneralLedgerBenefits(bcDoc.getDocumentNumber(), bcDoc.getUniversityFiscalYear(), bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getSubAccountNumber(), bcDoc.getAccount().getLaborBenefitRateCategoryCode());
} else {
// rate category code off - call original benefits calc
benefitsCalculationService.calculateAnnualBudgetConstructionGeneralLedgerBenefits(bcDoc.getDocumentNumber(), bcDoc.getUniversityFiscalYear(), bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getSubAccountNumber());
}
// write global message on calc success
KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BENEFITS_CALCULATED);
}
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#calculateMonthlyBenefits(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@Transactional
protected void calculateMonthlyBenefits(BudgetConstructionDocument bcDoc) {
// allow benefits calculation if document's account is not salary setting only lines
bcDoc.setMonthlyBenefitsCalcNeeded(false);
if (!bcDoc.isSalarySettingOnly()) {
// pbgl lines are saved at this point, calc benefits
String sysParam = SpringContext.getBean(ParameterService.class).getParameterValueAsString(KfsParameterConstants.FINANCIAL_SYSTEM_ALL.class, KFSParameterKeyConstants.LdParameterConstants.ENABLE_FRINGE_BENEFIT_CALC_BY_BENEFIT_RATE_CATEGORY_IND);
LOG.debug("sysParam: " + sysParam);
// if sysParam == Y then Labor Benefit Rate Category Code must be used
if (sysParam.equalsIgnoreCase("Y")) {
benefitsCalculationService.calculateMonthlyBudgetConstructionGeneralLedgerBenefits(bcDoc.getDocumentNumber(), bcDoc.getUniversityFiscalYear(), bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getSubAccountNumber(), bcDoc.getAccount().getLaborBenefitRateCategoryCode());
} else {
// rate category code off - call original benefits calc
benefitsCalculationService.calculateMonthlyBudgetConstructionGeneralLedgerBenefits(bcDoc.getDocumentNumber(), bcDoc.getUniversityFiscalYear(), bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getSubAccountNumber());
}
// write global message on calc success
KNSGlobalVariables.getMessageList().add(BCKeyConstants.MESSAGE_BENEFITS_MONTHLY_CALCULATED);
}
}
/**
* Does sanity checks for null document object and null documentNumber
*
* @param document
*/
@NonTransactional
protected void checkForNulls(Document document) {
if (document == null) {
throw new IllegalArgumentException("invalid (null) document");
}
else if (document.getDocumentNumber() == null) {
throw new IllegalStateException("invalid (null) documentHeaderId");
}
}
/**
* Runs validation and persists a document to the database.
*
* @param document
* @param event
* @throws WorkflowException
* @throws ValidationException
*/
@Transactional
public void validateAndPersistDocument(Document document, KualiDocumentEvent event) throws ValidationException {
if (document == null) {
LOG.error("document passed to validateAndPersist was null");
throw new IllegalArgumentException("invalid (null) document");
}
LOG.info("validating and preparing to persist document " + document.getDocumentNumber());
// runs business rules event.validate() and creates rule instance and runs rule method recursively
document.validateBusinessRules(event);
// calls overriden method for specific document for anything that needs to happen before the save
// currently nothing for BC document
document.prepareForSave(event);
// save the document to the database
try {
LOG.info("storing document " + document.getDocumentNumber());
documentDao.save(document);
}
catch (OptimisticLockingFailureException e) {
LOG.error("exception encountered on store of document " + e.getMessage());
throw e;
}
document.postProcessSave(event);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#validateDocument(org.kuali.rice.krad.document.Document)
*/
@Transactional
public void validateDocument(Document document) throws ValidationException {
if (document == null) {
LOG.error("document passed to validateDocument was null");
throw new IllegalArgumentException("invalid (null) document");
}
LOG.info("validating document " + document.getDocumentNumber());
document.validateBusinessRules(new SaveDocumentEvent(document));
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getPBGLSalarySettingRows(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@Transactional
public List<PendingBudgetConstructionGeneralLedger> getPBGLSalarySettingRows(BudgetConstructionDocument bcDocument) {
List<String> ssObjects = getDetailSalarySettingLaborObjects(bcDocument.getUniversityFiscalYear(), bcDocument.getChartOfAccountsCode());
ssObjects.add(KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG);
List<PendingBudgetConstructionGeneralLedger> pbglSalarySettingRows = budgetConstructionDao.getPBGLSalarySettingRows(bcDocument.getDocumentNumber(), ssObjects);
return pbglSalarySettingRows;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getDetailSalarySettingLaborObjects(java.lang.Integer, java.lang.String)
*/
@NonTransactional
public List<String> getDetailSalarySettingLaborObjects(Integer universityFiscalYear, String chartOfAccountsCode) {
List<String> detailSalarySettingObjects = new ArrayList<String>();
Map<String, Object> laborObjectCodeMap = new HashMap<String, Object>();
laborObjectCodeMap.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, universityFiscalYear);
laborObjectCodeMap.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, chartOfAccountsCode);
laborObjectCodeMap.put(KFSPropertyConstants.DETAIL_POSITION_REQUIRED_INDICATOR, true);
List<LaborLedgerObject> laborLedgerObjects = kualiModuleService.getResponsibleModuleService(LaborLedgerObject.class).getExternalizableBusinessObjectsList(LaborLedgerObject.class, laborObjectCodeMap);
for (LaborLedgerObject laborObject : laborLedgerObjects) {
detailSalarySettingObjects.add(laborObject.getFinancialObjectCode());
}
return detailSalarySettingObjects;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#addOrUpdatePBGLRow(org.kuali.kfs.module.bc.document.BudgetConstructionDocument,
* org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger)
*/
@NonTransactional
public BudgetConstructionDocument addOrUpdatePBGLRow(BudgetConstructionDocument bcDoc, PendingBudgetConstructionGeneralLedger sourceRow, boolean isRevenue) {
List<PendingBudgetConstructionGeneralLedger> pbglRows;
if (isRevenue){
pbglRows = bcDoc.getPendingBudgetConstructionGeneralLedgerRevenueLines();
} else {
pbglRows = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines();
}
// add or update salary setting row to set in memory - this assumes at least one row in the set
// we can't even do salary setting without at least one salary detail row
int index = 0;
boolean insertNeeded = true;
for (PendingBudgetConstructionGeneralLedger pbglRow : pbglRows) {
String pbglRowKey = pbglRow.getFinancialObjectCode() + pbglRow.getFinancialSubObjectCode();
String sourceRowKey = sourceRow.getFinancialObjectCode() + sourceRow.getFinancialSubObjectCode();
if (pbglRowKey.compareToIgnoreCase(sourceRowKey) == 0) {
// update
insertNeeded = false;
pbglRow.setAccountLineAnnualBalanceAmount(sourceRow.getAccountLineAnnualBalanceAmount());
pbglRow.setPersistedAccountLineAnnualBalanceAmount(sourceRow.getAccountLineAnnualBalanceAmount());
pbglRow.setVersionNumber(sourceRow.getVersionNumber());
break;
}
else {
if (pbglRowKey.compareToIgnoreCase(sourceRowKey) > 0) {
// insert here - drop out
break;
}
}
index++;
}
if (insertNeeded) {
// insert the row
sourceRow.setPersistedAccountLineAnnualBalanceAmount(sourceRow.getAccountLineAnnualBalanceAmount());
pbglRows.add(index, sourceRow);
}
return bcDoc;
}
/**
* Reloads benefits target accounting lines. Usually called right after an annual benefits calculation and the display needs
* updated with a fresh copy from the database. All old row versions are removed and database row versions are inserted in the
* list in the correct order.
*
* @param bcDoc
*/
@Transactional
protected void reloadBenefitsLines(BudgetConstructionDocument bcDoc) {
// get list of potential fringe objects to use as an in query param
Map<String, Object> fieldValues = new HashMap<String, Object>();
fieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, bcDoc.getUniversityFiscalYear());
fieldValues.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, bcDoc.getChartOfAccountsCode());
List<LaborLedgerBenefitsCalculation> benefitsCalculation = kualiModuleService.getResponsibleModuleService(LaborLedgerBenefitsCalculation.class).getExternalizableBusinessObjectsList(LaborLedgerBenefitsCalculation.class, fieldValues);
List<String> fringeObjects = new ArrayList<String>();
for (LaborLedgerBenefitsCalculation element : benefitsCalculation) {
fringeObjects.add(element.getPositionFringeBenefitObjectCode());
}
List<PendingBudgetConstructionGeneralLedger> dbPBGLFringeLines = budgetConstructionDao.getDocumentPBGLFringeLines(bcDoc.getDocumentNumber(), fringeObjects);
List<PendingBudgetConstructionGeneralLedger> docPBGLExpLines = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines();
// holds the request sums of removed, added records and used to adjust the document expenditure request total
KualiInteger docRequestTotals = KualiInteger.ZERO;
KualiInteger dbRequestTotals = KualiInteger.ZERO;
// remove the current set of fringe lines
ListIterator docLines = docPBGLExpLines.listIterator();
while (docLines.hasNext()) {
PendingBudgetConstructionGeneralLedger docLine = (PendingBudgetConstructionGeneralLedger) docLines.next();
if (fringeObjects.contains(docLine.getFinancialObjectCode())) {
docRequestTotals = docRequestTotals.add(docLine.getAccountLineAnnualBalanceAmount());
docLines.remove();
}
}
// add the dbset of fringe lines, if any
if (dbPBGLFringeLines != null && !dbPBGLFringeLines.isEmpty()) {
if (docPBGLExpLines == null || docPBGLExpLines.isEmpty()) {
docPBGLExpLines.addAll(dbPBGLFringeLines);
}
else {
ListIterator dbLines = dbPBGLFringeLines.listIterator();
docLines = docPBGLExpLines.listIterator();
PendingBudgetConstructionGeneralLedger dbLine = (PendingBudgetConstructionGeneralLedger) dbLines.next();
PendingBudgetConstructionGeneralLedger docLine = (PendingBudgetConstructionGeneralLedger) docLines.next();
boolean dbDone = false;
boolean docDone = false;
while (!dbDone) {
if (docDone || docLine.getFinancialObjectCode().compareToIgnoreCase(dbLine.getFinancialObjectCode()) > 0) {
if (!docDone) {
docLine = (PendingBudgetConstructionGeneralLedger) docLines.previous();
}
dbRequestTotals = dbRequestTotals.add(dbLine.getAccountLineAnnualBalanceAmount());
dbLine.setPersistedAccountLineAnnualBalanceAmount(dbLine.getAccountLineAnnualBalanceAmount());
this.populatePBGLLine(dbLine);
docLines.add(dbLine);
if (!docDone) {
docLine = (PendingBudgetConstructionGeneralLedger) docLines.next();
}
if (dbLines.hasNext()) {
dbLine = (PendingBudgetConstructionGeneralLedger) dbLines.next();
}
else {
dbDone = true;
}
}
else {
if (docLines.hasNext()) {
docLine = (PendingBudgetConstructionGeneralLedger) docLines.next();
}
else {
docDone = true;
}
}
}
}
}
// adjust the request total for the removed and added recs
bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(dbRequestTotals.subtract(docRequestTotals)));
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#populatePBGLLine(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger)
*/
@Transactional
public void populatePBGLLine(PendingBudgetConstructionGeneralLedger line) {
final List REFRESH_FIELDS;
if (StringUtils.isNotBlank(line.getFinancialSubObjectCode())) {
REFRESH_FIELDS = Collections.unmodifiableList(Arrays.asList(new String[] { KFSPropertyConstants.FINANCIAL_OBJECT, KFSPropertyConstants.FINANCIAL_SUB_OBJECT, BCPropertyConstants.BUDGET_CONSTRUCTION_MONTHLY }));
}
else {
REFRESH_FIELDS = Collections.unmodifiableList(Arrays.asList(new String[] { KFSPropertyConstants.FINANCIAL_OBJECT, BCPropertyConstants.BUDGET_CONSTRUCTION_MONTHLY }));
}
persistenceService.retrieveReferenceObjects(line, REFRESH_FIELDS);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getPendingBudgetConstructionAppointmentFundingRequestSum(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionGeneralLedger)
*/
@Transactional
public KualiInteger getPendingBudgetConstructionAppointmentFundingRequestSum(PendingBudgetConstructionGeneralLedger salaryDetailLine) {
return budgetConstructionDao.getPendingBudgetConstructionAppointmentFundingRequestSum(salaryDetailLine);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableDocument(org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader)
*/
@NonTransactional
public boolean isBudgetableDocument(BudgetConstructionHeader bcHeader) {
if (bcHeader == null) {
return false;
}
Integer budgetYear = bcHeader.getUniversityFiscalYear();
Account account = bcHeader.getAccount();
boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, true);
if (isBudgetableAccount) {
SubAccount subAccount = bcHeader.getSubAccount();
String subAccountNumber = bcHeader.getSubAccountNumber();
return this.isBudgetableSubAccount(subAccount, subAccountNumber);
}
return false;
}
@NonTransactional
public boolean isBudgetableDocumentNoWagesCheck(BudgetConstructionHeader bcHeader) {
if (bcHeader == null) {
return false;
}
Integer budgetYear = bcHeader.getUniversityFiscalYear();
Account account = bcHeader.getAccount();
boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, false);
if (isBudgetableAccount) {
SubAccount subAccount = bcHeader.getSubAccount();
String subAccountNumber = bcHeader.getSubAccountNumber();
return this.isBudgetableSubAccount(subAccount, subAccountNumber);
}
return false;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableDocument(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@NonTransactional
public boolean isBudgetableDocument(BudgetConstructionDocument document) {
if (document == null) {
return false;
}
Integer budgetYear = document.getUniversityFiscalYear();
Account account = document.getAccount();
boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, true);
if (isBudgetableAccount) {
SubAccount subAccount = document.getSubAccount();
String subAccountNumber = document.getSubAccountNumber();
return this.isBudgetableSubAccount(subAccount, subAccountNumber);
}
return false;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableDocumentNoWagesCheck(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@NonTransactional
public boolean isBudgetableDocumentNoWagesCheck(BudgetConstructionDocument document) {
if (document == null) {
return false;
}
Integer budgetYear = document.getUniversityFiscalYear();
Account account = document.getAccount();
boolean isBudgetableAccount = this.isBudgetableAccount(budgetYear, account, false);
if (isBudgetableAccount) {
SubAccount subAccount = document.getSubAccount();
String subAccountNumber = document.getSubAccountNumber();
return this.isBudgetableSubAccount(subAccount, subAccountNumber);
}
return false;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isAssociatedWithBudgetableDocument(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
*/
@NonTransactional
public boolean isAssociatedWithBudgetableDocument(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
BudgetConstructionHeader bcHeader = this.getBudgetConstructionHeader(appointmentFunding);
return this.isBudgetableDocument(bcHeader);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableAccount(java.lang.Integer,
* org.kuali.kfs.coa.businessobject.Account)
*/
@NonTransactional
public boolean isBudgetableAccount(Integer budgetYear, Account account, boolean isWagesCheck) {
if (budgetYear == null || account == null) {
return false;
}
// account cannot be closed.
if (!account.isActive()) {
return false;
}
// account cannot be expired before beginning of 6th accounting period, 2 years before budget construction fiscal year.
Calendar expDate = BudgetConstructionRuleUtil.getNoBudgetAllowedExpireDate(budgetYear);
if (account.isExpired(expDate)) {
return false;
}
// account cannot be a cash control account
if (StringUtils.equals(account.getBudgetRecordingLevelCode(), BCConstants.BUDGET_RECORDING_LEVEL_N)) {
return false;
}
// this check is needed for salary setting
if (isWagesCheck) {
// account must be flagged as wages allowed
SubFundGroup subFundGroup = account.getSubFundGroup();
if (subFundGroup == null || !subFundGroup.isSubFundGroupWagesIndicator()) {
return false;
}
}
return true;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isBudgetableSubAccount(org.kuali.kfs.coa.businessobject.SubAccount,
* java.lang.String)
*/
@NonTransactional
public boolean isBudgetableSubAccount(SubAccount subAccount, String subAccountNumber) {
if (StringUtils.isNotEmpty(subAccountNumber) && StringUtils.equals(subAccountNumber, KFSConstants.getDashSubAccountNumber())) {
return true;
}
// sub account must exist and be active.
if (ObjectUtils.isNull(subAccount) || !subAccount.isActive()) {
return false;
}
// sub account must not be flagged cost share
A21SubAccount a21SubAccount = subAccount.getA21SubAccount();
if (ObjectUtils.isNotNull(a21SubAccount) && StringUtils.equals(a21SubAccount.getSubAccountTypeCode(), KFSConstants.SubAccountType.COST_SHARE)) {
return false;
}
return true;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#isAccountReportsExist(java.lang.String, java.lang.String)
*/
@Transactional
public boolean isAccountReportsExist(String chartOfAccountsCode, String accountNumber) {
BudgetConstructionAccountReports accountReports = (BudgetConstructionAccountReports) budgetConstructionDao.getAccountReports(chartOfAccountsCode, accountNumber);
if (accountReports == null) {
return false;
}
else {
return true;
}
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#updatePendingBudgetGeneralLedger(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
* org.kuali.rice.core.api.util.type.KualiInteger)
*/
@Transactional
public void updatePendingBudgetGeneralLedger(PendingBudgetConstructionAppointmentFunding appointmentFunding, KualiInteger updateAmount) {
BudgetConstructionHeader budgetConstructionHeader = this.getBudgetConstructionHeader(appointmentFunding);
if (budgetConstructionHeader == null) {
return;
}
PendingBudgetConstructionGeneralLedger pendingRecord = this.getPendingBudgetConstructionGeneralLedger(budgetConstructionHeader, appointmentFunding, updateAmount, false);
businessObjectService.save(pendingRecord);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#updatePendingBudgetGeneralLedgerPlug(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
* org.kuali.rice.core.api.util.type.KualiInteger)
*/
@Transactional
public void updatePendingBudgetGeneralLedgerPlug(PendingBudgetConstructionAppointmentFunding appointmentFunding, KualiInteger updateAmount) {
if (updateAmount == null) {
throw new IllegalArgumentException("The update amount cannot be null");
}
BudgetConstructionHeader budgetConstructionHeader = this.getBudgetConstructionHeader(appointmentFunding);
if (budgetConstructionHeader == null) {
return;
}
if (this.canUpdatePlugRecord(appointmentFunding)) {
PendingBudgetConstructionGeneralLedger plugRecord = this.getPendingBudgetConstructionGeneralLedger(budgetConstructionHeader, appointmentFunding, updateAmount, true);
KualiInteger annualBalanceAmount = plugRecord.getAccountLineAnnualBalanceAmount();
KualiInteger beginningBalanceAmount = plugRecord.getFinancialBeginningBalanceLineAmount();
if ((annualBalanceAmount == null || annualBalanceAmount.isZero()) && (beginningBalanceAmount == null || beginningBalanceAmount.isZero())) {
businessObjectService.delete(plugRecord);
}
else {
businessObjectService.save(plugRecord);
}
}
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#updatePendingBudgetGeneralLedgerPlug(org.kuali.kfs.module.bc.document.BudgetConstructionDocument,
* org.kuali.rice.core.api.util.type.KualiInteger)
*/
@Transactional
public PendingBudgetConstructionGeneralLedger updatePendingBudgetGeneralLedgerPlug(BudgetConstructionDocument bcDoc, KualiInteger updateAmount) {
String twoPlugKey = KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG + KFSConstants.getDashFinancialSubObjectCode();
List<PendingBudgetConstructionGeneralLedger> expenditureRows = bcDoc.getPendingBudgetConstructionGeneralLedgerExpenditureLines();
PendingBudgetConstructionGeneralLedger twoPlugRow = null;
// update or insert the 2plg row - this assumes at least one row in the set
// we can't even do salary setting without at least one detail line
int index = 0;
boolean insertNeeded = true;
for (PendingBudgetConstructionGeneralLedger expRow : expenditureRows) {
String expRowKey = expRow.getFinancialObjectCode() + expRow.getFinancialSubObjectCode();
if (expRowKey.compareToIgnoreCase(twoPlugKey) == 0) {
// update the existing row
insertNeeded = false;
expRow.setAccountLineAnnualBalanceAmount(expRow.getAccountLineAnnualBalanceAmount().add(updateAmount.negated()));
expRow.setPersistedAccountLineAnnualBalanceAmount(expRow.getAccountLineAnnualBalanceAmount());
businessObjectService.save(expRow);
expRow.refresh();
twoPlugRow = expRow;
break;
}
else {
if (expRowKey.compareToIgnoreCase(twoPlugKey) > 0) {
// case where offsetting salary setting updates under different object codes - insert a new row here
break;
}
}
index++;
}
if (insertNeeded) {
// do insert in the middle or at end of list
String objectCode = KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG;
String subObjectCode = KFSConstants.getDashFinancialSubObjectCode();
String objectTypeCode = optionsService.getOptions(bcDoc.getUniversityFiscalYear()).getFinObjTypeExpenditureexpCd();
PendingBudgetConstructionGeneralLedger pendingRecord = new PendingBudgetConstructionGeneralLedger();
pendingRecord.setDocumentNumber(bcDoc.getDocumentNumber());
pendingRecord.setUniversityFiscalYear(bcDoc.getUniversityFiscalYear());
pendingRecord.setChartOfAccountsCode(bcDoc.getChartOfAccountsCode());
pendingRecord.setAccountNumber(bcDoc.getAccountNumber());
pendingRecord.setSubAccountNumber(bcDoc.getSubAccountNumber());
pendingRecord.setFinancialObjectCode(objectCode);
pendingRecord.setFinancialSubObjectCode(subObjectCode);
pendingRecord.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_BASE_BUDGET);
pendingRecord.setFinancialObjectTypeCode(objectTypeCode);
pendingRecord.setFinancialBeginningBalanceLineAmount(KualiInteger.ZERO);
pendingRecord.setAccountLineAnnualBalanceAmount(updateAmount);
// store and add to memory set
pendingRecord.setPersistedAccountLineAnnualBalanceAmount(pendingRecord.getAccountLineAnnualBalanceAmount());
businessObjectService.save(pendingRecord);
expenditureRows.add(index, pendingRecord);
twoPlugRow = pendingRecord;
bcDoc.setContainsTwoPlug(true);
}
bcDoc.setExpenditureAccountLineAnnualBalanceAmountTotal(bcDoc.getExpenditureAccountLineAnnualBalanceAmountTotal().add(updateAmount.negated()));
return twoPlugRow;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getBudgetConstructionHeader(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
*/
@NonTransactional
public BudgetConstructionHeader getBudgetConstructionHeader(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
String chartOfAccountsCode = appointmentFunding.getChartOfAccountsCode();
String accountNumber = appointmentFunding.getAccountNumber();
String subAccountNumber = appointmentFunding.getSubAccountNumber();
Integer fiscalYear = appointmentFunding.getUniversityFiscalYear();
return this.getByCandidateKey(chartOfAccountsCode, accountNumber, subAccountNumber, fiscalYear);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getBudgetConstructionDocument(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding)
*/
@NonTransactional
public BudgetConstructionDocument getBudgetConstructionDocument(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
Map<String, Object> fieldValues = new HashMap<String, Object>();
fieldValues.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, appointmentFunding.getUniversityFiscalYear());
fieldValues.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, appointmentFunding.getChartOfAccountsCode());
fieldValues.put(KFSPropertyConstants.ACCOUNT_NUMBER, appointmentFunding.getAccountNumber());
fieldValues.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, appointmentFunding.getSubAccountNumber());
// fiscalyear, chart, account, subaccount is a candidate key for BC document
// This should not need the special handling and just return the first (only document) in the collection
Collection<BudgetConstructionDocument> documents = businessObjectService.findMatching(BudgetConstructionDocument.class, fieldValues);
for (BudgetConstructionDocument document : documents) {
try {
return (BudgetConstructionDocument) documentService.getByDocumentHeaderId(document.getDocumentHeader().getDocumentNumber());
}
catch (WorkflowException e) {
throw new RuntimeException("Fail to retrieve the document for appointment funding" + appointmentFunding, e);
}
}
return null;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getBudgetConstructionDocument(org.kuali.kfs.module.bc.businessobject.SalarySettingExpansion)
*/
@NonTransactional
public BudgetConstructionDocument getBudgetConstructionDocument(SalarySettingExpansion salarySettingExpansion) {
try {
return (BudgetConstructionDocument) documentService.getByDocumentHeaderId(salarySettingExpansion.getDocumentNumber());
}
catch (WorkflowException e) {
throw new RuntimeException("Fail to retrieve the document for salary expansion" + salarySettingExpansion, e);
}
}
/**
* determine whether the plug line can be updated or created. If the given appointment funding is in the plug override mode or
* it associates with a contract and grant account, then no plug can be updated or created
*
* @param appointmentFunding the given appointment funding
* @return true if the plug line can be updated or created; otherwise, false
*/
@Transactional
protected boolean canUpdatePlugRecord(PendingBudgetConstructionAppointmentFunding appointmentFunding) {
// no plug if the override mode is enabled
if (appointmentFunding.isOverride2PlugMode()) {
return false;
}
Account account = appointmentFunding.getAccount();
// no plug for the account with the sub groups setup as a system parameter
if (BudgetParameterFinder.getNotGenerate2PlgSubFundGroupCodes().contains(account.getSubFundGroupCode())) {
return false;
}
// no plug for the contract and grant account
if (account.isForContractsAndGrants()) {
return false;
}
return true;
}
/**
* get a pending budget construction GL record, and set its to the given update amount if it exists in database; otherwise,
* create it with the given information
*
* @param budgetConstructionHeader the budget construction header of the pending budget construction GL record
* @param appointmentFunding the appointment funding associated with the pending budget construction GL record
* @param updateAmount the amount being used to update the retrieved pending budget construction GL record
* @param is2PLG the flag used to instrcut to retrieve a pending budget construction GL plug record
* @return a pending budget construction GL record if any; otherwise, create one with the given information
*/
@Transactional
protected PendingBudgetConstructionGeneralLedger getPendingBudgetConstructionGeneralLedger(BudgetConstructionHeader budgetConstructionHeader, PendingBudgetConstructionAppointmentFunding appointmentFunding, KualiInteger updateAmount, boolean is2PLG) {
if (budgetConstructionHeader == null) {
throw new IllegalArgumentException("The given budget construction document header cannot be null");
}
if (appointmentFunding == null) {
throw new IllegalArgumentException("The given pending budget appointment funding cannot be null");
}
if (updateAmount == null) {
throw new IllegalArgumentException("The update amount cannot be null");
}
PendingBudgetConstructionGeneralLedger pendingRecord = this.retrievePendingBudgetConstructionGeneralLedger(budgetConstructionHeader, appointmentFunding, is2PLG);
if (pendingRecord != null) {
KualiInteger newAnnaulBalanceAmount = pendingRecord.getAccountLineAnnualBalanceAmount().add(updateAmount);
pendingRecord.setAccountLineAnnualBalanceAmount(newAnnaulBalanceAmount);
}
else if (!is2PLG || (is2PLG && updateAmount.isNonZero())) {
// initialize a new pending record if not plug line or plug line not zero
Integer budgetYear = appointmentFunding.getUniversityFiscalYear();
String objectCode = is2PLG ? KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG : appointmentFunding.getFinancialObjectCode();
String subObjectCode = is2PLG ? KFSConstants.getDashFinancialSubObjectCode() : appointmentFunding.getFinancialSubObjectCode();
String objectTypeCode = optionsService.getOptions(budgetYear).getFinObjTypeExpenditureexpCd();
pendingRecord = new PendingBudgetConstructionGeneralLedger();
pendingRecord.setDocumentNumber(budgetConstructionHeader.getDocumentNumber());
pendingRecord.setUniversityFiscalYear(appointmentFunding.getUniversityFiscalYear());
pendingRecord.setChartOfAccountsCode(appointmentFunding.getChartOfAccountsCode());
pendingRecord.setAccountNumber(appointmentFunding.getAccountNumber());
pendingRecord.setSubAccountNumber(appointmentFunding.getSubAccountNumber());
pendingRecord.setFinancialObjectCode(objectCode);
pendingRecord.setFinancialSubObjectCode(subObjectCode);
pendingRecord.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_BASE_BUDGET);
pendingRecord.setFinancialObjectTypeCode(objectTypeCode);
pendingRecord.setFinancialBeginningBalanceLineAmount(KualiInteger.ZERO);
pendingRecord.setAccountLineAnnualBalanceAmount(updateAmount);
}
return pendingRecord;
}
/**
* retrieve a pending budget construction GL record based on the given infromation
*
* @param budgetConstructionHeader the budget construction header of the pending budget construction GL record to be retrieved
* @param appointmentFunding the appointment funding associated with the pending budget construction GL record to be retrieved
* @param is2PLG the flag used to instrcut to retrieve a pending budget construction GL plug record
* @return a pending budget construction GL record if any; otherwise, null
*/
@NonTransactional
protected PendingBudgetConstructionGeneralLedger retrievePendingBudgetConstructionGeneralLedger(BudgetConstructionHeader budgetConstructionHeader, PendingBudgetConstructionAppointmentFunding appointmentFunding, boolean is2PLG) {
String objectCode = is2PLG ? KFSConstants.BudgetConstructionConstants.OBJECT_CODE_2PLG : appointmentFunding.getFinancialObjectCode();
String subObjectCode = is2PLG ? KFSConstants.getDashFinancialSubObjectCode() : appointmentFunding.getFinancialSubObjectCode();
Map<String, Object> searchCriteria = new HashMap<String, Object>();
searchCriteria.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionHeader.getDocumentNumber());
searchCriteria.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, budgetConstructionHeader.getUniversityFiscalYear());
searchCriteria.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, budgetConstructionHeader.getChartOfAccountsCode());
searchCriteria.put(KFSPropertyConstants.ACCOUNT_NUMBER, budgetConstructionHeader.getAccountNumber());
searchCriteria.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, budgetConstructionHeader.getSubAccountNumber());
searchCriteria.put(KFSPropertyConstants.FINANCIAL_BALANCE_TYPE_CODE, KFSConstants.BALANCE_TYPE_BASE_BUDGET);
searchCriteria.put(KFSPropertyConstants.FINANCIAL_OBJECT_TYPE_CODE, optionsService.getOptions(appointmentFunding.getUniversityFiscalYear()).getFinObjTypeExpenditureexpCd());
searchCriteria.put(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, objectCode);
searchCriteria.put(KFSPropertyConstants.FINANCIAL_SUB_OBJECT_CODE, subObjectCode);
return (PendingBudgetConstructionGeneralLedger) businessObjectService.findByPrimaryKey(PendingBudgetConstructionGeneralLedger.class, searchCriteria);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#retrievePendingBudgetConstructionGeneralLedger(org.kuali.kfs.module.bc.businessobject.BudgetConstructionHeader)
*/
@NonTransactional
public List<PendingBudgetConstructionGeneralLedger> retrievePendingBudgetConstructionGeneralLedger(BudgetConstructionHeader budgetConstructionHeader) {
Map<String, Object> searchCriteria = new HashMap<String, Object>();
searchCriteria.put(KFSPropertyConstants.DOCUMENT_NUMBER, budgetConstructionHeader.getDocumentNumber());
searchCriteria.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, budgetConstructionHeader.getUniversityFiscalYear());
searchCriteria.put(KFSPropertyConstants.CHART_OF_ACCOUNTS_CODE, budgetConstructionHeader.getChartOfAccountsCode());
searchCriteria.put(KFSPropertyConstants.ACCOUNT_NUMBER, budgetConstructionHeader.getAccountNumber());
searchCriteria.put(KFSPropertyConstants.SUB_ACCOUNT_NUMBER, budgetConstructionHeader.getSubAccountNumber());
return (List<PendingBudgetConstructionGeneralLedger>) businessObjectService.findMatching(PendingBudgetConstructionGeneralLedger.class, searchCriteria);
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#retrieveOrBuildAccountOrganizationHierarchy(java.lang.Integer,
* java.lang.String, java.lang.String)
*/
@Transactional
public List<BudgetConstructionAccountOrganizationHierarchy> retrieveOrBuildAccountOrganizationHierarchy(Integer universityFiscalYear, String chartOfAccountsCode, String accountNumber) {
List<BudgetConstructionAccountOrganizationHierarchy> accountOrgHier = new ArrayList<BudgetConstructionAccountOrganizationHierarchy>();
BudgetConstructionAccountReports accountReports = (BudgetConstructionAccountReports) budgetConstructionDao.getAccountReports(chartOfAccountsCode, accountNumber);
if (accountReports != null) {
accountOrgHier = budgetConstructionDao.getAccountOrgHierForAccount(chartOfAccountsCode, accountNumber, universityFiscalYear);
if (accountOrgHier == null || accountOrgHier.isEmpty()) {
// attempt to build it
String[] rootNode = organizationService.getRootOrganizationCode();
String rootChart = rootNode[0];
String rootOrganization = rootNode[1];
Integer currentLevel = new Integer(1);
String organizationChartOfAccountsCode = accountReports.getReportsToChartOfAccountsCode();
String organizationCode = accountReports.getReportsToOrganizationCode();
boolean overFlow = budgetConstructionDao.insertAccountIntoAccountOrganizationHierarchy(rootChart, rootOrganization, universityFiscalYear, chartOfAccountsCode, accountNumber, currentLevel, organizationChartOfAccountsCode, organizationCode);
if (!overFlow) {
accountOrgHier = budgetConstructionDao.getAccountOrgHierForAccount(chartOfAccountsCode, accountNumber, universityFiscalYear);
}
}
}
return accountOrgHier;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#instantiateNewBudgetConstructionDocument(org.kuali.kfs.module.bc.document.BudgetConstructionDocument)
*/
@Transactional
public BudgetConstructionDocument instantiateNewBudgetConstructionDocument(BudgetConstructionDocument budgetConstructionDocument) throws WorkflowException {
budgetConstructionDocument.setOrganizationLevelChartOfAccountsCode(BCConstants.INITIAL_ORGANIZATION_LEVEL_CHART_OF_ACCOUNTS_CODE);
budgetConstructionDocument.setOrganizationLevelOrganizationCode(BCConstants.INITIAL_ORGANIZATION_LEVEL_ORGANIZATION_CODE);
budgetConstructionDocument.setOrganizationLevelCode(BCConstants.INITIAL_ORGANIZATION_LEVEL_CODE);
budgetConstructionDocument.setBudgetTransactionLockUserIdentifier(BCConstants.DEFAULT_BUDGET_HEADER_LOCK_IDS);
budgetConstructionDocument.setBudgetLockUserIdentifier(BCConstants.DEFAULT_BUDGET_HEADER_LOCK_IDS);
FinancialSystemDocumentHeader kualiDocumentHeader = budgetConstructionDocument.getFinancialSystemDocumentHeader();
budgetConstructionDocument.setDocumentNumber(budgetConstructionDocument.getDocumentHeader().getDocumentNumber());
kualiDocumentHeader.setOrganizationDocumentNumber(budgetConstructionDocument.getUniversityFiscalYear().toString());
kualiDocumentHeader.setFinancialDocumentStatusCode(KFSConstants.INITIAL_KUALI_DOCUMENT_STATUS_CD);
kualiDocumentHeader.setFinancialDocumentTotalAmount(KualiDecimal.ZERO);
kualiDocumentHeader.setDocumentDescription(String.format("%s %d %s %s", BCConstants.BUDGET_CONSTRUCTION_DOCUMENT_DESCRIPTION, budgetConstructionDocument.getUniversityFiscalYear(), budgetConstructionDocument.getChartOfAccountsCode(), budgetConstructionDocument.getAccountNumber()));
kualiDocumentHeader.setExplanation(BCConstants.BUDGET_CONSTRUCTION_DOCUMENT_DESCRIPTION);
List<AdHocRouteRecipient> emptyAdHocList = new ArrayList<AdHocRouteRecipient>();
// call route with document type configured for no route paths or post processor
documentService.routeDocument(budgetConstructionDocument, "created by application UI", emptyAdHocList);
return budgetConstructionDocument;
}
/**
* @see org.kuali.kfs.module.bc.document.service.BudgetDocumentService#getPushPullLevelList(org.kuali.kfs.module.bc.document.BudgetConstructionDocument,
* org.kuali.rice.kim.api.identity.Person)
*/
@Transactional
public List<BudgetConstructionAccountOrganizationHierarchy> getPushPullLevelList(BudgetConstructionDocument bcDoc, Person person) {
List<BudgetConstructionAccountOrganizationHierarchy> pushOrPullList = new ArrayList<BudgetConstructionAccountOrganizationHierarchy>();
pushOrPullList.addAll(budgetConstructionDao.getAccountOrgHierForAccount(bcDoc.getChartOfAccountsCode(), bcDoc.getAccountNumber(), bcDoc.getUniversityFiscalYear()));
if (pushOrPullList.size() >= 1) {
BudgetConstructionAccountOrganizationHierarchy levelZero = new BudgetConstructionAccountOrganizationHierarchy();
levelZero.setUniversityFiscalYear(bcDoc.getUniversityFiscalYear());
levelZero.setChartOfAccountsCode(bcDoc.getChartOfAccountsCode());
levelZero.setAccountNumber(bcDoc.getAccountNumber());
levelZero.setOrganizationLevelCode(0);
levelZero.setOrganizationChartOfAccountsCode(pushOrPullList.get(0).getOrganizationChartOfAccountsCode());
levelZero.setOrganizationCode(pushOrPullList.get(0).getOrganizationCode());
pushOrPullList.add(0, levelZero);
}
return pushOrPullList;
}
/**
* Sets the budgetConstructionDao attribute value.
*
* @param budgetConstructionDao The budgetConstructionDao to set.
*/
@NonTransactional
public void setBudgetConstructionDao(BudgetConstructionDao budgetConstructionDao) {
this.budgetConstructionDao = budgetConstructionDao;
}
/**
* Sets the documentService attribute value.
*
* @param documentService The documentService to set.
*/
@NonTransactional
public void setDocumentService(DocumentService documentService) {
this.documentService = documentService;
}
/**
* Sets the workflowDocumentService attribute value.
*
* @param workflowDocumentService The workflowDocumentService to set.
*/
@NonTransactional
public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
this.workflowDocumentService = workflowDocumentService;
}
/**
* Sets the documentDao attribute value.
*
* @param documentDao The documentDao to set.
*/
@NonTransactional
public void setDocumentDao(DocumentDao documentDao) {
this.documentDao = documentDao;
}
/**
* Sets the benefitsCalculationService attribute value.
*
* @param benefitsCalculationService The benefitsCalculationService to set.
*/
@NonTransactional
public void setBenefitsCalculationService(BenefitsCalculationService benefitsCalculationService) {
this.benefitsCalculationService = benefitsCalculationService;
}
/**
* Sets the businessObjectService attribute value.
*
* @param businessObjectService The businessObjectService to set.
*/
@NonTransactional
public void setBusinessObjectService(BusinessObjectService businessObjectService) {
this.businessObjectService = businessObjectService;
}
/**
* Sets the budgetParameterService attribute value.
*
* @param budgetParameterService The budgetParameterService to set.
*/
@NonTransactional
public void setBudgetParameterService(BudgetParameterService budgetParameterService) {
this.budgetParameterService = budgetParameterService;
}
/**
* Sets the parameterService attribute value.
*
* @param parameterService The parameterService to set.
*/
@NonTransactional
public void setParameterService(ParameterService parameterService) {
this.parameterService = parameterService;
}
/**
* Sets the fiscalYearFunctionControlService attribute value.
*
* @param fiscalYearFunctionControlService The fiscalYearFunctionControlService to set.
*/
@NonTransactional
public void setFiscalYearFunctionControlService(FiscalYearFunctionControlService fiscalYearFunctionControlService) {
this.fiscalYearFunctionControlService = fiscalYearFunctionControlService;
}
/**
* Sets the optionsService attribute value.
*
* @param optionsService The optionsService to set.
*/
@NonTransactional
public void setOptionsService(OptionsService optionsService) {
this.optionsService = optionsService;
}
/**
* Gets the persistenceService attribute.
*
* @return Returns the persistenceService.
*/
@NonTransactional
public PersistenceService getPersistenceService() {
return persistenceService;
}
/**
* Sets the persistenceService attribute value.
*
* @param persistenceService The persistenceService to set.
*/
@NonTransactional
public void setPersistenceService(PersistenceService persistenceService) {
this.persistenceService = persistenceService;
}
/**
* Sets the organizationService attribute value.
*
* @param organizationService The organizationService to set.
*/
@NonTransactional
public void setOrganizationService(OrganizationService organizationService) {
this.organizationService = organizationService;
}
/**
* Sets the kualiModuleService attribute value.
*
* @param kualiModuleService The kualiModuleService to set.
*/
@NonTransactional
public void setKualiModuleService(KualiModuleService kualiModuleService) {
this.kualiModuleService = kualiModuleService;
}
/**
* Gets the defaultLaborBenefitRateCategoryCode attribute.
* @return Returns the defaultLaborBenefitRateCategoryCode.
*/
@Transactional
public String getDefaultLaborBenefitRateCategoryCode() {
if(ObjectUtils.isNull(defaultLaborBenefitRateCategoryCode)){
// make sure the parameter exists
if (SpringContext.getBean(ParameterService.class).parameterExists(Account.class, KFSParameterKeyConstants.LdParameterConstants.DEFAULT_BENEFIT_RATE_CATEGORY_CODE)) {
this.defaultLaborBenefitRateCategoryCode = SpringContext.getBean(ParameterService.class).getParameterValueAsString(Account.class, KFSParameterKeyConstants.LdParameterConstants.DEFAULT_BENEFIT_RATE_CATEGORY_CODE);
}
else {
this.defaultLaborBenefitRateCategoryCode = "";
}
}
return defaultLaborBenefitRateCategoryCode;
}
/**
* Sets the defaultLaborBenefitRateCategoryCode attribute value.
* @param defaultLaborBenefitRateCategoryCode The defaultLaborBenefitRateCategoryCode to set.
*/
@Transactional
public void setDefaultLaborBenefitRateCategoryCode(String defaultLaborBenefitRateCategoryCode) {
this.defaultLaborBenefitRateCategoryCode = defaultLaborBenefitRateCategoryCode;
}
}