File indexing completed on 2024-06-16 04:47:22
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 MT940 import / export. 0008 * 0009 * @author Stephane MANKOWSKI / Guillaume DE BURE 0010 */ 0011 #include "skgimportpluginmt940.h" 0012 0013 #include <qfile.h> 0014 0015 #include <klocalizedstring.h> 0016 #include <kpluginfactory.h> 0017 0018 #include "skgbankincludes.h" 0019 #include "skgimportexportmanager.h" 0020 #include "skgobjectbase.h" 0021 #include "skgservices.h" 0022 #include "skgtraces.h" 0023 0024 /** 0025 * This plugin factory. 0026 */ 0027 K_PLUGIN_CLASS_WITH_JSON(SKGImportPluginMT940, "metadata.json") 0028 0029 SKGImportPluginMT940::SKGImportPluginMT940(QObject* iImporter, const QVariantList& iArg) 0030 : SKGImportPlugin(iImporter) 0031 { 0032 SKGTRACEINFUNC(10) 0033 Q_UNUSED(iArg) 0034 } 0035 0036 SKGImportPluginMT940::~SKGImportPluginMT940() 0037 = default; 0038 0039 bool SKGImportPluginMT940::isImportPossible() 0040 { 0041 SKGTRACEINFUNC(10) 0042 return (m_importer->getDocument() == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("MT940") || m_importer->getFileNameExtension() == QStringLiteral("STA")); 0043 } 0044 0045 SKGError SKGImportPluginMT940::importFile() 0046 { 0047 if (m_importer->getDocument() == nullptr) { 0048 return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters")); 0049 } 0050 SKGError err; 0051 SKGTRACEINFUNCRC(2, err) 0052 0053 // Begin transaction 0054 err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "MT940"), 2); 0055 IFOK(err) { 0056 // Open file 0057 IFOK(err) { 0058 QFile file(m_importer->getLocalFileName()); 0059 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 0060 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString())); 0061 } else { 0062 // Read lines 0063 QStringList lines; 0064 { 0065 QTextStream stream(&file); 0066 if (!m_importer->getCodec().isEmpty()) { 0067 stream.setCodec(m_importer->getCodec().toLatin1().constData()); 0068 } 0069 while (!stream.atEnd()) { 0070 // Read line 0071 QString line = stream.readLine().trimmed(); 0072 if (!line.isEmpty()) { 0073 lines.push_back(line); 0074 } 0075 } 0076 } 0077 // close file 0078 file.close(); 0079 0080 // Step 1 done 0081 IFOKDO(err, m_importer->getDocument()->stepForward(1)) 0082 0083 // Read lines 0084 bool importActivated = false; 0085 bool in86 = false; 0086 SKGAccountObject account; 0087 SKGOperationObject operation; 0088 SKGUnitObject unit; 0089 QString bankName = QStringLiteral("MT940"); 0090 0091 int nb = lines.count(); 0092 IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import transactions"), nb)) 0093 for (int i = 0; i < nb && !err; ++i) { 0094 // Read line 0095 const QString& line = lines.at(i); 0096 if (!line.isEmpty()) { 0097 if (line.startsWith(QLatin1String(":20:"))) { 0098 importActivated = true; 0099 in86 = false; 0100 bankName = line.right(line.count() - 4); 0101 } else if (importActivated) { 0102 if (line.startsWith(QLatin1String(":25:"))) { 0103 // Account 0104 QStringList vals = SKGServices::splitCSVLine(line.right(line.count() - 4), '/'); 0105 QString name = QStringLiteral("MT940"); 0106 QString number = name; 0107 if (vals.count() == 2) { 0108 bankName = vals.at(0); 0109 number = vals.at(1); 0110 name = number; 0111 } else if (vals.count() == 1) { 0112 number = vals.at(0); 0113 name = number; 0114 } 0115 0116 // Search if account is already existing 0117 SKGObjectBase::SKGListSKGObjectBase listAccount; 0118 err = m_importer->getDocument()->getObjects(QStringLiteral("v_account"), "t_number='" % number % '\'', listAccount); 0119 IFOK(err) { 0120 if (listAccount.count() == 1) { 0121 // Yes ! Only one account found 0122 account = listAccount.at(0); 0123 err = m_importer->getDocument()->sendMessage(i18nc("An information message", "Using account '%1' for import", account.getName())); 0124 } else { 0125 if (listAccount.count() > 1) { 0126 err = m_importer->getDocument()->sendMessage(i18nc("An information message", "More than one possible account found.")); 0127 } 0128 0129 SKGBankObject bank(m_importer->getDocument()); 0130 IFOKDO(err, bank.setName(bankName)) 0131 IFOKDO(err, bank.setNumber(bankName)) 0132 if (!err && bank.load().isFailed()) { 0133 err = bank.save(); 0134 } 0135 IFOKDO(err, bank.addAccount(account)) 0136 IFOKDO(err, account.setName(name)) 0137 IFOKDO(err, account.setNumber(number)) 0138 IFOKDO(err, account.setType(SKGAccountObject::CURRENT)) 0139 if (!err && account.load().isFailed()) { 0140 err = account.save(); 0141 } 0142 IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Default account '%1' created for import", name))) 0143 } 0144 } 0145 0146 in86 = false; 0147 } else if (line.startsWith(QLatin1String(":28C:"))) { 0148 in86 = false; 0149 } else if (line.startsWith(QLatin1String(":60F:")) || line.startsWith(QLatin1String(":60M:"))) { 0150 QString val = line.right(line.count() - 5); 0151 err = SKGUnitObject::createCurrencyUnit(m_importer->getDocument(), val.mid(7, 3), unit); 0152 // Example C040802EUR16,40 0153 if (account.getNbOperation() > 1) { 0154 IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has not been set because some transactions are already existing", account.getName()), SKGDocument::Warning)) 0155 } else { 0156 // Set initial balance 0157 IFOKDO(err, account.setInitialBalance((val[0] == 'C' ? 1.0 : -1.0) * SKGServices::stringToDouble(val.right(val.count() - 10)), unit)) 0158 IFOKDO(err, account.save()) 0159 IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has been set with MT940 file content", account.getName()))) 0160 } 0161 0162 in86 = false; 0163 } else if (line.startsWith(QLatin1String(":62F:")) || line.startsWith(QLatin1String(":62M:"))) { 0164 in86 = false; 0165 } else if (line.startsWith(QLatin1String(":61:"))) { 0166 // Example :61:0712280103D000000000200,00FMSCNONREF 0167 QString val = line.right(line.count() - 4); 0168 QDate date = QDate::fromString(val.left(6), QStringLiteral("yyMMdd")); 0169 if (date.year() < 1970) { 0170 date = date.addYears(100); 0171 } 0172 0173 int index = (val[10] == 'R' ? 11 : 10); 0174 double sign = (val[index] == 'C' ? 1.0 : -1.0); 0175 ++index; 0176 0177 if (val[index] == 'R') { 0178 ++index; 0179 } 0180 0181 QString amountString; 0182 while (true) { 0183 if ((val[index] >= '0' && val[index] <= '9') || val[index] == '-' || val[index] == ',') { 0184 amountString += val[index]; 0185 ++index; 0186 } else { 0187 break; 0188 } 0189 } 0190 0191 err = account.addOperation(operation, true); 0192 IFOKDO(err, operation.setDate(date)) 0193 IFOKDO(err, operation.setUnit(unit)) 0194 IFOKDO(err, operation.setAttribute(QStringLiteral("t_imported"), QStringLiteral("T"))) 0195 // if(!err) err = operation.setImportID("MT940"); 0196 IFOKDO(err, operation.save()) 0197 0198 SKGSubOperationObject subop; 0199 err = operation.addSubOperation(subop); 0200 IFOKDO(err, subop.setQuantity(sign * SKGServices::stringToDouble(amountString))) 0201 IFOKDO(err, subop.save()) 0202 0203 in86 = false; 0204 } else if (line.startsWith(QLatin1String(":NS:")) && !operation.exist()) { 0205 QString val = line.right(line.count() - 4); 0206 QString comment = account.getComment(); 0207 comment += (comment.isEmpty() || val.isEmpty() ? "" : " ") % val; 0208 IFOKDO(err, account.setComment(comment)) 0209 IFOKDO(err, account.save()) 0210 } else if (line.startsWith(QLatin1String(":86:")) || line.startsWith(QLatin1String(":NS:"))) { 0211 QString val = line.right(line.count() - 4); 0212 QString comment = operation.getComment(); 0213 comment += (comment.isEmpty() || val.isEmpty() ? "" : " ") % val; 0214 IFOKDO(err, operation.setComment(comment)) 0215 IFOKDO(err, operation.save()) 0216 0217 in86 = true; 0218 } else if (in86) { 0219 const QString& val = line; 0220 QString comment = operation.getComment(); 0221 comment += (comment.isEmpty() || val.isEmpty() ? "" : " ") % val; 0222 IFOKDO(err, operation.setComment(comment)) 0223 IFOKDO(err, operation.save()) 0224 } 0225 } 0226 } 0227 IFOKDO(err, m_importer->getDocument()->stepForward(i + 1)) 0228 } 0229 SKGENDTRANSACTION(m_importer->getDocument(), err) 0230 0231 // Step 2 done 0232 IFOKDO(err, m_importer->getDocument()->stepForward(2)) 0233 } 0234 } 0235 } 0236 SKGENDTRANSACTION(m_importer->getDocument(), err) 0237 0238 return err; 0239 } 0240 0241 QString SKGImportPluginMT940::getMimeTypeFilter() const 0242 { 0243 return "*.mt940|" % i18nc("A file format", "MT940 file") % '\n' % 0244 "*.sta|" % i18nc("A file format", "MT940 file"); 0245 } 0246 0247 #include <skgimportpluginmt940.moc>