diff --git a/doc/credits.docbook b/doc/credits.docbook index c71170edf..0ce8650b0 100644 --- a/doc/credits.docbook +++ b/doc/credits.docbook @@ -1,237 +1,235 @@ - 2014-08-30 - 4.7.01 + 2019-04-05 + 5.0.4 Credits - Program Copyright © 2000-2018 The KMyMoney Development Team + Program Copyright © 2000-2019 The KMyMoney Development Team &underGPL; - Documentation Copyright © 2000-2018 The KMyMoney Development Team + Documentation Copyright © 2000-2019 The KMyMoney Development Team &underFDL; Active Developers Thomas Baumgart &Thomas.Baumgart.mail; Release manager and de-facto Maintainer. Core engine. Project admin. - - - Łukasz Wojniłowicz &Lukasz.Wojnilowicz.mail; - Developer. - - - Ralf Habacker &Ralf.Habacker.mail; Developer. - - - - Cristian Oneţ &Cristian.Onet.mail; - Patches and Plugins. - - - - - - Cristian Dávid &Christian.David.mail; - Developer. - - - - Special Thanks Jack Ostroff &Jack.H.Ostroff.mail; Documentation and user support. Kevin Tambascio <email>ktambascio@users.sourceforge.net</email> Initial investment support. Javier Campos Morales <email>javi_c@users.sourceforge.net</email> Developer & Artist. Robert Wadley &Robert.Wadley.mail; Artist. Icons, splash screen, home view. Laurent Montel <email>montel@kde.org</email> Patches and port to &kde;4. Wolfgang Rohdewald <email>woro@users.sourceforge.net</email> Patches. Marko Käning <email>mk-lists@email.de</email> Patches, packaging and KF5-CI for OS-X. Allan Anderson ✝&Allan.Anderson.mail; Patches. CSV import/export. Inactive Developers and Contributors to Previous Versions + + + Cristian Oneţ &Cristian.Onet.mail; + Patches and Plugins. + + + + + + Cristian Dávid &Christian.David.mail; + Developer. + + + + + + Łukasz Wojniłowicz &Lukasz.Wojnilowicz.mail; + Developer. + + + Michael Edwardes &Michael.T.Edwardes.mail; Original author, much initial source code. Project admin. Alvaro Soliverez &Alvaro.Soliverez.mail; Forecast. Reports. Ace Jones &Ace.Jones.mail; Reporting logic. OFX Import. Online Quotes. Documentation editor. Tony Bloomfield &Tony.Bloomfield.mail; GnuCash Importer. Database support. Felix Rodriguez <email>frodriguez@users.sourceforge.net</email> Project admin. John C <email>tacoturtle@users.sourceforge.net</email> Developer. Fernando Vilas &Fernando.Vilas.mail; Database support. Roger Lum &Roger.Lum.mail; Documentation. Darin Strait &Darin.Strait.mail; Documentation. Colin Wright &Colin.Wright.mail; Patches and Documentation. Bernd Gonsior <email>bernd.gonsior@googlemail.com</email> Reports. Migration to &kde;4/&Qt;4. Ian Neal <email>iann_bugzilla@blueyonder.co.uk</email> Scheduled transactions and calendars. Migration to &kde;4/&Qt;4 This was a massive effort, and many developers contributed. Apologies to any we forgot to name explicitly. diff --git a/doc/details-investments.docbook b/doc/details-investments.docbook index e10a55993..2fb1786ff 100644 --- a/doc/details-investments.docbook +++ b/doc/details-investments.docbook @@ -1,646 +1,693 @@ &Ace.Jones; &Ace.Jones.mail; + + MichaelCarpino + mfcarpino@gmail.com + - 2014-08-30 - 4.7.01 + 2019-04-04 + 5.0.3 Investments Investments in &kmymoney; Investments - Investments are instruments for investing money that are traded on a market. - Stocks, bonds, and mutual funds are the most common investments; so they are - the ones supported most directly. Futures, commodities, options, and more - complex derivatives are also sometimes used, but &kmymoney; has no special - functionality for them. As long as they behave like a stock or a bond, they - can be tracked easily. + In very general terms, an investment is any allocation of money with the + expectation of a future benefit. &kmymoney;'s Investment accounts are designed for + one particular type of investment: securities. A security investment (or just a + security) is an instrument for investing money that is traded on a market with the + intention of a profitable return in the form of either appreciation in value, or + income such as interest or dividends. Stocks and mutual funds are the most common + securities; and they are the ones specifically supported within &kmymoney;. Bonds + have a different financial structure, but can usually be handled by &kmymoney;. + Futures, commodities, options, and more complex investment instruments like + derivatives are other types of investments, but &kmymoney; has no special + functionality for them. As long an investment has similar characteristics to a + stock or mutual fund, it can be tracked. Base Currency - Each investment has a Base Currency. This is the currency in which it is - traded. When a price quote is entered for an investment, the currency of the - value given is always its base currency. A stock on the NYSE (New York Stock - Exchange) would be in US dollars, and one on an Australian market would be in - Australian dollars. + Each security has a Base Currency. This is the currency in which it is traded. + When a price quote is entered for an investment, the currency of the value given is + always its base currency. A stock on the NYSE (New York Stock Exchange) would be + in US dollars (USD), and one on an Australian market would be in Australian dollars + (AUD). The Base currency for a security does not need to be the same as the + default currency for the Investment account holding that security. Investment Accounts - Investment Accounts hold a collection of investments. An Investment account - contains transactions, such as buys and sells, of those investments. All - transactions in an Investment account must relate to a specific investment. - There is no separate cash balance in an investment account. For - that, you need a Brokerage Account. + An Investment Account holds a collection of Securities, also referred to as + Equities. An Investment account contains transactions, recording activities such + as buys, sells, dividends, reinvestments, yields, splits and income of those + investments. Each transaction in an Investment account must relate to a specific + security. + + + + It is common for an account at a Brokerage Institution to contain both securities + and cash. In &kmymoney;, an Investment account holds only + securities. It cannot contain any cash balance. This was a design decision made + when Investment accounts were first included in &kmymoney;, and it might be changed + in a future version. For now, any cash needed to purchase a security or gained + from a sell or dividend transaction requires the use of a Brokerage Account. - + Brokerage Accounts - An investment account often has an associated Brokerage Account. This is also - sometimes referred to as a Cash Account. Investment accounts - cannot contain cash transactions, like a transfer from your bank. When a - stock is sold, the proceeds are typically placed in the Brokerage Account. + When an investment transaction uses (to buy a security) or produces (from a sell or + dividend) cash, that transaction must refer to another account which can hold the + money, most commonly a checking account. You can specify any suitable account for + any investment transaction which requires one. However, &kmymoney; has the concept + of a Brokerage Account, also sometimes referred to as + a Cash Account, which is the default account for such transactions. When you create an Investment Account, you have the option of creating an - associated Brokerage Account with it. + associated Brokerage Account with it. The default name of the Brokerage account + will be the name of the investment account with " (Brokerage)" appended. - - Creating an Investment Account The first step on the path to working with investments is to create an account - to hold your investments. Choose Account - New account... to begin the process of - adding a new account. Create an account as usual, making sure to choose + to hold your defined securities. Choose Account + New account... to begin the process. + Create an account as previously defined in the adding + Account section, making sure to choose Investment as the type of account. To work with the new investment account, navigate to the Investments view, and choose the account you have just created from the Select Account dropdown box. - + Adding Investments to Your Account - To add individual Investments to your Investment Account, navigate to the + To add individual securities to your Investment Account, navigate to the Investments view, select the - Equities tab, and choose the account where the + Equities tab, and choose the Investment account where the investment is held from the Select Account drop-down box. Investment View, Equities Tab Investment View, Equities Tab Right-click the mouse in the empty space in the view. This brings up - the Investment Options context menu. Choose + the Investment Options context menu. Choose New investment... from this menu. This launches the New Investment Wizard which you use to create your new Investment. New Investment Wizard The first thing you'll be asked to enter is the type of investment, whether it's a stock, bond, &etc; Next, the investment details page is presented. The following information is entered on this page: - Trading Symbol. The ticker symbol used to identify the - investment on whatever market it trades. &kmymoney; requires a trading - symbol for all investments; however some investments do not have symbols. - In this case, you will need to make up a symbol for it. + Trading Symbol. A trading or ticker symbol is an abbreviation + used to identify a publicly traded security of a particular instrument on a + particular stock market exchange. &kmymoney; requires a trading symbol for all + securities; however some investments do not have symbols. In this case, you + will need to make up a symbol for it. - Full name. The friendly, readable name of the investment - you're creating, ⪚, Advanced Micro Devices, Inc. This name is - also referred to as the security. + Full name. The friendly, readable name of the investment you're + creating, ⪚, Advanced Micro Devices, Inc. This name is also + referred to as the security. - Fraction. The degree of precision to which your holdings are - measured. For example, in the US most mutual funds measure holdings to - three decimal places, so you would enter 1000 in this field. Stocks are - often measured to only whole units, so you could enter 1 for a stock like - this. + Fraction. This indicates the degree of precision to which your + holdings are measured. For example, in the US most mutual funds measure holdings + to three decimal places, so you would enter 1000 in this field. Stocks are often + measured to only whole units, so you could enter 1 for a stock like this. You'll + want to mirror the same decimal places that your brokerage uses to record your + securities so the transactions amounts match within &kmymoney;. Using extra + precision will not cause a problem, but using too little precision can cause + rounding errors which can make &kmymoney; unable to exactly match the information + shown by your brokerage institution. Trading market. Where the stock trades. This is an optional field which is provided for your convenience. This information is not used - anywhere else in &kmymoney;. + anywhere else within &kmymoney;. Identification. An optional field to enter additional identification information you might like to keep track of. Again, this information is not used anywhere else. - Trading currency. The underlying currency in which this - investment trades on its market. + Trading currency. The market exchange currency in which this + investment trades. This is typically the country that the security trades + within. It is usually, but not necessarily, the same as the default currency + for the Investment account holding that security. - Price entry. Choose whether the price will be entered as an - individual price, or as the total for all shares. + Price entry. Choose whether the price will be entered as the + price per share or as the total for all shares when entering a transaction. - If you are using Online Quotes, ensure that the symbol exactly matches the - symbol used by your quote source. Yahoo covers most of the world's markets, - and requires a suffix on the end of symbols outside the US. For example, - Rubicon Limited on the New Zealand market should be entered as + If you are using Online Quotes, ensure that the symbol exactly matches the symbol + used by your quote source. Yahoo covers most of the world's markets, and requires + a suffix on the end of symbols outside the US, to specify the country or market. + For example, Rubicon Limited on the New Zealand market should be entered as RBC.NZ. Finally, you're presented with the Online Update screen. This is where you tell &kmymoney; how you would like to update the prices of your investment. The following items are set here: Use Finance::Quote. This is an option for GnuCash users who are used to this style of quotes. Most users can leave this unchecked. Online Source. The online source you'd like to use for this particular - investment. The most common choice is Yahoo. Try that - first, and if the investment cannot be found using this source, then - experiment with the others. + investment. The most common choice is Yahoo. Try that first, + and if the investment cannot be found using this source, then experiment with + the others. Factor. A multiplier that should be applied to quotes retrieved for this investment. This is most commonly needed for UK stocks where the price quoted is in pence (1/100), and the stock is denominated in pounds. In this case, enter 0,01 for the Factor. Editing an Investment The Equities tab of the Investments view window lists your - current holdings in this account, along with their symbol, value, quantity, and price. - Right-click the mouse on any of the investments to bring up the + current holdings in this account, along with their symbol, value, quantity, and + price. Right-click the mouse on any of the investments to bring up the Investment Options context menu, where you have the option to add, edit, or delete individual investments from this account. Also, you can update the price of your investments here either manually or via their - online source. In addition, it is possible to close an empty account, or to - reopen a closed account. + online source. In addition, it is possible to close an empty account, or to + reopen a closed account. The order for value, quantity and price can be changed + on the screen by selecting any of them by left clicking on the item in the top bar and + dragging it to the left or right. Investment Transactions Investment Transaction Form Investment Transaction Form Investment transactions are entered and edited in the - ledger view, as with other kinds of - accounts. However, the fields are different, and vary depending on the - investment transaction type or activity. Investment transactions have some - additional elements: + Ledger view, as with other kinds of + accounts. However, the fields will appear different, and vary depending on the + transaction type or activity. Investment transactions have some additional + elements: Activity Security Account Shares, Price, & Total Amount Fees Interest category Activity The Activity for an investment transaction describes what action is happening - to the stock. The following activities are supported: + to the security. The following activities are supported: Buy/Sell - Use to record purchases or sales of individual investments. - This action requires an account to transfer the funds from/to. + Use to record purchases or sales of individual securities. This action + requires an account to transfer the funds from/to, which defaults to the + Brokerage account, if one has been created. Dividend/Yield - Also known as a Cash Dividend, this action - is used for when you receive an interest or dividend disbursement from - your investment. This action requires an account to transfer the funds - from/to. + Also known as a Cash Dividend, this action is used when you + receive an interest or dividend disbursement from your security. This action + also requires an account to transfer the funds to. Reinvest Dividend - Reinvest Dividend. This is a dividend where the proceeds are re-invested - back into the investment. + Reinvest Dividend. This is a dividend where the proceeds are used to purchase + additional shares of the security. Add/Remove Shares - A simple increase or decrease in your balance. This - should be used very rarely, because it's uncommon for shares to just show - up in your account (or disappear) unless it's a purchase or a sale. + A simple increase or decrease in the number of shares you own. This should be + used very rarely, because it's uncommon for shares to just show up in your + account (or disappear) unless it's a purchase or a sale. One use of these + activities is for some situations &kmymoney; does not natively handle, such as + the exchange of some number of shares of a security for a different number of + shares of a different class of the same security. Split Shares - Used when the stock is split. Enter the ratio of the split - in the Split Ratio field. For example, in a 3:2 split, - enter 1.5 + This is used when the stock is split. Enter the ratio of the split in + the Split Ratio field. For example, in a 3:2 split, enter 1.5. + Reverse splits, where the ration is less than one, such as 2:3, although + uncommon, are also allowed. Security - Each investment transaction must be associated with an individual security, - which is here just another name for an investment. Choose the investment name - when adding or editing a transaction. The symbol will be displayed when - viewing it. + Each investment transaction must be associated with an individual security. Choose + the security name when adding or editing a transaction. The symbol will be + displayed when viewing it. Account - For any transactions which generate or require money, you must enter the - account where the money is transferred to/from. If your investment account - has an associated brokerage account, it's usually best to transfer the funds - there. This applies to funds for purchase or sale of the investment, as well - as for fees paid or interest or dividends earned. + For any transaction which generates or requires money, you must enter the account + where the money is transferred to/from. If your investment account has an + associated Brokerage account, it is usually best to transfer the funds there. This + applies to funds for purchase or sale of the security, as well as for fees paid or + interest or dividends earned. Shares, Price & Total Amount - For buy, sell, and cash dividend transactions, the number of shares, the price - per share, and the total amount of the transaction must be established. You - can enter any two of these, and &kmymoney; will calculate the third. It's - usually best to enter just the total amount and the number of shares, because - these are the known facts of the transaction. The price per share can be - calculated from these. + For buy, sell, and reinvestment transactions, the number of shares, the price per + share, and the total amount of the transaction must be established. You can enter + any two of these, and &kmymoney; will calculate the third. It's usually best to + enter just the total amount and the number of shares, because these are the known + facts of the transaction. &kmymoney; will automatically calculate the price per + share. Note that there is only one entry field for the price, and it will be + labeled + Transaction amount or Price/share depending on the + setting of the Price entry option when the account + was set up. Fees With many investment transactions you can include the fees (or commission) you paid the broker. If you enter a category for the fee, then a field will be shown to the right where you can enter the amount of the fee. If you need to enter more than one fee for the transaction, you can use the Split Transactions feature. In this case, when you complete entering all the splits, the total amount of the fees will be shown to the right. Interest - This is how you enter an interest or dividend payment from an investment. As - with fees, if you enter a category, then a field will be shown to the right - where you can enter the amount. You can also use the split transaction + This is how you enter an interest or dividend payment from a security. As + with fees, if you enter a category, then a field will be shown to the right + where you can enter the amount. You can also use the split transaction feature, if required. - Working With Foreign Investments - &kmymoney; supports multiple currencies and investments, and you may want to - combine the two. However, doing so requires extra care. As noted above, when - you added an investment, you had to specify its trading currency. This might - not be the same as your base currency, and it also might not be the same as - the account in which you hold the stock or the account where you transfer your - funds to/from for buys/sells. + &kmymoney; supports multiple currencies and securities, and you may want to combine + the two. However, doing so requires extra care to assure accurate records. As + noted above, when you add a security, you must specify its trading currency. This + might not be the same as the base currency for your &kmymoney; file, and it also + might not be the same as the default currency for the investment account in which + you hold the stock or the account where you transfer your funds to/from for + buys/sells. - Consider a hypothetical case. Your base currency is USD. You have an - investment account in EUR, and a brokerage account also in EUR. In that + Consider a hypothetical case, where your base currency is USD. You have an + investment account in EUR, and a brokerage account also in EUR. In that investment account, you hold shares of TietoEnator, which is traded in SEK. - When you enter a buy transaction on this investment, use SEK as the currency. - So if you buy 100 shares at a price of SEK 248.00, for a total of SEK - 24,800.00, enter these values in the transaction. + When you enter a buy transaction for this investment, use SEK as the currency. So + if you buy 100 shares at a price of SEK 248.00, for a total of SEK 24,800.00, enter + these values in the transaction. Currency Warning Currency Warning When you choose the brokerage account to fund the transfer, you'll be warned that it's in a different currency. Exchange Rate Editor Exchange Rate Editor - When you finish the transaction, you will be prompted for a price update to - the investment account's currency, in this case, SEK -> EUR. Review the + When you finish entering the transaction, you will be prompted for a price update + to the investment account's currency, in this case, SEK -> EUR. Review the documentation on Entering Prices Manually for more information on the price dialog. - If you then switch over to the brokerage account, you will see the transaction - as EUR 2,254.54, assuming an exchange rate is 11.0000 SEK / EUR. + If you then switch over to the brokerage account, you will see the transaction as + EUR 2,254.54, assuming an exchange rate is 11.0000 SEK/EUR. Updating Prices + There are two ways of updating the prices for your investments. You can - either enter the new price manually or have &kmymoney; fetch it from the web. - + either enter the price manually or have &kmymoney; fetch it from the web. Manual Price Updates + You can enter prices for your investments using the same Price Editor as used for currencies. Online Price Quotes - &kmymoney; has the ability to download the latest prices for your investments + &kmymoney; has the ability to download the latest prices for your securities and currencies via the web. How Online Quotes Work At your request, &kmymoney; will fetch a page from the web that contains the - latest price for each item. By default, prices are fetched from - finance.yahoo.com, and are subject to the terms and conditions of that - site. + latest price for each security. By default, prices are fetched from + finance.yahoo.com, and are + subject to the terms and conditions of that site. - The online quote lookup uses the investment's trading symbol to find the - price. Therefore, it's important to set the symbol correctly. Yahoo supports - stocks from most major world markets, so it's usually just a matter of finding - the correct symbol. For example, TietoEnator trades on the Stockholm Stock - Exchange market, and its Yahoo symbol is TIEN.ST. + The online quote lookup uses the security's trading symbol to find the price. + Therefore, it is important to set the symbol correctly. Yahoo supports stocks from + most major world markets, so it's usually just a matter of finding the correct + symbol. For example, TietoEnator trades on the Stockholm Stock Exchange market, + and its Yahoo symbol is TIEN.ST. To find the trading symbol for a security supported by Yahoo, use the - Symbol Lookup feature at finance.yahoo.com. + Symbol Lookup feature + at finance.yahoo.com. Assigning a Quote Source In order to get online price quotes, you first have to enable it for each - investment or currency you want updated, by setting a Online Quote + security or currency you want updated, by setting a Online Quote Source. This is the name of the service from which the quote should be fetched. &kmymoney; ships with several sources to choose from. Yahoo is the recommended default source, and should work for most investments and all - currencies. + currencies. - To assign a quote source to an investment, navigate to the investment summary - view for the account in which the security is held. Edit the security by - right-clicking it and selecting Edit Investment - .... In the Investment Detail Wizard, - click Next twice, for the Online Update section. In - the Online source dropdown box, select the online source. + To assign a quote source for an investment, navigate to the investment summary view + for the account in which the security is held. Edit the security by right-clicking + it and selecting Edit Investment .... In the Investment + Detail Wizard, click Next twice, for the Online Update + section. In the Online source dropdown box, select the online source for your + needs. - Versions of &kmymoney; starting with 0.9 contain support for the - Finance::Quote package for obtaining online quotes. This is intended primarily - as a convenience for those users converting from the GnuCash finance package, - which uses it as its native method. If you do select this option, you should - see a different list of sources, those supported by Finance::Quote. If the - list is empty, it suggests that the package is not properly installed. See - their web site at + Versions of &kmymoney; starting with 0.9 contain support for the Finance::Quote + package for obtaining online quotes. This is intended primarily as a convenience + for those users converting from the GnuCash finance package, which uses it as its + native method. If you do select this option, you should see a different list of + sources, those supported by Finance::Quote. If the list is empty, it suggests that + the package is not properly installed. See their web site at http://finance-quote.sourceforge.net for more information. Adjusting a quote Some online sources do not report the price in a base quantity (⪚, EUR) but in a fraction (⪚, Cent). Using this information as price will produce wrong values for your investments. If this is the case for your online source, you can use the Factor field to enter an adjusting factor. For the above mentioned example the factor would be 0.01. The Factor field is only available if a Quote Source has been selected. Fetching Quotes - Typically, you will update the prices for all your investments and currencies - at once. Choose the ToolsUpdate - Stock and Currency Prices... menu option to bring - up the online price quotes dialog. Press Update All to - fetch quotes for all investments and currencies in your &kmymoney; file. + Typically, you will update the prices for all your securities and currencies as a + single operation. Choose the + ToolsUpdate Stock and Currency + Prices... menu option to bring up the online price + quotes dialog. Press Update All to fetch quotes for all + securities and currencies in your &kmymoney; file. Update Stock and Currency Prices Online Stock and Currency Price Update Adding or Editing Quote Sources Adding or editing quote sources is not recommended for anyone but the most technical user. You should feel comfortable reading HTML and writing complex - regular expressions. If this doesn't sound like you, we recommend writing to - the developer's list if none of the quote sources work for you. Ideally, - please point us to a web page where these quotes can be obtained. + regular expressions. If this doesn't sound like you, we recommend writing to the + developer's list if none of the quote sources work for you. Ideally, please point + us to a web page where these quotes can be obtained. Additionally you can consult + with members of the KDE Community Forum for &kmymoney; as they may already have a + solution available for your needs. - If you do feel up to the challenge, here's how it works. The quote sources - are contained in the settings dialog. - Choose SettingsConfigure - &kmymoney;. From there, choose - the Online Quotes section. You can choose an existing - source to edit, or create a new one. When you are done with your changes, be - sure to press the Update button before exiting the - dialog. Your changes are not saved by default. + If you do feel up to the challenge, here's how it works. The quote sources are + contained in the settings dialog. Choose + SettingsConfigure + &kmymoney;. From there, choose the Online + Quotes section. You can choose an existing source to edit, or create a + new one. When you are done with your changes, be sure to press + the Update button before exiting the dialog. Your changes + are not saved by default. - The first thing to worry about in an online quote source is the URL. This is - the page that is fetched from the web. You will see a %1 in all sources, and - a %2 in currency sources. For investments, %1 is replaced by the trading - symbol. For currencies, %1 is replaced by the From currency, and %2 is - replaced by the To currency. This URL is then fetched, all HTML tags are - removed, and that stripped file is then sent to the page parser. + The first thing to consider in an online quote source is the URL. This is the page + that is fetched from the web. You will see a %1 in all sources, and a %2 in + currency sources. For investments, %1 is replaced by the trading symbol. For + currencies, %1 is replaced by the From currency, and %2 is replaced by the To + currency. This URL is then fetched, all HTML tags are (optionally) removed, and + that stripped file is then sent to the page parser. - Note that the URL can also be a file: URL, which the quote fetcher takes to - mean an executable script. It will pass any command-line arguments to it that - you have specified, and feed the stdout to the page parser. For example, you - might have a script called getquote.sh that contains custom quote logic, - taking the symbol as a single parameter. Your URL would be + Note that the URL can also be a file: URL, which the quote fetcher takes to be the + path to an executable script. It will pass any command-line arguments to it that + you have specified, and feed the stdout to the page parser. For example, you might + have a script called getquote.sh that contains custom quote logic, taking the + symbol as a single parameter. Your URL would be file:/path/to/getquote.sh %1. - The page parser looks for a symbol, a date, and a price. Regular expressions - tell it how to extract those items from the page. Please review the - documentation for the QRegExp class - for the exact makeup of the - regular expressions. There should be exactly one capture expression, - surrounded by parentheses, in each regexp. The date format further tells the - date parser the order of year, month, and day. This date format should always - be in the form "%x %x %x". where x is y, m, or d. The date parser is very - smart. %m %d %y will parse December 31st, 2005 - as easily as 12/31/05. Two digit years are interpreted as - being in the range of 1950-2049. + The page parser looks for a symbol, a date, and a price. Regular expressions tell + it how to extract those items from the page. Please review the documentation for + the QRegExp + class for the exact syntax regular expressions used by &kmymoney;. There + should be exactly one capture expression, surrounded by parentheses, in each regexp + field. The date format further tells the date parser the order of year, month, and + day. This date format should always be in the form "%x %x %x". where x is y, m, or + d. The date parser is very smart. %m %d %y will + parse December 31st, 2005 as easily as 12/31/05. Two + digit years are interpreted as being in the range of 1950-2049. Unimplemented Features + - Certain common features that are normally found with investments are not yet - implemented in &kmymoney;. These include: Derivatives (options, futures, - &etc;), capital gains, and tax reporting for investments. + There are some features that are normally associated with investments which are not + yet implemented in &kmymoney;. These include, but are not limited to derivatives, + options, and futures. In addition, when you sell a security, &kmymoney; does not + know which specific shares you are selling, &ie;, the oldest or the most recently + purchased, so it cannot calculate return on investment. Finally, it has no direct + knowledge about any country's specific tax reporting requirements, but these can + usually be handled by marking as Tax related all the categories you use for + transactions which might have tax consequences. + diff --git a/doc/makemostof.docbook b/doc/makemostof.docbook index c572ab710..df459b19b 100644 --- a/doc/makemostof.docbook +++ b/doc/makemostof.docbook @@ -1,383 +1,383 @@ Joe(joe1011010) joe1011010_km@users.sourceforge.net - 2010-07-19 - 4.5 + 2010-04-05 + 5.0.4 Making the most of &kmymoney; While you could go ahead, clicking some buttons and filling in some data, after a time, you could decide you have done it wrong and start again, even if you did read the documentation on each part of &kmymoney;. You will get a more effective system if you spend a little time planning how you are going to use &kmymoney;, so follow the steps given here. Basic Accounting Imagine your money as balls, or beans, and to stop them rolling around you keep them in a box, or pot. Accounting, or Bookkeeping, is the process of counting and keeping track of the beans in the pot, or several pots. You have some money in the pot marked Cash. You buy some goods, so you take some beans out of the cash pot and place them into another pot marked Supplier. The supplier gives you some goods in exchange for the cash, so you take the beans out of the pot marked Supplier and put them in the pot marked Goods. The goods have a value (the price you paid) so you still have the same amount of beans, some representing cash and some representing goods. In this case you have two movements of beans, or transactions. Each transaction needs two entries, one to take beans out and one to put beans in. This is called double entry bookkeeping or double entry accounting. The recording of the transactions is done in a Ledger; each pot is known as an Account or Ledger code. Now you take some goods and give them to a customer, who gives you some cash in exchange. The goods were worth some beans and, hopefully, the customer has given us more beans than that, so making a profit. To over simplify, the beans from the Goods pot come back as Cash, but we can split that as the Cost of Goods sold and Profit. This transaction has three entries; one side of the double entry has been split. This will be covered in more detail later on in this handbook. Defining the accounts (personal records) Most accounts, or pots, above represent a measure of our Worth. The cash and goods represent our Assets; so does what we are owed if our customers have not yet given us the money. The money we owe, say if we had not paid our suppliers, are our Liabilities. These accounts are transferable to Cash and have a value. Any pots that cannot be valued are Income or Expense. Our phone bill (or the phone company to which we pay the bill) cannot be valued; we know how much we have paid, but not how much anyone else has paid. We know how much our employer has paid us, but we do not know how much they have paid anybody else, or how much money they have left to pay us next month. Although we cannot determine an actual value for these pots, it is useful to monitor how much we have put into or taken out of each of them. In some cases a supplier is a Liability, in others it is an Expense. This is something we need to consider and decide for each case. Similarly, you may set up a loan as a Liability, particularly if you transfer the money into your bank account, but it could be an Expense if it was to buy some furniture. Consider how you want to track and analyze your income and expenses. This will help you decide how to set them up in &kmymoney;. Finally, consider if you want everything in one set of accounts, or two or more. This may depend on the legal framework or just how you want to analyze things. Each set of accounts would be handled separately. Defining the accounts (business records) There are similarities to setting up accounts for personal use, but there are additional considerations, including legal guidelines and requirements. &kmymoney; does not explicitly address any of these issues, as it has been designed as a personal finance manager. In addition, these additional issues are not addressed in this handbook, but you must be aware of them if you are going to attempt to use &kmymoney; for a business. Mapping your finances to &kmymoney; So, collect up your papers and see the following sections for each item in &kmymoney;. Accounts These hold a value. Transactions are created against one or more accounts. Accounts - Asset These are the accounts which hold your money and possessions that you wish to monitor. The following types are available: Checking Standard bank checking account. Savings Standard bank savings account. Cash Money in your hand or wallet. Loan Loans you make to someone else. Investment Money you invest. Asset Property, collections, &etc;. Accounts - Liability These are the accounts which represent your debts and money owed that you wish to monitor. The following types are available: Credit card Standard credit card account. Loan Loans made to you, mortgages, &etc;. Liability Anything else you owe that is not a loan. Institutions These are completely optional and can be used to group accounts, and show a total value for all accounts in each group. Categories These represent non-managed income and expense accounts that do not have a value. The total value of transactions is shown against each category. A category or transfer account is required for each transaction. Sub-Categories Categories can be split into sub-categories, but this relationship is only for display purposes, as the sub-category totals are not included in the higher level category total. Tags Tags are very similar to Categories, as they are also non-managed accounts that do not have a value. However, unlike Categories, the use of Tags is completely optional. Payees These are optional for transactions. They are required for Scheduled transactions. A transaction history, with category, is shown against each payee. Scheduled transactions Where transactions occur on a regular basis, these can be set up against a Schedule. Transactions are created from a schedule; any that are overdue can be seen on the home page. Useful Tips Unless you keep your money under your mattress or under the floorboards, you probably make use of one or more institutions where you have accounts, which may be in credit or debit. In order to familiarize yourself with the way &kmymoney; works, choose an account you want to track with &kmymoney; and select File New in order to set up an account file. If you have used another personal finance manager, you may be able to import the data from it into &kmymoney;. If you have some regular receipts into or outgoings from this account, go to Payee and enter the names of both the payers and the payees involved; then go to Schedule and fill in a New Schedule for each recurring transaction. If you haven't entered the payee or payer, &kmymoney; will offer you the opportunity to do this in the middle of entering the schedule. To add other new transactions go to Ledgers; you can add new payees and categories in the middle of a transaction or by going to Payees or Categories before entering the transaction. You will probably find that the default Categories do not exactly match your needs; you can easily delete ones you know you are never going to need and add others that you need. But when you are entering a transaction, you only have to type a few letters of a category and &kmymoney; will offer you a drop down list of the matching categories from which to choose. You can add different accounts managed by different institutions; the preferred one will show when you open &kmymoney; but you can quickly switch to any of the others. When you make a payment, &kmymoney; will work out what the next check number should be; delete this if you are not making a check payment or edit it if the first check you enter is not check number 1. Alternatively, it is possible to switch off auto-increment of check numbers. Every so often you may get statements of your account from the institutions you use; you can reconcile your &kmymoney; accounts against these statements so that you have an accurate statement of the current state of your finances. If you go to Reports, you will find several default reports; to customize these, open one similar to the sort you prefer and then select 'New' (not 'Copy'); you can then customize this to your needs and mark it as a preferred report if you wish. Though &kmymoney; is not intended for use in a business context, if you are running a business on your own and so do not need payroll functions, you will probably find that &kmymoney; is sufficiently customizable to meet your needs particularly as it comes with budgeting and forecasting features and you can export your customized reports via CSV into other applications. diff --git a/doc/whatsnew.docbook b/doc/whatsnew.docbook index 2e4c5fd82..a4bd31de2 100644 --- a/doc/whatsnew.docbook +++ b/doc/whatsnew.docbook @@ -1,117 +1,123 @@ - 2018-02-01 - 5.0.0 + 2019-04-04 + 5.0.4 - What's new in this release + +Bug fixes and enhancements to functionality + - As with every release, the &kmymoney; development team has been working hard - to make this release better and easier to use in every way. We have also made - quite a few improvements. We are confident you will like what you see. + As with every release, the &kmymoney; development team has been working hard to + make this release better and easier to use in every way. This is the fourth + release in the 5.0 series, and most of the activity has been directed to fixing + bugs. - - The largest amount of work has gone towards basing this version on - &kde-frameworks;. Many of the underlying libraries used by the application have - been reorganized and improved, but most of that is behind the scenes, and not - directly visible to the end user. Some of the general look and feel may have - changed, but the basic functionality of the program remains the same, aside - from intentional improvements and additions. + At this point, the conversion to using &kde-frameworks; is essentially complete, + although that and the work to update many of the underlying libraries used by the + application are behind the scenes and not directly visible to the end user. The + general look and feel should be stable, although some minor changes may still + occur. The basic functionality of the program also remains the same, aside from + intentional improvements and additions. - In addition to adapting to new and updated libraries, there have also been - numerous bug fixes, as well as several new features and improved - functionality. Although this means there have been some major changes to the - underlying code, this version has actually been used in production by many of - the developers, so it has actually had a significant amount of testing. + In past releases, this page of the manual has included a list of many of the + specific bug fixes and functional enhancements. + However, rather than duplicating that information, you can find it directly in the + Release Notes. - Here are some of the new features found in this release: + Since the release of 5.0, there have been some changes which will need adjustments + on the user's side if you are upgrading from 4.8.x or an older version. - - - Multiple improvements to reports, including better performance. - - - - - - Allow logarithmic axes in report graphs. - - - - - - When deleting a security, automatically delete its prices. - - - - - - Allow separate beginning balance accounts for different currencies. - - - - - - Added support for several new currencies - - + + The program searches for the templates for the printcheck plugin + in different locations. The user needs to account for this in the plugins + setting dialog, see this section for + more details. + + + + +Updates to this Handbook - Here are some of the major bugs which have been fixed: + While we continue to make progress, there are still chapters of this handbook which + have not been updated to reflect all changes in the program which are visible to + the user as of 5.0. Each chapter does identify the release of &kmymoney; for which + it was last updated, but we present the complete list here, as a quick reference. - - - - Elimination of several crashes. - - - - - - Fix problems with report graphs using older data. - - - - - - Fix rounding errors in some investment transactions. - - - - - - Removed Yahoo from stock and currency price sources, as they no longer - provide this service - - - - - There are a some changes which will need some adjustments on the user's side - if you are upgrading from 4.8.x or an older version + However, please note that extent to which the chapters which have not been + explicitly updated do or do not reflect the current operation of the program + depends on how much that area of the program has changed between the 4.8 and the + 5.0 releases. + + + + + + Chapters updated for 5.x + Chapters not yet updated for 5.x + + + + + + + Introduction + What's new in this release + Making the most of &kmymoney; + Using &kmymoney; for the first time + Institutions + Accounts + Payees + Schedules + Ledgers + Investments + Settings + Reference: Menus + Credits + + + + + Categories + Tags + Currencies + Reconciliation + Reports + Importing and Exporting + Loans + Budgets + Forecast + Search Transactions + File Formats + Database + Questions and Answers + Reference: Widgets + Installation + + + + + + + - - - - The program searches for the templates for the printcheck plugin in - different locations. The user needs to account for this in the plugins - setting dialog, see this section - for more details. - - - + + + diff --git a/kmymoney/dialogs/keditscheduledlg.cpp b/kmymoney/dialogs/keditscheduledlg.cpp index 5b5ae8471..60274f90e 100644 --- a/kmymoney/dialogs/keditscheduledlg.cpp +++ b/kmymoney/dialogs/keditscheduledlg.cpp @@ -1,778 +1,781 @@ /* * Copyright 2007-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 "keditscheduledlg.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // KDE Includes #include #include #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_keditscheduledlg.h" #include "tabbar.h" #include "mymoneyexception.h" #include "mymoneyfile.h" #include "mymoneyaccount.h" #include "mymoneymoney.h" #include "mymoneyschedule.h" #include "mymoneysplit.h" #include "mymoneytransaction.h" #include "register.h" #include "transactionform.h" #include "transaction.h" #include "selectedtransactions.h" #include "transactioneditor.h" #include "kmymoneylineedit.h" #include "kmymoneydateinput.h" #include "kmymoneymvccombo.h" #include "kguiutils.h" #include "kmymoneyutils.h" #include "knewaccountdlg.h" #include "knewinvestmentwizard.h" #include "keditloanwizard.h" #include "kmymoneysettings.h" #include "mymoneyenums.h" #include "widgetenums.h" using namespace eMyMoney; class KEditScheduleDlgPrivate { Q_DISABLE_COPY(KEditScheduleDlgPrivate) Q_DECLARE_PUBLIC(KEditScheduleDlg) public: explicit KEditScheduleDlgPrivate(KEditScheduleDlg *qq) : q_ptr(qq), ui(new Ui::KEditScheduleDlg), m_item(nullptr), m_editor(nullptr), m_requiredFields(nullptr) { } ~KEditScheduleDlgPrivate() { delete ui; } void init() { Q_Q(KEditScheduleDlg); ui->setupUi(q); m_requiredFields = new KMandatoryFieldGroup(q); m_requiredFields->setOkButton(ui->buttonBox->button(QDialogButtonBox::Ok)); // button to be enabled when all fields present // make sure, we have a tabbar with the form // insert it after the horizontal line ui->m_paymentInformationLayout->insertWidget(2, ui->m_form->getTabBar(ui->m_form->parentWidget())); // we never need to see the register ui->m_register->hide(); // ... setup the form ... ui->m_form->setupForm(m_schedule.account()); // ... and the register ... ui->m_register->clear(); // ... now add the transaction to register and form ... auto t = transaction(); if (m_schedule.transaction().splits().isEmpty()) m_item = KMyMoneyRegister::Register::transactionFactory(ui->m_register, t, MyMoneySplit(), 0); else m_item = KMyMoneyRegister::Register::transactionFactory(ui->m_register, t, m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : m_schedule.transaction().splits().front(), 0); ui->m_register->selectItem(m_item); // show the account row m_item->setShowRowInForm(0, true); ui->m_form->slotSetTransaction(m_item); // setup widget contents ui->m_nameEdit->setText(m_schedule.name()); ui->m_frequencyEdit->setCurrentItem((int)m_schedule.occurrencePeriod()); if (ui->m_frequencyEdit->currentItem() == Schedule::Occurrence::Any) ui->m_frequencyEdit->setCurrentItem((int)Schedule::Occurrence::Monthly); q->slotFrequencyChanged((int)ui->m_frequencyEdit->currentItem()); ui->m_frequencyNoEdit->setValue(m_schedule.occurrenceMultiplier()); // load option widgets ui->m_paymentMethodEdit->insertItem(i18n("Direct deposit"), (int)Schedule::PaymentType::DirectDeposit); ui->m_paymentMethodEdit->insertItem(i18n("Manual deposit"), (int)Schedule::PaymentType::ManualDeposit); ui->m_paymentMethodEdit->insertItem(i18n("Direct debit"), (int)Schedule::PaymentType::DirectDebit); ui->m_paymentMethodEdit->insertItem(i18n("Standing order"), (int)Schedule::PaymentType::StandingOrder); ui->m_paymentMethodEdit->insertItem(i18n("Bank transfer"), (int)Schedule::PaymentType::BankTransfer); ui->m_paymentMethodEdit->insertItem(i18n("Write check"), (int)Schedule::PaymentType::WriteChecque); ui->m_paymentMethodEdit->insertItem(i18nc("Other payment method", "Other"), (int)Schedule::PaymentType::Other); auto method = m_schedule.paymentType(); if (method == Schedule::PaymentType::Any) method = Schedule::PaymentType::Other; ui->m_paymentMethodEdit->setCurrentItem((int)method); switch (m_schedule.weekendOption()) { case Schedule::WeekendOption::MoveNothing: ui->m_weekendOptionEdit->setCurrentIndex(0); break; case Schedule::WeekendOption::MoveBefore: ui->m_weekendOptionEdit->setCurrentIndex(1); break; case Schedule::WeekendOption::MoveAfter: ui->m_weekendOptionEdit->setCurrentIndex(2); break; } ui->m_estimateEdit->setChecked(!m_schedule.isFixed()); ui->m_lastDayInMonthEdit->setChecked(m_schedule.lastDayInMonth()); ui->m_autoEnterEdit->setChecked(m_schedule.autoEnter()); ui->m_endSeriesEdit->setChecked(m_schedule.willEnd()); ui->m_endOptionsFrame->setEnabled(m_schedule.willEnd()); if (m_schedule.willEnd()) { ui->m_RemainingEdit->setValue(m_schedule.transactionsRemaining()); ui->m_FinalPaymentEdit->setDate(m_schedule.endDate()); } q->connect(ui->m_RemainingEdit, static_cast(&QSpinBox::valueChanged), q, &KEditScheduleDlg::slotRemainingChanged); q->connect(ui->m_FinalPaymentEdit, &KMyMoneyDateInput::dateChanged, q, &KEditScheduleDlg::slotEndDateChanged); q->connect(ui->m_frequencyEdit, &KMyMoneyGeneralCombo::itemSelected, q, &KEditScheduleDlg::slotFrequencyChanged); q->connect(ui->m_frequencyNoEdit, static_cast(&QSpinBox::valueChanged), q, &KEditScheduleDlg::slotOccurrenceMultiplierChanged); q->connect(ui->buttonBox, &QDialogButtonBox::helpRequested, q, &KEditScheduleDlg::slotShowHelp); q->setModal(true); // force the initial height to be as small as possible QTimer::singleShot(0, q, SLOT(slotSetupSize())); // we just hide the variation field for now and enable the logic // once we have a respective member in the MyMoneySchedule object ui->m_variation->hide(); } /** * Helper method to recalculate and update Transactions Remaining * when other values are changed */ void updateTransactionsRemaining() { auto remain = m_schedule.transactionsRemaining(); if (remain != ui->m_RemainingEdit->value()) { ui->m_RemainingEdit->blockSignals(true); ui->m_RemainingEdit->setValue(remain); ui->m_RemainingEdit->blockSignals(false); } } MyMoneyTransaction transaction() const { auto t = m_schedule.transaction(); if (m_editor) { m_editor->createTransaction(t, m_schedule.transaction(), m_schedule.transaction().splits().isEmpty() ? MyMoneySplit() : m_schedule.transaction().splits().front(), false); } t.clearId(); t.setEntryDate(QDate()); return t; } KEditScheduleDlg *q_ptr; Ui::KEditScheduleDlg *ui; MyMoneySchedule m_schedule; KMyMoneyRegister::Transaction* m_item; QWidgetList m_tabOrderWidgets; TransactionEditor* m_editor; KMandatoryFieldGroup* m_requiredFields; }; KEditScheduleDlg::KEditScheduleDlg(const MyMoneySchedule& schedule, QWidget *parent) : QDialog(parent), d_ptr(new KEditScheduleDlgPrivate(this)) { Q_D(KEditScheduleDlg); d->m_schedule = schedule; d->m_editor = 0; d->init(); } KEditScheduleDlg::~KEditScheduleDlg() { Q_D(KEditScheduleDlg); delete d; } void KEditScheduleDlg::slotSetupSize() { resize(width(), minimumSizeHint().height()); } TransactionEditor* KEditScheduleDlg::startEdit() { Q_D(KEditScheduleDlg); KMyMoneyRegister::SelectedTransactions list(d->ui->m_register); TransactionEditor* editor = d->m_item->createEditor(d->ui->m_form, list, QDate()); // check that we use the same transaction commodity in all selected transactions // if not, we need to update this in the editor's list. The user can also bail out // of this operation which means that we have to stop editing here. if (editor && !d->m_schedule.account().id().isEmpty()) { if (!editor->fixTransactionCommodity(d->m_schedule.account())) { // if the user wants to quit, we need to destroy the editor // and bail out delete editor; editor = 0; } } if (editor) { editor->setScheduleInfo(d->ui->m_nameEdit->text()); connect(editor, &TransactionEditor::transactionDataSufficient, d->ui->buttonBox->button(QDialogButtonBox::Ok), &QWidget::setEnabled); connect(editor, &TransactionEditor::escapePressed, d->ui->buttonBox->button(QDialogButtonBox::Cancel), &QAbstractButton::animateClick); connect(editor, &TransactionEditor::returnPressed, d->ui->buttonBox->button(QDialogButtonBox::Ok), &QAbstractButton::animateClick); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); // connect(editor, SIGNAL(finishEdit(KMyMoneyRegister::SelectedTransactions)), this, SLOT(slotLeaveEditMode(KMyMoneyRegister::SelectedTransactions))); connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, editor, &TransactionEditor::slotReloadEditWidgets); // create the widgets, place them in the parent and load them with data // setup tab order d->m_tabOrderWidgets.clear(); eWidgets::eRegister::Action action = eWidgets::eRegister::Action::Withdrawal; switch (d->m_schedule.type()) { case Schedule::Type::Deposit: action = eWidgets::eRegister::Action::Deposit; break; case Schedule::Type::Bill: action = eWidgets::eRegister::Action::Withdrawal; editor->setPaymentMethod(d->m_schedule.paymentType()); break; case Schedule::Type::Transfer: action = eWidgets::eRegister::Action::Transfer; break; default: // if we end up here, we don't have a known schedule type (yet). in this case, we just glimpse // into the transaction and determine the type. in case we don't have a transaction with splits // we stick with the default action already set up if (d->m_schedule.transaction().splits().count() > 0) { auto isDeposit = false; auto isTransfer = false; auto splits = d->m_schedule.transaction().splits(); foreach (const auto split, splits) { if (split.accountId() == d->m_schedule.account().id()) { isDeposit = !(split.shares().isNegative()); } else { auto acc = MyMoneyFile::instance()->account(split.accountId()); if (acc.isAssetLiability() && d->m_schedule.transaction().splits().count() == 2) { isTransfer = true; } } } if (isTransfer) action = eWidgets::eRegister::Action::Transfer; else if (isDeposit) action = eWidgets::eRegister::Action::Deposit; } break; } editor->setup(d->m_tabOrderWidgets, d->m_schedule.account(), action); // if it's not a check, then we need to clear // a possibly assigned check number if (d->m_schedule.paymentType() != Schedule::PaymentType::WriteChecque) { QWidget* w = editor->haveWidget("number"); if (w) { if (auto numberWidget = dynamic_cast(w)) { numberWidget->loadText(QString()); } } } Q_ASSERT(!d->m_tabOrderWidgets.isEmpty()); d->m_tabOrderWidgets.push_front(d->ui->m_paymentMethodEdit); // editor->setup() leaves the tabbar as the last widget in the stack, but we // need it as first here. So we move it around. QWidget* w = editor->haveWidget("tabbar"); if (w) { int idx = d->m_tabOrderWidgets.indexOf(w); if (idx != -1) { d->m_tabOrderWidgets.removeAt(idx); d->m_tabOrderWidgets.push_front(w); } } // don't forget our three buttons and additional widgets // make sure to use the correct order d->m_tabOrderWidgets.push_front(d->ui->m_frequencyEdit); d->m_tabOrderWidgets.push_front(d->ui->m_frequencyNoEdit); d->m_tabOrderWidgets.push_front(d->ui->m_nameEdit); d->m_tabOrderWidgets.append(d->ui->m_weekendOptionEdit); d->m_tabOrderWidgets.append(d->ui->m_estimateEdit); d->m_tabOrderWidgets.append(d->ui->m_variation); d->m_tabOrderWidgets.append(d->ui->m_lastDayInMonthEdit); d->m_tabOrderWidgets.append(d->ui->m_autoEnterEdit); d->m_tabOrderWidgets.append(d->ui->m_endSeriesEdit); d->m_tabOrderWidgets.append(d->ui->m_RemainingEdit); d->m_tabOrderWidgets.append(d->ui->m_FinalPaymentEdit); d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Ok)); d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Cancel)); d->m_tabOrderWidgets.append(d->ui->buttonBox->button(QDialogButtonBox::Help)); for (auto i = 0; i < d->m_tabOrderWidgets.size(); ++i) { w = d->m_tabOrderWidgets.at(i); if (w) { w->installEventFilter(this); w->installEventFilter(editor); } } // connect the postdate modification signal to our update routine if (auto dateEdit = dynamic_cast(editor->haveWidget("postdate"))) connect(dateEdit, &KMyMoneyDateInput::dateChanged, this, &KEditScheduleDlg::slotPostDateChanged); d->ui->m_nameEdit->setFocus(); // add the required fields to the mandatory group d->m_requiredFields->add(d->ui->m_nameEdit); d->m_requiredFields->add(editor->haveWidget("account")); d->m_requiredFields->add(editor->haveWidget("category")); // fix labels if (auto label = dynamic_cast(editor->haveWidget("date-label"))) label->setText(i18n("Next due date")); d->m_editor = editor; slotSetPaymentMethod((int)d->m_schedule.paymentType()); connect(d->ui->m_paymentMethodEdit, &KMyMoneyGeneralCombo::itemSelected, this, &KEditScheduleDlg::slotSetPaymentMethod); connect(editor, &TransactionEditor::operationTypeChanged, this, &KEditScheduleDlg::slotFilterPaymentType); } return editor; } void KEditScheduleDlg::accept() { Q_D(KEditScheduleDlg); // Force the focus to be on the OK button. This will trigger creation // of any unknown objects (payees, categories etc.) d->ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); // only accept if the button is really still enabled. We could end // up here, if the user filled all fields, the focus is on the category // field, but the category is not yet existent. When the user presses the // OK button in this context, he will be asked if he wants to create // the category or not. In case he decides no, we end up here with no // category filled in, so we don't run through the final acceptance. if (d->ui->buttonBox->button(QDialogButtonBox::Ok)->isEnabled()) QDialog::accept(); } const MyMoneySchedule& KEditScheduleDlg::schedule() { Q_D(KEditScheduleDlg); if (d->m_editor) { auto t = d->transaction(); if (d->m_schedule.nextDueDate() != t.postDate()) { d->m_schedule.setNextDueDate(t.postDate()); d->m_schedule.setStartDate(t.postDate()); } d->m_schedule.setTransaction(t); d->m_schedule.setName(d->ui->m_nameEdit->text()); d->m_schedule.setFixed(!d->ui->m_estimateEdit->isChecked()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); switch (d->ui->m_weekendOptionEdit->currentIndex()) { case 0: d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveNothing); break; case 1: d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveBefore); break; case 2: d->m_schedule.setWeekendOption(Schedule::WeekendOption::MoveAfter); break; } d->m_schedule.setType(Schedule::Type::Bill); if (auto tabbar = dynamic_cast(d->m_editor->haveWidget("tabbar"))) { switch (static_cast(tabbar->currentIndex())) { case eWidgets::eRegister::Action::Deposit: d->m_schedule.setType(Schedule::Type::Deposit); break; default: case eWidgets::eRegister::Action::Withdrawal: d->m_schedule.setType(Schedule::Type::Bill); break; case eWidgets::eRegister::Action::Transfer: d->m_schedule.setType(Schedule::Type::Transfer); break; } } else { qDebug("No tabbar found in KEditScheduleDlg::schedule(). Defaulting type to BILL"); } if(d->ui->m_lastDayInMonthEdit->isEnabled()) d->m_schedule.setLastDayInMonth(d->ui->m_lastDayInMonthEdit->isChecked()); else d->m_schedule.setLastDayInMonth(false); d->m_schedule.setAutoEnter(d->ui->m_autoEnterEdit->isChecked()); d->m_schedule.setPaymentType(static_cast(d->ui->m_paymentMethodEdit->currentItem())); if (d->ui->m_endSeriesEdit->isEnabled() && d->ui->m_endSeriesEdit->isChecked()) { d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); } else { d->m_schedule.setEndDate(QDate()); } } return d->m_schedule; } void KEditScheduleDlg::newSchedule(const MyMoneyTransaction& _t, eMyMoney::Schedule::Occurrence occurrence) { MyMoneySchedule schedule; schedule.setOccurrence(occurrence); // if the schedule is based on an existing transaction, // we take the post date and project it to the next // schedule in a month. if (_t != MyMoneyTransaction()) { MyMoneyTransaction t(_t); schedule.setTransaction(t); if (occurrence != eMyMoney::Schedule::Occurrence::Once) schedule.setNextDueDate(schedule.nextPayment(t.postDate())); } - QPointer dlg = new KEditScheduleDlg(schedule, nullptr); - QPointer transactionEditor = dlg->startEdit(); - if (transactionEditor) { + bool committed; + do { + committed = true; + QPointer dlg = new KEditScheduleDlg(schedule, nullptr); + QPointer transactionEditor = dlg->startEdit(); KMyMoneyMVCCombo::setSubstringSearchForChildren(dlg, !KMyMoneySettings::stringMatchFromStart()); if (dlg->exec() == QDialog::Accepted && dlg != 0) { MyMoneyFileTransaction ft; try { schedule = dlg->schedule(); MyMoneyFile::instance()->addSchedule(schedule); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::error(nullptr, i18n("Unable to add scheduled transaction: %1", QString::fromLatin1(e.what())), i18n("Add scheduled transaction")); + committed = false; } } - } - delete transactionEditor; - delete dlg; + delete transactionEditor; + delete dlg; + } while(!committed); } void KEditScheduleDlg::editSchedule(const MyMoneySchedule& inputSchedule) { try { auto schedule = MyMoneyFile::instance()->schedule(inputSchedule.id()); KEditScheduleDlg* sched_dlg = nullptr; KEditLoanWizard* loan_wiz = nullptr; switch (schedule.type()) { case eMyMoney::Schedule::Type::Bill: case eMyMoney::Schedule::Type::Deposit: case eMyMoney::Schedule::Type::Transfer: { sched_dlg = new KEditScheduleDlg(schedule, nullptr); QPointer transactionEditor = sched_dlg->startEdit(); if (transactionEditor) { KMyMoneyMVCCombo::setSubstringSearchForChildren(sched_dlg, !KMyMoneySettings::stringMatchFromStart()); if (sched_dlg->exec() == QDialog::Accepted) { MyMoneyFileTransaction ft; try { MyMoneySchedule sched = sched_dlg->schedule(); // Check whether the new Schedule Date // is at or before the lastPaymentDate // If it is, ask the user whether to clear the // lastPaymentDate const auto& next = sched.nextDueDate(); const auto& last = sched.lastPayment(); if (next.isValid() && last.isValid() && next <= last) { // Entered a date effectively no later // than previous payment. Date would be // updated automatically so we probably // want to clear it. Let's ask the user. if (KMessageBox::questionYesNo(nullptr, i18n("You have entered a scheduled transaction date of %1. Because the scheduled transaction was last paid on %2, KMyMoney will automatically adjust the scheduled transaction date to the next date unless the last payment date is reset. Do you want to reset the last payment date?", QLocale().toString(next, QLocale::ShortFormat), QLocale().toString(last, QLocale::ShortFormat)), i18n("Reset Last Payment Date"), KStandardGuiItem::yes(), KStandardGuiItem::no()) == KMessageBox::Yes) { sched.setLastPayment(QDate()); } } MyMoneyFile::instance()->modifySchedule(sched); // delete the editor before we emit the dataChanged() signal from the // engine. Calling this twice in a row does not hurt. delete transactionEditor; ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(nullptr, i18n("Unable to modify scheduled transaction '%1'", inputSchedule.name()), QString::fromLatin1(e.what())); } } delete transactionEditor; } delete sched_dlg; break; } case eMyMoney::Schedule::Type::LoanPayment: { loan_wiz = new KEditLoanWizard(schedule.account(2)); if (loan_wiz->exec() == QDialog::Accepted) { MyMoneyFileTransaction ft; try { MyMoneyFile::instance()->modifySchedule(loan_wiz->schedule()); MyMoneyFile::instance()->modifyAccount(loan_wiz->account()); ft.commit(); } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(nullptr, i18n("Unable to modify scheduled transaction '%1'", inputSchedule.name()), QString::fromLatin1(e.what())); } } delete loan_wiz; break; } case eMyMoney::Schedule::Type::Any: break; } } catch (const MyMoneyException &e) { KMessageBox::detailedSorry(nullptr, i18n("Unable to modify scheduled transaction '%1'", inputSchedule.name()), QString::fromLatin1(e.what())); } } bool KEditScheduleDlg::focusNextPrevChild(bool next) { Q_D(KEditScheduleDlg); auto rc = false; auto w = qApp->focusWidget(); auto currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); while (w && currentWidgetIndex == -1) { // qDebug("'%s' not in list, use parent", qPrintable(w->objectName())); w = w->parentWidget(); currentWidgetIndex = d->m_tabOrderWidgets.indexOf(w); } if (currentWidgetIndex != -1) { do { // if(w) qDebug("tab order is at '%s (%d/%d)'", qPrintable(w->objectName()), currentWidgetIndex, d->m_tabOrderWidgets.size()); currentWidgetIndex += next ? 1 : -1; if (currentWidgetIndex < 0) currentWidgetIndex = d->m_tabOrderWidgets.size() - 1; else if (currentWidgetIndex >= d->m_tabOrderWidgets.size()) currentWidgetIndex = 0; w = d->m_tabOrderWidgets[currentWidgetIndex]; // qDebug("currentWidgetIndex = %d, w = %p", currentWidgetIndex, w); if (((w->focusPolicy() & Qt::TabFocus) == Qt::TabFocus) && w->isVisible() && w->isEnabled()) { // qDebug("Selecting '%s' as focus", qPrintable(w->objectName())); w->setFocus(); rc = true; } } while (rc == false); } return rc; } void KEditScheduleDlg::resizeEvent(QResizeEvent* ev) { Q_D(KEditScheduleDlg); d->ui->m_register->resize((int)eWidgets::eTransaction::Column::Detail); d->ui->m_form->resize((int)eWidgets::eTransactionForm::Column::Value1); QDialog::resizeEvent(ev); } void KEditScheduleDlg::slotRemainingChanged(int value) { Q_D(KEditScheduleDlg); // Make sure the required fields are set if (auto dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate"))) d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); if (d->m_schedule.transactionsRemaining() != value) { d->ui->m_FinalPaymentEdit->blockSignals(true); d->ui->m_FinalPaymentEdit->setDate(d->m_schedule.dateAfter(value)); d->ui->m_FinalPaymentEdit->blockSignals(false); } } void KEditScheduleDlg::slotEndDateChanged(const QDate& date) { Q_D(KEditScheduleDlg); // Make sure the required fields are set if (auto dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate"))) d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); if (d->m_schedule.endDate() != date) { d->m_schedule.setEndDate(date); d->updateTransactionsRemaining(); } } void KEditScheduleDlg::slotPostDateChanged(const QDate& date) { Q_D(KEditScheduleDlg); if (d->m_schedule.nextDueDate() != date) { if (d->ui->m_endOptionsFrame->isEnabled()) { d->m_schedule.setNextDueDate(date); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); d->updateTransactionsRemaining(); } } } void KEditScheduleDlg::slotSetPaymentMethod(int item) { Q_D(KEditScheduleDlg); const bool isWriteCheck = item == (int)Schedule::PaymentType::WriteChecque; if (auto numberEdit = dynamic_cast(d->m_editor->haveWidget("number"))) { numberEdit->setVisible(isWriteCheck); // hiding the label does not work, because the label underneath will shine // through. So we either write the label or a blank if (auto label = dynamic_cast(d->m_editor->haveWidget("number-label"))) label->setText(isWriteCheck ? i18n("Number") : QStringLiteral(" ")); } } void KEditScheduleDlg::slotFrequencyChanged(int item) { Q_D(KEditScheduleDlg); d->ui->m_endSeriesEdit->setEnabled(item != (int)Schedule::Occurrence::Once); bool isEndSeries = d->ui->m_endSeriesEdit->isChecked(); if (isEndSeries) d->ui->m_endOptionsFrame->setEnabled(item != (int)Schedule::Occurrence::Once); switch (item) { case (int)Schedule::Occurrence::Daily: case (int)Schedule::Occurrence::Weekly: d->ui->m_frequencyNoEdit->setEnabled(true); d->ui->m_lastDayInMonthEdit->setEnabled(false); break; case (int)Schedule::Occurrence::EveryHalfMonth: case (int)Schedule::Occurrence::Monthly: case (int)Schedule::Occurrence::Yearly: // Supports Frequency Number d->ui->m_frequencyNoEdit->setEnabled(true); d->ui->m_lastDayInMonthEdit->setEnabled(true); break; default: // Multiplier is always 1 d->ui->m_frequencyNoEdit->setEnabled(false); d->ui->m_frequencyNoEdit->setValue(1); d->ui->m_lastDayInMonthEdit->setEnabled(true); break; } if (isEndSeries && (item != (int)Schedule::Occurrence::Once)) { // Changing the frequency changes the number // of remaining transactions if (auto dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate"))) d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrenceMultiplier(d->ui->m_frequencyNoEdit->value()); d->m_schedule.setOccurrencePeriod(static_cast(item)); d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); d->updateTransactionsRemaining(); } } void KEditScheduleDlg::slotOccurrenceMultiplierChanged(int multiplier) { Q_D(KEditScheduleDlg); // Make sure the required fields are set auto oldOccurrenceMultiplier = d->m_schedule.occurrenceMultiplier(); if (multiplier != oldOccurrenceMultiplier) { if (d->ui->m_endOptionsFrame->isEnabled()) { if (auto dateEdit = dynamic_cast(d->m_editor->haveWidget("postdate"))) d->m_schedule.setNextDueDate(dateEdit->date()); d->m_schedule.setOccurrenceMultiplier(multiplier); d->m_schedule.setOccurrencePeriod(static_cast(d->ui->m_frequencyEdit->currentItem())); d->m_schedule.setEndDate(d->ui->m_FinalPaymentEdit->date()); d->updateTransactionsRemaining(); } } } void KEditScheduleDlg::slotShowHelp() { KHelpClient::invokeHelp("details.schedules.intro"); } void KEditScheduleDlg::slotFilterPaymentType(int index) { Q_D(KEditScheduleDlg); //save selected item to reload if possible auto selectedId = d->ui->m_paymentMethodEdit->itemData(d->ui->m_paymentMethodEdit->currentIndex(), Qt::UserRole).toInt(); //clear and reload the widget with the correct items d->ui->m_paymentMethodEdit->clear(); // load option widgets eWidgets::eRegister::Action action = static_cast(index); if (action != eWidgets::eRegister::Action::Withdrawal) { d->ui->m_paymentMethodEdit->insertItem(i18n("Direct deposit"), (int)Schedule::PaymentType::DirectDeposit); d->ui->m_paymentMethodEdit->insertItem(i18n("Manual deposit"), (int)Schedule::PaymentType::ManualDeposit); } if (action != eWidgets::eRegister::Action::Deposit) { d->ui->m_paymentMethodEdit->insertItem(i18n("Direct debit"), (int)Schedule::PaymentType::DirectDebit); d->ui->m_paymentMethodEdit->insertItem(i18n("Write check"), (int)Schedule::PaymentType::WriteChecque); } d->ui->m_paymentMethodEdit->insertItem(i18n("Standing order"), (int)Schedule::PaymentType::StandingOrder); d->ui->m_paymentMethodEdit->insertItem(i18n("Bank transfer"), (int)Schedule::PaymentType::BankTransfer); d->ui->m_paymentMethodEdit->insertItem(i18nc("Other payment method", "Other"), (int)Schedule::PaymentType::Other); auto newIndex = d->ui->m_paymentMethodEdit->findData(QVariant(selectedId), Qt::UserRole, Qt::MatchExactly); if (newIndex > -1) { d->ui->m_paymentMethodEdit->setCurrentIndex(newIndex); } else { d->ui->m_paymentMethodEdit->setCurrentIndex(0); } } diff --git a/kmymoney/plugins/views/reports/kreportsview_p.h b/kmymoney/plugins/views/reports/kreportsview_p.h index 5ca6f47b9..cd2c609ed 100644 --- a/kmymoney/plugins/views/reports/kreportsview_p.h +++ b/kmymoney/plugins/views/reports/kreportsview_p.h @@ -1,1457 +1,1473 @@ /*************************************************************************** kreportsview_p.h - description ------------------- begin : Sat Mar 27 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 Łukasz Wojniłowicz 2018 Michael Kiefer ***************************************************************************/ /*************************************************************************** * * * 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. * * * ***************************************************************************/ #ifndef KREPORTSVIEW_P_H #define KREPORTSVIEW_P_H #include "kreportsview.h" // ---------------------------------------------------------------------------- // QT Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_WEBENGINE #include #else #include #endif // ---------------------------------------------------------------------------- // KDE Includes #include #include #include // ---------------------------------------------------------------------------- // Project Includes #include "ui_reportcontrol.h" #include "kmymoneyviewbase_p.h" #include "kreportconfigurationfilterdlg.h" #include "mymoneyfile.h" #include "mymoneyreport.h" #include "mymoneyexception.h" #include "kmymoneysettings.h" #include "querytable.h" #include "objectinfotable.h" #include "icons/icons.h" #include #include "tocitem.h" #include "tocitemgroup.h" #include "tocitemreport.h" #include "kreportchartview.h" #include "pivottable.h" #include "reporttable.h" #include "reportcontrolimpl.h" #include "mymoneyenums.h" #include "kmm_printer.h" using namespace reports; using namespace eMyMoney; using namespace Icons; #define VIEW_LEDGER "ledger" #define VIEW_SCHEDULE "schedule" #define VIEW_WELCOME "welcome" #define VIEW_HOME "home" #define VIEW_REPORTS "reports" /** * Helper class for KReportView. * * This is the widget which displays a single report in the TabWidget that comprises this view. * * @author Ace Jones */ class KReportTab: public QWidget { private: #ifdef ENABLE_WEBENGINE QWebEngineView *m_tableView; #else KWebView *m_tableView; #endif reports::KReportChartView *m_chartView; ReportControl *m_control; QVBoxLayout *m_layout; MyMoneyReport m_report; bool m_deleteMe; bool m_chartEnabled; bool m_showingChart; bool m_needReload; bool m_isChartViewValid; bool m_isTableViewValid; QPointer m_table; /** * Users character set encoding. */ QByteArray m_encoding; public: KReportTab(QTabWidget* parent, const MyMoneyReport& report, const KReportsView *eventHandler); ~KReportTab(); const MyMoneyReport& report() const { return m_report; } void print(); void toggleChart(); /** * Updates information about plotted chart in report's data */ void updateDataRange(); void copyToClipboard(); void saveAs(const QString& filename, bool includeCSS = false); void updateReport(); QString createTable(const QString& links = QString()); const ReportControl* control() const { return m_control; } bool isReadyToDelete() const { return m_deleteMe; } void setReadyToDelete(bool f) { m_deleteMe = f; } void modifyReport(const MyMoneyReport& report) { m_report = report; } void showEvent(QShowEvent * event) final override; void loadTab(); }; /** * Helper class for KReportView. * * This is a named list of reports, which will be one section * in the list of default reports * * @author Ace Jones */ class ReportGroup: public QList { private: QString m_name; ///< the title of the group in non-translated form QString m_title; ///< the title of the group in i18n-ed form public: ReportGroup() {} ReportGroup(const QString& name, const QString& title): m_name(name), m_title(title) {} const QString& name() const { return m_name; } const QString& title() const { return m_title; } }; /** * KReportTab Implementation */ KReportTab::KReportTab(QTabWidget* parent, const MyMoneyReport& report, const KReportsView* eventHandler): QWidget(parent), #ifdef ENABLE_WEBENGINE m_tableView(new QWebEngineView(this)), #else m_tableView(new KWebView(this)), #endif m_chartView(new KReportChartView(this)), m_control(new ReportControl(this)), m_layout(new QVBoxLayout(this)), m_report(report), m_deleteMe(false), m_chartEnabled(false), m_showingChart(report.isChartByDefault()), m_needReload(true), m_isChartViewValid(false), m_isTableViewValid(false), m_table(0) { m_layout->setSpacing(6); m_tableView->setPage(new MyQWebEnginePage(m_tableView)); m_tableView->setZoomFactor(KMyMoneySettings::zoomFactor()); //set button icons m_control->ui->buttonChart->setIcon(Icons::get(Icon::OfficeChartLine)); m_control->ui->buttonClose->setIcon(Icons::get(Icon::DocumentClose)); m_control->ui->buttonConfigure->setIcon(Icons::get(Icon::Configure)); m_control->ui->buttonCopy->setIcon(Icons::get(Icon::EditCopy)); m_control->ui->buttonDelete->setIcon(Icons::get(Icon::EditDelete)); m_control->ui->buttonExport->setIcon(Icons::get(Icon::DocumentExport)); m_control->ui->buttonNew->setIcon(Icons::get(Icon::DocumentNew)); m_chartView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); m_chartView->hide(); m_tableView->hide(); m_layout->addWidget(m_control); m_layout->addWidget(m_tableView); m_layout->addWidget(m_chartView); m_layout->setStretch(1, 10); m_layout->setStretch(2, 10); connect(m_control->ui->buttonChart, &QAbstractButton::clicked, eventHandler, &KReportsView::slotToggleChart); connect(m_control->ui->buttonConfigure, &QAbstractButton::clicked, eventHandler, &KReportsView::slotConfigure); connect(m_control->ui->buttonNew, &QAbstractButton::clicked, eventHandler, &KReportsView::slotDuplicate); connect(m_control->ui->buttonCopy, &QAbstractButton::clicked, eventHandler, &KReportsView::slotCopyView); connect(m_control->ui->buttonExport, &QAbstractButton::clicked, eventHandler, &KReportsView::slotSaveView); connect(m_control->ui->buttonDelete, &QAbstractButton::clicked, eventHandler, &KReportsView::slotDelete); connect(m_control->ui->buttonClose, &QAbstractButton::clicked, eventHandler, &KReportsView::slotCloseCurrent); #ifdef ENABLE_WEBENGINE connect(m_tableView->page(), &QWebEnginePage::urlChanged, eventHandler, &KReportsView::slotOpenUrl); #else m_tableView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); connect(m_tableView->page(), &KWebPage::linkClicked, eventHandler, &KReportsView::slotOpenUrl); #endif // if this is a default report, then you can't delete it! if (report.id().isEmpty()) m_control->ui->buttonDelete->setEnabled(false); int tabNr = parent->addTab(this, Icons::get(Icon::Spreadsheet), report.name()); parent->setTabEnabled(tabNr, true); parent->setCurrentIndex(tabNr); // get users character set encoding m_encoding = QTextCodec::codecForLocale()->name(); } KReportTab::~KReportTab() { delete m_table; } void KReportTab::print() { if (m_tableView) { auto printer = KMyMoneyPrinter::startPrint(); if (printer != nullptr) { + if (m_showingChart) { + QPainter painter(printer); + m_chartView->paint(&painter, painter.window()); + QFont font = painter.font(); + font.setPointSizeF(font.pointSizeF() * 0.8); + painter.setFont(font); + QLocale locale; + painter.drawText(0, 0, QDate::currentDate().toString(locale.dateFormat(QLocale::ShortFormat))); + + /// @todo extract url from KMyMoneyApp + QUrl file; + if (file.isValid()) { + painter.drawText(0, painter.window().height(), file.toLocalFile()); + } + } else { #ifdef ENABLE_WEBENGINE - m_tableView->page()->print(printer, [=] (bool) {}); + m_tableView->page()->print(printer, [=] (bool) {}); #else - m_tableView->print(printer); + m_tableView->print(printer); #endif + } } } } void KReportTab::copyToClipboard() { QMimeData* pMimeData = new QMimeData(); pMimeData->setHtml(m_table->renderReport(QLatin1String("html"), m_encoding, m_report.name(), true)); QApplication::clipboard()->setMimeData(pMimeData); } void KReportTab::saveAs(const QString& filename, bool includeCSS) { QFile file(filename); if (file.open(QIODevice::WriteOnly)) { if (QFileInfo(filename).suffix().toLower() == QLatin1String("csv")) { QTextStream(&file) << m_table->renderReport(QLatin1String("csv"), m_encoding, QString()); } else { QString table = m_table->renderReport(QLatin1String("html"), m_encoding, m_report.name(), includeCSS); QTextStream stream(&file); stream << table; } file.close(); } } void KReportTab::loadTab() { m_needReload = true; if (isVisible()) { m_needReload = false; updateReport(); } } void KReportTab::showEvent(QShowEvent * event) { if (m_needReload) { m_needReload = false; updateReport(); } QWidget::showEvent(event); } void KReportTab::updateReport() { m_isChartViewValid = false; m_isTableViewValid = false; // reload the report from the engine. It might have // been changed by the user try { // Don't try to reload default reports from the engine if (!m_report.id().isEmpty()) m_report = MyMoneyFile::instance()->report(m_report.id()); } catch (const MyMoneyException &) { } delete m_table; m_table = 0; if (m_report.reportType() == eMyMoney::Report::ReportType::PivotTable) { m_table = new PivotTable(m_report); m_chartEnabled = true; } else if (m_report.reportType() == eMyMoney::Report::ReportType::QueryTable) { m_table = new QueryTable(m_report); m_chartEnabled = false; } else if (m_report.reportType() == eMyMoney::Report::ReportType::InfoTable) { m_table = new ObjectInfoTable(m_report); m_chartEnabled = false; } m_control->ui->buttonChart->setEnabled(m_chartEnabled); m_showingChart = !m_showingChart; toggleChart(); } void KReportTab::toggleChart() { // for now it will just SHOW the chart. In the future it actually has to toggle it. if (m_showingChart) { if (!m_isTableViewValid) { m_tableView->setHtml(m_table->renderReport(QLatin1String("html"), m_encoding, m_report.name()), QUrl("file://")); // workaround for access permission to css file } m_isTableViewValid = true; m_tableView->show(); m_chartView->hide(); m_control->ui->buttonChart->setText(i18n("Chart")); m_control->ui->buttonChart->setToolTip(i18n("Show the chart version of this report")); m_control->ui->buttonChart->setIcon(Icons::get(Icon::OfficeChartLine)); } else { if (!m_isChartViewValid) m_table->drawChart(*m_chartView); m_isChartViewValid = true; m_tableView->hide(); m_chartView->show(); m_control->ui->buttonChart->setText(i18n("Report")); m_control->ui->buttonChart->setToolTip(i18n("Show the report version of this chart")); m_control->ui->buttonChart->setIcon(Icons::get(Icon::ViewFinancialList)); } m_showingChart = ! m_showingChart; } void KReportTab::updateDataRange() { QList grids = m_chartView->coordinatePlane()->gridDimensionsList(); // get dimensions of plotted graph if (grids.isEmpty()) return; QChar separator = locale().groupSeparator(); QChar decimalPoint = locale().decimalPoint(); int precision = m_report.yLabelsPrecision(); QList> dims; // create list of dimension values in string and qreal // get qreal values dims.append(qMakePair(QString(), grids.at(1).start)); dims.append(qMakePair(QString(), grids.at(1).end)); dims.append(qMakePair(QString(), grids.at(1).stepWidth)); dims.append(qMakePair(QString(), grids.at(1).subStepWidth)); // convert qreal values to string variables for (int i = 0; i < 4; ++i) { if (i > 2) ++precision; if (precision == 0) dims[i].first = locale().toString(qRound(dims.at(i).second)); else dims[i].first = locale().toString(dims.at(i).second, 'f', precision).remove(separator).remove(QRegularExpression("0+$")).remove(QRegularExpression("\\" + decimalPoint + "$")); } // save string variables in report's data m_report.setDataRangeStart(dims.at(0).first); m_report.setDataRangeEnd(dims.at(1).first); m_report.setDataMajorTick(dims.at(2).first); m_report.setDataMinorTick(dims.at(3).first); } class KReportsViewPrivate : public KMyMoneyViewBasePrivate { Q_DECLARE_PUBLIC(KReportsView) public: explicit KReportsViewPrivate(KReportsView *qq): q_ptr(qq), m_needLoad(true), m_reportListView(nullptr), m_reportTabWidget(nullptr), m_listTab(nullptr), m_listTabLayout(nullptr), m_tocTreeWidget(nullptr), m_columnsAlreadyAdjusted(false) { } ~KReportsViewPrivate() { } void init() { Q_Q(KReportsView); m_needLoad = false; auto vbox = new QVBoxLayout(q); q->setLayout(vbox); vbox->setSpacing(6); vbox->setMargin(0); // build reports toc setColumnsAlreadyAdjusted(false); m_reportTabWidget = new QTabWidget(q); vbox->addWidget(m_reportTabWidget); m_reportTabWidget->setTabsClosable(true); m_listTab = new QWidget(m_reportTabWidget); m_listTabLayout = new QVBoxLayout(m_listTab); m_listTabLayout->setSpacing(6); m_tocTreeWidget = new QTreeWidget(m_listTab); // report-group items have only 1 column (name of group), // report items have 2 columns (report name and comment) m_tocTreeWidget->setColumnCount(2); // headers QStringList headers; headers << i18n("Reports") << i18n("Comment"); m_tocTreeWidget->setHeaderLabels(headers); m_tocTreeWidget->setAlternatingRowColors(true); m_tocTreeWidget->setSortingEnabled(true); m_tocTreeWidget->sortByColumn(0, Qt::AscendingOrder); // for report group items: // doubleclick toggles the expand-state, m_tocTreeWidget->setExpandsOnDoubleClick(false); m_tocTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); m_tocTreeWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); m_listTabLayout->addWidget(m_tocTreeWidget); m_reportTabWidget->addTab(m_listTab, i18n("Reports")); q->connect(m_reportTabWidget, &QTabWidget::tabCloseRequested, q, &KReportsView::slotClose); q->connect(m_tocTreeWidget, &QTreeWidget::itemDoubleClicked, q, &KReportsView::slotItemDoubleClicked); q->connect(m_tocTreeWidget, &QWidget::customContextMenuRequested, q, &KReportsView::slotListContextMenu); q->connect(MyMoneyFile::instance(), &MyMoneyFile::dataChanged, q, &KReportsView::refresh); } void restoreTocExpandState(QMap& expandStates) { for (auto i = 0; i < m_tocTreeWidget->topLevelItemCount(); ++i) { QTreeWidgetItem* item = m_tocTreeWidget->topLevelItem(i); if (item) { QString itemLabel = item->text(0); if (expandStates.contains(itemLabel)) { item->setExpanded(expandStates[itemLabel]); } else { item->setExpanded(false); } } } } /** * Display a dialog to confirm report deletion */ int deleteReportDialog(const QString &reportName) { Q_Q(KReportsView); return KMessageBox::warningContinueCancel(q, i18n("Are you sure you want to delete report %1? There is no way to recover it.", reportName), i18n("Delete Report?")); } void addReportTab(const MyMoneyReport& report) { Q_Q(KReportsView); new KReportTab(m_reportTabWidget, report, q); } void loadView() { // remember the id of the current selected item QTreeWidgetItem* item = m_tocTreeWidget->currentItem(); QString selectedItem = (item) ? item->text(0) : QString(); // save expand states of all top-level items QMap expandStates; for (int i = 0; i < m_tocTreeWidget->topLevelItemCount(); ++i) { item = m_tocTreeWidget->topLevelItem(i); if (item) { QString itemLabel = item->text(0); if (item->isExpanded()) { expandStates.insert(itemLabel, true); } else { expandStates.insert(itemLabel, false); } } } // find the item visible on top QTreeWidgetItem* visibleTopItem = m_tocTreeWidget->itemAt(0, 0); // text of column 0 identifies the item visible on top QString visibleTopItemText; bool visibleTopItemFound = true; if (visibleTopItem == NULL) { visibleTopItemFound = false; } else { // this assumes, that all item-texts in column 0 are unique, // no matter, whether the item is a report- or a group-item visibleTopItemText = visibleTopItem->text(0); } // turn off updates to avoid flickering during reload //m_reportListView->setUpdatesEnabled(false); // // Rebuild the list page // m_tocTreeWidget->clear(); // Default Reports QList defaultreports; defaultReports(defaultreports); QList::const_iterator it_group = defaultreports.constBegin(); // the item to be set as current item QTreeWidgetItem* currentItem = 0L; // group number, this will be used as sort key for reportgroup items // we have: // 1st some default groups // 2nd a chart group // 3rd maybe a favorite group // 4th maybe an orphan group (for old reports) int defaultGroupNo = 1; int chartGroupNo = defaultreports.size() + 1; // group for diagrams QString groupName = I18N_NOOP("Charts"); TocItemGroup* chartTocItemGroup = new TocItemGroup(m_tocTreeWidget, chartGroupNo, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, chartTocItemGroup); while (it_group != defaultreports.constEnd()) { groupName = (*it_group).name(); TocItemGroup* defaultTocItemGroup = new TocItemGroup(m_tocTreeWidget, defaultGroupNo++, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, defaultTocItemGroup); if (groupName == selectedItem) { currentItem = defaultTocItemGroup; } QList::const_iterator it_report = (*it_group).begin(); while (it_report != (*it_group).end()) { MyMoneyReport report = *it_report; report.setGroup(groupName); TocItemReport* reportTocItemReport = new TocItemReport(defaultTocItemGroup, report); if (report.name() == selectedItem) { currentItem = reportTocItemReport; } // ALSO place it into the Charts list if it's displayed as a chart by default if (report.isChartByDefault()) { new TocItemReport(chartTocItemGroup, report); } ++it_report; } ++it_group; } // group for custom (favorite) reports int favoriteGroupNo = chartGroupNo + 1; groupName = I18N_NOOP("Favorite Reports"); TocItemGroup* favoriteTocItemGroup = new TocItemGroup(m_tocTreeWidget, favoriteGroupNo, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, favoriteTocItemGroup); TocItemGroup* orphanTocItemGroup = 0; QList customreports = MyMoneyFile::instance()->reportList(); QList::const_iterator it_report = customreports.constBegin(); while (it_report != customreports.constEnd()) { MyMoneyReport report = *it_report; groupName = (*it_report).group(); // If this report is in a known group, place it there // KReportGroupListItem* groupnode = groupitems[(*it_report).group()]; TocItemGroup* groupNode = m_allTocItemGroups[groupName]; if (groupNode) { new TocItemReport(groupNode, report); } else { // otherwise, place it in the orphanage if (!orphanTocItemGroup) { // group for orphaned reports int orphanGroupNo = favoriteGroupNo + 1; groupName = I18N_NOOP("Old Customized Reports"); orphanTocItemGroup = new TocItemGroup(m_tocTreeWidget, orphanGroupNo, i18n(groupName.toLatin1().data())); m_allTocItemGroups.insert(groupName, orphanTocItemGroup); } new TocItemReport(orphanTocItemGroup, report); } // ALSO place it into the Favorites list if it's a favorite if ((*it_report).isFavorite()) { new TocItemReport(favoriteTocItemGroup, report); } // ALSO place it into the Charts list if it's displayed as a chart by default if ((*it_report).isChartByDefault()) { new TocItemReport(chartTocItemGroup, report); } ++it_report; } // // Go through the tabs to set their update flag or delete them if needed // int index = 1; while (index < m_reportTabWidget->count()) { // TODO: Find some way of detecting the file is closed and kill these tabs!! if (auto tab = dynamic_cast(m_reportTabWidget->widget(index))) { if (tab->isReadyToDelete() /* || ! reports.count() */) { delete tab; --index; } else { tab->loadTab(); } } ++index; } if (visibleTopItemFound) { // try to find the visibleTopItem that we had at the start of this method // intentionally not using 'Qt::MatchCaseSensitive' here // to avoid 'item not found' if someone corrected a typo only QList visibleTopItemList = m_tocTreeWidget->findItems(visibleTopItemText, Qt::MatchFixedString | Qt::MatchRecursive); if (visibleTopItemList.isEmpty()) { // the item could not be found, it was deleted or renamed visibleTopItemFound = false; } else { visibleTopItem = visibleTopItemList.at(0); if (visibleTopItem == NULL) { visibleTopItemFound = false; } } } // adjust column widths, // but only the first time when the view is loaded, // maybe the user sets other column widths later, // so don't disturb him if (columnsAlreadyAdjusted()) { // restore expand states of all top-level items restoreTocExpandState(expandStates); // restore current item m_tocTreeWidget->setCurrentItem(currentItem); // try to scroll to the item visible on top // when this method started if (visibleTopItemFound) { m_tocTreeWidget->scrollToItem(visibleTopItem); } else { m_tocTreeWidget->scrollToTop(); } return; } // avoid flickering m_tocTreeWidget->setUpdatesEnabled(false); // expand all top-level items m_tocTreeWidget->expandAll(); // resize columns m_tocTreeWidget->resizeColumnToContents(0); m_tocTreeWidget->resizeColumnToContents(1); // restore expand states of all top-level items restoreTocExpandState(expandStates); // restore current item m_tocTreeWidget->setCurrentItem(currentItem); // try to scroll to the item visible on top // when this method started if (visibleTopItemFound) { m_tocTreeWidget->scrollToItem(visibleTopItem); } else { m_tocTreeWidget->scrollToTop(); } setColumnsAlreadyAdjusted(true); m_tocTreeWidget->setUpdatesEnabled(true); } void defaultReports(QList& groups) { { ReportGroup list("Income and Expenses", i18n("Income and Expenses")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::ExpenseIncome, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::CurrentMonth, eMyMoney::Report::DetailLevel::All, i18n("Income and Expenses This Month"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::ExpenseIncome, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Income and Expenses This Year"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::ExpenseIncome, static_cast(eMyMoney::Report::ColumnType::Years), TransactionFilter::Date::All, eMyMoney::Report::DetailLevel::All, i18n("Income and Expenses By Year"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::ExpenseIncome, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last12Months, eMyMoney::Report::DetailLevel::Top, i18n("Income and Expenses Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartType(eMyMoney::Report::ChartType::Line); list.back().setChartDataLabels(false); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::ExpenseIncome, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last12Months, eMyMoney::Report::DetailLevel::Top, i18n("Income and Expenses Bar Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartType(eMyMoney::Report::ChartType::StackedBar); list.back().setChartDataLabels(false); list.back().setNegExpenses(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::ExpenseIncome, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::Group, i18n("Income and Expenses Pie Chart"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartType(eMyMoney::Report::ChartType::Pie); list.back().setShowingRowTotals(false); groups.push_back(list); } { ReportGroup list("Net Worth", i18n("Net Worth")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::Top, i18n("Net Worth By Month"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Today, eMyMoney::Report::DetailLevel::Top, i18n("Net Worth Today"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Years), TransactionFilter::Date::All, eMyMoney::Report::DetailLevel::Top, i18n("Net Worth By Year"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Next7Days, eMyMoney::Report::DetailLevel::Top, i18n("7-day Cash Flow Forecast"), i18n("Default Report") )); list.back().setIncludingSchedules(true); list.back().setColumnsAreDays(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last12Months, eMyMoney::Report::DetailLevel::Total, i18n("Net Worth Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Line); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Institution, eMyMoney::Report::QueryColumn::None, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::Top, i18n("Account Balances by Institution"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AccountType, eMyMoney::Report::QueryColumn::None, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::Top, i18n("Account Balances by Type"), i18n("Default Report") )); groups.push_back(list); } { ReportGroup list("Transactions", i18n("Transactions")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Account, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Category | eMyMoney::Report::QueryColumn::Tag | eMyMoney::Report::QueryColumn::Balance, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Transactions by Account"), i18n("Default Report") )); //list.back().setConvertCurrency(false); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Category, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Account | eMyMoney::Report::QueryColumn::Tag, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Transactions by Category"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Payee, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Category | eMyMoney::Report::QueryColumn::Tag, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Transactions by Payee"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Tag, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Category, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Transactions by Tag"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Month, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Category | eMyMoney::Report::QueryColumn::Tag, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Transactions by Month"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Week, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Category | eMyMoney::Report::QueryColumn::Tag, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Transactions by Week"), i18n("Default Report") )); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Account, eMyMoney::Report::QueryColumn::Loan, TransactionFilter::Date::All, eMyMoney::Report::DetailLevel::All, i18n("Loan Transactions"), i18n("Default Report") )); list.back().setLoansOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AccountReconcile, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Category | eMyMoney::Report::QueryColumn::Balance, TransactionFilter::Date::Last3Months, eMyMoney::Report::DetailLevel::All, i18n("Transactions by Reconciliation Status"), i18n("Default Report") )); groups.push_back(list); } { ReportGroup list("CashFlow", i18n("Cash Flow")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::CashFlow, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Account, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Cash Flow Transactions This Month"), i18n("Default Report") )); groups.push_back(list); } { ReportGroup list("Investments", i18n("Investments")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::TopAccount, eMyMoney::Report::QueryColumn::Action | eMyMoney::Report::QueryColumn::Shares | eMyMoney::Report::QueryColumn::Price, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Investment Transactions"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AccountByTopAccount, eMyMoney::Report::QueryColumn::Shares | eMyMoney::Report::QueryColumn::Price, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Investment Holdings by Account"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::EquityType, eMyMoney::Report::QueryColumn::Shares | eMyMoney::Report::QueryColumn::Price, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Investment Holdings by Type"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AccountByTopAccount, eMyMoney::Report::QueryColumn::Performance, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Investment Performance by Account"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::EquityType, eMyMoney::Report::QueryColumn::Performance, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Investment Performance by Type"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AccountByTopAccount, eMyMoney::Report::QueryColumn::CapitalGain, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Investment Capital Gains by Account"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::EquityType, eMyMoney::Report::QueryColumn::CapitalGain, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Investment Capital Gains by Type"), i18n("Default Report") )); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Today, eMyMoney::Report::DetailLevel::All, i18n("Investment Holdings Pie"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Pie); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last12Months, eMyMoney::Report::DetailLevel::All, i18n("Investment Worth Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Line); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last12Months, eMyMoney::Report::DetailLevel::All, i18n("Investment Price Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Line); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(false); list.back().setIncludingPrice(true); list.back().setConvertCurrency(true); list.back().setChartDataLabels(false); list.back().setSkipZero(true); list.back().setShowingColumnTotals(false); list.back().setShowingRowTotals(false); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last12Months, eMyMoney::Report::DetailLevel::All, i18n("Investment Moving Average Price Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Line); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(false); list.back().setIncludingAveragePrice(true); list.back().setMovingAverageDays(10); list.back().setConvertCurrency(true); list.back().setChartDataLabels(false); list.back().setShowingColumnTotals(false); list.back().setShowingRowTotals(false); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last30Days, eMyMoney::Report::DetailLevel::All, i18n("Investment Moving Average"), i18n("Default Report") )); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Line); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(false); list.back().setIncludingMovingAverage(true); list.back().setMovingAverageDays(10); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Last30Days, eMyMoney::Report::DetailLevel::All, i18n("Investment Moving Average vs Actual"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Line); list.back().setColumnsAreDays(true); list.back().setInvestmentsOnly(true); list.back().setIncludingBudgetActuals(true); list.back().setIncludingMovingAverage(true); list.back().setMovingAverageDays(10); groups.push_back(list); } { ReportGroup list("Taxes", i18n("Taxes")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Category, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Account, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Tax Transactions by Category"), i18n("Default Report") )); list.back().setTax(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Payee, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Category | eMyMoney::Report::QueryColumn::Account, TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Tax Transactions by Payee"), i18n("Default Report") )); list.back().setTax(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Category, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Payee | eMyMoney::Report::QueryColumn::Account, TransactionFilter::Date::LastFiscalYear, eMyMoney::Report::DetailLevel::All, i18n("Tax Transactions by Category Last Fiscal Year"), i18n("Default Report") )); list.back().setTax(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Payee, eMyMoney::Report::QueryColumn::Number | eMyMoney::Report::QueryColumn::Category | eMyMoney::Report::QueryColumn::Account, TransactionFilter::Date::LastFiscalYear, eMyMoney::Report::DetailLevel::All, i18n("Tax Transactions by Payee Last Fiscal Year"), i18n("Default Report") )); list.back().setTax(true); groups.push_back(list); } { ReportGroup list("Budgeting", i18n("Budgeting")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::BudgetActual, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::YearToDate, eMyMoney::Report::DetailLevel::All, i18n("Budgeted vs. Actual This Year"), i18n("Default Report") )); list.back().setShowingRowTotals(true); list.back().setBudget("Any", true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::BudgetActual, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::YearToMonth, eMyMoney::Report::DetailLevel::All, i18n("Budgeted vs. Actual This Year (YTM)"), i18n("Default Report") )); list.back().setShowingRowTotals(true); list.back().setBudget("Any", true); // in case we're in January, we show the last year if (QDate::currentDate().month() == 1) { list.back().setDateFilter(TransactionFilter::Date::LastYear); } list.push_back(MyMoneyReport( eMyMoney::Report::RowType::BudgetActual, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::CurrentMonth, eMyMoney::Report::DetailLevel::All, i18n("Monthly Budgeted vs. Actual"), i18n("Default Report") )); list.back().setBudget("Any", true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::BudgetActual, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::CurrentYear, eMyMoney::Report::DetailLevel::All, i18n("Yearly Budgeted vs. Actual"), i18n("Default Report") )); list.back().setBudget("Any", true); list.back().setShowingRowTotals(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Budget, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::CurrentMonth, eMyMoney::Report::DetailLevel::All, i18n("Monthly Budget"), i18n("Default Report") )); list.back().setBudget("Any", false); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Budget, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::CurrentYear, eMyMoney::Report::DetailLevel::All, i18n("Yearly Budget"), i18n("Default Report") )); list.back().setBudget("Any", false); list.back().setShowingRowTotals(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::BudgetActual, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::CurrentYear, eMyMoney::Report::DetailLevel::Group, i18n("Yearly Budgeted vs Actual Graph"), i18n("Default Report") )); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setBudget("Any", true); list.back().setChartType(eMyMoney::Report::ChartType::Line); groups.push_back(list); } { ReportGroup list("Forecast", i18n("Forecast")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Next12Months, eMyMoney::Report::DetailLevel::Top, i18n("Forecast By Month"), i18n("Default Report") )); list.back().setIncludingForecast(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::NextQuarter, eMyMoney::Report::DetailLevel::Top, i18n("Forecast Next Quarter"), i18n("Default Report") )); list.back().setColumnsAreDays(true); list.back().setIncludingForecast(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::ExpenseIncome, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::CurrentYear, eMyMoney::Report::DetailLevel::Top, i18n("Income and Expenses Forecast This Year"), i18n("Default Report") )); list.back().setIncludingForecast(true); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AssetLiability, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Next3Months, eMyMoney::Report::DetailLevel::Total, i18n("Net Worth Forecast Graph"), i18n("Default Report") )); list.back().setColumnsAreDays(true); list.back().setIncludingForecast(true); list.back().setChartByDefault(true); list.back().setChartCHGridLines(false); list.back().setChartSVGridLines(false); list.back().setChartType(eMyMoney::Report::ChartType::Line); groups.push_back(list); } { ReportGroup list("Information", i18n("General Information")); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Schedule, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Next12Months, eMyMoney::Report::DetailLevel::All, i18n("Schedule Information"), i18n("Default Report") )); list.back().setDetailLevel(eMyMoney::Report::DetailLevel::All); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::Schedule, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Next12Months, eMyMoney::Report::DetailLevel::All, i18n("Schedule Summary Information"), i18n("Default Report") )); list.back().setDetailLevel(eMyMoney::Report::DetailLevel::Top); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AccountInfo, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Today, eMyMoney::Report::DetailLevel::All, i18n("Account Information"), i18n("Default Report") )); list.back().setConvertCurrency(false); list.push_back(MyMoneyReport( eMyMoney::Report::RowType::AccountLoanInfo, static_cast(eMyMoney::Report::ColumnType::Months), TransactionFilter::Date::Today, eMyMoney::Report::DetailLevel::All, i18n("Loan Information"), i18n("Default Report") )); list.back().setConvertCurrency(false); groups.push_back(list); } } bool columnsAlreadyAdjusted() const { return m_columnsAlreadyAdjusted; } void setColumnsAlreadyAdjusted(bool adjusted) { m_columnsAlreadyAdjusted = adjusted; } KReportsView *q_ptr; /** * This member holds the load state of page */ bool m_needLoad; QListWidget* m_reportListView; QTabWidget* m_reportTabWidget; QWidget* m_listTab; QVBoxLayout* m_listTabLayout; QTreeWidget* m_tocTreeWidget; QMap m_allTocItemGroups; QString m_selectedExportFilter; bool m_columnsAlreadyAdjusted; MyMoneyAccount m_currentAccount; }; #endif