django-ledger 0.5.6.4__py3-none-any.whl → 0.6.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 -1
- django_ledger/admin/entity.py +14 -0
- django_ledger/forms/account.py +13 -4
- django_ledger/io/io_core.py +20 -16
- django_ledger/io/io_digest.py +1 -1
- django_ledger/io/io_middleware.py +2 -4
- django_ledger/models/accounts.py +11 -1
- django_ledger/models/closing_entry.py +4 -4
- django_ledger/models/coa.py +269 -90
- django_ledger/models/entity.py +21 -2
- django_ledger/models/mixins.py +1 -1
- django_ledger/models/transactions.py +209 -111
- django_ledger/models/utils.py +117 -116
- django_ledger/templates/django_ledger/account/account_list.html +2 -2
- django_ledger/templates/django_ledger/account/account_update.html +18 -16
- django_ledger/urls/chart_of_accounts.py +3 -3
- django_ledger/views/account.py +36 -10
- django_ledger/views/coa.py +8 -9
- django_ledger/views/mixins.py +10 -8
- {django_ledger-0.5.6.4.dist-info → django_ledger-0.6.0.dist-info}/METADATA +81 -131
- {django_ledger-0.5.6.4.dist-info → django_ledger-0.6.0.dist-info}/RECORD +25 -26
- django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +0 -28
- {django_ledger-0.5.6.4.dist-info → django_ledger-0.6.0.dist-info}/AUTHORS.md +0 -0
- {django_ledger-0.5.6.4.dist-info → django_ledger-0.6.0.dist-info}/LICENSE +0 -0
- {django_ledger-0.5.6.4.dist-info → django_ledger-0.6.0.dist-info}/WHEEL +0 -0
- {django_ledger-0.5.6.4.dist-info → django_ledger-0.6.0.dist-info}/top_level.txt +0 -0
django_ledger/models/coa.py
CHANGED
|
@@ -9,24 +9,23 @@ Contributions to this module:
|
|
|
9
9
|
Chart Of Accounts
|
|
10
10
|
_________________
|
|
11
11
|
|
|
12
|
-
A Chart of Accounts (CoA) is a collection of
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
Accounts can be used when creating Journal Entries**. No commingling between CoAs is allowed in order to preserve the
|
|
23
|
-
integrity of the Journal Entry.
|
|
12
|
+
A Chart of Accounts (CoA) is a crucial collection of logically grouped accounts within a ChartOfAccountModel,
|
|
13
|
+
forming the backbone of financial statements. The CoA includes various account roles such as cash, accounts receivable,
|
|
14
|
+
expenses, liabilities, and income. For example, the Balance Sheet may have a Fixed Assets heading consisting of
|
|
15
|
+
Tangible and Intangible Assets with multiple accounts like Building, Plant & Equipments, and Machinery under
|
|
16
|
+
tangible assets. Aggregation of individual account balances based on the Chart of Accounts and AccountModel roles is
|
|
17
|
+
essential for preparing Financial Statements.
|
|
18
|
+
|
|
19
|
+
All EntityModel must have a default CoA to create any type of transaction. When no explicit CoA is specified, the
|
|
20
|
+
default behavior is to use the EntityModel default CoA. Only ONE Chart of Accounts can be used when creating
|
|
21
|
+
Journal Entries. No commingling between CoAs is allowed to preserve the integrity of the Journal Entry.
|
|
24
22
|
"""
|
|
25
23
|
from random import choices
|
|
26
24
|
from string import ascii_lowercase, digits
|
|
27
|
-
from typing import Optional, Union
|
|
25
|
+
from typing import Optional, Union, Dict
|
|
28
26
|
from uuid import uuid4
|
|
29
27
|
|
|
28
|
+
from django.apps import apps
|
|
30
29
|
from django.contrib.auth import get_user_model
|
|
31
30
|
from django.core.exceptions import ValidationError
|
|
32
31
|
from django.db import models
|
|
@@ -45,13 +44,20 @@ UserModel = get_user_model()
|
|
|
45
44
|
|
|
46
45
|
SLUG_SUFFIX = ascii_lowercase + digits
|
|
47
46
|
|
|
47
|
+
app_config = apps.get_app_config('django_ledger')
|
|
48
|
+
|
|
48
49
|
|
|
49
50
|
class ChartOfAccountsModelValidationError(ValidationError):
|
|
50
51
|
pass
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
class ChartOfAccountModelQuerySet(models.QuerySet):
|
|
54
|
-
|
|
55
|
+
|
|
56
|
+
def active(self):
|
|
57
|
+
"""
|
|
58
|
+
QuerySet method to retrieve active items.
|
|
59
|
+
"""
|
|
60
|
+
return self.filter(active=True)
|
|
55
61
|
|
|
56
62
|
|
|
57
63
|
class ChartOfAccountModelManager(models.Manager):
|
|
@@ -72,13 +78,8 @@ class ChartOfAccountModelManager(models.Manager):
|
|
|
72
78
|
user_model
|
|
73
79
|
Logged in and authenticated django UserModel instance.
|
|
74
80
|
|
|
75
|
-
Examples
|
|
76
|
-
________
|
|
77
|
-
>>> request_user = self.request.user
|
|
78
|
-
>>> coa_model_qs = ChartOfAccountModel.objects.for_user(user_model=request_user)
|
|
79
|
-
|
|
80
81
|
Returns
|
|
81
|
-
|
|
82
|
+
-------
|
|
82
83
|
ChartOfAccountQuerySet
|
|
83
84
|
Returns a ChartOfAccountQuerySet with applied filters.
|
|
84
85
|
"""
|
|
@@ -104,58 +105,33 @@ class ChartOfAccountModelManager(models.Manager):
|
|
|
104
105
|
user_model
|
|
105
106
|
Logged in and authenticated django UserModel instance.
|
|
106
107
|
|
|
107
|
-
Examples
|
|
108
|
-
________
|
|
109
|
-
|
|
110
|
-
>>> request_user = self.request.user
|
|
111
|
-
>>> slug = self.kwargs['entity_slug'] # may come from request kwargs
|
|
112
|
-
>>> coa_model_qs = ChartOfAccountModelManager.objects.for_entity(user_model=request_user, entity_slug=slug)
|
|
113
|
-
|
|
114
108
|
Returns
|
|
115
|
-
|
|
109
|
+
-------
|
|
116
110
|
ChartOfAccountQuerySet
|
|
117
111
|
Returns a ChartOfAccountQuerySet with applied filters.
|
|
118
112
|
"""
|
|
119
|
-
qs = self.
|
|
113
|
+
qs = self.for_user(user_model)
|
|
120
114
|
if isinstance(entity_slug, lazy_loader.get_entity_model()):
|
|
121
|
-
return qs.filter(
|
|
122
|
-
|
|
123
|
-
(
|
|
124
|
-
Q(entity__admin=user_model) |
|
|
125
|
-
Q(entity__managers__in=[user_model])
|
|
126
|
-
)
|
|
127
|
-
)
|
|
128
|
-
return qs.filter(
|
|
129
|
-
Q(entity__slug__iexact=entity_slug) &
|
|
130
|
-
(
|
|
131
|
-
Q(entity__admin=user_model) |
|
|
132
|
-
Q(entity__managers__in=[user_model])
|
|
133
|
-
)
|
|
134
|
-
).select_related('entity')
|
|
115
|
+
return qs.filter(entity=entity_slug).select_related('entity')
|
|
116
|
+
return qs.filter(entity__slug__iexact=entity_slug).select_related('entity')
|
|
135
117
|
|
|
136
118
|
|
|
137
119
|
class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
138
120
|
"""
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
2. :func:`CreateUpdateMixIn <django_ledger.models.mixins.SlugMixIn>`
|
|
142
|
-
2. :func:`CreateUpdateMixIn <django_ledger.models.mixins.CreateUpdateMixIn>`
|
|
143
|
-
|
|
121
|
+
Abstract base class for the Chart of Account model.
|
|
122
|
+
|
|
144
123
|
Attributes
|
|
145
124
|
----------
|
|
146
|
-
uuid :
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
description: str
|
|
158
|
-
A user generated description for this Chart of Accounts.
|
|
125
|
+
uuid : UUIDField
|
|
126
|
+
UUID field for the chart of account model (primary key).
|
|
127
|
+
entity : ForeignKey
|
|
128
|
+
ForeignKey to the EntityModel.
|
|
129
|
+
active : BooleanField
|
|
130
|
+
BooleanField indicating whether the chart of account is active or not.
|
|
131
|
+
description : TextField
|
|
132
|
+
TextField storing the description of the chart of account.
|
|
133
|
+
objects : ChartOfAccountModelManager
|
|
134
|
+
Manager for the ChartOfAccountModel.
|
|
159
135
|
"""
|
|
160
136
|
|
|
161
137
|
uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
|
|
@@ -182,16 +158,58 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
182
158
|
return self.slug
|
|
183
159
|
|
|
184
160
|
def get_coa_root_accounts_qs(self) -> AccountModelQuerySet:
|
|
161
|
+
"""
|
|
162
|
+
Retrieves the root accounts in the chart of accounts.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
AccountModelQuerySet: A queryset containing the root accounts in the chart of accounts.
|
|
166
|
+
"""
|
|
185
167
|
return self.accountmodel_set.all().is_coa_root()
|
|
186
168
|
|
|
187
|
-
def
|
|
169
|
+
def get_coa_root_node(self) -> AccountModel:
|
|
170
|
+
"""
|
|
171
|
+
Retrieves the root node of the chart of accounts.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
AccountModel: The root node of the chart of accounts.
|
|
175
|
+
|
|
176
|
+
"""
|
|
188
177
|
qs = self.get_coa_root_accounts_qs()
|
|
189
178
|
return qs.get(role__exact=ROOT_COA)
|
|
190
179
|
|
|
191
|
-
def
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
180
|
+
def get_account_root_node(self,
|
|
181
|
+
account_model: AccountModel,
|
|
182
|
+
root_account_qs: Optional[AccountModelQuerySet] = None,
|
|
183
|
+
as_queryset: bool = False) -> AccountModel:
|
|
184
|
+
"""
|
|
185
|
+
Fetches the root node of the ChartOfAccountModel instance. The root node is the highest level of the CoA
|
|
186
|
+
hierarchy. It can be used to traverse the hierarchy of the CoA structure downstream.
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
account_model : AccountModel
|
|
192
|
+
The account model for which to find the root node.
|
|
193
|
+
root_account_qs : Optional[AccountModelQuerySet], optional
|
|
194
|
+
The queryset of root accounts. If not provided, it will be retrieved using `get_coa_root_accounts_qs` method.
|
|
195
|
+
as_queryset : bool, optional
|
|
196
|
+
If True, return the root account queryset instead of a single root account. Default is False.
|
|
197
|
+
|
|
198
|
+
Returns
|
|
199
|
+
-------
|
|
200
|
+
Union[AccountModelQuerySet, AccountModel]
|
|
201
|
+
If `as_queryset` is True, returns the root account queryset. Otherwise, returns a single root account.
|
|
202
|
+
|
|
203
|
+
Raises
|
|
204
|
+
------
|
|
205
|
+
ChartOfAccountsModelValidationError
|
|
206
|
+
If the account model is not part of the chart of accounts.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
if account_model.coa_model_id != self.uuid:
|
|
210
|
+
raise ChartOfAccountsModelValidationError(
|
|
211
|
+
message=_(f'The account model {account_model} is not part of the chart of accounts {self.name}.'),
|
|
212
|
+
)
|
|
195
213
|
|
|
196
214
|
if not account_model.is_root_account():
|
|
197
215
|
|
|
@@ -219,13 +237,72 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
219
237
|
return qs.get()
|
|
220
238
|
|
|
221
239
|
def get_non_root_coa_accounts_qs(self) -> AccountModelQuerySet:
|
|
240
|
+
"""
|
|
241
|
+
Returns a query set of non-root accounts in the chart of accounts.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
AccountModelQuerySet
|
|
246
|
+
A query set of non-root accounts in the chart of accounts.
|
|
247
|
+
"""
|
|
222
248
|
return self.accountmodel_set.all().not_coa_root()
|
|
223
249
|
|
|
224
|
-
def
|
|
225
|
-
|
|
250
|
+
def get_coa_accounts(self, active_only: bool = True) -> AccountModelQuerySet:
|
|
251
|
+
"""
|
|
252
|
+
Returns the AccountModelQuerySet associated with the ChartOfAccounts model instance.
|
|
253
|
+
|
|
254
|
+
Parameters
|
|
255
|
+
----------
|
|
256
|
+
active_only : bool, optional
|
|
257
|
+
Flag to indicate whether to retrieve only active accounts or all accounts.
|
|
258
|
+
Default is True.
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
-------
|
|
262
|
+
AccountModelQuerySet
|
|
263
|
+
A queryset containing accounts from the chart of accounts.
|
|
264
|
+
|
|
265
|
+
"""
|
|
266
|
+
qs = self.get_non_root_coa_accounts_qs()
|
|
267
|
+
if active_only:
|
|
268
|
+
return qs.active()
|
|
269
|
+
return qs
|
|
270
|
+
|
|
271
|
+
def get_coa_account_tree(self) -> Dict:
|
|
272
|
+
"""
|
|
273
|
+
Performs a bulk dump of the ChartOfAccounts model instance accounts to a dictionary.
|
|
274
|
+
The method invokes the`dump_bulk` method on the ChartOfAccount model instance root node.
|
|
275
|
+
See Django Tree Beard documentation for more information.
|
|
276
|
+
|
|
277
|
+
Returns
|
|
278
|
+
-------
|
|
279
|
+
Dict
|
|
280
|
+
A dictionary containing all accounts from the chart of accounts in a nested structure.
|
|
281
|
+
"""
|
|
282
|
+
root_account = self.get_coa_root_node()
|
|
226
283
|
return AccountModel.dump_bulk(parent=root_account)
|
|
227
284
|
|
|
228
285
|
def generate_slug(self, raise_exception: bool = False) -> str:
|
|
286
|
+
"""
|
|
287
|
+
Generates and assigns a slug based on the ChartOfAccounts model instance EntityModel information.
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
Parameters
|
|
291
|
+
----------
|
|
292
|
+
raise_exception : bool, optional
|
|
293
|
+
If set to True, it will raise a ChartOfAccountsModelValidationError if the `self.slug` is already set.
|
|
294
|
+
|
|
295
|
+
Returns
|
|
296
|
+
-------
|
|
297
|
+
str
|
|
298
|
+
The generated slug for the Chart of Accounts.
|
|
299
|
+
|
|
300
|
+
Raises
|
|
301
|
+
------
|
|
302
|
+
ChartOfAccountsModelValidationError
|
|
303
|
+
If `raise_exception` is set to True and `self.slug` is already set.
|
|
304
|
+
|
|
305
|
+
"""
|
|
229
306
|
if self.slug:
|
|
230
307
|
if raise_exception:
|
|
231
308
|
raise ChartOfAccountsModelValidationError(
|
|
@@ -235,7 +312,17 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
235
312
|
self.slug = f'coa-{self.entity.slug[-5:]}-' + ''.join(choices(SLUG_SUFFIX, k=15))
|
|
236
313
|
|
|
237
314
|
def configure(self, raise_exception: bool = True):
|
|
315
|
+
"""
|
|
316
|
+
A method that properly configures the ChartOfAccounts model and creates the appropriate hierarchy boilerplate
|
|
317
|
+
to support the insertion of new accounts into the chart of account model tree.
|
|
318
|
+
This method must be called every time the ChartOfAccounts model is created.
|
|
238
319
|
|
|
320
|
+
Parameters
|
|
321
|
+
----------
|
|
322
|
+
raise_exception : bool, optional
|
|
323
|
+
Whether to raise an exception if root nodes already exist in the Chart of Accounts (default is True).
|
|
324
|
+
This indicates that the ChartOfAccountModel instance is already configured.
|
|
325
|
+
"""
|
|
239
326
|
self.generate_slug()
|
|
240
327
|
|
|
241
328
|
root_accounts_qs = self.get_coa_root_accounts_qs()
|
|
@@ -284,12 +371,44 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
284
371
|
))
|
|
285
372
|
|
|
286
373
|
def is_default(self) -> bool:
|
|
374
|
+
"""
|
|
375
|
+
Check if the ChartOfAccountModel instance is set as the default for the EntityModel.
|
|
376
|
+
|
|
377
|
+
Returns
|
|
378
|
+
-------
|
|
379
|
+
bool
|
|
380
|
+
True if the ChartOfAccountModel instance is set as the default for the EntityModel. Else, False.
|
|
381
|
+
"""
|
|
382
|
+
if not self.entity_id:
|
|
383
|
+
return False
|
|
384
|
+
if not self.entity.default_coa_id:
|
|
385
|
+
return False
|
|
287
386
|
return self.entity.default_coa_id == self.uuid
|
|
288
387
|
|
|
289
388
|
def is_active(self) -> bool:
|
|
389
|
+
"""
|
|
390
|
+
Check if the ChartOfAccountModel instance is active.
|
|
391
|
+
|
|
392
|
+
Returns:
|
|
393
|
+
bool: True if the ChartOfAccountModel instance is active, False otherwise.
|
|
394
|
+
"""
|
|
290
395
|
return self.active is True
|
|
291
396
|
|
|
292
397
|
def validate_account_model_qs(self, account_model_qs: AccountModelQuerySet):
|
|
398
|
+
"""
|
|
399
|
+
Validates the given AccountModelQuerySet for the ChartOfAccountsModel.
|
|
400
|
+
|
|
401
|
+
Parameters
|
|
402
|
+
----------
|
|
403
|
+
account_model_qs : AccountModelQuerySet
|
|
404
|
+
The AccountModelQuerySet to validate.
|
|
405
|
+
|
|
406
|
+
Raises
|
|
407
|
+
------
|
|
408
|
+
ChartOfAccountsModelValidationError
|
|
409
|
+
If the account_model_qs is not an instance of AccountModelQuerySet or if it contains an account model with a different coa_model_id than the current CoA model.
|
|
410
|
+
|
|
411
|
+
"""
|
|
293
412
|
if not isinstance(account_model_qs, AccountModelQuerySet):
|
|
294
413
|
raise ChartOfAccountsModelValidationError(
|
|
295
414
|
message='Must pass an instance of AccountModelQuerySet'
|
|
@@ -300,24 +419,40 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
300
419
|
message=f'Invalid root queryset for CoA {self.name}'
|
|
301
420
|
)
|
|
302
421
|
|
|
303
|
-
def
|
|
304
|
-
|
|
305
|
-
|
|
422
|
+
def insert_account(self,
|
|
423
|
+
account_model: AccountModel,
|
|
424
|
+
root_account_qs: Optional[AccountModelQuerySet] = None):
|
|
306
425
|
"""
|
|
307
|
-
|
|
426
|
+
This method inserts the given account model into the chart of accounts (COA) instance.
|
|
427
|
+
It first verifies if the account model's COA model ID matches the COA's UUID. If not, it
|
|
428
|
+
raises a `ChartOfAccountsModelValidationError`. If the `root_account_qs` is not provided, it retrieves the
|
|
429
|
+
root account query set using the `get_coa_root_accounts_qs` method. Providing a pre-fetched `root_account_qs`
|
|
430
|
+
avoids unnecessary retrieval of the root account query set every an account model is inserted into the CoA.
|
|
431
|
+
|
|
432
|
+
Next, it validates the provided `root_account_qs` if it is not None. Then, it obtains the root node for the
|
|
433
|
+
account model using the `get_account_root_node` method and assigns it to `account_root_node`.
|
|
434
|
+
|
|
435
|
+
Finally, it adds the account model as a child to the `account_root_node` and retrieves the updated COA accounts
|
|
436
|
+
query set using the `get_non_root_coa_accounts_qs` method. It returns the inserted account model found in the
|
|
437
|
+
COA accounts query set.
|
|
308
438
|
|
|
309
439
|
Parameters
|
|
310
440
|
----------
|
|
311
|
-
account_model: AccountModel
|
|
312
|
-
The
|
|
313
|
-
root_account_qs:
|
|
314
|
-
The
|
|
315
|
-
|
|
441
|
+
account_model : AccountModel
|
|
442
|
+
The account model to be inserted into the chart of accounts.
|
|
443
|
+
root_account_qs : Optional[AccountModelQuerySet], default=None
|
|
444
|
+
The root account query set. If not provided, it will be obtained using the `get_coa_root_accounts_qs`
|
|
445
|
+
method.
|
|
316
446
|
|
|
317
447
|
Returns
|
|
318
448
|
-------
|
|
319
449
|
AccountModel
|
|
320
|
-
The
|
|
450
|
+
The inserted account model.
|
|
451
|
+
|
|
452
|
+
Raises
|
|
453
|
+
------
|
|
454
|
+
ChartOfAccountsModelValidationError
|
|
455
|
+
If the provided account model has an invalid COA model ID for the current COA.
|
|
321
456
|
"""
|
|
322
457
|
|
|
323
458
|
if account_model.coa_model_id:
|
|
@@ -331,13 +466,12 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
331
466
|
else:
|
|
332
467
|
self.validate_account_model_qs(root_account_qs)
|
|
333
468
|
|
|
334
|
-
|
|
469
|
+
account_root_node: AccountModel = self.get_account_root_node(
|
|
335
470
|
account_model=account_model,
|
|
336
471
|
root_account_qs=root_account_qs
|
|
337
472
|
)
|
|
338
473
|
|
|
339
|
-
|
|
340
|
-
l2_root_node.add_child(instance=account_model)
|
|
474
|
+
account_root_node.add_child(instance=account_model)
|
|
341
475
|
coa_accounts_qs = self.get_non_root_coa_accounts_qs()
|
|
342
476
|
return coa_accounts_qs.get(uuid__exact=account_model.uuid)
|
|
343
477
|
|
|
@@ -348,17 +482,41 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
348
482
|
balance_type: str,
|
|
349
483
|
active: bool,
|
|
350
484
|
root_account_qs: Optional[AccountModelQuerySet] = None):
|
|
485
|
+
"""
|
|
486
|
+
Proper method for inserting a new Account Model into a CoA.
|
|
487
|
+
Use this in liu of the direct instantiation of the AccountModel of using the django related manager.
|
|
351
488
|
|
|
489
|
+
Parameters
|
|
490
|
+
----------
|
|
491
|
+
code : str
|
|
492
|
+
The code of the account to be created.
|
|
493
|
+
role : str
|
|
494
|
+
The role of the account. This can be a user-defined value.
|
|
495
|
+
name : str
|
|
496
|
+
The name of the account.
|
|
497
|
+
balance_type : str
|
|
498
|
+
The balance type of the account. This can be a user-defined value.
|
|
499
|
+
active : bool
|
|
500
|
+
Specifies whether the account is active or not.
|
|
501
|
+
root_account_qs : Optional[AccountModelQuerySet], optional
|
|
502
|
+
The query set of root accounts to which the created account should be linked. Defaults to None.
|
|
503
|
+
|
|
504
|
+
Returns
|
|
505
|
+
-------
|
|
506
|
+
AccountModel
|
|
507
|
+
The created account model instance.
|
|
508
|
+
"""
|
|
352
509
|
account_model = AccountModel(
|
|
353
510
|
code=code,
|
|
354
511
|
name=name,
|
|
355
512
|
role=role,
|
|
356
513
|
active=active,
|
|
357
|
-
balance_type=balance_type
|
|
514
|
+
balance_type=balance_type,
|
|
515
|
+
coa_model=self
|
|
358
516
|
)
|
|
359
517
|
account_model.clean()
|
|
360
518
|
|
|
361
|
-
account_model = self.
|
|
519
|
+
account_model = self.insert_account(
|
|
362
520
|
account_model=account_model,
|
|
363
521
|
root_account_qs=root_account_qs
|
|
364
522
|
)
|
|
@@ -367,14 +525,14 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
367
525
|
# ACTIONS -----
|
|
368
526
|
# todo: use these methods once multi CoA features are enabled...
|
|
369
527
|
def lock_all_accounts(self) -> AccountModelQuerySet:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
return
|
|
528
|
+
account_qs = self.get_coa_accounts()
|
|
529
|
+
account_qs.update(locked=True)
|
|
530
|
+
return account_qs
|
|
373
531
|
|
|
374
532
|
def unlock_all_accounts(self) -> AccountModelQuerySet:
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return
|
|
533
|
+
account_qs = self.get_non_root_coa_accounts_qs()
|
|
534
|
+
account_qs.update(locked=False)
|
|
535
|
+
return account_qs
|
|
378
536
|
|
|
379
537
|
def mark_as_default(self, commit: bool = False, raise_exception: bool = False, **kwargs):
|
|
380
538
|
"""
|
|
@@ -421,9 +579,23 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
421
579
|
)
|
|
422
580
|
|
|
423
581
|
def can_activate(self) -> bool:
|
|
582
|
+
"""
|
|
583
|
+
Check if the ChartOffAccountModel instance can be activated.
|
|
584
|
+
|
|
585
|
+
Returns
|
|
586
|
+
-------
|
|
587
|
+
True if the object can be activated, False otherwise.
|
|
588
|
+
"""
|
|
424
589
|
return self.active is False
|
|
425
590
|
|
|
426
591
|
def can_deactivate(self) -> bool:
|
|
592
|
+
"""
|
|
593
|
+
Check if the ChartOffAccountModel instance can be deactivated.
|
|
594
|
+
|
|
595
|
+
Returns
|
|
596
|
+
-------
|
|
597
|
+
True if the object can be deactivated, False otherwise.
|
|
598
|
+
"""
|
|
427
599
|
return all([
|
|
428
600
|
self.is_active(),
|
|
429
601
|
not self.is_default()
|
|
@@ -517,6 +689,14 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
517
689
|
}
|
|
518
690
|
)
|
|
519
691
|
|
|
692
|
+
def get_coa_list_url(self):
|
|
693
|
+
return reverse(
|
|
694
|
+
viewname='django_ledger:coa-list',
|
|
695
|
+
kwargs={
|
|
696
|
+
'entity_slug': self.entity.slug
|
|
697
|
+
}
|
|
698
|
+
)
|
|
699
|
+
|
|
520
700
|
def get_absolute_url(self) -> str:
|
|
521
701
|
return reverse(
|
|
522
702
|
viewname='django_ledger:coa-detail',
|
|
@@ -546,7 +726,6 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
546
726
|
|
|
547
727
|
def clean(self):
|
|
548
728
|
self.generate_slug()
|
|
549
|
-
|
|
550
729
|
if self.is_default() and not self.active:
|
|
551
730
|
raise ChartOfAccountsModelValidationError(
|
|
552
731
|
_('Default Chart of Accounts cannot be deactivated.')
|
django_ledger/models/entity.py
CHANGED
|
@@ -1100,7 +1100,7 @@ class EntityModelAbstract(MP_Node,
|
|
|
1100
1100
|
role=a['role'],
|
|
1101
1101
|
balance_type=a['balance_type'],
|
|
1102
1102
|
active=activate_accounts,
|
|
1103
|
-
|
|
1103
|
+
coa_model=coa_model,
|
|
1104
1104
|
) for a in v] for k, v in CHART_OF_ACCOUNTS_ROOT_MAP.items()
|
|
1105
1105
|
}
|
|
1106
1106
|
|
|
@@ -1115,7 +1115,7 @@ class EntityModelAbstract(MP_Node,
|
|
|
1115
1115
|
pass
|
|
1116
1116
|
|
|
1117
1117
|
account_model.clean()
|
|
1118
|
-
coa_model.
|
|
1118
|
+
coa_model.insert_account(account_model, root_account_qs=root_account_qs)
|
|
1119
1119
|
|
|
1120
1120
|
else:
|
|
1121
1121
|
if not ignore_if_default_coa:
|
|
@@ -1124,6 +1124,25 @@ class EntityModelAbstract(MP_Node,
|
|
|
1124
1124
|
'Use force=True to bypass this check'
|
|
1125
1125
|
)
|
|
1126
1126
|
|
|
1127
|
+
def get_coa_model_qs(self, active: bool = True):
|
|
1128
|
+
"""
|
|
1129
|
+
Fetches the current Entity Model instance Chart of Accounts Model Queryset.
|
|
1130
|
+
|
|
1131
|
+
Parameters
|
|
1132
|
+
----------
|
|
1133
|
+
active: bool
|
|
1134
|
+
Returns only active Chart of Account Models. Defaults to True.
|
|
1135
|
+
|
|
1136
|
+
Returns
|
|
1137
|
+
-------
|
|
1138
|
+
ChartOfAccountModelQuerySet
|
|
1139
|
+
"""
|
|
1140
|
+
|
|
1141
|
+
coa_model_qs = self.chartofaccountmodel_set.all()
|
|
1142
|
+
if active:
|
|
1143
|
+
return coa_model_qs.active()
|
|
1144
|
+
return coa_model_qs
|
|
1145
|
+
|
|
1127
1146
|
# Model Validators....
|
|
1128
1147
|
def validate_chart_of_accounts_for_entity(self,
|
|
1129
1148
|
coa_model: ChartOfAccountModel,
|
django_ledger/models/mixins.py
CHANGED
|
@@ -743,7 +743,7 @@ class AccrualMixIn(models.Model):
|
|
|
743
743
|
|
|
744
744
|
if commit:
|
|
745
745
|
JournalEntryModel = lazy_loader.get_journal_entry_model()
|
|
746
|
-
TransactionModel = lazy_loader.
|
|
746
|
+
TransactionModel = lazy_loader.get_txs_model()
|
|
747
747
|
|
|
748
748
|
unit_uuids = list(set(k[1] for k in idx_keys))
|
|
749
749
|
|