#include "LicenseModel.h" // Qt #include #include #include #include #include #include #include #include // 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(&(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(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.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 &rows) { m_status = Status::Working; emit statusChanged(); QList 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(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; } QList 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()); 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(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() { m_status = Status::Working; emit statusChanged(); 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; } 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]; }