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 }