org.kuali.kfs.module.purap.document.authorization.PurapAccountingLineAuthorizer.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.kfs.module.purap.document.authorization.PurapAccountingLineAuthorizer.java

Source

/*
 * The Kuali Financial System, a comprehensive financial management system for higher education.
 * 
 * Copyright 2005-2014 The Kuali Foundation
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kuali.kfs.module.purap.document.authorization;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.kuali.kfs.module.purap.PurapParameterConstants;
import org.kuali.kfs.module.purap.PurapPropertyConstants;
import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine;
import org.kuali.kfs.module.purap.businessobject.PurApItem;
import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument;
import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocumentBase;
import org.kuali.kfs.module.purap.document.RequisitionDocument;
import org.kuali.kfs.module.purap.document.service.PurapService;
import org.kuali.kfs.sys.KFSPropertyConstants;
import org.kuali.kfs.sys.businessobject.AccountingLine;
import org.kuali.kfs.sys.context.SpringContext;
import org.kuali.kfs.sys.document.AccountingDocument;
import org.kuali.kfs.sys.document.authorization.AccountingLineAuthorizerBase;
import org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentAuthorizerBase;
import org.kuali.kfs.sys.document.authorization.FinancialSystemTransactionalDocumentPresentationController;
import org.kuali.kfs.sys.document.web.AccountingLineRenderingContext;
import org.kuali.rice.coreservice.framework.parameter.ParameterService;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kns.datadictionary.TransactionalDocumentEntry;
import org.kuali.rice.kns.document.authorization.DocumentAuthorizer;
import org.kuali.rice.kns.service.DataDictionaryService;
import org.kuali.rice.krad.document.DocumentPresentationController;

/**
 * Authorizer which deals with financial processing document issues, specifically sales tax lines on documents
 * This class utilizes the new accountingLine model.
 */
public class PurapAccountingLineAuthorizer extends AccountingLineAuthorizerBase {

    /**
     * Overrides the method in AccountingLineAuthorizerBase so that the add button would
     * have the line item number in addition to the rest of the insertxxxx String for
     * methodToCall when the user clicks on the add button.
     *
     * @param accountingLine
     * @param accountingLineProperty
     * @return
     */
    @Override
    protected String getAddMethod(AccountingLine accountingLine, String accountingLineProperty) {
        final String infix = getActionInfixForNewAccountingLine(accountingLine, accountingLineProperty);
        String lineNumber = null;
        if (accountingLineProperty.equals(PurapPropertyConstants.ACCOUNT_DISTRIBUTION_NEW_SRC_LINE)) {
            lineNumber = "-2";
        } else {
            lineNumber = StringUtils.substringBetween(accountingLineProperty, "[", "]");
        }
        return "insert" + infix + "Line.line" + lineNumber + "." + "anchoraccounting" + infix + "Anchor";
    }

    /**
     * Overrides the method in AccountingLineAuthorizerBase so that the delete button would have both
     * the line item number and the accounting line number for methodToCall when the user clicks on
     * the delete button.
     *
     * @see org.kuali.kfs.sys.document.authorization.AccountingLineAuthorizerBase#getDeleteLineMethod(org.kuali.kfs.sys.businessobject.AccountingLine, java.lang.String, java.lang.Integer)
     */
    @Override
    protected String getDeleteLineMethod(AccountingLine accountingLine, String accountingLineProperty,
            Integer accountingLineIndex) {
        final String infix = getActionInfixForExtantAccountingLine(accountingLine, accountingLineProperty);
        String lineNumber = StringUtils.substringBetween(accountingLineProperty, "item[", "].sourceAccountingLine");
        if (lineNumber == null) {
            lineNumber = "-2";
        }
        String accountingLineNumber = StringUtils.substringBetween(accountingLineProperty, "sourceAccountingLine[",
                "]");
        return "delete" + infix + "Line.line" + lineNumber + ":" + accountingLineNumber + ".anchoraccounting"
                + infix + "Anchor";
    }

    /**
     * Overrides the method in AccountingLineAuthorizerBase so that the balance inquiry button would
     * have both the line item number and the accounting line number for methodToCall when the user
     * clicks on the balance inquiry button.
     * @see org.kuali.kfs.sys.document.authorization.AccountingLineAuthorizerBase#getBalanceInquiryMethod(org.kuali.kfs.sys.businessobject.AccountingLine, java.lang.String, java.lang.Integer)
     */
    @Override
    protected String getBalanceInquiryMethod(AccountingLine accountingLine, String accountingLineProperty,
            Integer accountingLineIndex) {
        final String infix = getActionInfixForNewAccountingLine(accountingLine, accountingLineProperty);
        String lineNumber = StringUtils.substringBetween(accountingLineProperty, "item[", "].sourceAccountingLine");
        if (lineNumber == null) {
            lineNumber = "-2";
        }
        String accountingLineNumber = StringUtils.substringBetween(accountingLineProperty, "sourceAccountingLine[",
                "]");
        return "performBalanceInquiryFor" + infix + "Line.line" + ":" + lineNumber + ":" + accountingLineNumber
                + ".anchoraccounting" + infix + "existingLineLineAnchor" + accountingLineNumber;
    }

    /**
     * @see org.kuali.kfs.sys.document.authorization.AccountingLineAuthorizerBase#getUnviewableBlocks(org.kuali.kfs.sys.document.AccountingDocument, org.kuali.kfs.sys.businessobject.AccountingLine, boolean, org.kuali.rice.kim.api.identity.Person)
     */
    @Override
    public Set<String> getUnviewableBlocks(AccountingDocument accountingDocument, AccountingLine accountingLine,
            boolean newLine, Person currentUser) {
        Set<String> unviewableBlocks = super.getUnviewableBlocks(accountingDocument, accountingLine, newLine,
                currentUser);
        if (showAmountOnly(accountingDocument)) {
            unviewableBlocks.add(KFSPropertyConstants.PERCENT);
        } else {
            unviewableBlocks.add(KFSPropertyConstants.AMOUNT);
        }
        return unviewableBlocks;
    }

    private boolean showAmountOnly(AccountingDocument accountingDocument) {
        PurapService purapService = SpringContext.getBean(PurapService.class);
        if (accountingDocument instanceof PurchasingAccountsPayableDocument) {
            if (purapService.isFullDocumentEntryCompleted((PurchasingAccountsPayableDocument) accountingDocument)) {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @param accountingDocument
     * @return
     */
    private FinancialSystemTransactionalDocumentPresentationController getPresentationController(
            AccountingDocument accountingDocument) {
        final Class<? extends DocumentPresentationController> presentationControllerClass = ((TransactionalDocumentEntry) SpringContext
                .getBean(DataDictionaryService.class).getDataDictionary()
                .getDictionaryObjectEntry(accountingDocument.getClass().getName()))
                        .getDocumentPresentationControllerClass();
        FinancialSystemTransactionalDocumentPresentationController presentationController = null;
        try {
            presentationController = (FinancialSystemTransactionalDocumentPresentationController) presentationControllerClass
                    .newInstance();
        } catch (InstantiationException ie) {
            throw new RuntimeException("Cannot instantiate instance of presentation controller for "
                    + accountingDocument.getClass().getName(), ie);
        } catch (IllegalAccessException iae) {
            throw new RuntimeException("Cannot instantiate instance of presentation controller for "
                    + accountingDocument.getClass().getName(), iae);
        }
        return presentationController;
    }

    /**
     *
     * @param accountingDocument
     * @return
     */
    @Override
    protected FinancialSystemTransactionalDocumentAuthorizerBase getDocumentAuthorizer(
            AccountingDocument accountingDocument) {
        final Class<? extends DocumentAuthorizer> documentAuthorizerClass = ((TransactionalDocumentEntry) SpringContext
                .getBean(DataDictionaryService.class).getDataDictionary()
                .getDictionaryObjectEntry(accountingDocument.getClass().getName())).getDocumentAuthorizerClass();
        FinancialSystemTransactionalDocumentAuthorizerBase documentAuthorizer = null;
        try {
            documentAuthorizer = (FinancialSystemTransactionalDocumentAuthorizerBase) documentAuthorizerClass
                    .newInstance();
        } catch (InstantiationException ie) {
            throw new RuntimeException("Cannot instantiate instance of document authorizer for "
                    + accountingDocument.getClass().getName(), ie);
        } catch (IllegalAccessException iae) {
            throw new RuntimeException("Cannot instantiate instance of document authorizer for "
                    + accountingDocument.getClass().getName(), iae);
        }
        return documentAuthorizer;
    }

    @Override
    public boolean isGroupEditable(AccountingDocument accountingDocument,
            List<? extends AccountingLineRenderingContext> accountingLineRenderingContexts, Person currentUser) {

        boolean isEditable = super.isGroupEditable(accountingDocument, accountingLineRenderingContexts,
                currentUser);

        if (isEditable) {
            if (accountingLineRenderingContexts.size() == 0) {
                return false;
            }
            isEditable = allowAccountingLinesAreEditable(accountingDocument,
                    accountingLineRenderingContexts.get(0).getAccountingLine());
        }

        return isEditable;
    }

    @Override
    public boolean determineEditPermissionOnField(AccountingDocument accountingDocument,
            AccountingLine accountingLine, String accountingLineCollectionProperty, String fieldName,
            boolean editablePage) {

        // the fields in a new line should be always editable
        if (accountingLine.getSequenceNumber() == null) {
            return true;
        }

        boolean isEditable = super.determineEditPermissionOnField(accountingDocument, accountingLine,
                accountingLineCollectionProperty, fieldName, editablePage);

        if (isEditable) {
            isEditable = allowAccountingLinesAreEditable(accountingDocument, accountingLine);
        }

        return isEditable;
    }

    @Override
    public boolean determineEditPermissionOnLine(AccountingDocument accountingDocument,
            AccountingLine accountingLine, String accountingLineCollectionProperty,
            boolean currentUserIsDocumentInitiator, boolean pageIsEditable) {

        boolean isEditable = super.determineEditPermissionOnLine(accountingDocument, accountingLine,
                accountingLineCollectionProperty, currentUserIsDocumentInitiator, pageIsEditable);

        if (isEditable) {
            isEditable = allowAccountingLinesAreEditable(accountingDocument, accountingLine);
        }

        return (isEditable && pageIsEditable);
    }

    /**
     * This method checks whether the accounting lines are editable for a specific item type.
     *
     */
    protected boolean allowAccountingLinesAreEditable(AccountingDocument accountingDocument,
            AccountingLine accountingLine) {

        PurApAccountingLine purapAccount = (PurApAccountingLine) accountingLine;
        @SuppressWarnings("rawtypes")
        Class clazz = getPurapDocumentClass(accountingDocument);
        if (clazz == null) {
            return true;
        }

        //if not calculated yet then the line is editable
        PurchasingAccountsPayableDocumentBase purapDoc = (PurchasingAccountsPayableDocumentBase) accountingDocument;
        if (!purapDoc.isCalculated()) {
            return true;
        }

        Collection<String> restrictedItemTypesList = new ArrayList<String>(
                SpringContext.getBean(ParameterService.class).getParameterValuesAsString(clazz,
                        PurapParameterConstants.PURAP_ITEM_TYPES_RESTRICTING_ACCOUNT_EDIT));

        // This call determines a new special case in which an item marked for trade-in cannot have editable accounting lines
        // once the calculate button image is clicked, even if the accounting line has not been saved yet.
        boolean retval = true;
        retval = isEditableBasedOnTradeInRestriction(accountingDocument, accountingLine);

        if (restrictedItemTypesList != null && purapAccount.getPurapItem() != null) {
            return (!restrictedItemTypesList.contains(purapAccount.getPurapItem().getItemTypeCode()) && retval);
        } else if (restrictedItemTypesList != null && purapAccount.getPurapItem() == null) {
            return retval;
        } else {
            return true;
        }
    }

    /**
     * Find the item to which an accounting line belongs. Convenience/Utility method.
     *
     * Some methods that require an accounting line with a purApItem attached were getting accounting lines
     * passed in that did not yet have a purApItem. I needed a way to match the accounting line to the
     * proper item.
     *
     * @param accountingDocument the document holding both the accounting line and the item to which the
     * accounting line is attached
     * @param accountingLine the accounting line of interest, for which a containing item should be found
     * @return the item to which the incoming accounting line is attached
     */
    protected PurApItem findTheItemForAccountingLine(AccountingDocument accountingDocument,
            AccountingLine accountingLine) {
        PurApItem retval = null;
        List<PurApItem> listItems = null;

        scan: {
            if (accountingDocument instanceof PurchasingAccountsPayableDocumentBase) {
                listItems = ((PurchasingAccountsPayableDocumentBase) accountingDocument).getItems();

                // loop through all items in the document to see if the item has an accounting line that
                // matches the one passed in
                for (PurApItem oneItem : listItems) {
                    List<PurApAccountingLine> acctLines = oneItem.getSourceAccountingLines();
                    for (PurApAccountingLine oneAcctLine : acctLines) {
                        // we want to compare the exact same memory location, not the contents
                        if (oneAcctLine == accountingLine) {
                            retval = oneItem;

                            // we found it, so I can stop altogether.
                            break scan;
                        }
                    }
                }
            }
        }

        return retval;
    }

    /**
     * Handles a restriction on accounting lines assigned to trade-in items.
     * If the accounting Line is for a trade-in item type, and the accounting line has contents,
     * the user is not allowed to change the contents of the calculated values.
     *
     * The trade-in may not yet have a sequence number, so the old way of relying solely on sequence
     * number (in method super.approvedForUnqualifiedEditing() is incomplete, and needs this extra check
     * for trade-ins.
     *
     * @param accountingLine the accounting line being examined
     * @return whether the accounting line is editable according to the trade-in/non-empty restriction
     */
    private boolean isEditableBasedOnTradeInRestriction(AccountingDocument accountingDocument,
            AccountingLine accountingLine) {
        boolean retval = true;
        // if the accounting Line is for a trade-In, and the line has contents, the user is not allowed to
        // change the contents of the calculated values
        if ((accountingLine != null) && (accountingLine instanceof PurApAccountingLine)) {
            PurApItem purApItem = (((PurApAccountingLine) accountingLine)).getPurapItem();

            // this small block is to allow for another way to get to the purApItem if the
            // incoming accounting line does not yet have a purApItem attached. Calling it is not
            // completely necessary any more, unless/until the functional team members decide to
            // add more item types to the read-only accounting lines list.
            if (purApItem == null) {
                purApItem = findTheItemForAccountingLine(accountingDocument, accountingLine);
            }

            if (purApItem != null) {
                String itemTypeCode = purApItem.getItemTypeCode();
                if (itemTypeCode.toUpperCase().equalsIgnoreCase(PurapParameterConstants.PURAP_ITEM_TYPE_TRDI)) {
                    // does the line have any contents? if so, then the user cannot edit them
                    if ((accountingLine.getChartOfAccountsCode() != null)
                            || (accountingLine.getAccountNumber() != null)
                            || (accountingLine.getFinancialObjectCode() != null)) {
                        retval = false;
                    }
                }
                // there has been a call to "if (purApItem.getItemAssignedToTradeInIndicator()) {" here
                // that required the earlier use of findTheItemForAccountingLine()
            }
        }
        return retval;
    }

    @SuppressWarnings("rawtypes")
    private Class getPurapDocumentClass(AccountingDocument accountingDocument) {
        if (accountingDocument instanceof RequisitionDocument) {
            return RequisitionDocument.class;
        } else if (accountingDocument instanceof PurchaseOrderDocument) {
            return PurchaseOrderDocument.class;
        } else if (accountingDocument instanceof PaymentRequestDocument) {
            return PaymentRequestDocument.class;
        } else {
            return null;
        }
    }

    /**
     * Determines if the given line is editable, no matter what a KIM check would say about line editability.  In the default case,
     * any accounting line is editable - minus KIM check - when the document is PreRoute, or if the line is a new line
     *
     * This overriding implementation is required because the Purap module has a new restriction that requires
     * that an accounting line for a Trade-In item cannot be manually editable, even if not yet saved ("not yet saved" means it has
     * no sequence number). Therefore, the base implementation that determines editability on the sequence number alone has to be
     * preceded by a check that declares ineligible for editing if it is a trade-in.
     *
     * @see org.kuali.kfs.module.purap.document.authorization.PurapAccountingLineAuthorizer#allowAccountingLinesAreEditable(AccountingDocument, AccountingLine)
     *
     * @param accountingDocument the accounting document the line is or wants to be associated with
     * @param accountingLine the accounting line itself
     * @param accountingLineCollectionProperty the collection the accounting line is or would be part of
     * @param currentUserIsDocumentInitiator is the current user the initiator of the document?
     * @return true if the line as a whole can be edited without the KIM check, false otherwise
     */
    @Override
    protected boolean approvedForUnqualifiedEditing(AccountingDocument accountingDocument,
            AccountingLine accountingLine, String accountingLineCollectionProperty,
            boolean currentUserIsDocumentInitiator) {
        boolean retval = true;

        retval = isEditableBasedOnTradeInRestriction(accountingDocument, accountingLine);

        if (retval) {
            retval = super.approvedForUnqualifiedEditing(accountingDocument, accountingLine,
                    accountingLineCollectionProperty, currentUserIsDocumentInitiator);
        }
        return retval;
    }
}