Merge branch 'upstream'
[chaz/homebank] / src / hb-account.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 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-account.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
37
38 void
39 da_acc_free(Account *item)
40 {
41 DB( g_print("da_acc_free\n") );
42 if(item != NULL)
43 {
44 DB( g_print(" => %d, %s\n", item->key, item->name) );
45
46 g_free(item->name);
47 g_free(item->number);
48 g_free(item->bankname);
49 g_free(item->notes);
50
51 g_queue_free (item->txn_queue);
52
53 g_free(item);
54 }
55 }
56
57
58 Account *
59 da_acc_malloc(void)
60 {
61 Account *item;
62
63 DB( g_print("da_acc_malloc\n") );
64 item = g_malloc0(sizeof(Account));
65 item->kcur = GLOBALS->kcur;
66 item->txn_queue = g_queue_new ();
67 return item;
68 }
69
70
71 void
72 da_acc_destroy(void)
73 {
74 DB( g_print("da_acc_destroy\n") );
75 g_hash_table_destroy(GLOBALS->h_acc);
76 }
77
78
79 void
80 da_acc_new(void)
81 {
82 DB( g_print("da_acc_new\n") );
83 GLOBALS->h_acc = g_hash_table_new_full(g_int_hash, g_int_equal, (GDestroyNotify)g_free, (GDestroyNotify)da_acc_free);
84 }
85
86
87 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
88
89
90 /**
91 * da_acc_length:
92 *
93 * Return value: the number of elements
94 */
95 guint
96 da_acc_length(void)
97 {
98 return g_hash_table_size(GLOBALS->h_acc);
99 }
100
101
102 static void da_acc_max_key_ghfunc(gpointer key, Account *item, guint32 *max_key)
103 {
104 *max_key = MAX(*max_key, item->key);
105 }
106
107
108 /**
109 * da_acc_get_max_key:
110 *
111 * Get the biggest key from the GHashTable
112 *
113 * Return value: the biggest key value
114 *
115 */
116 guint32
117 da_acc_get_max_key(void)
118 {
119 guint32 max_key = 0;
120
121 g_hash_table_foreach(GLOBALS->h_acc, (GHFunc)da_acc_max_key_ghfunc, &max_key);
122 return max_key;
123 }
124
125
126 /**
127 * da_acc_remove:
128 *
129 * delete an account from the GHashTable
130 *
131 * Return value: TRUE if the key was found and deleted
132 *
133 */
134 gboolean
135 da_acc_remove(guint32 key)
136 {
137 DB( g_print("da_acc_remove %d\n", key) );
138
139 return g_hash_table_remove(GLOBALS->h_acc, &key);
140 }
141
142 /**
143 * da_acc_insert:
144 *
145 * insert an account into the GHashTable
146 *
147 * Return value: TRUE if inserted
148 *
149 */
150 gboolean
151 da_acc_insert(Account *item)
152 {
153 guint32 *new_key;
154
155 DB( g_print("da_acc_insert\n") );
156
157 new_key = g_new0(guint32, 1);
158 *new_key = item->key;
159 g_hash_table_insert(GLOBALS->h_acc, new_key, item);
160
161 return TRUE;
162 }
163
164
165 /**
166 * da_acc_append:
167 *
168 * insert an account into the GHashTable
169 *
170 * Return value: TRUE if inserted
171 *
172 */
173 gboolean
174 da_acc_append(Account *item)
175 {
176 Account *existitem;
177
178 DB( g_print("da_acc_append\n") );
179
180 existitem = da_acc_get_by_name( item->name );
181 if( existitem == NULL )
182 {
183 item->key = da_acc_get_max_key() + 1;
184 item->pos = da_acc_length() + 1;
185 da_acc_insert(item);
186 return TRUE;
187 }
188
189 DB( g_print(" -> %s already exist: %d\n", item->name, item->key) );
190
191 return FALSE;
192 }
193
194
195 static gboolean da_acc_name_grfunc(gpointer key, Account *item, gchar *name)
196 {
197 if( name && item->name )
198 {
199 if(!strcasecmp(name, item->name))
200 return TRUE;
201 }
202 return FALSE;
203 }
204
205 /**
206 * da_acc_get_by_name:
207 *
208 * Get an account structure by its name
209 *
210 * Return value: Account * or NULL if not found
211 *
212 */
213 Account *
214 da_acc_get_by_name(gchar *rawname)
215 {
216 Account *retval = NULL;
217 gchar *stripname;
218
219 DB( g_print("da_acc_get_by_name\n") );
220
221 if( rawname )
222 {
223 stripname = g_strdup(rawname);
224 g_strstrip(stripname);
225 if( strlen(stripname) > 0 )
226 retval = g_hash_table_find(GLOBALS->h_acc, (GHRFunc)da_acc_name_grfunc, stripname);
227
228 g_free(stripname);
229 }
230
231 return retval;
232 }
233
234
235 /**
236 * da_acc_get:
237 *
238 * Get an account structure by key
239 *
240 * Return value: Account * or NULL if not found
241 *
242 */
243 Account *
244 da_acc_get(guint32 key)
245 {
246 //DB( g_print("da_acc_get\n") );
247
248 return g_hash_table_lookup(GLOBALS->h_acc, &key);
249 }
250
251
252 void da_acc_consistency(Account *item)
253 {
254 g_strstrip(item->name);
255 }
256
257
258 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
259 #if MYDEBUG
260
261 static void
262 da_acc_debug_list_ghfunc(gpointer key, gpointer value, gpointer user_data)
263 {
264 guint32 *id = key;
265 Account *item = value;
266
267 DB( g_print(" %d :: %s\n", *id, item->name) );
268
269 }
270
271 static void
272 da_acc_debug_list(void)
273 {
274
275 DB( g_print("\n** debug **\n") );
276
277 g_hash_table_foreach(GLOBALS->h_acc, da_acc_debug_list_ghfunc, NULL);
278
279 DB( g_print("\n** end debug **\n") );
280
281 }
282
283 #endif
284
285
286 static gint
287 account_glist_name_compare_func(Account *a, Account *b)
288 {
289 return hb_string_utf8_compare(a->name, b->name);
290 }
291
292
293 static gint
294 account_glist_key_compare_func(Account *a, Account *b)
295 {
296 return a->key - b->key;
297 }
298
299
300 GList *account_glist_sorted(gint column)
301 {
302 GList *list = g_hash_table_get_values(GLOBALS->h_acc);
303
304 if(column == 0)
305 return g_list_sort(list, (GCompareFunc)account_glist_key_compare_func);
306 else
307 return g_list_sort(list, (GCompareFunc)account_glist_name_compare_func);
308 }
309
310
311
312 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
313
314
315
316
317 /**
318 * account_is_used:
319 *
320 * controls if an account is used by any archive or transaction
321 *
322 * Return value: TRUE if used, FALSE, otherwise
323 */
324 gboolean
325 account_is_used(guint32 key)
326 {
327 Account *acc;
328 GList *list;
329 GList *lst_acc, *lnk_acc;
330 GList *lnk_txn;
331 gboolean retval;
332
333 retval = FALSE;
334 lst_acc = NULL;
335
336 acc = da_acc_get(key);
337 if( g_queue_get_length(acc->txn_queue) > 0 )
338 {
339 retval = TRUE;
340 goto end;
341 }
342
343 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
344 lnk_acc = g_list_first(lst_acc);
345 while (lnk_acc != NULL)
346 {
347 Account *acc = lnk_acc->data;
348
349 if(acc->key != key)
350 {
351 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
352 while (lnk_txn != NULL)
353 {
354 Transaction *entry = lnk_txn->data;
355
356 if( key == entry->kxferacc)
357 {
358 retval = TRUE;
359 goto end;
360 }
361
362 lnk_txn = g_list_next(lnk_txn);
363 }
364 }
365 lnk_acc = g_list_next(lnk_acc);
366 }
367
368 list = g_list_first(GLOBALS->arc_list);
369 while (list != NULL)
370 {
371 Archive *entry = list->data;
372
373 if( key == entry->kacc || key == entry->kxferacc)
374 {
375 retval = TRUE;
376 goto end;
377 }
378
379 list = g_list_next(list);
380 }
381
382 end:
383 g_list_free(lst_acc);
384
385 return retval;
386 }
387
388
389 static gchar *
390 account_get_stripname(gchar *name)
391 {
392 gchar *stripname = g_strdup(name);
393 g_strstrip(stripname);
394
395 return stripname;
396 }
397
398
399 gboolean
400 account_exists(gchar *name)
401 {
402 Account *existitem;
403 gchar *stripname = account_get_stripname(name);
404
405 existitem = da_acc_get_by_name(stripname);
406 g_free(stripname);
407
408 return existitem == NULL ? FALSE : TRUE;
409 }
410
411
412 gboolean
413 account_rename(Account *item, gchar *newname)
414 {
415 Account *existitem;
416 gchar *stripname = account_get_stripname(newname);
417
418 if( strlen(stripname) > 0 )
419 {
420 existitem = da_acc_get_by_name(stripname);
421 if( existitem == NULL )
422 {
423 g_free(item->name);
424 item->name = g_strdup(stripname);
425 return TRUE;
426 }
427
428 g_free(stripname);
429 }
430
431 return FALSE;
432 }
433
434
435 /*
436 * change the account currency
437 * change every txn to currency
438 * ensure dst xfer transaction account will be set to same currency
439 */
440 void account_set_currency(Account *acc, guint32 kcur)
441 {
442 GList *list;
443 Account *dstacc;
444 gboolean *xfer_list;
445 guint32 maxkey, i;
446
447 DB( g_print("\n[account] set currency\n") );
448
449 if(acc->kcur == kcur)
450 {
451 DB( g_print(" - already ok, return\n") );
452 return;
453 }
454
455 DB( g_print(" - set for '%s'\n", acc->name) );
456
457 maxkey = da_acc_get_max_key () + 1;
458 xfer_list = g_malloc0(sizeof(gboolean) * maxkey );
459 DB( g_print(" - alloc for %d account\n", da_acc_length() ) );
460
461 list = g_queue_peek_head_link(acc->txn_queue);
462 while (list != NULL)
463 {
464 Transaction *txn = list->data;
465
466 txn->kcur = kcur;
467 if( (txn->paymode == PAYMODE_INTXFER) && (txn->kxferacc > 0) && (txn->kxfer > 0) )
468 {
469 xfer_list[txn->kxferacc] = TRUE;
470 }
471 list = g_list_next(list);
472 }
473
474 acc->kcur = kcur;
475 DB( g_print(" - '%s'\n", acc->name) );
476
477 for(i=1;i<maxkey;i++)
478 {
479 DB( g_print(" - %d '%d'\n", i, xfer_list[i]) );
480 if( xfer_list[i] == TRUE )
481 {
482 dstacc = da_acc_get(i);
483 account_set_currency(dstacc, kcur);
484 }
485 }
486
487 g_free(xfer_list);
488
489 }
490
491
492 /**
493 * private function to sub transaction amount from account balances
494 */
495 static void account_balances_sub_internal(Account *acc, Transaction *trn)
496 {
497 acc->bal_future -= trn->amount;
498
499 if(trn->date <= GLOBALS->today)
500 acc->bal_today -= trn->amount;
501
502 if(trn->status == TXN_STATUS_RECONCILED)
503 //if(trn->flags & OF_VALID)
504 acc->bal_bank -= trn->amount;
505 }
506
507 /**
508 * private function to add transaction amount from account balances
509 */
510 static void account_balances_add_internal(Account *acc, Transaction *trn)
511 {
512 acc->bal_future += trn->amount;
513
514 if(trn->date <= GLOBALS->today)
515 acc->bal_today += trn->amount;
516
517 if(trn->status == TXN_STATUS_RECONCILED)
518 //if(trn->flags & OF_VALID)
519 acc->bal_bank += trn->amount;
520 }
521
522
523 /**
524 * public function to sub transaction amount from account balances
525 */
526 gboolean account_balances_sub(Transaction *trn)
527 {
528
529 if(!(trn->status == TXN_STATUS_REMIND))
530 //if(!(trn->flags & OF_REMIND))
531 {
532 Account *acc = da_acc_get(trn->kacc);
533 if(acc == NULL) return FALSE;
534 account_balances_sub_internal(acc, trn);
535 return TRUE;
536 }
537 return FALSE;
538 }
539
540
541 /**
542 * public function to add transaction amount from account balances
543 */
544 gboolean account_balances_add(Transaction *trn)
545 {
546 if(!(trn->status == TXN_STATUS_REMIND))
547 //if(!(trn->flags & OF_REMIND))
548 {
549 Account *acc = da_acc_get(trn->kacc);
550 if(acc == NULL) return FALSE;
551 account_balances_add_internal(acc, trn);
552 return TRUE;
553 }
554 return FALSE;
555 }
556
557
558 //todo: optim called 2 times from dsp_mainwindow
559 void account_compute_balances(void)
560 {
561 GList *lst_acc, *lnk_acc;
562 GList *lnk_txn;
563
564 DB( g_print("\naccount_compute_balances start\n") );
565
566 lst_acc = g_hash_table_get_values(GLOBALS->h_acc);
567 lnk_acc = g_list_first(lst_acc);
568 while (lnk_acc != NULL)
569 {
570 Account *acc = lnk_acc->data;
571
572 /* set initial amount */
573 acc->bal_bank = acc->initial;
574 acc->bal_today = acc->initial;
575 acc->bal_future = acc->initial;
576
577 /* add every txn */
578 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
579 while (lnk_txn != NULL)
580 {
581 Transaction *txn = lnk_txn->data;
582
583 if(!(txn->status == TXN_STATUS_REMIND))
584 {
585 account_balances_add_internal(acc, txn);
586 }
587 lnk_txn = g_list_next(lnk_txn);
588 }
589
590 lnk_acc = g_list_next(lnk_acc);
591 }
592 g_list_free(lst_acc);
593
594 DB( g_print("\naccount_compute_balances end\n") );
595
596 }
597
598
599 void account_convert_euro(Account *acc)
600 {
601 GList *lnk_txn;
602
603 lnk_txn = g_queue_peek_head_link(acc->txn_queue);
604 while (lnk_txn != NULL)
605 {
606 Transaction *txn = lnk_txn->data;
607 gdouble oldamount = txn->amount;
608
609 txn->amount = hb_amount_to_euro(oldamount);
610 DB( g_print("%10.6f => %10.6f, %s\n", oldamount, txn->amount, txn->memo) );
611 //todo: sync child xfer
612 lnk_txn = g_list_next(lnk_txn);
613 }
614
615 acc->initial = hb_amount_to_euro(acc->initial);
616 acc->warning = hb_amount_to_euro(acc->warning);
617 acc->minimum = hb_amount_to_euro(acc->minimum);
618 }
619
This page took 0.07209 seconds and 5 git commands to generate.