django-ledger 0.5.5.4__py3-none-any.whl → 0.5.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.

Files changed (52) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/admin/__init__.py +10 -0
  3. django_ledger/admin/coa.py +135 -0
  4. django_ledger/admin/entity.py +199 -0
  5. django_ledger/admin/ledger.py +283 -0
  6. django_ledger/forms/entity.py +4 -12
  7. django_ledger/forms/ledger.py +19 -0
  8. django_ledger/forms/transactions.py +1 -1
  9. django_ledger/io/__init__.py +4 -1
  10. django_ledger/io/{io_mixin.py → io_core.py} +95 -35
  11. django_ledger/io/io_digest.py +15 -0
  12. django_ledger/io/{data_generator.py → io_generator.py} +51 -8
  13. django_ledger/io/io_library.py +317 -0
  14. django_ledger/io/{io_context.py → io_middleware.py} +16 -9
  15. django_ledger/migrations/0001_initial.py +413 -132
  16. django_ledger/migrations/0014_ledgermodel_ledger_xid_and_more.py +22 -0
  17. django_ledger/models/accounts.py +68 -7
  18. django_ledger/models/bank_account.py +12 -11
  19. django_ledger/models/bill.py +5 -9
  20. django_ledger/models/closing_entry.py +14 -14
  21. django_ledger/models/coa.py +60 -18
  22. django_ledger/models/customer.py +5 -11
  23. django_ledger/models/data_import.py +12 -6
  24. django_ledger/models/entity.py +90 -12
  25. django_ledger/models/estimate.py +12 -9
  26. django_ledger/models/invoice.py +10 -4
  27. django_ledger/models/items.py +11 -6
  28. django_ledger/models/journal_entry.py +61 -18
  29. django_ledger/models/ledger.py +90 -24
  30. django_ledger/models/mixins.py +2 -3
  31. django_ledger/models/purchase_order.py +11 -7
  32. django_ledger/models/transactions.py +3 -1
  33. django_ledger/models/unit.py +22 -13
  34. django_ledger/models/vendor.py +12 -11
  35. django_ledger/report/cash_flow_statement.py +1 -1
  36. django_ledger/report/core.py +3 -2
  37. django_ledger/templates/django_ledger/journal_entry/includes/card_journal_entry.html +1 -1
  38. django_ledger/templates/django_ledger/journal_entry/je_list.html +3 -0
  39. django_ledger/templatetags/django_ledger.py +1 -1
  40. django_ledger/tests/base.py +1 -1
  41. django_ledger/tests/test_entity.py +1 -1
  42. django_ledger/urls/ledger.py +3 -0
  43. django_ledger/views/entity.py +9 -3
  44. django_ledger/views/ledger.py +14 -7
  45. django_ledger/views/mixins.py +9 -1
  46. {django_ledger-0.5.5.4.dist-info → django_ledger-0.5.6.0.dist-info}/METADATA +9 -9
  47. {django_ledger-0.5.5.4.dist-info → django_ledger-0.5.6.0.dist-info}/RECORD +51 -46
  48. {django_ledger-0.5.5.4.dist-info → django_ledger-0.5.6.0.dist-info}/top_level.txt +0 -1
  49. django_ledger/admin.py +0 -160
  50. {django_ledger-0.5.5.4.dist-info → django_ledger-0.5.6.0.dist-info}/AUTHORS.md +0 -0
  51. {django_ledger-0.5.5.4.dist-info → django_ledger-0.5.6.0.dist-info}/LICENSE +0 -0
  52. {django_ledger-0.5.5.4.dist-info → django_ledger-0.5.6.0.dist-info}/WHEEL +0 -0
@@ -111,6 +111,15 @@ class EstimateModelManager(models.Manager):
111
111
  A custom defined EstimateModelManager that that implements custom QuerySet methods related to the EstimateModel.
112
112
  """
113
113
 
114
+ def for_user(self, user_model):
115
+ qs = self.get_queryset()
116
+ if user_model.is_superuser:
117
+ return qs
118
+ return qs.filter(
119
+ Q(entity__admin=user_model) |
120
+ Q(entity__managers__in=[user_model])
121
+ )
122
+
114
123
  def for_entity(self, entity_slug: Union[EntityModel, str], user_model):
115
124
  """
116
125
  Fetches a QuerySet of EstimateModels associated with a specific EntityModel & UserModel.
@@ -134,19 +143,13 @@ class EstimateModelManager(models.Manager):
134
143
  EstimateModelQuerySet
135
144
  Returns a EstimateModelQuerySet with applied filters.
136
145
  """
137
- qs = self.get_queryset()
146
+ qs = self.for_user(user_model)
138
147
  if isinstance(entity_slug, EntityModel):
139
148
  return qs.filter(
140
- Q(entity=entity_slug) & (
141
- Q(entity__admin=user_model) |
142
- Q(entity__managers__in=[user_model])
143
- )
149
+ Q(entity=entity_slug)
144
150
  )
145
151
  return qs.filter(
146
- Q(entity__slug__exact=entity_slug) & (
147
- Q(entity__admin=user_model) |
148
- Q(entity__managers__in=[user_model])
149
- )
152
+ Q(entity__slug__exact=entity_slug)
150
153
  )
151
154
 
152
155
 
@@ -179,6 +179,15 @@ class InvoiceModelManager(models.Manager):
179
179
  'ledger__entity'
180
180
  )
181
181
 
182
+ def for_user(self, user_model):
183
+ qs = self.get_queryset()
184
+ if user_model.is_superuser:
185
+ return qs
186
+ return qs.filter(
187
+ Q(ledger__entity__admin=user_model) |
188
+ Q(ledger__entity__managers__in=[user_model])
189
+ )
190
+
182
191
  def for_entity(self, entity_slug, user_model) -> InvoiceModelQuerySet:
183
192
  """
184
193
  Returns a QuerySet of InvoiceModels associated with a specific EntityModel & UserModel.
@@ -196,10 +205,7 @@ class InvoiceModelManager(models.Manager):
196
205
  InvoiceModelQuerySet
197
206
  A Filtered InvoiceModelQuerySet.
198
207
  """
199
- qs = self.get_queryset().filter(
200
- Q(ledger__entity__admin=user_model) |
201
- Q(ledger__entity__managers__in=[user_model])
202
- )
208
+ qs = self.for_user(user_model)
203
209
  if isinstance(entity_slug, EntityModel):
204
210
  return qs.filter(ledger__entity=entity_slug)
205
211
  elif isinstance(entity_slug, str):
@@ -879,14 +879,19 @@ class ItemTransactionModelQuerySet(models.QuerySet):
879
879
 
880
880
  class ItemTransactionModelManager(models.Manager):
881
881
 
882
- def for_entity(self, user_model, entity_slug):
882
+ def for_user(self, user_model):
883
883
  qs = self.get_queryset()
884
+ if user_model.is_superuser:
885
+ return qs
884
886
  return qs.filter(
885
- Q(item_model__entity__slug__exact=entity_slug) &
886
- (
887
- Q(item_model__entity__admin=user_model) |
888
- Q(item_model__entity__managers__in=[user_model])
889
- )
887
+ Q(item_model__entity__admin=user_model) |
888
+ Q(item_model__entity__managers__in=[user_model])
889
+ )
890
+
891
+ def for_entity(self, user_model, entity_slug):
892
+ qs = self.for_user(user_model)
893
+ return qs.filter(
894
+ Q(item_model__entity__slug__exact=entity_slug)
890
895
  )
891
896
 
892
897
  def for_bill(self, user_model, entity_slug, bill_pk):
@@ -138,6 +138,15 @@ class JournalEntryModelManager(models.Manager):
138
138
  EntityModel and authenticated UserModel.
139
139
  """
140
140
 
141
+ def for_user(self, user_model):
142
+ qs = self.get_queryset()
143
+ if user_model.is_superuser:
144
+ return qs
145
+ return qs.filter(
146
+ Q(ledger__entity__admin=user_model) |
147
+ Q(ledger__entity__managers__in=[user_model])
148
+ )
149
+
141
150
  def for_entity(self, entity_slug, user_model):
142
151
  """
143
152
  Fetches a QuerySet of JournalEntryModels associated with a specific EntityModel & UserModel.
@@ -161,22 +170,13 @@ class JournalEntryModelManager(models.Manager):
161
170
  JournalEntryModelQuerySet
162
171
  Returns a JournalEntryModelQuerySet with applied filters.
163
172
  """
173
+ qs = self.for_user(user_model)
164
174
  if isinstance(entity_slug, lazy_loader.get_entity_model()):
165
- return self.get_queryset().filter(
166
- Q(ledger__entity=entity_slug) &
167
- (
168
- Q(ledger__entity__admin=user_model) |
169
- Q(ledger__entity__managers__in=[user_model])
170
- )
171
-
175
+ return qs.filter(
176
+ Q(ledger__entity=entity_slug)
172
177
  )
173
178
  return self.get_queryset().filter(
174
- Q(ledger__entity__slug__iexact=entity_slug) &
175
- (
176
- Q(ledger__entity__admin=user_model) |
177
- Q(ledger__entity__managers__in=[user_model])
178
- )
179
-
179
+ Q(ledger__entity__slug__iexact=entity_slug)
180
180
  )
181
181
 
182
182
  def for_ledger(self, ledger_pk: Union[str, UUID], entity_slug, user_model):
@@ -345,8 +345,18 @@ class JournalEntryModelAbstract(CreateUpdateMixIn):
345
345
 
346
346
  def __str__(self):
347
347
  if self.je_number:
348
- return 'JE: {x1} - Desc: {x2}'.format(x1=self.je_number, x2=self.description)
349
- return 'JE ID: {x1} - Desc: {x2}'.format(x1=self.pk, x2=self.description)
348
+ return 'JE: {x1} (posted={p}, locked={l}) - Desc: {x2}'.format(
349
+ x1=self.je_number,
350
+ x2=self.description,
351
+ p=self.posted,
352
+ l=self.locked
353
+ )
354
+ return 'JE ID: {x1} (posted={p}, locked={l}) - Desc: {x2}'.format(
355
+ x1=self.pk,
356
+ x2=self.description,
357
+ p=self.posted,
358
+ l=self.locked
359
+ )
350
360
 
351
361
  def __init__(self, *args, **kwargs):
352
362
  super().__init__(*args, **kwargs)
@@ -1011,7 +1021,7 @@ class JournalEntryModelAbstract(CreateUpdateMixIn):
1011
1021
  """
1012
1022
  if self.can_generate_je_number():
1013
1023
 
1014
- with transaction.atomic(durable=True):
1024
+ with transaction.atomic():
1015
1025
 
1016
1026
  state_model = None
1017
1027
  while not state_model:
@@ -1198,12 +1208,45 @@ class JournalEntryModelAbstract(CreateUpdateMixIn):
1198
1208
 
1199
1209
  return reverse('django_ledger:je-detail',
1200
1210
  kwargs={
1201
- 'je_pk': self.id,
1211
+ 'je_pk': self.uuid,
1202
1212
  'ledger_pk': self.ledger_id,
1203
- # pylint: disable=no-member
1204
1213
  'entity_slug': self.ledger.entity.slug
1205
1214
  })
1206
1215
 
1216
+ def get_detail_url(self) -> str:
1217
+ """
1218
+ Determines the update URL of the LedgerModel instance.
1219
+ Results in additional Database query if entity field is not selected in QuerySet.
1220
+
1221
+ Returns
1222
+ -------
1223
+ str
1224
+ URL as a string.
1225
+ """
1226
+ return reverse('django_ledger:je-detail',
1227
+ kwargs={
1228
+ 'entity_slug': self.ledger.entity.slug,
1229
+ 'ledger_pk': self.ledger_id,
1230
+ 'je_pk': self.uuid
1231
+ })
1232
+
1233
+ def get_detail_txs_url(self) -> str:
1234
+ """
1235
+ Determines the update URL of the LedgerModel instance.
1236
+ Results in additional Database query if entity field is not selected in QuerySet.
1237
+
1238
+ Returns
1239
+ -------
1240
+ str
1241
+ URL as a string.
1242
+ """
1243
+ return reverse('django_ledger:je-detail-txs',
1244
+ kwargs={
1245
+ 'entity_slug': self.ledger.entity.slug,
1246
+ 'ledger_pk': self.ledger_id,
1247
+ 'je_pk': self.uuid
1248
+ })
1249
+
1207
1250
  def get_unlock_url(self):
1208
1251
  return reverse('django_ledger:je-mark-as-unlocked',
1209
1252
  kwargs={
@@ -38,7 +38,7 @@ from django.db.models import Q, Min, F, Count
38
38
  from django.urls import reverse
39
39
  from django.utils.translation import gettext_lazy as _
40
40
 
41
- from django_ledger.io import IOMixIn
41
+ from django_ledger.io.io_core import IOMixIn
42
42
  from django_ledger.models import lazy_loader
43
43
  from django_ledger.models.mixins import CreateUpdateMixIn
44
44
 
@@ -54,6 +54,28 @@ class LedgerModelQuerySet(models.QuerySet):
54
54
  Custom defined LedgerModel QuerySet.
55
55
  """
56
56
 
57
+ def locked(self):
58
+ """
59
+ Filters the QuerySet to only locked LedgerModel.
60
+
61
+ Returns
62
+ -------
63
+ LedgerModelQuerySet
64
+ A QuerySet with applied filters.
65
+ """
66
+ return self.filter(locked=True)
67
+
68
+ def unlocked(self):
69
+ """
70
+ Filters the QuerySet to only un-locked LedgerModel.
71
+
72
+ Returns
73
+ -------
74
+ LedgerModelQuerySet
75
+ A QuerySet with applied filters.
76
+ """
77
+ return self.filter(locked=False)
78
+
57
79
  def posted(self):
58
80
  """
59
81
  Filters the QuerySet to only posted LedgerModel.
@@ -65,6 +87,17 @@ class LedgerModelQuerySet(models.QuerySet):
65
87
  """
66
88
  return self.filter(posted=True)
67
89
 
90
+ def unposted(self):
91
+ """
92
+ Filters the QuerySet to only un-posted LedgerModel.
93
+
94
+ Returns
95
+ -------
96
+ LedgerModelQuerySet
97
+ A QuerySet with applied filters.
98
+ """
99
+ return self.filter(posted=True)
100
+
68
101
  def hidden(self):
69
102
  return self.filter(hidden=True)
70
103
 
@@ -73,7 +106,8 @@ class LedgerModelQuerySet(models.QuerySet):
73
106
 
74
107
  def current(self):
75
108
  return self.filter(
76
- earliest_timestamp__gt=F('entity__last_closing_date')
109
+ Q(earliest_timestamp__gt=F('entity__last_closing_date'))
110
+ | Q(earliest_timestamp__isnull=True)
77
111
  )
78
112
 
79
113
 
@@ -90,6 +124,15 @@ class LedgerModelManager(models.Manager):
90
124
  filter=Q(journal_entries__posted=True)),
91
125
  )
92
126
 
127
+ def for_user(self, user_model):
128
+ qs = self.get_queryset()
129
+ if user_model.is_superuser:
130
+ return qs
131
+ return qs.filter(
132
+ Q(entity__admin=user_model) |
133
+ Q(entity__managers__in=[user_model])
134
+ )
135
+
93
136
  def for_entity(self, entity_slug, user_model):
94
137
  """
95
138
  Returns a QuerySet of LedgerModels associated with a specific EntityModel & UserModel.
@@ -107,21 +150,13 @@ class LedgerModelManager(models.Manager):
107
150
  LedgerModelQuerySet
108
151
  A Filtered LedgerModelQuerySet.
109
152
  """
110
- qs = self.get_queryset()
153
+ qs = self.for_user(user_model)
111
154
  if isinstance(entity_slug, lazy_loader.get_entity_model()):
112
155
  return qs.filter(
113
- Q(entity=entity_slug) &
114
- (
115
- Q(entity__admin=user_model) |
116
- Q(entity__managers__in=[user_model])
117
- )
156
+ Q(entity=entity_slug)
118
157
  )
119
158
  return qs.filter(
120
- Q(entity__slug__exact=entity_slug) &
121
- (
122
- Q(entity__admin=user_model) |
123
- Q(entity__managers__in=[user_model])
124
- )
159
+ Q(entity__slug__exact=entity_slug)
125
160
  )
126
161
 
127
162
 
@@ -135,6 +170,8 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
135
170
  This is a unique primary key generated for the table. The default value of this field is uuid4().
136
171
  name: str
137
172
  Human-readable name of the LedgerModel. Maximum 150 characters.
173
+ ledger_xid: str
174
+ A unique user-defined identifier for the LedgerModel. Unique for the Entity Model.
138
175
  entity: EntityModel
139
176
  The EntityModel associated with the LedgerModel instance.
140
177
  posted: bool
@@ -146,6 +183,9 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
146
183
  """
147
184
  _WRAPPED_MODEL_KEY = 'wrapped_model'
148
185
  uuid = models.UUIDField(default=uuid4, editable=False, primary_key=True)
186
+ ledger_xid = models.SlugField(allow_unicode=True, max_length=150, null=True, blank=True,
187
+ verbose_name=_('Ledger Slug'),
188
+ help_text=_('User Defined Ledger ID'))
149
189
  name = models.CharField(max_length=150, null=True, blank=True, verbose_name=_('Ledger Name'))
150
190
 
151
191
  # todo: rename to entity_model...
@@ -173,6 +213,9 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
173
213
  models.Index(fields=['entity', 'posted']),
174
214
  models.Index(fields=['entity', 'locked']),
175
215
  ]
216
+ unique_together = [
217
+ ('entity', 'ledger_xid')
218
+ ]
176
219
 
177
220
  def __str__(self):
178
221
  return self.name
@@ -192,6 +235,7 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
192
235
  return getattr(self, model_id)
193
236
  except ObjectDoesNotExist:
194
237
  pass
238
+ return False
195
239
 
196
240
  def remove_wrapped_model_info(self):
197
241
  if self.has_wrapped_model_info():
@@ -375,7 +419,7 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
375
419
  if not self.can_post():
376
420
  if raise_exception:
377
421
  raise LedgerModelValidationError(
378
- message=_(f'Ledger {self.uuid} cannot be posted.')
422
+ message=_(f'Ledger {self.name} cannot be posted. UUID: {self.uuid}')
379
423
  )
380
424
  return
381
425
  self.posted = True
@@ -417,7 +461,7 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
417
461
  'updated'
418
462
  ])
419
463
 
420
- def lock(self, commit: bool = False, **kwargs):
464
+ def lock(self, commit: bool = False, raise_exception: bool = True, **kwargs):
421
465
  """
422
466
  Locks the LedgerModel.
423
467
 
@@ -425,14 +469,22 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
425
469
  ----------
426
470
  commit: bool
427
471
  If True, saves the LedgerModel instance instantly. Defaults to False.
472
+ raise_exception: bool
473
+ Raises LedgerModelValidationError if locking not allowed.
428
474
  """
429
- if self.can_lock():
430
- self.locked = True
431
- if commit:
432
- self.save(update_fields=[
433
- 'locked',
434
- 'updated'
435
- ])
475
+
476
+ if not self.can_lock():
477
+ if raise_exception:
478
+ raise LedgerModelValidationError(
479
+ message=_(f'Ledger {self.name} cannot be locked. UUID: {self.uuid}')
480
+ )
481
+ return
482
+ self.locked = True
483
+ if commit:
484
+ self.save(update_fields=[
485
+ 'locked',
486
+ 'updated'
487
+ ])
436
488
 
437
489
  def lock_journal_entries(self, commit: bool = True, **kwargs):
438
490
  je_model_qs = self.journal_entries.unlocked()
@@ -493,9 +545,8 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
493
545
  str
494
546
  URL as a string.
495
547
  """
496
- return reverse('django_ledger:ledger-detail',
548
+ return reverse(viewname='django_ledger:je-list',
497
549
  kwargs={
498
- # pylint: disable=no-member
499
550
  'entity_slug': self.entity.slug,
500
551
  'ledger_pk': self.uuid
501
552
  })
@@ -531,6 +582,21 @@ class LedgerModelAbstract(CreateUpdateMixIn, IOMixIn):
531
582
  'ledger_pk': self.uuid
532
583
  })
533
584
 
585
+ def get_list_url(self) -> str:
586
+ """
587
+ Determines the list URL of the LedgerModel instances.
588
+ Results in additional Database query if entity field is not selected in QuerySet.
589
+
590
+ Returns
591
+ -------
592
+ str
593
+ URL as a string.
594
+ """
595
+ return reverse('django_ledger:ledger-list',
596
+ kwargs={
597
+ 'entity_slug': self.entity.slug
598
+ })
599
+
534
600
  def get_balance_sheet_url(self):
535
601
  return reverse(
536
602
  viewname='django_ledger:ledger-bs',
@@ -13,7 +13,7 @@ from collections import defaultdict
13
13
  from datetime import timedelta, date, datetime
14
14
  from decimal import Decimal
15
15
  from itertools import groupby
16
- from typing import Optional, Union, Dict, List
16
+ from typing import Optional, Union, Dict
17
17
  from uuid import UUID
18
18
 
19
19
  from django.conf import settings
@@ -27,8 +27,7 @@ from django.utils.timezone import localdate, localtime
27
27
  from django.utils.translation import gettext_lazy as _
28
28
  from markdown import markdown
29
29
 
30
- from django_ledger.io import (check_tx_balance, ASSET_CA_CASH, ASSET_CA_PREPAID, LIABILITY_CL_DEFERRED_REVENUE,
31
- validate_io_date)
30
+ from django_ledger.io.io_core import validate_io_date, check_tx_balance
32
31
  from django_ledger.models.utils import lazy_loader
33
32
 
34
33
  logging.basicConfig(format='%(asctime)s %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
@@ -101,6 +101,15 @@ class PurchaseOrderModelManager(models.Manager):
101
101
  A custom defined PurchaseOrderModel Manager.
102
102
  """
103
103
 
104
+ def for_user(self, user_model):
105
+ qs = self.get_queryset()
106
+ if user_model.is_superuser:
107
+ return qs
108
+ return qs.filter(
109
+ Q(entity__admin=user_model) |
110
+ Q(entity__managers__in=[user_model])
111
+ )
112
+
104
113
  def for_entity(self, entity_slug, user_model) -> PurchaseOrderModelQuerySet:
105
114
  """
106
115
  Fetches a QuerySet of PurchaseOrderModel associated with a specific EntityModel & UserModel.
@@ -111,15 +120,10 @@ class PurchaseOrderModelManager(models.Manager):
111
120
  PurchaseOrderModelQuerySet
112
121
  A PurchaseOrderModelQuerySet with applied filters.
113
122
  """
114
- qs = self.get_queryset()
123
+ qs = self.for_user(user_model)
115
124
  if isinstance(entity_slug, EntityModel):
116
125
  qs = qs.filter(entity=entity_slug)
117
- elif isinstance(entity_slug, str):
118
- qs = qs.filter(entity__slug__exact=entity_slug)
119
- return qs.filter(
120
- Q(entity__admin=user_model) |
121
- Q(entity__managers__in=[user_model])
122
- )
126
+ return qs.filter(entity__slug__exact=entity_slug)
123
127
 
124
128
 
125
129
  class PurchaseOrderModelAbstract(CreateUpdateMixIn,
@@ -26,7 +26,7 @@ from django.db import models
26
26
  from django.db.models import Q, QuerySet
27
27
  from django.utils.translation import gettext_lazy as _
28
28
 
29
- from django_ledger.io import validate_io_date
29
+ from django_ledger.io.io_core import validate_io_date
30
30
  from django_ledger.models.accounts import AccountModel
31
31
  from django_ledger.models.bill import BillModel
32
32
  from django_ledger.models.entity import EntityModel
@@ -220,6 +220,8 @@ class TransactionModelAdmin(models.Manager):
220
220
  Returns a TransactionModelQuerySet with applied filters.
221
221
  """
222
222
  qs = self.get_queryset()
223
+ if user_model.is_superuser:
224
+ return qs
223
225
  return qs.filter(
224
226
  Q(journal_entry__ledger__entity__admin=user_model) |
225
227
  Q(journal_entry__ledger__entity__managers__in=[user_model])
@@ -34,7 +34,7 @@ from django.utils.text import slugify
34
34
  from django.utils.translation import gettext_lazy as _
35
35
  from treebeard.mp_tree import MP_Node, MP_NodeManager, MP_NodeQuerySet
36
36
 
37
- from django_ledger.io.io_mixin import IOMixIn
37
+ from django_ledger.io.io_core import IOMixIn
38
38
  from django_ledger.models import lazy_loader
39
39
  from django_ledger.models.mixins import CreateUpdateMixIn, SlugNameMixIn
40
40
 
@@ -53,6 +53,15 @@ class EntityUnitModelQuerySet(MP_NodeQuerySet):
53
53
 
54
54
  class EntityUnitModelManager(MP_NodeManager):
55
55
 
56
+ def for_user(self, user_model):
57
+ qs = self.get_queryset()
58
+ if user_model.is_superuser:
59
+ return qs
60
+ return qs.filter(
61
+ Q(entity__admin=user_model) |
62
+ Q(entity__managers__in=[user_model])
63
+ )
64
+
56
65
  def for_entity(self, entity_slug: str, user_model):
57
66
  """
58
67
  Fetches a QuerySet of EntityUnitModels associated with a specific EntityModel & UserModel.
@@ -76,23 +85,14 @@ class EntityUnitModelManager(MP_NodeManager):
76
85
  EntityUnitModelQuerySet
77
86
  Returns a EntityUnitModelQuerySet with applied filters.
78
87
  """
79
- qs = self.get_queryset()
88
+ qs = self.for_user(user_model)
80
89
  if isinstance(entity_slug, lazy_loader.get_entity_model()):
81
90
  return qs.filter(
82
- Q(entity=entity_slug) &
83
- (
84
- Q(entity__admin=user_model) |
85
- Q(entity__managers__in=[user_model])
86
- )
91
+ Q(entity=entity_slug)
87
92
 
88
93
  )
89
94
  return qs.filter(
90
- Q(entity__slug__exact=entity_slug) &
91
- (
92
- Q(entity__admin=user_model) |
93
- Q(entity__managers__in=[user_model])
94
- )
95
-
95
+ Q(entity__slug__exact=entity_slug)
96
96
  )
97
97
 
98
98
 
@@ -212,6 +212,15 @@ class EntityUnitModelAbstract(MP_Node,
212
212
  self.slug = unit_slug
213
213
  return self.slug
214
214
 
215
+ def get_absolute_url(self):
216
+ return reverse(
217
+ viewname='django_ledger:unit-detail',
218
+ kwargs={
219
+ 'entity_slug': self.entity.slug,
220
+ 'unit_slug': self.slug
221
+ }
222
+ )
223
+
215
224
 
216
225
  class EntityUnitModel(EntityUnitModelAbstract):
217
226
  """
@@ -91,6 +91,15 @@ class VendorModelManager(models.Manager):
91
91
  Custom defined VendorModel Manager, which defines many methods for initial query of the Database.
92
92
  """
93
93
 
94
+ def for_user(self, user_model):
95
+ qs = self.get_queryset()
96
+ if user_model.is_superuser:
97
+ return qs
98
+ return qs.filter(
99
+ Q(entity_model__admin=user_model) |
100
+ Q(entity_model__managers__in=[user_model])
101
+ )
102
+
94
103
  def for_entity(self, entity_slug, user_model) -> VendorModelQuerySet:
95
104
  """
96
105
  Fetches a QuerySet of VendorModel associated with a specific EntityModel & UserModel.
@@ -114,21 +123,13 @@ class VendorModelManager(models.Manager):
114
123
  VendorModelQuerySet
115
124
  A filtered VendorModel QuerySet.
116
125
  """
117
- qs = self.get_queryset()
126
+ qs = self.for_user(user_model)
118
127
  if isinstance(entity_slug, lazy_loader.get_entity_model()):
119
128
  return qs.filter(
120
- Q(entity_model=entity_slug) &
121
- (
122
- Q(entity_model__admin=user_model) |
123
- Q(entity_model__managers__in=[user_model])
124
- )
129
+ Q(entity_model=entity_slug)
125
130
  )
126
131
  return qs.filter(
127
- Q(entity_model__slug__exact=entity_slug) &
128
- (
129
- Q(entity_model__admin=user_model) |
130
- Q(entity_model__managers__in=[user_model])
131
- )
132
+ Q(entity_model__slug__exact=entity_slug)
132
133
  )
133
134
 
134
135
 
@@ -1,7 +1,7 @@
1
1
  from datetime import datetime, date
2
2
  from typing import Optional, Dict, Union
3
3
 
4
- from django_ledger.io import IODigestContextManager
4
+ from django_ledger.io.io_digest import IODigestContextManager
5
5
  from django_ledger.report.core import BaseReportSupport, PDFReportValidationError
6
6
  from django_ledger.settings import DJANGO_LEDGER_CURRENCY_SYMBOL
7
7
  from django_ledger.templatetags.django_ledger import currency_format
@@ -3,8 +3,9 @@ from typing import Optional, Dict
3
3
  from django.contrib.staticfiles import finders
4
4
  from django.core.exceptions import ValidationError
5
5
 
6
- from django_ledger.io import IODigestContextManager
7
- from django_ledger.models import LedgerModel, EntityUnitModel
6
+ from django_ledger.io.io_digest import IODigestContextManager
7
+ from django_ledger.models.ledger import LedgerModel
8
+ from django_ledger.models.unit import EntityUnitModel
8
9
  from django_ledger.settings import DJANGO_LEDGER_PDF_SUPPORT_ENABLED
9
10
  from django_ledger.templatetags.django_ledger import currency_symbol, currency_format
10
11
 
@@ -35,7 +35,7 @@
35
35
  <div class="card-footer">
36
36
  {% if journal_entry.can_lock %}
37
37
  <a href="{{ journal_entry.get_lock_url }}" class="card-footer-item has-text-success has-text-weight-bold">{% trans 'Lock' %}</a>
38
- {% endif %}t
38
+ {% endif %}
39
39
  {% if journal_entry.can_unlock %}
40
40
  <a href="{{ journal_entry.get_unlock_url }}" class="card-footer-item has-text-warning has-text-weight-bold">{% trans 'UnLock' %}</a>
41
41
  {% endif %}
@@ -4,6 +4,9 @@
4
4
  {% load django_ledger %}
5
5
 
6
6
  {% block view_content %}
7
+
8
+ {# {% include 'django_ledger/journal_entry/includes/card_journal_entry.html' je_model= %}#}
9
+
7
10
  <div class="card">
8
11
  <div class="card-content">
9
12
  <div class="level">
@@ -19,7 +19,7 @@ from django_ledger import __version__
19
19
  from django_ledger.forms.app_filters import EntityFilterForm, ActivityFilterForm
20
20
  from django_ledger.forms.feedback import BugReportForm, RequestNewFeatureForm
21
21
  from django_ledger.io import CREDIT, DEBIT, ROLES_ORDER_ALL
22
- from django_ledger.io.io_mixin import validate_activity
22
+ from django_ledger.io.io_core import validate_activity
23
23
  from django_ledger.models import TransactionModel, BillModel, InvoiceModel, EntityUnitModel
24
24
  from django_ledger.settings import (
25
25
  DJANGO_LEDGER_FINANCIAL_ANALYSIS, DJANGO_LEDGER_CURRENCY_SYMBOL,
@@ -12,7 +12,7 @@ from django.test import TestCase
12
12
  from django.test.client import Client
13
13
  from django.utils.timezone import get_default_timezone
14
14
 
15
- from django_ledger.io.data_generator import EntityDataGenerator
15
+ from django_ledger.io.io_generator import EntityDataGenerator
16
16
  from django_ledger.models.entity import EntityModel, EntityModelQuerySet
17
17
 
18
18
  UserModel = get_user_model()