641 lines
21 KiB
C++
641 lines
21 KiB
C++
#include "LicenseModel.h"
|
|
|
|
// Qt
|
|
#include <QtConcurrent/QtConcurrent>
|
|
#include <QDebug>
|
|
#include <QUuid>
|
|
#include <algorithm>
|
|
#include <QtSql/QSqlDatabase>
|
|
#include <QtSql/QSqlQuery>
|
|
#include <QtSql/QSqlError>
|
|
#include <QtSql/QSqlRecord>
|
|
|
|
// Self
|
|
#include "../def.h"
|
|
|
|
const static int COLUMN_COUNT = 8;
|
|
|
|
LicenseModel::LicenseModel(QObject* parent)
|
|
: QAbstractItemModel(parent)
|
|
{
|
|
checkTables();
|
|
}
|
|
|
|
int LicenseModel::rowCount(const QModelIndex &parent) const
|
|
{
|
|
return m_data.size();
|
|
}
|
|
|
|
int LicenseModel::columnCount(const QModelIndex &parent) const
|
|
{
|
|
return COLUMN_COUNT;
|
|
}
|
|
|
|
QVariant LicenseModel::data(const QModelIndex &index, int role) const
|
|
{
|
|
if (!index.isValid() || index.row() < 0 || index.row() >= m_data.size())
|
|
return {};
|
|
|
|
if (role == Qt::ToolTipRole && index.column() == 0)
|
|
return m_data[index.row()].id;
|
|
|
|
if (role != Qt::DisplayRole)
|
|
return {};
|
|
|
|
switch (index.column())
|
|
{
|
|
case 0:
|
|
return m_data[index.row()].lastName ;
|
|
case 1:
|
|
return m_data[index.row()].firstName ;
|
|
case 2:
|
|
return m_data[index.row()].patronymic ;
|
|
case 3:
|
|
return m_data[index.row()].email ;
|
|
case 4:
|
|
return m_data[index.row()].phone ;
|
|
case 5:
|
|
return m_data[index.row()].yourCompany ;
|
|
case 6:
|
|
return m_data[index.row()].city ;
|
|
case 7:
|
|
return m_data[index.row()].comment ;
|
|
default:
|
|
return {};
|
|
}
|
|
}
|
|
|
|
QVariant LicenseModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
{
|
|
if (role != Qt::DisplayRole)
|
|
return {};
|
|
|
|
if (orientation == Qt::Vertical)
|
|
return QString::number(section + 1);
|
|
|
|
switch (section)
|
|
{
|
|
case 0:
|
|
return tr("Last name");
|
|
case 1:
|
|
return tr("First name");
|
|
case 2:
|
|
return tr("Patronymic");
|
|
case 3:
|
|
return tr("Email");
|
|
case 4:
|
|
return tr("Phone number");
|
|
case 5:
|
|
return tr("Company");
|
|
case 6:
|
|
return tr("City");
|
|
case 7:
|
|
return tr("Comment");
|
|
default:
|
|
return {};
|
|
}
|
|
}
|
|
|
|
QModelIndex LicenseModel::parent(const QModelIndex &index) const
|
|
{
|
|
Q_UNUSED(index)
|
|
return {};
|
|
}
|
|
|
|
QModelIndex LicenseModel::index(int row, int column, const QModelIndex &parent) const
|
|
{
|
|
if (row < 0 || row >= m_data.size() || column < 0 || column >= COLUMN_COUNT)
|
|
return QModelIndex();
|
|
|
|
if (parent.isValid())
|
|
return QModelIndex();
|
|
|
|
return createIndex(row, column, reinterpret_cast<quintptr>(&(m_data[row])));
|
|
}
|
|
|
|
LicenseModel::Status LicenseModel::getStatus()
|
|
{
|
|
return m_status;
|
|
}
|
|
|
|
QString LicenseModel::getStatusText()
|
|
{
|
|
return m_errors.join('\n');
|
|
}
|
|
|
|
bool LicenseModel::checkTables()
|
|
{
|
|
auto db = QSqlDatabase::addDatabase("QSQLITE");
|
|
db.setDatabaseName(DB_PATH);
|
|
|
|
if (!db.open())
|
|
{
|
|
m_status = Status::DbExistError;
|
|
m_errors.append("Database connection failed: " + db.lastError().text());
|
|
return false;
|
|
}
|
|
|
|
bool clientTableExist = false;
|
|
for (const auto &table : db.tables())
|
|
{
|
|
if (table == "clients")
|
|
{
|
|
m_status = Status::Ok;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
db.close();
|
|
|
|
if (!clientTableExist)
|
|
{
|
|
m_status = Status::DbStructError;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
m_status = Status::Ok;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool LicenseModel::prepareDatabase()
|
|
{
|
|
auto db = QSqlDatabase::addDatabase("QSQLITE");
|
|
db.setDatabaseName(DB_PATH);
|
|
|
|
if (!db.open())
|
|
{
|
|
m_status = Status::DbExistError;
|
|
m_errors << tr("Database not exist");
|
|
return false;
|
|
}
|
|
|
|
QFile tablesFile(QStringLiteral(":/deps/tables.ddl"));
|
|
if (!tablesFile.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
return false;
|
|
|
|
m_status = Status::None;
|
|
m_errors.clear();
|
|
|
|
for (auto item : QString(tablesFile.readAll()).split(';'))
|
|
{
|
|
if (item.trimmed().isEmpty())
|
|
continue;
|
|
QSqlQuery query(db);
|
|
if (!query.exec(item.trimmed()))
|
|
{
|
|
m_status = Status::DbStructError;
|
|
qDebug() << item.trimmed() << query.lastError().text();
|
|
m_errors.append(query.lastError().text());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
db.close();
|
|
|
|
m_status = Status::Ok;
|
|
return true;
|
|
}
|
|
|
|
void LicenseModel::addClient(const LicenseItem &item)
|
|
{
|
|
m_status = Status::Working;
|
|
emit statusChanged();
|
|
|
|
auto* watcher = new QFutureWatcher<Result>(this);
|
|
connect(watcher, &QFutureWatcher<Result>::finished, this, [this, watcher]() {
|
|
const Result result = watcher->result();
|
|
if (result.status != Status::Ok)
|
|
{
|
|
m_status = result.status;
|
|
if (!result.error.isEmpty())
|
|
m_errors.append(result.error);
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
return;
|
|
}
|
|
|
|
if (result.data.isEmpty())
|
|
{
|
|
m_status = Status::DbStructError;
|
|
m_errors.append("Inserted client not found");
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
return;
|
|
}
|
|
|
|
const int row = 0;
|
|
beginInsertRows({}, row, row);
|
|
m_data.insert(row, result.data.first());
|
|
endInsertRows();
|
|
|
|
m_status = Status::Ok;
|
|
emit statusChanged();
|
|
|
|
watcher->deleteLater();
|
|
});
|
|
|
|
watcher->setFuture(QtConcurrent::run([item]() {
|
|
Result result;
|
|
const QString connectionName = QString("license_add_%1").arg(QUuid::createUuid().toString(QUuid::Id128));
|
|
{
|
|
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
|
db.setDatabaseName(DB_PATH);
|
|
if (!db.open())
|
|
{
|
|
result.status = LicenseModel::Status::DbExistError;
|
|
result.error = "Database connection failed: " + db.lastError().text();
|
|
}
|
|
else
|
|
{
|
|
QSqlQuery queryInsert(db);
|
|
queryInsert.prepare(QString(
|
|
"INSERT INTO clients(lastName, firstName, patronymic, phone, email, city, yourCompany, hardwareHash, comment) "
|
|
"VALUES (:lastName, :firstName, :patronymic, :phone, :email, :city, :yourCompany, :hardwareHash, :comment);"
|
|
));
|
|
queryInsert.bindValue(":lastName", item.lastName);
|
|
queryInsert.bindValue(":firstName", item.firstName);
|
|
queryInsert.bindValue(":patronymic", item.patronymic);
|
|
queryInsert.bindValue(":phone", item.phone);
|
|
queryInsert.bindValue(":email", item.email);
|
|
queryInsert.bindValue(":city", item.city);
|
|
queryInsert.bindValue(":yourCompany", item.yourCompany);
|
|
queryInsert.bindValue(":hardwareHash", item.hardwareHash);
|
|
queryInsert.bindValue(":comment", item.comment);
|
|
|
|
if (!queryInsert.exec())
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = queryInsert.lastError().text();
|
|
}
|
|
else
|
|
{
|
|
const QString itemId = QString::number(queryInsert.lastInsertId().toLongLong());
|
|
|
|
QSqlQuery querySelect(db);
|
|
querySelect.prepare(QString("SELECT * FROM clients WHERE id=:id;"));
|
|
querySelect.bindValue(":id", itemId);
|
|
|
|
if (!querySelect.exec())
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = querySelect.lastError().text();
|
|
}
|
|
else if (!querySelect.next())
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = "Inserted client not found: " + itemId;
|
|
}
|
|
else
|
|
{
|
|
LicenseItem output;
|
|
output.id = itemId;
|
|
output.lastName = querySelect.value("lastName").toString();
|
|
output.firstName = querySelect.value("firstName").toString();
|
|
output.patronymic = querySelect.value("patronymic").toString();
|
|
output.phone = querySelect.value("phone").toString();
|
|
output.email = querySelect.value("email").toString();
|
|
output.city = querySelect.value("city").toString();
|
|
output.yourCompany = querySelect.value("yourCompany").toString();
|
|
output.hardwareHash = querySelect.value("hardwareHash").toString();
|
|
output.comment = querySelect.value("comment").toString();
|
|
const int createdAtUtcIndex = querySelect.record().indexOf("createdAtUtc");
|
|
if (createdAtUtcIndex >= 0)
|
|
output.createdAtUtc = querySelect.value(createdAtUtcIndex).toString();
|
|
result.data.append(output);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
QSqlDatabase::removeDatabase(connectionName);
|
|
return result;
|
|
}));
|
|
}
|
|
|
|
void LicenseModel::deleteClient(const QList<int> &rows)
|
|
{
|
|
m_status = Status::Working;
|
|
emit statusChanged();
|
|
|
|
QList<QString> ids;
|
|
ids.reserve(rows.size());
|
|
for (const auto row : rows)
|
|
{
|
|
if (row < 0 || row >= m_data.size())
|
|
continue;
|
|
const QString id = m_data[row].id;
|
|
if (!id.isEmpty())
|
|
ids.append(id);
|
|
}
|
|
|
|
auto* watcher = new QFutureWatcher<Result>(this);
|
|
connect(watcher, &QFutureWatcher<Result>::finished, this, [this, watcher]() {
|
|
const Result result = watcher->result();
|
|
if (result.status != Status::Ok)
|
|
{
|
|
m_status = result.status;
|
|
if (!result.error.isEmpty())
|
|
m_errors.append(result.error);
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
return;
|
|
}
|
|
|
|
QList<int> indices;
|
|
indices.reserve(result.ids.size());
|
|
for (const auto &id : result.ids)
|
|
{
|
|
for (int i = 0; i < m_data.size(); ++i)
|
|
{
|
|
if (m_data[i].id == id)
|
|
{
|
|
indices.append(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(indices.begin(), indices.end(), std::greater<int>());
|
|
for (const auto row : indices)
|
|
{
|
|
if (row < 0 || row >= m_data.size())
|
|
continue;
|
|
beginRemoveRows({}, row, row);
|
|
m_data.removeAt(row);
|
|
endRemoveRows();
|
|
}
|
|
|
|
m_status = Status::Ok;
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
});
|
|
|
|
watcher->setFuture(QtConcurrent::run([ids]() {
|
|
Result result;
|
|
result.ids = ids;
|
|
if (ids.isEmpty())
|
|
return result;
|
|
|
|
const QString connectionName = QString("license_delete_%1")
|
|
.arg(QUuid::createUuid().toString(QUuid::Id128));
|
|
{
|
|
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
|
db.setDatabaseName(DB_PATH);
|
|
if (!db.open())
|
|
{
|
|
result.status = LicenseModel::Status::DbExistError;
|
|
result.error = "Database connection failed: " + db.lastError().text();
|
|
}
|
|
else
|
|
{
|
|
QStringList placeholders;
|
|
placeholders.reserve(ids.size());
|
|
for (int i = 0; i < ids.size(); ++i)
|
|
placeholders << "?";
|
|
|
|
QSqlQuery query(db);
|
|
query.prepare(QString("DELETE FROM clients WHERE id IN (%1);")
|
|
.arg(placeholders.join(',')));
|
|
for (const auto &id : ids)
|
|
query.addBindValue(id);
|
|
|
|
if (!query.exec())
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = query.lastError().text();
|
|
}
|
|
}
|
|
}
|
|
QSqlDatabase::removeDatabase(connectionName);
|
|
return result;
|
|
}));
|
|
}
|
|
|
|
void LicenseModel::editClient(const LicenseItem &item, int index)
|
|
{
|
|
m_status = Status::Working;
|
|
emit statusChanged();
|
|
|
|
if (index < 0 || index >= m_data.size())
|
|
{
|
|
m_status = Status::Ok;
|
|
emit statusChanged();
|
|
return;
|
|
}
|
|
|
|
LicenseItem updateItem = item;
|
|
updateItem.id = m_data[index].id;
|
|
if (updateItem.createdAtUtc.isEmpty())
|
|
updateItem.createdAtUtc = m_data[index].createdAtUtc;
|
|
|
|
if (updateItem.id.isEmpty())
|
|
{
|
|
m_status = Status::DbStructError;
|
|
m_errors.append("Client id is empty");
|
|
emit statusChanged();
|
|
return;
|
|
}
|
|
|
|
auto* watcher = new QFutureWatcher<Result>(this);
|
|
connect(watcher, &QFutureWatcher<Result>::finished, this, [this, watcher, index]() {
|
|
const Result result = watcher->result();
|
|
if (result.status != Status::Ok)
|
|
{
|
|
m_status = result.status;
|
|
if (!result.error.isEmpty())
|
|
m_errors.append(result.error);
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
return;
|
|
}
|
|
|
|
if (result.data.isEmpty())
|
|
{
|
|
m_status = Status::DbStructError;
|
|
m_errors.append("Updated client not found");
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
return;
|
|
}
|
|
|
|
if (index < 0 || index >= m_data.size())
|
|
{
|
|
m_status = Status::Ok;
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
return;
|
|
}
|
|
|
|
m_data[index] = result.data.first();
|
|
dataChanged(this->index(index, 0), this->index(index, COLUMN_COUNT - 1));
|
|
|
|
m_status = Status::Ok;
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
});
|
|
|
|
watcher->setFuture(QtConcurrent::run([updateItem]() {
|
|
Result result;
|
|
const QString connectionName = QString("license_edit_%1").arg(QUuid::createUuid().toString(QUuid::Id128));
|
|
{
|
|
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
|
db.setDatabaseName(DB_PATH);
|
|
if (!db.open())
|
|
{
|
|
result.status = LicenseModel::Status::DbExistError;
|
|
result.error = "Database connection failed: " + db.lastError().text();
|
|
}
|
|
else
|
|
{
|
|
QSqlQuery queryUpdate(db);
|
|
queryUpdate.prepare(QString(
|
|
"UPDATE clients SET lastName=:lastName, firstName=:firstName, patronymic=:patronymic, "
|
|
"phone=:phone, email=:email, city=:city, yourCompany=:yourCompany, hardwareHash=:hardwareHash, "
|
|
"comment=:comment WHERE id=:id;"
|
|
));
|
|
queryUpdate.bindValue(":lastName", updateItem.lastName);
|
|
queryUpdate.bindValue(":firstName", updateItem.firstName);
|
|
queryUpdate.bindValue(":patronymic", updateItem.patronymic);
|
|
queryUpdate.bindValue(":phone", updateItem.phone);
|
|
queryUpdate.bindValue(":email", updateItem.email);
|
|
queryUpdate.bindValue(":city", updateItem.city);
|
|
queryUpdate.bindValue(":yourCompany", updateItem.yourCompany);
|
|
queryUpdate.bindValue(":hardwareHash", updateItem.hardwareHash);
|
|
queryUpdate.bindValue(":comment", updateItem.comment);
|
|
queryUpdate.bindValue(":id", updateItem.id);
|
|
|
|
if (!queryUpdate.exec())
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = queryUpdate.lastError().text();
|
|
}
|
|
else
|
|
{
|
|
QSqlQuery querySelect(db);
|
|
querySelect.prepare(QString("SELECT * FROM clients WHERE id=:id;"));
|
|
querySelect.bindValue(":id", updateItem.id);
|
|
|
|
if (!querySelect.exec())
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = querySelect.lastError().text();
|
|
}
|
|
else if (!querySelect.next())
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = "Updated client not found: " + updateItem.id;
|
|
}
|
|
else
|
|
{
|
|
LicenseItem output;
|
|
output.id = updateItem.id;
|
|
output.lastName = querySelect.value("lastName").toString();
|
|
output.firstName = querySelect.value("firstName").toString();
|
|
output.patronymic = querySelect.value("patronymic").toString();
|
|
output.phone = querySelect.value("phone").toString();
|
|
output.email = querySelect.value("email").toString();
|
|
output.city = querySelect.value("city").toString();
|
|
output.yourCompany = querySelect.value("yourCompany").toString();
|
|
output.hardwareHash = querySelect.value("hardwareHash").toString();
|
|
output.comment = querySelect.value("comment").toString();
|
|
const int createdAtUtcIndex = querySelect.record().indexOf("createdAtUtc");
|
|
if (createdAtUtcIndex >= 0)
|
|
output.createdAtUtc = querySelect.value(createdAtUtcIndex).toString();
|
|
else
|
|
output.createdAtUtc = updateItem.createdAtUtc;
|
|
result.data.append(output);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
QSqlDatabase::removeDatabase(connectionName);
|
|
return result;
|
|
}));
|
|
}
|
|
|
|
void LicenseModel::updateModel()
|
|
{
|
|
m_status = Status::Working;
|
|
emit statusChanged();
|
|
|
|
auto* watcher = new QFutureWatcher<Result>(this);
|
|
connect(watcher, &QFutureWatcher<Result>::finished, this, [this, watcher]() {
|
|
const Result result = watcher->result();
|
|
if (result.status != Status::Ok)
|
|
{
|
|
m_status = result.status;
|
|
if (!result.error.isEmpty())
|
|
m_errors.append(result.error);
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
return;
|
|
}
|
|
|
|
beginResetModel();
|
|
m_data = result.data;
|
|
endResetModel();
|
|
|
|
m_status = Status::Ok;
|
|
emit statusChanged();
|
|
watcher->deleteLater();
|
|
});
|
|
|
|
watcher->setFuture(QtConcurrent::run([]() {
|
|
Result result;
|
|
const QString connectionName = QString("license_load_%1")
|
|
.arg(QUuid::createUuid().toString(QUuid::Id128));
|
|
{
|
|
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName);
|
|
db.setDatabaseName(DB_PATH);
|
|
if (!db.open())
|
|
{
|
|
result.status = LicenseModel::Status::DbExistError;
|
|
result.error = "Database connection failed: " + db.lastError().text();
|
|
}
|
|
else
|
|
{
|
|
QSqlQuery query(db);
|
|
if (!query.exec(
|
|
"SELECT id, lastName, firstName, patronymic, phone, email, city, yourCompany, hardwareHash, comment "
|
|
"FROM clients ORDER BY id DESC;"
|
|
))
|
|
{
|
|
result.status = LicenseModel::Status::DbStructError;
|
|
result.error = query.lastError().text();
|
|
}
|
|
else
|
|
{
|
|
const int createdAtUtcIndex = query.record().indexOf("createdAtUtc");
|
|
while (query.next())
|
|
{
|
|
LicenseItem item;
|
|
item.id = query.value("id").toString();
|
|
item.lastName = query.value("lastName").toString();
|
|
item.firstName = query.value("firstName").toString();
|
|
item.patronymic = query.value("patronymic").toString();
|
|
item.phone = query.value("phone").toString();
|
|
item.email = query.value("email").toString();
|
|
item.city = query.value("city").toString();
|
|
item.yourCompany = query.value("yourCompany").toString();
|
|
item.hardwareHash = query.value("hardwareHash").toString();
|
|
item.comment = query.value("comment").toString();
|
|
if (createdAtUtcIndex >= 0)
|
|
item.createdAtUtc = query.value(createdAtUtcIndex).toString();
|
|
result.data.append(item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
QSqlDatabase::removeDatabase(connectionName);
|
|
return result;
|
|
}));
|
|
}
|
|
|
|
LicenseModel::LicenseItem LicenseModel::getItem(int index) const
|
|
{
|
|
if (index < 0 || index >= m_data.count())
|
|
return LicenseItem();
|
|
return m_data[index];
|
|
}
|