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.List;
020    
021    import org.kuali.kfs.module.endow.businessobject.EndowmentSourceTransactionLine;
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.CorporateReorganizationDocument;
028    import org.kuali.kfs.module.endow.document.SecurityTransferDocument;
029    import org.kuali.kfs.module.endow.document.service.HoldingTaxLotService;
030    import org.kuali.kfs.module.endow.document.service.KEMService;
031    import org.kuali.kfs.module.endow.document.service.SecurityService;
032    import org.kuali.kfs.module.endow.document.service.UpdateSecurityTransferTargetTaxLotsService;
033    import org.kuali.kfs.module.endow.util.KEMCalculationRoundingHelper;
034    import org.kuali.rice.kns.util.KualiDecimal;
035    import org.kuali.rice.kns.util.ObjectUtils;
036    
037    public class UpdateSecurityTransferTargetTaxLotsServiceImpl implements UpdateSecurityTransferTargetTaxLotsService {
038    
039        private HoldingTaxLotService taxLotService;
040        private SecurityService securityService;
041        private KEMService kemService;
042    
043        public void updateTransactionLineTaxLots(CorporateReorganizationDocument document, EndowmentTransactionLine transLine) {
044            // calculate the transaction line amount
045    
046            //if there are no source transaction lines, do not process anything.
047            if (document.getSourceTransactionLines().size() > 0) {         
048                // get the source transaction line and compute the unit value by dividing the source amount by the source number of units
049                EndowmentSourceTransactionLine sourceTransactionLine = (EndowmentSourceTransactionLine) document.getSourceTransactionLines().get(0);
050                EndowmentTransactionSecurity endowmentTransactionSecurity = document.getSourceTransactionSecurity();
051    
052                if (ObjectUtils.isNotNull(sourceTransactionLine)) {
053                    BigDecimal sourceAmount = BigDecimal.ZERO;
054                    if (sourceTransactionLine.getTransactionAmount() != null) {
055                        sourceAmount = sourceTransactionLine.getTransactionAmount().bigDecimalValue();
056                    }
057                    BigDecimal sourceUnits = BigDecimal.ZERO;
058                    if (sourceTransactionLine.getTransactionUnits() != null) {
059                        sourceUnits = sourceTransactionLine.getTransactionUnits().bigDecimalValue();
060                    }
061        
062                    BigDecimal unitValue = BigDecimal.ZERO;
063                    if (sourceUnits != null && sourceUnits.compareTo(BigDecimal.ZERO) != 0) {
064                        unitValue = KEMCalculationRoundingHelper.divide(sourceAmount, sourceUnits, 5);
065                    }
066        
067                    BigDecimal targetAmount = KEMCalculationRoundingHelper.multiply(transLine.getTransactionUnits().bigDecimalValue(), unitValue, 2);
068                    transLine.setTransactionAmount(new KualiDecimal(sourceAmount));
069        
070                    EndowmentTransactionTaxLotLine taxLotLine = null;
071                    boolean newLine = false;
072        
073                    if (transLine.getTaxLotLines() != null && transLine.getTaxLotLines().size() > 0) {
074                        // there is only one tax lot line per each transaction line
075                        taxLotLine = transLine.getTaxLotLines().get(0);
076                    }
077                    else {
078                        // create and set a new tax lot line
079                        newLine = true;
080                        taxLotLine = new EndowmentTransactionTaxLotLine();
081                        taxLotLine.setDocumentNumber(document.getDocumentNumber());
082                        taxLotLine.setDocumentLineNumber(transLine.getTransactionLineNumber());
083                    }
084        
085                    Security security = endowmentTransactionSecurity.getSecurity();
086                    String securityId = endowmentTransactionSecurity.getSecurityID();
087                    String regCode = endowmentTransactionSecurity.getRegistrationCode();
088                    String ipIndicator = transLine.getTransactionIPIndicatorCode();
089                    String kemid = transLine.getKemid();
090        
091                    // Step 6.
092                    if (!security.getClassCode().isTaxLotIndicator()) {
093                        taxLotLine.setTransactionHoldingLotNumber(1);
094                        HoldingTaxLot taxLot = taxLotService.getByPrimaryKey(kemid, securityId, regCode, 1, ipIndicator);
095                        if (taxLot != null) {
096                            if (taxLot.getUnits().compareTo(BigDecimal.ZERO) == 0 && taxLot.getCost().compareTo(BigDecimal.ZERO) == 0) {
097                                taxLotLine.setLotAcquiredDate(kemService.getCurrentProcessDate());
098                            }
099                            else {
100                                taxLotLine.setLotAcquiredDate(taxLot.getAcquiredDate());
101                            }
102                        }
103                        else {
104                            taxLotLine.setLotAcquiredDate(kemService.getCurrentProcessDate());
105                        }
106                        // Step 7.
107                    }
108                    else {
109                        List<HoldingTaxLot> taxLots = taxLotService.getAllTaxLots(kemid, securityId, regCode, ipIndicator);
110                        int lotNumber = 1;
111                        for (HoldingTaxLot taxLot : taxLots) {
112                            if (lotNumber < taxLot.getLotNumber().intValue()) {
113                                lotNumber = taxLot.getLotNumber().intValue();
114                            }
115                        }
116                        taxLotLine.setTransactionHoldingLotNumber(Integer.valueOf(lotNumber));
117                        taxLotLine.setLotAcquiredDate(kemService.getCurrentProcessDate());
118                    }
119        
120                    taxLotLine.setLotUnits(transLine.getTransactionUnits().bigDecimalValue());
121                    taxLotLine.setLotHoldingCost(sourceAmount);
122                    taxLotLine.setKemid(kemid);
123                    taxLotLine.setSecurityID(securityId);
124                    taxLotLine.setRegistrationCode(regCode);
125                    taxLotLine.setIpIndicator(ipIndicator);
126        
127                    // set the tax lot acquired date
128                    setTaxLotAcquiredDate(taxLotLine, document, transLine);
129        
130                    // set the new tax lot indicator
131                    setNewLotIndicator(taxLotLine, document);
132        
133                    if (newLine) {
134                        transLine.getTaxLotLines().add(taxLotLine);
135                    }
136                }
137            }
138        }
139    
140        /**
141         * @see org.kuali.kfs.module.endow.document.service.UpdateSecurityTransferTargetTaxLotsService#updateTransactionLineTaxLots(org.kuali.kfs.module.endow.document.SecurityTransferDocument,
142         *      org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine)
143         */
144        public void updateTransactionLineTaxLots(SecurityTransferDocument document, EndowmentTransactionLine transLine) {
145            // calculate the transaction line amount
146    
147            //if there are no source transaction lines, do not process anything.
148            if (document.getSourceTransactionLines().size() > 0) {         
149                // get the source transaction line and compute the unit value by dividing the source amount by the source number of units
150                EndowmentSourceTransactionLine sourceTransactionLine = (EndowmentSourceTransactionLine) document.getSourceTransactionLines().get(0);
151                EndowmentTransactionSecurity endowmentTransactionSecurity = document.getSourceTransactionSecurity();
152        
153                if (ObjectUtils.isNotNull(sourceTransactionLine)) {
154                    BigDecimal sourceAmount = BigDecimal.ZERO;
155                    if (sourceTransactionLine.getTransactionAmount() != null) {
156                        sourceAmount = sourceTransactionLine.getTransactionAmount().bigDecimalValue();
157                    }
158                    BigDecimal sourceUnits = BigDecimal.ZERO;
159                    if (sourceTransactionLine.getTransactionUnits() != null) {
160                        sourceUnits = sourceTransactionLine.getTransactionUnits().bigDecimalValue();
161                    }
162        
163                    BigDecimal unitValue = BigDecimal.ZERO;
164                    if (sourceUnits != null && sourceUnits.compareTo(BigDecimal.ZERO) != 0) {
165                        unitValue = KEMCalculationRoundingHelper.divide(sourceAmount, sourceUnits, 5);
166                    }
167        
168                    BigDecimal targetAmount = KEMCalculationRoundingHelper.multiply(transLine.getTransactionUnits().bigDecimalValue(), unitValue, 2);
169                    // set transaction line target amount
170                    transLine.setTransactionAmount(new KualiDecimal(targetAmount));
171        
172                    EndowmentTransactionTaxLotLine taxLotLine = null;
173                    boolean newLine = false;
174        
175                    if (transLine.getTaxLotLines() != null && transLine.getTaxLotLines().size() > 0) {
176                        // there is only one tax lot line per each transaction line
177                        taxLotLine = transLine.getTaxLotLines().get(0);
178                    }
179                    else {
180                        // create and set a new tax lot line
181                        newLine = true;
182                        taxLotLine = new EndowmentTransactionTaxLotLine();
183                        taxLotLine.setDocumentNumber(document.getDocumentNumber());
184                        taxLotLine.setDocumentLineNumber(transLine.getTransactionLineNumber());
185                        taxLotLine.setTransactionHoldingLotNumber(1);
186                    }
187        
188                    taxLotLine.setKemid(transLine.getKemid());
189                    taxLotLine.setSecurityID(endowmentTransactionSecurity.getSecurityID());
190                    taxLotLine.setRegistrationCode(endowmentTransactionSecurity.getRegistrationCode());
191                    taxLotLine.setIpIndicator(transLine.getTransactionIPIndicatorCode());
192                    taxLotLine.setLotUnits(transLine.getTransactionUnits().bigDecimalValue());
193                    taxLotLine.setLotHoldingCost(targetAmount);
194        
195                    // set the tax lot acquired date
196                    setTaxLotAcquiredDate(taxLotLine, document, transLine);
197        
198                    // set the new tax lot indicator
199                    setNewLotIndicator(taxLotLine, document);
200        
201                    if (newLine) {
202                        transLine.getTaxLotLines().add(taxLotLine);
203                    }
204        
205                }
206            }
207    
208        }
209    
210        /**
211         * Sets the Acquired date for the given tax lot line. If the tax lot indicator for the security (END_TRAN_SEC_T:
212         * SEC_TAX_LOT_IND) is No then for the lot acquired date - LOT_AQ_DATE - Search the END_HLDG_TAX_LOT_T records by KEMID by
213         * SEC_ID by REGIS_CD by HLDG_IP_IND [where HLDG_IP_IND is equal to END_TRAN_LN_T: TRAN_IP_IND_CD] by HLDG_LOT_NBR where
214         * HLDG_LOT_NBR is equal to 1 and return the HLDG_ACQD_DT: - If a lot exists for the security in END_HLDG_TAX_LOT_T, but the
215         * HLDG_UNITS and HLDG_COST are zero, insert the current date (System or Process) in LOT_ACQD_DT. - IF no lot exists for the
216         * security, then insert the current date (System or Process) in LOT_ACQD_DT. If the tax lot indicator for the security
217         * (END_TRAN_SEC_T: SEC_TAX_LOT_IND) is Yes: - LOT_AQ_DATE - insert the current date (System or Process) in this field
218         * 
219         * @param taxLotLine the tax lot line for which to set the acquired date
220         * @param aiDocument the Asset Increase Document the tax lot line belongs to
221         * @param transLine the transaction line the tax lot is related to
222         */
223        private void setTaxLotAcquiredDate(EndowmentTransactionTaxLotLine taxLotLine, SecurityTransferDocument document, EndowmentTransactionLine transLine) {
224            EndowmentTransactionSecurity endowmentTransactionSecurity = document.getSourceTransactionSecurity();
225    
226            Security security = securityService.getByPrimaryKey(endowmentTransactionSecurity.getSecurityID());
227    
228            // if security tax lot indicator is 'No' and a tax lot exists for the kemid, security, registration code and income
229            // principal indicator - set the lot acquired date to be the tax lot holding acquired date if units and cost is not zero;
230            // otherwise set the date to be the current date
231            if (ObjectUtils.isNotNull(security) && !security.getClassCode().isTaxLotIndicator()) {
232                HoldingTaxLot holdingTaxLot = taxLotService.getByPrimaryKey(transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), 1, transLine.getTransactionIPIndicatorCode());
233                if (ObjectUtils.isNotNull(holdingTaxLot)) {
234                    if (holdingTaxLot.getUnits().equals(KualiDecimal.ZERO) && holdingTaxLot.getCost().equals(KualiDecimal.ZERO)) {
235                        taxLotLine.setLotAcquiredDate(kemService.getCurrentDate());
236                    }
237                    else {
238                        taxLotLine.setLotAcquiredDate(holdingTaxLot.getAcquiredDate());
239                    }
240                }
241                else {
242                    taxLotLine.setLotAcquiredDate(kemService.getCurrentDate());
243                }
244    
245            }
246            // if security tax lot indicator is 'Yes' set the lot acquired date to be the current date
247            else {
248                taxLotLine.setLotAcquiredDate(kemService.getCurrentDate());
249            }
250        }
251    
252        /**
253         * Sets the new lot indicator for the tax lot: -- if the security tax lot indicator is No then I think we should set the field
254         * to 'N'. When the batch process runs we might need to create a new entry on the holding tax lot table in case no entry is
255         * found for the given KEMID, security ID, registration code, holding ip indicator, and holding lot number = 1. In case there is
256         * an entry we will just update that one; -- if the security tax lot is Yes then the field should be set to 'Y'.We are always
257         * creating a new field with the lot number being the next sequential lot number.
258         * 
259         * @param taxLotLine
260         * @param document
261         */
262        private void setNewLotIndicator(EndowmentTransactionTaxLotLine taxLotLine, SecurityTransferDocument document) {
263            EndowmentTransactionSecurity endowmentTransactionSecurity = document.getSourceTransactionSecurity();
264            Security security = securityService.getByPrimaryKey(endowmentTransactionSecurity.getSecurityID());
265    
266            if (ObjectUtils.isNotNull(security)) {
267                // if the security tax lot indicator is No then I think we should set the field to 'N'. When the batch process runs we
268                // might need to create a new entry on the holding tax lot table in case no entry is found for the given KEMID, security
269                // ID, registration code, holding ip indicator, and holding lot number = 1. In case there is an entry we will just
270                // update that one
271                if (!security.getClassCode().isTaxLotIndicator()) {
272    
273                    taxLotLine.setNewLotIndicator(false);
274                }
275                // if the security tax lot is Yes then the field should be set to 'Y'.We are always creating a new field with the lot
276                // number being the next sequential lot number.
277                else {
278                    taxLotLine.setNewLotIndicator(true);
279                }
280            }
281        }
282    
283        private void setTaxLotAcquiredDate(EndowmentTransactionTaxLotLine taxLotLine, CorporateReorganizationDocument document, EndowmentTransactionLine transLine) {
284            EndowmentTransactionSecurity endowmentTransactionSecurity = document.getSourceTransactionSecurity();
285    
286            Security security = securityService.getByPrimaryKey(endowmentTransactionSecurity.getSecurityID());
287    
288            // if security tax lot indicator is 'No' and a tax lot exists for the kemid, security, registration code and income
289            // principal indicator - set the lot acquired date to be the tax lot holding acquired date if units and cost is not zero;
290            // otherwise set the date to be the current date
291            if (ObjectUtils.isNotNull(security) && !security.getClassCode().isTaxLotIndicator()) {
292                HoldingTaxLot holdingTaxLot = taxLotService.getByPrimaryKey(transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), 1, transLine.getTransactionIPIndicatorCode());
293                if (ObjectUtils.isNotNull(holdingTaxLot)) {
294                    if (holdingTaxLot.getUnits().equals(KualiDecimal.ZERO) && holdingTaxLot.getCost().equals(KualiDecimal.ZERO)) {
295                        taxLotLine.setLotAcquiredDate(kemService.getCurrentDate());
296                    }
297                    else {
298                        taxLotLine.setLotAcquiredDate(holdingTaxLot.getAcquiredDate());
299                    }
300                }
301                else {
302                    taxLotLine.setLotAcquiredDate(kemService.getCurrentDate());
303                }
304    
305            }
306            // if security tax lot indicator is 'Yes' set the lot acquired date to be the current date
307            else {
308                taxLotLine.setLotAcquiredDate(kemService.getCurrentDate());
309            }
310        }
311    
312        private void setNewLotIndicator(EndowmentTransactionTaxLotLine taxLotLine, CorporateReorganizationDocument document) {
313            EndowmentTransactionSecurity endowmentTransactionSecurity = document.getSourceTransactionSecurity();
314            Security security = securityService.getByPrimaryKey(endowmentTransactionSecurity.getSecurityID());
315    
316            if (ObjectUtils.isNotNull(security)) {
317                // if the security tax lot indicator is No then I think we should set the field to 'N'. When the batch process runs we
318                // might need to create a new entry on the holding tax lot table in case no entry is found for the given KEMID, security
319                // ID, registration code, holding ip indicator, and holding lot number = 1. In case there is an entry we will just
320                // update that one
321                if (!security.getClassCode().isTaxLotIndicator()) {
322    
323                    taxLotLine.setNewLotIndicator(false);
324                }
325                // if the security tax lot is Yes then the field should be set to 'Y'.We are always creating a new field with the lot
326                // number being the next sequential lot number.
327                else {
328                    taxLotLine.setNewLotIndicator(true);
329                }
330            }
331        }
332    
333        /**
334         * Gets the taxLotService.
335         * 
336         * @return taxLotService
337         */
338        protected HoldingTaxLotService getTaxLotService() {
339            return taxLotService;
340        }
341    
342        /**
343         * Sets the taxLotService.
344         * 
345         * @param taxLotService
346         */
347        public void setTaxLotService(HoldingTaxLotService taxLotService) {
348            this.taxLotService = taxLotService;
349        }
350    
351        /**
352         * Gets the securityService.
353         * 
354         * @return securityService
355         */
356        protected SecurityService getSecurityService() {
357            return securityService;
358        }
359    
360        /**
361         * Sets the securityService.
362         * 
363         * @param securityService
364         */
365        public void setSecurityService(SecurityService securityService) {
366            this.securityService = securityService;
367        }
368    
369        /**
370         * Gets the kemService.
371         * 
372         * @return kemService
373         */
374        protected KEMService getKemService() {
375            return kemService;
376        }
377    
378        /**
379         * Sets the kemService.
380         * 
381         * @param kemService
382         */
383        public void setKemService(KEMService kemService) {
384            this.kemService = kemService;
385        }
386    }