File indexing completed on 2024-06-23 05:03:12

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 GNC import / export.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgimportplugingnc.h"
0012 
0013 #include <kcompressiondevice.h>
0014 #include <klocalizedstring.h>
0015 #include <kpluginfactory.h>
0016 
0017 #include <qdom.h>
0018 
0019 #include <cmath>
0020 
0021 #include "skgbankincludes.h"
0022 #include "skgimportexportmanager.h"
0023 #include "skgobjectbase.h"
0024 #include "skgpayeeobject.h"
0025 #include "skgservices.h"
0026 #include "skgtraces.h"
0027 
0028 /**
0029  * This plugin factory.
0030  */
0031 K_PLUGIN_CLASS_WITH_JSON(SKGImportPluginGnc, "metadata.json")
0032 
0033 SKGImportPluginGnc::SKGImportPluginGnc(QObject* iImporter, const QVariantList& iArg)
0034     : SKGImportPlugin(iImporter)
0035 {
0036     SKGTRACEINFUNC(10)
0037     Q_UNUSED(iArg)
0038 }
0039 
0040 SKGImportPluginGnc::~SKGImportPluginGnc()
0041     = default;
0042 
0043 bool SKGImportPluginGnc::isImportPossible()
0044 {
0045     SKGTRACEINFUNC(10)
0046     if (m_importer->getDocument() == nullptr) {
0047         return true;
0048     }
0049     QString extension = m_importer->getFileNameExtension();
0050     return (extension == QStringLiteral("UNCOMPRESSED") || extension == QStringLiteral("GNUCASH") || extension == QStringLiteral("GNC"));
0051 }
0052 
0053 SKGError SKGImportPluginGnc::importFile()
0054 {
0055     if (m_importer->getDocument() == nullptr) {
0056         return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
0057     }
0058     SKGError err;
0059     SKGTRACEINFUNCRC(2, err)
0060 
0061     // Open file
0062     KCompressionDevice file(m_importer->getLocalFileName(), KCompressionDevice::GZip);
0063     if (!file.open(QIODevice::ReadOnly)) {
0064         err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().toDisplayString()));
0065     } else {
0066         QDomDocument doc;
0067 
0068         // Set the file without uncompression
0069         QString errorMsg;
0070         int errorLine = 0;
0071         int errorCol = 0;
0072         bool contentOK = doc.setContent(file.readAll(), &errorMsg, &errorLine, &errorCol);
0073         file.close();
0074 
0075         if (!contentOK) {
0076             err.setReturnCode(ERR_ABORT).setMessage(i18nc("Error message",  "%1-%2: '%3'", errorLine, errorCol, errorMsg)).addError(ERR_INVALIDARG, i18nc("Error message",  "Invalid XML content in file '%1'", m_importer->getFileName().toDisplayString()));
0077         } else {
0078             // Get root
0079             QDomElement docElem = doc.documentElement();
0080 
0081             // // Get book
0082             QDomElement book = docElem.firstChildElement(QStringLiteral("gnc:book"));
0083             if (book.isNull()) {
0084                 book = docElem;
0085             }
0086             if (book.isNull()) {
0087                 err = SKGError(ERR_INVALIDARG, i18nc("Error message",  "Invalid XML content in file '%1'", m_importer->getFileName().toDisplayString()));
0088             } else {
0089                 QMap<QString, SKGUnitObject> mapUnitIdUnit;
0090                 err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "GNC"), 6);
0091                 IFOK(err) {
0092                     // Step 1-Get units
0093                     SKGUnitObject defaultUnit;
0094                     {
0095                         QDomNodeList unitList = book.elementsByTagName(QStringLiteral("gnc:commodity"));
0096                         int nbUnit = unitList.count();
0097                         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), nbUnit);
0098                         for (int i = 0; !err && i < nbUnit; ++i) {
0099                             QDomElement unit = unitList.at(i).toElement();
0100                             QDomElement unitName = unit.firstChildElement(QStringLiteral("cmdty:id"));
0101                             QDomElement unitSpace = unit.firstChildElement(QStringLiteral("cmdty:space"));
0102                             if (!unitName.isNull() && !unitSpace.isNull()) {
0103                                 SKGUnitObject unitObj(m_importer->getDocument());
0104                                 if (unitSpace.text() == QStringLiteral("ISO4217")) {
0105                                     // We try a creation
0106                                     SKGUnitObject::createCurrencyUnit(m_importer->getDocument(), unitName.text(), unitObj);
0107                                 }
0108 
0109                                 if (!err && !unitObj.exist()) {
0110                                     // Creation of unit
0111                                     err = unitObj.setName(unitName.text());
0112                                     IFOKDO(err, unitObj.setSymbol(unitName.text()))
0113                                     IFOKDO(err, unitObj.setType(SKGUnitObject::SHARE))
0114                                     IFOKDO(err, unitObj.save())
0115                                 }
0116 
0117                                 if (!err && !defaultUnit.exist()) {
0118                                     defaultUnit = unitObj;
0119                                 }
0120 
0121                                 mapUnitIdUnit[unitName.text()] = unitObj;
0122                             }
0123 
0124                             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0125                         }
0126 
0127                         SKGENDTRANSACTION(m_importer->getDocument(),  err)
0128                     }
0129                     IFOKDO(err, m_importer->getDocument()->stepForward(1))
0130 
0131                     // Step 2-Get accounts and categories
0132                     SKGAccountObject gnucashTemporaryAccount(m_importer->getDocument());
0133                     QMap<QString, QString> mapIdName;
0134                     QMap<QString, QChar> mapIdType;
0135                     QMap<QString, SKGUnitObject> mapIdUnit;
0136                     QMap<QString, SKGAccountObject> mapIdAccount;
0137                     QMap<QString, SKGCategoryObject> mapIdCategory;
0138 
0139                     // Create bank and temporary account for gnucash import
0140                     SKGBankObject bank(m_importer->getDocument());
0141                     IFOKDO(err, m_importer->getDocument()->addOrModifyAccount(QStringLiteral("GNUCASH-TEMPORARY-ACCOUNT"), QString(), QStringLiteral("GNUCASH")))
0142                     IFOKDO(err, bank.setName(QStringLiteral("GNUCASH")))
0143                     IFOKDO(err, bank.load())
0144                     IFOKDO(err, gnucashTemporaryAccount.setName(QStringLiteral("GNUCASH-TEMPORARY-ACCOUNT")))
0145                     IFOKDO(err, gnucashTemporaryAccount.load())
0146 
0147                     {
0148                         // Create accounts
0149                         QDomNodeList accountList = book.elementsByTagName(QStringLiteral("gnc:account"));
0150                         int nbAccount = accountList.count();
0151                         IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import banks and accounts"), nbAccount))
0152                         for (int i = 0; !err && i < nbAccount; ++i) {
0153                             QDomElement account = accountList.at(i).toElement();
0154                             QDomElement parentNode = account.parentNode().toElement();
0155                             if (parentNode.tagName() != QStringLiteral("gnc:template-transactions")) {
0156                                 QDomElement accountId = account.firstChildElement(QStringLiteral("act:id"));
0157                                 QDomElement accountName = account.firstChildElement(QStringLiteral("act:name"));
0158                                 QDomElement accountType = account.firstChildElement(QStringLiteral("act:type"));
0159                                 QDomElement accountCode = account.firstChildElement(QStringLiteral("act:code"));
0160                                 QDomElement accountDescription = account.firstChildElement(QStringLiteral("act:description"));
0161 
0162 
0163                                 QString realAccountName = accountName.text();
0164 
0165                                 if (!accountId.isNull() && !accountType.isNull() && !realAccountName.isEmpty()) {
0166                                     QString type = accountType.text();
0167                                     if (type == QStringLiteral("INCOME") || type == QStringLiteral("EXPENSE")) {
0168                                         // It is a category
0169                                         QString fullName = realAccountName;
0170 
0171                                         QDomElement accountParent = account.firstChildElement(QStringLiteral("act:parent"));
0172                                         if (!err && !accountParent.isNull() && mapIdType[accountParent.text()] == 'C') {
0173                                             fullName = mapIdName[accountParent.text()] % OBJECTSEPARATOR % fullName;
0174                                         }
0175 
0176                                         SKGCategoryObject catObj;
0177                                         err = SKGCategoryObject::createPathCategory(m_importer->getDocument(), fullName, catObj);
0178 
0179                                         mapIdCategory[accountId.text()] = catObj;
0180                                         mapIdType[accountId.text()] = 'C';  // Memorize an account
0181                                     } else if (type == QStringLiteral("BANK") ||
0182                                                type == QStringLiteral("CREDIT") ||
0183                                                type == QStringLiteral("STOCK") ||
0184                                                type == QStringLiteral("ASSET") ||
0185                                                type == QStringLiteral("LIABILITY") ||
0186                                                type == QStringLiteral("RECEIVABLE") ||
0187                                                type == QStringLiteral("PAYABLE") ||
0188                                                type == QStringLiteral("TRADING") ||
0189                                                type == QStringLiteral("CREDITCARD") ||
0190                                                type == QStringLiteral("CASH") ||
0191                                                type == QStringLiteral("MUTUAL")) {
0192                                         // It is a real account
0193                                         SKGAccountObject act(m_importer->getDocument());
0194                                         err = bank.addAccount(act);
0195                                         IFOKDO(err, act.setName(realAccountName))
0196                                         int index = 1;
0197                                         while (!err && act.exist()) {
0198                                             index++;
0199                                             realAccountName = accountName.text() % " (" % SKGServices::intToString(index) % ')';
0200                                             err = act.setName(realAccountName);
0201                                         }
0202                                         if (!err && !accountCode.isNull()) {
0203                                             err = act.setNumber(accountCode.text());
0204                                         }
0205                                         if (!err && !accountDescription.isNull()) {
0206                                             err = act.setComment(accountDescription.text());
0207                                         }
0208                                         IFOKDO(err, act.setType(type == QStringLiteral("ASSET") ? SKGAccountObject::ASSETS :
0209                                                                 type == QStringLiteral("STOCK") || type == QStringLiteral("MUTUAL") || type == QStringLiteral("RECEIVABLE") || type == QStringLiteral("PAYABLE") || type == QStringLiteral("TRADING") ? SKGAccountObject::INVESTMENT :
0210                                                                 type == QStringLiteral("CASH") ? SKGAccountObject::WALLET :
0211                                                                 type == QStringLiteral("LIABILITY") ? SKGAccountObject::LOAN :
0212                                                                 type == QStringLiteral("CREDIT") ? SKGAccountObject::CREDITCARD : SKGAccountObject::CURRENT))
0213                                         IFOKDO(err, act.save())
0214 
0215                                         // Change parent bank in case of ASSETS
0216                                         if (act.getType() == SKGAccountObject::WALLET) {
0217                                             // Get blank bank
0218                                             SKGBankObject blankBank(m_importer->getDocument());
0219                                             IFOKDO(err, blankBank.setName(QString()))
0220                                             if (blankBank.exist()) {
0221                                                 err = blankBank.load();
0222                                             } else {
0223                                                 err = blankBank.save();
0224                                             }
0225                                             IFOKDO(err, act.setBank(blankBank))
0226                                             IFOKDO(err, act.save())
0227                                         }
0228 
0229                                         mapIdAccount[accountId.text()] = act;
0230                                         mapIdType[accountId.text()] = 'A';  // Memorize an account
0231 
0232                                         QDomNodeList accountUnits = account.elementsByTagName(QStringLiteral("cmdty:id"));
0233                                         if (!err && (accountUnits.count() != 0)) {
0234                                             mapIdUnit[accountId.text()] = mapUnitIdUnit[accountUnits.at(0).toElement().text()];
0235                                         }
0236                                     } else if (type == QStringLiteral("EQUITY")) {
0237                                         mapIdType[accountId.text()] = 'E';  // Memorize an account
0238                                     } else {
0239                                         mapIdType[accountId.text()] = ' ';  // Memorize an account
0240                                     }
0241 
0242 
0243                                     mapIdName[accountId.text()] = realAccountName;
0244                                 }
0245                             }
0246 
0247                             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0248                         }
0249 
0250                         SKGENDTRANSACTION(m_importer->getDocument(),  err)
0251                     }
0252                     IFOKDO(err, m_importer->getDocument()->stepForward(2))
0253 
0254                     // Step 3- Get transactions
0255                     QMap<QString, SKGOperationObject> mapIdScheduledOperation;
0256                     {
0257                         QDomNodeList operationsList = book.elementsByTagName(QStringLiteral("gnc:transaction"));
0258                         int nbOperations = operationsList.count();
0259                         IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import transactions"), nbOperations))
0260                         QVector<SubOpInfo> suboperationsList;
0261                         for (int i = 0; !err && i < nbOperations; ++i) {
0262                             QDomElement operation = operationsList.at(i).toElement();
0263 
0264                             QDomElement operationId = operation.firstChildElement(QStringLiteral("trn:id"));
0265                             QDomElement operationDate = operation.firstChildElement(QStringLiteral("trn:date-posted"));
0266                             QDomElement operationNumber = operation.firstChildElement(QStringLiteral("trn:num"));
0267                             QDomElement operationPayee = operation.firstChildElement(QStringLiteral("trn:description"));
0268                             QDomElement operationSlots = operation.firstChildElement(QStringLiteral("trn:slots"));
0269 
0270                             SKGOperationObject operationObj;
0271                             err = gnucashTemporaryAccount.addOperation(operationObj, true);
0272                             if (!err && !operationDate.isNull()) {
0273                                 // Format 2007-01-13 00:00:00 +0100
0274                                 err = operationObj.setDate(QDate::fromString(operationDate.text().left(10), QStringLiteral("yyyy-MM-dd")));
0275                             }
0276                             if (!err && !operationNumber.isNull()) {
0277                                 err = operationObj.setNumber(operationNumber.text());
0278                             }
0279                             if (!err && !operationPayee.isNull()) {
0280                                 SKGPayeeObject payeeObject;
0281                                 err = SKGPayeeObject::createPayee(m_importer->getDocument(), operationPayee.text(), payeeObject);
0282                                 IFOKDO(err, operationObj.setPayee(payeeObject))
0283                             }
0284                             IFOKDO(err, operationObj.setImported(true))
0285                             IFOKDO(err, operationObj.setImportID("GNC-" % operationId.text()))
0286                             IFOKDO(err, operationObj.setUnit(defaultUnit))
0287 
0288                             IFOK(err) {
0289                                 QDomElement parentNode = operation.parentNode().toElement();
0290                                 err = operationObj.setTemplate(parentNode.tagName() == QStringLiteral("gnc:template-transactions"));
0291                             }
0292                             if (!operationSlots.isNull()) {
0293                                 QDomNodeList slotList = operationSlots.elementsByTagName(QStringLiteral("slot"));
0294                                 int nbSlots = slotList.count();
0295                                 for (int k = 0; !err && k < nbSlots; ++k) {
0296                                     QDomElement slot = slotList.at(k).toElement();
0297 
0298                                     QDomElement key = slot.firstChildElement(QStringLiteral("slot:key"));
0299                                     QDomElement value = slot.firstChildElement(QStringLiteral("slot:value"));
0300                                     if (!key.isNull() && !value.isNull()) {
0301                                         if (key.text() == QStringLiteral("notes")) {
0302                                             err = operationObj.setComment(value.text());
0303                                         }
0304                                     }
0305                                 }
0306                             }
0307                             IFOKDO(err, operationObj.save())
0308 
0309                             // Get splits
0310                             bool parentSet = false;
0311                             QDomElement splits = operation.firstChildElement(QStringLiteral("trn:splits"));
0312 
0313 
0314                             int nbSuboperations = 0;
0315                             suboperationsList.resize(0);
0316                             {
0317                                 QDomNodeList suboperationsListTmp = splits.elementsByTagName(QStringLiteral("trn:split"));
0318                                 nbSuboperations = suboperationsListTmp.count();
0319 
0320                                 int nbRealSuboperations = 0;
0321                                 for (int j = 0; !err && j < nbSuboperations; ++j) {
0322                                     SubOpInfo info;
0323                                     info.subOp = suboperationsListTmp.at(j).toElement();
0324                                     info.account = info.subOp.firstChildElement(QStringLiteral("split:account"));
0325                                     info.value = 0;
0326                                     QStringList vals = SKGServices::splitCSVLine(info.subOp.firstChildElement(QStringLiteral("split:quantity")).text(), '/');
0327                                     if (vals.count() == 1) {
0328                                         info.value = SKGServices::stringToDouble(vals.at(0));
0329                                     } else if (vals.count() == 2) {
0330                                         info.value = SKGServices::stringToDouble(vals.at(0)) / SKGServices::stringToDouble(vals.at(1));
0331                                     }
0332 
0333                                     QDomElement suboperationSlots = info.subOp.firstChildElement(QStringLiteral("split:slots"));
0334                                     if (!suboperationSlots.isNull()) {
0335                                         QDomNodeList slotList = suboperationSlots.elementsByTagName(QStringLiteral("slot"));
0336                                         int nbSlots = slotList.count();
0337                                         for (int k = 0; !err && k < nbSlots; ++k) {
0338                                             QDomElement slot = slotList.at(k).toElement();
0339 
0340                                             QDomElement key = slot.firstChildElement(QStringLiteral("slot:key"));
0341                                             QDomElement value = slot.firstChildElement(QStringLiteral("slot:value"));
0342                                             if (!key.isNull() && !value.isNull()) {
0343                                                 if (key.text() == QStringLiteral("sched-xaction")) {
0344                                                     // This is a scheduled transaction
0345                                                     QDomNodeList scheduledSlotList = value.elementsByTagName(QStringLiteral("slot"));
0346                                                     int nbscheduledSlotList = scheduledSlotList.count();
0347                                                     for (int k2 = 0; !err && k2 < nbscheduledSlotList; ++k2) {
0348                                                         QDomElement slot2 = scheduledSlotList.at(k2).toElement();
0349 
0350                                                         QDomElement key2 = slot2.firstChildElement(QStringLiteral("slot:key"));
0351                                                         QDomElement value2 = slot2.firstChildElement(QStringLiteral("slot:value"));
0352                                                         if (!key2.isNull() && !value2.isNull()) {
0353                                                             if (key2.text() == QStringLiteral("account")) {
0354                                                                 mapIdScheduledOperation[info.account.text()] = operationObj;
0355                                                                 info.account = value2;
0356                                                             } else if (key2.text() == QStringLiteral("debit-formula") && !value2.text().isEmpty()) {
0357                                                                 QStringList vals2 = SKGServices::splitCSVLine(value2.text(), '/');
0358                                                                 if (vals2.count() == 1) {
0359                                                                     info.value = SKGServices::stringToDouble(vals2.at(0));
0360                                                                 } else if (vals2.count() == 2) {
0361                                                                     info.value = SKGServices::stringToDouble(vals2.at(0)) / SKGServices::stringToDouble(vals2.at(1));
0362                                                                 }
0363                                                             } else if (key2.text() == QStringLiteral("credit-formula") && !value2.text().isEmpty()) {
0364                                                                 QStringList vals2 = SKGServices::splitCSVLine(value2.text(), '/');
0365                                                                 if (vals2.count() == 1) {
0366                                                                     info.value = -SKGServices::stringToDouble(vals2.at(0));
0367                                                                 } else if (vals2.count() == 2) {
0368                                                                     info.value = -SKGServices::stringToDouble(vals2.at(0)) / SKGServices::stringToDouble(vals2.at(1));
0369                                                                 }
0370                                                             }
0371                                                         }
0372                                                     }
0373                                                 }
0374                                             }
0375                                         }
0376                                     }
0377 
0378                                     if (!std::isnan(info.value)) {
0379                                         QChar accountType = mapIdType[info.account.text()];
0380                                         if (accountType == 'C') {
0381                                             suboperationsList.push_front(info);
0382                                         } else {
0383                                             suboperationsList.push_back(info);
0384                                         }
0385 
0386                                         ++nbRealSuboperations;
0387                                     }
0388                                 }
0389                                 nbSuboperations = nbRealSuboperations;
0390                             }
0391 
0392                             SKGSubOperationObject suboperationObj;
0393                             for (int j = 0; !err && j < nbSuboperations; ++j) {
0394                                 SubOpInfo suboperationInfo = suboperationsList.at(j);
0395                                 QDomElement suboperation = suboperationInfo.subOp.toElement();
0396                                 QDomElement suboperationReconciled = suboperation.firstChildElement(QStringLiteral("split:reconciled-state"));
0397                                 QDomElement suboperationMemo = suboperation.firstChildElement(QStringLiteral("split:memo"));
0398                                 QDomElement suboperationAction = suboperation.firstChildElement(QStringLiteral("split:action"));
0399 
0400                                 // Set transaction attribute
0401                                 if (!suboperationReconciled.isNull() && operationObj.getStatus() == SKGOperationObject::NONE) {
0402                                     QString val = suboperationReconciled.text();
0403                                     IFOKDO(err, operationObj.setStatus(val == QStringLiteral("c") ? SKGOperationObject::MARKED : val == QStringLiteral("y") ? SKGOperationObject::CHECKED : SKGOperationObject::NONE))
0404                                 }
0405                                 if (!err && !suboperationMemo.isNull() && operationObj.getComment().isEmpty()) {
0406                                     err = operationObj.setComment(suboperationMemo.text());
0407                                 }
0408                                 if (!err && !suboperationAction.isNull() && operationObj.getMode().isEmpty()) {
0409                                     err = operationObj.setMode(suboperationAction.text());
0410                                 }
0411                                 IFOKDO(err, operationObj.save())
0412 
0413                                 QChar accountType = mapIdType[suboperationInfo.account.text()];
0414                                 if (accountType == 'C') {
0415                                     // It is a split on category
0416                                     SKGCategoryObject cat = mapIdCategory[suboperationInfo.account.text()];
0417 
0418                                     double q = -suboperationInfo.value;
0419                                     if (!err && (!suboperationObj.exist() || suboperationObj.getQuantity() != q)) {
0420                                         err = operationObj.addSubOperation(suboperationObj);
0421                                     }
0422                                     IFOKDO(err, suboperationObj.setQuantity(q))
0423                                     IFOKDO(err, suboperationObj.setCategory(cat))
0424                                     if (!err && !suboperationMemo.isNull()) {
0425                                         err = suboperationObj.setComment(suboperationMemo.text());
0426                                     }
0427                                     IFOKDO(err, suboperationObj.save())
0428                                 } else if (accountType == 'E') {
0429                                     // Set as initial balance
0430                                     IFOKDO(err, operationObj.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00")))
0431                                     IFOKDO(err, operationObj.setStatus(SKGOperationObject::CHECKED))
0432                                     IFOKDO(err, operationObj.save())
0433 
0434                                     IFOKDO(err, suboperationObj.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00")))
0435                                     if (suboperationObj.getDocument() != nullptr) {
0436                                         IFOKDO(err, suboperationObj.save())
0437                                     }
0438                                 } else if (accountType == 'A') {
0439                                     // It is a transfer of account
0440                                     SKGAccountObject act(m_importer->getDocument());
0441                                     IFOKDO(err, act.setName(mapIdName[suboperationInfo.account.text()]))
0442                                     IFOKDO(err, act.load())
0443 
0444                                     // Set Unit
0445                                     SKGUnitObject unitName = mapIdUnit[suboperationInfo.account.text()];
0446                                     double quantity = 0;
0447                                     if (parentSet) {
0448                                         // If the parent is already set, it means that is a transfer
0449                                         SKGOperationObject operationObj2;
0450                                         IFOKDO(err, act.addOperation(operationObj2, true))
0451                                         IFOKDO(err, operationObj2.setDate(operationObj.getDate()))
0452                                         IFOKDO(err, operationObj2.setNumber(operationObj.getNumber()))
0453                                         IFOKDO(err, operationObj2.setMode(operationObj.getMode()))
0454                                         if (!err && !suboperationMemo.isNull()) {
0455                                             err = operationObj2.setComment(suboperationMemo.text());
0456                                         }
0457                                         SKGPayeeObject payeeObject;
0458                                         operationObj.getPayee(payeeObject);
0459                                         IFOKDO(err, operationObj2.setPayee(payeeObject))
0460                                         IFOK(err) {
0461                                             QString val = suboperationReconciled.text();
0462                                             err = operationObj2.setStatus(val == QStringLiteral("c") ? SKGOperationObject::MARKED : val == QStringLiteral("y") ? SKGOperationObject::CHECKED : SKGOperationObject::NONE);
0463                                         }
0464                                         IFOKDO(err, operationObj2.setImported(true))
0465                                         IFOKDO(err, operationObj2.setImportID(operationObj.getImportID()))
0466                                         IFOKDO(err, operationObj2.setTemplate(operationObj.isTemplate()))
0467                                         if (!err && unitName.exist()) {
0468                                             err = operationObj2.setUnit(unitName);
0469                                         }
0470                                         IFOKDO(err, operationObj2.save())
0471                                         IFOKDO(err, operationObj2.setGroupOperation(operationObj))
0472                                         IFOKDO(err, operationObj2.save())
0473 
0474                                         // Create sub transaction on operationObj2
0475                                         SKGSubOperationObject suboperationObj2;
0476                                         IFOKDO(err, operationObj2.addSubOperation(suboperationObj2))
0477                                         IFOK(err) {
0478                                             // We must take the quality of the split having an action
0479                                             quantity = suboperationInfo.value;
0480                                             err = suboperationObj2.setQuantity(suboperationInfo.value);
0481                                         }
0482                                         if (!err && !suboperationMemo.isNull()) {
0483                                             err = suboperationObj2.setComment(suboperationMemo.text());
0484                                         }
0485                                         IFOKDO(err, suboperationObj2.save())
0486                                     } else {
0487                                         // We set the parent
0488                                         IFOKDO(err, operationObj.setParentAccount(act))
0489                                         if (!err && unitName.exist()) {
0490                                             err = operationObj.setUnit(unitName);
0491                                         }
0492                                         IFOKDO(err, operationObj.save())
0493 
0494                                         // Compute quantity
0495                                         quantity = suboperationInfo.value;
0496 
0497                                         // Create sub transaction on operationObj
0498                                         quantity -= SKGServices::stringToDouble(operationObj.getAttribute(QStringLiteral("f_QUANTITY")));
0499                                         if (fabs(quantity) > 10e-9) {
0500                                             IFOKDO(err, operationObj.addSubOperation(suboperationObj))
0501                                             IFOKDO(err, suboperationObj.setQuantity(quantity))
0502                                             if (!err && !suboperationMemo.isNull()) {
0503                                                 err = suboperationObj.setComment(suboperationMemo.text());
0504                                             }
0505                                             IFOKDO(err, suboperationObj.save())
0506                                         }
0507 
0508                                         parentSet = true;
0509                                     }
0510                                 }
0511                             }
0512 
0513                             if (!err && i % 500 == 0) {
0514                                 err = m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE"));
0515                             }
0516 
0517                             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0518                         }
0519 
0520                         SKGENDTRANSACTION(m_importer->getDocument(),  err)
0521                     }
0522                     IFOKDO(err, m_importer->getDocument()->stepForward(3))
0523 
0524                     // Step 4-Get scheduled
0525                     {
0526                         // Create scheduled
0527                         QDomNodeList scheduleList = book.elementsByTagName(QStringLiteral("gnc:schedxaction"));
0528                         int nbSchedule = scheduleList.count();
0529                         IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import scheduled transactions"), nbSchedule))
0530                         for (int i = 0; !err && i < nbSchedule; ++i) {
0531                             QDomElement schedule = scheduleList.at(i).toElement();
0532                             QDomElement scheduleAutoCreate = schedule.firstChildElement(QStringLiteral("sx:autoCreate"));
0533                             QDomElement scheduleAutoCreateNotify = schedule.firstChildElement(QStringLiteral("sx:autoCreateNotify"));
0534                             QDomElement scheduleAdvanceCreateDays = schedule.firstChildElement(QStringLiteral("sx:advanceCreateDays"));
0535                             QDomElement scheduleAdvanceRemindDays = schedule.firstChildElement(QStringLiteral("sx:advanceRemindDays"));
0536                             QDomElement scheduleOccur = schedule.firstChildElement(QStringLiteral("sx:num-occur"));
0537                             QDomElement scheduleEnable = schedule.firstChildElement(QStringLiteral("sx:enabled"));
0538                             QDomElement scheduleLast = schedule.firstChildElement(QStringLiteral("sx:last"));
0539                             if (scheduleLast.isNull()) {
0540                                 scheduleLast = schedule.firstChildElement(QStringLiteral("sx:start"));
0541                             }
0542 
0543                             QDomElement scheduleMult;
0544                             QDomElement schedulePeriod;
0545                             QDomElement scheduleDate;
0546                             QDomNodeList scheduleScheduleList = schedule.elementsByTagName(QStringLiteral("gnc:recurrence"));
0547                             if (scheduleScheduleList.count() != 0) {
0548                                 QDomElement scheduleSchedule = scheduleScheduleList.at(0).toElement();
0549                                 scheduleMult = scheduleSchedule.firstChildElement(QStringLiteral("recurrence:mult"));
0550                                 schedulePeriod = scheduleSchedule.firstChildElement(QStringLiteral("recurrence:period_type"));
0551 
0552                                 scheduleDate = scheduleLast.firstChildElement(QStringLiteral("gdate"));
0553                             }
0554 
0555                             QDomElement scheduleTempl = schedule.firstChildElement(QStringLiteral("sx:templ-acct"));
0556                             if (!scheduleTempl.isNull()) {
0557                                 SKGOperationObject operation = mapIdScheduledOperation[scheduleTempl.text()];
0558                                 SKGRecurrentOperationObject recuOperation;
0559                                 err = operation.addRecurrentOperation(recuOperation);
0560                                 if (!err && !scheduleOccur.isNull()) {
0561                                     err = recuOperation.timeLimit(true);
0562                                     IFOKDO(err, recuOperation.setTimeLimit(SKGServices::stringToInt(scheduleOccur.text())))
0563                                 }
0564                                 if (!err && !scheduleAutoCreate.isNull()) {
0565                                     err = recuOperation.autoWriteEnabled(scheduleAutoCreate.text() == QStringLiteral("y"));
0566                                 }
0567                                 if (!err && !scheduleAdvanceCreateDays.isNull()) {
0568                                     err = recuOperation.setAutoWriteDays(SKGServices::stringToInt(scheduleAdvanceCreateDays.text()));
0569                                 }
0570                                 if (!err && !scheduleAutoCreateNotify.isNull()) {
0571                                     err = recuOperation.warnEnabled(scheduleAutoCreateNotify.text() == QStringLiteral("y"));
0572                                 }
0573                                 if (!err && !scheduleAdvanceRemindDays.isNull()) {
0574                                     err = recuOperation.setWarnDays(SKGServices::stringToInt(scheduleAdvanceRemindDays.text()));
0575                                 }
0576 
0577                                 if (!err && !scheduleMult.isNull() && !schedulePeriod.isNull()) {
0578                                     int p = SKGServices::stringToInt(scheduleMult.text());
0579                                     SKGRecurrentOperationObject::PeriodUnit punit = SKGRecurrentOperationObject::DAY;
0580                                     if (schedulePeriod.text() == QStringLiteral("month")) {
0581                                         punit = SKGRecurrentOperationObject::MONTH;
0582                                     } else if (schedulePeriod.text() == QStringLiteral("week")) {
0583                                         punit = SKGRecurrentOperationObject::WEEK;
0584                                     }
0585 
0586                                     err = recuOperation.setPeriodIncrement(p);
0587                                     IFOKDO(err, recuOperation.setPeriodUnit(punit))
0588                                 }
0589 
0590                                 if (!err && !scheduleDate.isNull()) {
0591                                     err = recuOperation.setDate(QDate::fromString(scheduleDate.text().left(10), QStringLiteral("yyyy-MM-dd")));
0592                                 }
0593                                 IFOKDO(err, recuOperation.setDate(recuOperation.getNextDate()))
0594                                 if (!err && !scheduleEnable.isNull() && scheduleEnable.text() == QStringLiteral("n")) {
0595                                     err = recuOperation.autoWriteEnabled(false);
0596                                     IFOKDO(err, recuOperation.setWarnDays(false))
0597                                 }
0598 
0599                                 IFOKDO(err, recuOperation.save())
0600                             }
0601 
0602                             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0603                         }
0604 
0605                         SKGENDTRANSACTION(m_importer->getDocument(),  err)
0606                     }
0607                     IFOKDO(err, m_importer->getDocument()->stepForward(4))
0608 
0609                     // Step 5-Get unit values
0610                     {
0611                         // Create unit values
0612                         QDomElement pricedb = book.firstChildElement(QStringLiteral("gnc:pricedb"));
0613                         if (!pricedb.isNull()) {
0614                             QDomNodeList priceList = pricedb.elementsByTagName(QStringLiteral("price"));
0615                             int nbPrice = priceList.count();
0616                             IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), nbPrice))
0617                             for (int i = 0; !err && i < nbPrice; ++i) {
0618                                 QDomElement price = priceList.at(i).toElement();
0619 
0620                                 QDomNodeList priceIdList = price.elementsByTagName(QStringLiteral("cmdty:id"));
0621                                 QDomNodeList priceDateList = price.elementsByTagName(QStringLiteral("ts:date"));
0622                                 QDomElement priceValue = price.firstChildElement(QStringLiteral("price:value"));
0623 
0624                                 if (!priceIdList.isEmpty() && !priceDateList.isEmpty() && !priceValue.isNull()) {
0625                                     QString unitName = priceIdList.at(0).toElement().text();
0626                                     QDate unitDate = QDate::fromString(priceDateList.at(0).toElement().text().left(10), QStringLiteral("yyyy-MM-dd"));
0627 
0628                                     double val = 0;
0629                                     QStringList vals = SKGServices::splitCSVLine(priceValue.text(), '/');
0630                                     if (vals.count() == 1) {
0631                                         val = SKGServices::stringToDouble(vals.at(0));
0632                                     } else if (vals.count() == 2) {
0633                                         val = SKGServices::stringToDouble(vals.at(0)) / SKGServices::stringToDouble(vals.at(1));
0634                                     }
0635 
0636                                     err = m_importer->getDocument()->addOrModifyUnitValue(unitName, unitDate, val);
0637                                 }
0638 
0639                                 IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
0640                             }
0641                             SKGENDTRANSACTION(m_importer->getDocument(),  err)
0642                         }
0643                     }
0644                     IFOKDO(err, m_importer->getDocument()->stepForward(5))
0645 
0646                     // Step 6-Remove temporary account
0647                     {
0648                         IFOKDO(err, gnucashTemporaryAccount.remove(false, true))
0649 
0650                         IFOKDO(err, m_importer->getDocument()->executeSqliteOrder("DELETE FROM account WHERE rd_bank_id=" % SKGServices::intToString(bank.getID()) % " AND t_name='GNUCASH-TEMPORARY-ACCOUNT' AND (SELECT COUNT(1) FROM operation WHERE operation.rd_account_id=account.id)=0"))
0651                         IFOKDO(err, m_importer->getDocument()->stepForward(6))
0652                     }
0653                 }
0654 
0655                 SKGENDTRANSACTION(m_importer->getDocument(),  err)
0656             }
0657             IFOKDO(err, m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")))
0658         }
0659     }
0660 
0661     return err;
0662 }
0663 
0664 QString SKGImportPluginGnc::getMimeTypeFilter() const
0665 {
0666     return "*.uncompressed *.gnucash *.gnc|" % i18nc("A file format", "GnuCash document");
0667 }
0668 
0669 #include <skgimportplugingnc.moc>