1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2019 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"
23 /****************************************************************************/
25 /****************************************************************************/
34 /* our global datas */
35 extern struct HomeBank
*GLOBALS
;
39 da_acc_free(Account
*item
)
41 DB( g_print("da_acc_free\n") );
44 DB( g_print(" => %d, %s\n", item
->key
, item
->name
) );
48 g_free(item
->bankname
);
51 g_queue_free (item
->txn_queue
);
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 ();
74 DB( g_print("da_acc_destroy\n") );
75 g_hash_table_destroy(GLOBALS
->h_acc
);
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
);
87 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
93 * Return value: the number of elements
98 return g_hash_table_size(GLOBALS
->h_acc
);
102 static void da_acc_max_key_ghfunc(gpointer key
, Account
*item
, guint32
*max_key
)
104 *max_key
= MAX(*max_key
, item
->key
);
109 * da_acc_get_max_key:
111 * Get the biggest key from the GHashTable
113 * Return value: the biggest key value
117 da_acc_get_max_key(void)
121 g_hash_table_foreach(GLOBALS
->h_acc
, (GHFunc
)da_acc_max_key_ghfunc
, &max_key
);
129 * delete an account from the GHashTable
131 * Return value: TRUE if the key was found and deleted
135 da_acc_remove(guint32 key
)
137 DB( g_print("da_acc_remove %d\n", key
) );
139 return g_hash_table_remove(GLOBALS
->h_acc
, &key
);
145 * insert an account into the GHashTable
147 * Return value: TRUE if inserted
151 da_acc_insert(Account
*item
)
155 DB( g_print("da_acc_insert\n") );
157 new_key
= g_new0(guint32
, 1);
158 *new_key
= item
->key
;
159 g_hash_table_insert(GLOBALS
->h_acc
, new_key
, item
);
168 * insert an account into the GHashTable
170 * Return value: TRUE if inserted
174 da_acc_append(Account
*item
)
178 DB( g_print("da_acc_append\n") );
180 existitem
= da_acc_get_by_name( item
->name
);
181 if( existitem
== NULL
)
183 item
->key
= da_acc_get_max_key() + 1;
184 item
->pos
= da_acc_length() + 1;
189 DB( g_print(" -> %s already exist: %d\n", item
->name
, item
->key
) );
195 static gboolean
da_acc_name_grfunc(gpointer key
, Account
*item
, gchar
*name
)
197 if( name
&& item
->name
)
199 if(!strcasecmp(name
, item
->name
))
206 * da_acc_get_by_name:
208 * Get an account structure by its name
210 * Return value: Account * or NULL if not found
214 da_acc_get_by_name(gchar
*rawname
)
216 Account
*retval
= NULL
;
219 DB( g_print("da_acc_get_by_name\n") );
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
);
238 * Get an account structure by key
240 * Return value: Account * or NULL if not found
244 da_acc_get(guint32 key
)
246 //DB( g_print("da_acc_get\n") );
248 return g_hash_table_lookup(GLOBALS
->h_acc
, &key
);
252 void da_acc_consistency(Account
*item
)
254 g_strstrip(item
->name
);
258 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
262 da_acc_debug_list_ghfunc(gpointer key
, gpointer value
, gpointer user_data
)
265 Account
*item
= value
;
267 DB( g_print(" %d :: %s\n", *id
, item
->name
) );
272 da_acc_debug_list(void)
275 DB( g_print("\n** debug **\n") );
277 g_hash_table_foreach(GLOBALS
->h_acc
, da_acc_debug_list_ghfunc
, NULL
);
279 DB( g_print("\n** end debug **\n") );
287 account_glist_name_compare_func(Account
*a
, Account
*b
)
289 return hb_string_utf8_compare(a
->name
, b
->name
);
294 account_glist_key_compare_func(Account
*a
, Account
*b
)
296 return a
->key
- b
->key
;
300 GList
*account_glist_sorted(gint column
)
302 GList
*list
= g_hash_table_get_values(GLOBALS
->h_acc
);
305 return g_list_sort(list
, (GCompareFunc
)account_glist_key_compare_func
);
307 return g_list_sort(list
, (GCompareFunc
)account_glist_name_compare_func
);
312 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */
320 * controls if an account is used by any archive or transaction
322 * Return value: TRUE if used, FALSE, otherwise
325 account_is_used(guint32 key
)
329 GList
*lst_acc
, *lnk_acc
;
336 acc
= da_acc_get(key
);
337 if( g_queue_get_length(acc
->txn_queue
) > 0 )
343 lst_acc
= g_hash_table_get_values(GLOBALS
->h_acc
);
344 lnk_acc
= g_list_first(lst_acc
);
345 while (lnk_acc
!= NULL
)
347 Account
*acc
= lnk_acc
->data
;
351 lnk_txn
= g_queue_peek_head_link(acc
->txn_queue
);
352 while (lnk_txn
!= NULL
)
354 Transaction
*entry
= lnk_txn
->data
;
356 if( key
== entry
->kxferacc
)
362 lnk_txn
= g_list_next(lnk_txn
);
365 lnk_acc
= g_list_next(lnk_acc
);
368 list
= g_list_first(GLOBALS
->arc_list
);
371 Archive
*entry
= list
->data
;
373 if( key
== entry
->kacc
|| key
== entry
->kxferacc
)
379 list
= g_list_next(list
);
383 g_list_free(lst_acc
);
390 account_get_stripname(gchar
*name
)
392 gchar
*stripname
= g_strdup(name
);
393 g_strstrip(stripname
);
400 account_exists(gchar
*name
)
403 gchar
*stripname
= account_get_stripname(name
);
405 existitem
= da_acc_get_by_name(stripname
);
408 return existitem
== NULL
? FALSE
: TRUE
;
413 account_rename(Account
*item
, gchar
*newname
)
416 gchar
*stripname
= account_get_stripname(newname
);
418 if( strlen(stripname
) > 0 )
420 existitem
= da_acc_get_by_name(stripname
);
421 if( existitem
== NULL
)
424 item
->name
= g_strdup(stripname
);
436 * change the account currency
437 * change every txn to currency
438 * ensure dst xfer transaction account will be set to same currency
440 void account_set_currency(Account
*acc
, guint32 kcur
)
447 DB( g_print("\n[account] set currency\n") );
449 if(acc
->kcur
== kcur
)
451 DB( g_print(" - already ok, return\n") );
455 DB( g_print(" - set for '%s'\n", acc
->name
) );
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() ) );
461 list
= g_queue_peek_head_link(acc
->txn_queue
);
464 Transaction
*txn
= list
->data
;
467 if( (txn
->paymode
== PAYMODE_INTXFER
) && (txn
->kxferacc
> 0) && (txn
->kxfer
> 0) )
469 xfer_list
[txn
->kxferacc
] = TRUE
;
471 list
= g_list_next(list
);
475 DB( g_print(" - '%s'\n", acc
->name
) );
477 for(i
=1;i
<maxkey
;i
++)
479 DB( g_print(" - %d '%d'\n", i
, xfer_list
[i
]) );
480 if( xfer_list
[i
] == TRUE
)
482 dstacc
= da_acc_get(i
);
483 account_set_currency(dstacc
, kcur
);
493 * private function to sub transaction amount from account balances
495 static void account_balances_sub_internal(Account
*acc
, Transaction
*trn
)
497 acc
->bal_future
-= trn
->amount
;
499 if(trn
->date
<= GLOBALS
->today
)
500 acc
->bal_today
-= trn
->amount
;
502 if(trn
->status
== TXN_STATUS_RECONCILED
)
503 //if(trn->flags & OF_VALID)
504 acc
->bal_bank
-= trn
->amount
;
508 * private function to add transaction amount from account balances
510 static void account_balances_add_internal(Account
*acc
, Transaction
*trn
)
512 acc
->bal_future
+= trn
->amount
;
514 if(trn
->date
<= GLOBALS
->today
)
515 acc
->bal_today
+= trn
->amount
;
517 if(trn
->status
== TXN_STATUS_RECONCILED
)
518 //if(trn->flags & OF_VALID)
519 acc
->bal_bank
+= trn
->amount
;
524 * public function to sub transaction amount from account balances
526 gboolean
account_balances_sub(Transaction
*trn
)
529 if(!(trn
->status
== TXN_STATUS_REMIND
))
530 //if(!(trn->flags & OF_REMIND))
532 Account
*acc
= da_acc_get(trn
->kacc
);
533 if(acc
== NULL
) return FALSE
;
534 account_balances_sub_internal(acc
, trn
);
542 * public function to add transaction amount from account balances
544 gboolean
account_balances_add(Transaction
*trn
)
546 if(!(trn
->status
== TXN_STATUS_REMIND
))
547 //if(!(trn->flags & OF_REMIND))
549 Account
*acc
= da_acc_get(trn
->kacc
);
550 if(acc
== NULL
) return FALSE
;
551 account_balances_add_internal(acc
, trn
);
558 //todo: optim called 2 times from dsp_mainwindow
559 void account_compute_balances(void)
561 GList
*lst_acc
, *lnk_acc
;
564 DB( g_print("\naccount_compute_balances start\n") );
566 lst_acc
= g_hash_table_get_values(GLOBALS
->h_acc
);
567 lnk_acc
= g_list_first(lst_acc
);
568 while (lnk_acc
!= NULL
)
570 Account
*acc
= lnk_acc
->data
;
572 /* set initial amount */
573 acc
->bal_bank
= acc
->initial
;
574 acc
->bal_today
= acc
->initial
;
575 acc
->bal_future
= acc
->initial
;
578 lnk_txn
= g_queue_peek_head_link(acc
->txn_queue
);
579 while (lnk_txn
!= NULL
)
581 Transaction
*txn
= lnk_txn
->data
;
583 if(!(txn
->status
== TXN_STATUS_REMIND
))
585 account_balances_add_internal(acc
, txn
);
587 lnk_txn
= g_list_next(lnk_txn
);
590 lnk_acc
= g_list_next(lnk_acc
);
592 g_list_free(lst_acc
);
594 DB( g_print("\naccount_compute_balances end\n") );
599 void account_convert_euro(Account
*acc
)
603 lnk_txn
= g_queue_peek_head_link(acc
->txn_queue
);
604 while (lnk_txn
!= NULL
)
606 Transaction
*txn
= lnk_txn
->data
;
607 gdouble oldamount
= txn
->amount
;
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
);
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
);