/** * If the SECURITY_ID has a class code type A (Alternative Investments), the system must validate * that the total END_SEC_T: SEC_CVAL for the SECURITY_ID plus the END_TRAN_LN_T:TRAN_AMT does not * exceed the value in END_SEC_T: CMTMNT_AMT for the Security. If it does, the transaction should * not be allowed. * * @return true if valid, false otherwise */ protected boolean validateTaxLotsCostAndTransactionAmountLessOrEqualToSecurityCommitment( AssetIncreaseDocument assetIncreaseDocument) { boolean isValid = true; if (assetIncreaseDocument.getTargetTransactionSecurity() != null && assetIncreaseDocument.getTargetTransactionSecurity().getSecurity() != null && assetIncreaseDocument.getTargetTransactionSecurity().getSecurity().getClassCode() != null && EndowConstants.ClassCodeTypes.ALTERNATIVE_INVESTMENT.equalsIgnoreCase( assetIncreaseDocument .getTargetTransactionSecurity() .getSecurity() .getClassCode() .getClassCodeType())) { BigDecimal totalTransactionLinesAmt = BigDecimal.ZERO; BigDecimal securityCommitmentAmt = BigDecimal.ZERO; BigDecimal securityCarryValue = BigDecimal.ZERO; if (assetIncreaseDocument.getTargetTransactionLines() != null) { for (EndowmentTransactionLine transactionLine : assetIncreaseDocument.getTargetTransactionLines()) { // add to total amount totalTransactionLinesAmt = totalTransactionLinesAmt.add( transactionLine.getTransactionAmount().bigDecimalValue()); } } if (assetIncreaseDocument.getTargetTransactionSecurity() != null && assetIncreaseDocument.getTargetTransactionSecurity().getSecurity() != null) { if (assetIncreaseDocument.getTargetTransactionSecurity().getSecurity().getCommitmentAmount() != null) { securityCommitmentAmt = assetIncreaseDocument .getTargetTransactionSecurity() .getSecurity() .getCommitmentAmount(); } if (assetIncreaseDocument.getTargetTransactionSecurity().getSecurity().getCarryValue() != null) { securityCarryValue = assetIncreaseDocument.getTargetTransactionSecurity().getSecurity().getCarryValue(); } } isValid = (securityCarryValue.add(totalTransactionLinesAmt)).compareTo(securityCommitmentAmt) <= 0; if (!isValid) { putFieldError( getErrorPrefix(assetIncreaseDocument.getTargetTransactionLine(0), 0) + EndowPropertyConstants.TRANSACTION_LINE_TRANSACTION_AMOUNT, EndowKeyConstants.EndowmentTransactionDocumentConstants .ERROR_TRANSACTION_SECURITY_COMMITMENT_AMT); } } return isValid; }
/** * Validates that validateKemId returns false when the kemid on the transaction line does not * exist in the db. */ public void testValidateKemId_False() { EndowmentTransactionLine endowmentTransactionLine = EndowmentTransactionLineFixture.ENDOWMENT_TRANSACTIONAL_LINE_INCOME .createEndowmentTransactionLine(false); endowmentTransactionLine.setKemid(EndowTestConstants.INVALID_KEMID); assertFalse( rule.validateKemId( endowmentTransactionLine, rule.getErrorPrefix(endowmentTransactionLine, -1))); }
/** * Validates that canKEMIDHaveAPrincipalTransaction returns false when the transaction line IP * indicator is P and the principal restriction code is NA. */ public void testKemidPrincRestrNotNAWhenTransLinePrincipal_False() { EndowmentTransactionLine endowmentTransactionLine = EndowmentTransactionLineFixture.ENDOWMENT_TRANSACTIONAL_LINE_PRINCIPAL .createEndowmentTransactionLine(false); KEMID kemid = KemIdFixture.NA_PRINC_RESTR_KEMID_RECORD.createKemidRecord(); endowmentTransactionLine.setKemid(kemid.getKemid()); endowmentTransactionLine.setKemidObj(kemid); assertFalse( rule.canKEMIDHaveAPrincipalTransaction( endowmentTransactionLine, rule.getErrorPrefix(endowmentTransactionLine, -1))); }
/** * Sets the transaction line amount to be the total amount of all tax lot lines times negative 1. * This is applied in case the transaction sub type is non-cash. * * @param transactionLine */ private void setTransactionLineTotal(EndowmentTransactionLine transactionLine) { List<EndowmentTransactionTaxLotLine> taxLots = transactionLine.getTaxLotLines(); BigDecimal totalAmount = BigDecimal.ZERO; if (taxLots != null && taxLots.size() > 0) { for (EndowmentTransactionTaxLotLine taxLot : taxLots) { totalAmount = totalAmount.add(taxLot.getLotHoldingCost()); } } totalAmount = totalAmount.negate(); transactionLine.setTransactionAmount(new KualiDecimal(totalAmount)); }
/** * Validates that validateEndowmentTransactionTypeCode returns false when the etran code type is * not income or expense. */ public void testEtranCodeIncomeOrExpense_False() { EndowmentTransactionLine endowmentTargetTransactionLine = EndowmentTransactionLineFixture.ENDOWMENT_TRANSACTIONAL_LINE_POSITIVE_AMT .createEndowmentTransactionLine(false); EndowmentTransactionCode endowmentTransactionCode = EndowmentTransactionCodeFixture.ASSET_TRANSACTION_CODE.createEndowmentTransactionCode(); endowmentTargetTransactionLine.setEtranCode(endowmentTransactionCode.getCode()); endowmentTargetTransactionLine.setEtranCodeObj(endowmentTransactionCode); assertFalse( rule.validateEndowmentTransactionTypeCode( document, endowmentTargetTransactionLine, rule.getErrorPrefix(endowmentTargetTransactionLine, -1))); }
/** * Adds a tax lot line to a transaction line but first it negates the units and cost. * * @param transactionLine * @param taxLotLine */ private void addTaxLotLine( EndowmentTransactionLine transactionLine, EndowmentTransactionTaxLotLine taxLotLine) { // negate units and cost taxLotLine.setLotUnits(taxLotLine.getLotUnits().negate()); taxLotLine.setLotHoldingCost(taxLotLine.getLotHoldingCost().negate()); // add the tax lot line to the transaction line transactionLine.getTaxLotLines().add(taxLotLine); }
/** * Validates that validateChartMatch returns false when etran code gl chart does not match the * chart for KEMID general ledger account. */ public void testKemidEtranCodeMatch_False() { EndowmentTransactionLine endowmentTargetTransactionLine = EndowmentTransactionLineFixture.ENDOWMENT_TRANSACTIONAL_LINE_INCOME .createEndowmentTransactionLine(false); EndowmentTransactionCode endowmentTransactionCode = EndowmentTransactionCodeFixture.INCOME_TRANSACTION_CODE.createEndowmentTransactionCode(); KEMID kemid = KemIdFixture.OPEN_KEMID_RECORD.createKemidRecord(); GLLink glLink = GLLinkFixture.GL_LINK_UA_CHART.createGLLink(); KemidGeneralLedgerAccount generalLedgerAccount = KemidGeneralLedgerAccountFixture.KEMID_GL_ACCOUNT.createKemidGeneralLedgerAccount(); kemid.getKemidGeneralLedgerAccounts().add(generalLedgerAccount); endowmentTransactionCode.getGlLinks().add(glLink); endowmentTargetTransactionLine.setKemid(kemid.getKemid()); endowmentTargetTransactionLine.setKemidObj(kemid); endowmentTargetTransactionLine.setEtranCode(endowmentTransactionCode.getCode()); endowmentTargetTransactionLine.setEtranCodeObj(endowmentTransactionCode); assertFalse( rule.validateChartMatch( endowmentTargetTransactionLine, rule.getErrorPrefix(endowmentTargetTransactionLine, -1))); }
/** * Adjusts the number of units and the amounts if the total is different from the transaction line * units. The correction will be done on the oldest tax lot. This method is specific to the Asset * Decrease document and is used in updateTaxLotsForSubTypeNonCashAndTransAmtNonZero(). * * @param transLine * @param keepIntegers */ private void adjustUnitsAndAmountsForNonCashAndTransactionAmtNotZero( EndowmentTransactionLine transLine) { // Adjust the number of units if the total is different from the transaction line units BigDecimal totalComputedTaxLotUnits = BigDecimal.ZERO; BigDecimal totalComputedCost = BigDecimal.ZERO; EndowmentTransactionTaxLotLine oldestTaxLotLine = null; if (transLine.getTaxLotLines() != null && transLine.getTaxLotLines().size() > 0) { for (EndowmentTransactionTaxLotLine taxLotLine : transLine.getTaxLotLines()) { BigDecimal lotUnits = taxLotLine.getLotUnits().negate(); // calculate the total number of units to be decreased totalComputedTaxLotUnits = totalComputedTaxLotUnits.add(lotUnits); totalComputedCost = totalComputedCost.add(taxLotLine.getLotHoldingCost().negate()); if (taxLotLine.getLotShortTermGainLoss() != null) { totalComputedCost = totalComputedCost.add(taxLotLine.getLotShortTermGainLoss()); } if (taxLotLine.getLotLongTermGainLoss() != null) { totalComputedCost = totalComputedCost.add(taxLotLine.getLotLongTermGainLoss()); } // keep the tax lot with the oldest acquired date so that we can adjust the units for that // one in case the // number of units needs and adjustment if (oldestTaxLotLine != null) { if (oldestTaxLotLine.getLotAcquiredDate().after(taxLotLine.getLotAcquiredDate())) { oldestTaxLotLine = taxLotLine; } } else { oldestTaxLotLine = taxLotLine; } } } // compare with the negated number of units on the transaction line because the units on the tax // lots have been negated if (totalComputedTaxLotUnits.compareTo( transLine.getTransactionUnits().bigDecimalValue().negate()) != 0) { BigDecimal difUnits = transLine.getTransactionUnits().bigDecimalValue().subtract(totalComputedTaxLotUnits); oldestTaxLotLine.setLotUnits(oldestTaxLotLine.getLotUnits().add(difUnits.negate())); BigDecimal difAmount = transLine.getTransactionAmount().bigDecimalValue().subtract(totalComputedCost); oldestTaxLotLine.setLotHoldingCost( oldestTaxLotLine.getLotHoldingCost().add(difAmount.negate())); } }
/** * @see * org.kuali.kfs.module.endow.document.service.UpdateTaxLotsBasedOnAccMethodAndTransSubtypeService#updateTransactionLineTaxLots(boolean, * org.kuali.kfs.module.endow.document.EndowmentTaxLotLinesDocument, * org.kuali.kfs.module.endow.businessobject.EndowmentTransactionLine) */ public void updateTransactionLineTaxLots( boolean isUpdate, EndowmentTaxLotLinesDocument endowmentTaxLotLinesDocument, EndowmentTransactionLine transLine) { EndowmentTransactionSecurity endowmentTransactionSecurity = endowmentTaxLotLinesDocument.getSourceTransactionSecurity(); String accountingMethod = parameterService.getParameterValueAsString( KfsParameterConstants.ENDOWMENT_ALL.class, EndowParameterKeyConstants.TAX_LOTS_ACCOUNTING_METHOD); Security security = securityService.getByPrimaryKey(endowmentTransactionSecurity.getSecurityID()); if (ObjectUtils.isNotNull(security)) { // In the Asset Decrease document, if transaction sub type is non-cash and IF the user inserts // an amount in the // transaction amount, that amount along with the units sill be used to generate the // transaction tax lot lines. The // amount will not be calculated by the addition of the transaction line. In the event the // user has entered an amount in // the transaction amount field, that amount along with the units will be spread // proportionately among the tax lots. if (endowmentTaxLotLinesDocument instanceof AssetDecreaseDocument && EndowConstants.TransactionSubTypeCode.NON_CASH.equalsIgnoreCase( endowmentTaxLotLinesDocument.getTransactionSubTypeCode()) && transLine.getTransactionAmount() != null && transLine.getTransactionAmount().bigDecimalValue().compareTo(BigDecimal.ZERO) != 0) { updateTaxLotsForSubTypeNonCashAndTransAmtNonZero( isUpdate, endowmentTransactionSecurity, transLine); } else { if (EndowConstants.TaxLotsAccountingMethodOptions.AVERAGE_BALANCE.equalsIgnoreCase( accountingMethod) || (EndowConstants.TaxLotsAccountingMethodOptions.FIFO.equalsIgnoreCase( accountingMethod) && !security.getClassCode().isTaxLotIndicator())) { if (EndowConstants.TransactionSubTypeCode.CASH.equalsIgnoreCase( endowmentTaxLotLinesDocument.getTransactionSubTypeCode())) { updateTaxLotsForAccountingMethodAverageBalance( true, isUpdate, endowmentTransactionSecurity, transLine); } if (EndowConstants.TransactionSubTypeCode.NON_CASH.equalsIgnoreCase( endowmentTaxLotLinesDocument.getTransactionSubTypeCode())) { updateTaxLotsForAccountingMethodAverageBalance( false, isUpdate, endowmentTransactionSecurity, transLine); setTransactionLineTotal(transLine); } } if ((EndowConstants.TaxLotsAccountingMethodOptions.FIFO.equalsIgnoreCase(accountingMethod) || EndowConstants.TaxLotsAccountingMethodOptions.LIFO.equalsIgnoreCase( accountingMethod)) && security.getClassCode().isTaxLotIndicator()) { boolean isFIFO = EndowConstants.TaxLotsAccountingMethodOptions.FIFO.equalsIgnoreCase(accountingMethod); if (EndowConstants.TransactionSubTypeCode.CASH.equalsIgnoreCase( endowmentTaxLotLinesDocument.getTransactionSubTypeCode())) { updateTaxLotsForAccountingMethodFIFOorLIFO( true, isUpdate, isFIFO, endowmentTransactionSecurity, transLine); } if (EndowConstants.TransactionSubTypeCode.NON_CASH.equalsIgnoreCase( endowmentTaxLotLinesDocument.getTransactionSubTypeCode())) { updateTaxLotsForAccountingMethodFIFOorLIFO( false, isUpdate, isFIFO, endowmentTransactionSecurity, transLine); setTransactionLineTotal(transLine); } } } } }
/** * Updates the tax lots for the transaction line in the case the transaction sub type is non-cash * and the user entered the transaction amount. This method is specific to the Asset Decrease * document. * * @param isSubTypeCash * @param endowmentTransactionSecurity * @param transLine */ private void updateTaxLotsForSubTypeNonCashAndTransAmtNonZero( boolean isUpdate, EndowmentTransactionSecurity endowmentTransactionSecurity, EndowmentTransactionLine transLine) { BigDecimal transactionUnits = transLine.getTransactionUnits().bigDecimalValue(); BigDecimal totalTaxLotsUnits = BigDecimal.ZERO; BigDecimal transactionAmount = transLine.getTransactionAmount().bigDecimalValue(); BigDecimal perUnitValue = BigDecimal.ZERO; List<HoldingTaxLot> holdingTaxLots = new ArrayList<HoldingTaxLot>(); Map<Integer, HoldingTaxLot> lotsMap = new HashMap<Integer, HoldingTaxLot>(); if (!isUpdate) { transLine.getTaxLotLines().clear(); holdingTaxLots = taxLotService.getAllTaxLots( transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), transLine.getTransactionIPIndicatorCode()); } else { List<EndowmentTransactionTaxLotLine> existingTransactionLines = transLine.getTaxLotLines(); for (EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine : existingTransactionLines) { HoldingTaxLot holdingTaxLot = taxLotService.getByPrimaryKey( transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), endowmentTransactionTaxLotLine.getTransactionHoldingLotNumber(), transLine.getTransactionIPIndicatorCode()); if (ObjectUtils.isNotNull(holdingTaxLot)) { holdingTaxLots.add(holdingTaxLot); } } transLine.getTaxLotLines().clear(); } Map<KualiInteger, EndowmentTransactionTaxLotLine> decreaseHoldingTaxLots = new HashMap<KualiInteger, EndowmentTransactionTaxLotLine>(); if (holdingTaxLots != null && holdingTaxLots.size() > 0) { boolean keepIntegers = true; // compute the total number of units for tax lots for (HoldingTaxLot holdingTaxLot : holdingTaxLots) { totalTaxLotsUnits = totalTaxLotsUnits.add(holdingTaxLot.getUnits()); // 3. Calculate the number of units to be transacted in each lot // check if percentage and tax lot units are integers BigDecimal lotUnits = BigDecimal.ZERO; try { int lotUnitsInt = holdingTaxLot.getUnits().intValueExact(); } catch (ArithmeticException ex) { keepIntegers = false; } } for (HoldingTaxLot holdingTaxLot : holdingTaxLots) { EndowmentTransactionTaxLotLine taxLotLine = new EndowmentTransactionTaxLotLine(); taxLotLine.setDocumentLineNumber(transLine.getTransactionLineNumber()); BigDecimal lotUnits = BigDecimal.ZERO; // 2. Calculate percentage each lot holds out of the total units BigDecimal percentage = KEMCalculationRoundingHelper.divide(holdingTaxLot.getUnits(), totalTaxLotsUnits, 5); lotUnits = KEMCalculationRoundingHelper.multiply( percentage, transLine.getTransactionUnits().bigDecimalValue(), 5); // IF all original units per lot are integers (no decimal values), the result is rounded to // the nearest // integer and stored with the five decimals as zero. If the original units are not all // integers, then the // value is rounded to five decimals and stored as the five decimal values. if (keepIntegers) { lotUnits = lotUnits.setScale(0, BigDecimal.ROUND_HALF_UP); lotUnits = lotUnits.setScale(5); } taxLotLine.setLotUnits(lotUnits); // 6. Calculate holding cost BigDecimal holdingCost = KEMCalculationRoundingHelper.multiply(percentage, transactionAmount, 2); taxLotLine.setLotHoldingCost(holdingCost); // set tax lot line lot number and acquired date taxLotLine.setTransactionHoldingLotNumber(holdingTaxLot.getLotNumber().intValue()); taxLotLine.setKemid(transLine.getKemid()); taxLotLine.setSecurityID(holdingTaxLot.getSecurityId()); taxLotLine.setRegistrationCode(holdingTaxLot.getRegistrationCode()); taxLotLine.setIpIndicator(holdingTaxLot.getIncomePrincipalIndicator()); taxLotLine.setLotAcquiredDate(holdingTaxLot.getAcquiredDate()); // set the new lot indicator taxLotLine.setNewLotIndicator(false); // add the new tax lot line to the transaction line tax lots addTaxLotLine(transLine, taxLotLine); lotsMap.put(taxLotLine.getTransactionHoldingLotNumber(), holdingTaxLot); } adjustUnitsAndAmountsForNonCashAndTransactionAmtNotZero(transLine); } }
/** * Updates the tax lots for the transaction line in the case the accounting method is FIFO or * LIFO. * * @param isSubTypeCash * @param isFIFO * @param endowmentTransactionSecurity * @param transLine */ private void updateTaxLotsForAccountingMethodFIFOorLIFO( boolean isSubTypeCash, boolean isUpdate, boolean isFIFO, EndowmentTransactionSecurity endowmentTransactionSecurity, EndowmentTransactionLine transLine) { BigDecimal transactionUnits = transLine.getTransactionUnits().bigDecimalValue(); BigDecimal transactionAmount = BigDecimal.ZERO; BigDecimal perUnitVal = BigDecimal.ZERO; List<HoldingTaxLot> holdingTaxLots = new ArrayList<HoldingTaxLot>(); if (!isUpdate) { transLine.getTaxLotLines().clear(); holdingTaxLots = taxLotService.getAllTaxLotsOrderByAcquiredDate( transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), transLine.getTransactionIPIndicatorCode(), isFIFO); } else { List<EndowmentTransactionTaxLotLine> existingTransactionLines = transLine.getTaxLotLines(); for (EndowmentTransactionTaxLotLine endowmentTransactionTaxLotLine : existingTransactionLines) { HoldingTaxLot holdingTaxLot = taxLotService.getByPrimaryKey( transLine.getKemid(), endowmentTransactionSecurity.getSecurityID(), endowmentTransactionSecurity.getRegistrationCode(), endowmentTransactionTaxLotLine.getTransactionHoldingLotNumber(), transLine.getTransactionIPIndicatorCode()); if (ObjectUtils.isNotNull(holdingTaxLot)) { holdingTaxLots.add(holdingTaxLot); } } transLine.getTaxLotLines().clear(); } Map<KualiInteger, EndowmentTransactionTaxLotLine> decreaseHoldingTaxLots = new HashMap<KualiInteger, EndowmentTransactionTaxLotLine>(); if (isSubTypeCash) { transactionAmount = transLine.getTransactionAmount().bigDecimalValue(); // 1. Compute per unit value perUnitVal = KEMCalculationRoundingHelper.divide(transactionAmount, transactionUnits, 5); } BigDecimal remainingUnits = transactionUnits; if (holdingTaxLots != null && holdingTaxLots.size() > 0) { // compute the total number of units for tax lots for (HoldingTaxLot holdingTaxLot : holdingTaxLots) { EndowmentTransactionTaxLotLine taxLotLine = new EndowmentTransactionTaxLotLine(); taxLotLine.setDocumentLineNumber(transLine.getTransactionLineNumber()); // 2. Set the lot units // if transaction units is greater than the holding tax lot units then take all the holding // tax lot units if (remainingUnits.compareTo(holdingTaxLot.getUnits()) == 1) { taxLotLine.setLotUnits(holdingTaxLot.getUnits()); remainingUnits = remainingUnits.subtract(holdingTaxLot.getUnits()); } // if transaction units is less than the holding tax lot units then take just the remaining // number of units else { taxLotLine.setLotUnits(remainingUnits); remainingUnits = BigDecimal.ZERO; } // 4. Calculate the original unit value: cost/units BigDecimal originalUnitVal = KEMCalculationRoundingHelper.divide( holdingTaxLot.getCost(), holdingTaxLot.getUnits(), 5); // 5. Calculate original cost BigDecimal originalCost = KEMCalculationRoundingHelper.multiply(originalUnitVal, taxLotLine.getLotUnits(), 2); // set the tax lot holding cost taxLotLine.setLotHoldingCost(originalCost); // if sub type cash then the if (isSubTypeCash) { // 3. Calculate the value received for the units sold and round to 2 decimals BigDecimal receivedValue = KEMCalculationRoundingHelper.multiply(perUnitVal, taxLotLine.getLotUnits(), 2); // 6. Calculate the gain or loss calculateGainLoss(holdingTaxLot, taxLotLine, receivedValue, originalCost); } // set the lot number taxLotLine.setTransactionHoldingLotNumber(holdingTaxLot.getLotNumber().intValue()); taxLotLine.setKemid(transLine.getKemid()); taxLotLine.setSecurityID(holdingTaxLot.getSecurityId()); taxLotLine.setRegistrationCode(holdingTaxLot.getRegistrationCode()); taxLotLine.setIpIndicator(holdingTaxLot.getIncomePrincipalIndicator()); // set the lot acquired date taxLotLine.setLotAcquiredDate(holdingTaxLot.getAcquiredDate()); addTaxLotLine(transLine, taxLotLine); if (remainingUnits.compareTo(BigDecimal.ZERO) == 0) { break; } } } }
/** * Adjusts the number of units if the total is different from the transaction line units. * * @param lotsMap * @param transLine * @param keepIntegers * @param isSubTypeCash * @param perUnitValue */ private void adjustUnitsNumberAndAmountsForAverageBalance( Map<Integer, HoldingTaxLot> lotsMap, EndowmentTransactionLine transLine, boolean keepIntegers, boolean isSubTypeCash, BigDecimal perUnitValue) { // Adjust the number of units if the total is different from the transaction line units BigDecimal totalComputedTaxLotUnits = BigDecimal.ZERO; BigDecimal totalComputedCost = BigDecimal.ZERO; EndowmentTransactionTaxLotLine oldestTaxLotLine = null; if (transLine.getTaxLotLines() != null && transLine.getTaxLotLines().size() > 0) { for (EndowmentTransactionTaxLotLine taxLotLine : transLine.getTaxLotLines()) { BigDecimal lotUnits = taxLotLine.getLotUnits().negate(); // calculate the total number of units to be decreased totalComputedTaxLotUnits = totalComputedTaxLotUnits.add(lotUnits); totalComputedCost = totalComputedCost.add(taxLotLine.getLotHoldingCost().negate()); if (taxLotLine.getLotShortTermGainLoss() != null) { totalComputedCost = totalComputedCost.add(taxLotLine.getLotShortTermGainLoss()); } if (taxLotLine.getLotLongTermGainLoss() != null) { totalComputedCost = totalComputedCost.add(taxLotLine.getLotLongTermGainLoss()); } // keep the tax lot with the oldest acquired date so that we can adjust the units for that // one in case the // number of units needs and adjustment if (oldestTaxLotLine != null) { if (oldestTaxLotLine.getLotAcquiredDate().after(taxLotLine.getLotAcquiredDate())) { oldestTaxLotLine = taxLotLine; } } else { oldestTaxLotLine = taxLotLine; } } } // compare with the negated number of units on the transaction line because the units on the tax // lots have been negated if (totalComputedTaxLotUnits.compareTo( transLine.getTransactionUnits().bigDecimalValue().negate()) != 0) { BigDecimal difUnits = transLine.getTransactionUnits().bigDecimalValue().subtract(totalComputedTaxLotUnits); oldestTaxLotLine.setLotUnits(oldestTaxLotLine.getLotUnits().add(difUnits.negate())); oldestTaxLotLine.setLotUnits(oldestTaxLotLine.getLotUnits().negate()); if (isSubTypeCash) { // update totalComputedCost totalComputedCost = totalComputedCost.subtract(oldestTaxLotLine.getLotHoldingCost().negate()); if (oldestTaxLotLine.getLotShortTermGainLoss() != null) { totalComputedCost = totalComputedCost.subtract(oldestTaxLotLine.getLotShortTermGainLoss()); } if (oldestTaxLotLine.getLotLongTermGainLoss() != null) { totalComputedCost = totalComputedCost.subtract(oldestTaxLotLine.getLotLongTermGainLoss()); } } HoldingTaxLot holdingTaxLot = lotsMap.get(oldestTaxLotLine.getTransactionHoldingLotNumber()); BigDecimal originalUnitValue = KEMCalculationRoundingHelper.divide(holdingTaxLot.getCost(), holdingTaxLot.getUnits(), 5); BigDecimal originalCost = KEMCalculationRoundingHelper.multiply( oldestTaxLotLine.getLotUnits(), originalUnitValue, 2); oldestTaxLotLine.setLotHoldingCost(originalCost); if (isSubTypeCash) { // 4. Calculate the value received for units sold in each tax lot BigDecimal valueReceived = KEMCalculationRoundingHelper.multiply(oldestTaxLotLine.getLotUnits(), perUnitValue, 2); // 7. Calculate Gain or loss calculateGainLoss(holdingTaxLot, oldestTaxLotLine, valueReceived, originalCost); // update totalComputedCost totalComputedCost = totalComputedCost.add(oldestTaxLotLine.getLotHoldingCost()); if (oldestTaxLotLine.getLotShortTermGainLoss() != null) { totalComputedCost = totalComputedCost.add(oldestTaxLotLine.getLotShortTermGainLoss()); } if (oldestTaxLotLine.getLotLongTermGainLoss() != null) { totalComputedCost = totalComputedCost.add(oldestTaxLotLine.getLotLongTermGainLoss()); } } oldestTaxLotLine.setLotHoldingCost(oldestTaxLotLine.getLotHoldingCost().negate()); oldestTaxLotLine.setLotUnits(oldestTaxLotLine.getLotUnits().negate()); } if (isSubTypeCash) { // compare total computed cost with the transaction line amount if (totalComputedCost.compareTo(transLine.getTransactionUnits().bigDecimalValue()) != 0) { BigDecimal difAmount = transLine.getTransactionAmount().bigDecimalValue().subtract(totalComputedCost); oldestTaxLotLine.setLotHoldingCost( oldestTaxLotLine.getLotHoldingCost().negate().add(difAmount)); oldestTaxLotLine.setLotHoldingCost(oldestTaxLotLine.getLotHoldingCost().negate()); } } }