|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * gncInvoice.c -- the Core Business Invoice * 00003 * * 00004 * This program is free software; you can redistribute it and/or * 00005 * modify it under the terms of the GNU General Public License as * 00006 * published by the Free Software Foundation; either version 2 of * 00007 * the License, or (at your option) any later version. * 00008 * * 00009 * This program is distributed in the hope that it will be useful, * 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00012 * GNU General Public License for more details. * 00013 * * 00014 * You should have received a copy of the GNU General Public License* 00015 * along with this program; if not, contact: * 00016 * * 00017 * Free Software Foundation Voice: +1-617-542-5942 * 00018 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00019 * Boston, MA 02110-1301, USA gnu@gnu.org * 00020 * * 00021 \********************************************************************/ 00022 00023 /* 00024 * Copyright (C) 2001,2002,2006 Derek Atkins 00025 * Copyright (C) 2003 Linas Vepstas <linas@linas.org> 00026 * Copyright (c) 2005 Neil Williams <linux@codehelp.co.uk> 00027 * Author: Derek Atkins <warlord@MIT.EDU> 00028 */ 00029 00030 #include "config.h" 00031 00032 #include <glib.h> 00033 #include <glib/gi18n.h> 00034 00035 #include "Transaction.h" 00036 #include "Account.h" 00037 #include "gncBillTermP.h" 00038 #include "gncEntry.h" 00039 #include "gncEntryP.h" 00040 #include "gnc-features.h" 00041 #include "gncJobP.h" 00042 #include "gncInvoice.h" 00043 #include "gncInvoiceP.h" 00044 #include "gncOwnerP.h" 00045 00046 struct _gncInvoice 00047 { 00048 QofInstance inst; 00049 00050 char *id; 00051 char *notes; 00052 gboolean active; 00053 00054 char *billing_id; 00055 char *printname; 00056 GncBillTerm *terms; 00057 GList *entries; 00058 GList *prices; 00059 GncOwner owner; 00060 GncOwner billto; 00061 GncJob *job; 00062 Timespec date_opened; 00063 Timespec date_posted; 00064 00065 gnc_numeric to_charge_amount; 00066 00067 gnc_commodity *currency; 00068 00069 Account *posted_acc; 00070 Transaction *posted_txn; 00071 GNCLot *posted_lot; 00072 }; 00073 00074 struct _gncInvoiceClass 00075 { 00076 QofInstanceClass parent_class; 00077 }; 00078 00079 static QofLogModule log_module = GNC_MOD_BUSINESS; 00080 00081 #define _GNC_MOD_NAME GNC_ID_INVOICE 00082 00083 #define GNC_INVOICE_ID "gncInvoice" 00084 #define GNC_INVOICE_GUID "invoice-guid" 00085 #define GNC_INVOICE_IS_CN "credit-note" 00086 00087 #define SET_STR(obj, member, str) { \ 00088 char * tmp; \ 00089 \ 00090 if (!safe_strcmp (member, str)) return; \ 00091 gncInvoiceBeginEdit (obj); \ 00092 tmp = CACHE_INSERT (str); \ 00093 CACHE_REMOVE (member); \ 00094 member = tmp; \ 00095 } 00096 00097 static void mark_invoice (GncInvoice *invoice); 00098 static void 00099 mark_invoice (GncInvoice *invoice) 00100 { 00101 qof_instance_set_dirty(&invoice->inst); 00102 qof_event_gen (&invoice->inst, QOF_EVENT_MODIFY, NULL); 00103 } 00104 00105 QofBook * gncInvoiceGetBook(GncInvoice *x) 00106 { 00107 return qof_instance_get_book(QOF_INSTANCE(x)); 00108 } 00109 00110 /* ================================================================== */ 00111 00112 enum 00113 { 00114 PROP_0, 00115 PROP_NOTES 00116 }; 00117 00118 /* GObject Initialization */ 00119 G_DEFINE_TYPE(GncInvoice, gnc_invoice, QOF_TYPE_INSTANCE); 00120 00121 static void 00122 gnc_invoice_init(GncInvoice* inv) 00123 { 00124 } 00125 00126 static void 00127 gnc_invoice_dispose(GObject *invp) 00128 { 00129 G_OBJECT_CLASS(gnc_invoice_parent_class)->dispose(invp); 00130 } 00131 00132 static void 00133 gnc_invoice_finalize(GObject* invp) 00134 { 00135 G_OBJECT_CLASS(gnc_invoice_parent_class)->finalize(invp); 00136 } 00137 00138 static void 00139 gnc_invoice_get_property (GObject *object, 00140 guint prop_id, 00141 GValue *value, 00142 GParamSpec *pspec) 00143 { 00144 GncInvoice *inv; 00145 00146 g_return_if_fail(GNC_IS_INVOICE(object)); 00147 00148 inv = GNC_INVOICE(object); 00149 switch (prop_id) 00150 { 00151 case PROP_NOTES: 00152 g_value_set_string(value, inv->notes); 00153 break; 00154 default: 00155 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00156 break; 00157 } 00158 } 00159 00160 static void 00161 gnc_invoice_set_property (GObject *object, 00162 guint prop_id, 00163 const GValue *value, 00164 GParamSpec *pspec) 00165 { 00166 GncInvoice *inv; 00167 00168 g_return_if_fail(GNC_IS_INVOICE(object)); 00169 00170 inv = GNC_INVOICE(object); 00171 switch (prop_id) 00172 { 00173 case PROP_NOTES: 00174 gncInvoiceSetNotes(inv, g_value_get_string(value)); 00175 break; 00176 default: 00177 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00178 break; 00179 } 00180 } 00181 00183 static gchar* 00184 impl_get_display_name(const QofInstance* inst) 00185 { 00186 GncInvoice* inv; 00187 QofInstance* owner; 00188 gchar* s; 00189 00190 g_return_val_if_fail(inst != NULL, FALSE); 00191 g_return_val_if_fail(GNC_IS_INVOICE(inst), FALSE); 00192 00193 inv = GNC_INVOICE(inst); 00194 owner = qofOwnerGetOwner(&inv->owner); 00195 if (owner != NULL) 00196 { 00197 gchar* display_name; 00198 00199 display_name = qof_instance_get_display_name(owner); 00200 s = g_strdup_printf("Invoice %s (%s)", inv->id, display_name); 00201 g_free(display_name); 00202 } 00203 else 00204 { 00205 s = g_strdup_printf("Invoice %s", inv->id); 00206 } 00207 00208 return s; 00209 } 00210 00212 static gboolean 00213 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref) 00214 { 00215 GncInvoice* inv; 00216 00217 g_return_val_if_fail(inst != NULL, FALSE); 00218 g_return_val_if_fail(GNC_IS_INVOICE(inst), FALSE); 00219 00220 inv = GNC_INVOICE(inst); 00221 00222 if (GNC_IS_BILLTERM(ref)) 00223 { 00224 return (inv->terms == GNC_BILLTERM(ref)); 00225 } 00226 else if (GNC_IS_JOB(ref)) 00227 { 00228 return (inv->job == GNC_JOB(ref)); 00229 } 00230 else if (GNC_IS_COMMODITY(ref)) 00231 { 00232 return (inv->currency == GNC_COMMODITY(ref)); 00233 } 00234 else if (GNC_IS_ACCOUNT(ref)) 00235 { 00236 return (inv->posted_acc == GNC_ACCOUNT(ref)); 00237 } 00238 else if (GNC_IS_TRANSACTION(ref)) 00239 { 00240 return (inv->posted_txn == GNC_TRANSACTION(ref)); 00241 } 00242 else if (GNC_IS_LOT(ref)) 00243 { 00244 return (inv->posted_lot == GNC_LOT(ref)); 00245 } 00246 00247 return FALSE; 00248 } 00249 00256 static GList* 00257 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref) 00258 { 00259 if (!GNC_IS_BILLTERM(ref) && !GNC_IS_JOB(ref) && !GNC_IS_COMMODITY(ref) && !GNC_IS_ACCOUNT(ref) 00260 && !GNC_IS_TRANSACTION(ref) && !GNC_IS_LOT(ref)) 00261 { 00262 return NULL; 00263 } 00264 00265 return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref); 00266 } 00267 00268 static void 00269 gnc_invoice_class_init (GncInvoiceClass *klass) 00270 { 00271 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 00272 QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass); 00273 00274 gobject_class->dispose = gnc_invoice_dispose; 00275 gobject_class->finalize = gnc_invoice_finalize; 00276 gobject_class->set_property = gnc_invoice_set_property; 00277 gobject_class->get_property = gnc_invoice_get_property; 00278 00279 qof_class->get_display_name = impl_get_display_name; 00280 qof_class->refers_to_object = impl_refers_to_object; 00281 qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list; 00282 00283 g_object_class_install_property 00284 (gobject_class, 00285 PROP_NOTES, 00286 g_param_spec_string ("notes", 00287 "Invoice Notes", 00288 "The invoice notes is an arbitrary string " 00289 "assigned by the user to provide notes regarding " 00290 "this invoice.", 00291 NULL, 00292 G_PARAM_READWRITE)); 00293 } 00294 00295 /* Create/Destroy Functions */ 00296 GncInvoice *gncInvoiceCreate (QofBook *book) 00297 { 00298 GncInvoice *invoice; 00299 00300 if (!book) return NULL; 00301 00302 invoice = g_object_new (GNC_TYPE_INVOICE, NULL); 00303 qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book); 00304 00305 invoice->id = CACHE_INSERT (""); 00306 invoice->notes = CACHE_INSERT (""); 00307 invoice->billing_id = CACHE_INSERT (""); 00308 00309 invoice->billto.type = GNC_OWNER_CUSTOMER; 00310 invoice->active = TRUE; 00311 00312 invoice->to_charge_amount = gnc_numeric_zero(); 00313 00314 qof_event_gen (&invoice->inst, QOF_EVENT_CREATE, NULL); 00315 00316 return invoice; 00317 } 00318 00319 GncInvoice *gncInvoiceCopy (const GncInvoice *from) 00320 { 00321 GncInvoice *invoice; 00322 QofBook* book; 00323 GList *node; 00324 gint64 is_cn; 00325 00326 g_assert(from); 00327 book = qof_instance_get_book(from); 00328 g_assert(book); 00329 00330 invoice = g_object_new (GNC_TYPE_INVOICE, NULL); 00331 qof_instance_init_data (&invoice->inst, _GNC_MOD_NAME, book); 00332 00333 gncInvoiceBeginEdit(invoice); 00334 00335 invoice->id = CACHE_INSERT (from->id); 00336 invoice->notes = CACHE_INSERT (from->notes); 00337 invoice->billing_id = CACHE_INSERT (from->billing_id); 00338 invoice->active = from->active; 00339 00340 is_cn = kvp_frame_get_gint64(from->inst.kvp_data, GNC_INVOICE_IS_CN); 00341 kvp_frame_set_gint64(invoice->inst.kvp_data, GNC_INVOICE_IS_CN, is_cn); 00342 00343 invoice->terms = from->terms; 00344 gncBillTermIncRef (invoice->terms); 00345 00346 gncOwnerCopy(&from->billto, &invoice->billto); 00347 gncOwnerCopy(&from->owner, &invoice->owner); 00348 invoice->job = from->job; // FIXME: Need IncRef or similar here?!? 00349 00350 invoice->to_charge_amount = from->to_charge_amount; 00351 invoice->date_opened = from->date_opened; 00352 00353 // Oops. Do not forget to copy the pointer to the correct currency here. 00354 invoice->currency = from->currency; 00355 00356 // Copy all invoice->entries 00357 for (node = from->entries; node; node = node->next) 00358 { 00359 GncEntry *from_entry = node->data; 00360 GncEntry *to_entry = gncEntryCreate(book); 00361 gncEntryCopy(from_entry, to_entry); 00362 00363 switch (gncInvoiceGetOwnerType (invoice)) 00364 { 00365 case GNC_OWNER_VENDOR: 00366 case GNC_OWNER_EMPLOYEE: 00367 // this is a vendor bill, or an expense voucher 00368 gncBillAddEntry(invoice, to_entry); 00369 break; 00370 case GNC_OWNER_CUSTOMER: 00371 default: 00372 // this is an invoice 00373 gncInvoiceAddEntry(invoice, to_entry); 00374 break; 00375 } 00376 } 00377 00378 // FIXME: The prices are not (yet) copied; is this a problem? 00379 00380 // Posted-date and the posted Txn is intentionally not copied; the 00381 // copy isn't "posted" but needs to be posted by the user. 00382 00383 gncInvoiceCommitEdit(invoice); 00384 00385 return invoice; 00386 } 00387 00388 void gncInvoiceDestroy (GncInvoice *invoice) 00389 { 00390 if (!invoice) return; 00391 qof_instance_set_destroying(invoice, TRUE); 00392 gncInvoiceCommitEdit (invoice); 00393 } 00394 00395 static void gncInvoiceFree (GncInvoice *invoice) 00396 { 00397 if (!invoice) return; 00398 00399 qof_event_gen (&invoice->inst, QOF_EVENT_DESTROY, NULL); 00400 00401 CACHE_REMOVE (invoice->id); 00402 CACHE_REMOVE (invoice->notes); 00403 CACHE_REMOVE (invoice->billing_id); 00404 g_list_free (invoice->entries); 00405 g_list_free (invoice->prices); 00406 00407 if (invoice->printname) g_free (invoice->printname); 00408 00409 if (invoice->terms) 00410 gncBillTermDecRef (invoice->terms); 00411 00412 /* qof_instance_release (&invoice->inst); */ 00413 g_object_unref (invoice); 00414 } 00415 00416 /* ================================================================== */ 00417 /* Set Functions */ 00418 00419 void gncInvoiceSetID (GncInvoice *invoice, const char *id) 00420 { 00421 if (!invoice || !id) return; 00422 SET_STR (invoice, invoice->id, id); 00423 mark_invoice (invoice); 00424 gncInvoiceCommitEdit (invoice); 00425 } 00426 00427 void gncInvoiceSetOwner (GncInvoice *invoice, GncOwner *owner) 00428 { 00429 if (!invoice || !owner) return; 00430 if (gncOwnerEqual (&invoice->owner, owner)) return; 00431 gncInvoiceBeginEdit (invoice); 00432 gncOwnerCopy (owner, &invoice->owner); 00433 mark_invoice (invoice); 00434 gncInvoiceCommitEdit (invoice); 00435 } 00436 00437 static void 00438 qofInvoiceSetOwner (GncInvoice *invoice, QofInstance *ent) 00439 { 00440 if (!invoice || !ent) 00441 { 00442 return; 00443 } 00444 gncInvoiceBeginEdit (invoice); 00445 qofOwnerSetEntity(&invoice->owner, ent); 00446 mark_invoice (invoice); 00447 gncInvoiceCommitEdit (invoice); 00448 } 00449 00450 static void 00451 qofInvoiceSetBillTo (GncInvoice *invoice, QofInstance *ent) 00452 { 00453 if (!invoice || !ent) 00454 { 00455 return; 00456 } 00457 gncInvoiceBeginEdit (invoice); 00458 qofOwnerSetEntity(&invoice->billto, ent); 00459 mark_invoice (invoice); 00460 gncInvoiceCommitEdit (invoice); 00461 } 00462 00463 void gncInvoiceSetDateOpenedGDate (GncInvoice *invoice, const GDate *date) 00464 { 00465 g_assert (date); 00466 gncInvoiceSetDateOpened(invoice, timespecCanonicalDayTime(gdate_to_timespec(*date))); 00467 } 00468 00469 void gncInvoiceSetDateOpened (GncInvoice *invoice, Timespec date) 00470 { 00471 if (!invoice) return; 00472 if (timespec_equal (&invoice->date_opened, &date)) return; 00473 gncInvoiceBeginEdit (invoice); 00474 invoice->date_opened = date; 00475 mark_invoice (invoice); 00476 gncInvoiceCommitEdit (invoice); 00477 } 00478 00479 void gncInvoiceSetDatePosted (GncInvoice *invoice, Timespec date) 00480 { 00481 if (!invoice) return; 00482 if (timespec_equal (&invoice->date_posted, &date)) return; 00483 gncInvoiceBeginEdit (invoice); 00484 invoice->date_posted = date; 00485 mark_invoice (invoice); 00486 gncInvoiceCommitEdit (invoice); 00487 } 00488 00489 void gncInvoiceSetTerms (GncInvoice *invoice, GncBillTerm *terms) 00490 { 00491 if (!invoice) return; 00492 if (invoice->terms == terms) return; 00493 gncInvoiceBeginEdit (invoice); 00494 if (invoice->terms) 00495 gncBillTermDecRef (invoice->terms); 00496 invoice->terms = terms; 00497 if (invoice->terms) 00498 gncBillTermIncRef (invoice->terms); 00499 mark_invoice (invoice); 00500 gncInvoiceCommitEdit (invoice); 00501 } 00502 00503 void gncInvoiceSetBillingID (GncInvoice *invoice, const char *billing_id) 00504 { 00505 if (!invoice) return; 00506 SET_STR (invoice, invoice->billing_id, billing_id); 00507 mark_invoice (invoice); 00508 gncInvoiceCommitEdit (invoice); 00509 } 00510 00511 void gncInvoiceSetNotes (GncInvoice *invoice, const char *notes) 00512 { 00513 if (!invoice || !notes) return; 00514 SET_STR (invoice, invoice->notes, notes); 00515 mark_invoice (invoice); 00516 gncInvoiceCommitEdit (invoice); 00517 } 00518 00519 void gncInvoiceSetActive (GncInvoice *invoice, gboolean active) 00520 { 00521 if (!invoice) return; 00522 if (invoice->active == active) return; 00523 gncInvoiceBeginEdit (invoice); 00524 invoice->active = active; 00525 mark_invoice (invoice); 00526 gncInvoiceCommitEdit (invoice); 00527 } 00528 00529 void gncInvoiceSetIsCreditNote (GncInvoice *invoice, gboolean credit_note) 00530 { 00531 if (!invoice) return; 00532 gncInvoiceBeginEdit (invoice); 00533 kvp_frame_set_gint64(invoice->inst.kvp_data, GNC_INVOICE_IS_CN, 00534 credit_note ? 1 : 0); 00535 mark_invoice (invoice); 00536 gncInvoiceCommitEdit (invoice); 00537 00538 /* If this is a credit note, set a feature flag for it in the book 00539 * This will prevent older GnuCash versions that don't support 00540 * credit notes to open this file. */ 00541 if (credit_note) 00542 gnc_features_set_used (gncInvoiceGetBook (invoice), GNC_FEATURE_CREDIT_NOTES); 00543 } 00544 00545 void gncInvoiceSetCurrency (GncInvoice *invoice, gnc_commodity *currency) 00546 { 00547 if (!invoice || !currency) return; 00548 if (invoice->currency && 00549 gnc_commodity_equal (invoice->currency, currency)) 00550 return; 00551 gncInvoiceBeginEdit (invoice); 00552 invoice->currency = currency; 00553 mark_invoice (invoice); 00554 gncInvoiceCommitEdit (invoice); 00555 } 00556 00557 void gncInvoiceSetBillTo (GncInvoice *invoice, GncOwner *billto) 00558 { 00559 if (!invoice || !billto) return; 00560 if (gncOwnerEqual (&invoice->billto, billto)) return; 00561 00562 gncInvoiceBeginEdit (invoice); 00563 gncOwnerCopy (billto, &invoice->billto); 00564 mark_invoice (invoice); 00565 gncInvoiceCommitEdit (invoice); 00566 } 00567 00568 void gncInvoiceSetToChargeAmount (GncInvoice *invoice, gnc_numeric amount) 00569 { 00570 if (!invoice) return; 00571 if (gnc_numeric_equal (invoice->to_charge_amount, amount)) return; 00572 gncInvoiceBeginEdit (invoice); 00573 invoice->to_charge_amount = amount; 00574 mark_invoice (invoice); 00575 gncInvoiceCommitEdit (invoice); 00576 } 00577 00578 void gncInvoiceSetPostedTxn (GncInvoice *invoice, Transaction *txn) 00579 { 00580 if (!invoice) return; 00581 g_return_if_fail (invoice->posted_txn == NULL); 00582 00583 gncInvoiceBeginEdit (invoice); 00584 invoice->posted_txn = txn; 00585 mark_invoice (invoice); 00586 gncInvoiceCommitEdit (invoice); 00587 } 00588 00589 void gncInvoiceSetPostedLot (GncInvoice *invoice, GNCLot *lot) 00590 { 00591 if (!invoice) return; 00592 g_return_if_fail (invoice->posted_lot == NULL); 00593 00594 gncInvoiceBeginEdit (invoice); 00595 invoice->posted_lot = lot; 00596 mark_invoice (invoice); 00597 gncInvoiceCommitEdit (invoice); 00598 } 00599 00600 void gncInvoiceSetPostedAcc (GncInvoice *invoice, Account *acc) 00601 { 00602 if (!invoice) return; 00603 g_return_if_fail (invoice->posted_acc == NULL); 00604 00605 gncInvoiceBeginEdit (invoice); 00606 invoice->posted_acc = acc; 00607 mark_invoice (invoice); 00608 gncInvoiceCommitEdit (invoice); 00609 } 00610 00611 void gncInvoiceAddEntry (GncInvoice *invoice, GncEntry *entry) 00612 { 00613 GncInvoice *old; 00614 00615 g_assert(invoice); 00616 g_assert(entry); 00617 if (!invoice || !entry) return; 00618 00619 old = gncEntryGetInvoice (entry); 00620 if (old == invoice) return; /* I already own this one */ 00621 if (old) gncInvoiceRemoveEntry (old, entry); 00622 00623 gncEntrySetInvoice (entry, invoice); 00624 invoice->entries = g_list_insert_sorted (invoice->entries, entry, 00625 (GCompareFunc)gncEntryCompare); 00626 mark_invoice (invoice); 00627 } 00628 00629 void gncInvoiceRemoveEntry (GncInvoice *invoice, GncEntry *entry) 00630 { 00631 if (!invoice || !entry) return; 00632 00633 gncEntrySetInvoice (entry, NULL); 00634 invoice->entries = g_list_remove (invoice->entries, entry); 00635 mark_invoice (invoice); 00636 } 00637 00638 void gncInvoiceAddPrice (GncInvoice *invoice, GNCPrice *price) 00639 { 00640 if (!invoice || !price) return; 00641 00642 invoice->prices = g_list_prepend(invoice->prices, price); 00643 mark_invoice (invoice); 00644 } 00645 00646 void gncBillAddEntry (GncInvoice *bill, GncEntry *entry) 00647 { 00648 GncInvoice *old; 00649 00650 g_assert(bill); 00651 g_assert(entry); 00652 if (!bill || !entry) return; 00653 00654 old = gncEntryGetBill (entry); 00655 if (old == bill) return; /* I already own this one */ 00656 if (old) gncBillRemoveEntry (old, entry); 00657 00658 gncEntrySetBill (entry, bill); 00659 bill->entries = g_list_insert_sorted (bill->entries, entry, 00660 (GCompareFunc)gncEntryCompare); 00661 mark_invoice (bill); 00662 } 00663 00664 void gncBillRemoveEntry (GncInvoice *bill, GncEntry *entry) 00665 { 00666 if (!bill || !entry) return; 00667 00668 gncEntrySetBill (entry, NULL); 00669 bill->entries = g_list_remove (bill->entries, entry); 00670 mark_invoice (bill); 00671 } 00672 00673 void gncInvoiceSortEntries (GncInvoice *invoice) 00674 { 00675 if (!invoice) return; 00676 invoice->entries = g_list_sort(invoice->entries, 00677 (GCompareFunc)gncEntryCompare); 00678 mark_invoice(invoice); 00679 } 00680 00681 /* ================================================================== */ 00682 /* Get Functions */ 00683 00684 const char * gncInvoiceGetID (const GncInvoice *invoice) 00685 { 00686 if (!invoice) return NULL; 00687 return invoice->id; 00688 } 00689 00690 const GncOwner * gncInvoiceGetOwner (const GncInvoice *invoice) 00691 { 00692 if (!invoice) return NULL; 00693 return &invoice->owner; 00694 } 00695 00696 static QofInstance* 00697 qofInvoiceGetOwner (GncInvoice *invoice) 00698 { 00699 GncOwner *owner; 00700 00701 if (!invoice) 00702 { 00703 return NULL; 00704 } 00705 owner = &invoice->owner; 00706 return QOF_INSTANCE(owner); 00707 } 00708 00709 static QofInstance* 00710 qofInvoiceGetBillTo (GncInvoice *invoice) 00711 { 00712 GncOwner *billto; 00713 00714 if (!invoice) 00715 { 00716 return NULL; 00717 } 00718 billto = &invoice->billto; 00719 return QOF_INSTANCE(billto); 00720 } 00721 00722 Timespec gncInvoiceGetDateOpened (const GncInvoice *invoice) 00723 { 00724 Timespec ts; 00725 ts.tv_sec = 0; 00726 ts.tv_nsec = 0; 00727 if (!invoice) return ts; 00728 return invoice->date_opened; 00729 } 00730 00731 Timespec gncInvoiceGetDatePosted (const GncInvoice *invoice) 00732 { 00733 Timespec ts; 00734 ts.tv_sec = 0; 00735 ts.tv_nsec = 0; 00736 if (!invoice) return ts; 00737 return invoice->date_posted; 00738 } 00739 00740 Timespec gncInvoiceGetDateDue (const GncInvoice *invoice) 00741 { 00742 Transaction *txn; 00743 Timespec ts; 00744 ts.tv_sec = 0; 00745 ts.tv_nsec = 0; 00746 if (!invoice) return ts; 00747 txn = gncInvoiceGetPostedTxn (invoice); 00748 if (!txn) return ts; 00749 return xaccTransRetDateDueTS (txn); 00750 } 00751 00752 GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice) 00753 { 00754 if (!invoice) return NULL; 00755 return invoice->terms; 00756 } 00757 00758 const char * gncInvoiceGetBillingID (const GncInvoice *invoice) 00759 { 00760 if (!invoice) return NULL; 00761 return invoice->billing_id; 00762 } 00763 00764 const char * gncInvoiceGetNotes (const GncInvoice *invoice) 00765 { 00766 if (!invoice) return NULL; 00767 return invoice->notes; 00768 } 00769 00770 GncOwnerType gncInvoiceGetOwnerType (const GncInvoice *invoice) 00771 { 00772 const GncOwner *owner; 00773 g_return_val_if_fail (invoice, GNC_OWNER_NONE); 00774 00775 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)); 00776 return (gncOwnerGetType (owner)); 00777 } 00778 00779 static gnc_numeric 00780 gncInvoiceGetTotalInternal (GncInvoice *invoice, gboolean use_value, 00781 gboolean use_tax, 00782 gboolean use_payment_type, GncEntryPaymentType type) 00783 { 00784 GList *node; 00785 gnc_numeric total = gnc_numeric_zero(); 00786 gboolean is_cust_doc, is_cn; 00787 00788 g_return_val_if_fail (invoice, total); 00789 00790 /* Is the current document an invoice/credit note related to a customer or a vendor/employee ? 00791 * The GncEntry code needs to know to return the proper entry amounts 00792 */ 00793 is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER); 00794 is_cn = gncInvoiceGetIsCreditNote (invoice); 00795 00796 for (node = gncInvoiceGetEntries(invoice); node; node = node->next) 00797 { 00798 GncEntry *entry = node->data; 00799 gnc_numeric value, tax; 00800 00801 if (use_payment_type && gncEntryGetBillPayment (entry) != type) 00802 continue; 00803 00804 value = gncEntryGetDocValue (entry, FALSE, is_cust_doc, is_cn); 00805 if (gnc_numeric_check (value) == GNC_ERROR_OK) 00806 { 00807 if (use_value) 00808 total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 00809 } 00810 else 00811 g_warning ("bad value in our entry"); 00812 00813 if (use_tax) 00814 { 00815 tax = gncEntryGetDocTaxValue (entry, FALSE, is_cust_doc, is_cn); 00816 if (gnc_numeric_check (tax) == GNC_ERROR_OK) 00817 total = gnc_numeric_add (total, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 00818 else 00819 g_warning ("bad tax-value in our entry"); 00820 } 00821 } 00822 return total; 00823 } 00824 00825 gnc_numeric gncInvoiceGetTotal (GncInvoice *invoice) 00826 { 00827 if (!invoice) return gnc_numeric_zero(); 00828 return gncInvoiceGetTotalInternal(invoice, TRUE, TRUE, FALSE, 0); 00829 } 00830 00831 gnc_numeric gncInvoiceGetTotalSubtotal (GncInvoice *invoice) 00832 { 00833 if (!invoice) return gnc_numeric_zero(); 00834 return gncInvoiceGetTotalInternal(invoice, TRUE, FALSE, FALSE, 0); 00835 } 00836 00837 gnc_numeric gncInvoiceGetTotalTax (GncInvoice *invoice) 00838 { 00839 if (!invoice) return gnc_numeric_zero(); 00840 return gncInvoiceGetTotalInternal(invoice, FALSE, TRUE, FALSE, 0); 00841 } 00842 00843 gnc_numeric gncInvoiceGetTotalOf (GncInvoice *invoice, GncEntryPaymentType type) 00844 { 00845 if (!invoice) return gnc_numeric_zero(); 00846 return gncInvoiceGetTotalInternal(invoice, TRUE, TRUE, TRUE, type); 00847 } 00848 00849 GList * gncInvoiceGetTypeListForOwnerType (GncOwnerType type) 00850 { 00851 GList *type_list = NULL; 00852 switch (type) 00853 { 00854 case GNC_OWNER_CUSTOMER: 00855 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_INVOICE)); 00856 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_CUST_CREDIT_NOTE)); 00857 return type_list; 00858 case GNC_OWNER_VENDOR: 00859 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_INVOICE)); 00860 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_VEND_CREDIT_NOTE)); 00861 return type_list; 00862 case GNC_OWNER_EMPLOYEE: 00863 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_INVOICE)); 00864 type_list = g_list_append (type_list, GINT_TO_POINTER(GNC_INVOICE_EMPL_CREDIT_NOTE)); 00865 return type_list; 00866 default: 00867 return NULL; 00868 } 00869 00870 } 00871 00872 GncInvoiceType gncInvoiceGetType (const GncInvoice *invoice) 00873 { 00874 if (!invoice) return GNC_INVOICE_UNDEFINED; 00875 switch (gncInvoiceGetOwnerType (invoice)) 00876 { 00877 case GNC_OWNER_CUSTOMER: 00878 return (gncInvoiceGetIsCreditNote(invoice) ? 00879 GNC_INVOICE_CUST_CREDIT_NOTE : 00880 GNC_INVOICE_CUST_INVOICE); 00881 case GNC_OWNER_VENDOR: 00882 return (gncInvoiceGetIsCreditNote(invoice) ? 00883 GNC_INVOICE_VEND_CREDIT_NOTE : 00884 GNC_INVOICE_VEND_INVOICE); 00885 case GNC_OWNER_EMPLOYEE: 00886 return (gncInvoiceGetIsCreditNote(invoice) ? 00887 GNC_INVOICE_EMPL_CREDIT_NOTE : 00888 GNC_INVOICE_EMPL_INVOICE); 00889 default: 00890 PWARN ("No invoice types defined for owner %d", 00891 gncInvoiceGetOwnerType (invoice)); 00892 return GNC_INVOICE_UNDEFINED; 00893 } 00894 } 00895 00896 const char * gncInvoiceGetTypeString (const GncInvoice *invoice) 00897 { 00898 GncInvoiceType type = gncInvoiceGetType(invoice); 00899 switch (type) 00900 { 00901 case GNC_INVOICE_CUST_INVOICE: 00902 return _("Invoice"); 00903 case GNC_INVOICE_VEND_INVOICE: 00904 return _("Bill"); 00905 case GNC_INVOICE_EMPL_INVOICE: 00906 return _("Expense"); 00907 case GNC_INVOICE_CUST_CREDIT_NOTE: 00908 return _("Customer Credit Note"); 00909 case GNC_INVOICE_VEND_CREDIT_NOTE: 00910 return _("Vendor Credit Note"); 00911 case GNC_INVOICE_EMPL_CREDIT_NOTE: 00912 return _("Employee Credit Note"); 00913 default: 00914 PWARN("Unknown invoice type"); 00915 return NULL; 00916 } 00917 } 00918 00919 gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice) 00920 { 00921 if (!invoice) return NULL; 00922 return invoice->currency; 00923 } 00924 00925 GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice) 00926 { 00927 if (!invoice) return NULL; 00928 return &invoice->billto; 00929 } 00930 00931 GNCLot * gncInvoiceGetPostedLot (const GncInvoice *invoice) 00932 { 00933 if (!invoice) return NULL; 00934 return invoice->posted_lot; 00935 } 00936 00937 Transaction * gncInvoiceGetPostedTxn (const GncInvoice *invoice) 00938 { 00939 if (!invoice) return NULL; 00940 return invoice->posted_txn; 00941 } 00942 00943 Account * gncInvoiceGetPostedAcc (const GncInvoice *invoice) 00944 { 00945 if (!invoice) return NULL; 00946 return invoice->posted_acc; 00947 } 00948 00949 gboolean gncInvoiceGetActive (const GncInvoice *invoice) 00950 { 00951 if (!invoice) return FALSE; 00952 return invoice->active; 00953 } 00954 00955 gboolean gncInvoiceGetIsCreditNote (const GncInvoice *invoice) 00956 { 00957 gint64 is_cn; 00958 if (!invoice) return FALSE; 00959 if (kvp_frame_get_gint64(invoice->inst.kvp_data, GNC_INVOICE_IS_CN)) 00960 return TRUE; 00961 else 00962 return FALSE; 00963 } 00964 00965 00966 gnc_numeric gncInvoiceGetToChargeAmount (const GncInvoice *invoice) 00967 { 00968 if (!invoice) return gnc_numeric_zero(); 00969 return invoice->to_charge_amount; 00970 } 00971 00972 EntryList * gncInvoiceGetEntries (GncInvoice *invoice) 00973 { 00974 if (!invoice) return NULL; 00975 return invoice->entries; 00976 } 00977 00978 GNCPrice * gncInvoiceGetPrice(GncInvoice *invoice, gnc_commodity *commodity) 00979 { 00980 GList *node = g_list_first(invoice->prices); 00981 00982 while (node != NULL) 00983 { 00984 GNCPrice *curr = (GNCPrice*)node->data; 00985 00986 if (gnc_commodity_equal(commodity, gnc_price_get_commodity(curr))) 00987 return curr; 00988 00989 node = g_list_next(node); 00990 } 00991 00992 return NULL; 00993 } 00994 00995 static QofCollection* 00996 qofInvoiceGetEntries (GncInvoice *invoice) 00997 { 00998 QofCollection *entry_coll; 00999 GList *list; 01000 QofInstance *entry; 01001 01002 entry_coll = qof_collection_new(GNC_ID_ENTRY); 01003 for (list = gncInvoiceGetEntries(invoice); list != NULL; list = list->next) 01004 { 01005 entry = QOF_INSTANCE(list->data); 01006 qof_collection_add_entity(entry_coll, entry); 01007 } 01008 return entry_coll; 01009 } 01010 01011 static void 01012 qofInvoiceEntryCB (QofInstance *ent, gpointer user_data) 01013 { 01014 GncInvoice *invoice; 01015 01016 invoice = (GncInvoice*)user_data; 01017 if (!invoice || !ent) 01018 { 01019 return; 01020 } 01021 switch (gncInvoiceGetOwnerType (invoice)) 01022 { 01023 case GNC_OWNER_VENDOR: 01024 { 01025 gncBillAddEntry (invoice, (GncEntry*) ent); 01026 break; 01027 } 01028 default : 01029 { 01030 gncInvoiceAddEntry(invoice, (GncEntry*)ent); 01031 break; 01032 } 01033 } 01034 } 01035 01036 static void 01037 qofInvoiceSetEntries(GncInvoice *invoice, QofCollection *entry_coll) 01038 { 01039 if (!entry_coll) 01040 { 01041 return; 01042 } 01043 if (0 == safe_strcmp(qof_collection_get_type(entry_coll), GNC_ID_ENTRY)) 01044 { 01045 qof_collection_foreach(entry_coll, qofInvoiceEntryCB, invoice); 01046 } 01047 } 01048 01049 static GncJob* 01050 qofInvoiceGetJob (const GncInvoice *invoice) 01051 { 01052 if (!invoice) 01053 { 01054 return NULL; 01055 } 01056 return invoice->job; 01057 } 01058 01059 static void 01060 qofInvoiceSetJob (GncInvoice *invoice, GncJob *job) 01061 { 01062 if (!invoice) 01063 { 01064 return; 01065 } 01066 invoice->job = job; 01067 } 01068 01069 static void 01070 gncInvoiceDetachFromLot (GNCLot *lot) 01071 { 01072 KvpFrame *kvp; 01073 01074 if (!lot) return; 01075 gnc_lot_begin_edit (lot); 01076 kvp = gnc_lot_get_slots (lot); 01077 kvp_frame_set_slot_path (kvp, NULL, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); 01078 qof_instance_set_dirty (QOF_INSTANCE (lot)); 01079 gnc_lot_commit_edit (lot); 01080 } 01081 01082 static void 01083 gncInvoiceAttachToLot (GncInvoice *invoice, GNCLot *lot) 01084 { 01085 KvpFrame *kvp; 01086 KvpValue *value; 01087 01088 if (!invoice || !lot) 01089 return; 01090 01091 if (invoice->posted_lot) return; /* Cannot reset invoice's lot */ 01092 01093 gnc_lot_begin_edit (lot); 01094 kvp = gnc_lot_get_slots (lot); 01095 value = kvp_value_new_guid (qof_instance_get_guid (QOF_INSTANCE(invoice))); 01096 kvp_frame_set_slot_path (kvp, value, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); 01097 qof_instance_set_dirty (QOF_INSTANCE (lot)); 01098 gnc_lot_commit_edit (lot); 01099 kvp_value_delete (value); 01100 gncInvoiceSetPostedLot (invoice, lot); 01101 } 01102 01103 GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot) 01104 { 01105 KvpFrame *kvp; 01106 KvpValue *value; 01107 GncGUID *guid; 01108 QofBook *book; 01109 01110 if (!lot) return NULL; 01111 01112 book = gnc_lot_get_book (lot); 01113 kvp = gnc_lot_get_slots (lot); 01114 value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); 01115 if (!value) return NULL; 01116 01117 guid = kvp_value_get_guid (value); 01118 return gncInvoiceLookup(book, guid); 01119 } 01120 01121 static void 01122 gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn) 01123 { 01124 KvpFrame *kvp; 01125 KvpValue *value; 01126 01127 if (!invoice || !txn) 01128 return; 01129 01130 if (invoice->posted_txn) return; /* Cannot reset invoice's txn */ 01131 01132 xaccTransBeginEdit (txn); 01133 kvp = xaccTransGetSlots (txn); 01134 value = kvp_value_new_guid (qof_instance_get_guid(QOF_INSTANCE(invoice))); 01135 kvp_frame_set_slot_path (kvp, value, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); 01136 kvp_value_delete (value); 01137 xaccTransSetTxnType (txn, TXN_TYPE_INVOICE); 01138 xaccTransCommitEdit (txn); 01139 gncInvoiceSetPostedTxn (invoice, txn); 01140 } 01141 01142 GncInvoice * 01143 gncInvoiceGetInvoiceFromTxn (const Transaction *txn) 01144 { 01145 KvpFrame *kvp; 01146 KvpValue *value; 01147 GncGUID *guid; 01148 QofBook *book; 01149 01150 if (!txn) return NULL; 01151 01152 book = xaccTransGetBook (txn); 01153 kvp = xaccTransGetSlots (txn); 01154 value = kvp_frame_get_slot_path (kvp, GNC_INVOICE_ID, GNC_INVOICE_GUID, NULL); 01155 if (!value) return NULL; 01156 01157 guid = kvp_value_get_guid (value); 01158 return gncInvoiceLookup(book, guid); 01159 } 01160 01161 gboolean gncInvoiceAmountPositive (const GncInvoice *invoice) 01162 { 01163 switch (gncInvoiceGetType (invoice)) 01164 { 01165 case GNC_INVOICE_CUST_INVOICE: 01166 case GNC_INVOICE_VEND_CREDIT_NOTE: 01167 case GNC_INVOICE_EMPL_CREDIT_NOTE: 01168 return TRUE; 01169 case GNC_INVOICE_CUST_CREDIT_NOTE: 01170 case GNC_INVOICE_VEND_INVOICE: 01171 case GNC_INVOICE_EMPL_INVOICE: 01172 return FALSE; 01173 case GNC_INVOICE_UNDEFINED: 01174 default: 01175 /* Should never be reached. 01176 * If it is, perhaps a new value is added to GncInvoiceType ? */ 01177 g_assert_not_reached(); 01178 return FALSE; 01179 } 01180 } 01181 01182 static gboolean gncInvoicePostAddSplit (QofBook *book, 01183 Account *acc, 01184 Transaction *txn, 01185 gnc_numeric value, 01186 const gchar *memo, 01187 const gchar *type, 01188 GncInvoice *invoice) 01189 { 01190 Split *split; 01191 01192 split = xaccMallocSplit (book); 01193 /* set action and memo? */ 01194 01195 xaccSplitSetMemo (split, memo); 01196 xaccSplitSetAction (split, type); 01197 01198 /* Need to insert this split into the account AND txn before 01199 * we set the Base Value. Otherwise SetBaseValue complains 01200 * that we don't have an account and fails to set the value. 01201 */ 01202 xaccAccountBeginEdit (acc); 01203 xaccAccountInsertSplit (acc, split); 01204 xaccAccountCommitEdit (acc); 01205 xaccTransAppendSplit (txn, split); 01206 01207 /* General note on the split creations below: 01208 * Invoice and bill amounts are always stored as positive values in entries 01209 * So to convert them to proper splits, the amounts may have to be reverted 01210 * to have the proper effect on the account balance. 01211 * Credit notes have the opposite effect of invoices/bills, but their amounts 01212 * are stored as negative values as well. So to convert them into splits 01213 * they can be treated exactly the same as their invoice/bill counter parts. 01214 * The net effect is that the owner type is sufficient to determine whether a 01215 * value has to be reverted when converting an invoice/bill/cn amount to a split. 01216 */ 01217 if (gnc_commodity_equal(xaccAccountGetCommodity(acc), invoice->currency)) 01218 { 01219 xaccSplitSetBaseValue (split, value, 01220 invoice->currency); 01221 } 01222 else 01223 { 01224 /*need to do conversion */ 01225 GNCPrice *price = gncInvoiceGetPrice(invoice, xaccAccountGetCommodity(acc)); 01226 01227 if (price == NULL) 01228 { 01229 /*This is an error, which shouldn't even be able to happen. 01230 We can't really do anything sensible about it, and this is 01231 a user-interface free zone so we can't try asking the user 01232 again either, have to return NULL*/ 01233 return FALSE; 01234 } 01235 else 01236 { 01237 gnc_numeric converted_amount; 01238 xaccSplitSetValue(split, value); 01239 converted_amount = gnc_numeric_div(value, gnc_price_get_value(price), GNC_DENOM_AUTO, GNC_HOW_RND_ROUND_HALF_UP); 01240 DEBUG("converting from %f to %f\n", gnc_numeric_to_double(value), gnc_numeric_to_double(converted_amount)); 01241 xaccSplitSetAmount(split, converted_amount); 01242 } 01243 } 01244 01245 return TRUE; 01246 } 01247 01248 Transaction * gncInvoicePostToAccount (GncInvoice *invoice, Account *acc, 01249 Timespec *post_date, Timespec *due_date, 01250 const char * memo, gboolean accumulatesplits) 01251 { 01252 Transaction *txn; 01253 QofBook *book; 01254 GNCLot *lot = NULL; 01255 GList *iter; 01256 GList *splitinfo = NULL; 01257 gnc_numeric total; 01258 gboolean is_cust_doc; 01259 gboolean is_cn; 01260 const char *name, *type; 01261 char *lot_title; 01262 Account *ccard_acct = NULL; 01263 const GncOwner *owner; 01264 gboolean autopay = TRUE; /* FIXME this will have to become a user selectable option at some point */ 01265 01266 if (!invoice || !acc) return NULL; 01267 01268 gncInvoiceBeginEdit (invoice); 01269 book = qof_instance_get_book(invoice); 01270 01271 /* Stabilize the Billing Terms of this invoice */ 01272 if (invoice->terms) 01273 gncInvoiceSetTerms (invoice, 01274 gncBillTermReturnChild (invoice->terms, TRUE)); 01275 01276 /* GncEntry functions need to know if the invoice/credit note is for a customer or a vendor/employee. */ 01277 is_cust_doc = (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_CUSTOMER); 01278 is_cn = gncInvoiceGetIsCreditNote (invoice); 01279 01280 /* Figure out if we need to separate out "credit-card" items */ 01281 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)); 01282 if (gncInvoiceGetOwnerType (invoice) == GNC_OWNER_EMPLOYEE) 01283 ccard_acct = gncEmployeeGetCCard (gncOwnerGetEmployee (owner)); 01284 01285 /* Create a new lot for this invoice */ 01286 lot = gnc_lot_new (book); 01287 gnc_lot_begin_edit (lot); 01288 01289 type = gncInvoiceGetTypeString (invoice); 01290 01291 /* Set the lot title */ 01292 lot_title = g_strdup_printf ("%s %s", type, gncInvoiceGetID (invoice)); 01293 gnc_lot_set_title (lot, lot_title); 01294 g_free (lot_title); 01295 01296 /* Create a new transaction */ 01297 txn = xaccMallocTransaction (book); 01298 xaccTransBeginEdit (txn); 01299 01300 name = gncOwnerGetName (gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice))); 01301 01302 /* Set Transaction Description (Owner Name) , Num (invoice ID), Currency */ 01303 xaccTransSetDescription (txn, name ? name : ""); 01304 xaccTransSetNum (txn, gncInvoiceGetID (invoice)); 01305 xaccTransSetCurrency (txn, invoice->currency); 01306 01307 /* Entered and Posted at date */ 01308 xaccTransSetDateEnteredSecs (txn, time(NULL)); 01309 if (post_date) 01310 { 01311 xaccTransSetDatePostedTS (txn, post_date); 01312 gncInvoiceSetDatePosted (invoice, *post_date); 01313 } 01314 01315 if (due_date) 01316 xaccTransSetDateDueTS (txn, due_date); 01317 01318 /* Iterate through the entries; sum up everything for each account. 01319 * then create the appropriate splits in this txn. 01320 */ 01321 total = gnc_numeric_zero(); 01322 for (iter = gncInvoiceGetEntries(invoice); iter; iter = iter->next) 01323 { 01324 gnc_numeric value, tax; 01325 GList *taxes; 01326 GncEntry * entry = iter->data; 01327 Account *this_acc; 01328 01329 /* Stabilize the TaxTable in this entry */ 01330 gncEntryBeginEdit (entry); 01331 if (is_cust_doc) 01332 gncEntrySetInvTaxTable 01333 (entry, gncTaxTableReturnChild (gncEntryGetInvTaxTable (entry), TRUE)); 01334 else 01335 { 01336 gncEntrySetBillTaxTable 01337 (entry, gncTaxTableReturnChild (gncEntryGetBillTaxTable (entry), TRUE)); 01338 01339 /* If this is a bill, and the entry came from an invoice originally, copy the price */ 01340 if (gncEntryGetBillable (entry)) 01341 gncEntrySetInvPrice (entry, gncEntryGetBillPrice (entry)); 01342 } 01343 gncEntryCommitEdit (entry); 01344 01345 /* Obtain the Entry's Value and TaxValues */ 01346 value = gncEntryGetBalValue (entry, FALSE, is_cust_doc); 01347 tax = gncEntryGetBalTaxValue (entry, FALSE, is_cust_doc); 01348 taxes = gncEntryGetBalTaxValues (entry, is_cust_doc); 01349 01350 /* add the value for the account split */ 01351 this_acc = (is_cust_doc ? gncEntryGetInvAccount (entry) : 01352 gncEntryGetBillAccount (entry)); 01353 if (this_acc) 01354 { 01355 if (gnc_numeric_check (value) == GNC_ERROR_OK) 01356 { 01357 if (accumulatesplits) 01358 splitinfo = gncAccountValueAdd (splitinfo, this_acc, value); 01359 else if (!gncInvoicePostAddSplit (book, this_acc, txn, value, 01360 gncEntryGetDescription (entry), 01361 type, invoice)) 01362 { 01363 /*This is an error, which shouldn't even be able to happen. 01364 We can't really do anything sensible about it, and this is 01365 a user-interface free zone so we can't try asking the user 01366 again either, have to return NULL*/ 01367 return NULL; 01368 } 01369 01370 /* If there is a credit-card account, and this is a CCard 01371 * payment type, the don't add it to the total, and instead 01372 * create a split to the CC Acct with a memo of the entry 01373 * description instead of the provided memo. Note that the 01374 * value reversal is the same as the post account. 01375 * 01376 * Note: we don't have to worry about the tax values -- 01377 * expense vouchers don't have them. 01378 */ 01379 if (ccard_acct && gncEntryGetBillPayment (entry) == GNC_PAYMENT_CARD) 01380 { 01381 Split *split; 01382 01383 split = xaccMallocSplit (book); 01384 /* set action? */ 01385 xaccSplitSetMemo (split, gncEntryGetDescription (entry)); 01386 xaccSplitSetAction (split, type); 01387 xaccAccountBeginEdit (ccard_acct); 01388 xaccAccountInsertSplit (ccard_acct, split); 01389 xaccAccountCommitEdit (ccard_acct); 01390 xaccTransAppendSplit (txn, split); 01391 xaccSplitSetBaseValue (split, gnc_numeric_neg (value), 01392 invoice->currency); 01393 01394 } 01395 else 01396 total = gnc_numeric_add (total, value, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01397 01398 } 01399 else 01400 g_warning ("bad value in our entry"); 01401 } 01402 01403 /* now merge in the TaxValues */ 01404 splitinfo = gncAccountValueAddList (splitinfo, taxes); 01405 01406 /* ... and add the tax total */ 01407 if (gnc_numeric_check (tax) == GNC_ERROR_OK) 01408 total = gnc_numeric_add (total, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01409 else 01410 g_warning ("bad tax in our entry"); 01411 01412 gncAccountValueDestroy (taxes); 01413 } /* for */ 01414 01415 /* Iterate through the splitinfo list and generate the splits */ 01416 for (iter = splitinfo; iter; iter = iter->next) 01417 { 01418 GncAccountValue *acc_val = iter->data; 01419 if (!gncInvoicePostAddSplit (book, acc_val->account, txn, acc_val->value, 01420 memo, type, invoice)) 01421 { 01422 /*This is an error, which shouldn't even be able to happen. 01423 We can't really do anything sensible about it, and this is 01424 a user-interface free zone so we can't try asking the user 01425 again either, have to return NULL*/ 01426 return NULL; 01427 } 01428 } 01429 01430 /* If there is a ccard account, we may have an additional "to_card" payment. 01431 * we should make that now. 01432 */ 01433 if (ccard_acct && !gnc_numeric_zero_p (invoice->to_charge_amount)) 01434 { 01435 Split *split = xaccMallocSplit (book); 01436 01437 /* To charge amount is stored in document value. We need balance value here 01438 * so convert if necessary. */ 01439 gnc_numeric to_charge_bal_amount = (is_cn ? gnc_numeric_neg (invoice->to_charge_amount) 01440 : invoice->to_charge_amount); 01441 01442 /* Set memo. action? */ 01443 xaccSplitSetMemo (split, _("Extra to Charge Card")); 01444 xaccSplitSetAction (split, type); 01445 01446 xaccAccountBeginEdit (ccard_acct); 01447 xaccAccountInsertSplit (ccard_acct, split); 01448 xaccAccountCommitEdit (ccard_acct); 01449 xaccTransAppendSplit (txn, split); 01450 xaccSplitSetBaseValue (split, gnc_numeric_neg (to_charge_bal_amount), 01451 invoice->currency); 01452 01453 total = gnc_numeric_sub (total, to_charge_bal_amount, 01454 GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01455 } 01456 01457 /* Now create the Posted split (which is the opposite sign of the above splits) */ 01458 { 01459 Split *split = xaccMallocSplit (book); 01460 01461 /* Set action/memo */ 01462 xaccSplitSetMemo (split, memo); 01463 xaccSplitSetAction (split, type); 01464 01465 xaccAccountBeginEdit (acc); 01466 xaccAccountInsertSplit (acc, split); 01467 xaccAccountCommitEdit (acc); 01468 xaccTransAppendSplit (txn, split); 01469 xaccSplitSetBaseValue (split, gnc_numeric_neg (total), 01470 invoice->currency); 01471 01472 /* add this split to the lot */ 01473 gnc_lot_add_split (lot, split); 01474 } 01475 01476 /* Now attach this invoice to the txn, lot, and account */ 01477 gncInvoiceAttachToLot (invoice, lot); 01478 gncInvoiceAttachToTxn (invoice, txn); 01479 gncInvoiceSetPostedAcc (invoice, acc); 01480 01481 xaccTransSetReadOnly (txn, _("Generated from an invoice. Try unposting the invoice.")); 01482 xaccTransCommitEdit (txn); 01483 01484 gncAccountValueDestroy (splitinfo); 01485 01486 gnc_lot_commit_edit (lot); 01487 gncInvoiceCommitEdit (invoice); 01488 01489 /* If requested, attempt to automatically apply open payments 01490 * and reverse documents to this lot to close it (or at least 01491 * reduce its balance) */ 01492 if (autopay) 01493 gncInvoiceAutoApplyPayments (invoice); 01494 01495 return txn; 01496 } 01497 01498 gboolean 01499 gncInvoiceUnpost (GncInvoice *invoice, gboolean reset_tax_tables) 01500 { 01501 Transaction *txn; 01502 GNCLot *lot; 01503 GList *lot_split_list, *lot_split_iter; 01504 01505 if (!invoice) return FALSE; 01506 if (!gncInvoiceIsPosted (invoice)) return FALSE; 01507 01508 txn = gncInvoiceGetPostedTxn (invoice); 01509 g_return_val_if_fail (txn, FALSE); 01510 01511 lot = gncInvoiceGetPostedLot (invoice); 01512 g_return_val_if_fail (lot, FALSE); 01513 01514 /* Destroy the Posted Transaction */ 01515 xaccTransClearReadOnly (txn); 01516 xaccTransBeginEdit (txn); 01517 xaccTransDestroy (txn); 01518 xaccTransCommitEdit (txn); 01519 01520 /* Disconnect the lot from the invoice; re-attach to the invoice owner */ 01521 gncInvoiceDetachFromLot (lot); 01522 gncOwnerAttachToLot (&invoice->owner, lot); 01523 01524 /* Check if this invoice was linked to other lots (payments/inverse signed 01525 * invoices). 01526 * If this is the case, recreate the link transaction between all the remaining lots. 01527 * 01528 * Note that before GnuCash 2.6 payments were not stored in separate lots, but 01529 * always ended up in invoice lots when matched to an invoice. Over-payments 01530 * were copied to a new lot, to which later an invoice was added again and so on. 01531 * These over-payments were handled with automatic payment forward transactions. 01532 * You could consider these transactions to be links between lots as well, but 01533 * to avoid some unexpected behavior, these will not be altered here. 01534 */ 01535 01536 // Note: make a copy of the lot list here, when splits are deleted from the lot, 01537 // the original list may be destroyed by the lot code. 01538 lot_split_list = g_list_copy (gnc_lot_get_split_list (lot)); 01539 for (lot_split_iter = lot_split_list; lot_split_iter; lot_split_iter = lot_split_iter->next) 01540 { 01541 Split *split = lot_split_iter->data; 01542 GList *other_split_list, *list_iter; 01543 Transaction *other_txn = xaccSplitGetParent (split); 01544 GList *lot_list = NULL; 01545 01546 /* Only work with transactions that link invoices and payments. 01547 * Note: this check also catches the possible case of NULL splits. */ 01548 if (xaccTransGetTxnType (other_txn) != TXN_TYPE_LINK) 01549 continue; 01550 01551 /* Save a list of lots this linking transaction linked to */ 01552 other_split_list = xaccTransGetSplitList (other_txn); 01553 for (list_iter = other_split_list; list_iter; list_iter = list_iter->next) 01554 { 01555 Split *other_split = list_iter->data; 01556 GNCLot *other_lot = xaccSplitGetLot (other_split); 01557 01558 /* Omit the lot we are about to delete */ 01559 if (other_lot == lot) 01560 continue; 01561 01562 lot_list = g_list_prepend (lot_list, other_lot); 01563 } 01564 /* Maintain original split order */ 01565 lot_list = g_list_reverse (lot_list); 01566 01567 /* Now remove this link transaction. */ 01568 xaccTransClearReadOnly (other_txn); 01569 xaccTransBeginEdit (other_txn); 01570 xaccTransDestroy (other_txn); 01571 xaccTransCommitEdit (other_txn); 01572 01573 /* Re-balance the saved lots as well as is possible */ 01574 gncOwnerAutoApplyPaymentsWithLots (&invoice->owner, lot_list); 01575 01576 /* If any of the saved lots has no more splits, then destroy it. 01577 * Otherwise if any has an invoice associated with it, 01578 * send it a modified event to reset its paid status */ 01579 for (list_iter = lot_list; list_iter; list_iter = list_iter->next) 01580 { 01581 GNCLot *other_lot = list_iter->data; 01582 GncInvoice *other_invoice = gncInvoiceGetInvoiceFromLot (other_lot); 01583 01584 if (!gnc_lot_count_splits (other_lot)) 01585 gnc_lot_destroy (other_lot); 01586 else if (other_invoice) 01587 qof_event_gen (QOF_INSTANCE(other_invoice), QOF_EVENT_MODIFY, NULL); 01588 } 01589 } 01590 g_list_free (lot_split_list); 01591 01592 /* If the lot has no splits, then destroy it */ 01593 if (!gnc_lot_count_splits (lot)) 01594 gnc_lot_destroy (lot); 01595 01596 /* Clear out the invoice posted information */ 01597 gncInvoiceBeginEdit (invoice); 01598 01599 invoice->posted_acc = NULL; 01600 invoice->posted_txn = NULL; 01601 invoice->posted_lot = NULL; 01602 invoice->date_posted.tv_sec = invoice->date_posted.tv_nsec = 0; 01603 01604 /* if we've been asked to reset the tax tables, then do so */ 01605 if (reset_tax_tables) 01606 { 01607 gboolean is_cust_doc = (gncInvoiceGetOwnerType(invoice) == GNC_OWNER_CUSTOMER); 01608 GList *iter; 01609 01610 for (iter = gncInvoiceGetEntries(invoice); iter; iter = iter->next) 01611 { 01612 GncEntry *entry = iter->data; 01613 01614 gncEntryBeginEdit(entry); 01615 if (is_cust_doc) 01616 gncEntrySetInvTaxTable(entry, 01617 gncTaxTableGetParent(gncEntryGetInvTaxTable(entry))); 01618 else 01619 gncEntrySetBillTaxTable(entry, 01620 gncTaxTableGetParent(gncEntryGetBillTaxTable(entry))); 01621 gncEntryCommitEdit(entry); 01622 } 01623 } 01624 01625 mark_invoice (invoice); 01626 gncInvoiceCommitEdit (invoice); 01627 01628 return TRUE; 01629 } 01630 01631 struct lotmatch 01632 { 01633 const GncOwner *owner; 01634 gboolean positive_balance; 01635 }; 01636 01637 static gboolean 01638 gnc_lot_match_owner_balancing (GNCLot *lot, gpointer user_data) 01639 { 01640 struct lotmatch *lm = user_data; 01641 GncOwner owner_def; 01642 const GncOwner *owner; 01643 gnc_numeric balance = gnc_lot_get_balance (lot); 01644 01645 /* Could (part of) this lot serve to balance the lot 01646 * for which this query was run ?*/ 01647 if (lm->positive_balance == gnc_numeric_positive_p (balance)) 01648 return FALSE; 01649 01650 /* Is it ours? Either the lot owner or the lot invoice owner should match */ 01651 if (!gncOwnerGetOwnerFromLot (lot, &owner_def)) 01652 { 01653 const GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot); 01654 if (!invoice) 01655 return FALSE; 01656 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)); 01657 } 01658 else 01659 owner = gncOwnerGetEndOwner (&owner_def); 01660 01661 return gncOwnerEqual (owner, lm->owner); 01662 } 01663 01664 void gncInvoiceAutoApplyPayments (GncInvoice *invoice) 01665 { 01666 GNCLot *inv_lot; 01667 Account *acct; 01668 const GncOwner *owner; 01669 GList *lot_list; 01670 struct lotmatch lm; 01671 01672 /* General note: "paying" in this context means balancing 01673 * a lot, by linking opposite signed lots together. So below the term 01674 * "payment" can both mean a true payment or it can mean a document of 01675 * the opposite sign (invoice vs credit note). It just 01676 * depends on what type of document was given as parameter 01677 * to this function. */ 01678 01679 /* Payments can only be applied to posted invoices */ 01680 g_return_if_fail (invoice); 01681 g_return_if_fail (invoice->posted_lot); 01682 01683 inv_lot = invoice->posted_lot; 01684 acct = invoice->posted_acc; 01685 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)); 01686 01687 /* Find all lots whose balance (or part of their balance) could be 01688 * used to close this lot. 01689 * To be eligible, the lots have to have an opposite signed balance 01690 * and be for the same owner. 01691 * For example, for an invoice lot, payment lots and credit note lots 01692 * could be used. */ 01693 lm.positive_balance = gnc_numeric_positive_p (gnc_lot_get_balance (inv_lot)); 01694 lm.owner = owner; 01695 lot_list = xaccAccountFindOpenLots (acct, gnc_lot_match_owner_balancing, 01696 &lm, NULL); 01697 01698 lot_list = g_list_prepend (lot_list, inv_lot); 01699 gncOwnerAutoApplyPaymentsWithLots (owner, lot_list); 01700 g_list_free (lot_list); 01701 } 01702 01703 /* 01704 * Create a payment of "amount" for the invoice owner and attempt 01705 * to balance it with the given invoice. 01706 */ 01707 void 01708 gncInvoiceApplyPayment (const GncInvoice *invoice, Transaction *txn, 01709 Account *xfer_acc, gnc_numeric amount, 01710 gnc_numeric exch, Timespec date, 01711 const char *memo, const char *num) 01712 { 01713 GNCLot *payment_lot, *invoice_lot; 01714 GList *selected_lots = NULL; 01715 const GncOwner *owner; 01716 01717 /* Verify our arguments */ 01718 if (!invoice || !gncInvoiceIsPosted (invoice) || !xfer_acc) return; 01719 01720 owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice)); 01721 g_return_if_fail (owner->owner.undefined); 01722 01723 /* Create a lot for this payment */ 01724 payment_lot = gncOwnerCreatePaymentLot (owner, txn, invoice->posted_acc, xfer_acc, 01725 amount, exch, date, memo, num); 01726 01727 /* Select the invoice as only payment candidate */ 01728 selected_lots = g_list_prepend (selected_lots, invoice->posted_lot); 01729 01730 /* And link the invoice lot and the payment lot together as well as possible. */ 01731 if (payment_lot) 01732 selected_lots = g_list_prepend (selected_lots, payment_lot); 01733 gncOwnerAutoApplyPaymentsWithLots (owner, selected_lots); 01734 } 01735 01736 static gboolean gncInvoiceDateExists (const Timespec *date) 01737 { 01738 g_return_val_if_fail (date, FALSE); 01739 if (date->tv_sec || date->tv_nsec) return TRUE; 01740 return FALSE; 01741 } 01742 01743 gboolean gncInvoiceIsPosted (const GncInvoice *invoice) 01744 { 01745 if (!invoice) return FALSE; 01746 return gncInvoiceDateExists (&(invoice->date_posted)); 01747 } 01748 01749 gboolean gncInvoiceIsPaid (const GncInvoice *invoice) 01750 { 01751 if (!invoice) return FALSE; 01752 if (!invoice->posted_lot) return FALSE; 01753 return gnc_lot_is_closed(invoice->posted_lot); 01754 } 01755 01756 /* ================================================================== */ 01757 01758 void gncInvoiceBeginEdit (GncInvoice *invoice) 01759 { 01760 qof_begin_edit(&invoice->inst); 01761 } 01762 01763 static void gncInvoiceOnError (QofInstance *inst, QofBackendError errcode) 01764 { 01765 PERR("Invoice QofBackend Failure: %d", errcode); 01766 gnc_engine_signal_commit_error( errcode ); 01767 } 01768 01769 static void gncInvoiceOnDone (QofInstance *invoice) { } 01770 01771 static void invoice_free (QofInstance *inst) 01772 { 01773 GncInvoice *invoice = (GncInvoice *) inst; 01774 gncInvoiceFree (invoice); 01775 } 01776 01777 void gncInvoiceCommitEdit (GncInvoice *invoice) 01778 { 01779 if (!qof_commit_edit (QOF_INSTANCE(invoice))) return; 01780 qof_commit_edit_part2 (&invoice->inst, gncInvoiceOnError, 01781 gncInvoiceOnDone, invoice_free); 01782 } 01783 01784 int gncInvoiceCompare (const GncInvoice *a, const GncInvoice *b) 01785 { 01786 int compare; 01787 01788 if (a == b) return 0; 01789 if (!a) return -1; 01790 if (!b) return 1; 01791 01792 compare = safe_strcmp (a->id, b->id); 01793 if (compare) return compare; 01794 01795 compare = timespec_cmp (&(a->date_opened), &(b->date_opened)); 01796 if (compare) return compare; 01797 01798 compare = timespec_cmp (&(a->date_posted), &(b->date_posted)); 01799 if (compare) return compare; 01800 01801 return qof_instance_guid_compare(a, b); 01802 } 01803 01804 gboolean gncInvoiceEqual(const GncInvoice *a, const GncInvoice *b) 01805 { 01806 if (a == NULL && b == NULL) return TRUE; 01807 if (a == NULL || b == NULL) return FALSE; 01808 01809 g_return_val_if_fail(GNC_IS_INVOICE(a), FALSE); 01810 g_return_val_if_fail(GNC_IS_INVOICE(b), FALSE); 01811 01812 if (safe_strcmp(a->id, b->id) != 0) 01813 { 01814 PWARN("IDs differ: %s vs %s", a->id, b->id); 01815 return FALSE; 01816 } 01817 01818 if (safe_strcmp(a->notes, b->notes) != 0) 01819 { 01820 PWARN("Notes differ: %s vs %s", a->notes, b->notes); 01821 return FALSE; 01822 } 01823 01824 if (safe_strcmp(a->billing_id, b->billing_id) != 0) 01825 { 01826 PWARN("Billing IDs differ: %s vs %s", a->billing_id, b->billing_id); 01827 return FALSE; 01828 } 01829 01830 if (safe_strcmp(a->printname, b->printname) != 0) 01831 { 01832 PWARN("Printnames differ: %s vs %s", a->printname, b->printname); 01833 return FALSE; 01834 } 01835 01836 if (a->active != b->active) 01837 { 01838 PWARN("Active flags differ"); 01839 return FALSE; 01840 } 01841 01842 if (!gncBillTermEqual(a->terms, b->terms)) 01843 { 01844 PWARN("Billterms differ"); 01845 return FALSE; 01846 } 01847 01848 if (!gncJobEqual(a->job, b->job)) 01849 { 01850 PWARN("Jobs differ"); 01851 return FALSE; 01852 } 01853 01854 if (!gnc_commodity_equal(a->currency, b->currency)) 01855 { 01856 PWARN("Currencies differ"); 01857 return FALSE; 01858 } 01859 01860 if (!xaccAccountEqual(a->posted_acc, b->posted_acc, TRUE)) 01861 { 01862 PWARN("Posted accounts differ"); 01863 return FALSE; 01864 } 01865 01866 if (!xaccTransEqual(a->posted_txn, b->posted_txn, TRUE, TRUE, TRUE, FALSE)) 01867 { 01868 PWARN("Posted tx differ"); 01869 return FALSE; 01870 } 01871 01872 #if 0 01873 if (!gncLotEqual(a->posted_lot, b->posted_lot)) 01874 { 01875 PWARN("Posted lots differ"); 01876 return FALSE; 01877 } 01878 #endif 01879 01880 /* FIXME: Need real checks */ 01881 #if 0 01882 GList *entries; 01883 GList *prices; 01884 GncOwner owner; 01885 GncOwner billto; 01886 Timespec date_opened; 01887 Timespec date_posted; 01888 01889 gnc_numeric to_charge_amount; 01890 #endif 01891 01892 return TRUE; 01893 } 01894 01895 /* ============================================================= */ 01896 /* Package-Private functions */ 01897 01898 static const char * _gncInvoicePrintable (gpointer obj) 01899 { 01900 GncInvoice *invoice = obj; 01901 01902 g_return_val_if_fail (invoice, NULL); 01903 01904 if (qof_instance_get_dirty_flag(invoice) || invoice->printname == NULL) 01905 { 01906 if (invoice->printname) g_free (invoice->printname); 01907 01908 invoice->printname = 01909 g_strdup_printf ("%s%s", invoice->id, 01910 gncInvoiceIsPosted (invoice) ? _(" (posted)") : ""); 01911 } 01912 01913 return invoice->printname; 01914 } 01915 01916 static void 01917 destroy_invoice_on_book_close(QofInstance *ent, gpointer data) 01918 { 01919 GncInvoice* invoice = GNC_INVOICE(ent); 01920 01921 gncInvoiceBeginEdit(invoice); 01922 gncInvoiceDestroy(invoice); 01923 } 01924 01925 static void 01926 gnc_invoice_book_end(QofBook* book) 01927 { 01928 QofCollection *col; 01929 01930 col = qof_book_get_collection(book, GNC_ID_INVOICE); 01931 qof_collection_foreach(col, destroy_invoice_on_book_close, NULL); 01932 } 01933 01934 static QofObject gncInvoiceDesc = 01935 { 01936 DI(.interface_version = ) QOF_OBJECT_VERSION, 01937 DI(.e_type = ) _GNC_MOD_NAME, 01938 DI(.type_label = ) "Invoice", 01939 DI(.create = ) (gpointer)gncInvoiceCreate, 01940 DI(.book_begin = ) NULL, 01941 DI(.book_end = ) gnc_invoice_book_end, 01942 DI(.is_dirty = ) qof_collection_is_dirty, 01943 DI(.mark_clean = ) qof_collection_mark_clean, 01944 DI(.foreach = ) qof_collection_foreach, 01945 DI(.printable = ) _gncInvoicePrintable, 01946 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp, 01947 }; 01948 01949 static void 01950 reg_lot (void) 01951 { 01952 static QofParam params[] = 01953 { 01954 { 01955 INVOICE_FROM_LOT, _GNC_MOD_NAME, 01956 (QofAccessFunc)gncInvoiceGetInvoiceFromLot, NULL 01957 }, 01958 { NULL }, 01959 }; 01960 01961 qof_class_register (GNC_ID_LOT, NULL, params); 01962 } 01963 01964 static void 01965 reg_txn (void) 01966 { 01967 static QofParam params[] = 01968 { 01969 { 01970 INVOICE_FROM_TXN, _GNC_MOD_NAME, 01971 (QofAccessFunc)gncInvoiceGetInvoiceFromTxn, NULL 01972 }, 01973 { NULL }, 01974 }; 01975 01976 qof_class_register (GNC_ID_TRANS, NULL, params); 01977 } 01978 01979 gboolean gncInvoiceRegister (void) 01980 { 01981 static QofParam params[] = 01982 { 01983 { INVOICE_ID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetID, (QofSetterFunc)gncInvoiceSetID }, 01984 { INVOICE_OWNER, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetOwner, NULL }, 01985 { INVOICE_OPENED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateOpened, (QofSetterFunc)gncInvoiceSetDateOpened }, 01986 { INVOICE_DUE, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDateDue, NULL }, 01987 { INVOICE_POSTED, QOF_TYPE_DATE, (QofAccessFunc)gncInvoiceGetDatePosted, (QofSetterFunc)gncInvoiceSetDatePosted }, 01988 { INVOICE_IS_POSTED, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPosted, NULL }, 01989 { INVOICE_IS_PAID, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceIsPaid, NULL }, 01990 { INVOICE_BILLINGID, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetBillingID, (QofSetterFunc)gncInvoiceSetBillingID }, 01991 { INVOICE_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetNotes, (QofSetterFunc)gncInvoiceSetNotes }, 01992 { INVOICE_ACC, GNC_ID_ACCOUNT, (QofAccessFunc)gncInvoiceGetPostedAcc, (QofSetterFunc)gncInvoiceSetPostedAcc }, 01993 { INVOICE_POST_TXN, GNC_ID_TRANS, (QofAccessFunc)gncInvoiceGetPostedTxn, (QofSetterFunc)gncInvoiceSetPostedTxn }, 01994 { INVOICE_POST_LOT, GNC_ID_LOT, (QofAccessFunc)gncInvoiceGetPostedLot, NULL/*(QofSetterFunc)gncInvoiceSetPostedLot*/ }, 01995 { INVOICE_TYPE, QOF_TYPE_INT32, (QofAccessFunc)gncInvoiceGetType, NULL }, 01996 { INVOICE_TYPE_STRING, QOF_TYPE_STRING, (QofAccessFunc)gncInvoiceGetTypeString, NULL }, 01997 { INVOICE_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncInvoiceGetTerms, (QofSetterFunc)gncInvoiceSetTerms }, 01998 { INVOICE_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncInvoiceGetBillTo, NULL }, 01999 { INVOICE_ENTRIES, QOF_TYPE_COLLECT, (QofAccessFunc)qofInvoiceGetEntries, (QofSetterFunc)qofInvoiceSetEntries }, 02000 { INVOICE_JOB, GNC_ID_JOB, (QofAccessFunc)qofInvoiceGetJob, (QofSetterFunc)qofInvoiceSetJob }, 02001 { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetActive, (QofSetterFunc)gncInvoiceSetActive }, 02002 { INVOICE_IS_CN, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncInvoiceGetIsCreditNote, (QofSetterFunc)gncInvoiceSetIsCreditNote }, 02003 { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL }, 02004 { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL }, 02005 { NULL }, 02006 }; 02007 02008 qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncInvoiceCompare, params); 02009 reg_lot (); 02010 reg_txn (); 02011 02012 /* Make the compiler happy... */ 02013 if (0) 02014 { 02015 qofInvoiceSetEntries(NULL, NULL); 02016 qofInvoiceGetEntries(NULL); 02017 qofInvoiceSetOwner(NULL, NULL); 02018 qofInvoiceGetOwner(NULL); 02019 qofInvoiceSetBillTo(NULL, NULL); 02020 qofInvoiceGetBillTo(NULL); 02021 } 02022 if (!qof_choice_create(GNC_ID_INVOICE)) 02023 { 02024 return FALSE; 02025 } 02026 return qof_object_register (&gncInvoiceDesc); 02027 } 02028 02029 gchar *gncInvoiceNextID (QofBook *book, const GncOwner *owner) 02030 { 02031 gchar *nextID; 02032 switch (gncOwnerGetType(gncOwnerGetEndOwner(owner))) 02033 { 02034 case GNC_OWNER_CUSTOMER: 02035 nextID = qof_book_increment_and_format_counter (book, "gncInvoice"); 02036 break; 02037 case GNC_OWNER_VENDOR: 02038 nextID = qof_book_increment_and_format_counter (book, "gncBill"); 02039 break; 02040 case GNC_OWNER_EMPLOYEE: 02041 nextID = qof_book_increment_and_format_counter (book, "gncExpVoucher"); 02042 break; 02043 default: 02044 nextID = qof_book_increment_and_format_counter (book, _GNC_MOD_NAME); 02045 break; 02046 } 02047 return nextID; 02048 }
1.7.4