Bug 603379 - Prevent changing some Account Options if it has transactions - followup

Relax the account type change restrictions again. In the new implementation
account types can't be changed for an account with splits:
- if the change woud force a commodity change (to/from normal accounts from/to stock related accounts)
- for immutable accounts

At the time of this commit the following account types are considered immutable:
- Accounts Receivable
- Accounts Payable
- Trading accounts
This commit is contained in:
Geert Janssens 2017-06-20 21:35:10 +02:00
parent 4032d33b48
commit c80dad742e
4 changed files with 89 additions and 14 deletions

View File

@ -4029,6 +4029,46 @@ xaccAccountGetTypeStr(GNCAccountType type)
/********************************************************************\ /********************************************************************\
\********************************************************************/ \********************************************************************/
guint32
xaccAccountTypesCompatibleWith (GNCAccountType type)
{
switch (type)
{
case ACCT_TYPE_BANK:
case ACCT_TYPE_CASH:
case ACCT_TYPE_ASSET:
case ACCT_TYPE_CREDIT:
case ACCT_TYPE_LIABILITY:
case ACCT_TYPE_INCOME:
case ACCT_TYPE_EXPENSE:
case ACCT_TYPE_EQUITY:
return
(1 << ACCT_TYPE_BANK) |
(1 << ACCT_TYPE_CASH) |
(1 << ACCT_TYPE_ASSET) |
(1 << ACCT_TYPE_CREDIT) |
(1 << ACCT_TYPE_LIABILITY) |
(1 << ACCT_TYPE_INCOME) |
(1 << ACCT_TYPE_EXPENSE) |
(1 << ACCT_TYPE_EQUITY);
case ACCT_TYPE_STOCK:
case ACCT_TYPE_MUTUAL:
case ACCT_TYPE_CURRENCY:
return
(1 << ACCT_TYPE_STOCK) |
(1 << ACCT_TYPE_MUTUAL) |
(1 << ACCT_TYPE_CURRENCY);
case ACCT_TYPE_RECEIVABLE:
return (1 << ACCT_TYPE_RECEIVABLE);
case ACCT_TYPE_PAYABLE:
return (1 << ACCT_TYPE_PAYABLE);
case ACCT_TYPE_TRADING:
return (1 << ACCT_TYPE_TRADING);
default:
PERR("bad account type: %d", type);
return 0;
}
}
guint32 guint32
xaccParentAccountTypesCompatibleWith (GNCAccountType type) xaccParentAccountTypesCompatibleWith (GNCAccountType type)
{ {

View File

@ -926,6 +926,11 @@ GNCAccountType xaccAccountStringToEnum (const char* str);
* to the local language. */ * to the local language. */
const char * xaccAccountGetTypeStr (GNCAccountType type); const char * xaccAccountGetTypeStr (GNCAccountType type);
/** Return the bitmask of account types compatible with a given type.
* That is, you could switch to any of the account types in the compatible
* list without unwanted side-effects. */
guint32 xaccAccountTypesCompatibleWith (GNCAccountType type);
/** Return the bitmask of parent account types compatible with a given type. */ /** Return the bitmask of parent account types compatible with a given type. */
guint32 xaccParentAccountTypesCompatibleWith (GNCAccountType type); guint32 xaccParentAccountTypesCompatibleWith (GNCAccountType type);

View File

@ -2189,6 +2189,7 @@ test_xaccAccountType_Stuff (void)
} }
/* xaccParentAccountTypesCompatibleWith /* xaccParentAccountTypesCompatibleWith
* xaccAccountTypesCompatible * xaccAccountTypesCompatible
* xaccAccountTypesCompatibleWith
guint32 guint32
xaccParentAccountTypesCompatibleWith (GNCAccountType type)// C: 5 in 3 */ xaccParentAccountTypesCompatibleWith (GNCAccountType type)// C: 5 in 3 */
static void static void
@ -2210,6 +2211,20 @@ test_xaccAccountType_Compatibility (void)
(1 << ACCT_TYPE_ROOT)); (1 << ACCT_TYPE_ROOT));
guint32 equity_compat = ((1 << ACCT_TYPE_EQUITY) | (1 << ACCT_TYPE_ROOT)); guint32 equity_compat = ((1 << ACCT_TYPE_EQUITY) | (1 << ACCT_TYPE_ROOT));
guint32 trading_compat = ((1 << ACCT_TYPE_TRADING) | (1 << ACCT_TYPE_ROOT)); guint32 trading_compat = ((1 << ACCT_TYPE_TRADING) | (1 << ACCT_TYPE_ROOT));
guint32 currency_compat = ((1 << ACCT_TYPE_BANK) |
(1 << ACCT_TYPE_CASH) |
(1 << ACCT_TYPE_ASSET) |
(1 << ACCT_TYPE_CREDIT) |
(1 << ACCT_TYPE_LIABILITY) |
(1 << ACCT_TYPE_INCOME) |
(1 << ACCT_TYPE_EXPENSE) |
(1 << ACCT_TYPE_EQUITY));
guint32 stock_compat = ((1 << ACCT_TYPE_STOCK) |
(1 << ACCT_TYPE_MUTUAL) |
(1 << ACCT_TYPE_CURRENCY));
guint32 immutable_ar_compat = (1 << ACCT_TYPE_RECEIVABLE);
guint32 immutable_ap_compat = (1 << ACCT_TYPE_PAYABLE);
guint32 immutable_trading_compat = (1 << ACCT_TYPE_TRADING);
guint32 compat; guint32 compat;
GNCAccountType type; GNCAccountType type;
gchar *msg1 = g_strdup_printf ("[xaccParentAccountTypesCompatibleWith()] bad account type: %d", ACCT_TYPE_ROOT); gchar *msg1 = g_strdup_printf ("[xaccParentAccountTypesCompatibleWith()] bad account type: %d", ACCT_TYPE_ROOT);
@ -2252,6 +2267,19 @@ test_xaccAccountType_Compatibility (void)
g_assert (xaccAccountTypesCompatible (type, child)); g_assert (xaccAccountTypesCompatible (type, child));
else else
g_assert (!xaccAccountTypesCompatible (type, child)); g_assert (!xaccAccountTypesCompatible (type, child));
compat = xaccAccountTypesCompatibleWith (type);
if (type <= ACCT_TYPE_LIABILITY ||
(type >= ACCT_TYPE_INCOME && type <= ACCT_TYPE_EQUITY))
g_assert_cmpint (compat, == , currency_compat);
else if (type >= ACCT_TYPE_STOCK && type <= ACCT_TYPE_CURRENCY)
g_assert_cmpint (compat, == , stock_compat);
else if (type == ACCT_TYPE_RECEIVABLE)
g_assert_cmpint (compat, == , immutable_ar_compat);
else if (type == ACCT_TYPE_PAYABLE)
g_assert_cmpint (compat, == , immutable_ap_compat);
else if (type == ACCT_TYPE_TRADING)
g_assert_cmpint (compat, == , immutable_trading_compat);
} }
loghandler = g_log_set_handler (logdomain, loglevel, loghandler = g_log_set_handler (logdomain, loglevel,

View File

@ -1120,17 +1120,18 @@ gnc_account_type_changed_cb (GtkTreeSelection *selection, gpointer data)
} }
static void static void
gnc_account_type_view_create (AccountWindow *aw) gnc_account_type_view_create (AccountWindow *aw, guint32 compat_types)
{ {
GtkTreeModel *model; GtkTreeModel *model;
GtkTreeSelection *selection; GtkTreeSelection *selection;
GtkCellRenderer *renderer; GtkCellRenderer *renderer;
GtkTreeView *view; GtkTreeView *view;
aw->valid_types &= compat_types;
if (aw->valid_types == 0) if (aw->valid_types == 0)
{ {
/* no type restrictions, choose aw->type */ /* no type restrictions, choose aw->type */
aw->valid_types = xaccAccountTypesValid () | (1 << aw->type); aw->valid_types = compat_types | (1 << aw->type);
aw->preferred_account_type = aw->type; aw->preferred_account_type = aw->type;
} }
else if ((aw->valid_types & (1 << aw->type)) != 0) else if ((aw->valid_types & (1 << aw->type)) != 0)
@ -1309,6 +1310,7 @@ gnc_account_window_create(AccountWindow *aw)
GtkBuilder *builder; GtkBuilder *builder;
GtkTreeSelection *selection; GtkTreeSelection *selection;
const gchar *tt = _("This Account contains Transactions.\nChanging this option is not possible."); const gchar *tt = _("This Account contains Transactions.\nChanging this option is not possible.");
guint32 compat_types = xaccAccountTypesValid ();
ENTER("aw %p, modal %d", aw, aw->modal); ENTER("aw %p, modal %d", aw, aw->modal);
builder = gtk_builder_new(); builder = gtk_builder_new();
@ -1413,23 +1415,23 @@ gnc_account_window_create(AccountWindow *aw)
/* This goes at the end so the select callback has good data. */ /* This goes at the end so the select callback has good data. */
aw->type_view = GTK_WIDGET(gtk_builder_get_object (builder, "type_view")); aw->type_view = GTK_WIDGET(gtk_builder_get_object (builder, "type_view"));
gnc_account_type_view_create (aw);
// If the account has transactions, prevent changes by displaying a label and tooltip // If the account has transactions, reduce the available account types
// to change the current account type to based on the following
// restrictions:
// - the new account type should not force a change of commodity
// - the old/new type is not an immutable type. Types are marked as
// immutable if gnucash depends on details that would be lost/missing
// if changing from/to such a type. At the time of this writing the
// immutable types are AR, AP and trading types.
if (xaccAccountCountSplits (aw_get_account (aw), FALSE) > 0) if (xaccAccountCountSplits (aw_get_account (aw), FALSE) > 0)
{ {
GNCAccountType atype = xaccAccountGetType (aw_get_account (aw)); GNCAccountType atype = xaccAccountGetType (aw_get_account (aw));
GtkWidget *label = gtk_label_new (xaccAccountGetTypeStr (atype)); compat_types = xaccAccountTypesCompatibleWith (atype);
GtkWidget *vbox = GTK_WIDGET(gtk_builder_get_object (builder, "type_vbox")); if (!compat_types)
GtkWidget *parent_widget = gtk_widget_get_parent (GTK_WIDGET(aw->type_view)); compat_types = xaccAccountTypesValid ();
g_object_ref (G_OBJECT(aw->type_view));
gtk_container_remove (GTK_CONTAINER(vbox), parent_widget);
gtk_widget_set_tooltip_text (label, tt);
gtk_box_pack_start (GTK_BOX(vbox), label, FALSE, FALSE, 0);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_widget_show (label);
} }
gnc_account_type_view_create (aw, compat_types);
gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(aw->dialog)); gnc_restore_window_size (GNC_PREFS_GROUP, GTK_WINDOW(aw->dialog));