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>