File indexing completed on 2024-06-16 04:47:23

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  * This file is Skrooge plugin for XHB import / export.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgimportpluginxhb.h"
0012 
0013 #include <kcompressiondevice.h>
0014 #include <klocalizedstring.h>
0015 #include <kpluginfactory.h>
0016 
0017 #include "skgbankincludes.h"
0018 #include "skgimportexportmanager.h"
0019 #include "skgobjectbase.h"
0020 #include "skgservices.h"
0021 #include "skgtraces.h"
0022 #include "skgtrackerobject.h"
0023 
0024 /**
0025  * This plugin factory.
0026  */
0027 K_PLUGIN_CLASS_WITH_JSON(SKGImportPluginXhb, "metadata.json")
0028 
0029 SKGImportPluginXhb::SKGImportPluginXhb(QObject* iImporter, const QVariantList& iArg)
0030     : SKGImportPlugin(iImporter)
0031 {
0032     SKGTRACEINFUNC(10)
0033     Q_UNUSED(iArg)
0034 }
0035 
0036 SKGImportPluginXhb::~SKGImportPluginXhb()
0037     = default;
0038 
0039 bool SKGImportPluginXhb::isImportPossible()
0040 {
0041     SKGTRACEINFUNC(10)
0042     return (m_importer->getDocument() == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("XHB"));
0043 }
0044 
0045 SKGError SKGImportPluginXhb::importFile()
0046 {
0047     if (m_importer->getDocument() == nullptr) {
0048         return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
0049     }
0050     SKGError err;
0051     SKGTRACEINFUNCRC(2, err)
0052 
0053     // Initialisation
0054     // Open file
0055     KCompressionDevice file(m_importer->getLocalFileName(), KCompressionDevice::GZip);
0056     if (!file.open(QIODevice::ReadOnly)) {
0057         err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().toDisplayString()));
0058     } else {
0059         QDomDocument doc;
0060 
0061         // Set the file without uncompression
0062         QString errorMsg;
0063         int errorLine = 0;
0064         int errorCol = 0;
0065         bool contentOK = doc.setContent(file.readAll(), &errorMsg, &errorLine, &errorCol);
0066         file.close();
0067 
0068         // Get root
0069         QDomElement docElem = doc.documentElement();
0070 
0071         if (!contentOK) {
0072             err.setReturnCode(ERR_ABORT).setMessage(i18nc("Error message",  "%1-%2: '%3'", errorLine, errorCol, errorMsg));
0073         }
0074         if (!contentOK) {
0075             err.addError(ERR_INVALIDARG, i18nc("Error message",  "Invalid XML content in file '%1'", m_importer->getFileName().toDisplayString()));
0076         } else {
0077             err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "XHB"), 4);
0078 
0079             QMap<QString, SKGAccountObject> mapIdAccount;
0080             QMap<QString, SKGCategoryObject> mapIdCategory;
0081             QMap<QString, SKGPayeeObject> mapIdPayee;
0082 
0083             SKGUnitObject unit;
0084             IFOKDO(err, m_importer->getDefaultUnit(unit))
0085 
0086             // Step 1-Create accounts
0087             IFOK(err) {
0088                 QDomNodeList accountList = docElem.elementsByTagName(QStringLiteral("account"));
0089                 int nb = accountList.count();
0090                 err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import accounts"), nb);
0091                 for (int i = 0; !err && i < nb; ++i) {
0092                     // Get account object
0093                     QDomElement account = accountList.at(i).toElement();
0094 
0095                     // Create the bank
0096                     SKGBankObject bank(m_importer->getDocument());
0097                     QString bname = getAttribute(account,  QStringLiteral("bankname"));
0098                     if (bname.isEmpty()) {
0099                         bname = QStringLiteral("HOMEBANK");
0100                     }
0101                     IFOKDO(err, bank.setName(bname))
0102                     IFOKDO(err, bank.save())
0103 
0104                     // Creation of the account
0105                     SKGAccountObject accountObj;
0106                     IFOKDO(err, bank.addAccount(accountObj))
0107                     IFOKDO(err, accountObj.setName(getAttribute(account,  QStringLiteral("name"))))
0108                     IFOKDO(err, accountObj.setNumber(getAttribute(account,  QStringLiteral("number"))))
0109                     IFOKDO(err, accountObj.setMinLimitAmount(SKGServices::stringToDouble(getAttribute(account,  QStringLiteral("minimum")))))
0110                     IFOKDO(err, accountObj.minLimitAmountEnabled(accountObj.getMinLimitAmount() != 0.0))
0111                     QString type = getAttribute(account,  QStringLiteral("type"));
0112                     IFOKDO(err, accountObj.setType(type == QStringLiteral("2") ? SKGAccountObject::WALLET : (type == QStringLiteral("3") ? SKGAccountObject::ASSETS : (type == QStringLiteral("4") ? SKGAccountObject::CREDITCARD : (type == QStringLiteral("5") ? SKGAccountObject::LOAN : SKGAccountObject::CURRENT)))))
0113                     IFOK(err) {
0114                         QString flags = getAttribute(account,  QStringLiteral("flags"));
0115                         err = accountObj.setClosed(flags == QStringLiteral("2") ||  flags == QStringLiteral("3"));
0116                     }
0117                     IFOKDO(err, accountObj.save())
0118 
0119                     // Change parent bank in case of ASSETS
0120                     if (accountObj.getType() == SKGAccountObject::WALLET) {
0121                         // Get blank bank
0122                         SKGBankObject blankBank(m_importer->getDocument());
0123                         IFOKDO(err, blankBank.setName(QString()))
0124                         if (blankBank.exist()) {
0125                             err = blankBank.load();
0126                         } else {
0127                             err = blankBank.save();
0128                         }
0129                         IFOKDO(err, accountObj.setBank(blankBank))
0130                         IFOKDO(err, accountObj.save())
0131                     }
0132 
0133                     // Set initial balance
0134                     IFOKDO(err, accountObj.setInitialBalance(SKGServices::stringToDouble(getAttribute(account,  QStringLiteral("initial"))), unit))
0135 
0136                     mapIdAccount[getAttribute(account,  QStringLiteral("key"))] = accountObj;
0137 
0138                     IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0139                 }
0140 
0141                 SKGENDTRANSACTION(m_importer->getDocument(),  err)
0142             }
0143             IFOKDO(err, m_importer->getDocument()->stepForward(1))
0144 
0145             // Step 2-Get payees
0146             IFOK(err) {
0147                 QDomNodeList partyList = docElem.elementsByTagName(QStringLiteral("pay"));
0148                 int nb = partyList.count();
0149                 err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import payees"), nb);
0150                 for (int i = 0; !err && i < nb; ++i) {
0151                     // Get payee object
0152                     QDomElement party = partyList.at(i).toElement();
0153                     SKGPayeeObject payeeObject;
0154                     err = SKGPayeeObject::createPayee(m_importer->getDocument(), getAttribute(party,  QStringLiteral("name")), payeeObject);
0155                     mapIdPayee[getAttribute(party,  QStringLiteral("key"))] = payeeObject;
0156 
0157                     IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0158                 }
0159 
0160                 SKGENDTRANSACTION(m_importer->getDocument(),  err)
0161             }
0162             IFOKDO(err, m_importer->getDocument()->stepForward(2))
0163 
0164             // Step 3-Create categories
0165             IFOK(err) {
0166                 QDomNodeList categoryList = docElem.elementsByTagName(QStringLiteral("cat"));
0167                 int nb = categoryList.count();
0168                 err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import categories"), 2 * nb);
0169                 for (int j = 0; !err && j < 2; ++j) {
0170                     for (int i = 0; !err && i < nb; ++i) {
0171                         // Get account object
0172                         QDomElement category = categoryList.at(i).toElement();
0173 
0174                         // Creation of the category
0175                         bool catCreated = false;
0176                         SKGCategoryObject catObj(m_importer->getDocument());
0177                         QString parent2 = getAttribute(category,  QStringLiteral("parent"));
0178                         if (parent2.isEmpty()) {
0179                             parent2 = '0';
0180                         }
0181                         if (parent2 == QStringLiteral("0") && j == 0) {
0182                             IFOKDO(err, catObj.setName(getAttribute(category,  QStringLiteral("name"))))
0183                             IFOKDO(err, catObj.save())
0184 
0185                             mapIdCategory[getAttribute(category,  QStringLiteral("key"))] = catObj;
0186                             catCreated = true;
0187 
0188                         } else if (parent2 != QStringLiteral("0") && j == 1) {
0189                             SKGCategoryObject catParentObj = mapIdCategory[parent2];
0190                             IFOKDO(err, catObj.setName(getAttribute(category,  QStringLiteral("name"))))
0191                             IFOKDO(err, catObj.setParentCategory(catParentObj))
0192                             IFOKDO(err, catObj.save())
0193 
0194                             mapIdCategory[getAttribute(category,  QStringLiteral("key"))] = catObj;
0195                             catCreated = true;
0196                         }
0197 
0198                         // Creation of the attached budget
0199                         if (!err && catCreated) {
0200                             QString b0 = getAttribute(category,  QStringLiteral("b0"));
0201                             QString b1 = getAttribute(category,  QStringLiteral("b1"));
0202                             if (!b0.isEmpty() || !b1.isEmpty()) {
0203                                 for (int m = 1; !err && m <= 12; ++m) {
0204                                     SKGBudgetObject budget(m_importer->getDocument());
0205 
0206                                     int year = QDate::currentDate().year();
0207                                     IFOKDO(err, budget.setCategory(catObj))
0208                                     IFOKDO(err, budget.setYear(year))
0209                                     IFOKDO(err, budget.setMonth(m))
0210                                     IFOKDO(err, budget.setBudgetedAmount(SKGServices::stringToDouble(getAttribute(category,  'b' % SKGServices::intToString(b0.isEmpty() ? m : 0)))))
0211                                     IFOKDO(err, budget.save())
0212                                 }
0213                             }
0214                         }
0215 
0216                         IFOKDO(err, m_importer->getDocument()->stepForward(nb * j + i + 1))
0217                     }
0218                 }
0219 
0220                 SKGENDTRANSACTION(m_importer->getDocument(),  err)
0221             }
0222             IFOKDO(err, m_importer->getDocument()->stepForward(3))
0223 
0224             // Step 4-Create transaction
0225             IFOK(err) {
0226                 err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import transactions"), 2);
0227                 for (int j = 1; !err && j <= 2; ++j) {
0228                     QDomNodeList transactionList = docElem.elementsByTagName(j == 1 ? QStringLiteral("ope") : QStringLiteral("fav"));
0229                     int nb = transactionList.count();
0230                     err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import transactions"), nb);
0231                     for (int i = 0; !err && i < nb; ++i) {
0232                         // Get account object
0233                         QDomElement transaction = transactionList.at(i).toElement();
0234 
0235                         // Get attributes
0236                         QString idAccount = getAttribute(transaction,  QStringLiteral("account"));
0237                         // QString dst_account = getAttribute(transaction,  QStringLiteral("dst_account"));
0238                         if (idAccount != QStringLiteral("0")) {
0239                             SKGAccountObject account = mapIdAccount[idAccount];
0240                             QDate date = QDate(1, 1, 1).addDays(1 + SKGServices::stringToInt(getAttribute(transaction,  j == 1 ? QStringLiteral("date") : QStringLiteral("nextdate"))));
0241                             QString mode = getAttribute(transaction,  QStringLiteral("paymode"));
0242                             if (mode == QStringLiteral("1")) {
0243                                 mode = i18nc("Noun: type of payement", "Credit card");
0244                             } else if (mode == QStringLiteral("2")) {
0245                                 mode = i18nc("Noun: type of payement", "Check");
0246                             } else if (mode == QStringLiteral("3")) {
0247                                 mode = i18nc("Noun: type of payement", "Cash");
0248                             } else if (mode == QStringLiteral("4") || mode == QStringLiteral("5")) {
0249                                 mode = i18nc("Noun: type of payement", "Transfer");
0250                             } else {
0251                                 mode = i18nc("Noun: type of payement", "Other");
0252                             }
0253                             QString comment = QString(getAttribute(transaction,  QStringLiteral("wording")) % ' ' % getAttribute(transaction,  QStringLiteral("info"))).trimmed();
0254                             int flags = SKGServices::stringToInt(getAttribute(transaction,  QStringLiteral("flags")));
0255                             double amount = SKGServices::stringToDouble(getAttribute(transaction,  QStringLiteral("amount")));
0256                             SKGOperationObject::OperationStatus status = (flags == 1 ||  flags == 3 ? SKGOperationObject::CHECKED : SKGOperationObject::NONE);
0257                             SKGPayeeObject payee = mapIdPayee[ getAttribute(transaction,  QStringLiteral("payee"))];
0258                             SKGCategoryObject category = mapIdCategory[getAttribute(transaction,  QStringLiteral("category"))];
0259 
0260                             // Creation of the transaction
0261                             SKGOperationObject opObj;
0262                             IFOKDO(err, account.addOperation(opObj, true))
0263                             IFOKDO(err, opObj.setDate(date))
0264                             IFOKDO(err, opObj.setUnit(unit))
0265                             IFOKDO(err, opObj.setPayee(payee))
0266                             IFOKDO(err, opObj.setMode(mode))
0267 
0268                             IFOKDO(err, opObj.setComment(comment))
0269                             IFOKDO(err, opObj.setTemplate(j == 2))
0270                             IFOKDO(err, opObj.setImported(true))
0271                             IFOKDO(err, opObj.setImportID("HXB-" % SKGServices::intToString(i)))
0272                             IFOKDO(err, opObj.setStatus(status))
0273                             IFOKDO(err, opObj.save())
0274 
0275                             SKGSubOperationObject subObj;
0276                             IFOKDO(err, opObj.addSubOperation(subObj))
0277                             IFOKDO(err, subObj.setCategory(category))
0278                             IFOKDO(err, subObj.setComment(comment))
0279                             IFOKDO(err, subObj.setQuantity(amount))
0280                             IFOKDO(err, subObj.save())
0281 
0282                             QString tags = getAttribute(transaction,  QStringLiteral("tags"));
0283                             if(!tags.isEmpty()) {
0284                                 SKGTrackerObject tracker;
0285                                 IFOKDO(err, SKGTrackerObject::createTracker(m_importer->getDocument(), tags, tracker))
0286                                 IFOKDO(err, subObj.setTracker(tracker))
0287                                 IFOKDO(err, subObj.save())
0288                             }
0289 
0290                             // Transfer
0291                             /*if(dst_account != idAccount && dst_account != "0") {
0292                                 SKGAccountObject account = mapIdAccount[dst_account];
0293                                 SKGOperationObject op2Obj;
0294                                 if(!err) err = account.addOperation(op2Obj, true);
0295                                 if(!err) err = op2Obj.setDate(date);
0296                                 if(!err) err = op2Obj.setUnit(unit);
0297                                 if(!err) err = op2Obj.setPayee(payee);
0298                                 if(!err) err = op2Obj.setMode(mode);
0299 
0300                                 if(!err) err = op2Obj.setComment(comment);
0301                                 if(!err) err = op2Obj.setTemplate(j == 2);
0302                                 if(!err) err = op2Obj.setImported(true);
0303                                 if(!err) err = op2Obj.setImportID("HXB-" % SKGServices::intToString(i) % "_TR");
0304                                 if(!err) err = op2Obj.setStatus(status);
0305                                 if(!err) err = op2Obj.save();
0306 
0307                                 SKGSubOperationObject subObj;
0308                                 if(!err) err = op2Obj.addSubOperation(subObj);
0309                                 if(!err) err = subObj.setCategory(category);
0310                                 if(!err) err = subObj.setComment(comment);
0311                                 if(!err) err = subObj.setQuantity(-amount);
0312                                 if(!err) err = subObj.save();
0313 
0314                                 if(!err) err = op2Obj.setGroupOperation(opObj);
0315                                 if(!err) err = op2Obj.save();
0316                             }*/
0317 
0318                             // Scheduled transactions
0319                             if (j == 2) {
0320                                 // Creation of scheduled transaction
0321                                 SKGRecurrentOperationObject recu;
0322                                 IFOKDO(err, opObj.addRecurrentOperation(recu))
0323                                 int limit = SKGServices::stringToInt(getAttribute(transaction,  QStringLiteral("limit")));
0324                                 IFOKDO(err, recu.timeLimit(flags & (1 << 7)))
0325                                 IFOKDO(err, recu.setTimeLimit(limit))
0326                                 int punit = SKGServices::stringToInt(getAttribute(transaction,  QStringLiteral("unit")));
0327                                 int pstep = SKGServices::stringToInt(getAttribute(transaction,  QStringLiteral("every")));
0328                                 SKGRecurrentOperationObject::PeriodUnit p = SKGRecurrentOperationObject::MONTH;
0329                                 if (punit == 0) {
0330                                     p = SKGRecurrentOperationObject::DAY;
0331                                 } else if (punit == 1) {
0332                                     p = SKGRecurrentOperationObject::WEEK;
0333                                 } else if (punit == 3) {
0334                                     p = SKGRecurrentOperationObject::YEAR;
0335                                 }
0336                                 IFOKDO(err, recu.setPeriodIncrement(pstep))
0337                                 IFOKDO(err, recu.setPeriodUnit(p))
0338                                 IFOKDO(err, recu.warnEnabled(flags & (1 << 5)))
0339                                 IFOKDO(err, recu.autoWriteEnabled(flags & (1 << 2)))
0340                                 IFOKDO(err, recu.save())
0341                                 // #define OF_VALID        (1<<0)
0342                                 // #define OF_INCOME       (1<<1)
0343                                 // #define OF_AUTO         (1<<2)
0344                                 // #define OF_ADDED        (1<<3)
0345                                 // #define OF_CHANGED      (1<<4)
0346                                 // #define OF_REMIND       (1<<5)
0347                                 // #define OF_CHEQ2        (1<<6)
0348                                 // #define OF_LIMIT        (1<<7)
0349                             }
0350                         }
0351 
0352                         if (!err && i % 500 == 0) {
0353                             err = m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE"));
0354                         }
0355                         IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0356                     }
0357 
0358                     SKGENDTRANSACTION(m_importer->getDocument(),  err)
0359                     IFOKDO(err, m_importer->getDocument()->stepForward(j))
0360                 }
0361 
0362                 SKGENDTRANSACTION(m_importer->getDocument(),  err)
0363             }
0364             IFOKDO(err, m_importer->getDocument()->stepForward(4))
0365 
0366             SKGENDTRANSACTION(m_importer->getDocument(),  err)
0367 
0368             IFOKDO(err, m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")))
0369         }
0370     }
0371 
0372     return err;
0373 }
0374 
0375 QString SKGImportPluginXhb::getAttribute(const QDomElement& iElement, const QString& iAttribute)
0376 {
0377     QString val = iElement.attribute(iAttribute);
0378     if (val == QStringLiteral("(null)")) {
0379         val = QString();
0380     }
0381     return val;
0382 }
0383 
0384 QString SKGImportPluginXhb::getMimeTypeFilter() const
0385 {
0386     return "*.xhb|" % i18nc("A file format", "Homebank document");
0387 }
0388 
0389 #include <skgimportpluginxhb.moc>