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>