MoneyWiseAnalysisTransactionHelper.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.data.analysis.data;

import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
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.metis.data.MetisDataDifference;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetDirection;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurity;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseSecurityHolding;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransAsset;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransaction;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseCurrency;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;

import java.util.Currency;

/**
 * Transaction Analysis Helper.
 */
public class MoneyWiseAnalysisTransactionHelper {
    /**
     * Currency Cursor.
     */
    private final MoneyWiseAnalysisExchangeRateCursor theRateCursor;

    /**
     * Security Cursor.
     */
    private final MoneyWiseAnalysisSecurityPriceCursor thePriceCursor;

    /**
     * Reporting currency.
     */
    private final MoneyWiseCurrency theCurrency;

    /**
     * The current transaction.
     */
    private MoneyWiseTransaction theCurrent;

    /**
     * The current date.
     */
    private OceanusDate theDate;

    /**
     * The account detail.
     */
    private TransactionDetail theAccountDetail;

    /**
     * The debit XchangeRate.
     */
    private OceanusRatio theDebitXchangeRate;

    /**
     * Constructor.
     *
     * @param pData the dataSet
     */
    public MoneyWiseAnalysisTransactionHelper(final MoneyWiseDataSet pData) {
        /* Create the cursors */
        theRateCursor = new MoneyWiseAnalysisExchangeRateCursor(pData);
        thePriceCursor = new MoneyWiseAnalysisSecurityPriceCursor(pData);

        /* Note the reporting currency */
        theCurrency = pData.getReportingCurrency();
    }

    /**
     * Obtain transaction.
     *
     * @return the transaction
     */
    public MoneyWiseTransaction getTransaction() {
        return theCurrent;
    }

    /**
     * Set the transaction.
     *
     * @param pTrans the transaction.
     */
    public void setTransaction(final MoneyWiseTransaction pTrans) {
        /* Record date */
        theCurrent = pTrans;
        theDate = theCurrent.getDate();

        /* Reset details */
        theAccountDetail = new TransactionDetail();
        theDebitXchangeRate = theAccountDetail.getDebitExchangeRate();
    }

    /**
     * Set the security (for PortfolioXfer).
     *
     * @param pSecurity the security.
     */
    public void setSecurity(final MoneyWiseSecurity pSecurity) {
        final MoneyWiseCurrency myCurr = pSecurity.getAssetCurrency();
        final boolean isForeign = !MetisDataDifference.isEqual(myCurr, theCurrency);
        theDebitXchangeRate = isForeign ? theRateCursor.getExchangeRate(myCurr, theDate) : null;
    }

    /**
     * Obtain date.
     *
     * @return the date
     */
    public OceanusDate getDate() {
        return theDate;
    }

    /**
     * Obtain account.
     *
     * @return the account
     */
    public MoneyWiseTransAsset getAccount() {
        return theAccountDetail.getAccount();
    }

    /**
     * Obtain partner.
     *
     * @return the partner
     */
    public MoneyWiseTransAsset getPartner() {
        return theAccountDetail.getPartner();
    }

    /**
     * Obtain direction.
     *
     * @return the direction
     */
    public MoneyWiseAssetDirection getDirection() {
        return theAccountDetail.getDirection();
    }

    /**
     * Obtain debit asset.
     *
     * @return the debit asset
     */
    public MoneyWiseTransAsset getDebitAsset() {
        return theAccountDetail.getDebitAsset();
    }

    /**
     * Obtain credit asset.
     *
     * @return the credit asset
     */
    public MoneyWiseTransAsset getCreditAsset() {
        return theAccountDetail.getCreditAsset();
    }

    /**
     * Obtain the category.
     *
     * @return the category
     */
    public MoneyWiseTransCategory getCategory() {
        return theAccountDetail.getCategory();
    }

    /**
     * Is this a particular category class?
     *
     * @param pClass the category class
     * @return true/false
     */
    public boolean isCategoryClass(final MoneyWiseTransCategoryClass pClass) {
        return theAccountDetail.isCategoryClass(pClass);
    }

    /**
     * Obtain the category class.
     *
     * @return the category class
     */
    public MoneyWiseTransCategoryClass getCategoryClass() {
        return theAccountDetail.getCategoryClass();
    }

    /**
     * Obtain debit amount.
     *
     * @return the debit amount.
     */
    public OceanusMoney getDebitAmount() {
        return theAccountDetail.getDebitAmount();
    }

    /**
     * Obtain local amount.
     *
     * @return the amount.
     */
    public OceanusMoney getLocalAmount() {
        return theAccountDetail.getLocalAmount();
    }

    /**
     * Obtain credit amount.
     *
     * @return the credit amount.
     */
    public OceanusMoney getCreditAmount() {
        return theAccountDetail.getCreditAmount();
    }

    /**
     * Obtain local returnedCash.
     *
     * @return the returnedCash.
     */
    public OceanusMoney getLocalReturnedCash() {
        return theAccountDetail.getLocalReturnedCash();
    }

    /**
     * Obtain returnedCash.
     *
     * @return the returned cash.
     */
    public OceanusMoney getReturnedCash() {
        return theAccountDetail.getReturnedCash();
    }

    /**
     * Obtain tax credit.
     *
     * @return the tax credit.
     */
    public OceanusMoney getTaxCredit() {
        return theAccountDetail.getTaxCredit();
    }

    /**
     * Obtain employer national insurance.
     *
     * @return the national insurance.
     */
    public OceanusMoney getEmployerNatIns() {
        return theAccountDetail.getEmployerNatIns();
    }

    /**
     * Obtain employee national insurance.
     *
     * @return the national insurance.
     */
    public OceanusMoney getEmployeeNatIns() {
        return theAccountDetail.getEmployeeNatIns();
    }

    /**
     * Obtain benefit.
     *
     * @return the benefit.
     */
    public OceanusMoney getDeemedBenefit() {
        return theAccountDetail.getBenefit();
    }

    /**
     * Obtain withheld.
     *
     * @return the withheld.
     */
    public OceanusMoney getWithheld() {
        return theAccountDetail.getWithheld();
    }

    /**
     * Obtain returnedCash Account.
     *
     * @return the returnedCash account
     */
    public MoneyWiseTransAsset getReturnedCashAccount() {
        return theAccountDetail.getReturnedCashAccount();
    }

    /**
     * Obtain debit units.
     *
     * @return the debit units
     */
    public OceanusUnits getDebitUnits() {
        OceanusUnits myUnits = getDirection().isTo()
                ? getAccountDeltaUnits()
                : getPartnerDeltaUnits();
        if (myUnits != null) {
            myUnits = new OceanusUnits(myUnits);
            myUnits.negate();
        }
        return myUnits;
    }

    /**
     * Obtain credit units.
     *
     * @return the debit units
     */
    public OceanusUnits getCreditUnits() {
        return getDirection().isFrom()
                ? getAccountDeltaUnits()
                : getPartnerDeltaUnits();
    }

    /**
     * Obtain account delta units.
     *
     * @return the delta units
     */
    public OceanusUnits getAccountDeltaUnits() {
        return theAccountDetail.getAccountDeltaUnits();
    }

    /**
     * Obtain partner delta units.
     *
     * @return the delta units
     */
    public OceanusUnits getPartnerDeltaUnits() {
        return theAccountDetail.getPartnerDeltaUnits();
    }

    /**
     * Obtain dilution.
     *
     * @return the dilution
     */
    public OceanusRatio getDilution() {
        return theAccountDetail.getDilution();
    }

    /**
     * Obtain debit price.
     *
     * @return the debit price
     */
    public OceanusPrice getDebitPrice() {
        return theAccountDetail.getDebitPrice();
    }

    /**
     * Obtain credit price.
     *
     * @return the credit price
     */
    public OceanusPrice getCreditPrice() {
        return theAccountDetail.getCreditPrice();
    }

    /**
     * Obtain debit exchangeRate.
     *
     * @return the rate
     */
    public OceanusRatio getDebitExchangeRate() {
        return theDebitXchangeRate;
    }

    /**
     * Obtain credit exchangeRate.
     *
     * @return the rate
     */
    public OceanusRatio getCreditExchangeRate() {
        return theAccountDetail.getCreditExchangeRate();
    }

    /**
     * Obtain returnedCash exchangeRate.
     *
     * @return the rate
     */
    public OceanusRatio getReturnedCashExchangeRate() {
        return theAccountDetail.getReturnedCashExchangeRate();
    }

    /**
     * Convert amount to reporting currency.
     *
     * @param pCurrency the currency
     * @param pDate     the date for the conversion
     * @return the reporting amount
     */
    protected OceanusRatio getExchangeRate(final MoneyWiseCurrency pCurrency,
                                           final OceanusDate pDate) {
        return theRateCursor.getExchangeRate(pCurrency, pDate);
    }

    /**
     * Transaction Detail class.
     */
    private final class TransactionDetail {
        /**
         * The account.
         */
        private final MoneyWiseTransAsset theAccount;

        /**
         * The partner.
         */
        private final MoneyWiseTransAsset thePartner;

        /**
         * The direction.
         */
        private final MoneyWiseAssetDirection theDirection;

        /**
         * The category.
         */
        private final MoneyWiseTransCategory theCategory;

        /**
         * The amount.
         */
        private final OceanusMoney theAmount;

        /**
         * The ReturnedCashAccount.
         */
        private final MoneyWiseTransAsset theReturnedCashAccount;

        /**
         * The returnedCash.
         */
        private final OceanusMoney theReturnedCash;

        /**
         * The tax credit.
         */
        private final OceanusMoney theTaxCredit;

        /**
         * The Employer natIns.
         */
        private final OceanusMoney theEmployerNatIns;

        /**
         * The Employee natIns.
         */
        private final OceanusMoney theEmployeeNatIns;

        /**
         * The benefit amount.
         */
        private final OceanusMoney theBenefit;

        /**
         * The withheld amount.
         */
        private final OceanusMoney theWithheld;

        /**
         * The account delta units.
         */
        private final OceanusUnits theAccountUnits;

        /**
         * The partner delta units.
         */
        private final OceanusUnits thePartnerUnits;

        /**
         * The dilution.
         */
        private final OceanusRatio theDilution;

        /**
         * The account price.
         */
        private final OceanusPrice theAccountPrice;

        /**
         * The partner price.
         */
        private final OceanusPrice thePartnerPrice;

        /**
         * The foreign account details.
         */
        private final ForeignAccountDetail theForeignAccount;

        /**
         * The foreign partner details.
         */
        private final ForeignPartnerDetail theForeignPartner;

        /**
         * The foreign returnedCash details.
         */
        private final ForeignPartnerDetail theForeignReturnedCash;

        /**
         * Constructor.
         */
        private TransactionDetail() {
            /* Store detail */
            theAccount = theCurrent.getAccount();
            thePartner = theCurrent.getPartner();
            theDirection = theCurrent.getDirection();
            theCategory = theCurrent.getCategory();
            theTaxCredit = theCurrent.getTaxCredit();
            theEmployerNatIns = theCurrent.getEmployerNatIns();
            theEmployeeNatIns = theCurrent.getEmployeeNatIns();
            theBenefit = theCurrent.getDeemedBenefit();
            theWithheld = theCurrent.getWithheld();
            theReturnedCashAccount = theCurrent.getReturnedCashAccount();
            theAccountUnits = theCurrent.getAccountDeltaUnits();
            thePartnerUnits = theCurrent.getPartnerDeltaUnits();
            theDilution = theCurrent.getDilution();

            /* Obtain the amounts */
            final OceanusMoney myAmount = theCurrent.getAmount();
            final OceanusMoney myPartnerAmount = theCurrent.getPartnerAmount();
            final OceanusMoney myReturnedCash = theCurrent.getReturnedCash();

            /* Determine account prices */
            theAccountPrice = theAccount instanceof MoneyWiseSecurityHolding
                    ? thePriceCursor.getSecurityPrice(((MoneyWiseSecurityHolding) theAccount).getSecurity(), theDate)
                    : null;
            thePartnerPrice = thePartner instanceof MoneyWiseSecurityHolding
                    ? thePriceCursor.getSecurityPrice(((MoneyWiseSecurityHolding) thePartner).getSecurity(), theDate)
                    : null;

            /* Determine foreign account detail */
            MoneyWiseCurrency myActCurrency = theAccount.getAssetCurrency();
            theForeignAccount = MetisDataDifference.isEqual(myActCurrency, theCurrency)
                    ? null
                    : new ForeignAccountDetail(this, myActCurrency, myAmount);

            /* If we have a partner amount */
            myActCurrency = thePartner.getAssetCurrency();
            theForeignPartner = myActCurrency == null
                    || MetisDataDifference.isEqual(myActCurrency, theCurrency)
                    ? null
                    : new ForeignPartnerDetail(myActCurrency, myPartnerAmount);

            /* If we have a returnedCash account */
            if (theReturnedCashAccount != null) {
                /* Determine foreign returnedCash detail */
                myActCurrency = theReturnedCashAccount.getAssetCurrency();
                theForeignReturnedCash = MetisDataDifference.isEqual(myActCurrency, theCurrency)
                        ? null
                        : new ForeignPartnerDetail(myActCurrency, myReturnedCash);
            } else {
                theForeignReturnedCash = null;
            }

            /* Determine the local amounts */
            theAmount = theForeignAccount == null
                    ? myAmount
                    : theForeignPartner == null
                    ? myPartnerAmount
                    : theForeignAccount.theAmount;
            theReturnedCash = theForeignReturnedCash == null
                    ? myReturnedCash
                    : theForeignReturnedCash.theAmount;
        }

        /**
         * Obtain account.
         *
         * @return the account
         */
        private MoneyWiseTransAsset getAccount() {
            return theAccount;
        }

        /**
         * Obtain partner.
         *
         * @return the partner
         */
        private MoneyWiseTransAsset getPartner() {
            return thePartner;
        }

        /**
         * Obtain direction.
         *
         * @return the direction
         */
        private MoneyWiseAssetDirection getDirection() {
            return theDirection;
        }

        /**
         * Obtain debit asset.
         *
         * @return the debit asset
         */
        private MoneyWiseTransAsset getDebitAsset() {
            return theDirection.isFrom()
                    ? thePartner
                    : theAccount;
        }

        /**
         * Obtain credit asset.
         *
         * @return the credit asset
         */
        private MoneyWiseTransAsset getCreditAsset() {
            return theDirection.isTo()
                    ? thePartner
                    : theAccount;
        }

        /**
         * Obtain returnedCash account.
         *
         * @return the returnedCash account
         */
        private MoneyWiseTransAsset getReturnedCashAccount() {
            return theReturnedCashAccount;
        }

        /**
         * Obtain category.
         *
         * @return the category class
         */
        private MoneyWiseTransCategory getCategory() {
            return theCategory;
        }

        /**
         * Is this a particular category class?
         *
         * @param pClass the category class
         * @return true/false
         */
        private boolean isCategoryClass(final MoneyWiseTransCategoryClass pClass) {
            return theCategory.isCategoryClass(pClass);
        }

        /**
         * Obtain category class?
         *
         * @return the category class
         */
        private MoneyWiseTransCategoryClass getCategoryClass() {
            return theCategory.getCategoryTypeClass();
        }

        /**
         * Obtain debit amount.
         *
         * @return the debit amount
         */
        private OceanusMoney getDebitAmount() {
            return theDirection.isFrom()
                    ? theForeignPartner == null
                    ? theAmount
                    : theForeignPartner.theBase
                    : theForeignAccount == null
                    ? theAmount
                    : theForeignAccount.theBase;
        }

        /**
         * Obtain local debit amount.
         *
         * @return the local debit amount
         */
        private OceanusMoney getLocalAmount() {
            return theAmount;
        }

        /**
         * Obtain credit amount.
         *
         * @return the credit amount
         */
        private OceanusMoney getCreditAmount() {
            return theDirection.isTo()
                    ? theForeignPartner == null
                    ? theAmount
                    : theForeignPartner.theBase
                    : theForeignAccount == null
                    ? theAmount
                    : theForeignAccount.theBase;
        }

        /**
         * Obtain local returnedCash.
         *
         * @return the local returnedCash
         */
        private OceanusMoney getLocalReturnedCash() {
            return theReturnedCash;
        }

        /**
         * Obtain returnedCash.
         *
         * @return the returnedCash
         */
        private OceanusMoney getReturnedCash() {
            return theForeignReturnedCash == null
                    ? theReturnedCash
                    : theForeignReturnedCash.theBase;
        }

        /**
         * Obtain debit price.
         *
         * @return the debit price
         */
        private OceanusPrice getDebitPrice() {
            return theDirection.isFrom()
                    ? thePartnerPrice
                    : theAccountPrice;
        }

        /**
         * Obtain credit price.
         *
         * @return the credit price
         */
        private OceanusPrice getCreditPrice() {
            return theDirection.isTo()
                    ? thePartnerPrice
                    : theAccountPrice;
        }

        /**
         * Obtain debit exchangeRate.
         *
         * @return the rate
         */
        private OceanusRatio getDebitExchangeRate() {
            return theDirection.isTo()
                    ? theForeignAccount == null
                    ? null
                    : theForeignAccount.theExchangeRate
                    : theForeignPartner == null
                    ? null
                    : theForeignPartner.theExchangeRate;
        }

        /**
         * Obtain credit exchangeRate.
         *
         * @return the rate
         */
        private OceanusRatio getCreditExchangeRate() {
            return theDirection.isTo()
                    ? theForeignPartner == null
                    ? null
                    : theForeignPartner.theExchangeRate
                    : theForeignAccount == null
                    ? null
                    : theForeignAccount.theExchangeRate;
        }

        /**
         * Obtain thirdParty exchangeRate.
         *
         * @return the rate
         */
        private OceanusRatio getReturnedCashExchangeRate() {
            return theForeignReturnedCash == null
                    ? null
                    : theForeignReturnedCash.theExchangeRate;
        }

        /**
         * Obtain taxCredit.
         *
         * @return the tax credit
         */
        private OceanusMoney getTaxCredit() {
            return theForeignAccount != null
                    ? theForeignAccount.theTaxCredit
                    : theTaxCredit;
        }

        /**
         * Obtain employer natInsurance.
         *
         * @return the national insurance
         */
        private OceanusMoney getEmployerNatIns() {
            return theForeignAccount != null
                    ? theForeignAccount.theEmployerNatIns
                    : theEmployerNatIns;
        }

        /**
         * Obtain employee natInsurance.
         *
         * @return the national insurance
         */
        private OceanusMoney getEmployeeNatIns() {
            return theForeignAccount != null
                    ? theForeignAccount.theEmployeeNatIns
                    : theEmployeeNatIns;
        }

        /**
         * Obtain benefit.
         *
         * @return the benefit
         */
        private OceanusMoney getBenefit() {
            return theForeignAccount != null
                    ? theForeignAccount.theBenefit
                    : theBenefit;
        }

        /**
         * Obtain donation.
         *
         * @return the donation
         */
        private OceanusMoney getWithheld() {
            return theForeignAccount != null
                    ? theForeignAccount.theWithheld
                    : theWithheld;
        }

        /**
         * Obtain account delta units.
         *
         * @return the delta units
         */
        private OceanusUnits getAccountDeltaUnits() {
            return theAccountUnits;
        }

        /**
         * Obtain partner delta units.
         *
         * @return the partner delta units
         */
        private OceanusUnits getPartnerDeltaUnits() {
            return thePartnerUnits;
        }

        /**
         * Obtain dilution.
         *
         * @return the dilution
         */
        private OceanusRatio getDilution() {
            return theDilution;
        }
    }

    /**
     * Foreign Account class.
     */
    private final class ForeignAccountDetail {
        /**
         * The exchange rate.
         */
        private final OceanusRatio theExchangeRate;

        /**
         * The base amount.
         */
        private final OceanusMoney theBase;

        /**
         * The amount.
         */
        private final OceanusMoney theAmount;

        /**
         * The tax credit.
         */
        private final OceanusMoney theTaxCredit;

        /**
         * The employer natInsurance.
         */
        private final OceanusMoney theEmployerNatIns;

        /**
         * The employee natInsurance.
         */
        private final OceanusMoney theEmployeeNatIns;

        /**
         * The benefit amount.
         */
        private final OceanusMoney theBenefit;

        /**
         * The withheld amount.
         */
        private final OceanusMoney theWithheld;

        /**
         * Constructor.
         *
         * @param pTrans    the transaction detail
         * @param pCurrency the foreign currency
         * @param pAmount   the amount
         */
        private ForeignAccountDetail(final TransactionDetail pTrans,
                                     final MoneyWiseCurrency pCurrency,
                                     final OceanusMoney pAmount) {
            /* Obtain the required exchange rate */
            theBase = pAmount;
            final OceanusRatio myEventRate = theCurrent.getExchangeRate();
            final OceanusRatio myRate = myEventRate == null
                    ? theRateCursor.getExchangeRate(pCurrency, theDate)
                    : myEventRate;
            theExchangeRate = myRate;
            final Currency myCurrency = theCurrency.getCurrency();

            /* Obtain local amount */
            theAmount = theBase != null
                    ? theBase.convertCurrency(myCurrency, myRate)
                    : null;

            /* Obtain tax value */
            OceanusMoney myValue = pTrans.theTaxCredit;
            theTaxCredit = myValue != null
                    ? myValue.convertCurrency(myCurrency, myRate)
                    : null;
            /* Obtain Employer NatIns */
            myValue = pTrans.theEmployerNatIns;
            theEmployerNatIns = myValue != null
                    ? myValue.convertCurrency(myCurrency, myRate)
                    : null;

            /* Obtain Employee NatIns */
            myValue = pTrans.theEmployeeNatIns;
            theEmployeeNatIns = myValue != null
                    ? myValue.convertCurrency(myCurrency, myRate)
                    : null;

            /* Obtain benefit */
            myValue = pTrans.theBenefit;
            theBenefit = myValue != null
                    ? myValue.convertCurrency(myCurrency, myRate)
                    : null;

            /* Obtain withheld */
            myValue = pTrans.theWithheld;
            theWithheld = myValue != null
                    ? myValue.convertCurrency(myCurrency, myRate)
                    : null;
        }
    }

    /**
     * Foreign Partner class.
     */
    private final class ForeignPartnerDetail {
        /**
         * The exchange rate.
         */
        private final OceanusRatio theExchangeRate;

        /**
         * The base amount.
         */
        private final OceanusMoney theBase;

        /**
         * The local amount.
         */
        private final OceanusMoney theAmount;

        /**
         * Constructor.
         *
         * @param pCurrency the foreign currency
         * @param pAmount   the amount
         */
        private ForeignPartnerDetail(final MoneyWiseCurrency pCurrency,
                                     final OceanusMoney pAmount) {
            /* Obtain the required exchange rate */
            theBase = pAmount;
            theExchangeRate = theRateCursor.getExchangeRate(pCurrency, theDate);
            final OceanusRatio myRate = theExchangeRate;
            final Currency myCurrency = theCurrency.getCurrency();

            /* Obtain local amount */
            theAmount = theBase != null
                    ? theBase.convertCurrency(myCurrency, myRate)
                    : null;
        }
    }
}