1 /***************************************************************************
2 * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3 * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4 * SPDX-License-Identifier: GPL-3.0-or-later
5 ***************************************************************************/
6 /** @file
7 * This file is Skrooge plugin to generate categories.
8 *
9 * @author Stephane MANKOWSKI / Guillaume DE BURE
10 */
11 #include "skgcategoriesplugin.h"
12
13 #include <kaboutdata.h>
14 #include <kactioncollection.h>
15 #include <kpluginfactory.h>
16 #include <kselectaction.h>
17
18 #include <qaction.h>
19 #include <qdiriterator.h>
20 #include <qfileinfo.h>
21 #include <qstandardpaths.h>
22
23 #include "skgcategoriespluginwidget.h"
24 #include "skgcategoryobject.h"
25 #include "skgdocumentbank.h"
26 #include "skghtmlboardwidget.h"
27 #include "skgimportexportmanager.h"
28 #include "skgmainpanel.h"
29 #include "skgtraces.h"
30 #include "skgtransactionmng.h"
31
32 /**
33 * This plugin factory.
34 */
K_PLUGIN_FACTORY(SKGCategoriesPluginFactory,registerPlugin<SKGCategoriesPlugin> ();)35 K_PLUGIN_FACTORY(SKGCategoriesPluginFactory, registerPlugin<SKGCategoriesPlugin>();)
36
37 SKGCategoriesPlugin::SKGCategoriesPlugin(QWidget* iWidget, QObject* iParent, const QVariantList& /*iArg*/)
38 : SKGInterfacePlugin(iParent), m_currentBankDocument(nullptr)
39 {
40 Q_UNUSED(iWidget)
41 SKGTRACEINFUNC(10)
42 }
43
~SKGCategoriesPlugin()44 SKGCategoriesPlugin::~SKGCategoriesPlugin()
45 {
46 SKGTRACEINFUNC(10)
47 m_currentBankDocument = nullptr;
48 }
49
setupActions(SKGDocument * iDocument)50 bool SKGCategoriesPlugin::setupActions(SKGDocument* iDocument)
51 {
52 SKGTRACEINFUNC(10)
53
54 m_currentBankDocument = qobject_cast<SKGDocumentBank*>(iDocument);
55 if (m_currentBankDocument == nullptr) {
56 return false;
57 }
58
59 setComponentName(QStringLiteral("skrooge_categories"), title());
60 setXMLFile(QStringLiteral("skrooge_categories.rc"));
61
62 // Import categories
63 QStringList overlaycategories;
64 overlaycategories.push_back(icon());
65
66 QStringList overlaydelete;
67 overlaydelete.push_back(QStringLiteral("edit-delete"));
68
69 auto contextMenu = new KSelectAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlaycategories), i18nc("Verb", "Import categories"), this);
70 registerGlobalAction(QStringLiteral("import_categories"), contextMenu);
71
72 QAction* actImportStdCat = contextMenu->addAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlaycategories), i18nc("Verb", "Import standard categories"));
73 actImportStdCat->setCheckable(false);
74 connect(actImportStdCat, &QAction::triggered, this, &SKGCategoriesPlugin::importStandardCategories);
75 registerGlobalAction(QStringLiteral("import_standard_categories"), actImportStdCat);
76
77 const auto dirs = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, "skrooge/categories/" % QLocale().name().split(QStringLiteral("_")).at(0), QStandardPaths::LocateDirectory);
78 for (const auto& dir : dirs) {
79 QDirIterator it(dir, QStringList() << QStringLiteral("*.qif"));
80 while (it.hasNext()) {
81 QString cat = it.next();
82
83 QString name = QFileInfo(cat).baseName().replace('_', ' ');
84 QAction* act = contextMenu->addAction(SKGServices::fromTheme(QStringLiteral("document-import"), overlaycategories), i18nc("Verb", "Import categories [%1]", name));
85 act->setCheckable(false);
86 act->setData(cat);
87 connect(act, &QAction::triggered, this, &SKGCategoriesPlugin::importCategories);
88 registerGlobalAction("import_categories_" % name, act);
89 }
90 }
91
92 auto deleteUnusedCategoriesAction = new QAction(SKGServices::fromTheme(icon(), overlaydelete), i18nc("Verb", "Delete unused categories"), this);
93 connect(deleteUnusedCategoriesAction, &QAction::triggered, this, &SKGCategoriesPlugin::deleteUnusedCategories);
94 registerGlobalAction(QStringLiteral("clean_delete_unused_categories"), deleteUnusedCategoriesAction);
95
96 // ------------
97 auto actTmp = new QAction(SKGServices::fromTheme(icon()), i18nc("Verb", "Open similar categories..."), this);
98 actTmp->setData(QString("skg://skrooge_categories_plugin/?title_icon=" % icon() % "&title=" % SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Similar categories")) %
99 "&whereClause=" % SKGServices::encodeForUrl(QStringLiteral("id IN (SELECT category.id FROM category, ("
100 "SELECT * FROM category C WHERE EXISTS (SELECT 1 FROM category p2 WHERE p2.id<>C.id AND upper(p2.t_fullname)=upper(C.t_fullname) AND p2.t_fullname<>C.t_fullname)) "
101 "AS C WHERE category.t_fullname=C.t_fullname OR C.t_fullname LIKE category.t_fullname||' > %')"))));
102 connect(actTmp, &QAction::triggered, SKGMainPanel::getMainPanel(), [ = ]() {
103 SKGMainPanel::getMainPanel()->SKGMainPanel::openPage();
104 });
105 registerGlobalAction(QStringLiteral("view_open_similar_categories"), actTmp);
106 /*TODO
107 WITH RECURSIVE
108 cat(t_fullname,id, level) AS (
109 SELECT t_fullname, id, 0 FROM v_category_display WHERE EXISTS (SELECT 1 FROM category p2 WHERE p2.id<>v_category_display.id AND upper(p2.t_fullname)=upper(v_category_display.t_fullname) AND p2.t_fullname<>v_category_display.t_fullname)
110 UNION ALL
111 SELECT v_category_display.t_name, v_category_display.id, cat.level+1
112 FROM v_category_display JOIN cat ON v_category_display.rd_category_id=cat.id
113 ORDER BY 2
114 ) SELECT substr('..........',1,level*3) || t_fullname FROM cat;
115 */
116 return true;
117 }
118
getNbDashboardWidgets()119 int SKGCategoriesPlugin::getNbDashboardWidgets()
120 {
121 SKGTRACEINFUNC(1)
122 return 4;
123 }
124
getDashboardWidgetTitle(int iIndex)125 QString SKGCategoriesPlugin::getDashboardWidgetTitle(int iIndex)
126 {
127 SKGTRACEINFUNC(1)
128 if (iIndex == 0) {
129 return i18nc("Report header", "5 main categories of expenditure");
130 }
131 if (iIndex == 1) {
132 return i18nc("Report header", "5 main variations");
133 }
134 if (iIndex == 2) {
135 return i18nc("Report header", "Budget");
136 }
137 return i18nc("Report header", "5 main variations (issues)");
138 }
139
getDashboardWidget(int iIndex)140 SKGBoardWidget* SKGCategoriesPlugin::getDashboardWidget(int iIndex)
141 {
142 SKGTRACEINFUNC(1)
143 // Get QML mode for dashboard
144 KConfigSkeleton* skl = SKGMainPanel::getMainPanel()->getPluginByName(QStringLiteral("Dashboard plugin"))->getPreferenceSkeleton();
145 KConfigSkeletonItem* sklItem = skl->findItem(QStringLiteral("qmlmode"));
146 bool qml = sklItem->property().toBool();
147
148 if (iIndex == 0) {
149 return new SKGHtmlBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument,
150 getDashboardWidgetTitle(iIndex),
151 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/html/default/categories_period_table.") % (qml ? QStringLiteral("qml") : QStringLiteral("html"))),
152 QStringList() << QStringLiteral("v_suboperation_consolidated"), SKGSimplePeriodEdit::ALL_PERIODS);
153 }
154 if (iIndex == 1) {
155 return new SKGHtmlBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument,
156 getDashboardWidgetTitle(iIndex) % " - %1",
157 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/html/default/categories_variations.") % (qml ? QStringLiteral("qml") : QStringLiteral("html"))),
158 QStringList() << QStringLiteral("v_suboperation_consolidated"), SKGSimplePeriodEdit::PREVIOUS_AND_CURRENT_PERIODS);
159 }
160 if (iIndex == 2) {
161 return new SKGHtmlBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument,
162 getDashboardWidgetTitle(iIndex) % " - %1",
163 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/html/default/budget_table.") % (qml ? QStringLiteral("qml") : QStringLiteral("html"))),
164 QStringList() << QStringLiteral("v_budget"), SKGSimplePeriodEdit::PREVIOUS_AND_CURRENT_MONTHS);
165 }
166 return new SKGHtmlBoardWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument,
167 getDashboardWidgetTitle(iIndex) % " - %1",
168 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/html/default/categories_variations_issues.") % (qml ? QStringLiteral("qml") : QStringLiteral("html"))),
169 QStringList() << QStringLiteral("v_suboperation_consolidated"), SKGSimplePeriodEdit::PREVIOUS_AND_CURRENT_PERIODS);
170 }
171
refresh()172 void SKGCategoriesPlugin::refresh()
173 {
174 SKGTRACEINFUNC(10)
175 if (m_currentBankDocument != nullptr) {
176 // Automatic categories creation
177 if (m_currentBankDocument->getMainDatabase() != nullptr) {
178 QString doc_id = m_currentBankDocument->getUniqueIdentifier();
179 if (m_docUniqueIdentifier != doc_id) {
180 m_docUniqueIdentifier = doc_id;
181
182 bool exist = false;
183 SKGError err = m_currentBankDocument->existObjects(QStringLiteral("category"), QString(), exist);
184 if (!err && !exist) {
185 importStandardCategories();
186
187 // The file is considered has not modified
188 m_currentBankDocument->setFileNotModified();
189 }
190 }
191 }
192 }
193 }
194
importCategories()195 void SKGCategoriesPlugin::importCategories()
196 {
197 SKGTRACEINFUNC(10)
198 SKGError err;
199 auto* act = qobject_cast< QAction* >(sender());
200 if (act != nullptr) {
201 QString fileName = act->data().toString();
202 QString name = QFileInfo(fileName).baseName().replace('_', ' ');
203 {
204 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Verb", "Import categories [%1]", name), err)
205
206 SKGImportExportManager imp(m_currentBankDocument, QUrl(fileName));
207 err = imp.importFile();
208 IFOKDO(err, m_currentBankDocument->removeMessages(m_currentBankDocument->getCurrentTransaction()))
209 }
210
211
212 // status
213 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Categories imported.")))
214 else {
215 err.addError(ERR_FAIL, i18nc("Error message", "Importing categories failed."));
216 }
217
218 // Display error
219 SKGMainPanel::displayErrorMessage(err);
220 }
221 }
222
importStandardCategories()223 void SKGCategoriesPlugin::importStandardCategories()
224 {
225 SKGTRACEINFUNC(10)
226 SKGError err;
227 {
228 QString cats = i18nc("List of categories. It is not needed to translate each item. You can set the list you want. ';' must be used to separate categories. ' > ' must be used to separate category and sub category (no limit of level).",
229 "Alimony;Auto;Auto > Fuel;Auto > Insurance;Auto > Lease;Auto > Loan;Auto > Registration;Auto > Service;Bank Charges;Bank Charges > Interest Paid;Bank Charges > Service Charge;Bills;Bills > Electricity;"
230 "Bills > Fuel Oil;Bills > Local Taxes;Bills > Mortgage;Bills > Natural Gas;Bills > Rent;Bills > TV;Bills > Telephone;Bills > Water & Sewage;Bonus;Business;Business > Auto;Business > Capital Goods;Business > Legal Expenses;Business > Office Rent;"
231 "Business > Office Supplies;Business > Other;Business > Revenue;Business > Taxes;Business > Travel;Business > Utilities;Business > Wages & Salary;Car;Car > Fuel;Car > Insurance;Car > Lease;Car > Loan;Car > Registration;Car > Service;"
232 "Cash Withdrawal;Charity;Charity > Donations;Child Care;Child Support;Clothing;Disability;Div Income;Div Income > Ord dividend;Div Income > Stock dividend;Education;Education > Board;Education > Books;Education > Fees;Education > Loans;"
233 "Education > Tuition;Employment;Employment > Benefits;Employment > Foreign;Employment > Lump sums;Employment > Other employ;Employment > Salary & wages;Food;Food > Dining Out;Food > Groceries;Gardening;"
234 "Gift Received;Gifts;Healthcare;Healthcare > Dental;Healthcare > Doctor;Healthcare > Hospital;Healthcare > Optician;Healthcare > Prescriptions;Holidays;Holidays > Accomodation;Holidays > Travel;Household;"
235 "Household > Furnishings;Household > Repairs;Insurance;Insurance > Auto;Insurance > Disability;Insurance > Home and Contents;Insurance > Life;Insurance > Medical;Int Inc;Int Inc > Bank Interest;Int Inc > Gross;Int Inc > Net;"
236 "Int Inc > Other savings;Invest. income;Invest. income > 1st option;Invest. income > Dividend;Invest. income > Foreign;Invest. income > Other savings;Invest. income > Other trusts;Invest. income > Other trusts#Capital;"
237 "Invest. income > Other trusts#Dist. rec'd;Invest. income > Other trusts#Estate;Investment Income;Investment Income > Dividends;Investment Income > Interest;Investment Income > Long-Term Capital Gains;"
238 "Investment Income > Short-Term Capital Gains;Investment Income > Tax-Exempt Interest;Job Expense;Job Expense > Non-Reimbursed;Job Expense > Reimbursed;Legal Fees;Leisure;Leisure > Books & Magazines;Leisure > Entertaining;"
239 "Leisure > Films & Video Rentals;Leisure > Hobbies;Leisure > Sporting Events;Leisure > Sports Goods;Leisure > Tapes & CDs;Leisure > Theatre & Concerts etc;Leisure > Toys & Games;Loan;Loan > Loan Interest;Long-Term Capital gains;Mortgage;Mortgage > Interest;Mortgage > PMI;Mortgage > Principle;Motor;Motor > Fuel;Motor > Loan;Motor > Service;Other Expense;Other Expense > Unknown;Other Income;Other Income > Child Support;"
240 "Other Income > Employee Share Option;Other Income > Gifts Received;Other Income > Loan Principal Received;Other Income > Lottery or Premium Bond Prizes;Other Income > Student loan;Other Income > Tax Refund;"
241 "Other Income > Unemployment Benefit;Pension;Pension > Employer;Personal Care;Pet Care;Pet Care > Food;Pet Care > Supplies;Pet Care > Vet's Bills;Recreation;Retirement Accounts;Retirement Accounts > 401(k)403(b) Plan Contributions;"
242 "Retirement Accounts > 529 Plan Contributions;Retirement Accounts > IRA Contributions;Retirement Income;Retirement Income > 401(k);Retirement Income > 401(k) > 403(b) Distributions;Retirement Income > IRA Distributions;"
243 "Retirement Income > Pensions & Annuities;Retirement Income > State Pension Benefits;Short-Term Capital gains;Social Security Benefits;Taxes;Taxes > AMT;Taxes > Federal Tax;Taxes > Federal Taxes;Taxes > Local Tax;Taxes > Local Taxes;"
244 "Taxes > Other Invest;Taxes > Other Tax;Taxes > Property Taxes;Taxes > Social Security;Taxes > State Tax;Taxes > State Taxes;Travel;Travel > Accomodations;Travel > Car Rental;Travel > Fares;Utilities;Utilities > Electricity;"
245 "Utilities > Garbage & Recycling;Utilities > Gas;Utilities > Sewer;Utilities > Telephone;Utilities > Water;Wages & Salary;Wages & Salary > Benefits;Wages & Salary > Bonus;Wages & Salary > Commission;"
246 "Wages & Salary > Employer Pension Contributions;Wages & Salary > Gross Pay;Wages & Salary > Net Pay;Wages & Salary > Overtime;Wages & Salary > Workman's Comp");
247
248 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Import standard categories"), err)
249
250 const auto items = SKGServices::splitCSVLine(cats, ';');
251 for (const auto& item : items) {
252 QString line = item.trimmed();
253 if (!line.isEmpty()) {
254 SKGCategoryObject cat;
255 err = SKGCategoryObject::createPathCategory(m_currentBankDocument, line, cat);
256 }
257 }
258 }
259
260
261 // status
262 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Categories imported.")))
263 else {
264 err.addError(ERR_FAIL, i18nc("Error message", "Importing categories failed."));
265 }
266
267 // Display error
268 SKGMainPanel::displayErrorMessage(err);
269 }
270
getWidget()271 SKGTabPage* SKGCategoriesPlugin::getWidget()
272 {
273 SKGTRACEINFUNC(10)
274 return new SKGCategoriesPluginWidget(SKGMainPanel::getMainPanel(), m_currentBankDocument);
275 }
276
title() const277 QString SKGCategoriesPlugin::title() const
278 {
279 return i18nc("Noun, categories of items", "Categories");
280 }
281
icon() const282 QString SKGCategoriesPlugin::icon() const
283 {
284 return QStringLiteral("view-categories");
285 }
286
toolTip() const287 QString SKGCategoriesPlugin::toolTip() const
288 {
289 return i18nc("A tool tip", "Categories management");
290 }
291
tips() const292 QStringList SKGCategoriesPlugin::tips() const
293 {
294 QStringList output;
295 output.push_back(i18nc("Description of a tips", "<p>... <a href=\"skg://skrooge_categories_plugin\">categories</a> can be reorganized by drag & drop.</p>"));
296 output.push_back(i18nc("Description of a tips", "<p>... if you delete a <a href=\"skg://skrooge_categories_plugin\">category</a>, all operations affected by this category will be associated to its parent category.</p>"));
297 return output;
298 }
299
getOrder() const300 int SKGCategoriesPlugin::getOrder() const
301 {
302 return 30;
303 }
304
isInPagesChooser() const305 bool SKGCategoriesPlugin::isInPagesChooser() const
306 {
307 return true;
308 }
309
advice(const QStringList & iIgnoredAdvice)310 SKGAdviceList SKGCategoriesPlugin::advice(const QStringList& iIgnoredAdvice)
311 {
312 SKGTRACEINFUNC(10)
313 SKGAdviceList output;
314 // Check unused categories
315 if (!iIgnoredAdvice.contains(QStringLiteral("skgcategoriesplugin_unused"))) {
316 bool exist = false;
317 m_currentBankDocument->existObjects(QStringLiteral("v_category_used2"), QStringLiteral("t_ISUSEDCASCADE='N'"), exist);
318 if (exist) {
319 SKGAdvice ad;
320 ad.setUUID(QStringLiteral("skgcategoriesplugin_unused"));
321 ad.setPriority(5);
322 ad.setShortMessage(i18nc("Advice on making the best (short)", "Many unused categories"));
323 ad.setLongMessage(i18nc("Advice on making the best (long)", "You can improve performances by removing categories that have no operations."));
324 SKGAdvice::SKGAdviceActionList autoCorrections;
325 {
326 SKGAdvice::SKGAdviceAction a;
327 a.Title = QStringLiteral("skg://clean_delete_unused_categories");
328 a.IsRecommended = true;
329 autoCorrections.push_back(a);
330 }
331 ad.setAutoCorrections(autoCorrections);
332 output.push_back(ad);
333 }
334 }
335
336 // Check operations not validated
337 if (!iIgnoredAdvice.contains(QStringLiteral("skgmonthlyplugin_maincategoriesvariation"))) {
338 QString month = QDate::currentDate().toString(QStringLiteral("yyyy-MM"));
339 QDate datepreviousmonth = QDate::currentDate().addDays(-QDate::currentDate().day());
340 QString previousmonth = datepreviousmonth.toString(QStringLiteral("yyyy-MM"));
341
342 QStringList listCategories;
343 QStringList listVariations = m_currentBankDocument->get5MainCategoriesVariationList(month, previousmonth, true, &listCategories);
344
345 int nb = listVariations.count();
346 SKGAdvice::SKGAdviceActionList autoCorrections;
347 for (int i = 0; i < nb; ++i) {
348 SKGAdvice ad;
349 ad.setUUID("skgmonthlyplugin_maincategoriesvariation|" % listCategories.at(i));
350 ad.setPriority(7);
351 ad.setShortMessage(i18nc("Advice on making the best (short)", "Important variation for '%1'", listCategories.at(i)));
352 ad.setLongMessage(listVariations.at(i));
353 autoCorrections.resize(0);
354 {
355 SKGAdvice::SKGAdviceAction a;
356 a.Title = i18nc("Advice on making the best (action)", "Open sub operations with category containing '%1'", listCategories.at(i));
357 a.IconName = QStringLiteral("quickopen");
358 a.IsRecommended = false;
359 autoCorrections.push_back(a);
360 }
361 ad.setAutoCorrections(autoCorrections);
362 output.push_back(ad);
363 }
364 }
365
366 // Check categories with different case
367 if (!iIgnoredAdvice.contains(QStringLiteral("skgcategoriesplugin_case"))) {
368 bool exist = false;
369 m_currentBankDocument->existObjects(QStringLiteral("category"), QStringLiteral("EXISTS (SELECT 1 FROM category p2 WHERE p2.id<>category.id AND upper(p2.t_fullname)=upper(category.t_fullname) AND p2.t_fullname<>category.t_fullname)"), exist);
370 if (exist) {
371 SKGAdvice ad;
372 ad.setUUID(QStringLiteral("skgcategoriesplugin_case"));
373 ad.setPriority(3);
374 ad.setShortMessage(i18nc("Advice on making the best (short)", "Some categories seem to be identical"));
375 ad.setLongMessage(i18nc("Advice on making the best (long)", "Some categories seem to be identical but with different syntax. They could be merged."));
376 SKGAdvice::SKGAdviceActionList autoCorrections;
377 {
378 SKGAdvice::SKGAdviceAction a;
379 a.Title = QStringLiteral("skg://view_open_similar_categories");
380 a.IsRecommended = false;
381 autoCorrections.push_back(a);
382 }
383 ad.setAutoCorrections(autoCorrections);
384 output.push_back(ad);
385 }
386 }
387 return output;
388 }
389
executeAdviceCorrection(const QString & iAdviceIdentifier,int iSolution)390 SKGError SKGCategoriesPlugin::executeAdviceCorrection(const QString& iAdviceIdentifier, int iSolution)
391 {
392 if ((m_currentBankDocument != nullptr) && iAdviceIdentifier.startsWith(QLatin1String("skgmonthlyplugin_maincategoriesvariation|"))) {
393 // Get parameters
394 QString category = iAdviceIdentifier.right(iAdviceIdentifier.length() - 41);
395 QString month = QDate::currentDate().toString(QStringLiteral("yyyy-MM"));
396
397 // Call operation plugin
398 SKGMainPanel::getMainPanel()->openPage("skg://skrooge_operation_plugin/SKGOPERATION_CONSOLIDATED_DEFAULT_PARAMETERS/?currentPage=-1&title_icon=" % icon() % "&operationTable=v_suboperation_consolidated&title=" %
399 SKGServices::encodeForUrl(i18nc("Noun, a list of items", "Sub operations with category containing '%1'", category)) % "&operationWhereClause=" % SKGServices::encodeForUrl("d_DATEMONTH='" % month % "' AND t_REALCATEGORY='" % SKGServices::stringToSqlString(category) % '\''));
400 return SKGError();
401 }
402 return SKGInterfacePlugin::executeAdviceCorrection(iAdviceIdentifier, iSolution);
403 }
404
deleteUnusedCategories() const405 void SKGCategoriesPlugin::deleteUnusedCategories() const
406 {
407 SKGError err;
408 _SKGTRACEINFUNCRC(10, err)
409 if (m_currentBankDocument != nullptr) {
410 SKGBEGINTRANSACTION(*m_currentBankDocument, i18nc("Noun, name of the user action", "Delete unused categories"), err)
411
412 QStringList categoriesUsed;
413 err = m_currentBankDocument->getDistinctValues(QStringLiteral("category"), QStringLiteral("t_fullname"), QStringLiteral("t_fullname in ("
414 "SELECT DISTINCT(category.t_fullname) FROM category, suboperation WHERE suboperation.r_category_id=category.id UNION ALL "
415 "SELECT DISTINCT(category.t_fullname) FROM category, budget WHERE budget.rc_category_id=category.id UNION ALL "
416 "SELECT DISTINCT(category.t_fullname) FROM category, budgetrule WHERE budgetrule.rc_category_id=category.id UNION ALL "
417 "SELECT DISTINCT(category.t_fullname) FROM category, budgetrule WHERE budgetrule.rc_category_id_target=category.id)"), categoriesUsed);
418
419 for (int i = 0; i < categoriesUsed.count(); ++i) { // Warning categoriesUsed is modified in the loop
420 QString cat = categoriesUsed.at(i);
421 categoriesUsed[i] = SKGServices::stringToSqlString(cat);
422 int pos = cat.lastIndexOf(OBJECTSEPARATOR);
423 if (pos != -1) {
424 categoriesUsed.push_back(cat.left(pos));
425 }
426 }
427
428 IFOK(err) {
429 QString sql;
430 if (!categoriesUsed.isEmpty()) {
431 sql = "DELETE FROM category WHERE t_fullname NOT IN ('" % categoriesUsed.join(QStringLiteral("','")) % "')";
432 } else {
433 sql = QStringLiteral("DELETE FROM category");
434 }
435 err = m_currentBankDocument->executeSqliteOrder(sql);
436 }
437 }
438
439 // status bar
440 IFOKDO(err, SKGError(0, i18nc("Successful message after an user action", "Unused categories deleted")))
441 else {
442 err.addError(ERR_FAIL, i18nc("Error message", "Unused categories deletion failed"));
443 }
444
445 // Display error
446 SKGMainPanel::displayErrorMessage(err);
447 }
448
449 #include <skgcategoriesplugin.moc>
450