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.
420 lines
15 KiB
C++
420 lines
15 KiB
C++
#include <QSqlQuery>
|
||
#include <QSqlError>
|
||
#include <QVariant>
|
||
|
||
#include "DatabaseManager.h"
|
||
#include "TransactionDAO.h"
|
||
|
||
#include "time/DateUtils.h"
|
||
|
||
bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
|
||
if (info.amount < 0) {
|
||
// Если исходящая, то мы знаем время timestamp
|
||
query.prepare(R"(
|
||
SELECT COUNT(*) FROM transactions
|
||
WHERE account_id = :account_id AND amount = :amount AND timestamp = :timestamp AND type = :type
|
||
)");
|
||
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
|
||
} else {
|
||
query.prepare(R"(
|
||
SELECT COUNT(*) FROM transactions
|
||
WHERE account_id = :account_id AND amount = :amount AND bank_time = :bank_time AND bank_name = :bank_name
|
||
)");
|
||
query.bindValue(":bank_time", info.bankTime);
|
||
query.bindValue(":bank_name", info.bankName);
|
||
}
|
||
|
||
query.bindValue(":account_id", info.accountId);
|
||
query.bindValue(":amount", info.amount);
|
||
query.bindValue(":type", transactionTypeToString(info.type));
|
||
|
||
if (!query.exec() || !query.next()) {
|
||
qWarning() << "Ошибка при проверке существования transaction:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
|
||
if (query.value(0).toInt() > 0) {
|
||
return false; // Уже существует
|
||
}
|
||
|
||
query.prepare(R"(
|
||
INSERT INTO transactions (
|
||
account_id, amount, fee, status, phone, type,
|
||
description, updated_description,
|
||
bank_name, bank_time, name, bank_tr_external_id,
|
||
update_time, complete_time, timestamp
|
||
) VALUES (
|
||
:account_id, :amount, :fee, :status, :phone, :type,
|
||
:description, :updated_description,
|
||
:bank_name, :bank_time, :name, :bank_tr_external_id,
|
||
:update_time, :complete_time, :timestamp
|
||
)
|
||
)");
|
||
|
||
query.bindValue(":account_id", info.accountId);
|
||
query.bindValue(":amount", info.amount);
|
||
query.bindValue(":fee", info.fee);
|
||
query.bindValue(":status", transactionStatusToString(info.status));
|
||
query.bindValue(":type", transactionTypeToString(info.type));
|
||
query.bindValue(":phone", info.phone);
|
||
query.bindValue(":name", info.name);
|
||
query.bindValue(":description", info.description);
|
||
query.bindValue(":updated_description", info.updatedDescription);
|
||
query.bindValue(":bank_name", info.bankName);
|
||
query.bindValue(":bank_time", info.bankTime);
|
||
query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
|
||
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
|
||
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
|
||
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при вставке transaction:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
int TransactionDAO::insertTransaction(const TransactionInfo &info) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
|
||
query.prepare(R"(
|
||
INSERT INTO transactions (
|
||
account_id, amount, fee, status, phone, type,
|
||
description, updated_description,
|
||
bank_name, bank_time, name, bank_tr_external_id,
|
||
update_time, complete_time
|
||
) VALUES (
|
||
:account_id, :amount, :fee, :status, :phone, :type,
|
||
:description, :updated_description,
|
||
:bank_name, :bank_time, :name, :bank_tr_external_id,
|
||
:update_time, :complete_time
|
||
)
|
||
)");
|
||
|
||
query.bindValue(":account_id", info.accountId);
|
||
query.bindValue(":amount", info.amount);
|
||
query.bindValue(":fee", info.fee);
|
||
query.bindValue(":status", transactionStatusToString(info.status));
|
||
query.bindValue(":type", transactionTypeToString(info.type));
|
||
query.bindValue(":phone", info.phone);
|
||
query.bindValue(":name", info.name);
|
||
query.bindValue(":description", info.description);
|
||
query.bindValue(":updated_description", info.updatedDescription);
|
||
query.bindValue(":bank_name", info.bankName);
|
||
query.bindValue(":bank_time", info.bankTime);
|
||
query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
|
||
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
|
||
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при вставке transaction:" << query.lastError().text();
|
||
return -1;
|
||
}
|
||
|
||
return query.lastInsertId().toInt();
|
||
}
|
||
|
||
bool TransactionDAO::deleteTransaction(const int id) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
query.prepare("DELETE FROM transactions WHERE id = :id");
|
||
query.bindValue(":id", id);
|
||
return query.exec();
|
||
}
|
||
|
||
|
||
TransactionInfo parseTransaction(const QSqlQuery &query) {
|
||
TransactionInfo tx;
|
||
tx.id = query.value("id").toInt();
|
||
tx.accountId = query.value("account_id").toInt();
|
||
tx.amount = query.value("amount").toDouble();
|
||
tx.fee = query.value("fee").toDouble();
|
||
tx.status = transactionStatusFromString(query.value("status").toString());
|
||
tx.type = transactionTypeFromString(query.value("type").toString());
|
||
tx.phone = query.value("phone").toString();
|
||
tx.name = query.value("name").toString();
|
||
tx.description = query.value("description").toString();
|
||
tx.updatedDescription = query.value("updated_description").toString();
|
||
tx.bankName = query.value("bank_name").toString();
|
||
tx.bankTime = query.value("bank_time").toString();
|
||
tx.bankTrExternalId = query.value("bank_tr_external_id").toString();
|
||
tx.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
|
||
tx.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString());
|
||
tx.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString());
|
||
tx.receiptImage = query.value("receipt_image").toByteArray();
|
||
return tx;
|
||
}
|
||
|
||
|
||
QList<TransactionInfo> TransactionDAO::getTransactions(const int accountId) {
|
||
QList<TransactionInfo> list;
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
|
||
query.prepare(R"(
|
||
SELECT * FROM transactions
|
||
WHERE account_id = :account_id
|
||
ORDER BY timestamp DESC
|
||
)");
|
||
|
||
query.bindValue(":account_id", accountId);
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при получении transactions:" << query.lastError().text();
|
||
return list;
|
||
}
|
||
|
||
while (query.next()) {
|
||
list.append(parseTransaction(query));
|
||
}
|
||
|
||
return list;
|
||
}
|
||
|
||
bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
|
||
qDebug().noquote().nospace() << convertTransactionToString(info);
|
||
|
||
query.prepare(R"(
|
||
UPDATE transactions SET
|
||
amount = :amount,
|
||
fee = :fee,
|
||
status = :status,
|
||
type = :type,
|
||
phone = :phone,
|
||
description = :description,
|
||
updated_description = :updated_description,
|
||
bank_name = :bank_name,
|
||
bank_time = :bank_time,
|
||
name = :name,
|
||
bank_tr_external_id = :bank_tr_external_id,
|
||
update_time = :update_time,
|
||
complete_time = :complete_time,
|
||
timestamp = :timestamp
|
||
WHERE id = :id
|
||
)");
|
||
|
||
query.bindValue(":id", info.id);
|
||
query.bindValue(":amount", info.amount);
|
||
query.bindValue(":fee", info.fee);
|
||
query.bindValue(":status", transactionStatusToString(info.status));
|
||
query.bindValue(":type", transactionTypeToString(info.type));
|
||
query.bindValue(":phone", info.phone);
|
||
query.bindValue(":name", info.name);
|
||
query.bindValue(":description", info.description);
|
||
query.bindValue(":updated_description", info.updatedDescription);
|
||
query.bindValue(":bank_name", info.bankName);
|
||
query.bindValue(":bank_time", info.bankTime);
|
||
query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
|
||
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
|
||
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
|
||
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool TransactionDAO::updateTransactionStatus(
|
||
const int id,
|
||
const TransactionStatus status
|
||
) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
|
||
query.prepare(R"(
|
||
UPDATE transactions SET
|
||
status = :status,
|
||
update_time = :update_time,
|
||
complete_time = :complete_time
|
||
WHERE id = :id
|
||
)");
|
||
|
||
// const QString currentTime = now.toString("yyyy-MM-dd HH:mm:ss");
|
||
query.bindValue(":id", id);
|
||
query.bindValue(":status", transactionStatusToString(status));
|
||
query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
|
||
|
||
if (status == TransactionStatus::Complete) {
|
||
query.bindValue(":complete_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
|
||
} else {
|
||
query.bindValue(":complete_time", QVariant(QMetaType(QMetaType::QString)));
|
||
}
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
TransactionInfo TransactionDAO::getTransactionById(const int id) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
query.prepare("SELECT * FROM transactions WHERE id = :id");
|
||
query.bindValue(":id", id);
|
||
|
||
|
||
if (!query.exec() || !query.next()) {
|
||
qWarning() << "Ошибка или не найден transaction с id:" << id << query.lastError().text();
|
||
return {};
|
||
}
|
||
return parseTransaction(query);
|
||
}
|
||
|
||
QList<TransactionInfo> TransactionDAO::getTransactionsWithinHours(const int accountId, const int hours) {
|
||
QList<TransactionInfo> list;
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
query.prepare(R"(
|
||
SELECT * FROM transactions
|
||
WHERE account_id = :accountId
|
||
AND timestamp >= datetime('now', '-' || :hours || ' hours')
|
||
)");
|
||
query.bindValue(":accountId", accountId);
|
||
query.bindValue(":hours", hours);
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка получения транзакций: " << query.lastError().text();
|
||
return {};
|
||
}
|
||
|
||
while (query.next()) {
|
||
list.append(parseTransaction(query));
|
||
}
|
||
return list;
|
||
}
|
||
|
||
QList<TransactionInfo> TransactionDAO::getLastTransactions(const int accountId, const int limit) {
|
||
QList<TransactionInfo> list;
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
query.prepare(R"(
|
||
SELECT * FROM transactions
|
||
WHERE account_id = :accountId
|
||
ORDER BY timestamp DESC
|
||
LIMIT :limit
|
||
)");
|
||
query.bindValue(":accountId", accountId);
|
||
query.bindValue(":limit", limit);
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка получения последних транзакций: " << query.lastError().text();
|
||
return {};
|
||
}
|
||
|
||
while (query.next()) {
|
||
list.append(parseTransaction(query));
|
||
}
|
||
return list;
|
||
}
|
||
|
||
bool TransactionDAO::updateTransaction(
|
||
const int id,
|
||
const TransactionStatus status,
|
||
const QString &oldDesc,
|
||
const QString &newDesc
|
||
) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
|
||
query.prepare(R"(
|
||
UPDATE transactions SET
|
||
status = :status,
|
||
update_time = :update_time,
|
||
complete_time = :complete_time,
|
||
description = :description,
|
||
updated_description = :updated_description
|
||
WHERE id = :id
|
||
)");
|
||
|
||
query.bindValue(":id", id);
|
||
query.bindValue(":status", transactionStatusToString(status));
|
||
query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
|
||
|
||
if (oldDesc.isEmpty() || oldDesc != newDesc) {
|
||
query.bindValue(":description", newDesc);
|
||
query.bindValue(":updated_description", oldDesc);
|
||
} else {
|
||
query.bindValue(":description", newDesc);
|
||
query.bindValue(":updated_description", "");
|
||
}
|
||
|
||
if (status == TransactionStatus::Complete) {
|
||
query.bindValue(":complete_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
|
||
} else {
|
||
query.bindValue(":complete_time", QVariant(QMetaType(QMetaType::QString)));
|
||
}
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool TransactionDAO::updateExternalTransactionId(const int id, const int externalId) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
|
||
query.prepare(R"(
|
||
UPDATE transactions SET
|
||
external_id = :external_id
|
||
WHERE id = :id
|
||
)");
|
||
|
||
query.bindValue(":id", id);
|
||
query.bindValue(":external_id", externalId);
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при обновлении external_id:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool TransactionDAO::updateBankTrExternalId(const int id, const QString &bankTrExternalId) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
query.prepare("UPDATE transactions SET bank_tr_external_id = :val WHERE id = :id");
|
||
query.bindValue(":id", id);
|
||
query.bindValue(":val", bankTrExternalId);
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при обновлении bank_tr_external_id:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool TransactionDAO::updateReceiptImage(const int id, const QByteArray &image) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
query.prepare("UPDATE transactions SET receipt_image = :image WHERE id = :id");
|
||
query.bindValue(":id", id);
|
||
query.bindValue(":image", image);
|
||
|
||
if (!query.exec()) {
|
||
qWarning() << "Ошибка при сохранении receipt_image:" << query.lastError().text();
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
TransactionInfo TransactionDAO::findByBankTimeAndAmount(
|
||
const int accountId, const QString &bankName, const QString &bankTime, const double amount
|
||
) {
|
||
QSqlQuery query(DatabaseManager::instance().database());
|
||
query.prepare(R"(
|
||
SELECT * FROM transactions
|
||
WHERE account_id = :account_id AND bank_name = :bank_name
|
||
AND bank_time = :bank_time AND amount = :amount
|
||
LIMIT 1
|
||
)");
|
||
query.bindValue(":account_id", accountId);
|
||
query.bindValue(":bank_name", bankName);
|
||
query.bindValue(":bank_time", bankTime);
|
||
query.bindValue(":amount", amount);
|
||
|
||
if (!query.exec() || !query.next()) {
|
||
return {};
|
||
}
|
||
return parseTransaction(query);
|
||
}
|