1 /*  HomeBank -- Free, easy, personal accounting for everyone.
2  *  Copyright (C) 1995-2021 Maxime DOYEN
3  *
4  *  This file is part of HomeBank.
5  *
6  *  HomeBank is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  HomeBank is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "homebank.h"
21 #include "hb-report.h"
22 
23 /****************************************************************************/
24 /* Debug macros                                                             */
25 /****************************************************************************/
26 #define MYDEBUG 0
27 
28 #if MYDEBUG
29 #define DB(x) (x);
30 #else
31 #define DB(x);
32 #endif
33 
34 /* our global datas */
35 extern struct HomeBank *GLOBALS;
36 extern struct Preferences *PREFS;
37 
38 
39 extern gchar *CYA_ABMONTHS[];
40 
41 /* = = = = = = = = = = = = = = = = = = = = */
42 /* CarCost */
43 
da_vehiclecost_free(CarCost * item)44 void da_vehiclecost_free(CarCost *item)
45 {
46 	if(item != NULL)
47 	{
48 		g_free(item);
49 	}
50 }
51 
52 
da_vehiclecost_malloc(void)53 CarCost *da_vehiclecost_malloc(void)
54 {
55 	return g_malloc0(sizeof(CarCost));
56 }
57 
da_vehiclecost_destroy(GList * list)58 void da_vehiclecost_destroy(GList *list)
59 {
60 GList *tmplist = g_list_first(list);
61 
62 	while (tmplist != NULL)
63 	{
64 	CarCost *item = tmplist->data;
65 		da_vehiclecost_free(item);
66 		tmplist = g_list_next(tmplist);
67 	}
68 	g_list_free(list);
69 }
70 
71 
72 
73 /* = = = = = = = = = = = = = = = = = = = = */
74 
da_datarow_free(DataRow * row)75 static void da_datarow_free(DataRow *row)
76 {
77 	if(row)
78 	{
79 		g_free(row->label);
80 		g_free(row->expense);
81 		g_free(row->income);
82 	}
83 }
84 
85 
da_datarow_malloc(guint32 nbcol)86 static DataRow *da_datarow_malloc(guint32 nbcol)
87 {
88 DataRow *row;
89 
90 	row = g_malloc0(sizeof(DataRow));
91 	if(!row)
92 		return NULL;
93 
94 	row->expense = g_malloc0((nbcol+2) * sizeof(gdouble));
95 	row->income  = g_malloc0((nbcol+2) * sizeof(gdouble));
96 	return row;
97 }
98 
99 
da_datatable_free(DataTable * dt)100 void da_datatable_free(DataTable *dt)
101 {
102 guint i;
103 
104 	DB( g_print("da_datatable_free\n") );
105 
106 	if(dt != NULL)
107 	{
108 		//free each rows
109 		for(i=0;i<dt->nbrows;i++)
110 		{
111 		DataRow *dr = dt->rows[i];
112 
113 			da_datarow_free(dr);
114 		}
115 
116 		da_datarow_free(dt->totrow);
117 
118 		g_free(dt->rows);
119 
120 		g_free(dt);
121 
122 	}
123 }
124 
125 
da_datatable_malloc(gshort type,guint32 nbrows,guint32 nbcols)126 DataTable *da_datatable_malloc(gshort type, guint32 nbrows, guint32 nbcols)
127 {
128 DataTable *dt = g_malloc0(sizeof(DataTable));
129 guint i;
130 
131 	DB( g_print("\nda_datatable_malloc\n") );
132 
133 	if(!dt)
134 		return NULL;
135 
136 	//allocate 2 more slot for total and average
137 	dt->nbrows = nbrows;
138 	dt->nbcols = nbcols;
139 	dt->rows = g_malloc0((nbrows) * sizeof(gpointer));
140 	for(i=0;i<dt->nbrows;i++)
141 	{
142 	DataRow *dr = da_datarow_malloc(dt->nbcols+2);
143 
144 		//dr->label = ;
145 		//dr.pos = ;
146 		dt->rows[i] = dr;
147 	}
148 
149 	dt->totrow = da_datarow_malloc(dt->nbcols+2);
150 
151 	DB( g_print("- @%p, r=%d, c=%d\n", dt, nbrows, nbcols) );
152 
153 	return dt;
154 }
155 
156 
157 /* = = = = = = = = = = = = = = = = = = = = */
158 
159 
160 //#1915643 week iso 8601
161 /*
162 ** return the week list position correponding to the passed date
163 */
DateInWeek(guint32 from,guint32 opedate)164 static guint DateInWeek(guint32 from, guint32 opedate)
165 {
166 GDate *date1, *date2;
167 gint pos;
168 GDateWeekday wday;
169 
170 	date1 = g_date_new_julian(from);
171 	date2 = g_date_new_julian(opedate);
172 
173 	//ISO 8601 from must be monday, to slice in correct week
174 	wday = g_date_get_weekday(date1);
175 	g_date_subtract_days (date1, wday-G_DATE_MONDAY);
176 
177 	pos = (opedate - g_date_get_julian(date1)) / 7;
178 
179 	DB( g_print(" from=%d %02d-%02d-%04d (shift %d) => %d\n", g_date_get_weekday(date1), g_date_get_day(date1), g_date_get_month(date1), g_date_get_year(date1), (wday-1), pos) );
180 
181 	g_date_free(date2);
182 	g_date_free(date1);
183 
184 	return(pos);
185 }
186 
187 
188 /*
189 ** return the month list position correponding to the passed date
190 */
DateInMonth(guint32 from,guint32 opedate)191 static guint DateInMonth(guint32 from, guint32 opedate)
192 {
193 GDate *date1, *date2;
194 guint pos;
195 
196 	//todo
197 	// this return sometimes -1, -2 which is wrong
198 
199 	date1 = g_date_new_julian(from);
200 	date2 = g_date_new_julian(opedate);
201 
202 	pos = ((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1);
203 
204 	DB( g_print(" from=%d-%d ope=%d-%d => %d\n", g_date_get_month(date1), g_date_get_year(date1), g_date_get_month(date2), g_date_get_year(date2), pos) );
205 
206 	g_date_free(date2);
207 	g_date_free(date1);
208 
209 	return(pos);
210 }
211 
212 
213 
214 //for fiscal sub gap between 1st fiscal and 1/1/year
215 
216 //int quarterNumber = (date.Month-1)/3+1;
217 //DateTime firstDayOfQuarter = new DateTime(date.Year, (quarterNumber-1)*3+1,1);
218 //DateTime lastDayOfQuarter = firstDayOfQuarter.AddMonths(3).AddDays(-1);
219 
DateInQuarter(guint32 from,guint32 opedate)220 static guint DateInQuarter(guint32 from, guint32 opedate)
221 {
222 GDate *date1, *date2;
223 guint quarter, pos;
224 
225 	date1 = g_date_new_julian(from);
226 	date2 = g_date_new_julian(opedate);
227 
228 	//#1758532 shift to first quarter day of 'from date'
229 	quarter = ((g_date_get_month(date1)-1)/3)+1;
230 	DB( g_print("-- from=%02d/%d :: Q%d\n", g_date_get_month(date1), g_date_get_year(date1), quarter) );
231 	g_date_set_day(date1, 1);
232 	g_date_set_month(date1, ((quarter-1)*3)+1);
233 
234 	pos = (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/3;
235 
236 	DB( g_print("-- from=%02d/%d ope=%02d/%d => pos=%d\n", g_date_get_month(date1), g_date_get_year(date1), g_date_get_month(date2), g_date_get_year(date2), pos) );
237 
238 	g_date_free(date2);
239 	g_date_free(date1);
240 
241 	return(pos);
242 }
243 
244 
DateInHalfYear(guint32 from,guint32 opedate)245 static guint DateInHalfYear(guint32 from, guint32 opedate)
246 {
247 GDate *date1, *date2;
248 guint hyear, pos;
249 
250 	date1 = g_date_new_julian(from);
251 	date2 = g_date_new_julian(opedate);
252 
253 	// shift to first half year of 'from date'
254 	hyear = ((g_date_get_month(date1)-1)/6)+1;
255 	DB( g_print("-- from=%02d/%d :: Q%d\n", g_date_get_month(date1), g_date_get_year(date1), hyear) );
256 	g_date_set_day(date1, 1);
257 	g_date_set_month(date1, ((hyear-1)*6)+1);
258 
259 	pos = (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/6;
260 
261 	DB( g_print(" from=%d-%d ope=%d-%d => %d\n", g_date_get_month(date1), g_date_get_year(date1), g_date_get_month(date2), g_date_get_year(date2), pos) );
262 
263 	g_date_free(date2);
264 	g_date_free(date1);
265 
266 	return(pos);
267 }
268 
269 
270 /*
271 ** return the year list position correponding to the passed date
272 */
DateInYear(guint32 from,guint32 opedate)273 static guint DateInYear(guint32 from, guint32 opedate)
274 {
275 GDate *date;
276 guint year_from, year_ope, pos;
277 
278 	date = g_date_new_julian(from);
279 	year_from = g_date_get_year(date);
280 
281 	g_date_set_julian(date, opedate);
282 	year_ope = g_date_get_year(date);
283 	g_date_free(date);
284 
285 	pos = year_ope - year_from;
286 
287 	DB( g_print(" from=%d ope=%d => %d\n", year_from, year_ope, pos) );
288 
289 	return(pos);
290 }
291 
292 
293 
datatable_init_items(DataTable * dt,gint src,guint32 jfrom)294 void datatable_init_items(DataTable *dt, gint src, guint32 jfrom)
295 {
296 GList *list = NULL;
297 gint pos;
298 gchar *name;
299 gchar buffer[64];
300 guint i;
301 
302 	DB( g_print("\ndatatable_init_items\n") );
303 
304 	switch(src)
305 	{
306 		case REPORT_SRC_CATEGORY:
307 		case REPORT_SRC_SUBCATEGORY:
308 			list = category_glist_sorted(1);
309 			break;
310 		case REPORT_SRC_PAYEE:
311 			list = payee_glist_sorted(1);
312 			break;
313 		case REPORT_SRC_ACCOUNT:
314 			list = account_glist_sorted(1);
315 			break;
316 		case REPORT_SRC_TAG:
317 			list = tag_glist_sorted(1);
318 			break;
319 	}
320 
321 
322 	//todo: list_index has a cost
323 	//preferable to iter through the list
324 	for(i=0;i<dt->nbrows;i++)
325 	{
326 	DataRow *dr = dt->rows[i];
327 
328 		pos = i;
329 		name = NULL;
330 		switch(src)
331 		{
332 			case REPORT_SRC_CATEGORY:
333 			case REPORT_SRC_SUBCATEGORY:
334 				{
335 				Category *entry = da_cat_get(i);
336 					if(entry != NULL)
337 					{
338 						name = entry->key == 0 ? _("(no category)") : entry->fullname;
339 						//if( src == REPORT_SRC_SUBCATEGORY )
340 							//name = entry->name;
341 						pos = g_list_index(list, entry);
342 					}
343 				}
344 				break;
345 			case REPORT_SRC_PAYEE:
346 				{
347 				Payee *entry = da_pay_get(i);
348 					if(entry != NULL)
349 					{
350 						name = entry->key == 0 ? _("(no payee)") : entry->name;
351 						pos = g_list_index(list, entry);
352 					}
353 				}
354 				break;
355 
356 			case REPORT_SRC_ACCOUNT:
357 				{
358 				Account *entry = da_acc_get(i);
359 					if(entry != NULL)
360 					{
361 						name = entry->name;
362 						pos = g_list_index(list, entry);
363 					}
364 				}
365 				break;
366 
367 			case REPORT_SRC_TAG:
368 				{
369 				Tag *entry = da_tag_get(i+1);
370 					if(entry != NULL)
371 					{
372 						name = entry->name;
373 						pos = g_list_index(list, entry);
374 					}
375 				}
376 				break;
377 
378 			case REPORT_SRC_MONTH:
379 				report_interval_snprint_name(buffer, sizeof(buffer)-1, REPORT_INTVL_MONTH, jfrom, i);
380 				name = buffer;
381 				pos = i;
382 				break;
383 
384 			case REPORT_SRC_YEAR:
385 				report_interval_snprint_name(buffer, sizeof(buffer)-1, REPORT_INTVL_YEAR, jfrom, i);
386 				name = buffer;
387 				pos = i;
388 				break;
389 		}
390 
391 		DB( g_print("- init row:%d / pos:%d '%s'\n", i, pos, name) );
392 
393 		dr->pos = pos;
394 		dr->label = g_strdup(name);
395 	}
396 
397 	g_list_free(list);
398 
399 }
400 
401 
402 // this is the add in total mode
datatable_amount_add(DataTable * dt,guint32 idx,gdouble amount)403 void datatable_amount_add(DataTable *dt, guint32 idx, gdouble amount)
404 {
405 	if( idx <= (dt->nbrows) )
406 	{
407 	DataRow *dr = dt->rows[idx];
408 
409 		if(amount > 0.0)
410 		{
411 			dr->income[1] += amount;
412 			dt->totinc += amount;
413 		}
414 		else
415 		{
416 			dr->expense[0] += amount;
417 			dt->totexp += amount;
418 		}
419 	}
420 	else
421 		g_warning("invalid datatable index");
422 }
423 
424 
report_compute_total(gint tmpsrc,Filter * flt,GQueue * txn_queue)425 DataTable *report_compute_total(gint tmpsrc, Filter *flt, GQueue *txn_queue)
426 {
427 DataTable *dt;
428 GList *list;
429 guint i;
430 gint n_result;
431 
432 	DB( g_print("\n[report] compute_total\n") );
433 
434 	//todo: remove this later on
435 	n_result = report_items_count(tmpsrc, flt->mindate, flt->maxdate);
436 
437 	DB( g_print(" %d :: n_result=%d\n", tmpsrc, n_result) );
438 
439 	dt = da_datatable_malloc (0, n_result, 3);
440 	if( dt != NULL )
441 	{
442 		datatable_init_items(dt, tmpsrc, flt->mindate);
443 
444 		list = g_queue_peek_head_link(txn_queue);
445 		while (list != NULL)
446 		{
447 		Transaction *ope = list->data;
448 
449 			DB( g_print("** testing '%s', cat=%d==> %d\n", ope->memo, ope->kcat, filter_txn_match(flt, ope)) );
450 
451 			if( (filter_txn_match(flt, ope) == 1) )
452 			{
453 			guint32 pos = 0;
454 			gdouble trn_amount;
455 
456 				DB( g_print(" - should insert\n") );
457 
458 				trn_amount = report_txn_amount_get(flt, ope);
459 				trn_amount = hb_amount_base(trn_amount, ope->kcur);
460 
461 
462 				if( tmpsrc != REPORT_SRC_TAG )
463 				{
464 					//for split, affect the amount to the category
465 					if( (tmpsrc == REPORT_SRC_CATEGORY || tmpsrc == REPORT_SRC_SUBCATEGORY) && ope->flags & OF_SPLIT )
466 					{
467 					guint nbsplit = da_splits_length(ope->splits);
468 					Split *split;
469 					gboolean status;
470 					gint sinsert;
471 
472 						for(i=0;i<nbsplit;i++)
473 						{
474 							split = da_splits_get(ope->splits, i);
475 							status = da_flt_status_cat_get(flt, split->kcat);
476 							sinsert = ( status == TRUE ) ? 1 : 0;
477 							if(flt->option[FLT_GRP_CATEGORY] == 2) sinsert ^= 1;
478 
479 							DB( g_print(" split insert=%d, kcat=%d\n", sinsert, split->kcat) );
480 
481 							if( (flt->option[FLT_GRP_CATEGORY] == 0) || sinsert)
482 							{
483 								pos = category_report_id(split->kcat, (tmpsrc == REPORT_SRC_SUBCATEGORY) ? TRUE : FALSE);
484 								trn_amount = hb_amount_base(split->amount, ope->kcur);
485 								datatable_amount_add(dt, pos, trn_amount);
486 							}
487 						}
488 					}
489 					else
490 					{
491 						pos = report_items_get_pos(tmpsrc, flt->mindate, ope);
492 						datatable_amount_add(dt, pos, trn_amount);
493 					}
494 				}
495 				else
496 				/* the TAG process is particular */
497 				{
498 					if(ope->tags != NULL)
499 					{
500 					guint32 *tptr = ope->tags;
501 
502 						while(*tptr)
503 						{
504 							pos = *tptr - 1;
505 							datatable_amount_add(dt, pos, trn_amount);
506 							tptr++;
507 						}
508 					}
509 				}
510 			}
511 			list = g_list_next(list);
512 		}
513 	}
514 
515 	return dt;
516 }
517 
518 
datatable_trend_add(DataTable * dt,guint32 row,guint32 col,gdouble amount,gboolean addtotal)519 static void datatable_trend_add(DataTable *dt, guint32 row, guint32 col, gdouble amount, gboolean addtotal)
520 {
521 	DB( g_print("   add to r%d,c%d %.2f\n", row, col, amount) );
522 
523 	if( row <= (dt->nbrows) )
524 	{
525 	DataRow *dr = dt->rows[row];
526 
527 		if( col <= dt->nbcols )
528 		{
529 			if(amount > 0.0)
530 			{
531 				dr->income[col] += amount;
532 				dr->income[dt->nbcols+1] += (amount/dt->nbcols);
533 				dr->income[dt->nbcols+2] += amount;
534 				//dt->totinc += amount;
535 				if( addtotal == TRUE )
536 				{
537 					dt->totrow->income[col] += amount;
538 					dt->totrow->income[dt->nbcols+1] += (amount/dt->nbcols);
539 					dt->totrow->income[dt->nbcols+2] += amount;
540 				}
541 			}
542 			else
543 			{
544 				dr->expense[col] += amount;
545 				dr->expense[dt->nbcols+1] += (amount/dt->nbcols);
546 				dr->expense[dt->nbcols+2] += amount;
547 				//dt->totexp += amount;
548 				if( addtotal == TRUE )
549 				{
550 					dt->totrow->expense[col] += amount;
551 					dt->totrow->expense[dt->nbcols+1] += (amount/dt->nbcols);
552 					dt->totrow->expense[dt->nbcols+2] += amount;
553 				}
554 			}
555 		}
556 	}
557 	else
558 		g_warning("invalid datatable index");
559 }
560 
561 
report_compute_trend(gint tmpsrc,gint tmpintvl,Filter * flt,GQueue * txn_queue)562 DataTable *report_compute_trend(gint tmpsrc, gint tmpintvl, Filter *flt, GQueue *txn_queue)
563 {
564 DataTable *dt;
565 GList *list;
566 guint i;
567 gint n_result, n_cols;
568 
569 	DB( g_print("\n[report] compute_trend\n") );
570 
571 	//todo: remove this later on
572 	n_result = report_items_count(tmpsrc, flt->mindate, flt->maxdate);
573 	n_cols   = report_interval_count(tmpintvl, flt->mindate, flt->maxdate);
574 
575 	DB( g_print(" %d :: n_result=%d\n", tmpsrc, n_result) );
576 
577 	dt = da_datatable_malloc (0, n_result, n_cols);
578 	if( dt != NULL )
579 	{
580 		datatable_init_items(dt, tmpsrc, flt->mindate);
581 
582 		list = g_queue_peek_head_link(txn_queue);
583 		while (list != NULL)
584 		{
585 		Transaction *ope = list->data;
586 
587 			if( (filter_txn_match(flt, ope) == 1) )
588 			{
589 			guint32 pos = 0;
590 			guint32 col = 0;
591 			gdouble trn_amount;
592 
593 				DB( g_print("\n srctxn '%s' %.2f, cat=%d, pay=%d, acc=%d\n", ope->memo, ope->amount, ope->kcat, ope->kpay, ope->kacc) );
594 
595 				trn_amount = report_txn_amount_get(flt, ope);
596 				trn_amount = hb_amount_base(trn_amount, ope->kcur);
597 
598 				col = report_interval_get_pos(tmpintvl, flt->mindate, ope);
599 
600 				switch( tmpsrc )
601 				{
602 					case REPORT_SRC_CATEGORY:
603 					case REPORT_SRC_SUBCATEGORY:
604 						//for split, affect the amount to the category
605 						if( ope->flags & OF_SPLIT )
606 						{
607 						guint nbsplit = da_splits_length(ope->splits);
608 						Split *split;
609 						gboolean status;
610 						gint sinsert;
611 
612 							for(i=0;i<nbsplit;i++)
613 							{
614 								split = da_splits_get(ope->splits, i);
615 								status = da_flt_status_cat_get(flt, split->kcat);
616 								sinsert = ( status == TRUE ) ? 1 : 0;
617 								if(flt->option[FLT_GRP_CATEGORY] == 2) sinsert ^= 1;
618 								if( (flt->option[FLT_GRP_CATEGORY] == 0) || sinsert)
619 								{
620 									DB( g_print(" split insert=%d, kcat=%d\n", sinsert, split->kcat) );
621 									trn_amount = hb_amount_base(split->amount, ope->kcur);
622 									pos = category_report_id(split->kcat, (tmpsrc == REPORT_SRC_CATEGORY) ? FALSE : TRUE);
623 									datatable_trend_add(dt, pos, col, trn_amount, TRUE);
624 									//add to parent as well
625 									if( tmpsrc == REPORT_SRC_SUBCATEGORY )
626 									{
627 									//#1859279 test cat as subtype here to avoid count twice
628 									Category *cat = da_cat_get(split->kcat);
629 
630 										if((cat != NULL) && (cat->flags & GF_SUB))
631 										{
632 											pos = cat->parent;
633 											datatable_trend_add(dt, pos, col, trn_amount, FALSE);
634 										}
635 									}
636 								}
637 							}
638 						}
639 						else
640 						{
641 							pos = category_report_id(ope->kcat, (tmpsrc == REPORT_SRC_CATEGORY) ? FALSE : TRUE);
642 							datatable_trend_add(dt, pos, col, trn_amount, TRUE);
643 							//add to parent as well
644 							if( tmpsrc == REPORT_SRC_SUBCATEGORY )
645 							{
646 							//#1859279 test cat as subtype here to avoid count twice
647 							Category *cat = da_cat_get(ope->kcat);
648 
649 								if((cat != NULL) && (cat->flags & GF_SUB))
650 								{
651 									pos = cat->parent;
652 									datatable_trend_add(dt, pos, col, trn_amount, FALSE);
653 								}
654 							}
655 						}
656 						break;
657 
658 					case REPORT_SRC_TAG:
659 						if(ope->tags != NULL)
660 						{
661 						guint32 *tptr = ope->tags;
662 
663 							while(*tptr)
664 							{
665 								pos = *tptr - 1;
666 								datatable_trend_add(dt, pos, col, trn_amount, TRUE);
667 								tptr++;
668 							}
669 						}
670 						break;
671 
672 					default:
673 						pos = report_items_get_pos(tmpsrc, flt->mindate, ope);
674 						datatable_trend_add(dt, pos, col, trn_amount, TRUE);
675 						break;
676 
677 				}
678 			}
679 			list = g_list_next(list);
680 		}
681 	}
682 
683 	return dt;
684 }
685 
686 
report_items_count(gint src,guint32 jfrom,guint32 jto)687 gint report_items_count(gint src, guint32 jfrom, guint32 jto)
688 {
689 GDate *date1, *date2;
690 gint nbsrc = 0;
691 
692 	switch(src)
693 	{
694 		case REPORT_SRC_CATEGORY:
695 		case REPORT_SRC_SUBCATEGORY:
696 			nbsrc = da_cat_get_max_key() + 1;
697 			break;
698 		case REPORT_SRC_PAYEE:
699 			nbsrc = da_pay_get_max_key() + 1;
700 			break;
701 		case REPORT_SRC_ACCOUNT:
702 			nbsrc = da_acc_get_max_key() + 1;
703 			break;
704 		case REPORT_SRC_TAG:
705 			nbsrc = da_tag_length();
706 			break;
707 		case REPORT_SRC_MONTH:
708 			date1 = g_date_new_julian(jfrom);
709 			date2 = g_date_new_julian(jto);
710 			nbsrc = ((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1) + 1;
711 			g_date_free(date2);
712 			g_date_free(date1);
713 			break;
714 		case REPORT_SRC_YEAR:
715 			date1 = g_date_new_julian(jfrom);
716 			date2 = g_date_new_julian(jto);
717 			nbsrc = g_date_get_year(date2) - g_date_get_year(date1) + 1;
718 			g_date_free(date2);
719 			g_date_free(date1);
720 			break;
721 	}
722 
723 	return nbsrc;
724 }
725 
726 
727 
report_items_get_pos(gint tmpsrc,guint jfrom,Transaction * ope)728 gint report_items_get_pos(gint tmpsrc, guint jfrom, Transaction *ope)
729 {
730 gint pos = 0;
731 
732 	switch(tmpsrc)
733 	{
734 		case REPORT_SRC_CATEGORY:
735 			pos = category_report_id(ope->kcat, FALSE);
736 			break;
737 		case REPORT_SRC_SUBCATEGORY:
738 			pos = ope->kcat;
739 			break;
740 		case REPORT_SRC_PAYEE:
741 			pos = ope->kpay;
742 			break;
743 		case REPORT_SRC_ACCOUNT:
744 			pos = ope->kacc;
745 			break;
746 		case REPORT_SRC_MONTH:
747 			pos = DateInMonth(jfrom, ope->date);
748 			break;
749 		case REPORT_SRC_YEAR:
750 			pos = DateInYear(jfrom, ope->date);
751 			break;
752 	}
753 	return pos;
754 }
755 
756 
757 
report_interval_get_pos(gint intvl,guint jfrom,Transaction * ope)758 gint report_interval_get_pos(gint intvl, guint jfrom, Transaction *ope)
759 {
760 gint pos = 0;
761 
762 	switch(intvl)
763 	{
764 		case REPORT_INTVL_DAY:
765 			pos = ope->date - jfrom;
766 			break;
767 		case REPORT_INTVL_WEEK:
768 			//#1915643 week iso 8601
769 			//pos = (ope->date - jfrom)/7;
770 			pos = DateInWeek(jfrom, ope->date);
771 			break;
772 		case REPORT_INTVL_MONTH:
773 			pos = DateInMonth(jfrom, ope->date);
774 			break;
775 		case REPORT_INTVL_QUARTER:
776 			pos = DateInQuarter(jfrom, ope->date);
777 			break;
778 		case REPORT_INTVL_HALFYEAR:
779 			pos = DateInHalfYear(jfrom, ope->date);
780 			break;
781 		case REPORT_INTVL_YEAR:
782 			pos = DateInYear(jfrom, ope->date);
783 			break;
784 	}
785 
786 	return pos;
787 }
788 
789 
790 
report_interval_count(gint intvl,guint32 jfrom,guint32 jto)791 gint report_interval_count(gint intvl, guint32 jfrom, guint32 jto)
792 {
793 GDate *date1, *date2;
794 gint nbintvl = 0;
795 
796 	date1 = g_date_new_julian(jfrom);
797 	date2 = g_date_new_julian(jto);
798 
799 	switch(intvl)
800 	{
801 		case REPORT_INTVL_DAY:
802 			nbintvl = 1 + (jto - jfrom);
803 			break;
804 		case REPORT_INTVL_WEEK:
805 			nbintvl = 1 + ((jto - jfrom) / 7);
806 			break;
807 		case REPORT_INTVL_MONTH:
808 			nbintvl = 1 + ((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1);
809 			break;
810 		case REPORT_INTVL_QUARTER:
811 			nbintvl = 1 + (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/3;
812 			break;
813 		case REPORT_INTVL_HALFYEAR:
814 			nbintvl = 1 + (((g_date_get_year(date2) - g_date_get_year(date1)) * 12) + g_date_get_month(date2) - g_date_get_month(date1))/6;
815 			break;
816 		case REPORT_INTVL_YEAR:
817 			nbintvl = 1 + g_date_get_year(date2) - g_date_get_year(date1);
818 			break;
819 	}
820 
821 	g_date_free(date2);
822 	g_date_free(date1);
823 
824 	return nbintvl;
825 }
826 
827 
report_interval_snprint_name(gchar * s,gint slen,gint intvl,guint32 jfrom,gint idx)828 void report_interval_snprint_name(gchar *s, gint slen, gint intvl, guint32 jfrom, gint idx)
829 {
830 GDate *date = g_date_new_julian(jfrom);
831 GDateWeekday wday;
832 
833 	switch(intvl)
834 	{
835 		case REPORT_INTVL_DAY:
836 			g_date_add_days(date, idx);
837 			g_date_strftime (s, slen, PREFS->date_format, date);
838 			break;
839 
840 		case REPORT_INTVL_WEEK:
841 			g_date_add_days(date, idx*7);
842 
843 			//#1915643 week iso 8601
844 			//ISO 8601 from must be monday, to slice in correct week
845 			wday = g_date_get_weekday(date);
846 			g_date_subtract_days (date, wday-G_DATE_MONDAY);
847 			g_date_add_days (date, G_DATE_WEDNESDAY);
848 
849 			//g_snprintf(s, slen, _("%d-w%d"), g_date_get_year(date), g_date_get_monday_week_of_year(date));
850 			//TRANSLATORS: printf string for year of week W, ex. 2019-W52 for week 52 of 2019
851 			g_snprintf(s, slen, _("%d-w%02d"), g_date_get_year(date), g_date_get_iso8601_week_of_year(date));
852 			break;
853 
854 		case REPORT_INTVL_MONTH:
855 			g_date_add_months(date, idx);
856 			//g_snprintf(buffer, 63, "%d-%02d", g_date_get_year(date), g_date_get_month(date));
857 			g_snprintf(s, slen, "%d-%s", g_date_get_year(date), _(CYA_ABMONTHS[g_date_get_month(date)]));
858 			break;
859 
860 		case REPORT_INTVL_QUARTER:
861 			g_date_add_months(date, idx*3);
862 			//g_snprintf(buffer, 63, "%d-%02d", g_date_get_year(date), g_date_get_month(date));
863 			//todo: will be innacurrate here if fiscal year start not 1/jan
864 			//TRANSLATORS: printf string for year of quarter Q, ex. 2019-Q4 for quarter 4 of 2019
865 			g_snprintf(s, slen, _("%d-q%d"), g_date_get_year(date), ((g_date_get_month(date)-1)/3)+1);
866 			break;
867 
868 		case REPORT_INTVL_HALFYEAR:
869 			g_date_add_months(date, idx*6);
870 			g_snprintf(s, slen, "%d-%s", g_date_get_year(date), g_date_get_month(date) < 7 ? "h1" : "h2");
871 			break;
872 
873 		case REPORT_INTVL_YEAR:
874 			g_date_add_years(date, idx);
875 			g_snprintf(s, slen, "%d", g_date_get_year(date));
876 			break;
877 		default:
878 			*s ='\0';
879 			break;
880 	}
881 
882 	g_date_free(date);
883 }
884 
885 
report_acc_initbalance_get(Filter * flt)886 gdouble report_acc_initbalance_get(Filter *flt)
887 {
888 GList *lst_acc, *lnk_acc;
889 gdouble initbal = 0.0;
890 
891 	lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
892 	lnk_acc = g_list_first(lst_acc);
893 	while (lnk_acc != NULL)
894 	{
895 	Account *acc = lnk_acc->data;
896 
897 		//later, filter on acc
898 		if( !(acc->flags & AF_NOREPORT) )
899 			initbal += hb_amount_base(acc->initial, acc->kcur);
900 
901 		lnk_acc = g_list_next(lnk_acc);
902 	}
903 	g_list_free(lst_acc);
904 	return initbal;
905 }
906 
907 
908 //TODO: maybe migrate this to filter as well
909 //#1562372 in case of a split we need to take amount for filter categories only
report_txn_amount_get(Filter * flt,Transaction * txn)910 gdouble report_txn_amount_get(Filter *flt, Transaction *txn)
911 {
912 gdouble amount;
913 
914 	amount = txn->amount;
915 
916 	if( flt->option[FLT_GRP_CATEGORY] > 0 )	//not inactive
917 	{
918 		if( txn->flags & OF_SPLIT )
919 		{
920 		guint i, nbsplit = da_splits_length(txn->splits);
921 		Split *split;
922 		gboolean status;
923 		gint sinsert;
924 
925 			amount = 0.0;
926 
927 			for(i=0;i<nbsplit;i++)
928 			{
929 				split = da_splits_get(txn->splits, i);
930 				status = da_flt_status_cat_get(flt, split->kcat);
931 				sinsert = ( status == TRUE ) ? 1 : 0;
932 				if(flt->option[FLT_GRP_CATEGORY] == 2) sinsert ^= 1;
933 
934 				DB( g_print(" split insert=%d, kcat=%d\n", sinsert, split->kcat) );
935 
936 				if( (flt->option[FLT_GRP_CATEGORY] == 0) || sinsert)
937 				{
938 					amount += split->amount;
939 				}
940 			}
941 
942 		}
943 	}
944 	return amount;
945 }
946 
947