1 /***************************************************************************
2  * SPDX-FileCopyrightText: 2021 S. MANKOWSKI stephane@mankowski.fr
3  * SPDX-FileCopyrightText: 2021 G. DE BURE support@mankowski.fr
4  * SPDX-License-Identifier: GPL-3.0-or-later
5  ***************************************************************************/
6 /** @file
7  * This file is Skrooge plugin for KMY import / export.
8  *
9  * @author Stephane MANKOWSKI / Guillaume DE BURE
10  */
11 #include "skgimportpluginkmy.h"
12 
13 #include <kcompressiondevice.h>
14 #include <kpluginfactory.h>
15 
16 #include <qdom.h>
17 #include <qmath.h>
18 
19 #include "skgbankincludes.h"
20 #include "skgimportexportmanager.h"
21 #include "skgobjectbase.h"
22 #include "skgpayeeobject.h"
23 #include "skgservices.h"
24 #include "skgtraces.h"
25 
26 /**
27  * Operations treated.
28  */
29 QSet<QString> SKGImportPluginKmy::m_opTreated;
30 
31 /**
32  * Map id / unit.
33  */
34 QMap<QString, SKGUnitObject> SKGImportPluginKmy::m_mapIdUnit;
35 
36 /**
37  * Map id / account.
38  */
39 QMap<QString, SKGAccountObject> SKGImportPluginKmy::m_mapIdAccount;
40 
41 /**
42  * Map id / category.
43  */
44 QMap<QString, SKGCategoryObject> SKGImportPluginKmy::m_mapIdCategory;
45 
46 /**
47  * Map id / payee.
48  */
49 QMap<QString, SKGPayeeObject> SKGImportPluginKmy::m_mapIdPayee;
50 
51 /**
52  * This plugin factory.
53  */
K_PLUGIN_FACTORY(SKGImportPluginKmyFactory,registerPlugin<SKGImportPluginKmy> ();)54 K_PLUGIN_FACTORY(SKGImportPluginKmyFactory, registerPlugin<SKGImportPluginKmy>();)
55 
56 SKGImportPluginKmy::SKGImportPluginKmy(QObject* iImporter, const QVariantList& iArg)
57     : SKGImportPlugin(iImporter)
58 {
59     SKGTRACEINFUNC(10)
60     Q_UNUSED(iArg)
61 }
62 
63 SKGImportPluginKmy::~SKGImportPluginKmy()
64     = default;
65 
isImportPossible()66 bool SKGImportPluginKmy::isImportPossible()
67 {
68     SKGTRACEINFUNC(10)
69     return isExportPossible();
70 }
71 
importSecurities(QDomElement & docElem)72 SKGError SKGImportPluginKmy::importSecurities(QDomElement& docElem)
73 {
74     SKGError err;
75     QDomElement securities = docElem.firstChildElement(QStringLiteral("SECURITIES"));
76     if (!err && !securities.isNull()) {
77         SKGTRACEINRC(10, "SKGImportPluginKmy::importFile-SECURITIES", err)
78         QDomNodeList securityList = securities.elementsByTagName(QStringLiteral("SECURITY"));
79         int nb = securityList.count();
80         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), nb);
81         for (int i = 0; !err && i < nb; ++i) {
82             QDomElement security = securityList.at(i).toElement();
83             QString unitName = security.attribute(QStringLiteral("name"));
84 
85             // We try a creation
86             SKGUnitObject unitObj(m_importer->getDocument());
87             SKGUnitObject::createCurrencyUnit(m_importer->getDocument(), unitName, unitObj);
88 
89             if (!err && (unitObj.getID() == 0)) {
90                 // Creation of unit
91                 err = unitObj.setName(unitName);
92                 QString symbol = security.attribute(QStringLiteral("symbol"));
93                 if (symbol.isEmpty()) {
94                     symbol = unitName;
95                 }
96                 IFOKDO(err, unitObj.setSymbol(symbol))
97                 IFOKDO(err, unitObj.setCountry(security.attribute(QStringLiteral("trading-market"))))
98                 IFOKDO(err, unitObj.setType(SKGUnitObject::SHARE))
99                 IFOK(err) {
100                     // Set pairs
101                     QDomNodeList pairList = security.elementsByTagName(QStringLiteral("PAIR"));
102                     int nb2 = pairList.count();
103                     for (int j = 0; !err && j < nb2; ++j) {
104                         QDomElement pair = pairList.at(j).toElement();
105                         if (pair.attribute(QStringLiteral("key")).toLower() == QStringLiteral("kmm-security-id")) {
106                             err = unitObj.setInternetCode(pair.attribute(QStringLiteral("value")));
107                         }
108                     }
109                 }
110                 IFOKDO(err, unitObj.save())
111             }
112 
113             m_mapIdUnit[security.attribute(QStringLiteral("id"))] = unitObj;
114 
115             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
116         }
117 
118         SKGENDTRANSACTION(m_importer->getDocument(),  err)
119     }
120     return err;
121 }
122 
importPrices(QDomElement & docElem)123 SKGError SKGImportPluginKmy::importPrices(QDomElement& docElem)
124 {
125     SKGError err;
126     QDomElement prices = docElem.firstChildElement(QStringLiteral("PRICES"));
127     if (!err && !prices.isNull()) {
128         SKGTRACEINRC(10, "SKGImportPluginKmy::importFile-PRICES", err)
129         QDomNodeList pricepairList = prices.elementsByTagName(QStringLiteral("PRICEPAIR"));
130         int nb = pricepairList.count();
131         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import units"), nb);
132         for (int i = 0; !err && i < nb; ++i) {
133             QDomElement pricepair = pricepairList.at(i).toElement();
134 
135             SKGUnitObject unitObj = m_mapIdUnit.value(pricepair.attribute(QStringLiteral("from")));
136             if (unitObj.getID() != 0) {
137                 // Unit is existing
138                 QDomNodeList pricesList = pricepair.elementsByTagName(QStringLiteral("PRICE"));
139                 int nb2 = pricesList.count();
140                 for (int j = 0; !err && j < nb2; ++j) {
141                     QDomElement price = pricesList.at(j).toElement();
142 
143                     SKGUnitValueObject unitValObj;
144                     err = unitObj.addUnitValue(unitValObj);
145                     IFOKDO(err, unitValObj.setDate(QDate::fromString(price.attribute(QStringLiteral("date")), QStringLiteral("yyyy-MM-dd"))))
146                     IFOKDO(err, unitValObj.setQuantity(toKmyValue(price.attribute(QStringLiteral("price")))))
147                     IFOKDO(err, unitValObj.save(true, false))
148                 }
149             }
150 
151             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
152         }
153 
154         SKGENDTRANSACTION(m_importer->getDocument(),  err)
155     }
156     return err;
157 }
158 
importInstitutions(QMap<QString,SKGBankObject> & mapIdBank,QDomElement & docElem)159 SKGError SKGImportPluginKmy::importInstitutions(QMap<QString, SKGBankObject>& mapIdBank, QDomElement& docElem)
160 {
161     SKGError err;
162     QDomElement institutions = docElem.firstChildElement(QStringLiteral("INSTITUTIONS"));
163     if (!err && !institutions.isNull()) {
164         SKGTRACEINRC(10, "SKGImportPluginKmy::importFile-INSTITUTIONS", err)
165         QDomNodeList institutionList = institutions.elementsByTagName(QStringLiteral("INSTITUTION"));
166         int nb = institutionList.count();
167         for (int i = 0; !err && i < nb; ++i) {
168             // Get bank object
169             QDomElement bank = institutionList.at(i).toElement();
170             SKGBankObject bankObject(m_importer->getDocument());
171             err = bankObject.setName(bank.attribute(QStringLiteral("name")));
172             IFOKDO(err, bankObject.setNumber(bank.attribute(QStringLiteral("sortcode"))))
173             IFOKDO(err, bankObject.save())
174             mapIdBank[bank.attribute(QStringLiteral("id"))] = bankObject;
175         }
176     }
177     return err;
178 }
179 
importPayees(QMap<QString,SKGPayeeObject> & mapIdPayee,QDomElement & docElem)180 SKGError SKGImportPluginKmy::importPayees(QMap<QString, SKGPayeeObject>& mapIdPayee, QDomElement& docElem)
181 {
182     SKGError err;
183     QDomElement payees = docElem.firstChildElement(QStringLiteral("PAYEES"));
184     if (!err && !payees.isNull()) {
185         SKGTRACEINRC(10, "SKGImportPluginKmy::importFile-PAYEES", err)
186         QDomNodeList payeeList = payees.elementsByTagName(QStringLiteral("PAYEE"));
187         int nb = payeeList.count();
188         for (int i = 0; !err && i < nb; ++i) {
189             // Get account object
190             QDomElement payee = payeeList.at(i).toElement();
191             QDomElement address = payee.firstChildElement(QStringLiteral("ADDRESS"));
192             SKGPayeeObject payeeObject;
193             err = SKGPayeeObject::createPayee(m_importer->getDocument(), payee.attribute(QStringLiteral("name")), payeeObject);
194             IFOK(err) {
195                 QString add = address.attribute(QStringLiteral("street")) % ' ' % address.attribute(QStringLiteral("postcode")) % ' ' % address.attribute(QStringLiteral("city")) % ' ' % address.attribute(QStringLiteral("state")) % ' ' % address.attribute(QStringLiteral("telephone"));
196                 add.replace(QStringLiteral("  "), QStringLiteral(" "));
197                 err = payeeObject.setAddress(add.trimmed());
198                 IFOKDO(err, payeeObject.save())
199             }
200             IFOK(err) {
201                 mapIdPayee[payee.attribute(QStringLiteral("id"))] = payeeObject;
202             }
203         }
204     }
205     return err;
206 }
207 
importTransactions(QDomElement & docElem,SKGAccountObject & kmymoneyTemporaryAccount,QMap<QString,SKGPayeeObject> & mapIdPayee)208 SKGError SKGImportPluginKmy::importTransactions(QDomElement& docElem, SKGAccountObject& kmymoneyTemporaryAccount, QMap<QString, SKGPayeeObject>& mapIdPayee)
209 {
210     SKGError err;
211     SKGTRACEINRC(10, "SKGImportPluginKmy::importFile-TRANSACTION", err)
212     QDomNodeList transactionList = docElem.elementsByTagName(QStringLiteral("TRANSACTION"));
213     int nb = transactionList.count();
214     err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb);
215     QVector<QDomNode> suboperationsList;
216     for (int i = 0; !err && i < nb; ++i) {
217         // Get operation object
218         QDomElement operation = transactionList.at(i).toElement();
219 
220         SKGOperationObject operationObj;
221         err = kmymoneyTemporaryAccount.addOperation(operationObj, true);
222         IFOKDO(err, operationObj.setDate(QDate::fromString(operation.attribute(QStringLiteral("postdate")), QStringLiteral("yyyy-MM-dd"))))
223         IFOKDO(err, operationObj.setComment(operation.attribute(QStringLiteral("memo"))))
224         IFOKDO(err, operationObj.setImported(true))
225         IFOKDO(err, operationObj.setImportID("KMY-" % operation.attribute(QStringLiteral("id"))))
226         IFOK(err) {
227             QString unitName = operation.attribute(QStringLiteral("commodity"));
228             if (unitName.isEmpty()) {
229                 unitName = QStringLiteral("??");
230             }
231             SKGUnitObject unit(m_importer->getDocument());
232             err = unit.setName(unitName);
233             IFOKDO(err, unit.setSymbol(unitName))
234             IFOK(err) {
235                 if (unit.exist()) {
236                     err = unit.load();
237                 } else {
238                     err = unit.save();
239                 }
240 
241                 IFOKDO(err, operationObj.setUnit(unit))
242             }
243         }
244         IFOKDO(err, operationObj.save(false, false))
245 
246         // Is it a schedule ?
247         IFOK(err) {
248             QDomElement recu = operation.parentNode().toElement();
249             if (recu.tagName() == QStringLiteral("SCHEDULED_TX")) {
250                 // Yes ==> creation of the schedule
251                 IFOKDO(err, operationObj.load())
252                 IFOKDO(err, operationObj.setTemplate(true))
253                 IFOKDO(err, operationObj.save(true, false))
254 
255                 // Yes ==> creation of the schedule
256                 SKGRecurrentOperationObject recuObj;
257                 IFOKDO(err, operationObj.addRecurrentOperation(recuObj))
258                 IFOKDO(err, recuObj.setDate(operationObj.getDate()))
259                 IFOKDO(err, recuObj.autoWriteEnabled(recu.attribute(QStringLiteral("autoEnter")) == QStringLiteral("1")))
260                 IFOKDO(err, recuObj.setAutoWriteDays(0))
261 
262                 int occu = SKGServices::stringToInt(recu.attribute(QStringLiteral("occurenceMultiplier")));
263                 QString occuType = recu.attribute(QStringLiteral("occurence"));  // krazy:exclude=spelling
264                 SKGRecurrentOperationObject::PeriodUnit period;
265                 if (occuType == QStringLiteral("32") ||
266                     occuType == QStringLiteral("1024") ||
267                     occuType == QStringLiteral("256") ||
268                     occuType == QStringLiteral("8192")) {
269                     period = SKGRecurrentOperationObject::MONTH;
270                 } else if (occuType == QStringLiteral("1")) {
271                     period = SKGRecurrentOperationObject::DAY;
272                     IFOKDO(err, recuObj.timeLimit(true))
273                     IFOKDO(err, recuObj.setTimeLimit(1))
274                 } else if (occuType == QStringLiteral("2")) {
275                     period = SKGRecurrentOperationObject::DAY;
276                 } else if (occuType == QStringLiteral("4")) {
277                     period = SKGRecurrentOperationObject::WEEK;
278                 } else if (occuType == QStringLiteral("18")) {
279                     period = SKGRecurrentOperationObject::WEEK;
280                     occu *= 2;
281                 } else {
282                     period = SKGRecurrentOperationObject::YEAR;
283                 }
284 
285                 IFOKDO(err, recuObj.setPeriodIncrement(occu))
286                 IFOKDO(err, recuObj.setPeriodUnit(period))
287 
288                 IFOK(err) {
289                     QString endDate = recu.attribute(QStringLiteral("endDate"));
290                     if (!endDate.isEmpty()) {
291                         IFOKDO(err, recuObj.timeLimit(true))
292                         IFOKDO(err, recuObj.setTimeLimit(QDate::fromString(recu.attribute(QStringLiteral("endDate")), QStringLiteral("yyyy-MM-dd"))))
293                     }
294                 }
295 
296                 if (occuType == QStringLiteral("1") && !recu.attribute(QStringLiteral("lastPayment")).isEmpty()) {
297                     // Single schedule already done
298                     IFOKDO(err, recuObj.timeLimit(true))
299                     IFOKDO(err, recuObj.setTimeLimit(0))
300                 }
301                 IFOKDO(err, recuObj.save(true, false))
302             }
303         }
304 
305         // Get splits
306         bool parentSet = false;
307         double quantity = 0;
308         QDomElement splits = operation.firstChildElement(QStringLiteral("SPLITS"));
309 
310         int nbSuboperations = 0;
311         suboperationsList.resize(0);
312         {
313             QDomNodeList suboperationsListTmp = splits.elementsByTagName(QStringLiteral("SPLIT"));
314             nbSuboperations = suboperationsListTmp.count();
315 
316             for (int j = 0; !err && j < nbSuboperations; ++j) {
317                 QDomElement suboperation = suboperationsListTmp.at(j).toElement();
318                 if (m_mapIdCategory.contains(suboperation.attribute(QStringLiteral("account")))) {
319                     suboperationsList.push_front(suboperation);
320                 } else {
321                     suboperationsList.push_back(suboperation);
322                 }
323             }
324         }
325 
326         SKGSubOperationObject suboperationObj;
327         for (int j = 0; !err && j < nbSuboperations; ++j) {
328             QDomElement suboperation = suboperationsList.at(j).toElement();
329 
330             // Set operation attributes
331             IFOKDO(err, operationObj.setPayee(mapIdPayee[suboperation.attribute(QStringLiteral("payee"))]))
332             if (!err && operationObj.getComment().isEmpty()) {
333                 err = operationObj.setComment(suboperation.attribute(QStringLiteral("memo")));
334             }
335             IFOKDO(err, operationObj.setNumber(suboperation.attribute(QStringLiteral("number"))))
336 
337             // Set state
338             if (operationObj.getStatus() == SKGOperationObject::NONE) {
339                 QString val = suboperation.attribute(QStringLiteral("reconcileflag"));
340                 IFOKDO(err, operationObj.setStatus(val == QStringLiteral("1") ? SKGOperationObject::POINTED : val == QStringLiteral("2") ? SKGOperationObject::CHECKED : SKGOperationObject::NONE))
341             }
342             IFOKDO(err, operationObj.save())
343 
344             if (m_mapIdCategory.contains(suboperation.attribute(QStringLiteral("account")))) {
345                 // It is a split on category
346                 SKGCategoryObject cat = m_mapIdCategory.value(suboperation.attribute(QStringLiteral("account")));
347 
348                 double q = -toKmyValue(suboperation.attribute(QStringLiteral("shares")));
349                 if (!err && ((suboperationObj.getID() == 0) || suboperationObj.getQuantity() != q)) {
350                     err = operationObj.addSubOperation(suboperationObj);
351                 }
352                 IFOKDO(err, suboperationObj.setQuantity(q))
353                 IFOKDO(err, suboperationObj.setCategory(cat))
354                 IFOKDO(err, suboperationObj.setComment(suboperation.attribute(QStringLiteral("memo"))))
355                 IFOKDO(err, suboperationObj.save(true, false))
356             } else {
357                 QString accountSubOp = suboperation.attribute(QStringLiteral("account"));
358                 if (!accountSubOp.isEmpty()) {
359                     if (nbSuboperations == 1) {
360                         SKGUnitObject unit = m_mapIdUnit.value(accountSubOp);
361                         if (unit.getID() != 0) {
362                             IFOKDO(err, operationObj.setUnit(unit))
363                         }
364                     }
365 
366                     if (!m_mapIdAccount.contains(accountSubOp) || (nbSuboperations == 2 &&
367                             m_mapIdAccount.value(accountSubOp) == kmymoneyTemporaryAccount &&
368                             suboperationsList.at(0).toElement().attribute(QStringLiteral("action")).isEmpty() &&
369                             suboperationsList.at(1).toElement().attribute(QStringLiteral("action")).isEmpty())) {
370                         // Set as initial balance
371                         IFOKDO(err, operationObj.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00")))
372                         IFOKDO(err, operationObj.setStatus(SKGOperationObject::CHECKED))
373                         IFOKDO(err, operationObj.save(true, false))
374 
375                         if (!err && (suboperationObj.getID() == 0)) {
376                             err = operationObj.addSubOperation(suboperationObj);
377                         }
378 
379                         IFOKDO(err, suboperationObj.setAttribute(QStringLiteral("d_date"), QStringLiteral("0000-00-00")))
380                         IFOKDO(err, suboperationObj.save(true, false))
381                     } else {
382                         // It is a transfer of account
383                         SKGAccountObject act = m_mapIdAccount.value(accountSubOp);
384 
385                         if (parentSet) {
386                             // If the parent is already set, it means that is a transfer
387                             SKGOperationObject operationObj2;
388                             IFOKDO(err, act.addOperation(operationObj2, true))
389                             IFOKDO(err, operationObj2.setTemplate(operationObj.isTemplate()))
390                             IFOKDO(err, operationObj2.setDate(operationObj.getDate()))
391                             IFOKDO(err, operationObj2.setNumber(operationObj.getNumber()))
392                             IFOKDO(err, operationObj2.setComment(operationObj.getComment()))
393                             SKGPayeeObject payeeObject;
394                             operationObj.getPayee(payeeObject);
395                             IFOKDO(err, operationObj2.setPayee(payeeObject))
396                             IFOK(err) {
397                                 QString val = suboperation.attribute(QStringLiteral("reconcileflag"));
398                                 err = operationObj2.setStatus(val == QStringLiteral("1") ? SKGOperationObject::POINTED : val == QStringLiteral("2") ? SKGOperationObject::CHECKED : SKGOperationObject::NONE);
399                             }
400                             IFOKDO(err, operationObj2.setImported(true))
401                             IFOKDO(err, operationObj2.setImportID(operationObj.getImportID()))
402 
403                             SKGUnitObject unit = m_mapIdUnit.value(accountSubOp);
404                             if (unit.getID() != 0) {
405                                 IFOKDO(err, operationObj2.setUnit(unit))
406                             } else {
407                                 IFOKDO(err, operationObj.getUnit(unit))
408                                 IFOKDO(err, operationObj2.setUnit(unit))
409                             }
410                             IFOKDO(err, operationObj2.save())
411                             IFOKDO(err, operationObj2.setGroupOperation(operationObj))
412                             IFOKDO(err, operationObj2.save())
413 
414                             // Create sub operation on operationObj2
415                             SKGSubOperationObject suboperationObj2;
416                             IFOKDO(err, operationObj2.addSubOperation(suboperationObj2))
417                             IFOK(err) {
418                                 // We must take the quality of the split having an action
419                                 quantity = toKmyValue(suboperation.attribute(QStringLiteral("shares")));
420                                 err = suboperationObj2.setQuantity(quantity);
421                             }
422                             IFOKDO(err, suboperationObj2.setComment(suboperation.attribute(QStringLiteral("memo"))))
423                             IFOKDO(err, suboperationObj2.save(true, false))
424                         } else {
425                             // We set the parent
426                             if (Q_LIKELY(!err) && (act.getID() == 0)) {
427                                 err = SKGError(ERR_FAIL, i18nc("Error message", "Account with identifier %1 not found", suboperation.attribute(QStringLiteral("account"))));
428                             }
429                             IFOKDO(err, operationObj.setParentAccount(act, true))
430                             IFOKDO(err, operationObj.save())
431 
432                             // Compute quantity
433                             quantity = toKmyValue(suboperation.attribute(QStringLiteral("shares")));
434 
435                             // Create sub operation on operationObj
436                             quantity -= SKGServices::stringToDouble(operationObj.getAttribute(QStringLiteral("f_QUANTITY")));
437                             if (quantity != 0 || nbSuboperations == 1) {
438                                 IFOKDO(err, operationObj.addSubOperation(suboperationObj))
439                                 IFOKDO(err, suboperationObj.setQuantity(quantity))
440                                 IFOKDO(err, suboperationObj.setComment(suboperation.attribute(QStringLiteral("memo"))))
441                                 IFOKDO(err, suboperationObj.save(true, false))
442                             }
443 
444                             parentSet = true;
445                         }
446                     }
447                 }
448             }
449         }
450 
451         if (!err && i % 500 == 0) {
452             err = m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE"));
453         }
454 
455         IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
456     }
457 
458     SKGENDTRANSACTION(m_importer->getDocument(),  err)
459 
460     return err;
461 }
462 
importBudget(QDomElement & docElem)463 SKGError SKGImportPluginKmy::importBudget(QDomElement& docElem)
464 {
465     SKGError err;
466     QDomElement budgets = docElem.firstChildElement(QStringLiteral("BUDGETS"));
467     if (!err && !budgets.isNull()) {
468         SKGTRACEINRC(10, "SKGImportPluginKmy::importFile-BUDGETS", err)
469         // Build cache of categories
470         QMap<int, bool> catExpense;
471         {
472             SKGStringListList list;
473             err = m_importer->getDocument()->executeSelectSqliteOrder(QStringLiteral("SELECT id, t_TYPEEXPENSE FROM v_category_display"), list);
474             int nb = list.count();
475             for (int i = 1; i < nb; ++i) {
476                 catExpense[SKGServices::stringToInt(list.at(i).at(0))] = (list.at(i).at(0) == QStringLiteral("-"));
477             }
478         }
479 
480         QDomNodeList budgetList = budgets.elementsByTagName(QStringLiteral("BUDGET"));
481         int nb = budgetList.count();
482         IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import budgets"), nb))
483         for (int i = 0; !err && i < nb; ++i) {
484             QDomElement budget = budgetList.at(i).toElement();
485             QDomNodeList accountList = budget.elementsByTagName(QStringLiteral("ACCOUNT"));
486             int nb2 = accountList.count();
487             for (int j = 0; !err && j < nb2; ++j) {
488                 QDomElement account = accountList.at(j).toElement();
489                 SKGCategoryObject cat = m_mapIdCategory.value(account.attribute(QStringLiteral("id")));
490                 QString budgetlevel = account.attribute(QStringLiteral("budgetlevel"));
491 
492                 QDomNodeList periodList = account.elementsByTagName(QStringLiteral("PERIOD"));
493                 int nb3 = periodList.count();
494                 for (int k = 0; !err && k < nb3; ++k) {
495                     QDomElement period = periodList.at(k).toElement();
496 
497                     double q = toKmyValue(period.attribute(QStringLiteral("amount")));
498 
499                     // Are we able to find to sign with the category ?
500                     if (catExpense[cat.getID()]) {
501                         q = -q;
502                     }
503 
504                     QStringList dates = SKGServices::splitCSVLine(period.attribute(QStringLiteral("start")), '-');
505                     if (dates.count() == 3) {
506                         // We try a creation
507                         for (int m = 1; !err && m <= (budgetlevel == QStringLiteral("monthly") ? 12 : 1); ++m) {
508                             SKGBudgetObject budget2(m_importer->getDocument());
509                             err = budget2.setCategory(cat);
510                             IFOKDO(err, budget2.setBudgetedAmount(q))
511                             IFOKDO(err, budget2.setYear(SKGServices::stringToDouble(dates.at(0))))
512                             IFOKDO(err, budget2.setMonth(budgetlevel == QStringLiteral("monthbymonth") ? SKGServices::stringToDouble(dates.at(1)) :
513                                                          budgetlevel == QStringLiteral("yearly") ? 0 : m));
514                             IFOKDO(err, budget2.save(true, false))
515                         }
516                     }
517                 }
518             }
519 
520             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
521         }
522 
523         SKGENDTRANSACTION(m_importer->getDocument(),  err)
524     }
525     return err;
526 }
527 
importAccounts(SKGBankObject & bank,SKGAccountObject & kmymoneyTemporaryAccount,QMap<QString,SKGBankObject> & mapIdBank,QDomElement & docElem)528 SKGError SKGImportPluginKmy::importAccounts(SKGBankObject& bank, SKGAccountObject& kmymoneyTemporaryAccount, QMap<QString, SKGBankObject>& mapIdBank, QDomElement& docElem)
529 {
530     SKGError err;
531     IFOKDO(err, m_importer->getDocument()->addOrModifyAccount(QStringLiteral("KMYMONEY-TEMPORARY-ACCOUNT"), QString(), QStringLiteral("KMYMONEY")))
532     IFOKDO(err, bank.setName(QStringLiteral("KMYMONEY")))
533     IFOKDO(err, bank.load())
534     IFOKDO(err, kmymoneyTemporaryAccount.setName(QStringLiteral("KMYMONEY-TEMPORARY-ACCOUNT")))
535     IFOKDO(err, kmymoneyTemporaryAccount.load())
536 
537     QDomElement accounts = docElem.firstChildElement(QStringLiteral("ACCOUNTS"));
538     if (!err && !accounts.isNull()) {
539         SKGTRACEINRC(10, "SKGImportPluginKmy::importFile-ACCOUNTS", err)
540         QDomNodeList accountList = accounts.elementsByTagName(QStringLiteral("ACCOUNT"));
541         int nb = accountList.count();
542         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import accounts"), nb);
543         QMap<QString, QString> type15Account;
544         QMap<QString, QString> type15Unit;
545         for (int i = 0; !err && i < nb; ++i) {
546             // Get account object
547             QDomElement account = accountList.at(i).toElement();
548             QString type = account.attribute(QStringLiteral("type"));
549             if (type == QStringLiteral("15")) {
550                 // Actions: the real account is the parent
551                 type15Account[account.attribute(QStringLiteral("id"))] = account.attribute(QStringLiteral("parentaccount"));
552                 type15Unit[account.attribute(QStringLiteral("id"))] = account.attribute(QStringLiteral("currency"));
553             } else if (type != QStringLiteral("12") && type != QStringLiteral("13") && type != QStringLiteral("16")) {
554                 // Get the bank
555                 QString bankId = account.attribute(QStringLiteral("institution"));
556                 SKGBankObject bankObj = (bankId.isEmpty() ? bank : mapIdBank[account.attribute(QStringLiteral("institution"))]);
557 
558                 // Creation of the account
559                 SKGAccountObject accountObj;
560                 IFOKDO(err, bankObj.addAccount(accountObj))
561                 IFOKDO(err, accountObj.setName(account.attribute(QStringLiteral("name"))))
562                 IFOKDO(err, accountObj.setNumber(account.attribute(QStringLiteral("number"))))
563                 IFOKDO(err, accountObj.setComment(account.attribute(QStringLiteral("description"))))
564                 IFOK(err) {
565                     SKGAccountObject::AccountType typeA = (type == QStringLiteral("4") ? SKGAccountObject::CREDITCARD : (type == QStringLiteral("7") ? SKGAccountObject::INVESTMENT : (type == QStringLiteral("9") ? SKGAccountObject::ASSETS : (type == QStringLiteral("3") ? SKGAccountObject::WALLET : (type == QStringLiteral("10") ? SKGAccountObject::LOAN : SKGAccountObject::CURRENT)))));
566                     err = accountObj.setType(typeA);
567                 }
568                 IFOK(err) {
569                     // Set pairs
570                     QDomNodeList pairList = account.elementsByTagName(QStringLiteral("PAIR"));
571                     int nb2 = pairList.count();
572                     for (int j = 0; !err && j < nb2; ++j) {
573                         QDomElement pair = pairList.at(j).toElement();
574                         if (pair.attribute(QStringLiteral("key")).toLower() == QStringLiteral("mm-closed") && pair.attribute(QStringLiteral("value")).toLower() == QStringLiteral("yes")) {
575                             err = accountObj.setClosed(true);
576                         } else if (pair.attribute(QStringLiteral("key")).toLower() == QStringLiteral("preferredaccount") && pair.attribute(QStringLiteral("value")).toLower() == QStringLiteral("yes")) {
577                             err = accountObj.bookmark(true);
578                         } else if (pair.attribute(QStringLiteral("key")).toLower() == QStringLiteral("minbalanceabsolute")) {
579                             err = accountObj.setMinLimitAmount(toKmyValue(pair.attribute(QStringLiteral("value"))));
580                             IFOKDO(err, accountObj.minLimitAmountEnabled(true))
581                         } else if (pair.attribute(QStringLiteral("key")).toLower() == QStringLiteral("maxcreditabsolute")) {
582                             err = accountObj.setMaxLimitAmount(-toKmyValue(pair.attribute(QStringLiteral("value"))));
583                             IFOKDO(err, accountObj.maxLimitAmountEnabled(true))
584                         }
585                     }
586                 }
587                 IFOKDO(err, accountObj.save())
588 
589                 // Change parent bank in case of ASSETS
590                 if (accountObj.getType() == SKGAccountObject::WALLET) {
591                     // Get blank bank
592                     SKGBankObject blankBank(m_importer->getDocument());
593                     IFOKDO(err, blankBank.setName(QString()))
594                     if (blankBank.exist()) {
595                         err = blankBank.load();
596                     } else {
597                         err = blankBank.save();
598                     }
599                     IFOKDO(err, accountObj.setBank(blankBank))
600                     IFOKDO(err, accountObj.save())
601                 }
602 
603                 m_mapIdAccount[account.attribute(QStringLiteral("id"))] = accountObj;
604             } else if (type == QStringLiteral("16")) {
605                 m_mapIdAccount[account.attribute(QStringLiteral("id"))] = kmymoneyTemporaryAccount;
606             } else {
607                 // Create category
608                 SKGCategoryObject cat = m_mapIdCategory.value(account.attribute(QStringLiteral("id")));
609                 if (cat.getID() != 0) {
610                     // Already existing ==> we must set the right name
611                     err = cat.setName(account.attribute(QStringLiteral("name")));
612                     IFOKDO(err, cat.save())
613                 } else {
614                     // We must create it
615                     cat = SKGCategoryObject(m_importer->getDocument());
616                     err = cat.setName(account.attribute(QStringLiteral("name")));
617                     IFOKDO(err, cat.save())
618                 }
619                 if (err) {
620                     // The category already exists
621                     SKGCategoryObject catp;
622                     err = cat.getParentCategory(catp);
623                     QString fullName = catp.getFullName() % OBJECTSEPARATOR % cat.getName();
624                     IFOKDO(err, cat.remove(false))
625                     IFOKDO(err, SKGCategoryObject::createPathCategory(m_importer->getDocument(), fullName, cat))
626                 }
627                 m_mapIdCategory[account.attribute(QStringLiteral("id"))] = cat;
628 
629                 // Create sub categories
630                 QDomNodeList subaccountList = account.elementsByTagName(QStringLiteral("SUBACCOUNT"));
631                 int nb2 = subaccountList.count();
632                 for (int j = 0; !err && j < nb2; ++j) {
633                     QDomElement subaccount = subaccountList.at(j).toElement();
634 
635                     // Is child already existing ?
636                     SKGCategoryObject cat2 = m_mapIdCategory.value(subaccount.attribute(QStringLiteral("id")));
637                     if (cat2.getID() != 0) {
638                         // Yes ==> reparent
639                         err = cat2.setParentCategory(cat);
640                         IFOKDO(err, cat2.save(true, false))
641                     } else {
642                         // No ==> create
643                         IFOKDO(err, cat.addCategory(cat2))
644                         IFOKDO(err, cat2.setName(subaccount.attribute(QStringLiteral("id"))))
645                         IFOKDO(err, cat2.save())
646                         m_mapIdCategory[subaccount.attribute(QStringLiteral("id"))] = cat2;
647                     }
648                 }
649             }
650 
651             QStringList list = type15Account.keys();
652             for (const auto& k : qAsConst(list)) {
653                 m_mapIdAccount[k] = m_mapIdAccount.value(type15Account[k]);
654                 m_mapIdUnit[account.attribute(QStringLiteral("id"))] = m_mapIdUnit.value(account.attribute(QStringLiteral("currency")));
655             }
656 
657             list = type15Unit.keys();
658             for (const auto& k : qAsConst(list)) {
659                 m_mapIdUnit[k] = m_mapIdUnit[type15Unit[k]];
660             }
661 
662             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
663         }
664 
665         SKGENDTRANSACTION(m_importer->getDocument(),  err)
666     }
667     return err;
668 }
669 
importFile()670 SKGError SKGImportPluginKmy::importFile()
671 {
672     if (m_importer == nullptr) {
673         return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
674     }
675 
676     SKGError err;
677     SKGTRACEINFUNCRC(2, err)
678 
679     // Initialisation
680     m_mapIdUnit.clear();
681     m_mapIdAccount.clear();
682     m_mapIdCategory.clear();
683     m_mapIdPayee.clear();
684 
685     // Open file
686     KCompressionDevice file(m_importer->getLocalFileName(), KCompressionDevice::GZip);
687     if (!file.open(QIODevice::ReadOnly)) {
688         err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().toDisplayString()));
689     } else {
690         QDomDocument doc;
691 
692         // Set the file without uncompression
693         QString errorMsg;
694         int errorLine = 0;
695         int errorCol = 0;
696         bool contentOK = doc.setContent(file.readAll(), &errorMsg, &errorLine, &errorCol);
697         file.close();
698 
699         if (!contentOK) {
700             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()));
701         } else {
702             // Get root
703             QDomElement docElem = doc.documentElement();
704             err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "KMY"), 8);
705 
706             // Step 1-Get units
707             IFOKDO(err, importSecurities(docElem))
708             IFOKDO(err, m_importer->getDocument()->stepForward(1))
709 
710             // Step 2-Get units prices
711             IFOKDO(err, importPrices(docElem))
712             IFOKDO(err, m_importer->getDocument()->stepForward(2))
713 
714             // Step 3-Create banks
715             QMap<QString, SKGBankObject> mapIdBank;
716             IFOKDO(err, importInstitutions(mapIdBank, docElem))
717             IFOKDO(err, m_importer->getDocument()->stepForward(3))
718 
719             // Step 4-Create account and categories
720             // Create bank and temporary account for kmymoney import
721             SKGAccountObject kmymoneyTemporaryAccount(m_importer->getDocument());
722             SKGBankObject bank(m_importer->getDocument());
723             IFOKDO(err, importAccounts(bank, kmymoneyTemporaryAccount, mapIdBank, docElem))
724             IFOKDO(err, m_importer->getDocument()->stepForward(4))
725 
726             // Step 5-Read payees
727             QMap<QString, SKGPayeeObject> mapIdPayee;
728             IFOKDO(err, importPayees(mapIdPayee, docElem))
729             IFOKDO(err, m_importer->getDocument()->stepForward(5))
730 
731             // Step 6-Create operations
732             IFOKDO(err, importTransactions(docElem, kmymoneyTemporaryAccount, mapIdPayee))
733             IFOKDO(err, m_importer->getDocument()->stepForward(6))
734 
735             // Step 7-Get budgets
736             IFOKDO(err, importBudget(docElem))
737             IFOKDO(err, m_importer->getDocument()->stepForward(7))
738 
739             // Step 8-Remove useless account and temporary account
740             {
741                 IFOKDO(err, kmymoneyTemporaryAccount.remove(false, true))
742                 IFOKDO(err, m_importer->getDocument()->executeSqliteOrder("DELETE FROM account WHERE rd_bank_id=" % SKGServices::intToString(bank.getID()) % " AND (SELECT COUNT(1) FROM operation WHERE operation.rd_account_id=account.id)=0"))
743             }
744             IFOKDO(err, m_importer->getDocument()->stepForward(8))
745 
746             SKGENDTRANSACTION(m_importer->getDocument(),  err)
747 
748             IFOKDO(err, m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")))
749         }
750     }
751 
752     // Clean
753     m_mapIdUnit.clear();
754     m_mapIdAccount.clear();
755     m_mapIdCategory.clear();
756     m_mapIdPayee.clear();
757 
758     return err;
759 }
760 
isExportPossible()761 bool SKGImportPluginKmy::isExportPossible()
762 {
763     SKGTRACEINFUNC(10)
764     return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("KMY"));
765 }
766 
exportHeader(QDomDocument & doc,QDomElement & root)767 SKGError SKGImportPluginKmy::exportHeader(QDomDocument& doc, QDomElement& root)
768 {
769     SKGError err;
770     QDomElement fileindo = doc.createElement(QStringLiteral("FILEINFO"));
771     root.appendChild(fileindo);
772 
773     {
774         // <CREATION_DATE>
775         QDomElement creation_date = doc.createElement(QStringLiteral("CREATION_DATE"));
776         fileindo.appendChild(creation_date);
777         creation_date.setAttribute(QStringLiteral("date"), SKGServices::dateToSqlString(QDateTime::currentDateTime()));
778 
779         // <LAST_MODIFIED_DATE>
780         QDomElement last_modified_date = doc.createElement(QStringLiteral("LAST_MODIFIED_DATE"));
781         fileindo.appendChild(last_modified_date);
782         last_modified_date.setAttribute(QStringLiteral("date"), SKGServices::dateToSqlString(QDateTime::currentDateTime()));
783 
784         // <VERSION>
785         QDomElement version = doc.createElement(QStringLiteral("VERSION"));
786         fileindo.appendChild(version);
787         version.setAttribute(QStringLiteral("id"), QStringLiteral("1"));
788 
789         // <FIXVERSION>
790         QDomElement fixversion = doc.createElement(QStringLiteral("FIXVERSION"));
791         fileindo.appendChild(fixversion);
792         fixversion.setAttribute(QStringLiteral("id"), QStringLiteral("2"));
793     }
794 
795     // <USER>
796     QDomElement user = doc.createElement(QStringLiteral("USER"));
797     root.appendChild(user);
798     user.setAttribute(QStringLiteral("email"), QString());
799     user.setAttribute(QStringLiteral("name"), QString());
800     {
801         // ADDRESS
802         QDomElement address = doc.createElement(QStringLiteral("ADDRESS"));
803         user.appendChild(address);
804         address.setAttribute(QStringLiteral("street"), QString());
805         address.setAttribute(QStringLiteral("zipcode"), QString());
806         address.setAttribute(QStringLiteral("county"), QString());
807         address.setAttribute(QStringLiteral("city"), QString());
808         address.setAttribute(QStringLiteral("telephone"), QString());
809     }
810     return err;
811 }
812 
exportSecurities(QDomDocument & doc,QDomElement & root,const QString & stdUnit)813 SKGError SKGImportPluginKmy::exportSecurities(QDomDocument& doc, QDomElement& root, const QString& stdUnit)
814 {
815     SKGError err;
816     QDomElement securities = doc.createElement(QStringLiteral("SECURITIES"));
817     root.appendChild(securities);
818 
819     QDomElement currencies = doc.createElement(QStringLiteral("CURRENCIES"));
820     root.appendChild(currencies);
821 
822     SKGObjectBase::SKGListSKGObjectBase objects;
823     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_unit"), QStringLiteral("t_type!='I'"), objects))
824     int nb = objects.count();
825     securities.setAttribute(QStringLiteral("count"), SKGServices::intToString(nb));
826 
827     QStringList importedCurrency;
828     IFOK(err) {
829         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export units"), nb);
830         for (int i = 0; !err && i < nb; ++i) {
831             SKGUnitObject obj(objects.at(i));
832             if (obj.getType() == SKGUnitObject::SHARE || obj.getType() == SKGUnitObject::OBJECT) {
833                 QDomElement security = doc.createElement(QStringLiteral("SECURITY"));
834                 securities.appendChild(security);
835 
836                 SKGUnitObject parentUnit;
837                 obj.getUnit(parentUnit);
838                 QString unitP = SKGUnitObject::getInternationalCode(parentUnit.getName());
839                 if (unitP.isEmpty()) {
840                     unitP = stdUnit;
841                 }
842 
843                 security.setAttribute(QStringLiteral("id"), obj.getName());
844                 security.setAttribute(QStringLiteral("trading-currency"), unitP);
845                 security.setAttribute(QStringLiteral("saf"), QStringLiteral("100000"));
846                 security.setAttribute(QStringLiteral("symbol"), obj.getSymbol());
847                 security.setAttribute(QStringLiteral("trading-market"), obj.getCountry());
848                 security.setAttribute(QStringLiteral("type"), QStringLiteral("4"));
849                 security.setAttribute(QStringLiteral("name"), obj.getName());
850 
851                 QString internetCode = obj.getInternetCode();
852                 if (!internetCode.isEmpty()) {
853                     QDomElement keyvaluepairs2 = doc.createElement(QStringLiteral("KEYVALUEPAIRS"));
854                     security.appendChild(keyvaluepairs2);
855 
856                     QDomElement pair1 = doc.createElement(QStringLiteral("PAIR"));
857                     keyvaluepairs2.appendChild(pair1);
858                     pair1.setAttribute(QStringLiteral("key"), QStringLiteral("kmm-online-source"));
859                     pair1.setAttribute(QStringLiteral("value"), QStringLiteral("Yahoo"));
860 
861                     QDomElement pair2 = doc.createElement(QStringLiteral("PAIR"));
862                     keyvaluepairs2.appendChild(pair2);
863                     pair2.setAttribute(QStringLiteral("key"), QStringLiteral("kmm-security-id"));
864                     pair2.setAttribute(QStringLiteral("value"), internetCode);
865                 }
866             } else {
867                 QDomElement currency = doc.createElement(QStringLiteral("CURRENCY"));
868                 currencies.appendChild(currency);
869 
870                 QString unit = SKGUnitObject::getInternationalCode(obj.getName());
871                 if (unit.isEmpty()) {
872                     unit = obj.getName();
873                 }
874 
875                 currency.setAttribute(QStringLiteral("saf"), QStringLiteral("100"));
876                 currency.setAttribute(QStringLiteral("symbol"), obj.getSymbol());
877                 currency.setAttribute(QStringLiteral("type"), QStringLiteral("3"));
878                 currency.setAttribute(QStringLiteral("id"), unit);
879                 currency.setAttribute(QStringLiteral("name"), obj.getName());
880                 currency.setAttribute(QStringLiteral("ppu"), QStringLiteral("100"));
881                 currency.setAttribute(QStringLiteral("scf"), QStringLiteral("100"));
882 
883                 importedCurrency.push_back(obj.getSymbol());
884             }
885             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
886         }
887 
888         SKGENDTRANSACTION(m_importer->getDocument(),  err)
889     }
890 
891     // <CURRENCIES>
892     QStringList units = SKGUnitObject::getListofKnownCurrencies(false);
893     nb = units.count();
894     int nbreal = 0;
895     for (int i = 0; i < nb; ++i) {
896         SKGServices::SKGUnitInfo info = SKGUnitObject::getUnitInfo(units.at(i));
897         if (info.Name != info.Symbol && !importedCurrency.contains(info.Symbol)) {
898             QDomElement currency = doc.createElement(QStringLiteral("CURRENCY"));
899             currencies.appendChild(currency);
900             currency.setAttribute(QStringLiteral("saf"), QStringLiteral("100"));
901             currency.setAttribute(QStringLiteral("symbol"), info.Symbol);
902             currency.setAttribute(QStringLiteral("type"), QStringLiteral("3"));
903             currency.setAttribute(QStringLiteral("id"), SKGUnitObject::getInternationalCode(info.Name));
904             currency.setAttribute(QStringLiteral("name"), info.Name);
905             currency.setAttribute(QStringLiteral("ppu"), QStringLiteral("100"));
906             currency.setAttribute(QStringLiteral("scf"), QStringLiteral("100"));
907 
908             ++nbreal;
909         }
910     }
911     currencies.setAttribute(QStringLiteral("count"), SKGServices::intToString(nbreal));
912 
913     // <PRICES>
914     QDomElement prices = doc.createElement(QStringLiteral("PRICES"));
915     root.appendChild(prices);
916     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_unit"), QStringLiteral("t_type='S'"), objects))
917     nb = objects.count();
918     prices.setAttribute(QStringLiteral("count"), SKGServices::intToString(nb));
919     IFOK(err) {
920         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export units"), nb);
921         for (int i = 0; !err && i < nb; ++i) {
922             SKGUnitObject obj(objects.at(i));
923             QDomElement pricepair = doc.createElement(QStringLiteral("PRICEPAIR"));
924             prices.appendChild(pricepair);
925 
926             QString unitP = SKGUnitObject::getInternationalCode(obj.getName());
927             if (unitP.isEmpty()) {
928                 unitP = stdUnit;
929             }
930 
931             pricepair.setAttribute(QStringLiteral("from"), obj.getName());
932             pricepair.setAttribute(QStringLiteral("to"), unitP);
933 
934             SKGObjectBase::SKGListSKGObjectBase unitValues;
935             err = obj.getUnitValues(unitValues);
936             int nb2 = unitValues.count();
937             for (int j = 0; !err && j < nb2; ++j) {
938                 QDomElement price = doc.createElement(QStringLiteral("PRICE"));
939                 pricepair.appendChild(price);
940 
941                 SKGUnitValueObject unitval(unitValues.at(j));
942                 price.setAttribute(QStringLiteral("price"), SKGImportPluginKmy::kmyValue(unitval.getQuantity()));
943                 price.setAttribute(QStringLiteral("source"), QStringLiteral("Utilisateur"));
944                 price.setAttribute(QStringLiteral("date"), SKGServices::dateToSqlString(unitval.getDate()));
945             }
946 
947             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
948         }
949 
950         SKGENDTRANSACTION(m_importer->getDocument(),  err)
951     }
952 
953     // <REPORTS>
954     QDomElement reports = doc.createElement(QStringLiteral("REPORTS"));
955     root.appendChild(reports);
956     reports.setAttribute(QStringLiteral("count"), QStringLiteral("0"));
957 
958     return err;
959 }
960 
exportBudgets(QDomDocument & doc,QDomElement & root)961 SKGError SKGImportPluginKmy::exportBudgets(QDomDocument& doc, QDomElement& root)
962 {
963     SKGError err;
964     QDomElement budgets = doc.createElement(QStringLiteral("BUDGETS"));
965     root.appendChild(budgets);
966 
967     SKGObjectBase::SKGListSKGObjectBase objects;
968     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_budget"), QStringLiteral("1=1 ORDER BY i_year, i_month"), objects))
969     int nb = objects.count();
970     int nbBudgets = 0;
971     int currentYear = 0;
972     QDomElement budget;
973 
974     QMap<QString, QDomElement> mapCatAccount;
975     IFOK(err) {
976         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export budgets"), nb);
977         for (int i = 0; !err && i < nb; ++i) {
978             SKGBudgetObject obj(objects.at(i));
979             SKGCategoryObject cat;
980             obj.getCategory(cat);
981             QString catId = getKmyUniqueIdentifier(cat);
982             int year = obj.getYear();
983             QString yearString = SKGServices::intToString(year);
984             int month = obj.getMonth();
985             QString monthString = SKGServices::intToString(month);
986             if (monthString.isEmpty()) {
987                 monthString = '0' % monthString;
988             }
989             if (currentYear != year) {
990                 budget = doc.createElement(QStringLiteral("BUDGET"));
991                 budgets.appendChild(budget);
992                 budget.setAttribute(QStringLiteral("version"), QStringLiteral("2"));
993                 budget.setAttribute(QStringLiteral("id"), yearString);
994                 budget.setAttribute(QStringLiteral("start"), yearString % "-01-01");
995                 budget.setAttribute(QStringLiteral("name"), yearString);
996 
997                 currentYear = year;
998                 mapCatAccount.clear();
999                 ++nbBudgets;
1000             }
1001 
1002             QDomElement account = mapCatAccount[catId];
1003             if (account.isNull() && !catId.isEmpty()) {
1004                 account = doc.createElement(QStringLiteral("ACCOUNT"));
1005                 budget.appendChild(account);
1006                 account.setAttribute(QStringLiteral("budgetsubaccounts"), QStringLiteral("0"));
1007                 account.setAttribute(QStringLiteral("id"), catId);
1008                 mapCatAccount[catId] = account;
1009             }
1010             if (!account.isNull()) {
1011                 account.setAttribute(QStringLiteral("budgetlevel"), obj.getMonth() == 0 ? QStringLiteral("yearly") : QStringLiteral("monthbymonth"));
1012 
1013                 QDomElement period = doc.createElement(QStringLiteral("PERIOD"));
1014                 account.appendChild(period);
1015                 period.setAttribute(QStringLiteral("amount"), SKGImportPluginKmy::kmyValue(qAbs(obj.getBudgetedAmount())));
1016                 period.setAttribute(QStringLiteral("start"), yearString % '-' % (obj.getMonth() == 0 ? QStringLiteral("01") : monthString) % "-01");
1017             }
1018 
1019             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
1020         }
1021 
1022         SKGENDTRANSACTION(m_importer->getDocument(),  err)
1023     }
1024     budgets.setAttribute(QStringLiteral("count"), SKGServices::intToString(nbBudgets));
1025     return err;
1026 }
1027 
exportSchedules(QDomDocument & doc,QDomElement & root)1028 SKGError SKGImportPluginKmy::exportSchedules(QDomDocument& doc, QDomElement& root)
1029 {
1030     SKGError err;
1031     QDomElement schedules = doc.createElement(QStringLiteral("SCHEDULES"));
1032     root.appendChild(schedules);
1033 
1034     SKGObjectBase::SKGListSKGObjectBase objects;
1035     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_recurrentoperation"), QString(), objects))
1036     int nb = objects.count();
1037     schedules.setAttribute(QStringLiteral("count"), SKGServices::intToString(nb));
1038     IFOK(err) {
1039         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export scheduled operations"), nb);
1040         for (int i = 0; !err && i < nb; ++i) {
1041             SKGRecurrentOperationObject obj(objects.at(i));
1042             SKGOperationObject op;
1043             err = obj.getParentOperation(op);
1044             IFOK(err) {
1045                 QDomElement scheduled_tx = doc.createElement(QStringLiteral("SCHEDULED_TX"));
1046                 schedules.appendChild(scheduled_tx);
1047 
1048                 scheduled_tx.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(obj));
1049                 scheduled_tx.setAttribute(QStringLiteral("name"), getKmyUniqueIdentifier(obj));
1050                 scheduled_tx.setAttribute(QStringLiteral("startDate"), obj.getAttribute(QStringLiteral("d_date")));
1051                 scheduled_tx.setAttribute(QStringLiteral("lastPayment"), obj.getAttribute(QStringLiteral("d_date")));
1052                 bool autoEnter = obj.isAutoWriteEnabled();
1053                 scheduled_tx.setAttribute(QStringLiteral("autoEnter"), autoEnter  ? QStringLiteral("1") : QStringLiteral("0"));
1054 
1055                 QString occuType;
1056                 int occu = obj.getPeriodIncrement();
1057                 SKGRecurrentOperationObject::PeriodUnit punit = obj.getPeriodUnit();
1058                 if (punit == SKGRecurrentOperationObject::MONTH) {
1059                     occuType = QStringLiteral("32");
1060                 } else if (punit == SKGRecurrentOperationObject::WEEK) {
1061                     occuType = '4';
1062                 } else if (punit == SKGRecurrentOperationObject::DAY) {
1063                     occuType = '2';
1064                 } else {
1065                     occuType = QStringLiteral("16384");
1066                 }
1067 
1068                 scheduled_tx.setAttribute(QStringLiteral("occurenceMultiplier"), SKGServices::intToString(occu));
1069                 scheduled_tx.setAttribute(QStringLiteral("occurence"), occuType);    // krazy:exclude=spelling
1070                 scheduled_tx.setAttribute(QStringLiteral("weekendOption"), QStringLiteral("0"));
1071                 scheduled_tx.setAttribute(QStringLiteral("paymentType"), QStringLiteral("1"));
1072                 QChar type = '1';
1073                 SKGOperationObject op2;
1074                 if (op.isTransfer(op2)) {
1075                     type = '4';
1076                 } else if (op.getCurrentAmount() > 0) {
1077                     type = '2';
1078                 }
1079                 scheduled_tx.setAttribute(QStringLiteral("type"), type);
1080                 scheduled_tx.setAttribute(QStringLiteral("fixed"), QStringLiteral("1"));
1081 
1082                 QString endDate;
1083                 if (obj.hasTimeLimit()) {
1084                     QDate firstDate = obj.getDate();
1085 
1086                     // We must compute the date
1087                     int p = occu * (obj.getTimeLimit() - 1);
1088                     if (punit == SKGRecurrentOperationObject::DAY) {
1089                         firstDate = firstDate.addDays(p);
1090                     } else if (punit == SKGRecurrentOperationObject::MONTH) {
1091                         firstDate = firstDate.addMonths(p);
1092                     } else if (punit == SKGRecurrentOperationObject::YEAR) {
1093                         firstDate = firstDate.addYears(p);
1094                     }
1095 
1096                     endDate = firstDate.toString(QStringLiteral("yyyy-MM-dd"));
1097                 }
1098                 scheduled_tx.setAttribute(QStringLiteral("endDate"), endDate);
1099 
1100                 err = exportOperation(op, doc, scheduled_tx);
1101             }
1102             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
1103         }
1104 
1105         SKGENDTRANSACTION(m_importer->getDocument(),  err)
1106     }
1107     return err;
1108 }
1109 
exportTransactions(QDomDocument & doc,QDomElement & root,const QString & stdUnit)1110 SKGError SKGImportPluginKmy::exportTransactions(QDomDocument& doc, QDomElement& root, const QString& stdUnit)
1111 {
1112     SKGError err;
1113     QDomElement transactions = doc.createElement(QStringLiteral("TRANSACTIONS"));
1114     root.appendChild(transactions);
1115 
1116     SKGObjectBase::SKGListSKGObjectBase objects;
1117     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_operation"), QStringLiteral("t_template='N' ORDER BY d_date DESC"), objects))
1118     int nb = objects.count();
1119     transactions.setAttribute(QStringLiteral("count"), SKGServices::intToString(nb));
1120     IFOK(err) {
1121         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export operations"), nb);
1122         for (int i = 0; !err && i < nb; ++i) {
1123             SKGOperationObject op(objects.at(i));
1124             err = exportOperation(op, doc, transactions);
1125             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
1126         }
1127 
1128         SKGENDTRANSACTION(m_importer->getDocument(),  err)
1129     }
1130 
1131     // <KEYVALUEPAIRS>
1132     QDomElement keyvaluepairs = doc.createElement(QStringLiteral("KEYVALUEPAIRS"));
1133     root.appendChild(keyvaluepairs);
1134     {
1135         QDomElement pair = doc.createElement(QStringLiteral("PAIR"));
1136         keyvaluepairs.appendChild(pair);
1137         pair.setAttribute(QStringLiteral("key"), QStringLiteral("kmm-baseCurrency"));
1138         pair.setAttribute(QStringLiteral("value"), stdUnit);
1139     }
1140     return err;
1141 }
1142 
exportPayees(QDomDocument & doc,QDomElement & root)1143 SKGError SKGImportPluginKmy::exportPayees(QDomDocument& doc, QDomElement& root)
1144 {
1145     SKGError err;
1146     QDomElement payees = doc.createElement(QStringLiteral("PAYEES"));
1147     root.appendChild(payees);
1148 
1149     SKGObjectBase::SKGListSKGObjectBase listPayees;
1150     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_payee"), QString(), listPayees))
1151     int nb = listPayees.count();
1152     payees.setAttribute(QStringLiteral("count"), SKGServices::intToString(nb));
1153     IFOK(err) {
1154         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export payees"), nb);
1155         for (int i = 0; !err && i < nb; ++i) {
1156             SKGPayeeObject payeeObject(listPayees.at(i));
1157             QDomElement payee = doc.createElement(QStringLiteral("PAYEE"));
1158             payees.appendChild(payee);
1159 
1160             payee.setAttribute(QStringLiteral("matchingenabled"), QStringLiteral("0"));
1161             payee.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(payeeObject));
1162             payee.setAttribute(QStringLiteral("name"), payeeObject.getName());
1163             payee.setAttribute(QStringLiteral("email"), QString());
1164             payee.setAttribute(QStringLiteral("reference"), QString());
1165 
1166             QDomElement address = doc.createElement(QStringLiteral("ADDRESS"));
1167             payee.appendChild(address);
1168 
1169             address.setAttribute(QStringLiteral("street"), payeeObject.getAddress());
1170             address.setAttribute(QStringLiteral("postcode"), QString());
1171             address.setAttribute(QStringLiteral("zip"), QString());
1172             address.setAttribute(QStringLiteral("city"), QString());
1173             address.setAttribute(QStringLiteral("telephone"), QString());
1174             address.setAttribute(QStringLiteral("state"), QString());
1175 
1176             m_mapIdPayee[SKGServices::intToString(i + 1)] = payeeObject;
1177 
1178             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
1179         }
1180 
1181         SKGENDTRANSACTION(m_importer->getDocument(),  err)
1182     }
1183     return err;
1184 }
1185 
exportInstitutions(QDomDocument & doc,QDomElement & root)1186 SKGError SKGImportPluginKmy::exportInstitutions(QDomDocument& doc, QDomElement& root)
1187 {
1188     SKGError err;
1189     QDomElement institutions = doc.createElement(QStringLiteral("INSTITUTIONS"));
1190     root.appendChild(institutions);
1191     SKGObjectBase::SKGListSKGObjectBase objects;
1192     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_bank"), QStringLiteral("EXISTS(SELECT 1 FROM account WHERE account.rd_bank_id=v_bank.id)"), objects))
1193     int nb = objects.count();
1194     institutions.setAttribute(QStringLiteral("count"), SKGServices::intToString(nb));
1195     IFOK(err) {
1196         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export banks"), nb);
1197         for (int i = 0; !err && i < nb; ++i) {
1198             SKGBankObject obj(objects.at(i));
1199             QDomElement institution = doc.createElement(QStringLiteral("INSTITUTION"));
1200             institutions.appendChild(institution);
1201 
1202             institution.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(obj));
1203             institution.setAttribute(QStringLiteral("name"), obj.getName());
1204             institution.setAttribute(QStringLiteral("sortcode"), obj.getNumber());
1205             institution.setAttribute(QStringLiteral("manager"), QString());
1206 
1207             QDomElement address = doc.createElement(QStringLiteral("ADDRESS"));
1208             institution.appendChild(address);
1209 
1210             address.setAttribute(QStringLiteral("street"), QString());
1211             address.setAttribute(QStringLiteral("zip"), QString());
1212             address.setAttribute(QStringLiteral("city"), QString());
1213             address.setAttribute(QStringLiteral("telephone"), QString());
1214 
1215             QDomElement accountids = doc.createElement(QStringLiteral("ACCOUNTIDS"));
1216             institution.appendChild(accountids);
1217 
1218             SKGObjectBase::SKGListSKGObjectBase accounts;
1219             err = obj.getAccounts(accounts);
1220             int nb2 = accounts.count();
1221             for (int j = 0; !err && j < nb2; ++j) {
1222                 QDomElement accountid = doc.createElement(QStringLiteral("ACCOUNTID"));
1223                 accountids.appendChild(accountid);
1224 
1225                 accountid.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(accounts.at(j)));
1226             }
1227             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
1228         }
1229 
1230         SKGENDTRANSACTION(m_importer->getDocument(),  err)
1231     }
1232 
1233     return err;
1234 }
1235 
exportCategories(QDomDocument & doc,QDomElement & accounts,const QString & stdUnit,QDomElement & accountIncome,QDomElement & accountExpense,int nbAccount)1236 SKGError SKGImportPluginKmy::exportCategories(QDomDocument& doc, QDomElement& accounts, const QString& stdUnit, QDomElement& accountIncome, QDomElement& accountExpense, int nbAccount)
1237 {
1238     // The v_category_display are retrieved to improve performances of getCurrentAmount
1239     SKGError err;
1240     SKGObjectBase::SKGListSKGObjectBase objects;
1241     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_category_display"), QString(), objects))
1242     accounts.setAttribute(QStringLiteral("count"), SKGServices::intToString(5 + nbAccount + objects.count()));
1243     int nb = objects.count();
1244     IFOK(err) {
1245         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export categories"), nb);
1246         for (int i = 0; !err && i < nb; ++i) {
1247             SKGCategoryObject obj(objects.at(i));
1248             QDomElement account = doc.createElement(QStringLiteral("ACCOUNT"));
1249             accounts.appendChild(account);
1250 
1251             account.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(obj));
1252             account.setAttribute(QStringLiteral("name"), obj.getName());
1253             account.setAttribute(QStringLiteral("number"), QString());
1254             account.setAttribute(QStringLiteral("type"), obj.getCurrentAmount() < 0 ? QStringLiteral("13") : QStringLiteral("12"));
1255 
1256             account.setAttribute(QStringLiteral("institution"), QString());
1257 
1258             SKGCategoryObject parentCat;
1259             obj.getParentCategory(parentCat);
1260 
1261             QString parentId = (parentCat.getID() != 0 ? getKmyUniqueIdentifier(parentCat) : (obj.getCurrentAmount() < 0 ? QStringLiteral("AStd::Expense") : QStringLiteral("AStd::Income")));
1262             if (parentId == QStringLiteral("AStd::Expense")) {
1263                 QDomElement subaccount = doc.createElement(QStringLiteral("SUBACCOUNT"));
1264                 accountExpense.appendChild(subaccount);
1265                 subaccount.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(obj));
1266             } else if (parentId == QStringLiteral("AStd::Income")) {
1267                 QDomElement subaccount = doc.createElement(QStringLiteral("SUBACCOUNT"));
1268                 accountIncome.appendChild(subaccount);
1269                 subaccount.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(obj));
1270             }
1271 
1272             account.setAttribute(QStringLiteral("parentaccount"), parentId);
1273             account.setAttribute(QStringLiteral("lastmodified"), QString());
1274             account.setAttribute(QStringLiteral("lastreconciled"), QString());
1275             account.setAttribute(QStringLiteral("opened"), QString());
1276             account.setAttribute(QStringLiteral("currency"), stdUnit);
1277             account.setAttribute(QStringLiteral("description"), QString());
1278 
1279             QDomElement subaccounts = doc.createElement(QStringLiteral("SUBACCOUNTS"));
1280             account.appendChild(subaccounts);
1281 
1282             SKGObjectBase::SKGListSKGObjectBase categories;
1283             IFOKDO(err, obj.getCategories(categories))
1284             int nb2 = categories.count();
1285             for (int j = 0; !err && j < nb2; ++j) {
1286                 QDomElement subaccount = doc.createElement(QStringLiteral("SUBACCOUNT"));
1287                 subaccounts.appendChild(subaccount);
1288 
1289                 subaccount.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(categories.at(j)));
1290             }
1291             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
1292         }
1293 
1294         SKGENDTRANSACTION(m_importer->getDocument(),  err)
1295     }
1296     return err;
1297 }
1298 
exportAccounts(QDomDocument & doc,QDomElement & root,const QString & stdUnit,QDomElement & accounts,QDomElement & accountIncome,QDomElement & accountExpense,int & nbAccounts)1299 SKGError SKGImportPluginKmy::exportAccounts(QDomDocument& doc, QDomElement& root, const QString& stdUnit, QDomElement& accounts, QDomElement& accountIncome, QDomElement& accountExpense, int& nbAccounts)
1300 {
1301     SKGError err;
1302     accounts = doc.createElement(QStringLiteral("ACCOUNTS"));
1303     root.appendChild(accounts);
1304 
1305     QDomElement accountAsset;
1306     {
1307         QDomElement account = doc.createElement(QStringLiteral("ACCOUNT"));
1308         accounts.appendChild(account);
1309 
1310         account.setAttribute(QStringLiteral("id"), QStringLiteral("AStd::Equity"));
1311         account.setAttribute(QStringLiteral("name"), QStringLiteral("Equity"));
1312         account.setAttribute(QStringLiteral("number"), QString());
1313         account.setAttribute(QStringLiteral("type"), QStringLiteral("16"));
1314         account.setAttribute(QStringLiteral("institution"), QString());
1315         account.setAttribute(QStringLiteral("parentaccount"), QString());
1316         account.setAttribute(QStringLiteral("lastmodified"), QString());
1317         account.setAttribute(QStringLiteral("lastreconciled"), QString());
1318         account.setAttribute(QStringLiteral("opened"), QString());
1319         account.setAttribute(QStringLiteral("currency"), stdUnit);
1320         account.setAttribute(QStringLiteral("description"), QString());
1321 
1322         QDomElement subaccounts = doc.createElement(QStringLiteral("SUBACCOUNTS"));
1323         account.appendChild(subaccounts);
1324     }
1325 
1326     {
1327         QDomElement account = doc.createElement(QStringLiteral("ACCOUNT"));
1328         accounts.appendChild(account);
1329 
1330         account.setAttribute(QStringLiteral("id"), QStringLiteral("AStd::Asset"));
1331         account.setAttribute(QStringLiteral("name"), QStringLiteral("Asset"));
1332         account.setAttribute(QStringLiteral("number"), QString());
1333         account.setAttribute(QStringLiteral("type"), QStringLiteral("9"));
1334         account.setAttribute(QStringLiteral("institution"), QString());
1335         account.setAttribute(QStringLiteral("parentaccount"), QString());
1336         account.setAttribute(QStringLiteral("lastmodified"), QString());
1337         account.setAttribute(QStringLiteral("lastreconciled"), QString());
1338         account.setAttribute(QStringLiteral("opened"), QString());
1339         account.setAttribute(QStringLiteral("currency"), stdUnit);
1340         account.setAttribute(QStringLiteral("description"), QString());
1341 
1342         QDomElement subaccounts = doc.createElement(QStringLiteral("SUBACCOUNTS"));
1343         account.appendChild(subaccounts);
1344         accountAsset = subaccounts;
1345     }
1346 
1347     {
1348         QDomElement account = doc.createElement(QStringLiteral("ACCOUNT"));
1349         accounts.appendChild(account);
1350 
1351         account.setAttribute(QStringLiteral("id"), QStringLiteral("AStd::Liability"));
1352         account.setAttribute(QStringLiteral("name"), QStringLiteral("Liability"));
1353         account.setAttribute(QStringLiteral("number"), QString());
1354         account.setAttribute(QStringLiteral("type"), QStringLiteral("10"));
1355         account.setAttribute(QStringLiteral("institution"), QString());
1356         account.setAttribute(QStringLiteral("parentaccount"), QString());
1357         account.setAttribute(QStringLiteral("lastmodified"), QString());
1358         account.setAttribute(QStringLiteral("lastreconciled"), QString());
1359         account.setAttribute(QStringLiteral("opened"), QString());
1360         account.setAttribute(QStringLiteral("currency"), stdUnit);
1361         account.setAttribute(QStringLiteral("description"), QString());
1362 
1363         QDomElement subaccounts = doc.createElement(QStringLiteral("SUBACCOUNTS"));
1364         account.appendChild(subaccounts);
1365     }
1366 
1367     {
1368         QDomElement account = doc.createElement(QStringLiteral("ACCOUNT"));
1369         accounts.appendChild(account);
1370 
1371         account.setAttribute(QStringLiteral("id"), QStringLiteral("AStd::Income"));
1372         account.setAttribute(QStringLiteral("name"), QStringLiteral("Income"));
1373         account.setAttribute(QStringLiteral("number"), QString());
1374         account.setAttribute(QStringLiteral("type"), QStringLiteral("12"));
1375         account.setAttribute(QStringLiteral("institution"), QString());
1376         account.setAttribute(QStringLiteral("parentaccount"), QString());
1377         account.setAttribute(QStringLiteral("lastmodified"), QString());
1378         account.setAttribute(QStringLiteral("lastreconciled"), QString());
1379         account.setAttribute(QStringLiteral("opened"), QString());
1380         account.setAttribute(QStringLiteral("currency"), stdUnit);
1381         account.setAttribute(QStringLiteral("description"), QString());
1382 
1383         QDomElement subaccounts = doc.createElement(QStringLiteral("SUBACCOUNTS"));
1384         account.appendChild(subaccounts);
1385         accountIncome = subaccounts;
1386     }
1387 
1388     {
1389         QDomElement account = doc.createElement(QStringLiteral("ACCOUNT"));
1390         accounts.appendChild(account);
1391 
1392         account.setAttribute(QStringLiteral("id"), QStringLiteral("AStd::Expense"));
1393         account.setAttribute(QStringLiteral("name"), QStringLiteral("Expense"));
1394         account.setAttribute(QStringLiteral("number"), QString());
1395         account.setAttribute(QStringLiteral("type"), QStringLiteral("13"));
1396         account.setAttribute(QStringLiteral("institution"), QString());
1397         account.setAttribute(QStringLiteral("parentaccount"), QString());
1398         account.setAttribute(QStringLiteral("lastmodified"), QString());
1399         account.setAttribute(QStringLiteral("lastreconciled"), QString());
1400         account.setAttribute(QStringLiteral("opened"), QString());
1401         account.setAttribute(QStringLiteral("currency"), stdUnit);
1402         account.setAttribute(QStringLiteral("description"), QString());
1403 
1404         QDomElement subaccounts = doc.createElement(QStringLiteral("SUBACCOUNTS"));
1405         account.appendChild(subaccounts);
1406         accountExpense = subaccounts;
1407     }
1408 
1409     SKGObjectBase::SKGListSKGObjectBase objects;
1410     IFOKDO(err, m_importer->getDocument()->getObjects(QStringLiteral("v_account"), QString(), objects))
1411     int nb = objects.count();
1412     IFOK(err) {
1413         err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export accounts"), nb);
1414         for (int i = 0; !err && i < nb; ++i) {
1415             SKGAccountObject obj(objects.at(i));
1416             QDomElement account = doc.createElement(QStringLiteral("ACCOUNT"));
1417             accounts.appendChild(account);
1418 
1419             account.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(obj));
1420             account.setAttribute(QStringLiteral("name"), obj.getName());
1421             account.setAttribute(QStringLiteral("number"), obj.getNumber());
1422             account.setAttribute(QStringLiteral("type"), obj.getType() == SKGAccountObject::CREDITCARD ? QStringLiteral("4") :
1423                                  (obj.getType() == SKGAccountObject::INVESTMENT ? QStringLiteral("7") :
1424                                   (obj.getType() == SKGAccountObject::ASSETS ? QStringLiteral("9") :
1425                                    (obj.getType() == SKGAccountObject::WALLET ? QStringLiteral("3") :
1426                                     (obj.getType() == SKGAccountObject::LOAN ? QStringLiteral("10") :
1427                                      QStringLiteral("1"))))));
1428 
1429             SKGBankObject bank;
1430             err = obj.getBank(bank);
1431             account.setAttribute(QStringLiteral("institution"), getKmyUniqueIdentifier(bank));
1432 
1433             account.setAttribute(QStringLiteral("parentaccount"), QStringLiteral("AStd::Asset"));
1434             account.setAttribute(QStringLiteral("lastmodified"), QString());
1435             account.setAttribute(QStringLiteral("lastreconciled"), QString());
1436             account.setAttribute(QStringLiteral("opened"), QString());
1437             SKGUnitObject unit;
1438             obj.getUnit(unit);
1439             QString unitS = SKGUnitObject::getInternationalCode(unit.getName());
1440             if (unitS.isEmpty()) {
1441                 unitS = QStringLiteral("EUR");
1442             }
1443             account.setAttribute(QStringLiteral("currency"), unitS);
1444             account.setAttribute(QStringLiteral("description"), QString());
1445 
1446             // Bookmarked account
1447             QDomElement keyvaluepairs = doc.createElement(QStringLiteral("KEYVALUEPAIRS"));
1448             account.appendChild(keyvaluepairs);
1449             if (obj.isBookmarked()) {
1450                 QDomElement pair = doc.createElement(QStringLiteral("PAIR"));
1451                 keyvaluepairs.appendChild(pair);
1452                 pair.setAttribute(QStringLiteral("key"), QStringLiteral("PreferredAccount"));
1453                 pair.setAttribute(QStringLiteral("value"), QStringLiteral("Yes"));
1454             }
1455             // Closed account
1456             if (obj.isClosed()) {
1457                 QDomElement pair = doc.createElement(QStringLiteral("PAIR"));
1458                 keyvaluepairs.appendChild(pair);
1459                 pair.setAttribute(QStringLiteral("key"), QStringLiteral("mm-closed"));
1460                 pair.setAttribute(QStringLiteral("value"), QStringLiteral("yes"));
1461             }
1462 
1463             // Maximum and minimum limits
1464             if (obj.isMaxLimitAmountEnabled()) {
1465                 QDomElement pair = doc.createElement(QStringLiteral("PAIR"));
1466                 keyvaluepairs.appendChild(pair);
1467                 pair.setAttribute(QStringLiteral("key"), QStringLiteral("maxCreditAbsolute"));
1468                 pair.setAttribute(QStringLiteral("value"), kmyValue(-obj.getMaxLimitAmount()));
1469             }
1470             if (obj.isMinLimitAmountEnabled()) {
1471                 QDomElement pair = doc.createElement(QStringLiteral("PAIR"));
1472                 keyvaluepairs.appendChild(pair);
1473                 pair.setAttribute(QStringLiteral("key"), QStringLiteral("minBalanceAbsolute"));
1474                 pair.setAttribute(QStringLiteral("value"), kmyValue(obj.getMinLimitAmount()));
1475             }
1476 
1477             // Add it in asset
1478             QDomElement subaccount = doc.createElement(QStringLiteral("SUBACCOUNT"));
1479             accountAsset.appendChild(subaccount);
1480             subaccount.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(obj));
1481 
1482             IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
1483         }
1484 
1485         SKGENDTRANSACTION(m_importer->getDocument(),  err)
1486     }
1487 
1488     nbAccounts = nb;
1489     return err;
1490 }
1491 
exportFile()1492 SKGError SKGImportPluginKmy::exportFile()
1493 {
1494     // Initialisation
1495     m_opTreated.clear();
1496 
1497     if (m_importer == nullptr) {
1498         return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
1499     }
1500     SKGError err;
1501     SKGTRACEINFUNCRC(2, err)
1502 
1503     // Open file
1504     KCompressionDevice file(m_importer->getLocalFileName(false), KCompressionDevice::GZip);
1505     if (!file.open(QIODevice::WriteOnly)) {
1506         err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Save file '%1' failed", m_importer->getFileName().toDisplayString()));
1507     } else {
1508         QDomDocument doc(QStringLiteral("KMYMONEY-FILE"));
1509         QDomComment comment = doc.createComment(QStringLiteral("Generated by libskgbankmodeler"));
1510         doc.appendChild(comment);
1511 
1512         QDomElement root = doc.createElement(QStringLiteral("KMYMONEY-FILE"));
1513         doc.appendChild(root);
1514         {
1515             err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Export step", "Export %1 file", "KMY"), 8);
1516             IFOK(err) {
1517                 // Step 1-<FILEINFO>
1518                 IFOKDO(err, exportHeader(doc, root))
1519                 IFOKDO(err, m_importer->getDocument()->stepForward(1))
1520 
1521                 // // Step 2-<INSTITUTIONS>
1522                 IFOKDO(err, exportInstitutions(doc, root))
1523                 IFOKDO(err, m_importer->getDocument()->stepForward(2))
1524 
1525                 // Step 3-<PAYEES>
1526                 IFOKDO(err, exportPayees(doc, root))
1527                 IFOKDO(err, m_importer->getDocument()->stepForward(3))
1528 
1529                 // Step 4-<ACCOUNTS>
1530                 // Std accounts
1531                 QString stdUnit = SKGUnitObject::getInternationalCode(m_importer->getDocument()->getPrimaryUnit().Name);
1532                 if (stdUnit.isEmpty()) {
1533                     stdUnit = QStringLiteral("EUR");
1534                 }
1535 
1536                 QDomElement accountIncome;
1537                 QDomElement accountExpense;
1538                 QDomElement accounts;
1539 
1540                 int nbAccounts = 0;
1541                 IFOKDO(err, exportAccounts(doc, root, stdUnit, accounts, accountIncome, accountExpense, nbAccounts))
1542                 IFOKDO(err, m_importer->getDocument()->stepForward(4))
1543 
1544                 // Step 5
1545                 IFOKDO(err, exportCategories(doc, accounts, stdUnit, accountIncome, accountExpense, nbAccounts))
1546                 IFOKDO(err, m_importer->getDocument()->stepForward(5))
1547 
1548                 // Step 6-<TRANSACTIONS>
1549                 IFOKDO(err, exportTransactions(doc, root, stdUnit))
1550 
1551                 // Step 6-<SCHEDULES>
1552                 IFOKDO(err, exportSchedules(doc, root))
1553                 IFOKDO(err, m_importer->getDocument()->stepForward(6))
1554 
1555                 // Step 7-<BUDGETS>
1556                 IFOKDO(err, exportBudgets(doc, root))
1557                 IFOKDO(err, m_importer->getDocument()->stepForward(7))
1558 
1559                 // Step 8-<SECURITIES> and <CURRENCIES>
1560                 IFOKDO(err, exportSecurities(doc, root, stdUnit))
1561 
1562                 // Save file
1563                 IFOK(err) {
1564                     file.write(doc.toString().toUtf8());
1565                 }
1566                 IFOKDO(err, m_importer->getDocument()->stepForward(8))
1567             }
1568 
1569             SKGENDTRANSACTION(m_importer->getDocument(),  err)
1570         }
1571 
1572         file.close();
1573     }
1574 
1575     // Clean
1576     m_opTreated.clear();
1577 
1578     return err;
1579 }
1580 
exportOperation(const SKGOperationObject & iOperation,QDomDocument & iDoc,QDomElement & iTransaction)1581 SKGError SKGImportPluginKmy::exportOperation(const SKGOperationObject& iOperation, QDomDocument& iDoc, QDomElement& iTransaction)
1582 {
1583     SKGError err;
1584     SKGTRACEINFUNCRC(2, err)
1585     if (!m_opTreated.contains(getKmyUniqueIdentifier(iOperation))) {
1586         QDomElement transaction = iDoc.createElement(QStringLiteral("TRANSACTION"));
1587         iTransaction.appendChild(transaction);
1588 
1589         SKGUnitObject unit;
1590         iOperation.getUnit(unit);
1591 
1592         QString date = iOperation.getAttribute(QStringLiteral("d_date"));
1593         transaction.setAttribute(QStringLiteral("id"), getKmyUniqueIdentifier(iOperation));
1594         transaction.setAttribute(QStringLiteral("entrydate"), date);
1595         transaction.setAttribute(QStringLiteral("postdate"), date);
1596         transaction.setAttribute(QStringLiteral("memo"), iOperation.getComment());
1597         transaction.setAttribute(QStringLiteral("commodity"), SKGUnitObject::getInternationalCode(unit.getName()));
1598 
1599         QString reconcileflag = (iOperation.getStatus() == SKGOperationObject::POINTED ? QStringLiteral("1") : (iOperation.getStatus() == SKGOperationObject::CHECKED ? QStringLiteral("2") : QStringLiteral("0")));
1600 
1601         SKGAccountObject act;
1602         IFOKDO(err, iOperation.getParentAccount(act))
1603 
1604         QDomElement splits = iDoc.createElement(QStringLiteral("SPLITS"));
1605         transaction.appendChild(splits);
1606 
1607         QDomElement split = iDoc.createElement(QStringLiteral("SPLIT"));
1608         splits.appendChild(split);
1609 
1610         SKGPayeeObject payeeObject;
1611         iOperation.getPayee(payeeObject);
1612         QString payeeId = (payeeObject.getID() != 0 ? getKmyUniqueIdentifier(payeeObject) : QString());
1613 
1614         int indexSubOp = 1;
1615 
1616         // Split for account
1617         split.setAttribute(QStringLiteral("payee"), payeeId);
1618         split.setAttribute(QStringLiteral("reconciledate"), QString());
1619         split.setAttribute(QStringLiteral("id"), "S" % SKGServices::intToString(indexSubOp++).rightJustified(4, '0'));
1620         double val2 = SKGServices::stringToDouble(iOperation.getAttribute(QStringLiteral("f_QUANTITY")));
1621         split.setAttribute(QStringLiteral("shares"), SKGImportPluginKmy::kmyValue(val2));
1622         split.setAttribute(QStringLiteral("action"), QString());
1623         split.setAttribute(QStringLiteral("bankid"), QString());
1624         split.setAttribute(QStringLiteral("number"), iOperation.getNumber());
1625         split.setAttribute(QStringLiteral("reconcileflag"), reconcileflag);
1626         split.setAttribute(QStringLiteral("memo"), iOperation.getComment());
1627         QString originalAmount = iOperation.getProperty(QStringLiteral("SKG_OP_ORIGINAL_AMOUNT"));
1628         if (!originalAmount.isEmpty()) {
1629             val2 = qAbs(SKGServices::stringToDouble(originalAmount)) * (val2 / qAbs(val2));
1630         }
1631         split.setAttribute(QStringLiteral("value"), SKGImportPluginKmy::kmyValue(val2));
1632         split.setAttribute(QStringLiteral("account"), getKmyUniqueIdentifier(act));
1633 
1634         SKGOperationObject obj2;
1635         if (!err && iOperation.isTransfer(obj2)) {
1636             // It is a Transfer
1637             QString reconcileflag2 = (obj2.getStatus() == SKGOperationObject::POINTED ? QStringLiteral("1") : (obj2.getStatus() == SKGOperationObject::CHECKED ? QStringLiteral("2") : QStringLiteral("0")));
1638 
1639             SKGAccountObject act2;
1640             IFOKDO(err, obj2.getParentAccount(act2))
1641 
1642             QDomElement split2 = iDoc.createElement(QStringLiteral("SPLIT"));
1643             splits.appendChild(split2);
1644 
1645             // Split for account
1646             val2 = -val2;
1647             split2.setAttribute(QStringLiteral("payee"), payeeId);
1648             split2.setAttribute(QStringLiteral("reconciledate"), QString());
1649             split2.setAttribute(QStringLiteral("id"), "S" % SKGServices::intToString(indexSubOp++).rightJustified(4, '0'));
1650             split2.setAttribute(QStringLiteral("shares"), SKGImportPluginKmy::kmyValue(SKGServices::stringToDouble(obj2.getAttribute(QStringLiteral("f_QUANTITY")))));
1651             split2.setAttribute(QStringLiteral("action"), QString());
1652             split2.setAttribute(QStringLiteral("bankid"), QString());
1653             split2.setAttribute(QStringLiteral("number"), obj2.getNumber());
1654             split2.setAttribute(QStringLiteral("reconcileflag"), reconcileflag2);
1655             split2.setAttribute(QStringLiteral("memo"), obj2.getComment());
1656             split2.setAttribute(QStringLiteral("value"), SKGImportPluginKmy::kmyValue(val2));
1657             split2.setAttribute(QStringLiteral("account"), getKmyUniqueIdentifier(act2));
1658 
1659             m_opTreated.insert(getKmyUniqueIdentifier(obj2));
1660         } else {
1661             SKGObjectBase::SKGListSKGObjectBase subops;
1662             IFOKDO(err, iOperation.getSubOperations(subops))
1663             int nb2 = subops.count();
1664             for (int j = 0; !err && j < nb2; ++j) {
1665                 QDomElement split2 = iDoc.createElement(QStringLiteral("SPLIT"));
1666                 splits.appendChild(split2);
1667 
1668                 SKGSubOperationObject subop(subops.at(j));
1669                 SKGCategoryObject cat;
1670                 subop.getCategory(cat);
1671                 split2.setAttribute(QStringLiteral("payee"), payeeId);
1672                 split2.setAttribute(QStringLiteral("reconciledate"), QString());
1673                 split2.setAttribute(QStringLiteral("id"), "S" % SKGServices::intToString(indexSubOp++).rightJustified(4, '0'));
1674                 QString shape3 = SKGImportPluginKmy::kmyValue(-subop.getQuantity());
1675                 split2.setAttribute(QStringLiteral("shares"), shape3);
1676                 split2.setAttribute(QStringLiteral("action"), QString());
1677                 split2.setAttribute(QStringLiteral("bankid"), QString());
1678                 split2.setAttribute(QStringLiteral("number"), iOperation.getNumber());
1679                 split2.setAttribute(QStringLiteral("reconcileflag"), reconcileflag);
1680                 split2.setAttribute(QStringLiteral("memo"), subop.getComment());
1681                 split2.setAttribute(QStringLiteral("value"), shape3);
1682                 split2.setAttribute(QStringLiteral("account"), date == QStringLiteral("0000-00-00") ? QStringLiteral("AStd::Equity") : (cat.getID() != 0 ? getKmyUniqueIdentifier(cat) : QString()));
1683             }
1684         }
1685 
1686         m_opTreated.insert(getKmyUniqueIdentifier(iOperation));
1687     }
1688     return err;
1689 }
1690 
kmyValue(double iValue)1691 QString SKGImportPluginKmy::kmyValue(double iValue)
1692 {
1693     QString output;
1694     for (int i = 0; output.isEmpty() && i < 11; ++i) {
1695         QString d = SKGServices::doubleToString(pow(10, i) * iValue);
1696         if (d.indexOf('.') == -1) {
1697             output = d % '/' % SKGServices::intToString(qPow(10, i));
1698         }
1699     }
1700     return output;
1701 }
1702 
toKmyValue(const QString & iString)1703 double SKGImportPluginKmy::toKmyValue(const QString& iString)
1704 {
1705     double output = 0;
1706     QStringList vals = SKGServices::splitCSVLine(iString, '/');
1707     if (vals.count() == 1) {
1708         output = SKGServices::stringToDouble(vals.at(0));
1709     } else if (vals.count() == 2) {
1710         output = SKGServices::stringToDouble(vals.at(0)) / SKGServices::stringToDouble(vals.at(1));
1711     }
1712     return output;
1713 }
1714 
getKmyUniqueIdentifier(const SKGObjectBase & iObject)1715 QString SKGImportPluginKmy::getKmyUniqueIdentifier(const SKGObjectBase& iObject)
1716 {
1717     QString id;
1718     if (iObject.getID() != 0) {
1719         QString table = iObject.getRealTable();
1720         if (table == QStringLiteral("operation") || table == QStringLiteral("suboperation")) {
1721             // T000000000000003623
1722             id = 'T' % SKGServices::intToString(iObject.getID()).rightJustified(18, '0');
1723         } else if (table == QStringLiteral("payee")) {
1724             // P000030
1725             id = 'P' % SKGServices::intToString(iObject.getID()).rightJustified(6, '0');
1726         } else {
1727             id = iObject.getUniqueID();
1728         }
1729     }
1730     return id;
1731 }
1732 
getMimeTypeFilter() const1733 QString SKGImportPluginKmy::getMimeTypeFilter() const
1734 {
1735     return "*.kmy|" % i18nc("A file format", "KMyMoney document");
1736 }
1737 
1738 #include <skgimportpluginkmy.moc>
1739