Refactor gncOwnerApplyPayment() so that the second part is available as separate function gncOwnerAssignPaymentTxn().

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@21367 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Christian Stimming 2011-09-30 18:47:56 +00:00
parent c8ecb4d937
commit 588ccc145f
2 changed files with 148 additions and 69 deletions

View File

@ -751,80 +751,62 @@ gnc_lot_sort_func (GNCLot *a, GNCLot *b)
return timespec_cmp (&da, &db); return timespec_cmp (&da, &db);
} }
/* static gboolean use_reversed_payment_amounts(const GncOwner *owner)
* 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). g_assert(owner);
*/ return (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER);
Transaction * }
gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
Account *posted_acc, Account *xfer_acc, gint
gnc_numeric amount, gnc_numeric exch, Timespec date, gncOwnerAssignPaymentTxn(const GncOwner *owner, Transaction *txn,
const char *memo, const char *num) Account *posted_account, GncInvoice* invoice)
{ {
QofBook *book;
Account *inv_posted_acc; Account *inv_posted_acc;
Transaction *txn; GList *lot_list, *fifo;
Split *split; GNCLot *inv_posted_lot = NULL, *prepay_lot = NULL;
GList *lot_list, *fifo = NULL;
GNCLot *lot, *inv_posted_lot = NULL, *prepay_lot = NULL;
GncInvoice *this_invoice;
const char *name;
gnc_commodity *commodity;
gnc_numeric split_amt; gnc_numeric split_amt;
gboolean reverse, inv_passed = TRUE; gboolean inv_passed = TRUE;
gnc_numeric payment_value = amount; Split *split;
QofBook *book = gnc_account_get_book(posted_account);
gnc_commodity *txn_commodity = xaccTransGetCurrency(txn);
gint result = 0;
gnc_numeric payment_value;
const char *memo;
gboolean reverse = use_reversed_payment_amounts(owner);
/* Verify our arguments */ g_assert(owner);
if (!owner || !posted_acc || !xfer_acc) return NULL; g_assert(txn);
g_return_val_if_fail (owner->owner.undefined != NULL, NULL); g_assert(xaccTransIsOpen(txn));
g_assert(posted_account);
/* Compute the ancillary data */ if (txn_commodity != gncOwnerGetCurrency (owner))
book = gnc_account_get_book (posted_acc);
name = gncOwnerGetName (gncOwnerGetEndOwner ((GncOwner*)owner));
commodity = gncOwnerGetCurrency (owner);
reverse = (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER);
txn = xaccMallocTransaction (book);
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 : // Uh oh
gnc_numeric_neg (amount), commodity); return result;
} }
else // We require exactly one split in the transaction
if (xaccTransCountSplits(txn) != 1)
{ {
/* Need to value the payment in terms of the owner commodity */ // Uh oh
xaccSplitSetAmount(split, reverse ? amount : gnc_numeric_neg (amount)); return result;
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));
} }
{
// Retrieve the payment value from the existing first split.
Split *asset_split = xaccTransGetSplit(txn, 0);
g_assert(asset_split);
payment_value = xaccSplitGetValue(asset_split);
if (!reverse)
payment_value = gnc_numeric_neg(payment_value);
memo = xaccSplitGetMemo(asset_split);
}
/* Now, find all "open" lots in the posting account for this /* Now, find all "open" lots in the posting account for this
* company and apply the payment on a FIFO basis. Create * company and apply the payment on a FIFO basis. Create
* a new split for each open lot until the payment is gone. * a new split for each open lot until the payment is gone.
*/ */
fifo = xaccAccountFindOpenLots (posted_acc, gnc_lot_match_invoice_owner, fifo = xaccAccountFindOpenLots (posted_account, gnc_lot_match_invoice_owner,
(gpointer)owner, (gpointer)owner,
(GCompareFunc)gnc_lot_sort_func); (GCompareFunc)gnc_lot_sort_func);
@ -838,7 +820,7 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
inv_posted_lot = gncInvoiceGetPostedLot(invoice); inv_posted_lot = gncInvoiceGetPostedLot(invoice);
if (inv_posted_acc && inv_posted_lot && if (inv_posted_acc && inv_posted_lot &&
guid_equal(xaccAccountGetGUID(inv_posted_acc), guid_equal(xaccAccountGetGUID(inv_posted_acc),
xaccAccountGetGUID(posted_acc)) && xaccAccountGetGUID(posted_account)) &&
!gnc_lot_is_closed(inv_posted_lot)) !gnc_lot_is_closed(inv_posted_lot))
{ {
/* Put this invoice at the beginning of the FIFO */ /* Put this invoice at the beginning of the FIFO */
@ -847,7 +829,7 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
} }
} }
xaccAccountBeginEdit (posted_acc); xaccAccountBeginEdit (posted_account);
/* Now iterate over the fifo until the payment is fully applied /* Now iterate over the fifo until the payment is fully applied
* (or all the lots are paid) * (or all the lots are paid)
@ -856,7 +838,7 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
{ {
gnc_numeric balance; gnc_numeric balance;
lot = lot_list->data; GNCLot *lot = lot_list->data;
/* Skip this lot if it matches the invoice that was passed in and /* Skip this lot if it matches the invoice that was passed in and
* we've seen it already. This way we post to it the first time * we've seen it already. This way we post to it the first time
@ -914,19 +896,22 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
payment_value = gnc_numeric_sub (payment_value, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); payment_value = gnc_numeric_sub (payment_value, split_amt, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD);
/* Create the split for this lot in the post account */ /* Create the split for this lot in the post account */
++result;
split = xaccMallocSplit (book); split = xaccMallocSplit (book);
xaccSplitSetMemo (split, memo); xaccSplitSetMemo (split, memo);
xaccSplitSetAction (split, _("Payment")); xaccSplitSetAction (split, _("Payment"));
xaccAccountInsertSplit (posted_acc, split); xaccAccountInsertSplit (posted_account, split);
xaccTransAppendSplit (txn, split); xaccTransAppendSplit (txn, split);
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (split_amt) : xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (split_amt) :
split_amt, commodity); split_amt, txn_commodity);
gnc_lot_add_split (lot, split); gnc_lot_add_split (lot, split);
/* Now send an event for the invoice so it gets updated as paid */ /* Now send an event for the invoice so it gets updated as paid */
this_invoice = gncInvoiceGetInvoiceFromLot(lot); {
if (this_invoice) GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(lot);
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); if (this_invoice)
qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL);
}
if (gnc_numeric_zero_p (payment_value)) if (gnc_numeric_zero_p (payment_value))
break; break;
@ -946,14 +931,82 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
split = xaccMallocSplit (book); split = xaccMallocSplit (book);
xaccSplitSetMemo (split, memo); xaccSplitSetMemo (split, memo);
xaccSplitSetAction (split, _("Pre-Payment")); xaccSplitSetAction (split, _("Pre-Payment"));
xaccAccountInsertSplit (posted_acc, split); xaccAccountInsertSplit (posted_account, split);
xaccTransAppendSplit (txn, split); xaccTransAppendSplit (txn, split);
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (payment_value) : xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (payment_value) :
payment_value, commodity); payment_value, txn_commodity);
gnc_lot_add_split (prepay_lot, split); gnc_lot_add_split (prepay_lot, split);
} }
xaccAccountCommitEdit (posted_acc); xaccAccountCommitEdit (posted_account);
return result;
}
/*
* 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).
*/
Transaction *
gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice,
Account *posted_acc, Account *xfer_acc,
gnc_numeric amount, gnc_numeric exch, Timespec date,
const char *memo, const char *num)
{
QofBook *book;
Transaction *txn;
Split *split;
const char *name;
gnc_commodity *commodity;
gboolean reverse;
gnc_numeric payment_value = amount;
/* 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);
txn = xaccMallocTransaction (book);
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));
}
gncOwnerAssignPaymentTxn(owner, txn, posted_acc, invoice);
/* Commit this new transaction */ /* Commit this new transaction */
xaccTransCommitEdit (txn); xaccTransCommitEdit (txn);

View File

@ -190,6 +190,32 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice *invoice,
gnc_numeric amount, gnc_numeric exch, Timespec date, gnc_numeric amount, gnc_numeric exch, Timespec date,
const char *memo, const char *num); const char *memo, const char *num);
/**
* Fill in a half-finished payment transaction for the owner. The
* transaction txn must already contain one split that belongs to a
* bank or other asset account. This function will add the other split
* (or splits) that go to the posted_account (A/R or A/P), including
* the linking to the lots so that the payment is recorded in the
* correct lot(s).
*
* If the caller supplies an (optional) invoice argument, then apply
* the payment to that invoice first before any other invoice.
*
* Preconditions: The arguments owner, txn, and posted_account must
* not be NULL. The txn must be open (by xaccTransBeginEdit()); it
* must contain exactly one split; its commodity (by
* xaccTransGetCurrency()) must be equal to the owner's commodity (by
* gncOwnerGetCurrency()).
*
* \return The number of splits that have been assigned as owner
* payments. On success, this is always positive (1 or larger). In
* case of failure (due to unfulfilled conditions on the input
* values), null is returned.
*/
gint
gncOwnerAssignPaymentTxn(const GncOwner *owner, Transaction *txn,
Account *posted_account, GncInvoice* invoice);
/** 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);