diff --git a/doc/index.docbook b/doc/index.docbook index 1751980a7..e81b98b15 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,2773 +1,2773 @@ Skrooge'> ]> The &skrooge; Handbook Stéphane MANKOWSKI
stephane@mankowski.fr
Guillaume DE BURE
guillaume.debure@gmail.com
Developer
2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 Stéphane MANKOWSKI Guillaume DE BURE &FDLNotice; 2017-08-30 - Skrooge 2.9.0 + &skrooge; 2.9.0 This document is a handbook for using &skrooge;, a personal finances manager application. KDE extragear office skrooge personal finances
Introduction &skrooge; &skrooge; is an application for managing your personal finances. It can keep track of your incomes and expenses across several accounts, in several currencies. It has all the features you should expect from such a tool, such as categories, scheduled operations, graphical reporting, stocks management... It also has some less common features, like fast operation edition, search as you type, refund trackers, customizable attributes... &skrooge; displaying pie charts of expenses per category &skrooge; displaying pie charts of expenses per category What &skrooge; Is &skrooge; is an Open Source personal finances manager built on &kde; frameworks. It is intended to be used by individuals who want to keep track of their incomes, expenses and investments. What &skrooge; Is Not &skrooge; is not a professional tool. It doesn't have functions expected from a software you would use to run a small business. It doesn't manage taxes, doesn't use double-entry system, or any advanced financial function. If you were looking for such a tool, you may want to look at Kmymoney or Kraft instead. Terminology Document A document is a &skrooge; file (extension .skg). It can contain an indefinite number of accounts. You can use one document to manage all your accounts at once. Account A &skrooge; account is similar to your bank account. For example, if you own one account for you, one for your wife, and one in common, you can create these three accounts in &skrooge;. Every time you spend or earn some money, you register an operation, and indicate the account this operation was made on. Operation An operation is either a credit or debit to one of your accounts. If you assign a category or tracker to operations, you can analyze how you spend your money. Standard Operation A standard operation is the basic operation in &skrooge;. For example, "Today, spent 20€ in credit card for groceries". Split Operation A split operation is an operation for which the total amount is split over several categories, dates and/or trackers. For example, "Yesterday, spent 100€ at the supermarket, of which 60€ were for food, and 40€ for clothes". Transfer A transfer in &skrooge; records moving a quantity of money from one of your &skrooge; accounts to another. This must not be confused with the payment mode chosen: if you made a transfer, for example using your bank's website, to someone else's account, this is not a transfer operation, because &skrooge; doesn't know about that other person's account. It is a standard operation with transfer as its payment mode. Read the transfer section if things are still unclear Validated Operation When you import operations from your financial institution into &skrooge;, they are not considered valid right away and appear in blue. You need to manually validate them. Such validated operations no longer appear blue. Note that it is possible to automatically validate operations on import, by turning on the corresponding option in the settings. Pointed Operation During the reconciliation process, you point in &skrooge; all operations that appear in the corresponding account position. Such pointed operations display a half-filled square in the Status column of an operations view. A pointed operation is not Checked until you complete reconciliation. Checked Operation When all operations on your account's position have been pointed in &skrooge;, you can complete the reconciliation process. &skrooge; then turns all Pointed operations into Checked Operations, which display a filled square in the Status column of an operations view. Category A category is basically a class of operations. Some classic examples include "Food", "Taxes","Salary"... A category can contain other categories: it is a hierarchical structure. For example, category "transport" can contain categories "Bus", "Automotive", "plane"... &skrooge; handles an infinite depth of categories. Unit A unit in &skrooge; can be anything that has a value. Examples: Currency $ (US Dollar) € (euro) £ (Sterling Pound) Stock or index GOOG (Google) NASDAQ Anything you own Your house Your car Main concepts User Interface The &skrooge; window is composed of a main area, surrounded by docks, and a status bar. Docks can be closed and reopened, and positioned left or right of the main area. The main area This is where &skrooge; will display the information you are interested in. It can contain many tabs, each one with the view you assigned to it. Docks Docks can be found in many &kde; applications. They are a subpart of an application window, and can be relocated, stacked, closed, or even detached from the main window. Some examples of docks layout: &skrooge; docks are: The page chooser The bookmark list The undo / redo browser The property editor The messages list Page Chooser The page chooser is a dock where you can select the view to be displayed in the current tab or in a new tab. To open in a new tab, hold the &Ctrl; key while clicking on the selected view.You can customize this list by using the contextual menu on this list. The page chooser Each context will be described in a specific chapter in this handbook. Bookmarks Imagine you need to customize the operations view on a per account basis: each account would be displayed in its own tab, maybe with different columns in each table. The forementioned method does not work, since it applies on all tabs with the same type. The solution here is to save each individual tab as a Bookmark. Much like in a web browser, bookmarks can be organized in a hierarchy of bookmark folders, that you can visualize in the Bookmark Browser: The bookmarks browser To create a bookmark, select a page from the page chooser you want to bookmark, and customize its content to your liking. Once you are done with that, in the Bookmarks dock, bring up the contextual menu, and select Bookmark current page. Each bookmark or bookmark folder can be "autostarted", &ie; it will be automatically opened when starting &skrooge;. This way, you can fully customize the default &skrooge; layout. You can select the text and icon you want for each bookmark or bookmark folder. Undo / Redo &skrooge; manages undo/redo in a rather classic fashion, except that you can undo or redo any action, even if it was made several days ago. As per default settings, the history is not cleared upon closing &skrooge; (you can change this behaviour in the settings), which means you could undo virtually everything up to the document creation. In order to limit the impact on filesize, &skrooge; is configured by default to keep a history of 50 entries. You can change this value in the settings. &skrooge; has a dock that lists all undoable actions. The history browser The history browser shows three columns: an icon showing the state of the action. A yellow counter clockwise arrow means the action is undoable. A green clockwise icon means the action is redoable. the description of the action the date when the action was made a disk icon means this action corresponds to a saved state, &ie; the document was saved right after this action. To undo an undoable operation, or redo a redoable operation, double click on it. Property Editor One of &skrooge; rather uncommon features is the ability to add custom properties to any object. For example, you may want to add the name and phone number of a bank employee to an account, because he / she is your contact. Or maybe attach a file to an operation, like an invoice scan. This can be achieved using the property editor, which is a dock of its own. The property editor, with an attachment When this dock is visible, it will display the custom properties of the selected object (For: Selection) or of all objects (For: All) , whether it is an account, an operation, category, unit... This dock allows to create, update or delete properties. A property can be: A simple string A copied file whatever the format (odt, png, pdf, ...). It means that the file is copied into the &skrooge; document. This file can be opened from &skrooge; by clicking on the appropriate button. Take care, all modifications of original file are not replicated in the version saved in &skrooge; document. A link to an existing file whatever the format (odt, png, pdf, ...). It means that the file is not copied into the &skrooge; document but just linked. This file can be opened from &skrooge; by clicking on the appropriate button. A link to an http page. Example: the website of your bank. Added properties can be displayed as a column on tables Message list This dock allows you to see all messages displayed. The messages list Each context will be described in a specific chapter in this handbook. Multiple Tabs &skrooge; showing several tabs &skrooge; can display several tabs. You can add as many tabs as you want, each one containing the information you need. The above screenshot shows 4 tabs (accounts, operations, and two graphs). Edition Panels In most &skrooge; tabs, the way to edit items is to use the edition panel, located at the bottom of the page. In order to preserve maximum space for visualizing information, especially on small screens, edition panels can be hidden or displayed using buttons. Buttons for showing or hiding Operations edition panels. When more than one edition mode is available, several buttons allow choosing amongst them. In the above screenshot, there are four buttons allowing to choose the edition mode for Operations, "Standard", "Split", "Transfer" and "Shares". All numerical fields are interpreted. It means that you can enter mathematical expressions like this: 5*3+10 5*(3+10) Math.sin(10) Tables In &skrooge;, all tables are customizable. Bring up the contextual menu (usually by right-clicking) on the column header, and you will see a menu appear: Customizing a table Columns Customize the columns appearance. View Appearance Use a predefined set of columns. The "default" column usually shows all columns. Other predefined sets may exist on a per table basis. Resize to Content Resize all columns to fit to the content. Auto Resize When activated, you will not be able to manually resize columns, &skrooge; will do it automatically based on the columns content. List of displayed columns Shows the list of all columns that can be displayed for this table. The ones currently displayed are ticked. Untick to hide a column, tick to show. The added properties can be selected to be displayed as a column. Group by Choose how to group the lines. None No grouping. Sorting column The lines will be grouped by the column where the sort is applied. List of columns Shows the list of all columns that can be used for grouping. Alternate row colors Alternate the colors used for each row. The colors used are based on the chosen &kde; color scheme. Export - Export the table as a stand-alone file. Supported formats are pdf, csv, html, svg, odt or txt. + Export the table as a stand-alone file. Supported formats are pdf, csv, html, &SVG;, odt or txt. You may also click on a header to choose the sorting column, or reorder columns by dragging them left or right. Filter the table Show table menu In all &skrooge; views, you will find a "Show" drop down menu allowing you to limit what the table displays. Search as you type Search field In all &skrooge; views, you will find a search field that filters the table to only display lines matching what you type: In an operations view, it will filter the operations containing the text entered, whatever the column (date, payee, category, comment...) In an accounts view, it will filter the accounts containing the text entered, whatever the column (bank, account name, number...) In a report view, it will redraw the graph based on this filter In a... well, you get the idea, right? The filtering string follows these rules: Searching is case-insensitive. So table, Table and TABLE are all the same. If you enter a word or series of words in the search box, the application will filter the table to only display lines having these words (logical operator AND). If you want to add (logical operator OR) some lines, you must prefix your word by "+". If you want to remove (logical operator NOT) some lines, you must prefix your word by "-". If you want to search only on one column, you must prefix your word by the column name, like: col1:word. If you want to use the character ":" in value, you must specify the column name, like: col1:value:rest. If you want to search for a phrase or something that contains spaces, you must put it in quotes, like: "yes, this is a phrase". You can also use operator "<" and ">". Some examples may help explain: +val1 +val2 => Keep lines containing val1 OR val2 +val1 -val2 => Keep lines containing val1 but NOT val2 "abc def" => Keep lines containing the sentence "abc def" "-att:abc def" => Remove lines having a column name starting by att and containing "abc def" abc:def => Keep lines having a column name starting by abc and containing def :abc:def => Keep lines containing "abc:def" Date>2015-03-01 => Keep lines where Date attribute is greater than 2015-03-01 Amount<10 =>Keep lines where Amount attribute is less than 10 Save Customized Content As explained in the previous chapter, tables can be totally customized to your liking. But you probably do not want to loose all the customization work you made upon closing a tab. There are two ways to save this work. Save page state This option allows you saving the current tab state as the default state that will always be called when opening this page. For example, you may want your Dashboard to contain 4 widgets, organized to your liking. What's even more important is that you always want the Dashboard to look the same, you do not want to repeat the customization work every time you open it. To do this, you simply need to do customization work once, then to save the tab state as the Default State for the page being displayed. Setting the default state is done either by right clicking on the tab and selecting Save page state. If you want to keep more than one state for a page then you must use bookmarks. Contextual menu on tabs A faster way is to click on the disk icon that appears on the left of the tab title whenever you modified the default state in a tab. Mass Update In &skrooge;, it is possible to massively update a selection of items. Though mostly useful for operations, it can also be used on accounts, units, scheduled operations... The way to do this is quite straightforward : select items to be updated, set the attribute value(s) that should be applied on all items, and click on Apply. For all attributes where you didn't set a value, it will be left untouched on items. Set mode as "Credit Card" and payee as "ACME" on selected operations. Updated operations. It is intentionally impossible to set date or quantity with a mass update Status Bar The status bar is a small section at the bottom of the &skrooge; window, displaying various information as you work. It includes a progress bar and a cancel button, in case you want to interrupt a long task, such as importing a large file. The &skrooge; progress bar Import Files &skrooge; is able to import files from other financial applications or from your bank. So, whether you are coming from another application, or simply do not want to go through the hassle of manually entering your operations, &skrooge; has a fast lane for you. Supported File Formats One the following formats may be used: AFB120: A French norm. CSV: Comma Separated Value. Though not strictly a financial format, it is quite often available as an export format from banks or other applications, mostly because it is so easy to use in a spreadsheet. GnuCash: Format of the GnuCash application. If you want to migrate from this application, this is the recommended format to use. Grisbi: Format of the Grisbi application. If you want to migrate from this application, this is the recommended format to use. Homebank: Format of the Homebank application. If you want to migrate from this application, this is the recommended format to use. Kmymoney: Format of the Kmymoney application. If you want to migrate from this application, this is the recommended format to use. MT940: An international norm defined by SWIFT. &Microsoft; Money: Format of the &Microsoft; Money application. If you want to migrate from this application, this is the recommended format to use. Money Manager Ex: Format of the Money Manager Ex application. If you want to migrate from this application, this is the recommended format to use. OFX: Open Financial eXchange. It is a well defined & documented format, that &skrooge; imports using a third party library (libofx). This is the recommended format for imports. QFX: QFX is a customization of OFX from the commercial software Quicken. QIF: Quicken Import File. Maybe the most common financial file format. However, it has some rather annoying limitations, like not giving the unit for operation, or no strict date formatting. IIF: Intuit Interchange Format is used by QuickBooks. SKG: This is useful to merge 2 &skrooge; documents &PDF;: This allows to create the operation from a &PDF; invoice. The invoice is also associated to the operation as a property. Read the How to if you want to know how to extract information from an invoice not supported yet. Backend: &skrooge; can also import operations by using a backend. For the moment, the three following backends can be used: weboob: By using this backend you can import all operations from all your banks in only one click. For that, you just have to install weboob and activate the corresponding backend from settings. weboob_coming: By using this backend you can import all coming operations from all your banks in only one click. This can be used for card with deferred debit. For that, you just have to install activate and activate the corresponding backend from settings. aqbanking: By using this backend you can import all operations from all your banks in only one click. For that, you just have to install and configure aqbanking-cli. If you do not want to store your bank passwords in the configuration file of WEBOOB, you can do that: Add passwords for each bank by doing kwallet-query -f Weboob kdewallet -w m_bank_name Edit the file ~/.config/weboob/backends in your favorite text editor Replace in the config file password = my_password by password = `kwallet-query -f Weboob kdewallet -r m_bank_name ` CSV Specificities Since CSV has no strictly defined format, there is no way for &skrooge; to know where it is supposed to find dates, categories, values... To do so, &skrooge; expects your CSV file to contain some headers indicating what is the column for. The file must contain at least columns "Date" and "Amount". You may manually define how the file is set up (&ie; ignoring the headers in the file) by setting them in the application settings (Settings Configure &skrooge;), in the Import / Export section. Importing Operations One of &skrooge; principles is to avoid multiple wizards or dialog boxes. This is especially true for imports, where other applications require many information from the user. Not &skrooge;. When selecting FileImport, you will be asked to select the files (yes you can import many at once), and that's it. Behind the scene, &skrooge; will find the file format and apply the relevant import mode. Account One thing that might surprise you is that you won't be asked in which account the operations should be imported. That is because &skrooge; will read the account number in the file, and will associate imported operations to that account. If no account exists with this account number, or if the file does not contain this information, &skrooge; will use the file name as the account number. If there is still no account with that number, &skrooge; will create a new account having the filename as the account number, and import all operations in that account. You will then be free to either rename the account, perform a mass update, or use Search & Process to associate imported operations to an existing account. Unit If no unit is specified in the imported file (which is often the case for QIF & CSV), &skrooge; will assume the unit is your primary currency. If this was not the case, use the Search & Process function to correct. Date Sometimes, the input files will not have dates formatted as per your country's customs (it may happen in QIF or CSV, OFX has an imposed date format). &skrooge; will do its best to detect the format. If this doesn't work as expected, you can force the date format in the application's settings. Import Status Imported Operations have a specific status. Right after import, they are considered as "Imported, not yet validated". The idea here is that you may want to check if the import went as expected, so you need to identify quickly those operations. As per default settings, those operations will also appear in blue. If you do not want this behaviour, you can set the option Automatic Validation after Import in the application's settings. The Search & Process function can be automatically launched after import to categorize operations. Merge operations after import There are some cases where you have manually entered an operation, but also imported it from your bank. In such cases, the operation will appear twice in the list. You may merge these two operations by selecting them, and select Merge imported operations in the contextual menu. This will add all information you manually entered to the imported operation, and will delete the manually entered operation. Export There are different ways of exporting data with &skrooge; depending on what you intend to do. Export all your data in one file - Using the menu File Export, a CSV, QIF, JSON, Kmymoney, Ledger, IIF, Sqlite, Sqlcipher or XML file will be created, containing all operations in the current document. This is the option to choose if you need to export your data to another application. + Using the menu File Export, a CSV, QIF, &JSON;, Kmymoney, Ledger, IIF, Sqlite, Sqlcipher or &XML; file will be created, containing all operations in the current document. This is the option to choose if you need to export your data to another application. Export partially your data in one file - If you select some accounts or some operations before using the menu File Export, a CSV, QIF, JSON, Kmymoney, Ledger, IIF, Sqlite, Sqlcipher or XML file will be created, containing the selected accounts (with their operations) or the selected operation. This is the option to choose if you need to export your data to another application. + If you select some accounts or some operations before using the menu File Export, a CSV, QIF, &JSON;, Kmymoney, Ledger, IIF, Sqlite, Sqlcipher or &XML; file will be created, containing the selected accounts (with their operations) or the selected operation. This is the option to choose if you need to export your data to another application. Export a specific table - All tables in &skrooge; can be exported, either in CSV, &HTML;, ODT, &PDF;, SVG or TXT format. + All tables in &skrooge; can be exported, either in CSV, &HTML;, ODT, &PDF;, &SVG; or TXT format. This works wherever a table is displayed, so you can use it to export whatever tabular information you need. Export Graph - &skrooge; allows you exporting a graph, either in &PDF;, SVG or just any image format supported by &Qt; (PNG, JPG, &GIF;, TIFF...). You can do so by right-clicking on the graph, and choosing the Export option. + &skrooge; allows you exporting a graph, either in &PDF;, &SVG; or just any image format supported by &Qt; (PNG, JPG, &GIF;, TIFF...). You can do so by right-clicking on the graph, and choosing the Export option. Using &skrooge; (beginner) The main menu &skrooge; follows the standard &kde; menu scheme. So it has many menu entries that are common for all &kde; applications. More information on these menu entries can be found this help section. Below, you can find descriptions for &skrooge; specific menu entries. The File Menu You can find in this menu all functions relative to the &skrooge; document Change password...: To change the password. Let the field empty to remove the password Print...: To print the open pages Print preview: To display the preview before printing Import: To import data. Import standard bookmarks: To import the standard bookmarks. They are created at the creation of a new document Import categories: To import a set of predefined categories. This option depends of your country Import: To import operation from files Import with backend: To import operations with all declared backends Import currency values...: To import currency values from files. Of course, the csv file must have the three columns (unit, date and amount). Import rules...: To import Search and process rules to associate a category to a payee. Of course, the csv file must have the two columns (payee and category). Export...: To export operations into a file The Edit Menu You can find in this menu all functions that can be used to modify selected objects Undo: Cancel the previous modification done Redo: Reapply the previous modification canceled Revert document: Cancel the previous modifications done up to the last save Delete: Delete the selected objects (operations, accounts, categories, ...) Add property: Add a property regularly used on the selected objects Select all: Select all lines of the table of the current page Fast edit: Facilitation the creation of new operations by filling fields automatically based on previous operations created Find...: Open the Search and process page with a predefined filter corresponding to the selected objects Switch highlight: Switch the highlight state of objects Switch close: Switch the close state of objects Reconcile...: Open the Operations page in reconciliation mode Point: Point the selected operations Group operations: Group the selected operations Ungroup operations: Ungroup the selected operations Duplicate: Duplicate the selected operation to create a new one from a previous one Create template: Create a template from a selected operation Apply template: Apply a template on selected operations Merge sub operations: Merge selected operations in one split operation Schedule: Schedule the selected operation Switch validation of imported operations: Validate all selected imported operations Merge imported operations: Merge one imported operation with another operation entered manually. If amounts are different then a confirmation is requested Split share: Split selected share The View Menu You can find in this menu all functions relative to pages Enable editor: Activate the editor on the current page New tab: Open a new tab Reopen last closed page: Recover the last closed page Close: Close the current page Close All: Close all pages except pinned ones Close All Other: Close all pages except the current one and the pinned ones Pin this page: Set the current page pinned. This will avoid replacement by another one or closure Reset page state: Reset the status of the current page Save page state: Save the status of the current page Overwrite bookmark state: Save the status of the current bookmarked page. The bookmark is updated with the new state The Go Menu You can find in this menu all functions relative to pages Home: Close all pages and reopen all bookmarks autostarted like when the document is opened Previous: Change the content of the current page with the content of the previous one. Like in a web browser Next: Change the content of the current page with the content of the next one. Like in a web browser Open highlights...: Open highlighted operations Open last modified...: Open operations modified by the last action done Open potential duplicates...: Open operations opens a page with operations having same amounts and same dates Open sub operations...: Open sub operations of the selected operation. Similar to a double click Open report...: Open a report based on the selection. This is really useful. For example: if you select 2 accounts and launch this action then the report page will be opened but only for operations of those accounts Open imported operations not yet validated...: nothing to add Open operations without category...: Open all operations not having a category Open transfers without category...: Open all transfers not having a category Open operations without payee...: Open all operations not having a payee Open transfers without payee...: Open all transfers not having a payee Open operations without mode...: Open all operations not having a mode Open operations with dates not aligned...: Open all single operations not having the date aligned with its sub operation Open operations with comments not aligned...: Open all single operations not having the comment aligned with its sub operation Open operations in groups with only one operation...: Open all single operations alone in a group Open very old operations: nothing to add The Tools Menu Find and group transfers: Create potential transfers Clean bank's import: Clean operations after imports Anonymize: Anonymize your document to help debugging of &skrooge; Align comment of suboperations: Correct single operations having comments not aligned Align date of suboperations: Correct single operations having dates not aligned Remove groups with only one operation: Correct alone in a group Delete unused payees: nothing to add Delete unused categories: nothing to add Delete unused units: nothing to add Validate operations that do not require further action: Validate imported operations Process budget rules: Recompute the budgets based on defined rules Dashboard The Dashboard is a &skrooge; plugin that displays global information about your financial situation. You can add new graphical items using button on the top right corner. You can configure each graphical item by clicking on the icon on it. You can organize the graphical items on the dashboard by using the drag and drop. Combined with bookmarks, this is a good way to have quick different views of your financial status. Advice This graphical item displays some advice by order of priority. For each of them, you will be able to: Open or clean concerned operations Dismiss this advice Dismiss this advice for the current month Dismiss this kind of advice Dismiss this kind of advice for the current month Tip of the day Display the tip of the day. You can click on the graphical item to see the next tip. Accounts (Light) Display the balance of each account and type of account. You can configure this graphical item to select what you want to see. Accounts (Full) Display the balance of each account and type of account. The balance can be compared with the balance one month before and one year before. Banks (Light) Display the balance of each bank. The corresponding report can be directly opened from the graphical item. Banks (Full) Display the balance of each bank. The balance can be compared with the balance one month before and one year before. Income & Expenditure Display the incomes, expenditures and savings for two different periods (example: current month, current year, previous month, ...). Of course, you can: Select the two periods you want Choose if transfers are taken into account or not Choose if tracked operations are taken into account or not Open the corresponding report Highlighted operations Display the list of highlighted operations. Scheduled operations Display the list of scheduled operations. 5 main categories of expenditure Display the 5 main categories of expenditure for the desired period. 5 main variations Display the 5 main variations of income and expenditure for the desired period. Budget Display the budget for the desired period. Alarms Display the alarms defined in Search and Process. Report Display the report you want. Quotes Display the quote of the defined units and the variations. You can choose the types of units to display. Portfolio Display all information relative to the shares you have. Estimated interest Display the estimation of interest for each account. You have to use the simulation page to define the rate of each account. Personal financial score Display your personal financial score. Bank & Accounts This is the plugin dedicated to managing your different accounts. The Bank & Accounts view Account properties A &skrooge; account has the following properties: Bank You may choose a bank from the drop down box (containing the list of known banks for your country), or type the name if your bank is not in the list (If the list is empty, the &skrooge; team has not yet been provided this information. You may contact us to help!). Account The name you want to use for this account. Type The account type can be Current Credit Card: This kind of account has a specific reconciliation mode. Saving Investment Assets Loan: The transfers to this kind of account are not taken into account in reports. Pension Wallet: This kind of account does not have bank name. Other Bank Number The identification number of your bank Agency Number The identification number of the agency Account Number The identification number of the account. This value is very important to match the appropriate account during imports Address The address of the agency Comment Anything you want ! Initial balance The initial amount of the account Minimum limit The minimum limit when &skrooge; must raise an alarm Maximum limit The maximum limit when &skrooge; must raise an alarm Accounts list The accounts list takes the form of a table with one line per account. In addition to the properties you defined for this account, &skrooge; displays the following columns: Amount The amount available on the account, considering all operations registered, whether they are validated or not. Checked The amount of all validated operations. This should be equal to the amount written on your last account's position from your bank. Foreseen The difference between the two previous columns Number of operations The total number of operations made on this account As with all &skrooge; list views, you can fully customize the table appearance. Create an Account The account creation is done using the edition panel below the accounts list. To create an account, enter its attributes (the mandatory ones are in bold), and click on Add. &skrooge; doesn't need you to provide the initial amount of this account. In order to set the initial amount of the account, create an operation corresponding to the initial amount. Modify an Account To modify an account, select it, modify its attributes, and click on "Modify" Delete Accounts To delete accounts, select them and either press Delete on the keyboard, use the contextual menu, or use the icon in the toolbar. Upon deleting an account, all operations belonging to this account will also be deleted ! (But you can always undo the deletion) Operations Operations are, well, any operation you make on an account, be it an expense, income, transfer, sales, purchase... This is where you will probably spend most of your time when using &skrooge;. In order to enter operations, you need to have at least one account created. The operations view Operation Properties Account The account the operation is made on. Select it from the list of existing accounts. Date The date when the operation was made. A calendar is accessible using the down arrow right of the field. Amount The amount is how much you spent or earned with this operation. &skrooge; requires you enter this in two separate fields: Quantity, and Unit. The quantity is negative for an expense (-30, for example), and positive for an income (+250, or simply 250). This field acts as a calculator, &ie; entering an expression such as 10 + 3.23*2 will result in the field containing 16.46. If the sign of the quantity is not specified, then &skrooge; will use the category to find the most appropriate one. &skrooge; requires you to enter the unit because an operation is not necessarily made in your main currency. For example, when you buy or purchase shares, the unit of the operation is the share unit. Payee Who did you pay this to, or who gave you the money. Mode What was the payment mode used for this operation. Something in the line of Credit Card, Cheque, Deposit... You name it ! Associated with the operation mode, is an optional number. It is mostly used to enter the check number, or a transfer number. Category Which Category this operation belongs to. If you need to affect more than one category to an operation, use the split mode. You can either choose an existing category, or type a new one. In this case, &skrooge; will create it for you along with the operation. The separator between a parent category and its children is the > character. If you type Clothes > Shoes, &skrooge; will create the category Clothes if it doesn't exist yet, and its child category Shoes. Of course, you may create entire category trees, such as Transport > Car > Fuel > Unleaded, &skrooge; will happily create all the hierarchical structure. Comment Any comment you'd like. Tracker If you want to track refund for this operation, enter the name of the Tracker here. If you need to affect more than one tracker to an operation, use the split mode. Operations list The accounts list takes the form of a table with one line per account. In addition to the properties you defined for this account, &skrooge; displays the following columns: Status This check box indicates whether this operation has been confirmed during account reconciliation. A half-greyed check box means the reconciliation is ongoing. Bookmark Mark this operation as Bookmarked, a synonym for Favorite. Scheduled When an operation is scheduled, this column shows a chronometer icon. These columns also have some specificities: Mode When the operation is part of a transfer, the mode is preceded by a double arrow icon. Category When the operation is split, the category is preceded by a triple arrow icon. As with all &skrooge; list views, you can fully customize the table appearance. Create an operation To create a standard operation, you may use the Clear button to clear all fields and start a new operation from scratch. You can then fill its attributes (manually or using fast edition), and click on Add. If the appropriate setting is enabled, then the category will be set automatically when the payee is set. You may also select a similar operation from the existing operations, modify what needs to be changed, and then click on Add. Split Operation A split operation is an operation that has several dates, categories, comments or trackers. You switch to the split operation edition mode using the Split Button at the bottom of &skrooge; window. When switching into the split operation edition mode, the fields Category, Comment and Tracker are replaced by a table. The split operation edition panel. To edit a cell in this table, double click on it. You can add as many lines in this table as you need. When changing the quantity in a line, &skrooge; will compute the difference with the operation global quantity, and display the remaining quantity in the last table line. If you changed the quantity in the last line, a new line will be added with the remaining quantity. The fields here have the same behaviour as in the standard operation edition mode: The split by Date is useful for example when you paid your sport training for 3 months. In this case, you can split the payment on 3 months, this will have an impact on reports. The Category is a drop down box of existing categories. You can add a new category structure, &skrooge; will create it when creating the operation. The Amount acts as a calculator. The Tracker is a drop down box of existing trackers. You can add a new tracker, &skrooge; will create it when creating the operation. Transfer A transfer operation is effectively a dual operation: when creating a transfer, &skrooge; will create two operations of opposite amounts (one positive, the other negative), on two different accounts. The attributes to provide here are slightly different from a standard operation: The transfer operation edition panel. You will find a "To Account" list where you should select the account receiving the money. Since a transfer means "Take some money from account A and put it account B", if you put a sign in the quantity field (+ or -), it will be ignored. The operation for account A will always be negative, positive for account B. Shares First of all you have to create some units with the shares you want to track, from the settings you can choose to update the data automatically when the file is opened. It is also recommended to create a different account (⪚ "ETF") in order to keep the investments separated and be able to see them from the dashboard. Now you can add your operations as "shares": The shares edition panel. Amount is the number of shares you've bought (positive value) or sold (negative value) Symbol is the previous unit you created Amount of shares is the total price you payed for the shares / the amount you got from the selling, excluding commissions and taxes Commissions is the amount you payed your bank for that transaction Tax is any amount you payed (usually when there are earnings) Schedule Operations If an operation is known to be repeated on a regular basis, you may want &skrooge; to automatically enter it for you in the list of operations. This can be done by scheduling an operation, using the Schedule button . The default scheduling parameters are: Repeat every month Remind me 5 days before term Automatically write on term You may change the default parameters in &skrooge; settings. You may also change each scheduled operation parameters in the Scheduled plugin. &skrooge; uses values from the last entry of the scheduled operation for writing the next one. If you increase for example the amount of your monthly contribution to the &kde; effort from 100$ to 200$, next operations automatically written by &skrooge; will have an amount of 200$. Fast Edition Whatever the chosen edition mode, there is a nifty function called Fast Edition that may speed up the work while creating new operations. It will fill the operation's attributes based on previously entered operations. Enter a value in any field of the editor, and press F10, or click on the Fast Edition Icon (without leaving the selected field). &skrooge; will look for the first (&ie; the most recent one) operation that has the same value in the same field, and fill all other fields with values from that operation. Call Fast Edition again, it will look for the next operation, and so on. Fast Edition has no effect on the date, since it is rather unlikely that you want to create exactly the same operation, complete with the same date... If you modified a field's value before calling Fast Edition, its content will not be affected. This is materialized by a different background color in the field, so you know it is "frozen". What's different here from similar functions in other personal finances software: It is called on user demand It loops in all past operations with the same field value And it even works on split operations ! Reconciliation Reconciliation is the process by which you ensure that all operations are aligned with your account's position from the bank point of view. It will involve you, your account's position, and a pen. Reconciliation can happen only for an account at one point in time. In the operation view, select the account you wish to reconciliate from the drop down box. Switch into reconciliation mode using the Switch Information button below the operations table. The reconciliation mode information. Enter the position of your account as provided by your bank in the dedicated field. Now, you can proceed to checking in &skrooge; every operation that appears on the account recordings. For the moment, the check box for this operation will appear half greyed, until you really validate the reconciliation. As you check operations, you will see &skrooge; display in the information zone: Delta: the difference between the previously entered account position and the sum of all checked operations Expenditure:the total amount of expenses Income: the total amount of incomes This may be useful when trying to spot operations you may have forgot to enter in &skrooge;. When all relevant operations have been checked, Delta is equal to 0, and the Validate checked operations button is activated. Click on this button to validate the reconciliation: all checked operations are now validated. can automatically point all imported operations for you. If the option Hide checked operations is selected, all these operations will now be hidden. If the reconciliation is not possible for any reason, a fake can be automatically created to allow the reconciliation. The default attributes of the fake operation can be defined from settings. During reconciliation, if the appropriate setting is enabled, all created operations are automatically pointed. Template Operations - &skrooge; allows you creating template operations, &ie; operations that can be reused whenever you need it. For example, imagine are used to renting a DVD to watch at home. The operation is always the same : + &skrooge; allows you creating template operations, &ie; operations that can be reused whenever you need it. For example, imagine are used to renting a &DVD; to watch at home. The operation is always the same : Amount: -4.99 Mode: Credit Card Payee: Acme Video Category: Leisure > Video - However, you cannot make it a recurrent operation, because you rent a DVD when you feel like it, not every week. The idea behind template operations is that it gives you a basic skeleton that can be quickly inserted in your operations list. + However, you cannot make it a recurrent operation, because you rent a &DVD; when you feel like it, not every week. The idea behind template operations is that it gives you a basic skeleton that can be quickly inserted in your operations list. Since Template operations are just another kind of operations, you can access them through the operations tab, using the dedicated option in Show menu: Switching between templates and operations. Creating a template is strictly equal to creating an operation, by defining its attributes. Note that you can also create a template from an existing operation (bring up the contextual menu on an operation). In this case, the template will have the same attributes as the operation. To use a template and create a new operation from it, double click on it. A new operation will be created with the same attributes as the template, at the current date. You can then modify its attributes if needed. Templates can also be scheduled. Payees The Payees view allow you to browse the payees list. In addition to the name of payees, the table also shows: Address The address of this payee. Category The default category for this payee. This category is used automatically when the payee is set in operations page. If you don't set it then the default category is computed with the existing operations. Amount The sum of all operations in this payee. Number of Operations The number of operations in this payee. Double clicking on a line in this table will open the list of all operations in this payee in a new tab. Categories The Categories view allow you to browse the categories tree. In addition to the name of categories, the table also shows: Amount The sum of all operations in this category. Number of Operations The number of operations in this category. Amount (Cumulative) The sum of all operations in this category and children categories (down to the last level). Number of Operations (Cumulative) The number of operations in this category and children categories (down to the last level). Double clicking on a line in this table will open the list of all operations in this category in a new tab. When deleting a category, all operations attached to it will have their category deleted, hence will not have any category attached Reports &skrooge; allows you to build highly customized reports, for a deep analysis of your financial status. A report is composed of a table (on the left) containing all data used to draw the graph (on the right). The table can be filtered using the dedicated field above, causing the graph to be redrawn with filtered data. You can choose to display only the table, only the graph, or both. Set up report Representation Table Display the table. Graph Display the graph. Text Display the text report, hide table and graph. Data This is where you choose what is the data to be present in the report. Select what will be shown in lines and in columns, and the mode of computation. There are two distinct values for the mode: Sum of operations In this mode, the total amount of operations for each month will be computed. If you selected "category" in line, and "month" in column, this will compute the total amount of operations for each category and for every month. Cumulated sum of operations If we reuse our previous example ("category" in line, "month" in column), we will have here a cumulated sum, &ie; for each month, we will compute the total amount of operations up to this month, for each category. As a hand on example, this author uses this mode to draw the evolution of his accounts balance by setting "Accounts" for Lines, and "Month" for Columns. Base 100 Like "Sum of operations" but on base 100. Cumulated sum in base 100 Like "Cumulated sum of operations" but on base 100. Percent of columns The values are expressed in percent of the high column value. Absolute percent of columns The values are expressed in percent of the high column value in absolute. Percent of lines The values are expressed in percent of the high line value. Absolute percent of lines The values are expressed in percent of the high line value in absolute. Count number of operations . For lines and columns, it is possible to expand the level of data presented using the plus icon right of the field. Use this to show subcategories in the report. Dates Use this to specify the date range for your report. You can use several ways to specify the date range, that should cover pretty much every possible date range you need. Operation Types Select the type of operations you want to include in your report. For example, if you want to draw a graph showing your expenses distribution per category, you probably do not want to include "Incomes" to your report. Other filters From this section, you can select some other filters defined by selection done in other pages. If you want a report only for the categories "Food" and "Gift", you just have to open the page of categories, select the 2 categories and come back to the report to activate the corresponding filter. Graph Types Once everything is set up to your needs, you should select the graph appearance using the drop down box below the graph. To better understand the differences between each type, let us build a report showing expenses per category (lines) and month (columns): Stack One bar per line, columns stacked. The second bar (yellowish) shows category "Security", all months stacked up. Histogram For each columns, every line has its own bar. Line Point Pie The classical pie graph. If the selected value for column is different from "nothing", displays one pie per column. Concentric Pie This one is a bit tricky, and probably needs rework on our side. The intention is to show one ring per level of data. Zoom on graph &skrooge; makes it possible to zoom in a graph for getting all the small details. Use the zoom control zone above the graph: Report Examples Here are some screenshots showing some classical report configurations. You may use them as a reference for your own reports. Incomes and Expenses For each month, compare the total amount of incomes versus the total amount of expenses. Incomes are in blue, expenses in yellow. Balance Evolution Show the evolution of the final balance on a monthly basis. Incomes and Expenses Display the distribution of expenses per category for the previous month. Going Deeper By double clicking on an item of a graph, you will open the operations included in this item. You can open a new report based on an item of a graph too. So you know you can make a deep analysis of your finances thanks to reports. But how deep is deep ? Well, that's even deeper than you imagined... If you are interested, read the hands on exercise analysing this author's expenses during its holidays :) Monthly Report This plugin shows an overview of your financial situation for a chosen period. You can choose the template you want. You can even create your own template by entering the name of the template you would like to create. Your default text editor will be launched with a sample for your template. You just have to follow the comments in this sample. - Do not hesitate to distribute your templates in the category Skrooge report templates of the KDE Store. + Do not hesitate to distribute your templates in the category &skrooge; report templates of the KDE Store. Units A unit is anything you can express an operation in. In most cases, this is a currency, but it can also be a stock, or even a car or a house. A unit has different values over time, its value being express in another unit. Example: the ACME stock is worth 31.2$ on January 2009. Edit Units Editing units can be done in different ways: Standard This mode allows you choosing amongst the set of predefined units which one you would like to add to the current document. Manual This mode allows you creating your own unit. Parameters to be provided are: Name: the comprehensive unit name. Symbol: the unit symbol, displayed in most tables and drop down boxes. Type: see Unit Types. Reference Unit: the (other) unit used to compute value of the (current) unit. For example, if you have EADS shares, and want to see their value in euros, you should select € as reference unit in EADS unit. Country Download source: The source to download quotes. The recommended one is "Yahoo". By clicking on the arrow on the right of the "Download" field, you can directly download new sources from store.kde.org Internet Code: The Internet code for this unit. If given this code, &skrooge; is able to download the unit values from selected source. Values This edition mode allows to download unit values from Internet, or to manually enter a unit value at a given date. Unit Types Even if &skrooge; considers just about anything as a unit, it makes a distinction depending on their type: Primary Currency This is the currency that will be used for displaying real operations amount. Secondary Currency If defined, the value in the secondary unit will be displayed upon hovering over an operation amount. Currency Any currency, with no specific role for display. Share This type of unit can be used for managing shares. Index A stock exchange index, such as the Dow Jones, Nasdaq, CAC40, SBF120... Other Any unit that doesn't fit in the above types. Using &skrooge; (advanced) Scheduled This plugin shows you the list of all scheduled operations. This is where you can set up each scheduled operation to have its own parameters. Next Occurrence Displayed for information, but can also be used to force the date of the next occurrence. Once every You can set an operation to be repeated over a given number of days, months, or years. Number of Occurrences If the occurrence has a limited number of occurrences, you can check this option and define either the number of remaining occurrences, or the date of the last occurrence. Remind me If you want &skrooge; to display a notification to remind you about the upcoming scheduled operation, you may check this option and set the number of days before term when the warning shall occur. Automatically Write If you want &skrooge; to automatically write the upcoming scheduled operation, you may check this option and set the number of days before term when it will be written. To modify the operation (category, amount, ...), you have to click on Jump to the operation. Double clicking on a scheduled operation will display the list of operations already registered for this scheduled operation in a new tab. Scheduled Operations If you schedule an operation, it will be used as a reference when inserting the next occurrence. Let us see an example, with this operation: Scheduled Operations (step 1) Date Payee Category Amount 20/06/2009 &kde; Donations > Open Source 20€
Now, if you schedule this operation, it will be written exactly like this for the next occurrence: Scheduled Operations (step 2) Date Payee Category Amount 20/06/2009 &kde; Donations > Open Source 20€ 20/07/2009 &kde; Donations > Open Source 20€
If you change something in the last occurrence, it will become the new reference for the next occurrence. For example, if you raise your monthly contribution to &kde; to 25€ in July, the next occurrence will also be with an amount of 25€: Scheduled Operations (step 3) Date Payee Category Amount 20/06/2009 &kde; Donations > Open Source 20€ 20/07/2009 &kde; Donations > Open Source 25€ 20/08/2009 &kde; Donations > Open Source 25€
All in all, a scheduled operation is a dynamic object, where the last occurrence is the reference. But maybe you'd like all occurrences to be static, &ie; the same values are always inserted for each new occurrence ? Enter Scheduled Templates.
Scheduled Templates Templates are some sort of reference operations. When scheduling a template, every new occurrence will be exactly equal to the template. Let's reuse our previous chapter example, by creating a template like this: Scheduled Templates (step 1) Date Payee Category Amount 20/06/2009 &kde; Donations > Open Source 20€
The next occurrence will be: Scheduled Templates (step 2) Date Payee Category Amount 20/06/2009 &kde; Donations > Open Source 20€ 20/07/2009 &kde; Donations > Open Source 20€
Now change your July contribution to &kde; to 25€. Because you have scheduled a template with 20€ as amount, the next occurrence will also have an amount of 20€: Scheduled Templates (step 3) Date Payee Category Amount 20/06/2009 &kde; Donations > Open Source 20€ 20/07/2009 &kde; Donations > Open Source 25€ 20/08/2009 &kde; Donations > Open Source 20€
If you need to change the monthly value, just edit the template, and all future occurrences will have the new value.
Trackers The trackers allow you to aggregate some operations together in a group, so you can follow them more closely. This may come handy to follow expenses for which you expect a refund, or simply knowing how much you spent during your last holidays in the Bahamas. For example, imagine you have been sent to Italy by your company. Once there, you paid your hotel room: in &skrooge;, you can create an operation in category "Business > Travels > Hotel", and assign a tracker called "Business Travel in Italy". You can assign this tracker to all professional expenses you make there. Then, if all goes well, your company pays you back for all these things, and you can create an income operation, and also affect it to the "Business Travel in Italy" tracker. By affecting trackers to operations that shall be reimbursed, you are able to follow completion of the reimbursement. This, of course, works also the other way round: if someone lends you some money, you are able to monitor how much you still have to reimburse. Double clicking on a tracker in the track view will open a new tab containing all operations attached to this tracker. Sometimes, you would like to track the refund of a payment done before you started to use &skrooge;. How to do that because it is not possible to set an initial amount on a tracker? You can create a split operation with: amount=0 to avoid impact on reports. a split with the expected amount associated to the tracker. the other split with the opposite amount not associated to a tracker. Close Tracker When you no longer need the tracker, for example because it has been fully reimbursed, you may simply delete it. Doing, this, however, will loose all information you have entered, and that may be of interest for future reference. A cleaner solution is to close the tracker: when a tracker is closed, it can be easily hidden from the trackers view, using the dedicated option. Search & Process This plugin allows you building complex queries to find operations, and optionally apply them some transformations. Here is a use case:
John downloads some data from his bank. The data comes, obviously, without indication on the category. In fact everything is written in the comment field. John would like some automatic way to correctly set up the category and other attributes based on the content of the comment.
So how does this work ? First, you will define a search criteria, that will give you a list of operations. Then, define the transformations to apply on these operations. A real case study can be found in appendix, providing some hands on exercise for the hereabove use case. Define search criteria Here, you can define the query for finding operations, using a combination of parameters. On the above picture, you can find a table with operation attributes for header. By editing a line, you create a new clause in the query. Each column is combined with other columns using a logical "and" : (column1 and column2) If you add a new line, you create another clause combine with the first line by a logical "or" : [line1] or [line2]. All in all, the combination of columns and lines can be read as: (column1 and column3) or (column2 and column3) Of course, you can have as many lines and columns as you need in a search query. Once satisfied with a query definition, you can add it to the list of existing search criteria, or modify an existing one. View search results When selecting a search query in the queries view, the number of found operations is displayed below the table: When double-clicking on a search query in the query view, all found operations are displayed in a new tab: Define Processing If you need to apply some processing on all operations found, select the concerned query, and activate the process definition panel: In this panel, you can define the transformations to perform on operations. Note that it is intentionally impossible to set an operation's amount or date in this panel. Define Alarm A special kind of use of the Search & Process plugin is the ability to define alarms. An alarm is basically &skrooge; calling your attention on some kind of event, such as your monthly budget for a category has been reached. To define an alarm, define first the search criteria giving the set of operations on which you need alert. For example, all operations in current month and in category "Clothes". Then, define the amount for which the alert shall be raised, for example 100€, and define the Alarm message that &skrooge; will send you when the amount is reached. Alarm messages can have parameters for the total amount (%1), alarm amount (%2) and difference (%3).
Budgets This plugin allows you to define and manage your budget. Like in all other financial software, &skrooge; allows you to define a budget. You can do it manually or automatically. Budget definition In manual mode, you can: Define a budget amount for a category for a year. Define a budget amount for a category for all months of a year. Define a budget amount for a category for a specific month of a year. Define if income or expenditure of sub categories must be taken into account in the defined budget (example: if the option is selected then a budget for the category "Car" will take into account the category "Car > Fuel"). You can define a budget capturing all income or expenditure not defined in another budget by leaving the category empty. This is really useful to be sure that you do not miss an expenditure. In automatic mode, you can: Define a budget for a year based on the operations of the previous year. Define missing budget items to balance each month and/or the year. Even on manually defined budget. Budget rules To go further in budgeting we will take as example the following balanced budget: -500 € / month for "Food". +500 € / month for all other categories of expenditure and income. Imagine now, that you spent 600 € for "Food" during the previous month! &skrooge; informed you that you are over the defined budget but you cannot stop to eat. So you spent more than expected! How to do with that? Budget rules will allow you to define how you want to do with the delta (positive or negative). For example, you can choose to transfer the delta of one month to the next month. In this case, your new budget for "Food" for the current month is -400 € (-500+100). So you have to take care to recover the situation. Budget rules can also help you for your project. For example, you can define a rule like this: All positive delta (saving) on "Food" are transferred in "Holidays". By this way, when you have the expected amount in "Holidays" budget then you know that you can go. Simulations This plugin allows you to do simulations You can define the rates for each account and estimate the annual interest. You can also generate an amortization table.
How to... How to use &skrooge; in my language? Check if &skrooge; is well translated to your language and if you have installed all packages for &skrooge; provided by your distribution. How to set default currency and date format? Default currency and date format are retrieved from &plasma;'s &systemsettings;. Some distributions do not ship it with &skrooge;. In such case you should install it through your distribution's package manager. You can then start it in a console: systemsettings5 And select your preferred settings, in the Format section. How to reduce the size of my document? The size of your document can be very important. If you delete some old transactions, the size will increase. This is normal because &skrooge; keeps the history of all modifications for the undo/redo mechanism. So if you want to reduce the size of your document, you just have to clear the history. How to define a new invoice extractor? &skrooge; uses pdftotext to extract all strings of a &PDF;. After that, it uses a text file describing how to find key values. If you want to define a new invoice extractor, you have to do that: Launch pdftotext on your &PDF; file Open the text file generated and the corresponding &PDF; file Create a new text with an extension .extractor. Example: google.extractor Your file must be like this: payee=REGEXPCAP:^(Biofan) SPRL$ date=REGEXPCAP:^Order Date: (.*)$ dateformat=dd MMM yyyy number=REGEXPCAP:^N° de facture (.*)$ mode=SET:Carte comment=REGEXPCAP:^N° de commande (.*)$|SET:Commande %1 amount=REGEXP:^Montant global:$|LINEOFFSET:2 Each attribute (payee, date, number, mode, comment and amount) use the same syntax: COMMAND:value|COMMAND:value|... The command can be: REGEXPCAP: This is a regular expression capturing a value REGEXP: To find the line in the file matching a regular expression LINEOFFSET: To change the line index. SET: To force the value. Can be used as first command or after the REGEXPCAP (see example). dateformat is the format of the date extracted. - Put this file into the same directory as all other .extractor files + Put this file into the same folder as all other .extractor files Credits and License &skrooge; Program copyright 2001-2017 Stéphane Mankowski stephane@mankowski.fr Documentation copyright 2001-2017 Guillaume DE BURE gdebure@yahoo.com &underFDL; &underGPL; Search & Process real case study OK, we have to admit that while being quite powerful, the Search & Process plugin is maybe a bit tricky to understand. Here is a small exercise that will give you a hold od it. Remember the use case for Search & Process ?
John downloads some data from his bank. The data comes, obviously, without indication on the category. In fact everything is written in the comment field. John would like some automatic way to correctly set up the category and other attributes based on the content of the comment.
John knows that his bank put the name of the payee in the comment field. And he wants all his payments to &kde; to be in category Donations & Open Source. We will look for all operations with the comment field containing &kde;: Search Definition Comment contains '&kde;'
Add this search definition to the list of existing searches using the Add button. Next we need to tell &skrooge; to set all operations found by this query in category Donations > Open Source. Change the Query type to Update using the drop down box, and define the transformations to apply: Update Definition Comment Category ='' ='Donations > Open Source'
Notice that we also removed the original comment by setting it to an empty string. This is for readability sake, but you may wish to keep it for historical reasons. Add this process to the previously defined search using the Add button. So far, so good. Now, let's apply the process on all imported operations not yet validated (click on the Apply button): That's it ! If you go back to the operations view, you will notice that all concerned operations have been updated. In case anything went wrong, do not forget that Undo is your friend ! Even if the process modified 2327 operations, this is still one undoable action for &skrooge;.
Deep analysis exercise: Holidays expenses To illustrate the analysis capabilities of &skrooge;, we are going to examine this author's expenses during his holidays :). Analysis Context In order to understand the exercise, you need to know a few things about these holidays. They span over nearly a full month during summer 2009, and involved renting several rooms in several locations in France. There was also a fair amount of fuel, as these holidays also spanned over a rather large part of France. Data Organisation The author carefully entered his operations, by affecting them categories, but also by adding them to the "Holidays, summer 2009" tracker. Categories often have subcategories, such as "Transport > Car > Fuel", allowing for this detailed analysis. Finding the holidays total cost This is so easy, it is not even funny: open the Trackers view, read the "Amount" column for our tracker: Let's move to more interesting stuff... Expenses Distribution So this is all very good, but where did all that money go ? To find this out, let us build a report on the tracker. Right click on our tracker line and choose Open report.... This will open a report in a new tab, only for operations associated with the tracker. The default parameters for dates are "Current Month", so there is a possibility that the report is empty if all operations in the tracker were made before this. Let's change it to "All Dates": This gives a first nice overview of the expenses per category. Let's display this in a pie chart, for a more natural presentation: It is now quite clear that most of the holidays expenses were for transport and food. Any more indication ? Sure, let's dig one level more in categories, using the plus icon . Hmm, not much more information here, except that in the "Transport" category, the "Car" subcategory is the only one used. Let's build a graph on that subcategory, by selecting the pie slice, right-clicking on it, and select Open report: This will open another tab with a report only with the "Transport > Car" category. Hmm, not really interesting... But there is more subcategories. Let's expand it again: Ah, now we know that we spent 362.27€ on fuel, and 131.90€ on road taxes (in France, you have to pay to drive on some highways). Ok, I know this was to be expected ;-). I am also interested in knowing how these two categories were used over time: Note that we changed the graph type to "Line", and the columns to "Week". Conclusion Through this simple exercise, we have been able to use the advanced reporting capabilities of &skrooge; to perform a fine analysis of the expenses made during holidays, specifically in the "Transports" category. Of course, you may think of other use cases: &skrooge; is probably able to cope with them. &documentation.index;
diff --git a/plugins/import/skrooge_import_csv/skgimportplugincsv.cpp b/plugins/import/skrooge_import_csv/skgimportplugincsv.cpp index e385c37e0..73048f4c9 100644 --- a/plugins/import/skrooge_import_csv/skgimportplugincsv.cpp +++ b/plugins/import/skrooge_import_csv/skgimportplugincsv.cpp @@ -1,1049 +1,1049 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file is Skrooge plugin for CSV import / export. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgimportplugincsv.h" #include #include #include #include #include #include "skgbankincludes.h" #include "skgimportexportmanager.h" #include "skgservices.h" #include "skgtraces.h" /** * This plugin factory. */ K_PLUGIN_FACTORY(SKGImportPluginCsvFactory, registerPlugin();) SKGImportPluginCsv::SKGImportPluginCsv(QObject* iImporter, const QVariantList& iArg) : SKGImportPlugin(iImporter), m_csvSeparator(QChar()), m_csvHeaderIndex(-1) { SKGTRACEINFUNC(10) Q_UNUSED(iArg) m_importParameters[QStringLiteral("mapping_date")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^date"); m_importParameters[QStringLiteral("mapping_account")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^account"); m_importParameters[QStringLiteral("mapping_number")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^number|^num?ro"); m_importParameters[QStringLiteral("mapping_mode")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^mode|^type"); m_importParameters[QStringLiteral("mapping_payee")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^payee|^tiers"); m_importParameters[QStringLiteral("mapping_comment")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^comment|^libell?|^d?tail|^info"); m_importParameters[QStringLiteral("mapping_status")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^status|^pointage"); m_importParameters[QStringLiteral("mapping_bookmarked")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^bookmarked"); m_importParameters[QStringLiteral("mapping_category")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^cat\\w*gor\\w*"); m_importParameters[QStringLiteral("mapping_amount")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^value|^amount|^valeur|^montant|^credit|^debit"); m_importParameters[QStringLiteral("mapping_quantity")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^quantity"); m_importParameters[QStringLiteral("mapping_unit")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^unit"); m_importParameters[QStringLiteral("mapping_sign")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^sign|^sens"); m_importParameters[QStringLiteral("mapping_debit")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and | and translate the words", "^-|^debit|^withdrawal"); m_importParameters[QStringLiteral("mapping_idgroup")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^idgroup"); m_importParameters[QStringLiteral("mapping_idtransaction")] = i18nc("This is a regular expression to find the column in a csv file. You should keep the ^ and translate the word", "^idtransaction"); m_importParameters[QStringLiteral("mapping_property")] = QString(); m_importParameters[QStringLiteral("automatic_search_header")] = 'Y'; m_importParameters[QStringLiteral("header_position")] = '1'; m_importParameters[QStringLiteral("automatic_search_columns")] = 'Y'; m_importParameters[QStringLiteral("columns_positions")] = QString(); m_importParameters[QStringLiteral("mode_csv_unit")] = 'N'; m_importParameters[QStringLiteral("mode_csv_rule")] = 'N'; m_importParameters[QStringLiteral("date_format")] = QString(); } SKGImportPluginCsv::~SKGImportPluginCsv() = default; void SKGImportPluginCsv::setImportParameters(const QMap< QString, QString >& iParameters) { SKGImportPlugin::setImportParameters(iParameters); if (m_importParameters.value(QStringLiteral("automatic_search_header")) == QStringLiteral("N")) { int header_position = SKGServices::stringToInt(m_importParameters.value(QStringLiteral("header_position"))); setCSVHeaderIndex(header_position); } if (m_importParameters.value(QStringLiteral("automatic_search_columns")) == QStringLiteral("N")) { QStringList columns_positions = m_importParameters.value(QStringLiteral("columns_positions")).split('|'); if (m_csvHeaderIndex == -1) { m_csvHeaderIndex = 1; } setCSVMapping(&columns_positions); } } bool SKGImportPluginCsv::isImportPossible() { SKGTRACEINFUNC(10) return isExportPossible(); } QStringList SKGImportPluginCsv::getCSVMappingFromLine(const QString& iLine) { QStringList output; QString line = iLine.trimmed(); // Split first line QStringList csvAttributes = SKGServices::splitCSVLine(line, getCSVSeparator(iLine), true); int nb = csvAttributes.count(); for (int i = 0; i < nb; ++i) { QString att = csvAttributes.at(i).toLower(); // Search if this csv column is mapping a std attribute QMapIterator csvMapperIterator(m_importParameters); bool found = false; while (!found && csvMapperIterator.hasNext()) { csvMapperIterator.next(); QString key = csvMapperIterator.key(); if (key.startsWith(QLatin1String("mapping_"))) { key = key.right(key.length() - 8); if (key != QStringLiteral("debit") && key != QStringLiteral("property") && !csvMapperIterator.value().isEmpty() && QRegExp(csvMapperIterator.value(), Qt::CaseInsensitive).indexIn(att) != -1 && (!output.contains(key) || key == QStringLiteral("comment") || key == QStringLiteral("category") || key == QStringLiteral("amount"))) { output.push_back(key); found = true; } } } // Search if this csv column must be added as a property if (!found && !m_importParameters.value(QStringLiteral("mapping_property")).isEmpty() && QRegExp(m_importParameters.value(QStringLiteral("mapping_property")), Qt::CaseInsensitive).indexIn(att) != -1 && !output.contains(att)) { output.push_back(att); found = true; } if (!found) { output.push_back(QString()); // To ignore this column } } return output; } SKGError SKGImportPluginCsv::setCSVMapping(const QStringList* iCSVMapping) { SKGError err; SKGTRACEINFUNCRC(10, err) m_csvMapping.clear(); if (iCSVMapping == nullptr) { // Automatic build // Open file QFile file(m_importer->getLocalFileName()); if (Q_UNLIKELY(!file.open(QIODevice::ReadOnly))) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } else { QTextStream stream(&file); if (!m_importer->getCodec().isEmpty()) { stream.setCodec(m_importer->getCodec().toLatin1().constData()); } // Ignore useless lines int headerIndex = getCSVHeaderIndex(); for (int i = 1; i < headerIndex; ++i) { stream.readLine(); } // Get mapping if (!stream.atEnd()) { m_csvMapping = getCSVMappingFromLine(stream.readLine()); } else { err.setReturnCode(ERR_INVALIDARG); } // close file file.close(); } } else { // Manual build m_csvMapping = *iCSVMapping; } IFOK(err) { // Check if mandatory attributes have been found if (m_importParameters.value(QStringLiteral("mode_csv_rule")) == QStringLiteral("Y")) { if (!m_csvMapping.contains(QStringLiteral("payee")) || !m_csvMapping.contains(QStringLiteral("category"))) { err = SKGError(ERR_FAIL, i18nc("Error message", "Columns payee and category not found. Set import parameters in settings (Configure Skrooge... / Import/Export / CSV / Edit regular expressions...).")); } } else { if (!m_csvMapping.contains(QStringLiteral("date")) || !m_csvMapping.contains(QStringLiteral("amount"))) { err = SKGError(ERR_FAIL, i18nc("Error message", "Columns date and amount not found. Set import parameters in settings (Configure Skrooge... / Import/Export / CSV / Edit regular expressions...).")); } } } return err; } SKGError SKGImportPluginCsv::setCSVHeaderIndex(int iIndex) { SKGError err; SKGTRACEINFUNCRC(10, err) if (iIndex == -1) { // Automatic build // Open file QFile file(m_importer->getLocalFileName()); if (Q_UNLIKELY(!file.open(QIODevice::ReadOnly))) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } else { QTextStream stream(&file); if (!m_importer->getCodec().isEmpty()) { stream.setCodec(m_importer->getCodec().toLatin1().constData()); } int i = 1; m_csvHeaderIndex = -1; while (!stream.atEnd() && m_csvHeaderIndex == -1) { // Read line QStringList map = getCSVMappingFromLine(stream.readLine()); if (m_importParameters.value(QStringLiteral("mode_csv_rule")) == QStringLiteral("Y")) { if (map.contains(QStringLiteral("payee")) && map.contains(QStringLiteral("category"))) { m_csvHeaderIndex = i; } } else { if (map.contains(QStringLiteral("date")) && map.contains(QStringLiteral("amount"))) { m_csvHeaderIndex = i; } } ++i; } // close file file.close(); } } else { // Manual build m_csvHeaderIndex = iIndex; } return err; } int SKGImportPluginCsv::getCSVHeaderIndex() { SKGTRACEINFUNC(10) if (m_csvHeaderIndex == -1) { setCSVHeaderIndex(-1); } return m_csvHeaderIndex; } QChar SKGImportPluginCsv::getCSVSeparator(const QString& iLine) { SKGTRACEINFUNC(10) if (m_csvSeparator.isNull()) { QStringList csvAttributes = SKGServices::splitCSVLine(iLine, ';', true, &m_csvSeparator); int nb = csvAttributes.count(); // If the split fails, we try with another separator if (nb == 1) { csvAttributes = SKGServices::splitCSVLine(iLine, ',', true, &m_csvSeparator); nb = csvAttributes.count(); // If the split fails, we try with another separator if (nb == 1) { csvAttributes = SKGServices::splitCSVLine(iLine, '\t', true, &m_csvSeparator); } } } return m_csvSeparator; } SKGError SKGImportPluginCsv::importFile() { if (m_importParameters.value(QStringLiteral("mode_csv_rule")) == QStringLiteral("Y")) { return importCSVRule(); } if (m_importParameters.value(QStringLiteral("mode_csv_unit")) == QStringLiteral("Y")) { return importCSVUnit(); } if (m_importer == nullptr) { return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); } SKGError err; SKGTRACEINFUNCRC(2, err) // Begin transaction err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "CSV"), 3); IFOK(err) { // Initialize some variables QDateTime now = QDateTime::currentDateTime(); QString postFix = SKGServices::dateToSqlString(now); // Default mapping if (m_csvMapping.isEmpty()) { err = setCSVMapping(nullptr); IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Use automatic search of the columns"))) } IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Mapping used: %1", m_csvMapping.join(QStringLiteral("|"))))) // Step 1 done IFOKDO(err, m_importer->getDocument()->stepForward(1)) // Open file IFOK(err) { QFile file(m_importer->getLocalFileName()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } else { QTextStream stream(&file); if (!m_importer->getCodec().isEmpty()) { stream.setCodec(m_importer->getCodec().toLatin1().constData()); } // Ignore useless lines int headerIndex = getCSVHeaderIndex(); if (headerIndex == -1) { err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "Header not found in CSV file")); } for (int i = 1; i <= headerIndex; ++i) { stream.readLine(); } QList amountIndexes; int nb = m_csvMapping.count(); for (int i = 0; i < nb; ++i) { if (m_csvMapping.at(i) == QStringLiteral("amount")) { amountIndexes.push_back(i); } } int nbAmount = amountIndexes.count(); // Get data column QStringList dates; QStringList lines; int posdate = -1; bool modeAutoCreditDebit = (!m_csvMapping.contains(QStringLiteral("sign")) && nbAmount == 2); IFOK(err) { posdate = m_csvMapping.indexOf(QStringLiteral("date")); if (posdate == -1) { posdate = m_csvMapping.indexOf(QStringLiteral("date1")); } if (posdate == -1) { posdate = m_csvMapping.indexOf(QStringLiteral("date2")); } if (posdate != -1) { QString currentLine; while (!stream.atEnd()) { // Read line QString line = stream.readLine().trimmed(); if (!line.isEmpty()) { if (line.startsWith(QLatin1String("\""))) { currentLine.clear(); } currentLine += line; // Get date QStringList field = SKGServices::splitCSVLine(currentLine, getCSVSeparator(currentLine)); if (field.isEmpty()) { // This is a multi line csv filename } else { lines.push_back(currentLine); currentLine.clear(); if (field.count() >= m_csvMapping.count()) { if (posdate < field.count()) { dates.push_back(field.value(posdate).remove(QStringLiteral(" 00:00:00")).trimmed()); } // Check if all amounts are positive if (modeAutoCreditDebit) { for (int i = 0; modeAutoCreditDebit && i < nbAmount; ++i) { QString s = field.at(amountIndexes.at(i)).trimmed(); if (!s.isEmpty() && SKGServices::stringToDouble(s) < 0) { modeAutoCreditDebit = false; } } } } } } } } } // close file file.close(); // Select dateformat QString dateFormat = m_importParameters.value(QStringLiteral("date_format")); if (dateFormat.isEmpty()) { dateFormat = SKGServices::getDateFormat(dates); // Automatic detection } if (!err) { int posdate2 = m_csvMapping.indexOf(QStringLiteral("date2")); if (dateFormat.isEmpty()) { // Check if another "date" attribute exists if (posdate2 != -1) { m_csvMapping[posdate] = QLatin1String(""); err = importFile(); SKGENDTRANSACTION(m_importer->getDocument(), err) return err; } err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "Date format not supported")); } else { // Disable other date columns if (posdate2 != -1 && posdate2 != posdate) { m_csvMapping[posdate2] = QLatin1String(""); } } } IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Import of '%1' with code '%2' and date format '%3'", m_importer->getFileName().toDisplayString(), m_importer->getCodec(), dateFormat))) // Step 2 done IFOKDO(err, m_importer->getDocument()->stepForward(2)) // Treat all lines IFOK(err) { int nb2 = lines.size(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb2); // Save last mapping used in a settings QString mappingDesc; int nbMap = m_csvMapping.count(); for (int i = 0; i < nbMap; ++i) { if (i != 0) { mappingDesc += '|'; } mappingDesc += m_csvMapping.at(i); } IFOKDO(err, m_importer->getDocument()->setParameter(QStringLiteral("SKG_LAST_CSV_MAPPING_USED"), mappingDesc)) SKGUnitObject defUnit; SKGAccountObject defAccount; QMap mapGroup; QMap mapOperation; bool noUnitColumn = (m_csvMapping.indexOf(QStringLiteral("unit")) == -1); bool noAccountColumn = (m_csvMapping.indexOf(QStringLiteral("account")) == -1); bool emptyAccount = false; for (int i = 0; !err && i < nb2; ++i) { QString currentCategory; SKGOperationObject currentOperation(m_importer->getDocument()); SKGSubOperationObject currentSubOperation(m_importer->getDocument()); // Valuate mandatory attribute with default value if (noUnitColumn && i == 0) { err = m_importer->getDefaultUnit(defUnit); } IFOKDO(err, currentOperation.setUnit(defUnit)) if (!err && noAccountColumn) { err = m_importer->getDefaultAccount(defAccount); if (i == 0) { emptyAccount = (defAccount.getNbOperation() == 0); } IFOKDO(err, currentOperation.setParentAccount(defAccount)) } const QString& line = lines.at(i); QString skg_op_original_amount; QStringList atts = SKGServices::splitCSVLine(line, getCSVSeparator(line)); int nbcol = m_csvMapping.count(); if (atts.count() < nbcol) { if (i == nb2 - 1) { - //This is a footer (see 408284) + // This is a footer (see 408284) break; } err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Invalid number of columns in line %1. Expected %2. Found %3.", headerIndex + i + 1, nbcol, atts.count())); } QString linecleaned; for (int z = 0; !err && z < nbcol; ++z) { if (!m_csvMapping[z].isEmpty()) { if (!linecleaned.isEmpty()) { linecleaned += getCSVSeparator(); } linecleaned += atts[z]; } } QByteArray hash = QCryptographicHash::hash(linecleaned.toUtf8(), QCryptographicHash::Md5); int initialBalance = 0; int idgroup = 0; int idtransaction = 0; QStringList propertiesAtt; QStringList propertiesVal; double sign = (modeAutoCreditDebit ? -1.0 : 1.0); bool amountSet = false; for (int c = 0; !err && c < nbcol; ++c) { QString col = m_csvMapping[c]; if (!col.isEmpty()) { QString val; if (c >= 0 && c < atts.count()) { val = atts.at(c).trimmed(); } if (col == QStringLiteral("date") || col == QStringLiteral("date1") || col == QStringLiteral("date2")) { QDate d = SKGServices::stringToTime(SKGServices::dateToSqlString(val.remove(QStringLiteral(" 00:00:00")), dateFormat)).date(); err = currentOperation.setDate(d); IFOKDO(err, currentSubOperation.setDate(d)) if (val == QStringLiteral("0000-00-00")) { initialBalance = 1; } } else if (col == QStringLiteral("number")) { if (!val.isEmpty()) { err = currentOperation.setNumber(val); } } else if (col == QStringLiteral("mode")) { if (val == QStringLiteral("1")) { val = i18nc("An operation mode", "Transfer"); } else if (val == QStringLiteral("2")) { val = i18nc("An operation mode", "Direct debit"); } else if (val == QStringLiteral("3")) { val = i18nc("An operation mode", "Check"); } else if (val == QStringLiteral("4")) { val = i18nc("An operation mode", "Deposit"); } else if (val == QStringLiteral("5")) { val = i18nc("An operation mode", "Payback"); } else if (val == QStringLiteral("6")) { val = i18nc("An operation mode", "Withdrawal"); } else if (val == QStringLiteral("7")) { val = i18nc("An operation mode", "Card"); } else if (val == QStringLiteral("8")) { val = i18nc("An operation mode", "Loan payment"); } else if (val == QStringLiteral("9")) { val = i18nc("An operation mode", "Subscription"); } else if (val == QStringLiteral("0")) { val = QString(); } else if (val == QStringLiteral("10")) { val = i18nc("An operation mode", "Cash deposit"); } else if (val == QStringLiteral("11")) { val = i18nc("An operation mode", "Card summary"); } else if (val == QStringLiteral("12")) { val = i18nc("An operation mode", "Deferred card"); } err = currentOperation.setMode(val); } else if (col == QStringLiteral("payee")) { SKGPayeeObject payeeObj; err = SKGPayeeObject::createPayee(m_importer->getDocument(), val, payeeObj); IFOKDO(err, currentOperation.setPayee(payeeObj)) } else if (col == QStringLiteral("comment")) { QString comment = currentOperation.getComment(); if (!comment.isEmpty()) { comment += ' '; } comment += val; err = currentOperation.setComment(comment); IFOKDO(err, currentSubOperation.setComment(comment)) } else if (col == QStringLiteral("status")) { err = currentOperation.setStatus(val == QStringLiteral("C") || val == QStringLiteral("Y") ? SKGOperationObject::CHECKED : val == QStringLiteral("P") ? SKGOperationObject::POINTED : SKGOperationObject::NONE); } else if (col == QStringLiteral("bookmarked")) { err = currentOperation.bookmark(val == QStringLiteral("Y")); } else if (col == QStringLiteral("idgroup")) { idgroup = SKGServices::stringToInt(val); } else if (col == QStringLiteral("idtransaction")) { idtransaction = SKGServices::stringToInt(val); } else if (col == QStringLiteral("amount")) { if (!val.isEmpty() && SKGServices::stringToDouble(val) != 0.0 && !amountSet) { amountSet = true; if (m_csvMapping.contains(QStringLiteral("quantity"))) { // 209705 vvvv skg_op_original_amount = val; // 209705 ^^^^ } else { err = currentSubOperation.setQuantity(sign * SKGServices::stringToDouble(val)); } } else if (modeAutoCreditDebit) { sign = 1.0; // Next one will be considered as a credit } } else if (col == QStringLiteral("quantity")) { err = currentSubOperation.setQuantity(SKGServices::stringToDouble(val)); } else if (col == QStringLiteral("sign")) { if (QRegExp(m_importParameters.value(QStringLiteral("mapping_debit")), Qt::CaseInsensitive).indexIn(val) != -1) { sign = -1; double cval = currentSubOperation.getQuantity(); if (cval > 0) { err = currentSubOperation.setQuantity(-cval); } } } else if (col == QStringLiteral("unit")) { // Looking for unit SKGUnitObject unit(m_importer->getDocument()); if (val != defUnit.getName()) { // For performance err = unit.setName(val); IFOKDO(err, unit.setSymbol(val)) if (!err && unit.load().isFailed()) { err = unit.save(false); // Save only } // This unit is now the default one, it is better for performance defUnit = unit; } else { unit = defUnit; } SKGUnitValueObject unitval; IFOKDO(err, unit.addUnitValue(unitval)) IFOK(err) { int posAmount = m_csvMapping.indexOf(QStringLiteral("amount")); int posQuantity = m_csvMapping.indexOf(QStringLiteral("quantity")); if (posAmount != -1 && posQuantity != -1) { err = unitval.setQuantity(SKGServices::stringToDouble(atts.at(posAmount)) / SKGServices::stringToDouble(atts.at(posQuantity))); } else { err = unitval.setQuantity(1); } } IFOKDO(err, unitval.setDate(now.date())) IFOKDO(err, unitval.save()) IFOKDO(err, currentOperation.setUnit(unit)) } else if (col == QStringLiteral("account")) { // Looking for account if (val.isEmpty()) { err = m_importer->getDefaultAccount(defAccount); if (i == 0) { emptyAccount = (defAccount.getNbOperation() == 0); } - } else if (val != defAccount.getName()) { // For performance + } else if (val != defAccount.getName()) { // For performance SKGAccountObject account(m_importer->getDocument()); account.setName(val); err = account.load(); IFKO(err) { // Not found, we have to create one SKGBankObject bank(m_importer->getDocument()); QString name = i18nc("Noun", "Bank for import %1", postFix); err = bank.setName(name); if (!err && bank.load().isFailed()) { err = bank.save(false); // Save only IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Default bank '%1' created for import", name))) } IFOKDO(err, bank.addAccount(account)) IFOKDO(err, account.setName(val)) if (!err && account.load().isFailed()) { err = account.save(false); // Save only } } // This account is now the default one, it is better for performance defAccount = account; } IFOKDO(err, currentOperation.setParentAccount(defAccount)) } else if (col == QStringLiteral("category")) { // Set Category if (!val.isEmpty()) { // Prepare val val.replace('/', OBJECTSEPARATOR); val.replace(':', OBJECTSEPARATOR); val.replace(',', OBJECTSEPARATOR); val.replace(';', OBJECTSEPARATOR); // Get previous category if (!currentCategory.isEmpty()) { val = currentCategory % OBJECTSEPARATOR % val; } currentCategory = val; // Create and set category SKGCategoryObject Category; err = SKGCategoryObject::createPathCategory(m_importer->getDocument(), val, Category); IFOKDO(err, currentSubOperation.setCategory(Category)) IFOK(err) { if (m_csvMapping.indexOf(QStringLiteral("payee")) == -1) { SKGPayeeObject payeeObj; QString p = m_importer->getDocument()->getCategoryForPayee(val, false); err = SKGPayeeObject::createPayee(m_importer->getDocument(), p, payeeObj); IFOKDO(err, currentOperation.setPayee(payeeObj)) } } } } else { // A property propertiesAtt.push_back(col); propertiesVal.push_back(val); } } } if (!err && (initialBalance != 0)) { // Specific values for initial balance err = currentOperation.setStatus(SKGOperationObject::CHECKED); IFOKDO(err, currentOperation.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00"))) IFOKDO(err, currentSubOperation.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00"))) } IFOKDO(err, currentOperation.setImportID(hash.toHex())) IFOK(err) { if (idtransaction != 0) { if (mapOperation.contains(idtransaction)) { currentOperation = mapOperation[idtransaction]; skg_op_original_amount = QString(); } else { err = currentOperation.save(); mapOperation[idtransaction] = currentOperation; } } else { err = currentOperation.save(false); // Save only } } if (!err && idgroup != 0) { if (mapGroup.contains(idgroup)) { err = currentOperation.setGroupOperation(mapGroup[idgroup]); IFOKDO(err, currentOperation.save()) } mapGroup[idgroup] = currentOperation; } IFOKDO(err, currentSubOperation.setParentOperation(currentOperation)) IFOKDO(err, currentSubOperation.save(false, false)) // 209705 vvvv if (!err && !skg_op_original_amount.isEmpty()) { err = currentOperation.setProperty(QStringLiteral("SKG_OP_ORIGINAL_AMOUNT"), skg_op_original_amount); } // 209705 ^^^^ // Add properties int nbp = propertiesAtt.count(); for (int p = 0; !err && p < nbp; ++p) { err = currentOperation.setProperty(propertiesAtt.at(p), propertiesVal.at(p)); } if (!err && i % 500 == 0) { err = m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")); } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } IFOK(err) { QString balance = m_importParameters.value(QStringLiteral("balance")); if (!balance.isEmpty()) { if (emptyAccount) { // Current amount double currentAmount = defAccount.getAmount(QDate::currentDate()); // Update account IFOKDO(err, defAccount.setInitialBalance(SKGServices::stringToDouble(balance) - currentAmount, defUnit)) IFOKDO(err, defAccount.save()) IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has been set", defAccount.getName()), SKGDocument::Warning)) } else { m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has not been set because some operations are already existing", defAccount.getName())); } } } SKGENDTRANSACTION(m_importer->getDocument(), err) // Lines treated IFOKDO(err, m_importer->getDocument()->stepForward(3)) } } } } SKGENDTRANSACTION(m_importer->getDocument(), err) return err; } bool SKGImportPluginCsv::isExportPossible() { SKGTRACEINFUNC(10) return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("CSV")); } SKGError SKGImportPluginCsv::exportFile() { if (m_importer == nullptr) { return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); } SKGError err; SKGTRACEINFUNCRC(2, err) auto listUUIDs = SKGServices::splitCSVLine(m_exportParameters.value(QStringLiteral("uuid_of_selected_accounts_or_operations"))); QString wc; for (const auto& uuid : listUUIDs) { auto items = SKGServices::splitCSVLine(uuid, '-'); if (items.at(1) == QStringLiteral("operation")) { if (!wc.isEmpty()) { wc += QLatin1String(" OR "); } wc += " i_OPID=" + items.at(0); } else if (items.at(1) == QStringLiteral("account")) { if (!wc.isEmpty()) { wc += QLatin1String(" OR "); } wc += " rd_account_id=" + items.at(0); } } if (wc.isEmpty()) { wc = QStringLiteral("1=1"); } else { IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Only selected accounts and operations have been exported"))) } // Open file QSaveFile file(m_importer->getLocalFileName(false)); if (!file.open(QIODevice::WriteOnly)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", m_importer->getFileName().toDisplayString())); } else { QTextStream out(&file); if (!m_importer->getCodec().isEmpty()) { out.setCodec(m_importer->getCodec().toLatin1().constData()); } err = m_importer->getDocument()->dumpSelectSqliteOrder( QStringLiteral("SELECT v.d_date as date, v.t_BANK as bank, v.t_ACCOUNT as account, v.t_number as number, v.t_mode as mode, " "v.t_PAYEE as payee, v.t_REALCOMMENT as comment, PRINTF('%.'||u.i_nbdecimal||'f', v.f_REALQUANTITY) as quantity, " "v.t_UNIT as unit, PRINTF('%.'||u.i_nbdecimal||'f', v.f_REALCURRENTAMOUNT) as amount, v.t_TYPEEXPENSE as sign, v.t_REALCATEGORY as category, v.t_status as status, " "v.t_REALREFUND as tracker, v.t_bookmarked as bookmarked, v.i_SUBOPID id, v.i_OPID idtransaction, v.i_group_id idgroup " "FROM v_suboperation_consolidated as v, unit as u WHERE v.rc_unit_id=u.id AND (") % wc % ") ORDER BY v.d_date, v.i_OPID, v.i_SUBOPID", &out, SKGServices::DUMP_CSV); // Close file file.commit(); } return err; } QString SKGImportPluginCsv::getMimeTypeFilter() const { return "*.csv|" % i18nc("A file format", "CSV file"); } SKGError SKGImportPluginCsv::importCSVUnit() { SKGError err; SKGTRACEINFUNCRC(2, err) if (m_importer->getDocument() != nullptr) { // Begin transaction err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), 3); IFOK(err) { // File name is the name of the unit QFileInfo fInfo(m_importer->getFileName().path()); QString unitName = fInfo.baseName(); // Default mapping if (m_csvMapping.isEmpty()) { err = setCSVMapping(nullptr); IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Use automatic search of the columns"))) } // Step 1 done IFOKDO(err, m_importer->getDocument()->stepForward(1)) // Open file IFOK(err) { QFile file(m_importer->getLocalFileName()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } else { QTextStream stream(&file); if (!m_importer->getCodec().isEmpty()) { stream.setCodec(m_importer->getCodec().toLatin1().constData()); } // Ignore useless lines int headerIndex = getCSVHeaderIndex(); for (int i = 1; i <= headerIndex; ++i) { stream.readLine(); } // Get data column QStringList dates; QStringList lines; int posdate = m_csvMapping.indexOf(QStringLiteral("date")); if (posdate != -1) { while (!stream.atEnd()) { // Read line QString line = stream.readLine().trimmed(); if (!line.isEmpty()) { lines.push_back(line); // Get date QStringList field = SKGServices::splitCSVLine(line, getCSVSeparator(line)); if (posdate < field.count()) { dates.push_back(field.at(posdate)); } } } } // close file file.close(); // Select dateformat QString dateFormat = SKGServices::getDateFormat(dates); if (dateFormat.isEmpty()) { err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "Date format not supported")); } IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Import of '%1' with codec '%2' and date format '%3'", m_importer->getFileName().toDisplayString(), m_importer->getCodec(), dateFormat))) // Step 2 done IFOKDO(err, m_importer->getDocument()->stepForward(2)) // Treat all lines IFOK(err) { int nb = lines.size(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), nb); // Save last mapping used in a settings QString mappingDesc; int nbMap = m_csvMapping.count(); for (int i = 0; i < nbMap; ++i) { if (i != 0) { mappingDesc += '|'; } mappingDesc += m_csvMapping.at(i); } IFOKDO(err, m_importer->getDocument()->setParameter(QStringLiteral("SKG_LAST_CSV_UNIT_MAPPING_USED"), mappingDesc)) int posdate2 = m_csvMapping.indexOf(QStringLiteral("date")); int posvalue = m_csvMapping.indexOf(QStringLiteral("amount")); if (posdate2 != -1 && posvalue != -1) { for (int i = 0; !err && i < nb; ++i) { QStringList atts = SKGServices::splitCSVLine(lines.at(i), getCSVSeparator(lines.at(i))); err = m_importer->getDocument()->addOrModifyUnitValue(unitName, SKGServices::stringToTime(SKGServices::dateToSqlString(atts.at(posdate2), dateFormat)).date(), SKGServices::stringToDouble(atts.at(posvalue))); IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } } SKGENDTRANSACTION(m_importer->getDocument(), err) // Lines treated IFOKDO(err, m_importer->getDocument()->stepForward(3)) } } } } SKGENDTRANSACTION(m_importer->getDocument(), err) } return err; } SKGError SKGImportPluginCsv::importCSVRule() { SKGError err; SKGTRACEINFUNCRC(2, err) if (m_importer->getDocument() != nullptr) { // Begin transaction err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import rules"), 3); IFOK(err) { // Default mapping if (m_csvMapping.isEmpty()) { err = setCSVMapping(nullptr); IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Use automatic search of the columns"))) } // Step 1 done IFOKDO(err, m_importer->getDocument()->stepForward(1)) // Open file IFOK(err) { QFile file(m_importer->getLocalFileName()); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } else { QTextStream stream(&file); if (!m_importer->getCodec().isEmpty()) { stream.setCodec(m_importer->getCodec().toLatin1().constData()); } // Ignore useless lines int headerIndex = getCSVHeaderIndex(); for (int i = 1; i <= headerIndex; ++i) { stream.readLine(); } // Get data column QStringList lines; while (!stream.atEnd()) { // Read line QString line = stream.readLine().trimmed(); if (!line.isEmpty()) { lines.push_back(line); } } // close file file.close(); IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Import of '%1' with codec '%2'", m_importer->getFileName().toDisplayString(), m_importer->getCodec()))) // Step 2 done IFOKDO(err, m_importer->getDocument()->stepForward(2)) // Treat all lines IFOK(err) { int nb = lines.size(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import rules"), nb); // Save last mapping used in a settings QString mappingDesc; int nbMap = m_csvMapping.count(); for (int i = 0; i < nbMap; ++i) { if (i != 0) { mappingDesc += '|'; } mappingDesc += m_csvMapping.at(i); } IFOKDO(err, m_importer->getDocument()->setParameter(QStringLiteral("SKG_LAST_CSV_RULE_MAPPING_USED"), mappingDesc)) int pospayee = m_csvMapping.indexOf(QStringLiteral("payee")); int poscategory = m_csvMapping.indexOf(QStringLiteral("category")); if (pospayee != -1 && poscategory != -1) { for (int i = 0; !err && i < nb; ++i) { QStringList atts = SKGServices::splitCSVLine(lines.at(i), getCSVSeparator(lines.at(i))); SKGRuleObject oRule; err = SKGRuleObject::createPayeeCategoryRule(m_importer->getDocument(), atts.at(pospayee), atts.at(poscategory), oRule); IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } } SKGENDTRANSACTION(m_importer->getDocument(), err) // Lines treated IFOKDO(err, m_importer->getDocument()->stepForward(3)) } } } } SKGENDTRANSACTION(m_importer->getDocument(), err) } return err; } #include diff --git a/plugins/import/skrooge_import_ofx/skgimportpluginofx.cpp b/plugins/import/skrooge_import_ofx/skgimportpluginofx.cpp index 4f6ac4a80..ff9eadb73 100644 --- a/plugins/import/skrooge_import_ofx/skgimportpluginofx.cpp +++ b/plugins/import/skrooge_import_ofx/skgimportpluginofx.cpp @@ -1,549 +1,549 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file is Skrooge plugin for OFX import / export. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgimportpluginofx.h" #include #include #include #include #include "skgbankincludes.h" #include "skgimportexportmanager.h" #include "skgservices.h" #include "skgtraces.h" SKGError SKGImportPluginOfx::m_ofxError; QStringList SKGImportPluginOfx::m_ofxInitialBalanceName; QList SKGImportPluginOfx::m_ofxInitialBalanceAmount; QList SKGImportPluginOfx::m_ofxInitialBalanceDate; QMap SKGImportPluginOfx::m_accounts; /** * This plugin factory. */ K_PLUGIN_FACTORY(SKGImportPluginOfxFactory, registerPlugin();) SKGImportPluginOfx::SKGImportPluginOfx(QObject* iImporter, const QVariantList& iArg) : SKGImportPlugin(iImporter) { SKGTRACEINFUNC(10) Q_UNUSED(iArg) SKGImportPluginOfx::m_accounts.clear(); } SKGImportPluginOfx::~SKGImportPluginOfx() = default; bool SKGImportPluginOfx::isImportPossible() { SKGTRACEINFUNC(10) return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("OFX") || m_importer->getFileNameExtension() == QStringLiteral("QFX")); } SKGError SKGImportPluginOfx::importFile() { if (m_importer == nullptr) { return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); } SKGError err; SKGTRACEINFUNCRC(2, err) if (!QFile(m_importer->getLocalFileName()).exists()) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "OFX"))) IFOK(err) { SKGImportPluginOfx::m_ofxError = SKGError(); SKGImportPluginOfx::m_ofxInitialBalanceName.clear(); SKGImportPluginOfx::m_ofxInitialBalanceAmount.clear(); SKGImportPluginOfx::m_ofxInitialBalanceDate.clear(); try { // Check file type - //auto type = libofx_detect_file_type( m_importer->getLocalFileName().toUtf8().data()); + // auto type = libofx_detect_file_type( m_importer->getLocalFileName().toUtf8().data()); LibofxContextPtr ctx = libofx_get_new_context(); ofx_set_account_cb(ctx, SKGImportPluginOfx::ofxAccountCallback, m_importer); ofx_set_statement_cb(ctx, SKGImportPluginOfx::ofxStatementCallback, m_importer); ofx_set_transaction_cb(ctx, SKGImportPluginOfx::ofxTransactionCallback, m_importer); // ofx_set_security_cb(ctx, ofxSecurityCallback, this); int rc = libofx_proc_file(ctx, m_importer->getLocalFileName().toUtf8().data(), AUTODETECT); if (rc != 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "Import OFX file '%1' failed", m_importer->getFileName().toDisplayString())); } IFOKDO(err, SKGImportPluginOfx::m_ofxError) { // This is an option ==> no error management SKGError err2; int nb = SKGImportPluginOfx::m_ofxInitialBalanceName.count(); for (int i = 0; !err2 && i < nb; ++i) { SKGAccountObject act; err2 = m_importer->getDocument()->getObject(QStringLiteral("v_account"), "t_number='" % SKGServices::stringToSqlString(m_ofxInitialBalanceName.at(i)) % '\'', act); // Date of balance auto date = m_ofxInitialBalanceDate.at(i); // Get unit SKGUnitObject unit; if (!err2) { err2 = m_importer->getDefaultUnit(unit); } // Current amount double currentAmount = act.getAmount(date); // Update account if (!err2) { err2 = act.setInitialBalance(m_ofxInitialBalanceAmount.at(i) - currentAmount, unit); } if (!err2) { err2 = act.save(); } if (!err2) { err2 = m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has been set", act.getName())); } } } libofx_free_context(ctx); } catch (...) { // NOLINT(whitespace/parens) err = SKGError(ERR_HANDLE, i18nc("Error message", "OFX import failed")); } SKGImportPluginOfx::m_ofxInitialBalanceName.clear(); SKGImportPluginOfx::m_ofxInitialBalanceAmount.clear(); SKGImportPluginOfx::m_ofxInitialBalanceDate.clear(); SKGENDTRANSACTION(m_importer->getDocument(), err) } return err; } QString SKGImportPluginOfx::getAccountName(OfxAccountData* iAccountData) { SKGTRACEINFUNC(3) QString accountNumber; if (iAccountData != nullptr) { accountNumber = QString::fromUtf8(iAccountData->account_id); QString bankNumber = QString::fromUtf8(iAccountData->bank_id); // Correction BUG 234771 vvvvv accountNumber = accountNumber.trimmed(); bankNumber = bankNumber.trimmed(); if (accountNumber.isEmpty()) { accountNumber = QString::fromUtf8(iAccountData->account_number); } // Correction BUG 234771 ^^^^^ if (accountNumber.startsWith(bankNumber % ' ')) { accountNumber = accountNumber.right(accountNumber.length() - bankNumber.length() - 1); QStringList splitNumbers = accountNumber.split(' '); if (splitNumbers.count() == 2) { accountNumber = splitNumbers.at(1); } } } SKGTRACEL(3) << "accountNumber=" << accountNumber << endl; return accountNumber; } SKGError SKGImportPluginOfx::getAccount(OfxAccountData* iAccountData, SKGDocumentBank* iDoc, SKGAccountObject& oAccount) { SKGError err; SKGTRACEINFUNCRC(3, err) if ((iAccountData != nullptr) && (iDoc != nullptr)) { // Check if account is already existing QString name = getAccountName(iAccountData); if (m_accounts.contains(name)) { SKGTRACEL(3) << "Found in index" << endl; oAccount = m_accounts[name]; } else { SKGTRACEL(3) << "NOT found in index" << endl; err = iDoc->getObject(QStringLiteral("v_account"), "t_number='" % SKGServices::stringToSqlString(name) % '\'', oAccount); } } return err; } int SKGImportPluginOfx::ofxStatementCallback(struct OfxStatementData data, void* pv) // clazy:exclude=function-args-by-ref { if (SKGImportPluginOfx::m_ofxError) { return 0; } SKGTRACEINFUNCRC(5, SKGImportPluginOfx::m_ofxError) auto* impotExporter = static_cast(pv); if (impotExporter == nullptr) { return 0; } SKGDocumentBank* doc = impotExporter->getDocument(); if (doc == nullptr) { return 0; } // // Get data OfxAccountData* accountData = data.account_ptr; if ((accountData != nullptr) && static_cast(data.ledger_balance_valid)) { // Get account SKGAccountObject act; SKGImportPluginOfx::m_ofxError = getAccount(accountData, doc, act); if (!SKGImportPluginOfx::m_ofxError) { if (act.getNbOperation() > 1) { SKGImportPluginOfx::m_ofxError = doc->sendMessage(i18nc("An information message", "The initial balance of '%1' has not been set because some operations are already existing", act.getName())); } else { m_ofxInitialBalanceName.push_back(getAccountName(accountData)); m_ofxInitialBalanceDate.push_back(static_cast(data.ledger_balance_date_valid) ? QDateTime::fromTime_t(data.ledger_balance_date).date() : QDate::currentDate()); m_ofxInitialBalanceAmount.push_back(data.ledger_balance); } } } return SKGImportPluginOfx::m_ofxError.getReturnCode(); } int SKGImportPluginOfx::ofxAccountCallback(struct OfxAccountData data, void* pv) { if (SKGImportPluginOfx::m_ofxError) { return 0; } SKGTRACEINFUNCRC(5, SKGImportPluginOfx::m_ofxError) auto* impotExporter = static_cast(pv); if (impotExporter == nullptr) { return 0; } SKGDocumentBank* doc = impotExporter->getDocument(); if (doc == nullptr) { return 0; } SKGObjectBase tmp; QString agencyNumber = QString::fromUtf8(data.branch_id); QString accountNumber = QString::fromUtf8(data.account_id); QString bankNumber = QString::fromUtf8(data.bank_id); // Correction BUG 234771 vvvvv accountNumber = accountNumber.trimmed(); bankNumber = bankNumber.trimmed(); // Correction BUG 234771 ^^^^^ if (accountNumber.isEmpty()) { accountNumber = QString::fromUtf8(data.account_number); } if (accountNumber.startsWith(bankNumber % ' ')) { accountNumber = accountNumber.right(accountNumber.length() - bankNumber.length() - 1); QStringList splitNumbers = accountNumber.split(' '); if (splitNumbers.count() == 2) { agencyNumber = splitNumbers.at(0); accountNumber = splitNumbers.at(1); } } // Check if account is already existing SKGAccountObject account; SKGImportPluginOfx::m_ofxError = getAccount(&data, doc, account); if (!SKGImportPluginOfx::m_ofxError) { // Already existing account = tmp; SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultAccount(&account); } else { // Not existing QString bankId = (static_cast(data.bank_id_valid) ? QString::fromUtf8(data.bank_id) : QString::fromUtf8(data.broker_id)); if (!bankId.isEmpty()) { bankId = i18nc("Adjective, an unknown item", "Unknown"); } // Check if bank is already existing SKGBankObject bank; SKGImportPluginOfx::m_ofxError = doc->getObject(QStringLiteral("v_bank"), "t_bank_number='" % SKGServices::stringToSqlString(bankId) % '\'', tmp); if (!SKGImportPluginOfx::m_ofxError) { // Already existing bank = tmp; } else { // Create new bank bank = SKGBankObject(doc); SKGImportPluginOfx::m_ofxError = bank.setName(bankId); if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = bank.setNumber(QString::fromUtf8(data.bank_id)); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = bank.save(); } } // Create new account QString name = QString::fromUtf8(data.account_name); if (name.isEmpty()) { name = QString::fromUtf8(data.account_id); } else { name = name.remove(QStringLiteral("Bank account ")); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = bank.addAccount(account); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = account.setName(name); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = account.setNumber(accountNumber); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = account.setAgencyNumber(agencyNumber); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = account.setComment(QString::fromUtf8(data.account_name)); } SKGAccountObject::AccountType type = SKGAccountObject::CURRENT; if (static_cast(data.account_type_valid)) { switch (data.account_type) { case OfxAccountData::OFX_CHECKING: case OfxAccountData::OFX_SAVINGS: case OfxAccountData::OFX_CREDITLINE: case OfxAccountData::OFX_CMA: type = SKGAccountObject::CURRENT; break; case OfxAccountData::OFX_MONEYMRKT: case OfxAccountData::OFX_INVESTMENT: type = SKGAccountObject::INVESTMENT; break; case OfxAccountData::OFX_CREDITCARD: type = SKGAccountObject::CREDITCARD; break; default: break; } } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = account.setType(type); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = account.save(); m_accounts[accountNumber] = account; SKGTRACEL(3) << "Add account '" << accountNumber << "' in index" << endl; } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultAccount(&account); } SKGTRACEL(10) << "Account [" << name << "] created" << endl; } if (static_cast(data.currency_valid)) { // Check if unit is already existing SKGUnitObject unit(doc); SKGImportPluginOfx::m_ofxError = doc->getObject(QStringLiteral("v_unit"), "t_name='" % SKGServices::stringToSqlString(QString::fromUtf8(data.currency)) % "' OR t_name like '%(" % SKGServices::stringToSqlString(QString::fromUtf8(data.currency)) % ")%'", tmp); if (!SKGImportPluginOfx::m_ofxError) { // Already existing unit = tmp; SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultUnit(&unit); } else { // Create new account SKGImportPluginOfx::m_ofxError = unit.setName(QString::fromUtf8(data.currency)); if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = unit.setSymbol(QString::fromUtf8(data.currency)); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = unit.save(); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = impotExporter->setDefaultUnit(&unit); } } } return SKGImportPluginOfx::m_ofxError.getReturnCode(); } int SKGImportPluginOfx::ofxTransactionCallback(struct OfxTransactionData data, void* pv) // clazy:exclude=function-args-by-ref { if (SKGImportPluginOfx::m_ofxError) { return 0; } SKGTRACEINFUNCRC(5, SKGImportPluginOfx::m_ofxError) auto* impotExporter = static_cast(pv); if (impotExporter == nullptr) { return 0; } SKGDocumentBank* doc = impotExporter->getDocument(); if (doc == nullptr) { return 0; } // Get account SKGAccountObject account; SKGImportPluginOfx::m_ofxError = getAccount(data.account_ptr, doc, account); if (!SKGImportPluginOfx::m_ofxError) { // Get operation date QDate date = QDateTime::fromTime_t(static_cast(data.date_initiated_valid) ? data.date_initiated : data.date_posted).date(); // Get unit SKGUnitObject unit; SKGImportPluginOfx::m_ofxError = impotExporter->getDefaultUnit(unit); // Create id QString ID; if (static_cast(data.fi_id_valid)) { if (QString::fromUtf8(data.fi_id).count('0') != QString::fromUtf8(data.fi_id).count()) { ID = QStringLiteral("ID-") % QString::fromUtf8(data.fi_id); } } else if (static_cast(data.reference_number_valid)) { if (QString::fromUtf8(data.reference_number).count('0') != QString::fromUtf8(data.reference_number).count()) { ID = QStringLiteral("REF-") % data.reference_number; } } if (ID.isEmpty()) { QByteArray hash = QCryptographicHash::hash(QString(SKGServices::dateToSqlString(date) % SKGServices::doubleToString(data.amount)).toUtf8(), QCryptographicHash::Md5); ID = QStringLiteral("ID-") % hash.toHex(); } // Create operation SKGOperationObject ope; if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = account.addOperation(ope, true); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.setDate(date); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.setUnit(unit); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.setAttribute(QStringLiteral("t_imported"), QStringLiteral("T")); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.setImportID(ID); } if (!SKGImportPluginOfx::m_ofxError) { QString payeeName; if (static_cast(data.payee_id_valid)) { payeeName = QString::fromUtf8(data.payee_id); } else if (static_cast(data.name_valid)) { payeeName = QString::fromUtf8(data.name); } if (!payeeName.isEmpty()) { SKGPayeeObject payeeObj; SKGImportPluginOfx::m_ofxError = SKGPayeeObject::createPayee(doc, payeeName, payeeObj); if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.setPayee(payeeObj); } } } if (!SKGImportPluginOfx::m_ofxError && static_cast(data.memo_valid)) { SKGImportPluginOfx::m_ofxError = ope.setComment(QString::fromUtf8(data.memo)); } if (!SKGImportPluginOfx::m_ofxError && static_cast(data.check_number_valid)) { SKGImportPluginOfx::m_ofxError = ope.setNumber(data.check_number); } if (!SKGImportPluginOfx::m_ofxError && static_cast(data.invtransactiontype_valid)) { SKGImportPluginOfx::m_ofxError = ope.setMode(i18nc("Noun, the title of an item", "Title")); } if (!SKGImportPluginOfx::m_ofxError && static_cast(data.transactiontype_valid)) { QString mode; if (data.transactiontype == OFX_CREDIT) { mode = i18nc("Noun, type of OFX transaction", "Credit"); } else if (data.transactiontype == OFX_DEBIT) { mode = i18nc("Noun, type of OFX transaction", "Debit"); } else if (data.transactiontype == OFX_INT) { mode = i18nc("Noun, type of OFX transaction", "Interest"); } else if (data.transactiontype == OFX_DIV) { mode = i18nc("Noun, type of OFX transaction", "Dividend"); } else if (data.transactiontype == OFX_FEE) { mode = i18nc("Noun, type of OFX transaction", "FI fee"); } else if (data.transactiontype == OFX_SRVCHG) { mode = i18nc("Noun, type of OFX transaction", "Service charge"); } else if (data.transactiontype == OFX_DEP) { mode = i18nc("Noun, type of OFX transaction", "Deposit"); } else if (data.transactiontype == OFX_ATM) { mode = i18nc("Noun, type of OFX transaction", "ATM"); } else if (data.transactiontype == OFX_POS) { mode = i18nc("Noun, type of OFX transaction", "Point of sale"); } else if (data.transactiontype == OFX_XFER) { mode = i18nc("An operation mode", "Transfer"); } else if (data.transactiontype == OFX_CHECK) { mode = i18nc("An operation mode", "Check"); } else if (data.transactiontype == OFX_PAYMENT) { mode = i18nc("Noun, type of OFX transaction", "Electronic payment"); } else if (data.transactiontype == OFX_CASH) { mode = i18nc("An operation mode", "Withdrawal"); } else if (data.transactiontype == OFX_DIRECTDEP) { mode = i18nc("Noun, type of OFX transaction", "Deposit"); } else if (data.transactiontype == OFX_REPEATPMT) { mode = i18nc("Noun, type of OFX transaction", "Repeating payment"); } else if (data.transactiontype == OFX_DIRECTDEBIT) { mode = i18nc("An operation mode", "Direct debit"); } SKGImportPluginOfx::m_ofxError = ope.setMode(mode); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.save(); } // Create sub operation SKGSubOperationObject subop; if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.addSubOperation(subop); } if (!SKGImportPluginOfx::m_ofxError && static_cast(data.amount_valid)) { bool mustFlipSign = (data.transactiontype == OFX_DEBIT || data.transactiontype == OFX_FEE || data.transactiontype == OFX_SRVCHG || data.transactiontype == OFX_PAYMENT || data.transactiontype == OFX_CASH || data.transactiontype == OFX_DIRECTDEBIT || data.transactiontype == OFX_REPEATPMT) && data.amount > 0; // OFX spec version 2.1.1, section 3.2.9.2 double sign = mustFlipSign ? -1.0 : 1.0; double commission = static_cast(data.commission_valid) ? data.commission : 0; SKGImportPluginOfx::m_ofxError = subop.setQuantity(sign * (data.amount + commission)); } if (!SKGImportPluginOfx::m_ofxError && static_cast(data.memo_valid)) { SKGImportPluginOfx::m_ofxError = subop.setComment(QString::fromUtf8(data.memo)); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = subop.save(); } // Commission if (!SKGImportPluginOfx::m_ofxError && static_cast(data.commission_valid) && static_cast(data.amount_valid) && data.commission > 0) { // Create splitter operation SKGSubOperationObject subop2; if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = ope.addSubOperation(subop2); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = subop2.setComment(i18nc("Noun, a quantity of money taken by a financial institution to perform an operation", "Commission")); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = subop2.setQuantity(-data.commission); } if (!SKGImportPluginOfx::m_ofxError) { SKGImportPluginOfx::m_ofxError = subop2.save(); } } } return SKGImportPluginOfx::m_ofxError.getReturnCode(); } QString SKGImportPluginOfx::getMimeTypeFilter() const { return "*.ofx *.qfx|" % i18nc("A file format", "OFX file"); } #include diff --git a/skgbasemodeler/skgdocument.h b/skgbasemodeler/skgdocument.h index 3f8734e10..a77a1e767 100644 --- a/skgbasemodeler/skgdocument.h +++ b/skgbasemodeler/skgdocument.h @@ -1,1029 +1,1029 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef SKGDOCUMENT_H #define SKGDOCUMENT_H /** @file * This file defines classes SKGDocument. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include #include #include #include #include #include #include #include #include "skgbasemodeler_export.h" #include "skgobjectbase.h" #include "skgservices.h" class SKGObjectBase; class SKGError; class SKGPropertyObject; class SKGReport; class SKGDocumentPrivate; -using FuncSelect = std::function; -using FuncExist = std::function; -using FuncProgress = std::function; +using FuncSelect = std::function; // NOLINT(whitespace/parens) +using FuncExist = std::function; // NOLINT(whitespace/parens) +using FuncProgress = std::function; // NOLINT(whitespace/parens) /** * This class manages skg documents */ class SKGBASEMODELER_EXPORT SKGDocument : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.skg.SKGDocument") using progressFunction = int (*)(int, qint64, const QString&, void*); /** * The unique identifier */ Q_PROPERTY(QString uuid READ getUniqueIdentifier NOTIFY modified) /** * The current file name */ Q_PROPERTY(QString fileName READ getCurrentFileName NOTIFY modified) /** * To know if the document is read only */ Q_PROPERTY(bool readOnly READ isReadOnly NOTIFY modified) /** * To know if the document is modifier */ Q_PROPERTY(bool modified READ isFileModified NOTIFY transactionSuccessfullyEnded) public: /** * This enumerate defines a type of modification */ enum ModificationType { U, /**< Update */ I, /**< Insert */ D /**< Delete */ }; /** * This enumerate defines a type of modification */ Q_ENUM(ModificationType) /** * This enumerate defines a type of message */ enum MessageType { Positive, /**< Positive */ Information, /**< Information */ Warning, /**< Warning */ Error, /**< Error */ Hidden /**< Hidden */ }; /** * This enumerate defines a type of message */ Q_ENUM(MessageType) /** * Describe a modification of an object */ struct SKGObjectModification { QString uuid; /**< The uuid of the object */ int id{}; /**< The id of the object */ QString table; /**< The table of the object */ ModificationType type; /**< The type of modification */ }; /** * Describe a modification of an object */ struct SKGMessage { QString Text; /**< The text of the message */ MessageType Type; /**< The type of the message */ QString Action; /**< The action associated */ }; /** * This structure represents a template to display data */ struct SKGModelTemplate { QString id; /**< Identifier of the schema */ QString name; /**< The nls name */ QString icon; /**< The icon */ QString schema; /**< The schema. The format of this string is the following one: attribute name[|visibility Y or N[|size]];attribute name[|visibility Y or N[|size]];... */ }; /** * A list of SKGModelTemplate ==> SKGModelTemplateList */ using SKGModelTemplateList = QVector; /** * A list of SKGObjectModification ==> SKGObjectModificationList */ using SKGObjectModificationList = QVector; /** * A list of SKGMessage ==> SKGObjectModificationList */ using SKGMessageList = QVector; /** * This enumerate defines the direction of the UNDO / REDO mechanism */ enum UndoRedoMode { UNDOLASTSAVE, /**< To do/get an undo=cancel of the last successfully extecuted transactions until last save */ UNDO, /**< To do/get an undo=cancel of the last successfully extecuted transaction */ REDO /**< To do/get a redo=replay the last cancelled transaction */ }; /** * This enumerate defines the direction of the UNDO / REDO mechanism */ Q_ENUM(UndoRedoMode) /** * Constructor */ explicit SKGDocument(); /** * Destructor */ ~SKGDocument() override; /** * Set the callback function to follow the progress of the transaction. * the first parameter is the progress between 0% and 100%. * the callback must return 0 to continue and !=0 to cancel the transaction. * @param iProgressFunction the pointer of the function * @param iData the data for the progress call back * @return an object managing the error * @see SKGError */ virtual SKGError setProgressCallback(FuncProgress iProgressFunction, void* iData); /** * Add a check function at the end of successfully executed transaction. * If an error is returned, the transaction will be cancelled. * @param iCheckFunction the pointer of the function * @return an object managing the error * @see SKGError */ virtual SKGError addEndOfTransactionCheck(SKGError(*iCheckFunction)(SKGDocument*)); /** * Get unique identifier * @return the unique identifier */ virtual QString getUniqueIdentifier() const; /** * Get database identifier * @return the database identifier */ virtual QString getDatabaseIdentifier() const; /** * Get message attached to a transaction. * @param iIdTransaction the identifier of a transaction * @param oMessages the messages * @param iAll to get all message (true) or only non hidden messages (false) * @return an object managing the error * @see SKGError */ virtual SKGError getMessages(int iIdTransaction, SKGMessageList& oMessages, bool iAll = true); /** * Remove all message attached to a transaction. * @param iIdTransaction the identifier of a transaction * @return an object managing the error * @see SKGError */ virtual SKGError removeMessages(int iIdTransaction); /** * Get list of direct modifications done in a transaction * @param iIdTransaction the identifier of a transaction * @param oModifications list of modifications * @return an object managing the error * @see SKGError */ virtual SKGError getModifications(int iIdTransaction, SKGObjectModificationList& oModifications) const; /** * Undo or redo the last transaction. * @param iMode the mode * @return an object managing the error * @see SKGError */ virtual SKGError undoRedoTransaction(UndoRedoMode iMode = SKGDocument::UNDO); /** * Group transactions * @param iFrom the first id transaction of the group, it will be the master of the group. * @param iTo the last id transaction of the group. * @return an object managing the error * @see SKGError */ virtual SKGError groupTransactions(int iFrom, int iTo); /** * Return the number of transaction stored including the current one * @param iMode the mode * @return the number of transaction */ virtual int getNbTransaction(UndoRedoMode iMode = SKGDocument::UNDO) const; /** * Return the internal identifier of the transaction * which must be treated for an undo or a redo * @param iMode the mode * @param oName if you want also the name of the transaction * @param oSaveStep if you want also to know if it is a save step * @param oDate if you want also the date of the transaction * @param oRefreshViews if you want also to know if views must be refreshed * @return the internal identifier of the last transaction * 0 if no transaction found */ virtual int getTransactionToProcess(UndoRedoMode iMode, QString* oName = nullptr, bool* oSaveStep = nullptr, QDateTime* oDate = nullptr, bool* oRefreshViews = nullptr) const; /** * Return the identifier of the current transaction * @return the internal identifier of the current transaction * 0 if no transaction found */ virtual int getCurrentTransaction() const; /** * Return the depth of the current transaction * @return the depth */ virtual int getDepthTransaction() const; /** * To know if a transaction is opened or not * @return an object managing the error. * @see SKGError */ virtual SKGError checkExistingTransaction() const; /** * Get the current password. * @return the password */ virtual QString getPassword() const; /** * To know if the current document has been modified or not. * @return true: the document has been modified, save is possible/needed. * false: the document hasn't been modified, save is not needed. */ virtual bool isFileModified() const; /** * To know if the document is loaded in read only. * @return true: the document is loaded in read only. * false: the document is loaded in read write. */ virtual bool isReadOnly() const; /** * Return the file name of the current document. * To set it, you must use saveAs. * @return the file name of the current document. */ virtual QString getCurrentFileName() const; /** * Set a parameter. * WARNING: This method must be used in a transaction. * @see beginTransaction * @param iName the parameter unique identifier. * @param iValue the parameter value. * @param iFileName the file name. * @param iParentUUID the unique identifier of the object owning this parameter. * @param oObjectCreated the parameter object created. Can be nullptr * @return an object managing the error. * @see SKGError */ virtual SKGError setParameter(const QString& iName, const QString& iValue, const QString& iFileName = QString(), const QString& iParentUUID = QStringLiteral("document"), SKGPropertyObject* oObjectCreated = nullptr) const; /** * Set a parameter. * WARNING: This method must be used in a transaction. * @see beginTransaction * @param iName the parameter unique identifier. * @param iValue the parameter value. * @param iBlob the parameter blob. * @param iParentUUID the unique identifier of the object owning this parameter. * @param oObjectCreated the parameter object created. Can be nullptr * @return an object managing the error. * @see SKGError */ virtual SKGError setParameter(const QString& iName, const QString& iValue, const QVariant& iBlob, const QString& iParentUUID = QStringLiteral("document"), SKGPropertyObject* oObjectCreated = nullptr) const; /** * Get the list of parameters * @param iParentUUID the unique identifier of the object owning parameters. * @param iWhereClause the additional where clause. * @return the list of parameters. */ virtual QStringList getParameters(const QString& iParentUUID, const QString& iWhereClause = QString()); /** * Get a parameter value * @param iName the parameter unique identifier. * @param iParentUUID the unique identifier of the object owning this parameter. * @return the value. */ virtual QString getParameter(const QString& iName, const QString& iParentUUID = QStringLiteral("document")) const; /** * Get a parameter blob * @param iName the parameter unique identifier. * @param iParentUUID the unique identifier of the object owning this parameter. * @return the blob. */ virtual QVariant getParameterBlob(const QString& iName, const QString& iParentUUID = QStringLiteral("document")) const; /** * Get the database pointer. * This is the database connection of the main thread * @return the database pointer. * MUST NOT BE REMOVED */ QSqlDatabase* getMainDatabase() const; /** * Get the database pointer. * This is the database connection of the current thread * @return the database pointer. */ virtual QSqlDatabase getThreadDatabase() const; /** * dump the document in the std output. * It is useful for debug. * @param iMode the select what you want to dump. * @code * document->dump (DUMPPARAMETERS|DUMPTRANSACTIONS); * @endcode * @return an object managing the error. * @see SKGError */ virtual SKGError dump(int iMode = DUMPPARAMETERS | DUMPTRANSACTIONS) const; /** * Create a consolidated view * @param iTable Table name * @param iAsColumn Attribute used as column names * @param iAsRow Attribute used as lines names * @param iAttribute Attribute * @param iOpAtt Operation to apply on @p iAttribute * @param iWhereClause Where clause * @param oTable the consolidated view * @param iMissingValue value to put if no value found * @return an object managing the error. * @see SKGError */ virtual SKGError getConsolidatedView(const QString& iTable, const QString& iAsColumn, const QString& iAsRow, const QString& iAttribute, const QString& iOpAtt, const QString& iWhereClause, SKGStringListList& oTable, const QString& iMissingValue = QStringLiteral("0")) const; /** * Get the display string for any modeler object (table, attribute) * @param iString the name of the object (example: v_operation, v_unit.t_name) * @return the display string */ virtual QString getDisplay(const QString& iString) const; /** * Get display schemas * @param iRealTable the real table name * @return list of schemas */ virtual SKGDocument::SKGModelTemplateList getDisplaySchemas(const QString& iRealTable) const; /** * Get the icon for attribute * @param iString the name of the attribute * @return the icon name */ virtual QString getIconName(const QString& iString) const; /** * Get the icon for attribute * @param iString the name of the attribute * @return the icon */ virtual QIcon getIcon(const QString& iString) const; /** * Get the real attribute * @param iString the name of the attribute (something like t_BANK) * @return the real attribute (something like bank.rd_bank_id.t_name) */ virtual QString getRealAttribute(const QString& iString) const; /** * Get the attribute type * @param iAttributeName the name of an attribute * @return the type */ virtual SKGServices::AttributeType getAttributeType(const QString& iAttributeName) const; /** * Get unit. WARNING: This value can be not uptodated in a transaction. * @param iPrefixInCache the prefix of the unit in the cache * @return unit. */ virtual SKGServices::SKGUnitInfo getUnit(const QString& iPrefixInCache) const; /** * Get the cached sql result * @param iKey key * @return the sql result */ virtual SKGStringListList getCachedSqlResult(const QString& iKey) const; /** * Get the cached value * @param iKey key * @return the value */ virtual QString getCachedValue(const QString& iKey) const; /** * Get the corresponding backup file * @param iFileName the file to save * @return the corresponding backup file */ virtual QString getBackupFile(const QString& iFileName) const; /** * Get the temporary file for a file * @param iFileName the file * @param iForceReadOnly force the read only mode * @return the temporary file */ static QString getTemporaryFile(const QString& iFileName, bool iForceReadOnly = false); /** * Get the temporary file for a document * @return the temporary file */ virtual QString getCurrentTemporaryFile() const; /** * Get the file extension for this kind of document (must be overwritten) * @return file extension (like skg) */ virtual QString getFileExtension() const; /** * Get the header of the file (useful for magic mime type). * @return document header */ virtual QString getDocumentHeader() const; /** * Return a number of objects (@p oNbObjects) corresponding to a where clause (@p iWhereClause ) * @param iTable the table where to search * @param iWhereClause the where clause * @param oNbObjects the result * @return an object managing the error * @see SKGError */ virtual SKGError getNbObjects(const QString& iTable, const QString& iWhereClause, int& oNbObjects) const; /** * To execute a function when at least one object exist corresponding to a where clause (@p iWhereClause) * @param iTable the table where to search * @param iWhereClause the where clause * @param iFunction the function to call * @param iExecuteInMainThread to execute the function in the main thread or not */ virtual void concurrentExistObjects(const QString& iTable, const QString& iWhereClause, const FuncExist& iFunction, bool iExecuteInMainThread = true); /** * To know if at least one object exist corresponding to a where clause (@p iWhereClause) * @param iTable the table where to search * @param iWhereClause the where clause * @param oExist the result * @return an object managing the error * @see SKGError */ virtual SKGError existObjects(const QString& iTable, const QString& iWhereClause, bool& oExist) const; /** * Return a list of objects (@p oListObject) corresponding to a where clause (@p iWhereClause ) * @param iTable the table where to search * @param iWhereClause the where clause * @param oListObject the result * @return an object managing the error * @see SKGError */ virtual SKGError getObjects(const QString& iTable, const QString& iWhereClause, SKGObjectBase::SKGListSKGObjectBase& oListObject) const; /** * Return the object (@p oObject) corresponding to a where clause (@p iWhereClause ). * If more than one objects are returned by the query, then an error is generated * If 0 object is returned by the query, then an error is generated * @param iTable the table where to search * @param iWhereClause the where clause * @param oObject the result * @return an object managing the error * @see SKGError */ virtual SKGError getObject(const QString& iTable, const QString& iWhereClause, SKGObjectBase& oObject) const; /** * Return the object (@p oObject) corresponding to an id (@p iId ). * If more than one objects are returned by the query, then an error is generated * If 0 object is returned by the query, then an error is generated * @param iTable the table where to search * @param iId id of the object in @p iTable * @param oObject the result * @return an object managing the error * @see SKGError */ virtual SKGError getObject(const QString& iTable, int iId, SKGObjectBase& oObject) const; /** * Retrieve list of tables * @param oResult the output result * @return An object managing the error * @see SKGError */ virtual SKGError getTablesList(QStringList& oResult) const; /** * Retrieve all distinct values of an attribute of a table * @param iTable the table where to look for * @param iAttribute the attribute wanted (only one) * @param iWhereClause a whereclause * @param oResult the output result * @return An object managing the error * @see SKGError */ virtual SKGError getDistinctValues(const QString& iTable, const QString& iAttribute, const QString& iWhereClause, QStringList& oResult) const; /** * Retrieve all distinct values of an attribute of a table * @param iTable the table where to look for * @param iAttribute the attribute wanted (only one) * @param oResult the output result * @return An object managing the error * @see SKGError */ virtual SKGError getDistinctValues(const QString& iTable, const QString& iAttribute, QStringList& oResult) const; /** * Execute a sqlite orders * @param iSqlOrders the sql orders * @return An object managing the error * @see SKGError */ virtual SKGError executeSqliteOrders(const QStringList& iSqlOrders) const; /** * Execute a sqlite order * @param iSqlOrder the sql order * @param iBind the binded variables and values * @param iLastId to retrieve the id of the last created object * @return An object managing the error * @see SKGError */ virtual SKGError executeSqliteOrder(const QString& iSqlOrder, const QMap& iBind, int* iLastId) const; /** * Execute a sqlite order * @param iSqlOrder the sql order * @param iLastId to retrieve the id of the last created object. Can be nullptr * @return An object managing the error * @see SKGError */ virtual SKGError executeSqliteOrder(const QString& iSqlOrder, int* iLastId) const; /** * Execute a sqlite order * @param iSqlOrder the sql order * @return An object managing the error * @see SKGError */ virtual SKGError executeSqliteOrder(const QString& iSqlOrder) const; /** * Execute a select sqlite order returning one value and return the result in @p oResult * @param iSqlOrder the sql order * @param oResult the result of the select * @param iUseCache to use the cache * @return An object managing the error * @see SKGError */ virtual SKGError executeSingleSelectSqliteOrder(const QString& iSqlOrder, QString& oResult, bool iUseCache = true) const; /** * Execute a select sqlite order and return the result in @p oResult * @param iSqlOrder the sql order * @param oResult the result of the select. It is a vector of vector of QString * @param iUseCache to use the cache * @return An object managing the error * @see SKGError */ virtual SKGError executeSelectSqliteOrder(const QString& iSqlOrder, SKGStringListList& oResult, bool iUseCache = true) const; /** * Execute a select sqlite order and pass the result to the function @p iFunction. This is done in another thread. * @param iSqlOrder the sql order * @param iFunction the function to call * @param iExecuteInMainThread to execute the function in the main thread or not */ virtual void concurrentExecuteSelectSqliteOrder(const QString& iSqlOrder, const FuncSelect& iFunction, bool iExecuteInMainThread = true) const; /** * dump a select sqlite order * @param iSqlOrder the sql order * @param oStream the output stream, nullptr for std output (cout) * @param iMode dump mode * @return An object managing the error * @see SKGError */ virtual SKGError dumpSelectSqliteOrder(const QString& iSqlOrder, QTextStream* oStream = nullptr, SKGServices::DumpMode iMode = SKGServices::DUMP_TEXT) const; /** * dump a select sqlite order * @param iSqlOrder the sql order * @param oResult the output * @param iMode dump mode * @return An object managing the error * @see SKGError */ virtual SKGError dumpSelectSqliteOrder(const QString& iSqlOrder, QString& oResult, SKGServices::DumpMode iMode = SKGServices::DUMP_TEXT) const; /** * dump a select sqlite order * @param iSqlOrder the sql order * @param oResult the output * @param iMode dump mode * @return An object managing the error * @see SKGError */ virtual SKGError dumpSelectSqliteOrder(const QString& iSqlOrder, QStringList& oResult, SKGServices::DumpMode iMode = SKGServices::DUMP_TEXT) const; /** * Retrieve description of each attribute of a table * @param iTable the table where to look for * @param oResult the output result * @return An object managing the error * @see SKGError */ virtual SKGError getAttributesDescription(const QString& iTable, SKGServices::SKGAttributesList& oResult) const; /** * Retrieve list of attributes * @param iTable the table where to look for * @param oResult the output result * @return An object managing the error * @see SKGError */ virtual SKGError getAttributesList(const QString& iTable, QStringList& oResult) const; /** * Get the report * Do not forget to delete the pointer * @return the report */ virtual SKGReport* getReport() const; /** * Copy a document into an JSON document. * @param oDocument the json document * @return An object managing the error * @see SKGError */ virtual SKGError copyToJson(QString& oDocument) const; /** * Refresh all views and indexes in the database * @param iForce force the refresh * @return an object managing the error. * @see SKGError */ virtual SKGError refreshViewsIndexesAndTriggers(bool iForce = false) const; /** * Get formated money * @param iValue value * @param iUnit unit * @param iHtml colored output * @return formated value in red or black */ Q_INVOKABLE virtual QString formatMoney(double iValue, const SKGServices::SKGUnitInfo& iUnit, bool iHtml = true) const; /** * Get formated money in primary unit * @param iValue value * @return formated value in red or black */ Q_INVOKABLE virtual QString formatPrimaryMoney(double iValue) const; /** * Get formated money in primary unit * @param iValue value * @return formated value in red or black */ Q_INVOKABLE virtual QString formatSecondaryMoney(double iValue) const; /** * Get formated percentage * @param iValue value * @param iInvertColors to set positive value in red and negative values in green * @return formated value in red or black */ Q_INVOKABLE virtual QString formatPercentage(double iValue, bool iInvertColors = false) const; public Q_SLOTS: /** * Call the progress callstack. * @param iPosition the position in the current transaction. * The value must be between 0 and the value passed to beginTransaction. * @param iText the text to display. If empty then the name of the last transaction is used. * @return an object managing the error * @see SKGError */ virtual SKGError stepForward(int iPosition, const QString& iText = QString()); /** * Start a transaction. * A transaction is needed to modify the SKGDocument. * This transaction is also used to manage the undo/redo. * @see endTransaction * @param iName the name of the transaction * @param iNbStep the number of step in this transaction. * It is used to call the progress callback. * @param iDate date of the transaction. * @param iRefreshViews at the end of the transaction, computed views will be recomputed. * @return an object managing the error * @see SKGError */ virtual SKGError beginTransaction(const QString& iName, int iNbStep = 0, const QDateTime& iDate = QDateTime::currentDateTime(), bool iRefreshViews = true); /** * Close the current transaction. * A transaction is needed to modify the SKGDocument. * This transaction is also used to manage the undo/redo. * @see beginTransaction * @param succeedded : true to indicate that current transaction has been successfully executed * : false to indicate that current transaction has failed * @return an object managing the error * @see SKGError */ virtual SKGError endTransaction(bool succeedded); /** * Remove all transactions of the history. * @return an object managing the error * @see SKGError */ virtual SKGError removeAllTransactions(); /** * Send a message attached to the current transaction. * @param iMessage the message * @param iMessageType the message type * @param iAction the associated action * @return an object managing the error * @see SKGError */ virtual SKGError sendMessage(const QString& iMessage, SKGDocument::MessageType iMessageType = SKGDocument::Information, const QString& iAction = QString()); /** * Change the passord of the document. * WARNING: This method must NOT be used in a transaction. * @see beginTransaction * @param iNewPassword the new password * @return an object managing the error * @see SKGError */ virtual SKGError changePassword(const QString& iNewPassword); /** * Change the language of the document. * @param iLanguage the new language * @return an object managing the error * @see SKGError */ virtual SKGError setLanguage(const QString& iLanguage); /** * Initialize a new document. * WARNING: This method must NOT be used in a transaction. * @see endTransaction * @return an object managing the error * @see SKGError */ virtual SKGError initialize(); /** * Recover a corrupted file. * WARNING: This method must NOT be used in a transaction. * @see endTransaction * @param iName the file name to load. * @param iPassword the password of the SKGDocument. * @param oRecoveredFile the recovered file. * @return an object managing the error. * @see SKGError */ virtual SKGError recover(const QString& iName, const QString& iPassword, QString& oRecoveredFile); /** * Load an existing document. * WARNING: This method must NOT be used in a transaction. * @see endTransaction * @param iName the file name to load. * @param iPassword the password of the SKGDocument. * @param iRestoreTmpFile restore the temporary file if existing. * @param iForceReadOnly force the read only mode. * @return an object managing the error. * @see SKGError */ virtual SKGError load(const QString& iName, const QString& iPassword = QString(), bool iRestoreTmpFile = false, bool iForceReadOnly = false); /** * Set the file not modified. */ virtual void setFileNotModified() const; /** * save the current SKGDocument. * WARNING: This method must NOT be used in a transaction. * @see endTransaction. * @return an object managing the error. * @see SKGError */ virtual SKGError save(); /** * save the current SKGDocument. * WARNING: This method must NOT be used in a transaction. * @see endTransaction * @param iName the file name to save. * @param iOverwrite to authorize the overwrite or not. * @return an object managing the error. * @see SKGError */ virtual SKGError saveAs(const QString& iName, bool iOverwrite = false); /** * close the current SKGDocument. * @return an object managing the error. * @see SKGError */ SKGError close(); /** * Add a sql result in the cache. Cache is cleaned after each transaction * @param iKey the key * @param iValue the sql result */ virtual void addSqlResultInCache(const QString& iKey, const SKGStringListList& iValue) const; /** * Add a value in the cache. Cache is cleaned after each transaction * @param iKey the key * @param iValue the value */ virtual void addValueInCache(const QString& iKey, const QString& iValue) const; /** * Refresh the case. * @param iTable the modified table triggering the cache refresh. */ virtual void refreshCache(const QString& iTable) const; /** * Set backup parameters. * The key word \ is supported. * Call setBackupParameters() to avoid backup creation. * @param iPrefix the prefix for the backup file * @param iSuffix the suffix for backup file */ void setBackupParameters(const QString& iPrefix = QString(), const QString& iSuffix = QString()) const; Q_SIGNALS: /** * This signal is launched by endTransaction on all tables modified when a huge modification occures on the model * @param iTableName the name of the modified table. iTableName="" if all tables must be refreshed * @param iIdTransaction the id of the transaction for direct modifications of the table (update of modify objects is enough) * @param iLightTransaction to if the transaction is light *or 0 in case of modifications by impact (full table must be refreshed) */ void tableModified(const QString& iTableName, int iIdTransaction, bool iLightTransaction); /** * This signal is launched by endTransaction when a transaction is successfully ended * @param iIdTransaction the id of the transaction for direct modifications of the table (update of modify objects is enough) */ void transactionSuccessfullyEnded(int iIdTransaction); /** * This signal is launched when the status of the document is changed (read only, modified) */ void modified(); protected: /** * Drop views and indexes attached to a list of tables * @param iTables the list of tables. * @return an object managing the error. * @see SKGError */ virtual SKGError dropViewsAndIndexes(const QStringList& iTables) const; /** * Migrate the current SKGDocument to the latest version of the data model. * WARNING: This method must be used in a transaction. * @see beginTransaction * @param oMigrationDone to know if a migration has been done or not. * @return an object managing the error. * @see SKGError */ virtual SKGError migrate(bool& oMigrationDone); /** * Create dynamic triggers for "undoable" tables. * @return an object managing the error. * @see SKGError */ virtual SKGError createUndoRedoTemporaryTriggers() const; /** * This list must contain a list of object * where the undo / redo is NOT applicable. * For a full table, the syntax is: T.nameofthetable * For an attribute, the syntax is: A.nameofthetable.nameoftheattribute */ QStringList SKGListNotUndoable; /** * Get impacted views if one object of @p iTable is modifier. * @param iTable name of a table * @return impacted views */ virtual QStringList getImpactedViews(const QString& iTable) const; /** * Compute all materialized views. * @param iTable Compute only materialized views linked to this table. If empty then compute all materialized views * @return an object managing the error. * @see SKGError */ virtual SKGError computeMaterializedViews(const QString& iTable = QString()) const; private: Q_DISABLE_COPY(SKGDocument) SKGMessageList m_unTransactionnalMessages; SKGDocumentPrivate* d; }; /** * Declare the class */ Q_DECLARE_TYPEINFO(SKGDocument, Q_MOVABLE_TYPE); #endif diff --git a/skgbasemodeler/skgdocumentprivate.h b/skgbasemodeler/skgdocumentprivate.h index df15ad94f..57a792a39 100644 --- a/skgbasemodeler/skgdocumentprivate.h +++ b/skgbasemodeler/skgdocumentprivate.h @@ -1,93 +1,93 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see * ***************************************************************************/ #ifndef SKGDOCUMENTPRIVATE_H #define SKGDOCUMENTPRIVATE_H /** @file * This file defines classes SKGDocumentPrivate. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include #include #include #include #include #include "skgdefine.h" #include "skgerror.h" #include "skgservices.h" -using FuncProgress = std::function; +using FuncProgress = std::function; using checksFunction = SKGError(*)(SKGDocument*); /** * This class manages skg documents */ class SKGDocumentPrivate { public: /** * Constructor */ explicit SKGDocumentPrivate(); /** * Destructor */ virtual ~SKGDocumentPrivate(); static SKGError m_lastCallbackError; static int m_databaseUniqueIdentifier; int m_lastSavedTransaction{0}; FuncProgress m_progressFunction{nullptr}; QList m_checkFunctions; void* m_progressData{nullptr}; QString m_currentFileName; QString m_databaseIdentifier; QSqlDatabase m_currentDatabase; SKGIntList m_nbStepForTransaction; SKGIntList m_posStepForTransaction; QStringList m_nameForTransaction; int m_inundoRedoTransaction{0}; int m_currentTransaction{0}; qint64 m_timeBeginTransaction{0}; QString m_temporaryFile; QString m_uniqueIdentifier; // SKGMessageList m_unTransactionnalMessages; QMap m_ImpactedViews; QMap m_MaterializedViews; QHash m_cache; QHash* m_cacheSql; bool m_inProgress{false}; QString m_backupPrefix; QString m_backupSuffix; bool m_directAccessDb{false}; bool m_modeReadOnly{false}; bool m_modeSQLCipher{false}; QList*> m_watchers; bool m_blockEmits{false}; QMutex m_mutex; QString m_password; bool m_password_got{false}; }; #endif diff --git a/tests/skgbankmodelertest/skgtestimportcsv.cpp b/tests/skgbankmodelertest/skgtestimportcsv.cpp index 35ea153dc..2ad0fdf39 100644 --- a/tests/skgbankmodelertest/skgtestimportcsv.cpp +++ b/tests/skgbankmodelertest/skgtestimportcsv.cpp @@ -1,815 +1,815 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file is a test script. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgtestmacro.h" #include "skgbankincludes.h" #include "skgimportexportmanager.h" /** * The main function of the unit test * @param argc the number of arguments * @param argv the list of arguments */ int main(int argc, char** argv) { Q_UNUSED(argc) Q_UNUSED(argv) // Init test SKGINITTEST(true) QDate now = QDate::currentDate(); { // Test import SKGImportExportManager::CSV skrooge SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_CSV"), err) SKGImportExportManager impmissing(&document1, QUrl::fromLocalFile(QStringLiteral("missingfile.csv"))); impmissing.setAutomaticApplyRules(true); SKGTESTERROR(QStringLiteral("imp1.importFile"), impmissing.importFile(), false) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/skrooge_partial.csv")); SKGImportExportManager::getImportMimeTypeFilter(); SKGImportExportManager::getExportMimeTypeFilter(); imp1.setCodec(QLatin1String("")); SKGTESTERROR(QStringLiteral("imp1.importFile"), imp1.importFile(), true) } SKGAccountObject account; SKGTESTERROR(QStringLiteral("ACCOUNT.getObjectByName"), SKGNamedObject::getObjectByName(&document1, QStringLiteral("v_account"), QStringLiteral("Courant steph"), account), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-935")) SKGTEST(QStringLiteral("document1:getCategoryForPayee"), document1.getCategoryForPayee(QStringLiteral("Anthony Hopkins"), false), QStringLiteral("Entertain > Movie")) SKGTEST(QStringLiteral("document1:getCategoryForPayee"), document1.getCategoryForPayee(QStringLiteral("NOT FOUND")), QLatin1String("")) } { // Test import QIF 1 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGBankObject bank(&document1); SKGAccountObject account; SKGUnitObject unit_euro(&document1); SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) // Creation bank SKGTESTERROR(QStringLiteral("BANK:setName"), bank.setName(QStringLiteral("CREDIT COOP")), true) SKGTESTERROR(QStringLiteral("BANK:save"), bank.save(), true) // Creation account SKGTESTERROR(QStringLiteral("BANK:addAccount"), bank.addAccount(account), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setName"), account.setName(QStringLiteral("Courant steph")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setNumber"), account.setNumber(QStringLiteral("12345P")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:save"), account.save(), true) // Creation unit SKGTESTERROR(QStringLiteral("UNIT:setName"), unit_euro.setName(QStringLiteral("euro")), true) SKGTESTERROR(QStringLiteral("UNIT:save"), unit_euro.save(), true) // Creation unitvalue SKGUnitValueObject unit_euro_val1; SKGTESTERROR(QStringLiteral("UNIT:addUnitValue"), unit_euro.addUnitValue(unit_euro_val1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setQuantity"), unit_euro_val1.setQuantity(1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setDate"), unit_euro_val1.setDate(now), true) SKGTESTERROR(QStringLiteral("UNITVALUE:save"), unit_euro_val1.save(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_CSV"), err) // Date;Libelle;Libelle complementaire;Montant;Sens;Numero de cheque SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/coopanet.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("automatic_search_header")] = 'N'; parameters[QStringLiteral("columns_positions")] = QStringLiteral("date|comment||amount|sign|number"); imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("QIF.setDefaultAccount"), imp1.setDefaultAccount(&account), true) SKGTESTERROR(QStringLiteral("imp1.importFile"), imp1.importFile(), true) } SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-680.28")) } { // Test import bankperfect SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGBankObject bank(&document1); SKGAccountObject account1; SKGAccountObject account2; SKGUnitObject unit_euro(&document1); SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) // Creation bank SKGTESTERROR(QStringLiteral("BANK:setName"), bank.setName(QStringLiteral("CREDIT COOP")), true) SKGTESTERROR(QStringLiteral("BANK:save"), bank.save(), true) // Creation account SKGTESTERROR(QStringLiteral("BANK:addAccount"), bank.addAccount(account1), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setName"), account1.setName(QStringLiteral("Courant steph")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setNumber"), account1.setNumber(QStringLiteral("12345P")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:save"), account1.save(), true) SKGTESTERROR(QStringLiteral("BANK:addAccount"), bank.addAccount(account2), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setName"), account2.setName(QStringLiteral("PEL")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:save"), account2.save(), true) // Creation unit SKGTESTERROR(QStringLiteral("UNIT:setName"), unit_euro.setName(QStringLiteral("euro")), true) SKGTESTERROR(QStringLiteral("UNIT:save"), unit_euro.save(), true) // Creation unitvalue SKGUnitValueObject unit_euro_val1; SKGTESTERROR(QStringLiteral("UNIT:addUnitValue"), unit_euro.addUnitValue(unit_euro_val1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setQuantity"), unit_euro_val1.setQuantity(1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setDate"), unit_euro_val1.setDate(now), true) SKGTESTERROR(QStringLiteral("UNITVALUE:save"), unit_euro_val1.save(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_BP_CSV"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/bankperfect.csv")); SKGTESTERROR(QStringLiteral("QIF.setDefaultAccount"), imp1.setDefaultAccount(&account1), true) SKGTESTERROR(QStringLiteral("imp1.importFile"), imp1.importFile(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_BP_CSV"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/bankperfect2.csv")); SKGTESTERROR(QStringLiteral("QIF.setDefaultAccount"), imp1.setDefaultAccount(&account2), true) SKGTESTERROR(QStringLiteral("imp1.importFile"), imp1.importFile(), true) int NbOperationsMerged = 0; SKGTESTERROR(QStringLiteral("imp1.findAndGroupTransfers"), imp1.findAndGroupTransfers(NbOperationsMerged), true) SKGTEST(QStringLiteral("imp1:NbOperationsMerged"), NbOperationsMerged, 6) } SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account1.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account1.getCurrentAmount()), QStringLiteral("2624.071111")) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account2.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account2.getCurrentAmount()), QStringLiteral("1500")) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportcsv/bankperfect.csv")); SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), exp1.exportFile(), true) { SKGStringListList oTable; SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QStringLiteral("d_DATEMONTH"), QStringLiteral("t_CATEGORY"), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) QStringList dump = SKGServices::tableToDump(oTable, SKGServices::DUMP_TEXT); int nbl = dump.count(); for (int i = 0; i < nbl; ++i) { SKGTRACE << dump.at(i) << endl; } } { SKGStringListList oTable; SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QLatin1String(""), QStringLiteral("t_CATEGORY"), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) QStringList dump = SKGServices::tableToDump(oTable, SKGServices::DUMP_TEXT); int nbl = dump.count(); for (int i = 0; i < nbl; ++i) { SKGTRACE << dump.at(i) << endl; } } { SKGStringListList oTable; SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QStringLiteral("d_DATEWEEK"), QLatin1String(""), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QStringLiteral("d_DATEQUARTER"), QLatin1String(""), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QStringLiteral("d_DATESEMESTER"), QLatin1String(""), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QStringLiteral("d_DATEYEAR"), QLatin1String(""), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QStringLiteral("d_date"), QLatin1String(""), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), document1.getConsolidatedView(QStringLiteral("v_operation_display"), QStringLiteral("d_DATEMONTH"), QLatin1String(""), QStringLiteral("f_CURRENTAMOUNT"), QStringLiteral("SUM"), QLatin1String(""), oTable), true) QStringList dump = SKGServices::tableToDump(oTable, SKGServices::DUMP_TEXT); int nbl = dump.count(); for (int i = 0; i < nbl; ++i) { SKGTRACE << dump.at(i) << endl; } } } { // Test import skrooge SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_SKROOGE_CSV"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportcsv/bankperfect.csv")); SKGTESTERROR(QStringLiteral("imp1.importFile"), imp1.importFile(), true) } SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportcsv/bankperfect2.csv")); SKGTESTERROR(QStringLiteral("SKGImportExportManager::CSV.exportFile"), exp1.exportFile(), true) } { // Test import skrooge+optimization SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_OPTIM"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/bankperfect.csv")); SKGTESTERROR(QStringLiteral("OPTIM.importFile"), imp1.importFile(), true) } SKGTESTERROR(QStringLiteral("OPTIM.undoRedoTransaction"), document1.undoRedoTransaction(SKGDocument::UNDOLASTSAVE), true) } { // Test import skrooge in double to check merge SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/remi_1.csv")); SKGTESTERROR(QStringLiteral("OPTIM.importFile"), imp1.importFile(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/remi_1.csv")); SKGTESTERROR(QStringLiteral("OPTIM.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.setName(QStringLiteral("remi 1")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-767.26")) } { // Test import 2638120 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/XXXXXXMxxxXXXXXXX.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.setName(QStringLiteral("XXXXXXMxxxXXXXXXX")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("8114.26")) } { // Test import 206894 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/206894.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.setName(QStringLiteral("206894")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-2986.39")) } { // Test import 397055 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/397055.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.setName(QStringLiteral("397055")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("50")) } { // Test import with tabulation SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/mutual fund.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.setName(QStringLiteral("Janus Twenty Fund")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("24.51428572")) } { // Test import shares with original amount SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/209705.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/209705_2.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1); int out = 0; SKGTESTERROR(QStringLiteral("CSV.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) SKGTEST(QStringLiteral("CSV:nb"), out, 2) } SKGObjectBase::SKGListSKGObjectBase grouped; SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getObjects(QStringLiteral("operation"), QStringLiteral("i_group_id!=0"), grouped), true) SKGTEST(QStringLiteral("CSV:grouped.count"), grouped.count(), 2) } { // Test import transactions split and grouped SKGAccountObject la; SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/compte.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) SKGObjectBase::SKGListSKGObjectBase banks; SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getObjects(QStringLiteral("bank"), QLatin1String(""), banks), true) SKGBankObject bank(banks.at(0)); bank.setName(QStringLiteral("bp")); bank.save(); } SKGObjectBase::SKGListSKGObjectBase grouped; SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getObjects(QStringLiteral("operation"), QLatin1String(""), grouped), true) SKGTEST(QStringLiteral("CSV:grouped.count"), grouped.count(), 4) SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getObjects(QStringLiteral("operation"), QStringLiteral("i_group_id!=0"), grouped), true) SKGTEST(QStringLiteral("CSV:grouped.count"), grouped.count(), 2) SKGObjectBase::SKGListSKGObjectBase result; SKGTESTERROR(QStringLiteral("DOC.getObjects"), document1.getObjects(QStringLiteral("account"), QStringLiteral("t_name='PEL'"), result), true) SKGTEST(QStringLiteral("DOC.getObjects.count"), result.count(), 1) if (result.count() != 0) { la = result.at(0); } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("EXPORT_CSV"), err) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportcsv/export_all.csv")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("EXPORT_CSV"), err) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportcsv/export_la.csv")); QMap params; params[QStringLiteral("uuid_of_selected_accounts_or_operations")] = la.getUniqueID(); exp1.setExportParameters(params); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } } { // Test import mmex SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/mmex.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } int nb = 0; SKGTESTERROR(QStringLiteral("CATEGORY.getNbObjects"), document1.getNbObjects(QStringLiteral("category"), QStringLiteral("t_fullname='Alimentation > Restaurant'"), nb), true) SKGTEST(QStringLiteral("CATEGORY:nb"), nb, 1) SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("mmex")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1418.44")) } { // Test import mmex SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/mmex_no_header.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("automatic_search_columns")] = 'N'; parameters[QStringLiteral("columns_positions")] = QStringLiteral("date|comment|sign|amount|category"); parameters[QStringLiteral("automatic_search_header")] = 'N'; parameters[QStringLiteral("header_position")] = '0'; imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("mmex no header")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1418.44")) } { // 263263 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/263263.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("mapping_debit")] = QStringLiteral("kreditrente"); parameters[QStringLiteral("automatic_search_columns")] = 'N'; parameters[QStringLiteral("columns_positions")] = QStringLiteral("date||number|sign|comment|amount|amount"); parameters[QStringLiteral("automatic_search_header")] = 'N'; parameters[QStringLiteral("header_position")] = '1'; imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("263263")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("800.09")) } { // CREDIT-DEBIT SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/credit-debit.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("credit debit")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1500")) } { // BACKSLASHES SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/backslashes.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("backslashes")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1000")) } { // MULTILINE SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/multiline.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("multiline")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("3000")) } { // MULTILINE SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/non_numerical_amount.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("non numerical amount")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-119.56")) } { // Test import 320112 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/320112.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("automatic_search_columns")] = 'N'; parameters[QStringLiteral("columns_positions")] = QStringLiteral("date|mode|payee|comment|amount"); imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("320112")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("6.13")) } { // Test import date DDMMMYYYY SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/date_DDMMMYYYY.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } document1.dump(DUMPOPERATION | DUMPACCOUNT); SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("date DDMMMYYYY")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getAmount"), SKGServices::doubleToString(account.getAmount(QDate(2004, 12, 31))), QStringLiteral("35")) } { // Test import separator tab SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/tabs.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } } { // Test import separator comma SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/commas.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } } { // BUG 406488 // - //"Date","Type","Number","Payee","Withdrawal (-)","Amount" + // "Date","Type","Number","Payee","Withdrawal (-)","Amount" SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/406488.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("automatic_search_columns")] = 'N'; parameters[QStringLiteral("columns_positions")] = QStringLiteral("date|mode|number|payee|amount|amount"); imp1.setImportParameters(parameters); SKGError err = imp1.importFile(); SKGTESTERROR(QStringLiteral("CSV.importFile"), err, false) SKGTEST(QStringLiteral("CSV:error message"), err.getMessage(), QStringLiteral("Invalid number of columns in line 2. Expected 6. Found 1.")) } } { // Test import separator comma SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/349961.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } int nb = 0; SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getNbObjects(QStringLiteral("operation"), QLatin1String(""), nb), true) SKGTEST(QStringLiteral("CSV:nb operations"), nb, 1) SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getNbObjects(QStringLiteral("suboperation"), QStringLiteral("d_date='2015-07-07'"), nb), true) SKGTEST(QStringLiteral("CSV:nb suboperations 2015-07-07"), nb, 1) SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getNbObjects(QStringLiteral("suboperation"), QStringLiteral("d_date='2015-07-08'"), nb), true) SKGTEST(QStringLiteral("CSV:nb suboperations 2015-07-08"), nb, 1) SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getNbObjects(QStringLiteral("suboperation"), QStringLiteral("d_date='2015-07-09'"), nb), true) SKGTEST(QStringLiteral("CSV:nb suboperations 2015-07-09"), nb, 1) } { // Test import separator comma SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/362231.csv")); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } int nb = 0; SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getNbObjects(QStringLiteral("operation"), QStringLiteral("d_date!='0000-00-00'"), nb), true) SKGTEST(QStringLiteral("CSV:nb operation"), nb, 1) SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getNbObjects(QStringLiteral("suboperation"), QStringLiteral("d_date='2016-05-15'"), nb), true) SKGTEST(QStringLiteral("CSV:nb suboperations 2016-05-15"), nb, 1) SKGTESTERROR(QStringLiteral("CSV.getObjects"), document1.getNbObjects(QStringLiteral("suboperation"), QStringLiteral("d_date='2016-05-20'"), nb), true) SKGTEST(QStringLiteral("CSV:nb suboperations 2016-05-20"), nb, 1) } { // 381562 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGBankObject bank(&document1); SKGAccountObject account; SKGUnitObject unit_euro(&document1); SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) // Creation bank SKGTESTERROR(QStringLiteral("BANK:setName"), bank.setName(QStringLiteral("CREDIT COOP")), true) SKGTESTERROR(QStringLiteral("BANK:save"), bank.save(), true) // Creation account SKGTESTERROR(QStringLiteral("BANK:addAccount"), bank.addAccount(account), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setName"), account.setName(QStringLiteral("Courant steph")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setNumber"), account.setNumber(QStringLiteral("DE00 1234 5678 9012 3456 78")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:save"), account.save(), true) // Creation unit SKGTESTERROR(QStringLiteral("UNIT:setName"), unit_euro.setName(QStringLiteral("euro")), true) SKGTESTERROR(QStringLiteral("UNIT:save"), unit_euro.save(), true) // Creation unitvalue SKGUnitValueObject unit_euro_val1; SKGTESTERROR(QStringLiteral("UNIT:addUnitValue"), unit_euro.addUnitValue(unit_euro_val1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setQuantity"), unit_euro_val1.setQuantity(1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setDate"), unit_euro_val1.setDate(now), true) SKGTESTERROR(QStringLiteral("UNITVALUE:save"), unit_euro_val1.save(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_CSV"), err) // Date;Libelle;Libelle complementaire;Montant;Sens;Numero de cheque SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/DE00 1234 5678 9012 3456 78.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("automatic_search_header")] = 'N'; parameters[QStringLiteral("columns_positions")] = QStringLiteral("date|comment||amount|sign|number"); imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("imp1.importFile"), imp1.importFile(), true) } SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-680.28")) } { // Test 411958 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/411958.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("mapping_date")] = QStringLiteral("^Buchungstag"); parameters[QStringLiteral("mapping_payee")] = QStringLiteral("^payee|^tiers|^.*Zahlungspflichtiger"); parameters[QStringLiteral("mapping_comment")] = QStringLiteral("^comment|^libell?|^d?tail|^info|^Vorgang.*"); parameters[QStringLiteral("mapping_amount")] = QStringLiteral("^value|^amount|^valeur|^montant|^credit|^debit|^Umsatz"); parameters[QStringLiteral("mapping_account")] = QStringLiteral("^Konto"); parameters[QStringLiteral("mapping_sign")] = QStringLiteral("^sign|^sens"); parameters[QStringLiteral("mapping_unit")] = QStringLiteral("^Währung"); parameters[QStringLiteral("mapping_debit")] = QStringLiteral("^S"); parameters[QStringLiteral("automatic_search_columns")] = 'Y'; parameters[QStringLiteral("automatic_search_header")] = 'Y'; imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.setName"), account.setName(QStringLiteral("411958")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("658.88")) } { // Test import with footer SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/footer.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("automatic_search_columns")] = 'N'; parameters[QStringLiteral("columns_positions")] = QStringLiteral("date|mode|payee|amount|"); imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.setName(QStringLiteral("footer")), true) SKGTESTERROR(QStringLiteral("ACCOUNT.load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-2")) } { // Test import separator comma SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportcsv/commas_2.csv")); QMap parameters = imp1.getImportParameters(); parameters[QStringLiteral("automatic_search_columns")] = 'N'; parameters[QStringLiteral("header_position")] = QStringLiteral("1"); parameters[QStringLiteral("columns_positions")] = QStringLiteral("|date||payee|amount||||mode"); imp1.setImportParameters(parameters); SKGTESTERROR(QStringLiteral("CSV.importFile"), imp1.importFile(), true) } } // End test SKGENDTEST() -} +} // NOLINT(readability/fn_size) diff --git a/tests/skgbankmodelertest/skgtestimportgnucash.cpp b/tests/skgbankmodelertest/skgtestimportgnucash.cpp index da469a5c8..91bcfd78a 100644 --- a/tests/skgbankmodelertest/skgtestimportgnucash.cpp +++ b/tests/skgbankmodelertest/skgtestimportgnucash.cpp @@ -1,594 +1,593 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file is a test script. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgtestmacro.h" #include "skgbankincludes.h" #include "skgimportexportmanager.h" /** * The main function of the unit test * @param argc the number of arguments * @param argv the list of arguments */ int main(int argc, char** argv) { Q_UNUSED(argc) Q_UNUSED(argv) // Init test SKGINITTEST(true) { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager impmissing(&document1, QUrl::fromLocalFile(QStringLiteral("/not-existing/missingfile.uncompressed"))); SKGTESTERROR(QStringLiteral("imp1.importFile"), impmissing.importFile(), false) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/test_data.uncompressed")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Accounts Receivable")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("231.11")) } } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/all.uncompressed")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("COMPTE COURANT")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-700")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("COMPTE EPARGNE")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("250")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Actif")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-420")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("TITRE")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("520")) } } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/initial_balance.uncompressed")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CCP")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("100")) } int nb = 0; SKGTESTERROR(QStringLiteral("GNUCASH:getNbObjects(unit, PRIMARY)"), document1.getNbObjects(QStringLiteral("unit"), QStringLiteral("t_type='1'"), nb), true) SKGTEST(QStringLiteral("GNUCASH:getNbObjects(unit, PRIMARY)"), nb, 1) SKGTESTERROR(QStringLiteral("GNUCASH:getNbObjects(unit, SHARE)"), document1.getNbObjects(QStringLiteral("unit"), QStringLiteral("t_type='S'"), nb), true) SKGTEST(QStringLiteral("GNUCASH:getNbObjects(unit, SHARE)"), nb, 13) } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/transfer-CPP-vers-PEE.uncompressed")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CCP")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("750")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CEL Donald")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("150")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("PEEs")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("190")) } { int nb = 0; SKGTESTERROR(QStringLiteral("GNUCASH:getNbObjects"), document1.getNbObjects(QStringLiteral("category"), QLatin1String(""), nb), true) SKGTEST(QStringLiteral("GNUCASH:getNbObjects"), nb, 27) } } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/transfer-PEE-vers-CCP.uncompressed")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CCP")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1140")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CEL Donald")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("150")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("PEEs")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("100")) } { SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGCategoryObject cat; SKGTESTERROR(QStringLiteral("GNUCASH.createPathCategory"), SKGCategoryObject::createPathCategory(&document1, QStringLiteral("Depenses > Frais bancaires"), cat), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(cat.getCurrentAmount()), QStringLiteral("-10")) } SKGTESTERROR(QStringLiteral("DOC:dump"), document1.dump(DUMPCATEGORY), true) } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/interet-revenue-frais.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CCP")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("750")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CEL Donald")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("150")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("PEEs")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("640")) } } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/interet-revenue-frais-emprunt.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("CCP")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("250")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Emprunts")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-99620")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Maison")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("100000")) } } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/rapprochement-incorrect.uncompressed")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { int nb = 0; SKGTESTERROR(QStringLiteral("GNUCASH:getNbObjects"), document1.getNbObjects(QStringLiteral("operation"), QStringLiteral("t_status='Y'"), nb), true) SKGTEST(QStringLiteral("GNUCASH:getNbObjects"), nb, 7) } } { // Test import GNUCASH SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/scheduled-action.uncompressed")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGObjectBase::SKGListSKGObjectBase recurrentoperations; SKGTESTERROR(QStringLiteral("GNUCASH:SKGListSKGObjectBase"), document1.getObjects(QStringLiteral("recurrentoperation"), QLatin1String(""), recurrentoperations), true) SKGTEST(QStringLiteral("GNUCASH:nb"), recurrentoperations.count(), 1) if (recurrentoperations.count() == 1) { SKGRecurrentOperationObject recu(recurrentoperations.at(0)); SKGTEST(QStringLiteral("GNUCASH:getDate"), recu.getDate().toString(), QDate(2010, 03, 02).toString()) SKGTEST(QStringLiteral("GNUCASH:getAutoWriteDays"), recu.getAutoWriteDays(), 10) SKGTEST(QStringLiteral("GNUCASH:getPeriodIncrement"), recu.getPeriodIncrement(), 1) SKGTEST(QStringLiteral("GNUCASH:getPeriodUnit"), static_cast(recu.getPeriodUnit()), static_cast(SKGRecurrentOperationObject::MONTH)) SKGTEST(QStringLiteral("GNUCASH:getWarnDays"), recu.getWarnDays(), 0) SKGTESTBOOL("GNUCASH:isAutoWriteEnabled", recu.isAutoWriteEnabled(), false) SKGTESTBOOL("GNUCASH:isWarnEnabled", recu.isWarnEnabled(), false) SKGTESTBOOL("GNUCASH:hasTimeLimit", recu.hasTimeLimit(), false) } } } { // Test import GNUCASH - bug 228808 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/228808.gnc")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGObjectBase::SKGListSKGObjectBase recurrentoperations; SKGTESTERROR(QStringLiteral("GNUCASH:SKGListSKGObjectBase"), document1.getObjects(QStringLiteral("recurrentoperation"), QStringLiteral("1=1 ORDER BY d_date"), recurrentoperations), true) SKGTEST(QStringLiteral("GNUCASH:nb"), recurrentoperations.count(), 2) if (recurrentoperations.count() == 2) { SKGRecurrentOperationObject recu(recurrentoperations.at(0)); SKGOperationObject op; SKGTESTERROR(QStringLiteral("GNUCASH:getParentOperation"), recu.getParentOperation(op), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), op.getCurrentAmount(), -50) recu = recurrentoperations.at(1); SKGTESTERROR(QStringLiteral("GNUCASH:getParentOperation"), recu.getParentOperation(op), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), op.getCurrentAmount(), -18.75) } } } { // Test import GNUCASH - bug 228904 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/228904.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGObjectBase::SKGListSKGObjectBase recurrentoperations; SKGTESTERROR(QStringLiteral("GNUCASH:SKGListSKGObjectBase"), document1.getObjects(QStringLiteral("recurrentoperation"), QStringLiteral("1=1 ORDER BY d_date"), recurrentoperations), true) SKGTEST(QStringLiteral("GNUCASH:nb"), recurrentoperations.count(), 3) } } { // Test import 228901 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/228901.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Compte emprunt")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-9476.01")) } // Test second import { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/228901.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } } { // Test import 234608 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/234608.gnc")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } } { // Test import 234597 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/234597.gnc")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { int nb = 0; SKGTESTERROR(QStringLiteral("GNUCASH:getNbObjects"), document1.getNbObjects(QStringLiteral("account"), QLatin1String(""), nb), true) SKGTEST(QStringLiteral("GNUCASH:getNbObjects"), nb, 11) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Compte cheques")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-260")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Compte cheques (2)")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-40")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Argent du porte-monnaie")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("0")) } } { // Test import 243738 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/243738.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } } { // Test import without book SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/without_book.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } } { // Test import 302388 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/302388.gnc")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), false) } } { // Test import 325174 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/325174.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } int nb = 0; SKGTESTERROR(QStringLiteral("GNUCASH:getNbObjects(account)"), document1.getNbObjects(QStringLiteral("account"), QLatin1String(""), nb), true) SKGTEST(QStringLiteral("GNUCASH:getNbObjects(account)"), nb, 11) } { // Test import wallet SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/wallet.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Espece")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGBankObject bank; SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.getBank(bank), true) SKGTEST(QStringLiteral("GNUCASH:getName"), bank.getName(), QLatin1String("")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Courant")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGBankObject bank; SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.getBank(bank), true) SKGTEST(QStringLiteral("GNUCASH:getName"), bank.getName(), QStringLiteral("GNUCASH")) } } { // 407257 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_GNUCASH"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportgnucash/407257.gnucash")); SKGTESTERROR(QStringLiteral("GNUCASH.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("AccountName (Nom de compte)")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) SKGTEST(QStringLiteral("GNUCASH:getNumber"), account.getNumber(), QStringLiteral("123_AccountCode(CodeCompte)")) SKGTEST(QStringLiteral("GNUCASH:getComment"), account.getComment(), QStringLiteral("DescriptionAccount(Compte123)")) SKGUnitObject unit; SKGTESTERROR(QStringLiteral("GNUCASH.getUnit"), account.getUnit(unit), true) SKGTEST(QStringLiteral("GNUCASH:getName"), unit.getName(), QStringLiteral("Canadian Dollar (CAD)")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("GNUCASH.setName"), account.setName(QStringLiteral("Account2Name(no transaction)")), true) SKGTESTERROR(QStringLiteral("GNUCASH.load"), account.load(), true) } - } // End test SKGENDTEST() } diff --git a/tests/skgbankmodelertest/skgtestimportqif1.cpp b/tests/skgbankmodelertest/skgtestimportqif1.cpp index 9db18f466..e36c55712 100644 --- a/tests/skgbankmodelertest/skgtestimportqif1.cpp +++ b/tests/skgbankmodelertest/skgtestimportqif1.cpp @@ -1,847 +1,847 @@ /*************************************************************************** * Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr * * * * 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 * ***************************************************************************/ /** @file * This file is a test script. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgtestmacro.h" #include "skgbankincludes.h" #include "skgimportexportmanager.h" /** * The main function of the unit test * @param argc the number of arguments * @param argv the list of arguments */ int main(int argc, char** argv) { Q_UNUSED(argc) Q_UNUSED(argv) // Init test SKGINITTEST(true) QDate d(1970, 1, 1); { // Test import QIF 1 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGBankObject bank(&document1); SKGAccountObject account; SKGUnitObject unit_euro(&document1); SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) SKGImportExportManager impmissing(&document1, QUrl::fromLocalFile(QStringLiteral("missingfile.qif"))); SKGTESTERROR(QStringLiteral("imp1.importFile"), impmissing.importFile(), false) SKGImportExportManager::getParameterDefaultValue(QStringLiteral("mapping_date")); // Creation bank SKGTESTERROR(QStringLiteral("BANK:setName"), bank.setName(QStringLiteral("CREDIT COOP")), true) SKGTESTERROR(QStringLiteral("BANK:setNumber"), bank.setNumber(QStringLiteral("0003")), true) SKGTESTERROR(QStringLiteral("BANK:save"), bank.save(), true) // Creation account SKGTESTERROR(QStringLiteral("BANK:addAccount"), bank.addAccount(account), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setName"), account.setName(QStringLiteral("Courant steph")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setNumber"), account.setNumber(QStringLiteral("12345P")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setAgencyNumber"), account.setAgencyNumber(QStringLiteral("98765")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setAgencyNumber"), account.setAgencyAddress(QStringLiteral("10 rue Dupon, 31000 TOULOUSE")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:save"), account.save(), true) // Creation unit SKGTESTERROR(QStringLiteral("UNIT:setName"), unit_euro.setName(QStringLiteral("euro")), true) SKGTESTERROR(QStringLiteral("UNIT:save"), unit_euro.save(), true) // Creation unitvalue SKGUnitValueObject unit_euro_val1; SKGTESTERROR(QStringLiteral("UNIT:addUnitValue"), unit_euro.addUnitValue(unit_euro_val1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setQuantity"), unit_euro_val1.setQuantity(1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setDate"), unit_euro_val1.setDate(d), true) SKGTESTERROR(QStringLiteral("UNITVALUE:save"), unit_euro_val1.save(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_BP_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/ref.qif")); SKGTESTERROR(QStringLiteral("QIF.setDefaultAccount"), imp1.setDefaultAccount(&account), true) SKGTESTERROR(QStringLiteral("QIF.setDefaultUnit"), imp1.setDefaultUnit(&unit_euro), true) SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } SKGTESTERROR(QStringLiteral("ACCOUNT:load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-935")) { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_BP_QIF"), err) SKGImportExportManager imp1(&document1); SKGTESTERROR(QStringLiteral("QIF.cleanBankImport"), imp1.cleanBankImport(), true) int out = 0; SKGTESTERROR(QStringLiteral("QIF.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/ref.qif")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } SKGTESTERROR(QStringLiteral("ACCOUNT:load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-935")) } { // Test import QIF 2 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGBankObject bank(&document1); SKGAccountObject account; SKGUnitObject unit_euro(&document1); SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) // Creation bank SKGTESTERROR(QStringLiteral("BANK:setName"), bank.setName(QStringLiteral("CREDIT COOP")), true) SKGTESTERROR(QStringLiteral("BANK:save"), bank.save(), true) // Creation account SKGTESTERROR(QStringLiteral("BANK:addAccount"), bank.addAccount(account), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setName"), account.setName(QStringLiteral("Courant Guillaume")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setNumber"), account.setNumber(QStringLiteral("98765A")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:save"), account.save(), true) // Creation unit SKGTESTERROR(QStringLiteral("UNIT:setName"), unit_euro.setName(QStringLiteral("euro")), true) SKGTESTERROR(QStringLiteral("UNIT:save"), unit_euro.save(), true) // Creation unitvalue SKGUnitValueObject unit_euro_val1; SKGTESTERROR(QStringLiteral("UNIT:addUnitValue"), unit_euro.addUnitValue(unit_euro_val1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setQuantity"), unit_euro_val1.setQuantity(1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setDate"), unit_euro_val1.setDate(d), true) SKGTESTERROR(QStringLiteral("UNITVALUE:save"), unit_euro_val1.save(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_BP_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/E0269787.qif")); SKGTESTERROR(QStringLiteral("QIF.setDefaultAccount"), imp1.setDefaultAccount(&account), true) SKGTESTERROR(QStringLiteral("QIF.setDefaultUnit"), imp1.setDefaultUnit(&unit_euro), true) SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGTESTERROR(QStringLiteral("QIF.cleanBankImport"), imp1.cleanBankImport(), true) int out = 0; SKGTESTERROR(QStringLiteral("QIF.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/E0269787.qif")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } SKGTESTERROR(QStringLiteral("ACCOUNT:load"), account.load(), true) SKGTEST(QStringLiteral("ACCOUNT:getCurrentAmount"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-1684.58")) // Check import with account retrieved from file SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) unit_euro = SKGUnitObject(&document1); { SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) // Creation unit SKGTESTERROR(QStringLiteral("UNIT:setName"), unit_euro.setName(QStringLiteral("euro")), true) SKGTESTERROR(QStringLiteral("UNIT:save"), unit_euro.save(), true) // Creation unitvalue SKGUnitValueObject unit_euro_val1; SKGTESTERROR(QStringLiteral("UNIT:addUnitValue"), unit_euro.addUnitValue(unit_euro_val1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setQuantity"), unit_euro_val1.setQuantity(1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setDate"), unit_euro_val1.setDate(d), true) SKGTESTERROR(QStringLiteral("UNITVALUE:save"), unit_euro_val1.save(), true) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/E0269787.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGTESTERROR(QStringLiteral("QIF.cleanBankImport"), imp1.cleanBankImport(), true) int out = 0; SKGTESTERROR(QStringLiteral("QIF.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/E0269787_bis.qif")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } // Check multi import SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) { SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/ref.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) // Double import SKGTESTERROR(QStringLiteral("QIF.cleanBankImport"), imp1.cleanBankImport(), true) int out = 0; SKGTESTERROR(QStringLiteral("QIF.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) } // Check import with account retrieved from files SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) { SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/ref.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGImportExportManager imp2(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/E0269787.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp2.importFile(), true) SKGTESTERROR(QStringLiteral("QIF.cleanBankImport"), imp1.cleanBankImport(), true) int out = 0; SKGTESTERROR(QStringLiteral("QIF.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/E0269787_ref.qif")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } // Check import qif multi accounts SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) { SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/E0269787_ref.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGTESTERROR(QStringLiteral("QIF.cleanBankImport"), imp1.cleanBankImport(), true) int out = 0; SKGTESTERROR(QStringLiteral("QIF.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) QStringList oResult; SKGTESTERROR(QStringLiteral("ACCOUNT:getDistinctValues"), document1.getDistinctValues(QStringLiteral("account"), QStringLiteral("t_name"), oResult), true) SKGTEST(QStringLiteral("ACCOUNT:oResult.size"), oResult.size(), 3) } } { // Test import QIF 2 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGBankObject bank(&document1); SKGAccountObject account; SKGUnitObject unit_euro(&document1); SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) // Creation bank SKGTESTERROR(QStringLiteral("BANK:setName"), bank.setName(QStringLiteral("ING")), true) SKGTESTERROR(QStringLiteral("BANK:save"), bank.save(), true) // Creation account SKGTESTERROR(QStringLiteral("BANK:addAccount"), bank.addAccount(account), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setName"), account.setName(QStringLiteral("ING")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:setNumber"), account.setNumber(QStringLiteral("ING")), true) SKGTESTERROR(QStringLiteral("ACCOUNT:save"), account.save(), true) // Creation unit SKGTESTERROR(QStringLiteral("UNIT:setName"), unit_euro.setName(QStringLiteral("euro")), true) SKGTESTERROR(QStringLiteral("UNIT:save"), unit_euro.save(), true) // Creation unitvalue SKGUnitValueObject unit_euro_val1; SKGTESTERROR(QStringLiteral("UNIT:addUnitValue"), unit_euro.addUnitValue(unit_euro_val1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setQuantity"), unit_euro_val1.setQuantity(1), true) SKGTESTERROR(QStringLiteral("UNITVALUE:setDate"), unit_euro_val1.setDate(d), true) SKGTESTERROR(QStringLiteral("UNITVALUE:save"), unit_euro_val1.save(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_ING_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/ing.qif")); SKGTESTERROR(QStringLiteral("QIF.setDefaultAccount"), imp1.setDefaultAccount(&account), true) SKGTESTERROR(QStringLiteral("QIF.setDefaultUnit"), imp1.setDefaultUnit(&unit_euro), true) SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGTESTERROR(QStringLiteral("QIF.cleanBankImport"), imp1.cleanBankImport(), true) int out = 0; SKGTESTERROR(QStringLiteral("QIF.findAndGroupTransfers"), imp1.findAndGroupTransfers(out), true) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/ing.qif")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } } { // Test import QIF MOTO SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_ING_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/moto.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("ACTIF Moto")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("26198.77")) } } { // Test import QIF MOTO with euro and franc SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) SKGUnitObject unit; SKGTESTERROR(QStringLiteral("SKGUnitObject::createCurrencyUnit"), SKGUnitObject::createCurrencyUnit(&document1, QStringLiteral("EUR"), unit), true) SKGTESTERROR(QStringLiteral("SKGUnitObject::createCurrencyUnit"), SKGUnitObject::createCurrencyUnit(&document1, QStringLiteral("FRF"), unit), true) SKGTESTERROR(QStringLiteral("FRANC.addOrModifyUnitValue"), document1.addOrModifyUnitValue(QStringLiteral("French Franc (FRF)"), QDate(1963, 1, 1), 1.0 / 6.55957), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_ING_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/moto.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("ACTIF Moto")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(static_cast(account.getCurrentAmount())), QStringLiteral("5009")) } } { // Test import QIF KMM SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_KMM_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/kmm-without-category.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("CCP")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-52.36")) } } { // Test import QIF KMM with category SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_KMM_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/kmm-with-category.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("CCP")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-52.36")) } } { // Test import QIF REMI in double to check merge SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_REMI_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/remi_2.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_REMI_QIF"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/remi_2.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("remi 2")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-1208.63")) } } { // Test import QIF REMI in double to check merge SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_DOUBLE"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/double.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("double")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-243")) } } { // Test import bug GNUCash 350286 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/350286.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("350286")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1057.37")) } } { // Test import bug GNUCash 393596 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/393596.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("393596")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("12.34")) } } { // Test import bug GNUCash 503166 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/503166.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("My Investments")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("20")) } } { // Test import bug GNUCash 392707 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/392707.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("392707")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1219.06")) } } { // Test import bug GNUCash 373584 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/373584.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("My Investments")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("1000000")) } } { // Test import bug 199818 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/199818.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("199818")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-345.64")) } } { // Test import bug 201316 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/201316.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGStringListList listTmp; SKGTESTERROR(QStringLiteral("QIF.executeSelectSqliteOrder"), document1.executeSelectSqliteOrder( QStringLiteral("SELECT TOTAL(f_CURRENTAMOUNT),t_status FROM v_operation_display GROUP BY t_status ORDER BY t_status"), listTmp), true); SKGTEST(QStringLiteral("QIF:listTmp.count"), listTmp.count(), 4) if (listTmp.count() == 4) { SKGTEST(QStringLiteral("QIF:listTmp.at(1).at(0)"), listTmp.at(1).at(0), QStringLiteral("-10")) SKGTEST(QStringLiteral("QIF:listTmp.at(2).at(0)"), listTmp.at(2).at(0), QStringLiteral("-100")) SKGTEST(QStringLiteral("QIF:listTmp.at(3).at(0)"), listTmp.at(3).at(0), QStringLiteral("-1000")) } } } { // Test import bug 201451 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/201451.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("liability")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-123.45")) } } { // Test import bug 214809 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/BNP_CC_virement.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("BNP CC")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("696.64")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("BNP CEL")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("500")) } } { // Test import bug 214851 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/Fortuneo PEA (Caisse).qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGImportExportManager imp2(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/Fortuneo PEA.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp2.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("BNP CC")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-3604")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("Fortuneo PEA")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("86.29")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("Fortuneo PEA (Caisse)")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("10319.5")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("Fortuneo Titres (Caisse)")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-6700")) } } { // Test import bug 215620 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/Cortal PEA.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGImportExportManager imp2(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/EADS.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp2.importFile(), true) } } { // Test import bug 216520 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/216520.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("BNP CC")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("11196.64")) } } { // Test import investment SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT_INIT"), err) SKGUnitObject unit; SKGTESTERROR(QStringLiteral("EUR.setName"), SKGUnitObject::createCurrencyUnit(&document1, QStringLiteral("Euro (EUR)"), unit), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/exp_inv.qif")); imp1.setCodec(QStringLiteral("UTF-8")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/exp_inv.qif")); exp1.setCodec(QStringLiteral("UTF-8")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } } { // Test transfer qif SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/t2.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("COURANT")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("4767.97")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("CODEVI")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("-4767.97")) } } { // 233930 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/233930.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } } { // Quicken SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/quicken.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } } { // 267996 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/267996.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGImportExportManager exp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("OUT")) % "/skgtestimportqif1/267996.csv")); SKGTESTERROR(QStringLiteral("QIF.exportFile"), exp1.exportFile(), true) } } { // 271708 SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.initialize()"), document1.initialize(), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("IMPORT"), err) { SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/271708/10Compte A.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/271708/20Compte B.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/271708/30Compte C.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } { SKGImportExportManager imp1(&document1, QUrl::fromLocalFile(SKGTest::getTestPath(QStringLiteral("IN")) % "/skgtestimportqif1/271708/40Compte D.qif")); SKGTESTERROR(QStringLiteral("QIF.importFile"), imp1.importFile(), true) } } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("10Compte A")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("0")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("20Compte B")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("10")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("30Compte C")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("20")) } { SKGAccountObject account(&document1); SKGTESTERROR(QStringLiteral("QIF.setName"), account.setName(QStringLiteral("40Compte D")), true) SKGTESTERROR(QStringLiteral("QIF.load"), account.load(), true) SKGTEST(QStringLiteral("QIF:getValue"), SKGServices::doubleToString(account.getCurrentAmount()), QStringLiteral("40")) } } // End test SKGENDTEST() -} +} // NOLINT(readability/fn_size)