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