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.

Files changed (33) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/admin/coa.py +3 -3
  3. django_ledger/admin/entity.py +14 -0
  4. django_ledger/forms/account.py +14 -5
  5. django_ledger/forms/coa.py +1 -6
  6. django_ledger/io/roles.py +25 -9
  7. django_ledger/migrations/0015_remove_chartofaccountmodel_locked_and_more.py +22 -0
  8. django_ledger/models/accounts.py +9 -9
  9. django_ledger/models/coa.py +261 -50
  10. django_ledger/models/entity.py +43 -28
  11. django_ledger/templates/django_ledger/account/account_create.html +17 -11
  12. django_ledger/templates/django_ledger/account/account_list.html +12 -9
  13. django_ledger/templates/django_ledger/account/account_update.html +18 -16
  14. django_ledger/templates/django_ledger/account/tags/accounts_table.html +97 -93
  15. django_ledger/templates/django_ledger/chart_of_accounts/coa_list.html +17 -0
  16. django_ledger/templates/django_ledger/{code_of_accounts → chart_of_accounts}/coa_update.html +1 -4
  17. django_ledger/templates/django_ledger/chart_of_accounts/includes/coa_card.html +74 -0
  18. django_ledger/templates/django_ledger/invoice/invoice_list.html +91 -94
  19. django_ledger/templatetags/django_ledger.py +1 -1
  20. django_ledger/tests/base.py +23 -1
  21. django_ledger/tests/test_accounts.py +16 -0
  22. django_ledger/tests/test_chart_of_accounts.py +46 -0
  23. django_ledger/urls/account.py +18 -3
  24. django_ledger/urls/chart_of_accounts.py +21 -1
  25. django_ledger/views/account.py +59 -10
  26. django_ledger/views/coa.py +83 -9
  27. django_ledger/views/mixins.py +10 -5
  28. {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/METADATA +1 -1
  29. {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/RECORD +33 -28
  30. {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/AUTHORS.md +0 -0
  31. {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/LICENSE +0 -0
  32. {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/WHEEL +0 -0
  33. {django_ledger-0.5.6.3.dist-info → django_ledger-0.5.6.5.dist-info}/top_level.txt +0 -0
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.3'
12
+ __version__ = '0.5.6.5'
13
13
  __license__ = 'GPLv3 License'
14
14
 
15
15
  __author__ = 'Miguel Sanda'
@@ -84,7 +84,7 @@ class ChartOfAccountsInLine(TabularInline):
84
84
  show_change_link = True
85
85
  fields = [
86
86
  'name',
87
- 'locked',
87
+ 'active',
88
88
  'assign_as_default'
89
89
  ]
90
90
 
@@ -92,13 +92,13 @@ class ChartOfAccountsInLine(TabularInline):
92
92
  class ChartOfAccountsModelAdmin(ModelAdmin):
93
93
  list_filter = [
94
94
  'entity__name',
95
- 'locked'
95
+ 'active'
96
96
  ]
97
97
  list_display = [
98
98
  'entity_name',
99
99
  'name',
100
100
  'slug',
101
- 'locked',
101
+ 'active',
102
102
  'account_model_count'
103
103
  ]
104
104
  search_fields = [
@@ -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 = [
@@ -53,7 +61,7 @@ class AccountModelCreateForm(ModelForm):
53
61
  'role_default',
54
62
  'balance_type',
55
63
  'active',
56
- 'locked'
64
+ 'active'
57
65
  ]
58
66
  widgets = {
59
67
  'code': TextInput(attrs={
@@ -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()
@@ -9,19 +9,14 @@ class ChartOfAccountsModelForm(ModelForm):
9
9
  class Meta:
10
10
  model = ChartOfAccountModel
11
11
  fields = [
12
- # 'slug',
13
12
  'name',
14
13
  'description'
15
14
  ]
16
15
  labels = {
17
- 'slug': _('CoA ID'),
18
16
  'name': _('Name'),
19
17
  'description': _('Description'),
20
18
  }
21
19
  widgets = {
22
- # 'slug': TextInput(attrs={
23
- # 'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
24
- # }),
25
20
  'name': TextInput(attrs={
26
21
  'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
27
22
  }),
@@ -36,7 +31,7 @@ class ChartOfAccountsModelUpdateForm(ModelForm):
36
31
  model = ChartOfAccountModel
37
32
  fields = [
38
33
  'name',
39
- 'locked'
34
+ 'active'
40
35
  ]
41
36
  labels = {
42
37
  'name': _('Name'),
django_ledger/io/roles.py CHANGED
@@ -119,48 +119,52 @@ ROOT_GROUP_LEVEL_2 = [
119
119
  ]
120
120
  ROOT_GROUP_META = {
121
121
  ROOT_COA: {
122
- 'code': '00000',
122
+ 'code': '00000000',
123
123
  'title': 'CoA Root Node',
124
124
  'balance_type': DEBIT
125
125
  },
126
126
  ROOT_ASSETS: {
127
- 'code': '01000',
127
+ 'code': '01000000',
128
128
  'title': 'Asset Accounts Root Node',
129
129
  'balance_type': DEBIT
130
130
  },
131
131
  ROOT_LIABILITIES: {
132
- 'code': '02000',
132
+ 'code': '02000000',
133
133
  'title': 'Liability Accounts Root Node',
134
134
  'balance_type': CREDIT
135
135
  },
136
136
  ROOT_CAPITAL: {
137
- 'code': '03000',
137
+ 'code': '03000000',
138
138
  'title': 'Capital Accounts Root Node',
139
139
  'balance_type': CREDIT
140
140
  },
141
141
  ROOT_INCOME: {
142
- 'code': '04000',
142
+ 'code': '04000000',
143
143
  'title': 'Income Accounts Root Node',
144
144
  'balance_type': CREDIT
145
145
  },
146
146
  ROOT_COGS: {
147
- 'code': '05000',
147
+ 'code': '05000000',
148
148
  'title': 'COGS Accounts Root Node',
149
149
  'balance_type': DEBIT
150
150
  },
151
151
  ROOT_EXPENSES: {
152
- 'code': '06000',
152
+ 'code': '06000000',
153
153
  'title': 'Expense Accounts Root Node',
154
154
  'balance_type': DEBIT
155
155
  },
156
156
  }
157
157
  # ------> ROLE GROUPS <-------#
158
158
 
159
+ GROUP_ASSETS = list()
160
+ GROUP_ASSETS.append(ROOT_ASSETS)
161
+
159
162
  # ASSET GROUPS...
160
163
  GROUP_QUICK_ASSETS = [
161
164
  ASSET_CA_CASH,
162
165
  ASSET_CA_MKT_SECURITIES
163
166
  ]
167
+ GROUP_ASSETS += GROUP_QUICK_ASSETS
164
168
 
165
169
  GROUP_CURRENT_ASSETS = [
166
170
  ASSET_CA_CASH,
@@ -171,6 +175,7 @@ GROUP_CURRENT_ASSETS = [
171
175
  ASSET_CA_UNCOLLECTIBLES,
172
176
  ASSET_CA_OTHER
173
177
  ]
178
+ GROUP_ASSETS += GROUP_CURRENT_ASSETS
174
179
 
175
180
  GROUP_NON_CURRENT_ASSETS = [
176
181
  ASSET_LTI_NOTES_RECEIVABLE,
@@ -187,9 +192,13 @@ GROUP_NON_CURRENT_ASSETS = [
187
192
  ASSET_ADJUSTMENTS
188
193
  ]
189
194
 
190
- GROUP_ASSETS = GROUP_CURRENT_ASSETS + GROUP_NON_CURRENT_ASSETS
195
+ GROUP_ASSETS += GROUP_NON_CURRENT_ASSETS
196
+ GROUP_ASSETS = list(set(GROUP_ASSETS))
191
197
 
192
198
  # LIABILITY GROUPS....
199
+ GROUP_LIABILITIES = list()
200
+ GROUP_LIABILITIES.append(ROOT_LIABILITIES)
201
+
193
202
  GROUP_CURRENT_LIABILITIES = [
194
203
  LIABILITY_CL_ACC_PAYABLE,
195
204
  LIABILITY_CL_DEFERRED_REVENUE,
@@ -200,17 +209,21 @@ GROUP_CURRENT_LIABILITIES = [
200
209
  LIABILITY_CL_WAGES_PAYABLE,
201
210
  LIABILITY_CL_TAXES_PAYABLE
202
211
  ]
212
+ GROUP_LIABILITIES += GROUP_CURRENT_LIABILITIES
203
213
 
204
214
  GROUP_LT_LIABILITIES = [
205
215
  LIABILITY_LTL_NOTES_PAYABLE,
206
216
  LIABILITY_LTL_BONDS_PAYABLE,
207
217
  LIABILITY_LTL_MORTGAGE_PAYABLE,
208
218
  ]
219
+ GROUP_LIABILITIES += GROUP_LT_LIABILITIES
209
220
 
210
- GROUP_LIABILITIES = GROUP_CURRENT_LIABILITIES + GROUP_LT_LIABILITIES
221
+ GROUP_LIABILITIES = list(set(GROUP_LIABILITIES))
211
222
 
212
223
  # CAPITAL/EQUITY...
224
+
213
225
  GROUP_CAPITAL = [
226
+ ROOT_CAPITAL,
214
227
  EQUITY_CAPITAL,
215
228
  EQUITY_COMMON_STOCK,
216
229
  EQUITY_PREFERRED_STOCK,
@@ -219,6 +232,7 @@ GROUP_CAPITAL = [
219
232
  ]
220
233
 
221
234
  GROUP_INCOME = [
235
+ ROOT_INCOME,
222
236
  INCOME_OPERATIONAL,
223
237
  INCOME_PASSIVE,
224
238
  INCOME_INTEREST,
@@ -227,10 +241,12 @@ GROUP_INCOME = [
227
241
  ]
228
242
 
229
243
  GROUP_COGS = [
244
+ ROOT_COGS,
230
245
  COGS
231
246
  ]
232
247
 
233
248
  GROUP_EXPENSES = [
249
+ ROOT_EXPENSES,
234
250
  EXPENSE_OPERATIONAL,
235
251
  EXPENSE_INTEREST_ST,
236
252
  EXPENSE_INTEREST_LT,
@@ -0,0 +1,22 @@
1
+ # Generated by Django 5.0.3 on 2024-03-14 02:17
2
+
3
+ from django.db import migrations, models
4
+
5
+
6
+ class Migration(migrations.Migration):
7
+
8
+ dependencies = [
9
+ ('django_ledger', '0014_ledgermodel_ledger_xid_and_more'),
10
+ ]
11
+
12
+ operations = [
13
+ migrations.RemoveField(
14
+ model_name='chartofaccountmodel',
15
+ name='locked',
16
+ ),
17
+ migrations.AddField(
18
+ model_name='chartofaccountmodel',
19
+ name='active',
20
+ field=models.BooleanField(default=True, verbose_name='Is Active'),
21
+ ),
22
+ ]
@@ -242,8 +242,9 @@ class AccountModelManager(MP_NodeManager):
242
242
  entity_slug=entity_slug,
243
243
  coa_slug=coa_slug)
244
244
  return qs.filter(
245
- active=True,
246
- locked=False
245
+ Q(active=True) &
246
+ Q(locked=False) &
247
+ Q(coa_model__active=True)
247
248
  )
248
249
 
249
250
  def with_roles(self, roles: Union[list, str], entity_slug, user_model) -> AccountModelQuerySet:
@@ -581,7 +582,7 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
581
582
  return self.balance_type == CREDIT
582
583
 
583
584
  def is_coa_root(self):
584
- return self.role in ROOT_GROUP
585
+ return self.role == ROOT_COA
585
586
 
586
587
  def is_asset(self) -> bool:
587
588
  return self.role in GROUP_ASSETS
@@ -604,6 +605,9 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
604
605
  def is_active(self) -> bool:
605
606
  return self.active is True
606
607
 
608
+ def is_locked(self) -> bool:
609
+ return self.locked is True
610
+
607
611
  def can_activate(self):
608
612
  return all([
609
613
  self.active is False
@@ -656,10 +660,7 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
656
660
  return '5'
657
661
  elif self.is_expense():
658
662
  return '6'
659
- elif self.is_coa_root():
660
- return '0'
661
- else:
662
- raise AccountModelValidationError(f'Invalid role match for role {self.role}...')
663
+ raise AccountModelValidationError(f'Invalid role match for role {self.role}...')
663
664
 
664
665
  def get_root_role(self) -> str:
665
666
  if self.is_asset():
@@ -676,8 +677,7 @@ class AccountModelAbstract(MP_Node, CreateUpdateMixIn):
676
677
  return ROOT_EXPENSES
677
678
  elif self.is_coa_root():
678
679
  return ROOT_COA
679
- else:
680
- raise AccountModelValidationError(f'Invalid role match for role {self.role}...')
680
+ raise AccountModelValidationError(f'Invalid role match for role {self.role}...')
681
681
 
682
682
  def get_account_move_choice_queryset(self):
683
683
  return self.coa_model.accountmodel_set.filter(