diff --git a/kmymoney/mymoney/mymoneyenums.h b/kmymoney/mymoney/mymoneyenums.h index 2e50b89f4..90db99faa 100644 --- a/kmymoney/mymoney/mymoneyenums.h +++ b/kmymoney/mymoney/mymoneyenums.h @@ -1,420 +1,421 @@ /* * Copyright 2017-2018 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MYMONEYENUMS_H #define MYMONEYENUMS_H #include namespace eMyMoney { /** * Account types currently supported. */ namespace Account { enum class Type { Unknown = 0, /**< For error handling */ Checkings, /**< Standard checking account */ Savings, /**< Typical savings account */ Cash, /**< Denotes a shoe-box or pillowcase stuffed with cash */ CreditCard, /**< Credit card accounts */ Loan, /**< Loan and mortgage accounts (liability) */ CertificateDep, /**< Certificates of Deposit */ Investment, /**< Investment account */ MoneyMarket, /**< Money Market Account */ Asset, /**< Denotes a generic asset account.*/ Liability, /**< Denotes a generic liability account.*/ Currency, /**< Denotes a currency trading account. */ Income, /**< Denotes an income account */ Expense, /**< Denotes an expense account */ AssetLoan, /**< Denotes a loan (asset of the owner of this object) */ Stock, /**< Denotes an security account as sub-account for an investment */ Equity, /**< Denotes an equity account e.g. opening/closeing balance */ /* insert new account types above this line */ MaxAccountTypes /**< Denotes the number of different account types */ }; inline uint qHash(const Type key, uint seed) { return ::qHash(static_cast(key), seed); } enum class Standard { Liability, Asset, Expense, Income, Equity }; inline uint qHash(const Standard key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace Payee { enum class MatchType { Disabled = 0, Name, Key, NameExact }; } namespace Security { enum class Type { Stock, MutualFund, Bond, Currency, None }; inline uint qHash(const Type key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace Report { enum class RowType { NoRows = 0, AssetLiability, ExpenseIncome, Category, TopCategory, Account, Tag, Payee, Month, Week, TopAccount, AccountByTopAccount, EquityType, AccountType, Institution, Budget, BudgetActual, Schedule, AccountInfo, AccountLoanInfo, AccountReconcile, CashFlow, Invalid }; inline uint qHash(const RowType key, uint seed) { return ::qHash(static_cast(key), seed); } enum class ColumnType { NoColumns = 0, Days = 1, Months = 1, BiMonths = 2, Quarters = 3, Weeks = 7, Years = 12, Invalid }; inline uint qHash(const ColumnType key, uint seed) { return ::qHash(static_cast(key), seed); } enum class ReportType { NoReport = 0, PivotTable, QueryTable, InfoTable, Invalid }; // if you add bits to this bitmask, start with the value currently assigned to QCend and update its value afterwards // also don't forget to add column names to kQueryColumnsText in mymoneyreport.cpp enum QueryColumn : int { None = 0x0, Begin = 0x1, Number = 0x1, Payee = 0x2, Category = 0x4, Tag = 0x8, Memo = 0x10, Account = 0x20, Reconciled = 0x40, Action = 0x80, Shares = 0x100, Price = 0x200, Performance = 0x400, Loan = 0x800, Balance = 0x1000, CapitalGain = 0x2000, End = 0x4000 }; enum class DetailLevel { None = 0, All, Top, Group, Total, End }; inline uint qHash(const DetailLevel key, uint seed) { return ::qHash(static_cast(key), seed); } enum class InvestmentSum { Period = 0, OwnedAndSold, Owned, Sold, Bought}; enum class ChartType { None = 0, Line, Bar, Pie, Ring, StackedBar, End }; inline uint qHash(const ChartType key, uint seed) { return ::qHash(static_cast(key), seed); } enum class DataLock { Automatic = 0, UserDefined, DataOptionCount }; inline uint qHash(const DataLock key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace Schedule { /** * This enum is used to describe all the possible schedule frequencies. * The special entry, Any, is used to combine all the other types. */ enum class Occurrence { Any = 0, Once = 1, Daily = 2, Weekly = 4, Fortnightly = 8, EveryOtherWeek = 16, EveryHalfMonth = 18, EveryThreeWeeks = 20, EveryThirtyDays = 30, Monthly = 32, EveryFourWeeks = 64, EveryEightWeeks = 126, EveryOtherMonth = 128, EveryThreeMonths = 256, TwiceYearly = 1024, EveryOtherYear = 2048, Quarterly = 4096, EveryFourMonths = 8192, Yearly = 16384 }; /** * This enum is used to describe the schedule type. */ enum class Type { Any = 0, Bill = 1, Deposit = 2, Transfer = 4, LoanPayment = 5 }; /** * This enum is used to describe the schedule's payment type. */ enum class PaymentType { Any = 0, DirectDebit = 1, DirectDeposit = 2, ManualDeposit = 4, Other = 8, WriteChecque = 16, StandingOrder = 32, BankTransfer = 64 }; /** * This enum is used by the auto-commit functionality. * * Depending upon the value of m_weekendOption the schedule can * be entered on a different date **/ enum class WeekendOption { MoveBefore = 0, MoveAfter = 1, MoveNothing = 2 }; } namespace Budget { enum class Level { None = 0, Monthly, MonthByMonth, Yearly, Max }; inline uint qHash(const Level key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace TransactionFilter { // Make sure to keep the following enum valus in sync with the values // used by the GUI (for KMyMoney in kfindtransactiondlgdecl.ui) enum class Type { All = 0, Payments, Deposits, Transfers, // insert new constants above of this line LastType }; // Make sure to keep the following enum valus in sync with the values // used by the GUI (for KMyMoney in kfindtransactiondlgdecl.ui) enum class State { All = 0, NotReconciled, Cleared, Reconciled, Frozen, // insert new constants above of this line LastState }; // Make sure to keep the following enum valus in sync with the values // used by the GUI (for KMyMoney in kfindtransactiondlgdecl.ui) enum class Validity { Any = 0, Valid, Invalid, // insert new constants above of this line LastValidity }; // Make sure to keep the following enum valus in sync with the values // used by the GUI (for KMyMoney in kfindtransactiondlgdecl.ui) enum class Date { All = 0, AsOfToday, CurrentMonth, CurrentYear, MonthToDate, YearToDate, YearToMonth, LastMonth, LastYear, Last7Days, Last30Days, Last3Months, Last6Months, Last12Months, Next7Days, Next30Days, Next3Months, Next6Months, Next12Months, UserDefined, Last3ToNext3Months, Last11Months, CurrentQuarter, LastQuarter, NextQuarter, CurrentFiscalYear, LastFiscalYear, Today, Next18Months, // insert new constants above of this line LastDateItem }; + inline uint qHash(const Date key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace Split { /** * This enum defines the possible reconciliation states a split * can be in. Possible values are as follows: * * @li NotReconciled * @li Cleared * @li Reconciled * @li Frozen * * Whenever a new split is created, it has the status NotReconciled. It * can be set to cleared when the transaction has been performed. Once the * account is reconciled, cleared splits will be set to Reconciled. The * state Frozen will be used, when the concept of books is introduced into * the engine and a split must not be changed anymore. */ enum class State { Unknown = -1, NotReconciled = 0, Cleared, Reconciled, Frozen, // insert new values above MaxReconcileState }; enum class InvestmentTransactionType { UnknownTransactionType = -1, BuyShares = 0, SellShares, Dividend, ReinvestDividend, Yield, AddShares, RemoveShares, SplitShares, InterestIncome/// }; inline uint qHash(const InvestmentTransactionType key, uint seed) { return ::qHash(static_cast(key), seed); } enum class Action { Check, Deposit, Transfer, Withdrawal, ATM, Amortization, Interest, BuyShares, Dividend, ReinvestDividend, Yield, AddShares, SplitShares, InterestIncome }; inline uint qHash(const Action key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace File { /** * notificationObject identifies the type of the object * for which this notification is stored */ enum class Object { Account = 1, Institution, Payee, Transaction, Tag, Schedule, Security, OnlineJob, CostCenter }; /** * notificationMode identifies the type of notifiation * (add, modify, remove) */ enum class Mode { Add = 1, Modify, Remove }; } /** * @brief Type of message * * An usually it is not easy to categorise log messages. This description is only a hint. */ namespace OnlineJob { enum class MessageType { Debug, /**< Just for debug purposes. In normal scenarios the user should not see this. No need to store this message. Plugins should not create them at all if debug mode is not enabled. */ Log, /**< A piece of information the user should not see during normal operation. It is not shown in any UI by default. It is stored persistantly. */ Information, /**< Information that should be kept but without the need to burden the user. The user can see this during normal operation. */ Warning, /**< A piece of information the user should see but not be enforced to do so (= no modal dialog). E.g. a task is expected to have direct effect but insted you have to wait a day (and that is commen behavior). */ Error /**< Important for the user - he must be warned. E.g. a task could unexpectedly not be executed */ }; /** * @brief The state of a job given by the onlinePlugin */ enum class sendingState { noBankAnswer, /**< Used during or before sending or if sendDate().isValid() the job was successfully sent */ acceptedByBank, /**< bank definetly confirmed the job */ rejectedByBank, /**< bank definetly rejected this job */ abortedByUser, /**< aborted by user during sending */ sendingError /**< an error occurred, the job is certainly not executed by the bank */ }; } namespace Statement { enum class Type { None = 0, Checkings, Savings, Investment, CreditCard, Invalid }; inline uint qHash(const Type key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace Transaction { // the following members are only used for investment accounts (m_eType==etInvestment) // eaNone means the action, shares, and security can be ignored. enum class Action { None = 0, Buy, Sell, ReinvestDividend, CashDividend, Shrsin, Shrsout, Stksplit, Fees, Interest, Invalid }; inline uint qHash(const Action key, uint seed) { return ::qHash(static_cast(key), seed); } } namespace Money { enum signPosition : int { // keep those in sync with the ones defined in klocale.h ParensAround = 0, BeforeQuantityMoney = 1, AfterQuantityMoney = 2, BeforeMoney = 3, AfterMoney = 4 }; } } #endif diff --git a/kmymoney/mymoney/mymoneyreport.cpp b/kmymoney/mymoney/mymoneyreport.cpp index 31f3eba38..dabde7d91 100644 --- a/kmymoney/mymoney/mymoneyreport.cpp +++ b/kmymoney/mymoney/mymoneyreport.cpp @@ -1,1003 +1,1020 @@ /* * Copyright 2004-2006 Ace Jones * Copyright 2006 Darren Gould * Copyright 2007-2010 Alvaro Soliverez * Copyright 2017-2018 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "mymoneyreport_p.h" // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneymoney.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneytransaction.h" #include "mymoneytransactionfilter.h" #include "mymoneyexception.h" MyMoneyReport::MyMoneyReport() : MyMoneyObject(*new MyMoneyReportPrivate) { } MyMoneyReport::MyMoneyReport(const QString &id) : MyMoneyObject(*new MyMoneyReportPrivate, id) { } MyMoneyReport::MyMoneyReport(eMyMoney::Report::RowType rt, unsigned ct, eMyMoney::TransactionFilter::Date dl, eMyMoney::Report::DetailLevel ss, const QString& name, const QString& comment) : MyMoneyObject(*new MyMoneyReportPrivate) { Q_D(MyMoneyReport); d->m_name = name; d->m_comment = comment; d->m_detailLevel = ss; d->m_investmentSum = ct & eMyMoney::Report::QueryColumn::CapitalGain ? eMyMoney::Report::InvestmentSum::Sold : eMyMoney::Report::InvestmentSum::Period; d->m_reportType = d->rowTypeToReportType(rt); d->m_rowType = rt; d->m_dateLock = dl; //set report type if (d->m_reportType == eMyMoney::Report::ReportType::PivotTable) d->m_columnType = static_cast(ct); if (d->m_reportType == eMyMoney::Report::ReportType::QueryTable) d->m_queryColumns = static_cast(ct); setDateFilter(dl); //throw exception if the type is inconsistent if (d->rowTypeToReportType(rt) == eMyMoney::Report::ReportType::Invalid || d->m_reportType == eMyMoney::Report::ReportType::NoReport) throw MYMONEYEXCEPTION_CSTRING("Invalid report type"); //add the corresponding account groups - if (rt == eMyMoney::Report::RowType::AssetLiability) { - addAccountGroup(eMyMoney::Account::Type::Asset); - addAccountGroup(eMyMoney::Account::Type::Liability); - d->m_showRowTotals = true; - } - if (rt == eMyMoney::Report::RowType::Account) { - addAccountGroup(eMyMoney::Account::Type::Asset); - addAccountGroup(eMyMoney::Account::Type::AssetLoan); - addAccountGroup(eMyMoney::Account::Type::Cash); - addAccountGroup(eMyMoney::Account::Type::Checkings); - addAccountGroup(eMyMoney::Account::Type::CreditCard); - if (m_expertMode) - addAccountGroup(eMyMoney::Account::Type::Equity); - addAccountGroup(eMyMoney::Account::Type::Expense); - addAccountGroup(eMyMoney::Account::Type::Income); - addAccountGroup(eMyMoney::Account::Type::Liability); - addAccountGroup(eMyMoney::Account::Type::Loan); - addAccountGroup(eMyMoney::Account::Type::Savings); - addAccountGroup(eMyMoney::Account::Type::Stock); - d->m_showRowTotals = true; - } - if (rt == eMyMoney::Report::RowType::ExpenseIncome) { - addAccountGroup(eMyMoney::Account::Type::Expense); - addAccountGroup(eMyMoney::Account::Type::Income); - d->m_showRowTotals = true; - } - //FIXME take this out once we have sorted out all issues regarding budget of assets and liabilities -- asoliverez@gmail.com - if (rt == eMyMoney::Report::RowType::Budget || rt == eMyMoney::Report::RowType::BudgetActual) { - addAccountGroup(eMyMoney::Account::Type::Expense); - addAccountGroup(eMyMoney::Account::Type::Income); - } - if (rt == eMyMoney::Report::RowType::AccountInfo) { - addAccountGroup(eMyMoney::Account::Type::Asset); - addAccountGroup(eMyMoney::Account::Type::Liability); - } - //cash flow reports show splits for all account groups - if (rt == eMyMoney::Report::RowType::CashFlow) { - addAccountGroup(eMyMoney::Account::Type::Expense); - addAccountGroup(eMyMoney::Account::Type::Income); - addAccountGroup(eMyMoney::Account::Type::Asset); - addAccountGroup(eMyMoney::Account::Type::Liability); + addAccountGroupsByRowType(rt); + switch(rt) { + case eMyMoney::Report::RowType::AssetLiability: + case eMyMoney::Report::RowType::Account: + case eMyMoney::Report::RowType::ExpenseIncome: + d->m_showRowTotals = true; + break; + default: + break; } + #ifdef DEBUG_REPORTS QDebug out = qDebug(); out << _name << toString(_rt) << toString(m_reportType); foreach(const eMyMoney::Account::Type accountType, m_accountGroups) out << MyMoneyeMyMoney::Account::accountTypeToString(accountType); if (m_accounts.size() > 0) out << m_accounts; #endif } MyMoneyReport::MyMoneyReport(const MyMoneyReport& other) : MyMoneyObject(*new MyMoneyReportPrivate(*other.d_func()), other.id()), MyMoneyTransactionFilter(other) { } MyMoneyReport::MyMoneyReport(const QString& id, const MyMoneyReport& other) : MyMoneyObject(*new MyMoneyReportPrivate(*other.d_func()), id), MyMoneyTransactionFilter(other) { Q_D(MyMoneyReport); d->m_movingAverageDays = 0; d->m_currentDateColumn = 0; } MyMoneyReport::~MyMoneyReport() { } +void MyMoneyReport::addAccountGroupsByRowType(eMyMoney::Report::RowType rt) +{ + //add the corresponding account groups + switch(rt) { + case eMyMoney::Report::RowType::AccountInfo: + case eMyMoney::Report::RowType::AssetLiability: + addAccountGroup(eMyMoney::Account::Type::Asset); + addAccountGroup(eMyMoney::Account::Type::Liability); + break; + + case eMyMoney::Report::RowType::Account: + addAccountGroup(eMyMoney::Account::Type::Asset); + addAccountGroup(eMyMoney::Account::Type::AssetLoan); + addAccountGroup(eMyMoney::Account::Type::Cash); + addAccountGroup(eMyMoney::Account::Type::Checkings); + addAccountGroup(eMyMoney::Account::Type::CreditCard); + if (m_expertMode) + addAccountGroup(eMyMoney::Account::Type::Equity); + addAccountGroup(eMyMoney::Account::Type::Expense); + addAccountGroup(eMyMoney::Account::Type::Income); + addAccountGroup(eMyMoney::Account::Type::Liability); + addAccountGroup(eMyMoney::Account::Type::Loan); + addAccountGroup(eMyMoney::Account::Type::Savings); + addAccountGroup(eMyMoney::Account::Type::Stock); + break; + + case eMyMoney::Report::RowType::ExpenseIncome: + addAccountGroup(eMyMoney::Account::Type::Expense); + addAccountGroup(eMyMoney::Account::Type::Income); + break; + + //FIXME take this out once we have sorted out all issues regarding budget of assets and liabilities -- asoliverez@gmail.com + case eMyMoney::Report::RowType::Budget: + case eMyMoney::Report::RowType::BudgetActual: + addAccountGroup(eMyMoney::Account::Type::Expense); + addAccountGroup(eMyMoney::Account::Type::Income); + break; + + //cash flow reports show splits for all account groups + case eMyMoney::Report::RowType::CashFlow: + addAccountGroup(eMyMoney::Account::Type::Expense); + addAccountGroup(eMyMoney::Account::Type::Income); + addAccountGroup(eMyMoney::Account::Type::Asset); + addAccountGroup(eMyMoney::Account::Type::Liability); + break; + default: + break; + } +} + eMyMoney::Report::ReportType MyMoneyReport::reportType() const { Q_D(const MyMoneyReport); return d->m_reportType; } void MyMoneyReport::setReportType(eMyMoney::Report::ReportType rt) { Q_D(MyMoneyReport); d->m_reportType = rt; } QString MyMoneyReport::name() const { Q_D(const MyMoneyReport); return d->m_name; } void MyMoneyReport::setName(const QString& s) { Q_D(MyMoneyReport); d->m_name = s; } bool MyMoneyReport::isShowingRowTotals() const { Q_D(const MyMoneyReport); return (d->m_showRowTotals); } void MyMoneyReport::setShowingRowTotals(bool f) { Q_D(MyMoneyReport); d->m_showRowTotals = f; } bool MyMoneyReport::isShowingColumnTotals() const { Q_D(const MyMoneyReport); return d->m_showColumnTotals; } void MyMoneyReport::setShowingColumnTotals(bool f) { Q_D(MyMoneyReport); d->m_showColumnTotals = f; } eMyMoney::Report::RowType MyMoneyReport::rowType() const { Q_D(const MyMoneyReport); return d->m_rowType; } void MyMoneyReport::setRowType(eMyMoney::Report::RowType rt) { Q_D(MyMoneyReport); d->m_rowType = rt; d->m_reportType = d->rowTypeToReportType(rt); d->m_accountGroupFilter = false; d->m_accountGroups.clear(); - if (rt == eMyMoney::Report::RowType::AssetLiability) { - addAccountGroup(eMyMoney::Account::Type::Asset); - addAccountGroup(eMyMoney::Account::Type::Liability); - } - if (rt == eMyMoney::Report::RowType::ExpenseIncome) { - addAccountGroup(eMyMoney::Account::Type::Expense); - addAccountGroup(eMyMoney::Account::Type::Income); - } + addAccountGroupsByRowType(rt); } bool MyMoneyReport::isRunningSum() const { Q_D(const MyMoneyReport); return (d->m_rowType == eMyMoney::Report::RowType::AssetLiability); } eMyMoney::Report::ColumnType MyMoneyReport::columnType() const { Q_D(const MyMoneyReport); return d->m_columnType; } void MyMoneyReport::setColumnType(eMyMoney::Report::ColumnType ct) { Q_D(MyMoneyReport); d->m_columnType = ct; } bool MyMoneyReport::isConvertCurrency() const { Q_D(const MyMoneyReport); return d->m_convertCurrency; } void MyMoneyReport::setConvertCurrency(bool f) { Q_D(MyMoneyReport); d->m_convertCurrency = f; } uint MyMoneyReport::columnPitch() const { Q_D(const MyMoneyReport); return static_cast(d->m_columnType); } QString MyMoneyReport::comment() const { Q_D(const MyMoneyReport); return d->m_comment; } void MyMoneyReport::setComment(const QString& comment) { Q_D(MyMoneyReport); d->m_comment = comment; } eMyMoney::Report::QueryColumn MyMoneyReport::queryColumns() const { Q_D(const MyMoneyReport); return d->m_queryColumns; } void MyMoneyReport::setQueryColumns(eMyMoney::Report::QueryColumn qc) { Q_D(MyMoneyReport); d->m_queryColumns = qc; } QString MyMoneyReport::group() const { Q_D(const MyMoneyReport); return d->m_group; } void MyMoneyReport::setGroup(const QString& group) { Q_D(MyMoneyReport); d->m_group = group; } bool MyMoneyReport::isFavorite() const { Q_D(const MyMoneyReport); return d->m_favorite; } void MyMoneyReport::setFavorite(bool f) { Q_D(MyMoneyReport); d->m_favorite = f; } bool MyMoneyReport::isTax() const { Q_D(const MyMoneyReport); return d->m_tax; } void MyMoneyReport::setTax(bool f) { Q_D(MyMoneyReport); d->m_tax = f; } bool MyMoneyReport::isInvestmentsOnly() const { Q_D(const MyMoneyReport); return d->m_investments; } void MyMoneyReport::setInvestmentsOnly(bool f) { Q_D(MyMoneyReport); d->m_investments = f; if (f) d->m_loans = false; } bool MyMoneyReport::isLoansOnly() const { Q_D(const MyMoneyReport); return d->m_loans; } void MyMoneyReport::setLoansOnly(bool f) { Q_D(MyMoneyReport); d->m_loans = f; if (f) d->m_investments = false; } eMyMoney::Report::DetailLevel MyMoneyReport::detailLevel() const { Q_D(const MyMoneyReport); return d->m_detailLevel; } void MyMoneyReport::setDetailLevel(eMyMoney::Report::DetailLevel detail) { Q_D(MyMoneyReport); d->m_detailLevel = detail; } eMyMoney::Report::InvestmentSum MyMoneyReport::investmentSum() const { Q_D(const MyMoneyReport); return d->m_investmentSum; } void MyMoneyReport::setInvestmentSum(eMyMoney::Report::InvestmentSum sum) { Q_D(MyMoneyReport); d->m_investmentSum = sum; } bool MyMoneyReport::isHideTransactions() const { Q_D(const MyMoneyReport); return d->m_hideTransactions; } void MyMoneyReport::setHideTransactions(bool f) { Q_D(MyMoneyReport); d->m_hideTransactions = f; } eMyMoney::Report::ChartType MyMoneyReport::chartType() const { Q_D(const MyMoneyReport); return d->m_chartType; } void MyMoneyReport::setChartType(eMyMoney::Report::ChartType type) { Q_D(MyMoneyReport); d->m_chartType = type; } bool MyMoneyReport::isChartDataLabels() const { Q_D(const MyMoneyReport); return d->m_chartDataLabels; } void MyMoneyReport::setChartDataLabels(bool f) { Q_D(MyMoneyReport); d->m_chartDataLabels = f; } bool MyMoneyReport::isChartCHGridLines() const { Q_D(const MyMoneyReport); return d->m_chartCHGridLines; } void MyMoneyReport::setChartCHGridLines(bool f) { Q_D(MyMoneyReport); d->m_chartCHGridLines = f; } bool MyMoneyReport::isChartSVGridLines() const { Q_D(const MyMoneyReport); return d->m_chartSVGridLines; } void MyMoneyReport::setChartSVGridLines(bool f) { Q_D(MyMoneyReport); d->m_chartSVGridLines = f; } bool MyMoneyReport::isChartByDefault() const { Q_D(const MyMoneyReport); return d->m_chartByDefault; } void MyMoneyReport::setChartByDefault(bool f) { Q_D(MyMoneyReport); d->m_chartByDefault = f; } uint MyMoneyReport::chartLineWidth() const { Q_D(const MyMoneyReport); return d->m_chartLineWidth; } void MyMoneyReport::setChartLineWidth(uint f) { Q_D(MyMoneyReport); d->m_chartLineWidth = f; } bool MyMoneyReport::isLogYAxis() const { Q_D(const MyMoneyReport); return d->m_logYaxis; } void MyMoneyReport::setLogYAxis(bool f) { Q_D(MyMoneyReport); d->m_logYaxis = f; } QString MyMoneyReport::dataRangeStart() const { Q_D(const MyMoneyReport); return d->m_dataRangeStart; } void MyMoneyReport::setDataRangeStart(const QString& f) { Q_D(MyMoneyReport); d->m_dataRangeStart = f; } QString MyMoneyReport::dataRangeEnd() const { Q_D(const MyMoneyReport); return d->m_dataRangeEnd; } void MyMoneyReport::setDataRangeEnd(const QString& f) { Q_D(MyMoneyReport); d->m_dataRangeEnd = f; } QString MyMoneyReport::dataMajorTick() const { Q_D(const MyMoneyReport); return d->m_dataMajorTick; } void MyMoneyReport::setDataMajorTick(const QString& f) { Q_D(MyMoneyReport); d->m_dataMajorTick = f; } QString MyMoneyReport::dataMinorTick() const { Q_D(const MyMoneyReport); return d->m_dataMinorTick; } void MyMoneyReport::setDataMinorTick(const QString& f) { Q_D(MyMoneyReport); d->m_dataMinorTick = f; } uint MyMoneyReport::yLabelsPrecision() const { Q_D(const MyMoneyReport); return d->m_yLabelsPrecision; } void MyMoneyReport::setYLabelsPrecision(int f) { Q_D(MyMoneyReport); d->m_yLabelsPrecision = f; } bool MyMoneyReport::isIncludingSchedules() const { Q_D(const MyMoneyReport); return d->m_includeSchedules; } void MyMoneyReport::setIncludingSchedules(bool f) { Q_D(MyMoneyReport); d->m_includeSchedules = f; } bool MyMoneyReport::isColumnsAreDays() const { Q_D(const MyMoneyReport); return d->m_columnsAreDays; } void MyMoneyReport::setColumnsAreDays(bool f) { Q_D(MyMoneyReport); d->m_columnsAreDays = f; } bool MyMoneyReport::isIncludingTransfers() const { Q_D(const MyMoneyReport); return d->m_includeTransfers; } void MyMoneyReport::setIncludingTransfers(bool f) { Q_D(MyMoneyReport); d->m_includeTransfers = f; } bool MyMoneyReport::isIncludingUnusedAccounts() const { Q_D(const MyMoneyReport); return d->m_includeUnusedAccounts; } void MyMoneyReport::setIncludingUnusedAccounts(bool f) { Q_D(MyMoneyReport); d->m_includeUnusedAccounts = f; } bool MyMoneyReport::hasBudget() const { Q_D(const MyMoneyReport); return !d->m_budgetId.isEmpty(); } QString MyMoneyReport::budget() const { Q_D(const MyMoneyReport); return d->m_budgetId; } /** * Sets the budget used for this report * * @param budget The ID of the budget to use, or an empty string * to indicate a budget is NOT included * @param fa Whether to display actual data alongside the budget. * Setting to false means the report displays ONLY the budget itself. * @warning For now, the budget ID is ignored. The budget id is * simply checked for any non-empty string, and if so, hasBudget() * will return true. */ void MyMoneyReport::setBudget(const QString& budget, bool fa) { Q_D(MyMoneyReport); d->m_budgetId = budget; d->m_includeBudgetActuals = fa; } bool MyMoneyReport::isIncludingBudgetActuals() const { Q_D(const MyMoneyReport); return d->m_includeBudgetActuals; } void MyMoneyReport::setIncludingBudgetActuals(bool f) { Q_D(MyMoneyReport); d->m_includeBudgetActuals = f; } bool MyMoneyReport::isIncludingForecast() const { Q_D(const MyMoneyReport); return d->m_includeForecast; } void MyMoneyReport::setIncludingForecast(bool f) { Q_D(MyMoneyReport); d->m_includeForecast = f; } bool MyMoneyReport::isIncludingMovingAverage() const { Q_D(const MyMoneyReport); return d->m_includeMovingAverage; } void MyMoneyReport::setIncludingMovingAverage(bool f) { Q_D(MyMoneyReport); d->m_includeMovingAverage = f; } int MyMoneyReport::movingAverageDays() const { Q_D(const MyMoneyReport); return d->m_movingAverageDays; } void MyMoneyReport::setMovingAverageDays(int days) { Q_D(MyMoneyReport); d->m_movingAverageDays = days; } bool MyMoneyReport::isIncludingPrice() const { Q_D(const MyMoneyReport); return d->m_includePrice; } void MyMoneyReport::setIncludingPrice(bool f) { Q_D(MyMoneyReport); d->m_includePrice = f; } bool MyMoneyReport::isIncludingAveragePrice() const { Q_D(const MyMoneyReport); return d->m_includeAveragePrice; } void MyMoneyReport::setIncludingAveragePrice(bool f) { Q_D(MyMoneyReport); d->m_includeAveragePrice = f; } eMyMoney::Report::DataLock MyMoneyReport::dataFilter() const { Q_D(const MyMoneyReport); return d->m_dataLock; } bool MyMoneyReport::isDataUserDefined() const { Q_D(const MyMoneyReport); return d->m_dataLock == eMyMoney::Report::DataLock::UserDefined; } void MyMoneyReport::setDataFilter(eMyMoney::Report::DataLock u) { Q_D(MyMoneyReport); d->m_dataLock = u; } eMyMoney::TransactionFilter::Date MyMoneyReport::dateRange() const { Q_D(const MyMoneyReport); return d->m_dateLock; } bool MyMoneyReport::isDateUserDefined() const { Q_D(const MyMoneyReport); return d->m_dateLock == eMyMoney::TransactionFilter::Date::UserDefined; } /** * Set the underlying date filter and LOCK that filter to the specified * range. For example, if @p _u is "CurrentMonth", this report should always * be updated to the current month no matter when the report is run. * * This updating is not entirely automatic, you should update it yourself by * calling updateDateFilter. * * @param _u The date range constant (MyMoneyTransactionFilter::dateRangeE) * which this report should be locked to. */ void MyMoneyReport::setDateFilter(eMyMoney::TransactionFilter::Date u) { Q_D(MyMoneyReport); d->m_dateLock = u; if (u != eMyMoney::TransactionFilter::Date::UserDefined) MyMoneyTransactionFilter::setDateFilter(u); } void MyMoneyReport::setDateFilter(const QDate& db, const QDate& de) { MyMoneyTransactionFilter::setDateFilter(db, de); } void MyMoneyReport::updateDateFilter() { Q_D(MyMoneyReport); if (d->m_dateLock != eMyMoney::TransactionFilter::Date::UserDefined) MyMoneyTransactionFilter::setDateFilter(d->m_dateLock); } bool MyMoneyReport::isMixedTime() const { Q_D(const MyMoneyReport); return d->m_mixedTime; } void MyMoneyReport::setMixedTime(bool f) { Q_D(MyMoneyReport); d->m_mixedTime = f; } int MyMoneyReport::currentDateColumn() const { Q_D(const MyMoneyReport); return d->m_currentDateColumn; } void MyMoneyReport::setCurrentDateColumn(int f) { Q_D(MyMoneyReport); d->m_currentDateColumn = f; } uint MyMoneyReport::settlementPeriod() const { Q_D(const MyMoneyReport); return d->m_settlementPeriod; } void MyMoneyReport::setSettlementPeriod(uint days) { Q_D(MyMoneyReport); d->m_settlementPeriod = days; } bool MyMoneyReport::isShowingSTLTCapitalGains() const { Q_D(const MyMoneyReport); return d->m_showSTLTCapitalGains; } void MyMoneyReport::setShowSTLTCapitalGains(bool f) { Q_D(MyMoneyReport); d->m_showSTLTCapitalGains = f; } QDate MyMoneyReport::termSeparator() const { Q_D(const MyMoneyReport); return d->m_tseparator; } void MyMoneyReport::setTermSeparator(const QDate& date) { Q_D(MyMoneyReport); d->m_tseparator = date; } bool MyMoneyReport::isSkippingZero() const { Q_D(const MyMoneyReport); return d->m_skipZero; } void MyMoneyReport::setSkipZero(int f) { Q_D(MyMoneyReport); d->m_skipZero = f; } void MyMoneyReport::clearTransactionFilter() { Q_D(MyMoneyReport); d->m_accountGroupFilter = false; d->m_accountGroups.clear(); MyMoneyTransactionFilter::clear(); } void MyMoneyReport::assignFilter(const MyMoneyTransactionFilter& filter) { MyMoneyTransactionFilter::operator=(filter); } void MyMoneyReport::validDateRange(QDate& db, QDate& de) { db = fromDate(); de = toDate(); // if either begin or end date are invalid we have one of the following // possible date filters: // // a) begin date not set - first transaction until given end date // b) end date not set - from given date until last transaction // c) both not set - first transaction until last transaction // // If there is no transaction in the engine at all, we use the current // year as the filter criteria. if (!db.isValid() || !de.isValid()) { QList list = MyMoneyFile::instance()->transactionList(*this); QDate tmpBegin, tmpEnd; if (!list.isEmpty()) { qSort(list); // try to use the post dates tmpBegin = list.front().postDate(); tmpEnd = list.back().postDate(); // if the post dates are not valid try the entry dates if (!tmpBegin.isValid()) tmpBegin = list.front().entryDate(); if (!tmpEnd.isValid()) tmpEnd = list.back().entryDate(); } // make sure that we leave this function with valid dates no mather what if (!tmpBegin.isValid() || !tmpEnd.isValid() || tmpBegin > tmpEnd) { tmpBegin = QDate(QDate::currentDate().year(), 1, 1); // the first date in the file tmpEnd = QDate(QDate::currentDate().year(), 12, 31); // the last date in the file } if (!db.isValid()) db = tmpBegin; if (!de.isValid()) de = tmpEnd; } if (db > de) db = de; } bool MyMoneyReport::accountGroups(QList& list) const { Q_D(const MyMoneyReport); bool result = d->m_accountGroupFilter; if (result) { QList::const_iterator it_group = d->m_accountGroups.begin(); while (it_group != d->m_accountGroups.end()) { list += (*it_group); ++it_group; } } return result; } void MyMoneyReport::addAccountGroup(eMyMoney::Account::Type type) { Q_D(MyMoneyReport); if (!d->m_accountGroups.isEmpty() && type != eMyMoney::Account::Type::Unknown) { if (d->m_accountGroups.contains(type)) return; } d->m_accountGroupFilter = true; if (type != eMyMoney::Account::Type::Unknown) d->m_accountGroups.push_back(type); } bool MyMoneyReport::includesAccountGroup(eMyMoney::Account::Type type) const { Q_D(const MyMoneyReport); bool result = (! d->m_accountGroupFilter) || (isIncludingTransfers() && d->m_rowType == eMyMoney::Report::RowType::ExpenseIncome) || d->m_accountGroups.contains(type); return result; } bool MyMoneyReport::includes(const MyMoneyAccount& acc) const { Q_D(const MyMoneyReport); auto result = false; if (includesAccountGroup(acc.accountGroup())) { switch (acc.accountGroup()) { case eMyMoney::Account::Type::Income: case eMyMoney::Account::Type::Expense: if (isTax()) result = (acc.value("Tax") == "Yes") && includesCategory(acc.id()); else result = includesCategory(acc.id()); break; case eMyMoney::Account::Type::Asset: case eMyMoney::Account::Type::Liability: if (isLoansOnly()) result = acc.isLoan() && includesAccount(acc.id()); else if (isInvestmentsOnly()) result = acc.isInvest() && includesAccount(acc.id()); else if (isIncludingTransfers() && d->m_rowType == eMyMoney::Report::RowType::ExpenseIncome) // If transfers are included, ONLY include this account if it is NOT // included in the report itself!! result = ! includesAccount(acc.id()); else result = includesAccount(acc.id()); break; case eMyMoney::Account::Type::Equity: if (isInvestmentsOnly()) result = (isIncludingPrice() || isIncludingAveragePrice()) && acc.isInvest() && includesAccount(acc.id()); break; default: result = includesAccount(acc.id()); } } return result; } bool MyMoneyReport::hasReferenceTo(const QString& id) const { QStringList list; // collect all ids accounts(list); categories(list); payees(list); tags(list); return list.contains(id); } int MyMoneyReport::m_lineWidth = 2; bool MyMoneyReport::m_expertMode = false; void MyMoneyReport::setLineWidth(int width) { m_lineWidth = width; } +int MyMoneyReport::lineWidth() +{ + return m_lineWidth; +} + void MyMoneyReport::setExpertMode(bool expertMode) { m_expertMode = expertMode; } QString MyMoneyReport::toString(eMyMoney::Report::RowType type) { switch(type) { case eMyMoney::Report::RowType::NoRows : return "eMyMoney::Report::RowType::NoRows"; case eMyMoney::Report::RowType::AssetLiability : return "eMyMoney::Report::RowType::AssetLiability"; case eMyMoney::Report::RowType::ExpenseIncome : return "eMyMoney::Report::RowType::ExpenseIncome"; case eMyMoney::Report::RowType::Category : return "eMyMoney::Report::RowType::Category"; case eMyMoney::Report::RowType::TopCategory : return "eTopCategory"; case eMyMoney::Report::RowType::Account : return "eAccount"; case eMyMoney::Report::RowType::Tag : return "eTag"; case eMyMoney::Report::RowType::Payee : return "ePayee"; case eMyMoney::Report::RowType::Month : return "eMonth"; case eMyMoney::Report::RowType::Week : return "eWeek"; case eMyMoney::Report::RowType::TopAccount : return "eTopAccount"; case eMyMoney::Report::RowType::AccountByTopAccount: return "eAccountByTopAccount"; case eMyMoney::Report::RowType::EquityType : return "eEquityType"; case eMyMoney::Report::RowType::AccountType : return "eAccountType"; case eMyMoney::Report::RowType::Institution : return "eInstitution"; case eMyMoney::Report::RowType::Budget : return "eBudget"; case eMyMoney::Report::RowType::BudgetActual : return "eBudgetActual"; case eMyMoney::Report::RowType::Schedule : return "eSchedule"; case eMyMoney::Report::RowType::AccountInfo : return "eAccountInfo"; case eMyMoney::Report::RowType::AccountLoanInfo : return "eAccountLoanInfo"; case eMyMoney::Report::RowType::AccountReconcile : return "eAccountReconcile"; case eMyMoney::Report::RowType::CashFlow : return "eCashFlow"; default : return "undefined"; } } QString MyMoneyReport::toString(eMyMoney::Report::ReportType type) { switch(type) { case eMyMoney::Report::ReportType::NoReport: return "eNoReport"; case eMyMoney::Report::ReportType::PivotTable: return "ePivotTable"; case eMyMoney::Report::ReportType::QueryTable: return "eQueryTable"; case eMyMoney::Report::ReportType::InfoTable: return "eInfoTable"; default: return "undefined"; } } diff --git a/kmymoney/mymoney/mymoneyreport.h b/kmymoney/mymoney/mymoneyreport.h index 2ebe94e0a..2d423cbae 100644 --- a/kmymoney/mymoney/mymoneyreport.h +++ b/kmymoney/mymoney/mymoneyreport.h @@ -1,437 +1,444 @@ /* * Copyright 2004-2006 Ace Jones * Copyright 2006 Darren Gould * Copyright 2007-2010 Alvaro Soliverez * Copyright 2017-2018 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef MYMONEYREPORT_H #define MYMONEYREPORT_H // ---------------------------------------------------------------------------- // QT Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyobject.h" #include "mymoneytransactionfilter.h" #include "kmm_mymoney_export.h" #include "mymoneyunittestable.h" class QString; class MyMoneyAccount; template class QList; namespace eMyMoney { namespace Account { enum class Type; } namespace TransactionFilter { enum class Date; } } namespace eMyMoney { namespace Report { enum class RowType; } } namespace eMyMoney { namespace Report { enum class ReportType; } } namespace eMyMoney { namespace Report { enum class ColumnType; } } namespace eMyMoney { namespace Report { enum QueryColumn : int; } } namespace eMyMoney { namespace Report { enum class DetailLevel; } } namespace eMyMoney { namespace Report { enum class InvestmentSum; } } namespace eMyMoney { namespace Report { enum class ChartType; } } namespace eMyMoney { namespace Report { enum class DataLock; } } /** * This class defines a report within the MyMoneyEngine. The report class * contains all the configuration parameters needed to run a report, plus * XML serialization. * * A report is a transactionfilter, so any report can specify which * transactions it's interested down to the most minute level of detail. * It extends the transactionfilter by providing identification (name, * comments, group type, etc) as well as layout information (what kind * of layout should be used, how the rows & columns should be presented, * currency converted, etc.) * * As noted above, this class only provides a report DEFINITION. The * generation and presentation of the report itself are left to higher * level classes. * * @author Ace Jones */ class MyMoneyReportPrivate; class KMM_MYMONEY_EXPORT MyMoneyReport: public MyMoneyObject, public MyMoneyTransactionFilter { Q_DECLARE_PRIVATE_D(MyMoneyObject::d_ptr, MyMoneyReport) KMM_MYMONEY_UNIT_TESTABLE public: MyMoneyReport(); explicit MyMoneyReport(const QString &id); explicit MyMoneyReport(eMyMoney::Report::RowType rt, unsigned ct, eMyMoney::TransactionFilter::Date dl, eMyMoney::Report::DetailLevel ss, const QString& name, const QString& comment); MyMoneyReport(const QString& id, const MyMoneyReport& other); MyMoneyReport(const MyMoneyReport & other); MyMoneyReport(MyMoneyReport && other); MyMoneyReport & operator=(MyMoneyReport other); friend void swap(MyMoneyReport& first, MyMoneyReport& second); ~MyMoneyReport(); eMyMoney::Report::ReportType reportType() const; void setReportType(eMyMoney::Report::ReportType rt); QString name() const; void setName(const QString& s); bool isShowingRowTotals() const; void setShowingRowTotals(bool f); bool isShowingColumnTotals() const; void setShowingColumnTotals(bool f); eMyMoney::Report::RowType rowType() const; void setRowType(eMyMoney::Report::RowType rt); bool isRunningSum() const; eMyMoney::Report::ColumnType columnType() const; void setColumnType(eMyMoney::Report::ColumnType ct); bool isConvertCurrency() const; void setConvertCurrency(bool f); uint columnPitch() const; QString comment() const; void setComment(const QString& comment); eMyMoney::Report::QueryColumn queryColumns() const; void setQueryColumns(eMyMoney::Report::QueryColumn qc); QString group() const; void setGroup(const QString& group); bool isFavorite() const; void setFavorite(bool f); bool isTax() const; void setTax(bool f); bool isInvestmentsOnly() const; void setInvestmentsOnly(bool f); bool isLoansOnly() const; void setLoansOnly(bool f); eMyMoney::Report::DetailLevel detailLevel() const; void setDetailLevel(eMyMoney::Report::DetailLevel detail); eMyMoney::Report::InvestmentSum investmentSum() const; void setInvestmentSum(eMyMoney::Report::InvestmentSum sum); bool isHideTransactions() const; void setHideTransactions(bool f); eMyMoney::Report::ChartType chartType() const; void setChartType(eMyMoney::Report::ChartType type); bool isChartDataLabels() const; void setChartDataLabels(bool f); bool isChartCHGridLines() const; void setChartCHGridLines(bool f); bool isChartSVGridLines() const; void setChartSVGridLines(bool f); bool isChartByDefault() const; void setChartByDefault(bool f); uint chartLineWidth() const; void setChartLineWidth(uint f); bool isLogYAxis() const; void setLogYAxis(bool f); QString dataRangeStart() const; void setDataRangeStart(const QString& f); QString dataRangeEnd() const; void setDataRangeEnd(const QString& f); QString dataMajorTick() const; void setDataMajorTick(const QString& f); QString dataMinorTick() const; void setDataMinorTick(const QString& f); uint yLabelsPrecision() const; void setYLabelsPrecision(int f); bool isIncludingSchedules() const; void setIncludingSchedules(bool f); bool isColumnsAreDays() const; void setColumnsAreDays(bool f); bool isIncludingTransfers() const; void setIncludingTransfers(bool f); bool isIncludingUnusedAccounts() const; void setIncludingUnusedAccounts(bool f); bool hasBudget() const; QString budget() const; /** * Sets the budget used for this report * * @param budget The ID of the budget to use, or an empty string * to indicate a budget is NOT included * @param fa Whether to display actual data alongside the budget. * Setting to false means the report displays ONLY the budget itself. * @warning For now, the budget ID is ignored. The budget id is * simply checked for any non-empty string, and if so, hasBudget() * will return true. */ void setBudget(const QString& budget, bool fa = true); bool isIncludingBudgetActuals() const; void setIncludingBudgetActuals(bool f); bool isIncludingForecast() const; void setIncludingForecast(bool f); bool isIncludingMovingAverage() const; void setIncludingMovingAverage(bool f); int movingAverageDays() const; void setMovingAverageDays(int days); bool isIncludingPrice() const; void setIncludingPrice(bool f); bool isIncludingAveragePrice() const; void setIncludingAveragePrice(bool f); eMyMoney::Report::DataLock dataFilter() const; bool isDataUserDefined() const; void setDataFilter(eMyMoney::Report::DataLock u); eMyMoney::TransactionFilter::Date dateRange() const; bool isDateUserDefined() const; /** * Set the underlying date filter and LOCK that filter to the specified * range. For example, if @p _u is "CurrentMonth", this report should always * be updated to the current month no matter when the report is run. * * This updating is not entirely automatic, you should update it yourself by * calling updateDateFilter. * * @param _u The date range constant (MyMoneyTransactionFilter::dateRangeE) * which this report should be locked to. */ void setDateFilter(eMyMoney::TransactionFilter::Date u); /** * Set the underlying date filter using the start and end dates provided. * Note that this does not LOCK to any range like setDateFilter(unsigned) * above. It is just a reimplementation of the MyMoneyTransactionFilter * version. * * @param _db The inclusive begin date of the date range * @param _de The inclusive end date of the date range */ void setDateFilter(const QDate& db, const QDate& de); /** * Set the underlying date filter using the 'date lock' property. * * Always call this function before executing the report to be sure that * the date filters properly match the plain-language 'date lock'. * * For example, if the report is date-locked to "Current Month", and the * last time you loaded or ran the report was in August, but it's now * September, this function will update the date range to be September, * as is proper. */ void updateDateFilter(); bool isMixedTime() const; void setMixedTime(bool f); int currentDateColumn() const; void setCurrentDateColumn(int f); uint settlementPeriod() const; void setSettlementPeriod(uint days); bool isShowingSTLTCapitalGains() const; void setShowSTLTCapitalGains(bool f); QDate termSeparator() const; void setTermSeparator(const QDate& date); bool isSkippingZero() const; void setSkipZero(int f); /** * This method allows you to clear the underlying transaction filter */ void clearTransactionFilter(); /** * This method allows you to set the underlying transaction filter * * @param _filter The filter which should replace the existing transaction * filter. */ void assignFilter(const MyMoneyTransactionFilter& filter); /** * Retrieves a VALID beginning & ending date for this report. * * The underlying date filter can return en empty QDate() for either the * begin or end date or both. This is typically unacceptable for reports, * which need the REAL begin and end date. * * This function gets the underlying date filter range, and if either is * an empty QDate(), it determines the missing date from looking at all * the transactions which match the underlying filter, and returning the * date of the first or last transaction (as appropriate). * * @param _db The inclusive begin date of the date range * @param _de The inclusive end date of the date range */ void validDateRange(QDate &db, QDate &de); /** * This method turns on the account group filter and adds the * @p type to the list of allowed groups. * * Note that account group filtering is handled differently * than all the filters of the underlying class. This filter * is meant to be applied to individual splits of matched * transactions AFTER the underlying filter is used to find * the matching transactions. * * @param type the account group to add to the allowed groups list */ void addAccountGroup(eMyMoney::Account::Type type); /** * This method returns whether an account group filter has been set, * and if so, it returns all the account groups set in the filter. * * @param list list to append account groups into * @return return true if an account group filter has been set */ bool accountGroups(QList& list) const; /** * This method returns whether the specified account group * is allowed by the account groups filter. * * @param type group to append account groups into * @return return true if an account group filter has been set */ bool includesAccountGroup(eMyMoney::Account::Type type) const; /** * This method is used to test whether a specific account * passes the accountGroup test and either the Account or * Category test, depending on which sort of Account it is. * * The m_tax and m_investments properties are also considered. * * @param acc the account in question * @return true if account is in filter set, false otherwise */ bool includes(const MyMoneyAccount& acc) const; /** * This method checks if a reference to the given object exists. It returns, * a @p true if the object is referencing the one requested by the * parameter @p id. If it does not, this method returns @p false. * * @param id id of the object to be checked for references * @retval true This object references object with id @p id. * @retval false This object does not reference the object with id @p id. */ bool hasReferenceTo(const QString& id) const override; - /** - * This method allows to modify the default lineWidth for graphs. - * The default is 2. - */ - static void setLineWidth(int width); - - /** - * This member keeps the current setting for line graphs lineWidth. - * @sa setLineWidth() - */ - static int m_lineWidth; - - static void setExpertMode(bool expertMode); - static bool m_expertMode; - /** * Return row type as string. * * @param type type to get string for * @return row type converted to string */ static QString toString(eMyMoney::Report::RowType type); /** * Return report type as string. * * @param type report type to get string for * @return report type converted to string */ static QString toString(eMyMoney::Report::ReportType type); + + /** + * This method allows to modify and retrieve the default lineWidth for graphs. + * The default is 2. + */ + static void setLineWidth(int width); + static int lineWidth(); + + // set the expert mode which shows equity accounts for some reports + static void setExpertMode(bool expertMode); + +private: + void addAccountGroupsByRowType(eMyMoney::Report::RowType rt); + +private: + /** + * This member keeps the current setting for line graphs lineWidth. + * @sa setLineWidth() + */ + static int m_lineWidth; + + static bool m_expertMode; }; inline void swap(MyMoneyReport& first, MyMoneyReport& second) // krazy:exclude=inline { using std::swap; swap(first.MyMoneyObject::d_ptr, second.MyMoneyObject::d_ptr); swap(first.MyMoneyTransactionFilter::d_ptr, second.MyMoneyTransactionFilter::d_ptr); } inline MyMoneyReport::MyMoneyReport(MyMoneyReport && other) : MyMoneyReport() // krazy:exclude=inline { swap(*this, other); } inline MyMoneyReport & MyMoneyReport::operator=(MyMoneyReport other) // krazy:exclude=inline { swap(*this, other); return *this; } /** * Make it possible to hold @ref MyMoneyReport objects inside @ref QVariant objects. */ Q_DECLARE_METATYPE(MyMoneyReport) #endif // MYMONEYREPORT_H diff --git a/kmymoney/plugins/views/reports/core/objectinfotable.cpp b/kmymoney/plugins/views/reports/core/objectinfotable.cpp index da0689ccd..757aba5c5 100644 --- a/kmymoney/plugins/views/reports/core/objectinfotable.cpp +++ b/kmymoney/plugins/views/reports/core/objectinfotable.cpp @@ -1,382 +1,388 @@ /* * Copyright 2004-2005 Ace Jones * Copyright 2008-2010 Alvaro Soliverez * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "objectinfotable.h" // ---------------------------------------------------------------------------- // QT Includes #include #include +#include // ---------------------------------------------------------------------------- // KDE Includes #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyfile.h" #include "mymoneyinstitution.h" #include "mymoneyaccount.h" #include "mymoneyaccountloan.h" #include "mymoneysecurity.h" #include "mymoneyprice.h" #include "mymoneypayee.h" #include "mymoneymoney.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "mymoneyreport.h" #include "mymoneyschedule.h" #include "mymoneyexception.h" #include "kmymoneyutils.h" #include "reportaccount.h" #include "mymoneyenums.h" namespace reports { // **************************************************************************** // // ObjectInfoTable implementation // // **************************************************************************** /** * TODO * * - Collapse 2- & 3- groups when they are identical * - Way more test cases (especially splits & transfers) * - Option to collapse splits * - Option to exclude transfers * */ ObjectInfoTable::ObjectInfoTable(const MyMoneyReport& _report): ListTable(_report) { // separated into its own method to allow debugging (setting breakpoints // directly in ctors somehow does not work for me (ipwizard)) // TODO: remove the init() method and move the code back to the ctor init(); } void ObjectInfoTable::init() { m_columns.clear(); m_group.clear(); m_subtotal.clear(); switch (m_config.rowType()) { case eMyMoney::Report::RowType::Schedule: constructScheduleTable(); m_columns << ctNextDueDate << ctName; break; + + default: + qDebug() << "ObjectInfoTable::ObjectInfoTable(): unhandled row type" << static_cast(m_config.rowType()); + qDebug() << "turned into eMyMoney::Report::RowType::AccountInfo"; + m_config.setRowType(eMyMoney::Report::RowType::AccountInfo); + // intentional fall through case eMyMoney::Report::RowType::AccountInfo: constructAccountTable(); m_columns << ctType << ctName; break; + case eMyMoney::Report::RowType::AccountLoanInfo: constructAccountLoanTable(); m_columns << ctType << ctName; break; - default: - break; } // Sort the data to match the report definition m_subtotal << ctValue; switch (m_config.rowType()) { case eMyMoney::Report::RowType::Schedule: m_group << ctType; m_subtotal << ctValue; break; case eMyMoney::Report::RowType::AccountInfo: case eMyMoney::Report::RowType::AccountLoanInfo: m_group << ctTopCategory << ctInstitution; m_subtotal << ctCurrentBalance; break; default: throw MYMONEYEXCEPTION_CSTRING("ObjectInfoTable::ObjectInfoTable(): unhandled row type"); } QVector sort = QVector::fromList(m_group) << QVector::fromList(m_columns) << ctID << ctRank; switch (m_config.rowType()) { case eMyMoney::Report::RowType::Schedule: if (m_config.detailLevel() == eMyMoney::Report::DetailLevel::All) { m_columns << ctPayee << ctPaymentType << ctOccurrence << ctNextDueDate << ctCategory; } else { m_columns << ctPayee << ctPaymentType << ctOccurrence << ctNextDueDate; } break; case eMyMoney::Report::RowType::AccountInfo: m_columns << ctNumber << ctDescription << ctOpeningDate << ctCurrencyName << ctBalanceWarning << ctCreditWarning << ctMaxCreditLimit << ctTax << ctFavorite; break; case eMyMoney::Report::RowType::AccountLoanInfo: m_columns << ctNumber << ctDescription << ctOpeningDate << ctCurrencyName << ctPayee << ctLoanAmount << ctInterestRate << ctNextInterestChange << ctPeriodicPayment << ctFinalPayment << ctFavorite; break; default: m_columns.clear(); } TableRow::setSortCriteria(sort); qSort(m_rows); } void ObjectInfoTable::constructScheduleTable() { MyMoneyFile* file = MyMoneyFile::instance(); QList schedules; schedules = file->scheduleList(QString(), eMyMoney::Schedule::Type::Any, eMyMoney::Schedule::Occurrence::Any, eMyMoney::Schedule::PaymentType::Any, m_config.fromDate(), m_config.toDate(), false); QList::const_iterator it_schedule = schedules.constBegin(); while (it_schedule != schedules.constEnd()) { MyMoneySchedule schedule = *it_schedule; ReportAccount account(schedule.account()); if (m_config.includes(account)) { //get fraction for account int fraction = account.fraction(); //use base currency fraction if not initialized if (fraction == -1) fraction = MyMoneyFile::instance()->baseCurrency().smallestAccountFraction(); TableRow scheduleRow; //convert to base currency if needed MyMoneyMoney xr = MyMoneyMoney::ONE; if (m_config.isConvertCurrency() && account.isForeignCurrency()) { xr = account.baseCurrencyPrice(QDate::currentDate()).reduce(); } // help for sort and render functions scheduleRow[ctRank] = QLatin1Char('1'); //schedule data scheduleRow[ctID] = schedule.id(); scheduleRow[ctName] = schedule.name(); scheduleRow[ctNextDueDate] = schedule.nextDueDate().toString(Qt::ISODate); scheduleRow[ctType] = KMyMoneyUtils::scheduleTypeToString(schedule.type()); scheduleRow[ctOccurrence] = i18nc("Frequency of schedule", schedule.occurrenceToString().toLatin1()); scheduleRow[ctPaymentType] = KMyMoneyUtils::paymentMethodToString(schedule.paymentType()); //scheduleRow["category"] = account.name(); //to get the payee we must look into the splits of the transaction MyMoneyTransaction transaction = schedule.transaction(); MyMoneySplit split = transaction.splitByAccount(account.id(), true); scheduleRow[ctValue] = (split.value() * xr).toString(); MyMoneyPayee payee = file->payee(split.payeeId()); scheduleRow[ctPayee] = payee.name(); m_rows += scheduleRow; //the text matches the main split bool transaction_text = m_config.match(split); if (m_config.detailLevel() == eMyMoney::Report::DetailLevel::All) { //get the information for all splits QList splits = transaction.splits(); QList::const_iterator split_it = splits.constBegin(); for (; split_it != splits.constEnd(); ++split_it) { TableRow splitRow; ReportAccount splitAcc((*split_it).accountId()); splitRow[ctRank] = QLatin1Char('2'); splitRow[ctID] = schedule.id(); splitRow[ctName] = schedule.name(); splitRow[ctPayee] = payee.name(); splitRow[ctType] = KMyMoneyUtils::scheduleTypeToString(schedule.type()); splitRow[ctNextDueDate] = schedule.nextDueDate().toString(Qt::ISODate); if ((*split_it).value() == MyMoneyMoney::autoCalc) { splitRow[ctSplit] = MyMoneyMoney::autoCalc.toString(); } else if (! splitAcc.isIncomeExpense()) { splitRow[ctSplit] = (*split_it).value().toString(); } else { splitRow[ctSplit] = (- (*split_it).value()).toString(); } //if it is an assett account, mark it as a transfer if (! splitAcc.isIncomeExpense()) { splitRow[ctCategory] = ((* split_it).value().isNegative()) ? i18n("Transfer from %1" , splitAcc.fullName()) : i18n("Transfer to %1" , splitAcc.fullName()); } else { splitRow [ctCategory] = splitAcc.fullName(); } //add the split only if it matches the text or it matches the main split if (m_config.match((*split_it)) || transaction_text) m_rows += splitRow; } } } ++it_schedule; } } void ObjectInfoTable::constructAccountTable() { MyMoneyFile* file = MyMoneyFile::instance(); //make sure we have all subaccounts of investment accounts includeInvestmentSubAccounts(); QList accounts; file->accountList(accounts); QList::const_iterator it_account = accounts.constBegin(); while (it_account != accounts.constEnd()) { TableRow accountRow; ReportAccount account(*it_account); if (m_config.includes(account) && account.accountType() != eMyMoney::Account::Type::Stock && !account.isClosed()) { MyMoneyMoney value; accountRow[ctRank] = QLatin1Char('1'); accountRow[ctTopCategory] = MyMoneyAccount::accountTypeToString(account.accountGroup()); if (!account.institutionId().isEmpty()) { accountRow[ctInstitution] = (file->institution(account.institutionId())).name(); } else { accountRow[ctInstitution] = QStringLiteral("Accounts with no institution assigned"); } accountRow[ctType] = MyMoneyAccount::accountTypeToString(account.accountType()); accountRow[ctName] = account.name(); accountRow[ctNumber] = account.number(); accountRow[ctDescription] = account.description(); accountRow[ctOpeningDate] = account.openingDate().toString(Qt::ISODate); //accountRow["currency"] = (file->currency(account.currencyId())).tradingSymbol(); accountRow[ctCurrencyName] = (file->currency(account.currencyId())).name(); accountRow[ctBalanceWarning] = account.value("minBalanceEarly"); accountRow[ctMaxBalanceLimit] = account.value("minBalanceAbsolute"); accountRow[ctCreditWarning] = account.value("maxCreditEarly"); accountRow[ctMaxCreditLimit] = account.value("maxCreditAbsolute"); accountRow[ctTax] = account.value("Tax") == QLatin1String("Yes") ? i18nc("Is this a tax account?", "Yes") : QString(); accountRow[ctOpeningBalance] = account.value("OpeningBalanceAccount") == QLatin1String("Yes") ? i18nc("Is this an opening balance account?", "Yes") : QString(); accountRow[ctFavorite] = account.value("PreferredAccount") == QLatin1String("Yes") ? i18nc("Is this a favorite account?", "Yes") : QString(); //investment accounts show the balances of all its subaccounts if (account.accountType() == eMyMoney::Account::Type::Investment) { value = investmentBalance(account); } else { value = file->balance(account.id()); } //convert to base currency if needed if (m_config.isConvertCurrency() && account.isForeignCurrency()) { MyMoneyMoney xr = account.baseCurrencyPrice(QDate::currentDate()).reduce(); value = value * xr; } accountRow[ctCurrentBalance] = value.toString(); m_rows += accountRow; } ++it_account; } } void ObjectInfoTable::constructAccountLoanTable() { MyMoneyFile* file = MyMoneyFile::instance(); QList accounts; file->accountList(accounts); QList::const_iterator it_account = accounts.constBegin(); while (it_account != accounts.constEnd()) { TableRow accountRow; ReportAccount account(*it_account); MyMoneyAccountLoan loan = *it_account; if (m_config.includes(account) && account.isLoan() && !account.isClosed()) { //convert to base currency if needed MyMoneyMoney xr = MyMoneyMoney::ONE; if (m_config.isConvertCurrency() && account.isForeignCurrency()) { xr = account.baseCurrencyPrice(QDate::currentDate()).reduce(); } accountRow[ctRank] = QLatin1Char('1'); accountRow[ctTopCategory] = MyMoneyAccount::accountTypeToString(account.accountGroup()); if (!account.institutionId().isEmpty()) { accountRow[ctInstitution] = (file->institution(account.institutionId())).name(); } else { accountRow[ctInstitution] = QStringLiteral("Accounts with no institution assigned"); } accountRow[ctType] = MyMoneyAccount::accountTypeToString(account.accountType()); accountRow[ctName] = account.name(); accountRow[ctNumber] = account.number(); accountRow[ctDescription] = account.description(); accountRow[ctOpeningDate] = account.openingDate().toString(Qt::ISODate); //accountRow["currency"] = (file->currency(account.currencyId())).tradingSymbol(); accountRow[ctCurrencyName] = (file->currency(account.currencyId())).name(); accountRow[ctPayee] = file->payee(loan.payee()).name(); accountRow[ctLoanAmount] = (loan.loanAmount() * xr).toString(); accountRow[ctInterestRate] = (loan.interestRate(QDate::currentDate()) / MyMoneyMoney(100, 1) * xr).toString(); accountRow[ctNextInterestChange] = loan.nextInterestChange().toString(Qt::ISODate); accountRow[ctPeriodicPayment] = (loan.periodicPayment() * xr).toString(); accountRow[ctFinalPayment] = (loan.finalPayment() * xr).toString(); accountRow[ctFavorite] = account.value("PreferredAccount") == QLatin1String("Yes") ? i18nc("Is this a favorite account?", "Yes") : QString(); MyMoneyMoney value = file->balance(account.id()); value = value * xr; accountRow[ctCurrentBalance] = value.toString(); m_rows += accountRow; } ++it_account; } } MyMoneyMoney ObjectInfoTable::investmentBalance(const MyMoneyAccount& acc) { MyMoneyFile* file = MyMoneyFile::instance(); MyMoneyMoney value = file->balance(acc.id()); foreach (const auto sAccount, acc.accountList()) { auto stock = file->account(sAccount); try { MyMoneyMoney val; MyMoneyMoney balance = file->balance(stock.id()); MyMoneySecurity security = file->security(stock.currencyId()); const MyMoneyPrice &price = file->price(stock.currencyId(), security.tradingCurrency()); val = balance * price.rate(security.tradingCurrency()); // adjust value of security to the currency of the account MyMoneySecurity accountCurrency = file->currency(acc.currencyId()); val = val * file->price(security.tradingCurrency(), accountCurrency.id()).rate(accountCurrency.id()); val = val.convert(acc.fraction()); value += val; } catch (const MyMoneyException &e) { qWarning("%s", qPrintable(QString("cannot convert stock balance of %1 to base currency: %2").arg(stock.name(), e.what()))); } } return value; } } diff --git a/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp b/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp index eee4cb6f1..d16724591 100644 --- a/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp +++ b/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp @@ -1,1204 +1,1210 @@ /* * Copyright 2004-2006 Ace Jones * Copyright 2006 Darren Gould * Copyright 2007-2010 Alvaro Soliverez * Copyright 2017-2018 Łukasz Wojniłowicz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "xmlstoragehelper.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneymoney.h" #include "mymoneybudget.h" #include "mymoneyreport.h" #include "mymoneytransactionfilter.h" #include "mymoneyenums.h" #include "mymoneyexception.h" namespace Element { enum class Report { Payee, Tag, Account, Text, Type, State, Number, Amount, Dates, Category, AccountGroup }; enum class Budget { Budget = 0, Account, Period }; } namespace Attribute { enum class Report { ID = 0, Group, Type, Name, Comment, ConvertCurrency, Favorite, SkipZero, DateLock, DataLock, MovingAverageDays, IncludesActuals, IncludesForecast, IncludesPrice, IncludesAveragePrice, IncludesMovingAverage, IncludesSchedules, IncludesTransfers, IncludesUnused, MixedTime, Investments, Budget, ShowRowTotals, ShowColumnTotals, Detail, ColumnsAreDays, ChartType, ChartCHGridLines, ChartSVGridLines, ChartDataLabels, ChartByDefault, LogYAxis, ChartLineWidth, ColumnType, RowType, DataRangeStart, DataRangeEnd, DataMajorTick, DataMinorTick, YLabelsPrecision, QueryColumns, Tax, Loans, HideTransactions, InvestmentSum, SettlementPeriod, ShowSTLTCapitalGains, TermsSeparator, Pattern, CaseSensitive, RegEx, InvertText, State, From, To, // insert new entries above this line LastAttribute }; enum class Budget { ID = 0, Name, Start, Version, BudgetLevel, BudgetSubAccounts, Amount, // insert new entries above this line LastAttribute }; } namespace MyMoneyXmlContentHandler2 { enum class Node { Report, Budget }; QString nodeName(Node nodeID) { static const QHash nodeNames { {Node::Report, QStringLiteral("REPORT")}, {Node::Budget, QStringLiteral("BUDGET")} }; return nodeNames.value(nodeID); } uint qHash(const Node key, uint seed) { return ::qHash(static_cast(key), seed); } QString elementName(Element::Report elementID) { static const QMap elementNames { {Element::Report::Payee, QStringLiteral("PAYEE")}, {Element::Report::Tag, QStringLiteral("TAG")}, {Element::Report::Account, QStringLiteral("ACCOUNT")}, {Element::Report::Text, QStringLiteral("TEXT")}, {Element::Report::Type, QStringLiteral("TYPE")}, {Element::Report::State, QStringLiteral("STATE")}, {Element::Report::Number, QStringLiteral("NUMBER")}, {Element::Report::Amount, QStringLiteral("AMOUNT")}, {Element::Report::Dates, QStringLiteral("DATES")}, {Element::Report::Category, QStringLiteral("CATEGORY")}, {Element::Report::AccountGroup, QStringLiteral("ACCOUNTGROUP")} }; return elementNames.value(elementID); } QString attributeName(Attribute::Report attributeID) { static const QMap attributeNames { {Attribute::Report::ID, QStringLiteral("id")}, {Attribute::Report::Group, QStringLiteral("group")}, {Attribute::Report::Type, QStringLiteral("type")}, {Attribute::Report::Name, QStringLiteral("name")}, {Attribute::Report::Comment, QStringLiteral("comment")}, {Attribute::Report::ConvertCurrency, QStringLiteral("convertcurrency")}, {Attribute::Report::Favorite, QStringLiteral("favorite")}, {Attribute::Report::SkipZero, QStringLiteral("skipZero")}, {Attribute::Report::DateLock, QStringLiteral("datelock")}, {Attribute::Report::DataLock, QStringLiteral("datalock")}, {Attribute::Report::MovingAverageDays, QStringLiteral("movingaveragedays")}, {Attribute::Report::IncludesActuals, QStringLiteral("includesactuals")}, {Attribute::Report::IncludesForecast, QStringLiteral("includesforecast")}, {Attribute::Report::IncludesPrice, QStringLiteral("includesprice")}, {Attribute::Report::IncludesAveragePrice, QStringLiteral("includesaverageprice")}, {Attribute::Report::IncludesMovingAverage, QStringLiteral("includesmovingaverage")}, {Attribute::Report::IncludesSchedules, QStringLiteral("includeschedules")}, {Attribute::Report::IncludesTransfers, QStringLiteral("includestransfers")}, {Attribute::Report::IncludesUnused, QStringLiteral("includeunused")}, {Attribute::Report::MixedTime, QStringLiteral("mixedtime")}, {Attribute::Report::Investments, QStringLiteral("investments")}, {Attribute::Report::Budget, QStringLiteral("budget")}, {Attribute::Report::ShowRowTotals, QStringLiteral("showrowtotals")}, {Attribute::Report::ShowColumnTotals, QStringLiteral("showcolumntotals")}, {Attribute::Report::Detail, QStringLiteral("detail")}, {Attribute::Report::ColumnsAreDays, QStringLiteral("columnsaredays")}, {Attribute::Report::ChartType, QStringLiteral("charttype")}, {Attribute::Report::ChartCHGridLines, QStringLiteral("chartchgridlines")}, {Attribute::Report::ChartSVGridLines, QStringLiteral("chartsvgridlines")}, {Attribute::Report::ChartDataLabels, QStringLiteral("chartdatalabels")}, {Attribute::Report::ChartByDefault, QStringLiteral("chartbydefault")}, {Attribute::Report::LogYAxis, QStringLiteral("logYaxis")}, {Attribute::Report::ChartLineWidth, QStringLiteral("chartlinewidth")}, {Attribute::Report::ColumnType, QStringLiteral("columntype")}, {Attribute::Report::RowType, QStringLiteral("rowtype")}, {Attribute::Report::DataRangeStart, QStringLiteral("dataRangeStart")}, {Attribute::Report::DataRangeEnd, QStringLiteral("dataRangeEnd")}, {Attribute::Report::DataMajorTick, QStringLiteral("dataMajorTick")}, {Attribute::Report::DataMinorTick, QStringLiteral("dataMinorTick")}, {Attribute::Report::YLabelsPrecision, QStringLiteral("yLabelsPrecision")}, {Attribute::Report::QueryColumns, QStringLiteral("querycolumns")}, {Attribute::Report::Tax, QStringLiteral("tax")}, {Attribute::Report::Loans, QStringLiteral("loans")}, {Attribute::Report::HideTransactions, QStringLiteral("hidetransactions")}, {Attribute::Report::InvestmentSum, QStringLiteral("investmentsum")}, {Attribute::Report::SettlementPeriod, QStringLiteral("settlementperiod")}, {Attribute::Report::ShowSTLTCapitalGains, QStringLiteral("showSTLTCapitalGains")}, {Attribute::Report::TermsSeparator, QStringLiteral("tseparator")}, {Attribute::Report::Pattern, QStringLiteral("pattern")}, {Attribute::Report::CaseSensitive, QStringLiteral("casesensitive")}, {Attribute::Report::RegEx, QStringLiteral("regex")}, {Attribute::Report::InvertText, QStringLiteral("inverttext")}, {Attribute::Report::State, QStringLiteral("state")}, {Attribute::Report::From, QStringLiteral("from")}, {Attribute::Report::To, QStringLiteral("to")} }; return attributeNames.value(attributeID); } QString elementName(Element::Budget elementID) { static const QMap elementNames { {Element::Budget::Budget, QStringLiteral("BUDGET")}, {Element::Budget::Account, QStringLiteral("ACCOUNT")}, {Element::Budget::Period, QStringLiteral("PERIOD")} }; return elementNames.value(elementID); } QString attributeName(Attribute::Budget attributeID) { static const QMap attributeNames { {Attribute::Budget::ID, QStringLiteral("id")}, {Attribute::Budget::Name, QStringLiteral("name")}, {Attribute::Budget::Start, QStringLiteral("start")}, {Attribute::Budget::Version, QStringLiteral("version")}, {Attribute::Budget::BudgetLevel, QStringLiteral("budgetlevel")}, {Attribute::Budget::BudgetSubAccounts, QStringLiteral("budgetsubaccounts")}, {Attribute::Budget::Amount, QStringLiteral("amount")} }; return attributeNames.value(attributeID); } QHash rowTypesLUT() { static const QHash lut { {eMyMoney::Report::RowType::NoRows, QStringLiteral("none")}, {eMyMoney::Report::RowType::AssetLiability, QStringLiteral("assetliability")}, {eMyMoney::Report::RowType::ExpenseIncome, QStringLiteral("expenseincome")}, {eMyMoney::Report::RowType::Category, QStringLiteral("category")}, {eMyMoney::Report::RowType::TopCategory, QStringLiteral("topcategory")}, {eMyMoney::Report::RowType::Account, QStringLiteral("account")}, {eMyMoney::Report::RowType::Tag, QStringLiteral("tag")}, {eMyMoney::Report::RowType::Payee, QStringLiteral("payee")}, {eMyMoney::Report::RowType::Month, QStringLiteral("month")}, {eMyMoney::Report::RowType::Week, QStringLiteral("week")}, {eMyMoney::Report::RowType::TopAccount, QStringLiteral("topaccount")}, {eMyMoney::Report::RowType::AccountByTopAccount, QStringLiteral("topaccount-account")}, {eMyMoney::Report::RowType::EquityType, QStringLiteral("equitytype")}, {eMyMoney::Report::RowType::AccountType, QStringLiteral("accounttype")}, {eMyMoney::Report::RowType::Institution, QStringLiteral("institution")}, {eMyMoney::Report::RowType::Budget, QStringLiteral("budget")}, {eMyMoney::Report::RowType::BudgetActual, QStringLiteral("budgetactual")}, {eMyMoney::Report::RowType::Schedule, QStringLiteral("schedule")}, {eMyMoney::Report::RowType::AccountInfo, QStringLiteral("accountinfo")}, {eMyMoney::Report::RowType::AccountLoanInfo, QStringLiteral("accountloaninfo")}, {eMyMoney::Report::RowType::AccountReconcile, QStringLiteral("accountreconcile")}, {eMyMoney::Report::RowType::CashFlow, QStringLiteral("cashflow")}, }; return lut; } QString reportNames(eMyMoney::Report::RowType textID) { return rowTypesLUT().value(textID); } eMyMoney::Report::RowType stringToRowType(const QString &text) { return rowTypesLUT().key(text, eMyMoney::Report::RowType::Invalid); } QHash columTypesLUT() { static const QHash lut { {eMyMoney::Report::ColumnType::NoColumns, QStringLiteral("none")}, {eMyMoney::Report::ColumnType::Months, QStringLiteral("months")}, {eMyMoney::Report::ColumnType::BiMonths, QStringLiteral("bimonths")}, {eMyMoney::Report::ColumnType::Quarters, QStringLiteral("quarters")}, // {eMyMoney::Report::ColumnType::, QStringLiteral("4")} // {eMyMoney::Report::ColumnType::, QStringLiteral("5")} // {eMyMoney::Report::ColumnType::, QStringLiteral("6")} {eMyMoney::Report::ColumnType::Weeks, QStringLiteral("weeks")}, // {eMyMoney::Report::ColumnType::, QStringLiteral("8")} // {eMyMoney::Report::ColumnType::, QStringLiteral("9")} // {eMyMoney::Report::ColumnType::, QStringLiteral("10")} // {eMyMoney::Report::ColumnType::, QStringLiteral("11")} {eMyMoney::Report::ColumnType::Years, QStringLiteral("years")} }; return lut; } QString reportNames(eMyMoney::Report::ColumnType textID) { return columTypesLUT().value(textID); } eMyMoney::Report::ColumnType stringToColumnType(const QString &text) { return columTypesLUT().key(text, eMyMoney::Report::ColumnType::Invalid); } QHash queryColumnsLUT() { static const QHash lut { {eMyMoney::Report::QueryColumn::None, QStringLiteral("none")}, {eMyMoney::Report::QueryColumn::Number, QStringLiteral("number")}, {eMyMoney::Report::QueryColumn::Payee, QStringLiteral("payee")}, {eMyMoney::Report::QueryColumn::Category, QStringLiteral("category")}, {eMyMoney::Report::QueryColumn::Tag, QStringLiteral("tag")}, {eMyMoney::Report::QueryColumn::Memo, QStringLiteral("memo")}, {eMyMoney::Report::QueryColumn::Account, QStringLiteral("account")}, {eMyMoney::Report::QueryColumn::Reconciled, QStringLiteral("reconcileflag")}, {eMyMoney::Report::QueryColumn::Action, QStringLiteral("action")}, {eMyMoney::Report::QueryColumn::Shares, QStringLiteral("shares")}, {eMyMoney::Report::QueryColumn::Price, QStringLiteral("price")}, {eMyMoney::Report::QueryColumn::Performance, QStringLiteral("performance")}, {eMyMoney::Report::QueryColumn::Loan, QStringLiteral("loan")}, {eMyMoney::Report::QueryColumn::Balance, QStringLiteral("balance")}, {eMyMoney::Report::QueryColumn::CapitalGain, QStringLiteral("capitalgain")} }; return lut; } QString reportNamesForQC(eMyMoney::Report::QueryColumn textID) { return queryColumnsLUT().value(textID); } eMyMoney::Report::QueryColumn stringToQueryColumn(const QString &text) { return queryColumnsLUT().key(text, eMyMoney::Report::QueryColumn::End); } QHash detailLevelLUT() { static const QHash lut { {eMyMoney::Report::DetailLevel::None, QStringLiteral("none")}, {eMyMoney::Report::DetailLevel::All, QStringLiteral("all")}, {eMyMoney::Report::DetailLevel::Top, QStringLiteral("top")}, {eMyMoney::Report::DetailLevel::Group, QStringLiteral("group")}, {eMyMoney::Report::DetailLevel::Total, QStringLiteral("total")}, {eMyMoney::Report::DetailLevel::End, QStringLiteral("invalid")} }; return lut; } QString reportNames(eMyMoney::Report::DetailLevel textID) { return detailLevelLUT().value(textID); } eMyMoney::Report::DetailLevel stringToDetailLevel(const QString &text) { return detailLevelLUT().key(text, eMyMoney::Report::DetailLevel::End); } QHash chartTypeLUT() { static const QHash lut { {eMyMoney::Report::ChartType::None, QStringLiteral("none")}, {eMyMoney::Report::ChartType::Line, QStringLiteral("line")}, {eMyMoney::Report::ChartType::Bar, QStringLiteral("bar")}, {eMyMoney::Report::ChartType::Pie, QStringLiteral("pie")}, {eMyMoney::Report::ChartType::Ring, QStringLiteral("ring")}, {eMyMoney::Report::ChartType::StackedBar, QStringLiteral("stackedbar")} }; return lut; } QString reportNames(eMyMoney::Report::ChartType textID) { return chartTypeLUT().value(textID); } eMyMoney::Report::ChartType stringToChartType(const QString &text) { return chartTypeLUT().key(text, eMyMoney::Report::ChartType::End); } QHash typeAttributeLUT() { static const QHash lut { {0, QStringLiteral("all")}, {1, QStringLiteral("payments")}, {2, QStringLiteral("deposits")}, {3, QStringLiteral("transfers")}, {4, QStringLiteral("none")}, }; return lut; } QString typeAttributeToString(int textID) { return typeAttributeLUT().value(textID); } int stringToTypeAttribute(const QString &text) { return typeAttributeLUT().key(text, 4); } QHash stateAttributeLUT() { static const QHash lut { {0, QStringLiteral("all")}, {1, QStringLiteral("notreconciled")}, {2, QStringLiteral("cleared")}, {3, QStringLiteral("reconciled")}, {4, QStringLiteral("frozen")}, {5, QStringLiteral("none")} }; return lut; } QString stateAttributeToString(int textID) { return stateAttributeLUT().value(textID); } int stringToStateAttribute(const QString &text) { return stateAttributeLUT().key(text, 5); } - QHash dateLockLUT() + QHash dateLockLUT() { - static const QHash lut { - {0, QStringLiteral("alldates")}, - {1, QStringLiteral("untiltoday")}, - {2, QStringLiteral("currentmonth")}, - {3, QStringLiteral("currentyear")}, - {4, QStringLiteral("monthtodate")}, - {5, QStringLiteral("yeartodate")}, - {6, QStringLiteral("yeartomonth")}, - {7, QStringLiteral("lastmonth")}, - {8, QStringLiteral("lastyear")}, - {9, QStringLiteral("last7days")}, - {10, QStringLiteral("last30days")}, - {11, QStringLiteral("last3months")}, - {12, QStringLiteral("last6months")}, - {13, QStringLiteral("last12months")}, - {14, QStringLiteral("next7days")}, - {15, QStringLiteral("next30days")}, - {16, QStringLiteral("next3months")}, - {17, QStringLiteral("next6months")}, - {18, QStringLiteral("next12months")}, - {19, QStringLiteral("userdefined")}, - {20, QStringLiteral("last3tonext3months")}, - {21, QStringLiteral("last11Months")}, - {22, QStringLiteral("currentQuarter")}, - {23, QStringLiteral("lastQuarter")}, - {24, QStringLiteral("nextQuarter")}, - {25, QStringLiteral("currentFiscalYear")}, - {26, QStringLiteral("lastFiscalYear")}, - {27, QStringLiteral("today")}, - {28, QStringLiteral("next18months")} + static const QHash lut { + {eMyMoney::TransactionFilter::Date::All, QStringLiteral("alldates")}, + {eMyMoney::TransactionFilter::Date::AsOfToday, QStringLiteral("untiltoday")}, + {eMyMoney::TransactionFilter::Date::CurrentMonth, QStringLiteral("currentmonth")}, + {eMyMoney::TransactionFilter::Date::CurrentYear, QStringLiteral("currentyear")}, + {eMyMoney::TransactionFilter::Date::MonthToDate, QStringLiteral("monthtodate")}, + {eMyMoney::TransactionFilter::Date::YearToDate, QStringLiteral("yeartodate")}, + {eMyMoney::TransactionFilter::Date::YearToMonth, QStringLiteral("yeartomonth")}, + {eMyMoney::TransactionFilter::Date::LastMonth, QStringLiteral("lastmonth")}, + {eMyMoney::TransactionFilter::Date::LastYear, QStringLiteral("lastyear")}, + {eMyMoney::TransactionFilter::Date::Last7Days, QStringLiteral("last7days")}, + {eMyMoney::TransactionFilter::Date::Last30Days, QStringLiteral("last30days")}, + {eMyMoney::TransactionFilter::Date::Last3Months, QStringLiteral("last3months")}, + {eMyMoney::TransactionFilter::Date::Last6Months, QStringLiteral("last6months")}, + {eMyMoney::TransactionFilter::Date::Last12Months, QStringLiteral("last12months")}, + {eMyMoney::TransactionFilter::Date::Next7Days, QStringLiteral("next7days")}, + {eMyMoney::TransactionFilter::Date::Next30Days, QStringLiteral("next30days")}, + {eMyMoney::TransactionFilter::Date::Next3Months, QStringLiteral("next3months")}, + {eMyMoney::TransactionFilter::Date::Next6Months, QStringLiteral("next6months")}, + {eMyMoney::TransactionFilter::Date::Next12Months, QStringLiteral("next12months")}, + {eMyMoney::TransactionFilter::Date::UserDefined, QStringLiteral("userdefined")}, + {eMyMoney::TransactionFilter::Date::Last3ToNext3Months, QStringLiteral("last3tonext3months")}, + {eMyMoney::TransactionFilter::Date::Last11Months, QStringLiteral("last11Months")}, + {eMyMoney::TransactionFilter::Date::CurrentQuarter, QStringLiteral("currentQuarter")}, + {eMyMoney::TransactionFilter::Date::LastQuarter, QStringLiteral("lastQuarter")}, + {eMyMoney::TransactionFilter::Date::NextQuarter, QStringLiteral("nextQuarter")}, + {eMyMoney::TransactionFilter::Date::CurrentFiscalYear, QStringLiteral("currentFiscalYear")}, + {eMyMoney::TransactionFilter::Date::LastFiscalYear, QStringLiteral("lastFiscalYear")}, + {eMyMoney::TransactionFilter::Date::Today, QStringLiteral("today")}, + {eMyMoney::TransactionFilter::Date::Next18Months, QStringLiteral("next18months")} }; return lut; } - QString dateLockAttributeToString(int textID) + QString dateLockAttributeToString(eMyMoney::TransactionFilter::Date textID) { return dateLockLUT().value(textID); } - int stringToDateLockAttribute(const QString &text) + eMyMoney::TransactionFilter::Date stringToDateLockAttribute(const QString &text) { - return dateLockLUT().key(text, 0); + return dateLockLUT().key(text, eMyMoney::TransactionFilter::Date::UserDefined); } QHash dataLockLUT() { static const QHash lut { {eMyMoney::Report::DataLock::Automatic, QStringLiteral("automatic")}, {eMyMoney::Report::DataLock::UserDefined, QStringLiteral("userdefined")} }; return lut; } QString reportNames(eMyMoney::Report::DataLock textID) { return dataLockLUT().value(textID); } eMyMoney::Report::DataLock stringToDataLockAttribute(const QString &text) { return dataLockLUT().key(text, eMyMoney::Report::DataLock::DataOptionCount); } - QHash accountTypeAttributeLUT() + QHash accountTypeAttributeLUT() { - static const QHash lut { - {0, QStringLiteral("unknown")}, - {1, QStringLiteral("checkings")}, - {2, QStringLiteral("savings")}, - {3, QStringLiteral("cash")}, - {4, QStringLiteral("creditcard")}, - {5, QStringLiteral("loan")}, - {6, QStringLiteral("certificatedep")}, - {7, QStringLiteral("investment")}, - {8, QStringLiteral("moneymarket")}, - {10, QStringLiteral("asset")}, - {11, QStringLiteral("liability")}, - {12, QStringLiteral("currency")}, - {13, QStringLiteral("income")}, - {14, QStringLiteral("expense")}, - {15, QStringLiteral("assetloan")}, - {16, QStringLiteral("stock")}, - {17, QStringLiteral("equity")}, - {18, QStringLiteral("invalid")} + static const QHash lut { + {eMyMoney::Account::Type::Unknown, QStringLiteral("unknown")}, + {eMyMoney::Account::Type::Checkings, QStringLiteral("checkings")}, + {eMyMoney::Account::Type::Savings, QStringLiteral("savings")}, + {eMyMoney::Account::Type::Cash, QStringLiteral("cash")}, + {eMyMoney::Account::Type::CreditCard, QStringLiteral("creditcard")}, + {eMyMoney::Account::Type::Loan, QStringLiteral("loan")}, + {eMyMoney::Account::Type::CertificateDep, QStringLiteral("certificatedep")}, + {eMyMoney::Account::Type::Investment, QStringLiteral("investment")}, + {eMyMoney::Account::Type::MoneyMarket, QStringLiteral("moneymarket")}, + {eMyMoney::Account::Type::Asset, QStringLiteral("asset")}, + {eMyMoney::Account::Type::Liability, QStringLiteral("liability")}, + {eMyMoney::Account::Type::Currency, QStringLiteral("currency")}, + {eMyMoney::Account::Type::Income, QStringLiteral("income")}, + {eMyMoney::Account::Type::Expense, QStringLiteral("expense")}, + {eMyMoney::Account::Type::AssetLoan, QStringLiteral("assetloan")}, + {eMyMoney::Account::Type::Stock, QStringLiteral("stock")}, + {eMyMoney::Account::Type::Equity, QStringLiteral("equity")}, }; return lut; } - QString accountTypeAttributeToString(int textID) + QString accountTypeAttributeToString(eMyMoney::Account::Type type) { - return accountTypeAttributeLUT().value(textID); + return accountTypeAttributeLUT().value(type); } - int stringToAccountTypeAttribute(const QString &text) + eMyMoney::Account::Type stringToAccountTypeAttribute(const QString &text) { - return accountTypeAttributeLUT().key(text, 0); + return accountTypeAttributeLUT().key(text, eMyMoney::Account::Type::Unknown); } eMyMoney::Report::ReportType rowTypeToReportType(eMyMoney::Report::RowType rowType) { static const QHash reportTypes { {eMyMoney::Report::RowType::NoRows, eMyMoney::Report::ReportType::NoReport}, {eMyMoney::Report::RowType::AssetLiability, eMyMoney::Report::ReportType::PivotTable}, {eMyMoney::Report::RowType::ExpenseIncome, eMyMoney::Report::ReportType::PivotTable}, {eMyMoney::Report::RowType::Category, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::TopCategory, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::Account, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::Tag, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::Payee, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::Month, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::Week, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::TopAccount, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::AccountByTopAccount, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::EquityType, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::AccountType, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::Institution, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::Budget, eMyMoney::Report::ReportType::PivotTable}, {eMyMoney::Report::RowType::BudgetActual, eMyMoney::Report::ReportType::PivotTable}, {eMyMoney::Report::RowType::Schedule, eMyMoney::Report::ReportType::InfoTable}, {eMyMoney::Report::RowType::AccountInfo, eMyMoney::Report::ReportType::InfoTable}, {eMyMoney::Report::RowType::AccountLoanInfo, eMyMoney::Report::ReportType::InfoTable}, {eMyMoney::Report::RowType::AccountReconcile, eMyMoney::Report::ReportType::QueryTable}, {eMyMoney::Report::RowType::CashFlow, eMyMoney::Report::ReportType::QueryTable}, }; return reportTypes.value(rowType, eMyMoney::Report::ReportType::Invalid); } QHash budgetLevelLUT() { static const QHash lut { {eMyMoney::Budget::Level::None, QStringLiteral("none")}, {eMyMoney::Budget::Level::Monthly, QStringLiteral("monthly")}, {eMyMoney::Budget::Level::MonthByMonth, QStringLiteral("monthbymonth")}, {eMyMoney::Budget::Level::Yearly, QStringLiteral("yearly")}, {eMyMoney::Budget::Level::Max, QStringLiteral("invalid")}, }; return lut; } QString budgetNames(eMyMoney::Budget::Level textID) { return budgetLevelLUT().value(textID); } eMyMoney::Budget::Level stringToBudgetLevel(const QString &text) { return budgetLevelLUT().key(text, eMyMoney::Budget::Level::Max); } QHash budgetLevelsLUT() { static const QHash lut { {eMyMoney::Budget::Level::None, QStringLiteral("none")}, {eMyMoney::Budget::Level::Monthly, QStringLiteral("monthly")}, {eMyMoney::Budget::Level::MonthByMonth, QStringLiteral("monthbymonth")}, {eMyMoney::Budget::Level::Yearly, QStringLiteral("yearly")}, {eMyMoney::Budget::Level::Max, QStringLiteral("invalid")}, }; return lut; } QString budgetLevels(eMyMoney::Budget::Level textID) { return budgetLevelsLUT().value(textID); } void writeBaseXML(const QString &id, QDomDocument &document, QDomElement &el) { Q_UNUSED(document); el.setAttribute(QStringLiteral("id"), id); } MyMoneyReport readReport(const QDomElement &node) { if (nodeName(Node::Report) != node.tagName()) throw MYMONEYEXCEPTION_CSTRING("Node was not REPORT"); MyMoneyReport report(node.attribute(attributeName(Attribute::Report::ID))); // The goal of this reading method is 100% backward AND 100% forward // compatibility. Any report ever created with any version of KMyMoney // should be able to be loaded by this method (as long as it's one of the // report types supported in this version, of course) // read report's internals QString type = node.attribute(attributeName(Attribute::Report::Type)); if (type.startsWith(QLatin1String("pivottable"))) report.setReportType(eMyMoney::Report::ReportType::PivotTable); else if (type.startsWith(QLatin1String("querytable"))) report.setReportType(eMyMoney::Report::ReportType::QueryTable); else if (type.startsWith(QLatin1String("infotable"))) report.setReportType(eMyMoney::Report::ReportType::InfoTable); else throw MYMONEYEXCEPTION_CSTRING("Unknown report type"); report.setGroup(node.attribute(attributeName(Attribute::Report::Group))); report.clearTransactionFilter(); // read date tab QString datelockstr = node.attribute(attributeName(Attribute::Report::DateLock), "userdefined"); // Handle the pivot 1.2/query 1.1 case where the values were saved as // numbers bool ok = false; - int i = datelockstr.toUInt(&ok); + eMyMoney::TransactionFilter::Date dateLock = static_cast(datelockstr.toUInt(&ok)); if (!ok) { - i = stringToDateLockAttribute(datelockstr); - if (i == -1) - i = (int)eMyMoney::TransactionFilter::Date::UserDefined; + dateLock = stringToDateLockAttribute(datelockstr); } - report.setDateFilter(static_cast(i)); + report.setDateFilter(dateLock); // read general tab report.setName(node.attribute(attributeName(Attribute::Report::Name))); report.setComment(node.attribute(attributeName(Attribute::Report::Comment), "Extremely old report")); report.setConvertCurrency(node.attribute(attributeName(Attribute::Report::ConvertCurrency), "1").toUInt()); report.setFavorite(node.attribute(attributeName(Attribute::Report::Favorite), "0").toUInt()); report.setSkipZero(node.attribute(attributeName(Attribute::Report::SkipZero), "0").toUInt()); + const auto rowTypeFromXML = stringToRowType(node.attribute(attributeName(Attribute::Report::RowType))); if (report.reportType() == eMyMoney::Report::ReportType::PivotTable) { // read report's internals report.setIncludingBudgetActuals(node.attribute(attributeName(Attribute::Report::IncludesActuals), "0").toUInt()); report.setIncludingForecast(node.attribute(attributeName(Attribute::Report::IncludesForecast), "0").toUInt()); report.setIncludingPrice(node.attribute(attributeName(Attribute::Report::IncludesPrice), "0").toUInt()); report.setIncludingAveragePrice(node.attribute(attributeName(Attribute::Report::IncludesAveragePrice), "0").toUInt()); report.setMixedTime(node.attribute(attributeName(Attribute::Report::MixedTime), "0").toUInt()); report.setInvestmentsOnly(node.attribute(attributeName(Attribute::Report::Investments), "0").toUInt()); // read rows/columns tab if (node.hasAttribute(attributeName(Attribute::Report::Budget))) report.setBudget(node.attribute(attributeName(Attribute::Report::Budget))); - const auto rowTypeFromXML = stringToRowType(node.attribute(attributeName(Attribute::Report::RowType))); if (rowTypeFromXML != eMyMoney::Report::RowType::Invalid) report.setRowType(rowTypeFromXML); else report.setRowType(eMyMoney::Report::RowType::ExpenseIncome); if (node.hasAttribute(attributeName(Attribute::Report::ShowRowTotals))) report.setShowingRowTotals(node.attribute(attributeName(Attribute::Report::ShowRowTotals)).toUInt()); else if (report.rowType() == eMyMoney::Report::RowType::ExpenseIncome) // for backward compatibility report.setShowingRowTotals(true); report.setShowingColumnTotals(node.attribute(attributeName(Attribute::Report::ShowColumnTotals), "1").toUInt()); //check for reports with older settings which didn't have the detail attribute const auto detailLevelFromXML = stringToDetailLevel(node.attribute(attributeName(Attribute::Report::Detail))); if (detailLevelFromXML != eMyMoney::Report::DetailLevel::End) report.setDetailLevel(detailLevelFromXML); else report.setDetailLevel(eMyMoney::Report::DetailLevel::All); report.setIncludingMovingAverage(node.attribute(attributeName(Attribute::Report::IncludesMovingAverage), "0").toUInt()); if (report.isIncludingMovingAverage()) report.setMovingAverageDays(node.attribute(attributeName(Attribute::Report::MovingAverageDays), "1").toUInt()); report.setIncludingSchedules(node.attribute(attributeName(Attribute::Report::IncludesSchedules), "0").toUInt()); report.setIncludingTransfers(node.attribute(attributeName(Attribute::Report::IncludesTransfers), "0").toUInt()); report.setIncludingUnusedAccounts(node.attribute(attributeName(Attribute::Report::IncludesUnused), "0").toUInt()); report.setColumnsAreDays(node.attribute(attributeName(Attribute::Report::ColumnsAreDays), "0").toUInt()); // read chart tab const auto chartTypeFromXML = stringToChartType(node.attribute(attributeName(Attribute::Report::ChartType))); if (chartTypeFromXML != eMyMoney::Report::ChartType::End) report.setChartType(chartTypeFromXML); else report.setChartType(eMyMoney::Report::ChartType::None); report.setChartCHGridLines(node.attribute(attributeName(Attribute::Report::ChartCHGridLines), "1").toUInt()); report.setChartSVGridLines(node.attribute(attributeName(Attribute::Report::ChartSVGridLines), "1").toUInt()); report.setChartDataLabels(node.attribute(attributeName(Attribute::Report::ChartDataLabels), "1").toUInt()); report.setChartByDefault(node.attribute(attributeName(Attribute::Report::ChartByDefault), "0").toUInt()); report.setLogYAxis(node.attribute(attributeName(Attribute::Report::LogYAxis), "0").toUInt()); - report.setChartLineWidth(node.attribute(attributeName(Attribute::Report::ChartLineWidth), QString(MyMoneyReport::m_lineWidth)).toUInt()); + report.setChartLineWidth(node.attribute(attributeName(Attribute::Report::ChartLineWidth), QString(MyMoneyReport::lineWidth())).toUInt()); // read range tab const auto columnTypeFromXML = stringToColumnType(node.attribute(attributeName(Attribute::Report::ColumnType))); if (columnTypeFromXML != eMyMoney::Report::ColumnType::Invalid) report.setColumnType(columnTypeFromXML); else report.setColumnType(eMyMoney::Report::ColumnType::Months); const auto dataLockFromXML = stringToDataLockAttribute(node.attribute(attributeName(Attribute::Report::DataLock))); if (dataLockFromXML != eMyMoney::Report::DataLock::DataOptionCount) report.setDataFilter(dataLockFromXML); else report.setDataFilter(eMyMoney::Report::DataLock::Automatic); report.setDataRangeStart(node.attribute(attributeName(Attribute::Report::DataRangeStart), "0")); report.setDataRangeEnd(node.attribute(attributeName(Attribute::Report::DataRangeEnd), "0")); report.setDataMajorTick(node.attribute(attributeName(Attribute::Report::DataMajorTick), "0")); report.setDataMinorTick(node.attribute(attributeName(Attribute::Report::DataMinorTick), "0")); report.setYLabelsPrecision(node.attribute(attributeName(Attribute::Report::YLabelsPrecision), "2").toUInt()); } else if (report.reportType() == eMyMoney::Report::ReportType::QueryTable) { // read rows/columns tab - const auto rowTypeFromXML = stringToRowType(node.attribute(attributeName(Attribute::Report::RowType))); if (rowTypeFromXML != eMyMoney::Report::RowType::Invalid) report.setRowType(rowTypeFromXML); else report.setRowType(eMyMoney::Report::RowType::Account); unsigned qc = 0; QStringList columns = node.attribute(attributeName(Attribute::Report::QueryColumns), "none").split(','); foreach (const auto column, columns) { const int queryColumnFromXML = stringToQueryColumn(column); - i = stringToQueryColumn(column); if (queryColumnFromXML != eMyMoney::Report::QueryColumn::End) qc |= queryColumnFromXML; } report.setQueryColumns(static_cast(qc)); report.setTax(node.attribute(attributeName(Attribute::Report::Tax), "0").toUInt()); report.setInvestmentsOnly(node.attribute(attributeName(Attribute::Report::Investments), "0").toUInt()); report.setLoansOnly(node.attribute(attributeName(Attribute::Report::Loans), "0").toUInt()); report.setHideTransactions(node.attribute(attributeName(Attribute::Report::HideTransactions), "0").toUInt()); report.setShowingColumnTotals(node.attribute(attributeName(Attribute::Report::ShowColumnTotals), "1").toUInt()); const auto detailLevelFromXML = stringToDetailLevel(node.attribute(attributeName(Attribute::Report::Detail), "none")); if (detailLevelFromXML == eMyMoney::Report::DetailLevel::All) report.setDetailLevel(detailLevelFromXML); else report.setDetailLevel(eMyMoney::Report::DetailLevel::None); // read performance or capital gains tab if (report.queryColumns() & eMyMoney::Report::QueryColumn::Performance) report.setInvestmentSum(static_cast(node.attribute(attributeName(Attribute::Report::InvestmentSum), QString::number(static_cast(eMyMoney::Report::InvestmentSum::Period))).toInt())); // read capital gains tab if (report.queryColumns() & eMyMoney::Report::QueryColumn::CapitalGain) { report.setInvestmentSum(static_cast(node.attribute(attributeName(Attribute::Report::InvestmentSum), QString::number(static_cast(eMyMoney::Report::InvestmentSum::Sold))).toInt())); if (report.investmentSum() == eMyMoney::Report::InvestmentSum::Sold) { report.setShowSTLTCapitalGains(node.attribute(attributeName(Attribute::Report::ShowSTLTCapitalGains), "0").toUInt()); report.setSettlementPeriod(node.attribute(attributeName(Attribute::Report::SettlementPeriod), "3").toUInt()); report.setTermSeparator(QDate::fromString(node.attribute(attributeName(Attribute::Report::TermsSeparator), QDate::currentDate().addYears(-1).toString(Qt::ISODate)),Qt::ISODate)); } } } else if (report.reportType() == eMyMoney::Report::ReportType::InfoTable) { + if (rowTypeFromXML != eMyMoney::Report::RowType::Invalid) + report.setRowType(rowTypeFromXML); + else + report.setRowType(eMyMoney::Report::RowType::AccountInfo); + if (node.hasAttribute(attributeName(Attribute::Report::ShowRowTotals))) report.setShowingRowTotals(node.attribute(attributeName(Attribute::Report::ShowRowTotals)).toUInt()); else report.setShowingRowTotals(true); } QDomNode child = node.firstChild(); while (!child.isNull() && child.isElement()) { QDomElement c = child.toElement(); if (elementName(Element::Report::Text) == c.tagName() && c.hasAttribute(attributeName(Attribute::Report::Pattern))) { report.setTextFilter(QRegExp(c.attribute(attributeName(Attribute::Report::Pattern)), c.attribute(attributeName(Attribute::Report::CaseSensitive), "1").toUInt() ? Qt::CaseSensitive : Qt::CaseInsensitive, c.attribute(attributeName(Attribute::Report::RegEx), "1").toUInt() ? QRegExp::Wildcard : QRegExp::RegExp), c.attribute(attributeName(Attribute::Report::InvertText), "0").toUInt()); } if (elementName(Element::Report::Type) == c.tagName() && c.hasAttribute(attributeName(Attribute::Report::Type))) { - i = stringToTypeAttribute(c.attribute(attributeName(Attribute::Report::Type))); - if (i != -1) - report.addType(i); + const auto reportType = stringToTypeAttribute(c.attribute(attributeName(Attribute::Report::Type))); + if (reportType != -1) + report.addType(reportType); } if (elementName(Element::Report::State) == c.tagName() && c.hasAttribute(attributeName(Attribute::Report::State))) { - i = stringToStateAttribute(c.attribute(attributeName(Attribute::Report::State))); - if (i != -1) - report.addState(i); + const auto state = stringToStateAttribute(c.attribute(attributeName(Attribute::Report::State))); + if (state != -1) + report.addState(state); } if (elementName(Element::Report::Number) == c.tagName()) report.setNumberFilter(c.attribute(attributeName(Attribute::Report::From)), c.attribute(attributeName(Attribute::Report::To))); if (elementName(Element::Report::Amount) == c.tagName()) report.setAmountFilter(MyMoneyMoney(c.attribute(attributeName(Attribute::Report::From), "0/100")), MyMoneyMoney(c.attribute(attributeName(Attribute::Report::To), "0/100"))); if (elementName(Element::Report::Dates) == c.tagName()) { QDate from, to; if (c.hasAttribute(attributeName(Attribute::Report::From))) from = QDate::fromString(c.attribute(attributeName(Attribute::Report::From)), Qt::ISODate); if (c.hasAttribute(attributeName(Attribute::Report::To))) to = QDate::fromString(c.attribute(attributeName(Attribute::Report::To)), Qt::ISODate); report.setDateFilter(from, to); } if (elementName(Element::Report::Payee) == c.tagName()) report.addPayee(c.attribute(attributeName(Attribute::Report::ID))); if (elementName(Element::Report::Tag) == c.tagName()) report.addTag(c.attribute(attributeName(Attribute::Report::ID))); if (elementName(Element::Report::Category) == c.tagName() && c.hasAttribute(attributeName(Attribute::Report::ID))) report.addCategory(c.attribute(attributeName(Attribute::Report::ID))); if (elementName(Element::Report::Account) == c.tagName() && c.hasAttribute(attributeName(Attribute::Report::ID))) report.addAccount(c.attribute(attributeName(Attribute::Report::ID))); +#if 0 + // account groups had a severe problem in versions 5.0.0 to 5.0.2. Therefor, we don't read them + // in anymore and rebuild them internally. They are written to the file nevertheless to maintain + // compatibility to older versions which rely on them. I left the old code for reference here + // ipwizard - 2019-01-13 if (elementName(Element::Report::AccountGroup) == c.tagName() && c.hasAttribute(attributeName(Attribute::Report::Group))) { - i = stringToAccountTypeAttribute(c.attribute(attributeName(Attribute::Report::Group))); - if (i != -1) - report.addAccountGroup(static_cast(i)); + const auto groupType = stringToAccountTypeAttribute(c.attribute(attributeName(Attribute::Report::Group))); + if (groupType != eMyMoney::Account::Type::Unknown) + report.addAccountGroup(groupType); } +#endif child = child.nextSibling(); } return report; } void writeReport(const MyMoneyReport &report, QDomDocument &document, QDomElement &parent) { auto el = document.createElement(nodeName(Node::Report)); // No matter what changes, be sure to have a 'type' attribute. Only change // the major type if it becomes impossible to maintain compatibility with // older versions of the program as new features are added to the reports. // Feel free to change the minor type every time a change is made here. // write report's internals if (report.reportType() == eMyMoney::Report::ReportType::PivotTable) el.setAttribute(attributeName(Attribute::Report::Type), "pivottable 1.15"); else if (report.reportType() == eMyMoney::Report::ReportType::QueryTable) el.setAttribute(attributeName(Attribute::Report::Type), "querytable 1.14"); else if (report.reportType() == eMyMoney::Report::ReportType::InfoTable) el.setAttribute(attributeName(Attribute::Report::Type), "infotable 1.0"); el.setAttribute(attributeName(Attribute::Report::Group), report.group()); el.setAttribute(attributeName(Attribute::Report::ID), report.id()); // write general tab auto anonymous = false; if (anonymous) { el.setAttribute(attributeName(Attribute::Report::Name), report.id()); el.setAttribute(attributeName(Attribute::Report::Comment), QString(report.comment()).fill('x')); } else { el.setAttribute(attributeName(Attribute::Report::Name), report.name()); el.setAttribute(attributeName(Attribute::Report::Comment), report.comment()); } el.setAttribute(attributeName(Attribute::Report::ConvertCurrency), report.isConvertCurrency()); el.setAttribute(attributeName(Attribute::Report::Favorite), report.isFavorite()); el.setAttribute(attributeName(Attribute::Report::SkipZero), report.isSkippingZero()); - el.setAttribute(attributeName(Attribute::Report::DateLock), dateLockAttributeToString(static_cast(report.dateRange()))); + el.setAttribute(attributeName(Attribute::Report::DateLock), dateLockAttributeToString(report.dateRange())); + el.setAttribute(attributeName(Attribute::Report::RowType), reportNames(report.rowType())); if (report.reportType() == eMyMoney::Report::ReportType::PivotTable) { // write report's internals el.setAttribute(attributeName(Attribute::Report::IncludesActuals), report.isIncludingBudgetActuals()); el.setAttribute(attributeName(Attribute::Report::IncludesForecast), report.isIncludingForecast()); el.setAttribute(attributeName(Attribute::Report::IncludesPrice), report.isIncludingPrice()); el.setAttribute(attributeName(Attribute::Report::IncludesAveragePrice), report.isIncludingAveragePrice()); el.setAttribute(attributeName(Attribute::Report::MixedTime), report.isMixedTime()); el.setAttribute(attributeName(Attribute::Report::Investments), report.isInvestmentsOnly()); // it's setable in rows/columns tab of querytable, but here it is internal setting // write rows/columns tab if (!report.budget().isEmpty()) el.setAttribute(attributeName(Attribute::Report::Budget), report.budget()); - el.setAttribute(attributeName(Attribute::Report::RowType), reportNames(report.rowType())); el.setAttribute(attributeName(Attribute::Report::ShowRowTotals), report.isShowingRowTotals()); el.setAttribute(attributeName(Attribute::Report::ShowColumnTotals), report.isShowingColumnTotals()); el.setAttribute(attributeName(Attribute::Report::Detail), reportNames(report.detailLevel())); el.setAttribute(attributeName(Attribute::Report::IncludesMovingAverage), report.isIncludingMovingAverage()); if (report.isIncludingMovingAverage()) el.setAttribute(attributeName(Attribute::Report::MovingAverageDays), report.movingAverageDays()); el.setAttribute(attributeName(Attribute::Report::IncludesSchedules), report.isIncludingSchedules()); el.setAttribute(attributeName(Attribute::Report::IncludesTransfers), report.isIncludingTransfers()); el.setAttribute(attributeName(Attribute::Report::IncludesUnused), report.isIncludingUnusedAccounts()); el.setAttribute(attributeName(Attribute::Report::ColumnsAreDays), report.isColumnsAreDays()); el.setAttribute(attributeName(Attribute::Report::ChartType), reportNames(report.chartType())); el.setAttribute(attributeName(Attribute::Report::ChartCHGridLines), report.isChartCHGridLines()); el.setAttribute(attributeName(Attribute::Report::ChartSVGridLines), report.isChartSVGridLines()); el.setAttribute(attributeName(Attribute::Report::ChartDataLabels), report.isChartDataLabels()); el.setAttribute(attributeName(Attribute::Report::ChartByDefault), report.isChartByDefault()); el.setAttribute(attributeName(Attribute::Report::LogYAxis), report.isLogYAxis()); el.setAttribute(attributeName(Attribute::Report::ChartLineWidth), report.chartLineWidth()); el.setAttribute(attributeName(Attribute::Report::ColumnType), reportNames(report.columnType())); el.setAttribute(attributeName(Attribute::Report::DataLock), reportNames(report.dataFilter())); el.setAttribute(attributeName(Attribute::Report::DataRangeStart), report.dataRangeStart()); el.setAttribute(attributeName(Attribute::Report::DataRangeEnd), report.dataRangeEnd()); el.setAttribute(attributeName(Attribute::Report::DataMajorTick), report.dataMajorTick()); el.setAttribute(attributeName(Attribute::Report::DataMinorTick), report.dataMinorTick()); el.setAttribute(attributeName(Attribute::Report::YLabelsPrecision), report.yLabelsPrecision()); + } else if (report.reportType() == eMyMoney::Report::ReportType::QueryTable) { // write rows/columns tab - el.setAttribute(attributeName(Attribute::Report::RowType), reportNames(report.rowType())); QStringList columns; unsigned qc = report.queryColumns(); unsigned it_qc = eMyMoney::Report::QueryColumn::Begin; unsigned index = 1; while (it_qc != eMyMoney::Report::QueryColumn::End) { if (qc & it_qc) columns += reportNamesForQC(static_cast(it_qc)); it_qc *= 2; index++; } el.setAttribute(attributeName(Attribute::Report::QueryColumns), columns.join(",")); el.setAttribute(attributeName(Attribute::Report::Tax), report.isTax()); el.setAttribute(attributeName(Attribute::Report::Investments), report.isInvestmentsOnly()); el.setAttribute(attributeName(Attribute::Report::Loans), report.isLoansOnly()); el.setAttribute(attributeName(Attribute::Report::HideTransactions), report.isHideTransactions()); el.setAttribute(attributeName(Attribute::Report::ShowColumnTotals), report.isShowingColumnTotals()); el.setAttribute(attributeName(Attribute::Report::Detail), reportNames(report.detailLevel())); // write performance tab if (report.queryColumns() & eMyMoney::Report::QueryColumn::Performance || report.queryColumns() & eMyMoney::Report::QueryColumn::CapitalGain) el.setAttribute(attributeName(Attribute::Report::InvestmentSum), static_cast(report.investmentSum())); // write capital gains tab if (report.queryColumns() & eMyMoney::Report::QueryColumn::CapitalGain) { if (report.investmentSum() == eMyMoney::Report::InvestmentSum::Sold) { el.setAttribute(attributeName(Attribute::Report::SettlementPeriod), report.settlementPeriod()); el.setAttribute(attributeName(Attribute::Report::ShowSTLTCapitalGains), report.isShowingSTLTCapitalGains()); el.setAttribute(attributeName(Attribute::Report::TermsSeparator), report.termSeparator().toString(Qt::ISODate)); } } } else if (report.reportType() == eMyMoney::Report::ReportType::InfoTable) el.setAttribute(attributeName(Attribute::Report::ShowRowTotals), report.isShowingRowTotals()); // // Text Filter // QRegExp textfilter; if (report.textFilter(textfilter)) { QDomElement f = document.createElement(elementName(Element::Report::Text)); f.setAttribute(attributeName(Attribute::Report::Pattern), textfilter.pattern()); f.setAttribute(attributeName(Attribute::Report::CaseSensitive), (textfilter.caseSensitivity() == Qt::CaseSensitive) ? 1 : 0); f.setAttribute(attributeName(Attribute::Report::RegEx), (textfilter.patternSyntax() == QRegExp::Wildcard) ? 1 : 0); f.setAttribute(attributeName(Attribute::Report::InvertText), report.MyMoneyTransactionFilter::isInvertingText()); el.appendChild(f); } // // Type & State Filters // QList typelist; if (report.types(typelist) && ! typelist.empty()) { // iterate over payees, and add each one QList::const_iterator it_type = typelist.constBegin(); while (it_type != typelist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::Type)); p.setAttribute(attributeName(Attribute::Report::Type), typeAttributeToString(*it_type)); el.appendChild(p); ++it_type; } } QList statelist; if (report.states(statelist) && ! statelist.empty()) { // iterate over payees, and add each one QList::const_iterator it_state = statelist.constBegin(); while (it_state != statelist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::State)); p.setAttribute(attributeName(Attribute::Report::State), stateAttributeToString(*it_state)); el.appendChild(p); ++it_state; } } // // Number Filter // QString nrFrom, nrTo; if (report.numberFilter(nrFrom, nrTo)) { QDomElement f = document.createElement(elementName(Element::Report::Number)); f.setAttribute(attributeName(Attribute::Report::From), nrFrom); f.setAttribute(attributeName(Attribute::Report::To), nrTo); el.appendChild(f); } // // Amount Filter // MyMoneyMoney from, to; if (report.amountFilter(from, to)) { // bool getAmountFilter(MyMoneyMoney&,MyMoneyMoney&); QDomElement f = document.createElement(elementName(Element::Report::Amount)); f.setAttribute(attributeName(Attribute::Report::From), from.toString()); f.setAttribute(attributeName(Attribute::Report::To), to.toString()); el.appendChild(f); } // // Payees Filter // QStringList payeelist; if (report.payees(payeelist)) { if (payeelist.empty()) { QDomElement p = document.createElement(elementName(Element::Report::Payee)); el.appendChild(p); } else { // iterate over payees, and add each one QStringList::const_iterator it_payee = payeelist.constBegin(); while (it_payee != payeelist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::Payee)); p.setAttribute(attributeName(Attribute::Report::ID), *it_payee); el.appendChild(p); ++it_payee; } } } // // Tags Filter // QStringList taglist; if (report.tags(taglist)) { if (taglist.empty()) { QDomElement p = document.createElement(elementName(Element::Report::Tag)); el.appendChild(p); } else { // iterate over tags, and add each one QStringList::const_iterator it_tag = taglist.constBegin(); while (it_tag != taglist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::Tag)); p.setAttribute(attributeName(Attribute::Report::ID), *it_tag); el.appendChild(p); ++it_tag; } } } // // Account Groups Filter // QList accountgrouplist; if (report.accountGroups(accountgrouplist)) { // iterate over accounts, and add each one QList::const_iterator it_group = accountgrouplist.constBegin(); while (it_group != accountgrouplist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::AccountGroup)); - p.setAttribute(attributeName(Attribute::Report::Group), accountTypeAttributeToString(static_cast(*it_group))); + p.setAttribute(attributeName(Attribute::Report::Group), accountTypeAttributeToString(*it_group)); el.appendChild(p); ++it_group; } } // // Accounts Filter // QStringList accountlist; if (report.accounts(accountlist)) { // iterate over accounts, and add each one QStringList::const_iterator it_account = accountlist.constBegin(); while (it_account != accountlist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::Account)); p.setAttribute(attributeName(Attribute::Report::ID), *it_account); el.appendChild(p); ++it_account; } } // // Categories Filter // accountlist.clear(); if (report.categories(accountlist)) { // iterate over accounts, and add each one QStringList::const_iterator it_account = accountlist.constBegin(); while (it_account != accountlist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::Category)); p.setAttribute(attributeName(Attribute::Report::ID), *it_account); el.appendChild(p); ++it_account; } } // // Date Filter // if (report.dateRange() == eMyMoney::TransactionFilter::Date::UserDefined) { QDate dateFrom, dateTo; if (report.dateFilter(dateFrom, dateTo)) { QDomElement f = document.createElement(elementName(Element::Report::Dates)); if (dateFrom.isValid()) f.setAttribute(attributeName(Attribute::Report::From), dateFrom.toString(Qt::ISODate)); if (dateTo.isValid()) f.setAttribute(attributeName(Attribute::Report::To), dateTo.toString(Qt::ISODate)); el.appendChild(f); } } parent.appendChild(el); } MyMoneyBudget readBudget(const QDomElement &node) { if (nodeName(Node::Budget) != node.tagName()) throw MYMONEYEXCEPTION_CSTRING("Node was not BUDGET"); MyMoneyBudget budget(node.attribute(QStringLiteral("id"))); // The goal of this reading method is 100% backward AND 100% forward // compatibility. Any Budget ever created with any version of KMyMoney // should be able to be loaded by this method (as long as it's one of the // Budget types supported in this version, of course) budget.setName(node.attribute(attributeName(Attribute::Budget::Name))); budget.setBudgetStart(QDate::fromString(node.attribute(attributeName(Attribute::Budget::Start)), Qt::ISODate)); QDomNode child = node.firstChild(); while (!child.isNull() && child.isElement()) { QDomElement c = child.toElement(); MyMoneyBudget::AccountGroup account; if (elementName(Element::Budget::Account) == c.tagName()) { if (c.hasAttribute(attributeName(Attribute::Budget::ID))) account.setId(c.attribute(attributeName(Attribute::Budget::ID))); if (c.hasAttribute(attributeName(Attribute::Budget::BudgetLevel))) account.setBudgetLevel(stringToBudgetLevel(c.attribute(attributeName(Attribute::Budget::BudgetLevel)))); if (c.hasAttribute(attributeName(Attribute::Budget::BudgetSubAccounts))) account.setBudgetSubaccounts(c.attribute(attributeName(Attribute::Budget::BudgetSubAccounts)).toUInt()); } QDomNode period = c.firstChild(); while (!period.isNull() && period.isElement()) { QDomElement per = period.toElement(); MyMoneyBudget::PeriodGroup pGroup; if (elementName(Element::Budget::Period) == per.tagName() && per.hasAttribute(attributeName(Attribute::Budget::Amount)) && per.hasAttribute(attributeName(Attribute::Budget::Start))) { pGroup.setAmount(MyMoneyMoney(per.attribute(attributeName(Attribute::Budget::Amount)))); pGroup.setStartDate(QDate::fromString(per.attribute(attributeName(Attribute::Budget::Start)), Qt::ISODate)); account.addPeriod(pGroup.startDate(), pGroup); } period = period.nextSibling(); } budget.setAccount(account, account.id()); child = child.nextSibling(); } return budget; } const int BUDGET_VERSION = 2; void writeBudget(const MyMoneyBudget &budget, QDomDocument &document, QDomElement &parent) { auto el = document.createElement(nodeName(Node::Budget)); writeBaseXML(budget.id(), document, el); el.setAttribute(attributeName(Attribute::Budget::Name), budget.name()); el.setAttribute(attributeName(Attribute::Budget::Start), budget.budgetStart().toString(Qt::ISODate)); el.setAttribute(attributeName(Attribute::Budget::Version), BUDGET_VERSION); QMap::const_iterator it; auto accounts = budget.accountsMap(); for (it = accounts.cbegin(); it != accounts.cend(); ++it) { // only add the account if there is a budget entered // or it covers some sub accounts if (!(*it).balance().isZero() || (*it).budgetSubaccounts()) { QDomElement domAccount = document.createElement(elementName(Element::Budget::Account)); domAccount.setAttribute(attributeName(Attribute::Budget::ID), it.key()); domAccount.setAttribute(attributeName(Attribute::Budget::BudgetLevel), budgetLevels(it.value().budgetLevel())); domAccount.setAttribute(attributeName(Attribute::Budget::BudgetSubAccounts), it.value().budgetSubaccounts()); const QMap periods = it.value().getPeriods(); QMap::const_iterator it_per; for (it_per = periods.begin(); it_per != periods.end(); ++it_per) { if (!(*it_per).amount().isZero()) { QDomElement domPeriod = document.createElement(elementName(Element::Budget::Period)); domPeriod.setAttribute(attributeName(Attribute::Budget::Amount), (*it_per).amount().toString()); domPeriod.setAttribute(attributeName(Attribute::Budget::Start), (*it_per).startDate().toString(Qt::ISODate)); domAccount.appendChild(domPeriod); } } el.appendChild(domAccount); } } parent.appendChild(el); } }