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 static org.kuali.kfs.sys.KFSKeyConstants.ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_INVALID_ACCOUNT;
019 import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_REQUIRED;
020 import static org.kuali.kfs.sys.KFSKeyConstants.ERROR_REQUIRED;
021
022 import java.io.FileNotFoundException;
023 import java.io.IOException;
024 import java.util.ArrayList;
025 import java.util.Arrays;
026 import java.util.HashMap;
027 import java.util.HashSet;
028 import java.util.Iterator;
029 import java.util.List;
030 import java.util.Map;
031 import java.util.Properties;
032 import java.util.Set;
033
034 import javax.servlet.http.HttpServletRequest;
035 import javax.servlet.http.HttpServletResponse;
036
037 import org.apache.commons.lang.StringUtils;
038 import org.apache.struts.action.ActionForm;
039 import org.apache.struts.action.ActionForward;
040 import org.apache.struts.action.ActionMapping;
041 import org.apache.struts.upload.FormFile;
042 import org.kuali.kfs.coa.service.AccountService;
043 import org.kuali.kfs.fp.businessobject.CapitalAssetInformation;
044 import org.kuali.kfs.fp.businessobject.CapitalAssetInformationDetail;
045 import org.kuali.kfs.fp.businessobject.SalesTax;
046 import org.kuali.kfs.fp.document.CapitalAssetEditable;
047 import org.kuali.kfs.sys.KFSConstants;
048 import org.kuali.kfs.sys.KFSKeyConstants;
049 import org.kuali.kfs.sys.KFSPropertyConstants;
050 import org.kuali.kfs.sys.businessobject.AccountingLine;
051 import org.kuali.kfs.sys.businessobject.AccountingLineOverride;
052 import org.kuali.kfs.sys.businessobject.AccountingLineParser;
053 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
054 import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry;
055 import org.kuali.kfs.sys.businessobject.SourceAccountingLine;
056 import org.kuali.kfs.sys.businessobject.TargetAccountingLine;
057 import org.kuali.kfs.sys.context.SpringContext;
058 import org.kuali.kfs.sys.document.AccountingDocument;
059 import org.kuali.kfs.sys.document.AmountTotaling;
060 import org.kuali.kfs.sys.document.validation.event.AddAccountingLineEvent;
061 import org.kuali.kfs.sys.document.validation.event.DeleteAccountingLineEvent;
062 import org.kuali.kfs.sys.document.validation.impl.AccountingDocumentRuleBaseConstants.APPLICATION_PARAMETER;
063 import org.kuali.kfs.sys.document.web.struts.FinancialSystemTransactionalDocumentActionBase;
064 import org.kuali.kfs.sys.exception.AccountingLineParserException;
065 import org.kuali.kfs.sys.service.impl.KfsParameterConstants;
066 import org.kuali.rice.kew.exception.WorkflowException;
067 import org.kuali.rice.kns.service.BusinessObjectService;
068 import org.kuali.rice.kns.service.DataDictionaryService;
069 import org.kuali.rice.kns.service.DictionaryValidationService;
070 import org.kuali.rice.kns.service.KualiRuleService;
071 import org.kuali.rice.kns.service.ParameterEvaluator;
072 import org.kuali.rice.kns.service.ParameterService;
073 import org.kuali.rice.kns.service.PersistenceService;
074 import org.kuali.rice.kns.util.GlobalVariables;
075 import org.kuali.rice.kns.util.KNSConstants;
076 import org.kuali.rice.kns.util.ObjectUtils;
077 import org.kuali.rice.kns.util.Timer;
078 import org.kuali.rice.kns.util.UrlFactory;
079 import org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase;
080
081 /**
082 * This class handles UI actions for all shared methods of financial documents.
083 */
084 public class KualiAccountingDocumentActionBase extends FinancialSystemTransactionalDocumentActionBase {
085 protected static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(KualiAccountingDocumentActionBase.class);
086
087 /**
088 * Adds check for accountingLine updates, generates and dispatches any events caused by such updates
089 *
090 * @see org.apache.struts.action.Action#execute(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm,
091 * javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
092 */
093 @Override
094 public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
095 Timer t0 = new Timer("KualiFinancialDocumentFormBase.execute");
096 KualiAccountingDocumentFormBase transForm = (KualiAccountingDocumentFormBase) form;
097
098 // handle changes to accountingLines
099 if (transForm.hasDocumentId()) {
100 AccountingDocument financialDocument = (AccountingDocument) transForm.getDocument();
101
102 processAccountingLines(financialDocument, transForm, KFSConstants.SOURCE);
103 processAccountingLines(financialDocument, transForm, KFSConstants.TARGET);
104 }
105
106 // This is after a potential handleUpdate(), to display automatically cleared overrides following a route or save.
107 processAccountingLineOverrides(transForm);
108
109 // proceed as usual
110 ActionForward result = super.execute(mapping, form, request, response);
111 t0.log();
112 return result;
113 }
114
115 /**
116 * All document-load operations get routed through here
117 *
118 * @see org.kuali.rice.kns.web.struts.action.KualiDocumentActionBase#loadDocument(org.kuali.rice.kns.web.struts.form.KualiDocumentFormBase)
119 */
120 @Override
121 protected void loadDocument(KualiDocumentFormBase kualiDocumentFormBase) throws WorkflowException {
122 super.loadDocument(kualiDocumentFormBase);
123
124
125 KualiAccountingDocumentFormBase tform = (KualiAccountingDocumentFormBase) kualiDocumentFormBase;
126
127 // clear out the new accounting line holders
128 tform.setNewSourceLine(null);
129 tform.setNewTargetLine(null);
130
131 processAccountingLineOverrides(tform);
132 }
133
134 /**
135 * Needed to override this to keep from losing Sales Tax information
136 *
137 * @see org.kuali.rice.kns.web.struts.action.KualiAction#refresh(org.apache.struts.action.ActionMapping,
138 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
139 */
140 @Override
141 public ActionForward refresh(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
142 super.refresh(mapping, form, request, response);
143 refreshSalesTaxInfo(form);
144
145 return mapping.findForward(KFSConstants.MAPPING_BASIC);
146 }
147
148 /**
149 * Needed to override this to keep from losing Sales Tax information
150 *
151 * @see org.kuali.rice.kns.web.struts.action.KualiAction#toggleTab(org.apache.struts.action.ActionMapping,
152 * org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
153 */
154 @Override
155 public ActionForward toggleTab(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
156 super.toggleTab(mapping, form, request, response);
157 refreshSalesTaxInfo(form);
158
159 return mapping.findForward(KFSConstants.MAPPING_BASIC);
160 }
161
162
163 // Set of actions for which updateEvents should be generated
164 protected static final Set UPDATE_EVENT_ACTIONS;
165 static {
166 String[] updateEventActions = { KFSConstants.SAVE_METHOD, KFSConstants.ROUTE_METHOD, KFSConstants.APPROVE_METHOD, KFSConstants.BLANKET_APPROVE_METHOD };
167 UPDATE_EVENT_ACTIONS = new HashSet();
168 for (int i = 0; i < updateEventActions.length; ++i) {
169 UPDATE_EVENT_ACTIONS.add(updateEventActions[i]);
170 }
171 }
172
173 /**
174 * @param transForm
175 */
176 protected void processAccountingLineOverrides(KualiAccountingDocumentFormBase transForm) {
177 processAccountingLineOverrides(transForm.getNewSourceLine());
178 processAccountingLineOverrides(transForm.getNewTargetLine());
179 if (transForm.hasDocumentId()) {
180 AccountingDocument financialDocument = (AccountingDocument) transForm.getDocument();
181
182 processAccountingLineOverrides(financialDocument.getSourceAccountingLines());
183 processAccountingLineOverrides(financialDocument.getTargetAccountingLines());
184 }
185 }
186
187 /**
188 * @param line
189 */
190 protected void processAccountingLineOverrides(AccountingLine line) {
191 processAccountingLineOverrides(Arrays.asList(new AccountingLine[] { line }));
192 }
193
194 /**
195 * @param accountingLines
196 */
197 protected void processAccountingLineOverrides(List accountingLines) {
198 if (!accountingLines.isEmpty()) {
199 SpringContext.getBean(PersistenceService.class).retrieveReferenceObjects(accountingLines, AccountingLineOverride.REFRESH_FIELDS);
200
201 for (Iterator i = accountingLines.iterator(); i.hasNext();) {
202 AccountingLine line = (AccountingLine) i.next();
203 AccountingLineOverride.processForOutput(line);
204 }
205 }
206 }
207
208 /**
209 * @param transDoc
210 * @param transForm
211 * @param lineSet
212 */
213 protected void processAccountingLines(AccountingDocument transDoc, KualiAccountingDocumentFormBase transForm, String lineSet) {
214 // figure out which set of lines we're looking at
215 List formLines;
216 String pathPrefix;
217 boolean source;
218 if (lineSet.equals(KFSConstants.SOURCE)) {
219 formLines = transDoc.getSourceAccountingLines();
220 pathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME;
221 source = true;
222 }
223 else {
224 formLines = transDoc.getTargetAccountingLines();
225 pathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_TARGET_ACCT_LINE_PROPERTY_NAME;
226 source = false;
227 }
228
229 // find and process corresponding form and baselines
230 int index = 0;
231 for (Iterator i = formLines.iterator(); i.hasNext(); index++) {
232 AccountingLine formLine = (AccountingLine) i.next();
233
234 // update sales tax required attribute for view
235 // handleSalesTaxRequired(transDoc, formLine, source, false, index);
236 checkSalesTax(transDoc, formLine, source, false, index);
237 }
238 }
239
240 /**
241 * Automatically clears any overrides that have become unneeded. This is for accounting lines that were changed right before
242 * final actions like route. Normally the unneeded overrides are cleared in accountingLineOverrideField.tag instead, but that
243 * requires another form submit. This method shouldn't be called on lines that haven't changed, to avoid automatically changing
244 * read-only lines. This cannot be done in the Rule because Rules cannot change the AccountingLines; they only get a deepCopy.
245 *
246 * @param formLine
247 */
248 protected void clearOverridesThatBecameUnneeded(AccountingLine formLine) {
249 AccountingLineOverride currentlyNeeded = AccountingLineOverride.determineNeededOverrides(formLine);
250 AccountingLineOverride currentOverride = AccountingLineOverride.valueOf(formLine.getOverrideCode());
251 if (!currentOverride.isValidMask(currentlyNeeded)) {
252 // todo: handle unsupported combinations of overrides (not a problem until we allow certain multiple overrides)
253 }
254 formLine.setOverrideCode(currentOverride.mask(currentlyNeeded).getCode());
255 }
256
257 /**
258 * This method will remove a TargetAccountingLine from a FinancialDocument. This assumes that the user presses the delete button
259 * for a specific accounting line on the document and that the document is represented by a FinancialDocumentFormBase.
260 *
261 * @param mapping
262 * @param form
263 * @param request
264 * @param response
265 * @return ActionForward
266 * @throws Exception
267 */
268 public ActionForward deleteTargetLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
269 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form;
270
271 int deleteIndex = getLineToDelete(request);
272 String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_TARGET_ACCT_LINE_PROPERTY_NAME + "[" + deleteIndex + "]";
273 boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new DeleteAccountingLineEvent(errorPath, financialDocumentForm.getDocument(), ((AccountingDocument) financialDocumentForm.getDocument()).getTargetAccountingLine(deleteIndex), false));
274
275 // if the rule evaluation passed, let's delete it
276 if (rulePassed) {
277 deleteAccountingLine(false, financialDocumentForm, deleteIndex);
278 }
279 else {
280 String[] errorParams = new String[] { "target", Integer.toString(deleteIndex + 1) };
281 GlobalVariables.getMessageMap().putError(errorPath, KFSKeyConstants.ERROR_ACCOUNTINGLINE_DELETERULE_INVALIDACCOUNT, errorParams);
282 }
283
284 return mapping.findForward(KFSConstants.MAPPING_BASIC);
285 }
286
287 /**
288 * This method will remove a SourceAccountingLine from a FinancialDocument. This assumes that the user presses the delete button
289 * for a specific accounting line on the document and that the document is represented by a FinancialDocumentFormBase.
290 *
291 * @param mapping
292 * @param form
293 * @param request
294 * @param response
295 * @return ActionForward
296 * @throws Exception
297 */
298 public ActionForward deleteSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
299 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form;
300
301 int deleteIndex = getLineToDelete(request);
302 String errorPath = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME + "[" + deleteIndex + "]";
303 boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new DeleteAccountingLineEvent(errorPath, financialDocumentForm.getDocument(), ((AccountingDocument) financialDocumentForm.getDocument()).getSourceAccountingLine(deleteIndex), false));
304
305 // if the rule evaluation passed, let's delete it
306 if (rulePassed) {
307 deleteAccountingLine(true, financialDocumentForm, deleteIndex);
308 }
309 else {
310 String[] errorParams = new String[] { "source", Integer.toString(deleteIndex + 1) };
311 GlobalVariables.getMessageMap().putError(errorPath, KFSKeyConstants.ERROR_ACCOUNTINGLINE_DELETERULE_INVALIDACCOUNT, errorParams);
312 }
313
314 return mapping.findForward(KFSConstants.MAPPING_BASIC);
315 }
316
317
318 /**
319 * Deletes the source or target accountingLine with the given index from the given form. Assumes that the rule- and form-
320 * validation have already occurred.
321 *
322 * @param isSource
323 * @param financialDocumentForm
324 * @param deleteIndex
325 */
326 protected void deleteAccountingLine(boolean isSource, KualiAccountingDocumentFormBase financialDocumentForm, int deleteIndex) {
327 if (isSource) {
328 // remove from document
329 financialDocumentForm.getFinancialDocument().getSourceAccountingLines().remove(deleteIndex);
330
331 }
332 else {
333 // remove from document
334 financialDocumentForm.getFinancialDocument().getTargetAccountingLines().remove(deleteIndex);
335 }
336 // update the doc total
337 AccountingDocument tdoc = (AccountingDocument) financialDocumentForm.getDocument();
338 if (tdoc instanceof AmountTotaling) {
339 ((FinancialSystemDocumentHeader) financialDocumentForm.getDocument().getDocumentHeader()).setFinancialDocumentTotalAmount(((AmountTotaling) tdoc).getTotalDollarAmount());
340 }
341
342 }
343
344
345 /**
346 * This action executes a call to upload CSV accounting line values as TargetAccountingLines for a given transactional document.
347 * The "uploadAccountingLines()" method handles the multi-part request.
348 *
349 * @param mapping
350 * @param form
351 * @param request
352 * @param response
353 * @return ActionForward
354 * @throws Exception
355 */
356 public ActionForward uploadTargetLines(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
357
358 // call method that sourceform and destination list
359 uploadAccountingLines(false, form);
360
361 return mapping.findForward(KFSConstants.MAPPING_BASIC);
362 }
363
364
365 /**
366 * This action executes a call to upload CSV accounting line values as SourceAccountingLines for a given transactional document.
367 * The "uploadAccountingLines()" method handles the multi-part request.
368 *
369 * @param mapping
370 * @param form
371 * @param request
372 * @param response
373 * @return ActionForward
374 * @throws FileNotFoundException
375 * @throws IOException
376 */
377 public ActionForward uploadSourceLines(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException, IOException {
378 LOG.info("Uploading source accounting lines");
379 // call method that sourceform and destination list
380 uploadAccountingLines(true, form);
381
382 return mapping.findForward(KFSConstants.MAPPING_BASIC);
383 }
384
385 /**
386 * This method determines whether we are uploading source or target lines, and then calls uploadAccountingLines directly on the
387 * document object. This method handles retrieving the actual upload file as an input stream into the document.
388 *
389 * @param isSource
390 * @param form
391 * @throws FileNotFoundException
392 * @throws IOException
393 */
394 protected void uploadAccountingLines(boolean isSource, ActionForm form) throws FileNotFoundException, IOException {
395 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form;
396
397 List importedLines = null;
398
399 AccountingDocument financialDocument = tmpForm.getFinancialDocument();
400 AccountingLineParser accountingLineParser = financialDocument.getAccountingLineParser();
401
402 // import the lines
403 String errorPathPrefix = null;
404 try {
405 if (isSource) {
406 errorPathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.SOURCE_ACCOUNTING_LINE_ERRORS;
407 FormFile sourceFile = tmpForm.getSourceFile();
408 checkUploadFile(sourceFile);
409 importedLines = accountingLineParser.importSourceAccountingLines(sourceFile.getFileName(), sourceFile.getInputStream(), financialDocument);
410 }
411 else {
412 errorPathPrefix = KFSConstants.DOCUMENT_PROPERTY_NAME + "." + KFSConstants.TARGET_ACCOUNTING_LINE_ERRORS;
413 FormFile targetFile = tmpForm.getTargetFile();
414 checkUploadFile(targetFile);
415 importedLines = accountingLineParser.importTargetAccountingLines(targetFile.getFileName(), targetFile.getInputStream(), financialDocument);
416 }
417 }
418 catch (AccountingLineParserException e) {
419 GlobalVariables.getMessageMap().putError(errorPathPrefix, e.getErrorKey(), e.getErrorParameters());
420 }
421
422 // add line to list for those lines which were successfully imported
423 if (importedLines != null) {
424 for (Iterator i = importedLines.iterator(); i.hasNext();) {
425 AccountingLine importedLine = (AccountingLine) i.next();
426 insertAccountingLine(isSource, tmpForm, importedLine);
427 }
428 }
429 }
430
431 protected void checkUploadFile(FormFile file) {
432 if (file == null) {
433 throw new AccountingLineParserException("invalid (null) upload file", KFSKeyConstants.ERROR_UPLOADFILE_NULL);
434 }
435 }
436
437 /**
438 * This method will add a TargetAccountingLine to a FinancialDocument. This assumes that the user presses the add button for a
439 * specific accounting line on the document and that the document is represented by a FinancialDocumentFormBase. It first
440 * validates the line for data integrity and then checks appropriate business rules.
441 *
442 * @param mapping
443 * @param form
444 * @param request
445 * @param response
446 * @return ActionForward
447 * @throws Exception
448 */
449 public ActionForward insertTargetLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
450 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form;
451 TargetAccountingLine line = financialDocumentForm.getNewTargetLine();
452
453 // populate chartOfAccountsCode from account number if accounts cant cross chart and Javascript is turned off
454 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line);
455
456 boolean rulePassed = true;
457 // before we check the regular rules we need to check the sales tax rules
458 // TODO: Refactor rules so we no longer have to call this before a copy of the
459 // accountingLine
460 rulePassed &= checkSalesTax((AccountingDocument) financialDocumentForm.getDocument(), line, false, true, 0);
461
462 // check any business rules
463 rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(KFSConstants.NEW_TARGET_ACCT_LINE_PROPERTY_NAME, financialDocumentForm.getDocument(), line));
464
465 // if the rule evaluation passed, let's add it
466 if (rulePassed) {
467 // add accountingLine
468 SpringContext.getBean(PersistenceService.class).refreshAllNonUpdatingReferences(line);
469 insertAccountingLine(false, financialDocumentForm, line);
470
471 // clear the used newTargetLine
472 financialDocumentForm.setNewTargetLine(null);
473 }
474
475 return mapping.findForward(KFSConstants.MAPPING_BASIC);
476 }
477
478
479 /**
480 * This action executes an insert of a SourceAccountingLine into a document only after validating the accounting line and
481 * checking any appropriate business rules.
482 *
483 * @param mapping
484 * @param form
485 * @param request
486 * @param response
487 * @return ActionForward
488 * @throws Exception
489 */
490 public ActionForward insertSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
491 KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form;
492 SourceAccountingLine line = financialDocumentForm.getNewSourceLine();
493
494 // populate chartOfAccountsCode from account number if accounts cant cross chart and Javascript is turned off
495 //SpringContext.getBean(AccountService.class).populateAccountingLineChartIfNeeded(line);
496
497 boolean rulePassed = true;
498 // before we check the regular rules we need to check the sales tax rules
499 // TODO: Refactor rules so we no longer have to call this before a copy of the
500 // accountingLine
501 rulePassed &= checkSalesTax((AccountingDocument) financialDocumentForm.getDocument(), line, true, true, 0);
502 // check any business rules
503 rulePassed &= SpringContext.getBean(KualiRuleService.class).applyRules(new AddAccountingLineEvent(KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME, financialDocumentForm.getDocument(), line));
504
505 if (rulePassed) {
506 // add accountingLine
507 SpringContext.getBean(PersistenceService.class).refreshAllNonUpdatingReferences(line);
508 insertAccountingLine(true, financialDocumentForm, line);
509
510 // clear the used newTargetLine
511 financialDocumentForm.setNewSourceLine(null);
512 }
513
514 return mapping.findForward(KFSConstants.MAPPING_BASIC);
515 }
516
517 /**
518 * Adds the given accountingLine to the appropriate form-related datastructures.
519 *
520 * @param isSource
521 * @param financialDocumentForm
522 * @param line
523 */
524 protected void insertAccountingLine(boolean isSource, KualiAccountingDocumentFormBase financialDocumentForm, AccountingLine line) {
525 AccountingDocument tdoc = financialDocumentForm.getFinancialDocument();
526 if (isSource) {
527 // add it to the document
528 tdoc.addSourceAccountingLine((SourceAccountingLine) line);
529
530 // add PK fields to sales tax if needed
531 if (line.isSalesTaxRequired()) {
532 populateSalesTax(line);
533 }
534
535 // Update the doc total
536 if (tdoc instanceof AmountTotaling)
537 ((FinancialSystemDocumentHeader) financialDocumentForm.getDocument().getDocumentHeader()).setFinancialDocumentTotalAmount(((AmountTotaling) tdoc).getTotalDollarAmount());
538 }
539 else {
540 // add it to the document
541 tdoc.addTargetAccountingLine((TargetAccountingLine) line);
542
543 // add PK fields to sales tax if needed
544 if (line.isSalesTaxRequired()) {
545 populateSalesTax(line);
546 }
547 }
548 }
549
550 /**
551 * TODO: remove this method once baseline accounting lines has been removed
552 */
553 protected List deepCopyAccountingLinesList(List originals) {
554 if (originals == null) {
555 return null;
556 }
557 List copiedLines = new ArrayList();
558 for (int i = 0; i < originals.size(); i++) {
559 copiedLines.add(ObjectUtils.deepCopy((AccountingLine) originals.get(i)));
560 }
561 return copiedLines;
562 }
563
564 /**
565 * This action changes the value of the hide field in the user interface so that when the page is rendered, the UI knows to show
566 * all of the labels for each of the accounting line values.
567 *
568 * @param mapping
569 * @param form
570 * @param request
571 * @param response
572 * @return ActionForward
573 * @throws Exception
574 */
575 public ActionForward showDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
576 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form;
577 tmpForm.setHideDetails(false);
578 return mapping.findForward(KFSConstants.MAPPING_BASIC);
579 }
580
581 /**
582 * This method is triggered when the user toggles the show/hide button to "hide" thus making the UI render without any of the
583 * accounting line labels/descriptions showing up underneath the values in the UI.
584 *
585 * @param mapping
586 * @param form
587 * @param request
588 * @param response
589 * @return ActionForward
590 * @throws Exception
591 */
592 public ActionForward hideDetails(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
593 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form;
594 tmpForm.setHideDetails(true);
595 return mapping.findForward(KFSConstants.MAPPING_BASIC);
596 }
597
598 /**
599 * Takes care of storing the action form in the User session and forwarding to the balance inquiry report menu action for a
600 * source accounting line.
601 *
602 * @param mapping
603 * @param form
604 * @param request
605 * @param response
606 * @return ActionForward
607 * @throws Exception
608 */
609 public ActionForward performBalanceInquiryForSourceLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
610 SourceAccountingLine line = this.getSourceAccountingLine(form, request);
611 return performBalanceInquiryForAccountingLine(mapping, form, request, line);
612 }
613
614 /**
615 * Takes care of storing the action form in the User session and forwarding to the balance inquiry report menu action for a
616 * target accounting line.
617 *
618 * @param mapping
619 * @param form
620 * @param request
621 * @param response
622 * @return ActionForward
623 * @throws Exception
624 */
625 public ActionForward performBalanceInquiryForTargetLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
626 int lineIndex = getSelectedLine(request);
627
628 TargetAccountingLine line = this.getTargetAccountingLine(form, request);
629
630 return performBalanceInquiryForAccountingLine(mapping, form, request, line);
631 }
632
633 /**
634 * This method is a helper method that will return a source accounting line. The reason we're making it protected in here is so
635 * that we can override this method in some of the modules. PurchasingActionBase is one of the subclasses that will be
636 * overriding this, because in PurchasingActionBase, we'll need to get the source accounting line using both an item index and
637 * an account index.
638 *
639 * @param form
640 * @param request
641 * @param isSource
642 * @return
643 */
644 protected SourceAccountingLine getSourceAccountingLine(ActionForm form, HttpServletRequest request) {
645 int lineIndex = getSelectedLine(request);
646 SourceAccountingLine line = (SourceAccountingLine) ObjectUtils.deepCopy(((KualiAccountingDocumentFormBase) form).getFinancialDocument().getSourceAccountingLine(lineIndex));
647 return line;
648 }
649
650 protected TargetAccountingLine getTargetAccountingLine(ActionForm form, HttpServletRequest request) {
651 int lineIndex = getSelectedLine(request);
652 TargetAccountingLine line = (TargetAccountingLine) ((KualiAccountingDocumentFormBase) form).getFinancialDocument().getTargetAccountingLine(lineIndex);
653
654 return line;
655 }
656
657 /**
658 * This method handles preparing all of the accounting line data so that it can be pushed up to the balance inquiries for
659 * populating the search criteria of each.
660 *
661 * @param mapping
662 * @param form
663 * @param request
664 * @param line
665 * @return ActionForward
666 */
667 protected ActionForward performBalanceInquiryForAccountingLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, AccountingLine line) {
668 // build out base path for return location
669 String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
670
671 // build out the actual form key that will be used to retrieve the form on refresh
672 String callerDocFormKey = GlobalVariables.getUserSession().addObject(form);
673
674 // now add required parameters
675 Properties parameters = new Properties();
676 parameters.put(KFSConstants.DISPATCH_REQUEST_PARAMETER, KFSConstants.START_METHOD);
677 // need this next param b/c the lookup's return back will overwrite
678 // the original doc form key
679 parameters.put(KFSConstants.BALANCE_INQUIRY_REPORT_MENU_CALLER_DOC_FORM_KEY, callerDocFormKey);
680 parameters.put(KFSConstants.DOC_FORM_KEY, callerDocFormKey);
681 parameters.put(KFSConstants.BACK_LOCATION, basePath + mapping.getPath() + ".do");
682
683 if (line.getPostingYear() != null) {
684 parameters.put(KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR, line.getPostingYear().toString());
685 }
686 if (StringUtils.isNotBlank(line.getReferenceOriginCode())) {
687 parameters.put("referenceOriginCode", line.getReferenceOriginCode());
688 }
689 if (StringUtils.isNotBlank(line.getReferenceNumber())) {
690 parameters.put("referenceNumber", line.getReferenceNumber());
691 }
692 if (StringUtils.isNotBlank(line.getReferenceTypeCode())) {
693 parameters.put("referenceTypeCode", line.getReferenceTypeCode());
694 }
695 if (StringUtils.isNotBlank(line.getDebitCreditCode())) {
696 parameters.put("debitCreditCode", line.getDebitCreditCode());
697 }
698 if (StringUtils.isNotBlank(line.getChartOfAccountsCode())) {
699 parameters.put("chartOfAccountsCode", line.getChartOfAccountsCode());
700 }
701 if (StringUtils.isNotBlank(line.getAccountNumber())) {
702 parameters.put("accountNumber", line.getAccountNumber());
703 }
704 if (StringUtils.isNotBlank(line.getFinancialObjectCode())) {
705 parameters.put("financialObjectCode", line.getFinancialObjectCode());
706 }
707 if (StringUtils.isNotBlank(line.getSubAccountNumber())) {
708 parameters.put("subAccountNumber", line.getSubAccountNumber());
709 }
710 if (StringUtils.isNotBlank(line.getFinancialSubObjectCode())) {
711 parameters.put("financialSubObjectCode", line.getFinancialSubObjectCode());
712 }
713 if (StringUtils.isNotBlank(line.getProjectCode())) {
714 parameters.put("projectCode", line.getProjectCode());
715 }
716 if (StringUtils.isNotBlank(getObjectTypeCodeFromLine(line))) {
717 if (!StringUtils.isBlank(line.getObjectTypeCode())) {
718 parameters.put("objectTypeCode", line.getObjectTypeCode());
719 }
720 else {
721 line.refreshReferenceObject("objectCode");
722 parameters.put("objectTypeCode", line.getObjectCode().getFinancialObjectTypeCode());
723 }
724 }
725
726 String lookupUrl = UrlFactory.parameterizeUrl(basePath + "/" + KFSConstants.BALANCE_INQUIRY_REPORT_MENU_ACTION, parameters);
727
728 // register that we're going to come back w/ to this form w/ a refresh methodToCall
729 ((KualiAccountingDocumentFormBase) form).registerEditableProperty(KNSConstants.DISPATCH_REQUEST_PARAMETER);
730
731 return new ActionForward(lookupUrl, true);
732 }
733
734 /**
735 * A hook so that most accounting lines - which don't have object types - can have their object type codes used in balance
736 * inquiries
737 *
738 * @param line the line to get the object type code from
739 * @return the object type code the line would use
740 */
741 protected String getObjectTypeCodeFromLine(AccountingLine line) {
742 line.refreshReferenceObject("objectCode");
743 return line.getObjectCode().getFinancialObjectTypeCode();
744 }
745
746 @Override
747 public ActionForward save(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
748 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form;
749 this.applyCapitalAssetInformation(tmpForm);
750
751 ActionForward forward = super.save(mapping, form, request, response);
752
753 // need to check on sales tax for all the accounting lines
754 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getSourceAccountingLines());
755 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getTargetAccountingLines());
756 return forward;
757 }
758
759 @Override
760 public ActionForward approve(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
761 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form;
762 this.applyCapitalAssetInformation(tmpForm);
763
764 ActionForward forward = super.approve(mapping, form, request, response);
765
766 // need to check on sales tax for all the accounting lines
767 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getSourceAccountingLines());
768 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getTargetAccountingLines());
769 return forward;
770 }
771
772 @Override
773 public ActionForward route(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
774 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form;
775 this.applyCapitalAssetInformation(tmpForm);
776
777 ActionForward forward = super.route(mapping, form, request, response);
778
779 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getSourceAccountingLines());
780 checkSalesTaxRequiredAllLines(tmpForm, tmpForm.getFinancialDocument().getTargetAccountingLines());
781
782 return forward;
783 }
784
785 @Override
786 public ActionForward blanketApprove(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
787 KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form;
788 this.applyCapitalAssetInformation(tmpForm);
789
790 ActionForward forward = super.blanketApprove(mapping, form, request, response);
791
792 return forward;
793 }
794
795 /**
796 * Encapsulate the rule check so we can call it from multiple places
797 *
798 * @param document
799 * @param line
800 * @return true if sales is either not required or it contains sales tax
801 */
802 protected boolean checkSalesTax(AccountingDocument document, AccountingLine line, boolean source, boolean newLine, int index) {
803 boolean passed = true;
804 if (isSalesTaxRequired(document, line)) {
805 // then set the salesTaxRequired on the accountingLine
806 line.setSalesTaxRequired(true);
807 populateSalesTax(line);
808 // check to see if the sales tax info has been put in
809 passed &= isValidSalesTaxEntered(line, source, newLine, index);
810 }
811 return passed;
812 }
813
814 /**
815 * This method checks to see if this doctype needs sales tax If it does then it checks to see if the account and object code
816 * require sales tax If it does then it returns true. Note - this is hackish as we shouldn't have to call rules directly from
817 * the action class But we need to in this instance because we are copying the lines before calling rules and need a way to
818 * modify them before they go on
819 *
820 * @param accountingLine
821 * @return true if sales tax check is needed, false otherwise
822 */
823 protected boolean isSalesTaxRequired(AccountingDocument financialDocument, AccountingLine accountingLine) {
824 boolean required = false;
825 String docType = SpringContext.getBean(DataDictionaryService.class).getDocumentTypeNameByClass(financialDocument.getClass());
826 // first we need to check just the doctype to see if it needs the sales tax check
827 ParameterService parameterService = SpringContext.getBean(ParameterService.class);
828 // apply the rule, see if it fails
829 ParameterEvaluator docTypeSalesTaxCheckEvaluator = SpringContext.getBean(ParameterService.class).getParameterEvaluator(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, APPLICATION_PARAMETER.DOCTYPE_SALES_TAX_CHECK, docType);
830 if (docTypeSalesTaxCheckEvaluator.evaluationSucceeds()) {
831 required = true;
832 }
833
834 // second we need to check the account and object code combination to see if it needs sales tax
835 if (required) {
836 // get the object code and account
837 String objCd = accountingLine.getFinancialObjectCode();
838 String account = accountingLine.getAccountNumber();
839 if (!StringUtils.isEmpty(objCd) && !StringUtils.isEmpty(account)) {
840 String compare = account + ":" + objCd;
841 ParameterEvaluator salesTaxApplicableAcctAndObjectEvaluator = SpringContext.getBean(ParameterService.class).getParameterEvaluator(KfsParameterConstants.FINANCIAL_PROCESSING_DOCUMENT.class, APPLICATION_PARAMETER.SALES_TAX_APPLICABLE_ACCOUNTS_AND_OBJECT_CODES, compare);
842 if (!salesTaxApplicableAcctAndObjectEvaluator.evaluationSucceeds()) {
843 required = false;
844 }
845 }
846 else {
847 // the two fields are currently empty and we don't need to check yet
848 required = false;
849 }
850 }
851 return required;
852 }
853
854 /**
855 * This method checks to see if the sales tax information was put into the accounting line
856 *
857 * @param accountingLine
858 * @return true if entered correctly, false otherwise
859 */
860 protected boolean isValidSalesTaxEntered(AccountingLine accountingLine, boolean source, boolean newLine, int index) {
861 boolean valid = true;
862 DictionaryValidationService dictionaryValidationService = SpringContext.getBean(DictionaryValidationService.class);
863 BusinessObjectService boService = SpringContext.getBean(BusinessObjectService.class);
864 String objCd = accountingLine.getFinancialObjectCode();
865 String account = accountingLine.getAccountNumber();
866 SalesTax salesTax = accountingLine.getSalesTax();
867 String pathPrefix = "";
868 if (source && !newLine) {
869 pathPrefix = "document." + KFSConstants.EXISTING_SOURCE_ACCT_LINE_PROPERTY_NAME + "[" + index + "]";
870 }
871 else if (!source && !newLine) {
872 pathPrefix = "document." + KFSConstants.EXISTING_TARGET_ACCT_LINE_PROPERTY_NAME + "[" + index + "]";
873 }
874 else if (source && newLine) {
875 pathPrefix = KFSConstants.NEW_SOURCE_ACCT_LINE_PROPERTY_NAME;
876 }
877 else if (!source && newLine) {
878 pathPrefix = KFSConstants.NEW_TARGET_ACCT_LINE_PROPERTY_NAME;
879 }
880 GlobalVariables.getMessageMap().addToErrorPath(pathPrefix);
881 if (ObjectUtils.isNull(salesTax)) {
882 valid &= false;
883 GlobalVariables.getMessageMap().putError("salesTax.chartOfAccountsCode", ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_REQUIRED, account, objCd);
884 }
885 else {
886
887 if (StringUtils.isBlank(salesTax.getChartOfAccountsCode())) {
888 valid &= false;
889 GlobalVariables.getMessageMap().putError("salesTax.chartOfAccountsCode", ERROR_REQUIRED, "Chart of Accounts");
890 }
891 if (StringUtils.isBlank(salesTax.getAccountNumber())) {
892 valid &= false;
893 GlobalVariables.getMessageMap().putError("salesTax.accountNumber", ERROR_REQUIRED, "Account Number");
894 }
895 if (salesTax.getFinancialDocumentGrossSalesAmount() == null) {
896 valid &= false;
897 GlobalVariables.getMessageMap().putError("salesTax.financialDocumentGrossSalesAmount", ERROR_REQUIRED, "Gross Sales Amount");
898 }
899 if (salesTax.getFinancialDocumentTaxableSalesAmount() == null) {
900 valid &= false;
901 GlobalVariables.getMessageMap().putError("salesTax.financialDocumentTaxableSalesAmount", ERROR_REQUIRED, "Taxable Sales Amount");
902 }
903 if (salesTax.getFinancialDocumentSaleDate() == null) {
904 valid &= false;
905 GlobalVariables.getMessageMap().putError("salesTax.financialDocumentSaleDate", ERROR_REQUIRED, "Sale Date");
906 }
907 if (StringUtils.isNotBlank(salesTax.getChartOfAccountsCode()) && StringUtils.isNotBlank(salesTax.getAccountNumber())) {
908
909 if (boService.getReferenceIfExists(salesTax, "account") == null) {
910 valid &= false;
911 GlobalVariables.getMessageMap().putError("salesTax.accountNumber", ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_INVALID_ACCOUNT, salesTax.getChartOfAccountsCode(), salesTax.getAccountNumber());
912
913 }
914 }
915 if (!valid) {
916 GlobalVariables.getMessageMap().putError("salesTax.chartOfAccountsCode", ERROR_DOCUMENT_ACCOUNTING_LINE_SALES_TAX_REQUIRED, account, objCd);
917 }
918 }
919 GlobalVariables.getMessageMap().removeFromErrorPath(pathPrefix);
920 return valid;
921 }
922
923 /**
924 * This method removes the sales tax information from a line that no longer requires it
925 *
926 * @param accountingLine
927 */
928 protected void removeSalesTax(AccountingLine accountingLine) {
929 SalesTax salesTax = accountingLine.getSalesTax();
930 if (ObjectUtils.isNotNull(salesTax)) {
931 accountingLine.setSalesTax(null);
932 }
933 }
934
935
936 /**
937 * This method checks to see if the given accounting needs sales tax and if it does it sets the salesTaxRequired variable on the
938 * line If it doesn't and it has it then it removes the sales tax information from the line This method is called from the
939 * execute() on all accounting lines that have been edited or lines that have already been added to the document, not on new
940 * lines
941 *
942 * @param transDoc
943 * @param formLine
944 * @param baseLine
945 */
946 protected void handleSalesTaxRequired(AccountingDocument transDoc, AccountingLine formLine, boolean source, boolean newLine, int index) {
947 boolean salesTaxRequired = isSalesTaxRequired(transDoc, formLine);
948 if (salesTaxRequired) {
949 formLine.setSalesTaxRequired(true);
950 populateSalesTax(formLine);
951 }
952 else if (!salesTaxRequired && hasSalesTaxBeenEntered(formLine, source, newLine, index)) {
953 // remove it if it has been added but is no longer required
954 removeSalesTax(formLine);
955 }
956 }
957
958 protected boolean hasSalesTaxBeenEntered(AccountingLine accountingLine, boolean source, boolean newLine, int index) {
959 boolean entered = true;
960 String objCd = accountingLine.getFinancialObjectCode();
961 String account = accountingLine.getAccountNumber();
962 SalesTax salesTax = accountingLine.getSalesTax();
963 if (ObjectUtils.isNull(salesTax)) {
964 return false;
965 }
966 if (StringUtils.isBlank(salesTax.getChartOfAccountsCode())) {
967 entered &= false;
968 }
969 if (StringUtils.isBlank(salesTax.getAccountNumber())) {
970 entered &= false;
971 }
972 if (salesTax.getFinancialDocumentGrossSalesAmount() == null) {
973 entered &= false;
974 }
975 if (salesTax.getFinancialDocumentTaxableSalesAmount() == null) {
976 entered &= false;
977 }
978 if (salesTax.getFinancialDocumentSaleDate() == null) {
979 entered &= false;
980 }
981 return entered;
982 }
983
984 /**
985 * This method is called from the createDocument and processes through all the accouting lines and checks to see if they need
986 * sales tax fields
987 *
988 * @param kualiDocumentFormBase
989 * @param baselineSourceLines
990 */
991 protected void handleSalesTaxRequiredAllLines(KualiDocumentFormBase kualiDocumentFormBase, List<AccountingLine> baselineAcctingLines) {
992 AccountingDocument accoutingDocument = (AccountingDocument) kualiDocumentFormBase.getDocument();
993 int index = 0;
994 for (AccountingLine accountingLine : baselineAcctingLines) {
995 boolean source = false;
996 if (accountingLine.isSourceAccountingLine()) {
997 source = true;
998 }
999 handleSalesTaxRequired(accoutingDocument, accountingLine, source, false, index);
1000 index++;
1001 }
1002
1003 }
1004
1005 protected boolean checkSalesTaxRequiredAllLines(KualiDocumentFormBase kualiDocumentFormBase, List<AccountingLine> baselineAcctingLines) {
1006 AccountingDocument accoutingDocument = (AccountingDocument) kualiDocumentFormBase.getDocument();
1007 boolean passed = true;
1008 int index = 0;
1009 for (AccountingLine accountingLine : baselineAcctingLines) {
1010 boolean source = false;
1011 if (accountingLine.isSourceAccountingLine()) {
1012 source = true;
1013 }
1014 passed &= checkSalesTax(accoutingDocument, accountingLine, source, false, index);
1015 index++;
1016 }
1017 return passed;
1018 }
1019
1020 /**
1021 * This method refreshes the sales tax fields on a refresh or tab toggle so that all the information that was there before is
1022 * still there after a state change
1023 *
1024 * @param form
1025 */
1026 protected void refreshSalesTaxInfo(ActionForm form) {
1027 KualiAccountingDocumentFormBase accountingForm = (KualiAccountingDocumentFormBase) form;
1028 AccountingDocument document = (AccountingDocument) accountingForm.getDocument();
1029 List sourceLines = document.getSourceAccountingLines();
1030 List targetLines = document.getTargetAccountingLines();
1031 handleSalesTaxRequiredAllLines(accountingForm, sourceLines);
1032 handleSalesTaxRequiredAllLines(accountingForm, targetLines);
1033
1034 AccountingLine newTargetLine = accountingForm.getNewTargetLine();
1035 AccountingLine newSourceLine = accountingForm.getNewSourceLine();
1036 if (newTargetLine != null) {
1037 handleSalesTaxRequired(document, newTargetLine, false, true, 0);
1038 }
1039 if (newSourceLine != null) {
1040 handleSalesTaxRequired(document, newSourceLine, true, true, 0);
1041 }
1042 }
1043
1044 /**
1045 * This method populates the sales tax for a given accounting line with the appropriate primary key fields from the accounting
1046 * line since OJB won't do it automatically for us
1047 *
1048 * @param line
1049 */
1050 protected void populateSalesTax(AccountingLine line) {
1051 SalesTax salesTax = line.getSalesTax();
1052
1053 if (ObjectUtils.isNotNull(salesTax)) {
1054 salesTax.setDocumentNumber(line.getDocumentNumber());
1055 salesTax.setFinancialDocumentLineTypeCode(line.getFinancialDocumentLineTypeCode());
1056 salesTax.setFinancialDocumentLineNumber(line.getSequenceNumber());
1057 }
1058 }
1059
1060 @Override
1061 public ActionForward performLookup(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1062 // parse out the business object name from our methodToCall parameter
1063 String fullParameter = (String) request.getAttribute(KFSConstants.METHOD_TO_CALL_ATTRIBUTE);
1064 String boClassName = StringUtils.substringBetween(fullParameter, KFSConstants.METHOD_TO_CALL_BOPARM_LEFT_DEL, KFSConstants.METHOD_TO_CALL_BOPARM_RIGHT_DEL);
1065
1066 if (!StringUtils.equals(boClassName, GeneralLedgerPendingEntry.class.getName())) {
1067 return super.performLookup(mapping, form, request, response);
1068 }
1069
1070 String path = super.performLookup(mapping, form, request, response).getPath();
1071 path = path.replaceFirst(KFSConstants.LOOKUP_ACTION, KFSConstants.GL_MODIFIED_INQUIRY_ACTION);
1072
1073 return new ActionForward(path, true);
1074 }
1075
1076 /**
1077 * clear up the capital asset information
1078 */
1079 public ActionForward clearCapitalAssetInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1080 LOG.debug("clearCapitalAssetInfo() - start");
1081
1082 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form;
1083 CapitalAssetInformation capitalAssetInformation = this.getCurrentCapitalAssetInformationObject(kualiAccountingDocumentFormBase);
1084 if (capitalAssetInformation == null) {
1085 return mapping.findForward(KFSConstants.MAPPING_BASIC);
1086 }
1087
1088 this.resetCapitalAssetInfo(capitalAssetInformation);
1089
1090 return mapping.findForward(KFSConstants.MAPPING_BASIC);
1091 }
1092
1093 /**
1094 * add the capital asset information
1095 */
1096 public ActionForward addCapitalAssetInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1097 LOG.debug("addCapitalAssetInfoDetail() - start");
1098
1099 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form;
1100 CapitalAssetInformation capitalAssetInformation = this.getCurrentCapitalAssetInformationObject(kualiAccountingDocumentFormBase);
1101 if (capitalAssetInformation == null) {
1102 return mapping.findForward(KFSConstants.MAPPING_BASIC);
1103 }
1104
1105 this.addCapitalAssetInfoDetailLines(capitalAssetInformation);
1106
1107 return mapping.findForward(KFSConstants.MAPPING_BASIC);
1108 }
1109
1110 /**
1111 * delete a detail line from the capital asset information
1112 */
1113 public ActionForward deleteCapitalAssetInfoDetailLine(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1114 LOG.debug("deleteCapitalAssetDetailInfo() - start");
1115
1116 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form;
1117 CapitalAssetInformation capitalAssetInformation = this.getCurrentCapitalAssetInformationObject(kualiAccountingDocumentFormBase);
1118 if (capitalAssetInformation == null) {
1119 return mapping.findForward(KFSConstants.MAPPING_BASIC);
1120 }
1121
1122 int lineToDelete = this.getLineToDelete(request);
1123 List<CapitalAssetInformationDetail> detailLines = capitalAssetInformation.getCapitalAssetInformationDetails();
1124
1125 detailLines.remove(lineToDelete);
1126
1127 return mapping.findForward(KFSConstants.MAPPING_BASIC);
1128 }
1129
1130 /**
1131 * get the capital asset information object currently associated with the document
1132 */
1133 protected CapitalAssetInformation getCurrentCapitalAssetInformationObject(KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase) {
1134 LOG.debug("getCurrentCapitalAssetInformationObject() - start");
1135
1136 AccountingDocument financialDocument = kualiAccountingDocumentFormBase.getFinancialDocument();
1137 if (!(financialDocument instanceof CapitalAssetEditable) || !(kualiAccountingDocumentFormBase instanceof CapitalAssetEditable)) {
1138 return null;
1139 }
1140
1141 CapitalAssetEditable capitalAssetEditable = (CapitalAssetEditable) financialDocument;
1142 CapitalAssetInformation capitalAssetInformation = capitalAssetEditable.getCapitalAssetInformation();
1143 if (ObjectUtils.isNotNull(capitalAssetInformation)) {
1144 return capitalAssetInformation;
1145 }
1146
1147 CapitalAssetEditable capitalAssetEditableForm = (CapitalAssetEditable) kualiAccountingDocumentFormBase;
1148 CapitalAssetInformation newCapitalAssetInformation = capitalAssetEditableForm.getCapitalAssetInformation();
1149
1150 return newCapitalAssetInformation;
1151 }
1152
1153 /**
1154 * add detail lines into the given capital asset information
1155 *
1156 * @param capitalAssetInformation the given capital asset information
1157 */
1158 protected void addCapitalAssetInfoDetailLines(CapitalAssetInformation capitalAssetInformation) {
1159 LOG.debug("addCapitalAssetInfoDetailLines() - start");
1160
1161 if (ObjectUtils.isNull(capitalAssetInformation)) {
1162 return;
1163 }
1164
1165 Integer quantity = capitalAssetInformation.getCapitalAssetQuantity();
1166 if (quantity == null || quantity <= 0) {
1167 String errorPath = KFSPropertyConstants.DOCUMENT + "." + KFSPropertyConstants.CAPITAL_ASSET_INFORMATION;
1168 GlobalVariables.getMessageMap().putError(errorPath, KFSKeyConstants.ERROR_INVALID_CAPITAL_ASSET_QUANTITY);
1169 return;
1170 }
1171
1172 List<CapitalAssetInformationDetail> detailLines = capitalAssetInformation.getCapitalAssetInformationDetails();
1173 // If details collection has old lines, this loop will add new lines to make the total equal to the quantity.
1174 for (int index = 1; detailLines.size() < quantity; index++) {
1175 CapitalAssetInformationDetail detailLine = new CapitalAssetInformationDetail();
1176 detailLine.setItemLineNumber(getNextItemLineNumberAndIncremented(capitalAssetInformation));
1177 detailLines.add(detailLine);
1178 }
1179 }
1180
1181 /**
1182 * Get next available item line number. If it's already stored in the session, pick it up and increment by 1. Otherwise get it
1183 * from the DB and save it to session.
1184 *
1185 * @param capitalAssetInformation
1186 * @return
1187 */
1188 protected Integer getNextItemLineNumberAndIncremented(CapitalAssetInformation capitalAssetInformation) {
1189 Integer nextItemLineNumber = capitalAssetInformation.getNextItemLineNumber();
1190 if (nextItemLineNumber == null) {
1191 nextItemLineNumber = new Integer(getMaxItemLineNumber(capitalAssetInformation) + 1);
1192 }
1193 capitalAssetInformation.setNextItemLineNumber(new Integer(nextItemLineNumber.intValue() + 1));
1194 return nextItemLineNumber;
1195 }
1196
1197 /**
1198 * Get the maximum item line number from DB.
1199 *
1200 * @param capitalAssetInformation
1201 * @return
1202 */
1203 protected int getMaxItemLineNumber(CapitalAssetInformation capitalAssetInformation) {
1204 int maxItemLineNumber = 0;
1205 if (ObjectUtils.isNotNull(capitalAssetInformation)) {
1206 List<CapitalAssetInformationDetail> detailLines = capitalAssetInformation.getCapitalAssetInformationDetails();
1207
1208 if (detailLines != null && !detailLines.isEmpty()) {
1209 maxItemLineNumber = detailLines.size();
1210 }
1211
1212 Map<String, Object> fieldValues = new HashMap<String, Object>();
1213 fieldValues.put(KFSPropertyConstants.DOCUMENT_NUMBER, capitalAssetInformation.getDocumentNumber());
1214 List<CapitalAssetInformationDetail> perisitentDetails = (List<CapitalAssetInformationDetail>) getBusinessObjectService().findMatching(CapitalAssetInformationDetail.class, fieldValues);
1215 for (CapitalAssetInformationDetail persistentDetail : perisitentDetails) {
1216 if (persistentDetail.getItemLineNumber().intValue() > maxItemLineNumber) {
1217 maxItemLineNumber = persistentDetail.getItemLineNumber().intValue();
1218 }
1219 }
1220 }
1221 return maxItemLineNumber;
1222 }
1223
1224 /**
1225 * reset the nonkey fields of the given capital asset information
1226 *
1227 * @param capitalAssetInformation the given capital asset information
1228 */
1229 protected void resetCapitalAssetInfo(CapitalAssetInformation capitalAssetInformation) {
1230 if (capitalAssetInformation != null) {
1231 capitalAssetInformation.setCapitalAssetDescription(null);
1232 capitalAssetInformation.setCapitalAssetManufacturerModelNumber(null);
1233 capitalAssetInformation.setCapitalAssetManufacturerName(null);
1234
1235 capitalAssetInformation.setCapitalAssetNumber(null);
1236 capitalAssetInformation.setCapitalAssetTypeCode(null);
1237 capitalAssetInformation.setCapitalAssetQuantity(null);
1238
1239 capitalAssetInformation.setVendorDetailAssignedIdentifier(null);
1240 capitalAssetInformation.setVendorHeaderGeneratedIdentifier(null);
1241 // Set the BO to null cause it won't be updated automatically when vendorDetailAssetIdentifier and
1242 // VendorHeanderGeneratedIndentifier set to null.
1243 capitalAssetInformation.setVendorDetail(null);
1244 capitalAssetInformation.setVendorName(null);
1245
1246 capitalAssetInformation.getCapitalAssetInformationDetails().clear();
1247 }
1248 }
1249
1250 // assoicate the new capital asset information with the current document if any
1251 protected void applyCapitalAssetInformation(KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase) {
1252 LOG.debug("applyCapitalAssetInformation() - start");
1253
1254 // do nothing if the given document is not required to have capital asset collection
1255 AccountingDocument document = kualiAccountingDocumentFormBase.getFinancialDocument();
1256 if (!(document instanceof CapitalAssetEditable)) {
1257 return;
1258 }
1259
1260 // do nothing if there exists capital asset information associated with the current document
1261 CapitalAssetEditable capitalAssetEditable = (CapitalAssetEditable) document;
1262 CapitalAssetInformation capitalAssetInformation = capitalAssetEditable.getCapitalAssetInformation();
1263 if (capitalAssetInformation != null || !(kualiAccountingDocumentFormBase instanceof CapitalAssetEditable)) {
1264 return;
1265 }
1266
1267 CapitalAssetEditable capitalAssetEditableForm = (CapitalAssetEditable) kualiAccountingDocumentFormBase;
1268 CapitalAssetInformation newCapitalAssetInformation = capitalAssetEditableForm.getCapitalAssetInformation();
1269 // apply capitalAsset information if there is at least one movable object code associated with the source accounting
1270 // lines
1271 newCapitalAssetInformation.setDocumentNumber(document.getDocumentNumber());
1272 capitalAssetEditable.setCapitalAssetInformation(newCapitalAssetInformation);
1273 }
1274
1275 /**
1276 * Overridden to guarantee that form of copied document is set to whatever the entry mode of the document is
1277 * @see org.kuali.rice.kns.web.struts.action.KualiTransactionalDocumentActionBase#copy(org.apache.struts.action.ActionMapping, org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
1278 */
1279 @Override
1280 public ActionForward copy(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
1281 ActionForward forward = super.copy(mapping, form, request, response);
1282
1283 // if the copied document has capital asset collection, remove the collection
1284 KualiAccountingDocumentFormBase kualiAccountingDocumentFormBase = (KualiAccountingDocumentFormBase) form;
1285 AccountingDocument document = kualiAccountingDocumentFormBase.getFinancialDocument();
1286 if (document instanceof CapitalAssetEditable) {
1287
1288 CapitalAssetEditable capitalAssetEditable = (CapitalAssetEditable) document;
1289 resetCapitalAssetInfo(capitalAssetEditable.getCapitalAssetInformation());
1290 }
1291
1292 return forward;
1293 }
1294
1295 }