mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
Rework interaction between payments and invoices part 2.
This commit deals with paying invoices. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@22000 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
6467765d7b
commit
de91d24722
@ -204,7 +204,9 @@ gnc_payment_dialog_document_selection_changed (PaymentWindow *pw)
|
||||
|
||||
/* Set the payment amount in the dialog */
|
||||
val = gnc_payment_dialog_calculate_selected_total (pw);
|
||||
gnc_ui_payment_window_set_amount(pw, val);
|
||||
/* XXX It may not always be correct to use the absolute value of amount here
|
||||
* This is an assumption from before the credit notes implementation. */
|
||||
gnc_ui_payment_window_set_amount(pw, gnc_numeric_abs (val));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -259,6 +261,7 @@ gnc_payment_window_fill_docs_list (PaymentWindow *pw)
|
||||
GtkTreeIter iter;
|
||||
Timespec doc_date;
|
||||
GncInvoice *document;
|
||||
gnc_numeric value = gnc_numeric_zero();
|
||||
gnc_numeric debit = gnc_numeric_zero();
|
||||
gnc_numeric credit = gnc_numeric_zero();
|
||||
GncInvoiceType doc_type = GNC_INVOICE_UNDEFINED;
|
||||
@ -297,22 +300,16 @@ gnc_payment_window_fill_docs_list (PaymentWindow *pw)
|
||||
}
|
||||
|
||||
/* Find the debit/credit amount.
|
||||
* Invoices/bills are debit
|
||||
* Credit notes are credit
|
||||
* Pre-payments are credit
|
||||
* Invoices/vendor credit notes are debit (increasing the balance)
|
||||
* Customer credit notes/bills are credit (decreasing the balance)
|
||||
* Pre-payments are debit or credit depending on their sign
|
||||
*/
|
||||
if (document)
|
||||
{
|
||||
if (!gncInvoiceGetIsCreditNote (document))
|
||||
debit = gnc_lot_get_balance (lot);
|
||||
else
|
||||
credit = gnc_lot_get_balance (lot);
|
||||
}
|
||||
value = gnc_lot_get_balance (lot);
|
||||
|
||||
if (gnc_numeric_positive_p (value))
|
||||
debit = value;
|
||||
else
|
||||
{
|
||||
/* This is a pre-payment */
|
||||
credit = gnc_lot_get_balance (lot);
|
||||
}
|
||||
credit = gnc_numeric_neg (value);
|
||||
|
||||
/* Only display non-zero debits/credits */
|
||||
if (!gnc_numeric_zero_p (debit))
|
||||
@ -514,6 +511,29 @@ gnc_payment_dialog_post_to_changed_cb (GtkWidget *widget, gpointer data)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* This helper function is called once for each row in the tree view
|
||||
* that is currently selected. Its task is to add the corresponding
|
||||
* lot to the end of a glist.
|
||||
*/
|
||||
static void
|
||||
get_selected_lots (GtkTreeModel *model,
|
||||
GtkTreePath *path,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
GList **return_list = data;
|
||||
GNCLot *lot;
|
||||
GValue value = { 0 };
|
||||
|
||||
gtk_tree_model_get_value (model, iter, 5, &value);
|
||||
lot = (GNCLot *) g_value_get_pointer (&value);
|
||||
g_value_unset (&value);
|
||||
|
||||
if (lot)
|
||||
*return_list = g_list_append(*return_list, lot);
|
||||
}
|
||||
|
||||
void
|
||||
gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
|
||||
{
|
||||
@ -527,6 +547,9 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
|
||||
|
||||
/* Verify the amount is non-zero */
|
||||
amount = gnc_amount_edit_get_amount (GNC_AMOUNT_EDIT (pw->amount_edit));
|
||||
|
||||
/* XXX Amounts could possibly be negative as well if you take credit notes into account
|
||||
* This still has to be reviewed */
|
||||
if (gnc_numeric_check (amount) || !gnc_numeric_positive_p (amount))
|
||||
{
|
||||
text = _("You must enter the amount of the payment. "
|
||||
@ -569,12 +592,18 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
|
||||
const char *memo, *num;
|
||||
Timespec date;
|
||||
gnc_numeric exch = gnc_numeric_create(1, 1); //default to "one to one" rate
|
||||
GNCLot *payment_lot;
|
||||
GList *selected_lots = NULL;
|
||||
GtkTreeSelection *selection;
|
||||
|
||||
/* Obtain all our ancillary information */
|
||||
memo = gtk_entry_get_text (GTK_ENTRY (pw->memo_entry));
|
||||
num = gtk_entry_get_text (GTK_ENTRY (pw->num_entry));
|
||||
date = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (pw->date_edit));
|
||||
/* FIXME Get a lotlist from the dialog */
|
||||
|
||||
/* Obtain the list of selected lots (documents/payments) from the dialog */
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(pw->docs_list_tree_view));
|
||||
gtk_tree_selection_selected_foreach (selection, get_selected_lots, &selected_lots);
|
||||
|
||||
/* If the 'acc' account and the post account don't have the same
|
||||
currency, we need to get the user to specify the exchange rate */
|
||||
@ -600,64 +629,17 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
|
||||
gnc_xfer_dialog_run_until_done(xfer);
|
||||
}
|
||||
|
||||
if (!gnc_payment_dialog_has_pre_existing_txn(pw))
|
||||
{
|
||||
/* Now apply the payment */
|
||||
gncOwnerApplyPayment (&pw->owner, pw->invoice,
|
||||
post, acc, amount, exch, date, memo, num);
|
||||
}
|
||||
else
|
||||
{
|
||||
// New implementation: Allow the user to have a
|
||||
// pre-selected transaction
|
||||
Transaction *txn = pw->pre_existing_txn;
|
||||
GncOwner *owner = &pw->owner;
|
||||
|
||||
Split *xfer_split = xaccTransFindSplitByAccount(txn, acc);
|
||||
|
||||
if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner))
|
||||
{
|
||||
g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction.");
|
||||
xfer_split = NULL;
|
||||
}
|
||||
|
||||
if (!xfer_split)
|
||||
{
|
||||
g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one.");
|
||||
|
||||
xaccTransBeginEdit (txn);
|
||||
xaccTransDestroy (txn);
|
||||
xaccTransCommitEdit (txn);
|
||||
|
||||
pw->pre_existing_txn = NULL;
|
||||
gncOwnerApplyPayment (owner, pw->invoice,
|
||||
post, acc, amount, exch, date, memo, num);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
xaccTransBeginEdit (txn);
|
||||
while (i < xaccTransCountSplits(txn))
|
||||
{
|
||||
Split *split = xaccTransGetSplit (txn, i);
|
||||
if (split == xfer_split)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
xaccSplitDestroy(split);
|
||||
}
|
||||
}
|
||||
|
||||
// We have opened the txn for editing, and we have deleted all the other splits.
|
||||
gncOwnerAssignPaymentTxn (owner, txn,
|
||||
post, pw->invoice);
|
||||
|
||||
xaccTransCommitEdit (txn);
|
||||
}
|
||||
}
|
||||
/* Create a lot for this payment */
|
||||
payment_lot = gncOwnerCreatePaymentLot (&pw->owner, pw->pre_existing_txn,
|
||||
post, acc, amount, exch, date, memo, num);
|
||||
|
||||
/* And link the selected lots and the payment lot together as well as possible.
|
||||
* If the payment was bigger than the selected documents/overpayments, only
|
||||
* part of the payment will be used. Similarly if more documents were selected
|
||||
* than the payment value set, not all documents will be marked as paid. */
|
||||
if (payment_lot)
|
||||
selected_lots = g_list_prepend (selected_lots, payment_lot);
|
||||
gncOwnerAutoApplyPaymentsWithLots (&pw->owner, selected_lots);
|
||||
}
|
||||
gnc_resume_gui_refresh ();
|
||||
|
||||
@ -1094,6 +1076,8 @@ PaymentWindow * gnc_ui_payment_new_with_txn (GncOwner *owner, Transaction *txn)
|
||||
GDate txn_date = xaccTransGetDatePostedGDate (txn);
|
||||
gnc_ui_payment_window_set_date(pw, &txn_date);
|
||||
}
|
||||
/* XXX It may not always be correct to use the absolute value of amount here
|
||||
* This is an assumption from before the credit notes implementation. */
|
||||
gnc_ui_payment_window_set_amount(pw, gnc_numeric_abs(amount));
|
||||
gnc_ui_payment_window_set_xferaccount(pw, xaccSplitGetAccount(assetaccount_split));
|
||||
if (postaccount_split)
|
||||
|
@ -183,7 +183,7 @@
|
||||
<property name="resizable">True</property>
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="min_width">50</property>
|
||||
<property name="title" translatable="yes">Debet</property>
|
||||
<property name="title" translatable="yes">Debit</property>
|
||||
<property name="reorderable">True</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="docs_list_deb_renderer"/>
|
||||
|
@ -895,6 +895,144 @@ gncOwnerAssignPaymentTxn(const GncOwner *owner, Transaction *txn,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Apply a payment of "amount" for the owner, between the xfer_account
|
||||
* (bank or other asset) and the posted_account (A/R or A/P).
|
||||
*/
|
||||
GNCLot *
|
||||
gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
|
||||
Account *posted_acc, Account *xfer_acc,
|
||||
gnc_numeric amount, gnc_numeric exch, Timespec date,
|
||||
const char *memo, const char *num)
|
||||
{
|
||||
QofBook *book;
|
||||
Split *split;
|
||||
const char *name;
|
||||
gnc_commodity *commodity;
|
||||
gboolean reverse;
|
||||
gnc_numeric payment_value = amount;
|
||||
Split *xfer_split = NULL;
|
||||
GNCLot *payment_lot;
|
||||
|
||||
/* Verify our arguments */
|
||||
if (!owner || !posted_acc || !xfer_acc) return NULL;
|
||||
g_return_val_if_fail (owner->owner.undefined != NULL, NULL);
|
||||
|
||||
/* Compute the ancillary data */
|
||||
book = gnc_account_get_book (posted_acc);
|
||||
name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner));
|
||||
commodity = gncOwnerGetCurrency (owner);
|
||||
reverse = use_reversed_payment_amounts(owner);
|
||||
|
||||
if (txn)
|
||||
{
|
||||
/* Pre-existing transaction was specified. We completely clear it,
|
||||
* except for the split in the transfer account, unless the
|
||||
* transaction can't be reused (wrong currency, wrong transfer account).
|
||||
* In that case, the transaction is simply removed and an new
|
||||
* one created. */
|
||||
|
||||
xfer_split = xaccTransFindSplitByAccount(txn, xfer_acc);
|
||||
|
||||
if (xaccTransGetCurrency(txn) != gncOwnerGetCurrency (owner))
|
||||
{
|
||||
g_message("Uh oh, mismatching currency/commodity between selected transaction and owner. We fall back to manual creation of a new transaction.");
|
||||
xfer_split = NULL;
|
||||
}
|
||||
|
||||
if (!xfer_split)
|
||||
{
|
||||
g_message("Huh? Asset account not found anymore. Fully deleting old txn and now creating a new one.");
|
||||
|
||||
xaccTransBeginEdit (txn);
|
||||
xaccTransDestroy (txn);
|
||||
xaccTransCommitEdit (txn);
|
||||
|
||||
txn = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
xaccTransBeginEdit (txn);
|
||||
while (i < xaccTransCountSplits(txn))
|
||||
{
|
||||
Split *split = xaccTransGetSplit (txn, i);
|
||||
if (split == xfer_split)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
xaccSplitDestroy(split);
|
||||
}
|
||||
}
|
||||
xaccTransCommitEdit (txn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create the transaction if we don't have one yet */
|
||||
if (!txn)
|
||||
txn = xaccMallocTransaction (book);
|
||||
|
||||
/* Insert a split for the transfer account if we don't have one yet */
|
||||
if (!xfer_split)
|
||||
{
|
||||
xaccTransBeginEdit (txn);
|
||||
|
||||
/* Set up the transaction */
|
||||
xaccTransSetDescription (txn, name ? name : "");
|
||||
xaccTransSetNum (txn, num);
|
||||
xaccTransSetCurrency (txn, commodity);
|
||||
xaccTransSetDateEnteredSecs (txn, time(NULL));
|
||||
xaccTransSetDatePostedTS (txn, &date);
|
||||
xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
|
||||
|
||||
|
||||
/* The split for the transfer account */
|
||||
split = xaccMallocSplit (book);
|
||||
xaccSplitSetMemo (split, memo);
|
||||
xaccSplitSetAction (split, _("Payment"));
|
||||
xaccAccountBeginEdit (xfer_acc);
|
||||
xaccAccountInsertSplit (xfer_acc, split);
|
||||
xaccAccountCommitEdit (xfer_acc);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
|
||||
if (gnc_commodity_equal(xaccAccountGetCommodity(xfer_acc), commodity))
|
||||
{
|
||||
xaccSplitSetBaseValue (split, reverse ? amount :
|
||||
gnc_numeric_neg (amount), commodity);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Need to value the payment in terms of the owner commodity */
|
||||
xaccSplitSetAmount(split, reverse ? amount : gnc_numeric_neg (amount));
|
||||
payment_value = gnc_numeric_mul(amount, exch, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP);
|
||||
xaccSplitSetValue(split, reverse ? payment_value : gnc_numeric_neg(payment_value));
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a split in the post account */
|
||||
split = xaccMallocSplit (book);
|
||||
xaccSplitSetMemo (split, memo);
|
||||
xaccSplitSetAction (split, _("Payment"));
|
||||
xaccAccountBeginEdit (posted_acc);
|
||||
xaccAccountInsertSplit (posted_acc, split);
|
||||
xaccAccountCommitEdit (posted_acc);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) : amount, commodity);
|
||||
|
||||
/* Create a new lot for the payment */
|
||||
payment_lot = gnc_lot_new (book);
|
||||
gncOwnerAttachToLot (owner, payment_lot);
|
||||
gnc_lot_add_split (payment_lot, split);
|
||||
|
||||
|
||||
/* Commit this new transaction */
|
||||
xaccTransCommitEdit (txn);
|
||||
|
||||
return payment_lot;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply a payment of "amount" for the owner, between the xfer_account
|
||||
* (bank or other asset) and the posted_account (A/R or A/P).
|
||||
@ -976,8 +1114,8 @@ void gncOwnerAutoApplyPaymentsWithLots (const GncOwner *owner, GList *lots)
|
||||
* perform a balancing action on a set of lots, so you
|
||||
* will also find frequent references to balancing instead. */
|
||||
|
||||
/* Payments can only be applied when at least an owner is given
|
||||
* and either a list of lots to use or a first lot */
|
||||
/* Payments can only be applied when at least an owner
|
||||
* and a list of lots to use are given */
|
||||
if (!owner) return;
|
||||
if (!lots) return;
|
||||
|
||||
|
@ -190,6 +190,19 @@ gboolean gncOwnerGetOwnerFromTypeGuid (QofBook *book, GncOwner *owner, QofIdType
|
||||
/** Get the kvp-frame from the underlying owner object */
|
||||
KvpFrame* gncOwnerGetSlots(GncOwner* owner);
|
||||
|
||||
/**
|
||||
* Create a lot for a payment for the given owner and with the given
|
||||
* parameters. If a transaction is passed, this transaction will be
|
||||
* reused if possible (meaning, if the transaction currency matches
|
||||
* the owner's currency and if the transaction has (at least?) one
|
||||
* split in the transfer account).
|
||||
*/
|
||||
GNCLot *
|
||||
gncOwnerCreatePaymentLot (const GncOwner *owner, Transaction *txn,
|
||||
Account *posted_acc, Account *xfer_acc,
|
||||
gnc_numeric amount, gnc_numeric exch, Timespec date,
|
||||
const char *memo, const char *num);
|
||||
|
||||
/**
|
||||
* Apply a payment of "amount" for the owner, between the xfer_account
|
||||
* (bank or other asset) and the posted_account (A/R or A/P). If the
|
||||
|
Loading…
Reference in New Issue
Block a user