diff --git a/kmymoney/mymoney/mymoneyenums.h b/kmymoney/mymoney/mymoneyenums.h index 90db99faa..e09f65326 100644 --- a/kmymoney/mymoney/mymoneyenums.h +++ b/kmymoney/mymoney/mymoneyenums.h @@ -1,421 +1,423 @@ /* * 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); } + enum class ChartPalette { Application = 0, Default, Rainbow, Subdued, End}; + inline uint qHash(const ChartPalette 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 dabde7d91..0a3212ce3 100644 --- a/kmymoney/mymoney/mymoneyreport.cpp +++ b/kmymoney/mymoney/mymoneyreport.cpp @@ -1,1020 +1,1032 @@ /* * 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 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(); 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; } +eMyMoney::Report::ChartPalette MyMoneyReport::chartPalette() const +{ + Q_D(const MyMoneyReport); + return d->m_chartPalette; +} + +void MyMoneyReport::setChartPalette(eMyMoney::Report::ChartPalette type) +{ + Q_D(MyMoneyReport); + d->m_chartPalette = 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 2d423cbae..7d0537793 100644 --- a/kmymoney/mymoney/mymoneyreport.h +++ b/kmymoney/mymoney/mymoneyreport.h @@ -1,444 +1,448 @@ /* * 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; } } +namespace eMyMoney { namespace Report { enum class ChartPalette; } } /** * 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); + eMyMoney::Report::ChartPalette chartPalette() const; + void setChartPalette(eMyMoney::Report::ChartPalette 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; /** * 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/mymoney/mymoneyreport_p.h b/kmymoney/mymoney/mymoneyreport_p.h index 63e4229ff..5996da4c1 100644 --- a/kmymoney/mymoney/mymoneyreport_p.h +++ b/kmymoney/mymoney/mymoneyreport_p.h @@ -1,370 +1,375 @@ /* * 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_P_H #define MYMONEYREPORT_P_H #include "mymoneyreport.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyobject_p.h" #include "mymoneyenums.h" class MyMoneyReportPrivate : public MyMoneyObjectPrivate { public: MyMoneyReportPrivate() : m_name(QStringLiteral("Unconfigured Pivot Table Report")), m_detailLevel(eMyMoney::Report::DetailLevel::None), m_investmentSum(eMyMoney::Report::InvestmentSum::Sold), m_hideTransactions(false), m_convertCurrency(true), m_favorite(false), m_tax(false), m_investments(false), m_loans(false), m_reportType(rowTypeToReportType(eMyMoney::Report::RowType::ExpenseIncome)), m_rowType(eMyMoney::Report::RowType::ExpenseIncome), m_columnType(eMyMoney::Report::ColumnType::Months), m_columnsAreDays(false), m_queryColumns(eMyMoney::Report::QueryColumn::None), m_dateLock(eMyMoney::TransactionFilter::Date::UserDefined), m_accountGroupFilter(false), m_chartType(eMyMoney::Report::ChartType::Line), + m_chartPalette(eMyMoney::Report::ChartPalette::Application), m_chartDataLabels(true), m_chartCHGridLines(true), m_chartSVGridLines(true), m_chartByDefault(false), m_chartLineWidth(MyMoneyReport::m_lineWidth), m_logYaxis(false), m_dataRangeStart('0'), m_dataRangeEnd('0'), m_dataMajorTick('0'), m_dataMinorTick('0'), m_yLabelsPrecision(2), m_dataLock(eMyMoney::Report::DataLock::Automatic), m_includeSchedules(false), m_includeTransfers(false), m_includeBudgetActuals(false), m_includeUnusedAccounts(false), m_showRowTotals(false), m_showColumnTotals(true), m_includeForecast(false), m_includeMovingAverage(false), m_movingAverageDays(0), m_includePrice(false), m_includeAveragePrice(false), m_mixedTime(false), m_currentDateColumn(0), m_settlementPeriod(3), m_showSTLTCapitalGains(false), m_tseparator(QDate::currentDate().addYears(-1)), m_skipZero(false) { } static 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); } /** * The user-assigned name of the report */ QString m_name; /** * The user-assigned comment for the report, in case they want to make * additional notes for themselves about the report. */ QString m_comment; /** * Where to group this report amongst the others in the UI view. This * should be assigned by the UI system. */ QString m_group; /** * How much detail to show in the accounts */ eMyMoney::Report::DetailLevel m_detailLevel; /** * Whether to sum: all, sold, bought or owned value */ eMyMoney::Report::InvestmentSum m_investmentSum; /** * Whether to show transactions or just totals. */ bool m_hideTransactions; /** * Whether to convert all currencies to the base currency of the file (true). * If this is false, it's up to the report generator to decide how to handle * the currency. */ bool m_convertCurrency; /** * Whether this is one of the users' favorite reports */ bool m_favorite; /** * Whether this report should only include categories marked as "Tax"="Yes" */ bool m_tax; /** * Whether this report should only include investment accounts */ bool m_investments; /** * Whether this report should only include loan accounts * Applies only to querytable reports. Mutually exclusive with * m_investments. */ bool m_loans; /** * What sort of algorithm should be used to run the report */ eMyMoney::Report::ReportType m_reportType; /** * What sort of values should show up on the ROWS of this report */ eMyMoney::Report::RowType m_rowType; /** * What sort of values should show up on the COLUMNS of this report, * in the case of a 'PivotTable' report. Really this is used more as a * QUANTITY of months or days. Whether it's months or days is determined * by m_columnsAreDays. */ eMyMoney::Report::ColumnType m_columnType; /** * Whether the base unit of columns of this report is days. Only applies to * 'PivotTable' reports. If false, then columns are months or multiples thereof. */ bool m_columnsAreDays; /** * What sort of values should show up on the COLUMNS of this report, * in the case of a 'QueryTable' report */ eMyMoney::Report::QueryColumn m_queryColumns; /** * The plain-language description of what the date range should be locked * to. 'userDefined' means NO locking, in any other case, the report * will be adjusted to match the date lock. So if the date lock is * 'currentMonth', the start and end dates of the underlying filter will * be updated to whatever the current month is. This updating happens * automatically when the report is loaded, and should also be done * manually by calling updateDateFilter() before generating the report */ eMyMoney::TransactionFilter::Date m_dateLock; /** * Which account groups should be included in the report. This filter * is applied to the individual splits AFTER a transaction has been * matched using the underlying filter. */ QList m_accountGroups; /** * Whether an account group filter has been set (see m_accountGroups) */ bool m_accountGroupFilter; /** * What format should be used to draw this report as a chart */ eMyMoney::Report::ChartType m_chartType; + /** + * What color palette should be used to draw this report as a chart + */ + eMyMoney::Report::ChartPalette m_chartPalette; /** * Whether the value of individual data points should be drawn on the chart */ bool m_chartDataLabels; /** * Whether grid lines should be drawn on the chart */ bool m_chartCHGridLines; bool m_chartSVGridLines; /** * Whether this report should be shown as a chart by default (otherwise it * should be shown as a textual report) */ bool m_chartByDefault; /** * Width of the chart lines */ uint m_chartLineWidth; /** * Whether Y axis is logarithmic or linear */ bool m_logYaxis; /** * Y data range */ QString m_dataRangeStart; QString m_dataRangeEnd; /** * Y data range division */ QString m_dataMajorTick; QString m_dataMinorTick; /** * Y labels precision */ uint m_yLabelsPrecision; /** * Whether data range should be calculated automatically or is user defined */ eMyMoney::Report::DataLock m_dataLock; /** * Whether to include scheduled transactions */ bool m_includeSchedules; /** * Whether to include transfers. Only applies to Income/Expense reports */ bool m_includeTransfers; /** * The id of the budget associated with this report. */ QString m_budgetId; /** * Whether this report should print the actual data to go along with * the budget. This is only valid if the report has a budget. */ bool m_includeBudgetActuals; /** * Whether this report should include all accounts and not only * accounts with transactions. */ bool m_includeUnusedAccounts; /** * Whether this report should include columns for row totals */ bool m_showRowTotals; /** * Whether this report should include rows for column totals */ bool m_showColumnTotals; /** * Whether this report should include forecast balance */ bool m_includeForecast; /** * Whether this report should include moving average */ bool m_includeMovingAverage; /** * The amount of days that spans each moving average */ int m_movingAverageDays; /** * Whether this report should include prices */ bool m_includePrice; /** * Whether this report should include moving average prices */ bool m_includeAveragePrice; /** * Make the actual and forecast lines display as one */ bool m_mixedTime; /** * This stores the column for the current date * This value is calculated dinamically and thus it is not saved in the file */ int m_currentDateColumn; /** * Time in days between the settlement date and the transaction date. */ uint m_settlementPeriod; /** * Controls showing short-term and long-term capital gains. */ bool m_showSTLTCapitalGains; /** * Date separating shot-term from long-term gains. */ QDate m_tseparator; /** * This option is for investments reports only which * show prices instead of balances as all other reports do. *

* Select this option to include prices for the given period (week, month, * quarter, ...) only. *

*

* If this option is off the last existing price is shown for a period, if * it is on, in a table the value is '0' shown and in a chart a linear * interpolation for the missing values will be performed. *
Example: *
There are prices for January and March, but there is no price for * February. *

    *
  • OFF: shows the price for February as the last price of * January *
  • ON: in a table the value is '0', in a chart a linear * interpolation for the February-price will be performed * (so it makes a kind of average-value using the January- and the * March-price in the chart) *
*

*/ bool m_skipZero; }; #endif diff --git a/kmymoney/plugins/views/reports/core/kreportchartview.cpp b/kmymoney/plugins/views/reports/core/kreportchartview.cpp index a82c143be..016b2895c 100644 --- a/kmymoney/plugins/views/reports/core/kreportchartview.cpp +++ b/kmymoney/plugins/views/reports/core/kreportchartview.cpp @@ -1,760 +1,774 @@ /* * Copyright 2005 Ace Jones * Copyright 2005-2018 Thomas Baumgart * 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 "kreportchartview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include #include #include #include #include #include #include #include #include #include #include #include #include "kmymoneysettings.h" #include "mymoneyfile.h" #include "mymoneysecurity.h" #include "mymoneyenums.h" using namespace reports; KReportChartView::KReportChartView(QWidget* parent) : KChart::Chart(parent), m_accountSeries(0), m_seriesTotals(0), m_numColumns(0), m_skipZero(0), m_backgroundBrush(KColorScheme(QPalette::Current).background()), m_foregroundBrush(KColorScheme(QPalette::Current).foreground()), m_precision(2) { // ******************************************************************** // Set KMyMoney's Chart Parameter Defaults // ******************************************************************** //Set the background obtained from the color scheme BackgroundAttributes backAttr(backgroundAttributes()); backAttr.setBrush(m_backgroundBrush); backAttr.setVisible(true); setBackgroundAttributes(backAttr); } void KReportChartView::drawPivotChart(const PivotGrid &grid, const MyMoneyReport &config, int numberColumns, const QStringList& columnHeadings, const QList& rowTypeList, const QStringList& columnTypeHeaderList) { if (numberColumns == 0) return; //set the number of columns setNumColumns(numberColumns); //set skipZero m_skipZero = config.isSkippingZero(); //remove existing headers const HeaderFooterList hfList = headerFooters(); foreach (const auto hf, hfList) delete hf; //remove existing legends const LegendList lgList = legends(); foreach (const auto lg, lgList) delete lg; //make sure the model is clear m_model.clear(); const bool blocked = m_model.blockSignals(true); // don't emit dataChanged() signal during each drawPivotRowSet //set the new header HeaderFooter* header = new HeaderFooter; // adding some extra spaces at the end here avoids truncation // of the label on the diagram and solves bug #399260 header->setText(config.name().append(QLatin1String(" "))); header->setType(HeaderFooter::Header); header->setPosition(Position::North); TextAttributes headerTextAttr(header->textAttributes()); headerTextAttr.setPen(m_foregroundBrush.color()); header->setTextAttributes(headerTextAttr); addHeaderFooter(header); // whether to limit the chart to use series totals only. Used for reports which only // show one dimension (pie). setSeriesTotals(false); // whether series (rows) are accounts (true) or months (false). This causes a lot // of complexity in the charts. The problem is that circular reports work best with // an account in a COLUMN, while line/bar prefer it in a ROW. setAccountSeries(true); switch (config.chartType()) { case eMyMoney::Report::ChartType::Line: case eMyMoney::Report::ChartType::Bar: case eMyMoney::Report::ChartType::StackedBar: { CartesianCoordinatePlane* cartesianPlane = new CartesianCoordinatePlane(this); cartesianPlane->setAutoAdjustVerticalRangeToData(2); cartesianPlane->setRubberBandZoomingEnabled(true); replaceCoordinatePlane(cartesianPlane); // set-up axis type if (config.isLogYAxis()) cartesianPlane->setAxesCalcModeY(KChart::AbstractCoordinatePlane::Logarithmic); else cartesianPlane->setAxesCalcModeY(KChart::AbstractCoordinatePlane::Linear); QLocale loc = locale(); // set-up grid GridAttributes ga = cartesianPlane->gridAttributes(Qt::Vertical); ga.setGridVisible(config.isChartCHGridLines()); ga.setGridStepWidth(config.isDataUserDefined() ? loc.toDouble(config.dataMajorTick()) : 0.0); ga.setGridSubStepWidth(config.isDataUserDefined() ? loc.toDouble(config.dataMinorTick()) : 0.0); cartesianPlane->setGridAttributes(Qt::Vertical, ga); ga = cartesianPlane->gridAttributes(Qt::Horizontal); ga.setGridVisible(config.isChartSVGridLines()); cartesianPlane->setGridAttributes(Qt::Horizontal, ga); // set-up data range cartesianPlane->setVerticalRange(qMakePair(config.isDataUserDefined() ? loc.toDouble(config.dataRangeStart()) : 0.0, config.isDataUserDefined() ? loc.toDouble(config.dataRangeEnd()) : 0.0)); //set-up x axis CartesianAxis *xAxis = new CartesianAxis(); xAxis->setPosition(CartesianAxis::Bottom); xAxis->setTitleText(i18n("Time")); TextAttributes xAxisTitleTextAttr(xAxis->titleTextAttributes()); xAxisTitleTextAttr.setMinimalFontSize(QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize()); xAxisTitleTextAttr.setPen(m_foregroundBrush.color()); xAxis->setTitleTextAttributes(xAxisTitleTextAttr); TextAttributes xAxisTextAttr(xAxis->textAttributes()); xAxisTextAttr.setPen(m_foregroundBrush.color()); xAxis->setTextAttributes(xAxisTextAttr); RulerAttributes xAxisRulerAttr(xAxis->rulerAttributes()); xAxisRulerAttr.setTickMarkPen(m_foregroundBrush.color()); xAxisRulerAttr.setShowRulerLine(true); xAxis->setRulerAttributes(xAxisRulerAttr); //set-up y axis KChart::CartesianAxis *yAxis = new KChart::CartesianAxis; yAxis->setPosition(CartesianAxis::Left); if (config.isIncludingPrice()) yAxis->setTitleText(i18n("Price")); else yAxis->setTitleText(i18n("Balance")); TextAttributes yAxisTitleTextAttr(yAxis->titleTextAttributes()); yAxisTitleTextAttr.setMinimalFontSize(QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSize()); yAxisTitleTextAttr.setPen(m_foregroundBrush.color()); yAxis->setTitleTextAttributes(yAxisTitleTextAttr); TextAttributes yAxisTextAttr(yAxis->textAttributes()); yAxisTextAttr.setPen(m_foregroundBrush.color()); yAxis->setTextAttributes(yAxisTextAttr); RulerAttributes yAxisRulerAttr(yAxis->rulerAttributes()); yAxisRulerAttr.setTickMarkPen(m_foregroundBrush.color()); yAxisRulerAttr.setShowRulerLine(true); yAxis->setRulerAttributes(yAxisRulerAttr); switch (config.chartType()) { case eMyMoney::Report::ChartType::End: case eMyMoney::Report::ChartType::Line: { KChart::LineDiagram* diagram = new KChart::LineDiagram(this, cartesianPlane); if (config.isSkippingZero()) { LineAttributes attributes = diagram->lineAttributes(); attributes.setMissingValuesPolicy(LineAttributes::MissingValuesAreBridged); diagram->setLineAttributes(attributes); } cartesianPlane->replaceDiagram(diagram); diagram->addAxis(xAxis); diagram->addAxis(yAxis); break; } case eMyMoney::Report::ChartType::Bar: { KChart::BarDiagram* diagram = new KChart::BarDiagram(this, cartesianPlane); cartesianPlane->replaceDiagram(diagram); diagram->addAxis(xAxis); diagram->addAxis(yAxis); break; } case eMyMoney::Report::ChartType::StackedBar: { KChart::BarDiagram* diagram = new KChart::BarDiagram(this, cartesianPlane); diagram->setType(BarDiagram::Stacked); cartesianPlane->replaceDiagram(diagram); diagram->addAxis(xAxis); diagram->addAxis(yAxis); break; } default: break; } break; } case eMyMoney::Report::ChartType::Pie: case eMyMoney::Report::ChartType::Ring:{ PolarCoordinatePlane* polarPlane = new PolarCoordinatePlane(this); replaceCoordinatePlane(polarPlane); // set-up grid GridAttributes ga = polarPlane->gridAttributes(true); ga.setGridVisible(config.isChartCHGridLines()); polarPlane->setGridAttributes(true, ga); ga = polarPlane->gridAttributes(false); ga.setGridVisible(config.isChartSVGridLines()); polarPlane->setGridAttributes(false, ga); setAccountSeries(false); switch (config.chartType()) { case eMyMoney::Report::ChartType::Pie: { KChart::PieDiagram* diagram = new KChart::PieDiagram(this, polarPlane); polarPlane->replaceDiagram(diagram); setSeriesTotals(true); break; } case eMyMoney::Report::ChartType::Ring: { KChart::RingDiagram* diagram = new KChart::RingDiagram(this, polarPlane); polarPlane->replaceDiagram(diagram); break; } default: break; } break; } default: // no valid chart types return; } //get the coordinate plane and the diagram for later use AbstractCoordinatePlane* cPlane = coordinatePlane(); AbstractDiagram* planeDiagram = cPlane->diagram(); planeDiagram->setAntiAliasing(true); - //the palette - we set it here because it is a property of the diagram - switch (KMyMoneySettings::chartsPalette()) { - case 0: + // setup chart color palette + switch (config.chartPalette()) { + case eMyMoney::Report::ChartPalette::Application: + switch (KMyMoneySettings::chartsPalette()) { + case 0: + planeDiagram->useDefaultColors(); + break; + case 1: + planeDiagram->useRainbowColors(); + break; + case 2: + default: + planeDiagram->useSubduedColors(); + break; + } + break; + case eMyMoney::Report::ChartPalette::Default: planeDiagram->useDefaultColors(); break; - case 1: + case eMyMoney::Report::ChartPalette::Rainbow: planeDiagram->useRainbowColors(); break; - case 2: default: + case eMyMoney::Report::ChartPalette::Subdued: planeDiagram->useSubduedColors(); break; } int eBudgetDiffIdx = rowTypeList.indexOf(eBudgetDiff); QList myRowTypeList = rowTypeList; myRowTypeList.removeAt(eBudgetDiffIdx); QStringList myColumnTypeHeaderList = columnTypeHeaderList; myColumnTypeHeaderList.removeAt(eBudgetDiffIdx); int myRowTypeListSize = myRowTypeList.size(); MyMoneyFile* file = MyMoneyFile::instance(); int precision = MyMoneyMoney::denomToPrec(file->baseCurrency().smallestAccountFraction()); int rowNum = 0; QStringList legendNames; QMap legendTotal; switch (config.detailLevel()) { case eMyMoney::Report::DetailLevel::None: case eMyMoney::Report::DetailLevel::All: { // iterate over outer groups PivotGrid::const_iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { // iterate over inner groups PivotOuterGroup::const_iterator it_innergroup = (*it_outergroup).begin(); while (it_innergroup != (*it_outergroup).end()) { // iterate over accounts PivotInnerGroup::const_iterator it_row = (*it_innergroup).begin(); while (it_row != (*it_innergroup).end()) { //Do not include investments accounts in the chart because they are merely container of stock and other accounts if (it_row.key().accountType() != eMyMoney::Account::Type::Investment) { // get displayed precision int currencyPrecision = precision; int securityPrecision = precision; if (!it_row.key().id().isEmpty()) { const MyMoneyAccount acc = file->account(it_row.key().id()); if (acc.isInvest()) { securityPrecision = file->currency(acc.currencyId()).pricePrecision(); // stock account isn't eveluated in currency, so take investment account instead currencyPrecision = MyMoneyMoney::denomToPrec(file->account(acc.parentAccountId()).fraction()); } else currencyPrecision = MyMoneyMoney::denomToPrec(acc.fraction()); } // iterate row types for (int i = 0 ; i < myRowTypeListSize; ++i) { QString legendText; //only show the column type in the header if there is more than one type if (myRowTypeListSize > 1) legendText = QString(myColumnTypeHeaderList.at(i) + QLatin1Literal(" - ") + it_row.key().name()); else legendText = it_row.key().name(); //set the legend text legendNames.append(legendText); legendTotal.insertMulti(it_row.value().value(myRowTypeList.at(i)).m_total.abs(), rowNum); precision = myRowTypeList.at(i) == ePrice ? securityPrecision : currencyPrecision; //set the cell value and tooltip rowNum = drawPivotGridRow(rowNum, it_row.value().value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), 0, numberColumns, precision); } } ++it_row; } ++it_innergroup; } ++it_outergroup; } } break; case eMyMoney::Report::DetailLevel::Top: { // iterate over outer groups PivotGrid::const_iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { // iterate over inner groups PivotOuterGroup::const_iterator it_innergroup = (*it_outergroup).begin(); while (it_innergroup != (*it_outergroup).end()) { // iterate row types for (int i = 0 ; i < myRowTypeListSize; ++i) { QString legendText; //only show the column type in the header if there is more than one type if (myRowTypeListSize > 1) legendText = QString(myColumnTypeHeaderList.at(i) + QLatin1Literal(" - ") + it_innergroup.key()); else legendText = it_innergroup.key(); //set the legend text legendNames.append(legendText); legendTotal.insertMulti((*it_innergroup).m_total.value(myRowTypeList.at(i)).m_total.abs(), rowNum); //set the cell value and tooltip rowNum = drawPivotGridRow(rowNum, (*it_innergroup).m_total.value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), 0, numberColumns, precision); } ++it_innergroup; } ++it_outergroup; } } break; case eMyMoney::Report::DetailLevel::Group: { // iterate over outer groups PivotGrid::const_iterator it_outergroup = grid.begin(); while (it_outergroup != grid.end()) { // iterate row types for (int i = 0 ; i < myRowTypeListSize; ++i) { QString legendText; //only show the column type in the header if there is more than one type if (myRowTypeListSize > 1) legendText = QString(myColumnTypeHeaderList.at(i) + QLatin1Literal(" - ") + it_outergroup.key()); else legendText = it_outergroup.key(); //set the legend text legendNames.append(legendText); legendTotal.insertMulti((*it_outergroup).m_total.value(myRowTypeList.at(i)).m_total.abs(), rowNum); //set the cell value and tooltip rowNum = drawPivotGridRow(rowNum, (*it_outergroup).m_total.value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), 0, numberColumns, precision); } ++it_outergroup; } //if selected, show totals too if (config.isShowingRowTotals()) { // iterate row types for (int i = 0 ; i < myRowTypeListSize; ++i) { QString legendText; //only show the column type in the header if there is more than one type if (myRowTypeListSize > 1) legendText = QString(myColumnTypeHeaderList.at(i) + QLatin1Literal(" - ") + i18nc("Total balance", "Total")); else legendText = QString(i18nc("Total balance", "Total")); //set the legend text legendNames.append(legendText); legendTotal.insertMulti(grid.m_total.value(myRowTypeList.at(i)).m_total.abs(), rowNum); //set the cell value and tooltip rowNum = drawPivotGridRow(rowNum, grid.m_total.value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), 0, numberColumns, precision); } } } break; case eMyMoney::Report::DetailLevel::Total: { // iterate row types for (int i = 0 ; i < myRowTypeListSize; ++i) { QString legendText; //only show the column type in the header if there is more than one type if (myRowTypeListSize > 1) legendText = QString(myColumnTypeHeaderList.at(i) + QLatin1Literal(" - ") + i18nc("Total balance", "Total")); else legendText = QString(i18nc("Total balance", "Total")); //set the legend text legendNames.append(legendText); legendTotal.insertMulti(grid.m_total.value(myRowTypeList.at(i)).m_total.abs(), rowNum); //set the cell value and tooltip if (config.isMixedTime()) { if (myRowTypeList.at(i) == eActual) rowNum = drawPivotGridRow(rowNum, grid.m_total.value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), 0, config.currentDateColumn(), precision); else if (myRowTypeList.at(i)== eForecast) { rowNum = drawPivotGridRow(rowNum, grid.m_total.value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), config.currentDateColumn(), numberColumns - config.currentDateColumn(), precision); } else rowNum = drawPivotGridRow(rowNum, grid.m_total.value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), 0, numberColumns, precision); } else rowNum = drawPivotGridRow(rowNum, grid.m_total.value(myRowTypeList.at(i)), config.isChartDataLabels() ? legendText : QString(), 0, numberColumns, precision); } } break; default: case eMyMoney::Report::DetailLevel::End: return; } auto legendRows = legendTotal.values(); // list of legend rows sorted ascending by total value for (auto i = 0; i < legendRows.count(); ++i) { const auto ixRow = legendRows.count() - 1 - i; // take row with the highest total value i.e. form the bottom const auto row = legendRows.at(ixRow); if ( row != i) { // if legend isn't sorted by total value, then rearrange model if ((accountSeries() && !seriesTotals()) || (seriesTotals() && !accountSeries())) m_model.insertColumn(i, m_model.takeColumn(row)); else m_model.insertRow(i, m_model.takeRow(row)); for (auto j = i; j < ixRow; ++j) { // fix invalid indexes after above move operation if (legendRows.at(j) < row) ++legendRows[j]; } legendRows[ixRow] = i; legendNames.move(row, i); } } // Set up X axis labels (ie "abscissa" to use the technical term) if (accountSeries()) { // if not, we will set these up while putting in the chart values. QStringList xLabels; foreach (const auto colHeading, columnHeadings) xLabels.append(QString(colHeading).replace(QLatin1String(" "), QLatin1String(" "))); m_model.setVerticalHeaderLabels(xLabels); } m_model.setHorizontalHeaderLabels(legendNames); // set line width for line chart if (config.chartType() == eMyMoney::Report::ChartType::Line) { AttributesModel* diagramAttributes = planeDiagram->attributesModel(); int penWidth = config.chartLineWidth(); for (int i = 0 ; i < rowNum ; ++i) { QPen pen = diagramAttributes->headerData(i, Qt::Horizontal, DatasetPenRole).value< QPen >(); pen.setWidth(penWidth); m_model.setHeaderData(i, Qt::Horizontal, qVariantFromValue(pen), DatasetPenRole); } } // set the text attributes after calling replaceLegend() otherwise fon sizes will get overwritten qreal generalFontSize = QFontDatabase::systemFont(QFontDatabase::GeneralFont).pointSizeF(); if (generalFontSize == -1) generalFontSize = 8; // this is a fallback if the fontsize was specified in pixels // the legend is needed only if at least two data sets are rendered if (qMin(static_cast(KMyMoneySettings::maximumLegendItems()), rowNum) > 1) { //the legend will be used later Legend* legend = new Legend(planeDiagram, this); legend->setTitleText(i18nc("Chart legend title", "Legend")); //set the legend basic attributes //this is done after adding the legend because the values are overridden when adding the legend to the chart const auto maxLegendItems = KMyMoneySettings::maximumLegendItems(); auto legendItems = legendNames.count(); auto i = 0; while (legendItems > maxLegendItems) { legend->setDatasetHidden(legendRows.at(i++), true); --legendItems; } legend->setUseAutomaticMarkerSize(false); FrameAttributes legendFrameAttr(legend->frameAttributes()); legendFrameAttr.setPen(m_foregroundBrush.color()); // leave some space between the content and the frame legendFrameAttr.setPadding(2); legend->setFrameAttributes(legendFrameAttr); legend->setPosition(Position::East); legend->setTextAlignment(Qt::AlignLeft); if (config.isChartDataLabels()) legend->setLegendStyle(KChart::Legend::MarkersAndLines); else legend->setLegendStyle(KChart::Legend::LinesOnly); replaceLegend(legend); TextAttributes legendTextAttr(legend->textAttributes()); legendTextAttr.setPen(m_foregroundBrush.color()); legendTextAttr.setFontSize(KChart::Measure(generalFontSize, KChartEnums::MeasureCalculationModeAbsolute)); legend->setTextAttributes(legendTextAttr); TextAttributes legendTitleTextAttr(legend->titleTextAttributes()); legendTitleTextAttr.setPen(m_foregroundBrush.color()); legendTitleTextAttr.setFontSize(KChart::Measure(generalFontSize + 4, KChartEnums::MeasureCalculationModeAbsolute)); legend->setTitleTextAttributes(legendTitleTextAttr); } //set data value attributes //make sure to show only the required number of fractional digits on the labels of the graph DataValueAttributes dataValueAttr(planeDiagram->dataValueAttributes()); MarkerAttributes markerAttr(dataValueAttr.markerAttributes()); markerAttr.setVisible(true); markerAttr.setMarkerStyle(MarkerAttributes::MarkerCircle); dataValueAttr.setMarkerAttributes(markerAttr); TextAttributes dataValueTextAttr(dataValueAttr.textAttributes()); dataValueTextAttr.setPen(m_foregroundBrush.color()); dataValueTextAttr.setFontSize(KChart::Measure(generalFontSize, KChartEnums::MeasureCalculationModeAbsolute)); dataValueAttr.setTextAttributes(dataValueTextAttr); m_precision = config.yLabelsPrecision(); dataValueAttr.setDecimalDigits(config.yLabelsPrecision()); dataValueAttr.setVisible(config.isChartDataLabels()); planeDiagram->setDataValueAttributes(dataValueAttr); planeDiagram->setAllowOverlappingDataValueTexts(true); m_model.blockSignals(blocked); // reenable dataChanged() signal //assign model to the diagram planeDiagram->setModel(&m_model); // connect needLayoutPlanes, so dimension of chart can be known, so custom Y labels can be generated connect(cPlane, &KChart::AbstractCoordinatePlane::needLayoutPlanes, this, &KReportChartView::slotNeedUpdate, Qt::QueuedConnection); } void KReportChartView::slotNeedUpdate() { disconnect(coordinatePlane(), &KChart::AbstractCoordinatePlane::needLayoutPlanes, this, &KReportChartView::slotNeedUpdate); // this won't cause hang-up in KReportsView::slotConfigure QList grids = coordinatePlane()->gridDimensionsList(); if (grids.isEmpty()) // ring and pie charts have no dimensions return; if (grids.at(1).stepWidth == 0) // no labels? return; QLocale loc = locale(); QChar separator = loc.groupSeparator(); QChar decimalPoint = loc.decimalPoint(); QStringList labels; if (m_precision > 10 || m_precision <= 0) // assure that conversion through QLocale::toString() will always work m_precision = 1; CartesianCoordinatePlane* cartesianplane = qobject_cast(coordinatePlane()); if (cartesianplane) { if (cartesianplane->axesCalcModeY() == KChart::AbstractCoordinatePlane::Logarithmic) { qreal labelValue = qFloor(log10(grids.at(1).start)); // first label is 10 to power of labelValue /// @todo this might also need some vertical adjustment in case of a horizontal line /// see below how this has been solved for linear graphs int labelCount = qFloor(log10(grids.at(1).end)) - qFloor(log10(grids.at(1).start)) + 1; for (auto i = 0; i < labelCount; ++i) { labels.append(loc.toString(qPow(10.0, labelValue), 'f', m_precision).remove(separator).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + decimalPoint + "$"))); ++labelValue; // next label is 10 to power of previous exponent + 1 } } else { qreal labelValue = grids.at(1).start; // first label is start value qreal step = grids.at(1).stepWidth; // in case we have a horizontal line, we extend the vertical range around it if (cartesianplane->verticalRange().first == cartesianplane->verticalRange().second) { cartesianplane->setVerticalRange(qMakePair(cartesianplane->verticalRange().first - 2, cartesianplane->verticalRange().first + 2)); grids[1].start -= 2*step; grids[1].end += 2*step; labelValue -= 2*step; } int labelCount = qFloor((grids.at(1).end - grids.at(1).start) / grids.at(1).stepWidth) + 1; for (auto i = 0; i < labelCount; ++i) { labels.append(loc.toString(labelValue, 'f', m_precision).remove(separator).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + decimalPoint + "$"))); labelValue += step; // next label is previous value + step value } } } else return; // nothing but cartesian plane is handled KChart::LineDiagram* lineDiagram = qobject_cast(coordinatePlane()->diagram()); if (lineDiagram) lineDiagram->axes().at(1)->setLabels(labels); KChart::BarDiagram* barDiagram = qobject_cast(coordinatePlane()->diagram()); if (barDiagram) barDiagram->axes().at(1)->setLabels(labels); } int KReportChartView::drawPivotGridRow(int rowNum, const PivotGridRow& gridRow, const QString& legendText, const int startColumn, const int columnsToDraw, const int precision) { // Columns const QString toolTip = QStringLiteral("

%1

%2
"); const bool isToolTip = !legendText.isEmpty(); if (seriesTotals()) { QStandardItem* item = new QStandardItem(); double value = gridRow.m_total.toDouble(); item->setData(QVariant(value), Qt::DisplayRole); if (isToolTip) item->setToolTip(toolTip.arg(legendText).arg(value, 0, 'f', precision)); //set the cell value if (accountSeries()) { m_model.insertRows(rowNum, 1); m_model.setItem(rowNum, 0, item); } else { m_model.insertColumns(rowNum, 1); m_model.setItem(0, rowNum, item); } } else { QList itemList; for (int i = 0; i < startColumn-1; ++i) { itemList.append(new QStandardItem); } for (int i = startColumn; i < startColumn + columnsToDraw; ++i) { QStandardItem* item = new QStandardItem(); if (!m_skipZero || !gridRow.at(i).isZero()) { double value = gridRow.at(i).toDouble(); item->setData(QVariant(value), Qt::DisplayRole); if (isToolTip) item->setToolTip(toolTip.arg(legendText).arg(value, 0, 'f', precision)); } itemList.append(item); } if (accountSeries()) m_model.appendColumn(itemList); else m_model.appendRow(itemList); } return ++rowNum; } void KReportChartView::setDataCell(int row, int column, const double value, QString tip) { QMap cellMap; cellMap.insert(Qt::DisplayRole, QVariant(value)); if (!tip.isEmpty()) cellMap.insert(Qt::ToolTipRole, QVariant(tip)); const QModelIndex index = m_model.index(row, column); m_model.setItemData(index, cellMap); } /** * Justifies the model, so that the given rows and columns fit into it. */ void KReportChartView::justifyModelSize(int rows, int columns) { const int currentRows = m_model.rowCount(); const int currentCols = m_model.columnCount(); if (currentCols < columns) if (! m_model.insertColumns(currentCols, columns - currentCols)) qDebug() << "justifyModelSize: could not increase model size."; if (currentRows < rows) if (! m_model.insertRows(currentRows, rows - currentRows)) qDebug() << "justifyModelSize: could not increase model size."; Q_ASSERT(m_model.rowCount() >= rows); Q_ASSERT(m_model.columnCount() >= columns); } void KReportChartView::setLineWidth(const int lineWidth) { LineDiagram* lineDiagram = qobject_cast(coordinatePlane()->diagram()); if (lineDiagram) { QList pens; pens = lineDiagram->datasetPens(); for (int i = 0; i < pens.count(); ++i) { pens[i].setWidth(lineWidth); lineDiagram->setPen(i, pens.at(i)); } } } void KReportChartView::drawLimitLine(const double limit) { if (coordinatePlane()->diagram()->datasetDimension() != 1) return; // temporarily disconnect the view from the model to aovid update of view on // emission of the dataChanged() signal for each call of setDataCell(). // This speeds up the runtime of drawLimitLine() by a factor of // approx. 60 on my box (1831ms vs. 31ms). AbstractDiagram* planeDiagram = coordinatePlane()->diagram(); planeDiagram->setModel(0); //we get the current number of rows and we add one after that int row = m_model.rowCount(); justifyModelSize(m_numColumns, row + 1); for (int col = 0; col < m_numColumns; ++col) { setDataCell(col, row, limit); } planeDiagram->setModel(&m_model); //TODO: add format to the line } void KReportChartView::removeLegend() { Legend* chartLegend = Chart::legend(); delete chartLegend; } diff --git a/kmymoney/plugins/views/reports/kbalancechartdlg.cpp b/kmymoney/plugins/views/reports/kbalancechartdlg.cpp index 9bd1346b4..b8fb81c63 100644 --- a/kmymoney/plugins/views/reports/kbalancechartdlg.cpp +++ b/kmymoney/plugins/views/reports/kbalancechartdlg.cpp @@ -1,164 +1,165 @@ /* * Copyright 2007-2011 Thomas Baumgart * Copyright 2017 Ł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 "kbalancechartdlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "mymoneyreport.h" #include "pivottable.h" #include "kreportchartview.h" #include "mymoneyenums.h" using namespace reports; KBalanceChartDlg::KBalanceChartDlg(const MyMoneyAccount& account, QWidget* parent) : QDialog(parent) { setWindowTitle(i18n("Balance of %1", account.name())); setSizeGripEnabled(true); setModal(true); // restore the last used dialog size winId(); // needs to be called to create the QWindow KConfigGroup grp = KSharedConfig::openConfig()->group("KBalanceChartDlg"); if (grp.isValid()) { KWindowConfig::restoreWindowSize(windowHandle(), grp); } // let the minimum size be 700x500 resize(QSize(700, 500).expandedTo(windowHandle() ? windowHandle()->size() : QSize())); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); //draw the chart and add it to the main layout KReportChartView* chartWidget = drawChart(account); mainLayout->addWidget(chartWidget); // add the buttons QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); mainLayout->addWidget(buttonBox); } KBalanceChartDlg::~KBalanceChartDlg() { // store the last used dialog size KConfigGroup grp = KSharedConfig::openConfig()->group("KBalanceChartDlg"); if (grp.isValid()) { KWindowConfig::saveWindowSize(windowHandle(), grp); } } KReportChartView* KBalanceChartDlg::drawChart(const MyMoneyAccount& account) { MyMoneyReport reportCfg = MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), eMyMoney::TransactionFilter::Date::Last3ToNext3Months, eMyMoney::Report::DetailLevel::Total, i18n("%1 Balance History", account.name()), i18n("Generated Report") ); reportCfg.setChartByDefault(true); reportCfg.setChartCHGridLines(false); reportCfg.setChartSVGridLines(false); reportCfg.setChartDataLabels(false); reportCfg.setChartType(eMyMoney::Report::ChartType::Line); + reportCfg.setChartPalette(eMyMoney::Report::ChartPalette::Application); reportCfg.setIncludingForecast(true); reportCfg.setIncludingBudgetActuals(true); if (account.accountType() == eMyMoney::Account::Type::Investment) { foreach (const auto accountID, account.accountList()) reportCfg.addAccount(accountID); } else reportCfg.addAccount(account.id()); reportCfg.setColumnsAreDays(true); reportCfg.setConvertCurrency(false); reportCfg.setMixedTime(true); reports::PivotTable table(reportCfg); reports::KReportChartView* chartWidget = new reports::KReportChartView(this); table.drawChart(*chartWidget); // add another row for limit bool needRow = false; bool haveMinBalance = false; bool haveMaxCredit = false; MyMoneyMoney minBalance, maxCredit; MyMoneyMoney factor(1, 1); if (account.accountGroup() == eMyMoney::Account::Type::Asset) factor = -factor; if (!account.value("maxCreditEarly").isEmpty()) { needRow = true; haveMaxCredit = true; maxCredit = MyMoneyMoney(account.value("maxCreditEarly")) * factor; } if (!account.value("maxCreditAbsolute").isEmpty()) { needRow = true; haveMaxCredit = true; maxCredit = MyMoneyMoney(account.value("maxCreditAbsolute")) * factor; } if (!account.value("minBalanceEarly").isEmpty()) { needRow = true; haveMinBalance = true; minBalance = MyMoneyMoney(account.value("minBalanceEarly")); } if (!account.value("minBalanceAbsolute").isEmpty()) { needRow = true; haveMinBalance = true; minBalance = MyMoneyMoney(account.value("minBalanceAbsolute")); } if (needRow) { if (haveMinBalance) { chartWidget->drawLimitLine(minBalance.toDouble()); } if (haveMaxCredit) { chartWidget->drawLimitLine(maxCredit.toDouble()); } } // always draw the y axis zero value line // TODO: port to KF5 - this crashes KChart //chartWidget->drawLimitLine(0); //remove the legend chartWidget->removeLegend(); return chartWidget; } diff --git a/kmymoney/plugins/views/reports/kreportconfigurationfilterdlg.cpp b/kmymoney/plugins/views/reports/kreportconfigurationfilterdlg.cpp index c3530b475..a50a768bc 100644 --- a/kmymoney/plugins/views/reports/kreportconfigurationfilterdlg.cpp +++ b/kmymoney/plugins/views/reports/kreportconfigurationfilterdlg.cpp @@ -1,666 +1,678 @@ /*************************************************************************** kreportconfigurationdlg.cpp - description ------------------- begin : Mon Jun 21 2004 copyright : (C) 2000-2004 by Michael Edwardes email : mte@users.sourceforge.net Javier Campos Morales Felix Rodriguez John C Thomas Baumgart Kevin Tambascio Ace Jones (C) 2017, 2018 by Ł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. * * * ***************************************************************************/ #include "kreportconfigurationfilterdlg.h" // ---------------------------------------------------------------------------- // QT Includes #include // ---------------------------------------------------------------------------- // KDE Includes #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ktransactionfilter.h" #include "kmymoneyaccountselector.h" #include "mymoneyfile.h" #include "mymoneyexception.h" #include "mymoneybudget.h" #include "mymoneyreport.h" #include "daterangedlg.h" #include "reporttabimpl.h" #include "mymoneyenums.h" #include #include #include #include #include #include #include #include class KReportConfigurationFilterDlgPrivate { Q_DISABLE_COPY(KReportConfigurationFilterDlgPrivate) public: KReportConfigurationFilterDlgPrivate(KReportConfigurationFilterDlg *qq) : q_ptr(qq), ui(new Ui::KReportConfigurationFilterDlg), m_tabRowColPivot(nullptr), m_tabRowColQuery(nullptr), m_tabChart(nullptr), m_tabRange(nullptr), m_dateRange(nullptr) { } ~KReportConfigurationFilterDlgPrivate() { delete ui; } KReportConfigurationFilterDlg *q_ptr; Ui::KReportConfigurationFilterDlg *ui; QPointer m_tabGeneral; QPointer m_tabRowColPivot; QPointer m_tabRowColQuery; QPointer m_tabChart; QPointer m_tabRange; QPointer m_tabCapitalGain; QPointer m_tabPerformance; QPointer m_tabFilters; MyMoneyReport m_initialState; MyMoneyReport m_currentState; QVector m_budgets; DateRangeDlg *m_dateRange; }; KReportConfigurationFilterDlg::KReportConfigurationFilterDlg(MyMoneyReport report, QWidget *parent) : QDialog(parent), d_ptr(new KReportConfigurationFilterDlgPrivate(this)) { Q_D(KReportConfigurationFilterDlg); d->ui->setupUi(this); d->m_initialState = report; d->m_currentState = report; // // Rework labeling // setWindowTitle(i18n("Report Configuration")); // // Rework the buttons // // the Apply button is always enabled d->ui->buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); d->ui->buttonBox->button(QDialogButtonBox::Apply)->setToolTip(i18nc("@info:tooltip for report configuration apply button", "Apply the configuration changes to the report")); connect(d->ui->buttonBox->button(QDialogButtonBox::Apply), &QAbstractButton::clicked, this, &KReportConfigurationFilterDlg::slotSearch); connect(d->ui->buttonBox->button(QDialogButtonBox::Close), &QAbstractButton::clicked, this, &QDialog::close); connect(d->ui->buttonBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, this, &KReportConfigurationFilterDlg::slotReset); connect(d->ui->buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, this, &KReportConfigurationFilterDlg::slotShowHelp); // // Add new tabs // if (d->m_initialState.reportType() == eMyMoney::Report::ReportType::PivotTable) { // we will use date range together with data range d->m_tabFilters = new KTransactionFilter(this, (report.rowType() == eMyMoney::Report::RowType::Account), false); } else { d->m_tabFilters = new KTransactionFilter(this, (report.rowType() == eMyMoney::Report::RowType::Account)); d->m_dateRange = d->m_tabFilters->dateRange(); } d->ui->m_tabWidget->addTab(d->m_tabFilters, i18nc("Filters tab", "Filters")); d->m_tabGeneral = new ReportTabGeneral(d->ui->m_criteriaTab); d->ui->m_criteriaTab->insertTab(0, d->m_tabGeneral, i18nc("General tab", "General")); if (d->m_initialState.reportType() == eMyMoney::Report::ReportType::PivotTable) { int tabNr = 1; if (!(d->m_initialState.isIncludingPrice() || d->m_initialState.isIncludingAveragePrice())) { d->m_tabRowColPivot = new ReportTabRowColPivot(d->ui->m_criteriaTab); d->ui->m_criteriaTab->insertTab(tabNr++, d->m_tabRowColPivot, i18n("Rows/Columns")); connect(d->m_tabRowColPivot->ui->m_comboRows, static_cast(&QComboBox::activated), this, static_cast(&KReportConfigurationFilterDlg::slotRowTypeChanged)); connect(d->m_tabRowColPivot->ui->m_comboRows, static_cast(&QComboBox::activated), this, static_cast(&KReportConfigurationFilterDlg::slotUpdateColumnsCombo)); //control the state of the includeTransfer check connect(d->m_tabFilters->categoriesView(), &KMyMoneySelector::stateChanged, this, &KReportConfigurationFilterDlg::slotUpdateCheckTransfers); } d->m_tabChart = new ReportTabChart(d->ui->m_criteriaTab); d->ui->m_criteriaTab->insertTab(tabNr++, d->m_tabChart, i18n("Chart")); d->m_tabRange = new ReportTabRange(d->ui->m_criteriaTab); d->ui->m_criteriaTab->insertTab(tabNr++, d->m_tabRange, i18n("Range")); d->m_dateRange = d->m_tabRange->m_dateRange; if (!(d->m_initialState.isIncludingPrice() || d->m_initialState.isIncludingAveragePrice())) { connect(d->m_tabRange->ui->m_comboColumns, static_cast(&QComboBox::activated), this, &KReportConfigurationFilterDlg::slotColumnTypeChanged); connect(d->m_tabRange->ui->m_comboColumns, static_cast(&QComboBox::activated), this, static_cast(&KReportConfigurationFilterDlg::slotUpdateColumnsCombo)); } connect(d->m_tabChart->ui->m_logYaxis, &QCheckBox::stateChanged, this, &KReportConfigurationFilterDlg::slotLogAxisChanged); } else if (d->m_initialState.reportType() == eMyMoney::Report::ReportType::QueryTable) { // eInvestmentHoldings is a special-case report, and you cannot configure the // rows & columns of that report. if (d->m_initialState.rowType() < eMyMoney::Report::RowType::AccountByTopAccount) { d->m_tabRowColQuery = new ReportTabRowColQuery(d->ui->m_criteriaTab); d->ui->m_criteriaTab->insertTab(1, d->m_tabRowColQuery, i18n("Rows/Columns")); } if (d->m_initialState.queryColumns() & eMyMoney::Report::QueryColumn::CapitalGain) { d->m_tabCapitalGain = new ReportTabCapitalGain(d->ui->m_criteriaTab); d->ui->m_criteriaTab->insertTab(1, d->m_tabCapitalGain, i18n("Report")); } if (d->m_initialState.queryColumns() & eMyMoney::Report::QueryColumn::Performance) { d->m_tabPerformance = new ReportTabPerformance(d->ui->m_criteriaTab); d->ui->m_criteriaTab->insertTab(1, d->m_tabPerformance, i18n("Report")); } } d->ui->m_criteriaTab->setCurrentIndex(d->ui->m_criteriaTab->indexOf(d->m_tabGeneral)); d->ui->m_criteriaTab->setMinimumSize(500, 200); QList list = MyMoneyFile::instance()->budgetList(); QList::const_iterator it_b; for (it_b = list.constBegin(); it_b != list.constEnd(); ++it_b) { d->m_budgets.push_back(*it_b); } // // Now set up the widgets with proper values // slotReset(); } KReportConfigurationFilterDlg::~KReportConfigurationFilterDlg() { } MyMoneyReport KReportConfigurationFilterDlg::getConfig() const { Q_D(const KReportConfigurationFilterDlg); return d->m_currentState; } void KReportConfigurationFilterDlg::slotSearch() { Q_D(KReportConfigurationFilterDlg); // setup the filter from the dialog widgets auto filter = d->m_tabFilters->setupFilter(); // Copy the m_filter over to the filter part of m_currentConfig. d->m_currentState.assignFilter(filter); // Then extract the report properties d->m_currentState.setName(d->m_tabGeneral->ui->m_editName->text()); d->m_currentState.setComment(d->m_tabGeneral->ui->m_editComment->text()); d->m_currentState.setConvertCurrency(d->m_tabGeneral->ui->m_checkCurrency->isChecked()); d->m_currentState.setFavorite(d->m_tabGeneral->ui->m_checkFavorite->isChecked()); d->m_currentState.setSkipZero(d->m_tabGeneral->ui->m_skipZero->isChecked()); if (d->m_tabRowColPivot) { eMyMoney::Report::DetailLevel dl[4] = { eMyMoney::Report::DetailLevel::All, eMyMoney::Report::DetailLevel::Top, eMyMoney::Report::DetailLevel::Group, eMyMoney::Report::DetailLevel::Total }; d->m_currentState.setDetailLevel(dl[d->m_tabRowColPivot->ui->m_comboDetail->currentIndex()]); // modify the rowtype only if the widget is enabled if (d->m_tabRowColPivot->ui->m_comboRows->isEnabled()) { eMyMoney::Report::RowType rt[2] = { eMyMoney::Report::RowType::ExpenseIncome, eMyMoney::Report::RowType::AssetLiability }; d->m_currentState.setRowType(rt[d->m_tabRowColPivot->ui->m_comboRows->currentIndex()]); } d->m_currentState.setShowingRowTotals(false); if (d->m_tabRowColPivot->ui->m_comboRows->currentIndex() == 0) d->m_currentState.setShowingRowTotals(d->m_tabRowColPivot->ui->m_checkTotalColumn->isChecked()); d->m_currentState.setShowingColumnTotals(d->m_tabRowColPivot->ui->m_checkTotalRow->isChecked()); d->m_currentState.setIncludingSchedules(d->m_tabRowColPivot->ui->m_checkScheduled->isChecked()); d->m_currentState.setIncludingTransfers(d->m_tabRowColPivot->ui->m_checkTransfers->isChecked()); d->m_currentState.setIncludingUnusedAccounts(d->m_tabRowColPivot->ui->m_checkUnused->isChecked()); if (d->m_tabRowColPivot->ui->m_comboBudget->isEnabled()) { d->m_currentState.setBudget(d->m_budgets[d->m_tabRowColPivot->ui->m_comboBudget->currentItem()].id(), d->m_initialState.rowType() == eMyMoney::Report::RowType::BudgetActual); } else { d->m_currentState.setBudget(QString(), false); } //set moving average days if (d->m_tabRowColPivot->ui->m_movingAverageDays->isEnabled()) { d->m_currentState.setMovingAverageDays(d->m_tabRowColPivot->ui->m_movingAverageDays->value()); } } else if (d->m_tabRowColQuery) { eMyMoney::Report::RowType rtq[8] = { eMyMoney::Report::RowType::Category, eMyMoney::Report::RowType::TopCategory, eMyMoney::Report::RowType::Tag, eMyMoney::Report::RowType::Payee, eMyMoney::Report::RowType::Account, eMyMoney::Report::RowType::TopAccount, eMyMoney::Report::RowType::Month, eMyMoney::Report::RowType::Week }; d->m_currentState.setRowType(rtq[d->m_tabRowColQuery->ui->m_comboOrganizeBy->currentIndex()]); unsigned qc = eMyMoney::Report::QueryColumn::None; if (d->m_currentState.queryColumns() & eMyMoney::Report::QueryColumn::Loan) // once a loan report, always a loan report qc = eMyMoney::Report::QueryColumn::Loan; if (d->m_tabRowColQuery->ui->m_checkNumber->isChecked()) qc |= eMyMoney::Report::QueryColumn::Number; if (d->m_tabRowColQuery->ui->m_checkPayee->isChecked()) qc |= eMyMoney::Report::QueryColumn::Payee; if (d->m_tabRowColQuery->ui->m_checkTag->isChecked()) qc |= eMyMoney::Report::QueryColumn::Tag; if (d->m_tabRowColQuery->ui->m_checkCategory->isChecked()) qc |= eMyMoney::Report::QueryColumn::Category; if (d->m_tabRowColQuery->ui->m_checkMemo->isChecked()) qc |= eMyMoney::Report::QueryColumn::Memo; if (d->m_tabRowColQuery->ui->m_checkAccount->isChecked()) qc |= eMyMoney::Report::QueryColumn::Account; if (d->m_tabRowColQuery->ui->m_checkReconciled->isChecked()) qc |= eMyMoney::Report::QueryColumn::Reconciled; if (d->m_tabRowColQuery->ui->m_checkAction->isChecked()) qc |= eMyMoney::Report::QueryColumn::Action; if (d->m_tabRowColQuery->ui->m_checkShares->isChecked()) qc |= eMyMoney::Report::QueryColumn::Shares; if (d->m_tabRowColQuery->ui->m_checkPrice->isChecked()) qc |= eMyMoney::Report::QueryColumn::Price; if (d->m_tabRowColQuery->ui->m_checkBalance->isChecked()) qc |= eMyMoney::Report::QueryColumn::Balance; d->m_currentState.setQueryColumns(static_cast(qc)); d->m_currentState.setTax(d->m_tabRowColQuery->ui->m_checkTax->isChecked()); d->m_currentState.setInvestmentsOnly(d->m_tabRowColQuery->ui->m_checkInvestments->isChecked()); d->m_currentState.setLoansOnly(d->m_tabRowColQuery->ui->m_checkLoans->isChecked()); d->m_currentState.setDetailLevel(d->m_tabRowColQuery->ui->m_checkHideSplitDetails->isChecked() ? eMyMoney::Report::DetailLevel::None : eMyMoney::Report::DetailLevel::All); d->m_currentState.setHideTransactions(d->m_tabRowColQuery->ui->m_checkHideTransactions->isChecked()); d->m_currentState.setShowingColumnTotals(!d->m_tabRowColQuery->ui->m_checkHideTotals->isChecked()); d->m_currentState.setIncludingTransfers(d->m_tabRowColQuery->ui->m_checkTransfers->isChecked()); } if (d->m_tabChart) { eMyMoney::Report::ChartType ct[5] = { eMyMoney::Report::ChartType::Line, eMyMoney::Report::ChartType::Bar, eMyMoney::Report::ChartType::StackedBar, eMyMoney::Report::ChartType::Pie, eMyMoney::Report::ChartType::Ring }; + eMyMoney::Report::ChartPalette cp[4] = { eMyMoney::Report::ChartPalette::Application, eMyMoney::Report::ChartPalette::Default, eMyMoney::Report::ChartPalette::Rainbow, eMyMoney::Report::ChartPalette::Subdued }; d->m_currentState.setChartType(ct[d->m_tabChart->ui->m_comboType->currentIndex()]); - + d->m_currentState.setChartPalette(cp[d->m_tabChart->ui->m_comboPalette->currentIndex()]); d->m_currentState.setChartCHGridLines(d->m_tabChart->ui->m_checkCHGridLines->isChecked()); d->m_currentState.setChartSVGridLines(d->m_tabChart->ui->m_checkSVGridLines->isChecked()); d->m_currentState.setChartDataLabels(d->m_tabChart->ui->m_checkValues->isChecked()); d->m_currentState.setChartByDefault(d->m_tabChart->ui->m_checkShowChart->isChecked()); d->m_currentState.setChartLineWidth(d->m_tabChart->ui->m_lineWidth->value()); d->m_currentState.setLogYAxis(d->m_tabChart->ui->m_logYaxis->isChecked()); } if (d->m_tabRange) { d->m_currentState.setDataRangeStart(d->m_tabRange->ui->m_dataRangeStart->text()); d->m_currentState.setDataRangeEnd(d->m_tabRange->ui->m_dataRangeEnd->text()); d->m_currentState.setDataMajorTick(d->m_tabRange->ui->m_dataMajorTick->text()); d->m_currentState.setDataMinorTick(d->m_tabRange->ui->m_dataMinorTick->text()); d->m_currentState.setYLabelsPrecision(d->m_tabRange->ui->m_yLabelsPrecision->value()); d->m_currentState.setDataFilter((eMyMoney::Report::DataLock)d->m_tabRange->ui->m_dataLock->currentIndex()); eMyMoney::Report::ColumnType ct[6] = { eMyMoney::Report::ColumnType::Days, eMyMoney::Report::ColumnType::Weeks, eMyMoney::Report::ColumnType::Months, eMyMoney::Report::ColumnType::BiMonths, eMyMoney::Report::ColumnType::Quarters, eMyMoney::Report::ColumnType::Years }; bool dy[6] = { true, true, false, false, false, false }; d->m_currentState.setColumnType(ct[d->m_tabRange->ui->m_comboColumns->currentIndex()]); //TODO (Ace) This should be implicit in the call above. MMReport needs fixin' d->m_currentState.setColumnsAreDays(dy[d->m_tabRange->ui->m_comboColumns->currentIndex()]); d->m_currentState.setDateFilter(d->m_dateRange->fromDate(), d->m_dateRange->toDate()); } // setup the date lock eMyMoney::TransactionFilter::Date range = d->m_dateRange->dateRange(); d->m_currentState.setDateFilter(range); if (d->m_tabCapitalGain) { d->m_currentState.setTermSeparator(d->m_tabCapitalGain->ui->m_termSeparator->date()); d->m_currentState.setShowSTLTCapitalGains(d->m_tabCapitalGain->ui->m_showSTLTCapitalGains->isChecked()); d->m_currentState.setSettlementPeriod(d->m_tabCapitalGain->ui->m_settlementPeriod->value()); d->m_currentState.setShowingColumnTotals(!d->m_tabCapitalGain->ui->m_checkHideTotals->isChecked()); d->m_currentState.setInvestmentSum(static_cast(d->m_tabCapitalGain->ui->m_investmentSum->currentData().toInt())); } if (d->m_tabPerformance) { d->m_currentState.setShowingColumnTotals(!d->m_tabPerformance->ui->m_checkHideTotals->isChecked()); d->m_currentState.setInvestmentSum(static_cast(d->m_tabPerformance->ui->m_investmentSum->currentData().toInt())); } done(true); } void KReportConfigurationFilterDlg::slotRowTypeChanged(int row) { Q_D(KReportConfigurationFilterDlg); d->m_tabRowColPivot->ui->m_checkTotalColumn->setEnabled(row == 0); } void KReportConfigurationFilterDlg::slotColumnTypeChanged(int row) { Q_D(KReportConfigurationFilterDlg); if ((d->m_tabRowColPivot->ui->m_comboBudget->isEnabled() && row < 2)) { d->m_tabRange->ui->m_comboColumns->setCurrentItem(i18nc("@item the columns will display monthly data", "Monthly"), false); } } void KReportConfigurationFilterDlg::slotUpdateColumnsCombo() { Q_D(KReportConfigurationFilterDlg); const int monthlyIndex = 2; const int incomeExpenseIndex = 0; const bool isIncomeExpenseForecast = d->m_currentState.isIncludingForecast() && d->m_tabRowColPivot->ui->m_comboRows->currentIndex() == incomeExpenseIndex; if (isIncomeExpenseForecast && d->m_tabRange->ui->m_comboColumns->currentIndex() != monthlyIndex) { d->m_tabRange->ui->m_comboColumns->setCurrentItem(i18nc("@item the columns will display monthly data", "Monthly"), false); } } void KReportConfigurationFilterDlg::slotUpdateColumnsCombo(int) { slotUpdateColumnsCombo(); } void KReportConfigurationFilterDlg::slotLogAxisChanged(int state) { Q_D(KReportConfigurationFilterDlg); if (state == Qt::Checked) d->m_tabRange->setRangeLogarythmic(true); else d->m_tabRange->setRangeLogarythmic(false); } void KReportConfigurationFilterDlg::slotReset() { Q_D(KReportConfigurationFilterDlg); // // Set up the widget from the initial filter // d->m_currentState = d->m_initialState; // // Report Properties // d->m_tabGeneral->ui->m_editName->setText(d->m_initialState.name()); d->m_tabGeneral->ui->m_editComment->setText(d->m_initialState.comment()); d->m_tabGeneral->ui->m_checkCurrency->setChecked(d->m_initialState.isConvertCurrency()); d->m_tabGeneral->ui->m_checkFavorite->setChecked(d->m_initialState.isFavorite()); if (d->m_initialState.isIncludingPrice() || d->m_initialState.isSkippingZero()) { d->m_tabGeneral->ui->m_skipZero->setChecked(d->m_initialState.isSkippingZero()); } else { d->m_tabGeneral->ui->m_skipZero->setEnabled(false); } if (d->m_tabRowColPivot) { KComboBox *combo = d->m_tabRowColPivot->ui->m_comboDetail; switch (d->m_initialState.detailLevel()) { case eMyMoney::Report::DetailLevel::None: case eMyMoney::Report::DetailLevel::End: case eMyMoney::Report::DetailLevel::All: combo->setCurrentItem(i18nc("All accounts", "All"), false); break; case eMyMoney::Report::DetailLevel::Top: combo->setCurrentItem(i18n("Top-Level"), false); break; case eMyMoney::Report::DetailLevel::Group: combo->setCurrentItem(i18n("Groups"), false); break; case eMyMoney::Report::DetailLevel::Total: combo->setCurrentItem(i18n("Totals"), false); break; } combo = d->m_tabRowColPivot->ui->m_comboRows; switch (d->m_initialState.rowType()) { case eMyMoney::Report::RowType::ExpenseIncome: case eMyMoney::Report::RowType::Budget: case eMyMoney::Report::RowType::BudgetActual: combo->setCurrentItem(i18n("Income & Expenses"), false); // income / expense break; default: combo->setCurrentItem(i18n("Assets & Liabilities"), false); // asset / liability break; } d->m_tabRowColPivot->ui->m_checkTotalColumn->setChecked(d->m_initialState.isShowingRowTotals()); d->m_tabRowColPivot->ui->m_checkTotalRow->setChecked(d->m_initialState.isShowingColumnTotals()); slotRowTypeChanged(combo->currentIndex()); //load budgets combo if (d->m_initialState.rowType() == eMyMoney::Report::RowType::Budget || d->m_initialState.rowType() == eMyMoney::Report::RowType::BudgetActual) { d->m_tabRowColPivot->ui->m_comboRows->setEnabled(false); d->m_tabRowColPivot->ui->m_rowsLabel->setEnabled(false); d->m_tabRowColPivot->ui->m_budgetFrame->setEnabled(!d->m_budgets.empty()); auto i = 0; for (QVector::const_iterator it_b = d->m_budgets.constBegin(); it_b != d->m_budgets.constEnd(); ++it_b) { d->m_tabRowColPivot->ui->m_comboBudget->insertItem((*it_b).name(), i); //set the current selected item if ((d->m_initialState.budget() == "Any" && (*it_b).budgetStart().year() == QDate::currentDate().year()) || d->m_initialState.budget() == (*it_b).id()) d->m_tabRowColPivot->ui->m_comboBudget->setCurrentItem(i); i++; } } //set moving average days spinbox QSpinBox *spinbox = d->m_tabRowColPivot->ui->m_movingAverageDays; spinbox->setEnabled(d->m_initialState.isIncludingMovingAverage()); d->m_tabRowColPivot->ui->m_movingAverageLabel->setEnabled(d->m_initialState.isIncludingMovingAverage()); if (d->m_initialState.isIncludingMovingAverage()) { spinbox->setValue(d->m_initialState.movingAverageDays()); } d->m_tabRowColPivot->ui->m_checkScheduled->setChecked(d->m_initialState.isIncludingSchedules()); d->m_tabRowColPivot->ui->m_checkTransfers->setChecked(d->m_initialState.isIncludingTransfers()); d->m_tabRowColPivot->ui->m_checkUnused->setChecked(d->m_initialState.isIncludingUnusedAccounts()); } else if (d->m_tabRowColQuery) { KComboBox *combo = d->m_tabRowColQuery->ui->m_comboOrganizeBy; switch (d->m_initialState.rowType()) { case eMyMoney::Report::RowType::NoRows: case eMyMoney::Report::RowType::Category: combo->setCurrentItem(i18n("Categories"), false); break; case eMyMoney::Report::RowType::TopCategory: combo->setCurrentItem(i18n("Top Categories"), false); break; case eMyMoney::Report::RowType::Tag: combo->setCurrentItem(i18n("Tags"), false); break; case eMyMoney::Report::RowType::Payee: combo->setCurrentItem(i18n("Payees"), false); break; case eMyMoney::Report::RowType::Account: combo->setCurrentItem(i18n("Accounts"), false); break; case eMyMoney::Report::RowType::TopAccount: combo->setCurrentItem(i18n("Top Accounts"), false); break; case eMyMoney::Report::RowType::Month: combo->setCurrentItem(i18n("Month"), false); break; case eMyMoney::Report::RowType::Week: combo->setCurrentItem(i18n("Week"), false); break; default: throw MYMONEYEXCEPTION_CSTRING("KReportConfigurationFilterDlg::slotReset(): QueryTable report has invalid rowtype"); } unsigned qc = d->m_initialState.queryColumns(); d->m_tabRowColQuery->ui->m_checkNumber->setChecked(qc & eMyMoney::Report::QueryColumn::Number); d->m_tabRowColQuery->ui->m_checkPayee->setChecked(qc & eMyMoney::Report::QueryColumn::Payee); d->m_tabRowColQuery->ui->m_checkTag->setChecked(qc & eMyMoney::Report::QueryColumn::Tag); d->m_tabRowColQuery->ui->m_checkCategory->setChecked(qc & eMyMoney::Report::QueryColumn::Category); d->m_tabRowColQuery->ui->m_checkMemo->setChecked(qc & eMyMoney::Report::QueryColumn::Memo); d->m_tabRowColQuery->ui->m_checkAccount->setChecked(qc & eMyMoney::Report::QueryColumn::Account); d->m_tabRowColQuery->ui->m_checkReconciled->setChecked(qc & eMyMoney::Report::QueryColumn::Reconciled); d->m_tabRowColQuery->ui->m_checkAction->setChecked(qc & eMyMoney::Report::QueryColumn::Action); d->m_tabRowColQuery->ui->m_checkShares->setChecked(qc & eMyMoney::Report::QueryColumn::Shares); d->m_tabRowColQuery->ui->m_checkPrice->setChecked(qc & eMyMoney::Report::QueryColumn::Price); d->m_tabRowColQuery->ui->m_checkBalance->setChecked(qc & eMyMoney::Report::QueryColumn::Balance); d->m_tabRowColQuery->ui->m_checkTax->setChecked(d->m_initialState.isTax()); d->m_tabRowColQuery->ui->m_checkInvestments->setChecked(d->m_initialState.isInvestmentsOnly()); d->m_tabRowColQuery->ui->m_checkLoans->setChecked(d->m_initialState.isLoansOnly()); d->m_tabRowColQuery->ui->m_checkHideTransactions->setChecked(d->m_initialState.isHideTransactions()); d->m_tabRowColQuery->ui->m_checkHideTotals->setChecked(!d->m_initialState.isShowingColumnTotals()); d->m_tabRowColQuery->ui->m_checkHideSplitDetails->setEnabled(!d->m_initialState.isHideTransactions()); d->m_tabRowColQuery->ui->m_checkHideSplitDetails->setChecked (d->m_initialState.detailLevel() == eMyMoney::Report::DetailLevel::None || d->m_initialState.isHideTransactions()); d->m_tabRowColQuery->ui->m_checkTransfers->setChecked(d->m_initialState.isIncludingTransfers()); } if (d->m_tabChart) { KMyMoneyGeneralCombo* combo = d->m_tabChart->ui->m_comboType; switch (d->m_initialState.chartType()) { case eMyMoney::Report::ChartType::None: combo->setCurrentItem(static_cast(eMyMoney::Report::ChartType::Line)); break; case eMyMoney::Report::ChartType::Line: case eMyMoney::Report::ChartType::Bar: case eMyMoney::Report::ChartType::StackedBar: case eMyMoney::Report::ChartType::Pie: case eMyMoney::Report::ChartType::Ring: combo->setCurrentItem(static_cast(d->m_initialState.chartType())); break; default: throw MYMONEYEXCEPTION_CSTRING("KReportConfigurationFilterDlg::slotReset(): Report has invalid charttype"); } + combo = d->m_tabChart->ui->m_comboPalette; + switch (d->m_initialState.chartPalette()) { + case eMyMoney::Report::ChartPalette::Application: + case eMyMoney::Report::ChartPalette::Default: + case eMyMoney::Report::ChartPalette::Rainbow: + case eMyMoney::Report::ChartPalette::Subdued: + combo->setCurrentItem(static_cast(d->m_initialState.chartPalette())); + break; + default: + throw MYMONEYEXCEPTION_CSTRING("KReportConfigurationFilterDlg::slotReset(): Report has invalid chartpalette"); + } d->m_tabChart->ui->m_checkCHGridLines->setChecked(d->m_initialState.isChartCHGridLines()); d->m_tabChart->ui->m_checkSVGridLines->setChecked(d->m_initialState.isChartSVGridLines()); d->m_tabChart->ui->m_checkValues->setChecked(d->m_initialState.isChartDataLabels()); d->m_tabChart->ui->m_checkShowChart->setChecked(d->m_initialState.isChartByDefault()); d->m_tabChart->ui->m_lineWidth->setValue(d->m_initialState.chartLineWidth()); d->m_tabChart->ui->m_logYaxis->setChecked(d->m_initialState.isLogYAxis()); } if (d->m_tabRange) { d->m_tabRange->ui->m_dataRangeStart->setText(d->m_initialState.dataRangeStart()); d->m_tabRange->ui->m_dataRangeEnd->setText(d->m_initialState.dataRangeEnd()); d->m_tabRange->ui->m_dataMajorTick->setText(d->m_initialState.dataMajorTick()); d->m_tabRange->ui->m_dataMinorTick->setText(d->m_initialState.dataMinorTick()); d->m_tabRange->ui->m_yLabelsPrecision->setValue(d->m_initialState.yLabelsPrecision()); d->m_tabRange->ui->m_dataLock->setCurrentIndex((int)d->m_initialState.dataFilter()); KComboBox *combo = d->m_tabRange->ui->m_comboColumns; if (d->m_initialState.isColumnsAreDays()) { switch (d->m_initialState.columnType()) { case eMyMoney::Report::ColumnType::NoColumns: case eMyMoney::Report::ColumnType::Days: combo->setCurrentItem(i18nc("@item the columns will display daily data", "Daily"), false); break; case eMyMoney::Report::ColumnType::Weeks: combo->setCurrentItem(i18nc("@item the columns will display weekly data", "Weekly"), false); break; default: break; } } else { switch (d->m_initialState.columnType()) { case eMyMoney::Report::ColumnType::NoColumns: case eMyMoney::Report::ColumnType::Months: combo->setCurrentItem(i18nc("@item the columns will display monthly data", "Monthly"), false); break; case eMyMoney::Report::ColumnType::BiMonths: combo->setCurrentItem(i18nc("@item the columns will display bi-monthly data", "Bi-Monthly"), false); break; case eMyMoney::Report::ColumnType::Quarters: combo->setCurrentItem(i18nc("@item the columns will display quarterly data", "Quarterly"), false); break; case eMyMoney::Report::ColumnType::Years: combo->setCurrentItem(i18nc("@item the columns will display yearly data", "Yearly"), false); break; default: break; } } } if (d->m_tabCapitalGain) { d->m_tabCapitalGain->ui->m_termSeparator->setDate(d->m_initialState.termSeparator()); d->m_tabCapitalGain->ui->m_showSTLTCapitalGains->setChecked(d->m_initialState.isShowingSTLTCapitalGains()); d->m_tabCapitalGain->ui->m_settlementPeriod->setValue(d->m_initialState.settlementPeriod()); d->m_tabCapitalGain->ui->m_checkHideTotals->setChecked(!d->m_initialState.isShowingColumnTotals()); d->m_tabCapitalGain->ui->m_investmentSum->blockSignals(true); d->m_tabCapitalGain->ui->m_investmentSum->clear(); d->m_tabCapitalGain->ui->m_investmentSum->addItem(i18n("Only owned"), static_cast(eMyMoney::Report::InvestmentSum::Owned)); d->m_tabCapitalGain->ui->m_investmentSum->addItem(i18n("Only sold"), static_cast(eMyMoney::Report::InvestmentSum::Sold)); d->m_tabCapitalGain->ui->m_investmentSum->blockSignals(false); d->m_tabCapitalGain->ui->m_investmentSum->setCurrentIndex(d->m_tabCapitalGain->ui->m_investmentSum->findData(static_cast(d->m_initialState.investmentSum()))); } if (d->m_tabPerformance) { d->m_tabPerformance->ui->m_checkHideTotals->setChecked(!d->m_initialState.isShowingColumnTotals()); d->m_tabPerformance->ui->m_investmentSum->blockSignals(true); d->m_tabPerformance->ui->m_investmentSum->clear(); d->m_tabPerformance->ui->m_investmentSum->addItem(i18n("From period"), static_cast(eMyMoney::Report::InvestmentSum::Period)); d->m_tabPerformance->ui->m_investmentSum->addItem(i18n("Owned and sold"), static_cast(eMyMoney::Report::InvestmentSum::OwnedAndSold)); d->m_tabPerformance->ui->m_investmentSum->addItem(i18n("Only owned"), static_cast(eMyMoney::Report::InvestmentSum::Owned)); d->m_tabPerformance->ui->m_investmentSum->addItem(i18n("Only sold"), static_cast(eMyMoney::Report::InvestmentSum::Sold)); d->m_tabPerformance->ui->m_investmentSum->blockSignals(false); d->m_tabPerformance->ui->m_investmentSum->setCurrentIndex(d->m_tabPerformance->ui->m_investmentSum->findData(static_cast(d->m_initialState.investmentSum()))); } d->m_tabFilters->resetFilter(d->m_initialState); if (d->m_dateRange) { d->m_initialState.updateDateFilter(); QDate dateFrom, dateTo; if (d->m_initialState.dateFilter(dateFrom, dateTo)) { if (d->m_initialState.isDateUserDefined()) { d->m_dateRange->setDateRange(dateFrom, dateTo); } else { d->m_dateRange->setDateRange(d->m_initialState.dateRange()); } } else { d->m_dateRange->setDateRange(eMyMoney::TransactionFilter::Date::All); } } } void KReportConfigurationFilterDlg::slotShowHelp() { Q_D(KReportConfigurationFilterDlg); if (d->ui->m_tabWidget->currentIndex() == 1) d->m_tabFilters->slotShowHelp(); else KHelpClient::invokeHelp("details.reports.config"); } //TODO Fix the reports and engine to include transfers even if categories are filtered - bug #1523508 void KReportConfigurationFilterDlg::slotUpdateCheckTransfers() { Q_D(KReportConfigurationFilterDlg); auto cb = d->m_tabRowColPivot->ui->m_checkTransfers; if (!d->m_tabFilters->categoriesView()->allItemsSelected()) { cb->setChecked(false); cb->setDisabled(true); } else { cb->setEnabled(true); } } diff --git a/kmymoney/plugins/views/reports/reporttabchart.ui b/kmymoney/plugins/views/reports/reporttabchart.ui index 77c4392ab..3b40df59d 100644 --- a/kmymoney/plugins/views/reports/reporttabchart.ui +++ b/kmymoney/plugins/views/reports/reporttabchart.ui @@ -1,199 +1,241 @@ ReportTabChart 0 0 600 234 Chart Tab <p>On this tab, you configure the chart drawn for this report.</p> 0 0 <p>Select what form you would like the chart to be drawn as.</p> Chart Type false Qt::Horizontal QSizePolicy::Expanding 121 20 + + + + + + + 0 + 0 + + + + <p>Select what form you would like the chart to be drawn as.</p> + + + Chart Palette + + + false + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 121 + 20 + + + + + + <p>Select this option to draw the numeric values for data points next to their plot location.</p> Draw values on chart <p>Select this option to cause the report to be shown as a chart when you first open the report. Otherwise, it will come up as a text report.</p> Show as chart by default SV grid lines <p>Select this option to show horizontal and vertical grid lines on the chart.</p> CH grid lines Logarithmic vertical axis 0 0 <p>Select what width should be used to draw the line on the chart</p> Line width false 1 10 Qt::Horizontal QSizePolicy::Expanding 121 20 Qt::Vertical QSizePolicy::Expanding 20 20 KMyMoneyGeneralCombo QWidget
kmymoneygeneralcombo.h
1
m_checkCHGridLines m_checkSVGridLines m_checkValues m_checkShowChart m_logYaxis m_lineWidth
diff --git a/kmymoney/plugins/views/reports/reporttabimpl.cpp b/kmymoney/plugins/views/reports/reporttabimpl.cpp index e252f39b4..42219f7f8 100644 --- a/kmymoney/plugins/views/reports/reporttabimpl.cpp +++ b/kmymoney/plugins/views/reports/reporttabimpl.cpp @@ -1,331 +1,336 @@ /* This file is part of the KDE project Copyright (C) 2009 Laurent Montel (C) 2017 by Łukasz Wojniłowicz This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "reporttabimpl.h" #include #include "daterangedlg.h" #include "ui_reporttabgeneral.h" #include "ui_reporttabrowcolpivot.h" #include "ui_reporttabrowcolquery.h" #include "ui_reporttabchart.h" #include "ui_reporttabrange.h" #include "ui_reporttabcapitalgain.h" #include "ui_reporttabperformance.h" #include "mymoney/mymoneyreport.h" #include "mymoneyenums.h" ReportTabGeneral::ReportTabGeneral(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabGeneral; ui->setupUi(this); } ReportTabGeneral::~ReportTabGeneral() { delete ui; } ReportTabRowColPivot::ReportTabRowColPivot(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabRowColPivot; ui->setupUi(this); } ReportTabRowColPivot::~ReportTabRowColPivot() { delete ui; } ReportTabRowColQuery::ReportTabRowColQuery(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabRowColQuery; ui->setupUi(this); ui->buttonGroup1->setExclusive(false); ui->buttonGroup1->setId(ui->m_checkMemo, 0); ui->buttonGroup1->setId(ui->m_checkShares, 1); ui->buttonGroup1->setId(ui->m_checkPrice, 2); ui->buttonGroup1->setId(ui->m_checkReconciled, 3); ui->buttonGroup1->setId(ui->m_checkAccount, 4); ui->buttonGroup1->setId(ui->m_checkNumber, 5); ui->buttonGroup1->setId(ui->m_checkPayee, 6); ui->buttonGroup1->setId(ui->m_checkCategory, 7); ui->buttonGroup1->setId(ui->m_checkAction, 8); ui->buttonGroup1->setId(ui->m_checkBalance, 9); connect(ui->m_checkHideTransactions, &QAbstractButton::toggled, this, &ReportTabRowColQuery::slotHideTransactionsChanged); } void ReportTabRowColQuery::slotHideTransactionsChanged(bool checked) { if (checked) // toggle m_checkHideSplitDetails only if it's mandatory ui->m_checkHideSplitDetails->setChecked(checked); ui->m_checkHideSplitDetails->setEnabled(!checked); // hiding transactions without hiding splits isn't allowed } ReportTabRowColQuery::~ReportTabRowColQuery() { delete ui; } ReportTabChart::ReportTabChart(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabChart; ui->setupUi(this); ui->m_comboType->addItem(i18nc("type of graphic chart", "Line"), static_cast(eMyMoney::Report::ChartType::Line)); ui->m_comboType->addItem(i18nc("type of graphic chart", "Bar"), static_cast(eMyMoney::Report::ChartType::Bar)); ui->m_comboType->addItem(i18nc("type of graphic chart", "Stacked Bar"), static_cast(eMyMoney::Report::ChartType::StackedBar)); ui->m_comboType->addItem(i18nc("type of graphic chart", "Pie"), static_cast(eMyMoney::Report::ChartType::Pie)); ui->m_comboType->addItem(i18nc("type of graphic chart", "Ring"), static_cast(eMyMoney::Report::ChartType::Ring)); connect(ui->m_comboType, static_cast(&QComboBox::currentIndexChanged), this, &ReportTabChart::slotChartTypeChanged); emit ui->m_comboType->currentIndexChanged(ui->m_comboType->currentIndex()); + + ui->m_comboPalette->addItem(i18nc("type of graphic palette", "Use application setting"), static_cast(eMyMoney::Report::ChartPalette::Application)); + ui->m_comboPalette->addItem(i18nc("type of graphic palette", "Default"), static_cast(eMyMoney::Report::ChartPalette::Default)); + ui->m_comboPalette->addItem(i18nc("type of graphic palette", "Rainbow"), static_cast(eMyMoney::Report::ChartPalette::Rainbow)); + ui->m_comboPalette->addItem(i18nc("type of graphic palette", "Subdued"), static_cast(eMyMoney::Report::ChartPalette::Subdued)); } ReportTabChart::~ReportTabChart() { delete ui; } void ReportTabChart::slotChartTypeChanged(int index) { if (index == static_cast(eMyMoney::Report::ChartType::Pie) || index == static_cast(eMyMoney::Report::ChartType::Ring)) { ui->m_checkCHGridLines->setText(i18n("Show circular grid lines")); ui->m_checkSVGridLines->setText(i18n("Show sagittal grid lines")); ui->m_logYaxis->setChecked(false); ui->m_logYaxis->setEnabled(false); } else { ui->m_checkCHGridLines->setText(i18n("Show horizontal grid lines")); ui->m_checkSVGridLines->setText(i18n("Show vertical grid lines")); ui->m_logYaxis->setEnabled(true); } } ReportTabRange::ReportTabRange(QWidget *parent) : QWidget(parent), ui(new Ui::ReportTabRange) { ui->setupUi(this); m_dateRange = new DateRangeDlg; ui->dateRangeGrid->addWidget(m_dateRange, 0, 0, 1, 2); connect(ui->m_yLabelsPrecision, static_cast(&QSpinBox::valueChanged), this, &ReportTabRange::slotYLabelsPrecisionChanged); emit ui->m_yLabelsPrecision->valueChanged(ui->m_yLabelsPrecision->value()); connect(ui->m_dataRangeStart, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedStart); connect(ui->m_dataRangeEnd, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedEnd); connect(ui->m_dataMajorTick, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedMajor); connect(ui->m_dataMinorTick, &QLineEdit::editingFinished, this, &ReportTabRange::slotEditingFinishedMinor); connect(ui->m_dataLock, static_cast(&QComboBox::currentIndexChanged), this, &ReportTabRange::slotDataLockChanged); emit ui->m_dataLock->currentIndexChanged(ui->m_dataLock->currentIndex()); } ReportTabRange::~ReportTabRange() { delete ui; } void ReportTabRange::setRangeLogarythmic(bool set) { // major and minor tick have no influence if axis is logarithmic so hide them if (set) { ui->lblDataMajorTick->hide(); ui->lblDataMinorTick->hide(); ui->m_dataMajorTick->hide(); ui->m_dataMinorTick->hide(); } else { ui->lblDataMajorTick->show(); ui->lblDataMinorTick->show(); ui->m_dataMajorTick->show(); ui->m_dataMinorTick->show(); } } void ReportTabRange::slotEditingFinished(EDimension dim) { qreal dataRangeStart = locale().toDouble(ui->m_dataRangeStart->text()); qreal dataRangeEnd = locale().toDouble(ui->m_dataRangeEnd->text()); qreal dataMajorTick = locale().toDouble(ui->m_dataMajorTick->text()); qreal dataMinorTick = locale().toDouble(ui->m_dataMinorTick->text()); if (dataRangeEnd < dataRangeStart) { // end must be higher than start if (dim == eRangeEnd) { ui->m_dataRangeStart->setText(ui->m_dataRangeEnd->text()); dataRangeStart = dataRangeEnd; } else { ui->m_dataRangeEnd->setText(ui->m_dataRangeStart->text()); dataRangeEnd = dataRangeStart; } } if ((dataRangeStart != 0 || dataRangeEnd != 0)) { // if data range isn't going to be reset if ((dataRangeEnd - dataRangeStart) < dataMajorTick) // major tick cannot be greater than data range dataMajorTick = dataRangeEnd - dataRangeStart; if (dataMajorTick != 0 && // if major tick isn't going to be reset dataMajorTick < (dataRangeEnd - dataRangeStart) * 0.01) // constraint major tick to be greater or equal to 1% of data range dataMajorTick = (dataRangeEnd - dataRangeStart) * 0.01; // that should produce more than 256 Y labels in KReportChartView::slotNeedUpdate //set precision of major tick to be greater by 1 ui->m_dataMajorTick->setText(locale().toString(dataMajorTick, 'f', ui->m_yLabelsPrecision->value() + 1).remove(locale().groupSeparator()).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + locale().decimalPoint() + "$"))); } if (dataMajorTick < dataMinorTick) { // major tick must be higher than minor if (dim == eMinorTick) { ui->m_dataMajorTick->setText(ui->m_dataMinorTick->text()); dataMajorTick = dataMinorTick; } else { ui->m_dataMinorTick->setText(ui->m_dataMajorTick->text()); dataMinorTick = dataMajorTick; } } if (dataMinorTick < dataMajorTick * 0.1) { // constraint minor tick to be greater or equal to 10% of major tick, and set precision to be greater by 1 dataMinorTick = dataMajorTick * 0.1; ui->m_dataMinorTick->setText(locale().toString(dataMinorTick, 'f', ui->m_yLabelsPrecision->value() + 1).remove(locale().groupSeparator()).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + locale().decimalPoint() + "$"))); } } void ReportTabRange::slotEditingFinishedStart() { slotEditingFinished(eRangeStart); } void ReportTabRange::slotEditingFinishedEnd() { slotEditingFinished(eRangeEnd); } void ReportTabRange::slotEditingFinishedMajor() { slotEditingFinished(eMajorTick); } void ReportTabRange::slotEditingFinishedMinor() { slotEditingFinished(eMinorTick); } void ReportTabRange::slotYLabelsPrecisionChanged(const int& value) { ui->m_dataRangeStart->setValidator(0); ui->m_dataRangeEnd->setValidator(0); ui->m_dataMajorTick->setValidator(0); ui->m_dataMinorTick->setValidator(0); MyDoubleValidator *dblVal = new MyDoubleValidator(value); ui->m_dataRangeStart->setValidator(dblVal); ui->m_dataRangeEnd->setValidator(dblVal); MyDoubleValidator *dblVal2 = new MyDoubleValidator(value + 1); ui->m_dataMajorTick->setValidator(dblVal2); ui->m_dataMinorTick->setValidator(dblVal2); } void ReportTabRange::slotDataLockChanged(int index) { if (index == static_cast(eMyMoney::Report::DataLock::Automatic)) { ui->m_dataRangeStart->setText(QStringLiteral("0")); ui->m_dataRangeEnd->setText(QStringLiteral("0")); ui->m_dataMajorTick->setText(QStringLiteral("0")); ui->m_dataMinorTick->setText(QStringLiteral("0")); ui->m_dataRangeStart->setEnabled(false); ui->m_dataRangeEnd->setEnabled(false); ui->m_dataMajorTick->setEnabled(false); ui->m_dataMinorTick->setEnabled(false); } else { ui->m_dataRangeStart->setEnabled(true); ui->m_dataRangeEnd->setEnabled(true); ui->m_dataMajorTick->setEnabled(true); ui->m_dataMinorTick->setEnabled(true); } } ReportTabCapitalGain::ReportTabCapitalGain(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabCapitalGain; ui->setupUi(this); connect(ui->m_investmentSum, static_cast(&QComboBox::currentIndexChanged), this, &ReportTabCapitalGain::slotInvestmentSumChanged); } ReportTabCapitalGain::~ReportTabCapitalGain() { delete ui; } void ReportTabCapitalGain::slotInvestmentSumChanged(int index) { Q_UNUSED(index); if (ui->m_investmentSum->currentData() == static_cast(eMyMoney::Report::InvestmentSum::Owned)) { ui->m_settlementPeriod->setValue(0); ui->m_settlementPeriod->setEnabled(false); ui->m_showSTLTCapitalGains->setChecked(false); ui->m_showSTLTCapitalGains->setEnabled(false); ui->m_termSeparator->setEnabled(false); } else { ui->m_settlementPeriod->setEnabled(true); ui->m_showSTLTCapitalGains->setEnabled(true); ui->m_termSeparator->setEnabled(true); } } ReportTabPerformance::ReportTabPerformance(QWidget *parent) : QWidget(parent) { ui = new Ui::ReportTabPerformance; ui->setupUi(this); } ReportTabPerformance::~ReportTabPerformance() { delete ui; } MyDoubleValidator::MyDoubleValidator(int decimals, QObject * parent) : QDoubleValidator(0, 0, decimals, parent) { } QValidator::State MyDoubleValidator::validate(QString &s, int &i) const { Q_UNUSED(i); if (s.isEmpty() || s == "-") { return QValidator::Intermediate; } QChar decimalPoint = locale().decimalPoint(); if(s.indexOf(decimalPoint) != -1) { int charsAfterPoint = s.length() - s.indexOf(decimalPoint) - 1; if (charsAfterPoint > decimals()) { return QValidator::Invalid; } } bool ok; locale().toDouble(s, &ok); if (ok) { return QValidator::Acceptable; } else { return QValidator::Invalid; } } diff --git a/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp b/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp index 6964cac12..ac042f5e2 100644 --- a/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp +++ b/kmymoney/plugins/xmlhelper/xmlstoragehelper.cpp @@ -1,1255 +1,1305 @@ /* * Copyright 2004-2006 Ace Jones * Copyright 2006 Darren Gould * Copyright 2007-2010 Alvaro Soliverez * Copyright 2017-2018 Łukasz Wojniłowicz * Copyright 2019 Thomas Baumgart * * 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, Validity }; 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, - Validity, + Validity, ChartPalette, // 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")}, {Element::Report::Validity, QStringLiteral("VALIDITY")} }; 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::ChartPalette, QStringLiteral("chartpalette")}, {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")}, {Attribute::Report::Validity, QStringLiteral("validity")} }; return attributeNames.value(attributeID); } uint qHash(const Attribute::Report key, uint seed) { Q_UNUSED(seed); return ::qHash(static_cast(key), 0); } 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); } uint qHash(const Attribute::Budget key, uint seed) { Q_UNUSED(seed); return ::qHash(static_cast(key), 0); } 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 chartPaletteLUT() + { + static const QHash lut { + {eMyMoney::Report::ChartPalette::Application, QStringLiteral("appplication")}, + {eMyMoney::Report::ChartPalette::Default, QStringLiteral("default")}, + {eMyMoney::Report::ChartPalette::Rainbow, QStringLiteral("rainbow")}, + {eMyMoney::Report::ChartPalette::Subdued, QStringLiteral("subdued")}, + }; + return lut; + } + + QString reportNames(eMyMoney::Report::ChartPalette textID) + { + return chartPaletteLUT().value(textID); + } + + eMyMoney::Report::ChartPalette stringToChartPalette(const QString &text) + { + return chartPaletteLUT().key(text, eMyMoney::Report::ChartPalette::End); + } + + QHash paletteAttributeLUT() + { + static const QHash lut { + {0, QStringLiteral("application")}, + {1, QStringLiteral("default")}, + {2, QStringLiteral("rainbow")}, + {3, QStringLiteral("subdued")}, + }; + return lut; + } + + QString paletteAttributeToString(int textID) + { + return paletteAttributeLUT().value(textID); + } + + int stringToPaletteAttribute(const QString &text) + { + return paletteAttributeLUT().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 validityAttributeLUT() { static const QHash lut { {0, QStringLiteral("any")}, {1, QStringLiteral("valid")}, {2, QStringLiteral("invalid")}, }; return lut; } QString validityAttributeToString(int textID) { return validityAttributeLUT().value(textID); } int stringToValidityAttribute(const QString &text) { return validityAttributeLUT().key(text, 0); } QHash dateLockLUT() { 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(eMyMoney::TransactionFilter::Date textID) { return dateLockLUT().value(textID); } eMyMoney::TransactionFilter::Date stringToDateLockAttribute(const QString &text) { 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() { 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(eMyMoney::Account::Type type) { return accountTypeAttributeLUT().value(type); } eMyMoney::Account::Type stringToAccountTypeAttribute(const QString &text) { 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; eMyMoney::TransactionFilter::Date dateLock = static_cast(datelockstr.toUInt(&ok)); if (!ok) { dateLock = stringToDateLockAttribute(datelockstr); } 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)), report.isIncludingBudgetActuals()); 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); + const auto chartPaletteFromXML = stringToChartPalette(node.attribute(attributeName(Attribute::Report::ChartPalette))); + if (chartPaletteFromXML != eMyMoney::Report::ChartPalette::End) + report.setChartPalette(chartPaletteFromXML); + else + report.setChartPalette(eMyMoney::Report::ChartPalette::Application); + 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::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 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); 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()); report.setIncludingTransfers(node.attribute(attributeName(Attribute::Report::IncludesTransfers), "0").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))) { 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))) { const auto state = stringToStateAttribute(c.attribute(attributeName(Attribute::Report::State))); if (state != -1) report.addState(state); } if (elementName(Element::Report::Validity) == c.tagName() && c.hasAttribute(attributeName(Attribute::Report::Validity))) { const auto validity = stringToValidityAttribute(c.attribute(attributeName(Attribute::Report::Validity))); if (validity != -1) report.addValidity(validity); } 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))) { 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.15"); 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 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(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::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::ChartPalette), reportNames(report.chartPalette())); 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 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())); el.setAttribute(attributeName(Attribute::Report::IncludesTransfers), report.isIncludingTransfers()); // 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; } } QList validitylist; if (report.validities(validitylist) && ! validitylist.empty()) { // iterate over payees, and add each one QList::const_iterator it_validity = validitylist.constBegin(); while (it_validity != validitylist.constEnd()) { QDomElement p = document.createElement(elementName(Element::Report::Validity)); p.setAttribute(attributeName(Attribute::Report::Validity), validityAttributeToString(*it_validity)); el.appendChild(p); ++it_validity; } } // // 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(*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); } }