1
0
forked from BRT/arc
arc/views/bank/BankSetupWizard.cpp

688 lines
31 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 "BankSetupWizard.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QCoreApplication>
#include <QRadioButton>
#include "ozon/LoginAndCheckAccountsScript.h"
#include "ozon/GetProfileInfoScript.h"
#include "ozon/GetCardInfoScript.h"
#include "black/LoginAndCheckAccountsScript.h"
#include "black/GetProfileInfoScript.h"
#include "black/GetCardInfoScript.h"
#include "dushanbe/LoginAndCheckAccountsScript.h"
#include "dushanbe/GetProfileInfoScript.h"
#include "dushanbe/GetCardInfoScript.h"
#include "dao/BankProfileDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/MaterialDAO.h"
#include "net/NetworkService.h"
BankSetupWizard::BankSetupWizard(QWidget *parent, const QString &deviceId, const BankProfileInfo &app)
: QDialog(parent), m_deviceId(deviceId), m_app(app) {
setWindowTitle("Настройка банка — " + app.name);
setMinimumSize(500, 350);
resize(520, 380);
auto *mainLayout = new QVBoxLayout(this);
m_stack = new QStackedWidget(this);
m_stack->addWidget(createStep1()); // 0
m_stack->addWidget(createStep2()); // 1
m_stack->addWidget(createStep3()); // 2
m_stack->addWidget(createStep4Success()); // 3
m_stack->addWidget(createStep4Error()); // 4
m_stack->setCurrentIndex(0);
mainLayout->addWidget(m_stack);
pauseActiveProfiles();
}
BankSetupWizard::~BankSetupWizard() {
stopWorkerThread();
resumePausedProfiles();
}
// ── Step 1: Intro ──────────────────────────────────────────────────────────
QWidget *BankSetupWizard::createStep1() {
auto *page = new QWidget;
auto *layout = new QVBoxLayout(page);
layout->setAlignment(Qt::AlignTop);
auto *icon = new QLabel;
QPixmap pixmap(QCoreApplication::applicationDirPath() + "/images/" + m_app.code + "_icon.png");
if (!pixmap.isNull())
icon->setPixmap(pixmap.scaled(48, 48, Qt::KeepAspectRatio, Qt::SmoothTransformation));
icon->setAlignment(Qt::AlignCenter);
layout->addWidget(icon);
auto *title = new QLabel("<h2>" + m_app.name + "</h2>");
title->setAlignment(Qt::AlignCenter);
layout->addWidget(title);
auto *desc = new QLabel(
"Мастер поможет настроить банковский профиль для приложения <b>" + m_app.name + "</b>.<br><br>"
"Будут выполнены следующие шаги:<br>"
"1. Ввод пин-кода<br>"
"2. Проверка входа в приложение<br>"
"3. Получение данных профиля и карт"
);
desc->setWordWrap(true);
desc->setAlignment(Qt::AlignCenter);
layout->addWidget(desc);
layout->addStretch();
auto *btnLayout = new QHBoxLayout;
btnLayout->addStretch();
auto *nextBtn = new QPushButton("Далее");
nextBtn->setStyleSheet("background-color: green; color: white; padding: 6px 24px;");
connect(nextBtn, &QPushButton::clicked, this, [this]() {
m_stack->setCurrentIndex(1);
});
btnLayout->addWidget(nextBtn);
layout->addLayout(btnLayout);
return page;
}
// ── Step 2: PIN Code ───────────────────────────────────────────────────────
QWidget *BankSetupWizard::createStep2() {
auto *page = new QWidget;
auto *layout = new QVBoxLayout(page);
layout->setAlignment(Qt::AlignTop);
auto *title = new QLabel("<h3>Введите пин-код</h3>");
title->setAlignment(Qt::AlignCenter);
layout->addWidget(title);
auto *desc = new QLabel("Укажите пин-код для входа в приложение " + m_app.name + ".");
desc->setWordWrap(true);
desc->setAlignment(Qt::AlignCenter);
layout->addWidget(desc);
layout->addSpacing(16);
m_pinCodeEdit = new QLineEdit;
m_pinCodeEdit->setPlaceholderText("Пин-код");
m_pinCodeEdit->setMaxLength(10);
m_pinCodeEdit->setAlignment(Qt::AlignCenter);
m_pinCodeEdit->setFixedWidth(200);
auto *editLayout = new QHBoxLayout;
editLayout->addStretch();
editLayout->addWidget(m_pinCodeEdit);
editLayout->addStretch();
layout->addLayout(editLayout);
layout->addStretch();
auto *btnLayout = new QHBoxLayout;
auto *backBtn = new QPushButton("Назад");
connect(backBtn, &QPushButton::clicked, this, [this]() {
m_stack->setCurrentIndex(0);
});
btnLayout->addWidget(backBtn);
btnLayout->addStretch();
auto *nextBtn = new QPushButton("Далее");
nextBtn->setStyleSheet("background-color: green; color: white; padding: 6px 24px;");
connect(nextBtn, &QPushButton::clicked, this, [this]() {
if (m_pinCodeEdit->text().trimmed().isEmpty()) return;
m_app.pinCode = m_pinCodeEdit->text().trimmed();
m_stack->setCurrentIndex(2);
startVerification();
});
btnLayout->addWidget(nextBtn);
layout->addLayout(btnLayout);
return page;
}
// ── Step 3: Progress ───────────────────────────────────────────────────────
QWidget *BankSetupWizard::createStep3() {
auto *page = new QWidget;
auto *layout = new QVBoxLayout(page);
layout->setAlignment(Qt::AlignTop);
auto *title = new QLabel("<h3>Идёт проверка банка</h3>");
title->setAlignment(Qt::AlignCenter);
layout->addWidget(title);
auto *desc = new QLabel(
"Пожалуйста, не закрывайте приложение и не трогайте телефон.\n"
"Проверка может занять некоторое время."
);
desc->setWordWrap(true);
desc->setAlignment(Qt::AlignCenter);
desc->setStyleSheet("color: gray; margin-bottom: 12px;");
layout->addWidget(desc);
m_progressBar = new QProgressBar;
m_progressBar->setRange(0, 100);
m_progressBar->setValue(0);
m_progressBar->setTextVisible(true);
layout->addWidget(m_progressBar);
m_progressLabel = new QLabel("Подготовка...");
m_progressLabel->setAlignment(Qt::AlignCenter);
m_progressLabel->setStyleSheet("margin-top: 8px;");
layout->addWidget(m_progressLabel);
layout->addStretch();
auto *btnLayout = new QHBoxLayout;
m_backBtn3 = new QPushButton("Назад");
connect(m_backBtn3, &QPushButton::clicked, this, [this]() {
stopWorkerThread();
m_progressBar->setValue(0);
m_progressLabel->setText("Подготовка...");
m_stack->setCurrentIndex(1);
});
btnLayout->addWidget(m_backBtn3);
btnLayout->addStretch();
layout->addLayout(btnLayout);
return page;
}
// ── Step 4a: Success ───────────────────────────────────────────────────────
QWidget *BankSetupWizard::createStep4Success() {
m_successPage = new QWidget;
auto *layout = new QVBoxLayout(m_successPage);
layout->setAlignment(Qt::AlignTop);
auto *title = new QLabel("<h3 style='color:green;'>Проверка прошла успешно</h3>");
title->setAlignment(Qt::AlignCenter);
layout->addWidget(title);
m_resultLabel = new QLabel;
m_resultLabel->setWordWrap(true);
m_resultLabel->setTextFormat(Qt::RichText);
layout->addWidget(m_resultLabel);
auto *cardsTitle = new QLabel("<b>Найденные карты:</b> выберите одну активную");
layout->addWidget(cardsTitle);
m_cardsTable = new QTableWidget;
m_cardsTable->setSelectionMode(QAbstractItemView::NoSelection);
m_cardsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_cardsTable->setFocusPolicy(Qt::NoFocus);
m_cardsTable->setColumnCount(4);
m_cardsTable->setHorizontalHeaderLabels({"Номер", "Сумма", "Валюта", "Активна"});
m_cardsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
layout->addWidget(m_cardsTable);
m_cardsGroup = new QButtonGroup(this);
m_cardsGroup->setExclusive(true);
connect(m_cardsGroup, &QButtonGroup::idClicked, this, [this](int id) {
m_selectedMaterialId = id;
m_finishBtn->setEnabled(true);
});
layout->addStretch();
auto *btnLayout = new QHBoxLayout;
btnLayout->addStretch();
m_finishBtn = new QPushButton("Завершить");
m_finishBtn->setStyleSheet("background-color: green; color: white; padding: 6px 24px;");
m_finishBtn->setEnabled(false);
connect(m_finishBtn, &QPushButton::clicked, this, [this]() {
applyActiveCardSelection();
accept();
});
btnLayout->addWidget(m_finishBtn);
layout->addLayout(btnLayout);
return m_successPage;
}
// ── Step 4b: Error ─────────────────────────────────────────────────────────
QWidget *BankSetupWizard::createStep4Error() {
m_errorPage = new QWidget;
auto *layout = new QVBoxLayout(m_errorPage);
layout->setAlignment(Qt::AlignTop);
auto *title = new QLabel("<h3 style='color:red;'>Ошибка</h3>");
title->setAlignment(Qt::AlignCenter);
layout->addWidget(title);
m_errorText = new QLabel;
m_errorText->setWordWrap(true);
m_errorText->setStyleSheet("color: red; margin: 12px 0;");
m_errorText->setAlignment(Qt::AlignCenter);
layout->addWidget(m_errorText);
layout->addStretch();
auto *btnLayout = new QHBoxLayout;
auto *backBtn = new QPushButton("Назад");
connect(backBtn, &QPushButton::clicked, this, [this]() {
m_stack->setCurrentIndex(1);
});
btnLayout->addWidget(backBtn);
btnLayout->addStretch();
auto *restartBtn = new QPushButton("Начать заново");
restartBtn->setStyleSheet("background-color: orange; color: white; padding: 6px 24px;");
connect(restartBtn, &QPushButton::clicked, this, [this]() {
// Очищаем данные из БД если ещё не отправлены на сервер
const BankProfileInfo current = BankProfileDAO::getApplication(m_deviceId, m_app.code);
if (current.id >= 0 && current.bankProfileId.isEmpty()) {
MaterialDAO::deleteAccountsByApp(m_deviceId, m_app.code);
BankProfileDAO::deleteProfile(m_deviceId, m_app.code);
m_app.id = -1;
m_app.pinCodeChecked = false;
m_app.pinCode.clear();
}
m_pinCodeEdit->clear();
m_progressBar->setValue(0);
m_progressLabel->setText("Подготовка...");
m_stack->setCurrentIndex(0);
});
btnLayout->addWidget(restartBtn);
layout->addLayout(btnLayout);
return m_errorPage;
}
// ── Pause/Resume active profiles ───────────────────────────────────────────
static void toggleProfileOnServer(const BankProfileInfo &app, bool enable) {
if (app.bankProfileId.isEmpty()) return;
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 BankSetupWizard::pauseActiveProfiles() {
const QList<BankProfileInfo> apps = BankProfileDAO::getApplicationsByDeviceId(m_deviceId);
for (const BankProfileInfo &app : apps) {
if (app.status == "active") {
m_pausedProfiles.append(app.code);
BankProfileDAO::update(app.id, app.pinCode, app.comment, "off");
toggleProfileOnServer(app, false);
qDebug() << "[BankSetupWizard] Paused profile:" << app.code;
}
}
}
void BankSetupWizard::resumePausedProfiles() {
for (const QString &code : m_pausedProfiles) {
const BankProfileInfo app = BankProfileDAO::getApplication(m_deviceId, code);
if (app.id >= 0 && app.status == "off") {
BankProfileDAO::update(app.id, app.pinCode, app.comment, "active");
toggleProfileOnServer(app, true);
qDebug() << "[BankSetupWizard] Resumed profile:" << app.code;
}
}
m_pausedProfiles.clear();
}
// ── Verification flow ──────────────────────────────────────────────────────
void BankSetupWizard::setProgress(int value, const QString &text) {
m_progressBar->setValue(value);
m_progressLabel->setText(text);
}
void BankSetupWizard::stopWorkerThread() {
if (!m_workerThread) return;
QThread *t = m_workerThread;
m_workerThread = nullptr;
t->requestInterruption();
t->quit();
if (t->wait(3000)) {
t->deleteLater();
return;
}
// Воркер залип (скорее всего в AdbUtils::startProcess / QProcess::waitForFinished).
// Отсоединяем QThread от виджета, чтобы ~QWidget не удалил работающий поток
// (это триггерит qFatal в ~QThread), и откладываем удаление до естественного завершения.
qWarning() << "[BankSetupWizard] worker thread still running after 3s, detaching";
t->setParent(nullptr);
connect(t, &QThread::finished, t, &QObject::deleteLater);
}
void BankSetupWizard::startVerification() {
// Проверяем что устройство онлайн
const DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId);
if (device.status != "device") {
onVerificationError("Устройство не подключено.\nУбедитесь, что телефон подключён и попробуйте снова.");
return;
}
// Save pin code to DB first via upsert
m_app.status = "off";
m_app.install = true;
BankProfileDAO::upsertApplication(m_app);
// Re-read to get the id
m_app = BankProfileDAO::getApplication(m_deviceId, m_app.code);
setProgress(5, "Запуск проверки пин-кода...");
m_backBtn3->setEnabled(true);
runPinCheck();
}
void BankSetupWizard::runPinCheck() {
stopWorkerThread();
m_workerThread = new QThread(this);
const QString deviceId = m_deviceId;
const QString appCode = m_app.code;
const int appId = m_app.id;
if (appCode == "ozon") {
auto *worker = new Ozon::LoginAndCheckAccountsScript(deviceId, appCode, nullptr, false, true);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Ozon::LoginAndCheckAccountsScript::start);
connect(worker, &Ozon::LoginAndCheckAccountsScript::finishedWithResult, this,
[this, appId, appCode](const QString &result) {
if (!result.isEmpty()) {
onVerificationError("Ошибка проверки пин-кода: " + result);
return;
}
BankProfileDAO::updatePinCodeStatus(appId, "yes", "");
m_app.pinCodeChecked = true;
setProgress(33, "Пин-код проверен. Получение профиля...");
runProfileCheck();
}, Qt::QueuedConnection);
connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater);
connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, m_workerThread, &QThread::quit);
} else if (appCode == "black") {
auto *worker = new Black::LoginAndCheckAccountsScript(deviceId, appCode, nullptr, true);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Black::LoginAndCheckAccountsScript::start);
connect(worker, &Black::LoginAndCheckAccountsScript::finishedWithResult, this,
[this, appId](const QString &result) {
if (!result.isEmpty()) {
onVerificationError("Ошибка проверки пин-кода: " + result);
return;
}
BankProfileDAO::updatePinCodeStatus(appId, "yes", "");
m_app.pinCodeChecked = true;
setProgress(33, "Пин-код проверен. Получение профиля...");
runProfileCheck();
}, Qt::QueuedConnection);
connect(worker, &Black::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater);
connect(worker, &Black::LoginAndCheckAccountsScript::finished, m_workerThread, &QThread::quit);
} else if (appCode == "dushanbe_city_bank") {
auto *worker = new Dushanbe::LoginAndCheckAccountsScript(deviceId, appCode, nullptr, true);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Dushanbe::LoginAndCheckAccountsScript::start);
connect(worker, &Dushanbe::LoginAndCheckAccountsScript::finishedWithResult, this,
[this, appId](const QString &result) {
if (!result.isEmpty()) {
onVerificationError("Ошибка проверки пин-кода: " + result);
return;
}
BankProfileDAO::updatePinCodeStatus(appId, "yes", "");
m_app.pinCodeChecked = true;
setProgress(33, "Пин-код проверен. Получение профиля...");
runProfileCheck();
}, Qt::QueuedConnection);
connect(worker, &Dushanbe::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater);
connect(worker, &Dushanbe::LoginAndCheckAccountsScript::finished, m_workerThread, &QThread::quit);
} else {
onVerificationError("Банк " + appCode + " не поддерживается для автоматической настройки.");
return;
}
m_workerThread->start();
}
void BankSetupWizard::runProfileCheck() {
stopWorkerThread();
m_workerThread = new QThread(this);
const QString deviceId = m_deviceId;
const QString appCode = m_app.code;
auto onProfileResult = [this, deviceId, appCode](const QString &result) {
if (!result.isEmpty()) {
onVerificationError("Ошибка получения профиля: " + result);
return;
}
// Проверяем что bank_profile_id получен от сервера
const BankProfileInfo updated = BankProfileDAO::getApplication(deviceId, appCode);
if (updated.bankProfileId.isEmpty()) {
onVerificationError("Не удалось зарегистрировать банковский профиль на сервере.\n"
"Возможно, банк ещё не поддерживается сервером.");
return;
}
setProgress(66, "Профиль получен. Проверка карт...");
runCardCheck();
};
if (appCode == "ozon") {
auto *worker = new Ozon::GetProfileInfoScript(deviceId, appCode, nullptr);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Ozon::GetProfileInfoScript::start);
connect(worker, &Ozon::GetProfileInfoScript::finishedWithResult, this, onProfileResult, Qt::QueuedConnection);
connect(worker, &Ozon::GetProfileInfoScript::finished, worker, &QObject::deleteLater);
connect(worker, &Ozon::GetProfileInfoScript::finished, m_workerThread, &QThread::quit);
} else if (appCode == "black") {
auto *worker = new Black::GetProfileInfoScript(deviceId, appCode, nullptr);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Black::GetProfileInfoScript::start);
connect(worker, &Black::GetProfileInfoScript::finishedWithResult, this, onProfileResult, Qt::QueuedConnection);
connect(worker, &Black::GetProfileInfoScript::finished, worker, &QObject::deleteLater);
connect(worker, &Black::GetProfileInfoScript::finished, m_workerThread, &QThread::quit);
} else if (appCode == "dushanbe_city_bank") {
auto *worker = new Dushanbe::GetProfileInfoScript(deviceId, appCode, nullptr);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Dushanbe::GetProfileInfoScript::start);
connect(worker, &Dushanbe::GetProfileInfoScript::finishedWithResult, this, onProfileResult, Qt::QueuedConnection);
connect(worker, &Dushanbe::GetProfileInfoScript::finished, worker, &QObject::deleteLater);
connect(worker, &Dushanbe::GetProfileInfoScript::finished, m_workerThread, &QThread::quit);
} else {
onVerificationError("Банк " + appCode + " не поддерживается.");
return;
}
m_workerThread->start();
}
void BankSetupWizard::runCardCheck() {
stopWorkerThread();
m_workerThread = new QThread(this);
const QString deviceId = m_deviceId;
const QString appCode = m_app.code;
auto onCardResult = [this, deviceId, appCode](const QString &result) {
if (!result.isEmpty()) {
onVerificationError("Ошибка получения карт: " + result);
return;
}
// Проверяем что хотя бы одна карта синхронизирована с сервером
const QList<MaterialInfo> materials = MaterialDAO::getAccounts(deviceId, appCode);
bool hasSynced = false;
for (const auto &m : materials) {
if (!m.materialId.isEmpty()) { hasSynced = true; break; }
}
if (!hasSynced && !materials.isEmpty()) {
onVerificationError("Не удалось отправить данные карт на сервер.\n"
"Проверьте подключение к серверу.");
return;
}
setProgress(100, "Готово!");
onVerificationSuccess();
};
if (appCode == "ozon") {
auto *worker = new Ozon::GetCardInfoScript(deviceId, appCode, "", nullptr);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Ozon::GetCardInfoScript::start);
connect(worker, &Ozon::GetCardInfoScript::finishedWithResult, this, onCardResult, Qt::QueuedConnection);
connect(worker, &Ozon::GetCardInfoScript::finished, worker, &QObject::deleteLater);
connect(worker, &Ozon::GetCardInfoScript::finished, m_workerThread, &QThread::quit);
} else if (appCode == "black") {
auto *worker = new Black::GetCardInfoScript(deviceId, appCode, nullptr);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Black::GetCardInfoScript::start);
connect(worker, &Black::GetCardInfoScript::finishedWithResult, this, onCardResult, Qt::QueuedConnection);
connect(worker, &Black::GetCardInfoScript::finished, worker, &QObject::deleteLater);
connect(worker, &Black::GetCardInfoScript::finished, m_workerThread, &QThread::quit);
} else if (appCode == "dushanbe_city_bank") {
auto *worker = new Dushanbe::GetCardInfoScript(deviceId, appCode, nullptr);
worker->moveToThread(m_workerThread);
connect(m_workerThread, &QThread::started, worker, &Dushanbe::GetCardInfoScript::start);
connect(worker, &Dushanbe::GetCardInfoScript::finishedWithResult, this, onCardResult, Qt::QueuedConnection);
connect(worker, &Dushanbe::GetCardInfoScript::finished, worker, &QObject::deleteLater);
connect(worker, &Dushanbe::GetCardInfoScript::finished, m_workerThread, &QThread::quit);
} else {
onVerificationError("Банк " + appCode + " не поддерживается.");
return;
}
m_workerThread->start();
}
void BankSetupWizard::onVerificationSuccess() {
// Re-read profile from DB
m_app = BankProfileDAO::getApplication(m_deviceId, m_app.code);
QString profileHtml;
if (!m_app.fullName.isEmpty())
profileHtml += "<b>ФИО:</b> " + m_app.fullName + "<br>";
if (!m_app.phone.isEmpty())
profileHtml += "<b>Телефон:</b> " + m_app.phone + "<br>";
if (!m_app.email.isEmpty())
profileHtml += "<b>Email:</b> " + m_app.email + "<br>";
if (profileHtml.isEmpty())
profileHtml = "<i>Данные профиля не получены</i>";
m_resultLabel->setText(profileHtml);
// Load cards
const QList<MaterialInfo> cards = MaterialDAO::getAccounts(m_deviceId, m_app.code);
// Очистка предыдущих радио-кнопок, если мастер перезапускался
for (QAbstractButton *b : m_cardsGroup->buttons()) {
m_cardsGroup->removeButton(b);
b->deleteLater();
}
m_selectedMaterialId = -1;
m_cardsTable->setRowCount(cards.size());
for (int i = 0; i < cards.size(); ++i) {
const MaterialInfo &c = cards[i];
const QString displayNumber = c.cardNumber.isEmpty() ? c.lastNumbers : c.cardNumber;
m_cardsTable->setItem(i, 0, new QTableWidgetItem(displayNumber));
m_cardsTable->setItem(i, 1, new QTableWidgetItem(QString::number(c.amount)));
m_cardsTable->setItem(i, 2, new QTableWidgetItem(c.currency));
auto *radio = new QRadioButton;
auto *wrapper = new QWidget;
auto *h = new QHBoxLayout(wrapper);
h->addWidget(radio);
h->setAlignment(Qt::AlignCenter);
h->setContentsMargins(0, 0, 0, 0);
m_cardsTable->setCellWidget(i, 3, wrapper);
m_cardsGroup->addButton(radio, c.id);
}
m_cardsTable->resizeColumnsToContents();
if (cards.isEmpty()) {
m_cardsTable->setRowCount(1);
auto *item = new QTableWidgetItem("Карты не найдены");
item->setForeground(QBrush(Qt::gray));
m_cardsTable->setItem(0, 0, item);
m_cardsTable->setSpan(0, 0, 1, 4);
m_finishBtn->setEnabled(true); // нечего выбирать — выйти можно
} else if (cards.size() == 1) {
// Одна карта — автовыбор, скрываем колонку с радио
m_selectedMaterialId = cards.first().id;
m_cardsTable->setColumnHidden(3, true);
m_finishBtn->setEnabled(true);
} else {
m_cardsTable->setColumnHidden(3, false);
m_selectedMaterialId = cards.first().id;
if (auto *first = m_cardsGroup->button(m_selectedMaterialId)) {
first->setChecked(true);
}
m_finishBtn->setEnabled(true);
}
m_stack->setCurrentIndex(3);
}
void BankSetupWizard::applyActiveCardSelection() {
if (m_selectedMaterialId < 0) return;
const QList<MaterialInfo> deactivated =
MaterialDAO::activateExclusive(m_deviceId, m_app.code, m_selectedMaterialId);
const MaterialInfo selected = MaterialDAO::getAccountById(m_selectedMaterialId);
auto sendToggle = [](const QString &materialId, bool enable) {
if (materialId.isEmpty()) return;
auto *thread = new QThread;
auto *net = new NetworkService;
net->moveToThread(thread);
net->addExtra("material_id", materialId);
net->addExtra("enable", enable);
QObject::connect(thread, &QThread::started, net, &NetworkService::toggleMaterial);
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();
};
sendToggle(selected.materialId, true);
for (const MaterialInfo &m : deactivated) {
sendToggle(m.materialId, false);
}
}
static QString humanReadableError(const QString &error) {
// Определяем этап
QString stage;
if (error.contains("пин-кода"))
stage = "Проверка пин-кода: ";
else if (error.contains("профиля"))
stage = "Получение профиля: ";
else if (error.contains("карт"))
stage = "Получение карт: ";
if (error.contains("Device or app not found"))
return stage + "Устройство или приложение не найдено.\nУбедитесь, что телефон подключён и приложение установлено.";
if (error.contains("Cant open the home screen") || error.contains("Cannot reach home screen"))
return stage + "Не удалось открыть главный экран приложения.\nПопробуйте перезапустить приложение на телефоне и повторить.";
if (error.contains("Cannot find user name") || error.contains("Cannot find Hola button"))
return stage + "Не удалось найти данные профиля на главном экране.\nВозможно, приложение обновилось или экран выглядит иначе.";
if (error.contains("Profile screen not detected") || error.contains("Profile page not detected"))
return stage + "Не удалось открыть страницу профиля.\nПопробуйте повторить позже.";
if (error.contains("Side menu not detected"))
return stage + "Не удалось открыть боковое меню приложения.\nПопробуйте повторить.";
if (error.contains("Cannot find Tu Informaci") || error.contains("Cannot find Tu CVU"))
return stage + "Не удалось найти нужный раздел в меню приложения.\nВозможно, приложение обновилось.";
if (error.contains("CVU screen not detected") || error.contains("Cannot parse CVU"))
return stage + "Не удалось получить данные карты.\nПопробуйте повторить.";
if (error.contains("No cards found"))
return stage + "Карты не найдены на главном экране.\nУбедитесь, что в приложении есть привязанные карты.";
if (error.contains("не поддерживается") || error.contains("не подключено"))
return error;
return stage + "Произошла непредвиденная ошибка.\nПопробуйте повторить позже.";
}
void BankSetupWizard::onVerificationError(const QString &error) {
qWarning() << "[BankSetupWizard] Error:" << error;
m_errorText->setText(humanReadableError(error));
m_stack->setCurrentIndex(4);
}