* 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 read the value out of the GNCAmountEdit entry when we create
the Query Predicate, because we're not getting the amount_changed the Query Predicate, because we're not getting the amount_changed
signal. Fixes #101000. 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> 2002-12-30 Benoit Grégoire <bock@step.polymtl.ca>
* src/import-export/hbci/druid-hbci-initial.c * src/import-export/hbci/druid-hbci-initial.c

View File

@ -843,6 +843,186 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
return txn; 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) static gboolean gncInvoiceDateExists (Timespec *date)
{ {
g_return_val_if_fail (date, FALSE); g_return_val_if_fail (date, FALSE);

View File

@ -78,6 +78,18 @@ gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
Timespec *posted_date, Timespec *due_date, Timespec *posted_date, Timespec *due_date,
const char *memo); 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 */ /* Given a transaction, find and return the Invoice */
GncInvoice * gncInvoiceGetInvoiceFromTxn (Transaction *txn); 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); 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 static void
gnc_payment_ok_cb (GtkWidget *widget, gpointer data) 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 */ /* Ok, now post the damn thing */
gnc_suspend_gui_refresh (); gnc_suspend_gui_refresh ();
{ {
Transaction *txn;
Split *split;
GList *lot_list, *fifo = NULL;
GNCLot *lot, *prepay_lot = NULL;
char *memo, *num; char *memo, *num;
const char *name;
gnc_commodity *commodity;
gnc_numeric split_amt;
Timespec date; Timespec date;
gboolean reverse;
/* Obtain all our ancillary information */ /* Obtain all our ancillary information */
memo = gtk_entry_get_text (GTK_ENTRY (pw->memo_entry)); memo = gtk_entry_get_text (GTK_ENTRY (pw->memo_entry));
num = gtk_entry_get_text (GTK_ENTRY (pw->num_entry)); num = gtk_entry_get_text (GTK_ENTRY (pw->num_entry));
date = gnc_date_edit_get_date_ts (GNC_DATE_EDIT (pw->date_edit)); 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 */ /* Now apply the payment */
xaccTransSetDescription (txn, name); gncOwnerApplyPayment (&pw->owner, post, acc, amount, date, memo, num);
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);
} }
gnc_resume_gui_refresh (); gnc_resume_gui_refresh ();