|
GnuCash 2.4.99
|
00001 /********************************************************************\ 00002 * gncEntry.c -- the Core Business Entry Interface * 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 Derek Atkins 00025 * Author: Derek Atkins <warlord@MIT.EDU> 00026 */ 00027 00028 #include "config.h" 00029 00030 #include <glib.h> 00031 00032 #include "gnc-commodity.h" 00033 00034 #include "gncEntry.h" 00035 #include "gncEntryP.h" 00036 #include "gncInvoice.h" 00037 #include "gncOrder.h" 00038 00039 struct _gncEntry 00040 { 00041 QofInstance inst; 00042 00043 Timespec date; 00044 Timespec date_entered; 00045 char * desc; 00046 char * action; 00047 char * notes; 00048 gnc_numeric quantity; 00049 00050 /* customer invoice data */ 00051 Account * i_account; 00052 gnc_numeric i_price; 00053 gboolean i_taxable; 00054 gboolean i_taxincluded; 00055 GncTaxTable * i_tax_table; 00056 gnc_numeric i_discount; 00057 GncAmountType i_disc_type; 00058 GncDiscountHow i_disc_how; 00059 00060 /* vendor bill data */ 00061 Account * b_account; 00062 gnc_numeric b_price; 00063 gboolean b_taxable; 00064 gboolean b_taxincluded; 00065 GncTaxTable * b_tax_table; 00066 gboolean billable; 00067 GncOwner billto; 00068 00069 /* employee bill data */ 00070 GncEntryPaymentType b_payment; 00071 00072 /* my parent(s) */ 00073 GncOrder * order; 00074 GncInvoice * invoice; 00075 GncInvoice * bill; 00076 00077 /* CACHED VALUES */ 00078 gboolean values_dirty; 00079 00080 /* customer invoice */ 00081 gnc_numeric i_value; 00082 gnc_numeric i_value_rounded; 00083 GList * i_tax_values; 00084 gnc_numeric i_tax_value; 00085 gnc_numeric i_tax_value_rounded; 00086 gnc_numeric i_disc_value; 00087 gnc_numeric i_disc_value_rounded; 00088 Timespec i_taxtable_modtime; 00089 00090 /* vendor bill */ 00091 gnc_numeric b_value; 00092 gnc_numeric b_value_rounded; 00093 GList * b_tax_values; 00094 gnc_numeric b_tax_value; 00095 gnc_numeric b_tax_value_rounded; 00096 Timespec b_taxtable_modtime; 00097 }; 00098 00099 struct _gncEntryClass 00100 { 00101 QofInstanceClass parent_class; 00102 }; 00103 00104 static QofLogModule log_module = GNC_MOD_BUSINESS; 00105 00106 00107 /* You must edit the functions in this block in tandem. 00108 * KEEP THIS FUNCTION IN SYNC with the one below! */ 00109 const char * 00110 gncEntryDiscountHowToString (GncDiscountHow how) 00111 { 00112 switch (how) 00113 { 00114 case (GNC_DISC_PRETAX): 00115 return "PRETAX"; 00116 case (GNC_DISC_SAMETIME): 00117 return "SAMETIME"; 00118 case (GNC_DISC_POSTTAX): 00119 return "POSTTAX"; 00120 default: 00121 g_warning ("asked to translate unknown discount-how %d.\n", how); 00122 break; 00123 } 00124 return NULL; 00125 } 00126 00127 /* You must edit the functions in this block in tandem. 00128 * KEEP THIS FUNCTION IN SYNC with the one above! */ 00129 gboolean gncEntryDiscountStringToHow (const char *str, GncDiscountHow *how) 00130 { 00131 if (g_strcmp0 ("PRETAX", str) == 0) 00132 { 00133 *how = GNC_DISC_PRETAX; 00134 return TRUE; 00135 } 00136 if (g_strcmp0 ("SAMETIME", str) == 0) 00137 { 00138 *how = GNC_DISC_SAMETIME; 00139 return TRUE; 00140 } 00141 if (g_strcmp0 ("POSTTAX", str) == 0) 00142 { 00143 *how = GNC_DISC_POSTTAX; 00144 return TRUE; 00145 } 00146 g_warning ("asked to translate unknown discount-how string %s.\n", 00147 str ? str : "(null)"); 00148 00149 return FALSE; 00150 } 00151 00152 /* You must edit the functions in this block in tandem. 00153 * KEEP THIS FUNCTION IN SYNC with the one below! */ 00154 const char * gncEntryPaymentTypeToString (GncEntryPaymentType type) 00155 { 00156 switch (type) 00157 { 00158 case (GNC_PAYMENT_CASH): 00159 return "CASH"; 00160 case (GNC_PAYMENT_CARD): 00161 return "CARD"; 00162 default: 00163 g_warning ("asked to translate unknown payment type %d.\n", type); 00164 break; 00165 } 00166 return NULL ; 00167 } 00168 00169 /* You must edit the functions in this block in tandem. 00170 * KEEP THIS FUNCTION IN SYNC with the one above! */ 00171 gboolean gncEntryPaymentStringToType (const char *str, GncEntryPaymentType *type) 00172 { 00173 if (g_strcmp0 ("CASH", str) == 0) 00174 { 00175 *type = GNC_PAYMENT_CASH; 00176 return TRUE; 00177 } 00178 if (g_strcmp0 ("CARD", str) == 0) 00179 { 00180 *type = GNC_PAYMENT_CARD; 00181 return TRUE; 00182 } 00183 g_warning ("asked to translate unknown discount-how string %s.\n", 00184 str ? str : "(null)"); 00185 00186 return FALSE; 00187 } 00188 00189 #define _GNC_MOD_NAME GNC_ID_ENTRY 00190 00191 #define SET_STR(obj, member, str) { \ 00192 char * tmp; \ 00193 \ 00194 if (!safe_strcmp (member, str)) return; \ 00195 gncEntryBeginEdit (obj); \ 00196 tmp = CACHE_INSERT (str); \ 00197 CACHE_REMOVE (member); \ 00198 member = tmp; \ 00199 } 00200 00201 G_INLINE_FUNC void mark_entry (GncEntry *entry); 00202 void mark_entry (GncEntry *entry) 00203 { 00204 qof_instance_set_dirty(&entry->inst); 00205 qof_event_gen (&entry->inst, QOF_EVENT_MODIFY, NULL); 00206 } 00207 00208 /* ================================================================ */ 00209 00210 enum 00211 { 00212 PROP_0, 00213 PROP_DESCRIPTION 00214 }; 00215 00216 /* GObject Initialization */ 00217 G_DEFINE_TYPE(GncEntry, gnc_entry, QOF_TYPE_INSTANCE); 00218 00219 static void 00220 gnc_entry_init(GncEntry* entry) 00221 { 00222 } 00223 00224 static void 00225 gnc_entry_dispose(GObject *entryp) 00226 { 00227 G_OBJECT_CLASS(gnc_entry_parent_class)->dispose(entryp); 00228 } 00229 00230 static void 00231 gnc_entry_finalize(GObject* entryp) 00232 { 00233 G_OBJECT_CLASS(gnc_entry_parent_class)->finalize(entryp); 00234 } 00235 00236 static void 00237 gnc_entry_get_property (GObject *object, 00238 guint prop_id, 00239 GValue *value, 00240 GParamSpec *pspec) 00241 { 00242 GncEntry *entry; 00243 00244 g_return_if_fail(GNC_IS_ENTRY(object)); 00245 00246 entry = GNC_ENTRY(object); 00247 switch (prop_id) 00248 { 00249 case PROP_DESCRIPTION: 00250 g_value_set_string(value, entry->desc); 00251 break; 00252 default: 00253 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00254 break; 00255 } 00256 } 00257 00258 static void 00259 gnc_entry_set_property (GObject *object, 00260 guint prop_id, 00261 const GValue *value, 00262 GParamSpec *pspec) 00263 { 00264 GncEntry *entry; 00265 00266 g_return_if_fail(GNC_IS_ENTRY(object)); 00267 00268 entry = GNC_ENTRY(object); 00269 switch (prop_id) 00270 { 00271 case PROP_DESCRIPTION: 00272 gncEntrySetDescription(entry, g_value_get_string(value)); 00273 break; 00274 default: 00275 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); 00276 break; 00277 } 00278 } 00279 00281 static gchar* 00282 impl_get_display_name(const QofInstance* inst) 00283 { 00284 GncEntry* entry; 00285 gchar* display_name; 00286 gchar* s; 00287 00288 g_return_val_if_fail(inst != NULL, FALSE); 00289 g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE); 00290 00291 entry = GNC_ENTRY(inst); 00292 if (entry->order != NULL) 00293 { 00294 display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->order)); 00295 s = g_strdup_printf("Entry in %s", display_name); 00296 g_free(display_name); 00297 return s; 00298 } 00299 if (entry->invoice != NULL) 00300 { 00301 display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->invoice)); 00302 s = g_strdup_printf("Entry in %s", display_name); 00303 g_free(display_name); 00304 return s; 00305 } 00306 if (entry->bill != NULL) 00307 { 00308 display_name = qof_instance_get_display_name(QOF_INSTANCE(entry->bill)); 00309 s = g_strdup_printf("Entry in %s", display_name); 00310 g_free(display_name); 00311 return s; 00312 } 00313 00314 return g_strdup_printf("Entry %p", inst); 00315 } 00316 00318 static gboolean 00319 impl_refers_to_object(const QofInstance* inst, const QofInstance* ref) 00320 { 00321 GncEntry* entry; 00322 00323 g_return_val_if_fail(inst != NULL, FALSE); 00324 g_return_val_if_fail(GNC_IS_ENTRY(inst), FALSE); 00325 00326 entry = GNC_ENTRY(inst); 00327 00328 if (GNC_IS_ACCOUNT(ref)) 00329 { 00330 Account* acc = GNC_ACCOUNT(ref); 00331 return (entry->i_account == acc || entry->b_account == acc); 00332 } 00333 else if (GNC_IS_TAXTABLE(ref)) 00334 { 00335 GncTaxTable* tt = GNC_TAXTABLE(ref); 00336 return (entry->i_tax_table == tt || entry->b_tax_table == tt); 00337 } 00338 00339 return FALSE; 00340 } 00341 00348 static GList* 00349 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref) 00350 { 00351 if (!GNC_IS_ACCOUNT(ref) && !GNC_IS_TAXTABLE(ref)) 00352 { 00353 return NULL; 00354 } 00355 00356 return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref); 00357 } 00358 00359 static void 00360 gnc_entry_class_init (GncEntryClass *klass) 00361 { 00362 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 00363 QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass); 00364 00365 gobject_class->dispose = gnc_entry_dispose; 00366 gobject_class->finalize = gnc_entry_finalize; 00367 gobject_class->set_property = gnc_entry_set_property; 00368 gobject_class->get_property = gnc_entry_get_property; 00369 00370 qof_class->get_display_name = impl_get_display_name; 00371 qof_class->refers_to_object = impl_refers_to_object; 00372 qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list; 00373 00374 g_object_class_install_property 00375 (gobject_class, 00376 PROP_DESCRIPTION, 00377 g_param_spec_string ("description", 00378 "Entry Description", 00379 "The description is an arbitrary string " 00380 "assigned by the user. It provides identification " 00381 "for this entry.", 00382 NULL, 00383 G_PARAM_READWRITE)); 00384 } 00385 00386 /* Create/Destroy Functions */ 00387 GncEntry *gncEntryCreate (QofBook *book) 00388 { 00389 GncEntry *entry; 00390 gnc_numeric zero = gnc_numeric_zero (); 00391 00392 if (!book) return NULL; 00393 00394 entry = g_object_new (GNC_TYPE_ENTRY, NULL); 00395 qof_instance_init_data (&entry->inst, _GNC_MOD_NAME, book); 00396 00397 entry->desc = CACHE_INSERT (""); 00398 entry->action = CACHE_INSERT (""); 00399 entry->notes = CACHE_INSERT (""); 00400 entry->quantity = zero; 00401 00402 entry->i_price = zero; 00403 entry->i_taxable = TRUE; 00404 entry->i_discount = zero; 00405 entry->i_disc_type = GNC_AMT_TYPE_PERCENT; 00406 entry->i_disc_how = GNC_DISC_PRETAX; 00407 00408 entry->b_price = zero; 00409 entry->b_taxable = TRUE; 00410 entry->billto.type = GNC_OWNER_CUSTOMER; 00411 entry->b_payment = GNC_PAYMENT_CASH; 00412 00413 entry->values_dirty = TRUE; 00414 00415 qof_event_gen (&entry->inst, QOF_EVENT_CREATE, NULL); 00416 00417 return entry; 00418 } 00419 00420 void gncEntryDestroy (GncEntry *entry) 00421 { 00422 if (!entry) return; 00423 qof_instance_set_destroying(entry, TRUE); 00424 gncEntryCommitEdit(entry); 00425 } 00426 00427 static void gncEntryFree (GncEntry *entry) 00428 { 00429 if (!entry) return; 00430 00431 qof_event_gen (&entry->inst, QOF_EVENT_DESTROY, NULL); 00432 00433 CACHE_REMOVE (entry->desc); 00434 CACHE_REMOVE (entry->action); 00435 CACHE_REMOVE (entry->notes); 00436 if (entry->i_tax_values) 00437 gncAccountValueDestroy (entry->i_tax_values); 00438 if (entry->b_tax_values) 00439 gncAccountValueDestroy (entry->b_tax_values); 00440 if (entry->i_tax_table) 00441 gncTaxTableDecRef (entry->i_tax_table); 00442 if (entry->b_tax_table) 00443 gncTaxTableDecRef (entry->b_tax_table); 00444 00445 /* qof_instance_release (&entry->inst); */ 00446 g_object_unref (entry); 00447 } 00448 00449 /* ================================================================ */ 00450 /* Set Functions */ 00451 00452 void gncEntrySetDate (GncEntry *entry, Timespec date) 00453 { 00454 gboolean first_date = FALSE; 00455 Timespec zero_time = { 0, 0 }; 00456 00457 if (!entry) return; 00458 if (timespec_equal (&entry->date, &date)) return; 00459 if (timespec_equal (&entry->date, &zero_time)) 00460 first_date = TRUE; 00461 gncEntryBeginEdit (entry); 00462 entry->date = date; 00463 mark_entry (entry); 00464 gncEntryCommitEdit (entry); 00465 00466 /* Don't re-sort the first time we set the date on this entry */ 00467 if (!first_date) 00468 { 00469 if (entry->invoice) 00470 gncInvoiceSortEntries(entry->invoice); 00471 if (entry->bill) 00472 gncInvoiceSortEntries(entry->bill); 00473 } 00474 } 00475 00476 void gncEntrySetDateGDate (GncEntry *entry, const GDate* date) 00477 { 00478 if (!entry || !date || !g_date_valid(date)) 00479 return; 00480 00481 /* Watch out: Here we are deviating from the initial convention that a 00482 GDate always converts to the start time of the day. Instead, the GDate is 00483 converted to "noon" on the respective date. This is not nice, but this 00484 convention was used for the Timespec of GncEntry all the time, so we better 00485 stick to it.*/ 00486 gncEntrySetDate(entry, timespecCanonicalDayTime(gdate_to_timespec(*date))); 00487 } 00488 00489 void gncEntrySetDateEntered (GncEntry *entry, Timespec date) 00490 { 00491 if (!entry) return; 00492 if (timespec_equal (&entry->date_entered, &date)) return; 00493 gncEntryBeginEdit (entry); 00494 entry->date_entered = date; 00495 mark_entry (entry); 00496 gncEntryCommitEdit (entry); 00497 } 00498 00499 void gncEntrySetDescription (GncEntry *entry, const char *desc) 00500 { 00501 if (!entry || !desc) return; 00502 SET_STR (entry, entry->desc, desc); 00503 mark_entry (entry); 00504 gncEntryCommitEdit (entry); 00505 } 00506 00507 void gncEntrySetAction (GncEntry *entry, const char *action) 00508 { 00509 if (!entry || !action) return; 00510 SET_STR (entry, entry->action, action); 00511 mark_entry (entry); 00512 gncEntryCommitEdit (entry); 00513 } 00514 00515 void gncEntrySetNotes (GncEntry *entry, const char *notes) 00516 { 00517 if (!entry || !notes) return; 00518 SET_STR (entry, entry->notes, notes); 00519 mark_entry (entry); 00520 gncEntryCommitEdit (entry); 00521 } 00522 00523 void gncEntrySetQuantity (GncEntry *entry, gnc_numeric quantity) 00524 { 00525 if (!entry) return; 00526 if (gnc_numeric_eq (entry->quantity, quantity)) return; 00527 gncEntryBeginEdit (entry); 00528 entry->quantity = quantity; 00529 entry->values_dirty = TRUE; 00530 mark_entry (entry); 00531 gncEntryCommitEdit (entry); 00532 } 00533 00534 void gncEntrySetDocQuantity (GncEntry *entry, gnc_numeric quantity, gboolean is_cn) 00535 { 00536 if (!entry) return; 00537 if (gnc_numeric_eq (entry->quantity, quantity)) return; 00538 gncEntryBeginEdit (entry); 00539 entry->quantity = (is_cn ? gnc_numeric_neg (quantity) : quantity); 00540 entry->values_dirty = TRUE; 00541 mark_entry (entry); 00542 gncEntryCommitEdit (entry); 00543 } 00544 00545 /* Customer Invoices */ 00546 00547 void gncEntrySetInvAccount (GncEntry *entry, Account *acc) 00548 { 00549 if (!entry) return; 00550 if (entry->i_account == acc) return; 00551 gncEntryBeginEdit (entry); 00552 entry->i_account = acc; 00553 mark_entry (entry); 00554 gncEntryCommitEdit (entry); 00555 } 00556 00557 void gncEntrySetInvPrice (GncEntry *entry, gnc_numeric price) 00558 { 00559 if (!entry) return; 00560 if (gnc_numeric_eq (entry->i_price, price)) return; 00561 gncEntryBeginEdit (entry); 00562 entry->i_price = price; 00563 entry->values_dirty = TRUE; 00564 mark_entry (entry); 00565 gncEntryCommitEdit (entry); 00566 } 00567 00568 void gncEntrySetInvTaxable (GncEntry *entry, gboolean taxable) 00569 { 00570 if (!entry) return; 00571 if (entry->i_taxable == taxable) return; 00572 gncEntryBeginEdit (entry); 00573 entry->i_taxable = taxable; 00574 entry->values_dirty = TRUE; 00575 mark_entry (entry); 00576 gncEntryCommitEdit (entry); 00577 } 00578 00579 void gncEntrySetInvTaxIncluded (GncEntry *entry, gboolean taxincluded) 00580 { 00581 if (!entry) return; 00582 if (entry->i_taxincluded == taxincluded) return; 00583 gncEntryBeginEdit (entry); 00584 entry->i_taxincluded = taxincluded; 00585 entry->values_dirty = TRUE; 00586 mark_entry (entry); 00587 gncEntryCommitEdit (entry); 00588 } 00589 00590 void gncEntrySetInvTaxTable (GncEntry *entry, GncTaxTable *table) 00591 { 00592 if (!entry) return; 00593 if (entry->i_tax_table == table) return; 00594 gncEntryBeginEdit (entry); 00595 if (entry->i_tax_table) 00596 gncTaxTableDecRef (entry->i_tax_table); 00597 if (table) 00598 gncTaxTableIncRef (table); 00599 entry->i_tax_table = table; 00600 entry->values_dirty = TRUE; 00601 mark_entry (entry); 00602 gncEntryCommitEdit (entry); 00603 } 00604 00605 void gncEntrySetInvDiscount (GncEntry *entry, gnc_numeric discount) 00606 { 00607 if (!entry) return; 00608 if (gnc_numeric_eq (entry->i_discount, discount)) return; 00609 gncEntryBeginEdit (entry); 00610 entry->i_discount = discount; 00611 entry->values_dirty = TRUE; 00612 mark_entry (entry); 00613 gncEntryCommitEdit (entry); 00614 } 00615 00616 void gncEntrySetInvDiscountType (GncEntry *entry, GncAmountType type) 00617 { 00618 if (!entry) return; 00619 if (entry->i_disc_type == type) return; 00620 00621 gncEntryBeginEdit (entry); 00622 entry->i_disc_type = type; 00623 entry->values_dirty = TRUE; 00624 mark_entry (entry); 00625 gncEntryCommitEdit (entry); 00626 } 00627 00628 void gncEntrySetInvDiscountHow (GncEntry *entry, GncDiscountHow how) 00629 { 00630 if (!entry) return; 00631 if (entry->i_disc_how == how) return; 00632 00633 gncEntryBeginEdit (entry); 00634 entry->i_disc_how = how; 00635 entry->values_dirty = TRUE; 00636 mark_entry (entry); 00637 gncEntryCommitEdit (entry); 00638 } 00639 00640 void qofEntrySetInvDiscType (GncEntry *entry, const char *type_string) 00641 { 00642 GncAmountType type; 00643 00644 if (!entry) return; 00645 gncAmountStringToType(type_string, &type); 00646 if (entry->i_disc_type == type) return; 00647 gncEntryBeginEdit (entry); 00648 entry->i_disc_type = type; 00649 entry->values_dirty = TRUE; 00650 mark_entry (entry); 00651 gncEntryCommitEdit (entry); 00652 00653 } 00654 00655 void qofEntrySetInvDiscHow (GncEntry *entry, const char *type) 00656 { 00657 GncDiscountHow how = GNC_DISC_PRETAX; 00658 00659 if (!entry) return; 00660 gncEntryBeginEdit (entry); 00661 gncEntryDiscountStringToHow(type, &how); 00662 if (entry->i_disc_how == how) return; 00663 entry->i_disc_how = how; 00664 entry->values_dirty = TRUE; 00665 mark_entry (entry); 00666 gncEntryCommitEdit (entry); 00667 } 00668 00669 /* Vendor Bills */ 00670 00671 void gncEntrySetBillAccount (GncEntry *entry, Account *acc) 00672 { 00673 if (!entry) return; 00674 if (entry->b_account == acc) return; 00675 gncEntryBeginEdit (entry); 00676 entry->b_account = acc; 00677 mark_entry (entry); 00678 gncEntryCommitEdit (entry); 00679 } 00680 00681 void gncEntrySetBillPrice (GncEntry *entry, gnc_numeric price) 00682 { 00683 if (!entry) return; 00684 if (gnc_numeric_eq (entry->b_price, price)) return; 00685 gncEntryBeginEdit (entry); 00686 entry->b_price = price; 00687 entry->values_dirty = TRUE; 00688 mark_entry (entry); 00689 gncEntryCommitEdit (entry); 00690 } 00691 00692 void gncEntrySetBillTaxable (GncEntry *entry, gboolean taxable) 00693 { 00694 if (!entry) return; 00695 if (entry->b_taxable == taxable) return; 00696 gncEntryBeginEdit (entry); 00697 entry->b_taxable = taxable; 00698 entry->values_dirty = TRUE; 00699 mark_entry (entry); 00700 gncEntryCommitEdit (entry); 00701 } 00702 00703 void gncEntrySetBillTaxIncluded (GncEntry *entry, gboolean taxincluded) 00704 { 00705 if (!entry) return; 00706 if (entry->b_taxincluded == taxincluded) return; 00707 gncEntryBeginEdit (entry); 00708 entry->b_taxincluded = taxincluded; 00709 entry->values_dirty = TRUE; 00710 mark_entry (entry); 00711 gncEntryCommitEdit (entry); 00712 } 00713 00714 void gncEntrySetBillTaxTable (GncEntry *entry, GncTaxTable *table) 00715 { 00716 if (!entry) return; 00717 if (entry->b_tax_table == table) return; 00718 gncEntryBeginEdit (entry); 00719 if (entry->b_tax_table) 00720 gncTaxTableDecRef (entry->b_tax_table); 00721 if (table) 00722 gncTaxTableIncRef (table); 00723 entry->b_tax_table = table; 00724 entry->values_dirty = TRUE; 00725 mark_entry (entry); 00726 gncEntryCommitEdit (entry); 00727 } 00728 00729 void gncEntrySetBillable (GncEntry *entry, gboolean billable) 00730 { 00731 if (!entry) return; 00732 if (entry->billable == billable) return; 00733 00734 gncEntryBeginEdit (entry); 00735 entry->billable = billable; 00736 mark_entry (entry); 00737 gncEntryCommitEdit (entry); 00738 } 00739 00740 void gncEntrySetBillTo (GncEntry *entry, GncOwner *billto) 00741 { 00742 if (!entry || !billto) return; 00743 if (gncOwnerEqual (&entry->billto, billto)) return; 00744 00745 gncEntryBeginEdit (entry); 00746 gncOwnerCopy (billto, &entry->billto); 00747 mark_entry (entry); 00748 gncEntryCommitEdit (entry); 00749 } 00750 00751 void gncEntrySetBillPayment (GncEntry *entry, GncEntryPaymentType type) 00752 { 00753 if (!entry) return; 00754 if (entry->b_payment == type) return; 00755 gncEntryBeginEdit (entry); 00756 entry->b_payment = type; 00757 mark_entry (entry); 00758 gncEntryCommitEdit (entry); 00759 } 00760 00761 /* Called from gncOrder when we're added to the Order */ 00762 void gncEntrySetOrder (GncEntry *entry, GncOrder *order) 00763 { 00764 if (!entry) return; 00765 if (entry->order == order) return; 00766 gncEntryBeginEdit (entry); 00767 entry->order = order; 00768 mark_entry (entry); 00769 gncEntryCommitEdit (entry); 00770 00771 /* Generate an event modifying the Order's end-owner */ 00772 #if 0 00773 qof_event_gen (gncOwnerGetEndGUID (gncOrderGetOwner (order)), 00774 QOF_EVENT_MODIFY, NULL); 00775 #endif 00776 } 00777 00778 /* called from gncInvoice when we're added to the Invoice */ 00779 void gncEntrySetInvoice (GncEntry *entry, GncInvoice *invoice) 00780 { 00781 if (!entry) return; 00782 if (entry->invoice == invoice) return; 00783 gncEntryBeginEdit (entry); 00784 entry->invoice = invoice; 00785 mark_entry (entry); 00786 gncEntryCommitEdit (entry); 00787 } 00788 00789 /* called from gncInvoice when we're added to the Invoice/Bill */ 00790 void gncEntrySetBill (GncEntry *entry, GncInvoice *bill) 00791 { 00792 if (!entry) return; 00793 if (entry->bill == bill) return; 00794 gncEntryBeginEdit (entry); 00795 entry->bill = bill; 00796 mark_entry (entry); 00797 gncEntryCommitEdit (entry); 00798 } 00799 00800 void gncEntryCopy (const GncEntry *src, GncEntry *dest) 00801 { 00802 if (!src || !dest) return; 00803 00804 gncEntryBeginEdit (dest); 00805 dest->date = src->date; 00806 dest->date_entered = src->date_entered; /* ??? */ 00807 gncEntrySetDescription (dest, src->desc); 00808 gncEntrySetAction (dest, src->action); 00809 gncEntrySetNotes (dest, src->notes); 00810 dest->quantity = src->quantity; 00811 00812 dest->i_account = src->i_account; 00813 dest->i_price = src->i_price; 00814 dest->i_taxable = src->i_taxable; 00815 dest->i_taxincluded = src->i_taxincluded; 00816 dest->i_discount = src->i_discount; 00817 dest->i_disc_type = src->i_disc_type; 00818 dest->i_disc_how = src->i_disc_how; 00819 00820 /* vendor bill data */ 00821 dest->b_account = src->b_account; 00822 dest->b_price = src->b_price; 00823 dest->b_taxable = src->b_taxable; 00824 dest->b_taxincluded = src->b_taxincluded; 00825 dest->billable = src->billable; 00826 dest->billto = src->billto; 00827 00828 if (src->i_tax_table) 00829 gncEntrySetInvTaxTable (dest, src->i_tax_table); 00830 00831 if (src->b_tax_table) 00832 gncEntrySetBillTaxTable (dest, src->b_tax_table); 00833 00834 if (src->order) 00835 gncOrderAddEntry (src->order, dest); 00836 00837 if (src->invoice) 00838 gncInvoiceAddEntry (src->invoice, dest); 00839 00840 if (src->bill) 00841 gncBillAddEntry (src->bill, dest); 00842 00843 dest->values_dirty = TRUE; 00844 gncEntryCommitEdit (dest); 00845 } 00846 00847 /* ================================================================ */ 00848 /* Get Functions */ 00849 00850 Timespec gncEntryGetDate (const GncEntry *entry) 00851 { 00852 Timespec ts; 00853 ts.tv_sec = 0; 00854 ts.tv_nsec = 0; 00855 if (!entry) return ts; 00856 return entry->date; 00857 } 00858 00859 GDate gncEntryGetDateGDate(const GncEntry *entry) 00860 { 00861 return timespec_to_gdate(gncEntryGetDate(entry)); 00862 } 00863 00864 Timespec gncEntryGetDateEntered (const GncEntry *entry) 00865 { 00866 Timespec ts; 00867 ts.tv_sec = 0; 00868 ts.tv_nsec = 0; 00869 if (!entry) return ts; 00870 return entry->date_entered; 00871 } 00872 00873 const char * gncEntryGetDescription (const GncEntry *entry) 00874 { 00875 if (!entry) return NULL; 00876 return entry->desc; 00877 } 00878 00879 const char * gncEntryGetAction (const GncEntry *entry) 00880 { 00881 if (!entry) return NULL; 00882 return entry->action; 00883 } 00884 00885 const char * gncEntryGetNotes (const GncEntry *entry) 00886 { 00887 if (!entry) return NULL; 00888 return entry->notes; 00889 } 00890 00891 gnc_numeric gncEntryGetQuantity (const GncEntry *entry) 00892 { 00893 if (!entry) return gnc_numeric_zero(); 00894 return entry->quantity; 00895 } 00896 00897 gnc_numeric gncEntryGetDocQuantity (const GncEntry *entry, gboolean is_cn) 00898 { 00899 gnc_numeric value = gncEntryGetQuantity (entry); 00900 return (is_cn ? gnc_numeric_neg (value) : value); 00901 } 00902 00903 /* Customer Invoice */ 00904 00905 Account * gncEntryGetInvAccount (const GncEntry *entry) 00906 { 00907 if (!entry) return NULL; 00908 return entry->i_account; 00909 } 00910 00911 gnc_numeric gncEntryGetInvPrice (const GncEntry *entry) 00912 { 00913 if (!entry) return gnc_numeric_zero(); 00914 return entry->i_price; 00915 } 00916 00917 gnc_numeric gncEntryGetInvDiscount (const GncEntry *entry) 00918 { 00919 if (!entry) return gnc_numeric_zero(); 00920 return entry->i_discount; 00921 } 00922 00923 GncAmountType gncEntryGetInvDiscountType (const GncEntry *entry) 00924 { 00925 if (!entry) return 0; 00926 return entry->i_disc_type; 00927 } 00928 00929 GncDiscountHow gncEntryGetInvDiscountHow (const GncEntry *entry) 00930 { 00931 if (!entry) return 0; 00932 return entry->i_disc_how; 00933 } 00934 00935 char* qofEntryGetInvDiscType (const GncEntry *entry) 00936 { 00937 char *type_string; 00938 00939 if (!entry) return 0; 00940 type_string = g_strdup(gncAmountTypeToString(entry->i_disc_type)); 00941 return type_string; 00942 } 00943 00944 char* qofEntryGetInvDiscHow (const GncEntry *entry) 00945 { 00946 char *type_string; 00947 00948 if (!entry) return 0; 00949 type_string = g_strdup(gncEntryDiscountHowToString(entry->i_disc_how)); 00950 return type_string; 00951 } 00952 00953 gboolean gncEntryGetInvTaxable (const GncEntry *entry) 00954 { 00955 if (!entry) return FALSE; 00956 return entry->i_taxable; 00957 } 00958 00959 gboolean gncEntryGetInvTaxIncluded (const GncEntry *entry) 00960 { 00961 if (!entry) return FALSE; 00962 return entry->i_taxincluded; 00963 } 00964 00965 GncTaxTable * gncEntryGetInvTaxTable (const GncEntry *entry) 00966 { 00967 if (!entry) return NULL; 00968 return entry->i_tax_table; 00969 } 00970 00971 /* vendor bills */ 00972 00973 Account * gncEntryGetBillAccount (const GncEntry *entry) 00974 { 00975 if (!entry) return NULL; 00976 return entry->b_account; 00977 } 00978 00979 gnc_numeric gncEntryGetBillPrice (const GncEntry *entry) 00980 { 00981 if (!entry) return gnc_numeric_zero(); 00982 return entry->b_price; 00983 } 00984 00985 gboolean gncEntryGetBillTaxable (const GncEntry *entry) 00986 { 00987 if (!entry) return FALSE; 00988 return entry->b_taxable; 00989 } 00990 00991 gboolean gncEntryGetBillTaxIncluded (const GncEntry *entry) 00992 { 00993 if (!entry) return FALSE; 00994 return entry->b_taxincluded; 00995 } 00996 00997 GncTaxTable * gncEntryGetBillTaxTable (const GncEntry *entry) 00998 { 00999 if (!entry) return NULL; 01000 return entry->b_tax_table; 01001 } 01002 01003 gboolean gncEntryGetBillable (const GncEntry *entry) 01004 { 01005 if (!entry) return FALSE; 01006 return entry->billable; 01007 } 01008 01009 GncOwner * gncEntryGetBillTo (GncEntry *entry) 01010 { 01011 if (!entry) return NULL; 01012 return &entry->billto; 01013 } 01014 01015 GncEntryPaymentType gncEntryGetBillPayment (const GncEntry* entry) 01016 { 01017 if (!entry) return 0; 01018 return entry->b_payment; 01019 } 01020 01021 GncInvoice * gncEntryGetInvoice (const GncEntry *entry) 01022 { 01023 if (!entry) return NULL; 01024 return entry->invoice; 01025 } 01026 01027 GncInvoice * gncEntryGetBill (const GncEntry *entry) 01028 { 01029 if (!entry) return NULL; 01030 return entry->bill; 01031 } 01032 01033 GncOrder * gncEntryGetOrder (const GncEntry *entry) 01034 { 01035 if (!entry) return NULL; 01036 return entry->order; 01037 } 01038 01039 /* ================================================================ */ 01040 /* 01041 * This is the logic of computing the total for an Entry, so you know 01042 * what values to put into various Splits or to display in the ledger. 01043 * In other words, we combine the quantity, unit-price, discount and 01044 * taxes together, depending on various flags. 01045 * 01046 * There are four potential ways to combine these numbers: 01047 * Discount: Pre-Tax Post-Tax 01048 * Tax : Included Not-Included 01049 * 01050 * The process is relatively simple: 01051 * 01052 * 1) compute the aggregate price (price*qty) 01053 * 2) if taxincluded, then back-compute the aggregate pre-tax price 01054 * 3) apply discount and taxes in the appropriate order 01055 * 4) return the requested results. 01056 * 01057 * Step 2 can be done with aggregate taxes; no need to compute them all 01058 * unless the caller asked for the tax_value. 01059 * 01060 * Note that the returned "value" is such that 01061 * value + tax == "total to pay" 01062 * which means in the case of tax-included that the returned 01063 * "value" may be less than the aggregate price, even without a 01064 * discount. If you want to display the tax-included value, you need 01065 * to add the value and taxes together. In other words, the value is 01066 * the amount the merchant gets; the taxes are the amount the gov't 01067 * gets, and the customer pays the sum or value + taxes. 01068 * 01069 * The SCU is the denominator to convert the value. 01070 * 01071 * The discount return value is just for entertainment -- you may want 01072 * to let a consumer know how much they saved. 01073 */ 01074 void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, 01075 const GncTaxTable *tax_table, gboolean tax_included, 01076 gnc_numeric discount, GncAmountType discount_type, 01077 GncDiscountHow discount_how, int SCU, 01078 gnc_numeric *value, gnc_numeric *discount_value, 01079 GList **tax_value) 01080 { 01081 gnc_numeric aggregate; 01082 gnc_numeric pretax; 01083 gnc_numeric result; 01084 gnc_numeric tax; 01085 gnc_numeric percent = gnc_numeric_create (100, 1); 01086 gnc_numeric tpercent = gnc_numeric_zero (); 01087 gnc_numeric tvalue = gnc_numeric_zero (); 01088 01089 GList * entries = gncTaxTableGetEntries (tax_table); 01090 GList * node; 01091 01092 /* Step 1: compute the aggregate price */ 01093 01094 aggregate = gnc_numeric_mul (qty, price, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01095 01096 /* Step 2: compute the pre-tax aggregate */ 01097 01098 /* First, compute the aggregate tpercent and tvalue numbers */ 01099 for (node = entries; node; node = node->next) 01100 { 01101 GncTaxTableEntry *entry = node->data; 01102 gnc_numeric amount = gncTaxTableEntryGetAmount (entry); 01103 01104 switch (gncTaxTableEntryGetType (entry)) 01105 { 01106 case GNC_AMT_TYPE_VALUE: 01107 tvalue = gnc_numeric_add (tvalue, amount, GNC_DENOM_AUTO, 01108 GNC_HOW_DENOM_LCD); 01109 break; 01110 case GNC_AMT_TYPE_PERCENT: 01111 tpercent = gnc_numeric_add (tpercent, amount, GNC_DENOM_AUTO, 01112 GNC_HOW_DENOM_LCD); 01113 break; 01114 default: 01115 g_warning ("Unknown tax type: %d", gncTaxTableEntryGetType (entry)); 01116 break; 01117 } 01118 } 01119 /* now we need to convert from 5% -> .05 */ 01120 tpercent = gnc_numeric_div (tpercent, percent, GNC_DENOM_AUTO, 01121 GNC_HOW_DENOM_LCD); 01122 01123 /* Next, actually compute the pre-tax aggregate value based on the 01124 * taxincluded flag. 01125 */ 01126 if (tax_table && tax_included) 01127 { 01128 /* Back-compute the pre-tax aggregate value. 01129 * We know that aggregate = pretax + pretax*tpercent + tvalue, so 01130 * pretax = (aggregate-tvalue)/(1+tpercent) 01131 */ 01132 pretax = gnc_numeric_sub (aggregate, tvalue, GNC_DENOM_AUTO, 01133 GNC_HOW_DENOM_LCD); 01134 pretax = gnc_numeric_div (pretax, 01135 gnc_numeric_add (tpercent, 01136 gnc_numeric_create (1, 1), 01137 GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD), 01138 GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01139 } 01140 else 01141 { 01142 pretax = aggregate; 01143 } 01144 01145 /* Step 3: apply discount and taxes in the appropriate order */ 01146 01147 /* 01148 * There are two ways to apply discounts and taxes. In one way, you 01149 * always compute the discount off the pretax number, and compute 01150 * the taxes off of either the pretax value or "pretax-discount" 01151 * value. In the other way, you always compute the tax on "pretax", 01152 * and compute the discount on either "pretax" or "pretax+taxes". 01153 * 01154 * I don't know which is the "correct" way. 01155 */ 01156 01157 /* 01158 * Type: discount tax 01159 * PRETAX pretax pretax-discount 01160 * SAMETIME pretax pretax 01161 * POSTTAX pretax+tax pretax 01162 */ 01163 01164 switch (discount_how) 01165 { 01166 case GNC_DISC_PRETAX: 01167 case GNC_DISC_SAMETIME: 01168 /* compute the discount from pretax */ 01169 01170 if (discount_type == GNC_AMT_TYPE_PERCENT) 01171 { 01172 discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, 01173 GNC_HOW_DENOM_LCD); 01174 discount = gnc_numeric_mul (pretax, discount, GNC_DENOM_AUTO, 01175 GNC_HOW_DENOM_LCD); 01176 } 01177 01178 result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01179 01180 /* Figure out when to apply the tax, pretax or pretax-discount */ 01181 if (discount_how == GNC_DISC_PRETAX) 01182 pretax = result; 01183 break; 01184 01185 case GNC_DISC_POSTTAX: 01186 /* compute discount on pretax+taxes */ 01187 01188 if (discount_type == GNC_AMT_TYPE_PERCENT) 01189 { 01190 gnc_numeric after_tax; 01191 01192 tax = gnc_numeric_mul (pretax, tpercent, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01193 after_tax = gnc_numeric_add (pretax, tax, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01194 after_tax = gnc_numeric_add (after_tax, tvalue, GNC_DENOM_AUTO, 01195 GNC_HOW_DENOM_LCD); 01196 discount = gnc_numeric_div (discount, percent, GNC_DENOM_AUTO, 01197 GNC_HOW_DENOM_LCD); 01198 discount = gnc_numeric_mul (after_tax, discount, GNC_DENOM_AUTO, 01199 GNC_HOW_DENOM_LCD); 01200 } 01201 01202 result = gnc_numeric_sub (pretax, discount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01203 break; 01204 01205 default: 01206 g_warning ("unknown DiscountHow value: %d", discount_how); 01207 break; 01208 } 01209 01210 /* Step 4: return the requested results. */ 01211 01212 /* result == amount merchant gets 01213 * discount == amount of discount 01214 * need to compute taxes (based on 'pretax') if the caller wants it. 01215 */ 01216 01217 if (discount_value != NULL) 01218 { 01219 if (SCU) discount = gnc_numeric_convert(discount, SCU, GNC_HOW_RND_ROUND_HALF_UP); 01220 *discount_value = discount; 01221 } 01222 01223 if (value != NULL) 01224 { 01225 if (SCU) result = gnc_numeric_convert(result, SCU, GNC_HOW_RND_ROUND_HALF_UP); 01226 *value = result; 01227 } 01228 01229 /* Now... Compute the list of tax values (if the caller wants it) */ 01230 01231 if (tax_value != NULL) 01232 { 01233 GList * taxes = NULL; 01234 01235 for (node = entries; node; node = node->next) 01236 { 01237 GncTaxTableEntry *entry = node->data; 01238 Account *acc = gncTaxTableEntryGetAccount (entry); 01239 gnc_numeric amount = gncTaxTableEntryGetAmount (entry); 01240 01241 g_return_if_fail (acc); 01242 01243 switch (gncTaxTableEntryGetType (entry)) 01244 { 01245 case GNC_AMT_TYPE_VALUE: 01246 if (SCU) amount = gnc_numeric_convert(amount, SCU, GNC_HOW_RND_ROUND_HALF_UP); 01247 taxes = gncAccountValueAdd (taxes, acc, amount); 01248 break; 01249 case GNC_AMT_TYPE_PERCENT: 01250 amount = gnc_numeric_div (amount, percent, GNC_DENOM_AUTO, 01251 GNC_HOW_DENOM_LCD); 01252 tax = gnc_numeric_mul (pretax, amount, GNC_DENOM_AUTO, GNC_HOW_DENOM_LCD); 01253 if (SCU) tax = gnc_numeric_convert(tax, SCU, GNC_HOW_RND_ROUND_HALF_UP); 01254 taxes = gncAccountValueAdd (taxes, acc, tax); 01255 break; 01256 default: 01257 break; 01258 } 01259 } 01260 *tax_value = taxes; 01261 } 01262 01263 return; 01264 } 01265 01266 static int 01267 get_entry_commodity_denom (const GncEntry *entry) 01268 { 01269 gnc_commodity *c; 01270 if (!entry) 01271 return 0; 01272 if (entry->invoice) 01273 { 01274 c = gncInvoiceGetCurrency (entry->invoice); 01275 if (c) 01276 return (gnc_commodity_get_fraction (c)); 01277 } 01278 if (entry->bill) 01279 { 01280 c = gncInvoiceGetCurrency (entry->bill); 01281 if (c) 01282 return (gnc_commodity_get_fraction (c)); 01283 } 01284 return 100000; 01285 } 01286 01287 static void 01288 gncEntryRecomputeValues (GncEntry *entry) 01289 { 01290 int denom; 01291 01292 /* See if either tax table changed since we last computed values */ 01293 if (entry->i_tax_table) 01294 { 01295 Timespec modtime = gncTaxTableLastModified (entry->i_tax_table); 01296 if (timespec_cmp (&entry->i_taxtable_modtime, &modtime)) 01297 { 01298 entry->values_dirty = TRUE; 01299 entry->i_taxtable_modtime = modtime; 01300 } 01301 } 01302 if (entry->b_tax_table) 01303 { 01304 Timespec modtime = gncTaxTableLastModified (entry->b_tax_table); 01305 if (timespec_cmp (&entry->b_taxtable_modtime, &modtime)) 01306 { 01307 entry->values_dirty = TRUE; 01308 entry->b_taxtable_modtime = modtime; 01309 } 01310 } 01311 01312 if (!entry->values_dirty) 01313 return; 01314 01315 /* Clear the last-computed tax values */ 01316 if (entry->i_tax_values) 01317 { 01318 gncAccountValueDestroy (entry->i_tax_values); 01319 entry->i_tax_values = NULL; 01320 } 01321 if (entry->b_tax_values) 01322 { 01323 gncAccountValueDestroy (entry->b_tax_values); 01324 entry->b_tax_values = NULL; 01325 } 01326 01327 /* Determine the commodity denominator */ 01328 denom = get_entry_commodity_denom (entry); 01329 01330 /* Compute the invoice values */ 01331 gncEntryComputeValue (entry->quantity, entry->i_price, 01332 (entry->i_taxable ? entry->i_tax_table : NULL), 01333 entry->i_taxincluded, 01334 entry->i_discount, entry->i_disc_type, 01335 entry->i_disc_how, 01336 denom, 01337 &(entry->i_value), &(entry->i_disc_value), 01338 &(entry->i_tax_values)); 01339 01340 /* Compute the bill values */ 01341 gncEntryComputeValue (entry->quantity, entry->b_price, 01342 (entry->b_taxable ? entry->b_tax_table : NULL), 01343 entry->b_taxincluded, 01344 gnc_numeric_zero(), GNC_AMT_TYPE_VALUE, GNC_DISC_PRETAX, 01345 denom, 01346 &(entry->b_value), NULL, &(entry->b_tax_values)); 01347 01348 entry->i_value_rounded = gnc_numeric_convert (entry->i_value, denom, 01349 GNC_HOW_RND_ROUND_HALF_UP); 01350 entry->i_disc_value_rounded = gnc_numeric_convert (entry->i_disc_value, denom, 01351 GNC_HOW_RND_ROUND_HALF_UP); 01352 entry->i_tax_value = gncAccountValueTotal (entry->i_tax_values); 01353 entry->i_tax_value_rounded = gnc_numeric_convert (entry->i_tax_value, denom, 01354 GNC_HOW_RND_ROUND_HALF_UP); 01355 01356 entry->b_value_rounded = gnc_numeric_convert (entry->b_value, denom, 01357 GNC_HOW_RND_ROUND_HALF_UP); 01358 entry->b_tax_value = gncAccountValueTotal (entry->b_tax_values); 01359 entry->b_tax_value_rounded = gnc_numeric_convert (entry->b_tax_value, denom, 01360 GNC_HOW_RND_ROUND_HALF_UP); 01361 entry->values_dirty = FALSE; 01362 } 01363 01364 /* The "Int" functions below are for internal use only. 01365 * Outside this file, use the "Doc" or "Bal" variants found below instead. */ 01366 static gnc_numeric gncEntryGetIntValue (GncEntry *entry, gboolean round, gboolean is_cust_doc) 01367 { 01368 if (!entry) return gnc_numeric_zero(); 01369 gncEntryRecomputeValues (entry); 01370 if (round) 01371 return (is_cust_doc ? entry->i_value_rounded : entry->b_value_rounded); 01372 else 01373 return (is_cust_doc ? entry->i_value : entry->b_value); 01374 } 01375 01376 static gnc_numeric gncEntryGetIntTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc) 01377 { 01378 if (!entry) return gnc_numeric_zero(); 01379 gncEntryRecomputeValues (entry); 01380 if (round) 01381 return (is_cust_doc ? entry->i_tax_value_rounded : entry->b_tax_value_rounded); 01382 else 01383 return (is_cust_doc ? entry->i_tax_value : entry->b_tax_value); 01384 } 01385 01386 /* Careful: the returned list is managed by the entry, and will only be valid for a short time */ 01387 static AccountValueList * gncEntryGetIntTaxValues (GncEntry *entry, gboolean is_cust_doc) 01388 { 01389 if (!entry) return NULL; 01390 gncEntryRecomputeValues (entry); 01391 return (is_cust_doc ? entry->i_tax_values : entry->b_tax_values); 01392 } 01393 01394 static gnc_numeric gncEntryGetIntDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc) 01395 { 01396 if (!entry) return gnc_numeric_zero(); 01397 gncEntryRecomputeValues (entry); 01398 if (round) 01399 return (is_cust_doc ? entry->i_disc_value_rounded : gnc_numeric_zero()); 01400 else 01401 return (is_cust_doc ? entry->i_disc_value : gnc_numeric_zero()); 01402 } 01403 01404 gnc_numeric gncEntryGetDocValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn) 01405 { 01406 gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc); 01407 return (is_cn ? gnc_numeric_neg (value) : value); 01408 } 01409 01410 gnc_numeric gncEntryGetDocTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn) 01411 { 01412 gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc); 01413 return (is_cn ? gnc_numeric_neg (value) : value); 01414 } 01415 01416 /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */ 01417 AccountValueList * gncEntryGetDocTaxValues (GncEntry *entry, gboolean is_cust_doc, gboolean is_cn) 01418 { 01419 AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc); 01420 AccountValueList *values = NULL, *node; 01421 01422 /* Make a copy of the list with negated values if necessary. */ 01423 for (node = int_values; node; node = node->next) 01424 { 01425 GncAccountValue *acct_val = node->data; 01426 values = gncAccountValueAdd (values, acct_val->account, 01427 (is_cn ? gnc_numeric_neg (acct_val->value) 01428 : acct_val->value)); 01429 } 01430 01431 return values; 01432 } 01433 01434 gnc_numeric gncEntryGetDocDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc, gboolean is_cn) 01435 { 01436 gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc); 01437 return (is_cn ? gnc_numeric_neg (value) : value); 01438 } 01439 01440 gnc_numeric gncEntryGetBalValue (GncEntry *entry, gboolean round, gboolean is_cust_doc) 01441 { 01442 gnc_numeric value = gncEntryGetIntValue (entry, round, is_cust_doc); 01443 return (is_cust_doc ? gnc_numeric_neg (value) : value); 01444 } 01445 01446 gnc_numeric gncEntryGetBalTaxValue (GncEntry *entry, gboolean round, gboolean is_cust_doc) 01447 { 01448 gnc_numeric value = gncEntryGetIntTaxValue (entry, round, is_cust_doc); 01449 return (is_cust_doc ? gnc_numeric_neg (value) : value); 01450 } 01451 01452 /* Careful: the returned list is NOT owned by the entry and should be freed by the caller */ 01453 AccountValueList * gncEntryGetBalTaxValues (GncEntry *entry, gboolean is_cust_doc) 01454 { 01455 AccountValueList *int_values = gncEntryGetIntTaxValues (entry, is_cust_doc); 01456 AccountValueList *values = NULL, *node; 01457 01458 /* Make a copy of the list with negated values if necessary. */ 01459 for (node = int_values; node; node = node->next) 01460 { 01461 GncAccountValue *acct_val = node->data; 01462 values = gncAccountValueAdd (values, acct_val->account, 01463 (is_cust_doc ? gnc_numeric_neg (acct_val->value) 01464 : acct_val->value)); 01465 } 01466 01467 return values; 01468 } 01469 01470 gnc_numeric gncEntryGetBalDiscountValue (GncEntry *entry, gboolean round, gboolean is_cust_doc) 01471 { 01472 gnc_numeric value = gncEntryGetIntDiscountValue (entry, round, is_cust_doc); 01473 return (is_cust_doc ? gnc_numeric_neg (value) : value); 01474 } 01475 01476 /* XXX this existence of this routine is just wrong */ 01477 gboolean gncEntryIsOpen (const GncEntry *entry) 01478 { 01479 if (!entry) return FALSE; 01480 return (qof_instance_get_editlevel(entry) > 0); 01481 } 01482 01483 /* ================================================================ */ 01484 01485 void gncEntryBeginEdit (GncEntry *entry) 01486 { 01487 qof_begin_edit(&entry->inst); 01488 } 01489 01490 static void gncEntryOnError (QofInstance *entry, QofBackendError errcode) 01491 { 01492 PERR("Entry QofBackend Failure: %d", errcode); 01493 gnc_engine_signal_commit_error( errcode ); 01494 } 01495 01496 static void gncEntryOnDone (QofInstance *inst) {} 01497 01498 static void entry_free (QofInstance *inst) 01499 { 01500 GncEntry *entry = (GncEntry *)inst; 01501 gncEntryFree (entry); 01502 } 01503 01504 void gncEntryCommitEdit (GncEntry *entry) 01505 { 01506 if (!qof_commit_edit (QOF_INSTANCE(entry))) return; 01507 qof_commit_edit_part2 (&entry->inst, gncEntryOnError, 01508 gncEntryOnDone, entry_free); 01509 } 01510 01511 int gncEntryCompare (const GncEntry *a, const GncEntry *b) 01512 { 01513 int compare; 01514 01515 if (a == b) return 0; 01516 if (!a && b) return -1; 01517 if (a && !b) return 1; 01518 01519 compare = timespec_cmp (&(a->date), &(b->date)); 01520 if (compare) return compare; 01521 01522 compare = timespec_cmp (&(a->date_entered), &(b->date_entered)); 01523 if (compare) return compare; 01524 01525 compare = safe_strcmp (a->desc, b->desc); 01526 if (compare) return compare; 01527 01528 compare = safe_strcmp (a->action, b->action); 01529 if (compare) return compare; 01530 01531 return qof_instance_guid_compare(a, b); 01532 } 01533 01534 #define CHECK_STRING(X, Y, FIELD) \ 01535 if (safe_strcmp((X)->FIELD, (Y)->FIELD) != 0) \ 01536 { \ 01537 PWARN("%s differ: %s vs %s", #FIELD, (X)->FIELD, (Y)->FIELD); \ 01538 return FALSE; \ 01539 } 01540 01541 #define CHECK_ACCOUNT(X, Y, FIELD) \ 01542 if (!xaccAccountEqual((X)->FIELD, (Y)->FIELD, TRUE)) \ 01543 { \ 01544 PWARN("%s differ", #FIELD); \ 01545 return FALSE; \ 01546 } 01547 01548 #define CHECK_NUMERIC(X, Y, FIELD) \ 01549 if (!gnc_numeric_equal((X)->FIELD, (Y)->FIELD)) \ 01550 { \ 01551 PWARN("%s differ", #FIELD); \ 01552 return FALSE; \ 01553 } 01554 01555 #define CHECK_VALUE(X, Y, FIELD) \ 01556 if ((X)->FIELD != (Y)->FIELD) \ 01557 { \ 01558 PWARN("%s differ", #FIELD); \ 01559 return FALSE; \ 01560 } 01561 01562 01563 /* ============================================================= */ 01564 /* Object declaration */ 01565 01566 static void 01567 destroy_entry_on_book_close(QofInstance *ent, gpointer data) 01568 { 01569 GncEntry* entry = GNC_ENTRY(ent); 01570 01571 gncEntryBeginEdit(entry); 01572 gncEntryDestroy(entry); 01573 } 01574 01579 static void 01580 gnc_entry_book_end(QofBook* book) 01581 { 01582 QofCollection *col; 01583 01584 col = qof_book_get_collection(book, GNC_ID_ENTRY); 01585 qof_collection_foreach(col, destroy_entry_on_book_close, NULL); 01586 } 01587 01588 static QofObject gncEntryDesc = 01589 { 01590 DI(.interface_version = ) QOF_OBJECT_VERSION, 01591 DI(.e_type = ) _GNC_MOD_NAME, 01592 DI(.type_label = ) "Order/Invoice/Bill Entry", 01593 DI(.create = ) (gpointer)gncEntryCreate, 01594 DI(.book_begin = ) NULL, 01595 DI(.book_end = ) gnc_entry_book_end, 01596 DI(.is_dirty = ) qof_collection_is_dirty, 01597 DI(.mark_clean = ) qof_collection_mark_clean, 01598 DI(.foreach = ) qof_collection_foreach, 01599 DI(.printable = ) NULL, 01600 DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp, 01601 }; 01602 01603 gboolean gncEntryRegister (void) 01604 { 01605 static QofParam params[] = 01606 { 01607 { ENTRY_DATE, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDate, (QofSetterFunc)gncEntrySetDate }, 01608 { ENTRY_DATE_ENTERED, QOF_TYPE_DATE, (QofAccessFunc)gncEntryGetDateEntered, (QofSetterFunc)gncEntrySetDateEntered }, 01609 { ENTRY_DESC, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetDescription, (QofSetterFunc)gncEntrySetDescription }, 01610 { ENTRY_ACTION, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetAction, (QofSetterFunc)gncEntrySetAction }, 01611 { ENTRY_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncEntryGetNotes, (QofSetterFunc)gncEntrySetNotes }, 01612 { ENTRY_QTY, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetQuantity, (QofSetterFunc)gncEntrySetQuantity }, 01613 { ENTRY_IPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetInvPrice, (QofSetterFunc)gncEntrySetInvPrice }, 01614 { ENTRY_BPRICE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncEntryGetBillPrice, (QofSetterFunc)gncEntrySetBillPrice }, 01615 { ENTRY_INVOICE, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetInvoice, NULL }, 01616 { ENTRY_IACCT, GNC_ID_ACCOUNT, (QofAccessFunc)gncEntryGetInvAccount, (QofSetterFunc)gncEntrySetInvAccount }, 01617 { ENTRY_BACCT, GNC_ID_ACCOUNT, (QofAccessFunc)gncEntryGetBillAccount, (QofSetterFunc)gncEntrySetBillAccount }, 01618 { ENTRY_BILL, GNC_ID_INVOICE, (QofAccessFunc)gncEntryGetBill, NULL }, 01619 { 01620 ENTRY_INV_DISC_TYPE, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscType, 01621 (QofSetterFunc)qofEntrySetInvDiscType 01622 }, 01623 { 01624 ENTRY_INV_DISC_HOW, QOF_TYPE_STRING, (QofAccessFunc)qofEntryGetInvDiscHow, 01625 (QofSetterFunc)qofEntrySetInvDiscHow 01626 }, 01627 { 01628 ENTRY_INV_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxable, 01629 (QofSetterFunc)gncEntrySetInvTaxable 01630 }, 01631 { 01632 ENTRY_INV_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetInvTaxIncluded, 01633 (QofSetterFunc)gncEntrySetInvTaxIncluded 01634 }, 01635 { 01636 ENTRY_BILL_TAXABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxable, 01637 (QofSetterFunc)gncEntrySetBillTaxable 01638 }, 01639 { 01640 ENTRY_BILL_TAX_INC, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillTaxIncluded, 01641 (QofSetterFunc)gncEntrySetBillTaxIncluded 01642 }, 01643 { ENTRY_BILLABLE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncEntryGetBillable, (QofSetterFunc)gncEntrySetBillable }, 01644 { ENTRY_BILLTO, GNC_ID_OWNER, (QofAccessFunc)gncEntryGetBillTo, (QofSetterFunc)gncEntrySetBillTo }, 01645 { ENTRY_ORDER, GNC_ID_ORDER, (QofAccessFunc)gncEntryGetOrder, NULL }, 01646 { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL }, 01647 { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL }, 01648 { NULL }, 01649 }; 01650 01651 qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncEntryCompare, params); 01652 01653 return qof_object_register (&gncEntryDesc); 01654 }
1.7.4