django-ledger 0.5.6.3__py3-none-any.whl → 0.5.6.5__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 +3 -3
- django_ledger/admin/entity.py +14 -0
- django_ledger/forms/account.py +14 -5
- django_ledger/forms/coa.py +1 -6
- django_ledger/io/roles.py +25 -9
- django_ledger/migrations/0015_remove_chartofaccountmodel_locked_and_more.py +22 -0
- django_ledger/models/accounts.py +9 -9
- django_ledger/models/coa.py +261 -50
- django_ledger/models/entity.py +43 -28
- django_ledger/templates/django_ledger/account/account_create.html +17 -11
- django_ledger/templates/django_ledger/account/account_list.html +12 -9
- django_ledger/templates/django_ledger/account/account_update.html +18 -16
- django_ledger/templates/django_ledger/account/tags/accounts_table.html +97 -93
- django_ledger/templates/django_ledger/chart_of_accounts/coa_list.html +17 -0
- django_ledger/templates/django_ledger/{code_of_accounts → chart_of_accounts}/coa_update.html +1 -4
- django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html +74 -0
- django_ledger/templates/django_ledger/invoice/invoice_list.html +91 -94
- django_ledger/templatetags/django_ledger.py +1 -1
- django_ledger/tests/base.py +23 -1
- django_ledger/tests/test_accounts.py +16 -0
- django_ledger/tests/test_chart_of_accounts.py +46 -0
- django_ledger/urls/account.py +18 -3
- django_ledger/urls/chart_of_accounts.py +21 -1
- django_ledger/views/account.py +59 -10
- django_ledger/views/coa.py +83 -9
- django_ledger/views/mixins.py +10 -5
- {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/METADATA +1 -1
- {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/RECORD +33 -28
- {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/AUTHORS.md +0 -0
- {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/LICENSE +0 -0
- {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/WHEEL +0 -0
- {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/top_level.txt +0 -0
django_ledger/models/coa.py
CHANGED
|
@@ -31,7 +31,7 @@ from django.contrib.auth import get_user_model
|
|
|
31
31
|
from django.core.exceptions import ValidationError
|
|
32
32
|
from django.db import models
|
|
33
33
|
from django.db.models import Q
|
|
34
|
-
from django.
|
|
34
|
+
from django.urls import reverse
|
|
35
35
|
from django.utils.translation import gettext_lazy as _
|
|
36
36
|
|
|
37
37
|
from django_ledger.io import (ROOT_COA, ROOT_GROUP_LEVEL_2, ROOT_GROUP_META, ROOT_ASSETS,
|
|
@@ -40,7 +40,6 @@ from django_ledger.io import (ROOT_COA, ROOT_GROUP_LEVEL_2, ROOT_GROUP_META, ROO
|
|
|
40
40
|
from django_ledger.models import lazy_loader
|
|
41
41
|
from django_ledger.models.accounts import AccountModel, AccountModelQuerySet
|
|
42
42
|
from django_ledger.models.mixins import CreateUpdateMixIn, SlugNameMixIn
|
|
43
|
-
from django_ledger.settings import logger
|
|
44
43
|
|
|
45
44
|
UserModel = get_user_model()
|
|
46
45
|
|
|
@@ -52,7 +51,9 @@ class ChartOfAccountsModelValidationError(ValidationError):
|
|
|
52
51
|
|
|
53
52
|
|
|
54
53
|
class ChartOfAccountModelQuerySet(models.QuerySet):
|
|
55
|
-
|
|
54
|
+
|
|
55
|
+
def active(self):
|
|
56
|
+
return self.filter(active=True)
|
|
56
57
|
|
|
57
58
|
|
|
58
59
|
class ChartOfAccountModelManager(models.Manager):
|
|
@@ -117,22 +118,10 @@ class ChartOfAccountModelManager(models.Manager):
|
|
|
117
118
|
ChartOfAccountQuerySet
|
|
118
119
|
Returns a ChartOfAccountQuerySet with applied filters.
|
|
119
120
|
"""
|
|
120
|
-
qs = self.
|
|
121
|
+
qs = self.for_user(user_model)
|
|
121
122
|
if isinstance(entity_slug, lazy_loader.get_entity_model()):
|
|
122
|
-
return qs.filter(
|
|
123
|
-
|
|
124
|
-
(
|
|
125
|
-
Q(entity__admin=user_model) |
|
|
126
|
-
Q(entity__managers__in=[user_model])
|
|
127
|
-
)
|
|
128
|
-
)
|
|
129
|
-
return qs.filter(
|
|
130
|
-
Q(entity__slug__iexact=entity_slug) &
|
|
131
|
-
(
|
|
132
|
-
Q(entity__admin=user_model) |
|
|
133
|
-
Q(entity__managers__in=[user_model])
|
|
134
|
-
)
|
|
135
|
-
).select_related('entity')
|
|
123
|
+
return qs.filter(entity=entity_slug).select_related('entity')
|
|
124
|
+
return qs.filter(entity__slug__iexact=entity_slug).select_related('entity')
|
|
136
125
|
|
|
137
126
|
|
|
138
127
|
class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
@@ -150,10 +139,10 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
150
139
|
entity: EntityModel
|
|
151
140
|
The EntityModel associated with this Chart of Accounts.
|
|
152
141
|
|
|
153
|
-
|
|
142
|
+
active: bool
|
|
154
143
|
This determines whether any changes can be done to the Chart of Accounts.
|
|
155
|
-
|
|
156
|
-
Default value is set to False (
|
|
144
|
+
Inactive Chart of Accounts will not be able to be used in new Transactions.
|
|
145
|
+
Default value is set to False (inactive).
|
|
157
146
|
|
|
158
147
|
description: str
|
|
159
148
|
A user generated description for this Chart of Accounts.
|
|
@@ -164,7 +153,7 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
164
153
|
editable=False,
|
|
165
154
|
verbose_name=_('Entity'),
|
|
166
155
|
on_delete=models.CASCADE)
|
|
167
|
-
|
|
156
|
+
active = models.BooleanField(default=True, verbose_name=_('Is Active'))
|
|
168
157
|
description = models.TextField(verbose_name=_('CoA Description'), null=True, blank=True)
|
|
169
158
|
objects = ChartOfAccountModelManager.from_queryset(queryset_class=ChartOfAccountModelQuerySet)()
|
|
170
159
|
|
|
@@ -194,7 +183,7 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
194
183
|
root_account_qs: Optional[AccountModelQuerySet] = None,
|
|
195
184
|
as_queryset: bool = False) -> Union[AccountModelQuerySet, AccountModel]:
|
|
196
185
|
|
|
197
|
-
if not account_model.
|
|
186
|
+
if not account_model.is_root_account():
|
|
198
187
|
|
|
199
188
|
if not root_account_qs:
|
|
200
189
|
root_account_qs = self.get_coa_root_accounts_qs()
|
|
@@ -262,8 +251,9 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
262
251
|
locked=True,
|
|
263
252
|
balance_type=role_meta['balance_type']
|
|
264
253
|
)
|
|
265
|
-
|
|
254
|
+
AccountModel.add_root(instance=root_account)
|
|
266
255
|
|
|
256
|
+
# must retrieve root model after added pero django-treebeard documentation...
|
|
267
257
|
coa_root_account_model = AccountModel.objects.get(uuid__exact=account_pk)
|
|
268
258
|
|
|
269
259
|
for root_role in ROOT_GROUP_LEVEL_2:
|
|
@@ -284,10 +274,15 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
284
274
|
))
|
|
285
275
|
|
|
286
276
|
def is_default(self) -> bool:
|
|
287
|
-
if self.entity_id
|
|
277
|
+
if not self.entity_id:
|
|
278
|
+
return False
|
|
279
|
+
if not self.entity.default_coa_id:
|
|
288
280
|
return False
|
|
289
281
|
return self.entity.default_coa_id == self.uuid
|
|
290
282
|
|
|
283
|
+
def is_active(self) -> bool:
|
|
284
|
+
return self.active is True
|
|
285
|
+
|
|
291
286
|
def validate_account_model_qs(self, account_model_qs: AccountModelQuerySet):
|
|
292
287
|
if not isinstance(account_model_qs, AccountModelQuerySet):
|
|
293
288
|
raise ChartOfAccountsModelValidationError(
|
|
@@ -299,23 +294,72 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
299
294
|
message=f'Invalid root queryset for CoA {self.name}'
|
|
300
295
|
)
|
|
301
296
|
|
|
302
|
-
def
|
|
303
|
-
|
|
297
|
+
def allocate_account(self,
|
|
298
|
+
account_model: AccountModel,
|
|
299
|
+
root_account_qs: Optional[AccountModelQuerySet] = None):
|
|
300
|
+
"""
|
|
301
|
+
Allocates a given account model to the appropriate root account depending on the Account Model Role.
|
|
304
302
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
account_model: AccountModel
|
|
306
|
+
The Account Model to Allocate
|
|
307
|
+
root_account_qs:
|
|
308
|
+
The Root Account QuerySet of the Chart Of Accounts to use.
|
|
309
|
+
Will be validated against current CoA Model.
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
AccountModel
|
|
314
|
+
The saved and allocated AccountModel.
|
|
315
|
+
"""
|
|
309
316
|
|
|
310
|
-
|
|
317
|
+
if account_model.coa_model_id:
|
|
318
|
+
if account_model.coa_model_id != self.uuid:
|
|
319
|
+
raise ChartOfAccountsModelValidationError(
|
|
320
|
+
message=f'Invalid Account Model {account_model} for CoA {self}'
|
|
321
|
+
)
|
|
311
322
|
|
|
312
|
-
|
|
313
|
-
|
|
323
|
+
if not root_account_qs:
|
|
324
|
+
root_account_qs = self.get_coa_root_accounts_qs()
|
|
325
|
+
else:
|
|
326
|
+
self.validate_account_model_qs(root_account_qs)
|
|
327
|
+
|
|
328
|
+
l2_root_node: AccountModel = self.get_coa_l2_root(
|
|
329
|
+
account_model=account_model,
|
|
330
|
+
root_account_qs=root_account_qs
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
account_model.coa_model = self
|
|
334
|
+
l2_root_node.add_child(instance=account_model)
|
|
335
|
+
coa_accounts_qs = self.get_non_root_coa_accounts_qs()
|
|
336
|
+
return coa_accounts_qs.get(uuid__exact=account_model.uuid)
|
|
337
|
+
|
|
338
|
+
def create_account(self,
|
|
339
|
+
code: str,
|
|
340
|
+
role: str,
|
|
341
|
+
name: str,
|
|
342
|
+
balance_type: str,
|
|
343
|
+
active: bool,
|
|
344
|
+
root_account_qs: Optional[AccountModelQuerySet] = None):
|
|
345
|
+
|
|
346
|
+
account_model = AccountModel(
|
|
347
|
+
code=code,
|
|
348
|
+
name=name,
|
|
349
|
+
role=role,
|
|
350
|
+
active=active,
|
|
351
|
+
balance_type=balance_type
|
|
352
|
+
)
|
|
353
|
+
account_model.clean()
|
|
354
|
+
|
|
355
|
+
account_model = self.allocate_account(
|
|
356
|
+
account_model=account_model,
|
|
357
|
+
root_account_qs=root_account_qs
|
|
358
|
+
)
|
|
314
359
|
return account_model
|
|
315
360
|
|
|
316
361
|
# ACTIONS -----
|
|
317
|
-
|
|
318
|
-
# todo: use these methods once multi CoA fetures are enabled...
|
|
362
|
+
# todo: use these methods once multi CoA features are enabled...
|
|
319
363
|
def lock_all_accounts(self) -> AccountModelQuerySet:
|
|
320
364
|
non_root_accounts_qs = self.get_non_root_coa_accounts_qs()
|
|
321
365
|
non_root_accounts_qs.update(locked=True)
|
|
@@ -326,25 +370,192 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
326
370
|
non_root_accounts_qs.update(locked=False)
|
|
327
371
|
return non_root_accounts_qs
|
|
328
372
|
|
|
329
|
-
def
|
|
330
|
-
|
|
373
|
+
def mark_as_default(self, commit: bool = False, raise_exception: bool = False, **kwargs):
|
|
374
|
+
"""
|
|
375
|
+
Marks the current Chart of Accounts instances as default for the EntityModel.
|
|
331
376
|
|
|
377
|
+
Parameters
|
|
378
|
+
----------
|
|
379
|
+
commit: bool
|
|
380
|
+
Commit the action into the Database. Default is False.
|
|
381
|
+
raise_exception: bool
|
|
382
|
+
Raises exception if Chart of Account model instance is already marked as default.
|
|
383
|
+
"""
|
|
384
|
+
if self.is_default():
|
|
385
|
+
if raise_exception:
|
|
386
|
+
raise ChartOfAccountsModelValidationError(
|
|
387
|
+
message=_(f'The Chart of Accounts {self.slug} is already default')
|
|
388
|
+
)
|
|
389
|
+
return
|
|
390
|
+
self.entity.default_coa_id = self.uuid
|
|
391
|
+
self.clean()
|
|
392
|
+
if commit:
|
|
393
|
+
self.entity.save(
|
|
394
|
+
update_fields=[
|
|
395
|
+
'default_coa_id',
|
|
396
|
+
'updated'
|
|
397
|
+
]
|
|
398
|
+
)
|
|
332
399
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
400
|
+
def mark_as_default_url(self) -> str:
|
|
401
|
+
"""
|
|
402
|
+
Returns the URL to mark the current Chart of Accounts instances as Default for the EntityModel.
|
|
403
|
+
|
|
404
|
+
Returns
|
|
405
|
+
-------
|
|
406
|
+
str
|
|
407
|
+
The URL as a String.
|
|
408
|
+
"""
|
|
409
|
+
return reverse(
|
|
410
|
+
viewname='django_ledger:coa-action-mark-as-default',
|
|
411
|
+
kwargs={
|
|
412
|
+
'entity_slug': self.entity.slug,
|
|
413
|
+
'coa_slug': self.slug
|
|
414
|
+
}
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
def can_activate(self) -> bool:
|
|
418
|
+
return self.active is False
|
|
419
|
+
|
|
420
|
+
def can_deactivate(self) -> bool:
|
|
421
|
+
return all([
|
|
422
|
+
self.is_active(),
|
|
423
|
+
not self.is_default()
|
|
424
|
+
])
|
|
425
|
+
|
|
426
|
+
def mark_as_active(self, commit: bool = False, raise_exception: bool = False, **kwargs):
|
|
427
|
+
"""
|
|
428
|
+
Marks the current Chart of Accounts as Active.
|
|
429
|
+
|
|
430
|
+
Parameters
|
|
431
|
+
----------
|
|
432
|
+
commit: bool
|
|
433
|
+
Commit the action into the Database. Default is False.
|
|
434
|
+
raise_exception: bool
|
|
435
|
+
Raises exception if Chart of Account model instance is already active. Default is False.
|
|
436
|
+
"""
|
|
437
|
+
if self.is_active():
|
|
438
|
+
if raise_exception:
|
|
439
|
+
raise ChartOfAccountsModelValidationError(
|
|
440
|
+
message=_('The Chart of Accounts is currently active.')
|
|
441
|
+
)
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
self.active = True
|
|
445
|
+
self.clean()
|
|
446
|
+
if commit:
|
|
447
|
+
self.save(
|
|
448
|
+
update_fields=[
|
|
449
|
+
'active',
|
|
450
|
+
'updated'
|
|
451
|
+
])
|
|
452
|
+
|
|
453
|
+
def mark_as_active_url(self) -> str:
|
|
454
|
+
"""
|
|
455
|
+
Returns the URL to mark the current Chart of Accounts instances as active.
|
|
456
|
+
|
|
457
|
+
Returns
|
|
458
|
+
-------
|
|
459
|
+
str
|
|
460
|
+
The URL as a String.
|
|
461
|
+
"""
|
|
462
|
+
return reverse(
|
|
463
|
+
viewname='django_ledger:coa-action-mark-as-active',
|
|
464
|
+
kwargs={
|
|
465
|
+
'entity_slug': self.entity.slug,
|
|
466
|
+
'coa_slug': self.slug
|
|
467
|
+
}
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
def mark_as_inactive(self, commit: bool = False, raise_exception: bool = False, **kwargs):
|
|
471
|
+
"""
|
|
472
|
+
Marks the current Chart of Accounts as Active.
|
|
337
473
|
|
|
474
|
+
Parameters
|
|
475
|
+
----------
|
|
476
|
+
commit: bool
|
|
477
|
+
Commit the action into the Database. Default is False.
|
|
478
|
+
raise_exception: bool
|
|
479
|
+
Raises exception if Chart of Account model instance is already active. Default is False.
|
|
480
|
+
"""
|
|
481
|
+
if not self.is_active():
|
|
482
|
+
if raise_exception:
|
|
483
|
+
raise ChartOfAccountsModelValidationError(
|
|
484
|
+
message=_('The Chart of Accounts is currently not active.')
|
|
485
|
+
)
|
|
486
|
+
return
|
|
487
|
+
|
|
488
|
+
self.active = False
|
|
489
|
+
self.clean()
|
|
490
|
+
if commit:
|
|
491
|
+
self.save(
|
|
492
|
+
update_fields=[
|
|
493
|
+
'active',
|
|
494
|
+
'updated'
|
|
495
|
+
])
|
|
496
|
+
|
|
497
|
+
def mark_as_inactive_url(self) -> str:
|
|
498
|
+
"""
|
|
499
|
+
Returns the URL to mark the current Chart of Accounts instances as inactive.
|
|
338
500
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
501
|
+
Returns
|
|
502
|
+
-------
|
|
503
|
+
str
|
|
504
|
+
The URL as a String.
|
|
505
|
+
"""
|
|
506
|
+
return reverse(
|
|
507
|
+
viewname='django_ledger:coa-action-mark-as-inactive',
|
|
508
|
+
kwargs={
|
|
509
|
+
'entity_slug': self.entity.slug,
|
|
510
|
+
'coa_slug': self.slug
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
def get_coa_list_url(self):
|
|
515
|
+
return reverse(
|
|
516
|
+
viewname='django_ledger:coa-list',
|
|
517
|
+
kwargs={
|
|
518
|
+
'entity_slug': self.entity.slug
|
|
519
|
+
}
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
def get_absolute_url(self) -> str:
|
|
523
|
+
return reverse(
|
|
524
|
+
viewname='django_ledger:coa-detail',
|
|
525
|
+
kwargs={
|
|
526
|
+
'coa_slug': self.slug,
|
|
527
|
+
'entity_slug': self.entity.slug
|
|
528
|
+
}
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
def get_account_list_url(self):
|
|
532
|
+
return reverse(
|
|
533
|
+
viewname='django_ledger:account-list-coa',
|
|
534
|
+
kwargs={
|
|
535
|
+
'entity_slug': self.entity.slug,
|
|
536
|
+
'coa_slug': self.slug
|
|
537
|
+
}
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
def get_create_coa_account_url(self):
|
|
541
|
+
return reverse(
|
|
542
|
+
viewname='django_ledger:account-create-coa',
|
|
543
|
+
kwargs={
|
|
544
|
+
'coa_slug': self.slug,
|
|
545
|
+
'entity_slug': self.entity.slug
|
|
546
|
+
}
|
|
547
|
+
)
|
|
342
548
|
|
|
549
|
+
def clean(self):
|
|
550
|
+
self.generate_slug()
|
|
343
551
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
552
|
+
if self.is_default() and not self.active:
|
|
553
|
+
raise ChartOfAccountsModelValidationError(
|
|
554
|
+
_('Default Chart of Accounts cannot be deactivated.')
|
|
555
|
+
)
|
|
347
556
|
|
|
348
557
|
|
|
349
|
-
|
|
350
|
-
|
|
558
|
+
class ChartOfAccountModel(ChartOfAccountModelAbstract):
|
|
559
|
+
"""
|
|
560
|
+
Base ChartOfAccounts Model
|
|
561
|
+
"""
|
django_ledger/models/entity.py
CHANGED
|
@@ -990,6 +990,21 @@ class EntityModelAbstract(MP_Node,
|
|
|
990
990
|
raise EntityModelValidationError(f'EntityModel {self.slug} does not have a default CoA')
|
|
991
991
|
return self.default_coa
|
|
992
992
|
|
|
993
|
+
def set_default_coa(self, coa_model: Optional[Union[ChartOfAccountModel, str]], commit: bool = False):
|
|
994
|
+
|
|
995
|
+
# if str, will look up CoA Model by slug...
|
|
996
|
+
if isinstance(coa_model, str):
|
|
997
|
+
coa_model = self.chartofaccountmodel_set.get(slug=coa_model)
|
|
998
|
+
else:
|
|
999
|
+
self.validate_chart_of_accounts_for_entity(coa_model)
|
|
1000
|
+
|
|
1001
|
+
self.default_coa = coa_model
|
|
1002
|
+
if commit:
|
|
1003
|
+
self.save(update_fields=[
|
|
1004
|
+
'default_coa',
|
|
1005
|
+
'updated'
|
|
1006
|
+
])
|
|
1007
|
+
|
|
993
1008
|
def create_chart_of_accounts(self,
|
|
994
1009
|
assign_as_default: bool = False,
|
|
995
1010
|
coa_name: Optional[str] = None,
|
|
@@ -1075,10 +1090,10 @@ class EntityModelAbstract(MP_Node,
|
|
|
1075
1090
|
coa_has_accounts = coa_accounts_qs.not_coa_root().exists()
|
|
1076
1091
|
|
|
1077
1092
|
if not coa_has_accounts or force:
|
|
1078
|
-
|
|
1093
|
+
root_account_qs = coa_accounts_qs.is_coa_root()
|
|
1079
1094
|
|
|
1080
1095
|
root_maps = {
|
|
1081
|
-
|
|
1096
|
+
root_account_qs.get(role__exact=k): [
|
|
1082
1097
|
AccountModel(
|
|
1083
1098
|
code=a['code'],
|
|
1084
1099
|
name=a['name'],
|
|
@@ -1100,12 +1115,14 @@ class EntityModelAbstract(MP_Node,
|
|
|
1100
1115
|
pass
|
|
1101
1116
|
|
|
1102
1117
|
account_model.clean()
|
|
1103
|
-
coa_model.
|
|
1118
|
+
coa_model.allocate_account(account_model, root_account_qs=root_account_qs)
|
|
1104
1119
|
|
|
1105
1120
|
else:
|
|
1106
1121
|
if not ignore_if_default_coa:
|
|
1107
|
-
raise EntityModelValidationError(
|
|
1108
|
-
|
|
1122
|
+
raise EntityModelValidationError(
|
|
1123
|
+
f'Entity {self.name} already has existing accounts. '
|
|
1124
|
+
'Use force=True to bypass this check'
|
|
1125
|
+
)
|
|
1109
1126
|
|
|
1110
1127
|
# Model Validators....
|
|
1111
1128
|
def validate_chart_of_accounts_for_entity(self,
|
|
@@ -1254,19 +1271,12 @@ class EntityModelAbstract(MP_Node,
|
|
|
1254
1271
|
"""
|
|
1255
1272
|
|
|
1256
1273
|
if not coa_model:
|
|
1257
|
-
account_model_qs = self.default_coa.accountmodel_set.all().select_related(
|
|
1274
|
+
account_model_qs = self.default_coa.accountmodel_set.all().select_related(
|
|
1275
|
+
'coa_model', 'coa_model__entity').not_coa_root()
|
|
1258
1276
|
else:
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
if isinstance(coa_model, ChartOfAccountModel):
|
|
1264
|
-
self.validate_chart_of_accounts_for_entity(coa_model=coa_model, raise_exception=True)
|
|
1265
|
-
account_model_qs = coa_model.accountmodel_set.all()
|
|
1266
|
-
if isinstance(coa_model, str):
|
|
1267
|
-
account_model_qs = account_model_qs.filter(coa_model__slug__exact=coa_model)
|
|
1268
|
-
elif isinstance(coa_model, UUID):
|
|
1269
|
-
account_model_qs = account_model_qs.filter(coa_model__uuid__exact=coa_model)
|
|
1277
|
+
self.validate_chart_of_accounts_for_entity(coa_model=coa_model)
|
|
1278
|
+
account_model_qs = coa_model.accountmodel_set.select_related(
|
|
1279
|
+
'coa_model', 'coa_model__entity').not_coa_root()
|
|
1270
1280
|
|
|
1271
1281
|
if active:
|
|
1272
1282
|
account_model_qs = account_model_qs.active()
|
|
@@ -1367,7 +1377,7 @@ class EntityModelAbstract(MP_Node,
|
|
|
1367
1377
|
role: str,
|
|
1368
1378
|
name: str,
|
|
1369
1379
|
balance_type: str,
|
|
1370
|
-
active: bool,
|
|
1380
|
+
active: bool = False,
|
|
1371
1381
|
coa_model: Optional[Union[ChartOfAccountModel, UUID, str]] = None,
|
|
1372
1382
|
raise_exception: bool = True) -> AccountModel:
|
|
1373
1383
|
"""
|
|
@@ -1409,17 +1419,14 @@ class EntityModelAbstract(MP_Node,
|
|
|
1409
1419
|
else:
|
|
1410
1420
|
coa_model = self.default_coa
|
|
1411
1421
|
|
|
1412
|
-
|
|
1422
|
+
return coa_model.create_account(
|
|
1413
1423
|
code=code,
|
|
1414
|
-
name=name,
|
|
1415
1424
|
role=role,
|
|
1416
|
-
|
|
1417
|
-
balance_type=balance_type
|
|
1425
|
+
name=name,
|
|
1426
|
+
balance_type=balance_type,
|
|
1427
|
+
active=active
|
|
1418
1428
|
)
|
|
1419
1429
|
|
|
1420
|
-
account_model.clean()
|
|
1421
|
-
return coa_model.create_account(account_model=account_model)
|
|
1422
|
-
|
|
1423
1430
|
def create_account_by_kwargs(self,
|
|
1424
1431
|
account_model_kwargs: Dict,
|
|
1425
1432
|
coa_model: Optional[Union[ChartOfAccountModel, UUID, str]] = None,
|
|
@@ -1457,9 +1464,9 @@ class EntityModelAbstract(MP_Node,
|
|
|
1457
1464
|
else:
|
|
1458
1465
|
coa_model = self.default_coa
|
|
1459
1466
|
|
|
1460
|
-
account_model = AccountModel(**account_model_kwargs)
|
|
1461
|
-
account_model.clean()
|
|
1462
|
-
return coa_model, coa_model.create_account(
|
|
1467
|
+
# account_model = AccountModel(**account_model_kwargs)
|
|
1468
|
+
# account_model.clean()
|
|
1469
|
+
return coa_model, coa_model.create_account(**account_model_kwargs)
|
|
1463
1470
|
|
|
1464
1471
|
# ### LEDGER MANAGEMENT ####
|
|
1465
1472
|
def get_ledgers(self, posted: bool = True):
|
|
@@ -2962,6 +2969,14 @@ class EntityModelAbstract(MP_Node,
|
|
|
2962
2969
|
'entity_slug': self.slug
|
|
2963
2970
|
})
|
|
2964
2971
|
|
|
2972
|
+
def get_coa_list_url(self) -> str:
|
|
2973
|
+
return reverse(
|
|
2974
|
+
viewname='django_ledger:coa-list',
|
|
2975
|
+
kwargs={
|
|
2976
|
+
'entity_slug': self.slug
|
|
2977
|
+
}
|
|
2978
|
+
)
|
|
2979
|
+
|
|
2965
2980
|
def get_accounts_url(self) -> str:
|
|
2966
2981
|
"""
|
|
2967
2982
|
The EntityModel Code of Accounts llist import URL.
|
|
@@ -4,17 +4,23 @@
|
|
|
4
4
|
{% load django_ledger %}
|
|
5
5
|
|
|
6
6
|
{% block view_content %}
|
|
7
|
-
<div class="
|
|
8
|
-
<div class="
|
|
9
|
-
<div class="
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
<div class="box">
|
|
8
|
+
<div class="columns is-centered is-multiline">
|
|
9
|
+
<div class="column is-12 has-text-centered">
|
|
10
|
+
<h1 class="is-size-2">{% trans 'Create Account' %}</h1>
|
|
11
|
+
<h2 class="is-size-3 has-text-info">{{ coa_model.name }}</h2>
|
|
12
|
+
<p class="is-size-4 has-text-danger is-italic">({{ coa_model.slug }})</p>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="column is-8-tablet is-6-desktop">
|
|
15
|
+
<div class="box">
|
|
16
|
+
<form method="post">
|
|
17
|
+
{% csrf_token %}
|
|
18
|
+
{{ form.as_p }}
|
|
19
|
+
<button type="submit" class="button is-primary is-fullwidth djetler_my_1">Submit</button>
|
|
20
|
+
<a class="button is-dark is-small is-fullwidth"
|
|
21
|
+
href="{% url 'django_ledger:account-list' entity_slug=view.kwargs.entity_slug %}">Back</a>
|
|
22
|
+
</form>
|
|
23
|
+
</div>
|
|
18
24
|
</div>
|
|
19
25
|
</div>
|
|
20
26
|
</div>
|
|
@@ -5,18 +5,21 @@
|
|
|
5
5
|
|
|
6
6
|
{% block view_content %}
|
|
7
7
|
<div class="box">
|
|
8
|
-
<div class="columns">
|
|
9
|
-
<div class="column">
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
<
|
|
13
|
-
class="button is-dark">{% trans 'Back' %}</a>
|
|
8
|
+
<div class="columns is-multiline">
|
|
9
|
+
<div class="column is-12 has-text-centered">
|
|
10
|
+
<h1 class="is-size-3">{% trans 'Accounts List' %}</h1>
|
|
11
|
+
<h2 class="is-size-1 has-text-info">{{ coa_model.name }}</h2>
|
|
12
|
+
<p class="is-italic has-text-danger">({{ coa_model.slug }})</p>
|
|
14
13
|
</div>
|
|
15
|
-
|
|
16
|
-
<div class="columns">
|
|
17
|
-
<div class="column">
|
|
14
|
+
<div class="column is-12">
|
|
18
15
|
{% accounts_table accounts %}
|
|
19
16
|
</div>
|
|
17
|
+
<div class="column is-12 has-text-centered">
|
|
18
|
+
<a href="{{ coa_model.get_create_coa_account_url }}"
|
|
19
|
+
class="button is-success">{% trans 'Create Account' %}</a>
|
|
20
|
+
<a href="{{ coa_model.get_coa_list_url }}"
|
|
21
|
+
class="button is-dark">{% trans 'Back to CoA List' %}</a>
|
|
22
|
+
</div>
|
|
20
23
|
</div>
|
|
21
24
|
</div>
|
|
22
25
|
{% endblock %}
|
|
@@ -4,22 +4,24 @@
|
|
|
4
4
|
{% load django_ledger %}
|
|
5
5
|
|
|
6
6
|
{% block view_content %}
|
|
7
|
-
<div class="
|
|
8
|
-
<div class="
|
|
9
|
-
<div class="
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
<div class="box">
|
|
8
|
+
<div class="columns is-centered is-multiline">
|
|
9
|
+
<div class="column is-12 has-text-centered">
|
|
10
|
+
<h2 class="is-size-3 has-text-info">{{ coa_model.name }}</h2>
|
|
11
|
+
<p class="is-size-4 has-text-danger is-italic">({{ coa_model.slug }})</p>
|
|
12
|
+
<h1 class="is-size-2">{{ account }}</h1>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="column is-8-tablet is-6-desktop">
|
|
15
|
+
<div class="box">
|
|
16
|
+
<form method="post">
|
|
17
|
+
{% csrf_token %}
|
|
18
|
+
{{ form.as_p }}
|
|
19
|
+
<button type="submit" class="button is-primary is-fullwidth djetler_my_1">Submit</button>
|
|
20
|
+
<a class="button is-dark is-small is-fullwidth"
|
|
21
|
+
href="{% url 'django_ledger:account-list' entity_slug=view.kwargs.entity_slug %}">Back</a>
|
|
22
|
+
</form>
|
|
23
|
+
</div>
|
|
19
24
|
</div>
|
|
20
25
|
</div>
|
|
21
26
|
</div>
|
|
22
|
-
{% endblock %}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
{% endblock %}
|