import datetime
from django.shortcuts import render
from accounts.models import Account, Header
from core.core import process_year_start_date_range_form
from events.models import Event, HistoricalEvent
[docs]def events_report(request, template_name="reports/events.html"):
"""Display all :class:`Events<events.models.Event>`.
:param template_name: The template file to use to render the response.
:type template_name: str
:returns: HTTP Response with an ``events`` context variable.
:rtype: HttpResponse
"""
events = Event.objects.all()
historical_events = HistoricalEvent.objects.all()
return render(request, template_name, locals())
[docs]def profit_loss_report(request, template_name="reports/profit_loss.html"):
"""
Display the Profit or Loss for a time period calculated using all Income
and Expense :class:`Accounts<accounts.models.Account>`.
The available ``GET`` parameters are ``start_date`` and ``stop_date``. They
control the date range used for the calculations.
This view is used to show the Total, Header and Account Net Changes over
a specified date range. It uses the Net Changes to calculate various Profit
amounts, passing them as context variables:
* ``gross_profit``: Income - Cost of Goods Sold
* ``operating_profit``: Gross Profit - Expenses
* ``net_profit``: Operating Profit + Other Income - Other Expenses
Also included is the ``headers`` dictionary which contains the ``income``,
``cost_of_goods_sold``, ``expenses``, ``other_income``, and
``other_expense`` keys. These keys point to the root node for the
respective :attr:~accounts.models.BaseAccountModel.type`.
These nodes have additional attributes appended to them, ``total``,
``accounts`` and ``descendants``. ``total`` represents the total Net Change
for the node. ``accounts`` and ``descendants`` are lists of child
nodes(also with a ``total`` attribute).
:param template_name: The template file to use to render the response.
:type template_name: str
:returns: HTTP Response with the start/stop dates, ``headers`` dictionary
and Profit Totals
:rtype: HttpResponse
"""
form, start_date, stop_date = process_year_start_date_range_form(request)
headers_and_types = _get_profit_loss_header_keys_and_types()
headers = {
header_key: _get_profit_loss_header_totals(
header_type, start_date, stop_date)
for (header_key, header_type) in headers_and_types}
gross_profit, operating_profit, net_profit = _get_profit_totals(headers)
return render(request, template_name, locals())
def _get_profit_loss_header_keys_and_types():
"""Return a tuple of tuples containing a root header key and it's type."""
return (('income', 4),
('cost_of_goods_sold', 5),
('expenses', 6),
('other_income', 7),
('other_expenses', 8))
def _get_profit_loss_header_totals(header_type, start_date, stop_date):
"""
Return a root Header with the `descendants`, `accounts` and `total`
attributes.
The `descendants` attribute is a list of direct descendants, with their own
`total` and `descendants` attributes.
The `total` attribute should represent the net change of the Header or
Account over the specified `start_date` and `stop_date`.
"""
root_header = Header.objects.get(parent=None, type=header_type)
return _get_profit_loss_header(root_header, start_date, stop_date)
def _get_profit_loss_header(header, start_date, stop_date):
"""
Return the `header` instance with additional attributes of accounts,
descendants and total change for the time period.
"""
header.accounts = [
_get_profit_loss_account(account, start_date, stop_date)
for account in header.account_set.all()
]
if header.level < 2:
header.descendants = [
_get_profit_loss_header(child, start_date, stop_date)
for child in header.get_children()]
header.total = (sum(account.total for account in header.accounts) +
sum(child.total for child in header.descendants))
else:
descendants = header.get_descendants(include_self=True)
accounts = [_get_profit_loss_account(account, start_date, stop_date)
for descendant in descendants
for account in descendant.account_set.all()]
header.total = sum(account.total for account in accounts)
return header
def _get_profit_loss_account(account, start_date, stop_date):
"""
Return the `account` instance with an additional `total` attribute,
containing the net change for the time period.
"""
in_range_transactions = account.transaction_set.filter(
date__lte=stop_date, date__gte=start_date)
_, _, net_change = in_range_transactions.get_totals(net_change=True)
if account.flip_balance():
net_change *= -1
account.total = net_change
return account
def _get_profit_totals(headers):
"""Calculate and return the Gross, Operating and Net Profits."""
gross_profit = (headers['income'].total -
headers['cost_of_goods_sold'].total)
operating_profit = gross_profit - headers['expenses'].total
net_profit = (operating_profit + headers['other_income'].total -
headers['other_expenses'].total)
return (gross_profit, operating_profit, net_profit)
[docs]def trial_balance_report(request, template_name="reports/trial_balance.html"):
"""
Display the state and change of all :class:`Accounts
<accounts.models.Account>` over a time period.
The available ``GET`` parameters are ``start_date`` and ``stop_date``.
The view also provides the ``start_date``, ``stop_date`` and ``accounts``
context variables.
The ``start_date`` and ``stop_date`` variables default to the first day of
the year and the current date.
The ``accounts`` variable is a list of dictionaries, each representing an
:class:`~accounts.models.Account`. Each dictionary contains the
:class:`Account's<accounts.models.Account>` number, name, balance at the
beginning and end of the date range, total debits and credits and the net
change.
:param template_name: The template file to use to render the response.
:type template_name: str
:returns: HTTP Response with start and stop dates and an ``accounts`` list.
:rtype: HttpResponse
"""
form, start_date, stop_date = process_year_start_date_range_form(request)
accounts = [_get_account_details(x, start_date, stop_date) for x in
list(Account.objects.all().order_by('full_number'))]
return render(request, template_name, {'start_date': start_date,
'stop_date': stop_date,
'accounts': accounts,
'form': form})
def _get_account_details(account, start_date, stop_date):
"""
Return the Name, Number, URL, Starting/Ending Balances, Net Change and
Credit/Debit Totals of an :class:`~accounts.models.Account` in the
specified range.
:param account: The Account whose details should be retrieved.
:type account: :class:`~accounts.models.Account`
:param start_date: The date representing the first day of the time period.
:type start_date: :class:`datetime.date`
:param stop_date: The date representing the last day of the time period.
:type stop_date: :class:`datetime.date`
:returns: Account details and activity information
:rtype: dict
"""
one_day = datetime.timedelta(days=1)
start_balance = account.get_balance_by_date(start_date - one_day)
end_balance = account.get_balance_by_date(stop_date)
in_range_transactions = account.transaction_set.filter(
date__gte=start_date, date__lte=stop_date)
debit_total, credit_total, net_change = in_range_transactions.get_totals(
net_change=True)
return {'name': account.name,
'number': account.get_full_number(),
'beginning_balance': start_balance,
'total_debits': debit_total,
'total_credits': credit_total,
'net_change': net_change,
'ending_balance': end_balance,
'url': account.get_absolute_url()}