399 lines
19 KiB
C++
399 lines
19 KiB
C++
#include "GetLastDaysHistoryScript.h"
|
||
|
||
#include <QJsonObject>
|
||
#include <QRandomGenerator>
|
||
#include <QThread>
|
||
#include <utility>
|
||
|
||
#include "DatabaseManager.h"
|
||
#include "adb/AdbUtils.h"
|
||
#include "dao/AccountDAO.h"
|
||
#include "dao/ApplicationDAO.h"
|
||
#include "dao/DeviceDAO.h"
|
||
#include "dao/TransactionDAO.h"
|
||
#include "db/DeviceInfo.h"
|
||
#include "net/NetworkService.h"
|
||
#include "time/DateUtils.h"
|
||
|
||
namespace Rshb {
|
||
|
||
GetLastDaysHistoryScript::GetLastDaysHistoryScript(
|
||
AccountInfo account,
|
||
QObject *parent
|
||
) : CommonScript(parent),
|
||
m_account(std::move(account)) {
|
||
}
|
||
|
||
GetLastDaysHistoryScript::~GetLastDaysHistoryScript() = default;
|
||
|
||
QList<TransactionInfo> findSavedTransaction(
|
||
const QList<TransactionInfo> &transactions,
|
||
const TransactionInfo &parsed
|
||
) {
|
||
QList<TransactionInfo> txs;
|
||
const QDateTime dateTime = QDateTime::fromString(parsed.bankTime, "dd.MM.yyyy"); // Московское время
|
||
|
||
for (const TransactionInfo &info: transactions) {
|
||
QDateTime timestamp = info.timestamp;
|
||
QString description = parsed.description;
|
||
// если тот же день
|
||
if (dateTime.date() == timestamp.date() && parsed.amount == info.amount) {
|
||
// Перевод Надежда Юрьевна К** в Альфа-Банк через СБП, по номеру телефона +79641815...
|
||
if (description.contains("Перевод")) {
|
||
if (description.contains(info.name)) {
|
||
txs.append(info);
|
||
}
|
||
} else {
|
||
// Перевод по карте
|
||
if (description == info.description) {
|
||
txs.append(info);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return txs;
|
||
}
|
||
|
||
int countSimilar(const TransactionInfo &transaction, const QList<TransactionInfo> &transactions) {
|
||
int count = 0;
|
||
for (const TransactionInfo &info: transactions) {
|
||
if (info.amount == transaction.amount
|
||
&& info.description == transaction.description
|
||
&& info.bankTime == transaction.bankTime) {
|
||
++count;
|
||
}
|
||
}
|
||
return count;
|
||
}
|
||
|
||
QString formatPrice(const double value) {
|
||
QString sign = value < 0 ? "Минус" : "Плюс";
|
||
const auto intValue = static_cast<qint64>(std::abs(value)); // округление до целого
|
||
|
||
QString number = QString::number(intValue);
|
||
QString formatted;
|
||
|
||
int count = 0;
|
||
for (int i = number.length() - 1; i >= 0; --i) {
|
||
formatted.prepend(number[i]);
|
||
++count;
|
||
if (count % 3 == 0 && i != 0) {
|
||
formatted.prepend(' ');
|
||
}
|
||
}
|
||
|
||
return QString("%1 %2").arg(sign, formatted);
|
||
}
|
||
|
||
bool GetLastDaysHistoryScript::saveAndPostNewTransactionData(
|
||
const AccountInfo &account,
|
||
const TransactionInfo &transaction,
|
||
const TransactionStatus &status
|
||
) {
|
||
TransactionInfo tr;
|
||
tr.accountId = account.id;
|
||
tr.description = transaction.description;
|
||
tr.bankTime = transaction.bankTime;
|
||
tr.bankName = transaction.bankName;
|
||
tr.amount = transaction.amount;
|
||
tr.phone = transaction.phone;
|
||
tr.updateTime = DateUtils::getUtcNow();
|
||
tr.fee = transaction.fee;
|
||
tr.name = transaction.name;
|
||
tr.bankTrExternalId = transaction.bankTrExternalId;
|
||
tr.status = status;
|
||
tr.type = transaction.description.startsWith("Перевод") ? TransactionType::Phone : TransactionType::Card;
|
||
tr.completeTime = DateUtils::getUtcNow();
|
||
tr.id = TransactionDAO::insertTransaction(tr);
|
||
|
||
if (tr.id == -1) {
|
||
return false;
|
||
}
|
||
postNewTransactionData(account, tr);
|
||
return true;
|
||
}
|
||
|
||
bool GetLastDaysHistoryScript::updateAndPostTransactionData(
|
||
const AccountInfo &account,
|
||
const int transactionId,
|
||
const TransactionStatus status,
|
||
const QString &oldDesc,
|
||
const QString &newDesc
|
||
) {
|
||
if (TransactionDAO::updateTransaction(transactionId, status, oldDesc, newDesc)) {
|
||
updateTransactionData(account, transactionId, status, oldDesc, newDesc);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void GetLastDaysHistoryScript::doStart() {
|
||
// берем все транзакции из БД
|
||
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
|
||
const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode);
|
||
|
||
if (!xmlScreenParser.isHomeScreen()) {
|
||
if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) {
|
||
m_error = "Не смогли дойти до домашней страницы";
|
||
qCritical() << m_error;
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||
Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(device.screenWidth, device.screenHeight);
|
||
if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) {
|
||
AdbUtils::makeTap(device.id, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y());
|
||
QThread::msleep(500);
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||
|
||
if (xmlScreenParser.isHomeScreen()) {
|
||
if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) {
|
||
QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
|
||
|
||
QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();
|
||
for (auto &[account, node]: accounts) {
|
||
account.deviceId = m_account.deviceId;
|
||
account.appCode = m_account.appCode;
|
||
if (!AccountDAO::upsertAccount(account)) {
|
||
qDebug() << "Not updated: " << account.lastNumbers;
|
||
}
|
||
}
|
||
postAccountsData(accounts);
|
||
|
||
findAndTapOnAccount(device.id, m_account.lastNumbers);
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||
Node actionBtn = xmlScreenParser.findButtonNode("Действия", false);
|
||
if (!actionBtn.isEmpty()) {
|
||
AdbUtils::makeTap(m_account.deviceId, actionBtn.x(), actionBtn.y());
|
||
QThread::msleep(200);
|
||
} else {
|
||
m_error = "All action btn not found";
|
||
qCritical() << m_error;
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
Node reportButton = swipeToButton(
|
||
m_account.deviceId,
|
||
"Выписка",
|
||
m_counter, device.screenWidth,
|
||
device.screenHeight
|
||
);
|
||
|
||
if (!reportButton.isEmpty()) {
|
||
AdbUtils::makeTap(m_account.deviceId, reportButton.x(), reportButton.y());
|
||
QThread::msleep(1000);
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||
|
||
Node reportTitle = xmlScreenParser.findTextNode("Выписка");
|
||
if (!reportTitle.isEmpty() && reportTitle.y() < 300) {
|
||
QDate beforeYesterday = DateUtils::getMoscowNow().date().addDays(-2);
|
||
QString beforeYesterdayTitle = beforeYesterday.toString("dd.MM.yyyy");
|
||
|
||
for (int i = 0; i < 5; ++i) {
|
||
Node byDayTitle = xmlScreenParser.findTextNode(beforeYesterdayTitle);
|
||
if (!byDayTitle.isEmpty()) {
|
||
qWarning() << "--- beforeYesterdayTitle found!";
|
||
break;
|
||
}
|
||
AdbUtils::makeSwipe(
|
||
m_account.deviceId,
|
||
device.screenWidth / 2, device.screenHeight / 4 * 3,
|
||
device.screenWidth / 2, device.screenHeight / 4
|
||
);
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||
}
|
||
} else {
|
||
m_error = "All action - report title not found";
|
||
qCritical() << m_error;
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
|
||
// все поддвержденные
|
||
QList<TransactionInfo> parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory();
|
||
QSet<int> parsedTransactionIds;
|
||
for (const TransactionInfo &transaction: parsedTransactions) {
|
||
bool updated = false;
|
||
|
||
for (const TransactionInfo &saved: localTransactions) {
|
||
if (parsedTransactionIds.contains(saved.id)) {
|
||
continue;
|
||
}
|
||
QDate sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy HH:mm:ss").date();
|
||
if (!sDate.isValid()) {
|
||
sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy").date();
|
||
}
|
||
QDate pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy HH:mm:ss").date();
|
||
if (!pDate.isValid()) {
|
||
pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy").date();
|
||
}
|
||
// Если сумма и дата совпадают
|
||
if (saved.amount == transaction.amount && sDate == pDate) {
|
||
if (transaction.description.startsWith("Перевод")) {
|
||
// Перевод по номеру
|
||
if (saved.bankName == transaction.bankName && saved.phone == transaction.phone) {
|
||
parsedTransactionIds.insert(saved.id);
|
||
updated = true;
|
||
if (saved.status != TransactionStatus::Complete || saved.description.isEmpty()) {
|
||
if (!TransactionDAO::updateTransaction(
|
||
saved.id,
|
||
TransactionStatus::Complete,
|
||
saved.description,
|
||
transaction.description
|
||
)) {
|
||
qDebug().noquote().nospace() << "--- updated+phone failed" <<
|
||
convertTransactionToString(transaction);;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
} else {
|
||
// перевод по карте
|
||
// перезаписать saved.description -> updatedDescription если не равны
|
||
if (!saved.description.isEmpty()) {
|
||
if (saved.description == transaction.description) {
|
||
// описания совпадают
|
||
if (saved.status != TransactionStatus::Complete) {
|
||
if (!TransactionDAO::updateTransaction(
|
||
saved.id,
|
||
TransactionStatus::Complete,
|
||
saved.description,
|
||
transaction.description
|
||
)) {
|
||
qDebug().noquote().nospace() << "--- updated+card[description same] failed"
|
||
<<
|
||
convertTransactionToString(transaction);;
|
||
}
|
||
}
|
||
parsedTransactionIds.insert(saved.id);
|
||
updated = true;
|
||
break;
|
||
} else {
|
||
// описания не совпадают, то пробуем найти такое же, если не находим то это новая
|
||
bool findWithSameName = false;
|
||
for (const TransactionInfo &saved2: localTransactions) {
|
||
if (saved2.description == transaction.description && !parsedTransactionIds.
|
||
contains(
|
||
saved2.id)) {
|
||
// мы нашли с одинаковым описанием и еще не обработанную
|
||
if (saved2.status != TransactionStatus::Complete) {
|
||
if (!TransactionDAO::updateTransaction(
|
||
saved2.id,
|
||
TransactionStatus::Complete,
|
||
saved2.description,
|
||
transaction.description
|
||
)) {
|
||
qDebug().noquote().nospace() <<
|
||
"--- updated+card2[description same] failed"
|
||
<<
|
||
convertTransactionToString(transaction);;
|
||
}
|
||
}
|
||
parsedTransactionIds.insert(saved2.id);
|
||
updated = true;
|
||
findWithSameName = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!findWithSameName) {
|
||
// обновляем описание
|
||
if (!TransactionDAO::updateTransaction(
|
||
saved.id,
|
||
TransactionStatus::Complete,
|
||
saved.description,
|
||
transaction.description
|
||
)) {
|
||
qDebug().noquote().nospace() <<
|
||
"--- updated+card2+new desc[description same] failed" <<
|
||
convertTransactionToString(transaction);;
|
||
}
|
||
}
|
||
parsedTransactionIds.insert(saved.id);
|
||
updated = true;
|
||
break;
|
||
}
|
||
} else {
|
||
// пустой описание
|
||
if (!TransactionDAO::updateTransaction(
|
||
saved.id,
|
||
TransactionStatus::Complete,
|
||
saved.description,
|
||
transaction.description
|
||
)) {
|
||
qDebug().noquote().nospace() <<
|
||
"--- updated+card[empty description]" <<
|
||
convertTransactionToString(transaction);;
|
||
}
|
||
parsedTransactionIds.insert(saved.id);
|
||
updated = true;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (!updated) {
|
||
if (!saveAndPostNewTransactionData(m_account, transaction, TransactionStatus::Complete)) {
|
||
qDebug() << "--- saveAndPostNewTransactionData failed";
|
||
}
|
||
}
|
||
|
||
// Если это перевод
|
||
// Если совпадает по сумме + телефон + банк дата (без времени) + банк
|
||
|
||
// Если по карте, мы знаем ток дату + сумма
|
||
}
|
||
Node waitTab = xmlScreenParser.findButtonNode("Ожидающие обработки", true);
|
||
if (!waitTab.isEmpty()) {
|
||
AdbUtils::makeTap(m_account.deviceId, waitTab.x(), waitTab.y());
|
||
QThread::msleep(1000);
|
||
|
||
QList<TransactionInfo> parsedWaitTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory();
|
||
for (const TransactionInfo &transaction: parsedWaitTransactions) {
|
||
QList<TransactionInfo> localActualTransactions = TransactionDAO::getTransactionsWithinHours(
|
||
m_account.id, 48
|
||
);
|
||
for (const TransactionInfo &saved: localActualTransactions) {
|
||
if (saved.status != TransactionStatus::Wait) {
|
||
continue;
|
||
}
|
||
|
||
QDate sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy HH:mm:ss").date();
|
||
if (!sDate.isValid()) {
|
||
sDate = QDateTime::fromString(saved.bankTime, "dd.MM.yyyy").date();
|
||
}
|
||
QDate pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy HH:mm:ss").date();
|
||
if (!pDate.isValid()) {
|
||
pDate = QDateTime::fromString(transaction.bankTime, "dd.MM.yyyy").date();
|
||
}
|
||
if (saved.amount == transaction.amount && sDate == pDate) {
|
||
// одно и тоже
|
||
} else {
|
||
if (!saveAndPostNewTransactionData(m_account, transaction, TransactionStatus::Wait)) {
|
||
qDebug() << "--- saveAndPostNewTransactionData - Wait failed";
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
m_error = "GetLastDaysHistoryScript - home screen not found";
|
||
qWarning() << m_error;
|
||
emit finishedWithResult(m_error);
|
||
}
|
||
AdbUtils::goBack(m_account.deviceId);
|
||
AdbUtils::goBack(m_account.deviceId);
|
||
AdbUtils::goBack(m_account.deviceId);
|
||
emit finishedWithResult(m_error);
|
||
}
|
||
|
||
} // namespace Rshb
|