001/*
002 * Copyright 2006 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 */
016package org.kuali.ole.coa.document.validation.impl;
017
018import java.util.List;
019
020import org.apache.commons.lang.StringUtils;
021import org.kuali.ole.coa.businessobject.AccountGlobalDetail;
022import org.kuali.ole.sys.OLEKeyConstants;
023import org.kuali.ole.sys.document.validation.impl.KfsMaintenanceDocumentRuleBase;
024import org.kuali.rice.krad.util.GlobalVariables;
025
026/**
027 * This class contains common Business Rule functionality for Global Documents.
028 */
029public class GlobalDocumentRuleBase extends KfsMaintenanceDocumentRuleBase {
030
031    /**
032     * Constructs a GlobalDocumentRuleBase.java.
033     */
034    public GlobalDocumentRuleBase() {
035        super();
036    }
037
038    /**
039     * This method checks whether the set of Account Change Detail records on this document all are under the same Chart of
040     * Accounts. It will set the appropriate field error if it did fail, and return the result.
041     * 
042     * @param accountGlobalDetails
043     * @return True if the test passed with no errors, False if any errors occurred.
044     */
045    protected boolean checkOnlyOneChartErrorWrapper(List<AccountGlobalDetail> accountGlobalDetails) {
046        CheckOnlyOneChartResult result = checkOnlyOneChart(accountGlobalDetails);
047        if (!result.success) {
048            putFieldError("accountGlobalDetails[" + result.firstLineNumber + "].chartOfAccountsCode", OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_ONE_CHART_ONLY);
049            putFieldError("accountGlobalDetails[" + result.failedLineNumber + "].chartOfAccountsCode", OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_ONE_CHART_ONLY);
050        }
051        return result.success;
052    }
053
054    /**
055     * This method checks whether the set of Account Change Detail records on this document all are under the same Chart of
056     * Accounts. It will return a failed CheckOnlyOneChartResult if so. Note that this method doesnt actually set any errors, it
057     * just returns whether or not the test succeeded, and where it failed if it failed.
058     * 
059     * @param accountGlobalDetails The popualted accountGlobalDocument to test.
060     * @return A populated CheckOnlyOneChartResult object. This will contain whether the test succeeded or failed, and if failed,
061     *         what lines the failures occurred on.
062     */
063    protected CheckOnlyOneChartResult checkOnlyOneChart(List<AccountGlobalDetail> accountGlobalDetails) {
064        // if there is not enough information to do the test, then exit happily with no failure
065        if (accountGlobalDetails == null) {
066            return new CheckOnlyOneChartResult(true);
067        }
068        if (accountGlobalDetails.isEmpty()) {
069            return new CheckOnlyOneChartResult(true);
070        }
071
072        // test to see if there is more than one chart listed, ignores blank chartcodes
073        int compareLineNumber = 0;
074        int firstChartLineNumber = 0;
075        String firstChart = "";
076        for (AccountGlobalDetail account : accountGlobalDetails) {
077            if (StringUtils.isBlank(firstChart)) {
078                if (StringUtils.isNotBlank(account.getChartOfAccountsCode())) {
079                    firstChart = account.getChartOfAccountsCode();
080                    firstChartLineNumber = compareLineNumber;
081                }
082            }
083            else {
084                if (StringUtils.isNotBlank(account.getChartOfAccountsCode())) {
085                    if (!firstChart.equalsIgnoreCase(account.getChartOfAccountsCode())) {
086                        return new CheckOnlyOneChartResult(false, firstChartLineNumber, compareLineNumber);
087                    }
088                }
089            }
090            compareLineNumber++;
091        }
092        return new CheckOnlyOneChartResult(true);
093    }
094
095    /**
096     * This class is used internally to represent the result of the CheckOnlyOneChart method.
097     */
098    protected class CheckOnlyOneChartResult {
099
100        public int firstLineNumber;
101        public int failedLineNumber;
102        public boolean success;
103
104        /**
105         * Constructs a CheckOnlyOneChartResult
106         */
107        public CheckOnlyOneChartResult() {
108            firstLineNumber = -1;
109            failedLineNumber = -1;
110            success = true;
111        }
112
113        /**
114         * Constructs a CheckOnlyOneChartResult
115         * 
116         * @param success
117         */
118        public CheckOnlyOneChartResult(boolean success) {
119            this();
120            this.success = success;
121        }
122
123        /**
124         * Constructs a CheckOnlyOneChartResult
125         * 
126         * @param success
127         * @param firstLineNumber
128         * @param failedLineNumber
129         */
130        public CheckOnlyOneChartResult(boolean success, int firstLineNumber, int failedLineNumber) {
131            this.success = success;
132            this.firstLineNumber = firstLineNumber;
133            this.failedLineNumber = failedLineNumber;
134        }
135
136    }
137
138    /**
139     * This method tests whether the line being added has a different Chart of Accounts Code from any of the existing lines. It will
140     * set an Error and return false if this is the case.
141     * 
142     * @param newAccountLine
143     * @param accountGlobalDetails
144     * @return True if the line being added has the exact same chart as all the existing lines, False if not.
145     */
146    protected boolean checkOnlyOneChartAddLineErrorWrapper(AccountGlobalDetail newAccountLine, List<AccountGlobalDetail> accountGlobalDetails) {
147        boolean success = checkOnlyOneChartAddLine(newAccountLine, accountGlobalDetails);
148        if (!success) {
149            // putGlobalError(OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_ONE_CHART_ONLY_ADDNEW);
150            // TODO: KULCOA-1091 Need to add this error to the add line, but this doesn't work right, as the
151            // error message comes out at the bottom, and the field doesn't get highlighted.
152            // putFieldError("newAccountGlobalDetail.chartOfAccountsCode",
153            // OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_ONE_CHART_ONLY);
154
155            // added to prevent error from showing at the top of the document, but rather in the Account Detail Edit section
156            GlobalVariables.getMessageMap().putError("chartOfAccountsCode", OLEKeyConstants.ERROR_DOCUMENT_GLOBAL_ACCOUNT_ONE_CHART_ONLY_ADDNEW);
157        }
158        return success;
159    }
160
161    /**
162     * This method tests whether a new line can be added, based on the rule that says all the accounts being used must belong to the
163     * same chart. If the line being added differs from any existing line's Chart code, it will return false. Note that this
164     * document does not actually set any errors, it just reports success or failure.
165     * 
166     * @param newAccountLine
167     * @param accountGlobalDetails
168     * @return True if no errors are found, False if the line being added has a different Chart code than any of the existing lines.
169     */
170    protected boolean checkOnlyOneChartAddLine(AccountGlobalDetail newAccountLine, List<AccountGlobalDetail> accountGlobalDetails) {
171        if (newAccountLine == null || accountGlobalDetails == null) {
172            return true;
173        }
174        if (accountGlobalDetails.isEmpty()) {
175            return true;
176        }
177        String newChart = newAccountLine.getChartOfAccountsCode();
178        if (StringUtils.isBlank(newChart)) {
179            return true;
180        }
181        for (AccountGlobalDetail account : accountGlobalDetails) {
182            if (StringUtils.isNotBlank(account.getChartOfAccountsCode())) {
183                if (!newChart.equalsIgnoreCase(account.getChartOfAccountsCode())) {
184                    return false;
185                }
186            }
187        }
188        return true;
189    }
190
191}