django-ledger 0.6.4__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/contrib/django_ledger_graphene/accounts/schema.py +1 -1
- django_ledger/forms/account.py +43 -38
- 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/migrations/0017_alter_accountmodel_unique_together_and_more.py +31 -0
- django_ledger/models/accounts.py +225 -265
- 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 +70 -37
- 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/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.4.dist-info → django_ledger-0.7.0.dist-info}/AUTHORS.md +8 -2
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/METADATA +34 -43
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/RECORD +77 -76
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/WHEEL +1 -1
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/LICENSE +0 -0
- {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/top_level.txt +0 -0
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
|
|
|
@@ -1294,9 +1310,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
1294
1310
|
if not coa_model:
|
|
1295
1311
|
coa_model = self.default_coa
|
|
1296
1312
|
elif isinstance(coa_model, UUID):
|
|
1297
|
-
coa_model = self.chartofaccountmodel_set.get(uuid__exact=coa_model)
|
|
1313
|
+
coa_model = self.chartofaccountmodel_set.select_related('entity').get(uuid__exact=coa_model)
|
|
1298
1314
|
elif isinstance(coa_model, str):
|
|
1299
|
-
coa_model = self.chartofaccountmodel_set.get(slug__exact=coa_model)
|
|
1315
|
+
coa_model = self.chartofaccountmodel_set.select_related('entity').get(slug__exact=coa_model)
|
|
1300
1316
|
elif isinstance(coa_model, ChartOfAccountModel):
|
|
1301
1317
|
self.validate_chart_of_accounts_for_entity(coa_model=coa_model)
|
|
1302
1318
|
else:
|
|
@@ -1309,9 +1325,14 @@ class EntityModelAbstract(MP_Node,
|
|
|
1309
1325
|
if active:
|
|
1310
1326
|
account_model_qs = account_model_qs.active()
|
|
1311
1327
|
|
|
1328
|
+
if locked:
|
|
1329
|
+
account_model_qs = account_model_qs.locked()
|
|
1330
|
+
|
|
1312
1331
|
if order_by:
|
|
1313
1332
|
account_model_qs = account_model_qs.order_by(*order_by)
|
|
1314
1333
|
|
|
1334
|
+
if return_coa_model:
|
|
1335
|
+
return coa_model, account_model_qs
|
|
1315
1336
|
return account_model_qs
|
|
1316
1337
|
|
|
1317
1338
|
def get_default_coa_accounts(self,
|
|
@@ -1694,11 +1715,12 @@ class EntityModelAbstract(MP_Node,
|
|
|
1694
1715
|
|
|
1695
1716
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
1696
1717
|
|
|
1697
|
-
account_model_qs = account_model_qs.with_roles(
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
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()
|
|
1702
1724
|
|
|
1703
1725
|
# evaluates the queryset...
|
|
1704
1726
|
len(account_model_qs)
|
|
@@ -1803,11 +1825,12 @@ class EntityModelAbstract(MP_Node,
|
|
|
1803
1825
|
raise EntityModelValidationError('CustomerModel must be an instance of CustomerModel, UUID or str.')
|
|
1804
1826
|
|
|
1805
1827
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
1806
|
-
account_model_qs = account_model_qs.with_roles(
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
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()
|
|
1811
1834
|
|
|
1812
1835
|
# evaluates the queryset...
|
|
1813
1836
|
len(account_model_qs)
|
|
@@ -1998,7 +2021,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
1998
2021
|
raise EntityModelValidationError(
|
|
1999
2022
|
_(f'Invalid Account Type: choices are {BankAccountModel.VALID_ACCOUNT_TYPES}'))
|
|
2000
2023
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2001
|
-
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()
|
|
2002
2027
|
bank_account_model = BankAccountModel(
|
|
2003
2028
|
name=name,
|
|
2004
2029
|
entity_model=self,
|
|
@@ -2154,11 +2179,12 @@ class EntityModelAbstract(MP_Node,
|
|
|
2154
2179
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2155
2180
|
|
|
2156
2181
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2157
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
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()
|
|
2162
2188
|
|
|
2163
2189
|
# evaluates the queryset...
|
|
2164
2190
|
len(account_model_qs)
|
|
@@ -2229,10 +2255,11 @@ class EntityModelAbstract(MP_Node,
|
|
|
2229
2255
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2230
2256
|
|
|
2231
2257
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2232
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2258
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2259
|
+
roles=[
|
|
2260
|
+
roles_module.COGS,
|
|
2261
|
+
roles_module.INCOME_OPERATIONAL
|
|
2262
|
+
]).is_role_default()
|
|
2236
2263
|
|
|
2237
2264
|
# evaluates the queryset...
|
|
2238
2265
|
len(account_model_qs)
|
|
@@ -2308,7 +2335,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
2308
2335
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2309
2336
|
|
|
2310
2337
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2311
|
-
account_model_qs = account_model_qs.with_roles(
|
|
2338
|
+
account_model_qs = account_model_qs.with_roles(
|
|
2339
|
+
roles=roles_module.EXPENSE_OPERATIONAL
|
|
2340
|
+
)
|
|
2312
2341
|
if not expense_account:
|
|
2313
2342
|
expense_account = account_model_qs.is_role_default().get()
|
|
2314
2343
|
elif isinstance(expense_account, UUID):
|
|
@@ -2407,7 +2436,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
2407
2436
|
raise EntityModelValidationError(f'Invalid UnitOfMeasureModel for entity {self.slug}...')
|
|
2408
2437
|
|
|
2409
2438
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model, active=True)
|
|
2410
|
-
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
|
+
)
|
|
2411
2442
|
if not inventory_account:
|
|
2412
2443
|
inventory_account = account_model_qs.is_role_default().get()
|
|
2413
2444
|
elif isinstance(inventory_account, UUID):
|
|
@@ -2627,7 +2658,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
2627
2658
|
ROLES_NEEDED.append(roles_module.EQUITY_CAPITAL)
|
|
2628
2659
|
|
|
2629
2660
|
account_model_qs = self.get_coa_accounts(coa_model=coa_model)
|
|
2630
|
-
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()
|
|
2631
2664
|
|
|
2632
2665
|
if not cash_account or not capital_account:
|
|
2633
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
|
"""
|
|
@@ -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 purchase order is a commercial source document that is issued by a business purchasing department when placing an
|
|
10
6
|
order with its vendors or suppliers. The document indicates the details on the items that are to be purchased, such as
|
|
11
7
|
the types of goods, quantity, and price. In simple terms, it is the contract drafted by the buyer when purchasing goods
|
django_ledger/models/signals.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 signals module provide the means to notify listeners about important events or states in the models,
|
|
9
6
|
such as a ledger model being posted or a bill status changing.
|
|
10
7
|
"""
|