Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(#5685): add charts to Transaction Report #6440

Merged
merged 3 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion src/filtertransdialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ static const wxString COLUMN_NAMES[] = { "ID", "Color", "Date", "Number", "Accou
static const wxString TRANSACTION_STATUSES[] = { wxTRANSLATE("Unreconciled"), wxTRANSLATE("Reconciled"), wxTRANSLATE("Void"),
wxTRANSLATE("Follow Up"), wxTRANSLATE("Duplicate"), wxTRANSLATE("All Except Reconciled") };

static const wxString GROUPBY_OPTIONS[] = { wxTRANSLATE("Account"), wxTRANSLATE("Payee"), wxTRANSLATE("Category"), wxTRANSLATE("Type") };
static const wxString GROUPBY_OPTIONS[] = { wxTRANSLATE("Account"), wxTRANSLATE("Payee"), wxTRANSLATE("Category"), wxTRANSLATE("Type"),
wxTRANSLATE("Day"), wxTRANSLATE("Month"), wxTRANSLATE("Year") };

static const wxString CHART_OPTIONS[] = { wxTRANSLATE("Bar"), wxTRANSLATE("Line"), wxTRANSLATE("Line DateTime"),
wxTRANSLATE("Pie"), wxTRANSLATE("Donut"), wxTRANSLATE("Radar"),
wxTRANSLATE("Bar Line"), wxTRANSLATE("Stacked Bar Line"), wxTRANSLATE("Stacked Area")};
// Keep options aligned with HtmlBuilder GraphData::type

// Used to determine if we need to refresh the tag text ctrl after
// accelerator hints are shown which only occurs once.
Expand Down Expand Up @@ -440,6 +446,14 @@ void mmFilterTransactionsDialog::mmDoDataToControls(const wxString& json)
bGroupBy_->Enable(groupByCheckBox_->IsChecked() && isReportMode_);
bGroupBy_->SetStringSelection(s_groupBy);

// Chart
Value& j_chart = GetValueByPointerWithDefault(j_doc, "/CHART", "");
const wxString& s_chart = j_chart.IsString() ? wxString::FromUTF8(j_chart.GetString()) : "";
chartCheckBox_->SetValue(!s_chart.empty());
bChart_->Enable(chartCheckBox_->IsChecked() && isReportMode_);
bChart_->SetStringSelection(s_chart);

// Combine splits
Value& j_combineSplits = GetValueByPointerWithDefault(j_doc, "/COMBINE_SPLITS", "");
const bool& b_combineSplits = j_combineSplits.IsBool() ? j_combineSplits.GetBool() : false;
combineSplitsCheckBox_->SetValue(b_combineSplits);
Expand Down Expand Up @@ -704,6 +718,18 @@ void mmFilterTransactionsDialog::mmDoCreateControls()
presPanelSizer->Add(bGroupBy_, g_flagsExpand);
mmToolTip(bGroupBy_, _("Specify how the report should be grouped"));

// Chart
chartCheckBox_ = new wxCheckBox(presPanel, wxID_ANY, _("Chart"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE);
presPanelSizer->Add(chartCheckBox_, g_flagsH);

bChart_ = new wxChoice(presPanel, wxID_ANY);
for (const auto& i : CHART_OPTIONS)
{
bChart_->Append(wxGetTranslation(i), new wxStringClientData(i));
}
presPanelSizer->Add(bChart_, g_flagsExpand);
mmToolTip(bChart_, _("Specify which chart will be included in the report"));

// Compress Splits
combineSplitsCheckBox_ = new wxCheckBox(presPanel, wxID_ANY, _("Combine Splits"), wxDefaultPosition, wxDefaultSize, wxCHK_2STATE);
combineSplitsCheckBox_->SetMinSize(wxSize(-1, bGroupBy_->GetSize().GetHeight()));
Expand Down Expand Up @@ -789,10 +815,14 @@ void mmFilterTransactionsDialog::mmDoCreateControls()
{
groupByCheckBox_->Disable();
bGroupBy_->Disable();
chartCheckBox_->Disable();
bChart_->Disable();
showColumnsCheckBox_->Disable();
bHideColumns_->Disable();
groupByCheckBox_->Hide();
bGroupBy_->Hide();
chartCheckBox_->Hide();
bChart_->Hide();
showColumnsCheckBox_->Hide();
bHideColumns_->Hide();
combineSplitsCheckBox_->Hide();
Expand Down Expand Up @@ -877,6 +907,7 @@ void mmFilterTransactionsDialog::OnCheckboxClick(wxCommandEvent& event)
colorButton_->Enable(colorCheckBox_->IsChecked());
bHideColumns_->Enable(showColumnsCheckBox_->IsChecked());
bGroupBy_->Enable(groupByCheckBox_->IsChecked() && isReportMode_);
bChart_->Enable(chartCheckBox_->IsChecked() && isReportMode_);

event.Skip();
}
Expand Down Expand Up @@ -1036,6 +1067,12 @@ bool mmFilterTransactionsDialog::mmIsValuesCorrect() const
return false;
}

if (chartCheckBox_->IsChecked() && bChart_->GetSelection() == wxNOT_FOUND)
{
mmErrorDialogs::ToolTip4Object(bChart_, _("Invalid value"), _("Chart"), wxICON_ERROR);
return false;
}

if (!m_custom_fields->ValidateCustomValues(0))
{
return false;
Expand Down Expand Up @@ -1855,6 +1892,17 @@ const wxString mmFilterTransactionsDialog::mmGetJsonSetings(bool i18n) const
}
}

// Chart
if (chartCheckBox_->IsChecked())
{
const wxString chart = bChart_->GetStringSelection();
if (!chart.empty())
{
json_writer.Key((i18n ? _("Chart") : "CHART").utf8_str());
json_writer.String(chart.utf8_str());
}
}

// Compress Splits
const bool combineSplits = combineSplitsCheckBox_->IsChecked();
if (combineSplits)
Expand Down Expand Up @@ -1950,6 +1998,14 @@ int mmFilterTransactionsDialog::mmGetGroupBy() const
return by;
}

int mmFilterTransactionsDialog::mmGetChart() const
{
int by = -1;
if (chartCheckBox_->IsChecked())
by = bChart_->GetSelection();
return by;
}

void mmFilterTransactionsDialog::mmDoResetFilterStatus()
{
// m_custom_fields->ResetWidgetsChanged();
Expand Down
9 changes: 8 additions & 1 deletion src/filtertransdialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,15 @@ class mmFilterTransactionsDialog: public wxDialog
GROUPBY_ACCOUNT,
GROUPBY_PAYEE,
GROUPBY_CATEGORY,
GROUPBY_TYPE
GROUPBY_TYPE,
GROUPBY_DAY,
GROUPBY_MONTH,
GROUPBY_YEAR
};
int mmGetGroupBy() const;

int mmGetChart() const;

const wxArrayInt mmGetAccountsID() const;
const wxArrayInt mmGetHideColumnsID() const;

Expand Down Expand Up @@ -203,6 +208,8 @@ class mmFilterTransactionsDialog: public wxDialog
wxButton* bHideColumns_ = nullptr;
wxCheckBox* groupByCheckBox_ = nullptr;
wxChoice* bGroupBy_ = nullptr;
wxCheckBox* chartCheckBox_ = nullptr;
wxChoice* bChart_ = nullptr;
wxCheckBox* combineSplitsCheckBox_ = nullptr;
private:
wxString m_settings_json;
Expand Down
3 changes: 2 additions & 1 deletion src/reports/htmlbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ struct GraphSeries
struct GraphData
{
wxString title;
enum { BAR = 0, LINE, LINE_DATETIME, PIE, DONUT, RADAR, BARLINE, STACKEDBARLINE, STACKEDAREA } type;
enum GraphType { BAR = 0, LINE, LINE_DATETIME, PIE, DONUT, RADAR, BARLINE, STACKEDBARLINE, STACKEDAREA } type;
//Keep type aligned in FilterTransDialog CHART_OPTIONS
std::vector<wxString> labels;
std::vector<GraphSeries> series;
std::vector<wxColour> colors;
Expand Down
56 changes: 55 additions & 1 deletion src/reports/transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "util.h"
#include "model/allmodel.h"
#include <algorithm>
#include <numeric>
#include <vector>
#include <float.h>

Expand Down Expand Up @@ -127,6 +128,7 @@ table {
m_noOfCols = (m_transDialog->mmIsHideColumnsChecked()) ? m_transDialog->mmGetHideColumnsID().GetCount() : 11;
const wxString& AttRefType = Model_Attachment::reftype_desc(Model_Attachment::TRANSACTION);
const int groupBy = m_transDialog->mmGetGroupBy();
const int chart = m_transDialog->mmGetChart();
wxString lastSortLabel = "";

std::map<int, double> total; //Store transaction amount with original currency
Expand All @@ -135,6 +137,7 @@ table {
std::map<int, double> grand_total_in_base_curr; //Grad - Store transactions amount daily converted to base currency
std::map<int, double> grand_total_extrans; //Grand - Store transaction amount with original currency - excluding TRANSFERS
std::map<int, double> grand_total_in_base_curr_extrans; //Grand - Store transactions amount daily converted to base currency - excluding TRANSFERS
std::map<wxString, double> values_chart; // Store grouped values for chart

const wxString RefType = Model_Attachment::reftype_desc(Model_Attachment::TRANSACTION);
Model_CustomField::FIELDTYPE UDFC01_Type = Model_CustomField::getUDFCType(RefType, "UDFC01");
Expand All @@ -161,6 +164,12 @@ table {
sortLabel = transaction.CATEGNAME;
else if (groupBy == mmFilterTransactionsDialog::GROUPBY_TYPE)
sortLabel = wxGetTranslation(transaction.TRANSCODE);
else if (groupBy == mmFilterTransactionsDialog::GROUPBY_DAY)
sortLabel = mmGetDateForDisplay(transaction.TRANSDATE);
else if (groupBy == mmFilterTransactionsDialog::GROUPBY_MONTH)
sortLabel = Model_Checking::TRANSDATE(transaction).Format("%Y-%m");
else if (groupBy == mmFilterTransactionsDialog::GROUPBY_YEAR)
sortLabel = Model_Checking::TRANSDATE(transaction).Format("%Y");

if (sortLabel != lastSortLabel)
{
Expand All @@ -174,12 +183,19 @@ table {
hb.endTbody();
hb.endTable();
hb.endDiv();

if (chart > -1)
{
double value_chart = std::accumulate(total_in_base_curr.begin(), total_in_base_curr.end(), 0,
[](const double previous, decltype(*total_in_base_curr.begin()) p) { return previous + p.second; });
values_chart[lastSortLabel] += value_chart;
}
total.clear();
total_in_base_curr.clear();
}
hb.addDivContainer("shadow");
if (groupBy > -1)
hb.addHeader(2, sortLabel);
hb.addHeader(3, sortLabel);
hb.startSortTable();
hb.startThead();
hb.startTableRow();
Expand Down Expand Up @@ -287,6 +303,10 @@ table {
grand_total_extrans[curr->CURRENCYID] += amount;
grand_total_in_base_curr_extrans[curr->CURRENCYID] += amount * convRate;
}
if (chart > -1 && groupBy == -1)
{
values_chart[std::to_string(transaction.TRANSID)] += (amount * convRate);
}
}
else
{
Expand Down Expand Up @@ -378,11 +398,18 @@ table {
hb.startTable();
hb.startTbody();
displayTotals(total, total_in_base_curr, m_noOfCols);
if (chart > -1)
{
double value_chart = std::accumulate(total_in_base_curr.begin(), total_in_base_curr.end(), 0,
[](const double previous, decltype(*total_in_base_curr.begin()) p) { return previous + p.second; });
values_chart[lastSortLabel] += value_chart;
}
}
hb.endTbody();
hb.endTable();
hb.endDiv();

// Totals box
hb.addDivContainer("shadow");
{
hb.startTable();
Expand Down Expand Up @@ -426,6 +453,24 @@ table {
hb.endTable();
}
hb.endDiv();

// Chart
if (chart > -1 && values_chart.size() > 0)
{
GraphData gd;
GraphSeries gs;
for (const auto& kv : values_chart)
{
gd.labels.push_back(kv.first);
gs.values.push_back(kv.second);
}
gd.series.push_back(gs);
//gd.colors = { mmThemeMetaColour(meta::COLOR_REPORT_DELTA) };
gd.type = static_cast<GraphData::GraphType>(chart);
hb.addChart(gd);
}

// Filters recap
hb.addDivContainer("shadow");
{
m_transDialog->mmGetDescription(hb);
Expand Down Expand Up @@ -503,6 +548,15 @@ void mmReportTransactions::Run(wxSharedPtr<mmFilterTransactionsDialog>& dlg)
case mmFilterTransactionsDialog::GROUPBY_TYPE:
std::stable_sort(trans_.begin(), trans_.end(), SorterByTRANSCODE());
break;
case mmFilterTransactionsDialog::GROUPBY_DAY:
std::stable_sort(trans_.begin(), trans_.end(), SorterByTRANSDATE());
break;
case mmFilterTransactionsDialog::GROUPBY_MONTH:
std::stable_sort(trans_.begin(), trans_.end(), SorterByTRANSDATE());
break;
case mmFilterTransactionsDialog::GROUPBY_YEAR:
std::stable_sort(trans_.begin(), trans_.end(), SorterByTRANSDATE());
break;
}
}

Expand Down