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 AFB120 import / export.
8 *
9 * @author Stephane MANKOWSKI / Guillaume DE BURE
10 */
11 #include "skgimportpluginafb120.h"
12
13 #include <kpluginfactory.h>
14
15 #include <qcryptographichash.h>
16 #include <qfile.h>
17 #include <qmath.h>
18
19 #include "skgbankincludes.h"
20 #include "skgimportexportmanager.h"
21 #include "skgservices.h"
22 #include "skgtraces.h"
23
24 /**
25 * This plugin factory.
26 */
K_PLUGIN_FACTORY(SKGImportPluginAFB120Factory,registerPlugin<SKGImportPluginAFB120> ();)27 K_PLUGIN_FACTORY(SKGImportPluginAFB120Factory, registerPlugin<SKGImportPluginAFB120>();)
28
29 SKGImportPluginAFB120::SKGImportPluginAFB120(QObject* iImporter, const QVariantList& iArg)
30 : SKGImportPlugin(iImporter)
31 {
32 SKGTRACEINFUNC(10)
33 Q_UNUSED(iArg)
34 }
35
36 SKGImportPluginAFB120::~SKGImportPluginAFB120()
37 = default;
38
isImportPossible()39 bool SKGImportPluginAFB120::isImportPossible()
40 {
41 SKGTRACEINFUNC(10)
42 return (m_importer == nullptr ? true : m_importer->getFileNameExtension() == QStringLiteral("AFB120") || m_importer->getFileNameExtension() == QStringLiteral("CFO"));
43 }
44
toAmount(const QString & iAmount,int iNbDecimal)45 double SKGImportPluginAFB120::toAmount(const QString& iAmount, int iNbDecimal)
46 {
47 QString amount = iAmount;
48 QChar lastChar = amount.right(1).at(0);
49 int codeAscii = lastChar.toLatin1();
50 int sign = (codeAscii > 79 && codeAscii != 123 ? -1 : 1);
51 if (codeAscii == 123 || codeAscii == 125) {
52 amount[amount.count() - 1] = '0';
53 } else {
54 bool ok = false;
55 amount[amount.count() - 1] = QChar(codeAscii + QChar('1').toLatin1() - QString(sign == -1 ? QStringLiteral("0x4A") : QStringLiteral("0x41")).toUInt(&ok, 16));
56 }
57 return static_cast<double>(sign) * SKGServices::stringToDouble(amount) / qPow(10, iNbDecimal);
58 }
59
importFile()60 SKGError SKGImportPluginAFB120::importFile()
61 {
62 if (m_importer == nullptr) {
63 return SKGError(ERR_ABORT, i18nc("Error message", "Invalid parameters"));
64 }
65 SKGError err;
66 SKGTRACEINFUNCRC(2, err)
67
68 // Begin transaction
69 err = m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import %1 file", "AFB120"), 2);
70 IFOK(err) {
71 // Open file
72 IFOK(err) {
73 QFile file(m_importer->getLocalFileName());
74 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
75 err.setReturnCode(ERR_INVALIDARG).setMessage(i18nc("Error message", "Open file '%1' failed", m_importer->getFileName().toDisplayString()));
76 } else {
77 // Read lines
78 QStringList lines;
79 {
80 QTextStream stream(&file);
81 if (!m_importer->getCodec().isEmpty()) {
82 stream.setCodec(m_importer->getCodec().toLatin1().constData());
83 }
84 while (!stream.atEnd()) {
85 // Read line
86 QString line = stream.readLine().trimmed();
87 if (!line.isEmpty()) {
88 lines.push_back(line);
89 }
90 }
91 }
92 // close file
93 file.close();
94
95 // Step 1 done
96 IFOKDO(err, m_importer->getDocument()->stepForward(1))
97
98 // Read lines
99 SKGAccountObject account;
100 SKGUnitObject unit;
101 QString bankName = QStringLiteral("AFB120");
102 QString inititalAmount;
103
104 int nb = lines.count();
105 IFOKDO(err, m_importer->getDocument()->beginTransaction("#INTERNAL#" % i18nc("Import step", "Import operations"), nb))
106 for (int i = 0; i < nb && !err; ++i) {
107 // Read line
108 const QString& line = lines.at(i);
109 if (!line.isEmpty()) {
110 if (line.startsWith(QLatin1String("01"))) {
111 // Previous balance
112 QString accountNumber = line.mid(22 - 1, 11);
113 inititalAmount = line.mid(91 - 1, 14);
114
115 SKGObjectBase::SKGListSKGObjectBase listAccount;
116 err = m_importer->getDocument()->getObjects(QStringLiteral("v_account"), "t_number='" % accountNumber % '\'', listAccount);
117 IFOK(err) {
118 if (listAccount.count() == 1) {
119 // Yes ! Only one account found
120 account = listAccount.at(0);
121 err = m_importer->getDocument()->sendMessage(i18nc("An information message", "Using account '%1' for import", account.getName()));
122 } else {
123 if (listAccount.count() > 1) {
124 err = m_importer->getDocument()->sendMessage(i18nc("An information message", "More than one possible account found."));
125 }
126
127 SKGBankObject bank(m_importer->getDocument());
128 IFOKDO(err, bank.setName(bankName))
129 IFOKDO(err, bank.setNumber(bankName))
130 if (!err && bank.load().isFailed()) {
131 err = bank.save();
132 }
133 IFOKDO(err, bank.addAccount(account))
134 IFOKDO(err, account.setName(accountNumber))
135 IFOKDO(err, account.setNumber(accountNumber))
136 IFOKDO(err, account.setType(SKGAccountObject::CURRENT))
137 if (!err && account.load().isFailed()) {
138 err = account.save();
139 }
140 IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "Default account '%1' created for import", accountNumber)))
141 }
142 }
143 } else if (line.startsWith(QLatin1String("04"))) {
144 // Operation
145 QString unitCode = line.mid(17 - 1, 3);
146 QString nbDecimal = line.mid(20 - 1, 1);
147 // QString codeMode = line.mid(33 - 1, 2);
148 QString dateJJMMAA = line.mid(35 - 1, 6);
149 QString comment = line.mid(49 - 1, 31).trimmed();
150 QString amount = line.mid(91 - 1, 14);
151
152 // Initialize balance
153 if (unit.getID() == 0) {
154 err = SKGUnitObject::createCurrencyUnit(m_importer->getDocument(), unitCode, unit);
155 if (account.getNbOperation() > 1) {
156 IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has not been set because some operations are already existing", account.getName()), SKGDocument::Warning))
157 } else {
158 // Set initial balance
159 IFOKDO(err, account.setInitialBalance(toAmount(inititalAmount, SKGServices::stringToInt(nbDecimal)), unit))
160 IFOKDO(err, account.save())
161 IFOKDO(err, m_importer->getDocument()->sendMessage(i18nc("An information message", "The initial balance of '%1' has been set with AFB120 file content", account.getName())))
162 }
163 }
164
165 SKGOperationObject operation;
166 IFOKDO(err, account.addOperation(operation, true))
167 IFOKDO(err, operation.setDate(SKGServices::stringToTime(SKGServices::dateToSqlString(dateJJMMAA, QStringLiteral("DDMMYYYY"))).date()))
168 IFOKDO(err, operation.setUnit(unit))
169 IFOKDO(err, operation.setAttribute(QStringLiteral("t_imported"), QStringLiteral("T")))
170 IFOKDO(err, operation.setComment(comment))
171 QByteArray hash = QCryptographicHash::hash(line.toUtf8(), QCryptographicHash::Md5);
172 IFOKDO(err, operation.setImportID(QStringLiteral("AFB120-") % hash.toHex()))
173 IFOKDO(err, operation.save(false))
174
175 SKGSubOperationObject subop;
176 IFOKDO(err, operation.addSubOperation(subop))
177 IFOKDO(err, subop.setComment(comment))
178 IFOKDO(err, subop.setQuantity(toAmount(amount, SKGServices::stringToInt(nbDecimal))))
179 IFOKDO(err, subop.save(false, false))
180 }
181 }
182 IFOKDO(err, m_importer->getDocument()->stepForward(i + 1))
183 }
184 SKGENDTRANSACTION(m_importer->getDocument(), err)
185
186 // Step 2 done
187 IFOKDO(err, m_importer->getDocument()->stepForward(2))
188 }
189 }
190 }
191 SKGENDTRANSACTION(m_importer->getDocument(), err)
192
193 return err;
194 }
195
getMimeTypeFilter() const196 QString SKGImportPluginAFB120::getMimeTypeFilter() const
197 {
198 return "*.afb120 *.cfo|" % i18nc("A file format", "AFB120 file (cfomb)");
199 }
200
201 #include <skgimportpluginafb120.moc>
202