* src/business/business-core/gncInvoice.[ch]: create new function

for logic to apply a payment.  Moved logic from dialog-payment.
	* src/business/business-gnome/dialog-payment.c: move logic to
	  apply payment to business-core.


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@7749 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Derek Atkins 2003-01-02 02:30:44 +00:00
parent 04b99369c6
commit 05809c5a38
4 changed files with 200 additions and 156 deletions

View File

@ -22,6 +22,12 @@
read the value out of the GNCAmountEdit entry when we create
the Query Predicate, because we're not getting the amount_changed
signal. Fixes #101000.
* src/business/business-core/gncInvoice.[ch]: create new function
for logic to apply a payment. Moved logic from dialog-payment.
* src/business/business-gnome/dialog-payment.c: move logic to
apply payment to business-core.
2002-12-30 Benoit Grégoire <bock@step.polymtl.ca>
* src/import-export/hbci/druid-hbci-initial.c

View File

@ -843,6 +843,186 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
return txn;
}
static gboolean
gnc_lot_match_invoice_owner (GNCLot *lot, gpointer user_data)
{
GncOwner owner_def, *owner, *this_owner = user_data;
GncInvoice *invoice;
/* If this lot is not for this owner, then ignore it */
invoice = gncInvoiceGetInvoiceFromLot (lot);
if (invoice) {
owner = gncInvoiceGetOwner (invoice);
owner = gncOwnerGetEndOwner (owner);
} else {
if (!gncOwnerGetOwnerFromLot (lot, &owner_def))
return FALSE;
owner = gncOwnerGetEndOwner (&owner_def);
}
return gncOwnerEqual (owner, this_owner);
}
static gint
gnc_lot_sort_func (GNCLot *a, GNCLot *b)
{
GncInvoice *ia, *ib;
Timespec da, db;
ia = gncInvoiceGetInvoiceFromLot (a);
ib = gncInvoiceGetInvoiceFromLot (b);
da = gncInvoiceGetDateDue (ia);
db = gncInvoiceGetDateDue (ib);
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).
*
* XXX: yes, this should be in gncOwner, but all the other logic is
* in gncInvoice...
*/
Transaction *
gncOwnerApplyPayment (GncOwner *owner, Account *posted_acc, Account *xfer_acc,
gnc_numeric amount, Timespec date,
const char *memo, const char *num)
{
GNCBook *book;
Transaction *txn;
Split *split;
GList *lot_list, *fifo = NULL;
GNCLot *lot, *prepay_lot = NULL;
const char *name;
gnc_commodity *commodity;
gnc_numeric split_amt;
gboolean reverse;
/* 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 = xaccAccountGetBook (posted_acc);
name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
commodity = gncOwnerGetCommodity (owner);
reverse = (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER);
txn = xaccMallocTransaction (book);
xaccTransBeginEdit (txn);
/* Set up the transaction */
xaccTransSetDescription (txn, name);
xaccTransSetNum (txn, num);
xaccTransSetCurrency (txn, commodity);
xaccTransSetDateEnteredTS (txn, &date);
xaccTransSetDatePostedTS (txn, &date);
xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
/* The split for the transfer account */
split = xaccMallocSplit (book);
xaccSplitSetMemo (split, memo);
xaccSplitSetBaseValue (split, reverse ? amount :
gnc_numeric_neg (amount), commodity);
xaccAccountBeginEdit (posted_acc);
xaccAccountInsertSplit (posted_acc, split);
xaccAccountCommitEdit (posted_acc);
xaccTransAppendSplit (txn, 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,
owner,
(GCompareFunc)gnc_lot_sort_func);
xaccAccountBeginEdit (posted_acc);
/* Now iterate over the fifo until the payment is fully applied
* (or all the lots are paid)
*/
for (lot_list = fifo; lot_list; lot_list = lot_list->next) {
gnc_numeric balance;
lot = lot_list->data;
balance = gnc_lot_get_balance (lot);
if (!reverse)
balance = gnc_numeric_neg (balance);
/* If the balance is "negative" then skip this lot.
* (just save the pre-payment lot for later)
*/
if (gnc_numeric_negative_p (balance)) {
if (prepay_lot) {
g_warning ("Multiple pre-payment lots are found. Skipping.");
} else {
prepay_lot = lot;
}
continue;
}
/*
* If the amount <= the balance; we're done -- apply the amount.
* Otherwise, apply the balance, subtract that from the amount,
* and move on to the next one.
*/
if (gnc_numeric_compare (amount, balance) <= 0) {
/* amount <= balance */
split_amt = amount;
} else {
/* amount > balance */
split_amt = balance;
}
/* reduce the amount by split_amt */
amount = gnc_numeric_sub (amount, split_amt, GNC_DENOM_AUTO, GNC_DENOM_LCD);
/* Create the split for this lot in the post account */
split = xaccMallocSplit (book);
xaccSplitSetMemo (split, memo);
xaccSplitSetAction (split, _("Payment"));
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (split_amt) :
split_amt, commodity);
xaccAccountInsertSplit (posted_acc, split);
xaccTransAppendSplit (txn, split);
gnc_lot_add_split (lot, split);
if (gnc_numeric_zero_p (amount))
break;
}
g_list_free (fifo);
/* If there is still money left here, then create a pre-payment lot */
if (gnc_numeric_positive_p (amount)) {
if (prepay_lot == NULL) {
prepay_lot = gnc_lot_new (book);
gncOwnerAttachToLot (owner, prepay_lot);
}
split = xaccMallocSplit (book);
xaccSplitSetMemo (split, memo);
xaccSplitSetAction (split, _("Payment"));
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) :
amount, commodity);
xaccAccountInsertSplit (posted_acc, split);
xaccTransAppendSplit (txn, split);
gnc_lot_add_split (prepay_lot, split);
}
xaccAccountCommitEdit (posted_acc);
/* Commit this new transaction */
xaccTransCommitEdit (txn);
return txn;
}
static gboolean gncInvoiceDateExists (Timespec *date)
{
g_return_val_if_fail (date, FALSE);

View File

@ -78,6 +78,18 @@ gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
Timespec *posted_date, Timespec *due_date,
const char *memo);
/*
* 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).
*
* XXX: yes, this should be in gncOwner, but all the other logic is
* in gncInvoice...
*/
Transaction *
gncOwnerApplyPayment (GncOwner *owner, Account *posted_acc, Account *xfer_acc,
gnc_numeric amount, Timespec date,
const char *memo, const char *num);
/* Given a transaction, find and return the Invoice */
GncInvoice * gncInvoiceGetInvoiceFromTxn (Transaction *txn);

View File

@ -71,41 +71,6 @@ gnc_payment_set_owner (PaymentWindow *pw, GncOwner *owner)
gnc_owner_set_owner (pw->owner_choice, owner);
}
static gboolean
gnc_lot_match_invoice_owner (GNCLot *lot, gpointer user_data)
{
GncOwner owner_def, *owner, *this_owner = user_data;
GncInvoice *invoice;
/* If this lot is not for this owner, then ignore it */
invoice = gncInvoiceGetInvoiceFromLot (lot);
if (invoice) {
owner = gncInvoiceGetOwner (invoice);
owner = gncOwnerGetEndOwner (owner);
} else {
if (!gncOwnerGetOwnerFromLot (lot, &owner_def))
return FALSE;
owner = gncOwnerGetEndOwner (&owner_def);
}
return gncOwnerEqual (owner, this_owner);
}
static gint
gnc_lot_sort_func (GNCLot *a, GNCLot *b)
{
GncInvoice *ia, *ib;
Timespec da, db;
ia = gncInvoiceGetInvoiceFromLot (a);
ib = gncInvoiceGetInvoiceFromLot (b);
da = gncInvoiceGetDateDue (ia);
db = gncInvoiceGetDateDue (ib);
return timespec_cmp (&da, &db);
}
static void
gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
{
@ -165,135 +130,16 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
/* Ok, now post the damn thing */
gnc_suspend_gui_refresh ();
{
Transaction *txn;
Split *split;
GList *lot_list, *fifo = NULL;
GNCLot *lot, *prepay_lot = NULL;
char *memo, *num;
const char *name;
gnc_commodity *commodity;
gnc_numeric split_amt;
Timespec date;
gboolean reverse;
/* 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));
name = gncOwnerGetName (gncOwnerGetEndOwner (&(pw->owner)));
commodity = gncOwnerGetCommodity (&(pw->owner));
reverse = (gncOwnerGetType (&(pw->owner)) == GNC_OWNER_CUSTOMER);
txn = xaccMallocTransaction (pw->book);
xaccTransBeginEdit (txn);
/* Set up the transaction */
xaccTransSetDescription (txn, name);
xaccTransSetNum (txn, num);
xaccTransSetCurrency (txn, commodity);
xaccTransSetDateEnteredTS (txn, &date);
xaccTransSetDatePostedTS (txn, &date);
xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
/* The split for the transfer account */
split = xaccMallocSplit (pw->book);
xaccSplitSetMemo (split, memo);
xaccSplitSetBaseValue (split, reverse ? amount :
gnc_numeric_neg (amount), commodity);
xaccAccountBeginEdit (acc);
xaccAccountInsertSplit (acc, split);
xaccAccountCommitEdit (acc);
xaccTransAppendSplit (txn, 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 (post, gnc_lot_match_invoice_owner,
&pw->owner,
(GCompareFunc)gnc_lot_sort_func);
xaccAccountBeginEdit (post);
/* Now iterate over the fifo until the payment is fully applied
* (or all the lots are paid)
*/
for (lot_list = fifo; lot_list; lot_list = lot_list->next) {
gnc_numeric balance;
lot = lot_list->data;
balance = gnc_lot_get_balance (lot);
if (!reverse)
balance = gnc_numeric_neg (balance);
/* If the balance is "negative" then skip this lot.
* (just save the pre-payment lot for later)
*/
if (gnc_numeric_negative_p (balance)) {
if (prepay_lot) {
g_warning ("Multiple pre-payment lots are found. Skipping.");
} else {
prepay_lot = lot;
}
continue;
}
/*
* If the amount <= the balance; we're done -- apply the amount.
* Otherwise, apply the balance, subtract that from the amount,
* and move on to the next one.
*/
if (gnc_numeric_compare (amount, balance) <= 0) {
/* amount <= balance */
split_amt = amount;
} else {
/* amount > balance */
split_amt = balance;
}
/* reduce the amount by split_amt */
amount = gnc_numeric_sub (amount, split_amt, GNC_DENOM_AUTO,
GNC_DENOM_LCD);
/* Create the split for this lot in the post account */
split = xaccMallocSplit (pw->book);
xaccSplitSetMemo (split, memo);
xaccSplitSetAction (split, _("Payment"));
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (split_amt) :
split_amt, commodity);
xaccAccountInsertSplit (post, split);
xaccTransAppendSplit (txn, split);
gnc_lot_add_split (lot, split);
if (gnc_numeric_zero_p (amount))
break;
}
g_list_free (fifo);
/* If there is still money left here, then create a pre-payment lot */
if (gnc_numeric_positive_p (amount)) {
if (prepay_lot == NULL) {
prepay_lot = gnc_lot_new (pw->book);
gncOwnerAttachToLot (&pw->owner, prepay_lot);
}
split = xaccMallocSplit (pw->book);
xaccSplitSetMemo (split, memo);
xaccSplitSetAction (split, _("Payment"));
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) :
amount, commodity);
xaccAccountInsertSplit (post, split);
xaccTransAppendSplit (txn, split);
gnc_lot_add_split (prepay_lot, split);
}
xaccAccountCommitEdit (post);
/* Commit this new transaction */
xaccTransCommitEdit (txn);
/* Now apply the payment */
gncOwnerApplyPayment (&pw->owner, post, acc, amount, date, memo, num);
}
gnc_resume_gui_refresh ();