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