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.

Files changed (115) hide show
  1. django_ledger/__init__.py +1 -4
  2. django_ledger/admin/coa.py +1 -2
  3. django_ledger/contrib/django_ledger_graphene/accounts/schema.py +1 -1
  4. django_ledger/forms/account.py +60 -44
  5. django_ledger/forms/bank_account.py +3 -2
  6. django_ledger/forms/bill.py +24 -36
  7. django_ledger/forms/customer.py +1 -1
  8. django_ledger/forms/data_import.py +3 -3
  9. django_ledger/forms/estimate.py +1 -1
  10. django_ledger/forms/invoice.py +5 -7
  11. django_ledger/forms/item.py +24 -15
  12. django_ledger/forms/transactions.py +3 -3
  13. django_ledger/io/io_core.py +4 -2
  14. django_ledger/io/io_middleware.py +5 -0
  15. django_ledger/io/roles.py +6 -0
  16. django_ledger/migrations/0017_alter_accountmodel_unique_together_and_more.py +31 -0
  17. django_ledger/models/accounts.py +629 -342
  18. django_ledger/models/bank_account.py +0 -4
  19. django_ledger/models/bill.py +0 -3
  20. django_ledger/models/closing_entry.py +0 -3
  21. django_ledger/models/coa.py +59 -48
  22. django_ledger/models/coa_default.py +9 -8
  23. django_ledger/models/customer.py +0 -4
  24. django_ledger/models/data_import.py +0 -3
  25. django_ledger/models/entity.py +80 -40
  26. django_ledger/models/estimate.py +0 -9
  27. django_ledger/models/invoice.py +0 -3
  28. django_ledger/models/items.py +4 -6
  29. django_ledger/models/journal_entry.py +2 -5
  30. django_ledger/models/ledger.py +0 -3
  31. django_ledger/models/mixins.py +0 -3
  32. django_ledger/models/purchase_order.py +0 -4
  33. django_ledger/models/signals.py +0 -3
  34. django_ledger/models/transactions.py +2 -5
  35. django_ledger/models/unit.py +0 -3
  36. django_ledger/models/utils.py +0 -3
  37. django_ledger/models/vendor.py +0 -3
  38. django_ledger/report/cash_flow_statement.py +1 -1
  39. django_ledger/static/.DS_Store +0 -0
  40. django_ledger/static/django_ledger/.DS_Store +0 -0
  41. django_ledger/static/django_ledger/logo_2/.DS_Store +0 -0
  42. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark.png +0 -0
  43. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@0.5x.png +0 -0
  44. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@2x.png +0 -0
  45. django_ledger/static/django_ledger/logo_2/django_ledger_logo_dark@3x.png +0 -0
  46. django_ledger/static/django_ledger/logo_2/djl-full-vert.png +0 -0
  47. django_ledger/static/django_ledger/logo_2/djl-full-vert@0.5x.png +0 -0
  48. django_ledger/static/django_ledger/logo_2/djl-full-vert@2x.png +0 -0
  49. django_ledger/static/django_ledger/logo_2/djl-full-vert@3x.png +0 -0
  50. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz.png +0 -0
  51. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@0.5x.png +0 -0
  52. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@2x.png +0 -0
  53. django_ledger/static/django_ledger/logo_2/djl-logo-full-horiz@3x.png +0 -0
  54. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert.png +0 -0
  55. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@0.5x.png +0 -0
  56. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@2x.png +0 -0
  57. django_ledger/static/django_ledger/logo_2/djl-logo-full-vert@3x.png +0 -0
  58. django_ledger/static/django_ledger/logo_2/djl-logo.png +0 -0
  59. django_ledger/static/django_ledger/logo_2/djl-logo@0.5x.png +0 -0
  60. django_ledger/static/django_ledger/logo_2/djl-logo@2x.png +0 -0
  61. django_ledger/static/django_ledger/logo_2/djl-logo@3x.png +0 -0
  62. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz.png +0 -0
  63. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@0.5x.png +0 -0
  64. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@2x.png +0 -0
  65. django_ledger/static/django_ledger/logo_2/djl-txt-full-horiz@3x.png +0 -0
  66. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert.png +0 -0
  67. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@0.5x.png +0 -0
  68. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@2x.png +0 -0
  69. django_ledger/static/django_ledger/logo_2/djl-txt-full-vert@3x.png +0 -0
  70. django_ledger/static/django_ledger/logo_2/djl-txt-horiz.png +0 -0
  71. django_ledger/static/django_ledger/logo_2/djl-txt-horiz@0.5x.png +0 -0
  72. django_ledger/static/django_ledger/logo_2/djl-txt-horiz@2x.png +0 -0
  73. django_ledger/static/django_ledger/logo_2/djl-txt-horiz@3x.png +0 -0
  74. django_ledger/templates/django_ledger/account/account_create.html +2 -2
  75. django_ledger/templates/django_ledger/account/account_update.html +1 -1
  76. django_ledger/templates/django_ledger/account/tags/account_txs_table.html +1 -0
  77. django_ledger/templates/django_ledger/account/tags/accounts_table.html +27 -18
  78. django_ledger/templates/django_ledger/bills/bill_detail.html +3 -3
  79. django_ledger/templates/django_ledger/expense/tags/expense_item_table.html +7 -0
  80. django_ledger/templates/django_ledger/invoice/invoice_detail.html +3 -3
  81. django_ledger/templatetags/django_ledger.py +7 -1
  82. django_ledger/tests/base.py +23 -7
  83. django_ledger/tests/test_accounts.py +145 -9
  84. django_ledger/urls/account.py +17 -24
  85. django_ledger/utils.py +8 -0
  86. django_ledger/views/__init__.py +1 -1
  87. django_ledger/views/account.py +80 -118
  88. django_ledger/views/auth.py +1 -1
  89. django_ledger/views/bank_account.py +9 -11
  90. django_ledger/views/bill.py +91 -80
  91. django_ledger/views/closing_entry.py +8 -0
  92. django_ledger/views/coa.py +2 -1
  93. django_ledger/views/customer.py +1 -1
  94. django_ledger/views/data_import.py +1 -1
  95. django_ledger/views/entity.py +1 -1
  96. django_ledger/views/estimate.py +13 -8
  97. django_ledger/views/feedback.py +1 -1
  98. django_ledger/views/financial_statement.py +1 -1
  99. django_ledger/views/home.py +1 -1
  100. django_ledger/views/inventory.py +9 -0
  101. django_ledger/views/invoice.py +5 -2
  102. django_ledger/views/item.py +58 -68
  103. django_ledger/views/journal_entry.py +1 -1
  104. django_ledger/views/ledger.py +3 -1
  105. django_ledger/views/mixins.py +9 -8
  106. django_ledger/views/purchase_order.py +1 -1
  107. django_ledger/views/transactions.py +1 -1
  108. django_ledger/views/unit.py +9 -0
  109. django_ledger/views/vendor.py +1 -1
  110. {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/AUTHORS.md +8 -2
  111. {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/METADATA +34 -43
  112. {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/RECORD +115 -79
  113. {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/WHEEL +1 -1
  114. {django_ledger-0.6.3.dist-info → django_ledger-0.7.0.dist-info}/top_level.txt +0 -1
  115. {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.
@@ -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.
@@ -1,9 +1,6 @@
1
1
  """
2
2
  Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
-
5
- Contributions to this module:
6
- * Miguel Sanda <msanda@arrobalytics.com>
7
4
  """
8
5
 
9
6
  from datetime import datetime, time
@@ -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.entity.slug,
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.entity.slug
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
- 'coa_slug': self.slug,
707
- 'entity_slug': self.entity.slug
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-coa',
724
+ viewname='django_ledger:account-list',
714
725
  kwargs={
715
- 'entity_slug': self.entity.slug,
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-coa',
733
+ viewname='django_ledger:account-create',
723
734
  kwargs={
724
735
  'coa_slug': self.slug,
725
- 'entity_slug': self.entity.slug
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. Expenses
18
- 5. Revenue
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 root_expenses
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 root_expenses
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},
@@ -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
  """
@@ -1,9 +1,6 @@
1
1
  """
2
2
  Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
-
5
- Contributions to this module:
6
- Miguel Sanda <msanda@arrobalytics.com>
7
4
  """
8
5
 
9
6
  from decimal import Decimal
@@ -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 which is the core structure of
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(self.model, using=self._db).order_by('path')
117
- return qs.order_by('path').select_related('admin', 'default_coa')
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
- order_by: Optional[Tuple] = ('code',)) -> AccountModelQuerySet:
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
- account_model_qs = self.default_coa.accountmodel_set.all().select_related(
1296
- 'coa_model', 'coa_model__entity').not_coa_root()
1297
- else:
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
- account_model_qs = coa_model.accountmodel_set.select_related(
1300
- 'coa_model', 'coa_model__entity').not_coa_root()
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(roles=[
1691
- roles_module.ASSET_CA_CASH,
1692
- roles_module.ASSET_CA_PREPAID,
1693
- roles_module.LIABILITY_CL_ACC_PAYABLE
1694
- ]).is_role_default()
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(roles=[
1800
- roles_module.ASSET_CA_CASH,
1801
- roles_module.ASSET_CA_RECEIVABLES,
1802
- roles_module.LIABILITY_CL_DEFERRED_REVENUE
1803
- ]).is_role_default()
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(roles=roles_module.ASSET_CA_CASH).is_role_default()
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(roles=[
2151
- roles_module.ASSET_CA_INVENTORY,
2152
- roles_module.COGS,
2153
- roles_module.INCOME_OPERATIONAL
2154
- ]).is_role_default()
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(roles=[
2226
- roles_module.COGS,
2227
- roles_module.INCOME_OPERATIONAL
2228
- ]).is_role_default()
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(roles=roles_module.EXPENSE_OPERATIONAL)
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(roles=roles_module.ASSET_CA_INVENTORY)
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(roles=ROLES_NEEDED).is_role_default()
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:
@@ -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
@@ -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
@@ -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(entity_slug=entity_slug, user_model=user_model)
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(models.Manager):
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.
@@ -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.
@@ -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
  """