File indexing completed on 2025-02-02 04:57:03

0001 /***************************************************************************
0002  * SPDX-FileCopyrightText: 2022 S. MANKOWSKI stephane@mankowski.fr
0003  * SPDX-FileCopyrightText: 2022 G. DE BURE support@mankowski.fr
0004  * SPDX-License-Identifier: GPL-3.0-or-later
0005  ***************************************************************************/
0006 /** @file
0007  * This file is Skrooge plugin for XML import / export.
0008  *
0009  * @author Stephane MANKOWSKI / Guillaume DE BURE
0010  */
0011 #include "skgimportpluginxml.h"
0012 
0013 #include <klocalizedstring.h>
0014 #include <kpluginfactory.h>
0015 
0016 #include <qsavefile.h>
0017 #include <qfile.h>
0018 #include <QtXmlPatterns/qxmlquery.h>
0019 #include <qstandardpaths.h>
0020 
0021 #include "skgbankincludes.h"
0022 #include "skgservices.h"
0023 #include "skgtraces.h"
0024 
0025 /**
0026  * This plugin factory.
0027  */
0028 K_PLUGIN_CLASS_WITH_JSON(SKGImportPluginXml, "metadata.json")
0029 
0030 SKGImportPluginXml::SKGImportPluginXml(QObject* iImporter, const QVariantList& iArg)
0031     : SKGImportPlugin(iImporter)
0032 {
0033     SKGTRACEINFUNC(10)
0034     Q_UNUSED(iArg)
0035 }
0036 
0037 SKGImportPluginXml::~SKGImportPluginXml()
0038     = default;
0039 
0040 bool SKGImportPluginXml::isImportPossible()
0041 {
0042     SKGTRACEINFUNC(10)
0043     if (m_importer->getDocument() == nullptr) {
0044         return true;
0045     }
0046     if (m_importer->getFileNameExtension() != QStringLiteral("XML")) {
0047         return false;
0048     }
0049     return true;
0050 }
0051 
0052 
0053 SKGError SKGImportPluginXml::importFile()
0054 {
0055     if (m_importer->getDocument() == nullptr) {
0056         return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
0057     }
0058     SKGError err;
0059     SKGTRACEINFUNCRC(2, err)
0060 
0061     // Determine the XML format
0062     QString format;
0063     QDomElement docElem;
0064     IFOKDO(err, m_importer->getXMLDocument(docElem))
0065     IFOK(err) {
0066         if (docElem.tagName() == "skrooge") {
0067             // SKROOGE XML FORMAT
0068             format = "XML";
0069         } else if (docElem.tagName() == "Document") {
0070             // ISO20022 XML FORMAT
0071             QFile file(m_importer->getLocalFileName());
0072             if (!file.open(QIODevice::ReadOnly)) {
0073                 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Open file '%1' failed", m_importer->getFileName().toDisplayString()));
0074             } else {
0075                 QTextStream xmlStream(file.readAll(), QIODevice::ReadOnly);
0076                 QString xml = xmlStream.readAll().replace(QRegularExpression("<Document[^>]*>"), "<Document>");
0077                 QString out;
0078                 QXmlQuery query(QXmlQuery::XSLT20);
0079                 query.setFocus(xml);
0080                 query.setQuery(QUrl::fromLocalFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("skrooge/ISO20022.xslt"))));
0081                 query.evaluateTo(&out);
0082                 SKGTRACEL(1) << "OUT=" << out << Qt::endl;
0083 
0084                 // Set the file without uncompression
0085                 QString errorMsg;
0086                 int errorLine = 0;
0087                 int errorCol = 0;
0088                 QDomDocument doc;
0089                 bool contentOK = doc.setContent(out, &errorMsg, &errorLine, &errorCol);
0090 
0091                 if (!contentOK) {
0092                     err.setReturnCode(ERR_ABORT).setMessage(i18nc("Error message",  "%1-%2: '%3'", errorLine, errorCol, errorMsg)).addError(ERR_INVALIDARG, i18nc("Error message",  "Invalid XML content in %1", out));
0093                 } else {
0094                     // Get root
0095                     format = "ISO20022";
0096                     docElem = doc.documentElement();
0097                 }
0098             }
0099         } else {
0100             err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Unknown XML format. Only Skrooge and ISO20022 XML formats are supported"));
0101         }
0102     }
0103 
0104     // Build list of items to import
0105     QList<QString> types;
0106     types.append(QStringLiteral("unit"));
0107     types.append(QStringLiteral("unitvalue"));
0108     types.append(QStringLiteral("bank"));
0109     types.append(QStringLiteral("account"));
0110     types.append(QStringLiteral("payee"));
0111     types.append(QStringLiteral("refund"));
0112     types.append(QStringLiteral("rule"));
0113     types.append(QStringLiteral("category"));
0114     types.append(QStringLiteral("budget"));
0115     types.append(QStringLiteral("budgetrule"));
0116     types.append(QStringLiteral("operation"));
0117     types.append(QStringLiteral("suboperation"));
0118     types.append(QStringLiteral("recurrentoperation"));
0119     types.append(QStringLiteral("interest"));
0120 
0121     auto nb = types.length();
0122     IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", format), nb))
0123     for (int i = 0; !err && i < nb; ++i) {
0124         auto type = types.at(i);
0125         QDomElement itemsList = docElem.firstChildElement(QStringLiteral("list_") + type);
0126         IFOKDO(err, m_importer->importItems(itemsList));
0127 
0128         IFOKDO(err, m_importer->getDocument()->stepForward(i + 1));
0129     }
0130 
0131     SKGENDTRANSACTION(m_importer->getDocument(),  err)
0132 
0133     IFOKDO(err, m_importer->getDocument()->executeSqliteOrder(QStringLiteral("ANALYZE")))
0134 
0135     return err;
0136 }
0137 
0138 bool SKGImportPluginXml::isExportPossible()
0139 {
0140     SKGTRACEINFUNC(10)
0141     return (m_importer->getDocument() == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("XML"));
0142 }
0143 
0144 SKGError SKGImportPluginXml::exportFile()
0145 {
0146     SKGError err;
0147     QDomDocument doc;
0148     QVector<QString> ignore;
0149     ignore.append(QStringLiteral("parameters"));
0150     ignore.append(QStringLiteral("doctransaction"));
0151     ignore.append(QStringLiteral("doctransactionitem"));
0152     ignore.append(QStringLiteral("doctransactionmsg"));
0153     ignore.append(QStringLiteral("operationbalance"));
0154     ignore.append(QStringLiteral("budgetsuboperation"));
0155     ignore.append(QStringLiteral("node"));
0156     ignore.append(QStringLiteral("interest_result"));
0157     ignore.append(QStringLiteral("category.t_fullname"));
0158     ignore.append(QStringLiteral("operation.i_tmp"));
0159     ignore.append(QStringLiteral("suboperation.i_tmp"));
0160     ignore.append(QStringLiteral("unit.f_CURRENTAMOUNT_CACHE"));
0161     err = SKGServices::copySqliteDatabaseToXml(*(m_importer->getDocument()->getMainDatabase()), doc, &ignore);
0162     IFOK(err) {
0163         QSaveFile file(m_importer->getLocalFileName(false));
0164         if (!file.open(QIODevice::WriteOnly)) {
0165             err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message",  "Save file '%1' failed", m_importer->getFileName().toDisplayString()));
0166         } else {
0167             QTextStream stream(&file);
0168             if (!m_importer->getCodec().isEmpty()) {
0169                 stream.setCodec(m_importer->getCodec().toLatin1().constData());
0170             }
0171             stream << doc.toString() << SKGENDL;
0172 
0173             // Close file
0174             file.commit();
0175         }
0176     }
0177     return err;
0178 }
0179 
0180 QString SKGImportPluginXml::getMimeTypeFilter() const
0181 {
0182     return "*.xml|" % i18nc("A file format", "XML file");
0183 }
0184 
0185 #include <skgimportpluginxml.moc>