1 /*******************************************************
2  Copyright (C) 2006 Madhan Kanagavel
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  ********************************************************/
18 
19 #include "categexp.h"
20 #include "budget.h"
21 #include "htmlbuilder.h"
22 
23 #include "htmlbuilder.h"
24 #include "mmOption.h"
25 #include <algorithm>
26 #include "model/Model_Category.h"
27 
28 #define CATEGORY_SORT_BY_NAME        1
29 #define CATEGORY_SORT_BY_AMOUNT      2
30 
mmReportCategoryExpenses(mmDateRange * date_range,const wxString & title,int type)31 mmReportCategoryExpenses::mmReportCategoryExpenses
32 (mmDateRange* date_range, const wxString& title, int type)
33 : mmPrintableBase("mmReportCategoryExpenses")
34 , date_range_(date_range)
35 , title_(title)
36 , type_(type)
37 , ignoreFutureDate_(mmIniOptions::instance().ignoreFutureTransactions_)
38 , with_date_(false)
39 {
40     with_date_ = date_range_->is_with_date();
41 }
42 
~mmReportCategoryExpenses()43 mmReportCategoryExpenses::~mmReportCategoryExpenses()
44 {
45     if(date_range_)
46         delete date_range_;
47 }
48 
RefreshData()49 void  mmReportCategoryExpenses::RefreshData()
50 {
51     data_.clear();
52     wxString color;
53     std::map<int, std::map<int, std::map<int, double> > > categoryStats;
54     Model_Category::instance().getCategoryStats(categoryStats
55         , date_range_
56         , ignoreFutureDate_
57         , false
58         , with_date_);
59 
60     data_holder line;
61     int i = 0;
62     mmHTMLBuilder hb;
63     int groupID = 1;
64     for (const auto& category : Model_Category::instance().all(Model_Category::COL_CATEGNAME))
65     {
66         const wxString& sCategName = category.CATEGNAME;
67         double amt = categoryStats[category.CATEGID][-1][0];
68         if (type_ == GOES && amt < 0.0) amt = 0;
69         if (type_ == COME && amt > 0.0) amt = 0;
70         if (amt != 0.0)
71             data_.push_back({ hb.getColor(i++), sCategName, amt, groupID });
72 
73         auto subcategories = Model_Category::sub_category(category);
74         std::stable_sort(subcategories.begin(), subcategories.end(), SorterBySUBCATEGNAME());
75         for (const auto& sub_category : subcategories)
76         {
77             wxString sFullCategName = Model_Category::full_name(category.CATEGID, sub_category.SUBCATEGID);
78             amt = categoryStats[category.CATEGID][sub_category.SUBCATEGID][0];
79             if (type_ == GOES && amt < 0.0) amt = 0;
80             if (type_ == COME && amt > 0.0) amt = 0;
81             if (amt != 0.0)
82                 data_.push_back({ hb.getColor(i++), sFullCategName, amt, groupID });
83         }
84         groupID++;
85     }
86 }
87 
getHTMLText()88 wxString mmReportCategoryExpenses::getHTMLText()
89 {
90     RefreshData();
91     valueList_.clear();
92     // Data is presorted by name
93     std::vector<data_holder> sortedData(data_);
94 
95     std::map <int, int> group_counter;
96     std::map <int, double> group_total;
97     for (const auto& entry : sortedData)
98     {
99         group_counter[entry.categs]++;
100         group_total[entry.categs] += entry.amount;
101         group_total[-1] += entry.amount < 0 ? entry.amount : 0;
102         group_total[-2] += entry.amount > 0 ? entry.amount : 0;
103         if (type_ != NONE) valueList_.push_back({ entry.color, entry.name, entry.amount });
104     }
105 
106     std::stable_sort(valueList_.begin(), valueList_.end()
107         , [](const ValueTrio& x, const ValueTrio& y)
108         {
109         if (x.amount != y.amount)
110                 return fabs(x.amount) > fabs(y.amount);
111             else
112                 return x.label < y.label;
113         }
114     );
115 
116     mmHTMLBuilder hb;
117     hb.init();
118     hb.addDivContainer();
119     hb.addHeader(2, title_);
120     hb.DisplayDateHeading(date_range_->start_date(), date_range_->end_date(), with_date_);
121 
122     hb.addDivRow();
123     hb.addDivCol17_67();
124 
125     // Add the graph
126     hb.addDivCol25_50();
127     if (type_ != NONE && !valueList_.empty())
128         hb.addPieChart(valueList_, "Categories");
129     hb.endDiv();
130 
131     hb.startTable();
132     hb.startThead();
133     hb.startTableRow();
134     if (type_ != NONE) hb.addTableHeaderCell(" ");
135     hb.addTableHeaderCell(_("Category"));
136     hb.addTableHeaderCell(_("Amount"), true);
137     hb.addTableHeaderCell(_("Total"), true);
138     hb.endTableRow();
139     hb.endThead();
140 
141     hb.startTbody();
142     int group = 0;
143     for (const auto& entry : sortedData)
144     {
145         group++;
146         hb.startTableRow();
147         if (type_ != NONE) hb.addColorMarker(entry.color);
148         hb.addTableCell(entry.name);
149         hb.addMoneyCell(entry.amount);
150         if (group_counter[entry.categs] > 1)
151             hb.addTableCell("");
152         else
153             hb.addMoneyCell(entry.amount);
154         hb.endTableRow();
155 
156         if (group_counter[entry.categs] == group && group_counter[entry.categs] > 1)
157         {
158             group = 0;
159             hb.startTableRow();
160             if (type_ != NONE) hb.addTableCell("");
161             hb.addTableCell(_("Category Total: "));
162             hb.addTableCell("");
163             hb.addMoneyCell(group_total[entry.categs]);
164             hb.endTableRow();
165         }
166         if (group_counter[entry.categs] == 1 || group == 0) {
167 			group = 0;
168 		}
169     }
170     hb.endTbody();
171 
172     int span = (type_ != NONE) ? 4 : 3;
173     hb.startTfoot();
174     if (type_ == NONE)
175     {
176         hb.addTotalRow(_("Total Expenses: "), span, group_total[-1]);
177         hb.addTotalRow(_("Total Income: "), span, group_total[-2]);
178     }
179     hb.addTotalRow(_("Grand Total: "), span, group_total[-1] + group_total[-2]);
180     hb.endTfoot();
181 
182     hb.endTable();
183     hb.endDiv();
184     hb.endDiv();
185     hb.endDiv();
186     hb.end();
187 
188     return hb.getHTMLText();
189 }
190 
mmReportCategoryExpensesGoes(mmDateRange * date_range,const wxString & title)191 mmReportCategoryExpensesGoes::mmReportCategoryExpensesGoes
192 ( mmDateRange* date_range, const wxString& title)
193 : mmReportCategoryExpenses(date_range, title, 2)
194 {}
195 
mmReportCategoryExpensesGoesCurrentMonth()196 mmReportCategoryExpensesGoesCurrentMonth::mmReportCategoryExpensesGoesCurrentMonth
197 ( )
198 : mmReportCategoryExpensesGoes(new mmCurrentMonth()
199 , wxString::Format(_("Where the Money Goes - %s"), _("Current Month")))
200 {}
201 
mmReportCategoryExpensesGoesCurrentMonthToDate()202 mmReportCategoryExpensesGoesCurrentMonthToDate::mmReportCategoryExpensesGoesCurrentMonthToDate
203 ( )
204 : mmReportCategoryExpensesGoes(new mmCurrentMonthToDate()
205 , wxString::Format(_("Where the Money Goes - %s"), _("Current Month to Date")))
206 {}
207 
mmReportCategoryExpensesGoesLastMonth()208 mmReportCategoryExpensesGoesLastMonth::mmReportCategoryExpensesGoesLastMonth
209 ( )
210 : mmReportCategoryExpensesGoes(new mmLastMonth()
211 , wxString::Format(_("Where the Money Goes - %s"), _("Last Month")))
212 {}
213 
mmReportCategoryExpensesGoesLast30Days()214 mmReportCategoryExpensesGoesLast30Days::mmReportCategoryExpensesGoesLast30Days
215 ( )
216 : mmReportCategoryExpensesGoes(new mmLast30Days()
217 , wxString::Format(_("Where the Money Goes - %s"), _("Last 30 Days")))
218 {}
219 
mmReportCategoryExpensesGoesLastYear()220 mmReportCategoryExpensesGoesLastYear::mmReportCategoryExpensesGoesLastYear
221 ( )
222 : mmReportCategoryExpensesGoes(new mmLastYear()
223 ,  wxString::Format(_("Where the Money Goes - %s"), _("Last Year")))
224 {}
225 
mmReportCategoryExpensesGoesCurrentYear()226 mmReportCategoryExpensesGoesCurrentYear::mmReportCategoryExpensesGoesCurrentYear()
227 : mmReportCategoryExpensesGoes(new mmCurrentYear()
228 , wxString::Format(_("Where the Money Goes - %s"), _("Current Year")))
229 {}
230 
mmReportCategoryExpensesGoesCurrentYearToDate()231 mmReportCategoryExpensesGoesCurrentYearToDate::mmReportCategoryExpensesGoesCurrentYearToDate
232 ( )
233 : mmReportCategoryExpensesGoes(new mmCurrentYearToDate()
234 , wxString::Format(_("Where the Money Goes - %s"), _("Current Year to Date")))
235 {}
236 
mmReportCategoryExpensesGoesLastFinancialYear(const int day,const int month)237 mmReportCategoryExpensesGoesLastFinancialYear::mmReportCategoryExpensesGoesLastFinancialYear
238 ( const int day, const int month)
239 : mmReportCategoryExpensesGoes(new mmLastFinancialYear(day, month)
240 , wxString::Format(_("Where the Money Goes - %s"), _("Last Financial Year")))
241 {}
242 
mmReportCategoryExpensesGoesCurrentFinancialYear(const int day,const int month)243 mmReportCategoryExpensesGoesCurrentFinancialYear::mmReportCategoryExpensesGoesCurrentFinancialYear
244 ( const int day, const int month)
245 : mmReportCategoryExpensesGoes(new mmCurrentFinancialYear(day, month)
246 , wxString::Format(_("Where the Money Goes - %s"), _("Current Financial Year")))
247 {}
248 
mmReportCategoryExpensesGoesCurrentFinancialYearToDate(const int day,const int month)249 mmReportCategoryExpensesGoesCurrentFinancialYearToDate::mmReportCategoryExpensesGoesCurrentFinancialYearToDate
250 (const int day, const int month)
251 : mmReportCategoryExpensesGoes(new mmCurrentFinancialYearToDate(day, month)
252 , wxString::Format(_("Where the Money Goes - %s"), _("Current Financial Year to Date")))
253 {}
254 
mmReportCategoryExpensesComes(mmDateRange * date_range,const wxString & title)255 mmReportCategoryExpensesComes::mmReportCategoryExpensesComes
256 (mmDateRange* date_range, const wxString& title)
257 : mmReportCategoryExpenses(date_range, title, 1)
258 {}
259 
mmReportCategoryExpensesComesCurrentMonth()260 mmReportCategoryExpensesComesCurrentMonth::mmReportCategoryExpensesComesCurrentMonth
261 ( )
262 : mmReportCategoryExpensesComes(new  mmCurrentMonth()
263 , wxString::Format(_("Where the Money Comes From - %s"), _("Current Month")))
264 {}
265 
mmReportCategoryExpensesComesCurrentMonthToDate()266 mmReportCategoryExpensesComesCurrentMonthToDate::mmReportCategoryExpensesComesCurrentMonthToDate
267 ( )
268 : mmReportCategoryExpensesComes(new mmCurrentMonthToDate()
269 , wxString::Format(_("Where the Money Comes From - %s"), _("Current Month to Date")))
270 {}
271 
mmReportCategoryExpensesComesLastMonth()272 mmReportCategoryExpensesComesLastMonth::mmReportCategoryExpensesComesLastMonth
273 ( )
274 : mmReportCategoryExpensesComes(new mmLastMonth()
275 ,  wxString::Format(_("Where the Money Comes From - %s"), _("Last Month")))
276 {}
277 
mmReportCategoryExpensesComesLast30Days()278 mmReportCategoryExpensesComesLast30Days::mmReportCategoryExpensesComesLast30Days
279 ( )
280 : mmReportCategoryExpensesComes(new mmLast30Days()
281 , wxString::Format(_("Where the Money Comes From - %s"), _("Last 30 Days")))
282 {}
283 
mmReportCategoryExpensesComesLastYear()284 mmReportCategoryExpensesComesLastYear::mmReportCategoryExpensesComesLastYear
285 ( )
286 : mmReportCategoryExpensesComes(new mmLastYear()
287 , wxString::Format(_("Where the Money Comes From - %s"), _("Last Year")))
288 {}
289 
mmReportCategoryExpensesComesCurrentYear()290 mmReportCategoryExpensesComesCurrentYear::mmReportCategoryExpensesComesCurrentYear
291 ( )
292 : mmReportCategoryExpensesComes(new mmCurrentYear()
293 , wxString::Format(_("Where the Money Comes From - %s"), _("Current Year")))
294 {}
295 
mmReportCategoryExpensesComesCurrentYearToDate()296 mmReportCategoryExpensesComesCurrentYearToDate::mmReportCategoryExpensesComesCurrentYearToDate
297 ( )
298 : mmReportCategoryExpensesComes(new mmCurrentYearToDate()
299 , wxString::Format(_("Where the Money Comes From - %s"), _("Current Year to Date")))
300 {}
301 
mmReportCategoryExpensesComesLastFinancialYear(int day,int month)302 mmReportCategoryExpensesComesLastFinancialYear::mmReportCategoryExpensesComesLastFinancialYear
303 ( int day, int month)
304 : mmReportCategoryExpensesComes(new mmLastFinancialYear(day, month)
305 , wxString::Format(_("Where the Money Comes From - %s"), _("Last Financial Year")))
306 {}
307 
mmReportCategoryExpensesComesCurrentFinancialYear(int day,int month)308 mmReportCategoryExpensesComesCurrentFinancialYear::mmReportCategoryExpensesComesCurrentFinancialYear
309 ( int day, int month)
310 : mmReportCategoryExpensesComes(new mmCurrentFinancialYear(day, month)
311 , wxString::Format(_("Where the Money Comes From - %s"), _("Current Financial Year")))
312 {}
313 
mmReportCategoryExpensesComesCurrentFinancialYearToDate(int day,int month)314 mmReportCategoryExpensesComesCurrentFinancialYearToDate::mmReportCategoryExpensesComesCurrentFinancialYearToDate
315 (int day, int month)
316 : mmReportCategoryExpensesComes(new mmCurrentFinancialYearToDate(day, month)
317 , wxString::Format(_("Where the Money Comes From - %s"), _("Current Financial Year to Date")))
318 {}
319 
mmReportCategoryExpensesCategories(mmDateRange * date_range,const wxString & title)320 mmReportCategoryExpensesCategories::mmReportCategoryExpensesCategories
321 ( mmDateRange* date_range, const wxString& title)
322 : mmReportCategoryExpenses(date_range, title, 0)
323 {}
324 
mmReportCategoryExpensesCategoriesCurrentMonth()325 mmReportCategoryExpensesCategoriesCurrentMonth::mmReportCategoryExpensesCategoriesCurrentMonth
326 ( )
327 : mmReportCategoryExpensesCategories(new mmCurrentMonth()
328 , wxString::Format(_("Categories - %s"), _("Current Month")))
329 {}
330 
mmReportCategoryExpensesCategoriesCurrentMonthToDate()331 mmReportCategoryExpensesCategoriesCurrentMonthToDate::mmReportCategoryExpensesCategoriesCurrentMonthToDate
332 ( )
333 : mmReportCategoryExpensesCategories(new mmCurrentMonthToDate()
334 , wxString::Format(_("Categories - %s"), _("Current Month to Date")))
335 {}
336 
mmReportCategoryExpensesCategoriesLastMonth()337 mmReportCategoryExpensesCategoriesLastMonth::mmReportCategoryExpensesCategoriesLastMonth()
338 : mmReportCategoryExpensesCategories(new mmLastMonth()
339 , wxString::Format(_("Categories - %s"), _("Last Month")))
340 {}
341 
mmReportCategoryExpensesCategoriesLast30Days()342 mmReportCategoryExpensesCategoriesLast30Days::mmReportCategoryExpensesCategoriesLast30Days
343 ( )
344 : mmReportCategoryExpensesCategories(new mmLast30Days()
345 , wxString::Format(_("Categories - %s"), _("Last 30 Days")))
346 {}
347 
mmReportCategoryExpensesCategoriesLastYear()348 mmReportCategoryExpensesCategoriesLastYear::mmReportCategoryExpensesCategoriesLastYear()
349 : mmReportCategoryExpensesCategories(new mmLastYear()
350 , wxString::Format(_("Categories - %s"), _("Last Year")))
351 {}
352 
mmReportCategoryExpensesCategoriesCurrentYear()353 mmReportCategoryExpensesCategoriesCurrentYear::mmReportCategoryExpensesCategoriesCurrentYear
354 ( )
355 : mmReportCategoryExpensesCategories(new mmCurrentYear()
356 , wxString::Format(_("Categories - %s"), _("Current Year")))
357 {}
358 
mmReportCategoryExpensesCategoriesCurrentYearToDate()359 mmReportCategoryExpensesCategoriesCurrentYearToDate::mmReportCategoryExpensesCategoriesCurrentYearToDate
360 ( )
361 : mmReportCategoryExpensesCategories(new mmCurrentYearToDate()
362 , wxString::Format(_("Categories - %s"), _("Current Year to Date")))
363 {}
364 
mmReportCategoryExpensesCategoriesLastFinancialYear(int day,int month)365 mmReportCategoryExpensesCategoriesLastFinancialYear::mmReportCategoryExpensesCategoriesLastFinancialYear
366 ( int day, int month)
367 : mmReportCategoryExpensesCategories(new mmLastFinancialYear(day, month)
368 , wxString::Format(_("Categories - %s"), _("Last Financial Year")))
369 {}
370 
mmReportCategoryExpensesCategoriesCurrentFinancialYear(int day,int month)371 mmReportCategoryExpensesCategoriesCurrentFinancialYear::mmReportCategoryExpensesCategoriesCurrentFinancialYear
372 ( int day, int month)
373 : mmReportCategoryExpensesCategories(new mmCurrentFinancialYear(day, month)
374 , wxString::Format(_("Categories - %s"), _("Current Financial Year")))
375 {}
376 
mmReportCategoryExpensesCategoriesCurrentFinancialYearToDate(int day,int month)377 mmReportCategoryExpensesCategoriesCurrentFinancialYearToDate::mmReportCategoryExpensesCategoriesCurrentFinancialYearToDate
378 (int day, int month)
379 : mmReportCategoryExpensesCategories(new mmCurrentFinancialYearToDate(day, month)
380 , wxString::Format(_("Categories - %s"), _("Current Financial Year to Date")))
381 {}
382