View Javadoc
1   /*
2    * Copyright 2007 The Kuali Foundation
3    * 
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    * http://www.opensource.org/licenses/ecl2.php
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.sys.web.struts;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import javax.servlet.http.HttpServletRequest;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.apache.struts.upload.FormFile;
28  import org.kuali.ole.coa.businessobject.Account;
29  import org.kuali.ole.coa.businessobject.ObjectCode;
30  import org.kuali.ole.coa.businessobject.SubAccount;
31  import org.kuali.ole.coa.businessobject.SubObjectCode;
32  import org.kuali.ole.sys.OLEConstants;
33  import org.kuali.ole.sys.OLEPropertyConstants;
34  import org.kuali.ole.sys.businessobject.AccountingLine;
35  import org.kuali.ole.sys.businessobject.AccountingLineOverride;
36  import org.kuali.ole.sys.businessobject.SourceAccountingLine;
37  import org.kuali.ole.sys.businessobject.TargetAccountingLine;
38  import org.kuali.ole.sys.context.SpringContext;
39  import org.kuali.ole.sys.document.AccountingDocument;
40  import org.kuali.ole.sys.document.web.struts.FinancialSystemTransactionalDocumentFormBase;
41  import org.kuali.ole.sys.service.impl.OleParameterConstants;
42  import org.kuali.rice.core.api.config.property.ConfigurationService;
43  import org.kuali.rice.core.web.format.CurrencyFormatter;
44  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
45  import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
46  import org.kuali.rice.krad.util.KRADConstants;
47  import org.kuali.rice.krad.util.ObjectUtils;
48  
49  /**
50   * This class is the base action form for all financial documents.
51   */
52  public class KualiAccountingDocumentFormBase extends FinancialSystemTransactionalDocumentFormBase {
53      private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiAccountingDocumentFormBase.class);
54  
55      protected SourceAccountingLine newSourceLine;
56      protected TargetAccountingLine newTargetLine;
57  
58      protected Map editableAccounts;
59      protected Map forcedLookupOptionalFields;
60  
61      // TODO: FormFile isn't Serializable, so mark these fields need as transient or create a Serializable subclass of FormFile
62      protected FormFile sourceFile;
63      protected FormFile targetFile;
64      protected boolean hideDetails = false;
65  
66      /**
67       * This constructor sets up empty instances for the dependent objects...
68       */
69      public KualiAccountingDocumentFormBase() {
70          super();
71  
72          // create an empty editableAccounts map, for safety's sake
73          editableAccounts = new HashMap();
74          forcedReadOnlyFields = new HashMap();
75          forcedLookupOptionalFields = new HashMap();
76      }
77  
78  
79      /**
80       * Overrides the parent to call super.populate and then to call the accounting lines populate method that is specific to loading
81       * the two select lists on the page.
82       * 
83       * @see org.kuali.rice.kns.web.struts.pojo.PojoForm#populate(javax.servlet.http.HttpServletRequest)
84       */
85      @Override
86      public void populate(HttpServletRequest request) {
87          super.populate(request);
88          final String methodToCall = this.getMethodToCall();
89          final Map parameterMap = request.getParameterMap();
90  
91          populateAccountingLinesForResponse(methodToCall, parameterMap);
92  
93          setDocTypeName(discoverDocumentTypeName());
94      }
95      
96      /**
97       * Populates the accounting lines which need to be updated to successfully complete a response to the request
98       * @param methodToCall the method to call in the action to complete this request transaction
99       * @param parameterMap the map of parameters which came in with the transaction
100      */
101     protected void populateAccountingLinesForResponse(String methodToCall, Map parameterMap) {
102         populateSourceAccountingLine(getNewSourceLine(), OLEPropertyConstants.NEW_SOURCE_LINE, parameterMap);
103         populateTargetAccountingLine(getNewTargetLine(), OLEPropertyConstants.NEW_TARGET_LINE, parameterMap);
104 
105         // don't call populateAccountingLines if you are copying or errorCorrecting a document,
106         // since you want the accountingLines in the copy to be "identical" to those in the original
107         if (!StringUtils.equals(methodToCall, OLEConstants.COPY_METHOD) && !StringUtils.equals(methodToCall, OLEConstants.ERRORCORRECT_METHOD)) {
108             populateAccountingLines(parameterMap);
109         }
110     }
111 
112     /**
113      * This method iterates over all of the source lines and all of the target lines in a transactional document, and calls
114      * prepareAccountingLineForValidationAndPersistence on each one. This is called because a user could have updated already
115      * existing accounting lines that had blank values in composite key fields.
116      * 
117      * @param parameterMap the map of parameters that were sent in with the request
118      */
119     protected void populateAccountingLines(Map parameterMap) {
120         Iterator sourceLines = getFinancialDocument().getSourceAccountingLines().iterator();
121         int count = 0;
122         while (sourceLines.hasNext()) {
123             SourceAccountingLine sourceLine = (SourceAccountingLine) sourceLines.next();
124             populateSourceAccountingLine(sourceLine, OLEPropertyConstants.DOCUMENT+"."+OLEPropertyConstants.SOURCE_ACCOUNTING_LINE+"["+count+"]", parameterMap);
125             count += 1;
126         }
127 
128         Iterator targetLines = getFinancialDocument().getTargetAccountingLines().iterator();
129         count = 0;
130         while (targetLines.hasNext()) {
131             TargetAccountingLine targetLine = (TargetAccountingLine) targetLines.next();
132             populateTargetAccountingLine(targetLine, OLEPropertyConstants.DOCUMENT+"."+OLEPropertyConstants.TARGET_ACCOUNTING_LINE+"["+count+"]", parameterMap);
133             count += 1;
134         }
135     }
136 
137     /**
138      * Populates a source accounting line bo using values from the struts form. This is in place to make sure that all of the
139      * composite key objects have the correct values in them. This should be overridden by children forms in the situation where
140      * document level attributes need to be pushed down into the accounting lines.
141      * 
142      * @param sourceLine
143      * @param accountingLinePropertyName the property path from the form to the accounting line
144      * @param parameterMap the map of parameters that were sent in with the request
145      */
146     public void populateSourceAccountingLine(SourceAccountingLine sourceLine, String accountingLinePropertyName, Map parameterMap) {
147         populateAccountingLine(sourceLine, accountingLinePropertyName, parameterMap);
148     }
149 
150     /**
151      * Populates a target accounting line bo using values from the struts form. This is in place to make sure that all of the
152      * composite key objects have the correct values in them. This should be overridden by children forms in the situation where
153      * document level attributes need to be pushed down into the accounting lines.
154      * 
155      * @param targetLine
156      * @param accountingLinePropertyName the property path from the form to the accounting line
157      * @param parameterMap the map of parameters that were sent in with the request
158      */
159     public void populateTargetAccountingLine(TargetAccountingLine targetLine, String accountingLinePropertyName, Map parameterMap) {
160         populateAccountingLine(targetLine, accountingLinePropertyName, parameterMap);
161     }
162 
163     /**
164      * Populates the dependent fields of objects contained within the given accountingLine
165      * 
166      * @param line
167      * @param accountingLinePropertyName the property path from the form to the accounting line
168      * @param parameterMap the map of parameters that were sent in with the request
169      */
170     @SuppressWarnings("deprecation")
171     protected void populateAccountingLine(AccountingLine line, String accountingLinePropertyName, Map parameterMap) {
172         SpringContext.getBean(BusinessObjectDictionaryService.class).performForceUppercase(line);
173 
174         line.setDocumentNumber(getDocument().getDocumentNumber());
175 
176         if (ObjectUtils.isNull(line.getAccount())) {
177             line.setAccount(new Account());
178         }
179         line.getAccount().setChartOfAccountsCode(line.getChartOfAccountsCode());
180 
181         if (ObjectUtils.isNull(line.getObjectCode())) {
182             line.setObjectCode(new ObjectCode());
183         }
184         line.getObjectCode().setUniversityFiscalYear(getFinancialDocument().getPostingYear());
185         line.getObjectCode().setChartOfAccountsCode(line.getChartOfAccountsCode());
186 
187         if (ObjectUtils.isNull(line.getSubAccount())) {
188             line.setSubAccount(new SubAccount());
189         }
190         line.getSubAccount().setChartOfAccountsCode(line.getChartOfAccountsCode());
191         line.getSubAccount().setAccountNumber(line.getAccountNumber());
192 
193         if (ObjectUtils.isNull(line.getSubObjectCode())) {
194             line.setSubObjectCode(new SubObjectCode());
195         }
196         line.getSubObjectCode().setChartOfAccountsCode(line.getChartOfAccountsCode());
197         line.getSubObjectCode().setAccountNumber(line.getAccountNumber());
198         line.getSubObjectCode().setFinancialObjectCode(line.getFinancialObjectCode());
199         line.getSubObjectCode().setUniversityFiscalYear(getFinancialDocument().getPostingYear());
200         
201         repopulateOverrides(line, accountingLinePropertyName, parameterMap);
202 
203         AccountingLineOverride.populateFromInput(line);
204     }
205     
206     /**
207      * This repopulates the override values from the request
208      * @param line the line to repopulate override values for
209      * @param accountingLinePropertyName the property path from the form to the accounting line
210      * @param parameterMap the map of parameters that were sent in with the request
211      */
212     protected void repopulateOverrides(AccountingLine line, String accountingLinePropertyName, Map parameterMap) {
213         AccountingLineOverride.determineNeededOverrides(getFinancialDocument() , line);
214         if (line.getAccountExpiredOverrideNeeded()) {
215             if (LOG.isDebugEnabled()) {
216                 StringUtils.join(parameterMap.keySet(), "\n");
217             }
218             if (parameterMap.containsKey(accountingLinePropertyName+".accountExpiredOverride.present")) {
219                 line.setAccountExpiredOverride(parameterMap.containsKey(accountingLinePropertyName+".accountExpiredOverride"));
220             }
221         } else {
222             line.setAccountExpiredOverride(false);
223         }
224         if (line.isObjectBudgetOverrideNeeded()) {
225             if (parameterMap.containsKey(accountingLinePropertyName+".objectBudgetOverride.present")) {
226                 line.setObjectBudgetOverride(parameterMap.containsKey(accountingLinePropertyName+".objectBudgetOverride"));
227             }
228         } else {
229             line.setObjectBudgetOverride(false);
230         }
231     }
232 
233     /**
234      * This method retrieves an instance of the form.
235      * 
236      * @return
237      */
238     public AccountingDocument getFinancialDocument() {
239         return (AccountingDocument) getDocument();
240     }
241 
242     /**
243      * @return Returns the newTargetLine.
244      */
245     public TargetAccountingLine getNewTargetLine() {
246         if (newTargetLine == null) {
247             newTargetLine = createNewTargetAccountingLine(getFinancialDocument());
248         }
249         return newTargetLine;
250     }
251 
252     /**
253      * @param newExpenseLine The newTargetLine to set.
254      */
255     public void setNewTargetLine(TargetAccountingLine newExpenseLine) {
256         this.newTargetLine = newExpenseLine;
257     }
258 
259     /**
260      * @return Returns the newSourceLine.
261      */
262     public SourceAccountingLine getNewSourceLine() {
263         if (newSourceLine == null) {
264             newSourceLine = createNewSourceAccountingLine(getFinancialDocument());
265         }
266         return newSourceLine;
267     }
268 
269     /**
270      * @param newIncomeLine The newSourceLine to set.
271      */
272     public void setNewSourceLine(SourceAccountingLine newIncomeLine) {
273         this.newSourceLine = newIncomeLine;
274     }
275 
276     /**
277      * @return Returns the sourceFile.
278      */
279     public FormFile getSourceFile() {
280         return sourceFile;
281     }
282 
283     /**
284      * @param sourceFile The sourceFile to set.
285      */
286     public void setSourceFile(FormFile sourceFile) {
287         this.sourceFile = sourceFile;
288     }
289 
290     /**
291      * @return Returns the targetFile.
292      */
293     public FormFile getTargetFile() {
294         return targetFile;
295     }
296 
297     /**
298      * @param targetFile The targetFile to set.
299      */
300     public void setTargetFile(FormFile targetFile) {
301         this.targetFile = targetFile;
302     }
303 
304 
305     /**
306      * @return current Map of editableAccounts
307      */
308     public Map getEditableAccounts() {
309         return editableAccounts;
310     }
311 
312     /**
313      * @param editableAccounts the account Map to set
314      */
315     public void setEditableAccounts(Map editableAccounts) {
316         this.editableAccounts = editableAccounts;
317     }
318 
319     /**
320      * @return hideDetails attribute
321      */
322     public boolean isHideDetails() {
323         return hideDetails;
324     }
325 
326     /**
327      * @return hideDetails attribute
328      * @see #isHideDetails()
329      */
330     public boolean getHideDetails() {
331         return isHideDetails();
332     }
333 
334     /**
335      * @param hideDetails
336      */
337     public void setHideDetails(boolean hideDetails) {
338         this.hideDetails = hideDetails;
339     }
340 
341     /**
342      * Retrieves the source accounting lines total in a currency format with commas.
343      * 
344      * @return String
345      */
346     public String getCurrencyFormattedSourceTotal() {
347         return (String) new CurrencyFormatter().format(getFinancialDocument().getSourceTotal());
348     }
349 
350     /**
351      * Retrieves the source accounting lines total in a currency format with commas.
352      * 
353      * @return String
354      */
355     public String getCurrencyFormattedTargetTotal() {
356         return (String) new CurrencyFormatter().format(getFinancialDocument().getTargetTotal());
357     }
358 
359     /**
360      * @return the URL to the accounting line import instructions
361      */
362     public String getAccountingLineImportInstructionsUrl() {
363         return SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(OLEConstants.EXTERNALIZABLE_HELP_URL_KEY) + SpringContext.getBean(ParameterService.class).getParameterValueAsString(OleParameterConstants.FINANCIAL_SYSTEM_DOCUMENT.class, OLEConstants.FinancialApcParms.ACCOUNTING_LINE_IMPORT_HELP);
364     }
365 
366     /**
367      * @param financialDocument
368      * @return a new source accounting line for the document
369      */
370     protected SourceAccountingLine createNewSourceAccountingLine(AccountingDocument financialDocument) {
371         if (financialDocument == null) {
372             throw new IllegalArgumentException("invalid (null) document");
373         }
374         try {
375             return (SourceAccountingLine) financialDocument.getSourceAccountingLineClass().newInstance();
376         }
377         catch (Exception e) {
378             throw new IllegalArgumentException("unable to create a new source accounting line", e);
379         }
380     }
381 
382     /**
383      * @param financialDocument
384      * @return a new target accounting line for the documet
385      */
386     protected TargetAccountingLine createNewTargetAccountingLine(AccountingDocument financialDocument) {
387         if (financialDocument == null) {
388             throw new IllegalArgumentException("invalid (null) document");
389         }
390         try {
391             return (TargetAccountingLine) financialDocument.getTargetAccountingLineClass().newInstance();
392         }
393         catch (Exception e) {
394             throw new IllegalArgumentException("unable to create a new target accounting line", e);
395         }
396     }
397 
398     /**
399      * This method takes a generic list, hopefully with some AccountingLine objects in it, and returns a list of AccountingLine
400      * objects, because Java generics are just so wonderful.
401      * 
402      * @param lines a list of objects
403      * @return a list of the accounting lines that were in the lines parameter
404      */
405     protected List<AccountingLine> harvestAccountingLines(List lines) {
406         List<AccountingLine> accountingLines = new ArrayList<AccountingLine>();
407         for (Object o : lines) {
408             if (o instanceof AccountingLine) {
409                 accountingLines.add((AccountingLine) o);
410             }
411         }
412         return accountingLines;
413     }
414 
415     /**
416      * A <code>{@link Map}</code> of names of optional accounting line fields that require a quickfinder.
417      * 
418      * @return a Map of fields
419      */
420     public void setForcedLookupOptionalFields(Map fieldMap) {
421         forcedLookupOptionalFields = fieldMap;
422     }
423 
424     /**
425      * A <code>{@link Map}</code> of names of optional accounting line fields that require a quickfinder.
426      * 
427      * @return a Map of fields
428      */
429     public Map getForcedLookupOptionalFields() {
430         return forcedLookupOptionalFields;
431     }
432 
433     /**
434      * Adds the accounting line file size to the list of max file sizes.
435      * 
436      * @see org.kuali.rice.kns.web.struts.form.pojo.PojoFormBase#customInitMaxUploadSizes()
437      */
438     @Override
439     protected void customInitMaxUploadSizes() {
440         super.customInitMaxUploadSizes();
441         addMaxUploadSize(SpringContext.getBean(ParameterService.class).getParameterValueAsString(OleParameterConstants.FINANCIAL_SYSTEM_DOCUMENT.class, OLEConstants.ACCOUNTING_LINE_IMPORT_MAX_FILE_SIZE_PARM_NM));
442     }
443     
444     /**
445      * @see org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase#shouldMethodToCallParameterBeUsed(java.lang.String, java.lang.String, javax.servlet.http.HttpServletRequest)
446      */
447     @Override
448     public boolean shouldMethodToCallParameterBeUsed(String methodToCallParameterName, String methodToCallParameterValue, HttpServletRequest request) {                
449         if(StringUtils.equals(methodToCallParameterName, KRADConstants.DISPATCH_REQUEST_PARAMETER)) {
450             if(this.getExcludedmethodToCall().contains(methodToCallParameterValue)) {
451                 return true;
452             }
453         }
454         return super.shouldMethodToCallParameterBeUsed(methodToCallParameterName, methodToCallParameterValue, request);
455     }
456     
457     /**
458      * get the names of the methods to call that can be excluded from the "be used" check.
459      * @return the names of the methods to call that can be excluded from the "be used" check 
460      */
461     protected List<String> getExcludedmethodToCall() {
462         return new ArrayList<String>();
463     }
464 }