mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
* gncOwner.[ch]: Provide functions to store/lookup an owner in a
Lot's kvp_frame. * gncInvoice.c: add implemention of GetPostedLot(); when posting to an Account, look for any pre-payment Lots and post to that. If the Invoice total < pre-payment, then autmatically forward the payments to a future Lot. If no pre-payment Lot is found, then create a new one. * dialog-payment.c: force payment amounts to be positive values (you cannot un-pay via this dialog). Apply payments to open Invoice Lots in a FIFO manner (by Due Date). If there is anything left over, then create a pre-payment Lot. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@7024 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
parent
131add077d
commit
ec0dbe6b44
16
ChangeLog
16
ChangeLog
@ -1,3 +1,19 @@
|
||||
2002-06-25 Derek Atkins <derek@ihtfp.com>
|
||||
|
||||
* gncOwner.[ch]: Provide functions to store/lookup an owner in a
|
||||
Lot's kvp_frame.
|
||||
|
||||
* gncInvoice.c: add implemention of GetPostedLot(); when posting
|
||||
to an Account, look for any pre-payment Lots and post to that. If
|
||||
the Invoice total < pre-payment, then autmatically forward the
|
||||
payments to a future Lot. If no pre-payment Lot is found, then
|
||||
create a new one.
|
||||
|
||||
* dialog-payment.c: force payment amounts to be positive values
|
||||
(you cannot un-pay via this dialog). Apply payments to open
|
||||
Invoice Lots in a FIFO manner (by Due Date). If there is anything
|
||||
left over, then create a pre-payment Lot.
|
||||
|
||||
2002-06-24 David Hampton <hampton@employees.org>
|
||||
|
||||
* src/import-export/ofx/Makefile.am:
|
||||
|
@ -366,6 +366,12 @@ gnc_commodity * gncInvoiceGetCommonCommodity (GncInvoice *invoice)
|
||||
return invoice->common_commodity;
|
||||
}
|
||||
|
||||
GNCLot * gncInvoiceGetPostedLot (GncInvoice *invoice)
|
||||
{
|
||||
if (!invoice) return NULL;
|
||||
return invoice->posted_lot;
|
||||
}
|
||||
|
||||
Transaction * gncInvoiceGetPostedTxn (GncInvoice *invoice)
|
||||
{
|
||||
if (!invoice) return NULL;
|
||||
@ -475,12 +481,41 @@ GncInvoice * gncInvoiceGetInvoiceFromTxn (Transaction *txn)
|
||||
guid, _GNC_MOD_NAME);
|
||||
}
|
||||
|
||||
struct lotmatch {
|
||||
GncOwner *owner;
|
||||
gboolean reverse;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
gnc_lot_match_owner_payment (GNCLot *lot, gpointer user_data)
|
||||
{
|
||||
struct lotmatch *lm = user_data;
|
||||
GncOwner owner_def, *owner;
|
||||
gnc_numeric balance = gnc_lot_get_balance (lot);
|
||||
|
||||
/* Is this a payment lot */
|
||||
if (gnc_numeric_positive_p (lm->reverse ? balance :
|
||||
gnc_numeric_neg (balance)))
|
||||
return FALSE;
|
||||
|
||||
/* Is there an invoice attached? */
|
||||
if (gncInvoiceGetInvoiceFromLot (lot))
|
||||
return FALSE;
|
||||
|
||||
/* Is it ours? */
|
||||
if (!gncOwnerGetOwnerFromLot (lot, &owner_def))
|
||||
return FALSE;
|
||||
owner = gncOwnerGetEndOwner (&owner_def);
|
||||
|
||||
return gncOwnerEqual (owner, lm->owner);
|
||||
}
|
||||
|
||||
Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
|
||||
Timespec *post_date, Timespec *due_date,
|
||||
const char * memo)
|
||||
{
|
||||
Transaction *txn;
|
||||
GNCLot *lot;
|
||||
GNCLot *lot = NULL;
|
||||
GList *iter;
|
||||
GList *splitinfo = NULL;
|
||||
gnc_numeric total;
|
||||
@ -497,8 +532,25 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
|
||||
/* Figure out if we need to "reverse" the numbers. */
|
||||
reverse = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
|
||||
|
||||
/* Create a new lot for this invoice */
|
||||
lot = gnc_lot_new (invoice->book);
|
||||
/* Find an existing payment-lot for this owner */
|
||||
{
|
||||
LotList *lot_list;
|
||||
struct lotmatch lm;
|
||||
|
||||
lm.reverse = reverse;
|
||||
lm.owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
|
||||
|
||||
lot_list = xaccAccountFindOpenLots (acc, gnc_lot_match_owner_payment,
|
||||
&lm, NULL);
|
||||
if (lot_list)
|
||||
lot = lot_list->data;
|
||||
|
||||
g_list_free (lot_list);
|
||||
}
|
||||
|
||||
/* Create a new lot for this invoice, if we need to do so */
|
||||
if (!lot)
|
||||
lot = gnc_lot_new (invoice->book);
|
||||
|
||||
/* Create a new transaction */
|
||||
txn = xaccMallocTransaction (invoice->book);
|
||||
@ -583,9 +635,6 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
|
||||
{
|
||||
Split *split = xaccMallocSplit (invoice->book);
|
||||
|
||||
/* add this split to the lot */
|
||||
gnc_lot_add_split (lot, split);
|
||||
|
||||
/* Set action/memo */
|
||||
xaccSplitSetMemo (split, memo);
|
||||
xaccSplitSetAction (split, gncInvoiceGetType (invoice));
|
||||
@ -596,6 +645,9 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
|
||||
xaccAccountInsertSplit (acc, split);
|
||||
xaccAccountCommitEdit (acc);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
|
||||
/* add this split to the lot */
|
||||
gnc_lot_add_split (lot, split);
|
||||
}
|
||||
|
||||
/* Now attach this invoice to the txn, lot, and account */
|
||||
@ -607,6 +659,59 @@ Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc,
|
||||
|
||||
gncAccountValueDestroy (splitinfo);
|
||||
|
||||
/* check the lot -- if we still look like a payment lot, then that
|
||||
* means we need to create a balancing split and create a new payment
|
||||
* lot for the next invoice
|
||||
*/
|
||||
total = gnc_lot_get_balance (lot);
|
||||
if (!reverse)
|
||||
total = gnc_numeric_neg (total);
|
||||
|
||||
if (gnc_numeric_negative_p (total)) {
|
||||
Transaction *t2;
|
||||
GNCLot *lot2;
|
||||
Split *split;
|
||||
char *memo2 = _("Automatic Payment Forward");
|
||||
|
||||
t2 = xaccMallocTransaction (invoice->book);
|
||||
lot2 = gnc_lot_new (invoice->book);
|
||||
gncOwnerAttachToLot (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)),
|
||||
lot2);
|
||||
|
||||
xaccTransBeginEdit (t2);
|
||||
xaccAccountBeginEdit (acc);
|
||||
|
||||
/* Set Transaction Description (Owner Name), Currency */
|
||||
xaccTransSetDescription (t2, name);
|
||||
xaccTransSetCurrency (t2, invoice->common_commodity);
|
||||
|
||||
/* Entered and Posted at date */
|
||||
if (post_date) {
|
||||
xaccTransSetDateEnteredTS (t2, post_date);
|
||||
xaccTransSetDatePostedTS (t2, post_date);
|
||||
}
|
||||
|
||||
/* Balance out this lot */
|
||||
split = xaccMallocSplit (invoice->book);
|
||||
xaccSplitSetMemo (split, memo2);
|
||||
xaccSplitSetBaseValue (split, gnc_numeric_neg (total),
|
||||
invoice->common_commodity);
|
||||
xaccAccountInsertSplit (acc, split);
|
||||
xaccTransAppendSplit (t2, split);
|
||||
gnc_lot_add_split (lot, split);
|
||||
|
||||
/* And apply the pre-payment to a new lot */
|
||||
split = xaccMallocSplit (invoice->book);
|
||||
xaccSplitSetMemo (split, memo2);
|
||||
xaccSplitSetBaseValue (split, total, invoice->common_commodity);
|
||||
xaccAccountInsertSplit (acc, split);
|
||||
xaccTransAppendSplit (t2, split);
|
||||
gnc_lot_add_split (lot2, split);
|
||||
|
||||
xaccTransCommitEdit (t2);
|
||||
xaccAccountCommitEdit (acc);
|
||||
}
|
||||
|
||||
return txn;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,10 @@
|
||||
|
||||
#define _GNC_MOD_NAME GNC_OWNER_MODULE_NAME
|
||||
|
||||
#define GNC_OWNER_ID "gncOwner"
|
||||
#define GNC_OWNER_TYPE "owner-type"
|
||||
#define GNC_OWNER_GUID "owner-guid"
|
||||
|
||||
GncOwner * gncOwnerCreate (void)
|
||||
{
|
||||
GncOwner *o = g_new0 (GncOwner, 1);
|
||||
@ -203,6 +207,68 @@ const GUID * gncOwnerGetEndGUID (GncOwner *owner)
|
||||
return gncOwnerGetGUID (owner);
|
||||
}
|
||||
|
||||
void gncOwnerAttachToLot (GncOwner *owner, GNCLot *lot)
|
||||
{
|
||||
kvp_frame *kvp;
|
||||
kvp_value *value;
|
||||
|
||||
if (!owner || !lot)
|
||||
return;
|
||||
|
||||
kvp = gnc_lot_get_slots (lot);
|
||||
|
||||
value = kvp_value_new_gint64 (gncOwnerGetType (owner));
|
||||
kvp_frame_set_slot_path (kvp, value, GNC_OWNER_ID, GNC_OWNER_TYPE, NULL);
|
||||
kvp_value_delete (value);
|
||||
|
||||
value = kvp_value_new_guid (gncOwnerGetGUID (owner));
|
||||
kvp_frame_set_slot_path (kvp, value, GNC_OWNER_ID, GNC_OWNER_GUID, NULL);
|
||||
kvp_value_delete (value);
|
||||
|
||||
}
|
||||
|
||||
gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner)
|
||||
{
|
||||
kvp_frame *kvp;
|
||||
kvp_value *value;
|
||||
GUID *guid;
|
||||
GNCBook *book;
|
||||
GncOwnerType type;
|
||||
|
||||
if (!lot) return FALSE;
|
||||
|
||||
book = gnc_lot_get_book (lot);
|
||||
kvp = gnc_lot_get_slots (lot);
|
||||
|
||||
value = kvp_frame_get_slot_path (kvp, GNC_OWNER_ID, GNC_OWNER_TYPE, NULL);
|
||||
if (!value) return FALSE;
|
||||
|
||||
type = kvp_value_get_gint64 (value);
|
||||
|
||||
value = kvp_frame_get_slot_path (kvp, GNC_OWNER_ID, GNC_OWNER_GUID, NULL);
|
||||
if (!value) return FALSE;
|
||||
|
||||
guid = kvp_value_get_guid (value);
|
||||
if (!guid)
|
||||
return FALSE;
|
||||
|
||||
switch (type) {
|
||||
case GNC_OWNER_CUSTOMER:
|
||||
gncOwnerInitCustomer (owner, gncCustomerLookup (book, guid));
|
||||
break;
|
||||
case GNC_OWNER_VENDOR:
|
||||
gncOwnerInitVendor (owner, gncVendorLookup (book, guid));
|
||||
break;
|
||||
case GNC_OWNER_JOB:
|
||||
gncOwnerInitJob (owner, gncJobLookup (book, guid));
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return (owner->owner.undefined != NULL);
|
||||
}
|
||||
|
||||
gboolean gncOwnerRegister (void)
|
||||
{
|
||||
static QueryObjectDef params[] = {
|
||||
|
@ -14,6 +14,7 @@ typedef struct gnc_owner_s GncOwner;
|
||||
#include "gncCustomer.h"
|
||||
#include "gncJob.h"
|
||||
#include "gncVendor.h"
|
||||
#include "gnc-lot.h"
|
||||
|
||||
typedef enum {
|
||||
GNC_OWNER_NONE,
|
||||
@ -61,6 +62,14 @@ const GUID * gncOwnerGetGUID (GncOwner *owner);
|
||||
GncOwner * gncOwnerGetEndOwner (GncOwner *owner);
|
||||
const GUID * gncOwnerGetEndGUID (GncOwner *owner);
|
||||
|
||||
/* attach an owner to a lot */
|
||||
void gncOwnerAttachToLot (GncOwner *owner, GNCLot *lot);
|
||||
|
||||
/* Get the owner from the lot. If an owner is found in the lot,
|
||||
* fill in "owner" and return TRUE. Otherwise return FALSE.
|
||||
*/
|
||||
gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner);
|
||||
|
||||
#define OWNER_TYPE "type"
|
||||
#define OWNER_CUSTOMER "customer"
|
||||
#define OWNER_JOB "job"
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "Account.h"
|
||||
#include "gnc-numeric.h"
|
||||
|
||||
#include "gncInvoice.h"
|
||||
|
||||
#include "dialog-payment.h"
|
||||
#include "business-utils.h"
|
||||
|
||||
@ -69,6 +71,41 @@ 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)
|
||||
{
|
||||
@ -82,9 +119,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));
|
||||
if (gnc_numeric_check (amount) || gnc_numeric_zero_p (amount)) {
|
||||
if (gnc_numeric_check (amount) || !gnc_numeric_positive_p (amount)) {
|
||||
text = _("You must enter the amount of the payment. "
|
||||
"Payments may not be zero.");
|
||||
"The payment amount must be greater than zero.");
|
||||
gnc_error_dialog_parented (GTK_WINDOW (pw->dialog), text);
|
||||
return;
|
||||
}
|
||||
@ -126,12 +163,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;
|
||||
|
||||
@ -164,25 +205,97 @@ gnc_payment_ok_cb (GtkWidget *widget, gpointer data)
|
||||
xaccAccountCommitEdit (acc);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
|
||||
/* The split for the posting account */
|
||||
split = xaccMallocSplit (pw->book);
|
||||
xaccSplitSetMemo (split, memo);
|
||||
xaccSplitSetAction (split, _("Payment"));
|
||||
xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) :
|
||||
amount, commodity);
|
||||
xaccAccountBeginEdit (post);
|
||||
xaccAccountInsertSplit (post, split);
|
||||
xaccAccountCommitEdit (post);
|
||||
xaccTransAppendSplit (txn, split);
|
||||
|
||||
/*
|
||||
* Attach the OWNER to the transaction? Or, better yet, attach to
|
||||
* the proper lots from this owner in this post account
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
/* Commit it */
|
||||
|
||||
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_ui_payment_window_destroy (pw);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user