django-ledger 0.6.3__py3-none-any.whl → 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-ledger might be problematic. Click here for more details.
- django_ledger/__init__.py +1 -4
- django_ledger/admin/coa.py +1 -2
- django_ledger/contrib/django_ledger_graphene/accounts/schema.py +1 -1
- django_ledger/forms/account.py +60 -44
- django_ledger/forms/bank_account.py +3 -2
- django_ledger/forms/bill.py +24 -36
- django_ledger/forms/customer.py +1 -1
- django_ledger/forms/data_import.py +3 -3
- django_ledger/forms/estimate.py +1 -1
- django_ledger/forms/invoice.py +5 -7
- django_ledger/forms/item.py +24 -15
- django_ledger/forms/transactions.py +3 -3
- django_ledger/io/io_core.py +4 -2
- django_ledger/io/io_middleware.py +5 -0
- django_ledger/io/roles.py +6 -0
- django_ledger/migrations/0017_alter_accountmodel_unique_together_and_more.py +31 -0
- django_ledger/models/accounts.py +629 -342
- django_ledger/models/bank_account.py +0 -4
- django_ledger/models/bill.py +0 -3
- django_ledger/models/closing_entry.py +0 -3
- django_ledger/models/coa.py +59 -48
- django_ledger/models/coa_default.py +9 -8
- django_ledger/models/customer.py +0 -4
- django_ledger/models/data_import.py +0 -3
- django_ledger/models/entity.py +80 -40
- django_ledger/models/estimate.py +0 -9
- django_ledger/models/invoice.py +0 -3
- django_ledger/models/items.py +4 -6
- django_ledger/models/journal_entry.py +2 -5
- django_ledger/models/ledger.py +0 -3
- django_ledger/models/mixins.py +0 -3
- django_ledger/models/purchase_order.py +0 -4
- django_ledger/models/signals.py +0 -3
- django_ledger/models/transactions.py +2 -5
- django_ledger/models/unit.py +0 -3
- django_ledger/models/utils.py +0 -3
- django_ledger/models/vendor.py +0 -3
- django_ledger/report/cash_flow_statement.py +1 -1
- django_ledger/static/.DS_Store +0 -0
- django_ledger/static/django_ledger/.DS_Store +0 -0
- django_ledger/static/django_ledger/logo_2/.DS_Store +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-logo@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@3x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@0.5x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@2x.png +0 -0
- django_ledger/static/django_ledger/logo_2/djl-txt-horiz@3x.png +0 -0
- django_ledger/templates/django_ledger/account/account_create.html +2 -2
- django_ledger/templates/django_ledger/account/account_update.html +1 -1
- django_ledger/templates/django_ledger/account/tags/account_txs_table.html +1 -0
- django_ledger/templates/django_ledger/account/tags/accounts_table.html +27 -18
- django_ledger/templates/django_ledger/bills/bill_detail.html +3 -3
- django_ledger/templates/django_ledger/expense/tags/expense_item_table.html +7 -0
- django_ledger/templates/django_ledger/invoice/invoice_detail.html +3 -3
- django_ledger/templatetags/django_ledger.py +7 -1
- django_ledger/tests/base.py +23 -7
- django_ledger/tests/test_accounts.py +145 -9
- django_ledger/urls/account.py +17 -24
- django_ledger/utils.py +8 -0
- django_ledger/views/__init__.py +1 -1
- django_ledger/views/account.py +80 -118
- django_ledger/views/auth.py +1 -1
- django_ledger/views/bank_account.py +9 -11
- django_ledger/views/bill.py +91 -80
- django_ledger/views/closing_entry.py +8 -0
- django_ledger/views/coa.py +2 -1
- django_ledger/views/customer.py +1 -1
- django_ledger/views/data_import.py +1 -1
- django_ledger/views/entity.py +1 -1
- django_ledger/views/estimate.py +13 -8
- django_ledger/views/feedback.py +1 -1
- django_ledger/views/financial_statement.py +1 -1
- django_ledger/views/home.py +1 -1
- django_ledger/views/inventory.py +9 -0
- django_ledger/views/invoice.py +5 -2
- django_ledger/views/item.py +58 -68
- django_ledger/views/journal_entry.py +1 -1
- django_ledger/views/ledger.py +3 -1
- django_ledger/views/mixins.py +9 -8
- django_ledger/views/purchase_order.py +1 -1
- django_ledger/views/transactions.py +1 -1
- django_ledger/views/unit.py +9 -0
- django_ledger/views/vendor.py +1 -1
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/AUTHORS.md +8 -2
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/METADATA +34 -43
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/RECORD +115 -79
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/WHEEL +1 -1
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/top_level.txt +0 -1
- {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/LICENSE +0 -0
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
* Pranav P Tulshyan <ptulshyan77@gmail.com>
|
|
8
|
-
|
|
9
5
|
A Bank Account refers to the financial institution which holds financial assets for the EntityModel.
|
|
10
6
|
A bank account usually holds cash, which is a Current Asset. Transactions may be imported using the open financial
|
|
11
7
|
format specification OFX into a staging area for final disposition into the EntityModel ledger.
|
django_ledger/models/bill.py
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
|
|
8
5
|
This module implements the BillModel, which represents an Invoice received from a Supplier/Vendor, on which
|
|
9
6
|
the Vendor states the amount owed by the recipient for the purposes of supplying goods and/or services.
|
|
10
7
|
In addition to tracking the bill amount, it tracks the paid and due amount.
|
django_ledger/models/coa.py
CHANGED
|
@@ -2,12 +2,8 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
* Pranav P Tulshyan <ptulshyan77@gmail.com>
|
|
8
|
-
|
|
9
5
|
Chart Of Accounts
|
|
10
|
-
|
|
6
|
+
-----------------
|
|
11
7
|
|
|
12
8
|
A Chart of Accounts (CoA) is a crucial collection of logically grouped accounts within a ChartOfAccountModel,
|
|
13
9
|
forming the backbone of financial statements. The CoA includes various account roles such as cash, accounts receivable,
|
|
@@ -29,7 +25,7 @@ from django.apps import apps
|
|
|
29
25
|
from django.contrib.auth import get_user_model
|
|
30
26
|
from django.core.exceptions import ValidationError
|
|
31
27
|
from django.db import models
|
|
32
|
-
from django.db.models import Q
|
|
28
|
+
from django.db.models import Q, F
|
|
33
29
|
from django.urls import reverse
|
|
34
30
|
from django.utils.translation import gettext_lazy as _
|
|
35
31
|
|
|
@@ -66,6 +62,12 @@ class ChartOfAccountModelManager(models.Manager):
|
|
|
66
62
|
to the ChartOfAccountModel.
|
|
67
63
|
"""
|
|
68
64
|
|
|
65
|
+
def get_queryset(self):
|
|
66
|
+
qs = super().get_queryset()
|
|
67
|
+
return qs.annotate(
|
|
68
|
+
_entity_slug=F('entity__slug')
|
|
69
|
+
)
|
|
70
|
+
|
|
69
71
|
def for_user(self, user_model) -> ChartOfAccountModelQuerySet:
|
|
70
72
|
"""
|
|
71
73
|
Fetches a QuerySet of ChartOfAccountModel that the UserModel as access to. May include ChartOfAccountModel from
|
|
@@ -157,6 +159,14 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
157
159
|
return f'{self.name} ({self.slug})'
|
|
158
160
|
return self.slug
|
|
159
161
|
|
|
162
|
+
@property
|
|
163
|
+
def entity_slug(self) -> str:
|
|
164
|
+
try:
|
|
165
|
+
# from QS annotation...
|
|
166
|
+
return getattr(self, '_entity_slug')
|
|
167
|
+
except AttributeError:
|
|
168
|
+
return self.entity.slug
|
|
169
|
+
|
|
160
170
|
def get_coa_root_accounts_qs(self) -> AccountModelQuerySet:
|
|
161
171
|
"""
|
|
162
172
|
Retrieves the root accounts in the chart of accounts.
|
|
@@ -563,23 +573,6 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
563
573
|
]
|
|
564
574
|
)
|
|
565
575
|
|
|
566
|
-
def mark_as_default_url(self) -> str:
|
|
567
|
-
"""
|
|
568
|
-
Returns the URL to mark the current Chart of Accounts instances as Default for the EntityModel.
|
|
569
|
-
|
|
570
|
-
Returns
|
|
571
|
-
-------
|
|
572
|
-
str
|
|
573
|
-
The URL as a String.
|
|
574
|
-
"""
|
|
575
|
-
return reverse(
|
|
576
|
-
viewname='django_ledger:coa-action-mark-as-default',
|
|
577
|
-
kwargs={
|
|
578
|
-
'entity_slug': self.entity.slug,
|
|
579
|
-
'coa_slug': self.slug
|
|
580
|
-
}
|
|
581
|
-
)
|
|
582
|
-
|
|
583
576
|
def can_activate(self) -> bool:
|
|
584
577
|
"""
|
|
585
578
|
Check if the ChartOffAccountModel instance can be activated.
|
|
@@ -630,23 +623,6 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
630
623
|
'updated'
|
|
631
624
|
])
|
|
632
625
|
|
|
633
|
-
def mark_as_active_url(self) -> str:
|
|
634
|
-
"""
|
|
635
|
-
Returns the URL to mark the current Chart of Accounts instances as active.
|
|
636
|
-
|
|
637
|
-
Returns
|
|
638
|
-
-------
|
|
639
|
-
str
|
|
640
|
-
The URL as a String.
|
|
641
|
-
"""
|
|
642
|
-
return reverse(
|
|
643
|
-
viewname='django_ledger:coa-action-mark-as-active',
|
|
644
|
-
kwargs={
|
|
645
|
-
'entity_slug': self.entity.slug,
|
|
646
|
-
'coa_slug': self.slug
|
|
647
|
-
}
|
|
648
|
-
)
|
|
649
|
-
|
|
650
626
|
def mark_as_inactive(self, commit: bool = False, raise_exception: bool = False, **kwargs):
|
|
651
627
|
"""
|
|
652
628
|
Marks the current Chart of Accounts as Active.
|
|
@@ -674,6 +650,41 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
674
650
|
'updated'
|
|
675
651
|
])
|
|
676
652
|
|
|
653
|
+
# URLS....
|
|
654
|
+
def mark_as_default_url(self) -> str:
|
|
655
|
+
"""
|
|
656
|
+
Returns the URL to mark the current Chart of Accounts instances as Default for the EntityModel.
|
|
657
|
+
|
|
658
|
+
Returns
|
|
659
|
+
-------
|
|
660
|
+
str
|
|
661
|
+
The URL as a String.
|
|
662
|
+
"""
|
|
663
|
+
return reverse(
|
|
664
|
+
viewname='django_ledger:coa-action-mark-as-default',
|
|
665
|
+
kwargs={
|
|
666
|
+
'entity_slug': self.entity_slug,
|
|
667
|
+
'coa_slug': self.slug
|
|
668
|
+
}
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
def mark_as_active_url(self) -> str:
|
|
672
|
+
"""
|
|
673
|
+
Returns the URL to mark the current Chart of Accounts instances as active.
|
|
674
|
+
|
|
675
|
+
Returns
|
|
676
|
+
-------
|
|
677
|
+
str
|
|
678
|
+
The URL as a String.
|
|
679
|
+
"""
|
|
680
|
+
return reverse(
|
|
681
|
+
viewname='django_ledger:coa-action-mark-as-active',
|
|
682
|
+
kwargs={
|
|
683
|
+
'entity_slug': self.entity_slug,
|
|
684
|
+
'coa_slug': self.slug
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
|
|
677
688
|
def mark_as_inactive_url(self) -> str:
|
|
678
689
|
"""
|
|
679
690
|
Returns the URL to mark the current Chart of Accounts instances as inactive.
|
|
@@ -686,7 +697,7 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
686
697
|
return reverse(
|
|
687
698
|
viewname='django_ledger:coa-action-mark-as-inactive',
|
|
688
699
|
kwargs={
|
|
689
|
-
'entity_slug': self.
|
|
700
|
+
'entity_slug': self.entity_slug,
|
|
690
701
|
'coa_slug': self.slug
|
|
691
702
|
}
|
|
692
703
|
)
|
|
@@ -695,7 +706,7 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
695
706
|
return reverse(
|
|
696
707
|
viewname='django_ledger:coa-list',
|
|
697
708
|
kwargs={
|
|
698
|
-
'entity_slug': self.
|
|
709
|
+
'entity_slug': self.entity_slug
|
|
699
710
|
}
|
|
700
711
|
)
|
|
701
712
|
|
|
@@ -703,26 +714,26 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
703
714
|
return reverse(
|
|
704
715
|
viewname='django_ledger:coa-detail',
|
|
705
716
|
kwargs={
|
|
706
|
-
'
|
|
707
|
-
'
|
|
717
|
+
'entity_slug': self.entity_slug,
|
|
718
|
+
'coa_slug': self.slug
|
|
708
719
|
}
|
|
709
720
|
)
|
|
710
721
|
|
|
711
722
|
def get_account_list_url(self):
|
|
712
723
|
return reverse(
|
|
713
|
-
viewname='django_ledger:account-list
|
|
724
|
+
viewname='django_ledger:account-list',
|
|
714
725
|
kwargs={
|
|
715
|
-
'entity_slug': self.
|
|
726
|
+
'entity_slug': self.entity_slug,
|
|
716
727
|
'coa_slug': self.slug
|
|
717
728
|
}
|
|
718
729
|
)
|
|
719
730
|
|
|
720
731
|
def get_create_coa_account_url(self):
|
|
721
732
|
return reverse(
|
|
722
|
-
viewname='django_ledger:account-create
|
|
733
|
+
viewname='django_ledger:account-create',
|
|
723
734
|
kwargs={
|
|
724
735
|
'coa_slug': self.slug,
|
|
725
|
-
'entity_slug': self.
|
|
736
|
+
'entity_slug': self.entity_slug
|
|
726
737
|
}
|
|
727
738
|
)
|
|
728
739
|
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
* Pranav P Tulshyan <ptulshyan77@gmail.com>
|
|
8
|
-
|
|
9
5
|
This is the base Chart of Accounts that has all the possible accounts that are useful for the preparation of the
|
|
10
6
|
Financial Statements. A user may choose to use the default CoA at the creation of each EntityModel but it is not
|
|
11
7
|
required. The default CoA is intended to provide a QuickStart solution for most use cases.
|
|
@@ -14,8 +10,10 @@ The Chart of Accounts is broadly bifurcated into 5 different Sections:
|
|
|
14
10
|
1. Assets:
|
|
15
11
|
2. Liabilities
|
|
16
12
|
3. Shareholder's Equity
|
|
17
|
-
4.
|
|
18
|
-
5.
|
|
13
|
+
4. Income
|
|
14
|
+
5. COGS
|
|
15
|
+
6. Expenses
|
|
16
|
+
|
|
19
17
|
|
|
20
18
|
The Django Ledger Default Chart of Accounts must include the following fields:
|
|
21
19
|
* Code - String
|
|
@@ -75,7 +73,8 @@ Default Chart of Accounts Table
|
|
|
75
73
|
6040 ex_regular debit Bad Debt root_expenses
|
|
76
74
|
6050 ex_regular debit Bank Charges root_expenses
|
|
77
75
|
6060 ex_regular debit Commission Expense root_expenses
|
|
78
|
-
6080 ex_regular debit Employee Benefits
|
|
76
|
+
6080 ex_regular debit Employee Benefits root_expenses
|
|
77
|
+
6081 ex_regular debit Employee Wages root_expenses
|
|
79
78
|
6090 ex_regular debit Freight root_expenses
|
|
80
79
|
6110 ex_regular debit Gifts root_expenses
|
|
81
80
|
6120 ex_regular debit Insurance root_expenses
|
|
@@ -83,7 +82,7 @@ Default Chart of Accounts Table
|
|
|
83
82
|
6150 ex_regular debit License Expense root_expenses
|
|
84
83
|
6170 ex_regular debit Maintenance Expense root_expenses
|
|
85
84
|
6180 ex_regular debit Meals & Entertainment root_expenses
|
|
86
|
-
6190 ex_regular debit Office Expense
|
|
85
|
+
6190 ex_regular debit Office Expense root_expenses
|
|
87
86
|
6220 ex_regular debit Printing root_expenses
|
|
88
87
|
6230 ex_regular debit Postage root_expenses
|
|
89
88
|
6240 ex_regular debit Rent root_expenses
|
|
@@ -262,6 +261,8 @@ DEFAULT_CHART_OF_ACCOUNTS = [
|
|
|
262
261
|
'parent': None},
|
|
263
262
|
{'code': '6080', 'role': roles.EXPENSE_OPERATIONAL, 'balance_type': 'debit', 'name': 'Employee Benefits',
|
|
264
263
|
'parent': None},
|
|
264
|
+
{'code': '6081', 'role': roles.EXPENSE_OPERATIONAL, 'balance_type': 'debit', 'name': 'Employee Wages',
|
|
265
|
+
'parent': None},
|
|
265
266
|
{'code': '6090', 'role': roles.EXPENSE_OPERATIONAL, 'balance_type': 'debit', 'name': 'Freight', 'parent': None},
|
|
266
267
|
{'code': '6110', 'role': roles.EXPENSE_OPERATIONAL, 'balance_type': 'debit', 'name': 'Gifts', 'parent': None},
|
|
267
268
|
{'code': '6120', 'role': roles.EXPENSE_OPERATIONAL, 'balance_type': 'debit', 'name': 'Insurance', 'parent': None},
|
django_ledger/models/customer.py
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
* Pranav P Tulshyan <ptulshyan77@gmail.com>
|
|
8
|
-
|
|
9
5
|
A Customer refers to the person or entity that buys product and services. When issuing Invoices, a Customer must be
|
|
10
6
|
created before it can be assigned to the InvoiceModel. Only customers who are active can be assigned to new Invoices.
|
|
11
7
|
"""
|
django_ledger/models/entity.py
CHANGED
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
* Pranav P Tulshyan ptulshyan77@gmail.com<>
|
|
8
|
-
|
|
9
5
|
The EntityModel represents the Company, Corporation, Legal Entity, Enterprise or Person that engage and operate as a
|
|
10
6
|
business. EntityModels can be created as part of a parent/child model structure to accommodate complex corporate
|
|
11
7
|
structures where certain entities may be owned by other entities and may also generate consolidated financial statements.
|
|
8
|
+
|
|
12
9
|
Another use case of parent/child model structures is the coordination and authorization of inter-company transactions
|
|
13
|
-
across multiple related entities. The EntityModel encapsulates all LedgerModel, JournalEntryModel and TransactionModel
|
|
14
|
-
Django Ledger in order to track and produce all financials.
|
|
10
|
+
across multiple related entities. The EntityModel encapsulates all LedgerModel, JournalEntryModel and TransactionModel
|
|
11
|
+
which is the core structure of Django Ledger in order to track and produce all financials.
|
|
15
12
|
|
|
16
13
|
The EntityModel must be assigned an Administrator at creation, and may have optional Managers that will have the ability
|
|
17
14
|
to operate on such EntityModel.
|
|
@@ -36,7 +33,7 @@ from django.core.cache import caches
|
|
|
36
33
|
from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
|
37
34
|
from django.core.validators import MinValueValidator
|
|
38
35
|
from django.db import models
|
|
39
|
-
from django.db.models import Q
|
|
36
|
+
from django.db.models import Q, F
|
|
40
37
|
from django.db.models.signals import pre_save
|
|
41
38
|
from django.urls import reverse
|
|
42
39
|
from django.utils.text import slugify
|
|
@@ -113,8 +110,14 @@ class EntityModelManager(MP_NodeManager):
|
|
|
113
110
|
|
|
114
111
|
def get_queryset(self):
|
|
115
112
|
"""Sets the custom queryset as the default."""
|
|
116
|
-
qs = EntityModelQuerySet(
|
|
117
|
-
|
|
113
|
+
qs = EntityModelQuerySet(
|
|
114
|
+
self.model,
|
|
115
|
+
using=self._db).order_by('path')
|
|
116
|
+
return qs.order_by('path').select_related(
|
|
117
|
+
'admin',
|
|
118
|
+
'default_coa').annotate(
|
|
119
|
+
_default_coa_slug=F('default_coa__slug'),
|
|
120
|
+
)
|
|
118
121
|
|
|
119
122
|
def for_user(self, user_model, authorized_superuser: bool = False):
|
|
120
123
|
"""
|
|
@@ -421,6 +424,9 @@ class EntityModelFiscalPeriodMixIn:
|
|
|
421
424
|
|
|
422
425
|
|
|
423
426
|
class EntityModelClosingEntryMixIn:
|
|
427
|
+
"""
|
|
428
|
+
Closing Entries provide
|
|
429
|
+
"""
|
|
424
430
|
|
|
425
431
|
def validate_closing_entry_model(self, closing_entry_model, closing_date: Optional[date] = None):
|
|
426
432
|
if isinstance(self, EntityModel):
|
|
@@ -519,7 +525,6 @@ class EntityModelClosingEntryMixIn:
|
|
|
519
525
|
return self.get_closing_entry_queryset_for_date(closing_date=closing_date)
|
|
520
526
|
|
|
521
527
|
# ----> Create Closing Entries <----
|
|
522
|
-
|
|
523
528
|
def create_closing_entry_for_date(self,
|
|
524
529
|
closing_date: date,
|
|
525
530
|
closing_entry_model=None,
|
|
@@ -578,7 +583,6 @@ class EntityModelClosingEntryMixIn:
|
|
|
578
583
|
return f'closing_entry_{end_dt_str}_{self.uuid}'
|
|
579
584
|
|
|
580
585
|
# ----> Closing Entry Caching Month < -----
|
|
581
|
-
|
|
582
586
|
def get_closing_entry_cache_for_date(self,
|
|
583
587
|
closing_date: date,
|
|
584
588
|
cache_name: str = 'default',
|
|
@@ -801,6 +805,13 @@ class EntityModelAbstract(MP_Node,
|
|
|
801
805
|
super().__init__(*args, **kwargs)
|
|
802
806
|
self._CLOSING_ENTRY_DATES: Optional[List[date]] = None
|
|
803
807
|
|
|
808
|
+
@property
|
|
809
|
+
def default_coa_slug(self):
|
|
810
|
+
try:
|
|
811
|
+
return getattr(self, '_default_coa_slug')
|
|
812
|
+
except AttributeError:
|
|
813
|
+
return self.default_coa.slug
|
|
814
|
+
|
|
804
815
|
# ## Logging ###
|
|
805
816
|
def get_logger_name(self):
|
|
806
817
|
return f'EntityModel {self.uuid}'
|
|
@@ -1272,7 +1283,10 @@ class EntityModelAbstract(MP_Node,
|
|
|
1272
1283
|
def get_coa_accounts(self,
|
|
1273
1284
|
coa_model: Optional[Union[ChartOfAccountModel, UUID, str]] = None,
|
|
1274
1285
|
active: bool = True,
|
|
1275
|
-
|
|
1286
|
+
locked: bool = False,
|
|
1287
|
+
order_by: Optional[Tuple] = ('code',),
|
|
1288
|
+
return_coa_model: bool = False,
|
|
1289
|
+
) -> Union[AccountModelQuerySet, Tuple[ChartOfAccountModel, AccountModelQuerySet]]:
|
|
1276
1290
|
"""
|
|
1277
1291
|
Fetches the AccountModelQuerySet for a specific ChartOfAccountModel.
|
|
1278
1292
|
|
|
@@ -1282,6 +1296,8 @@ class EntityModelAbstract(MP_Node,
|
|
|
1282
1296
|
The ChartOfAccountsModel UUID, model instance or slug to pull accounts from. If None, will use default CoA.
|
|
1283
1297
|
active: bool
|
|
1284
1298
|
Selects only active accounts.
|
|
1299
|
+
locked: bool
|
|
1300
|
+
Selects only locked accounts.
|
|
1285
1301
|
order_by: list of strings.
|
|
1286
1302
|
Optional list of fields passed to the order_by QuerySet method.
|
|
1287
1303
|
|
|
@@ -1292,19 +1308,31 @@ class EntityModelAbstract(MP_Node,
|
|
|
1292
1308
|
"""
|
|
1293
1309
|
|
|
1294
1310
|
if not coa_model:
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1311
|
+
coa_model = self.default_coa
|
|
1312
|
+
elif isinstance(coa_model, UUID):
|
|
1313
|
+
coa_model = self.chartofaccountmodel_set.select_related('entity').get(uuid__exact=coa_model)
|
|
1314
|
+
elif isinstance(coa_model, str):
|
|
1315
|
+
coa_model = self.chartofaccountmodel_set.select_related('entity').get(slug__exact=coa_model)
|
|
1316
|
+
elif isinstance(coa_model, ChartOfAccountModel):
|
|
1298
1317
|
self.validate_chart_of_accounts_for_entity(coa_model=coa_model)
|
|
1299
|
-
|
|
1300
|
-
|
|
1318
|
+
else:
|
|
1319
|
+
raise EntityModelValidationError(
|
|
1320
|
+
f'CoA Model {coa_model} must be an instance of ChartOfAccountModel, UUID, str or None.'
|
|
1321
|
+
)
|
|
1322
|
+
|
|
1323
|
+
account_model_qs = coa_model.accountmodel_set.select_related('coa_model', 'coa_model__entity').not_coa_root()
|
|
1301
1324
|
|
|
1302
1325
|
if active:
|
|
1303
1326
|
account_model_qs = account_model_qs.active()
|
|
1304
1327
|
|
|
1328
|
+
if locked:
|
|
1329
|
+
account_model_qs = account_model_qs.locked()
|
|
1330
|
+
|
|
1305
1331
|
if order_by:
|
|
1306
1332
|
account_model_qs = account_model_qs.order_by(*order_by)
|
|
1307
1333
|
|
|
1334
|
+
if return_coa_model:
|
|
1335
|
+
return coa_model, account_model_qs
|
|
1308
1336
|
return account_model_qs
|
|
1309
1337
|
|
|
1310
1338
|
def get_default_coa_accounts(self,
|
|
@@ -1687,11 +1715,12 @@ class EntityModelAbstract(MP_Node,
|
|
|
1687
1715
|
|
|
1688
1716
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
1689
1717
|
|
|
1690
|
-
account_model_qs = account_model_qs.with_roles(
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1718
|
+
account_model_qs = account_model_qs.with_roles(
|
|
1719
|
+
roles=[
|
|
1720
|
+
roles_module.ASSET_CA_CASH,
|
|
1721
|
+
roles_module.ASSET_CA_PREPAID,
|
|
1722
|
+
roles_module.LIABILITY_CL_ACC_PAYABLE
|
|
1723
|
+
]).is_role_default()
|
|
1695
1724
|
|
|
1696
1725
|
# evaluates the queryset...
|
|
1697
1726
|
len(account_model_qs)
|
|
@@ -1796,11 +1825,12 @@ class EntityModelAbstract(MP_Node,
|
|
|
1796
1825
|
raise EntityModelValidationError('CustomerModel must be an instance of CustomerModel, UUID or str.')
|
|
1797
1826
|
|
|
1798
1827
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
1799
|
-
account_model_qs = account_model_qs.with_roles(
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1828
|
+
account_model_qs = account_model_qs.with_roles(
|
|
1829
|
+
roles=[
|
|
1830
|
+
roles_module.ASSET_CA_CASH,
|
|
1831
|
+
roles_module.ASSET_CA_RECEIVABLES,
|
|
1832
|
+
roles_module.LIABILITY_CL_DEFERRED_REVENUE
|
|
1833
|
+
]).is_role_default()
|
|
1804
1834
|
|
|
1805
1835
|
# evaluates the queryset...
|
|
1806
1836
|
len(account_model_qs)
|
|
@@ -1991,7 +2021,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
1991
2021
|
raise EntityModelValidationError(
|
|
1992
2022
|
_(f'Invalid Account Type: choices are {BankAccountModel.VALID_ACCOUNT_TYPES}'))
|
|
1993
2023
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
1994
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2024
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2025
|
+
roles=roles_module.ASSET_CA_CASH
|
|
2026
|
+
).is_role_default()
|
|
1995
2027
|
bank_account_model = BankAccountModel(
|
|
1996
2028
|
name=name,
|
|
1997
2029
|
entity_model=self,
|
|
@@ -2147,11 +2179,12 @@ class EntityModelAbstract(MP_Node,
|
|
|
2147
2179
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2148
2180
|
|
|
2149
2181
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2150
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2182
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2183
|
+
roles=[
|
|
2184
|
+
roles_module.ASSET_CA_INVENTORY,
|
|
2185
|
+
roles_module.COGS,
|
|
2186
|
+
roles_module.INCOME_OPERATIONAL
|
|
2187
|
+
]).is_role_default()
|
|
2155
2188
|
|
|
2156
2189
|
# evaluates the queryset...
|
|
2157
2190
|
len(account_model_qs)
|
|
@@ -2222,10 +2255,11 @@ class EntityModelAbstract(MP_Node,
|
|
|
2222
2255
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2223
2256
|
|
|
2224
2257
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2225
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2258
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2259
|
+
roles=[
|
|
2260
|
+
roles_module.COGS,
|
|
2261
|
+
roles_module.INCOME_OPERATIONAL
|
|
2262
|
+
]).is_role_default()
|
|
2229
2263
|
|
|
2230
2264
|
# evaluates the queryset...
|
|
2231
2265
|
len(account_model_qs)
|
|
@@ -2301,7 +2335,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
2301
2335
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2302
2336
|
|
|
2303
2337
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2304
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2338
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2339
|
+
roles=roles_module.EXPENSE_OPERATIONAL
|
|
2340
|
+
)
|
|
2305
2341
|
if not expense_account:
|
|
2306
2342
|
expense_account = account_model_qs.is_role_default().get()
|
|
2307
2343
|
elif isinstance(expense_account, UUID):
|
|
@@ -2400,7 +2436,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
2400
2436
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2401
2437
|
|
|
2402
2438
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2403
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2439
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2440
|
+
roles=roles_module.ASSET_CA_INVENTORY
|
|
2441
|
+
)
|
|
2404
2442
|
if not inventory_account:
|
|
2405
2443
|
inventory_account = account_model_qs.is_role_default().get()
|
|
2406
2444
|
elif isinstance(inventory_account, UUID):
|
|
@@ -2620,7 +2658,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
2620
2658
|
ROLES_NEEDED.append(roles_module.EQUITY_CAPITAL)
|
|
2621
2659
|
|
|
2622
2660
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model)
|
|
2623
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2661
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2662
|
+
roles=ROLES_NEEDED
|
|
2663
|
+
).is_role_default()
|
|
2624
2664
|
|
|
2625
2665
|
if not cash_account or not capital_account:
|
|
2626
2666
|
if cash_account or capital_account:
|
django_ledger/models/estimate.py
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
|
|
8
5
|
The EstimateModel provides the means to estimate customer requests, jobs or quotes that may ultimately be considered
|
|
9
6
|
contracts, if approved. The EstimateModels will estimate revenues and costs associated with a specific scope of work
|
|
10
7
|
which is documented using ItemTransactionModels.
|
|
@@ -140,12 +137,6 @@ class EstimateModelManager(models.Manager):
|
|
|
140
137
|
user_model
|
|
141
138
|
Logged in and authenticated django UserModel instance.
|
|
142
139
|
|
|
143
|
-
Examples
|
|
144
|
-
--------
|
|
145
|
-
>>> request_user = request.user
|
|
146
|
-
>>> slug = kwargs['entity_slug'] # may come from request kwargs
|
|
147
|
-
>>> bill_model_qs = EstimateModel.objects.for_entity(user_model=request_user, entity_slug=slug)
|
|
148
|
-
|
|
149
140
|
Returns
|
|
150
141
|
-------
|
|
151
142
|
EstimateModelQuerySet
|
django_ledger/models/invoice.py
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
|
|
8
5
|
This module implements the InvoiceModel, which represents the Sales Invoice/ Sales Invoice/ Tax Invoice/ Proof of Sale
|
|
9
6
|
which the :func:`EntityModel <django_ledger.models.entity.EntityModel>` issues to its customers for the supply of
|
|
10
7
|
goods or services. The model manages all the Sales Invoices which are issued by the :func:`EntityModel
|
django_ledger/models/items.py
CHANGED
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
* Pranav P Tulshyan <ptulshyan77@gmail.com>
|
|
8
|
-
|
|
9
5
|
The Items refer to the additional detail provided to Bills, Invoices, Purchase Orders and Estimates for the purposes of
|
|
10
6
|
documenting a breakdown of materials, labor, equipment, and other resources used for the purposes of the business
|
|
11
7
|
operations.
|
|
@@ -389,7 +385,10 @@ class ItemModelManager(models.Manager):
|
|
|
389
385
|
ItemModelQuerySet
|
|
390
386
|
A Filtered ItemModelQuerySet.
|
|
391
387
|
"""
|
|
392
|
-
qs = self.for_entity_active(
|
|
388
|
+
qs = self.for_entity_active(
|
|
389
|
+
entity_slug=entity_slug,
|
|
390
|
+
user_model=user_model
|
|
391
|
+
)
|
|
393
392
|
return qs.filter(
|
|
394
393
|
(
|
|
395
394
|
Q(is_product_or_service=False) &
|
|
@@ -527,7 +526,6 @@ class ItemModelAbstract(CreateUpdateMixIn):
|
|
|
527
526
|
uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
|
|
528
527
|
name = models.CharField(max_length=100, verbose_name=_('Item Name'))
|
|
529
528
|
|
|
530
|
-
# todo: rename this and remove 'id' from it.
|
|
531
529
|
item_id = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Internal ID'))
|
|
532
530
|
item_number = models.CharField(max_length=30, editable=False, verbose_name=_('Item Number'))
|
|
533
531
|
item_role = models.CharField(max_length=10, choices=ITEM_ROLE_CHOICES, null=True, blank=True)
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
|
|
8
5
|
A Journal Entry (JE) is the foundation of all double entry accounting and financial data of any EntityModel.
|
|
9
6
|
A JE encapsulates a collection of TransactionModel, which must contain two transactions at a minimum. Each transaction
|
|
10
7
|
must perform a DEBIT or a CREDIT to an AccountModel. The JE Model performs additional validation to make sure that
|
|
@@ -37,7 +34,7 @@ from uuid import uuid4, UUID
|
|
|
37
34
|
|
|
38
35
|
from django.core.exceptions import FieldError, ObjectDoesNotExist, ValidationError
|
|
39
36
|
from django.db import models, transaction, IntegrityError
|
|
40
|
-
from django.db.models import Q, Sum, QuerySet, F
|
|
37
|
+
from django.db.models import Q, Sum, QuerySet, F, Manager
|
|
41
38
|
from django.db.models.functions import Coalesce
|
|
42
39
|
from django.db.models.signals import pre_save
|
|
43
40
|
from django.urls import reverse
|
|
@@ -144,7 +141,7 @@ class JournalEntryModelQuerySet(QuerySet):
|
|
|
144
141
|
return self.filter(locked=False)
|
|
145
142
|
|
|
146
143
|
|
|
147
|
-
class JournalEntryModelManager(
|
|
144
|
+
class JournalEntryModelManager(Manager):
|
|
148
145
|
"""
|
|
149
146
|
A custom defined Journal Entry Model Manager that supports additional complex initial Queries based on the
|
|
150
147
|
EntityModel and authenticated UserModel.
|
django_ledger/models/ledger.py
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
|
|
8
5
|
The LedgerModel is the heart of Django Ledger. It is a self-contained unit of accounting that implements a
|
|
9
6
|
double-entry accounting system capable of creating and managing transactions into the ledger and producing any financial
|
|
10
7
|
statements. In essence, an EntityModel is made of a collection of LedgerModels that drive the whole bookkeeping process.
|
django_ledger/models/mixins.py
CHANGED
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
3
|
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
4
|
|
|
5
|
-
Contributions to this module:
|
|
6
|
-
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
-
|
|
8
5
|
This module implements the different model MixIns used on different Django Ledger Models to implement common
|
|
9
6
|
functionality.
|
|
10
7
|
"""
|