439 lines
15 KiB
C++
439 lines
15 KiB
C++
#include "filterform.h"
|
|
|
|
#include "recordedit.h"
|
|
#include "ui_filterform.h"
|
|
#include <QDesktopServices>
|
|
#include <QDir>
|
|
#include <QMessageBox>
|
|
#include <QRegularExpressionValidator>
|
|
#include <QShowEvent>
|
|
#include <algorithm>
|
|
#include <unordered_map>
|
|
#include <QDateTime>
|
|
|
|
FilterForm::FilterForm(QWidget* parent, std::unique_ptr<ACTSqlOpr>& sqlOpr, std::unique_ptr<ComSqlOpr>& comSqlOpr,
|
|
std::unique_ptr<RepaySqlOpr>& repaySqlOpr)
|
|
: QDialog(parent), ui(new Ui::FilterForm), sqlOpr_(sqlOpr), comSqlOpr_(comSqlOpr), repaySqlOpr_(repaySqlOpr)
|
|
{
|
|
ui->setupUi(this);
|
|
statistic_ = std::make_shared<Statistic>(repaySqlOpr_);
|
|
Init();
|
|
setWindowTitle("结果");
|
|
}
|
|
|
|
FilterForm::~FilterForm()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
int FilterForm::exec()
|
|
{
|
|
ShowResult();
|
|
if (!over_) {
|
|
return QDialog::Rejected;
|
|
}
|
|
return QDialog::exec();
|
|
}
|
|
|
|
void FilterForm::Init()
|
|
{
|
|
auto* lay = new QVBoxLayout(this);
|
|
tw_ = new QTableWidget(this);
|
|
|
|
QStringList headers;
|
|
headers << "ID" << "类型" << "分类" << "金额" << "日期" << "内容" << "备注";
|
|
|
|
tw_->setColumnCount(headers.size());
|
|
tw_->setHorizontalHeaderLabels(headers);
|
|
tw_->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
|
|
tw_->setColumnWidth(0, 50);
|
|
tw_->setColumnWidth(1, 100);
|
|
tw_->setColumnWidth(2, 100);
|
|
tw_->setColumnWidth(3, 100);
|
|
tw_->setColumnWidth(4, 150);
|
|
tw_->setColumnWidth(5, 300);
|
|
tw_->setColumnWidth(6, 100);
|
|
|
|
lay->addWidget(tw_);
|
|
ui->widget->setLayout(lay);
|
|
|
|
ui->edCashIn->setReadOnly(true);
|
|
ui->edCashOut->setReadOnly(true);
|
|
ui->edCreditIn->setReadOnly(true);
|
|
ui->edCreditOut->setReadOnly(true);
|
|
ui->edCreditCash->setReadOnly(true);
|
|
|
|
tw_->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
connect(tw_, &QTableWidget::customContextMenuRequested, this, &FilterForm::ShowContextMenu);
|
|
}
|
|
|
|
void FilterForm::ShowResult()
|
|
{
|
|
over_ = false;
|
|
AccountRecordList list;
|
|
if (!sqlOpr_->GetAccountList(list)) {
|
|
QMessageBox::warning(this, "错误", QString::fromStdString(sqlOpr_->GetLastErr()));
|
|
return;
|
|
}
|
|
|
|
result_.clear();
|
|
if (!Filter(list, result_)) {
|
|
QMessageBox::warning(this, "错误", "筛选失败");
|
|
return;
|
|
}
|
|
|
|
if (result_.empty()) {
|
|
QMessageBox::warning(this, "提示", "没有符合条件的记录");
|
|
return;
|
|
}
|
|
|
|
for (const auto& item : result_) {
|
|
tw_->insertRow(tw_->rowCount());
|
|
int row = tw_->rowCount() - 1;
|
|
|
|
auto* i1 = new QTableWidgetItem(QString::number(item.id));
|
|
i1->setFlags(i1->flags() & ~Qt::ItemIsEditable);
|
|
|
|
auto* i2 = new QTableWidgetItem(QString::fromStdString(item.payType));
|
|
i2->setFlags(i2->flags() & ~Qt::ItemIsEditable);
|
|
|
|
auto* i3 = new QTableWidgetItem(QString::fromStdString(item.classify));
|
|
i3->setFlags(i3->flags() & ~Qt::ItemIsEditable);
|
|
|
|
auto* i4 = new QTableWidgetItem(QString::number(item.money / 100.0));
|
|
i4->setFlags(i4->flags() & ~Qt::ItemIsEditable);
|
|
|
|
auto* i5 = new QTableWidgetItem(QString::fromStdString(item.dt));
|
|
i5->setFlags(i5->flags() & ~Qt::ItemIsEditable);
|
|
|
|
auto* i6 = new QTableWidgetItem(QString::fromStdString(item.thing));
|
|
i6->setFlags(i6->flags() & ~Qt::ItemIsEditable);
|
|
|
|
auto* i7 = new QTableWidgetItem(QString::fromStdString(item.remark));
|
|
i7->setFlags(i7->flags() & ~Qt::ItemIsEditable);
|
|
|
|
tw_->setItem(row, 0, i1);
|
|
tw_->setItem(row, 1, i2);
|
|
tw_->setItem(row, 2, i3);
|
|
tw_->setItem(row, 3, i4);
|
|
tw_->setItem(row, 4, i5);
|
|
tw_->setItem(row, 5, i6);
|
|
tw_->setItem(row, 6, i7);
|
|
}
|
|
CalcShow();
|
|
over_ = true;
|
|
}
|
|
|
|
void FilterForm::CalcShow()
|
|
{
|
|
statistic_->Calculate(result_);
|
|
auto* d = SharedData::instance();
|
|
ui->edCashIn->setText(QString::number(d->ttCashIn_ / 100.0));
|
|
ui->edCashOut->setText(QString::number(d->ttCashOut_ / 100.0));
|
|
ui->edCreditIn->setText(QString::number(d->ttCreditIn_ / 100.0));
|
|
ui->edCreditOut->setText(QString::number(d->ttCreditOut_ / 100.0));
|
|
ui->edCreditCash->setText(QString::number(d->ttCreditCash_ / 100.0));
|
|
ui->edCreditPay->setText(QString::number(d->ttCashPay_ / 100.0));
|
|
|
|
auto ret1 = GetClassifyCash(result_, true);
|
|
auto ret2 = GetClassifyCash(result_, false);
|
|
|
|
ui->lwCashCl->clear();
|
|
for (const auto& item : ret1) {
|
|
ui->lwCashCl->addItem(QString("%1 => %2/%3")
|
|
.arg(QString::fromStdString(item.first))
|
|
.arg(QString::number(item.second.first / 100.0))
|
|
.arg(QString::number(item.second.second / 100.0)));
|
|
}
|
|
ui->lwCreditCl->clear();
|
|
for (const auto& item : ret2) {
|
|
ui->lwCreditCl->addItem(QString("%1 => %2/%3")
|
|
.arg(QString::fromStdString(item.first))
|
|
.arg(QString::number(item.second.first / 100.0))
|
|
.arg(QString::number(item.second.second / 100.0)));
|
|
}
|
|
}
|
|
|
|
int32_t FilterForm::rePayValue(int accID)
|
|
{
|
|
RepayRecordList reuslt;
|
|
if (!repaySqlOpr_->GetRepayResult(reuslt, accID)) {
|
|
return 0;
|
|
}
|
|
int32_t sum = 0;
|
|
for (const auto& item : reuslt) {
|
|
sum += item.money;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
bool FilterForm::Filter(const AccountRecordList& list, AccountRecordList& result)
|
|
{
|
|
auto* d = SharedData::instance();
|
|
result.clear();
|
|
for (const auto& item : list) {
|
|
if (d->flType) {
|
|
if (item.payType != d->type.toStdString()) {
|
|
continue;
|
|
}
|
|
}
|
|
if (d->flKeys) {
|
|
auto qkeys = d->key.toUpper();
|
|
auto skeys = QString::fromStdString(item.thing).toUpper();
|
|
if (!skeys.contains(qkeys)) {
|
|
continue;
|
|
}
|
|
}
|
|
switch (d->filter) {
|
|
case CashFilter::FIL_BETWEEN: {
|
|
if (item.money < d->lowMoney || item.money > d->highMoney) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case CashFilter::FIL_HIGHER: {
|
|
if (item.money <= d->highMoney) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
case CashFilter::FIL_LOWER: {
|
|
if (item.money >= d->lowMoney) {
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
if (d->flClassify) {
|
|
auto qkeys = d->classify.toUpper();
|
|
auto skeys = QString::fromStdString(item.classify).toUpper();
|
|
if (!skeys.contains(qkeys)) {
|
|
continue;
|
|
}
|
|
}
|
|
if (d->flDays) {
|
|
auto days = d->days;
|
|
QDateTime datetime = QDateTime::fromString(QString::fromStdString(item.dt), "yyyy-MM-dd hh:mm:ss");
|
|
QDateTime now = QDateTime::currentDateTime();
|
|
if (datetime.daysTo(now) > days) {
|
|
continue;
|
|
}
|
|
}
|
|
result.push_back(item);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::pair<uint32_t, uint32_t>>> FilterForm::GetClassifyCash(const AccountRecordList& list,
|
|
bool isCash)
|
|
{
|
|
std::vector<std::pair<std::string, std::pair<uint32_t, uint32_t>>> ret;
|
|
std::unordered_map<std::string, std::pair<uint32_t, uint32_t>> classifyMap;
|
|
for (const auto& item : list) {
|
|
if (isCash) {
|
|
if (item.payType == "现金支出") {
|
|
if (!classifyMap.count(item.classify)) {
|
|
classifyMap[item.classify] = {0, 0};
|
|
}
|
|
classifyMap[item.classify].first += item.money;
|
|
} else if (item.payType == "现金收入") {
|
|
if (!classifyMap.count(item.classify)) {
|
|
classifyMap[item.classify] = {0, 0};
|
|
}
|
|
classifyMap[item.classify].second += item.money;
|
|
}
|
|
} else {
|
|
if (item.payType == "信用支出") {
|
|
if (!classifyMap.count(item.classify)) {
|
|
classifyMap[item.classify] = {0, 0};
|
|
}
|
|
classifyMap[item.classify].first += item.money;
|
|
} else if (item.payType == "信用收入") {
|
|
if (!classifyMap.count(item.classify)) {
|
|
classifyMap[item.classify] = {0, 0};
|
|
}
|
|
classifyMap[item.classify].second += item.money;
|
|
} else if (item.payType == "信用借款") {
|
|
// 信用借款
|
|
if (!classifyMap.count(item.classify)) {
|
|
classifyMap[item.classify] = {0, 0};
|
|
}
|
|
classifyMap[item.classify].first += item.money;
|
|
}
|
|
}
|
|
}
|
|
for (const auto& item : classifyMap) {
|
|
ret.emplace_back(item.first, item.second);
|
|
}
|
|
std::sort(ret.begin(), ret.end(),
|
|
[](const std::pair<std::string, std::pair<uint32_t, uint32_t>>& a,
|
|
const std::pair<std::string, std::pair<uint32_t, uint32_t>>& b) {
|
|
auto at = a.second.first + a.second.second;
|
|
auto bt = b.second.first + b.second.second;
|
|
return at > bt;
|
|
});
|
|
return ret;
|
|
}
|
|
void FilterForm::ShowContextMenu(const QPoint& pos)
|
|
{
|
|
QList<QTableWidgetItem*> selectedItems = tw_->selectedItems();
|
|
if (selectedItems.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QMenu menu(this);
|
|
|
|
if (selectedItems.size() != 7) {
|
|
return;
|
|
}
|
|
QAction* acFile = menu.addAction("查看附件");
|
|
int id = selectedItems[0]->text().toInt();
|
|
QString fileName;
|
|
|
|
for (const auto& item : result_) {
|
|
if (item.id == id) {
|
|
fileName = QString::fromStdString(item.additionFile);
|
|
break;
|
|
}
|
|
}
|
|
TransMenu(id, menu, selectedItems[1]);
|
|
connect(acFile, &QAction::triggered, [this, selectedItems, id, fileName]() {
|
|
if (fileName.isEmpty()) {
|
|
QMessageBox::warning(this, "警告", "附件文件名为空");
|
|
return;
|
|
}
|
|
QString mediaDir = QString::fromStdString(Util::GetMediaDir());
|
|
QString filePath = QDir(mediaDir).filePath(fileName);
|
|
if (!QFile::exists(filePath)) {
|
|
QMessageBox::critical(this, "错误", QString("文件不存在: %1").arg(filePath));
|
|
return;
|
|
}
|
|
if (!QDesktopServices::openUrl(QUrl::fromLocalFile(filePath))) {
|
|
QMessageBox::critical(nullptr, "错误", "无法打开图片文件");
|
|
}
|
|
});
|
|
QAction* acEdit = menu.addAction("编辑");
|
|
connect(acEdit, &QAction::triggered, [this, id, selectedItems]() {
|
|
RecordEdit* re = new RecordEdit(this, comSqlOpr_, repaySqlOpr_);
|
|
AccountRecord* recPtr = nullptr;
|
|
for (auto& item : result_) {
|
|
if (item.id == id) {
|
|
re->record_ = item;
|
|
recPtr = &item;
|
|
break;
|
|
}
|
|
}
|
|
re->exec();
|
|
|
|
// headers << "ID" << "类型" << "分类" << "金额" << "日期" << "内容" << "备注";
|
|
if (!re->modify_) {
|
|
return;
|
|
}
|
|
|
|
if (!sqlOpr_->UpdateAccount(re->record_)) {
|
|
QMessageBox::warning(this, "错误", QString::fromStdString(sqlOpr_->GetLastErr()));
|
|
return;
|
|
}
|
|
|
|
selectedItems[1]->setText(QString::fromStdString(re->record_.payType));
|
|
selectedItems[2]->setText(QString::fromStdString(re->record_.classify));
|
|
selectedItems[3]->setText(QString::number(re->record_.money / 100.0));
|
|
selectedItems[4]->setText(QString::fromStdString(re->record_.dt));
|
|
selectedItems[5]->setText(QString::fromStdString(re->record_.thing));
|
|
selectedItems[6]->setText(QString::fromStdString(re->record_.remark));
|
|
|
|
*recPtr = re->record_;
|
|
CalcShow();
|
|
});
|
|
|
|
QAction* acDelete = menu.addAction("删除");
|
|
connect(acDelete, &QAction::triggered, [this, id, selectedItems]() {
|
|
if (QMessageBox::question(this, "确认删除", "确定删除记录吗?") != QMessageBox::Yes) {
|
|
return;
|
|
}
|
|
|
|
if (!sqlOpr_->DeleteAccount(id)) {
|
|
QMessageBox::warning(this, "错误", QString::fromStdString(sqlOpr_->GetLastErr()));
|
|
return;
|
|
}
|
|
|
|
// 从结果列表中移除记录
|
|
for (auto it = result_.begin(); it != result_.end(); ++it) {
|
|
if (it->id == id) {
|
|
result_.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 从表格中移除行
|
|
tw_->removeRow(selectedItems[0]->row());
|
|
CalcShow();
|
|
});
|
|
|
|
menu.exec(tw_->viewport()->mapToGlobal(pos));
|
|
}
|
|
|
|
void FilterForm::TransMenu(int id, QMenu& menu, QTableWidgetItem* item)
|
|
{
|
|
QString payType;
|
|
for (const auto& item : result_) {
|
|
if (item.id == id) {
|
|
payType = QString::fromStdString(item.payType);
|
|
break;
|
|
}
|
|
}
|
|
if (payType.isEmpty()) {
|
|
QMessageBox::warning(this, "警告", QString("未找到ID为%1的支付类型。").arg(id));
|
|
return;
|
|
}
|
|
|
|
// 根据支付类型执行不同操作
|
|
QAction* acFile = nullptr;
|
|
QString pur;
|
|
if (payType == "现金支出") {
|
|
acFile = menu.addAction("转为信用支出");
|
|
pur = "信用支出";
|
|
} else if (payType == "现金收入") {
|
|
acFile = menu.addAction("转为信用收入");
|
|
pur = "信用收入";
|
|
} else if (payType == "信用支出") {
|
|
acFile = menu.addAction("转为现金支出");
|
|
pur = "现金支出";
|
|
} else if (payType == "信用收入") {
|
|
acFile = menu.addAction("转为现金收入");
|
|
pur = "现金收入";
|
|
}
|
|
|
|
if (acFile == nullptr) {
|
|
return;
|
|
}
|
|
|
|
connect(acFile, &QAction::triggered, [this, id, pur, item]() {
|
|
// 执行支付类型转换逻辑
|
|
if (QMessageBox::question(this, "确认转换", QString("确定将ID为%1的记录转为%2吗?").arg(id).arg(pur),
|
|
QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
|
|
// 1. 更新数据库中的支付类型
|
|
bool done = false;
|
|
for (auto& item : result_) {
|
|
if (item.id != id) {
|
|
continue;
|
|
}
|
|
item.payType = pur.toStdString();
|
|
if (!sqlOpr_->UpdateAccount(item)) {
|
|
QMessageBox::critical(this, "错误", "更新数据库失败");
|
|
return;
|
|
}
|
|
done = true;
|
|
break;
|
|
}
|
|
if (done) {
|
|
item->setText(pur);
|
|
CalcShow();
|
|
QMessageBox::information(this, "成功", QString("ID为%1的记录已成功转为%2。").arg(id).arg(pur));
|
|
}
|
|
}
|
|
});
|
|
} |