django-ledger 0.6.0.1__py3-none-any.whl → 0.6.1__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.
- assets/node_modules/node-gyp/update-gyp.py +0 -0
- django_ledger/__init__.py +1 -1
- django_ledger/admin/ledger.py +3 -1
- django_ledger/io/__init__.py +1 -3
- django_ledger/io/{io_digest.py → io_context.py} +8 -0
- django_ledger/io/io_core.py +19 -6
- django_ledger/io/io_library.py +79 -30
- django_ledger/io/roles.py +80 -86
- django_ledger/migrations/0016_remove_accountmodel_django_ledg_coa_mod_e19964_idx_and_more.py +44 -0
- django_ledger/models/accounts.py +73 -43
- django_ledger/models/bill.py +2 -0
- django_ledger/models/coa.py +2 -0
- django_ledger/models/entity.py +5 -3
- django_ledger/models/invoice.py +2 -1
- django_ledger/models/items.py +4 -2
- django_ledger/models/ledger.py +24 -12
- django_ledger/models/mixins.py +11 -7
- django_ledger/models/transactions.py +4 -35
- django_ledger/settings.py +1 -0
- django_ledger/urls/entity.py +1 -1
- django_ledger/urls/unit.py +1 -1
- django_ledger/views/entity.py +18 -12
- django_ledger/views/ledger.py +0 -1
- django_ledger/views/mixins.py +60 -30
- {django_ledger-0.6.0.1.dist-info → django_ledger-0.6.1.dist-info}/METADATA +10 -8
- {django_ledger-0.6.0.1.dist-info → django_ledger-0.6.1.dist-info}/RECORD +29 -28
- {django_ledger-0.6.0.1.dist-info → django_ledger-0.6.1.dist-info}/top_level.txt +1 -0
- {django_ledger-0.6.0.1.dist-info → django_ledger-0.6.1.dist-info}/AUTHORS.md +0 -0
- {django_ledger-0.6.0.1.dist-info → django_ledger-0.6.1.dist-info}/LICENSE +0 -0
- {django_ledger-0.6.0.1.dist-info → django_ledger-0.6.1.dist-info}/WHEEL +0 -0
django_ledger/models/accounts.py
CHANGED
|
@@ -87,6 +87,26 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
87
87
|
"""
|
|
88
88
|
return self.filter(active=False)
|
|
89
89
|
|
|
90
|
+
def locked(self):
|
|
91
|
+
"""
|
|
92
|
+
Filter locked elements.
|
|
93
|
+
|
|
94
|
+
This method filters the elements based on the `locked` attribute and returns a filtered queryset.
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
A filtered queryset containing the locked elements.
|
|
98
|
+
"""
|
|
99
|
+
return self.filter(locked=True)
|
|
100
|
+
|
|
101
|
+
def unlocked(self):
|
|
102
|
+
"""
|
|
103
|
+
Returns a filtered version of an object, excluding any locked items.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
A filtered version of the object, excluding any locked items.
|
|
107
|
+
"""
|
|
108
|
+
return self.filter(locked=False)
|
|
109
|
+
|
|
90
110
|
def with_roles(self, roles: Union[List, str]):
|
|
91
111
|
"""
|
|
92
112
|
This method is used to make query of accounts with a certain role. For instance, the fixed assets like
|
|
@@ -110,12 +130,25 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
110
130
|
return self.filter(role__in=roles)
|
|
111
131
|
|
|
112
132
|
def expenses(self):
|
|
133
|
+
"""
|
|
134
|
+
Return the expenses filtered by the roles specified in GROUP_EXPENSES.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
QuerySet: A queryset containing the expenses filtered by the GROUP_EXPENSES roles..
|
|
138
|
+
"""
|
|
113
139
|
return self.filter(role__in=GROUP_EXPENSES)
|
|
114
140
|
|
|
115
141
|
def is_coa_root(self):
|
|
142
|
+
"""
|
|
143
|
+
Check if the account model instance is the Chart of Account Root.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
bool: True if the Account is the CoA Root, False otherwise.
|
|
147
|
+
"""
|
|
116
148
|
return self.filter(role__in=ROOT_GROUP)
|
|
117
149
|
|
|
118
150
|
def not_coa_root(self):
|
|
151
|
+
|
|
119
152
|
return self.exclude(role__in=ROOT_GROUP)
|
|
120
153
|
|
|
121
154
|
def for_entity(self, entity_slug, user_model):
|
|
@@ -146,12 +179,16 @@ class AccountModelQuerySet(MP_NodeQuerySet):
|
|
|
146
179
|
def is_role_default(self):
|
|
147
180
|
return self.not_coa_root().filter(role_default=True)
|
|
148
181
|
|
|
182
|
+
def can_transact(self):
|
|
183
|
+
return self.filter(
|
|
184
|
+
Q(locked=False) & Q(active=True)
|
|
185
|
+
)
|
|
186
|
+
|
|
149
187
|
|
|
150
188
|
class AccountModelManager(MP_NodeManager):
|
|
151
189
|
"""
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
function which return all rows of a model.
|
|
190
|
+
AccountModelManager class provides methods to manage and retrieve AccountModel objects.
|
|
191
|
+
It inherits from MP_NodeManager for tree-like model implementation.
|
|
155
192
|
"""
|
|
156
193
|
|
|
157
194
|
def get_queryset(self) -> AccountModelQuerySet:
|
|
@@ -407,49 +444,41 @@ def account_code_validator(value: str):
|
|
|
407
444
|
|
|
408
445
|
|
|
409
446
|
class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
410
|
-
"""
|
|
411
|
-
Django Ledger Base Account Model Abstract. This is the main abstract class which the Account Model database will
|
|
412
|
-
inherit, and it contains the fields/columns/attributes which the said ledger table will have. In addition to the
|
|
413
|
-
attributes mentioned below, it also has the fields/columns/attributes mentioned in the ParentChileMixin & the
|
|
414
|
-
CreateUpdateMixIn. Read about these mixin here.
|
|
447
|
+
""" AccountModelAbstract
|
|
415
448
|
|
|
416
|
-
|
|
449
|
+
Abstract class representing an Account Model.
|
|
417
450
|
|
|
418
451
|
Attributes
|
|
419
452
|
----------
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
coa_model: ChartOfAccountsModel
|
|
451
|
-
Each Accounts must be assigned a ChartOfAccountsModel. By default, one CoA will be created for each entity.
|
|
452
|
-
However, the creating of a new AccountModel must have an explicit assignment of a ChartOfAccountModel.
|
|
453
|
+
BALANCE_TYPE : list
|
|
454
|
+
List of choices for the balance type of the account.
|
|
455
|
+
|
|
456
|
+
uuid : UUIDField
|
|
457
|
+
UUID field representing the primary key of the account.
|
|
458
|
+
|
|
459
|
+
code : CharField
|
|
460
|
+
CharField representing the account code.
|
|
461
|
+
|
|
462
|
+
name : CharField
|
|
463
|
+
CharField representing the account name.
|
|
464
|
+
|
|
465
|
+
role : CharField
|
|
466
|
+
CharField representing the account role.
|
|
467
|
+
|
|
468
|
+
role_default : BooleanField
|
|
469
|
+
BooleanField representing whether the account is a default account for the role.
|
|
470
|
+
|
|
471
|
+
balance_type : CharField
|
|
472
|
+
CharField representing the balance type of the account. Must be 'debit' or 'credit'.
|
|
473
|
+
|
|
474
|
+
locked : BooleanField
|
|
475
|
+
BooleanField representing whether the account is locked.
|
|
476
|
+
|
|
477
|
+
active : BooleanField
|
|
478
|
+
BooleanField representing whether the account is active.
|
|
479
|
+
|
|
480
|
+
coa_model : ForeignKey
|
|
481
|
+
ForeignKey representing the associated ChartOfAccountModel.
|
|
453
482
|
"""
|
|
454
483
|
BALANCE_TYPE = [
|
|
455
484
|
(CREDIT, _('Credit')),
|
|
@@ -485,7 +514,8 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
|
|
|
485
514
|
models.Index(fields=['balance_type']),
|
|
486
515
|
models.Index(fields=['active']),
|
|
487
516
|
models.Index(fields=['locked']),
|
|
488
|
-
models.Index(fields=['coa_model'])
|
|
517
|
+
models.Index(fields=['coa_model', 'code']),
|
|
518
|
+
models.Index(fields=['code'])
|
|
489
519
|
]
|
|
490
520
|
|
|
491
521
|
def __str__(self):
|
django_ledger/models/bill.py
CHANGED
|
@@ -486,6 +486,8 @@ class BillModelAbstract(
|
|
|
486
486
|
|
|
487
487
|
if self.can_generate_bill_number():
|
|
488
488
|
self.generate_bill_number(commit=commit)
|
|
489
|
+
ledger_model.ledger_xid = f'bill-{self.bill_number.lower()}-{str(ledger_model.entity_id)[-5:]}'
|
|
490
|
+
ledger_model.save(update_fields=['ledger_xid'])
|
|
489
491
|
|
|
490
492
|
self.clean()
|
|
491
493
|
|
django_ledger/models/coa.py
CHANGED
|
@@ -460,6 +460,8 @@ class ChartOfAccountModelAbstract(SlugNameMixIn, CreateUpdateMixIn):
|
|
|
460
460
|
raise ChartOfAccountsModelValidationError(
|
|
461
461
|
message=f'Invalid Account Model {account_model} for CoA {self}'
|
|
462
462
|
)
|
|
463
|
+
else:
|
|
464
|
+
account_model.coa_model = self
|
|
463
465
|
|
|
464
466
|
if not root_account_qs:
|
|
465
467
|
root_account_qs = self.get_coa_root_accounts_qs()
|
django_ledger/models/entity.py
CHANGED
|
@@ -113,10 +113,10 @@ class EntityModelManager(MP_NodeManager):
|
|
|
113
113
|
|
|
114
114
|
def get_queryset(self):
|
|
115
115
|
"""Sets the custom queryset as the default."""
|
|
116
|
-
qs = EntityModelQuerySet(self.model).order_by('path')
|
|
116
|
+
qs = EntityModelQuerySet(self.model, using=self._db).order_by('path')
|
|
117
117
|
return qs.order_by('path').select_related('admin', 'default_coa')
|
|
118
118
|
|
|
119
|
-
def for_user(self, user_model):
|
|
119
|
+
def for_user(self, user_model, authorized_superuser: bool = False):
|
|
120
120
|
"""
|
|
121
121
|
This QuerySet guarantees that Users do not access or operate on EntityModels that don't have access to.
|
|
122
122
|
This is the recommended initial QuerySet.
|
|
@@ -125,6 +125,8 @@ class EntityModelManager(MP_NodeManager):
|
|
|
125
125
|
----------
|
|
126
126
|
user_model
|
|
127
127
|
The Django User Model making the request.
|
|
128
|
+
authorized_superuser
|
|
129
|
+
Allows any superuser to access the EntityModel. Default is False.
|
|
128
130
|
|
|
129
131
|
Returns
|
|
130
132
|
-------
|
|
@@ -134,7 +136,7 @@ class EntityModelManager(MP_NodeManager):
|
|
|
134
136
|
2. Is a manager.
|
|
135
137
|
"""
|
|
136
138
|
qs = self.get_queryset()
|
|
137
|
-
if user_model.is_superuser:
|
|
139
|
+
if user_model.is_superuser and authorized_superuser:
|
|
138
140
|
return qs
|
|
139
141
|
return qs.filter(
|
|
140
142
|
Q(admin=user_model) |
|
django_ledger/models/invoice.py
CHANGED
|
@@ -446,10 +446,11 @@ class InvoiceModelAbstract(
|
|
|
446
446
|
|
|
447
447
|
if self.can_generate_invoice_number():
|
|
448
448
|
self.generate_invoice_number(commit=commit)
|
|
449
|
+
ledger_model.ledger_xid=f'invoice-{self.invoice_number.lower()}-{str(ledger_model.entity_id)[-5:]}'
|
|
450
|
+
ledger_model.save(update_fields=['ledger_xid'])
|
|
449
451
|
|
|
450
452
|
self.clean()
|
|
451
453
|
|
|
452
|
-
|
|
453
454
|
if commit:
|
|
454
455
|
self.save()
|
|
455
456
|
|
django_ledger/models/items.py
CHANGED
|
@@ -881,8 +881,6 @@ class ItemTransactionModelManager(models.Manager):
|
|
|
881
881
|
|
|
882
882
|
def for_user(self, user_model):
|
|
883
883
|
qs = self.get_queryset()
|
|
884
|
-
if user_model.is_superuser:
|
|
885
|
-
return qs
|
|
886
884
|
return qs.filter(
|
|
887
885
|
Q(item_model__entity__admin=user_model) |
|
|
888
886
|
Q(item_model__entity__managers__in=[user_model])
|
|
@@ -890,6 +888,10 @@ class ItemTransactionModelManager(models.Manager):
|
|
|
890
888
|
|
|
891
889
|
def for_entity(self, user_model, entity_slug):
|
|
892
890
|
qs = self.for_user(user_model)
|
|
891
|
+
if isinstance(entity_slug, lazy_loader.get_entity_model()):
|
|
892
|
+
return qs.filter(
|
|
893
|
+
Q(item_model__entity=entity_slug)
|
|
894
|
+
)
|
|
893
895
|
return qs.filter(
|
|
894
896
|
Q(item_model__entity__slug__exact=entity_slug)
|
|
895
897
|
)
|
django_ledger/models/ledger.py
CHANGED
|
@@ -184,13 +184,11 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
|
|
|
184
184
|
_WRAPPED_MODEL_KEY = 'wrapped_model'
|
|
185
185
|
uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
|
|
186
186
|
ledger_xid = models.SlugField(allow_unicode=True, max_length=150, null=True, blank=True,
|
|
187
|
-
verbose_name=_('Ledger
|
|
187
|
+
verbose_name=_('Ledger External ID'),
|
|
188
188
|
help_text=_('User Defined Ledger ID'))
|
|
189
189
|
name = models.CharField(max_length=150, null=True, blank=True, verbose_name=_('Ledger Name'))
|
|
190
190
|
|
|
191
|
-
# todo: rename to entity_model...
|
|
192
191
|
entity = models.ForeignKey('django_ledger.EntityModel',
|
|
193
|
-
editable=False,
|
|
194
192
|
on_delete=models.CASCADE,
|
|
195
193
|
verbose_name=_('Ledger Entity'))
|
|
196
194
|
posted = models.BooleanField(default=False, verbose_name=_('Posted Ledger'))
|
|
@@ -212,13 +210,21 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
|
|
|
212
210
|
models.Index(fields=['entity']),
|
|
213
211
|
models.Index(fields=['entity', 'posted']),
|
|
214
212
|
models.Index(fields=['entity', 'locked']),
|
|
213
|
+
models.Index(fields=['entity', 'ledger_xid']),
|
|
214
|
+
models.Index(fields=['ledger_xid']),
|
|
215
215
|
]
|
|
216
216
|
unique_together = [
|
|
217
217
|
('entity', 'ledger_xid')
|
|
218
218
|
]
|
|
219
219
|
|
|
220
220
|
def __str__(self):
|
|
221
|
-
|
|
221
|
+
if self.name is not None:
|
|
222
|
+
ledger_str = f'LedgerModel: {self.name}'
|
|
223
|
+
elif self.ledger_xid is not None:
|
|
224
|
+
ledger_str = f'LedgerModel: {self.ledger_xid}'
|
|
225
|
+
else:
|
|
226
|
+
ledger_str = f'LedgerModel: {self.uuid}'
|
|
227
|
+
return f'{ledger_str} | Posted: {self.posted} | Locked: {self.locked}'
|
|
222
228
|
|
|
223
229
|
def has_wrapped_model_info(self):
|
|
224
230
|
if self.additional_info is not None:
|
|
@@ -516,7 +522,7 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
|
|
|
516
522
|
je_model_qs.bulk_update(objs=je_model_qs, fields=['locked', 'updated'])
|
|
517
523
|
return je_model_qs
|
|
518
524
|
|
|
519
|
-
def unlock(self, commit: bool = False, **kwargs):
|
|
525
|
+
def unlock(self, commit: bool = False, raise_exception: bool = True, **kwargs):
|
|
520
526
|
"""
|
|
521
527
|
Un-locks the LedgerModel.
|
|
522
528
|
|
|
@@ -525,13 +531,19 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
|
|
|
525
531
|
commit: bool
|
|
526
532
|
If True, saves the LedgerModel instance instantly. Defaults to False.
|
|
527
533
|
"""
|
|
528
|
-
if self.can_unlock():
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
534
|
+
if not self.can_unlock():
|
|
535
|
+
if raise_exception:
|
|
536
|
+
raise LedgerModelValidationError(
|
|
537
|
+
message=_(f'Ledger {self.name} cannot be un-locked. UUID: {self.uuid}')
|
|
538
|
+
)
|
|
539
|
+
return
|
|
540
|
+
|
|
541
|
+
self.locked = False
|
|
542
|
+
if commit:
|
|
543
|
+
self.save(update_fields=[
|
|
544
|
+
'locked',
|
|
545
|
+
'updated'
|
|
546
|
+
])
|
|
535
547
|
|
|
536
548
|
def hide(self, commit: bool = False, raise_exception: bool = True, **kwargs):
|
|
537
549
|
if not self.can_hide():
|
django_ledger/models/mixins.py
CHANGED
|
@@ -487,7 +487,8 @@ class AccrualMixIn(models.Model):
|
|
|
487
487
|
if ledger_model.locked:
|
|
488
488
|
if raise_exception:
|
|
489
489
|
raise ValidationError(f'Bill ledger {ledger_model.name} is already locked...')
|
|
490
|
-
|
|
490
|
+
return
|
|
491
|
+
ledger_model.lock(commit, raise_exception=raise_exception)
|
|
491
492
|
|
|
492
493
|
def unlock_ledger(self, commit: bool = False, raise_exception: bool = True, **kwargs):
|
|
493
494
|
"""
|
|
@@ -501,10 +502,11 @@ class AccrualMixIn(models.Model):
|
|
|
501
502
|
If True, raises ValidationError if LedgerModel already locked.
|
|
502
503
|
"""
|
|
503
504
|
ledger_model = self.ledger
|
|
504
|
-
if not ledger_model.
|
|
505
|
+
if not ledger_model.is_locked():
|
|
505
506
|
if raise_exception:
|
|
506
507
|
raise ValidationError(f'Bill ledger {ledger_model.name} is already unlocked...')
|
|
507
|
-
|
|
508
|
+
return
|
|
509
|
+
ledger_model.unlock(commit, raise_exception=raise_exception)
|
|
508
510
|
|
|
509
511
|
# POST/UNPOST Ledger...
|
|
510
512
|
def post_ledger(self, commit: bool = False, raise_exception: bool = True, **kwargs):
|
|
@@ -522,7 +524,8 @@ class AccrualMixIn(models.Model):
|
|
|
522
524
|
if ledger_model.posted:
|
|
523
525
|
if raise_exception:
|
|
524
526
|
raise ValidationError(f'Bill ledger {ledger_model.name} is already posted...')
|
|
525
|
-
|
|
527
|
+
return
|
|
528
|
+
ledger_model.post(commit, raise_exception=raise_exception)
|
|
526
529
|
|
|
527
530
|
def unpost_ledger(self, commit: bool = False, raise_exception: bool = True, **kwargs):
|
|
528
531
|
"""
|
|
@@ -536,13 +539,14 @@ class AccrualMixIn(models.Model):
|
|
|
536
539
|
If True, raises ValidationError if LedgerModel already locked.
|
|
537
540
|
"""
|
|
538
541
|
ledger_model = self.ledger
|
|
539
|
-
if not ledger_model.
|
|
542
|
+
if not ledger_model.is_posted():
|
|
540
543
|
if raise_exception:
|
|
541
544
|
raise ValidationError(f'Bill ledger {ledger_model.name} is not posted...')
|
|
542
|
-
|
|
545
|
+
return
|
|
546
|
+
ledger_model.post(commit, raise_exception=raise_exception)
|
|
543
547
|
|
|
544
548
|
def migrate_state(self,
|
|
545
|
-
# todo: remove usermodel param
|
|
549
|
+
# todo: remove usermodel param...?
|
|
546
550
|
user_model,
|
|
547
551
|
entity_slug: str,
|
|
548
552
|
itemtxs_qs: Optional[QuerySet] = None,
|
|
@@ -24,7 +24,7 @@ from django.contrib.auth import get_user_model
|
|
|
24
24
|
from django.core.exceptions import ValidationError
|
|
25
25
|
from django.core.validators import MinValueValidator
|
|
26
26
|
from django.db import models
|
|
27
|
-
from django.db.models import Q, QuerySet
|
|
27
|
+
from django.db.models import Q, QuerySet, F
|
|
28
28
|
from django.db.models.signals import pre_save
|
|
29
29
|
from django.utils.translation import gettext_lazy as _
|
|
30
30
|
|
|
@@ -49,35 +49,6 @@ class TransactionModelQuerySet(QuerySet):
|
|
|
49
49
|
"""
|
|
50
50
|
A custom QuerySet class for TransactionModels implementing methods to effectively and safely read
|
|
51
51
|
TransactionModels from the database.
|
|
52
|
-
|
|
53
|
-
Methods
|
|
54
|
-
-------
|
|
55
|
-
posted() -> TransactionModelQuerySet:
|
|
56
|
-
Fetches a QuerySet of posted transactions only.
|
|
57
|
-
|
|
58
|
-
for_accounts(account_list: List[str or AccountModel]) -> TransactionModelQuerySet:
|
|
59
|
-
Fetches a QuerySet of TransactionModels which AccountModel has a specific role.
|
|
60
|
-
|
|
61
|
-
for_roles(role_list: Union[str, List[str]]) -> TransactionModelQuerySet:
|
|
62
|
-
Fetches a QuerySet of TransactionModels which AccountModel has a specific role.
|
|
63
|
-
|
|
64
|
-
for_unit(unit_slug: Union[str, EntityUnitModel]) -> TransactionModelQuerySet:
|
|
65
|
-
Fetches a QuerySet of TransactionModels associated with a specific EntityUnitModel.
|
|
66
|
-
|
|
67
|
-
for_activity(activity_list: Union[str, List[str]]) -> TransactionModelQuerySet:
|
|
68
|
-
Fetches a QuerySet of TransactionModels associated with a specific activity or list of activities.
|
|
69
|
-
|
|
70
|
-
to_date(to_date: Union[str, date, datetime]) -> TransactionModelQuerySet:
|
|
71
|
-
Fetches a QuerySet of TransactionModels associated with a maximum date or timestamp filter.
|
|
72
|
-
|
|
73
|
-
from_date(from_date: Union[str, date, datetime]) -> TransactionModelQuerySet:
|
|
74
|
-
Fetches a QuerySet of TransactionModels associated with a minimum date or timestamp filter.
|
|
75
|
-
|
|
76
|
-
not_closing_entry() -> TransactionModelQuerySet:
|
|
77
|
-
Fetches a QuerySet of TransactionModels that are not part of a closing entry.
|
|
78
|
-
|
|
79
|
-
is_closing_entry() -> TransactionModelQuerySet:
|
|
80
|
-
Fetches a QuerySet of TransactionModels that are part of a closing entry.
|
|
81
52
|
"""
|
|
82
53
|
|
|
83
54
|
def posted(self) -> QuerySet:
|
|
@@ -111,7 +82,7 @@ class TransactionModelQuerySet(QuerySet):
|
|
|
111
82
|
TransactionModelQuerySet
|
|
112
83
|
Returns a TransactionModelQuerySet with applied filters.
|
|
113
84
|
"""
|
|
114
|
-
if
|
|
85
|
+
if isinstance(account_list, list) > 0 and isinstance(account_list[0], str):
|
|
115
86
|
return self.filter(account__code__in=account_list)
|
|
116
87
|
return self.filter(account__in=account_list)
|
|
117
88
|
|
|
@@ -238,7 +209,7 @@ class TransactionModelQuerySet(QuerySet):
|
|
|
238
209
|
return self.filter(journal_entry__is_closing_entry=True)
|
|
239
210
|
|
|
240
211
|
|
|
241
|
-
class
|
|
212
|
+
class TransactionModelManager(models.Manager):
|
|
242
213
|
"""
|
|
243
214
|
A manager class for the TransactionModel.
|
|
244
215
|
"""
|
|
@@ -276,8 +247,6 @@ class TransactionModelAdmin(models.Manager):
|
|
|
276
247
|
ledger or the user is one of the managers of the entity associated with the transaction's ledger.
|
|
277
248
|
"""
|
|
278
249
|
qs = self.get_queryset()
|
|
279
|
-
if user_model.is_superuser:
|
|
280
|
-
return qs
|
|
281
250
|
return qs.filter(
|
|
282
251
|
Q(journal_entry__ledger__entity__admin=user_model) |
|
|
283
252
|
Q(journal_entry__ledger__entity__managers__in=[user_model])
|
|
@@ -531,7 +500,7 @@ class TransactionModelAbstract(CreateUpdateMixIn):
|
|
|
531
500
|
verbose_name=_('Tx Description'),
|
|
532
501
|
help_text=_('A description to be included with this individual transaction'))
|
|
533
502
|
|
|
534
|
-
objects =
|
|
503
|
+
objects = TransactionModelManager()
|
|
535
504
|
|
|
536
505
|
class Meta:
|
|
537
506
|
abstract = True
|
django_ledger/settings.py
CHANGED
|
@@ -34,6 +34,7 @@ logger.info(f'Django Ledger GraphQL Enabled: {DJANGO_LEDGER_GRAPHQL_SUPPORT_ENAB
|
|
|
34
34
|
DJANGO_LEDGER_USE_CLOSING_ENTRIES = getattr(settings, 'DJANGO_LEDGER_USE_CLOSING_ENTRIES', True)
|
|
35
35
|
DJANGO_LEDGER_DEFAULT_CLOSING_ENTRY_CACHE_TIMEOUT = getattr(settings,
|
|
36
36
|
'DJANGO_LEDGER_DEFAULT_CLOSING_ENTRY_CACHE_TIMEOUT', 3600)
|
|
37
|
+
DJANGO_LEDGER_AUTHORIZED_SUPERUSER = getattr(settings, 'DJANGO_LEDGER_AUTHORIZED_SUPERUSER', False)
|
|
37
38
|
DJANGO_LEDGER_LOGIN_URL = getattr(settings, 'DJANGO_LEDGER_LOGIN_URL', settings.LOGIN_URL)
|
|
38
39
|
DJANGO_LEDGER_BILL_NUMBER_LENGTH = getattr(settings, 'DJANGO_LEDGER_BILL_NUMBER_LENGTH', 10)
|
|
39
40
|
DJANGO_LEDGER_INVOICE_NUMBER_LENGTH = getattr(settings, 'DJANGO_LEDGER_INVOICE_NUMBER_LENGTH', 10)
|
django_ledger/urls/entity.py
CHANGED
|
@@ -9,7 +9,7 @@ urlpatterns = [
|
|
|
9
9
|
|
|
10
10
|
# DASHBOARD Views...
|
|
11
11
|
path('<slug:entity_slug>/dashboard/',
|
|
12
|
-
views.
|
|
12
|
+
views.EntityModelDetailHandlerView.as_view(),
|
|
13
13
|
name='entity-dashboard'),
|
|
14
14
|
path('<slug:entity_slug>/dashboard/year/<int:year>/',
|
|
15
15
|
views.FiscalYearEntityModelDashboardView.as_view(),
|
django_ledger/urls/unit.py
CHANGED
|
@@ -19,7 +19,7 @@ urlpatterns = [
|
|
|
19
19
|
|
|
20
20
|
# DASHBOARD Views ...
|
|
21
21
|
path('<slug:entity_slug>/dashboard/<slug:unit_slug>/',
|
|
22
|
-
views.
|
|
22
|
+
views.EntityModelDetailHandlerView.as_view(),
|
|
23
23
|
name='unit-dashboard'),
|
|
24
24
|
path('<slug:entity_slug>/dashboard/<slug:unit_slug>/year/<int:year>/',
|
|
25
25
|
views.FiscalYearEntityModelDashboardView.as_view(),
|
django_ledger/views/entity.py
CHANGED
|
@@ -159,9 +159,9 @@ class EntityDeleteView(DjangoLedgerSecurityMixIn, EntityModelModelViewQuerySetMi
|
|
|
159
159
|
|
|
160
160
|
|
|
161
161
|
# DASHBOARD VIEWS START ----
|
|
162
|
-
class
|
|
163
|
-
|
|
164
|
-
|
|
162
|
+
class EntityModelDetailHandlerView(DjangoLedgerSecurityMixIn,
|
|
163
|
+
EntityUnitMixIn,
|
|
164
|
+
RedirectView):
|
|
165
165
|
|
|
166
166
|
def get_redirect_url(self, *args, **kwargs):
|
|
167
167
|
loc_date = get_localdate()
|
|
@@ -182,14 +182,14 @@ class EntityModelDetailView(DjangoLedgerSecurityMixIn,
|
|
|
182
182
|
})
|
|
183
183
|
|
|
184
184
|
|
|
185
|
-
class
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
185
|
+
class EntityModelDetailBaseView(DjangoLedgerSecurityMixIn,
|
|
186
|
+
EntityModelModelViewQuerySetMixIn,
|
|
187
|
+
BaseDateNavigationUrlMixIn,
|
|
188
|
+
UnpaidElementsMixIn,
|
|
189
|
+
EntityUnitMixIn,
|
|
190
|
+
DigestContextMixIn,
|
|
191
|
+
YearlyReportMixIn,
|
|
192
|
+
DetailView):
|
|
193
193
|
context_object_name = 'entity'
|
|
194
194
|
slug_url_kwarg = 'entity_slug'
|
|
195
195
|
template_name = 'django_ledger/entity/entity_dashboard.html'
|
|
@@ -203,7 +203,7 @@ class FiscalYearEntityModelDashboardView(DjangoLedgerSecurityMixIn,
|
|
|
203
203
|
IO_DIGEST_EQUITY = True
|
|
204
204
|
|
|
205
205
|
def get_context_data(self, **kwargs):
|
|
206
|
-
context = super(
|
|
206
|
+
context = super().get_context_data(**kwargs)
|
|
207
207
|
entity_model: EntityModel = self.object
|
|
208
208
|
context['page_title'] = entity_model.name
|
|
209
209
|
context['header_title'] = entity_model.name
|
|
@@ -228,6 +228,12 @@ class FiscalYearEntityModelDashboardView(DjangoLedgerSecurityMixIn,
|
|
|
228
228
|
return context
|
|
229
229
|
|
|
230
230
|
|
|
231
|
+
class FiscalYearEntityModelDashboardView(EntityModelDetailBaseView):
|
|
232
|
+
"""
|
|
233
|
+
Entity Fiscal Year Dashboard View.
|
|
234
|
+
"""
|
|
235
|
+
|
|
236
|
+
|
|
231
237
|
class QuarterlyEntityDashboardView(FiscalYearEntityModelDashboardView, QuarterlyReportMixIn):
|
|
232
238
|
"""
|
|
233
239
|
Entity Quarterly Dashboard View.
|
django_ledger/views/ledger.py
CHANGED
|
@@ -107,7 +107,6 @@ class LedgerModelCreateView(DjangoLedgerSecurityMixIn, LedgerModelModelViewQuery
|
|
|
107
107
|
def form_valid(self, form):
|
|
108
108
|
instance = form.save(commit=False)
|
|
109
109
|
instance.entity = self.AUTHORIZED_ENTITY_MODEL
|
|
110
|
-
self.object = form.save()
|
|
111
110
|
return super().form_valid(form)
|
|
112
111
|
|
|
113
112
|
def get_success_url(self):
|