diff --git a/src/EditClientDialog/EditClientDialog.cpp b/src/EditClientDialog/EditClientDialog.cpp index 4835a47..0085e00 100644 --- a/src/EditClientDialog/EditClientDialog.cpp +++ b/src/EditClientDialog/EditClientDialog.cpp @@ -229,4 +229,6 @@ void EditClientDialog::setClientInfo(int index) m_cityLineEdit->setText(item.city); m_yourCompanyNameTextEdit->setText(item.yourCompany); m_hardwareHashLineEdit->setText(item.hardwareHash); + + m_configPathLabel->setText(tr("Drop file here")); } diff --git a/src/LicenseModel/LicenseModel.cpp b/src/LicenseModel/LicenseModel.cpp index 8b98657..fb87809 100644 --- a/src/LicenseModel/LicenseModel.cpp +++ b/src/LicenseModel/LicenseModel.cpp @@ -11,58 +11,14 @@ #include // Self -#include - #include "../def.h" -const static int COLUMN_COUNT = 9; - -namespace { -struct LoadResult -{ - QList data; - LicenseModel::Status status = LicenseModel::Status::Ok; - QString error; -}; - -struct DeleteResult -{ - QList ids; - LicenseModel::Status status = LicenseModel::Status::Ok; - QString error; -}; -} +const static int COLUMN_COUNT = 8; LicenseModel::LicenseModel(QObject* parent) : QAbstractItemModel(parent) { - m_db = QSqlDatabase::addDatabase("QSQLITE"); - m_db.setDatabaseName(DB_PATH); - - if (!m_db.open()) - { - m_status = Status::DbExistError; - m_errors.append("Database connection failed: " + m_db.lastError().text()); - } - else - { - if (!checkTables()) - { - m_status = Status::DbStructError; - m_errors.append("Database tables are not valid"); - } - else - { - m_status = Status::Ok; - } - } - - if (m_db.open()) - m_db.close(); -} - -LicenseModel::~LicenseModel() -{ + checkTables(); } int LicenseModel::rowCount(const QModelIndex &parent) const @@ -103,8 +59,6 @@ QVariant LicenseModel::data(const QModelIndex &index, int role) const case 6: return m_data[index.row()].city ; case 7: - return m_data[index.row()].createdAtUtc ; - case 8: return m_data[index.row()].comment ; default: return {}; @@ -136,8 +90,6 @@ QVariant LicenseModel::headerData(int section, Qt::Orientation orientation, int case 6: return tr("City"); case 7: - return tr("Created date"); - case 8: return tr("Comment"); default: return {}; @@ -173,20 +125,52 @@ QString LicenseModel::getStatusText() 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 : m_db.tables()) + 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; @@ -198,7 +182,7 @@ bool LicenseModel::prepareDatabase() { if (item.trimmed().isEmpty()) continue; - QSqlQuery query(m_db); + QSqlQuery query(db); if (!query.exec(item.trimmed())) { m_status = Status::DbStructError; @@ -208,6 +192,9 @@ bool LicenseModel::prepareDatabase() } } + db.close(); + + m_status = Status::Ok; return true; } @@ -216,12 +203,31 @@ void LicenseModel::addClient(const LicenseItem &item) m_status = Status::Working; emit statusChanged(); - // handler thread finish - auto* watcher = new QFutureWatcher(this); - connect(watcher, &QFutureWatcher::finished, this, [this, watcher]() { - const int row = m_data.size(); + auto* watcher = new QFutureWatcher(this); + connect(watcher, &QFutureWatcher::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.append(watcher->result()); + m_data.insert(row, result.data.first()); endInsertRows(); m_status = Status::Ok; @@ -230,16 +236,17 @@ void LicenseModel::addClient(const LicenseItem &item) watcher->deleteLater(); }); - // another thread lambda watcher->setFuture(QtConcurrent::run([item]() { + Result result; const QString connectionName = QString("license_add_%1").arg(QUuid::createUuid().toString(QUuid::Id128)); - LicenseItem output; - bool ok = false; { - auto db = QSqlDatabase::addDatabase("QSQLITE", connectionName); + QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", connectionName); db.setDatabaseName(DB_PATH); if (!db.open()) - qDebug() << "db not open"; + { + result.status = LicenseModel::Status::DbExistError; + result.error = "Database connection failed: " + db.lastError().text(); + } else { QSqlQuery queryInsert(db); @@ -258,21 +265,31 @@ void LicenseModel::addClient(const LicenseItem &item) queryInsert.bindValue(":comment", item.comment); if (!queryInsert.exec()) - qDebug() << "queryInsert error" << queryInsert.lastError(); + { + result.status = LicenseModel::Status::DbStructError; + result.error = queryInsert.lastError().text(); + } else { - QString itemId = QString::number(queryInsert.lastInsertId().toLongLong()); + 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()) - qDebug() << "querySelect exec error" << querySelect.lastError(); + { + result.status = LicenseModel::Status::DbStructError; + result.error = querySelect.lastError().text(); + } else if (!querySelect.next()) - qDebug() << "querySelect no rows for id" << itemId; + { + 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(); @@ -283,15 +300,16 @@ void LicenseModel::addClient(const LicenseItem &item) output.yourCompany = querySelect.value("yourCompany").toString(); output.hardwareHash = querySelect.value("hardwareHash").toString(); output.comment = querySelect.value("comment").toString(); - ok = true; + const int createdAtUtcIndex = querySelect.record().indexOf("createdAtUtc"); + if (createdAtUtcIndex >= 0) + output.createdAtUtc = querySelect.value(createdAtUtcIndex).toString(); + result.data.append(output); } } } } QSqlDatabase::removeDatabase(connectionName); - if (!ok) - return LicenseItem(); - return output; + return result; })); } @@ -311,9 +329,9 @@ void LicenseModel::deleteClient(const QList &rows) ids.append(id); } - auto* watcher = new QFutureWatcher(this); - connect(watcher, &QFutureWatcher::finished, this, [this, watcher]() { - const DeleteResult result = watcher->result(); + auto* watcher = new QFutureWatcher(this); + connect(watcher, &QFutureWatcher::finished, this, [this, watcher]() { + const Result result = watcher->result(); if (result.status != Status::Ok) { m_status = result.status; @@ -354,7 +372,7 @@ void LicenseModel::deleteClient(const QList &rows) }); watcher->setFuture(QtConcurrent::run([ids]() { - DeleteResult result; + Result result; result.ids = ids; if (ids.isEmpty()) return result; @@ -399,8 +417,141 @@ void LicenseModel::editClient(const LicenseItem &item, int index) m_status = Status::Working; emit statusChanged(); - m_data[index] = item; - dataChanged(this->index(index, 0), this->index(index, COLUMN_COUNT - 1)); + 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(this); + connect(watcher, &QFutureWatcher::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() @@ -408,9 +559,9 @@ void LicenseModel::updateModel() m_status = Status::Working; emit statusChanged(); - auto* watcher = new QFutureWatcher(this); - connect(watcher, &QFutureWatcher::finished, this, [this, watcher]() { - const LoadResult result = watcher->result(); + auto* watcher = new QFutureWatcher(this); + connect(watcher, &QFutureWatcher::finished, this, [this, watcher]() { + const Result result = watcher->result(); if (result.status != Status::Ok) { m_status = result.status; @@ -431,7 +582,7 @@ void LicenseModel::updateModel() }); watcher->setFuture(QtConcurrent::run([]() { - LoadResult result; + Result result; const QString connectionName = QString("license_load_%1") .arg(QUuid::createUuid().toString(QUuid::Id128)); { diff --git a/src/LicenseModel/LicenseModel.h b/src/LicenseModel/LicenseModel.h index 73cda99..f29f0dd 100644 --- a/src/LicenseModel/LicenseModel.h +++ b/src/LicenseModel/LicenseModel.h @@ -35,8 +35,15 @@ public: QString comment; }; + struct Result + { + QList data; + QStringList ids; + Status status = Status::Ok; + QString error; + }; + explicit LicenseModel(QObject* parent = nullptr); - ~LicenseModel(); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -61,7 +68,6 @@ private: QList m_data; Status m_status = Status::None; QStringList m_errors; - QSqlDatabase m_db; }; #endif // LICENSEMANAGER_LICENSEMODEL_H diff --git a/src/MainWidget/MainWidget.cpp b/src/MainWidget/MainWidget.cpp index f9281d1..9c59b4b 100644 --- a/src/MainWidget/MainWidget.cpp +++ b/src/MainWidget/MainWidget.cpp @@ -184,7 +184,14 @@ void MainWidget::onDeleteClientTriggered() for (auto i = selectedRows.size() - 1; i >= 0; --i) list << selectedRows.at(i).row(); - m_licenseModel->deleteClient(list); + auto result = QMessageBox::question( + this, + tr("Delete rows"), + tr(QString("Do you want to delete %1 rows?").arg(list.size()).toUtf8()) + ); + + if (result == QMessageBox::Yes) + m_licenseModel->deleteClient(list); } void MainWidget::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)