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.module.ec.document.validation.impl;
017    
018    import java.util.Collection;
019    import java.util.List;
020    import java.util.Map;
021    import java.util.Set;
022    
023    import org.kuali.kfs.coa.businessobject.Account;
024    import org.kuali.kfs.coa.businessobject.Organization;
025    import org.kuali.kfs.coa.businessobject.SubFundGroup;
026    import org.kuali.kfs.integration.cg.ContractsAndGrantsModuleService;
027    import org.kuali.kfs.integration.ld.LaborLedgerBalance;
028    import org.kuali.kfs.module.ec.EffortConstants;
029    import org.kuali.kfs.module.ec.EffortKeyConstants;
030    import org.kuali.kfs.module.ec.util.LedgerBalanceConsolidationHelper;
031    import org.kuali.kfs.sys.Message;
032    import org.kuali.kfs.sys.MessageBuilder;
033    import org.kuali.kfs.sys.context.SpringContext;
034    import org.kuali.rice.kns.util.KualiDecimal;
035    import org.kuali.rice.kns.util.ObjectUtils;
036    
037    /**
038     * The validator provides a set of facilities to determine whether the given ledger balances meet the specified requirements. As a
039     * pattern, null would be returned if the requirements are met; otherwise, return an error message.
040     */
041    public class LedgerBalanceFieldValidator {
042    
043        /**
044         * check if the given ledger balance has an account qualified for effort reporting
045         * 
046         * @param ledgerBalance the given ledger balance
047         * @return null if the given ledger balance has an account qualified for effort reporting; otherwise, a message
048         */
049        public static Message hasValidAccount(LaborLedgerBalance ledgerBalance) {
050            if (ObjectUtils.isNull(ledgerBalance.getAccount())) {
051                String account = new StringBuilder(ledgerBalance.getChartOfAccountsCode()).append(EffortConstants.VALUE_SEPARATOR).append(ledgerBalance.getAccountNumber()).toString();
052                return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_ACCOUNT_NUMBER_NOT_FOUND, account);
053            }
054            return null;
055        }
056    
057        /**
058         * detetermine if the fund group code associated with the given ledger balance is in the given fund group codes
059         * 
060         * @param ledgerBalance the given ledger balance
061         * @param fundGroupCodes the given fund group codes
062         * @return null if the fund group code associated with the given ledger balance is in the given fund group codes; otherwise, a
063         *         message
064         */
065        public static Message isInFundGroups(LaborLedgerBalance ledgerBalance, List<String> fundGroupCodes) {
066            SubFundGroup subFundGroup = getSubFundGroup(ledgerBalance);
067    
068            if (ObjectUtils.isNull(subFundGroup) || !fundGroupCodes.contains(subFundGroup.getFundGroupCode())) {
069                return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_FUND_GROUP_NOT_FOUND, subFundGroup.getFundGroupCode());
070            }
071            return null;
072        }
073    
074        /**
075         * detetermine if the sub fund group code associated with the given ledger balance is in the given sub fund group codes
076         * 
077         * @param ledgerBalance the given ledger balance
078         * @param subFundGroupCodes the given sub fund group codes
079         * @return null if the sub fund group code associated with the given ledger balance is in the given sub fund group codes;
080         *         otherwise, an error message
081         */
082        public static Message isInSubFundGroups(LaborLedgerBalance ledgerBalance, List<String> subFundGroupCodes) {
083            SubFundGroup subFundGroup = getSubFundGroup(ledgerBalance);
084    
085            if (ObjectUtils.isNull(subFundGroup) || !subFundGroupCodes.contains(subFundGroup.getSubFundGroupCode())) {
086                return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_FUND_GROUP_NOT_FOUND, subFundGroup.getSubFundGroupCode());
087            }
088            return null;
089        }
090    
091        /**
092         * determine if the total amount within the specified periods of the given ledger balance is ZERO
093         * 
094         * @param ledgerBalance the given ledger balance
095         * @param reportPeriods the specified periods
096         * @return null the total amount within the specified periods of the given ledger balance is NOT ZERO; otherwise, a message
097         *         message
098         */
099        public static Message isNonZeroAmountBalanceWithinReportPeriod(LaborLedgerBalance ledgerBalance, Map<Integer, Set<String>> reportPeriods) {
100            KualiDecimal totalAmount = LedgerBalanceConsolidationHelper.calculateTotalAmountWithinReportPeriod(ledgerBalance, reportPeriods);
101    
102            if (totalAmount.isZero()) {
103                return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_ZERO_PAYROLL_AMOUNT, Message.TYPE_FATAL);
104            }
105            return null;
106        }
107    
108        /**
109         * determine if the total amount within the specified periods of the given ledger balances is positive
110         * 
111         * @param ledgerBalance the given ledger balance
112         * @param reportPeriods the specified periods
113         * @return null the total amount within the specified periods of the given ledger balance is positive; otherwise, a message
114         *         message
115         */
116        public static Message isTotalAmountPositive(Collection<LaborLedgerBalance> ledgerBalances, Map<Integer, Set<String>> reportPeriods) {
117            KualiDecimal totalAmount = LedgerBalanceConsolidationHelper.calculateTotalAmountWithinReportPeriod(ledgerBalances, reportPeriods);
118    
119            if (!totalAmount.isPositive()) {
120                return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_NONPOSITIVE_PAYROLL_AMOUNT, totalAmount.toString());
121            }
122            return null;
123        }
124    
125        /**
126         * check if there is at least one account of the given ledger balances that has a fund group code or subfund group code that is
127         * in the specifed group codes. If fundGroupDenotesCGIndictor is ture, only examine the fund group code associated with the
128         * ledger balances; otherwise, the sub fund group code.
129         * 
130         * @param ledgerBalances the given ledger balances
131         * @return null if one of the group codes associated with the ledger balances is in the specified codes; otherwise, a message
132         *         message
133         */
134        public static Message hasGrantAccount(Collection<LaborLedgerBalance> ledgerBalances) {
135            for (LaborLedgerBalance balance : ledgerBalances) {
136                if (balance.getAccount().isForContractsAndGrants()) {
137                    return null;
138                }
139            }
140    
141            return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_NOT_PAID_BY_GRANT_ACCOUNT, Message.TYPE_FATAL);
142        }
143    
144        /**
145         * determine whether there is at least one account of the given ledger balances that is funded by a federal grant. The award
146         * associated with the account must be one of the given federal agency types or have an enabled federal pass through flag.
147         * 
148         * @param the given labor ledger balances
149         * @param federalAgencyTypeCodes the given federal agency type codes
150         * @return null if there is at least one account with federal funding; otherwise, a message
151         */
152        public static Message hasFederalFunds(Collection<LaborLedgerBalance> ledgerBalances, List<String> federalAgencyTypeCodes) {
153            for (LaborLedgerBalance balance : ledgerBalances) {
154                Account account = balance.getAccount();
155                if (SpringContext.getBean(ContractsAndGrantsModuleService.class).isAwardedByFederalAgency(account.getChartOfAccountsCode(), account.getAccountNumber(), federalAgencyTypeCodes)) {
156                    return null;
157                }
158            }
159            return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_NOT_PAID_BY_FEDERAL_FUNDS, Message.TYPE_FATAL);
160        }
161    
162        /**
163         * determine if the given ledger balances have the accounts that belong to multiple organizations
164         * 
165         * @param ledgerBalance the given ledger balance
166         * @return null if the given ledger balances have the accounts that belong to a single organization; otherwise, a message
167         */
168        public static Message isFromSingleOrganization(Collection<LaborLedgerBalance> ledgerBalances) {
169            Organization tempOrganization = null;
170    
171            boolean isFirstTime = true;
172            for (LaborLedgerBalance balance : ledgerBalances) {
173                Organization organization = balance.getAccount().getOrganization();
174    
175                if (isFirstTime) {
176                    tempOrganization = organization;
177                    isFirstTime = false;
178                }
179    
180                if (!organization.equals(tempOrganization)) {
181                    return MessageBuilder.buildMessage(EffortKeyConstants.ERROR_MULTIPLE_ORGANIZATIONS_FOUND, Message.TYPE_FATAL);
182                }
183            }
184            return null;
185        }
186    
187        /**
188         * get the sub fund group associated with the given ledger balance
189         * 
190         * @param ledgerBalance the given ledger balance
191         * @return the sub fund group associated with the given ledger balance
192         */
193        public static SubFundGroup getSubFundGroup(LaborLedgerBalance ledgerBalance) {
194            SubFundGroup subFundGroup = null;
195            try {
196                subFundGroup = ledgerBalance.getAccount().getSubFundGroup();
197            }
198            catch (NullPointerException npe) {
199                return null;
200            }
201            return subFundGroup;
202        }
203    }