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