MoneyWiseReportCapitalGains.java

/*
 * MoneyWise: Finance Application
 * Copyright 2012-2026. Tony Washer
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.  You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package io.github.tonywasher.joceanus.moneywise.lethe.reports;

import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusPrice;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusRatio;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusUnits;
import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.metis.report.MetisReportBase;
import io.github.tonywasher.joceanus.metis.report.MetisReportHTMLBuilder;
import io.github.tonywasher.joceanus.metis.report.MetisReportHTMLBuilder.MetisHTMLTable;
import io.github.tonywasher.joceanus.metis.report.MetisReportManager;
import io.github.tonywasher.joceanus.metis.report.MetisReportReferenceManager.DelayedTable;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicResource;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction.MoneyWiseTransactionList;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysis;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisSecurityBucket;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityAttr;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisSecurityValues;
import io.github.tonywasher.joceanus.moneywise.lethe.views.MoneyWiseAnalysisFilter;
import io.github.tonywasher.joceanus.moneywise.tax.MoneyWiseCashType;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import java.util.Iterator;

/**
 * CapitalGains report builder.
 */
public class MoneyWiseReportCapitalGains
        extends MetisReportBase<MoneyWiseAnalysis, MoneyWiseAnalysisFilter<?, ?>> {
    /**
     * The Title text.
     */
    private static final String TEXT_TITLE = MoneyWiseReportResource.CAPITALGAINS_TITLE.getValue();

    /**
     * HTML builder.
     */
    private final MetisReportHTMLBuilder theBuilder;

    /**
     * The Formatter.
     */
    private final OceanusDataFormatter theFormatter;

    /**
     * The string builder.
     */
    private final StringBuilder theStringBuilder;

    /**
     * The source SecurityBucket.
     */
    private MoneyWiseAnalysisSecurityBucket theSecurity;

    /**
     * The transactions.
     */
    private MoneyWiseTransactionList theTransactions;

    /**
     * The EndDate.
     */
    private OceanusDate theEndDate;

    /**
     * The table.
     */
    private MetisHTMLTable theTable;

    /**
     * The attribute table.
     */
    private MetisHTMLTable theAttrTable;

    /**
     * Constructor.
     *
     * @param pManager the Report Manager
     */
    protected MoneyWiseReportCapitalGains(final MetisReportManager<MoneyWiseAnalysisFilter<?, ?>> pManager) {
        /* Access underlying utilities */
        theBuilder = pManager.getBuilder();
        theFormatter = theBuilder.getDataFormatter();
        theStringBuilder = new StringBuilder();
    }

    /**
     * Set the security bucket.
     *
     * @param pSecurity the security bucket
     */
    protected void setSecurity(final MoneyWiseAnalysisSecurityBucket pSecurity) {
        theSecurity = pSecurity;
    }

    @Override
    public Document createReport(final MoneyWiseAnalysis pAnalysis) {
        /* Access the securities and the date */
        theTransactions = pAnalysis.getEditSet().getDataList(MoneyWiseBasicDataType.TRANSACTION, MoneyWiseTransactionList.class);
        theEndDate = pAnalysis.getDateRange().getEnd();

        /* Start the report */
        final Element myBody = theBuilder.startReport();
        theBuilder.makeTitle(myBody, TEXT_TITLE, theFormatter.formatObject(theEndDate));
        theBuilder.makeSubTitle(myBody, theSecurity.getDecoratedName());

        /* Initialise the table */
        theTable = theBuilder.startTable(myBody);
        theBuilder.startHdrRow(theTable);
        theBuilder.makeTitleCell(theTable, MoneyWiseBasicResource.MONEYWISEDATA_FIELD_DATE.getValue());
        theBuilder.makeTitleCell(theTable, MoneyWiseBasicDataType.TRANSACTION.getItemName());

        /* Format the history */
        formatHistory();

        /* Return the document */
        return theBuilder.getDocument();
    }

    /**
     * format the cost history.
     */
    private void formatHistory() {
        /* Loop through the transactions */
        final Iterator<MoneyWiseTransaction> myIterator = theTransactions.iterator();
        while (myIterator.hasNext()) {
            final MoneyWiseTransaction myTrans = myIterator.next();

            /* Check for End of report */
            if (theEndDate != null
                    && theEndDate.compareTo(myTrans.getDate()) < 0) {
                break;
            }

            /* If the transaction relates to the security */
            final MoneyWiseAnalysisSecurityValues myValues = theSecurity.getValuesForTransaction(myTrans);
            if (myValues != null) {
                /* Format the transaction */
                formatTransaction(myTrans, myValues);

                /* If we have an attribute table */
                if (theAttrTable != null) {
                    /* Embed the table correctly and reset the indicator */
                    theBuilder.embedTable(theAttrTable);
                    theAttrTable = null;
                }
            }
        }
    }

    /**
     * Format the details.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatTransaction(final MoneyWiseTransaction pTrans,
                                   final MoneyWiseAnalysisSecurityValues pValues) {
        /* Switch on the class */
        switch (pTrans.getCategoryClass()) {
            case TRANSFER:
            case STOCKRIGHTSISSUE:
            case INHERITED:
                formatTransfer(pTrans, pValues);
                break;
            case SECURITYREPLACE:
            case STOCKTAKEOVER:
                formatStockTakeOver(pTrans, pValues);
                break;
            case STOCKDEMERGER:
                formatStockDeMerger(pTrans, pValues);
                break;
            case STOCKSPLIT:
            case UNITSADJUST:
                formatUnitsAdjust(pTrans, pValues);
                break;
            case DIVIDEND:
                formatDividend(pTrans, pValues);
                break;
            case PORTFOLIOXFER:
                formatPortfolioXfer(pTrans, pValues);
                break;
            default:
                break;
        }
    }

    /**
     * Format basic details of a transaction.
     *
     * @param pTrans the transaction
     */
    private void formatBasicTransaction(final MoneyWiseTransaction pTrans) {
        /* Create the transaction row */
        theBuilder.startRow(theTable);
        theBuilder.makeValueCell(theTable, pTrans.getDate());
        theBuilder.makeValueCell(theTable, pTrans);
    }

    /**
     * Check whether this is a debit transaction for the security.
     *
     * @param pTrans the transaction
     * @return true/false
     */
    private boolean isDebit(final MoneyWiseTransaction pTrans) {
        final MoneyWiseTransAsset myDebit = pTrans.getDirection().isTo()
                ? pTrans.getAccount()
                : pTrans.getPartner();
        return myDebit.equals(theSecurity.getSecurityHolding());
    }

    /**
     * Check whether this is a credit transaction for the security.
     *
     * @param pTrans the transaction
     * @return true/false
     */
    private boolean isCredit(final MoneyWiseTransaction pTrans) {
        final MoneyWiseTransAsset myCredit = pTrans.getDirection().isFrom()
                ? pTrans.getAccount()
                : pTrans.getPartner();
        return myCredit.equals(theSecurity.getSecurityHolding());
    }

    /**
     * Ensure the attribute table.
     */
    private void ensureAttrTable() {
        /* If we do not have a current attribute table */
        if (theAttrTable == null) {
            /* Create a new table */
            theAttrTable = theBuilder.createEmbeddedTable(theTable);
        }
    }

    /**
     * Format a value.
     *
     * @param pAttr  the attribute
     * @param pValue the value
     */
    private void formatValue(final MoneyWiseAnalysisSecurityAttr pAttr,
                             final Object pValue) {
        /* Ensure that we have an attribute table */
        ensureAttrTable();

        /* Format the attribute */
        theBuilder.startRow(theAttrTable);
        theBuilder.makeValueCell(theAttrTable, pAttr);
        theBuilder.makeStretchedValueCell(theAttrTable, pValue);
    }

    /**
     * Format a division.
     *
     * @param pAttr      the attribute
     * @param pValue     the value
     * @param pNumerator the numerator
     * @param pDivisor   the divisor
     */
    private void formatDivision(final MoneyWiseAnalysisSecurityAttr pAttr,
                                final Object pValue,
                                final OceanusDecimal pNumerator,
                                final OceanusDecimal pDivisor) {
        /* Ensure that we have an attribute table */
        ensureAttrTable();

        /* Format the attribute */
        theBuilder.startRow(theAttrTable);
        theBuilder.makeValueCell(theAttrTable, pAttr);
        theBuilder.makeValueCell(theAttrTable, formatDivision(pNumerator, pDivisor));
        theBuilder.makeValueCell(theAttrTable, pValue);
    }

    /**
     * Format a division.
     *
     * @param pNumerator the numerator
     * @param pDivisor   the divisor
     * @return the formatted division
     */
    private String formatDivision(final OceanusDecimal pNumerator,
                                  final OceanusDecimal pDivisor) {
        return formatCombination(pNumerator, pDivisor, '/');
    }

    /**
     * Format a valuation.
     *
     * @param pAttr        the attribute
     * @param pValue       the value
     * @param pUnits       the units
     * @param pPrice       the price
     * @param pXchangeRate the exchange rate
     */
    private void formatValuation(final MoneyWiseAnalysisSecurityAttr pAttr,
                                 final Object pValue,
                                 final OceanusUnits pUnits,
                                 final OceanusPrice pPrice,
                                 final OceanusRatio pXchangeRate) {
        /* Ensure that we have an attribute table */
        ensureAttrTable();

        /* Format the attribute */
        theBuilder.startRow(theAttrTable);
        theBuilder.makeValueCell(theAttrTable, pAttr);
        theBuilder.makeValueCell(theAttrTable, formatValuation(pUnits, pPrice, pXchangeRate));
        theBuilder.makeValueCell(theAttrTable, pValue);
    }

    /**
     * Format a valuation.
     *
     * @param pUnits       the units
     * @param pPrice       the price
     * @param pXchangeRate the exchange rate
     * @return the formatted valuation
     */
    private String formatValuation(final OceanusUnits pUnits,
                                   final OceanusPrice pPrice,
                                   final OceanusRatio pXchangeRate) {
        theStringBuilder.setLength(0);
        theStringBuilder.append(theFormatter.formatObject(pUnits));
        theStringBuilder.append('@');
        theStringBuilder.append(theFormatter.formatObject(pPrice));
        if (pXchangeRate != null) {
            theStringBuilder.append('/');
            theStringBuilder.append(theFormatter.formatObject(pXchangeRate));
        }
        return theStringBuilder.toString();
    }

    /**
     * Format a multiplication.
     *
     * @param pAttr   the attribute
     * @param pValue  the value
     * @param pFirst  the first item
     * @param pSecond the second item
     */
    private void formatMultiplication(final MoneyWiseAnalysisSecurityAttr pAttr,
                                      final Object pValue,
                                      final OceanusDecimal pFirst,
                                      final OceanusDecimal pSecond) {
        /* Ensure that we have an attribute table */
        ensureAttrTable();

        /* Format the attribute */
        theBuilder.startRow(theAttrTable);
        theBuilder.makeValueCell(theAttrTable, pAttr);
        theBuilder.makeValueCell(theAttrTable, formatMultiplication(pFirst, pSecond));
        theBuilder.makeValueCell(theAttrTable, pValue);
    }

    /**
     * Format a multiplication.
     *
     * @param pFirst  the first item
     * @param pSecond the second item
     * @return the formatted multiplication
     */
    private String formatMultiplication(final OceanusDecimal pFirst,
                                        final OceanusDecimal pSecond) {
        return formatCombination(pFirst, pSecond, '*');
    }

    /**
     * Format an addition.
     *
     * @param pAttr   the attribute
     * @param pValue  the value
     * @param pFirst  the first item
     * @param pSecond the second item
     */
    private void formatAddition(final MoneyWiseAnalysisSecurityAttr pAttr,
                                final Object pValue,
                                final OceanusDecimal pFirst,
                                final OceanusDecimal pSecond) {
        /* Ensure that we have an attribute table */
        ensureAttrTable();

        /* Format the attribute */
        theBuilder.startRow(theAttrTable);
        theBuilder.makeValueCell(theAttrTable, pAttr);
        theBuilder.makeValueCell(theAttrTable, formatAddition(pFirst, pSecond));
        theBuilder.makeValueCell(theAttrTable, pValue);
    }

    /**
     * Format an addition.
     *
     * @param pFirst  the first item
     * @param pSecond the second item
     * @return the formatted addition
     */
    private String formatAddition(final OceanusDecimal pFirst,
                                  final OceanusDecimal pSecond) {
        return formatCombination(pFirst, pSecond, '+');
    }

    /**
     * Format a subtraction.
     *
     * @param pAttr   the attribute
     * @param pValue  the value
     * @param pFirst  the first item
     * @param pSecond the second item
     */
    private void formatSubtraction(final MoneyWiseAnalysisSecurityAttr pAttr,
                                   final Object pValue,
                                   final OceanusDecimal pFirst,
                                   final OceanusDecimal pSecond) {
        /* Ensure that we have an attribute table */
        ensureAttrTable();

        /* Format the attribute */
        theBuilder.startRow(theAttrTable);
        theBuilder.makeValueCell(theAttrTable, pAttr);
        theBuilder.makeValueCell(theAttrTable, formatSubtraction(pFirst, pSecond));
        theBuilder.makeValueCell(theAttrTable, pValue);
    }

    /**
     * Format a subtraction.
     *
     * @param pFirst  the first item
     * @param pSecond the second item
     * @return the formatted subtraction
     */
    private String formatSubtraction(final OceanusDecimal pFirst,
                                     final OceanusDecimal pSecond) {
        return formatCombination(pFirst, pSecond, '-');
    }

    /**
     * Format a combination.
     *
     * @param pFirst  the first item
     * @param pSecond the second item
     * @param pSymbol the symbol
     * @return the formatted combination
     */
    private String formatCombination(final OceanusDecimal pFirst,
                                     final OceanusDecimal pSecond,
                                     final char pSymbol) {
        theStringBuilder.setLength(0);
        theStringBuilder.append(theFormatter.formatObject(pFirst));
        theStringBuilder.append(pSymbol);
        theStringBuilder.append(theFormatter.formatObject(pSecond));
        return theStringBuilder.toString();
    }

    /**
     * Format a Transfer.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatTransfer(final MoneyWiseTransaction pTrans,
                                final MoneyWiseAnalysisSecurityValues pValues) {
        /* Format the basic transaction */
        formatBasicTransaction(pTrans);

        /* Split workings for transfer in/out */
        if (isDebit(pTrans)) {
            formatTransferOut(pTrans, pValues);
        } else {
            formatTransferIn(pTrans, pValues);
        }
    }

    /**
     * Format a Dividend.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatDividend(final MoneyWiseTransaction pTrans,
                                final MoneyWiseAnalysisSecurityValues pValues) {
        /* If this is a dividend re-investment */
        if (isCredit(pTrans)) {
            /* Format the basic transaction */
            formatBasicTransaction(pTrans);

            /* Deal as investment */
            formatTransferIn(pTrans, pValues);
        }
    }

    /**
     * Format transfer money in.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatTransferIn(final MoneyWiseTransaction pTrans,
                                  final MoneyWiseAnalysisSecurityValues pValues) {

        /* Access interesting values */
        final OceanusUnits myUnits = pValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
        OceanusUnits myDeltaUnits = pTrans.getAccountDeltaUnits();
        if (myDeltaUnits == null) {
            myDeltaUnits = pTrans.getPartnerDeltaUnits();
        }
        final OceanusMoney myCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusMoney myAmount = theSecurity.getMoneyDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusPrice myPrice = pValues.getPriceValue(MoneyWiseAnalysisSecurityAttr.PRICE);
        final OceanusRatio myXchangeRate = pValues.getRatioValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE);

        /* Obtain the original units/cost */
        final MoneyWiseAnalysisSecurityValues myPreviousValues = theSecurity.getPreviousValuesForTransaction(pTrans);
        final OceanusUnits myOriginalUnits = myPreviousValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
        final OceanusMoney myOriginalCost = myPreviousValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);

        /* If this is an inheritance */
        if (pTrans.isCategoryClass(MoneyWiseTransCategoryClass.INHERITED)) {
            formatValuation(MoneyWiseAnalysisSecurityAttr.INVESTED, myAmount, myDeltaUnits, myPrice, myXchangeRate);
        } else {
            formatValue(MoneyWiseAnalysisSecurityAttr.INVESTED, myAmount);
        }

        /* Record the details */
        if (myDeltaUnits != null) {
            formatAddition(MoneyWiseAnalysisSecurityAttr.UNITS, myUnits, myOriginalUnits, myDeltaUnits);
        }
        formatAddition(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myCost, myOriginalCost, myAmount);
    }

    /**
     * Format transfer money out.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatTransferOut(final MoneyWiseTransaction pTrans,
                                   final MoneyWiseAnalysisSecurityValues pValues) {
        /* Access interesting values */
        final OceanusMoney myGain = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CAPITALGAIN);
        final OceanusMoney myAllowedCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST);
        final OceanusRatio myCostDilution = pValues.getRatioValue(MoneyWiseAnalysisSecurityAttr.COSTDILUTION);
        final OceanusMoney myTotalGains = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS);
        final OceanusMoney myCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusUnits myUnits = pValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
        final OceanusMoney myCash = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RETURNEDCASH);
        final OceanusMoney myConsideration = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CONSIDERATION);
        final MoneyWiseCashType myCashType = pValues.getEnumValue(MoneyWiseAnalysisSecurityAttr.CASHTYPE, MoneyWiseCashType.class);

        /* Obtain the original values */
        final MoneyWiseAnalysisSecurityValues myPreviousValues = theSecurity.getPreviousValuesForTransaction(pTrans);
        final OceanusMoney myOriginalCost = myPreviousValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusUnits myOriginalUnits = myPreviousValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);

        /* Obtain the delta in units/money */
        OceanusUnits myDeltaUnits = theSecurity.getUnitsDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.UNITS);
        final OceanusMoney myAmount = new OceanusMoney(myCash);
        myAmount.negate();

        /* Report the returned cash */
        formatValue(MoneyWiseAnalysisSecurityAttr.RETURNEDCASH, myCash);
        if (myCashType != null) {
            formatValue(MoneyWiseAnalysisSecurityAttr.CASHTYPE, myCashType);
        }

        /* If we have changed the number of units */
        if (myDeltaUnits.isNonZero()) {
            /* Obtain the various values */
            myDeltaUnits = new OceanusUnits(myDeltaUnits);
            myDeltaUnits.negate();

            /* Format the units */
            formatSubtraction(MoneyWiseAnalysisSecurityAttr.UNITS, myUnits, myOriginalUnits, myDeltaUnits);

            /* Format the dilution */
            formatDivision(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution, myUnits, myOriginalUnits);

            /* Else we need to format the cost dilution */
        } else if (myConsideration != null) {
            /* Format the valuation */
            final OceanusMoney myValuation = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.VALUATION);
            final OceanusPrice myPrice = pValues.getPriceValue(MoneyWiseAnalysisSecurityAttr.PRICE);
            final OceanusRatio myXchangeRate = pValues.getRatioValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE);
            formatValuation(MoneyWiseAnalysisSecurityAttr.VALUATION, myValuation, myUnits, myPrice, myXchangeRate);
            formatAddition(MoneyWiseAnalysisSecurityAttr.CONSIDERATION, myConsideration, myCash, myValuation);

            /* Format the dilution */
            formatDivision(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution, myValuation, myConsideration);
        }

        /* Record the details */
        if (myCostDilution != null) {
            formatMultiplication(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myCost, myOriginalCost, myCostDilution);
            formatSubtraction(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST, myAllowedCost, myOriginalCost, myCost);
        } else {
            formatValue(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST, myAllowedCost);
            formatSubtraction(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myCost, myOriginalCost, myAllowedCost);
        }

        /* Record the gains allocation */
        if (myGain != null) {
            formatSubtraction(MoneyWiseAnalysisSecurityAttr.CAPITALGAIN, myGain, myCash, myAllowedCost);
            formatValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS, myTotalGains);
        }
    }

    /**
     * Format a Units Adjustment.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatUnitsAdjust(final MoneyWiseTransaction pTrans,
                                   final MoneyWiseAnalysisSecurityValues pValues) {
        /* Format the basic transaction */
        formatBasicTransaction(pTrans);

        /* Access interesting values */
        final OceanusUnits myUnits = pValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
        OceanusUnits myDeltaUnits = pTrans.getAccountDeltaUnits();

        /* Obtain the original units */
        final MoneyWiseAnalysisSecurityValues myPreviousValues = theSecurity.getPreviousValuesForTransaction(pTrans);
        final OceanusUnits myOriginalUnits = myPreviousValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);

        /* Record the details */
        if (myDeltaUnits.isPositive()) {
            formatAddition(MoneyWiseAnalysisSecurityAttr.UNITS, myUnits, myOriginalUnits, myDeltaUnits);
        } else {
            myDeltaUnits = new OceanusUnits(myDeltaUnits);
            myDeltaUnits.negate();
            formatSubtraction(MoneyWiseAnalysisSecurityAttr.UNITS, myUnits, myOriginalUnits, myDeltaUnits);
        }
    }

    /**
     * Format a Stock DeMerger.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatStockDeMerger(final MoneyWiseTransaction pTrans,
                                     final MoneyWiseAnalysisSecurityValues pValues) {
        /* Format the basic transaction */
        formatBasicTransaction(pTrans);

        /* Split workings for credit and debit */
        if (isDebit(pTrans)) {
            formatDebitStockDeMerger(pTrans, pValues);
        } else {
            formatCreditStockDeMerger(pTrans, pValues);
        }
    }

    /**
     * Format debit side of a Stock DeMerger.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatDebitStockDeMerger(final MoneyWiseTransaction pTrans,
                                          final MoneyWiseAnalysisSecurityValues pValues) {
        /* Access interesting values */
        final OceanusRatio myCostDilution = pValues.getRatioValue(MoneyWiseAnalysisSecurityAttr.COSTDILUTION);
        final OceanusMoney myResidualCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusMoney myXferredCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST);
        OceanusUnits myDeltaUnits = theSecurity.getUnitsDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.UNITS);

        /* Check whether the units have changed */
        final boolean isDeltaUnits = myDeltaUnits.isNonZero();

        /* Obtain the original cost */
        final MoneyWiseAnalysisSecurityValues myPreviousValues = theSecurity.getPreviousValuesForTransaction(pTrans);
        final OceanusMoney myOriginalCost = myPreviousValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);

        /* If we have changed the number of units */
        if (isDeltaUnits) {
            /* Obtain the various values */
            final OceanusUnits myOriginalUnits = myPreviousValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
            final OceanusUnits myUnits = pValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
            myDeltaUnits = new OceanusUnits(myDeltaUnits);
            myDeltaUnits.negate();

            /* Format the units/dilution */
            formatSubtraction(MoneyWiseAnalysisSecurityAttr.UNITS, myUnits, myOriginalUnits, myDeltaUnits);
            formatDivision(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution, myUnits, myOriginalUnits);

            /* else just report the dilution */
        } else {
            formatValue(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution);
        }

        /* Record the details */
        formatMultiplication(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myResidualCost, myOriginalCost, myCostDilution);
        formatSubtraction(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myXferredCost, myOriginalCost, myResidualCost);
    }

    /**
     * Format credit side of a Stock DeMerger.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatCreditStockDeMerger(final MoneyWiseTransaction pTrans,
                                           final MoneyWiseAnalysisSecurityValues pValues) {
        /* Access interesting values */
        final OceanusMoney myResidualCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusMoney myXferredCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST);
        final OceanusMoney myValueXfer = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE);
        final OceanusUnits myUnits = theSecurity.getUnitsDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.UNITS);
        final OceanusPrice myPrice = pValues.getPriceValue(MoneyWiseAnalysisSecurityAttr.PRICE);
        final OceanusRatio myXchangeRate = pValues.getRatioValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE);

        /* Record the details */
        formatValuation(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myValueXfer, myUnits, myPrice, myXchangeRate);
        formatValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myXferredCost);
        formatValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myResidualCost);
    }

    /**
     * Format a Stock TakeOver.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatStockTakeOver(final MoneyWiseTransaction pTrans,
                                     final MoneyWiseAnalysisSecurityValues pValues) {
        /* Format the basic transaction */
        formatBasicTransaction(pTrans);

        /* Split out Stock and Cash TakeOver */
        final OceanusMoney myCash = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RETURNEDCASH);
        if (myCash != null) {
            formatStockAndCashTakeOver(pTrans, pValues, myCash);

            /* Split workings for credit and debit */
        } else if (isDebit(pTrans)) {
            /* Record the transfer of cost for simple replacement takeOver */
            final OceanusMoney myCostXfer = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST);
            formatValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCostXfer);
        } else {
            formatCreditStockTakeOver(pTrans, pValues);
        }
    }

    /**
     * Format a StockAndCash TakeOver.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     * @param pCash   the cash consideration
     */
    private void formatStockAndCashTakeOver(final MoneyWiseTransaction pTrans,
                                            final MoneyWiseAnalysisSecurityValues pValues,
                                            final OceanusMoney pCash) {
        /* Split workings for credit and debit */
        if (isDebit(pTrans)) {
            formatDebitStockAndCashTakeOver(pTrans, pValues, pCash);
        } else {
            formatCreditStockTakeOver(pTrans, pValues);
        }
    }

    /**
     * Format debit side of a StockAndCash TakeOver.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     * @param pCash   the cash consideration
     */
    private void formatDebitStockAndCashTakeOver(final MoneyWiseTransaction pTrans,
                                                 final MoneyWiseAnalysisSecurityValues pValues,
                                                 final OceanusMoney pCash) {
        /* Access interesting values */
        final OceanusMoney myStock = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE);
        final OceanusMoney myConsideration = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CONSIDERATION);
        final OceanusMoney myCostXfer = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST);
        final OceanusRatio myCostDilution = pValues.getRatioValue(MoneyWiseAnalysisSecurityAttr.COSTDILUTION);
        final OceanusMoney myAllowedCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST);
        final OceanusMoney myGain = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.CAPITALGAIN);
        final OceanusMoney myTotalGains = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS);
        final MoneyWiseCashType myCashType = pValues.getEnumValue(MoneyWiseAnalysisSecurityAttr.CASHTYPE, MoneyWiseCashType.class);

        /* Record the calculation of total consideration */
        formatValue(MoneyWiseAnalysisSecurityAttr.RETURNEDCASH, pCash);
        formatValue(MoneyWiseAnalysisSecurityAttr.CASHTYPE, myCashType);
        formatValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myStock);
        formatAddition(MoneyWiseAnalysisSecurityAttr.CONSIDERATION, myConsideration, pCash, myStock);

        /* Obtain the original cost */
        final MoneyWiseAnalysisSecurityValues myPreviousValues = theSecurity.getPreviousValuesForTransaction(pTrans);
        final OceanusMoney myOriginalCost = myPreviousValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);

        /* Format the cost dilution */
        if (myCostDilution != null) {
            formatDivision(MoneyWiseAnalysisSecurityAttr.COSTDILUTION, myCostDilution, pCash, myConsideration);
            formatMultiplication(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST, myAllowedCost, myOriginalCost, myCostDilution);
        } else {
            formatValue(MoneyWiseAnalysisSecurityAttr.ALLOWEDCOST, myAllowedCost);
        }
        formatSubtraction(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCostXfer, myOriginalCost, myAllowedCost);

        /* Record the gains allocation */
        if (myGain != null) {
            formatSubtraction(MoneyWiseAnalysisSecurityAttr.CAPITALGAIN, myGain, pCash, myAllowedCost);
            formatValue(MoneyWiseAnalysisSecurityAttr.REALISEDGAINS, myTotalGains);
        }
    }

    /**
     * Format credit side of a StockAndCash TakeOver.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatCreditStockTakeOver(final MoneyWiseTransaction pTrans,
                                           final MoneyWiseAnalysisSecurityValues pValues) {
        /* Access interesting values */
        final OceanusPrice myPrice = pValues.getPriceValue(MoneyWiseAnalysisSecurityAttr.PRICE);
        final OceanusUnits myUnits = theSecurity.getUnitsDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.UNITS);
        final OceanusMoney myValueXfer = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE);
        final OceanusMoney myCostXfer = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST);
        final OceanusMoney myResidualCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
        final OceanusRatio myXchangeRate = pValues.getRatioValue(MoneyWiseAnalysisSecurityAttr.EXCHANGERATE);

        /* Detail the new units and cost */
        final MoneyWiseAnalysisSecurityValues myPreviousValues = theSecurity.getPreviousValuesForTransaction(pTrans);
        final OceanusUnits myNewUnits = pValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
        final OceanusUnits myOriginalUnits = myPreviousValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
        formatAddition(MoneyWiseAnalysisSecurityAttr.UNITS, myNewUnits, myOriginalUnits, myUnits);

        /* Record the transfer of value and cost */
        formatValuation(MoneyWiseAnalysisSecurityAttr.XFERREDVALUE, myValueXfer, myUnits, myPrice, myXchangeRate);
        formatValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCostXfer);
        formatValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myResidualCost);
    }

    /**
     * Format a Stock DeMerger.
     *
     * @param pTrans  the transaction
     * @param pValues the values for the transaction
     */
    private void formatPortfolioXfer(final MoneyWiseTransaction pTrans,
                                     final MoneyWiseAnalysisSecurityValues pValues) {
        /* Format the basic transaction */
        formatBasicTransaction(pTrans);

        /* Determine the direction of transfer */
        final OceanusMoney myCostXfer = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST);
        formatValue(MoneyWiseAnalysisSecurityAttr.XFERREDCOST, myCostXfer);

        final OceanusUnits myUnits = theSecurity.getUnitsDeltaForTransaction(pTrans, MoneyWiseAnalysisSecurityAttr.UNITS);
        if (myUnits.isPositive()) {
            /* Detail the new units and cost */
            final MoneyWiseAnalysisSecurityValues myPreviousValues = theSecurity.getPreviousValuesForTransaction(pTrans);
            final OceanusUnits myNewUnits = pValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
            final OceanusUnits myOriginalUnits = myPreviousValues.getUnitsValue(MoneyWiseAnalysisSecurityAttr.UNITS);
            formatAddition(MoneyWiseAnalysisSecurityAttr.UNITS, myNewUnits, myOriginalUnits, myUnits);
            final OceanusMoney myCost = pValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
            final OceanusMoney myOriginalCost = myPreviousValues.getMoneyValue(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST);
            formatAddition(MoneyWiseAnalysisSecurityAttr.RESIDUALCOST, myCost, myOriginalCost, myCostXfer);
        }
    }

    @Override
    public MoneyWiseAnalysisFilter<?, ?> processFilter(final Object pSource) {
        return null;
    }

    @Override
    public MetisHTMLTable createDelayedTable(final DelayedTable pTable) {
        return null;
    }
}