mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Merge branch 'maint'
* Bug 711440 - Tab labels have different background colour than containing gui element * Bug 434462 - register color don't work correct with system theme color * Add scrub function for (business) lots to eliminate unneeded lot links * Rewrite invoice payment logic to use lot links only when absolutely needed * Bug 684719 - Man pages for gnc-fq-* perl scripts
This commit is contained in:
commit
4ecc4cb7d0
@ -154,6 +154,7 @@ void gnc_payment_acct_tree_row_activated_cb (GtkWidget *widget, GtkTreePath *pat
|
|||||||
GtkTreeViewColumn *column, PaymentWindow *pw);
|
GtkTreeViewColumn *column, PaymentWindow *pw);
|
||||||
void gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event,
|
void gnc_payment_leave_amount_cb (GtkWidget *widget, GdkEventFocus *event,
|
||||||
PaymentWindow *pw);
|
PaymentWindow *pw);
|
||||||
|
void gnc_payment_window_fill_docs_list (PaymentWindow *pw);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -161,6 +162,7 @@ gnc_payment_window_refresh_handler (GHashTable *changes, gpointer data)
|
|||||||
{
|
{
|
||||||
PaymentWindow *pw = data;
|
PaymentWindow *pw = data;
|
||||||
|
|
||||||
|
gnc_payment_window_fill_docs_list (pw);
|
||||||
pw->post_acct = gnc_account_select_combo_fill (pw->post_combo, pw->book, pw->acct_types, pw->acct_commodities);
|
pw->post_acct = gnc_account_select_combo_fill (pw->post_combo, pw->book, pw->acct_types, pw->acct_commodities);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,6 +326,8 @@ gnc_payment_dialog_highlight_document (PaymentWindow *pw)
|
|||||||
lot = (GNCLot *) g_value_get_pointer (&value);
|
lot = (GNCLot *) g_value_get_pointer (&value);
|
||||||
g_value_unset (&value);
|
g_value_unset (&value);
|
||||||
|
|
||||||
|
if (!lot)
|
||||||
|
continue; /* Lot has been deleted behind our back... */
|
||||||
|
|
||||||
invoice = gncInvoiceGetInvoiceFromLot (lot);
|
invoice = gncInvoiceGetInvoiceFromLot (lot);
|
||||||
if (!invoice)
|
if (!invoice)
|
||||||
@ -340,7 +344,7 @@ gnc_payment_dialog_highlight_document (PaymentWindow *pw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
gnc_payment_window_fill_docs_list (PaymentWindow *pw)
|
gnc_payment_window_fill_docs_list (PaymentWindow *pw)
|
||||||
{
|
{
|
||||||
GtkListStore *store;
|
GtkListStore *store;
|
||||||
@ -1102,37 +1106,13 @@ gnc_ui_payment_new (GncOwner *owner, QofBook *book)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ////////////////////////////////////////////////////////////
|
// ////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
static gboolean isAssetLiabType(GNCAccountType t)
|
|
||||||
{
|
|
||||||
switch (t)
|
|
||||||
{
|
|
||||||
case ACCT_TYPE_RECEIVABLE:
|
|
||||||
case ACCT_TYPE_PAYABLE:
|
|
||||||
return FALSE;
|
|
||||||
default:
|
|
||||||
return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
|
|
||||||
|| xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static gboolean isAPARType(GNCAccountType t)
|
|
||||||
{
|
|
||||||
switch (t)
|
|
||||||
{
|
|
||||||
case ACCT_TYPE_RECEIVABLE:
|
|
||||||
case ACCT_TYPE_PAYABLE:
|
|
||||||
return TRUE;
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void increment_if_asset_account (gpointer data,
|
static void increment_if_asset_account (gpointer data,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
int *r = user_data;
|
int *r = user_data;
|
||||||
const Split *split = data;
|
const Split *split = data;
|
||||||
const Account *account = xaccSplitGetAccount(split);
|
const Account *account = xaccSplitGetAccount(split);
|
||||||
if (isAssetLiabType(xaccAccountGetType(account)))
|
if (xaccAccountIsAssetLiabType(xaccAccountGetType(account)))
|
||||||
++(*r);
|
++(*r);
|
||||||
}
|
}
|
||||||
static int countAssetAccounts(SplitList* slist)
|
static int countAssetAccounts(SplitList* slist)
|
||||||
@ -1147,7 +1127,7 @@ static gint predicate_is_asset_account(gconstpointer a,
|
|||||||
{
|
{
|
||||||
const Split *split = a;
|
const Split *split = a;
|
||||||
const Account *account = xaccSplitGetAccount(split);
|
const Account *account = xaccSplitGetAccount(split);
|
||||||
if (isAssetLiabType(xaccAccountGetType(account)))
|
if (xaccAccountIsAssetLiabType(xaccAccountGetType(account)))
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
@ -1157,7 +1137,7 @@ static gint predicate_is_apar_account(gconstpointer a,
|
|||||||
{
|
{
|
||||||
const Split *split = a;
|
const Split *split = a;
|
||||||
const Account *account = xaccSplitGetAccount(split);
|
const Account *account = xaccSplitGetAccount(split);
|
||||||
if (isAPARType(xaccAccountGetType(account)))
|
if (xaccAccountIsAPARType(xaccAccountGetType(account)))
|
||||||
return 0;
|
return 0;
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -49,17 +49,6 @@ typedef enum
|
|||||||
GNCENTRY_NUM_REGISTER_TYPES
|
GNCENTRY_NUM_REGISTER_TYPES
|
||||||
} GncEntryLedgerType;
|
} GncEntryLedgerType;
|
||||||
|
|
||||||
typedef struct entry_ledger_colors
|
|
||||||
{
|
|
||||||
guint32 header_bg_color;
|
|
||||||
|
|
||||||
guint32 primary_bg_color;
|
|
||||||
guint32 secondary_bg_color;
|
|
||||||
|
|
||||||
guint32 primary_active_bg_color;
|
|
||||||
guint32 secondary_active_bg_color;
|
|
||||||
} GncEntryLedgerColors;
|
|
||||||
|
|
||||||
#define ENTRY_IACCT_CELL "inv-account"
|
#define ENTRY_IACCT_CELL "inv-account"
|
||||||
#define ENTRY_BACCT_CELL "bill-account"
|
#define ENTRY_BACCT_CELL "bill-account"
|
||||||
#define ENTRY_ACTN_CELL "action"
|
#define ENTRY_ACTN_CELL "action"
|
||||||
|
@ -36,15 +36,6 @@
|
|||||||
#include "gncEntryLedgerP.h"
|
#include "gncEntryLedgerP.h"
|
||||||
#include "gncEntryLedgerModel.h"
|
#include "gncEntryLedgerModel.h"
|
||||||
|
|
||||||
static GncEntryLedgerColors reg_colors =
|
|
||||||
{
|
|
||||||
0x96B183,
|
|
||||||
0xBFDEB9,
|
|
||||||
0xF6FFDA,
|
|
||||||
|
|
||||||
0xFFEF98,
|
|
||||||
0xFFEF98,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Private Interfaces ***********************************************/
|
/** Private Interfaces ***********************************************/
|
||||||
|
|
||||||
@ -926,43 +917,85 @@ static CellIOFlags get_qty_io_flags (VirtualLocation virt_loc, gpointer user_dat
|
|||||||
|
|
||||||
/* GET BG_COLORS */
|
/* GET BG_COLORS */
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_entry_ledger_get_color_internal (VirtualLocation virt_loc,
|
||||||
|
GncEntryLedger *ledger,
|
||||||
|
const guint32 *color_table,
|
||||||
|
gboolean foreground)
|
||||||
|
{
|
||||||
|
const char *cursor_name;
|
||||||
|
VirtualCell *vcell;
|
||||||
|
gboolean is_current;
|
||||||
|
guint32 colorbase = 0; /* By default return background colors */
|
||||||
|
|
||||||
|
if (foreground)
|
||||||
|
colorbase = COLOR_UNKNOWN_FG; /* a bit of enum arithmetic */
|
||||||
|
|
||||||
|
if (!ledger)
|
||||||
|
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||||
|
|
||||||
|
if (gnc_table_virtual_location_in_header (ledger->table, virt_loc))
|
||||||
|
return color_table[colorbase + COLOR_HEADER_BG];
|
||||||
|
|
||||||
|
vcell = gnc_table_get_virtual_cell (ledger->table, virt_loc.vcell_loc);
|
||||||
|
if (!vcell || !vcell->cellblock)
|
||||||
|
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||||
|
|
||||||
|
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
||||||
|
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
||||||
|
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||||
|
|
||||||
|
is_current = virt_cell_loc_equal (ledger->table->current_cursor_loc.vcell_loc,
|
||||||
|
virt_loc.vcell_loc);
|
||||||
|
|
||||||
|
if (is_current)
|
||||||
|
return vcell->start_primary_color ?
|
||||||
|
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||||
|
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||||
|
|
||||||
|
return vcell->start_primary_color ?
|
||||||
|
color_table[colorbase + COLOR_PRIMARY_BG] : color_table[colorbase + COLOR_SECONDARY_BG];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_entry_ledger_get_fg_color (VirtualLocation virt_loc,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GncEntryLedger *ledger = user_data;
|
||||||
|
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_default, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_entry_ledger_get_gtkrc_fg_color (VirtualLocation virt_loc,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GncEntryLedger *ledger = user_data;
|
||||||
|
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_gtkrc, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
static guint32
|
static guint32
|
||||||
gnc_entry_ledger_get_bg_color (VirtualLocation virt_loc,
|
gnc_entry_ledger_get_bg_color (VirtualLocation virt_loc,
|
||||||
gboolean *hatching, gpointer user_data)
|
gboolean *hatching, gpointer user_data)
|
||||||
{
|
{
|
||||||
GncEntryLedger *ledger = user_data;
|
GncEntryLedger *ledger = user_data;
|
||||||
VirtualCell *vcell;
|
|
||||||
guint32 bg_color;
|
|
||||||
gboolean is_current;
|
|
||||||
|
|
||||||
if (hatching)
|
if (hatching)
|
||||||
*hatching = FALSE;
|
*hatching = FALSE;
|
||||||
|
|
||||||
bg_color = 0xffffff; /* white */
|
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_default, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ledger) return bg_color;
|
static guint32
|
||||||
|
gnc_entry_ledger_get_gtkrc_bg_color (VirtualLocation virt_loc,
|
||||||
|
gboolean *hatching, gpointer user_data)
|
||||||
|
{
|
||||||
|
GncEntryLedger *ledger = user_data;
|
||||||
|
|
||||||
if (gnc_table_virtual_location_in_header (ledger->table, virt_loc))
|
if (hatching)
|
||||||
return reg_colors.header_bg_color;
|
*hatching = FALSE;
|
||||||
|
|
||||||
vcell = gnc_table_get_virtual_cell (ledger->table, virt_loc.vcell_loc);
|
return gnc_entry_ledger_get_color_internal (virt_loc, ledger, reg_colors_gtkrc, FALSE);
|
||||||
if (!vcell || !vcell->cellblock)
|
|
||||||
return bg_color;
|
|
||||||
|
|
||||||
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
|
||||||
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
|
||||||
return bg_color;
|
|
||||||
|
|
||||||
is_current = virt_cell_loc_equal
|
|
||||||
(ledger->table->current_cursor_loc.vcell_loc, virt_loc.vcell_loc);
|
|
||||||
|
|
||||||
if (is_current)
|
|
||||||
return vcell->start_primary_color ?
|
|
||||||
reg_colors.primary_active_bg_color :
|
|
||||||
reg_colors.secondary_active_bg_color;
|
|
||||||
|
|
||||||
return vcell->start_primary_color ?
|
|
||||||
reg_colors.primary_bg_color : reg_colors.secondary_bg_color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SAVE CELLS */
|
/* SAVE CELLS */
|
||||||
@ -1214,9 +1247,18 @@ static void gnc_entry_ledger_model_new_handlers (TableModel *model,
|
|||||||
};
|
};
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
|
gnc_table_model_set_default_fg_color_handler
|
||||||
|
(model, gnc_entry_ledger_get_fg_color);
|
||||||
|
|
||||||
|
gnc_table_model_set_fg_color_handler
|
||||||
|
(model, gnc_entry_ledger_get_gtkrc_fg_color, "gtkrc");
|
||||||
|
|
||||||
gnc_table_model_set_default_bg_color_handler
|
gnc_table_model_set_default_bg_color_handler
|
||||||
(model, gnc_entry_ledger_get_bg_color);
|
(model, gnc_entry_ledger_get_bg_color);
|
||||||
|
|
||||||
|
gnc_table_model_set_bg_color_handler
|
||||||
|
(model, gnc_entry_ledger_get_gtkrc_bg_color, "gtkrc");
|
||||||
|
|
||||||
|
|
||||||
for (i = 0; i < (sizeof(models) / sizeof(*models)); i++)
|
for (i = 0; i < (sizeof(models) / sizeof(*models)); i++)
|
||||||
{
|
{
|
||||||
|
@ -4208,6 +4208,30 @@ xaccAccountTypesValid(void)
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gboolean xaccAccountIsAssetLiabType(GNCAccountType t)
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case ACCT_TYPE_RECEIVABLE:
|
||||||
|
case ACCT_TYPE_PAYABLE:
|
||||||
|
return FALSE;
|
||||||
|
default:
|
||||||
|
return (xaccAccountTypesCompatible(ACCT_TYPE_ASSET, t)
|
||||||
|
|| xaccAccountTypesCompatible(ACCT_TYPE_LIABILITY, t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gboolean xaccAccountIsAPARType(GNCAccountType t)
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case ACCT_TYPE_RECEIVABLE:
|
||||||
|
case ACCT_TYPE_PAYABLE:
|
||||||
|
return TRUE;
|
||||||
|
default:
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
xaccAccountIsPriced(const Account *acc)
|
xaccAccountIsPriced(const Account *acc)
|
||||||
{
|
{
|
||||||
|
@ -943,6 +943,16 @@ gboolean xaccAccountTypesCompatible (GNCAccountType parent_type,
|
|||||||
* root account types are stripped. */
|
* root account types are stripped. */
|
||||||
guint32 xaccAccountTypesValid(void);
|
guint32 xaccAccountTypesValid(void);
|
||||||
|
|
||||||
|
/** Convenience function to check if the account is a valid
|
||||||
|
* Asset or Liability type, but not a business account type
|
||||||
|
* (meaning not an Accounts Payable/Accounts Receivable). */
|
||||||
|
gboolean xaccAccountIsAssetLiabType(GNCAccountType t);
|
||||||
|
|
||||||
|
/** Convenience function to check if the account is a valid
|
||||||
|
* business account type
|
||||||
|
* (meaning an Accounts Payable/Accounts Receivable). */
|
||||||
|
gboolean xaccAccountIsAPARType(GNCAccountType t);
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ libgncmod_engine_la_SOURCES = \
|
|||||||
Scrub.c \
|
Scrub.c \
|
||||||
Scrub2.c \
|
Scrub2.c \
|
||||||
Scrub3.c \
|
Scrub3.c \
|
||||||
|
ScrubBusiness.c \
|
||||||
Split.c \
|
Split.c \
|
||||||
TransLog.c \
|
TransLog.c \
|
||||||
Transaction.c \
|
Transaction.c \
|
||||||
@ -70,6 +71,7 @@ gncinclude_HEADERS = \
|
|||||||
Scrub.h \
|
Scrub.h \
|
||||||
Scrub2.h \
|
Scrub2.h \
|
||||||
Scrub3.h \
|
Scrub3.h \
|
||||||
|
ScrubBusiness.h \
|
||||||
Split.h \
|
Split.h \
|
||||||
TransLog.h \
|
TransLog.h \
|
||||||
Transaction.h \
|
Transaction.h \
|
||||||
|
@ -529,7 +529,10 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
|
|||||||
|
|
||||||
/* Return immediately if things are balanced. */
|
/* Return immediately if things are balanced. */
|
||||||
if (xaccTransIsBalanced (trans))
|
if (xaccTransIsBalanced (trans))
|
||||||
|
{
|
||||||
|
LEAVE ("transaction is balanced");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
currency = xaccTransGetCurrency (trans);
|
currency = xaccTransGetCurrency (trans);
|
||||||
|
|
||||||
@ -626,7 +629,7 @@ xaccTransScrubImbalance (Transaction *trans, Account *root,
|
|||||||
imbal_list = xaccTransGetImbalance (trans);
|
imbal_list = xaccTransGetImbalance (trans);
|
||||||
if (!imbal_list)
|
if (!imbal_list)
|
||||||
{
|
{
|
||||||
LEAVE("()");
|
LEAVE("transaction is balanced");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +344,7 @@ merge_splits (Split *sa, Split *sb)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
xaccScrubMergeSubSplits (Split *split)
|
xaccScrubMergeSubSplits (Split *split, gboolean strict)
|
||||||
{
|
{
|
||||||
gboolean rc = FALSE;
|
gboolean rc = FALSE;
|
||||||
Transaction *txn;
|
Transaction *txn;
|
||||||
@ -352,7 +352,7 @@ xaccScrubMergeSubSplits (Split *split)
|
|||||||
GNCLot *lot;
|
GNCLot *lot;
|
||||||
const GncGUID *guid;
|
const GncGUID *guid;
|
||||||
|
|
||||||
if (FALSE == is_subsplit (split)) return FALSE;
|
if (strict && (FALSE == is_subsplit (split))) return FALSE;
|
||||||
|
|
||||||
txn = split->parent;
|
txn = split->parent;
|
||||||
lot = xaccSplitGetLot (split);
|
lot = xaccSplitGetLot (split);
|
||||||
@ -392,7 +392,7 @@ restart:
|
|||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
xaccScrubMergeLotSubSplits (GNCLot *lot)
|
xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
|
||||||
{
|
{
|
||||||
gboolean rc = FALSE;
|
gboolean rc = FALSE;
|
||||||
SplitList *node;
|
SplitList *node;
|
||||||
@ -404,7 +404,7 @@ restart:
|
|||||||
for (node = gnc_lot_get_split_list(lot); node; node = node->next)
|
for (node = gnc_lot_get_split_list(lot); node; node = node->next)
|
||||||
{
|
{
|
||||||
Split *s = node->data;
|
Split *s = node->data;
|
||||||
if (!xaccScrubMergeSubSplits(s)) continue;
|
if (!xaccScrubMergeSubSplits(s, strict)) continue;
|
||||||
|
|
||||||
rc = TRUE;
|
rc = TRUE;
|
||||||
goto restart;
|
goto restart;
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
* not in a lot will be used to close the oldest open lot(s).
|
* not in a lot will be used to close the oldest open lot(s).
|
||||||
* If there are no open lots, a new lot will be started.
|
* If there are no open lots, a new lot will be started.
|
||||||
* By trying to close the oldest lots, this effectively
|
* By trying to close the oldest lots, this effectively
|
||||||
* implements a FIFO acounting policy.
|
* implements a FIFO accounting policy.
|
||||||
*/
|
*/
|
||||||
void xaccAccountAssignLots (Account *acc);
|
void xaccAccountAssignLots (Account *acc);
|
||||||
|
|
||||||
@ -89,16 +89,26 @@ void xaccLotScrubDoubleBalance (GNCLot *lot);
|
|||||||
* the same lot, or in no lot. Note that, by definition, all
|
* the same lot, or in no lot. Note that, by definition, all
|
||||||
* subsplits belong to the same transaction.
|
* subsplits belong to the same transaction.
|
||||||
*
|
*
|
||||||
|
* There are two ways to find matching subsplits. The first
|
||||||
|
* way will consider splits to be subsplits only if they
|
||||||
|
* are explicitly marked as such while splitting the original
|
||||||
|
* split. Set strict to TRUE for this matching algorhythm.
|
||||||
|
*
|
||||||
|
* The second way is more relaxed. It will consider any two
|
||||||
|
* splits that happen to be part of the same lot and the
|
||||||
|
* same transaction to be subsplits. Set strict to FALSE for
|
||||||
|
* this matching algorhythm.
|
||||||
|
*
|
||||||
* The routine returns TRUE if a merger was performed, else
|
* The routine returns TRUE if a merger was performed, else
|
||||||
* it returns FALSE.
|
* it returns FALSE.
|
||||||
*
|
|
||||||
* The xaccScrubMergeTransSubSplits() routine does the same, except
|
|
||||||
* that it does it for all of the splits in the transaction.
|
|
||||||
* The xaccScrubMergeLotSubSplits() routine does the same, except
|
|
||||||
* that it does it for all of the splits in the lot.
|
|
||||||
*/
|
*/
|
||||||
gboolean xaccScrubMergeSubSplits (Split *split);
|
gboolean xaccScrubMergeSubSplits (Split *split, gboolean strict);
|
||||||
gboolean xaccScrubMergeLotSubSplits (GNCLot *lot);
|
|
||||||
|
/** The xaccScrubMergeLotSubSplits() routine does the same as
|
||||||
|
* the xaccScrubMergSubSplits, except that it does it
|
||||||
|
* for all of the splits in the lot.
|
||||||
|
*/
|
||||||
|
gboolean xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict);
|
||||||
|
|
||||||
#endif /* XACC_SCRUB2_H */
|
#endif /* XACC_SCRUB2_H */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -96,7 +96,7 @@ xaccScrubLot (GNCLot *lot)
|
|||||||
acc = gnc_lot_get_account (lot);
|
acc = gnc_lot_get_account (lot);
|
||||||
pcy = gnc_account_get_policy(acc);
|
pcy = gnc_account_get_policy(acc);
|
||||||
xaccAccountBeginEdit(acc);
|
xaccAccountBeginEdit(acc);
|
||||||
xaccScrubMergeLotSubSplits (lot);
|
xaccScrubMergeLotSubSplits (lot, TRUE);
|
||||||
|
|
||||||
/* If the lot balance is zero, we don't need to rebalance */
|
/* If the lot balance is zero, we don't need to rebalance */
|
||||||
lot_baln = gnc_lot_get_balance (lot);
|
lot_baln = gnc_lot_get_balance (lot);
|
||||||
@ -132,7 +132,7 @@ rethin:
|
|||||||
xaccLotFill (lot);
|
xaccLotFill (lot);
|
||||||
|
|
||||||
/* Make sure there are no subsplits. */
|
/* Make sure there are no subsplits. */
|
||||||
splits_deleted = xaccScrubMergeLotSubSplits (lot);
|
splits_deleted = xaccScrubMergeLotSubSplits (lot, TRUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now re-compute cap gains, and then double-check that.
|
/* Now re-compute cap gains, and then double-check that.
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
@{ */
|
@{ */
|
||||||
|
|
||||||
/** @file Scrub3.h
|
/** @file Scrub3.h
|
||||||
* @brief Hiogh-Level API for imposing Lot constraints
|
* @brief High-Level API for imposing Lot constraints
|
||||||
* @author Created by Linas Vepstas Sept 2003
|
* @author Created by Linas Vepstas Sept 2003
|
||||||
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
* @author Copyright (c) 2003 Linas Vepstas <linas@linas.org>
|
||||||
*/
|
*/
|
||||||
@ -44,7 +44,7 @@
|
|||||||
* self-consistent and properly balanced, and fixes it if its not.
|
* self-consistent and properly balanced, and fixes it if its not.
|
||||||
* This is an important routine to call if the amount of any split
|
* This is an important routine to call if the amount of any split
|
||||||
* in the lot is changed. That's because (obviously) changing
|
* in the lot is changed. That's because (obviously) changing
|
||||||
* split values is gaurenteed to throw off lot balances.
|
* split values is guaranteed to throw off lot balances.
|
||||||
* This routine may end up closing the lot, or at least trying
|
* This routine may end up closing the lot, or at least trying
|
||||||
* to. It will also cause cap gains to be recomputed.
|
* to. It will also cause cap gains to be recomputed.
|
||||||
*
|
*
|
||||||
|
333
src/engine/ScrubBusiness.c
Normal file
333
src/engine/ScrubBusiness.c
Normal file
@ -0,0 +1,333 @@
|
|||||||
|
/********************************************************************\
|
||||||
|
* ScrubBusiness.h -- Cleanup functions for the business objects. *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License as *
|
||||||
|
* published by the Free Software Foundation; either version 2 of *
|
||||||
|
* the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License*
|
||||||
|
* along with this program; if not, contact: *
|
||||||
|
* *
|
||||||
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||||
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||||
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
/** @file ScrubBusiness.h
|
||||||
|
* @brief Cleanup functions for business objects
|
||||||
|
* @author Created by Geert Janssens August 2014
|
||||||
|
* @author Copyright (c) 2014 Geert Janssens <geert@kobaltwit.be>
|
||||||
|
*
|
||||||
|
* Provides the high-level API for checking and repairing ('scrubbing
|
||||||
|
* clean') the various data objects used by the business functions.*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
|
#include "gnc-engine.h"
|
||||||
|
#include "gnc-lot.h"
|
||||||
|
#include "policy-p.h"
|
||||||
|
#include "Account.h"
|
||||||
|
#include "gncInvoice.h"
|
||||||
|
#include "Scrub2.h"
|
||||||
|
#include "ScrubBusiness.h"
|
||||||
|
#include "Transaction.h"
|
||||||
|
|
||||||
|
static QofLogModule log_module = GNC_MOD_LOT;
|
||||||
|
|
||||||
|
// A helper function that takes two splits. If the splits are of opposite sign
|
||||||
|
// it reduces the biggest split to have the same value (but with opposite sign)
|
||||||
|
// of the smaller split.
|
||||||
|
// To make sure everything still continues to balance in addition a "remainder" split
|
||||||
|
// will be created that will be added to the same lot and transaction as the biggest
|
||||||
|
// split.
|
||||||
|
// The opposite sign restriction is because that's the only scenario that makes sense
|
||||||
|
// in the context of scrubbing business lots below.
|
||||||
|
// If we created new splits, return TRUE, otherwise FALSE
|
||||||
|
static gboolean reduce_biggest_split (Split *splitA, Split *splitB)
|
||||||
|
{
|
||||||
|
gnc_numeric valA = xaccSplitGetValue (splitA);
|
||||||
|
gnc_numeric valB = xaccSplitGetValue (splitB);
|
||||||
|
|
||||||
|
if (gnc_numeric_compare (gnc_numeric_abs (valA), gnc_numeric_abs (valB)) >= 0)
|
||||||
|
return gncOwnerReduceSplitTo (splitA, gnc_numeric_neg (valB));
|
||||||
|
else
|
||||||
|
return gncOwnerReduceSplitTo (splitB, gnc_numeric_neg (valA));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to eliminate or reduce the lot link splits (ll_*_split)
|
||||||
|
// between from_lot and to_lot. To do so this function will attempt
|
||||||
|
// to move a payment split from from_lot to to_lot in order to
|
||||||
|
// balance the lot link split that will be deleted.
|
||||||
|
// To ensure everything remains balanced at most
|
||||||
|
// min (val-ll-*-split, val-pay-split) (in absolute values) can be moved.
|
||||||
|
// If any split involved has a larger value, it will be split in two
|
||||||
|
// and only the part matching the other splits' value will be used.
|
||||||
|
// The leftover splits are kept in the respective transactions/lots.
|
||||||
|
// A future scrub action can still act on those if needed.
|
||||||
|
//
|
||||||
|
// Note that this function assumes that ll_from_split and ll_to_split are
|
||||||
|
// of opposite sign. The calling function should check this.
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
scrub_other_link (GNCLot *from_lot, Split *ll_from_split,
|
||||||
|
GNCLot *to_lot, Split *ll_to_split)
|
||||||
|
{
|
||||||
|
Split *real_from_split; // This refers to the split in the payment lot representing the payment itself
|
||||||
|
gnc_numeric from_val, real_from_val, to_val;
|
||||||
|
gboolean modified = FALSE;
|
||||||
|
Transaction *ll_txn = xaccSplitGetParent (ll_to_split);
|
||||||
|
|
||||||
|
// Per iteration we can only scrub at most max (val-doc-split, val-pay-split)
|
||||||
|
// So split the bigger one in two if needed and continue with the equal valued splits only
|
||||||
|
// The remainder is added to the lot link transaction and the lot to keep everything balanced
|
||||||
|
// and will be processed in a future iteration
|
||||||
|
modified = reduce_biggest_split (ll_from_split, ll_to_split);
|
||||||
|
|
||||||
|
// Next we have to find the original payment split so we can
|
||||||
|
// add (part of) it to the document lot
|
||||||
|
real_from_split = gncOwnerFindOffsettingSplit (from_lot, xaccSplitGetValue (ll_from_split));
|
||||||
|
if (!real_from_split)
|
||||||
|
return modified; // No usable split in the payment lot
|
||||||
|
|
||||||
|
// Here again per iteration we can only scrub at most max (val-other-pay-split, val-pay-split)
|
||||||
|
// So split the bigger one in two if needed and continue with the equal valued splits only
|
||||||
|
// The remainder is added to the lot link transaction and the lot to keep everything balanced
|
||||||
|
// and will be processed in a future iteration
|
||||||
|
modified = reduce_biggest_split (real_from_split, ll_from_split);
|
||||||
|
|
||||||
|
// Once more check for max (val-doc-split, val-pay-split), and reduce if necessary.
|
||||||
|
// It may have changed while looking for the real payment split
|
||||||
|
modified = reduce_biggest_split (ll_from_split, ll_to_split);
|
||||||
|
|
||||||
|
// At this point ll_to_split and real_from_split should have the same value
|
||||||
|
// If not, flag a warning and skip to the next iteration
|
||||||
|
to_val = xaccSplitGetValue (ll_to_split);
|
||||||
|
from_val = xaccSplitGetValue (ll_from_split);
|
||||||
|
real_from_val = xaccSplitGetValue (real_from_split);
|
||||||
|
if (!gnc_numeric_equal (real_from_val, to_val))
|
||||||
|
{
|
||||||
|
// This is unexpected - write a warning message and skip this split
|
||||||
|
PWARN("real_from_val and to_val differ. "
|
||||||
|
"This is unexpected! Skip scrubbing of real_from_split %p against ll_to_split %p.", real_from_split, ll_to_split);
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do the actual split dance
|
||||||
|
// - move real payment split to doc lot
|
||||||
|
// - delete both lot link splits from the lot link transaction
|
||||||
|
gnc_lot_add_split (to_lot, real_from_split);
|
||||||
|
xaccTransBeginEdit (ll_txn);
|
||||||
|
xaccSplitDestroy (ll_to_split);
|
||||||
|
xaccSplitDestroy (ll_from_split);
|
||||||
|
xaccTransCommitEdit (ll_txn);
|
||||||
|
|
||||||
|
// Cleanup the lots
|
||||||
|
xaccScrubMergeLotSubSplits (to_lot, FALSE);
|
||||||
|
xaccScrubMergeLotSubSplits (from_lot, FALSE);
|
||||||
|
|
||||||
|
return TRUE; // We did change splits/transactions/lots...
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gncScrubLotLinks (GNCLot *scrub_lot)
|
||||||
|
{
|
||||||
|
gboolean modified = FALSE, restart_needed = FALSE;
|
||||||
|
SplitList *sls_iter = NULL;
|
||||||
|
|
||||||
|
scrub_start:
|
||||||
|
restart_needed = FALSE;
|
||||||
|
|
||||||
|
// Iterate over all splits in the lot
|
||||||
|
for (sls_iter = gnc_lot_get_split_list (scrub_lot); sls_iter; sls_iter = sls_iter->next)
|
||||||
|
{
|
||||||
|
Split *sl_split = sls_iter->data;
|
||||||
|
Transaction *ll_txn = NULL; // ll_txn = "Lot Link Transaction"
|
||||||
|
SplitList *lts_iter = NULL;
|
||||||
|
|
||||||
|
if (!sl_split)
|
||||||
|
continue; // next scrub lot split
|
||||||
|
|
||||||
|
// Only lot link transactions need to be scrubbed
|
||||||
|
ll_txn = xaccSplitGetParent (sl_split);
|
||||||
|
|
||||||
|
if (!ll_txn)
|
||||||
|
{
|
||||||
|
// Ooops - the split doesn't belong to any transaction !
|
||||||
|
// This is not expected so issue a warning and continue with next split
|
||||||
|
PWARN("Encountered a split in a business lot that's not part of any transaction. "
|
||||||
|
"This is unexpected! Skipping split %p.", sl_split);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
|
||||||
|
continue; // next scrub lot split
|
||||||
|
|
||||||
|
// Iterate over all splits in the lot link transaction
|
||||||
|
for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
|
||||||
|
{
|
||||||
|
Split *ll_txn_split = lts_iter->data; // These all refer to splits in the lot link transaction
|
||||||
|
GNCLot *remote_lot = NULL; // lot at the other end of the lot link transaction
|
||||||
|
gboolean sl_is_doc_lot, rl_is_doc_lot;
|
||||||
|
|
||||||
|
if (!ll_txn_split)
|
||||||
|
continue; // next lot link transaction split
|
||||||
|
|
||||||
|
// Skip the split in the lot we're currently scrubbing
|
||||||
|
if (sl_split == ll_txn_split)
|
||||||
|
continue; // next lot link transaction split
|
||||||
|
|
||||||
|
// Only splits of opposite sign can be scrubbed
|
||||||
|
if (gnc_numeric_positive_p (xaccSplitGetValue (sl_split)) ==
|
||||||
|
gnc_numeric_positive_p (xaccSplitGetValue (ll_txn_split)))
|
||||||
|
continue; // next lot link transaction split
|
||||||
|
|
||||||
|
// Find linked lot via split
|
||||||
|
remote_lot = xaccSplitGetLot (ll_txn_split);
|
||||||
|
if (!remote_lot)
|
||||||
|
{
|
||||||
|
// This is unexpected - write a warning message and skip this split
|
||||||
|
PWARN("Encountered a Lot Link transaction with a split that's not in any lot. "
|
||||||
|
"This is unexpected! Skipping split %p from transaction %p.", ll_txn_split, ll_txn);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (scrub_lot) != NULL);
|
||||||
|
rl_is_doc_lot = (gncInvoiceGetInvoiceFromLot (remote_lot) != NULL);
|
||||||
|
|
||||||
|
// Depending on the type of lots we're comparing, we need different actions
|
||||||
|
// - Two document lots (an invoice and a credit note):
|
||||||
|
// Special treatment - look for all document lots linked via ll_txn
|
||||||
|
// and update the memo to be of more use to the uses.
|
||||||
|
// - Two payment lots:
|
||||||
|
// (Part of) the link will be eliminated and instead (part of)
|
||||||
|
// one payment will be added to the other lot to keep the balance.
|
||||||
|
// If the payments are not equal in abs value part of the bigger payment
|
||||||
|
// will be moved to the smaller payment's lot.
|
||||||
|
// - A document and a payment lot:
|
||||||
|
// (Part of) the link will be eliminated and instead (part of) the real
|
||||||
|
// payment will be added to the document lot to handle the payment.
|
||||||
|
if (sl_is_doc_lot && rl_is_doc_lot)
|
||||||
|
gncOwnerSetLotLinkMemo (ll_txn);
|
||||||
|
else if (!sl_is_doc_lot && !rl_is_doc_lot)
|
||||||
|
{
|
||||||
|
gint cmp = gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (sl_split)),
|
||||||
|
gnc_numeric_abs (xaccSplitGetValue (ll_txn_split)));
|
||||||
|
if (cmp >= 0)
|
||||||
|
restart_needed = scrub_other_link (scrub_lot, sl_split, remote_lot, ll_txn_split);
|
||||||
|
else
|
||||||
|
restart_needed = scrub_other_link (remote_lot, ll_txn_split, scrub_lot, sl_split);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GNCLot *doc_lot = sl_is_doc_lot ? scrub_lot : remote_lot;
|
||||||
|
GNCLot *pay_lot = sl_is_doc_lot ? remote_lot : scrub_lot;
|
||||||
|
Split *ll_doc_split = sl_is_doc_lot ? sl_split : ll_txn_split;
|
||||||
|
Split *ll_pay_split = sl_is_doc_lot ? ll_txn_split : sl_split;
|
||||||
|
// Ok, let's try to move a payment from pay_lot to doc_lot
|
||||||
|
restart_needed = scrub_other_link (pay_lot, ll_pay_split, doc_lot, ll_doc_split);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we got here, the splits in our lot and ll_txn have been severely mixed up
|
||||||
|
// And our iterator lists are probably no longer valid
|
||||||
|
// So let's start over
|
||||||
|
if (restart_needed)
|
||||||
|
{
|
||||||
|
modified = TRUE;
|
||||||
|
goto scrub_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gncScrubBusinessLot (GNCLot *lot)
|
||||||
|
{
|
||||||
|
gboolean splits_deleted = FALSE;
|
||||||
|
Account *acc;
|
||||||
|
gchar *lotname=NULL;
|
||||||
|
|
||||||
|
if (!lot) return FALSE;
|
||||||
|
lotname = g_strdup (gnc_lot_get_title (lot));
|
||||||
|
ENTER ("(lot=%p) %s", lot, lotname ? lotname : "(no lotname)");
|
||||||
|
|
||||||
|
acc = gnc_lot_get_account (lot);
|
||||||
|
if (acc)
|
||||||
|
xaccAccountBeginEdit(acc);
|
||||||
|
|
||||||
|
// Scrub lot links.
|
||||||
|
// They should only remain when two document lots are linked together
|
||||||
|
xaccScrubMergeLotSubSplits (lot, FALSE);
|
||||||
|
splits_deleted = gncScrubLotLinks (lot);
|
||||||
|
|
||||||
|
// If lot is empty now, delete it
|
||||||
|
if (0 == gnc_lot_count_splits (lot))
|
||||||
|
{
|
||||||
|
PINFO("All splits were removed from lot, deleting");
|
||||||
|
gnc_lot_destroy (lot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (acc)
|
||||||
|
xaccAccountCommitEdit(acc);
|
||||||
|
|
||||||
|
LEAVE ("(lot=%s, deleted=%d)", lotname ? lotname : "(no lotname)", splits_deleted);
|
||||||
|
g_free (lotname);
|
||||||
|
|
||||||
|
return splits_deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
gncScrubBusinessAccountLots (Account *acc)
|
||||||
|
{
|
||||||
|
LotList *lots, *node;
|
||||||
|
if (!acc) return;
|
||||||
|
if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
|
||||||
|
|
||||||
|
ENTER ("(acc=%s)", xaccAccountGetName(acc));
|
||||||
|
xaccAccountBeginEdit(acc);
|
||||||
|
|
||||||
|
lots = xaccAccountGetLotList(acc);
|
||||||
|
for (node = lots; node; node = node->next)
|
||||||
|
{
|
||||||
|
GNCLot *lot = node->data;
|
||||||
|
if (lot)
|
||||||
|
gncScrubBusinessLot (lot);
|
||||||
|
}
|
||||||
|
g_list_free(lots);
|
||||||
|
xaccAccountCommitEdit(acc);
|
||||||
|
LEAVE ("(acc=%s)", xaccAccountGetName(acc));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ============================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
lot_scrub_cb (Account *acc, gpointer data)
|
||||||
|
{
|
||||||
|
if (FALSE == xaccAccountIsAPARType (xaccAccountGetType (acc))) return;
|
||||||
|
gncScrubBusinessAccountLots (acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gncScrubBusinessAccountTreeLots (Account *acc)
|
||||||
|
{
|
||||||
|
if (!acc) return;
|
||||||
|
|
||||||
|
gnc_account_foreach_descendant(acc, lot_scrub_cb, NULL);
|
||||||
|
gncScrubBusinessAccountLots (acc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========================== END OF FILE ========================= */
|
79
src/engine/ScrubBusiness.h
Normal file
79
src/engine/ScrubBusiness.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/********************************************************************\
|
||||||
|
* ScrubBusiness.h -- Cleanup functions for the business objects. *
|
||||||
|
* *
|
||||||
|
* This program is free software; you can redistribute it and/or *
|
||||||
|
* modify it under the terms of the GNU General Public License as *
|
||||||
|
* published by the Free Software Foundation; either version 2 of *
|
||||||
|
* the License, or (at your option) any later version. *
|
||||||
|
* *
|
||||||
|
* This program is distributed in the hope that it will be useful, *
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
|
* GNU General Public License for more details. *
|
||||||
|
* *
|
||||||
|
* You should have received a copy of the GNU General Public License*
|
||||||
|
* along with this program; if not, contact: *
|
||||||
|
* *
|
||||||
|
* Free Software Foundation Voice: +1-617-542-5942 *
|
||||||
|
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
|
||||||
|
* Boston, MA 02110-1301, USA gnu@gnu.org *
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
/** @addtogroup Engine
|
||||||
|
@{ */
|
||||||
|
/** @addtogroup Scrub
|
||||||
|
@{ */
|
||||||
|
|
||||||
|
/** @file ScrubBusiness.h
|
||||||
|
* @brief Cleanup functions for business objects
|
||||||
|
* @author Created by Geert Janssens August 2014
|
||||||
|
* @author Copyright (c) 2014 Geert Janssens <geert@kobaltwit.be>
|
||||||
|
*/
|
||||||
|
#ifndef GNC_SCRUBBUSINESS_H
|
||||||
|
#define GNC_SCRUBBUSINESS_H
|
||||||
|
|
||||||
|
#include "gnc-engine.h"
|
||||||
|
|
||||||
|
/** @name Cleanup functions for business objects
|
||||||
|
* Provides the high-level API for checking and repairing ('scrubbing
|
||||||
|
* clean') the various data objects used by the business functions.
|
||||||
|
@{ */
|
||||||
|
|
||||||
|
/** The gncScrubBusinessLot() function makes sure that the indicated
|
||||||
|
* lot has all the correct properties required for a lot used in
|
||||||
|
* the business features.
|
||||||
|
*
|
||||||
|
* Currently this function only does one thing: eliminate lot link
|
||||||
|
* transactions between invoice lots and payment lots (which were
|
||||||
|
* generated by GnuCash versions 2.6.0-2.6.3). Lot links
|
||||||
|
* between invoices and credit notes will still remain.
|
||||||
|
*
|
||||||
|
* Scrubbing the lot may cause subsplits to be merged together,
|
||||||
|
* i.e. for splits to be deleted. This routine returns true if
|
||||||
|
* any splits were modified or deleted.
|
||||||
|
*/
|
||||||
|
gboolean gncScrubBusinessLot (GNCLot *lot);
|
||||||
|
|
||||||
|
/** The gncScrubBusinessAccountLots() function will call
|
||||||
|
* gncScrubBusinessLot() on each lot in the given account.
|
||||||
|
*
|
||||||
|
* This routine is the primary routine for ensuring that the
|
||||||
|
* lot structure of every lot of a business account is in good
|
||||||
|
* order.
|
||||||
|
*/
|
||||||
|
void gncScrubBusinessAccountLots (Account *acc);
|
||||||
|
|
||||||
|
/** The gncScrubBusinessAccountTreeLots() function will call
|
||||||
|
* gncScrubBusinessAccountLots() on each lot in the given account
|
||||||
|
* and its sub accounts.
|
||||||
|
*
|
||||||
|
* This routine is the primary routine for ensuring that the
|
||||||
|
* lot structure of every lot of a business account is in good
|
||||||
|
* order.
|
||||||
|
*/
|
||||||
|
void gncScrubBusinessAccountTreeLots (Account *acc);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
#endif /* GNC_SCRUBBUSINESS_H */
|
||||||
|
/** @} */
|
||||||
|
/** @} */
|
@ -43,7 +43,6 @@
|
|||||||
#include "Split.h"
|
#include "Split.h"
|
||||||
#include "AccountP.h"
|
#include "AccountP.h"
|
||||||
#include "Scrub.h"
|
#include "Scrub.h"
|
||||||
#include "Scrub3.h"
|
|
||||||
#include "TransactionP.h"
|
#include "TransactionP.h"
|
||||||
#include "TransLog.h"
|
#include "TransLog.h"
|
||||||
#include "cap-gains.h"
|
#include "cap-gains.h"
|
||||||
|
@ -213,7 +213,7 @@ void xaccTransRollbackEdit (Transaction *trans);
|
|||||||
|
|
||||||
/** The xaccTransIsOpen() method returns TRUE if the transaction
|
/** The xaccTransIsOpen() method returns TRUE if the transaction
|
||||||
is open for editing. Otherwise, it returns false.
|
is open for editing. Otherwise, it returns false.
|
||||||
XXX this routne should probably be deprecated. its, umm,
|
XXX this routine should probably be deprecated. its, umm,
|
||||||
hard to imagine legitimate uses (but it is used by
|
hard to imagine legitimate uses (but it is used by
|
||||||
the import/export code for reasons I can't understand.)
|
the import/export code for reasons I can't understand.)
|
||||||
*/
|
*/
|
||||||
|
@ -309,6 +309,8 @@ gnc_lot_free(GNCLot* lot)
|
|||||||
priv->is_closed = TRUE;
|
priv->is_closed = TRUE;
|
||||||
/* qof_instance_release (&lot->inst); */
|
/* qof_instance_release (&lot->inst); */
|
||||||
g_object_unref (lot);
|
g_object_unref (lot);
|
||||||
|
|
||||||
|
LEAVE();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -635,6 +637,7 @@ gnc_lot_remove_split (GNCLot *lot, Split *split)
|
|||||||
}
|
}
|
||||||
gnc_lot_commit_edit(lot);
|
gnc_lot_commit_edit(lot);
|
||||||
qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, NULL);
|
qof_event_gen (QOF_INSTANCE(lot), QOF_EVENT_MODIFY, NULL);
|
||||||
|
LEAVE("removed from lot");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
|
@ -44,14 +44,17 @@
|
|||||||
#include "gncVendorP.h"
|
#include "gncVendorP.h"
|
||||||
#include "gncInvoice.h"
|
#include "gncInvoice.h"
|
||||||
#include "gnc-commodity.h"
|
#include "gnc-commodity.h"
|
||||||
#include "Transaction.h"
|
#include "Scrub2.h"
|
||||||
#include "Split.h"
|
#include "Split.h"
|
||||||
|
#include "Transaction.h"
|
||||||
#include "engine-helpers.h"
|
#include "engine-helpers.h"
|
||||||
|
|
||||||
#define _GNC_MOD_NAME GNC_ID_OWNER
|
#define _GNC_MOD_NAME GNC_ID_OWNER
|
||||||
|
|
||||||
#define GNC_OWNER_ID "gncOwner"
|
#define GNC_OWNER_ID "gncOwner"
|
||||||
|
|
||||||
|
static QofLogModule log_module = GNC_MOD_ENGINE;
|
||||||
|
|
||||||
GncOwner * gncOwnerNew (void)
|
GncOwner * gncOwnerNew (void)
|
||||||
{
|
{
|
||||||
GncOwner *o;
|
GncOwner *o;
|
||||||
@ -834,9 +837,368 @@ gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
|
|||||||
return payment_lot;
|
return payment_lot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
is_equal = 8,
|
||||||
|
is_more = 4,
|
||||||
|
is_less = 2,
|
||||||
|
is_pay_split = 1
|
||||||
|
} split_flags;
|
||||||
|
|
||||||
|
Split *gncOwnerFindOffsettingSplit (GNCLot *lot, gnc_numeric target_value)
|
||||||
|
{
|
||||||
|
SplitList *ls_iter = NULL;
|
||||||
|
Split *best_split = NULL;
|
||||||
|
gnc_numeric best_val = { 0, 1};
|
||||||
|
gint best_flags = 0;
|
||||||
|
|
||||||
|
if (!lot)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
|
||||||
|
{
|
||||||
|
Split *split = ls_iter->data;
|
||||||
|
Transaction *txn;
|
||||||
|
gnc_numeric split_value;
|
||||||
|
gint new_flags = 0;
|
||||||
|
gint val_cmp = 0;
|
||||||
|
|
||||||
|
if (!split)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
txn = xaccSplitGetParent (split);
|
||||||
|
if (!txn)
|
||||||
|
{
|
||||||
|
// Ooops - the split doesn't belong to any transaction !
|
||||||
|
// This is not expected so issue a warning and continue with next split
|
||||||
|
PWARN("Encountered a split in a payment lot that's not part of any transaction. "
|
||||||
|
"This is unexpected! Skipping split %p.", split);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this split has the opposite sign of the target value we want to offset
|
||||||
|
split_value = xaccSplitGetValue (split);
|
||||||
|
if (gnc_numeric_positive_p (target_value) == gnc_numeric_positive_p (split_value))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Ok we have found a split that potentially can offset the target value
|
||||||
|
// Let's see if it's better than what we have found already.
|
||||||
|
val_cmp = gnc_numeric_compare (gnc_numeric_abs (split_value),
|
||||||
|
gnc_numeric_abs (target_value));
|
||||||
|
if (val_cmp == 0)
|
||||||
|
new_flags += is_equal;
|
||||||
|
else if (val_cmp > 0)
|
||||||
|
new_flags += is_more;
|
||||||
|
else
|
||||||
|
new_flags += is_less;
|
||||||
|
|
||||||
|
if (xaccTransGetTxnType (txn) != TXN_TYPE_LINK)
|
||||||
|
new_flags += is_pay_split;
|
||||||
|
|
||||||
|
if ((new_flags >= best_flags) &&
|
||||||
|
(gnc_numeric_compare (gnc_numeric_abs (split_value),
|
||||||
|
gnc_numeric_abs (best_val)) > 0))
|
||||||
|
{
|
||||||
|
// The new split is a better match than what we found so far
|
||||||
|
best_split = split;
|
||||||
|
best_flags = new_flags;
|
||||||
|
best_val = split_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_split;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value)
|
||||||
|
{
|
||||||
|
gnc_numeric split_val = xaccSplitGetValue (split);
|
||||||
|
gnc_numeric rem_val;
|
||||||
|
Split *rem_split;
|
||||||
|
Transaction *txn;
|
||||||
|
GNCLot *lot;
|
||||||
|
|
||||||
|
if (gnc_numeric_positive_p (split_val) != gnc_numeric_positive_p (target_value))
|
||||||
|
return FALSE; // Split and target value have to be of the same sign
|
||||||
|
|
||||||
|
if (gnc_numeric_equal (split_val, target_value))
|
||||||
|
return FALSE; // Split already has the target value
|
||||||
|
|
||||||
|
rem_val = gnc_numeric_sub (split_val, target_value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); // note: values are of opposite sign
|
||||||
|
rem_split = xaccMallocSplit (xaccSplitGetBook (split));
|
||||||
|
xaccSplitCopyOnto (split, rem_split);
|
||||||
|
xaccSplitSetValue (rem_split, rem_val);
|
||||||
|
|
||||||
|
txn = xaccSplitGetParent (split);
|
||||||
|
xaccTransBeginEdit (txn);
|
||||||
|
xaccSplitSetValue (split, target_value);
|
||||||
|
xaccSplitSetParent (rem_split, txn);
|
||||||
|
xaccTransCommitEdit (txn);
|
||||||
|
|
||||||
|
lot = xaccSplitGetLot (split);
|
||||||
|
gnc_lot_add_split (lot, rem_split);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
gncOwnerSetLotLinkMemo (Transaction *ll_txn)
|
||||||
|
{
|
||||||
|
gchar *memo_prefix = _("Offset between documents: ");
|
||||||
|
gchar *new_memo;
|
||||||
|
SplitList *lts_iter;
|
||||||
|
SplitList *splits = NULL, *siter;
|
||||||
|
GList *titles = NULL, *titer;
|
||||||
|
|
||||||
|
if (!ll_txn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Find all splits in the lot link transaction that are also in a document lot
|
||||||
|
for (lts_iter = xaccTransGetSplitList (ll_txn); lts_iter; lts_iter = lts_iter->next)
|
||||||
|
{
|
||||||
|
Split *split = lts_iter->data;
|
||||||
|
GNCLot *lot;
|
||||||
|
GncInvoice *invoice;
|
||||||
|
gchar *title;
|
||||||
|
|
||||||
|
if (!split)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lot = xaccSplitGetLot (split);
|
||||||
|
if (!lot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
invoice = gncInvoiceGetInvoiceFromLot (lot);
|
||||||
|
if (!invoice)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
title = g_strdup_printf ("%s %s", gncInvoiceGetTypeString (invoice), gncInvoiceGetID (invoice));
|
||||||
|
|
||||||
|
titles = g_list_insert_sorted (titles, title, (GCompareFunc)g_strcmp0);
|
||||||
|
splits = g_list_prepend (splits, split); // splits don't need to be sorted
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!titles)
|
||||||
|
return; // We didn't find document lots
|
||||||
|
|
||||||
|
// Create the memo as we'd want it to be
|
||||||
|
new_memo = g_strconcat (memo_prefix, titles->data, NULL);
|
||||||
|
for (titer = titles->next; titer; titer = titer->next)
|
||||||
|
{
|
||||||
|
gchar *tmp_memo = g_strconcat (new_memo, " - ", titer->data, NULL);
|
||||||
|
g_free (new_memo);
|
||||||
|
new_memo = tmp_memo;
|
||||||
|
}
|
||||||
|
g_list_free_full (titles, g_free);
|
||||||
|
|
||||||
|
// Update the memos of all the splits we found previously (if needed)
|
||||||
|
for (siter = splits; siter; siter = siter->next)
|
||||||
|
{
|
||||||
|
if (g_strcmp0 (xaccSplitGetMemo (siter->data), new_memo) != 0)
|
||||||
|
xaccSplitSetMemo (siter->data, new_memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_list_free (splits);
|
||||||
|
g_free (new_memo);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find an existing lot link transaction in the given lot
|
||||||
|
* Only use a lot link that already links at least two
|
||||||
|
* documents (to avoid perpetuating the lot link proliferation
|
||||||
|
* that happened in 2.6.0-2.6.3).
|
||||||
|
*/
|
||||||
|
static Transaction *
|
||||||
|
get_ll_transaction_from_lot (GNCLot *lot)
|
||||||
|
{
|
||||||
|
SplitList *ls_iter;
|
||||||
|
|
||||||
|
/* This should really only be called on a document lot */
|
||||||
|
if (!gncInvoiceGetInvoiceFromLot (lot))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* The given lot is a valid document lot. Now iterate over all
|
||||||
|
* other lot links in this lot to find one more document lot.
|
||||||
|
*/
|
||||||
|
for (ls_iter = gnc_lot_get_split_list (lot); ls_iter; ls_iter = ls_iter->next)
|
||||||
|
{
|
||||||
|
Split *ls = ls_iter->data;
|
||||||
|
Transaction *ll_txn = xaccSplitGetParent (ls);
|
||||||
|
SplitList *ts_iter;
|
||||||
|
|
||||||
|
if (xaccTransGetTxnType (ll_txn) != TXN_TYPE_LINK)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (ts_iter = xaccTransGetSplitList (ll_txn); ts_iter; ts_iter = ts_iter->next)
|
||||||
|
{
|
||||||
|
Split *ts = ts_iter->data;
|
||||||
|
GNCLot *tslot = xaccSplitGetLot (ts);
|
||||||
|
|
||||||
|
if (!tslot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tslot == lot)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (gncInvoiceGetInvoiceFromLot (lot))
|
||||||
|
return ll_txn; /* Got one more document lot - mission accomplished */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The lot doesn't have an ll_txn with the requested criteria... */
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gncOwnerCreateLotLink (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
|
||||||
|
{
|
||||||
|
const gchar *action = _("Lot Link");
|
||||||
|
Account *acct = gnc_lot_get_account (from_lot);
|
||||||
|
const gchar *name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
|
||||||
|
Transaction *ll_txn = NULL;
|
||||||
|
gnc_numeric from_lot_bal, to_lot_bal;
|
||||||
|
Timespec from_ts, to_ts;
|
||||||
|
time64 time_posted;
|
||||||
|
Split *split;
|
||||||
|
|
||||||
|
/* Sanity check */
|
||||||
|
if (!gncInvoiceGetInvoiceFromLot (from_lot) ||
|
||||||
|
!gncInvoiceGetInvoiceFromLot (to_lot))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Determine transaction date based on lot splits */
|
||||||
|
from_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (from_lot)));
|
||||||
|
to_ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (to_lot)));
|
||||||
|
if (timespecToTime64 (from_ts) >= timespecToTime64 (to_ts))
|
||||||
|
time_posted = timespecToTime64 (from_ts);
|
||||||
|
else
|
||||||
|
time_posted = timespecToTime64 (to_ts);
|
||||||
|
|
||||||
|
/* Figure out how much we can offset between the lots */
|
||||||
|
from_lot_bal = gnc_lot_get_balance (from_lot);
|
||||||
|
to_lot_bal = gnc_lot_get_balance (to_lot);
|
||||||
|
if (gnc_numeric_compare (gnc_numeric_abs (from_lot_bal),
|
||||||
|
gnc_numeric_abs (to_lot_bal)) > 0)
|
||||||
|
from_lot_bal = gnc_numeric_neg (to_lot_bal);
|
||||||
|
else
|
||||||
|
to_lot_bal = gnc_numeric_neg (from_lot_bal);
|
||||||
|
|
||||||
|
xaccAccountBeginEdit (acct);
|
||||||
|
|
||||||
|
/* Look for a pre-existing lot link we can extend */
|
||||||
|
ll_txn = get_ll_transaction_from_lot (from_lot);
|
||||||
|
|
||||||
|
if (!ll_txn)
|
||||||
|
ll_txn = get_ll_transaction_from_lot (to_lot);
|
||||||
|
|
||||||
|
if (!ll_txn)
|
||||||
|
{
|
||||||
|
/* No pre-existing lot link. Create one. */
|
||||||
|
Timespec ts;
|
||||||
|
|
||||||
|
timespecFromTime64 (&ts, time_posted);
|
||||||
|
|
||||||
|
ll_txn = xaccMallocTransaction (gnc_lot_get_book (from_lot));
|
||||||
|
xaccTransBeginEdit (ll_txn);
|
||||||
|
|
||||||
|
xaccTransSetDescription (ll_txn, name ? name : "(Unknown)");
|
||||||
|
xaccTransSetCurrency (ll_txn, xaccAccountGetCommodity(acct));
|
||||||
|
xaccTransSetDateEnteredSecs (ll_txn, gnc_time (NULL));
|
||||||
|
xaccTransSetDatePostedTS (ll_txn, &ts);
|
||||||
|
xaccTransSetTxnType (ll_txn, TXN_TYPE_LINK);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Timespec ts = xaccTransRetDatePostedTS (ll_txn);
|
||||||
|
xaccTransBeginEdit (ll_txn);
|
||||||
|
|
||||||
|
/* Maybe we need to update the post date of the transaction ? */
|
||||||
|
if (time_posted > timespecToTime64 (ts))
|
||||||
|
{
|
||||||
|
timespecFromTime64 (&ts, time_posted);
|
||||||
|
xaccTransSetDatePostedTS (ll_txn, &ts);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a split for the from_lot */
|
||||||
|
split = xaccMallocSplit (gnc_lot_get_book (from_lot));
|
||||||
|
/* set Action using utility function */
|
||||||
|
gnc_set_num_action (NULL, split, NULL, action);
|
||||||
|
xaccAccountInsertSplit (acct, split);
|
||||||
|
xaccTransAppendSplit (ll_txn, split);
|
||||||
|
/* To offset the lot balance, the split must be of the opposite sign */
|
||||||
|
xaccSplitSetBaseValue (split, gnc_numeric_neg (from_lot_bal), xaccAccountGetCommodity(acct));
|
||||||
|
gnc_lot_add_split (from_lot, split);
|
||||||
|
|
||||||
|
/* Create a split for the to_lot */
|
||||||
|
split = xaccMallocSplit (gnc_lot_get_book (to_lot));
|
||||||
|
/* set Action using utility function */
|
||||||
|
gnc_set_num_action (NULL, split, NULL, action);
|
||||||
|
xaccAccountInsertSplit (acct, split);
|
||||||
|
xaccTransAppendSplit (ll_txn, split);
|
||||||
|
/* To offset the lot balance, the split must be of the opposite sign */
|
||||||
|
xaccSplitSetBaseValue (split, gnc_numeric_neg (to_lot_bal), xaccAccountGetCommodity(acct));
|
||||||
|
gnc_lot_add_split (to_lot, split);
|
||||||
|
|
||||||
|
xaccTransCommitEdit (ll_txn);
|
||||||
|
|
||||||
|
|
||||||
|
/* Do some post-cleaning on the lots
|
||||||
|
* The above actions may have created splits that are
|
||||||
|
* in the same transaction and lot. These can be merged.
|
||||||
|
*/
|
||||||
|
xaccScrubMergeLotSubSplits (to_lot, FALSE);
|
||||||
|
xaccScrubMergeLotSubSplits (from_lot, FALSE);
|
||||||
|
/* And finally set the same memo for all remaining splits
|
||||||
|
* It's a convenience for the users to identify all documents
|
||||||
|
* involved in the link.
|
||||||
|
*/
|
||||||
|
gncOwnerSetLotLinkMemo (ll_txn);
|
||||||
|
xaccAccountCommitEdit (acct);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gncOwnerOffsetLots (GNCLot *from_lot, GNCLot *to_lot, const GncOwner *owner)
|
||||||
|
{
|
||||||
|
gnc_numeric target_offset;
|
||||||
|
Split *split;
|
||||||
|
|
||||||
|
/* from lot should not be a document lot because we're removing a split from there ! */
|
||||||
|
if (gncInvoiceGetInvoiceFromLot (from_lot))
|
||||||
|
{
|
||||||
|
PWARN ("from_lot %p is a document lot. That is not allowed in gncOwnerOffsetLots", from_lot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get best matching split from from_lot to offset to_lot */
|
||||||
|
target_offset = gnc_lot_get_balance (to_lot);
|
||||||
|
if (gnc_numeric_zero_p (target_offset))
|
||||||
|
return; // to_lot is already balanced, nothing more to do
|
||||||
|
|
||||||
|
split = gncOwnerFindOffsettingSplit (from_lot, target_offset);
|
||||||
|
if (!split)
|
||||||
|
return; // No suitable offsetting split found, nothing more to do
|
||||||
|
|
||||||
|
/* If the offsetting split is bigger than the amount needed to balance
|
||||||
|
* to_lot, reduce the split so its reduced value closes to_lot exactly.
|
||||||
|
* Note the negation in the reduction function. The split must be of
|
||||||
|
* opposite sign of to_lot's balance in order to be able to close it.
|
||||||
|
*/
|
||||||
|
if (gnc_numeric_compare (gnc_numeric_abs (xaccSplitGetValue (split)),
|
||||||
|
gnc_numeric_abs (target_offset)) > 0)
|
||||||
|
gncOwnerReduceSplitTo (split, gnc_numeric_neg (target_offset));
|
||||||
|
|
||||||
|
/* Move the reduced split from from_lot to to_lot */
|
||||||
|
gnc_lot_add_split (to_lot, split);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
|
void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
|
||||||
{
|
{
|
||||||
GList *base_iter;
|
GList *left_iter;
|
||||||
|
|
||||||
/* General note: in the code below the term "payment" can
|
/* General note: in the code below the term "payment" can
|
||||||
* both mean a true payment or a document of
|
* both mean a true payment or a document of
|
||||||
@ -850,164 +1212,126 @@ void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
|
|||||||
if (!owner) return;
|
if (!owner) return;
|
||||||
if (!lots) return;
|
if (!lots) return;
|
||||||
|
|
||||||
for (base_iter = lots; base_iter; base_iter = base_iter->next)
|
for (left_iter = lots; left_iter; left_iter = left_iter->next)
|
||||||
{
|
{
|
||||||
GNCLot *base_lot = base_iter->data;
|
GNCLot *left_lot = left_iter->data;
|
||||||
QofBook *book;
|
gnc_numeric left_lot_bal;
|
||||||
|
gboolean left_lot_has_doc;
|
||||||
|
gboolean left_modified = FALSE;
|
||||||
Account *acct;
|
Account *acct;
|
||||||
const gchar *name;
|
GList *right_iter;
|
||||||
GList *lot_list, *lot_iter;
|
|
||||||
Transaction *txn = NULL;
|
|
||||||
gnc_numeric base_lot_bal, val_to_pay, val_paid = { 0, 1 };
|
|
||||||
gboolean base_bal_is_pos;
|
|
||||||
const gchar *action, *memo;
|
|
||||||
|
|
||||||
/* Only attempt to apply payments to open lots.
|
/* Only attempt to apply payments to open lots.
|
||||||
* Note that due to the iterative nature of this function lots
|
* Note that due to the iterative nature of this function lots
|
||||||
* in the list may become closed before they are evaluated as
|
* in the list may become empty/closed before they are evaluated as
|
||||||
* base lot, so we should check this for each lot. */
|
* base lot, so we should check this for each lot. */
|
||||||
base_lot_bal = gnc_lot_get_balance (base_lot);
|
if (!left_lot)
|
||||||
if (gnc_numeric_zero_p (base_lot_bal))
|
continue;
|
||||||
|
if (gnc_lot_count_splits (left_lot) == 0)
|
||||||
|
{
|
||||||
|
gnc_lot_destroy (left_lot);
|
||||||
|
left_iter->data = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (gnc_lot_is_closed (left_lot))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
book = gnc_lot_get_book (base_lot);
|
acct = gnc_lot_get_account (left_lot);
|
||||||
acct = gnc_lot_get_account (base_lot);
|
xaccAccountBeginEdit (acct);
|
||||||
name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
|
|
||||||
lot_list = base_iter->next;
|
|
||||||
|
|
||||||
/* Strings used when creating splits later on. */
|
left_lot_bal = gnc_lot_get_balance (left_lot);
|
||||||
action = _("Lot Link");
|
left_lot_has_doc = (gncInvoiceGetInvoiceFromLot (left_lot) != NULL);
|
||||||
memo = _("Internal link between invoice and payment lots");
|
|
||||||
|
|
||||||
/* Note: to balance the lot the payment to assign
|
/* Attempt to offset left_lot with any of the remaining lots. To do so
|
||||||
* must have the opposite sign of the existing lot balance */
|
* iterate over the remaining lots adding lot links or moving payments
|
||||||
val_to_pay = gnc_numeric_neg (base_lot_bal);
|
* around.
|
||||||
base_bal_is_pos = gnc_numeric_positive_p (base_lot_bal);
|
|
||||||
|
|
||||||
|
|
||||||
/* Create splits in a linking transaction between lots until
|
|
||||||
* - either the invoice lot is balanced
|
|
||||||
* - or there are no more balancing lots.
|
|
||||||
*/
|
*/
|
||||||
for (lot_iter = lot_list; lot_iter; lot_iter = lot_iter->next)
|
for (right_iter = left_iter->next; right_iter; right_iter = right_iter->next)
|
||||||
{
|
{
|
||||||
gnc_numeric payment_lot_balance;
|
GNCLot *right_lot = right_iter->data;
|
||||||
Split *split;
|
gnc_numeric right_lot_bal;
|
||||||
Account *bal_acct;
|
gboolean right_lot_has_doc;
|
||||||
gnc_numeric split_amt;
|
|
||||||
|
|
||||||
GNCLot *balancing_lot = lot_iter->data;
|
|
||||||
|
|
||||||
/* Only attempt to use open lots to balance the base lot.
|
/* Only attempt to use open lots to balance the base lot.
|
||||||
* Note that due to the iterative nature of this function lots
|
* Note that due to the iterative nature of this function lots
|
||||||
* in the list may become closed before they are evaluated as
|
* in the list may become empty/closed before they are evaluated as
|
||||||
* base lot, so we should check this for each lot. */
|
* base lot, so we should check this for each lot. */
|
||||||
if (gnc_lot_is_closed (balancing_lot))
|
if (!right_lot)
|
||||||
|
continue;
|
||||||
|
if (gnc_lot_count_splits (right_lot) == 0)
|
||||||
|
{
|
||||||
|
gnc_lot_destroy (right_lot);
|
||||||
|
right_iter->data = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (gnc_lot_is_closed (right_lot))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Balancing transactions for invoice/payments can only happen
|
/* Balancing transactions for invoice/payments can only happen
|
||||||
* in the same account. */
|
* in the same account. */
|
||||||
bal_acct = gnc_lot_get_account (balancing_lot);
|
if (acct != gnc_lot_get_account (right_lot))
|
||||||
if (acct != bal_acct)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
payment_lot_balance = gnc_lot_get_balance (balancing_lot);
|
|
||||||
|
|
||||||
/* Only attempt to balance if the base lot and balancing lot are
|
/* Only attempt to balance if the base lot and balancing lot are
|
||||||
* of the opposite sign. (Otherwise we would increase the balance
|
* of the opposite sign. (Otherwise we would increase the balance
|
||||||
* of the lot - Duh */
|
* of the lot - Duh */
|
||||||
if (base_bal_is_pos == gnc_numeric_positive_p (payment_lot_balance))
|
right_lot_bal = gnc_lot_get_balance (right_lot);
|
||||||
|
if (gnc_numeric_positive_p (left_lot_bal) == gnc_numeric_positive_p (right_lot_bal))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/*
|
/* Ok we found two lots than can (partly) offset each other.
|
||||||
* If there is less to pay than there's open in the lot; we're done -- apply the base_lot_vale.
|
* Depending on the lot types, a different action is needed to accomplish this.
|
||||||
* Note that payment_value and balance are opposite in sign, so we have to compare absolute values here
|
* 1. Both lots are document lots (invoices/credit notes)
|
||||||
*
|
* -> Create a lot linking transaction between the lots
|
||||||
* Otherwise, apply the balance, subtract that from the payment_value,
|
* 2. Both lots are payment lots (lots without a document attached)
|
||||||
* and move on to the next one.
|
* -> Use part of the bigger lot to the close the smaller lot
|
||||||
|
* 3. One document lot with one payment lot
|
||||||
|
* -> Use (part of) the payment to offset (part of) the document lot,
|
||||||
|
* Which one will be closed depends on which is the bigger one
|
||||||
*/
|
*/
|
||||||
if (gnc_numeric_compare (gnc_numeric_abs (val_to_pay), gnc_numeric_abs (payment_lot_balance)) <= 0)
|
right_lot_has_doc = (gncInvoiceGetInvoiceFromLot (right_lot) != NULL);
|
||||||
|
if (left_lot_has_doc && right_lot_has_doc)
|
||||||
|
gncOwnerCreateLotLink (left_lot, right_lot, owner);
|
||||||
|
else if (!left_lot_has_doc && !right_lot_has_doc)
|
||||||
{
|
{
|
||||||
/* abs(val_to_pay) <= abs(balance) */
|
gint cmp = gnc_numeric_compare (gnc_numeric_abs (left_lot_bal),
|
||||||
split_amt = val_to_pay;
|
gnc_numeric_abs (right_lot_bal));
|
||||||
|
if (cmp >= 0)
|
||||||
|
gncOwnerOffsetLots (left_lot, right_lot, owner);
|
||||||
|
else
|
||||||
|
gncOwnerOffsetLots (right_lot, left_lot, owner);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* abs(val_to_pay) > abs(balance)
|
GNCLot *doc_lot = left_lot_has_doc ? left_lot : right_lot;
|
||||||
* Remember payment_value and balance are opposite in sign,
|
GNCLot *pay_lot = left_lot_has_doc ? right_lot : left_lot;
|
||||||
* and we want a payment to neutralize the current balance
|
// Ok, let's try to move a payment from pay_lot to doc_lot
|
||||||
* so we need to negate here */
|
gncOwnerOffsetLots (pay_lot, doc_lot, owner);
|
||||||
split_amt = payment_lot_balance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If not created yet, create a new transaction linking
|
/* If we get here, then right_lot was modified
|
||||||
* the base lot and the balancing lot(s) */
|
* If the lot has a document, send an event for send an event for it as well
|
||||||
if (!txn)
|
* so it gets potentially updated as paid */
|
||||||
|
|
||||||
{
|
{
|
||||||
Timespec ts = xaccTransRetDatePostedTS (xaccSplitGetParent (gnc_lot_get_latest_split (base_lot)));
|
GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(right_lot);
|
||||||
|
|
||||||
xaccAccountBeginEdit (acct);
|
|
||||||
|
|
||||||
txn = xaccMallocTransaction (book);
|
|
||||||
xaccTransBeginEdit (txn);
|
|
||||||
|
|
||||||
xaccTransSetDescription (txn, name ? name : "");
|
|
||||||
xaccTransSetCurrency (txn, xaccAccountGetCommodity(acct));
|
|
||||||
xaccTransSetDateEnteredSecs (txn, gnc_time (NULL));
|
|
||||||
xaccTransSetDatePostedTS (txn, &ts);
|
|
||||||
xaccTransSetTxnType (txn, TXN_TYPE_LINK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the split for this link in current balancing lot */
|
|
||||||
split = xaccMallocSplit (book);
|
|
||||||
xaccSplitSetMemo (split, memo);
|
|
||||||
/* set Action using utility function */
|
|
||||||
gnc_set_num_action (NULL, split, NULL, action);
|
|
||||||
xaccAccountInsertSplit (acct, split);
|
|
||||||
xaccTransAppendSplit (txn, split);
|
|
||||||
xaccSplitSetBaseValue (split, gnc_numeric_neg (split_amt), xaccAccountGetCommodity(acct));
|
|
||||||
gnc_lot_add_split (balancing_lot, split);
|
|
||||||
|
|
||||||
/* If the balancing lot was linked to a document (invoice/credit note),
|
|
||||||
* send an event for it as well so it gets potentially updated as paid */
|
|
||||||
{
|
|
||||||
GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(balancing_lot);
|
|
||||||
if (this_invoice)
|
if (this_invoice)
|
||||||
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
|
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
|
||||||
}
|
}
|
||||||
|
left_modified = TRUE;
|
||||||
val_paid = gnc_numeric_add (val_paid, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
|
|
||||||
val_to_pay = gnc_numeric_sub (val_to_pay, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
|
|
||||||
if (gnc_numeric_zero_p (val_to_pay))
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If left_lot was modified and the lot has a document,
|
||||||
/* If the above loop managed to create a transaction and some balancing splits,
|
* send an event for send an event for it as well
|
||||||
* create the final split for the link transaction in the base lot */
|
* so it gets potentially updated as paid */
|
||||||
if (txn)
|
if (left_modified)
|
||||||
{
|
{
|
||||||
GncInvoice *this_invoice;
|
GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(left_lot);
|
||||||
Split *split = xaccMallocSplit (book);
|
|
||||||
|
|
||||||
xaccSplitSetMemo (split, memo);
|
|
||||||
/* set Action with utiltity function */
|
|
||||||
gnc_set_num_action (NULL, split, NULL, action);
|
|
||||||
xaccAccountInsertSplit (acct, split);
|
|
||||||
xaccTransAppendSplit (txn, split);
|
|
||||||
xaccSplitSetBaseValue (split, val_paid, xaccAccountGetCommodity(acct));
|
|
||||||
gnc_lot_add_split (base_lot, split);
|
|
||||||
|
|
||||||
xaccTransCommitEdit (txn);
|
|
||||||
xaccAccountCommitEdit (acct);
|
|
||||||
|
|
||||||
/* If the base lot was linked to a document (invoice/credit note),
|
|
||||||
* send an event for it as well so it gets potentially updated as paid */
|
|
||||||
this_invoice = gncInvoiceGetInvoiceFromLot(base_lot);
|
|
||||||
if (this_invoice)
|
if (this_invoice)
|
||||||
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
|
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
xaccAccountCommitEdit (acct);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -263,6 +263,34 @@ gncOwnerApplyPayment (const GncOwner *owner, Transaction *txn, GList *lots,
|
|||||||
gnc_numeric amount, gnc_numeric exch, Timespec date,
|
gnc_numeric amount, gnc_numeric exch, Timespec date,
|
||||||
const char *memo, const char *num, gboolean auto_pay);
|
const char *memo, const char *num, gboolean auto_pay);
|
||||||
|
|
||||||
|
/** Helper function to find a split in lot that best offsets target_value
|
||||||
|
* Obviously it should be of opposite sign.
|
||||||
|
* If there are more splits of opposite sign the following
|
||||||
|
* criteria are used in order of preference:
|
||||||
|
* 1. exact match in abs value is preferred over larger abs value
|
||||||
|
* 2. larger abs value is preferred over smaller abs value
|
||||||
|
* 3. if previous and new candidate are in the same value category,
|
||||||
|
* prefer real payment splits over lot link splits
|
||||||
|
* 4. if previous and new candiate are of same split type
|
||||||
|
* prefer biggest abs value.
|
||||||
|
*/
|
||||||
|
Split *gncOwnerFindOffsettingSplit (GNCLot *pay_lot, gnc_numeric target_value);
|
||||||
|
|
||||||
|
/** Helper function to reduce the value of a split to target_value. To make
|
||||||
|
* sure the split's parent transaction remains balanced a second split
|
||||||
|
* will be created with the remainder. Similarly if the split was part of a
|
||||||
|
* (business) lot, the remainder split will be added to the same lot to
|
||||||
|
* keep the lot's balance unchanged.
|
||||||
|
*/
|
||||||
|
gboolean gncOwnerReduceSplitTo (Split *split, gnc_numeric target_value);
|
||||||
|
|
||||||
|
/** To help a user understand what a lot link transaction does,
|
||||||
|
* we set the memo to name all documents involved in the link.
|
||||||
|
* The function below calculates this memo and sets it for
|
||||||
|
* all splits in the lot link transaction.
|
||||||
|
*/
|
||||||
|
void gncOwnerSetLotLinkMemo (Transaction *ll_txn);
|
||||||
|
|
||||||
/** Returns a GList of account-types based on the owner type */
|
/** Returns a GList of account-types based on the owner type */
|
||||||
GList * gncOwnerGetAccountTypesList (const GncOwner *owner);
|
GList * gncOwnerGetAccountTypesList (const GncOwner *owner);
|
||||||
|
|
||||||
|
@ -130,7 +130,6 @@ static void gnc_main_window_destroy (GtkObject *object);
|
|||||||
|
|
||||||
static void gnc_main_window_setup_window (GncMainWindow *window);
|
static void gnc_main_window_setup_window (GncMainWindow *window);
|
||||||
static void gnc_window_main_window_init (GncWindowIface *iface);
|
static void gnc_window_main_window_init (GncWindowIface *iface);
|
||||||
static gboolean main_window_find_tab_event (GncMainWindow *window, GncPluginPage *page, GtkWidget **event_p);
|
|
||||||
#ifndef MAC_INTEGRATION
|
#ifndef MAC_INTEGRATION
|
||||||
static void gnc_main_window_update_all_menu_items (void);
|
static void gnc_main_window_update_all_menu_items (void);
|
||||||
#endif
|
#endif
|
||||||
@ -2009,31 +2008,11 @@ static void
|
|||||||
gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
|
gnc_main_window_update_tab_color_one_page (GncPluginPage *page,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
GncMainWindow *window = user_data;
|
|
||||||
GncMainWindowPrivate *priv;
|
|
||||||
const gchar *color_string;
|
const gchar *color_string;
|
||||||
GdkColor tab_color;
|
|
||||||
GtkWidget *event_box;
|
|
||||||
|
|
||||||
ENTER("page %p", page);
|
ENTER("page %p", page);
|
||||||
|
|
||||||
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
|
||||||
|
|
||||||
/* Get the event box to update the tab */
|
|
||||||
main_window_find_tab_event(window, page, &event_box);
|
|
||||||
|
|
||||||
color_string = gnc_plugin_page_get_page_color(page);
|
color_string = gnc_plugin_page_get_page_color(page);
|
||||||
if (color_string == NULL) color_string = "";
|
main_window_update_page_color (page, color_string);
|
||||||
if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
|
||||||
{
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
|
|
||||||
}
|
|
||||||
LEAVE(" ");
|
LEAVE(" ");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2142,7 +2121,7 @@ main_window_find_tab_items (GncMainWindow *window,
|
|||||||
GtkWidget **entry_p)
|
GtkWidget **entry_p)
|
||||||
{
|
{
|
||||||
GncMainWindowPrivate *priv;
|
GncMainWindowPrivate *priv;
|
||||||
GtkWidget *tab_hbox, *widget, *event_box;
|
GtkWidget *tab_hbox, *widget, *tab_widget;
|
||||||
GList *children, *tmp;
|
GList *children, *tmp;
|
||||||
|
|
||||||
ENTER("window %p, page %p, label_p %p, entry_p %p",
|
ENTER("window %p, page %p, label_p %p, entry_p %p",
|
||||||
@ -2156,10 +2135,17 @@ main_window_find_tab_items (GncMainWindow *window,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
tab_widget = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
||||||
page->notebook_page);
|
page->notebook_page);
|
||||||
|
if (GTK_IS_EVENT_BOX (tab_widget))
|
||||||
tab_hbox = gtk_bin_get_child(GTK_BIN(event_box));
|
tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
|
||||||
|
else if (GTK_IS_HBOX (tab_widget))
|
||||||
|
tab_hbox = tab_widget;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PWARN ("Unknown widget for tab label %p", tab_widget);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
|
children = gtk_container_get_children(GTK_CONTAINER(tab_hbox));
|
||||||
for (tmp = children; tmp; tmp = g_list_next(tmp))
|
for (tmp = children; tmp; tmp = g_list_next(tmp))
|
||||||
@ -2181,16 +2167,15 @@ main_window_find_tab_items (GncMainWindow *window,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
main_window_find_tab_event (GncMainWindow *window,
|
main_window_find_tab_widget (GncMainWindow *window,
|
||||||
GncPluginPage *page,
|
GncPluginPage *page,
|
||||||
GtkWidget **event_p)
|
GtkWidget **widget_p)
|
||||||
{
|
{
|
||||||
GncMainWindowPrivate *priv;
|
GncMainWindowPrivate *priv;
|
||||||
GtkWidget *event_box;
|
|
||||||
|
|
||||||
ENTER("window %p, page %p, event %p",
|
ENTER("window %p, page %p, widget %p",
|
||||||
window, page, event_p);
|
window, page, widget_p);
|
||||||
*event_p = NULL;
|
*widget_p = NULL;
|
||||||
|
|
||||||
if (!page->notebook_page)
|
if (!page->notebook_page)
|
||||||
{
|
{
|
||||||
@ -2199,17 +2184,11 @@ main_window_find_tab_event (GncMainWindow *window,
|
|||||||
}
|
}
|
||||||
|
|
||||||
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
||||||
event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
*widget_p = gtk_notebook_get_tab_label(GTK_NOTEBOOK(priv->notebook),
|
||||||
page->notebook_page);
|
page->notebook_page);
|
||||||
if (GTK_IS_EVENT_BOX(event_box))
|
|
||||||
{
|
|
||||||
*event_p = event_box;
|
|
||||||
LEAVE("event %p", *event_p);
|
|
||||||
return (TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
LEAVE("event %p", *event_p);
|
LEAVE("widget %p", *widget_p);
|
||||||
return (FALSE);
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -2218,7 +2197,7 @@ main_window_update_page_name (GncPluginPage *page,
|
|||||||
{
|
{
|
||||||
GncMainWindow *window;
|
GncMainWindow *window;
|
||||||
GncMainWindowPrivate *priv;
|
GncMainWindowPrivate *priv;
|
||||||
GtkWidget *label, *entry, *event_box;
|
GtkWidget *label, *entry;
|
||||||
gchar *name, *old_page_name, *old_page_long_name;
|
gchar *name, *old_page_name, *old_page_long_name;
|
||||||
|
|
||||||
ENTER(" ");
|
ENTER(" ");
|
||||||
@ -2264,14 +2243,15 @@ main_window_update_page_name (GncPluginPage *page,
|
|||||||
{
|
{
|
||||||
gchar *new_page_long_name;
|
gchar *new_page_long_name;
|
||||||
gint string_position;
|
gint string_position;
|
||||||
|
GtkWidget *tab_widget;
|
||||||
|
|
||||||
string_position = strlen(old_page_long_name) - strlen(old_page_name);
|
string_position = strlen(old_page_long_name) - strlen(old_page_name);
|
||||||
new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
|
new_page_long_name = g_strconcat(g_strndup(old_page_long_name, string_position), name, NULL);
|
||||||
|
|
||||||
gnc_plugin_page_set_page_long_name(page, new_page_long_name);
|
gnc_plugin_page_set_page_long_name(page, new_page_long_name);
|
||||||
|
|
||||||
if (main_window_find_tab_event(window, page, &event_box))
|
if (main_window_find_tab_widget(window, page, &tab_widget))
|
||||||
gtk_widget_set_tooltip_text(event_box, new_page_long_name);
|
gtk_widget_set_tooltip_text(tab_widget, new_page_long_name);
|
||||||
|
|
||||||
g_free(new_page_long_name);
|
g_free(new_page_long_name);
|
||||||
}
|
}
|
||||||
@ -2300,44 +2280,55 @@ main_window_update_page_color (GncPluginPage *page,
|
|||||||
{
|
{
|
||||||
GncMainWindow *window;
|
GncMainWindow *window;
|
||||||
GncMainWindowPrivate *priv;
|
GncMainWindowPrivate *priv;
|
||||||
GtkWidget *event_box;
|
GtkWidget *tab_widget;
|
||||||
GdkColor tab_color;
|
GdkColor tab_color;
|
||||||
gchar *color_string;
|
gchar *color_string = NULL;
|
||||||
|
gboolean want_color = FALSE;
|
||||||
|
|
||||||
ENTER(" ");
|
ENTER(" ");
|
||||||
if ((color_in == NULL) || (*color_in == '\0'))
|
if (color_in)
|
||||||
{
|
color_string = g_strstrip(g_strdup(color_in));
|
||||||
LEAVE("no string");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
color_string = g_strstrip(g_strdup(color_in));
|
|
||||||
|
|
||||||
/* Optimization, if the color hasn't changed, don't update. */
|
if (color_string && *color_string != '\0')
|
||||||
if (*color_string == '\0' || 0 == g_strcmp0(color_string, gnc_plugin_page_get_page_color(page)))
|
want_color = TRUE;
|
||||||
{
|
|
||||||
g_free(color_string);
|
|
||||||
LEAVE("empty string or color unchanged");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the plugin */
|
/* Update the plugin */
|
||||||
window = GNC_MAIN_WINDOW(page->window);
|
window = GNC_MAIN_WINDOW(page->window);
|
||||||
gnc_plugin_page_set_page_color(page, color_string);
|
if (want_color)
|
||||||
|
gnc_plugin_page_set_page_color(page, color_string);
|
||||||
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
else
|
||||||
|
gnc_plugin_page_set_page_color(page, NULL);
|
||||||
|
|
||||||
/* Update the notebook tab */
|
/* Update the notebook tab */
|
||||||
main_window_find_tab_event(window, page, &event_box);
|
main_window_find_tab_widget (window, page, &tab_widget);
|
||||||
|
priv = GNC_MAIN_WINDOW_GET_PRIVATE(window);
|
||||||
|
|
||||||
if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
if (want_color && gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
||||||
{
|
{
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
|
if (!GTK_IS_EVENT_BOX (tab_widget))
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
|
{
|
||||||
|
GtkWidget *event_box = gtk_event_box_new ();
|
||||||
|
g_object_ref (tab_widget);
|
||||||
|
gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
|
||||||
|
page->notebook_page, event_box);
|
||||||
|
gtk_container_add (GTK_CONTAINER(event_box), tab_widget);
|
||||||
|
g_object_unref (tab_widget);
|
||||||
|
tab_widget = event_box;
|
||||||
|
}
|
||||||
|
gtk_widget_modify_bg(tab_widget, GTK_STATE_NORMAL, &tab_color);
|
||||||
|
gtk_widget_modify_bg(tab_widget, GTK_STATE_ACTIVE, &tab_color);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
|
if (GTK_IS_EVENT_BOX (tab_widget))
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
|
{
|
||||||
|
GtkWidget *tab_hbox = gtk_bin_get_child(GTK_BIN(tab_widget));
|
||||||
|
g_object_ref (tab_hbox);
|
||||||
|
gtk_container_remove (GTK_CONTAINER(tab_widget), tab_hbox);
|
||||||
|
gtk_notebook_set_tab_label (GTK_NOTEBOOK(priv->notebook),
|
||||||
|
page->notebook_page, tab_hbox);
|
||||||
|
g_object_unref (tab_hbox);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
g_free(color_string);
|
g_free(color_string);
|
||||||
LEAVE("done");
|
LEAVE("done");
|
||||||
@ -2882,7 +2873,7 @@ gnc_main_window_open_page (GncMainWindow *window,
|
|||||||
{
|
{
|
||||||
GncMainWindowPrivate *priv;
|
GncMainWindowPrivate *priv;
|
||||||
GtkWidget *tab_hbox;
|
GtkWidget *tab_hbox;
|
||||||
GtkWidget *label, *entry, *event_box;
|
GtkWidget *label, *entry;
|
||||||
const gchar *icon, *text, *color_string;
|
const gchar *icon, *text, *color_string;
|
||||||
GtkWidget *image;
|
GtkWidget *image;
|
||||||
GList *tmp;
|
GList *tmp;
|
||||||
@ -2955,27 +2946,10 @@ gnc_main_window_open_page (GncMainWindow *window,
|
|||||||
else
|
else
|
||||||
gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
|
gtk_box_pack_start (GTK_BOX (tab_hbox), label, TRUE, TRUE, 0);
|
||||||
|
|
||||||
event_box = gtk_event_box_new();
|
|
||||||
gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box), TRUE);
|
|
||||||
gtk_widget_show(event_box);
|
|
||||||
gtk_container_add(GTK_CONTAINER(event_box), tab_hbox);
|
|
||||||
color_string = gnc_plugin_page_get_page_color(page);
|
|
||||||
if (color_string == NULL) color_string = "";
|
|
||||||
if (gdk_color_parse(color_string, &tab_color) && priv->show_color_tabs)
|
|
||||||
{
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, &tab_color);
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, &tab_color);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_NORMAL, NULL);
|
|
||||||
gtk_widget_modify_bg(event_box, GTK_STATE_ACTIVE, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
text = gnc_plugin_page_get_page_long_name(page);
|
text = gnc_plugin_page_get_page_long_name(page);
|
||||||
if (text)
|
if (text)
|
||||||
{
|
{
|
||||||
gtk_widget_set_tooltip_text(event_box, text);
|
gtk_widget_set_tooltip_text(tab_hbox, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
entry = gtk_entry_new();
|
entry = gtk_entry_new();
|
||||||
@ -3029,7 +3003,10 @@ gnc_main_window_open_page (GncMainWindow *window,
|
|||||||
/*
|
/*
|
||||||
* Now install it all in the window.
|
* Now install it all in the window.
|
||||||
*/
|
*/
|
||||||
gnc_main_window_connect(window, page, event_box, label);
|
gnc_main_window_connect(window, page, tab_hbox, label);
|
||||||
|
|
||||||
|
color_string = gnc_plugin_page_get_page_color(page);
|
||||||
|
main_window_update_page_color (page, color_string);
|
||||||
LEAVE("");
|
LEAVE("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,7 +868,8 @@ gnc_plugin_page_set_page_color (GncPluginPage *page, const gchar *color)
|
|||||||
priv = GNC_PLUGIN_PAGE_GET_PRIVATE(page);
|
priv = GNC_PLUGIN_PAGE_GET_PRIVATE(page);
|
||||||
if (priv->page_color)
|
if (priv->page_color)
|
||||||
g_free(priv->page_color);
|
g_free(priv->page_color);
|
||||||
priv->page_color = g_strdup(color);
|
if (color)
|
||||||
|
priv->page_color = g_strdup(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ gnc_tree_view_add_date_column (GncTreeView *view,
|
|||||||
* column used to determine the foreground color of any text in this
|
* column used to determine the foreground color of any text in this
|
||||||
* column. It should be used to display negative numbers in red.
|
* column. It should be used to display negative numbers in red.
|
||||||
* Use GNC_TREE_VIEW_COLUMN_COLOR_NONE if the text in this column
|
* Use GNC_TREE_VIEW_COLUMN_COLOR_NONE if the text in this column
|
||||||
* should always be displayed in black.
|
* should always be displayed in the default theme color for text.
|
||||||
*
|
*
|
||||||
* @param model_visibility_column The index of the GtkTreeModel data
|
* @param model_visibility_column The index of the GtkTreeModel data
|
||||||
* column used to determine whether or not a checkbox for each row
|
* column used to determine whether or not a checkbox for each row
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
#include "Recurrence.h"
|
#include "Recurrence.h"
|
||||||
#include "Query.h"
|
#include "Query.h"
|
||||||
#include "Scrub.h"
|
#include "Scrub.h"
|
||||||
#include "Scrub3.h"
|
|
||||||
#include "Transaction.h"
|
#include "Transaction.h"
|
||||||
#include "dialog-utils.h"
|
#include "dialog-utils.h"
|
||||||
#include "assistant-acct-period.h"
|
#include "assistant-acct-period.h"
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "qof.h"
|
#include "qof.h"
|
||||||
#include "gnc-lot.h"
|
#include "gnc-lot.h"
|
||||||
#include "Scrub3.h"
|
#include "Scrub3.h"
|
||||||
|
#include "ScrubBusiness.h"
|
||||||
#include "Transaction.h"
|
#include "Transaction.h"
|
||||||
#include "engine-helpers.h"
|
#include "engine-helpers.h"
|
||||||
#include "gncInvoice.h"
|
#include "gncInvoice.h"
|
||||||
@ -750,14 +751,20 @@ lv_response_cb (GtkDialog *dialog, gint response, gpointer data)
|
|||||||
case RESPONSE_SCRUB_LOT:
|
case RESPONSE_SCRUB_LOT:
|
||||||
if (NULL == lot)
|
if (NULL == lot)
|
||||||
return;
|
return;
|
||||||
xaccScrubLot (lot);
|
if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
|
||||||
|
gncScrubBusinessLot (lot);
|
||||||
|
else
|
||||||
|
xaccScrubLot (lot);
|
||||||
gnc_lot_viewer_fill (lv);
|
gnc_lot_viewer_fill (lv);
|
||||||
lv_show_splits_in_lot (lv);
|
lv_show_splits_in_lot (lv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RESPONSE_SCRUB_ACCOUNT:
|
case RESPONSE_SCRUB_ACCOUNT:
|
||||||
gnc_suspend_gui_refresh ();
|
gnc_suspend_gui_refresh ();
|
||||||
xaccAccountScrubLots (lv->account);
|
if (xaccAccountIsAPARType (xaccAccountGetType(lv->account)))
|
||||||
|
gncScrubBusinessAccountLots (lv->account);
|
||||||
|
else
|
||||||
|
xaccAccountScrubLots (lv->account);
|
||||||
gnc_resume_gui_refresh ();
|
gnc_resume_gui_refresh ();
|
||||||
gnc_lot_viewer_fill (lv);
|
gnc_lot_viewer_fill (lv);
|
||||||
lv_show_splits_free (lv);
|
lv_show_splits_free (lv);
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
#include "Scrub.h"
|
#include "Scrub.h"
|
||||||
#include "Scrub3.h"
|
#include "Scrub3.h"
|
||||||
|
#include "ScrubBusiness.h"
|
||||||
#include "Transaction.h"
|
#include "Transaction.h"
|
||||||
#include "dialog-account.h"
|
#include "dialog-account.h"
|
||||||
#include "dialog-transfer.h"
|
#include "dialog-transfer.h"
|
||||||
@ -1568,10 +1569,13 @@ gnc_plugin_page_account_tree_cmd_scrub (GtkAction *action, GncPluginPageAccountT
|
|||||||
xaccAccountScrubOrphans (account);
|
xaccAccountScrubOrphans (account);
|
||||||
xaccAccountScrubImbalance (account);
|
xaccAccountScrubImbalance (account);
|
||||||
|
|
||||||
// XXX: Lots are disabled
|
// XXX: Lots/capital gains scrubbing is disabled
|
||||||
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
||||||
xaccAccountScrubLots(account);
|
xaccAccountScrubLots(account);
|
||||||
|
|
||||||
|
gncScrubBusinessAccountLots(account);
|
||||||
|
|
||||||
|
|
||||||
gnc_resume_gui_refresh ();
|
gnc_resume_gui_refresh ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1587,10 +1591,12 @@ gnc_plugin_page_account_tree_cmd_scrub_sub (GtkAction *action, GncPluginPageAcco
|
|||||||
xaccAccountTreeScrubOrphans (account);
|
xaccAccountTreeScrubOrphans (account);
|
||||||
xaccAccountTreeScrubImbalance (account);
|
xaccAccountTreeScrubImbalance (account);
|
||||||
|
|
||||||
// XXX: Lots are disabled
|
// XXX: Lots/capital gains scrubbing is disabled
|
||||||
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
||||||
xaccAccountTreeScrubLots(account);
|
xaccAccountTreeScrubLots(account);
|
||||||
|
|
||||||
|
gncScrubBusinessAccountTreeLots(account);
|
||||||
|
|
||||||
gnc_resume_gui_refresh ();
|
gnc_resume_gui_refresh ();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1603,10 +1609,12 @@ gnc_plugin_page_account_tree_cmd_scrub_all (GtkAction *action, GncPluginPageAcco
|
|||||||
|
|
||||||
xaccAccountTreeScrubOrphans (root);
|
xaccAccountTreeScrubOrphans (root);
|
||||||
xaccAccountTreeScrubImbalance (root);
|
xaccAccountTreeScrubImbalance (root);
|
||||||
// XXX: Lots are disabled
|
// XXX: Lots/capital gains scrubbing is disabled
|
||||||
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
if (g_getenv("GNC_AUTO_SCRUB_LOTS") != NULL)
|
||||||
xaccAccountTreeScrubLots(root);
|
xaccAccountTreeScrubLots(root);
|
||||||
|
|
||||||
|
gncScrubBusinessAccountTreeLots(root);
|
||||||
|
|
||||||
gnc_resume_gui_refresh ();
|
gnc_resume_gui_refresh ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,6 +74,7 @@
|
|||||||
#include "gnucash-sheet.h"
|
#include "gnucash-sheet.h"
|
||||||
#include "dialog-lot-viewer.h"
|
#include "dialog-lot-viewer.h"
|
||||||
#include "Scrub.h"
|
#include "Scrub.h"
|
||||||
|
#include "ScrubBusiness.h"
|
||||||
#include "qof.h"
|
#include "qof.h"
|
||||||
#include "window-reconcile.h"
|
#include "window-reconcile.h"
|
||||||
#include "window-autoclear.h"
|
#include "window-autoclear.h"
|
||||||
@ -3691,6 +3692,8 @@ gnc_plugin_page_register_cmd_scrub_current (GtkAction *action,
|
|||||||
Query *query;
|
Query *query;
|
||||||
Account *root;
|
Account *root;
|
||||||
Transaction *trans;
|
Transaction *trans;
|
||||||
|
Split *split;
|
||||||
|
GNCLot *lot;
|
||||||
SplitRegister *reg;
|
SplitRegister *reg;
|
||||||
|
|
||||||
g_return_if_fail(GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
|
g_return_if_fail(GNC_IS_PLUGIN_PAGE_REGISTER(plugin_page));
|
||||||
@ -3717,6 +3720,11 @@ gnc_plugin_page_register_cmd_scrub_current (GtkAction *action,
|
|||||||
root = gnc_get_current_root_account();
|
root = gnc_get_current_root_account();
|
||||||
xaccTransScrubOrphans(trans);
|
xaccTransScrubOrphans(trans);
|
||||||
xaccTransScrubImbalance(trans, root, NULL);
|
xaccTransScrubImbalance(trans, root, NULL);
|
||||||
|
|
||||||
|
split = gnc_split_register_get_current_split (reg);
|
||||||
|
lot = xaccSplitGetLot (split);
|
||||||
|
if (lot && xaccAccountIsAPARType (xaccAccountGetType (xaccSplitGetAccount (split))))
|
||||||
|
gncScrubBusinessLot (lot);
|
||||||
gnc_resume_gui_refresh();
|
gnc_resume_gui_refresh();
|
||||||
LEAVE(" ");
|
LEAVE(" ");
|
||||||
}
|
}
|
||||||
@ -3729,6 +3737,7 @@ gnc_plugin_page_register_cmd_scrub_all (GtkAction *action,
|
|||||||
Query *query;
|
Query *query;
|
||||||
Account *root;
|
Account *root;
|
||||||
Transaction *trans;
|
Transaction *trans;
|
||||||
|
GNCLot *lot;
|
||||||
Split *split;
|
Split *split;
|
||||||
GList *node;
|
GList *node;
|
||||||
|
|
||||||
@ -3754,6 +3763,10 @@ gnc_plugin_page_register_cmd_scrub_all (GtkAction *action,
|
|||||||
|
|
||||||
xaccTransScrubOrphans(trans);
|
xaccTransScrubOrphans(trans);
|
||||||
xaccTransScrubImbalance(trans, root, NULL);
|
xaccTransScrubImbalance(trans, root, NULL);
|
||||||
|
|
||||||
|
lot = xaccSplitGetLot (split);
|
||||||
|
if (lot && xaccAccountIsAPARType (xaccAccountGetType (xaccSplitGetAccount (split))))
|
||||||
|
gncScrubBusinessLot (lot);
|
||||||
}
|
}
|
||||||
|
|
||||||
gnc_resume_gui_refresh();
|
gnc_resume_gui_refresh();
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
#include "Scrub.h"
|
#include "Scrub.h"
|
||||||
#include "Scrub3.h"
|
|
||||||
#include "dialog-account.h"
|
#include "dialog-account.h"
|
||||||
#include "dialog-transfer.h"
|
#include "dialog-transfer.h"
|
||||||
#include "dialog-utils.h"
|
#include "dialog-utils.h"
|
||||||
|
@ -39,18 +39,6 @@
|
|||||||
#include "split-register-p.h"
|
#include "split-register-p.h"
|
||||||
#include "engine-helpers.h"
|
#include "engine-helpers.h"
|
||||||
|
|
||||||
|
|
||||||
static SplitRegisterColors reg_colors =
|
|
||||||
{
|
|
||||||
0x96B183,
|
|
||||||
0xBFDEB9,
|
|
||||||
0xF6FFDA,
|
|
||||||
0xFFEF98,
|
|
||||||
0xFFEF98,
|
|
||||||
0xEDE7D3,
|
|
||||||
0xFFEF98,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This static indicates the debugging module that this .o belongs to. */
|
/* This static indicates the debugging module that this .o belongs to. */
|
||||||
static QofLogModule log_module = GNC_MOD_LEDGER;
|
static QofLogModule log_module = GNC_MOD_LEDGER;
|
||||||
|
|
||||||
@ -513,116 +501,33 @@ get_trans_total_balance (SplitRegister *reg, Transaction *trans)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static guint32
|
static guint32
|
||||||
gnc_split_register_get_shares_fg_color (VirtualLocation virt_loc,
|
gnc_split_register_get_color_internal (VirtualLocation virt_loc,
|
||||||
gpointer user_data)
|
SplitRegister *reg,
|
||||||
|
const guint32 *color_table,
|
||||||
|
gboolean foreground)
|
||||||
{
|
{
|
||||||
SplitRegister *reg = user_data;
|
|
||||||
const guint32 black = 0x000000;
|
|
||||||
const guint32 red = 0xff0000;
|
|
||||||
const char * cell_name;
|
|
||||||
gboolean is_current;
|
|
||||||
gnc_numeric shares;
|
|
||||||
Split *split;
|
|
||||||
|
|
||||||
if (!use_red_for_negative)
|
|
||||||
return black;
|
|
||||||
|
|
||||||
split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
|
|
||||||
if (!split)
|
|
||||||
return black;
|
|
||||||
|
|
||||||
cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
|
|
||||||
|
|
||||||
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
|
||||||
virt_loc.vcell_loc);
|
|
||||||
|
|
||||||
if (gnc_cell_name_equal (cell_name, TSHRS_CELL))
|
|
||||||
shares = get_trans_total_amount (reg, xaccSplitGetParent (split));
|
|
||||||
else if (is_current)
|
|
||||||
shares = gnc_price_cell_get_value
|
|
||||||
((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
|
|
||||||
SHRS_CELL));
|
|
||||||
else
|
|
||||||
shares = xaccSplitGetAmount (split);
|
|
||||||
|
|
||||||
if (gnc_numeric_negative_p (shares))
|
|
||||||
return red;
|
|
||||||
|
|
||||||
return black;
|
|
||||||
}
|
|
||||||
|
|
||||||
static guint32
|
|
||||||
gnc_split_register_get_balance_fg_color (VirtualLocation virt_loc,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
SplitRegister *reg = user_data;
|
|
||||||
const guint32 black = 0x000000;
|
|
||||||
const guint32 red = 0xff0000;
|
|
||||||
const char * cell_name;
|
|
||||||
gnc_numeric balance;
|
|
||||||
Split *split;
|
|
||||||
|
|
||||||
if (!use_red_for_negative)
|
|
||||||
return black;
|
|
||||||
|
|
||||||
split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
|
|
||||||
if (!split)
|
|
||||||
return black;
|
|
||||||
|
|
||||||
cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
|
|
||||||
|
|
||||||
if (gnc_cell_name_equal (cell_name, BALN_CELL))
|
|
||||||
balance = xaccSplitGetBalance (split);
|
|
||||||
else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
|
|
||||||
balance = gnc_split_register_get_rbaln (virt_loc, user_data, TRUE);
|
|
||||||
else
|
|
||||||
balance = get_trans_total_balance (reg, xaccSplitGetParent (split));
|
|
||||||
|
|
||||||
{
|
|
||||||
Account *account;
|
|
||||||
|
|
||||||
account = xaccSplitGetAccount (split);
|
|
||||||
|
|
||||||
if (gnc_reverse_balance (account))
|
|
||||||
balance = gnc_numeric_neg (balance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gnc_numeric_negative_p (balance))
|
|
||||||
return red;
|
|
||||||
|
|
||||||
return black;
|
|
||||||
}
|
|
||||||
|
|
||||||
static guint32
|
|
||||||
gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
|
||||||
gboolean *hatching,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
SplitRegister *reg = user_data;
|
|
||||||
const char *cursor_name;
|
const char *cursor_name;
|
||||||
VirtualCell *vcell;
|
VirtualCell *vcell;
|
||||||
guint32 bg_color;
|
|
||||||
gboolean is_current;
|
gboolean is_current;
|
||||||
gboolean double_alternate_virt;
|
gboolean double_alternate_virt;
|
||||||
|
guint32 colorbase = 0; /* By default return background colors */
|
||||||
|
|
||||||
if (hatching)
|
if (foreground)
|
||||||
*hatching = FALSE;
|
colorbase = COLOR_UNKNOWN_FG; /* a bit of enum arithmetic */
|
||||||
|
|
||||||
bg_color = 0xffffff; /* white */
|
|
||||||
|
|
||||||
if (!reg)
|
if (!reg)
|
||||||
return bg_color;
|
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||||
|
|
||||||
if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
|
if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
|
||||||
return reg_colors.header_bg_color;
|
return color_table[colorbase + COLOR_HEADER_BG];
|
||||||
|
|
||||||
vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
|
vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
|
||||||
if (!vcell || !vcell->cellblock)
|
if (!vcell || !vcell->cellblock)
|
||||||
return bg_color;
|
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||||
|
|
||||||
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
||||||
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
||||||
return bg_color;
|
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||||
|
|
||||||
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
||||||
virt_loc.vcell_loc);
|
virt_loc.vcell_loc);
|
||||||
@ -634,11 +539,11 @@ gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
|||||||
{
|
{
|
||||||
if (is_current)
|
if (is_current)
|
||||||
return vcell->start_primary_color ?
|
return vcell->start_primary_color ?
|
||||||
reg_colors.primary_active_bg_color :
|
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||||
reg_colors.secondary_active_bg_color;
|
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||||
|
|
||||||
return vcell->start_primary_color ?
|
return vcell->start_primary_color ?
|
||||||
reg_colors.primary_bg_color : reg_colors.secondary_bg_color;
|
color_table[colorbase + COLOR_PRIMARY_BG] : color_table[colorbase + COLOR_SECONDARY_BG];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
|
if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
|
||||||
@ -652,119 +557,135 @@ gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
|||||||
{
|
{
|
||||||
if (double_alternate_virt)
|
if (double_alternate_virt)
|
||||||
return vcell->start_primary_color ?
|
return vcell->start_primary_color ?
|
||||||
reg_colors.primary_active_bg_color :
|
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||||
reg_colors.secondary_active_bg_color;
|
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||||
|
|
||||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
return (virt_loc.phys_row_offset % 2 == 0) ?
|
||||||
reg_colors.primary_active_bg_color :
|
color_table[colorbase + COLOR_PRIMARY_BG_ACTIVE] :
|
||||||
reg_colors.secondary_active_bg_color;
|
color_table[colorbase + COLOR_SECONDARY_BG_ACTIVE];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (double_alternate_virt)
|
if (double_alternate_virt)
|
||||||
return vcell->start_primary_color ?
|
return vcell->start_primary_color ?
|
||||||
reg_colors.primary_bg_color :
|
color_table[colorbase + COLOR_PRIMARY_BG] :
|
||||||
reg_colors.secondary_bg_color;
|
color_table[colorbase + COLOR_SECONDARY_BG];
|
||||||
|
|
||||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
return (virt_loc.phys_row_offset % 2 == 0) ?
|
||||||
reg_colors.primary_bg_color :
|
color_table[colorbase + COLOR_PRIMARY_BG] :
|
||||||
reg_colors.secondary_bg_color;
|
color_table[colorbase + COLOR_SECONDARY_BG];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
|
if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
|
||||||
{
|
{
|
||||||
if (is_current)
|
if (is_current)
|
||||||
return reg_colors.split_active_bg_color;
|
return color_table[colorbase + COLOR_SPLIT_BG_ACTIVE];
|
||||||
|
|
||||||
return reg_colors.split_bg_color;
|
return color_table[colorbase + COLOR_SPLIT_BG];
|
||||||
}
|
}
|
||||||
|
|
||||||
PWARN ("Unexpected cursor: %s\n", cursor_name);
|
PWARN ("Unexpected cursor: %s\n", cursor_name);
|
||||||
|
|
||||||
return bg_color;
|
return color_table[colorbase + COLOR_UNKNOWN_BG];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_split_register_get_fg_color_internal (VirtualLocation virt_loc,
|
||||||
|
SplitRegister *reg,
|
||||||
|
const guint32 *color_table)
|
||||||
|
{
|
||||||
|
const guint32 red_color = color_table[COLOR_NEGATIVE];
|
||||||
|
guint32 fg_color;
|
||||||
|
const char * cell_name;
|
||||||
|
gboolean is_current;
|
||||||
|
gnc_numeric value;
|
||||||
|
Split *split;
|
||||||
|
|
||||||
|
fg_color = gnc_split_register_get_color_internal (virt_loc, reg, color_table, TRUE);
|
||||||
|
|
||||||
|
if (!use_red_for_negative)
|
||||||
|
return fg_color;
|
||||||
|
|
||||||
|
split = gnc_split_register_get_split (reg, virt_loc.vcell_loc);
|
||||||
|
if (!split)
|
||||||
|
return fg_color;
|
||||||
|
|
||||||
|
cell_name = gnc_table_get_cell_name (reg->table, virt_loc);
|
||||||
|
|
||||||
|
if (gnc_cell_name_equal (cell_name, TSHRS_CELL))
|
||||||
|
value = get_trans_total_amount (reg, xaccSplitGetParent (split));
|
||||||
|
else if (gnc_cell_name_equal (cell_name, SHRS_CELL))
|
||||||
|
{
|
||||||
|
if (virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
||||||
|
virt_loc.vcell_loc))
|
||||||
|
value = gnc_price_cell_get_value
|
||||||
|
((PriceCell *) gnc_table_layout_get_cell (reg->table->layout,
|
||||||
|
SHRS_CELL));
|
||||||
|
else
|
||||||
|
value = xaccSplitGetAmount (split);
|
||||||
|
}
|
||||||
|
else if (gnc_cell_name_equal (cell_name, BALN_CELL))
|
||||||
|
value = xaccSplitGetBalance (split);
|
||||||
|
else if (gnc_cell_name_equal (cell_name, RBALN_CELL))
|
||||||
|
value = gnc_split_register_get_rbaln (virt_loc, reg, TRUE);
|
||||||
|
else if (gnc_cell_name_equal (cell_name, TBALN_CELL))
|
||||||
|
value = get_trans_total_balance (reg, xaccSplitGetParent (split));
|
||||||
|
|
||||||
|
if ((gnc_cell_name_equal (cell_name, BALN_CELL)) ||
|
||||||
|
(gnc_cell_name_equal (cell_name, RBALN_CELL)) ||
|
||||||
|
(gnc_cell_name_equal (cell_name, TBALN_CELL)))
|
||||||
|
{
|
||||||
|
Account *account = xaccSplitGetAccount (split);
|
||||||
|
if (gnc_reverse_balance (account))
|
||||||
|
value = gnc_numeric_neg (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gnc_numeric_negative_p (value))
|
||||||
|
return red_color;
|
||||||
|
|
||||||
|
return fg_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_split_register_get_fg_color (VirtualLocation virt_loc,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
SplitRegister *reg = user_data;
|
||||||
|
return gnc_split_register_get_fg_color_internal (virt_loc, reg, reg_colors_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_split_register_get_gtkrc_fg_color (VirtualLocation virt_loc,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
SplitRegister *reg = user_data;
|
||||||
|
return gnc_split_register_get_fg_color_internal (virt_loc, reg, reg_colors_gtkrc);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_split_register_get_bg_color (VirtualLocation virt_loc,
|
||||||
|
gboolean *hatching,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
SplitRegister *reg = user_data;
|
||||||
|
|
||||||
|
if (hatching)
|
||||||
|
*hatching = FALSE;
|
||||||
|
|
||||||
|
return gnc_split_register_get_color_internal (virt_loc, reg, reg_colors_default, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static RegisterColor
|
static RegisterColor
|
||||||
gnc_split_register_get_gtkrc_bg_color (VirtualLocation virt_loc,
|
gnc_split_register_get_gtkrc_bg_color (VirtualLocation virt_loc,
|
||||||
gboolean *hatching,
|
gboolean *hatching,
|
||||||
gpointer user_data)
|
gpointer user_data)
|
||||||
{
|
{
|
||||||
SplitRegister *reg = user_data;
|
SplitRegister *reg = user_data;
|
||||||
const char *cursor_name;
|
|
||||||
VirtualCell *vcell;
|
|
||||||
gboolean is_current;
|
|
||||||
gboolean double_alternate_virt;
|
|
||||||
|
|
||||||
if (!reg)
|
if (hatching)
|
||||||
return COLOR_UNKNOWN;
|
*hatching = FALSE;
|
||||||
|
|
||||||
if (gnc_table_virtual_location_in_header (reg->table, virt_loc))
|
return gnc_split_register_get_color_internal (virt_loc, reg, reg_colors_gtkrc, FALSE);
|
||||||
return COLOR_HEADER;
|
|
||||||
|
|
||||||
vcell = gnc_table_get_virtual_cell (reg->table, virt_loc.vcell_loc);
|
|
||||||
if (!vcell || !vcell->cellblock)
|
|
||||||
return COLOR_UNKNOWN;
|
|
||||||
|
|
||||||
if ((virt_loc.phys_col_offset < vcell->cellblock->start_col) ||
|
|
||||||
(virt_loc.phys_col_offset > vcell->cellblock->stop_col))
|
|
||||||
return COLOR_UNKNOWN;
|
|
||||||
|
|
||||||
is_current = virt_cell_loc_equal (reg->table->current_cursor_loc.vcell_loc,
|
|
||||||
virt_loc.vcell_loc);
|
|
||||||
|
|
||||||
cursor_name = vcell->cellblock->cursor_name;
|
|
||||||
|
|
||||||
if (g_strcmp0 (cursor_name, CURSOR_SINGLE_JOURNAL) == 0 ||
|
|
||||||
g_strcmp0 (cursor_name, CURSOR_SINGLE_LEDGER) == 0)
|
|
||||||
{
|
|
||||||
if (is_current)
|
|
||||||
return vcell->start_primary_color ?
|
|
||||||
COLOR_PRIMARY_ACTIVE :
|
|
||||||
COLOR_SECONDARY_ACTIVE;
|
|
||||||
|
|
||||||
return vcell->start_primary_color ?
|
|
||||||
COLOR_PRIMARY : COLOR_SECONDARY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL) == 0 ||
|
|
||||||
g_strcmp0 (cursor_name, CURSOR_DOUBLE_JOURNAL_NUM_ACTN) == 0 ||
|
|
||||||
g_strcmp0 (cursor_name, CURSOR_DOUBLE_LEDGER) == 0 ||
|
|
||||||
g_strcmp0 (cursor_name, CURSOR_DOUBLE_LEDGER_NUM_ACTN) == 0)
|
|
||||||
{
|
|
||||||
double_alternate_virt = gnc_prefs_get_bool (GNC_PREFS_GROUP_GENERAL_REGISTER,
|
|
||||||
GNC_PREF_ALT_COLOR_BY_TRANS);
|
|
||||||
if (is_current)
|
|
||||||
{
|
|
||||||
if (double_alternate_virt)
|
|
||||||
return vcell->start_primary_color ?
|
|
||||||
COLOR_PRIMARY_ACTIVE :
|
|
||||||
COLOR_SECONDARY_ACTIVE;
|
|
||||||
|
|
||||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
|
||||||
COLOR_PRIMARY_ACTIVE :
|
|
||||||
COLOR_SECONDARY_ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (double_alternate_virt)
|
|
||||||
return vcell->start_primary_color ?
|
|
||||||
COLOR_PRIMARY :
|
|
||||||
COLOR_SECONDARY;
|
|
||||||
|
|
||||||
return (virt_loc.phys_row_offset % 2 == 0) ?
|
|
||||||
COLOR_PRIMARY :
|
|
||||||
COLOR_SECONDARY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g_strcmp0 (cursor_name, CURSOR_SPLIT) == 0)
|
|
||||||
{
|
|
||||||
if (is_current)
|
|
||||||
return COLOR_SPLIT_ACTIVE;
|
|
||||||
|
|
||||||
return COLOR_SPLIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
PWARN ("Unexpected cursor: %s\n", cursor_name);
|
|
||||||
|
|
||||||
return COLOR_UNKNOWN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint32
|
static guint32
|
||||||
@ -2662,19 +2583,22 @@ gnc_split_register_model_new (void)
|
|||||||
|
|
||||||
|
|
||||||
gnc_table_model_set_fg_color_handler(
|
gnc_table_model_set_fg_color_handler(
|
||||||
model, gnc_split_register_get_shares_fg_color, SHRS_CELL);
|
model, gnc_split_register_get_fg_color, SHRS_CELL);
|
||||||
|
|
||||||
gnc_table_model_set_fg_color_handler(
|
gnc_table_model_set_fg_color_handler(
|
||||||
model, gnc_split_register_get_shares_fg_color, TSHRS_CELL);
|
model, gnc_split_register_get_fg_color, TSHRS_CELL);
|
||||||
|
|
||||||
gnc_table_model_set_fg_color_handler(
|
gnc_table_model_set_fg_color_handler(
|
||||||
model, gnc_split_register_get_balance_fg_color, BALN_CELL);
|
model, gnc_split_register_get_fg_color, BALN_CELL);
|
||||||
|
|
||||||
gnc_table_model_set_fg_color_handler(
|
gnc_table_model_set_fg_color_handler(
|
||||||
model, gnc_split_register_get_balance_fg_color, TBALN_CELL);
|
model, gnc_split_register_get_fg_color, TBALN_CELL);
|
||||||
|
|
||||||
gnc_table_model_set_fg_color_handler(
|
gnc_table_model_set_fg_color_handler(
|
||||||
model, gnc_split_register_get_balance_fg_color, RBALN_CELL);
|
model, gnc_split_register_get_fg_color, RBALN_CELL);
|
||||||
|
|
||||||
|
gnc_table_model_set_fg_color_handler(
|
||||||
|
model, gnc_split_register_get_gtkrc_fg_color, "gtkrc");
|
||||||
|
|
||||||
|
|
||||||
gnc_table_model_set_default_bg_color_handler(
|
gnc_table_model_set_default_bg_color_handler(
|
||||||
|
@ -28,16 +28,4 @@
|
|||||||
TableModel * gnc_split_register_model_new (void);
|
TableModel * gnc_split_register_model_new (void);
|
||||||
TableModel * gnc_template_register_model_new (void);
|
TableModel * gnc_template_register_model_new (void);
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
COLOR_UNKNOWN,
|
|
||||||
COLOR_HEADER,
|
|
||||||
COLOR_PRIMARY,
|
|
||||||
COLOR_PRIMARY_ACTIVE,
|
|
||||||
COLOR_SECONDARY,
|
|
||||||
COLOR_SECONDARY_ACTIVE,
|
|
||||||
COLOR_SPLIT,
|
|
||||||
COLOR_SPLIT_ACTIVE,
|
|
||||||
} RegisterColor;
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -238,20 +238,6 @@ typedef enum
|
|||||||
NUM_CURSOR_CLASSES
|
NUM_CURSOR_CLASSES
|
||||||
} CursorClass;
|
} CursorClass;
|
||||||
|
|
||||||
typedef struct split_register_colors
|
|
||||||
{
|
|
||||||
guint32 header_bg_color;
|
|
||||||
|
|
||||||
guint32 primary_bg_color;
|
|
||||||
guint32 secondary_bg_color;
|
|
||||||
|
|
||||||
guint32 primary_active_bg_color;
|
|
||||||
guint32 secondary_active_bg_color;
|
|
||||||
|
|
||||||
guint32 split_bg_color;
|
|
||||||
guint32 split_active_bg_color;
|
|
||||||
} SplitRegisterColors;
|
|
||||||
|
|
||||||
|
|
||||||
/** @brief A split register created with ::gnc_split_register_new */
|
/** @brief A split register created with ::gnc_split_register_new */
|
||||||
typedef struct split_register SplitRegister;
|
typedef struct split_register SplitRegister;
|
||||||
@ -270,7 +256,7 @@ struct split_register
|
|||||||
or split action for number
|
or split action for number
|
||||||
field in register */
|
field in register */
|
||||||
gboolean is_template;
|
gboolean is_template;
|
||||||
gboolean do_auto_complete; /**< whether to use auto-competion */
|
gboolean do_auto_complete; /**< whether to use auto-completion */
|
||||||
|
|
||||||
SRInfo * sr_info; /**< private data; outsiders should not access this */
|
SRInfo * sr_info; /**< private data; outsiders should not access this */
|
||||||
};
|
};
|
||||||
|
@ -345,19 +345,21 @@ gnc_table_get_label (Table *table, VirtualLocation virt_loc)
|
|||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
guint32
|
static guint32
|
||||||
gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
|
gnc_table_get_fg_color_internal (Table *table, VirtualLocation virt_loc,
|
||||||
|
gboolean want_gtkrc)
|
||||||
{
|
{
|
||||||
TableGetFGColorHandler fg_color_handler;
|
TableGetFGColorHandler fg_color_handler;
|
||||||
const char *cell_name;
|
const char *handler_name = "gtkrc";
|
||||||
|
|
||||||
if (!table || !table->model)
|
if (!table || !table->model)
|
||||||
return 0x0; /* black */
|
return 0x0; /* black */
|
||||||
|
|
||||||
cell_name = gnc_table_get_cell_name (table, virt_loc);
|
if (!want_gtkrc)
|
||||||
|
handler_name = gnc_table_get_cell_name (table, virt_loc);
|
||||||
|
|
||||||
fg_color_handler = gnc_table_model_get_fg_color_handler (table->model,
|
fg_color_handler = gnc_table_model_get_fg_color_handler (table->model,
|
||||||
cell_name);
|
handler_name);
|
||||||
if (!fg_color_handler)
|
if (!fg_color_handler)
|
||||||
return 0x0;
|
return 0x0;
|
||||||
|
|
||||||
@ -365,11 +367,24 @@ gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
guint32
|
guint32
|
||||||
gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc)
|
||||||
gboolean *hatching)
|
{
|
||||||
|
return gnc_table_get_fg_color_internal (table, virt_loc, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
guint32
|
||||||
|
gnc_table_get_gtkrc_fg_color (Table *table, VirtualLocation virt_loc)
|
||||||
|
{
|
||||||
|
return gnc_table_get_fg_color_internal (table, virt_loc, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
gnc_table_get_bg_color_internal (Table *table, VirtualLocation virt_loc,
|
||||||
|
gboolean *hatching,
|
||||||
|
gboolean want_gtkrc)
|
||||||
{
|
{
|
||||||
TableGetBGColorHandler bg_color_handler;
|
TableGetBGColorHandler bg_color_handler;
|
||||||
const char *cell_name;
|
const char *handler_name = "gtkrc";
|
||||||
|
|
||||||
if (hatching)
|
if (hatching)
|
||||||
*hatching = FALSE;
|
*hatching = FALSE;
|
||||||
@ -377,10 +392,11 @@ gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
|||||||
if (!table || !table->model)
|
if (!table || !table->model)
|
||||||
return 0xffffff; /* white */
|
return 0xffffff; /* white */
|
||||||
|
|
||||||
cell_name = gnc_table_get_cell_name (table, virt_loc);
|
if (!want_gtkrc)
|
||||||
|
handler_name = gnc_table_get_cell_name (table, virt_loc);
|
||||||
|
|
||||||
bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
|
bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
|
||||||
cell_name);
|
handler_name);
|
||||||
if (!bg_color_handler)
|
if (!bg_color_handler)
|
||||||
return 0xffffff;
|
return 0xffffff;
|
||||||
|
|
||||||
@ -388,25 +404,18 @@ gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
|||||||
table->model->handler_user_data);
|
table->model->handler_user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
guint32
|
||||||
|
gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
||||||
|
gboolean *hatching)
|
||||||
|
{
|
||||||
|
return gnc_table_get_bg_color_internal (table, virt_loc, hatching, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
guint32
|
guint32
|
||||||
gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
|
gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
|
||||||
gboolean *hatching)
|
gboolean *hatching)
|
||||||
{
|
{
|
||||||
TableGetBGColorHandler bg_color_handler;
|
return gnc_table_get_bg_color_internal (table, virt_loc, hatching, TRUE);
|
||||||
|
|
||||||
if (hatching)
|
|
||||||
*hatching = FALSE;
|
|
||||||
|
|
||||||
if (!table || !table->model)
|
|
||||||
return 0xffffff; /* white */
|
|
||||||
|
|
||||||
bg_color_handler = gnc_table_model_get_bg_color_handler (table->model,
|
|
||||||
"gtkrc");
|
|
||||||
if (!bg_color_handler)
|
|
||||||
return 0xffffff;
|
|
||||||
|
|
||||||
return bg_color_handler (virt_loc, hatching,
|
|
||||||
table->model->handler_user_data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -156,6 +156,92 @@ struct table
|
|||||||
gpointer ui_data;
|
gpointer ui_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Color definitions used for table elements */
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
/* Colors used for background drawing */
|
||||||
|
COLOR_UNKNOWN_BG,
|
||||||
|
COLOR_HEADER_BG,
|
||||||
|
COLOR_PRIMARY_BG,
|
||||||
|
COLOR_PRIMARY_BG_ACTIVE,
|
||||||
|
COLOR_SECONDARY_BG,
|
||||||
|
COLOR_SECONDARY_BG_ACTIVE,
|
||||||
|
COLOR_SPLIT_BG,
|
||||||
|
COLOR_SPLIT_BG_ACTIVE,
|
||||||
|
|
||||||
|
/* Colors used for foreground drawing (text etc)
|
||||||
|
* ATTENTION: the background and foreground lists should have
|
||||||
|
* the same types (the same amount of entries) !
|
||||||
|
* The code relies on this ! */
|
||||||
|
COLOR_UNKNOWN_FG,
|
||||||
|
COLOR_HEADER_FG,
|
||||||
|
COLOR_PRIMARY_FG,
|
||||||
|
COLOR_PRIMARY_FG_ACTIVE,
|
||||||
|
COLOR_SECONDARY_FG,
|
||||||
|
COLOR_SECONDARY_FG_ACTIVE,
|
||||||
|
COLOR_SPLIT_FG,
|
||||||
|
COLOR_SPLIT_FG_ACTIVE,
|
||||||
|
|
||||||
|
/* Other colors */
|
||||||
|
COLOR_NEGATIVE, /* Color to use for negative numbers */
|
||||||
|
} RegisterColor;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Alternative color tables to use for the register.
|
||||||
|
* The colors in this array are ordered according to the RegisterColor Enum
|
||||||
|
* Be careful to respect this order !
|
||||||
|
*/
|
||||||
|
static const guint32 reg_colors_default [] =
|
||||||
|
{
|
||||||
|
0xFFFFFF, // COLOR_UNKNOWN_BG
|
||||||
|
0x96B183, // COLOR_HEADER_BG
|
||||||
|
0xBFDEB9, // COLOR_PRIMARY_BG
|
||||||
|
0xFFEF98, // COLOR_PRIMARY_BG_ACTIVE
|
||||||
|
0xF6FFDA, // COLOR_SECONDARY_BG
|
||||||
|
0xFFEF98, // COLOR_SECONDARY_BG_ACTIVE
|
||||||
|
0xEDE7D3, // COLOR_SPLIT_BG
|
||||||
|
0xFFEF98, // COLOR_SPLIT_BG_ACTIVE
|
||||||
|
|
||||||
|
0x000000, // COLOR_UNKNOWN_FG
|
||||||
|
0x000000, // COLOR_HEADER_FG
|
||||||
|
0x000000, // COLOR_PRIMARY_FG
|
||||||
|
0x000000, // COLOR_PRIMARY_FG_ACTIVE
|
||||||
|
0x000000, // COLOR_SECONDARY_FG
|
||||||
|
0x000000, // COLOR_SECONDARY_FG_ACTIVE
|
||||||
|
0x000000, // COLOR_SPLIT_FG
|
||||||
|
0x000000, // COLOR_SPLIT_FG_ACTIVE
|
||||||
|
|
||||||
|
0xFF0000, // COLOR_NEGATIVE
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The colors in this array are ordered according to the RegisterColor Enum
|
||||||
|
* Be careful to respect this order !
|
||||||
|
*/
|
||||||
|
static const guint32 reg_colors_gtkrc [] =
|
||||||
|
{
|
||||||
|
COLOR_UNKNOWN_BG, // COLOR_UNKNOWN_BG
|
||||||
|
COLOR_HEADER_BG, // COLOR_HEADER_BG
|
||||||
|
COLOR_PRIMARY_BG, // COLOR_PRIMARY_BG
|
||||||
|
COLOR_PRIMARY_BG_ACTIVE, // COLOR_PRIMARY_BG_ACTIVE
|
||||||
|
COLOR_SECONDARY_BG, // COLOR_SECONDARY_BG
|
||||||
|
COLOR_SECONDARY_BG_ACTIVE, // COLOR_SECONDARY_BG_ACTIVE
|
||||||
|
COLOR_SPLIT_BG, // COLOR_SPLIT_BG
|
||||||
|
COLOR_SPLIT_BG_ACTIVE, // COLOR_SPLIT_BG_ACTIVE
|
||||||
|
|
||||||
|
COLOR_UNKNOWN_FG, // COLOR_UNKNOWN_FG
|
||||||
|
COLOR_HEADER_FG, // COLOR_HEADER_FG
|
||||||
|
COLOR_PRIMARY_FG, // COLOR_PRIMARY_FG
|
||||||
|
COLOR_PRIMARY_FG_ACTIVE, // COLOR_PRIMARY_FG_ACTIVE
|
||||||
|
COLOR_SECONDARY_FG, // COLOR_SECONDARY_FG
|
||||||
|
COLOR_SECONDARY_FG_ACTIVE, // COLOR_SECONDARY_FG_ACTIVE
|
||||||
|
COLOR_SPLIT_FG, // COLOR_SPLIT_FG
|
||||||
|
COLOR_SPLIT_FG_ACTIVE, // COLOR_SPLIT_FG_ACTIVE
|
||||||
|
|
||||||
|
COLOR_NEGATIVE, // COLOR_NEGATIVE
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/* Set the default gui handlers used by new tables. */
|
/* Set the default gui handlers used by new tables. */
|
||||||
void gnc_table_set_default_gui_handlers (TableGUIHandlers *gui_handlers);
|
void gnc_table_set_default_gui_handlers (TableGUIHandlers *gui_handlers);
|
||||||
@ -210,6 +296,8 @@ CellIOFlags gnc_table_get_io_flags (Table *table, VirtualLocation virt_loc);
|
|||||||
|
|
||||||
guint32 gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc);
|
guint32 gnc_table_get_fg_color (Table *table, VirtualLocation virt_loc);
|
||||||
|
|
||||||
|
guint32 gnc_table_get_gtkrc_fg_color (Table *table, VirtualLocation virt_loc);
|
||||||
|
|
||||||
guint32 gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
guint32 gnc_table_get_bg_color (Table *table, VirtualLocation virt_loc,
|
||||||
gboolean *hatching);
|
gboolean *hatching);
|
||||||
guint32 gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
|
guint32 gnc_table_get_gtkrc_bg_color (Table *table, VirtualLocation virt_loc,
|
||||||
|
@ -605,17 +605,25 @@ draw_cell (GnucashGrid *grid,
|
|||||||
context = pango_layout_get_context (layout);
|
context = pango_layout_get_context (layout);
|
||||||
font = pango_font_description_copy (pango_context_get_font_description (context));
|
font = pango_font_description_copy (pango_context_get_font_description (context));
|
||||||
|
|
||||||
argb = gnc_table_get_fg_color (table, virt_loc);
|
if (grid->sheet->use_theme_colors)
|
||||||
#ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
|
|
||||||
// Are we in a read-only row? Then make the foreground color somewhat less black
|
|
||||||
if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
|
|
||||||
&& (table->model->dividing_row_upper >= 0)
|
|
||||||
&& (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
|
|
||||||
{
|
{
|
||||||
argb = inc_intensity_10percent(argb);
|
color_type = gnc_table_get_gtkrc_fg_color (table, virt_loc);
|
||||||
|
fg_color = get_gtkrc_color(grid->sheet, color_type);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
argb = gnc_table_get_fg_color (table, virt_loc);
|
||||||
|
#ifdef READONLY_LINES_WITH_CHANGED_FG_COLOR
|
||||||
|
// Are we in a read-only row? Then make the foreground color somewhat less black
|
||||||
|
if ((virt_loc.phys_row_offset == (block->style->nrows - 1))
|
||||||
|
&& (table->model->dividing_row_upper >= 0)
|
||||||
|
&& (virt_loc.vcell_loc.virt_row < table->model->dividing_row_upper))
|
||||||
|
{
|
||||||
|
argb = inc_intensity_10percent(argb);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
fg_color = gnucash_color_argb_to_gdk (argb);
|
fg_color = gnucash_color_argb_to_gdk (argb);
|
||||||
|
}
|
||||||
|
|
||||||
gdk_gc_set_foreground (grid->gc, fg_color);
|
gdk_gc_set_foreground (grid->gc, fg_color);
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
|||||||
VirtualLocation virt_loc;
|
VirtualLocation virt_loc;
|
||||||
VirtualCell *vcell;
|
VirtualCell *vcell;
|
||||||
CellDimensions *cd;
|
CellDimensions *cd;
|
||||||
GdkColor *bg_color;
|
GdkColor *bg_color, *fg_color;
|
||||||
int xpaint, ypaint;
|
int xpaint, ypaint;
|
||||||
const char *text;
|
const char *text;
|
||||||
CellBlock *cb;
|
CellBlock *cb;
|
||||||
@ -94,11 +94,15 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
|||||||
color_type = gnc_table_get_gtkrc_bg_color (table, virt_loc,
|
color_type = gnc_table_get_gtkrc_bg_color (table, virt_loc,
|
||||||
NULL);
|
NULL);
|
||||||
bg_color = get_gtkrc_color(header->sheet, color_type);
|
bg_color = get_gtkrc_color(header->sheet, color_type);
|
||||||
|
color_type = gnc_table_get_gtkrc_fg_color (table, virt_loc);
|
||||||
|
fg_color = get_gtkrc_color(header->sheet, color_type);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
argb = gnc_table_get_bg_color (table, virt_loc, NULL);
|
argb = gnc_table_get_bg_color (table, virt_loc, NULL);
|
||||||
bg_color = gnucash_color_argb_to_gdk (argb);
|
bg_color = gnucash_color_argb_to_gdk (argb);
|
||||||
|
argb = gnc_table_get_fg_color (table, virt_loc);
|
||||||
|
fg_color = gnucash_color_argb_to_gdk (argb);
|
||||||
}
|
}
|
||||||
|
|
||||||
h = style->dimensions->height;
|
h = style->dimensions->height;
|
||||||
@ -111,7 +115,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
|||||||
style->dimensions->width, h);
|
style->dimensions->width, h);
|
||||||
|
|
||||||
gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
|
gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
|
||||||
gdk_gc_set_foreground (header->gc, &gn_black);
|
gdk_gc_set_foreground (header->gc, fg_color);
|
||||||
|
|
||||||
gdk_draw_rectangle (drawable, header->gc, FALSE, -x, -y,
|
gdk_draw_rectangle (drawable, header->gc, FALSE, -x, -y,
|
||||||
style->dimensions->width, h);
|
style->dimensions->width, h);
|
||||||
@ -121,7 +125,7 @@ gnc_header_draw (GnomeCanvasItem *item, GdkDrawable *drawable,
|
|||||||
|
|
||||||
gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
|
gdk_gc_set_line_attributes (header->gc, 1, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
|
||||||
gdk_gc_set_background (header->gc, &gn_white);
|
gdk_gc_set_background (header->gc, &gn_white);
|
||||||
gdk_gc_set_foreground (header->gc, &gn_black);
|
gdk_gc_set_foreground (header->gc, fg_color);
|
||||||
/*font = gnucash_register_font;*/
|
/*font = gnucash_register_font;*/
|
||||||
|
|
||||||
vcell = gnc_table_get_virtual_cell
|
vcell = gnc_table_get_virtual_cell
|
||||||
|
@ -196,16 +196,20 @@ gnc_item_edit_draw_info (GncItemEdit *item_edit, int x, int y, TextDrawInfo *inf
|
|||||||
item_edit->virt_loc,
|
item_edit->virt_loc,
|
||||||
&hatching);
|
&hatching);
|
||||||
info->bg_color = get_gtkrc_color(item_edit->sheet, color_type);
|
info->bg_color = get_gtkrc_color(item_edit->sheet, color_type);
|
||||||
|
color_type = gnc_table_get_gtkrc_fg_color (table,
|
||||||
|
item_edit->virt_loc);
|
||||||
|
info->fg_color = get_gtkrc_color(item_edit->sheet, color_type);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
argb = gnc_table_get_bg_color (table, item_edit->virt_loc,
|
argb = gnc_table_get_bg_color (table, item_edit->virt_loc,
|
||||||
&hatching);
|
&hatching);
|
||||||
info->bg_color = gnucash_color_argb_to_gdk (argb);
|
info->bg_color = gnucash_color_argb_to_gdk (argb);
|
||||||
|
argb = gnc_table_get_fg_color (table, item_edit->virt_loc);
|
||||||
|
info->fg_color = gnucash_color_argb_to_gdk (argb);
|
||||||
}
|
}
|
||||||
|
|
||||||
info->hatching = hatching;
|
info->hatching = hatching;
|
||||||
info->fg_color = &gn_black;
|
|
||||||
|
|
||||||
info->bg_color2 = &gn_dark_gray;
|
info->bg_color2 = &gn_dark_gray;
|
||||||
info->fg_color2 = &gn_white;
|
info->fg_color2 = &gn_white;
|
||||||
|
@ -2609,31 +2609,49 @@ get_gtkrc_color (GnucashSheet *sheet,
|
|||||||
{
|
{
|
||||||
GtkWidget *widget = NULL;
|
GtkWidget *widget = NULL;
|
||||||
GtkStyle *style;
|
GtkStyle *style;
|
||||||
GdkColor *white;
|
GdkColor *white, *black, *red;
|
||||||
GdkColor *color = NULL;
|
GdkColor *color = NULL;
|
||||||
|
|
||||||
white = gnucash_color_argb_to_gdk (0xFFFFFF);
|
white = gnucash_color_argb_to_gdk (0xFFFFFF);
|
||||||
|
black = gnucash_color_argb_to_gdk (0x000000);
|
||||||
|
red = gnucash_color_argb_to_gdk (0xFF0000); /* Hardcoded...*/
|
||||||
switch (field_type)
|
switch (field_type)
|
||||||
{
|
{
|
||||||
default:
|
default:
|
||||||
return white;
|
return white;
|
||||||
|
|
||||||
case COLOR_HEADER:
|
case COLOR_UNKNOWN_BG:
|
||||||
|
return white;
|
||||||
|
|
||||||
|
case COLOR_UNKNOWN_FG:
|
||||||
|
return black;
|
||||||
|
|
||||||
|
case COLOR_NEGATIVE:
|
||||||
|
return red;
|
||||||
|
|
||||||
|
case COLOR_HEADER_BG:
|
||||||
|
case COLOR_HEADER_FG:
|
||||||
widget = sheet->header_color;
|
widget = sheet->header_color;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_PRIMARY:
|
case COLOR_PRIMARY_BG:
|
||||||
case COLOR_PRIMARY_ACTIVE:
|
case COLOR_PRIMARY_BG_ACTIVE:
|
||||||
|
case COLOR_PRIMARY_FG:
|
||||||
|
case COLOR_PRIMARY_FG_ACTIVE:
|
||||||
widget = sheet->primary_color;
|
widget = sheet->primary_color;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_SECONDARY:
|
case COLOR_SECONDARY_BG:
|
||||||
case COLOR_SECONDARY_ACTIVE:
|
case COLOR_SECONDARY_BG_ACTIVE:
|
||||||
|
case COLOR_SECONDARY_FG:
|
||||||
|
case COLOR_SECONDARY_FG_ACTIVE:
|
||||||
widget = sheet->secondary_color;
|
widget = sheet->secondary_color;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_SPLIT:
|
case COLOR_SPLIT_BG:
|
||||||
case COLOR_SPLIT_ACTIVE:
|
case COLOR_SPLIT_BG_ACTIVE:
|
||||||
|
case COLOR_SPLIT_FG:
|
||||||
|
case COLOR_SPLIT_FG_ACTIVE:
|
||||||
widget = sheet->split_color;
|
widget = sheet->split_color;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2647,18 +2665,31 @@ get_gtkrc_color (GnucashSheet *sheet,
|
|||||||
default:
|
default:
|
||||||
return white;
|
return white;
|
||||||
|
|
||||||
case COLOR_HEADER:
|
case COLOR_HEADER_BG:
|
||||||
case COLOR_PRIMARY:
|
case COLOR_PRIMARY_BG:
|
||||||
case COLOR_SECONDARY:
|
case COLOR_SECONDARY_BG:
|
||||||
case COLOR_SPLIT:
|
case COLOR_SPLIT_BG:
|
||||||
color = &style->base[GTK_STATE_NORMAL];
|
color = &style->base[GTK_STATE_NORMAL];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_PRIMARY_ACTIVE:
|
case COLOR_PRIMARY_BG_ACTIVE:
|
||||||
case COLOR_SECONDARY_ACTIVE:
|
case COLOR_SECONDARY_BG_ACTIVE:
|
||||||
case COLOR_SPLIT_ACTIVE:
|
case COLOR_SPLIT_BG_ACTIVE:
|
||||||
color = &style->base[GTK_STATE_SELECTED];
|
color = &style->base[GTK_STATE_SELECTED];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case COLOR_HEADER_FG:
|
||||||
|
case COLOR_PRIMARY_FG:
|
||||||
|
case COLOR_SECONDARY_FG:
|
||||||
|
case COLOR_SPLIT_FG:
|
||||||
|
color = &style->text[GTK_STATE_NORMAL];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case COLOR_PRIMARY_FG_ACTIVE:
|
||||||
|
case COLOR_SECONDARY_FG_ACTIVE:
|
||||||
|
case COLOR_SPLIT_FG_ACTIVE:
|
||||||
|
color = &style->text[GTK_STATE_SELECTED];
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
gnucash_color_alloc_gdk(color);
|
gnucash_color_alloc_gdk(color);
|
||||||
|
Loading…
Reference in New Issue
Block a user