Examples
The following are a few examples to illustrate how to use the Library to accomplish common accounting tasks. If there’s a scenario you would like to see an example of, open an issue on github and we’ll prepare it for you.
Tax Inclusive Transactions
Under ordinary circumstances, the Tax amount is added to the amount of the Transaction to arrive at the total. In some cases however, the tax amount is included in the Transaction amount already which requires different accounting treatment. This Example will demonstrate how to create one of each kind of transaction.
Tax Exclusive
This Transaction will have the Tax amount added on top of the Transaction amount. We first need the Accounts involved.
from python_accounting.models import Account
with get_session(engine) as session:
tax_account = Account(
name="Tax Account",
account_type=Account.AccountType.CONTROL,
currency_id=currency.id,
entity_id=entity.id,
)
bank_account = Account(
name="Bank Account",
account_type=Account.AccountType.BANK,
currency_id=currency.id,
entity_id=entity.id,
)
revenue_account = Account(
name="Revenue Account",
account_type=Account.AccountType.OPERATING_REVENUE,
currency_id=currency.id,
entity_id=entity.id,
)
session.add_all([
tax_account,
bank_account,
revenue_account,
])
session.commit()
Next we’ll create the Tax.
from python_accounting.models import Tax
with get_session(engine) as session:
output_tax = Tax(
name="Input Vat",
code="INPT",
account_id=tax_account.id,
rate=10,
entity_id=entity.id,
)
session.add_all(output_tax, input_tax)
session.commit()
Now we’re ready to create the Tax exclusive Transaction. By default, the Library treats Transactions as Tax Exclusive.
from datetime import datetime
from python_accounting.models import Transaction, LineItem
from python_accounting.transactions import CashSale
with get_session(engine) as session:
cash_sale = CashSale(
narration="Cash Sale Transaction",
transaction_date=datetime.now(),
account_id=bank_account.id,
entity_id=entity.id,
)
session.add(cash_sale)
session.flush()
cash_sale_line_item = LineItem(
narration="Cash Sale line item",
account_id=revenue_account.id,
amount=100,
tax_id=output_tax.id,
entity_id=entity.id,
)
session.add(cash_sale_line_item)
session.flush()
cash_sale.line_items.add(cash_sale_line_item)
session.add(cash_sale)
cash_sale.post(session)
print(bank_account.closing_balance(session)) # 110
print(revenue_account.closing_balance(session)) # -100
print(tax_account.closing_balance(session)) # -10
Tax Inclusive
The procedure for a Tax Inclusive Transaction is identical to the one above, with the only difference being that the Line Item is explicitly marked as being tax inclusive.
from datetime import datetime
from python_accounting.models import Transaction, LineItem
from python_accounting.transactions import CashSale
with get_session(engine) as session:
cash_sale = CashSale(
narration="Cash Sale Transaction",
transaction_date=datetime.now(),
account_id=bank_account.id,
entity_id=entity.id,
)
session.add(cash_sale)
session.flush()
cash_sale_line_item = LineItem(
narration="Cash Sale line item",
account_id=revenue_account.id,
amount=100,
tax_id=output_tax.id,
tax_inclusive=True, # <- Turn on Tax Inclusive for the Line Item
entity_id=entity.id,
)
session.add(cash_sale_line_item)
session.flush()
cash_sale.line_items.add(cash_sale_line_item)
session.add(cash_sale)
cash_sale.post(session)
print(bank_account.closing_balance(session)) # 100
print(revenue_account.closing_balance(session)) # -90.9091
print(tax_account.closing_balance(session)) # -9.0909
Compound Transactions
By default, the library enforces the double entry principle by posting the amounts in a Transaction’s Line Items to the opposite side of the ledger from that of its main Account. As such each Transaction type has a fixed setting about to which side of the main Account the amount should be posted.
:warning: While its technically possible to create Transaction objects directly, doing so would bypass the validation rules built into the derived Transaction classes which could lead to incorrect bookkeeping and is therefore strongly discouraged.
The Journal Entry is the only Transaction type that allows you to specify whether the amount should be credited to the main Account or not. In a standard Journal Entry Transaction, all the Line Item amounts are posted to the opposite side of the ledger from that specified for the main Account, i.e if credited=True all the Line Item Accounts will have the Line Item amounts posted to the debit side of the ledger and vice versa.
Standard Journal Entry
This Transaction will post the total amount to the debit side of the main Account, and the Line Item amounts to the credit side of each Line Item Account.
First we’ll create some Accounts.
from python_accounting.models import Account
with get_session(engine) as session:
bank_account = Account(
name="Bank Account",
account_type=Account.AccountType.BANK,
currency_id=currency.id,
entity_id=entity.id,
)
client_account = Account(
name="Client Account",
account_type=Account.AccountType.RECEIVABLE,
currency_id=currency.id,
entity_id=entity.id,
)
supplier_account = Account(
name="Supplier Account",
account_type=Account.AccountType.PAYABLE,
currency_id=currency.id,
entity_id=entity.id,
)
asset_account = Account(
name="Asset Account",
account_type=Account.AccountType.NON_CURRENT_ASSET,
currency_id=currency.id,
entity_id=entity.id,
)
session.add_all([
bank_account,
client_account,
supplier_account,
asset_account
])
session.commit()
The Journal Entry’s credited property is set to True by default, so for this example we’ll turn it to False.
from datetime import datetime
from python_accounting.models import Transaction, LineItem
from python_accounting.transactions import JournalEntry
with get_session(engine) as session:
journal_entry = JournalEntry(
narration="Journal Entry Transaction",
transaction_date=datetime.now(),
account_id=bank_account.id,
entity_id=entity.id,
credited=False # <- Debit the main (bank) account
)
session.add(journal_entry)
session.flush()
client_account_line_item = LineItem(
narration="Client Account line item",
account_id=client_account.id,
amount=30,
entity_id=entity.id,
)
supplier_account_line_item = LineItem(
narration="Supplier Account line item",
account_id=supplier_account.id,
amount=15,
entity_id=entity.id,
)
asset_account_line_item = LineItem(
narration="Asset Account line item",
account_id=asset_account.id,
amount=10,
entity_id=entity.id,
)
session.add_all([
client_account_line_item,
supplier_account_line_item,
asset_account_line_item
])
session.flush()
journal_entry.line_items.add(client_account_line_item)
journal_entry.line_items.add(supplier_account_line_item)
journal_entry.line_items.add(asset_account_line_item)
session.add(journal_entry)
journal_entry.post(session)
print(bank_account.closing_balance(session)) # 55
print(client_account.closing_balance(session)) # -30
print(supplier_account.closing_balance(session)) # -15
print(asset_account.closing_balance(session)) # -10
Compound Journal Entry
Sometimes however, you might want to post line item amounts to different sides of the ledger for different accounts. To accomplish this, you turn on the compound property of the Transaction and also specify a main_account_amount. This is because as opposed to the simple case above, different amounts are being posted to different sides of the ledger by the Line Items and its therefore not obvious what amount should be posted to the main account. On the other side, we specify the side to which each Line Item Account will have their amount posted, which is debit by default.
Needless to say the totals of amounts posted to the debit and credit side of the ledger must equal exactly otherwise the Transaction will throw an error.
from datetime import datetime
from python_accounting.models import Transaction, LineItem
from python_accounting.transactions import JournalEntry
with get_session(engine) as session:
journal_entry = JournalEntry(
narration="Journal Entry Transaction",
transaction_date=datetime.now(),
account_id=bank_account.id,
entity_id=entity.id,
compound=True, # <- Turn on compound flag
credited=False,
main_account_amount=25, # <- Specify how much to post to the main (bank) account
)
session.add(journal_entry)
session.flush()
client_account_line_item = LineItem(
narration="Client Account line item",
account_id=client_account.id,
credited=True, # <- Specify to credit this line item's amount to is account
amount=30,
entity_id=entity.id,
)
supplier_account_line_item = LineItem(
narration="Supplier Account line item",
account_id=client_account.id,
amount=15,
entity_id=entity.id,
)
asset_account_line_item = LineItem(
narration="Asset Account line item",
account_id=asset_account.id,
credited=True, # <- Specify to credit this line item's amount to is account
amount=10,
entity_id=entity.id,
)
session.add_all([
client_account_line_item,
supplier_account_line_item,
asset_account_line_item
])
session.flush()
journal_entry.line_items.add(client_account_line_item)
journal_entry.line_items.add(supplier_account_line_item)
journal_entry.line_items.add(asset_account_line_item)
session.add(journal_entry)
journal_entry.post(session)
print(bank_account.closing_balance(session)) # 25
print(client_account.closing_balance(session)) # -30
print(supplier_account.closing_balance(session)) # 15
print(asset_account.closing_balance(session)) # -10