import os
from datetime import date, datetime
from decimal import Decimal
from random import randint, choices, random
from zoneinfo import ZoneInfo
import django
# for easier visualization it is recommended to use pandas to render data...
# if pandas is not installed, you may install it with this command: pip install -U pandas
# pandas is not a dependecy of django_ledger...
import pandas as pd
from django.core.exceptions import ObjectDoesNotExist
# Set your django settings module if needed...
os.environ['DJANGO_SETTINGS_MODULE'] = 'dev_env.settings'
# if using jupyter notebook need to set DJANGO_ALLOW_ASYNC_UNSAFE as "true"
os.environ['DJANGO_ALLOW_ASYNC_UNSAFE'] = 'true'
# change your working directory as needed...
os.chdir('../')
django.setup()
from django_ledger.models.entity import EntityModel
from django_ledger.models.items import ItemModel
from django_ledger.models.invoice import InvoiceModel
from django_ledger.models.bill import BillModel
from django_ledger.models.estimate import EstimateModel
from django.contrib.auth import get_user_model
from django_ledger.io import roles, DEBIT, CREDIT
from django_ledger.io.io_library import IOBluePrint, IOLibrary
Get Your Entity Administrator UserModel
# change this to your preferred django username...
MY_USERNAME = 'ceo_user'
MY_PASSWORD = 'NeverUseMe|VeryInsecure!'
UserModel = get_user_model()
try:
user_model = UserModel.objects.get(username__exact=MY_USERNAME)
except:
user_model = UserModel(username=MY_USERNAME)
user_model.set_password(MY_PASSWORD)
user_model.save()
Get or Create an Entity Model
ENTITY_NAME = 'One Big Company, LLC'
entity_model = EntityModel.create_entity(
name=ENTITY_NAME,
admin=user_model,
use_accrual_method=True,
fy_start_month=1
)
entity_model
Chart of Accounts
Create a Default Chart of Accounts
Newly created EntityModel do not have a default Code of Accounts yet.
entity_model.has_default_coa()
default_coa_model = entity_model.create_chart_of_accounts(
assign_as_default=True,
commit=True,
coa_name='My QuickStart CoA'
)
default_coa_model
entity_model.default_coa == default_coa_model
Populate Entity with Random Data (Optional)
Define a Start Date for Transactions
START_DTTM = datetime(year=2022, month=10, day=1, tzinfo=ZoneInfo('UTC'))
This action will populate the EntityModel with random data.
It will populate a Code of Accounts using a default pre-defined list.
This approach is for illustration, educational and testing purposes, not encouraged for new production entities.
entity_model.populate_random_data(start_date=START_DTTM)
EntityModel has now a Default Chart of Accounts
entity_model.has_default_coa()
default_coa_model = entity_model.get_default_coa()
default_coa_model
Chart of Accounts (CoA)
A Chart of Accounts is a user-defined list of accounts.
Each Entity Model must have at least one default Chart of Accounts.
Django Ledger support multiple chart of accounts.
another_coa_model = entity_model.create_chart_of_accounts(
assign_as_default=False,
commit=True,
coa_name='My Empty Chart of Accounts'
)
another_coa_model
Accounts
Default CoA Accounts
default_coa_accounts_qs = entity_model.get_default_coa_accounts()
pd.DataFrame(default_coa_accounts_qs)
Get CoA Accounts by CoA Model
coa_accounts_by_coa_model_qs = entity_model.get_coa_accounts(coa_model=default_coa_model)
pd.DataFrame(coa_accounts_by_coa_model_qs)
No Accounts yet on this CoA…
coa_accounts_by_coa_model_qs = entity_model.get_coa_accounts(coa_model=another_coa_model)
pd.DataFrame(coa_accounts_by_coa_model_qs)
Get CoA Accounts by CoA Model UUID
May pass UUID instance instead of ChartOF AccountsModel…
coa_accounts_by_coa_uuid_qs = entity_model.get_coa_accounts(coa_model=default_coa_model.uuid)
pd.DataFrame(coa_accounts_by_coa_uuid_qs)
Get CoA Accounts by CoA Model Slug
If string is passed, will lookup by slug…
coa_accounts_by_coa_slug_qs = entity_model.get_coa_accounts(coa_model=default_coa_model.slug)
pd.DataFrame(coa_accounts_by_coa_slug_qs)
Get Accounts With Codes and CoA Model
Assumes default CoA if no coa_model is passed…
coa_accounts_by_codes_qs = entity_model.get_accounts_with_codes(code_list=['1010', '1050'])
pd.DataFrame(coa_accounts_by_codes_qs)
Empty ChartOfAccountModel…
coa_accounts_by_codes_qs = entity_model.get_accounts_with_codes(
code_list=['1010', '1050'],
coa_model=another_coa_model
)
pd.DataFrame(coa_accounts_by_codes_qs)
Get All Accounts at Once
coa_qs, coa_map = entity_model.get_all_coa_accounts()
A dictionary, CoA Model -> Account List.
coa_map
pd.DataFrame(coa_map[default_coa_model])
pd.DataFrame(coa_map[another_coa_model])
Create Account Model
Creating AccountModel into empty “another_coa_model”…
account_model = entity_model.create_account(
coa_model=another_coa_model,
code=f'1{str(randint(10000, 99999))}',
role=roles.ASSET_CA_INVENTORY,
name='A cool account created from the EntityModel API!',
balance_type=roles.DEBIT,
active=True)
account_model
another_coa_accounts_qs = entity_model.get_coa_accounts(coa_model=another_coa_model)
pd.DataFrame(another_coa_accounts_qs)
Basic Django Ledger Usage
The LedgerModel name is whatever your heart desires.
Examples:
A month.
A customer.
A vendor.
A project.
The more ledgers are created, the more segregation and control over transactions is possible.
ledger_model = entity_model.create_ledger(name='My October 2023 Ledger', posted=True)
Create a Library
library = IOLibrary(name='quickstart-library')
Create and Register a BluePrint
@library.register
def sale_blueprint(
sale_amount,
contribution_margin_percent: float,
description: str = None
) -> IOBluePrint:
blueprint = IOBluePrint()
cogs_amount = (1 - contribution_margin_percent) * sale_amount
blueprint.debit(account_code='1010', amount=sale_amount, description=description)
blueprint.credit(account_code='4010', amount=sale_amount, description=description)
blueprint.credit(account_code='1200', amount=cogs_amount, description=description)
blueprint.debit(account_code='5010', amount=cogs_amount, description=description)
return blueprint
Get a Cursor
cursor = library.get_cursor(entity_model=entity_model, user_model=user_model)
Dispatch Some Instructions
cursor.dispatch('sale_blueprint',
ledger_model='ledger-test-1',
sale_amount=120.345,
contribution_margin_percent=0.25,
description='so cool')
cursor.dispatch('sale_blueprint',
ledger_model='ledger-test-1',
sale_amount=12.345,
contribution_margin_percent=0.2,
description='so cool')
cursor.dispatch('sale_blueprint',
ledger_model=ledger_model,
sale_amount=34.455,
contribution_margin_percent=0.13,
description='so cool')
cursor.dispatch('sale_blueprint',
ledger_model='ledger-test-12',
sale_amount=90.43,
contribution_margin_percent=0.17,
description='so cool')
Commit Your Instructions
Not recommended to post both ledger and journal entries. Posted transactions will immediately hit the books. result contains resulting ledger models, journal entries and transactions fro the committed
result = cursor.commit(
post_new_ledgers=True,
post_journal_entries=True,
je_timestamp='2023-12-02 12:00'
)
# result
Get Financial Statement Report Data fro Ledger Model
Balance Sheet
bs_data = ledger_model.digest_balance_sheet(
to_date=date(2023, 12, 31),
entity_slug=entity_model
)
bs_data.get_balance_sheet_data()
Income Statement
is_data = ledger_model.digest_income_statement(
from_date=date(2023, 1, 1),
to_date=date(2023, 12, 31),
entity_slug=entity_model
)
is_data.get_income_statement_data()
Cash Flow Statement
cfs_data = ledger_model.digest_cash_flow_statement(
from_date=date(2023, 1, 1),
to_date=date(2023, 12, 31),
entity_slug=entity_model
)
cfs_data.get_cash_flow_statement_data()
All Statements in a Single Call
fin_digest = ledger_model.digest_financial_statements(
from_date=date(2023, 1, 1),
to_date=date(2023, 12, 31),
entity_slug=entity_model
)
statement_data = fin_digest.get_financial_statements_data()
statement_data['balance_sheet']
statement_data['income_statement']
statement_data['cash_flow_statement']
Customers
Get Customers
customer_qs = entity_model.get_customers()
pd.DataFrame(customer_qs.values())
Create Customers
customer_model = entity_model.create_customer(customer_model_kwargs={
'customer_name': 'Mr. Big',
'description': 'A great paying customer!',
})
Vendors
Get Vendors
vendor_qs = entity_model.get_vendors()
pd.DataFrame(vendor_qs.values())
Create Vendor
vendor_model = entity_model.create_vendor(vendor_model_kwargs={
'vendor_name': 'ACME LLC',
'description': 'A Reliable Vendor!'
})
Invoices
Get Invoices
invoices_qs = entity_model.get_invoices()
pd.DataFrame(invoices_qs.values())
Create Invoice
invoice_model = entity_model.create_invoice(
customer_model='C-0000000006',
terms=InvoiceModel.TERMS_NET_30
)
invoice_model
Add Items to Invoices
invoices_item_models = invoice_model.get_item_model_qs()
# K= number of items...
K = 6
invoice_itemtxs = {
im.item_number: {
'unit_cost': round(random() * 10, 2),
'quantity': round(random() * 100, 2),
'total_amount': None
} for im in choices(invoices_item_models, k=K)
}
# Choose operation ITEMIZE_APPEND to append itemtxs...
invoice_itemtxs = invoice_model.migrate_itemtxs(itemtxs=invoice_itemtxs,
commit=True,
operation=InvoiceModel.ITEMIZE_REPLACE)
invoice_itemtxs
invoice_model.amount_due
Bills
Get Bills
bills_qs = entity_model.get_bills()
pd.DataFrame(bills_qs.values())
Create Bill
bill_model = entity_model.create_bill(
vendor_model='V-0000000002',
terms=BillModel.TERMS_NET_60
)
bill_model
Add Items to Bills
bill_item_models = bill_model.get_item_model_qs()
K = 6
bill_itemtxs = {
im.item_number: {
'unit_cost': round(random() * 10, 2),
'quantity': round(random() * 100, 2),
'total_amount': None
} for im in choices(bill_item_models, k=K)
}
# Choose operation ITEMIZE_APPEND to append itemtxs...
bill_itemtxs = bill_model.migrate_itemtxs(itemtxs=bill_itemtxs,
commit=True,
operation=BillModel.ITEMIZE_REPLACE)
bill_itemtxs
bill_model.amount_due
Purchase Orders
Get Purchase Orders
purchase_orders_qs = entity_model.get_purchase_orders()
pd.DataFrame(purchase_orders_qs.values())
Create Purchase Order
po_model = entity_model.create_purchase_order()
Add Items to Purchase Orders
po_item_models = po_model.get_item_model_qs()
K = 6
po_itemtxs = {
im.item_number: {
'unit_cost': round(random() * 10, 2),
'quantity': round(random() * 100, 2),
'total_amount': None
} for im in choices(po_item_models, k=K)
}
# Choose operation ITEMIZE_APPEND to append itemtxs...
po_itemtxs = po_model.migrate_itemtxs(itemtxs=po_itemtxs,
commit=True,
operation=EstimateModel.ITEMIZE_REPLACE)
po_itemtxs
po_model.po_amount
Estimates/Contracts
Get Estimates/Contracts
estimates_qs = entity_model.get_estimates()
pd.DataFrame(estimates_qs.values())
Create Estimate
estimate_model = entity_model.create_estimate(
estimate_title='A quote for new potential customer!',
customer_model='C-0000000009',
contract_terms=EstimateModel.CONTRACT_TERMS_FIXED
)
Add Items to Estimates
estimate_item_models = estimate_model.get_item_model_qs()
K = 6
estimate_itemtxs = {
im.item_number: {
'unit_cost': round(random() * 10, 2),
'unit_revenue': round(random() * 20, 2),
'quantity': round(random() * 100, 2),
'total_amount': None
} for im in choices(estimate_item_models, k=K)
}
# Choose operation ITEMIZE_APPEND to append itemtxs...
estimate_itemtxs = estimate_model.migrate_itemtxs(itemtxs=estimate_itemtxs,
commit=True,
operation=EstimateModel.ITEMIZE_REPLACE)
estimate_itemtxs
estimate_model.get_cost_estimate()
estimate_model.get_revenue_estimate()
estimate_model.get_profit_estimate()
estimate_model.get_gross_margin_estimate(as_percent=True)
Bank Accounts
Get Bank Accounts
bank_accounts_qs = entity_model.get_bank_accounts()
pd.DataFrame(bank_accounts_qs.values())
Create Bank Account
bank_account_model = entity_model.create_bank_account(name='A big bank account!',
account_type='checking')
Items
Unit of Measures
Get Unit of Measures
uom_qs = entity_model.get_uom_all()
pd.DataFrame(uom_qs.values())
Create a UOM
uom_model_ft = entity_model.create_uom(
name='Linear Feet',
unit_abbr='lin-ft'
)
Get Some UoMs
uom_model_unit = uom_qs.get(unit_abbr__exact='unit')
uom_model_man_hr = uom_qs.get(unit_abbr__exact='man-hour')
Expenses
Get Expense Items
expenses_qs = entity_model.get_items_expenses()
pd.DataFrame(expenses_qs.values())
Create Expense Item
expense_item_model = entity_model.create_item_expense(
name='Premium Pencils',
uom_model=uom_model_unit,
expense_type=ItemModel.ITEM_TYPE_MATERIAL
)
expense_item_model.is_expense()
Services
Get Service Items
services_qs = entity_model.get_items_services()
pd.DataFrame(services_qs.values())
Create Service Item
service_model = entity_model.create_item_service(
name='Yoga Class',
uom_model=uom_model_man_hr
)
service_model.is_service()
Products
Get Product Items
products_qs = entity_model.get_items_products()
pd.DataFrame(products_qs.values())
Create Product Items
product_model = entity_model.create_item_product(
name='1/2" Premium PVC Pipe',
uom_model=uom_model_ft,
item_type=ItemModel.ITEM_TYPE_MATERIAL
)
product_model.is_product()
Inventory
Get Inventory Items
inventory_qs = entity_model.get_items_inventory()
pd.DataFrame(inventory_qs.values())
Create Inventory Items
inventory_model = entity_model.create_item_inventory(
name='A Home to Flip!',
uom_model=uom_model_unit,
item_type=ItemModel.ITEM_TYPE_LUMP_SUM
)
inventory_model.is_inventory()
Financial Statement PDF Reports
Set Up
Must enable PDF support by installing dependencies via pipenv.
pipenv install –categories pdf
Balance Sheet
bs_report = entity_model.get_balance_sheet_statement(
to_date=date(2022, 12, 31),
save_pdf=True,
filepath='./'
)
# save_pdf=True saves the PDF report in the project's BASE_DIR.
# filename and filepath may also be specified...
# will raise not implemented error if PDF support is not enabled...
Balance Sheet Statement Raw Data
bs_report.get_report_data()
Income Statement
ic_report = entity_model.get_income_statement(
from_date=date(2022, 1, 1),
to_date=date(2022, 12, 31),
save_pdf=True,
filepath='./'
)
# save_pdf=True saves the PDF report in the project's BASE_DIR.
# filename and filepath may also be specified...
# will raise not implemented error if PDF support is not enabled...
Income Statement Raw Data
ic_report.get_report_data()
Cash Flow Statement
cf_report = entity_model.get_cash_flow_statement(
from_date=date(2022, 1, 1),
to_date=date(2022, 12, 31),
save_pdf=True,
filepath='./'
)
# save_pdf=True saves the PDF report in the project's BASE_DIR.
# filename and filepath may also be specified...
Cash Flow Statement Raw Data
cf_report.get_report_data()
All Financial Statements Data in a single Call
reports = entity_model.get_financial_statements(
user_model=user_model,
from_date=date(2022, 1, 1),
to_date=date(2022, 12, 31),
save_pdf=True,
filepath='./'
)
# save_pdf=True saves the PDF report in the project's BASE_DIR.
# filename and filepath may also be specified...
reports.balance_sheet_statement.get_report_data()
reports.income_statement.get_report_data()
reports.cash_flow_statement.get_report_data()