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    }