File indexing completed on 2024-04-28 16:30:13
0001 /*************************************************************************** 0002 * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr 0003 * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 ***************************************************************************/ 0006 /** @file 0007 * A skrooge plugin for monthly report. 0008 * 0009 * @author Stephane MANKOWSKI 0010 */ 0011 #include "skgreportbank.h" 0012 0013 #include <qdir.h> 0014 #include <qstandardpaths.h> 0015 #include <qurl.h> 0016 0017 #include <kcolorscheme.h> 0018 0019 #include "skgaccountobject.h" 0020 #include "skgdocumentbank.h" 0021 #include "skgoperationobject.h" 0022 #include "skgrecurrentoperationobject.h" 0023 #include "skgruleobject.h" 0024 #include "skgtraces.h" 0025 #include "skgunitobject.h" 0026 0027 SKGReportBank::SKGReportBank(SKGDocument* iDocument) : SKGReport(iDocument) 0028 { 0029 SKGTRACEINFUNC(1) 0030 connect(this, &SKGReportBank::changed, this, &SKGReportBank::changed2); 0031 } 0032 0033 SKGReportBank::~SKGReportBank() 0034 { 0035 SKGTRACEINFUNC(1) 0036 } 0037 0038 QVariantList SKGReportBank::getAlarms() 0039 { 0040 QString cacheId = QStringLiteral("getAlarms"); 0041 QVariantList table = m_cache.value(cacheId).toList(); 0042 if (table.isEmpty()) { 0043 SKGTRACEINFUNC(10) 0044 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0045 if (doc != nullptr) { 0046 SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); 0047 0048 SKGObjectBase::SKGListSKGObjectBase rules; 0049 SKGError err = doc->getObjects(QStringLiteral("v_rule"), QStringLiteral("t_action_type='A' ORDER BY i_ORDER"), rules); 0050 int nb = rules.count(); 0051 if (nb != 0) { 0052 for (int i = 0; !err && i < nb; ++i) { 0053 SKGRuleObject rule(rules.at(i)); 0054 SKGRuleObject::SKGAlarmInfo alarm = rule.getAlarmInfo(); 0055 0056 QVariantList item; // clazy:exclude=container-inside-loop 0057 // Build the message 0058 if (alarm.Message.contains(QLatin1String("%3"))) { 0059 alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, primary, false), doc->formatMoney(alarm.Limit, primary, false), doc->formatMoney(alarm.Amount - alarm.Limit, primary, false)); 0060 } else if (alarm.Message.contains(QLatin1String("%2"))) { 0061 alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, primary, false), doc->formatMoney(alarm.Limit, primary, false)); 0062 } else if (alarm.Message.contains(QLatin1String("%1"))) { 0063 alarm.Message = alarm.Message.arg(doc->formatMoney(alarm.Amount, primary, false)); 0064 } 0065 item.push_back(alarm.Message); 0066 item.push_back(alarm.Amount); 0067 item.push_back(alarm.Limit); 0068 item.push_back(alarm.Amount - alarm.Limit); 0069 item.push_back(alarm.Raised); 0070 0071 table.push_back(item); 0072 } 0073 } 0074 m_cache[cacheId] = table; 0075 } 0076 } 0077 return table; 0078 } 0079 0080 QVariantList SKGReportBank::getInterests() 0081 { 0082 QString cacheId = QStringLiteral("getInterests"); 0083 QVariantList table = m_cache.value(cacheId).toList(); 0084 if (table.isEmpty()) { 0085 SKGTRACEINFUNC(10) 0086 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0087 if (doc != nullptr) { 0088 // Build display 0089 int year = SKGServices::periodToDate(getPeriod()).year(); 0090 SKGObjectBase::SKGListSKGObjectBase objs; 0091 auto sqlFilter = getSqlFilter(); 0092 if (sqlFilter.isEmpty()) { 0093 sqlFilter = QStringLiteral("1=1"); 0094 } 0095 SKGError err = doc->getObjects(QStringLiteral("v_account"), "(" % sqlFilter % QStringLiteral(") AND t_close='N' AND EXISTS(select 1 from interest where interest.rd_account_id=v_account.id) ORDER BY t_name"), objs); 0096 IFOK(err) { 0097 int nb = objs.count(); 0098 table.reserve(nb + 2); 0099 if (nb != 0) { 0100 { 0101 // Add header 0102 QVariantList item; 0103 item.push_back(false); 0104 item.push_back(i18nc("Title", "Account")); 0105 item.push_back(year); 0106 table.push_back(item); 0107 } 0108 0109 // Add items 0110 double sum = 0; 0111 for (int i = 0; i < nb; ++i) { 0112 SKGAccountObject obj(objs.at(i)); 0113 0114 SKGAccountObject::SKGInterestItemList oInterestList; 0115 double oInterests = 0; 0116 obj.getInterestItems(oInterestList, oInterests, year); 0117 sum += oInterests; 0118 0119 QVariantList item; // clazy:exclude=container-inside-loop 0120 item.push_back(false); 0121 item.push_back(obj.getName()); 0122 item.push_back(oInterests); 0123 table.push_back(item); 0124 } 0125 { 0126 // Add sum 0127 QVariantList item; 0128 item.push_back(true); 0129 item.push_back(i18nc("Noun, the numerical total of a sum of values", "Total")); 0130 item.push_back(sum); 0131 table.push_back(item); 0132 } 0133 } 0134 } 0135 0136 m_cache[cacheId] = table; 0137 } 0138 } 0139 return table; 0140 } 0141 0142 QVariantList SKGReportBank::getBudgetTable() 0143 { 0144 QString cacheId = QStringLiteral("getBudgetTable"); 0145 QVariantList table = m_cache.value(cacheId).toList(); 0146 if (table.isEmpty()) { 0147 SKGTRACEINFUNC(10) 0148 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0149 table = doc != nullptr ? doc->getBudget(getPeriod()) : QVariantList(); 0150 m_cache[cacheId] = table; 0151 } 0152 return table; 0153 } 0154 0155 QVariantList SKGReportBank::getPortfolio() 0156 { 0157 QString cacheId = QStringLiteral("getPortfolio"); 0158 QVariantList table = m_cache.value(cacheId).toList(); 0159 if (table.isEmpty()) { 0160 SKGTRACEINFUNC(10) 0161 QString period = getPeriod(); 0162 if (!period.isEmpty()) { 0163 QDate date = qMin(SKGServices::periodToDate(period), QDate::currentDate().addDays(1 - QDate::currentDate().day()).addMonths(1).addDays(-1)); 0164 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0165 if (doc != nullptr) { 0166 SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); 0167 0168 // Get list of transactions 0169 SKGObjectBase::SKGListSKGObjectBase objs; 0170 auto sqlFilter = getSqlFilter(); 0171 if (sqlFilter.isEmpty()) { 0172 sqlFilter = QStringLiteral("1=1"); 0173 } 0174 SKGError err = doc->getObjects(QStringLiteral("v_operation_display"), "d_date<'" % SKGServices::dateToSqlString(date) % "' AND rc_unit_id IN (SELECT id FROM v_unit_display WHERE t_type='S' AND (" % sqlFilter % ") AND f_QUANTITYOWNED>0.01) ORDER BY t_UNIT", objs); 0175 int nb = objs.count(); 0176 if (!err && nb > 0) { 0177 table.reserve(nb + 1); 0178 0179 QVariantList line; 0180 line << doc->getDisplay(QStringLiteral("t_symbol")) 0181 << doc->getDisplay(QStringLiteral("t_UNIT")) 0182 << i18nc("Column table title", "Quantity") 0183 << i18nc("Column table title", "Purchase amount") 0184 << i18nc("Column table title", "Initial amount") 0185 << QLocale().toString(date, QLocale::ShortFormat) 0186 << i18nc("Column table title", "Variation") 0187 << i18nc("Column table title", "Variation %"); 0188 table << QVariant(line); 0189 0190 QVector<unitValues> listUnitValues; 0191 unitValues current; 0192 current.initalAmount = 0.0; 0193 current.purchaseAmount = 0.0; 0194 current.currentAmount = 0.0; 0195 current.quantity = 0.0; 0196 listUnitValues.reserve(nb); 0197 for (int i = 0; i < nb; ++i) { 0198 SKGOperationObject obj(objs.at(i)); 0199 SKGUnitObject unit; 0200 obj.getUnit(unit); 0201 if (i != 0 && current.unit != unit) { 0202 listUnitValues.push_back(current); 0203 current.initalAmount = 0.0; 0204 current.purchaseAmount = 0.0; 0205 current.currentAmount = 0.0; 0206 current.quantity = 0.0; 0207 } 0208 0209 current.unit = unit; 0210 current.initalAmount += obj.getAmount(obj.getDate()); 0211 current.currentAmount += obj.getAmount(date); 0212 SKGObjectBase::SKGListSKGObjectBase oGroupedOperations; 0213 obj.getGroupedOperations(oGroupedOperations); 0214 oGroupedOperations.removeAll(obj); 0215 if (oGroupedOperations.count() == 1) { 0216 SKGOperationObject obj2(oGroupedOperations.at(0)); 0217 current.purchaseAmount += obj2.getAmount(obj.getDate()); 0218 } 0219 current.quantity += SKGServices::stringToDouble(obj.getAttribute(QStringLiteral("f_QUANTITY"))); 0220 } 0221 if (!current.unit.getName().isEmpty()) { 0222 listUnitValues.push_back(current); 0223 } 0224 0225 nb = listUnitValues.count(); 0226 for (int j = 0; j < nb; ++j) { 0227 unitValues current2 = listUnitValues.at(j); 0228 0229 SKGServices::SKGUnitInfo ui = current2.unit.getUnitInfo(); 0230 ui.Value = 1; 0231 0232 QVariantList line2; // clazy:exclude=container-inside-loop 0233 line2 << current2.unit.getSymbol() 0234 << current2.unit.getName() 0235 << doc->formatMoney(current2.quantity, ui, false) 0236 << current2.purchaseAmount 0237 << current2.initalAmount 0238 << current2.currentAmount 0239 << current2.currentAmount - current2.initalAmount 0240 << (current2.initalAmount == 0.0 ? 0.0 : 100.0 * (current2.currentAmount - current2.initalAmount) / current2.initalAmount); 0241 table << QVariant(line2); 0242 } 0243 } 0244 } 0245 } 0246 m_cache[cacheId] = table; 0247 } 0248 0249 return table; 0250 } 0251 0252 QVariantList SKGReportBank::getUnitTable() 0253 { 0254 QString cacheId = QStringLiteral("getUnitTable"); 0255 QVariantList table = m_cache.value(cacheId).toList(); 0256 if (table.isEmpty()) { 0257 SKGTRACEINFUNC(10) 0258 QString period = getPeriod(); 0259 if (!period.isEmpty()) { 0260 QDate date1 = SKGServices::periodToDate(getPreviousPeriod()); 0261 QDate date2 = SKGServices::periodToDate(period); 0262 0263 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0264 if (doc != nullptr) { 0265 SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); 0266 0267 SKGObjectBase::SKGListSKGObjectBase units; 0268 SKGError err = doc->getObjects(QStringLiteral("v_unit_display"), QStringLiteral("1=1 ORDER BY t_TYPENLS"), units); 0269 int nbUnits = units.count(); 0270 if (nbUnits != 0) { 0271 table.reserve(nbUnits + 1); 0272 QVariantList line; 0273 line << "sum" 0274 << doc->getDisplay(QStringLiteral("t_UNIT")) 0275 << QLocale().toString(date1, QLocale::ShortFormat) 0276 << QLocale().toString(date2, QLocale::ShortFormat) 0277 << "%" 0278 << doc->getDisplay(QStringLiteral("t_symbol")); 0279 table << QVariant(line); 0280 for (const auto& item : qAsConst(units)) { 0281 SKGUnitObject unit(item); 0282 double v1 = unit.getAmount(date1); 0283 double v2 = unit.getAmount(date2); 0284 QVariantList line2; // clazy:exclude=container-inside-loop 0285 line2 << false << unit.getName() << v1 << v2 << (100.0 * (v2 - v1) / qAbs(v1)) << unit.getSymbol(); 0286 table << QVariant(line2); 0287 } 0288 } 0289 } 0290 } 0291 m_cache[cacheId] = table; 0292 } 0293 0294 return table; 0295 } 0296 0297 QVariantList SKGReportBank::getAccountTable() 0298 { 0299 QString cacheId = QStringLiteral("getAccountTable"); 0300 QVariantList table = m_cache.value(cacheId).toList(); 0301 if (table.isEmpty()) { 0302 SKGTRACEINFUNC(10) 0303 QString period = getPeriod(); 0304 if (!period.isEmpty()) { 0305 QDate date1 = SKGServices::periodToDate(getPreviousPeriod()); 0306 QDate date2 = SKGServices::periodToDate(period); 0307 QDate date3 = date2.addYears(-1); 0308 if (date3 == date1) { 0309 date3 = date3.addYears(-1); 0310 } 0311 0312 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0313 if (doc != nullptr) { 0314 SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); 0315 0316 SKGObjectBase::SKGListSKGObjectBase accounts; 0317 auto sqlFilter = getSqlFilter(); 0318 if (sqlFilter.isEmpty()) { 0319 sqlFilter = QStringLiteral("1=1"); 0320 } 0321 SKGError err = doc->getObjects(QStringLiteral("v_account"), sqlFilter + QStringLiteral(" ORDER BY t_TYPENLS, t_BANK, t_name"), accounts); 0322 IFOK(err) { 0323 table.push_back(QVariantList() << "sum" << doc->getDisplay(QStringLiteral("t_ACCOUNT")) << QLocale().toString(date1, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << 0324 "%" << QLocale().toString(date3, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << "%"); 0325 double sumTypeV1 = 0; 0326 double sumTypeV2 = 0; 0327 double sumTypeV3 = 0; 0328 double sumV1 = 0; 0329 double sumV2 = 0; 0330 double sumV3 = 0; 0331 int nbAdded = 0; 0332 QString currentType; 0333 int nb = accounts.count(); 0334 for (int i = 0; !err && i < nb; ++i) { 0335 SKGAccountObject account(accounts.at(i)); 0336 double v1 = account.getAmount(date1); 0337 double v2 = account.getAmount(date2); 0338 double v3 = account.getAmount(date3); 0339 QString type = account.getAttribute(QStringLiteral("t_TYPENLS")); 0340 bool closed = account.isClosed(); 0341 if (type != currentType) { 0342 if (!currentType.isEmpty() && nbAdded > 0) { 0343 table.push_back(QVariantList() << true << i18nc("Noun", "Total of %1", currentType) << sumTypeV1 << sumTypeV2 << 0344 (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 << sumTypeV2 0345 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) 0346 << "" << ""); 0347 sumTypeV1 = 0; 0348 sumTypeV2 = 0; 0349 sumTypeV3 = 0; 0350 nbAdded = 0; 0351 } 0352 currentType = type; 0353 } 0354 if (!closed || qAbs(v1) > 0.01 || qAbs(v2) > 0.01 || qAbs(v3) > 0.01) { 0355 QString icon = account.getAttribute(QStringLiteral("t_ICON")); 0356 if (!icon.isEmpty()) { 0357 QString iconfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % icon); 0358 if (!iconfile.isEmpty()) { 0359 icon = iconfile; 0360 } 0361 } 0362 table.push_back(QVariantList() << false << account.getName() << v1 << v2 << (100.0 * (v2 - v1) / qAbs(v1)) << v3 << v2 0363 << (100.0 * (v2 - v3) / qAbs(v3)) << account.getAttribute(QStringLiteral("t_BANK")) << icon); 0364 nbAdded++; 0365 } 0366 sumTypeV1 += v1; 0367 sumTypeV2 += v2; 0368 sumTypeV3 += v3; 0369 sumV1 += v1; 0370 sumV2 += v2; 0371 sumV3 += v3; 0372 } 0373 0374 table.push_back(QVariantList() << true << i18nc("Noun", "Total of %1", currentType) << sumTypeV1 << sumTypeV2 0375 << (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) 0376 << "" << ""); 0377 0378 table.push_back(QVariantList() << true << i18nc("Noun, the numerical total of a sum of values", "Total") << sumV1 << sumV2 0379 << (100.0 * (sumV2 - sumV1) / qAbs(sumV1)) << sumV3 << sumV2 << (100.0 * (sumV2 - sumV3) / qAbs(sumV3)) 0380 << "" << ""); 0381 } 0382 } 0383 } 0384 m_cache[cacheId] = table; 0385 } 0386 return table; 0387 } 0388 0389 QVariantList SKGReportBank::getBankTable() 0390 { 0391 QString cacheId = QStringLiteral("getBankTable"); 0392 QVariantList table = m_cache.value(cacheId).toList(); 0393 if (table.isEmpty()) { 0394 SKGTRACEINFUNC(10) 0395 QString period = getPeriod(); 0396 if (!period.isEmpty()) { 0397 QDate date1 = SKGServices::periodToDate(getPreviousPeriod()); 0398 QDate date2 = SKGServices::periodToDate(period); 0399 QDate date3 = date2.addYears(-1); 0400 if (date3 == date1) { 0401 date3 = date3.addYears(-1); 0402 } 0403 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0404 if (doc != nullptr) { 0405 SKGServices::SKGUnitInfo primary = doc->getPrimaryUnit(); 0406 0407 SKGObjectBase::SKGListSKGObjectBase accounts; 0408 auto sqlFilter = getSqlFilter(); 0409 if (sqlFilter.isEmpty()) { 0410 sqlFilter = QStringLiteral("1=1"); 0411 } 0412 SKGError err = doc->getObjects(QStringLiteral("v_account"), sqlFilter + QStringLiteral(" ORDER BY t_BANK"), accounts); 0413 IFOK(err) { 0414 table.push_back(QVariantList() << "sum" << doc->getDisplay(QStringLiteral("t_BANK")) << QLocale().toString(date1, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << 0415 "%" << QLocale().toString(date3, QLocale::ShortFormat) << QLocale().toString(date2, QLocale::ShortFormat) << "%"); 0416 double sumTypeV1 = 0; 0417 double sumTypeV2 = 0; 0418 double sumTypeV3 = 0; 0419 double sumV1 = 0; 0420 double sumV2 = 0; 0421 double sumV3 = 0; 0422 QString currentName; 0423 QString currentIcon; 0424 bool currentOpen = false; 0425 int nb = accounts.count(); 0426 for (int i = 0; !err && i < nb; ++i) { 0427 SKGAccountObject account(accounts.at(i)); 0428 double v1 = account.getAmount(date1); 0429 double v2 = account.getAmount(date2); 0430 double v3 = account.getAmount(date3); 0431 QString name = account.getAttribute(QStringLiteral("t_BANK")); 0432 QString icon = account.getAttribute(QStringLiteral("t_ICON")); 0433 if (!icon.isEmpty()) { 0434 QString iconfile = QStandardPaths::locate(QStandardPaths::GenericDataLocation, "skrooge/images/logo/" % icon); 0435 if (!iconfile.isEmpty()) { 0436 icon = iconfile; 0437 } 0438 } 0439 bool open = !account.isClosed(); 0440 if (name != currentName) { 0441 if (!currentName.isEmpty() && currentOpen) { 0442 table.push_back(QVariantList() << false << currentName << sumTypeV1 << sumTypeV2 << 0443 (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) << 0444 currentIcon); 0445 sumTypeV1 = 0; 0446 sumTypeV2 = 0; 0447 sumTypeV3 = 0; 0448 currentOpen = open; 0449 } 0450 currentName = name; 0451 currentIcon = icon; 0452 } 0453 currentOpen = currentOpen || open; 0454 0455 sumTypeV1 += v1; 0456 sumTypeV2 += v2; 0457 sumTypeV3 += v3; 0458 sumV1 += v1; 0459 sumV2 += v2; 0460 sumV3 += v3; 0461 } 0462 0463 if (currentOpen) { 0464 table.push_back(QVariantList() << false << currentName << sumTypeV1 << sumTypeV2 0465 << (100.0 * (sumTypeV2 - sumTypeV1) / qAbs(sumTypeV1)) << sumTypeV3 0466 << sumTypeV2 << (100.0 * (sumTypeV2 - sumTypeV3) / qAbs(sumTypeV3)) 0467 << currentIcon); 0468 } 0469 0470 table.push_back(QVariantList() << true << i18nc("Noun, the numerical total of a sum of values", "Total") << sumV1 << sumV2 << (100.0 * (sumV2 - sumV1) / qAbs(sumV1)) << sumV3 << sumV2 << (100.0 * (sumV2 - sumV3) / qAbs(sumV3))); 0471 } 0472 } 0473 } 0474 m_cache[cacheId] = table; 0475 } 0476 return table; 0477 } 0478 0479 QVariantList SKGReportBank::getScheduledOperations() 0480 { 0481 QString cacheId = QStringLiteral("getScheduledOperations"); 0482 QVariantList table = m_cache.value(cacheId).toList(); 0483 if (table.isEmpty()) { 0484 SKGTRACEINFUNC(10) 0485 SKGObjectBase::SKGListSKGObjectBase objs; 0486 auto scheduled_operation_days_max = m_parameters.value(QStringLiteral("scheduled_operation_days_max"), QStringLiteral("30")).toString(); 0487 SKGError err = m_document->getObjects(QStringLiteral("v_recurrentoperation_display"), 0488 QStringLiteral("i_nb_times!=0 AND d_date<=date('now', 'localtime','+") + scheduled_operation_days_max + " day') ORDER BY d_date", objs); 0489 QDate d = QDate::currentDate().addDays(SKGServices::stringToInt(scheduled_operation_days_max)); 0490 QString dateFormatShort = QLocale().dateFormat(QLocale::ShortFormat); 0491 IFOK(err) { 0492 int nb = objs.count(); 0493 if (nb != 0) { 0494 table.reserve(nb); 0495 for (int i = 0; i < nb; ++i) { 0496 SKGRecurrentOperationObject obj(objs.at(i)); 0497 bool first = true; 0498 auto obj_date = obj.getDate().toString(dateFormatShort); 0499 while (true) { 0500 if (obj.getDate() > d || (obj.hasTimeLimit() && obj.getTimeLimit() == 0)) { 0501 break; 0502 } else { 0503 bool bold = false; 0504 if (obj.isWarnEnabled() && QDate::currentDate() >= obj.getDate().addDays(-obj.getWarnDays())) { 0505 bold = true; 0506 } 0507 auto name = obj.getDisplayName(); 0508 if (!first) { 0509 name = name.replace(obj_date, obj.getDate().toString(dateFormatShort)); 0510 } 0511 table.push_back(QVariantList() << bold << name << (first ? obj.getUniqueID() : QString()) << obj.getDate()); 0512 first = false; 0513 obj.setDate(obj.getNextDate()); 0514 if (obj.hasTimeLimit()) { 0515 obj.setTimeLimit(obj.getTimeLimit() - 1); 0516 } 0517 } 0518 } 0519 } 0520 } 0521 std::sort(table.begin(), table.end(), [](const QVariant & v1, const QVariant & v2) { 0522 return v1.toList().at(3).toDate() < v2.toList().at(3).toDate(); 0523 }); 0524 m_cache[cacheId] = table; 0525 } 0526 } 0527 return table; 0528 } 0529 0530 QVariantList SKGReportBank::getMainCategoriesForPeriod() 0531 { 0532 QString cacheId = QStringLiteral("getMainCategoriesForPeriod"); 0533 QVariantList table = m_cache.value(cacheId).toList(); 0534 if (table.isEmpty()) { 0535 SKGTRACEINFUNC(10) 0536 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0537 table = doc != nullptr ? doc->getMainCategories(getPeriod(), 5) : QVariantList(); 0538 m_cache[cacheId] = table; 0539 } 0540 return table; 0541 } 0542 0543 QVariantList SKGReportBank::getMainCategoriesForPreviousPeriod() 0544 { 0545 QString cacheId = QStringLiteral("getMainCategoriesForPreviousPeriod"); 0546 QVariantList table = m_cache.value(cacheId).toList(); 0547 if (table.isEmpty()) { 0548 SKGTRACEINFUNC(10) 0549 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0550 table = doc != nullptr ? doc->getMainCategories(getPreviousPeriod(), 5) : QVariantList(); 0551 m_cache[cacheId] = table; 0552 } 0553 return table; 0554 } 0555 0556 QStringList SKGReportBank::get5MainCategoriesVariation() 0557 { 0558 QString cacheId = QStringLiteral("get5MainCategoriesVariation"); 0559 QStringList table = m_cache.value(cacheId).toStringList(); 0560 if (table.isEmpty()) { 0561 SKGTRACEINFUNC(10) 0562 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0563 table = doc != nullptr ? doc->get5MainCategoriesVariationList(getPeriod(), getPreviousPeriod(), false) : QStringList(); 0564 m_cache[cacheId] = table; 0565 } 0566 return table; 0567 } 0568 0569 QStringList SKGReportBank::get5MainCategoriesVariationIssue() 0570 { 0571 QString cacheId = QStringLiteral("get5MainCategoriesVariationIssue"); 0572 QStringList table = m_cache.value(cacheId).toStringList(); 0573 if (table.isEmpty()) { 0574 SKGTRACEINFUNC(10) 0575 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0576 table = doc != nullptr ? doc->get5MainCategoriesVariationList(getPeriod(), getPreviousPeriod(), true) : QStringList(); 0577 m_cache[cacheId] = table; 0578 } 0579 return table; 0580 } 0581 0582 QVariantMap SKGReportBank::getPersonalFinanceScoreDetails(bool iTransfer, bool iTracker) 0583 { 0584 KColorScheme scheme(QPalette::Normal, KColorScheme::Window); 0585 0586 QVariantMap output; 0587 double pfs = getPersonalFinanceScore(iTransfer, iTracker); 0588 output[QStringLiteral("value")] = pfs; 0589 if (pfs < 0) { 0590 output[QStringLiteral("level")] = QStringLiteral("danger"); 0591 output[QStringLiteral("message")] = i18nc("An advice", "You must try to get out of debt."); 0592 output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NegativeText).color().name().right(6); 0593 } else if (pfs >= 25) { 0594 output[QStringLiteral("level")] = QStringLiteral("success"); 0595 output[QStringLiteral("message")] = i18nc("An advice", "Congratulations, you are now financially independent."); 0596 output[QStringLiteral("color")] = scheme.foreground(KColorScheme::PositiveText).color().name().right(6); 0597 } else if (pfs >= 10) { 0598 output[QStringLiteral("level")] = QStringLiteral("success"); 0599 output[QStringLiteral("message")] = i18nc("An advice", "Congratulations, You saved up ten year’s worth of expenses."); 0600 output[QStringLiteral("color")] = scheme.foreground(KColorScheme::PositiveText).color().name().right(6); 0601 } else if (pfs >= 2) { 0602 output[QStringLiteral("level")] = QStringLiteral("warning"); 0603 output[QStringLiteral("message")] = i18nc("An advice", "You saved up %1 year’s worth of expenses. You should continue your effort.", SKGServices::intToString(pfs)); 0604 output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NeutralText).color().name().right(6); 0605 } else if (pfs >= 1) { 0606 output[QStringLiteral("level")] = QStringLiteral("warning"); 0607 output[QStringLiteral("message")] = i18nc("An advice", "You saved up one year’s worth of expenses. You should maintain your effort."); 0608 output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NeutralText).color().name().right(6); 0609 } else { 0610 output[QStringLiteral("level")] = QStringLiteral("warning"); 0611 output[QStringLiteral("message")] = i18nc("An advice", "You do not have debt but you have no margin. You must maintain your effort."); 0612 output[QStringLiteral("color")] = scheme.foreground(KColorScheme::NeutralText).color().name().right(6); 0613 } 0614 return output; 0615 } 0616 0617 double SKGReportBank::getPersonalFinanceScore(bool iTransfer, bool iTracker) 0618 { 0619 double as = getAnnualSpending(iTransfer, iTracker); 0620 return (as == 0.0 ? 0.0 : getNetWorth(iTransfer, iTracker) / as); 0621 } 0622 0623 double SKGReportBank::getAnnualSpending(bool iTransfer, bool iTracker) 0624 { 0625 QString cacheId = QStringLiteral("getAnnualSpending-") % (iTransfer ? QStringLiteral("Y") : QStringLiteral("N")) % (iTracker ? QStringLiteral("Y") : QStringLiteral("N")); 0626 double output = m_cache.value(cacheId).toDouble(); 0627 if (!m_cache.contains(cacheId)) { 0628 SKGTRACEINFUNC(10) 0629 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0630 if (doc != nullptr) { 0631 QString result; 0632 QDate d2 = SKGServices::periodToDate(getPeriod()); 0633 QDate d1 = d2.addYears(-1); 0634 doc->executeSingleSelectSqliteOrder("SELECT TOTAL(f_REALCURRENTAMOUNT) FROM v_suboperation_consolidated WHERE d_date BETWEEN '" % 0635 SKGServices::dateToSqlString(d1) % "' AND '" % SKGServices::dateToSqlString(d2) % 0636 "' AND t_TYPEEXPENSE='-'" 0637 % (iTransfer ? "" : " AND t_TRANSFER='N'") 0638 % (iTracker ? "" : " AND t_REFUND=''") 0639 , result); 0640 output = -SKGServices::stringToDouble(result); 0641 m_cache[cacheId] = output; 0642 } 0643 } 0644 return output; 0645 } 0646 0647 double SKGReportBank::getNetWorth(bool iTransfer, bool iTracker) 0648 { 0649 QString cacheId = QStringLiteral("getNetWorth-") % (iTransfer ? QStringLiteral("Y") : QStringLiteral("N")) % (iTracker ? QStringLiteral("Y") : QStringLiteral("N")); 0650 double output = m_cache.value(cacheId).toDouble(); 0651 if (!m_cache.contains(cacheId)) { 0652 SKGTRACEINFUNC(10) 0653 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0654 if (doc != nullptr) { 0655 QString result; 0656 QDate d = SKGServices::periodToDate(getPeriod()); 0657 doc->executeSingleSelectSqliteOrder("SELECT TOTAL(f_REALCURRENTAMOUNT) FROM v_suboperation_consolidated WHERE d_date<='" % 0658 SKGServices::dateToSqlString(d) % "' " 0659 % (iTransfer ? "" : " AND t_TRANSFER='N'") 0660 % (iTracker ? "" : " AND t_REFUND=''") 0661 , result); 0662 output = SKGServices::stringToDouble(result); 0663 m_cache[cacheId] = output; 0664 } 0665 } 0666 return output; 0667 } 0668 0669 QVariantList SKGReportBank::getIncomeVsExpenditure(bool iOnSubOperation, bool iGrouped, bool iTransfer, bool iTracker, const QString& iWhereClause1, const QString& iWhereClause2) 0670 { 0671 QString cacheId = QStringLiteral("getIncomeVsExpenditure-") % (iOnSubOperation ? QStringLiteral("Y") : QStringLiteral("N")) % (iGrouped ? QStringLiteral("Y") : QStringLiteral("N")) % (iTransfer ? QStringLiteral("Y") : QStringLiteral("N")) % (iTracker ? QStringLiteral("Y") : QStringLiteral("N")) % iWhereClause1 % iWhereClause2; 0672 QVariantList table = m_cache.value(cacheId).toList(); 0673 if (table.isEmpty()) { 0674 SKGTRACEINFUNC(10) 0675 auto* doc = qobject_cast<SKGDocumentBank*>(m_document); 0676 if (doc != nullptr) { 0677 QString tableDb = (iOnSubOperation ? QStringLiteral("v_suboperation_consolidated") : QStringLiteral("v_operation_display")); 0678 QString amount = (iOnSubOperation ? QStringLiteral("f_REALCURRENTAMOUNT") : QStringLiteral("f_CURRENTAMOUNT")); 0679 0680 SKGStringListList listTmp; 0681 auto wc1 = iWhereClause1; 0682 if (wc1.isEmpty()) { 0683 wc1 = SKGServices::getPeriodWhereClause(getPeriod()); 0684 } 0685 auto wc2 = iWhereClause2; 0686 if (wc2.isEmpty()) { 0687 wc2 = SKGServices::getPeriodWhereClause(getPreviousPeriod()); 0688 } 0689 0690 auto sqlFilter = getSqlFilter(); 0691 if (sqlFilter.isEmpty()) { 0692 sqlFilter = QStringLiteral("1=1"); 0693 } 0694 SKGError err = doc->executeSelectSqliteOrder( 0695 "SELECT TOTAL(" % amount % "), '1' from " % tableDb % " WHERE " % wc1 % " AND " % sqlFilter 0696 % (iGrouped ? "" : " AND i_group_id=0") 0697 % (iTransfer ? "" : " AND t_TRANSFER='N'") 0698 % (iTracker ? "" : (iOnSubOperation ? " AND t_REALREFUND=''" : " AND t_REFUND=''")) 0699 % " group by t_TYPEEXPENSE " 0700 % " UNION ALL " 0701 % "SELECT TOTAL(" % amount % "), '2' from " % tableDb % " WHERE " % wc2 % " AND " % sqlFilter 0702 % (iGrouped ? "" : " AND i_group_id=0") 0703 % (iTransfer ? "" : " AND t_TRANSFER='N'") 0704 % (iTracker ? "" : (iOnSubOperation ? " AND t_REALREFUND=''" : " AND t_REFUND=''")) 0705 % " group by t_TYPEEXPENSE ", 0706 listTmp); 0707 IFOK(err) { 0708 double income_previous_period = 0; 0709 double expense_previous_period = 0; 0710 double income_period = 0; 0711 double expense_period = 0; 0712 0713 int nbval = listTmp.count(); 0714 for (int i = 1; i < nbval; ++i) { // Ignore header 0715 QString m = listTmp.at(i).at(1); 0716 double v = SKGServices::stringToDouble(listTmp.at(i).at(0)); 0717 if (v > 0 && m == QStringLiteral("1")) { 0718 income_period = v; 0719 } else if (v < 0 && m == QStringLiteral("1")) { 0720 expense_period = v; 0721 } else if (v > 0 && m == QStringLiteral("2")) { 0722 income_previous_period = v; 0723 } else if (v < 0 && m == QStringLiteral("2")) { 0724 expense_previous_period = v; 0725 } 0726 } 0727 0728 double saving_previous_period = income_previous_period + expense_previous_period; 0729 double saving_period = income_period + expense_period; 0730 0731 table.push_back(QVariantList() << QStringLiteral("sum") << QLatin1String("") << getPreviousPeriod() << getPeriod() << QStringLiteral("max")); 0732 table.push_back(QVariantList() << false << doc->getDisplay(QStringLiteral("f_CURRENTAMOUNT_INCOME")) << qAbs(income_previous_period) << qAbs(income_period)); 0733 table.push_back(QVariantList() << false << doc->getDisplay(QStringLiteral("f_CURRENTAMOUNT_EXPENSE")) << qAbs(expense_previous_period) << qAbs(expense_period)); 0734 table.push_back(QVariantList() << true << i18nc("Noun", "Savings possible") << saving_previous_period << saving_period); 0735 table.push_back(QVariantList() << true << i18nc("Noun", "Max") << qMax(qAbs(income_previous_period), qAbs(expense_previous_period)) << qMax(qAbs(income_period), qAbs(expense_period))); 0736 } 0737 m_cache[cacheId] = table; 0738 } 0739 } 0740 return table; 0741 } 0742 0743 void SKGReportBank::addItemsInMapping(QVariantHash& iMapping) 0744 { 0745 SKGReport::addItemsInMapping(iMapping); 0746 iMapping.insert(QStringLiteral("about_forumpage"), QStringLiteral("https://forum.kde.org/viewforum.php?f=210")); 0747 iMapping.insert(QStringLiteral("about_newspage"), QStringLiteral("https://skrooge.org/news")); 0748 iMapping.insert(QStringLiteral("about_operationpage"), QStringLiteral("skg://Skrooge_operation_plugin/")); 0749 iMapping.insert(QStringLiteral("about_accountpage"), QStringLiteral("skg://Skrooge_bank_plugin/")); 0750 iMapping.insert(QStringLiteral("about_importurl"), QStringLiteral("skg://import_operation/")); 0751 iMapping.insert(QStringLiteral("about_maintext"), i18nc("The main text of skrooge", 0752 "Skrooge allows you to keep a hold on your expenses, by tracking and budgeting them.<br/>" 0753 "What should you do now ?<br/>" 0754 "<ul>" 0755 "<li>Create at least one <a href=\"%1\">account</a></li>" 0756 "<li>Add some transactions, using <a href=\"%3\">import</a> or <a href=\"%2\">manual input</a></li>" 0757 "<li>Categorize them</li>" 0758 "</ul>" 0759 "<p>You may come back to this page any time by closing all tabs.<br/>" 0760 "For more information about using Skrooge, check the <a href=\"https://skrooge.org\">Skrooge website</a>.</p>" 0761 "<p>We hope that you will enjoy Skrooge</p>" 0762 " The Skrooge Team", 0763 iMapping[QStringLiteral("about_accountpage")].toString(), iMapping[QStringLiteral("about_operationpage")].toString(), iMapping[QStringLiteral("about_importurl")].toString())); 0764 iMapping.insert(QStringLiteral("logo"), QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("icons/breeze/apps/48/skrooge.svg"))).url()); 0765 iMapping.insert(QStringLiteral("logo_black"), QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("icons/breeze/apps/48/skrooge-black.svg"))).url()); 0766 0767 iMapping.insert(QStringLiteral("title_main"), i18nc("A monthly report title", "Report for %1", getPeriod())); 0768 iMapping.insert(QStringLiteral("title_budget"), i18nc("A monthly report title", "Budget")); 0769 iMapping.insert(QStringLiteral("title_main_categories"), i18nc("A monthly report title", "5 main categories of expenditure")); 0770 iMapping.insert(QStringLiteral("title_variations"), i18nc("A monthly report title", "5 main variations")); 0771 iMapping.insert(QStringLiteral("title_account"), i18nc("A monthly report title", "Amounts in accounts")); 0772 iMapping.insert(QStringLiteral("title_unit"), i18nc("A monthly report title", "Amounts of units")); 0773 iMapping.insert(QStringLiteral("title_advice"), i18nc("A monthly report title", "Advice")); 0774 iMapping.insert(QStringLiteral("title_portfolio"), i18nc("A monthly report title", "Stock portfolio")); 0775 iMapping.insert(QStringLiteral("title_interests"), i18nc("A monthly report title", "Estimated interests")); 0776 iMapping.insert(QStringLiteral("title_alarms"), i18nc("A monthly report title", "Alarms")); 0777 iMapping.insert(QStringLiteral("title_highlighted"), i18nc("A monthly report title", "Highlighted transactions")); 0778 iMapping.insert(QStringLiteral("title_networth"), i18nc("A monthly report title", "Net Worth")); 0779 iMapping.insert(QStringLiteral("title_annual_spending"), i18nc("A monthly report title", "Annual Spending")); 0780 iMapping.insert(QStringLiteral("title_personal_finance_score"), i18nc("A monthly report title", "Personal Finance Score")); 0781 0782 iMapping.insert(QStringLiteral("msg_no_variation"), i18nc("A monthly report message", "No variation found.")); 0783 iMapping.insert(QStringLiteral("msg_no_scheduled"), i18nc("A monthly report message", R"(No scheduled transactions defined on the "<a href="%1">Scheduled transactions</a>" page.)", "skg://Skrooge_scheduled_plugin/")); 0784 iMapping.insert(QStringLiteral("msg_no_highlighted"), i18nc("A monthly report message", R"(No highlighted transactions defined on the "<a href="%1">Transactions</a>" page.)", "skg://Skrooge_operation_plugin/")); 0785 iMapping.insert(QStringLiteral("msg_no_budget"), i18nc("A monthly report message", R"(No budget defined on the "<a href="%1">Budget</a>" page.)", "skg://Skrooge_budget_plugin/")); 0786 iMapping.insert(QStringLiteral("msg_no_share"), i18nc("A monthly report message", R"(No share defined on the "<a href="%1">Unit</a>" page.)", "skg://Skrooge_unit_plugin/")); 0787 iMapping.insert(QStringLiteral("msg_amount_unit_date"), i18nc("A monthly report message", "All amounts are calculated using the unit rates of the last day of the corresponding period.")); 0788 }