Merge Bob Fewell's 'bug798673' into master.

This commit is contained in:
John Ralls 2022-12-30 16:00:12 -08:00
commit 31d79e3004
13 changed files with 930 additions and 6 deletions

View File

@ -106,6 +106,14 @@ typedef struct _AccountWindow
GtkTreeView *parent_tree;
GtkWidget *parent_scroll;
GtkWidget *more_properties_page;
GtkWidget *balance_grid;
GtkWidget *higher_balance_limit_edit;
GtkWidget *lower_balance_limit_edit;
GtkWidget *include_balance_sub_accts;
gboolean balance_is_reversed;
GtkWidget *opening_balance_button;
GtkWidget *opening_balance_edit;
GtkWidget *opening_balance_date_edit;
@ -250,6 +258,8 @@ gnc_account_to_ui (AccountWindow *aw)
GdkRGBA color;
gboolean flag, nonstd_scu;
gint index;
gnc_numeric balance_limit;
gboolean balance_limit_valid;
ENTER("%p", aw);
account = aw_get_account (aw);
@ -325,6 +335,41 @@ gnc_account_to_ui (AccountWindow *aw)
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(aw->hidden_button),
flag);
aw->balance_is_reversed = gnc_reverse_balance (account);
flag = xaccAccountGetIncludeSubAccountBalances (account);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(aw->include_balance_sub_accts),
flag);
balance_limit_valid = xaccAccountGetHigherBalanceLimit (account, &balance_limit);
if (balance_limit_valid)
{
if (aw->balance_is_reversed)
{
balance_limit = gnc_numeric_neg (balance_limit);
gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(aw->lower_balance_limit_edit),
balance_limit);
}
else
gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(aw->higher_balance_limit_edit),
balance_limit);
}
balance_limit_valid = xaccAccountGetLowerBalanceLimit (account, &balance_limit);
if (balance_limit_valid)
{
if (aw->balance_is_reversed)
{
balance_limit = gnc_numeric_neg (balance_limit);
gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(aw->higher_balance_limit_edit),
balance_limit);
}
else
gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT(aw->lower_balance_limit_edit),
balance_limit);
}
set_auto_interest_box (aw);
LEAVE(" ");
}
@ -393,6 +438,9 @@ gnc_ui_to_account (AccountWindow *aw)
GdkRGBA color;
gboolean flag;
gnc_numeric balance;
gnc_numeric balance_limit;
gint higher_balance_limit_valid;
gint lower_balance_limit_valid;
gboolean use_equity, nonstd;
time64 date;
gint index, old_scu, new_scu;
@ -505,6 +553,60 @@ gnc_ui_to_account (AccountWindow *aw)
if (parent_account != gnc_account_get_parent (account))
gnc_account_append_child (parent_account, account);
flag = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(
aw->include_balance_sub_accts));
xaccAccountSetIncludeSubAccountBalances (account, flag);
higher_balance_limit_valid = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(
aw->higher_balance_limit_edit),
&balance_limit, TRUE, NULL);
if (higher_balance_limit_valid == 0)
{
if (aw->balance_is_reversed)
{
balance_limit = gnc_numeric_neg (balance_limit);
xaccAccountSetLowerBalanceLimit (account, balance_limit);
}
else
xaccAccountSetHigherBalanceLimit (account, balance_limit);
}
if (higher_balance_limit_valid == -1)
{
if (aw->balance_is_reversed)
xaccAccountClearLowerBalanceLimit (account);
else
xaccAccountClearHigherBalanceLimit (account);
}
lower_balance_limit_valid = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(
aw->lower_balance_limit_edit),
&balance_limit, TRUE, NULL);
if (lower_balance_limit_valid == 0)
{
if (aw->balance_is_reversed)
{
balance_limit = gnc_numeric_neg (balance_limit);
xaccAccountSetHigherBalanceLimit (account, balance_limit);
}
else
xaccAccountSetLowerBalanceLimit (account, balance_limit);
}
if (lower_balance_limit_valid == -1)
{
if (aw->balance_is_reversed)
xaccAccountClearHigherBalanceLimit (account);
else
xaccAccountClearLowerBalanceLimit (account);
}
if ((higher_balance_limit_valid == -1) && (lower_balance_limit_valid == -1))
xaccAccountSetIncludeSubAccountBalances (account, FALSE);
xaccAccountCommitEdit (account);
balance = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT(aw->opening_balance_edit));
@ -811,6 +913,10 @@ gnc_common_ok (AccountWindow *aw)
gnc_commodity * commodity;
gchar *fullname, *fullname_parent;
const gchar *name, *separator;
gboolean higher_limit_valid;
gnc_numeric higher_balance_limit;
gboolean lower_limit_valid;
gnc_numeric lower_balance_limit;
ENTER("aw %p", aw);
root = gnc_book_get_root_account (aw->book);
@ -892,6 +998,34 @@ gnc_common_ok (AccountWindow *aw)
return FALSE;
}
/* check for higher balance limit greater than lower */
higher_limit_valid = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(aw->higher_balance_limit_edit),
&higher_balance_limit, TRUE, NULL);
lower_limit_valid = gnc_amount_edit_expr_is_valid (GNC_AMOUNT_EDIT(aw->lower_balance_limit_edit),
&lower_balance_limit, TRUE, NULL);
if ((lower_limit_valid == 0) && (higher_limit_valid == 0))
{
gint compare = gnc_numeric_compare (higher_balance_limit,
lower_balance_limit);
if ((compare == 0) && (!gnc_numeric_zero_p (higher_balance_limit)))
{
const char *message = _("Balance limits must be different unless they are both zero.");
gnc_error_dialog (GTK_WINDOW(aw->dialog), "%s", message);
LEAVE("invalid balance limit, both the same but not zero");
return FALSE;
}
else if (compare == -1)
{
const char *message = _("The lower balance limit must be less than the higher limit.");
gnc_error_dialog (GTK_WINDOW(aw->dialog), "%s", message);
LEAVE("invalid balance limit, lower limit not less than upper");
return FALSE;
}
}
LEAVE("passed");
return TRUE;
}
@ -1488,6 +1622,29 @@ gnc_account_window_create (GtkWindow *parent, AccountWindow *aw)
g_signal_connect (G_OBJECT(selection), "changed",
G_CALLBACK(gnc_account_parent_changed_cb), aw);
aw->balance_grid = GTK_WIDGET(gtk_builder_get_object (builder, "balance_grid"));
box = GTK_WIDGET(gtk_builder_get_object (builder, "higher_balance_limit_hbox"));
aw->higher_balance_limit_edit = gnc_amount_edit_new ();
gtk_box_pack_start (GTK_BOX(box), aw->higher_balance_limit_edit, TRUE, TRUE, 0);
gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT(aw->higher_balance_limit_edit), TRUE);
gnc_amount_edit_set_validate_on_change (GNC_AMOUNT_EDIT(aw->higher_balance_limit_edit), TRUE);
gnc_amount_edit_show_warning_symbol (GNC_AMOUNT_EDIT(aw->higher_balance_limit_edit), TRUE);
gtk_widget_show (aw->higher_balance_limit_edit);
box = GTK_WIDGET(gtk_builder_get_object (builder, "lower_balance_limit_hbox"));
aw->lower_balance_limit_edit = gnc_amount_edit_new ();
gtk_box_pack_start (GTK_BOX(box), aw->lower_balance_limit_edit, TRUE, TRUE, 0);
gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT(aw->lower_balance_limit_edit), TRUE);
gnc_amount_edit_set_validate_on_change (GNC_AMOUNT_EDIT(aw->lower_balance_limit_edit), TRUE);
gnc_amount_edit_show_warning_symbol (GNC_AMOUNT_EDIT(aw->lower_balance_limit_edit), TRUE);
gtk_widget_show (aw->lower_balance_limit_edit);
aw->include_balance_sub_accts = GTK_WIDGET(gtk_builder_get_object (builder, "include_sub_accts_tb"));
aw->more_properties_page =
gtk_notebook_get_nth_page (GTK_NOTEBOOK(aw->notebook), 1);
aw->opening_balance_button = GTK_WIDGET(gtk_builder_get_object (builder, "opening_balance_button"));
aw->tax_related_button = GTK_WIDGET(gtk_builder_get_object (builder, "tax_related_button"));
aw->placeholder_button = GTK_WIDGET(gtk_builder_get_object (builder, "placeholder_button"));
@ -1515,7 +1672,7 @@ gnc_account_window_create (GtkWindow *parent, AccountWindow *aw)
gtk_widget_show (date_edit);
aw->opening_balance_page =
gtk_notebook_get_nth_page (GTK_NOTEBOOK(aw->notebook), 1);
gtk_notebook_get_nth_page (GTK_NOTEBOOK(aw->notebook), 2);
aw->opening_equity_radio = GTK_WIDGET(gtk_builder_get_object (builder,
"opening_equity_radio"));

View File

@ -394,6 +394,7 @@ gnc_tree_model_account_get_column_type (GtkTreeModel *tree_model, int index)
case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE:
case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT:
case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD:
case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT:
case GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED:
@ -830,6 +831,12 @@ gnc_tree_model_account_get_value (GtkTreeModel *tree_model,
g_free (string);
break;
case GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT:
g_value_init (value, G_TYPE_STRING);
string = gnc_ui_account_get_balance_limit_icon_name (account);
g_value_set_string (value, string);
break;
case GNC_TREE_MODEL_ACCOUNT_COL_CLEARED:
g_value_init (value, G_TYPE_STRING);
string = gnc_ui_account_get_print_balance (xaccAccountGetClearedBalanceInCurrency,

View File

@ -66,6 +66,7 @@ typedef enum
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE,
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_PERIOD,
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT,
GNC_TREE_MODEL_ACCOUNT_COL_CLEARED,
GNC_TREE_MODEL_ACCOUNT_COL_CLEARED_REPORT,
GNC_TREE_MODEL_ACCOUNT_COL_RECONCILED,

View File

@ -763,7 +763,7 @@ gnc_tree_view_account_new_with_root (Account *root, gboolean show_root)
GtkTreePath *virtual_root_path = NULL;
const gchar *sample_type, *sample_commodity;
GncTreeViewAccountPrivate *priv;
GtkTreeViewColumn *tax_info_column, *acc_color_column;
GtkTreeViewColumn *tax_info_column, *acc_color_column, *acc_balance_limit_column;
GtkCellRenderer *renderer;
GList *col_list = NULL, *node = NULL;
@ -957,6 +957,22 @@ gnc_tree_view_account_new_with_root (Account *root, gboolean show_root)
/* Also add the full title to the column header as a tooltip */
gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (acc_color_column), _("Account Color"));
acc_balance_limit_column
= gnc_tree_view_add_pix_column (view,
C_("Column header for 'Balance Limit'", "L"),
"account-balance-limit",
"xx",
GNC_TREE_MODEL_ACCOUNT_COL_BALANCE_LIMIT,
GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS,
NULL);
/* Add the full title to the object for menu creation */
g_object_set_data_full(G_OBJECT(acc_balance_limit_column), REAL_TITLE,
g_strdup(_("Balance Limit")), g_free);
/* Also add the full title to the column header as a tooltip */
gtk_widget_set_tooltip_text (gtk_tree_view_column_get_button (acc_balance_limit_column), _("Balance Limit"));
priv->notes_column
= gnc_tree_view_add_text_view_column(view, _("Notes"), "notes", NULL,
"Sample account notes.",

View File

@ -1957,6 +1957,63 @@ gnc_tree_view_add_text_view_column (GncTreeView *view,
column_sort_fn);
}
/** This function adds a new pixbuf view column to a GncTreeView base view.
* It takes all the parameters necessary to hook a GtkTreeModel
* column to a GtkTreeViewColumn. If the tree has a state section
* associated with it, this function also wires up the column so that
* its visibility and width are remembered.
*
* Parameters are defined in gnc-tree-view.h
*/
GtkTreeViewColumn *
gnc_tree_view_add_pix_column (GncTreeView *view,
const gchar *column_title,
const gchar *pref_name,
const gchar *sizing_text,
gint model_data_column,
gint model_visibility_column,
GtkTreeIterCompareFunc column_sort_fn)
{
GtkTreeViewColumn *column;
PangoLayout* layout;
int default_width, title_width;
GtkCellRenderer *renderer;
g_return_val_if_fail (GNC_IS_TREE_VIEW(view), NULL);
renderer = gtk_cell_renderer_pixbuf_new ();
column = gtk_tree_view_column_new ();
gtk_tree_view_column_set_title (column, column_title);
/* Set up a text renderer and attributes */
gtk_tree_view_column_pack_start (column, renderer, TRUE);
/* Set renderer attributes controlled by the model */
if (model_data_column != GNC_TREE_VIEW_COLUMN_DATA_NONE)
gtk_tree_view_column_add_attribute (column, renderer,
"icon-name", model_data_column);
if (model_visibility_column != GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS)
gtk_tree_view_column_add_attribute (column, renderer,
"visible", model_visibility_column);
/* Default size is the larger of the column title and the sizing text */
layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), column_title);
pango_layout_get_pixel_size (layout, &title_width, NULL);
g_object_unref (layout);
layout = gtk_widget_create_pango_layout (GTK_WIDGET(view), sizing_text);
pango_layout_get_pixel_size (layout, &default_width, NULL);
g_object_unref (layout);
default_width = MAX(default_width, title_width);
if (default_width)
default_width += 10; /* padding on either side */
gnc_tree_view_column_properties (view, column, pref_name, model_data_column,
default_width, TRUE, column_sort_fn);
gnc_tree_view_append_column (view, column);
return column;
}
/** This function adds a new date column to a GncTreeView base view.
* It takes all the parameters necessary to hook a GtkTreeModel

View File

@ -246,6 +246,47 @@ gnc_tree_view_add_text_view_column (GncTreeView *view,
gint model_visibility_column,
GtkTreeIterCompareFunc column_sort_fn);
/** This function adds a pixbuf view column to a GncTreeView base view.
* It takes all the parameters necessary to hook a GtkTreeModel
* column to a GtkTreeViewColumn.
*
* @param view A pointer to a generic GncTreeView.
*
* @param column_title The title for this column.
*
* @param pref_name The internal name of this column. This name is
* used in several functions to look up the column, and it is also
* used when saving/restoring the view's state.
*
* @param sizing_text A string used to compute the default width of
* the column. This text is never displayed.
*
* @param model_data_column The index of the GtkTreeModel data column
* used to determine the data that will be displayed in this column
* for each row in the view. Use GNC_TREE_VIEW_COLUMN_DATA_NONE if
* you plan on using a non-model data source for this column. This
* index is connected to the "icon-name" attribute of the cell renderer.
*
* @param model_visibility_column The index of the GtkTreeModel data
* column used to determine whether or not a checkbox for each row
* will be displayed at all. Use GNC_TREE_VIEW_COLUMN_VISIBLE_ALWAYS
* if the checkbox should be displayed in all rows.
*
* @param column_sort_fn The function that GtkTreeModelSort
* will call to compare two rows to determine their displayed
* order.
*
* @return The newly created GtkTreeViewColumn.
*/
GtkTreeViewColumn *
gnc_tree_view_add_pix_column (GncTreeView *view,
const gchar *column_title,
const gchar *pref_name,
const gchar *sizing_text,
gint model_data_column,
gint model_visibility_column,
GtkTreeIterCompareFunc column_sort_fn);
/** This function adds a new combobox column to a GncTreeView base
* view. The parameters it takes in common with
* gnc_tree_view_add_text_column() behave the same as there. In

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.22"/>
<object class="GtkDialog" id="account_cascade_dialog">
@ -1612,7 +1612,155 @@
</packing>
</child>
<child>
<object class="GtkBox" id="vbox302">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="border-width">6</property>
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
<!-- n-columns=2 n-rows=4 -->
<object class="GtkGrid" id="balance_grid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="row-spacing">3</property>
<property name="column-spacing">6</property>
<child>
<object class="GtkLabel" id="higher_balance_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">_Higher Balance Limit</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">higher_balance_limit_hbox</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="higher_balance_limit_hbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">If a value is present, an indication can be shown in the chart of accounts when today's balance is more than this value.
i.e.
Today's balance of -90 will show icon if limit is set to -100
Today's balance of 100 will show icon if limit is set to 90
Clear the entry to have no warning.</property>
<property name="hexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="lower_balance_limit_hbox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">If a value is present, an indication can be shown in the chart of accounts when today's balance is less than this value.
i.e.
Today's balance of -100 will show icon if limit is set to -90
Today's balance of 90 will show icon if limit is set to 100
Clear the entry to have no warning.</property>
<property name="hexpand">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="lower_balance_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">_Lower Balance Limit</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Balance Limit</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="include_sub_accts_tb">
<property name="label" translatable="yes">_Include sub accounts</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<style>
<class name="gnc-class-account"/>
</style>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">More Properties</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab-fill">False</property>
</packing>
</child>
<child>
<object class="GtkBox" id="vbox1">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">6</property>
@ -1787,7 +1935,7 @@
</style>
</object>
<packing>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
@ -1798,7 +1946,7 @@
<property name="justify">center</property>
</object>
<packing>
<property name="position">1</property>
<property name="position">2</property>
<property name="tab-fill">False</property>
</packing>
</child>

View File

@ -250,6 +250,111 @@ gnc_ui_account_get_reconciled_balance_as_of_date (Account *account,
xaccAccountGetReconciledBalanceAsOfDate);
}
static gint
account_balance_limit_reached (const Account *account, gnc_numeric balance_limit)
{
gboolean inc_sub = xaccAccountGetIncludeSubAccountBalances (account);
// we use today's date because account may have future dated splits
gnc_numeric balance = gnc_ui_account_get_balance_as_of_date (
(Account*)account,
gnc_time64_get_day_end (gnc_time (NULL)),
inc_sub);
if (gnc_numeric_zero_p (balance))
return 0;
if (gnc_reverse_balance (account))
balance_limit = gnc_numeric_neg (balance_limit);
// Returns 1 if a>b, -1 if b>a, 0 if a == b
return gnc_numeric_compare (balance, balance_limit);
}
gboolean
gnc_ui_account_is_higher_balance_limit_reached (const Account *account,
gboolean *is_zero)
{
gnc_numeric balance_limit;
gboolean limit_valid = FALSE;
gboolean retval = FALSE;
gint compare_result;
g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE);
if (gnc_reverse_balance (account))
limit_valid = xaccAccountGetLowerBalanceLimit (account, &balance_limit);
else
limit_valid = xaccAccountGetHigherBalanceLimit (account, &balance_limit);
if (!limit_valid)
return retval;
if (gnc_numeric_zero_p (balance_limit))
*is_zero = TRUE;
if (account_balance_limit_reached (account, balance_limit) == 1)
retval = TRUE;
return retval;
}
gboolean
gnc_ui_account_is_lower_balance_limit_reached (const Account *account,
gboolean *is_zero)
{
gnc_numeric balance_limit;
gboolean limit_valid = FALSE;
gboolean retval = FALSE;
gint compare_result;
g_return_val_if_fail (GNC_IS_ACCOUNT(account), FALSE);
if (gnc_reverse_balance (account))
limit_valid = xaccAccountGetHigherBalanceLimit (account, &balance_limit);
else
limit_valid = xaccAccountGetLowerBalanceLimit (account, &balance_limit);
if (!limit_valid)
return retval;
if (gnc_numeric_zero_p (balance_limit))
*is_zero = TRUE;
if (account_balance_limit_reached (account, balance_limit) == -1)
retval = TRUE;
return retval;
}
gchar *
gnc_ui_account_get_balance_limit_icon_name (const Account *account)
{
gboolean lower_limit_reached, higher_limit_reached;
gboolean lower_is_zero = FALSE;
gboolean higher_is_zero = FALSE;
g_return_val_if_fail (GNC_IS_ACCOUNT(account), g_strdup (""));
higher_limit_reached = gnc_ui_account_is_higher_balance_limit_reached (account, &higher_is_zero);
// assume the higher value will be set mostly so test that first
if (higher_limit_reached && !higher_is_zero)
return g_strdup ("go-top");
lower_limit_reached = gnc_ui_account_is_lower_balance_limit_reached (account, &lower_is_zero);
if (lower_limit_reached && (!lower_is_zero || !higher_is_zero))
return g_strdup ("go-bottom");
if (higher_limit_reached && !lower_is_zero)
return g_strdup ("go-top");
if ((lower_limit_reached || higher_limit_reached ) && lower_is_zero && higher_is_zero)
return g_strdup ("dialog-warning");
return g_strdup ("");
}
/********************************************************************
* Balance calculations related to owners

View File

@ -154,4 +154,36 @@ gchar * gnc_ui_owner_get_print_report_balance (GncOwner *owner,
*/
GList * gnc_account_get_autoclear_splits (Account *account, gnc_numeric toclear_value,
gchar **errmsg);
/** Test the account balance as of today for it passing the
* higher limit if set.
*
* @param account A pointer to the account.
*
* @param is_zero A return value, set to TRUE if limit is zero
*
* @return TRUE if account balance has passed limit.
*/
gboolean gnc_ui_account_is_higher_balance_limit_reached (const Account *account, gboolean *is_zero);
/** Test the account balance as of today for it passing the
* lower limit if set.
*
* @param account A pointer to the account.
*
* @param is_zero A return value, set to TRUE if limit is zero
*
* @return TRUE if account balance has passed limit.
*/
gboolean gnc_ui_account_is_lower_balance_limit_reached (const Account *account, gboolean *is_zero);
/** Test the account balance as of today for it passing the
* lower and higher limits if set.
*
* @param account A pointer to the account.
*
* @return The icon name to be displayed.
*/
gchar * gnc_ui_account_get_balance_limit_icon_name (const Account *account);
#endif /* GNC_UI_BALANCES_H_ */

View File

@ -73,6 +73,11 @@ static const std::string AB_ACCOUNT_UID("account-uid");
static const std::string AB_BANK_CODE("bank-code");
static const std::string AB_TRANS_RETRIEVAL("trans-retrieval");
static const std::string KEY_BALANCE_LIMIT("balance-limit");
static const std::string KEY_BALANCE_HIGHER_LIMIT_VALUE("higher-value");
static const std::string KEY_BALANCE_LOWER_LIMIT_VALUE("lower-value");
static const std::string KEY_BALANCE_INCLUDE_SUB_ACCTS("inlude-sub-accts");
static gnc_numeric GetBalanceAsOfDate (Account *acc, time64 date, gboolean ignclosing);
using FinalProbabilityVec=std::vector<std::pair<std::string, int32_t>>;
@ -328,6 +333,12 @@ gnc_account_init(Account* acc)
priv->starting_reconciled_balance = gnc_numeric_zero();
priv->balance_dirty = FALSE;
priv->higher_balance_limit = gnc_numeric_create (1,0);
priv->higher_balance_cached = false;
priv->lower_balance_limit = gnc_numeric_create (1,0);
priv->lower_balance_cached = false;
priv->include_sub_account_balances = TriState::Unset;
priv->last_num = (char*) is_unset;
priv->tax_us_code = (char*) is_unset;
priv->tax_us_pns = (char*) is_unset;
@ -4930,6 +4941,252 @@ xaccAccountSetLastNum (Account *acc, const char *num)
set_kvp_string_tag (acc, "last-num", priv->last_num);
}
/********************************************************************\
\********************************************************************/
gboolean
xaccAccountGetHigherBalanceLimit (const Account *acc,
gnc_numeric *balance)
{
g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
if (GET_PRIVATE(acc)->higher_balance_cached)
{
*balance = GET_PRIVATE(acc)->higher_balance_limit;
if (gnc_numeric_check (*balance) == 0)
return true;
else
return false;
}
else
{
gnc_numeric bal = gnc_numeric_create (1,0);
GValue v = G_VALUE_INIT;
gboolean retval = false;
qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {KEY_BALANCE_LIMIT,
KEY_BALANCE_HIGHER_LIMIT_VALUE});
if (G_VALUE_HOLDS_BOXED(&v))
{
bal = *(gnc_numeric*)g_value_get_boxed (&v);
if (bal.denom)
{
if (balance)
*balance = bal;
retval = true;
}
}
g_value_unset (&v);
GET_PRIVATE(acc)->higher_balance_limit = bal;
GET_PRIVATE(acc)->higher_balance_cached = true;
return retval;
}
}
gboolean
xaccAccountGetLowerBalanceLimit (const Account *acc,
gnc_numeric *balance)
{
g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
if (GET_PRIVATE(acc)->lower_balance_cached)
{
*balance = GET_PRIVATE(acc)->lower_balance_limit;
if (gnc_numeric_check (*balance) == 0)
return true;
else
return false;
}
else
{
gnc_numeric bal = gnc_numeric_create (1,0);
GValue v = G_VALUE_INIT;
gboolean retval = false;
qof_instance_get_path_kvp (QOF_INSTANCE(acc), &v, {KEY_BALANCE_LIMIT,
KEY_BALANCE_LOWER_LIMIT_VALUE});
if (G_VALUE_HOLDS_BOXED(&v))
{
bal = *(gnc_numeric*)g_value_get_boxed (&v);
if (bal.denom)
{
if (balance)
*balance = bal;
retval = true;
}
}
g_value_unset (&v);
GET_PRIVATE(acc)->lower_balance_limit = bal;
GET_PRIVATE(acc)->lower_balance_cached = true;
return retval;
}
}
static void
set_balance_limits (Account *acc, gnc_numeric balance, gboolean higher)
{
gnc_numeric balance_limit;
gboolean balance_limit_valid;
std::vector<std::string> path {KEY_BALANCE_LIMIT};
if (higher)
{
path.push_back (KEY_BALANCE_HIGHER_LIMIT_VALUE);
balance_limit_valid = xaccAccountGetHigherBalanceLimit (acc, &balance_limit);
}
else
{
path.push_back (KEY_BALANCE_LOWER_LIMIT_VALUE);
balance_limit_valid = xaccAccountGetLowerBalanceLimit (acc, &balance_limit);
}
if (!balance_limit_valid || gnc_numeric_compare (balance, balance_limit) != 0)
{
GValue v = G_VALUE_INIT;
g_value_init (&v, GNC_TYPE_NUMERIC);
g_value_set_boxed (&v, &balance);
xaccAccountBeginEdit (acc);
qof_instance_set_path_kvp (QOF_INSTANCE(acc), &v, path);
if (higher)
{
GET_PRIVATE(acc)->higher_balance_limit.denom = balance.denom;
GET_PRIVATE(acc)->higher_balance_limit.num = balance.num;
GET_PRIVATE(acc)->higher_balance_cached = true;
}
else
{
GET_PRIVATE(acc)->lower_balance_limit.denom = balance.denom;
GET_PRIVATE(acc)->lower_balance_limit.num = balance.num;
GET_PRIVATE(acc)->lower_balance_cached = true;
}
mark_account (acc);
xaccAccountCommitEdit (acc);
g_value_unset (&v);
}
}
void
xaccAccountSetHigherBalanceLimit (Account *acc, gnc_numeric balance)
{
g_return_if_fail (GNC_IS_ACCOUNT(acc));
if (gnc_numeric_check (balance) != 0)
return;
set_balance_limits (acc, balance, true);
}
void
xaccAccountSetLowerBalanceLimit (Account *acc, gnc_numeric balance)
{
g_return_if_fail (GNC_IS_ACCOUNT(acc));
if (gnc_numeric_check (balance) != 0)
return;
set_balance_limits (acc, balance, false);
}
static void
clear_balance_limits (Account *acc, gboolean higher)
{
gnc_numeric balance_limit;
gboolean balance_limit_valid;
std::vector<std::string> path {KEY_BALANCE_LIMIT};
if (higher)
{
path.push_back (KEY_BALANCE_HIGHER_LIMIT_VALUE);
balance_limit_valid = xaccAccountGetHigherBalanceLimit (acc, &balance_limit);
}
else
{
path.push_back (KEY_BALANCE_LOWER_LIMIT_VALUE);
balance_limit_valid = xaccAccountGetLowerBalanceLimit (acc, &balance_limit);
}
if (balance_limit_valid)
{
xaccAccountBeginEdit (acc);
qof_instance_set_path_kvp (QOF_INSTANCE(acc), nullptr, path);
qof_instance_slot_path_delete_if_empty (QOF_INSTANCE(acc), {KEY_BALANCE_LIMIT});
if (higher)
GET_PRIVATE(acc)->higher_balance_cached = false;
else
GET_PRIVATE(acc)->lower_balance_cached = false;
mark_account (acc);
xaccAccountCommitEdit (acc);
}
}
void
xaccAccountClearHigherBalanceLimit (Account *acc)
{
g_return_if_fail (GNC_IS_ACCOUNT(acc));
clear_balance_limits (acc, true);
}
void
xaccAccountClearLowerBalanceLimit (Account *acc)
{
g_return_if_fail (GNC_IS_ACCOUNT(acc));
clear_balance_limits (acc, false);
}
gboolean
xaccAccountGetIncludeSubAccountBalances (const Account *acc)
{
g_return_val_if_fail (GNC_IS_ACCOUNT(acc), false);
if (GET_PRIVATE(acc)->include_sub_account_balances == TriState::Unset)
{
gboolean inc_sub = boolean_from_key (acc, {KEY_BALANCE_LIMIT,
KEY_BALANCE_INCLUDE_SUB_ACCTS});
GET_PRIVATE(acc)->include_sub_account_balances = inc_sub ? TriState::True
: TriState::False;
}
return GET_PRIVATE(acc)->include_sub_account_balances == TriState::True;
}
void
xaccAccountSetIncludeSubAccountBalances (Account *acc, gboolean inc_sub)
{
g_return_if_fail (GNC_IS_ACCOUNT(acc));
if (inc_sub != xaccAccountGetIncludeSubAccountBalances (acc))
{
GValue v = G_VALUE_INIT;
g_value_init (&v, G_TYPE_BOOLEAN);
g_value_set_boolean (&v, inc_sub);
std::vector<std::string> path {KEY_BALANCE_LIMIT,
KEY_BALANCE_INCLUDE_SUB_ACCTS};
xaccAccountBeginEdit (acc);
if (inc_sub)
qof_instance_set_path_kvp (QOF_INSTANCE(acc), &v, path);
else
qof_instance_set_path_kvp (QOF_INSTANCE(acc), nullptr, path);
GET_PRIVATE(acc)->include_sub_account_balances =
inc_sub ? TriState::True : TriState::False;
mark_account (acc);
xaccAccountCommitEdit (acc);
g_value_unset (&v);
}
}
/********************************************************************\
\********************************************************************/
static Account *
GetOrMakeOrphanAccount (Account *root, gnc_commodity * currency)
{

View File

@ -1182,6 +1182,74 @@ typedef enum
void xaccAccountClearReconcilePostpone (Account *account);
/** @} */
/** @name Account Balance Limits
@{
*/
/** Get the higher balance limit for the account.
*
* @param account The account whose higher limit is to be retrieved
*
* @param balance The placeholder to store the retrieved balance
*
* @return True if the limit is valid. */
gboolean xaccAccountGetHigherBalanceLimit (const Account *account,
gnc_numeric *balance);
/** Set the higher balance limit for the account.
*
* @param account The account whose higher limit is to be saved
*
* @param balance The balance to be saved
*/
void xaccAccountSetHigherBalanceLimit (Account *account, gnc_numeric balance);
/** Clear the higher balance limit for the account.
*
* @param account The account to clear the limit on
*/
void xaccAccountClearHigherBalanceLimit (Account *account);
/** Get the lower balance limit for the account.
*
* @param account The account whose lower limit is to be retrieved
*
* @param balance The placeholder to store the retrieved balance
*
* @return True if the limit is valid. */
gboolean xaccAccountGetLowerBalanceLimit (const Account *account,
gnc_numeric *balance);
/** Set the lower balance limit for the account.
*
* @param account The account whose lower limit is to be saved
*
* @param balance The balance to be saved
*/
void xaccAccountSetLowerBalanceLimit (Account *account, gnc_numeric balance);
/** Clear the lower balance limit for the account.
*
* @param account The account to clear the limit on
*/
void xaccAccountClearLowerBalanceLimit (Account *account);
/** Get whether to include balances of sub accounts.
*
* @param account The account to get setting on
*
* @return TRUE to include, default is FALSE
*/
gboolean xaccAccountGetIncludeSubAccountBalances (const Account *account);
/** Set whether to include balances of sub accounts.
*
* @param account The account to set the setting on
*
* @param include Set to TRUE for including sub account balances
*/
void xaccAccountSetIncludeSubAccountBalances (Account *account, gboolean include);
/** @} */
/** DOCUMENT ME! */
typedef enum

View File

@ -121,6 +121,12 @@ typedef struct AccountPrivate
gnc_numeric cleared_balance;
gnc_numeric reconciled_balance;
gnc_numeric higher_balance_limit;
gboolean higher_balance_cached;
gnc_numeric lower_balance_limit;
gboolean lower_balance_cached;
TriState include_sub_account_balances;
gboolean balance_dirty; /* balances in splits incorrect */
GList *splits; /* list of split pointers */

View File

@ -1089,6 +1089,9 @@ test_gnc_account_kvp_setters_getters (Fixture *fixture, gconstpointer pData)
{
Account *account = xaccMallocAccount (gnc_account_get_book (fixture->acct));
xaccAccountSetType (account, ACCT_TYPE_EQUITY);
gnc_numeric h_balance = gnc_numeric_create (1700, 100);
gnc_numeric l_balance = gnc_numeric_create (1000, 100);
gnc_numeric balance_limit;
// equity_type getter/setter
g_assert (xaccAccountGetIsOpeningBalance (account) == FALSE);
@ -1231,6 +1234,32 @@ test_gnc_account_kvp_setters_getters (Fixture *fixture, gconstpointer pData)
xaccAccountSetNotes (account, nullptr);
g_assert_cmpstr (xaccAccountGetNotes (account), ==, nullptr);
// Balance Limits getter/setter
g_assert (xaccAccountGetHigherBalanceLimit (account, &balance_limit) == false);
g_assert (xaccAccountGetLowerBalanceLimit (account, &balance_limit) == false);
g_assert (xaccAccountGetIncludeSubAccountBalances (account) == false);
xaccAccountSetHigherBalanceLimit (account, h_balance);
xaccAccountSetLowerBalanceLimit (account, l_balance);
xaccAccountSetIncludeSubAccountBalances (account, true);
g_assert (xaccAccountGetHigherBalanceLimit (account, &balance_limit) == true);
g_assert_cmpint (gnc_numeric_compare (h_balance, balance_limit), ==, 0);
g_assert (xaccAccountGetLowerBalanceLimit (account, &balance_limit) == true);
g_assert_cmpint (gnc_numeric_compare (l_balance, balance_limit), ==, 0);
g_assert (xaccAccountGetIncludeSubAccountBalances (account) == true);
xaccAccountSetIncludeSubAccountBalances (account, false);
xaccAccountClearHigherBalanceLimit (account);
xaccAccountClearLowerBalanceLimit (account);
g_assert (xaccAccountGetHigherBalanceLimit (account, &balance_limit) == false);
g_assert (xaccAccountGetLowerBalanceLimit (account, &balance_limit) == false);
g_assert (xaccAccountGetIncludeSubAccountBalances (account) == false);
// STOCK_ACCOUNT tests from now on
xaccAccountSetType (account, ACCT_TYPE_STOCK);