django-ledger 0.6.2__py3-none-any.whl → 0.6.4__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 -1
- django_ledger/admin/coa.py +1 -2
- django_ledger/admin/ledger.py +1 -0
- django_ledger/forms/account.py +22 -11
- django_ledger/forms/data_import.py +2 -1
- django_ledger/io/ofx.py +3 -2
- django_ledger/io/roles.py +6 -0
- django_ledger/models/accounts.py +523 -196
- django_ledger/models/bill.py +28 -14
- django_ledger/models/closing_entry.py +8 -0
- django_ledger/models/entity.py +12 -5
- django_ledger/models/estimate.py +93 -21
- django_ledger/models/invoice.py +44 -14
- django_ledger/models/journal_entry.py +112 -49
- django_ledger/models/ledger.py +32 -0
- django_ledger/models/purchase_order.py +34 -3
- django_ledger/models/signals.py +58 -0
- django_ledger/report/cash_flow_statement.py +1 -1
- django_ledger/report/core.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/financial_statements/balance_sheet.html +2 -2
- django_ledger/tests/test_io_ofx/__init__.py +0 -0
- django_ledger/tests/test_io_ofx/tests.py +52 -0
- django_ledger/views/account.py +1 -1
- django_ledger/views/bill.py +1 -1
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/METADATA +1 -1
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/RECORD +65 -27
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/top_level.txt +0 -1
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/AUTHORS.md +0 -0
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/LICENSE +0 -0
- {django_ledger-0.6.2.dist-info → django_ledger-0.6.4.dist-info}/WHEEL +0 -0
django_ledger/models/accounts.py
CHANGED
|
@@ -6,24 +6,52 @@ Contributions to this module:
|
|
|
6
6
|
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
* Pranav P Tulshyan <ptulshyan77@gmail.com>
|
|
8
8
|
|
|
9
|
-
The AccountModel groups and sorts transactions involving the company's assets, liabilities and equities.
|
|
10
|
-
Per accounting principles, an Account must be either a DEBIT-type balance account or a CREDIT-type balance account,
|
|
11
|
-
depending on its purpose.
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
reduce its balance. Conversely, a CREDIT to a CREDIT-type AccountModel will increase its balance, and a
|
|
16
|
-
DEBIT to a CREDIT-type AccountModel will reduce its balance.
|
|
10
|
+
AccountModel
|
|
11
|
+
------------
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
The AccountModel is a fundamental component of the Django Ledger system, responsible for categorizing and organizing
|
|
14
|
+
financial transactions related to an entity's assets, liabilities, and equity.
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
regardless of its user-defined fields. Roles are an integral part to Django Ledger since they are critical when
|
|
24
|
-
requesting and producing financial statements and financial ratio calculations.
|
|
16
|
+
Account Types
|
|
17
|
+
-------------
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
In accordance with accounting principles, each AccountModel must be classified as either:
|
|
20
|
+
|
|
21
|
+
1. **DEBIT-type balance account**
|
|
22
|
+
2. **CREDIT-type balance account**
|
|
23
|
+
|
|
24
|
+
The account type determines how transactions affect the account's balance.
|
|
25
|
+
|
|
26
|
+
Double Entry Accounting
|
|
27
|
+
-----------------------
|
|
28
|
+
|
|
29
|
+
The AccountModel is crucial in implementing double entry accounting systems:
|
|
30
|
+
|
|
31
|
+
* For DEBIT-type accounts:
|
|
32
|
+
- A DEBIT increases the balance
|
|
33
|
+
- A CREDIT decreases the balance
|
|
34
|
+
|
|
35
|
+
* For CREDIT-type accounts:
|
|
36
|
+
- A CREDIT increases the balance
|
|
37
|
+
- A DEBIT decreases the balance
|
|
38
|
+
|
|
39
|
+
Chart of Accounts
|
|
40
|
+
-----------------
|
|
41
|
+
|
|
42
|
+
Users have the flexibility to adopt a chart of accounts that best suits their EntityModel. Django Ledger provides a
|
|
43
|
+
default Chart of Accounts when creating a new EntityModel, which can be customized as needed.
|
|
44
|
+
|
|
45
|
+
Account Roles
|
|
46
|
+
-------------
|
|
47
|
+
|
|
48
|
+
All AccountModels must be assigned a role from the `ACCOUNT_ROLES` function in `django_ledger.io.roles`.
|
|
49
|
+
Roles serve several purposes:
|
|
50
|
+
|
|
51
|
+
1. Group accounts into common namespaces
|
|
52
|
+
2. Provide consistency across user-defined fields
|
|
53
|
+
3. Enable accurate generation of financial statements
|
|
54
|
+
4. Facilitate financial ratio calculations
|
|
27
55
|
"""
|
|
28
56
|
from itertools import groupby
|
|
29
57
|
from random import randint
|
|
@@ -43,7 +71,7 @@ from django_ledger.io.roles import (ACCOUNT_ROLE_CHOICES, BS_ROLES, GROUP_INVOIC
|
|
|
43
71
|
GROUP_ASSETS,
|
|
44
72
|
GROUP_LIABILITIES, GROUP_CAPITAL, GROUP_INCOME, GROUP_EXPENSES, GROUP_COGS,
|
|
45
73
|
ROOT_GROUP, BS_BUCKETS, ROOT_ASSETS, ROOT_LIABILITIES,
|
|
46
|
-
ROOT_CAPITAL, ROOT_INCOME, ROOT_EXPENSES, ROOT_COA)
|
|
74
|
+
ROOT_CAPITAL, ROOT_INCOME, ROOT_EXPENSES, ROOT_COA, VALID_PARENTS)
|
|
47
75
|
from django_ledger.models.mixins import CreateUpdateMixIn
|
|
48
76
|
from django_ledger.models.utils import lazy_loader
|
|
49
77
|
from django_ledger.settings import DJANGO_LEDGER_ACCOUNT_CODE_GENERATE, DJANGO_LEDGER_ACCOUNT_CODE_USE_PREFIX
|
|
@@ -61,68 +89,71 @@ class AccountModelValidationError(ValidationError):
|
|
|
61
89
|
|
|
62
90
|
class AccountModelQuerySet(MP_NodeQuerySet):
|
|
63
91
|
"""
|
|
64
|
-
|
|
65
|
-
of Django Treebeard for tree-like model implementation.
|
|
92
|
+
Custom QuerySet for AccountModel inheriting from MP_NodeQuerySet.
|
|
66
93
|
"""
|
|
67
94
|
|
|
68
95
|
def active(self):
|
|
69
96
|
"""
|
|
70
|
-
|
|
97
|
+
Filters the queryset to include only active items.
|
|
71
98
|
|
|
72
99
|
Returns
|
|
73
|
-
|
|
100
|
+
-------
|
|
74
101
|
AccountModelQuerySet
|
|
75
|
-
A filtered
|
|
102
|
+
A filtered queryset containing only the items marked as active.
|
|
76
103
|
"""
|
|
77
104
|
return self.filter(active=True)
|
|
78
105
|
|
|
79
106
|
def inactive(self):
|
|
80
107
|
"""
|
|
81
|
-
|
|
108
|
+
Filters and returns queryset entries where the active field is set to False.
|
|
82
109
|
|
|
83
110
|
Returns
|
|
84
|
-
|
|
111
|
+
-------
|
|
85
112
|
AccountModelQuerySet
|
|
86
|
-
A
|
|
113
|
+
A queryset containing entries with active=False.
|
|
87
114
|
"""
|
|
88
115
|
return self.filter(active=False)
|
|
89
116
|
|
|
90
117
|
def locked(self):
|
|
91
118
|
"""
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
This method filters the elements based on the `locked` attribute and returns a filtered queryset.
|
|
119
|
+
Filters the queryset to include only locked AccountModels.
|
|
95
120
|
|
|
96
|
-
Returns
|
|
97
|
-
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
AccountModelQuerySet
|
|
124
|
+
A queryset containing only the objects with locked set to True.
|
|
98
125
|
"""
|
|
99
126
|
return self.filter(locked=True)
|
|
100
127
|
|
|
101
128
|
def unlocked(self):
|
|
102
129
|
"""
|
|
103
|
-
Returns a filtered
|
|
130
|
+
Returns a filtered list of items where the 'locked' attribute is set to False.
|
|
104
131
|
|
|
105
|
-
Returns
|
|
106
|
-
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
AccountModelQuerySet
|
|
135
|
+
A queryset of items with 'locked' attribute set to False
|
|
107
136
|
"""
|
|
108
137
|
return self.filter(locked=False)
|
|
109
138
|
|
|
110
139
|
def with_roles(self, roles: Union[List, str]):
|
|
111
140
|
"""
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
141
|
+
Filter the accounts based on the specified roles. This method helps to retrieve accounts associated
|
|
142
|
+
with a particular role or a list of roles.
|
|
143
|
+
|
|
144
|
+
For example, to get all accounts categorized under the role "asset_ppe_build" (which might include
|
|
145
|
+
fixed assets like Buildings), you can utilize this method.
|
|
116
146
|
|
|
117
147
|
Parameters
|
|
118
|
-
|
|
119
|
-
roles:
|
|
120
|
-
|
|
148
|
+
----------
|
|
149
|
+
roles : Union[List[str], str]
|
|
150
|
+
The role or a list of roles to filter the accounts by. If a single string is provided, it is converted
|
|
151
|
+
into a list containing that role.
|
|
121
152
|
|
|
122
153
|
Returns
|
|
123
|
-
|
|
154
|
+
-------
|
|
124
155
|
AccountModelQuerySet
|
|
125
|
-
|
|
156
|
+
A QuerySet of accounts filtered by the provided roles.
|
|
126
157
|
"""
|
|
127
158
|
if isinstance(roles, str):
|
|
128
159
|
roles = [roles]
|
|
@@ -131,27 +162,58 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
131
162
|
|
|
132
163
|
def expenses(self):
|
|
133
164
|
"""
|
|
134
|
-
|
|
165
|
+
Retrieve a queryset containing expenses filtered by specified roles.
|
|
135
166
|
|
|
136
|
-
|
|
137
|
-
|
|
167
|
+
This method filters the expenses based on roles defined in the
|
|
168
|
+
`GROUP_EXPENSES` constant. It ensures that only the relevant expenses
|
|
169
|
+
associated with the specified roles are included in the queryset.
|
|
170
|
+
|
|
171
|
+
Returns
|
|
172
|
+
-------
|
|
173
|
+
AccountModelQuerySet
|
|
174
|
+
A queryset consisting of expenses filtered according to the roles in `GROUP_EXPENSES`.
|
|
138
175
|
"""
|
|
139
176
|
return self.filter(role__in=GROUP_EXPENSES)
|
|
140
177
|
|
|
141
178
|
def is_coa_root(self):
|
|
142
179
|
"""
|
|
143
|
-
|
|
180
|
+
Retrieves the Chart of Accounts (CoA) root node queryset.
|
|
181
|
+
|
|
182
|
+
A Chart of Accounts Root is a foundational element indicating the primary node in the
|
|
183
|
+
account hierarchy. This method filters the queryset to include only the Chart of Accounts (CoA)
|
|
184
|
+
root node.
|
|
144
185
|
|
|
145
|
-
Returns
|
|
146
|
-
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
AccountModelQuerySet
|
|
147
189
|
"""
|
|
148
190
|
return self.filter(role__in=ROOT_GROUP)
|
|
149
191
|
|
|
150
192
|
def not_coa_root(self):
|
|
193
|
+
"""
|
|
194
|
+
Exclude AccountModels with ROOT_GROUP role from the QuerySet.
|
|
151
195
|
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
AccountModelQuerySet
|
|
199
|
+
A QuerySet excluding users with role in ROOT_GROUP.
|
|
200
|
+
"""
|
|
152
201
|
return self.exclude(role__in=ROOT_GROUP)
|
|
153
202
|
|
|
154
203
|
def for_entity(self, entity_slug, user_model):
|
|
204
|
+
"""
|
|
205
|
+
Parameters
|
|
206
|
+
----------
|
|
207
|
+
entity_slug : str
|
|
208
|
+
The slug identifier for the entity.
|
|
209
|
+
user_model : UserModel
|
|
210
|
+
The user model instance to use for filtering.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
AccountModelQuerySet
|
|
215
|
+
A Django QuerySet filtered by the specified entity and user permissions, ordered by 'code'.
|
|
216
|
+
"""
|
|
155
217
|
if isinstance(self, lazy_loader.get_entity_model()):
|
|
156
218
|
return self.filter(
|
|
157
219
|
Q(coa_model__entity=entity_slug) &
|
|
@@ -169,6 +231,16 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
169
231
|
).order_by('code')
|
|
170
232
|
|
|
171
233
|
def gb_bs_role(self):
|
|
234
|
+
"""
|
|
235
|
+
Groups accounts by Balance Sheet Bucket and then further groups them by role.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
List[Tuple]
|
|
240
|
+
A list where each element is a tuple. The first element of the tuple is the BS bucket,
|
|
241
|
+
and the second element is a list of tuples where each sub-tuple contains a role display
|
|
242
|
+
and a list of accounts that fall into that role within the BS bucket.
|
|
243
|
+
"""
|
|
172
244
|
accounts_gb = list((r, list(gb)) for r, gb in groupby(self, key=lambda acc: acc.get_bs_bucket()))
|
|
173
245
|
return [
|
|
174
246
|
(bsr, [
|
|
@@ -177,9 +249,26 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
177
249
|
]
|
|
178
250
|
|
|
179
251
|
def is_role_default(self):
|
|
252
|
+
"""
|
|
253
|
+
Filter the queryset to include only entries where `role_default`
|
|
254
|
+
is set to True, excluding entries marked as 'coa_root'.
|
|
255
|
+
|
|
256
|
+
Returns
|
|
257
|
+
-------
|
|
258
|
+
AccountModelQuerySet
|
|
259
|
+
Filtered queryset with `role_default` set to True and excluding 'coa_root' entries.
|
|
260
|
+
"""
|
|
180
261
|
return self.not_coa_root().filter(role_default=True)
|
|
181
262
|
|
|
182
263
|
def can_transact(self):
|
|
264
|
+
"""
|
|
265
|
+
Filter the queryset to include only accounts that can accept new transactions.
|
|
266
|
+
|
|
267
|
+
Returns
|
|
268
|
+
-------
|
|
269
|
+
QuerySet
|
|
270
|
+
A QuerySet containing the filtered results.
|
|
271
|
+
"""
|
|
183
272
|
return self.filter(
|
|
184
273
|
Q(locked=False) & Q(active=True)
|
|
185
274
|
)
|
|
@@ -193,14 +282,34 @@ class AccountModelManager(MP_NodeManager):
|
|
|
193
282
|
|
|
194
283
|
def get_queryset(self) -> AccountModelQuerySet:
|
|
195
284
|
"""
|
|
196
|
-
|
|
285
|
+
Retrieve and return athe default AccountModel QuerySet.
|
|
286
|
+
|
|
287
|
+
The query set is ordered by the 'path' field and uses 'select_related' to reduce the number of database queries
|
|
288
|
+
by retrieving the related 'coa_model'.
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
AccountModelQuerySet
|
|
293
|
+
An instance of AccountModelQuerySet ordered by 'path' and prefetching related 'coa_model'.
|
|
197
294
|
"""
|
|
198
295
|
return AccountModelQuerySet(
|
|
199
296
|
self.model,
|
|
200
297
|
using=self._db
|
|
201
298
|
).order_by('path').select_related('coa_model')
|
|
202
299
|
|
|
203
|
-
def for_user(self, user_model):
|
|
300
|
+
def for_user(self, user_model) -> AccountModelQuerySet:
|
|
301
|
+
"""
|
|
302
|
+
Parameters
|
|
303
|
+
----------
|
|
304
|
+
user_model : UserModel
|
|
305
|
+
The user model instance to use for filtering.
|
|
306
|
+
|
|
307
|
+
Returns
|
|
308
|
+
-------
|
|
309
|
+
AccountModelQuerySet
|
|
310
|
+
The filtered queryset based on the user's permissions. Superusers get the complete queryset whereas other
|
|
311
|
+
users get a filtered queryset based on their role as admin or manager in the entity.
|
|
312
|
+
"""
|
|
204
313
|
qs = self.get_queryset()
|
|
205
314
|
if user_model.is_superuser:
|
|
206
315
|
return qs
|
|
@@ -210,32 +319,33 @@ class AccountModelManager(MP_NodeManager):
|
|
|
210
319
|
)
|
|
211
320
|
|
|
212
321
|
# todo: search for uses and pass EntityModel whenever possible.
|
|
213
|
-
def for_entity(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
322
|
+
def for_entity(
|
|
323
|
+
self,
|
|
324
|
+
user_model,
|
|
325
|
+
entity_slug,
|
|
326
|
+
coa_slug: Optional[str] = None,
|
|
327
|
+
select_coa_model: bool = True
|
|
328
|
+
) -> AccountModelQuerySet:
|
|
218
329
|
"""
|
|
219
|
-
|
|
330
|
+
Retrieves accounts associated with the specified EntityModel.
|
|
220
331
|
|
|
221
332
|
Parameters
|
|
222
333
|
----------
|
|
223
|
-
|
|
224
|
-
The EntityModel or EntityModel slug to pull accounts from. If slug is passed and coa_slug is None will
|
|
225
|
-
result in an additional Database query to determine the default code of accounts.
|
|
226
|
-
coa_slug: str
|
|
227
|
-
Explicitly specify which chart of accounts to use. If None, will pull default Chart of Accounts.
|
|
228
|
-
Discussed in detail in the CoA Model CoA slug, basically helps in identifying the complete Chart of
|
|
229
|
-
Accounts for a particular EntityModel.
|
|
230
|
-
user_model:
|
|
334
|
+
user_model: User
|
|
231
335
|
The Django User Model making the request to check for permissions.
|
|
232
|
-
|
|
233
|
-
|
|
336
|
+
entity_slug: Union[EntityModel, str]
|
|
337
|
+
The EntityModel instance or its slug to filter accounts by. If a slug is provided and `coa_slug` is None,
|
|
338
|
+
an additional
|
|
339
|
+
database query will be executed to determine the default Chart of Accounts.
|
|
340
|
+
coa_slug: Optional[str], default=None
|
|
341
|
+
The slug of the specific Chart of Accounts to use. If None, the default Chart of Accounts is selected.
|
|
342
|
+
select_coa_model: bool, default=True
|
|
343
|
+
If True, prefetches the CoA Model information in the QuerySet.
|
|
234
344
|
|
|
235
345
|
Returns
|
|
236
346
|
-------
|
|
237
347
|
AccountModelQuerySet
|
|
238
|
-
A QuerySet
|
|
348
|
+
A QuerySet containing accounts associated with the specified EntityModel and Chart of Accounts.
|
|
239
349
|
"""
|
|
240
350
|
qs = self.for_user(user_model)
|
|
241
351
|
if select_coa_model:
|
|
@@ -256,26 +366,28 @@ class AccountModelManager(MP_NodeManager):
|
|
|
256
366
|
|
|
257
367
|
def for_entity_available(self, user_model, entity_slug, coa_slug: Optional[str] = None) -> AccountModelQuerySet:
|
|
258
368
|
"""
|
|
259
|
-
|
|
369
|
+
Retrieve available and unlocked AccountModels for a specific EntityModel.
|
|
370
|
+
|
|
371
|
+
This method filters AccountModels associated with the specified EntityModel
|
|
372
|
+
that are active, not locked, and have an active Chart of Accounts.
|
|
260
373
|
|
|
261
374
|
Parameters
|
|
262
375
|
----------
|
|
263
|
-
|
|
264
|
-
The
|
|
265
|
-
result in an additional Database query to determine the default code of accounts.
|
|
376
|
+
user_model: User
|
|
377
|
+
The Django User Model instance making the request, used to validate permissions.
|
|
266
378
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
379
|
+
entity_slug: EntityModel or str
|
|
380
|
+
The EntityModel instance or its slug to pull accounts from. If entity_slug is passed
|
|
381
|
+
and coa_slug is None, an additional database query will be performed to determine
|
|
382
|
+
the default Chart of Accounts.
|
|
271
383
|
|
|
272
|
-
|
|
273
|
-
The
|
|
384
|
+
coa_slug: str, optional
|
|
385
|
+
The specific Chart of Accounts to use. If None, the default Chart of Accounts will be pulled.
|
|
274
386
|
|
|
275
387
|
Returns
|
|
276
388
|
-------
|
|
277
389
|
AccountModelQuerySet
|
|
278
|
-
A QuerySet
|
|
390
|
+
A QuerySet containing available and unlocked AccountModels for the specified EntityModel and Chart of Accounts.
|
|
279
391
|
"""
|
|
280
392
|
qs = self.for_entity(
|
|
281
393
|
user_model=user_model,
|
|
@@ -289,24 +401,27 @@ class AccountModelManager(MP_NodeManager):
|
|
|
289
401
|
|
|
290
402
|
def with_roles(self, roles: Union[list, str], entity_slug, user_model) -> AccountModelQuerySet:
|
|
291
403
|
"""
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
accounts
|
|
295
|
-
|
|
404
|
+
Retrieve accounts based on specific roles.
|
|
405
|
+
|
|
406
|
+
This method filters accounts associated with a given role or a list of roles. For example, if you need to
|
|
407
|
+
find all accounts under the "asset_ppe_build" role, which includes all buildings fixed assets, this method
|
|
408
|
+
can be used.
|
|
296
409
|
|
|
297
410
|
Parameters
|
|
298
411
|
----------
|
|
299
412
|
entity_slug: EntityModel or str
|
|
300
|
-
The EntityModel or
|
|
301
|
-
|
|
302
|
-
user_model
|
|
303
|
-
The Django User
|
|
413
|
+
The EntityModel instance or its slug to fetch accounts from. If only the slug is provided and coa_slug is
|
|
414
|
+
not specified, an additional database query will be performed to determine the default chart of accounts.
|
|
415
|
+
user_model: User
|
|
416
|
+
The Django User model instance making the request to ensure appropriate permissions are checked.
|
|
304
417
|
roles: list or str
|
|
305
|
-
|
|
418
|
+
Accepts either a single role as a string or a list of roles. Refer to io.roles.py for a comprehensive
|
|
419
|
+
list of roles.
|
|
420
|
+
|
|
306
421
|
Returns
|
|
307
422
|
-------
|
|
308
423
|
AccountModelQuerySet
|
|
309
|
-
|
|
424
|
+
A QuerySet of accounts filtered by the specified roles.
|
|
310
425
|
"""
|
|
311
426
|
roles = validate_roles(roles)
|
|
312
427
|
if isinstance(roles, str):
|
|
@@ -319,27 +434,26 @@ class AccountModelManager(MP_NodeManager):
|
|
|
319
434
|
user_model,
|
|
320
435
|
coa_slug: Optional[str]) -> AccountModelQuerySet:
|
|
321
436
|
"""
|
|
322
|
-
|
|
323
|
-
specific list of roles.
|
|
437
|
+
Retrieve available and unlocked AccountModels for a specified EntityModel and list of roles.
|
|
324
438
|
|
|
325
439
|
Parameters
|
|
326
440
|
----------
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
The
|
|
336
|
-
|
|
337
|
-
Function accepts a single str instance of a role or a list of roles. For a list of roles , refer io.roles.py
|
|
441
|
+
roles : Union[list, str]
|
|
442
|
+
A single role as a string or a list of roles.
|
|
443
|
+
entity_slug : Union[str, 'EntityModel']
|
|
444
|
+
The EntityModel object or its slug. If a slug is provided and `coa_slug` is None, an additional
|
|
445
|
+
database query will be executed to fetch the default Chart of Accounts.
|
|
446
|
+
user_model : 'UserModel'
|
|
447
|
+
The Django UserModel instance making the request, used to check permissions.
|
|
448
|
+
coa_slug : Optional[str], default None
|
|
449
|
+
The specific Chart of Accounts slug. If None, the default Chart of Accounts will be used.
|
|
450
|
+
This parameter assists in identifying the complete Chart of Accounts for the EntityModel.
|
|
338
451
|
|
|
339
452
|
Returns
|
|
340
453
|
-------
|
|
341
454
|
AccountModelQuerySet
|
|
342
|
-
A QuerySet
|
|
455
|
+
A QuerySet containing available and unlocked AccountModel instances for the specified
|
|
456
|
+
EntityModel and roles.
|
|
343
457
|
"""
|
|
344
458
|
|
|
345
459
|
if isinstance(roles, str):
|
|
@@ -350,53 +464,53 @@ class AccountModelManager(MP_NodeManager):
|
|
|
350
464
|
|
|
351
465
|
def coa_roots(self, user_model, entity_slug, coa_slug) -> AccountModelQuerySet:
|
|
352
466
|
"""
|
|
353
|
-
|
|
467
|
+
Retrieves the root accounts of a specified Code of Accounts (CoA).
|
|
354
468
|
|
|
355
469
|
Parameters
|
|
356
470
|
----------
|
|
357
|
-
|
|
358
|
-
The
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
The Django User Model making the request to check for permissions.
|
|
471
|
+
user_model: object
|
|
472
|
+
The Django User model instance requesting the data, used for permission checking.
|
|
473
|
+
entity_slug: Union[EntityModel, str]
|
|
474
|
+
The entity or its slug from which to fetch accounts. If a slug is provided and `coa_slug` is None,
|
|
475
|
+
an additional database query is performed to determine the default Code of Accounts.
|
|
476
|
+
coa_slug: Optional[str]
|
|
477
|
+
The specific chart of accounts to retrieve. If None, the default chart of accounts for the entity
|
|
478
|
+
will be used. This is crucial for identifying the complete set of accounts for a given entity.
|
|
366
479
|
|
|
367
480
|
Returns
|
|
368
481
|
-------
|
|
369
|
-
|
|
482
|
+
AccountModelQuerySet
|
|
483
|
+
A queryset of root accounts for the specified Code of Accounts.
|
|
370
484
|
"""
|
|
371
485
|
qs = self.for_entity(user_model=user_model, entity_slug=entity_slug, coa_slug=coa_slug)
|
|
372
486
|
return qs.is_coa_root()
|
|
373
487
|
|
|
374
488
|
def for_invoice(self, user_model, entity_slug: str, coa_slug: Optional[str] = None) -> AccountModelQuerySet:
|
|
375
489
|
"""
|
|
376
|
-
|
|
377
|
-
|
|
490
|
+
Retrieves available and unlocked AccountModels for a specific EntityModel, specifically for the creation
|
|
491
|
+
and management of Invoices.
|
|
378
492
|
|
|
379
|
-
|
|
493
|
+
This method ensures that only relevant accounts are pulled, as defined under the roles in `GROUP_INVOICE`.
|
|
494
|
+
These roles include: ASSET_CA_CASH, ASSET_CA_RECEIVABLES, and LIABILITY_CL_DEFERRED_REVENUE.
|
|
380
495
|
|
|
381
496
|
Parameters
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
The EntityModel or EntityModel slug to pull accounts from. If slug is passed and coa_slug is None will
|
|
386
|
-
result in an additional Database query to determine the default code of accounts.
|
|
497
|
+
----------
|
|
498
|
+
user_model: User
|
|
499
|
+
The Django User Model instance requesting access. It is used to check the necessary permissions.
|
|
387
500
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
Accounts for a particular EntityModel.
|
|
501
|
+
entity_slug: Union[EntityModel, str]
|
|
502
|
+
Specifies the EntityModel or its slug to pull accounts from. If a slug is provided and `coa_slug` is `None`,
|
|
503
|
+
the method will perform an additional database query to determine the default chart of accounts.
|
|
392
504
|
|
|
393
|
-
|
|
394
|
-
|
|
505
|
+
coa_slug: Optional[str], default=None
|
|
506
|
+
Explicitly specifies which chart of accounts to use. If `None`, the method will default to using
|
|
507
|
+
the EntityModel's default chart of accounts.
|
|
395
508
|
|
|
396
509
|
Returns
|
|
397
|
-
|
|
510
|
+
-------
|
|
398
511
|
AccountModelQuerySet
|
|
399
|
-
A QuerySet
|
|
512
|
+
A QuerySet containing the AccountModels relevant for the specified EntityModel and the roles defined
|
|
513
|
+
in `GROUP_INVOICE`.
|
|
400
514
|
"""
|
|
401
515
|
qs = self.for_entity_available(
|
|
402
516
|
user_model=user_model,
|
|
@@ -406,30 +520,26 @@ class AccountModelManager(MP_NodeManager):
|
|
|
406
520
|
|
|
407
521
|
def for_bill(self, user_model, entity_slug, coa_slug: Optional[str] = None) -> AccountModelQuerySet:
|
|
408
522
|
"""
|
|
409
|
-
|
|
410
|
-
for
|
|
411
|
-
|
|
412
|
-
Roles in GROUP_BILL: ASSET_CA_CASH, ASSET_CA_PREPAID, LIABILITY_CL_ACC_PAYABLE.
|
|
523
|
+
Retrieves only available and unlocked AccountModels for a specific EntityModel,
|
|
524
|
+
specifically for the creation and management of Bills. Roles within the 'GROUP_BILL'
|
|
525
|
+
context include: ASSET_CA_CASH, ASSET_CA_PREPAID, and LIABILITY_CL_ACC_PAYABLE.
|
|
413
526
|
|
|
414
527
|
Parameters
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
The EntityModel or EntityModel slug to pull accounts from. If slug is passed and coa_slug is None will
|
|
419
|
-
result in an additional Database query to determine the default code of accounts.
|
|
528
|
+
----------
|
|
529
|
+
user_model : Django User Model
|
|
530
|
+
The Django User Model that is making the request, used to check for permissions.
|
|
420
531
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
Accounts for a particular EntityModel.
|
|
532
|
+
entity_slug : Union[EntityModel, str]
|
|
533
|
+
The EntityModel or EntityModel slug from which to pull accounts. If given a slug and coa_slug
|
|
534
|
+
is None, an additional database query will be made to determine the default chart of accounts.
|
|
425
535
|
|
|
426
|
-
|
|
427
|
-
The
|
|
536
|
+
coa_slug : Optional[str]
|
|
537
|
+
The specific chart of accounts to use. If None, it will default to the EntityModel's default chart of accounts.
|
|
428
538
|
|
|
429
539
|
Returns
|
|
430
|
-
|
|
540
|
+
-------
|
|
431
541
|
AccountModelQuerySet
|
|
432
|
-
A QuerySet of
|
|
542
|
+
A QuerySet of the requested EntityModel's chart of accounts.
|
|
433
543
|
"""
|
|
434
544
|
qs = self.for_entity_available(
|
|
435
545
|
user_model=user_model,
|
|
@@ -444,42 +554,33 @@ def account_code_validator(value: str):
|
|
|
444
554
|
|
|
445
555
|
|
|
446
556
|
class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
447
|
-
"""
|
|
448
|
-
|
|
557
|
+
"""
|
|
449
558
|
Abstract class representing an Account Model.
|
|
450
559
|
|
|
451
560
|
Attributes
|
|
452
561
|
----------
|
|
453
562
|
BALANCE_TYPE : list
|
|
454
|
-
List of choices for the balance type of the account.
|
|
455
|
-
|
|
563
|
+
List of choices for the balance type of the account. Options include 'Credit' and 'Debit'.
|
|
456
564
|
uuid : UUIDField
|
|
457
|
-
|
|
458
|
-
|
|
565
|
+
Unique identifier for each account instance.
|
|
459
566
|
code : CharField
|
|
460
|
-
|
|
461
|
-
|
|
567
|
+
Code representing the account, constrained by length and specific validation rules.
|
|
462
568
|
name : CharField
|
|
463
|
-
|
|
464
|
-
|
|
569
|
+
Name of the account, constrained by length.
|
|
465
570
|
role : CharField
|
|
466
|
-
|
|
467
|
-
|
|
571
|
+
Role associated with the account, with specific predefined choices.
|
|
468
572
|
role_default : BooleanField
|
|
469
|
-
|
|
470
|
-
|
|
573
|
+
Flag indicating if this account is the default for its role.
|
|
471
574
|
balance_type : CharField
|
|
472
|
-
|
|
473
|
-
|
|
575
|
+
Type of balance the account holds, must be either 'debit' or 'credit'.
|
|
474
576
|
locked : BooleanField
|
|
475
|
-
|
|
476
|
-
|
|
577
|
+
Indicates whether the account is locked.
|
|
477
578
|
active : BooleanField
|
|
478
|
-
|
|
479
|
-
|
|
579
|
+
Indicates whether the account is active.
|
|
480
580
|
coa_model : ForeignKey
|
|
481
|
-
|
|
581
|
+
Reference to the associated ChartOfAccountModel.
|
|
482
582
|
"""
|
|
583
|
+
|
|
483
584
|
BALANCE_TYPE = [
|
|
484
585
|
(CREDIT, _('Credit')),
|
|
485
586
|
(DEBIT, _('Debit'))
|
|
@@ -537,30 +638,33 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
537
638
|
active: bool = False,
|
|
538
639
|
**kwargs):
|
|
539
640
|
"""
|
|
540
|
-
|
|
541
|
-
|
|
641
|
+
Create a new AccountModel instance, managing parent/child relationships properly.
|
|
642
|
+
|
|
643
|
+
This convenience method ensures correct creation of new accounts, handling the intricate logic needed for
|
|
644
|
+
maintaining hierarchical relationships between accounts.
|
|
542
645
|
|
|
543
646
|
Parameters
|
|
544
647
|
----------
|
|
545
|
-
name: str
|
|
546
|
-
|
|
547
|
-
role: str
|
|
548
|
-
|
|
549
|
-
balance_type: str
|
|
550
|
-
|
|
551
|
-
is_role_default: bool
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
648
|
+
name : str
|
|
649
|
+
Name of the new account entity.
|
|
650
|
+
role : str
|
|
651
|
+
Role assigned to the account.
|
|
652
|
+
balance_type : str
|
|
653
|
+
Type of balance associated with the account. Must be either 'debit' or 'credit'.
|
|
654
|
+
is_role_default : bool, optional
|
|
655
|
+
Indicates if the account should be the default for its role. Only one default account per role is allowed.
|
|
656
|
+
Defaults to False.
|
|
657
|
+
locked : bool, optional
|
|
658
|
+
Flags the account as locked. Defaults to False.
|
|
659
|
+
active : bool, optional
|
|
660
|
+
Flags the account as active. Defaults to True.
|
|
661
|
+
**kwargs : dict, optional
|
|
662
|
+
Additional attributes for account creation.
|
|
558
663
|
|
|
559
664
|
Returns
|
|
560
665
|
-------
|
|
561
666
|
AccountModel
|
|
562
|
-
The newly created AccountModel instance.
|
|
563
|
-
|
|
667
|
+
The newly created `AccountModel` instance.
|
|
564
668
|
"""
|
|
565
669
|
account_model = cls(
|
|
566
670
|
name=name,
|
|
@@ -578,25 +682,35 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
578
682
|
@property
|
|
579
683
|
def role_bs(self) -> str:
|
|
580
684
|
"""
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
685
|
+
Returns the principal role of the account on the balance sheet.
|
|
686
|
+
|
|
687
|
+
The principal role can be one of the following:
|
|
688
|
+
- 'asset'
|
|
689
|
+
- 'liability'
|
|
690
|
+
- 'equity'
|
|
586
691
|
|
|
587
692
|
Returns
|
|
588
693
|
-------
|
|
589
694
|
str
|
|
590
|
-
A
|
|
695
|
+
A string representing the principal role of the account on the balance sheet.
|
|
591
696
|
"""
|
|
592
697
|
return BS_ROLES.get(self.role)
|
|
593
698
|
|
|
594
699
|
def is_root_account(self):
|
|
700
|
+
"""
|
|
701
|
+
Checks if the current user's role belongs to the ROOT_GROUP.
|
|
702
|
+
|
|
703
|
+
Returns
|
|
704
|
+
-------
|
|
705
|
+
bool
|
|
706
|
+
True if the role is in the ROOT_GROUP, False otherwise
|
|
707
|
+
"""
|
|
595
708
|
return self.role in ROOT_GROUP
|
|
596
709
|
|
|
597
710
|
def is_debit(self) -> bool:
|
|
598
711
|
"""
|
|
599
|
-
Checks if the account has a DEBIT balance.
|
|
712
|
+
Checks if the account has a DEBIT balance type.
|
|
713
|
+
|
|
600
714
|
Returns
|
|
601
715
|
-------
|
|
602
716
|
bool
|
|
@@ -606,7 +720,8 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
606
720
|
|
|
607
721
|
def is_credit(self):
|
|
608
722
|
"""
|
|
609
|
-
Checks if the
|
|
723
|
+
Checks if the Account Model has a CREDIT balance type.
|
|
724
|
+
|
|
610
725
|
Returns
|
|
611
726
|
-------
|
|
612
727
|
bool
|
|
@@ -615,43 +730,156 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
615
730
|
return self.balance_type == CREDIT
|
|
616
731
|
|
|
617
732
|
def is_coa_root(self):
|
|
733
|
+
"""
|
|
734
|
+
Check if the current Account Model role is 'ROOT_COA'.
|
|
735
|
+
|
|
736
|
+
Returns
|
|
737
|
+
-------
|
|
738
|
+
bool
|
|
739
|
+
True if the role is 'ROOT_COA', False otherwise.
|
|
740
|
+
"""
|
|
618
741
|
return self.role == ROOT_COA
|
|
619
742
|
|
|
620
743
|
def is_asset(self) -> bool:
|
|
744
|
+
"""
|
|
745
|
+
Determines if the current Account Model role of the instance is considered an asset.
|
|
746
|
+
|
|
747
|
+
Returns
|
|
748
|
+
-------
|
|
749
|
+
bool
|
|
750
|
+
True if the role is part of the GROUP_ASSETS, False otherwise.
|
|
751
|
+
"""
|
|
621
752
|
return self.role in GROUP_ASSETS
|
|
622
753
|
|
|
623
754
|
def is_liability(self) -> bool:
|
|
755
|
+
"""
|
|
756
|
+
Determines if the current Account Model role is considered a liability.
|
|
757
|
+
|
|
758
|
+
Returns
|
|
759
|
+
-------
|
|
760
|
+
bool
|
|
761
|
+
True if the role is part of GROUP_LIABILITIES, otherwise False.
|
|
762
|
+
"""
|
|
624
763
|
return self.role in GROUP_LIABILITIES
|
|
625
764
|
|
|
626
765
|
def is_capital(self) -> bool:
|
|
766
|
+
"""
|
|
767
|
+
Checks if the current Account Model role is in the capital group.
|
|
768
|
+
|
|
769
|
+
Returns
|
|
770
|
+
-------
|
|
771
|
+
bool
|
|
772
|
+
True if the role is in GROUP_CAPITAL, otherwise False.
|
|
773
|
+
"""
|
|
627
774
|
return self.role in GROUP_CAPITAL
|
|
628
775
|
|
|
629
776
|
def is_income(self) -> bool:
|
|
777
|
+
"""
|
|
778
|
+
Determines whether the current Account Model role belongs to the income group.
|
|
779
|
+
|
|
780
|
+
Parameters
|
|
781
|
+
----------
|
|
782
|
+
self : object
|
|
783
|
+
The instance of the class containing attribute 'role'.
|
|
784
|
+
|
|
785
|
+
Returns
|
|
786
|
+
-------
|
|
787
|
+
bool
|
|
788
|
+
True if the role is in the GROUP_INCOME list, False otherwise.
|
|
789
|
+
"""
|
|
630
790
|
return self.role in GROUP_INCOME
|
|
631
791
|
|
|
632
792
|
def is_cogs(self) -> bool:
|
|
793
|
+
"""
|
|
794
|
+
Determines if the role of the object is part of the GROUP_COGS.
|
|
795
|
+
|
|
796
|
+
Returns
|
|
797
|
+
-------
|
|
798
|
+
bool
|
|
799
|
+
True if the object's role is part of the GROUP_COGS, False otherwise.
|
|
800
|
+
"""
|
|
633
801
|
return self.role in GROUP_COGS
|
|
634
802
|
|
|
635
803
|
def is_expense(self) -> bool:
|
|
804
|
+
"""
|
|
805
|
+
Checks if the current Account Model `role` is categorized under `GROUP_EXPENSES`.
|
|
806
|
+
|
|
807
|
+
Parameters
|
|
808
|
+
----------
|
|
809
|
+
None
|
|
810
|
+
|
|
811
|
+
Returns
|
|
812
|
+
-------
|
|
813
|
+
bool
|
|
814
|
+
True if `role` is in `GROUP_EXPENSES`, otherwise False.
|
|
815
|
+
"""
|
|
636
816
|
return self.role in GROUP_EXPENSES
|
|
637
817
|
|
|
638
818
|
def is_active(self) -> bool:
|
|
819
|
+
"""
|
|
820
|
+
Determines if the current instance is active.
|
|
821
|
+
|
|
822
|
+
Returns
|
|
823
|
+
-------
|
|
824
|
+
bool
|
|
825
|
+
True if the instance is active, otherwise False
|
|
826
|
+
"""
|
|
639
827
|
return self.active is True
|
|
640
828
|
|
|
641
829
|
def is_locked(self) -> bool:
|
|
830
|
+
"""
|
|
831
|
+
Determines if the current object is locked.
|
|
832
|
+
|
|
833
|
+
Returns
|
|
834
|
+
-------
|
|
835
|
+
bool
|
|
836
|
+
True if the object is locked, False otherwise.
|
|
837
|
+
|
|
838
|
+
"""
|
|
642
839
|
return self.locked is True
|
|
643
840
|
|
|
644
841
|
def can_activate(self):
|
|
842
|
+
"""
|
|
843
|
+
Determines if the object can be activated.
|
|
844
|
+
|
|
845
|
+
Returns
|
|
846
|
+
-------
|
|
847
|
+
bool
|
|
848
|
+
True if the object is inactive, otherwise False.
|
|
849
|
+
"""
|
|
645
850
|
return all([
|
|
646
851
|
self.active is False
|
|
647
852
|
])
|
|
648
853
|
|
|
649
854
|
def can_deactivate(self):
|
|
855
|
+
"""
|
|
856
|
+
Determine if the object can be deactivated.
|
|
857
|
+
|
|
858
|
+
Checks if the `active` attribute is set to `True`.
|
|
859
|
+
|
|
860
|
+
Returns
|
|
861
|
+
-------
|
|
862
|
+
bool
|
|
863
|
+
True if the object is currently active and can be deactivated, otherwise False.
|
|
864
|
+
"""
|
|
650
865
|
return all([
|
|
651
866
|
self.active is True
|
|
652
867
|
])
|
|
653
868
|
|
|
654
869
|
def activate(self, commit: bool = True, raise_exception: bool = True, **kwargs):
|
|
870
|
+
"""
|
|
871
|
+
Checks if the Account Model instance can be activated, then Activates the AccountModel instance.
|
|
872
|
+
Raises exception if AccountModel cannot be activated.
|
|
873
|
+
|
|
874
|
+
Parameters
|
|
875
|
+
----------
|
|
876
|
+
commit : bool, optional
|
|
877
|
+
If True, commit the changes to the database by calling the save method.
|
|
878
|
+
raise_exception : bool, optional
|
|
879
|
+
If True, raises an AccountModelValidationError if the account cannot be activated.
|
|
880
|
+
kwargs : dict
|
|
881
|
+
Additional parameters that can be passed for further customization.
|
|
882
|
+
"""
|
|
655
883
|
if not self.can_activate():
|
|
656
884
|
if raise_exception:
|
|
657
885
|
raise AccountModelValidationError(
|
|
@@ -666,6 +894,19 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
666
894
|
])
|
|
667
895
|
|
|
668
896
|
def deactivate(self, commit: bool = True, raise_exception: bool = True, **kwargs):
|
|
897
|
+
"""
|
|
898
|
+
Checks if the Account Model instance can be de-activated, then De-activates the AccountModel instance.
|
|
899
|
+
Raises exception if AccountModel cannot be de-activated.
|
|
900
|
+
|
|
901
|
+
Parameters
|
|
902
|
+
----------
|
|
903
|
+
commit : bool, optional
|
|
904
|
+
If True, commit the changes to the database by calling the save method.
|
|
905
|
+
raise_exception : bool, optional
|
|
906
|
+
If True, raises an AccountModelValidationError if the account cannot be activated.
|
|
907
|
+
kwargs : dict
|
|
908
|
+
Additional parameters that can be passed for further customization.
|
|
909
|
+
"""
|
|
669
910
|
if not self.can_deactivate():
|
|
670
911
|
if raise_exception:
|
|
671
912
|
raise AccountModelValidationError(
|
|
@@ -674,12 +915,26 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
674
915
|
return
|
|
675
916
|
self.active = False
|
|
676
917
|
if commit:
|
|
677
|
-
self.save(
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
918
|
+
self.save(
|
|
919
|
+
update_fields=[
|
|
920
|
+
'active',
|
|
921
|
+
'updated'
|
|
922
|
+
])
|
|
681
923
|
|
|
682
924
|
def can_transact(self) -> bool:
|
|
925
|
+
"""
|
|
926
|
+
Determines if a transaction can be performed based on multiple conditions.
|
|
927
|
+
|
|
928
|
+
Returns
|
|
929
|
+
-------
|
|
930
|
+
bool
|
|
931
|
+
True if all conditions are met, enabling a transaction; False otherwise.
|
|
932
|
+
|
|
933
|
+
Conditions:
|
|
934
|
+
1. The chart of accounts (coa_model) must be active.
|
|
935
|
+
2. The entity must not be locked.
|
|
936
|
+
3. The entity itself must be active.
|
|
937
|
+
"""
|
|
683
938
|
return all([
|
|
684
939
|
self.coa_model.is_active(),
|
|
685
940
|
not self.is_locked(),
|
|
@@ -687,7 +942,25 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
687
942
|
])
|
|
688
943
|
|
|
689
944
|
def get_code_prefix(self) -> str:
|
|
945
|
+
"""
|
|
946
|
+
Returns the code prefix based on the account type.
|
|
690
947
|
|
|
948
|
+
This method determines the account type by calling the respective
|
|
949
|
+
account type methods and returns the corresponding code prefix based on Accounting best practices..
|
|
950
|
+
|
|
951
|
+
Returns
|
|
952
|
+
-------
|
|
953
|
+
str
|
|
954
|
+
The code prefix for the account type. The possible values are:
|
|
955
|
+
'1' for assets, '2' for liabilities, '3' for capital,
|
|
956
|
+
'4' for income, '5' for cost of goods sold (COGS),
|
|
957
|
+
'6' for expenses.
|
|
958
|
+
|
|
959
|
+
Raises
|
|
960
|
+
------
|
|
961
|
+
AccountModelValidationError
|
|
962
|
+
If the account role does not match any of the predefined categories.
|
|
963
|
+
"""
|
|
691
964
|
if self.is_asset():
|
|
692
965
|
return '1'
|
|
693
966
|
elif self.is_liability():
|
|
@@ -703,6 +976,19 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
703
976
|
raise AccountModelValidationError(f'Invalid role match for role {self.role}...')
|
|
704
977
|
|
|
705
978
|
def get_root_role(self) -> str:
|
|
979
|
+
"""
|
|
980
|
+
Returns the root role corresponding to the account type.
|
|
981
|
+
|
|
982
|
+
Returns
|
|
983
|
+
-------
|
|
984
|
+
str
|
|
985
|
+
The root role corresponding to the account type.
|
|
986
|
+
|
|
987
|
+
Raises
|
|
988
|
+
------
|
|
989
|
+
AccountModelValidationError
|
|
990
|
+
If no valid role match is found for the account's role.
|
|
991
|
+
"""
|
|
706
992
|
if self.is_asset():
|
|
707
993
|
return ROOT_ASSETS
|
|
708
994
|
elif self.is_liability():
|
|
@@ -720,10 +1006,23 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
720
1006
|
raise AccountModelValidationError(f'Invalid role match for role {self.role}...')
|
|
721
1007
|
|
|
722
1008
|
def get_account_move_choice_queryset(self):
|
|
1009
|
+
"""
|
|
1010
|
+
Retrieves a filtered queryset of account models that the current Account Model instance
|
|
1011
|
+
can be a child of.
|
|
1012
|
+
|
|
1013
|
+
The queryset is filtered based on the specified role and its hierarchical parent roles.
|
|
1014
|
+
Account models with a UUID matching the current instance's UUID are excluded from the results.
|
|
1015
|
+
|
|
1016
|
+
Returns
|
|
1017
|
+
-------
|
|
1018
|
+
QuerySet
|
|
1019
|
+
A filtered set of account models suitable for moving the current instance under.
|
|
1020
|
+
"""
|
|
723
1021
|
return self.coa_model.accountmodel_set.filter(
|
|
724
1022
|
role__in=[
|
|
725
1023
|
self.role,
|
|
726
|
-
self.get_root_role()
|
|
1024
|
+
self.get_root_role(),
|
|
1025
|
+
*VALID_PARENTS.get(self.role, [])
|
|
727
1026
|
],
|
|
728
1027
|
).exclude(uuid__exact=self.uuid)
|
|
729
1028
|
|
|
@@ -731,12 +1030,41 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
731
1030
|
return BS_BUCKETS[self.get_code_prefix()]
|
|
732
1031
|
|
|
733
1032
|
def is_indented(self):
|
|
1033
|
+
"""
|
|
1034
|
+
Check if the current depth level is greater than 2.
|
|
1035
|
+
|
|
1036
|
+
Returns
|
|
1037
|
+
-------
|
|
1038
|
+
bool
|
|
1039
|
+
True if the depth is greater than 2, False otherwise.
|
|
1040
|
+
"""
|
|
734
1041
|
return self.depth > 2
|
|
735
1042
|
|
|
736
1043
|
def get_html_pixel_indent(self):
|
|
1044
|
+
"""
|
|
1045
|
+
Calculates the pixel indentation for HTML elements based on the depth attribute for UI purposes
|
|
1046
|
+
|
|
1047
|
+
Returns
|
|
1048
|
+
-------
|
|
1049
|
+
str
|
|
1050
|
+
The calculated pixel indentation as a string with 'px' suffix.
|
|
1051
|
+
"""
|
|
737
1052
|
return f'{(self.depth - 2) * 40}px'
|
|
738
1053
|
|
|
739
1054
|
def generate_random_code(self):
|
|
1055
|
+
"""
|
|
1056
|
+
Generates a random code for the account adding a prefix 1-6 depending on account role.
|
|
1057
|
+
|
|
1058
|
+
Raises
|
|
1059
|
+
------
|
|
1060
|
+
AccountModelValidationError
|
|
1061
|
+
If the account role is not assigned before code generation.
|
|
1062
|
+
|
|
1063
|
+
Returns
|
|
1064
|
+
-------
|
|
1065
|
+
str
|
|
1066
|
+
A randomly generated code prefixed with a role-based prefix.
|
|
1067
|
+
"""
|
|
740
1068
|
if not self.role:
|
|
741
1069
|
raise AccountModelValidationError('Must assign account role before generate random code')
|
|
742
1070
|
|
|
@@ -755,7 +1083,6 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
755
1083
|
)
|
|
756
1084
|
|
|
757
1085
|
def clean(self):
|
|
758
|
-
|
|
759
1086
|
if not self.code and DJANGO_LEDGER_ACCOUNT_CODE_GENERATE:
|
|
760
1087
|
self.code = self.generate_random_code()
|
|
761
1088
|
|