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