/* * 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.sys.web.struts; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.apache.struts.upload.FormFile; import org.kuali.kfs.coa.businessobject.Account; import org.kuali.kfs.coa.businessobject.ObjectCode; import org.kuali.kfs.coa.businessobject.SubAccount; import org.kuali.kfs.coa.businessobject.SubObjectCode; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.businessobject.AccountingLine; import org.kuali.kfs.sys.businessobject.AccountingLineOverride; import org.kuali.kfs.sys.businessobject.SourceAccountingLine; import org.kuali.kfs.sys.businessobject.TargetAccountingLine; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.document.AccountingDocument; import org.kuali.kfs.sys.document.web.struts.FinancialSystemTransactionalDocumentFormBase; import org.kuali.kfs.sys.service.impl.KfsParameterConstants; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.core.web.format.CurrencyFormatter; import org.kuali.rice.coreservice.framework.parameter.ParameterService; import org.kuali.rice.kns.service.BusinessObjectDictionaryService; import org.kuali.rice.krad.exception.InfrastructureException; import org.kuali.rice.krad.util.KRADConstants; import org.kuali.rice.krad.util.ObjectUtils; /** * This class is the base action form for all financial documents. */ public class KualiAccountingDocumentFormBase extends FinancialSystemTransactionalDocumentFormBase { protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiAccountingDocumentFormBase.class); protected SourceAccountingLine newSourceLine; protected TargetAccountingLine newTargetLine; protected Map editableAccounts; protected Map forcedLookupOptionalFields; // TODO: FormFile isn't Serializable, so mark these fields need as transient or create a Serializable subclass of FormFile protected FormFile sourceFile; protected FormFile targetFile; protected boolean hideDetails = false; /** * This constructor sets up empty instances for the dependent objects... */ public KualiAccountingDocumentFormBase() { super(); // create an empty editableAccounts map, for safety's sake editableAccounts = new HashMap(); forcedReadOnlyFields = new HashMap(); forcedLookupOptionalFields = new HashMap(); } /** * Overrides the parent to call super.populate and then to call the accounting lines populate method that is specific to loading * the two select lists on the page. * * @see org.kuali.rice.kns.web.struts.pojo.PojoForm#populate(javax.servlet.http.HttpServletRequest) */ @Override public void populate(HttpServletRequest request) { super.populate(request); final String methodToCall = this.getMethodToCall(); final Map parameterMap = request.getParameterMap(); populateAccountingLinesForResponse(methodToCall, parameterMap); setDocTypeName(discoverDocumentTypeName()); } /** * Populates the accounting lines which need to be updated to successfully complete a response to the request * @param methodToCall the method to call in the action to complete this request transaction * @param parameterMap the map of parameters which came in with the transaction */ protected void populateAccountingLinesForResponse(String methodToCall, Map parameterMap) { populateSourceAccountingLine(getNewSourceLine(), KFSPropertyConstants.NEW_SOURCE_LINE, parameterMap); populateTargetAccountingLine(getNewTargetLine(), KFSPropertyConstants.NEW_TARGET_LINE, parameterMap); // don't call populateAccountingLines if you are copying or errorCorrecting a document, // since you want the accountingLines in the copy to be "identical" to those in the original if (!StringUtils.equals(methodToCall, KFSConstants.COPY_METHOD) && !StringUtils.equals(methodToCall, KFSConstants.ERRORCORRECT_METHOD)) { populateAccountingLines(parameterMap); } } /** * This method iterates over all of the source lines and all of the target lines in a transactional document, and calls * prepareAccountingLineForValidationAndPersistence on each one. This is called because a user could have updated already * existing accounting lines that had blank values in composite key fields. * * @param parameterMap the map of parameters that were sent in with the request */ protected void populateAccountingLines(Map parameterMap) { Iterator sourceLines = getFinancialDocument().getSourceAccountingLines().iterator(); int count = 0; while (sourceLines.hasNext()) { SourceAccountingLine sourceLine = (SourceAccountingLine) sourceLines.next(); populateSourceAccountingLine(sourceLine, KFSPropertyConstants.DOCUMENT+"."+KFSPropertyConstants.SOURCE_ACCOUNTING_LINE+"["+count+"]", parameterMap); count += 1; } Iterator targetLines = getFinancialDocument().getTargetAccountingLines().iterator(); count = 0; while (targetLines.hasNext()) { TargetAccountingLine targetLine = (TargetAccountingLine) targetLines.next(); populateTargetAccountingLine(targetLine, KFSPropertyConstants.DOCUMENT+"."+KFSPropertyConstants.TARGET_ACCOUNTING_LINE+"["+count+"]", parameterMap); count += 1; } } /** * Populates a source accounting line bo using values from the struts form. This is in place to make sure that all of the * composite key objects have the correct values in them. This should be overridden by children forms in the situation where * document level attributes need to be pushed down into the accounting lines. * * @param sourceLine * @param accountingLinePropertyName the property path from the form to the accounting line * @param parameterMap the map of parameters that were sent in with the request */ public void populateSourceAccountingLine(SourceAccountingLine sourceLine, String accountingLinePropertyName, Map parameterMap) { populateAccountingLine(sourceLine, accountingLinePropertyName, parameterMap); } /** * Populates a target accounting line bo using values from the struts form. This is in place to make sure that all of the * composite key objects have the correct values in them. This should be overridden by children forms in the situation where * document level attributes need to be pushed down into the accounting lines. * * @param targetLine * @param accountingLinePropertyName the property path from the form to the accounting line * @param parameterMap the map of parameters that were sent in with the request */ public void populateTargetAccountingLine(TargetAccountingLine targetLine, String accountingLinePropertyName, Map parameterMap) { populateAccountingLine(targetLine, accountingLinePropertyName, parameterMap); } /** * Populates the dependent fields of objects contained within the given accountingLine * * @param line * @param accountingLinePropertyName the property path from the form to the accounting line * @param parameterMap the map of parameters that were sent in with the request */ @SuppressWarnings("deprecation") protected void populateAccountingLine(AccountingLine line, String accountingLinePropertyName, Map parameterMap) { SpringContext.getBean(BusinessObjectDictionaryService.class).performForceUppercase(line); line.setDocumentNumber(getDocument().getDocumentNumber()); if (ObjectUtils.isNull(line.getAccount())) { line.setAccount(new Account()); } line.getAccount().setChartOfAccountsCode(line.getChartOfAccountsCode()); if (ObjectUtils.isNull(line.getObjectCode())) { line.setObjectCode(new ObjectCode()); } line.getObjectCode().setUniversityFiscalYear(getFinancialDocument().getPostingYear()); line.getObjectCode().setChartOfAccountsCode(line.getChartOfAccountsCode()); if (ObjectUtils.isNull(line.getSubAccount())) { line.setSubAccount(new SubAccount()); } line.getSubAccount().setChartOfAccountsCode(line.getChartOfAccountsCode()); line.getSubAccount().setAccountNumber(line.getAccountNumber()); if (ObjectUtils.isNull(line.getSubObjectCode())) { line.setSubObjectCode(new SubObjectCode()); } line.getSubObjectCode().setChartOfAccountsCode(line.getChartOfAccountsCode()); line.getSubObjectCode().setAccountNumber(line.getAccountNumber()); line.getSubObjectCode().setFinancialObjectCode(line.getFinancialObjectCode()); line.getSubObjectCode().setUniversityFiscalYear(getFinancialDocument().getPostingYear()); repopulateOverrides(line, accountingLinePropertyName, parameterMap); AccountingLineOverride.populateFromInput(line); } /** * This repopulates the override values from the request * @param line the line to repopulate override values for * @param accountingLinePropertyName the property path from the form to the accounting line * @param parameterMap the map of parameters that were sent in with the request */ protected void repopulateOverrides(AccountingLine line, String accountingLinePropertyName, Map parameterMap) { AccountingLineOverride.determineNeededOverrides(getFinancialDocument() , line); if (line.getAccountExpiredOverrideNeeded()) { if (LOG.isDebugEnabled()) { StringUtils.join(parameterMap.keySet(), "\n"); } if (parameterMap.containsKey(accountingLinePropertyName+".accountExpiredOverride.present")) { line.setAccountExpiredOverride(parameterMap.containsKey(accountingLinePropertyName+".accountExpiredOverride")); } } else { line.setAccountExpiredOverride(false); } if (line.isObjectBudgetOverrideNeeded()) { if (parameterMap.containsKey(accountingLinePropertyName+".objectBudgetOverride.present")) { line.setObjectBudgetOverride(parameterMap.containsKey(accountingLinePropertyName+".objectBudgetOverride")); } } else { line.setObjectBudgetOverride(false); } } /** * This method retrieves an instance of the form. * * @return */ public AccountingDocument getFinancialDocument() { return (AccountingDocument) getDocument(); } /** * @return Returns the newTargetLine. */ public TargetAccountingLine getNewTargetLine() { if (newTargetLine == null) { newTargetLine = createNewTargetAccountingLine(getFinancialDocument()); } return newTargetLine; } /** * @param newExpenseLine The newTargetLine to set. */ public void setNewTargetLine(TargetAccountingLine newExpenseLine) { this.newTargetLine = newExpenseLine; } /** * @return Returns the newSourceLine. */ public SourceAccountingLine getNewSourceLine() { if (newSourceLine == null) { newSourceLine = createNewSourceAccountingLine(getFinancialDocument()); } return newSourceLine; } /** * @param newIncomeLine The newSourceLine to set. */ public void setNewSourceLine(SourceAccountingLine newIncomeLine) { this.newSourceLine = newIncomeLine; } /** * @return Returns the sourceFile. */ public FormFile getSourceFile() { return sourceFile; } /** * @param sourceFile The sourceFile to set. */ public void setSourceFile(FormFile sourceFile) { this.sourceFile = sourceFile; } /** * @return Returns the targetFile. */ public FormFile getTargetFile() { return targetFile; } /** * @param targetFile The targetFile to set. */ public void setTargetFile(FormFile targetFile) { this.targetFile = targetFile; } /** * @return current Map of editableAccounts */ public Map getEditableAccounts() { return editableAccounts; } /** * @param editableAccounts the account Map to set */ public void setEditableAccounts(Map editableAccounts) { this.editableAccounts = editableAccounts; } /** * @return hideDetails attribute */ public boolean isHideDetails() { return hideDetails; } /** * @return hideDetails attribute * @see #isHideDetails() */ public boolean getHideDetails() { return isHideDetails(); } /** * @param hideDetails */ public void setHideDetails(boolean hideDetails) { this.hideDetails = hideDetails; } /** * Retrieves the source accounting lines total in a currency format with commas. * * @return String */ public String getCurrencyFormattedSourceTotal() { return (String) new CurrencyFormatter().format(getFinancialDocument().getSourceTotal()); } /** * Retrieves the source accounting lines total in a currency format with commas. * * @return String */ public String getCurrencyFormattedTargetTotal() { return (String) new CurrencyFormatter().format(getFinancialDocument().getTargetTotal()); } /** * @return the URL to the accounting line import instructions */ public String getAccountingLineImportInstructionsUrl() { return SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(KFSConstants.EXTERNALIZABLE_HELP_URL_KEY) + SpringContext.getBean(ParameterService.class).getParameterValueAsString(KfsParameterConstants.FINANCIAL_SYSTEM_DOCUMENT.class, KFSConstants.FinancialApcParms.ACCOUNTING_LINE_IMPORT_HELP); } /** * @param financialDocument * @return a new source accounting line for the document */ protected SourceAccountingLine createNewSourceAccountingLine(AccountingDocument financialDocument) { if (financialDocument == null) { throw new IllegalArgumentException("invalid (null) document"); } try { return (SourceAccountingLine) financialDocument.getSourceAccountingLineClass().newInstance(); } catch (Exception e) { throw new InfrastructureException("unable to create a new source accounting line", e); } } /** * @param financialDocument * @return a new target accounting line for the documet */ protected TargetAccountingLine createNewTargetAccountingLine(AccountingDocument financialDocument) { if (financialDocument == null) { throw new IllegalArgumentException("invalid (null) document"); } try { return (TargetAccountingLine) financialDocument.getTargetAccountingLineClass().newInstance(); } catch (Exception e) { throw new InfrastructureException("unable to create a new target accounting line", e); } } /** * This method takes a generic list, hopefully with some AccountingLine objects in it, and returns a list of AccountingLine * objects, because Java generics are just so wonderful. * * @param lines a list of objects * @return a list of the accounting lines that were in the lines parameter */ protected List<AccountingLine> harvestAccountingLines(List lines) { List<AccountingLine> accountingLines = new ArrayList<AccountingLine>(); for (Object o : lines) { if (o instanceof AccountingLine) { accountingLines.add((AccountingLine) o); } } return accountingLines; } /** * A <code>{@link Map}</code> of names of optional accounting line fields that require a quickfinder. * * @return a Map of fields */ public void setForcedLookupOptionalFields(Map fieldMap) { forcedLookupOptionalFields = fieldMap; } /** * A <code>{@link Map}</code> of names of optional accounting line fields that require a quickfinder. * * @return a Map of fields */ public Map getForcedLookupOptionalFields() { return forcedLookupOptionalFields; } /** * Adds the accounting line file size to the list of max file sizes. * * @see org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase#customInitMaxUploadSizes() */ @Override protected void customInitMaxUploadSizes() { super.customInitMaxUploadSizes(); addMaxUploadSize(SpringContext.getBean(ParameterService.class).getParameterValueAsString(KfsParameterConstants.FINANCIAL_SYSTEM_DOCUMENT.class, KFSConstants.ACCOUNTING_LINE_IMPORT_MAX_FILE_SIZE_PARM_NM)); } /** * @see org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase#shouldMethodToCallParameterBeUsed(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest) */ @Override public boolean shouldMethodToCallParameterBeUsed(String methodToCallParameterName, String methodToCallParameterValue, HttpServletRequest request) { if(StringUtils.equals(methodToCallParameterName, KRADConstants.DISPATCH_REQUEST_PARAMETER)) { if(this.getExcludedmethodToCall().contains(methodToCallParameterValue)) { return true; } } return super.shouldMethodToCallParameterBeUsed(methodToCallParameterName, methodToCallParameterValue, request); } /** * get the names of the methods to call that can be excluded from the "be used" check. * @return the names of the methods to call that can be excluded from the "be used" check */ protected List<String> getExcludedmethodToCall() { return new ArrayList<String>(); } }