1
0
forked from BRT/arc

Add database migration, DAO enhancements, and OCR-based transaction handling updates

Implemented database migration for adding new fields (`json`, `sent_to_server`) in `events` table. Updated DAO methods to support the new fields and introduced `markSentToServer`. Enhanced Ozon transaction workflows with OCR-based extraction, event error handling improvements, and result reporting. Refactored `EventHandler` for streamlined task processing and stale event cleanup.
This commit is contained in:
slava 2026-03-08 14:20:12 +07:00
parent 832fb03aac
commit 8eedd07ffd
24 changed files with 2524 additions and 158 deletions

View File

@ -45,8 +45,8 @@ static QString recognizeTextFromImage(const QByteArray &pngData) {
} }
static QString extractTransactionId(const QString &ocrText) { static QString extractTransactionId(const QString &ocrText) {
// "ID операции A60130542076330K0000040011670301" // "ID операции A60130542076330K0000040011670301" или "ID операции = Аб60650..."
QRegularExpression reId(R"(ID\s+операции\s+([A-Za-z0-9]+))"); QRegularExpression reId(R"(ID\s+операции\s*=?\s*([A-Za-zА-Яа-я0-9]+))");
if (auto m = reId.match(ocrText); m.hasMatch()) return m.captured(1); if (auto m = reId.match(ocrText); m.hasMatch()) return m.captured(1);
// "№ Ф-2026-25235663" // "№ Ф-2026-25235663"
@ -270,6 +270,17 @@ void GetLastTransactionsScript::doStart() {
<< "phone:" << tx.phone << "phone:" << tx.phone
<< "amount:" << (tx.amount >= 0 ? "+" : "") + QString::number(tx.amount, 'f', 2); << "amount:" << (tx.amount >= 0 ? "+" : "") + QString::number(tx.amount, 'f', 2);
// Если транзакция старше 24 часов — прекращаем
if (tx.completeTime.isValid()) {
const qint64 ageSecs = tx.completeTime.toUTC().secsTo(QDateTime::currentDateTimeUtc());
if (ageSecs > 24 * 3600) {
qDebug() << "[GetLastTransactions] Transaction older than 24h, stopping";
AdbUtils::goBack(m_deviceId);
QThread::msleep(1000);
break;
}
}
// Сохраняем в БД если не существует // Сохраняем в БД если не существует
// Берём первый аккаунт для этого устройства и приложения // Берём первый аккаунт для этого устройства и приложения
if (accountId != -1) { if (accountId != -1) {
@ -286,14 +297,14 @@ void GetLastTransactionsScript::doStart() {
// Ждём появления кнопки "Поделиться" // Ждём появления кнопки "Поделиться"
bool docLoaded = false; bool docLoaded = false;
for (int da = 0; da < 10; ++da) { for (int da = 0; da < 20; ++da) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
Node shareBtn = xmlScreenParser.findNodeByContentDesc("Поделиться"); Node shareBtn = xmlScreenParser.findNodeByContentDesc("Поделиться");
if (!shareBtn.isEmpty()) { if (!shareBtn.isEmpty()) {
docLoaded = true; docLoaded = true;
break; break;
} }
qDebug() << "[GetLastTransactions] Waiting for 'Поделиться', attempt" << da + 1 << "/ 10"; qDebug() << "[GetLastTransactions] Waiting for 'Поделиться', attempt" << da + 1 << "/ 20";
QThread::msleep(1500); QThread::msleep(1500);
} }

View File

@ -3,13 +3,58 @@
#include <QProcess> #include <QProcess>
#include <QRegularExpression> #include <QRegularExpression>
#include <QThread> #include <QThread>
#include <QTimeZone>
#include <utility> #include <utility>
#include <tesseract/baseapi.h>
#include <leptonica/allheaders.h>
#include "adb/AdbUtils.h" #include "adb/AdbUtils.h"
#include "dao/AccountDAO.h" #include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h" #include "dao/ApplicationDAO.h"
#include "dao/DeviceDAO.h" #include "dao/DeviceDAO.h"
#include "dao/TransactionDAO.h" #include "dao/TransactionDAO.h"
#include "time/DateUtils.h"
static QString recognizeTextFromImage(const QByteArray &pngData) {
auto *api = new tesseract::TessBaseAPI();
if (api->Init(nullptr, "rus+eng")) {
qWarning() << "[OCR] Failed to init Tesseract";
delete api;
return {};
}
Pix *pix = pixReadMem(reinterpret_cast<const l_uint8 *>(pngData.constData()),
static_cast<size_t>(pngData.size()));
if (!pix) {
qWarning() << "[OCR] Failed to read image";
api->End();
delete api;
return {};
}
api->SetImage(pix);
char *text = api->GetUTF8Text();
QString result = QString::fromUtf8(text).trimmed();
delete[] text;
pixDestroy(&pix);
api->End();
delete api;
return result;
}
static QString extractTransactionId(const QString &ocrText) {
// "ID операции A60130542076330K0000040011670301" или "ID операции = Аб60650..."
QRegularExpression reId(R"(ID\s+операции\s*=?\s*([A-Za-zА-Яа-я0-9]+))");
if (auto m = reId.match(ocrText); m.hasMatch()) return m.captured(1);
// "№ Ф-2026-25235663"
QRegularExpression reRef(R"(№\s*(Ф-[\d-]+))");
if (auto m = reRef.match(ocrText); m.hasMatch()) return m.captured(1);
return {};
}
namespace Ozon { namespace Ozon {
@ -329,7 +374,106 @@ void PayByPhoneScript::makePayment(
return; return;
} }
qDebug() << "[PayByPhone] Confirm screen loaded, stopping before confirmation"; // 8. Нажимаем "Перевести ..." (ищем по первому слову)
Node transferConfirmBtn = xmlScreenParser.findButtonNode("Перевести", true);
if (transferConfirmBtn.isEmpty()) {
m_error = "Кнопка 'Перевести ...' не найдена на экране подтверждения";
qWarning() << "[PayByPhone]" << m_error;
return;
}
qDebug() << "[PayByPhone] Tapping confirm button:" << transferConfirmBtn.text;
AdbUtils::makeTap(deviceId, transferConfirmBtn.x(), transferConfirmBtn.y());
QThread::msleep(3000);
// 9. Ждём результат: "Успешно" или "Не удалось"
for (int attempt = 0; attempt < 15; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
if (!xmlScreenParser.findTextNode("Успешно").isEmpty()) {
qDebug() << "[PayByPhone] Payment successful!";
m_error.clear();
// Сохраняем транзакцию в БД
TransactionInfo tx;
tx.accountId = m_account.id;
tx.amount = -m_amount;
tx.phone = m_phone;
tx.bankName = "ozon";
tx.type = TransactionType::Phone;
tx.status = TransactionStatus::Complete;
tx.timestamp = DateUtils::getUtcNow();
tx.completeTime = QDateTime::currentDateTime().toTimeZone(QTimeZone("Europe/Moscow"));
tx.bankTime = tx.completeTime.toString("dd.MM.yyyy, HH:mm");
const int txId = TransactionDAO::insertTransaction(tx);
qDebug() << "[PayByPhone] Saved transaction to DB, id:" << txId;
// Ищем кнопку "Документы" и достаём ID операции
Node docBtn = xmlScreenParser.findButtonNode("Документы", false);
if (!docBtn.isEmpty() && txId != -1) {
qDebug() << "[PayByPhone] Tapping 'Документы'...";
AdbUtils::makeTap(deviceId, docBtn.x(), docBtn.y());
QThread::msleep(2000);
// Ждём появления кнопки "Поделиться"
bool docLoaded = false;
for (int da = 0; da < 20; ++da) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
Node shareBtn = xmlScreenParser.findNodeByContentDesc("Поделиться");
if (!shareBtn.isEmpty()) {
docLoaded = true;
break;
}
qDebug() << "[PayByPhone] Waiting for 'Поделиться', attempt" << da + 1 << "/ 20";
QThread::msleep(1500);
}
if (docLoaded) {
QThread::msleep(500);
const QByteArray screenshot = AdbUtils::captureScreenshotBytes(deviceId);
if (!screenshot.isEmpty()) {
TransactionDAO::updateReceiptImage(txId, screenshot);
qDebug() << "[PayByPhone] Receipt saved to DB, txId:" << txId;
const QString ocrText = recognizeTextFromImage(screenshot);
if (!ocrText.isEmpty()) {
qDebug() << "[PayByPhone] OCR result:\n" << ocrText;
const QString externalId = extractTransactionId(ocrText);
if (!externalId.isEmpty()) {
TransactionDAO::updateBankTrExternalId(txId, externalId);
qDebug() << "[PayByPhone] Extracted transaction ID:" << externalId;
} else {
qWarning() << "[PayByPhone] Could not extract transaction ID from OCR text";
}
} else {
qWarning() << "[PayByPhone] OCR returned empty text";
}
} else {
qWarning() << "[PayByPhone] Screenshot is empty";
}
} else {
qWarning() << "[PayByPhone] 'Поделиться' not found, skipping receipt";
}
AdbUtils::goBack(deviceId);
QThread::msleep(1000);
}
return;
}
if (!xmlScreenParser.findTextNode("Не удалось").isEmpty()) {
m_error = "Перевод не удался (экран 'Не удалось')";
qWarning() << "[PayByPhone]" << m_error;
return;
}
qDebug() << "[PayByPhone] Waiting for payment result, attempt" << attempt + 1 << "/ 15";
QThread::msleep(2000);
}
m_error = "Не дождались результата перевода";
qWarning() << "[PayByPhone]" << m_error;
} }
} // namespace Ozon } // namespace Ozon

View File

@ -388,20 +388,38 @@ QString ScreenXmlParser::parseProfilePhone() {
} }
QList<Node> ScreenXmlParser::findPinCodeNodes(const QString &pinCode) { QList<Node> ScreenXmlParser::findPinCodeNodes(const QString &pinCode) {
// Ozon PIN кнопки — анонимные FrameLayout без text/content-desc. // Способ 1: ищем кликабельные ноды с текстом-цифрой (0-9)
// Алгоритм не зависит от разрешения: ищем строки из ровно 3 кликабельных узлов. QMap<QString, Node> digitMap;
for (const Node &node : m_nodes) {
if (!node.clickable || node.width() <= 0 || node.height() <= 0) continue;
const QString t = node.text.trimmed();
if (t.size() == 1 && t.at(0).isDigit()) {
digitMap[t] = node;
}
}
// 1. Собираем все кликабельные узлы с ненулевым размером if (digitMap.size() >= 10) {
QList<Node> result;
for (const QChar &c : pinCode) {
const QString d = QString(c);
if (digitMap.contains(d)) {
result.append(digitMap[d]);
} else {
qWarning() << "[findPinCodeNodes] digit not found:" << d;
return {};
}
}
return result;
}
// Способ 2 (fallback): анонимные FrameLayout — ищем сетку 4x3
QList<Node> clickable; QList<Node> clickable;
for (const Node &node : m_nodes) { for (const Node &node : m_nodes) {
if (node.clickable && node.width() > 0 && node.height() > 0) { if (node.clickable && node.width() > 0 && node.height() > 0)
clickable.append(node); clickable.append(node);
} }
}
if (clickable.isEmpty()) return {}; if (clickable.isEmpty()) return {};
// 2. Находим минимальную высоту — это примерный размер одной кнопки.
// Исключаем полноэкранные элементы (WebView и т.п.) фильтром height <= minH * 3.
int minH = INT_MAX; int minH = INT_MAX;
for (const Node &n : clickable) minH = std::min(minH, n.height()); for (const Node &n : clickable) minH = std::min(minH, n.height());
@ -410,12 +428,10 @@ QList<Node> ScreenXmlParser::findPinCodeNodes(const QString &pinCode) {
if (n.height() <= minH * 3) candidates.append(n); if (n.height() <= minH * 3) candidates.append(n);
} }
// 3. Сортируем по y
std::sort(candidates.begin(), candidates.end(), [](const Node &a, const Node &b) { std::sort(candidates.begin(), candidates.end(), [](const Node &a, const Node &b) {
return a.y() < b.y(); return a.y() < b.y();
}); });
// 4. Группируем в строки: узлы с разницей y < height первого узла строки — одна строка
QList<QList<Node>> rows; QList<QList<Node>> rows;
for (const Node &node : candidates) { for (const Node &node : candidates) {
bool placed = false; bool placed = false;
@ -429,32 +445,22 @@ QList<Node> ScreenXmlParser::findPinCodeNodes(const QString &pinCode) {
if (!placed) rows.append({node}); if (!placed) rows.append({node});
} }
// 5. Внутри каждой строки сортируем по x (слева направо)
for (QList<Node> &row : rows) { for (QList<Node> &row : rows) {
std::sort(row.begin(), row.end(), [](const Node &a, const Node &b) { std::sort(row.begin(), row.end(), [](const Node &a, const Node &b) {
return a.x() < b.x(); return a.x() < b.x();
}); });
} }
// 6. PIN-сетка — строки ровно с 3 узлами (3 столбца)
// Строки 1-3: цифры 1-9
// Строка 4: (биометрия) [0] (backspace) → берём средний элемент
qDebug() << "[findPinCodeNodes] clickable:" << clickable.size()
<< "minH:" << minH << "candidates:" << candidates.size() << "rows:" << rows.size();
for (int i = 0; i < rows.size(); ++i) {
QStringList info;
for (const Node &n : rows[i])
info << QString("(%1,%2 %3x%4 '%5')").arg(n.x()).arg(n.y()).arg(n.width()).arg(n.height()).arg(n.text.left(15));
qDebug() << "[findPinCodeNodes] row" << i << "size:" << rows[i].size() << info.join(" | ");
}
QList<QList<Node>> gridRows; QList<QList<Node>> gridRows;
for (const QList<Node> &row : rows) { for (const QList<Node> &row : rows) {
if (row.size() == 3) gridRows.append(row); if (row.size() == 3) gridRows.append(row);
} }
qDebug() << "[findPinCodeNodes] gridRows:" << gridRows.size() << "pinCode len:" << pinCode.size();
QMap<QString, Node> digitMap; if (gridRows.size() < 4) {
qWarning() << "[findPinCodeNodes] grid not found: gridRows=" << gridRows.size();
return {};
}
const QStringList digits19 = {"1", "2", "3", "4", "5", "6", "7", "8", "9"}; const QStringList digits19 = {"1", "2", "3", "4", "5", "6", "7", "8", "9"};
int idx = 0; int idx = 0;
for (int r = 0; r < gridRows.size() - 1 && idx < digits19.size(); ++r) { for (int r = 0; r < gridRows.size() - 1 && idx < digits19.size(); ++r) {
@ -462,8 +468,8 @@ QList<Node> ScreenXmlParser::findPinCodeNodes(const QString &pinCode) {
if (idx < digits19.size()) digitMap[digits19[idx++]] = n; if (idx < digits19.size()) digitMap[digits19[idx++]] = n;
} }
} }
if (!gridRows.isEmpty() && gridRows.last().size() >= 2) { if (gridRows.last().size() >= 2) {
digitMap["0"] = gridRows.last()[1]; // средний столбец последней строки digitMap["0"] = gridRows.last()[1];
} }
QList<Node> result; QList<Node> result;

BIN
assets/error.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -0,0 +1,539 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation="0">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="0" hint="">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/action_bar_root"
class="android.widget.LinearLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="android:id/content" class="android.widget.FrameLayout"
package="ru.ozon.fintech.finance" content-desc="" checkable="false" checked="false"
clickable="false" enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2400]"
drawing-order="2" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/main_activity_top"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="1" text="" resource-id="ru.ozon.fintech.finance:id/over_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2400]" drawing-order="4" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/over_fragment_top"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,0][1080,2400]" drawing-order="1"
hint=""/>
</node>
<node index="3" text="" resource-id="" class="android.view.ViewGroup"
package="ru.ozon.fintech.finance" content-desc="" checkable="false" checked="false"
clickable="false" enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2400]"
drawing-order="3" hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,0][1080,2400]" drawing-order="2"
hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/web_view_top"
class="android.view.ViewGroup" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/web_view_holder"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="2" hint="">
<node index="0" text="" resource-id=""
class="android.widget.FrameLayout"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="1" hint="">
<node index="0" text="" resource-id=""
class="android.webkit.WebView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false"
scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]" drawing-order="0"
hint="">
<node index="0" text="" resource-id=""
class="android.webkit.WebView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="true" focused="true"
scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false"
scrollable="false" long-clickable="false"
password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="0" hint="">
<node index="0" text="" resource-id="svelte"
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id="MFC2C@sbp"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[0,0][1080,252]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="base-go-back-button"
checkable="false"
checked="false"
clickable="true"
enabled="true"
focusable="true"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[44,149][133,236]"
drawing-order="0" hint=""/>
<node index="1" text=""
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="native-toolbar-close-button"
checkable="false"
checked="false"
clickable="true"
enabled="true"
focusable="true"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[1021,191][1080,252]"
drawing-order="0" hint=""/>
</node>
<node index="1"
text="Подтвердите перевод"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[42,312][1039,430]"
drawing-order="0" hint=""/>
<node index="2"
text="Владислав Николаевич К."
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,490][777,556]"
drawing-order="0" hint=""/>
<node index="3"
text="+7 (985) 553-28-96"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,569][496,627]"
drawing-order="0" hint=""/>
<node NAF="true" index="4" text=""
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[501,574][546,619]"
drawing-order="0" hint=""/>
<node index="5" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[858,490][976,609]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[926,559][987,619]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.widget.Image"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[937,569][976,609]"
drawing-order="0"
hint=""/>
</node>
</node>
<node index="6" text="Банк получателя"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,693][976,742]"
drawing-order="0" hint=""/>
<node index="7" text="Альфа-Банк"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,750][976,816]"
drawing-order="0" hint=""/>
<node index="8" text="Счёт списания"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,855][976,905]"
drawing-order="0" hint=""/>
<node index="9" text="Основной счёт"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,913][976,979]"
drawing-order="0" hint=""/>
<node index="10" text="1 251,50₽"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,987][249,1031]"
drawing-order="0" hint=""/>
<node index="11" text="Сумма списания"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,1071][976,1120]"
drawing-order="0" hint=""/>
<node index="12" text="100₽"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,1128][976,1194]"
drawing-order="0" hint=""/>
<node index="13" text="Без комисcии"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[105,1202][976,1246]"
drawing-order="0" hint=""/>
<node index="14" text="Перевести 100₽"
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[42,2079][1039,2228]"
drawing-order="0" hint=""/>
<node index="15"
text="Система быстрых платежей"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[267,2247][763,2296]"
drawing-order="0" hint=""/>
</node>
</node>
</node>
<node index="1" text=""
resource-id="modal-container"
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,2336][1080,2336]"
drawing-order="0" hint=""/>
<node index="2" text=""
resource-id="svelte-announcer"
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][5,5]"
drawing-order="0" hint=""/>
</node>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="2" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
<node index="3" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
</node>
</node>
</node>
</node>
</node>
<node index="1" text=""
resource-id="ru.ozon.fintech.finance:id/web_view_toolbar"
class="android.view.ViewGroup" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,147]" drawing-order="1" hint=""/>
</node>
</node>
</node>
<node index="1" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_bottom_navigation_holder"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,2201][1080,2400]" drawing-order="3"
hint=""/>
</node>
</node>
</node>
</node>
<node index="1" text="" resource-id="" class="android.view.View" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[818,0][1080,262]" drawing-order="2" hint=""/>
</node>
</node>
</node>
</hierarchy>

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 KiB

View File

@ -0,0 +1,698 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation="0">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="0" hint="">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/action_bar_root"
class="android.widget.LinearLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="android:id/content" class="android.widget.FrameLayout"
package="ru.ozon.fintech.finance" content-desc="" checkable="false" checked="false"
clickable="false" enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2400]"
drawing-order="2" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/main_activity_top"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="1" text="" resource-id="ru.ozon.fintech.finance:id/over_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2400]" drawing-order="4" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/over_fragment_top"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,0][1080,2400]" drawing-order="1"
hint=""/>
</node>
<node index="3" text="" resource-id="" class="android.view.ViewGroup"
package="ru.ozon.fintech.finance" content-desc="" checkable="false" checked="false"
clickable="false" enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2400]"
drawing-order="3" hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,0][1080,2400]" drawing-order="2"
hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/web_view_top"
class="android.view.ViewGroup" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/web_view_holder"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="2" hint="">
<node index="0" text="" resource-id=""
class="android.widget.FrameLayout"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="1" hint="">
<node index="0" text="" resource-id=""
class="android.webkit.WebView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false"
scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]" drawing-order="0"
hint="">
<node index="0" text="" resource-id=""
class="android.webkit.WebView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="true" focused="true"
scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false"
scrollable="false" long-clickable="false"
password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="0" hint="">
<node index="0" text="" resource-id="svelte"
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id="MFC2C@sbp"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
</node>
<node index="1" text=""
resource-id="modal-container"
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,2336][1080,2336]"
drawing-order="0" hint=""/>
<node index="2" text=""
resource-id="svelte-announcer"
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][5,5]"
drawing-order="0" hint=""/>
</node>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="2" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
<node index="3" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
<node index="4" text="" resource-id=""
class="android.app.AlertDialog"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="2" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,1569]"
drawing-order="0" hint=""/>
<node index="3" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,133][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[42,165][105,231]"
drawing-order="0" hint=""/>
<node index="1" text="Система быстрых платежей"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[291,173][787,223]"
drawing-order="0" hint=""/>
<node NAF="true" index="2" text=""
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[952,154][1039,241]"
drawing-order="0" hint=""/>
<node index="3" text=""
resource-id="payment-result-body"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[0,259][1080,2170]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id="payment-result-body-center"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[86,777][992,1653]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[86,777][992,1653]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[433,672][645,887]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[433,672][645,887]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[580,819][666,908]"
drawing-order="0"
hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[580,819][666,908]"
drawing-order="0"
hint="">
<node index="0"
text=""
resource-id=""
class="android.widget.Image"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[580,819][666,908]"
drawing-order="0"
hint=""/>
</node>
</node>
</node>
</node>
<node index="1" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,777][992,1653]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id="payment-result-ticket-top"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,779][992,1336]"
drawing-order="0" hint="">
<node index="0"
text="Не удалось"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,926][929,994]"
drawing-order="0"
hint=""/>
<node index="1"
text=" 100₽"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1000][929,1084]"
drawing-order="0"
hint=""/>
<node index="2"
text="Технические неполадки у СБП. Попробуйте снова"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1120][929,1294]"
drawing-order="0"
hint=""/>
<node index="3" text=""
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,1288][992,1336]"
drawing-order="0"
hint=""/>
</node>
<node index="1" text=""
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,1330][992,1375]"
drawing-order="0"
hint=""/>
<node index="2"
text="Альфа-Банк"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1372][929,1422]"
drawing-order="0"
hint=""/>
<node index="3"
text="Владислав Николаевич К."
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1438][929,1506]"
drawing-order="0"
hint=""/>
<node index="4"
text="+7 (985) 553-28-96"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1522][929,1590]"
drawing-order="0"
hint=""/>
</node>
</node>
</node>
</node>
<node index="4" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[42,2168][1039,2296]"
drawing-order="0" hint="">
<node index="0" text="Готово" resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[42,2168][1039,2296]"
drawing-order="0" hint=""/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
<node index="1" text=""
resource-id="ru.ozon.fintech.finance:id/web_view_toolbar"
class="android.view.ViewGroup" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,147]" drawing-order="1" hint=""/>
</node>
</node>
</node>
<node index="1" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_bottom_navigation_holder"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,2201][1080,2400]" drawing-order="3"
hint=""/>
</node>
</node>
</node>
</node>
<node index="1" text="" resource-id="" class="android.view.View" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[818,0][1080,262]" drawing-order="2" hint=""/>
</node>
</node>
</node>
</hierarchy>

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 KiB

View File

@ -0,0 +1,780 @@
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
<hierarchy rotation="0">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="0" hint="">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/action_bar_root"
class="android.widget.LinearLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="android:id/content" class="android.widget.FrameLayout"
package="ru.ozon.fintech.finance" content-desc="" checkable="false" checked="false"
clickable="false" enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2400]"
drawing-order="2" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/main_activity_top"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="1" text="" resource-id="ru.ozon.fintech.finance:id/over_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2400]" drawing-order="4" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/over_fragment_top"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,0][1080,2400]" drawing-order="1"
hint=""/>
</node>
<node index="3" text="" resource-id="" class="android.view.ViewGroup"
package="ru.ozon.fintech.finance" content-desc="" checkable="false" checked="false"
clickable="false" enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2400]"
drawing-order="3" hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,0][1080,2400]" drawing-order="2"
hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_host_fragment"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text="" resource-id="ru.ozon.fintech.finance:id/web_view_top"
class="android.view.ViewGroup" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2400]" drawing-order="1" hint="">
<node index="0" text=""
resource-id="ru.ozon.fintech.finance:id/web_view_holder"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="2" hint="">
<node index="0" text="" resource-id=""
class="android.widget.FrameLayout"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="1" hint="">
<node index="0" text="" resource-id=""
class="android.webkit.WebView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false"
scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]" drawing-order="0"
hint="">
<node index="0" text="" resource-id=""
class="android.webkit.WebView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="true" focused="true"
scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false"
scrollable="false" long-clickable="false"
password="false" selected="false"
bounds="[0,0][1080,2337]" drawing-order="0" hint="">
<node index="0" text="" resource-id="svelte"
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id="MFC2C@sbp"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
</node>
<node index="1" text=""
resource-id="modal-container"
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,2336][1080,2336]"
drawing-order="0" hint=""/>
<node index="2" text=""
resource-id="svelte-announcer"
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][5,5]"
drawing-order="0" hint=""/>
</node>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="2" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
<node index="3" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
</node>
<node index="4" text="" resource-id=""
class="android.app.AlertDialog"
package="ru.ozon.fintech.finance" content-desc=""
checkable="false" checked="false"
clickable="false" enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="1" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,0][1080,2337]"
drawing-order="0" hint=""/>
<node index="2" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,15][1080,1596]"
drawing-order="0" hint=""/>
<node index="3" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false" bounds="[0,133][1080,2337]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[42,165][105,231]"
drawing-order="0" hint=""/>
<node index="1" text="Система быстрых платежей"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[291,173][787,223]"
drawing-order="0" hint=""/>
<node NAF="true" index="2" text=""
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[952,154][1039,241]"
drawing-order="0" hint=""/>
<node index="3" text=""
resource-id="payment-result-body"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[0,259][1080,2170]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id="payment-result-body-center"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[86,805][992,1627]"
drawing-order="0" hint="">
<node index="0" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[86,805][992,1627]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[422,685][640,903]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[422,685][640,903]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[572,832][661,921]"
drawing-order="0"
hint="">
<node index="0" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[572,832][661,921]"
drawing-order="0"
hint="">
<node index="0"
text=""
resource-id=""
class="android.widget.Image"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[572,832][661,921]"
drawing-order="0"
hint=""/>
</node>
</node>
</node>
</node>
<node index="1" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,805][992,1470]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id="payment-result-ticket-top"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[78,779][992,1149]"
drawing-order="0" hint="">
<node index="0"
text="Успешно"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[147,929][924,1015]"
drawing-order="0"
hint=""/>
<node index="1"
text=" 100₽"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[147,1002][926,1105]"
drawing-order="0"
hint=""/>
<node index="2" text=""
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,1078][992,1149]"
drawing-order="0"
hint=""/>
</node>
<node index="1" text=""
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,1144][992,1191]"
drawing-order="0"
hint=""/>
<node index="2"
text="Альфа-Банк"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1186][929,1239]"
drawing-order="0"
hint=""/>
<node index="3"
text="Владислав Николаевич К."
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1254][929,1323]"
drawing-order="0"
hint=""/>
<node index="4"
text="+7 (985) 553-28-96"
resource-id=""
class="android.widget.TextView"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[149,1338][929,1407]"
drawing-order="0"
hint=""/>
</node>
<node index="2" text=""
resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[86,1464][992,1627]"
drawing-order="0" hint="">
<node index="0" text=""
resource-id="MFPromoBanners@promo"
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="false"
enabled="true"
focusable="false"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[128,1464][950,1596]"
drawing-order="0" hint="">
<node index="0"
text="Banner Получайте ежедневный доход со ставкой 12,5% годовых"
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc=""
checkable="false"
checked="false"
clickable="true"
enabled="true"
focusable="true"
focused="false"
scrollable="false"
long-clickable="false"
password="false"
selected="false"
bounds="[128,1498][950,1596]"
drawing-order="0"
hint=""/>
</node>
</node>
</node>
</node>
<node index="1" text="Повторить"
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[149,1687][330,1869]"
drawing-order="0" hint=""/>
<node index="2" text="Документы"
resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[349,1687][530,1869]"
drawing-order="0" hint=""/>
<node index="3" text="Шаблон" resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[548,1687][729,1869]"
drawing-order="0" hint=""/>
<node index="4" text="Помощь" resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[748,1687][929,1869]"
drawing-order="0" hint=""/>
</node>
<node index="4" text="" resource-id=""
class="android.view.View"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="false"
enabled="true" focusable="false"
focused="false" scrollable="false"
long-clickable="false" password="false"
selected="false"
bounds="[42,2168][1039,2296]"
drawing-order="0" hint="">
<node index="0" text="Готово" resource-id=""
class="android.widget.Button"
package="ru.ozon.fintech.finance"
content-desc="" checkable="false"
checked="false" clickable="true"
enabled="true" focusable="true"
focused="false" scrollable="false"
long-clickable="false"
password="false" selected="false"
bounds="[42,2168][1039,2296]"
drawing-order="0" hint=""/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
<node index="1" text=""
resource-id="ru.ozon.fintech.finance:id/web_view_toolbar"
class="android.view.ViewGroup" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false"
enabled="true" focusable="false" focused="false" scrollable="false"
long-clickable="false" password="false" selected="false"
bounds="[0,0][1080,147]" drawing-order="1" hint=""/>
</node>
</node>
</node>
<node index="1" text=""
resource-id="ru.ozon.fintech.finance:id/main_activity_bottom_navigation_holder"
class="android.widget.FrameLayout" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false"
password="false" selected="false" bounds="[0,2201][1080,2400]" drawing-order="3"
hint=""/>
</node>
</node>
</node>
</node>
<node index="1" text="" resource-id="" class="android.view.View" package="ru.ozon.fintech.finance"
content-desc="" checkable="false" checked="false" clickable="false" enabled="true"
focusable="false" focused="false" scrollable="false" long-clickable="false" password="false"
selected="false" bounds="[818,0][1080,262]" drawing-order="2" hint=""/>
</node>
</node>
</node>
</hierarchy>

View File

@ -2,13 +2,13 @@
list=ozon list=ozon
[common] [common]
access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjNDU1MjE1Ni1kMTc5LTRhNzktOWU5MS01ODcwMzhjOTVjZjAiLCJ0eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcyNzE0MTQyfQ.-074b8eEWeLgzoxxpq5o54G1npivuC_VWvu4NLEz_gY access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjNDU1MjE1Ni1kMTc5LTRhNzktOWU5MS01ODcwMzhjOTVjZjAiLCJ0eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcyODg4MjU2fQ.5WF3EQobCOQpuQr5wcCeYOJW1ER0RJG506WRhEB_Y5E
api_key=0eee77b62013253e02e1a69bc6a505bb37757fb7 api_key=0eee77b62013253e02e1a69bc6a505bb37757fb7
currencies=USD, KRW, AZN, RUB currencies=USD, KRW, AZN, RUB
desktop_id=b86eac8e-dc89-4a9e-a5e8-7f85db011391 desktop_id=b86eac8e-dc89-4a9e-a5e8-7f85db011391
profile_id=c4552156-d179-4a79-9e91-587038c95cf0 profile_id=c4552156-d179-4a79-9e91-587038c95cf0
refresh_token=def3036b-b62d-4550-8971-94d7ef57eba2 refresh_token=4c78f097-d70b-4bdb-b351-c839fa9eb442
token_expires_at=1772714142 token_expires_at=1772888256
version=0.0.1 version=0.0.1
[db] [db]

View File

@ -361,6 +361,16 @@ void DatabaseManager::initSchema() {
q.exec("PRAGMA user_version = 11"); q.exec("PRAGMA user_version = 11");
qDebug() << "[DB] Migration 11 applied"; qDebug() << "[DB] Migration 11 applied";
} }
// ── Версия 12: json и sent_to_server в events ────────────────────────────
if (version < 12) {
db.transaction();
q.exec("ALTER TABLE events ADD COLUMN json TEXT DEFAULT ''");
q.exec("ALTER TABLE events ADD COLUMN sent_to_server INTEGER DEFAULT 0");
db.commit();
q.exec("PRAGMA user_version = 12");
qDebug() << "[DB] Migration 12 applied";
}
} }
void DatabaseManager::closeConnection() { void DatabaseManager::closeConnection() {

Binary file not shown.

View File

@ -224,8 +224,10 @@ QStringList AccountDAO::getActiveMaterialIds() {
SELECT a.material_id SELECT a.material_id
FROM accounts a FROM accounts a
JOIN applications app ON a.device_id = app.device_id AND a.app_code = app.code JOIN applications app ON a.device_id = app.device_id AND a.app_code = app.code
JOIN devices d ON a.device_id = d.id
WHERE app.status = 'active' WHERE app.status = 'active'
AND a.material_id IS NOT NULL AND a.material_id != '' AND a.material_id IS NOT NULL AND a.material_id != ''
AND d.status = 'device' AND d.image IS NOT NULL
)"); )");
if (!query.exec()) { if (!query.exec()) {

View File

@ -29,6 +29,19 @@ QList<BankInfo> BankDAO::getAll() {
return list; return list;
} }
BankInfo BankDAO::findByAlias(const QString &alias) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare("SELECT * FROM banks WHERE alias = :alias LIMIT 1");
query.bindValue(":alias", alias);
if (!query.exec() || !query.next()) return {};
BankInfo bank;
bank.id = query.value("id").toInt();
bank.alias = query.value("alias").toString();
bank.name = query.value("name").toString();
bank.nspkName = query.value("nspk_name").toString();
return bank;
}
void BankDAO::replaceAll(const QList<BankInfo> &banks) { void BankDAO::replaceAll(const QList<BankInfo> &banks) {
QSqlDatabase db = DatabaseManager::instance().database(); QSqlDatabase db = DatabaseManager::instance().database();
QSqlQuery query(db); QSqlQuery query(db);

View File

@ -7,5 +7,7 @@ class BankDAO {
public: public:
[[nodiscard]] static QList<BankInfo> getAll(); [[nodiscard]] static QList<BankInfo> getAll();
[[nodiscard]] static BankInfo findByAlias(const QString &alias);
static void replaceAll(const QList<BankInfo> &banks); static void replaceAll(const QList<BankInfo> &banks);
}; };

View File

@ -18,6 +18,8 @@ EventInfo parseEvent(const QSqlQuery &query) {
event.phone = query.value("phone").toString(); event.phone = query.value("phone").toString();
event.bankName = query.value("bank_name").toString(); event.bankName = query.value("bank_name").toString();
event.comment = query.value("comment").toString(); event.comment = query.value("comment").toString();
event.json = query.value("json").toString();
event.sentToServer = query.value("sent_to_server").toBool();
event.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString()); event.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString());
event.updateTime = DateUtils::fromUtcString(query.value("update_time").toString()); event.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
event.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString()); event.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString());
@ -29,9 +31,9 @@ int EventDAO::insertEvent(const EventInfo &event) {
query.prepare(R"( query.prepare(R"(
INSERT INTO events ( INSERT INTO events (
status, external_id, account_id, amount, phone, bank_name, timestamp, device_id, type status, external_id, account_id, amount, phone, bank_name, comment, json, timestamp, device_id, type
) VALUES ( ) VALUES (
:status, :external_id, :account_id, :amount, :phone, :bank_name, :timestamp, :device_id, :type :status, :external_id, :account_id, :amount, :phone, :bank_name, :comment, :json, :timestamp, :device_id, :type
) )
)"); )");
@ -43,6 +45,8 @@ int EventDAO::insertEvent(const EventInfo &event) {
query.bindValue(":amount", event.amount); query.bindValue(":amount", event.amount);
query.bindValue(":phone", event.phone); query.bindValue(":phone", event.phone);
query.bindValue(":bank_name", event.bankName); query.bindValue(":bank_name", event.bankName);
query.bindValue(":comment", event.comment);
query.bindValue(":json", event.json);
query.bindValue(":timestamp", DateUtils::toUtcString(event.timestamp)); query.bindValue(":timestamp", DateUtils::toUtcString(event.timestamp));
if (!query.exec()) { if (!query.exec()) {
@ -114,12 +118,24 @@ bool EventDAO::updateEventAmount(int id, double amount) {
return true; return true;
} }
bool EventDAO::markSentToServer(int id) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare("UPDATE events SET sent_to_server = 1 WHERE id = :id");
query.bindValue(":id", id);
if (!query.exec()) {
qWarning() << "Ошибка markSentToServer:" << query.lastError().text();
return false;
}
return true;
}
QList<EventInfo> EventDAO::getAllEvents(const EventStatus &status) { QList<EventInfo> EventDAO::getAllEvents(const EventStatus &status) {
QList<EventInfo> events; QList<EventInfo> events;
QSqlQuery query(DatabaseManager::instance().database()); QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"( query.prepare(R"(
SELECT id, account_id, device_id, external_id, amount, phone, bank_name, status, update_time, complete_time, timestamp, type SELECT id, account_id, device_id, external_id, amount, phone, bank_name,
status, type, comment, json, sent_to_server, update_time, complete_time, timestamp
FROM events FROM events
WHERE status = :status WHERE status = :status
)"); )");
@ -141,7 +157,7 @@ EventInfo EventDAO::getFirstWaitEventByDeviceId(const QString &key) {
QSqlQuery query(DatabaseManager::instance().database()); QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"( query.prepare(R"(
SELECT id, account_id, device_id, external_id, amount, phone, bank_name, SELECT id, account_id, device_id, external_id, amount, phone, bank_name,
status, type, comment, update_time, complete_time, timestamp status, type, comment, json, sent_to_server, update_time, complete_time, timestamp
FROM events FROM events
WHERE status = :status AND device_id = :device_id WHERE status = :status AND device_id = :device_id
ORDER BY timestamp ASC ORDER BY timestamp ASC
@ -166,7 +182,7 @@ QList<EventInfo> EventDAO::getEvents(int page, int pageSize) {
QSqlQuery query(DatabaseManager::instance().database()); QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"( query.prepare(R"(
SELECT id, account_id, device_id, external_id, amount, phone, bank_name, SELECT id, account_id, device_id, external_id, amount, phone, bank_name,
status, type, comment, update_time, complete_time, timestamp status, type, comment, json, sent_to_server, update_time, complete_time, timestamp
FROM events FROM events
ORDER BY id DESC ORDER BY id DESC
LIMIT :limit OFFSET :offset LIMIT :limit OFFSET :offset

View File

@ -20,6 +20,8 @@ public:
[[nodiscard]] static bool updateEventAmount(int id, double amount); [[nodiscard]] static bool updateEventAmount(int id, double amount);
[[nodiscard]] static bool markSentToServer(int id);
[[nodiscard]] static QList<EventInfo> getEvents(int page, int pageSize); [[nodiscard]] static QList<EventInfo> getEvents(int page, int pageSize);
[[nodiscard]] static int getTotalCount(); [[nodiscard]] static int getTotalCount();

View File

@ -67,6 +67,7 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
query.bindValue(":bank_tr_external_id", info.bankTrExternalId); query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime)); query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime)); query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
if (!query.exec()) { if (!query.exec()) {
qWarning() << "Ошибка при вставке transaction:" << query.lastError().text(); qWarning() << "Ошибка при вставке transaction:" << query.lastError().text();

View File

@ -67,6 +67,9 @@ struct EventInfo {
EventStatus status = EventStatus::Unknown; EventStatus status = EventStatus::Unknown;
EventType type = EventType::Unknown; EventType type = EventType::Unknown;
QString json;
bool sentToServer = false;
QByteArray screenshot; QByteArray screenshot;
QDateTime updateTime; QDateTime updateTime;

View File

@ -203,7 +203,7 @@ void DeviceScreener::postScreenshotToApi(const QString &apiId, const QByteArray
} else { } else {
const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll()); const QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
if (doc.isObject() && doc.object()["success"].toBool()) { if (doc.isObject() && doc.object()["success"].toBool()) {
qDebug() << "postScreenshotToApi done for device" << apiId; // ok
} else { } else {
qWarning() << "postScreenshotToApi failed for device" << apiId << ":" << doc.toJson(QJsonDocument::Compact); qWarning() << "postScreenshotToApi failed for device" << apiId << ":" << doc.toJson(QJsonDocument::Compact);
} }
@ -229,7 +229,6 @@ void DeviceScreener::toggleBankProfilesOnApi(const QString &deviceId, bool enabl
const QString enableStr = enable ? "true" : "false"; const QString enableStr = enable ? "true" : "false";
const QString path = "/api/v1/profile/bank_profile/" + app.bankProfileId + "/" + enableStr; const QString path = "/api/v1/profile/bank_profile/" + app.bankProfileId + "/" + enableStr;
qDebug() << "[DeviceScreener] toggleBankProfile:" << app.code << app.bankProfileId << "enable=" << enable;
apiPatch(path, QJsonObject()); apiPatch(path, QJsonObject());
} }
} }

View File

@ -1,18 +1,23 @@
#include "EventHandler.h" #include "EventHandler.h"
#include <QDir>
#include <QFile>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QMap> #include <QMap>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QTimeZone>
#include "dao/AccountDAO.h" #include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h" #include "dao/ApplicationDAO.h"
#include "dao/BankDAO.h"
#include "dao/DeviceDAO.h" #include "dao/DeviceDAO.h"
#include "dao/EventDAO.h" #include "dao/EventDAO.h"
#include "dao/TransactionDAO.h" #include "dao/TransactionDAO.h"
#include "db/ApplicationInfo.h" #include "db/ApplicationInfo.h"
#include "adb/AdbUtils.h"
#include "net/NetworkService.h" #include "net/NetworkService.h"
#include "birbank/GetLastDaysHistoryBirbankScript.h" #include "birbank/GetLastDaysHistoryBirbankScript.h"
#include "birbank/LoginAndCheckAccountsBirbankScript.h" #include "birbank/LoginAndCheckAccountsBirbankScript.h"
@ -24,8 +29,11 @@
#include "toss/LoginAndCheckAccountsTossScript.h" #include "toss/LoginAndCheckAccountsTossScript.h"
#include "ziraat/GetLastDaysHistoryZiraatScript.h" #include "ziraat/GetLastDaysHistoryZiraatScript.h"
#include "ziraat/LoginAndCheckAccountsZiraatScript.h" #include "ziraat/LoginAndCheckAccountsZiraatScript.h"
#include "ozon/PayByPhoneScript.h"
#include "rshb/PayByPhoneScript.h"
static void sendTaskResult(const QString &type, const QString &materialId, const QString &bankName, const QJsonValue &data); static void sendTaskResult(const QString &type, const QString &materialId, const QString &bankName, const QJsonValue &data);
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName, const QString &description);
EventHandler::EventHandler(QObject *parent) : QObject(parent) {} EventHandler::EventHandler(QObject *parent) : QObject(parent) {}
@ -41,6 +49,18 @@ void EventHandler::start() {
connect(m_pollTimer, &QTimer::timeout, m_network, &NetworkService::getTasks); connect(m_pollTimer, &QTimer::timeout, m_network, &NetworkService::getTasks);
m_pollTimer->start(); m_pollTimer->start();
// Закрываем зависшие события с прошлого запуска (только не отправленные на сервер)
for (const EventStatus st : {EventStatus::Wait, EventStatus::InProgress}) {
const QList<EventInfo> stale = EventDAO::getAllEvents(st);
for (const EventInfo &e : stale) {
if (e.sentToServer) continue;
EventDAO::updateEvent(e.id, EventStatus::Error, "App restarted");
EventDAO::markSentToServer(e.id);
sendTaskError(eventTypeToString(e.type), e.externalId, e.bankName, "App restarted, event was pending");
qDebug() << "[EventHandler] Closed stale event id:" << e.id;
}
}
// Первый запрос сразу при старте // Первый запрос сразу при старте
qDebug() << "[EventHandler] starting first getTasks()"; qDebug() << "[EventHandler] starting first getTasks()";
m_network->getTasks(); m_network->getTasks();
@ -74,10 +94,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
const AccountInfo account = AccountDAO::findByMaterialId(materialId); const AccountInfo account = AccountDAO::findByMaterialId(materialId);
if (account.id == -1) { if (account.id == -1) {
qWarning() << "[EventHandler] Account not found for bank:" << bankName << "material_id:" << materialId; qWarning() << "[EventHandler] Account not found for bank:" << bankName << "material_id:" << materialId;
QJsonObject data; sendTaskError(typeStr, materialId, bankName, "Account not found for material_id: " + materialId);
data["status"] = "error";
data["description"] = "Account not found for material_id: " + materialId;
sendTaskResult(typeStr, materialId, bankName, QJsonValue(data));
continue; continue;
} }
@ -89,11 +106,19 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
event.deviceId = account.deviceId; event.deviceId = account.deviceId;
event.bankName = bankName; event.bankName = bankName;
event.timestamp = QDateTime::currentDateTimeUtc(); event.timestamp = QDateTime::currentDateTimeUtc();
event.json = QJsonDocument(task).toJson(QJsonDocument::Compact);
const QJsonObject body = task["body"].toObject(); const QJsonObject body = task["body"].toObject();
if (eventType == EventType::CreateTransaction) { if (eventType == EventType::CreateTransaction) {
event.amount = body["amount"].toDouble(); event.amount = body["amount"].toDouble() / 100.0;
event.phone = body["bank_participator_id"].toString(); event.phone = body["bank_participator_id"].toString();
// Резолвим название банка-получателя по алиасу
const QString bankAlias = body["extra"].toObject()["bank_participator_name"].toString();
if (!bankAlias.isEmpty()) {
const BankInfo bank = BankDAO::findByAlias(bankAlias);
event.comment = !bank.nspkName.isEmpty() ? bank.nspkName : bank.name;
}
} }
const int eventId = EventDAO::insertEvent(event); const int eventId = EventDAO::insertEvent(event);
@ -106,10 +131,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
if (!onlineDevices.contains(account.deviceId)) { if (!onlineDevices.contains(account.deviceId)) {
qWarning() << "[EventHandler] Device offline for material_id:" << materialId << "device:" << account.deviceId; qWarning() << "[EventHandler] Device offline for material_id:" << materialId << "device:" << account.deviceId;
EventDAO::updateEvent(eventId, EventStatus::Error, "Device is offline"); EventDAO::updateEvent(eventId, EventStatus::Error, "Device is offline");
QJsonObject errData; sendTaskError(typeStr, materialId, bankName, "Device is offline");
errData["status"] = "error";
errData["description"] = "Device is offline";
sendTaskResult(typeStr, materialId, bankName, QJsonValue(errData));
} }
} }
@ -125,10 +147,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
if (waitSecs > 5 * 60) { if (waitSecs > 5 * 60) {
qWarning() << "[EventHandler] Task expired after" << waitSecs << "s, event id:" << event.id; qWarning() << "[EventHandler] Task expired after" << waitSecs << "s, event id:" << event.id;
EventDAO::updateEvent(event.id, EventStatus::Error, "Task expired: waited " + QString::number(waitSecs) + "s"); EventDAO::updateEvent(event.id, EventStatus::Error, "Task expired: waited " + QString::number(waitSecs) + "s");
QJsonObject errData; sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Task expired: not started within 5 minutes");
errData["status"] = "error";
errData["description"] = "Task expired: not started within 5 minutes";
sendTaskResult(eventTypeToString(event.type), event.externalId, event.bankName, QJsonValue(errData));
continue; continue;
} }
@ -146,7 +165,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
} }
static void sendTaskResult(const QString &type, const QString &materialId, const QString &bankName, static void sendTaskResult(const QString &type, const QString &materialId, const QString &bankName,
const QJsonValue &data) { const QJsonValue &data, int eventId = -1) {
QJsonObject obj; QJsonObject obj;
obj["type"] = type; obj["type"] = type;
obj["material_id"] = materialId; // UUID string obj["material_id"] = materialId; // UUID string
@ -157,7 +176,45 @@ static void sendTaskResult(const QString &type, const QString &materialId, const
auto *networkService = new NetworkService; auto *networkService = new NetworkService;
networkService->moveToThread(thread); networkService->moveToThread(thread);
networkService->setPayload(QJsonDocument(obj).toJson()); networkService->setPayload(QJsonDocument(obj).toJson());
QObject::connect(thread, &QThread::started, networkService, &NetworkService::postTaskResult); QObject::connect(thread, &QThread::started, networkService, [networkService, eventId]() {
networkService->postTaskResult();
if (eventId != -1) {
EventDAO::markSentToServer(eventId);
}
});
QObject::connect(networkService, &NetworkService::finished, thread, &QThread::quit);
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName,
const QString &description) {
const QString message = QStringLiteral("[%1] bank=%2 material=%3: %4")
.arg(type, bankName, materialId, description);
auto *thread = new QThread;
auto *networkService = new NetworkService;
networkService->moveToThread(thread);
QObject::connect(thread, &QThread::started, networkService, [networkService, message]() {
networkService->postMonitoringLog("error", "task_error", message, {});
emit networkService->finished();
});
QObject::connect(networkService, &NetworkService::finished, thread, &QThread::quit);
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
static void sendMonitoringLog(const QString &type, const QString &event, const QString &message,
const QByteArray &image = {}) {
auto *thread = new QThread;
auto *networkService = new NetworkService;
networkService->moveToThread(thread);
QObject::connect(thread, &QThread::started, networkService, [networkService, type, event, message, image]() {
networkService->postMonitoringLog(type, event, message, image);
emit networkService->finished();
});
QObject::connect(networkService, &NetworkService::finished, thread, &QThread::quit); QObject::connect(networkService, &NetworkService::finished, thread, &QThread::quit);
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater); QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
@ -190,47 +247,69 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
if (!EventDAO::updateEvent(event.id, newStatus, result)) { if (!EventDAO::updateEvent(event.id, newStatus, result)) {
qCritical() << "[EventHandler] Failed to update event:" << event.id; qCritical() << "[EventHandler] Failed to update event:" << event.id;
} else { } else {
QJsonValue taskData;
if (newStatus == EventStatus::Error) { if (newStatus == EventStatus::Error) {
QJsonObject errObj; sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result);
errObj["status"] = "error"; EventDAO::markSentToServer(event.id);
errObj["description"] = result; }
taskData = errObj;
} else if (event.type == EventType::FetchProfile) { if (newStatus != EventStatus::Error) {
QJsonValue taskData;
if (event.type == EventType::FetchProfile) {
const AccountInfo account = AccountDAO::getAccountById(event.accountId); const AccountInfo account = AccountDAO::getAccountById(event.accountId);
EventDAO::updateEventAmount(event.id, account.amount); EventDAO::updateEventAmount(event.id, account.amount);
QJsonObject obj; QJsonObject obj;
obj["bank_account_id"] = QString(); obj["bank_account_id"] = QString();
obj["amount"] = static_cast<int>(account.amount); obj["amount"] = static_cast<int>(account.amount * 100);
obj["currency"] = 643; // RUB obj["currency"] = 643; // RUB
taskData = obj; taskData = obj;
} else if (event.type == EventType::FetchTransactions) { } else if (event.type == EventType::FetchTransactions) {
QList<TransactionInfo> txList = TransactionDAO::getTransactionsWithinHours(event.accountId, 24); QList<TransactionInfo> txList = TransactionDAO::getTransactionsWithinHours(event.accountId, 24);
if (txList.isEmpty()) {
txList = TransactionDAO::getLastTransactions(event.accountId, 10);
}
QJsonArray arr; QJsonArray arr;
for (const auto &tx : txList) { for (const auto &tx : txList) {
QJsonObject txObj; QJsonObject txObj;
txObj["bank_transaction_id"] = tx.bankTrExternalId.isEmpty() ? QJsonValue(QJsonValue::Null) : QJsonValue(tx.bankTrExternalId); txObj["bank_transaction_id"] = tx.bankTrExternalId.isEmpty() ? QJsonValue(QJsonValue::Null) : QJsonValue(tx.bankTrExternalId);
txObj["amount"] = static_cast<int>(tx.amount); txObj["amount"] = static_cast<int>(tx.amount * 100);
txObj["currency"] = 643; // RUB txObj["currency"] = 643; // RUB
txObj["transaction_type"] = (tx.type == TransactionType::Phone) ? "top-up" : "bank"; txObj["transaction_type"] = (tx.type == TransactionType::Phone) ? "top-up" : "bank";
txObj["bank_participator_id"]= tx.phone; txObj["bank_participator_id"]= tx.phone;
txObj["status"] = tx.status == TransactionStatus::Complete ? "completed" : (tx.status == TransactionStatus::Wait ? "progress" : "completed"); txObj["status"] = tx.status == TransactionStatus::Complete ? "completed" : (tx.status == TransactionStatus::Wait ? "progress" : "completed");
txObj["extra"] = QJsonObject(); txObj["extra"] = QJsonObject();
if (tx.timestamp.isValid()) { if (tx.timestamp.isValid()) {
txObj["created_at"] = tx.timestamp.toString(Qt::ISODate); txObj["created_at"] = tx.timestamp.toTimeZone(QTimeZone("Europe/Moscow")).toString(Qt::ISODateWithMs);
} else if (!tx.bankTime.isEmpty()) { } else if (!tx.bankTime.isEmpty()) {
QDateTime dt = QDateTime::fromString(tx.bankTime, "dd.MM.yyyy, HH:mm"); QDateTime dt = QDateTime::fromString(tx.bankTime, "dd.MM.yyyy, HH:mm");
if (!dt.isValid()) dt = QDateTime::fromString(tx.bankTime, "dd.MM.yyyy HH:mm"); if (!dt.isValid()) dt = QDateTime::fromString(tx.bankTime, "dd.MM.yyyy HH:mm");
txObj["created_at"] = dt.isValid() ? dt.toString(Qt::ISODate) : QJsonValue(QJsonValue::Null); if (dt.isValid()) {
dt.setTimeZone(QTimeZone("Europe/Moscow"));
txObj["created_at"] = dt.toString(Qt::ISODateWithMs);
} else {
txObj["created_at"] = QJsonValue(QJsonValue::Null);
}
} else { } else {
txObj["created_at"] = QJsonValue(QJsonValue::Null); txObj["created_at"] = QJsonValue(QJsonValue::Null);
} }
arr.append(txObj); arr.append(txObj);
} }
taskData = arr; taskData = arr;
} else if (event.type == EventType::CreateTransaction) {
QJsonObject obj;
obj["amount"] = -static_cast<int>(event.amount * 100);
obj["currency"] = 643;
obj["transaction_type"] = "top-up";
obj["bank_participator_id"]= event.phone;
obj["status"] = "completed";
obj["created_at"] = QDateTime::currentDateTime().toTimeZone(QTimeZone("Europe/Moscow")).toString(Qt::ISODateWithMs);
// Берём bank_transaction_id из последней сохранённой транзакции
QList<TransactionInfo> txList = TransactionDAO::getTransactionsWithinHours(event.accountId, 1);
for (const auto &tx : txList) {
if (!tx.bankTrExternalId.isEmpty() && qAbs(tx.amount) == event.amount && tx.phone == event.phone) {
obj["bank_transaction_id"] = tx.bankTrExternalId;
break;
}
}
taskData = obj;
} else { } else {
QJsonObject obj; QJsonObject obj;
obj["status"] = "completed"; obj["status"] = "completed";
@ -240,10 +319,28 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
eventTypeToString(event.type), eventTypeToString(event.type),
event.externalId, event.externalId,
event.bankName, event.bankName,
taskData taskData,
event.id
); );
} }
if (newStatus == EventStatus::Complete && event.type == EventType::CreateTransaction) {
const QString msg = QStringLiteral("[CREATE_TRANSACTION] bank=%1 material=%2 amount=%3 phone=%4")
.arg(event.bankName, event.externalId, QString::number(event.amount, 'f', 2), event.phone);
QByteArray screenshot;
const QString tmpPath = QDir::tempPath() + "/arc_tx_" + QString::number(event.id);
if (AdbUtils::takeScreenshot(event.deviceId, tmpPath)) {
QFile f(tmpPath + ".png");
if (f.open(QIODevice::ReadOnly)) {
screenshot = f.readAll();
f.close();
}
f.remove();
}
sendMonitoringLog("info", "task_success", msg, screenshot);
}
}
if (const auto tm = m_timers.take(key)) { if (const auto tm = m_timers.take(key)) {
tm->stop(); tm->stop();
tm->deleteLater(); tm->deleteLater();
@ -292,32 +389,35 @@ void EventHandler::startThreadForTask(const AccountInfo &account, const EventInf
} }
if (event.type == EventType::CreateTransaction) { if (event.type == EventType::CreateTransaction) {
// TODO: реализовать CREATE_TRANSACTION const QString recipientBank = event.comment; // резолвленное имя банка-получателя
if (appCode == "ozon") setup(new Ozon::PayByPhoneScript(account, event.phone, recipientBank, event.amount));
else if (appCode == "rshb") setup(new Rshb::PayByPhoneScript(account, event.phone, recipientBank, event.amount));
else qWarning() << "[EventHandler] CREATE_TRANSACTION: unknown appCode:" << appCode;
} }
// Watchdog-таймер: 5 минут — убиваем зависший поток // // Watchdog-таймер: 5 минут — убиваем зависший поток
const auto timer = new QTimer(this); // const auto timer = new QTimer(this);
timer->setSingleShot(true); // timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [this, key]() { // connect(timer, &QTimer::timeout, this, [this, key]() {
qWarning() << "[EventHandler] Timeout for key" << key << "- killing stuck thread."; // qWarning() << "[EventHandler] Timeout for key" << key << "- killing stuck thread.";
if (QThread *thread = m_threads.value(key); thread && thread->isRunning()) { // if (QThread *thread = m_threads.value(key); thread && thread->isRunning()) {
thread->requestInterruption(); // thread->requestInterruption();
if (!thread->wait(500)) { // if (!thread->wait(500)) {
qWarning() << "[EventHandler] Hard terminate for key" << key; // qWarning() << "[EventHandler] Hard terminate for key" << key;
thread->terminate(); // thread->terminate();
thread->wait(); // thread->wait();
} // }
} // }
if (const auto tm = m_timers.take(key)) { // if (const auto tm = m_timers.take(key)) {
tm->deleteLater(); // tm->deleteLater();
} // }
m_threads.remove(key); // m_threads.remove(key);
}); // });
m_threads[key] = thr; m_threads[key] = thr;
m_timers[key] = timer; // m_timers[key] = timer;
timer->start(5 * 60 * 1000); // timer->start(5 * 60 * 1000);
thr->start(); thr->start();
} }

View File

@ -1,5 +1,6 @@
#include "NetworkService.h" #include "NetworkService.h"
#include <QFile>
#include <QUrlQuery> #include <QUrlQuery>
#include <QHttpPart> #include <QHttpPart>
#include <QNetworkReply> #include <QNetworkReply>
@ -626,6 +627,10 @@ void NetworkService::getTasks() {
QUrl url(m_apiBase + m_pathGetTasks); QUrl url(m_apiBase + m_pathGetTasks);
QUrlQuery urlQuery; QUrlQuery urlQuery;
const QStringList materialIds = AccountDAO::getActiveMaterialIds(); const QStringList materialIds = AccountDAO::getActiveMaterialIds();
if (materialIds.isEmpty()) {
m_fetchingTasks = false;
return;
}
for (const QString &mid : materialIds) { for (const QString &mid : materialIds) {
urlQuery.addQueryItem("materials_id", mid); urlQuery.addQueryItem("materials_id", mid);
} }
@ -665,7 +670,7 @@ void NetworkService::getTasks() {
void NetworkService::postTaskResult() { void NetworkService::postTaskResult() {
const QJsonObject body = QJsonDocument::fromJson(m_json).object(); const QJsonObject body = QJsonDocument::fromJson(m_json).object();
const QJsonValue result = postJson(m_apiBase + m_pathTaskResult, body, true); const QJsonValue result = postJson(m_apiBase + m_pathTaskResult, body, true);
if (result.isNull() || result.isUndefined()) { if (result.isUndefined()) {
qWarning() << "[NetworkService] postTaskResult failed, body:" << QJsonDocument(body).toJson(QJsonDocument::Compact); qWarning() << "[NetworkService] postTaskResult failed, body:" << QJsonDocument(body).toJson(QJsonDocument::Compact);
} }
emit finished(); emit finished();
@ -675,18 +680,20 @@ void NetworkService::postMonitoringLog(const QString &type, const QString &event
const QString &message, const QByteArray &image) { const QString &message, const QByteArray &image) {
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "type", type); addFormField(multiPart, "type", type.toUpper());
addFormField(multiPart, "event", event); addFormField(multiPart, "event", event);
addFormField(multiPart, "message", message); addFormField(multiPart, "message", message);
if (!image.isEmpty()) {
QHttpPart imagePart; QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png"); imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/png");
imagePart.setHeader( imagePart.setHeader(
QNetworkRequest::ContentDispositionHeader, QNetworkRequest::ContentDispositionHeader,
QStringLiteral("form-data; name=\"image\"; filename=\"screenshot.png\"") QStringLiteral("form-data; name=\"image\"; filename=\"screenshot.png\"")
); );
imagePart.setBody(image.isEmpty() ? QByteArray(1, '\0') : image); imagePart.setBody(image);
multiPart->append(imagePart); multiPart->append(imagePart);
}
const QUrl url(m_apiBase + m_pathMonitoringLog); const QUrl url(m_apiBase + m_pathMonitoringLog);
QNetworkRequest request(url); QNetworkRequest request(url);
@ -694,12 +701,15 @@ void NetworkService::postMonitoringLog(const QString &type, const QString &event
QNetworkReply *reply = m_manager->post(request, multiPart); QNetworkReply *reply = m_manager->post(request, multiPart);
multiPart->setParent(reply); multiPart->setParent(reply);
connect(reply, &QNetworkReply::finished, reply, [reply]() { QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString(); qWarning() << "[NetworkService] postMonitoringLog failed:" << reply->errorString()
<< "\n response body:" << reply->readAll();
} }
reply->deleteLater(); reply->deleteLater();
});
} }
void NetworkService::start() { void NetworkService::start() {

View File

@ -1,9 +1,11 @@
#include "EventWindow.h" #include "EventWindow.h"
#include <QDialog>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QHeaderView> #include <QHeaderView>
#include <QJsonDocument> #include <QJsonDocument>
#include <QJsonObject> #include <QJsonObject>
#include <QPlainTextEdit>
#include <QThread> #include <QThread>
#include <QVBoxLayout> #include <QVBoxLayout>
@ -18,7 +20,9 @@ static constexpr int COL_DEVICE = 4;
static constexpr int COL_STATUS = 5; static constexpr int COL_STATUS = 5;
static constexpr int COL_AMOUNT = 6; static constexpr int COL_AMOUNT = 6;
static constexpr int COL_COMMENT = 7; static constexpr int COL_COMMENT = 7;
static constexpr int COL_ACTION = 8; static constexpr int COL_JSON = 8;
static constexpr int COL_SENT = 9;
static constexpr int COL_ACTION = 10;
static void sendErrorToServer(const QString &type, const QString &materialId, const QString &bankName) { static void sendErrorToServer(const QString &type, const QString &materialId, const QString &bankName) {
QJsonObject errData; QJsonObject errData;
@ -63,13 +67,13 @@ static QString typeDisplayName(EventType t) {
EventWindow::EventWindow(QWidget *parent) : QDialog(parent) { EventWindow::EventWindow(QWidget *parent) : QDialog(parent) {
setWindowTitle("События"); setWindowTitle("События");
resize(1100, 600); resize(1400, 600);
// ── Таблица ─────────────────────────────────────────────────────────────── // ── Таблица ───────────────────────────────────────────────────────────────
m_table = new QTableWidget(this); m_table = new QTableWidget(this);
m_table->setColumnCount(9); m_table->setColumnCount(11);
m_table->setHorizontalHeaderLabels({ m_table->setHorizontalHeaderLabels({
"#", "Время", "Тип", "Банк", "Устройство", "Статус", "Сумма", "Комментарий", "" "#", "Время", "Тип", "Банк", "Устройство", "Статус", "Сумма", "Комментарий", "JSON", "Отпр.", ""
}); });
m_table->setEditTriggers(QAbstractItemView::NoEditTriggers); m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
m_table->setSelectionBehavior(QAbstractItemView::SelectRows); m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
@ -83,6 +87,8 @@ EventWindow::EventWindow(QWidget *parent) : QDialog(parent) {
m_table->setColumnWidth(COL_DEVICE, 120); m_table->setColumnWidth(COL_DEVICE, 120);
m_table->setColumnWidth(COL_STATUS, 100); m_table->setColumnWidth(COL_STATUS, 100);
m_table->setColumnWidth(COL_AMOUNT, 80); m_table->setColumnWidth(COL_AMOUNT, 80);
m_table->setColumnWidth(COL_JSON, 200);
m_table->setColumnWidth(COL_SENT, 45);
m_table->setColumnWidth(COL_ACTION, 80); m_table->setColumnWidth(COL_ACTION, 80);
// ── Пагинация ───────────────────────────────────────────────────────────── // ── Пагинация ─────────────────────────────────────────────────────────────
@ -116,6 +122,24 @@ EventWindow::EventWindow(QWidget *parent) : QDialog(parent) {
m_page = 0; m_page = 0;
loadPage(); loadPage();
}); });
connect(m_table, &QTableWidget::cellDoubleClicked, this, [this](int row, int col) {
if (col != COL_JSON) return;
auto *item = m_table->item(row, col);
if (!item) return;
const QString json = item->toolTip();
if (json.isEmpty()) return;
auto *dlg = new QDialog(this);
dlg->setWindowTitle("JSON");
dlg->resize(600, 400);
auto *edit = new QPlainTextEdit(dlg);
edit->setReadOnly(true);
edit->setPlainText(QJsonDocument::fromJson(json.toUtf8()).toJson(QJsonDocument::Indented));
auto *layout = new QVBoxLayout(dlg);
layout->addWidget(edit);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->show();
});
loadPage(); loadPage();
} }
@ -141,6 +165,12 @@ void EventWindow::loadPage() {
e.amount > 0 ? QString::number(e.amount, 'f', 2) : "")); e.amount > 0 ? QString::number(e.amount, 'f', 2) : ""));
m_table->setItem(i, COL_COMMENT, new QTableWidgetItem(e.comment)); m_table->setItem(i, COL_COMMENT, new QTableWidgetItem(e.comment));
auto *jsonItem = new QTableWidgetItem(e.json.left(80));
jsonItem->setToolTip(e.json);
m_table->setItem(i, COL_JSON, jsonItem);
m_table->setItem(i, COL_SENT, new QTableWidgetItem(e.sentToServer ? "+" : ""));
// Подсветка строки по статусу // Подсветка строки по статусу
QColor bg; QColor bg;
switch (e.status) { switch (e.status) {