- Add `event_type` to error data. - Replace dynamic `type` with "ERROR" for task results and error messages.
409 lines
17 KiB
C++
409 lines
17 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/BankProfileDAO.h"
|
||
#include "dao/EventDAO.h"
|
||
#include "db/BankProfileInfo.h"
|
||
#include "net/NetworkService.h"
|
||
#include "common/PaddedItemDelegate.h"
|
||
#include "device/EditBankDialog.h"
|
||
#include "ozon/LoginAndCheckAccountsScript.h"
|
||
#include "black/LoginAndCheckAccountsScript.h"
|
||
#include "dushanbe/LoginAndCheckAccountsScript.h"
|
||
|
||
QTableWidgetItem *BankSettingsWindow::createTableWidget(const QString &text) {
|
||
QTableWidgetItem *item = new QTableWidgetItem(text);
|
||
item->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||
return item;
|
||
}
|
||
|
||
QString parseStatus(const BankProfileInfo &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);
|
||
const QString deviceId = m_deviceId;
|
||
|
||
auto onResult = [deviceId, appCode](const QString &result) {
|
||
if (result.isEmpty()) {
|
||
const BankProfileInfo app = BankProfileDAO::getApplication(deviceId, appCode);
|
||
if (app.id >= 0) {
|
||
BankProfileDAO::updatePinCodeStatus(app.id, "yes", "");
|
||
}
|
||
}
|
||
};
|
||
|
||
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::finishedWithResult, worker, onResult);
|
||
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 == "black") {
|
||
auto *worker = new Black::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr);
|
||
worker->moveToThread(thread);
|
||
connect(thread, &QThread::started, worker, &Black::LoginAndCheckAccountsScript::start);
|
||
connect(worker, &Black::LoginAndCheckAccountsScript::finishedWithResult, worker, onResult);
|
||
connect(worker, &Black::LoginAndCheckAccountsScript::finished, thread, &QThread::quit);
|
||
connect(worker, &Black::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater);
|
||
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||
} else if (appCode == "dushanbe_city_bank") {
|
||
auto *worker = new Dushanbe::LoginAndCheckAccountsScript(m_deviceId, appCode, nullptr);
|
||
worker->moveToThread(thread);
|
||
connect(thread, &QThread::started, worker, &Dushanbe::LoginAndCheckAccountsScript::start);
|
||
connect(worker, &Dushanbe::LoginAndCheckAccountsScript::finishedWithResult, worker, onResult);
|
||
connect(worker, &Dushanbe::LoginAndCheckAccountsScript::finished, thread, &QThread::quit);
|
||
connect(worker, &Dushanbe::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater);
|
||
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||
} else {
|
||
thread->deleteLater();
|
||
return;
|
||
}
|
||
|
||
thread->start();
|
||
}
|
||
|
||
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName) {
|
||
QJsonObject errData;
|
||
errData["status"] = "error";
|
||
errData["event_type"] = type;
|
||
errData["description"] = "Bank profile disabled";
|
||
|
||
QJsonObject obj;
|
||
obj["type"] = "ERROR";
|
||
obj["material_id"] = materialId;
|
||
obj["bank_name"] = bankName;
|
||
obj["data"] = errData;
|
||
|
||
auto *thread = new QThread;
|
||
auto *net = new NetworkService;
|
||
net->moveToThread(thread);
|
||
net->setPayload(QJsonDocument(obj).toJson());
|
||
QObject::connect(thread, &QThread::started, net, &NetworkService::postTaskResult);
|
||
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 sendAppStatus(const QString &deviceId, const QString &code, const QString &status) {
|
||
const BankProfileInfo app = BankProfileDAO::getApplication(deviceId, code);
|
||
if (app.bankProfileId.isEmpty()) {
|
||
qWarning() << "[BankSettings] sendAppStatus: no bankProfileId for" << code;
|
||
return;
|
||
}
|
||
|
||
// При выключении — отменяем ожидающие задачи
|
||
if (status != "active") {
|
||
const QList<EventInfo> cancelled = EventDAO::cancelWaitingByBank(deviceId, code);
|
||
for (const EventInfo &e : cancelled) {
|
||
sendTaskError(eventTypeToString(e.type), e.externalId, e.bankName);
|
||
EventDAO::markSentToServer(e.id);
|
||
qDebug() << "[BankSettings] cancelled task" << e.id << eventTypeToString(e.type);
|
||
}
|
||
}
|
||
|
||
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<BankProfileInfo> applications = BankProfileDAO::getApplicationsByDeviceId(m_deviceId);
|
||
|
||
tableWidget->setRowCount(applications.length());
|
||
tableWidget->setColumnCount(8);
|
||
tableWidget->setHorizontalHeaderLabels({
|
||
"",
|
||
"Имя",
|
||
"Пин-код",
|
||
"Статус",
|
||
"Сервер",
|
||
"Комментарий",
|
||
"",
|
||
""
|
||
});
|
||
|
||
tableWidget->setWordWrap(true);
|
||
tableWidget->setTextElideMode(Qt::ElideNone);
|
||
|
||
tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||
for (int i = 0; i < 8; ++i) {
|
||
tableWidget->horizontalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||
}
|
||
for (int i = 0; i < applications.length(); ++i) {
|
||
tableWidget->verticalHeader()->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||
}
|
||
tableWidget->horizontalHeader()->setSectionResizeMode(5, QHeaderView::Stretch);
|
||
tableWidget->setColumnWidth(4, 140);
|
||
|
||
|
||
for (int k = 0; k < applications.size(); ++k) {
|
||
const BankProfileInfo &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)));
|
||
|
||
// Статус синхронизации с сервером
|
||
auto *syncItem = new QTableWidgetItem(app.bankProfileId.isEmpty() ? "Не синхронизирован" : "Синхронизирован");
|
||
syncItem->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
|
||
syncItem->setForeground(app.bankProfileId.isEmpty() ? QBrush(Qt::red) : QBrush(Qt::darkGreen));
|
||
tableWidget->setItem(k, 4, syncItem);
|
||
|
||
QTableWidgetItem *item = new QTableWidgetItem(app.comment);
|
||
item->setTextAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||
item->setFlags(item->flags() & ~Qt::ItemIsEditable);
|
||
tableWidget->setItem(k, 5, item);
|
||
|
||
|
||
if (app.install) {
|
||
if (!app.pinCode.isEmpty() && !app.pinCodeChecked) {
|
||
auto *pinCodeBtn = new QPushButton(" Проверить пин-код ", tableWidget);
|
||
pinCodeBtn->setProperty("btnClass", "pincode");
|
||
tableWidget->setCellWidget(k, 6, 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, 6, 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 (!BankProfileDAO::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, 7, 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 (!BankProfileDAO::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 BankProfileInfo &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);
|
||
}
|