1# gnucash_core.py -- High level python wrapper classes for the core parts 2# of GnuCash 3# 4# Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@parit.ca> 5# This program is free software; you can redistribute it and/or 6# modify it under the terms of the GNU General Public License as 7# published by the Free Software Foundation; either version 2 of 8# the License, or (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program; if not, contact: 17# Free Software Foundation Voice: +1-617-542-5942 18# 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 19# Boston, MA 02110-1301, USA gnu@gnu.org 20# 21# @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca> 22# @author Jeff Green, ParIT Worker Co-operative <jeff@parit.ca> 23 24# The following is for doxygen 25## @file 26# @brief High level python wrapper classes for the core parts of GnuCash 27# @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca> 28# @author Jeff Green, ParIT Worker Co-operative <jeff@parit.ca> 29# @ingroup python_bindings 30 31from enum import IntEnum 32from urllib.parse import urlparse 33 34from gnucash import gnucash_core_c 35from gnucash import _sw_core_utils 36 37from gnucash.function_class import \ 38 ClassFromFunctions, extract_attributes_with_prefix, \ 39 default_arguments_decorator, method_function_returns_instance, \ 40 methods_return_instance, process_list_convert_to_instance, \ 41 method_function_returns_instance_list, methods_return_instance_lists 42 43from gnucash.gnucash_core_c import gncInvoiceLookup, gncInvoiceGetInvoiceFromTxn, \ 44 gncInvoiceGetInvoiceFromLot, gncEntryLookup, gncInvoiceLookup, \ 45 gncCustomerLookup, gncVendorLookup, gncJobLookup, gncEmployeeLookup, \ 46 gncTaxTableLookup, gncTaxTableLookupByName, gnc_search_invoice_on_id, \ 47 gnc_search_customer_on_id, gnc_search_bill_on_id , \ 48 gnc_search_vendor_on_id, gncInvoiceNextID, gncCustomerNextID, \ 49 gncVendorNextID, gncTaxTableGetTables, gnc_numeric_zero, \ 50 gnc_numeric_create, double_to_gnc_numeric, string_to_gnc_numeric, \ 51 gnc_numeric_to_string 52 53from gnucash.deprecation import ( 54 deprecated_args_session, 55 deprecated_args_session_init, 56 deprecated_args_session_begin, 57 deprecated 58) 59 60try: 61 import gettext 62 63 _localedir = _sw_core_utils.gnc_path_get_localedir() 64 gettext.install(_sw_core_utils.GETTEXT_PACKAGE, _localedir) 65except: 66 print() 67 print("Problem importing gettext!") 68 import traceback 69 import sys 70 exc_type, exc_value, exc_traceback = sys.exc_info() 71 traceback.print_exception(exc_type, exc_value, exc_traceback) 72 print() 73 74 def _(s): 75 """Null translator function, gettext not available""" 76 return s 77 78 import builtins 79 builtins.__dict__['_'] = _ 80 81class GnuCashCoreClass(ClassFromFunctions): 82 _module = gnucash_core_c 83 84 def do_lookup_create_oo_instance(self, lookup_function, cls, *args): 85 thing = lookup_function(self.get_instance(), *args) 86 if thing != None: 87 thing = cls(instance=thing) 88 return thing 89 90 91class GnuCashBackendException(Exception): 92 def __init__(self, msg, errors): 93 Exception.__init__(self, msg) 94 self.errors = errors 95 96 97class SessionOpenMode(IntEnum): 98 """Mode for opening sessions. 99 100 This replaces three booleans that were passed in order: ignore_lock, create, 101 and force. It's structured so that one can use it as a bit field with the 102 values in the same order, i.e. ignore_lock = 1 << 2, create_new = 1 << 1, and 103 force_new = 1. 104 105 enumeration members 106 ------------------- 107 108 SESSION_NORMAL_OPEN = 0 (All False) 109 Open will fail if the URI doesn't exist or is locked. 110 111 SESSION_NEW_STORE = 2 (False, True, False (create)) 112 Create a new store at the URI. It will fail if the store already exists and is found to contain data that would be overwritten. 113 114 SESSION_NEW_OVERWRITE = 3 (False, True, True (create | force)) 115 Create a new store at the URI even if a store already exists there. 116 117 SESSION_READ_ONLY = 4, (True, False, False (ignore_lock)) 118 Open the session read-only, ignoring any existing lock and not creating one if the URI isn't locked. 119 120 SESSION_BREAK_LOCK = 5 (True, False, True (ignore_lock | force)) 121 Open the session, taking over any existing lock. 122 123 source: lignucash/engine/qofsession.h 124 """ 125 126 SESSION_NORMAL_OPEN = gnucash_core_c.SESSION_NORMAL_OPEN 127 """All False 128 Open will fail if the URI doesn't exist or is locked.""" 129 130 SESSION_NEW_STORE = gnucash_core_c.SESSION_NEW_STORE 131 """False, True, False (create) 132 Create a new store at the URI. It will fail if the store already exists and is found to contain data that would be overwritten.""" 133 134 SESSION_NEW_OVERWRITE = gnucash_core_c.SESSION_NEW_OVERWRITE 135 """False, True, True (create | force) 136 Create a new store at the URI even if a store already exists there.""" 137 138 SESSION_READ_ONLY = gnucash_core_c.SESSION_READ_ONLY 139 """True, False, False (ignore_lock) 140 Open the session read-only, ignoring any existing lock and not creating one if the URI isn't locked.""" 141 142 SESSION_BREAK_LOCK = gnucash_core_c.SESSION_BREAK_LOCK 143 """True, False, True (ignore_lock | force) 144 Open the session, taking over any existing lock.""" 145 146 147class Session(GnuCashCoreClass): 148 """A GnuCash book editing session 149 150 To commit changes to the session you may need to call save, 151 (this is always the case with the file backend). 152 153 When you're down with a session you may need to call end() 154 155 Every Session has a Book in the book attribute, which you'll definitely 156 be interested in, as every GnuCash entity (Transaction, Split, Vendor, 157 Invoice..) is associated with a particular book where it is stored. 158 """ 159 160 @deprecated_args_session_init 161 def __init__(self, book_uri=None, mode=None, instance=None, book=None): 162 """! 163 A convenient constructor that allows you to specify a book URI, 164 begin the session, and load the book. 165 166 This can give you the power of calling 167 qof_session_new, qof_session_begin, and qof_session_load all in one! 168 169 qof_session_load is only called if url scheme is "xml" and 170 mode is SESSION_NEW_STORE or SESSION_NEW_OVERWRITE 171 172 @param book_uri must be a string in the form of a URI/URL. The access 173 method specified depends on the loaded backends. Paths may be relative 174 or absolute. If the path is relative, that is if the argument is 175 "file://somefile.xml", then the current working directory is 176 assumed. Customized backends can choose to search other 177 application-specific directories or URI schemes as well. 178 It be None to skip the calls to qof_session_begin and 179 qof_session_load. 180 181 @param instance argument can be passed if new Session is used as a 182 wrapper for an existing session instance 183 184 @param mode The SessionOpenMode. 185 @note SessionOpenMode replaces deprecated ignore_lock, is_new and force_new. 186 187 @par SessionOpenMode 188 `SESSION_NORMAL_OPEN`: Find an existing file or database at the provided uri and 189 open it if it is unlocked. If it is locked post a QOF_BACKEND_LOCKED error. 190 @par 191 `SESSION_NEW_STORE`: Check for an existing file or database at the provided 192 uri and if none is found, create it. If the file or database exists post a 193 QOF_BACKED_STORE_EXISTS and return. 194 @par 195 `SESSION_NEW_OVERWRITE`: Create a new file or database at the provided uri, 196 deleting any existing file or database. 197 @par 198 `SESSION_READ_ONLY`: Find an existing file or database and open it without 199 disturbing the lock if it exists or setting one if not. This will also set a 200 flag on the book that will prevent many elements from being edited and will 201 prevent the backend from saving any edits. 202 @par 203 `SESSION_BREAK_LOCK`: Find an existing file or database, lock it, and open 204 it. If there is already a lock replace it with a new one for this session. 205 206 @par Errors 207 qof_session_begin() signals failure by queuing errors. After it completes use 208 qof_session_get_error() and test that the value is `ERROR_BACKEND_NONE` to 209 determine that the session began successfully. 210 211 @exception as begin() and load() are wrapped with raise_backend_errors_after_call() 212 this function can raise a GnuCashBackendException. If it does, 213 you don't need to cleanup and call end() and destroy(), that is handled 214 for you, and the exception is raised. 215 """ 216 if instance is not None: 217 GnuCashCoreClass.__init__(self, instance=instance) 218 else: 219 if book is None: 220 book = Book() 221 GnuCashCoreClass.__init__(self, book) 222 223 if book_uri is not None: 224 try: 225 if mode is None: 226 mode = SessionOpenMode.SESSION_NORMAL_OPEN 227 self.begin(book_uri, mode) 228 # Take care of backend inconsistency 229 # New xml file can't be loaded, new sql store 230 # has to be loaded before it can be altered 231 # Any existing store obviously has to be loaded 232 # More background: https://bugs.gnucash.org/show_bug.cgi?id=726891 233 is_new = mode in (SessionOpenMode.SESSION_NEW_STORE, SessionOpenMode.SESSION_NEW_OVERWRITE) 234 scheme = urlparse(book_uri).scheme 235 if not (is_new and scheme == 'xml'): 236 self.load() 237 except GnuCashBackendException as backend_exception: 238 self.end() 239 self.destroy() 240 raise 241 242 def __enter__(self): 243 return self 244 245 def __exit__(self, exc_type, exc_value, traceback): 246 # Roll back changes on exception by not calling save. Only works for XMl backend. 247 if not exc_type: 248 self.save() 249 self.end() 250 251 def raise_backend_errors(self, called_function="qof_session function"): 252 """Raises a GnuCashBackendException if there are outstanding 253 QOF_BACKEND errors. 254 255 set called_function to name the function that was last called 256 """ 257 errors = self.pop_all_errors() 258 if errors != (): 259 raise GnuCashBackendException( 260 "call to %s resulted in the " 261 "following errors, %s" % (called_function, backend_error_dict[errors[0]]), 262 errors ) 263 264 def generate_errors(self): 265 """A generator that yields any outstanding QofBackend errors 266 """ 267 while self.get_error() is not ERR_BACKEND_NO_ERR: 268 error = self.pop_error() 269 yield error 270 271 def pop_all_errors(self): 272 """Returns any accumulated qof backend errors as a tuple 273 """ 274 return tuple( self.generate_errors() ) 275 276 # STATIC METHODS 277 @staticmethod 278 def raise_backend_errors_after_call(function, *args, **kwargs): 279 """A function decorator that results in a call to 280 raise_backend_errors after execution. 281 """ 282 def new_function(self, *args, **kwargs): 283 return_value = function(self, *args, **kwargs) 284 self.raise_backend_errors(function.__name__) 285 return return_value 286 return new_function 287 288class Book(GnuCashCoreClass): 289 """A Book encapsulates all of the GnuCash data, it is the place where 290 all GnuCash entities (Transaction, Split, Vendor, Invoice...), are 291 stored. You'll notice that all of the constructors for those entities 292 need a book to be associated with. 293 294 The most common way to get a book is through the book property in the 295 Session class, that is, create a session that connects to some storage, 296 such as through 'my_session = Session('file:my_books.xac')', and access 297 the book via the book property, 'my_session.book' 298 299 If you would like to create a Book without any backing storage, call the 300 Book constructor without any parameters, 'Book()'. You can later merge 301 such a book into a book with actual store by using merge_init. 302 303 Methods of interest 304 get_root_account -- Returns the root level Account 305 get_table -- Returns a commodity lookup table, of type GncCommodityTable 306 """ 307 def InvoiceLookup(self, guid): 308 from gnucash.gnucash_business import Invoice 309 return self.do_lookup_create_oo_instance( 310 gncInvoiceLookup, Invoice, guid.get_instance() ) 311 312 def EntryLookup(self, guid): 313 from gnucash.gnucash_business import Entry 314 return self.do_lookup_create_oo_instance( 315 gncEntryLookup, Entry, guid.get_instance() ) 316 317 def CustomerLookup(self, guid): 318 from gnucash.gnucash_business import Customer 319 return self.do_lookup_create_oo_instance( 320 gncCustomerLookup, Customer, guid.get_instance()) 321 322 def JobLookup(self, guid): 323 from gnucash.gnucash_business import Job 324 return self.do_lookup_create_oo_instance( 325 gncJobLookup, Job, guid.get_instance() ) 326 327 def VendorLookup(self, guid): 328 from gnucash.gnucash_business import Vendor 329 return self.do_lookup_create_oo_instance( 330 gncVendorLookup, Vendor, guid.get_instance() ) 331 332 def EmployeeLookup(self, guid): 333 from gnucash.gnucash_business import Employee 334 return self.do_lookup_create_oo_instance( 335 gncEmployeeLookup, Employee, guid.get_instance() ) 336 337 def TaxTableLookup(self, guid): 338 from gnucash.gnucash_business import TaxTable 339 return self.do_lookup_create_oo_instance( 340 gncTaxTableLookup, TaxTable, guid.get_instance() ) 341 342 def TaxTableLookupByName(self, name): 343 from gnucash.gnucash_business import TaxTable 344 return self.do_lookup_create_oo_instance( 345 gncTaxTableLookupByName, TaxTable, name) 346 347 def TaxTableGetTables(self): 348 from gnucash.gnucash_business import TaxTable 349 return [ TaxTable(instance=item) for item in gncTaxTableGetTables(self.instance) ] 350 351 def BillLookupByID(self, id): 352 from gnucash.gnucash_business import Bill 353 return self.do_lookup_create_oo_instance( 354 gnc_search_bill_on_id, Bill, id) 355 356 def InvoiceLookupByID(self, id): 357 from gnucash.gnucash_business import Invoice 358 return self.do_lookup_create_oo_instance( 359 gnc_search_invoice_on_id, Invoice, id) 360 361 def CustomerLookupByID(self, id): 362 from gnucash.gnucash_business import Customer 363 return self.do_lookup_create_oo_instance( 364 gnc_search_customer_on_id, Customer, id) 365 366 def VendorLookupByID(self, id): 367 from gnucash.gnucash_business import Vendor 368 return self.do_lookup_create_oo_instance( 369 gnc_search_vendor_on_id, Vendor, id) 370 371 def InvoiceNextID(self, customer): 372 ''' Return the next invoice ID. 373 ''' 374 from gnucash.gnucash_core_c import gncInvoiceNextID 375 return gncInvoiceNextID(self.get_instance(),customer.GetEndOwner().get_instance()[1]) 376 377 def BillNextID(self, vendor): 378 ''' Return the next Bill ID. ''' 379 from gnucash.gnucash_core_c import gncInvoiceNextID 380 return gncInvoiceNextID(self.get_instance(),vendor.GetEndOwner().get_instance()[1]) 381 382 def CustomerNextID(self): 383 ''' Return the next Customer ID. ''' 384 from gnucash.gnucash_core_c import gncCustomerNextID 385 return gncCustomerNextID(self.get_instance()) 386 387 def VendorNextID(self): 388 ''' Return the next Vendor ID. ''' 389 from gnucash.gnucash_core_c import gncVendorNextID 390 return gncVendorNextID(self.get_instance()) 391 392class GncNumeric(GnuCashCoreClass): 393 """Object used by GnuCash to store all numbers. Always consists of a 394 numerator and denominator. 395 396 The constants GNC_DENOM_AUTO, 397 GNC_HOW_RND_FLOOR, GNC_HOW_RND_CEIL, GNC_HOW_RND_TRUNC, 398 GNC_HOW_RND_PROMOTE, GNC_HOW_RND_ROUND_HALF_DOWN, 399 GNC_HOW_RND_ROUND_HALF_UP, GNC_HOW_RND_ROUND, GNC_HOW_RND_NEVER, 400 GNC_HOW_DENOM_EXACT, GNC_HOW_DENOM_REDUCE, GNC_HOW_DENOM_LCD, 401 and GNC_HOW_DENOM_FIXED are available for arithmetic 402 functions like GncNumeric.add 403 404 Look at gnc-numeric.h to see how to use these 405 """ 406 407 def __init__(self, *args, **kargs): 408 """Constructor that supports the following formats: 409 * No arguments defaulting to zero: eg. GncNumeric() == 0/1 410 * A integer: e.g. GncNumeric(1) == 1/1 411 * Numerator and denominator intager pair: eg. GncNumeric(1, 2) == 1/2 412 * A floating point number: e.g. GncNumeric(0.5) == 1/2 413 * A floating point number with defined conversion: e.g. 414 GncNumeric(0.5, GNC_DENOM_AUTO, 415 GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER) == 1/2 416 * A string: e.g. GncNumeric("1/2") == 1/2 417 """ 418 if 'instance' not in kargs: 419 kargs['instance'] = GncNumeric.__args_to_instance(args) 420 GnuCashCoreClass.__init__(self, [], **kargs) 421 422 @staticmethod 423 def __args_to_instance(args): 424 if len(args) == 0: 425 return gnc_numeric_zero() 426 elif len(args) == 1: 427 arg = args[0] 428 if isinstance(arg, int): 429 return gnc_numeric_create(arg ,1) 430 elif isinstance(arg, float): 431 return double_to_gnc_numeric(arg, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER) 432 elif isinstance(arg, str): 433 instance = gnc_numeric_zero() 434 if not string_to_gnc_numeric(arg, instance): 435 raise TypeError('Failed to convert to GncNumeric: ' + str(args)) 436 return instance 437 else: 438 raise TypeError('Only single int/float/str allowed: ' + str(args)) 439 elif len(args) == 2: 440 if isinstance(args[0], int) and isinstance(args[1], int): 441 return gnc_numeric_create(*args) 442 else: 443 raise TypeError('Only two ints allowed: ' + str(args)) 444 elif len(args) == 3: 445 if isinstance(args[0], float) \ 446 and isinstance(args[1], int) \ 447 and type(args[2]) == type(GNC_HOW_DENOM_FIXED): 448 return double_to_gnc_numeric(*args) 449 else: 450 raise TypeError('Only (float, int, GNC_HOW_RND_*) allowed: ' + str(args)) 451 else: 452 raise TypeError('Required single int/float/str or two ints: ' + str(args)) 453 454 def to_fraction(self): 455 from fractions import Fraction 456 return Fraction(self.num(), self.denom()) 457 458 def __str__(self): 459 """Returns a human readable numeric value string as UTF8.""" 460 return gnc_numeric_to_string(self.instance) 461 462class GncPrice(GnuCashCoreClass): 463 ''' 464 Each priceEach price in the database represents an "instantaneous" 465 quote for a given commodity with respect to another commodity. 466 For example, a given price might represent the value of LNUX in USD on 2001-02-03. 467 468 Fields: 469 * commodity: the item being priced. 470 * currency: the denomination of the value of the item being priced. 471 * value: the value of the item being priced. 472 * time: the time the price was valid. 473 * source: a string describing the source of the quote. These strings will be something like this: 474 "Finance::Quote", "user:misc", "user:foo", etc. If the quote came from a user, as a matter of policy, 475 you *must* prefix the string you give with "user:". For now, the only other reserved values are 476 "Finance::Quote" and "old-file-import". Any string used must be added to the source_list array in 477 dialog-price-edit-db.c so that it can be properly translated. (There are unfortunately many strings 478 in users' databases, so this string must be translated on output instead of always being used in untranslated form). 479 * type: the type of quote - types possible right now are bid, ask, last, nav, and 480 unknown.Each price in the database represents an "instantaneous" quote for a given 481 commodity with respect to another commodity. 482 For example, a given price might represent the value of LNUX in USD on 2001-02-03. 483 484 See also https://code.gnucash.org/docs/head/group__Price.html 485 ''' 486 _new_instance = 'gnc_price_create' 487GncPrice.add_methods_with_prefix('gnc_price_') 488 489 490class GncPriceDB(GnuCashCoreClass): 491 ''' 492 a simple price database for gnucash. 493 The PriceDB is intended to be a database of price quotes, or more specifically, 494 a database of GNCPrices. For the time being, it is still a fairly simple 495 database supporting only fairly simple queries. It is expected that new 496 queries will be added as needed, and that there is some advantage to delaying 497 complex queries for now in the hope that we get a real DB implementation 498 before they're really needed. 499 500 Every QofBook contains a GNCPriceDB, accessible via gnc_pricedb_get_db. 501 502 Definition in file gnc-pricedb.h. 503 See also https://code.gnucash.org/docs/head/gnc-pricedb_8h.html 504 ''' 505 506@deprecated("Use gnc_pricedb_latest_before_t64") 507def gnc_pricedb_lookup_latest_before_t64(self, commodity, currency, date): 508 return self.lookup_nearest_before_t64(commodity, currency, date) 509 510GncPriceDB.add_method('gnc_pricedb_lookup_latest_before_t64', 'lookup_latest_before_t64') 511 512GncPriceDB.lookup_latest_before_t64 = method_function_returns_instance(GncPriceDB.lookup_latest_before_t64, GncPrice) 513 514GncPriceDB.add_methods_with_prefix('gnc_pricedb_') 515PriceDB_dict = { 516 'lookup_latest' : GncPrice, 517 'lookup_nearest_in_time64' : GncPrice, 518 'lookup_nearest_before_t64' : GncPrice, 519 'convert_balance_latest_price' : GncNumeric, 520 'convert_balance_nearest_price_t64' : GncNumeric, 521 } 522methods_return_instance(GncPriceDB,PriceDB_dict) 523GncPriceDB.get_prices = method_function_returns_instance_list( 524 GncPriceDB.get_prices, GncPrice ) 525 526class GncCommodity(GnuCashCoreClass): pass 527 528class GncCommodityTable(GnuCashCoreClass): 529 """A CommodityTable provides a way to store and lookup commodities. 530 Commodities are primarily currencies, but other tradable things such as 531 stocks, mutual funds, and material substances are possible. 532 533 Users of this library should not create their own CommodityTable, instead 534 the get_table method from the Book class should be used. 535 536 This table is automatically populated with the GnuCash default commodity's 537 which includes most of the world's currencies. 538 """ 539 540 def _get_namespaces_py(self): 541 return [ns.get_name() for ns in self.get_namespaces_list()] 542 543class GncCommodityNamespace(GnuCashCoreClass): 544 pass 545 546class GncLot(GnuCashCoreClass): 547 def GetInvoiceFromLot(self): 548 from gnucash.gnucash_business import Invoice 549 return self.do_lookup_create_oo_instance( 550 gncInvoiceGetInvoiceFromLot, Invoice ) 551 552class Transaction(GnuCashCoreClass): 553 """A GnuCash Transaction 554 555 Consists of at least one (generally two) splits to represent a transaction 556 between two accounts. 557 558 559 Has a GetImbalance() method that returns a list of all the imbalanced 560 currencies. Each list item is a two element tuple, the first element is 561 the imbalanced commodity, the second element is the value. 562 563 Warning, the commodity.get_instance() value can be None when there 564 is no currency set for the transaction. 565 """ 566 _new_instance = 'xaccMallocTransaction' 567 def GetNthSplit(self, n): 568 return self.GetSplitList().pop(n) 569 570 def GetInvoiceFromTxn(self): 571 from gnucash.gnucash_business import Transaction 572 return self.do_lookup_create_oo_instance( 573 gncInvoiceGetInvoiceFromTxn, Transaction ) 574 575 def __eq__(self, other): 576 return self.Equal(other, True, False, False, False) 577 578def decorate_monetary_list_returning_function(orig_function): 579 def new_function(self, *args): 580 """decorate function that returns list of gnc_monetary to return tuples of GncCommodity and GncNumeric 581 582 Args: 583 *args: Variable length argument list. Will get passed to orig_function 584 585 Returns: 586 array of tuples: (GncCommodity, GncNumeric) 587 588 ToDo: 589 Maybe this function should better reside in module function_class (?)""" 590 # warning, item.commodity has been shown to be None 591 # when the transaction doesn't have a currency 592 return [(GncCommodity(instance=item.commodity), 593 GncNumeric(instance=item.value)) 594 for item in orig_function(self, *args) ] 595 return new_function 596 597class Split(GnuCashCoreClass): 598 """A GnuCash Split 599 600 The most basic representation of a movement of currency from one account to 601 another. 602 """ 603 _new_instance = 'xaccMallocSplit' 604 605 def __eq__(self, other): 606 return self.Equal(other, True, False, False) 607 608class Account(GnuCashCoreClass): 609 """A GnuCash Account. 610 611 A fundamental entity in accounting, an Account provides representation 612 for a financial object, such as a ACCT_TYPE_BANK account, an 613 ACCT_TYPE_ASSET (like a building), 614 a ACCT_TYPE_LIABILITY (such as a bank loan), a summary of some type of 615 ACCT_TYPE_EXPENSE, or a summary of some source of ACCT_TYPE_INCOME . 616 617 The words in upper case are the constants that GnuCash and this library uses 618 to describe account type. Here is the full list: 619 ACCT_TYPE_ASSET, ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CHECKING, \ 620 ACCT_TYPE_CREDIT, ACCT_TYPE_EQUITY, ACCT_TYPE_EXPENSE, ACCT_TYPE_INCOME, \ 621 ACCT_TYPE_LIABILITY, ACCT_TYPE_MUTUAL, ACCT_TYPE_PAYABLE, \ 622 ACCT_TYPE_RECEIVABLE, ACCT_TYPE_STOCK, ACCT_TYPE_ROOT, ACCT_TYPE_TRADING 623 624 These are not strings, they are attributes you can import from this 625 module 626 """ 627 _new_instance = 'xaccMallocAccount' 628 629class GUID(GnuCashCoreClass): 630 _new_instance = 'guid_new_return' 631 632# Session 633Session.add_constructor_and_methods_with_prefix('qof_session_', 'new') 634 635def one_arg_default_none(function): 636 return default_arguments_decorator(function, None, None) 637Session.decorate_functions(one_arg_default_none, "load", "save") 638 639Session.decorate_functions( Session.raise_backend_errors_after_call, 640 "begin", "load", "save", "end") 641Session.decorate_method(default_arguments_decorator, "begin", None, mode=SessionOpenMode.SESSION_NORMAL_OPEN) 642Session.decorate_functions(deprecated_args_session_begin, "begin") 643 644Session.get_book = method_function_returns_instance( 645 Session.get_book, Book ) 646 647Session.book = property( Session.get_book ) 648 649# import all of the session backend error codes into this module 650this_module_dict = globals() 651for error_name, error_value, error_name_after_prefix in \ 652 extract_attributes_with_prefix(gnucash_core_c, 'ERR_'): 653 this_module_dict[ error_name ] = error_value 654 655#backend error codes used for reverse lookup 656backend_error_dict = {} 657for error_name, error_value, error_name_after_prefix in \ 658 extract_attributes_with_prefix(gnucash_core_c, 'ERR_'): 659 backend_error_dict[ error_value ] = error_name 660 661# GncNumeric denominator computation schemes 662# Used for the denom argument in arithmetic functions like GncNumeric.add 663from gnucash.gnucash_core_c import GNC_DENOM_AUTO 664 665# GncNumeric rounding instructions 666# used for the how argument in arithmetic functions like GncNumeric.add 667from gnucash.gnucash_core_c import \ 668 GNC_HOW_RND_FLOOR, GNC_HOW_RND_CEIL, GNC_HOW_RND_TRUNC, \ 669 GNC_HOW_RND_PROMOTE, GNC_HOW_RND_ROUND_HALF_DOWN, \ 670 GNC_HOW_RND_ROUND_HALF_UP, GNC_HOW_RND_ROUND, GNC_HOW_RND_NEVER 671 672# GncNumeric denominator types 673# used for the how argument in arithmetic functions like GncNumeric.add 674from gnucash.gnucash_core_c import \ 675 GNC_HOW_DENOM_EXACT, GNC_HOW_DENOM_REDUCE, GNC_HOW_DENOM_LCD, \ 676 GNC_HOW_DENOM_FIXED 677 678# import account types 679from gnucash.gnucash_core_c import \ 680 ACCT_TYPE_ASSET, ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CHECKING, \ 681 ACCT_TYPE_CREDIT, ACCT_TYPE_EQUITY, ACCT_TYPE_EXPENSE, ACCT_TYPE_INCOME, \ 682 ACCT_TYPE_LIABILITY, ACCT_TYPE_MUTUAL, ACCT_TYPE_PAYABLE, \ 683 ACCT_TYPE_RECEIVABLE, ACCT_TYPE_STOCK, ACCT_TYPE_ROOT, ACCT_TYPE_TRADING 684 685#Book 686Book.add_constructor_and_methods_with_prefix('qof_book_', 'new') 687Book.add_method('gnc_book_get_root_account', 'get_root_account') 688Book.add_method('gnc_book_set_root_account', 'set_root_account') 689Book.add_method('gnc_commodity_table_get_table', 'get_table') 690Book.add_method('gnc_pricedb_get_db', 'get_price_db') 691Book.add_method('qof_book_increment_and_format_counter', 'increment_and_format_counter') 692 693#Functions that return Account 694Book.get_root_account = method_function_returns_instance( 695 Book.get_root_account, Account ) 696#Functions that return GncCommodityTable 697Book.get_table = method_function_returns_instance( 698 Book.get_table, GncCommodityTable ) 699#Functions that return GNCPriceDB 700Book.get_price_db = method_function_returns_instance( 701 Book.get_price_db, GncPriceDB) 702 703# GncNumeric 704GncNumeric.add_constructor_and_methods_with_prefix('gnc_numeric_', 'create') 705 706gncnumeric_dict = { 707 'same' : GncNumeric, 708 'add' : GncNumeric, 709 'sub' : GncNumeric, 710 'mul' : GncNumeric, 711 'div' : GncNumeric, 712 'neg' : GncNumeric, 713 'abs' : GncNumeric, 714 'add_fixed' : GncNumeric, 715 'sub_fixed' : GncNumeric, 716 'convert' : GncNumeric, 717 'reduce' : GncNumeric 718 } 719methods_return_instance(GncNumeric, gncnumeric_dict) 720 721# GncCommodity 722GncCommodity.add_constructor_and_methods_with_prefix('gnc_commodity_', 'new') 723#Functions that return GncCommodity 724GncCommodity.clone = method_function_returns_instance( 725 GncCommodity.clone, GncCommodity ) 726 727# GncCommodityTable 728GncCommodityTable.add_methods_with_prefix('gnc_commodity_table_') 729commoditytable_dict = { 730 'lookup' : GncCommodity, 731 'lookup_unique' : GncCommodity, 732 'find_full' : GncCommodity, 733 'insert' : GncCommodity, 734 'add_namespace': GncCommodityNamespace, 735 'find_namespace': GncCommodityNamespace, 736 } 737methods_return_instance(GncCommodityTable, commoditytable_dict) 738 739methods_return_instance_lists( 740 GncCommodityTable, { 'get_namespaces_list': GncCommodityNamespace, 741 'get_commodities': GncCommodity, 742 'get_quotable_commodities': GncCommodity, 743 744 } ) 745setattr(GncCommodityTable, 'get_namespaces', getattr(GncCommodityTable, '_get_namespaces_py')) 746 747# GncCommodityNamespace 748GncCommodityNamespace.add_methods_with_prefix('gnc_commodity_namespace_') 749GncCommodityNamespace.get_commodity_list = \ 750 method_function_returns_instance_list( 751 GncCommodityNamespace.get_commodity_list, GncCommodity ) 752 753# GncLot 754GncLot.add_constructor_and_methods_with_prefix('gnc_lot_', 'new') 755 756gnclot_dict = { 757 'get_account' : Account, 758 'get_book' : Book, 759 'get_earliest_split' : Split, 760 'get_latest_split' : Split, 761 'get_balance' : GncNumeric, 762 'lookup' : GncLot, 763 'make_default' : GncLot 764 } 765methods_return_instance(GncLot, gnclot_dict) 766 767# Transaction 768Transaction.add_methods_with_prefix('xaccTrans') 769Transaction.add_method('gncTransGetGUID', 'GetGUID') 770 771Transaction.add_method('xaccTransGetDescription', 'GetDescription') 772Transaction.add_method('xaccTransDestroy', 'Destroy') 773 774trans_dict = { 775 'GetSplit': Split, 776 'FindSplitByAccount': Split, 777 'Clone': Transaction, 778 'Reverse': Transaction, 779 'GetReversedBy': Transaction, 780 'GetImbalanceValue': GncNumeric, 781 'GetAccountValue': GncNumeric, 782 'GetAccountAmount': GncNumeric, 783 'GetAccountConvRate': GncNumeric, 784 'GetAccountBalance': GncNumeric, 785 'GetCurrency': GncCommodity, 786 'GetGUID': GUID 787 } 788 789methods_return_instance(Transaction, trans_dict) 790methods_return_instance_lists( 791 Transaction, { 'GetSplitList': Split, 792 }) 793Transaction.decorate_functions( 794 decorate_monetary_list_returning_function, 'GetImbalance') 795 796# Split 797Split.add_methods_with_prefix('xaccSplit') 798Split.add_method('gncSplitGetGUID', 'GetGUID') 799Split.add_method('xaccSplitDestroy', 'Destroy') 800 801split_dict = { 802 'GetBook': Book, 803 'GetAccount': Account, 804 'GetParent': Transaction, 805 'Lookup': Split, 806 'GetOtherSplit': Split, 807 'GetAmount': GncNumeric, 808 'GetValue': GncNumeric, 809 'GetSharePrice': GncNumeric, 810 'ConvertAmount': GncNumeric, 811 'GetBaseValue': GncNumeric, 812 'GetBalance': GncNumeric, 813 'GetClearedBalance': GncNumeric, 814 'GetReconciledBalance': GncNumeric, 815 'VoidFormerAmount': GncNumeric, 816 'VoidFormerValue': GncNumeric, 817 'GetGUID': GUID 818 } 819methods_return_instance(Split, split_dict) 820 821Split.account = property( Split.GetAccount, Split.SetAccount ) 822Split.parent = property( Split.GetParent, Split.SetParent ) 823 824# Account 825Account.add_methods_with_prefix('xaccAccount') 826Account.add_methods_with_prefix('gnc_account_') 827Account.add_method('gncAccountGetGUID', 'GetGUID') 828Account.add_method('xaccAccountGetPlaceholder', 'GetPlaceholder') 829 830account_dict = { 831 'get_book' : Book, 832 'Lookup' : Account, 833 'get_parent' : Account, 834 'get_root' : Account, 835 'nth_child' : Account, 836 'lookup_by_code' : Account, 837 'lookup_by_name' : Account, 838 'lookup_by_full_name' : Account, 839 'FindTransByDesc' : Transaction, 840 'FindSplitByDesc' : Split, 841 'GetBalance' : GncNumeric, 842 'GetClearedBalance' : GncNumeric, 843 'GetReconciledBalance' : GncNumeric, 844 'GetPresentBalance' : GncNumeric, 845 'GetProjectedMinimumBalance' : GncNumeric, 846 'GetBalanceAsOfDate' : GncNumeric, 847 'ConvertBalanceToCurrency' : GncNumeric, 848 'ConvertBalanceToCurrencyAsOfDate' : GncNumeric, 849 'GetBalanceInCurrency' : GncNumeric, 850 'GetClearedBalanceInCurrency' : GncNumeric, 851 'GetReconciledBalanceInCurrency' : GncNumeric, 852 'GetPresentBalanceInCurrency' : GncNumeric, 853 'GetProjectedMinimumBalanceInCurrency' : GncNumeric, 854 'GetBalanceAsOfDateInCurrency' : GncNumeric, 855 'GetBalanceChangeForPeriod' : GncNumeric, 856 'GetCommodity' : GncCommodity, 857 'GetGUID': GUID 858 } 859methods_return_instance(Account, account_dict) 860methods_return_instance_lists( 861 Account, { 'GetSplitList': Split, 862 'get_children': Account, 863 'get_children_sorted': Account, 864 'get_descendants': Account, 865 'get_descendants_sorted': Account 866 }) 867Account.name = property( Account.GetName, Account.SetName ) 868 869#GUID 870GUID.add_methods_with_prefix('guid_') 871GUID.add_method('xaccAccountLookup', 'AccountLookup') 872GUID.add_method('xaccTransLookup', 'TransLookup') 873GUID.add_method('xaccSplitLookup', 'SplitLookup') 874 875## define addition methods for GUID object - do we need these 876GUID.add_method('guid_to_string', 'to_string') 877#GUID.add_method('string_to_guid', 'string_to_guid') 878 879guid_dict = { 880 'copy' : GUID, 881 'TransLookup': Transaction, 882 'AccountLookup': Account, 883 'SplitLookup': Split 884 } 885methods_return_instance(GUID, guid_dict) 886 887#GUIDString 888class GUIDString(GnuCashCoreClass): 889 pass 890 891GUIDString.add_constructor_and_methods_with_prefix('string_', 'to_guid') 892 893#Query 894from gnucash.gnucash_core_c import \ 895 QOF_QUERY_AND, \ 896 QOF_QUERY_OR, \ 897 QOF_QUERY_NAND, \ 898 QOF_QUERY_NOR, \ 899 QOF_QUERY_XOR 900 901from gnucash.gnucash_core_c import \ 902 QOF_STRING_MATCH_NORMAL, \ 903 QOF_STRING_MATCH_CASEINSENSITIVE 904 905from gnucash.gnucash_core_c import \ 906 QOF_COMPARE_LT, \ 907 QOF_COMPARE_LTE, \ 908 QOF_COMPARE_EQUAL, \ 909 QOF_COMPARE_GT, \ 910 QOF_COMPARE_GTE, \ 911 QOF_COMPARE_NEQ 912 913from gnucash.gnucash_core_c import \ 914 INVOICE_TYPE 915 916from gnucash.gnucash_core_c import \ 917 INVOICE_IS_PAID 918 919class Query(GnuCashCoreClass): 920 921 def search_for(self, obj_type): 922 """Set search_for to obj_type 923 924 calls qof_query_search_for. Buffers search string for queries lifetime. 925 @see https://bugs.gnucash.org/show_bug.cgi?id=796137""" 926 self.__search_for_buf = obj_type 927 self._search_for(self.__search_for_buf) 928 929Query.add_constructor_and_methods_with_prefix('qof_query_', 'create', exclude=["qof_query_search_for"]) 930 931Query.add_method('qof_query_set_book', 'set_book') 932Query.add_method('qof_query_search_for', '_search_for') 933Query.add_method('qof_query_run', 'run') 934Query.add_method('qof_query_add_term', 'add_term') 935Query.add_method('qof_query_add_boolean_match', 'add_boolean_match') 936Query.add_method('qof_query_add_guid_list_match', 'add_guid_list_match') 937Query.add_method('qof_query_add_guid_match', 'add_guid_match') 938Query.add_method('qof_query_destroy', 'destroy') 939 940class QueryStringPredicate(GnuCashCoreClass): 941 pass 942 943QueryStringPredicate.add_constructor_and_methods_with_prefix( 944 'qof_query_','string_predicate') 945 946class QueryBooleanPredicate(GnuCashCoreClass): 947 pass 948 949QueryBooleanPredicate.add_constructor_and_methods_with_prefix( 950 'qof_query_', 'boolean_predicate') 951 952class QueryInt32Predicate(GnuCashCoreClass): 953 pass 954 955QueryInt32Predicate.add_constructor_and_methods_with_prefix( 956 'qof_query_', 'int32_predicate') 957 958class QueryDatePredicate(GnuCashCoreClass): 959 pass 960 961QueryDatePredicate.add_constructor_and_methods_with_prefix( 962 'qof_query_', 'date_predicate', exclude=["qof_query_date_predicate_get_date"]) 963QueryDatePredicate.add_method('qof_query_date_predicate_get_date', 'get_date') 964 965class QueryGuidPredicate(GnuCashCoreClass): 966 pass 967 968QueryGuidPredicate.add_constructor_and_methods_with_prefix( 969 'qof_query_', 'guid_predicate') 970