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-hbfile.h"
22 #include "hb-archive.h"
23 #include "hb-transaction.h"
24
25
26 /****************************************************************************/
27 /* Debug macros */
28 /****************************************************************************/
29 #define MYDEBUG 0
30
31 #if MYDEBUG
32 #define DB(x) (x);
33 #else
34 #define DB(x);
35 #endif
36
37 /* our global datas */
38 extern struct HomeBank *GLOBALS;
39 extern struct Preferences *PREFS;
40
41
42 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
43
44
hbfile_file_isbackup(gchar * filepath)45 gboolean hbfile_file_isbackup(gchar *filepath)
46 {
47 gboolean retval = FALSE;
48
49 if( filepath == NULL )
50 return FALSE;
51
52 if( g_str_has_suffix(filepath, "xhb~") || g_str_has_suffix(filepath, "bak") )
53 retval = TRUE;
54
55 return retval;
56 }
57
58
hbfile_file_hasrevert(gchar * filepath)59 gboolean hbfile_file_hasrevert(gchar *filepath)
60 {
61 gchar *bakfilepath;
62
63 bakfilepath = hb_filename_new_with_extension(GLOBALS->xhb_filepath, "xhb~");
64
65 DB( g_print(" test bak exists '%s'\n", bakfilepath) );
66
67 GLOBALS->xhb_hasrevert = g_file_test(bakfilepath, G_FILE_TEST_EXISTS);
68 g_free(bakfilepath);
69 //todo check here if need to return something
70 return GLOBALS->xhb_hasrevert;
71 }
72
73
74 //#1750161
hbfile_file_get_time_modified(gchar * filepath)75 guint64 hbfile_file_get_time_modified(gchar *filepath)
76 {
77 guint64 retval = 0ULL;
78 GFile *gfile;
79 GFileInfo *gfileinfo;
80
81 DB( g_print("\n[hbfile] get time modified\n") );
82
83 gfile = g_file_new_for_path(filepath);
84 gfileinfo = g_file_query_info (gfile, G_FILE_ATTRIBUTE_TIME_MODIFIED, 0, NULL, NULL);
85 if( gfileinfo )
86 {
87 retval = g_file_info_get_attribute_uint64 (gfileinfo, G_FILE_ATTRIBUTE_TIME_MODIFIED);
88 DB( g_print("- '%s' last access = %lu\n", filepath, retval) );
89 g_object_unref(gfileinfo);
90 }
91 g_object_unref(gfile);
92
93 return retval;
94 }
95
96
hbfile_file_default(void)97 void hbfile_file_default(void)
98 {
99 DB( g_print("\n[hbfile] default\n") );
100
101 //todo: maybe translate this also
102 hbfile_change_filepath(g_build_filename(PREFS->path_hbfile, "untitled.xhb", NULL));
103 GLOBALS->hbfile_is_new = TRUE;
104 GLOBALS->hbfile_is_bak = FALSE;
105 GLOBALS->xhb_timemodified = 0ULL;
106
107 DB( g_print("- path_hbfile is '%s'\n", PREFS->path_hbfile) );
108 DB( g_print("- xhb_filepath is '%s'\n", GLOBALS->xhb_filepath) );
109 }
110
111
112
113 /*
114 static gint hbfile_file_load_xhb(gchar *filepath)
115 {
116
117
118
119
120
121
122 }
123
124
125 static void hbfile_file_load_backup_xhb(void)
126 {
127 //todo: get from dialog.c, and split between dilaog.c/hbfile.c
128
129
130
131 }
132 */
133
hbfile_replace_basecurrency(Currency4217 * curfmt)134 void hbfile_replace_basecurrency(Currency4217 *curfmt)
135 {
136 Currency *item;
137 guint32 oldkcur;
138
139 DB( g_print("\n[hbfile] replace base currency\n") );
140
141 oldkcur = GLOBALS->kcur;
142 da_cur_delete(oldkcur);
143 item = currency_add_from_user(curfmt);
144 GLOBALS->kcur = item->key;
145
146 DB( g_print(" %d ==> %d %s\n", oldkcur, GLOBALS->kcur, item->iso_code) );
147 }
148
149
hbfile_change_basecurrency(guint32 key)150 void hbfile_change_basecurrency(guint32 key)
151 {
152 GList *list;
153 //guint32 oldkcur;
154
155 // set every rate to 0
156 list = g_hash_table_get_values(GLOBALS->h_cur);
157 while (list != NULL)
158 {
159 Currency *entry = list->data;
160
161 if(entry->key != GLOBALS->kcur)
162 {
163 entry->rate = 0.0;
164 entry->mdate = 0;
165 }
166
167 list = g_list_next(list);
168 }
169 g_list_free(list);
170
171 //oldkcur = GLOBALS->kcur;
172 GLOBALS->kcur = key;
173
174 //#1851103: absolutely no reason to do that
175 // update account with old base currency
176 /*list = g_hash_table_get_values(GLOBALS->h_acc);
177 while (list != NULL)
178 {
179 Account *acc = list->data;
180
181 if( acc->kcur == oldkcur )
182 acc->kcur = key;
183
184 list = g_list_next(list);
185 }
186 g_list_free(list);
187 */
188
189 GLOBALS->changes_count++;
190 }
191
192
hbfile_transaction_get_partial_internal(guint32 minjulian,guint32 maxjulian,gushort exclusionflags)193 static GQueue *hbfile_transaction_get_partial_internal(guint32 minjulian, guint32 maxjulian, gushort exclusionflags)
194 {
195 GList *lst_acc, *lnk_acc;
196 GList *lnk_txn;
197 GQueue *txn_queue;
198
199 txn_queue = g_queue_new ();
200
201 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
202 lnk_acc = g_list_first(lst_acc);
203 while (lnk_acc != NULL)
204 {
205 Account *acc = lnk_acc->data;
206
207 if( (acc->flags & exclusionflags) )
208 goto next_acc;
209
210 lnk_txn = g_queue_peek_tail_link(acc->txn_queue);
211 while (lnk_txn != NULL)
212 {
213 Transaction *txn = lnk_txn->data;
214
215 if( txn->date < minjulian ) //no need to go below mindate
216 break;
217
218 if( !((txn->status == TXN_STATUS_REMIND) || (txn->status == TXN_STATUS_VOID))
219 && (txn->date >= minjulian)
220 && (txn->date <= maxjulian)
221 )
222 {
223 g_queue_push_head (txn_queue, txn);
224 }
225
226 lnk_txn = g_list_previous(lnk_txn);
227 }
228
229 next_acc:
230 lnk_acc = g_list_next(lnk_acc);
231 }
232 g_list_free(lst_acc);
233
234 return txn_queue;
235 }
236
237
hbfile_transaction_get_partial(guint32 minjulian,guint32 maxjulian)238 GQueue *hbfile_transaction_get_partial(guint32 minjulian, guint32 maxjulian)
239 {
240 //#1674045 ony rely on nosummary
241 //return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_CLOSED|AF_NOREPORT));
242 return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_NOREPORT));
243 }
244
245
hbfile_transaction_get_partial_budget(guint32 minjulian,guint32 maxjulian)246 GQueue *hbfile_transaction_get_partial_budget(guint32 minjulian, guint32 maxjulian)
247 {
248 //#1674045 ony rely on nosummary
249 //return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_CLOSED|AF_NOREPORT|AF_NOBUDGET));
250 return hbfile_transaction_get_partial_internal(minjulian, maxjulian, (AF_NOREPORT|AF_NOBUDGET));
251 }
252
253
hbfile_sanity_check(void)254 void hbfile_sanity_check(void)
255 {
256 GList *lst_acc, *lnk_acc;
257 GList *lnk_txn;
258 GList *lxxx, *list;
259
260 DB( g_print("\n[hbfile] !! full sanity check !! \n") );
261
262 DB( g_print(" - transaction\n") );
263 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
264 lnk_acc = g_list_first(lst_acc);
265 while (lnk_acc != NULL)
266 {
267 Account *acc = lnk_acc->data;
268
269 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
270 while (lnk_txn != NULL)
271 {
272 Transaction *txn = lnk_txn->data;
273
274 da_transaction_consistency(txn);
275 lnk_txn = g_list_next(lnk_txn);
276 }
277 lnk_acc = g_list_next(lnk_acc);
278 }
279 g_list_free(lst_acc);
280
281
282 DB( g_print(" - scheduled/template\n") );
283 list = g_list_first(GLOBALS->arc_list);
284 while (list != NULL)
285 {
286 Archive *entry = list->data;
287
288 da_archive_consistency(entry);
289 list = g_list_next(list);
290 }
291
292
293 DB( g_print(" - account\n") );
294 lxxx = list = g_hash_table_get_values(GLOBALS->h_acc);
295 while (list != NULL)
296 {
297 Account *item = list->data;
298
299 da_acc_consistency(item);
300 list = g_list_next(list);
301 }
302 g_list_free(lxxx);
303
304
305 DB( g_print(" - payee\n") );
306 lxxx = list = g_hash_table_get_values(GLOBALS->h_pay);
307 while (list != NULL)
308 {
309 Payee *item = list->data;
310
311 da_pay_consistency(item);
312 list = g_list_next(list);
313 }
314 g_list_free(lxxx);
315
316
317 DB( g_print(" - category\n") );
318 lxxx = list = g_hash_table_get_values(GLOBALS->h_cat);
319 while (list != NULL)
320 {
321 Category *item = list->data;
322
323 da_cat_consistency(item);
324 list = g_list_next(list);
325 }
326 g_list_free(lxxx);
327
328
329 DB( g_print(" - assignments\n") );
330 lxxx = list = g_hash_table_get_values(GLOBALS->h_rul);
331 while (list != NULL)
332 {
333 Assign *item = list->data;
334
335 da_asg_consistency(item);
336 list = g_list_next(list);
337 }
338 g_list_free(lxxx);
339
340 }
341
342
hbfile_anonymize(void)343 void hbfile_anonymize(void)
344 {
345 GList *lst_acc, *lnk_acc;
346 GList *lnk_txn;
347 GList *lxxx, *list;
348 guint cnt, i;
349
350 DB( g_print("\n[hbfile] anonymize\n") );
351
352 // owner
353 hbfile_change_owner(g_strdup("An0nym0us"));
354 GLOBALS->changes_count++;
355 GLOBALS->hbfile_is_new = TRUE;
356
357 // filename
358 hbfile_change_filepath(g_build_filename(PREFS->path_hbfile, "anonymized.xhb", NULL));
359
360 // accounts
361 lxxx = list = g_hash_table_get_values(GLOBALS->h_acc);
362 while (list != NULL)
363 {
364 Account *item = list->data;
365 g_free(item->name);
366 item->name = g_strdup_printf("account %d", item->key);
367 g_free(item->number);
368 item->number = NULL;
369 g_free(item->bankname);
370 item->bankname = NULL;
371
372 GLOBALS->changes_count++;
373 list = g_list_next(list);
374 }
375 g_list_free(lxxx);
376
377 //payees
378 lxxx = list = g_hash_table_get_values(GLOBALS->h_pay);
379 while (list != NULL)
380 {
381 Payee *item = list->data;
382
383 if(item->key != 0)
384 {
385 g_free(item->name);
386 item->name = g_strdup_printf("payee %d", item->key);
387 GLOBALS->changes_count++;
388 }
389 list = g_list_next(list);
390 }
391 g_list_free(lxxx);
392
393 //categories
394 lxxx = list = g_hash_table_get_values(GLOBALS->h_cat);
395 while (list != NULL)
396 {
397 Category *item = list->data;
398
399 if(item->key != 0)
400 {
401 g_free(item->name);
402 item->name = g_strdup_printf("category %d", item->key);
403 GLOBALS->changes_count++;
404 }
405 list = g_list_next(list);
406 }
407 g_list_free(lxxx);
408
409 //tags
410 lxxx = list = g_hash_table_get_values(GLOBALS->h_tag);
411 while (list != NULL)
412 {
413 Tag *item = list->data;
414
415 if(item->key != 0)
416 {
417 g_free(item->name);
418 item->name = g_strdup_printf("tag %d", item->key);
419 GLOBALS->changes_count++;
420 }
421 list = g_list_next(list);
422 }
423 g_list_free(lxxx);
424
425 //assigns
426 lxxx = list = g_hash_table_get_values(GLOBALS->h_rul);
427 while (list != NULL)
428 {
429 Assign *item = list->data;
430
431 if(item->key != 0)
432 {
433 g_free(item->search);
434 item->search = g_strdup_printf("assign %d", item->key);
435 GLOBALS->changes_count++;
436 }
437 list = g_list_next(list);
438 }
439 g_list_free(lxxx);
440
441 //archives
442 cnt = 0;
443 list = g_list_first(GLOBALS->arc_list);
444 while (list != NULL)
445 {
446 Archive *item = list->data;
447
448 g_free(item->memo);
449 item->memo = g_strdup_printf("archive %d", cnt++);
450 GLOBALS->changes_count++;
451
452 //later split anonymize also
453
454 list = g_list_next(list);
455 }
456
457 //transaction
458 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
459 lnk_acc = g_list_first(lst_acc);
460 while (lnk_acc != NULL)
461 {
462 Account *acc = lnk_acc->data;
463
464 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
465 while (lnk_txn != NULL)
466 {
467 Transaction *item = lnk_txn->data;
468 Split *split;
469
470 g_free(item->info);
471 item->info = NULL;
472 g_free(item->memo);
473 item->memo = g_strdup_printf("memo %d", item->date);
474 GLOBALS->changes_count++;
475
476 if(item->flags & OF_SPLIT)
477 {
478 cnt = da_splits_length (item->splits);
479 for(i=0;i<cnt;i++)
480 {
481 split = da_splits_get(item->splits, i);
482 if( split == NULL ) break;
483
484 if(split->memo != NULL)
485 g_free(split->memo);
486
487 split->memo = g_strdup_printf("memo %d", i);
488 GLOBALS->changes_count++;
489 }
490 }
491 lnk_txn = g_list_next(lnk_txn);
492 }
493 lnk_acc = g_list_next(lnk_acc);
494 }
495 g_list_free(lst_acc);
496
497 }
498
499
500 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =*/
501
502
hbfile_change_owner(gchar * owner)503 void hbfile_change_owner(gchar *owner)
504 {
505 g_free(GLOBALS->owner);
506 GLOBALS->owner = (owner != NULL) ? owner : NULL;
507 }
508
509
hbfile_change_filepath(gchar * filepath)510 void hbfile_change_filepath(gchar *filepath)
511 {
512 g_free(GLOBALS->xhb_filepath);
513 GLOBALS->xhb_filepath = (filepath != NULL) ? filepath : NULL;
514 }
515
516
hbfile_cleanup(gboolean file_clear)517 void hbfile_cleanup(gboolean file_clear)
518 {
519 GSList *list;
520 //Transaction *txn;
521
522 DB( g_print("\n[hbfile] cleanup\n") );
523 DB( g_print("- file clear is %d\n", file_clear) );
524
525 // Free data storage
526 /*txn = g_trash_stack_pop(&GLOBALS->txn_stk);
527 while( txn != NULL )
528 {
529 da_transaction_free (txn);
530 txn = g_trash_stack_pop(&GLOBALS->txn_stk);
531 }*/
532 list = GLOBALS->deltxn_list;
533 while(list != NULL)
534 {
535 da_transaction_free (list->data);
536 list = g_slist_next(list);
537 }
538 g_slist_free(GLOBALS->deltxn_list);
539
540 g_slist_free(GLOBALS->openwindows);
541
542 da_transaction_destroy();
543 da_archive_destroy(GLOBALS->arc_list);
544 g_hash_table_destroy(GLOBALS->h_memo);
545 da_asg_destroy();
546 da_tag_destroy();
547 da_cat_destroy();
548 da_pay_destroy();
549 da_acc_destroy();
550 da_grp_destroy();
551 da_cur_destroy();
552
553 hbfile_change_owner(NULL);
554
555 if(file_clear)
556 hbfile_change_filepath(NULL);
557
558 }
559
560
hbfile_setup(gboolean file_clear)561 void hbfile_setup(gboolean file_clear)
562 {
563
564 DB( g_print("\n[hbfile] setup\n") );
565 DB( g_print("- file clear is %d\n", file_clear) );
566
567 // Allocate data storage
568 da_cur_new();
569 da_grp_new();
570 da_acc_new();
571 da_pay_new();
572 da_cat_new();
573 da_tag_new();
574 da_asg_new();
575 //txn queue is allocated into account
576
577 GLOBALS->h_memo = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, NULL);
578 GLOBALS->arc_list = NULL;
579
580 //5.5.1 list of opened windows
581 GLOBALS->openwindows = NULL;
582
583 //#1419304 we keep the deleted txn to a trash stack
584 //GLOBALS->txn_stk = NULL;
585 GLOBALS->deltxn_list = NULL;
586
587 if(file_clear == TRUE)
588 {
589 hbfile_file_default();
590 }
591 else
592 {
593 GLOBALS->hbfile_is_new = FALSE;
594 }
595
596 hbfile_change_owner(g_strdup(_("Unknown")));
597
598 GLOBALS->kcur = 1;
599
600 GLOBALS->vehicle_category = 0;
601
602 GLOBALS->auto_smode = 1;
603 GLOBALS->auto_nbdays = 0;
604 GLOBALS->auto_weekday = 1;
605
606 GLOBALS->changes_count = 0;
607
608 GLOBALS->xhb_hasrevert = FALSE;
609
610 }
611
612