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    }