001 /*
002 * Copyright 2011 The Kuali Foundation.
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.kfs.sys.web.struts;
017
018 import java.util.ArrayList;
019 import java.util.HashMap;
020 import java.util.Iterator;
021 import java.util.List;
022 import java.util.Map;
023
024 import javax.servlet.http.HttpServletRequest;
025
026 import org.apache.commons.lang.StringUtils;
027 import org.apache.struts.upload.FormFile;
028 import org.kuali.kfs.coa.businessobject.Account;
029 import org.kuali.kfs.coa.businessobject.ObjectCode;
030 import org.kuali.kfs.coa.businessobject.SubAccount;
031 import org.kuali.kfs.coa.businessobject.SubObjectCode;
032 import org.kuali.kfs.sys.KFSConstants;
033 import org.kuali.kfs.sys.KFSPropertyConstants;
034 import org.kuali.kfs.sys.businessobject.AccountingLine;
035 import org.kuali.kfs.sys.businessobject.AccountingLineOverride;
036 import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
037 import org.kuali.kfs.sys.businessobject.TargetAccountingLine;
038 import org.kuali.kfs.sys.context.SpringContext;
039 import org.kuali.kfs.sys.document.AccountingDocument;
040 import org.kuali.kfs.sys.document.web.struts.FinancialSystemTransactionalDocumentFormBase;
041 import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
042 import org.kuali.rice.kns.exception.InfrastructureException;
043 import org.kuali.rice.kns.service.BusinessObjectDictionaryService;
044 import org.kuali.rice.kns.service.KualiConfigurationService;
045 import org.kuali.rice.kns.service.ParameterService;
046 import org.kuali.rice.kns.util.KNSConstants;
047 import org.kuali.rice.kns.util.ObjectUtils;
048 import org.kuali.rice.kns.web.format.CurrencyFormatter;
049
050 /**
051 * This class is the base action form for all financial documents.
052 */
053 public class KualiAccountingDocumentFormBase extends FinancialSystemTransactionalDocumentFormBase {
054 protected static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiAccountingDocumentFormBase.class);
055 protected SourceAccountingLine newSourceLine;
056 protected TargetAccountingLine newTargetLine;
057
058 protected Map editableAccounts;
059 protected Map forcedLookupOptionalFields;
060
061 // TODO: FormFile isn't Serializable, so mark these fields need as transient or create a Serializable subclass of FormFile
062 protected FormFile sourceFile;
063 protected FormFile targetFile;
064 protected boolean hideDetails = false;
065
066 /**
067 * This constructor sets up empty instances for the dependent objects...
068 */
069 public KualiAccountingDocumentFormBase() {
070 super();
071
072 // create an empty editableAccounts map, for safety's sake
073 editableAccounts = new HashMap();
074 forcedReadOnlyFields = new HashMap();
075 forcedLookupOptionalFields = new HashMap();
076 }
077
078
079 /**
080 * Overrides the parent to call super.populate and then to call the accounting lines populate method that is specific to loading
081 * the two select lists on the page.
082 *
083 * @see org.kuali.rice.kns.web.struts.pojo.PojoForm#populate(javax.servlet.http.HttpServletRequest)
084 */
085 @Override
086 public void populate(HttpServletRequest request) {
087 super.populate(request);
088 final String methodToCall = this.getMethodToCall();
089 final Map parameterMap = request.getParameterMap();
090
091 populateAccountingLinesForResponse(methodToCall, parameterMap);
092
093 setDocTypeName(discoverDocumentTypeName());
094 }
095
096 /**
097 * Populates the accounting lines which need to be updated to successfully complete a response to the request
098 * @param methodToCall the method to call in the action to complete this request transaction
099 * @param parameterMap the map of parameters which came in with the transaction
100 */
101 protected void populateAccountingLinesForResponse(String methodToCall, Map parameterMap) {
102 populateSourceAccountingLine(getNewSourceLine(), KFSPropertyConstants.NEW_SOURCE_LINE, parameterMap);
103 populateTargetAccountingLine(getNewTargetLine(), KFSPropertyConstants.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, KFSConstants.COPY_METHOD) && !StringUtils.equals(methodToCall, KFSConstants.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, KFSPropertyConstants.DOCUMENT+"."+KFSPropertyConstants.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, KFSPropertyConstants.DOCUMENT+"."+KFSPropertyConstants.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(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(KualiConfigurationService.class).getPropertyString(KFSConstants.EXTERNALIZABLE_HELP_URL_KEY) + SpringContext.getBean(ParameterService.class).getParameterValue(KfsParameterConstants.FINANCIAL_SYSTEM_DOCUMENT.class, KFSConstants.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 InfrastructureException("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 InfrastructureException("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.pojo.PojoFormBase#customInitMaxUploadSizes()
437 */
438 @Override
439 protected void customInitMaxUploadSizes() {
440 super.customInitMaxUploadSizes();
441 addMaxUploadSize(SpringContext.getBean(ParameterService.class).getParameterValue(KfsParameterConstants.FINANCIAL_SYSTEM_DOCUMENT.class, KFSConstants.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, KNSConstants.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 }