File indexing completed on 2024-05-26 05:10:40
0001 0002 /*************************************************************************** 0003 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr 0004 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr 0005 * SPDX-License-Identifier: GPL-3.0-or-later 0006 ***************************************************************************/ 0007 /** @file 0008 * This file is Skrooge plugin for transaction management. 0009 * 0010 * @author Stephane MANKOWSKI / Guillaume DE BURE 0011 */ 0012 #include "skgoperationplugin.h" 0013 0014 #include <kaboutdata.h> 0015 #include <kactioncollection.h> 0016 #include <kpluginfactory.h> 0017 #include <kstandardaction.h> 0018 #include <ktoolbarpopupaction.h> 0019 0020 #include <qdom.h> 0021 #include <qstandardpaths.h> 0022 #include <qthread.h> 0023 0024 #include "skgaccountobject.h" 0025 #include "skgbudgetobject.h" 0026 #include "skgcategoryobject.h" 0027 #include "skgdocumentbank.h" 0028 #include "skghtmlboardwidget.h" 0029 #include "skgmainpanel.h" 0030 #include "skgoperation_settings.h" 0031 #include "skgoperationboardwidgetqml.h" 0032 #include "skgoperationobject.h" 0033 #include "skgoperationpluginwidget.h" 0034 #include "skgpayeeobject.h" 0035 #include "skgrecurrentoperationobject.h" 0036 #include "skgruleobject.h" 0037 #include "skgsuboperationobject.h" 0038 #include "skgtableview.h" 0039 #include "skgtraces.h" 0040 #include "skgtrackerobject.h" 0041 #include "skgtransactionmng.h" 0042 0043 /** 0044 * This plugin factory. 0045 */ 0046 K_PLUGIN_CLASS_WITH_JSON(SKGOperationPlugin, "metadata.json") 0047 0048 SKGOperationPlugin::SKGOperationPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/) : 0049 SKGInterfacePlugin(iParent), 0050 m_applyTemplateMenu(nullptr), m_openOperationsWithMenu(nullptr), m_openSubOperationsWithMenu(nullptr), m_currentBankDocument(nullptr) 0051 { 0052 Q_UNUSED(iWidget) 0053 SKGTRACEINFUNC(10) 0054 } 0055 0056 SKGOperationPlugin::~SKGOperationPlugin() 0057 { 0058 SKGTRACEINFUNC(10) 0059 m_currentBankDocument = nullptr; 0060 m_applyTemplateMenu = nullptr; 0061 m_openOperationsWithMenu = nullptr; 0062 m_openSubOperationsWithMenu = nullptr; 0063 } 0064 0065 bool SKGOperationPlugin::setupActions(SKGDocument* iDocument) 0066 { 0067 SKGTRACEINFUNC(10) 0068 0069 m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument); 0070 if (m_currentBankDocument == nullptr) { 0071 return false; 0072 } 0073 0074 m_currentBankDocument->setComputeBalances(skgoperation_settings::computeBalances()); 0075 m_currentBankDocument->addEndOfTransactionCheck(SKGOperationPlugin::checkReconciliation); 0076 m_currentBankDocument->addEndOfTransactionCheck(SKGOperationPlugin::checkImport); 0077 0078 setComponentName(QStringLiteral("skrooge_operation"), title()); 0079 setXMLFile(QStringLiteral("skrooge_operation.rc")); 0080 0081 QStringList listOperation; 0082 listOperation << QStringLiteral("operation"); 0083 0084 // Menu 0085 // ------------ 0086 auto actDuplicateAction = new QAction(SKGServices::fromTheme(QStringLiteral("window-duplicate")), i18nc("Verb, duplicate an object", "Duplicate"), this); 0087 connect(actDuplicateAction, &QAction::triggered, this, &SKGOperationPlugin::onDuplicate); 0088 actionCollection()->setDefaultShortcut(actDuplicateAction, Qt::CTRL + Qt::Key_D); 0089 registerGlobalAction(QStringLiteral("edit_duplicate_operation"), actDuplicateAction, listOperation, 1, -1, 400); 0090 0091 // ------------ 0092 auto actCreateTemplateAction = new QAction(SKGServices::fromTheme(QStringLiteral("edit-guides")), i18nc("Verb", "Create template"), this); 0093 connect(actCreateTemplateAction, &QAction::triggered, this, &SKGOperationPlugin::onCreateTemplate); 0094 actionCollection()->setDefaultShortcut(actCreateTemplateAction, Qt::CTRL + Qt::SHIFT + Qt::Key_T); 0095 registerGlobalAction(QStringLiteral("edit_template_operation"), actCreateTemplateAction, listOperation, 1, -1, 401); 0096 0097 // ------------ 0098 auto actSwitchToMarkedAction = new QAction(SKGServices::fromTheme(QStringLiteral("dialog-ok")), i18nc("Verb, mark an object", "Mark"), this); 0099 connect(actSwitchToMarkedAction, &QAction::triggered, this, &SKGOperationPlugin::onSwitchToMarked); 0100 actionCollection()->setDefaultShortcut(actSwitchToMarkedAction, Qt::CTRL + Qt::Key_R); 0101 registerGlobalAction(QStringLiteral("edit_mark_selected_operation"), actSwitchToMarkedAction, listOperation, 1, -1, 310); 0102 0103 // ------------ 0104 auto actFastEdition = new QAction(SKGServices::fromTheme(QStringLiteral("games-solve")), i18nc("Verb", "Fast edit"), this); 0105 actFastEdition->setEnabled(false); 0106 actionCollection()->setDefaultShortcut(actFastEdition, Qt::Key_F10); 0107 registerGlobalAction(QStringLiteral("fast_edition"), actFastEdition, listOperation); 0108 0109 // ------------ 0110 QStringList overlayopen; 0111 overlayopen.push_back(QStringLiteral("quickopen")); 0112 0113 QStringList overlayrun; 0114 overlayrun.push_back(QStringLiteral("system-run")); 0115 auto actOpen = new QAction(SKGServices::fromTheme(icon(), overlayopen), i18nc("Verb", "Open transactions…"), this); 0116 connect(actOpen, &QAction::triggered, this, &SKGOperationPlugin::onOpenOperations); 0117 registerGlobalAction(QStringLiteral("open"), actOpen, QStringList() << QStringLiteral("account") << QStringLiteral("unit") << QStringLiteral("category") << QStringLiteral("refund") << QStringLiteral("payee") << QStringLiteral("budget") << QStringLiteral("recurrentoperation") << QStringLiteral("operation") << QStringLiteral("suboperation") << QStringLiteral("rule"), 0118 1, -1, 110); 0119 0120 auto actOpen2 = new KToolBarPopupAction(SKGServices::fromTheme(icon(), overlayopen), i18nc("Verb", "Open transactions with …"), this); 0121 m_openOperationsWithMenu = actOpen2->menu(); 0122 connect(m_openOperationsWithMenu, &QMenu::aboutToShow, this, &SKGOperationPlugin::onShowOpenWithMenu); 0123 actOpen2->setStickyMenu(false); 0124 actOpen2->setDelayed(false); 0125 registerGlobalAction(QStringLiteral("open_operations_with"), actOpen2, QStringList() << QStringLiteral("operation") << QStringLiteral("suboperation"), 1, 1, 111); 0126 0127 auto actOpen3 = new KToolBarPopupAction(SKGServices::fromTheme(icon(), overlayopen), i18nc("Verb", "Open sub transactions with …"), this); 0128 m_openSubOperationsWithMenu = actOpen3->menu(); 0129 connect(m_openSubOperationsWithMenu, &QMenu::aboutToShow, this, &SKGOperationPlugin::onShowOpenWithMenu); 0130 actOpen3->setStickyMenu(false); 0131 actOpen3->setDelayed(false); 0132 registerGlobalAction(QStringLiteral("open_suboperations_with"), actOpen3, QStringList() << QStringLiteral("operation") << QStringLiteral("suboperation"), 1, 1, 112); 0133 0134 auto actOpenHighLights = new QAction(SKGServices::fromTheme(QStringLiteral("bookmarks"), overlayopen), i18nc("Verb", "Open highlights…"), this); 0135 actOpenHighLights->setData(QString("skg://skrooge_operation_plugin/?title_icon=bookmarks&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Highlighted transactions")) % 0136 "&operationWhereClause=t_bookmarked='Y'")); 0137 connect(actOpenHighLights, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0138 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0139 }); 0140 actionCollection()->setDefaultShortcut(actOpenHighLights, Qt::CTRL + Qt::META + Qt::Key_H); 0141 registerGlobalAction(QStringLiteral("view_open_highlight"), actOpenHighLights); 0142 0143 // ------------ 0144 auto actOpenLastModified = new QAction(SKGServices::fromTheme(QStringLiteral("view-refresh"), overlayopen), i18nc("Verb", "Open last modified…"), this); 0145 actOpenLastModified->setData(QString("skg://skrooge_operation_plugin/?title_icon=view-refresh&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions modified or created during last action")) % 0146 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("id in (SELECT i_object_id FROM doctransactionitem di, doctransaction dt WHERE dt.t_mode='U' AND +di.rd_doctransaction_id=dt.id AND di.t_object_table='operation'AND NOT EXISTS(select 1 from doctransaction B where B.i_parent=dt.id))")))); 0147 connect(actOpenLastModified, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0148 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0149 }); 0150 actionCollection()->setDefaultShortcut(actOpenLastModified, Qt::META + Qt::Key_L); 0151 registerGlobalAction(QStringLiteral("view_open_last_modified"), actOpenLastModified); 0152 0153 // ------------ 0154 auto actOpenModifiedByTransaction = new QAction(SKGServices::fromTheme(QStringLiteral("view-refresh"), overlayopen), i18nc("Verb", "Open transactions modified by this transaction…"), this); 0155 connect(actOpenModifiedByTransaction, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0156 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0157 if (!selection.isEmpty()) { 0158 SKGObjectBase obj = selection[0]; 0159 QString name = obj.getAttribute(QStringLiteral("t_name")); 0160 QString url = QString("skg://skrooge_operation_plugin/?title_icon=view-refresh&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions modified or created during the action '%1'", name)) % "&operationWhereClause=" % SKGServices::encodeForUrl("id in (SELECT i_object_id FROM doctransactionitem WHERE rd_doctransaction_id=" % SKGServices::intToString(obj.getID()) % " AND t_object_table='operation')")); 0161 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(url); 0162 } 0163 }); 0164 registerGlobalAction(QStringLiteral("view_open_modified_by_transaction"), actOpenModifiedByTransaction, QStringList() << QStringLiteral("doctransaction"), 1, 1, 100); 0165 0166 // ------------ 0167 auto actOpenSuboperations = new QAction(SKGServices::fromTheme(QStringLiteral("split"), overlayopen), i18nc("Verb", "Open sub transactions…"), this); 0168 actOpenSuboperations->setData(QString("skg://skrooge_operation_plugin/SKGOPERATION_CONSOLIDATED_DEFAULT_PARAMETERS/?title_icon=split&operationTable=v_suboperation_consolidated&operationWhereClause=&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Sub transactions")) % "#" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Sub transactions")))); 0169 connect(actOpenSuboperations, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0170 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0171 }); 0172 actionCollection()->setDefaultShortcut(actOpenSuboperations, Qt::META + Qt::Key_S); 0173 registerGlobalAction(QStringLiteral("view_open_suboperations"), actOpenSuboperations); 0174 0175 // ------------ 0176 auto actOpenDuplicate = new QAction(SKGServices::fromTheme(QStringLiteral("window-duplicate"), overlayopen), i18nc("Verb", "Open potential duplicates…"), this); 0177 actOpenDuplicate->setData(QString("skg://skrooge_operation_plugin/?title_icon=window-duplicate&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions potentially duplicated")) % 0178 "&operationWhereClause=" % SKGServices::encodeForUrl("id in (SELECT o1.id FROM v_operation o1 WHERE EXISTS (SELECT 1 FROM v_operation o2 WHERE o1.id<>o2.id AND o1.t_template='N' AND o2.t_template='N' AND o1.d_date=o2.d_date AND ABS(o1.f_CURRENTAMOUNT-o2.f_CURRENTAMOUNT)<" % SKGServices::doubleToString(EPSILON) % " AND o1.rd_account_id=o2.rd_account_id AND o1.rc_unit_id=o2.rc_unit_id AND (o1.t_status<>'Y' OR o2.t_status<>'Y')))"))); 0179 connect(actOpenDuplicate, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0180 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0181 }); 0182 actionCollection()->setDefaultShortcut(actOpenDuplicate, Qt::META + Qt::Key_D); 0183 registerGlobalAction(QStringLiteral("view_open_duplicates"), actOpenDuplicate); 0184 0185 // ------------ 0186 auto actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlayopen), i18nc("Verb", "Open transfers without payee…"), this); 0187 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=user-group-properties&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transfers without payee")) % 0188 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("t_TRANSFER='Y' AND r_payee_id=0")))); 0189 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0190 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0191 }); 0192 registerGlobalAction(QStringLiteral("view_open_transfers_without_payee"), actTmp); 0193 0194 // ------------ 0195 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("user-group-properties"), overlayopen), i18nc("Verb", "Open transactions without payee…"), this); 0196 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=user-group-properties&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions without payee")) % 0197 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("t_TRANSFER='N' AND r_payee_id=0")))); 0198 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0199 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0200 }); 0201 registerGlobalAction(QStringLiteral("view_open_operation_without_payee"), actTmp); 0202 0203 // ------------ 0204 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlayopen), i18nc("Verb", "Open transfers without category…"), this); 0205 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=view-categories&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transfers without category")) % 0206 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("t_TRANSFER='Y' AND EXISTS (SELECT 1 FROM suboperation WHERE rd_operation_id=v_operation_display.id AND r_category_id=0)")))); 0207 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0208 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0209 }); 0210 registerGlobalAction(QStringLiteral("view_open_transfers_without_category"), actTmp); 0211 0212 // ------------ 0213 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("view-categories"), overlayopen), i18nc("Verb", "Open transactions without category…"), this); 0214 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=view-categories&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions without category")) % 0215 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("t_TRANSFER='N' AND EXISTS (SELECT 1 FROM suboperation WHERE rd_operation_id=v_operation_display.id AND r_category_id=0)")))); 0216 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0217 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0218 }); 0219 registerGlobalAction(QStringLiteral("view_open_operation_without_category"), actTmp); 0220 0221 // ------------ 0222 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("skrooge_credit_card"), overlayopen), i18nc("Verb", "Open transactions without mode…"), this); 0223 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=skrooge_credit_card&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions without mode")) % 0224 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("t_template='N' AND t_mode='' AND d_date<>'0000-00-00'")))); 0225 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0226 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0227 }); 0228 registerGlobalAction(QStringLiteral("view_open_operation_without_mode"), actTmp); 0229 0230 // ------------ 0231 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("draw-freehand"), overlayopen), i18nc("Verb", "Open transactions with comments not aligned…"), this); 0232 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=draw-freehand&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions with comments not aligned")) % 0233 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("id IN (SELECT op.id FROM operation op, suboperation so WHERE so.rd_operation_id=op.id AND so.t_comment<>op.t_comment AND (SELECT COUNT(1) FROM suboperation so2 WHERE so2.rd_operation_id=op.id)=1)")))); 0234 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0235 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0236 }); 0237 registerGlobalAction(QStringLiteral("view_open_operation_with_comment_not_aligned"), actTmp); 0238 0239 // ------------ 0240 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("view-pim-calendar"), overlayopen), i18nc("Verb", "Open transactions with dates not aligned…"), this); 0241 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=draw-freehand&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions with dates not aligned")) % 0242 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("id IN (SELECT op.id FROM operation op, suboperation so WHERE so.rd_operation_id=op.id AND (so.d_date<op.d_date OR (so.d_date<>op.d_date AND (SELECT COUNT(1) FROM suboperation so2 WHERE so2.rd_operation_id=op.id)=1)))")))); 0243 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0244 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0245 }); 0246 registerGlobalAction(QStringLiteral("view_open_operation_with_date_not_aligned"), actTmp); 0247 0248 // ------------ 0249 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlayopen), i18nc("Verb", "Open transactions without payees category…"), this); 0250 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=view-categories&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Sub-transactions without payees category")) % 0251 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("id IN (SELECT v_suboperation_consolidated.i_OPID FROM v_suboperation_consolidated INNER JOIN payee ON v_suboperation_consolidated.r_payee_id=payee.id WHERE payee.r_category_id!=0 AND v_suboperation_consolidated.r_category_id!=payee.r_category_id AND i_NBSUBOPERATIONS=1)")))); 0252 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0253 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0254 }); 0255 registerGlobalAction(QStringLiteral("view_open_suboperations_without_payee_category"), actTmp); 0256 0257 // ------------ 0258 auto actAlignComment = new QAction(SKGServices::fromTheme(QStringLiteral("draw-freehand"), overlayrun), i18nc("Verb", "Align comment of subtransactions of all single transactions"), this); 0259 connect(actAlignComment, &QAction::triggered, this, &SKGOperationPlugin::onAlignComment); 0260 registerGlobalAction(QStringLiteral("align_comment"), actAlignComment); 0261 0262 auto actAlignComment2 = new QAction(SKGServices::fromTheme(QStringLiteral("draw-freehand"), overlayrun), i18nc("Verb", "Align comment of transactions of all single transactions"), this); 0263 connect(actAlignComment2, &QAction::triggered, this, &SKGOperationPlugin::onAlignComment2); 0264 registerGlobalAction(QStringLiteral("align_comment2"), actAlignComment2); 0265 0266 // ------------ 0267 auto actAlignDate = new QAction(SKGServices::fromTheme(QStringLiteral("view-pim-calendar"), overlayrun), i18nc("Verb", "Align date of subtransactions of all transactions"), this); 0268 connect(actAlignDate, &QAction::triggered, this, &SKGOperationPlugin::onAlignDate); 0269 registerGlobalAction(QStringLiteral("align_date"), actAlignDate); 0270 0271 auto actAlignCategory = new QAction(SKGServices::fromTheme(QStringLiteral("view-pim-calendar"), overlayrun), i18nc("Verb", "Align the category of all single transactions with the category of their payee"), this); 0272 connect(actAlignCategory, &QAction::triggered, this, &SKGOperationPlugin::onAlignWithCategoryOfPayee); 0273 registerGlobalAction(QStringLiteral("align_category"), actAlignCategory); 0274 0275 0276 // ------------ 0277 actTmp = new QAction(SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlayopen), i18nc("Verb", "Open transactions in groups with only one transaction…"), this); 0278 actTmp->setData(QString("skg://skrooge_operation_plugin/?title_icon=exchange-positions&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions in groups with only one transaction")) % 0279 "&operationWhereClause=" % SKGServices::encodeForUrl(QStringLiteral("v_operation_display.i_group_id<>0 AND (SELECT COUNT(1) FROM operation o WHERE o.i_group_id=v_operation_display.i_group_id)<2")))); 0280 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() { 0281 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage(); 0282 }); 0283 registerGlobalAction(QStringLiteral("view_open_operation_in_group_of_one"), actTmp); 0284 0285 // ------------ 0286 auto actCleanRemoveGroupWithOneOperation = new QAction(SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlayrun), i18nc("Verb", "Remove groups with only one transaction of all transactions"), this); 0287 connect(actCleanRemoveGroupWithOneOperation, &QAction::triggered, this, &SKGOperationPlugin::onRemoveGroupWithOneOperation); 0288 registerGlobalAction(QStringLiteral("clean_remove_group_with_one_operation"), actCleanRemoveGroupWithOneOperation); 0289 0290 // ------------ 0291 auto actGroupOperation = new QAction(SKGServices::fromTheme(QStringLiteral("exchange-positions")), i18nc("Verb", "Group transactions"), this); 0292 connect(actGroupOperation, &QAction::triggered, this, &SKGOperationPlugin::onGroupOperation); 0293 actionCollection()->setDefaultShortcut(actGroupOperation, Qt::CTRL + Qt::Key_G); 0294 registerGlobalAction(QStringLiteral("edit_group_operation"), actGroupOperation, listOperation, 2, -1, 311); 0295 0296 // ------------ 0297 QStringList overlay; 0298 overlay.push_back(QStringLiteral("edit-delete")); 0299 auto actUngroupOperation = new QAction(SKGServices::fromTheme(QStringLiteral("exchange-positions"), overlay), i18nc("Verb", "Ungroup transactions"), this); 0300 connect(actUngroupOperation, &QAction::triggered, this, &SKGOperationPlugin::onUngroupOperation); 0301 actionCollection()->setDefaultShortcut(actUngroupOperation, Qt::CTRL + Qt::SHIFT + Qt::Key_G); 0302 registerGlobalAction(QStringLiteral("edit_ungroup_operation"), actUngroupOperation, listOperation, 1, -1, 312); 0303 0304 // ------------ 0305 auto actMergeOperationAction = new QAction(SKGServices::fromTheme(QStringLiteral("split")), i18nc("Verb, action to merge", "Merge sub transactions"), this); 0306 connect(actMergeOperationAction, &QAction::triggered, this, &SKGOperationPlugin::onMergeSubOperations); 0307 actionCollection()->setDefaultShortcut(actMergeOperationAction, Qt::CTRL + Qt::SHIFT + Qt::Key_M); 0308 registerGlobalAction(QStringLiteral("merge_sub_operations"), actMergeOperationAction, listOperation, 1, -1, 320); 0309 0310 auto actApplyTemplateAction = new KToolBarPopupAction(SKGServices::fromTheme(QStringLiteral("edit-guides")), i18nc("Verb, action to apply a template", "Apply template"), this); 0311 m_applyTemplateMenu = actApplyTemplateAction->menu(); 0312 connect(m_applyTemplateMenu, &QMenu::aboutToShow, this, &SKGOperationPlugin::onShowApplyTemplateMenu); 0313 actApplyTemplateAction->setStickyMenu(false); 0314 actApplyTemplateAction->setData(1); 0315 registerGlobalAction(QStringLiteral("edit_apply_template"), actApplyTemplateAction, listOperation, 1, -1, 402); 0316 0317 return true; 0318 } 0319 0320 void SKGOperationPlugin::onShowApplyTemplateMenu() 0321 { 0322 if ((m_applyTemplateMenu != nullptr) && (m_currentBankDocument != nullptr)) { 0323 // Clean Menu 0324 QMenu* m = m_applyTemplateMenu; 0325 m->clear(); 0326 0327 // Search templates 0328 SKGStringListList listTmp; 0329 m_currentBankDocument->executeSelectSqliteOrder( 0330 QStringLiteral("SELECT t_displayname, id, t_bookmarked FROM v_operation_displayname WHERE t_template='Y' ORDER BY t_bookmarked DESC, t_PAYEE ASC"), 0331 listTmp); 0332 0333 // Build menus 0334 int count = 0; 0335 bool fav = true; 0336 int nb = listTmp.count(); 0337 for (int i = 1; i < nb; ++i) { 0338 // Add more sub menu 0339 if (count == 8) { 0340 m = m->addMenu(i18nc("More items in a menu", "More")); 0341 count = 0; 0342 } 0343 count++; 0344 0345 // Add separator for bookmarked templates 0346 if (fav && listTmp.at(i).at(2) == QStringLiteral("N") && i > 1) { 0347 m->addSeparator(); 0348 } 0349 fav = (listTmp.at(i).at(2) == QStringLiteral("Y")); 0350 0351 // Add actions 0352 QAction* act = m->addAction(SKGServices::fromTheme(QStringLiteral("edit-guides")), listTmp.at(i).at(0)); 0353 if (act != nullptr) { 0354 act->setData(listTmp.at(i).at(1)); 0355 connect(act, &QAction::triggered, this, &SKGOperationPlugin::onApplyTemplate); 0356 } 0357 } 0358 } 0359 } 0360 0361 void SKGOperationPlugin::refresh() 0362 { 0363 SKGTRACEINFUNC(10) 0364 if ((m_currentBankDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr)) { 0365 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0366 int nb = selection.count(); 0367 bool onOperation = (nb > 0 && selection.at(0).getRealTable() == QStringLiteral("operation")); 0368 0369 QAction* act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("align_date")); 0370 act->setText(onOperation ? i18nc("Verb", "Align date of subtransactions of selected single transactions") : i18nc("Verb", "Align date of subtransactions of all single transactions")); 0371 act->setData(onOperation); 0372 0373 act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("align_comment2")); 0374 act->setText(onOperation ? i18nc("Verb", "Align comment of transactions of selected single transactions") : i18nc("Verb", "Align comment of transactions of all single transactions")); 0375 act->setData(onOperation); 0376 0377 act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("align_category")); 0378 act->setText(onOperation ? i18nc("Verb", "Align the category of all selected single transactions with the category of their payee") : i18nc("Verb", "Align the category of all single transactions with the category of their payee")); 0379 act->setData(onOperation); 0380 0381 act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("align_comment")); 0382 act->setText(onOperation ? i18nc("Verb", "Align comment of subtransactions of selected single transactions") : i18nc("Verb", "Align comment of subtransactions of all single transactions")); 0383 act->setData(onOperation); 0384 0385 act = SKGMainPanel::getMainPanel()->getGlobalAction(QStringLiteral("clean_remove_group_with_one_operation")); 0386 act->setText(onOperation ? i18nc("Verb", "Remove groups with only one transaction of selected transactions") : i18nc("Verb", "Remove groups with only one transaction of all transactions")); 0387 act->setData(onOperation); 0388 } 0389 } 0390 0391 SKGError SKGOperationPlugin::checkReconciliation(SKGDocument* iDocument) 0392 { 0393 SKGError err; 0394 SKGTRACEINFUNCRC(5, err) 0395 if ((iDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr) && skgoperation_settings::broken_reconciliation() > QStringLiteral("0")) { 0396 // Check the reconciliation for all opened account 0397 SKGObjectBase::SKGListSKGObjectBase accounts; 0398 iDocument->getObjects(QStringLiteral("v_account"), QStringLiteral("t_close='N' AND f_reconciliationbalance!='' AND (SELECT COUNT(1) FROM (SELECT DISTINCT(operation.rc_unit_id) FROM operation WHERE operation.rd_account_id=v_account.id GROUP BY operation.rc_unit_id))=1"), accounts); 0399 for (const auto& account : qAsConst(accounts)) { 0400 SKGAccountObject a(account); 0401 auto soluces = a.getPossibleReconciliations(SKGServices::stringToDouble(account.getAttribute(QStringLiteral("f_reconciliationbalance"))), false); 0402 if (soluces.isEmpty()) { 0403 if (skgoperation_settings::broken_reconciliation() == QStringLiteral("1")) { 0404 iDocument->sendMessage(i18nc("Warning message", "The previous reconciliation of '%1' has been broken by this action or a previous one.", a.getDisplayName()), SKGDocument::Warning, QStringLiteral("skg://edit_undo")); 0405 } else { 0406 auto msg = i18nc("Warning message", "This action would break the previous reconciliation of '%1', so it is cancelled.", a.getDisplayName()); 0407 iDocument->sendMessage(msg, SKGDocument::Error); 0408 return err = SKGError(ERR_ABORT, msg); 0409 } 0410 } 0411 } 0412 } 0413 return err; 0414 } 0415 0416 SKGError SKGOperationPlugin::checkImport(SKGDocument* iDocument) 0417 { 0418 SKGError err; 0419 SKGTRACEINFUNCRC(5, err) 0420 if ((iDocument != nullptr) && (SKGMainPanel::getMainPanel() != nullptr) && skgoperation_settings::broken_import() > QStringLiteral("0")) { 0421 // Check the reconciliation for all opened account 0422 SKGObjectBase::SKGListSKGObjectBase accounts; 0423 iDocument->getObjects(QStringLiteral("v_account"), QStringLiteral("t_close='N' AND f_importbalance!=''"), accounts); 0424 for (const auto& account : qAsConst(accounts)) { 0425 SKGAccountObject a(account); 0426 auto soluces = a.getPossibleReconciliations(SKGServices::stringToDouble(account.getAttribute(QStringLiteral("f_importbalance"))), false); 0427 if (soluces.isEmpty()) { 0428 if (skgoperation_settings::broken_import() == QStringLiteral("1")) { 0429 iDocument->sendMessage(i18nc("Warning message", "The previous import in '%1' has been broken by this action or a previous one.", a.getDisplayName()), SKGDocument::Warning, QStringLiteral("skg://edit_undo")); 0430 } else { 0431 auto msg = i18nc("Warning message", "This action would break the previous import in '%1', so it is cancelled.", a.getDisplayName()); 0432 iDocument->sendMessage(msg, SKGDocument::Error); 0433 return err = SKGError(ERR_ABORT, msg); 0434 } 0435 } 0436 } 0437 } 0438 return err; 0439 } 0440 0441 int SKGOperationPlugin::getNbDashboardWidgets() 0442 { 0443 return 2; 0444 } 0445 0446 QString SKGOperationPlugin::getDashboardWidgetTitle(int iIndex) 0447 { 0448 if (iIndex == 0) { 0449 return i18nc("Noun, the title of a section", "Income && Expenditure"); 0450 } 0451 return i18nc("Noun, the title of a section", "Highlighted transactions"); 0452 } 0453 0454 SKGBoardWidget* SKGOperationPlugin::getDashboardWidget(int iIndex) 0455 { 0456 if (iIndex == 0) { 0457 return new SKGOperationBoardWidgetQml(SKGMainPanel::getMainPanel(), m_currentBankDocument); 0458 } 0459 return new SKGHtmlBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument, 0460 getDashboardWidgetTitle(iIndex), 0461 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/html/default/highlighted_operations.html")), 0462 QStringList() << QStringLiteral("operation")); 0463 } 0464 0465 SKGTabPage* SKGOperationPlugin::getWidget() 0466 { 0467 SKGTRACEINFUNC(10) 0468 return new SKGOperationPluginWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument); 0469 } 0470 0471 QWidget* SKGOperationPlugin::getPreferenceWidget() 0472 { 0473 SKGTRACEINFUNC(10) 0474 auto w = new QWidget(); 0475 ui.setupUi(w); 0476 0477 // Set labels 0478 ui.kPayeeFakeLbl->setText(i18n("%1:", m_currentBankDocument->getDisplay(QStringLiteral("t_payee")))); 0479 ui.kCategoryFakeLbl->setText(i18n("%1:", m_currentBankDocument->getDisplay(QStringLiteral("t_CATEGORY")))); 0480 ui.kCommentFakeLbl->setText(i18n("%1:", m_currentBankDocument->getDisplay(QStringLiteral("t_comment")))); 0481 0482 ui.kCategoryCommissionLbl->setText(ui.kCategoryFakeLbl->text()); 0483 ui.kCommentCommissionLbl->setText(ui.kCommentFakeLbl->text()); 0484 0485 ui.kCategoryTaxLbl->setText(ui.kCategoryFakeLbl->text()); 0486 ui.kCommentTaxLbl->setText(ui.kCommentFakeLbl->text()); 0487 0488 // Fill combo boxes and auto complession 0489 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kcfg_categoryFakeOperation << ui.kcfg_categoryCommissionOperation << ui.kcfg_categoryTaxOperation, m_currentBankDocument, QStringLiteral("category"), QStringLiteral("t_fullname"), QLatin1String("")); 0490 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kcfg_payeeFakeOperation, m_currentBankDocument, QStringLiteral("payee"), QStringLiteral("t_name"), QLatin1String("")); 0491 SKGMainPanel::fillWithDistinctValue(QList<QWidget*>() << ui.kcfg_commentFakeOperation << ui.kcfg_commentCommissionOperation << ui.kcfg_commentTaxOperation, m_currentBankDocument, QStringLiteral("v_operation_all_comment"), QStringLiteral("t_comment"), QLatin1String(""), true); 0492 0493 return w; 0494 } 0495 0496 KConfigSkeleton* SKGOperationPlugin::getPreferenceSkeleton() 0497 { 0498 return skgoperation_settings::self(); 0499 } 0500 0501 SKGError SKGOperationPlugin::savePreferences() const 0502 { 0503 m_currentBankDocument->setComputeBalances(skgoperation_settings::computeBalances()); 0504 return SKGInterfacePlugin::savePreferences(); 0505 } 0506 0507 QString SKGOperationPlugin::title() const 0508 { 0509 return i18nc("Noun", "Transactions"); 0510 } 0511 0512 QString SKGOperationPlugin::icon() const 0513 { 0514 return QStringLiteral("view-bank-account"); 0515 } 0516 0517 QString SKGOperationPlugin::toolTip() const 0518 { 0519 return i18nc("Noun", "Transaction management"); 0520 } 0521 0522 int SKGOperationPlugin::getOrder() const 0523 { 0524 return 15; 0525 } 0526 0527 QStringList SKGOperationPlugin::tips() const 0528 { 0529 QStringList output; 0530 output.push_back(i18nc("Description of a tips", "<p>… you can press +, -, CTRL + or CTRL - to quickly change dates.</p>")); 0531 output.push_back(i18nc("Description of a tips", "<p>… you can update many <a href=\"skg://skrooge_operation_plugin\">transactions</a> in one shot.</p>")); 0532 output.push_back(i18nc("Description of a tips", "<p>… you can double click on an <a href=\"skg://skrooge_operation_plugin\">operation</a> to show or edit sub transactions.</p>")); 0533 output.push_back(i18nc("Description of a tips", "<p>… you can duplicate an <a href=\"skg://skrooge_operation_plugin\">operation</a> including complex transactions (split, grouped, …).</p>")); 0534 output.push_back(i18nc("Description of a tips", "<p>… you can create template of <a href=\"skg://skrooge_operation_plugin\">transactions</a>.</p>")); 0535 output.push_back(i18nc("Description of a tips", "<p>… you can group and ungroup <a href=\"skg://skrooge_operation_plugin\">transactions</a>.</p>")); 0536 output.push_back(i18nc("Description of a tips", "<p>… you have to indicate the sign of an <a href=\"skg://skrooge_operation_plugin\">operation</a> only if you want to force it, else it will be determined automatically with the <a href=\"skg://skrooge_category_plugin\">category</a>.</p>")); 0537 return output; 0538 } 0539 0540 bool SKGOperationPlugin::isInPagesChooser() const 0541 { 0542 return true; 0543 } 0544 0545 void SKGOperationPlugin::onApplyTemplate() 0546 { 0547 SKGError err; 0548 SKGTRACEINFUNCRC(10, err) 0549 auto* act = qobject_cast<QAction*>(sender()); 0550 if (act != nullptr) { 0551 // Get template 0552 SKGOperationObject temp(m_currentBankDocument, SKGServices::stringToInt(act->data().toString())); 0553 0554 // Get Selection 0555 if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) { 0556 QStringList listUUID; 0557 0558 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0559 int nb = selection.count(); 0560 { 0561 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Apply template"), err, nb) 0562 for (int i = 0; !err && i < nb; ++i) { 0563 SKGOperationObject operationObj(selection.at(i)); 0564 0565 SKGOperationObject op; 0566 IFOKDO(err, temp.duplicate(op)) 0567 IFOKDO(err, op.mergeAttribute(operationObj, SKGOperationObject::PROPORTIONAL, false)) 0568 listUUID.push_back(op.getUniqueID()); 0569 0570 IFOKDO(err, m_currentBankDocument->stepForward(i + 1)) 0571 } 0572 } 0573 0574 // status bar 0575 IFOK(err) { 0576 err = SKGError(0, i18nc("Successful message after an user action", "Template applied.")); 0577 auto* w = qobject_cast<SKGOperationPluginWidget*>(SKGMainPanel::getMainPanel()->currentPage()); 0578 if (w != nullptr) { 0579 w->getTableView()->selectObjects(listUUID, true); 0580 } 0581 } else { 0582 err.addError(ERR_FAIL, i18nc("Error message", "Apply of template failed")); 0583 } 0584 } 0585 // Display error 0586 SKGMainPanel::displayErrorMessage(err); 0587 } 0588 } 0589 0590 void SKGOperationPlugin::onGroupOperation() 0591 { 0592 SKGError err; 0593 SKGTRACEINFUNCRC(10, err) 0594 // Get Selection 0595 if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) { 0596 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0597 int nb = selection.count(); 0598 if (nb >= 2) { 0599 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Group transactions"), err, nb) 0600 SKGOperationObject main(selection.at(0)); 0601 IFOKDO(err, m_currentBankDocument->stepForward(1)) 0602 for (int i = 1; !err && i < nb; ++i) { 0603 SKGOperationObject operationObj(selection.at(i)); 0604 IFOKDO(err, operationObj.setGroupOperation(main)) 0605 IFOKDO(err, operationObj.save()) 0606 IFOKDO(err, main.load()) 0607 0608 // Send message 0609 IFOKDO(err, m_currentBankDocument->sendMessage(i18nc("An information to the user", "The transaction '%1' has been grouped with '%2'", operationObj.getDisplayName(), main.getDisplayName()), SKGDocument::Hidden)) 0610 0611 IFOKDO(err, m_currentBankDocument->stepForward(i + 1)) 0612 } 0613 } 0614 0615 // status bar 0616 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Transactions grouped."))) 0617 else { 0618 err.addError(ERR_FAIL, i18nc("Error message", "Group creation failed")); 0619 } 0620 0621 // Display error 0622 SKGMainPanel::displayErrorMessage(err); 0623 } 0624 } 0625 0626 void SKGOperationPlugin::onUngroupOperation() 0627 { 0628 SKGError err; 0629 SKGTRACEINFUNCRC(10, err) 0630 // Get Selection 0631 if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) { 0632 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0633 int nb = selection.count(); 0634 { 0635 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Ungroup transaction"), err, nb) 0636 for (int i = 0; !err && i < nb; ++i) { 0637 SKGOperationObject operationObj(selection.at(i)); 0638 IFOKDO(err, operationObj.setGroupOperation(operationObj)) 0639 IFOKDO(err, operationObj.save()) 0640 0641 // Send message 0642 IFOKDO(err, m_currentBankDocument->sendMessage(i18nc("An information to the user", "The transaction '%1' has been ungrouped", operationObj.getDisplayName()), SKGDocument::Hidden)) 0643 0644 IFOKDO(err, m_currentBankDocument->stepForward(i + 1)) 0645 } 0646 } 0647 0648 // status bar 0649 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Transaction ungrouped."))) 0650 else { 0651 err.addError(ERR_FAIL, i18nc("Error message", "Group deletion failed")); 0652 } 0653 0654 // Display error 0655 SKGMainPanel::displayErrorMessage(err); 0656 } 0657 } 0658 0659 void SKGOperationPlugin::onSwitchToMarked() 0660 { 0661 SKGError err; 0662 SKGTRACEINFUNCRC(10, err) 0663 // Get Selection 0664 if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) { 0665 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0666 int nb = selection.count(); 0667 { 0668 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Switch to marked"), err, nb) 0669 for (int i = 0; !err && i < nb; ++i) { 0670 SKGOperationObject operationObj(selection.at(i)); 0671 IFOKDO(err, operationObj.setStatus(operationObj.getStatus() != SKGOperationObject::MARKED ? SKGOperationObject::MARKED : SKGOperationObject::NONE)) 0672 IFOKDO(err, operationObj.save()) 0673 0674 // Send message 0675 IFOKDO(err, m_currentBankDocument->sendMessage(i18nc("An information to the user", "The status of the transaction '%1' has been changed", operationObj.getDisplayName()), SKGDocument::Hidden)) 0676 0677 IFOKDO(err, m_currentBankDocument->stepForward(i + 1)) 0678 } 0679 } 0680 0681 // status bar 0682 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Transaction marked."))) 0683 else { 0684 err.addError(ERR_FAIL, i18nc("Error message", "Switch failed")); 0685 } 0686 0687 // Display error 0688 SKGMainPanel::displayErrorMessage(err); 0689 } 0690 } 0691 0692 void SKGOperationPlugin::onDuplicate() 0693 { 0694 SKGError err; 0695 SKGTRACEINFUNCRC(10, err) 0696 // Get Selection 0697 if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) { 0698 QStringList listUUID; 0699 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0700 int nb = selection.count(); 0701 { 0702 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Duplicate transaction"), err, nb) 0703 for (int i = 0; !err && i < nb; ++i) { 0704 SKGOperationObject operationObj(selection.at(i)); 0705 SKGOperationObject dup; 0706 IFOKDO(err, operationObj.duplicate(dup)) 0707 IFOKDO(err, m_currentBankDocument->stepForward(i + 1)) 0708 0709 // Send message 0710 IFOKDO(err, m_currentBankDocument->sendMessage(i18nc("An information to the user", "The duplicate transaction '%1' has been added", dup.getDisplayName()), SKGDocument::Hidden)) 0711 0712 0713 listUUID.push_back(dup.getUniqueID()); 0714 } 0715 } 0716 0717 // status bar 0718 IFOK(err) { 0719 err = SKGError(0, i18nc("Successful message after an user action", "Transaction duplicated.")); 0720 auto* w = qobject_cast<SKGOperationPluginWidget*>(SKGMainPanel::getMainPanel()->currentPage()); 0721 if (w != nullptr) { 0722 w->getTableView()->selectObjects(listUUID, true); 0723 } 0724 } else { 0725 err.addError(ERR_FAIL, i18nc("Error message", "Duplicate transaction failed")); 0726 } 0727 0728 // Display error 0729 SKGMainPanel::displayErrorMessage(err); 0730 } 0731 } 0732 0733 void SKGOperationPlugin::onCreateTemplate() 0734 { 0735 SKGError err; 0736 SKGTRACEINFUNCRC(10, err) 0737 // Get Selection 0738 if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) { 0739 QStringList listUUID; 0740 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0741 int nb = selection.count(); 0742 { 0743 SKGBEGINPROGRESSTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Create template"), err, nb) 0744 for (int i = 0; !err && i < nb; ++i) { 0745 SKGOperationObject operationObj(selection.at(i)); 0746 SKGOperationObject dup; 0747 IFOKDO(err, operationObj.duplicate(dup, QDate::currentDate(), true)) 0748 IFOKDO(err, m_currentBankDocument->stepForward(i + 1)) 0749 0750 // Send message 0751 IFOKDO(err, m_currentBankDocument->sendMessage(i18nc("An information to the user", "The template '%1' has been added", dup.getDisplayName()), SKGDocument::Hidden)) 0752 0753 listUUID.push_back(dup.getUniqueID()); 0754 } 0755 } 0756 0757 // status bar 0758 IFOK(err) { 0759 err = SKGError(0, i18nc("Successful message after an user action", "Template created.")); 0760 auto* w = qobject_cast<SKGOperationPluginWidget*>(SKGMainPanel::getMainPanel()->currentPage()); 0761 if (w != nullptr) { 0762 w->setTemplateMode(true); 0763 w->getTableView()->selectObjects(listUUID, true); 0764 } 0765 } else { 0766 err.addError(ERR_FAIL, i18nc("Error message", "Creation template failed")); 0767 } 0768 0769 // Display error 0770 SKGMainPanel::displayErrorMessage(err); 0771 } 0772 } 0773 0774 void SKGOperationPlugin::onOpenOperations() 0775 { 0776 SKGTRACEINFUNC(10) 0777 if (SKGMainPanel::getMainPanel() != nullptr) { 0778 SKGObjectBase::SKGListSKGObjectBase selection; 0779 auto* act = qobject_cast< QAction* >(sender()); 0780 if (act != nullptr) { 0781 QStringList data = SKGServices::splitCSVLine(act->data().toString(), '-'); 0782 if (data.count() == 2) { 0783 selection.push_back(SKGObjectBase(m_currentBankDocument, data[1], SKGServices::stringToInt(data[0]))); 0784 } 0785 } 0786 if (selection.isEmpty()) { 0787 selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 0788 } 0789 0790 int nb = selection.count(); 0791 if (nb > 0) { 0792 QString wc; 0793 QString titleOpen; 0794 QString iconOpen; 0795 QString table = selection.at(0).getRealTable(); 0796 QString view; 0797 QString account; 0798 if (table == QStringLiteral("account")) { 0799 if (nb == 1) { 0800 SKGAccountObject tmp(selection.at(0)); 0801 account = tmp.getName(); 0802 } else { 0803 // Build whereclause and title 0804 wc = QStringLiteral("rd_account_id in ("); 0805 titleOpen = i18nc("Noun, a list of items", "Transactions of account:"); 0806 0807 for (int i = 0; i < nb; ++i) { 0808 SKGAccountObject tmp(selection.at(i)); 0809 if (i != 0) { 0810 wc += ','; 0811 titleOpen += ','; 0812 } 0813 wc += SKGServices::intToString(tmp.getID()); 0814 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0815 } 0816 wc += ')'; 0817 // Set icon 0818 iconOpen = QStringLiteral("view-bank-account"); 0819 } 0820 } else if (table == QStringLiteral("unit")) { 0821 // Build whereclause and title 0822 wc = QStringLiteral("rc_unit_id in ("); 0823 titleOpen = i18nc("Noun, a list of items", "Transactions with unit:"); 0824 0825 for (int i = 0; i < nb; ++i) { 0826 SKGUnitObject tmp(selection.at(i)); 0827 if (i != 0) { 0828 wc += ','; 0829 titleOpen += ','; 0830 } 0831 wc += SKGServices::intToString(tmp.getID()); 0832 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0833 } 0834 wc += ')'; 0835 0836 // Set icon 0837 iconOpen = QStringLiteral("taxes-finances"); 0838 } else if (table == QStringLiteral("category")) { 0839 titleOpen = i18nc("Noun, a list of items", "Sub transactions with category:"); 0840 0841 for (int i = 0; i < nb; ++i) { 0842 SKGCategoryObject tmp(selection.at(i)); 0843 if (i != 0) { 0844 wc += QStringLiteral(" OR "); 0845 titleOpen += ','; 0846 } 0847 0848 QString name = tmp.getFullName(); 0849 0850 wc += QStringLiteral("(t_REALCATEGORY"); 0851 if (name.isEmpty()) { 0852 wc += QStringLiteral(" IS NULL OR t_REALCATEGORY='')"); 0853 } else { 0854 wc += " = '" % SKGServices::stringToSqlString(name) % "' OR t_REALCATEGORY like '" % SKGServices::stringToSqlString(name) % OBJECTSEPARATOR % "%')"; 0855 } 0856 0857 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0858 } 0859 0860 // Set icon 0861 iconOpen = QStringLiteral("view-categories"); 0862 view = QStringLiteral("v_suboperation_consolidated"); 0863 } else if (table == QStringLiteral("refund")) { 0864 // Build whereclause and title 0865 wc = QStringLiteral("t_REALREFUND in ("); 0866 titleOpen = i18nc("Noun, a list of items", "Sub transactions followed by tracker:"); 0867 0868 for (int i = 0; i < nb; ++i) { 0869 SKGTrackerObject tmp(selection.at(i)); 0870 if (i != 0) { 0871 wc += ','; 0872 titleOpen += ','; 0873 } 0874 wc += '\'' % SKGServices::stringToSqlString(tmp.getName()) % '\''; 0875 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0876 } 0877 wc += ')'; 0878 0879 // Set icon 0880 iconOpen = QStringLiteral("crosshairs"); 0881 view = QStringLiteral("v_suboperation_consolidated"); 0882 } else if (table == QStringLiteral("payee")) { 0883 // Build whereclause and title 0884 wc = QStringLiteral("r_payee_id in ("); 0885 titleOpen = i18nc("Noun, a list of items", "Transactions assigned to payee:"); 0886 0887 for (int i = 0; i < nb; ++i) { 0888 SKGPayeeObject tmp(selection.at(i)); 0889 if (i != 0) { 0890 wc += ','; 0891 titleOpen += ','; 0892 } 0893 wc += SKGServices::intToString(tmp.getID()); 0894 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0895 } 0896 wc += ')'; 0897 0898 // Set icon 0899 iconOpen = QStringLiteral("user-group-properties"); 0900 } else if (table == QStringLiteral("budget")) { 0901 titleOpen = i18nc("Noun, a list of items", "Transactions assigned to budget:"); 0902 0903 for (int i = 0; i < nb; ++i) { 0904 SKGBudgetObject tmp(selection.at(i)); 0905 if (i != 0) { 0906 wc += QStringLiteral(" OR "); 0907 titleOpen += ','; 0908 } 0909 0910 wc += "(t_TYPEACCOUNT<>'L' AND i_SUBOPID IN (SELECT b2.id_suboperation FROM budgetsuboperation b2 WHERE b2.id=" % SKGServices::intToString(tmp.getID()) % "))"; 0911 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0912 } 0913 0914 // Set icon 0915 iconOpen = QStringLiteral("view-calendar-whatsnext"); 0916 view = QStringLiteral("v_suboperation_consolidated"); 0917 } else if (table == QStringLiteral("recurrentoperation")) { 0918 titleOpen = i18nc("Noun, a list of items", "Scheduled transactions:"); 0919 0920 for (int i = 0; i < nb; ++i) { 0921 SKGRecurrentOperationObject tmp(selection.at(i)); 0922 if (i != 0) { 0923 wc += QStringLiteral(" OR "); 0924 titleOpen += ','; 0925 } 0926 0927 wc += "(EXISTS(SELECT 1 FROM recurrentoperation s WHERE s.rd_operation_id=v_operation_display.id and s.id=" % SKGServices::intToString(tmp.getID()) % ") OR r_recurrentoperation_id=" % SKGServices::intToString(tmp.getID()) + ')'; 0928 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0929 } 0930 0931 // Set icon 0932 iconOpen = QStringLiteral("chronometer"); 0933 view = QStringLiteral("v_operation_display"); 0934 } else if (table == QStringLiteral("rule")) { 0935 titleOpen = i18nc("Noun, a list of items", "Sub transactions corresponding to rule:"); 0936 0937 for (int i = 0; i < nb; ++i) { 0938 SKGRuleObject tmp(selection.at(i)); 0939 if (i != 0) { 0940 wc += QStringLiteral(" OR "); 0941 titleOpen += ','; 0942 } 0943 0944 wc += "i_SUBOPID in (SELECT i_SUBOPID FROM v_operation_prop WHERE " % tmp.getSelectSqlOrder() % ')'; 0945 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0946 } 0947 0948 // Set icon 0949 iconOpen = QStringLiteral("edit-find"); 0950 view = QStringLiteral("v_suboperation_consolidated"); 0951 } else if (table == QStringLiteral("operation")) { 0952 // Build whereclause and title 0953 titleOpen = i18nc("Noun, a list of items", "Sub transactions grouped or split of:"); 0954 view = QStringLiteral("v_suboperation_consolidated"); 0955 0956 for (int i = 0; i < nb; ++i) { 0957 SKGOperationObject tmp(selection.at(i)); 0958 if (i != 0) { 0959 wc += QStringLiteral(" OR "); 0960 titleOpen += ','; 0961 } 0962 0963 int opid = tmp.getID(); 0964 wc += QStringLiteral("(i_OPID=") % SKGServices::intToString(opid); 0965 0966 opid = SKGServices::stringToInt(tmp.getAttribute(QStringLiteral("i_group_id"))); 0967 if (opid != 0) { 0968 wc += " or i_group_id=" % SKGServices::intToString(opid); 0969 } 0970 wc += ')'; 0971 0972 titleOpen += i18n("'%1'", tmp.getDisplayName()); 0973 } 0974 0975 // Set icon 0976 iconOpen = icon(); 0977 } else if (table == QStringLiteral("suboperation")) { 0978 // Build whereclause and title 0979 titleOpen = i18nc("Noun, a list of items", "Transactions grouped with:"); 0980 view = QStringLiteral("v_operation_display_all"); 0981 0982 for (int i = 0; i < nb; ++i) { 0983 SKGSubOperationObject tmp(selection.at(i).getDocument(), selection.at(i).getID()); 0984 SKGOperationObject op; 0985 tmp.getParentOperation(op); 0986 if (i != 0) { 0987 wc += QStringLiteral(" OR "); 0988 titleOpen += ','; 0989 } 0990 0991 int opid = op.getID(); 0992 wc += QStringLiteral("(id=") % SKGServices::intToString(opid); 0993 0994 opid = SKGServices::stringToInt(op.getAttribute(QStringLiteral("i_group_id"))); 0995 if (opid != 0) { 0996 wc += " or i_group_id=" % SKGServices::intToString(opid); 0997 } 0998 wc += ')'; 0999 1000 titleOpen += i18n("'%1'", op.getDisplayName()); 1001 } 1002 1003 // Set icon 1004 iconOpen = icon(); 1005 } 1006 1007 // Open 1008 QDomDocument doc(QStringLiteral("SKGML")); 1009 doc.setContent(SKGMainPanel::getMainPanel()->getDocument()->getParameter(view != QStringLiteral("v_suboperation_consolidated") ? QStringLiteral("SKGOPERATION_DEFAULT_PARAMETERS") : QStringLiteral("SKGOPERATION_CONSOLIDATED_DEFAULT_PARAMETERS"))); 1010 QDomElement root = doc.documentElement(); 1011 if (root.isNull()) { 1012 root = doc.createElement(QStringLiteral("parameters")); 1013 doc.appendChild(root); 1014 } 1015 1016 if (!view.isEmpty()) { 1017 root.setAttribute(QStringLiteral("operationTable"), view); 1018 } 1019 if (!wc.isEmpty()) { 1020 root.setAttribute(QStringLiteral("operationWhereClause"), wc); 1021 } 1022 if (!titleOpen.isEmpty()) { 1023 root.setAttribute(QStringLiteral("title"), titleOpen); 1024 } 1025 if (!iconOpen.isEmpty()) { 1026 root.setAttribute(QStringLiteral("title_icon"), iconOpen); 1027 } 1028 if (!account.isEmpty()) { 1029 root.setAttribute(QStringLiteral("account"), account); 1030 } else { 1031 root.setAttribute(QStringLiteral("currentPage"), QStringLiteral("-1")); 1032 } 1033 1034 SKGMainPanel::getMainPanel()->openPage(SKGMainPanel::getMainPanel()->getPluginByName(QStringLiteral("Skrooge operation plugin")), -1, doc.toString(), view == QStringLiteral("v_suboperation_consolidated") ? i18nc("Noun, a list of items", "Sub transactions") : QString()); 1035 } 1036 } 1037 } 1038 1039 SKGAdviceList SKGOperationPlugin::advice(const QStringList& iIgnoredAdvice) 1040 { 1041 SKGTRACEINFUNC(10) 1042 SKGAdviceList output; 1043 output.reserve(20); 1044 int nbConcurrentCheckExecuted = 0; 1045 int nbConcurrentCheckTargeted = 0; 1046 QMutex mutex; 1047 1048 // Search duplicate number on operation 1049 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_duplicate"))) { 1050 nbConcurrentCheckTargeted++; 1051 m_currentBankDocument->concurrentExecuteSelectSqliteOrder(QStringLiteral("SELECT count(1), t_ACCOUNT, t_number FROM v_operation WHERE t_number!='' GROUP BY t_ACCOUNT, t_number HAVING count(1)>1 ORDER BY count(1) DESC"), [ & ](const SKGStringListList & iResult) { 1052 int nb = iResult.count(); 1053 SKGAdvice::SKGAdviceActionList autoCorrections; 1054 for (int i = 1; i < nb; ++i) { // Ignore header 1055 // Get parameters 1056 const QStringList& line = iResult.at(i); 1057 const QString& account = line.at(1); 1058 const QString& number = line.at(2); 1059 1060 SKGAdvice ad; 1061 ad.setUUID("skgoperationplugin_duplicate|" % number % ';' % account); 1062 ad.setPriority(7); 1063 ad.setShortMessage(i18nc("Advice on making the best (short)", "Duplicate number %1 in account '%2'", number, account)); 1064 ad.setLongMessage(i18nc("Advice on making the best (long)", "Your account '%1' contains more than one transaction with number %2.The transaction number should be unique (check number, transaction reference…)", account, number)); 1065 autoCorrections.resize(0); 1066 { 1067 SKGAdvice::SKGAdviceAction a; 1068 a.Title = i18nc("Advice on making the best (action)", "Edit transactions with duplicate number"); 1069 a.IconName = QStringLiteral("quickopen"); 1070 a.IsRecommended = false; 1071 autoCorrections.push_back(a); 1072 } 1073 ad.setAutoCorrections(autoCorrections); 1074 mutex.lock(); 1075 output.push_back(ad); 1076 mutex.unlock(); 1077 } 1078 mutex.lock(); 1079 nbConcurrentCheckExecuted++; 1080 mutex.unlock(); 1081 }, false); 1082 } 1083 1084 // Check transactions not reconciled 1085 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_notreconciled"))) { 1086 nbConcurrentCheckTargeted++; 1087 m_currentBankDocument->concurrentExecuteSelectSqliteOrder(QStringLiteral("SELECT count(1), t_ACCOUNT FROM v_operation WHERE t_status='N' GROUP BY t_ACCOUNT HAVING count(1)>100 ORDER BY count(1) DESC"), [ & ](const SKGStringListList & iResult) { 1088 int nb = iResult.count(); 1089 SKGAdvice::SKGAdviceActionList autoCorrections; 1090 for (int i = 1; i < nb; ++i) { // Ignore header 1091 // Get parameters 1092 const QStringList& line = iResult.at(i); 1093 const QString& account = line.at(1); 1094 1095 SKGAdvice ad; 1096 ad.setUUID("skgoperationplugin_notreconciled|" % account); 1097 ad.setPriority(9); 1098 ad.setShortMessage(i18nc("Advice on making the best (short)", "Many transactions of '%1' not reconciled", account)); 1099 ad.setLongMessage(i18nc("Advice on making the best (long)", "Do not forget to reconcile your accounts. By doing so, you acknowledge that your bank has indeed processed these transactions on your account. This is how you enforce compliance with your bank's statements. See online help for more details")); 1100 autoCorrections.resize(0); 1101 { 1102 SKGAdvice::SKGAdviceAction a; 1103 a.Title = i18nc("Advice on making the best (action)", "Open account '%1' for reconciliation", account); 1104 a.IconName = QStringLiteral("quickopen"); 1105 a.IsRecommended = false; 1106 autoCorrections.push_back(a); 1107 } 1108 ad.setAutoCorrections(autoCorrections); 1109 mutex.lock(); 1110 output.push_back(ad); 1111 mutex.unlock(); 1112 } 1113 mutex.lock(); 1114 nbConcurrentCheckExecuted++; 1115 mutex.unlock(); 1116 }, false); 1117 } 1118 1119 // Not enough money in your account 1120 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_minimum_limit"))) { 1121 nbConcurrentCheckTargeted++; 1122 // Get accounts with amount close or below the minimum limit 1123 m_currentBankDocument->concurrentExecuteSelectSqliteOrder(QStringLiteral("SELECT t_name FROM v_account_amount WHERE t_close='N' AND t_minamount_enabled='Y' AND f_CURRENTAMOUNT<f_CURRENTAMOUNTUNIT*f_minamount"), [ & ](const SKGStringListList & iResult) { 1124 int nb = iResult.count(); 1125 mutex.lock(); 1126 output.reserve(output.count() + nb); 1127 mutex.unlock(); 1128 for (int i = 1; i < nb; ++i) { // Ignore header 1129 // Get parameters 1130 QString account = iResult.at(i).at(0); 1131 1132 SKGAdvice ad; 1133 ad.setUUID("skgoperationplugin_minimum_limit|" % account); 1134 ad.setPriority(9); 1135 ad.setShortMessage(i18nc("Advice on making the best (short)", "Not enough money in your account '%1'", account)); 1136 ad.setLongMessage(i18nc("Advice on making the best (long)", "The amount of this account is below the minimum limit. You should replenish it.")); 1137 mutex.lock(); 1138 output.push_back(ad); 1139 mutex.unlock(); 1140 } 1141 mutex.lock(); 1142 nbConcurrentCheckExecuted++; 1143 mutex.unlock(); 1144 }, false); 1145 } 1146 1147 // Maximum limit reached 1148 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_maximum_limit"))) { 1149 nbConcurrentCheckTargeted++; 1150 // Get accounts with amount over the maximum limit 1151 m_currentBankDocument->concurrentExecuteSelectSqliteOrder(QStringLiteral("SELECT t_name FROM v_account_amount WHERE t_close='N' AND t_maxamount_enabled='Y' AND f_CURRENTAMOUNT>f_CURRENTAMOUNTUNIT*f_maxamount"), [ & ](const SKGStringListList & iResult) { 1152 int nb = iResult.count(); 1153 mutex.lock(); 1154 output.reserve(output.count() + nb); 1155 mutex.unlock(); 1156 for (int i = 1; i < nb; ++i) { // Ignore header 1157 // Get parameters 1158 QString account = iResult.at(i).at(0); 1159 1160 SKGAdvice ad; 1161 ad.setUUID("skgoperationplugin_maximum_limit|" % account); 1162 ad.setPriority(6); 1163 ad.setShortMessage(i18nc("Advice on making the best (short)", "Balance in account '%1' exceeds the maximum limit", account)); 1164 ad.setLongMessage(i18nc("Advice on making the best (long)", "The balance of this account exceeds the maximum limit.")); 1165 mutex.lock(); 1166 output.push_back(ad); 1167 mutex.unlock(); 1168 } 1169 mutex.lock(); 1170 nbConcurrentCheckExecuted++; 1171 mutex.unlock(); 1172 }, false); 1173 } 1174 1175 // Minimum limit reached 1176 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_close_minimum_limit"))) { 1177 nbConcurrentCheckTargeted++; 1178 // Get accounts with amount close or below the minimum limit 1179 m_currentBankDocument->concurrentExecuteSelectSqliteOrder(QStringLiteral("SELECT t_name FROM v_account_amount WHERE t_close='N' AND t_minamount_enabled='Y' AND t_maxamount_enabled='Y'" 1180 " AND f_CURRENTAMOUNT>=f_CURRENTAMOUNTUNIT*f_minamount AND f_CURRENTAMOUNT<=f_CURRENTAMOUNTUNIT*f_minamount+0.1*(f_CURRENTAMOUNTUNIT*f_maxamount-f_CURRENTAMOUNTUNIT*f_minamount)"), [ & ](const SKGStringListList & iResult) { 1181 int nb = iResult.count(); 1182 mutex.lock(); 1183 output.reserve(output.count() + nb); 1184 mutex.unlock(); 1185 for (int i = 1; i < nb; ++i) { // Ignore header 1186 // Get parameters 1187 QString account = iResult.at(i).at(0); 1188 1189 SKGAdvice ad; 1190 ad.setUUID("skgoperationplugin_close_minimum_limit|" % account); 1191 ad.setPriority(5); 1192 ad.setShortMessage(i18nc("Advice on making the best (short)", "Your account '%1' is close to the minimum limit", account)); 1193 ad.setLongMessage(i18nc("Advice on making the best (long)", "The amount of this account is close to the minimum limit. You should take care of it.")); 1194 mutex.lock(); 1195 output.push_back(ad); 1196 mutex.unlock(); 1197 } 1198 mutex.lock(); 1199 nbConcurrentCheckExecuted++; 1200 mutex.unlock(); 1201 }, false); 1202 } 1203 1204 // Too much money in your account 1205 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_too_much_money"))) { 1206 nbConcurrentCheckTargeted++; 1207 m_currentBankDocument->concurrentExecuteSelectSqliteOrder(QStringLiteral("SELECT t_name, f_RATE FROM v_account_display WHERE t_close='N' AND f_RATE>0 AND (t_maxamount_enabled='N' OR f_CURRENTAMOUNT<f_CURRENTAMOUNTUNIT*f_maxamount*0.9) ORDER BY f_RATE DESC"), [ & ](const SKGStringListList & iResult) { 1208 int nb = iResult.count(); 1209 if (nb > 1) { 1210 // Get better interest account 1211 QString target = iResult.at(1).at(0); 1212 QString rate = iResult.at(1).at(1); 1213 1214 // Get accounts with too much money 1215 m_currentBankDocument->concurrentExecuteSelectSqliteOrder("SELECT t_name FROM v_account_display WHERE t_close='N' AND (" 1216 // Interest are computed on amount over the limit too, not need to transfer them "(t_maxamount_enabled='Y' AND f_CURRENTAMOUNT>f_CURRENTAMOUNTUNIT*f_maxamount) OR " 1217 "(f_RATE<" % rate % " AND t_type='C' AND f_CURRENTAMOUNT>-2*(SELECT TOTAL(s.f_CURRENTAMOUNT) FROM v_operation_display s WHERE s.rd_account_id=v_account_display.id AND s.t_TYPEEXPENSE='-' AND s.d_DATEMONTH = (SELECT strftime('%Y-%m',date('now', 'localtime','start of month', '-1 MONTH')))))" 1218 ")", [ &output, target, rate ](const SKGStringListList & iResult2) { 1219 int nb = iResult2.count(); 1220 QMutex mutex; 1221 mutex.lock(); 1222 output.reserve(output.count() + nb); 1223 mutex.unlock(); 1224 for (int i = 1; i < nb; ++i) { // Ignore header 1225 // Get parameters 1226 QString account = iResult2.at(i).at(0); 1227 1228 SKGAdvice ad; 1229 ad.setUUID("skgoperationplugin_too_much_money|" % account); 1230 ad.setPriority(6); 1231 ad.setShortMessage(i18nc("Advice on making the best (short)", "Too much money in your account '%1'", account)); 1232 ad.setLongMessage(i18nc("Advice on making the best (long)", "You could save money on an account with a better rate. Example: '%1' (%2%)", target, rate)); 1233 mutex.lock(); 1234 output.push_back(ad); 1235 mutex.unlock(); 1236 } 1237 }, false); 1238 } 1239 mutex.lock(); 1240 nbConcurrentCheckExecuted++; 1241 mutex.unlock(); 1242 }, false); 1243 } 1244 1245 // Check transactions without mode 1246 if (!iIgnoredAdvice.contains(QStringLiteral("skgimportexportplugin_notvalidated"))) { 1247 nbConcurrentCheckTargeted++; 1248 m_currentBankDocument->concurrentExistObjects(QStringLiteral("operation"), 1249 QStringLiteral("t_template='N' AND t_mode='' AND d_date<>'0000-00-00'"), 1250 [ & ](bool iFound) { 1251 if (iFound) { 1252 SKGAdvice ad; 1253 ad.setUUID(QStringLiteral("skgimportexportplugin_notvalidated")); 1254 ad.setPriority(5); 1255 ad.setShortMessage(i18nc("Advice on making the best (short)", "Many transactions do not have mode")); 1256 ad.setLongMessage(i18nc("Advice on making the best (long)", "Do not forget to set a mode for each transaction. This will allow you to generate better reports.")); 1257 QStringList autoCorrections; 1258 autoCorrections.push_back(QStringLiteral("skg://view_open_operation_without_mode")); 1259 ad.setAutoCorrections(autoCorrections); 1260 1261 mutex.lock(); 1262 output.push_back(ad); 1263 mutex.unlock(); 1264 } 1265 mutex.lock(); 1266 nbConcurrentCheckExecuted++; 1267 mutex.unlock(); 1268 }, false); 1269 } 1270 1271 // Check transactions without category 1272 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_nocategory"))) { 1273 nbConcurrentCheckTargeted++; 1274 m_currentBankDocument->concurrentExistObjects(QStringLiteral("v_operation_display"), 1275 QStringLiteral("t_TRANSFER='N' AND EXISTS (SELECT 1 FROM suboperation WHERE rd_operation_id=v_operation_display.id AND r_category_id=0)"), 1276 [ & ](bool iFound) { 1277 if (iFound) { 1278 SKGAdvice ad; 1279 ad.setUUID(QStringLiteral("skgoperationplugin_nocategory")); 1280 ad.setPriority(5); 1281 ad.setShortMessage(i18nc("Advice on making the best (short)", "Many transactions do not have category")); 1282 ad.setLongMessage(i18nc("Advice on making the best (long)", "Do not forget to associate a category for each transaction. This will allow you to generate better reports.")); 1283 QStringList autoCorrections; 1284 autoCorrections.push_back(QStringLiteral("skg://view_open_operation_without_category")); 1285 ad.setAutoCorrections(autoCorrections); 1286 1287 mutex.lock(); 1288 output.push_back(ad); 1289 mutex.unlock(); 1290 } 1291 mutex.lock(); 1292 nbConcurrentCheckExecuted++; 1293 mutex.unlock(); 1294 }, false); 1295 } 1296 1297 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_transfer_nocategory"))) { 1298 nbConcurrentCheckTargeted++; 1299 m_currentBankDocument->concurrentExistObjects(QStringLiteral("v_operation_display"), 1300 QStringLiteral("t_TRANSFER='Y' AND EXISTS (SELECT 1 FROM suboperation WHERE rd_operation_id=v_operation_display.id AND r_category_id=0)"), 1301 [ & ](bool iFound) { 1302 if (iFound) { 1303 SKGAdvice ad; 1304 ad.setUUID(QStringLiteral("skgoperationplugin_transfer_nocategory")); 1305 ad.setPriority(3); 1306 ad.setShortMessage(i18nc("Advice on making the best (short)", "Many transfers do not have category")); 1307 ad.setLongMessage(i18nc("Advice on making the best (long)", "Do not forget to associate a category for each transfer.")); 1308 QStringList autoCorrections; 1309 autoCorrections.push_back(QStringLiteral("skg://view_open_transfers_without_category")); 1310 ad.setAutoCorrections(autoCorrections); 1311 1312 mutex.lock(); 1313 output.push_back(ad); 1314 mutex.unlock(); 1315 } 1316 mutex.lock(); 1317 nbConcurrentCheckExecuted++; 1318 mutex.unlock(); 1319 }, false); 1320 } 1321 1322 // Check transactions without payee 1323 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_nopayee"))) { 1324 nbConcurrentCheckTargeted++; 1325 m_currentBankDocument->concurrentExistObjects(QStringLiteral("v_operation_display"), 1326 QStringLiteral("t_TRANSFER='N' AND r_payee_id=0"), 1327 [ & ](bool iFound) { 1328 if (iFound) { 1329 SKGAdvice ad; 1330 ad.setUUID(QStringLiteral("skgoperationplugin_nopayee")); 1331 ad.setPriority(5); 1332 ad.setShortMessage(i18nc("Advice on making the best (short)", "Many transactions do not have payee")); 1333 ad.setLongMessage(i18nc("Advice on making the best (long)", "Do not forget to associate a payee for each transaction. This will allow you to generate better reports.")); 1334 QStringList autoCorrections; 1335 autoCorrections.push_back(QStringLiteral("skg://view_open_operation_without_payee")); 1336 ad.setAutoCorrections(autoCorrections); 1337 1338 mutex.lock(); 1339 output.push_back(ad); 1340 mutex.unlock(); 1341 } 1342 mutex.lock(); 1343 nbConcurrentCheckExecuted++; 1344 mutex.unlock(); 1345 }, false); 1346 } 1347 1348 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_transfer_nopayee"))) { 1349 nbConcurrentCheckTargeted++; 1350 m_currentBankDocument->concurrentExistObjects(QStringLiteral("v_operation_display"), 1351 QStringLiteral("t_TRANSFER='Y' AND r_payee_id=0"), 1352 [ & ](bool iFound) { 1353 if (iFound) { 1354 SKGAdvice ad; 1355 ad.setUUID(QStringLiteral("skgoperationplugin_transfer_nopayee")); 1356 ad.setPriority(3); 1357 ad.setShortMessage(i18nc("Advice on making the best (short)", "Many transfers do not have payee")); 1358 ad.setLongMessage(i18nc("Advice on making the best (long)", "Do not forget to associate a payee for each transfer.")); 1359 QStringList autoCorrections; 1360 autoCorrections.push_back(QStringLiteral("skg://view_open_transfers_without_payee")); 1361 ad.setAutoCorrections(autoCorrections); 1362 1363 mutex.lock(); 1364 output.push_back(ad); 1365 mutex.unlock(); 1366 } 1367 mutex.lock(); 1368 nbConcurrentCheckExecuted++; 1369 mutex.unlock(); 1370 }, false); 1371 } 1372 1373 // Check transactions in group of only one transaction 1374 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_groupofone"))) { 1375 nbConcurrentCheckTargeted++; 1376 m_currentBankDocument->concurrentExistObjects(QStringLiteral("v_operation_display"), 1377 QStringLiteral("i_group_id<>0 AND (SELECT COUNT(1) FROM operation o WHERE o.i_group_id=v_operation_display.i_group_id)<2"), 1378 [ & ](bool iFound) { 1379 if (iFound) { 1380 SKGAdvice ad; 1381 ad.setUUID(QStringLiteral("skgoperationplugin_groupofone")); 1382 ad.setPriority(4); 1383 ad.setShortMessage(i18nc("Advice on making the best (short)", "Some transactions are in groups with only one transaction")); 1384 ad.setLongMessage(i18nc("Advice on making the best (long)", "When a transfer is created and when only one part of this transfer is removed, the second part is in a group of only one transaction. This makes no sense.")); 1385 SKGAdvice::SKGAdviceActionList autoCorrections; 1386 { 1387 SKGAdvice::SKGAdviceAction a; 1388 a.Title = QStringLiteral("skg://view_open_operation_in_group_of_one"); 1389 a.IsRecommended = false; 1390 autoCorrections.push_back(a); 1391 } 1392 { 1393 SKGAdvice::SKGAdviceAction a; 1394 a.Title = QStringLiteral("skg://clean_remove_group_with_one_operation"); 1395 a.IsRecommended = true; 1396 autoCorrections.push_back(a); 1397 } 1398 ad.setAutoCorrections(autoCorrections); 1399 1400 mutex.lock(); 1401 output.push_back(ad); 1402 mutex.unlock(); 1403 } 1404 mutex.lock(); 1405 nbConcurrentCheckExecuted++; 1406 mutex.unlock(); 1407 }, false); 1408 } 1409 1410 // Check simple transactions with comment not aligned with the comment of the subtransaction 1411 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_commentsnotaligned"))) { 1412 nbConcurrentCheckTargeted++; 1413 m_currentBankDocument->concurrentExistObjects(QStringLiteral("operation op, suboperation so"), 1414 QStringLiteral("so.rd_operation_id=op.id AND so.t_comment<>op.t_comment AND (SELECT COUNT(1) FROM suboperation so2 WHERE so2.rd_operation_id=op.id)=1"), 1415 [ & ](bool iFound) { 1416 if (iFound) { 1417 SKGAdvice ad; 1418 ad.setUUID(QStringLiteral("skgoperationplugin_commentsnotaligned")); 1419 ad.setPriority(5); 1420 ad.setShortMessage(i18nc("Advice on making the best (short)", "Some simple transactions do not have their comments aligned")); 1421 ad.setLongMessage(i18nc("Advice on making the best (long)", "The comment of the transaction is not aligned with the comment of the subtransaction.")); 1422 SKGAdvice::SKGAdviceActionList autoCorrections; 1423 { 1424 SKGAdvice::SKGAdviceAction a; 1425 a.Title = QStringLiteral("skg://view_open_operation_with_comment_not_aligned"); 1426 a.IsRecommended = false; 1427 autoCorrections.push_back(a); 1428 } 1429 { 1430 SKGAdvice::SKGAdviceAction a; 1431 a.Title = QStringLiteral("skg://align_comment"); 1432 a.IsRecommended = true; 1433 autoCorrections.push_back(a); 1434 } 1435 { 1436 SKGAdvice::SKGAdviceAction a; 1437 a.Title = QStringLiteral("skg://align_comment2"); 1438 a.IsRecommended = false; 1439 autoCorrections.push_back(a); 1440 } 1441 ad.setAutoCorrections(autoCorrections); 1442 1443 mutex.lock(); 1444 output.push_back(ad); 1445 mutex.unlock(); 1446 } 1447 mutex.lock(); 1448 nbConcurrentCheckExecuted++; 1449 mutex.unlock(); 1450 }, false); 1451 } 1452 1453 // Check simple transactions with date not aligned with the date of the subtransaction 1454 if (!iIgnoredAdvice.contains(QStringLiteral("skgoperationplugin_datesnotaligned"))) { 1455 nbConcurrentCheckTargeted++; 1456 m_currentBankDocument->concurrentExistObjects(QStringLiteral("operation op, suboperation so"), 1457 QStringLiteral("so.rd_operation_id=op.id AND (so.d_date<op.d_date OR (so.d_date<>op.d_date AND (SELECT COUNT(1) FROM suboperation so2 WHERE so2.rd_operation_id=op.id)=1))"), 1458 [ & ](bool iFound) { 1459 if (iFound) { 1460 SKGAdvice ad; 1461 ad.setUUID(QStringLiteral("skgoperationplugin_datesnotaligned")); 1462 ad.setPriority(5); 1463 ad.setShortMessage(i18nc("Advice on making the best (short)", "Some transactions do not have their dates aligned")); 1464 ad.setLongMessage(i18nc("Advice on making the best (long)", "The date of the transaction is not aligned with the date of the subtransaction. This case seems to be abnormal.")); 1465 SKGAdvice::SKGAdviceActionList autoCorrections; 1466 { 1467 SKGAdvice::SKGAdviceAction a; 1468 a.Title = QStringLiteral("skg://view_open_operation_with_date_not_aligned"); 1469 a.IsRecommended = false; 1470 autoCorrections.push_back(a); 1471 } 1472 { 1473 SKGAdvice::SKGAdviceAction a; 1474 a.Title = QStringLiteral("skg://align_date"); 1475 a.IsRecommended = true; 1476 autoCorrections.push_back(a); 1477 } 1478 ad.setAutoCorrections(autoCorrections); 1479 1480 mutex.lock(); 1481 output.push_back(ad); 1482 mutex.unlock(); 1483 } 1484 mutex.lock(); 1485 nbConcurrentCheckExecuted++; 1486 mutex.unlock(); 1487 }, false); 1488 } 1489 1490 // Check transactions having category not aligned with the category of the payee 1491 if (!iIgnoredAdvice.contains(QStringLiteral("skgpayeeplugin_subop_with_wrong_wategory"))) { 1492 bool exist = false; 1493 m_currentBankDocument->existObjects(QStringLiteral("v_suboperation_consolidated INNER JOIN payee ON v_suboperation_consolidated.r_payee_id=payee.id"), QStringLiteral("payee.r_category_id!=0 AND v_suboperation_consolidated.r_category_id!=payee.r_category_id"), exist); 1494 if (exist) { 1495 SKGAdvice ad; 1496 ad.setUUID(QStringLiteral("skgpayeeplugin_subop_with_wrong_wategory")); 1497 ad.setPriority(3); 1498 ad.setShortMessage(i18nc("Advice on making the best (short)", "Category not aligned with payee's category")); 1499 ad.setLongMessage(i18nc("Advice on making the best (long)", "Some transactions don't have the category aligned with the category of the payee.")); 1500 SKGAdvice::SKGAdviceActionList autoCorrections; 1501 { 1502 SKGAdvice::SKGAdviceAction a; 1503 a.Title = QStringLiteral("skg://view_open_suboperations_without_payee_category"); 1504 autoCorrections.push_back(a); 1505 } 1506 { 1507 SKGAdvice::SKGAdviceAction a; 1508 a.Title = QStringLiteral("skg://align_category"); 1509 1510 autoCorrections.push_back(a); 1511 } 1512 ad.setAutoCorrections(autoCorrections); 1513 output.push_back(ad); 1514 } 1515 } 1516 1517 do { 1518 QThread::yieldCurrentThread(); 1519 if (nbConcurrentCheckExecuted == nbConcurrentCheckTargeted) { 1520 break; 1521 } 1522 } while (true); 1523 1524 return output; 1525 } 1526 1527 SKGError SKGOperationPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution) 1528 { 1529 if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgoperationplugin_duplicate|"))) { 1530 // Get parameters 1531 QString parameters = iAdviceIdentifier.right(iAdviceIdentifier.length() - 29); 1532 int pos = parameters.indexOf(';'); 1533 QString num = parameters.left(pos); 1534 QString account = parameters.right(parameters.length() - 1 - pos); 1535 1536 // Call transaction plugin 1537 SKGMainPanel::getMainPanel()->openPage("skg://skrooge_operation_plugin/?title_icon=security-low&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Transactions of '%1' with duplicate number %2", account, num)) % 1538 "&operationWhereClause=" % SKGServices::encodeForUrl("t_number='" % SKGServices::stringToSqlString(num) % "' AND t_ACCOUNT='" % SKGServices::stringToSqlString(account) % '\'')); 1539 return SKGError(); 1540 } 1541 if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgoperationplugin_notreconciled|"))) { 1542 // Get parameters 1543 QString account = iAdviceIdentifier.right(iAdviceIdentifier.length() - 36); 1544 SKGMainPanel::getMainPanel()->openPage("skg://skrooge_operation_plugin/?currentPage=-1&modeInfoZone=1&account=" % SKGServices::encodeForUrl(account)); 1545 return SKGError(); 1546 } 1547 1548 return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution); 1549 } 1550 1551 void SKGOperationPlugin::onRemoveGroupWithOneOperation() 1552 { 1553 SKGError err; 1554 SKGTRACEINFUNCRC(10, err) { 1555 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 1556 auto* act = qobject_cast< QAction* >(sender()); 1557 if ((act == nullptr) || !act->data().toBool()) { 1558 // Clear selection to enable the model "All" 1559 selection.clear(); 1560 } 1561 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Remove groups with only one transaction"), err) 1562 QString q = QStringLiteral("UPDATE operation SET i_group_id=0 WHERE i_group_id<>0 AND (SELECT COUNT(1) FROM operation o WHERE o.i_group_id=operation.i_group_id)<2"); 1563 int nb = selection.count(); 1564 if (nb == 0) { 1565 err = m_currentBankDocument->executeSqliteOrder(q); 1566 } else { 1567 for (int i = 0; !err && i < nb; ++i) { 1568 SKGOperationObject op(selection.at(i)); 1569 err = m_currentBankDocument->executeSqliteOrder(q % " AND id=" % SKGServices::intToString(op.getID())); 1570 } 1571 } 1572 } 1573 1574 // status bar 1575 IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Remove groups done."))) 1576 else { 1577 err.addError(ERR_FAIL, i18nc("Error message", "Remove groups failed")); 1578 } 1579 1580 // Display error 1581 SKGMainPanel::displayErrorMessage(err); 1582 } 1583 1584 void SKGOperationPlugin::onAlignComment() 1585 { 1586 SKGError err; 1587 SKGTRACEINFUNCRC(10, err) 1588 1589 { 1590 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 1591 auto* act = qobject_cast< QAction* >(sender()); 1592 if ((act == nullptr) || !act->data().toBool()) { 1593 // Clear selection to enable the model "All" 1594 selection.clear(); 1595 } 1596 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Align comment of subtransactions"), err) 1597 QString q = QStringLiteral("UPDATE suboperation SET t_comment=(SELECT op.t_comment FROM operation op WHERE suboperation.rd_operation_id=op.id) WHERE suboperation.id IN (SELECT so.id FROM operation op, suboperation so WHERE so.rd_operation_id=op.id AND so.t_comment<>op.t_comment AND (SELECT COUNT(1) FROM suboperation so2 WHERE so2.rd_operation_id=op.id)=1)"); 1598 int nb = selection.count(); 1599 if (nb == 0) { 1600 err = m_currentBankDocument->executeSqliteOrder(q); 1601 } else { 1602 for (int i = 0; !err && i < nb; ++i) { 1603 SKGOperationObject op(selection.at(i)); 1604 err = m_currentBankDocument->executeSqliteOrder(q % " AND rd_operation_id=" % SKGServices::intToString(op.getID())); 1605 } 1606 } 1607 } 1608 1609 // status bar 1610 IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Comments aligned."))) 1611 else { 1612 err.addError(ERR_FAIL, i18nc("Error message", "Comments alignment failed")); 1613 } 1614 1615 // Display error 1616 SKGMainPanel::displayErrorMessage(err); 1617 } 1618 1619 void SKGOperationPlugin::onAlignComment2() 1620 { 1621 SKGError err; 1622 SKGTRACEINFUNCRC(10, err) 1623 1624 { 1625 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 1626 auto* act = qobject_cast< QAction* >(sender()); 1627 if ((act == nullptr) || !act->data().toBool()) { 1628 // Clear selection to enable the model "All" 1629 selection.clear(); 1630 } 1631 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Align comment of transactions"), err) 1632 QString q = QStringLiteral("UPDATE operation SET t_comment=(SELECT suboperation.t_comment FROM suboperation WHERE suboperation.rd_operation_id=operation.id) WHERE operation.id IN (SELECT operation.id FROM operation op, suboperation so WHERE so.rd_operation_id=op.id AND so.t_comment<>op.t_comment AND (SELECT COUNT(1) FROM suboperation so2 WHERE so2.rd_operation_id=op.id)=1)"); 1633 int nb = selection.count(); 1634 if (nb == 0) { 1635 err = m_currentBankDocument->executeSqliteOrder(q); 1636 } else { 1637 for (int i = 0; !err && i < nb; ++i) { 1638 SKGOperationObject op(selection.at(i)); 1639 err = m_currentBankDocument->executeSqliteOrder(q % " AND id=" % SKGServices::intToString(op.getID())); 1640 } 1641 } 1642 } 1643 1644 // status bar 1645 IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Comments aligned."))) 1646 else { 1647 err.addError(ERR_FAIL, i18nc("Error message", "Comments alignment failed")); 1648 } 1649 1650 // Display error 1651 SKGMainPanel::displayErrorMessage(err); 1652 } 1653 1654 void SKGOperationPlugin::onAlignDate() 1655 { 1656 SKGError err; 1657 SKGTRACEINFUNCRC(10, err) 1658 1659 { 1660 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 1661 auto* act = qobject_cast< QAction* >(sender()); 1662 if ((act == nullptr) || !act->data().toBool()) { 1663 // Clear selection to enable the model "All" 1664 selection.clear(); 1665 } 1666 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Align date of subtransactions"), err) 1667 QString q = QStringLiteral("UPDATE suboperation SET d_date=(SELECT op.d_date FROM operation op WHERE suboperation.rd_operation_id=op.id) WHERE suboperation.id IN (SELECT so.id FROM operation op, suboperation so WHERE so.rd_operation_id=op.id AND (so.d_date<op.d_date OR (so.d_date<>op.d_date AND (SELECT COUNT(1) FROM suboperation so2 WHERE so2.rd_operation_id=op.id)=1)))"); 1668 int nb = selection.count(); 1669 if (nb == 0) { 1670 err = m_currentBankDocument->executeSqliteOrder(q); 1671 } else { 1672 for (int i = 0; !err && i < nb; ++i) { 1673 SKGOperationObject op(selection.at(i)); 1674 err = m_currentBankDocument->executeSqliteOrder(q % " AND rd_operation_id=" % SKGServices::intToString(op.getID())); 1675 } 1676 } 1677 } 1678 1679 // status bar 1680 IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Dates aligned."))) 1681 else { 1682 err.addError(ERR_FAIL, i18nc("Error message", "Dates alignment failed")); 1683 } 1684 1685 // Display error 1686 SKGMainPanel::displayErrorMessage(err); 1687 } 1688 1689 void SKGOperationPlugin::onAlignWithCategoryOfPayee() 1690 { 1691 SKGError err; 1692 SKGTRACEINFUNCRC(10, err) 1693 1694 { 1695 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 1696 auto* act = qobject_cast< QAction* >(sender()); 1697 if ((act == nullptr) || !act->data().toBool()) { 1698 // Clear selection to enable the model "All" 1699 selection.clear(); 1700 } 1701 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Align the category of all single transactions with the category of their payee"), err) 1702 QString q = QStringLiteral("UPDATE suboperation SET r_category_id=(SELECT p.r_category_id FROM v_suboperation_consolidated so INNER JOIN payee p ON so.r_payee_id=p.id WHERE so.id=suboperation.id AND p.r_category_id!=0 AND so.r_category_id!=p.r_category_id) WHERE suboperation.id IN (SELECT so.id FROM v_suboperation_consolidated so INNER JOIN payee p ON so.r_payee_id=p.id WHERE p.r_category_id!=0 AND so.r_category_id!=p.r_category_id AND so.i_NBSUBOPERATIONS)"); 1703 int nb = selection.count(); 1704 if (nb == 0) { 1705 err = m_currentBankDocument->executeSqliteOrder(q); 1706 } else { 1707 for (int i = 0; !err && i < nb; ++i) { 1708 SKGOperationObject op(selection.at(i)); 1709 err = m_currentBankDocument->executeSqliteOrder(q % " AND rd_operation_id=" % SKGServices::intToString(op.getID())); 1710 } 1711 } 1712 } 1713 1714 // status bar 1715 IFOKDO(err, SKGError(0, i18nc("Message for successful user action", "Categories aligned."))) 1716 else { 1717 err.addError(ERR_FAIL, i18nc("Error message", "Categories alignment failed")); 1718 } 1719 1720 // Display error 1721 SKGMainPanel::displayErrorMessage(err); 1722 } 1723 1724 void SKGOperationPlugin::onMergeSubOperations() 1725 { 1726 SKGError err; 1727 SKGTRACEINFUNCRC(10, err) 1728 1729 if ((SKGMainPanel::getMainPanel() != nullptr) && (m_currentBankDocument != nullptr)) { 1730 SKGObjectBase::SKGListSKGObjectBase selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 1731 int nb = selection.count(); 1732 if (nb >= 2) { 1733 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Merge sub transactions"), err) 1734 SKGOperationObject op(selection.at(0)); 1735 for (int i = 1; !err && i < nb; ++i) { 1736 SKGOperationObject op2(selection.at(i)); 1737 err = op.mergeSuboperations(op2); 1738 1739 // Send message 1740 IFOKDO(err, op.getDocument()->sendMessage(i18nc("An information to the user", "The sub transactions of '%1' have been merged in the transaction '%2'", op2.getDisplayName(), op.getDisplayName()), SKGDocument::Hidden)) 1741 } 1742 } 1743 } 1744 1745 // status bar 1746 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Transactions merged."))) 1747 else { 1748 err.addError(ERR_FAIL, i18nc("Error message", "Merge failed")); 1749 } 1750 1751 // Display error 1752 SKGMainPanel::displayErrorMessage(err); 1753 } 1754 1755 void SKGOperationPlugin::onShowOpenWithMenu() 1756 { 1757 if ((m_openOperationsWithMenu != nullptr) && (m_currentBankDocument != nullptr)) { 1758 // Clean Menu 1759 auto* m = qobject_cast<QMenu*>(sender()); 1760 bool modeOperation = (m == m_openOperationsWithMenu); 1761 m->clear(); 1762 1763 // Get Selection 1764 auto selection = SKGMainPanel::getMainPanel()->getSelectedObjects(); 1765 1766 // Build list od attribute to take into account 1767 QStringList existingAttributes; 1768 m_currentBankDocument->getAttributesList(modeOperation ? QStringLiteral("v_operation_display") : QStringLiteral("v_suboperation_consolidated"), existingAttributes); 1769 1770 QStringList attributes; 1771 auto schema = SKGServices::splitCSVLine(m_currentBankDocument->getDisplaySchemas(modeOperation ? QStringLiteral("operation") : QStringLiteral("suboperation")).at(0).schema); 1772 attributes.reserve(attributes.count()); 1773 for (const auto& att : qAsConst(schema)) { 1774 auto a = SKGServices::splitCSVLine(att, QLatin1Char('|')).value(0); 1775 if (existingAttributes.contains(a) && !a.endsWith(QLatin1String("_INCOME")) && !a.endsWith(QLatin1String("_EXPENSE"))) { 1776 attributes.push_back(a); 1777 } 1778 } 1779 1780 // Build menus 1781 auto unitp = m_currentBankDocument->getPrimaryUnit(); 1782 int nb = attributes.count(); 1783 for (int i = 0; i < nb; ++i) { 1784 // Add actions 1785 QString att = attributes[i]; 1786 QString attDisplay = m_currentBankDocument->getDisplay(att); 1787 QString value = selection[0].getAttribute(att); 1788 QString iconName = m_currentBankDocument->getIconName(att); 1789 QString wc = att; 1790 if (att.startsWith(QLatin1String("f_"))) { 1791 wc += '=' % SKGServices::stringToSqlString(value); 1792 } else { 1793 wc += "='" % SKGServices::stringToSqlString(value) % '\''; 1794 } 1795 if (att.startsWith(QStringLiteral("f_"))) { 1796 auto unit = unitp; 1797 if (!att.contains(QStringLiteral("QUANTITY")) && !att.contains(QStringLiteral("f_BALANCE_ENTERED"))) { 1798 value = SKGServices::toCurrencyString(SKGServices::stringToDouble(value), unit.Symbol, unit.NbDecimal); 1799 } else { 1800 unit.NbDecimal = SKGServices::stringToInt(selection[0].getAttribute(QStringLiteral("i_NBDEC"))); 1801 if (unit.NbDecimal == 0) { 1802 unit.NbDecimal = 2; 1803 } 1804 if (att != QStringLiteral("f_QUANTITYOWNED")) { 1805 unit.Symbol = selection[0].getAttribute(QStringLiteral("t_UNIT")); 1806 } 1807 value = SKGServices::toCurrencyString(SKGServices::stringToDouble(value), unit.Symbol, unit.NbDecimal); 1808 } 1809 } 1810 QString title = i18nc("A condition", "%1 = %2", attDisplay, value); 1811 1812 if (!value.isEmpty() && att != attDisplay) { 1813 QAction* act = m->addAction(m_currentBankDocument->getIcon(att), title); 1814 if (act != nullptr) { 1815 connect(act, &QAction::triggered, this, [ title, wc, iconName, modeOperation ]() { 1816 QString view = modeOperation ? QStringLiteral("v_operation_display") : QStringLiteral("v_suboperation_consolidated"); 1817 // Open 1818 QDomDocument doc(QStringLiteral("SKGML")); 1819 doc.setContent(SKGMainPanel::getMainPanel()->getDocument()->getParameter(view != QStringLiteral("v_suboperation_consolidated") ? QStringLiteral("SKGOPERATION_DEFAULT_PARAMETERS") : QStringLiteral("SKGOPERATION_CONSOLIDATED_DEFAULT_PARAMETERS"))); 1820 QDomElement root = doc.documentElement(); 1821 if (root.isNull()) { 1822 root = doc.createElement(QStringLiteral("parameters")); 1823 doc.appendChild(root); 1824 } 1825 1826 1827 root.setAttribute(QStringLiteral("operationTable"), view); 1828 root.setAttribute(QStringLiteral("operationWhereClause"), wc); 1829 root.setAttribute(QStringLiteral("title"), title); 1830 root.setAttribute(QStringLiteral("title_icon"), iconName); 1831 1832 SKGMainPanel::getMainPanel()->openPage(SKGMainPanel::getMainPanel()->getPluginByName(QStringLiteral("Skrooge operation plugin")), -1, doc.toString(), modeOperation ? QString() : i18nc("Noun, a list of items", "Sub transactions")); 1833 }); 1834 } 1835 } 1836 } 1837 } 1838 } 1839 1840 #include <skgoperationplugin.moc>