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 CHANGED
@@ -9,7 +9,7 @@ Contributions to this module:
9
9
  default_app_config = 'django_ledger.apps.DjangoLedgerConfig'
10
10
 
11
11
  """Django Ledger"""
12
- __version__ = '0.5.6.4'
12
+ __version__ = '0.6.0'
13
13
  __license__ = 'GPLv3 License'
14
14
 
15
15
  __author__ = 'Miguel Sanda'
@@ -53,11 +53,13 @@ class EntityModelAdmin(ModelAdmin):
53
53
  list_display = [
54
54
  'slug',
55
55
  'name',
56
+ 'admin',
56
57
  'accrual_method',
57
58
  'last_closing_date',
58
59
  'hidden',
59
60
  'get_coa_count',
60
61
  'add_ledger_link',
62
+ 'dashboard_link',
61
63
  'balance_sheet_link',
62
64
  'income_statement_link',
63
65
  'cash_flow_statement_link'
@@ -166,6 +168,18 @@ class EntityModelAdmin(ModelAdmin):
166
168
 
167
169
  cash_flow_statement_link.short_description = 'Cash Flow'
168
170
 
171
+ def dashboard_link(self, obj: EntityModel):
172
+ add_ledger_url = reverse(
173
+ viewname='django_ledger:entity-dashboard',
174
+ kwargs={
175
+ 'entity_slug': obj.slug
176
+ })
177
+ return format_html('<a class="viewlink" href="{url}">View</a>',
178
+ url=add_ledger_url,
179
+ slug=obj.slug)
180
+
181
+ dashboard_link.short_description = 'Dashboard'
182
+
169
183
  def add_code_of_accounts(self, request, queryset):
170
184
  lt = get_localtime().isoformat()
171
185
  for entity_model in queryset:
@@ -31,8 +31,9 @@ class AccountModelCreateForm(ModelForm):
31
31
  balance_type: Need to be selected from drop down as "Debit" or Credit"
32
32
  """
33
33
 
34
- def __init__(self, entity_slug, user_model, *args, **kwargs):
35
- self.ENTITY_SLUG = entity_slug
34
+ def __init__(self, entity_model, coa_model, user_model, *args, **kwargs):
35
+ self.ENTITY = entity_model
36
+ self.COA_MODEL = coa_model
36
37
  self.USER_MODEL = user_model
37
38
  super().__init__(*args, **kwargs)
38
39
  self.fields['role'].choices = ACCOUNT_CHOICES_NO_ROOT
@@ -44,6 +45,13 @@ class AccountModelCreateForm(ModelForm):
44
45
  return None
45
46
  return role_default
46
47
 
48
+ def clean_code(self):
49
+ code = self.cleaned_data['code']
50
+ is_code_valid = not self.COA_MODEL.accountmodel_set.filter(code=code).exists()
51
+ if not is_code_valid:
52
+ raise ValidationError(message=_('Code {} already exists for CoA {}').format(code, self.COA_MODEL.slug))
53
+ return code
54
+
47
55
  class Meta:
48
56
  model = AccountModel
49
57
  fields = [
@@ -92,8 +100,9 @@ class AccountModelUpdateForm(MoveNodeForm):
92
100
  'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
93
101
  }))
94
102
 
95
- def __init__(self, entity_slug, user_model, *args, **kwargs):
96
- self.ENTITY_SLUG = entity_slug
103
+ def __init__(self, entity_model, coa_model, user_model, *args, **kwargs):
104
+ self.ENTITY = entity_model
105
+ self.COA_MODEL = coa_model
97
106
  self.USER_MODEL = user_model
98
107
  super().__init__(*args, **kwargs)
99
108
  # self.fields['_ref_node_id'].choices = self.mk_dropdown_tree_choices()
@@ -38,8 +38,11 @@ from django_ledger.exceptions import InvalidDateInputError, TransactionNotInBala
38
38
  from django_ledger.io import roles as roles_module
39
39
  from django_ledger.io.io_digest import IODigestContextManager
40
40
  from django_ledger.io.io_middleware import (
41
- AccountRoleIOMiddleware, AccountGroupIOMiddleware, JEActivityIOMiddleware,
42
- BalanceSheetIOMiddleware, IncomeStatementIOMiddleware,
41
+ AccountRoleIOMiddleware,
42
+ AccountGroupIOMiddleware,
43
+ JEActivityIOMiddleware,
44
+ BalanceSheetIOMiddleware,
45
+ IncomeStatementIOMiddleware,
43
46
  CashFlowStatementIOMiddleware
44
47
  )
45
48
  from django_ledger.io.ratios import FinancialRatioManager
@@ -263,7 +266,7 @@ class IODatabaseMixIn:
263
266
  return isinstance(self, lazy_loader.get_ledger_model())
264
267
 
265
268
  def is_entity_unit_model(self):
266
- return isinstance(self, lazy_loader.get_unit_model())
269
+ return isinstance(self, lazy_loader.get_entity_unit_model())
267
270
 
268
271
  def get_entity_model_from_io(self):
269
272
  if self.is_entity_model():
@@ -601,6 +604,8 @@ class IODatabaseMixIn:
601
604
  use_closing_entries=use_closing_entries,
602
605
  **kwargs)
603
606
 
607
+ TransactionModel = lazy_loader.get_txs_model()
608
+
604
609
  for tx_model in io_result.txs_queryset:
605
610
  if tx_model['account__balance_type'] != tx_model['tx_type']:
606
611
  tx_model['balance'] = -tx_model['balance']
@@ -625,7 +630,6 @@ class IODatabaseMixIn:
625
630
  acc['balance_abs'] = abs(acc['balance'])
626
631
 
627
632
  if signs:
628
- TransactionModel = lazy_loader.get_txs_model()
629
633
  for acc in accounts_digest:
630
634
  if any([
631
635
  all([acc['role_bs'] == roles_module.BS_ASSET_ROLE,
@@ -945,8 +949,8 @@ class IOReportMixIn:
945
949
  **kwargs
946
950
  )
947
951
 
948
- report_klass = lazy_loader.get_balance_sheet_report_class()
949
- report = report_klass(
952
+ BalanceSheetReport = lazy_loader.get_balance_sheet_report_class()
953
+ report = BalanceSheetReport(
950
954
  self.PDF_REPORT_ORIENTATION,
951
955
  self.PDF_REPORT_MEASURE_UNIT,
952
956
  self.PDF_REPORT_PAGE_SIZE,
@@ -1000,8 +1004,8 @@ class IOReportMixIn:
1000
1004
  txs_queryset=txs_queryset,
1001
1005
  **kwargs
1002
1006
  )
1003
- report_klass = lazy_loader.get_income_statement_report_class()
1004
- report = report_klass(
1007
+ IncomeStatementReport = lazy_loader.get_income_statement_report_class()
1008
+ report = IncomeStatementReport(
1005
1009
  self.PDF_REPORT_ORIENTATION,
1006
1010
  self.PDF_REPORT_MEASURE_UNIT,
1007
1011
  self.PDF_REPORT_PAGE_SIZE,
@@ -1054,8 +1058,8 @@ class IOReportMixIn:
1054
1058
  **kwargs
1055
1059
  )
1056
1060
 
1057
- report_klass = lazy_loader.get_cash_flow_statement_report_class()
1058
- report = report_klass(
1061
+ CashFlowStatementReport = lazy_loader.get_cash_flow_statement_report_class()
1062
+ report = CashFlowStatementReport(
1059
1063
  self.PDF_REPORT_ORIENTATION,
1060
1064
  self.PDF_REPORT_MEASURE_UNIT,
1061
1065
  self.PDF_REPORT_PAGE_SIZE,
@@ -1108,22 +1112,22 @@ class IOReportMixIn:
1108
1112
  **kwargs
1109
1113
  )
1110
1114
 
1111
- bs_report_klass = lazy_loader.get_balance_sheet_report_class()
1112
- bs_report = bs_report_klass(
1115
+ BalanceSheetReport = lazy_loader.get_balance_sheet_report_class()
1116
+ bs_report = BalanceSheetReport(
1113
1117
  self.PDF_REPORT_ORIENTATION,
1114
1118
  self.PDF_REPORT_MEASURE_UNIT,
1115
1119
  self.PDF_REPORT_PAGE_SIZE,
1116
1120
  io_digest=io_digest
1117
1121
  )
1118
- is_report_klass = lazy_loader.get_income_statement_report_class()
1119
- is_report = is_report_klass(
1122
+ IncomeStatementReport = lazy_loader.get_income_statement_report_class()
1123
+ is_report = IncomeStatementReport(
1120
1124
  self.PDF_REPORT_ORIENTATION,
1121
1125
  self.PDF_REPORT_MEASURE_UNIT,
1122
1126
  self.PDF_REPORT_PAGE_SIZE,
1123
1127
  io_digest=io_digest
1124
1128
  )
1125
- cfs_report_klass = lazy_loader.get_cash_flow_statement_report_class()
1126
- cfs_report = cfs_report_klass(
1129
+ CashFlowStatementReport = lazy_loader.get_cash_flow_statement_report_class()
1130
+ cfs_report = CashFlowStatementReport(
1127
1131
  self.PDF_REPORT_ORIENTATION,
1128
1132
  self.PDF_REPORT_MEASURE_UNIT,
1129
1133
  self.PDF_REPORT_PAGE_SIZE,
@@ -69,7 +69,7 @@ class IODigestContextManager:
69
69
  def is_unit_model(self) -> bool:
70
70
  return isinstance(
71
71
  self.IO_MODEL,
72
- lazy_loader.get_unit_model()
72
+ lazy_loader.get_entity_unit_model()
73
73
  )
74
74
 
75
75
  def is_by_unit(self) -> bool:
@@ -11,9 +11,7 @@ from itertools import groupby, chain
11
11
  from django.core.exceptions import ValidationError
12
12
 
13
13
  from django_ledger.io import roles as roles_module
14
- from django_ledger.models.utils import LazyLoader, lazy_loader
15
-
16
- lazy_importer = LazyLoader()
14
+ from django_ledger.models.utils import lazy_loader
17
15
 
18
16
 
19
17
  class AccountRoleIOMiddleware:
@@ -197,7 +195,7 @@ class JEActivityIOMiddleware:
197
195
  return (acc for acc in self.ACCOUNTS if acc['activity'] == activity)
198
196
 
199
197
  def process_activity(self):
200
- JournalEntryModel = lazy_importer.get_journal_entry_model()
198
+ JournalEntryModel = lazy_loader.get_journal_entry_model()
201
199
  for act in JournalEntryModel.VALID_ACTIVITIES:
202
200
  acc_list = list(self.get_accounts_generator(act))
203
201
  self.ACTIVITY_ACCOUNTS[act] = acc_list
@@ -158,7 +158,10 @@ class AccountModelManager(MP_NodeManager):
158
158
  """
159
159
  Sets the custom queryset as the default.
160
160
  """
161
- return AccountModelQuerySet(self.model).order_by('path')
161
+ return AccountModelQuerySet(
162
+ self.model,
163
+ using=self._db
164
+ ).order_by('path').select_related('coa_model')
162
165
 
163
166
  def for_user(self, user_model):
164
167
  qs = self.get_queryset()
@@ -646,6 +649,13 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
646
649
  'updated'
647
650
  ])
648
651
 
652
+ def can_transact(self) -> bool:
653
+ return all([
654
+ self.coa_model.is_active(),
655
+ not self.is_locked(),
656
+ self.is_active()
657
+ ])
658
+
649
659
  def get_code_prefix(self) -> str:
650
660
 
651
661
  if self.is_asset():
@@ -1,14 +1,14 @@
1
+ from datetime import datetime, time
1
2
  from decimal import Decimal
2
3
  from itertools import groupby, chain
3
4
  from typing import Optional
4
5
  from uuid import uuid4, UUID
5
- from datetime import datetime, time
6
6
 
7
7
  from django.core.exceptions import ValidationError
8
8
  from django.core.validators import MinValueValidator
9
9
  from django.db import models
10
10
  from django.db.models import Q
11
- from django.db.models.signals import pre_save, pre_delete
11
+ from django.db.models.signals import pre_save
12
12
  from django.urls import reverse
13
13
  from django.utils.timezone import make_aware
14
14
  from django.utils.translation import gettext_lazy as _
@@ -120,8 +120,6 @@ class ClosingEntryModelAbstract(CreateUpdateMixIn, MarkdownNotesMixIn):
120
120
  f'do not equal Debits {ce_txs_sum[TransactionModel.DEBIT]}'
121
121
  )
122
122
 
123
-
124
-
125
123
  key_func = lambda i: (str(i.unit_model_id) if i.unit_model_id else '', i.activity if i.activity else '')
126
124
 
127
125
  ce_txs.sort(key=key_func)
@@ -338,6 +336,8 @@ class ClosingEntryModel(ClosingEntryModelAbstract):
338
336
  pass
339
337
 
340
338
 
339
+ # todo: Remove this model!
340
+
341
341
  class ClosingEntryTransactionModelQuerySet(models.QuerySet):
342
342
  pass
343
343