View Javadoc
1   /*
2    * Copyright 2008 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.module.purap.document.authorization;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.ole.module.purap.PurapParameterConstants;
20  import org.kuali.ole.module.purap.PurapPropertyConstants;
21  import org.kuali.ole.module.purap.businessobject.PurApAccountingLine;
22  import org.kuali.ole.module.purap.businessobject.PurApItem;
23  import org.kuali.ole.module.purap.document.*;
24  import org.kuali.ole.module.purap.document.service.PurapService;
25  import org.kuali.ole.sys.businessobject.AccountingLine;
26  import org.kuali.ole.sys.context.SpringContext;
27  import org.kuali.ole.sys.document.AccountingDocument;
28  import org.kuali.ole.sys.document.authorization.AccountingLineAuthorizerBase;
29  import org.kuali.ole.sys.document.authorization.FinancialSystemTransactionalDocumentAuthorizerBase;
30  import org.kuali.ole.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationController;
31  import org.kuali.ole.sys.document.web.AccountingLineRenderingContext;
32  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
33  import org.kuali.rice.kim.api.identity.Person;
34  import org.kuali.rice.kns.datadictionary.TransactionalDocumentEntry;
35  import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
36  import org.kuali.rice.kns.service.DataDictionaryService;
37  import org.kuali.rice.krad.document.DocumentPresentationController;
38  
39  import java.util.ArrayList;
40  import java.util.Collection;
41  import java.util.List;
42  
43  /**
44   * Authorizer which deals with financial processing document issues, specifically sales tax lines on documents
45   * This class utilizes the new accountingLine model.
46   */
47  public class PurapAccountingLineAuthorizer extends AccountingLineAuthorizerBase {
48  
49      /**
50       * Overrides the method in AccountingLineAuthorizerBase so that the add button would
51       * have the line item number in addition to the rest of the insertxxxx String for
52       * methodToCall when the user clicks on the add button.
53       *
54       * @param accountingLine
55       * @param accountingLineProperty
56       * @return
57       */
58      @Override
59      protected String getAddMethod(AccountingLine accountingLine, String accountingLineProperty) {
60          final String infix = getActionInfixForNewAccountingLine(accountingLine, accountingLineProperty);
61          String lineNumber = null;
62          if (accountingLineProperty.equals(PurapPropertyConstants.ACCOUNT_DISTRIBUTION_NEW_SRC_LINE)) {
63              lineNumber = "-2";
64          } else {
65              lineNumber = StringUtils.substringBetween(accountingLineProperty, "[", "]");
66          }
67          return "insert" + infix + "Line.line" + lineNumber + "." + "anchoraccounting" + infix + "Anchor";
68      }
69  
70      /**
71       * Overrides the method in AccountingLineAuthorizerBase so that the delete button would have both
72       * the line item number and the accounting line number for methodToCall when the user clicks on
73       * the delete button.
74       *
75       * @see org.kuali.ole.sys.document.authorization.AccountingLineAuthorizerBase#getDeleteLineMethod(org.kuali.ole.sys.businessobject.AccountingLine, java.lang.String, java.lang.Integer)
76       */
77      @Override
78      protected String getDeleteLineMethod(AccountingLine accountingLine, String accountingLineProperty, Integer accountingLineIndex) {
79          final String infix = getActionInfixForExtantAccountingLine(accountingLine, accountingLineProperty);
80          String lineNumber = StringUtils.substringBetween(accountingLineProperty, "item[", "].sourceAccountingLine");
81          if (lineNumber == null) {
82              lineNumber = "-2";
83          }
84          String accountingLineNumber = StringUtils.substringBetween(accountingLineProperty, "sourceAccountingLine[", "]");
85          return "delete" + infix + "Line.line" + lineNumber + ":" + accountingLineNumber + ".anchoraccounting" + infix + "Anchor";
86      }
87  
88      /**
89       * Overrides the method in AccountingLineAuthorizerBase so that the balance inquiry button would
90       * have both the line item number and the accounting line number for methodToCall when the user
91       * clicks on the balance inquiry button.
92       *
93       * @see org.kuali.ole.sys.document.authorization.AccountingLineAuthorizerBase#getBalanceInquiryMethod(org.kuali.ole.sys.businessobject.AccountingLine, java.lang.String, java.lang.Integer)
94       */
95      @Override
96      protected String getBalanceInquiryMethod(AccountingLine accountingLine, String accountingLineProperty, Integer accountingLineIndex) {
97          final String infix = getActionInfixForNewAccountingLine(accountingLine, accountingLineProperty);
98          String lineNumber = StringUtils.substringBetween(accountingLineProperty, "item[", "].sourceAccountingLine");
99          if (lineNumber == null) {
100             lineNumber = "-2";
101         }
102         String accountingLineNumber = StringUtils.substringBetween(accountingLineProperty, "sourceAccountingLine[", "]");
103         return "performBalanceInquiryFor" + infix + "Line.line" + ":" + lineNumber + ":" + accountingLineNumber + ".anchoraccounting" + infix + "existingLineLineAnchor" + accountingLineNumber;
104     }
105 
106     // Commented for Add new column "Dollar $" between Org Ref Id and Percent Columns on the Accounting Lines Tab. Ref:OLE-2042
107 
108     /**
109      * @see org.kuali.ole.sys.document.authorization.AccountingLineAuthorizerBase#getUnviewableBlocks(org.kuali.ole.sys.document.AccountingDocument, org.kuali.ole.sys.businessobject.AccountingLine, boolean, org.kuali.rice.kim.api.identity.Person)
110      */
111    /* @Override
112     public Set<String> getUnviewableBlocks(AccountingDocument accountingDocument, AccountingLine accountingLine, boolean newLine, Person currentUser) {
113         Set<String> unviewableBlocks = super.getUnviewableBlocks(accountingDocument, accountingLine, newLine, currentUser);
114         if (showAmountOnly(accountingDocument)) {
115             unviewableBlocks.add(OLEPropertyConstants.PERCENT);
116         }
117         else {
118             unviewableBlocks.add(OLEPropertyConstants.AMOUNT);
119         }
120         return unviewableBlocks;
121     }*/
122     private boolean showAmountOnly(AccountingDocument accountingDocument) {
123         PurapService purapService = SpringContext.getBean(PurapService.class);
124         if (accountingDocument instanceof PurchasingAccountsPayableDocument)
125             if (purapService.isFullDocumentEntryCompleted((PurchasingAccountsPayableDocument) accountingDocument)) {
126                 return true;
127             }
128         return false;
129     }
130 
131     /**
132      * @param accountingDocument
133      * @return
134      */
135     private FinancialSystemTransactionalDocumentPresentationController getPresentationController(AccountingDocument accountingDocument) {
136         final Class<? extends DocumentPresentationController> presentationControllerClass = ((TransactionalDocumentEntry) SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getDictionaryObjectEntry(accountingDocument.getClass().getName())).getDocumentPresentationControllerClass();
137         FinancialSystemTransactionalDocumentPresentationController presentationController = null;
138         try {
139             presentationController = (FinancialSystemTransactionalDocumentPresentationController) presentationControllerClass.newInstance();
140         } catch (InstantiationException ie) {
141             throw new RuntimeException("Cannot instantiate instance of presentation controller for " + accountingDocument.getClass().getName(), ie);
142         } catch (IllegalAccessException iae) {
143             throw new RuntimeException("Cannot instantiate instance of presentation controller for " + accountingDocument.getClass().getName(), iae);
144         }
145         return presentationController;
146     }
147 
148     /**
149      * @param accountingDocument
150      * @return
151      */
152     protected FinancialSystemTransactionalDocumentAuthorizerBase getDocumentAuthorizer(AccountingDocument accountingDocument) {
153         final Class<? extends DocumentAuthorizer> documentAuthorizerClass = ((TransactionalDocumentEntry) SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getDictionaryObjectEntry(accountingDocument.getClass().getName())).getDocumentAuthorizerClass();
154         FinancialSystemTransactionalDocumentAuthorizerBase documentAuthorizer = null;
155         try {
156             documentAuthorizer = (FinancialSystemTransactionalDocumentAuthorizerBase) documentAuthorizerClass.newInstance();
157         } catch (InstantiationException ie) {
158             throw new RuntimeException("Cannot instantiate instance of document authorizer for " + accountingDocument.getClass().getName(), ie);
159         } catch (IllegalAccessException iae) {
160             throw new RuntimeException("Cannot instantiate instance of document authorizer for " + accountingDocument.getClass().getName(), iae);
161         }
162         return documentAuthorizer;
163     }
164 
165     @Override
166     public boolean isGroupEditable(AccountingDocument accountingDocument,
167                                    List<? extends AccountingLineRenderingContext> accountingLineRenderingContexts,
168                                    Person currentUser) {
169 
170         boolean isEditable = super.isGroupEditable(accountingDocument, accountingLineRenderingContexts, currentUser);
171 
172         if (isEditable) {
173             if (accountingLineRenderingContexts.size() == 0) {
174                 return false;
175             }
176             isEditable = allowAccountingLinesAreEditable(accountingDocument, accountingLineRenderingContexts.get(0).getAccountingLine());
177         }
178 
179         return isEditable;
180     }
181 
182     @Override
183     public boolean determineEditPermissionOnField(AccountingDocument accountingDocument,
184                                                   AccountingLine accountingLine,
185                                                   String accountingLineCollectionProperty,
186                                                   String fieldName,
187                                                   boolean editablePage) {
188 
189         boolean isEditable = super.determineEditPermissionOnField(accountingDocument, accountingLine, accountingLineCollectionProperty, fieldName, editablePage);
190 
191         if (isEditable) {
192             isEditable = allowAccountingLinesAreEditable(accountingDocument, accountingLine);
193         }
194 
195         return isEditable;
196     }
197 
198     @Override
199     public boolean determineEditPermissionOnLine(AccountingDocument accountingDocument,
200                                                  AccountingLine accountingLine,
201                                                  String accountingLineCollectionProperty,
202                                                  boolean currentUserIsDocumentInitiator,
203                                                  boolean pageIsEditable) {
204 
205         boolean isEditable = super.determineEditPermissionOnLine(accountingDocument, accountingLine, accountingLineCollectionProperty, currentUserIsDocumentInitiator, pageIsEditable);
206 
207         if (isEditable) {
208             isEditable = allowAccountingLinesAreEditable(accountingDocument, accountingLine);
209         }
210 
211         return (isEditable && pageIsEditable);
212     }
213 
214     /**
215      * This method checks whether the accounting lines are editable for a specific item type.
216      */
217     protected boolean allowAccountingLinesAreEditable(AccountingDocument accountingDocument,
218                                                       AccountingLine accountingLine) {
219 
220         PurApAccountingLine purapAccount = (PurApAccountingLine) accountingLine;
221         @SuppressWarnings("rawtypes")
222         Class clazz = getPurapDocumentClass(accountingDocument);
223         if (clazz == null) {
224             return true;
225         }
226 
227         //if not calculated yet then the line is editable
228         PurchasingAccountsPayableDocumentBase purapDoc = (PurchasingAccountsPayableDocumentBase) accountingDocument;
229         if (!purapDoc.isCalculated()) {
230             return true;
231         }
232 
233         Collection<String> restrictedItemTypesList = new ArrayList<String>(SpringContext.getBean(ParameterService.class).getParameterValuesAsString(clazz, PurapParameterConstants.PURAP_ITEM_TYPES_RESTRICTING_ACCOUNT_EDIT));
234 
235         // This call determines a new special case in which an item marked for trade-in cannot have editable accounting lines
236         // once the calculate button image is clicked, even if the accounting line has not been saved yet.
237         boolean retval = true;
238         retval = isEditableBasedOnTradeInRestriction(accountingDocument, accountingLine);
239 
240         if (restrictedItemTypesList != null && purapAccount.getPurapItem() != null) {
241             return (!restrictedItemTypesList.contains(((PurApItem) purapAccount.getPurapItem()).getItemTypeCode()) && retval);
242         } else if (restrictedItemTypesList != null && purapAccount.getPurapItem() == null) {
243             return retval;
244         } else {
245             return true;
246         }
247     }
248 
249     /**
250      * Find the item to which an accounting line belongs. Convenience/Utility method.
251      * <p/>
252      * Some methods that require an accounting line with a purApItem attached were getting accounting lines
253      * passed in that did not yet have a purApItem. I needed a way to match the accounting line to the
254      * proper item.
255      *
256      * @param accountingDocument the document holding both the accounting line and the item to which the
257      *                           accounting line is attached
258      * @param accountingLine     the accounting line of interest, for which a containing item should be found
259      * @return the item to which the incoming accounting line is attached
260      */
261     protected PurApItem findTheItemForAccountingLine(AccountingDocument accountingDocument, AccountingLine accountingLine) {
262         PurApItem retval = null;
263         List<PurApItem> listItems = null;
264 
265         scan:
266         {
267             if (accountingDocument instanceof PurchasingAccountsPayableDocumentBase) {
268                 listItems = ((PurchasingAccountsPayableDocumentBase) accountingDocument).getItems();
269 
270                 // loop through all items in the document to see if the item has an accounting line that
271                 // matches the one passed in
272                 for (PurApItem oneItem : listItems) {
273                     List<PurApAccountingLine> acctLines = oneItem.getSourceAccountingLines();
274                     for (PurApAccountingLine oneAcctLine : acctLines) {
275                         // we want to compare the exact same memory location, not the contents
276                         if (oneAcctLine == accountingLine) {
277                             retval = oneItem;
278 
279                             // we found it, so I can stop altogether.
280                             break scan;
281                         }
282                     }
283                 }
284             }
285         }
286 
287         return retval;
288     }
289 
290     /**
291      * Handles a restriction on accounting lines assigned to trade-in items.
292      * If the accounting Line is for a trade-in item type, and the accounting line has contents,
293      * the user is not allowed to change the contents of the calculated values.
294      * <p/>
295      * The trade-in may not yet have a sequence number, so the old way of relying solely on sequence
296      * number (in method super.approvedForUnqualifiedEditing() is incomplete, and needs this extra check
297      * for trade-ins.
298      *
299      * @param accountingLine the accounting line being examined
300      * @return whether the accounting line is editable according to the trade-in/non-empty restriction
301      */
302     private boolean isEditableBasedOnTradeInRestriction(AccountingDocument accountingDocument, AccountingLine accountingLine) {
303         boolean retval = true;
304         // if the accounting Line is for a trade-In, and the line has contents, the user is not allowed to
305         // change the contents of the calculated values
306         if ((accountingLine != null) && (accountingLine instanceof PurApAccountingLine)) {
307             PurApItem purApItem = (((PurApAccountingLine) accountingLine)).getPurapItem();
308 
309             // this small block is to allow for another way to get to the purApItem if the
310             // incoming accounting line does not yet have a purApItem attached. Calling it is not
311             // completely necessary any more, unless/until the functional team members decide to
312             // add more item types to the read-only accounting lines list.
313             if (purApItem == null) {
314                 purApItem = findTheItemForAccountingLine(accountingDocument, accountingLine);
315             }
316 
317             if (purApItem != null) {
318                 String itemTypeCode = purApItem.getItemTypeCode();
319                 if (itemTypeCode.toUpperCase().equalsIgnoreCase(PurapParameterConstants.PURAP_ITEM_TYPE_TRDI)) {
320                     // does the line have any contents? if so, then the user cannot edit them
321                     if ((accountingLine.getChartOfAccountsCode() != null) || (accountingLine.getAccountNumber() != null) || (accountingLine.getFinancialObjectCode() != null)) {
322                         retval = false;
323                     }
324                 }
325                 // there has been a call to "if (purApItem.getItemAssignedToTradeInIndicator()) {" here
326                 // that required the earlier use of findTheItemForAccountingLine()
327             }
328         }
329         return retval;
330     }
331 
332     @SuppressWarnings("rawtypes")
333     private Class getPurapDocumentClass(AccountingDocument accountingDocument) {
334         if (accountingDocument instanceof RequisitionDocument) {
335             return RequisitionDocument.class;
336         } else if (accountingDocument instanceof PurchaseOrderDocument) {
337             return PurchaseOrderDocument.class;
338         } else if (accountingDocument instanceof PaymentRequestDocument) {
339             return PaymentRequestDocument.class;
340         } else {
341             return null;
342         }
343     }
344 
345     /**
346      * Determines if the given line is editable, no matter what a KIM check would say about line editability.  In the default case,
347      * any accounting line is editable - minus KIM check - when the document is PreRoute, or if the line is a new line
348      * <p/>
349      * This overriding implementation is required because the Purap module has a new restriction that requires
350      * that an accounting line for a Trade-In item cannot be manually editable, even if not yet saved ("not yet saved" means it has
351      * no sequence number). Therefore, the base implementation that determines editability on the sequence number alone has to be
352      * preceded by a check that declares ineligible for editing if it is a trade-in.
353      *
354      * @param accountingDocument             the accounting document the line is or wants to be associated with
355      * @param accountingLine                 the accounting line itself
356      * @param accountingLineCollectionProperty
357      *                                       the collection the accounting line is or would be part of
358      * @param currentUserIsDocumentInitiator is the current user the initiator of the document?
359      * @return true if the line as a whole can be edited without the KIM check, false otherwise
360      * @see org.kuali.ole.module.purap.document.authorization.PurapAccountingLineAuthorizer#allowAccountingLinesAreEditable(AccountingDocument, AccountingLine)
361      */
362     @Override
363     protected boolean approvedForUnqualifiedEditing(AccountingDocument accountingDocument, AccountingLine accountingLine, String accountingLineCollectionProperty, boolean currentUserIsDocumentInitiator) {
364         boolean retval = true;
365 
366         retval = isEditableBasedOnTradeInRestriction(accountingDocument, accountingLine);
367 
368         if (retval) {
369             retval = super.approvedForUnqualifiedEditing(accountingDocument, accountingLine, accountingLineCollectionProperty, currentUserIsDocumentInitiator);
370         }
371         return retval;
372     }
373 }
374