Files
gnucash/src/business/business-ledger/gncEntryLedger.c
Derek Atkins 5165819059 * modify the Entry Ledger query to pull in Billable Entries from
Posted Bills that belong to an Invoice's Owner.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@7114 57a11ea4-9604-0410-9ed3-97b8803252fd
2002-07-10 21:12:35 +00:00

726 lines
18 KiB
C

/*
* gncEntryLedger.c -- a Ledger widget for entering GncEntry objects
* Copyright (C) 2001, 2002 Derek Atkins
* Author: Derek Atkins <warlord@MIT.EDU>
*/
#define _GNU_SOURCE
#include "config.h"
#include <glib.h>
#include "Account.h"
#include "gnc-ui-util.h"
#include "combocell.h"
#include "pricecell.h"
#include "recncell.h"
#include "messages.h"
#include "gnc-component-manager.h"
#include "gnc-ui.h"
#include "gncEntry.h"
#include "gncEntryLedger.h"
#include "gncEntryLedgerP.h"
#include "gncEntryLedgerLayout.h"
#include "gncEntryLedgerModel.h"
#include "gncEntryLedgerControl.h"
/** Static Functions ***************************************************/
static void
gnc_entry_ledger_clear_blank_entry (GncEntryLedger *ledger)
{
GncEntry *entry;
if (!ledger) return;
entry = gnc_entry_ledger_get_blank_entry (ledger);
if (entry)
gncEntryDestroy (entry);
ledger->blank_entry_guid = *xaccGUIDNULL ();
ledger->blank_entry_edited = FALSE;
}
/** Exported Functions ***************************************************/
GncEntry *
gnc_entry_ledger_get_blank_entry (GncEntryLedger *ledger)
{
if (!ledger) return NULL;
return gncEntryLookup (ledger->book, &(ledger->blank_entry_guid));
}
Account * gnc_entry_ledger_get_account (GncEntryLedger *ledger,
const char * cell_name)
{
const char * name =
gnc_table_layout_get_cell_value (ledger->table->layout, cell_name);
return xaccGetAccountFromFullName (gnc_book_get_group (ledger->book),
name,
gnc_get_account_separator ());
}
GncTaxTable * gnc_entry_ledger_get_taxtable (GncEntryLedger *ledger,
const char *cell_name)
{
const char * name =
gnc_table_layout_get_cell_value (ledger->table->layout, cell_name);
return gncTaxTableLookupByName (ledger->book, name);
}
gboolean gnc_entry_ledger_get_checkmark (GncEntryLedger *ledger,
const char * cell_name)
{
const char *value =
gnc_table_layout_get_cell_value (ledger->table->layout, cell_name);
if (!value || *value == '\0')
return FALSE;
switch (*value) {
case 'X':
return TRUE;
case ' ':
return FALSE;
default:
g_warning ("Invalid checkmark character: %d", *value);
return FALSE;
}
}
char gnc_entry_ledger_get_inv (GncEntryLedger *ledger, const char * cell_name)
{
const char *value =
gnc_table_layout_get_cell_value (ledger->table->layout, cell_name);
if (value)
return *value;
return '\0';
}
gint gnc_entry_ledger_get_type (GncEntryLedger *ledger, const char * cell_name)
{
RecnCell *cell =
(RecnCell *) gnc_table_layout_get_cell (ledger->table->layout, cell_name);
if (!cell)
return -1;
return (gnc_recn_cell_get_flag (cell) - '0');
}
/* Return TRUE if value is valid, return FALSE if invalid */
gboolean gnc_entry_ledger_get_numeric (GncEntryLedger *ledger,
const char *cell_name,
gnc_numeric *value)
{
PriceCell *cell =
(PriceCell *) gnc_table_layout_get_cell (ledger->table->layout, cell_name);
if (!value || !cell)
return FALSE;
*value = gnc_price_cell_get_value (cell);
return TRUE;
}
GncEntry * gnc_entry_ledger_get_entry (GncEntryLedger *ledger,
VirtualCellLocation vcell_loc)
{
GUID *guid;
if (!ledger) return NULL;
guid = gnc_table_get_vcell_data (ledger->table, vcell_loc);
if (!guid) return NULL;
return gncEntryLookup (ledger->book, guid);
}
/* Returns the Entry where the cursor is currently located. */
GncEntry * gnc_entry_ledger_get_current_entry (GncEntryLedger *ledger)
{
if (!ledger) return NULL;
return
gnc_entry_ledger_get_entry (ledger,
ledger->table->current_cursor_loc.vcell_loc);
}
static void gnc_entry_ledger_config_action (GncEntryLedger *ledger)
{
ComboCell *cell;
cell = (ComboCell *) gnc_table_layout_get_cell (ledger->table->layout,
ENTRY_ACTN_CELL);
if (!cell) return;
/* XXX: change this based on the ledger type */
gnc_combo_cell_add_menu_item (cell, _("Hours"));
gnc_combo_cell_add_menu_item (cell, _("Project"));
gnc_combo_cell_add_menu_item (cell, _("Material"));
}
static void
gnc_entry_ledger_config_cells (GncEntryLedger *ledger)
{
/* the action cell */
gnc_combo_cell_set_autosize
((ComboCell *)
gnc_table_layout_get_cell (ledger->table->layout, ENTRY_ACTN_CELL), TRUE);
/* Use 6 decimal places for all prices and quantities */
gnc_price_cell_set_fraction
((PriceCell *)
gnc_table_layout_get_cell (ledger->table->layout, ENTRY_PRIC_CELL),
1000000);
gnc_price_cell_set_fraction
((PriceCell *)
gnc_table_layout_get_cell (ledger->table->layout, ENTRY_DISC_CELL),
1000000);
gnc_price_cell_set_fraction
((PriceCell *) gnc_table_layout_get_cell (ledger->table->layout,
ENTRY_QTY_CELL),
1000000);
/* add menu items for the action cell */
gnc_entry_ledger_config_action (ledger);
}
/* Create and return a new GncEntry Ledger */
GncEntryLedger * gnc_entry_ledger_new (GNCBook *book, GncEntryLedgerType type)
{
GncEntryLedger *ledger;
if (!book) return NULL;
if (type < 0 || type >= GNCENTRY_NUM_REGISTER_TYPES) return NULL;
ledger = g_new0 (GncEntryLedger, 1);
ledger->type = type;
ledger->book = book;
ledger->blank_entry_guid = *xaccGUIDNULL();
ledger->blank_entry_edited = FALSE;
{
Timespec ts = { 0, 0 };
ts.tv_sec = time (NULL);
ledger->last_date_entered = timespecCanonicalDayTime (ts);
}
{
TableLayout *layout = gnc_entry_ledger_layout_new (ledger);
TableModel *model = gnc_entry_ledger_model_new (type);
TableControl *control = gnc_entry_ledger_control_new ();
model->handler_user_data = ledger;
control->user_data = ledger;
ledger->table = gnc_table_new (layout, model, control);
}
gnc_entry_ledger_config_cells (ledger);
/* set up header */
{
VirtualCellLocation vcell_loc = { 0, 0 };
CellBlock *header;
header = gnc_table_layout_get_cursor (ledger->table->layout, CURSOR_HEADER);
gnc_table_set_vcell (ledger->table, header, NULL, TRUE, TRUE, vcell_loc);
}
/* set up first initial row */
{
VirtualLocation vloc;
CellBlock *cursor;
vloc.vcell_loc.virt_row = 1;
vloc.vcell_loc.virt_col = 0;
vloc.phys_row_offset = 0;
vloc.phys_col_offset = 0;
cursor = gnc_table_layout_get_cursor (ledger->table->layout, "cursor");
gnc_table_set_vcell (ledger->table, cursor, NULL, TRUE, TRUE, vloc.vcell_loc);
if (gnc_table_find_close_valid_cell (ledger->table, &vloc, FALSE))
gnc_table_move_cursor (ledger->table, vloc);
else
{
g_warning ("Can't find valid initial location");
}
}
/* Initialize Display */
gnc_entry_ledger_display_init (ledger);
return ledger;
}
/* Destroy the GncEntry Ledger */
void gnc_entry_ledger_destroy (GncEntryLedger *ledger)
{
if (!ledger) return;
/* Destroy blank entry, etc. */
gnc_entry_ledger_clear_blank_entry (ledger);
gnc_entry_ledger_display_fini (ledger);
gnc_table_destroy (ledger->table);
gncQueryDestroy (ledger->query);
g_free (ledger);
}
Table * gnc_entry_ledger_get_table (GncEntryLedger *ledger)
{
if (!ledger) return NULL;
return ledger->table;
}
void gnc_entry_ledger_set_default_order (GncEntryLedger *ledger,
GncOrder *order)
{
if (!ledger) return;
ledger->order = order;
if (!ledger->query && order) {
ledger->query = gncQueryCreateFor (GNC_ENTRY_MODULE_NAME);
gncQuerySetBook (ledger->query, gncOrderGetBook (order));
gncQueryAddGUIDMatch (ledger->query,
g_slist_prepend (g_slist_prepend (NULL,
QUERY_PARAM_GUID),
ENTRY_ORDER),
gncOrderGetGUID (order), QUERY_AND);
}
gnc_entry_ledger_display_refresh (ledger);
}
static GSList *
build_param_list (char const *param, ...)
{
GSList *param_list = NULL;
char const *this_param;
va_list ap;
if (!param)
return NULL;
va_start (ap, param);
for (this_param = param; this_param; this_param = va_arg (ap, const char *))
param_list = g_slist_prepend (param_list, (gpointer)this_param);
va_end (ap);
return g_slist_reverse (param_list);
}
static void create_invoice_query (GncEntryLedger *ledger)
{
QueryNew *q, *q1;
char * type = NULL;
if (!ledger->invoice)
return;
if (ledger->query)
gncQueryDestroy (ledger->query);
/* Match: (where I-TYPE == Invoice or Bill)
*
* 1. book AND
* 2. ( Entry->I-TYPE == ledger->invoice
* #if I-TYPE == Invoice (entry only)
* OR
* 3. ( Entry->Invoice == NULL AND
* ( Entry->Billable == TRUE AND
* Entry->Bill->Is-Posted? == TRUE AND
* ( Entry->BillTo == Invoice->parent OR
* Entry->Bill->BillTo == Invoice->parent ) )
* OR
* ( Entry->Order->real-parent == Invoice->parent ) )
* #endif
* )
*
* Note that term 3 is only for Editable invoices.
*/
/* Term 1 */
ledger->query = gncQueryCreateFor (GNC_ENTRY_MODULE_NAME);
gncQuerySetBook (ledger->query, gncInvoiceGetBook (ledger->invoice));
/* Term 2 */
switch (ledger->type) {
case GNCENTRY_INVOICE_ENTRY:
case GNCENTRY_INVOICE_VIEWER:
type = ENTRY_INVOICE;
break;
case GNCENTRY_BILL_ENTRY:
case GNCENTRY_BILL_VIEWER:
type = ENTRY_BILL;
break;
default:
g_warning ("Invalid Ledger type");
type = ENTRY_INVOICE;
break;
}
q = gncQueryCreateFor (GNC_ENTRY_MODULE_NAME);
gncQueryAddGUIDMatch (q, build_param_list (type, QUERY_PARAM_GUID, NULL),
gncInvoiceGetGUID (ledger->invoice), QUERY_OR);
/* Term 3 */
if (ledger->type == GNCENTRY_INVOICE_ENTRY &&
gncOwnerGetEndGUID (gncInvoiceGetOwner (ledger->invoice)) != NULL) {
const GUID *invoice_parent =
gncOwnerGetGUID (gncInvoiceGetOwner (ledger->invoice));
QueryNew *q2 = gncQueryCreateFor (GNC_ENTRY_MODULE_NAME);
/*
* Entry->BillTo == Invoice->parent OR
* ( Entry->BillTo == NULL AND Entry->Bill->BillTo == Invoice->parent )
*/
gncQueryAddGUIDMatch (q2, build_param_list (ENTRY_BILLTO,
QUERY_PARAM_GUID, NULL),
NULL, QUERY_AND);
gncQueryAddGUIDMatch (q2, build_param_list (ENTRY_BILL, INVOICE_BILLTO,
QUERY_PARAM_GUID, NULL),
invoice_parent, QUERY_AND);
gncQueryAddGUIDMatch (q2, build_param_list (ENTRY_BILLTO,
QUERY_PARAM_GUID, NULL),
invoice_parent, QUERY_OR);
/* Entry->Billable == TRUE AND Entry->Bill->Is-Posted? == TRUE */
gncQueryAddBooleanMatch (q2, build_param_list (ENTRY_BILLABLE, NULL),
TRUE, QUERY_AND);
gncQueryAddBooleanMatch (q2, build_param_list (ENTRY_BILL,
INVOICE_IS_POSTED, NULL),
TRUE, QUERY_AND);
/* Entry->Order->real-parent == Invoice->parent */
gncQueryAddGUIDMatch (q2, build_param_list (ENTRY_ORDER, ORDER_OWNER,
OWNER_PARENTG, NULL),
invoice_parent, QUERY_OR);
/* Entry->Invoice == NULL */
gncQueryAddGUIDMatch (q2, build_param_list (ENTRY_INVOICE,
QUERY_PARAM_GUID, NULL),
NULL, QUERY_AND);
/* Combine terms 2 and 3 */
q1 = gncQueryMerge (q, q2, QUERY_OR);
gncQueryDestroy (q);
gncQueryDestroy (q2);
q = q1;
}
/* Combine terms 1 and 2 */
q1 = gncQueryMerge (ledger->query, q, QUERY_AND);
gncQueryDestroy (q);
gncQueryDestroy (ledger->query);
ledger->query = q1;
}
void gnc_entry_ledger_set_default_invoice (GncEntryLedger *ledger,
GncInvoice *invoice)
{
if (!ledger) return;
ledger->invoice = invoice;
if (!ledger->query && invoice)
create_invoice_query (ledger);
gnc_entry_ledger_display_refresh (ledger);
}
void gnc_entry_ledger_reset_query (GncEntryLedger *ledger)
{
if (!ledger) return;
if (!ledger->invoice) return;
create_invoice_query (ledger);
gnc_entry_ledger_display_refresh (ledger);
}
void gnc_entry_ledger_set_parent (GncEntryLedger *ledger, gncUIWidget parent)
{
if (!ledger) return;
ledger->parent = parent;
}
gboolean gnc_entry_ledger_find_entry (GncEntryLedger *ledger, GncEntry *entry,
VirtualCellLocation *vcell_loc)
{
Table *table = ledger->table;
int v_row;
GncEntry *e;
for (v_row = 1; v_row < table->num_virt_rows; v_row++) {
VirtualCellLocation vc_loc = { v_row, 0 };
e = gnc_entry_ledger_get_entry (ledger, vc_loc);
if (e == entry) {
if (vcell_loc != NULL)
*vcell_loc = vc_loc;
return TRUE;
}
}
return FALSE;
}
void gnc_entry_ledger_set_readonly (GncEntryLedger *ledger)
{
if (!ledger) return;
/* reset the ledger type to a viewer */
switch (ledger->type) {
case GNCENTRY_ORDER_ENTRY:
ledger->type = GNCENTRY_ORDER_VIEWER;
break;
case GNCENTRY_INVOICE_ENTRY:
ledger->type = GNCENTRY_INVOICE_VIEWER;
create_invoice_query (ledger);
break;
case GNCENTRY_BILL_ENTRY:
ledger->type = GNCENTRY_BILL_VIEWER;
create_invoice_query (ledger);
break;
default:
return; /* Nothing to do */
}
/* reset the model */
gnc_table_model_set_read_only (ledger->table->model, TRUE);
/* get rid of the blank entry, if it exists */
gnc_entry_ledger_clear_blank_entry (ledger);
/* and refresh the display */
gnc_entry_ledger_display_refresh (ledger);
}
gboolean
gnc_entry_ledger_changed (GncEntryLedger *ledger)
{
if (!ledger)
return FALSE;
if (gnc_table_current_cursor_changed (ledger->table, FALSE))
return TRUE;
return FALSE;
}
void
gnc_entry_ledger_compute_value (GncEntryLedger *ledger,
gnc_numeric *value, gnc_numeric *tax_value)
{
gnc_numeric qty, price, discount;
gint disc_type, disc_how;
gboolean taxable, taxincluded;
GncTaxTable *table;
GList *taxes = NULL;
gnc_entry_ledger_get_numeric (ledger, ENTRY_QTY_CELL, &qty);
gnc_entry_ledger_get_numeric (ledger, ENTRY_PRIC_CELL, &price);
gnc_entry_ledger_get_numeric (ledger, ENTRY_DISC_CELL, &discount);
disc_type = gnc_entry_ledger_get_type (ledger, ENTRY_DISTYPE_CELL);
disc_how = gnc_entry_ledger_get_type (ledger, ENTRY_DISHOW_CELL);
/* If we're so early in the process that we don't have info, stop now */
if (disc_type < 0 || disc_how < 0) {
if (value)
*value = gnc_numeric_zero ();
if (tax_value)
*tax_value = gnc_numeric_zero ();
return;
}
taxable = gnc_entry_ledger_get_checkmark (ledger, ENTRY_TAXABLE_CELL);
taxincluded = gnc_entry_ledger_get_checkmark (ledger, ENTRY_TAXINCLUDED_CELL);
table = gnc_entry_ledger_get_taxtable (ledger, ENTRY_TAXTABLE_CELL);
gncEntryComputeValue (qty, price, (taxable ? table : NULL), taxincluded,
discount, disc_type, disc_how,
value, NULL, &taxes);
/* Now convert the values to the proper denomination */
if (value)
*value = gnc_numeric_convert (*value, 100 /* XXX */, GNC_RND_ROUND);
if (tax_value) {
*tax_value = gncAccountValueTotal (taxes);
*tax_value = gnc_numeric_convert (*tax_value, 100 /* XXX */, GNC_RND_ROUND);
}
}
gboolean
gnc_entry_ledger_get_entry_virt_loc (GncEntryLedger *ledger, GncEntry *entry,
VirtualCellLocation *vcell_loc)
{
Table *table;
int v_row;
int v_col;
if ((ledger == NULL) || (entry == NULL))
return FALSE;
table = ledger->table;
/* go backwards because typically you search for entries at the end */
for (v_row = table->num_virt_rows - 1; v_row > 0; v_row--)
for (v_col = 0; v_col < table->num_virt_cols; v_col++)
{
VirtualCellLocation vc_loc = { v_row, v_col };
VirtualCell *vcell;
GncEntry *e;
vcell = gnc_table_get_virtual_cell (table, vc_loc);
if (vcell == NULL)
continue;
if (!vcell->visible)
continue;
e = gncEntryLookup (ledger->book, vcell->vcell_data);
if (e == entry)
{
if (vcell_loc)
*vcell_loc = vc_loc;
return TRUE;
}
}
return FALSE;
}
void
gnc_entry_ledger_delete_current_entry (GncEntryLedger *ledger)
{
GncEntry *entry;
if (!ledger)
return;
/* If there is no entry, just return */
entry = gnc_entry_ledger_get_current_entry (ledger);
if (!entry)
return;
/* If this is the blank entry, just cancel the changes */
if (entry == gnc_entry_ledger_get_blank_entry (ledger)) {
gnc_entry_ledger_cancel_cursor_changes (ledger);
return;
}
/* Ok, let's delete this entry */
gnc_suspend_gui_refresh ();
{
GncOrder *order;
GncInvoice *invoice;
order = gncEntryGetOrder (entry);
if (order)
gncOrderRemoveEntry (order, entry);
invoice = gncEntryGetInvoice (entry);
if (invoice)
gncInvoiceRemoveEntry (invoice, entry);
invoice = gncEntryGetBill (entry);
if (invoice)
gncBillRemoveEntry (invoice, entry);
gncEntryDestroy (entry);
/* XXX: Commit the deletion? */
}
gnc_resume_gui_refresh ();
}
void
gnc_entry_ledger_duplicate_current_entry (GncEntryLedger *ledger)
{
GncEntry *entry;
gboolean changed;
if (!ledger)
return;
/* Be paranoid */
entry = gnc_entry_ledger_get_current_entry (ledger);
if (!entry)
return;
changed = gnc_table_current_cursor_changed (ledger->table, FALSE);
/* See if we're asked to duplicate an unchanged blank entry --
* there is no point in doing that.
*/
if (!changed && entry == gnc_entry_ledger_get_blank_entry (ledger))
return;
gnc_suspend_gui_refresh ();
/* If the cursor has been edited, we are going to have to commit
* it before we can duplicate. Make sure the user wants to do that. */
if (changed) {
const char *message = _("The current entry has been changed.\n"
"Would you like to save it?");
GNCVerifyResult result;
result = gnc_ok_cancel_dialog_parented (ledger->parent,
GNC_VERIFY_OK, message);
if (result == GNC_VERIFY_CANCEL) {
gnc_resume_gui_refresh ();
return;
}
if (!gnc_entry_ledger_commit_entry (ledger)) {
gnc_resume_gui_refresh ();
return;
}
}
/* Ok, we're ready to make the copy */
{
GncEntry * new_entry;
new_entry = gncEntryCreate (ledger->book);
gncEntryCopy (entry, new_entry);
gncEntrySetDate (new_entry, ledger->last_date_entered);
/* Set the hint for where to display on the refresh */
ledger->hint_entry = new_entry;
}
gnc_resume_gui_refresh ();
return;
}
QueryNew *
gnc_entry_ledger_get_query (GncEntryLedger *ledger)
{
if (!ledger)
return NULL;
return ledger->query;
}