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
020 import org.kuali.kfs.module.endow.EndowKeyConstants;
021 import org.kuali.kfs.module.endow.EndowPropertyConstants;
022 import org.kuali.kfs.module.endow.businessobject.HoldingTaxLot;
023 import org.kuali.kfs.module.endow.businessobject.HoldingTaxLotRebalance;
024 import org.kuali.rice.kns.document.MaintenanceDocument;
025 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
026 import org.kuali.rice.kns.util.GlobalVariables;
027 import org.kuali.rice.kns.util.MessageMap;
028
029 public class HoldingTaxLotRebalanceRule extends MaintenanceDocumentRuleBase {
030
031 /**
032 * @see org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase#processCustomRouteDocumentBusinessRules(org.kuali.rice.kns.document.MaintenanceDocument)
033 */
034 @Override
035 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document)
036 {
037 boolean isValid = true;
038
039 // First check for errors from parent class, and determine if any previous errors
040 // were found.
041 isValid &= super.processCustomRouteDocumentBusinessRules(document);
042 MessageMap errorMap = GlobalVariables.getMessageMap();
043 isValid &= errorMap.hasNoErrors();
044
045 // 'newDocument' represents the re-balanced document, and 'oldDocument' represents the original document.
046 HoldingTaxLotRebalance newBusinessObject = getNewHoldingTaxLotRebalanceMaintenceDocument(document);
047 HoldingTaxLotRebalance oldBusinessObject = getOldHoldingTaxLotRebalanceMaintenceDocument(document);
048
049 // If no previous errors have been found, then perform our own custom error checks.
050 if (isValid) {
051
052 // First verify that the unit and cost values are valid.
053 isValid &= validateUnitValue(newBusinessObject);
054 isValid &= validateCostValue(newBusinessObject);
055
056 // Are the unit and cost values valid? If not, then throw the error(s)
057 // early before further testing. There's no reason to perform future
058 // checks on invalid values.
059 if (!isValid) {
060 return isValid;
061 }
062
063 // Verify that if either units or cost are zero, they're both zero.
064 isValid &= validateAllZero(newBusinessObject);
065
066 // If only one of the units or cost fields are zero, show error to
067 // user before continuing.
068 if (!isValid) {
069 return isValid;
070 }
071
072 // Verify that total units and total cost for all tax lot balances
073 // are still the same.
074 isValid &= validateTotalUnits(oldBusinessObject, newBusinessObject);
075 isValid &= validateTotalCost(oldBusinessObject, newBusinessObject);
076 }
077
078 return isValid;
079 }
080
081 /**
082 * Helper method, used to generate the correct path of the cost/unit value
083 * of a given tax lot.
084 *
085 * @param index
086 * @return
087 */
088 private String getHoldingTaxLotErrorPath(int index)
089 {
090 return EndowPropertyConstants.HOLDING_TAX_LOT_REBAL_LOTS_TAB + "[" + index + "].";
091 }
092
093 /**
094 * Helper method to get the re-balanced i.e. new business object.
095 *
096 * @param document
097 * @return Re-balanced HoldingTaxLotRebalance business object
098 */
099 private HoldingTaxLotRebalance getNewHoldingTaxLotRebalanceMaintenceDocument(MaintenanceDocument document)
100 {
101 return (HoldingTaxLotRebalance) document.getNewMaintainableObject().getBusinessObject();
102 }
103
104 /**
105 *
106 * Helper method to get the original i.e. old business object.
107 *
108 * @param document
109 * @return Original HoldingTaxLotRebalance business object
110 */
111 private HoldingTaxLotRebalance getOldHoldingTaxLotRebalanceMaintenceDocument(MaintenanceDocument document)
112 {
113 return (HoldingTaxLotRebalance) document.getOldMaintainableObject().getBusinessObject();
114 }
115
116 /**
117 * Verifies that the total units for all the tax lots is still the same i.e. balanced.
118 *
119 * @param oldDocument
120 * @param newDocument
121 * @return True if total units are balanced
122 */
123 protected boolean validateTotalUnits(HoldingTaxLotRebalance oldBusinessObject, HoldingTaxLotRebalance newBusinessObject)
124 {
125 boolean isValid = true;
126
127 // Calculate the total number of units from the new and old documents to ensure
128 // that they still match the original (old) document.
129 BigDecimal totalOldUnits = BigDecimal.ZERO;
130 for (HoldingTaxLot taxLot : newBusinessObject.getHoldingTaxLots()) {
131 totalOldUnits = totalOldUnits.add(taxLot.getUnits());
132 }
133
134 BigDecimal totalNewUnits = BigDecimal.ZERO;
135 for (HoldingTaxLot taxLot : oldBusinessObject.getHoldingTaxLots()) {
136 totalNewUnits = totalNewUnits.add(taxLot.getUnits());
137 }
138
139 // Determine if the calculated total value of all the tax lots is still equal
140 // to the original total i.e. is still balanced.
141 if (totalNewUnits.compareTo(totalOldUnits) != 0) {
142 putFieldError(EndowPropertyConstants.HOLDING_TAX_LOT_REBAL_LOTS_TAB,
143 EndowKeyConstants.HoldingTaxLotRebalanceConstants.ERROR_HLDG_TAX_LOT_REBALANCE_TOTAL_UNITS_NOT_BALANCED);
144 isValid = false;
145 }
146
147 return isValid;
148 }
149
150 /**
151 * Verifies that the total cost for all the tax lots is still the same i.e. balanced.
152 *
153 * @param oldDocument
154 * @param newDocument
155 * @return True if total cost is balanced
156 */
157 protected boolean validateTotalCost(HoldingTaxLotRebalance oldBusinessObject, HoldingTaxLotRebalance newBusinessObject)
158 {
159 boolean isValid = true;
160 // Calculate the total cost from the new and old documents to ensure
161 // that they still match the original (old) document.
162 BigDecimal totalOldCost = BigDecimal.ZERO;
163 for (HoldingTaxLot taxLot : newBusinessObject.getHoldingTaxLots()) {
164 totalOldCost = totalOldCost.add(taxLot.getCost());
165 }
166
167 BigDecimal totalNewCost = BigDecimal.ZERO;
168 for (HoldingTaxLot taxLot : newBusinessObject.getHoldingTaxLots()) {
169 totalNewCost = totalNewCost.add(taxLot.getCost());
170 }
171
172 // Determine if the calculated total value of all the tax lots is still equal
173 // to the original total i.e. is still balanced.
174 if (totalNewCost.compareTo(totalOldCost) != 0) {
175 putFieldError(EndowPropertyConstants.HOLDING_TAX_LOT_REBAL_LOTS_TAB,
176 EndowKeyConstants.HoldingTaxLotRebalanceConstants.ERROR_HLDG_TAX_LOT_REBALANCE_TOTAL_COST_NOT_BALANCED);
177 isValid = false;
178 }
179
180 return isValid;
181 }
182
183 /**
184 * Validates if the units value isn't negative.
185 *
186 * @param newDocument
187 * @return True if units is non-negative.
188 */
189 protected boolean validateUnitValue(HoldingTaxLotRebalance newBusinessObject)
190 {
191 boolean isValid = true;
192 for (HoldingTaxLot taxLot : newBusinessObject.getHoldingTaxLots()) {
193 if (taxLot.getUnits().signum() < 0) {
194 int index = newBusinessObject.getHoldingTaxLots().indexOf(taxLot);
195 putFieldError(getHoldingTaxLotErrorPath(index) + EndowPropertyConstants.HOLDING_TAX_LOT_UNITS,
196 EndowKeyConstants.HoldingTaxLotRebalanceConstants.ERROR_HLDG_TAX_LOT_REBALANCE_UNITS_INVALID);
197 isValid = false;
198 }
199 }
200
201 return isValid;
202 }
203
204 /**
205 * Validates if the cost value isn't negative.
206 *
207 * @param newDocument
208 * @return True if cost is non-negative.
209 */
210 protected boolean validateCostValue(HoldingTaxLotRebalance newBusinessObject)
211 {
212 boolean isValid = true;
213 for (HoldingTaxLot taxLot : newBusinessObject.getHoldingTaxLots()) {
214 if (taxLot.getCost().signum() < 0) {
215 int index = newBusinessObject.getHoldingTaxLots().indexOf(taxLot);
216 putFieldError(getHoldingTaxLotErrorPath(index) + EndowPropertyConstants.HOLDING_TAX_LOT_COST,
217 EndowKeyConstants.HoldingTaxLotRebalanceConstants.ERROR_HLDG_TAX_LOT_REBALANCE_COST_INVALID);
218 isValid = false;
219 }
220 }
221 return isValid;
222 }
223
224 /**
225 * This method ensures that if the unit or cost field for a particular tax
226 * is zero that both are zero. If one field is zero, both must be zero.
227 *
228 * @param newDocument
229 * @return false if only one of the units or cost fields are zero.
230 */
231 protected boolean validateAllZero(HoldingTaxLotRebalance newBusinessObject)
232 {
233 boolean isValid = true;
234 for (HoldingTaxLot taxLot : newBusinessObject.getHoldingTaxLots()) {
235 boolean zeroUnits = (taxLot.getUnits().signum() == 0);
236 boolean zeroCost = (taxLot.getCost().signum() == 0);
237 if (zeroUnits || zeroCost) {
238 if (!(zeroUnits && zeroCost)) {
239 int index = newBusinessObject.getHoldingTaxLots().indexOf(taxLot);
240 putFieldError(getHoldingTaxLotErrorPath(index) + EndowPropertyConstants.HOLDING_TAX_LOT_UNITS,
241 EndowKeyConstants.HoldingTaxLotRebalanceConstants.ERROR_HLDG_TAX_LOT_REBALANCE_UNITS_COST_ZERO);
242 isValid = false;
243 }
244 }
245 }
246
247 return isValid;
248 }
249
250 }