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