1
0
forked from BRT/arc
arc/database/dao/TransactionDAO.cpp
trnsmkot 04025aa32b 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.
2026-03-08 14:20:12 +07:00

420 lines
15 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <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);
}