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
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.5.4'
12
+ __version__ = '0.5.6.0'
13
13
  __license__ = 'GPLv3 License'
14
14
 
15
15
  __author__ = 'Miguel Sanda'
@@ -0,0 +1,10 @@
1
+ from django.contrib import admin
2
+
3
+ from django_ledger.admin.coa import ChartOfAccountsModelAdmin
4
+ from django_ledger.admin.entity import EntityModelAdmin
5
+ from django_ledger.admin.ledger import LedgerModelAdmin
6
+ from django_ledger.models import EntityModel, ChartOfAccountModel, LedgerModel
7
+
8
+ admin.site.register(EntityModel, EntityModelAdmin)
9
+ admin.site.register(ChartOfAccountModel, ChartOfAccountsModelAdmin)
10
+ admin.site.register(LedgerModel, LedgerModelAdmin)
@@ -0,0 +1,135 @@
1
+ from django.contrib.admin import TabularInline, ModelAdmin
2
+ from django.db.models import Count
3
+ from django.forms import ModelForm, BooleanField, BaseInlineFormSet
4
+
5
+ from django_ledger.models.accounts import AccountModel
6
+ from django_ledger.models.coa import ChartOfAccountModel
7
+ from django_ledger.models.entity import EntityModel
8
+
9
+
10
+ class AccountModelInLineForm(ModelForm):
11
+ role_default = BooleanField(initial=False, required=False)
12
+
13
+ class Meta:
14
+ model = AccountModel
15
+ fields = []
16
+
17
+
18
+ class AccountModelInLineFormSet(BaseInlineFormSet):
19
+
20
+ def save_new(self, form, commit=True):
21
+ setattr(form.instance, self.fk.name, self.instance)
22
+ if commit:
23
+ account_model = AccountModel.add_root(
24
+ instance=super().save_new(form, commit=False)
25
+ )
26
+ return account_model
27
+ return super().save_new(form, commit=False)
28
+
29
+
30
+ class AccountModelInLine(TabularInline):
31
+ extra = 0
32
+ form = AccountModelInLineForm
33
+ formset = AccountModelInLineFormSet
34
+ show_change_link = True
35
+ exclude = [
36
+ 'path',
37
+ 'depth',
38
+ 'numchild'
39
+ ]
40
+ model = AccountModel
41
+ fieldsets = [
42
+ ('', {
43
+ 'fields': [
44
+ 'role',
45
+ 'balance_type',
46
+ 'code',
47
+ 'name',
48
+ 'role_default',
49
+ 'locked',
50
+ 'active'
51
+ ]
52
+ })
53
+ ]
54
+
55
+ def get_queryset(self, request):
56
+ qs = super().get_queryset(request)
57
+ return qs.not_coa_root()
58
+
59
+
60
+ class ChartOfAccountsAdminForm(ModelForm):
61
+ assign_as_default = BooleanField(initial=False, required=False)
62
+
63
+ def __init__(self, *args, **kwargs):
64
+ super().__init__(*args, **kwargs)
65
+ if self.instance.is_default():
66
+ self.fields['assign_as_default'].initial = True
67
+ self.fields['assign_as_default'].disabled = True
68
+
69
+ def save(self, commit=True):
70
+ if commit:
71
+ if self.cleaned_data['assign_as_default']:
72
+ entity_model: EntityModel = self.instance.entity
73
+ entity_model.default_coa = self.instance
74
+ entity_model.save(update_fields=[
75
+ 'default_coa'
76
+ ])
77
+ return super().save(commit=commit)
78
+
79
+
80
+ class ChartOfAccountsInLine(TabularInline):
81
+ form = ChartOfAccountsAdminForm
82
+ model = ChartOfAccountModel
83
+ extra = 0
84
+ show_change_link = True
85
+ fields = [
86
+ 'name',
87
+ 'locked',
88
+ 'assign_as_default'
89
+ ]
90
+
91
+
92
+ class ChartOfAccountsModelAdmin(ModelAdmin):
93
+ list_filter = [
94
+ 'entity__name',
95
+ 'locked'
96
+ ]
97
+ list_display = [
98
+ 'entity_name',
99
+ 'name',
100
+ 'slug',
101
+ 'locked',
102
+ 'account_model_count'
103
+ ]
104
+ search_fields = [
105
+ 'slug',
106
+ 'entity__name'
107
+ ]
108
+ list_display_links = ['name']
109
+ fields = [
110
+ 'name',
111
+ 'locked',
112
+ 'description',
113
+ ]
114
+ inlines = [
115
+ AccountModelInLine
116
+ ]
117
+
118
+ class Meta:
119
+ model = ChartOfAccountModel
120
+
121
+ def entity_name(self, obj):
122
+ return obj.entity.name
123
+
124
+ def get_queryset(self, request):
125
+ qs = ChartOfAccountModel.objects.for_user(user_model=request.user)
126
+ ordering = self.get_ordering(request)
127
+ if ordering:
128
+ qs = qs.order_by(*ordering)
129
+ qs = qs.select_related('entity').annotate(Count('accountmodel'))
130
+ return qs
131
+
132
+ def account_model_count(self, obj):
133
+ return obj.accountmodel__count
134
+
135
+ account_model_count.short_description = 'Accounts'
@@ -0,0 +1,199 @@
1
+ from datetime import timedelta
2
+ from uuid import uuid4
3
+
4
+ from django.contrib.admin import TabularInline, ModelAdmin
5
+ from django.db.models import Count
6
+ from django.forms import BaseInlineFormSet
7
+ from django.urls import reverse
8
+ from django.utils.html import format_html
9
+ from django.utils.timezone import localtime
10
+
11
+ from django_ledger.admin.coa import ChartOfAccountsInLine
12
+ from django_ledger.models import EntityUnitModel, ItemTransactionModel, TransactionModel
13
+ from django_ledger.models.entity import EntityModel, EntityManagementModel
14
+
15
+
16
+ class EntityManagementInLine(TabularInline):
17
+ model = EntityManagementModel
18
+ extra = 1
19
+ fields = [
20
+ 'user'
21
+ ]
22
+
23
+
24
+ class EntityUnitModelInLineFormSet(BaseInlineFormSet):
25
+
26
+ def save_new(self, form, commit=True):
27
+ setattr(form.instance, self.fk.name, self.instance)
28
+ if commit:
29
+ unit_model = EntityUnitModel.add_root(
30
+ instance=super().save_new(form, commit=False)
31
+ )
32
+ return unit_model
33
+ return super().save_new(form, commit=False)
34
+
35
+
36
+ class EntityUnitModelInLine(TabularInline):
37
+ model = EntityUnitModel
38
+ formset = EntityUnitModelInLineFormSet
39
+ extra = 0
40
+ readonly_fields = [
41
+ 'slug'
42
+ ]
43
+ fields = [
44
+ 'slug',
45
+ 'name',
46
+ 'document_prefix',
47
+ 'active',
48
+ 'hidden'
49
+ ]
50
+
51
+
52
+ class EntityModelAdmin(ModelAdmin):
53
+ list_display = [
54
+ 'slug',
55
+ 'name',
56
+ 'accrual_method',
57
+ 'last_closing_date',
58
+ 'hidden',
59
+ 'get_coa_count',
60
+ 'add_ledger_link',
61
+ 'balance_sheet_link',
62
+ 'income_statement_link',
63
+ 'cash_flow_statement_link'
64
+ ]
65
+ readonly_fields = [
66
+ 'depth',
67
+ 'path',
68
+ 'numchild',
69
+ 'last_closing_date',
70
+ 'default_coa'
71
+ ]
72
+ fieldsets = [
73
+ (
74
+ 'Entity Information', {
75
+ 'fields': [
76
+ 'name',
77
+ 'admin',
78
+ 'fy_start_month',
79
+ 'accrual_method',
80
+ 'hidden',
81
+ 'picture'
82
+ ]
83
+ }
84
+ ),
85
+ (
86
+ 'Contact Information', {
87
+ 'fields': [
88
+ 'address_1',
89
+ 'address_2',
90
+ 'city',
91
+ 'state',
92
+ 'zip_code',
93
+ 'email',
94
+ 'website',
95
+ 'phone'
96
+ ]
97
+ }
98
+ ),
99
+ (
100
+ 'Chart of Accounts', {
101
+ 'fields': [
102
+ 'default_coa'
103
+ ]
104
+ }
105
+ )
106
+ ]
107
+ inlines = [
108
+ ChartOfAccountsInLine,
109
+ EntityUnitModelInLine,
110
+ EntityManagementInLine
111
+ ]
112
+ actions = [
113
+ 'add_code_of_accounts',
114
+ 'populate_random_data'
115
+ ]
116
+
117
+ class Meta:
118
+ model = EntityModel
119
+
120
+ def get_queryset(self, request):
121
+ qs = super().get_queryset(request=request)
122
+ qs = qs.annotate(Count('chartofaccountmodel'))
123
+ if request.user.is_superuser:
124
+ return qs
125
+ return qs.for_user(user_model=request.user)
126
+
127
+ def add_ledger_link(self, obj):
128
+ add_ledger_url = reverse('admin:django_ledger_ledgermodel_add')
129
+ return format_html('<a class="addlink" href="{url}?entity_slug={slug}">Add Ledger</a>',
130
+ url=add_ledger_url,
131
+ slug=obj.slug)
132
+
133
+ def balance_sheet_link(self, obj: EntityModel):
134
+ add_ledger_url = reverse(
135
+ viewname='django_ledger:entity-bs',
136
+ kwargs={
137
+ 'entity_slug': obj.slug
138
+ })
139
+ return format_html('<a class="viewlink" href="{url}">View</a>',
140
+ url=add_ledger_url,
141
+ slug=obj.slug)
142
+
143
+ balance_sheet_link.short_description = 'Balance Sheet'
144
+
145
+ def income_statement_link(self, obj: EntityModel):
146
+ add_ledger_url = reverse(
147
+ viewname='django_ledger:entity-ic',
148
+ kwargs={
149
+ 'entity_slug': obj.slug
150
+ })
151
+ return format_html('<a class="viewlink" href="{url}">View</a>',
152
+ url=add_ledger_url,
153
+ slug=obj.slug)
154
+
155
+ income_statement_link.short_description = 'P&L'
156
+
157
+ def cash_flow_statement_link(self, obj: EntityModel):
158
+ add_ledger_url = reverse(
159
+ viewname='django_ledger:entity-cf',
160
+ kwargs={
161
+ 'entity_slug': obj.slug
162
+ })
163
+ return format_html('<a class="viewlink" href="{url}">View</a>',
164
+ url=add_ledger_url,
165
+ slug=obj.slug)
166
+
167
+ cash_flow_statement_link.short_description = 'Cash Flow'
168
+
169
+ def add_code_of_accounts(self, request, queryset):
170
+ for entity_model in queryset:
171
+ entity_model.create_chart_of_accounts(
172
+ coa_name=f'{entity_model.name} CoA {localtime().isoformat()}',
173
+ commit=True,
174
+ assign_as_default=False
175
+ )
176
+
177
+ def populate_random_data(self, request, queryset):
178
+ for entity_model in queryset:
179
+ start_date = localtime() - timedelta(days=180)
180
+ entity_model.populate_random_data(
181
+ start_date=start_date,
182
+ days_forward=180,
183
+ tx_quantity=25)
184
+
185
+ def get_coa_count(self, obj):
186
+ return obj.chartofaccountmodel__count
187
+
188
+ get_coa_count.short_description = 'CoA Count'
189
+
190
+ def save_model(self, request, obj, form, change):
191
+ if not change:
192
+ if obj.uuid is None:
193
+ obj.uuid = uuid4()
194
+ EntityModel.add_root(instance=obj)
195
+ return
196
+ super().save_model(request, obj, form, change)
197
+
198
+ def has_delete_permission(self, request, obj=None):
199
+ return False
@@ -0,0 +1,283 @@
1
+ from django.contrib import messages
2
+ from django.contrib.admin import ModelAdmin, TabularInline
3
+ from django.db.models import Count
4
+ from django.forms import BaseInlineFormSet
5
+ from django.shortcuts import get_object_or_404
6
+ from django.utils.html import format_html
7
+
8
+ from django_ledger.models import (
9
+ LedgerModel, JournalEntryModel, EntityModel,
10
+ LedgerModelValidationError
11
+ )
12
+
13
+
14
+ class JournalEntryModelInLineFormSet(BaseInlineFormSet):
15
+
16
+ def __init__(self, *args, **kwargs):
17
+ self.ledger_model: LedgerModel = kwargs['instance']
18
+ self.entity_model = self.ledger_model.entity
19
+ super().__init__(*args, **kwargs)
20
+
21
+ def add_fields(self, form, index):
22
+ super().add_fields(form=form, index=index)
23
+ form.fields['entity_unit'].queryset = self.entity_model.entityunitmodel_set.all()
24
+
25
+
26
+ class JournalEntryModelInLine(TabularInline):
27
+ extra = 0
28
+ fields = [
29
+ 'timestamp',
30
+ 'description',
31
+ 'entity_unit',
32
+ 'posted',
33
+ 'locked',
34
+ 'activity',
35
+ 'origin',
36
+ 'txs_count',
37
+ 'view_txs_link',
38
+ 'edit_txs_link'
39
+ ]
40
+ readonly_fields = [
41
+ 'posted',
42
+ 'locked',
43
+ 'origin',
44
+ 'activity',
45
+ 'txs_count',
46
+ 'view_txs_link',
47
+ 'edit_txs_link'
48
+ ]
49
+ model = JournalEntryModel
50
+ formset = JournalEntryModelInLineFormSet
51
+
52
+ def get_queryset(self, request):
53
+ qs = self.model.objects.for_user(request.user)
54
+ ordering = self.get_ordering(request)
55
+ if ordering:
56
+ qs = qs.order_by(*ordering)
57
+ return qs.annotate(
58
+ txs_count=Count('transactionmodel')
59
+ ).select_related(
60
+ 'ledger',
61
+ 'ledger__entity'
62
+ )
63
+
64
+ def txs_count(self, obj):
65
+ return obj.txs_count
66
+
67
+ txs_count.short_description = 'Transactions'
68
+
69
+ def has_change_permission(self, request, obj=None):
70
+ if obj:
71
+ return all([
72
+ not obj.is_locked(),
73
+ super().has_change_permission(request, obj)
74
+ ])
75
+ return super().has_change_permission(request, obj)
76
+
77
+ def has_delete_permission(self, request, obj=None):
78
+ if obj:
79
+ return all([
80
+ obj.can_delete(),
81
+ super().has_delete_permission(request, obj)
82
+ ])
83
+ return super().has_delete_permission(request, obj)
84
+
85
+ def view_txs_link(self, obj: JournalEntryModel):
86
+ detail_url = obj.get_detail_url()
87
+ return format_html(
88
+ format_string='<a class="viewlink" target="_blank" href="{url}">View Txs</a>',
89
+ url=detail_url
90
+ )
91
+
92
+ def edit_txs_link(self, obj: JournalEntryModel):
93
+ detail_url = obj.get_detail_txs_url()
94
+ next_url = None
95
+ return format_html(
96
+ format_string='<a class="changelink" target="_blank" href="{url}">Edit Txs</a>',
97
+ url=detail_url
98
+ )
99
+
100
+
101
+ class LedgerModelAdmin(ModelAdmin):
102
+ readonly_fields = [
103
+ 'entity',
104
+ 'posted',
105
+ 'locked'
106
+ ]
107
+ list_filter = [
108
+ 'posted',
109
+ 'locked',
110
+ 'entity__name'
111
+ ]
112
+ list_display = [
113
+ 'name',
114
+ 'is_posted',
115
+ 'is_locked',
116
+ 'is_extended',
117
+ 'journal_entry_count',
118
+ 'earliest_journal_entry'
119
+ ]
120
+ actions = [
121
+ 'post',
122
+ 'unpost',
123
+ 'lock',
124
+ 'unlock'
125
+ ]
126
+ inlines = [
127
+ JournalEntryModelInLine
128
+ ]
129
+
130
+ def get_queryset(self, request):
131
+ qs = LedgerModel.objects.for_user(user_model=request.user)
132
+ ordering = self.get_ordering(request)
133
+ if ordering:
134
+ qs = qs.order_by(*ordering)
135
+ return qs.select_related('entity')
136
+
137
+ def get_inlines(self, request, obj):
138
+ if obj is None:
139
+ return []
140
+ return super().get_inlines(request, obj)
141
+
142
+ def get_fieldsets(self, request, obj=None):
143
+ if obj is None:
144
+ return [
145
+ ('', {
146
+ 'fields': [
147
+ 'name',
148
+ 'hidden',
149
+ 'additional_info'
150
+ ]
151
+ })
152
+ ]
153
+ return [
154
+ ('', {
155
+ 'fields': [
156
+ 'entity',
157
+ 'name',
158
+ 'hidden',
159
+ 'additional_info'
160
+ ]
161
+ })
162
+ ]
163
+
164
+ def get_entity_model(self, request) -> EntityModel:
165
+ entity_slug = request.GET.get('entity_slug')
166
+ entity_model_qs = EntityModel.objects.for_user(user_model=request.user)
167
+ entity_model = get_object_or_404(entity_model_qs, slug__exact=entity_slug)
168
+ return entity_model
169
+
170
+ def has_add_permission(self, request):
171
+ if request.GET.get('entity_slug') is not None:
172
+ return True
173
+ return False
174
+
175
+ def add_view(self, request, form_url="", extra_context=None):
176
+ entity_model = self.get_entity_model(request)
177
+ extra_context = {
178
+ 'entity_model': entity_model,
179
+ 'title': f'Add Ledger: {entity_model.name}'
180
+ }
181
+ return super().add_view(request, form_url="", extra_context=extra_context)
182
+
183
+ def is_locked(self, obj):
184
+ return obj.is_locked()
185
+
186
+ is_locked.boolean = True
187
+
188
+ def is_posted(self, obj):
189
+ return obj.is_posted()
190
+
191
+ is_posted.boolean = True
192
+
193
+ def is_extended(self, obj):
194
+ return obj.has_wrapped_model()
195
+
196
+ is_extended.boolean = True
197
+
198
+ # ACTIONS....
199
+ def post(self, request, queryset):
200
+ for obj in queryset:
201
+ try:
202
+ obj.post(raise_exception=True, commit=False)
203
+ except LedgerModelValidationError as e:
204
+ messages.error(
205
+ request=request,
206
+ message=e.message
207
+ )
208
+ queryset.bulk_update(
209
+ objs=queryset,
210
+ fields=[
211
+ 'posted',
212
+ 'updated'
213
+ ])
214
+
215
+ def lock(self, request, queryset):
216
+ for obj in queryset:
217
+ try:
218
+ obj.lock(raise_exception=True, commit=False)
219
+ except LedgerModelValidationError as e:
220
+ messages.error(
221
+ request=request,
222
+ message=e.message
223
+ )
224
+
225
+ queryset.bulk_update(
226
+ objs=queryset,
227
+ fields=[
228
+ 'locked',
229
+ 'updated'
230
+ ])
231
+
232
+ def unpost(self, request, queryset):
233
+ for obj in queryset:
234
+ try:
235
+ obj.unpost(raise_exception=True, commit=False)
236
+ except LedgerModelValidationError as e:
237
+ messages.error(
238
+ request=request,
239
+ message=e.message
240
+ )
241
+ queryset.bulk_update(
242
+ objs=queryset,
243
+ fields=[
244
+ 'posted',
245
+ 'updated'
246
+ ])
247
+
248
+ def unlock(self, request, queryset):
249
+ for obj in queryset:
250
+ try:
251
+ obj.unlock(raise_exception=True, commit=False)
252
+ except LedgerModelValidationError as e:
253
+ messages.error(
254
+ request=request,
255
+ message=e.message
256
+ )
257
+
258
+ queryset.bulk_update(
259
+ objs=queryset,
260
+ fields=[
261
+ 'locked',
262
+ 'updated'
263
+ ])
264
+
265
+ def journal_entry_count(self, obj):
266
+ return obj.journal_entries__count
267
+
268
+ def earliest_journal_entry(self, obj):
269
+ return obj.earliest_timestamp
270
+
271
+ def save_model(self, request, obj: LedgerModel, form, change):
272
+ if not change:
273
+ entity_model = self.get_entity_model(request)
274
+ obj.entity = entity_model
275
+ return super().save_model(
276
+ request=request,
277
+ obj=obj,
278
+ form=form,
279
+ change=change
280
+ )
281
+
282
+ class Meta:
283
+ model = LedgerModel
@@ -20,15 +20,6 @@ class EntityModelCreateForm(ModelForm):
20
20
  default_coa = BooleanField(required=False, initial=False, label=_('Populate Default CoA'))
21
21
  activate_all_accounts = BooleanField(required=False, initial=False, label=_('Activate All Accounts'))
22
22
  generate_sample_data = BooleanField(required=False, initial=False, label=_('Fill With Sample Data?'))
23
- tx_quantity = IntegerField(required=True, initial=50, label=_('Number of Transaction Loops'),
24
- validators=[
25
- MinValueValidator(limit_value=0),
26
- MaxValueValidator(limit_value=100)
27
- ],
28
- widget=TextInput(attrs={
29
- 'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
30
- }))
31
-
32
23
 
33
24
  def clean_name(self):
34
25
  name = self.cleaned_data.get('name')
@@ -200,7 +191,8 @@ class EntityModelUpdateForm(ModelForm):
200
191
  'placeholder': _('Website...')
201
192
  }
202
193
  ),
203
- 'fy_start_month': Select(attrs={
204
- 'class': 'input'
205
- })
194
+ 'fy_start_month': Select(
195
+ attrs={
196
+ 'class': 'input'
197
+ })
206
198
  }
@@ -1,4 +1,6 @@
1
+ from django.core.exceptions import ValidationError
1
2
  from django.forms import ModelForm, TextInput, Select
3
+ from django.utils.translation import gettext_lazy as _
2
4
 
3
5
  from django_ledger.models.ledger import LedgerModel
4
6
  from django_ledger.settings import DJANGO_LEDGER_FORM_INPUT_CLASSES
@@ -11,10 +13,19 @@ class LedgerModelCreateForm(ModelForm):
11
13
  self.ENTITY_SLUG: str = entity_slug
12
14
  self.USER_MODEL = user_model
13
15
 
16
+ def validate_unique(self):
17
+ exclude = self._get_validation_exclusions()
18
+ exclude.remove('entity')
19
+ try:
20
+ self.instance.validate_unique(exclude=exclude)
21
+ except ValidationError as e:
22
+ self._update_errors(e)
23
+
14
24
  class Meta:
15
25
  model = LedgerModel
16
26
  fields = [
17
27
  'name',
28
+ 'ledger_xid'
18
29
  ]
19
30
  widgets = {
20
31
  'name': TextInput(
@@ -22,6 +33,14 @@ class LedgerModelCreateForm(ModelForm):
22
33
  'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
23
34
  }
24
35
  ),
36
+ 'ledger_xid': TextInput(
37
+ attrs={
38
+ 'class': DJANGO_LEDGER_FORM_INPUT_CLASSES
39
+ }
40
+ ),
41
+ }
42
+ labels = {
43
+ 'ledger_xid': _('Ledger External ID')
25
44
  }
26
45
 
27
46