618 lines
28 KiB
C++
618 lines
28 KiB
C++
#include "BankSetupWizard.h"
|
||
|
||
#include <QVBoxLayout>
|
||
#include <QHBoxLayout>
|
||
#include <QHeaderView>
|
||
#include <QCoreApplication>
|
||
|
||
#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(3);
|
||
m_cardsTable->setHorizontalHeaderLabels({"Номер", "Сумма", "Валюта"});
|
||
m_cardsTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
|
||
layout->addWidget(m_cardsTable);
|
||
|
||
layout->addStretch();
|
||
|
||
auto *btnLayout = new QHBoxLayout;
|
||
btnLayout->addStretch();
|
||
auto *finishBtn = new QPushButton("Завершить");
|
||
finishBtn->setStyleSheet("background-color: green; color: white; padding: 6px 24px;");
|
||
connect(finishBtn, &QPushButton::clicked, this, &QDialog::accept);
|
||
btnLayout->addWidget(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);
|
||
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));
|
||
}
|
||
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, 3);
|
||
}
|
||
|
||
m_stack->setCurrentIndex(3);
|
||
}
|
||
|
||
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);
|
||
}
|