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