diff --git a/ChangeLog b/ChangeLog index 9d4db1f52e..f07bd814f5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2000-12-14 Robert Graham Merkel + + * src/engine/gnc-associate-account.[ch] : New files with code for associating + income/expense accounts with a stock account. Not yet hooked up to the UI. + 2000-12-13 Rob Browning * src/scm/text-export.scm: kill pointer-token* diff --git a/src/engine/Makefile.am b/src/engine/Makefile.am index 9e38f1e610..ce85503782 100644 --- a/src/engine/Makefile.am +++ b/src/engine/Makefile.am @@ -24,6 +24,7 @@ libgncengine_la_SOURCES = \ io-gncxml-w.c \ md5.c \ kvp_frame.c \ + gnc-associate-account.c \ gnc-book.c \ gnc-commodity.c \ gnc-engine.c \ @@ -56,6 +57,7 @@ noinst_HEADERS = \ io-gncxml.h \ md5.h \ kvp_frame.h \ + gnc-associate-account.h \ gnc-book.h \ gnc-commodity.h \ gnc-engine.h \ diff --git a/src/engine/gnc-associate-account.c b/src/engine/gnc-associate-account.c new file mode 100644 index 0000000000..0cdd5289d1 --- /dev/null +++ b/src/engine/gnc-associate-account.c @@ -0,0 +1,377 @@ +/********************************************************************\ + * gnc-associate-account.h : api for associating income and * + * expense accounts with stock/mutual fund accounts, for tracking * + * dividends, brokerage, and other stock-related expenses and * + * income so that they can be reported * + * Copyright 2000 Gnumatic Incorporated * + * Written by Robert Merkel * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * + * Boston, MA 02111-1307, USA gnu@gnu.org * +\********************************************************************/ + +#include "config.h" + +#include "gnc-associate-account.h" +#include "gnc-engine-util.h" + +static short module = MOD_ENGINE; + +/* Maps GNCTrackingIncomeCategory to string keys. If this enum changes, update */ + +static char * income_to_key[] = {"income-miscellaneous", + "income-interest", + "income-dividend" + "income-long-term-capital-gain", + "income-short-term-capital-gain"}; + +/* Maps GNCTrackingExpenseCategory to string keys. If this enum changes, update */ + +static char * expense_to_key[] = {"expense-miscellaneous", + "expense-commission"}; + +static kvp_frame * +get_assoc_acc_frame(kvp_frame *account_frame) +{ + kvp_frame *assoc_acc_frame; + kvp_value *assoc_acc_frame_kvpvalue = kvp_frame_get_slot(account_frame, + "associated-accounts"); + assoc_acc_frame = kvp_value_get_frame(assoc_acc_frame_kvpvalue); + if(!assoc_acc_frame) + { + assoc_acc_frame = kvp_frame_new(); + assoc_acc_frame_kvpvalue = kvp_value_new_frame(assoc_acc_frame); + kvp_frame_set_slot(account_frame, "associated-accounts", + assoc_acc_frame_kvpvalue); + } + + return assoc_acc_frame; +} + +static void +back_associate_expense_accounts(Account *stock_account, + GList *accounts, + GNCTrackingExpenseCategory category) +{ + kvp_frame *acc_frame; + kvp_value *val, *stock_acc_guid_kvpval, *stock_acc_category_kvpval; + const GUID *stock_acc_guid; + const GUID *existing_acc_guid; + stock_acc_guid = xaccAccountGetGUID(stock_account); + stock_acc_guid_kvpval = kvp_value_new_guid(stock_acc_guid); + + stock_acc_category_kvpval = kvp_value_new_string(expense_to_key[category]); + + for(; accounts; accounts = g_list_next(accounts)) + { + acc_frame = xaccAccountGetSlots(accounts->data); + g_return_if_fail(val = kvp_frame_get_slot(acc_frame, + "associated-stock-account")); + g_return_if_fail(kvp_value_get_type(val) == KVP_TYPE_GUID); + existing_acc_guid = kvp_value_get_guid(val); + + g_return_if_fail(xaccGUIDType(existing_acc_guid) == GNC_ID_NONE); + + kvp_frame_set_slot(acc_frame, "associated-stock-account", + stock_acc_guid_kvpval); + kvp_frame_set_slot(acc_frame, "associated-stock-account-category", + stock_acc_category_kvpval); + } + + return; +} + static void +back_associate_income_accounts(Account *stock_account, + GList *accounts, + GNCTrackingIncomeCategory category) +{ + kvp_frame *acc_frame; + kvp_value *val, *stock_acc_guid_kvpval, *stock_acc_category_kvpval; + const GUID *stock_acc_guid; + const GUID *existing_acc_guid; + stock_acc_guid = xaccAccountGetGUID(stock_account); + stock_acc_guid_kvpval = kvp_value_new_guid(stock_acc_guid); + + stock_acc_category_kvpval = kvp_value_new_string(income_to_key[category]); + + for(; accounts; accounts = g_list_next(accounts)) + { + acc_frame = xaccAccountGetSlots(accounts->data); + g_return_if_fail(val = kvp_frame_get_slot(acc_frame, + "associated-stock-account")); + g_return_if_fail(kvp_value_get_type(val) == KVP_TYPE_GUID); + existing_acc_guid = kvp_value_get_guid(val); + + g_return_if_fail(xaccGUIDType(existing_acc_guid) == GNC_ID_NONE); + + kvp_frame_set_slot(acc_frame, "associated-stock-account", + stock_acc_guid_kvpval); + kvp_frame_set_slot(acc_frame, "associated-stock-account-category", + stock_acc_category_kvpval); + } + + return; +} + +static kvp_value * +make_kvpd_on_list(GList *account_list) +{ + GList *iter; + kvp_value *retval; + kvp_value *guid_kvp; + GList *kvp_acc_list = NULL; + const GUID *acc_id; + + for(iter = g_list_last(account_list); + iter; + iter = g_list_previous(iter)) + { + GNCAccountType type; + Account *current_account; + current_account = iter->data; + type = xaccAccountGetType(current_account); + g_return_val_if_fail(type == INCOME || type == EXPENSE, NULL); + + acc_id = xaccAccountGetGUID(current_account); + guid_kvp = kvp_value_new_guid(acc_id); + kvp_acc_list = g_list_prepend(kvp_acc_list, guid_kvp); + } + + retval = kvp_value_new_glist_nc(kvp_acc_list); + return retval; +} + +static GList * +de_kvp_account_list(kvp_value *kvpd_list) +{ + GList *guid_account_list = kvp_value_get_glist(kvpd_list); + if (guid_account_list) + { + GList *expense_acc_list= NULL; + for(; guid_account_list; guid_account_list=g_list_next(guid_account_list)) + { + g_list_prepend(expense_acc_list, xaccAccountLookup(guid_account_list->data)); + } + + g_list_reverse(expense_acc_list); + return expense_acc_list; + } + else + { + return NULL; + } +} + +void +gnc_tracking_associate_income_accounts(Account *stock_account, + GNCTrackingIncomeCategory category, + GList *account_list) +{ + kvp_frame *account_frame, *inc_account_frame; + kvp_value *kvpd_on_account_list; + GNCAccountType type; + g_return_if_fail(stock_account); + type = xaccAccountGetType(stock_account); + g_return_if_fail(type == STOCK || type == MUTUAL); + account_frame = xaccAccountGetSlots(stock_account); + g_return_if_fail(account_frame); + g_return_if_fail(category >= 0); + g_return_if_fail(category < GNC_TR_INC_N_CATEGORIES); + + inc_account_frame = get_assoc_acc_frame(account_frame); + kvpd_on_account_list = make_kvpd_on_list(account_list); + + back_associate_income_accounts(stock_account, account_list, category); + + kvp_frame_set_slot(inc_account_frame, + income_to_key[category], + kvpd_on_account_list); + return; +} + +void gnc_tracking_asssociate_expense_account(Account *stock_account, + GNCTrackingExpenseCategory category, + GList *account_list) +{ + kvp_frame *account_frame, *expense_acc_frame; + kvp_value *kvpd_on_account_list; + GNCAccountType type; + g_return_if_fail(stock_account); + type = xaccAccountGetType(stock_account); + g_return_if_fail(type == STOCK || type == MUTUAL); + account_frame = xaccAccountGetSlots(stock_account); + g_return_if_fail(account_frame); + g_return_if_fail(category >= 0); + g_return_if_fail(category < GNC_TR_EXP_N_CATEGORIES); + + expense_acc_frame = get_assoc_acc_frame(account_frame); + kvpd_on_account_list = make_kvpd_on_list(account_list); + + back_associate_expense_accounts(stock_account, account_list, category); + + kvp_frame_set_slot(expense_acc_frame, + expense_to_key[category], + kvpd_on_account_list); + return; +} + +GList *gnc_tracking_find_expense_accounts(Account *stock_account, + GNCTrackingExpenseCategory category) +{ + + GNCAccountType type; + kvp_frame *account_frame, *expense_acc_frame; + kvp_value *kvpd_on_account_list; + + type = xaccAccountGetType(stock_account); + g_return_val_if_fail(category >= 0 && category < GNC_TR_EXP_N_CATEGORIES, + NULL); + g_return_val_if_fail(type == STOCK || type == MUTUAL, NULL); + + account_frame = xaccAccountGetSlots(stock_account); + g_return_val_if_fail(account_frame, NULL); + + expense_acc_frame = get_assoc_acc_frame(account_frame); + kvpd_on_account_list = kvp_frame_get_slot(account_frame, + expense_to_key[category]); + + return de_kvp_account_list(kvpd_on_account_list); +} +GList *gnc_tracking_find_income_accounts(Account *stock_account, + GNCTrackingIncomeCategory category) +{ + + GNCAccountType type; + kvp_frame *account_frame, *income_acc_frame; + kvp_value *kvpd_on_account_list; + + type = xaccAccountGetType(stock_account); + g_return_val_if_fail(category >= 0 && category < GNC_TR_INC_N_CATEGORIES, + NULL); + g_return_val_if_fail(type == STOCK || type == MUTUAL, NULL); + + account_frame = xaccAccountGetSlots(stock_account); + g_return_val_if_fail(account_frame, NULL); + + income_acc_frame = get_assoc_acc_frame(account_frame); + kvpd_on_account_list = kvp_frame_get_slot(income_acc_frame, + income_to_key[category]); + + return de_kvp_account_list(kvpd_on_account_list); +} + +GList *gnc_tracking_find_all_expense_accounts(Account *stock_account) +{ + GList *complete_list=NULL; + int i; + + for(i = 0; i < GNC_TR_EXP_N_CATEGORIES; i++) + { + g_list_concat(complete_list, + gnc_tracking_find_expense_accounts(stock_account, + i)); + } + return complete_list; +} + +GList *gnc_tracking_find_all_income_accounts(Account *stock_account) +{ + GList *complete_list= NULL; + int i; + + for(i = 0; i < GNC_TR_EXP_N_CATEGORIES; i++) + { + g_list_concat(complete_list, + gnc_tracking_find_expense_accounts(stock_account, + i)); + } + return complete_list; +} + +Account *gnc_tracking_find_stock_account(Account *inc_or_expense_acc) +{ + return NULL; +} + +void +gnc_tracking_dissociate_account(Account *inc_or_expense_account) +{ + GNCAccountType type; + kvp_frame *stock_account_kvpframe, *assoc_acc_kvpframe, *current_account_kvpframe; + kvp_value *stock_account_kvpval, *acc_list_kvpval, *category_kvpval; + const GUID *stock_account_guid, *inc_or_expense_account_guid, *current_guid; + Account *stock_account; + char *category_name; + GList *assoc_acc_list, *assoc_acc_list_start; + type = xaccAccountGetType(inc_or_expense_account); + + g_return_if_fail(type == INCOME || type == EXPENSE); + + current_account_kvpframe = xaccAccountGetSlots(inc_or_expense_account); + + stock_account_kvpval = kvp_frame_get_slot(current_account_kvpframe, + "associated-stock-account"); + + stock_account_guid = kvp_value_get_guid(stock_account_kvpval); + if(xaccGUIDType(stock_account_guid) == GNC_ID_NULL) + return; + + category_kvpval = kvp_frame_get_slot(current_account_kvpframe, + "associated-stock-account-category"); + category_name = kvp_value_get_string(category_kvpval); + + + inc_or_expense_account_guid = xaccAccountGetGUID(inc_or_expense_account); + stock_account = xaccAccountLookup(stock_account_guid); + + stock_account_kvpframe = xaccAccountGetSlots(stock_account); + + g_return_if_fail(stock_account_kvpval = kvp_frame_get_slot(stock_account_kvpframe, + "associated-accounts")); + + assoc_acc_kvpframe = kvp_value_get_frame(stock_account_kvpval); + + g_return_if_fail(acc_list_kvpval = kvp_frame_get_slot(assoc_acc_kvpframe, + category_name)); + g_return_if_fail(assoc_acc_list_start = kvp_value_get_glist(acc_list_kvpval)); + + for(assoc_acc_list = assoc_acc_list_start;assoc_acc_list;assoc_acc_list = g_list_next(assoc_acc_list)) + { + g_return_if_fail(current_guid = kvp_value_get_guid(assoc_acc_list->data)); + if(guid_equal(current_guid, inc_or_expense_account_guid)) + { + assoc_acc_list_start = g_list_remove_link(assoc_acc_list_start, assoc_acc_list); + g_list_free_1(assoc_acc_list); + acc_list_kvpval = kvp_value_new_glist_nc(assoc_acc_list); + kvp_frame_set_slot(assoc_acc_kvpframe, + category_name, + acc_list_kvpval); + return; + } + } + /* should never happen */ + PERR("Income/Expense account and stock account disagree on association"); +} + + + + + + + + + diff --git a/src/engine/gnc-associate-account.h b/src/engine/gnc-associate-account.h new file mode 100644 index 0000000000..09335475dc --- /dev/null +++ b/src/engine/gnc-associate-account.h @@ -0,0 +1,89 @@ +/********************************************************************\ + * gnc-associate-account.h : api for associating income and * + * expense accounts with stock/mutual fund accounts, for tracking * + * dividends, brokerage, and other stock-related expenses and * + * income so that they can be reported * + * Copyright 2000 Gnumatic Incorporated * + * Written by Robert Merkel * + * + * WARNING WARNING WARNING: THIS CODE IS TOTALLY UNTESTED. * + * THE ONLY REASON IT'S IN CVS IS FOR SAFEKEEPING * + * * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * + * Boston, MA 02111-1307, USA gnu@gnu.org * +\********************************************************************/ + +#include +#include + +/* + * account_list is a list of account *'s, all of which much be expense + * accounts + */ +typedef enum {GNC_TR_INC_MISC, + GNC_TR_INC_INTEREST, + GNC_TR_INC__DIVIDEND, + GNC_TR_INC_LT_CG, + GNC_TR_INC_ST_CG, + GNC_TR_INC_N_CATEGORIES} GNCTrackingIncomeCategory; + +typedef enum {GNC_TR_EXP_MISC, + GNC_TR_EXP_COMMISSION, + GNC_TR_EXP_N_CATEGORIES} GNCTrackingExpenseCategory; + + +/* + * account_list is a list of account *'s, all of which much be expense + * accounts. You can clear associations by setting account_list to NULL + */ + +void gnc_tracking_associate_income_accounts(Account *stock_account, + GNCTrackingIncomeCategory category, + GList *account_list); + + +void gnc_tracking_asssociate_expense_account(Account *stock_account, + GNCTrackingExpenseCategory category, + GList *account_list); + +/* + * returns a list of account *'s, + * returns null if no association specified + */ + +GList *gnc_tracking_find_expense_accounts(Account *stock_account, + GNCTrackingExpenseCategory category); + +GList *gnc_tracking_find_income_accounts(Account *stock_account, + GNCTrackingIncomeCategory category); + +/* for ROI purposes we don't care about categories, these are "grab +all" for that purpose */ + +GList *gnc_tracking_find_all_expense_accounts(Account *stock_account); + +GList *gnc_tracking_find_all_income_accounts(Account *stock_account); + + +/* + * reverse lookup - obviously returns a stock account (or NULL if none + * associated), and argument must be an income or expense account + */ +Account *gnc_tracking_find_stock_account(Account *inc_or_expense_acc); + +void gnc_tracking_dissociate_account(Account *inc_or_expense_account);