GnuCash  5.6-150-g038405b370+
gnucash_core.py
Go to the documentation of this file.
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 
26 
28 
29 
32 
33 
152 
153 
154 
159 
160 import operator
161 
162 from enum import IntEnum
163 from urllib.parse import urlparse
164 
165 from gnucash import gnucash_core_c
166 from gnucash import _sw_core_utils
167 
168 from gnucash.function_class import \
169  ClassFromFunctions, extract_attributes_with_prefix, \
170  default_arguments_decorator, method_function_returns_instance, \
171  methods_return_instance, process_list_convert_to_instance, \
172  method_function_returns_instance_list, methods_return_instance_lists
173 
174 from gnucash.gnucash_core_c import gncInvoiceLookup, gncInvoiceGetInvoiceFromTxn, \
175  gncInvoiceGetInvoiceFromLot, gncEntryLookup, gncInvoiceLookup, \
176  gncCustomerLookup, gncVendorLookup, gncJobLookup, gncEmployeeLookup, \
177  gncTaxTableLookup, gncTaxTableLookupByName, gnc_search_invoice_on_id, \
178  gnc_search_customer_on_id, gnc_search_bill_on_id , \
179  gnc_search_vendor_on_id, gncInvoiceNextID, gncCustomerNextID, \
180  gncVendorNextID, gncTaxTableGetTables, gnc_numeric_zero, \
181  gnc_numeric_create, double_to_gnc_numeric, gnc_numeric_from_string, \
182  gnc_numeric_to_string, gnc_numeric_check
183 
184 from gnucash.deprecation import (
185  deprecated_args_session,
186  deprecated_args_session_init,
187  deprecated_args_session_begin,
188  deprecated
189 )
190 
191 try:
192  import gettext
193 
194  _localedir = _sw_core_utils.gnc_path_get_localedir()
195  gettext.install(_sw_core_utils.GETTEXT_PACKAGE, _localedir)
196 except:
197  print()
198  print("Problem importing gettext!")
199  import traceback
200  import sys
201  exc_type, exc_value, exc_traceback = sys.exc_info()
202  traceback.print_exception(exc_type, exc_value, exc_traceback)
203  print()
204 
205  def _(s):
206  """Null translator function, gettext not available"""
207  return s
208 
209  import builtins
210  builtins.__dict__['_'] = _
211 
212 class GnuCashCoreClass(ClassFromFunctions):
213  _module = gnucash_core_c
214 
215  def do_lookup_create_oo_instance(self, lookup_function, cls, *args):
216  thing = lookup_function(self.get_instance(), *args)
217  if thing != None:
218  thing = cls(instance=thing)
219  return thing
220 
221 
222 class GnuCashBackendException(Exception):
223  def __init__(self, msg, errors):
224  Exception.__init__(self, msg)
225  self.errors = errors
226 
227 
228 class SessionOpenMode(IntEnum):
229  """Mode for opening sessions.
230 
231  This replaces three booleans that were passed in order: ignore_lock, create,
232  and force. It's structured so that one can use it as a bit field with the
233  values in the same order, i.e. ignore_lock = 1 << 2, create_new = 1 << 1, and
234  force_new = 1.
235 
236  enumeration members
237  -------------------
238 
239  SESSION_NORMAL_OPEN = 0 (All False)
240  Open will fail if the URI doesn't exist or is locked.
241 
242  SESSION_NEW_STORE = 2 (False, True, False (create))
243  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.
244 
245  SESSION_NEW_OVERWRITE = 3 (False, True, True (create | force))
246  Create a new store at the URI even if a store already exists there.
247 
248  SESSION_READ_ONLY = 4, (True, False, False (ignore_lock))
249  Open the session read-only, ignoring any existing lock and not creating one if the URI isn't locked.
250 
251  SESSION_BREAK_LOCK = 5 (True, False, True (ignore_lock | force))
252  Open the session, taking over any existing lock.
253 
254  source: lignucash/engine/qofsession.h
255  """
256 
257  SESSION_NORMAL_OPEN = gnucash_core_c.SESSION_NORMAL_OPEN
258  """All False
259  Open will fail if the URI doesn't exist or is locked."""
260 
261  SESSION_NEW_STORE = gnucash_core_c.SESSION_NEW_STORE
262  """False, True, False (create)
263  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."""
264 
265  SESSION_NEW_OVERWRITE = gnucash_core_c.SESSION_NEW_OVERWRITE
266  """False, True, True (create | force)
267  Create a new store at the URI even if a store already exists there."""
268 
269  SESSION_READ_ONLY = gnucash_core_c.SESSION_READ_ONLY
270  """True, False, False (ignore_lock)
271  Open the session read-only, ignoring any existing lock and not creating one if the URI isn't locked."""
272 
273  SESSION_BREAK_LOCK = gnucash_core_c.SESSION_BREAK_LOCK
274  """True, False, True (ignore_lock | force)
275  Open the session, taking over any existing lock."""
276 
277 
279  """A GnuCash book editing session
280 
281  To commit changes to the session you may need to call save,
282  (this is always the case with the file backend).
283 
284  When you're down with a session you may need to call end()
285 
286  Every Session has a Book in the book attribute, which you'll definitely
287  be interested in, as every GnuCash entity (Transaction, Split, Vendor,
288  Invoice..) is associated with a particular book where it is stored.
289  """
290 
291  @deprecated_args_session_init
292  def __init__(self, book_uri=None, mode=None, instance=None, book=None):
293  """!
294  A convenient constructor that allows you to specify a book URI,
295  begin the session, and load the book.
296 
297  This can give you the power of calling
298  qof_session_new, qof_session_begin, and qof_session_load all in one!
299 
300  qof_session_load is only called if url scheme is "xml" and
301  mode is SESSION_NEW_STORE or SESSION_NEW_OVERWRITE
302 
303  @param book_uri must be a string in the form of a URI/URL. The access
304  method specified depends on the loaded backends. Paths may be relative
305  or absolute. If the path is relative, that is if the argument is
306  "file://somefile.xml", then the current working directory is
307  assumed. Customized backends can choose to search other
308  application-specific directories or URI schemes as well.
309  It be None to skip the calls to qof_session_begin and
310  qof_session_load.
311 
312  @param instance argument can be passed if new Session is used as a
313  wrapper for an existing session instance
314 
315  @param mode The SessionOpenMode.
316  @note SessionOpenMode replaces deprecated ignore_lock, is_new and force_new.
317 
318  @par SessionOpenMode
319  `SESSION_NORMAL_OPEN`: Find an existing file or database at the provided uri and
320  open it if it is unlocked. If it is locked post a QOF_BACKEND_LOCKED error.
321  @par
322  `SESSION_NEW_STORE`: Check for an existing file or database at the provided
323  uri and if none is found, create it. If the file or database exists post a
324  QOF_BACKED_STORE_EXISTS and return.
325  @par
326  `SESSION_NEW_OVERWRITE`: Create a new file or database at the provided uri,
327  deleting any existing file or database.
328  @par
329  `SESSION_READ_ONLY`: Find an existing file or database and open it without
330  disturbing the lock if it exists or setting one if not. This will also set a
331  flag on the book that will prevent many elements from being edited and will
332  prevent the backend from saving any edits.
333  @par
334  `SESSION_BREAK_LOCK`: Find an existing file or database, lock it, and open
335  it. If there is already a lock replace it with a new one for this session.
336 
337  @par Errors
338  qof_session_begin() signals failure by queuing errors. After it completes use
339  qof_session_get_error() and test that the value is `ERROR_BACKEND_NONE` to
340  determine that the session began successfully.
341 
342  @exception as begin() and load() are wrapped with raise_backend_errors_after_call()
343  this function can raise a GnuCashBackendException. If it does,
344  you don't need to cleanup and call end() and destroy(), that is handled
345  for you, and the exception is raised.
346  """
347  if instance is not None:
348  GnuCashCoreClass.__init__(self, instance=instance)
349  else:
350  if book is None:
351  book = Book()
352  GnuCashCoreClass.__init__(self, book)
353 
354  if book_uri is not None:
355  try:
356  if mode is None:
357  mode = SessionOpenMode.SESSION_NORMAL_OPEN
358  self.begin(book_uri, mode)
359  is_new = mode in (SessionOpenMode.SESSION_NEW_STORE, SessionOpenMode.SESSION_NEW_OVERWRITE)
360  if not is_new:
361  self.load()
362  except GnuCashBackendException as backend_exception:
363  self.end()
364  self.destroy()
365  raise
366 
367  def __enter__(self):
368  return self
369 
370  def __exit__(self, exc_type, exc_value, traceback):
371  # Roll back changes on exception by not calling save. Only works for XMl backend.
372  if not exc_type:
373  self.save()
374  self.end()
375 
376  def raise_backend_errors(self, called_function="qof_session function"):
377  """Raises a GnuCashBackendException if there are outstanding
378  QOF_BACKEND errors.
379 
380  set called_function to name the function that was last called
381  """
382  errors = self.pop_all_errors()
383  if errors != ():
385  "call to %s resulted in the "
386  "following errors, %s" % (called_function, backend_error_dict[errors[0]]),
387  errors )
388 
389  def generate_errors(self):
390  """A generator that yields any outstanding QofBackend errors
391  """
392  while self.get_error() is not ERR_BACKEND_NO_ERR:
393  error = self.pop_error()
394  yield error
395 
396  def pop_all_errors(self):
397  """Returns any accumulated qof backend errors as a tuple
398  """
399  return tuple( self.generate_errors() )
400 
401  # STATIC METHODS
402  @staticmethod
403  def raise_backend_errors_after_call(function, *args, **kwargs):
404  """A function decorator that results in a call to
405  raise_backend_errors after execution.
406  """
407  def new_function(self, *args, **kwargs):
408  return_value = function(self, *args, **kwargs)
409  self.raise_backend_errors(function.__name__)
410  return return_value
411  return new_function
412 
414  """A Book encapsulates all of the GnuCash data, it is the place where
415  all GnuCash entities (Transaction, Split, Vendor, Invoice...), are
416  stored. You'll notice that all of the constructors for those entities
417  need a book to be associated with.
418 
419  The most common way to get a book is through the book property in the
420  Session class, that is, create a session that connects to some storage,
421  such as through 'my_session = Session('file:my_books.xac')', and access
422  the book via the book property, 'my_session.book'
423 
424  If you would like to create a Book without any backing storage, call the
425  Book constructor without any parameters, 'Book()'. You can later merge
426  such a book into a book with actual store by using merge_init.
427 
428  Methods of interest
429  get_root_account -- Returns the root level Account
430  get_table -- Returns a commodity lookup table, of type GncCommodityTable
431  """
432  def InvoiceLookup(self, guid):
433  from gnucash.gnucash_business import Invoice
434  return self.do_lookup_create_oo_instance(
435  gncInvoiceLookup, Invoice, guid.get_instance() )
436 
437  def EntryLookup(self, guid):
438  from gnucash.gnucash_business import Entry
439  return self.do_lookup_create_oo_instance(
440  gncEntryLookup, Entry, guid.get_instance() )
441 
442  def CustomerLookup(self, guid):
443  from gnucash.gnucash_business import Customer
444  return self.do_lookup_create_oo_instance(
445  gncCustomerLookup, Customer, guid.get_instance())
446 
447  def JobLookup(self, guid):
448  from gnucash.gnucash_business import Job
449  return self.do_lookup_create_oo_instance(
450  gncJobLookup, Job, guid.get_instance() )
451 
452  def VendorLookup(self, guid):
453  from gnucash.gnucash_business import Vendor
454  return self.do_lookup_create_oo_instance(
455  gncVendorLookup, Vendor, guid.get_instance() )
456 
457  def EmployeeLookup(self, guid):
458  from gnucash.gnucash_business import Employee
459  return self.do_lookup_create_oo_instance(
460  gncEmployeeLookup, Employee, guid.get_instance() )
461 
462  def TaxTableLookup(self, guid):
463  from gnucash.gnucash_business import TaxTable
464  return self.do_lookup_create_oo_instance(
465  gncTaxTableLookup, TaxTable, guid.get_instance() )
466 
467  def TaxTableLookupByName(self, name):
468  from gnucash.gnucash_business import TaxTable
469  return self.do_lookup_create_oo_instance(
470  gncTaxTableLookupByName, TaxTable, name)
471 
472  def TaxTableGetTables(self):
473  from gnucash.gnucash_business import TaxTable
474  return [ TaxTable(instance=item) for item in gncTaxTableGetTables(self.instance) ]
475 
476  def BillLookupByID(self, id):
477  from gnucash.gnucash_business import Bill
478  return self.do_lookup_create_oo_instance(
479  gnc_search_bill_on_id, Bill, id)
480 
481  def InvoiceLookupByID(self, id):
482  from gnucash.gnucash_business import Invoice
483  return self.do_lookup_create_oo_instance(
484  gnc_search_invoice_on_id, Invoice, id)
485 
486  def CustomerLookupByID(self, id):
487  from gnucash.gnucash_business import Customer
488  return self.do_lookup_create_oo_instance(
489  gnc_search_customer_on_id, Customer, id)
490 
491  def VendorLookupByID(self, id):
492  from gnucash.gnucash_business import Vendor
493  return self.do_lookup_create_oo_instance(
494  gnc_search_vendor_on_id, Vendor, id)
495 
496  def InvoiceNextID(self, customer):
497  ''' Return the next invoice ID.
498  '''
499  from gnucash.gnucash_core_c import gncInvoiceNextID
500  return gncInvoiceNextID(self.get_instance(),customer.GetEndOwner().get_instance()[1])
501 
502  def BillNextID(self, vendor):
503  ''' Return the next Bill ID. '''
504  from gnucash.gnucash_core_c import gncInvoiceNextID
505  return gncInvoiceNextID(self.get_instance(),vendor.GetEndOwner().get_instance()[1])
506 
507  def CustomerNextID(self):
508  ''' Return the next Customer ID. '''
509  from gnucash.gnucash_core_c import gncCustomerNextID
510  return gncCustomerNextID(self.get_instance())
511 
512  def VendorNextID(self):
513  ''' Return the next Vendor ID. '''
514  from gnucash.gnucash_core_c import gncVendorNextID
515  return gncVendorNextID(self.get_instance())
516 
518  """Object used by GnuCash to store all numbers. Always consists of a
519  numerator and denominator.
520 
521  The constants GNC_DENOM_AUTO,
522  GNC_HOW_RND_FLOOR, GNC_HOW_RND_CEIL, GNC_HOW_RND_TRUNC,
523  GNC_HOW_RND_PROMOTE, GNC_HOW_RND_ROUND_HALF_DOWN,
524  GNC_HOW_RND_ROUND_HALF_UP, GNC_HOW_RND_ROUND, GNC_HOW_RND_NEVER,
525  GNC_HOW_DENOM_EXACT, GNC_HOW_DENOM_REDUCE, GNC_HOW_DENOM_LCD,
526  and GNC_HOW_DENOM_FIXED are available for arithmetic
527  functions like GncNumeric.add
528 
529  Look at gnc-numeric.h to see how to use these
530  """
531 
532  def __init__(self, *args, **kargs):
533  """Constructor that supports the following formats:
534  * No arguments defaulting to zero: eg. GncNumeric() == 0/1
535  * A integer: e.g. GncNumeric(1) == 1/1
536  * Numerator and denominator intager pair: eg. GncNumeric(1, 2) == 1/2
537  * A floating point number: e.g. GncNumeric(0.5) == 1/2
538  * A floating point number with defined conversion: e.g.
539  GncNumeric(0.5, GNC_DENOM_AUTO,
540  GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER) == 1/2
541  * A string: e.g. GncNumeric("1/2") == 1/2
542  """
543  if 'instance' not in kargs:
544  kargs['instance'] = GncNumeric.__args_to_instance(args)
545  GnuCashCoreClass.__init__(self, [], **kargs)
546 
547  @staticmethod
548  def __args_to_instance(args):
549  if len(args) == 0:
550  return gnc_numeric_zero()
551  elif len(args) == 1:
552  arg = args[0]
553  if isinstance(arg, int):
554  return gnc_numeric_create(arg, 1)
555  elif isinstance(arg, float):
556  return double_to_gnc_numeric(arg, GNC_DENOM_AUTO, GNC_HOW_DENOM_FIXED | GNC_HOW_RND_NEVER)
557  elif isinstance(arg, str):
558  instance = gnc_numeric_from_string(arg)
559  if gnc_numeric_check(instance):
560  raise TypeError('Failed to convert to GncNumeric: ' + str(args))
561  return instance
562  elif isinstance(arg, GncNumeric):
563  return arg.instance
564  else:
565  raise TypeError('Only single int/float/str/GncNumeric allowed: ' + str(args))
566  elif len(args) == 2:
567  if isinstance(args[0], int) and isinstance(args[1], int):
568  return gnc_numeric_create(*args)
569  else:
570  raise TypeError('Only two ints allowed: ' + str(args))
571  elif len(args) == 3:
572  if isinstance(args[0], float) \
573  and isinstance(args[1], int) \
574  and type(args[2]) == type(GNC_HOW_DENOM_FIXED):
575  return double_to_gnc_numeric(*args)
576  else:
577  raise TypeError('Only (float, int, GNC_HOW_RND_*) allowed: ' + str(args))
578  else:
579  raise TypeError('Required single int/float/str or two ints: ' + str(args))
580 
581  # from https://docs.python.org/3/library/numbers.html#numbers.Integral
582  # and https://github.com/python/cpython/blob/3.7/Lib/fractions.py
583 
584  def _operator_fallbacks(monomorphic_operator, fallback_operator):
585  """fallbacks are not needed except for method name,
586  keep for possible later use"""
587  def forward(a, b):
588  if isinstance(b, GncNumeric):
589  return monomorphic_operator(a, b)
590  if isinstance(b, (int, float)):
591  return monomorphic_operator(a, GncNumeric(b))
592  else:
593  return NotImplemented
594  forward.__name__ = '__' + fallback_operator.__name__ + '__'
595  forward.__doc__ = monomorphic_operator.__doc__
596 
597  def reverse(b, a):
598  if isinstance(a, (GncNumeric, int, float)):
599  return forward(b, a)
600  else:
601  return NotImplemented
602  reverse.__name__ = '__r' + fallback_operator.__name__ + '__'
603  reverse.__doc__ = monomorphic_operator.__doc__
604 
605  return forward, reverse
606 
607  def _add(a, b):
608  return a.add(b, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND)
609 
610  def _sub(a, b):
611  return a.sub(b, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND)
612 
613  def _mul(a, b):
614  return a.mul(b, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND)
615 
616  def _div(a, b):
617  return a.div(b, GNC_DENOM_AUTO, GNC_HOW_RND_ROUND)
618 
619  def _floordiv(a, b):
620  return a.div(b, 1, GNC_HOW_RND_TRUNC)
621 
622  __add__, __radd__ = _operator_fallbacks(_add, operator.add)
623  __iadd__ = __add__
624  __sub__, __rsub__ = _operator_fallbacks(_sub, operator.sub)
625  __isub__ = __sub__
626  __mul__, __rmul__ = _operator_fallbacks(_mul, operator.mul)
627  __imul__ = __mul__
628  __truediv__, __rtruediv__ = _operator_fallbacks(_div, operator.truediv)
629  __itruediv__ = __truediv__
630  __floordiv__, __rfloordiv__ = _operator_fallbacks(_floordiv, operator.floordiv)
631  __ifloordiv__ = __floordiv__
632 
633  # Comparisons derived from https://github.com/python/cpython/blob/3.7/Lib/fractions.py
634  def _lt(a, b):
635  return a.compare(b) == -1
636 
637  def _gt(a, b):
638  return a.compare(b) == 1
639 
640  def _le(a, b):
641  return a.compare(b) in (0,-1)
642 
643  def _ge(a, b):
644  return a.compare(b) in (0,1)
645 
646  def _eq(a, b):
647  return a.compare(b) == 0
648 
649  def _richcmp(self, other, op):
650  """Helper for comparison operators, for internal use only.
651  Implement comparison between a GncNumeric instance `self`,
652  and either another GncNumeric instance, an int or a float
653  `other`. If `other` is not an instance of that kind, return
654  NotImplemented. `op` should be one of the six standard
655  comparison operators. The comparisons are based on
656  GncNumeric.compare().
657  """
658  import math
659  if isinstance(other, GncNumeric):
660  return op(other)
661  elif isinstance(other, (int, float)):
662  return op(GncNumeric(other))
663  else:
664  return NotImplemented
665 
666  def __lt__(a, b):
667  """a < b"""
668  return a._richcmp(b, a._lt)
669 
670  def __gt__(a, b):
671  """a > b"""
672  return a._richcmp(b, a._gt)
673 
674  def __le__(a, b):
675  """a <= b"""
676  return a._richcmp(b, a._le)
677 
678  def __ge__(a, b):
679  """a >= b"""
680  return a._richcmp(b, a._ge)
681 
682  def __eq__(a, b):
683  """a == b"""
684  return a._richcmp(b, a._eq)
685 
686  def __bool__(a):
687  """a != 0"""
688  return bool(a.num())
689 
690  def __float__(self):
691  return self.to_double()
692 
693  def __int__(self):
694  return int(self.to_double())
695 
696  def __pos__(a):
697  """+a"""
698  return GncNumeric(a.num(), a.denom())
699 
700  def __neg__(a):
701  """-a"""
702  return a.neg()
703 
704  def __abs__(a):
705  """abs(a)"""
706  return a.abs()
707 
708  def to_fraction(self):
709  from fractions import Fraction
710  return Fraction(self.num(), self.denom())
711 
712  def __str__(self):
713  """Returns a human readable numeric value string as UTF8."""
714  return gnc_numeric_to_string(self.instance)
715 
717  '''
718  Each priceEach price in the database represents an "instantaneous"
719  quote for a given commodity with respect to another commodity.
720  For example, a given price might represent the value of LNUX in USD on 2001-02-03.
721 
722  Fields:
723  * commodity: the item being priced.
724  * currency: the denomination of the value of the item being priced.
725  * value: the value of the item being priced.
726  * time: the time the price was valid.
727  * source: a string describing the source of the quote. These strings will be something like this:
728  "Finance::Quote", "user:misc", "user:foo", etc. If the quote came from a user, as a matter of policy,
729  you *must* prefix the string you give with "user:". For now, the only other reserved values are
730  "Finance::Quote" and "old-file-import". Any string used must be added to the source_list array in
731  dialog-price-edit-db.c so that it can be properly translated. (There are unfortunately many strings
732  in users' databases, so this string must be translated on output instead of always being used in untranslated form).
733  * type: the type of quote - types possible right now are bid, ask, last, nav, and
734  unknown.Each price in the database represents an "instantaneous" quote for a given
735  commodity with respect to another commodity.
736  For example, a given price might represent the value of LNUX in USD on 2001-02-03.
737 
738  See also https://code.gnucash.org/docs/head/group__Price.html
739  '''
740  _new_instance = 'gnc_price_create'
741 GncPrice.add_methods_with_prefix('gnc_price_')
742 
743 
745  '''
746  a simple price database for gnucash.
747  The PriceDB is intended to be a database of price quotes, or more specifically,
748  a database of GNCPrices. For the time being, it is still a fairly simple
749  database supporting only fairly simple queries. It is expected that new
750  queries will be added as needed, and that there is some advantage to delaying
751  complex queries for now in the hope that we get a real DB implementation
752  before they're really needed.
753 
754  Every QofBook contains a GNCPriceDB, accessible via gnc_pricedb_get_db.
755 
756  Definition in file gnc-pricedb.h.
757  See also https://code.gnucash.org/docs/head/gnc-pricedb_8h.html
758  '''
759 
760 @deprecated("Use gnc_pricedb_latest_before_t64")
761 def gnc_pricedb_lookup_latest_before_t64(self, commodity, currency, date):
762  return self.lookup_nearest_before_t64(commodity, currency, date)
763 
764 GncPriceDB.add_method('gnc_pricedb_lookup_latest_before_t64', 'lookup_latest_before_t64')
765 
766 GncPriceDB.lookup_latest_before_t64 = method_function_returns_instance(GncPriceDB.lookup_latest_before_t64, GncPrice)
767 
768 GncPriceDB.add_methods_with_prefix('gnc_pricedb_')
769 PriceDB_dict = {
770  'lookup_latest' : GncPrice,
771  'lookup_nearest_in_time64' : GncPrice,
772  'lookup_nearest_before_t64' : GncPrice,
773  'nth_price' : GncPrice,
774  'lookup_day_t64' : GncPrice,
775  'convert_balance_latest_price' : GncNumeric,
776  'convert_balance_nearest_price_t64' : GncNumeric,
777  'convert_balance_nearest_before_price_t64' : GncNumeric,
778  'get_latest_price' : GncNumeric,
779  'get_nearest_price' : GncNumeric,
780  'get_nearest_before_price' : GncNumeric,
781  }
782 methods_return_instance(GncPriceDB,PriceDB_dict)
783 methods_return_instance_lists(
784  GncPriceDB, { 'get_prices': GncPrice,
785  'lookup_latest_any_currency': GncPrice,
786  'lookup_nearest_before_any_currency_t64': GncPrice,
787  'lookup_nearest_in_time_any_currency_t64': GncPrice,
788  })
789 
791 
793  """A CommodityTable provides a way to store and lookup commodities.
794  Commodities are primarily currencies, but other tradable things such as
795  stocks, mutual funds, and material substances are possible.
796 
797  Users of this library should not create their own CommodityTable, instead
798  the get_table method from the Book class should be used.
799 
800  This table is automatically populated with the GnuCash default commodity's
801  which includes most of the world's currencies.
802  """
803 
804  def _get_namespaces_py(self):
805  return [ns.get_name() for ns in self.get_namespaces_list()]
806 
808  pass
809 
810 class GncLot(GnuCashCoreClass):
811  def GetInvoiceFromLot(self):
812  from gnucash.gnucash_business import Invoice
813  return self.do_lookup_create_oo_instance(
814  gncInvoiceGetInvoiceFromLot, Invoice )
815 
817  """A GnuCash Transaction
818 
819  Consists of at least one (generally two) splits to represent a transaction
820  between two accounts.
821 
822 
823  Has a GetImbalance() method that returns a list of all the imbalanced
824  currencies. Each list item is a two element tuple, the first element is
825  the imbalanced commodity, the second element is the value.
826 
827  Warning, the commodity.get_instance() value can be None when there
828  is no currency set for the transaction.
829  """
830  _new_instance = 'xaccMallocTransaction'
831  def GetNthSplit(self, n):
832  return self.GetSplitList().pop(n)
833 
834  def GetInvoiceFromTxn(self):
835  from gnucash.gnucash_business import Invoice
836  return self.do_lookup_create_oo_instance(
837  gncInvoiceGetInvoiceFromTxn, Invoice )
838 
839  def __eq__(self, other):
840  return self.Equal(other, True, False, False, False)
841 
842 def decorate_monetary_list_returning_function(orig_function):
843  def new_function(self, *args):
844  """decorate function that returns list of gnc_monetary to return tuples of GncCommodity and GncNumeric
845 
846  Args:
847  *args: Variable length argument list. Will get passed to orig_function
848 
849  Returns:
850  array of tuples: (GncCommodity, GncNumeric)
851 
852  ToDo:
853  Maybe this function should better reside in module function_class (?)"""
854  # warning, item.commodity has been shown to be None
855  # when the transaction doesn't have a currency
856  return [(GncCommodity(instance=item.commodity),
857  GncNumeric(instance=item.value))
858  for item in orig_function(self, *args) ]
859  return new_function
860 
862  """A GnuCash Split
863 
864  The most basic representation of a movement of currency from one account to
865  another.
866  """
867  _new_instance = 'xaccMallocSplit'
868 
869  def __eq__(self, other):
870  return self.Equal(other, True, False, False)
871 
873  """A GnuCash Account.
874 
875  A fundamental entity in accounting, an Account provides representation
876  for a financial object, such as a ACCT_TYPE_BANK account, an
877  ACCT_TYPE_ASSET (like a building),
878  a ACCT_TYPE_LIABILITY (such as a bank loan), a summary of some type of
879  ACCT_TYPE_EXPENSE, or a summary of some source of ACCT_TYPE_INCOME .
880 
881  The words in upper case are the constants that GnuCash and this library uses
882  to describe account type. Here is the full list:
883  ACCT_TYPE_ASSET, ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CHECKING, \
884  ACCT_TYPE_CREDIT, ACCT_TYPE_EQUITY, ACCT_TYPE_EXPENSE, ACCT_TYPE_INCOME, \
885  ACCT_TYPE_LIABILITY, ACCT_TYPE_MUTUAL, ACCT_TYPE_PAYABLE, \
886  ACCT_TYPE_RECEIVABLE, ACCT_TYPE_STOCK, ACCT_TYPE_ROOT, ACCT_TYPE_TRADING
887 
888  These are not strings, they are attributes you can import from this
889  module
890  """
891  _new_instance = 'xaccMallocAccount'
892 
894  _new_instance = 'guid_new_return'
895 
896 # Session
897 Session.add_constructor_and_methods_with_prefix('qof_session_', 'new')
898 
899 def one_arg_default_none(function):
900  return default_arguments_decorator(function, None, None)
901 Session.decorate_functions(one_arg_default_none, "load", "save")
902 
903 Session.decorate_functions( Session.raise_backend_errors_after_call,
904  "begin", "load", "save", "end")
905 Session.decorate_method(default_arguments_decorator, "begin", None, mode=SessionOpenMode.SESSION_NORMAL_OPEN)
906 Session.decorate_functions(deprecated_args_session_begin, "begin")
907 
908 Session.get_book = method_function_returns_instance(
909  Session.get_book, Book )
910 
911 Session.book = property( Session.get_book )
912 
913 # import all of the session backend error codes into this module
914 this_module_dict = globals()
915 for error_name, error_value, error_name_after_prefix in \
916  extract_attributes_with_prefix(gnucash_core_c, 'ERR_'):
917  this_module_dict[ error_name ] = error_value
918 
919 #backend error codes used for reverse lookup
920 backend_error_dict = {}
921 for error_name, error_value, error_name_after_prefix in \
922  extract_attributes_with_prefix(gnucash_core_c, 'ERR_'):
923  backend_error_dict[ error_value ] = error_name
924 
925 # GncNumeric denominator computation schemes
926 # Used for the denom argument in arithmetic functions like GncNumeric.add
927 from gnucash.gnucash_core_c import GNC_DENOM_AUTO
928 
929 # GncNumeric rounding instructions
930 # used for the how argument in arithmetic functions like GncNumeric.add
931 from gnucash.gnucash_core_c import \
932  GNC_HOW_RND_FLOOR, GNC_HOW_RND_CEIL, GNC_HOW_RND_TRUNC, \
933  GNC_HOW_RND_PROMOTE, GNC_HOW_RND_ROUND_HALF_DOWN, \
934  GNC_HOW_RND_ROUND_HALF_UP, GNC_HOW_RND_ROUND, GNC_HOW_RND_NEVER
935 
936 # GncNumeric denominator types
937 # used for the how argument in arithmetic functions like GncNumeric.add
938 from gnucash.gnucash_core_c import \
939  GNC_HOW_DENOM_EXACT, GNC_HOW_DENOM_REDUCE, GNC_HOW_DENOM_LCD, \
940  GNC_HOW_DENOM_FIXED, GNC_HOW_DENOM_SIGFIG
941 
942 # import account types
943 from gnucash.gnucash_core_c import \
944  ACCT_TYPE_ASSET, ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CHECKING, \
945  ACCT_TYPE_CREDIT, ACCT_TYPE_EQUITY, ACCT_TYPE_EXPENSE, ACCT_TYPE_INCOME, \
946  ACCT_TYPE_LIABILITY, ACCT_TYPE_MUTUAL, ACCT_TYPE_PAYABLE, \
947  ACCT_TYPE_RECEIVABLE, ACCT_TYPE_STOCK, ACCT_TYPE_ROOT, ACCT_TYPE_TRADING
948 
949 #Book
950 Book.add_constructor_and_methods_with_prefix('qof_book_', 'new')
951 Book.add_method('gnc_book_get_root_account', 'get_root_account')
952 Book.add_method('gnc_book_set_root_account', 'set_root_account')
953 Book.add_method('gnc_commodity_table_get_table', 'get_table')
954 Book.add_method('gnc_pricedb_get_db', 'get_price_db')
955 Book.add_method('qof_book_increment_and_format_counter', 'increment_and_format_counter')
956 
957 #Functions that return Account
958 Book.get_root_account = method_function_returns_instance(
959  Book.get_root_account, Account )
960 #Functions that return GncCommodityTable
961 Book.get_table = method_function_returns_instance(
962  Book.get_table, GncCommodityTable )
963 #Functions that return GNCPriceDB
964 Book.get_price_db = method_function_returns_instance(
965  Book.get_price_db, GncPriceDB)
966 
967 # GncNumeric
968 GncNumeric.add_constructor_and_methods_with_prefix('gnc_numeric_', 'create')
969 
970 gncnumeric_dict = {
971  'same' : GncNumeric,
972  'add' : GncNumeric,
973  'sub' : GncNumeric,
974  'mul' : GncNumeric,
975  'div' : GncNumeric,
976  'neg' : GncNumeric,
977  'abs' : GncNumeric,
978  'add_fixed' : GncNumeric,
979  'sub_fixed' : GncNumeric,
980  'convert' : GncNumeric,
981  'reduce' : GncNumeric,
982  'invert' : GncNumeric
983  }
984 methods_return_instance(GncNumeric, gncnumeric_dict)
985 
986 # GncCommodity
987 GncCommodity.add_constructor_and_methods_with_prefix('gnc_commodity_', 'new')
988 gnc_commodity_dict = {
989  'clone': GncCommodity,
990  'obtain_twin': GncCommodity,
991  'get_namespace_ds': GncCommodityNamespace,
992  }
993 methods_return_instance(GncCommodity, gnc_commodity_dict)
994 
995 # GncPrice (deferred until after GncCommodity is defined)
996 gnc_price_dict = {
997  'get_commodity': GncCommodity,
998  'get_currency': GncCommodity,
999  'clone': GncPrice,
1000  'get_value': GncNumeric,
1001  }
1002 methods_return_instance(GncPrice, gnc_price_dict)
1003 
1004 # GncCommodityTable
1005 GncCommodityTable.add_methods_with_prefix('gnc_commodity_table_')
1006 commoditytable_dict = {
1007  'lookup' : GncCommodity,
1008  'lookup_unique' : GncCommodity,
1009  'find_full' : GncCommodity,
1010  'insert' : GncCommodity,
1011  'add_namespace': GncCommodityNamespace,
1012  'find_namespace': GncCommodityNamespace,
1013  }
1014 methods_return_instance(GncCommodityTable, commoditytable_dict)
1015 
1016 methods_return_instance_lists(
1017  GncCommodityTable, { 'get_namespaces_list': GncCommodityNamespace,
1018  'get_commodities': GncCommodity,
1019  'get_quotable_commodities': GncCommodity,
1020 
1021  } )
1022 setattr(GncCommodityTable, 'get_namespaces', getattr(GncCommodityTable, '_get_namespaces_py'))
1023 
1024 # GncCommodityNamespace
1025 GncCommodityNamespace.add_methods_with_prefix('gnc_commodity_namespace_')
1026 GncCommodityNamespace.get_commodity_list = \
1027  method_function_returns_instance_list(
1028  GncCommodityNamespace.get_commodity_list, GncCommodity )
1029 
1030 # GncLot
1031 GncLot.add_constructor_and_methods_with_prefix('gnc_lot_', 'new')
1032 
1033 gnclot_dict = {
1034  'get_account' : Account,
1035  'get_book' : Book,
1036  'get_earliest_split' : Split,
1037  'get_latest_split' : Split,
1038  'get_balance' : GncNumeric,
1039  'lookup' : GncLot,
1040  'make_default' : GncLot
1041  }
1042 methods_return_instance(GncLot, gnclot_dict)
1043 methods_return_instance_lists(
1044  GncLot, { 'get_split_list': Split,
1045  })
1046 
1047 # Transaction
1048 Transaction.add_methods_with_prefix('xaccTrans')
1049 Transaction.add_method('gncTransGetGUID', 'GetGUID')
1050 
1051 Transaction.add_method('xaccTransGetDescription', 'GetDescription')
1052 Transaction.add_method('xaccTransDestroy', 'Destroy')
1053 
1054 trans_dict = {
1055  'GetSplit': Split,
1056  'FindSplitByAccount': Split,
1057  'Clone': Transaction,
1058  'Reverse': Transaction,
1059  'GetReversedBy': Transaction,
1060  'GetImbalanceValue': GncNumeric,
1061  'GetAccountValue': GncNumeric,
1062  'GetAccountAmount': GncNumeric,
1063  'GetAccountConvRate': GncNumeric,
1064  'GetAccountBalance': GncNumeric,
1065  'GetCurrency': GncCommodity,
1066  'GetGUID': GUID
1067  }
1068 
1069 methods_return_instance(Transaction, trans_dict)
1070 methods_return_instance_lists(
1071  Transaction, { 'GetSplitList': Split,
1072  })
1073 Transaction.decorate_functions(
1074  decorate_monetary_list_returning_function, 'GetImbalance')
1075 
1076 # Split
1077 Split.add_methods_with_prefix('xaccSplit')
1078 Split.add_method('gncSplitGetGUID', 'GetGUID')
1079 Split.add_method('xaccSplitDestroy', 'Destroy')
1080 
1081 split_dict = {
1082  'GetBook': Book,
1083  'GetAccount': Account,
1084  'GetParent': Transaction,
1085  'Lookup': Split,
1086  'GetOtherSplit': Split,
1087  'GetAmount': GncNumeric,
1088  'GetValue': GncNumeric,
1089  'GetSharePrice': GncNumeric,
1090  'ConvertAmount': GncNumeric,
1091  'GetBaseValue': GncNumeric,
1092  'GetBalance': GncNumeric,
1093  'GetClearedBalance': GncNumeric,
1094  'GetReconciledBalance': GncNumeric,
1095  'VoidFormerAmount': GncNumeric,
1096  'VoidFormerValue': GncNumeric,
1097  'GetNoclosingBalance': GncNumeric,
1098  'GetCapGains': GncNumeric,
1099  'GetGUID': GUID
1100  }
1101 methods_return_instance(Split, split_dict)
1102 
1103 Split.account = property( Split.GetAccount, Split.SetAccount )
1104 Split.parent = property( Split.GetParent, Split.SetParent )
1105 
1106 # Account
1107 Account.add_methods_with_prefix('xaccAccount')
1108 Account.add_methods_with_prefix('gnc_account_')
1109 Account.add_method('gncAccountGetGUID', 'GetGUID')
1110 Account.add_method('xaccAccountGetPlaceholder', 'GetPlaceholder')
1111 
1112 account_dict = {
1113  'get_book' : Book,
1114  'Lookup' : Account,
1115  'get_parent' : Account,
1116  'get_root' : Account,
1117  'nth_child' : Account,
1118  'lookup_by_code' : Account,
1119  'lookup_by_name' : Account,
1120  'lookup_by_full_name' : Account,
1121  'FindTransByDesc' : Transaction,
1122  'FindSplitByDesc' : Split,
1123  'GetBalance' : GncNumeric,
1124  'GetClearedBalance' : GncNumeric,
1125  'GetReconciledBalance' : GncNumeric,
1126  'GetPresentBalance' : GncNumeric,
1127  'GetProjectedMinimumBalance' : GncNumeric,
1128  'GetBalanceAsOfDate' : GncNumeric,
1129  'ConvertBalanceToCurrency' : GncNumeric,
1130  'ConvertBalanceToCurrencyAsOfDate' : GncNumeric,
1131  'GetBalanceInCurrency' : GncNumeric,
1132  'GetClearedBalanceInCurrency' : GncNumeric,
1133  'GetReconciledBalanceInCurrency' : GncNumeric,
1134  'GetPresentBalanceInCurrency' : GncNumeric,
1135  'GetProjectedMinimumBalanceInCurrency' : GncNumeric,
1136  'GetBalanceAsOfDateInCurrency' : GncNumeric,
1137  'GetBalanceChangeForPeriod' : GncNumeric,
1138  'GetCommodity' : GncCommodity,
1139  'get_currency_or_parent' : GncCommodity,
1140  'GetGUID': GUID
1141  }
1142 methods_return_instance(Account, account_dict)
1143 methods_return_instance_lists(
1144  Account, { 'GetSplitList': Split,
1145  'GetLotList': GncLot,
1146  'get_children': Account,
1147  'get_children_sorted': Account,
1148  'get_descendants': Account,
1149  'get_descendants_sorted': Account
1150  })
1151 Account.name = property( Account.GetName, Account.SetName )
1152 
1153 #GUID
1154 GUID.add_methods_with_prefix('guid_')
1155 GUID.add_method('xaccAccountLookup', 'AccountLookup')
1156 GUID.add_method('xaccTransLookup', 'TransLookup')
1157 GUID.add_method('xaccSplitLookup', 'SplitLookup')
1158 
1159 
1160 GUID.add_method('guid_to_string', 'to_string')
1161 #GUID.add_method('string_to_guid', 'string_to_guid')
1162 
1163 guid_dict = {
1164  'copy' : GUID,
1165  'TransLookup': Transaction,
1166  'AccountLookup': Account,
1167  'SplitLookup': Split
1168  }
1169 methods_return_instance(GUID, guid_dict)
1170 
1171 #GUIDString
1173  pass
1174 
1175 GUIDString.add_constructor_and_methods_with_prefix('string_', 'to_guid')
1176 
1177 #Query
1178 from gnucash.gnucash_core_c import \
1179  QOF_QUERY_AND, \
1180  QOF_QUERY_OR, \
1181  QOF_QUERY_NAND, \
1182  QOF_QUERY_NOR, \
1183  QOF_QUERY_XOR
1184 
1185 from gnucash.gnucash_core_c import \
1186  QOF_STRING_MATCH_NORMAL, \
1187  QOF_STRING_MATCH_CASEINSENSITIVE
1188 
1189 from gnucash.gnucash_core_c import \
1190  QOF_COMPARE_LT, \
1191  QOF_COMPARE_LTE, \
1192  QOF_COMPARE_EQUAL, \
1193  QOF_COMPARE_GT, \
1194  QOF_COMPARE_GTE, \
1195  QOF_COMPARE_NEQ, \
1196  QOF_COMPARE_CONTAINS, \
1197  QOF_COMPARE_NCONTAINS
1198 
1199 from gnucash.gnucash_core_c import \
1200  QOF_DATE_MATCH_NORMAL, \
1201  QOF_DATE_MATCH_DAY
1202 
1203 from gnucash.gnucash_core_c import \
1204  QOF_NUMERIC_MATCH_DEBIT, \
1205  QOF_NUMERIC_MATCH_CREDIT, \
1206  QOF_NUMERIC_MATCH_ANY
1207 
1208 from gnucash.gnucash_core_c import \
1209  QOF_GUID_MATCH_ANY, \
1210  QOF_GUID_MATCH_NONE, \
1211  QOF_GUID_MATCH_NULL, \
1212  QOF_GUID_MATCH_ALL, \
1213  QOF_GUID_MATCH_LIST_ANY
1214 
1215 from gnucash.gnucash_core_c import \
1216  QOF_CHAR_MATCH_ANY, \
1217  QOF_CHAR_MATCH_NONE
1218 
1219 from gnucash.gnucash_core_c import \
1220  INVOICE_TYPE
1221 
1222 from gnucash.gnucash_core_c import \
1223  INVOICE_IS_PAID
1224 
1226 
1227  def search_for(self, obj_type):
1228  """Set search_for to obj_type
1229 
1230  calls qof_query_search_for. Buffers search string for queries lifetime.
1231  @see https://bugs.gnucash.org/show_bug.cgi?id=796137"""
1232  self.__search_for_buf = obj_type
1233  self._search_for(self.__search_for_buf)
1234 
1235 Query.add_constructor_and_methods_with_prefix('qof_query_', 'create', exclude=["qof_query_search_for"])
1236 
1237 Query.add_method('qof_query_set_book', 'set_book')
1238 Query.add_method('qof_query_search_for', '_search_for')
1239 Query.add_method('qof_query_run', 'run')
1240 Query.add_method('qof_query_add_term', 'add_term')
1241 Query.add_method('qof_query_add_boolean_match', 'add_boolean_match')
1242 Query.add_method('qof_query_add_guid_list_match', 'add_guid_list_match')
1243 Query.add_method('qof_query_add_guid_match', 'add_guid_match')
1244 Query.add_method('qof_query_destroy', 'destroy')
1245 
1247  pass
1248 
1249 QueryStringPredicate.add_constructor_and_methods_with_prefix(
1250  'qof_query_','string_predicate')
1251 
1253  pass
1254 
1255 QueryBooleanPredicate.add_constructor_and_methods_with_prefix(
1256  'qof_query_', 'boolean_predicate')
1257 
1259  pass
1260 
1261 QueryInt32Predicate.add_constructor_and_methods_with_prefix(
1262  'qof_query_', 'int32_predicate')
1263 
1265  pass
1266 
1267 QueryDatePredicate.add_constructor_and_methods_with_prefix(
1268  'qof_query_', 'date_predicate', exclude=["qof_query_date_predicate_get_date"])
1269 QueryDatePredicate.add_method('qof_query_date_predicate_get_date', 'get_date')
1270 
1272  pass
1273 
1274 QueryGuidPredicate.add_constructor_and_methods_with_prefix(
1275  'qof_query_', 'guid_predicate')
1276 
1278  pass
1279 
1280 QueryNumericPredicate.add_constructor_and_methods_with_prefix(
1281  'qof_query_', 'numeric_predicate')
gnc_numeric double_to_gnc_numeric(double n, gint64 denom, gint how)
Convert a floating-point number to a gnc_numeric.
def search_for(self, obj_type)
def raise_backend_errors_after_call(function, args, kwargs)
gchar * gnc_numeric_to_string(gnc_numeric n)
Convert to string.
def BillNextID(self, vendor)
def raise_backend_errors(self, called_function="qof_session function")
def do_lookup_create_oo_instance(self, lookup_function, cls, args)
def InvoiceNextID(self, customer)
def __init__(self, args, kargs)
gnc_numeric gnc_numeric_from_string(const gchar *str)
Read a gnc_numeric from str, skipping any leading whitespace.
def __init__(self, book_uri=None, mode=None, instance=None, book=None)
A convenient constructor that allows you to specify a book URI, begin the session, and load the book.
GNCNumericErrorCode gnc_numeric_check(gnc_numeric a)
Check for error signal in value.