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()