gnucash maint: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Thu May 23 12:54:48 EDT 2019


Updated	 via  https://github.com/Gnucash/gnucash/commit/2d4f771b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/f6df367f (commit)
	 via  https://github.com/Gnucash/gnucash/commit/51dda770 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/91aceaa7 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/d16a236e (commit)
	 via  https://github.com/Gnucash/gnucash/commit/0319144b (commit)
	 via  https://github.com/Gnucash/gnucash/commit/5bb54d07 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/df1cc590 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/52895eab (commit)
	 via  https://github.com/Gnucash/gnucash/commit/93bf1a0f (commit)
	from  https://github.com/Gnucash/gnucash/commit/311f7d26 (commit)



commit 2d4f771bedf13ef9a2de4bd45d25d9afc860a8ec
Merge: 311f7d26e f6df367f5
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu May 23 09:50:20 2019 -0700

    Merge Adrién Panella's 'budget' into maint.


commit f6df367f50506aef8ae6f18cffbbbad6db2b945f
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Sat May 18 14:14:27 2019 -0500

    [budget] Remove unused code
    
    Remove some priv attributes and code that were never used

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 697701fce..3710196b3 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -161,10 +161,6 @@ struct GncBudgetViewPrivate
     GtkTreeViewColumn* total_col;
     AccountFilterDialog *fd;
 
-    Account* income;
-    Account* expenses;
-    Account* assets;
-    Account* liabilities;
     Account* rootAcct;
 
     GtkCellRenderer *temp_cr;
@@ -235,29 +231,6 @@ gnc_budget_view_init(GncBudgetView *budget_view)
 
     priv->rootAcct = root;
 
-    for (i = 0; i < num_top_accounts; ++i)
-    {
-        Account* acc = gnc_account_nth_child(root, i);
-        GNCAccountType type = xaccAccountGetType(acc);
-
-        if (type == ACCT_TYPE_ASSET)
-        {
-            priv->assets = acc;
-        }
-        else if (type == ACCT_TYPE_LIABILITY)
-        {
-            priv->liabilities = acc;
-        }
-        else if (type == ACCT_TYPE_INCOME)
-        {
-            priv->income = acc;
-        }
-        else if (type == ACCT_TYPE_EXPENSE)
-        {
-            priv->expenses = acc;
-        }
-    }
-
     LEAVE("");
 }
 

commit 51dda770579efa666ea516a96de7a8f4639e1473
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Fri May 17 23:10:25 2019 -0500

    Bug 577968 - Values are not stored by pressing <TAB> in budget page
    
    Also fix duplicates: 760194

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index e6b06aea2..697701fce 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -118,10 +118,8 @@ static void gbv_create_widget(GncBudgetView *view);
 static gboolean gbv_button_press_cb(
     GtkWidget *widget, GdkEventButton *event, GncBudgetView *view);
 #endif
-#if 0
-static gboolean gbv_key_press_cb(
-    GtkWidget *treeview, GdkEventKey *event, gpointer userdata);
-#endif
+static gboolean gbv_key_press_cb(GtkWidget *treeview, GdkEventKey *event,
+                                 gpointer userdata);
 static void gbv_row_activated_cb(
     GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *col,
     GncBudgetView *view);
@@ -168,6 +166,9 @@ struct GncBudgetViewPrivate
     Account* assets;
     Account* liabilities;
     Account* rootAcct;
+
+    GtkCellRenderer *temp_cr;
+    GtkCellEditable *temp_ce;
 };
 
 G_DEFINE_TYPE_WITH_PRIVATE(GncBudgetView, gnc_budget_view, GTK_TYPE_BOX)
@@ -427,9 +428,6 @@ gbv_create_widget(GncBudgetView *view)
                      G_CALLBACK(gbv_selection_changed_cb), view);
     g_signal_connect(G_OBJECT(tree_view), "button-press-event",
                      G_CALLBACK(gbv_button_press_cb), view);
-    g_signal_connect_after(G_OBJECT(tree_view), "key-press-event",
-                           G_CALLBACK(gbv_key_press_cb), NULL);
-
     gbv_selection_changed_cb(NULL, view);
 #endif
 
@@ -669,39 +667,103 @@ gbv_button_press_cb(GtkWidget *widget, GdkEventButton *event,
 }
 #endif
 
-#if 0
-/** \brief Key press action for gnc budget view.
+/** \brief Key press action for gnc budget view when in editing mode.
+ * Used for navigating with tab while editing.
+ * The handler is for the cell-editable, not for the treeview
 */
 static gboolean
-gbv_key_press_cb(GtkWidget *treeview, GdkEventKey *event, gpointer userdata)
+gbv_key_press_cb(GtkWidget *widget, GdkEventKey *event, gpointer userdata)
 {
-    GtkTreeView *tv = GTK_TREE_VIEW(treeview);
     GtkTreeViewColumn *col;
-    GtkTreePath *path = NULL;
-
-    if (event->type != GDK_KEY_PRESS) return TRUE;
+    GtkTreePath *path          = NULL;
+    GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(userdata);
+    GtkTreeView *tv            = priv->tree_view;
+    gboolean shifted;
+    gint period_num, num_periods;
+    gpointer data;
+
+    if (event->type != GDK_KEY_PRESS || !priv->temp_cr)
+        return FALSE;
 
     switch (event->keyval)
     {
     case GDK_KEY_Tab:
     case GDK_KEY_ISO_Left_Tab:
     case GDK_KEY_KP_Tab:
-    case GDK_KEY_Return:
-    case GDK_KEY_KP_Enter:
+        shifted = event->state & GDK_SHIFT_MASK;
         gtk_tree_view_get_cursor(tv, &path, &col);
-        if (!path) return TRUE;
-        //finish_edit(col);
+        if (!path)
+            return TRUE;
+        data        = g_object_get_data(G_OBJECT(col), "period_num");
+        period_num  = GPOINTER_TO_UINT(data);
+        num_periods = gnc_budget_get_num_periods(priv->budget);
+
+        if (period_num >= num_periods)
+            period_num = num_periods - 1;
+
+        if (shifted)
+            period_num--;
+        else
+            period_num++;
+
+        if (period_num >= num_periods)
+        {
+            period_num = 0;
+            if (gtk_tree_view_row_expanded(tv, path))
+            {
+                gtk_tree_path_down(path);
+            }
+            else
+            {
+                gtk_tree_path_next(path);
+                while (!gnc_tree_view_path_is_valid(GNC_TREE_VIEW(tv), path) &&
+                       gtk_tree_path_get_depth(path) > 1)
+                {
+                    gtk_tree_path_up(path);
+                    gtk_tree_path_next(path);
+                }
+            }
+        }
+        else if (period_num < 0)
+        {
+            period_num = num_periods - 1;
+            if (!gtk_tree_path_prev(path))
+                gtk_tree_path_up(path);
+            else
+                while (gtk_tree_view_row_expanded(tv, path))
+                {
+                    gtk_tree_path_down(path);
+                    do
+                    {
+                        gtk_tree_path_next(path);
+                    } while (
+                        gnc_tree_view_path_is_valid(GNC_TREE_VIEW(tv), path));
+                    gtk_tree_path_prev(path);
+                }
+        }
+
+        col = g_list_nth_data(priv->period_col_list, period_num);
+
+        // finish editing
+        if (priv->temp_ce)
+        {
+            gtk_cell_editable_editing_done(priv->temp_ce);
+            gtk_cell_editable_remove_widget(priv->temp_ce);
+
+            while (gtk_events_pending())
+                gtk_main_iteration();
+        }
+
+        if (gnc_tree_view_path_is_valid(GNC_TREE_VIEW(tv), path))
+            gtk_tree_view_set_cursor(tv, path, col, TRUE);
+        gtk_tree_path_free(path);
         break;
     default:
-        return TRUE;
+        return FALSE;
     }
-    gnc_tree_view_keynav(GNC_TREE_VIEW(tv), &col, path, event);
 
-    if (path && gnc_tree_view_path_is_valid(GNC_TREE_VIEW(tv), path))
-        gtk_tree_view_set_cursor(tv, path, col, TRUE);
     return TRUE;
 }
-#endif
 
 /** \brief gnc budget view actions for resize of treeview.
 */
@@ -1270,6 +1332,30 @@ gbv_col_edited_cb(GtkCellRendererText* cell, gchar* path_string, gchar* new_text
     gtk_widget_queue_draw(GTK_WIDGET(priv->totals_tree_view));
 }
 
+/* The main Start Editing Call back for the budget columns, for key navigation
+ */
+static void
+gdv_editing_started_cb(GtkCellRenderer *cr, GtkCellEditable *editable,
+                       const gchar *path_string, gpointer user_data)
+{
+    GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
+
+    priv->temp_cr = cr;
+    priv->temp_ce = editable;
+
+    g_signal_connect(G_OBJECT(editable), "key-press-event",
+                     G_CALLBACK(gbv_key_press_cb), user_data);
+}
+
+static void
+gdv_editing_canceled_cb(GtkCellRenderer *cr, gpointer user_data)
+{
+    GncBudgetViewPrivate *priv = GNC_BUDGET_VIEW_GET_PRIVATE(user_data);
+
+    priv->temp_cr = NULL;
+    priv->temp_ce = NULL;
+}
+
 /** \brief refreshes the current budget view
 
 The function will step through to only display the columns that are set
@@ -1343,7 +1429,10 @@ gnc_budget_view_refresh(GncBudgetView *view)
         gbv_renderer_add_padding (renderer);
 
         g_signal_connect(G_OBJECT(renderer), "edited", (GCallback)gbv_col_edited_cb, view);
-
+        g_signal_connect(G_OBJECT(renderer), "editing-started",
+                         (GCallback)gdv_editing_started_cb, view);
+        g_signal_connect(G_OBJECT(renderer), "editing-canceled",
+                         (GCallback)gdv_editing_canceled_cb, view);
         col = gbv_create_totals_column(view, num_periods_visible);
         if (col != NULL)
         {

commit 91aceaa71ba4a162d246b60fcdc09b14b6545d62
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Fri May 17 23:04:35 2019 -0500

    Bug 781345 - Equity disregarded by budget totals

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 3c6013ccc..e6b06aea2 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -1125,6 +1125,7 @@ totals_col_source(GtkTreeViewColumn *col, GtkCellRenderer *cell,
                 continue;
             break;
         case ACCT_TYPE_LIABILITY:
+        case ACCT_TYPE_EQUITY:
             if (row_type != TOTALS_TYPE_TRANSFERS &&
                 row_type != TOTALS_TYPE_TOTAL)
                 continue;

commit d16a236eceaaf7dd322996fbbdcafdc42da23789
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Fri May 17 23:03:14 2019 -0500

    [Budget] Fix negative Numbers Not Colored Red

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 92d603177..3c6013ccc 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -869,6 +869,19 @@ gbv_get_accumulated_budget_amount(GncBudget* budget, Account* account, guint per
     return info.total;
 }
 
+static gchar*
+get_negative_color (void)
+{
+    GdkRGBA color;
+    GtkWidget *label = gtk_label_new ("Color");
+    GtkStyleContext *context = gtk_widget_get_style_context (GTK_WIDGET(label));
+    gtk_style_context_add_class (context, "negative-numbers");
+    gtk_style_context_get_color (context, GTK_STATE_FLAG_NORMAL, &color);
+    gtk_widget_destroy(label);
+    
+    return gdk_rgba_to_string(&color);
+}
+
 /** \brief Calculates and displays budget amount for a period in a defined account.
 
    Displays budget amount for a period for an account.  If a budget
@@ -885,6 +898,7 @@ budget_col_source(Account *account, GtkTreeViewColumn *col,
     guint period_num;
     gnc_numeric numeric;
     gchar amtbuff[100]; //FIXME: overkill, where's the #define?
+    gboolean red = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
 
     budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
     bview = GTK_TREE_VIEW(g_object_get_data(G_OBJECT(col), "budget_view"));
@@ -906,10 +920,18 @@ budget_col_source(Account *account, GtkTreeViewColumn *col,
             numeric = gbv_get_accumulated_budget_amount(budget, account, period_num);
             xaccSPrintAmount(amtbuff, numeric,
                              gnc_account_print_info(account, FALSE));
-            if (gnc_is_dark_theme (&color))
-                g_object_set(cell, "foreground", "darkgray", NULL);
+            if (gnc_is_dark_theme(&color))
+                g_object_set(cell, "foreground",
+                             red && gnc_numeric_negative_p(numeric)
+                                 ? "darkred"
+                                 : "darkgray",
+                             NULL);
             else
-                g_object_set(cell, "foreground", "dimgray", NULL);
+                g_object_set(cell, "foreground",
+                             red && gnc_numeric_negative_p(numeric)
+                                 ? "PaleVioletRed"
+                                 : "dimgray",
+                             NULL);
         }
     }
     else
@@ -924,7 +946,11 @@ budget_col_source(Account *account, GtkTreeViewColumn *col,
         {
             xaccSPrintAmount(amtbuff, numeric,
                              gnc_account_print_info(account, FALSE));
-            g_object_set(cell, "foreground", NULL, NULL);
+            g_object_set(cell, "foreground",
+                         red && gnc_numeric_negative_p(numeric)
+                             ? get_negative_color()
+                             : NULL,
+                         NULL);
         }
     }
     return g_strdup(amtbuff);
@@ -994,11 +1020,14 @@ budget_total_col_source(Account *account, GtkTreeViewColumn *col,
     GncBudget *budget;
     gnc_numeric total;
     gchar amtbuff[100]; //FIXME: overkill, where's the #define?
+    gboolean red = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
 
     budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
     total = bgv_get_total_for_account(account, budget, NULL);
     xaccSPrintAmount(amtbuff, total,
                      gnc_account_print_info(account, TRUE));
+    g_object_set(cell, "foreground",
+                 red && gnc_numeric_negative_p(total) ? get_negative_color () : NULL, NULL);
     return g_strdup(amtbuff);
 }
 
@@ -1060,14 +1089,16 @@ totals_col_source(GtkTreeViewColumn *col, GtkCellRenderer *cell,
     gchar amtbuff[100]; //FIXME: overkill, where's the #define?
     gint i;
     gint num_top_accounts;
-    gboolean neg;
+    gboolean neg, red;
     GNCPriceDB *pdb;
     gnc_commodity *total_currency, *currency;
 
+
     gnc_numeric total = gnc_numeric_zero();
 
     view = GNC_BUDGET_VIEW(user_data);
     priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
+    red = gnc_prefs_get_bool(GNC_PREFS_GROUP_GENERAL, GNC_PREF_NEGATIVE_IN_RED);
 
     gtk_tree_model_get(s_model, s_iter, 1, &row_type, -1);
     budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
@@ -1141,7 +1172,7 @@ totals_col_source(GtkTreeViewColumn *col, GtkCellRenderer *cell,
                      gnc_commodity_print_info(total_currency,
                                               period_num < 0 ? TRUE : FALSE));
     g_object_set(cell, "foreground",
-                 gnc_numeric_negative_p(total) ? "red" : NULL, NULL);
+                 red && gnc_numeric_negative_p(total) ? get_negative_color () : NULL, NULL);
 
     g_object_set(G_OBJECT(cell), "text", amtbuff, "xalign", 1.0, NULL);
 }

commit 0319144ba6d04e94d84bbae190952beccee93f63
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Mon May 13 22:57:51 2019 -0500

    Bug 788157 - Open Budget Menu Item Doesn't Select Default Budget

diff --git a/gnucash/gnome/gnc-plugin-budget.c b/gnucash/gnome/gnc-plugin-budget.c
index c1afd95f7..358567303 100644
--- a/gnucash/gnome/gnc-plugin-budget.c
+++ b/gnucash/gnome/gnc-plugin-budget.c
@@ -312,6 +312,13 @@ gnc_budget_gui_select_budget(GtkWindow *parent, QofBook *book)
     gtk_container_add(GTK_CONTAINER (gtk_dialog_get_content_area (dlg)), GTK_WIDGET(tv));
     gtk_widget_show_all(GTK_WIDGET(dlg));
 
+    // Preselect the default budget
+    bgt = gnc_budget_get_default(book);
+    if (bgt && gnc_tree_model_budget_get_iter_for_budget(tm, &iter, bgt))
+    {
+        gtk_tree_view_set_cursor(tv, gtk_tree_model_get_path(tm, &iter), NULL,
+                                 FALSE);
+    }
     bgt = NULL;
     response = gtk_dialog_run(dlg);
     switch (response)

commit 5bb54d073c2fc936ff5cc129110844b18096c599
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Mon May 13 22:56:04 2019 -0500

    [budget] Fix error in qof_book property path
    
    The "Default budget" property had an erroneus path
    and was not returning any value.

diff --git a/libgnucash/engine/qofbook.cpp b/libgnucash/engine/qofbook.cpp
index 3991e96d6..8e7dc93cf 100644
--- a/libgnucash/engine/qofbook.cpp
+++ b/libgnucash/engine/qofbook.cpp
@@ -163,6 +163,8 @@ qof_book_init (QofBook *book)
 
 static const std::string str_KVP_OPTION_PATH(KVP_OPTION_PATH);
 static const std::string str_OPTION_SECTION_ACCOUNTS(OPTION_SECTION_ACCOUNTS);
+static const std::string str_OPTION_SECTION_BUDGETING(OPTION_SECTION_BUDGETING);
+static const std::string str_OPTION_NAME_DEFAULT_BUDGET(OPTION_NAME_DEFAULT_BUDGET);
 static const std::string str_OPTION_NAME_TRADING_ACCOUNTS(OPTION_NAME_TRADING_ACCOUNTS);
 static const std::string str_OPTION_NAME_AUTO_READONLY_DAYS(OPTION_NAME_AUTO_READONLY_DAYS);
 static const std::string str_OPTION_NAME_NUM_FIELD_SOURCE(OPTION_NAME_NUM_FIELD_SOURCE);
@@ -206,7 +208,7 @@ qof_book_get_property (GObject* object,
         break;
     case PROP_OPT_DEFAULT_BUDGET:
         qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
+                str_OPTION_SECTION_BUDGETING, str_OPTION_NAME_DEFAULT_BUDGET});
         break;
     case PROP_OPT_FY_END:
         qof_instance_get_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});
@@ -261,7 +263,7 @@ qof_book_set_property (GObject      *object,
         break;
     case PROP_OPT_DEFAULT_BUDGET:
         qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {str_KVP_OPTION_PATH,
-                str_OPTION_SECTION_ACCOUNTS, OPTION_NAME_DEFAULT_BUDGET});
+                str_OPTION_SECTION_BUDGETING, OPTION_NAME_DEFAULT_BUDGET});
         break;
     case PROP_OPT_FY_END:
         qof_instance_set_path_kvp (QOF_INSTANCE (book), value, {"fy_end"});

commit df1cc5902b17916f8da7da9866e9371bfadcb8d0
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Thu May 9 08:07:34 2019 -0500

    Bug 676810 - Wrong accounting in multi-currency budget report
    
    Adds multicurrency support in budget page
    Convert currencies before summing up to parent and totals.

diff --git a/gnucash/gnome/gnc-budget-view.c b/gnucash/gnome/gnc-budget-view.c
index 6fe4d9eda..92d603177 100644
--- a/gnucash/gnome/gnc-budget-view.c
+++ b/gnucash/gnome/gnc-budget-view.c
@@ -804,6 +804,8 @@ typedef struct
     gnc_numeric total;
     GncBudget* budget;
     guint period_num;
+    GNCPriceDB *pdb;
+    gnc_commodity *total_currency;
 } BudgetAccumulationInfo;
 
 /** \brief Function to assist in the calculation of sub-account totals.
@@ -815,16 +817,29 @@ budget_accum_helper(Account* account, gpointer data)
 {
     BudgetAccumulationInfo* info = (BudgetAccumulationInfo*)data;
     gnc_numeric numeric;
+    gnc_commodity *currency;
+
+    currency = gnc_account_get_currency_or_parent(account);
 
     if (gnc_budget_is_account_period_value_set(info->budget, account, info->period_num))
     {
-        numeric = gnc_budget_get_account_period_value(info->budget, account, info->period_num);
-        info->total = gnc_numeric_add(info->total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+        numeric = gnc_budget_get_account_period_value(info->budget, account,
+                                                      info->period_num);
+        numeric = gnc_pricedb_convert_balance_nearest_price_t64(
+                    info->pdb, numeric, currency, info->total_currency,
+                    gnc_budget_get_period_start_date(info->budget, info->period_num));
+        info->total = gnc_numeric_add(info->total, numeric, GNC_DENOM_AUTO,
+                                      GNC_HOW_DENOM_LCD);
     }
     else if (gnc_account_n_children(account) != 0)
     {
-        numeric = gbv_get_accumulated_budget_amount(info->budget, account, info->period_num);
-        info->total = gnc_numeric_add(info->total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+        numeric = gbv_get_accumulated_budget_amount(info->budget, account,
+                                                    info->period_num);
+        numeric = gnc_pricedb_convert_balance_nearest_price_t64(
+                    info->pdb, numeric, currency, info->total_currency,
+                    gnc_budget_get_period_start_date(info->budget, info->period_num));
+        info->total = gnc_numeric_add(info->total, numeric, GNC_DENOM_AUTO,
+                                      GNC_HOW_DENOM_LCD);
     }
 }
 
@@ -840,6 +855,8 @@ gbv_get_accumulated_budget_amount(GncBudget* budget, Account* account, guint per
     info.total = gnc_numeric_zero();
     info.budget = budget;
     info.period_num = period_num;
+    info.pdb = gnc_pricedb_get_db (gnc_account_get_book (account));
+    info.total_currency = gnc_account_get_currency_or_parent(account);
 
     if (!gnc_budget_is_account_period_value_set(budget, account, period_num))
     {
@@ -917,12 +934,20 @@ budget_col_source(Account *account, GtkTreeViewColumn *col,
  totals column to the right.
 */
 static gnc_numeric
-bgv_get_total_for_account(Account* account, GncBudget* budget)
+bgv_get_total_for_account(Account* account, GncBudget* budget, gnc_commodity *new_currency)
 {
     guint num_periods;
     int period_num;
     gnc_numeric numeric;
     gnc_numeric total = gnc_numeric_zero();
+    GNCPriceDB *pdb;
+    gnc_commodity *currency;
+
+    if (new_currency)
+    {
+        pdb      = gnc_pricedb_get_db(gnc_get_current_book());
+        currency = gnc_account_get_currency_or_parent(account);
+    }
 
     num_periods = gnc_budget_get_num_periods(budget);
     for (period_num = 0; period_num < num_periods; ++period_num)
@@ -932,6 +957,13 @@ bgv_get_total_for_account(Account* account, GncBudget* budget)
             if (gnc_account_n_children(account) != 0)
             {
                 numeric = gbv_get_accumulated_budget_amount(budget, account, period_num);
+
+                if (new_currency)
+                {
+                    numeric = gnc_pricedb_convert_balance_nearest_price_t64(
+                                pdb, numeric, currency, new_currency,
+                                gnc_budget_get_period_start_date(budget, period_num));
+                }
                 total = gnc_numeric_add(total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
             }
         }
@@ -940,6 +972,12 @@ bgv_get_total_for_account(Account* account, GncBudget* budget)
             numeric = gnc_budget_get_account_period_value(budget, account, period_num);
             if (!gnc_numeric_check(numeric))
             {
+                if (new_currency)
+                {
+                    numeric = gnc_pricedb_convert_balance_nearest_price_t64(
+                                pdb, numeric, currency, new_currency,
+                                gnc_budget_get_period_start_date(budget, period_num));
+                }
                 total = gnc_numeric_add(total, numeric, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
             }
         }
@@ -958,9 +996,9 @@ budget_total_col_source(Account *account, GtkTreeViewColumn *col,
     gchar amtbuff[100]; //FIXME: overkill, where's the #define?
 
     budget = GNC_BUDGET(g_object_get_data(G_OBJECT(col), "budget"));
-    total = bgv_get_total_for_account(account, budget);
+    total = bgv_get_total_for_account(account, budget, NULL);
     xaccSPrintAmount(amtbuff, total,
-                     gnc_account_print_info(account, FALSE));
+                     gnc_account_print_info(account, TRUE));
     return g_strdup(amtbuff);
 }
 
@@ -1022,11 +1060,11 @@ totals_col_source(GtkTreeViewColumn *col, GtkCellRenderer *cell,
     gchar amtbuff[100]; //FIXME: overkill, where's the #define?
     gint i;
     gint num_top_accounts;
+    gboolean neg;
+    GNCPriceDB *pdb;
+    gnc_commodity *total_currency, *currency;
 
-    gnc_numeric totalincome = gnc_numeric_zero();
-    gnc_numeric totalexpenses = gnc_numeric_zero();
-    gnc_numeric totalassets = gnc_numeric_zero();
-    gnc_numeric totalliabilities = gnc_numeric_zero();
+    gnc_numeric total = gnc_numeric_zero();
 
     view = GNC_BUDGET_VIEW(user_data);
     priv = GNC_BUDGET_VIEW_GET_PRIVATE(view);
@@ -1036,93 +1074,75 @@ totals_col_source(GtkTreeViewColumn *col, GtkCellRenderer *cell,
     period_num = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(col),
                                  "period_num"));
 
+    pdb = gnc_pricedb_get_db (gnc_get_current_book());
+    total_currency = gnc_default_currency();
     num_top_accounts = gnc_account_n_children(priv->rootAcct);
 
     // step through each child account of the root, find the total income, expenses, liabilities, and assets.
 
     for (i = 0; i < num_top_accounts; ++i)
     {
-        account = gnc_account_nth_child(priv->rootAcct, i);
+        account  = gnc_account_nth_child(priv->rootAcct, i);
+        currency = gnc_account_get_currency_or_parent(account);
+        neg      = FALSE;
 
+        switch (xaccAccountGetType(account))
+        {
+        case ACCT_TYPE_INCOME:
+            if (row_type != TOTALS_TYPE_INCOME &&
+                row_type != TOTALS_TYPE_TOTAL)
+                continue;
+            break;
+        case ACCT_TYPE_LIABILITY:
+            if (row_type != TOTALS_TYPE_TRANSFERS &&
+                row_type != TOTALS_TYPE_TOTAL)
+                continue;
+            break;
+        case ACCT_TYPE_EXPENSE:
+            if (row_type == TOTALS_TYPE_TOTAL)
+                neg = TRUE;
+            else if (row_type != TOTALS_TYPE_EXPENSES)
+                continue;
+            break;
+        case ACCT_TYPE_ASSET:
+            if (row_type != TOTALS_TYPE_TRANSFERS &&
+                row_type != TOTALS_TYPE_TOTAL)
+                continue;
+            neg = TRUE;
+            break;
+        default:
+            continue;
+        }
         // find the total for this account
 
         if (period_num < 0)
         {
-            value = bgv_get_total_for_account(account, budget);
+            value = bgv_get_total_for_account(account, budget, total_currency);
         }
         else
         {
-            value = gbv_get_accumulated_budget_amount(budget, account, period_num);
-        }
-
-        // test for what account type, and add 'value' to the appopriate total
+            value =
+                gbv_get_accumulated_budget_amount(budget, account, period_num);
 
-        if (xaccAccountGetType(account) == ACCT_TYPE_INCOME)
-        {
-            totalincome = gnc_numeric_add(totalincome, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-        }
-        else if (xaccAccountGetType(account) == ACCT_TYPE_EXPENSE)
-        {
-            totalexpenses = gnc_numeric_add(totalexpenses, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-        }
-        else if (xaccAccountGetType(account) == ACCT_TYPE_ASSET)
-        {
-            totalassets = gnc_numeric_add(totalassets, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-        }
-        else if (xaccAccountGetType(account) == ACCT_TYPE_LIABILITY)
-        {
-            totalliabilities = gnc_numeric_add(totalliabilities, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
+            value = gnc_pricedb_convert_balance_nearest_price_t64(
+                        pdb, value, currency, total_currency,
+                        gnc_budget_get_period_start_date(budget, period_num));
         }
+
+        if (neg)
+            total = gnc_numeric_sub(total, value, GNC_DENOM_AUTO,
+                                    GNC_HOW_DENOM_LCD);
         else
-        {
-            // Do nothing because this account is not of interest
-        }
+            total = gnc_numeric_add(total, value, GNC_DENOM_AUTO,
+                                    GNC_HOW_DENOM_LCD);
     }
 
-    // at this point we should have variables holding the values for assets, liabilities, expenses and incomes.
-
-    // Set the text to display, depending on which of the totals rows we are currently looking at
+    xaccSPrintAmount(amtbuff, total,
+                     gnc_commodity_print_info(total_currency,
+                                              period_num < 0 ? TRUE : FALSE));
+    g_object_set(cell, "foreground",
+                 gnc_numeric_negative_p(total) ? "red" : NULL, NULL);
 
-    if (row_type == TOTALS_TYPE_INCOME)
-    {
-        // FIXME: There must be a better way to get the GncAccountPrintInfo object than this. Would prefer to depreciate the tracking of top level accounts.
-        xaccSPrintAmount(amtbuff, totalincome,
-                         gnc_account_print_info(priv->income, FALSE));
-        g_object_set(cell, "foreground", NULL, NULL);
-    }
-    else if (row_type == TOTALS_TYPE_EXPENSES)
-    {
-        xaccSPrintAmount(amtbuff, totalexpenses,
-                         gnc_account_print_info(priv->expenses, FALSE));
-        g_object_set(cell, "foreground", NULL, NULL);
-    }
-    else if (row_type == TOTALS_TYPE_TRANSFERS)
-    {
-        xaccSPrintAmount(amtbuff, gnc_numeric_sub(totalassets, totalliabilities, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
-                         gnc_account_print_info(priv->assets, FALSE));
-        g_object_set(cell, "foreground", NULL, NULL);
-    }
-    else if (row_type == TOTALS_TYPE_TOTAL)
-    {
-        value = gnc_numeric_sub(totalincome, totalexpenses, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-        value = gnc_numeric_sub(value, totalassets, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-        value = gnc_numeric_add(value, totalliabilities, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
-        xaccSPrintAmount(amtbuff, value,
-                         gnc_account_print_info(priv->assets, FALSE));
-        if (gnc_numeric_negative_p(value))
-        {
-            g_object_set(cell, "foreground", "red", NULL);
-        }
-        else
-        {
-            g_object_set(cell, "foreground", NULL, NULL);
-        }
-    }
-    else
-    {
-        // if it reaches here then the row type was not set correctly
-        g_strlcpy(amtbuff, "error", sizeof(amtbuff));
-    }
     g_object_set(G_OBJECT(cell), "text", amtbuff, "xalign", 1.0, NULL);
 }
 

commit 52895eabd1996f9cf3693a813889dcb29a3e0ccd
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Wed May 15 21:50:36 2019 -0500

    Bug 679800 - Shortcut to allow population of all budgeting periods
    
    Add functionality to change at once the budget for all
    periods of selected accounts.
    It can :
    replace with a new value
    unset the budget
    add a value to current budget
    multiply current budget with value
    
    Solvese issue #679800 and a duplicates (#639404, #760634)

diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index 8bb2283f9..d47ae121c 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -115,6 +115,8 @@ static void gnc_plugin_page_budget_cmd_view_options(
     GtkAction *action, GncPluginPageBudget *page);
 static void gnc_plugin_page_budget_cmd_estimate_budget(
     GtkAction *action, GncPluginPageBudget *page);
+static void gnc_plugin_page_budget_cmd_allperiods_budget(
+    GtkAction *action, GncPluginPageBudget *page);
 
 static GtkActionEntry gnc_plugin_page_budget_actions [] =
 {
@@ -151,6 +153,12 @@ static GtkActionEntry gnc_plugin_page_budget_actions [] =
         N_("Estimate a budget value for the selected accounts from past transactions"),
         G_CALLBACK (gnc_plugin_page_budget_cmd_estimate_budget)
     },
+    {
+        "AllPeriodsBudgetAction", "system-run", N_("All Periods"),
+        NULL,
+        N_("Edit budget for all periods for the selected accounts"),
+        G_CALLBACK (gnc_plugin_page_budget_cmd_allperiods_budget)
+    },
 
     /* View menu */
     {
@@ -179,9 +187,18 @@ static action_toolbar_labels toolbar_labels[] =
     { "DeleteBudgetAction",         N_("Delete") },
     { "OptionsBudgetAction",        N_("Options") },
     { "EstimateBudgetAction",       N_("Estimate") },
+    { "AllPeriodsBudgetAction",     N_("All Periods") },
     { NULL, NULL },
 };
 
+typedef enum allperiods_action 
+{
+    REPLACE,
+    ADD,
+    MULTIPLY,
+    UNSET
+} allperiods_action; 
+
 typedef struct GncPluginPageBudgetPrivate
 {
     GtkActionGroup *action_group;
@@ -205,6 +222,11 @@ typedef struct GncPluginPageBudgetPrivate
     Recurrence r;
     gint sigFigs;
     gboolean useAvg;
+
+    /* For the allPeriods value dialog */
+    gnc_numeric allValue;
+    allperiods_action action;
+
 } GncPluginPageBudgetPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageBudget, gnc_plugin_page_budget, GNC_TYPE_PLUGIN_PAGE)
@@ -864,11 +886,13 @@ estimate_budget_helper(GtkTreeModel *model, GtkTreePath *path,
     if (priv->useAvg && num_periods)
     {
         num = xaccAccountGetBalanceChangeForPeriod(acct, 
-            recurrenceGetPeriodTime(&priv->r, 0, FALSE),
-            recurrenceGetPeriodTime(&priv->r, num_periods - 1, TRUE), TRUE);
+                recurrenceGetPeriodTime(&priv->r, 0, FALSE),
+                recurrenceGetPeriodTime(&priv->r, num_periods - 1, TRUE), TRUE);
         num = gnc_numeric_div(num, 
-            gnc_numeric_create(num_periods, 1), GNC_DENOM_AUTO,
-            GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) | GNC_HOW_RND_ROUND_HALF_UP);
+                              gnc_numeric_create(num_periods, 1), 
+                              GNC_DENOM_AUTO,
+                              GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) | 
+                              GNC_HOW_RND_ROUND_HALF_UP);
 
         if (gnc_reverse_balance(acct))
             num = gnc_numeric_neg(num);
@@ -979,6 +1003,133 @@ gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
     g_object_unref(G_OBJECT(builder));
 }
 
+static void
+allperiods_budget_helper(GtkTreeModel *model, GtkTreePath *path,
+                         GtkTreeIter *iter, gpointer data)
+{
+    Account *acct;
+    guint num_periods, i;
+    gnc_numeric num;
+    GncPluginPageBudgetPrivate *priv;
+    GncPluginPageBudget *page = data;
+
+    g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
+    priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
+    acct = gnc_budget_view_get_account_from_path(priv->budget_view, path);
+    num_periods = gnc_budget_get_num_periods(priv->budget);
+    num = priv->allValue;
+
+    for (i = 0; i < num_periods; i++)
+    {
+        switch (priv->action)
+        {
+        case ADD:
+            num = gnc_budget_get_account_period_value(priv->budget, acct, i);
+            num = gnc_numeric_add(num, priv->allValue, GNC_DENOM_AUTO,
+                                  GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) |
+                                  GNC_HOW_RND_ROUND_HALF_UP);
+            gnc_budget_set_account_period_value(priv->budget, acct, i, num);
+            break;
+        case MULTIPLY:
+            num = gnc_budget_get_account_period_value(priv->budget, acct, i);
+            num = gnc_numeric_mul(num, priv->allValue, GNC_DENOM_AUTO,
+                                  GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) |
+                                  GNC_HOW_RND_ROUND_HALF_UP);
+            gnc_budget_set_account_period_value(priv->budget, acct, i, num);
+            break;
+        case UNSET:
+            gnc_budget_unset_account_period_value(priv->budget, acct, i);
+            break;
+        default:
+            gnc_budget_set_account_period_value(priv->budget, acct, i,
+                                                priv->allValue);
+            break;
+        }
+    }
+}
+
+/*******************************/
+/*  All Periods Value Dialog   */
+/*******************************/
+static void
+gnc_plugin_page_budget_cmd_allperiods_budget(GtkAction *action,
+                                             GncPluginPageBudget *page)
+{
+    GncPluginPageBudgetPrivate *priv;
+    GtkTreeSelection *sel;
+    GtkWidget *dialog, *gde, *val, *dtr, *add, *mult;
+    gint result;
+    GtkBuilder *builder;
+    const gchar *txt;
+
+    g_return_if_fail(GNC_IS_PLUGIN_PAGE_BUDGET(page));
+    priv = GNC_PLUGIN_PAGE_BUDGET_GET_PRIVATE(page);
+    sel = gnc_budget_view_get_selection(priv->budget_view);
+
+    if (gtk_tree_selection_count_selected_rows(sel) <= 0)
+    {
+        dialog = gtk_message_dialog_new(
+                    GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))),
+                    GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
+                    GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s",
+                    _("You must select at least one account to edit."));
+        gtk_dialog_run(GTK_DIALOG(dialog));
+        gtk_widget_destroy(dialog);
+        return;
+    }
+
+    builder = gtk_builder_new();
+    gnc_builder_add_from_file(builder, "gnc-plugin-page-budget.glade",
+                              "DigitsToRound_Adj");
+    gnc_builder_add_from_file(builder, "gnc-plugin-page-budget.glade",
+                              "budget_allperiods_dialog");
+
+    dialog = GTK_WIDGET(
+        gtk_builder_get_object(builder, "budget_allperiods_dialog"));
+
+    gtk_window_set_transient_for(
+        GTK_WINDOW(dialog),
+        GTK_WINDOW(gnc_plugin_page_get_window(GNC_PLUGIN_PAGE(page))));
+
+    val = GTK_WIDGET(gtk_builder_get_object(builder, "Value"));
+    gtk_entry_set_text(GTK_ENTRY(val), "");
+
+    dtr = GTK_WIDGET(gtk_builder_get_object(builder, "DigitsToRound1"));
+    gtk_spin_button_set_value(GTK_SPIN_BUTTON(dtr), (gdouble)priv->sigFigs);
+
+    add  = GTK_WIDGET(gtk_builder_get_object(builder, "RB_Add"));
+    mult = GTK_WIDGET(gtk_builder_get_object(builder, "RB_Multiply"));
+
+    gtk_widget_show_all(dialog);
+    result = gtk_dialog_run(GTK_DIALOG(dialog));
+    switch (result)
+    {
+    case GTK_RESPONSE_OK:
+
+        priv->sigFigs = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dtr));
+        priv->action = REPLACE;
+        txt = gtk_entry_get_text(GTK_ENTRY(val));
+
+        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(add)))
+            priv->action = ADD;
+        else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mult)))
+            priv->action = MULTIPLY;
+
+        if (priv->action == REPLACE &&
+            !gtk_entry_get_text_length(GTK_ENTRY(val)))
+            priv->action = UNSET;
+
+        if (xaccParseAmount(txt, TRUE, &priv->allValue, NULL) ||
+            priv->action == UNSET)
+            gtk_tree_selection_selected_foreach(sel, allperiods_budget_helper,
+                                                page);
+        break;
+    default:
+        break;
+    }
+    gtk_widget_destroy(dialog);
+    g_object_unref(G_OBJECT(builder));
+}
 
 static void
 gnc_plugin_page_budget_cmd_view_filter_by (GtkAction *action,
diff --git a/gnucash/gtkbuilder/gnc-plugin-page-budget.glade b/gnucash/gtkbuilder/gnc-plugin-page-budget.glade
index 088d8b504..7f102b3dd 100644
--- a/gnucash/gtkbuilder/gnc-plugin-page-budget.glade
+++ b/gnucash/gtkbuilder/gnc-plugin-page-budget.glade
@@ -9,6 +9,244 @@
     <property name="step_increment">1</property>
     <property name="page_increment">1</property>
   </object>
+  <object class="GtkDialog" id="budget_allperiods_dialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Edit budget for all periods</property>
+    <property name="modal">True</property>
+    <property name="type_hint">dialog</property>
+    <child>
+      <placeholder/>
+    </child>
+    <child internal-child="vbox">
+      <object class="GtkBox">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="cancelbutton3">
+                <property name="label" translatable="yes">_Cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="okbutton3">
+                <property name="label" translatable="yes">_OK</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="receives_default">False</property>
+                <property name="use_underline">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="vbox1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="valign">start</property>
+                <property name="label" translatable="yes">
+                  Use a fixed value or apply transformation for all periods.
+                </property>
+                <property name="wrap">True</property>
+                <property name="width_chars">40</property>
+                <property name="max_width_chars">40</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">2</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkGrid" id="table1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="label" translatable="yes">Value:</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkEntry" id="Value">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="input_purpose">number</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Action</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkRadioButton" id="RB_Replace">
+                    <property name="label" translatable="yes">Replace</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">
+                      Replace the budget for all periods with new 'value'. Use empty value to unset budget for the accounts.
+                    </property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="RB_Add">
+                    <property name="label" translatable="yes">Add</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">
+                      Add 'value' to current budget for each period
+                    </property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                    <property name="group">RB_Replace</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="RB_Multiply">
+                    <property name="label" translatable="yes">Multiply</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="tooltip_text" translatable="yes">
+                      Multiply current budget for each period by 'value'
+                    </property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                    <property name="group">RB_Replace</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSpinButton" id="DigitsToRound1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="tooltip_text" translatable="yes">The number of leading digits to keep when rounding</property>
+                <property name="hexpand">True</property>
+                <property name="text" translatable="yes">1</property>
+                <property name="primary_icon_activatable">False</property>
+                <property name="secondary_icon_activatable">False</property>
+                <property name="adjustment">DigitsToRound_Adj</property>
+                <property name="climb_rate">1</property>
+                <property name="numeric">True</property>
+                <property name="value">1</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">start</property>
+                <property name="label" translatable="yes">Significant Digits:</property>
+              </object>
+              <packing>
+                <property name="left_attach">0</property>
+                <property name="top_attach">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">3</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="-6">cancelbutton3</action-widget>
+      <action-widget response="-5">okbutton3</action-widget>
+    </action-widgets>
+  </object>
   <object class="GtkDialog" id="budget_estimate_dialog">
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
diff --git a/gnucash/ui/gnc-plugin-page-budget-ui.xml b/gnucash/ui/gnc-plugin-page-budget-ui.xml
index 87fcceb11..9f45fe45a 100644
--- a/gnucash/ui/gnc-plugin-page-budget-ui.xml
+++ b/gnucash/ui/gnc-plugin-page-budget-ui.xml
@@ -3,6 +3,7 @@
     <menu name="Edit" action="EditAction">
       <placeholder name="EditSelectedPlaceholder">
         <menuitem name="Estimate" action="EstimateBudgetAction"/>
+        <menuitem name="AllPeriods" action="AllPeriodsBudgetAction"/>
         <menuitem name="Delete" action="DeleteBudgetAction"/>
       </placeholder>
       <menuitem name="Options" action="OptionsBudgetAction"/>
@@ -22,6 +23,7 @@
       <toolitem name="Options" action="OptionsBudgetAction"/>
       <separator name="ToolbarSep4"/>
       <toolitem name="Estimate" action="EstimateBudgetAction"/>
+      <toolitem name="AllPeriods" action="AllPeriodsBudgetAction"/>
       <toolitem name="Delete" action="DeleteBudgetAction"/>
     </placeholder>
   </toolbar>

commit 93bf1a0f23913fb9c88e29470c782e3aac4de92b
Author: Adrian Panella <ianchi74 at outlook.com>
Date:   Sat May 4 20:47:52 2019 -0500

    Bug 646361 - Estimate budget option to have same amount in each budget period
    
    If set, all periods get the same budget, from the average
    of the reference period.

diff --git a/gnucash/gnome/gnc-plugin-page-budget.c b/gnucash/gnome/gnc-plugin-page-budget.c
index ccddfd306..8bb2283f9 100644
--- a/gnucash/gnome/gnc-plugin-page-budget.c
+++ b/gnucash/gnome/gnc-plugin-page-budget.c
@@ -204,6 +204,7 @@ typedef struct GncPluginPageBudgetPrivate
     /* For the estimation dialog */
     Recurrence r;
     gint sigFigs;
+    gboolean useAvg;
 } GncPluginPageBudgetPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginPageBudget, gnc_plugin_page_budget, GNC_TYPE_PLUGIN_PAGE)
@@ -309,6 +310,7 @@ gnc_plugin_page_budget_init (GncPluginPageBudget *plugin_page)
     priv->fd.filter_override = g_hash_table_new (g_direct_hash, g_direct_equal);
 
     priv->sigFigs = 1;
+    priv->useAvg = FALSE;
     recurrenceSet(&priv->r, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE);
 
     LEAVE("page %p, priv %p, action group %p",
@@ -859,19 +861,38 @@ estimate_budget_helper(GtkTreeModel *model, GtkTreePath *path,
 
     num_periods = gnc_budget_get_num_periods(priv->budget);
 
-    for (i = 0; i < num_periods; i++)
+    if (priv->useAvg && num_periods)
     {
-        num = recurrenceGetAccountPeriodValue(&priv->r, acct, i);
-        if (!gnc_numeric_check(num))
-        {
-            if (gnc_reverse_balance (acct))
-                num = gnc_numeric_neg (num);
+        num = xaccAccountGetBalanceChangeForPeriod(acct, 
+            recurrenceGetPeriodTime(&priv->r, 0, FALSE),
+            recurrenceGetPeriodTime(&priv->r, num_periods - 1, TRUE), TRUE);
+        num = gnc_numeric_div(num, 
+            gnc_numeric_create(num_periods, 1), GNC_DENOM_AUTO,
+            GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) | GNC_HOW_RND_ROUND_HALF_UP);
 
+        if (gnc_reverse_balance(acct))
+            num = gnc_numeric_neg(num);
 
-            num = gnc_numeric_convert(num, GNC_DENOM_AUTO,
-                                      GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) | GNC_HOW_RND_ROUND_HALF_UP);
-            gnc_budget_set_account_period_value(
-                priv->budget, acct, i, num);
+        for (i = 0; i < num_periods; i++)
+        {
+            gnc_budget_set_account_period_value(priv->budget, acct, i, num);
+        }
+    }
+    else
+    {
+        for (i = 0; i < num_periods; i++)
+        {
+            num = recurrenceGetAccountPeriodValue(&priv->r, acct, i);
+            if (!gnc_numeric_check(num))
+            {
+                if (gnc_reverse_balance(acct))
+                    num = gnc_numeric_neg(num);
+
+                num = gnc_numeric_convert(num, GNC_DENOM_AUTO,
+                                          GNC_HOW_DENOM_SIGFIGS(priv->sigFigs) |
+                                          GNC_HOW_RND_ROUND_HALF_UP);
+                gnc_budget_set_account_period_value(priv->budget, acct, i, num);
+            }
         }
     }
 }
@@ -886,7 +907,7 @@ gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
 {
     GncPluginPageBudgetPrivate *priv;
     GtkTreeSelection *sel;
-    GtkWidget *dialog, *gde, *dtr, *hb;
+    GtkWidget *dialog, *gde, *dtr, *hb, *avg;
     gint result;
     GDate date;
     const Recurrence *r;
@@ -930,6 +951,9 @@ gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
     gtk_spin_button_set_value(GTK_SPIN_BUTTON(dtr),
                               (gdouble)priv->sigFigs);
 
+    avg = GTK_WIDGET(gtk_builder_get_object(builder, "UseAverage"));
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(avg), priv->useAvg);
+
     gtk_widget_show_all (dialog);
     result = gtk_dialog_run(GTK_DIALOG(dialog));
     switch (result)
@@ -944,6 +968,8 @@ gnc_plugin_page_budget_cmd_estimate_budget(GtkAction *action,
         priv->sigFigs =
             gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dtr));
 
+        priv->useAvg = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(avg));
+        
         gtk_tree_selection_selected_foreach(sel, estimate_budget_helper, page);
         break;
     default:
diff --git a/gnucash/gtkbuilder/gnc-plugin-page-budget.glade b/gnucash/gtkbuilder/gnc-plugin-page-budget.glade
index da3c11e75..088d8b504 100644
--- a/gnucash/gtkbuilder/gnc-plugin-page-budget.glade
+++ b/gnucash/gtkbuilder/gnc-plugin-page-budget.glade
@@ -151,6 +151,22 @@
                 <property name="top_attach">0</property>
               </packing>
             </child>
+            <child>
+              <object class="GtkCheckButton" id="UseAverage">
+                <property name="label" translatable="yes">Use Average</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="tooltip_text" translatable="yes">
+                  Use the average value over all actual periods for all projected periods
+                </property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="top_attach">2</property>
+              </packing>
+            </child>
           </object>
           <packing>
             <property name="expand">False</property>



Summary of changes:
 gnucash/gnome/gnc-budget-view.c                 | 380 +++++++++++++++---------
 gnucash/gnome/gnc-plugin-budget.c               |   7 +
 gnucash/gnome/gnc-plugin-page-budget.c          | 199 ++++++++++++-
 gnucash/gtkbuilder/gnc-plugin-page-budget.glade | 254 ++++++++++++++++
 gnucash/ui/gnc-plugin-page-budget-ui.xml        |   2 +
 libgnucash/engine/qofbook.cpp                   |   6 +-
 6 files changed, 702 insertions(+), 146 deletions(-)



More information about the gnucash-changes mailing list