mirror of
https://github.com/Gnucash/gnucash.git
synced 2024-11-29 12:14:31 -06:00
1788 lines
54 KiB
C
1788 lines
54 KiB
C
/********************************************************************\
|
|
* gncEntry.c -- the Core Business Entry Interface *
|
|
* *
|
|
* 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 *
|
|
* *
|
|
\********************************************************************/
|
|
|
|
/*
|
|
* Copyright (C) 2001,2002 Derek Atkins
|
|
* Author: Derek Atkins <warlord@MIT.EDU>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <glib.h>
|
|
#include <qofinstance-p.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "gnc-commodity.h"
|
|
|
|
#include "gncEntry.h"
|
|
#include "gncEntryP.h"
|
|
#include "gnc-features.h"
|
|
#include "gncInvoice.h"
|
|
#include "gncOrder.h"
|
|
|
|
struct _gncEntry
|
|
{
|
|
QofInstance inst;
|
|
|
|
time64 date;
|
|
time64 date_entered;
|
|
const char * desc;
|
|
const char * action;
|
|
const char * notes;
|
|
gnc_numeric quantity;
|
|
|
|
/* customer invoice data */
|
|
Account * i_account;
|
|
gnc_numeric i_price;
|
|
gboolean i_taxable;
|
|
gboolean i_taxincluded;
|
|
GncTaxTable * i_tax_table;
|
|
gnc_numeric i_discount;
|
|
GncAmountType i_disc_type;
|
|
GncDiscountHow i_disc_how;
|
|
|
|
/* vendor bill data */
|
|
Account * b_account;
|
|
gnc_numeric b_price;
|
|
gboolean b_taxable;
|
|
gboolean b_taxincluded;
|
|
GncTaxTable * b_tax_table;
|
|
gboolean billable;
|
|
GncOwner billto;
|
|
|
|
/* employee bill data */
|
|
GncEntryPaymentType b_payment;
|
|
|
|
/* my parent(s) */
|
|
GncOrder * order;
|
|
GncInvoice * invoice;
|
|
GncInvoice * bill;
|
|
|
|
/* CACHED VALUES */
|
|
gboolean values_dirty;
|
|
|
|
/* customer invoice */
|
|
gnc_numeric i_value;
|
|
gnc_numeric i_value_rounded;
|
|
GList * i_tax_values;
|
|
gnc_numeric i_tax_value;
|
|
gnc_numeric i_tax_value_rounded;
|
|
gnc_numeric i_disc_value;
|
|
gnc_numeric i_disc_value_rounded;
|
|
time64 i_taxtable_modtime;
|
|
|
|
/* vendor bill */
|
|
gnc_numeric b_value;
|
|
gnc_numeric b_value_rounded;
|
|
GList * b_tax_values;
|
|
gnc_numeric b_tax_value;
|
|
gnc_numeric b_tax_value_rounded;
|
|
time64 b_taxtable_modtime;
|
|
};
|
|
|
|
struct _gncEntryClass
|
|
{
|
|
QofInstanceClass parent_class;
|
|
};
|
|
|
|
static QofLogModule log_module = GNC_MOD_BUSINESS;
|
|
|
|
|
|
/* You must edit the functions in this block in tandem.
|
|
* KEEP THIS FUNCTION IN SYNC with the one below! */
|
|
const char *
|
|
gncEntryDiscountHowToString (GncDiscountHow how)
|
|
{
|
|
switch (how)
|
|
{
|
|
case (GNC_DISC_PRETAX):
|
|
return "PRETAX";
|
|
case (GNC_DISC_SAMETIME):
|
|
return "SAMETIME";
|
|
case (GNC_DISC_POSTTAX):
|
|
return "POSTTAX";
|
|
default:
|
|
PWARN ("asked to translate unknown discount-how %d.\n", how);
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* You must edit the functions in this block in tandem.
|
|
* KEEP THIS FUNCTION IN SYNC with the one above! */
|
|
gboolean gncEntryDiscountStringToHow (const char *str, GncDiscountHow *how)
|
|
{
|
|
if (g_strcmp0 ("PRETAX", str) == 0)
|
|
{
|
|
*how = GNC_DISC_PRETAX;
|
|
return TRUE;
|
|
}
|
|
if (g_strcmp0 ("SAMETIME", str) == 0)
|
|
{
|
|
*how = GNC_DISC_SAMETIME;
|
|
return TRUE;
|
|
}
|
|
if (g_strcmp0 ("POSTTAX", str) == 0)
|
|
{
|
|
*how = GNC_DISC_POSTTAX;
|
|
return TRUE;
|
|
}
|
|
PWARN ("asked to translate unknown discount-how string %s.\n",
|
|
str ? str : "(null)");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* You must edit the functions in this block in tandem.
|
|
* KEEP THIS FUNCTION IN SYNC with the one below! */
|
|
const char * gncEntryPaymentTypeToString (GncEntryPaymentType type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case (GNC_PAYMENT_CASH):
|
|
return "CASH";
|
|
case (GNC_PAYMENT_CARD):
|
|
return "CARD";
|
|
default:
|
|
PWARN ("asked to translate unknown payment type %d.\n", type);
|
|
break;
|
|
}
|
|
return NULL ;
|
|
}
|
|
|
|
/* You must edit the functions in this block in tandem.
|
|
* KEEP THIS FUNCTION IN SYNC with the one above! */
|
|
gboolean gncEntryPaymentStringToType (const char *str, GncEntryPaymentType *type)
|
|
{
|
|
if (g_strcmp0 ("CASH", str) == 0)
|
|
{
|
|
*type = GNC_PAYMENT_CASH;
|
|
return TRUE;
|
|
}
|
|
if (g_strcmp0 ("CARD", str) == 0)
|
|
{
|
|
*type = GNC_PAYMENT_CARD;
|
|
return TRUE;
|
|
}
|
|
PWARN ("asked to translate unknown discount-how string %s.\n",
|
|
str ? str : "(null)");
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#define _GNC_MOD_NAME GNC_ID_ENTRY
|
|
|
|
#define SET_STR(obj, member, str) { \
|
|
if (!g_strcmp0 (member, str)) return; \
|
|
gncEntryBeginEdit (obj); \
|
|
CACHE_REPLACE (member, str); \
|
|
}
|
|
|
|
static inline void mark_entry (GncEntry *entry);
|
|
void mark_entry (GncEntry *entry)
|
|
{
|
|
qof_instance_set_dirty(&entry->inst);
|
|
qof_event_gen (&entry->inst, QOF_EVENT_MODIFY, NULL);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
// PROP_DATE, /* Table */
|
|
// PROP_DATE_ENTERED, /* Table */
|
|
PROP_DESCRIPTION, /* Table */
|
|
// PROP_ACTION, /* Table */
|
|
// PROP_NOTES, /* Table */
|
|
// PROP_QUANTITY, /* Table (numeric) */
|
|
// PROP_I_ACCT, /* Table */
|
|
// PROP_I_PRICE, /* Table (numeric) */
|
|
// PROP_I_DISCOUNT, /* Table (numeric) */
|
|
// PROP_INVOICE, /* Table */
|
|
// PROP_I_DISC_TYPE, /* Table */
|
|
// PROP_I_DISC_HOW, /* Table */
|
|
// PROP_I_TAXABLE, /* Table */
|
|
// PROP_I_TAX_INCL, /* Table */
|
|
// PROP_I_TAXTABLE, /* Table */
|
|
// PROP_B_ACCT, /* Table */
|
|
// PROP_B_PRICE, /* Table (numeric) */
|
|
// PROP_BILL, /* Table */
|
|
// PROP_B_TAXTABLE_1, /* Table */
|
|
// PROP_B_TAX_INCL, /* Table */
|
|
// PROP_B_TAXTABLE, /* Table */
|
|
// PROP_B_PAYTYPE, /* Table */
|
|
// PROP_BILLABLE, /* Table */
|
|
// PROP_BILLTO_TYPE, /* Table */
|
|
// PROP_BILLTO, /* Table */
|
|
// PROP_ORDER, /* Table */
|
|
};
|
|
|
|
/* GObject Initialization */
|
|
G_DEFINE_TYPE(GncEntry, gnc_entry, QOF_TYPE_INSTANCE)
|
|
|
|
static void
|
|
gnc_entry_init(GncEntry* entry)
|
|
{
|
|
}
|
|
|
|
static void
|
|
gnc_entry_dispose(GObject *entryp)
|
|
{
|
|
G_OBJECT_CLASS(gnc_entry_parent_class)->dispose(entryp);
|
|
}
|
|
|
|
static void
|
|
gnc_entry_finalize(GObject* entryp)
|
|
{
|
|
G_OBJECT_CLASS(gnc_entry_parent_class)->finalize(entryp);
|
|
}
|
|
|
|
static void
|
|
gnc_entry_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GncEntry *entry;
|
|
|
|
g_return_if_fail(GNC_IS_ENTRY(object));
|
|
|
|
entry = GNC_ENTRY(object);
|
|
switch (prop_id)
|
|
{
|
|
case PROP_DESCRIPTION:
|
|
g_value_set_string(value, entry->desc);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gnc_entry_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GncEntry *entry;
|
|
|
|
g_return_if_fail(GNC_IS_ENTRY(object));
|
|
|
|
entry = GNC_ENTRY(object);
|
|
g_assert (qof_instance_get_editlevel(entry));
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_DESCRIPTION:
|
|
gncEntrySetDescription(entry, g_value_get_string(value));
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/** Return displayable name */
|
|
static gchar*
|
|
impl_get_display_name(const QofInstance* inst)
|
|
{
|
|
GncEntry* entry;
|
|
gchar* display_name;
|
|
gchar* s;
|
|
|
|
g_return_val_if_fail(inst != NULL, FALSE);
|
|
g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
|
|
|
|
entry = GNC_ENTRY(inst);
|
|
if (entry->order != NULL)
|
|
{
|
|
display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->order));
|
|
s = g_strdup_printf("Entry in %s", display_name);
|
|
g_free(display_name);
|
|
return s;
|
|
}
|
|
if (entry->invoice != NULL)
|
|
{
|
|
display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->invoice));
|
|
s = g_strdup_printf("Entry in %s", display_name);
|
|
g_free(display_name);
|
|
return s;
|
|
}
|
|
if (entry->bill != NULL)
|
|
{
|
|
display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->bill));
|
|
s = g_strdup_printf("Entry in %s", display_name);
|
|
g_free(display_name);
|
|
return s;
|
|
}
|
|
|
|
return g_strdup_printf("Entry %p", inst);
|
|
}
|
|
|
|
/** Does this object refer to a specific object */
|
|
static gboolean
|
|
impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
|
|
{
|
|
GncEntry* entry;
|
|
|
|
g_return_val_if_fail(inst != NULL, FALSE);
|
|
g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE);
|
|
|
|
entry = GNC_ENTRY(inst);
|
|
|
|
if (GNC_IS_ACCOUNT(ref))
|
|
{
|
|
Account* acc = GNC_ACCOUNT(ref);
|
|
return (entry->i_account == acc || entry->b_account == acc);
|
|
}
|
|
else if (GNC_IS_TAXTABLE(ref))
|
|
{
|
|
GncTaxTable* tt = GNC_TAXTABLE(ref);
|
|
return (entry->i_tax_table == tt || entry->b_tax_table == tt);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/** Returns a list of my type of object which refers to an object. For example, when called as
|
|
qof_instance_get_typed_referring_object_list(taxtable, account);
|
|
it will return the list of taxtables which refer to a specific account. The result should be the
|
|
same regardless of which taxtable object is used. The list must be freed by the caller but the
|
|
objects on the list must not.
|
|
*/
|
|
static GList*
|
|
impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
|
|
{
|
|
if (!GNC_IS_ACCOUNT(ref) && !GNC_IS_TAXTABLE(ref))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
|
|
}
|
|
|
|
static void
|
|
gnc_entry_class_init (GncEntryClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
|
|
|
|
gobject_class->dispose = gnc_entry_dispose;
|
|
gobject_class->finalize = gnc_entry_finalize;
|
|
gobject_class->set_property = gnc_entry_set_property;
|
|
gobject_class->get_property = gnc_entry_get_property;
|
|
|
|
qof_class->get_display_name = impl_get_display_name;
|
|
qof_class->refers_to_object = impl_refers_to_object;
|
|
qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
|
|
|
|
g_object_class_install_property
|
|
(gobject_class,
|
|
PROP_DESCRIPTION,
|
|
g_param_spec_string ("description",
|
|
"Entry Description",
|
|
"The description is an arbitrary string "
|
|
"assigned by the user. It provides identification "
|
|
"for this entry.",
|
|
NULL,
|
|
G_PARAM_READWRITE));
|
|
}
|
|
|
|
/* Create/Destroy Functions */
|
|
GncEntry *gncEntryCreate (QofBook *book)
|
|
{
|
|
GncEntry *entry;
|
|
gnc_numeric zero = gnc_numeric_zero ();
|
|
|
|
if (!book) return NULL;
|
|
|
|
entry = g_object_new (GNC_TYPE_ENTRY, NULL);
|
|
qof_instance_init_data (&entry->inst, _GNC_MOD_NAME, book);
|
|
|
|
entry->desc = CACHE_INSERT ("");
|
|
entry->action = CACHE_INSERT ("");
|
|
entry->notes = CACHE_INSERT ("");
|
|
entry->quantity = zero;
|
|
|
|
entry->i_price = zero;
|
|
entry->i_taxable = TRUE;
|
|
entry->i_discount = zero;
|
|
entry->i_disc_type = GNC_AMT_TYPE_PERCENT;
|
|
entry->i_disc_how = GNC_DISC_PRETAX;
|
|
|
|
entry->b_price = zero;
|
|
entry->b_taxable = TRUE;
|
|
entry->billto.type = GNC_OWNER_CUSTOMER;
|
|
entry->b_payment = GNC_PAYMENT_CASH;
|
|
|
|
entry->values_dirty = TRUE;
|
|
|
|
qof_event_gen (&entry->inst, QOF_EVENT_CREATE, NULL);
|
|
|
|
return entry;
|
|
}
|
|
|
|
void gncEntryDestroy (GncEntry *entry)
|
|
{
|
|
if (!entry) return;
|
|
qof_instance_set_destroying(entry, TRUE);
|
|
gncEntryCommitEdit(entry);
|
|
}
|
|
|
|
static void gncEntryFree (GncEntry *entry)
|
|
{
|
|
if (!entry) return;
|
|
|
|
qof_event_gen (&entry->inst, QOF_EVENT_DESTROY, NULL);
|
|
|
|
CACHE_REMOVE (entry->desc);
|
|
CACHE_REMOVE (entry->action);
|
|
CACHE_REMOVE (entry->notes);
|
|
if (entry->i_tax_values)
|
|
gncAccountValueDestroy (entry->i_tax_values);
|
|
if (entry->b_tax_values)
|
|
gncAccountValueDestroy (entry->b_tax_values);
|
|
|
|
if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(entry))))
|
|
{
|
|
if (entry->i_tax_table)
|
|
gncTaxTableDecRef (entry->i_tax_table);
|
|
if (entry->b_tax_table)
|
|
gncTaxTableDecRef (entry->b_tax_table);
|
|
}
|
|
|
|
/* qof_instance_release (&entry->inst); */
|
|
g_object_unref (entry);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
/* Set Functions */
|
|
|
|
void gncEntrySetDate (GncEntry *entry, time64 date)
|
|
{
|
|
gboolean first_date = FALSE;
|
|
if (!entry) return;
|
|
if (entry->date == date) return;
|
|
if (!entry->date)
|
|
first_date = TRUE;
|
|
gncEntryBeginEdit (entry);
|
|
entry->date = date;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
|
|
/* Don't re-sort the first time we set the date on this entry */
|
|
if (!first_date)
|
|
{
|
|
if (entry->invoice)
|
|
gncInvoiceSortEntries(entry->invoice);
|
|
if (entry->bill)
|
|
gncInvoiceSortEntries(entry->bill);
|
|
}
|
|
}
|
|
|
|
void gncEntrySetDateGDate (GncEntry *entry, const GDate* date)
|
|
{
|
|
if (!entry || !date || !g_date_valid(date))
|
|
return;
|
|
|
|
/* Watch out: Here we are deviating from the initial convention that a
|
|
GDate always converts to the start time of the day. Instead, the GDate is
|
|
converted to "noon" on the respective date. This is not nice, but this
|
|
convention was used for the time64 of GncEntry all the time, so we better
|
|
stick to it.*/
|
|
gncEntrySetDate(entry, time64CanonicalDayTime(gdate_to_time64(*date)));
|
|
}
|
|
|
|
void gncEntrySetDateEntered (GncEntry *entry, time64 date)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->date_entered == date) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->date_entered = date;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetDescription (GncEntry *entry, const char *desc)
|
|
{
|
|
if (!entry || !desc) return;
|
|
SET_STR (entry, entry->desc, desc);
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetAction (GncEntry *entry, const char *action)
|
|
{
|
|
if (!entry || !action) return;
|
|
SET_STR (entry, entry->action, action);
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetNotes (GncEntry *entry, const char *notes)
|
|
{
|
|
if (!entry || !notes) return;
|
|
SET_STR (entry, entry->notes, notes);
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetQuantity (GncEntry *entry, gnc_numeric quantity)
|
|
{
|
|
if (!entry) return;
|
|
if (gnc_numeric_eq (entry->quantity, quantity)) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->quantity = quantity;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetDocQuantity (GncEntry *entry, gnc_numeric quantity, gboolean is_cn)
|
|
{
|
|
if (!entry) return;
|
|
if (gnc_numeric_eq (entry->quantity, (is_cn ? gnc_numeric_neg (quantity) : quantity))) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->quantity = (is_cn ? gnc_numeric_neg (quantity) : quantity);
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
/* Customer Invoices */
|
|
|
|
void gncEntrySetInvAccount (GncEntry *entry, Account *acc)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->i_account == acc) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_account = acc;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetInvPrice (GncEntry *entry, gnc_numeric price)
|
|
{
|
|
if (!entry) return;
|
|
if (gnc_numeric_eq (entry->i_price, price)) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_price = price;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetInvTaxable (GncEntry *entry, gboolean taxable)
|
|
{
|
|
if (!entry) return;
|
|
|
|
ENTER ("%d", taxable);
|
|
if (entry->i_taxable == taxable) {
|
|
LEAVE ("Value already set");
|
|
return;
|
|
}
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_taxable = taxable;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
LEAVE ("");
|
|
}
|
|
|
|
void gncEntrySetInvTaxIncluded (GncEntry *entry, gboolean taxincluded)
|
|
{
|
|
if (!entry) return;
|
|
|
|
ENTER ("%d", taxincluded);
|
|
if (entry->i_taxincluded == taxincluded) {
|
|
LEAVE ("Value already set");
|
|
return;
|
|
}
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_taxincluded = taxincluded;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
LEAVE ("");
|
|
}
|
|
|
|
void gncEntrySetInvTaxTable (GncEntry *entry, GncTaxTable *table)
|
|
{
|
|
if (!entry) return;
|
|
|
|
ENTER ("%s", gncTaxTableGetName (table));
|
|
if (entry->i_tax_table == table) {
|
|
LEAVE ("Value already set");
|
|
return;
|
|
}
|
|
gncEntryBeginEdit (entry);
|
|
if (entry->i_tax_table)
|
|
gncTaxTableDecRef (entry->i_tax_table);
|
|
if (table)
|
|
gncTaxTableIncRef (table);
|
|
entry->i_tax_table = table;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
LEAVE ("");
|
|
}
|
|
|
|
void gncEntrySetInvDiscount (GncEntry *entry, gnc_numeric discount)
|
|
{
|
|
if (!entry) return;
|
|
if (gnc_numeric_eq (entry->i_discount, discount)) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_discount = discount;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetInvDiscountType (GncEntry *entry, GncAmountType type)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->i_disc_type == type) return;
|
|
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_disc_type = type;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetInvDiscountHow (GncEntry *entry, GncDiscountHow how)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->i_disc_how == how) return;
|
|
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_disc_how = how;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void qofEntrySetInvDiscType (GncEntry *entry, const char *type_string)
|
|
{
|
|
GncAmountType type;
|
|
|
|
if (!entry) return;
|
|
gncAmountStringToType(type_string, &type);
|
|
if (entry->i_disc_type == type) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->i_disc_type = type;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
|
|
}
|
|
|
|
void qofEntrySetInvDiscHow (GncEntry *entry, const char *type)
|
|
{
|
|
GncDiscountHow how = GNC_DISC_PRETAX;
|
|
|
|
if (!entry) return;
|
|
gncEntryBeginEdit (entry);
|
|
gncEntryDiscountStringToHow(type, &how);
|
|
if (entry->i_disc_how == how) return;
|
|
entry->i_disc_how = how;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
/* Vendor Bills */
|
|
|
|
void gncEntrySetBillAccount (GncEntry *entry, Account *acc)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->b_account == acc) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->b_account = acc;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetBillPrice (GncEntry *entry, gnc_numeric price)
|
|
{
|
|
if (!entry) return;
|
|
if (gnc_numeric_eq (entry->b_price, price)) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->b_price = price;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetBillTaxable (GncEntry *entry, gboolean taxable)
|
|
{
|
|
if (!entry) return;
|
|
|
|
ENTER ("%d", taxable);
|
|
if (entry->b_taxable == taxable) {
|
|
LEAVE ("Value already set");
|
|
return;
|
|
}
|
|
gncEntryBeginEdit (entry);
|
|
entry->b_taxable = taxable;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
LEAVE ("");
|
|
}
|
|
|
|
void gncEntrySetBillTaxIncluded (GncEntry *entry, gboolean taxincluded)
|
|
{
|
|
if (!entry) return;
|
|
|
|
ENTER ("%d", taxincluded);
|
|
if (entry->b_taxincluded == taxincluded) {
|
|
LEAVE ("Value already set");
|
|
return;
|
|
}
|
|
gncEntryBeginEdit (entry);
|
|
entry->b_taxincluded = taxincluded;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
LEAVE ("");
|
|
}
|
|
|
|
void gncEntrySetBillTaxTable (GncEntry *entry, GncTaxTable *table)
|
|
{
|
|
if (!entry) return;
|
|
|
|
ENTER ("%s", gncTaxTableGetName (table));
|
|
if (entry->b_tax_table == table) {
|
|
LEAVE ("Value already set");
|
|
return;
|
|
}
|
|
gncEntryBeginEdit (entry);
|
|
if (entry->b_tax_table)
|
|
gncTaxTableDecRef (entry->b_tax_table);
|
|
if (table)
|
|
gncTaxTableIncRef (table);
|
|
entry->b_tax_table = table;
|
|
entry->values_dirty = TRUE;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
LEAVE ("");
|
|
}
|
|
|
|
void gncEntrySetBillable (GncEntry *entry, gboolean billable)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->billable == billable) return;
|
|
|
|
gncEntryBeginEdit (entry);
|
|
entry->billable = billable;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetBillTo (GncEntry *entry, GncOwner *billto)
|
|
{
|
|
if (!entry || !billto) return;
|
|
if (gncOwnerEqual (&entry->billto, billto)) return;
|
|
|
|
gncEntryBeginEdit (entry);
|
|
gncOwnerCopy (billto, &entry->billto);
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntrySetBillPayment (GncEntry *entry, GncEntryPaymentType type)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->b_payment == type) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->b_payment = type;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
/* Called from gncOrder when we're added to the Order */
|
|
void gncEntrySetOrder (GncEntry *entry, GncOrder *order)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->order == order) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->order = order;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
|
|
}
|
|
|
|
/* called from gncInvoice when we're added to the Invoice */
|
|
void gncEntrySetInvoice (GncEntry *entry, GncInvoice *invoice)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->invoice == invoice) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->invoice = invoice;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
/* called from gncInvoice when we're added to the Invoice/Bill */
|
|
void gncEntrySetBill (GncEntry *entry, GncInvoice *bill)
|
|
{
|
|
if (!entry) return;
|
|
if (entry->bill == bill) return;
|
|
gncEntryBeginEdit (entry);
|
|
entry->bill = bill;
|
|
mark_entry (entry);
|
|
gncEntryCommitEdit (entry);
|
|
}
|
|
|
|
void gncEntryCopy (const GncEntry *src, GncEntry *dest, gboolean add_entry)
|
|
{
|
|
if (!src || !dest) return;
|
|
|
|
gncEntryBeginEdit (dest);
|
|
dest->date = src->date;
|
|
dest->date_entered = src->date_entered; /* ??? */
|
|
gncEntrySetDescription (dest, src->desc);
|
|
gncEntrySetAction (dest, src->action);
|
|
gncEntrySetNotes (dest, src->notes);
|
|
dest->quantity = src->quantity;
|
|
|
|
dest->i_account = src->i_account;
|
|
dest->i_price = src->i_price;
|
|
dest->i_taxable = src->i_taxable;
|
|
dest->i_taxincluded = src->i_taxincluded;
|
|
dest->i_discount = src->i_discount;
|
|
dest->i_disc_type = src->i_disc_type;
|
|
dest->i_disc_how = src->i_disc_how;
|
|
|
|
/* vendor bill data */
|
|
dest->b_account = src->b_account;
|
|
dest->b_price = src->b_price;
|
|
dest->b_taxable = src->b_taxable;
|
|
dest->b_taxincluded = src->b_taxincluded;
|
|
dest->billable = src->billable;
|
|
dest->billto = src->billto;
|
|
|
|
if (src->i_tax_table)
|
|
gncEntrySetInvTaxTable (dest, src->i_tax_table);
|
|
|
|
if (src->b_tax_table)
|
|
gncEntrySetBillTaxTable (dest, src->b_tax_table);
|
|
|
|
if (add_entry)
|
|
{
|
|
if (src->order)
|
|
gncOrderAddEntry (src->order, dest);
|
|
|
|
if (src->invoice)
|
|
gncInvoiceAddEntry (src->invoice, dest);
|
|
|
|
if (src->bill)
|
|
gncBillAddEntry (src->bill, dest);
|
|
}
|
|
|
|
dest->values_dirty = TRUE;
|
|
mark_entry (dest);
|
|
gncEntryCommitEdit (dest);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
/* Get Functions */
|
|
|
|
time64 gncEntryGetDate (const GncEntry *entry)
|
|
{
|
|
return entry ? entry->date : 0;
|
|
}
|
|
|
|
GDate gncEntryGetDateGDate(const GncEntry *entry)
|
|
{
|
|
return time64_to_gdate(gncEntryGetDate(entry));
|
|
}
|
|
|
|
time64 gncEntryGetDateEntered (const GncEntry *entry)
|
|
{
|
|
return entry ? entry->date_entered : 0;
|
|
}
|
|
|
|
const char * gncEntryGetDescription (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->desc;
|
|
}
|
|
|
|
const char * gncEntryGetAction (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->action;
|
|
}
|
|
|
|
const char * gncEntryGetNotes (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->notes;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetQuantity (const GncEntry *entry)
|
|
{
|
|
if (!entry) return gnc_numeric_zero();
|
|
return entry->quantity;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetDocQuantity (const GncEntry *entry, gboolean is_cn)
|
|
{
|
|
gnc_numeric value = gncEntryGetQuantity (entry);
|
|
return (is_cn ? gnc_numeric_neg (value) : value);
|
|
}
|
|
|
|
/* Customer Invoice */
|
|
|
|
Account * gncEntryGetInvAccount (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->i_account;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetInvPrice (const GncEntry *entry)
|
|
{
|
|
if (!entry) return gnc_numeric_zero();
|
|
return entry->i_price;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetInvDiscount (const GncEntry *entry)
|
|
{
|
|
if (!entry) return gnc_numeric_zero();
|
|
return entry->i_discount;
|
|
}
|
|
|
|
GncAmountType gncEntryGetInvDiscountType (const GncEntry *entry)
|
|
{
|
|
if (!entry) return 0;
|
|
return entry->i_disc_type;
|
|
}
|
|
|
|
GncDiscountHow gncEntryGetInvDiscountHow (const GncEntry *entry)
|
|
{
|
|
if (!entry) return 0;
|
|
return entry->i_disc_how;
|
|
}
|
|
|
|
char* qofEntryGetInvDiscType (const GncEntry *entry)
|
|
{
|
|
char *type_string;
|
|
|
|
if (!entry) return 0;
|
|
type_string = g_strdup(gncAmountTypeToString(entry->i_disc_type));
|
|
return type_string;
|
|
}
|
|
|
|
char* qofEntryGetInvDiscHow (const GncEntry *entry)
|
|
{
|
|
char *type_string;
|
|
|
|
if (!entry) return 0;
|
|
type_string = g_strdup(gncEntryDiscountHowToString(entry->i_disc_how));
|
|
return type_string;
|
|
}
|
|
|
|
gboolean gncEntryGetInvTaxable (const GncEntry *entry)
|
|
{
|
|
if (!entry) return FALSE;
|
|
return entry->i_taxable;
|
|
}
|
|
|
|
gboolean gncEntryGetInvTaxIncluded (const GncEntry *entry)
|
|
{
|
|
if (!entry) return FALSE;
|
|
return entry->i_taxincluded;
|
|
}
|
|
|
|
GncTaxTable * gncEntryGetInvTaxTable (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->i_tax_table;
|
|
}
|
|
|
|
/* vendor bills */
|
|
|
|
Account * gncEntryGetBillAccount (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->b_account;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetBillPrice (const GncEntry *entry)
|
|
{
|
|
if (!entry) return gnc_numeric_zero();
|
|
return entry->b_price;
|
|
}
|
|
|
|
gboolean gncEntryGetBillTaxable (const GncEntry *entry)
|
|
{
|
|
if (!entry) return FALSE;
|
|
return entry->b_taxable;
|
|
}
|
|
|
|
gboolean gncEntryGetBillTaxIncluded (const GncEntry *entry)
|
|
{
|
|
if (!entry) return FALSE;
|
|
return entry->b_taxincluded;
|
|
}
|
|
|
|
GncTaxTable * gncEntryGetBillTaxTable (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->b_tax_table;
|
|
}
|
|
|
|
gboolean gncEntryGetBillable (const GncEntry *entry)
|
|
{
|
|
if (!entry) return FALSE;
|
|
return entry->billable;
|
|
}
|
|
|
|
GncOwner * gncEntryGetBillTo (GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return &entry->billto;
|
|
}
|
|
|
|
GncEntryPaymentType gncEntryGetBillPayment (const GncEntry* entry)
|
|
{
|
|
if (!entry) return 0;
|
|
return entry->b_payment;
|
|
}
|
|
|
|
GncInvoice * gncEntryGetInvoice (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->invoice;
|
|
}
|
|
|
|
GncInvoice * gncEntryGetBill (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->bill;
|
|
}
|
|
|
|
GncOrder * gncEntryGetOrder (const GncEntry *entry)
|
|
{
|
|
if (!entry) return NULL;
|
|
return entry->order;
|
|
}
|
|
|
|
/* ================================================================ */
|
|
/*
|
|
* This is the logic of computing the total for an Entry, so you know
|
|
* what values to put into various Splits or to display in the ledger.
|
|
* In other words, we combine the quantity, unit-price, discount and
|
|
* taxes together, depending on various flags.
|
|
*
|
|
* There are four potential ways to combine these numbers:
|
|
* Discount: Pre-Tax Post-Tax
|
|
* Tax : Included Not-Included
|
|
*
|
|
* The process is relatively simple:
|
|
*
|
|
* 1) compute the aggregate price (price*qty)
|
|
* 2) if taxincluded, then back-compute the aggregate pre-tax price
|
|
* 3) apply discount and taxes in the appropriate order
|
|
* 4) return the requested results.
|
|
*
|
|
* Step 2 can be done with aggregate taxes; no need to compute them all
|
|
* unless the caller asked for the tax_value.
|
|
*
|
|
* Note that the returned "value" is such that
|
|
* value + tax == "total to pay"
|
|
* which means in the case of tax-included that the returned
|
|
* "value" may be less than the aggregate price, even without a
|
|
* discount. If you want to display the tax-included value, you need
|
|
* to add the value and taxes together. In other words, the value is
|
|
* the amount the merchant gets; the taxes are the amount the gov't
|
|
* gets, and the customer pays the sum or value + taxes.
|
|
*
|
|
* The discount return value is just for entertainment -- you may want
|
|
* to let a consumer know how much they saved.
|
|
*
|
|
* Note this function will not do any rounding unless forced to prevent overflow.
|
|
* It's the caller's responsibility to round to the proper commodity
|
|
* denominator if needed.
|
|
*/
|
|
static void gncEntryComputeValueInt (gnc_numeric qty, gnc_numeric price,
|
|
const GncTaxTable *tax_table, gboolean tax_included,
|
|
gnc_numeric discount, GncAmountType discount_type,
|
|
GncDiscountHow discount_how,
|
|
gnc_numeric *value, gnc_numeric *discount_value,
|
|
GList **tax_value, gnc_numeric *net_price)
|
|
{
|
|
gnc_numeric aggregate;
|
|
gnc_numeric pretax;
|
|
gnc_numeric result;
|
|
gnc_numeric tax;
|
|
gnc_numeric percent = gnc_numeric_create (100, 1);
|
|
gnc_numeric tpercent = gnc_numeric_zero ();
|
|
gnc_numeric tvalue = gnc_numeric_zero ();
|
|
gnc_numeric i_net_price = price;
|
|
|
|
GList * entries = gncTaxTableGetEntries (tax_table);
|
|
GList * node;
|
|
|
|
ENTER ("");
|
|
/* Step 1: compute the aggregate price */
|
|
|
|
aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
|
|
|
PINFO ("Aggregate value %" PRId64 "/%" PRId64, aggregate.num, aggregate.denom);
|
|
/* Step 2: compute the pre-tax aggregate */
|
|
|
|
/* First, compute the aggregate tpercent and tvalue numbers */
|
|
for (node = entries; node; node = node->next)
|
|
{
|
|
GncTaxTableEntry *entry = node->data;
|
|
gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
|
|
|
|
switch (gncTaxTableEntryGetType (entry))
|
|
{
|
|
case GNC_AMT_TYPE_VALUE:
|
|
tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_LCD);
|
|
break;
|
|
case GNC_AMT_TYPE_PERCENT:
|
|
tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_LCD);
|
|
break;
|
|
default:
|
|
PWARN ("Unknown tax type: %d", gncTaxTableEntryGetType (entry));
|
|
break;
|
|
}
|
|
}
|
|
/* now we need to convert from 5% -> .05 */
|
|
tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
PINFO("Tax rate %" PRId64 "/%" PRId64, tpercent.num, tpercent.denom);
|
|
/* Next, actually compute the pre-tax aggregate value based on the
|
|
* taxincluded flag.
|
|
*/
|
|
if (tax_table && tax_included)
|
|
{
|
|
/* Back-compute the pre-tax aggregate value.
|
|
* We know that aggregate = pretax + pretax*tpercent + tvalue, so
|
|
* pretax = (aggregate-tvalue)/(1+tpercent)
|
|
*/
|
|
pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_LCD);
|
|
pretax = gnc_numeric_div (pretax,
|
|
gnc_numeric_add (tpercent,
|
|
gnc_numeric_create (1, 1),
|
|
GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD),
|
|
GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
|
PINFO ("pretax %" PRId64 "/%" PRId64, pretax.num, pretax.denom);
|
|
if (!gnc_numeric_zero_p(qty))
|
|
{
|
|
i_net_price = gnc_numeric_div (pretax, qty, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
|
}
|
|
PINFO("i_net_price %" PRId64 "/%" PRId64, i_net_price.num, i_net_price.denom);
|
|
}
|
|
else
|
|
{
|
|
PINFO ("Tax not included or no tax table, pretax is aggregate");
|
|
pretax = aggregate;
|
|
}
|
|
|
|
/* Step 3: apply discount and taxes in the appropriate order */
|
|
|
|
/*
|
|
* There are two ways to apply discounts and taxes. In one way, you
|
|
* always compute the discount off the pretax number, and compute
|
|
* the taxes off of either the pretax value or "pretax-discount"
|
|
* value. In the other way, you always compute the tax on "pretax",
|
|
* and compute the discount on either "pretax" or "pretax+taxes".
|
|
*
|
|
* I don't know which is the "correct" way.
|
|
*/
|
|
|
|
/*
|
|
* Type: discount tax
|
|
* PRETAX pretax pretax-discount
|
|
* SAMETIME pretax pretax
|
|
* POSTTAX pretax+tax pretax
|
|
*/
|
|
|
|
switch (discount_how)
|
|
{
|
|
case GNC_DISC_PRETAX:
|
|
case GNC_DISC_SAMETIME:
|
|
/* compute the discount from pretax */
|
|
|
|
if (discount_type == GNC_AMT_TYPE_PERCENT)
|
|
{
|
|
discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
|
}
|
|
|
|
result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
|
|
|
|
/* Figure out when to apply the tax, pretax or pretax-discount */
|
|
if (discount_how == GNC_DISC_PRETAX)
|
|
pretax = result;
|
|
break;
|
|
|
|
case GNC_DISC_POSTTAX:
|
|
/* compute discount on pretax+taxes */
|
|
|
|
if (discount_type == GNC_AMT_TYPE_PERCENT)
|
|
{
|
|
gnc_numeric after_tax;
|
|
|
|
tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
|
after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
|
|
after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_LCD);
|
|
discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
|
}
|
|
|
|
result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
|
|
break;
|
|
|
|
default:
|
|
PWARN ("unknown DiscountHow value: %d", discount_how);
|
|
break;
|
|
}
|
|
|
|
/* Step 4: return the requested results. */
|
|
|
|
/* result == amount merchant gets
|
|
* discount == amount of discount
|
|
* need to compute taxes (based on 'pretax') if the caller wants it.
|
|
*/
|
|
|
|
if (discount_value != NULL)
|
|
*discount_value = discount;
|
|
|
|
if (value != NULL)
|
|
*value = result;
|
|
|
|
/* Now... Compute the list of tax values (if the caller wants it) */
|
|
|
|
if (tax_value != NULL)
|
|
{
|
|
GList * taxes = NULL;
|
|
|
|
PINFO("Computing tax value list");
|
|
for (node = entries; node; node = node->next)
|
|
{
|
|
GncTaxTableEntry *entry = node->data;
|
|
Account *acc = gncTaxTableEntryGetAccount (entry);
|
|
gnc_numeric amount = gncTaxTableEntryGetAmount (entry);
|
|
|
|
g_return_if_fail (acc);
|
|
|
|
switch (gncTaxTableEntryGetType (entry))
|
|
{
|
|
case GNC_AMT_TYPE_VALUE:
|
|
taxes = gncAccountValueAdd (taxes, acc, amount);
|
|
break;
|
|
case GNC_AMT_TYPE_PERCENT:
|
|
amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
|
|
tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_REDUCE | GNC_HOW_RND_ROUND);
|
|
taxes = gncAccountValueAdd (taxes, acc, tax);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
*tax_value = taxes;
|
|
}
|
|
|
|
if (net_price != NULL)
|
|
*net_price = i_net_price;
|
|
|
|
LEAVE ("");
|
|
return;
|
|
}
|
|
|
|
void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price,
|
|
const GncTaxTable *tax_table, gboolean tax_included,
|
|
gnc_numeric discount, GncAmountType discount_type,
|
|
GncDiscountHow discount_how, G_GNUC_UNUSED int SCU,
|
|
gnc_numeric *value, gnc_numeric *discount_value,
|
|
GList **tax_value)
|
|
{
|
|
gncEntryComputeValueInt (qty, price, tax_table, tax_included, discount, discount_type,
|
|
discount_how, value, discount_value, tax_value, NULL);
|
|
}
|
|
|
|
static int
|
|
get_entry_commodity_denom (const GncEntry *entry)
|
|
{
|
|
gnc_commodity *c;
|
|
if (!entry)
|
|
return 0;
|
|
if (entry->invoice)
|
|
{
|
|
c = gncInvoiceGetCurrency (entry->invoice);
|
|
if (c)
|
|
return (gnc_commodity_get_fraction (c));
|
|
}
|
|
if (entry->bill)
|
|
{
|
|
c = gncInvoiceGetCurrency (entry->bill);
|
|
if (c)
|
|
return (gnc_commodity_get_fraction (c));
|
|
}
|
|
return 100000;
|
|
}
|
|
|
|
static void
|
|
gncEntryRecomputeValues (GncEntry *entry)
|
|
{
|
|
int denom;
|
|
GList *tv_iter;
|
|
|
|
ENTER ("");
|
|
/* See if either tax table changed since we last computed values */
|
|
if (entry->i_tax_table)
|
|
{
|
|
time64 modtime = gncTaxTableLastModifiedSecs (entry->i_tax_table);
|
|
if (entry->i_taxtable_modtime != modtime)
|
|
{
|
|
PINFO ("Invoice tax table changed since last recompute.");
|
|
entry->values_dirty = TRUE;
|
|
entry->i_taxtable_modtime = modtime;
|
|
}
|
|
}
|
|
if (entry->b_tax_table)
|
|
{
|
|
time64 modtime = gncTaxTableLastModifiedSecs (entry->b_tax_table);
|
|
if (entry->b_taxtable_modtime != modtime)
|
|
{
|
|
PINFO ("Bill tax table changed since last recompute.");
|
|
entry->values_dirty = TRUE;
|
|
entry->b_taxtable_modtime = modtime;
|
|
}
|
|
}
|
|
|
|
if (!entry->values_dirty) {
|
|
LEAVE ("No changes");
|
|
return;
|
|
}
|
|
|
|
/* Clear the last-computed tax values */
|
|
if (entry->i_tax_values)
|
|
{
|
|
gncAccountValueDestroy (entry->i_tax_values);
|
|
entry->i_tax_values = NULL;
|
|
}
|
|
if (entry->b_tax_values)
|
|
{
|
|
gncAccountValueDestroy (entry->b_tax_values);
|
|
entry->b_tax_values = NULL;
|
|
}
|
|
|
|
/* Determine the commodity denominator */
|
|
denom = get_entry_commodity_denom (entry);
|
|
|
|
/* Compute the invoice values */
|
|
DEBUG("Compute Invoice Values.");
|
|
gncEntryComputeValue (entry->quantity, entry->i_price,
|
|
(entry->i_taxable ? entry->i_tax_table : NULL),
|
|
entry->i_taxincluded,
|
|
entry->i_discount, entry->i_disc_type,
|
|
entry->i_disc_how,
|
|
denom,
|
|
&(entry->i_value), &(entry->i_disc_value),
|
|
&(entry->i_tax_values));
|
|
|
|
/* Compute the bill values */
|
|
DEBUG("Compute BILL Values.");
|
|
gncEntryComputeValue (entry->quantity, entry->b_price,
|
|
(entry->b_taxable ? entry->b_tax_table : NULL),
|
|
entry->b_taxincluded,
|
|
gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
|
|
denom,
|
|
&(entry->b_value), NULL, &(entry->b_tax_values));
|
|
|
|
entry->i_value_rounded = gnc_numeric_convert (entry->i_value, denom,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
|
|
entry->i_disc_value_rounded = gnc_numeric_convert (entry->i_disc_value, denom,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
|
|
entry->i_tax_value = gncAccountValueTotal (entry->i_tax_values);
|
|
entry->i_tax_value_rounded = gnc_numeric_zero();
|
|
for (tv_iter = entry->i_tax_values; tv_iter; tv_iter=tv_iter->next)
|
|
{
|
|
GncAccountValue *acc_val = tv_iter->data;
|
|
entry->i_tax_value_rounded = gnc_numeric_add (entry->i_tax_value_rounded, acc_val->value,
|
|
denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
|
|
}
|
|
|
|
entry->b_value_rounded = gnc_numeric_convert (entry->b_value, denom,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
|
|
entry->b_tax_value = gncAccountValueTotal (entry->b_tax_values);
|
|
entry->b_tax_value_rounded = gnc_numeric_zero();
|
|
for (tv_iter = entry->b_tax_values; tv_iter; tv_iter=tv_iter->next)
|
|
{
|
|
GncAccountValue *acc_val = tv_iter->data;
|
|
entry->b_tax_value_rounded = gnc_numeric_add (entry->b_tax_value_rounded, acc_val->value,
|
|
denom, GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
|
|
}
|
|
entry->values_dirty = FALSE;
|
|
LEAVE ("");
|
|
}
|
|
|
|
/* The "Int" functions below are for internal use only.
|
|
* Outside this file, use the "Doc" or "Bal" variants found below instead. */
|
|
static gnc_numeric gncEntryGetIntValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
|
|
{
|
|
if (!entry) return gnc_numeric_zero();
|
|
gncEntryRecomputeValues (entry);
|
|
if (round)
|
|
return (is_cust_doc ? entry->i_value_rounded : entry->b_value_rounded);
|
|
else
|
|
return (is_cust_doc ? entry->i_value : entry->b_value);
|
|
}
|
|
|
|
static gnc_numeric gncEntryGetIntTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
|
|
{
|
|
if (!entry) return gnc_numeric_zero();
|
|
gncEntryRecomputeValues (entry);
|
|
if (round)
|
|
return (is_cust_doc ? entry->i_tax_value_rounded : entry->b_tax_value_rounded);
|
|
else
|
|
return (is_cust_doc ? entry->i_tax_value : entry->b_tax_value);
|
|
}
|
|
|
|
/* Careful: the returned list is managed by the entry, and will only be valid for a short time */
|
|
static AccountValueList * gncEntryGetIntTaxValues (GncEntry *entry, gboolean is_cust_doc)
|
|
{
|
|
if (!entry) return NULL;
|
|
gncEntryRecomputeValues (entry);
|
|
return (is_cust_doc ? entry->i_tax_values : entry->b_tax_values);
|
|
}
|
|
|
|
static gnc_numeric gncEntryGetIntDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
|
|
{
|
|
if (!entry) return gnc_numeric_zero();
|
|
gncEntryRecomputeValues (entry);
|
|
if (round)
|
|
return (is_cust_doc ? entry->i_disc_value_rounded : gnc_numeric_zero());
|
|
else
|
|
return (is_cust_doc ? entry->i_disc_value : gnc_numeric_zero());
|
|
}
|
|
|
|
/* Note contrary to the GetDoc*Value and GetBal*Value functions below
|
|
* this function will always round the net price to the entry's
|
|
* currency denominator (being the invoice/bill denom or 100000 if not
|
|
* included in a bill or invoice) */
|
|
gnc_numeric gncEntryGetPrice (const GncEntry *entry, gboolean cust_doc, gboolean net)
|
|
{
|
|
gnc_numeric result;
|
|
int denom;
|
|
|
|
if (!entry) return gnc_numeric_zero();
|
|
if (!net) return (cust_doc ? entry->i_price : entry->b_price);
|
|
|
|
|
|
/* Compute the net price */
|
|
if (cust_doc)
|
|
gncEntryComputeValueInt (entry->quantity, entry->i_price,
|
|
(entry->i_taxable ? entry->i_tax_table : NULL),
|
|
entry->i_taxincluded,
|
|
entry->i_discount, entry->i_disc_type,
|
|
entry->i_disc_how,
|
|
NULL, NULL, NULL, &result);
|
|
else
|
|
gncEntryComputeValueInt (entry->quantity, entry->b_price,
|
|
(entry->b_taxable ? entry->b_tax_table : NULL),
|
|
entry->b_taxincluded,
|
|
gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX,
|
|
NULL, NULL, NULL, &result);
|
|
|
|
/* Determine the commodity denominator */
|
|
denom = get_entry_commodity_denom (entry);
|
|
|
|
result = gnc_numeric_convert (result, denom,
|
|
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_ROUND_HALF_UP);
|
|
|
|
return result;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetDocValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
|
|
{
|
|
gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
|
|
return (is_cn ? gnc_numeric_neg (value) : value);
|
|
}
|
|
|
|
gnc_numeric gncEntryGetDocTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
|
|
{
|
|
gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
|
|
return (is_cn ? gnc_numeric_neg (value) : value);
|
|
}
|
|
|
|
/* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
|
|
AccountValueList * gncEntryGetDocTaxValues (GncEntry *entry, gboolean is_cust_doc, gboolean is_cn)
|
|
{
|
|
AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
|
|
AccountValueList *values = NULL, *node;
|
|
|
|
/* Make a copy of the list with negated values if necessary. */
|
|
for (node = int_values; node; node = node->next)
|
|
{
|
|
GncAccountValue *acct_val = node->data;
|
|
values = gncAccountValueAdd (values, acct_val->account,
|
|
(is_cn ? gnc_numeric_neg (acct_val->value)
|
|
: acct_val->value));
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetDocDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn)
|
|
{
|
|
gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
|
|
return (is_cn ? gnc_numeric_neg (value) : value);
|
|
}
|
|
|
|
gnc_numeric gncEntryGetBalValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
|
|
{
|
|
gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc);
|
|
return (is_cust_doc ? gnc_numeric_neg (value) : value);
|
|
}
|
|
|
|
gnc_numeric gncEntryGetBalTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
|
|
{
|
|
gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc);
|
|
return (is_cust_doc ? gnc_numeric_neg (value) : value);
|
|
}
|
|
|
|
/* Careful: the returned list is NOT owned by the entry and should be freed by the caller */
|
|
AccountValueList * gncEntryGetBalTaxValues (GncEntry *entry, gboolean is_cust_doc)
|
|
{
|
|
AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc);
|
|
AccountValueList *values = NULL, *node;
|
|
|
|
/* Make a copy of the list with negated values if necessary. */
|
|
for (node = int_values; node; node = node->next)
|
|
{
|
|
GncAccountValue *acct_val = node->data;
|
|
values = gncAccountValueAdd (values, acct_val->account,
|
|
(is_cust_doc ? gnc_numeric_neg (acct_val->value)
|
|
: acct_val->value));
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
gnc_numeric gncEntryGetBalDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc)
|
|
{
|
|
gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc);
|
|
return (is_cust_doc ? gnc_numeric_neg (value) : value);
|
|
}
|
|
|
|
/* XXX this existence of this routine is just wrong */
|
|
gboolean gncEntryIsOpen (const GncEntry *entry)
|
|
{
|
|
if (!entry) return FALSE;
|
|
return (qof_instance_get_editlevel(entry) > 0);
|
|
}
|
|
|
|
/* ================================================================ */
|
|
|
|
void gncEntryBeginEdit (GncEntry *entry)
|
|
{
|
|
qof_begin_edit(&entry->inst);
|
|
}
|
|
|
|
static void gncEntryOnError (QofInstance *entry, QofBackendError errcode)
|
|
{
|
|
PERR("Entry QofBackend Failure: %d", errcode);
|
|
gnc_engine_signal_commit_error( errcode );
|
|
}
|
|
|
|
static void gncEntryOnDone (QofInstance *inst) {}
|
|
|
|
static void entry_free (QofInstance *inst)
|
|
{
|
|
GncEntry *entry = (GncEntry *)inst;
|
|
gncEntryFree (entry);
|
|
}
|
|
|
|
void gncEntryCommitEdit (GncEntry *entry)
|
|
{
|
|
/* GnuCash 2.6.3 and earlier didn't handle entry kvp's... */
|
|
if (qof_instance_has_kvp(QOF_INSTANCE(entry)))
|
|
gnc_features_set_used (qof_instance_get_book (QOF_INSTANCE (entry)),
|
|
GNC_FEATURE_KVP_EXTRA_DATA);
|
|
|
|
if (!qof_commit_edit (QOF_INSTANCE(entry))) return;
|
|
qof_commit_edit_part2 (&entry->inst, gncEntryOnError,
|
|
gncEntryOnDone, entry_free);
|
|
}
|
|
|
|
int gncEntryCompare (const GncEntry *a, const GncEntry *b)
|
|
{
|
|
int compare;
|
|
|
|
if (a == b) return 0;
|
|
if (!a && b) return -1;
|
|
if (a && !b) return 1;
|
|
g_assert (a && b); /* Silence a static analysis warning. */
|
|
if (a->date != b->date) return a->date - b->date;
|
|
if (a->date_entered != b->date_entered) return a->date_entered - b->date_entered;
|
|
|
|
compare = g_strcmp0 (a->desc, b->desc);
|
|
if (compare) return compare;
|
|
|
|
compare = g_strcmp0 (a->action, b->action);
|
|
if (compare) return compare;
|
|
|
|
return qof_instance_guid_compare(a, b);
|
|
}
|
|
|
|
#define CHECK_STRING(X, Y, FIELD) \
|
|
if (g_strcmp0((X)->FIELD, (Y)->FIELD) != 0) \
|
|
{ \
|
|
PWARN("%s differ: %s vs %s", #FIELD, (X)->FIELD, (Y)->FIELD); \
|
|
return FALSE; \
|
|
}
|
|
|
|
#define CHECK_ACCOUNT(X, Y, FIELD) \
|
|
if (!xaccAccountEqual((X)->FIELD, (Y)->FIELD, TRUE)) \
|
|
{ \
|
|
PWARN("%s differ", #FIELD); \
|
|
return FALSE; \
|
|
}
|
|
|
|
#define CHECK_NUMERIC(X, Y, FIELD) \
|
|
if (!gnc_numeric_equal((X)->FIELD, (Y)->FIELD)) \
|
|
{ \
|
|
PWARN("%s differ", #FIELD); \
|
|
return FALSE; \
|
|
}
|
|
|
|
#define CHECK_VALUE(X, Y, FIELD) \
|
|
if ((X)->FIELD != (Y)->FIELD) \
|
|
{ \
|
|
PWARN("%s differ", #FIELD); \
|
|
return FALSE; \
|
|
}
|
|
|
|
|
|
/* ============================================================= */
|
|
/* Object declaration */
|
|
|
|
static void
|
|
destroy_entry_on_book_close(QofInstance *ent, gpointer data)
|
|
{
|
|
GncEntry* entry = GNC_ENTRY(ent);
|
|
|
|
gncEntryBeginEdit(entry);
|
|
gncEntryDestroy(entry);
|
|
}
|
|
|
|
/** Handles book end - frees all entries from the book
|
|
*
|
|
* @param book Book being closed
|
|
*/
|
|
static void
|
|
gnc_entry_book_end(QofBook* book)
|
|
{
|
|
QofCollection *col;
|
|
|
|
col = qof_book_get_collection(book, GNC_ID_ENTRY);
|
|
qof_collection_foreach(col, destroy_entry_on_book_close, NULL);
|
|
}
|
|
|
|
static QofObject gncEntryDesc =
|
|
{
|
|
DI(.interface_version = ) QOF_OBJECT_VERSION,
|
|
DI(.e_type = ) _GNC_MOD_NAME,
|
|
DI(.type_label = ) "Order/Invoice/Bill Entry",
|
|
DI(.create = ) (gpointer)gncEntryCreate,
|
|
DI(.book_begin = ) NULL,
|
|
DI(.book_end = ) gnc_entry_book_end,
|
|
DI(.is_dirty = ) qof_collection_is_dirty,
|
|
DI(.mark_clean = ) qof_collection_mark_clean,
|
|
DI(.foreach = ) qof_collection_foreach,
|
|
DI(.printable = ) NULL,
|
|
DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
|
|
};
|
|
|
|
gboolean gncEntryRegister (void)
|
|
{
|
|
static QofParam params[] =
|
|
{
|
|
{ ENTRY_DATE, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDate, (QofSetterFunc)gncEntrySetDate },
|
|
{ ENTRY_DATE_ENTERED, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDateEntered, (QofSetterFunc)gncEntrySetDateEntered },
|
|
{ ENTRY_DESC, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetDescription, (QofSetterFunc)gncEntrySetDescription },
|
|
{ ENTRY_ACTION, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetAction, (QofSetterFunc)gncEntrySetAction },
|
|
{ ENTRY_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetNotes, (QofSetterFunc)gncEntrySetNotes },
|
|
{ ENTRY_QTY, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetQuantity, (QofSetterFunc)gncEntrySetQuantity },
|
|
{ ENTRY_IPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetInvPrice, (QofSetterFunc)gncEntrySetInvPrice },
|
|
{ ENTRY_BPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetBillPrice, (QofSetterFunc)gncEntrySetBillPrice },
|
|
{ ENTRY_INVOICE, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetInvoice, NULL },
|
|
{ ENTRY_IACCT, GNC_ID_ACCOUNT, (QofAccessFunc)gncEntryGetInvAccount, (QofSetterFunc)gncEntrySetInvAccount },
|
|
{ ENTRY_BACCT, GNC_ID_ACCOUNT, (QofAccessFunc)gncEntryGetBillAccount, (QofSetterFunc)gncEntrySetBillAccount },
|
|
{ ENTRY_BILL, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetBill, NULL },
|
|
{
|
|
ENTRY_INV_DISC_TYPE, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscType,
|
|
(QofSetterFunc)qofEntrySetInvDiscType
|
|
},
|
|
{
|
|
ENTRY_INV_DISC_HOW, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscHow,
|
|
(QofSetterFunc)qofEntrySetInvDiscHow
|
|
},
|
|
{
|
|
ENTRY_INV_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxable,
|
|
(QofSetterFunc)gncEntrySetInvTaxable
|
|
},
|
|
{
|
|
ENTRY_INV_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxIncluded,
|
|
(QofSetterFunc)gncEntrySetInvTaxIncluded
|
|
},
|
|
{
|
|
ENTRY_BILL_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxable,
|
|
(QofSetterFunc)gncEntrySetBillTaxable
|
|
},
|
|
{
|
|
ENTRY_BILL_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxIncluded,
|
|
(QofSetterFunc)gncEntrySetBillTaxIncluded
|
|
},
|
|
{ ENTRY_BILLABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillable, (QofSetterFunc)gncEntrySetBillable },
|
|
{ ENTRY_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncEntryGetBillTo, (QofSetterFunc)gncEntrySetBillTo },
|
|
{ ENTRY_ORDER, GNC_ID_ORDER, (QofAccessFunc)gncEntryGetOrder, NULL },
|
|
{ QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
|
|
{ QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
|
|
{ NULL },
|
|
};
|
|
|
|
qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncEntryCompare, params);
|
|
|
|
return qof_object_register (&gncEntryDesc);
|
|
}
|