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.service.impl; 017 018 import java.math.BigDecimal; 019 import java.util.ArrayList; 020 import java.util.List; 021 022 import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine; 023 import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionSecurity; 024 import org.kuali.kfs.module.endow.businessobject.EndowmentTransactionTaxLotLine; 025 import org.kuali.kfs.module.endow.businessobject.HoldingTaxLot; 026 import org.kuali.kfs.module.endow.businessobject.Security; 027 import org.kuali.kfs.module.endow.document.HoldingAdjustmentDocument; 028 import org.kuali.kfs.module.endow.document.service.HoldingTaxLotService; 029 import org.kuali.kfs.module.endow.document.service.SecurityService; 030 import org.kuali.kfs.module.endow.document.service.UpdateHoldingAdjustmentDocumentTaxLotsService; 031 import org.kuali.kfs.module.endow.util.KEMCalculationRoundingHelper; 032 import org.kuali.rice.kns.util.KualiDecimal; 033 import org.kuali.rice.kns.util.ObjectUtils; 034 035 public class UpdateHoldingAdjustmentDocumentTaxLotsServiceImpl implements UpdateHoldingAdjustmentDocumentTaxLotsService { 036 037 private HoldingTaxLotService taxLotService; 038 private SecurityService securityService; 039 040 /** 041 * @see org.kuali.kfs.module.endow.document.service.UpdateHoldingAdjustmentDocumentTaxLotsService#updateTransactionLineTaxLotsByUnitAdjustmentAmount(boolean, 042 * org.kuali.kfs.module.endow.document.EndowmentUnitShareAdjustmentDocument, 043 * org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine, boolean) 044 */ 045 public void updateTransactionLineTaxLotsByUnitAdjustmentAmount(boolean isUpdate, HoldingAdjustmentDocument holdingAdjustmentDocument, EndowmentTransactionLine transLine, boolean isSource) { 046 EndowmentTransactionSecurity endowmentTransactionSecurity = holdingAdjustmentDocument.getSourceTransactionSecurity(); 047 048 Security security = securityService.getByPrimaryKey(endowmentTransactionSecurity.getSecurityID()); 049 if (ObjectUtils.isNotNull(security)) { 050 List<HoldingTaxLot> holdingTaxLots = new ArrayList<HoldingTaxLot>(); 051 052 // Retrieve the tax lot records 053 if (isUpdate) { 054 holdingTaxLots = retrieveTaxLotLineForUpdate(transLine, endowmentTransactionSecurity); 055 } 056 else { 057 holdingTaxLots = retrieveAllTaxLotsWithPositiveCost(transLine, endowmentTransactionSecurity); 058 } 059 060 // create a record for each lot in holding lot table 061 createEndowmentHoldingLotRecord(holdingAdjustmentDocument, transLine, holdingTaxLots); 062 063 // lot holding cost calculations.. 064 if (transLine.getTaxLotLines() != null) { 065 calculateLotHoldingCostForUnitAdjustmentAmount(transLine, isSource); 066 } 067 068 // Update Transaction Amount field with absolute value of the lot holding cost 069 updateTransactionAmountWithLotHoldingCost(isSource, transLine); 070 } 071 } 072 073 /** 074 * @see org.kuali.kfs.module.endow.document.service.UpdateHoldingAdjustmentDocumentTaxLotsService#updateTransactionLineTaxLotsByTransactionAmount(boolean, 075 * org.kuali.kfs.module.endow.document.EndowmentUnitShareAdjustmentDocument, 076 * org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine, boolean) 077 */ 078 public void updateTransactionLineTaxLotsByTransactionAmount(boolean isUpdate, HoldingAdjustmentDocument holdingAdjustmentDocument, EndowmentTransactionLine transLine, boolean isSource) { 079 EndowmentTransactionSecurity endowmentTransactionSecurity = holdingAdjustmentDocument.getSourceTransactionSecurity(); 080 081 Security security = securityService.getByPrimaryKey(endowmentTransactionSecurity.getSecurityID()); 082 if (ObjectUtils.isNotNull(security)) { 083 List<HoldingTaxLot> holdingTaxLots = new ArrayList<HoldingTaxLot>(); 084 085 // Retrieve the tax lot records 086 if (isUpdate) { 087 holdingTaxLots = retrieveTaxLotLineForUpdate(transLine, endowmentTransactionSecurity); 088 } 089 else { 090 holdingTaxLots = retrieveAllTaxLotsWithPositiveCost(transLine, endowmentTransactionSecurity); 091 } 092 093 // create a record for each lot in holding lot table 094 createEndowmentHoldingLotRecord(holdingAdjustmentDocument, transLine, holdingTaxLots); 095 096 // lot holding cost calculations.. 097 if (transLine.getTaxLotLines() != null) { 098 BigDecimal taxLotsTotalUnits = BigDecimal.ZERO; 099 taxLotsTotalUnits = calculateTotalLotsTotalUnits(holdingTaxLots); 100 calculateLotHoldingCostForTransactionAmount(transLine, isSource, taxLotsTotalUnits); 101 } 102 } 103 } 104 105 /** 106 * Helper method to calculate the TotalLots total units 107 * 108 * @param holdingTaxLots 109 * @return taxLotsTotalUnits 110 */ 111 protected BigDecimal calculateTotalLotsTotalUnits(List<HoldingTaxLot> holdingTaxLots) { 112 113 BigDecimal taxLotsTotalUnits = BigDecimal.ZERO; 114 115 for (HoldingTaxLot holdingTaxLot : holdingTaxLots) { 116 taxLotsTotalUnits = taxLotsTotalUnits.add(holdingTaxLot.getUnits()); 117 } 118 119 return taxLotsTotalUnits; 120 } 121 122 /** 123 * Adjusts the oldest tax lot units if the transaction line units do not match the total of the tax lot units. 124 * 125 * @param totalTaxLotsUnits 126 * @param transLineUnits 127 * @param oldestTaxLot 128 * @param isSource 129 */ 130 private void adjustTaxLotsUnits(BigDecimal totalTaxLotsUnits, BigDecimal transLineUnits, EndowmentTransactionTaxLotLine oldestTaxLot, boolean isSource) { 131 if (totalTaxLotsUnits.compareTo(transLineUnits) != 0 && oldestTaxLot != null) { 132 BigDecimal diff = transLineUnits.subtract(totalTaxLotsUnits); 133 134 if (isSource) { 135 oldestTaxLot.setLotUnits(oldestTaxLot.getLotUnits().add(diff.negate())); 136 } 137 else { 138 oldestTaxLot.setLotUnits(oldestTaxLot.getLotUnits().add(diff)); 139 } 140 } 141 } 142 143 /** 144 * Helper method to retrieve all the tax lot records with units greater than zero from endowment holding tax lot table for a 145 * specific lot number 146 * 147 * @param transLine The endowment transaction line 148 * @param endowmentTransactionSecurity endowment transaction security 149 * @return holdingTaxLots 150 */ 151 protected List<HoldingTaxLot> retrieveTaxLotLineForUpdate(EndowmentTransactionLine transLine, EndowmentTransactionSecurity endowmentTransactionSecurity) { 152 List<HoldingTaxLot> holdingTaxLots = new ArrayList<HoldingTaxLot>(); 153 154 List<EndowmentTransactionTaxLotLine> existingTransactionLines = transLine.getTaxLotLines(); 155 for (EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine : existingTransactionLines) { 156 HoldingTaxLot holdingTaxLot = taxLotService.getByPrimaryKey(transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), endowmentTransactionTaxLotLine.getTransactionHoldingLotNumber(), transLine.getTransactionIPIndicatorCode()); 157 158 if (ObjectUtils.isNotNull(holdingTaxLot)) { 159 holdingTaxLots.add(holdingTaxLot); 160 } 161 } 162 163 transLine.getTaxLotLines().clear(); 164 return holdingTaxLots; 165 } 166 167 /** 168 * Helper method to retrieve all tax lots with positive cost holding tax lot lines 169 * 170 * @param transLine The endowment transaction line 171 * @param endowmentTransactionSecurity endowment transaction security 172 * @return holdingTaxLots 173 */ 174 protected List<HoldingTaxLot> retrieveAllTaxLotsWithPositiveCost(EndowmentTransactionLine transLine, EndowmentTransactionSecurity endowmentTransactionSecurity) { 175 List<HoldingTaxLot> holdingTaxLots = new ArrayList<HoldingTaxLot>(); 176 177 transLine.getTaxLotLines().clear(); 178 holdingTaxLots = taxLotService.getAllTaxLotsWithPositiveCost(transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), transLine.getTransactionIPIndicatorCode()); 179 180 return holdingTaxLots; 181 } 182 183 /** 184 * Helper method to update the transaction amount with the total holding cost 185 * 186 * @param transLine The endowment transaction line 187 * @param isSource whether this is source or target transaction line 188 */ 189 protected void updateTransactionAmountWithLotHoldingCost(boolean isSource, EndowmentTransactionLine transLine) { 190 BigDecimal totalHoldingCost = BigDecimal.ZERO; 191 192 for (EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine : transLine.getTaxLotLines()) { 193 totalHoldingCost = totalHoldingCost.add(endowmentTransactionTaxLotLine.getLotHoldingCost()); 194 } 195 196 // on the decrease side the transaction amount should be positive though the holding cost on the tax lots is negative 197 if (isSource && totalHoldingCost.signum() == -1) { 198 totalHoldingCost = totalHoldingCost.negate(); 199 } 200 201 transLine.setTransactionAmount(new KualiDecimal(totalHoldingCost)); 202 } 203 204 /** 205 * Helper method to calculate lot holding cost when unit adjustment amount given 206 * 207 * @param transLine 208 * @param isSource 209 */ 210 protected void calculateLotHoldingCostForUnitAdjustmentAmount(EndowmentTransactionLine transLine, boolean isSource) { 211 BigDecimal holdingCost = BigDecimal.ZERO; 212 213 for (EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine : transLine.getTaxLotLines()) { 214 endowmentTransactionTaxLotLine.setLotHoldingCost(KEMCalculationRoundingHelper.multiply(transLine.getUnitAdjustmentAmount(), endowmentTransactionTaxLotLine.getLotUnits(), 2)); 215 216 if (isSource) { 217 endowmentTransactionTaxLotLine.setLotHoldingCost(endowmentTransactionTaxLotLine.getLotHoldingCost().negate()); 218 } 219 } 220 } 221 222 /** 223 * Helper method to calculate lot holding cost when transaction amount given 224 * 225 * @param transLine 226 * @param isSource 227 */ 228 protected void calculateLotHoldingCostForTransactionAmount(EndowmentTransactionLine transLine, boolean isSource, BigDecimal taxLotsTotalUnits) { 229 BigDecimal totalUnits = BigDecimal.ZERO; 230 231 EndowmentTransactionTaxLotLine oldestTaxLot = null; 232 for (EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine : transLine.getTaxLotLines()) { 233 if (endowmentTransactionTaxLotLine.getTransactionHoldingLotNumber().compareTo(1) != 0) { 234 oldestTaxLot = endowmentTransactionTaxLotLine; 235 } 236 237 BigDecimal percentage = KEMCalculationRoundingHelper.divide(endowmentTransactionTaxLotLine.getLotUnits(), taxLotsTotalUnits, 5); 238 endowmentTransactionTaxLotLine.setLotHoldingCost(KEMCalculationRoundingHelper.multiply(percentage, transLine.getTransactionAmount().bigDecimalValue(), 2)); 239 240 totalUnits = totalUnits.add(endowmentTransactionTaxLotLine.getLotUnits()); 241 242 if (isSource) { 243 endowmentTransactionTaxLotLine.setLotHoldingCost(endowmentTransactionTaxLotLine.getLotHoldingCost().negate()); 244 } 245 } 246 247 BigDecimal transLineUnits = transLine.getTransactionUnits().bigDecimalValue(); 248 adjustTaxLotsUnits(totalUnits, transLineUnits, oldestTaxLot, isSource); 249 } 250 251 /** 252 * Helper method to create a record for each lot in endowment holding lot table 253 * 254 * @param holdingAdjustmentDocument 255 * @param transLine 256 * @param holdingTaxLots 257 */ 258 /** 259 * This method... 260 * 261 * @param holdingAdjustmentDocument 262 * @param transLine 263 * @param holdingTaxLots 264 */ 265 protected void createEndowmentHoldingLotRecord(HoldingAdjustmentDocument holdingAdjustmentDocument, EndowmentTransactionLine transLine, List<HoldingTaxLot> holdingTaxLots) { 266 267 if (holdingTaxLots != null) { 268 for (HoldingTaxLot holdingTaxLot : holdingTaxLots) { 269 EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine = new EndowmentTransactionTaxLotLine(); 270 endowmentTransactionTaxLotLine.setDocumentNumber(holdingAdjustmentDocument.getDocumentNumber()); 271 endowmentTransactionTaxLotLine.setDocumentLineNumber(transLine.getTransactionLineNumber()); 272 endowmentTransactionTaxLotLine.setTransactionHoldingLotNumber(holdingTaxLot.getLotNumber().intValue()); 273 274 endowmentTransactionTaxLotLine.setSecurityID(holdingAdjustmentDocument.getSourceTransactionSecurity().getSecurityID()); 275 endowmentTransactionTaxLotLine.setRegistrationCode(holdingAdjustmentDocument.getSourceTransactionSecurity().getRegistrationCode()); 276 endowmentTransactionTaxLotLine.setIpIndicator(transLine.getTransactionIPIndicatorCode()); 277 endowmentTransactionTaxLotLine.setKemid(transLine.getKemid()); 278 279 endowmentTransactionTaxLotLine.setLotAcquiredDate(holdingTaxLot.getAcquiredDate()); 280 // set it just for future computation 281 endowmentTransactionTaxLotLine.setLotUnits(holdingTaxLot.getUnits()); 282 transLine.getTaxLotLines().add(endowmentTransactionTaxLotLine); 283 284 // set new lot indicator 285 endowmentTransactionTaxLotLine.setNewLotIndicator(false); 286 } 287 } 288 } 289 290 /** 291 * Adjusts the oldest tax lot units if the transaction line units do not match the total of the tax lot units. 292 * 293 * @param totalTaxLotsUnits 294 * @param transLineUnits 295 * @param oldestTaxLot 296 * @param isSource 297 */ 298 private void adjustTaxLotsCost(BigDecimal totalTaxLotsCost, BigDecimal transLineCost, EndowmentTransactionTaxLotLine oldestTaxLot, boolean isSource) { 299 300 if (totalTaxLotsCost.compareTo(transLineCost) != 0 && oldestTaxLot != null) { 301 BigDecimal diff = transLineCost.subtract(totalTaxLotsCost); 302 303 if (isSource) { 304 oldestTaxLot.setLotHoldingCost(oldestTaxLot.getLotHoldingCost().add(diff.negate())); 305 } 306 else { 307 oldestTaxLot.setLotHoldingCost(oldestTaxLot.getLotHoldingCost().add(diff)); 308 } 309 } 310 } 311 312 /** 313 * Gets the taxLotService. 314 * 315 * @return taxLotService 316 */ 317 public HoldingTaxLotService getTaxLotService() { 318 return taxLotService; 319 } 320 321 /** 322 * Sets the taxLotService. 323 * 324 * @param taxLotService 325 */ 326 public void setTaxLotService(HoldingTaxLotService taxLotService) { 327 this.taxLotService = taxLotService; 328 } 329 330 /** 331 * Gets the securityService. 332 * 333 * @return securityService 334 */ 335 public SecurityService getSecurityService() { 336 return securityService; 337 } 338 339 /** 340 * Sets the securityService. 341 * 342 * @param securityService 343 */ 344 public void setSecurityService(SecurityService securityService) { 345 this.securityService = securityService; 346 } 347 }