1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2016 Maxime DOYEN
4 * This file is part of HomeBank.
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.
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.
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/>.
21 #include "hb-account.h"
26 /****************************************************************************/
28 /****************************************************************************/
37 /* our global datas */
38 extern struct HomeBank
*GLOBALS
;
42 da_acc_free(Account
*item
)
44 DB( g_print("da_acc_free\n") );
47 DB( g_print(" => %d, %s\n", item
->key
, item
->name
) );
49 g_free(item
->imp_name
);
52 g_free(item
->bankname
);
55 g_queue_free (item
->txn_queue
);
67 DB( g_print("da_acc_malloc\n") );
68 item
= rc_alloc(sizeof(Account
));
69 item
->txn_queue
= g_queue_new ();
77 DB( g_print("da_acc_destroy\n") );
78 g_hash_table_destroy(GLOBALS
->h_acc
);
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
);
90 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
91 static void da_acc_max_key_ghfunc(gpointer key
, Account
*item
, guint32
*max_key
)
93 *max_key
= MAX(*max_key
, item
->key
);
96 static gboolean
da_acc_name_grfunc(gpointer key
, Account
*item
, gchar
*name
)
98 if( name
&& item
->name
)
100 if(!strcasecmp(name
, item
->name
))
106 static gboolean
da_acc_imp_name_grfunc(gpointer key
, Account
*item
, gchar
*name
)
108 if( name
&& item
->imp_name
)
110 if(!strcasecmp(name
, item
->imp_name
))
119 * Return value: the number of elements
124 return g_hash_table_size(GLOBALS
->h_acc
);
131 * delete an account from the GHashTable
133 * Return value: TRUE if the key was found and deleted
137 da_acc_remove(guint32 key
)
139 DB( g_print("da_acc_remove %d\n", key
) );
141 return g_hash_table_remove(GLOBALS
->h_acc
, &key
);
147 * insert an account into the GHashTable
149 * Return value: TRUE if inserted
153 da_acc_insert(Account
*item
)
157 DB( g_print("da_acc_insert\n") );
159 new_key
= g_new0(guint32
, 1);
160 *new_key
= item
->key
;
161 g_hash_table_insert(GLOBALS
->h_acc
, new_key
, item
);
163 GValue item_val
= G_VALUE_INIT
;
164 ext_hook("account_inserted", EXT_ACCOUNT(&item_val
, item
), NULL
);
173 * insert an account into the GHashTable
175 * Return value: TRUE if inserted
179 da_acc_append(Account
*item
)
184 DB( g_print("da_acc_append\n") );
186 /* ensure no duplicate */
187 g_strstrip(item
->name
);
188 if(item
->name
!= NULL
)
190 existitem
= da_acc_get_by_name( item
->name
);
191 if( existitem
== NULL
)
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;
198 DB( g_print(" -> insert id: %d\n", *new_key
) );
200 g_hash_table_insert(GLOBALS
->h_acc
, new_key
, item
);
202 GValue item_val
= G_VALUE_INIT
;
203 ext_hook("account_inserted", EXT_ACCOUNT(&item_val
, item
), NULL
);
209 DB( g_print(" -> %s already exist: %d\n", item
->name
, item
->key
) );
215 * da_acc_get_max_key:
217 * Get the biggest key from the GHashTable
219 * Return value: the biggest key value
223 da_acc_get_max_key(void)
227 g_hash_table_foreach(GLOBALS
->h_acc
, (GHFunc
)da_acc_max_key_ghfunc
, &max_key
);
235 * da_acc_get_by_name:
237 * Get an account structure by its name
239 * Return value: Account * or NULL if not found
243 da_acc_get_by_name(gchar
*name
)
245 DB( g_print("da_acc_get_by_name\n") );
247 return g_hash_table_find(GLOBALS
->h_acc
, (GHRFunc
)da_acc_name_grfunc
, name
);
251 da_acc_get_by_imp_name(gchar
*name
)
253 DB( g_print("da_acc_get_by_imp_name\n") );
255 return g_hash_table_find(GLOBALS
->h_acc
, (GHRFunc
)da_acc_imp_name_grfunc
, name
);
262 * Get an account structure by key
264 * Return value: Account * or NULL if not found
268 da_acc_get(guint32 key
)
270 //DB( g_print("da_acc_get\n") );
272 return g_hash_table_lookup(GLOBALS
->h_acc
, &key
);
276 void da_acc_consistency(Account
*item
)
278 g_strstrip(item
->name
);
282 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
286 da_acc_debug_list_ghfunc(gpointer key
, gpointer value
, gpointer user_data
)
289 Account
*item
= value
;
291 DB( g_print(" %d :: %s\n", *id
, item
->name
) );
296 da_acc_debug_list(void)
299 DB( g_print("\n** debug **\n") );
301 g_hash_table_foreach(GLOBALS
->h_acc
, da_acc_debug_list_ghfunc
, NULL
);
303 DB( g_print("\n** end debug **\n") );
311 account_glist_name_compare_func(Account
*a
, Account
*b
)
313 return hb_string_utf8_compare(a
->name
, b
->name
);
318 account_glist_key_compare_func(Account
*a
, Account
*b
)
320 return a
->key
- b
->key
;
324 GList
*account_glist_sorted(gint column
)
326 GList
*list
= g_hash_table_get_values(GLOBALS
->h_acc
);
329 return g_list_sort(list
, (GCompareFunc
)account_glist_key_compare_func
);
331 return g_list_sort(list
, (GCompareFunc
)account_glist_name_compare_func
);
336 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
344 * controls if an account is used by any archive or transaction
346 * Return value: TRUE if used, FALSE, otherwise
349 account_is_used(guint32 key
)
353 GList
*lst_acc
, *lnk_acc
;
360 acc
= da_acc_get(key
);
361 if( g_queue_get_length(acc
->txn_queue
) > 0 )
367 lst_acc
= g_hash_table_get_values(GLOBALS
->h_acc
);
368 lnk_acc
= g_list_first(lst_acc
);
369 while (lnk_acc
!= NULL
)
371 Account
*acc
= lnk_acc
->data
;
375 lnk_txn
= g_queue_peek_head_link(acc
->txn_queue
);
376 while (lnk_txn
!= NULL
)
378 Transaction
*entry
= lnk_txn
->data
;
380 if( key
== entry
->kxferacc
)
386 lnk_txn
= g_list_next(lnk_txn
);
389 lnk_acc
= g_list_next(lnk_acc
);
392 list
= g_list_first(GLOBALS
->arc_list
);
395 Archive
*entry
= list
->data
;
397 if( key
== entry
->kacc
|| key
== entry
->kxferacc
)
403 list
= g_list_next(list
);
407 g_list_free(lst_acc
);
414 account_get_stripname(gchar
*name
)
416 gchar
*stripname
= g_strdup(name
);
417 g_strstrip(stripname
);
424 account_exists(gchar
*name
)
427 gchar
*stripname
= account_get_stripname(name
);
429 existitem
= da_acc_get_by_name(stripname
);
432 return existitem
== NULL
? FALSE
: TRUE
;
437 account_rename(Account
*item
, gchar
*newname
)
440 gchar
*stripname
= account_get_stripname(newname
);
442 existitem
= da_acc_get_by_name(stripname
);
443 if( existitem
== NULL
)
446 item
->name
= g_strdup(stripname
);
457 * change the account currency
458 * change every txn to currency
459 * ensure dst xfer transaction account will be set to same currency
461 void account_set_currency(Account
*acc
, guint32 kcur
)
468 DB( g_print("\n[account] set currency\n") );
470 if(acc
->kcur
== kcur
)
472 DB( g_print(" - already ok, return\n") );
476 DB( g_print(" - set for '%s'\n", acc
->name
) );
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() ) );
482 list
= g_queue_peek_head_link(acc
->txn_queue
);
485 Transaction
*txn
= list
->data
;
488 if( (txn
->paymode
== PAYMODE_INTXFER
) && (txn
->kxferacc
> 0) && (txn
->kxfer
> 0) )
490 xfer_list
[txn
->kxferacc
] = TRUE
;
492 list
= g_list_next(list
);
496 DB( g_print(" - '%s'\n", acc
->name
) );
498 for(i
=1;i
<maxkey
;i
++)
500 DB( g_print(" - %d '%d'\n", i
, xfer_list
[i
]) );
501 if( xfer_list
[i
] == TRUE
)
503 dstacc
= da_acc_get(i
);
504 account_set_currency(dstacc
, kcur
);
514 * private function to sub transaction amount from account balances
516 static void account_balances_sub_internal(Account
*acc
, Transaction
*trn
)
518 acc
->bal_future
-= trn
->amount
;
520 if(trn
->date
<= GLOBALS
->today
)
521 acc
->bal_today
-= trn
->amount
;
523 if(trn
->status
== TXN_STATUS_RECONCILED
)
524 //if(trn->flags & OF_VALID)
525 acc
->bal_bank
-= trn
->amount
;
529 * private function to add transaction amount from account balances
531 static void account_balances_add_internal(Account
*acc
, Transaction
*trn
)
533 acc
->bal_future
+= trn
->amount
;
535 if(trn
->date
<= GLOBALS
->today
)
536 acc
->bal_today
+= trn
->amount
;
538 if(trn
->status
== TXN_STATUS_RECONCILED
)
539 //if(trn->flags & OF_VALID)
540 acc
->bal_bank
+= trn
->amount
;
545 * public function to sub transaction amount from account balances
547 gboolean
account_balances_sub(Transaction
*trn
)
550 if(!(trn
->status
== TXN_STATUS_REMIND
))
551 //if(!(trn->flags & OF_REMIND))
553 Account
*acc
= da_acc_get(trn
->kacc
);
554 if(acc
== NULL
) return FALSE
;
555 account_balances_sub_internal(acc
, trn
);
563 * public function to add transaction amount from account balances
565 gboolean
account_balances_add(Transaction
*trn
)
567 if(!(trn
->status
== TXN_STATUS_REMIND
))
568 //if(!(trn->flags & OF_REMIND))
570 Account
*acc
= da_acc_get(trn
->kacc
);
571 if(acc
== NULL
) return FALSE
;
572 account_balances_add_internal(acc
, trn
);
579 //todo: optim called 2 times from dsp_mainwindow
580 void account_compute_balances(void)
582 GList
*lst_acc
, *lnk_acc
;
585 DB( g_print("\naccount_compute_balances start\n") );
587 lst_acc
= g_hash_table_get_values(GLOBALS
->h_acc
);
588 lnk_acc
= g_list_first(lst_acc
);
589 while (lnk_acc
!= NULL
)
591 Account
*acc
= lnk_acc
->data
;
593 /* set initial amount */
594 acc
->bal_bank
= acc
->initial
;
595 acc
->bal_today
= acc
->initial
;
596 acc
->bal_future
= acc
->initial
;
599 lnk_txn
= g_queue_peek_head_link(acc
->txn_queue
);
600 while (lnk_txn
!= NULL
)
602 account_balances_add_internal(acc
, lnk_txn
->data
);
603 lnk_txn
= g_list_next(lnk_txn
);
606 lnk_acc
= g_list_next(lnk_acc
);
608 g_list_free(lst_acc
);
610 DB( g_print("\naccount_compute_balances end\n") );
615 void account_convert_euro(Account
*acc
)
619 lnk_txn
= g_queue_peek_head_link(acc
->txn_queue
);
620 while (lnk_txn
!= NULL
)
622 Transaction
*txn
= lnk_txn
->data
;
623 gdouble oldamount
= txn
->amount
;
625 txn
->amount
= hb_amount_to_euro(oldamount
);
626 DB( g_print("%10.6f => %10.6f, %s\n", oldamount
, txn
->amount
, txn
->wording
) );
627 //todo: sync child xfer
628 lnk_txn
= g_list_next(lnk_txn
);
631 acc
->initial
= hb_amount_to_euro(acc
->initial
);
632 acc
->warning
= hb_amount_to_euro(acc
->warning
);
633 acc
->minimum
= hb_amount_to_euro(acc
->minimum
);