1
0
forked from BRT/arc
arc/views/bank/BankSettingsWindow.cpp
trnsmkot ea576e6558 Standardize error reporting across services:
- Add `event_type` to error data.
- Replace dynamic `type` with "ERROR" for task results and error messages.
2026-04-20 17:21:22 +07:00

409 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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);
}