MoneyWiseArchiveTransaction.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.archive;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.oceanus.date.OceanusDate;
import io.github.tonywasher.joceanus.oceanus.profile.OceanusProfile;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseDataSet;
import io.github.tonywasher.joceanus.moneywise.data.basic.MoneyWiseTransInfo.MoneyWiseTransInfoList;
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.MoneyWiseTransInfoClass;
import io.github.tonywasher.joceanus.moneywise.exc.MoneyWiseIOException;
import io.github.tonywasher.joceanus.prometheus.service.sheet.PrometheusSheetCell;
import io.github.tonywasher.joceanus.prometheus.service.sheet.PrometheusSheetRow;
import io.github.tonywasher.joceanus.prometheus.service.sheet.PrometheusSheetView;
import io.github.tonywasher.joceanus.prometheus.service.sheet.PrometheusSheetWorkBook;
import io.github.tonywasher.joceanus.tethys.api.base.TethysUIConstant;
import io.github.tonywasher.joceanus.tethys.api.thread.TethysUIThreadCancelException;
import io.github.tonywasher.joceanus.tethys.api.thread.TethysUIThreadStatusReport;

import java.util.ListIterator;

/**
 * ArchiveLoader for Transaction.
 *
 * @author Tony Washer
 */
public final class MoneyWiseArchiveTransaction {
    /**
     * NamedArea for Transactions.
     */
    private static final String AREA_TRANS = MoneyWiseTransaction.LIST_NAME;

    /**
     * Report processor.
     */
    private final TethysUIThreadStatusReport theReport;

    /**
     * Workbook.
     */
    private final PrometheusSheetWorkBook theWorkBook;

    /**
     * DataSet.
     */
    private final MoneyWiseDataSet theData;

    /**
     * Cache.
     */
    private final MoneyWiseArchiveCache theCache;

    /**
     * Constructor.
     *
     * @param pReport   the report
     * @param pWorkBook the workbook
     * @param pData     the data set to load into
     * @param pCache    the cache
     */
    MoneyWiseArchiveTransaction(final TethysUIThreadStatusReport pReport,
                                final PrometheusSheetWorkBook pWorkBook,
                                final MoneyWiseDataSet pData,
                                final MoneyWiseArchiveCache pCache) {
        theReport = pReport;
        theWorkBook = pWorkBook;
        theData = pData;
        theCache = pCache;
    }

    /**
     * Load the ExchangeRates from an archive.
     *
     * @param pStage the stage
     * @throws OceanusException on error
     */
    void loadArchive(final OceanusProfile pStage) throws OceanusException {
        /* Access the list of transactions */
        pStage.startTask(AREA_TRANS);
        final MoneyWiseTransactionList myList = theData.getTransactions();
        final MoneyWiseTransInfoList myInfoList = theData.getTransactionInfo();

        /* Protect against exceptions */
        try {
            /* Obtain the range iterator */
            final ListIterator<MoneyWiseArchiveYear> myIterator = theCache.reverseIterator();

            /* Loop through the individual year ranges */
            while (myIterator.hasPrevious()) {
                /* Access year */
                final MoneyWiseArchiveYear myYear = myIterator.previous();

                /* Find the range of cells */
                final PrometheusSheetView myView = theWorkBook.getRangeView(myYear.getRangeName());

                /* Declare the new stage */
                theReport.setNewStage("Events from " + myYear.getDate().getYear());

                /* Count the number of Transactions */
                final int myTotal = myView.getRowCount();

                /* Declare the number of steps */
                theReport.setNumSteps(myTotal);

                /* Loop through the rows of the table */
                for (int i = 0; i < myTotal; i++) {
                    /* Access the row */
                    final PrometheusSheetRow myRow = myView.getRowByIndex(i);

                    /* Process transaction and break loop if requested */
                    if (!processTransaction(myView, myRow)) {
                        break;
                    }

                    /* Report the progress */
                    theReport.setNextStep();
                }

                /* If we have finished */
                if (!theCache.checkDate(myYear.getDate())) {
                    /* Break the loop */
                    break;
                }
            }

            /* Resolve ValueLinks */
            myInfoList.resolveValueLinks();

            /* Sort the list */
            myList.resolveDataSetLinks();
            myList.reSort();

            /* Validate the list */
            myList.validateOnLoad();

            /* Handle Exceptions */
        } catch (TethysUIThreadCancelException e) {
            throw e;
        } catch (OceanusException e) {
            throw new MoneyWiseIOException("Failed to load " + myList.getItemType().getListName(), e);
        }
    }

    /**
     * Process transaction row from archive.
     *
     * @param pView the spreadsheet view
     * @param pRow  the spreadsheet row
     * @return continue true/false
     * @throws OceanusException on error
     */
    private boolean processTransaction(final PrometheusSheetView pView,
                                       final PrometheusSheetRow pRow) throws OceanusException {
        /* Access cache */
        int iAdjust = -1;

        /* Access date */
        PrometheusSheetCell myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        final OceanusDate myDate = (myCell != null)
                ? myCell.getDate()
                : null;

        /* Access the values */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        final String myDebit = (myCell != null)
                ? myCell.getString()
                : null;
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        final String myCredit = (myCell != null)
                ? myCell.getString()
                : null;
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        final String myAmount = (myCell != null)
                ? myCell.getString()
                : null;
        final String myCategory = pView.getRowCellByIndex(pRow, ++iAdjust).getString();

        /* Handle Reconciled which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        Boolean myReconciled = Boolean.FALSE;
        if (myCell != null) {
            myReconciled = Boolean.TRUE;
        }

        /* Set defaults */
        if (!theCache.resolveValues(myDate, myDebit, myCredit, myCategory)) {
            return false;
        }

        /* Build transaction */
        final MoneyWiseTransaction myTrans = theCache.buildTransaction(myAmount, myReconciled);
        if (myTrans == null) {
            return true;
        }

        /* Process TransactionInfo */
        final int myLast = pRow.getMaxValuedCellIndex();
        if (iAdjust < myLast) {
            processTransInfo(pView, pRow, myTrans, iAdjust);
        }

        /* Continue */
        return true;
    }

    /**
     * Process transaction row from archive.
     *
     * @param pView   the spreadsheet view
     * @param pRow    the spreadsheet row
     * @param pTrans  the transaction
     * @param pAdjust the cell#
     * @throws OceanusException on error
     */
    private void processTransInfo(final PrometheusSheetView pView,
                                  final PrometheusSheetRow pRow,
                                  final MoneyWiseTransaction pTrans,
                                  final int pAdjust) throws OceanusException {
        /* Handle Description which may be missing */
        int iAdjust = pAdjust;
        PrometheusSheetCell myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myDesc = null;
        if (myCell != null) {
            myDesc = myCell.getString();
        }

        /* Handle Tax Credit which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myTaxCredit = null;
        if (myCell != null) {
            myTaxCredit = myCell.getString();
        }

        /* Handle EmployeeNatIns which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myEmployeeNatIns = null;
        if (myCell != null) {
            myEmployeeNatIns = myCell.getString();
        }

        /* Handle EmployerNatIns which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myEmployerNatIns = null;
        if (myCell != null) {
            myEmployerNatIns = myCell.getString();
        }

        /* Handle Benefit which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myBenefit = null;
        if (myCell != null) {
            myBenefit = myCell.getString();
        }

        /* Handle DebitUnits which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myDebitUnits = null;
        if (myCell != null) {
            myDebitUnits = myCell.getString();
        }

        /* Handle CreditUnits which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myCreditUnits = null;
        if (myCell != null) {
            myCreditUnits = myCell.getString();
        }

        /* Handle Dilution which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myDilution = null;
        if (myCell != null) {
            myDilution = myCell.getString();
        }

        /* Handle Reference which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myReference = null;
        if (myCell != null) {
            myReference = myCell.getString();
        }

        /* Handle Years which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        Integer myYears = null;
        if (myCell != null) {
            myYears = myCell.getInteger();
        }

        /* Handle Withheld which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myWithheld = null;
        if (myCell != null) {
            myWithheld = myCell.getString();
        }

        /* Handle ReturnedCashAccount which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myReturnedCashAccount = null;
        if (myCell != null) {
            myReturnedCashAccount = myCell.getString();
        }

        /* Handle ReturnedCash which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myReturnedCash = null;
        if (myCell != null) {
            myReturnedCash = myCell.getString();
        }

        /* Handle PartnerAmount which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myPartnerAmount = null;
        if (myCell != null) {
            myPartnerAmount = myCell.getString();
        }

        /* Handle XchangeRate which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myXchangeRate = null;
        if (myCell != null) {
            myXchangeRate = myCell.getString();
        }

        /* Handle TagList which may be missing */
        myCell = pView.getRowCellByIndex(pRow, ++iAdjust);
        String myTagList = null;
        if (myCell != null) {
            myTagList = myCell.getString();
        }

        /* If the debit was reversed */
        if (theCache.isDebitReversed()) {
            /* Flip the Debit and credit values */
            final String myTemp = myDebitUnits;
            myDebitUnits = myCreditUnits;
            myCreditUnits = myTemp;
            if (myCreditUnits != null) {
                myCreditUnits = "-" + myCreditUnits;
            }
        } else if (myDebitUnits != null) {
            myDebitUnits = "-" + myDebitUnits;
        } else if (theCache.isRecursive()) {
            myDebitUnits = myCreditUnits;
            myCreditUnits = null;
        }

        /* Add information relating to the account */
        final MoneyWiseTransInfoList myInfoList = theData.getTransactionInfo();
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.COMMENTS, myDesc);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.TAXCREDIT, myTaxCredit);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.EMPLOYEENATINS, myEmployeeNatIns);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.EMPLOYERNATINS, myEmployerNatIns);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.DEEMEDBENEFIT, myBenefit);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.ACCOUNTDELTAUNITS, myDebitUnits);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.PARTNERDELTAUNITS, myCreditUnits);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.DILUTION, myDilution);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.REFERENCE, myReference);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.QUALIFYYEARS, myYears);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.WITHHELD, myWithheld);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.RETURNEDCASHACCOUNT, myReturnedCashAccount);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.RETURNEDCASH, myReturnedCash);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.PARTNERAMOUNT, myPartnerAmount);
        myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.XCHANGERATE, myXchangeRate);

        /* If we have a TagList */
        if (myTagList != null) {
            /* Process any separated items */
            int iIndex = myTagList.indexOf(TethysUIConstant.LIST_SEP);
            while (iIndex != -1) {
                myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.TRANSTAG, myTagList.substring(0, iIndex));
                myTagList = myTagList.substring(iIndex + 1);
                iIndex = myTagList.indexOf(TethysUIConstant.LIST_SEP);
            }

            /* Process the single remaining item */
            myInfoList.addInfoItem(null, pTrans, MoneyWiseTransInfoClass.TRANSTAG, myTagList);
        }
    }
}