def test_tag_split_zero_quantity(self, book_transactions): broker = book_transactions.accounts(name="broker") asset = book_transactions.accounts.get(name="asset") inc = book_transactions.accounts.get(name="inc") currency = book_transactions.default_currency value = Decimal(250) splits = [ Split(asset, value), Split(inc, -value), Split(broker, value=0, quantity=0), # tag split for assigning dividend income to stock ] Transaction(currency, description='Dividend income', splits=splits) book_transactions.validate()
def test_create_cdtytransaction_cdtycurrency(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") s = book_basic.accounts(name="broker") tr = Transaction(currency=s.commodity, description="buy stock", notes=u"on St-Eugène day", post_date=date(2014, 1, 2), enter_date=datetime(2014, 1, 3), splits=[ Split(account=a, value=100, quantity=10, memo=u"mémo asset"), Split(account=s, value=-100, quantity=-10, memo=u"mémo brok"), ]) # raise error as Transaction has a non CURRENCY commodity with pytest.raises(GncValidationError): book_basic.validate()
def test_create_basictransaction_splitfirst(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") e = book_basic.accounts(name="exp") s = Split(account=a, value=Decimal(1)) assert repr(s)
def add_transaction(book, description, amount, debit_acct, credit_acct, enter_datetime): """Add a transaction to an existing book. `amount` should be a float out to 2 decimal places for the value of the transaction. `debit_acct` and `credit_acct` should be the names of the accounts as given by the .fullname method from a GnuCash Account, e.g. book.accounts.get(fullname="Expenses:Food"). `enter_datetime` should be of type datetime.datetime. """ try: credit = get_account(credit_acct, book) debit = get_account(debit_acct, book) if credit and debit: usd = book.currencies(mnemonic='USD') logger.info('Creating a new transaction in the GnuCash book.') transaction = Transaction(currency=usd, description=description, enter_date=enter_datetime, splits=[ Split(value=amount, account=credit), Split(value=-amount, account=debit) ]) logger.debug('Transaction successfully created.') logger.debug('Attempting to save transaction') book.save() logger.info('Successfully saved transaction') return True elif credit and not debit: logger.error( f'The debit account {debit_acct} was not found. Skipping.') return False elif debit and not credit: logger.error( f'The credit account {credit_acct} was not found. Skipping.') return False except GnucashException as gce: logger.error('Failed to add the transaction') logger.error(gce) return False except ValueError as ve: logger.error('Failed to add the transaction with ValueError:') logger.error(ve) return False
def test_create_simplelot_inconsistentaccounts(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") s = book_basic.accounts(name="broker") l = Lot(title=u"test mé", account=a, notes=u"ïlya") # raise valueerror as split account not the same as lot account tr = Transaction(currency=EUR, description="trade stock", notes=u"àçö", post_date=date(2014, 1, 1), enter_date=datetime(2014, 1, 1), splits=[ Split(account=a, value=10, memo=u"mémo asset"), Split(account=s, value=- 10, quantity=-2, memo=u"mémo brok", lot=l), ]) with pytest.raises(ValueError): book_basic.validate()
def test_create_basictransaction_validation_date(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") e = book_basic.accounts(name="exp") splits = [ Split(account=a, value=100, memo=u"mémo asset"), Split(account=e, value=-10, memo=u"mémo exp"), ] with pytest.raises(GncValidationError): tr = Transaction(currency=EUR, description=u"wire from Hélène", notes=u"on St-Eugène day", post_date=datetime(2014, 1, 1), enter_date=datetime(2014, 1, 1), splits=splits) with pytest.raises(GncValidationError): tr = Transaction(currency=EUR, description=u"wire from Hélène", notes=u"on St-Eugène day", post_date=datetime(2014, 1, 1), enter_date=time(10, 59, 00), splits=splits) with pytest.raises(GncValidationError): tr = Transaction(currency=EUR, description=u"wire from Hélène", notes=u"on St-Eugène day", post_date=date(2014, 1, 1), enter_date=date(2014, 1, 1), splits=splits) tr = Transaction(currency=EUR, description=u"wire from Hélène", notes=u"on St-Eugène day", post_date=None, enter_date=None, splits=splits) with pytest.raises(GncImbalanceError): book_basic.flush() book_basic.validate()
def test_create_simpletlot_initialsplits(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") s = book_basic.accounts(name="broker") sp = [] for i, am in enumerate([45, -35, -20]): tr = Transaction(currency=EUR, description="trade stock", notes=u"àçö", post_date=date(2014, 1, 1 + i), enter_date=datetime(2014, 1, 1 + i), splits=[ Split(account=a, value=am * 10, memo=u"mémo asset"), Split(account=s, value=-am * 10, quantity=-am, memo=u"mémo brok"), ]) sp.append(tr.splits(account=s)) l = Lot(title=u"test mé", account=s, notes=u"ïlya", splits=sp) book_basic.flush()
def test_create_cdtytransaction_tradingaccount(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") s = book_basic.accounts(name="broker") book_basic.use_trading_accounts = True tr = Transaction(currency=EUR, description="buy stock", notes=u"on St-Eugène day", post_date=date(2014, 1, 2), enter_date=datetime(2014, 1, 3), splits=[ Split(account=a, value=100, memo=u"mémo asset"), Split(account=s, value=-100, quantity=-15, memo=u"mémo brok"), ]) book_basic.validate() assert "{}".format(tr) == "Transaction<[EUR] 'buy stock' on 2014-01-02>" assert "{}".format(s) == "Account<asset:broker[ïoà]>" assert "{}".format(tr.splits(account=s)) == "Split<Account<asset:broker[ïoà]> -100 EUR [-15 ïoà]>" assert "{}".format(tr.splits(account=a)) == "Split<Account<asset[EUR]> 100 EUR>" # check sum of quantities are all balanced per commodity as values are d = defaultdict(lambda: Decimal(0)) for sp in tr.splits: assert sp.quantity == sp.value or sp.account != a d[sp.account.commodity] += sp.quantity d["cur"] += sp.value assert d["cur"] == 0 assert all([v == Decimal(0) for k, v in d.items() if k != "cur"]) # change existing quantity sp = tr.splits(memo=u"mémo brok") sp.quantity += 1 book_basic.validate() # check sum of quantities are all balanced per commodity as values are d = defaultdict(lambda: Decimal(0)) for sp in tr.splits: assert sp.quantity == sp.value or sp.account != a d[sp.account.commodity] += sp.quantity d["cur"] += sp.value assert d["cur"] == 0 assert all([v == Decimal(0) for k, v in d.items() if k != "cur"])
def setUp(self): self.date = datetime(2018, 7, 30, 0, 00).date() self.price = 1000 splits = [ Split(account=None, value=self.price, transaction=Transaction(currency=None, post_date=self.date), reconcile_state='n') ] self.reconciler = Reconciler(splits)
def test_create_basictransaction_neutraltime(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") e = book_basic.accounts(name="exp") tr = Transaction(currency=EUR, description=u"wire from Hélène", notes=u"on St-Eugène day", post_date=date(2014, 1, 1), splits=[ Split(account=a, value=100, memo=u"mémo asset"), Split(account=e, value=-100, memo=u"mémo exp"), ]) assert isinstance(tr.post_date, date) book_basic.flush() book_basic.validate() assert isinstance(tr.post_date, date)
def test_create_closedlot_addsplits(self, book_basic): EUR = book_basic.commodities(namespace="CURRENCY") racc = book_basic.root_account a = book_basic.accounts(name="asset") s = book_basic.accounts(name="broker") l = Lot(title="test mé", account=s, notes="ïlya") l.is_closed = 1 # raise valueerror as lot is closed with pytest.raises(ValueError): tr = Transaction( currency=EUR, description="trade stock", notes="àçö", post_date=date(2014, 1, 1), enter_date=datetime(2014, 1, 1), splits=[ Split(account=a, value=10, memo="mémo asset"), Split(account=s, value=-10, quantity=-2, memo="mémo brok", lot=l), ], )
def add_transaction(book, item, currency): logging.info('Adding transaction for account "%s" (%s %s)..', item.account, item.split_amount, currency.mnemonic) root = book.root_account acc = lookup_account(root, item.account) acc2 = lookup_account(root, item.split_category) amount = Decimal(item.split_amount.replace(',', '.')) _ = Transaction( currency=currency, description=item.memo, enter_date=datetime.datetime.now(), post_date=item.date, splits=[ Split(account=acc, value=amount), Split(account=acc2, value=- amount) ] ) book.flush()
def test_tag_split_zero_quantity_with_value(self, book_transactions): broker = book_transactions.accounts(name="broker") inc = book_transactions.accounts.get(name="inc") value = Decimal(250) # Transaction recording capital gains. splits = [ Split(broker, value, quantity=0), Split(inc, -value), ] Transaction(inc.commodity, description="Capital gains", splits=splits) book_transactions.validate() # Transaction recording capital loss. splits = [ Split(broker, -value, quantity=0), Split(inc, value), ] Transaction(inc.commodity, description="Capital loss", splits=splits) book_transactions.validate() # Do the same tests with a -0.0 quantity. This Decimal has is_signed=True. mzero = Decimal("-0.00") # Transaction recording capital gains. splits = [ Split(broker, value, quantity=mzero), Split(inc, -value), ] Transaction(inc.commodity, description="Capital gains", splits=splits) book_transactions.validate() # Transaction recording capital loss. splits = [ Split(broker, -value, quantity=mzero), Split(inc, value), ] Transaction(inc.commodity, description="Capital loss", splits=splits) book_transactions.validate()
def test_delete__replace_existing_split(self, book_transactions): s = len(book_transactions.splits) transaction = book_transactions.transactions(description="my revenue") split = transaction.splits(value=1000) assert len(transaction.splits) == 2 splits = [ Split( account=split.account, value=split.value, quantity=split.quantity, ) for split in list(transaction.splits) ] assert len(transaction.splits) == 2 transaction.splits[:] = splits assert split.transaction is None assert len(transaction.splits) == 2 book_transactions.flush() book_transactions.save() ns = len(book_transactions.splits) assert ns == s
def book_transactions(request): name = request.param if name and database_exists(name): drop_database(name) # create new book with create_book(uri_conn=name, currency="EUR", keep_foreign_keys=False) as b: # create some accounts curr = b.default_currency other_curr = b.currencies(mnemonic="USD") cdty = Commodity(namespace=u"BEL20", mnemonic=u"GnuCash Inc.", fullname=u"GnuCash Inc. stock") asset = Account(name="asset", type="ASSET", commodity=curr, parent=b.root_account) foreign_asset = Account(name="foreign asset", type="ASSET", commodity=other_curr, parent=b.root_account) stock = Account(name="broker", type="STOCK", commodity=cdty, parent=asset) expense = Account(name="exp", type="EXPENSE", commodity=curr, parent=b.root_account) income = Account(name="inc", type="INCOME", commodity=curr, parent=b.root_account) tr1 = Transaction( post_date=date(2015, 10, 21), description="my revenue", currency=curr, splits=[Split(account=asset, value=(1000, 1)), Split(account=income, value=(-1000, 1))], ) tr2 = Transaction( post_date=date(2015, 10, 25), description="my expense", currency=curr, splits=[ Split(account=asset, value=(-100, 1)), Split(account=expense, value=(20, 1), memo="cost of X"), Split(account=expense, value=(80, 1), memo="cost of Y"), ], ) tr_stock = Transaction( post_date=date(2015, 10, 29), description="my purchase of stock", currency=curr, splits=[ Split(account=asset, value=(-200, 1)), Split(account=expense, value=(15, 1), memo="transaction costs"), Split(account=stock, value=(185, 1), quantity=(6, 1), memo="purchase of stock"), ], ) tr_to_foreign = Transaction( post_date=date(2015, 10, 30), description="transfer to foreign asset", currency=curr, splits=[ Split(account=asset, value=(-200, 1)), Split(account=foreign_asset, value=(200, 1), quantity=(135, 1)), ], ) tr_from_foreign = Transaction( post_date=date(2015, 10, 31), description="transfer from foreign asset", currency=other_curr, splits=[ Split(account=asset, value=(135, 1), quantity=(215, 1)), Split(account=foreign_asset, value=(-135, 1)), ], ) Price(commodity=cdty, currency=other_curr, date=date(2015, 11, 1), value=(123, 100)) Price(commodity=cdty, currency=other_curr, date=date(2015, 11, 4), value=(127, 100)) Price(commodity=cdty, currency=curr, date=date(2015, 11, 2), value=(234, 100)) b.save() yield b if name and database_exists(name): drop_database(name)
name="Savings", type="BANK", commodity=cad) opening_acct = Account(parent=root_acct, name="Opening Balance", type="EQUITY", commodity=cad) num1 = Decimal("4") num2 = Decimal("100") num3 = Decimal("15") # create transaction with core objects in one step trans1 = Transaction(currency=cad, description="Groceries", splits=[ Split(value=num1, account=expenses_acct), Split(value=-num1, account=savings_acct), ]) # create transaction with core object in multiple steps trans2 = Transaction(currency=cad, description="Opening Savings Balance") split3 = Split(value=num2, account=savings_acct, transaction=trans2) split4 = Split(value=-num2, account=opening_acct, transaction=trans2)
with create_book(bookname, currency="EUR", keep_foreign_keys=False, overwrite=True) as b: # create some accounts curr = b.default_currency other_curr = b.currencies(mnemonic="USD") cdty = Commodity(namespace=u"BEL20", mnemonic=u"GnuCash Inc.", fullname=u"GnuCash Inc. stock") asset = Account(name="asset", type="ASSET", commodity=curr, parent=b.root_account) foreign_asset = Account(name="foreign asset", type="ASSET", commodity=other_curr, parent=b.root_account) stock = Account(name="broker", type="STOCK", commodity=cdty, parent=asset) expense = Account(name="exp", type="EXPENSE", commodity=curr, parent=b.root_account) income = Account(name="inc", type="INCOME", commodity=curr, parent=b.root_account) tr1 = Transaction(post_date=datetime(2015, 10, 21), description="my revenue", currency=curr, splits=[ Split(account=asset, value=(1000, 1)), Split(account=income, value=(-1000, 1)), ] ) tr2 = Transaction(post_date=datetime(2015, 10, 25), description="my expense", currency=curr, splits=[ Split(account=asset, value=(-100, 1)), Split(account=expense, value=(20, 1), memo="cost of X"), Split(account=expense, value=(80, 1), memo="cost of Y"), ] ) tr_stock = Transaction(post_date=datetime(2015, 10, 29), description="my purchase of stock", currency=curr,
# retrieve the currency from the book USD = mybook.currencies(mnemonic="USD") # define the amount as Decimal amount = Decimal("25.35") # retrieve accounts to_account = mybook.accounts(fullname="Expenses:Some Expense Account") from_account = mybook.accounts(fullname="Assets:Current Assets:Checking") # create the transaction with its two splits Transaction( post_date=today, enter_date=today, currency=USD, description="Transaction Description!", splits=[ Split(account=to_account, value=amount, memo="Split Memo!"), Split(account=from_account, value=-amount, memo="Other Split Memo!"), ] ) # save the book mybook.save() from piecash import ledger # check the book by exporting to ledger format with open_book("../gnucash_books/simple_book_transaction_creation.gnucash", open_if_lock=True) as mybook: print(ledger(mybook))
open_if_lock=True) as mybook: # iterate on all the transactions in the book for transaction in mybook.transactions: # add some extra text to the transaction description transaction.description = ( transaction.description + " (some extra info added to the description)") # iterate over all the splits of the transaction # as we will modify the transaction splits in the loop, # we need to use list(...) to take a copy of the splits at the start of the loop for split in list(transaction.splits): # create the new split (here a copy of the each existing split # in the transaction with value/quantity divided by 10) new_split = Split( account=split.account, value=split.value / 10, quantity=split.quantity / 10, memo="my new split", transaction= transaction, # attach the split to the current transaction ) # register the changes (but not save) mybook.flush() # print the book in ledger format to view the changes print(ledger(mybook)) # save the book # this will raise an error as readonly=True (change to readonly=False to successfully save the book) mybook.save()
# number of transactions T = 100 # create accounts accounts = [ Account("account {}".format(i), "ASSET", eur, parent=ra) for i in range(N) ] # create transactions for i, v in enumerate(random.randrange(10) for j in range(T)): tx = Transaction( eur, "transaction {}".format(i), ) Split(accounts[random.randrange(N)], value=v, transaction=tx) Split(accounts[random.randrange(N)], value=-v, transaction=tx) s.save() # select two accounts acc = accounts[0] tacc = accounts[1] # move all splits from account acc to account tacc for spl in list(acc.splits): spl.account = tacc s.save() # check no more splits in account acc assert len(acc.splits) == 0 # try to change a split account to an account that is a placeholder
splits = [] if (transaction['transferAccount'] == 'Expenses:Bizspace Rent:F6' and (-1 * transaction['amount']) > (f6Rent + g456Rent)): # pre slipt the rent # prep rent ammounts f6Amount = Decimal(f6Rent) / 100 g456Amount = Decimal(g456Rent) / 100 # calculate electric amount electricAmount = Decimal(transaction['amount'] + f6Rent + g456Rent) / 100 splits = [ Split(account=tsbAccount, value=amount), Split(account=transferAccount, value=f6Amount), #F6 Split(account=g456Account, value=g456Amount), Split(account=electricAccont, value=-1 * electricAmount) ] elif (transaction['transferAccount'] == 'Liabilities:Membership Loan Payable'): # per split loan repayments # 20.83 3.34 24.17 # 83.33 13.34 96.67 # 41.67 6.66 48.33 # 104.17 16.66 120.83 payableAmount = -1 * amount intrestAmount = Decimal(0) if (transaction['amount'] == -2417): payableAmount = Decimal(2083) / 100
def add_transaction(t): settings_file = os.environ['HOME'] + "/gnucash/settings.yaml" with open(settings_file) as ymlfile: settings = yaml.load(ymlfile) book_path = settings['location'] + settings['gnucash'] log_file = settings['location'] + settings['log'] # check for existance of to_account and from_account book = piecash.open_book(book_path) to_account_found = False from_account_found = False for a in book.accounts: if a.fullname == t.account: from_account_found = True elif a.fullname == t.expense: to_account_found = True success = True expense_account_created = False try: # income - allow for not found accounts to instead go to Imbalance account if t.income: if not to_account_found: t.account = 'Imbalance' if not from_account_found: t.expense = 'Imbalance' # expense - allow creation of expense accounts ONLY # - allow not found "from" accounts to instead go to Imbalance account else: # add missing expense account if not to_account_found: with open_book(book_path, open_if_lock=True, readonly=False) as book: acc = book.root_account for subacc in book.root_account.children: if subacc.name == 'Expenses': acc = subacc break # could change this and loop to support mutli-level expense account creation #t.expense = 'Expense:' + t.expense.split(':')[-1] a = Account( parent=acc, name=t.expense.split(':')[-1], type="EXPENSE", description='Automatically Added from SMS transaction.', commodity=book.commodities.get(mnemonic="USD")) book.save() to_account_found = True expense_account_created = True if not from_account_found: t.account = "Imbalance" # reopen the book and add a transaction # this must be a sqlite3 file with open_book(book_path, open_if_lock=True, readonly=False) as mybook: today = datetime.now() today = today.replace(microsecond=0) # retrieve the currency from the book USD = mybook.currencies(mnemonic='USD') # define the amount as Decimal amount = t.amount # retrieve accounts to_account = mybook.accounts(fullname=t.expense) from_account = mybook.accounts(fullname=t.account) # if income, flip the accounts so 'income' is used instead of 'charge' if t.income: to_account = mybook.accounts(fullname=t.account) from_account = mybook.accounts(fullname=t.expense) # create the transaction with its two splits Transaction(post_date=today, enter_date=today, currency=USD, description=t.description, splits=[ Split(account=to_account, value=amount, memo='Automated from script'), Split(account=from_account, value=-amount, memo='Automated from script'), ]) # save the book mybook.save() except: success = False log(success, t, to_account_found, from_account_found, expense_account_created, log_file)
if sumUpTransaction['type'] == 'PAYMENT': # build description for gnu cash description = "SumUp: {} ({})".format( sumUpTransaction['transaction_code'], transaction['card']['last_4_digits']) # which account are we assigning this to toAccount = snackspaceIncomeAccount Transaction(currency=gbp, enter_date=createdAt, post_date=createdAt.date(), num=sumUpTransaction['id'], description=description, splits=[ Split(account=sumUpAccount, value=net), Split(account=toAccount, value=amount), Split(account=feeExpenseAccount, value=fee) ]) logger.info("Saved charge: {}, {}, {}".format( sumUpTransaction['id'], createdAt.date(), description)) elif sumUpTransaction['type'] == 'CHARGE_BACK': description = "SumUp: {}".format( sumUpTransaction['transaction_code']) if net < 0: toAccount = miscellaneousExpenseAccount else: toAccount = snackspaceIncomeAccount Transaction(currency=gbp,
# create a book (in memory) s = create_book(currency="EUR") # get the EUR and create the USD currencies c1 = s.book.default_currency c2 = s.book.create_currency_from_ISO("USD") # create two accounts a1 = Account("Acc 1", "ASSET", c1, parent=s.book.root_account) a2 = Account("Acc 2", "ASSET", c2, parent=s.book.root_account) # create a transaction from a1 to a2 tr = Transaction(currency=c1, description="transfer", splits=[ Split(account=a1, value=-100), Split(account=a2, value=100, quantity=30) ]) s.flush() # ledger_str() returns a representation of the transaction in the ledger-cli format tr.ledger_str() # change the book to use the "trading accounts" options s.book.use_trading_accounts = True # add a new transaction identical to the previous tr2 = Transaction(currency=c1, description="transfer 2", splits=[ Split(account=a1, value=-100), Split(account=a2, value=100, quantity=30)