diff --git a/gnucash/gnome/dialog-lot-viewer.c b/gnucash/gnome/dialog-lot-viewer.c index 061617a9ab..4fa86b9471 100644 --- a/gnucash/gnome/dialog-lot-viewer.c +++ b/gnucash/gnome/dialog-lot-viewer.c @@ -46,8 +46,10 @@ #include "dialog-utils.h" #include "dialog-lot-viewer.h" #include "gnc-component-manager.h" +#include "gnc-event.h" #include "gnc-prefs.h" #include "gnc-ui-util.h" +#include "gnc-session.h" #include "gnc-window.h" #include "misc-gnome-utils.h" #include "tree-view-utils.h" @@ -97,28 +99,31 @@ enum split_cols struct _GNCLotViewer { - GtkWidget * window; + GtkWidget * window; #ifdef LOTS_READY_FOR_SHOWTIME - GtkButton * regview_button; + GtkButton * regview_button; #endif - GtkButton * delete_button; - GtkButton * scrub_lot_button; - GtkButton * new_lot_button; - GtkTreeView * lot_view; - GtkListStore * lot_store; - GtkTextView * lot_notes; - GtkEntry * title_entry; - GtkTreeView * split_in_lot_view; - GtkListStore * split_in_lot_store; - GtkTreeView * split_free_view; - GtkListStore * split_free_store; - GtkWidget * split_hpaned; - GtkButton * add_split_to_lot_button; - GtkButton * remove_split_from_lot_button; + GtkButton * delete_button; + GtkButton * scrub_lot_button; + GtkButton * new_lot_button; + GtkTreeView * lot_view; + GtkListStore * lot_store; + GtkTextView * lot_notes; + GtkEntry * title_entry; + GtkTreeView * split_in_lot_view; + GtkListStore * split_in_lot_store; + GtkTreeView * split_free_view; + GtkListStore * split_free_store; + GtkWidget * split_hpaned; + GtkButton * add_split_to_lot_button; + GtkButton * remove_split_from_lot_button; GtkToggleButton * only_show_open_lots_checkbutton; + GtkToggleButton * show_adjusted_amounts_checkbutton; - Account * account; - GNCLot * selected_lot; + Account * account; + GNCLot * selected_lot; + gint component_id; + GncGUID account_id; }; static void gnc_lot_viewer_fill (GNCLotViewer *lv); @@ -130,9 +135,6 @@ static void gnc_split_viewer_fill (GNCLotViewer *lv, GtkListStore *store, SplitL void lv_title_entry_changed_cb (GtkEntry *ent, gpointer user_data); void lv_response_cb (GtkDialog *dialog, gint response, gpointer data); void lv_window_destroy_cb (GtkWidget *object, gpointer user_data); -void lv_paned_notify_cb (GObject *gobject, - GParamSpec *pspec, - gpointer user_data); /* ======================================================================== */ /* Get the realized gains for this lot. This routine or a variant of it @@ -146,12 +148,12 @@ find_first_currency (GNCLot *lot) { SplitList *split_list, *node; - split_list = gnc_lot_get_split_list(lot); + split_list = gnc_lot_get_split_list (lot); for (node = split_list; node; node = node->next) { Split *s = node->data; Transaction *trans; - if (FALSE == gnc_numeric_zero_p(xaccSplitGetAmount(s))) continue; + if (FALSE == gnc_numeric_zero_p (xaccSplitGetAmount (s))) continue; trans = xaccSplitGetParent (s); return xaccTransGetCurrency (trans); } @@ -161,21 +163,21 @@ find_first_currency (GNCLot *lot) static gnc_numeric get_realized_gains (GNCLot *lot, gnc_commodity *currency) { - gnc_numeric zero = gnc_numeric_zero(); + gnc_numeric zero = gnc_numeric_zero (); gnc_numeric gains = zero; SplitList *split_list, *node; if (!currency) return zero; - split_list = gnc_lot_get_split_list(lot); + split_list = gnc_lot_get_split_list (lot); for (node = split_list; node; node = node->next) { Split *s = node->data; Transaction *trans; - if (FALSE == gnc_numeric_zero_p(xaccSplitGetAmount(s))) continue; + if (FALSE == gnc_numeric_zero_p (xaccSplitGetAmount (s))) continue; trans = xaccSplitGetParent (s); - if (FALSE == gnc_commodity_equal (xaccTransGetCurrency(trans), currency)) continue; + if (FALSE == gnc_commodity_equal (xaccTransGetCurrency (trans), currency)) continue; gains = gnc_numeric_add (gains, xaccSplitGetValue (s), GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED); } @@ -195,16 +197,7 @@ lv_show_splits_in_lot (GNCLotViewer *lv) if (NULL == lot) return; split_list = gnc_lot_get_split_list (lot); - gnc_split_viewer_fill(lv, lv->split_in_lot_store, split_list); -} - -/* ======================================================================== */ -/* Remove all splits from the split list view */ - -static void -lv_clear_splits_in_lot (GNCLotViewer *lv) -{ - gtk_list_store_clear (lv->split_in_lot_store); + gnc_split_viewer_fill (lv, lv->split_in_lot_store, split_list); } /* ======================================================================== */ @@ -216,17 +209,14 @@ lv_show_splits_free (GNCLotViewer *lv) SplitList *split_list, *node; SplitList *filtered_list = NULL; - /* cleanup */ - gtk_list_store_clear (lv->split_free_store); - /* get splits */ - split_list = xaccAccountGetSplitList(lv->account); + split_list = xaccAccountGetSplitList (lv->account); /* filter splits */ for (node = split_list; node; node = node->next) { Split *split = node->data; - if (NULL == xaccSplitGetLot(split)) + if (NULL == xaccSplitGetLot (split)) { filtered_list = g_list_prepend (filtered_list, split); } @@ -234,7 +224,7 @@ lv_show_splits_free (GNCLotViewer *lv) filtered_list = g_list_reverse (filtered_list); /* display list */ - gnc_split_viewer_fill(lv, lv->split_free_store, filtered_list); + gnc_split_viewer_fill (lv, lv->split_free_store, filtered_list); g_list_free (filtered_list); g_list_free (split_list); } @@ -251,7 +241,7 @@ lv_save_current_lot (GNCLotViewer *lv) if (lot) { - gnc_lot_begin_edit(lot); + gnc_lot_begin_edit (lot); /* Get the title, save_the_title */ str = gtk_entry_get_text (lv->title_entry); @@ -260,9 +250,9 @@ lv_save_current_lot (GNCLotViewer *lv) /* Get the notes, save the notes */ notes = xxxgtk_textview_get_text (lv->lot_notes); gnc_lot_set_notes (lot, notes); - g_free(notes); + g_free (notes); - gnc_lot_commit_edit(lot); + gnc_lot_commit_edit (lot); } } @@ -284,13 +274,13 @@ lv_unset_lot (GNCLotViewer *lv) gtk_text_view_set_editable (lv->lot_notes, FALSE); /* Erase the mini-view area */ - lv_clear_splits_in_lot (lv); + gtk_list_store_clear (lv->split_in_lot_store); #ifdef LOTS_READY_FOR_SHOWTIME - gtk_widget_set_sensitive(GTK_WIDGET(lv->regview_button), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->regview_button), FALSE); #endif - gtk_widget_set_sensitive(GTK_WIDGET(lv->delete_button), FALSE); - gtk_widget_set_sensitive(GTK_WIDGET(lv->scrub_lot_button), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->delete_button), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->scrub_lot_button), FALSE); } /* ======================================================================== */ @@ -320,10 +310,10 @@ lv_select_row (GNCLotViewer *lv, lv_show_splits_in_lot (lv); #ifdef LOTS_READY_FOR_SHOWTIME - gtk_widget_set_sensitive(GTK_WIDGET(lv->regview_button), TRUE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->regview_button), TRUE); #endif - gtk_widget_set_sensitive(GTK_WIDGET(lv->delete_button), TRUE); - gtk_widget_set_sensitive(GTK_WIDGET(lv->scrub_lot_button), TRUE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->delete_button), TRUE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->scrub_lot_button), TRUE); } /* ======================================================================== */ @@ -342,24 +332,23 @@ lv_unselect_row (GNCLotViewer *lv) static void gnc_lot_viewer_fill (GNCLotViewer *lv) { - LotList *lot_list, *node; + LotList *lot_list = xaccAccountGetLotList (lv->account); GNCLot *this_lot, *selected_lot = NULL; GtkListStore *store; GtkTreeModel *model; GtkTreeIter iter; - GtkTreeSelection *selection; + GtkTreeSelection *selection = gtk_tree_view_get_selection (lv->lot_view); gboolean found = FALSE; + gboolean is_business_lot = xaccAccountIsAPARType (xaccAccountGetType (lv->account)); + gboolean show_only_open_lots = gtk_toggle_button_get_active (lv->only_show_open_lots_checkbutton); - lot_list = xaccAccountGetLotList (lv->account); - - selection = gtk_tree_view_get_selection(lv->lot_view); if (gtk_tree_selection_get_selected (selection, &model, &iter)) - gtk_tree_model_get(model, &iter, LOT_COL_PNTR, &selected_lot, -1); + gtk_tree_model_get (model, &iter, LOT_COL_PNTR, &selected_lot, -1); /* Crazy. Should update in place if possible. */ gtk_list_store_clear (lv->lot_store); - for (node = lot_list; node; node = node->next) + for (LotList *node = lot_list; node; node = node->next) { char type_buff[200]; char baln_buff[200]; @@ -371,31 +360,31 @@ gnc_lot_viewer_fill (GNCLotViewer *lv) gnc_numeric gains_baln = get_realized_gains (lot, currency); /* Skip closed lots when only open should be shown */ - if (TRUE == gtk_toggle_button_get_active(lv->only_show_open_lots_checkbutton) && gnc_lot_is_closed (lot)) + if (show_only_open_lots && gnc_lot_is_closed (lot)) { continue; } store = lv->lot_store; - gtk_list_store_append(store, &iter); + gtk_list_store_append (store, &iter); /* Part of invoice */ type_buff[0] = '\0'; - if ( NULL != gncInvoiceGetInvoiceFromLot(lot) ) + if ( NULL != gncInvoiceGetInvoiceFromLot (lot) ) { - snprintf(type_buff, 200, "I"); + snprintf (type_buff, 200, "I"); } - gtk_list_store_set(store, &iter, LOT_COL_TYPE, type_buff, -1); + gtk_list_store_set (store, &iter, LOT_COL_TYPE, type_buff, -1); /* Opening date */ if (esplit) { Transaction *etrans = xaccSplitGetParent (esplit); time64 open_date = xaccTransGetDate (etrans); - gtk_list_store_set(store, &iter, LOT_COL_OPEN, open_date, -1); + gtk_list_store_set (store, &iter, LOT_COL_OPEN, open_date, -1); } else - gtk_list_store_set(store, &iter, LOT_COL_OPEN, G_MININT64, -1); + gtk_list_store_set (store, &iter, LOT_COL_OPEN, G_MININT64, -1); /* Closing date */ if (gnc_lot_is_closed (lot)) @@ -404,56 +393,56 @@ gnc_lot_viewer_fill (GNCLotViewer *lv) Transaction *ftrans = xaccSplitGetParent (fsplit); time64 close_date = xaccTransGetDate (ftrans); - gtk_list_store_set(store, &iter, LOT_COL_CLOSE, close_date, -1); + gtk_list_store_set (store, &iter, LOT_COL_CLOSE, close_date, -1); } else { - gtk_list_store_set(store, &iter, LOT_COL_CLOSE, G_MAXINT64, -1); + gtk_list_store_set (store, &iter, LOT_COL_CLOSE, G_MAXINT64, -1); } /* Title */ - gtk_list_store_set(store, &iter, LOT_COL_TITLE, gnc_lot_get_title(lot), -1); + gtk_list_store_set (store, &iter, LOT_COL_TITLE, gnc_lot_get_title (lot), -1); /* Amount */ xaccSPrintAmount (baln_buff, amt_baln, - gnc_account_print_info (lv->account, TRUE)); - gtk_list_store_set(store, &iter, LOT_COL_BALN, baln_buff, -1); - gtk_list_store_set(store, &iter, LOT_COL_BALN_DOUBLE, gnc_numeric_to_double (amt_baln), -1); + gnc_account_print_info (lv->account, is_business_lot)); + gtk_list_store_set (store, &iter, LOT_COL_BALN, baln_buff, -1); + gtk_list_store_set (store, &iter, LOT_COL_BALN_DOUBLE, gnc_numeric_to_double (amt_baln), -1); /* Capital Gains/Losses Appreciation/Depreciation */ xaccSPrintAmount (gain_buff, gains_baln, gnc_commodity_print_info (currency, TRUE)); - gtk_list_store_set(store, &iter, LOT_COL_GAINS, gain_buff, -1); - gtk_list_store_set(store, &iter, LOT_COL_GAINS_DOUBLE, gnc_numeric_to_double (gains_baln), -1); + gtk_list_store_set (store, &iter, LOT_COL_GAINS, gain_buff, -1); + gtk_list_store_set (store, &iter, LOT_COL_GAINS_DOUBLE, gnc_numeric_to_double (gains_baln), -1); /* Self-reference */ - gtk_list_store_set(store, &iter, LOT_COL_PNTR, lot, -1); + gtk_list_store_set (store, &iter, LOT_COL_PNTR, lot, -1); } - g_list_free(lot_list); + g_list_free (lot_list); /* re-select the row that the user had previously selected, * if possible. */ if (selected_lot) { - model = GTK_TREE_MODEL(lv->lot_store); - if (gtk_tree_model_get_iter_first(model, &iter)) + model = GTK_TREE_MODEL (lv->lot_store); + if (gtk_tree_model_get_iter_first (model, &iter)) { do { - gtk_tree_model_get(model, &iter, LOT_COL_PNTR, &this_lot, -1); + gtk_tree_model_get (model, &iter, LOT_COL_PNTR, &this_lot, -1); if (this_lot == selected_lot) { - gtk_tree_selection_select_iter(selection, &iter); + gtk_tree_selection_select_iter (selection, &iter); found = TRUE; break; } } - while (gtk_tree_model_iter_next(model, &iter)); + while (gtk_tree_model_iter_next (model, &iter)); } } if (!found) - gtk_tree_selection_unselect_all(selection); + gtk_tree_selection_unselect_all (selection); } /* ======================================================================== */ @@ -467,10 +456,10 @@ lv_get_selected_split (GNCLotViewer *lv, GtkTreeView *view) GtkTreeSelection *selection; GtkTreeIter iter; - selection = gtk_tree_view_get_selection(view); + selection = gtk_tree_view_get_selection (view); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { - gtk_tree_model_get(model, &iter, SPLIT_COL_PNTR, &split, -1); + gtk_tree_model_get (model, &iter, SPLIT_COL_PNTR, &split, -1); } return split; @@ -480,14 +469,14 @@ lv_get_selected_split (GNCLotViewer *lv, GtkTreeView *view) /* Check if split is main invoice split in lot */ static gboolean -lv_can_remove_split_from_lot(Split * split, GNCLot * lot) +lv_can_remove_split_from_lot (Split * split, GNCLot * lot) { GncInvoice *lot_invoice, *txn_invoice; Transaction *txn; - lot_invoice = gncInvoiceGetInvoiceFromLot(lot); - txn = xaccSplitGetParent(split); - txn_invoice = gncInvoiceGetInvoiceFromTxn(txn); + lot_invoice = gncInvoiceGetInvoiceFromLot (lot); + txn = xaccSplitGetParent (split); + txn_invoice = gncInvoiceGetInvoiceFromTxn (txn); if ( lot_invoice != NULL && lot_invoice == txn_invoice ) return FALSE; @@ -500,16 +489,13 @@ lv_can_remove_split_from_lot(Split * split, GNCLot * lot) static void gnc_split_viewer_fill (GNCLotViewer *lv, GtkListStore *store, SplitList *split_list) { - SplitList *node; - GtkTreeIter iter; - gboolean is_business_lot = FALSE; - gnc_numeric baln = gnc_numeric_zero(); + gboolean is_business_lot = xaccAccountIsAPARType (xaccAccountGetType (lv->account)); + gboolean show_adjusted_amounts = gtk_toggle_button_get_active (lv->show_adjusted_amounts_checkbutton); + gnc_numeric baln = gnc_numeric_zero (); - if (lv->selected_lot) - is_business_lot = xaccAccountIsAPARType (xaccAccountGetType (gnc_lot_get_account (lv->selected_lot))); + gtk_list_store_clear (store); - gtk_list_store_clear (lv->split_in_lot_store); - for (node = split_list; node; node = node->next) + for (SplitList *node = split_list; node; node = node->next) { Split *split = node->data; char amtbuff[200]; @@ -519,12 +505,16 @@ gnc_split_viewer_fill (GNCLotViewer *lv, GtkListStore *store, SplitList *split_l gnc_commodity *currency; Transaction *trans = xaccSplitGetParent (split); time64 date = xaccTransGetDate (trans); - gnc_numeric amnt, value, gains; + gnc_numeric amnt = xaccSplitGetAdjustedAmount (split); + gnc_numeric value, gain; + GtkTreeIter iter; /* Do not show gains splits, however do show empty business splits */ - if (!is_business_lot && gnc_numeric_zero_p (xaccSplitGetAmount(split))) continue; + if (!is_business_lot && gnc_numeric_zero_p (amnt)) continue; - gtk_list_store_append(store, &iter); + if (xaccSplitIsStockSplit (split)) continue; + + gtk_list_store_append (store, &iter); /* Date */ gtk_list_store_set (store, &iter, SPLIT_COL_DATE, date, -1); @@ -537,9 +527,8 @@ gnc_split_viewer_fill (GNCLotViewer *lv, GtkListStore *store, SplitList *split_l gtk_list_store_set (store, &iter, SPLIT_COL_DESCRIPTION, xaccTransGetDescription (trans), -1); /* Amount */ - amnt = xaccSplitGetAmount (split); - xaccSPrintAmount (amtbuff, amnt, - gnc_account_print_info (lv->account, TRUE)); + xaccSPrintAmount (amtbuff, show_adjusted_amounts ? amnt : xaccSplitGetAmount (split), + gnc_account_print_info (lv->account, is_business_lot)); gtk_list_store_set (store, &iter, SPLIT_COL_AMOUNT, amtbuff, -1); gtk_list_store_set (store, &iter, SPLIT_COL_AMOUNT_DOUBLE, gnc_numeric_to_double (amnt), -1); @@ -556,65 +545,84 @@ gnc_split_viewer_fill (GNCLotViewer *lv, GtkListStore *store, SplitList *split_l gtk_list_store_set (store, &iter, SPLIT_COL_VALUE_DOUBLE, gnc_numeric_to_double (value), -1); /* Gains. Blank if none. */ - gains = xaccSplitGetCapGains (split); - if (gnc_numeric_zero_p(gains)) + gain = (store == lv->split_in_lot_store) ? + xaccSplitGetCapGains (split) : xaccLotFreeSplitCapGain (split, lv->selected_lot); + if (gnc_numeric_zero_p (gain)) { gainbuff[0] = 0; } else { - xaccSPrintAmount (gainbuff, gains, + xaccSPrintAmount (gainbuff, gain, gnc_commodity_print_info (currency, TRUE)); } gtk_list_store_set (store, &iter, SPLIT_COL_GAIN_LOSS, gainbuff, -1); - gtk_list_store_set (store, &iter, SPLIT_COL_GAIN_LOSS_DOUBLE, gnc_numeric_to_double (gains), -1); + gtk_list_store_set (store, &iter, SPLIT_COL_GAIN_LOSS_DOUBLE, gnc_numeric_to_double (gain), -1); - /* Balance of Gains */ + /* Balance of Amounts */ baln = gnc_numeric_add_fixed (baln, amnt); - if (gnc_numeric_zero_p(baln)) + if (gnc_numeric_zero_p (baln)) { balnbuff[0] = 0; } else { xaccSPrintAmount (balnbuff, baln, - gnc_account_print_info (lv->account, TRUE)); + gnc_account_print_info (lv->account, is_business_lot)); } gtk_list_store_set (store, &iter, SPLIT_COL_BALANCE, balnbuff, -1); gtk_list_store_set (store, &iter, SPLIT_COL_BALANCE_DOUBLE, gnc_numeric_to_double (baln), -1); /* Self-reference */ - gtk_list_store_set(store, &iter, SPLIT_COL_PNTR, split, -1); + gtk_list_store_set (store, &iter, SPLIT_COL_PNTR, split, -1); } } /* ======================================================================== */ static void -lv_update_split_buttons(GNCLotViewer *lv) +lv_update_split_buttons (GNCLotViewer *lv) { - Split * split; - gtk_widget_set_sensitive(GTK_WIDGET(lv->add_split_to_lot_button), FALSE); - gtk_widget_set_sensitive(GTK_WIDGET(lv->remove_split_from_lot_button), FALSE); + Split *split; + gtk_widget_set_sensitive (GTK_WIDGET(lv->add_split_to_lot_button), FALSE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->remove_split_from_lot_button), FALSE); if (NULL != lv->selected_lot) { - if (NULL != lv_get_selected_split(lv, lv->split_free_view) ) + if (NULL != lv_get_selected_split (lv, lv->split_free_view) ) { - gtk_widget_set_sensitive(GTK_WIDGET(lv->add_split_to_lot_button), TRUE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->add_split_to_lot_button), TRUE); } - split = lv_get_selected_split(lv, lv->split_in_lot_view); - if (NULL != split && TRUE == lv_can_remove_split_from_lot(split, lv->selected_lot)) + split = lv_get_selected_split (lv, lv->split_in_lot_view); + if (NULL != split && TRUE == lv_can_remove_split_from_lot (split, lv->selected_lot)) { - gtk_widget_set_sensitive(GTK_WIDGET(lv->remove_split_from_lot_button), TRUE); + gtk_widget_set_sensitive (GTK_WIDGET(lv->remove_split_from_lot_button), TRUE); } } } -static void lv_refresh(GNCLotViewer * lv) +static void set_window_title (GNCLotViewer *lv) +{ + gchar *win_title = g_strdup_printf (_("Lots in Account %s"), xaccAccountGetName (lv->account)); + gtk_window_set_title (GTK_WINDOW (lv->window), win_title); + g_free (win_title); +} + +static void set_adjusted_ammounts_checkbutton_visibility (GNCLotViewer *lv) +{ + GtkWidget *widget = GTK_WIDGET(lv->show_adjusted_amounts_checkbutton); + gboolean has_stock_split = xaccAccountHasStockSplit (lv->account); + + gtk_widget_set_visible (widget, has_stock_split); + gtk_widget_set_no_show_all (widget, !has_stock_split); +} + +static void lv_refresh (GNCLotViewer *lv) { gnc_lot_viewer_fill (lv); lv_show_splits_free (lv); lv_show_splits_in_lot (lv); + set_window_title (lv); + set_adjusted_ammounts_checkbutton_visibility (lv); } /* ======================================================================== */ @@ -623,6 +631,17 @@ static void lv_refresh_handler (GHashTable *changes, gpointer user_data) { GNCLotViewer *lv = user_data; + + if (changes) + { + const EventInfo *info = gnc_gui_get_entity_events (changes, &lv->account_id); + if (info && (info->event_mask & QOF_EVENT_DESTROY)) + { + gnc_close_gui_component (lv->component_id); + return; + } + } + lv_refresh (lv); } @@ -633,7 +652,7 @@ lv_close_handler (gpointer user_data) lv_save_current_lot (lv); - gnc_save_window_size(GNC_PREFS_GROUP, GTK_WINDOW(lv->window)); + gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(lv->window)); gtk_widget_destroy (lv->window); } @@ -651,10 +670,10 @@ lv_title_entry_changed_cb (GtkEntry *ent, gpointer user_data) const char * title; title = gtk_entry_get_text (lv->title_entry); - selection = gtk_tree_view_get_selection(lv->lot_view); + selection = gtk_tree_view_get_selection (lv->lot_view); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { - gtk_list_store_set(GTK_LIST_STORE(model), &iter, LOT_COL_TITLE, title, -1); + gtk_list_store_set (GTK_LIST_STORE(model), &iter, LOT_COL_TITLE, title, -1); } } @@ -671,14 +690,14 @@ lv_selection_changed_cb (GtkTreeSelection *selection, if (gtk_tree_selection_get_selected (selection, &model, &iter)) { - gtk_tree_model_get(model, &iter, LOT_COL_PNTR, &lot, -1); - lv_select_row(lv, lot); + gtk_tree_model_get (model, &iter, LOT_COL_PNTR, &lot, -1); + lv_select_row (lv, lot); } else { - lv_unselect_row(lv); + lv_unselect_row (lv); } - lv_update_split_buttons(lv); + lv_update_split_buttons (lv); } /* ======================================================================== */ @@ -688,7 +707,9 @@ void lv_window_destroy_cb (GtkWidget *object, gpointer user_data) { GNCLotViewer *lv = user_data; - gnc_unregister_gui_component_by_data (LOT_VIEWER_CM_CLASS, lv); + + gnc_unregister_gui_component (lv->component_id); + g_free (lv); } @@ -696,7 +717,7 @@ static void lv_split_selection_changed_cb (GtkTreeSelection *selection, GNCLotViewer *lv) { - lv_update_split_buttons(lv); + lv_update_split_buttons (lv); } static void @@ -705,16 +726,16 @@ lv_add_split_to_lot_cb (GtkWidget *widget, GNCLotViewer * lv) Split *split; if ( NULL == lv->selected_lot ) return; - split = lv_get_selected_split(lv, lv->split_free_view); + split = lv_get_selected_split (lv, lv->split_free_view); if ( NULL == split ) return; - gnc_suspend_gui_refresh(); - xaccAccountBeginEdit(lv->account); - gnc_lot_add_split(lv->selected_lot, split); - xaccAccountCommitEdit(lv->account); - gnc_resume_gui_refresh(); + gnc_suspend_gui_refresh (); + xaccAccountBeginEdit (lv->account); + gnc_lot_add_split (lv->selected_lot, split); + xaccAccountCommitEdit (lv->account); + gnc_resume_gui_refresh (); - lv_refresh(lv); + lv_refresh (lv); } static void @@ -723,25 +744,31 @@ lv_remove_split_from_lot_cb (GtkWidget *widget, GNCLotViewer * lv) Split *split; if ( NULL == lv->selected_lot ) return; - split = lv_get_selected_split(lv, lv->split_in_lot_view); + split = lv_get_selected_split (lv, lv->split_in_lot_view); if ( NULL == split ) return; - if ( FALSE == lv_can_remove_split_from_lot(split, lv->selected_lot) ) + if ( FALSE == lv_can_remove_split_from_lot (split, lv->selected_lot) ) return; - gnc_suspend_gui_refresh(); - xaccAccountBeginEdit(lv->account); - gnc_lot_remove_split(lv->selected_lot, split); - xaccAccountCommitEdit(lv->account); - gnc_resume_gui_refresh(); + gnc_suspend_gui_refresh (); + xaccAccountBeginEdit (lv->account); + gnc_lot_remove_split (lv->selected_lot, split); + xaccAccountCommitEdit (lv->account); + gnc_resume_gui_refresh (); - lv_refresh(lv); + lv_refresh (lv); } static void lv_only_show_open_lots_changed_cb (GtkWidget *widget, GNCLotViewer * lv) { - lv_refresh(lv); + lv_refresh (lv); +} + +static void +lv_show_adjusted_amounts_changed_cb (GtkWidget *widget, GNCLotViewer * lv) +{ + lv_refresh (lv); } /* ======================================================================== */ @@ -769,9 +796,9 @@ lv_response_cb (GtkDialog *dialog, gint response, gpointer data) if (NULL == lot) return; /* Prevent broken invoices */ - if (NULL != gncInvoiceGetInvoiceFromLot(lot)) + if (NULL != gncInvoiceGetInvoiceFromLot (lot)) return; - xaccAccountRemoveLot (gnc_lot_get_account(lot), lot); + xaccAccountRemoveLot (gnc_lot_get_account (lot), lot); gnc_lot_destroy (lot); lv_unset_lot (lv); gnc_lot_viewer_fill (lv); @@ -780,7 +807,7 @@ lv_response_cb (GtkDialog *dialog, gint response, gpointer data) case RESPONSE_SCRUB_LOT: if (NULL == lot) return; - if (xaccAccountIsAPARType (xaccAccountGetType(lv->account))) + if (xaccAccountIsAPARType (xaccAccountGetType (lv->account))) gncScrubBusinessLot (lot); else xaccScrubLot (lot); @@ -790,14 +817,12 @@ lv_response_cb (GtkDialog *dialog, gint response, gpointer data) case RESPONSE_SCRUB_ACCOUNT: gnc_suspend_gui_refresh (); - if (xaccAccountIsAPARType (xaccAccountGetType(lv->account))) + if (xaccAccountIsAPARType (xaccAccountGetType (lv->account))) gncScrubBusinessAccountLots (lv->account, gnc_window_show_progress); else xaccAccountScrubLots (lv->account); gnc_resume_gui_refresh (); - gnc_lot_viewer_fill (lv); - lv_show_splits_free (lv); - lv_show_splits_in_lot (lv); + lv_refresh (lv); break; case RESPONSE_NEW_LOT: @@ -891,71 +916,68 @@ lv_init_lot_view (GNCLotViewer *lv) GtkTreeSelection *selection; GtkCellRenderer *renderer; - g_return_if_fail(GTK_IS_TREE_VIEW(lv->lot_view)); + g_return_if_fail (GTK_IS_TREE_VIEW(lv->lot_view)); view = lv->lot_view; - store = gtk_list_store_new(NUM_LOT_COLS, G_TYPE_STRING, G_TYPE_INT64, - G_TYPE_INT64, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_DOUBLE, - G_TYPE_STRING,G_TYPE_DOUBLE, - G_TYPE_POINTER); - gtk_tree_view_set_model(view, GTK_TREE_MODEL(store)); - g_object_unref(store); + store = gtk_list_store_new (NUM_LOT_COLS, G_TYPE_STRING, G_TYPE_INT64, + G_TYPE_INT64, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_DOUBLE, + G_TYPE_STRING,G_TYPE_DOUBLE, + G_TYPE_POINTER); + gtk_tree_view_set_model (view, GTK_TREE_MODEL(store)); + g_object_unref (store); lv->lot_store = store; /* Set up the columns */ - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Type"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Type"), renderer, "text", LOT_COL_TYPE, NULL); - gtk_tree_view_column_set_sort_column_id(column, LOT_COL_TYPE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_sort_column_id (column, LOT_COL_TYPE); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Opened"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Opened"), renderer, "text", LOT_COL_OPEN, NULL); - gtk_tree_view_column_set_sort_column_id(column, LOT_COL_OPEN); + gtk_tree_view_column_set_sort_column_id (column, LOT_COL_OPEN); tree_view_column_set_default_width (view, column, "31-12-2013"); gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc) lot_print_date, lot_get_open_date, NULL); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Closed"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Closed"), renderer, "text", LOT_COL_CLOSE, NULL); - gtk_tree_view_column_set_sort_column_id(column, LOT_COL_CLOSE); + gtk_tree_view_column_set_sort_column_id (column, LOT_COL_CLOSE); tree_view_column_set_default_width (view, column, "31-12-2013"); gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc) lot_print_date, lot_get_closing_date, NULL); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Title"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Title"), renderer, "text", LOT_COL_TITLE, NULL); - gtk_tree_view_column_set_sort_column_id(column, LOT_COL_TITLE); + gtk_tree_view_column_set_sort_column_id (column, LOT_COL_TITLE); gtk_tree_view_column_set_expand (column, TRUE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Balance"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Balance"), renderer, "text", LOT_COL_BALN, NULL); configure_number_columns (column, renderer, LOT_COL_BALN_DOUBLE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Gains"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Gains"), renderer, "text", LOT_COL_GAINS, NULL); configure_number_columns (column, renderer, LOT_COL_GAINS_DOUBLE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); /* Set up signals */ - selection = gtk_tree_view_get_selection(view); - g_signal_connect(selection, "changed", - G_CALLBACK(lv_selection_changed_cb), lv); - g_signal_connect(lv->only_show_open_lots_checkbutton, "toggled", - G_CALLBACK(lv_only_show_open_lots_changed_cb), lv); - + selection = gtk_tree_view_get_selection (view); + g_signal_connect (selection, "changed", + G_CALLBACK(lv_selection_changed_cb), lv); } /* ======================================================================== */ @@ -968,37 +990,37 @@ lv_init_split_view (GNCLotViewer *lv, GtkTreeView *view) GtkTreeSelection *selection; GtkCellRenderer *renderer; - g_return_val_if_fail(GTK_IS_TREE_VIEW(view), NULL); + g_return_val_if_fail (GTK_IS_TREE_VIEW(view), NULL); - store = gtk_list_store_new(NUM_SPLIT_COLS, G_TYPE_INT64, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_DOUBLE, - G_TYPE_STRING, G_TYPE_DOUBLE, - G_TYPE_STRING, G_TYPE_DOUBLE, - G_TYPE_STRING, G_TYPE_DOUBLE, - G_TYPE_POINTER); - gtk_tree_view_set_model(view, GTK_TREE_MODEL(store)); - g_object_unref(store); + store = gtk_list_store_new (NUM_SPLIT_COLS, G_TYPE_INT64, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_DOUBLE, + G_TYPE_STRING, G_TYPE_DOUBLE, + G_TYPE_STRING, G_TYPE_DOUBLE, + G_TYPE_STRING, G_TYPE_DOUBLE, + G_TYPE_POINTER); + gtk_tree_view_set_model (view, GTK_TREE_MODEL(store)); + g_object_unref (store); /* Set up the columns */ - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Date"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Date"), renderer, "text", SPLIT_COL_DATE, NULL); - gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_DATE); + gtk_tree_view_column_set_sort_column_id (column, SPLIT_COL_DATE); tree_view_column_set_default_width (view, column, "31-12-2013"); gtk_tree_view_column_set_cell_data_func (column, renderer, (GtkTreeCellDataFunc) print_date, GINT_TO_POINTER (SPLIT_COL_DATE), NULL); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Num"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Num"), renderer, "text", SPLIT_COL_NUM, NULL); - gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_NUM); - gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_sort_column_id (column, SPLIT_COL_NUM); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Description"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Description"), renderer, "text", SPLIT_COL_DESCRIPTION, NULL); g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL); @@ -1006,39 +1028,39 @@ lv_init_split_view (GNCLotViewer *lv, GtkTreeView *view) width would be small, so set a minimum width */ gtk_tree_view_column_set_min_width (column, 200); - gtk_tree_view_column_set_sort_column_id(column, SPLIT_COL_DESCRIPTION); + gtk_tree_view_column_set_sort_column_id (column, SPLIT_COL_DESCRIPTION); gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_column_set_resizable (column, TRUE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Amount"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Amount"), renderer, "text", SPLIT_COL_AMOUNT, NULL); configure_number_columns (column, renderer, SPLIT_COL_AMOUNT_DOUBLE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Value"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Value"), renderer, "text", SPLIT_COL_VALUE, NULL); configure_number_columns (column, renderer, SPLIT_COL_VALUE_DOUBLE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Gain/Loss"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Gain/Loss"), renderer, "text", SPLIT_COL_GAIN_LOSS, NULL); configure_number_columns (column, renderer, SPLIT_COL_GAIN_LOSS_DOUBLE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); - renderer = gtk_cell_renderer_text_new(); - column = gtk_tree_view_column_new_with_attributes(_("Balance"), renderer, + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes (_("Balance"), renderer, "text", SPLIT_COL_BALANCE, NULL); configure_number_columns (column, renderer, SPLIT_COL_BALANCE_DOUBLE); - gtk_tree_view_append_column(view, column); + gtk_tree_view_append_column (view, column); /* Set up the selection callbacks */ - selection = gtk_tree_view_get_selection(view); - g_signal_connect(selection, "changed", - G_CALLBACK(lv_split_selection_changed_cb), lv); + selection = gtk_tree_view_get_selection (view); + g_signal_connect (selection, "changed", + G_CALLBACK(lv_split_selection_changed_cb), lv); return store; } @@ -1054,10 +1076,10 @@ static void lv_init_split_buttons (GNCLotViewer *lv) { /* Set up the add/remove callbacks */ - g_signal_connect(G_OBJECT(lv->add_split_to_lot_button), "clicked", - G_CALLBACK(lv_add_split_to_lot_cb), lv); - g_signal_connect(G_OBJECT(lv->remove_split_from_lot_button), "clicked", - G_CALLBACK(lv_remove_split_from_lot_cb), lv); + g_signal_connect (G_OBJECT(lv->add_split_to_lot_button), "clicked", + G_CALLBACK(lv_add_split_to_lot_cb), lv); + g_signal_connect (G_OBJECT(lv->remove_split_from_lot_button), "clicked", + G_CALLBACK(lv_remove_split_from_lot_cb), lv); } /* ======================================================================== */ @@ -1075,10 +1097,9 @@ window_realize_set_split_paned_position_cb (GtkWidget *widget, gpointer user_dat static void lv_create (GNCLotViewer *lv, GtkWindow *parent) { - gchar *win_title; GtkBuilder *builder; - builder = gtk_builder_new(); + builder = gtk_builder_new (); gnc_builder_add_from_file (builder, "dialog-lot-viewer.glade", "lot_viewer_dialog"); lv->window = GTK_WIDGET(gtk_builder_get_object (builder, "lot_viewer_dialog")); @@ -1088,11 +1109,6 @@ lv_create (GNCLotViewer *lv, GtkWindow *parent) // Set the name for this dialog so it can be easily manipulated with css gtk_widget_set_name (GTK_WIDGET(lv->window), "gnc-id-lot-viewer"); - win_title = g_strdup_printf (_("Lots in Account %s"), - xaccAccountGetName(lv->account)); - gtk_window_set_title (GTK_WINDOW (lv->window), win_title); - g_free (win_title); - #ifdef LOTS_READY_FOR_SHOWTIME lv->regview_button = GTK_BUTTON(glade_xml_get_widget (builder, "regview_button")); #endif @@ -1100,20 +1116,26 @@ lv_create (GNCLotViewer *lv, GtkWindow *parent) lv->scrub_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "scrub_lot_button")); lv->new_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "new_lot_button")); - lv->lot_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "lot_view")); lv->only_show_open_lots_checkbutton = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder, "only_show_open_lots_checkbutton")); - lv_init_lot_view(lv); + g_signal_connect (lv->only_show_open_lots_checkbutton, "toggled", G_CALLBACK(lv_only_show_open_lots_changed_cb), lv); + + lv->show_adjusted_amounts_checkbutton = GTK_TOGGLE_BUTTON(gtk_builder_get_object (builder, "show_adjusted_amounts_checkbutton")); + g_signal_connect (lv->show_adjusted_amounts_checkbutton, "toggled", G_CALLBACK(lv_show_adjusted_amounts_changed_cb), lv); + set_adjusted_ammounts_checkbutton_visibility (lv); + + lv->lot_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "lot_view")); + lv_init_lot_view (lv); lv->lot_notes = GTK_TEXT_VIEW(gtk_builder_get_object (builder, "lot_notes_text")); lv->title_entry = GTK_ENTRY (gtk_builder_get_object (builder, "lot_title_entry")); lv->split_in_lot_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "split_in_lot_view")); lv->split_free_view = GTK_TREE_VIEW(gtk_builder_get_object (builder, "split_free_view")); lv->split_hpaned = GTK_WIDGET(gtk_builder_get_object (builder, "split_hpaned")); - lv_init_split_views(lv); + lv_init_split_views (lv); lv->add_split_to_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "add_split_to_lot_button")); lv->remove_split_from_lot_button = GTK_BUTTON(gtk_builder_get_object (builder, "remove_split_from_lot_button")); - lv_init_split_buttons(lv); + lv_init_split_buttons (lv); // Set grid lines option to preference gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(lv->lot_view), gnc_tree_view_get_grid_lines_pref ()); @@ -1125,7 +1147,7 @@ lv_create (GNCLotViewer *lv, GtkWindow *parent) gtk_tree_view_set_grid_lines (GTK_TREE_VIEW(lv->split_free_view), gnc_tree_view_get_grid_lines_pref ()); - if (gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY)) + if (gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL, GNC_PREF_SAVE_GEOMETRY)) { GObject *object; object = gtk_builder_get_object (builder, "lot_vpaned"); @@ -1142,12 +1164,14 @@ lv_create (GNCLotViewer *lv, GtkWindow *parent) G_CALLBACK(window_realize_set_split_paned_position_cb), lv); /* Setup signals */ - gtk_builder_connect_signals(builder, lv); - g_object_unref(G_OBJECT(builder)); + gtk_builder_connect_signals (builder, lv); + g_object_unref (G_OBJECT(builder)); - lv_update_split_buttons(lv); + lv_update_split_buttons (lv); - gnc_restore_window_size(GNC_PREFS_GROUP, GTK_WINDOW(lv->window), parent); + gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(lv->window), parent); + gtk_widget_show_all (lv->window); + gnc_window_adjust_for_screen (GTK_WINDOW(lv->window)); } /* ======================================================================== */ @@ -1156,27 +1180,23 @@ GNCLotViewer * gnc_lot_viewer_dialog (GtkWindow *parent, Account *account) { GNCLotViewer *lv; - gint component_id; if (!account) return NULL; lv = g_new0 (GNCLotViewer, 1); lv->account = account; lv_create (lv, parent); - gnc_lot_viewer_fill (lv); - lv_show_splits_free (lv); + lv_refresh (lv); - component_id = gnc_register_gui_component (LOT_VIEWER_CM_CLASS, - lv_refresh_handler, - lv_close_handler, - lv); + lv->component_id = gnc_register_gui_component (LOT_VIEWER_CM_CLASS, lv_refresh_handler, lv_close_handler, lv); + lv->account_id = *xaccAccountGetGUID (account); - gnc_gui_component_watch_entity_type (component_id, - GNC_ID_LOT, - QOF_EVENT_CREATE | QOF_EVENT_ADD | QOF_EVENT_REMOVE | QOF_EVENT_MODIFY | QOF_EVENT_DESTROY); + gnc_gui_component_set_session (lv->component_id, gnc_get_current_session ()); + gnc_gui_component_watch_entity_type (lv->component_id, GNC_ID_LOT, + QOF_EVENT_CREATE | QOF_EVENT_ADD | QOF_EVENT_REMOVE | QOF_EVENT_MODIFY | QOF_EVENT_DESTROY); + gnc_gui_component_watch_entity (lv->component_id, &lv->account_id, + QOF_EVENT_MODIFY | QOF_EVENT_DESTROY | GNC_EVENT_ITEM_CHANGED); - gtk_widget_show_all (lv->window); - gnc_window_adjust_for_screen (GTK_WINDOW(lv->window)); return lv; } diff --git a/gnucash/gtkbuilder/dialog-lot-viewer.glade b/gnucash/gtkbuilder/dialog-lot-viewer.glade index a24d50f3b8..6ffb030822 100644 --- a/gnucash/gtkbuilder/dialog-lot-viewer.glade +++ b/gnucash/gtkbuilder/dialog-lot-viewer.glade @@ -1,5 +1,5 @@ - + @@ -107,6 +107,22 @@ 0 + + + Show stock split adjusted amounts + True + True + False + start + True + + + False + True + end + 1 + + True @@ -191,13 +207,13 @@ False start _Title - True True center lot_title_entry + 0 @@ -224,12 +240,12 @@ False start _Lots in This Account - True True lot_view + 0 @@ -242,8 +258,6 @@ True True True - always - always in @@ -308,12 +322,12 @@ False start Splits _free - True True split_free_view + 0 @@ -366,12 +380,12 @@ False start Splits _in lot - True True split_in_lot_view + 1 diff --git a/gnucash/register/ledger-core/split-register-copy-ops.c b/gnucash/register/ledger-core/split-register-copy-ops.c index 7c88c84c2a..25c40e4eec 100644 --- a/gnucash/register/ledger-core/split-register-copy-ops.c +++ b/gnucash/register/ledger-core/split-register-copy-ops.c @@ -82,6 +82,12 @@ gnc_numeric gnc_float_split_get_value (const FloatingSplit *fs) return fs->m_value; } +gboolean gnc_float_split_get_stock_split (const FloatingSplit *fs) +{ + g_return_val_if_fail (fs, FALSE); + return fs->m_stock_split; +} + /* modifiers */ void gnc_float_split_set_split(FloatingSplit *fs, Split *split) @@ -129,17 +135,21 @@ void gnc_float_split_set_reconcile_date (FloatingSplit *fs, time64 reconcile_dat void gnc_float_split_set_amount (FloatingSplit *fs, const gnc_numeric amount) { g_return_if_fail (fs); - fs->m_amount = amount; } void gnc_float_split_set_value (FloatingSplit *fs, const gnc_numeric value) { g_return_if_fail (fs); - fs->m_value = value; } +void gnc_float_split_set_stock_split (FloatingSplit *fs, gboolean stock_split) +{ + g_return_if_fail (fs); + fs->m_stock_split = stock_split; +} + static void gnc_float_template_sx_data_free (FloatingTemplateSxData *ftsd) { @@ -247,6 +257,7 @@ FloatingSplit *gnc_split_to_float_split (Split *split, gboolean is_template) fs->m_reconcile_date = xaccSplitGetDateReconciled (split); fs->m_amount = xaccSplitGetAmount (split); fs->m_value = xaccSplitGetValue (split); + fs->m_stock_split = xaccSplitIsStockSplit (split); if (is_template) fs->m_template_sx_data = gnc_split_to_float_template_sx_data (split); @@ -336,6 +347,8 @@ gnc_float_split_to_split (const FloatingSplit *fs, Split *split, Account *templa { xaccSplitSetAmount (split, fs->m_amount); xaccSplitSetValue (split, fs->m_value); + if (fs->m_stock_split) + xaccSplitMakeStockSplit(split); } if (split_account) diff --git a/gnucash/register/ledger-core/split-register-copy-ops.h b/gnucash/register/ledger-core/split-register-copy-ops.h index a3cae8ca79..d372f78038 100644 --- a/gnucash/register/ledger-core/split-register-copy-ops.h +++ b/gnucash/register/ledger-core/split-register-copy-ops.h @@ -55,6 +55,7 @@ typedef struct char m_reconcile_state; gnc_numeric m_value; gnc_numeric m_amount; + gboolean m_stock_split; FloatingTemplateSxData *m_template_sx_data; @@ -83,6 +84,7 @@ char gnc_float_split_get_reconcile_state (const FloatingSplit *fs); time64 gnc_float_split_get_reconcile_date (const FloatingSplit *fs); gnc_numeric gnc_float_split_get_amount (const FloatingSplit *fs); gnc_numeric gnc_float_split_get_value (const FloatingSplit *fs); +gboolean gnc_float_split_get_stock_split (const FloatingSplit *fs); /* modifiers */ void gnc_float_split_set_split (FloatingSplit *fs, Split *split); @@ -94,6 +96,7 @@ void gnc_float_split_set_reconcile_state (FloatingSplit *fs, char reconcile_stat void gnc_float_split_set_reconcile_date (FloatingSplit *fs, time64 reconcile_date); void gnc_float_split_set_amount (FloatingSplit *fs, gnc_numeric amount); void gnc_float_split_set_value (FloatingSplit *fs, gnc_numeric value); +void gnc_float_split_set_stock_split (FloatingSplit *fs, gboolean stock_split); FloatingSplit *gnc_split_to_float_split (Split *split, gboolean is_template); void gnc_float_split_to_split (const FloatingSplit *fs, Split *split, Account *template_account); diff --git a/gnucash/register/ledger-core/split-register-model.c b/gnucash/register/ledger-core/split-register-model.c index 34ed7d5a1f..e62e280b82 100644 --- a/gnucash/register/ledger-core/split-register-model.c +++ b/gnucash/register/ledger-core/split-register-model.c @@ -1546,7 +1546,7 @@ gnc_split_register_get_mxfrm_entry (VirtualLocation virt_loc, if (s) name = g_strdup (SPLIT_TRANS_STR); - else if (g_strcmp0 ("stock-split", xaccSplitGetType (split)) == 0) + else if (xaccSplitIsStockSplit (split)) name = g_strdup (STOCK_SPLIT_STR); else name = g_strdup (""); @@ -1588,7 +1588,7 @@ gnc_split_register_get_mxfrm_help (VirtualLocation virt_loc, if (s) help = _ ("This transaction has multiple splits; " "press the Split button to see them all"); - else if (g_strcmp0 ("stock-split", xaccSplitGetType (split)) == 0) + else if (xaccSplitIsStockSplit (split)) help = _ ("This transaction is a stock split; " "press the Split button to see details"); else @@ -2080,7 +2080,7 @@ gnc_split_register_get_debcred_io_flags (VirtualLocation virt_loc, split = gnc_split_register_get_split (reg, virt_loc.vcell_loc); - if (g_strcmp0 ("stock-split", xaccSplitGetType (split)) == 0) + if (xaccSplitIsStockSplit (split)) return XACC_CELL_ALLOW_NONE; return XACC_CELL_ALLOW_ALL; diff --git a/libgnucash/engine/Account.cpp b/libgnucash/engine/Account.cpp index 852d5ac674..612fbd7832 100644 --- a/libgnucash/engine/Account.cpp +++ b/libgnucash/engine/Account.cpp @@ -328,6 +328,7 @@ gnc_account_init(Account* acc) priv->starting_cleared_balance = gnc_numeric_zero(); priv->starting_reconciled_balance = gnc_numeric_zero(); priv->balance_dirty = FALSE; + priv->has_stock_split = false; new (&priv->children) AccountVec (); new (&priv->splits) SplitsVec (); @@ -1456,6 +1457,7 @@ xaccFreeAccount (Account *acc) priv->commodity = nullptr; priv->balance_dirty = FALSE; + priv->has_stock_split = false; priv->sort_dirty = FALSE; priv->splits.~SplitsVec(); priv->children.~AccountVec(); @@ -2137,30 +2139,26 @@ xaccAccountRemoveLot (Account *acc, GNCLot *lot) void xaccAccountInsertLot (Account *acc, GNCLot *lot) { - AccountPrivate *priv, *opriv; - Account * old_acc = nullptr; - Account* lot_account; - /* errors */ g_return_if_fail(GNC_IS_ACCOUNT(acc)); g_return_if_fail(GNC_IS_LOT(lot)); /* optimizations */ - lot_account = gnc_lot_get_account(lot); - if (lot_account == acc) + auto lot_acc = gnc_lot_get_account(lot); + if (lot_acc == acc) return; ENTER ("(acc=%p, lot=%p)", acc, lot); /* pull it out of the old account */ - if (lot_account) + if (lot_acc) { - old_acc = lot_account; - opriv = GET_PRIVATE(old_acc); - opriv->lots = g_list_remove(opriv->lots, lot); + auto priv = GET_PRIVATE(lot_acc); + priv->lots = g_list_remove(priv->lots, lot); + qof_event_gen (&lot_acc->inst, QOF_EVENT_MODIFY, nullptr); } - priv = GET_PRIVATE(acc); + auto priv = GET_PRIVATE(acc); priv->lots = g_list_prepend(priv->lots, lot); gnc_lot_set_account(lot, acc); @@ -2276,32 +2274,52 @@ xaccAccountMoveAllSplits (Account *accfrom, Account *accto) void xaccAccountRecomputeBalance (Account * acc) { - AccountPrivate *priv; - gnc_numeric balance; - gnc_numeric noclosing_balance; - gnc_numeric cleared_balance; - gnc_numeric reconciled_balance; - if (nullptr == acc) return; - priv = GET_PRIVATE(acc); + auto priv = GET_PRIVATE(acc); if (qof_instance_get_editlevel(acc) > 0) return; if (!priv->balance_dirty || priv->defer_bal_computation) return; if (qof_instance_get_destroying(acc)) return; if (qof_book_shutting_down(qof_instance_get_book(acc))) return; - balance = priv->starting_balance; - noclosing_balance = priv->starting_noclosing_balance; - cleared_balance = priv->starting_cleared_balance; - reconciled_balance = priv->starting_reconciled_balance; + auto balance = priv->starting_balance; + auto noclosing_balance = priv->starting_noclosing_balance; + auto cleared_balance = priv->starting_cleared_balance; + auto reconciled_balance = priv->starting_reconciled_balance; + auto has_stock_split = false; PINFO ("acct=%s starting baln=%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT, priv->accountName, balance.num, balance.denom); for (auto split : priv->splits) { - gnc_numeric amt = xaccSplitGetAmount (split); + auto amt = xaccSplitGetAmount (split); - balance = gnc_numeric_add_fixed(balance, amt); + if (xaccSplitIsStockSplit(split)) + { + if (gnc_numeric_zero_p(balance)) + continue; + auto new_balance = gnc_numeric_add_fixed(balance, amt); + if (gnc_numeric_zero_p(new_balance)) + continue; + auto ratio = gnc_numeric_div(new_balance, balance, GNC_DENOM_AUTO, + GNC_HOW_DENOM_REDUCE); + auto denom = xaccAccountGetCommoditySCU(acc); + for (auto psplit : priv->splits) + { + if (psplit == split) + break; + xaccSplitSetAdjustedAmount(psplit, + gnc_numeric_mul(xaccSplitGetAdjustedAmount(psplit), ratio, denom, + GNC_HOW_RND_ROUND_HALF_UP)); + } + balance = new_balance; + has_stock_split = true; + } + else + { + split->adjusted_amount = amt; + balance = gnc_numeric_add_fixed(balance, amt); + } if (NREC != split->reconciled) { @@ -2330,6 +2348,7 @@ xaccAccountRecomputeBalance (Account * acc) priv->cleared_balance = cleared_balance; priv->reconciled_balance = reconciled_balance; priv->balance_dirty = FALSE; + priv->has_stock_split = has_stock_split; } /********************************************************************\ @@ -3484,6 +3503,13 @@ xaccAccountGetProjectedMinimumBalance (const Account *acc) return minimum ? *minimum : gnc_numeric_zero(); } +gboolean +xaccAccountHasStockSplit (const Account *acc) +{ + g_return_val_if_fail(GNC_IS_ACCOUNT(acc), false); + return GET_PRIVATE(acc)->has_stock_split; +} + /********************************************************************\ \********************************************************************/ @@ -3935,7 +3961,8 @@ xaccAccountGetSplitsSize (const Account *account) return GNC_IS_ACCOUNT(account) ? GET_PRIVATE(account)->splits.size() : 0; } -gboolean gnc_account_and_descendants_empty (Account *acc) +gboolean +gnc_account_and_descendants_empty (Account *acc) { g_return_val_if_fail (GNC_IS_ACCOUNT (acc), FALSE); auto priv = GET_PRIVATE (acc); diff --git a/libgnucash/engine/Account.h b/libgnucash/engine/Account.h index d8e7dd406b..510b326ae3 100644 --- a/libgnucash/engine/Account.h +++ b/libgnucash/engine/Account.h @@ -648,6 +648,10 @@ typedef enum gnc_numeric xaccAccountGetBalanceChangeForPeriod ( Account *acc, time64 date1, time64 date2, gboolean recurse); + /** Returns true if the account has a stock split, + * otherwise false. */ + gboolean xaccAccountHasStockSplit (const Account *acc); + /** @} */ /** @name Account Children and Parents. diff --git a/libgnucash/engine/AccountP.hpp b/libgnucash/engine/AccountP.hpp index ea617fe011..58faf542df 100644 --- a/libgnucash/engine/AccountP.hpp +++ b/libgnucash/engine/AccountP.hpp @@ -114,6 +114,7 @@ typedef struct AccountPrivate gnc_numeric reconciled_balance; gboolean balance_dirty; /* balances in splits incorrect */ + gboolean has_stock_split; /* account includes a stock split */ std::vector splits; /* list of split pointers */ GHashTable* splits_hash; diff --git a/libgnucash/engine/Scrub2.cpp b/libgnucash/engine/Scrub2.cpp index 7c78bca4d7..9862c761f6 100644 --- a/libgnucash/engine/Scrub2.cpp +++ b/libgnucash/engine/Scrub2.cpp @@ -68,6 +68,9 @@ restart_loop: { /* If already in lot, then no-op */ if (split->lot) continue; + + /* Skip stock splits */ + if (xaccSplitIsStockSplit(split)) continue; /* Skip voided transactions */ if (gnc_numeric_zero_p (split->amount) && @@ -104,25 +107,33 @@ xaccLotFill (GNCLot *lot) /* If balance already zero, we have nothing to do. */ if (gnc_lot_is_closed (lot)) { - LEAVE ("Lot Closed (lot=%s, acc=%s)", gnc_lot_get_title(lot), - xaccAccountGetName(acc)); - return; + LEAVE ("Lot Closed (lot=%s, acc=%s)", gnc_lot_get_title(lot), + xaccAccountGetName(acc)); + return; } split = pcy->PolicyGetSplit (pcy, lot); if (!split) { - LEAVE ("No Split (lot=%s, acc=%s)", gnc_lot_get_title(lot), - xaccAccountGetName(acc)); - return; /* Handle the common case */ + LEAVE ("No Split (lot=%s, acc=%s)", gnc_lot_get_title(lot), + xaccAccountGetName(acc)); + return; /* Handle the common case */ + } + + /* Reject stock split transactions */ + if (xaccSplitIsStockSplit(split)) + { + LEAVE ("Stock split transaction (lot=%s, acc=%s)", + gnc_lot_get_title(lot), xaccAccountGetName(acc)); + return; } /* Reject voided transactions */ if (gnc_numeric_zero_p(split->amount) && xaccTransGetVoidStatus(split->parent)) { - LEAVE ("Voided transaction (lot=%s, acc=%s)", - gnc_lot_get_title(lot), xaccAccountGetName(acc)); - return; + LEAVE ("Voided transaction (lot=%s, acc=%s)", + gnc_lot_get_title(lot), xaccAccountGetName(acc)); + return; } xaccAccountBeginEdit (acc); @@ -178,8 +189,8 @@ xaccLotScrubDoubleBalance (GNCLot *lot) /* We double-check only closed lots */ if (FALSE == gnc_lot_is_closed (lot)) { - LEAVE ("lot=%s is closed", gnc_lot_get_title(lot)); - return; + LEAVE ("lot=%s is closed", gnc_lot_get_title(lot)); + return; } for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next) diff --git a/libgnucash/engine/Split.cpp b/libgnucash/engine/Split.cpp index 1c042cac3e..7347fbee86 100644 --- a/libgnucash/engine/Split.cpp +++ b/libgnucash/engine/Split.cpp @@ -128,6 +128,9 @@ gnc_split_init(Split* split) split->reconciled_balance = gnc_numeric_zero(); split->noclosing_balance = gnc_numeric_zero(); + split->adjusted_amount = gnc_numeric_zero(); + split->split_type = nullptr; + split->gains = GAINS_STATUS_UNKNOWN; split->gains_split = nullptr; } @@ -520,6 +523,9 @@ xaccSplitReinit(Split * split) split->reconciled_balance = gnc_numeric_zero(); split->noclosing_balance = gnc_numeric_zero(); + split->adjusted_amount = gnc_numeric_zero(); + split->split_type = nullptr; + qof_instance_set_idata(split, 0); split->gains = GAINS_STATUS_UNKNOWN; @@ -604,6 +610,8 @@ xaccSplitCloneNoKvp (const Split *s) split->cleared_balance = s->cleared_balance; split->reconciled_balance = s->reconciled_balance; split->noclosing_balance = s->noclosing_balance; + split->adjusted_amount = s->adjusted_amount; + split->split_type = s->split_type; split->gains = GAINS_STATUS_UNKNOWN; split->gains_split = nullptr; @@ -689,6 +697,8 @@ xaccSplitDump (const Split *split, const char *tag) printf(" RBalance: %s\n", gnc_numeric_to_string(split->reconciled_balance)); printf(" NoClose: %s\n", gnc_numeric_to_string(split->noclosing_balance)); + printf(" AdjAmt: %s\n", gnc_numeric_to_string(split->adjusted_amount)); + printf(" Type: %s\n", split->split_type ? split->split_type : "(null)"); printf(" idata: %x\n", qof_instance_get_idata(split)); } #endif @@ -749,6 +759,13 @@ xaccFreeSplit (Split *split) split->acc = nullptr; split->orig_acc = nullptr; + split->balance = gnc_numeric_zero(); + split->cleared_balance = gnc_numeric_zero(); + split->reconciled_balance = gnc_numeric_zero(); + split->noclosing_balance = gnc_numeric_zero(); + split->adjusted_amount = gnc_numeric_zero(); + split->split_type = nullptr; + split->date_reconciled = 0; G_OBJECT_CLASS (QOF_INSTANCE_GET_CLASS (&split->inst))->dispose(G_OBJECT (split)); @@ -766,12 +783,11 @@ xaccFreeSplit (Split *split) void mark_split (Split *s) { if (s->acc) - { g_object_set(s->acc, "sort-dirty", TRUE, "balance-dirty", TRUE, nullptr); - } /* set dirty flag on lot too. */ - if (s->lot) gnc_lot_set_closed_unknown(s->lot); + if (s->lot) + gnc_lot_set_closed_unknown(s->lot); } /* @@ -915,6 +931,9 @@ xaccSplitEqual(const Split *sa, const Split *sb, if (!xaccSplitEqualCheckBal ("noclosing ", sa->noclosing_balance, sb->noclosing_balance)) return FALSE; + if (!xaccSplitEqualCheckBal ("adjusted ", sa->adjusted_amount, + sb->adjusted_amount)) + return FALSE; } if (!xaccTransEqual(sa->parent, sb->parent, check_guids, check_txn_splits, @@ -1238,6 +1257,7 @@ xaccSplitSetAmount (Split *s, gnc_numeric amt) } else s->amount = amt; + s->adjusted_amount = s->amount; SET_GAINS_ADIRTY(s); mark_split (s); @@ -1316,6 +1336,21 @@ xaccSplitGetReconciledBalance (const Split *s) return s ? s->reconciled_balance : gnc_numeric_zero(); } +gnc_numeric +xaccSplitGetAdjustedAmount (const Split *s) +{ + return s ? s->adjusted_amount : gnc_numeric_zero(); +} + +void +xaccSplitSetAdjustedAmount (Split *s, gnc_numeric amt) +{ + if (!s) return; + if (gnc_numeric_check (amt)) return; + + s->adjusted_amount = amt; +} + void xaccSplitSetBaseValue (Split *s, gnc_numeric value, const gnc_commodity * base_currency) @@ -2001,20 +2036,25 @@ xaccSplitGetBook (const Split *split) } const char * -xaccSplitGetType(const Split *s) +xaccSplitGetType(Split *s) { if (!s) return nullptr; - auto type{qof_instance_get_path_kvp (QOF_INSTANCE(s), {"split-type"})}; + if (!s->split_type) + { + auto type{qof_instance_get_path_kvp (QOF_INSTANCE(s), {"split-type"})}; - if (!type || !g_strcmp0 (*type, split_type_normal)) - return split_type_normal; - - if (!g_strcmp0 (*type, split_type_stock_split)) - return split_type_stock_split; - - PERR ("unexpected split-type %s, reset to normal.", *type); - return split_type_normal; + if (!type || !g_strcmp0 (*type, split_type_normal)) + s->split_type = split_type_normal; + else if (!g_strcmp0 (*type, split_type_stock_split)) + s->split_type = split_type_stock_split; + else + { + PERR ("unexpected split-type %s, reset to normal.", *type); + s->split_type = split_type_normal; + } + } + return s->split_type; } /* reconfigure a split to be a stock split - after this, you shouldn't @@ -2025,6 +2065,7 @@ xaccSplitMakeStockSplit(Split *s) xaccTransBeginEdit (s->parent); s->value = gnc_numeric_zero(); + s->split_type = split_type_stock_split; qof_instance_set_path_kvp (QOF_INSTANCE(s), g_strdup(split_type_stock_split), {"split-type"}); SET_GAINS_VDIRTY(s); @@ -2033,6 +2074,12 @@ xaccSplitMakeStockSplit(Split *s) xaccTransCommitEdit(s->parent); } +gboolean +xaccSplitIsStockSplit (Split *s) +{ + return g_strcmp0(split_type_stock_split, xaccSplitGetType(s)) == 0; +} + void xaccSplitAddPeerSplit (Split *split, const Split *other_split, time64 timestamp) diff --git a/libgnucash/engine/Split.h b/libgnucash/engine/Split.h index 26fd6b01c3..63fb1ac370 100644 --- a/libgnucash/engine/Split.h +++ b/libgnucash/engine/Split.h @@ -345,6 +345,16 @@ gnc_numeric xaccSplitGetClearedBalance (const Split *split); */ gnc_numeric xaccSplitGetReconciledBalance (const Split *split); +/** Sets the stock split adjusted amount of a split. + * @note The adjusted amount will be reset when the account is rebalanced. + */ +void xaccSplitSetAdjustedAmount (Split *split, gnc_numeric amount); + +/** Returns the stock-split adjusted amount of the split in the account's commodity. + */ +gnc_numeric xaccSplitGetAdjustedAmount (const Split *split); + + /** @} */ /** @name Split utility functions @@ -410,20 +420,18 @@ void xaccSplitMergePeerSplits (Split *split, const Split *other_split); */ Split * xaccSplitGetOtherSplit (const Split *split); -/** The xaccIsPeerSplit() is a convenience routine that returns TRUE - * (a non-zero value) if the two splits share a common parent - * transaction, else it returns FALSE (zero). - * -gboolean xaccIsPeerSplit (const Split *split_1, const Split *split_2); -*/ /** Returns the split type, which is either the string "normal", or - * "stock-split" for a split from a stock split (pun intended? :-). */ -const char *xaccSplitGetType(const Split *s); + * "stock-split" for a split from a stock split. */ +const char *xaccSplitGetType(Split *s); /** Mark a split to be of type stock split - after this, you shouldn't modify the value anymore, just the amount. */ void xaccSplitMakeStockSplit(Split *s); +/** Returns true if the split is of type stock split + */ +gboolean xaccSplitIsStockSplit(Split *s); + /** * The xaccSplitOrder(sa,sb) method is useful for sorting. * if sa and sb have different transactions, return their xaccTransOrder diff --git a/libgnucash/engine/SplitP.hpp b/libgnucash/engine/SplitP.hpp index d13c2ffa1b..cda33cc5f6 100644 --- a/libgnucash/engine/SplitP.hpp +++ b/libgnucash/engine/SplitP.hpp @@ -115,7 +115,7 @@ struct split_s gnc_numeric value; gnc_numeric amount; - const gchar * split_type; + const char *split_type; /* -------------------------------------------------------------- */ /* Below follow some 'temporary' fields */ @@ -128,6 +128,9 @@ struct split_s gnc_numeric noclosing_balance; gnc_numeric cleared_balance; gnc_numeric reconciled_balance; + + /* The stock-split adjusted amount */ + gnc_numeric adjusted_amount; }; struct _SplitClass diff --git a/libgnucash/engine/cap-gains.cpp b/libgnucash/engine/cap-gains.cpp index e9b677ee7c..6c3a4d1e7e 100644 --- a/libgnucash/engine/cap-gains.cpp +++ b/libgnucash/engine/cap-gains.cpp @@ -298,7 +298,7 @@ xaccSplitAssignToLot (Split *split, GNCLot *lot) * then we split the split into two pieces: one piece that will * bring the lot balance to zero, and another to be dealt with * later. */ - cmp = gnc_numeric_compare (gnc_numeric_abs(split->amount), + cmp = gnc_numeric_compare (gnc_numeric_abs(xaccSplitGetAdjustedAmount (split)), gnc_numeric_abs(baln)); PINFO ("found open lot with baln=%s (%s)", gnc_num_dbg_to_string (baln), @@ -331,6 +331,16 @@ xaccSplitAssignToLot (Split *split, GNCLot *lot) trans = split->parent; xaccTransBeginEdit (trans); + /* Adjust split's view of the lot balance for stock splits */ + if (!gnc_numeric_equal (split->amount, xaccSplitGetAdjustedAmount (split))) + { + frac = gnc_numeric_div (split->amount, xaccSplitGetAdjustedAmount (split), + GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); + baln = gnc_numeric_mul (baln, frac, + xaccAccountGetCommoditySCU(acc), + GNC_HOW_RND_ROUND_HALF_UP); + } + amt_tot = split->amount; amt_a = gnc_numeric_neg (baln); amt_b = gnc_numeric_sub_fixed (amt_tot, amt_a); @@ -476,48 +486,34 @@ xaccSplitAssign (Split *split) /* ============================================================== */ -Split * -xaccSplitGetCapGainsSplit (const Split *split) +static Split * +find_split_by_type (const gchar *type, const Split *split) { - GncGUID *gains_guid; - Split *gains_split; + GncGUID *type_guid = nullptr; if (!split) return nullptr; - qof_instance_get (QOF_INSTANCE (split), - "gains-split", &gains_guid, - nullptr); - if (!gains_guid) return nullptr; + qof_instance_get (QOF_INSTANCE (split), type, &type_guid, nullptr); + if (!type_guid) return nullptr; /* Both splits will be in the same collection, so search there. */ - gains_split = (Split*) qof_collection_lookup_entity ( - qof_instance_get_collection(split), gains_guid); - PINFO ("split=%p has gains-split=%p", split, gains_split); - guid_free (gains_guid); - return gains_split; + auto *guid_split = (Split *) qof_collection_lookup_entity (qof_instance_get_collection (split), type_guid); + PINFO ("split=%p has %s=%p", split, type, guid_split); + guid_free (type_guid); + return guid_split; } -/* ============================================================== */ + +Split * +xaccSplitGetCapGainsSplit (const Split *split) +{ + return find_split_by_type ("gains-split", split); +} Split * xaccSplitGetGainsSourceSplit (const Split *split) { - GncGUID *source_guid; - Split *source_split; - - if (!split) return nullptr; - - qof_instance_get (QOF_INSTANCE (split), - "gains-source", &source_guid, - nullptr); - if (!source_guid) return nullptr; - - /* Both splits will be in the same collection, so search there. */ - source_split = (Split*) qof_collection_lookup_entity( - qof_instance_get_collection(split), source_guid); - PINFO ("split=%p has source-split=%p", split, source_split); - guid_free (source_guid); - return source_split; + return find_split_by_type ("gains-source", split); } /* ============================================================== */ @@ -581,7 +577,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) return; } - if (g_strcmp0 ("stock-split", xaccSplitGetType (split)) == 0) + if (xaccSplitIsStockSplit (split)) { LEAVE ("Stock split split, returning."); return; @@ -675,38 +671,38 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) * cleans up the lot, before we get at it! */ if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount), - gnc_numeric_abs(split->amount))) + gnc_numeric_abs(xaccSplitGetAdjustedAmount (split)))) { GList *n; for (n = gnc_lot_get_split_list(lot); n; n = n->next) { Split *s = GNC_SPLIT(n->data); - PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount)); + PINFO ("split adj amt=%s", gnc_num_dbg_to_string(xaccSplitGetAdjustedAmount (s))); } PERR ("Malformed Lot \"%s\"! (too thin!) " - "opening amt=%s split amt=%s baln=%s", + "opening amt=%s split adj amt=%s baln=%s", gnc_lot_get_title (lot), gnc_num_dbg_to_string (lot_amount), - gnc_num_dbg_to_string (split->amount), + gnc_num_dbg_to_string (xaccSplitGetAdjustedAmount (split)), gnc_num_dbg_to_string (gnc_lot_get_balance(lot))); return; } if ( (gnc_numeric_negative_p(lot_amount) || - gnc_numeric_positive_p(split->amount)) && + gnc_numeric_positive_p(xaccSplitGetAdjustedAmount (split))) && (gnc_numeric_positive_p(lot_amount) || - gnc_numeric_negative_p(split->amount))) + gnc_numeric_negative_p(xaccSplitGetAdjustedAmount (split)))) { GList *n; for (n = gnc_lot_get_split_list(lot); n; n = n->next) { Split *s = GNC_SPLIT(n->data); - PINFO ("split amt=%s", gnc_num_dbg_to_string(s->amount)); + PINFO ("split adj amt=%s", gnc_num_dbg_to_string(xaccSplitGetAdjustedAmount (s))); } PERR ("Malformed Lot \"%s\"! (too fat!) " - "opening amt=%s split amt=%s baln=%s", + "opening adj amt=%s split adj amt=%s baln=%s", gnc_lot_get_title (lot), gnc_num_dbg_to_string (lot_amount), - gnc_num_dbg_to_string (split->amount), + gnc_num_dbg_to_string (xaccSplitGetAdjustedAmount (split)), gnc_num_dbg_to_string (gnc_lot_get_balance(lot))); return; } @@ -719,7 +715,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) * cap_gain = current_split_value - cost_basis */ /* Fraction of the lot that this split represents: */ - frac = gnc_numeric_div (split->amount, lot_amount, + frac = gnc_numeric_div (xaccSplitGetAdjustedAmount (split), lot_amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE); /* Basis for this split: */ @@ -729,10 +725,10 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) /* Capital gain for this split: */ value = gnc_numeric_sub (value, split->value, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED); - PINFO ("Open amt=%s val=%s; split amt=%s val=%s; gains=%s\n", + PINFO ("Open amt=%s val=%s; split adj amt=%s val=%s; gains=%s\n", gnc_num_dbg_to_string (lot_amount), gnc_num_dbg_to_string (lot_value), - gnc_num_dbg_to_string (split->amount), + gnc_num_dbg_to_string (xaccSplitGetAdjustedAmount (split)), gnc_num_dbg_to_string (split->value), gnc_num_dbg_to_string (value)); if (gnc_numeric_check (value)) @@ -744,7 +740,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) xaccTransGetDescription(split->parent), gnc_num_dbg_to_string (lot_amount), gnc_num_dbg_to_string (lot_value), - gnc_num_dbg_to_string (split->amount), + gnc_num_dbg_to_string (xaccSplitGetAdjustedAmount (split)), gnc_num_dbg_to_string (split->value), gnc_num_dbg_to_string (value)); return; @@ -752,7 +748,7 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) /* Are the cap gains zero? If not, add a balancing transaction. * As per design doc lots.txt: the transaction has two splits, - * with equal & opposite values. The amt of one iz zero (so as + * with equal & opposite values. The amt of one is zero (so as * not to upset the lot balance), the amt of the other is the same * as its value (its the realized gain/loss). */ @@ -896,6 +892,148 @@ xaccSplitComputeCapGains(Split *split, Account *gain_acc) /* ============================================================== */ +gnc_numeric +xaccLotFreeSplitCapGain(Split *split, GNCLot *lot) +{ + g_return_val_if_fail (split && lot && !split->lot, gnc_numeric_zero()); + + ENTER ("(split=%p lot=%s)", split, gnc_lot_get_title(lot)); + + auto pcy = gnc_account_get_policy (gnc_lot_get_account (lot)); + auto currency = split->parent->common_currency; + + /* Not possible to have gains if the transaction currency and + * account commodity are identical. */ + if (gnc_commodity_equal (currency, + xaccAccountGetCommodity(split->acc))) + { + LEAVE ("Currency transfer, gains not possible, returning."); + return gnc_numeric_zero(); + } + + auto *esplit = gnc_lot_get_earliest_split (lot); + if (!esplit) + { + LEAVE ("Lot is empty, returning."); + return gnc_numeric_zero(); + } + + if (xaccTransGetDate (xaccSplitGetParent (esplit)) >= xaccTransGetDate (xaccSplitGetParent (split))) + { + LEAVE ("Split is too early, returning."); + return gnc_numeric_zero(); + } + + if (xaccSplitIsStockSplit (split)) + { + LEAVE ("Stock split split, returning."); + return gnc_numeric_zero(); + } + + /* If amount is zero, there's nothing to do! Amount-zero splits + * may exist if users attempted to manually record gains. */ + if (gnc_numeric_zero_p (split->amount)) return gnc_numeric_zero(); + + /* If we got to here, then the gains really do need to be recomputed. + * So start working things. */ + + /* Get the amount and value in this lot at the time of this transaction. */ + gnc_numeric lot_amount, lot_value; + gnc_lot_get_balance_before (lot, split, &lot_amount, &lot_value); + + gnc_numeric opening_amount, opening_value; + gnc_commodity *opening_currency; + pcy->PolicyGetLotOpening (pcy, lot, &opening_amount, &opening_value, + &opening_currency); + + /* Check to make sure the lot-opening currency and this split + * use the same currency */ + if (FALSE == gnc_commodity_equiv (currency, opening_currency)) + { + /* OK, the purchase and the sale were made in different currencies. + * I don't know how to compute cap gains for that. This is not + * an error. Just punt, silently. + */ + LEAVE ("Can't compute gains, mismatched commodities!"); + return gnc_numeric_zero(); + } + + /* Opening amount should be larger (or equal) to current split, + * and it should be of the opposite sign. + * XXX This should really be a part of a scrub routine that + * cleans up the lot, before we get at it! + */ + if (0 > gnc_numeric_compare (gnc_numeric_abs(lot_amount), + gnc_numeric_abs(xaccSplitGetAdjustedAmount (split)))) + { + for (auto *n = gnc_lot_get_split_list(lot); n; n = n->next) + { + auto *s = GNC_SPLIT(n->data); + PINFO ("split adj amt=%s", gnc_num_dbg_to_string(xaccSplitGetAdjustedAmount (s))); + } + return gnc_numeric_zero(); + } + if ( (gnc_numeric_negative_p(lot_amount) || + gnc_numeric_positive_p(xaccSplitGetAdjustedAmount (split))) && + (gnc_numeric_positive_p(lot_amount) || + gnc_numeric_negative_p(xaccSplitGetAdjustedAmount (split)))) + { + for (auto *n = gnc_lot_get_split_list(lot); n; n = n->next) + { + auto *s = GNC_SPLIT(n->data); + PINFO ("split adj amt=%s", gnc_num_dbg_to_string(xaccSplitGetAdjustedAmount (s))); + } + return gnc_numeric_zero(); + } + + /* The cap gains is the difference between the basis prior to the + * current split, and the current split, pro-rated for an equal + * amount of shares. + * i.e. purchase_price = lot_value / lot_amount + * cost_basis = purchase_price * current_split_amount + * cap_gain = current_split_value - cost_basis + */ + /* Fraction of the lot that this split represents: */ + auto frac = gnc_numeric_div (xaccSplitGetAdjustedAmount (split), lot_amount, + GNC_DENOM_AUTO, + GNC_HOW_DENOM_REDUCE); + /* Basis for this split: */ + auto value = gnc_numeric_mul (frac, lot_value, + gnc_numeric_denom(opening_value), + GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP); + /* Capital gain for this split: */ + value = gnc_numeric_sub (value, split->value, + GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED); + + PINFO ("Open amt=%s val=%s; split adj amt=%s val=%s; gains=%s\n", + gnc_num_dbg_to_string (lot_amount), + gnc_num_dbg_to_string (lot_value), + gnc_num_dbg_to_string (xaccSplitGetAdjustedAmount (split)), + gnc_num_dbg_to_string (split->value), + gnc_num_dbg_to_string (value)); + + if (gnc_numeric_check (value)) + { + PERR ("Numeric overflow during gains calculation\n" + "Acct=%s Txn=%s\n" + "\tOpen amt=%s val=%s\n\tsplit amt=%s val=%s\n\tgains=%s\n", + xaccAccountGetName(split->acc), + xaccTransGetDescription(split->parent), + gnc_num_dbg_to_string (lot_amount), + gnc_num_dbg_to_string (lot_value), + gnc_num_dbg_to_string (xaccSplitGetAdjustedAmount (split)), + gnc_num_dbg_to_string (split->value), + gnc_num_dbg_to_string (value)); + return gnc_numeric_zero(); + } + + LEAVE ("(lot=%s)", gnc_lot_get_title(lot)); + + return value; +} + +/* ============================================================== */ + gnc_numeric xaccSplitGetCapGains(Split * split) { diff --git a/libgnucash/engine/cap-gains.h b/libgnucash/engine/cap-gains.h index c8ced0be1c..9b6b7606f9 100644 --- a/libgnucash/engine/cap-gains.h +++ b/libgnucash/engine/cap-gains.h @@ -187,6 +187,20 @@ gboolean xaccSplitAssign (Split *split); */ Split * xaccSplitAssignToLot (Split *split, GNCLot *lot); +/** The xaccLotFreeSplitCapGain() method returns the value of capital gain + * (if any) associated with the indicated split against the indicated + * lot. In order for there to be any capital gain, a few things must + * hold true about this split: + * (1) It must have been involved in trading (for example, + * by belonging to a stock or trading account) + * (2) It must not be assigned to a lot. + * (3) It cannot be the opening split of a lot; that + * is, it must be a matching sale of an earlier purchase + * (or vice versa). + */ +gnc_numeric xaccLotFreeSplitCapGain(Split *split, GNCLot *lot); + + /** The xaccSplitComputeCapGains() routine computes the cap gains * or losses for the indicated split. The gains are placed into * the 'gains_acct'. If the gains_acct is NULL, then the appropriate diff --git a/libgnucash/engine/gnc-lot.cpp b/libgnucash/engine/gnc-lot.cpp index 8b83efbbac..b6269bd34c 100644 --- a/libgnucash/engine/gnc-lot.cpp +++ b/libgnucash/engine/gnc-lot.cpp @@ -505,7 +505,7 @@ gnc_lot_get_balance (GNCLot *lot) for (node = priv->splits; node; node = node->next) { Split *s = GNC_SPLIT(node->data); - gnc_numeric amt = xaccSplitGetAmount (s); + gnc_numeric amt = xaccSplitGetAdjustedAmount (s); baln = gnc_numeric_add_fixed (baln, amt); g_assert (gnc_numeric_check (baln) == GNC_ERROR_OK); } @@ -561,7 +561,7 @@ gnc_lot_get_balance_before (const GNCLot *lot, const Split *split, if ((ta == tb && source != target) || xaccTransOrder (ta, tb) < 0) { - gnc_numeric tmpval = xaccSplitGetAmount (s); + gnc_numeric tmpval = xaccSplitGetAdjustedAmount (s); amt = gnc_numeric_add_fixed (amt, tmpval); tmpval = xaccSplitGetValue (s); val = gnc_numeric_add_fixed (val, tmpval); diff --git a/libgnucash/engine/policy.cpp b/libgnucash/engine/policy.cpp index f67cc9d822..33346ef86e 100644 --- a/libgnucash/engine/policy.cpp +++ b/libgnucash/engine/policy.cpp @@ -119,7 +119,7 @@ static GNCLot * FIFOPolicyGetLot (GNCPolicy *pcy, Split *split) { if (!split) return nullptr; - return xaccAccountFindEarliestOpenLot (split->acc, split->amount, + return xaccAccountFindEarliestOpenLot (split->acc, xaccSplitGetAdjustedAmount (split), split->parent->common_currency); } @@ -138,7 +138,7 @@ FIFOPolicyGetLotOpening (GNCPolicy *pcy, Split *opening_split; opening_split = gnc_lot_get_earliest_split(lot); - if (ret_amount) *ret_amount = opening_split->amount; + if (ret_amount) *ret_amount = xaccSplitGetAdjustedAmount (opening_split); if (ret_value) *ret_value = opening_split->value; if (ret_currency) *ret_currency = opening_split->parent->common_currency; } diff --git a/libgnucash/engine/test/utest-Account.cpp b/libgnucash/engine/test/utest-Account.cpp index fc52032ba1..fcffccf780 100644 --- a/libgnucash/engine/test/utest-Account.cpp +++ b/libgnucash/engine/test/utest-Account.cpp @@ -1565,7 +1565,7 @@ test_xaccAccountInsertRemoveLot (Fixture *fixture, gconstpointer pData) g_assert_cmpuint (g_list_length (p_priv->lots), == , 1); test_signal_assert_hits (sig1, 2); test_signal_assert_hits (sig4, 1); - test_signal_assert_hits (sig2, 1); + test_signal_assert_hits (sig2, 2); /* Check that removing the lot works */ xaccAccountRemoveLot (parent, lot); /* The following test should fail, but it doesn't because of an @@ -1576,7 +1576,7 @@ test_xaccAccountInsertRemoveLot (Fixture *fixture, gconstpointer pData) g_assert_cmpuint (g_list_length (p_priv->lots), == , 0); test_signal_assert_hits (sig3, 1); test_signal_assert_hits (sig4, 2); - test_signal_assert_hits (sig2, 1); + test_signal_assert_hits (sig2, 2); /* Check that destroying the lot removes its reference */ /* Because the lot's account pointer doesn't get nulled when the lot * is removed, we have to do that for the next test to work: */ @@ -1635,7 +1635,7 @@ test_xaccAccountRecomputeBalance (Fixture *fixture, gconstpointer pData) rec_bal = gnc_numeric_add_fixed (rec_bal, p.amount); } g_assert_true (gnc_numeric_zero_p (priv->starting_balance)); - g_assert_true (gnc_numeric_zero_p (priv->balance)); + priv->cleared_balance = gnc_numeric_zero (); priv->balance_dirty = TRUE; xaccAccountRecomputeBalance (fixture->acct); g_assert_true (gnc_numeric_zero_p (priv->starting_balance)); @@ -2375,6 +2375,7 @@ test_xaccAccountFindOpenLots (Fixture *fixture, gconstpointer pData) LotList* lots; g_assert_true (acct); + xaccAccountRecomputeBalance (acct); lots = xaccAccountFindOpenLots (acct, NULL, NULL, NULL); g_assert_true (g_list_length (lots) == 2); if (lots) g_list_free (lots); diff --git a/libgnucash/engine/test/utest-Split.cpp b/libgnucash/engine/test/utest-Split.cpp index b9b177b6b0..a35cf7e98c 100644 --- a/libgnucash/engine/test/utest-Split.cpp +++ b/libgnucash/engine/test/utest-Split.cpp @@ -91,6 +91,7 @@ setup (Fixture *fixture, gconstpointer pData) fixture->split->balance = amount; fixture->split->cleared_balance = amount; fixture->split->reconciled_balance = amount; + fixture->split->adjusted_amount = amount; qof_instance_mark_clean (QOF_INSTANCE (fixture->split)); qof_instance_mark_clean (QOF_INSTANCE (acc)); qof_instance_mark_clean (QOF_INSTANCE (txn));