From 588ccc145ff2f07856972d40dfd2304978532e9f Mon Sep 17 00:00:00 2001 From: Christian Stimming Date: Fri, 30 Sep 2011 18:47:56 +0000 Subject: [PATCH] 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 --- src/engine/gncOwner.c | 191 +++++++++++++++++++++++++++--------------- src/engine/gncOwner.h | 26 ++++++ 2 files changed, 148 insertions(+), 69 deletions(-) diff --git a/src/engine/gncOwner.c b/src/engine/gncOwner.c index d358e4d024..d285a6edc8 100644 --- a/src/engine/gncOwner.c +++ b/src/engine/gncOwner.c @@ -751,80 +751,62 @@ gnc_lot_sort_func (GNCLot *a, GNCLot *b) return timespec_cmp (&da, &db); } -/* - * 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) +static gboolean use_reversed_payment_amounts(const GncOwner *owner) +{ + g_assert(owner); + return (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER); +} + +gint +gncOwnerAssignPaymentTxn(const GncOwner *owner, Transaction *txn, + Account *posted_account, GncInvoice* invoice) { - QofBook *book; Account *inv_posted_acc; - Transaction *txn; - Split *split; - GList *lot_list, *fifo = NULL; - GNCLot *lot, *inv_posted_lot = NULL, *prepay_lot = NULL; - GncInvoice *this_invoice; - const char *name; - gnc_commodity *commodity; + GList *lot_list, *fifo; + GNCLot *inv_posted_lot = NULL, *prepay_lot = NULL; gnc_numeric split_amt; - gboolean reverse, inv_passed = TRUE; - gnc_numeric payment_value = amount; + gboolean inv_passed = TRUE; + 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 */ - if (!owner || !posted_acc || !xfer_acc) return NULL; - g_return_val_if_fail (owner->owner.undefined != NULL, NULL); + g_assert(owner); + g_assert(txn); + g_assert(xaccTransIsOpen(txn)); + g_assert(posted_account); - /* Compute the ancillary data */ - 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)) + if (txn_commodity != gncOwnerGetCurrency (owner)) { - xaccSplitSetBaseValue (split, reverse ? amount : - gnc_numeric_neg (amount), commodity); + // Uh oh + 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 */ - 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)); + // Uh oh + return result; } + { + // 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 * company and apply the payment on a FIFO basis. Create * 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, (GCompareFunc)gnc_lot_sort_func); @@ -838,7 +820,7 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice, inv_posted_lot = gncInvoiceGetPostedLot(invoice); if (inv_posted_acc && inv_posted_lot && guid_equal(xaccAccountGetGUID(inv_posted_acc), - xaccAccountGetGUID(posted_acc)) && + xaccAccountGetGUID(posted_account)) && !gnc_lot_is_closed(inv_posted_lot)) { /* 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 * (or all the lots are paid) @@ -856,7 +838,7 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice, { 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 * 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); /* Create the split for this lot in the post account */ + ++result; split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); xaccSplitSetAction (split, _("Payment")); - xaccAccountInsertSplit (posted_acc, split); + xaccAccountInsertSplit (posted_account, split); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (split_amt) : - split_amt, commodity); + split_amt, txn_commodity); gnc_lot_add_split (lot, split); /* Now send an event for the invoice so it gets updated as paid */ - this_invoice = gncInvoiceGetInvoiceFromLot(lot); - if (this_invoice) - qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); + { + GncInvoice *this_invoice = gncInvoiceGetInvoiceFromLot(lot); + if (this_invoice) + qof_event_gen (QOF_INSTANCE(this_invoice), QOF_EVENT_MODIFY, NULL); + } if (gnc_numeric_zero_p (payment_value)) break; @@ -946,14 +931,82 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice* invoice, split = xaccMallocSplit (book); xaccSplitSetMemo (split, memo); xaccSplitSetAction (split, _("Pre-Payment")); - xaccAccountInsertSplit (posted_acc, split); + xaccAccountInsertSplit (posted_account, split); xaccTransAppendSplit (txn, split); xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (payment_value) : - payment_value, commodity); + payment_value, txn_commodity); 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 */ xaccTransCommitEdit (txn); diff --git a/src/engine/gncOwner.h b/src/engine/gncOwner.h index 538615f123..ad5f1567b1 100644 --- a/src/engine/gncOwner.h +++ b/src/engine/gncOwner.h @@ -190,6 +190,32 @@ gncOwnerApplyPayment (const GncOwner *owner, GncInvoice *invoice, gnc_numeric amount, gnc_numeric exch, Timespec date, 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 */ GList * gncOwnerGetAccountTypesList (const GncOwner *owner);