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 }