Full Code of dulacp/django-accounting for AI

master 2e61776a653e cached
169 files
257.8 KB
62.2k tokens
570 symbols
1 requests
Download .txt
Showing preview only (298K chars total). Download the full file or copy to clipboard to get everything.
Repository: dulacp/django-accounting
Branch: master
Commit: 2e61776a653e
Files: 169
Total size: 257.8 KB

Directory structure:
gitextract_lsm6zxq8/

├── .coveragerc
├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── README.rst
├── accounting/
│   ├── __init__.py
│   ├── apps/
│   │   ├── __init__.py
│   │   ├── books/
│   │   │   ├── __init__.py
│   │   │   ├── admin.py
│   │   │   ├── apps.py
│   │   │   ├── calculators.py
│   │   │   ├── context_processors.py
│   │   │   ├── forms.py
│   │   │   ├── managers.py
│   │   │   ├── middlewares.py
│   │   │   ├── migrations/
│   │   │   │   ├── 0001_initial.py
│   │   │   │   ├── 0002_auto_20141029_1606.py
│   │   │   │   ├── 0003_auto_20141029_1606.py
│   │   │   │   ├── 0004_auto_20141104_1026.py
│   │   │   │   ├── 0005_auto_20150128_1458.py
│   │   │   │   ├── 0006_auto_20150206_1836.py
│   │   │   │   ├── 0007_auto_20150206_1836.py
│   │   │   │   ├── 0008_auto_20150206_1843.py
│   │   │   │   ├── 0009_auto_20150206_1850.py
│   │   │   │   ├── 0010_auto_20150210_1449.py
│   │   │   │   ├── 0011_auto_20150324_1430.py
│   │   │   │   └── __init__.py
│   │   │   ├── mixins.py
│   │   │   ├── models.py
│   │   │   ├── templatetags/
│   │   │   │   ├── __init__.py
│   │   │   │   └── status_filters.py
│   │   │   ├── urls.py
│   │   │   ├── utils.py
│   │   │   └── views.py
│   │   ├── connect/
│   │   │   ├── __init__.py
│   │   │   ├── middlewares.py
│   │   │   ├── models.py
│   │   │   ├── steps.py
│   │   │   ├── urls.py
│   │   │   └── views.py
│   │   ├── context_processors.py
│   │   ├── people/
│   │   │   ├── __init__.py
│   │   │   ├── admin.py
│   │   │   ├── forms.py
│   │   │   ├── migrations/
│   │   │   │   ├── 0001_initial.py
│   │   │   │   ├── 0002_auto_20141029_1609.py
│   │   │   │   ├── 0003_employee_payroll_tax_rate.py
│   │   │   │   ├── 0004_auto_20141029_2306.py
│   │   │   │   ├── 0005_auto_20141029_2308.py
│   │   │   │   └── __init__.py
│   │   │   ├── models.py
│   │   │   ├── urls.py
│   │   │   └── views.py
│   │   └── reports/
│   │       ├── __init__.py
│   │       ├── admin.py
│   │       ├── forms.py
│   │       ├── migrations/
│   │       │   ├── 0001_initial.py
│   │       │   ├── 0002_auto_20150128_1458.py
│   │       │   ├── 0003_auto_20150131_1902.py
│   │       │   └── __init__.py
│   │       ├── models.py
│   │       ├── urls.py
│   │       ├── views.py
│   │       └── wrappers.py
│   ├── defaults.py
│   ├── libs/
│   │   ├── __init__.py
│   │   ├── checks.py
│   │   ├── decorators.py
│   │   ├── exceptions.py
│   │   ├── fields.py
│   │   ├── foundation.py
│   │   ├── intervals.py
│   │   ├── prices.py
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   ├── check_filters.py
│   │   │   ├── check_tags.py
│   │   │   ├── currency_filters.py
│   │   │   ├── display_tags.py
│   │   │   ├── distance_filters.py
│   │   │   ├── float_filters.py
│   │   │   ├── form_filters.py
│   │   │   ├── form_tags.py
│   │   │   ├── format_filters.py
│   │   │   ├── introspection_filters.py
│   │   │   ├── my_filters.py
│   │   │   ├── nav.py
│   │   │   └── url_tags.py
│   │   └── utils.py
│   ├── static/
│   │   └── accounting/
│   │       ├── css/
│   │       │   └── main.css
│   │       └── js/
│   │           ├── books/
│   │           │   └── invoice_or_bill_create.js
│   │           ├── jquery.formset.js
│   │           └── main.js
│   ├── templates/
│   │   └── accounting/
│   │       ├── _generics/
│   │       │   ├── check_tag.html
│   │       │   ├── delete_entity.html
│   │       │   └── form.html
│   │       ├── _partials/
│   │       │   └── form_fields.html
│   │       ├── base.html
│   │       ├── books/
│   │       │   ├── _generics/
│   │       │   │   ├── sale_content.html
│   │       │   │   ├── sale_detail.html
│   │       │   │   └── sale_list.html
│   │       │   ├── _partials/
│   │       │   │   ├── expense_claim_list.html
│   │       │   │   ├── payment_list.html
│   │       │   │   └── tax_rate_list.html
│   │       │   ├── bill_create_or_update.html
│   │       │   ├── bill_detail.html
│   │       │   ├── bill_list.html
│   │       │   ├── dashboard.html
│   │       │   ├── estimate_create_or_update.html
│   │       │   ├── estimate_detail.html
│   │       │   ├── estimate_list.html
│   │       │   ├── expense_claim_create_or_update.html
│   │       │   ├── expense_claim_detail.html
│   │       │   ├── expense_claim_list.html
│   │       │   ├── invoice_create_or_update.html
│   │       │   ├── invoice_detail.html
│   │       │   ├── invoice_list.html
│   │       │   ├── organization_create_or_update.html
│   │       │   ├── organization_detail.html
│   │       │   ├── organization_list.html
│   │       │   ├── organization_selector.html
│   │       │   ├── payment_create_or_update.html
│   │       │   ├── tax_rate_create_or_update.html
│   │       │   └── tax_rate_list.html
│   │       ├── connect/
│   │       │   └── getting_started.html
│   │       ├── layout.html
│   │       ├── people/
│   │       │   ├── client_create_or_update.html
│   │       │   ├── client_detail.html
│   │       │   ├── client_list.html
│   │       │   ├── employee_create_or_update.html
│   │       │   ├── employee_detail.html
│   │       │   └── employee_list.html
│   │       └── reports/
│   │           ├── _partials/
│   │           │   └── period_form.html
│   │           ├── financial_settings_update.html
│   │           ├── invoice_details_report.html
│   │           ├── pay_run_report.html
│   │           ├── payrun_settings_update.html
│   │           ├── profit_and_loss_report.html
│   │           ├── report_list.html
│   │           ├── settings_list.html
│   │           └── tax_report.html
│   ├── urls.py
│   └── wsgi.py
├── lint.sh
├── requirements.txt
├── runtests.py
├── setup.cfg
├── setup.py
└── tests/
    ├── __init__.py
    ├── _site/
    │   ├── __init__.py
    │   └── model_tests_app/
    │       ├── __init__.py
    │       └── models.py
    ├── config.py
    ├── functional/
    │   ├── __init__.py
    │   └── libs/
    │       ├── __init__.py
    │       ├── context_processors_tests.py
    │       ├── template_tags_tests.py
    │       └── utils_tests.py
    ├── integration/
    │   └── __init__.py
    ├── settings.py
    └── unit/
        ├── __init__.py
        ├── books/
        │   ├── __init__.py
        │   ├── bill_tests.py
        │   └── invoice_tests.py
        ├── clients/
        │   ├── __init__.py
        │   ├── organization_tests.py
        │   └── user_tests.py
        └── libs/
            ├── __init__.py
            └── prices_tests.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .coveragerc
================================================
[run]
source = accounting
omit = *migrations*


================================================
FILE: .gitignore
================================================
## Byte-compiled
__pycache__/
*.py[cod]
*.egg-info
/dist/
/build/

## Database
*.sqlite3

## Translations
*.mo

## Components
components/

## Testing
.coverage
.noseids
coverage.xml
violations.txt
nosetests.xml
/htmlcov/*


================================================
FILE: .travis.yml
================================================
# Use the newer container-based infrastructure
# http://docs.travis-ci.com/user/workers/container-based-infrastructure/
sudo: false

# Cache pip downloads
cache:
    directories:
      - $HOME/.pip-cache/

language: python

python:
    - '3.3'
    - '3.4'

install:
    - pip install -e . -r requirements.txt

script:
    - make travis

after_success:
    - coveralls


================================================
FILE: LICENSE
================================================
Copyright (c) 2013 Pierre Dulac (https://dulacp.com/)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: Makefile
================================================
# These targets are not files
.PHONY: contribute travis test lint coverage

clean:
	# Remove files not in source control
	find . -type f -name "*.pyc" -delete
	rm -rf nosetests.xml coverage.xml htmlcov violations.txt

todo:
	# Look for areas of the code that need updating when some event has taken place
	grep --exclude-dir=components -rnH TODO reqs
	grep --exclude-dir=components -rnH TODO accounting
	grep --exclude-dir=components -rnH TODO tests

publish:
	git push --tag origin master
	rm -rf dist/*
	python setup.py sdist
	twine upload dist/*


## Testing

lint:
	./lint.sh

test:
	./runtests.py tests

coverage:
	coverage run ./runtests.py --with-xunit tests
	coverage xml -i

# This target is run on Travis.ci. We lint, test and build.
# We don't call 'install' first as that is run as a separate part
# of the Travis build process.
travis: lint coverage


## Install / Upgrade

install:
	pip install -r requirements.txt

upgrade:
	pip install --upgrade -r requirements.txt


================================================
FILE: README.rst
================================================
django-accounting
=================

    In the beginning God created man, and the costs followed afterwards.

|Build Status| |Coverage Status|

Check the associated project
`Accountant <https://github.com/dulacp/Accountant>`__, a concrete
integration of the *django-accounting* application that you can deploy
with one click to Heroku.

Requirements
------------

-  Python 3.x
-  Django 1.7+
-  `dj-static <https://github.com/kennethreitz/dj-static>`__

Features supported
------------------

*with inspiration from already existing very good services (Xero,
Freshbooks, etc)*

    Those crossed are not yet supported

Books
~~~~~

-  **Estimating** generate estimates that can lead to an invoice or not
-  **Invoicing** generate invoices
-  **Billing** share the maximum of logic with the invoicing system
-  **Payments** to track partial/complete payments of invoices and bills
-  **ExpenseClaim** for employees of organizations that used their
   personnal accounts
-  **Dashboard / Current balance** displayed the current balance
-  **Dashboard / Overdued invoices & bills** to track what's late

Clients
~~~~~~~

-  **Creation/Update**
-  [STRIKEOUT:**Deletion** inform the user of the cascade deletion of
   invoices and bills]
-  **Professional address** specify the address on the client model
-  **Linked to organization** to implicitly create the bill when
   cross-invoicing between organizations

Reports
~~~~~~~

-  **Profit and Loss** to know how much you've collected, and how much
   you've spent
-  **Tax Report** to know how much you need to keep for taxes
   declarations
-  **Payroll Report** to know how much you need to keep for payroll
   taxes
-  **Invoice details Report** to understand the calculations that lead
   to the tax report and the payroll report
-  [STRIKEOUT:**Expense claims Report** to compute what the company owes
   to whom quickly]

Contact
-------

| `Pierre Dulac <http://github.com/dulacp>`_
| `@_dulacp <https://twitter.com/_dulacp>`_

License
-------

Accounting is available under the MIT license. See the LICENSE file for
more info.

.. |Build Status| image:: https://travis-ci.org/dulacp/django-accounting.svg
   :target: https://travis-ci.org/dulacp/django-accounting
.. |Coverage Status| image:: https://coveralls.io/repos/dulacp/django-accounting/badge.svg
   :target: https://coveralls.io/r/dulacp/django-accounting


================================================
FILE: accounting/__init__.py
================================================
import os

# Use 'final' as the 4th element to indicate
# a full release

VERSION = (0, 2, 10)


def get_short_version():
    return '%s.%s' % (VERSION[0], VERSION[1])


def get_version():
    version = '%s.%s' % (VERSION[0], VERSION[1])
    # Append 3rd digit if > 0
    if VERSION[2]:
        version = '%s.%s' % (version, VERSION[2])
    return version


# Cheeky setting that allows each template to be accessible by two paths.
# Eg: the template 'accounting/templates/accounting/base.html' can be accessed
# via both 'base.html' and 'accounting/base.html'.  This allows Accounting's
# templates to be extended by templates with the same filename
ACCOUNTING_MAIN_TEMPLATE_DIR = os.path.join(
    os.path.dirname(os.path.abspath(__file__)), 'templates/accounting')


ACCOUNTING_APPS = (
    'accounting',
    'accounting.libs',
    'accounting.apps.connect',
    'accounting.apps.people',
    'accounting.apps.books',
    'accounting.apps.reports',

    # Third party apps that accounting depends on
    'bootstrap3',
    'django_select2',
    'datetimewidget',
)


ACCOUNTING_TEMPLATE_CONTEXT_PROCESSORS = (
    'accounting.apps.context_processors.metadata',
    'accounting.apps.books.context_processors.organizations',
)


ACCOUNTING_MIDDLEWARE_CLASSES = (
    'accounting.apps.books.middlewares.AutoSelectOrganizationMiddleware',
)


def get_apps():
    return ACCOUNTING_APPS


================================================
FILE: accounting/apps/__init__.py
================================================


================================================
FILE: accounting/apps/books/__init__.py
================================================
default_app_config = 'accounting.apps.books.apps.FrenchBooksConfig'


================================================
FILE: accounting/apps/books/admin.py
================================================
from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline

from . import models


@admin.register(models.Organization)
class OrganizationAdmin(admin.ModelAdmin):
    pass


@admin.register(models.TaxRate)
class TaxRateAdmin(admin.ModelAdmin):
    pass


class PaymentInline(GenericTabularInline):
    model = models.Payment
    extra = 1


class EstimateLineInline(admin.TabularInline):
    model = models.EstimateLine
    extra = 1


@admin.register(models.Estimate)
class EstimateAdmin(admin.ModelAdmin):
    inlines = (
        EstimateLineInline,
    )
    readonly = (
        'total_incl_tax', 'total_excl_tax',
    )


class InvoiceLineInline(admin.TabularInline):
    model = models.InvoiceLine
    extra = 1


@admin.register(models.Invoice)
class InvoiceAdmin(admin.ModelAdmin):
    inlines = (
        InvoiceLineInline,
        PaymentInline,
    )
    readonly = (
        'total_incl_tax', 'total_excl_tax',
    )


class BillLineInline(admin.TabularInline):
    model = models.BillLine
    extra = 1


@admin.register(models.Bill)
class BillAdmin(admin.ModelAdmin):
    inlines = (
        BillLineInline,
        PaymentInline,
    )
    readonly = (
        'total_incl_tax', 'total_excl_tax',
    )


class ExpenseClaimLineInline(admin.TabularInline):
    model = models.ExpenseClaimLine
    extra = 1


@admin.register(models.ExpenseClaim)
class ExpenseClaimAdmin(admin.ModelAdmin):
    inlines = (
        ExpenseClaimLineInline,
        PaymentInline,
    )
    readonly = (
        'total_incl_tax', 'total_excl_tax',
    )


@admin.register(models.Payment)
class PaymentAdmin(admin.ModelAdmin):
    pass


================================================
FILE: accounting/apps/books/apps.py
================================================
from django.apps import AppConfig


class FrenchBooksConfig(AppConfig):
    name = "accounting.apps.books"
    verbose_name = "Accounting Books"


================================================
FILE: accounting/apps/books/calculators.py
================================================
from decimal import Decimal as D

from accounting.libs.intervals import TimeInterval


class SalePaymentLineProcessed(object):
    sale = None
    payment = None
    amount_excl_tax = None
    tax_rate = None

    def __init__(self, sale, payment):
        self.sale = sale
        self.payment = payment
        self.amount_excl_tax = D('0')

    def process(self):
        for line in self.sale.lines.all():
            tax_rate = line.tax_rate
            line_factor = line.line_price_incl_tax / self.sale.total_incl_tax
            portion_amount = self.payment.amount * line_factor
            portion_amount_excl_tax = portion_amount / (D('1') + tax_rate.rate)

            if self.tax_rate is None:
                self.tax_rate = tax_rate
            elif self.tax_rate.pk != tax_rate.pk:
                raise NotImplementedError("the system doesn't support "
                                          "yet multiple tax rates "
                                          "into a same invoice")

            self.amount_excl_tax += portion_amount_excl_tax


class ProfitsLossCalculator(object):
    """
    Compute the profits value in the most effective way
    considering the sum type (collected / accurial)
    and the given period of time.
    """

    # TODO support Accurial sum
    # SUM_TYPE_ACCURIAL = 'accurial'
    SUM_TYPE_COLLECTED = 'collected'
    SUM_TYPE_CHOICES = (
        # SUM_TYPE_ACCURIAL,
        SUM_TYPE_COLLECTED,
    )

    organization = None

    def __init__(self, organization, sum_type=SUM_TYPE_COLLECTED,
                 start=None, end=None):
        assert sum_type in self.SUM_TYPE_CHOICES, "Not a supported sum type"
        self.organization = organization
        self.period = TimeInterval(start=start, end=end)

    def process_generator(self, sales_queryset):
        """
        Generator that computes the profits/loss for each sale payment objects

        It's a complex machine because of the partial payments that need
        to be taken into account with the sum type Collected.

        So it yield a tuple similar to

            (sale, payment, amount)

        """
        sales_queryset = sales_queryset.filter(organization=self.organization)

        if self.period.start:
            sales_queryset = (sales_queryset
                .filter(payments__date_paid__gte=self.period.start))

        if self.period.end:
            sales_queryset = (sales_queryset
                .filter(payments__date_paid__lte=self.period.end))

        # optimize the queryset
        sales_queryset = (sales_queryset
            .prefetch_related(
                'lines',
                'lines__tax_rate',
                'payments')
            .distinct())

        for sale in sales_queryset:
            for pay in sale.payments.all():

                # NB: even with the queryset filters we can still get payments
                #     outside the period interval [start, end], because
                #     `self.payements.all()` is uncorrelated with the filters
                if self.period.start and pay.date_paid < self.period.start:
                    continue
                if self.period.end and pay.date_paid > self.period.end:
                    continue

                output = SalePaymentLineProcessed(sale, pay)
                output.process()

                yield output

    def total_collected(self):
        collected = D('0')
        invoices_queryset = self.organization.invoices.all()
        for output in self.process_generator(invoices_queryset):
            collected += output.amount_excl_tax
        return collected

    def total_expenses(self):
        expenses = D('0')
        bills_queryset = self.organization.bills.all()
        for output in self.process_generator(bills_queryset):
            expenses += output.amount_excl_tax
        return expenses

    def profits(self):
        return self.total_collected() - self.total_expenses()


================================================
FILE: accounting/apps/books/context_processors.py
================================================
from .utils import organization_manager


def organizations(request):
    """
    Add some generally useful metadata to the template context
    """
    # selected organization
    orga = organization_manager.get_selected_organization(request)

    # all user authorized organizations
    if not request.user or not request.user.is_authenticated():
        user_organizations = None
    else:
        user = request.user
        user_organizations = organization_manager.get_user_organizations(user)

    return {
        'user_organizations': user_organizations,
        'selected_organization': orga,
    }


================================================
FILE: accounting/apps/books/forms.py
================================================
from django.forms import ModelForm, BaseInlineFormSet
from django.forms.models import inlineformset_factory

from .models import (
    Organization,
    TaxRate,
    Estimate,
    EstimateLine,
    Invoice,
    InvoiceLine,
    Bill,
    BillLine,
    ExpenseClaim,
    ExpenseClaimLine,
    Payment)
from .utils import organization_manager
from accounting.apps.people.models import Client, Employee
from accounting.apps.people.forms import UserMultipleChoices

from django_select2.fields import AutoModelSelect2Field
from datetimewidget.widgets import DateWidget


class RequiredFirstInlineFormSet(BaseInlineFormSet):
    """
    Used to make empty formset forms required
    See http://stackoverflow.com/questions/2406537/django-formsets-\
        make-first-required/4951032#4951032
    """
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if len(self.forms) > 0:
            first_form = self.forms[0]
            first_form.empty_permitted = False


class SaleInlineLineFormSet(RequiredFirstInlineFormSet):

    def __init__(self, *args, **kwargs):
        orga = kwargs.pop('organization')
        super().__init__(*args, **kwargs)
        for f in self.forms:
            f.restrict_to_organization(orga)


class ClientForOrganizationChoices(AutoModelSelect2Field):
    queryset = Client.objects.all()
    search_fields = (
        'name__icontains',
    )

    def prepare_qs_params(self, request, search_term, search_fields):
        """restrict to the current selected organization"""
        params = super().prepare_qs_params(request, search_term, search_fields)
        orga = organization_manager.get_selected_organization(request)
        params['and']['organization'] = orga
        return params


class EmployeeForOrganizationChoices(AutoModelSelect2Field):
    queryset = Employee.objects.all()
    search_fields = (
        'first_name__icontains',
        'last_name__icontains',
        'email__icontains',
    )

    def prepare_qs_params(self, request, search_term, search_fields):
        """restrict to the current selected organization"""
        params = super().prepare_qs_params(request, search_term, search_fields)
        orga = organization_manager.get_selected_organization(request)
        params['and']['organization'] = orga
        return params


class OrganizationForm(ModelForm):
    members = UserMultipleChoices(required=False)

    class Meta:
        model = Organization
        fields = (
            "display_name",
            "legal_name",
            "members",
        )


class TaxRateForm(ModelForm):
    class Meta:
        model = TaxRate
        fields = (
            "name",
            "rate",
        )


class RestrictLineFormToOrganizationMixin(object):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        instance = kwargs.get('instance', None)
        if instance:
            if isinstance(instance, InvoiceLine):
                organization = instance.invoice.organization
            elif isinstance(instance, BillLine):
                organization = instance.bill.organization
            elif isinstance(instance, ExpenseClaimLine):
                organization = instance.expense_claim.organization
            else:
                raise NotImplementedError("The mixin has been applied to a "
                                          "form model that is not supported")
            self.restrict_to_organization(organization)

    def restrict_to_organization(self, organization):
        self.fields['tax_rate'].queryset = organization.tax_rates.all()


class EstimateForm(ModelForm):
    client = ClientForOrganizationChoices()

    class Meta:
        model = Estimate
        fields = (
            "number",
            "client",
            "date_issued",
            "date_dued",
        )
        widgets = {
            'date_issued': DateWidget(
                attrs={'id': "id_date_issued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
            'date_dued': DateWidget(
                attrs={'id': "id_date_dued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
        }


class EstimateLineForm(RestrictLineFormToOrganizationMixin,
                       ModelForm):
    class Meta:
        model = EstimateLine
        fields = (
            "label",
            "description",
            "unit_price_excl_tax",
            "quantity",
            "tax_rate",
        )


EstimateLineFormSet = inlineformset_factory(Estimate,
                                            EstimateLine,
                                            form=EstimateLineForm,
                                            formset=SaleInlineLineFormSet,
                                            min_num=1,
                                            extra=0)


class InvoiceForm(ModelForm):
    client = ClientForOrganizationChoices()

    class Meta:
        model = Invoice
        fields = (
            "number",
            "client",
            "date_issued",
            "date_dued",
        )
        widgets = {
            'date_issued': DateWidget(
                attrs={'id': "id_date_issued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
            'date_dued': DateWidget(
                attrs={'id': "id_date_dued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
        }


class InvoiceLineForm(RestrictLineFormToOrganizationMixin,
                      ModelForm):
    class Meta:
        model = InvoiceLine
        fields = (
            "label",
            "description",
            "unit_price_excl_tax",
            "quantity",
            "tax_rate",
        )


InvoiceLineFormSet = inlineformset_factory(Invoice,
                                           InvoiceLine,
                                           form=InvoiceLineForm,
                                           formset=SaleInlineLineFormSet,
                                           min_num=1,
                                           extra=0)


class BillForm(ModelForm):
    client = ClientForOrganizationChoices()

    class Meta:
        model = Bill
        fields = (
            "number",
            "client",
            "date_issued",
            "date_dued",
        )
        widgets = {
            'date_issued': DateWidget(
                attrs={'id': "id_date_issued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
            'date_dued': DateWidget(
                attrs={'id': "id_date_dued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
        }


class BillLineForm(RestrictLineFormToOrganizationMixin,
                   ModelForm):
    class Meta:
        model = BillLine
        fields = (
            "label",
            "description",
            "unit_price_excl_tax",
            "quantity",
            "tax_rate",
        )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


BillLineFormSet = inlineformset_factory(Bill,
                                        BillLine,
                                        form=BillLineForm,
                                        formset=SaleInlineLineFormSet,
                                        min_num=1,
                                        extra=0)


class ExpenseClaimForm(ModelForm):
    employee = EmployeeForOrganizationChoices()

    class Meta:
        model = ExpenseClaim
        fields = (
            "number",
            "employee",
            "date_issued",
            "date_dued",
        )
        widgets = {
            'date_issued': DateWidget(
                attrs={'id': "id_date_issued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
            'date_dued': DateWidget(
                attrs={'id': "id_date_dued"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3),
        }


class ExpenseClaimLineForm(RestrictLineFormToOrganizationMixin,
                           ModelForm):
    class Meta:
        model = ExpenseClaimLine
        fields = (
            "label",
            "description",
            "unit_price_excl_tax",
            "quantity",
            "tax_rate",
        )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)


ExpenseClaimLineFormSet = inlineformset_factory(ExpenseClaim,
                                                ExpenseClaimLine,
                                                form=ExpenseClaimLineForm,
                                                formset=SaleInlineLineFormSet,
                                                min_num=1,
                                                extra=0)


class PaymentForm(ModelForm):
    class Meta:
        model = Payment
        fields = (
            "amount",
            "reference",
            "detail",
            "date_paid",
        )
        widgets = {
            'date_paid': DateWidget(
                attrs={'id': "id_date_paid"},
                options={'clearBtn': 'false'},
                usel10n=True,
                bootstrap_version=3,),
        }


================================================
FILE: accounting/apps/books/managers.py
================================================
from datetime import date

from django.db import models
from django.db.models import Sum


class TotalQuerySetMixin(object):

    def _get_total(self, prop):
        return self.aggregate(sum=Sum(prop))["sum"]

    def total_paid(self):
        return self._get_total('payments__amount')


class InvoiceQuerySetMixin(object):

    def dued(self):
        return self.filter(date_dued__lte=date.today())


class EstimateQuerySet(TotalQuerySetMixin, models.QuerySet):
    pass


class InvoiceQuerySet(TotalQuerySetMixin,
                      InvoiceQuerySetMixin,
                      models.QuerySet):

    def turnover_excl_tax(self):
        return self._get_total('total_excl_tax')

    def turnover_incl_tax(self):
        return self._get_total('total_incl_tax')


class BillQuerySet(TotalQuerySetMixin,
                   InvoiceQuerySetMixin,
                   models.QuerySet):

    def debts_excl_tax(self):
        return self._get_total('total_excl_tax')

    def debts_incl_tax(self):
        return self._get_total('total_incl_tax')


class ExpenseClaimQuerySet(TotalQuerySetMixin,
                           InvoiceQuerySetMixin,
                           models.QuerySet):

    def debts_excl_tax(self):
        return self._get_total('total_excl_tax')

    def debts_incl_tax(self):
        return self._get_total('total_incl_tax')


================================================
FILE: accounting/apps/books/middlewares.py
================================================
from .utils import organization_manager


class AutoSelectOrganizationMiddleware(object):

    def process_request(self, request):
        if not request.user or not request.user.is_authenticated():
            return

        orga = organization_manager.get_selected_organization(request)
        if orga is not None:
            return

        user_orgas = organization_manager.get_user_organizations(request.user)
        if user_orgas.count():
            orga = user_orgas.first()
            organization_manager.set_selected_organization(request, orga)


================================================
FILE: accounting/apps/books/migrations/0001_initial.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import accounting.apps.books.utils
from django.conf import settings
import datetime
import accounting.libs.checks
import django.core.validators
from decimal import Decimal


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Organization',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, primary_key=True, auto_created=True)),
                ('display_name', models.CharField(help_text='Name that you communicate', max_length=150)),
                ('legal_name', models.CharField(help_text='Official name to appear on your reports, sales invoices and bills', max_length=150)),
                ('members', models.ManyToManyField(null=True, blank=True, related_name='organizations', to=settings.AUTH_USER_MODEL)),
                ('owner', models.ForeignKey(related_name='owned_organizations', to=settings.AUTH_USER_MODEL)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0002_auto_20141029_1606.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import django.core.validators
import accounting.apps.books.utils
from decimal import Decimal
import datetime
import accounting.libs.checks


def next_invoice_number():
    return 100


class Migration(migrations.Migration):

    dependencies = [
        ('contenttypes', '0001_initial'),
        ('books', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='Bill',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('number', models.CharField(default=next_invoice_number, max_length=6)),
                ('total_incl_tax', models.DecimalField(decimal_places=2, default=Decimal('0'), verbose_name='Total (inc. tax)', max_digits=12)),
                ('total_excl_tax', models.DecimalField(decimal_places=2, default=Decimal('0'), verbose_name='Total (excl. tax)', max_digits=12)),
                ('draft', models.BooleanField(default=False)),
                ('sent', models.BooleanField(default=False)),
                ('paid', models.BooleanField(default=False)),
                ('date_issued', models.DateField(default=datetime.date.today)),
                ('date_dued', models.DateField(null=True, help_text='The date when the total amount should have been collected', verbose_name='Due date', blank=True)),
                ('date_paid', models.DateField(null=True, blank=True)),
            ],
            options={
                'ordering': ('-date_issued', 'id'),
            },
            bases=(accounting.libs.checks.CheckingModelMixin, models.Model),
        ),
        migrations.CreateModel(
            name='BillLine',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('label', models.CharField(max_length=255)),
                ('description', models.TextField(null=True, blank=True)),
                ('unit_price_excl_tax', models.DecimalField(decimal_places=2, max_digits=8)),
                ('quantity', models.DecimalField(decimal_places=2, default=1, max_digits=8)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='Estimate',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('number', models.CharField(default=next_invoice_number, max_length=6)),
                ('total_incl_tax', models.DecimalField(decimal_places=2, default=Decimal('0'), verbose_name='Total (inc. tax)', max_digits=12)),
                ('total_excl_tax', models.DecimalField(decimal_places=2, default=Decimal('0'), verbose_name='Total (excl. tax)', max_digits=12)),
                ('draft', models.BooleanField(default=False)),
                ('sent', models.BooleanField(default=False)),
                ('paid', models.BooleanField(default=False)),
                ('date_issued', models.DateField(default=datetime.date.today)),
                ('date_dued', models.DateField(null=True, help_text='The date when the total amount should have been collected', verbose_name='Due date', blank=True)),
                ('date_paid', models.DateField(null=True, blank=True)),
            ],
            options={
                'ordering': ('-date_issued', 'id'),
            },
            bases=(accounting.libs.checks.CheckingModelMixin, models.Model),
        ),
        migrations.CreateModel(
            name='EstimateLine',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('label', models.CharField(max_length=255)),
                ('description', models.TextField(null=True, blank=True)),
                ('unit_price_excl_tax', models.DecimalField(decimal_places=2, max_digits=8)),
                ('quantity', models.DecimalField(decimal_places=2, default=1, max_digits=8)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='Invoice',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('number', models.CharField(default=next_invoice_number, max_length=6)),
                ('total_incl_tax', models.DecimalField(decimal_places=2, default=Decimal('0'), verbose_name='Total (inc. tax)', max_digits=12)),
                ('total_excl_tax', models.DecimalField(decimal_places=2, default=Decimal('0'), verbose_name='Total (excl. tax)', max_digits=12)),
                ('draft', models.BooleanField(default=False)),
                ('sent', models.BooleanField(default=False)),
                ('paid', models.BooleanField(default=False)),
                ('date_issued', models.DateField(default=datetime.date.today)),
                ('date_dued', models.DateField(null=True, help_text='The date when the total amount should have been collected', verbose_name='Due date', blank=True)),
                ('date_paid', models.DateField(null=True, blank=True)),
            ],
            options={
                'ordering': ('-date_issued', 'id'),
            },
            bases=(accounting.libs.checks.CheckingModelMixin, models.Model),
        ),
        migrations.CreateModel(
            name='InvoiceLine',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('label', models.CharField(max_length=255)),
                ('description', models.TextField(null=True, blank=True)),
                ('unit_price_excl_tax', models.DecimalField(decimal_places=2, max_digits=8)),
                ('quantity', models.DecimalField(decimal_places=2, default=1, max_digits=8)),
                ('invoice', models.ForeignKey(to='books.Invoice', related_name='lines')),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='Payment',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('amount', models.DecimalField(decimal_places=2, verbose_name='Amount', max_digits=12)),
                ('detail', models.CharField(null=True, blank=True, max_length=255)),
                ('date_paid', models.DateField(default=datetime.date.today)),
                ('reference', models.CharField(null=True, blank=True, max_length=255)),
                ('object_id', models.PositiveIntegerField()),
                ('content_type', models.ForeignKey(to='contenttypes.ContentType')),
            ],
            options={
                'ordering': ('-date_paid',),
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='TaxRate',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('name', models.CharField(max_length=50)),
                ('rate', models.DecimalField(decimal_places=5, validators=[django.core.validators.MinValueValidator(Decimal('0')), django.core.validators.MaxValueValidator(Decimal('1'))], max_digits=6)),
                ('organization', models.ForeignKey(related_name='tax_rates', to='books.Organization', verbose_name='Attached to Organization')),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.AddField(
            model_name='invoiceline',
            name='tax_rate',
            field=models.ForeignKey(to='books.TaxRate'),
            preserve_default=True,
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0003_auto_20141029_1606.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0002_auto_20141029_1606'),
        ('people', '0001_initial'),
    ]

    operations = [
        migrations.AddField(
            model_name='invoice',
            name='client',
            field=models.ForeignKey(to='people.Client', verbose_name='To Client'),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='invoice',
            name='organization',
            field=models.ForeignKey(related_name='invoices', to='books.Organization', verbose_name='From Organization'),
            preserve_default=True,
        ),
        migrations.AlterUniqueTogether(
            name='invoice',
            unique_together=set([('number', 'organization')]),
        ),
        migrations.AddField(
            model_name='estimateline',
            name='invoice',
            field=models.ForeignKey(to='books.Estimate', related_name='lines'),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='estimateline',
            name='tax_rate',
            field=models.ForeignKey(to='books.TaxRate'),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='estimate',
            name='client',
            field=models.ForeignKey(to='people.Client', verbose_name='To Client'),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='estimate',
            name='organization',
            field=models.ForeignKey(related_name='estimates', to='books.Organization', verbose_name='From Organization'),
            preserve_default=True,
        ),
        migrations.AlterUniqueTogether(
            name='estimate',
            unique_together=set([('number', 'organization')]),
        ),
        migrations.AddField(
            model_name='billline',
            name='bill',
            field=models.ForeignKey(to='books.Bill', related_name='lines'),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='billline',
            name='tax_rate',
            field=models.ForeignKey(to='books.TaxRate'),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='bill',
            name='client',
            field=models.ForeignKey(to='people.Client', verbose_name='From Client'),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='bill',
            name='organization',
            field=models.ForeignKey(related_name='bills', to='books.Organization', verbose_name='To Organization'),
            preserve_default=True,
        ),
        migrations.AlterUniqueTogether(
            name='bill',
            unique_together=set([('number', 'organization')]),
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0004_auto_20141104_1026.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import accounting.apps.books.utils


def next_invoice_number():
    return 100


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0003_auto_20141029_1606'),
    ]

    operations = [
        migrations.AlterModelOptions(
            name='bill',
            options={'ordering': ('-number',)},
        ),
        migrations.AlterModelOptions(
            name='estimate',
            options={'ordering': ('-number',)},
        ),
        migrations.AlterModelOptions(
            name='invoice',
            options={'ordering': ('-number',)},
        ),
        migrations.AlterField(
            model_name='bill',
            name='number',
            field=models.CharField(max_length=6, db_index=True, default=next_invoice_number),
        ),
        migrations.AlterField(
            model_name='estimate',
            name='number',
            field=models.CharField(max_length=6, db_index=True, default=next_invoice_number),
        ),
        migrations.AlterField(
            model_name='invoice',
            name='number',
            field=models.CharField(max_length=6, db_index=True, default=next_invoice_number),
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0005_auto_20150128_1458.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0004_auto_20141104_1026'),
    ]

    operations = [
        migrations.AlterField(
            model_name='bill',
            name='number',
            field=models.CharField(default=1, max_length=6, db_index=True),
        ),
        migrations.AlterField(
            model_name='estimate',
            name='number',
            field=models.CharField(default=1, max_length=6, db_index=True),
        ),
        migrations.AlterField(
            model_name='invoice',
            name='number',
            field=models.CharField(default=1, max_length=6, db_index=True),
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0006_auto_20150206_1836.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0005_auto_20150128_1458'),
    ]

    operations = [
        migrations.AddField(
            model_name='bill',
            name='number_int',
            field=models.IntegerField(default=1, db_index=True),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='estimate',
            name='number_int',
            field=models.IntegerField(default=1, db_index=True),
            preserve_default=True,
        ),
        migrations.AddField(
            model_name='invoice',
            name='number_int',
            field=models.IntegerField(default=1, db_index=True),
            preserve_default=True,
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0007_auto_20150206_1836.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


def _migrate_sale_numbers(sales):
    for s in sales:
        s.number_int = int(s.number.strip())
        s.save()


def migrate_estimate_numbers(apps, schema_editor):
    Estimate = apps.get_model("books", "Estimate")
    _migrate_sale_numbers(Estimate.objects.all())


def migrate_invoice_numbers(apps, schema_editor):
    Invoice = apps.get_model("books", "Invoice")
    _migrate_sale_numbers(Invoice.objects.all())


def migrate_bill_numbers(apps, schema_editor):
    Bill = apps.get_model("books", "Bill")
    _migrate_sale_numbers(Bill.objects.all())


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0006_auto_20150206_1836'),
    ]

    operations = [
        migrations.RunPython(migrate_estimate_numbers),
        migrations.RunPython(migrate_invoice_numbers),
        migrations.RunPython(migrate_bill_numbers)
    ]


================================================
FILE: accounting/apps/books/migrations/0008_auto_20150206_1843.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0007_auto_20150206_1836'),
    ]

    operations = [
        migrations.AlterModelOptions(
            name='bill',
            options={'ordering': ('-number_int',)},
        ),
        migrations.AlterModelOptions(
            name='estimate',
            options={'ordering': ('-number_int',)},
        ),
        migrations.AlterModelOptions(
            name='invoice',
            options={'ordering': ('-number_int',)},
        ),
        migrations.AlterUniqueTogether(
            name='bill',
            unique_together=set([('number_int', 'organization')]),
        ),
        migrations.RemoveField(
            model_name='bill',
            name='number',
        ),
        migrations.AlterUniqueTogether(
            name='estimate',
            unique_together=set([('number_int', 'organization')]),
        ),
        migrations.RemoveField(
            model_name='estimate',
            name='number',
        ),
        migrations.AlterUniqueTogether(
            name='invoice',
            unique_together=set([('number_int', 'organization')]),
        ),
        migrations.RemoveField(
            model_name='invoice',
            name='number',
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0009_auto_20150206_1850.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0008_auto_20150206_1843'),
    ]

    operations = [
        migrations.AlterModelOptions(
            name='bill',
            options={'ordering': ('-number',)},
        ),
        migrations.AlterModelOptions(
            name='estimate',
            options={'ordering': ('-number',)},
        ),
        migrations.AlterModelOptions(
            name='invoice',
            options={'ordering': ('-number',)},
        ),
        migrations.RenameField(
            model_name='bill',
            old_name='number_int',
            new_name='number',
        ),
        migrations.RenameField(
            model_name='estimate',
            old_name='number_int',
            new_name='number',
        ),
        migrations.RenameField(
            model_name='invoice',
            old_name='number_int',
            new_name='number',
        ),
        migrations.AlterUniqueTogether(
            name='bill',
            unique_together=set([('number', 'organization')]),
        ),
        migrations.AlterUniqueTogether(
            name='estimate',
            unique_together=set([('number', 'organization')]),
        ),
        migrations.AlterUniqueTogether(
            name='invoice',
            unique_together=set([('number', 'organization')]),
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0010_auto_20150210_1449.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0009_auto_20150206_1850'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='bill',
            name='draft',
        ),
        migrations.RemoveField(
            model_name='bill',
            name='paid',
        ),
        migrations.RemoveField(
            model_name='bill',
            name='sent',
        ),
        migrations.RemoveField(
            model_name='estimate',
            name='draft',
        ),
        migrations.RemoveField(
            model_name='estimate',
            name='paid',
        ),
        migrations.RemoveField(
            model_name='estimate',
            name='sent',
        ),
        migrations.RemoveField(
            model_name='invoice',
            name='draft',
        ),
        migrations.RemoveField(
            model_name='invoice',
            name='paid',
        ),
        migrations.RemoveField(
            model_name='invoice',
            name='sent',
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/0011_auto_20150324_1430.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from decimal import Decimal
import datetime
import accounting.libs.checks


class Migration(migrations.Migration):

    dependencies = [
        ('people', '0005_auto_20141029_2308'),
        ('books', '0010_auto_20150210_1449'),
    ]

    operations = [
        migrations.CreateModel(
            name='ExpenseClaim',
            fields=[
                ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
                ('number', models.IntegerField(db_index=True, default=1)),
                ('total_incl_tax', models.DecimalField(verbose_name='Total (inc. tax)', max_digits=12, default=Decimal('0'), decimal_places=2)),
                ('total_excl_tax', models.DecimalField(verbose_name='Total (excl. tax)', max_digits=12, default=Decimal('0'), decimal_places=2)),
                ('date_issued', models.DateField(default=datetime.date.today)),
                ('date_dued', models.DateField(verbose_name='Due date', blank=True, null=True, help_text='The date when the total amount should have been collected')),
                ('date_paid', models.DateField(blank=True, null=True)),
                ('employee', models.ForeignKey(verbose_name='Paid by employee', to='people.Employee')),
                ('organization', models.ForeignKey(related_name='expense_claims', to='books.Organization', verbose_name='From Organization')),
            ],
            options={
                'ordering': ('-number',),
            },
            bases=(accounting.libs.checks.CheckingModelMixin, models.Model),
        ),
        migrations.CreateModel(
            name='ExpenseClaimLine',
            fields=[
                ('id', models.AutoField(primary_key=True, auto_created=True, serialize=False, verbose_name='ID')),
                ('label', models.CharField(max_length=255)),
                ('description', models.TextField(blank=True, null=True)),
                ('unit_price_excl_tax', models.DecimalField(max_digits=8, decimal_places=2)),
                ('quantity', models.DecimalField(max_digits=8, default=1, decimal_places=2)),
                ('expense_claim', models.ForeignKey(related_name='lines', to='books.ExpenseClaim')),
                ('tax_rate', models.ForeignKey(to='books.TaxRate')),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.AlterUniqueTogether(
            name='expenseclaim',
            unique_together=set([('number', 'organization')]),
        ),
    ]


================================================
FILE: accounting/apps/books/migrations/__init__.py
================================================


================================================
FILE: accounting/apps/books/mixins.py
================================================
from django.db.models.fields import FieldDoesNotExist
from django.views import generic
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from .utils import organization_manager


class RestrictToSelectedOrganizationQuerySetMixin(object):
    """
    To restrict objects to the current selected organization
    """

    def get_restriction_filters(self):
        # check for the field
        meta = self.model._meta
        field, model, direct, m2m = meta.get_field_by_name('organization')

        # build the restriction
        orga = organization_manager.get_selected_organization(self.request)
        return {field.name: orga.pk}

    def get_queryset(self):
        filters = self.get_restriction_filters()
        queryset = super().get_queryset()
        queryset = queryset.filter(**filters)
        return queryset

    def get(self, request, *args, **kwargs):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return HttpResponseRedirect(reverse('books:organization-selector'))
        return super().get(request, *args, **kwargs)


class RestrictToOrganizationFormRelationsMixin(object):
    """
    To restrict relations choices to the organization linked instances
    """
    relation_name = 'organization'

    def _restrict_fields_choices(self, model, organization, fields):
        for source in fields:
            field, m, direct, m2m = model._meta.get_field_by_name(source)
            rel = field.rel
            if not rel:
                # next field
                continue

            rel_model = rel.to
            try:
                rel_model._meta.get_field_by_name(self.relation_name)
            except FieldDoesNotExist:
                # next field
                continue

            form_field = fields[source]
            form_field.queryset = (form_field.choices.queryset
                .filter(**{self.relation_name: organization}))

    def restrict_fields_choices_to_organization(self, form, organization):
        assert organization is not None, "no organization to restrict to"
        model = form._meta.model
        self._restrict_fields_choices(model, organization, form.fields)


class SaleListQuerySetMixin(object):

    def get_queryset(self):
        queryset = super().get_queryset()
        queryset = (queryset
            .select_related(
                'organization')
            .prefetch_related(
                'lines',
                'lines__tax_rate'))

        try:
            # to raise the exception
            self.model._meta.get_field_by_name('client')
            queryset = queryset.select_related('client')
        except FieldDoesNotExist:
            pass

        try:
            # to raise the exception
            self.model._meta.get_field_by_name('payments')
            queryset = queryset.prefetch_related('payments')
        except FieldDoesNotExist:
            pass

        return queryset


class AutoSetSelectedOrganizationMixin(object):

    def form_valid(self, form):
        obj = form.save(commit=False)
        orga = organization_manager.get_selected_organization(self.request)
        obj.organization = orga

        return super().form_valid(form)


class AbstractSaleCreateUpdateMixin(RestrictToOrganizationFormRelationsMixin,
                                    object):
    formset_class = None

    def get_context_data(self, **kwargs):
        assert self.formset_class is not None, "No formset class specified"
        context = super().get_context_data(**kwargs)
        orga = organization_manager.get_selected_organization(self.request)
        if self.request.POST:
            context['line_formset'] = self.formset_class(
                self.request.POST,
                instance=self.object,
                organization=orga)
        else:
            context['line_formset'] = self.formset_class(
                instance=self.object,
                organization=orga)
        return context

    def get_form(self, form_class=None):
        """Restrict the form relations to the current organization"""
        form = super().get_form(form_class)
        orga = organization_manager.get_selected_organization(self.request)
        self.restrict_fields_choices_to_organization(form, orga)
        return form

    def form_valid(self, form):
        context = self.get_context_data()
        line_formset = context['line_formset']
        if not line_formset.is_valid():
            return super().form_invalid(form)

        self.object = form.save()
        line_formset.instance = self.object
        line_formset.save()

        # update totals
        self.object.compute_totals()

        return super().form_valid(form)


class AbstractSaleDetailMixin(object):

    def get_queryset(self):
        queryset = super().get_queryset()
        queryset = queryset.select_related('organization')

        try:
            # to raise the exception
            self.model._meta.get_field_by_name('client')
            queryset = queryset.select_related('client')
        except FieldDoesNotExist:
            pass

        return queryset

    def get_object(self):
        # save some db queries by caching the fetched object
        if hasattr(self, '_object'):
            return getattr(self, '_object')

        obj = super().get_object()
        setattr(self, '_object', obj)
        return obj

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        obj = self.get_object()
        ctx["checklist"] = obj.full_check()
        ctx["lines"] = (obj.lines.all()
            .select_related(
                'tax_rate'))
        return ctx


class PaymentFormMixin(generic.edit.FormMixin):
    payment_form_class = None

    def get_context_data(self, **kwargs):
        assert self.payment_form_class is not None, \
            "No formset class specified"
        self.object = self.get_object()
        context = super().get_context_data(**kwargs)
        form = self.get_form(self.payment_form_class)
        context['payment_form'] = form
        return context

    def post(self, request, *args, **kwargs):
        """
        Handles POST requests, instantiating a form instance with the passed
        POST variables and then checked for validity.
        """
        form = self.get_form(self.payment_form_class)
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form):
        self.object = self.get_object()

        # save payment
        payment = form.save(commit=False)
        payment.content_object = self.object
        payment.save()
        return super().form_valid(form)


================================================
FILE: accounting/apps/books/models.py
================================================
from decimal import Decimal as D
from datetime import date

from django.conf import settings
from django.db import models
from django.core.urlresolvers import reverse
from django.core.validators import MaxValueValidator, MinValueValidator
from django.contrib.contenttypes.fields import (
    GenericForeignKey,
    GenericRelation)
from django.contrib.contenttypes.models import ContentType
from django.utils import timezone

from accounting.libs import prices
from accounting.libs.checks import CheckingModelMixin
from accounting.libs.templatetags.currency_filters import currency_formatter
from accounting.libs.templatetags.format_filters import percentage_formatter
from .managers import (
    EstimateQuerySet,
    InvoiceQuerySet,
    BillQuerySet,
    ExpenseClaimQuerySet)

TWO_PLACES = D(10) ** -2


class Organization(models.Model):
    display_name = models.CharField(max_length=150,
        help_text="Name that you communicate")
    legal_name = models.CharField(max_length=150,
        help_text="Official name to appear on your reports, sales "
                  "invoices and bills")

    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              related_name="owned_organizations")
    members = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                     related_name="organizations",
                                     blank=True, null=True)

    class Meta:
        pass

    def __str__(self):
        return self.legal_name

    def get_absolute_url(self):
        return reverse('books:organization-detail', args=[self.pk])

    @property
    def turnover_excl_tax(self):
        return self.invoices.turnover_excl_tax() or D('0.00')

    @property
    def turnover_incl_tax(self):
        return self.invoices.turnover_incl_tax() or D('0.00')

    @property
    def debts_excl_tax(self):
        return self.bills.debts_excl_tax() or D('0.00')

    @property
    def debts_incl_tax(self):
        return self.bills.debts_incl_tax() or D('0.00')

    @property
    def profits(self):
        return self.turnover_excl_tax - self.debts_excl_tax

    @property
    def collected_tax(self):
        return self.turnover_incl_tax - self.turnover_excl_tax

    @property
    def deductible_tax(self):
        return self.debts_incl_tax - self.debts_excl_tax

    @property
    def tax_provisionning(self):
        return self.collected_tax - self.deductible_tax

    @property
    def overdue_total(self):
        due_invoices = self.invoices.dued()
        due_turnonver = due_invoices.turnover_incl_tax()
        total_paid = due_invoices.total_paid()
        return due_turnonver - total_paid


class TaxRate(models.Model):
    """
    Every transaction line item needs a Tax Rate.
    Tax Rates can have multiple Tax Components.

    For instance, you can have an item that is charged a Tax Rate
    called "City Import Tax (8%)" that has two components:
        - a city tax of 5%
        - an import tax of 3%.

    *inspired by Xero*
    """
    organization = models.ForeignKey('books.Organization',
                                     related_name="tax_rates",
                                     verbose_name="Attached to Organization")

    name = models.CharField(max_length=50)
    rate = models.DecimalField(max_digits=6,
                               decimal_places=5,
                               validators=[MinValueValidator(D('0')),
                                           MaxValueValidator(D('1'))])

    class Meta:
        pass

    def __str__(self):
        return "{} ({})".format(self.name, percentage_formatter(self.rate))


class AbstractSale(CheckingModelMixin, models.Model):
    number = models.IntegerField(default=1,
                                 db_index=True)

    # Total price needs to be stored with and wihtout taxes
    # because the tax percentage can vary depending on the associated lines
    total_incl_tax = models.DecimalField("Total (inc. tax)",
                                         decimal_places=2,
                                         max_digits=12,
                                         default=D('0'))
    total_excl_tax = models.DecimalField("Total (excl. tax)",
                                         decimal_places=2,
                                         max_digits=12,
                                         default=D('0'))

    # tracking
    date_issued = models.DateField(default=date.today)
    date_dued = models.DateField("Due date",
                                 blank=True, null=True,
                                 help_text="The date when the total amount "
                                           "should have been collected")
    date_paid = models.DateField(blank=True, null=True)

    class Meta:
        abstract = True

    class CheckingOptions:
        fields = (
            'total_incl_tax',
            'total_excl_tax',
            'date_dued',
        )

    def __str__(self):
        return "#{} ({})".format(self.number, self.total_incl_tax)

    def get_detail_url(self):
        raise NotImplementedError

    def get_edit_url(self):
        raise NotImplementedError

    def compute_totals(self):
        self.total_excl_tax = self.get_total_excl_tax()
        self.total_incl_tax = self.get_total_incl_tax()

    def _get_total(self, prop):
        """
        For executing a named method on each line of the basket
        and returning the total.
        """
        total = D('0.00')
        line_queryset = self.lines.all()
        for line in line_queryset:
            total = total + getattr(line, prop)
        return total

    @property
    def total_tax(self):
        return self.total_incl_tax - self.total_excl_tax

    def get_total_excl_tax(self):
        return self._get_total('line_price_excl_tax')

    def get_total_incl_tax(self):
        return self._get_total('line_price_incl_tax')

    @property
    def total_paid(self):
        total = D('0')
        for p in self.payments.all():
            total += p.amount
        return total

    @property
    def total_due_incl_tax(self):
        due = self.total_incl_tax
        due -= self.total_paid
        return due

    def is_fully_paid(self):
        paid = self.total_paid.quantize(TWO_PLACES)
        total = self.total_incl_tax.quantize(TWO_PLACES)
        return paid >= total

    def is_partially_paid(self):
        paid = self.total_paid.quantize(TWO_PLACES)
        total = self.total_incl_tax.quantize(TWO_PLACES)
        return paid and paid > 0 and paid < total

    @property
    def payroll_taxes(self):
        # TODO implement collected/accurial
        paid = self.total_paid
        payroll = D('0')
        for emp in self.organization.employees.all():
            if not emp.salary_follows_profits:
                continue
            payroll += paid * emp.shares_percentage * emp.payroll_tax_rate
        return payroll

    def _check_total(self, check, total, computed_total):
        if total.quantize(TWO_PLACES) != computed_total.quantize(TWO_PLACES):
            check.mark_fail(level=check.LEVEL_ERROR,
                            message="The computed amount isn't correct, it "
                                    "should be {}, please edit and save the "
                                    "{} to fix it.".format(
                                        currency_formatter(total),
                                        self._meta.verbose_name))
        else:
            check.mark_pass()
        return check

    def check_total_excl_tax(self, check):
        total = self.get_total_excl_tax()
        return self._check_total(check, total, self.total_excl_tax)

    def check_total_incl_tax(self, check):
        total = self.get_total_incl_tax()
        return self._check_total(check, total, self.total_incl_tax)

    def check_date_dued(self, check):
        if self.date_dued is None:
            check.mark_fail(message="No due date specified")
            return check

        if self.total_excl_tax == D('0'):
            check.mark_fail(message="The invoice has no value")
            return check

        if self.is_fully_paid():
            last_payment = self.payments.all().first()
            formatted_date = last_payment.date_paid.strftime('%B %d, %Y')
            check.mark_pass(message="Has been paid on the {}"
                .format(formatted_date))
            return check

        if timezone.now().date() > self.date_dued:
            check.mark_fail(message="The due date has been exceeded.")
        else:
            check.mark_pass()
        return check


class AbstractSaleLine(models.Model):
    label = models.CharField(max_length=255)
    description = models.TextField(blank=True, null=True)
    unit_price_excl_tax = models.DecimalField(max_digits=8,
                                              decimal_places=2)
    quantity = models.DecimalField(max_digits=8,
                                   decimal_places=2,
                                   default=1)

    class Meta:
        abstract = True

    def __str__(self):
        return self.label

    @property
    def unit_price(self):
        """Returns the `Price` instance representing the instance"""
        unit = self.unit_price_excl_tax
        tax = unit * self.tax_rate.rate
        p = prices.Price(settings.ACCOUNTING_DEFAULT_CURRENCY, unit, tax=tax)
        return p

    @property
    def line_price_excl_tax(self):
        return self.quantity * self.unit_price.excl_tax

    @property
    def line_price_incl_tax(self):
        return self.quantity * self.unit_price.incl_tax

    @property
    def taxes(self):
        return self.line_price_incl_tax - self.line_price_excl_tax

    def from_client(self):
        raise NotImplementedError

    def to_client(self):
        raise NotImplementedError


class Estimate(AbstractSale):
    organization = models.ForeignKey('books.Organization',
                                     related_name="estimates",
                                     verbose_name="From Organization")
    client = models.ForeignKey('people.Client',
                               verbose_name="To Client")

    objects = EstimateQuerySet.as_manager()

    class Meta:
        unique_together = (("number", "organization"),)
        ordering = ('-number',)

    def get_detail_url(self):
        return reverse('books:estimate-detail', args=[self.pk])

    def get_edit_url(self):
        return reverse('books:estimate-edit', args=[self.pk])

    def from_client(self):
        return self.organization

    def to_client(self):
        return self.client


class EstimateLine(AbstractSaleLine):
    invoice = models.ForeignKey('books.Estimate',
                                related_name="lines")
    tax_rate = models.ForeignKey('books.TaxRate')

    class Meta:
        pass


class Invoice(AbstractSale):
    organization = models.ForeignKey('books.Organization',
                                     related_name="invoices",
                                     verbose_name="From Organization")
    client = models.ForeignKey('people.Client',
                               verbose_name="To Client")
    payments = GenericRelation('books.Payment')

    objects = InvoiceQuerySet.as_manager()

    class Meta:
        unique_together = (("number", "organization"),)
        ordering = ('-number',)

    def get_detail_url(self):
        return reverse('books:invoice-detail', args=[self.pk])

    def get_edit_url(self):
        return reverse('books:invoice-edit', args=[self.pk])

    def from_client(self):
        return self.organization

    def to_client(self):
        return self.client


class InvoiceLine(AbstractSaleLine):
    invoice = models.ForeignKey('books.Invoice',
                                related_name="lines")
    tax_rate = models.ForeignKey('books.TaxRate')

    class Meta:
        pass


class Bill(AbstractSale):
    organization = models.ForeignKey('books.Organization',
                                     related_name="bills",
                                     verbose_name="To Organization")
    client = models.ForeignKey('people.Client',
                               verbose_name="From Client")
    payments = GenericRelation('books.Payment')

    objects = BillQuerySet.as_manager()

    class Meta:
        unique_together = (("number", "organization"),)
        ordering = ('-number',)

    def get_detail_url(self):
        return reverse('books:bill-detail', args=[self.pk])

    def get_edit_url(self):
        return reverse('books:bill-edit', args=[self.pk])

    def from_client(self):
        return self.client

    def to_client(self):
        return self.organization


class BillLine(AbstractSaleLine):
    bill = models.ForeignKey('books.Bill',
                             related_name="lines")
    tax_rate = models.ForeignKey('books.TaxRate')

    class Meta:
        pass


class ExpenseClaim(AbstractSale):
    organization = models.ForeignKey('books.Organization',
                                     related_name="expense_claims",
                                     verbose_name="From Organization")
    employee = models.ForeignKey('people.Employee',
                                 verbose_name="Paid by employee")
    payments = GenericRelation('books.Payment')

    objects = ExpenseClaimQuerySet.as_manager()

    class Meta:
        unique_together = (("number", "organization"),)
        ordering = ('-number',)

    def get_detail_url(self):
        return reverse('books:expense_claim-detail', args=[self.pk])

    def get_edit_url(self):
        return reverse('books:expense_claim-edit', args=[self.pk])

    def from_client(self):
        return self.employee

    def to_client(self):
        return self.organization


class ExpenseClaimLine(AbstractSaleLine):
    expense_claim = models.ForeignKey('books.ExpenseClaim',
                                      related_name="lines")
    tax_rate = models.ForeignKey('books.TaxRate')

    class Meta:
        pass


class Payment(models.Model):
    amount = models.DecimalField("Amount",
                                 decimal_places=2,
                                 max_digits=12)
    detail = models.CharField(max_length=255,
                              blank=True,
                              null=True)
    date_paid = models.DateField(default=date.today)
    reference = models.CharField(max_length=255,
                                 blank=True,
                                 null=True)

    # relationship to an object
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        ordering = ('-date_paid',)

    def __str__(self):
        if self.detail:
            return self.detail
        return "Payment of {}".format(currency_formatter(self.amount))


================================================
FILE: accounting/apps/books/templatetags/__init__.py
================================================


================================================
FILE: accounting/apps/books/templatetags/status_filters.py
================================================
from django import template

register = template.Library()


@register.filter('status_to_css_classname')
def _invoice_or_bill_status_to_classname(invoice_or_bill):
    """
    Return the appropriated css classname for the invoice/bill status
    """
    if not invoice_or_bill.pass_full_checking():
        checks = invoice_or_bill.full_check()
        for c in checks:
            if c.level == c.LEVEL_ERROR:
                return 'danger'
        return 'warning'

    if invoice_or_bill.is_fully_paid():
        return 'success'
    elif invoice_or_bill.is_partially_paid():
        return 'info'
    else:
        return ''


================================================
FILE: accounting/apps/books/urls.py
================================================
from django.conf.urls import patterns, url

from . import views


urlpatterns = patterns('',
    url(r'^$',
        views.DashboardView.as_view(),
        name="dashboard"),

    # Organizations
    url(r'^organization/$',
        views.OrganizationListView.as_view(),
        name="organization-list"),
    url(r'^organization/selector/$',
        views.OrganizationSelectorView.as_view(),
        name="organization-selector"),
    url(r'^organization/create/$',
        views.OrganizationCreateView.as_view(),
        name="organization-create"),
    url(r'^organization/(?P<pk>\d+)/edit/$',
        views.OrganizationUpdateView.as_view(),
        name="organization-edit"),
    url(r'^organization/(?P<pk>\d+)/detail/$',
        views.OrganizationDetailView.as_view(),
        name="organization-detail"),
    url(r'^organization/(?P<pk>\d+)/select/$',
        views.OrganizationSelectionView.as_view(),
        name="organization-select"),

    # Tax Rates
    url(r'^tax_rates/$',
        views.TaxRateListView.as_view(),
        name="tax_rate-list"),
    url(r'^tax_rates/create/$',
        views.TaxRateCreateView.as_view(),
        name="tax_rate-create"),
    url(r'^tax_rates/(?P<pk>\d+)/edit/$',
        views.TaxRateUpdateView.as_view(),
        name="tax_rate-edit"),
    url(r'^tax_rates/(?P<pk>\d+)/delete/$',
        views.TaxRateDeleteView.as_view(),
        name="tax_rate-delete"),

    # Estimates
    url(r'^estimate/$',
        views.EstimateListView.as_view(),
        name="estimate-list"),
    url(r'^estimate/create/$',
        views.EstimateCreateView.as_view(),
        name="estimate-create"),
    url(r'^estimate/(?P<pk>\d+)/edit/$',
        views.EstimateUpdateView.as_view(),
        name="estimate-edit"),
    url(r'^estimate/(?P<pk>\d+)/delete/$',
        views.EstimateDeleteView.as_view(),
        name="estimate-delete"),
    url(r'^estimate/(?P<pk>\d+)/detail/$',
        views.EstimateDetailView.as_view(),
        name="estimate-detail"),

    # Invoices
    url(r'^invoice/$',
        views.InvoiceListView.as_view(),
        name="invoice-list"),
    url(r'^invoice/create/$',
        views.InvoiceCreateView.as_view(),
        name="invoice-create"),
    url(r'^invoice/(?P<pk>\d+)/edit/$',
        views.InvoiceUpdateView.as_view(),
        name="invoice-edit"),
    url(r'^invoice/(?P<pk>\d+)/delete/$',
        views.InvoiceDeleteView.as_view(),
        name="invoice-delete"),
    url(r'^invoice/(?P<pk>\d+)/detail/$',
        views.InvoiceDetailView.as_view(),
        name="invoice-detail"),

    # Bills
    url(r'^bill/$',
        views.BillListView.as_view(),
        name="bill-list"),
    url(r'^bill/create/$',
        views.BillCreateView.as_view(),
        name="bill-create"),
    url(r'^bill/(?P<pk>\d+)/edit/$',
        views.BillUpdateView.as_view(),
        name="bill-edit"),
    url(r'^bill/(?P<pk>\d+)/delete/$',
        views.BillDeleteView.as_view(),
        name="bill-delete"),
    url(r'^bill/(?P<pk>\d+)/detail/$',
        views.BillDetailView.as_view(),
        name="bill-detail"),

    # ExpenseClaims
    url(r'^expense-claim/$',
        views.ExpenseClaimListView.as_view(),
        name="expense_claim-list"),
    url(r'^expense-claim/create/$',
        views.ExpenseClaimCreateView.as_view(),
        name="expense_claim-create"),
    url(r'^expense-claim/(?P<pk>\d+)/edit/$',
        views.ExpenseClaimUpdateView.as_view(),
        name="expense_claim-edit"),
    url(r'^expense-claim/(?P<pk>\d+)/delete/$',
        views.ExpenseClaimDeleteView.as_view(),
        name="expense_claim-delete"),
    url(r'^expense-claim/(?P<pk>\d+)/detail/$',
        views.ExpenseClaimDetailView.as_view(),
        name="expense_claim-detail"),

    # Payments
    url(r'^payment/(?P<pk>\d+)/edit/$',
        views.PaymentUpdateView.as_view(),
        name="payment-edit"),
    url(r'^payment/(?P<pk>\d+)/delete/$',
        views.PaymentDeleteView.as_view(),
        name="payment-delete"),
)


================================================
FILE: accounting/apps/books/utils.py
================================================
from django.db.models import Q


class OrganizationManager(object):
    selected_organization_key = 'selected_organization_pk'

    def get_user_organizations(self, user):
        # To avoid circular imports
        from .models import Organization

        orgas = (Organization.objects
            .filter(Q(members=user) | Q(owner=user))
            .distinct())
        return orgas

    def set_selected_organization(self, request, organization):
        key = self.selected_organization_key
        request.session[key] = organization.pk

    def get_selected_organization(self, request):
        key = self.selected_organization_key
        if key not in request.session:
            return

        # To avoid circular imports
        from .models import Organization

        pk = request.session[key]
        organization = Organization.objects.get(pk=pk)
        return organization


organization_manager = OrganizationManager()


class BaseNumberGenerator(object):
    """
    Simple object for generating sale numbers.
    """

    def next_number(self, organization):
        raise NotImplementedError


class EstimateNumberGenerator(BaseNumberGenerator):

    def next_number(self, organization):
        last = organization.estimates.all().order_by('-number').first()
        if last is not None:
            last_number = int(last.number)
        else:
            last_number = 0
        return last_number + 1


class InvoiceNumberGenerator(BaseNumberGenerator):

    def next_number(self, organization):
        last = organization.invoices.all().order_by('-number').first()
        if last is not None:
            last_number = int(last.number)
        else:
            last_number = 0
        return last_number + 1


class BillNumberGenerator(BaseNumberGenerator):

    def next_number(self, organization):
        last = organization.bills.all().order_by('-number').first()
        if last is not None:
            last_number = int(last.number)
        else:
            last_number = 0
        return last_number + 1


class ExpenseClaimNumberGenerator(BaseNumberGenerator):

    def next_number(self, organization):
        last = organization.expense_claims.all().order_by('-number').first()
        if last is not None:
            last_number = int(last.number)
        else:
            last_number = 0
        return last_number + 1


================================================
FILE: accounting/apps/books/views.py
================================================
import logging
from decimal import Decimal as D

from django.views import generic
from django.core.urlresolvers import reverse, reverse_lazy
from django.db.models import Sum
from django.http import HttpResponseRedirect

from .mixins import (
    RestrictToSelectedOrganizationQuerySetMixin,
    SaleListQuerySetMixin,
    AutoSetSelectedOrganizationMixin,
    AbstractSaleCreateUpdateMixin,
    AbstractSaleDetailMixin,
    PaymentFormMixin)
from .models import (
    Organization,
    TaxRate,
    Estimate,
    Invoice,
    Bill,
    ExpenseClaim,
    Payment)
from .forms import (
    OrganizationForm,
    TaxRateForm,
    EstimateForm,
    EstimateLineFormSet,
    InvoiceForm,
    InvoiceLineFormSet,
    BillForm,
    BillLineFormSet,
    ExpenseClaimForm,
    ExpenseClaimLineFormSet,
    PaymentForm)
from .utils import (
    organization_manager,
    EstimateNumberGenerator,
    InvoiceNumberGenerator,
    BillNumberGenerator,
    ExpenseClaimNumberGenerator)

logger = logging.getLogger(__name__)


class OrganizationSelectorView(generic.TemplateView):
    template_name = "books/organization_selector.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        user = self.request.user
        orgas = organization_manager.get_user_organizations(user)
        cumulated_turnovers = (orgas
            .aggregate(sum=Sum('invoices__total_excl_tax'))["sum"]) or D('0')
        cumulated_debts = (orgas
            .aggregate(sum=Sum('bills__total_excl_tax'))["sum"]) or D('0')
        cumulated_profits = cumulated_turnovers - cumulated_debts

        context["organizations_count"] = orgas.count()
        context["organizations_cumulated_turnovers"] = cumulated_turnovers
        context["organizations_cumulated_profits"] = cumulated_profits
        context["organizations_cumulated_active_days"] = 0

        context["organizations"] = orgas
        context["last_invoices"] = Invoice.objects.all()[:10]

        return context


class DashboardView(generic.DetailView):
    template_name = "books/dashboard.html"
    model = Organization
    context_object_name = "organization"

    def get_object(self):
        return organization_manager.get_selected_organization(self.request)

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        organization = self.get_object()
        ctx['invoices'] = (organization.invoices.all()
            .select_related(
                'client',
                'organization')
            .prefetch_related(
                'lines',
                'lines__tax_rate',
                'payments')
            .distinct())
        ctx['bills'] = (organization.bills.all()
            .select_related(
                'client',
                'organization')
            .prefetch_related(
                'lines',
                'lines__tax_rate',
                'payments')
            .distinct())
        return ctx

    def get(self, request, *args, **kwargs):
        orga = organization_manager.get_selected_organization(self.request)
        if orga is None:
            return HttpResponseRedirect(reverse('books:organization-selector'))
        return super().get(request, *args, **kwargs)


class OrganizationListView(generic.ListView):
    template_name = "books/organization_list.html"
    model = Organization
    context_object_name = "organizations"

    def get_queryset(self):
        # only current authenticated user organizations
        return organization_manager.get_user_organizations(self.request.user)


class OrganizationCreateView(generic.CreateView):
    template_name = "books/organization_create_or_update.html"
    model = Organization
    form_class = OrganizationForm
    success_url = reverse_lazy("books:organization-list")

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.owner = self.request.user
        return super().form_valid(form)


class OrganizationUpdateView(generic.UpdateView):
    template_name = "books/organization_create_or_update.html"
    model = Organization
    form_class = OrganizationForm
    success_url = reverse_lazy("books:organization-list")

    def get_queryset(self):
        # only current authenticated user organizations
        return organization_manager.get_user_organizations(self.request.user)


class OrganizationDetailView(generic.DetailView):
    template_name = "books/organization_detail.html"
    model = Organization
    context_object_name = "organization"

    def get_queryset(self):
        # only current authenticated user organizations
        return organization_manager.get_user_organizations(self.request.user)

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        organization = self.get_object()
        ctx['invoices'] = (organization.invoices.all()
            .select_related('client', 'organization')
            .prefetch_related('lines'))
        ctx['bills'] = (organization.bills.all()
            .select_related('client', 'organization')
            .prefetch_related('lines'))
        return ctx


class OrganizationSelectionView(generic.DetailView):
    model = Organization

    def get_queryset(self):
        # only current authenticated user organizations
        return organization_manager.get_user_organizations(self.request.user)

    def post(self, request, *args, **kwargs):
        orga = self.get_object()
        organization_manager.set_selected_organization(self.request, orga)
        return HttpResponseRedirect(reverse('books:dashboard'))


class TaxRateListView(RestrictToSelectedOrganizationQuerySetMixin,
                      generic.ListView):
    template_name = "books/tax_rate_list.html"
    model = TaxRate
    context_object_name = "tax_rates"


class TaxRateCreateView(AutoSetSelectedOrganizationMixin,
                        generic.CreateView):
    template_name = "books/tax_rate_create_or_update.html"
    model = TaxRate
    form_class = TaxRateForm
    success_url = reverse_lazy("books:tax_rate-list")


class TaxRateUpdateView(AutoSetSelectedOrganizationMixin,
                        generic.UpdateView):
    template_name = "books/tax_rate_create_or_update.html"
    model = TaxRate
    form_class = TaxRateForm
    success_url = reverse_lazy("books:tax_rate-list")


class TaxRateDeleteView(generic.DeleteView):
    template_name = "_generics/delete_entity.html"
    model = TaxRate
    success_url = reverse_lazy('books:tax_rate-list')


class PaymentUpdateView(generic.UpdateView):
    template_name = "books/payment_create_or_update.html"
    model = Payment
    form_class = PaymentForm

    def get_success_url(self):
        related_obj = self.object.content_object
        if isinstance(related_obj, Invoice):
            return reverse("books:invoice-detail", args=[related_obj.pk])
        elif isinstance(related_obj, Bill):
            return reverse("books:bill-detail", args=[related_obj.pk])

        logger.warning("Unsupported related object '{}' for "
                       "payment '{}'".format(self.object, related_obj))
        return reverse("books:dashboard")


class PaymentDeleteView(generic.DeleteView):
    template_name = "_generics/delete_entity.html"
    model = Payment
    success_url = reverse_lazy('books:invoice-list')


class EstimateListView(RestrictToSelectedOrganizationQuerySetMixin,
                       SaleListQuerySetMixin,
                       generic.ListView):
    template_name = "books/estimate_list.html"
    model = Estimate
    context_object_name = "estimates"


class EstimateCreateView(AutoSetSelectedOrganizationMixin,
                         AbstractSaleCreateUpdateMixin,
                         generic.CreateView):
    template_name = "books/bill_create_or_update.html"
    model = Estimate
    form_class = EstimateForm
    formset_class = EstimateLineFormSet
    success_url = reverse_lazy("books:estimate-list")

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        orga = organization_manager.get_selected_organization(self.request)
        self.restrict_fields_choices_to_organization(form, orga)
        return form

    def get_initial(self):
        initial = super().get_initial()

        orga = organization_manager.get_selected_organization(self.request)
        initial['number'] = EstimateNumberGenerator().next_number(orga)

        return initial


class EstimateUpdateView(AutoSetSelectedOrganizationMixin,
                         AbstractSaleCreateUpdateMixin,
                         generic.UpdateView):
    template_name = "books/estimate_create_or_update.html"
    model = Estimate
    form_class = EstimateForm
    formset_class = EstimateLineFormSet
    success_url = reverse_lazy("books:estimate-list")


class EstimateDeleteView(generic.DeleteView):
    template_name = "_generics/delete_entity.html"
    model = Estimate
    success_url = reverse_lazy('books:estimate-list')


class EstimateDetailView(AbstractSaleDetailMixin,
                         generic.DetailView):
    template_name = "books/estimate_detail.html"
    model = Estimate
    context_object_name = "estimate"

    def get_success_url(self):
        return reverse('books:estimate-detail', args=[self.object.pk])


class InvoiceListView(RestrictToSelectedOrganizationQuerySetMixin,
                      SaleListQuerySetMixin,
                      generic.ListView):
    template_name = "books/invoice_list.html"
    model = Invoice
    context_object_name = "invoices"


class InvoiceCreateView(AutoSetSelectedOrganizationMixin,
                        AbstractSaleCreateUpdateMixin,
                        generic.CreateView):
    template_name = "books/invoice_create_or_update.html"
    model = Invoice
    form_class = InvoiceForm
    formset_class = InvoiceLineFormSet
    success_url = reverse_lazy("books:invoice-list")

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        orga = organization_manager.get_selected_organization(self.request)
        self.restrict_fields_choices_to_organization(form, orga)
        return form

    def get_initial(self):
        initial = super().get_initial()

        orga = organization_manager.get_selected_organization(self.request)
        initial['number'] = InvoiceNumberGenerator().next_number(orga)

        return initial


class InvoiceUpdateView(AutoSetSelectedOrganizationMixin,
                        AbstractSaleCreateUpdateMixin,
                        generic.UpdateView):
    template_name = "books/invoice_create_or_update.html"
    model = Invoice
    form_class = InvoiceForm
    formset_class = InvoiceLineFormSet
    success_url = reverse_lazy("books:invoice-list")


class InvoiceDeleteView(generic.DeleteView):
    template_name = "_generics/delete_entity.html"
    model = Invoice
    success_url = reverse_lazy('books:invoice-list')


class InvoiceDetailView(PaymentFormMixin,
                        AbstractSaleDetailMixin,
                        generic.DetailView):
    template_name = "books/invoice_detail.html"
    model = Invoice
    context_object_name = "invoice"
    payment_form_class = PaymentForm

    def get_success_url(self):
        return reverse('books:invoice-detail', args=[self.object.pk])


class BillListView(RestrictToSelectedOrganizationQuerySetMixin,
                   SaleListQuerySetMixin,
                   generic.ListView):
    template_name = "books/bill_list.html"
    model = Bill
    context_object_name = "bills"


class BillCreateView(AutoSetSelectedOrganizationMixin,
                     AbstractSaleCreateUpdateMixin,
                     generic.CreateView):
    template_name = "books/bill_create_or_update.html"
    model = Bill
    form_class = BillForm
    formset_class = BillLineFormSet
    success_url = reverse_lazy("books:bill-list")

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        orga = organization_manager.get_selected_organization(self.request)
        self.restrict_fields_choices_to_organization(form, orga)
        return form

    def get_initial(self):
        initial = super().get_initial()

        orga = organization_manager.get_selected_organization(self.request)
        initial['number'] = BillNumberGenerator().next_number(orga)

        return initial


class BillUpdateView(AutoSetSelectedOrganizationMixin,
                     AbstractSaleCreateUpdateMixin,
                     generic.UpdateView):
    template_name = "books/bill_create_or_update.html"
    model = Bill
    form_class = BillForm
    formset_class = BillLineFormSet
    success_url = reverse_lazy("books:bill-list")


class BillDeleteView(generic.DeleteView):
    template_name = "_generics/delete_entity.html"
    model = Bill
    success_url = reverse_lazy('books:bill-list')


class BillDetailView(PaymentFormMixin,
                     AbstractSaleDetailMixin,
                     generic.DetailView):
    template_name = "books/bill_detail.html"
    model = Bill
    context_object_name = "bill"
    payment_form_class = PaymentForm

    def get_success_url(self):
        return reverse('books:bill-detail', args=[self.object.pk])


class ExpenseClaimListView(RestrictToSelectedOrganizationQuerySetMixin,
                           SaleListQuerySetMixin,
                           generic.ListView):
    template_name = "books/expense_claim_list.html"
    model = ExpenseClaim
    context_object_name = "expense_claims"


class ExpenseClaimCreateView(AutoSetSelectedOrganizationMixin,
                             AbstractSaleCreateUpdateMixin,
                             generic.CreateView):
    template_name = "books/expense_claim_create_or_update.html"
    model = ExpenseClaim
    form_class = ExpenseClaimForm
    formset_class = ExpenseClaimLineFormSet
    success_url = reverse_lazy("books:expense_claim-list")

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        orga = organization_manager.get_selected_organization(self.request)
        self.restrict_fields_choices_to_organization(form, orga)
        return form

    def get_initial(self):
        initial = super().get_initial()

        orga = organization_manager.get_selected_organization(self.request)
        initial['number'] = ExpenseClaimNumberGenerator().next_number(orga)

        return initial


class ExpenseClaimUpdateView(AutoSetSelectedOrganizationMixin,
                             AbstractSaleCreateUpdateMixin,
                             generic.UpdateView):
    template_name = "books/expense_claim_create_or_update.html"
    model = ExpenseClaim
    form_class = ExpenseClaimForm
    formset_class = ExpenseClaimLineFormSet
    success_url = reverse_lazy("books:expense_claim-list")


class ExpenseClaimDeleteView(generic.DeleteView):
    template_name = "_generics/delete_entity.html"
    model = ExpenseClaim
    success_url = reverse_lazy('books:expense_claim-list')


class ExpenseClaimDetailView(PaymentFormMixin,
                             AbstractSaleDetailMixin,
                             generic.DetailView):
    template_name = "books/expense_claim_detail.html"
    model = ExpenseClaim
    context_object_name = "expense_claim"
    payment_form_class = PaymentForm

    def get_success_url(self):
        return reverse('books:expense_claim-detail', args=[self.object.pk])


================================================
FILE: accounting/apps/connect/__init__.py
================================================


================================================
FILE: accounting/apps/connect/middlewares.py
================================================
class ForceGettingStartedMiddleware(object):

    def process_request(self, request):
        pass


================================================
FILE: accounting/apps/connect/models.py
================================================


================================================
FILE: accounting/apps/connect/steps.py
================================================
import logging

from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError

from accounting.apps.books.utils import organization_manager
from accounting.apps.reports.models import (
    BusinessSettings,
    FinancialSettings,
    PayRunSettings)

logger = logging.getLogger(__name__)


class StepOptions(object):
    """
    Meta class options for a `BaseStep` subclass
    """
    def __init__(self, meta):
        self.name = getattr(meta, 'name', None)
        assert isinstance(self.name, str), \
            '`name` must be a string instance'
        self.description = getattr(meta, 'description', "")
        assert isinstance(self.description, str), \
            '`description` must be a string instance'


class BaseStep(object):
    """
    Abstract class to subclass to create a getting started step
    """
    user = None

    _completion = None
    _options_class = StepOptions

    class StepOptions:
        name = "<Abstract>"
        description = None

    def __init__(self, user):
        super().__init__()
        self.opts = self._options_class(getattr(self, 'StepOptions', None))
        self.user = user

    def completed(self, request):
        if self._completion is None:
            self._completion = self.check_completion(request)
        return self._completion

    def is_completed(self):
        """pre computed value, to be called in templates"""
        if self._completion is None:
            logger.error("`completed` needs to be run before using "
                         "this method")
            return False
        return self._completion

    def check_completion(self, request):
        """
        Implement the logic of the step
        and returns a boolean
        """
        raise NotImplementedError

    def get_action_url(self):
        """Returns the url to complete the step"""
        pass


class CreateOrganizationStep(BaseStep):
    """
    At least one organization has been created
    """

    class StepOptions:
        name = "Create an Organization"
        description = "the organization is the foundation of the accounting " \
                      "system, tell Accountant-x more about it"

    def check_completion(self, request):
        orgas = organization_manager.get_user_organizations(request.user)
        count = orgas.count()
        return count > 0

    def get_action_url(self):
        return reverse('books:organization-create')


class ConfigureTaxRatesStep(BaseStep):
    """
    At least one tax rate has been added (even if the rate is 0)
    """

    class StepOptions:
        name = "Configure Tax Rates"
        description = "even if you are not subject to tax collecting rules " \
                      "you should create a 0% tax entry"

    def check_completion(self, request):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return False
        count = orga.tax_rates.all().count()
        return count > 0

    def get_action_url(self):
        return reverse('books:tax_rate-create')


class ConfigureBusinessSettingsStep(BaseStep):
    """
    The associated business settings has been completed
    """

    class StepOptions:
        name = "Configure Business Settings"
        description = "for now there is not much thing, but please create it"

    def check_completion(self, request):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return False
        try:
            settings = orga.business_settings
            settings.full_clean()
        except BusinessSettings.DoesNotExist:
            return False
        except ValidationError:
            return False
        return True

    def get_action_url(self):
        return reverse('reports:settings-business')


class ConfigureFinancialSettingsStep(BaseStep):

    class StepOptions:
        name = "Configure Financial Settings"
        description = "tell Accountant-x what is your financial rulling rule"

    def check_completion(self, request):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return False
        try:
            settings = orga.financial_settings
            settings.full_clean()
        except FinancialSettings.DoesNotExist:
            return False
        except ValidationError:
            return False
        return True

    def get_action_url(self):
        return reverse('reports:settings-financial')


class AddEmployeesStep(BaseStep):

    class StepOptions:
        name = "Add Employees"
        description = "add at least one *employee*, even if you are giving " \
                      "yourself a salary that follows the profits"

    def check_completion(self, request):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return False
        count = orga.employees.all().count()
        return count > 0

    def get_action_url(self):
        return reverse('people:employee-create')


class ConfigurePayRunSettingsStep(BaseStep):

    class StepOptions:
        name = "Configure Pay Run Settings"
        description = "tell to Accountant-x how you distribute salaries"

    def check_completion(self, request):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return False
        try:
            settings = orga.payrun_settings
            settings.full_clean()
        except PayRunSettings.DoesNotExist:
            return False
        except ValidationError:
            return False
        return True

    def get_action_url(self):
        return reverse('reports:settings-payrun')


class AddFirstClientStep(BaseStep):

    class StepOptions:
        name = "Add the first Client"
        description = "close to the first invoice"

    def check_completion(self, request):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return False
        count = orga.clients.all().count()
        return count > 0

    def get_action_url(self):
        return reverse('people:client-create')


class AddFirstInvoiceStep(BaseStep):

    class StepOptions:
        name = "Add the first Invoice"
        description = "finally create it !"

    def check_completion(self, request):
        orga = organization_manager.get_selected_organization(request)
        if orga is None:
            return False
        count = orga.invoices.all().count()
        return count > 0

    def get_action_url(self):
        return reverse('books:invoice-create')


================================================
FILE: accounting/apps/connect/urls.py
================================================
from django.conf.urls import patterns, url

from . import views


urlpatterns = patterns('',

    url(r'^$',
        views.RootRedirectionView.as_view(),
        name="root"),

    # Step by step
    url(r'^getting-started/$',
        views.GettingStartedView.as_view(),
        name="getting-started")
)


================================================
FILE: accounting/apps/connect/views.py
================================================
from django.views import generic
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse

from accounting.apps.books.models import Organization
from .steps import (
    CreateOrganizationStep,
    ConfigureTaxRatesStep,
    ConfigureBusinessSettingsStep,
    ConfigureFinancialSettingsStep,
    AddEmployeesStep,
    ConfigurePayRunSettingsStep,
    AddFirstClientStep,
    AddFirstInvoiceStep)


class RootRedirectionView(generic.View):
    """
    Redirect to the books if an organization is already configured

    Otherwise we begin the step by step creation process to help the user
    begin and configure his books
    """

    def get(self, *args, **kwargs):
        if Organization.objects.all().count():
            return HttpResponseRedirect(reverse('books:dashboard'))


class GettingStartedView(generic.TemplateView):
    template_name = "connect/getting_started.html"

    def get_steps(self, request):
        user = request.user
        steps = steps = [
            CreateOrganizationStep(user),
            ConfigureTaxRatesStep(user),
            ConfigureBusinessSettingsStep(user),
            ConfigureFinancialSettingsStep(user),
            AddEmployeesStep(user),
            ConfigurePayRunSettingsStep(user),
            AddFirstClientStep(user),
            AddFirstInvoiceStep(user),
        ]
        return steps

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)

        request = self.request
        steps = self.get_steps(self.request)

        def uncomplete_filter(s):
            return not s.completed(request)

        uncompleted_steps = list(filter(uncomplete_filter, steps))
        try:
            next_step = next(s for s in uncompleted_steps)
        except StopIteration:
            next_step = None

        ctx['steps'] = steps
        ctx['next_step'] = next_step
        ctx['all_steps_completed'] = bool(next_step is None)

        return ctx

    def post(self, request, *args, **kwargs):
        steps = self.get_steps(request)
        uncompleted_steps = filter(lambda s: not s.completed(request), steps)
        if not len(uncompleted_steps):
            return super().post(request, *args, **kwargs)

        # unmark the session as getting started
        request.sessions['getting_started_done'] = True
        return HttpResponseRedirect(reverse('books:dashboard'))


================================================
FILE: accounting/apps/context_processors.py
================================================
from django.conf import settings


def metadata(request):
    """
    Add some generally useful metadata to the template context
    """
    return {
        'display_version': getattr(settings,
            'DISPLAY_VERSION', 'N/A'),
        'display_short_version': getattr(settings,
            'DISPLAY_SHORT_VERSION', 'N/A'),
        'version': getattr(settings,
            'VERSION', 'N/A'),
    }


================================================
FILE: accounting/apps/people/__init__.py
================================================


================================================
FILE: accounting/apps/people/admin.py
================================================
from django.contrib import admin

from . import models


@admin.register(models.Client)
class ClientAdmin(admin.ModelAdmin):
    pass


@admin.register(models.Employee)
class EmployeeAdmin(admin.ModelAdmin):
    pass


================================================
FILE: accounting/apps/people/forms.py
================================================
from django.forms import ModelForm
from django.contrib.auth import get_user_model

from .models import Client, Employee

from django_select2.fields import (
    AutoModelSelect2Field,
    AutoModelSelect2MultipleField)


class ClientForm(ModelForm):
    class Meta:
        model = Client
        fields = (
            "name",
            "address_line_1",
            "address_line_2",
            "city",
            "postal_code",
            "country",
        )


class EmployeeForm(ModelForm):
    class Meta:
        model = Employee
        fields = (
            "first_name",
            "last_name",
            "email",

            "payroll_tax_rate",

            "salary_follows_profits",
            "shares_percentage",

        )


# TODO: avoid calling this in the global scope, can lead to circular imports
User = get_user_model()


class UserChoices(AutoModelSelect2Field):
    queryset = User.objects.all()
    search_fields = (
        'first_name__icontains',
        'last_name__icontains',
        'username__icontains',
        'email__icontains',
    )


class UserMultipleChoices(AutoModelSelect2MultipleField):
    queryset = User.objects.all()
    search_fields = (
        'first_name__icontains',
        'last_name__icontains',
        'username__icontains',
        'email__icontains',
    )


================================================
FILE: accounting/apps/people/migrations/0001_initial.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from decimal import Decimal
import django.core.validators


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0002_auto_20141029_1606'),
    ]

    operations = [
        migrations.CreateModel(
            name='Client',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('name', models.CharField(max_length=150)),
                ('address_line_1', models.CharField(max_length=128)),
                ('address_line_2', models.CharField(null=True, blank=True, max_length=128)),
                ('city', models.CharField(max_length=64)),
                ('postal_code', models.CharField(max_length=7)),
                ('country', models.CharField(max_length=50)),
                ('organization', models.ForeignKey(null=True, related_name='orgas', to='books.Organization', blank=True)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='Employee',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('first_name', models.CharField(max_length=150)),
                ('last_name', models.CharField(max_length=150)),
                ('email', models.EmailField(max_length=254)),
                ('salaries_follow_profits', models.BooleanField(default=False)),
                ('shares_percentage', models.DecimalField(decimal_places=5, validators=[django.core.validators.MinValueValidator(Decimal('0')), django.core.validators.MaxValueValidator(Decimal('1'))], max_digits=6)),
                ('organization', models.ForeignKey(to='books.Organization', related_name='employees')),
            ],
            options={
            },
            bases=(models.Model,),
        ),
    ]


================================================
FILE: accounting/apps/people/migrations/0002_auto_20141029_1609.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('people', '0001_initial'),
    ]

    operations = [
        migrations.RenameField(
            model_name='employee',
            old_name='salaries_follow_profits',
            new_name='salary_follows_profits',
        ),
    ]


================================================
FILE: accounting/apps/people/migrations/0003_employee_payroll_tax_rate.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from decimal import Decimal
import django.core.validators


class Migration(migrations.Migration):

    dependencies = [
        ('people', '0002_auto_20141029_1609'),
    ]

    operations = [
        migrations.AddField(
            model_name='employee',
            name='payroll_tax_rate',
            field=models.DecimalField(default=0, decimal_places=5, validators=[django.core.validators.MinValueValidator(Decimal('0')), django.core.validators.MaxValueValidator(Decimal('1'))], max_digits=6),
            preserve_default=False,
        ),
    ]


================================================
FILE: accounting/apps/people/migrations/0004_auto_20141029_2306.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


def _link_to_first_organization(apps, schema_editor):
    Client = apps.get_model("people", "Client")
    Client.objects.update(organization_id=1)


class Migration(migrations.Migration):

    dependencies = [
        ('people', '0003_employee_payroll_tax_rate'),
    ]

    operations = [
        migrations.RunPython(_link_to_first_organization)
    ]


================================================
FILE: accounting/apps/people/migrations/0005_auto_20141029_2308.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('people', '0004_auto_20141029_2306'),
    ]

    operations = [
        migrations.AlterField(
            model_name='client',
            name='organization',
            field=models.ForeignKey(related_name='clients', to='books.Organization'),
        ),
    ]


================================================
FILE: accounting/apps/people/migrations/__init__.py
================================================


================================================
FILE: accounting/apps/people/models.py
================================================
from decimal import Decimal as D

from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator


class Client(models.Model):
    name = models.CharField(max_length=150)

    # address
    address_line_1 = models.CharField(max_length=128)
    address_line_2 = models.CharField(max_length=128,
                                      blank=True, null=True)
    city = models.CharField(max_length=64)
    postal_code = models.CharField(max_length=7)
    country = models.CharField(max_length=50)

    organization = models.ForeignKey('books.Organization',
                                     related_name="clients")

    class Meta:
        pass

    def __str__(self):
        return self.name

    def active_address_fields(self):
        """
        Return the non-empty components of the address
        """
        fields = [self.address_line_1, self.address_line_2,
                  self.city, self.postal_code, self.country]
        fields = [f.strip() for f in fields if f]
        return fields

    def full_address(self, separator="\n"):
        return separator.join(filter(bool, self.active_address_fields()))


class Employee(models.Model):
    first_name = models.CharField(max_length=150)
    last_name = models.CharField(max_length=150)
    email = models.EmailField(max_length=254)

    payroll_tax_rate = models.DecimalField(
        max_digits=6,
        decimal_places=5,
        validators=[
            MinValueValidator(D('0')),
            MaxValueValidator(D('1'))
        ]
    )

    salary_follows_profits = models.BooleanField(default=False)
    shares_percentage = models.DecimalField(
        max_digits=6,
        decimal_places=5,
        validators=[
            MinValueValidator(D('0')),
            MaxValueValidator(D('1'))
        ]
    )

    organization = models.ForeignKey('books.Organization',
                                     related_name="employees")

    class Meta:
        pass

    def __str__(self):
        return "{}".format(self.composite_name)

    @property
    def composite_name(self):
        return "{} {}".format(self.first_name, self.last_name)


================================================
FILE: accounting/apps/people/urls.py
================================================
from django.conf.urls import patterns, url

from . import views


urlpatterns = patterns('',

    # Clients
    url(r'^client/$',
        views.ClientListView.as_view(),
        name="client-list"),
    url(r'^client/create/$',
        views.ClientCreateView.as_view(),
        name="client-create"),
    url(r'^client/(?P<pk>\d+)/edit/$',
        views.ClientUpdateView.as_view(),
        name="client-edit"),
    url(r'^client/(?P<pk>\d+)/detail/$',
        views.ClientDetailView.as_view(),
        name="client-detail"),

    # Employees
    url(r'^employee/$',
        views.EmployeeListView.as_view(),
        name="employee-list"),
    url(r'^employee/create/$',
        views.EmployeeCreateView.as_view(),
        name="employee-create"),
    url(r'^employee/(?P<pk>\d+)/edit/$',
        views.EmployeeUpdateView.as_view(),
        name="employee-edit"),
    url(r'^employee/(?P<pk>\d+)/detail/$',
        views.EmployeeDetailView.as_view(),
        name="employee-detail"),
)


================================================
FILE: accounting/apps/people/views.py
================================================
from django.views import generic
from django.core.urlresolvers import reverse

from accounting.apps.books.mixins import (
    RestrictToSelectedOrganizationQuerySetMixin,
    AutoSetSelectedOrganizationMixin)
from .models import Client, Employee
from .forms import ClientForm, EmployeeForm


class ClientListView(RestrictToSelectedOrganizationQuerySetMixin,
                     generic.ListView):
    template_name = "people/client_list.html"
    model = Client
    context_object_name = "clients"


class ClientCreateView(AutoSetSelectedOrganizationMixin,
                       generic.CreateView):
    template_name = "people/client_create_or_update.html"
    model = Client
    form_class = ClientForm

    def get_success_url(self):
        return reverse("people:client-list")


class ClientUpdateView(RestrictToSelectedOrganizationQuerySetMixin,
                       AutoSetSelectedOrganizationMixin,
                       generic.UpdateView):
    template_name = "people/client_create_or_update.html"
    model = Client
    form_class = ClientForm

    def get_success_url(self):
        return reverse("people:client-list")


class ClientDetailView(RestrictToSelectedOrganizationQuerySetMixin,
                       generic.DetailView):
    template_name = "people/client_detail.html"
    model = Client
    context_object_name = "client"


class EmployeeListView(RestrictToSelectedOrganizationQuerySetMixin,
                       generic.ListView):
    template_name = "people/employee_list.html"
    model = Employee
    context_object_name = "employees"


class EmployeeCreateView(AutoSetSelectedOrganizationMixin,
                         generic.CreateView):
    template_name = "people/employee_create_or_update.html"
    model = Employee
    form_class = EmployeeForm

    def get_success_url(self):
        return reverse("people:employee-list")


class EmployeeUpdateView(RestrictToSelectedOrganizationQuerySetMixin,
                         AutoSetSelectedOrganizationMixin,
                         generic.UpdateView):
    template_name = "people/employee_create_or_update.html"
    model = Employee
    form_class = EmployeeForm

    def get_success_url(self):
        return reverse("people:employee-list")


class EmployeeDetailView(RestrictToSelectedOrganizationQuerySetMixin,
                         generic.DetailView):
    template_name = "people/employee_detail.html"
    model = Employee
    context_object_name = "employee"


================================================
FILE: accounting/apps/reports/__init__.py
================================================


================================================
FILE: accounting/apps/reports/admin.py
================================================
from django.contrib import admin

from . import models


@admin.register(models.FinancialSettings)
class FinancialSettingsAdmin(admin.ModelAdmin):
    pass


================================================
FILE: accounting/apps/reports/forms.py
================================================
from datetime import timedelta

from django import forms

from .models import (
    BusinessSettings,
    FinancialSettings,
    PayRunSettings)


class BusinessSettingsForm(forms.ModelForm):
    class Meta:
        model = BusinessSettings
        fields = (
            "business_type",
        )


class FinancialSettingsForm(forms.ModelForm):
    class Meta:
        model = FinancialSettings
        fields = (
            "financial_year_end_day",
            "financial_year_end_month",

            "tax_id_number",
            "tax_id_display_name",
            "tax_period",
        )


class PayRunSettingsForm(forms.ModelForm):
    class Meta:
        model = PayRunSettings
        fields = (
            "salaries_follow_profits",
            "payrun_period",
        )


class TimePeriodForm(forms.Form):
    date_from = forms.DateField(required=False,
                                label="From")
    date_to = forms.DateField(required=False,
                              label="To")

    _filters = None
    _description = None

    def _determine_filter_metadata(self):
        self._filters = {}
        self._description = "All orders"
        if self.errors:
            return

        date_from = self.cleaned_data['date_from']
        date_to = self.cleaned_data['date_to']
        if date_from and date_to:
            # We want to include end date so we adjust the date we
            # use with the 'range' function.
            self._filters = {
                'date_placed__range': [
                    date_from,
                    date_to + timedelta(days=1)
                ]
            }
            self._description = ("Between {} and {}"
                .format(date_from, date_to))
        elif date_from and not date_to:
            self._filters = {'date_placed__gte': date_from}
            self._description = "Since {}".format(date_from)
        elif not date_from and date_to:
            self._filters = {'date_placed__lte': date_to}
            self._description = "Until {}".format(date_to)
        else:
            self._filters = {}
            self._description = "From the begining to now"

    def get_filters(self):
        if self._filters is None:
            self._determine_filter_metadata()
        return self._filters

    def get_filter_description(self):
        if self._description is None:
            self._determine_filter_metadata()
        return self._description


================================================
FILE: accounting/apps/reports/migrations/0001_initial.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import django.core.validators


class Migration(migrations.Migration):

    dependencies = [
        ('books', '0003_auto_20141029_1606'),
    ]

    operations = [
        migrations.CreateModel(
            name='BusinessSettings',
            fields=[
                ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
                ('business_type', models.CharField(choices=[('sole_proprietorship', 'Sole Proprietorship')], max_length=50)),
                ('organization', models.OneToOneField(related_name='business_settings', to='books.Organization', null=True, blank=True)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='FinancialSettings',
            fields=[
                ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
                ('financial_year_end_day', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(31)])),
                ('financial_year_end_month', models.PositiveSmallIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(12)])),
                ('tax_id_number', models.CharField(null=True, blank=True, max_length=150)),
                ('tax_id_display_name', models.CharField(null=True, blank=True, max_length=150)),
                ('tax_period', models.CharField(verbose_name='Tax Period', choices=[('monthly', '1 month'), ('bimonthly', '2 months'), ('quarter', '3 months'), ('half', '6 months'), ('year', '1 year')], max_length=20)),
                ('organization', models.OneToOneField(related_name='financial_settings', to='books.Organization', null=True, blank=True)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='PayRunSettings',
            fields=[
                ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True, serialize=False)),
                ('salaries_follow_profits', models.BooleanField(default=False)),
                ('payrun_period', models.CharField(verbose_name='Payrun Period', default='monthly', choices=[('monthly', 'monthly')], max_length=20)),
                ('organization', models.OneToOneField(related_name='payrun_settings', to='books.Organization', null=True, blank=True)),
            ],
            options={
            },
            bases=(models.Model,),
        ),
    ]


================================================
FILE: accounting/apps/reports/migrations/0002_auto_20150128_1458.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import django.core.validators


class Migration(migrations.Migration):

    dependencies = [
        ('reports', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='financialsettings',
            name='financial_year_end_day',
            field=models.PositiveSmallIntegerField(default=31, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(31)]),
        ),
        migrations.AlterField(
            model_name='financialsettings',
            name='financial_year_end_month',
            field=models.PositiveSmallIntegerField(default=12, validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(12)]),
        ),
    ]


================================================
FILE: accounting/apps/reports/migrations/0003_auto_20150131_1902.py
================================================
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

    dependencies = [
        ('reports', '0002_auto_20150128_1458'),
    ]

    operations = [
        migrations.AlterField(
            model_name='businesssettings',
            name='business_type',
            field=models.CharField(choices=[('sole_proprietorship', 'Sole Proprietorship'), ('partnership', 'Partnership'), ('corporation', 'Corporation')], max_length=50),
            preserve_default=True,
        ),
    ]


================================================
FILE: accounting/apps/reports/migrations/__init__.py
================================================


================================================
FILE: accounting/apps/reports/models.py
================================================
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator


class BusinessSettings(models.Model):
    BUSINESS_TYPE_SOLE_PROPRIETORSHIP = 'sole_proprietorship'
    BUSINESS_TYPE_PARTNERSHIP = 'partnership'
    BUSINESS_TYPE_CORPORATION = 'corporation'
    BUSINESS_TYPE_CHOICES = (
        (BUSINESS_TYPE_SOLE_PROPRIETORSHIP, "Sole Proprietorship"),
        (BUSINESS_TYPE_PARTNERSHIP, "Partnership"),
        (BUSINESS_TYPE_CORPORATION, "Corporation"),
    )
    business_type = models.CharField(max_length=50,
                                     choices=BUSINESS_TYPE_CHOICES)

    # optionnaly linked to an organization
    # for automated behaviors during cross-organizations invoicing
    organization = models.OneToOneField('books.Organization',
                                        related_name="business_settings",
                                        blank=True, null=True)

    class Meta:
        pass


class FinancialSettings(models.Model):
    financial_year_end_day = models.PositiveSmallIntegerField(default=31,
        validators=[
            MinValueValidator(1),
            MaxValueValidator(31)
        ])
    financial_year_end_month = models.PositiveSmallIntegerField(default=12,
        validators=[
            MinValueValidator(1),
            MaxValueValidator(12)
        ])

    tax_id_number = models.CharField(max_length=150,
                                     blank=True, null=True)
    tax_id_display_name = models.CharField(max_length=150,
                                           blank=True, null=True)

    TAX_PERIOD_MONTHLY = 'monthly'      # 1 month
    TAX_PERIOD_BIMONTHLY = 'bimonthly'  # 2 months
    TAX_PERIOD_QUARTER = 'quarter'      # 3 months
    TAX_PERIOD_HALF = 'half'            # 6 months
    TAX_PERIOD_YEAR = 'year'            # 12 months
    TAX_PERIOD_CHOICES = (
        (TAX_PERIOD_MONTHLY, "1 month"),
        (TAX_PERIOD_BIMONTHLY, "2 months"),
        (TAX_PERIOD_QUARTER, "3 months"),
        (TAX_PERIOD_HALF, "6 months"),
        (TAX_PERIOD_YEAR, "1 year"),
    )
    tax_period = models.CharField("Tax Period",
                                  max_length=20,
                                  choices=TAX_PERIOD_CHOICES)

    # optionnaly linked to an organization
    # for automated behaviors during cross-organizations invoicing
    organization = models.OneToOneField('books.Organization',
                                        related_name="financial_settings",
                                        blank=True, null=True)

    class Meta:
        pass


class PayRunSettings(models.Model):
    salaries_follow_profits = models.BooleanField(default=False)

    PAYRUN_MONTHLY = 'monthly'      # 1 month
    # PAYRUN_QUARTER = 'quarter'      # 3 months
    PAYRUN_CHOICES = (
        (PAYRUN_MONTHLY, "monthly"),
    )
    payrun_period = models.CharField("Payrun Period",
                                     max_length=20,
                                     choices=PAYRUN_CHOICES,
                                     default=PAYRUN_MONTHLY)

    # optionnaly linked to an organization
    # for automated behaviors during cross-organizations invoicing
    organization = models.OneToOneField('books.Organization',
                                        related_name="payrun_settings",
                                        blank=True, null=True)

    class Meta:
        pass


================================================
FILE: accounting/apps/reports/urls.py
================================================
from django.conf.urls import patterns, url

from . import views


urlpatterns = patterns('',

    # Reports
    url(r'^report/$',
        views.ReportListView.as_view(),
        name="report-list"),
    url(r'^report/tax/$',
        views.TaxReportView.as_view(),
        name="tax-report"),
    url(r'^report/profitloss/$',
        views.ProfitAndLossReportView.as_view(),
        name="profit-and-loss-report"),
    url(r'^report/payrun/$',
        views.PayRunReportView.as_view(),
        name="pay-run-report"),
    url(r'^report/invoicedetails/$',
        views.InvoiceDetailsView.as_view(),
        name="invoice-details-report"),

    # Settings
    url(r'^settings/$',
        views.SettingsListView.as_view(),
        name="settings-list"),
    url(r'^settings/business/$',
        views.BusinessSettingsUpdateView.as_view(),
        name="settings-business"),
    url(r'^settings/financial/$',
        views.FinancialSettingsUpdateView.as_view(),
        name="settings-financial"),
    url(r'^settings/payrun/$',
        views.PayRunSettingsUpdateView.as_view(),
        name="settings-payrun"),
)


================================================
FILE: accounting/apps/reports/views.py
================================================
from datetime import date

from django.views import generic
from django.core.urlresolvers import reverse
from django.utils import timezone

from dateutil.relativedelta import relativedelta

from accounting.apps.books.utils import organization_manager
from accounting.libs.intervals import TimeInterval
from .models import (
    BusinessSettings,
    FinancialSettings,
    PayRunSettings)
from .forms import (
    BusinessSettingsForm,
    FinancialSettingsForm,
    PayRunSettingsForm,
    TimePeriodForm)
from .wrappers import (
    TaxReport,
    ProfitAndLossReport,
    PayRunReport,
    InvoiceDetailsReport)


class TimePeriodFormMixin(object):

    period = None

    def get_initial(self):
        initial = super().get_initial()

        # currrent quarter
        now = timezone.now()
        start = date(
            year=now.year,
            month=(now.month - ((now.month - 1) % 3)),
            day=1
        )
        end = start + relativedelta(months=3)

        initial['date_from'] = start
        initial['date_to'] = end

        return initial

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        if self.request.GET:
            kwargs.update({
                'data': self.request.GET,
            })
        return kwargs

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)

        form = ctx['form']
        if form.is_valid():
            start = form.cleaned_data['date_from']
            end = form.cleaned_data['date_to']
            ctx['form_title'] = form.get_filter_description()
        else:
            start = end = None
            ctx['form_title'] = "Time Interval"

        if self.period is None:
            self.period = TimeInterval(start=start, end=end)

        return ctx


class ReportListView(generic.TemplateView):
    template_name = "reports/report_list.html"


class SettingsListView(generic.TemplateView):
    template_name = "reports/settings_list.html"


class GenericSettingsMixin(object):

    def get_object(self):
        orga = organization_manager.get_selected_organization(self.request)
        try:
            settings = self.model.objects.get(organization=orga)
        except self.model.DoesNotExist:
            settings = self.model.objects.create(organization=orga)
        return settings

    def get_success_url(self):
        return reverse("reports:settings-list")


class BusinessSettingsUpdateView(GenericSettingsMixin,
                                 generic.UpdateView):
    template_name = "reports/financial_settings_update.html"
    model = BusinessSettings
    form_class = BusinessSettingsForm


class FinancialSettingsUpdateView(GenericSettingsMixin,
                                  generic.UpdateView):
    template_name = "reports/financial_settings_update.html"
    model = FinancialSettings
    form_class = FinancialSettingsForm


class PayRunSettingsUpdateView(GenericSettingsMixin,
                               generic.UpdateView):
    template_name = "reports/payrun_settings_update.html"
    model = PayRunSettings
    form_class = PayRunSettingsForm


class TaxReportView(TimePeriodFormMixin,
                    generic.FormView):
    template_name = "reports/tax_report.html"
    form_class = TimePeriodForm

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        orga = organization_manager.get_selected_organization(self.request)
        report = TaxReport(orga,
                           start=self.period.start,
                           end=self.period.end)
        report.generate()
        ctx['tax_summaries'] = report.tax_summaries.values()
        return ctx


class ProfitAndLossReportView(generic.TemplateView):
    template_name = "reports/profit_and_loss_report.html"

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        orga = organization_manager.get_selected_organization(self.request)

        # currrent quarter
        now = timezone.now()
        start = date(
            year=now.year,
            month=(now.month - ((now.month - 1) % 3)),
            day=1
        )
        end = start + relativedelta(months=3)

        report = ProfitAndLossReport(orga, start=start, end=end)
        report.generate()
        ctx['summaries'] = report.summaries
        ctx['total_summary'] = report.total_summary
        return ctx


class PayRunReportView(TimePeriodFormMixin,
                       generic.FormView):
    template_name = "reports/pay_run_report.html"
    form_class = TimePeriodForm

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        orga = organization_manager.get_selected_organization(self.request)

        report = PayRunReport(orga,
                              start=self.period.start,
                              end=self.period.end)
        report.generate()
        ctx['summaries'] = report.summaries.values()
        ctx['total_payroll_taxes'] = report.total_payroll_taxes

        return ctx


class InvoiceDetailsView(TimePeriodFormMixin,
                         generic.FormView):
    template_name = "reports/invoice_details_report.html"
    form_class = TimePeriodForm

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        orga = organization_manager.get_selected_organization(self.request)
        report = InvoiceDetailsReport(orga,
                                      start=self.period.start,
                                      end=self.period.end)
        report.generate()
        ctx['invoices'] = report.invoices
        ctx['tax_rates'] = report.tax_rates
        ctx['payrun_settings'] = orga.payrun_settings
        return ctx


================================================
FILE: accounting/apps/reports/wrappers.py
================================================
from decimal import Decimal as D
from collections import defaultdict, OrderedDict

from dateutil.relativedelta import relativedelta

from accounting.apps.books.models import Invoice, Bill
from accounting.apps.books.calculators import ProfitsLossCalculator
from accounting.libs.intervals import TimeInterval


class BaseReport(object):
    title = None
    period = None

    def __init__(self, title, start, end):
        self.title = title
        self.period = TimeInterval(start, end)

    def generate(self):
        raise NotImplementedError


class TaxRateSummary(object):
    tax_rate = None
    taxable_amount = D('0')
    expenses_amount = D('0')

    @property
    def collected_taxes(self):
        return self.tax_rate.rate * self.taxable_amount

    @property
    def deductible_taxes(self):
        return self.tax_rate.rate * self.expenses_amount

    @property
    def net_amount(self):
        return self.taxable_amount - self.expenses_amount

    @property
    def net_taxes(self):
        return self.tax_rate.rate * self.net_amount


class TaxReport(BaseReport):
    # TODO implement 'Billed (Accrual) / Collected (Cash based)'
    organization = None
    tax_summaries = None

    def __init__(self, organization, start, end):
        super().__init__("Tax Report", start, end)
        self.organization = organization
        self.tax_summaries = defaultdict(TaxRateSummary)

    def generate(self):
        invoice_queryset = Invoice.objects.all()
        bill_queryset = Bill.objects.all()
        self.generate_for_sales(invoice_queryset)
        self.generate_for_sales(bill_queryset)

    def generate_for_sales(self, sales_queryset):
        calculator = ProfitsLossCalculator(self.organization,
                                           start=self.period.start,
                                           end=self.period.end)

        for output in calculator.process_generator(sales_queryset):
            summary = self.tax_summaries[output.tax_rate.pk]
            summary.tax_rate = output.tax_rate

            if isinstance(output.sale, Invoice):
                summary.taxable_amount += output.amount_excl_tax
            elif isinstance(output.sale, Bill):
                summary.expenses_amount += output.amount_excl_tax
            else:
                raise ValueError("Unsupported type of sale {}"
                    .format(output.sale.__class__))


class ProfitAndLossSummary(object):
    grouping_date = None
    sales_amount = D('0')
    expenses_amount = D('0')

    @property
    def net_profit(self):
        return self.sales_amount - self.expenses_amount


class ProfitAndLossReport(BaseReport):
    # TODO implement 'Billed (Accrual) / Collected (Cash based)'
    organization = None
    summaries = None
    total_summary = None

    RESOLUTION_MONTHLY = 'monthly'
    RESOLUTION_CHOICES = (
        RESOLUTION_MONTHLY,
    )
    group_by_resolution = RESOLUTION_MONTHLY

    def __init__(self, organization, start, end):
        super().__init__("Profit and Loss", start, end)
        self.organization = organization
        self.summaries = {}
        steps_interval = relativedelta(end, start)

        assert self.group_by_resolution in self.RESOLUTION_CHOICES, \
            "No a resolution choice"
        if self.group_by_resolution == self.RESOLUTION_MONTHLY:
            for step in range(0, steps_interval.months):
                key_date = start + relativedelta(months=step)
                self.summaries[key_date] = ProfitAndLossSummary()
        else:
            raise ValueError("Unsupported resolution {}"
                .format(self.group_by_resolution))

        self.total_summary = ProfitAndLossSummary()

    def group_by_date(self, date):
        if self.group_by_resolution == self.RESOLUTION_MONTHLY:
            grouping_date = date.replace(day=1)
        else:
            raise ValueError("Unsupported resolution {}"
                .format(self.group_by_resolution))
        return grouping_date

    def generate(self):
        invoice_queryset = Invoice.objects.all()
        bill_queryset = Bill.objects.all()
        self.generate_for_sales(invoice_queryset)
        self.generate_for_sales(bill_queryset)

        # order the results
        self.summaries = OrderedDict(sorted(self.summaries.items()))

        # compute totals
        for summary in self.summaries.values():
            self.total_summary.sales_amount += summary.sales_amount
            self.total_summary.expenses_amount += summary.expenses_amount

    def generate_for_sales(self, sales_queryset):
        calculator = ProfitsLossCalculator(self.organization,
                                           start=self.period.start,
                                           end=self.period.end)

        for output in calculator.process_generator(sales_queryset):
            key_date = self.group_by_date(output.payment.date_paid)
            summary = self.summaries[key_date]

            if isinstance(output.sale, Invoice):
                summary.sales_amount += output.amount_excl_tax
            elif isinstance(output.sale, Bill):
                summary.expenses_amount += output.amount_excl_tax
            else:
                raise ValueError("Unsupported type of sale {}"
                    .format(output.sale.__class__))


class PayRunSummary(object):
    payroll_tax_rate = None
    total_excl_tax = D('0')

    @property
    def payroll_taxes(self):
        return self.payroll_tax_rate * self.total_excl_tax


class PayRunReport(BaseReport):
    organization = None
    summaries = None
    total_payroll_taxes = D('0')

    def __init__(self, organization, start, end):
        super().__init__("Pay Run Report", start, end)
        self.organization = organization
        self.summaries = defaultdict(PayRunSummary)

    def generate(self):
        employee_queryset = self.organization.employees.all()
        self.generate_for_employees(employee_queryset)

    def generate_for_employees(self, employee_queryset):
        total_payroll_taxes = D('0')
        calculator = ProfitsLossCalculator(self.organization,
                                           start=self.period.start,
                                           end=self.period.end)

        for emp in employee_queryset:
            summary = self.summaries[emp.composite_name]
            summary.employee = emp
            summary.payroll_tax_rate = emp.payroll_tax_rate
            if emp.salary_follows_profits:
                # TODO compute profits based on the period interval
                profits = calculator.profits()
                summary.total_excl_tax = profits * emp.shares_percentage
            else:
                raise ValueError("Salary not indexed on the profits "
                                 "are not supported yet")

            total_payroll_taxes += summary.payroll_taxes

        # Total payroll
        self.total_payroll_taxes = total_payroll_taxes


class InvoiceDetailsReport(BaseReport):
    organization = None
    invoices = None
    tax_rates = None

    def __init__(self, organization, start, end):
        super().__init__("Pay Run Report", start, end)
        self.organization = organization
        self.tax_rates = organization.tax_rates.all()

    def generate(self):
        invoice_queryset = self.organization.invoices.all()
        self.generate_for_invoices(invoice_queryset)

    def generate_for_invoices(self, invoice_queryset):
        invoice_queryset = (invoice_queryset
            .filter(payments__date_paid__range=[
                self.period.start,
                self.period.end
            ]))

        # optimize the query
        invoice_queryset = (invoice_queryset
            .select_related(
                'organization')
            .prefetch_related(
                'lines',
                'lines__tax_rate',
                'payments',
                'organization__employees',)
            .distinct())

        self.invoices = invoice_queryset


================================================
FILE: accounting/defaults.py
================================================
ACCOUNTING_DEFAULT_CURRENCY = "EUR"


================================================
FILE: accounting/libs/__init__.py
================================================


================================================
FILE: accounting/libs/checks.py
================================================
from django.core.validators import EMPTY_VALUES
from django.utils.datastructures import SortedDict


class PrimaryKeyRelatedField(object):
    pass


class CheckResult(object):
    """
    Stands for a checking result of a model field
    """

    RESULT_NEUTRAL = 'neutral'
    RESULT_FAILED = 'failed'
    RESULT_PASSED = 'passed'
    RESULT_CHOICES = (
        (RESULT_NEUTRAL, "Neutral"),
        (RESULT_FAILED, "Failed"),
        (RESULT_PASSED, "Passed"),
    )

    LEVEL_WARNING = 'warning'
    LEVEL_ERROR = 'error'
    LEVEL_CHOICES = (
        (LEVEL_WARNING, "Warning"),
        (LEVEL_ERROR, "Error"),
    )

    def __init__(self, field, result=None, level=None, message=None):
        self.field = field
        self.message = message

        if not result:
            result = self.RESULT_NEUTRAL
        self.result = result

        if not level:
            level = self.LEVEL_WARNING
        self.level = level

    def mark_fail(self, level=LEVEL_WARNING, message=None):
        self.result = self.RESULT_FAILED
        self.level = level
        self.message = message

    def mark_pass(self, message=None):
        self.result = self.RESULT_PASSED
        self.message = message

    @property
    def has_failed(self):
        return self.result == self.RESULT_FAILED

    @property
    def has_passed(self):
        return self.result == self.RESULT_PASSED


class CheckingModelOptions(object):
    """
    Meta class options for `CheckingModelMixin`
    """
    def __init__(self, meta):
        self.fields = getattr(meta, 'fields', ())
        assert isinstance(self.fields, (list, tuple)), \
            '`fields` must be a list or tuple'

        self.exclude = getattr(meta, 'exclude', ())
        assert isinstance(self.exclude, (list, tuple)), \
            '`exclude` must be a list or tuple'


class CheckingModelMixin(object):

    _options_class = CheckingModelOptions

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.opts = self._options_class(getattr(self, 'CheckingOptions', None))

    def has_custom_check_for_field(self, field_name):
        return hasattr(self, 'check_%s' % field_name)

    def get_check_for_field(self, field_name, checking_fields=None):
        if checking_fields is None:
            checking_fields = self.get_checking_fields()

        if field_name not in checking_fields:
            raise AttributeError("Field '%s' not checkable" % field_name)

        field = checking_fields.get(field_name)
        check = CheckResult(field=field)

        # custom check method
        if self.has_custom_check_for_field(field_name):
            return getattr(self, 'check_%s' % field_name)(check)

        # default check
        if isinstance(field, PrimaryKeyRelatedField):
            value = getattr(self, field_name).all()
            has_failed = bool(value.count() == 0)
        else:
            value = getattr(self, field_name)
            has_failed = bool(value in EMPTY_VALUES)

        if has_failed:
            check.mark_fail()
        else:
            check.mark_pass()
        return check

    def get_checking_fields(self, special_exclude=['id']):
        """
        Returns the set of fields on which we perform checkings
        """
        ret = SortedDict()
        for f in self._meta.fields:
            # avoid special_exclude fields
            if f.attname in special_exclude:
                continue
            ret[f.attname] = f

        # Deal with reverse relationships
        reverse_rels = self._meta.get_all_related_objects()
        # reverse_rels += self._meta.get_all_related_many_to_many_objects()
        for relation in reverse_rels:
            accessor_name = relation.get_accessor_name()
            to_many = relation.field.rel.multiple
            if not self.opts.fields or accessor_name not in self.opts.fields:
                continue
            if not to_many:
                raise NotImplementedError
            ret[accessor_name] = PrimaryKeyRelatedField()

        # If 'fields' is specified, use those fields, in that order.
        if self.opts.fields:
            new = SortedDict()
            for key in self.opts.fields:
                new[key] = ret[key]
            ret = new

        # Remove anything in 'exclude'
        if self.opts.exclude:
            for key in self.opts.exclude:
                ret.pop(key, None)

        return ret

    def check_fields(self):
        """
        First `self.clean_fields` is called to ensure data integrity

        Checks all fields and return a list of `CheckResult` instances
        """
        # TODO try to reintegrate this one without
        #      increase the db queries too much
        # self.clean_fields()

        checks = []
        fields = self.get_checking_fields()
        for key, field in fields.items():
            check = self.get_check_for_field(key, checking_fields=fields)
            checks.append(check)
        return checks

    def full_check(self):
        """
        Calls `self.check_fields`, `self.check` in that order

        NB: no need to call `self.full_clean` because the above
            methods already made those calls internally
        """
        # basic field checks
        checks = self.check_fields()

        # special checks
        additional_checks = self.check_additionnals()
        checks.extend(additional_checks)

        return checks

    def check_additionnals(self):
        """Additional checks that the user can implement"""
        return []

    def _raw_checking_completion(self):
        """
        Useful for additional checking completion computations
        """
        checks = self.full_check()
        completed = sum(1 for c in checks if c.has_passed)
        return completed, len(checks)

    def checking_completion(self):
        """
        Compute the percentage of checking completed
        on the model instance
        """
        completed, total = self._raw_checking_completion()
        return float(completed) / total

    def full_checking_completion(self):
        """
        Calls `self.checking_completion`

        This method should be used to do checking completion on
        related objects
        """
        completion = self.checking_completion()
        return completion

    def pass_full_checking(self):
        completion = self.full_checking_completion()
        return completion == 1.0


================================================
FILE: accounting/libs/decorators.py
================================================
# encoding: utf-8


def composed(*decs):
    """
    Compose multiple decorators

    Example :

    >>> @composed(dec1, dec2)
    ...     def some(f):
    ...         pass
    """
    def deco(f):
        for dec in reversed(decs):
            f = dec(f)
        return f
    return deco


def order_fields(*field_list):
    def decorator(form):
        original_init = form.__init__

        def init(self, *args, **kwargs):
            original_init(self, *args, **kwargs)
            for field in field_list[::-1]:
                self.fields.insert(0, field, self.fields.pop(field))
        form.__init__ = init
        return form
    return decorator


def memoize(func):
    """
    Memoization decorator for a function taking one or more arguments.
    """
    class memodict(dict):
        def __getitem__(self, *key):
            return dict.__getitem__(self, key)

        def __missing__(self, key):
            ret = self[key] = func(*key)
            return ret

    return memodict().__getitem__


================================================
FILE: accounting/libs/exceptions.py
================================================


================================================
FILE: accounting/libs/fields.py
================================================
from django.db import models
import uuid


class UUIDField(models.CharField):

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = kwargs.get('max_length', 64 )
        kwargs['blank'] = True
        models.CharField.__init__(self, *args, **kwargs)

    def _generate_uuid(self):
        return str(uuid.uuid4())

    def pre_save(self, model_instance, add):
        if add or not getattr(model_instance, self.attname):
            value = self._generate_uuid()
            setattr(model_instance, self.attname, value)
            return value
        else:
            return super(models.CharField, self).pre_save(model_instance, add)


================================================
FILE: accounting/libs/foundation.py
================================================
"""
Python helpers
"""

from collections import Mapping


def update(d, u, depth=-1):
    """
    Recursively merge or update dict-like objects.
    >>> update({'k1': {'k2': 2}}, {'k1': {'k2': {'k3': 3}}, 'k4': 4})
    {'k1': {'k2': {'k3': 3}}, 'k4': 4}
    """

    for k, v in u.iteritems():
        if isinstance(v, Mapping) and not depth == 0:
            r = update(d.get(k, {}), v, depth=max(depth - 1, -1))
            d[k] = r
        elif isinstance(d, Mapping):
            d[k] = u[k]
        else:
            d = {k: u[k]}
    return d


================================================
FILE: accounting/libs/intervals.py
================================================
from datetime import date


class TimeInterval(object):
    start = None
    end = None

    def __init__(self, start, end):
        assert start is None or isinstance(start, date), \
            "start should be a date instance"
        assert end is None or isinstance(end, date), \
            "end should be a date instance"
        self.start = start
        self.end = end


================================================
FILE: accounting/libs/prices.py
================================================
class TaxNotKnown(Exception):
    """
    Exception for when a tax-inclusive price is requested but we don't know
    what the tax applicable is (yet).
    """


class Price(object):
    """
    Simple price class that encapsulates a price and its tax information

    Attributes:
        incl_tax (Decimal): Price including taxes
        excl_tax (Decimal): Price excluding taxes
        tax (Decimal): Tax amount
        is_tax_known (bool): Whether tax is known
        currency (str): 3 character currency code
    """

    def __init__(self, currency, excl_tax, incl_tax=None, tax=None):
        self.currency = currency
        self.excl_tax = excl_tax
        if incl_tax is not None:
            self.incl_tax = incl_tax
            self.is_tax_known = True
        elif tax is not None:
            self.incl_tax = excl_tax + tax
            self.is_tax_known = True
        else:
            self.incl_tax = None
            self.is_tax_known = False

    def _get_tax(self):
        return self.incl_tax - self.excl_tax

    def _set_tax(self, value):
        self.incl_tax = self.excl_tax + value
        self.is_tax_known = True

    tax = property(_get_tax, _set_tax)

    def __repr__(self):
        if self.is_tax_known:
            return "%s(currency=%r, excl_tax=%r, incl_tax=%r, tax=%r)" % (
                self.__class__.__name__, self.currency, self.excl_tax,
                self.incl_tax, self.tax)
        return "%s(currency=%r, excl_tax=%r)" % (
            self.__class__.__name__, self.currency, self.excl_tax)

    def __eq__(self, other):
        """
        Two price objects are equal if currency, price.excl_tax and tax match.
        """
        return (self.currency == other.currency and
                self.excl_tax == other.excl_tax and
                self.incl_tax == other.incl_tax)


================================================
FILE: accounting/libs/templatetags/__init__.py
================================================


================================================
FILE: accounting/libs/templatetags/check_filters.py
================================================
from django import template

from accounting.libs.checks import CheckingModelMixin

register = template.Library()


@register.filter
def check(obj, field_name=None):
    """
    Can check an entire model or just a single model field
    """
    if isinstance(obj, CheckingModelMixin):
        if not field_name:
            check = obj.full_check()
        else:
            check = obj.get_check_for_field(field_name)
    else:
        return None

    return check


@register.filter('level_to_css_classname')
def _check_level_to_classname(check):
    """
    Return the appropriated css classname for the check level
    """
    if check.has_failed:
        if check.level == check.LEVEL_ERROR:
            return 'danger'
        elif check.level == check.LEVEL_WARNING:
            return 'warning'
        else:
            return 'info'

    return 'default'


@register.filter('level_to_glyphicon')
def _check_level_to_glyphicon(check):
    """
    Return the appropriated glyph icon for the check level
    """
    if check.has_failed:
        if check.level == check.LEVEL_ERROR:
            return 'minus-sign'
        elif check.level == check.LEVEL_WARNING:
            return 'exclamation-sign'
        else:
            return 'question-sign'

    return 'ok'


================================================
FILE: accounting/libs/templatetags/check_tags.py
================================================
from django import template

from classytags.core import Options
from classytags.arguments import Argument
from classytags.helpers import InclusionTag

register = template.Library()


@register.tag
class Check(InclusionTag):
    name = 'render_check'
    template = '_generics/check_tag.html'
    options = Options(
        Argument('check'),
    )

    def get_context(self, context, check):
        context.update({
            'check': check
        })

        return context


================================================
FILE: accounting/libs/templatetags/currency_filters.py
================================================
# encoding: utf-8

from decimal import Decimal as D, InvalidOperation

from django import template
from django.conf import settings
from django.utils.translation import to_locale, get_language

from babel.numbers import format_currency

register = template.Library()


@register.filter(name='currency')
def currency_formatter(value, currency=None):
    """
    Format decimal value as currency
    """
    try:
        value = D(value)
    except (TypeError, InvalidOperation):
        return ""
    # Using Babel's currency formatting
    # http://babel.pocoo.org/docs/api/numbers/#babel.numbers.format_currency
    currency = currency or settings.ACCOUNTING_DEFAULT_CURRENCY
    kwargs = {
        'currency': currency,
        'format': getattr(settings, 'CURRENCY_FORMAT', None),
        'locale': to_locale(get_language()),
    }
    return format_currency(value, **kwargs)


================================================
FILE: accounting/libs/templatetags/display_tags.py
================================================
from django import template

register = template.Library()


================================================
FILE: accounting/libs/templatetags/distance_filters.py
================================================
from django import template

from django.contrib.gis.measure import Distance

register = template.Library()


@register.filter
def has_distance(search_object):
    return bool(search_object._point_of_origin)


@register.filter
def distance(dist):
    if isinstance(dist, Distance):
        d = dist.m
        unit = "m"

        if d > 1000:
            d = dist.km
            unit = "km"

        ctx = {
            'distance': float('%.2g' % d),
            'unit': unit,
        }
        return u"%(distance)s %(unit)s" % ctx


================================================
FILE: accounting/libs/templatetags/float_filters.py
================================================
from django.template import Library
from django.utils.numberformat import format

register = Library()


@register.filter(name="float_dot")
def do_float_dot(value, decimal_pos=4):
    return format(value or 0, ".", decimal_pos)


do_float_dot.is_safe = True


================================================
FILE: accounting/libs/templatetags/form_filters.py
================================================
from django import template
from django.forms import ModelForm, BaseFormSet
from django.forms.forms import BoundField


register = template.Library()


@register.filter
def css_class(field):
    if isinstance(field, BoundField):
        field = field.field
    return field.widget.__class__.__name__.lower()


@register.filter
def is_disabled(field):
    if isinstance(field, BoundField):
        field = field.field
    return 'disabled' in field.widget.attrs


@register.filter
def is_readonly(field):
    if isinstance(field, BoundField):
        field = field.field
    return ('readonly' in field.widget.attrs and
        field.widget.attrs.get('readonly') is True)


@register.filter
def get_form_model_verbose_name(instance):
    if isinstance(instance, ModelForm):
        return instance._meta.model._meta.verbose_name.title()
    if isinstance(instance, BaseFormSet):
        return instance.model._meta.verbose_name_plural.title()
    return '<unknown>'


================================================
FILE: accounting/libs/templatetags/form_tags.py
================================================
from django import template

register = template.Library()


@register.tag
def annotate_form_field(parser, token):
    """
    Set an attribute on a form field with the widget type

    This means templates can use the widget type to render things differently
    if they want to.  Django doesn't make this available by default.
    """
    args = token.split_contents()
    if len(args) < 2:
        raise template.TemplateSyntaxError(
            "annotate_form_field tag requires a form field to be passed")
    return FormFieldNode(args[1])


class FormFieldNode(template.Node):

    def __init__(self, field_str):
        self.field = template.Variable(field_str)

    def render(self, context):
        field = self.field.resolve(context)
        field.widget_type = field.field.widget.__class__.__name__
        return ''


================================================
FILE: accounting/libs/templatetags/format_filters.py
================================================
import datetime

from django import template
from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now as django_now
from django.utils.translation import to_locale, get_language

from babel.numbers import format_percent

register = template.Library()


@register.filter('percentage')
def percentage_formatter(value):
    if value or value == 0:
        kwargs = {
            'locale': to_locale(get_language()),
            'format': "#,##0.00 %",
        }
        return format_percent(value, **kwargs)


@register.filter
def smartdate(value):
    if isinstance(value, datetime.datetime):
        now = django_now()
    else:
        now = datetime.date.today()

    timedelta = value - now
    format = _(u"%(delta)s %(unit)s")
    delta = abs(timedelta.days)

    if delta > 30:
        delta = int(delta / 30)
        unit = _(u"mois")
    else:
        unit = _(u"jours")

    ctx = {
        'delta': delta,
        'unit': unit,
    }

    return format % ctx


================================================
FILE: accounting/libs/templatetags/introspection_filters.py
================================================
from django import template
from django.forms import ModelForm, BaseFormSet
from django.db.models import Model

from django_select2.fields import (
    AutoModelSelect2Field,
    AutoModelSelect2MultipleField)


register = template.Library()


@register.filter
def get_model_verbose_name(instance):
    if isinstance(instance, Model):
        return instance._meta.verbose_name.title()
    return '<unknown>'


@register.filter
def get_form_model_verbose_name(instance):
    if isinstance(instance, ModelForm):
        return instance._meta.model._meta.verbose_name.title()
    if isinstance(instance, BaseFormSet):
        return instance.model._meta.verbose_name_plural.title()
    return '<unknown>'


@register.filter
def is_select2_field(form, field):
    select2_classes = (AutoModelSelect2Field, AutoModelSelect2MultipleField)
    res = any(isinstance(field.field, cls) for cls in select2_classes)
    return res


================================================
FILE: accounting/libs/templatetags/my_filters.py
================================================
from django import template

register = template.Library()


@register.filter(name='times')
def times(number):
    return range(number)


@register.filter
def get_object(l, index):
    return l[index]


@register.filter
def get_item(d, key, default=None):
    if default:
        d.get(key, None)
    return d.get(key)


================================================
FILE: accounting/libs/templatetags/nav.py
================================================
# encoding: utf-8

import re

from django import template
register = template.Library()


@register.simple_tag
def active(request, pattern, exact_match=False):
    if exact_match:
        if not pattern.startswith('^'):
            pattern = '^' + pattern
        if not pattern.endswith('$'):
            pattern = pattern + '$'
    if hasattr(request, 'path') and re.search(pattern, request.path):
        return 'active'
    return ''


================================================
FILE: accounting/libs/templatetags/url_tags.py
================================================
# encoding: utf-8

from django import template
from django.http import QueryDict

from classytags.core import Tag, Options
from classytags.arguments import MultiKeywordArgument, MultiValueArgument

register = template.Library()


class QueryParameters(Tag):
    name = 'query'
    options = Options(
        MultiKeywordArgument('kwa'),
    )

    def render_tag(self, context, kwa):
        q = QueryDict('').copy()
        q.update(kwa)
        return q.urlencode()


register.tag(QueryParameters)


class GetParameters(Tag):

    """
    {% get_parameters [except_field, ] %}
    """
    name = 'get_parameters'
    options = Options(
        MultiValueArgument('except_fields', required=False),
    )

    def render_tag(self, context, except_fields):
        try:
            # If there's an exception (500), default context_processors may not
            # be called.
            request = context['request']
        except KeyError:
            return context

        getvars = request.GET.copy()

        for field in except_fields:
            if field in getvars:
                del getvars[field]

        return getvars.urlencode()


register.tag(GetParameters)


================================================
FILE: accounting/libs/utils.py
================================================
import os
import uuid
import datetime
import random
import hashlib
import copy
import decimal


def banker_round(decimal_value):
    """
    Force the value to be rounded with the `ROUND_HALF_EVEN` method,
    also called the Banking Rounding due to the heavy use in the
    banking system
    """
    return decimal_value.quantize(decimal.Decimal('0.01'),
                                  rounding=decimal.ROUND_HALF_EVEN)


def random_token(extra=None, hash_func=hashlib.sha256):
    """
    Extracted from `django-user-accounts`
    """
    if extra is None:
        extra = []
    bits = extra + [str(random.SystemRandom().getrandbits(512))]
    return hash_func("".join(bits)).hexdigest()


def create_hash(string, hash_func=hashlib.sha256):
    """
    Create a 10-caracters string hash
    """
    _hash = hash_func(string)
    return _hash.hexdigest()[:10]


def nested_hash(data):
    """
    Make a hash from a nested dictionnary
    """

    if isinstance(data, (set, tuple, list)):
        return tuple(nested_hash(d) for d in data)

    elif not isinstance(data, dict):
        return data

    new_data = copy.deepcopy(data)
    for k, v in new_data.items():
        new_data[k] = nested_hash(v)

    return hash(tuple(frozenset(sorted(new_data.items()))))


def unique_filename(path):
    """
    Return a unique filename, which is usefull for image upload for instance
    """
    def _unique_path(obj, name):
        parts = name.split('.')
        extension = parts[-1]
        directory_path = os.path.normpath(
            datetime.datetime.now().strftime(path))
        unique_name = "{0}.{1}".format(uuid.uuid4(), extension)
        return os.path.join(directory_path, unique_name)
    return _unique_path


def queryset_iterator(queryset, chunksize=1000, reverse=False):
    """
    Execute the request by chunks to avoid database memory error
    """
    ordering = '-' if reverse else ''
    queryset = queryset.order_by(ordering + 'pk')
    last_pk = None
    new_items = True
    while new_items:
        new_items = False
        chunk = queryset
        if last_pk is not None:
            func = 'lt' if reverse else 'gt'
            chunk = chunk.filter(**{'pk__' + func: last_pk})
        chunk = chunk[:chunksize]
        row = None
        for row in chunk:
            yield row
        if row is not None:
            last_pk = row.pk
            new_items = True


================================================
FILE: accounting/static/accounting/css/main.css
================================================
/*
 * Base structure
 */

body {
  padding-top: 0px;
}


/*
 * Utils
 */

.overflow-box {
  overflow: auto;
}

.table .empty-line > td {
  border-top: none;
}
.table .empty-line + tr > td {
  border-top: none;
}

.table .empty-cell {
  border-top: none;
}


/*
 * Global add-ons
 */

.sub-header {
  padding-bottom: 10px;
  border-bottom: 1px solid #eee;
}

/*
 * Top navigation
 * Hide default border to remove 1px line.
 */
.navbar-fixed-top {
  border: 0;
}
.navbar-inverse {
  background: #222;
}
.navbar-nav > li {
  border-right: 1px solid rgba(255, 255, 255, 0.15);
}
.navbar-nav > li > a {
  padding-left: 2em;
  padding-right: 2em;
}
.navbar-nav > li > a:hover {
  background: rgba(255, 255, 255, 0.06) !important;
}
@media (min-width: 768px) {
  .nav .breadcrumb {
    margin: 7px 10px 7px 0px;
  }
}

/*
 * Sidebar
 */

/* Hide for mobile, show later */
.sidebar {
  display: none;
}
@media (min-width: 768px) {
  .sidebar {
    position: fixed;
    top: 0px;
    bottom: 0;
    left: 0;
    z-index: 1000;
    display: block;
    padding: 80px 20px 20px 20px;
    overflow-x: hidden;
    overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
    background-color: #f5f5f5;
    border-right: 1px solid #eee;
  }
}

/* Sidebar navigation */
.nav-sidebar {
  font-weight: 500;

  margin-right: -21px; /* 20px padding + 1px border */
  margin-bottom: 20px;
  margin-left: -20px;
}
.nav-sidebar > li > a {
  padding-right: 20px;
  padding-left: 20px;
}
.nav-sidebar > .active > a,
.nav-sidebar > .active > a:hover,
.nav-sidebar > .active > a:focus {
  color: #fff;
  background-color: #428bca;
}


/*
 * Main content
 */

.main {
  padding: 20px;
}
@media (min-width: 768px) {
  .main {
    padding-right: 40px;
    padding-left: 40px;
  }
}
.main .page-header {
  margin-top: 0;
}


/*
 * Placeholder dashboard ideas
 */

.placeholders {
  margin-bottom: 30px;
  text-align: center;
}
.placeholders h4 {
  margin-bottom: 0;
}
.placeholder {
  margin-bottom: 20px;
}
.placeholder img {
  display: inline-block;
  border-radius: 50%;
}


/*
 * Figure
 */
.figure {
  margin-bottom: 1em;
  padding: 0.8em 0.5em 1.5em 0.5em;
  background: white;
  background: rgba(255, 255, 255, 0.50);
  border: 1px solid rgba(0, 0, 0, 0.06);
  border-radius: 4px;
}


/*
 * Panels
 */
.panel-link {
  display: block;
}
.panel-link:hover {
  background: #f0f0f0;
  text-decoration: none;
}


/*
 * Check list
 */
.check-list {

}
.check-item {
  margin-right: 10px;
}

/*
 * Form labels
 */

.label-inlineblock {
    display: inline-block;
}

.control-label-block {
    display: block;
}


================================================
FILE: accounting/static/accounting/js/books/invoice_or_bill_create.js
================================================
$(function() {
    $('.formset-form').formset({
        prefix: 'lines',
        addText: 'add another line',
        deleteText: 'remove line',
        addCssClass: 'btn btn-primary btn-sm',
        deleteCssClass: 'btn btn-danger btn-sm',
        added: function($row) {
            // update line title
            var index = $row.index('.formset-form') + 1;
            $row.find('.counter').text(index);
        }
    });
})


================================================
FILE: accounting/static/accounting/js/jquery.formset.js
================================================
/**
 * jQuery Dynamic Formset
 * Inspired from the jQuery Formset plugin of Stanislaus Madueke
 */
;(function($) {
    $.fn.formset = function(opts)
    {
        var options = $.extend({}, $.fn.formset.defaults, opts),
            flatExtraClasses = options.extraClasses.join(' '),
            totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'),
            maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'),
            childElementSelector = 'input,select,textarea,label,div',
            $$ = $(this),

            applyExtraClasses = function(row, ndx) {
                if (options.extraClasses) {
                    row.removeClass(flatExtraClasses);
                    row.addClass(options.extraClasses[ndx % options.extraClasses.length]);
                }
            },

            updateElementIndex = function(elem, prefix, ndx) {
                var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'),
                    replacement = prefix + '-' + ndx + '-';
                if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement));
                if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement));
                if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement));
            },

            hasChildElements = function(row) {
                return row.find(childElementSelector).length > 0;
            },

            showAddButton = function() {
                return maxForms.length == 0 ||   // For Django versions pre 1.2
                    (maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0));
            },

            insertDeleteLink = function(row) {
                var delCssSelector = options.deleteCssClass.trim().replace(/\s+/g, '.'),
                    addCssSelector = options.addCssClass.trim().replace(/\s+/g, '.');
                if (row.is('TR')) {
                    // If the forms are laid out in table rows, insert
                    // the remove button into the last table cell:
                    row.children(':last').append('<a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + '</a>');
                } else if (row.is('UL') || row.is('OL')) {
                    // If they're laid out as an ordered/unordered list,
                    // insert an <li> after the last list item:
                    row.append('<li><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a></li>');
                } else {
                    // Otherwise, just insert the remove button as the
                    // last child element of the form's container:
                    row.append('<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>');
                }
                row.find('a.' + delCssSelector).click(function() {
                    var row = $(this).parents('.' + options.formCssClass),
                        del = row.find('input:hidden[id $= "-DELETE"]'),
                        buttonRow = row.siblings("a." + addCssSelector + ', .' + options.formCssClass + '-add'),
                        forms;
                    if (del.length) {
                        // We're dealing with an inline formset.
                        // Rather than remove this form from the DOM, we'll mark it as deleted
                        // and hide it, then let Django handle the deleting:
                        del.val('on');
                        row.hide();
                        forms = $('.' + options.formCssClass).not(':hidden');
                    } else {
                        row.remove();
                        // Update the TOTAL_FORMS count:
                        forms = $('.' + options.formCssClass).not('.formset-custom-template');
                        totalForms.val(forms.length);
                    }
                    for (var i=0, formCount=forms.length; i<formCount; i++) {
                        // Apply `extraClasses` to form rows so they're nicely alternating:
                        applyExtraClasses(forms.eq(i), i);
                        if (!del.length) {
                            // Also update names and IDs for all child controls (if this isn't
                            // a delete-able inline formset) so they remain in sequence:
                            forms.eq(i).find(childElementSelector).each(function() {
                                updateElementIndex($(this), options.prefix, i);
                            });
                        }
                    }
                    // Check if we need to show the add button:
                    if (buttonRow.is(':hidden') && showAddButton()) buttonRow.show();
                    // If a post-delete callback was provided, call it with the deleted form:
                    if (options.removed) options.removed(row);
                    return false;
                });
            };

        $$.each(function(i) {
            var row = $(this),
                del = row.find('input:checkbox[id $= "-DELETE"]');
            if (del.length) {
                // If you specify "can_delete = True" when creating an inline formset,
                // Django adds a checkbox to each form in the formset.
                // Replace the default checkbox with a hidden field:
                if (del.is(':checked')) {
                    // If an inline formset containing deleted forms fails validation, make sure
                    // we keep the forms hidden (thanks for the bug report and suggested fix Mike)
                    del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" value="on" />');
                    row.hide();
                } else {
                    del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" />');
                }
                // Hide any labels associated with the DELETE checkbox:
                $('label[for="' + del.attr('id') + '"]').hide();
                del.remove();
            }
            if (hasChildElements(row)) {
                row.addClass(options.formCssClass);
                if (row.is(':visible')) {
                    insertDeleteLink(row);
                    applyExtraClasses(row, i);
                }
            }
        });

        if ($$.length) {
            var hideAddButton = !showAddButton(),
                addButton, template;
            if (options.formTemplate) {
                // If a form template was specified, we'll clone it to generate new form instances:
                template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate);
                template.removeAttr('id').addClass(options.formCssClass + ' formset-custom-template');
                template.find(childElementSelector).each(function() {
                    updateElementIndex($(this), options.prefix, '__prefix__');
                });
                insertDeleteLink(template);
            } else {
                // Otherwise, use the last form in the formset; this works much better if you've got
                // extra (>= 1) forms (thnaks to justhamade for pointing this out):
                template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id');
                template.find('input:hidden[id $= "-DELETE"]').remove();
                // Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion):
                template.find(childElementSelector).not(options.keepFieldValues).each(function() {
                    var elem = $(this);
                    // If this is a checkbox or radiobutton, uncheck it.
                    // This fixes Issue 1, reported by Wilson.Andrew.J:
                    if (elem.is('input:checkbox') || elem.is('input:radio')) {
                        elem.attr('checked', false);
                    } else {
                        elem.val('');
                    }
                });
            }
            // FIXME: Perhaps using $.data would be a better idea?
            options.formTemplate = template;

            if ($$.is('TR')) {
                // If forms are laid out as table rows, insert the
                // "add" button in a new table row:
                var numCols = $$.eq(0).children().length,   // This is a bit of an assumption :|
                    buttonRow = $('<tr><td colspan="' + numCols + '"><a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a></tr>')
                                .addClass(options.formCssClass + '-add');
                $$.parent().append(buttonRow);
                if (hideAddButton) buttonRow.hide();
                addButton = buttonRow.find('a');
            } else {
                // Otherwise, insert it immediately after the last form:
                $$.filter(':last').after('<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>');
                addButton = $$.filter(':last').next();
                if (hideAddButton) addButton.hide();
            }
            addButton.click(function() {
                var formCount = parseInt(totalForms.val() || 0),
                    row = options.formTemplate.clone(true).removeClass('formset-custom-template'),
                    buttonRow = $($(this).parents('tr.' + options.formCssClass + '-add').get(0) || this);
                applyExtraClasses(row, formCount);
                row.insertBefore(buttonRow).show();
                row.find(childElementSelector).each(function() {
                    updateElementIndex($(this), options.prefix, formCount);
                });
                totalForms.val(formCount + 1);
                // Check if we've exceeded the maximum allowed number of forms:
                if (!showAddButton()) buttonRow.hide();
                // If a post-add callback was supplied, call it with the added form:
                if (options.added) options.added(row);
                return false;
            });
        }

        return $$;
    };

    /* Setup plugin defaults */
    $.fn.formset.defaults = {
        prefix: 'form',                  // The form prefix for your django formset
        formTemplate: null,              // The jQuery selection cloned to generate new form instances
        addText: 'add another',          // Text for the add link
        deleteText: 'remove',            // Text for the delete link
        addCssClass: 'add-row',          // CSS class applied to the add link
        deleteCssClass: 'delete-row',    // CSS class applied to the delete link
        formCssClass: 'dynamic-form',    // CSS class applied to each form in a formset
        extraClasses: [],                // Additional CSS classes, which will be applied to each form in turn
        keepFieldValues: '',             // jQuery selector for fields whose values should be kept when the form is cloned
        added: null,                     // Function called each time a new form is added
        removed: null                    // Function called each time a form is deleted
    };
})(jQuery);


================================================
FILE: accounting/static/accounting/js/main.js
================================================
$('[data-toggle="tooltip"]').tooltip();

================================================
FILE: accounting/templates/accounting/_generics/check_tag.html
================================================
{% load check_filters %}

{% if check.message %}
    <h4 class="list-group-item-heading">
        <span class="check-item"><span class="glyphicon glyphicon-{{ check|level_to_glyphicon }}"></span></span>
        {{ check.field.verbose_name }}
    </h4>
    <p class="list-group-item-text">
        <small>{{ check.message }}</small>
    </p>
{% else %}
    <span class="check-item"><span class="glyphicon glyphicon-{{ check|level_to_glyphicon }}"></span></span>
    {{ check.field.verbose_name }}
{% endif %}

================================================
FILE: accounting/templates/accounting/_generics/delete_entity.html
================================================
{% extends "accounting/layout.html" %}
{% load bootstrap3 introspection_filters %}

{% block sidebar %}{% endblock %}

{% block container %}

<div class="container-fluid">
    <div class="row">
        <div class="col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 main">

            <h1 class="page-header">You are about to delete...</h1>

            <div class="panel panel-danger">
                <div class="panel-heading">
                    <h3 class="panel-title">{{ object|get_model_verbose_name }}</h3>
                </div>
                <div class="panel-body">
                    {{ object }}
                </div>
            </div>

            <form action="." method="post" class="form">
                {% csrf_token %}
                {% buttons %}
                    <button type="submit" class="btn btn-danger">
                        {% bootstrap_icon "trash" %} Delete
                    </button>
                    <a href="#" onclick="window.history.back(); return false;" class="btn btn-warning pull-right">
                        Cancel
                    </a>
                {% endbuttons %}
            </form>

        </div>
    </div>
</div>
{% endblock container %}


================================================
FILE: accounting/templates/accounting/_generics/form.html
================================================
{% extends "accounting/layout.html" %}
{% load bootstrap3 introspection_filters %}

{% block head %}
    {{ block.super }}
    {{ form.media }}
{% endblock %}

{% block content %}
    {% if object.pk %}
        <h1 class="page-header">Edit a {{ form|get_form_model_verbose_name }}</h1>
    {% else %}
        <h1 class="page-header">Create a {{ form|get_form_model_verbose_name }}</h1>
    {% endif %}

    <form action="." method="post" class="form">
        {% csrf_token %}

        {% include "accounting/_partials/form_fields.html" with form=form %}

        {% buttons %}
            <button type="submit" class="btn btn-success">
                {% if object.pk %}
                    {% bootstrap_icon "hand-right" %} Save changes
                {% else %}
                    {% bootstrap_icon "thumbs-up" %} Send creation
                {% endif %}
            </button>
        {% endbuttons %}
    </form>
{% endblock content %}


================================================
FILE: accounting/templates/accounting/_partials/form_fields.html
================================================
{% load bootstrap3 introspection_filters %}

{% if return_url %}
<input type="hidden" name="_return_url" value="{{ return_url }}">
{% endif %}

{% bootstrap_form_errors form %}

{% for field in form %}
    {% if form|is_select2_field:field %}
        <div class="form-group">
            <label class="control-label control-label-block" for="{{ field.id_for_label }}">{{ field.label }}</label>
            {% bootstrap_field field %}
        </div>
    {% else %}
        {% bootstrap_field field %}
    {% endif %}
{% endfor %}

================================================
FILE: accounting/templates/accounting/base.html
================================================
{% load static %}<!DOCTYPE html>
<!--[if lt IE 7]>      <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]>         <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]>         <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title></title>
        <meta name="description" content="">

        <!-- Place favicon.ico and apple-touch-icon.png in the root directory -->

        <link rel="stylesheet" href="{% static 'bootstrap/dist/css/bootstrap.css' %}">
        <link rel="stylesheet" href="{% static 'bootstrap/dist/css/bootstrap-theme.css' %}">

        <style>
            @import url(http://fonts.googleapis.com/css?family=Bree+Serif);
            body {
                font-family: 'Helvetica Neue', sans-serif;
            }
            h1, h2, h3, h4, h5, h6{
                font-family: 'Bree Serif', serif;
            }
        </style>

        <!-- Custom styles for this template -->
        <link rel="stylesheet" href="{% static 'accounting/css/main.css' %}">

        <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
        <!--[if lt IE 9]>
          <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
          <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
        <![endif]-->

        <script src="{% static 'modernizr/modernizr.js' %}"></script>

        {% block head %}{% endblock %}
    </head>
    <body>
        <!--[if lt IE 7]>
            <p class="browsehappy">You are using an <strong>outdated</strong> browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
        <![endif]-->

        {% block maincontent %}{% endblock %}

        {% block scripts %}
            <script src="{% static 'jquery/dist/jquery.js' %}"></script>
            <script src="{% static 'bootstrap/dist/js/bootstrap.min.js' %}"></script>
            <script src="{% static 'accounting/js/main.js' %}"></script>
        {% endblock %}
    </body>
</html>


================================================
FILE: accounting/templates/accounting/books/_generics/sale_content.html
================================================
{% load currency_filters format_filters %}

<div class="row">
    <div class="col-xs-5">
        <div class="panel panel-default">
            {% with client=object.from_client %}
            <div class="panel-heading">
                <h4>From: <a href="{{ client.get_absolute_url }}">{{ client }}</a></h4>
            </div>
            <div class="panel-body">
                <address>
                    <strong>{{ client.name }}</strong><br>
                    {{ client.full_address|linebreaksbr }}<br>
                </address>
            </div>
            {% endwith %}
        </div>
    </div>
    <div class="col-xs-5 col-xs-offset-2 text-right">
        <div class="panel panel-default">
            {% with client=object.to_client %}
            <div class="panel-heading">
                <h4>To: <a href="{{ client.get_absolute_url }}">{{ client }}</a></h4>
            </div>
            <div class="panel-body">
                <address>
                    <strong>{{ client.name }}</strong><br>
                    {{ client.full_address|linebreaksbr }}<br>
                </address>
            </div>
            {% endwith %}
        </div>
    </div>
</div>
<!-- / end client details section -->
<table class="table table-striped table-bordered">
    <thead>
        <tr class="row">
            <th>#</th>
            <th>Label</th>
            <th class="col-sm-4">Description</th>
            <th class="col-sm-2">Unit (excl. tax)</th>
            <th class="col-sm-1">Qt.</th>
            <th class="col-sm-1">Tax rate</th>
            <th class="col-sm-2 text-right">Amount</th>
        </tr>
    </thead>
    <tbody>
        {% for line in lines.all %}
        <tr class="row">
            <td>{{ forloop.counter }}</td>
            <td>{{ line.label }}</td>
            <td class="col-sm-4">{{ line.description|linebreaks }}</td>
            <td class="col-sm-2">{{ line.unit_price_excl_tax|currency }}</td>
            <td class="col-sm-1">{{ line.quantity }}</td>
            <td class="col-sm-1">{{ line.tax_rate.rate|percentage }}</td>
            <td class="col-sm-2 text-right">{{ line.line_price_excl_tax|currency }}</td>
        </tr>
        {% endfor %}
    </tbody>
</table>
<div class="row text-right">
    <div class="col-xs-2 col-xs-offset-8">
        <p>
            <strong>
                Sub Total : <br>
                Tax : <br>
                Total : <br>
            </strong>
        </p>
    </div>
    <div class="col-xs-2">
        <strong>
            {{ object.total_excl_tax|currency }} <br>
            {{ object.total_tax|currency }} <br>
            {{ object.total_incl_tax|currency }} <br>
        </strong>
    </div>
</div>


================================================
FILE: accounting/templates/accounting/books/_generics/sale_detail.html
================================================
{% load currency_filters bootstrap3 form_filters check_filters check_tags %}

<div class="row placeholders">
    <div class="col-xs-6 col-sm-4">
        <div class="figure">
            <h4>{{ object.total_incl_tax|currency|default:"-" }}</h4>
            <span class="text-muted">Total (incl. tax)</span>
        </div>
    </div>
    <div class="col-xs-6 col-sm-4">
        <div class="figure">
            <h4>{{ object.total_excl_tax|currency|default:"-" }}</h4>
            <span class="text-muted">Total (excl. tax)</span>
        </div>
    </div>
    <div class="col-xs-6 col-sm-4">
        <div class="figure">
            <h4>{{ object.total_tax|currency }}</h4>
            <span class="text-muted">Tax</span>
        </div>
    </div>
    <div class="col-xs-6 col-sm-4">
        <div class="figure">
            <h4>{{ object.date_issued|default:"-" }}</h4>
            <span class="text-muted">Date issued</span>
        </div>
    </div>
    <div class="col-xs-6 col-sm-4">
        <div class="figure">
            <h4>{{ object.total_due_incl_tax|currency|default:"-" }}</h4>
            <span class="text-muted">Total still due (incl. tax)</span>
        </div>
    </div>
    <div class="col-xs-6 col-sm-4">
        <div class="figure">
            <h4>{{ object.date_dued|default:"-" }}</h4>
            <span class="text-muted">Date dued</span>
        </div>
    </div>
</div>

{% if checklist %}
<h3 class="page-header">Checklist</h3>
<div class="row">
    <div class="col-xs-6 col-sm-4">
        <div class="list-group check-list">
        {% for check in checklist %}
            <div class="list-group-item list-group-item-{{ check|level_to_css_classname }}">
                {% render_check check %}
            </div>
        {% endfor %}
        </div>
    </div>
</div>
{% endif %}

<h3 class="page-header">Content</h3>
{% include "books/_generics/sale_content.html" with object=object %}

<hr>

{% if payment_form %}
<div class="panel panel-default">
    <div class="panel-heading">
        <h4>Payments</h4>
    </div>

    {% include "books/_partials/payment_list.html" with payments=payments %}

    <div class="panel-footer">
        <form action="." method="post" class="form">
            {% csrf_token %}

            {{ payment_form.non_field_errors }}
            <div class="row">
                <div class="col-md-2">
                    {% bootstrap_field payment_form.date_paid show_label=False %}
                </div>
                <div class="col-md-2">
                    {% bootstrap_field payment_form.reference show_label=False %}
                </div>
                <div class="col-md-4">
                    {% bootstrap_field payment_form.detail show_label=False %}
                </div>
                <div class="col-md-2">
                    {% bootstrap_field payment_form.amount show_label=False %}
                </div>
                <div class="col-md-2 text-right">
                    <button type="submit" class="btn btn-success">Add payment</button>
                </div>
            </div>
        </form>
    </div>
</div>
{% endif %}


========================================
Download .txt
gitextract_lsm6zxq8/

├── .coveragerc
├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── README.rst
├── accounting/
│   ├── __init__.py
│   ├── apps/
│   │   ├── __init__.py
│   │   ├── books/
│   │   │   ├── __init__.py
│   │   │   ├── admin.py
│   │   │   ├── apps.py
│   │   │   ├── calculators.py
│   │   │   ├── context_processors.py
│   │   │   ├── forms.py
│   │   │   ├── managers.py
│   │   │   ├── middlewares.py
│   │   │   ├── migrations/
│   │   │   │   ├── 0001_initial.py
│   │   │   │   ├── 0002_auto_20141029_1606.py
│   │   │   │   ├── 0003_auto_20141029_1606.py
│   │   │   │   ├── 0004_auto_20141104_1026.py
│   │   │   │   ├── 0005_auto_20150128_1458.py
│   │   │   │   ├── 0006_auto_20150206_1836.py
│   │   │   │   ├── 0007_auto_20150206_1836.py
│   │   │   │   ├── 0008_auto_20150206_1843.py
│   │   │   │   ├── 0009_auto_20150206_1850.py
│   │   │   │   ├── 0010_auto_20150210_1449.py
│   │   │   │   ├── 0011_auto_20150324_1430.py
│   │   │   │   └── __init__.py
│   │   │   ├── mixins.py
│   │   │   ├── models.py
│   │   │   ├── templatetags/
│   │   │   │   ├── __init__.py
│   │   │   │   └── status_filters.py
│   │   │   ├── urls.py
│   │   │   ├── utils.py
│   │   │   └── views.py
│   │   ├── connect/
│   │   │   ├── __init__.py
│   │   │   ├── middlewares.py
│   │   │   ├── models.py
│   │   │   ├── steps.py
│   │   │   ├── urls.py
│   │   │   └── views.py
│   │   ├── context_processors.py
│   │   ├── people/
│   │   │   ├── __init__.py
│   │   │   ├── admin.py
│   │   │   ├── forms.py
│   │   │   ├── migrations/
│   │   │   │   ├── 0001_initial.py
│   │   │   │   ├── 0002_auto_20141029_1609.py
│   │   │   │   ├── 0003_employee_payroll_tax_rate.py
│   │   │   │   ├── 0004_auto_20141029_2306.py
│   │   │   │   ├── 0005_auto_20141029_2308.py
│   │   │   │   └── __init__.py
│   │   │   ├── models.py
│   │   │   ├── urls.py
│   │   │   └── views.py
│   │   └── reports/
│   │       ├── __init__.py
│   │       ├── admin.py
│   │       ├── forms.py
│   │       ├── migrations/
│   │       │   ├── 0001_initial.py
│   │       │   ├── 0002_auto_20150128_1458.py
│   │       │   ├── 0003_auto_20150131_1902.py
│   │       │   └── __init__.py
│   │       ├── models.py
│   │       ├── urls.py
│   │       ├── views.py
│   │       └── wrappers.py
│   ├── defaults.py
│   ├── libs/
│   │   ├── __init__.py
│   │   ├── checks.py
│   │   ├── decorators.py
│   │   ├── exceptions.py
│   │   ├── fields.py
│   │   ├── foundation.py
│   │   ├── intervals.py
│   │   ├── prices.py
│   │   ├── templatetags/
│   │   │   ├── __init__.py
│   │   │   ├── check_filters.py
│   │   │   ├── check_tags.py
│   │   │   ├── currency_filters.py
│   │   │   ├── display_tags.py
│   │   │   ├── distance_filters.py
│   │   │   ├── float_filters.py
│   │   │   ├── form_filters.py
│   │   │   ├── form_tags.py
│   │   │   ├── format_filters.py
│   │   │   ├── introspection_filters.py
│   │   │   ├── my_filters.py
│   │   │   ├── nav.py
│   │   │   └── url_tags.py
│   │   └── utils.py
│   ├── static/
│   │   └── accounting/
│   │       ├── css/
│   │       │   └── main.css
│   │       └── js/
│   │           ├── books/
│   │           │   └── invoice_or_bill_create.js
│   │           ├── jquery.formset.js
│   │           └── main.js
│   ├── templates/
│   │   └── accounting/
│   │       ├── _generics/
│   │       │   ├── check_tag.html
│   │       │   ├── delete_entity.html
│   │       │   └── form.html
│   │       ├── _partials/
│   │       │   └── form_fields.html
│   │       ├── base.html
│   │       ├── books/
│   │       │   ├── _generics/
│   │       │   │   ├── sale_content.html
│   │       │   │   ├── sale_detail.html
│   │       │   │   └── sale_list.html
│   │       │   ├── _partials/
│   │       │   │   ├── expense_claim_list.html
│   │       │   │   ├── payment_list.html
│   │       │   │   └── tax_rate_list.html
│   │       │   ├── bill_create_or_update.html
│   │       │   ├── bill_detail.html
│   │       │   ├── bill_list.html
│   │       │   ├── dashboard.html
│   │       │   ├── estimate_create_or_update.html
│   │       │   ├── estimate_detail.html
│   │       │   ├── estimate_list.html
│   │       │   ├── expense_claim_create_or_update.html
│   │       │   ├── expense_claim_detail.html
│   │       │   ├── expense_claim_list.html
│   │       │   ├── invoice_create_or_update.html
│   │       │   ├── invoice_detail.html
│   │       │   ├── invoice_list.html
│   │       │   ├── organization_create_or_update.html
│   │       │   ├── organization_detail.html
│   │       │   ├── organization_list.html
│   │       │   ├── organization_selector.html
│   │       │   ├── payment_create_or_update.html
│   │       │   ├── tax_rate_create_or_update.html
│   │       │   └── tax_rate_list.html
│   │       ├── connect/
│   │       │   └── getting_started.html
│   │       ├── layout.html
│   │       ├── people/
│   │       │   ├── client_create_or_update.html
│   │       │   ├── client_detail.html
│   │       │   ├── client_list.html
│   │       │   ├── employee_create_or_update.html
│   │       │   ├── employee_detail.html
│   │       │   └── employee_list.html
│   │       └── reports/
│   │           ├── _partials/
│   │           │   └── period_form.html
│   │           ├── financial_settings_update.html
│   │           ├── invoice_details_report.html
│   │           ├── pay_run_report.html
│   │           ├── payrun_settings_update.html
│   │           ├── profit_and_loss_report.html
│   │           ├── report_list.html
│   │           ├── settings_list.html
│   │           └── tax_report.html
│   ├── urls.py
│   └── wsgi.py
├── lint.sh
├── requirements.txt
├── runtests.py
├── setup.cfg
├── setup.py
└── tests/
    ├── __init__.py
    ├── _site/
    │   ├── __init__.py
    │   └── model_tests_app/
    │       ├── __init__.py
    │       └── models.py
    ├── config.py
    ├── functional/
    │   ├── __init__.py
    │   └── libs/
    │       ├── __init__.py
    │       ├── context_processors_tests.py
    │       ├── template_tags_tests.py
    │       └── utils_tests.py
    ├── integration/
    │   └── __init__.py
    ├── settings.py
    └── unit/
        ├── __init__.py
        ├── books/
        │   ├── __init__.py
        │   ├── bill_tests.py
        │   └── invoice_tests.py
        ├── clients/
        │   ├── __init__.py
        │   ├── organization_tests.py
        │   └── user_tests.py
        └── libs/
            ├── __init__.py
            └── prices_tests.py
Download .txt
SYMBOL INDEX (570 symbols across 75 files)

FILE: accounting/__init__.py
  function get_short_version (line 9) | def get_short_version():
  function get_version (line 13) | def get_version():
  function get_apps (line 55) | def get_apps():

FILE: accounting/apps/books/admin.py
  class OrganizationAdmin (line 8) | class OrganizationAdmin(admin.ModelAdmin):
  class TaxRateAdmin (line 13) | class TaxRateAdmin(admin.ModelAdmin):
  class PaymentInline (line 17) | class PaymentInline(GenericTabularInline):
  class EstimateLineInline (line 22) | class EstimateLineInline(admin.TabularInline):
  class EstimateAdmin (line 28) | class EstimateAdmin(admin.ModelAdmin):
  class InvoiceLineInline (line 37) | class InvoiceLineInline(admin.TabularInline):
  class InvoiceAdmin (line 43) | class InvoiceAdmin(admin.ModelAdmin):
  class BillLineInline (line 53) | class BillLineInline(admin.TabularInline):
  class BillAdmin (line 59) | class BillAdmin(admin.ModelAdmin):
  class ExpenseClaimLineInline (line 69) | class ExpenseClaimLineInline(admin.TabularInline):
  class ExpenseClaimAdmin (line 75) | class ExpenseClaimAdmin(admin.ModelAdmin):
  class PaymentAdmin (line 86) | class PaymentAdmin(admin.ModelAdmin):

FILE: accounting/apps/books/apps.py
  class FrenchBooksConfig (line 4) | class FrenchBooksConfig(AppConfig):

FILE: accounting/apps/books/calculators.py
  class SalePaymentLineProcessed (line 6) | class SalePaymentLineProcessed(object):
    method __init__ (line 12) | def __init__(self, sale, payment):
    method process (line 17) | def process(self):
  class ProfitsLossCalculator (line 34) | class ProfitsLossCalculator(object):
    method __init__ (line 51) | def __init__(self, organization, sum_type=SUM_TYPE_COLLECTED,
    method process_generator (line 57) | def process_generator(self, sales_queryset):
    method total_collected (line 103) | def total_collected(self):
    method total_expenses (line 110) | def total_expenses(self):
    method profits (line 117) | def profits(self):

FILE: accounting/apps/books/context_processors.py
  function organizations (line 4) | def organizations(request):

FILE: accounting/apps/books/forms.py
  class RequiredFirstInlineFormSet (line 24) | class RequiredFirstInlineFormSet(BaseInlineFormSet):
    method __init__ (line 30) | def __init__(self, *args, **kwargs):
  class SaleInlineLineFormSet (line 37) | class SaleInlineLineFormSet(RequiredFirstInlineFormSet):
    method __init__ (line 39) | def __init__(self, *args, **kwargs):
  class ClientForOrganizationChoices (line 46) | class ClientForOrganizationChoices(AutoModelSelect2Field):
    method prepare_qs_params (line 52) | def prepare_qs_params(self, request, search_term, search_fields):
  class EmployeeForOrganizationChoices (line 60) | class EmployeeForOrganizationChoices(AutoModelSelect2Field):
    method prepare_qs_params (line 68) | def prepare_qs_params(self, request, search_term, search_fields):
  class OrganizationForm (line 76) | class OrganizationForm(ModelForm):
    class Meta (line 79) | class Meta:
  class TaxRateForm (line 88) | class TaxRateForm(ModelForm):
    class Meta (line 89) | class Meta:
  class RestrictLineFormToOrganizationMixin (line 97) | class RestrictLineFormToOrganizationMixin(object):
    method __init__ (line 99) | def __init__(self, *args, **kwargs):
    method restrict_to_organization (line 114) | def restrict_to_organization(self, organization):
  class EstimateForm (line 118) | class EstimateForm(ModelForm):
    class Meta (line 121) | class Meta:
  class EstimateLineForm (line 143) | class EstimateLineForm(RestrictLineFormToOrganizationMixin,
    class Meta (line 145) | class Meta:
  class InvoiceForm (line 164) | class InvoiceForm(ModelForm):
    class Meta (line 167) | class Meta:
  class InvoiceLineForm (line 189) | class InvoiceLineForm(RestrictLineFormToOrganizationMixin,
    class Meta (line 191) | class Meta:
  class BillForm (line 210) | class BillForm(ModelForm):
    class Meta (line 213) | class Meta:
  class BillLineForm (line 235) | class BillLineForm(RestrictLineFormToOrganizationMixin,
    class Meta (line 237) | class Meta:
    method __init__ (line 247) | def __init__(self, *args, **kwargs):
  class ExpenseClaimForm (line 259) | class ExpenseClaimForm(ModelForm):
    class Meta (line 262) | class Meta:
  class ExpenseClaimLineForm (line 284) | class ExpenseClaimLineForm(RestrictLineFormToOrganizationMixin,
    class Meta (line 286) | class Meta:
    method __init__ (line 296) | def __init__(self, *args, **kwargs):
  class PaymentForm (line 308) | class PaymentForm(ModelForm):
    class Meta (line 309) | class Meta:

FILE: accounting/apps/books/managers.py
  class TotalQuerySetMixin (line 7) | class TotalQuerySetMixin(object):
    method _get_total (line 9) | def _get_total(self, prop):
    method total_paid (line 12) | def total_paid(self):
  class InvoiceQuerySetMixin (line 16) | class InvoiceQuerySetMixin(object):
    method dued (line 18) | def dued(self):
  class EstimateQuerySet (line 22) | class EstimateQuerySet(TotalQuerySetMixin, models.QuerySet):
  class InvoiceQuerySet (line 26) | class InvoiceQuerySet(TotalQuerySetMixin,
    method turnover_excl_tax (line 30) | def turnover_excl_tax(self):
    method turnover_incl_tax (line 33) | def turnover_incl_tax(self):
  class BillQuerySet (line 37) | class BillQuerySet(TotalQuerySetMixin,
    method debts_excl_tax (line 41) | def debts_excl_tax(self):
    method debts_incl_tax (line 44) | def debts_incl_tax(self):
  class ExpenseClaimQuerySet (line 48) | class ExpenseClaimQuerySet(TotalQuerySetMixin,
    method debts_excl_tax (line 52) | def debts_excl_tax(self):
    method debts_incl_tax (line 55) | def debts_incl_tax(self):

FILE: accounting/apps/books/middlewares.py
  class AutoSelectOrganizationMiddleware (line 4) | class AutoSelectOrganizationMiddleware(object):
    method process_request (line 6) | def process_request(self, request):

FILE: accounting/apps/books/migrations/0001_initial.py
  class Migration (line 13) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0002_auto_20141029_1606.py
  function next_invoice_number (line 12) | def next_invoice_number():
  class Migration (line 16) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0003_auto_20141029_1606.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0004_auto_20141104_1026.py
  function next_invoice_number (line 8) | def next_invoice_number():
  class Migration (line 12) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0005_auto_20150128_1458.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0006_auto_20150206_1836.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0007_auto_20150206_1836.py
  function _migrate_sale_numbers (line 7) | def _migrate_sale_numbers(sales):
  function migrate_estimate_numbers (line 13) | def migrate_estimate_numbers(apps, schema_editor):
  function migrate_invoice_numbers (line 18) | def migrate_invoice_numbers(apps, schema_editor):
  function migrate_bill_numbers (line 23) | def migrate_bill_numbers(apps, schema_editor):
  class Migration (line 28) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0008_auto_20150206_1843.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0009_auto_20150206_1850.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0010_auto_20150210_1449.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/books/migrations/0011_auto_20150324_1430.py
  class Migration (line 10) | class Migration(migrations.Migration):

FILE: accounting/apps/books/mixins.py
  class RestrictToSelectedOrganizationQuerySetMixin (line 9) | class RestrictToSelectedOrganizationQuerySetMixin(object):
    method get_restriction_filters (line 14) | def get_restriction_filters(self):
    method get_queryset (line 23) | def get_queryset(self):
    method get (line 29) | def get(self, request, *args, **kwargs):
  class RestrictToOrganizationFormRelationsMixin (line 36) | class RestrictToOrganizationFormRelationsMixin(object):
    method _restrict_fields_choices (line 42) | def _restrict_fields_choices(self, model, organization, fields):
    method restrict_fields_choices_to_organization (line 61) | def restrict_fields_choices_to_organization(self, form, organization):
  class SaleListQuerySetMixin (line 67) | class SaleListQuerySetMixin(object):
    method get_queryset (line 69) | def get_queryset(self):
  class AutoSetSelectedOrganizationMixin (line 95) | class AutoSetSelectedOrganizationMixin(object):
    method form_valid (line 97) | def form_valid(self, form):
  class AbstractSaleCreateUpdateMixin (line 105) | class AbstractSaleCreateUpdateMixin(RestrictToOrganizationFormRelationsM...
    method get_context_data (line 109) | def get_context_data(self, **kwargs):
    method get_form (line 124) | def get_form(self, form_class=None):
    method form_valid (line 131) | def form_valid(self, form):
  class AbstractSaleDetailMixin (line 147) | class AbstractSaleDetailMixin(object):
    method get_queryset (line 149) | def get_queryset(self):
    method get_object (line 162) | def get_object(self):
    method get_context_data (line 171) | def get_context_data(self, **kwargs):
  class PaymentFormMixin (line 181) | class PaymentFormMixin(generic.edit.FormMixin):
    method get_context_data (line 184) | def get_context_data(self, **kwargs):
    method post (line 193) | def post(self, request, *args, **kwargs):
    method form_valid (line 204) | def form_valid(self, form):

FILE: accounting/apps/books/models.py
  class Organization (line 27) | class Organization(models.Model):
    class Meta (line 40) | class Meta:
    method __str__ (line 43) | def __str__(self):
    method get_absolute_url (line 46) | def get_absolute_url(self):
    method turnover_excl_tax (line 50) | def turnover_excl_tax(self):
    method turnover_incl_tax (line 54) | def turnover_incl_tax(self):
    method debts_excl_tax (line 58) | def debts_excl_tax(self):
    method debts_incl_tax (line 62) | def debts_incl_tax(self):
    method profits (line 66) | def profits(self):
    method collected_tax (line 70) | def collected_tax(self):
    method deductible_tax (line 74) | def deductible_tax(self):
    method tax_provisionning (line 78) | def tax_provisionning(self):
    method overdue_total (line 82) | def overdue_total(self):
  class TaxRate (line 89) | class TaxRate(models.Model):
    class Meta (line 111) | class Meta:
    method __str__ (line 114) | def __str__(self):
  class AbstractSale (line 118) | class AbstractSale(CheckingModelMixin, models.Model):
    class Meta (line 141) | class Meta:
    class CheckingOptions (line 144) | class CheckingOptions:
    method __str__ (line 151) | def __str__(self):
    method get_detail_url (line 154) | def get_detail_url(self):
    method get_edit_url (line 157) | def get_edit_url(self):
    method compute_totals (line 160) | def compute_totals(self):
    method _get_total (line 164) | def _get_total(self, prop):
    method total_tax (line 176) | def total_tax(self):
    method get_total_excl_tax (line 179) | def get_total_excl_tax(self):
    method get_total_incl_tax (line 182) | def get_total_incl_tax(self):
    method total_paid (line 186) | def total_paid(self):
    method total_due_incl_tax (line 193) | def total_due_incl_tax(self):
    method is_fully_paid (line 198) | def is_fully_paid(self):
    method is_partially_paid (line 203) | def is_partially_paid(self):
    method payroll_taxes (line 209) | def payroll_taxes(self):
    method _check_total (line 219) | def _check_total(self, check, total, computed_total):
    method check_total_excl_tax (line 231) | def check_total_excl_tax(self, check):
    method check_total_incl_tax (line 235) | def check_total_incl_tax(self, check):
    method check_date_dued (line 239) | def check_date_dued(self, check):
  class AbstractSaleLine (line 262) | class AbstractSaleLine(models.Model):
    class Meta (line 271) | class Meta:
    method __str__ (line 274) | def __str__(self):
    method unit_price (line 278) | def unit_price(self):
    method line_price_excl_tax (line 286) | def line_price_excl_tax(self):
    method line_price_incl_tax (line 290) | def line_price_incl_tax(self):
    method taxes (line 294) | def taxes(self):
    method from_client (line 297) | def from_client(self):
    method to_client (line 300) | def to_client(self):
  class Estimate (line 304) | class Estimate(AbstractSale):
    class Meta (line 313) | class Meta:
    method get_detail_url (line 317) | def get_detail_url(self):
    method get_edit_url (line 320) | def get_edit_url(self):
    method from_client (line 323) | def from_client(self):
    method to_client (line 326) | def to_client(self):
  class EstimateLine (line 330) | class EstimateLine(AbstractSaleLine):
    class Meta (line 335) | class Meta:
  class Invoice (line 339) | class Invoice(AbstractSale):
    class Meta (line 349) | class Meta:
    method get_detail_url (line 353) | def get_detail_url(self):
    method get_edit_url (line 356) | def get_edit_url(self):
    method from_client (line 359) | def from_client(self):
    method to_client (line 362) | def to_client(self):
  class InvoiceLine (line 366) | class InvoiceLine(AbstractSaleLine):
    class Meta (line 371) | class Meta:
  class Bill (line 375) | class Bill(AbstractSale):
    class Meta (line 385) | class Meta:
    method get_detail_url (line 389) | def get_detail_url(self):
    method get_edit_url (line 392) | def get_edit_url(self):
    method from_client (line 395) | def from_client(self):
    method to_client (line 398) | def to_client(self):
  class BillLine (line 402) | class BillLine(AbstractSaleLine):
    class Meta (line 407) | class Meta:
  class ExpenseClaim (line 411) | class ExpenseClaim(AbstractSale):
    class Meta (line 421) | class Meta:
    method get_detail_url (line 425) | def get_detail_url(self):
    method get_edit_url (line 428) | def get_edit_url(self):
    method from_client (line 431) | def from_client(self):
    method to_client (line 434) | def to_client(self):
  class ExpenseClaimLine (line 438) | class ExpenseClaimLine(AbstractSaleLine):
    class Meta (line 443) | class Meta:
  class Payment (line 447) | class Payment(models.Model):
    class Meta (line 464) | class Meta:
    method __str__ (line 467) | def __str__(self):

FILE: accounting/apps/books/templatetags/status_filters.py
  function _invoice_or_bill_status_to_classname (line 7) | def _invoice_or_bill_status_to_classname(invoice_or_bill):

FILE: accounting/apps/books/utils.py
  class OrganizationManager (line 4) | class OrganizationManager(object):
    method get_user_organizations (line 7) | def get_user_organizations(self, user):
    method set_selected_organization (line 16) | def set_selected_organization(self, request, organization):
    method get_selected_organization (line 20) | def get_selected_organization(self, request):
  class BaseNumberGenerator (line 36) | class BaseNumberGenerator(object):
    method next_number (line 41) | def next_number(self, organization):
  class EstimateNumberGenerator (line 45) | class EstimateNumberGenerator(BaseNumberGenerator):
    method next_number (line 47) | def next_number(self, organization):
  class InvoiceNumberGenerator (line 56) | class InvoiceNumberGenerator(BaseNumberGenerator):
    method next_number (line 58) | def next_number(self, organization):
  class BillNumberGenerator (line 67) | class BillNumberGenerator(BaseNumberGenerator):
    method next_number (line 69) | def next_number(self, organization):
  class ExpenseClaimNumberGenerator (line 78) | class ExpenseClaimNumberGenerator(BaseNumberGenerator):
    method next_number (line 80) | def next_number(self, organization):

FILE: accounting/apps/books/views.py
  class OrganizationSelectorView (line 46) | class OrganizationSelectorView(generic.TemplateView):
    method get_context_data (line 49) | def get_context_data(self, **kwargs):
  class DashboardView (line 71) | class DashboardView(generic.DetailView):
    method get_object (line 76) | def get_object(self):
    method get_context_data (line 79) | def get_context_data(self, **kwargs):
    method get (line 102) | def get(self, request, *args, **kwargs):
  class OrganizationListView (line 109) | class OrganizationListView(generic.ListView):
    method get_queryset (line 114) | def get_queryset(self):
  class OrganizationCreateView (line 119) | class OrganizationCreateView(generic.CreateView):
    method form_valid (line 125) | def form_valid(self, form):
  class OrganizationUpdateView (line 131) | class OrganizationUpdateView(generic.UpdateView):
    method get_queryset (line 137) | def get_queryset(self):
  class OrganizationDetailView (line 142) | class OrganizationDetailView(generic.DetailView):
    method get_queryset (line 147) | def get_queryset(self):
    method get_context_data (line 151) | def get_context_data(self, **kwargs):
  class OrganizationSelectionView (line 163) | class OrganizationSelectionView(generic.DetailView):
    method get_queryset (line 166) | def get_queryset(self):
    method post (line 170) | def post(self, request, *args, **kwargs):
  class TaxRateListView (line 176) | class TaxRateListView(RestrictToSelectedOrganizationQuerySetMixin,
  class TaxRateCreateView (line 183) | class TaxRateCreateView(AutoSetSelectedOrganizationMixin,
  class TaxRateUpdateView (line 191) | class TaxRateUpdateView(AutoSetSelectedOrganizationMixin,
  class TaxRateDeleteView (line 199) | class TaxRateDeleteView(generic.DeleteView):
  class PaymentUpdateView (line 205) | class PaymentUpdateView(generic.UpdateView):
    method get_success_url (line 210) | def get_success_url(self):
  class PaymentDeleteView (line 222) | class PaymentDeleteView(generic.DeleteView):
  class EstimateListView (line 228) | class EstimateListView(RestrictToSelectedOrganizationQuerySetMixin,
  class EstimateCreateView (line 236) | class EstimateCreateView(AutoSetSelectedOrganizationMixin,
    method get_form (line 245) | def get_form(self, form_class=None):
    method get_initial (line 251) | def get_initial(self):
  class EstimateUpdateView (line 260) | class EstimateUpdateView(AutoSetSelectedOrganizationMixin,
  class EstimateDeleteView (line 270) | class EstimateDeleteView(generic.DeleteView):
  class EstimateDetailView (line 276) | class EstimateDetailView(AbstractSaleDetailMixin,
    method get_success_url (line 282) | def get_success_url(self):
  class InvoiceListView (line 286) | class InvoiceListView(RestrictToSelectedOrganizationQuerySetMixin,
  class InvoiceCreateView (line 294) | class InvoiceCreateView(AutoSetSelectedOrganizationMixin,
    method get_form (line 303) | def get_form(self, form_class=None):
    method get_initial (line 309) | def get_initial(self):
  class InvoiceUpdateView (line 318) | class InvoiceUpdateView(AutoSetSelectedOrganizationMixin,
  class InvoiceDeleteView (line 328) | class InvoiceDeleteView(generic.DeleteView):
  class InvoiceDetailView (line 334) | class InvoiceDetailView(PaymentFormMixin,
    method get_success_url (line 342) | def get_success_url(self):
  class BillListView (line 346) | class BillListView(RestrictToSelectedOrganizationQuerySetMixin,
  class BillCreateView (line 354) | class BillCreateView(AutoSetSelectedOrganizationMixin,
    method get_form (line 363) | def get_form(self, form_class=None):
    method get_initial (line 369) | def get_initial(self):
  class BillUpdateView (line 378) | class BillUpdateView(AutoSetSelectedOrganizationMixin,
  class BillDeleteView (line 388) | class BillDeleteView(generic.DeleteView):
  class BillDetailView (line 394) | class BillDetailView(PaymentFormMixin,
    method get_success_url (line 402) | def get_success_url(self):
  class ExpenseClaimListView (line 406) | class ExpenseClaimListView(RestrictToSelectedOrganizationQuerySetMixin,
  class ExpenseClaimCreateView (line 414) | class ExpenseClaimCreateView(AutoSetSelectedOrganizationMixin,
    method get_form (line 423) | def get_form(self, form_class=None):
    method get_initial (line 429) | def get_initial(self):
  class ExpenseClaimUpdateView (line 438) | class ExpenseClaimUpdateView(AutoSetSelectedOrganizationMixin,
  class ExpenseClaimDeleteView (line 448) | class ExpenseClaimDeleteView(generic.DeleteView):
  class ExpenseClaimDetailView (line 454) | class ExpenseClaimDetailView(PaymentFormMixin,
    method get_success_url (line 462) | def get_success_url(self):

FILE: accounting/apps/connect/middlewares.py
  class ForceGettingStartedMiddleware (line 1) | class ForceGettingStartedMiddleware(object):
    method process_request (line 3) | def process_request(self, request):

FILE: accounting/apps/connect/steps.py
  class StepOptions (line 15) | class StepOptions(object):
    method __init__ (line 19) | def __init__(self, meta):
  class BaseStep (line 28) | class BaseStep(object):
    class StepOptions (line 37) | class StepOptions:
    method __init__ (line 41) | def __init__(self, user):
    method completed (line 46) | def completed(self, request):
    method is_completed (line 51) | def is_completed(self):
    method check_completion (line 59) | def check_completion(self, request):
    method get_action_url (line 66) | def get_action_url(self):
  class CreateOrganizationStep (line 71) | class CreateOrganizationStep(BaseStep):
    class StepOptions (line 76) | class StepOptions:
    method check_completion (line 81) | def check_completion(self, request):
    method get_action_url (line 86) | def get_action_url(self):
  class ConfigureTaxRatesStep (line 90) | class ConfigureTaxRatesStep(BaseStep):
    class StepOptions (line 95) | class StepOptions:
    method check_completion (line 100) | def check_completion(self, request):
    method get_action_url (line 107) | def get_action_url(self):
  class ConfigureBusinessSettingsStep (line 111) | class ConfigureBusinessSettingsStep(BaseStep):
    class StepOptions (line 116) | class StepOptions:
    method check_completion (line 120) | def check_completion(self, request):
    method get_action_url (line 133) | def get_action_url(self):
  class ConfigureFinancialSettingsStep (line 137) | class ConfigureFinancialSettingsStep(BaseStep):
    class StepOptions (line 139) | class StepOptions:
    method check_completion (line 143) | def check_completion(self, request):
    method get_action_url (line 156) | def get_action_url(self):
  class AddEmployeesStep (line 160) | class AddEmployeesStep(BaseStep):
    class StepOptions (line 162) | class StepOptions:
    method check_completion (line 167) | def check_completion(self, request):
    method get_action_url (line 174) | def get_action_url(self):
  class ConfigurePayRunSettingsStep (line 178) | class ConfigurePayRunSettingsStep(BaseStep):
    class StepOptions (line 180) | class StepOptions:
    method check_completion (line 184) | def check_completion(self, request):
    method get_action_url (line 197) | def get_action_url(self):
  class AddFirstClientStep (line 201) | class AddFirstClientStep(BaseStep):
    class StepOptions (line 203) | class StepOptions:
    method check_completion (line 207) | def check_completion(self, request):
    method get_action_url (line 214) | def get_action_url(self):
  class AddFirstInvoiceStep (line 218) | class AddFirstInvoiceStep(BaseStep):
    class StepOptions (line 220) | class StepOptions:
    method check_completion (line 224) | def check_completion(self, request):
    method get_action_url (line 231) | def get_action_url(self):

FILE: accounting/apps/connect/views.py
  class RootRedirectionView (line 17) | class RootRedirectionView(generic.View):
    method get (line 25) | def get(self, *args, **kwargs):
  class GettingStartedView (line 30) | class GettingStartedView(generic.TemplateView):
    method get_steps (line 33) | def get_steps(self, request):
    method get_context_data (line 47) | def get_context_data(self, **kwargs):
    method post (line 68) | def post(self, request, *args, **kwargs):

FILE: accounting/apps/context_processors.py
  function metadata (line 4) | def metadata(request):

FILE: accounting/apps/people/admin.py
  class ClientAdmin (line 7) | class ClientAdmin(admin.ModelAdmin):
  class EmployeeAdmin (line 12) | class EmployeeAdmin(admin.ModelAdmin):

FILE: accounting/apps/people/forms.py
  class ClientForm (line 11) | class ClientForm(ModelForm):
    class Meta (line 12) | class Meta:
  class EmployeeForm (line 24) | class EmployeeForm(ModelForm):
    class Meta (line 25) | class Meta:
  class UserChoices (line 44) | class UserChoices(AutoModelSelect2Field):
  class UserMultipleChoices (line 54) | class UserMultipleChoices(AutoModelSelect2MultipleField):

FILE: accounting/apps/people/migrations/0001_initial.py
  class Migration (line 9) | class Migration(migrations.Migration):

FILE: accounting/apps/people/migrations/0002_auto_20141029_1609.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/people/migrations/0003_employee_payroll_tax_rate.py
  class Migration (line 9) | class Migration(migrations.Migration):

FILE: accounting/apps/people/migrations/0004_auto_20141029_2306.py
  function _link_to_first_organization (line 7) | def _link_to_first_organization(apps, schema_editor):
  class Migration (line 12) | class Migration(migrations.Migration):

FILE: accounting/apps/people/migrations/0005_auto_20141029_2308.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/people/models.py
  class Client (line 7) | class Client(models.Model):
    class Meta (line 21) | class Meta:
    method __str__ (line 24) | def __str__(self):
    method active_address_fields (line 27) | def active_address_fields(self):
    method full_address (line 36) | def full_address(self, separator="\n"):
  class Employee (line 40) | class Employee(models.Model):
    class Meta (line 67) | class Meta:
    method __str__ (line 70) | def __str__(self):
    method composite_name (line 74) | def composite_name(self):

FILE: accounting/apps/people/views.py
  class ClientListView (line 11) | class ClientListView(RestrictToSelectedOrganizationQuerySetMixin,
  class ClientCreateView (line 18) | class ClientCreateView(AutoSetSelectedOrganizationMixin,
    method get_success_url (line 24) | def get_success_url(self):
  class ClientUpdateView (line 28) | class ClientUpdateView(RestrictToSelectedOrganizationQuerySetMixin,
    method get_success_url (line 35) | def get_success_url(self):
  class ClientDetailView (line 39) | class ClientDetailView(RestrictToSelectedOrganizationQuerySetMixin,
  class EmployeeListView (line 46) | class EmployeeListView(RestrictToSelectedOrganizationQuerySetMixin,
  class EmployeeCreateView (line 53) | class EmployeeCreateView(AutoSetSelectedOrganizationMixin,
    method get_success_url (line 59) | def get_success_url(self):
  class EmployeeUpdateView (line 63) | class EmployeeUpdateView(RestrictToSelectedOrganizationQuerySetMixin,
    method get_success_url (line 70) | def get_success_url(self):
  class EmployeeDetailView (line 74) | class EmployeeDetailView(RestrictToSelectedOrganizationQuerySetMixin,

FILE: accounting/apps/reports/admin.py
  class FinancialSettingsAdmin (line 7) | class FinancialSettingsAdmin(admin.ModelAdmin):

FILE: accounting/apps/reports/forms.py
  class BusinessSettingsForm (line 11) | class BusinessSettingsForm(forms.ModelForm):
    class Meta (line 12) | class Meta:
  class FinancialSettingsForm (line 19) | class FinancialSettingsForm(forms.ModelForm):
    class Meta (line 20) | class Meta:
  class PayRunSettingsForm (line 32) | class PayRunSettingsForm(forms.ModelForm):
    class Meta (line 33) | class Meta:
  class TimePeriodForm (line 41) | class TimePeriodForm(forms.Form):
    method _determine_filter_metadata (line 50) | def _determine_filter_metadata(self):
    method get_filters (line 79) | def get_filters(self):
    method get_filter_description (line 84) | def get_filter_description(self):

FILE: accounting/apps/reports/migrations/0001_initial.py
  class Migration (line 8) | class Migration(migrations.Migration):

FILE: accounting/apps/reports/migrations/0002_auto_20150128_1458.py
  class Migration (line 8) | class Migration(migrations.Migration):

FILE: accounting/apps/reports/migrations/0003_auto_20150131_1902.py
  class Migration (line 7) | class Migration(migrations.Migration):

FILE: accounting/apps/reports/models.py
  class BusinessSettings (line 5) | class BusinessSettings(models.Model):
    class Meta (line 23) | class Meta:
  class FinancialSettings (line 27) | class FinancialSettings(models.Model):
    class Meta (line 66) | class Meta:
  class PayRunSettings (line 70) | class PayRunSettings(models.Model):
    class Meta (line 89) | class Meta:

FILE: accounting/apps/reports/views.py
  class TimePeriodFormMixin (line 27) | class TimePeriodFormMixin(object):
    method get_initial (line 31) | def get_initial(self):
    method get_form_kwargs (line 48) | def get_form_kwargs(self):
    method get_context_data (line 56) | def get_context_data(self, **kwargs):
  class ReportListView (line 74) | class ReportListView(generic.TemplateView):
  class SettingsListView (line 78) | class SettingsListView(generic.TemplateView):
  class GenericSettingsMixin (line 82) | class GenericSettingsMixin(object):
    method get_object (line 84) | def get_object(self):
    method get_success_url (line 92) | def get_success_url(self):
  class BusinessSettingsUpdateView (line 96) | class BusinessSettingsUpdateView(GenericSettingsMixin,
  class FinancialSettingsUpdateView (line 103) | class FinancialSettingsUpdateView(GenericSettingsMixin,
  class PayRunSettingsUpdateView (line 110) | class PayRunSettingsUpdateView(GenericSettingsMixin,
  class TaxReportView (line 117) | class TaxReportView(TimePeriodFormMixin,
    method get_context_data (line 122) | def get_context_data(self, **kwargs):
  class ProfitAndLossReportView (line 133) | class ProfitAndLossReportView(generic.TemplateView):
    method get_context_data (line 136) | def get_context_data(self, **kwargs):
  class PayRunReportView (line 156) | class PayRunReportView(TimePeriodFormMixin,
    method get_context_data (line 161) | def get_context_data(self, **kwargs):
  class InvoiceDetailsView (line 175) | class InvoiceDetailsView(TimePeriodFormMixin,
    method get_context_data (line 180) | def get_context_data(self, **kwargs):

FILE: accounting/apps/reports/wrappers.py
  class BaseReport (line 11) | class BaseReport(object):
    method __init__ (line 15) | def __init__(self, title, start, end):
    method generate (line 19) | def generate(self):
  class TaxRateSummary (line 23) | class TaxRateSummary(object):
    method collected_taxes (line 29) | def collected_taxes(self):
    method deductible_taxes (line 33) | def deductible_taxes(self):
    method net_amount (line 37) | def net_amount(self):
    method net_taxes (line 41) | def net_taxes(self):
  class TaxReport (line 45) | class TaxReport(BaseReport):
    method __init__ (line 50) | def __init__(self, organization, start, end):
    method generate (line 55) | def generate(self):
    method generate_for_sales (line 61) | def generate_for_sales(self, sales_queryset):
  class ProfitAndLossSummary (line 79) | class ProfitAndLossSummary(object):
    method net_profit (line 85) | def net_profit(self):
  class ProfitAndLossReport (line 89) | class ProfitAndLossReport(BaseReport):
    method __init__ (line 101) | def __init__(self, organization, start, end):
    method group_by_date (line 119) | def group_by_date(self, date):
    method generate (line 127) | def generate(self):
    method generate_for_sales (line 141) | def generate_for_sales(self, sales_queryset):
  class PayRunSummary (line 159) | class PayRunSummary(object):
    method payroll_taxes (line 164) | def payroll_taxes(self):
  class PayRunReport (line 168) | class PayRunReport(BaseReport):
    method __init__ (line 173) | def __init__(self, organization, start, end):
    method generate (line 178) | def generate(self):
    method generate_for_employees (line 182) | def generate_for_employees(self, employee_queryset):
  class InvoiceDetailsReport (line 206) | class InvoiceDetailsReport(BaseReport):
    method __init__ (line 211) | def __init__(self, organization, start, end):
    method generate (line 216) | def generate(self):
    method generate_for_invoices (line 220) | def generate_for_invoices(self, invoice_queryset):

FILE: accounting/libs/checks.py
  class PrimaryKeyRelatedField (line 5) | class PrimaryKeyRelatedField(object):
  class CheckResult (line 9) | class CheckResult(object):
    method __init__ (line 30) | def __init__(self, field, result=None, level=None, message=None):
    method mark_fail (line 42) | def mark_fail(self, level=LEVEL_WARNING, message=None):
    method mark_pass (line 47) | def mark_pass(self, message=None):
    method has_failed (line 52) | def has_failed(self):
    method has_passed (line 56) | def has_passed(self):
  class CheckingModelOptions (line 60) | class CheckingModelOptions(object):
    method __init__ (line 64) | def __init__(self, meta):
  class CheckingModelMixin (line 74) | class CheckingModelMixin(object):
    method __init__ (line 78) | def __init__(self, *args, **kwargs):
    method has_custom_check_for_field (line 82) | def has_custom_check_for_field(self, field_name):
    method get_check_for_field (line 85) | def get_check_for_field(self, field_name, checking_fields=None):
    method get_checking_fields (line 113) | def get_checking_fields(self, special_exclude=['id']):
    method check_fields (line 150) | def check_fields(self):
    method full_check (line 167) | def full_check(self):
    method check_additionnals (line 183) | def check_additionnals(self):
    method _raw_checking_completion (line 187) | def _raw_checking_completion(self):
    method checking_completion (line 195) | def checking_completion(self):
    method full_checking_completion (line 203) | def full_checking_completion(self):
    method pass_full_checking (line 213) | def pass_full_checking(self):

FILE: accounting/libs/decorators.py
  function composed (line 4) | def composed(*decs):
  function order_fields (line 21) | def order_fields(*field_list):
  function memoize (line 34) | def memoize(func):

FILE: accounting/libs/fields.py
  class UUIDField (line 5) | class UUIDField(models.CharField):
    method __init__ (line 7) | def __init__(self, *args, **kwargs):
    method _generate_uuid (line 12) | def _generate_uuid(self):
    method pre_save (line 15) | def pre_save(self, model_instance, add):

FILE: accounting/libs/foundation.py
  function update (line 8) | def update(d, u, depth=-1):

FILE: accounting/libs/intervals.py
  class TimeInterval (line 4) | class TimeInterval(object):
    method __init__ (line 8) | def __init__(self, start, end):

FILE: accounting/libs/prices.py
  class TaxNotKnown (line 1) | class TaxNotKnown(Exception):
  class Price (line 8) | class Price(object):
    method __init__ (line 20) | def __init__(self, currency, excl_tax, incl_tax=None, tax=None):
    method _get_tax (line 33) | def _get_tax(self):
    method _set_tax (line 36) | def _set_tax(self, value):
    method __repr__ (line 42) | def __repr__(self):
    method __eq__ (line 50) | def __eq__(self, other):

FILE: accounting/libs/templatetags/check_filters.py
  function check (line 9) | def check(obj, field_name=None):
  function _check_level_to_classname (line 25) | def _check_level_to_classname(check):
  function _check_level_to_glyphicon (line 41) | def _check_level_to_glyphicon(check):

FILE: accounting/libs/templatetags/check_tags.py
  class Check (line 11) | class Check(InclusionTag):
    method get_context (line 18) | def get_context(self, context, check):

FILE: accounting/libs/templatetags/currency_filters.py
  function currency_formatter (line 15) | def currency_formatter(value, currency=None):

FILE: accounting/libs/templatetags/distance_filters.py
  function has_distance (line 9) | def has_distance(search_object):
  function distance (line 14) | def distance(dist):

FILE: accounting/libs/templatetags/float_filters.py
  function do_float_dot (line 8) | def do_float_dot(value, decimal_pos=4):

FILE: accounting/libs/templatetags/form_filters.py
  function css_class (line 10) | def css_class(field):
  function is_disabled (line 17) | def is_disabled(field):
  function is_readonly (line 24) | def is_readonly(field):
  function get_form_model_verbose_name (line 32) | def get_form_model_verbose_name(instance):

FILE: accounting/libs/templatetags/form_tags.py
  function annotate_form_field (line 7) | def annotate_form_field(parser, token):
  class FormFieldNode (line 21) | class FormFieldNode(template.Node):
    method __init__ (line 23) | def __init__(self, field_str):
    method render (line 26) | def render(self, context):

FILE: accounting/libs/templatetags/format_filters.py
  function percentage_formatter (line 14) | def percentage_formatter(value):
  function smartdate (line 24) | def smartdate(value):

FILE: accounting/libs/templatetags/introspection_filters.py
  function get_model_verbose_name (line 14) | def get_model_verbose_name(instance):
  function get_form_model_verbose_name (line 21) | def get_form_model_verbose_name(instance):
  function is_select2_field (line 30) | def is_select2_field(form, field):

FILE: accounting/libs/templatetags/my_filters.py
  function times (line 7) | def times(number):
  function get_object (line 12) | def get_object(l, index):
  function get_item (line 17) | def get_item(d, key, default=None):

FILE: accounting/libs/templatetags/nav.py
  function active (line 10) | def active(request, pattern, exact_match=False):

FILE: accounting/libs/templatetags/url_tags.py
  class QueryParameters (line 12) | class QueryParameters(Tag):
    method render_tag (line 18) | def render_tag(self, context, kwa):
  class GetParameters (line 27) | class GetParameters(Tag):
    method render_tag (line 37) | def render_tag(self, context, except_fields):

FILE: accounting/libs/utils.py
  function banker_round (line 10) | def banker_round(decimal_value):
  function random_token (line 20) | def random_token(extra=None, hash_func=hashlib.sha256):
  function create_hash (line 30) | def create_hash(string, hash_func=hashlib.sha256):
  function nested_hash (line 38) | def nested_hash(data):
  function unique_filename (line 56) | def unique_filename(path):
  function queryset_iterator (line 70) | def queryset_iterator(queryset, chunksize=1000, reverse=False):

FILE: runtests.py
  function run_tests (line 47) | def run_tests(verbosity, *test_args):

FILE: tests/_site/model_tests_app/models.py
  class MockModelWitNoFields (line 9) | class MockModelWitNoFields(models.Model):
  class MockModelWithUUIDField (line 14) | class MockModelWithUUIDField(models.Model):

FILE: tests/config.py
  function configure (line 5) | def configure():

FILE: tests/functional/libs/context_processors_tests.py
  class TestLibsMetadata (line 7) | class TestLibsMetadata(TestCase):
    method setUp (line 9) | def setUp(self):
    method test_has_version (line 14) | def test_has_version(self):

FILE: tests/functional/libs/template_tags_tests.py
  class TestGetParameterTemplateTag (line 12) | class TestGetParameterTemplateTag(TestCase):
    method setUp (line 14) | def setUp(self):
    method test_retrieve_get_param (line 21) | def test_retrieve_get_param(self):
    method test_except_fields (line 28) | def test_except_fields(self):
  class TestFormFilterCssClass (line 36) | class TestFormFilterCssClass(TestCase):
    method setUp (line 38) | def setUp(self):
    method test_text_field_class_name (line 45) | def test_text_field_class_name(self):
  class TestFormFilterIsDisable (line 57) | class TestFormFilterIsDisable(TestCase):
    method setUp (line 59) | def setUp(self):
    method test_return_true_for_disable_fields (line 67) | def test_return_true_for_disable_fields(self):
    method test_return_true_for_disable_bounded_fields (line 71) | def test_return_true_for_disable_bounded_fields(self):
  class TestFormFilterIsReadonly (line 81) | class TestFormFilterIsReadonly(TestCase):
    method setUp (line 83) | def setUp(self):
    method test_return_true_for_readonly_fields (line 91) | def test_return_true_for_readonly_fields(self):
    method test_return_true_for_readonly_bounded_fields (line 95) | def test_return_true_for_readonly_bounded_fields(self):
  class TestMyFilterTimes (line 105) | class TestMyFilterTimes(TestCase):
    method test_simple_times_filter_loop (line 107) | def test_simple_times_filter_loop(self):
  class TestMyFilterGetItem (line 117) | class TestMyFilterGetItem(TestCase):
    method test_wrong_type_should_raise_exception (line 119) | def test_wrong_type_should_raise_exception(self):
    method test_simple_dict_value_for_key (line 128) | def test_simple_dict_value_for_key(self):
  class TestMyFilterGetObject (line 138) | class TestMyFilterGetObject(TestCase):
    method test_simple_list_value_for_index (line 140) | def test_simple_list_value_for_index(self):
  class TestFormatFilterPercentage (line 150) | class TestFormatFilterPercentage(TestCase):
    method test_basic_value (line 152) | def test_basic_value(self):
    method test_zero_value (line 161) | def test_zero_value(self):
  class TestUrlTagQuery (line 171) | class TestUrlTagQuery(TestCase):
    method test_no_parameter_raises_exception (line 173) | def test_no_parameter_raises_exception(self):
    method test_multiple_parameters (line 181) | def test_multiple_parameters(self):
    method test_url_encode_spaces (line 188) | def test_url_encode_spaces(self):
  class TestCurrencyFilter (line 196) | class TestCurrencyFilter(TestCase):
    method setUp (line 198) | def setUp(self):
    method test_renders_price_correctly (line 204) | def test_renders_price_correctly(self):
    method test_handles_none_price_gracefully (line 212) | def test_handles_none_price_gracefully(self):
    method test_handles_string_price_gracefully (line 217) | def test_handles_string_price_gracefully(self):

FILE: tests/functional/libs/utils_tests.py
  class TestQuerysetIteratorHelper (line 16) | class TestQuerysetIteratorHelper(TransactionTestCase):
    method setUp (line 18) | def setUp(self):

FILE: tests/unit/books/bill_tests.py
  class TestBillQuerySetMethods (line 11) | class TestBillQuerySetMethods(TestCase):
    method setUp (line 13) | def setUp(self):
    method test_returns_correct_turnovers (line 16) | def test_returns_correct_turnovers(self):

FILE: tests/unit/books/invoice_tests.py
  class TestInvoiceQuerySetMethods (line 11) | class TestInvoiceQuerySetMethods(TestCase):
    method setUp (line 13) | def setUp(self):
    method test_returns_correct_turnovers (line 16) | def test_returns_correct_turnovers(self):

FILE: tests/unit/clients/organization_tests.py
  class TestOrganizationCalcultation (line 10) | class TestOrganizationCalcultation(TestCase):
    method setUp (line 12) | def setUp(self):
    method tearDown (line 15) | def tearDown(self):
    method test_turnover_excl_tax_is_valid (line 18) | def test_turnover_excl_tax_is_valid(self):
    method test_turnover_incl_tax_is_valid (line 32) | def test_turnover_incl_tax_is_valid(self):
    method test_debts_excl_tax_is_valid (line 46) | def test_debts_excl_tax_is_valid(self):
    method test_debts_incl_tax_is_valid (line 60) | def test_debts_incl_tax_is_valid(self):
    method test_profits_is_valid (line 74) | def test_profits_is_valid(self):
    method test_collected_tax_is_valid (line 93) | def test_collected_tax_is_valid(self):
    method test_deductible_tax_is_valid (line 112) | def test_deductible_tax_is_valid(self):
    method test_tax_provisionning_is_valid (line 131) | def test_tax_provisionning_is_valid(self):

FILE: tests/unit/clients/user_tests.py
  class TestUserBelongsToOrganization (line 11) | class TestUserBelongsToOrganization(TestCase):
    method setUp (line 13) | def setUp(self):
    method tearDown (line 17) | def tearDown(self):
    method test_no_organization_by_default (line 20) | def test_no_organization_by_default(self):
    method test_belongs_to_an_organization (line 23) | def test_belongs_to_an_organization(self):

FILE: tests/unit/libs/prices_tests.py
  class TestPriceObject (line 8) | class TestPriceObject(TestCase):
    method test_can_be_instantiated_with_tax_amount (line 10) | def test_can_be_instantiated_with_tax_amount(self):
    method test_can_have_tax_set_later (line 15) | def test_can_have_tax_set_later(self):
    method test_price_equals_reflexivity (line 20) | def test_price_equals_reflexivity(self):
    method test_price_equals_formats (line 28) | def test_price_equals_formats(self):
    method test_price_equals_currency_matters (line 33) | def test_price_equals_currency_matters(self):
    method test_price_equals_transitivity (line 38) | def test_price_equals_transitivity(self):
Condensed preview — 169 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (287K chars).
[
  {
    "path": ".coveragerc",
    "chars": 46,
    "preview": "[run]\nsource = accounting\nomit = *migrations*\n"
  },
  {
    "path": ".gitignore",
    "chars": 222,
    "preview": "## Byte-compiled\n__pycache__/\n*.py[cod]\n*.egg-info\n/dist/\n/build/\n\n## Database\n*.sqlite3\n\n## Translations\n*.mo\n\n## Compo"
  },
  {
    "path": ".travis.yml",
    "chars": 368,
    "preview": "# Use the newer container-based infrastructure\n# http://docs.travis-ci.com/user/workers/container-based-infrastructure/\n"
  },
  {
    "path": "LICENSE",
    "chars": 1078,
    "preview": "Copyright (c) 2013 Pierre Dulac (https://dulacp.com/)\n\nPermission is hereby granted, free of charge, to any person obtai"
  },
  {
    "path": "Makefile",
    "chars": 982,
    "preview": "# These targets are not files\n.PHONY: contribute travis test lint coverage\n\nclean:\n\t# Remove files not in source control"
  },
  {
    "path": "README.rst",
    "chars": 2374,
    "preview": "django-accounting\n=================\n\n    In the beginning God created man, and the costs followed afterwards.\n\n|Build St"
  },
  {
    "path": "accounting/__init__.py",
    "chars": 1384,
    "preview": "import os\n\n# Use 'final' as the 4th element to indicate\n# a full release\n\nVERSION = (0, 2, 10)\n\n\ndef get_short_version()"
  },
  {
    "path": "accounting/apps/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/books/__init__.py",
    "chars": 68,
    "preview": "default_app_config = 'accounting.apps.books.apps.FrenchBooksConfig'\n"
  },
  {
    "path": "accounting/apps/books/admin.py",
    "chars": 1669,
    "preview": "from django.contrib import admin\nfrom django.contrib.contenttypes.admin import GenericTabularInline\n\nfrom . import model"
  },
  {
    "path": "accounting/apps/books/apps.py",
    "chars": 145,
    "preview": "from django.apps import AppConfig\n\n\nclass FrenchBooksConfig(AppConfig):\n    name = \"accounting.apps.books\"\n    verbose_n"
  },
  {
    "path": "accounting/apps/books/calculators.py",
    "chars": 3944,
    "preview": "from decimal import Decimal as D\n\nfrom accounting.libs.intervals import TimeInterval\n\n\nclass SalePaymentLineProcessed(ob"
  },
  {
    "path": "accounting/apps/books/context_processors.py",
    "chars": 609,
    "preview": "from .utils import organization_manager\n\n\ndef organizations(request):\n    \"\"\"\n    Add some generally useful metadata to "
  },
  {
    "path": "accounting/apps/books/forms.py",
    "chars": 9554,
    "preview": "from django.forms import ModelForm, BaseInlineFormSet\nfrom django.forms.models import inlineformset_factory\n\nfrom .model"
  },
  {
    "path": "accounting/apps/books/managers.py",
    "chars": 1351,
    "preview": "from datetime import date\n\nfrom django.db import models\nfrom django.db.models import Sum\n\n\nclass TotalQuerySetMixin(obje"
  },
  {
    "path": "accounting/apps/books/middlewares.py",
    "chars": 561,
    "preview": "from .utils import organization_manager\n\n\nclass AutoSelectOrganizationMiddleware(object):\n\n    def process_request(self,"
  },
  {
    "path": "accounting/apps/books/migrations/0001_initial.py",
    "chars": 1237,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nimport account"
  },
  {
    "path": "accounting/apps/books/migrations/0002_auto_20141029_1606.py",
    "chars": 7977,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nimport django."
  },
  {
    "path": "accounting/apps/books/migrations/0003_auto_20141029_1606.py",
    "chars": 2976,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/books/migrations/0004_auto_20141104_1026.py",
    "chars": 1288,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nimport account"
  },
  {
    "path": "accounting/apps/books/migrations/0005_auto_20150128_1458.py",
    "chars": 782,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/books/migrations/0006_auto_20150206_1836.py",
    "chars": 860,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/books/migrations/0007_auto_20150206_1836.py",
    "chars": 969,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\ndef _migrate"
  },
  {
    "path": "accounting/apps/books/migrations/0008_auto_20150206_1843.py",
    "chars": 1378,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/books/migrations/0009_auto_20150206_1850.py",
    "chars": 1471,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/books/migrations/0010_auto_20150210_1449.py",
    "chars": 1162,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/books/migrations/0011_auto_20150324_1430.py",
    "chars": 2627,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nfrom decimal i"
  },
  {
    "path": "accounting/apps/books/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/books/mixins.py",
    "chars": 6774,
    "preview": "from django.db.models.fields import FieldDoesNotExist\nfrom django.views import generic\nfrom django.http import HttpRespo"
  },
  {
    "path": "accounting/apps/books/models.py",
    "chars": 14896,
    "preview": "from decimal import Decimal as D\nfrom datetime import date\n\nfrom django.conf import settings\nfrom django.db import model"
  },
  {
    "path": "accounting/apps/books/templatetags/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/books/templatetags/status_filters.py",
    "chars": 630,
    "preview": "from django import template\n\nregister = template.Library()\n\n\n@register.filter('status_to_css_classname')\ndef _invoice_or"
  },
  {
    "path": "accounting/apps/books/urls.py",
    "chars": 3957,
    "preview": "from django.conf.urls import patterns, url\n\nfrom . import views\n\n\nurlpatterns = patterns('',\n    url(r'^$',\n        view"
  },
  {
    "path": "accounting/apps/books/utils.py",
    "chars": 2368,
    "preview": "from django.db.models import Q\n\n\nclass OrganizationManager(object):\n    selected_organization_key = 'selected_organizati"
  },
  {
    "path": "accounting/apps/books/views.py",
    "chars": 15416,
    "preview": "import logging\nfrom decimal import Decimal as D\n\nfrom django.views import generic\nfrom django.core.urlresolvers import r"
  },
  {
    "path": "accounting/apps/connect/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/connect/middlewares.py",
    "chars": 99,
    "preview": "class ForceGettingStartedMiddleware(object):\n\n    def process_request(self, request):\n        pass\n"
  },
  {
    "path": "accounting/apps/connect/models.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/connect/steps.py",
    "chars": 6668,
    "preview": "import logging\n\nfrom django.core.urlresolvers import reverse\nfrom django.core.exceptions import ValidationError\n\nfrom ac"
  },
  {
    "path": "accounting/apps/connect/urls.py",
    "chars": 305,
    "preview": "from django.conf.urls import patterns, url\n\nfrom . import views\n\n\nurlpatterns = patterns('',\n\n    url(r'^$',\n        vie"
  },
  {
    "path": "accounting/apps/connect/views.py",
    "chars": 2405,
    "preview": "from django.views import generic\nfrom django.http import HttpResponseRedirect\nfrom django.core.urlresolvers import rever"
  },
  {
    "path": "accounting/apps/context_processors.py",
    "chars": 404,
    "preview": "from django.conf import settings\n\n\ndef metadata(request):\n    \"\"\"\n    Add some generally useful metadata to the template"
  },
  {
    "path": "accounting/apps/people/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/people/admin.py",
    "chars": 217,
    "preview": "from django.contrib import admin\n\nfrom . import models\n\n\n@admin.register(models.Client)\nclass ClientAdmin(admin.ModelAdm"
  },
  {
    "path": "accounting/apps/people/forms.py",
    "chars": 1328,
    "preview": "from django.forms import ModelForm\nfrom django.contrib.auth import get_user_model\n\nfrom .models import Client, Employee\n"
  },
  {
    "path": "accounting/apps/people/migrations/0001_initial.py",
    "chars": 2020,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nfrom decimal i"
  },
  {
    "path": "accounting/apps/people/migrations/0002_auto_20141029_1609.py",
    "chars": 410,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/people/migrations/0003_employee_payroll_tax_rate.py",
    "chars": 661,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nfrom decimal i"
  },
  {
    "path": "accounting/apps/people/migrations/0004_auto_20141029_2306.py",
    "chars": 462,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\ndef _link_to"
  },
  {
    "path": "accounting/apps/people/migrations/0005_auto_20141029_2308.py",
    "chars": 442,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/people/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/people/models.py",
    "chars": 2149,
    "preview": "from decimal import Decimal as D\n\nfrom django.db import models\nfrom django.core.validators import MinValueValidator, Max"
  },
  {
    "path": "accounting/apps/people/urls.py",
    "chars": 985,
    "preview": "from django.conf.urls import patterns, url\n\nfrom . import views\n\n\nurlpatterns = patterns('',\n\n    # Clients\n    url(r'^c"
  },
  {
    "path": "accounting/apps/people/views.py",
    "chars": 2462,
    "preview": "from django.views import generic\nfrom django.core.urlresolvers import reverse\n\nfrom accounting.apps.books.mixins import "
  },
  {
    "path": "accounting/apps/reports/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/reports/admin.py",
    "chars": 156,
    "preview": "from django.contrib import admin\n\nfrom . import models\n\n\n@admin.register(models.FinancialSettings)\nclass FinancialSettin"
  },
  {
    "path": "accounting/apps/reports/forms.py",
    "chars": 2440,
    "preview": "from datetime import timedelta\n\nfrom django import forms\n\nfrom .models import (\n    BusinessSettings,\n    FinancialSetti"
  },
  {
    "path": "accounting/apps/reports/migrations/0001_initial.py",
    "chars": 2732,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nimport django."
  },
  {
    "path": "accounting/apps/reports/migrations/0002_auto_20150128_1458.py",
    "chars": 864,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\nimport django."
  },
  {
    "path": "accounting/apps/reports/migrations/0003_auto_20150131_1902.py",
    "chars": 576,
    "preview": "# -*- coding: utf-8 -*-\nfrom __future__ import unicode_literals\n\nfrom django.db import models, migrations\n\n\nclass Migrat"
  },
  {
    "path": "accounting/apps/reports/migrations/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/apps/reports/models.py",
    "chars": 3416,
    "preview": "from django.db import models\nfrom django.core.validators import MinValueValidator, MaxValueValidator\n\n\nclass BusinessSet"
  },
  {
    "path": "accounting/apps/reports/urls.py",
    "chars": 1110,
    "preview": "from django.conf.urls import patterns, url\n\nfrom . import views\n\n\nurlpatterns = patterns('',\n\n    # Reports\n    url(r'^r"
  },
  {
    "path": "accounting/apps/reports/views.py",
    "chars": 5761,
    "preview": "from datetime import date\n\nfrom django.views import generic\nfrom django.core.urlresolvers import reverse\nfrom django.uti"
  },
  {
    "path": "accounting/apps/reports/wrappers.py",
    "chars": 7987,
    "preview": "from decimal import Decimal as D\nfrom collections import defaultdict, OrderedDict\n\nfrom dateutil.relativedelta import re"
  },
  {
    "path": "accounting/defaults.py",
    "chars": 36,
    "preview": "ACCOUNTING_DEFAULT_CURRENCY = \"EUR\"\n"
  },
  {
    "path": "accounting/libs/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/libs/checks.py",
    "chars": 6436,
    "preview": "from django.core.validators import EMPTY_VALUES\nfrom django.utils.datastructures import SortedDict\n\n\nclass PrimaryKeyRel"
  },
  {
    "path": "accounting/libs/decorators.py",
    "chars": 1012,
    "preview": "# encoding: utf-8\n\n\ndef composed(*decs):\n    \"\"\"\n    Compose multiple decorators\n\n    Example :\n\n    >>> @composed(dec1,"
  },
  {
    "path": "accounting/libs/exceptions.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/libs/fields.py",
    "chars": 657,
    "preview": "from django.db import models\nimport uuid\n\n\nclass UUIDField(models.CharField):\n\n    def __init__(self, *args, **kwargs):\n"
  },
  {
    "path": "accounting/libs/foundation.py",
    "chars": 549,
    "preview": "\"\"\"\nPython helpers\n\"\"\"\n\nfrom collections import Mapping\n\n\ndef update(d, u, depth=-1):\n    \"\"\"\n    Recursively merge or u"
  },
  {
    "path": "accounting/libs/intervals.py",
    "chars": 379,
    "preview": "from datetime import date\n\n\nclass TimeInterval(object):\n    start = None\n    end = None\n\n    def __init__(self, start, e"
  },
  {
    "path": "accounting/libs/prices.py",
    "chars": 1827,
    "preview": "class TaxNotKnown(Exception):\n    \"\"\"\n    Exception for when a tax-inclusive price is requested but we don't know\n    wh"
  },
  {
    "path": "accounting/libs/templatetags/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/libs/templatetags/check_filters.py",
    "chars": 1275,
    "preview": "from django import template\n\nfrom accounting.libs.checks import CheckingModelMixin\n\nregister = template.Library()\n\n\n@reg"
  },
  {
    "path": "accounting/libs/templatetags/check_tags.py",
    "chars": 480,
    "preview": "from django import template\n\nfrom classytags.core import Options\nfrom classytags.arguments import Argument\nfrom classyta"
  },
  {
    "path": "accounting/libs/templatetags/currency_filters.py",
    "chars": 879,
    "preview": "# encoding: utf-8\n\nfrom decimal import Decimal as D, InvalidOperation\n\nfrom django import template\nfrom django.conf impo"
  },
  {
    "path": "accounting/libs/templatetags/display_tags.py",
    "chars": 59,
    "preview": "from django import template\n\nregister = template.Library()\n"
  },
  {
    "path": "accounting/libs/templatetags/distance_filters.py",
    "chars": 532,
    "preview": "from django import template\n\nfrom django.contrib.gis.measure import Distance\n\nregister = template.Library()\n\n\n@register."
  },
  {
    "path": "accounting/libs/templatetags/float_filters.py",
    "chars": 258,
    "preview": "from django.template import Library\nfrom django.utils.numberformat import format\n\nregister = Library()\n\n\n@register.filte"
  },
  {
    "path": "accounting/libs/templatetags/form_filters.py",
    "chars": 965,
    "preview": "from django import template\nfrom django.forms import ModelForm, BaseFormSet\nfrom django.forms.forms import BoundField\n\n\n"
  },
  {
    "path": "accounting/libs/templatetags/form_tags.py",
    "chars": 829,
    "preview": "from django import template\n\nregister = template.Library()\n\n\n@register.tag\ndef annotate_form_field(parser, token):\n    \""
  },
  {
    "path": "accounting/libs/templatetags/format_filters.py",
    "chars": 1005,
    "preview": "import datetime\n\nfrom django import template\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.utils.t"
  },
  {
    "path": "accounting/libs/templatetags/introspection_filters.py",
    "chars": 920,
    "preview": "from django import template\nfrom django.forms import ModelForm, BaseFormSet\nfrom django.db.models import Model\n\nfrom dja"
  },
  {
    "path": "accounting/libs/templatetags/my_filters.py",
    "chars": 319,
    "preview": "from django import template\n\nregister = template.Library()\n\n\n@register.filter(name='times')\ndef times(number):\n    retur"
  },
  {
    "path": "accounting/libs/templatetags/nav.py",
    "chars": 438,
    "preview": "# encoding: utf-8\n\nimport re\n\nfrom django import template\nregister = template.Library()\n\n\n@register.simple_tag\ndef activ"
  },
  {
    "path": "accounting/libs/templatetags/url_tags.py",
    "chars": 1176,
    "preview": "# encoding: utf-8\n\nfrom django import template\nfrom django.http import QueryDict\n\nfrom classytags.core import Tag, Optio"
  },
  {
    "path": "accounting/libs/utils.py",
    "chars": 2401,
    "preview": "import os\nimport uuid\nimport datetime\nimport random\nimport hashlib\nimport copy\nimport decimal\n\n\ndef banker_round(decimal"
  },
  {
    "path": "accounting/static/accounting/css/main.css",
    "chars": 2604,
    "preview": "/*\n * Base structure\n */\n\nbody {\n  padding-top: 0px;\n}\n\n\n/*\n * Utils\n */\n\n.overflow-box {\n  overflow: auto;\n}\n\n.table .e"
  },
  {
    "path": "accounting/static/accounting/js/books/invoice_or_bill_create.js",
    "chars": 431,
    "preview": "$(function() {\n    $('.formset-form').formset({\n        prefix: 'lines',\n        addText: 'add another line',\n        de"
  },
  {
    "path": "accounting/static/accounting/js/jquery.formset.js",
    "chars": 11296,
    "preview": "/**\n * jQuery Dynamic Formset\n * Inspired from the jQuery Formset plugin of Stanislaus Madueke\n */\n;(function($) {\n    $"
  },
  {
    "path": "accounting/static/accounting/js/main.js",
    "chars": 39,
    "preview": "$('[data-toggle=\"tooltip\"]').tooltip();"
  },
  {
    "path": "accounting/templates/accounting/_generics/check_tag.html",
    "chars": 507,
    "preview": "{% load check_filters %}\n\n{% if check.message %}\n    <h4 class=\"list-group-item-heading\">\n        <span class=\"check-ite"
  },
  {
    "path": "accounting/templates/accounting/_generics/delete_entity.html",
    "chars": 1214,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load bootstrap3 introspection_filters %}\n\n{% block sidebar %}{% endblock %}\n\n{"
  },
  {
    "path": "accounting/templates/accounting/_generics/form.html",
    "chars": 943,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load bootstrap3 introspection_filters %}\n\n{% block head %}\n    {{ block.super "
  },
  {
    "path": "accounting/templates/accounting/_partials/form_fields.html",
    "chars": 528,
    "preview": "{% load bootstrap3 introspection_filters %}\n\n{% if return_url %}\n<input type=\"hidden\" name=\"_return_url\" value=\"{{ retur"
  },
  {
    "path": "accounting/templates/accounting/base.html",
    "chars": 2283,
    "preview": "{% load static %}<!DOCTYPE html>\n<!--[if lt IE 7]>      <html class=\"no-js lt-ie9 lt-ie8 lt-ie7\"> <![endif]-->\n<!--[if I"
  },
  {
    "path": "accounting/templates/accounting/books/_generics/sale_content.html",
    "chars": 2700,
    "preview": "{% load currency_filters format_filters %}\n\n<div class=\"row\">\n    <div class=\"col-xs-5\">\n        <div class=\"panel panel"
  },
  {
    "path": "accounting/templates/accounting/books/_generics/sale_detail.html",
    "chars": 3115,
    "preview": "{% load currency_filters bootstrap3 form_filters check_filters check_tags %}\n\n<div class=\"row placeholders\">\n    <div cl"
  },
  {
    "path": "accounting/templates/accounting/books/_generics/sale_list.html",
    "chars": 1133,
    "preview": "{% load currency_filters status_filters %}\n\n<table class=\"table table-striped\">\n    <thead>\n        <tr class=\"row\">\n   "
  },
  {
    "path": "accounting/templates/accounting/books/_partials/expense_claim_list.html",
    "chars": 1028,
    "preview": "{% load currency_filters status_filters %}\n\n<table class=\"table table-striped\">\n    <thead>\n        <tr class=\"row\">\n   "
  },
  {
    "path": "accounting/templates/accounting/books/_partials/payment_list.html",
    "chars": 1045,
    "preview": "{% load currency_filters %}\n\n<table class=\"table table-striped\">\n    <thead>\n        <tr class=\"row\">\n            <th cl"
  },
  {
    "path": "accounting/templates/accounting/books/_partials/tax_rate_list.html",
    "chars": 994,
    "preview": "{% load currency_filters format_filters %}\n\n<div class=\"table-responsive\">\n    <table class=\"table table-striped\">\n     "
  },
  {
    "path": "accounting/templates/accounting/books/bill_create_or_update.html",
    "chars": 2491,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static bootstrap3 introspection_filters %}\n\n{% block head %}\n    {{ block"
  },
  {
    "path": "accounting/templates/accounting/books/bill_detail.html",
    "chars": 934,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static %}\n\n{% block head %}\n    {{ block.super }}\n\n    {# NB: because the"
  },
  {
    "path": "accounting/templates/accounting/books/bill_list.html",
    "chars": 387,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Bill"
  },
  {
    "path": "accounting/templates/accounting/books/dashboard.html",
    "chars": 2809,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">\n   "
  },
  {
    "path": "accounting/templates/accounting/books/estimate_create_or_update.html",
    "chars": 2491,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static bootstrap3 introspection_filters %}\n\n{% block head %}\n    {{ block"
  },
  {
    "path": "accounting/templates/accounting/books/estimate_detail.html",
    "chars": 917,
    "preview": "{% extends \"accounting/layout.html\" %}\n\n{% block head %}\n    {{ block.super }}\n\n    {# NB: because the payment form is a"
  },
  {
    "path": "accounting/templates/accounting/books/estimate_list.html",
    "chars": 403,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Esti"
  },
  {
    "path": "accounting/templates/accounting/books/expense_claim_create_or_update.html",
    "chars": 2491,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static bootstrap3 introspection_filters %}\n\n{% block head %}\n    {{ block"
  },
  {
    "path": "accounting/templates/accounting/books/expense_claim_detail.html",
    "chars": 1006,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static %}\n\n{% block head %}\n    {{ block.super }}\n\n    {# NB: because the"
  },
  {
    "path": "accounting/templates/accounting/books/expense_claim_list.html",
    "chars": 433,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Expe"
  },
  {
    "path": "accounting/templates/accounting/books/invoice_create_or_update.html",
    "chars": 2514,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static bootstrap3 introspection_filters %}\n\n{% block head %}\n    {{ block"
  },
  {
    "path": "accounting/templates/accounting/books/invoice_detail.html",
    "chars": 958,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static %}\n\n{% block head %}\n    {{ block.super }}\n\n    {# NB: because the"
  },
  {
    "path": "accounting/templates/accounting/books/invoice_list.html",
    "chars": 400,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Invo"
  },
  {
    "path": "accounting/templates/accounting/books/organization_create_or_update.html",
    "chars": 36,
    "preview": "{% extends \"_generics/form.html\" %}\n"
  },
  {
    "path": "accounting/templates/accounting/books/organization_detail.html",
    "chars": 2064,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">\n   "
  },
  {
    "path": "accounting/templates/accounting/books/organization_list.html",
    "chars": 1305,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Orga"
  },
  {
    "path": "accounting/templates/accounting/books/organization_selector.html",
    "chars": 2448,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block container %}\n<div class=\"container-fluid\">\n"
  },
  {
    "path": "accounting/templates/accounting/books/payment_create_or_update.html",
    "chars": 898,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load static bootstrap3 introspection_filters %}\n\n{% block head %}\n    {{ block"
  },
  {
    "path": "accounting/templates/accounting/books/tax_rate_create_or_update.html",
    "chars": 35,
    "preview": "{% extends \"_generics/form.html\" %}"
  },
  {
    "path": "accounting/templates/accounting/books/tax_rate_list.html",
    "chars": 397,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Tax "
  },
  {
    "path": "accounting/templates/accounting/connect/getting_started.html",
    "chars": 1720,
    "preview": "{% extends \"accounting/layout.html\" %}\n\n{% block content %}\n<div class=\"container-fluid\">\n    <div class=\"row\">\n        "
  },
  {
    "path": "accounting/templates/accounting/layout.html",
    "chars": 4792,
    "preview": "{% extends \"accounting/base.html\" %}\n{% load nav bootstrap3 %}\n\n{% block maincontent %}\n    {% block nav %}\n    <div cla"
  },
  {
    "path": "accounting/templates/accounting/people/client_create_or_update.html",
    "chars": 36,
    "preview": "{% extends \"_generics/form.html\" %}\n"
  },
  {
    "path": "accounting/templates/accounting/people/client_detail.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/templates/accounting/people/client_list.html",
    "chars": 906,
    "preview": "{% extends \"accounting/layout.html\" %}\n\n{% block content %}\n    <h1 class=\"page-header\">Clients <small>which belong to y"
  },
  {
    "path": "accounting/templates/accounting/people/employee_create_or_update.html",
    "chars": 36,
    "preview": "{% extends \"_generics/form.html\" %}\n"
  },
  {
    "path": "accounting/templates/accounting/people/employee_detail.html",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "accounting/templates/accounting/people/employee_list.html",
    "chars": 1081,
    "preview": "{% extends \"accounting/layout.html\" %}\n\n{% block content %}\n    <h1 class=\"page-header\">Employees <small>which belong to"
  },
  {
    "path": "accounting/templates/accounting/reports/_partials/period_form.html",
    "chars": 500,
    "preview": "{% load bootstrap3 %}\n<form action=\".\" method=\"get\">\n    {% bootstrap_form_errors form %}\n\n    <div class=\"row\">\n       "
  },
  {
    "path": "accounting/templates/accounting/reports/financial_settings_update.html",
    "chars": 36,
    "preview": "{% extends \"_generics/form.html\" %}\n"
  },
  {
    "path": "accounting/templates/accounting/reports/invoice_details_report.html",
    "chars": 4044,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters bootstrap3 %}\n\n{% block content %}\n    <h1 class=\"page-h"
  },
  {
    "path": "accounting/templates/accounting/reports/pay_run_report.html",
    "chars": 1629,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters format_filters bootstrap3 %}\n\n{% block content %}\n    <h"
  },
  {
    "path": "accounting/templates/accounting/reports/payrun_settings_update.html",
    "chars": 36,
    "preview": "{% extends \"_generics/form.html\" %}\n"
  },
  {
    "path": "accounting/templates/accounting/reports/profit_and_loss_report.html",
    "chars": 2043,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Prof"
  },
  {
    "path": "accounting/templates/accounting/reports/report_list.html",
    "chars": 1500,
    "preview": "{% extends \"accounting/layout.html\" %}\n\n{% block content %}\n    <h1 class=\"page-header\">Reports <small>which belong to y"
  },
  {
    "path": "accounting/templates/accounting/reports/settings_list.html",
    "chars": 699,
    "preview": "{% extends \"accounting/layout.html\" %}\n\n{% block content %}\n    <h1 class=\"page-header\">Settings <small>for powerful rep"
  },
  {
    "path": "accounting/templates/accounting/reports/tax_report.html",
    "chars": 1779,
    "preview": "{% extends \"accounting/layout.html\" %}\n{% load currency_filters %}\n\n{% block content %}\n    <h1 class=\"page-header\">Tax "
  },
  {
    "path": "accounting/urls.py",
    "chars": 715,
    "preview": "from django.conf import settings\nfrom django.conf.urls import patterns, include, url\nfrom django.conf.urls.static import"
  },
  {
    "path": "accounting/wsgi.py",
    "chars": 448,
    "preview": "\"\"\"\nWSGI config for accounting project.\n\nIt exposes the WSGI callable as a module-level variable named ``application``.\n"
  },
  {
    "path": "lint.sh",
    "chars": 913,
    "preview": "#/usr/bin/env bash\n#\n# Run static analysis of the codebase\n#\n# This is run on Travis to ensure that pull requests confor"
  },
  {
    "path": "requirements.txt",
    "chars": 258,
    "preview": "## Testing\ncoverage==4.3.4\ndjango-dynamic-fixture>=1.7,<1.8\ndjango-nose>=1.4,<1.5\n\nmock>=1.0.1,<1.1.0\nspec>=0.11.1,<0.12"
  },
  {
    "path": "runtests.py",
    "chars": 2648,
    "preview": "#!/usr/bin/env python\n\"\"\"\nCustom test runner\n\nIf args or options, we run the testsuite as quickly as possible.\n\nIf args "
  },
  {
    "path": "setup.cfg",
    "chars": 119,
    "preview": "[wheel]\n# accounting is not Python2 ready for now, so we cannot build universal wheels.\nuniversal = 0\npython-tag = py32"
  },
  {
    "path": "setup.py",
    "chars": 2165,
    "preview": "#!/usr/bin/env python\n\"\"\"\nInstallation script:\n\nTo release a new version to PyPi:\n- Ensure the version is correctly set "
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/_site/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/_site/model_tests_app/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/_site/model_tests_app/models.py",
    "chars": 381,
    "preview": "from django.test import TestCase\nfrom django.db import models\nfrom django.db.models.query import QuerySet\nfrom django.co"
  },
  {
    "path": "tests/config.py",
    "chars": 345,
    "preview": "import os\nimport sys\n\n\ndef configure():\n    os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'\n    from django.conf"
  },
  {
    "path": "tests/functional/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/functional/libs/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/functional/libs/context_processors_tests.py",
    "chars": 461,
    "preview": "from django.test import TestCase\nfrom django.test.client import RequestFactory\n\nfrom accounting.apps.context_processors "
  },
  {
    "path": "tests/functional/libs/template_tags_tests.py",
    "chars": 6735,
    "preview": "from decimal import Decimal as D\n\nfrom django.test import TestCase\nfrom django.test.client import RequestFactory\nfrom dj"
  },
  {
    "path": "tests/functional/libs/utils_tests.py",
    "chars": 1945,
    "preview": "# encoding: utf-8\n\nimport datetime\n\nfrom django.test import TransactionTestCase, TestCase\nfrom django.test.utils import "
  },
  {
    "path": "tests/integration/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/settings.py",
    "chars": 2100,
    "preview": "import os\nimport logging\n\nimport accounting\nfrom accounting.defaults import *\n\n\nBASE_DIR = os.path.dirname(os.path.dirna"
  },
  {
    "path": "tests/unit/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/unit/books/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/unit/books/bill_tests.py",
    "chars": 737,
    "preview": "from decimal import Decimal as D\n\nfrom django.test import TestCase\n\nfrom django_dynamic_fixture import G\nimport mock\n\nfr"
  },
  {
    "path": "tests/unit/books/invoice_tests.py",
    "chars": 758,
    "preview": "from decimal import Decimal as D\n\nfrom django.test import TestCase\n\nfrom django_dynamic_fixture import G\nimport mock\n\nfr"
  },
  {
    "path": "tests/unit/clients/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/unit/clients/organization_tests.py",
    "chars": 4990,
    "preview": "from decimal import Decimal as D\n\nfrom django.test import TestCase\n\nfrom django_dynamic_fixture import G\n\nfrom accountin"
  },
  {
    "path": "tests/unit/clients/user_tests.py",
    "chars": 672,
    "preview": "import datetime\n\nfrom django.contrib.auth import get_user_model\nfrom django.test import TestCase\n\nfrom django_dynamic_fi"
  },
  {
    "path": "tests/unit/libs/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/unit/libs/prices_tests.py",
    "chars": 1891,
    "preview": "from decimal import Decimal as D\nfrom itertools import product\nfrom django.test import TestCase\n\nfrom accounting.libs.pr"
  }
]

About this extraction

This page contains the full source code of the dulacp/django-accounting GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 169 files (257.8 KB), approximately 62.2k tokens, and a symbol index with 570 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!