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.bc.document.service.impl;
017    
018    import static org.kuali.kfs.module.bc.BCConstants.AppointmentFundingDurationCodes.LWPA;
019    import static org.kuali.kfs.module.bc.BCConstants.AppointmentFundingDurationCodes.LWPF;
020    import static org.kuali.kfs.module.bc.BCConstants.AppointmentFundingDurationCodes.NONE;
021    
022    import java.math.BigDecimal;
023    import java.util.List;
024    
025    import org.apache.commons.lang.ObjectUtils;
026    import org.apache.commons.lang.StringUtils;
027    import org.kuali.kfs.module.bc.BCConstants;
028    import org.kuali.kfs.module.bc.BCKeyConstants;
029    import org.kuali.kfs.module.bc.BCPropertyConstants;
030    import org.kuali.kfs.module.bc.BCConstants.SynchronizationCheckType;
031    import org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding;
032    import org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService;
033    import org.kuali.kfs.module.bc.document.service.SalarySettingService;
034    import org.kuali.kfs.module.bc.service.HumanResourcesPayrollService;
035    import org.kuali.kfs.sys.KFSConstants;
036    import org.kuali.kfs.sys.KFSPropertyConstants;
037    import org.kuali.rice.kns.util.KualiDecimal;
038    import org.kuali.rice.kns.util.KualiInteger;
039    import org.kuali.rice.kns.util.MessageMap;
040    
041    /**
042     * provide a set of rule elements for salary setting.
043     */
044    public class SalarySettingRuleHelperServiceImpl implements SalarySettingRuleHelperService {
045        private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(SalarySettingRuleHelperServiceImpl.class);
046    
047        private SalarySettingService salarySettingService;
048        private HumanResourcesPayrollService humanResourcesPayrollService;
049        
050        /**
051         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#canBeAdjusted(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding, org.kuali.rice.kns.util.ErrorMap)
052         */
053        public boolean canBeAdjusted(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
054            if (appointmentFunding.getEffectiveCSFTracker() == null) {
055                // These error messages should not be displayed since the reported condition is obvious and benign.
056                //errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_AMOUNT, BCKeyConstants.ERROR_CANNOT_ADJUST_FUNDING_WITHOUT_EFFECTIVE_CSF_TRACKER);
057                return false;
058            }
059            
060            if (appointmentFunding.isAppointmentFundingDeleteIndicator()) {
061                // These error messages should not be displayed since the reported condition is obvious and benign.
062                //errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_AMOUNT, BCKeyConstants.ERROR_CANNOT_ADJUST_FUNDING_MARKED_AS_DELETE);
063                return false;
064            }
065    
066            return true;
067        }
068    
069        /**
070         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasActiveJob(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
071         *      org.kuali.rice.kns.util.ErrorMap)
072         */
073        public boolean hasActiveJob(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
074            Integer fiscalYear = appointmentFunding.getUniversityFiscalYear();
075            String emplid = appointmentFunding.getEmplid();
076            String positionNumber = appointmentFunding.getPositionNumber();
077    
078            boolean hasActiveJob = humanResourcesPayrollService.isActiveJob(emplid, positionNumber, fiscalYear, SynchronizationCheckType.ALL);
079            if (!hasActiveJob) {
080                errorMap.putError(KFSConstants.GLOBAL_ERRORS, BCKeyConstants.ERROR_NO_ACTIVE_JOB_FOUND, appointmentFunding.getEmplid(), appointmentFunding.getPositionNumber());
081                return false;
082            }
083    
084            return true;
085        }
086    
087        /**
088         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasObjectCodeMatchingDefaultOfPosition(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
089         *      org.kuali.core.util.ErrorMap)
090         */
091        public boolean hasObjectCodeMatchingDefaultOfPosition(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
092            String defaultObjectCode = appointmentFunding.getBudgetConstructionPosition().getIuDefaultObjectCode();
093            String objectCode = appointmentFunding.getFinancialObjectCode();
094    
095            if (!StringUtils.equals(objectCode, defaultObjectCode)) {
096                errorMap.putError(KFSPropertyConstants.FINANCIAL_OBJECT_CODE, BCKeyConstants.ERROR_NOT_DEFAULT_OBJECT_CODE, defaultObjectCode);
097                return false;
098            }
099    
100            return true;
101        }
102    
103        /**
104         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasRequestedAmountZeroWhenFullYearLeave(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
105         *      org.kuali.core.util.ErrorMap)
106         */
107        public boolean hasRequestedAmountZeroWhenFullYearLeave(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
108            String leaveDurationCode = appointmentFunding.getAppointmentFundingDurationCode();
109    
110            // Request Salary Amount must be zero because these leave codes are for full year leave without pay.
111            if (StringUtils.equals(leaveDurationCode, LWPA.durationCode) || StringUtils.equals(leaveDurationCode, LWPF.durationCode)) {
112                KualiInteger requestedAmount = appointmentFunding.getAppointmentRequestedAmount();
113    
114                if (!requestedAmount.isZero()) {
115                    errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_AMOUNT, BCKeyConstants.ERROR_REQUEST_AMOUNT_NOT_ZERO_WHEN_FULL_YEAR_LEAVE);
116                    return false;
117                }
118            }
119    
120            return true;
121        }
122    
123        /**
124         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasRequestedFteQuantityZeroWhenFullYearLeave(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
125         *      org.kuali.core.util.ErrorMap)
126         */
127        public boolean hasRequestedFteQuantityZeroWhenFullYearLeave(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
128            String leaveDurationCode = appointmentFunding.getAppointmentFundingDurationCode();
129    
130            // Request Salary Amount must be zero because these leave codes are for full year leave without pay.
131            if (StringUtils.equals(leaveDurationCode, LWPA.durationCode) || StringUtils.equals(leaveDurationCode, LWPF.durationCode)) {
132                BigDecimal requestedFteQuantity = appointmentFunding.getAppointmentRequestedFteQuantity();
133    
134                if (requestedFteQuantity.compareTo(BigDecimal.ZERO) != 0) {
135                    errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_FTE_QUANTITY, BCKeyConstants.ERROR_REQUEST_FTE_NOT_ZERO_WHEN_FULL_YEAR_LEAVE);
136                    return false;
137                }
138                
139                return true;
140            }
141    
142            return true;
143        }
144    
145        /**
146         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasNoExistingLine(java.util.List,
147         *      org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding, org.kuali.rice.kns.util.ErrorMap)
148         */
149        public boolean hasNoExistingLine(List<PendingBudgetConstructionAppointmentFunding> appointmentFundings, PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
150            boolean hasNoExistingLine = salarySettingService.findAppointmentFunding(appointmentFundings, appointmentFunding) == null;
151            if (!hasNoExistingLine) {
152                errorMap.putError(BCPropertyConstants.NEW_BCAF_LINE, BCKeyConstants.ERROR_DUPLICATE_FUNDING_LINE);
153                return false;
154            }
155    
156            return true;
157        }
158    
159        /**
160         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidRequestedAmount(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
161         *      org.kuali.core.util.ErrorMap)
162         */
163        public boolean hasValidRequestedAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
164            KualiInteger requestedAmount = appointmentFunding.getAppointmentRequestedAmount();
165            if (requestedAmount == null || requestedAmount.isNegative()) {
166                errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_AMOUNT, BCKeyConstants.ERROR_REQUESTED_AMOUNT_NONNEGATIVE_REQUIRED);
167                return false;
168            }
169    
170            return true;
171        }
172    
173        public boolean hasValidRequestedAmountQuickSalarySetting(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
174    
175            if (this.hasValidRequestedAmount(appointmentFunding, errorMap)){
176                KualiInteger requestedAmount = appointmentFunding.getAppointmentRequestedAmount();
177                BigDecimal requestedFteQuantity = appointmentFunding.getAppointmentRequestedFteQuantity();
178                if (requestedAmount.isPositive() && (requestedFteQuantity != null && requestedFteQuantity.compareTo(BigDecimal.ZERO) == 0)) {
179                    errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_AMOUNT, BCKeyConstants.ERROR_REQUESTED_AMOUNT_NEEDS_FTE_FIRST);
180                    return false;
181                }
182            }
183    
184            return true;
185        }
186    
187        /**
188         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidRequestedCsfAmount(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
189         *      org.kuali.core.util.ErrorMap)
190         */
191        public boolean hasValidRequestedCsfAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
192            KualiInteger csfAmount = appointmentFunding.getAppointmentRequestedCsfAmount();
193            String leaveDurationCode = appointmentFunding.getAppointmentFundingDurationCode();
194    
195            // Requested csf amount must be greater than 0 if there is a leave
196            if (!StringUtils.equals(leaveDurationCode, NONE.durationCode)) {
197                if (csfAmount == null || !csfAmount.isPositive()) {
198                    errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_CSF_AMOUNT, BCKeyConstants.ERROR_FTE_GREATER_THAN_ZERO_REQUIRED);
199                    return false;
200                }
201                
202                return true;
203            }
204    
205            return true;
206        }
207    
208        /**
209         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidRequestedCsfTimePercent(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
210         *      org.kuali.core.util.ErrorMap)
211         */
212        public boolean hasValidRequestedCsfTimePercent(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
213            BigDecimal csfTimePercent = appointmentFunding.getAppointmentRequestedCsfTimePercent();
214            String leaveDurationCode = appointmentFunding.getAppointmentFundingDurationCode();
215    
216            // Requested csf amount must be greater than 0 if there is a leave
217            if (!StringUtils.equals(leaveDurationCode, NONE.durationCode)) {
218                if (csfTimePercent == null || csfTimePercent.compareTo(BigDecimal.ZERO) <= 0 || csfTimePercent.compareTo(BCConstants.ONE_HUNDRED) > 0 ) {
219                    errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_CSF_TIME_PERCENT, BCKeyConstants.ERROR_LEAVE_TIME_PERCENT_NOT_IN_RANGE, BigDecimal.ZERO.toPlainString(), BCConstants.ONE_HUNDRED.toPlainString());
220                    return false;
221                }
222                
223                return true;
224            }
225    
226            return true;
227        }
228    
229        /**
230         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidRequestedFteQuantity(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
231         *      org.kuali.core.util.ErrorMap)
232         */
233        public boolean hasValidRequestedFteQuantity(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
234            BigDecimal requestedFteQuantity = appointmentFunding.getAppointmentRequestedFteQuantity();
235            if (requestedFteQuantity == null || requestedFteQuantity.compareTo(BigDecimal.ZERO) < 0 || requestedFteQuantity.compareTo(BigDecimal.ONE) > 0) {
236                errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_FTE_QUANTITY, BCKeyConstants.ERROR_FTE_QUANTITY_NOT_IN_RANGE, BigDecimal.ZERO.toPlainString(), BigDecimal.ONE.toPlainString());
237                return false;
238            }
239    
240            return true;
241        }
242    
243        /**
244         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidRequestedFundingMonth(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
245         *      org.kuali.core.util.ErrorMap)
246         */
247        public boolean hasValidRequestedFundingMonth(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
248            Integer fundingMonths = appointmentFunding.getAppointmentFundingMonth();
249            if (fundingMonths == null) {
250                errorMap.putError(BCPropertyConstants.APPOINTMENT_FUNDING_MONTH, BCKeyConstants.ERROR_EMPTY_FUNDIN_MONTH);
251                return false;
252            }
253    
254            // Requested funding months must be between 0 and position normal work months if there is a leave
255            String leaveDurationCode = appointmentFunding.getAppointmentFundingDurationCode();
256            Integer normalWorkMonths = appointmentFunding.getBudgetConstructionPosition().getIuNormalWorkMonths();
257            if (!StringUtils.equals(leaveDurationCode, NONE.durationCode)) {
258                if (fundingMonths < 0 || fundingMonths > normalWorkMonths) {
259                    errorMap.putError(BCPropertyConstants.APPOINTMENT_FUNDING_MONTH, BCKeyConstants.ERROR_FUNDIN_MONTH_NOT_IN_RANGE, ObjectUtils.toString(fundingMonths), "0", ObjectUtils.toString(normalWorkMonths));
260                    return false;
261                }
262                
263                return true;
264            }
265    
266            // Requested funding months must equal to position normal work months if no leave
267            if (!fundingMonths.equals(normalWorkMonths)) {
268                errorMap.putError(BCPropertyConstants.APPOINTMENT_FUNDING_MONTH, BCKeyConstants.ERROR_NOT_EQUAL_NORMAL_WORK_MONTHS, ObjectUtils.toString(fundingMonths), ObjectUtils.toString(normalWorkMonths));
269                return false;
270            }
271    
272            return true;
273        }
274    
275        /**
276         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidRequestedTimePercent(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
277         *      org.kuali.core.util.ErrorMap)
278         */
279        public boolean hasValidRequestedTimePercent(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
280            KualiInteger requestedAmount = appointmentFunding.getAppointmentRequestedAmount();
281            BigDecimal requestedTimePercent = appointmentFunding.getAppointmentRequestedTimePercent();
282    
283            if (requestedTimePercent == null) {
284                errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_TIME_PERCENT, BCKeyConstants.ERROR_EMPTY_REQUESTED_TIME_PERCENT);
285                return false;
286            }
287    
288            if (requestedTimePercent.compareTo(BigDecimal.ZERO) < 0 || requestedTimePercent.compareTo(BCConstants.ONE_HUNDRED) > 0 ) {
289                errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_TIME_PERCENT, BCKeyConstants.ERROR_TIME_PERCENT_NOT_IN_RANGE, BigDecimal.ZERO.toPlainString(), BCConstants.ONE_HUNDRED.toPlainString());
290                return false;
291            }
292    
293            if (requestedAmount.isPositive() && requestedTimePercent.compareTo(BigDecimal.ZERO) <= 0) {
294                errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_TIME_PERCENT, BCKeyConstants.ERROR_TIME_PERCENT_GREATER_THAN_ZERO_REQUIRED);
295                return false;
296            }
297    
298            return true;
299        }
300    
301        /**
302         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidAdjustmentAmount(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
303         *      org.kuali.rice.kns.util.ErrorMap)
304         */
305        public boolean hasValidAdjustmentAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
306            KualiDecimal adjustmentAmount = appointmentFunding.getAdjustmentAmount();
307    
308            if (adjustmentAmount == null) {
309                errorMap.putError(BCPropertyConstants.ADJUSTMENT_AMOUNT, BCKeyConstants.ERROR_ADJUSTMENT_AMOUNT_REQUIRED);
310                return false;
311            }
312    
313            return true;
314        }
315    
316        /**
317         * @see org.kuali.kfs.module.bc.document.service.SalarySettingRuleHelperService#hasValidAdjustmentAmount(org.kuali.kfs.module.bc.businessobject.PendingBudgetConstructionAppointmentFunding,
318         *      org.kuali.rice.kns.util.ErrorMap)
319         */
320        public boolean hasValidPayRateOrAnnualAmount(PendingBudgetConstructionAppointmentFunding appointmentFunding, MessageMap errorMap) {
321            BigDecimal payRate = appointmentFunding.getAppointmentRequestedPayRate();
322            KualiInteger requestedAmount = appointmentFunding.getAppointmentRequestedAmount();
323    
324            if (requestedAmount == null && payRate == null) {
325                errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_PAY_RATE, BCKeyConstants.ERROR_EMPTY_PAY_RATE_ANNUAL_AMOUNT);
326                errorMap.putError(BCPropertyConstants.APPOINTMENT_REQUESTED_AMOUNT, BCKeyConstants.ERROR_EMPTY_PAY_RATE_ANNUAL_AMOUNT);
327                return false;
328            }
329    
330            return true;
331        }
332    
333        /**
334         * Sets the salarySettingService attribute value.
335         * 
336         * @param salarySettingService The salarySettingService to set.
337         */
338        public void setSalarySettingService(SalarySettingService salarySettingService) {
339            this.salarySettingService = salarySettingService;
340        }
341    
342        /**
343         * Sets the humanResourcesPayrollService attribute value.
344         * 
345         * @param humanResourcesPayrollService The humanResourcesPayrollService to set.
346         */
347        public void setHumanResourcesPayrollService(HumanResourcesPayrollService humanResourcesPayrollService) {
348            this.humanResourcesPayrollService = humanResourcesPayrollService;
349        }
350    }