MoneyWiseAnalysisTransCategoryBucket.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.date.OceanusDateRange;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
import io.github.tonywasher.joceanus.oceanus.decimal.OceanusMoney;
import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
import io.github.tonywasher.joceanus.metis.data.MetisDataFieldValue;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataFieldId;
import io.github.tonywasher.joceanus.metis.data.MetisDataItem.MetisDataList;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem;
import io.github.tonywasher.joceanus.metis.field.MetisFieldItem.MetisFieldTableItem;
import io.github.tonywasher.joceanus.metis.field.MetisFieldSet;
import io.github.tonywasher.joceanus.metis.list.MetisListIndexed;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseAssetDirection;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseBasicDataType;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWisePortfolio;
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.MoneyWiseTransCategory;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransCategory.MoneyWiseTransCategoryList;
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.MoneyWiseStaticDataType;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTaxClass;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryClass;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransCategoryType;
import io.github.tonywasher.joceanus.moneywise.data.statics.MoneyWiseTransInfoClass;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.base.MoneyWiseAnalysisHistory;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.data.MoneyWiseAnalysisTaxBasisBucket.MoneyWiseAnalysisTaxBasisBucketList;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisCategoryValues;
import io.github.tonywasher.joceanus.moneywise.lethe.data.analysis.values.MoneyWiseAnalysisTransAttr;
import io.github.tonywasher.joceanus.prometheus.views.PrometheusEditSet;
import java.util.Comparator;
import java.util.Currency;
import java.util.Iterator;
import java.util.List;
/**
* Transaction Category Bucket.
*/
public final class MoneyWiseAnalysisTransCategoryBucket
implements MetisFieldTableItem {
/**
* Local Report fields.
*/
private static final MetisFieldSet<MoneyWiseAnalysisTransCategoryBucket> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseAnalysisTransCategoryBucket.class);
/*
* Declare Fields.
*/
static {
FIELD_DEFS.declareLocalField(MoneyWiseAnalysisDataResource.ANALYSIS_NAME, MoneyWiseAnalysisTransCategoryBucket::getAnalysis);
FIELD_DEFS.declareLocalField(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseAnalysisTransCategoryBucket::getTransactionCategory);
FIELD_DEFS.declareLocalField(MoneyWiseStaticDataType.TRANSTYPE, MoneyWiseAnalysisTransCategoryBucket::getTransactionCategoryType);
FIELD_DEFS.declareLocalField(MoneyWiseAnalysisDataResource.BUCKET_BASEVALUES, MoneyWiseAnalysisTransCategoryBucket::getBaseValues);
FIELD_DEFS.declareLocalField(MoneyWiseAnalysisDataResource.BUCKET_HISTORY, MoneyWiseAnalysisTransCategoryBucket::getHistoryMap);
FIELD_DEFS.declareLocalFieldsForEnum(MoneyWiseAnalysisTransAttr.class, MoneyWiseAnalysisTransCategoryBucket::getAttributeValue);
}
/**
* Totals bucket name.
*/
private static final MetisDataFieldId NAME_TOTALS = MoneyWiseAnalysisDataResource.ANALYSIS_TOTALS;
/**
* The analysis.
*/
private final MoneyWiseAnalysis theAnalysis;
/**
* The transaction category.
*/
private final MoneyWiseTransCategory theCategory;
/**
* The transaction category type.
*/
private final MoneyWiseTransCategoryType theType;
/**
* Values.
*/
private final MoneyWiseAnalysisCategoryValues theValues;
/**
* The base values.
*/
private final MoneyWiseAnalysisCategoryValues theBaseValues;
/**
* History Map.
*/
private final MoneyWiseAnalysisHistory<MoneyWiseAnalysisCategoryValues, MoneyWiseAnalysisTransAttr> theHistory;
/**
* Constructor.
*
* @param pAnalysis the analysis
* @param pCategory the transaction category
*/
private MoneyWiseAnalysisTransCategoryBucket(final MoneyWiseAnalysis pAnalysis,
final MoneyWiseTransCategory pCategory) {
/* Store the parameters */
theAnalysis = pAnalysis;
theCategory = pCategory;
theType = pCategory == null
? null
: pCategory.getCategoryType();
/* Create the history map */
final MoneyWiseCurrency myDefault = theAnalysis.getCurrency();
final Currency myCurrency = myDefault == null
? MoneyWiseAnalysisAccountBucket.DEFAULT_CURRENCY
: myDefault.getCurrency();
final MoneyWiseAnalysisCategoryValues myValues = new MoneyWiseAnalysisCategoryValues(myCurrency);
theHistory = new MoneyWiseAnalysisHistory<>(myValues);
/* Access the key value maps */
theValues = theHistory.getValues();
theBaseValues = theHistory.getBaseValues();
}
/**
* Constructor.
*
* @param pAnalysis the analysis
* @param pBase the underlying bucket
* @param pDate the date for the bucket
*/
private MoneyWiseAnalysisTransCategoryBucket(final MoneyWiseAnalysis pAnalysis,
final MoneyWiseAnalysisTransCategoryBucket pBase,
final OceanusDate pDate) {
/* Copy details from base */
theCategory = pBase.getTransactionCategory();
theType = pBase.getTransactionCategoryType();
theAnalysis = pAnalysis;
/* Access the relevant history */
theHistory = new MoneyWiseAnalysisHistory<>(pBase.getHistoryMap(), pDate);
/* Access the key value maps */
theValues = theHistory.getValues();
theBaseValues = theHistory.getBaseValues();
}
/**
* Constructor.
*
* @param pAnalysis the analysis
* @param pBase the underlying bucket
* @param pRange the range for the bucket
*/
private MoneyWiseAnalysisTransCategoryBucket(final MoneyWiseAnalysis pAnalysis,
final MoneyWiseAnalysisTransCategoryBucket pBase,
final OceanusDateRange pRange) {
/* Copy details from base */
theCategory = pBase.getTransactionCategory();
theType = pBase.getTransactionCategoryType();
theAnalysis = pAnalysis;
/* Access the relevant history */
theHistory = new MoneyWiseAnalysisHistory<>(pBase.getHistoryMap(), pRange);
/* Access the key value maps */
theValues = theHistory.getValues();
theBaseValues = theHistory.getBaseValues();
}
@Override
public MetisFieldSet<MoneyWiseAnalysisTransCategoryBucket> getDataFieldSet() {
return FIELD_DEFS;
}
@Override
public String formatObject(final OceanusDataFormatter pFormatter) {
return toString();
}
@Override
public String toString() {
return getName();
}
/**
* Obtain the name.
*
* @return the name
*/
public String getName() {
return theCategory == null
? NAME_TOTALS.getId()
: theCategory.getName();
}
@Override
public Integer getIndexedId() {
return theCategory.getIndexedId();
}
/**
* Obtain the transaction category.
*
* @return the transaction category
*/
public MoneyWiseTransCategory getTransactionCategory() {
return theCategory;
}
/**
* Obtain the transaction category type.
*
* @return the transaction category type
*/
public MoneyWiseTransCategoryType getTransactionCategoryType() {
return theType;
}
/**
* Is this bucket idle?
*
* @return true/false
*/
public Boolean isIdle() {
return theHistory.isIdle();
}
/**
* Obtain the value map.
*
* @return the value map
*/
public MoneyWiseAnalysisCategoryValues getValues() {
return theValues;
}
/**
* Obtain the base value map.
*
* @return the base value map
*/
public MoneyWiseAnalysisCategoryValues getBaseValues() {
return theBaseValues;
}
/**
* Obtain values for transaction.
*
* @param pTrans the transaction
* @return the values (or null)
*/
public MoneyWiseAnalysisCategoryValues getValuesForTransaction(final MoneyWiseTransaction pTrans) {
/* Obtain values for transaction */
return theHistory.getValuesForTransaction(pTrans);
}
/**
* Obtain previous values for transaction.
*
* @param pTrans the transaction
* @return the values (or null)
*/
public MoneyWiseAnalysisCategoryValues getPreviousValuesForTransaction(final MoneyWiseTransaction pTrans) {
return theHistory.getPreviousValuesForTransaction(pTrans);
}
/**
* Obtain delta for transaction.
*
* @param pTrans the transaction
* @param pAttr the attribute
* @return the delta (or null)
*/
public OceanusDecimal getDeltaForTransaction(final MoneyWiseTransaction pTrans,
final MoneyWiseAnalysisTransAttr pAttr) {
/* Obtain delta for transaction */
return theHistory.getDeltaValue(pTrans, pAttr);
}
/**
* Obtain the history map.
*
* @return the history map
*/
private MoneyWiseAnalysisHistory<MoneyWiseAnalysisCategoryValues, MoneyWiseAnalysisTransAttr> getHistoryMap() {
return theHistory;
}
/**
* Obtain the analysis.
*
* @return the analysis
*/
MoneyWiseAnalysis getAnalysis() {
return theAnalysis;
}
/**
* Obtain date range.
*
* @return the range
*/
public OceanusDateRange getDateRange() {
return theAnalysis.getDateRange();
}
/**
* Set Attribute.
*
* @param pAttr the attribute
* @param pValue the value of the attribute
*/
void setValue(final MoneyWiseAnalysisTransAttr pAttr,
final OceanusMoney pValue) {
/* Set the value */
theValues.setValue(pAttr, pValue);
}
/**
* Get an attribute value.
*
* @param pAttr the attribute
* @return the value to set
*/
private Object getAttributeValue(final MoneyWiseAnalysisTransAttr pAttr) {
/* Access value of object */
final Object myValue = getValue(pAttr);
/* Return the value */
return myValue != null
? myValue
: MetisDataFieldValue.SKIP;
}
/**
* Obtain an attribute value.
*
* @param pAttr the attribute
* @return the value of the attribute or null
*/
private Object getValue(final MoneyWiseAnalysisTransAttr pAttr) {
/* Obtain the value */
return theValues.getValue(pAttr);
}
/**
* Adjust counter.
*
* @param pAttr the attribute
* @param pDelta the delta
*/
void adjustCounter(final MoneyWiseAnalysisTransAttr pAttr,
final OceanusMoney pDelta) {
OceanusMoney myValue = theValues.getMoneyValue(pAttr);
myValue = new OceanusMoney(myValue);
myValue.addAmount(pDelta);
setValue(pAttr, myValue);
}
/**
* Add income value.
*
* @param pTrans the transaction helper
* @param pValue the value to add
*/
public void addIncome(final MoneyWiseAnalysisTransactionHelper pTrans,
final OceanusMoney pValue) {
/* Add the expense */
addIncome(pValue);
/* Register the transaction in the history */
registerTransaction(pTrans);
}
/**
* Add income value.
*
* @param pValue the value to add
*/
public void addIncome(final OceanusMoney pValue) {
/* Only adjust on non-zero */
if (pValue.isNonZero()) {
adjustCounter(MoneyWiseAnalysisTransAttr.INCOME, pValue);
}
}
/**
* Subtract income value.
*
* @param pValue the value to subtract
*/
public void subtractIncome(final OceanusMoney pValue) {
/* Only adjust on non-zero */
if (pValue.isNonZero()) {
final OceanusMoney myIncome = new OceanusMoney(pValue);
myIncome.negate();
adjustCounter(MoneyWiseAnalysisTransAttr.INCOME, myIncome);
}
}
/**
* Add expense value.
*
* @param pTrans the transaction helper
* @param pValue the value to add
*/
public void addExpense(final MoneyWiseAnalysisTransactionHelper pTrans,
final OceanusMoney pValue) {
/* Add the expense */
addExpense(pValue);
/* Register the transaction in the history */
registerTransaction(pTrans);
}
/**
* Add expense value.
*
* @param pValue the value to add
*/
public void addExpense(final OceanusMoney pValue) {
/* Only adjust on non-zero */
if (pValue.isNonZero()) {
adjustCounter(MoneyWiseAnalysisTransAttr.EXPENSE, pValue);
}
}
/**
* Subtract expense value.
*
* @param pTrans the transaction causing the expense
* @param pValue the value to subtract
*/
public void subtractExpense(final MoneyWiseAnalysisTransactionHelper pTrans,
final OceanusMoney pValue) {
/* Subtract the expense */
subtractExpense(pValue);
/* Register the event in the history */
registerTransaction(pTrans);
}
/**
* Subtract expense value.
*
* @param pValue the value to subtract
*/
public void subtractExpense(final OceanusMoney pValue) {
/* Only adjust on non-zero */
if (pValue.isNonZero()) {
final OceanusMoney myExpense = new OceanusMoney(pValue);
myExpense.negate();
adjustCounter(MoneyWiseAnalysisTransAttr.EXPENSE, myExpense);
}
}
/**
* Add transaction to totals.
*
* @param pTrans the transaction helper
* @return isIncome true/false
*/
boolean adjustValues(final MoneyWiseAnalysisTransactionHelper pTrans) {
/* Analyse the event */
final MoneyWiseTransCategoryClass myClass = pTrans.getCategoryClass();
MoneyWiseAssetDirection myDir = pTrans.getDirection();
boolean isExpense = myClass.isExpense();
final OceanusMoney myAmount = new OceanusMoney(pTrans.getLocalAmount());
/* If this is an expense */
if (isExpense) {
/* Adjust for TaxCredit */
final OceanusMoney myTaxCredit = pTrans.getTaxCredit();
if (myTaxCredit != null) {
myAmount.addAmount(myTaxCredit);
}
/* if we have a non-zero amount */
if (myAmount.isNonZero()) {
/* If this is a recovered expense */
if (myDir.isFrom()) {
/* Add as income */
adjustCounter(MoneyWiseAnalysisTransAttr.INCOME, myAmount);
isExpense = false;
/* else its standard expense */
} else {
/* Add as expense */
adjustCounter(MoneyWiseAnalysisTransAttr.EXPENSE, myAmount);
}
}
/* else this is an income */
} else {
/* Adjust for TaxCredit */
final OceanusMoney myTaxCredit = pTrans.getTaxCredit();
if (myTaxCredit != null) {
myAmount.addAmount(myTaxCredit);
}
/* Adjust for Withheld */
final OceanusMoney myWithheld = pTrans.getWithheld();
if (myWithheld != null) {
myAmount.addAmount(myWithheld);
}
/* If we need to switch direction */
if (myClass.isSwitchDirection()) {
/* switch the direction */
myDir = myDir.reverse();
}
/* if we have a non-zero amount */
if (myAmount.isNonZero()) {
/* If this is a returned income */
if (myDir.isTo()) {
/* Add as expense */
adjustCounter(MoneyWiseAnalysisTransAttr.EXPENSE, myAmount);
/* else standard income */
} else {
/* Add as income */
adjustCounter(MoneyWiseAnalysisTransAttr.INCOME, myAmount);
}
}
}
/* Register the transaction in the history */
registerTransaction(pTrans);
/* Return the income flag */
return !isExpense;
}
/**
* Calculate Income delta.
*/
void calculateDelta() {
/* Calculate delta for the values */
theValues.calculateDelta();
}
/**
* Adjust to base.
*/
void adjustToBase() {
/* Adjust to base values */
theValues.adjustToBaseValues(theBaseValues);
theBaseValues.resetBaseValues();
}
/**
* Add bucket to totals.
*
* @param pSource the bucket to add
*/
void addValues(final MoneyWiseAnalysisTransCategoryBucket pSource) {
/* Access source values */
final MoneyWiseAnalysisCategoryValues mySource = pSource.getValues();
/* Add income values */
OceanusMoney myValue = theValues.getMoneyValue(MoneyWiseAnalysisTransAttr.INCOME);
OceanusMoney mySrcValue = mySource.getMoneyValue(MoneyWiseAnalysisTransAttr.INCOME);
myValue.addAmount(mySrcValue);
/* Add expense values */
myValue = theValues.getMoneyValue(MoneyWiseAnalysisTransAttr.EXPENSE);
mySrcValue = mySource.getMoneyValue(MoneyWiseAnalysisTransAttr.EXPENSE);
myValue.addAmount(mySrcValue);
}
/**
* Register the transaction.
*
* @param pTrans the transaction helper
*/
void registerTransaction(final MoneyWiseAnalysisTransactionHelper pTrans) {
/* Register the transaction in the history */
theHistory.registerTransaction(pTrans.getTransaction(), theValues);
}
/**
* TransactionCategoryBucket list class.
*/
public static final class MoneyWiseAnalysisTransCategoryBucketList
implements MetisFieldItem, MetisDataList<MoneyWiseAnalysisTransCategoryBucket> {
/**
* Local Report fields.
*/
private static final MetisFieldSet<MoneyWiseAnalysisTransCategoryBucketList> FIELD_DEFS = MetisFieldSet.newFieldSet(MoneyWiseAnalysisTransCategoryBucketList.class);
/*
* Declare Fields.
*/
static {
FIELD_DEFS.declareLocalField(MoneyWiseAnalysisDataResource.ANALYSIS_NAME, MoneyWiseAnalysisTransCategoryBucketList::getAnalysis);
FIELD_DEFS.declareLocalField(MoneyWiseAnalysisDataResource.ANALYSIS_TOTALS, MoneyWiseAnalysisTransCategoryBucketList::getTotals);
}
/**
* The analysis.
*/
private final MoneyWiseAnalysis theAnalysis;
/**
* The list.
*/
private final MetisListIndexed<MoneyWiseAnalysisTransCategoryBucket> theList;
/**
* The editSet.
*/
private final PrometheusEditSet theEditSet;
/**
* The totals.
*/
private final MoneyWiseAnalysisTransCategoryBucket theTotals;
/**
* The TaxBasis.
*/
private final MoneyWiseAnalysisTaxBasisBucketList theTaxBasis;
/**
* The TaxCredit.
*/
private final MoneyWiseAnalysisTransCategoryBucket theTaxCredit;
/**
* The TaxRelief.
*/
private final MoneyWiseAnalysisTransCategoryBucket theTaxRelief;
/**
* The EmployeeNatIns.
*/
private final MoneyWiseAnalysisTransCategoryBucket theEmployeeNatIns;
/**
* The EmployerNatIns.
*/
private final MoneyWiseAnalysisTransCategoryBucket theEmployerNatIns;
/**
* The DeemedBenefit.
*/
private final MoneyWiseAnalysisTransCategoryBucket theDeemedBenefit;
/**
* The Withheld.
*/
private final MoneyWiseAnalysisTransCategoryBucket theWithheld;
/**
* The CapitalGains.
*/
private final MoneyWiseAnalysisTransCategoryBucket theCapitalGains;
/**
* The TaxFreeGains.
*/
private final MoneyWiseAnalysisTransCategoryBucket theTaxFreeGains;
/**
* The ChargeableGains.
*/
private final MoneyWiseAnalysisTransCategoryBucket theChargeableGains;
/**
* Construct a top-level List.
*
* @param pAnalysis the analysis
*/
MoneyWiseAnalysisTransCategoryBucketList(final MoneyWiseAnalysis pAnalysis) {
/* Initialise class */
theAnalysis = pAnalysis;
theEditSet = theAnalysis.getEditSet();
theTotals = allocateTotalsBucket();
theList = new MetisListIndexed<>();
/* Access taxBasis list */
theTaxBasis = theAnalysis.getTaxBasis();
/* Obtain the implied buckets */
theTaxCredit = getEventInfoBucket(MoneyWiseTransInfoClass.TAXCREDIT);
theEmployeeNatIns = getEventInfoBucket(MoneyWiseTransInfoClass.EMPLOYEENATINS);
theEmployerNatIns = getEventInfoBucket(MoneyWiseTransInfoClass.EMPLOYERNATINS);
theDeemedBenefit = getEventInfoBucket(MoneyWiseTransInfoClass.DEEMEDBENEFIT);
theWithheld = getEventInfoBucket(MoneyWiseTransInfoClass.WITHHELD);
theTaxRelief = getEventSingularBucket(MoneyWiseTransCategoryClass.TAXRELIEF);
theChargeableGains = getEventSingularBucket(MoneyWiseTransCategoryClass.CHARGEABLEGAIN);
theTaxFreeGains = getEventSingularBucket(MoneyWiseTransCategoryClass.TAXFREEGAIN);
theCapitalGains = getEventSingularBucket(MoneyWiseTransCategoryClass.CAPITALGAIN);
}
/**
* Construct a dated List.
*
* @param pAnalysis the analysis
* @param pBase the base list
* @param pDate the Date
*/
MoneyWiseAnalysisTransCategoryBucketList(final MoneyWiseAnalysis pAnalysis,
final MoneyWiseAnalysisTransCategoryBucketList pBase,
final OceanusDate pDate) {
/* Initialise class */
theAnalysis = pAnalysis;
theEditSet = theAnalysis.getEditSet();
theTotals = allocateTotalsBucket();
theList = new MetisListIndexed<>();
/* Don't use implied buckets */
theTaxBasis = null;
theTaxCredit = null;
theEmployeeNatIns = null;
theEmployerNatIns = null;
theDeemedBenefit = null;
theWithheld = null;
theTaxRelief = null;
theChargeableGains = null;
theTaxFreeGains = null;
theCapitalGains = null;
/* Loop through the buckets */
final Iterator<MoneyWiseAnalysisTransCategoryBucket> myIterator = pBase.iterator();
while (myIterator.hasNext()) {
final MoneyWiseAnalysisTransCategoryBucket myCurr = myIterator.next();
/* Access the bucket for this date */
final MoneyWiseAnalysisTransCategoryBucket myBucket = new MoneyWiseAnalysisTransCategoryBucket(pAnalysis, myCurr, pDate);
/* If the bucket is non-idle */
if (Boolean.FALSE.equals(myBucket.isIdle())) {
/* Calculate the delta and add to the list */
theList.add(myBucket);
}
}
}
/**
* Construct a ranged List.
*
* @param pAnalysis the analysis
* @param pBase the base list
* @param pRange the Date Range
*/
MoneyWiseAnalysisTransCategoryBucketList(final MoneyWiseAnalysis pAnalysis,
final MoneyWiseAnalysisTransCategoryBucketList pBase,
final OceanusDateRange pRange) {
/* Initialise class */
theAnalysis = pAnalysis;
theEditSet = theAnalysis.getEditSet();
theTotals = allocateTotalsBucket();
theList = new MetisListIndexed<>();
/* Don't use implied buckets */
theTaxBasis = null;
theTaxCredit = null;
theEmployeeNatIns = null;
theEmployerNatIns = null;
theDeemedBenefit = null;
theWithheld = null;
theTaxRelief = null;
theChargeableGains = null;
theTaxFreeGains = null;
theCapitalGains = null;
/* Loop through the buckets */
final Iterator<MoneyWiseAnalysisTransCategoryBucket> myIterator = pBase.iterator();
while (myIterator.hasNext()) {
final MoneyWiseAnalysisTransCategoryBucket myCurr = myIterator.next();
/* Access the bucket for this range */
final MoneyWiseAnalysisTransCategoryBucket myBucket = new MoneyWiseAnalysisTransCategoryBucket(pAnalysis, myCurr, pRange);
/* If the bucket is non-idle */
if (Boolean.FALSE.equals(myBucket.isIdle())) {
/* Adjust to the base */
myBucket.adjustToBase();
theList.add(myBucket);
}
}
}
@Override
public MetisFieldSet<MoneyWiseAnalysisTransCategoryBucketList> getDataFieldSet() {
return FIELD_DEFS;
}
@Override
public List<MoneyWiseAnalysisTransCategoryBucket> getUnderlyingList() {
return theList.getUnderlyingList();
}
@Override
public String formatObject(final OceanusDataFormatter pFormatter) {
return getDataFieldSet().getName();
}
/**
* Obtain the analysis.
*
* @return the analysis
*/
MoneyWiseAnalysis getAnalysis() {
return theAnalysis;
}
/**
* Obtain item by id.
*
* @param pId the id to lookup
* @return the item (or null if not present)
*/
public MoneyWiseAnalysisTransCategoryBucket findItemById(final Integer pId) {
/* Return results */
return theList.getItemById(pId);
}
/**
* Obtain the Totals.
*
* @return the totals bucket
*/
public MoneyWiseAnalysisTransCategoryBucket getTotals() {
return theTotals;
}
/**
* Allocate the Totals TransactionCategoryBucket.
*
* @return the bucket
*/
private MoneyWiseAnalysisTransCategoryBucket allocateTotalsBucket() {
/* Obtain the totals category */
return new MoneyWiseAnalysisTransCategoryBucket(theAnalysis, null);
}
/**
* Obtain the TransactionCategoryBucket for a given transaction infoClass.
*
* @param pClass the transaction infoClass
* @return the bucket
*/
MoneyWiseAnalysisTransCategoryBucket getEventInfoBucket(final MoneyWiseTransInfoClass pClass) {
/* Determine category */
final MoneyWiseTransCategoryList myList = theEditSet.getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class);
final MoneyWiseTransCategory myCategory = myList.getEventInfoCategory(pClass);
/* Access bucket */
return myCategory == null
? null
: getBucket(myCategory);
}
/**
* Obtain the TransactionCategoryBucket for a given transactionClass.
*
* @param pClass the transaction infoClass
* @return the bucket
*/
MoneyWiseAnalysisTransCategoryBucket getEventSingularBucket(final MoneyWiseTransCategoryClass pClass) {
/* Determine category */
final MoneyWiseTransCategoryList myList = theEditSet.getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class);
final MoneyWiseTransCategory myCategory = myList.getSingularClass(pClass);
/* Access bucket */
return myCategory == null
? null
: getBucket(myCategory);
}
/**
* Obtain the TransactionCategoryBucket for a given transaction category.
*
* @param pCategory the transaction category
* @return the bucket
*/
public MoneyWiseAnalysisTransCategoryBucket getBucket(final MoneyWiseTransCategory pCategory) {
/* Handle null category */
if (pCategory == null) {
throw new IllegalArgumentException();
}
/* Locate the bucket in the list */
MoneyWiseAnalysisTransCategoryBucket myItem = findItemById(pCategory.getIndexedId());
/* If the item does not yet exist */
if (myItem == null) {
/* Create the new bucket */
myItem = new MoneyWiseAnalysisTransCategoryBucket(theAnalysis, pCategory);
/* Add to the list */
theList.add(myItem);
}
/* Return the bucket */
return myItem;
}
/**
* Obtain the TransactionCategoryBucket for a given transaction category class.
*
* @param pClass the transaction category class
* @return the bucket
*/
public MoneyWiseAnalysisTransCategoryBucket getBucket(final MoneyWiseTransCategoryClass pClass) {
/* Determine required category */
final MoneyWiseTransCategory myCategory = theEditSet.getDataList(MoneyWiseBasicDataType.TRANSCATEGORY, MoneyWiseTransCategoryList.class)
.getSingularClass(pClass);
/* Return the bucket */
return getBucket(myCategory);
}
/**
* Obtain the matching CategoryBucket.
*
* @param pCategory the category
* @return the matching bucket
*/
public MoneyWiseAnalysisTransCategoryBucket getMatchingCategory(final MoneyWiseTransCategory pCategory) {
/* Return the matching category if it exists else an orphan bucket */
final MoneyWiseAnalysisTransCategoryBucket myCategory = findItemById(pCategory.getIndexedId());
return myCategory != null
? myCategory
: new MoneyWiseAnalysisTransCategoryBucket(theAnalysis, pCategory);
}
/**
* Obtain the default CategoryBucket.
*
* @return the default bucket
*/
public MoneyWiseAnalysisTransCategoryBucket getDefaultCategory() {
/* Return the first category in the list if it exists */
return isEmpty()
? null
: theList.getUnderlyingList().get(0);
}
/**
* Adjust category buckets.
*
* @param pTrans the transaction helper
* @param pCategory primary category
*/
public void adjustCategories(final MoneyWiseAnalysisTransactionHelper pTrans,
final MoneyWiseTransCategory pCategory) {
/* Adjust the primary category bucket */
final MoneyWiseAnalysisTransCategoryBucket myCatBucket = getBucket(pCategory);
/* Adjust for Tax Credit */
OceanusMoney myTaxCredit = pTrans.getTaxCredit();
if (myTaxCredit != null) {
if (pCategory.isCategoryClass(MoneyWiseTransCategoryClass.LOANINTERESTCHARGED)) {
theTaxRelief.addIncome(pTrans, myTaxCredit);
myTaxCredit = new OceanusMoney(myTaxCredit);
myTaxCredit.negate();
theTaxBasis.adjustValue(pTrans, MoneyWiseTaxClass.VIRTUAL, myTaxCredit);
} else {
theTaxCredit.addExpense(pTrans, myTaxCredit);
theTaxBasis.adjustGrossValue(pTrans, MoneyWiseTaxClass.TAXPAID, myTaxCredit);
}
}
/* Adjust for EmployeeNatInsurance */
final boolean isPension = pCategory.isCategoryClass(MoneyWiseTransCategoryClass.PENSIONCONTRIB);
OceanusMoney myNatIns = pTrans.getEmployeeNatIns();
if (myNatIns != null && myNatIns.isNonZero()) {
if (!isPension) {
theEmployeeNatIns.addIncome(pTrans, myNatIns);
} else {
myCatBucket.addIncome(myNatIns);
}
myNatIns = new OceanusMoney(myNatIns);
myNatIns.negate();
theTaxBasis.adjustNettValue(pTrans, MoneyWiseTaxClass.VIRTUAL, myNatIns);
}
/* Adjust for EmployerNatInsurance */
myNatIns = pTrans.getEmployerNatIns();
if (myNatIns != null) {
if (!isPension) {
theEmployerNatIns.addIncome(pTrans, myNatIns);
} else {
myCatBucket.addIncome(myNatIns);
}
theTaxBasis.adjustValue(pTrans, MoneyWiseTaxClass.TAXFREE, myNatIns);
}
/* Adjust for DeemedBenefit */
final OceanusMoney myBenefit = pTrans.getDeemedBenefit();
if (myBenefit != null) {
theDeemedBenefit.addIncome(pTrans, myBenefit);
theWithheld.addExpense(pTrans, myBenefit);
theTaxBasis.adjustGrossValue(pTrans, MoneyWiseTaxClass.VIRTUAL, myBenefit);
}
/* Adjust for Withheld */
final OceanusMoney myWithheld = pTrans.getWithheld();
if (myWithheld != null) {
theWithheld.addExpense(pTrans, myWithheld);
theTaxBasis.adjustValue(pTrans, MoneyWiseTaxClass.VIRTUAL, myWithheld);
}
/* Adjust the category bucket */
myCatBucket.adjustValues(pTrans);
/* Adjust tax basis */
theTaxBasis.adjustBasis(pTrans, pCategory);
}
/**
* Adjust for Standard Gains.
*
* @param pTrans the transaction helper
* @param pSource the source security holding
* @param pGains the gains
*/
public void adjustStandardGain(final MoneyWiseAnalysisTransactionHelper pTrans,
final MoneyWiseSecurityHolding pSource,
final OceanusMoney pGains) {
/* Access security and portfolio */
final MoneyWiseSecurity mySecurity = pSource.getSecurity();
final MoneyWisePortfolio myPortfolio = pSource.getPortfolio();
final boolean bTaxFreeGains = myPortfolio.isTaxFree()
|| !mySecurity.getCategoryClass().isCapitalGains();
final MoneyWiseAnalysisTransCategoryBucket myCategory = bTaxFreeGains
? theTaxFreeGains
: theCapitalGains;
final MoneyWiseTaxClass myTaxBasis = bTaxFreeGains
? MoneyWiseTaxClass.TAXFREE
: MoneyWiseTaxClass.CAPITALGAINS;
/* Add to Capital Gains income/expense */
if (pGains.isPositive()) {
myCategory.addIncome(pTrans, pGains);
} else {
myCategory.subtractExpense(pTrans, pGains);
}
theTaxBasis.adjustValue(pTrans, myTaxBasis, pGains);
}
/**
* Adjust for Chargeable Gains.
*
* @param pTrans the transaction helper
* @param pReduction the income reduction
*/
public void adjustChargeableGain(final MoneyWiseAnalysisTransactionHelper pTrans,
final OceanusMoney pReduction) {
/* Adjust Taxable Gains */
theChargeableGains.subtractIncome(pReduction);
theChargeableGains.adjustValues(pTrans);
/* Obtain normalised value */
final OceanusMoney myGains = new OceanusMoney(pTrans.getLocalAmount());
myGains.subtractAmount(pReduction);
/* Adjust for Tax Credit */
OceanusMoney myTaxCredit = pTrans.getTaxCredit();
if (myTaxCredit != null) {
/* Adjust the taxCredit category */
theTaxCredit.addExpense(pTrans, myTaxCredit);
/* Adjust tax basis */
theTaxBasis.adjustGrossValue(pTrans, MoneyWiseTaxClass.TAXPAID, myTaxCredit);
myTaxCredit = new OceanusMoney(myTaxCredit);
myTaxCredit.negate();
theTaxBasis.adjustGrossValue(pTrans, MoneyWiseTaxClass.VIRTUAL, myTaxCredit);
}
/* Adjust tax basis */
theTaxBasis.adjustValue(pTrans, MoneyWiseTaxClass.CHARGEABLEGAINS, myGains);
}
/**
* Produce totals for the TransactionCategories.
*/
void produceTotals() {
/* Create a list of new buckets */
final MetisListIndexed<MoneyWiseAnalysisTransCategoryBucket> myTotals = new MetisListIndexed<>();
/* Loop through the buckets */
Iterator<MoneyWiseAnalysisTransCategoryBucket> myIterator = iterator();
while (myIterator.hasNext()) {
final MoneyWiseAnalysisTransCategoryBucket myCurr = myIterator.next();
/* Obtain category and parent category */
final MoneyWiseTransCategory myCategory = myCurr.getTransactionCategory();
final MoneyWiseTransCategory myParent = myCategory.getParentCategory();
/* Access parent bucket */
MoneyWiseAnalysisTransCategoryBucket myTotal = findItemById(myParent.getIndexedId());
/* Calculate the delta */
myCurr.calculateDelta();
/* If the bucket does not exist */
if (myTotal == null) {
/* Look for bucket in the new list */
myTotal = myTotals.getItemById(myParent.getIndexedId());
/* If the bucket is completely new */
if (myTotal == null) {
/* Create the new bucket and add to new list */
myTotal = new MoneyWiseAnalysisTransCategoryBucket(theAnalysis, myParent);
myTotals.add(myTotal);
}
}
/* Add to totals bucket */
myTotal.addValues(myCurr);
theTotals.addValues(myCurr);
}
/* Loop through the new totals */
myIterator = myTotals.listIterator();
while (myIterator.hasNext()) {
final MoneyWiseAnalysisTransCategoryBucket myCurr = myIterator.next();
/* Calculate delta for the category total */
myCurr.calculateDelta();
/* Add it to the list */
theList.add(myCurr);
}
/* Sort the list */
theList.getUnderlyingList().sort(Comparator.comparing(MoneyWiseAnalysisTransCategoryBucket::getTransactionCategory));
/* Calculate delta for the totals */
if (theTotals != null) {
theTotals.calculateDelta();
}
}
}
}