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.purap.document.validation.impl;
017
018 import java.math.BigDecimal;
019 import java.util.ArrayList;
020 import java.util.HashMap;
021 import java.util.List;
022 import java.util.Map;
023
024 import org.apache.commons.lang.StringUtils;
025 import org.kuali.kfs.fp.businessobject.NonResidentAlienTaxPercent;
026 import org.kuali.kfs.module.purap.PurapConstants;
027 import org.kuali.kfs.module.purap.PurapKeyConstants;
028 import org.kuali.kfs.module.purap.PurapPropertyConstants;
029 import org.kuali.kfs.module.purap.PurapConstants.PaymentRequestStatuses;
030 import org.kuali.kfs.module.purap.document.PaymentRequestDocument;
031 import org.kuali.kfs.sys.document.validation.GenericValidation;
032 import org.kuali.kfs.sys.document.validation.event.AttributedDocumentEvent;
033 import org.kuali.rice.kns.service.BusinessObjectService;
034 import org.kuali.rice.kns.util.ErrorMap;
035 import org.kuali.rice.kns.util.GlobalVariables;
036 import org.kuali.rice.kns.util.KualiDecimal;
037 import org.kuali.rice.kns.util.MessageMap;
038 import org.kuali.rice.kns.util.ObjectUtils;
039
040 public class PaymentRequestTaxAreaValidation extends GenericValidation {
041
042 private BusinessObjectService businessObjectService;
043
044 /** Map for allowed federal and state tax rates based on income class. *
045 private static HashMap<String, ArrayList<BigDecimal>> federalTaxRates;
046 private static HashMap<String, ArrayList<BigDecimal>> stateTaxRates;
047
048 //TODO these rates shall be kept in DB tables or as parameter
049 // set up the tax rate maps
050 static {
051 federalTaxRates = new HashMap<String, ArrayList<BigDecimal>>();
052 stateTaxRates = new HashMap<String, ArrayList<BigDecimal>>();
053
054 ArrayList<BigDecimal> fedrates = new ArrayList<BigDecimal>();
055 fedrates.add(new BigDecimal(30));
056 fedrates.add(new BigDecimal(14));
057 fedrates.add(new BigDecimal(0));
058 federalTaxRates.put("F", fedrates);
059
060 fedrates = new ArrayList<BigDecimal>();
061 fedrates.add(new BigDecimal(30));
062 fedrates.add(new BigDecimal(15));
063 fedrates.add(new BigDecimal(10));
064 fedrates.add(new BigDecimal(5));
065 fedrates.add(new BigDecimal(0));
066 federalTaxRates.put("R", fedrates);
067
068 fedrates = new ArrayList<BigDecimal>();
069 fedrates.add(new BigDecimal(30));
070 fedrates.add(new BigDecimal(0));
071 federalTaxRates.put("I", fedrates);
072 federalTaxRates.put("A", fedrates);
073 federalTaxRates.put("O", fedrates);
074
075 ArrayList<BigDecimal> strates = new ArrayList<BigDecimal>();
076 strates.add(new BigDecimal("3.40"));
077 strates.add(new BigDecimal(0));
078 stateTaxRates.put("F", strates);
079 stateTaxRates.put("A", strates);
080 stateTaxRates.put("O", strates);
081
082 strates = new ArrayList<BigDecimal>();
083 strates.add(new BigDecimal(0));
084 stateTaxRates.put("I", strates);
085 stateTaxRates.put("R", strates);
086 }
087 */
088
089 /**
090 * Process business rules applicable to tax area data before calculating the withholding tax on payment request.
091 * @param paymentRequest - payment request document
092 * @return true if all business rules applicable passes; false otherwise.
093 */
094 public boolean validate(AttributedDocumentEvent event) {
095 PaymentRequestDocument preq = (PaymentRequestDocument)event.getDocument();
096
097 // do this validation only at route level of awaiting tax review
098 if (!preq.getStatusCode().equals(PaymentRequestStatuses.AWAITING_TAX_REVIEW))
099 return true;
100
101 MessageMap errorMap = GlobalVariables.getMessageMap();
102 errorMap.clearErrorPath();
103 //errorMap.addToErrorPath(KFSPropertyConstants.DOCUMENT);
104 errorMap.addToErrorPath(PurapConstants.PAYMENT_REQUEST_TAX_TAB_ERRORS);
105
106 boolean valid = true;
107 valid &= validateTaxIncomeClass(preq);
108 valid &= validateTaxRates(preq);
109 valid &= validateTaxIndicators(preq);
110
111 errorMap.clearErrorPath();
112 return valid;
113 }
114
115 /**
116 * Validates tax income class: when Non-Reportable income class is chosen, all other fields shall be left blank;
117 * It assumed that the input tax income class code is valid (existing and active in the system) since it's chosen from drop-down list.
118 * otherwise tax rates and country are required;
119 * @param paymentRequest - payment request document
120 * @return true if this validation passes; false otherwise.
121 */
122 protected boolean validateTaxIncomeClass(PaymentRequestDocument preq) {
123 boolean valid = true;
124 MessageMap errorMap = GlobalVariables.getMessageMap();
125
126 // TaxClassificationCode is required field
127 if (StringUtils.isEmpty(preq.getTaxClassificationCode())) {
128 valid = false;
129 errorMap.putError(PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_REQUIRED, PurapPropertyConstants.TAX_CLASSIFICATION_CODE);
130 }
131 // If TaxClassificationCode is N (Non_Reportable, then other fields shall be blank.
132 else if (StringUtils.equalsIgnoreCase(preq.getTaxClassificationCode(), "N")) {
133 if (preq.getTaxFederalPercent() != null && preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0) {
134 valid = false;
135 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
136 }
137 if (preq.getTaxStatePercent() != null && preq.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0) {
138 valid = false;
139 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_STATE_PERCENT);
140 }
141 if (!StringUtils.isEmpty(preq.getTaxCountryCode())) {
142 valid = false;
143 errorMap.putError(PurapPropertyConstants.TAX_COUNTRY_CODE, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_COUNTRY_CODE);
144 }
145 if (!StringUtils.isEmpty(preq.getTaxNQIId())) {
146 valid = false;
147 errorMap.putError(PurapPropertyConstants.TAX_NQI_ID, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_NQI_ID);
148 }
149 if (preq.getTaxSpecialW4Amount() != null && preq.getTaxSpecialW4Amount().compareTo(new BigDecimal(0)) != 0) {
150 valid = false;
151 errorMap.putError(PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT);
152 }
153 if (ObjectUtils.nullSafeEquals(preq.getTaxExemptTreatyIndicator(), true)) {
154 valid = false;
155 errorMap.putError(PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR);
156 }
157 if (ObjectUtils.nullSafeEquals(preq.getTaxGrossUpIndicator(), true)) {
158 valid = false;
159 errorMap.putError(PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR);
160 }
161 if (ObjectUtils.nullSafeEquals(preq.getTaxForeignSourceIndicator(), true)) {
162 valid = false;
163 errorMap.putError(PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR);
164 }
165 if (ObjectUtils.nullSafeEquals(preq.getTaxUSAIDPerDiemIndicator(), true)) {
166 valid = false;
167 errorMap.putError(PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR);
168 }
169 if (ObjectUtils.nullSafeEquals(preq.getTaxOtherExemptIndicator(), true)) {
170 valid = false;
171 errorMap.putError(PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR);
172 }
173 }
174 else {
175 // If TaxClassificationCode is not N (Non_Reportable, then the federal/state tax percent and country are required.
176 if (preq.getTaxFederalPercent() == null) {
177 valid = false;
178 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_REQUIRED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
179 }
180 if (preq.getTaxStatePercent() == null) {
181 valid = false;
182 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_REQUIRED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_STATE_PERCENT);
183 }
184 if (StringUtils.isEmpty(preq.getTaxCountryCode())) {
185 valid = false;
186 errorMap.putError(PurapPropertyConstants.TAX_COUNTRY_CODE, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_REQUIRED_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_COUNTRY_CODE);
187 }
188 }
189
190 return valid;
191 }
192
193 /**
194 * Validates federal and state tax rates based on each other and the income class.
195 * Validation will be bypassed if income class is empty or N, or tax rates are null.
196 * @param paymentRequest - payment request document
197 * @return true if this validation passes; false otherwise.
198 */
199 protected boolean validateTaxRates(PaymentRequestDocument preq) {
200 boolean valid = true;
201 String code = preq.getTaxClassificationCode();
202 BigDecimal fedrate = preq.getTaxFederalPercent();
203 BigDecimal strate = preq.getTaxStatePercent();
204 MessageMap errorMap = GlobalVariables.getMessageMap();
205
206 // only test the cases when income class and tax rates aren't empty/N
207 if (StringUtils.isEmpty(code) || StringUtils.equalsIgnoreCase(code, "N") || fedrate == null || strate == null)
208 return true;
209
210 // validate that the federal and state tax rates are among the allowed set
211 ArrayList<BigDecimal> fedrates = retrieveTaxRates(code, "F"); //(ArrayList<BigDecimal>) federalTaxRates.get(code);
212 if (!listContainsValue(fedrates, fedrate)) {
213 valid = false;
214 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_VALUE_INVALID_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
215 }
216 ArrayList<BigDecimal> strates = retrieveTaxRates(code, "S"); //(ArrayList<BigDecimal>) stateTaxRates.get(code);
217 if (!listContainsValue(strates, strate)) {
218 valid = false;
219 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_VALUE_INVALID_IF, PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapPropertyConstants.TAX_STATE_PERCENT);
220 }
221
222 // validate that the federal and state tax rate abide to certain relationship
223 if (fedrate.compareTo(new BigDecimal(0)) == 0 && strate.compareTo(new BigDecimal(0)) != 0) {
224 valid = false;
225 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapPropertyConstants.TAX_STATE_PERCENT);
226 }
227 boolean hasstrate = code.equalsIgnoreCase("F") || code.equalsIgnoreCase("A") || code.equalsIgnoreCase("O");
228 if (fedrate.compareTo(new BigDecimal(0)) > 0 && strate.compareTo(new BigDecimal(0)) <= 0 && hasstrate) {
229 valid = false;
230 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_NOT_ZERO_IF, PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapPropertyConstants.TAX_STATE_PERCENT);
231 }
232
233 return valid;
234 }
235
236 /**
237 * Validates rules among tax treaty, gross up, foreign source, USAID, other exempt, and Special W-4.
238 * @param paymentRequest - payment request document
239 * @return true if this validation passes; false otherwise.
240 */
241 protected boolean validateTaxIndicators(PaymentRequestDocument preq) {
242 boolean valid = true;
243 MessageMap errorMap = GlobalVariables.getMessageMap();
244
245 // if choose tax treaty, cannot choose any of the other above
246 if (ObjectUtils.nullSafeEquals(preq.getTaxExemptTreatyIndicator(), true)) {
247 if (ObjectUtils.nullSafeEquals(preq.getTaxGrossUpIndicator(), true)) {
248 valid = false;
249 errorMap.putError(PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR);
250 }
251 if (ObjectUtils.nullSafeEquals(preq.getTaxForeignSourceIndicator(), true)) {
252 valid = false;
253 errorMap.putError(PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR);
254 }
255 if (ObjectUtils.nullSafeEquals(preq.getTaxUSAIDPerDiemIndicator(), true)) {
256 valid = false;
257 errorMap.putError(PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR);
258 }
259 if (ObjectUtils.nullSafeEquals(preq.getTaxOtherExemptIndicator(), true)) {
260 valid = false;
261 errorMap.putError(PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR);
262 }
263 if (preq.getTaxSpecialW4Amount() != null && preq.getTaxSpecialW4Amount().compareTo(new KualiDecimal(0)) != 0) {
264 valid = false;
265 errorMap.putError(PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT);
266 }
267 }
268
269 // if choose gross up, cannot choose any other above, and fed tax rate cannot be zero
270 if (ObjectUtils.nullSafeEquals(preq.getTaxGrossUpIndicator(), true)) {
271 if (ObjectUtils.nullSafeEquals(preq.getTaxExemptTreatyIndicator(), true)) {
272 valid = false;
273 errorMap.putError(PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR);
274 }
275 if (ObjectUtils.nullSafeEquals(preq.getTaxForeignSourceIndicator(), true)) {
276 valid = false;
277 errorMap.putError(PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR);
278 }
279 if (ObjectUtils.nullSafeEquals(preq.getTaxUSAIDPerDiemIndicator(), true)) {
280 valid = false;
281 errorMap.putError(PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR);
282 }
283 if (ObjectUtils.nullSafeEquals(preq.getTaxOtherExemptIndicator(), true)) {
284 valid = false;
285 errorMap.putError(PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR);
286 }
287 if (preq.getTaxSpecialW4Amount() != null && preq.getTaxSpecialW4Amount().compareTo(new KualiDecimal(0)) != 0) {
288 valid = false;
289 errorMap.putError(PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT);
290 }
291 if (preq.getTaxFederalPercent() == null || preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) == 0 ) {
292 valid = false;
293 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_NOT_ZERO_IF, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
294 }
295 }
296
297 // if choose foreign source, cannot choose any other above, and tax rates shall be zero
298 if (ObjectUtils.nullSafeEquals(preq.getTaxForeignSourceIndicator(), true)) {
299 if (ObjectUtils.nullSafeEquals(preq.getTaxExemptTreatyIndicator(), true)) {
300 valid = false;
301 errorMap.putError(PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR);
302 }
303 if (ObjectUtils.nullSafeEquals(preq.getTaxGrossUpIndicator(), true)) {
304 valid = false;
305 errorMap.putError(PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR);
306 }
307 if (ObjectUtils.nullSafeEquals(preq.getTaxUSAIDPerDiemIndicator(), true)) {
308 valid = false;
309 errorMap.putError(PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR);
310 }
311 if (ObjectUtils.nullSafeEquals(preq.getTaxOtherExemptIndicator(), true)) {
312 valid = false;
313 errorMap.putError(PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR);
314 }
315 if (preq.getTaxSpecialW4Amount() != null && preq.getTaxSpecialW4Amount().compareTo(new KualiDecimal(0)) != 0) {
316 valid = false;
317 errorMap.putError(PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT);
318 }
319 if (preq.getTaxFederalPercent() != null && preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0) {
320 valid = false;
321 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
322 }
323 if (preq.getTaxStatePercent() != null && preq.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0) {
324 valid = false;
325 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapPropertyConstants.TAX_STATE_PERCENT);
326 }
327 }
328
329 // if choose USAID per diem, cannot choose any other above except other exempt code, which must be checked; income class shall be fellowship with tax rates 0
330 if (ObjectUtils.nullSafeEquals(preq.getTaxUSAIDPerDiemIndicator(), true)) {
331 if (ObjectUtils.nullSafeEquals(preq.getTaxExemptTreatyIndicator(), true)) {
332 valid = false;
333 errorMap.putError(PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR);
334 }
335 if (ObjectUtils.nullSafeEquals(preq.getTaxGrossUpIndicator(), true)) {
336 valid = false;
337 errorMap.putError(PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR);
338 }
339 if (ObjectUtils.nullSafeEquals(preq.getTaxForeignSourceIndicator(), true)) {
340 valid = false;
341 errorMap.putError(PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR);
342 }
343 if (!ObjectUtils.nullSafeEquals(preq.getTaxOtherExemptIndicator(), true)) {
344 valid = false;
345 errorMap.putError(PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_REQUIRED_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR);
346 }
347 if (preq.getTaxSpecialW4Amount() != null && preq.getTaxSpecialW4Amount().compareTo(new KualiDecimal(0)) != 0) {
348 valid = false;
349 errorMap.putError(PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT);
350 }
351 if (StringUtils.isEmpty(preq.getTaxClassificationCode()) || !StringUtils.equalsIgnoreCase(preq.getTaxClassificationCode(), "F")) {
352 valid = false;
353 errorMap.putError(PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_VALUE_INVALID_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_CLASSIFICATION_CODE);
354 }
355 if (preq.getTaxFederalPercent() != null && preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0 ) {
356 valid = false;
357 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
358 }
359 if (preq.getTaxStatePercent() != null && preq.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0 ) {
360 valid = false;
361 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_USAID_PER_DIEM_INDICATOR, PurapPropertyConstants.TAX_STATE_PERCENT);
362 }
363 }
364
365 // if choose exempt under other code, cannot choose any other above except USAID, and tax rates shall be zero
366 if (ObjectUtils.nullSafeEquals(preq.getTaxOtherExemptIndicator(), true)) {
367 if (ObjectUtils.nullSafeEquals(preq.getTaxExemptTreatyIndicator(), true)) {
368 valid = false;
369 errorMap.putError(PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR);
370 }
371 if (ObjectUtils.nullSafeEquals(preq.getTaxGrossUpIndicator(), true)) {
372 valid = false;
373 errorMap.putError(PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR);
374 }
375 if (ObjectUtils.nullSafeEquals(preq.getTaxForeignSourceIndicator(), true)) {
376 valid = false;
377 errorMap.putError(PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR);
378 }
379 if (preq.getTaxSpecialW4Amount() != null && preq.getTaxSpecialW4Amount().compareTo(new KualiDecimal(0)) != 0) {
380 valid = false;
381 errorMap.putError(PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT);
382 }
383 if (preq.getTaxFederalPercent() != null && preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0 ) {
384 valid = false;
385 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
386 }
387 if (preq.getTaxStatePercent() != null && preq.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0 ) {
388 valid = false;
389 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_OTHER_EXEMPT_INDICATOR, PurapPropertyConstants.TAX_STATE_PERCENT);
390 }
391 }
392
393 // if choose Special W-4, cannot choose tax treaty, gross up, and foreign source; income class shall be fellowship with tax rates 0
394 if (preq.getTaxSpecialW4Amount() != null && preq.getTaxSpecialW4Amount().compareTo(new KualiDecimal(0)) != 0 ) {
395 if (ObjectUtils.nullSafeEquals(preq.getTaxExemptTreatyIndicator(), true)) {
396 valid = false;
397 errorMap.putError(PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapPropertyConstants.TAX_EXEMPT_TREATY_INDICATOR);
398 }
399 if (ObjectUtils.nullSafeEquals(preq.getTaxGrossUpIndicator(), true)) {
400 valid = false;
401 errorMap.putError(PurapPropertyConstants.TAX_GROSS_UP_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapPropertyConstants.TAX_GROSS_UP_INDICATOR);
402 }
403 if (ObjectUtils.nullSafeEquals(preq.getTaxForeignSourceIndicator(), true)) {
404 valid = false;
405 errorMap.putError(PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_DISALLOWED_IF, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapPropertyConstants.TAX_FOREIGN_SOURCE_INDICATOR);
406 }
407 if (preq.getTaxSpecialW4Amount().compareTo(new KualiDecimal(0)) < 0) {
408 valid = false;
409 errorMap.putError(PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_VALUE_MUST_NOT_NEGATIVE, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT);
410 }
411 if (StringUtils.isEmpty(preq.getTaxClassificationCode()) || !StringUtils.equalsIgnoreCase(preq.getTaxClassificationCode(), "F")) {
412 valid = false;
413 errorMap.putError(PurapPropertyConstants.TAX_CLASSIFICATION_CODE, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_FIELD_VALUE_INVALID_IF, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapPropertyConstants.TAX_CLASSIFICATION_CODE);
414 }
415 if (preq.getTaxFederalPercent() != null && preq.getTaxFederalPercent().compareTo(new BigDecimal(0)) != 0 ) {
416 valid = false;
417 errorMap.putError(PurapPropertyConstants.TAX_FEDERAL_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapPropertyConstants.TAX_FEDERAL_PERCENT);
418 }
419 if (preq.getTaxStatePercent() != null && preq.getTaxStatePercent().compareTo(new BigDecimal(0)) != 0 ) {
420 valid = false;
421 errorMap.putError(PurapPropertyConstants.TAX_STATE_PERCENT, PurapKeyConstants.ERROR_PAYMENT_REQUEST_TAX_RATE_MUST_ZERO_IF, PurapPropertyConstants.TAX_SPECIAL_W4_AMOUNT, PurapPropertyConstants.TAX_STATE_PERCENT);
422 }
423 }
424
425 return valid;
426 }
427
428 /**
429 * Initiates the federal and state tax rate maps for the purpose of validation on tax rates.
430 *
431 private void loadTaxRates() {
432 Collection<TaxIncomeClassCode> incomeClasses = retrieveAllTaxIncomeClasses();
433 for (TaxIncomeClassCode incomeClass : incomeClasses) {
434 String incomeCode = incomeClass.getCode();
435 ArrayList<BigDecimal> fedrates = retrieveTaxRates(incomeCode, "F"); // federal rates
436 federalTaxRates.put(incomeCode, fedrates);
437 ArrayList<BigDecimal> strates = retrieveTaxRates(incomeCode, "S"); // state rates
438 federalTaxRates.put(incomeCode, strates);
439 }
440 }
441 */
442
443 /**
444 * Retrieves all valid tax income classes in the system.
445 *
446 public Collection<TaxIncomeClassCode> retrieveAllTaxIncomeClasses() {
447 return businessObjectService.findAll(TaxIncomeClassCode.class);
448 }
449 */
450
451 /**
452 * Retrieve active NonResidentAlien tax rate percent from database based on the specified income class and federal/state tax type.
453 * @param incomeClassCode The specified income class type code.
454 * @param incomeTaxTypeCode The specified income tax type code.
455 * @return The array list containing the tax rates retrieved.
456 */
457 public ArrayList<BigDecimal> retrieveTaxRates(String incomeClassCode, String incomeTaxTypeCode) {
458 ArrayList<BigDecimal> rates = new ArrayList<BigDecimal>();
459 Map<String, String> criterion = new HashMap<String, String>();
460 criterion.put("incomeClassCode", incomeClassCode);
461 criterion.put("incomeTaxTypeCode", incomeTaxTypeCode);
462 criterion.put("active", "Y"); // only retrieve active tax percents
463 List<NonResidentAlienTaxPercent> percents = (List<NonResidentAlienTaxPercent>)businessObjectService.findMatching(NonResidentAlienTaxPercent.class, criterion);
464
465 for (NonResidentAlienTaxPercent percent : percents) {
466 rates.add(percent.getIncomeTaxPercent().bigDecimalValue());
467 }
468 return rates;
469 }
470
471 /**
472 * Returns true if the specified ArrayList contains the specified BigDecimal value.
473 * @param list the specified ArrayList
474 * @param value the specified BigDecimal
475 */
476 protected boolean listContainsValue(ArrayList<BigDecimal> list, BigDecimal value) {
477 if (list == null || value == null)
478 return false;
479 for (BigDecimal val : list) {
480 if (val.compareTo(value) == 0)
481 return true;
482 }
483 return false;
484 }
485
486 public BusinessObjectService getBusinessObjectService() {
487 return businessObjectService;
488 }
489
490 public void setBusinessObjectService(BusinessObjectService businessObjectService) {
491 this.businessObjectService = businessObjectService;
492 }
493 }