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