Invoice
[Business]


Detailed Description

An invoice holds a list of entries, a pointer to the customer, and the job, the dates entered and posted, as well as the account, transaction and lot for the posted invoice.


Files

file  gncInvoice.h
 Business Invoice Interface.

Create/Destroy Functions

GncInvoicegncInvoiceCreate (QofBook *book)
void gncInvoiceDestroy (GncInvoice *invoice)

Set Functions

void gncInvoiceSetID (GncInvoice *invoice, const char *id)
void gncInvoiceSetOwner (GncInvoice *invoice, GncOwner *owner)
void gncInvoiceSetDateOpened (GncInvoice *invoice, Timespec date)
void gncInvoiceSetDatePosted (GncInvoice *invoice, Timespec date)
void gncInvoiceSetTerms (GncInvoice *invoice, GncBillTerm *terms)
void gncInvoiceSetBillingID (GncInvoice *invoice, const char *billing_id)
void gncInvoiceSetNotes (GncInvoice *invoice, const char *notes)
void gncInvoiceSetCurrency (GncInvoice *invoice, gnc_commodity *currency)
void gncInvoiceSetActive (GncInvoice *invoice, gboolean active)
void gncInvoiceSetBillTo (GncInvoice *invoice, GncOwner *billto)
void gncInvoiceSetToChargeAmount (GncInvoice *invoice, gnc_numeric amount)

Get Functions

const char * gncInvoiceGetID (GncInvoice *invoice)
GncOwnergncInvoiceGetOwner (GncInvoice *invoice)
Timespec gncInvoiceGetDateOpened (GncInvoice *invoice)
Timespec gncInvoiceGetDatePosted (GncInvoice *invoice)
Timespec gncInvoiceGetDateDue (GncInvoice *invoice)
GncBillTermgncInvoiceGetTerms (GncInvoice *invoice)
const char * gncInvoiceGetBillingID (GncInvoice *invoice)
const char * gncInvoiceGetNotes (GncInvoice *invoice)
const char * gncInvoiceGetType (GncInvoice *invoice)
gnc_commoditygncInvoiceGetCurrency (GncInvoice *invoice)
GncOwnergncInvoiceGetBillTo (GncInvoice *invoice)
gnc_numeric gncInvoiceGetToChargeAmount (GncInvoice *invoice)
gboolean gncInvoiceGetActive (GncInvoice *invoice)
GNCLotgncInvoiceGetPostedLot (GncInvoice *invoice)
TransactiongncInvoiceGetPostedTxn (GncInvoice *invoice)
AccountgncInvoiceGetPostedAcc (GncInvoice *invoice)

Defines

#define GNC_ID_INVOICE   "gncInvoice"
#define GNC_TYPE_INVOICE   (gnc_invoice_get_type ())
#define GNC_INVOICE(o)   (G_TYPE_CHECK_INSTANCE_CAST ((o), GNC_TYPE_INVOICE, GncInvoice))
#define GNC_INVOICE_CLASS(k)   (G_TYPE_CHECK_CLASS_CAST((k), GNC_TYPE_INVOICE, GncInvoiceClass))
#define GNC_IS_INVOICE(o)   (G_TYPE_CHECK_INSTANCE_TYPE ((o), GNC_TYPE_INVOICE))
#define GNC_IS_INVOICE_CLASS(k)   (G_TYPE_CHECK_CLASS_TYPE ((k), GNC_TYPE_INVOICE))
#define GNC_INVOICE_GET_CLASS(o)   (G_TYPE_INSTANCE_GET_CLASS ((o), GNC_TYPE_INVOICE, GncInvoiceClass))
#define gncInvoiceLookup(book, guid)   QOF_BOOK_LOOKUP_ENTITY((book),(guid),GNC_ID_INVOICE, GncInvoice)
#define INVOICE_ID   "id"
#define INVOICE_OWNER   "owner"
#define INVOICE_OPENED   "date_opened"
#define INVOICE_POSTED   "date_posted"
#define INVOICE_DUE   "date_due"
#define INVOICE_IS_POSTED   "is_posted?"
#define INVOICE_IS_PAID   "is_paid?"
#define INVOICE_TERMS   "terms"
#define INVOICE_BILLINGID   "billing_id"
#define INVOICE_NOTES   "notes"
#define INVOICE_ACC   "account"
#define INVOICE_POST_TXN   "posted_txn"
#define INVOICE_POST_LOT   "posted_lot"
#define INVOICE_TYPE   "type"
#define INVOICE_BILLTO   "bill-to"
#define INVOICE_ENTRIES   "list_of_entries"
#define INVOICE_JOB   "invoice_job"
#define INVOICE_FROM_LOT   "invoice-from-lot"
#define INVOICE_FROM_TXN   "invoice-from-txn"
#define gncInvoiceGetGUID(x)   qof_instance_get_guid(QOF_INSTANCE(x))
#define gncInvoiceRetGUID(x)   (x ? *(qof_instance_get_guid(QOF_INSTANCE(x))) : *(guid_null()))
#define gncInvoiceLookupDirect(G, B)   gncInvoiceLookup((B),&(G))

Typedefs

typedef _gncInvoice GncInvoice
typedef _gncInvoiceClass GncInvoiceClass
typedef GList EntryList

Functions

GType gnc_invoice_get_type (void)
void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry)
void gncInvoiceRemoveEntry (GncInvoice *invoice, GncEntry *entry)
void gncBillAddEntry (GncInvoice *bill, GncEntry *entry)
void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry)
void gncInvoiceSortEntries (GncInvoice *invoice)
gnc_numeric gncInvoiceGetTotal (GncInvoice *invoice)
gnc_numeric gncInvoiceGetTotalOf (GncInvoice *invoice, GncEntryPaymentType type)
gnc_numeric gncInvoiceGetTotalSubtotal (GncInvoice *invoice)
gnc_numeric gncInvoiceGetTotalTax (GncInvoice *invoice)
EntryListgncInvoiceGetEntries (GncInvoice *invoice)
TransactiongncInvoicePostToAccount (GncInvoice *invoice, Account *acc, Timespec *posted_date, Timespec *due_date, const char *memo, gboolean accumulatesplits)
gboolean gncInvoiceUnpost (GncInvoice *invoice, gboolean reset_tax_tables)
TransactiongncOwnerApplyPayment (GncOwner *owner, GncInvoice *invoice, Account *posted_acc, Account *xfer_acc, gnc_numeric amount, Timespec date, const char *memo, const char *num)
GncInvoicegncInvoiceGetInvoiceFromTxn (Transaction *txn)
GncInvoicegncInvoiceGetInvoiceFromLot (GNCLot *lot)
void gncInvoiceBeginEdit (GncInvoice *invoice)
void gncInvoiceCommitEdit (GncInvoice *invoice)
int gncInvoiceCompare (GncInvoice *a, GncInvoice *b)
gboolean gncInvoiceIsPosted (GncInvoice *invoice)
gboolean gncInvoiceIsPaid (GncInvoice *invoice)
QofBookgncInvoiceGetBook (GncInvoice *x)


Define Documentation

#define gncInvoiceGetGUID (  )     qof_instance_get_guid(QOF_INSTANCE(x))

deprecated functions

Definition at line 212 of file gncInvoice.h.

#define gncInvoiceLookup ( book,
guid   )     QOF_BOOK_LOOKUP_ENTITY((book),(guid),GNC_ID_INVOICE, GncInvoice)

Return a pointer to the instance gncInvoice that is identified by the guid, and is residing in the book. Returns NULL if the instance can't be found. Equivalent function prototype is GncInvoice * gncInvoiceLookup (QofBook *book, const GUID *guid);

Definition at line 179 of file gncInvoice.h.


Function Documentation

void gncBillAddEntry ( GncInvoice bill,
GncEntry entry 
)

Call this function when adding an entry to a bill instead of an invoice

Definition at line 436 of file gncInvoice.c.

00437 {
00438   GncInvoice *old;
00439 
00440   if (!bill || !entry) return;
00441 
00442   old = gncEntryGetBill (entry);
00443   if (old == bill) return;      /* I already own this one */
00444   if (old) gncBillRemoveEntry (old, entry);
00445 
00446   gncEntrySetBill (entry, bill);
00447   bill->entries = g_list_insert_sorted (bill->entries, entry,
00448                                            (GCompareFunc)gncEntryCompare);
00449   mark_invoice (bill);
00450 }

GncInvoice* gncInvoiceGetInvoiceFromLot ( GNCLot lot  ) 

Given a LOT, find and return the Invoice attached to the lot

Definition at line 769 of file gncInvoice.c.

00770 {
00771   KvpFrame *kvp;
00772   KvpValue *value;
00773   GUID *guid;
00774   QofBook *book;
00775 
00776   if (!lot) return NULL;
00777 
00778   book = gnc_lot_get_book (lot);
00779   kvp = gnc_lot_get_slots (lot);
00780   value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL);
00781   if (!value) return NULL;
00782 
00783   guid = kvp_value_get_guid (value);
00784   return gncInvoiceLookup(book, guid);
00785 }

GncInvoice* gncInvoiceGetInvoiceFromTxn ( Transaction txn  ) 

Given a transaction, find and return the Invoice

Definition at line 809 of file gncInvoice.c.

00810 {
00811   KvpFrame *kvp;
00812   KvpValue *value;
00813   GUID *guid;
00814   QofBook *book;
00815 
00816   if (!txn) return NULL;
00817 
00818   book = xaccTransGetBook (txn);
00819   kvp = xaccTransGetSlots (txn);
00820   value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL);
00821   if (!value) return NULL;
00822 
00823   guid = kvp_value_get_guid (value);
00824   return gncInvoiceLookup(book,guid);
00825 }

gnc_numeric gncInvoiceGetTotal ( GncInvoice invoice  ) 

return the "total" amount of the invoice

Definition at line 592 of file gncInvoice.c.

00593 {
00594   if (!invoice) return gnc_numeric_zero();
00595   return gncInvoiceGetTotalInternal(invoice, TRUE, TRUE, FALSE, 0);
00596 }

Transaction* gncInvoicePostToAccount ( GncInvoice invoice,
Account acc,
Timespec posted_date,
Timespec due_date,
const char *  memo,
gboolean  accumulatesplits 
)

Post this invoice to an account. Returns the new Transaction that is tied to this invoice. The transaction is set with the supplied posted date, due date, and memo. The Transaction description is set to the name of the company.

Definition at line 856 of file gncInvoice.c.

00859 {
00860   Transaction *txn;
00861   QofBook *book;
00862   GNCLot *lot = NULL;
00863   GList *iter;
00864   GList *splitinfo = NULL;
00865   gnc_numeric total;
00866   gboolean reverse;
00867   const char *name, *type;
00868   char *lot_title;
00869   Account *ccard_acct = NULL;
00870   GncOwner *owner;
00871 
00872   if (!invoice || !acc) return NULL;
00873 
00874   gncInvoiceBeginEdit (invoice);
00875   book = qof_instance_get_book(invoice);
00876 
00877   /* Stabilize the Billing Terms of this invoice */
00878   if (invoice->terms)
00879     gncInvoiceSetTerms (invoice,
00880                         gncBillTermReturnChild (invoice->terms, TRUE));
00881 
00882   /* Figure out if we need to "reverse" the numbers. */
00883   reverse = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER);
00884 
00885   /* Figure out if we need to separate out "credit-card" items */
00886   owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
00887   if (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_EMPLOYEE)
00888     ccard_acct = gncEmployeeGetCCard (gncOwnerGetEmployee (owner));
00889 
00890   /* Find an existing payment-lot for this owner */
00891   {
00892     LotList *lot_list;
00893     struct lotmatch lm;
00894 
00895     lm.reverse = reverse;
00896     lm.owner = owner;
00897 
00898     lot_list = xaccAccountFindOpenLots (acc, gnc_lot_match_owner_payment,
00899                                         &lm, NULL);
00900     if (lot_list)
00901       lot = lot_list->data;
00902 
00903     g_list_free (lot_list);
00904   }
00905 
00906   /* Create a new lot for this invoice, if we need to do so */
00907   if (!lot)
00908     lot = gnc_lot_new (book);
00909 
00910   type = gncInvoiceGetType (invoice);
00911 
00912   /* Set the lot title */
00913   lot_title = g_strdup_printf ("%s %s", type, gncInvoiceGetID (invoice));
00914   gnc_lot_set_title (lot, lot_title);
00915   g_free (lot_title);
00916 
00917   /* Create a new transaction */
00918   txn = xaccMallocTransaction (book);
00919   xaccTransBeginEdit (txn);
00920 
00921   name = gncOwnerGetName (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)));
00922 
00923   /* Set Transaction Description (Owner Name) , Num (invoice ID), Currency */
00924   xaccTransSetDescription (txn, name ? name : "");
00925   xaccTransSetNum (txn, gncInvoiceGetID (invoice));
00926   xaccTransSetCurrency (txn, invoice->currency);
00927 
00928   /* Entered and Posted at date */
00929   xaccTransSetDateEnteredSecs (txn, time(NULL));
00930   if (post_date) {
00931     xaccTransSetDatePostedTS (txn, post_date);
00932     gncInvoiceSetDatePosted (invoice, *post_date);
00933   }
00934 
00935   if (due_date)
00936     xaccTransSetDateDueTS (txn, due_date);
00937 
00938   /* Iterate through the entries; sum up everything for each account.
00939    * then create the appropriate splits in this txn.
00940    */
00941   total = gnc_numeric_zero();
00942   for (iter = gncInvoiceGetEntries(invoice); iter; iter = iter->next) {
00943     gnc_numeric value, tax;
00944     GList *taxes;
00945     GncEntry * entry = iter->data;
00946     Account *this_acc;
00947 
00948     /* Stabilize the TaxTable in this entry */
00949     gncEntryBeginEdit (entry);
00950     if (reverse)
00951       gncEntrySetInvTaxTable
00952         (entry, gncTaxTableReturnChild (gncEntryGetInvTaxTable (entry), TRUE));
00953     else {
00954       gncEntrySetBillTaxTable
00955         (entry, gncTaxTableReturnChild (gncEntryGetBillTaxTable (entry), TRUE));
00956 
00957       /* If this is a bill, and the entry is billable, copy the price */
00958       if (gncEntryGetBillable (entry))
00959         gncEntrySetInvPrice (entry, gncEntryGetBillPrice (entry));
00960     }
00961     gncEntryCommitEdit (entry);
00962 
00963     /* Obtain the Entry's Value and TaxValues */
00964     gncEntryGetValue (entry, reverse, &value, NULL, &tax, &taxes);
00965 
00966     /* add the value for the account split */
00967     this_acc = (reverse ? gncEntryGetInvAccount (entry) :
00968                 gncEntryGetBillAccount (entry));
00969     if (this_acc) {
00970       if (gnc_numeric_check (value) == GNC_ERROR_OK) {
00971         if (accumulatesplits) {
00972             splitinfo = gncAccountValueAdd (splitinfo, this_acc, value);
00973         } else {
00974           Split *split;
00975 
00976           split = xaccMallocSplit (book);
00977           /* set action and memo? */
00978 
00979           xaccSplitSetMemo (split, gncEntryGetDescription (entry));
00980           xaccSplitSetAction (split, type);
00981 
00982           /* Need to insert this split into the account AND txn before
00983            * we set the Base Value.  Otherwise SetBaseValue complains
00984            * that we don't have an account and fails to set the value.
00985            */
00986           xaccAccountBeginEdit (this_acc);
00987           xaccAccountInsertSplit (this_acc, split);
00988           xaccAccountCommitEdit (this_acc);
00989           xaccTransAppendSplit (txn, split);
00990           xaccSplitSetBaseValue (split, (reverse ? gnc_numeric_neg (value)
00991                                          : value),
00992                                  invoice->currency);
00993         }
00994 
00995         /* If there is a credit-card account, and this is a CCard
00996          * payment type, the don't add it to the total, and instead
00997          * create a split to the CC Acct with a memo of the entry
00998          * description instead of the provided memo.  Note that the
00999          * value reversal is the same as the post account.
01000          *
01001          * Note: we don't have to worry about the tax values --
01002          * expense vouchers don't have them.
01003          */
01004         if (ccard_acct && gncEntryGetBillPayment (entry) == GNC_PAYMENT_CARD) {
01005           Split *split;
01006 
01007           split = xaccMallocSplit (book);
01008           /* set action? */
01009           xaccSplitSetMemo (split, gncEntryGetDescription (entry));
01010           xaccSplitSetAction (split, type);
01011           xaccAccountBeginEdit (ccard_acct);
01012           xaccAccountInsertSplit (ccard_acct, split);
01013           xaccAccountCommitEdit (ccard_acct);
01014           xaccTransAppendSplit (txn, split);
01015           xaccSplitSetBaseValue (split, (reverse ? value : gnc_numeric_neg (value)),
01016                                  invoice->currency);
01017           
01018         } else
01019           total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_DENOM_LCD);
01020 
01021       } else
01022         g_warning ("bad value in our entry");
01023     }
01024 
01025     /* now merge in the TaxValues */
01026     splitinfo = gncAccountValueAddList (splitinfo, taxes);
01027 
01028     /* ... and add the tax total */
01029     if (gnc_numeric_check (tax) == GNC_ERROR_OK) 
01030       total = gnc_numeric_add (total, tax, GNC_DENOM_AUTO, GNC_DENOM_LCD);
01031     else
01032       g_warning ("bad tax in our entry");
01033 
01034   } /* for */
01035 
01036   /* Iterate through the splitinfo list and generate the splits */
01037   for (iter = splitinfo; iter; iter = iter->next) {
01038     Split *split;
01039     GncAccountValue *acc_val = iter->data;
01040 
01041     split = xaccMallocSplit (book);
01042     /* set action and memo? */
01043 
01044     xaccSplitSetMemo (split, memo);
01045     xaccSplitSetAction (split, type);
01046 
01047     xaccAccountBeginEdit (acc_val->account);
01048     xaccAccountInsertSplit (acc_val->account, split);
01049     xaccAccountCommitEdit (acc_val->account);
01050     xaccTransAppendSplit (txn, split);
01051     xaccSplitSetBaseValue (split, (reverse ? gnc_numeric_neg (acc_val->value)
01052                                    : acc_val->value),
01053                            invoice->currency);
01054   }
01055 
01056   /* If there is a ccard account, we may have an additional "to_card" payment.
01057    * we should make that now..
01058    */
01059   if (ccard_acct && !gnc_numeric_zero_p (invoice->to_charge_amount)) {
01060     Split *split = xaccMallocSplit (book);
01061 
01062     /* Set memo.  action? */
01063     xaccSplitSetMemo (split, _("Extra to Charge Card"));
01064     xaccSplitSetAction (split, type);
01065     
01066     xaccAccountBeginEdit (ccard_acct);
01067     xaccAccountInsertSplit (ccard_acct, split);
01068     xaccAccountCommitEdit (ccard_acct);
01069     xaccTransAppendSplit (txn, split);
01070     xaccSplitSetBaseValue (split, (reverse ? invoice->to_charge_amount :
01071                                    gnc_numeric_neg(invoice->to_charge_amount)),
01072                            invoice->currency);
01073 
01074     total = gnc_numeric_sub (total, invoice->to_charge_amount,
01075                              GNC_DENOM_AUTO, GNC_DENOM_LCD);
01076   }
01077 
01078   /* Now create the Posted split (which is negative -- it's a credit) */
01079   {
01080     Split *split = xaccMallocSplit (book);
01081 
01082     /* Set action/memo */
01083     xaccSplitSetMemo (split, memo);
01084     xaccSplitSetAction (split, type);
01085                            
01086     xaccAccountBeginEdit (acc);
01087     xaccAccountInsertSplit (acc, split);
01088     xaccAccountCommitEdit (acc);
01089     xaccTransAppendSplit (txn, split);
01090     xaccSplitSetBaseValue (split, (reverse ? total : gnc_numeric_neg (total)),
01091                            invoice->currency);
01092 
01093     /* add this split to the lot */
01094     gnc_lot_add_split (lot, split);
01095   }
01096 
01097   /* Now attach this invoice to the txn, lot, and account */
01098   gncInvoiceAttachToLot (invoice, lot);
01099   gncInvoiceAttachToTxn (invoice, txn);
01100   gncInvoiceSetPostedAcc (invoice, acc);
01101 
01102   xaccTransSetReadOnly (txn, _("Generated from an invoice.  Try unposting the invoice."));
01103   xaccTransCommitEdit (txn);
01104 
01105   gncAccountValueDestroy (splitinfo);
01106 
01107   /* check the lot -- if we still look like a payment lot, then that
01108    * means we need to create a balancing split and create a new payment
01109    * lot for the next invoice
01110    *
01111    * we're looking for a positive balance for bill/AP, and a negative balance
01112    * for invoice/AR.
01113    * (because bill payments debit AP accounts and invoice payments
01114    * credit AR accounts)
01115    */
01116   total = gnc_lot_get_balance (lot);
01117   
01118   if ( (gnc_numeric_negative_p (total) && reverse) || 
01119        (gnc_numeric_positive_p (total) && !reverse) ) {
01120     Transaction *t2;
01121     GNCLot *lot2;
01122     Split *split;
01123     /* Translators: This is the memo of an auto-created split */
01124     char *memo2 = _("Automatic Payment Forward");
01125     char *action2 = _("Auto Split");
01126 
01127     t2 = xaccMallocTransaction (book);
01128     lot2 = gnc_lot_new (book);
01129     gncOwnerAttachToLot (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)),
01130                          lot2);
01131     
01132     xaccTransBeginEdit (t2);
01133     xaccAccountBeginEdit (acc);
01134 
01135     /* Set Transaction Description (Owner Name), Currency */
01136     xaccTransSetDescription (t2, name ? name : "");
01137     xaccTransSetCurrency (t2, invoice->currency);
01138 
01139     /* Entered and Posted at date */
01140     xaccTransSetDateEnteredSecs (t2, time(NULL));
01141     if (post_date)
01142       xaccTransSetDatePostedTS (t2, post_date);
01143 
01144     /* Balance out this lot */
01145     split = xaccMallocSplit (book);
01146     xaccSplitSetMemo (split, memo2);
01147     xaccSplitSetAction (split, action2);
01148     xaccAccountInsertSplit (acc, split);
01149     xaccTransAppendSplit (t2, split);
01150     // the value of total used here is correct for both bill/AP and
01151     // invoice/AR. See the comment before this if block
01152     xaccSplitSetBaseValue (split, gnc_numeric_neg (total),
01153                            invoice->currency);
01154     gnc_lot_add_split (lot, split);
01155 
01156     /* And apply the pre-payment to a new lot */
01157     split = xaccMallocSplit (book);
01158     xaccSplitSetMemo (split, memo2);
01159     xaccSplitSetAction (split, action2);
01160     xaccAccountInsertSplit (acc, split);
01161     xaccTransAppendSplit (t2, split);
01162     xaccSplitSetBaseValue (split, total, invoice->currency);
01163     gnc_lot_add_split (lot2, split);
01164 
01165     xaccTransCommitEdit (t2);
01166     xaccAccountCommitEdit (acc);
01167   }
01168 
01169   gncInvoiceCommitEdit (invoice);
01170 
01171   return txn;
01172 }

void gncInvoiceSortEntries ( GncInvoice invoice  ) 

Call this function when an Entry is changed and you want to re-sort the list of entries

Definition at line 461 of file gncInvoice.c.

00462 {
00463   if (!invoice) return;
00464   invoice->entries = g_list_sort(invoice->entries,
00465                                  (GCompareFunc)gncEntryCompare);
00466   mark_invoice(invoice);
00467 }

gboolean gncInvoiceUnpost ( GncInvoice invoice,
gboolean  reset_tax_tables 
)

UNpost this invoice. This will destroy the posted transaction and return the invoice to its unposted state. It may leave empty lots out there. If reset_tax_tables is TRUE, then it will also revert all the Tax Tables to the parent, which will potentially change the total value of the invoice. It may also leave some orphaned Tax Table children.

Returns TRUE if successful, FALSE if there is a problem.

Definition at line 1175 of file gncInvoice.c.

01176 {
01177   Transaction *txn;
01178   GNCLot *lot;
01179 
01180   if (!invoice) return FALSE;
01181   if (!gncInvoiceIsPosted (invoice)) return FALSE;
01182 
01183   txn = gncInvoiceGetPostedTxn (invoice);
01184   g_return_val_if_fail (txn, FALSE);
01185 
01186   lot = gncInvoiceGetPostedLot (invoice);
01187   g_return_val_if_fail (lot, FALSE);
01188 
01189   /* Destroy the Posted Transaction */
01190   xaccTransClearReadOnly (txn);
01191   xaccTransBeginEdit (txn);
01192   xaccTransDestroy (txn);
01193   xaccTransCommitEdit (txn);
01194 
01195   /* Disconnect the lot from the invoice; re-attach to the invoice owner */
01196   gncInvoiceDetachFromLot (lot);
01197   gncOwnerAttachToLot (&invoice->owner, lot);
01198 
01199   /* If the lot has no splits, then destroy it */
01200   if (!gnc_lot_count_splits (lot))
01201     gnc_lot_destroy (lot);
01202 
01203   /* Clear out the invoice posted information */
01204   gncInvoiceBeginEdit (invoice);
01205 
01206   invoice->posted_acc = NULL;
01207   invoice->posted_txn = NULL;
01208   invoice->posted_lot = NULL;
01209   invoice->date_posted.tv_sec = invoice->date_posted.tv_nsec = 0;
01210 
01211   /* if we've been asked to reset the tax tables, then do so */
01212   if (reset_tax_tables) {
01213     gboolean reverse = (gncInvoiceGetOwnerType(invoice) == GNC_OWNER_CUSTOMER);
01214     GList *iter;
01215 
01216     for (iter = gncInvoiceGetEntries(invoice); iter; iter = iter->next) {
01217       GncEntry *entry = iter->data;
01218 
01219       gncEntryBeginEdit(entry);
01220       if (reverse)
01221         gncEntrySetInvTaxTable(entry,
01222                                gncTaxTableGetParent(gncEntryGetInvTaxTable(entry)));
01223       else
01224         gncEntrySetBillTaxTable(entry,
01225                                gncTaxTableGetParent(gncEntryGetBillTaxTable(entry)));
01226       gncEntryCommitEdit(entry);
01227     }
01228   }
01229 
01230   mark_invoice (invoice);
01231   gncInvoiceCommitEdit (invoice);
01232 
01233   return TRUE;
01234 }

Transaction* gncOwnerApplyPayment ( GncOwner owner,
GncInvoice invoice,
Account posted_acc,
Account xfer_acc,
gnc_numeric  amount,
Timespec  date,
const char *  memo,
const char *  num 
)

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). If the caller supplies an (optional) invoice argument, then apply the payment to that invoice first before any other invoice.

XXX: yes, this should be in gncOwner, but all the other logic is in gncInvoice...

Definition at line 1279 of file gncInvoice.c.

01283 {
01284   QofBook *book;
01285   Account *inv_posted_acc;
01286   Transaction *txn;
01287   Split *split;
01288   GList *lot_list, *fifo = NULL;
01289   GNCLot *lot, *inv_posted_lot = NULL, *prepay_lot = NULL;
01290   GncInvoice *this_invoice;
01291   const char *name;
01292   gnc_commodity *commodity;
01293   gnc_numeric split_amt;
01294   gboolean reverse, inv_passed = TRUE;
01295 
01296   /* Verify our arguments */
01297   if (!owner || !posted_acc || !xfer_acc) return NULL;
01298   g_return_val_if_fail (owner->owner.undefined != NULL, NULL);
01299 
01300   /* Compute the ancillary data */
01301   book = gnc_account_get_book (posted_acc);
01302   name = gncOwnerGetName (gncOwnerGetEndOwner (owner));
01303   commodity = gncOwnerGetCurrency (owner);
01304   reverse = (gncOwnerGetType (owner) == GNC_OWNER_CUSTOMER);
01305 
01306   txn = xaccMallocTransaction (book);
01307   xaccTransBeginEdit (txn);
01308 
01309   /* Set up the transaction */
01310   xaccTransSetDescription (txn, name ? name : "");
01311   xaccTransSetNum (txn, num);
01312   xaccTransSetCurrency (txn, commodity);
01313   xaccTransSetDateEnteredSecs (txn, time(NULL));
01314   xaccTransSetDatePostedTS (txn, &date);
01315   xaccTransSetTxnType (txn, TXN_TYPE_PAYMENT);
01316 
01317   /* The split for the transfer account */
01318   split = xaccMallocSplit (book);
01319   xaccSplitSetMemo (split, memo);
01320   xaccSplitSetAction (split, _("Payment"));
01321   xaccAccountBeginEdit (xfer_acc);
01322   xaccAccountInsertSplit (xfer_acc, split);
01323   xaccAccountCommitEdit (xfer_acc);
01324   xaccTransAppendSplit (txn, split);
01325   xaccSplitSetBaseValue (split, reverse ? amount :
01326                          gnc_numeric_neg (amount), commodity);
01327 
01328   /* Now, find all "open" lots in the posting account for this
01329    * company and apply the payment on a FIFO basis.  Create
01330    * a new split for each open lot until the payment is gone.
01331    */
01332 
01333   fifo = xaccAccountFindOpenLots (posted_acc, gnc_lot_match_invoice_owner,
01334                                   owner,
01335                                   (GCompareFunc)gnc_lot_sort_func);
01336 
01337   /* Check if an invoice was passed in, and if so, does it match the
01338    * account, and is it an open lot?  If so, put it at the beginning
01339    * of the lot list fifo so we post to this invoice's lot first.
01340    */
01341   if (invoice) {
01342     inv_posted_acc = gncInvoiceGetPostedAcc(invoice);
01343     inv_posted_lot = gncInvoiceGetPostedLot(invoice);
01344     if (inv_posted_acc && inv_posted_lot &&
01345         guid_equal(xaccAccountGetGUID(inv_posted_acc),
01346                    xaccAccountGetGUID(posted_acc)) &&
01347         !gnc_lot_is_closed(inv_posted_lot)) {
01348       /* Put this invoice at the beginning of the FIFO */
01349       fifo = g_list_prepend (fifo, inv_posted_lot);
01350       inv_passed = FALSE;
01351     }
01352   }
01353 
01354   xaccAccountBeginEdit (posted_acc);
01355 
01356   /* Now iterate over the fifo until the payment is fully applied
01357    * (or all the lots are paid)
01358    */
01359   for (lot_list = fifo; lot_list; lot_list = lot_list->next) {
01360     gnc_numeric balance;
01361 
01362     lot = lot_list->data;
01363 
01364     /* Skip this lot if it matches the invoice that was passed in and
01365      * we've seen it already.  This way we post to it the first time
01366      * (from the beginning of the lot-list) but not when we reach it
01367      * the second time.
01368      */
01369     if (inv_posted_lot &&
01370         guid_equal(qof_instance_get_guid(QOF_INSTANCE(lot)),
01371                    qof_instance_get_guid(QOF_INSTANCE(inv_posted_lot)))) {
01372       if (inv_passed)
01373         continue;
01374       else
01375         inv_passed = TRUE;
01376     }
01377 
01378     balance = gnc_lot_get_balance (lot);
01379 
01380     if (!reverse)
01381       balance = gnc_numeric_neg (balance);
01382 
01383     /* If the balance is "negative" then skip this lot.
01384      * (just save the pre-payment lot for later)
01385      */
01386     if (gnc_numeric_negative_p (balance)) {
01387       if (prepay_lot) {
01388         g_warning ("Multiple pre-payment lots are found.  Skipping.");
01389       } else {
01390         prepay_lot = lot;
01391       }
01392       continue;
01393     }
01394 
01395     /*
01396      * If the amount <= the balance; we're done -- apply the amount.
01397      * Otherwise, apply the balance, subtract that from the amount,
01398      * and move on to the next one.
01399      */
01400     if (gnc_numeric_compare (amount, balance) <= 0) {
01401       /* amount <= balance */
01402       split_amt = amount;
01403     } else {
01404       /* amount > balance */
01405       split_amt = balance;
01406     }
01407 
01408     /* reduce the amount by split_amt */
01409     amount = gnc_numeric_sub (amount, split_amt, GNC_DENOM_AUTO, GNC_DENOM_LCD);
01410 
01411     /* Create the split for this lot in the post account */
01412     split = xaccMallocSplit (book);
01413     xaccSplitSetMemo (split, memo);
01414     xaccSplitSetAction (split, _("Payment"));
01415     xaccAccountInsertSplit (posted_acc, split);
01416     xaccTransAppendSplit (txn, split);
01417     xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (split_amt) :
01418                            split_amt, commodity);
01419     gnc_lot_add_split (lot, split);
01420 
01421     /* Now send an event for the invoice so it gets updated as paid */
01422     this_invoice = gncInvoiceGetInvoiceFromLot(lot);
01423     if (this_invoice)
01424       qof_event_gen (&this_invoice->inst, QOF_EVENT_MODIFY, NULL);
01425 
01426     if (gnc_numeric_zero_p (amount))
01427       break;
01428   }
01429 
01430   g_list_free (fifo);
01431 
01432   /* If there is still money left here, then create a pre-payment lot */
01433   if (gnc_numeric_positive_p (amount)) {
01434     if (prepay_lot == NULL) {
01435       prepay_lot = gnc_lot_new (book);
01436       gncOwnerAttachToLot (owner, prepay_lot);
01437     }
01438 
01439     split = xaccMallocSplit (book);
01440     xaccSplitSetMemo (split, memo);
01441     xaccSplitSetAction (split, _("Pre-Payment"));
01442     xaccAccountInsertSplit (posted_acc, split);
01443     xaccTransAppendSplit (txn, split);
01444     xaccSplitSetBaseValue (split, reverse ? gnc_numeric_neg (amount) :
01445                            amount, commodity);
01446     gnc_lot_add_split (prepay_lot, split);
01447   }
01448 
01449   xaccAccountCommitEdit (posted_acc);
01450 
01451   /* Commit this new transaction */
01452   xaccTransCommitEdit (txn);
01453 
01454   return txn;    
01455 }


Generated on Thu Jul 3 05:07:14 2008 for GnuCash by  doxygen 1.5.2