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 }