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  'convert_balance_latest_price' : GncNumeric,
774  'convert_balance_nearest_price_t64' : GncNumeric,
775  }
776 methods_return_instance(GncPriceDB,PriceDB_dict)
777 GncPriceDB.get_prices = method_function_returns_instance_list(
778  GncPriceDB.get_prices, GncPrice )
779 
781 
783  """A CommodityTable provides a way to store and lookup commodities.
784  Commodities are primarily currencies, but other tradable things such as
785  stocks, mutual funds, and material substances are possible.
786 
787  Users of this library should not create their own CommodityTable, instead
788  the get_table method from the Book class should be used.
789 
790  This table is automatically populated with the GnuCash default commodity's
791  which includes most of the world's currencies.
792  """
793 
794  def _get_namespaces_py(self):
795  return [ns.get_name() for ns in self.get_namespaces_list()]
796 
798  pass
799 
800 class GncLot(GnuCashCoreClass):
801  def GetInvoiceFromLot(self):
802  from gnucash.gnucash_business import Invoice
803  return self.do_lookup_create_oo_instance(
804  gncInvoiceGetInvoiceFromLot, Invoice )
805 
807  """A GnuCash Transaction
808 
809  Consists of at least one (generally two) splits to represent a transaction
810  between two accounts.
811 
812 
813  Has a GetImbalance() method that returns a list of all the imbalanced
814  currencies. Each list item is a two element tuple, the first element is
815  the imbalanced commodity, the second element is the value.
816 
817  Warning, the commodity.get_instance() value can be None when there
818  is no currency set for the transaction.
819  """
820  _new_instance = 'xaccMallocTransaction'
821  def GetNthSplit(self, n):
822  return self.GetSplitList().pop(n)
823 
824  def GetInvoiceFromTxn(self):
825  from gnucash.gnucash_business import Transaction
826  return self.do_lookup_create_oo_instance(
827  gncInvoiceGetInvoiceFromTxn, Transaction )
828 
829  def __eq__(self, other):
830  return self.Equal(other, True, False, False, False)
831 
832 def decorate_monetary_list_returning_function(orig_function):
833  def new_function(self, *args):
834  """decorate function that returns list of gnc_monetary to return tuples of GncCommodity and GncNumeric
835 
836  Args:
837  *args: Variable length argument list. Will get passed to orig_function
838 
839  Returns:
840  array of tuples: (GncCommodity, GncNumeric)
841 
842  ToDo:
843  Maybe this function should better reside in module function_class (?)"""
844  # warning, item.commodity has been shown to be None
845  # when the transaction doesn't have a currency
846  return [(GncCommodity(instance=item.commodity),
847  GncNumeric(instance=item.value))
848  for item in orig_function(self, *args) ]
849  return new_function
850 
852  """A GnuCash Split
853 
854  The most basic representation of a movement of currency from one account to
855  another.
856  """
857  _new_instance = 'xaccMallocSplit'
858 
859  def __eq__(self, other):
860  return self.Equal(other, True, False, False)
861 
863  """A GnuCash Account.
864 
865  A fundamental entity in accounting, an Account provides representation
866  for a financial object, such as a ACCT_TYPE_BANK account, an
867  ACCT_TYPE_ASSET (like a building),
868  a ACCT_TYPE_LIABILITY (such as a bank loan), a summary of some type of
869  ACCT_TYPE_EXPENSE, or a summary of some source of ACCT_TYPE_INCOME .
870 
871  The words in upper case are the constants that GnuCash and this library uses
872  to describe account type. Here is the full list:
873  ACCT_TYPE_ASSET, ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CHECKING, \
874  ACCT_TYPE_CREDIT, ACCT_TYPE_EQUITY, ACCT_TYPE_EXPENSE, ACCT_TYPE_INCOME, \
875  ACCT_TYPE_LIABILITY, ACCT_TYPE_MUTUAL, ACCT_TYPE_PAYABLE, \
876  ACCT_TYPE_RECEIVABLE, ACCT_TYPE_STOCK, ACCT_TYPE_ROOT, ACCT_TYPE_TRADING
877 
878  These are not strings, they are attributes you can import from this
879  module
880  """
881  _new_instance = 'xaccMallocAccount'
882 
884  _new_instance = 'guid_new_return'
885 
886 # Session
887 Session.add_constructor_and_methods_with_prefix('qof_session_', 'new')
888 
889 def one_arg_default_none(function):
890  return default_arguments_decorator(function, None, None)
891 Session.decorate_functions(one_arg_default_none, "load", "save")
892 
893 Session.decorate_functions( Session.raise_backend_errors_after_call,
894  "begin", "load", "save", "end")
895 Session.decorate_method(default_arguments_decorator, "begin", None, mode=SessionOpenMode.SESSION_NORMAL_OPEN)
896 Session.decorate_functions(deprecated_args_session_begin, "begin")
897 
898 Session.get_book = method_function_returns_instance(
899  Session.get_book, Book )
900 
901 Session.book = property( Session.get_book )
902 
903 # import all of the session backend error codes into this module
904 this_module_dict = globals()
905 for error_name, error_value, error_name_after_prefix in \
906  extract_attributes_with_prefix(gnucash_core_c, 'ERR_'):
907  this_module_dict[ error_name ] = error_value
908 
909 #backend error codes used for reverse lookup
910 backend_error_dict = {}
911 for error_name, error_value, error_name_after_prefix in \
912  extract_attributes_with_prefix(gnucash_core_c, 'ERR_'):
913  backend_error_dict[ error_value ] = error_name
914 
915 # GncNumeric denominator computation schemes
916 # Used for the denom argument in arithmetic functions like GncNumeric.add
917 from gnucash.gnucash_core_c import GNC_DENOM_AUTO
918 
919 # GncNumeric rounding instructions
920 # used for the how argument in arithmetic functions like GncNumeric.add
921 from gnucash.gnucash_core_c import \
922  GNC_HOW_RND_FLOOR, GNC_HOW_RND_CEIL, GNC_HOW_RND_TRUNC, \
923  GNC_HOW_RND_PROMOTE, GNC_HOW_RND_ROUND_HALF_DOWN, \
924  GNC_HOW_RND_ROUND_HALF_UP, GNC_HOW_RND_ROUND, GNC_HOW_RND_NEVER
925 
926 # GncNumeric denominator types
927 # used for the how argument in arithmetic functions like GncNumeric.add
928 from gnucash.gnucash_core_c import \
929  GNC_HOW_DENOM_EXACT, GNC_HOW_DENOM_REDUCE, GNC_HOW_DENOM_LCD, \
930  GNC_HOW_DENOM_FIXED, GNC_HOW_DENOM_SIGFIG
931 
932 # import account types
933 from gnucash.gnucash_core_c import \
934  ACCT_TYPE_ASSET, ACCT_TYPE_BANK, ACCT_TYPE_CASH, ACCT_TYPE_CHECKING, \
935  ACCT_TYPE_CREDIT, ACCT_TYPE_EQUITY, ACCT_TYPE_EXPENSE, ACCT_TYPE_INCOME, \
936  ACCT_TYPE_LIABILITY, ACCT_TYPE_MUTUAL, ACCT_TYPE_PAYABLE, \
937  ACCT_TYPE_RECEIVABLE, ACCT_TYPE_STOCK, ACCT_TYPE_ROOT, ACCT_TYPE_TRADING
938 
939 #Book
940 Book.add_constructor_and_methods_with_prefix('qof_book_', 'new')
941 Book.add_method('gnc_book_get_root_account', 'get_root_account')
942 Book.add_method('gnc_book_set_root_account', 'set_root_account')
943 Book.add_method('gnc_commodity_table_get_table', 'get_table')
944 Book.add_method('gnc_pricedb_get_db', 'get_price_db')
945 Book.add_method('qof_book_increment_and_format_counter', 'increment_and_format_counter')
946 
947 #Functions that return Account
948 Book.get_root_account = method_function_returns_instance(
949  Book.get_root_account, Account )
950 #Functions that return GncCommodityTable
951 Book.get_table = method_function_returns_instance(
952  Book.get_table, GncCommodityTable )
953 #Functions that return GNCPriceDB
954 Book.get_price_db = method_function_returns_instance(
955  Book.get_price_db, GncPriceDB)
956 
957 # GncNumeric
958 GncNumeric.add_constructor_and_methods_with_prefix('gnc_numeric_', 'create')
959 
960 gncnumeric_dict = {
961  'same' : GncNumeric,
962  'add' : GncNumeric,
963  'sub' : GncNumeric,
964  'mul' : GncNumeric,
965  'div' : GncNumeric,
966  'neg' : GncNumeric,
967  'abs' : GncNumeric,
968  'add_fixed' : GncNumeric,
969  'sub_fixed' : GncNumeric,
970  'convert' : GncNumeric,
971  'reduce' : GncNumeric,
972  'invert' : GncNumeric
973  }
974 methods_return_instance(GncNumeric, gncnumeric_dict)
975 
976 # GncCommodity
977 GncCommodity.add_constructor_and_methods_with_prefix('gnc_commodity_', 'new')
978 #Functions that return GncCommodity
979 GncCommodity.clone = method_function_returns_instance(
980  GncCommodity.clone, GncCommodity )
981 
982 # GncCommodityTable
983 GncCommodityTable.add_methods_with_prefix('gnc_commodity_table_')
984 commoditytable_dict = {
985  'lookup' : GncCommodity,
986  'lookup_unique' : GncCommodity,
987  'find_full' : GncCommodity,
988  'insert' : GncCommodity,
989  'add_namespace': GncCommodityNamespace,
990  'find_namespace': GncCommodityNamespace,
991  }
992 methods_return_instance(GncCommodityTable, commoditytable_dict)
993 
994 methods_return_instance_lists(
995  GncCommodityTable, { 'get_namespaces_list': GncCommodityNamespace,
996  'get_commodities': GncCommodity,
997  'get_quotable_commodities': GncCommodity,
998 
999  } )
1000 setattr(GncCommodityTable, 'get_namespaces', getattr(GncCommodityTable, '_get_namespaces_py'))
1001 
1002 # GncCommodityNamespace
1003 GncCommodityNamespace.add_methods_with_prefix('gnc_commodity_namespace_')
1004 GncCommodityNamespace.get_commodity_list = \
1005  method_function_returns_instance_list(
1006  GncCommodityNamespace.get_commodity_list, GncCommodity )
1007 
1008 # GncLot
1009 GncLot.add_constructor_and_methods_with_prefix('gnc_lot_', 'new')
1010 
1011 gnclot_dict = {
1012  'get_account' : Account,
1013  'get_book' : Book,
1014  'get_earliest_split' : Split,
1015  'get_latest_split' : Split,
1016  'get_balance' : GncNumeric,
1017  'lookup' : GncLot,
1018  'make_default' : GncLot
1019  }
1020 methods_return_instance(GncLot, gnclot_dict)
1021 
1022 # Transaction
1023 Transaction.add_methods_with_prefix('xaccTrans')
1024 Transaction.add_method('gncTransGetGUID', 'GetGUID')
1025 
1026 Transaction.add_method('xaccTransGetDescription', 'GetDescription')
1027 Transaction.add_method('xaccTransDestroy', 'Destroy')
1028 
1029 trans_dict = {
1030  'GetSplit': Split,
1031  'FindSplitByAccount': Split,
1032  'Clone': Transaction,
1033  'Reverse': Transaction,
1034  'GetReversedBy': Transaction,
1035  'GetImbalanceValue': GncNumeric,
1036  'GetAccountValue': GncNumeric,
1037  'GetAccountAmount': GncNumeric,
1038  'GetAccountConvRate': GncNumeric,
1039  'GetAccountBalance': GncNumeric,
1040  'GetCurrency': GncCommodity,
1041  'GetGUID': GUID
1042  }
1043 
1044 methods_return_instance(Transaction, trans_dict)
1045 methods_return_instance_lists(
1046  Transaction, { 'GetSplitList': Split,
1047  })
1048 Transaction.decorate_functions(
1049  decorate_monetary_list_returning_function, 'GetImbalance')
1050 
1051 # Split
1052 Split.add_methods_with_prefix('xaccSplit')
1053 Split.add_method('gncSplitGetGUID', 'GetGUID')
1054 Split.add_method('xaccSplitDestroy', 'Destroy')
1055 
1056 split_dict = {
1057  'GetBook': Book,
1058  'GetAccount': Account,
1059  'GetParent': Transaction,
1060  'Lookup': Split,
1061  'GetOtherSplit': Split,
1062  'GetAmount': GncNumeric,
1063  'GetValue': GncNumeric,
1064  'GetSharePrice': GncNumeric,
1065  'ConvertAmount': GncNumeric,
1066  'GetBaseValue': GncNumeric,
1067  'GetBalance': GncNumeric,
1068  'GetClearedBalance': GncNumeric,
1069  'GetReconciledBalance': GncNumeric,
1070  'VoidFormerAmount': GncNumeric,
1071  'VoidFormerValue': GncNumeric,
1072  'GetGUID': GUID
1073  }
1074 methods_return_instance(Split, split_dict)
1075 
1076 Split.account = property( Split.GetAccount, Split.SetAccount )
1077 Split.parent = property( Split.GetParent, Split.SetParent )
1078 
1079 # Account
1080 Account.add_methods_with_prefix('xaccAccount')
1081 Account.add_methods_with_prefix('gnc_account_')
1082 Account.add_method('gncAccountGetGUID', 'GetGUID')
1083 Account.add_method('xaccAccountGetPlaceholder', 'GetPlaceholder')
1084 
1085 account_dict = {
1086  'get_book' : Book,
1087  'Lookup' : Account,
1088  'get_parent' : Account,
1089  'get_root' : Account,
1090  'nth_child' : Account,
1091  'lookup_by_code' : Account,
1092  'lookup_by_name' : Account,
1093  'lookup_by_full_name' : Account,
1094  'FindTransByDesc' : Transaction,
1095  'FindSplitByDesc' : Split,
1096  'GetBalance' : GncNumeric,
1097  'GetClearedBalance' : GncNumeric,
1098  'GetReconciledBalance' : GncNumeric,
1099  'GetPresentBalance' : GncNumeric,
1100  'GetProjectedMinimumBalance' : GncNumeric,
1101  'GetBalanceAsOfDate' : GncNumeric,
1102  'ConvertBalanceToCurrency' : GncNumeric,
1103  'ConvertBalanceToCurrencyAsOfDate' : GncNumeric,
1104  'GetBalanceInCurrency' : GncNumeric,
1105  'GetClearedBalanceInCurrency' : GncNumeric,
1106  'GetReconciledBalanceInCurrency' : GncNumeric,
1107  'GetPresentBalanceInCurrency' : GncNumeric,
1108  'GetProjectedMinimumBalanceInCurrency' : GncNumeric,
1109  'GetBalanceAsOfDateInCurrency' : GncNumeric,
1110  'GetBalanceChangeForPeriod' : GncNumeric,
1111  'GetCommodity' : GncCommodity,
1112  'GetGUID': GUID
1113  }
1114 methods_return_instance(Account, account_dict)
1115 methods_return_instance_lists(
1116  Account, { 'GetSplitList': Split,
1117  'get_children': Account,
1118  'get_children_sorted': Account,
1119  'get_descendants': Account,
1120  'get_descendants_sorted': Account
1121  })
1122 Account.name = property( Account.GetName, Account.SetName )
1123 
1124 #GUID
1125 GUID.add_methods_with_prefix('guid_')
1126 GUID.add_method('xaccAccountLookup', 'AccountLookup')
1127 GUID.add_method('xaccTransLookup', 'TransLookup')
1128 GUID.add_method('xaccSplitLookup', 'SplitLookup')
1129 
1130 
1131 GUID.add_method('guid_to_string', 'to_string')
1132 #GUID.add_method('string_to_guid', 'string_to_guid')
1133 
1134 guid_dict = {
1135  'copy' : GUID,
1136  'TransLookup': Transaction,
1137  'AccountLookup': Account,
1138  'SplitLookup': Split
1139  }
1140 methods_return_instance(GUID, guid_dict)
1141 
1142 #GUIDString
1144  pass
1145 
1146 GUIDString.add_constructor_and_methods_with_prefix('string_', 'to_guid')
1147 
1148 #Query
1149 from gnucash.gnucash_core_c import \
1150  QOF_QUERY_AND, \
1151  QOF_QUERY_OR, \
1152  QOF_QUERY_NAND, \
1153  QOF_QUERY_NOR, \
1154  QOF_QUERY_XOR
1155 
1156 from gnucash.gnucash_core_c import \
1157  QOF_STRING_MATCH_NORMAL, \
1158  QOF_STRING_MATCH_CASEINSENSITIVE
1159 
1160 from gnucash.gnucash_core_c import \
1161  QOF_COMPARE_LT, \
1162  QOF_COMPARE_LTE, \
1163  QOF_COMPARE_EQUAL, \
1164  QOF_COMPARE_GT, \
1165  QOF_COMPARE_GTE, \
1166  QOF_COMPARE_NEQ, \
1167  QOF_COMPARE_CONTAINS, \
1168  QOF_COMPARE_NCONTAINS
1169 
1170 from gnucash.gnucash_core_c import \
1171  QOF_DATE_MATCH_NORMAL, \
1172  QOF_DATE_MATCH_DAY
1173 
1174 from gnucash.gnucash_core_c import \
1175  QOF_NUMERIC_MATCH_DEBIT, \
1176  QOF_NUMERIC_MATCH_CREDIT, \
1177  QOF_NUMERIC_MATCH_ANY
1178 
1179 from gnucash.gnucash_core_c import \
1180  QOF_GUID_MATCH_ANY, \
1181  QOF_GUID_MATCH_NONE, \
1182  QOF_GUID_MATCH_NULL, \
1183  QOF_GUID_MATCH_ALL, \
1184  QOF_GUID_MATCH_LIST_ANY
1185 
1186 from gnucash.gnucash_core_c import \
1187  QOF_CHAR_MATCH_ANY, \
1188  QOF_CHAR_MATCH_NONE
1189 
1190 from gnucash.gnucash_core_c import \
1191  INVOICE_TYPE
1192 
1193 from gnucash.gnucash_core_c import \
1194  INVOICE_IS_PAID
1195 
1197 
1198  def search_for(self, obj_type):
1199  """Set search_for to obj_type
1200 
1201  calls qof_query_search_for. Buffers search string for queries lifetime.
1202  @see https://bugs.gnucash.org/show_bug.cgi?id=796137"""
1203  self.__search_for_buf = obj_type
1204  self._search_for(self.__search_for_buf)
1205 
1206 Query.add_constructor_and_methods_with_prefix('qof_query_', 'create', exclude=["qof_query_search_for"])
1207 
1208 Query.add_method('qof_query_set_book', 'set_book')
1209 Query.add_method('qof_query_search_for', '_search_for')
1210 Query.add_method('qof_query_run', 'run')
1211 Query.add_method('qof_query_add_term', 'add_term')
1212 Query.add_method('qof_query_add_boolean_match', 'add_boolean_match')
1213 Query.add_method('qof_query_add_guid_list_match', 'add_guid_list_match')
1214 Query.add_method('qof_query_add_guid_match', 'add_guid_match')
1215 Query.add_method('qof_query_destroy', 'destroy')
1216 
1218  pass
1219 
1220 QueryStringPredicate.add_constructor_and_methods_with_prefix(
1221  'qof_query_','string_predicate')
1222 
1224  pass
1225 
1226 QueryBooleanPredicate.add_constructor_and_methods_with_prefix(
1227  'qof_query_', 'boolean_predicate')
1228 
1230  pass
1231 
1232 QueryInt32Predicate.add_constructor_and_methods_with_prefix(
1233  'qof_query_', 'int32_predicate')
1234 
1236  pass
1237 
1238 QueryDatePredicate.add_constructor_and_methods_with_prefix(
1239  'qof_query_', 'date_predicate', exclude=["qof_query_date_predicate_get_date"])
1240 QueryDatePredicate.add_method('qof_query_date_predicate_get_date', 'get_date')
1241 
1243  pass
1244 
1245 QueryGuidPredicate.add_constructor_and_methods_with_prefix(
1246  'qof_query_', 'guid_predicate')
1247 
1249  pass
1250 
1251 QueryNumericPredicate.add_constructor_and_methods_with_prefix(
1252  'qof_query_', 'numeric_predicate')
gnc_numeric double_to_gnc_numeric(double in, 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 in)
Check for error signal in value.