1
0
forked from BRT/arc
arc/android/ozon/GetCardInfoScript.cpp

579 lines
30 KiB
C++
Raw Permalink 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 "GetCardInfoScript.h"
#include <QDateTime>
#include <QJsonDocument>
#include <QJsonObject>
#include <QSettings>
#include <QThread>
#include "adb/AdbUtils.h"
#include "dao/MaterialDAO.h"
#include "dao/BankProfileDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/SettingsDAO.h"
#include "dump/TaskDumpRecorder.h"
#include "net/NetworkService.h"
#include "AppLogger.h"
namespace Ozon {
GetCardInfoScript::GetCardInfoScript(
QString deviceId,
QString appCode,
QString lastNumbers,
QObject *parent
) : CommonScript(parent),
m_deviceId(std::move(deviceId)),
m_appCode(std::move(appCode)),
m_lastNumbers(std::move(lastNumbers)) {
}
GetCardInfoScript::~GetCardInfoScript() = default;
void GetCardInfoScript::doStart() {
DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); if (device.id.isEmpty()) device = DeviceDAO::findByAdbSerial(m_deviceId); if (!device.adbSerial.isEmpty()) m_deviceId = device.adbSerial;
const BankProfileInfo app = BankProfileDAO::getApplication(device.id, m_appCode);
if (device.id.isEmpty() || app.id == -1) {
m_error = "Device or app not found: " + m_deviceId + " " + m_appCode;
qCritical() << m_error;
emit finishedWithResult(m_error);
return;
}
const TaskDumpMeta dumpMeta{};
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
// Читаем текущий экран
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Закрываем рекламный bottom sheet, если открыт
if (Node adNode = xmlScreenParser.tryToFindAdBanner(); !adNode.isEmpty()) {
qDebug() << "[GetCardInfo] Ad banner detected, closing...";
AdbUtils::makeTap(m_deviceId, adNode.x(), adNode.y());
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
}
// Свежий дамп перед проверкой домашнего экрана
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Если не на домашнем экране — перезапускаем приложение
if (!xmlScreenParser.isHomeScreen()) {
qDebug() << "[GetCardInfo] Not on home screen, restarting app...";
if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode,
device.screenWidth, device.screenHeight)) {
m_error = "Cannot reach home screen: " + device.name + " (" + m_deviceId + ")";
qWarning() << m_error;
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
return;
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
}
// Закрываем баннеры на главной
tryToCloseAllBannersOnHomePage(m_deviceId, device.screenWidth, device.screenHeight);
QThread::msleep(500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Переходим в профиль и парсим имя и телефон (только если ещё нет в БД)
const bool hasProfileInfo = !app.fullName.isEmpty() && !app.phone.isEmpty();
Node userNameNode;
if (!hasProfileInfo) {
userNameNode = xmlScreenParser.findUserNameOnHomeScreen(device.screenHeight);
}
if (!hasProfileInfo && !userNameNode.isEmpty()) {
qDebug() << "[GetCardInfo] Navigating to profile to parse name/phone...";
AdbUtils::makeTap(m_deviceId, userNameNode.x(), userNameNode.y());
QThread::msleep(2500);
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
if (xmlScreenParser.isProfileScreen()) break;
QThread::msleep(1000);
}
if (xmlScreenParser.isProfileScreen()) {
const QString fullName = xmlScreenParser.parseProfileFullName();
const QString phone = xmlScreenParser.parseProfilePhone();
qDebug() << "[GetCardInfo] Profile parsed - fullName:" << fullName << "phone:" << phone;
BankProfileDAO::updateProfileInfo(app.id, fullName, phone);
} else {
qWarning() << "[GetCardInfo] Profile screen not reached, skipping profile parse";
}
AdbUtils::goBack(m_deviceId);
QThread::msleep(1500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
} else {
qDebug() << "[GetCardInfo] Profile info already in DB or username not found, skipping profile parse";
}
// Проверяем что на домашнем экране перед поиском карт
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
if (!xmlScreenParser.isHomeScreen()) {
qDebug() << "[GetCardInfo] Not on home screen before card search, restarting...";
if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode,
device.screenWidth, device.screenHeight)) {
m_error = "Cannot reach home screen before card search: " + device.name + " (" + m_deviceId + ")";
qWarning() << m_error;
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
return;
}
tryToCloseAllBannersOnHomePage(m_deviceId, device.screenWidth, device.screenHeight);
QThread::msleep(500);
}
// Ждём пока все карты подгрузятся — до 5 попыток
QList<MaterialInfo> cards;
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight)) {
// баннер закрыт, дамп уже обновлён внутри helper'а
}
cards = xmlScreenParser.parseCardsOnHomeScreen();
if (!cards.isEmpty()) break;
qDebug() << "[GetCardInfo] No cards found on home screen, attempt" << attempt + 1 << "/ 5";
QThread::msleep(1500);
}
if (cards.isEmpty()) {
m_error = "No cards found on home screen: " + m_deviceId;
qWarning() << m_error;
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
return;
}
qDebug() << "[GetCardInfo] Found" << cards.size() << "card(s) on home screen";
// Заполняем метаданные на найденных картах. Новые карты (которых ещё нет в БД)
// не создаём здесь — запись появится только после успешного OCR полного номера
// в success-ветке ниже. Это защищает от записей из ленты переводов и не оставляет
// в БД материал с пустым card_number.
//
// Для карт, которые уже в БД, обновляем только amount/update_time —
// прочие поля (cardNumber, materialId, status, ...) сохраняем как было.
for (MaterialInfo &card : cards) {
card.deviceId = device.id;
card.appCode = m_appCode;
card.updateTime = QDateTime::currentDateTimeUtc();
card.status = MaterialStatus::Off;
const MaterialInfo existing = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers);
if (existing.id == -1) continue;
MaterialInfo refresh = existing;
refresh.amount = card.amount;
refresh.updateTime = card.updateTime;
if (!MaterialDAO::upsertAccount(refresh)) {
qWarning() << "[GetCardInfo] Failed to refresh amount for card" << card.lastNumbers;
} else {
qDebug() << "[GetCardInfo] Refreshed amount for card"
<< card.lastNumbers << "to" << card.amount;
}
}
// Сохраняем баланс домашнего экрана в application
const double totalAmount = cards.isEmpty() ? 0.0 : cards[0].amount;
if (totalAmount > 0.0) {
BankProfileDAO::updateAmount(app.id, totalAmount);
}
// Синхронная отправка на сервер (NetworkService блокируется через QEventLoop внутри)
NetworkService ns;
ns.setDeviceId(m_deviceId);
// 1. Создаём девайс если нет api_id
if (device.apiId.isEmpty()) {
QJsonObject deviceBody;
deviceBody["desktop_id"] = SettingsDAO::get("desktop_id");
deviceBody["status"] = "ONLINE";
deviceBody["name"] = device.name;
deviceBody["screen_width"] = device.screenWidth;
deviceBody["screen_height"]= device.screenHeight;
deviceBody["battery"] = device.battery;
ns.setPayload(QJsonDocument(deviceBody).toJson());
ns.createDevice();
qDebug() << "[GetCardInfo] createDevice done";
}
// 2. Отправляем банковский профиль (используем свежие данные из БД — имя/телефон уже спарсены)
{
const BankProfileInfo freshApp = BankProfileDAO::getApplication(device.id, m_appCode);
const QStringList parts = freshApp.fullName.split(' ', Qt::SkipEmptyParts);
// ISO 4217 numeric currency codes
const QMap<QString, int> currencyCodes = {
{"RUB", 643}, {"USD", 840}, {"KRW", 410}, {"AZN", 944}, {"ARS", 32}, {"TJS", 972}
};
const int currencyCode = currencyCodes.value(freshApp.currency.toUpper(), 643);
QJsonObject profileBody;
profileBody["first_name"] = parts.value(0);
profileBody["middle_name"] = parts.size() >= 3 ? parts.value(1) : "";
profileBody["last_name"] = parts.size() >= 3 ? parts.value(2) : parts.value(1);
profileBody["bank_name"] = m_appCode;
// Телефон: убираем пробелы, оставляем "+"
const QString phone = freshApp.phone.simplified().remove(' ');
profileBody["phone_number"] = phone;
profileBody["balance"] = QString::number(qRound64(totalAmount * 100));
profileBody["state"] = (freshApp.status == "active") ? "enabled" : "disabled";
profileBody["device_id"] = device.apiId;
profileBody["currency"] = currencyCode;
ns.setPayload(QJsonDocument(profileBody).toJson());
if (freshApp.bankProfileId.isEmpty()) {
ns.addExtra("app_id", freshApp.id);
ns.postBankProfile();
// Проверяем что bankProfileId сохранился
const BankProfileInfo afterPost = BankProfileDAO::getApplication(device.id, m_appCode);
if (afterPost.bankProfileId.isEmpty()) {
const QString err = "postBankProfile failed for " + m_deviceId;
qWarning() << "[GetCardInfo]" << err;
AppLogger::log("ozon/GetCardInfo", m_deviceId, err,
AdbUtils::captureScreenshotBytes(m_deviceId),
"FETCH_PROFILE");
m_error = err;
emit finishedWithResult(m_error);
return;
}
qDebug() << "[GetCardInfo] postBankProfile done";
} else {
ns.addExtra("bank_profile_id", freshApp.bankProfileId);
ns.patchBankProfile();
qDebug() << "[GetCardInfo] patchBankProfile done";
}
}
// Для каждой карты переходим на её экран и получаем полный номер через OCR
for (int i = 0; i < cards.size(); ++i) {
const MaterialInfo &card = cards[i];
// Если передан конкретный lastNumbers — обрабатываем только его
if (!m_lastNumbers.isEmpty() && card.lastNumbers != m_lastNumbers) continue;
qDebug() << "[GetCardInfo] Getting card number for" << card.lastNumbers;
// Если номер карты уже есть в БД — сумма уже обновлена через upsert, навигация не нужна
MaterialInfo existing = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers);
if (existing.id != -1 && !existing.cardNumber.isEmpty()) {
qDebug() << "[GetCardInfo] Card" << card.lastNumbers << "already has number, skipping";
continue;
}
// Тапаем по кнопке конкретной карты — каждая открывает свой экран
Node cardBtn = xmlScreenParser.findCardButtonByLastNumbers(card.lastNumbers);
if (cardBtn.isEmpty()) {
qWarning() << "[GetCardInfo] Card button not found for" << card.lastNumbers;
continue;
}
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
// Скроллим WebView вверх, пока "Показать" не окажется в верхней трети экрана.
// На устройствах с короткой высотой (например, SM-A165F, h=2340) кнопка "Показать"
// находится за bottom navigation, и тап уходит в нав-бар.
const int targetTopY = device.screenHeight / 3;
for (int scrollAttempt = 0; scrollAttempt < 6; ++scrollAttempt) {
QThread::msleep(scrollAttempt == 0 ? 2000 : 600);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
Node candidate = xmlScreenParser.findButtonNode("Показать", false);
if (candidate.isEmpty()) {
if (xmlScreenParser.isHomeScreen()) {
cardBtn = xmlScreenParser.findCardButtonByLastNumbers(card.lastNumbers);
if (!cardBtn.isEmpty()) {
qDebug() << "[GetCardInfo] Still on home before scroll, re-tapping card" << card.lastNumbers;
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
}
}
continue;
}
if (candidate.y() <= targetTopY) {
qDebug() << "[GetCardInfo] 'Показать' already at top y=" << candidate.y();
break;
}
const int swipeFromY = candidate.y();
const int swipeToY = device.screenHeight / 6;
qDebug() << "[GetCardInfo] Scrolling 'Показать' up:" << swipeFromY << "->" << swipeToY;
AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2, swipeFromY,
device.screenWidth / 2, swipeToY);
}
// Ждём экран карты и стабильной геометрии блока реквизитов.
// Без этого тап по "Показать" во время reflow уезжает ниже — в "Защиту от мошенников".
// Стабильность: "Показать" совпадает по y1 с "Реквизиты карты" И координаты не меняются в двух подряд дампах.
Node showBtn;
int prevShowY = -1;
for (int attempt = 0; attempt < 10; ++attempt) {
QThread::msleep(attempt == 0 ? 2000 : 500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
Node candidate = xmlScreenParser.findButtonNode("Показать", false);
Node requisites = xmlScreenParser.findTextNode("Реквизиты карты");
if (candidate.isEmpty() || requisites.isEmpty()) {
// Если всё ещё на домашнем экране — тап по карточке не сработал, повторяем
if (xmlScreenParser.isHomeScreen()) {
cardBtn = xmlScreenParser.findCardButtonByLastNumbers(card.lastNumbers);
if (!cardBtn.isEmpty()) {
qDebug() << "[GetCardInfo] Still on home screen, re-tapping card" << card.lastNumbers;
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
}
}
prevShowY = -1;
qDebug() << "[GetCardInfo] Requisites not ready, attempt" << attempt + 1 << "/ 10";
continue;
}
// "Показать" должен быть в одной строке с "Реквизиты карты"
if (qAbs(candidate.y1() - requisites.y1()) > 10) {
prevShowY = -1;
qDebug() << "[GetCardInfo] 'Показать' not aligned with 'Реквизиты карты', waiting reflow";
continue;
}
// Y должна совпасть в двух подряд дампах (layout устаканился)
if (prevShowY >= 0 && qAbs(candidate.y() - prevShowY) <= 5) {
showBtn = candidate;
break;
}
prevShowY = candidate.y();
qDebug() << "[GetCardInfo] 'Показать' seen at y=" << candidate.y() << ", waiting for stable frame";
}
if (showBtn.isEmpty()) {
qWarning() << "[GetCardInfo] 'Показать' not stable for card" << card.lastNumbers;
AdbUtils::goBack(m_deviceId);
QThread::msleep(1500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
continue;
}
qDebug() << "[GetCardInfo] 'Показать' at" << showBtn.x() << showBtn.y();
AdbUtils::makeTap(m_deviceId, showBtn.x(), showBtn.y());
// Ждём появления полного номера карты в XML дампе
QString cardNumber;
for (int attempt = 0; attempt < 10; ++attempt) {
QThread::msleep(2000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
cardNumber = xmlScreenParser.findFullCardNumber();
if (!cardNumber.isEmpty()) {
qDebug() << "[GetCardInfo] Card number found on attempt" << attempt + 1;
break;
}
// Закрываем баннер "Понятно" и повторно нажимаем "Показать"
Node bannerBtn = xmlScreenParser.findTextNode("Понятно");
if (!bannerBtn.isEmpty()) {
qDebug() << "[GetCardInfo] Banner 'Понятно' detected, closing...";
AdbUtils::makeTap(m_deviceId, bannerBtn.x(), bannerBtn.y());
QThread::msleep(1500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
Node retryShowBtn = xmlScreenParser.findButtonNode("Показать", false);
if (!retryShowBtn.isEmpty()) {
qDebug() << "[GetCardInfo] Re-tapping 'Показать' after banner close";
AdbUtils::makeTap(m_deviceId, retryShowBtn.x(), retryShowBtn.y());
}
continue;
}
// "Подождите" — загрузка в процессе, просто ждём
if (!xmlScreenParser.findButtonNode("Подождите", false).isEmpty()) {
qDebug() << "[GetCardInfo] 'Подождите' visible, waiting... attempt" << attempt + 1 << "/ 10";
continue;
}
// "Скрыть" видна — номер должен быть раскрыт, но парсер не нашёл
if (!xmlScreenParser.findButtonNode("Скрыть", false).isEmpty()) {
qDebug() << "[GetCardInfo] 'Скрыть' visible but card number not parsed, attempt" << attempt + 1 << "/ 10";
continue;
}
// Повторно нажимаем "Показать" если тап не сработал
Node retryShowBtn = xmlScreenParser.findButtonNode("Показать", false);
if (!retryShowBtn.isEmpty()) {
qDebug() << "[GetCardInfo] Re-tapping 'Показать', attempt" << attempt + 1 << "/ 10";
AdbUtils::makeTap(m_deviceId, retryShowBtn.x(), retryShowBtn.y());
} else {
qDebug() << "[GetCardInfo] Card number not visible, attempt" << attempt + 1 << "/ 10";
}
}
if (!cardNumber.isEmpty()) {
qDebug() << "[GetCardInfo] Card" << card.lastNumbers << "full number:" << cardNumber;
// Апсёртим запись только теперь — с полным номером карты.
MaterialInfo toSave = card;
toSave.cardNumber = cardNumber;
toSave.updateTime = QDateTime::currentDateTimeUtc();
if (!MaterialDAO::upsertAccount(toSave)) {
qWarning() << "[GetCardInfo] Failed to upsert account for card" << card.lastNumbers;
} else {
qDebug() << "[GetCardInfo] Upserted account:" << card.lastNumbers
<< card.description << card.amount;
}
} else {
const QString err = "Card number not found for " + card.lastNumbers;
qWarning() << "[GetCardInfo]" << err;
// Без полного номера материал в БД не создаём.
AppLogger::log("ozon/GetCardInfo", m_deviceId, err,
AdbUtils::captureScreenshotBytes(m_deviceId),
"FETCH_PROFILE");
}
// Возвращаемся на домашний экран для следующей карты
if (i < cards.size() - 1) {
AdbUtils::goBack(m_deviceId);
QThread::msleep(2000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
}
}
// 2.5. Достаём номер счёта (общий для всех карт): Основной счёт → Посмотреть реквизиты
// ВРЕМЕННО ОТКЛЮЧЕНО
// QString accountNumber;
// {
// bool needFetch = false;
// for (const MaterialInfo &c : cards) {
// const MaterialInfo dbCard = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers);
// if (dbCard.id != -1 && dbCard.accountNumber.isEmpty()) { needFetch = true; break; }
// }
//
// if (needFetch) {
// // Возвращаемся на главный экран — критерий именно по наличию кнопки
// // "Основной счёт ... ₽". isHomeScreen() для Озона нестрогий
// // (Перевести/Пополнить присутствуют и на других экранах).
// Node mainAcc;
// for (int attempt = 0; attempt < 6; ++attempt) {
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// mainAcc = xmlScreenParser.findMainAccountButton();
// if (mainAcc.x() > 0) break;
// AdbUtils::goBack(m_deviceId);
// QThread::msleep(1500);
// }
//
// if (mainAcc.x() > 0) {
// AdbUtils::makeTap(m_deviceId, mainAcc.x(), mainAcc.y());
// QThread::msleep(2000);
//
// // Скроллим WebView, пока "Посмотреть реквизиты" не окажется в верхней половине,
// // чтобы тап не ушёл в системный нав-бар.
// Node showReq;
// const int targetMaxY = device.screenHeight / 2;
// for (int attempt = 0; attempt < 6; ++attempt) {
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// Node candidate = xmlScreenParser.findShowRequisitesButton();
// if (candidate.x() > 0 && candidate.y() <= targetMaxY) {
// showReq = candidate;
// break;
// }
// AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2,
// device.screenHeight * 2 / 3,
// device.screenWidth / 2,
// device.screenHeight / 3);
// QThread::msleep(800);
// }
//
// if (showReq.x() > 0) {
// AdbUtils::makeTap(m_deviceId, showReq.x(), showReq.y());
// QThread::msleep(2000);
//
// bool reqScreen = false;
// for (int attempt = 0; attempt < 6; ++attempt) {
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// if (xmlScreenParser.isAccountRequisitesScreen()) {
// reqScreen = true;
// break;
// }
// QThread::msleep(1000);
// }
//
// if (reqScreen) {
// for (int attempt = 0; attempt < 5; ++attempt) {
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// accountNumber = xmlScreenParser.parseAccountNumberFromRequisites();
// if (!accountNumber.isEmpty()) break;
// QThread::msleep(800);
// }
// qDebug() << "[GetCardInfo] accountNumber:" << accountNumber;
// } else {
// qWarning() << "[GetCardInfo] Account requisites screen not detected";
// }
// } else {
// qWarning() << "[GetCardInfo] 'Посмотреть реквизиты' not reached";
// }
// } else {
// qWarning() << "[GetCardInfo] 'Основной счёт' button not found on home";
// }
//
// // Возврат на главный — критерий по кнопке "Основной счёт ... ₽"
// for (int attempt = 0; attempt < 5; ++attempt) {
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
// if (xmlScreenParser.findMainAccountButton().x() > 0) break;
// AdbUtils::goBack(m_deviceId);
// QThread::msleep(1500);
// }
//
// // Записываем номер счёта во все материалы device/app
// if (!accountNumber.isEmpty()) {
// for (const MaterialInfo &c : cards) {
// MaterialInfo upd = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers);
// if (upd.id == -1) continue;
// upd.accountNumber = accountNumber;
// upd.updateTime = QDateTime::currentDateTimeUtc();
// if (!MaterialDAO::upsertAccount(upd)) {
// qWarning() << "[GetCardInfo] Failed to save accountNumber for" << upd.lastNumbers;
// }
// }
// }
// }
// }
// 3. Отправляем материалы (карты) — после OCR, когда номера карт уже в БД
const BankProfileInfo updatedApp = BankProfileDAO::getApplication(device.id, m_appCode);
if (!updatedApp.bankProfileId.isEmpty()) {
for (const MaterialInfo &card : cards) {
const MaterialInfo dbCard = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers);
if (dbCard.cardNumber.isEmpty()) continue;
if (!dbCard.materialId.isEmpty()) {
qDebug() << "[GetCardInfo] material already exists for card" << dbCard.lastNumbers << ", skipping";
continue;
}
const QMap<QString, int> currencyCodes = {
{"RUB", 643}, {"USD", 840}, {"KRW", 410}, {"AZN", 944}, {"ARS", 32}, {"TJS", 972}
};
QJsonObject materialBody;
materialBody["bank_profile_id"] = updatedApp.bankProfileId;
materialBody["card_number"] = dbCard.cardNumber;
materialBody["account_number"] = dbCard.accountNumber;
materialBody["name"] = updatedApp.fullName;
materialBody["state"] = materialStatusToString(dbCard.status) == "active" ? "enabled" : "disabled";
materialBody["currency"] = currencyCodes.value(dbCard.currency.toUpper(), 643);
ns.setPayload(QJsonDocument(materialBody).toJson());
ns.addExtra("account_id", dbCard.id);
ns.postMaterial();
qDebug() << "[GetCardInfo] postMaterial done for card" << dbCard.lastNumbers;
}
} else {
qWarning() << "[GetCardInfo] bankProfileId empty, skipping materials";
}
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
}
} // namespace Ozon