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.endow.document.validation.impl; 017 018 import java.math.BigDecimal; 019 import java.util.Collection; 020 021 import org.apache.commons.lang.StringUtils; 022 import org.kuali.kfs.module.endow.EndowConstants; 023 import org.kuali.kfs.module.endow.EndowKeyConstants; 024 import org.kuali.kfs.module.endow.EndowPropertyConstants; 025 import org.kuali.kfs.module.endow.businessobject.ClassCode; 026 import org.kuali.kfs.module.endow.businessobject.HoldingHistory; 027 import org.kuali.kfs.module.endow.businessobject.Security; 028 import org.kuali.kfs.module.endow.document.HoldingHistoryValueAdjustmentDocument; 029 import org.kuali.kfs.module.endow.document.service.HoldingHistoryService; 030 import org.kuali.kfs.module.endow.document.service.SecurityService; 031 import org.kuali.kfs.module.endow.util.KEMCalculationRoundingHelper; 032 import org.kuali.kfs.sys.context.SpringContext; 033 import org.kuali.rice.kns.document.Document; 034 import org.kuali.rice.kns.util.GlobalVariables; 035 import org.kuali.rice.kns.util.ObjectUtils; 036 import org.kuali.rice.kns.rules.TransactionalDocumentRuleBase; 037 038 public class HoldingHistoryValueAdjustmentDocumentRules extends TransactionalDocumentRuleBase { 039 040 /** 041 * @see org.kuali.rice.kns.rules.DocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.Document) 042 */ 043 @Override 044 protected boolean processCustomRouteDocumentBusinessRules(Document document) { 045 046 HoldingHistoryValueAdjustmentDocument holdingHistoryValueAdjustmentDocument = (HoldingHistoryValueAdjustmentDocument) document; 047 048 boolean isValid = true; 049 050 if (this.isSecurityCodeEmpty(holdingHistoryValueAdjustmentDocument)) { 051 return false; 052 } 053 if (!this.validateSecurityCode(holdingHistoryValueAdjustmentDocument)) { 054 return false; 055 } 056 057 // Checks if Security is Active 058 isValid &= this.isSecurityActive(holdingHistoryValueAdjustmentDocument); 059 // check if it is Liability class type code for the given security id 060 isValid &= this.validateSecurityClassCodeTypeNotLiability(holdingHistoryValueAdjustmentDocument); 061 // check the valuation method for Unit value and make sure market value is not entered. 062 isValid &= this.checkValuationMethodForUnitOrSecurityValue(holdingHistoryValueAdjustmentDocument); 063 // check if the unit value is a positve value 064 isValid &= this.isUnitValuePositive(holdingHistoryValueAdjustmentDocument); 065 // check if the market value is a positive value 066 isValid &= this.isMarketValuePositive(holdingHistoryValueAdjustmentDocument); 067 068 return isValid; 069 } 070 071 /** 072 * Validates the Security code to make sure the value is not empty. 073 * 074 * @param tranSecurity 075 * @return true if security code is empty, else false. 076 */ 077 protected boolean isSecurityCodeEmpty(HoldingHistoryValueAdjustmentDocument document) { 078 079 if (StringUtils.isEmpty(document.getSecurityId())) { 080 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_SECURITY_ID, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_SECURITY_ID_REQUIRED); 081 return true; 082 } 083 084 return false; 085 } 086 087 /** 088 * Validates the Security code by trying to create a Security object from the code. 089 * 090 * @param document 091 * @return true if security code is valid one, false otherwise 092 */ 093 protected boolean validateSecurityCode(HoldingHistoryValueAdjustmentDocument document) { 094 Security security = (Security) SpringContext.getBean(SecurityService.class).getByPrimaryKey(document.getSecurityId()); 095 document.setSecurity(security); 096 097 if (ObjectUtils.isNull(security)) { 098 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_SECURITY_ID, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_SECURITY_ID_INVALID); 099 return false; 100 } 101 102 return true; 103 } 104 105 /** 106 * Checks if the Security is Active. 107 * 108 * @param document 109 * @return true if the security is active, else false 110 */ 111 protected boolean isSecurityActive(HoldingHistoryValueAdjustmentDocument document) { 112 Security security = document.getSecurity(); 113 114 if (!security.isActive()) { 115 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_SECURITY_ID, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_SECURITY_ID_INACTIVE); 116 return false; 117 } 118 119 return true; 120 } 121 122 /** 123 * Validates that the security class code type is not Liability. 124 * 125 * @param document 126 * @return true is valid, false otherwise 127 */ 128 protected boolean validateSecurityClassCodeTypeNotLiability(HoldingHistoryValueAdjustmentDocument document) { 129 boolean isValid = true; 130 131 Security security = document.getSecurity(); 132 133 ClassCode classCode = security.getClassCode(); 134 if (ObjectUtils.isNotNull(classCode)) { 135 String classCodeType = classCode.getClassCodeType(); 136 if (EndowConstants.ClassCodeTypes.LIABILITY.equalsIgnoreCase(classCodeType)) { 137 isValid = false; 138 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_SECURITY_ID, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_SECURITY_ID_NOT_LIABILITY); 139 } 140 } 141 142 return isValid; 143 } 144 145 /** 146 * Checks if the unit value entered is positive value 147 * 148 * @param document 149 * @return true if positive, else false 150 */ 151 protected boolean isUnitValuePositive(HoldingHistoryValueAdjustmentDocument document) { 152 if (ObjectUtils.isNotNull(document.getSecurityUnitValue()) && document.getSecurityUnitValue().compareTo(BigDecimal.ZERO) <= 0) { 153 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_UNIT_VALUE, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_UNIT_VALUE_NOT_POSITIVE); 154 return false; 155 } 156 157 return true; 158 } 159 160 /** 161 * Checks if the market value entered is positive value 162 * 163 * @param document 164 * @return true if positive, else false 165 */ 166 protected boolean isMarketValuePositive(HoldingHistoryValueAdjustmentDocument document) { 167 // reset Market value if unit valuation method is U (Unit value) 168 resetMarketValueToNullWhenUnitValueEntered(document); 169 170 if (ObjectUtils.isNotNull(document.getSecurityMarketValue()) && document.getSecurityMarketValue().compareTo(BigDecimal.ZERO) <= 0) { 171 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_MARKET_VALUE, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_MARKET_VALUE_NOT_POSITIVE); 172 return false; 173 } 174 175 return true; 176 } 177 178 /** 179 * Checks if security valuation method is Unit value and if so make sure nothing entered for Market Value 180 * 181 * @param document 182 * @return true if only Unit Value is entered, else false 183 */ 184 protected boolean checkValuationMethodForUnitOrSecurityValue(HoldingHistoryValueAdjustmentDocument document) { 185 String valuationMethodCode = document.getSecurity().getClassCode().getSecurityValuationMethod().getCode(); 186 187 // check if the valuation method is U (unit value) and if so, then make sure no value is entered for market value. 188 if (EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_VALUATION_METHOD_FOR_UNIT_VALUE.equals(valuationMethodCode)) { 189 if (ObjectUtils.isNull(document.getSecurityUnitValue())) { 190 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_UNIT_VALUE, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_UNIT_VALUE_REQUIRED); 191 return false; 192 } 193 194 if (ObjectUtils.isNotNull(document.getSecurityMarketValue())) { 195 document.setSecurityMarketValue(null); 196 return true; 197 } 198 } 199 200 // check if the valuation method is M (market value) and if so, then make sure no value is entered for unit value. 201 if (EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_VALUATION_METHOD_FOR_MARKET_VALUE.equals(valuationMethodCode)) { 202 if (ObjectUtils.isNull(document.getSecurityMarketValue())) { 203 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_DETAILS_ERRORS + EndowPropertyConstants.HISTORY_VALUE_ADJUSTMENT_MARKET_VALUE, EndowKeyConstants.HoldingHistoryValueAdjustmentConstants.ERROR_HISTORY_VALUE_ADJUSTMENT_MARKET_VALUE_REQUIRED); 204 return false; 205 } 206 207 if (ObjectUtils.isNotNull(document.getSecurityMarketValue())) { 208 // calculate Unit value as per 5.6.2.1.2 in KEM Adjustment_Transactions+v.1.3 document... 209 document.setSecurityUnitValue(calculateUnitValueWhenMarketValueEntered(document)); 210 return true; 211 } 212 } 213 214 return true; 215 } 216 217 /** 218 * Reset the value for Market Value that the user entered if the valuation method is U (Unit Value) 219 * 220 * @param document 221 */ 222 protected void resetMarketValueToNullWhenUnitValueEntered(HoldingHistoryValueAdjustmentDocument document) { 223 String valuationMethodCode = document.getSecurity().getClassCode().getSecurityValuationMethod().getCode(); 224 225 // check if the valuation method is U (unit value) and if so, then make sure no value is entered for market value. 226 if (EndowConstants.HistoryHoldingValueAdjustmentValuationCodes.HISTORY_VALUE_ADJUSTMENT_VALUATION_METHOD_FOR_UNIT_VALUE.equals(valuationMethodCode)) { 227 if (ObjectUtils.isNotNull(document.getSecurityMarketValue())) { 228 document.setSecurityMarketValue(null); 229 } 230 } 231 } 232 233 /** 234 * Calculates unit value when security id's valuation method is M and market value is entered. 235 */ 236 protected BigDecimal calculateUnitValueWhenMarketValueEntered(HoldingHistoryValueAdjustmentDocument document) { 237 BigDecimal unitValue = BigDecimal.ZERO; 238 BigDecimal totalUnits = BigDecimal.ZERO; 239 240 BigDecimal marketValue = document.getSecurityMarketValue(); 241 242 Collection<HoldingHistory> holdingHistoryRecords = SpringContext.getBean(HoldingHistoryService.class).getHoldingHistoryBySecuritIdAndMonthEndId(document.getSecurityId(), document.getHoldingMonthEndDate()); 243 for (HoldingHistory holdingHistory : holdingHistoryRecords) { 244 totalUnits = totalUnits.add(holdingHistory.getUnits()); // sum up the units and store it 245 } 246 247 ClassCode classCode = document.getSecurity().getClassCode(); 248 249 if (ObjectUtils.isNotNull(classCode) && ObjectUtils.isNotNull(totalUnits) && totalUnits.compareTo(BigDecimal.ZERO) != 0) { 250 if (EndowConstants.ClassCodeTypes.BOND.equalsIgnoreCase(classCode.getClassCodeType())) { 251 unitValue = KEMCalculationRoundingHelper.divide((marketValue.multiply(new BigDecimal(100))), totalUnits, EndowConstants.Scale.SECURITY_UNIT_VALUE); 252 } 253 else { 254 unitValue = KEMCalculationRoundingHelper.divide(marketValue, totalUnits, EndowConstants.Scale.SECURITY_UNIT_VALUE); 255 } 256 } 257 258 return unitValue; 259 } 260 261 }