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