diff --git a/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob.desktop b/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob.desktop index 5ba66500c..5a09f4b4d 100644 --- a/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob.desktop +++ b/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob.desktop @@ -1,110 +1,110 @@ [Desktop Entry] Name=Weboob backend Name[ca]=Dorsal del Weboob Name[ca@valencia]=Dorsal del Weboob Name[cs]=Podpůrná vrstva Weboob Name[de]=Weboob-Modul Name[en_GB]=Weboob backend Name[es]=Motor Weboob Name[fi]=Weboob-taustaosa Name[fr]=Moteur Weboob Name[gl]=Infraestrutura de weboob Name[it]=Motore Weboob Name[nl]=Weboob-backend Name[pl]=Silnik Weboob Name[pt]=Infra-estrutura do Weboob Name[sv]=Weboob-gränssnitt Name[tr]=Weboob arka ucu Name[uk]=Модуль Weboob Name[x-test]=xxWeboob backendxx Comment=An import backend for Skrooge using weboob.\n You must install weboob and configure correctly the boobank module before using this backend.\n\n Parameters:\n 1-The list of accounts to import separated by '|'. If you don't pass parameter then all accounts will be imported.\n Example: 123@ca|456@bp\n 2-The first date of imported operations in format YYYY-MM-DD.\n Example: 2017-09-25\nWarning:If you want to specify this parameter, you have to specify the first one too but it could be blank.\n Example: ,2017-09-25 Comment[ca]=Un dorsal d'importació per a l'Skrooge usant Weboob.\n Cal instal·lar el «weboob» i configurar correctament el mòdul «boobank» abans d'usar aquest dorsal.\n\n Paràmetres:\n 1-La llista de comptes a importar, separats «|». Si no es passa el paràmetre, s'importaran tots els comptes.\n Exemple: 123@ca|456@bp\n 2-La primera data de les operacions importades en format AAAA-MM-DD.\n Exemple: 2017-09-25\nAvís: Si voleu indicar aquest paràmetre, també cal indicar el primer, però pot estar en blanc.\n Exemple: ,2017-09-25 Comment[ca@valencia]=Un dorsal d'importació per a l'Skrooge usant Weboob.\n Cal instal·lar el «weboob» i configurar correctament el mòdul «boobank» abans d'usar aquest dorsal.\n\n Paràmetres:\n 1-La llista de comptes a importar, separats «|». Si no es passa el paràmetre, s'importaran tots els comptes.\n Exemple: 123@ca|456@bp\n 2-La primera data de les operacions importades en format AAAA-MM-DD.\n Exemple: 2017-09-25\nAvís: Si voleu indicar aquest paràmetre, també cal indicar el primer, però pot estar en blanc.\n Exemple: ,2017-09-25 Comment[en_GB]=An import backend for Skrooge using weboob.\n You must install weboob and configure correctly the boobank module before using this backend.\n\n Parameters:\n 1-The list of accounts to import separated by '|'. If you don't pass parameter then all accounts will be imported.\n Example: 123@ca|456@bp\n 2-The first date of imported operations in format YYYY-MM-DD.\n Example: 2017-09-25\nWarning:If you want to specify this parameter, you have to specify the first one too but it could be blank.\n Example: ,2017-09-25 Comment[es]=Un motor de importación para Skrooge que usa weboob.\n Debe instalar weboob y configurar correctamente el módulo «boobank» antes de usar este motor.\n\n Parámetros:\n 1 - La lista de cuentas a importar separada por «|». Si no pasa ningún parámetro se importarán todas las cuentas.\n Ejemplo: 123@ca|456@bp\n 2 - La primera fecha de las operaciones a importar en el formato AAAA-MM-DD.\n Ejemplo: 2017-09-25\nAdvertencia: Si usa este parámetro, debe usar también el primero, aunque puede dejarlo en blanco.\nEjemplo: ,2017-07-25 Comment[fi]=Weboopia käyttävä tuontitaustaosa Skroogeen.\nSinun on asennettava weboob ja asetettava boobank-moduuli oikein ennen tämän taustaosan käyttöä.\n\n Parametrit:\n1 – Tuotavien tilien luettelo ”|”-merkein erotettuna. Parametritta tuodaan kaikki tilit.\n Esimerkki: 123@ca|456@bp\n2 – Ensimmäisen tuotavan tapahtuma päivämäärä muodossa VVVV-KK-P.\n Esimerkki: 2017-09-25\nVaroitus: Jos annat tämän parametrin, myös ensimmäinen on annettava, mutta se voi olla tyhjä.\n Esimerkki: ,2017-09-25 Comment[fr]=Un moteur d'importation pour Skrooge utilisant weboob\n Vous devez installer weboob et configurer correctement le module boobank avant d'utiliser ce moteur.\n\n Paramètres : 1-La liste des comptes à importer séparés par '|'. Si vous ne passez pas de paramètre alors tous les comptes seront importés.\n Exemple: 123@ca|456@bp\n 2-La première date d'importation des opérations au format YYYY-MM-DD.\n Exemple: 2017-09-25\nAttention : Si vous voulez préciser ce paramètre, vous devez d'abord préciser le premier mais il peut être vide.\n Exemple: ,2017-09-25 Comment[gl]=Unha infraestrutura de importación para Skrooge que usa weboob.\nDebe instalar weboob e configurar correctamente o módulo boobank para poder usar esta infraestrutura.\n\n Parámetros:\n 1. A lista de contas para importar separadas por «|». Se non pasa o parámetro, importaranse todas as contas.\nPor exemplo: «123@ca|456@bp».\n 2. A primeira data das operacións importadas en formato AAAA-MM-DD.\n Por exemplo: 2017-09-25\nAviso: Se quere indicar este parámetro, ten que indicar tamén o primeiro, pero pode deixalo baleiro.\n Por exemplo: ,2017-09-25 Comment[it]=Un motore di importazione per Skrooge che utilizza weboob.\n Prima di usare questo motore devi installare weboob e configurare correttamente il modulo boobank.\n\n Parametri:\n l'elenco dei conti da importare separati da «|». Se non passi il parametro verranno importati tutti i conti.\n Esempio: 123@ca|456@bp\n 2-La prima data delle operazioni importate nel formato YYYY-MM-DD.\n Esempio: 2017-09-25\nAttenzione: se vuoi specificare questo parametro, devi specificare anche il primo, ma esso potrebbe risultare vuoto.\n Esempio: ,2017-09-25 Comment[nl]=Een backend voor importeren voor Skrooge met gebruik van weboob.\n U moet weboob installeren en de boobank-module juist configureren vóór deze backend te gebruiken.\n\n Parameters:\n 1-De lijst accounts om apart te importeren gescheiden door '|'. Als u geen parameter meegeeft dan worden alle accounts geïmporteerd.\n Voorbeeld: 123@ca|456@bp\n 2-De eerste datum van geïmporteerde bewerkingen in formaat YYYY-MM-DD.\n Voorbeeld: 2017-09-25\nWaarschuwing:Als u deze parameter wilt specificeren, dan moet u ook de eerste specificeren, maar die kan leeg zijn.\n Voorbeeld: ,2017-09-25 Comment[pl]=Silnik importu dla Skrooge używający weboob.\n Musisz wgrać weboob i ustawić poprawnie moduł boobank przed użyciem tego silnika.\n\n Parametr:\n Lista kont do zaimportowania oddzielonych '|'. Jeśli nie podasz tego parametru, to zostaną zaimportowane wszystkie konta.\n Na przykład: 123@ca|456@bp\n 2-Pierwsza data importowanych operacji zapisana jako RRRR-MM-DD.\n Przykład: 2017-09-25\nUwaga:Jeśli chcesz podać ten parametr, to musisz podać ten pierwszy także, lecz może on pozostać pustym.\n Przykład: ,2017-09-25 Comment[pt]=Uma infra-estrutura de importação para o Skrooge que usa o Weboob.\n Deverá instalar o Weboob e configurar correctamente o módulo 'boobank' antes de usar esta infra-estrutura.\n\n. Parâmetros:\n 1-A lista de contas a importar, separadas por '|'. Se não passar parâmetros, então serão importadas todas as contas.\n Exemplo: 123@ca|456@bp\n 2-A primeira data das operações importadas no formato AAAA-MM-DD. Exemplo: 2017-09-25\nAtenção: Se quiser indicar este parâmetro, também terá de indicar o primeiro, embora este possa estar em branco.\nExemplo: ,2017-09-25 Comment[sv]=Ett importgränssnitt för Skrooge som använder weboob.\nDu måste installera weboob och ställa in modulen boobank korrekt innan gränssnittet används.\n\nParameter:\n1: Listan med konton som ska importeras åtskilda av '|'. Om du inte skickar med parametern importeras alla konton.\nExempel: 123@ca|456@bp\n2: Det första datumet för importerade transaktioner på formatet ÅÅÅÅ-MM-DD.\nExempel: 2017-09-25\nVarning: Om du vill ange parametern måste den första också anges, men den kan vara tom.\nExempel: ,2017-09-25 Comment[uk]=Модуль імпортування даних до Skrooge з використанням weboob.\n Вам слід встановити weboob і налаштувати належним чином модуль boobank, перш ніж користуватися цим модулем обробки.\n\n Параметри:\n 1-список рахунків, дані яких слід імпортувати з відокремленням записів символом «|». Якщо ви не передасте параметр, буде імпортовано дані усіх рахунків.\n Приклад: 123@ca|456@bp\n 2-Перша дата імпортованих операцій у форматі РРРР-ММ-ДД.\n Приклад: 2017-09-25\nПопередження: якщо ви хочете задати цей параметр, вам слід також задати і перший параметр, але він може бути і порожнім.\n Приклад: ,2017-09-25 Comment[x-test]=xxAn import backend for Skrooge using weboob.\n You must install weboob and configure correctly the boobank module before using this backend.\n\n Parameters:\n 1-The list of accounts to import separated by '|'. If you don't pass parameter then all accounts will be imported.\n Example: 123@ca|456@bp\n 2-The first date of imported operations in format YYYY-MM-DD.\n Example: 2017-09-25\nWarning:If you want to specify this parameter, you have to specify the first one too but it could be blank.\n Example: ,2017-09-25xx Encoding=UTF-8 Icon=skrooge Type=Service X-KDE-ServiceTypes=Skrooge/Import/Backend X-Krunner-ID=weboob X-KDE-PluginInfo-Author=Stephane MANKOWSKI,miraks X-KDE-PluginInfo-Email=stephane@mankowski.fr X-KDE-PluginInfo-Name=weboob X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=https://skrooge.org/ X-KDE-PluginInfo-Category=Plugins X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true #The command line to get the list of accounts in the standard output, something like this: #id;balance #12345@bankname;123.45 #78900@bankname;789.00 # #The command must provide one line per account. This could be a csv or a text plain file. #Warning: The first line is ignored #%3 will be replaced by the password requeted interactively to the en user #This parameter is MANDATORY X-SKROOGE-getaccounts=PYTHONIOENCODING=utf_8 boobank ls -q -f csv -s id,label,balance 2>/dev/null |grep -v "If --auto-update" | grep -E "id;label;balance|%parameter1" #Regular expression to capture the account id of each accounts -#For the regexp syntax, you can consult this page: http://qt-project.org/doc/qt-4.8/qregexp.html +#For the regexp syntax, you can consult this page: https://doc.qt.io/qt-5/qregexp.html #If the account id is something like aa@bb then aa is the account identifier and bb the bank name. #This parameter is MANDATORY X-SKROOGE-getaccountid=([^;]*); #Regular expression to capture the account balance of each accounts #The account balance is used only during the first import to compute the initial balance of the account. #For an existing account with already some operations, this is used to check the account. #This parameter is not MANDATORY. X-SKROOGE-getaccountbalance=[^;]*;[^;]*;([^;]*) #Regular expression to capture the name of account #This parameter is not MANDATORY. X-SKROOGE-getaccountname=[^;]*;([^;]*) #The command line to get the csv operations list. #Example: #id;date;rdate;type;raw;category;label;amount #0@creditcooperatif;2013-04-12;2013-04-12;1;VIREMENT I.P.E.C.A. PREVOYANCE- VIRT IPECA - FRAIS DE SANTE;Not available;I.P.E.C.A. PREVOYANCE;16.80 #0@creditcooperatif;2013-04-12;2013-04-12;1;VIR C.P.A.M. TOULOUSE- 000000 131000017450;Not available;C.P.A.M. TOULOUSE;16.10 #0@creditcooperatif;2013-04-12;2013-04-12;3;CHEQUE NC 1962805;Not available;CHEQUE NC 1962805;-319.00 # #The command must provide one line per account. This must be csv file. # #In the command line: # %1 will be replaced by the account id [MANDATORY] # %2 will be replaced by the nb max operations to download [NOT MANDATORY] # %3 will be replaced by the password) [NOT MANDATORY] # %4 will be replaced by the begin date (format: YYYY-MM-DD) of operations [NOT MANDATORY] # #This parameter is not MANDATORY but if not used then getbulk must be used X-SKROOGE-getoperations=a="%parameter2" && b="%4" && m=$( [[ ${a} > ${b} ]] && echo "$a" || echo "$b" ) && PYTHONIOENCODING=utf_8 boobank -q -f csv history "%1" -s date,rdate,type,raw,label,amount --condition "rdate>$m OR date>$m OR rdate>$m 00:00:00 OR date>$m 00:00:00" -n 99999 %parameter3 2>/dev/null #The command line to get all csv files in one call. #Format of CSV files must be the same than for getoperations #If getbulk is declare then getoperations is ignored # #In the command line: # %1 will be replaced by the temporary directory [MANDATORY] # #This parameter is not MANDATORY but if not used then getoperations must be used #getbulk=bulkdownload "%1" #The csv columns. See CSV import settings for more detail. X-SKROOGE-csvcolumns=date2|date1|mode|comment|payee|amount #The operating systems supported (linux, windows, osx, ...) or blank for all (see QSysInfo::kernelType) X-SKROOGE-ossuppored=linux,freebsd,darwin diff --git a/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob_coming.desktop b/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob_coming.desktop index 0a67dc9a9..1c70217de 100644 --- a/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob_coming.desktop +++ b/plugins/import/skrooge_import_backend/backends/org.kde.skrooge-import-backend-weboob_coming.desktop @@ -1,107 +1,107 @@ [Desktop Entry] Name=Weboob coming backend Name[ca]=Dorsal per a entrades del Weboob Name[ca@valencia]=Dorsal per a entrades del Weboob Name[cs]=Podpůrná vrstva Weboob Name[en_GB]=Weboob coming backend Name[es]=Motor Weboob para opciones venideras Name[fr]=Moteur Weboob à venir Name[gl]=Infraestrutura de futuro de weboob Name[it]=Motore Weboob in arrivo Name[nl]=Weboob inkomende backend Name[pl]=Silnik Weboob nadchodzących Name[pt]=Infra-estrutura de adiantamento do Weboob Name[sv]=Weboob coming-gränssnitt Name[tr]=Weboob arka ucu Name[uk]=Модуль Weboob для майбутніх операцій Name[x-test]=xxWeboob coming backendxx Comment=An import backend for Skrooge using weboob for coming operation. This plugin can be used for card with deferred debit.\n You must install weboob and configure correctly the boobank module before using this backend.\n\n Parameter:\n The list of accounts to import separated by '|'. If you don't pass parameter then all accounts will be imported.\n Example: 123@ca|456@bp Comment[ca]=Un dorsal d'importació per a l'Skrooge usant Weboob per operacions entrants. Aquest connector es pot usar per a targetes amb càrrec diferit.\n Cal instal·lar el «weboob» i configurar correctament el mòdul «boobank» abans d'usar aquest dorsal.\n\n Paràmetre:\n La llista de comptes a importar, separats amb «|». Si no es passa el paràmetre, s'importaran tots els comptes.\n Exemple: 123@ca|456@bp Comment[ca@valencia]=Un dorsal d'importació per a l'Skrooge usant Weboob per operacions entrants. Aquest connector es pot usar per a targetes amb càrrec diferit.\n Cal instal·lar el «weboob» i configurar correctament el mòdul «boobank» abans d'usar aquest dorsal.\n\n Paràmetre:\n La llista de comptes a importar, separats amb «|». Si no es passa el paràmetre, s'importaran tots els comptes.\n Exemple: 123@ca|456@bp Comment[en_GB]=An import backend for Skrooge using weboob for coming operation. This plugin can be used for card with deferred debit.\n You must install weboob and configure correctly the boobank module before using this backend.\n\n Parameter:\n The list of accounts to import separated by '|'. If you don't pass parameter then all accounts will be imported.\n Example: 123@ca|456@bp Comment[es]=Un motor de importación para Skrooge que usa weboob para operaciones venideras. Este complemento se puede usar para tarjetas de débito diferido.\n Debe instalar weboob y configurar correctamente el módulo «boobank» antes de usar este motor.\n\n Parámetro:\n La lista de cuentas a importar separada por «|». Si no pasa ningún parámetro se importarán todas las cuentas.\n Ejemplo: 123@ca|456@bp Comment[fr]=Un moteur d'importation pour Skrooge utilisant weboob pour les opérations à venir\n Vous devez installer weboob et configurer correctement le module boobank avant d'utiliser ce moteur.\n\n Paramètres : La liste des comptes à importer séparés par '|'. Si vous ne passez pas de paramètre alors tous les comptes seront importés.\n Exemple: 123@ca|456@bp Comment[gl]=Unha infraestrutura de importación para Skrooge que usa weboob para operacións futuras. Este complemento pode usarse para tarxetas con débito en diferido.\nDebe instalar weboob e configurar correctamente o módulo boobank para poder usar esta infraestrutura.\n\n Parámetro:\n A lista de contas para importar separadas por «|». Se non pasa o parámetro, impórtanse todas as contas.\nPor exemplo: «123@ca|456@bp». Comment[it]=Un motore di importazione per Skrooge che utilizza weboob per le operazioni in arrivo. Questa estensione può essere usata per le carte con addebito differito.\n Prima di utilizzare questo motore devi installare weboob e configurare il modulo boobank.\n\n Parametro:\n l'elenco dei conti da importare separati da «|». Se non passi il parametro verranno importati tutti i conti.\n Esempio: 123@ca|456@bp Comment[nl]=Een backend voor importeren voor Skrooge met gebruik van weboob voor aankomende bewerkingen. Deze plug-in kan gebruikt worden voor verschoven debet. U moet weboob installeren en de boobank-module juist configureren alvorens deze backend te gebruiken.\n\n Parameter:\n De lijst accounts om apart te importeren gescheiden door '|'. Als u geen parameter meegeeft dan worden alle accounts geïmporteerd.\n Voorbeeld: 123@ca|456@bp Comment[pl]=Silnik importu dla Skrooge używający weboob do operacji przychodzących. Tej wtyczki można używać dla kart z odroczonym debetem.\n Musisz wgrać weboob i ustawić poprawnie moduł boobank przed użyciem tego silnika.\n\n Parametr:\n Lista kont do zaimportowania oddzielonych '|'. Jeśli nie podasz tego parametru, to zostaną zaimportowane wszystkie konta.\n Na przykład: 123@ca|456@bp Comment[pt]=Uma infra-estrutura de importação para o Skrooge que usa o Weboob para operações de adiantamento. Este 'plugin' poderá ser usado para cartões com débito pré-datado.\n Deverá instalar o Weboob e configurar correctamente o módulo 'boobank' antes de usar esta infra-estrutura.\n\n. Parâmetro:\n A lista de contas a importar, separadas por '|' Se não passar parâmetros, então serão importadas todas as contas.\n Exemplo: 123@ca|456@bp Comment[sv]=Ett importgränssnitt för Skrooge som använder weboob för framtida transaktioner. Insticksprogrammet kan användas för kreditkort med fakturering.\n Du måste installera weboob och ställa in modulen boobank korrekt innan gränssnittet används.\n\nParameter:\n Listan med konton som ska importeras åtskilda av '|'. Om du inte skickar med parametern importeras alla konton.\nExempel: 123@ca|456@bp Comment[uk]=Модуль імпортування даних до Skrooge з використанням weboob для майбутніх операцій. Цим додатком можна скористатися для карток із відкладеними видатками.\n Вам слід встановити weboob і налаштувати належним чином модуль boobank, перш ніж користуватися цим модулем обробки.\n\n Параметр:\n список рахунків, дані яких слід імпортувати з відокремленням записів символом «|». Якщо ви не передасте параметр, буде імпортовано дані усіх рахунків.\n Приклад: 123@ca|456@bp Comment[x-test]=xxAn import backend for Skrooge using weboob for coming operation. This plugin can be used for card with deferred debit.\n You must install weboob and configure correctly the boobank module before using this backend.\n\n Parameter:\n The list of accounts to import separated by '|'. If you don't pass parameter then all accounts will be imported.\n Example: 123@ca|456@bpxx Encoding=UTF-8 Icon=skrooge Type=Service X-KDE-ServiceTypes=Skrooge/Import/Backend X-Krunner-ID=weboob_coming X-KDE-PluginInfo-Author=Stephane MANKOWSKI,miraks X-KDE-PluginInfo-Email=stephane@mankowski.fr X-KDE-PluginInfo-Name=weboob X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=https://skrooge.org/ X-KDE-PluginInfo-Category=Plugins X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true #The command line to get the list of accounts in the standard output, something like this: #id;balance #12345@bankname;123.45 #78900@bankname;789.00 # #The command must provide one line per account. This could be a csv or a text plain file. #Warning: The first line is ignored #%3 will be replaced by the password requeted interactively to the en user #This parameter is MANDATORY X-SKROOGE-getaccounts=PYTHONIOENCODING=utf_8 boobank ls -q -f csv -s id,label,balance | grep -E "id;label;balance|%parameter1" #Regular expression to capture the account id of each accounts -#For the regexp syntax, you can consult this page: http://qt-project.org/doc/qt-4.8/qregexp.html +#For the regexp syntax, you can consult this page: https://doc.qt.io/qt-5/qregexp.html #If the account id is something like aa@bb then aa is the account identifier and bb the bank name. #This parameter is MANDATORY X-SKROOGE-getaccountid=([^;]*); #Regular expression to capture the account balance of each accounts #The account balance is used only during the first import to compute the initial balance of the account. #For an existing account with already some operations, this is used to check the account. #This parameter is not MANDATORY. X-SKROOGE-getaccountbalance=[^;]*;[^;]*;([^;]*) #Regular expression to capture the name of account #This parameter is not MANDATORY. X-SKROOGE-getaccountname=[^;]*;([^;]*) #The command line to get the csv operations list. #Example: #id;date;rdate;type;raw;category;label;amount #0@creditcooperatif;2013-04-12;2013-04-12;1;VIREMENT I.P.E.C.A. PREVOYANCE- VIRT IPECA - FRAIS DE SANTE;Not available;I.P.E.C.A. PREVOYANCE;16.80 #0@creditcooperatif;2013-04-12;2013-04-12;1;VIR C.P.A.M. TOULOUSE- 000000 131000017450;Not available;C.P.A.M. TOULOUSE;16.10 #0@creditcooperatif;2013-04-12;2013-04-12;3;CHEQUE NC 1962805;Not available;CHEQUE NC 1962805;-319.00 # #The command must provide one line per account. This must be csv file. # #In the command line: # %1 will be replaced by the account id [MANDATORY] # %2 will be replaced by the nb max operations to download [NOT MANDATORY] # %3 will be replaced by the password) [NOT MANDATORY] # %4 will be replaced by the begin date (format: YYYY-MM-DD) of operations [NOT MANDATORY] # #This parameter is not MANDATORY but if not used then getbulk must be used X-SKROOGE-getoperations=PYTHONIOENCODING=utf_8 boobank -q -f csv coming "%1" -s date,rdate,type,raw,label,amount --condition "rdate>%4 OR date>%4 OR rdate>%4 00:00:00 OR date>%4 00:00:00" -n 99999 %parameter2 2>/dev/null #The command line to get all csv files in one call. #Format of CSV files must be the same than for getoperations #If getbulk is declare then getoperations is ignored # #In the command line: # %1 will be replaced by the temporary directory [MANDATORY] # #This parameter is not MANDATORY but if not used then getoperations must be used #getbulk=bulkdownload "%1" #The csv columns. See CSV import settings for more detail. X-SKROOGE-csvcolumns=date2|date1|mode|comment|payee|amount #The operating systems supported (linux, windows, osx, ...) or blank for all (see QSysInfo::kernelType) X-SKROOGE-ossuppored=linux,freebsd,darwin diff --git a/plugins/import/skrooge_import_gsb/skgimportplugingsb.cpp b/plugins/import/skrooge_import_gsb/skgimportplugingsb.cpp index 7587ce673..5076eed1c 100644 --- a/plugins/import/skrooge_import_gsb/skgimportplugingsb.cpp +++ b/plugins/import/skrooge_import_gsb/skgimportplugingsb.cpp @@ -1,523 +1,521 @@ /*************************************************************************** * 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 GSB import / export. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgimportplugingsb.h" #include #include #include #include #include "skgbankincludes.h" #include "skgimportexportmanager.h" #include "skgobjectbase.h" #include "skgpayeeobject.h" #include "skgservices.h" #include "skgtraces.h" /** * This plugin factory. */ K_PLUGIN_FACTORY(SKGImportPluginGsbFactory, registerPlugin();) SKGImportPluginGsb::SKGImportPluginGsb(QObject* iImporter, const QVariantList& iArg) : SKGImportPlugin(iImporter) { SKGTRACEINFUNC(10) Q_UNUSED(iArg) } SKGImportPluginGsb::~SKGImportPluginGsb() = default; bool SKGImportPluginGsb::isImportPossible() { SKGTRACEINFUNC(10) return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("GSB")); } SKGError SKGImportPluginGsb::importFile() { if (m_importer == nullptr) { return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); } SKGError err; SKGTRACEINFUNCRC(2, err) // Initialisation // Open file KCompressionDevice file(m_importer->getLocalFileName(), KCompressionDevice::GZip); if (!file.open(QIODevice::ReadOnly)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); } else { QDomDocument doc; // Set the file without uncompression QString errorMsg; int errorLine = 0; int errorCol = 0; bool contentOK = doc.setContent(file.readAll(), &errorMsg, &errorLine, &errorCol); file.close(); // Get root QDomElement docElem = doc.documentElement(); if (!contentOK) { err.setReturnCode(ERR_ABORT).setMessage(i18nc("Error message", "%1-%2: '%3'", errorLine, errorCol, errorMsg)); } else { // Check version QDomElement general = docElem.firstChildElement(QStringLiteral("General")); if (general.isNull()) { err.setReturnCode(ERR_ABORT).setMessage(i18nc("Error message", "Bad version of Grisbi document. Version must be >= 0.6.0")); contentOK = false; } } if (!contentOK) { err.addError(ERR_INVALIDARG, i18nc("Error message", "Invalid XML content in file '%1'", m_importer->getFileName().toDisplayString())); } else { err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "GSB"), 10); QMap mapIdUnit; QMap mapIdBank; QMap mapIdAccount; QMap mapIdCategory; QMap mapIdOperation; QMap mapIdPayee; QMap mapIdMode; QMap mapIdBudgetCat; - // Specifications: http://wiki.grisbi.org/doku.php?id=docs:dev:format_gsb - // Step 1-Create units IFOK(err) { QDomNodeList currencyList = docElem.elementsByTagName(QStringLiteral("Currency")); int nb = currencyList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), nb); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement currency = currencyList.at(i).toElement(); // Creation of the units SKGUnitObject unitObj(m_importer->getDocument()); IFOKDO(err, unitObj.setName(getAttribute(currency, QStringLiteral("Na")))) IFOKDO(err, unitObj.setSymbol(getAttribute(currency, QStringLiteral("Co")))) IFOKDO(err, unitObj.setNumberDecimal(SKGServices::stringToInt(getAttribute(currency, QStringLiteral("Fl"))))) IFOKDO(err, unitObj.save()) mapIdUnit[getAttribute(currency, QStringLiteral("Nb"))] = unitObj; IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(1)) // Step 2-Create banks IFOK(err) { QDomNodeList bankList = docElem.elementsByTagName(QStringLiteral("Bank")); int nb = bankList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import banks"), nb); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement bank = bankList.at(i).toElement(); // Creation of the banks SKGBankObject bankObj(m_importer->getDocument()); IFOKDO(err, bankObj.setName(getAttribute(bank, QStringLiteral("Na")))) IFOKDO(err, bankObj.setNumber(getAttribute(bank, QStringLiteral("BIC")))) IFOKDO(err, bankObj.save()) mapIdBank[getAttribute(bank, QStringLiteral("Nb"))] = bankObj; IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(2)) // Step 3-Create accounts SKGBankObject bankDefault(m_importer->getDocument()); IFOKDO(err, bankDefault.setName(QStringLiteral("GRISBI"))) IFOKDO(err, bankDefault.save()) IFOK(err) { QDomNodeList accountList = docElem.elementsByTagName(QStringLiteral("Account")); int nb = accountList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import accounts"), nb); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement account = accountList.at(i).toElement(); // Creation of the account SKGAccountObject accountObj; SKGBankObject bank = mapIdBank[getAttribute(account, QStringLiteral("Bank"))]; if (!bank.exist()) { bank = bankDefault; } IFOKDO(err, bank.addAccount(accountObj)) IFOKDO(err, accountObj.setName(getAttribute(account, QStringLiteral("Name")))) IFOKDO(err, accountObj.setNumber(getAttribute(account, QStringLiteral("Bank_account_number")))) IFOKDO(err, accountObj.setComment(getAttribute(account, QStringLiteral("Comment")))) IFOKDO(err, accountObj.setAgencyAddress(getAttribute(account, QStringLiteral("Owner_address")))) IFOKDO(err, accountObj.setMinLimitAmount(SKGServices::stringToDouble(getAttribute(account, QStringLiteral("Minimum_authorised_balance"))))) IFOKDO(err, accountObj.minLimitAmountEnabled(accountObj.getMinLimitAmount() != 0.0)) IFOKDO(err, accountObj.setClosed(getAttribute(account, QStringLiteral("Closed_account")) != QStringLiteral("0"))) IFOKDO(err, accountObj.save()) IFOKDO(err, accountObj.setInitialBalance(SKGServices::stringToDouble(getAttribute(account, QStringLiteral("Initial_balance"))), mapIdUnit[getAttribute(account, QStringLiteral("Currency"))])) mapIdAccount[getAttribute(account, QStringLiteral("Number"))] = accountObj; IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(3)) // Step 4-Get payees IFOK(err) { QDomNodeList partyList = docElem.elementsByTagName(QStringLiteral("Party")); int nb = partyList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import payees"), nb); for (int i = 0; !err && i < nb; ++i) { // Get payee object QDomElement party = partyList.at(i).toElement(); SKGPayeeObject payeeObject; err = SKGPayeeObject::createPayee(m_importer->getDocument(), getAttribute(party, QStringLiteral("Na")), payeeObject); mapIdPayee[getAttribute(party, QStringLiteral("Nb"))] = payeeObject; IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(4)) // Step 5-Get payement mode IFOK(err) { QDomNodeList paymentList = docElem.elementsByTagName(QStringLiteral("Payment")); int nb = paymentList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import payment mode"), nb); for (int i = 0; !err && i < nb; ++i) { // Get mode object QDomElement payment = paymentList.at(i).toElement(); mapIdMode[getAttribute(payment, QStringLiteral("Number"))] = getAttribute(payment, QStringLiteral("Name")); IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(5)) // Step 6-Create categories IFOK(err) { QDomNodeList categoryList = docElem.elementsByTagName(QStringLiteral("Category")); int nb = categoryList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import categories"), nb); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement category = categoryList.at(i).toElement(); // Creation of the categories SKGCategoryObject catObj(m_importer->getDocument()); IFOKDO(err, catObj.setName(getAttribute(category, QStringLiteral("Na")))) IFOKDO(err, catObj.save()) QString id = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(category, QStringLiteral("Nb")))); mapIdCategory[id] = catObj; IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(6)) // Step 7-Create subcategories IFOK(err) { QDomNodeList categoryList = docElem.elementsByTagName(QStringLiteral("Sub_category")); int nb = categoryList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import categories"), nb); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement category = categoryList.at(i).toElement(); // Creation of the subcategories SKGCategoryObject catParentObj = mapIdCategory[SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(category, QStringLiteral("Nbc"))))]; SKGCategoryObject catObj; IFOKDO(err, catParentObj.addCategory(catObj)) IFOKDO(err, catObj.setName(getAttribute(category, QStringLiteral("Na")))) IFOKDO(err, catObj.save()) QString id = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(category, QStringLiteral("Nbc"))) + SKGServices::stringToInt(getAttribute(category, QStringLiteral("Nb")))); mapIdCategory[id] = catObj; IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(7)) // Step 8-Index of budget categories IFOK(err) { QDomNodeList budgetaryList = docElem.elementsByTagName(QStringLiteral("Budgetary")); int nb = budgetaryList.count(); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement budgetary = budgetaryList.at(i).toElement(); QString id = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(budgetary, QStringLiteral("Nb")))); mapIdBudgetCat[id] = getAttribute(budgetary, QStringLiteral("Na")); } } IFOK(err) { QDomNodeList budgetaryList = docElem.elementsByTagName(QStringLiteral("Sub_budgetary")); int nb = budgetaryList.count(); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement budgetary = budgetaryList.at(i).toElement(); QString id1 = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(budgetary, QStringLiteral("Nb")))); QString id = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(budgetary, QStringLiteral("Nbb"))) + SKGServices::stringToInt(getAttribute(budgetary, QStringLiteral("Nb")))); mapIdBudgetCat[id] = mapIdBudgetCat[id1] % OBJECTSEPARATOR % getAttribute(budgetary, QStringLiteral("Na")); } } IFOKDO(err, m_importer->getDocument()->stepForward(8)) // Step 9-Create transaction IFOK(err) { QDomNodeList transactionList = docElem.elementsByTagName(QStringLiteral("Transaction")); int nb = transactionList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement transaction = transactionList.at(i).toElement(); // Creation of the operation SKGOperationObject opObj; QString parentOpId = getAttribute(transaction, QStringLiteral("Mo")); if (parentOpId == QStringLiteral("0")) { SKGAccountObject account = mapIdAccount[getAttribute(transaction, QStringLiteral("Ac"))]; IFOKDO(err, account.addOperation(opObj, true)) IFOKDO(err, opObj.setDate(QDate::fromString(getAttribute(transaction, QStringLiteral("Dt")), QStringLiteral("MM/dd/yyyy")))) IFOKDO(err, opObj.setUnit(mapIdUnit[ getAttribute(transaction, QStringLiteral("Cu"))])) IFOKDO(err, opObj.setPayee(mapIdPayee[ getAttribute(transaction, QStringLiteral("Pa"))])) IFOKDO(err, opObj.setMode(mapIdMode[getAttribute(transaction, QStringLiteral("Pn"))])) IFOKDO(err, opObj.setNumber(getAttribute(transaction, QStringLiteral("Pc")))) IFOKDO(err, opObj.setComment(getAttribute(transaction, QStringLiteral("No")))) IFOKDO(err, opObj.setImported(true)) IFOKDO(err, opObj.setImportID("GSB-" % getAttribute(transaction, QStringLiteral("Nb")))) IFOKDO(err, opObj.setStatus(getAttribute(transaction, QStringLiteral("Re")) == QStringLiteral("0") ? SKGOperationObject::NONE : SKGOperationObject::CHECKED)) IFOKDO(err, opObj.setGroupOperation(mapIdOperation[getAttribute(transaction, QStringLiteral("Trt"))])) IFOKDO(err, opObj.save()) } else { opObj = mapIdOperation[parentOpId]; } // Budgetary allocation IFOK(err) { int sbu = SKGServices::stringToInt(getAttribute(transaction, QStringLiteral("Sbu"))); QString id = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(transaction, QStringLiteral("Bu"))) + qMax(sbu, 0)); QString buCat = mapIdBudgetCat[id]; if (!buCat.isEmpty()) { err = opObj.setProperty(i18nc("Noun", "Budgetary allocation"), buCat); IFOKDO(err, opObj.save()) } } if (getAttribute(transaction, QStringLiteral("Br")) == QStringLiteral("0")) { SKGSubOperationObject subObj; IFOKDO(err, opObj.addSubOperation(subObj)) QString id = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(transaction, QStringLiteral("Ca"))) + SKGServices::stringToInt(getAttribute(transaction, QStringLiteral("Sca")))); IFOKDO(err, subObj.setCategory(mapIdCategory[id])) IFOKDO(err, subObj.setComment(getAttribute(transaction, QStringLiteral("No")))) IFOKDO(err, subObj.setQuantity(SKGServices::stringToDouble(getAttribute(transaction, QStringLiteral("Am"))))) IFOKDO(err, subObj.save()) } // Fiscal year IFOK(err) { QString fiscalYear = getAttribute(transaction, QStringLiteral("Vo")); if (!fiscalYear.isEmpty()) { err = opObj.setProperty(i18nc("Noun", "Fiscal year"), fiscalYear); IFOKDO(err, opObj.save()) } } if (parentOpId == QStringLiteral("0")) { mapIdOperation[getAttribute(transaction, QStringLiteral("Nb"))] = opObj; } if (!err && i % 500 == 0) { err = m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")); } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(9)) // Step 10-Create scheduled transaction IFOK(err) { QDomNodeList scheduledList = docElem.elementsByTagName(QStringLiteral("Scheduled")); int nb = scheduledList.count(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import scheduled operations"), nb); for (int i = 0; !err && i < nb; ++i) { // Get account object QDomElement transaction = scheduledList.at(i).toElement(); // Creation of the operation SKGAccountObject account = mapIdAccount[getAttribute(transaction, QStringLiteral("Ac"))]; SKGOperationObject opObj; IFOKDO(err, account.addOperation(opObj, true)) QDate firstDate = QDate::fromString(getAttribute(transaction, QStringLiteral("Dt")), QStringLiteral("MM/dd/yyyy")); IFOKDO(err, opObj.setDate(firstDate)) IFOKDO(err, opObj.setUnit(mapIdUnit[ getAttribute(transaction, QStringLiteral("Cu"))])) IFOKDO(err, opObj.setPayee(mapIdPayee[ getAttribute(transaction, QStringLiteral("Pa"))])) IFOKDO(err, opObj.setMode(mapIdMode[getAttribute(transaction, QStringLiteral("Pn"))])) IFOKDO(err, opObj.setNumber(getAttribute(transaction, QStringLiteral("Pc")))) IFOKDO(err, opObj.setComment(getAttribute(transaction, QStringLiteral("No")))) IFOKDO(err, opObj.setImported(true)) IFOKDO(err, opObj.setImportID("GSB-" % getAttribute(transaction, QStringLiteral("Nb")))) IFOKDO(err, opObj.setTemplate(true)) IFOKDO(err, opObj.save()) SKGSubOperationObject subObj; IFOKDO(err, opObj.addSubOperation(subObj)) QString id = SKGServices::intToString(10000 * SKGServices::stringToInt(getAttribute(transaction, QStringLiteral("Ca"))) + SKGServices::stringToInt(getAttribute(transaction, QStringLiteral("Sca")))); IFOKDO(err, subObj.setCategory(mapIdCategory[id])) IFOKDO(err, subObj.setComment(getAttribute(transaction, QStringLiteral("No")))) IFOKDO(err, subObj.setQuantity(SKGServices::stringToDouble(getAttribute(transaction, QStringLiteral("Am"))))) IFOKDO(err, subObj.save()) QString Tra = getAttribute(transaction, QStringLiteral("Tra")); if (Tra != QStringLiteral("0")) { // This is a transfer SKGOperationObject opObj2; IFOKDO(err, opObj.duplicate(opObj2, opObj.getDate(), true)) IFOKDO(err, opObj2.setImported(true)) IFOKDO(err, opObj2.setImportID("GSB-" % getAttribute(transaction, QStringLiteral("Nb")) % "_TR")) IFOKDO(err, opObj2.save()) SKGObjectBase::SKGListSKGObjectBase subObjs2; IFOKDO(err, opObj2.getSubOperations(subObjs2)) if (!err && !subObjs2.isEmpty()) { SKGSubOperationObject subObj2(subObjs2.at(0)); err = subObj2.setQuantity(-subObj.getQuantity()); IFOKDO(err, subObj2.save()) } // Group operations IFOKDO(err, opObj.setGroupOperation(opObj2)) IFOKDO(err, opObj.save()) } // Create the schedule SKGRecurrentOperationObject recuObj; IFOKDO(err, opObj.addRecurrentOperation(recuObj)) IFOKDO(err, recuObj.setAutoWriteDays(0)) IFOKDO(err, recuObj.autoWriteEnabled(getAttribute(transaction, QStringLiteral("Au")) != QStringLiteral("0"))) IFOK(err) { // text_frequency [] = { _("Once"), _("Weekly"), _("Monthly"), _("two months"), ("trimester"), _("Yearly"), _("Custom"), nullptr }; int occu = 1; SKGRecurrentOperationObject::PeriodUnit period = SKGRecurrentOperationObject::DAY; QString Pe = getAttribute(transaction, QStringLiteral("Pe")); if (Pe == QStringLiteral("0")) { period = SKGRecurrentOperationObject::MONTH; IFOKDO(err, recuObj.timeLimit(true)) IFOKDO(err, recuObj.setTimeLimit(1)) } else if (Pe == QStringLiteral("1")) { period = SKGRecurrentOperationObject::WEEK; } else if (Pe == QStringLiteral("2")) { period = SKGRecurrentOperationObject::MONTH; } else if (Pe == QStringLiteral("3")) { occu = 2; period = SKGRecurrentOperationObject::MONTH; } else if (Pe == QStringLiteral("4")) { occu = 3; period = SKGRecurrentOperationObject::MONTH; } else if (Pe == QStringLiteral("5")) { period = SKGRecurrentOperationObject::YEAR; } else if (Pe == QStringLiteral("6")) { // text_frequency_user [] = { _("Days"), _("Weeks"), _("Months"), _("Years"), nullptr }; occu = SKGServices::stringToInt(getAttribute(transaction, QStringLiteral("Pep"))); QString Pei = getAttribute(transaction, QStringLiteral("Pei")); if (Pei == QStringLiteral("0")) { period = SKGRecurrentOperationObject::DAY; } else if (Pei == QStringLiteral("1")) { period = SKGRecurrentOperationObject::WEEK; } else if (Pei == QStringLiteral("2")) { period = SKGRecurrentOperationObject::MONTH; } else { period = SKGRecurrentOperationObject::YEAR; } } IFOKDO(err, recuObj.setPeriodUnit(period)) IFOKDO(err, recuObj.setPeriodIncrement(occu)) QString Dtl = getAttribute(transaction, QStringLiteral("Dtl")); if (!err && !Dtl.isEmpty()) { IFOKDO(err, recuObj.timeLimit(true)) IFOKDO(err, recuObj.setTimeLimit(QDate::fromString(Dtl, QStringLiteral("MM/dd/yyyy")))) } } IFOKDO(err, recuObj.save()) if (!err && i % 500 == 0) { err = m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")); } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(10)) SKGENDTRANSACTION(m_importer->getDocument(), err) IFOKDO(err, m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE"))) } } return err; } QString SKGImportPluginGsb::getAttribute(const QDomElement& iElement, const QString& iAttribute) { QString val = iElement.attribute(iAttribute); if (val == QStringLiteral("(null)")) { val = QString(); } return val; } QString SKGImportPluginGsb::getMimeTypeFilter() const { return "*.gsb|" % i18nc("A file format", "Grisbi file"); } #include diff --git a/plugins/import/skrooge_import_pdf/skgimportpluginpdf.cpp b/plugins/import/skrooge_import_pdf/skgimportpluginpdf.cpp index 424761782..8c12b0962 100644 --- a/plugins/import/skrooge_import_pdf/skgimportpluginpdf.cpp +++ b/plugins/import/skrooge_import_pdf/skgimportpluginpdf.cpp @@ -1,260 +1,259 @@ /*************************************************************************** * 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 PDF import / export. - * http://jerome.girod.perso.sfr.fr/finance/pdfx.php * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgimportpluginpdf.h" #include #include #include #include #include #include #include "skgbankincludes.h" #include "skgimportexportmanager.h" #include "skgservices.h" #include "skgtraces.h" /** * This plugin factory. */ K_PLUGIN_FACTORY(SKGImportPluginPDFFactory, registerPlugin();) SKGImportPluginPDF::SKGImportPluginPDF(QObject* iImporter, const QVariantList& iArg) : SKGImportPlugin(iImporter) { SKGTRACEINFUNC(10) Q_UNUSED(iArg) } SKGImportPluginPDF::~SKGImportPluginPDF() = default; bool SKGImportPluginPDF::isImportPossible() { SKGTRACEINFUNC(10) return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("PDF")); } QString SKGImportPluginPDF::extract(const QStringList& iLine, const QString& iSyntax) { QString output; int currentIndex = -1; bool setpossible = true; // Interpret the syntax auto items = SKGServices::splitCSVLine(iSyntax, '|', false); for (const auto& item : items) { if (item.startsWith(QLatin1String("REGEXPCAP:"))) { QRegExp regexp(item.right(item.length() - 10)); if (output.isEmpty()) { for (const auto& line : iLine) { if (regexp.indexIn(line) > -1) { output = regexp.cap(1); break; } } } else { if (regexp.indexIn(output) > -1) { output = regexp.cap(1); } } } else if (item.startsWith(QLatin1String("REGEXP:"))) { setpossible = false; QRegExp regexp(item.right(item.length() - 7)); int nb = iLine.count(); for (int i = 0; i < nb; ++i) { if (regexp.indexIn(iLine.at(i)) > -1) { currentIndex = i; setpossible = true; break; } } } else if (item.startsWith(QLatin1String("LINEOFFSET:"))) { currentIndex += SKGServices::stringToInt(item.right(item.length() - 11)); if (currentIndex >= 0 && currentIndex < iLine.count()) { output = iLine.at(currentIndex); } } else if (item.startsWith(QLatin1String("SET:")) && setpossible) { QString s = item.right(item.length() - 4); if (s.contains(QLatin1String("%1"))) { output = s.arg(output); } else { output = s; } } } return output; } SKGError SKGImportPluginPDF::importFile() { 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", "PDF"), 2); IFOK(err) { // Open file IFOK(err) { // Extract text from PDF QString file = m_importer->getLocalFileName(); QTemporaryFile txtFile; txtFile.open(); QStringList args = QStringList() << file << txtFile.fileName(); QProcess p; p.start(QStringLiteral("pdftotext"), args); if (!p.waitForFinished(1000 * 60 * 2) || p.exitCode() != 0) { QString cmd = "pdftotext " % args.join(QStringLiteral(" ")); err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "The following command line failed with code %2:\n'%1'", cmd, p.exitCode())); } else { // Step 1 done IFOKDO(err, m_importer->getDocument()->stepForward(1)) // Read the text file QStringList lines; QTextStream stream(&txtFile); while (!stream.atEnd()) { // Read line lines.push_back(stream.readLine()); } // Search extractors QStringList listOfExtractors; bool found = false; QString a = QStringLiteral("skrooge/extractors"); const auto dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, a, QStandardPaths::LocateDirectory); for (const auto& dir : dirs) { QDirIterator it(dir, QStringList() << QStringLiteral("*.extractor")); while (it.hasNext() && !found) { // Read extractor QString fileName = it.next(); QString extractor = QFileInfo(fileName).baseName().toUpper(); listOfExtractors.push_back(extractor); QHash< QString, QString > properties; err = SKGServices::readPropertyFile(fileName, properties); IFOK(err) { // Check if this extractor is done for this file QString payee = extract(lines, properties[QStringLiteral("payee")]); if (!payee.isEmpty()) { // Search the date QString date = extract(lines, properties[QStringLiteral("date")]); QString dateFormat = properties[QStringLiteral("dateformat")]; auto d = QDate::fromString(date, dateFormat); if (!d.isValid()) { d = QDate::fromString(date); if (!d.isValid()) { SKGTRACE << "WARNING: Impossible to parse the date [" << date << "] with [" << dateFormat << "]" << endl; } } if (!dateFormat.contains(QStringLiteral("yyyy")) && d.year() < 2000) { d = d.addYears(100); } // Search the amount double amount = SKGServices::stringToDouble(extract(lines, properties[QStringLiteral("amount")])); // Search the comment QString comment = extract(lines, properties[QStringLiteral("comment")]); // Search the number QString number = extract(lines, properties[QStringLiteral("number")]); // Search the mode QString mode = extract(lines, properties[QStringLiteral("mode")]); // Get account SKGAccountObject account; SKGOperationObject act; m_importer->getDocument()->getObject(QStringLiteral("v_account_display"), QStringLiteral("t_close='N' AND t_type='C' ORDER BY i_NBOPERATIONS DESC LIMIT 1"), act); if (act.exist()) { account = act; IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Using account '%1' for import", account.getName()))) } else { IFOKDO(err, m_importer->getDefaultAccount(account)) } if (d.isValid() && !qFuzzyCompare(1 + amount, 1.0)) { // Get unit SKGUnitObject unit; IFOKDO(err, m_importer->getDefaultUnit(unit)) // Create operation SKGOperationObject operation; IFOKDO(err, account.addOperation(operation, true)) IFOKDO(err, operation.setDate(d)) IFOKDO(err, operation.setUnit(unit)) SKGPayeeObject payeeObj; IFOKDO(err, SKGPayeeObject::createPayee(m_importer->getDocument(), payee, payeeObj)) IFOKDO(err, operation.setPayee(payeeObj)) IFOKDO(err, operation.setComment(comment)) IFOKDO(err, operation.setMode(mode)) IFOKDO(err, operation.setImportID(QStringLiteral("PDF-") % extractor % QStringLiteral("-") % number)) // This is normal. PDF inport is for only one operation, so no check if already imported IFOKDO(err, operation.setAttribute(QStringLiteral("t_imported"), QStringLiteral("Y"))) IFOKDO(err, operation.save(false)) SKGSubOperationObject subop; IFOKDO(err, operation.addSubOperation(subop)) IFOKDO(err, subop.setComment(comment)) IFOKDO(err, subop.setQuantity(-amount)) IFOKDO(err, subop.save(false, false)) // Add file IFOKDO(err, err = operation.setProperty(i18n("Invoice"), file, file)) found = true; } } } } } if (!found) { IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Invoice %1 has not been imported because it is not recognized (List of recognized extractors: %2).", file, listOfExtractors.join(',')), SKGDocument::Error)) } // Step 2 done IFOKDO(err, m_importer->getDocument()->stepForward(2)) } } } SKGENDTRANSACTION(m_importer->getDocument(), err) return err; } QString SKGImportPluginPDF::getMimeTypeFilter() const { return "*.pdf|" % i18nc("A file format", "PDF file (invoice)"); } #include diff --git a/plugins/import/skrooge_import_qif/skgimportpluginqif.cpp b/plugins/import/skrooge_import_qif/skgimportpluginqif.cpp index 36e48ab21..4cd09387a 100644 --- a/plugins/import/skrooge_import_qif/skgimportpluginqif.cpp +++ b/plugins/import/skrooge_import_qif/skgimportpluginqif.cpp @@ -1,1258 +1,1254 @@ /*************************************************************************** * 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 QIF import / export. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgimportpluginqif.h" #include #include #include #include #include #include "skgbankincludes.h" #include "skgimportexportmanager.h" #include "skgservices.h" #include "skgtraces.h" /** * Opening balance string */ #define OPENINGBALANCE QStringLiteral("Opening Balance") /** * This plugin factory. */ K_PLUGIN_FACTORY(SKGImportPluginQifFactory, registerPlugin();) SKGImportPluginQif::SKGImportPluginQif(QObject* iImporter, const QVariantList& iArg) : SKGImportPlugin(iImporter) { SKGTRACEINFUNC(10) Q_UNUSED(iArg) m_importParameters[QStringLiteral("date_format")] = QString(); m_exportParameters[QStringLiteral("uuid_of_selected_accounts_or_operations")] = QString(); } SKGImportPluginQif::~SKGImportPluginQif() = default; bool SKGImportPluginQif::isImportPossible() { SKGTRACEINFUNC(10) return isExportPossible(); } SKGError SKGImportPluginQif::importFile() { if (m_importer == nullptr) { return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); } SKGError err; SKGTRACEINFUNCRC(2, err) - // Info for QIF format: - // http://mb-net.net/Debian/src/gnucash/gnucash-2.2.6/src/import-export/qif-import/file-format.txt - // http://web.intuit.com/support/quicken/docs/d_qif.html - // Begin transaction err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "QIF"), 3); IFOK(err) { // Create account if needed QDateTime now = QDateTime::currentDateTime(); QString postFix = SKGServices::dateToSqlString(now); // Step 1 done IFOKDO(err, m_importer->getDocument()->stepForward(1)) // Open file 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()); } // load file in memory QStringList lines; QStringList dates; bool inWrongSection = false; bool inPriceSection = false; while (!stream.atEnd()) { // Read line // Check line if line is empty or is a commented QString line = stream.readLine().trimmed().toUtf8(); if (!line.isEmpty() && line[0] != '#') { lines.push_back(line); // Manage !Account section if (line.startsWith(QLatin1String("!"))) { inWrongSection = false; inPriceSection = false; } if (QString::compare(line, QStringLiteral("!account"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:cat"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:tag"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:class"), Qt::CaseInsensitive) == 0) { inWrongSection = true; } else if (QString::compare(line, QStringLiteral("!type:prices"), Qt::CaseInsensitive) == 0) { inPriceSection = true; } // We try to find automatically the date format if (!inWrongSection && line[0] == 'D') { dates.push_back(line.right(line.length() - 1)); } else if (inPriceSection) { QStringList vals = SKGServices::splitCSVLine(line, ','); if (vals.count() == 3) { dates.push_back(vals.at(2)); } } } } // close file file.close(); // Select dateformat QString dateFormat = m_importParameters.value(QStringLiteral("date_format")); if (dateFormat.isEmpty()) { dateFormat = SKGServices::getDateFormat(dates); // Automatic detection } 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 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) { SKGAccountObject* account = nullptr; SKGOperationObject currentOperation; SKGOperationObject payement; SKGPayeeObject currentPayee; SKGTrackerObject currentTracker; SKGUnitObject currentUnit; SKGSubOperationObject currentSubOperation; QDate currentOperationDate; QString lastTransferAccount; QList transferAccount; QList transferQuantity; bool addNextAmountToTransferQuantity = false; QString stringForHash; QString currentUnitForInvestment; QChar inSection = 'B'; bool currentOperationInitialized = false; bool latestSubCatMustBeRemoved = false; bool investmentAccount = false; bool div = false; bool automaticAccount = true; int quantityFactor = 1; double currentUnitPrice = 1; double checkOperationAmount = 0; double checkSuboperationsAmount = 0; bool openingbalancecreated = false; int nb = lines.size(); err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb); for (int i = 0; !err && i < nb; ++i) { QString line = lines.at(i); QString val; QChar op = line[0]; if (line.length() > 1) { val = line.right(line.length() - 1).trimmed(); } // Manage !Account section if (QString::compare(line, QStringLiteral("!type:bank"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:cash"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:ccard"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:oth a"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:oth l"), Qt::CaseInsensitive) == 0 || QString::compare(line, QStringLiteral("!type:invst"), Qt::CaseInsensitive) == 0) { inSection = 'B'; openingbalancecreated = false; investmentAccount = (QString::compare(val, QStringLiteral("type:invst"), Qt::CaseInsensitive) == 0); // Set type of account if (account == nullptr) { SKGAccountObject defAccount; err = m_importer->getDefaultAccount(defAccount); IFOKDO(err, defAccount.addOperation(currentOperation, true)) IFOK(err) account = new SKGAccountObject(defAccount); } if (!err && (account != nullptr)) { err = account->setType(QString::compare(line, QStringLiteral("!type:bank"), Qt::CaseInsensitive) == 0 ? SKGAccountObject::CURRENT : (QString::compare(line, QStringLiteral("!type:ccard"), Qt::CaseInsensitive) == 0 ? SKGAccountObject::CREDITCARD : (QString::compare(line, QStringLiteral("!type:invst"), Qt::CaseInsensitive) == 0 ? SKGAccountObject::INVESTMENT : (QString::compare(line, QStringLiteral("!type:oth a"), Qt::CaseInsensitive) == 0 ? SKGAccountObject::ASSETS : SKGAccountObject::OTHER)))); IFOKDO(err, account->save()) } } else if (QString::compare(line, QStringLiteral("!account"), Qt::CaseInsensitive) == 0) { inSection = 'A'; openingbalancecreated = false; automaticAccount = false; } else if (QString::compare(line, QStringLiteral("!type:cat"), Qt::CaseInsensitive) == 0) { inSection = 'C'; openingbalancecreated = false; IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Categories found and imported"))) } else if (QString::compare(line, QStringLiteral("!type:prices"), Qt::CaseInsensitive) == 0) { inSection = 'U'; openingbalancecreated = false; IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Units prices found and imported"))) } else if (QString::compare(line, QStringLiteral("!type:security"), Qt::CaseInsensitive) == 0) { inSection = 'S'; IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Units found and imported"))) } else if (QString::compare(line, QStringLiteral("!type:tag"), Qt::CaseInsensitive) == 0) { inSection = 'T'; IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Trackers found and imported"))) } else if (line.at(0) == '!') { inSection = '?'; openingbalancecreated = false; } else if (inSection == 'U') { // Unit value creation openingbalancecreated = false; QStringList vals = SKGServices::splitCSVLine(line, ','); if (vals.count() == 3 && !vals.at(0).isEmpty()) { err = m_importer->getDocument()->addOrModifyUnitValue(vals.at(0), SKGServices::stringToTime(SKGServices::dateToSqlString(vals.at(2), dateFormat)).date(), SKGServices::stringToDouble(vals.at(1))); } } else if (inSection == 'T') { // Tracker creation if (op == 'N') { IFOKDO(err, SKGTrackerObject::createTracker(m_importer->getDocument(), val, currentTracker)) } else if (op == 'D') { IFOKDO(err, currentTracker.setComment(val)) IFOKDO(err, currentTracker.save()) } } else if (inSection == 'S') { // Unit creation if (op == 'N') { currentUnit = SKGUnitObject(m_importer->getDocument()); IFOKDO(err, currentUnit.setName(val)) IFOKDO(err, currentUnit.setSymbol(val)) IFOKDO(err, currentUnit.setType(SKGUnitObject::CURRENCY)) IFOKDO(err, currentUnit.setNumberDecimal(2)) IFOKDO(err, currentUnit.save()) } else if (op == 'S') { IFOKDO(err, currentUnit.setSymbol(val)) IFOKDO(err, currentUnit.save()) } else if (op == 'T') { if (QString::compare(val, QStringLiteral("stock"), Qt::CaseInsensitive) == 0) { IFOKDO(err, currentUnit.setType(SKGUnitObject::SHARE)) IFOKDO(err, currentUnit.setNumberDecimal(4)) IFOKDO(err, currentUnit.save()) } } } else if (inSection == 'C') { // Category creation openingbalancecreated = false; if (op == 'N') { SKGCategoryObject Category; val.replace('/', OBJECTSEPARATOR); val.replace(':', OBJECTSEPARATOR); err = SKGCategoryObject::createPathCategory(m_importer->getDocument(), val, Category); } } else if (inSection == 'A') { // Account creation openingbalancecreated = false; if (op == 'N') { // Check if the account already exist SKGAccountObject account2; err = SKGNamedObject::getObjectByName(m_importer->getDocument(), QStringLiteral("account"), val, account2); IFKO(err) { // Create account SKGBankObject bank(m_importer->getDocument()); err = bank.setName(i18nc("Noun", "Bank for import %1", postFix)); if (!err && bank.load().isFailed()) { err = bank.save(false); } IFOKDO(err, bank.addAccount(account2)) IFOKDO(err, account2.setName(val)) if (!err && account2.load().isFailed()) { err = account2.save(false); // Save only } } IFOK(err) { delete account; account = new SKGAccountObject(account2); } } else if (op == 'D') { if (account != nullptr) { err = account->setNumber(val); } } else if (op == 'T') { if (account != nullptr) { err = account->setType(val == QStringLiteral("Bank") ? SKGAccountObject::CURRENT : (val == QStringLiteral("CCard") ? SKGAccountObject::CREDITCARD : (val == QStringLiteral("Invst") ? SKGAccountObject::INVESTMENT : (val == QStringLiteral("Oth A") ? SKGAccountObject::ASSETS : SKGAccountObject::OTHER)))); } } else if (op == '^') { // ^ End of entry // save if (account != nullptr) { err = account->save(); } } } else if (inSection == 'B') { // Operation creation /* >>>> Items for Non-Investment Accounts <<<< DONE D Date DONE T Amount U Transaction amount (higher possible value than T) DONE C Cleared status DONE N Number (check or reference number) DONE P Payee/description DONE M Memo DONE A Address (up to 5 lines; 6th line is an optional message) DONE L Category (category/class or transfer/class) DONE S Category in split (category/class or transfer/class) DONE E Memo in split DONE $ Dollar amount of split % Percentage of split if percentages are used F Reimbursable business expense flag X Small Business extensions DONE ^ End of entry >>>> Items for Investment Accounts <<<< DONE D Date N Action DONE Y Security DONE I Price DONE Q Quantity (number of shares or split ratio) DONE T Transaction amount DONE C Cleared status P Text in the first line for transfers and reminders DONE M Memo O Commission L Account for the transfer $ Amount transferred ^ End of entry */ stringForHash += line; if (op == 'D') { // D Date /* Dates in US QIF files are usually in the format MM/DD/YY, although four-digit years are not uncommon. Dates sometimes occur without the slash separator, or using other separators in place of the slash, commonly '-' and '.'. US Quicken seems to be using the ' to indicate post-2000 two-digit years (such as 01/01'00 for Jan 1 2000). Some banks appear to be using a completely undifferentiated numeric QString formateed YYYYMMDD in downloaded QIF files. */ // Operation creation SKGUnitObject unit; IFOK(err) { if (account != nullptr) { err = account->addOperation(currentOperation, true); if (!openingbalancecreated) { double initBalance; account->getInitialBalance(initBalance, unit); } } else { SKGAccountObject defAccount; err = m_importer->getDefaultAccount(defAccount); IFOKDO(err, defAccount.addOperation(currentOperation, true)) if (!openingbalancecreated) { double initBalance; defAccount.getInitialBalance(initBalance, unit); } } currentOperationInitialized = true; } // Set date currentOperationDate = SKGServices::stringToTime(SKGServices::dateToSqlString(val, dateFormat)).date(); IFOKDO(err, currentOperation.setDate(currentOperationDate)) // Set unit IFOK(err) { // Create unit if needed // If an initial balance is existing for the account then we use the unit else we look for the most appropriate unit if (!unit.exist()) { err = m_importer->getDefaultUnit(unit, ¤tOperationDate); } IFOKDO(err, currentOperation.setUnit(unit)) } IFOK(err) currentOperation.save(); // Create suboperation IFOKDO(err, currentOperation.addSubOperation(currentSubOperation)) } else if (op == 'Y') { // Y Security if (!div) { currentUnitForInvestment = val; SKGUnitObject unit(m_importer->getDocument()); if (currentUnitForInvestment.isEmpty()) { IFOKDO(err, err = m_importer->getDefaultUnit(unit)) } else { IFOKDO(err, unit.setName(currentUnitForInvestment)) IFOKDO(err, unit.setSymbol(currentUnitForInvestment)) if (unit.load().isFailed()) { IFOKDO(err, unit.setType(investmentAccount ? SKGUnitObject::SHARE : SKGUnitObject::CURRENCY)) IFOKDO(err, unit.save(false)) } } IFOKDO(err, currentOperation.setUnit(unit)) } else { // For dividend, if comment is empty, we set the security in comment if (currentOperation.getComment().isEmpty()) { err = currentOperation.setComment(val); } } } else if (op == 'O') { // O Commission // Get previous quantity double quantity = SKGServices::stringToDouble(val); SKGObjectBase::SKGListSKGObjectBase subops; payement.getSubOperations(subops); if (!subops.isEmpty()) { SKGSubOperationObject subpayement(subops.at(0)); err = subpayement.setQuantity(subpayement.getQuantity() + quantity); IFOKDO(err, subpayement.save()) } SKGSubOperationObject subcommission; if (!payement.exist()) { // We have to create a new operation if (account != nullptr) { err = account->addOperation(payement, true); } else { SKGAccountObject defAccount; err = m_importer->getDefaultAccount(defAccount); IFOKDO(err, defAccount.addOperation(payement, true)) } IFOKDO(err, payement.setDate(currentOperationDate)) IFOK(err) { // If an initial balance is existing for the account then we use the unit else we look for the most appropriate unit SKGUnitObject unit; if ((account != nullptr) && !openingbalancecreated) { double initBalance; account->getInitialBalance(initBalance, unit); } if (!unit.exist()) { err = m_importer->getDefaultUnit(unit, ¤tOperationDate); } IFOKDO(err, payement.setUnit(unit)) } IFOKDO(err, payement.save()) } IFOKDO(err, payement.addSubOperation(subcommission)) IFOKDO(err, subcommission.setQuantity(-quantity)) IFOKDO(err, subcommission.save(false, false)) } else if (op == 'I') { // I Price currentUnitPrice = SKGServices::stringToDouble(val); if ((currentUnitPrice != 0.0) && !currentUnitForInvestment.isEmpty()) { err = m_importer->getDocument()->addOrModifyUnitValue(currentUnitForInvestment, currentOperationDate, currentUnitPrice); } } else if (op == 'N') { if (investmentAccount) { // N Action /* QIF N Line Notes ============ ===== Aktab Same as ShrsOut. AktSplit Same as StkSplit. Aktzu Same as ShrsIn. Buy Buy shares. BuyX Buy shares. Used with an L line. Cash Miscellaneous cash transaction. Used with an L line. CGMid Mid-term capital gains. CGMidX Mid-term capital gains. For use with an L line. CGLong Long-term capital gains. CGLongX Long-term capital gains. For use with an L line. CGShort Short-term capital gains. CGShortX Short-term capital gains. For use with an L line. ContribX Same as XIn. Used for tax-advantaged accounts. CvrShrt Buy shares to cover a short sale. CvrShrtX Buy shares to cover a short sale. Used with an L line. Div Dividend received. DivX Dividend received. For use with an L line. Errinerg Same as Reminder. Exercise Exercise an option. ExercisX Exercise an option. For use with an L line. Expire Mark an option as expired. (Uses D, N, Y & M lines) Grant Receive a grant of stock options. Int Same as IntInc. IntX Same as IntIncX. IntInc Interest received. IntIncX Interest received. For use with an L line. K.gewsp Same as CGShort. (German) K.gewspX Same as CGShortX. (German)2307068 Kapgew Same as CGLong. Kapitalgewinnsteuer.(German) KapgewX Same as CGLongX. Kapitalgewinnsteuer. (German) Kauf Same as Buy. (German) KaufX Same as BuyX. (German) MargInt Margin interest paid. MargIntX Margin interest paid. For use with an L line. MiscExp Miscellaneous expense. MiscExpX Miscellaneous expense. For use with an L line. MiscInc Miscellaneous income. MiscIncX Miscellaneous income. For use with an L line. ReinvDiv Reinvested dividend. ReinvInt Reinvested interest. ReinvLG Reinvested long-term capital gains. Reinvkur Same as ReinvLG. Reinvksp Same as ReinvSh. ReinvMd Reinvested mid-term capital gains. ReinvSG Same as ReinvSh. ReinvSh Reinvested short-term capital gains. Reinvzin Same as ReinvDiv. Reminder Reminder. (Uses D, N, C & M lines) RtrnCap Return of capital. RtrnCapX Return of capital. For use with an L line. Sell Sell shares. SellX Sell shares. For use with an L line. ShtSell Short sale. ShrsIn Deposit shares. ShrsOut Withdraw shares. StkSplit Share split. Verkauf Same as Sell. (German) VerkaufX Same as SellX. (German) Vest Mark options as vested. (Uses N, Y, Q, C & M lines) WithDrwX Same as XOut. Used for tax-advantaged accounts. XIn Transfer cash from another account. XOut Transfer cash to another account. */ val = val.toLower(); if (val.contains(QStringLiteral("div")) && val != QStringLiteral("reinvdiv")) { // TODO(Stephane MANKOWSKI) err=currentOperation.setProperty ( "SKG_OP_ORIGINAL_AMOUNT", "" ); div = true; } else if (val.contains(QStringLiteral("sell")) || val.contains(QStringLiteral("verkauf")) || val.contains(QStringLiteral("miscexp")) || val.contains(QStringLiteral("shrsout")) ) { quantityFactor = -1; } // Correction 214851 vvvv // err=currentOperation.setComment ( val ); // if ( !err ) err=currentOperation.setMode ( i18nc ( "Noun, the title of an item","Title" ) ); // Correction 214851 ^^^^ } else { // N Num (check or reference number) // Set number bool ok; int number = val.toInt(&ok); if (ok && number != 0) { err = currentOperation.setNumber(val); } else { err = currentOperation.setMode(val); } } } else if (op == 'Q') { // Q Quantity (number of shares or split ratio) // Set value if (!val.isEmpty()) { double previousQuantity = currentSubOperation.getQuantity(); if (previousQuantity != 0.0) { // We have to create a new operation if (account != nullptr) { err = account->addOperation(payement, true); } else { SKGAccountObject defAccount; err = m_importer->getDefaultAccount(defAccount); IFOKDO(err, defAccount.addOperation(payement, true)) } IFOKDO(err, payement.setDate(currentOperationDate)) IFOK(err) { // Create unit if needed // If an initial balance is existing for the account then we use the unit else we look for the most appropriate unit SKGUnitObject unit; if ((account != nullptr) && !openingbalancecreated) { double initBalance; account->getInitialBalance(initBalance, unit); } if (!unit.exist()) { err = m_importer->getDefaultUnit(unit, ¤tOperationDate); } IFOKDO(err, payement.setUnit(unit)) } IFOKDO(err, payement.save()) IFOKDO(err, currentOperation.setGroupOperation(payement)) SKGSubOperationObject subpayement; IFOKDO(err, payement.addSubOperation(subpayement)) IFOKDO(err, subpayement.setQuantity(-previousQuantity)) IFOKDO(err, subpayement.save()) } IFOKDO(err, currentSubOperation.setQuantity(quantityFactor * SKGServices::stringToDouble(val))) } } else if (op == 'T') { // T Amount // Set value checkOperationAmount = SKGServices::stringToDouble(val); err = currentSubOperation.setQuantity(checkOperationAmount / currentUnitPrice); if (!err && investmentAccount) { err = currentOperation.setProperty(QStringLiteral("SKG_OP_ORIGINAL_AMOUNT"), val); } } else if (op == '$') { // Dollar amount of split // Set value if (!investmentAccount) { double vald = SKGServices::stringToDouble(val); checkSuboperationsAmount += vald; if (addNextAmountToTransferQuantity && !lastTransferAccount.isEmpty()) { transferQuantity[transferAccount.count() - 1] += vald; } addNextAmountToTransferQuantity = false; lastTransferAccount = QString(); err = currentSubOperation.setQuantity(vald); // save IFOKDO(err, currentSubOperation.save()) // Create suboperation IFOKDO(err, currentOperation.addSubOperation(currentSubOperation)) latestSubCatMustBeRemoved = true; } } else if (op == 'P') { // P Payee // Set Payee // Clean QIF coming from bankperfect val.remove(QStringLiteral("[auto]")); err = SKGPayeeObject::createPayee(m_importer->getDocument(), val, currentPayee); IFOKDO(err, currentOperation.setPayee(currentPayee)) } else if (op == 'A') { // A Address (up to 5 lines; 6th line is an optional message) QString add = currentPayee.getAddress(); if (!add.isEmpty()) { add += ' '; } add += val; err = currentPayee.setAddress(add); IFOKDO(err, currentPayee.save()) } else if (op == 'M') { // M Memo // Set Memo err = currentOperation.setComment(val); } else if (op == 'E') { // E Memo in split // Set Memo err = currentSubOperation.setComment(val); } else if (op == 'S' || op == 'L') { // S Category in split (Category/Transfer/Class) // L Category (Category/Subcategory/Transfer/Class) // LCategory of transaction // L[Transfer account] // LCategory of transaction/Class of transaction // L[Transfer account]/Class of transaction// Set Category if (!val.isEmpty()) { if (val[0] == '[') { addNextAmountToTransferQuantity = true; int pos = val.indexOf(']'); if (pos != -1) { SKGPayeeObject payeeObj; currentOperation.getPayee(payeeObj); bool opening = (payeeObj.getName().compare(OPENINGBALANCE, Qt::CaseInsensitive) == 0); // If the very first Bank transaction in the file has a payee of "Opening Balance", the L line contains the name of the account that the file describes. This is not a transfer if (op == 'L' && automaticAccount && (account != nullptr) && opening) { QString accountName = val.mid(1, pos - 1); SKGAccountObject newAccount(m_importer->getDocument()); err = newAccount.setName(accountName); IFOK(err) { if (newAccount.exist()) { // Oups, the real account is existing and it is another one err = newAccount.load(); // We move the operation in the right account IFOKDO(err, currentOperation.setParentAccount(newAccount)) IFOKDO(err, currentOperation.save()) // We delete the previous account if empty IFOK(err) { if (account->getNbOperation() == 0) { err = account->remove(); } delete account; account = new SKGAccountObject(newAccount); } } else { err = account->setName(accountName); IFOKDO(err, account->save()) } } } // if ( op=='L' && currentOperation.getPayee().compare ( "Opening Balance", Qt::CaseInsensitive ) !=0 && !investmentAccount) if (!opening) { lastTransferAccount = val.mid(1, pos - 1); if ((account != nullptr) && lastTransferAccount == account->getName()) { lastTransferAccount = QString(); } if (!lastTransferAccount.isEmpty() && (transferAccount.count() == 0 || transferAccount.at(transferAccount.count() - 1) != lastTransferAccount || transferQuantity.at(transferQuantity.count() - 1) != 0.0 ) ) { transferAccount.append(lastTransferAccount); transferQuantity.append(0.0); } } val = val.mid(pos + 2); } } if (!err && !val.isEmpty()) { auto cat_tag = SKGServices::splitCSVLine(val, '/', false); val = cat_tag.at(0); SKGCategoryObject Category; val.replace('/', OBJECTSEPARATOR); val.replace(':', OBJECTSEPARATOR); val.replace(',', OBJECTSEPARATOR); val.replace(';', OBJECTSEPARATOR); err = SKGCategoryObject::createPathCategory(m_importer->getDocument(), val, Category); IFOKDO(err, currentSubOperation.setCategory(Category)) if (!err && cat_tag.count() > 1) { SKGTrackerObject tracker; err = SKGTrackerObject::createTracker(m_importer->getDocument(), cat_tag.at(1), tracker); IFOKDO(err, currentSubOperation.setTracker(tracker)) } } } } else if (op == 'C') { // C Cleared status // Set status err = currentOperation.setStatus((val == QStringLiteral("C") || val == QStringLiteral("*") ? SKGOperationObject::POINTED : (val == QStringLiteral("R") || val == QStringLiteral("X") ? SKGOperationObject::CHECKED : SKGOperationObject::NONE))); } else if (op == '^') { // ^ End of entry // save if (currentOperationInitialized) { QByteArray hash = QCryptographicHash::hash(stringForHash.toUtf8(), QCryptographicHash::Md5); SKGPayeeObject payeeObj; currentOperation.getPayee(payeeObj); bool opening = (payeeObj.getName().compare(OPENINGBALANCE, Qt::CaseInsensitive) == 0); if (!err && opening) { // 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"))) openingbalancecreated = true; } IFOKDO(err, currentOperation.setImportID(hash.toHex())) IFOKDO(err, currentOperation.save()) if (!latestSubCatMustBeRemoved && !err) { err = currentSubOperation.save(); } // Create transfers if needed // Get origin op SKGOperationObject opOrigin(m_importer->getDocument(), currentOperation.getID()); SKGAccountObject accountOrigin; IFOKDO(err, opOrigin.getParentAccount(accountOrigin)) int nbTransfers = transferAccount.count(); for (int j = 0; !err && j < nbTransfers; ++j) { bool merged = false; double tq = transferQuantity.at(j); const QString& ta = transferAccount.at(j); // Is the transfert operation already existing? double qua = tq == 0.0 && addNextAmountToTransferQuantity ? SKGServices::stringToDouble(opOrigin.getAttribute(QStringLiteral("f_QUANTITY"))) : tq; QString wc = "t_ACCOUNT='" % SKGServices::stringToSqlString(ta) % "' AND t_TOACCOUNT='" % SKGServices::stringToSqlString(accountOrigin.getName()) % "' AND ABS(f_QUANTITY-(" % SKGServices::doubleToString(-qua) % "))<0.0001" " AND ABS(julianday(d_date) - julianday('" % SKGServices::dateToSqlString(QDateTime(opOrigin.getDate())) % "'))<1" " ORDER BY ABS(julianday(d_date) - julianday('" % SKGServices::dateToSqlString(QDateTime(opOrigin.getDate())) % "')) ASC"; SKGObjectBase::SKGListSKGObjectBase obs; m_importer->getDocument()->getObjects(QStringLiteral("v_operation_display"), wc, obs); if (!obs.isEmpty()) { // We have to merge them and we do not need to create the transfer SKGOperationObject firstOne(obs.at(0)); // Remove all operation attached to this transfer SKGObjectBase::SKGListSKGObjectBase list; IFOKDO(err, firstOne.getGroupedOperations(list)) for (const auto& o : qAsConst(list)) { SKGOperationObject op2(o); if (op2 != firstOne) { IFOKDO(err, op2.setStatus(SKGOperationObject::NONE)) IFOKDO(err, op2.remove(false, true)) } } // Attach myself IFOKDO(err, currentOperation.setGroupOperation(firstOne)) IFOKDO(err, currentOperation.save()) merged = true; } else { // Is the operation already created as a transfer of an other one? QString wc = "t_import_id='QIF TRANSFER-" % SKGServices::stringToSqlString(ta) % "' AND t_ACCOUNT='" % SKGServices::stringToSqlString(accountOrigin.getName()) % "' AND (ABS(f_CURRENTAMOUNT-(" % SKGServices::doubleToString(opOrigin.getCurrentAmount()) % "))<0.0001 OR f_QUANTITY=" % SKGServices::doubleToString(qua) % ")" " AND ABS(julianday(d_date) - julianday('" % SKGServices::dateToSqlString(QDateTime(opOrigin.getDate())) % "'))<1" " ORDER BY ABS(julianday(d_date) - julianday('" % SKGServices::dateToSqlString(QDateTime(opOrigin.getDate())) % "')) ASC"; m_importer->getDocument()->getObjects(QStringLiteral("v_operation_display"), wc, obs); if (!obs.isEmpty()) { // We have to merge them and we do not need to create the transfer SKGOperationObject firstOne(obs.at(0)); err = opOrigin.setStatus(SKGOperationObject::NONE); // To be sure we can delete it IFOKDO(err, opOrigin.save()) IFOKDO(err, firstOne.mergeAttribute(opOrigin)) SKGObjectBase::SKGListSKGObjectBase list; IFOKDO(err, currentOperation.getGroupedOperations(list)) for (const auto& o : qAsConst(list)) { SKGOperationObject op2(o); IFOKDO(err, op2.setStatus(SKGOperationObject::NONE)) IFOKDO(err, op2.remove(false, true)) } merged = true; } } if (!merged) { // Create target account if needed SKGAccountObject accountTransfer(m_importer->getDocument()); if (m_accountCache.contains(ta)) { accountTransfer = m_accountCache[ta]; } else { accountTransfer.setName(ta); if (!accountTransfer.exist()) { // The account is created in the same bank by default SKGBankObject bankOrigin; IFOKDO(err, accountOrigin.getBank(bankOrigin)) IFOKDO(err, accountTransfer.setBank(bankOrigin)) IFOKDO(err, accountTransfer.save(false, true)) } else { err = accountTransfer.load(); } m_accountCache[ta] = accountTransfer; } // Create operation SKGUnitObject unit; opOrigin.getUnit(unit); SKGOperationObject opTransfer; IFOKDO(err, accountTransfer.addOperation(opTransfer, true)) IFOKDO(err, opTransfer.setDate(opOrigin.getDate())) IFOKDO(err, opTransfer.setComment(opOrigin.getComment())) SKGPayeeObject payeeObj2; opTransfer.getPayee(payeeObj2); IFOKDO(err, opTransfer.setPayee(payeeObj2)) IFOKDO(err, opTransfer.setStatus(opOrigin.getStatus())) IFOKDO(err, opTransfer.setUnit(unit)) IFOKDO(err, opTransfer.setImportID("QIF TRANSFER-" % accountOrigin.getName())) IFOKDO(err, opTransfer.save()) // save needed before setGroupOperation IFOKDO(err, opTransfer.setGroupOperation(opOrigin)) IFOKDO(err, opOrigin.load()) // Must be reload because of setGroupOperation modified it IFOKDO(err, opTransfer.save()) SKGSubOperationObject subopTransfer; IFOKDO(err, opTransfer.addSubOperation(subopTransfer)) IFOKDO(err, subopTransfer.setQuantity(-qua)) IFOKDO(err, subopTransfer.save()) } } } // Check Sum($)=T for incident 214462 QString checkOperationAmountString = SKGServices::doubleToString(checkOperationAmount); QString checkSuboperationsAmountString = SKGServices::doubleToString(checkSuboperationsAmount); if (!err && checkOperationAmount != 0 && checkSuboperationsAmount != 0 && checkOperationAmountString != checkSuboperationsAmountString) { SKGSubOperationObject suboprepair; IFOKDO(err, currentOperation.addSubOperation(suboprepair)) IFOKDO(err, suboprepair.setQuantity(checkOperationAmount - checkSuboperationsAmount)) IFOKDO(err, suboprepair.setComment(i18nc("An information message", "Auto repaired operation"))) IFOKDO(err, suboprepair.save()) IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The total amount of the operation (%1) was different to the sum of the sub-operations (%2). The operation has been repaired.", checkOperationAmountString, checkSuboperationsAmountString), SKGDocument::Warning)) } // Initialize variables currentOperationInitialized = false; latestSubCatMustBeRemoved = false; currentUnitForInvestment = QString(); quantityFactor = 1; currentUnitPrice = 1; stringForHash = QString(); checkOperationAmount = 0; checkSuboperationsAmount = 0; lastTransferAccount = QString(); transferAccount.clear(); transferQuantity.clear(); payement = SKGOperationObject(); } else { // A Address (up to five lines; the sixth line is an optional message) } } if (!err && i % 500 == 0) { err = m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")); } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } delete account; account = nullptr; SKGENDTRANSACTION(m_importer->getDocument(), err) // Lines treated IFOKDO(err, m_importer->getDocument()->stepForward(3)) } } } SKGENDTRANSACTION(m_importer->getDocument(), err) return err; } bool SKGImportPluginQif::isExportPossible() { SKGTRACEINFUNC(10) return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("QIF")); } SKGError SKGImportPluginQif::exportFile() { if (m_importer == nullptr) { return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); } SKGError err; SKGTRACEINFUNCRC(2, err) // Read parameters auto listUUIDs = SKGServices::splitCSVLine(m_exportParameters.value(QStringLiteral("uuid_of_selected_accounts_or_operations"))); QStringList listOperationsToExport; listOperationsToExport.reserve(listUUIDs.count()); QStringList listAccountsToExport; listAccountsToExport.reserve(listUUIDs.count()); for (const auto& uuid : listUUIDs) { if (uuid.endsWith(QLatin1String("-operation"))) { listOperationsToExport.push_back(uuid); } else if (uuid.endsWith(QLatin1String("-account"))) { listAccountsToExport.push_back(uuid); } } if ((listAccountsToExport.count() != 0) || (listOperationsToExport.count() != 0)) { 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 stream(&file); if (!m_importer->getCodec().isEmpty()) { stream.setCodec(m_importer->getCodec().toLatin1().constData()); } err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export %1 file", "QIF"), 3); IFOK(err) { // Export categories SKGObjectBase::SKGListSKGObjectBase categories; IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_category_display_tmp"), QStringLiteral("1=1 ORDER BY t_fullname, id"), categories)) int nbcat = categories.count(); if (!err && (nbcat != 0)) { stream << "!Type:Cat\n"; err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export categories"), nbcat); for (int i = 0; !err && i < nbcat; ++i) { SKGCategoryObject cat(categories.at(i)); QString catName = cat.getFullName(); if (!catName.isEmpty()) { stream << QStringLiteral("N") << catName.replace(OBJECTSEPARATOR, QStringLiteral(":")) << endl; if (SKGServices::stringToDouble(cat.getAttribute(QStringLiteral("f_REALCURRENTAMOUNT"))) < 0) { stream << "E" << endl; } else { stream << "I" << endl; } stream << "^" << endl; } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(1)) SKGServices::SKGUnitInfo primaryUnit = m_importer->getDocument()->getPrimaryUnit(); // Get operations QString currentAccountName; SKGObjectBase::SKGListSKGObjectBase operations; IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_operation_display_all"), QStringLiteral("t_template='N' ORDER BY t_ACCOUNT, d_date, id"), operations)) int nb = operations.count(); IFOK(err) { err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export operations"), nb); for (int i = 0; !err && i < nb; ++i) { SKGOperationObject operation(operations.at(i)); SKGAccountObject a; operation.getParentAccount(a); if ((listOperationsToExport.isEmpty() || listOperationsToExport.contains(operation.getUniqueID())) && (listAccountsToExport.isEmpty() || listAccountsToExport.contains(a.getUniqueID()))) { // Get account name QString accountName = operation.getAttribute(QStringLiteral("t_ACCOUNT")); // In the same account ? if (accountName != currentAccountName) { SKGAccountObject account(m_importer->getDocument()); account.setName(accountName); account.load(); SKGBankObject bank; account.getBank(bank); // Write header stream << "!Account\n"; stream << 'N' << accountName << endl; QString type = (account.getType() == SKGAccountObject::CURRENT ? QStringLiteral("Bank") : (account.getType() == SKGAccountObject::CREDITCARD ? QStringLiteral("CCard") : (account.getType() == SKGAccountObject::INVESTMENT ? QStringLiteral("Invst") : (account.getType() == SKGAccountObject::ASSETS ? QStringLiteral("Oth A") : QStringLiteral("Cash"))))); stream << 'T' << type << endl; QString number = bank.getNumber(); QString bnumber = account.getAgencyNumber(); QString cnumber = account.getNumber(); if (!bnumber.isEmpty()) { if (!number.isEmpty()) { number += '-'; } number += bnumber; } if (!cnumber.isEmpty()) { if (!number.isEmpty()) { number += '-'; } number += cnumber; } stream << 'D' << number << endl; // stream << "/" Statement balance date // stream << "$" Statement balance amount stream << '^' << endl; currentAccountName = accountName; stream << "!Type:" << type << "\n"; } // Write operation /* DONE D Date DONE T Amount N/A U Transaction amount (higher possible value than T) DONE C Cleared status DONE N Number (check or reference number) DONE P Payee/description DONE M Memo N/A A Address (up to 5 lines; 6th line is an optional message) DONE L Category (category/class or transfer/class) DONE S Category in split (category/class or transfer/class) DONE E Memo in split DONE $ Dollar amount of split N/A % Percentage of split if percentages are used N/A F Reimbursable business expense flag N/A X Small Business extensions DONE Y Security DONE I Price DONE Q Quantity (number of shares or split ratio) N/A O Commission DONE ^ End of entry */ SKGUnitObject unit; operation.getUnit(unit); bool investment = false; bool unitExported = false; if (unit.getSymbol() != primaryUnit.Symbol && !primaryUnit.Symbol.isEmpty()) { unitExported = true; } if (unit.getType() == SKGUnitObject::SHARE) { unitExported = true; investment = true; } QString date = SKGServices::dateToSqlString(QDateTime(operation.getDate())); if (date.isEmpty()) { // This is an opening balance date = QStringLiteral("0000-00-00"); } stream << 'D' << date << endl; if (!unitExported) { stream << 'T' << SKGServices::doubleToString(operation.getCurrentAmount()) << endl; } if (!investment) { auto number = operation.getNumber(); if (!number.isEmpty()) { stream << 'N' << operation.getNumber() << endl; } } else { stream << 'N' << (operation.getCurrentAmount() > 0 ? "Buy" : "Sell") << endl; } if (unitExported) { stream << 'Y' << unit.getSymbol() << endl; } SKGPayeeObject payeeObj; operation.getPayee(payeeObj); QString payee = payeeObj.getName(); QString address = payeeObj.getAddress(); if (date == QStringLiteral("0000-00-00")) { payee = OPENINGBALANCE; } if (!payee.isEmpty()) { stream << 'P' << payee << endl; } if (!address.isEmpty()) { stream << 'A' << address << endl; } QString memo = operation.getMode() % " " % operation.getComment(); memo = memo.trimmed(); if (!memo.isEmpty()) { stream << 'M' << memo << endl; } SKGOperationObject::OperationStatus status = operation.getStatus(); stream << 'C' << (status == SKGOperationObject::POINTED ? "C" : (status == SKGOperationObject::CHECKED ? "R" : "")) << endl; // Get sub operations SKGObjectBase::SKGListSKGObjectBase suboperations; err = operation.getSubOperations(suboperations); IFOK(err) { int nbSubOps = suboperations.size(); QString category; if (nbSubOps == 1) { SKGSubOperationObject suboperation(suboperations.at(0)); // Dump quantity if (unitExported) { stream << 'Q' << SKGServices::doubleToString(qAbs(suboperation.getQuantity())) << endl; stream << 'I' << SKGServices::doubleToString(qAbs(operation.getCurrentAmount() / suboperation.getQuantity())) << endl; } // Get category of this simple operation SKGCategoryObject cat; suboperation.getCategory(cat); category = cat.getFullName().replace(OBJECTSEPARATOR, QStringLiteral(":")); } // Is it a transfer SKGOperationObject transfer; if (operation.isTransfer(transfer)) { if (!category.isEmpty()) { category.prepend('/'); } SKGAccountObject transferAccount; err = transfer.getParentAccount(transferAccount); IFOK(err) category.prepend('[' % transferAccount.getName() % ']'); } if (!category.isEmpty()) { stream << 'L' << category << endl; } if (nbSubOps > 1) { // Split operation for (int k = 0; k < nbSubOps; ++k) { SKGSubOperationObject suboperation(suboperations.at(k)); SKGCategoryObject cat; suboperation.getCategory(cat); QString category2 = cat.getFullName().replace(OBJECTSEPARATOR, QStringLiteral(":")); if (!category2.isEmpty()) { stream << 'S' << category2 << endl; } QString memo2 = suboperation.getComment(); memo2 = memo2.trimmed(); if (!memo2.isEmpty()) { stream << 'E' << memo2 << endl; } stream << '$' << SKGServices::doubleToString(suboperation.getQuantity()) << endl; } } } stream << '^' << endl; } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(2)) // Export prices SKGObjectBase::SKGListSKGObjectBase unitvalues; IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_unitvalue"), QStringLiteral("1=1 ORDER BY (select t_name from unit where v_unitvalue.rd_unit_id=unit.id), d_date"), unitvalues)) nb = unitvalues.count(); if (!err && (nb != 0)) { stream << "!Type:Prices\n"; err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export units"), nb); for (int i = 0; !err && i < nb; ++i) { SKGUnitValueObject unitVal(unitvalues.at(i)); SKGUnitObject unit; err = unitVal.getUnit(unit); IFOK(err) { QStringList vals; QString v = unit.getSymbol(); if (v.isEmpty()) { v = unit.getName(); } vals.push_back(v); vals.push_back(SKGServices::doubleToString(unitVal.getQuantity())); vals.push_back(SKGServices::dateToSqlString(QDateTime(unitVal.getDate()))); stream << SKGServices::stringsToCsv(vals) << endl; } IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) } stream << "^" << endl; SKGENDTRANSACTION(m_importer->getDocument(), err) } IFOKDO(err, m_importer->getDocument()->stepForward(3)) SKGENDTRANSACTION(m_importer->getDocument(), err) } // Close file file.commit(); } return err; } QString SKGImportPluginQif::getMimeTypeFilter() const { return "*.qif|" % i18nc("A file format", "QIF file"); } #include diff --git a/scripts/skrooge-release.py b/scripts/skrooge-release.py index 9d06c4d5c..78ef7eee1 100755 --- a/scripts/skrooge-release.py +++ b/scripts/skrooge-release.py @@ -1,376 +1,379 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- #************************************************************************** #* Copyright (C) 2017 by S. MANKOWSKI / G. DE BURE support@mankowski.fr #* Redistribution and use in source and binary forms, with or without #* modification, are permitted provided that the following conditions #* are met: #* #* 1. Redistributions of source code must retain the above copyright #* notice, this list of conditions and the following disclaimer. #* 2. Redistributions in binary form must reproduce the above copyright #* notice, this list of conditions and the following disclaimer in the #* documentation and/or other materials provided with the distribution. #* #* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR #* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES #* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. #* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, #* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT #* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, #* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY #* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT #* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF #* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #************************************************************************** import argparse import datetime import os import shutil import subprocess import sys import fileinput __VERSION__ = '1.0.0' toolPath=os.path.dirname(os.path.realpath(__file__)) localPath=os.path.dirname(toolPath) tempDir='/data' class Releasor(object): def __init__(self, args): self.ubuntuVersions=['bionic', 'cosmic', 'disco'] self.workdingDir=os.path.join(tempDir, 'skrooge-release_' + args.version) print("# Working directory :" + self.workdingDir) if args.version.endswith(".0") or args.stable: self.ppa = "ppa" self.ppatotreat = ["beta", self.ppa] else: self.ppa = "beta" self.ppatotreat = [self.ppa] if os.path.exists(self.workdingDir): self.logfile = open(os.path.join(self.workdingDir, 'log.txt'), 'w') else: self.logfile = None def prepareWorkingDirectory(self, args): print('# Prepare the working directory') if os.path.exists(self.workdingDir): print('# Remove '+self.workdingDir) shutil.rmtree(self.workdingDir) os.mkdir(self.workdingDir) os.chdir(self.workdingDir) self.logfile = open(os.path.join(self.workdingDir, 'log.txt'), 'w') print('# DONE') return 0 def makeTarFile(self, args): print('# Make the tar file') cmd = ['git', 'clone', 'https://github.com/KDE/releaseme.git'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: cmd = ['releaseme/tarme.rb', '--version', args.version, '--origin', 'trunk', 'skrooge'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) shutil.rmtree(os.path.join(self.workdingDir, 'skrooge-'+args.version)) shutil.rmtree('releaseme') os.remove(os.path.join(self.workdingDir, 'release_data')) sig_file = os.path.join(self.workdingDir, 'skrooge-'+args.version+'.tar.xz.sig') if os.path.exists(sig_file): os.remove(sig_file) print('# '+("DONE" if rc == 0 else "FAILED")) return rc def updateTarFile(self, args): print('# Update the tar file') os.chdir(self.workdingDir) p = 'skrooge-'+args.version if os.path.exists(p): shutil.rmtree(p) tarfile = 'skrooge-'+args.version+'.tar.xz' print('# Untar '+tarfile) cmd = ['tar', '-xvf', tarfile] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: print('# Change the CMakeLists.txt') with open(os.path.join(p, 'CMakeLists.txt'), 'r') as fileCMakeLists: CMakeListsContent = '' for line in fileCMakeLists: if line.startswith('SET(SKG_VERSION'): print('# SKG_VERSION changes to "'+args.version+'"') CMakeListsContent += 'SET(SKG_VERSION "'+args.version+'")\n' else: if line.startswith('SET(SKG_BETA'): bb = 'BETA' if self.ppa == "beta" else '' print('# SKG_BETA changes to "'+bb+'"') CMakeListsContent += 'SET(SKG_BETA "'+bb+'")\n' else: if line.startswith('FEATURE_SUMMARY'): CMakeListsContent += line break else: CMakeListsContent += line with open(os.path.join(p, 'CMakeLists.txt'), 'w') as fileCMakeLists: fileCMakeLists.write(CMakeListsContent) + + #for line in fileinput.input(['skrooge/org.kde.skrooge.appdata.xml'], inplace=True): + # print(line.replace('', '\n'), end='') print('# Build the new splash screen') buildPath = os.path.join(p, 'build') os.mkdir(buildPath) os.chdir(buildPath) cmd = ['cmake', '..', '-DCMAKE_INSTALL_PREFIX=`kf5-config --prefix`', '-DQT_PLUGIN_INSTALL_DIR=`kf5-config --qt-plugins`'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: cmd = ['make', 'splash'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: print('# Create the tar file') os.chdir(self.workdingDir) shutil.rmtree(buildPath) os.remove(tarfile) cmd = ['tar', '-cJf', tarfile, p] rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) self.logfile.write('### '+' '.join(cmd)+'\n') if rc == 0: cmd = ['gpg2', '--armor', '--detach-sig', '-o', tarfile+'.sig', tarfile] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: print("Skrooge "+args.version+" released\n\nHi,\n\nCould you publish the following files in skrooge/"+("stable" if args.stable else "unstable")+"?\n") cmd = ['sha256sum', tarfile] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd) if rc == 0: cmd = ['sha256sum', tarfile+'.sig'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd) print("Thank you.\nRegards.\n" ) if rc == 0: shutil.rmtree(p) print('# '+("DONE" if rc == 0 else "FAILED")) return rc def modify(self, args): print('# Get dsc') os.chdir(self.workdingDir) cmd = ['apt', 'source', 'skrooge'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: previousPackage = [os.path.join(self.workdingDir, o) for o in os.listdir(self.workdingDir) if os.path.isdir(os.path.join(self.workdingDir, o)) and o.startswith('skrooge-')][0] print('# previousPackage='+previousPackage) os.chdir(previousPackage) if os.path.exists('debian'): shutil.rmtree('debian') cmd = ['tar', '-xvf', os.path.join(toolPath, 'skrooge-release-debian.tar.gz')] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: cmd = ['uupdate', '-u', 'skrooge-'+args.version+'.tar.xz'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: # Read the changelog os.chdir('../skrooge-'+args.version) with open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/CHANGELOG'), 'r') as fileChangelog: fileChangelog.readline() # To pass the first line changelogContent = '' done = False for line in fileChangelog: if line.strip() == '' and done == False: changelogContent += ' -- Stephane MANKOWSKI (Perso) {}\n'.format(datetime.datetime.now().strftime('%a, %d %b %Y %H:%M:%S +0100')) done = True if not line.startswith(' -- '): changelogContent += line # for ppa in self.ppatotreat: for i in range(len(self.ubuntuVersions)): print('# {}/{}: {} - {}'.format(i+1, len(self.ubuntuVersions), self.ubuntuVersions[i], ppa)) f = open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/debian/skrooge-kf5-common.install'), 'r') cf = f.read() f.close() if self.ubuntuVersions[i] == 'xenial' and 'usr/share/metainfo/*.xml' in cf: print('# make changes for '+self.ubuntuVersions[i]) f = open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/debian/skrooge-kf5-common.install'), 'w') f.write(cf.replace('usr/share/metainfo/*.xml', 'usr/share/appdata/*.xml')) f.close() if self.ubuntuVersions[i] != 'xenial' and 'usr/share/appdata/*.xml' in cf: print('# make changes for '+self.ubuntuVersions[i]) f = open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/debian/skrooge-kf5-common.install'), 'w') f.write(cf.replace('usr/share/appdata/*.xml', 'usr/share/metainfo/*.xml')) f.close() with open(os.path.join(self.workdingDir, 'skrooge-'+args.version+'/debian/changelog'), 'w') as fileDebianChangelog: fileDebianChangelog.write('skrooge ('+args.version+'-0ubuntu1~'+ppa+str(i+1)+') '+self.ubuntuVersions[i]+'; urgency=medium\n') fileDebianChangelog.write(changelogContent+'\n') cmd = ['debuild', '-S', '-sa'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc!=0: break if rc!=0: break print('# '+("DONE" if rc == 0 else "FAILED")) return rc def publishLaunchpad(self, args): print('# Publish on launchpad') rc = 0 if not args.publish: print('# Publication ignored') os.chdir(self.workdingDir) for ppa in self.ppatotreat: for i in range(len(self.ubuntuVersions)): print('# {}/{}: {} {}'.format(i+1, len(self.ubuntuVersions), self.ubuntuVersions[i], ppa)) cmd = ['dput', '-f', 'ppa:s-mankowski/'+ppa+'-kf5', 'skrooge_'+args.version+'-0ubuntu1~'+ppa+str(i+1)+'_source.changes'] if not args.publish: print('# '+' '.join(cmd)+'\n') else: self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc!=0: break print('# '+("DONE" if rc == 0 else "FAILED")) return rc def publishKDE(self, args): print('# Publish on kde') rc = 0 if not args.publish: print("# Publication ignored") os.chdir(self.workdingDir) cmd = ['kdecp5', 'skrooge-'+args.version+'.tar.xz', 'ftp://upload.kde.org/incoming'] if not args.publish: print('# '+' '.join(cmd)+'\n') else: self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: self.logfile.write('### '+' '.join(cmd)+'\n') cmd = ['kdecp5', 'skrooge-'+args.version+'.tar.xz.sig', 'ftp://upload.kde.org/incoming'] if not args.publish: print('# '+' '.join(cmd)+'\n') else: self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) print('# '+("DONE" if rc == 0 else "FAILED")) return rc def buildAppImage(self, args): print('# Generate App Image') os.chdir(self.workdingDir) targteappimage = os.path.join(self.workdingDir, "skrooge-"+args.version+'-x86_64.AppImage') if os.path.exists(targteappimage): os.remove(targteappimage) cmd = ['wget', '-c', '-nv', 'https://raw.githubusercontent.com/probonopd/AppImages/master/pkg2appimage'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: - for line in fileinput.input(['pkg2appimage'], inplace=True): - print(line.replace('trusty', 'cosmic'), end='') + #for line in fileinput.input(['pkg2appimage'], inplace=True): + # print(line.replace('trusty', 'bionic').replace('xenial', 'bionic'), end='') os.chmod('./pkg2appimage', 0o775) if args.fromlocal: print('# Get appimage.yml from local path') cmd = ['cp', os.path.join(localPath, 'appimage.yml'), '.'] else: cmd = ['wget', '-c', '-nv', 'https://cgit.kde.org/skrooge.git/plain/appimage.yml'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: if not args.stable: for line in fileinput.input(['appimage.yml'], inplace=True): print(line.replace('ppa-kf5', 'beta-kf5'), end='') cmd = ['./pkg2appimage', 'appimage.yml'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: appimage_file = [os.path.join(self.workdingDir, "out/"+o) for o in os.listdir("./out") if o.endswith('.AppImage')][0] shutil.move(appimage_file, targteappimage) print('# '+("DONE" if rc == 0 else "FAILED")) return rc def buildSnap(self, args): print('# Generate Snap') os.chdir(self.workdingDir) targteappimage = os.path.join(self.workdingDir, "skrooge_"+args.version+'_amd64.snap') if os.path.exists(targteappimage): os.remove(targteappimage) if args.fromlocal: print('# Get snapcraft.yaml from local path') cmd = ['cp', os.path.join(localPath, 'snapcraft.yaml'), '.'] else: cmd = ['wget', '-c', '-nv', 'https://cgit.kde.org/skrooge.git/plain/snapcraft.yaml'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) if rc == 0: for line in fileinput.input(['snapcraft.yaml'], inplace=True): print(line.replace('version: "X.X.X"', 'version: "'+args.version+'"').replace('source: XXX', 'source: '+localPath if args.fromlocal else 'source: git://anongit.kde.org/skrooge.git'), end='') cmd = ['snapcraft'] self.logfile.write('### '+' '.join(cmd)+'\n') rc = subprocess.call(cmd, stdout=self.logfile, stderr=self.logfile) print('# '+("DONE" if rc == 0 else "FAILED")) # TO PUBLISH https://docs.snapcraft.io/build-snaps/c # snapcraft push --release=edge skrooge_*.snap return rc def main(): parser = argparse.ArgumentParser(prog='skrooge-release', description='skrooge release maker') # Global arguments parser.add_argument('--version', required=True, help='The release version') parser.add_argument('--pwd', required=False, help='The password') parser.add_argument('--stable', action='store_true', help='To define this version as a master version') parser.add_argument('--publish', action='store_true', help='To publish on launchpad and KDE') parser.add_argument('--appimage', action='store_true', help='To generate the appimage only') parser.add_argument('--snap', action='store_true', help='To generate the snap only') parser.add_argument('--fromlocal', action='store_true', help='To generate The snap and the appimage from local path (' + localPath + ')') args = parser.parse_args() print("#####################") print("# Launching release #") print("#####################") print("# Version :" + args.version) print("# Stable :" + ("Y" if args.stable else "N")) print("# Publish :" + ("Y" if args.publish else "N")) print("# Appimage :" + ("Y" if args.appimage else "N")) print("# Snap :" + ("Y" if args.snap else "N")) if args.fromlocal: print("# From local path :" + localPath) # Launch the release r = Releasor(args) rc = 0 rc=r.prepareWorkingDirectory(args) if(rc == 0 and not (args.appimage or args.snap)): rc=r.makeTarFile(args) if(rc == 0 and not (args.appimage or args.snap)): rc=r.updateTarFile(args) if(rc == 0 and not (args.appimage or args.snap)): rc=r.modify(args) if(rc == 0 and not (args.appimage or args.snap)): rc=r.publishLaunchpad(args) if(rc == 0 and not (args.appimage or args.snap)): rc=r.publishKDE(args) if(rc == 0 and args.appimage): rc=r.buildAppImage(args) if(rc == 0 and args.snap): rc=r.buildSnap(args) print("#####################") print("# End of release #" if rc == 0 else "# FAILURE #") print("#####################") return rc if __name__ == '__main__': sys.exit(main()) diff --git a/skgbankmodeler/skgreportbank.cpp b/skgbankmodeler/skgreportbank.cpp index 60587f108..eb4750d60 100644 --- a/skgbankmodeler/skgreportbank.cpp +++ b/skgbankmodeler/skgreportbank.cpp @@ -1,776 +1,776 @@ /*************************************************************************** * 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 * A skrooge plugin for monthly report. * * @author Stephane MANKOWSKI */ #include "skgreportbank.h" #include #include #include #include #include "skgaccountobject.h" #include "skgdocumentbank.h" #include "skgoperationobject.h" #include "skgrecurrentoperationobject.h" #include "skgruleobject.h" #include "skgtraces.h" #include "skgunitobject.h" SKGReportBank::SKGReportBank(SKGDocument* iDocument) : SKGReport(iDocument) { SKGTRACEINFUNC(1) connect(this, &SKGReportBank::changed, this, &SKGReportBank::changed2); } SKGReportBank::~SKGReportBank() { SKGTRACEINFUNC(1) } QVariantList SKGReportBank::getAlarms() { QString cacheId = QStringLiteral("getAlarms"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); if (doc != nullptr) { SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); SKGObjectBase::SKGListSKGObjectBase rules; SKGError err = doc->getObjects(QStringLiteral("v_rule"), QStringLiteral("t_action_type='A' ORDER BY i_ORDER"), rules); int nb = rules.count(); if (nb != 0) { for (int i = 0; !err && i < nb; ++i) { SKGRuleObject rule(rules.at(i)); SKGRuleObject::SKGAlarmInfo alarm = rule.getAlarmInfo(); QVariantList item; // clazy:exclude=container-inside-loop // Build the message if (alarm.Message.contains(QLatin1String("%3"))) { alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, primary, false), doc->formatMoney(alarm.Limit, primary, false), doc->formatMoney(alarm.Amount - alarm.Limit, primary, false)); } else if (alarm.Message.contains(QLatin1String("%2"))) { alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, primary, false), doc->formatMoney(alarm.Limit, primary, false)); } else if (alarm.Message.contains(QLatin1String("%1"))) { alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, primary, false)); } item.push_back(alarm.Message); item.push_back(alarm.Amount); item.push_back(alarm.Limit); item.push_back(alarm.Amount - alarm.Limit); item.push_back(alarm.Raised); table.push_back(item); } } m_cache[cacheId] = table; } } return table; } QVariantList SKGReportBank::getInterests() { QString cacheId = QStringLiteral("getInterests"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); if (doc != nullptr) { // Build display int year = SKGServices::periodToDate(getPeriod()).year(); SKGObjectBase::SKGListSKGObjectBase objs; SKGError err = doc->getObjects(QStringLiteral("v_account"), QStringLiteral("t_close='N' AND EXISTS(select 1 from interest where interest.rd_account_id=v_account.id) ORDER BY t_name"), objs); IFOK(err) { int nb = objs.count(); table.reserve(nb + 2); if (nb != 0) { { // Add header QVariantList item; item.push_back(false); item.push_back(i18nc("Title", "Account")); item.push_back(year); table.push_back(item); } // Add items double sum = 0; for (int i = 0; i < nb; ++i) { SKGAccountObject obj(objs.at(i)); SKGAccountObject::SKGInterestItemList oInterestList; double oInterests = 0; obj.getInterestItems(oInterestList, oInterests, year); sum += oInterests; QVariantList item; // clazy:exclude=container-inside-loop item.push_back(false); item.push_back(obj.getName()); item.push_back(oInterests); table.push_back(item); } { // Add sum QVariantList item; item.push_back(true); item.push_back(i18nc("Noun, the numerical total of a sum of values", "Total")); item.push_back(sum); table.push_back(item); } } } m_cache[cacheId] = table; } } return table; } QVariantList SKGReportBank::getBudgetTable() { QString cacheId = QStringLiteral("getBudgetTable"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); table = doc != nullptr ? doc->getBudget(getPeriod()) : QVariantList(); m_cache[cacheId] = table; } return table; } QVariantList SKGReportBank::getPortfolio() { QString cacheId = QStringLiteral("getPortfolio"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) QString period = getPeriod(); if (!period.isEmpty()) { QDate date = qMin(SKGServices::periodToDate(period), QDate::currentDate().addDays(1 - QDate::currentDate().day()).addMonths(1).addDays(-1)); auto* doc = qobject_cast(m_document); if (doc != nullptr) { SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); // Get list of operations SKGObjectBase::SKGListSKGObjectBase objs; SKGError err = doc->getObjects(QStringLiteral("v_operation_display"), "d_date<'" % SKGServices::dateToSqlString(QDateTime(date)) % "' AND rc_unit_id IN (SELECT id FROM v_unit_display WHERE t_type='S' AND f_QUANTITYOWNED>0.01) ORDER BY t_UNIT", objs); int nb = objs.count(); if (!err && nb > 0) { table.reserve(nb + 1); QVariantList line; line << doc->getDisplay(QStringLiteral("t_symbol")) << doc->getDisplay(QStringLiteral("t_UNIT")) << i18nc("Column table title", "Quantity") << i18nc("Column table title", "Purchase amount") << i18nc("Column table title", "Initial amount") << QLocale().toString(date, QLocale::ShortFormat) << i18nc("Column table title", "Variation") << i18nc("Column table title", "Variation %"); table << QVariant(line); QVector listUnitValues; unitValues current; current.initalAmount = 0.0; current.purchaseAmount = 0.0; current.currentAmount = 0.0; current.quantity = 0.0; listUnitValues.reserve(nb); for (int i = 0; i < nb; ++i) { SKGOperationObject obj(objs.at(i)); SKGUnitObject unit; obj.getUnit(unit); if (i != 0 && current.unit != unit) { listUnitValues.push_back(current); current.initalAmount = 0.0; current.purchaseAmount = 0.0; current.currentAmount = 0.0; current.quantity = 0.0; } current.unit = unit; current.initalAmount += obj.getAmount(obj.getDate()); current.currentAmount += obj.getAmount(date); SKGObjectBase::SKGListSKGObjectBase oGroupedOperations; obj.getGroupedOperations(oGroupedOperations); oGroupedOperations.removeAll(obj); if (oGroupedOperations.count() == 1) { SKGOperationObject obj2(oGroupedOperations.at(0)); current.purchaseAmount += obj2.getAmount(obj.getDate()); } current.quantity += SKGServices::stringToDouble(obj.getAttribute(QStringLiteral("f_QUANTITY"))); } if (!current.unit.getName().isEmpty()) { listUnitValues.push_back(current); } nb = listUnitValues.count(); for (int j = 0; j < nb; ++j) { unitValues current2 = listUnitValues.at(j); SKGServices::SKGUnitInfo ui = current2.unit.getUnitInfo(); ui.Value = 1; QVariantList line2; // clazy:exclude=container-inside-loop line2 << current2.unit.getSymbol() << current2.unit.getName() << doc->formatMoney(current2.quantity, ui, false) << current2.purchaseAmount << current2.initalAmount << current2.currentAmount << current2.currentAmount - current2.initalAmount << (current2.initalAmount == 0.0 ? 0.0 : 100.0 * (current2.currentAmount - current2.initalAmount) / current2.initalAmount); table << QVariant(line2); } } } } m_cache[cacheId] = table; } return table; } QVariantList SKGReportBank::getUnitTable() { QString cacheId = QStringLiteral("getUnitTable"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) QString period = getPeriod(); if (!period.isEmpty()) { QDate date1 = SKGServices::periodToDate(getPreviousPeriod()); QDate date2 = SKGServices::periodToDate(period); auto* doc = qobject_cast(m_document); if (doc != nullptr) { SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); SKGObjectBase::SKGListSKGObjectBase units; SKGError err = doc->getObjects(QStringLiteral("v_unit_display"), QStringLiteral("1=1 ORDER BY t_TYPENLS"), units); int nbUnits = units.count(); if (nbUnits != 0) { table.reserve(nbUnits + 1); QVariantList line; line << "sum" << doc->getDisplay(QStringLiteral("t_UNIT")) << QLocale().toString(date1, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << "%" << doc->getDisplay(QStringLiteral("t_symbol")); table << QVariant(line); for (const auto& item : qAsConst(units)) { SKGUnitObject unit(item); double v1 = unit.getAmount(date1); double v2 = unit.getAmount(date2); QVariantList line2; // clazy:exclude=container-inside-loop line2 << false << unit.getName() << v1 << v2 << (100.0 * (v2 - v1) / qAbs(v1)) << unit.getSymbol(); table << QVariant(line2); } } } } m_cache[cacheId] = table; } return table; } QVariantList SKGReportBank::getAccountTable() { QString cacheId = QStringLiteral("getAccountTable"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) QString period = getPeriod(); if (!period.isEmpty()) { QDate date1 = SKGServices::periodToDate(getPreviousPeriod()); QDate date2 = SKGServices::periodToDate(period); QDate date3 = date2.addYears(-1); if (date3 == date1) { date3 = date3.addYears(-1); } auto* doc = qobject_cast(m_document); if (doc != nullptr) { SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); SKGObjectBase::SKGListSKGObjectBase accounts; SKGError err = doc->getObjects(QStringLiteral("v_account"), QStringLiteral("1=1 ORDER BY t_TYPENLS, t_BANK, t_name"), accounts); IFOK(err) { table.push_back(QVariantList() << "sum" << doc->getDisplay(QStringLiteral("t_ACCOUNT")) << QLocale().toString(date1, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << "%" << QLocale().toString(date3, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << "%"); double sumTypeV1 = 0; double sumTypeV2 = 0; double sumTypeV3 = 0; double sumV1 = 0; double sumV2 = 0; double sumV3 = 0; QString currentType; int nb = accounts.count(); for (int i = 0; !err && i < nb; ++i) { SKGAccountObject account(accounts.at(i)); double v1 = account.getAmount(date1); double v2 = account.getAmount(date2); double v3 = account.getAmount(date3); QString type = account.getAttribute(QStringLiteral("t_TYPENLS")); bool closed = account.isClosed(); if (type != currentType) { if (!currentType.isEmpty()) { table.push_back(QVariantList() << true << i18nc("Noun", "Total of %1", currentType) << sumTypeV1 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) << "" << ""); sumTypeV1 = 0; sumTypeV2 = 0; sumTypeV3 = 0; } currentType = type; } if (!closed || qAbs(v1) > 0.01 || qAbs(v2) > 0.01 || qAbs(v3) > 0.01) { QString icon = account.getAttribute(QStringLiteral("t_ICON")); if (!icon.isEmpty()) { QString iconfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % icon); if (!iconfile.isEmpty()) { icon = iconfile; } } table.push_back(QVariantList() << false << account.getName() << v1 << v2 << (100.0 * (v2 - v1) / qAbs(v1)) << v3 << v2 << (100.0 * (v2 - v3) / qAbs(v3)) << account.getAttribute(QStringLiteral("t_BANK")) << icon); } sumTypeV1 += v1; sumTypeV2 += v2; sumTypeV3 += v3; sumV1 += v1; sumV2 += v2; sumV3 += v3; } table.push_back(QVariantList() << true << i18nc("Noun", "Total of %1", currentType) << sumTypeV1 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) << "" << ""); table.push_back(QVariantList() << true << i18nc("Noun, the numerical total of a sum of values", "Total") << sumV1 << sumV2 << (100.0 * (sumV2 - sumV1) / qAbs(sumV1)) << sumV3 << sumV2 << (100.0 * (sumV2 - sumV3) / qAbs(sumV3)) << "" << ""); } } } m_cache[cacheId] = table; } return table; } QVariantList SKGReportBank::getBankTable() { QString cacheId = QStringLiteral("getBankTable"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) QString period = getPeriod(); if (!period.isEmpty()) { QDate date1 = SKGServices::periodToDate(getPreviousPeriod()); QDate date2 = SKGServices::periodToDate(period); QDate date3 = date2.addYears(-1); if (date3 == date1) { date3 = date3.addYears(-1); } auto* doc = qobject_cast(m_document); if (doc != nullptr) { SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); SKGObjectBase::SKGListSKGObjectBase accounts; SKGError err = doc->getObjects(QStringLiteral("v_account"), QStringLiteral("1=1 ORDER BY t_BANK"), accounts); IFOK(err) { table.push_back(QVariantList() << "sum" << doc->getDisplay(QStringLiteral("t_BANK")) << QLocale().toString(date1, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << "%" << QLocale().toString(date3, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << "%"); double sumTypeV1 = 0; double sumTypeV2 = 0; double sumTypeV3 = 0; double sumV1 = 0; double sumV2 = 0; double sumV3 = 0; QString currentName; QString currentIcon; bool currentOpen = false; int nb = accounts.count(); for (int i = 0; !err && i < nb; ++i) { SKGAccountObject account(accounts.at(i)); double v1 = account.getAmount(date1); double v2 = account.getAmount(date2); double v3 = account.getAmount(date3); QString name = account.getAttribute(QStringLiteral("t_BANK")); QString icon = account.getAttribute(QStringLiteral("t_ICON")); if (!icon.isEmpty()) { QString iconfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % icon); if (!iconfile.isEmpty()) { icon = iconfile; } } bool open = !account.isClosed(); if (name != currentName) { if (!currentName.isEmpty() && currentOpen) { table.push_back(QVariantList() << false << currentName << sumTypeV1 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) << currentIcon); sumTypeV1 = 0; sumTypeV2 = 0; sumTypeV3 = 0; currentOpen = open; } currentName = name; currentIcon = icon; } currentOpen = currentOpen || open; sumTypeV1 += v1; sumTypeV2 += v2; sumTypeV3 += v3; sumV1 += v1; sumV2 += v2; sumV3 += v3; } if (currentOpen) { table.push_back(QVariantList() << false << currentName << sumTypeV1 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) << currentIcon); } table.push_back(QVariantList() << true << i18nc("Noun, the numerical total of a sum of values", "Total") << sumV1 << sumV2 << (100.0 * (sumV2 - sumV1) / qAbs(sumV1)) << sumV3 << sumV2 << (100.0 * (sumV2 - sumV3) / qAbs(sumV3))); } } } m_cache[cacheId] = table; } return table; } QVariantList SKGReportBank::getScheduledOperations() { QString cacheId = QStringLiteral("getScheduledOperations"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) SKGObjectBase::SKGListSKGObjectBase objs; auto scheduled_operation_days_max = m_parameters.value(QStringLiteral("scheduled_operation_days_max"), QStringLiteral("30")).toString(); SKGError err = m_document->getObjects(QStringLiteral("v_recurrentoperation_display"), QStringLiteral("i_nb_times!=0 AND d_date<=date('now','+") + scheduled_operation_days_max + " day') ORDER BY d_date", objs); QDate d = QDate::currentDate().addDays(SKGServices::stringToInt(scheduled_operation_days_max)); QString dateFormatShort = QLocale::system().dateFormat(QLocale::ShortFormat); IFOK(err) { int nb = objs.count(); if (nb != 0) { table.reserve(nb); for (int i = 0; i < nb; ++i) { SKGRecurrentOperationObject obj(objs.at(i)); bool first = true; auto obj_date = obj.getDate().toString(dateFormatShort); while (true) { if (obj.getDate() > d || (obj.hasTimeLimit() && obj.getTimeLimit() == 0)) { break; } else { bool bold = false; if (obj.isWarnEnabled() && QDate::currentDate() >= obj.getDate().addDays(-obj.getWarnDays())) { bold = true; } auto name = obj.getDisplayName(); if (!first) { name = name.replace(obj_date, obj.getDate().toString(dateFormatShort)); } table.push_back(QVariantList() << bold << name << (first ? obj.getUniqueID() : QString()) << obj.getDate()); first = false; obj.setDate(obj.getNextDate()); if (obj.hasTimeLimit()) { obj.setTimeLimit(obj.getTimeLimit() - 1); } } } } } std::sort(table.begin(), table.end(), [](const QVariant & v1, const QVariant & v2) { return v1.toList().at(3).toDate() < v2.toList().at(3).toDate(); }); m_cache[cacheId] = table; } } return table; } QVariantList SKGReportBank::getMainCategoriesForPeriod() { QString cacheId = QStringLiteral("getMainCategoriesForPeriod"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); table = doc != nullptr ? doc->getMainCategories(getPeriod(), 5) : QVariantList(); m_cache[cacheId] = table; } return table; } QVariantList SKGReportBank::getMainCategoriesForPreviousPeriod() { QString cacheId = QStringLiteral("getMainCategoriesForPreviousPeriod"); QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); table = doc != nullptr ? doc->getMainCategories(getPreviousPeriod(), 5) : QVariantList(); m_cache[cacheId] = table; } return table; } QStringList SKGReportBank::get5MainCategoriesVariation() { QString cacheId = QStringLiteral("get5MainCategoriesVariation"); QStringList table = m_cache.value(cacheId).toStringList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); table = doc != nullptr ? doc->get5MainCategoriesVariationList(getPeriod(), getPreviousPeriod(), false) : QStringList(); m_cache[cacheId] = table; } return table; } QStringList SKGReportBank::get5MainCategoriesVariationIssue() { QString cacheId = QStringLiteral("get5MainCategoriesVariationIssue"); QStringList table = m_cache.value(cacheId).toStringList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); table = doc != nullptr ? doc->get5MainCategoriesVariationList(getPeriod(), getPreviousPeriod(), true) : QStringList(); m_cache[cacheId] = table; } return table; } QVariantMap SKGReportBank::getPersonalFinanceScoreDetails(bool iTransfer, bool iTracker) { KColorScheme scheme(QPalette::Normal, KColorScheme::Window); QVariantMap output; double pfs = getPersonalFinanceScore(iTransfer, iTracker); output[QStringLiteral("value")] = pfs; if (pfs < 0) { output[QStringLiteral("level")] = QStringLiteral("danger"); output[QStringLiteral("message")] = i18nc("An advice", "You must try to get out of debt."); output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NegativeText).color().name().right(6); } else if (pfs >= 25) { output[QStringLiteral("level")] = QStringLiteral("success"); output[QStringLiteral("message")] = i18nc("An advice", "Congratulations, you are now financially independent."); output[QStringLiteral("color")] = scheme.foreground(KColorScheme::PositiveText).color().name().right(6); } else if (pfs >= 10) { output[QStringLiteral("level")] = QStringLiteral("success"); output[QStringLiteral("message")] = i18nc("An advice", "Congratulations, You saved up ten year’s worth of expenses."); output[QStringLiteral("color")] = scheme.foreground(KColorScheme::PositiveText).color().name().right(6); } else if (pfs >= 2) { output[QStringLiteral("level")] = QStringLiteral("warning"); output[QStringLiteral("message")] = i18nc("An advice", "You saved up %1 year’s worth of expenses. You should continue your effort.", SKGServices::intToString(pfs)); output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NeutralText).color().name().right(6); } else if (pfs >= 1) { output[QStringLiteral("level")] = QStringLiteral("warning"); output[QStringLiteral("message")] = i18nc("An advice", "You saved up one year’s worth of expenses. You should maintain your effort."); output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NeutralText).color().name().right(6); } else { output[QStringLiteral("level")] = QStringLiteral("warning"); output[QStringLiteral("message")] = i18nc("An advice", "You do not have debt but you have no margin. You must maintain your effort."); output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NeutralText).color().name().right(6); } return output; } double SKGReportBank::getPersonalFinanceScore(bool iTransfer, bool iTracker) { double as = getAnnualSpending(iTransfer, iTracker); return (as == 0.0 ? 0.0 : getNetWorth(iTransfer, iTracker) / as); } double SKGReportBank::getAnnualSpending(bool iTransfer, bool iTracker) { QString cacheId = QStringLiteral("getAnnualSpending-") % (iTransfer ? QStringLiteral("Y") : QStringLiteral("N")) % (iTracker ? QStringLiteral("Y") : QStringLiteral("N")); double output = m_cache.value(cacheId).toDouble(); if (!m_cache.contains(cacheId)) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); if (doc != nullptr) { QString result; QDate d2 = SKGServices::periodToDate(getPeriod()); QDate d1 = d2.addYears(-1); doc->executeSingleSelectSqliteOrder("SELECT TOTAL(f_REALCURRENTAMOUNT) FROM v_suboperation_consolidated WHERE d_date BETWEEN '" % SKGServices::dateToSqlString(d1) % "' AND '" % SKGServices::dateToSqlString(d2) % "' AND t_TYPEEXPENSE='-'" % (iTransfer ? "" : " AND t_TRANSFER='N'") % (iTracker ? "" : " AND t_REFUND=''") , result); output = -SKGServices::stringToDouble(result); m_cache[cacheId] = output; } } return output; } double SKGReportBank::getNetWorth(bool iTransfer, bool iTracker) { QString cacheId = QStringLiteral("getNetWorth-") % (iTransfer ? QStringLiteral("Y") : QStringLiteral("N")) % (iTracker ? QStringLiteral("Y") : QStringLiteral("N")); double output = m_cache.value(cacheId).toDouble(); if (!m_cache.contains(cacheId)) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); if (doc != nullptr) { QString result; QDate d = SKGServices::periodToDate(getPeriod()); doc->executeSingleSelectSqliteOrder("SELECT TOTAL(f_REALCURRENTAMOUNT) FROM v_suboperation_consolidated WHERE d_date<='" % SKGServices::dateToSqlString(d) % "' " % (iTransfer ? "" : " AND t_TRANSFER='N'") % (iTracker ? "" : " AND t_REFUND=''") , result); output = SKGServices::stringToDouble(result); m_cache[cacheId] = output; } } return output; } QVariantList SKGReportBank::getIncomeVsExpenditure(bool iOnSubOperation, bool iGrouped, bool iTransfer, bool iTracker, const QString& iWhereClause1, const QString& iWhereClause2) { QString cacheId = QStringLiteral("getIncomeVsExpenditure-") % (iOnSubOperation ? QStringLiteral("Y") : QStringLiteral("N")) % (iGrouped ? QStringLiteral("Y") : QStringLiteral("N")) % (iTransfer ? QStringLiteral("Y") : QStringLiteral("N")) % (iTracker ? QStringLiteral("Y") : QStringLiteral("N")) % iWhereClause1 % iWhereClause2; QVariantList table = m_cache.value(cacheId).toList(); if (table.isEmpty()) { SKGTRACEINFUNC(10) auto* doc = qobject_cast(m_document); if (doc != nullptr) { QString tableDb = (iOnSubOperation ? QStringLiteral("v_suboperation_consolidated") : QStringLiteral("v_operation_display")); QString amount = (iOnSubOperation ? QStringLiteral("f_REALCURRENTAMOUNT") : QStringLiteral("f_CURRENTAMOUNT")); SKGStringListList listTmp; QString wc1 = iWhereClause1; if (wc1.isEmpty()) { wc1 = SKGServices::getPeriodWhereClause(getPeriod()); } QString wc2 = iWhereClause2; if (wc2.isEmpty()) { wc2 = SKGServices::getPeriodWhereClause(getPreviousPeriod()); } SKGError err = doc->executeSelectSqliteOrder( "SELECT TOTAL(" % amount % "), '1' from " % tableDb % " WHERE " % wc1 % " AND t_TYPEACCOUNT<>'L'" % (iGrouped ? "" : " AND i_group_id=0") % (iTransfer ? "" : " AND t_TRANSFER='N'") % (iTracker ? "" : (iOnSubOperation ? " AND t_REALREFUND=''" : " AND t_REFUND=''")) % " group by t_TYPEEXPENSE " % " UNION ALL " % "SELECT TOTAL(" % amount % "), '2' from " % tableDb % " WHERE " % wc2 % " AND t_TYPEACCOUNT<>'L'" % (iGrouped ? "" : " AND i_group_id=0") % (iTransfer ? "" : " AND t_TRANSFER='N'") % (iTracker ? "" : (iOnSubOperation ? " AND t_REALREFUND=''" : " AND t_REFUND=''")) % " group by t_TYPEEXPENSE ", listTmp); IFOK(err) { double income_previous_period = 0; double expense_previous_period = 0; double income_period = 0; double expense_period = 0; int nbval = listTmp.count(); for (int i = 1; i < nbval; ++i) { // Ignore header QString m = listTmp.at(i).at(1); double v = SKGServices::stringToDouble(listTmp.at(i).at(0)); if (v > 0 && m == QStringLiteral("1")) { income_period = v; } else if (v < 0 && m == QStringLiteral("1")) { expense_period = v; } else if (v > 0 && m == QStringLiteral("2")) { income_previous_period = v; } else if (v < 0 && m == QStringLiteral("2")) { expense_previous_period = v; } } double saving_previous_period = income_previous_period + expense_previous_period; double saving_period = income_period + expense_period; table.push_back(QVariantList() << QStringLiteral("sum") << QLatin1String("") << getPreviousPeriod() << getPeriod() << QStringLiteral("max")); table.push_back(QVariantList() << false << doc->getDisplay(QStringLiteral("f_CURRENTAMOUNT_INCOME")) << qAbs(income_previous_period) << qAbs(income_period)); table.push_back(QVariantList() << false << doc->getDisplay(QStringLiteral("f_CURRENTAMOUNT_EXPENSE")) << qAbs(expense_previous_period) << qAbs(expense_period)); table.push_back(QVariantList() << true << i18nc("Noun", "Savings possible") << saving_previous_period << saving_period); table.push_back(QVariantList() << true << i18nc("Noun", "Max") << qMax(qAbs(income_previous_period), qAbs(expense_previous_period)) << qMax(qAbs(income_period), qAbs(expense_period))); } m_cache[cacheId] = table; } } return table; } void SKGReportBank::addItemsInMapping(QVariantHash& iMapping) { SKGReport::addItemsInMapping(iMapping); - iMapping.insert(QStringLiteral("about_forumpage"), QStringLiteral("http://forum.kde.org/viewforum.php?f=210")); + iMapping.insert(QStringLiteral("about_forumpage"), QStringLiteral("https://forum.kde.org/viewforum.php?f=210")); iMapping.insert(QStringLiteral("about_newspage"), QStringLiteral("https://skrooge.org/news")); iMapping.insert(QStringLiteral("about_operationpage"), QStringLiteral("skg://Skrooge_operation_plugin/")); iMapping.insert(QStringLiteral("about_accountpage"), QStringLiteral("skg://Skrooge_bank_plugin/")); iMapping.insert(QStringLiteral("about_importurl"), QStringLiteral("skg://import_operation/")); iMapping.insert(QStringLiteral("about_maintext"), i18nc("The main text of skrooge", "Skrooge allows you to keep a hold on your expenses, by tracking and budgeting them.
" "What should you do now ?
" "" "

You may come back to this page any time by closing all tabs.
" - "For more information about using Skrooge, check the Skrooge website.

" + "For more information about using Skrooge, check the Skrooge website.

" "

We hope that you will enjoy Skrooge

" " The Skrooge Team", iMapping[QStringLiteral("about_accountpage")].toString(), iMapping[QStringLiteral("about_operationpage")].toString(), iMapping[QStringLiteral("about_importurl")].toString())); iMapping.insert(QStringLiteral("logo"), QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("icons/breeze/apps/48/skrooge.svg"))).url()); iMapping.insert(QStringLiteral("logo_black"), QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("icons/breeze/apps/48/skrooge-black.svg"))).url()); iMapping.insert(QStringLiteral("title_main"), i18nc("A monthly report title", "Report for %1", getPeriod())); iMapping.insert(QStringLiteral("title_budget"), i18nc("A monthly report title", "Budget")); iMapping.insert(QStringLiteral("title_main_categories"), i18nc("A monthly report title", "5 main categories of expenditure")); iMapping.insert(QStringLiteral("title_variations"), i18nc("A monthly report title", "5 main variations")); iMapping.insert(QStringLiteral("title_account"), i18nc("A monthly report title", "Amounts in accounts")); iMapping.insert(QStringLiteral("title_unit"), i18nc("A monthly report title", "Amounts of units")); iMapping.insert(QStringLiteral("title_advice"), i18nc("A monthly report title", "Advice")); iMapping.insert(QStringLiteral("title_portfolio"), i18nc("A monthly report title", "Stock portfolio")); iMapping.insert(QStringLiteral("title_interests"), i18nc("A monthly report title", "Estimated interests")); iMapping.insert(QStringLiteral("title_alarms"), i18nc("A monthly report title", "Alarms")); iMapping.insert(QStringLiteral("title_highlighted"), i18nc("A monthly report title", "Highlighted operations")); iMapping.insert(QStringLiteral("title_networth"), i18nc("A monthly report title", "Net Worth")); iMapping.insert(QStringLiteral("title_annual_spending"), i18nc("A monthly report title", "Annual Spending")); iMapping.insert(QStringLiteral("title_personal_finance_score"), i18nc("A monthly report title", "Personal Finance Score")); iMapping.insert(QStringLiteral("msg_no_variation"), i18nc("A monthly report message", "No variation found.")); iMapping.insert(QStringLiteral("msg_no_scheduled"), i18nc("A monthly report message", R"(No scheduled operations defined on the "Scheduled operations" page.)", "skg://Skrooge_scheduled_plugin/")); iMapping.insert(QStringLiteral("msg_no_highlighted"), i18nc("A monthly report message", R"(No highlighted operations defined on the "Operations" page.)", "skg://Skrooge_operation_plugin/")); iMapping.insert(QStringLiteral("msg_no_budget"), i18nc("A monthly report message", R"(No budget defined on the "Budget" page.)", "skg://Skrooge_budget_plugin/")); iMapping.insert(QStringLiteral("msg_no_share"), i18nc("A monthly report message", R"(No share defined on the "Unit" page.)", "skg://Skrooge_unit_plugin/")); iMapping.insert(QStringLiteral("msg_amount_unit_date"), i18nc("A monthly report message", "All amounts are calculated using the unit rates of the last day of the corresponding period.")); } diff --git a/skgbankmodeler/skgunitobject.cpp b/skgbankmodeler/skgunitobject.cpp index f913401c9..f9a98c525 100644 --- a/skgbankmodeler/skgunitobject.cpp +++ b/skgbankmodeler/skgunitobject.cpp @@ -1,2578 +1,2578 @@ /*************************************************************************** * 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 defines classes SKGUnitObject. * * @author Stephane MANKOWSKI / Guillaume DE BURE */ #include "skgunitobject.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "skgdocumentbank.h" #include "skgoperationobject.h" #include "skgtraces.h" #include "skgtransactionmng.h" #include "skgunitvalueobject.h" SKGUnitObject::SKGUnitObject() : SKGUnitObject(nullptr) {} SKGUnitObject::SKGUnitObject(SKGDocument* iDocument, int iID) : SKGNamedObject(iDocument, QStringLiteral("v_unit"), iID) {} SKGUnitObject::~SKGUnitObject() = default; SKGUnitObject::SKGUnitObject(const SKGUnitObject& iObject) = default; SKGUnitObject::SKGUnitObject(const SKGNamedObject& iObject) { if (iObject.getRealTable() == QStringLiteral("unit")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_unit"), iObject.getID()); } } SKGUnitObject::SKGUnitObject(const SKGObjectBase& iObject) { if (iObject.getRealTable() == QStringLiteral("unit")) { copyFrom(iObject); } else { *this = SKGNamedObject(iObject.getDocument(), QStringLiteral("v_unit"), iObject.getID()); } } SKGUnitObject& SKGUnitObject::operator= (const SKGObjectBase& iObject) { copyFrom(iObject); return *this; } QString SKGUnitObject::getWhereclauseId() const { QString output = SKGObjectBase::getWhereclauseId(); // clazy:exclude=skipped-base-method if (output.isEmpty()) { QString name = getName(); if (!name.isEmpty()) { output = "t_name='" % SKGServices::stringToSqlString(name) % '\''; } QString symbol = getSymbol(); if (!symbol.isEmpty()) { if (!output.isEmpty()) { output += QStringLiteral(" OR "); } output += "t_symbol='" % SKGServices::stringToSqlString(symbol) % '\''; } if (!output.isEmpty()) { output = '(' % output % ')'; } } return output; } QList SKGUnitObject::currencies; QStringList SKGUnitObject::getListofKnownCurrencies(bool iIncludingObsolete) { SKGTRACEINFUNC(10) if (currencies.isEmpty()) { // Search currencies const QStringList dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/currency/"), QStandardPaths::LocateDirectory); for (const auto& dir : dirs) { auto listDesktopFiles = QDir(dir).entryList(QStringList() << QStringLiteral("*.desktop")); for (const auto& path : qAsConst(listDesktopFiles)) { // Read the file QFileInfo file(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/currency/") + path)); KConfig cgFile(file.absoluteFilePath()); KConfigGroup cg(&cgFile, QStringLiteral("Currency Code")); SKGServices::SKGUnitInfo unit; unit.Name = cg.readEntry(QStringLiteral("Name"), QString()) + QStringLiteral(" (") + cg.readEntry(QStringLiteral("CurrencyCodeIsoAlpha3"), QString()) + QStringLiteral(")"); unit.Symbol = cg.readEntry(QStringLiteral("CurrencyUnitSymbolDefault"), QString()); if (unit.Symbol.isEmpty()) { unit.Symbol = cg.readEntry(QStringLiteral("CurrencyCodeIsoAlpha3"), file.baseName()); } unit.Value = 1; unit.NbDecimal = cg.readEntry(QStringLiteral("CurrencyDecimalPlacesDisplay"), 2); unit.Source = QStringLiteral("GrandTrunk"); unit.Date = cg.readEntry(QStringLiteral("CurrencyIntroducedDate"), QDate::currentDate()); unit.Obsolete = (cg.readEntry(QStringLiteral("CurrencySuspendedDate"), cg.readEntry(QStringLiteral("CurrencyWithdrawnDate"), QDate())) != QDate()); currencies.push_back(unit); } } // Add other units { // CAC40 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "CAC 40"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "France"); info.Date = QDate(1987, 1, 1); info.Internet = QStringLiteral("^FCHI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // NASDAQ SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "NASDAQ"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "United States"); info.Date = QDate(1971, 2, 5); info.Internet = QStringLiteral("^IXIC"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // Dow Jones SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "Dow Jones (DJIA)"); info.Symbol = QStringLiteral("DJIA"); info.Country = i18nc("Noun, a country", "United States"); info.Date = QDate(1884, 1, 1); info.Internet = QStringLiteral("^DJI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // SBF 120 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "SBF 120"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "France"); info.Date = QDate(1990, 12, 31); info.Internet = QStringLiteral("^SBF120"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // S&P 500 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "S&P 500"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "United States"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^GSPC"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // FTSE 100 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "FTSE 100"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "England"); info.Date = QDate(1984, 1, 3); info.Internet = QStringLiteral("^FTSE"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // DAX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "DAX"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "Germany"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^GDAXI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // NIKKEI 225 SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "NIKKEI 225"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "Japan"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^N225"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // HANG SENG SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "HANG SENG"); info.Symbol = info.Name; info.Country = i18nc("Noun, a country", "China"); info.Date = QDate(1920, 1, 1); info.Internet = QStringLiteral("^HSI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // STRAITS TIMES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "STRAITS TIMES"); info.Symbol = info.Name; info.Date = QDate(1920, 1, 1); info.Country = i18nc("Noun, a country", "Singapore"); info.Internet = QStringLiteral("^STI"); info.Source = QStringLiteral("Yahoo"); info.NbDecimal = 2; info.Value = -1; currencies.push_back(info); } { // BITCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a currency", "Bitcoin"); info.Symbol = QStringLiteral("BTC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHEREUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ethereum"); info.Symbol = QStringLiteral("ETH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RIPPLE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ripple"); info.Symbol = QStringLiteral("XRP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XRP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCOIN-CASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bitcoin Cash"); info.Symbol = QStringLiteral("BCH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BCH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // CARDANO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Cardano"); info.Symbol = QStringLiteral("ADA"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ADA"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "NEM"); info.Symbol = QStringLiteral("XEM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XEM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // LITECOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Litecoin"); info.Symbol = QStringLiteral("LTC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LTC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STELLAR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Stellar"); info.Symbol = QStringLiteral("XLM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XLM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // IOTA SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "IOTA"); info.Symbol = QStringLiteral("MIOTA"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("MIOTA"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TRON SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "TRON"); info.Symbol = QStringLiteral("TRX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("TRX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dash"); info.Symbol = QStringLiteral("DASH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DASH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "NEO"); info.Symbol = QStringLiteral("NEO"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NEO"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // MONERO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Monero"); info.Symbol = QStringLiteral("XMR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XMR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // EOS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "EOS"); info.Symbol = QStringLiteral("EOS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("EOS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // QTUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Qtum"); info.Symbol = QStringLiteral("QTUM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("QTUM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ICON SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ICON"); info.Symbol = QStringLiteral("ICX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ICX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCOIN-GOLD SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bitcoin Gold"); info.Symbol = QStringLiteral("BTG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // LISK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Lisk"); info.Symbol = QStringLiteral("LSK"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LSK"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RAIBLOCKS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "RaiBlocks"); info.Symbol = QStringLiteral("XRB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XRB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHEREUM-CLASSIC SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ethereum Classic"); info.Symbol = QStringLiteral("ETC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // VERGE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Verge"); info.Symbol = QStringLiteral("XVG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XVG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SIACOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Siacoin"); info.Symbol = QStringLiteral("SC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // OMISEGO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "OmiseGO"); info.Symbol = QStringLiteral("OMG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("OMG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BYTECOIN-BCN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bytecoin"); info.Symbol = QStringLiteral("BCN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BCN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCONNECT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BitConnect"); info.Symbol = QStringLiteral("BCC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BCC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // POPULOUS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Populous"); info.Symbol = QStringLiteral("PPT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PPT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STRATIS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Stratis"); info.Symbol = QStringLiteral("STRAT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("STRAT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ZCASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Zcash"); info.Symbol = QStringLiteral("ZEC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ZEC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DENTACOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dentacoin"); info.Symbol = QStringLiteral("DCN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DCN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITSHARES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BitShares"); info.Symbol = QStringLiteral("BTS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BINANCE-COIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Binance Coin"); info.Symbol = QStringLiteral("BNB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BNB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DOGECOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dogecoin"); info.Symbol = QStringLiteral("DOGE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DOGE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STATUS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Status"); info.Symbol = QStringLiteral("SNT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SNT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ARDOR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ardor"); info.Symbol = QStringLiteral("ARDR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ARDR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KUCOIN-SHARES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "KuCoin Shares"); info.Symbol = QStringLiteral("KCS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KCS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TETHER SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Tether"); info.Symbol = QStringLiteral("USDT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("USDT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // STEEM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Steem"); info.Symbol = QStringLiteral("STEEM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("STEEM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // WAVES SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Waves"); info.Symbol = QStringLiteral("WAVES"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("WAVES"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // VECHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "VeChain"); info.Symbol = QStringLiteral("VEN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("VEN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DIGIBYTE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DigiByte"); info.Symbol = QStringLiteral("DGB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DGB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KOMODO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Komodo"); info.Symbol = QStringLiteral("KMD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KMD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DRAGONCHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dragonchain"); info.Symbol = QStringLiteral("DRGN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DRGN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AUGUR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Augur"); info.Symbol = QStringLiteral("REP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("REP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // GOLEM-NETWORK-TOKENS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Golem"); info.Symbol = QStringLiteral("GNT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GNT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // VERITASEUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Veritaseum"); info.Symbol = QStringLiteral("VERI"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("VERI"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // HSHARE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Hshare"); info.Symbol = QStringLiteral("HSR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("HSR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Kin"); info.Symbol = QStringLiteral("KIN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KIN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SALT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "SALT"); info.Symbol = QStringLiteral("SALT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SALT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ELECTRONEUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Electroneum"); info.Symbol = QStringLiteral("ETN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ARK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ark"); info.Symbol = QStringLiteral("ARK"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ARK"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DENT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Dent"); info.Symbol = QStringLiteral("DENT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DENT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHOS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Ethos"); info.Symbol = QStringLiteral("ETHOS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ETHOS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BASIC-ATTENTION-TOKEN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Basic Attention Token"); info.Symbol = QStringLiteral("BAT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BAT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // REDDCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ReddCoin"); info.Symbol = QStringLiteral("RDD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("RDD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // 0X SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "0x"); info.Symbol = QStringLiteral("ZRX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ZRX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DECRED SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Decred"); info.Symbol = QStringLiteral("DCR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DCR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEXUS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Nexus"); info.Symbol = QStringLiteral("NXS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NXS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // EXPERIENCE-POINTS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Experience Points"); info.Symbol = QStringLiteral("XP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // QASH SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "QASH"); info.Symbol = QStringLiteral("QASH"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("QASH"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // KYBER-NETWORK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Kyber Network"); info.Symbol = QStringLiteral("KNC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("KNC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // PIVX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "PIVX"); info.Symbol = QStringLiteral("PIVX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PIVX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // FUNFAIR SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "FunFair"); info.Symbol = QStringLiteral("FUN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("FUN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // FACTOM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Factom"); info.Symbol = QStringLiteral("FCT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("FCT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NEBLIO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Neblio"); info.Symbol = QStringLiteral("NEBL"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NEBL"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // REQUEST-NETWORK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Request Network"); info.Symbol = QStringLiteral("REQ"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("REQ"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AETERNITY SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Aeternity"); info.Symbol = QStringLiteral("AE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("AE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SUBSTRATUM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Substratum"); info.Symbol = QStringLiteral("SUB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SUB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // POWER-LEDGER SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Power Ledger"); info.Symbol = QStringLiteral("POWR"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("POWR"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // WAX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "WAX"); info.Symbol = QStringLiteral("WAX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("WAX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AELF SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "aelf"); info.Symbol = QStringLiteral("ELF"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ELF"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BYTOM SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Bytom"); info.Symbol = QStringLiteral("BTM"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTM"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // AION SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Aion"); info.Symbol = QStringLiteral("AION"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("AION"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RCHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "RChain"); info.Symbol = QStringLiteral("RHOC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("RHOC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DIGITALNOTE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DigitalNote"); info.Symbol = QStringLiteral("XDN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XDN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ENIGMA-PROJECT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Enigma"); info.Symbol = QStringLiteral("ENG"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ENG"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // NXT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Nxt"); info.Symbol = QStringLiteral("NXT"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("NXT"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TIME-NEW-BANK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Time New Bank"); info.Symbol = QStringLiteral("TNB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("TNB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BITCOINDARK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BitcoinDark"); info.Symbol = QStringLiteral("BTCD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("BTCD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // MONACOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "MonaCoin"); info.Symbol = QStringLiteral("MONA"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("MONA"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // QUANTSTAMP SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Quantstamp"); info.Symbol = QStringLiteral("QSP"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("QSP"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // MAIDSAFECOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "MaidSafeCoin"); info.Symbol = QStringLiteral("MAID"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("MAID"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BYTEBALL SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Byteball Bytes"); info.Symbol = QStringLiteral("GBYTE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GBYTE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // GAS SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Gas"); info.Symbol = QStringLiteral("GAS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GAS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // CHAINLINK SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ChainLink"); info.Symbol = QStringLiteral("LINK"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LINK"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SYSCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Syscoin"); info.Symbol = QStringLiteral("SYS"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SYS"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // SANTIMENT SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Santiment Network Token"); info.Symbol = QStringLiteral("SAN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("SAN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // COBINHOOD SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Cobinhood"); info.Symbol = QStringLiteral("COB"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("COB"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // RED-PULSE SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Red Pulse"); info.Symbol = QStringLiteral("RPX"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("RPX"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DIGIXDAO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DigixDAO"); info.Symbol = QStringLiteral("DGD"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DGD"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // TENX SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "TenX"); info.Symbol = QStringLiteral("PAY"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PAY"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ICONOMI SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Iconomi"); info.Symbol = QStringLiteral("ICN"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("ICN"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // POET SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Po.et"); info.Symbol = QStringLiteral("POE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("POE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ZCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ZCoin"); info.Symbol = QStringLiteral("XZC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("XZC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // GNOSIS-GNO SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Gnosis"); info.Symbol = QStringLiteral("GNO"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("GNO"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // BLOCKV SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "BLOCKv"); info.Symbol = QStringLiteral("VEE"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("VEE"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // WALTON SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Walton"); info.Symbol = QStringLiteral("WTC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("WTC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // PACCOIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "PACcoin"); info.Symbol = QStringLiteral("PAC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("PAC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // DEEPBRAIN-CHAIN SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "DeepBrain Chain"); info.Symbol = QStringLiteral("DBC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("DBC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // ETHLEND SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "ETHLend"); info.Symbol = QStringLiteral("LEND"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("LEND"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } { // CIVIC SKGServices::SKGUnitInfo info; info.Name = i18nc("Noun, a cryptocurrency", "Civic"); info.Symbol = QStringLiteral("CVC"); info.Date = QDate(2009, 2, 4); info.Country = i18nc("Noun, the country of bitcoin", "Internet"); info.Internet = QStringLiteral("CVC"); info.Source = QStringLiteral("CoinMarketCap"); info.Parent = QStringLiteral("USD"); info.NbDecimal = 4; info.Value = -1; currencies.push_back(info); } } QStringList output; output.reserve(currencies.count()); for (const auto& unit : qAsConst(currencies)) { if (iIncludingObsolete || !unit.Obsolete) { output.push_back(unit.Name); } } output.sort(); return output; } QString SKGUnitObject::getInternationalCode(const QString& iUnitName) { SKGTRACEINFUNC(10) QString output = iUnitName; QRegExp rx(QStringLiteral(".*\\(([^\\(\\)]+)\\)[^\\(\\)]*")); if (rx.indexIn(iUnitName) != -1) { output = rx.cap(1); } return output; } SKGServices::SKGUnitInfo SKGUnitObject::getUnitInfo() { SKGTRACEINFUNC(10) SKGServices::SKGUnitInfo info; info.Name = getName(); info.Value = getAmount(); info.NbDecimal = getNumberDecimal(); info.Symbol = getSymbol(); info.Country = getCountry(); info.Internet = getInternetCode(); info.Date = QDate::currentDate(); return info; } SKGServices::SKGUnitInfo SKGUnitObject::getUnitInfo(const QString& iUnitName) { SKGTRACEINFUNC(10) SKGServices::SKGUnitInfo info; if (currencies.isEmpty()) { getListofKnownCurrencies(false); } QString isoCode = getInternationalCode(iUnitName); for (const auto& unit : qAsConst(currencies)) { if (getInternationalCode(unit.Name) == isoCode) { info = unit; break; } } return info; } SKGError SKGUnitObject::createCurrencyUnit(SKGDocumentBank* iDocument, const QString& iUnitName, SKGUnitObject& oUnit) { SKGError err; SKGTRACEINFUNCRC(10, err) if (iDocument != nullptr) { SKGUnitObject parentUnit; oUnit = SKGUnitObject(iDocument); SKGUnitObject::UnitType type = SKGUnitObject::CURRENCY; SKGServices::SKGUnitInfo prim = iDocument->getPrimaryUnit(); SKGServices::SKGUnitInfo seco = iDocument->getSecondaryUnit(); // Get information on the unit SKGServices::SKGUnitInfo info = getUnitInfo(iUnitName); if (info.Name.isEmpty()) { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Unknown unit '%1'", iUnitName)); } if (!err && !info.Parent.isEmpty()) { err = createCurrencyUnit(iDocument, info.Parent, parentUnit); } // Set the type if (info.Name == info.Symbol) { // This is an index type = SKGUnitObject::INDEX; } else if (!info.Parent.isEmpty()) { // This is a secondary unit type = (seco.Symbol.isEmpty() || seco.Symbol == info.Symbol ? SKGUnitObject::SECONDARY : SKGUnitObject::CURRENCY); } else { // As primary type = (prim.Symbol.isEmpty() || prim.Symbol == info.Symbol ? SKGUnitObject::PRIMARY : SKGUnitObject::CURRENCY); } // Point on primary unit if (info.Value == 1 && !err && (type == SKGUnitObject::CURRENCY || type == SKGUnitObject::SECONDARY)) { SKGUnitObject primunit(iDocument); err = primunit.setSymbol(prim.Symbol); IFOKDO(err, primunit.load()) IFOK(err) { QString codeprimunit = getInternationalCode(primunit.getName()); QString codeunit = getInternationalCode(info.Name); if (!codeprimunit.isEmpty()) { info.Internet = codeunit % '/' % codeprimunit; info.Value = -1; parentUnit = SKGUnitObject(iDocument); err = parentUnit.setSymbol(prim.Symbol); IFOKDO(err, parentUnit.load()) } } } IFOKDO(err, oUnit.setName(info.Name)) if (!err && oUnit.exist()) { err = oUnit.load(); } IFOKDO(err, oUnit.setType(type)) IFOKDO(err, oUnit.setSymbol(info.Symbol)) IFOKDO(err, oUnit.setInternetCode(info.Internet)) IFOKDO(err, oUnit.setDownloadSource(info.Source)) IFOKDO(err, oUnit.setCountry(info.Country)) IFOKDO(err, oUnit.setNumberDecimal(info.NbDecimal)) if (!err && parentUnit.exist()) { err = oUnit.setUnit(parentUnit); } IFOKDO(err, oUnit.save()) // Creation of the value if (info.Value > 0) { SKGUnitValueObject unitValue; IFOKDO(err, oUnit.addUnitValue(unitValue)) IFOKDO(err, unitValue.setDate(info.Date)) IFOKDO(err, unitValue.setQuantity(info.Value)) IFOKDO(err, unitValue.save()) } } return err; } double SKGUnitObject::convert(double iValue, const SKGUnitObject& iUnitFrom, const SKGUnitObject& iUnitTo, QDate iDate) { double output = iValue; if (iUnitFrom != iUnitTo) { double valFrom = iUnitFrom.getAmount(iDate); double valTo = iUnitTo.getAmount(iDate); output = iValue * valFrom / valTo; } return output; } SKGError SKGUnitObject::setSymbol(const QString& iSymbol) { return setAttribute(QStringLiteral("t_symbol"), iSymbol); } QString SKGUnitObject::getSymbol() const { return getAttribute(QStringLiteral("t_symbol")); } QString SKGUnitObject::getDownloadSource() const { return getAttribute(QStringLiteral("t_source")); } SKGError SKGUnitObject::setDownloadSource(const QString& iSource) { return setAttribute(QStringLiteral("t_source"), iSource); } SKGError SKGUnitObject::setNumberDecimal(int iNb) { return setAttribute(QStringLiteral("i_nbdecimal"), SKGServices::intToString(iNb)); } int SKGUnitObject::getNumberDecimal() const { return SKGServices::stringToInt(getAttribute(QStringLiteral("i_nbdecimal"))); } SKGError SKGUnitObject::setCountry(const QString& iCountry) { return setAttribute(QStringLiteral("t_country"), iCountry); } QString SKGUnitObject::getCountry() const { return getAttribute(QStringLiteral("t_country")); } SKGError SKGUnitObject::setInternetCode(const QString& iCode) { return setAttribute(QStringLiteral("t_internet_code"), iCode); } QString SKGUnitObject::getInternetCode() const { return getAttribute(QStringLiteral("t_internet_code")); } SKGError SKGUnitObject::setType(SKGUnitObject::UnitType iType) { SKGError err; if (getAttribute(QStringLiteral("t_type")).isEmpty() || this->getType() != iType) { // Guaranty that PRIMARY and SECONDARY is unique if (iType == PRIMARY || iType == SECONDARY) { // Set old SECONDARY as CURRENCY err = getDocument()->executeSqliteOrder(QStringLiteral("UPDATE unit SET t_type='C' WHERE t_type='2'")); // Set old PRIMARY as SECONDARY if (!err && iType == PRIMARY) { err = getDocument()->executeSqliteOrder(QStringLiteral("UPDATE unit SET t_type='2' WHERE t_type='1'")); } } } IFOKDO(err, setAttribute(QStringLiteral("t_type"), (iType == CURRENCY ? QStringLiteral("C") : (iType == PRIMARY ? QStringLiteral("1") : (iType == SECONDARY ? QStringLiteral("2") : (iType == SHARE ? QStringLiteral("S") : (iType == INDEX ? QStringLiteral("I") : QStringLiteral("O")))))))) return err; } SKGUnitObject::UnitType SKGUnitObject::getType() const { QString typeString = getAttribute(QStringLiteral("t_type")); return (typeString == QStringLiteral("C") ? CURRENCY : (typeString == QStringLiteral("S") ? SHARE : (typeString == QStringLiteral("1") ? PRIMARY : (typeString == QStringLiteral("2") ? SECONDARY : (typeString == QStringLiteral("I") ? INDEX : OBJECT))))); } SKGError SKGUnitObject::getUnit(SKGUnitObject& oUnit) const { SKGError err; if (getDocument() != nullptr) { err = getDocument()->getObject(QStringLiteral("v_unit"), "id=" % getAttribute(QStringLiteral("rd_unit_id")), oUnit); } return err; } SKGError SKGUnitObject::setUnit(const SKGUnitObject& iUnit) { SKGError err; if (*this == iUnit && iUnit.getID() != 0) { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Reference unit of a unit cannot be itself.")); } else { err = setAttribute(QStringLiteral("rd_unit_id"), SKGServices::intToString(iUnit.getID())); } return err; } SKGError SKGUnitObject::removeUnit() { return setAttribute(QStringLiteral("rd_unit_id"), QStringLiteral("0")); } SKGError SKGUnitObject::addUnitValue(SKGUnitValueObject& oUnitValue) { SKGError err; if (getID() == 0) { err = SKGError(ERR_FAIL, i18nc("Error message", "%1 failed because linked object is not yet saved in the database.", QStringLiteral("SKGUnitObject::addUnitValue"))); } else { oUnitValue = SKGUnitValueObject(qobject_cast(getDocument())); err = oUnitValue.setAttribute(QStringLiteral("rd_unit_id"), SKGServices::intToString(getID())); } return err; } SKGError SKGUnitObject::simplify() { SKGListSKGObjectBase values; SKGError err = getUnitValues(values); int nb = values.count(); if (!err && (nb != 0)) { QHash groups; SKGBEGINPROGRESSTRANSACTION(*getDocument(), "#INTERNAL#" % i18nc("Progression step", "Simplify unit"), err, 2) // Build groups { SKGBEGINPROGRESSTRANSACTION(*getDocument(), "#INTERNAL#" % i18nc("Progression step", "Analyze unit"), err, nb) QDate limit1 = QDate::currentDate().addMonths(-3); QDate limit2 = QDate::currentDate().addYears(-1); QDate limit3 = QDate::currentDate().addYears(-3); for (int i = nb - 1; !err && i >= 0 ; --i) { SKGUnitValueObject v(values.at(i)); QDate date = v.getDate(); if (date >= limit1) { // No simplification ==> nothing to do } else if (date >= limit2) { // Simplification by group of 30 days QString key = limit1.addDays(30 * (limit1.daysTo(date) / 30)).toString(); SKGListSKGObjectBase group = groups[key]; group.push_back(v); groups[key] = group; } else if (date >= limit3) { // Simplification by group of 2 months QString key = limit2.addDays(60 * (limit2.daysTo(date) / 60)).toString(); SKGListSKGObjectBase group = groups[key]; group.push_back(v); groups[key] = group; } else { // Simplification by group of 6 months QString key = limit3.addDays(180 * (limit2.daysTo(date) / 180)).toString(); SKGListSKGObjectBase group = groups[key]; group.push_back(v); groups[key] = group; } IFOKDO(err, getDocument()->stepForward(nb - i)) } } IFOKDO(err, getDocument()->stepForward(1)) // Simplify { QList keys = groups.keys(); nb = keys.count(); SKGBEGINPROGRESSTRANSACTION(*getDocument(), "#INTERNAL#" % i18nc("Progression step", "Remove useless values"), err, nb) for (int i = 0; !err && i < nb ; ++i) { const QString& k = keys.at(i); SKGListSKGObjectBase group = groups[k]; // Simplify the group int nb2 = group.count(); // Compute min and max double min = 10e20; double max = 0; for (int j = 0; j < nb2; ++j) { SKGUnitValueObject v1(group.at(j)); double y1 = v1.getQuantity(); min = qMin(y1, min); max = qMax(y1, max); } // Simplify for (int j = 1; !err && j < nb2 - 1; ++j) { SKGUnitValueObject v1(group.at(j)); double y1 = v1.getQuantity(); if (y1 != min && y1 != max) { err = v1.remove(); } } IFOKDO(err, getDocument()->stepForward(i + 1)) } } IFOKDO(err, getDocument()->stepForward(2)) } return err; } SKGError SKGUnitObject::getUnitValues(SKGListSKGObjectBase& oUnitValueList) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_unitvalue"), "rd_unit_id=" % SKGServices::intToString(getID()) % " ORDER BY d_date", oUnitValueList); return err; } SKGError SKGUnitObject::getLastUnitValue(SKGUnitValueObject& oUnitValue) const { return SKGObjectBase::getDocument()->getObject(QStringLiteral("v_unitvalue"), "rd_unit_id=" % SKGServices::intToString(getID()) % " AND d_date=(select MAX(u2.d_date) from unitvalue u2 where u2.rd_unit_id=" % SKGServices::intToString(getID()) % ')', oUnitValue); } SKGError SKGUnitObject::getUnitValue(QDate iDate, SKGUnitValueObject& oUnitValue) const { QString ids = SKGServices::intToString(getID()); QString dates = SKGServices::dateToSqlString(QDateTime(iDate)); SKGError err = SKGObjectBase::getDocument()->getObject(QStringLiteral("v_unitvalue"), "rd_unit_id=" % ids % " AND d_date<='" % dates % "' AND ABS(strftime('%s','" % dates % "')-strftime('%s',d_date))=(select MIN(ABS(strftime('%s','" % dates % "')-strftime('%s',u2.d_date))) from unitvalue u2 where u2.rd_unit_id=" % ids % " AND u2.d_date<='" % dates % "')", oUnitValue); // If not found then get first IFKO(err) err = SKGObjectBase::getDocument()->getObject(QStringLiteral("v_unitvalue"), "rd_unit_id=" % SKGServices::intToString(getID()) % " AND d_date=(select MIN(d_date) from unitvalue where rd_unit_id=" % SKGServices::intToString(getID()) % ')', oUnitValue); return err; } double SKGUnitObject::getAmount(QDate iDate) const { SKGTRACEINFUNC(10) double output = 0; if (getType() == SKGUnitObject::PRIMARY) { output = 1.0; } else if (getDocument() != nullptr) { // Search result in cache QString ids = SKGServices::intToString(getID()); QString dates = SKGServices::dateToSqlString(QDateTime(iDate)); QString key = "unitvalue-" % ids % '-' % dates; QString val = getDocument()->getCachedValue(key); if (val.isEmpty()) { // Get quantity double quantity = 1; SKGUnitValueObject uv; if (getUnitValue(iDate, uv).isSucceeded()) { quantity = uv.getQuantity(); } SKGUnitObject unit; double coef = 1; if (getUnit(unit).isSucceeded()) { if (unit != *this) { coef = unit.getAmount(iDate); } } output = coef * quantity; getDocument()->addValueInCache(key, SKGServices::doubleToString(output)); if (getAttribute(QStringLiteral("i_NBVALUES")) == QStringLiteral("1")) { // Store value for this symbol for all date getDocument()->addValueInCache("unitvalue-" % ids, SKGServices::doubleToString(output)); } } else { output = SKGServices::stringToDouble(val); } } return output; } double SKGUnitObject::getDailyChange(QDate iDate) const { double output = 0; SKGStringListList result; SKGError err = getDocument()->executeSelectSqliteOrder( "SELECT d_date, f_quantity from unitvalue where rd_unit_id=" % SKGServices::intToString(getID()) % " AND d_date<='" % SKGServices::dateToSqlString(QDateTime(iDate)) % "' ORDER BY d_date DESC LIMIT 2", result); if (!err && result.count() == 3) { double v2 = SKGServices::stringToDouble(result.at(1).at(1)); double v1 = SKGServices::stringToDouble(result.at(2).at(1)); QDate d2 = SKGServices::stringToTime(result.at(1).at(0)).date(); QDate d1 = SKGServices::stringToTime(result.at(2).at(0)).date(); output = 100 * (qExp(qLn(v2 / v1) / (SKGServices::nbWorkingDays(d1, d2))) - 1); } return output; } SKGError SKGUnitObject::split(double iRatio) const { SKGError err; if (iRatio > 0) { err = getDocument()->executeSqliteOrder("UPDATE unitvalue SET f_quantity=f_quantity/" % SKGServices::doubleToString(iRatio) % " WHERE rd_unit_id=" % SKGServices::intToString(getID())); IFOKDO(err, getDocument()->executeSqliteOrder("UPDATE suboperation SET f_value=f_value*" % SKGServices::doubleToString(iRatio) % " WHERE rd_operation_id IN (SELECT id FROM operation WHERE rc_unit_id=" % SKGServices::intToString(getID()) % ')')); } else { err = SKGError(ERR_INVALIDARG, i18nc("Error message", "Invalid ratio. Ratio must be greater than 0.")); } return err; } QStringList SKGUnitObject::downloadSources() { QStringList sources; // Get sources from .txt file (old mode) TODO: Remove QString a = QStringLiteral("skrooge/quotes"); const auto list = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, a, QStandardPaths::LocateDirectory); for (const auto& dir : list) { QDirIterator it(dir, QStringList() << QStringLiteral("*.txt")); while (it.hasNext()) { QFileInfo f(it.next()); QString file2 = f.completeBaseName(); if (!sources.contains(file2)) { sources.push_back(file2); } } } // Get sources from.desktop file const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (!sources.contains(name)) { sources.push_back(name); } } sources.sort(); return sources; } QString SKGUnitObject::getCommentFromSource(const QString& iSource) { QString output; const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (name == iSource) { output = service->property(QStringLiteral("Comment"), QVariant::String).toString(); break; } } return output; } SKGError SKGUnitObject::addSource(const QString& iNewSource, bool iOpenSource) { SKGError err; QString path = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation); QDir(path).mkpath(QStringLiteral("skrooge/quotes/")); QString newfile = path + "/skrooge/quotes/" % iNewSource % ".txt"; // Create the new file QSaveFile file(newfile); // Check if file already existing if (!QFile(newfile).exists()) { // Creation of the template if (!file.open(QIODevice::WriteOnly)) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Save file '%1' failed", newfile)); } else { QTextStream out(&file); out << "#" << i18nc("Description test for a text file used to define a source of download", "The URL or the SCRIPT of the source. %1 will be replaced by the internet code of the unit", "%1") << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "%1 will be replaced by the current day in format yyyy-MM-dd", "%2") << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "%1 will be replaced by the previous date in format yyyy-MM-dd", "%3") << endl; - out << "url=http://server/?s=%1" << endl; + out << "url=https://server/?s=%1" << endl; out << "or" << endl; out << "script=mydownloadscript %1" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", "The mode (HTML or CSV or CSVR). In HTML mode, only one value will be extracted from downloaded page. In CSV mode, a value per line will be extracted. CSVR means CSV in reverse mode.") << endl; out << "mode=CSV, CSVR or or HTML" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", - "The regular expression for the price (see %1)", "http://doc.qt.io/qt-5/qregexp.html") << endl; + "The regular expression for the price (see %1)", "https://doc.qt.io/qt-5/qregexp.html") << endl; out << "price=" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", - "The regular expression for the date (see %1)", "http://doc.qt.io/qt-5/qregexp.html") << endl; + "The regular expression for the date (see %1)", "https://doc.qt.io/qt-5/qregexp.html") << endl; out << "date=" << endl << endl; out << "#" << i18nc("Description test for a text file used to define a source of download", - "The format of the date (see %1) or \"UNIX\" for unix time", "http://doc.qt.io/qt-5/qdate.html#fromString-1") << endl; + "The format of the date (see %1) or \"UNIX\" for unix time", "https://doc.qt.io/qt-5/qdate.html#fromString-1") << endl; out << "dateformat=yyyy-MM-dd" << endl; // Close file file.commit(); } } // Open the created or already existing file if (iOpenSource) { QDesktopServices::openUrl(QUrl::fromLocalFile(newfile)); } return err; } SKGError SKGUnitObject::deleteSource(const QString& iSource) { SKGError err; QString fileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("skrooge/quotes/") % iSource % ".txt"; // Delete the file QFile file(fileName); if (!file.remove()) { err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Deletion of '%1' failed", fileName)); } return err; } bool SKGUnitObject::isWritable(const QString& iSource) { QString fileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + QStringLiteral("skrooge/quotes/") % iSource % ".txt"; return QFileInfo(fileName).isWritable(); } SKGError SKGUnitObject::downloadUnitValue(UnitDownloadMode iMode, int iNbMaxValues) { SKGError err; SKGTRACEINFUNCRC(10, err) QString unitname = getName(); QString code = getInternetCode(); bool invert = code.contains(QStringLiteral(" /")); code.remove(QStringLiteral(" /")); QString source = getDownloadSource(); auto* doc = qobject_cast(getDocument()); if (!code.isEmpty() && (doc != nullptr)) { // Get last date QDate firstDate; SKGStringListList result; doc->executeSelectSqliteOrder("SELECT MAX(d_date) FROM unitvalue where rd_unit_id=" % SKGServices::intToString(getID()), result); if (result.count() == 2) { firstDate = SKGServices::stringToTime(result.at(1).at(0)).date().addDays(-1); } if (code.startsWith(QLatin1String("="))) { // MODE FORMULAR // Set 1st january if date is not found if (!firstDate.isValid()) { firstDate.setDate(QDate::currentDate().year(), 1, 1); } // Compute yearly rate double rate = SKGServices::stringToDouble(code.right(code.length() - 1)); // Compute rate for step double step = (iMode == LAST_MONTHLY || iMode == ALL_MONTHLY ? 12.0 : (iMode == LAST_WEEKLY || iMode == ALL_WEEKLY ? 365.0 / 7.0 : 365)); double rate2 = 100.0 * (qExp(qLn(1 + rate / 100.0) / step) - 1.0); // Get last value SKGStringListList result2; double value = 100; doc->executeSelectSqliteOrder("SELECT f_quantity FROM unitvalue where rd_unit_id=(SELECT id from unit where t_name='" % SKGServices::stringToSqlString(unitname) % "') AND d_date='" % SKGServices::dateToSqlString(QDateTime(firstDate)) % '\'', result2); if (result2.count() == 2) { value = SKGServices::stringToDouble(result2.at(1).at(0)); } // Compute and add values while (!err && firstDate <= QDate::currentDate()) { // Compute next date and value if (iMode == LAST_MONTHLY || iMode == ALL_MONTHLY) { firstDate = firstDate.addMonths(1); } else if (iMode == LAST_WEEKLY || iMode == ALL_WEEKLY) { firstDate = firstDate.addDays(7); } else { firstDate = firstDate.addDays(1); } value *= (1 + rate2 / 100.0); // Create value SKGUnitValueObject val; err = addUnitValue(val); IFOKDO(err, val.setDate(firstDate)) IFOKDO(err, val.setQuantity(value)) IFOKDO(err, val.save()) } } else { // Quote download // Set 1st january 1970 if date is not found if (!firstDate.isValid()) { firstDate.setDate(1970, 1, 1); } QString interval = QStringLiteral("1d"); bool last = (iMode == LAST); if (last) { interval = QStringLiteral("1d"); } else if (iMode == LAST_MONTHLY) { interval = QStringLiteral("1mo"); } else if (iMode == LAST_WEEKLY) { interval = QStringLiteral("1wk"); } else if (iMode == LAST_DAILY) { interval = QStringLiteral("1d"); } else if (iMode == ALL_MONTHLY) { firstDate = QDate(1970, 01, 01); interval = QStringLiteral("1mo"); } else if (iMode == ALL_WEEKLY) { firstDate = QDate(1970, 01, 01); interval = QStringLiteral("1wk"); } else if (iMode == ALL_DAILY) { firstDate = QDate(1970, 01, 01); interval = QStringLiteral("1d"); } bool modeScript = false; QString path; QString mode; QString price; QString date; QString dateFormat; QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/quotes/" % source % ".txt"); if (fileName.isEmpty()) { const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (name == source) { path = service->property(QStringLiteral("X-SKROOGE-url"), QVariant::String).toString(); if (path.isEmpty()) { path = service->property(QStringLiteral("X-SKROOGE-script"), QVariant::String).toString(); modeScript = true; } mode = service->property(QStringLiteral("X-SKROOGE-mode"), QVariant::String).toString().toUpper(); price = service->property(QStringLiteral("X-SKROOGE-price"), QVariant::String).toString().replace(QStringLiteral("%1"), code); date = service->property(QStringLiteral("X-SKROOGE-date"), QVariant::String).toString().replace(QStringLiteral("%1"), code); dateFormat = service->property(QStringLiteral("X-SKROOGE-dateformat"), QVariant::String).toString(); break; } } if (path.isEmpty()) { err = SKGError(ERR_FAIL, i18nc("Error message", "Source of download %1 is not installed.", source)); } } else { // Read source file from .txt file QHash< QString, QString > properties; err = SKGServices::readPropertyFile(fileName, properties); IFOK(err) { path = properties[QStringLiteral("url")]; if (path.isEmpty()) { path = properties[QStringLiteral("script")]; modeScript = true; } mode = properties[QStringLiteral("mode")].toUpper(); price = properties[QStringLiteral("price")].replace(QStringLiteral("%1"), code); date = properties[QStringLiteral("date")].replace(QStringLiteral("%1"), code); dateFormat = properties[QStringLiteral("dateformat")]; } else { doc->sendMessage(i18nc("An information message", "Open url '%1' failed", path), SKGDocument::Warning); } } IFOK(err) { path = path.replace(QStringLiteral("%1"), code); path = path.replace(QStringLiteral("%2"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))); path = path.replace(QStringLiteral("%3"), firstDate.toString(QStringLiteral("yyyy-MM-dd"))); path = path.replace(QStringLiteral("%4"), interval); SKGTRACEL(1) << "path=[" << path << "]" << endl; SKGTRACEL(1) << "mode=[" << mode << "]" << endl; SKGTRACEL(1) << "price=[" << price << "]" << endl; SKGTRACEL(1) << "date=[" << date << "]" << endl; SKGTRACEL(1) << "dateFormat=[" << dateFormat << "]" << endl; // Download url QByteArray stream; if (modeScript) { path = SKGServices::getFullPathCommandLine(path); SKGTRACEL(1) << "full path=[" << path << "]" << endl; QProcess p; p.start(path); if (p.waitForFinished(1000 * 60 * 2) && p.exitCode() == 0) { stream = p.readAllStandardOutput(); } else { err.setReturnCode(ERR_FAIL).setMessage(i18nc("Error message", "The following command line failed with code %2:\n'%1'", path, p.exitCode())); } } else { SKGServices::downloadToStream(QUrl::fromUserInput(path), stream); } if (!err && !stream.isEmpty()) { SKGTRACEL(1) << "stream=[" << stream << "]" << endl; // Parse data QRegExp priceRegExp(price, Qt::CaseInsensitive); QRegExp dateRegExp(date, Qt::CaseInsensitive); QStringList lines = (mode.startsWith(QLatin1String("CSV")) ? SKGServices::splitCSVLine(stream, '\n') : QStringList() << QLatin1String("") << stream); int nb = lines.count(); int nbLoaded = 0; for (int i = 1; i < nb && !err && (i < iNbMaxValues || iNbMaxValues == 0); ++i) { QString data = lines.at(mode == QStringLiteral("CSVR") ? nb - i : i).trimmed(); if (!data.isEmpty()) { SKGTRACEL(1) << "Downloaded data from [" << path << "]=[" << data << "]" << endl; double val = 0.0; if (priceRegExp.indexIn(data) > -1) { val = SKGServices::stringToDouble(priceRegExp.cap(1)); } QString date2; if (dateRegExp.indexIn(data) > -1) { date2 = dateRegExp.cap(1); } SKGTRACEL(1) << "Price found=[" << val << "]" << endl; SKGTRACEL(1) << "Date found=[" << date2 << "]" << endl; // Set value if (val != 0.0) { QDate ds; if (dateFormat == QStringLiteral("UNIX")) { ds = QDateTime::fromTime_t(SKGServices::stringToInt(date2)).date(); } else { ds = QDate::fromString(date2, dateFormat); // Try with an english locale if (!ds.isValid()) { QLocale en(QStringLiteral("en_EN")); ds = en.toDate(date2, dateFormat); } } if (!ds.isValid()) { ds = QDate::currentDate(); SKGTRACE << "WARNING:" << date2 << " not well parsed with format " << dateFormat << endl; } if (!dateFormat.contains(QStringLiteral("yyyy")) && ds.year() < 2000) { ds = ds.addYears(100); } // Creation or update of the value SKGUnitValueObject value; IFOKDO(err, addUnitValue(value)) IFOKDO(err, value.setDate(ds)) IFOKDO(err, value.setQuantity(invert && val != 0 ? 1 / val : val)) IFOKDO(err, value.save()) if (last) { break; } nbLoaded++; } } if (nbLoaded == 0 && !data.isEmpty()) { err = doc->sendMessage(i18nc("Information message", "Price not found for '%1' with regular expression '%2' in line '%3'", getName(), SKGServices::stringToHtml(price), data), SKGDocument::Warning); // TODO(Stephane MANKOWSKI) does not work with html } } } } } } IFKO(err) { err.addError(ERR_FAIL, i18nc("Error message", "Impossible to download unit %1 with Internet code %2 on the source %3.", unitname, code, source)); } return err; } SKGError SKGUnitObject::getUrl(QUrl& oUrl) const { SKGError err; SKGTRACEINFUNCRC(10, err) QString url; QString code = getInternetCode(); code.remove(QStringLiteral(" /")); QString source = getDownloadSource(); if (!code.isEmpty()) { if (code.startsWith(QLatin1String("="))) { // MODE FORMULAR } else { // ALTERNATIVE MODE QString fileName = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/quotes/" % source % ".txt"); if (fileName.isEmpty()) { const auto list2 = KServiceTypeTrader::self()->query(QStringLiteral("skrooge/source")); for (const auto& service : list2) { auto name = service->property(QStringLiteral("X-KDE-PluginInfo-Name"), QVariant::String).toString(); if (name == source) { url = service->property(QStringLiteral("X-SKROOGE-url"), QVariant::String).toString().replace(QStringLiteral("%1"), code); url = url.replace(QStringLiteral("%2"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))); url = url.replace(QStringLiteral("%3"), QDate::currentDate().addDays(-15).toString(QStringLiteral("yyyy-MM-dd"))); break; } } if (url.isEmpty()) { err = SKGError(ERR_FAIL, i18nc("Error message", "Source of download %1 is not installed.", source)); } } else { // Read source file QHash< QString, QString > properties; err = SKGServices::readPropertyFile(fileName, properties); IFOK(err) { url = properties[QStringLiteral("url")].replace(QStringLiteral("%1"), code); url = url.replace(QStringLiteral("%2"), QDate::currentDate().toString(QStringLiteral("yyyy-MM-dd"))); url = url.replace(QStringLiteral("%3"), QDate::currentDate().addDays(-15).toString(QStringLiteral("yyyy-MM-dd"))); } } } } IFOK(err) { oUrl = QUrl(url); } return err; } SKGError SKGUnitObject::openURL() const { QUrl url; SKGError err = getUrl(url); IFKO(err) { err.addError(ERR_FAIL, i18nc("Error message", "Impossible to open unit %1 with Internet code %2.", getName(), getInternetCode())); } else { QDesktopServices::openUrl(url); } return err; } SKGError SKGUnitObject::getOperations(SKGObjectBase::SKGListSKGObjectBase& oOperations) const { SKGError err = getDocument()->getObjects(QStringLiteral("v_operation"), "rc_unit_id=" % SKGServices::intToString(getID()), oOperations); return err; } SKGError SKGUnitObject::merge(const SKGUnitObject& iUnit) { SKGError err; SKGObjectBase::SKGListSKGObjectBase ops; IFOKDO(err, iUnit.getOperations(ops)) int nb = ops.count(); for (int i = 0; !err && i < nb; ++i) { SKGOperationObject op(ops.at(i)); err = op.setUnit(*this); IFOKDO(err, op.save(true, false)) } IFOKDO(err, iUnit.remove(false)) return err; } diff --git a/skgbankmodeler/sources/org.kde.skrooge-source-grandtrunk.desktop b/skgbankmodeler/sources/org.kde.skrooge-source-grandtrunk.desktop index 9f283caf4..e65d708ce 100644 --- a/skgbankmodeler/sources/org.kde.skrooge-source-grandtrunk.desktop +++ b/skgbankmodeler/sources/org.kde.skrooge-source-grandtrunk.desktop @@ -1,68 +1,68 @@ [Desktop Entry] Name=Grand Trunk Name[ca]=Grand Trunk Name[ca@valencia]=Grand Trunk Name[en_GB]=Grand Trunk Name[es]=Grand Trunk Name[fr]=Grand Trunk Name[gl]=Grand Trunk Name[it]=Grand Trunk Name[nl]=Grand Trunk Name[pl]=Grand Trunk Name[pt]=Grand Trunk Name[sv]=Grand Trunk Name[uk]=Grand Trunk Name[x-test]=xxGrand Trunkxx -Comment=You can get the list of supported currencies from here.
Then, enter the expected currency. -Comment[ca]=Podeu obtenir la llista de les divises admeses aquí.
Després introduïu la divisa esperada. -Comment[ca@valencia]=Podeu obtindre la llista de les divises admeses ací.
Després introduïu la divisa esperada. -Comment[en_GB]=You can get the list of supported currencies from here.
Then, enter the expected currency. -Comment[es]=Puede obtener la lista de las monedas permitidas de aquí.
A continuación, introduzca la moneda esperada. -Comment[fr]=Vous pouvez récupérer la liste des cotation disponibles depuis ici.
Ensuite, saisissez la devise souhaitée. -Comment[gl]=Pode obter a lista de divisas compatíbeis de aquí.
A continuación escriba a divisa esperada. -Comment[it]=Puoi ottenere un elenco delle valute supportate qui.
Digita, quindi, la valuta desiderata. -Comment[nl]=U kunt de lijst met ondersteunde valuta ophalen vanaf hier.
Voer dan de naam van de gewenste valuta in. -Comment[pl]=Możesz pobrać wykaz dostępnych walut stąd.
Następnie podaj żądaną walutę. -Comment[pt]=Poderá obter a lista de moedas suportadas a partir de aqui.
Depois, introduza a moeda esperada. -Comment[sv]=Lista över valutor som stöds kan hämtas här. Skriv därefter in förväntad valuta. -Comment[uk]=Ознайомитися зі списком підтримуваних валют можна тут.
Далі, введіть потрібну валюту. -Comment[x-test]=xxYou can get the list of supported currencies from here.
Then, enter the expected currency.xx +Comment=You can get the list of supported currencies from here.
Then, enter the expected currency. +Comment[ca]=Podeu obtenir la llista de les divises admeses aquí.
Després introduïu la divisa esperada. +Comment[ca@valencia]=Podeu obtindre la llista de les divises admeses ací.
Després introduïu la divisa esperada. +Comment[en_GB]=You can get the list of supported currencies from here.
Then, enter the expected currency. +Comment[es]=Puede obtener la lista de las monedas permitidas de aquí.
A continuación, introduzca la moneda esperada. +Comment[fr]=Vous pouvez récupérer la liste des cotation disponibles depuis ici.
Ensuite, saisissez la devise souhaitée. +Comment[gl]=Pode obter a lista de divisas compatíbeis de aquí.
A continuación escriba a divisa esperada. +Comment[it]=Puoi ottenere un elenco delle valute supportate qui.
Digita, quindi, la valuta desiderata. +Comment[nl]=U kunt de lijst met ondersteunde valuta ophalen vanaf hier.
Voer dan de naam van de gewenste valuta in. +Comment[pl]=Możesz pobrać wykaz dostępnych walut stąd.
Następnie podaj żądaną walutę. +Comment[pt]=Poderá obter a lista de moedas suportadas a partir de aqui.
Depois, introduza a moeda esperada. +Comment[sv]=Lista över valutor som stöds kan hämtas här. Skriv därefter in förväntad valuta. +Comment[uk]=Ознайомитися зі списком підтримуваних валют можна тут.
Далі, введіть потрібну валюту. +Comment[x-test]=xxYou can get the list of supported currencies from here.
Then, enter the expected currency.xx Encoding=UTF-8 Icon=skrooge Type=Service X-KDE-ServiceTypes=skrooge/source X-Krunner-ID=grandtrunk X-KDE-PluginInfo-Author=Stephane MANKOWSKI,miraks X-KDE-PluginInfo-Email=stephane@mankowski.fr X-KDE-PluginInfo-Name=GrandTrunk X-KDE-PluginInfo-Version=1.0 X-KDE-PluginInfo-Website=https://skrooge.org/ X-KDE-PluginInfo-Category=Plugins X-KDE-PluginInfo-Depends= X-KDE-PluginInfo-License=GPL X-KDE-PluginInfo-EnabledByDefault=true #The url or the command line to get the list of accounts in the standard output, something like this: #%1 will be replaced by the internet code of the unit #%2 will be replaced by the current day in format yyyy-MM-dd #%3 will be replaced by the previous date in format yyyy-MM-dd #Example: # X-SKROOGE-url=https://server/?s=%1 # or # X-SKROOGE-script=mydownloadscript %1 #This parameter is MANDATORY X-SKROOGE-url=http://currencies.apps.grandtrunk.net/getrange/%3/%2/%1 #The mode (HTML or CSV or CSVR). In HTML mode, only one value will be extracted from downloaded page. In CSV mode, a value per line will be extracted. CSVR means CSV in reverse mode X-SKROOGE-mode=CSVR #Regular expression to capture the price of the quote #This parameter is not MANDATORY. X-SKROOGE-price=^[^ ]* (.*)$ #Regular expression to capture the date of the quote #This parameter is not MANDATORY. X-SKROOGE-date=^(.*) [^ ]*$ #The date format X-SKROOGE-dateformat=yyyy-MM-dd diff --git a/tests/skgbankmodelertest/skgtestbudget.cpp b/tests/skgbankmodelertest/skgtestbudget.cpp index 6eb789f0a..eaade6237 100644 --- a/tests/skgbankmodelertest/skgtestbudget.cpp +++ b/tests/skgbankmodelertest/skgtestbudget.cpp @@ -1,159 +1,159 @@ /*************************************************************************** * 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) // ============================================================================ { // Import SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.load()"), document1.load(SKGTest::getTestPath(QStringLiteral("IN")) % "skgtestbudget/budget.skg"), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("BUDGET_CREATION"), err) SKGTESTERROR(QStringLiteral("BUDGET.createAutomaticBudget"), SKGBudgetObject::createAutomaticBudget(&document1, 2010, 2010, true, true), true) SKGTESTERROR(QStringLiteral("BUDGET.balanceBudget"), SKGBudgetObject::balanceBudget(&document1, 2010), true) } { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("BUDGETRULE_CREATION"), err) SKGBudgetRuleObject br(&document1); SKGTESTERROR(QStringLiteral("BUDGETRULE.enableYearCondition"), br.enableYearCondition(true), true) SKGTESTBOOL("BUDGETRULE.isYearConditionEnabled", br.isYearConditionEnabled(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableYearCondition"), br.enableYearCondition(false), true) SKGTESTBOOL("BUDGETRULE.isYearConditionEnabled", br.isYearConditionEnabled(), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.setBudgetYear"), br.setBudgetYear(2010), true) SKGTEST(QStringLiteral("BUDGETRULE.getBudgetYear"), br.getBudgetYear(), 2010) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableMonthCondition"), br.enableMonthCondition(true), true) SKGTESTBOOL("BUDGETRULE.isMonthConditionEnabled", br.isMonthConditionEnabled(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableMonthCondition"), br.enableMonthCondition(false), true) SKGTESTBOOL("BUDGETRULE.isMonthConditionEnabled", br.isMonthConditionEnabled(), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.setBudgetMonth"), br.setBudgetMonth(10), true) SKGTEST(QStringLiteral("BUDGETRULE.getBudgetMonth"), br.getBudgetMonth(), 10) SKGTESTERROR(QStringLiteral("BUDGETRULE.setOrder"), br.setOrder(1.0), true) SKGTEST(QStringLiteral("BUDGETRULE.getOrder"), br.getOrder(), 1.0) SKGTESTERROR(QStringLiteral("BUDGETRULE.setOrder"), br.setOrder(-1), true) SKGTEST(QStringLiteral("BUDGETRULE.getOrder"), br.getOrder(), 1.0) SKGTESTERROR(QStringLiteral("BUDGETRULE.enableCategoryCondition"), br.enableCategoryCondition(true), true) SKGTESTBOOL("BUDGETRULE.isCategoryConditionEnabled", br.isCategoryConditionEnabled(), true) SKGCategoryObject cat; SKGTESTERROR(QStringLiteral("BUDGETRULE.enableCategoryChange"), br.enableCategoryChange(br.isCategoryChangeEnabled()), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.getBudgetCategory"), br.getBudgetCategory(cat), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.createPathCategory"), SKGCategoryObject::createPathCategory(&document1, QStringLiteral("category_55 > category_57"), cat), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.removeBudgetCategory"), br.removeBudgetCategory(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.setBudgetCategory"), br.setBudgetCategory(cat), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.setCondition"), br.setCondition(SKGBudgetRuleObject::NEGATIVE), true) SKGTEST(QStringLiteral("BUDGETRULE.getCondition"), static_cast(br.getCondition()), static_cast(SKGBudgetRuleObject::NEGATIVE)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setCondition"), br.setCondition(SKGBudgetRuleObject::POSITIVE), true) SKGTEST(QStringLiteral("BUDGETRULE.getCondition"), static_cast(br.getCondition()), static_cast(SKGBudgetRuleObject::POSITIVE)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setCondition"), br.setCondition(SKGBudgetRuleObject::ALL), true) SKGTEST(QStringLiteral("BUDGETRULE.getCondition"), static_cast(br.getCondition()), static_cast(SKGBudgetRuleObject::ALL)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setQuantity"), br.setQuantity(100, false), true) SKGTEST(QStringLiteral("BUDGETRULE.getQuantity"), br.getQuantity(), 100) SKGTESTBOOL("BUDGETRULE.isAbolute", br.isAbolute(), false) SKGTESTERROR(QStringLiteral("BUDGETRULE.setTransfer"), br.setTransfer(SKGBudgetRuleObject::CURRENT), true) SKGTEST(QStringLiteral("BUDGETRULE.getTransferMode"), static_cast(br.getTransferMode()), static_cast(SKGBudgetRuleObject::CURRENT)) SKGTESTERROR(QStringLiteral("BUDGETRULE.setTransfer"), br.setTransfer(SKGBudgetRuleObject::NEXT), true) SKGTEST(QStringLiteral("BUDGETRULE.getTransferMode"), static_cast(br.getTransferMode()), static_cast(SKGBudgetRuleObject::NEXT)) SKGTESTERROR(QStringLiteral("BUDGETRULE.save"), br.save(), true) SKGBudgetRuleObject br2 = br; SKGBudgetRuleObject br3(br); SKGBudgetRuleObject br4(static_cast(br)); SKGBudgetRuleObject br5(SKGObjectBase(&document1, QStringLiteral("xxx"), br.getID())); SKGTESTERROR(QStringLiteral("BUDGETRULE.processAllRules"), SKGBudgetRuleObject::processAllRules(&document1), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.setTransfer"), br.setTransfer(SKGBudgetRuleObject::YEAR), true) SKGTEST(QStringLiteral("BUDGETRULE.getTransferMode"), static_cast(br.getTransferMode()), static_cast(SKGBudgetRuleObject::YEAR)) SKGTESTERROR(QStringLiteral("BUDGETRULE.save"), br.save(), true) SKGTESTERROR(QStringLiteral("BUDGETRULE.processAllRules"), SKGBudgetRuleObject::processAllRules(&document1), true) } SKGTESTERROR(QStringLiteral("document1.saveAs()"), document1.saveAs(SKGTest::getTestPath(QStringLiteral("OUT")) % "skgtestbudget/budget.skg", true), true) } // ============================================================================ { // Import SKGDocumentBank document1; SKGTESTERROR(QStringLiteral("document1.load()"), document1.load(SKGTest::getTestPath(QStringLiteral("IN")) % "skgtestbudget/320323.skg"), true) SKGError err; { // Scope of the transaction SKGBEGINTRANSACTION(document1, QStringLiteral("BUDGET_PROCESS"), err) SKGTESTERROR(QStringLiteral("BUDGETRULE.processAllRules"), SKGBudgetRuleObject::processAllRules(&document1), true) } document1.dump(DUMPBUDGET); bool check = false; document1.existObjects(QStringLiteral("v_budget_display"), QStringLiteral("t_PERIOD='2013-02' AND t_CATEGORY='Alimentation' AND f_budgeted_modified=300"), check); SKGTESTBOOL("BUDGETRULE.Alimentation 2013-02 300", check, true) document1.existObjects(QStringLiteral("v_budget_display"), QStringLiteral("t_PERIOD='2013-02' AND t_CATEGORY='Loisirs' AND f_budgeted_modified=2100"), check); SKGTESTBOOL("BUDGETRULE.Loisirs 2013-02 2100", check, true) SKGObjectBase bo; SKGTESTERROR(QStringLiteral("document1.getObject()"), document1.getObject(QStringLiteral("v_budget_display"), QStringLiteral("t_PERIOD='2013-02' AND t_CATEGORY='Loisirs' AND f_budgeted_modified=2100"), bo), true) SKGBudgetObject b(bo); SKGTEST(QStringLiteral("BUDGET.getBudgetedAmount"), b.getBudgetedAmount(), 300) SKGCategoryObject cat; SKGTESTERROR(QStringLiteral("BUDGET.getCategory"), b.getCategory(cat), true) - SKGTEST(QStringLiteral("BUDGET.getModificationReasons"), b.getModificationReasons(), QStringLiteral("Transfer of -1800 from ' 2013-01 2000.0' to 'Loisirs 2013-02 300.0' due to the rule 'All 100.0% Next Loisirs'")) + SKGTEST(QStringLiteral("BUDGET.getModificationReasons"), b.getModificationReasons().remove(","), QStringLiteral("Transfer of -1800 from ' 2013-01 2000.0' to 'Loisirs 2013-02 300.0' due to the rule 'All 100.0% Next Loisirs'")) SKGTESTERROR(QStringLiteral("BUDGET.removeCategory"), b.removeCategory(), true) SKGTESTERROR(QStringLiteral("BUDGET.enableSubCategoriesInclusion"), b.enableSubCategoriesInclusion(true), true) SKGTESTBOOL(QStringLiteral("BUDGET.isSubCategoriesInclusionEnabled"), b.isSubCategoriesInclusionEnabled(), true) SKGTESTERROR(QStringLiteral("BUDGET.enableSubCategoriesInclusion"), b.enableSubCategoriesInclusion(false), true) SKGTESTBOOL(QStringLiteral("BUDGET.isSubCategoriesInclusionEnabled"), b.isSubCategoriesInclusionEnabled(), false) SKGBudgetObject bu; } // End test SKGENDTEST() }