Implemented `EventWindow` for viewing events with pagination and table-based UI. Added database migration to remove `UNIQUE` constraint on `external_id` in `events`. Enhanced DAO methods for event retrieval, pagination, and transaction updates. Integrated OCR recognition in transaction scripts and streamlined event handling in services.
374 lines
16 KiB
C++
374 lines
16 KiB
C++
#include "BankSettingsWindow.h"
|
||
#include <QVBoxLayout>
|
||
#include <QDir>
|
||
#include <QFile>
|
||
#include <QHeaderView>
|
||
#include <QScrollArea>
|
||
#include <QCoreApplication>
|
||
#include <QPushButton>
|
||
#include <QTableWidget>
|
||
#include <utility>
|
||
#include <QMessageBox>
|
||
#include <QLabel>
|
||
#include <QDialog>
|
||
#include <QJsonArray>
|
||
#include <QJsonObject>
|
||
#include <QThread>
|
||
|
||
#include "dao/ApplicationDAO.h"
|
||
#include "db/ApplicationInfo.h"
|
||
#include "net/NetworkService.h"
|
||
#include "common/PaddedItemDelegate.h"
|
||
#include "device/EditBankDialog.h"
|
||
#include "birbank/LoginAndCheckAccountsBirbankScript.h"
|
||
#include "ozon/LoginAndCheckAccountsScript.h"
|
||
#include "rshb/LoginAndCheckAccountsScript.h"
|
||
#include "toss/LoginAndCheckAccountsTossScript.h"
|
||
#include "widget/common/CollapsibleSection.h"
|
||
#include "ziraat/LoginAndCheckAccountsZiraatScript.h"
|
||
|
||
QTableWidgetItem *BankSettingsWindow::createTableWidget(const QString &text) {
|
||
QTableWidgetItem *item = new QTableWidgetItem(text);
|
||
item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||
return item;
|
||
}
|
||
|
||
QString parseStatus(const ApplicationInfo &app) {
|
||
if (!app.install) {
|
||
return "не установлен";
|
||
}
|
||
if (app.status == "active") {
|
||
if (!app.pinCodeChecked) {
|
||
return app.pinCode.isEmpty() ? "нет пин-код" : "пин-код не проверен";
|
||
}
|
||
return "работает";
|
||
} else if (app.status == "off") {
|
||
return "выключено";
|
||
} else {
|
||
return app.status;
|
||
}
|
||
}
|
||
|
||
void BankSettingsWindow::startCheckPinCode(const QString &appCode) {
|
||
QThread *thread = new QThread(nullptr);
|
||
|
||
if (appCode == "rshb") {
|
||
auto *worker = new Rshb::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr);
|
||
worker->moveToThread(thread);
|
||
connect(thread, &QThread::started, worker, &Rshb::LoginAndCheckAccountsScript::start);
|
||
connect(worker, &Rshb::LoginAndCheckAccountsScript::finished, thread, &QThread::quit);
|
||
connect(worker, &Rshb::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater);
|
||
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||
} else if (appCode == "ziraat") {
|
||
auto *worker = new LoginAndCheckAccountsZiraatScript(m_deviceId, appCode, nullptr);
|
||
worker->moveToThread(thread);
|
||
connect(thread, &QThread::started, worker, &LoginAndCheckAccountsZiraatScript::start);
|
||
connect(worker, &LoginAndCheckAccountsZiraatScript::finished, thread, &QThread::quit);
|
||
connect(worker, &LoginAndCheckAccountsZiraatScript::finished, worker, &QObject::deleteLater);
|
||
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||
} else if (appCode == "ozon") {
|
||
auto *worker = new Ozon::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr);
|
||
worker->moveToThread(thread);
|
||
connect(thread, &QThread::started, worker, &Ozon::LoginAndCheckAccountsScript::start);
|
||
connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, thread, &QThread::quit);
|
||
connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater);
|
||
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||
} else if (appCode == "birbank") {
|
||
auto *worker = new LoginAndCheckAccountsBirbankScript(m_deviceId, appCode, nullptr);
|
||
worker->moveToThread(thread);
|
||
connect(thread, &QThread::started, worker, &LoginAndCheckAccountsBirbankScript::start);
|
||
connect(worker, &LoginAndCheckAccountsBirbankScript::finished, thread, &QThread::quit);
|
||
connect(worker, &LoginAndCheckAccountsBirbankScript::finished, worker, &QObject::deleteLater);
|
||
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||
} else if (appCode == "toss") {
|
||
auto *worker = new LoginAndCheckAccountsTossScript(m_deviceId, appCode, nullptr);
|
||
worker->moveToThread(thread);
|
||
connect(thread, &QThread::started, worker, &LoginAndCheckAccountsTossScript::start);
|
||
connect(worker, &LoginAndCheckAccountsTossScript::finished, thread, &QThread::quit);
|
||
connect(worker, &LoginAndCheckAccountsTossScript::finished, worker, &QObject::deleteLater);
|
||
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||
} else {
|
||
thread->deleteLater();
|
||
return;
|
||
}
|
||
|
||
thread->start();
|
||
}
|
||
|
||
void sendAppStatus(const QString &deviceId, const QString &code, const QString &status) {
|
||
const ApplicationInfo app = ApplicationDAO::getApplication(deviceId, code);
|
||
if (app.bankProfileId.isEmpty()) {
|
||
qWarning() << "[BankSettings] sendAppStatus: no bankProfileId for" << code;
|
||
return;
|
||
}
|
||
|
||
const bool enable = (status == "active");
|
||
auto *thread = new QThread;
|
||
auto *net = new NetworkService;
|
||
net->moveToThread(thread);
|
||
net->addExtra("bank_profile_id", app.bankProfileId);
|
||
net->addExtra("enable", enable);
|
||
QObject::connect(thread, &QThread::started, net, &NetworkService::toggleBankProfile);
|
||
QObject::connect(net, &NetworkService::finished, thread, &QThread::quit);
|
||
QObject::connect(net, &NetworkService::finished, net, &QObject::deleteLater);
|
||
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||
thread->start();
|
||
}
|
||
|
||
void BankSettingsWindow::reloadContent(QTableWidget *tableWidget) {
|
||
QList<ApplicationInfo> applications = ApplicationDAO::getApplicationsByDeviceId(m_deviceId);
|
||
|
||
tableWidget->setRowCount(applications.length());
|
||
tableWidget->setColumnCount(7);
|
||
tableWidget->setHorizontalHeaderLabels({
|
||
"",
|
||
"Имя",
|
||
"Пин-код",
|
||
"Статус",
|
||
"Комментарий",
|
||
"",
|
||
""
|
||
});
|
||
|
||
tableWidget->setWordWrap(true);
|
||
tableWidget->setTextElideMode(Qt::ElideNone);
|
||
|
||
tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||
for (int i = 0; i < 7; ++i) {
|
||
tableWidget->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||
}
|
||
for (int i = 0; i < applications.length(); ++i) {
|
||
tableWidget->verticalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||
}
|
||
tableWidget->horizontalHeader()->setSectionResizeMode(4, QHeaderView::Stretch);
|
||
|
||
|
||
for (int k = 0; k < applications.size(); ++k) {
|
||
const ApplicationInfo &app = applications[k];
|
||
|
||
|
||
auto *icon = new QLabel(tableWidget);
|
||
QPixmap pixmap(QCoreApplication::applicationDirPath() + "/images/" + app.code + "_icon.png");
|
||
icon->setPixmap(pixmap);
|
||
icon->setPixmap(pixmap.scaled(20, 20, Qt::KeepAspectRatio, Qt::SmoothTransformation));
|
||
icon->setStyleSheet(
|
||
"background-color: transparent;"
|
||
"text-align: center;"
|
||
"padding:0 4px 0 0;"
|
||
);
|
||
icon->setMinimumSize(24, 20);
|
||
tableWidget->setCellWidget(k, 0, icon);
|
||
|
||
tableWidget->setItem(k, 1, createTableWidget(app.name));
|
||
|
||
|
||
// QTableWidgetItem *picCodeItem = new QTableWidgetItem(app.pinCode);
|
||
// picCodeItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||
auto *picCode = new QLabel(app.pinCode);
|
||
if (!app.pinCode.isEmpty() && !app.pinCodeChecked) {
|
||
picCode->setStyleSheet(
|
||
"background-color: orange;"
|
||
"color: white;"
|
||
);
|
||
}
|
||
picCode->setAlignment(Qt::AlignCenter);
|
||
tableWidget->setCellWidget(k, 2, picCode);
|
||
|
||
tableWidget->setItem(k, 3, createTableWidget(parseStatus(app)));
|
||
|
||
// tableWidget->setItem(k, 4, createTableWidget(app.comment));
|
||
|
||
QTableWidgetItem *item = new QTableWidgetItem(app.comment);
|
||
item->setTextAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||
// item->setSizeHint(QSize(100, app.comment.length() / 2)); // Минимальная высота ячейки для переноса
|
||
tableWidget->setItem(k, 4, item);
|
||
|
||
|
||
if (app.install) {
|
||
if (!app.pinCode.isEmpty() && !app.pinCodeChecked) {
|
||
auto *pinCodeBtn = new QPushButton(" Проверить пин-код ", tableWidget);
|
||
pinCodeBtn->setProperty("btnClass", "pincode");
|
||
tableWidget->setCellWidget(k, 5, pinCodeBtn);
|
||
|
||
connect(pinCodeBtn, &QPushButton::clicked, this, [this, app]() {
|
||
QMessageBox msgBox(this);
|
||
msgBox.setWindowTitle(tr("Проверка пин-код"));
|
||
msgBox.setText("Сейчас запустится скрипт проверки пин-код в приложении: " + app.name);
|
||
|
||
QPushButton *deleteBtn = msgBox.addButton(tr("Запустить"), QMessageBox::AcceptRole);
|
||
QPushButton *closeBtn = msgBox.addButton(tr("Отмена"), QMessageBox::RejectRole);
|
||
msgBox.setDefaultButton(closeBtn);
|
||
msgBox.setModal(true);
|
||
msgBox.exec();
|
||
|
||
if (msgBox.clickedButton() == deleteBtn) {
|
||
startCheckPinCode(app.code);
|
||
}
|
||
});
|
||
} else {
|
||
if (app.status == "active" && !app.pinCode.isEmpty()) {
|
||
auto *deleteBtn = new QPushButton(" Отключить ", tableWidget);
|
||
deleteBtn->setProperty("btnClass", "delete");
|
||
// deleteBtn->setStyleSheet("padding: 4px 8px;");
|
||
// deleteBtn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||
tableWidget->setCellWidget(k, 5, deleteBtn);
|
||
|
||
connect(deleteBtn, &QPushButton::clicked, this, [this,tableWidget, app]() {
|
||
QMessageBox msgBox(this);
|
||
msgBox.setWindowTitle(tr("Подтверждение"));
|
||
msgBox.setText("Вы уверены, что хотите отключить от работы данный банк: " + app.name + "?");
|
||
|
||
QPushButton *deleteBtn = msgBox.addButton(tr("Отключить"), QMessageBox::AcceptRole);
|
||
QPushButton *closeBtn = msgBox.addButton(tr("Отмена"), QMessageBox::RejectRole);
|
||
msgBox.setDefaultButton(closeBtn);
|
||
msgBox.setModal(true);
|
||
msgBox.exec();
|
||
|
||
if (msgBox.clickedButton() == deleteBtn) {
|
||
if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, "off")) {
|
||
qDebug() << "Cant update bank data";
|
||
} else {
|
||
sendAppStatus(app.deviceId, app.code, "off");
|
||
tableWidget->clearContents();
|
||
reloadContent(tableWidget);
|
||
tableWidget->resizeRowsToContents();
|
||
}
|
||
} else {
|
||
// просто закрыли — ничего не делаем
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
|
||
// auto *editBtn = new QTableWidgetItem;
|
||
// tableWidget->setItem(k, 5, editBtn);
|
||
auto *btn = new QPushButton(" Редактировать ", tableWidget);
|
||
btn->setProperty("btnClass", "edit");
|
||
// btn->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
|
||
tableWidget->setCellWidget(k, 6, btn);
|
||
connect(btn, &QPushButton::clicked, this, [this, tableWidget, k,app]() {
|
||
BankEditDialog dlg(app, this);
|
||
if (dlg.exec() == QDialog::Accepted) {
|
||
const QString pinCode = dlg.firstValue();
|
||
const QString comment = dlg.secondValue();
|
||
bool on = dlg.toggled();
|
||
|
||
if (!ApplicationDAO::update(app.id, pinCode, comment, on ? "active" : "off")) {
|
||
qDebug() << "Cant update bank data";
|
||
} else {
|
||
sendAppStatus(app.deviceId, app.code, on ? "active" : "off");
|
||
tableWidget->clearContents();
|
||
reloadContent(tableWidget);
|
||
tableWidget->resizeRowsToContents();
|
||
}
|
||
} else {
|
||
// Пользователь нажал «Cancel» или закрыл окно
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
BankSettingsWindow::BankSettingsWindow(QWidget *parent, QString deviceId, QString deviceName, const ApplicationInfo &app)
|
||
: QWidget(parent,
|
||
Qt::Window
|
||
| Qt::WindowTitleHint
|
||
| Qt::WindowSystemMenuHint
|
||
| Qt::WindowCloseButtonHint
|
||
| Qt::WindowMinMaxButtonsHint),
|
||
m_deviceId(std::move(deviceId)),
|
||
m_deviceName(std::move(deviceName)
|
||
) {
|
||
setWindowTitle("Настройки банков [" + m_deviceName + "]");
|
||
setMinimumSize(800, 400);
|
||
setMaximumSize(1000, 800);
|
||
resize(800, 400);
|
||
|
||
auto *mainLayout = new QVBoxLayout(this);
|
||
|
||
// Скролл-область
|
||
auto *scrollArea = new QScrollArea(this);
|
||
scrollArea->setWidgetResizable(true);
|
||
|
||
// Виджет-контейнер, в котором будет layout с таблицами и лейблами
|
||
auto *contentWidget = new QWidget;
|
||
auto *layout = new QVBoxLayout(contentWidget);
|
||
|
||
// Убираем отступы и рамку
|
||
contentWidget->setContentsMargins(0, 0, 0, 0); // Убираем отступы
|
||
layout->setContentsMargins(0, 0, 0, 0); // Убираем отступы у layout
|
||
scrollArea->setStyleSheet("border: none;"); // Убираем рамку
|
||
layout->setAlignment(Qt::AlignTop);
|
||
|
||
|
||
QTableWidget *tableWidget = new QTableWidget(this);
|
||
tableWidget->setSelectionMode(QAbstractItemView::NoSelection);
|
||
tableWidget->setFocusPolicy(Qt::NoFocus);
|
||
tableWidget->setStyleSheet(
|
||
"QTableWidget {"
|
||
" border: 1px solid grey;"
|
||
// " gridline-color: lightgrey;"
|
||
// " background-color: #f9f9f9;"
|
||
" font-size: 14px;"
|
||
"}"
|
||
"QHeaderView::section {"
|
||
// " background-color: #e0e0e0;"
|
||
// " border: 1px solid grey;"
|
||
" padding: 4px;"
|
||
" font-weight: bold;"
|
||
" text-align: left center;"
|
||
"}"
|
||
"QTableWidget::item {"
|
||
" padding: 2px;"
|
||
" border: none;"
|
||
" text-align: left;"
|
||
" vertical-align: middle; "
|
||
"}"
|
||
"QTableWidget::item:selected {"
|
||
// " background-color: #cce5ff;"
|
||
// " color: #003366;"
|
||
"}"
|
||
"QTableCornerButton::section {"
|
||
// " background-color: #e0e0e0;"
|
||
// " border: 1px solid grey;"
|
||
"}"
|
||
"QPushButton {"
|
||
"border-radius: 4px;"
|
||
"height: 26px;"
|
||
"}"
|
||
"QPushButton[btnClass='delete'] {"
|
||
"background-color: red;"
|
||
"}"
|
||
"QPushButton[btnClass='pincode'] {"
|
||
"background-color: orange;"
|
||
"}"
|
||
"QPushButton[btnClass='edit'] {"
|
||
"background-color: green;"
|
||
"}"
|
||
"QPushButton[btnClass='delete']:pressed {"
|
||
"background-color: blue;"
|
||
"}"
|
||
"QPushButton[btnClass='edit']:pressed {"
|
||
"background-color: blue;"
|
||
"}"
|
||
);
|
||
|
||
tableWidget->clearContents();
|
||
reloadContent(tableWidget);
|
||
tableWidget->resizeColumnsToContents();
|
||
|
||
layout->addWidget(tableWidget);
|
||
tableWidget->resizeRowsToContents();
|
||
|
||
// Добавим вертикальный Spacer в конец, чтобы прокручиваемая область не растягивала контент
|
||
// QSpacerItem *spacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
|
||
// layout->addItem(spacer);
|
||
|
||
contentWidget->setLayout(layout);
|
||
scrollArea->setWidget(contentWidget);
|
||
mainLayout->addWidget(scrollArea);
|
||
setLayout(mainLayout);
|
||
}
|