django-ledger 0.6.4__py3-none-any.whl → 0.7.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 (77) hide show
  1. django_ledger/__init__.py +1 -4
  2. django_ledger/contrib/django_ledger_graphene/accounts/schema.py +1 -1
  3. django_ledger/forms/account.py +43 -38
  4. django_ledger/forms/bank_account.py +3 -2
  5. django_ledger/forms/bill.py +24 -36
  6. django_ledger/forms/customer.py +1 -1
  7. django_ledger/forms/data_import.py +3 -3
  8. django_ledger/forms/estimate.py +1 -1
  9. django_ledger/forms/invoice.py +5 -7
  10. django_ledger/forms/item.py +24 -15
  11. django_ledger/forms/transactions.py +3 -3
  12. django_ledger/io/io_core.py +4 -2
  13. django_ledger/io/io_middleware.py +5 -0
  14. django_ledger/migrations/0017_alter_accountmodel_unique_together_and_more.py +31 -0
  15. django_ledger/models/accounts.py +225 -265
  16. django_ledger/models/bank_account.py +0 -4
  17. django_ledger/models/bill.py +0 -3
  18. django_ledger/models/closing_entry.py +0 -3
  19. django_ledger/models/coa.py +59 -48
  20. django_ledger/models/coa_default.py +9 -8
  21. django_ledger/models/customer.py +0 -4
  22. django_ledger/models/data_import.py +0 -3
  23. django_ledger/models/entity.py +70 -37
  24. django_ledger/models/estimate.py +0 -9
  25. django_ledger/models/invoice.py +0 -3
  26. django_ledger/models/items.py +4 -6
  27. django_ledger/models/journal_entry.py +2 -5
  28. django_ledger/models/ledger.py +0 -3
  29. django_ledger/models/mixins.py +0 -3
  30. django_ledger/models/purchase_order.py +0 -4
  31. django_ledger/models/signals.py +0 -3
  32. django_ledger/models/transactions.py +2 -5
  33. django_ledger/models/unit.py +0 -3
  34. django_ledger/models/utils.py +0 -3
  35. django_ledger/models/vendor.py +0 -3
  36. django_ledger/templates/django_ledger/account/account_create.html +2 -2
  37. django_ledger/templates/django_ledger/account/account_update.html +1 -1
  38. django_ledger/templates/django_ledger/account/tags/account_txs_table.html +1 -0
  39. django_ledger/templates/django_ledger/account/tags/accounts_table.html +27 -18
  40. django_ledger/templates/django_ledger/bills/bill_detail.html +3 -3
  41. django_ledger/templates/django_ledger/expense/tags/expense_item_table.html +7 -0
  42. django_ledger/templates/django_ledger/invoice/invoice_detail.html +3 -3
  43. django_ledger/templatetags/django_ledger.py +7 -1
  44. django_ledger/tests/base.py +23 -7
  45. django_ledger/tests/test_accounts.py +145 -9
  46. django_ledger/urls/account.py +17 -24
  47. django_ledger/utils.py +8 -0
  48. django_ledger/views/__init__.py +1 -1
  49. django_ledger/views/account.py +80 -118
  50. django_ledger/views/auth.py +1 -1
  51. django_ledger/views/bank_account.py +9 -11
  52. django_ledger/views/bill.py +91 -80
  53. django_ledger/views/closing_entry.py +8 -0
  54. django_ledger/views/coa.py +2 -1
  55. django_ledger/views/customer.py +1 -1
  56. django_ledger/views/data_import.py +1 -1
  57. django_ledger/views/entity.py +1 -1
  58. django_ledger/views/estimate.py +13 -8
  59. django_ledger/views/feedback.py +1 -1
  60. django_ledger/views/financial_statement.py +1 -1
  61. django_ledger/views/home.py +1 -1
  62. django_ledger/views/inventory.py +9 -0
  63. django_ledger/views/invoice.py +5 -2
  64. django_ledger/views/item.py +58 -68
  65. django_ledger/views/journal_entry.py +1 -1
  66. django_ledger/views/ledger.py +3 -1
  67. django_ledger/views/mixins.py +9 -8
  68. django_ledger/views/purchase_order.py +1 -1
  69. django_ledger/views/transactions.py +1 -1
  70. django_ledger/views/unit.py +9 -0
  71. django_ledger/views/vendor.py +1 -1
  72. {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/AUTHORS.md +8 -2
  73. {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/METADATA +34 -43
  74. {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/RECORD +77 -76
  75. {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/WHEEL +1 -1
  76. {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/LICENSE +0 -0
  77. {django_ledger-0.6.4.dist-info → django_ledger-0.7.0.dist-info}/top_level.txt +0 -0
@@ -3,12 +3,11 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
5
  Contributions to this module:
6
- Miguel Sanda <msanda@arrobalytics.com>
6
+ * Miguel Sanda <msanda@arrobalytics.com>
7
7
  """
8
8
  from django.contrib import messages
9
9
  from django.core.exceptions import ImproperlyConfigured, ValidationError
10
10
  from django.http import HttpResponseRedirect
11
- from django.shortcuts import get_object_or_404
12
11
  from django.urls import reverse
13
12
  from django.utils.translation import gettext as _
14
13
  from django.views.generic import ListView, UpdateView, CreateView, DetailView
@@ -17,7 +16,7 @@ from django.views.generic.detail import SingleObjectMixin
17
16
 
18
17
  from django_ledger.forms.account import AccountModelUpdateForm, AccountModelCreateForm
19
18
  from django_ledger.io.io_core import get_localdate
20
- from django_ledger.models import ChartOfAccountModel
19
+ from django_ledger.models import EntityModel, ChartOfAccountModel
21
20
  from django_ledger.models.accounts import AccountModel
22
21
  from django_ledger.views.mixins import (
23
22
  YearlyReportMixIn, MonthlyReportMixIn, QuarterlyReportMixIn, DjangoLedgerSecurityMixIn,
@@ -25,58 +24,51 @@ from django_ledger.views.mixins import (
25
24
  )
26
25
 
27
26
 
28
- class BaseAccountModelViewQuerySetMixIn:
27
+ class BaseAccountModelBaseView(DjangoLedgerSecurityMixIn):
29
28
  queryset = None
30
29
  coa_model = None
31
30
 
32
- def get_coa_model(self) -> ChartOfAccountModel:
33
- if self.coa_model is None:
34
- coa_slug = self.kwargs.get('coa_slug')
35
- if coa_slug:
36
- coa_model_qs = self.AUTHORIZED_ENTITY_MODEL.chartofaccountmodel_set.all().active()
37
- coa_model = get_object_or_404(coa_model_qs, slug__exact=coa_slug)
38
- else:
39
- coa_model = self.AUTHORIZED_ENTITY_MODEL.default_coa
40
- self.coa_model = coa_model
31
+ def get_authorized_entity_queryset(self):
32
+ qs = super().get_authorized_entity_queryset()
33
+ return qs.select_related('admin', 'default_coa', 'default_coa__entity')
34
+
35
+ def get_coa_model(self):
36
+ if not self.coa_model:
37
+ entity_model: EntityModel = self.get_authorized_entity_instance()
38
+ self.coa_model = entity_model.chartofaccountmodel_set.get(slug__exact=self.kwargs['coa_slug'])
41
39
  return self.coa_model
42
40
 
43
41
  def get_queryset(self):
44
42
  if self.queryset is None:
45
- qs = AccountModel.objects.for_entity(
46
- entity_slug=self.kwargs['entity_slug'],
47
- user_model=self.request.user,
48
- ).select_related(
43
+ entity_model: EntityModel = self.get_authorized_entity_instance()
44
+ coa_slug = self.kwargs['coa_slug']
45
+
46
+ coa_model, account_model_qs = entity_model.get_coa_accounts(
47
+ coa_model=entity_model.default_coa if coa_slug == entity_model.default_coa_slug else coa_slug,
48
+ return_coa_model=True,
49
+ active=False
50
+ )
51
+
52
+ account_model_qs = account_model_qs.select_related(
49
53
  'coa_model',
50
54
  'coa_model__entity'
51
55
  ).order_by(
52
- 'coa_model', 'role', 'code').not_coa_root()
56
+ 'coa_model', 'role', 'code'
57
+ ).not_coa_root()
53
58
 
54
- coa_slug = self.kwargs.get('coa_slug')
55
- account_pk = self.kwargs.get('account_pk')
56
-
57
- if coa_slug:
58
- qs = qs.filter(coa_model__slug__exact=coa_slug)
59
- elif account_pk:
60
- qs = qs.filter(uuid__exact=account_pk)
61
- else:
62
- qs = qs.filter(coa_model__slug__exact=self.AUTHORIZED_ENTITY_MODEL.default_coa.slug)
59
+ self.coa_model = coa_model
60
+ self.queryset = account_model_qs
63
61
 
64
- self.queryset = qs
65
62
  return super().get_queryset()
66
63
 
67
- def get_context_data(self, *args, **kwargs):
68
- context = super().get_context_data(*args, **kwargs)
69
- entity_model = self.get_authorized_entity_instance()
70
- if self.kwargs.get('coa_slug'):
71
- coa_model_qs = entity_model.chartofaccountmodel_set.all()
72
- context['coa_model'] = get_object_or_404(coa_model_qs, slug__exact=self.kwargs['coa_slug'])
73
- else:
74
- context['coa_model'] = entity_model.default_coa
64
+ def get_context_data(self, **kwargs):
65
+ context = super().get_context_data(**kwargs)
66
+ context['coa_model'] = self.get_coa_model()
75
67
  return context
76
68
 
77
69
 
78
70
  # Account Views ----
79
- class AccountModelListView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQuerySetMixIn, ListView):
71
+ class AccountModelListView(BaseAccountModelBaseView, ListView):
80
72
  template_name = 'django_ledger/account/account_list.html'
81
73
  context_object_name = 'accounts'
82
74
  PAGE_TITLE = _('Entity Accounts')
@@ -93,40 +85,7 @@ class AccountModelListView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQueryS
93
85
  return qs
94
86
 
95
87
 
96
- class AccountModelUpdateView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQuerySetMixIn, UpdateView):
97
- context_object_name = 'account'
98
- template_name = 'django_ledger/account/account_update.html'
99
- slug_url_kwarg = 'account_pk'
100
- slug_field = 'uuid'
101
-
102
- def get_context_data(self, **kwargs):
103
- context = super().get_context_data(**kwargs)
104
- context['page_title'] = _('Update Account')
105
- context['header_title'] = _(f'Update Account: {self.object.code} - {self.object.name}')
106
- context['header_subtitle_icon'] = 'ic:twotone-account-tree'
107
- return context
108
-
109
- def get_form(self, form_class=None):
110
- account_model = self.object
111
-
112
- # Set here because user_model is needed to instantiate an instance of MoveNodeForm (AccountModelUpdateForm)
113
- account_model.USER_MODEL = self.request.user
114
- return AccountModelUpdateForm(
115
- entity_model=self.AUTHORIZED_ENTITY_MODEL,
116
- coa_model=self.get_coa_model(),
117
- user_model=self.request.user,
118
- **self.get_form_kwargs()
119
- )
120
-
121
- def get_success_url(self):
122
- entity_slug = self.kwargs['entity_slug']
123
- return reverse('django_ledger:account-list',
124
- kwargs={
125
- 'entity_slug': entity_slug,
126
- })
127
-
128
-
129
- class AccountModelCreateView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQuerySetMixIn, CreateView):
88
+ class AccountModelCreateView(BaseAccountModelBaseView, CreateView):
130
89
  template_name = 'django_ledger/account/account_create.html'
131
90
  PAGE_TITLE = _('Create Account')
132
91
  extra_context = {
@@ -138,58 +97,63 @@ class AccountModelCreateView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQuer
138
97
 
139
98
  def get_form(self, form_class=None):
140
99
  return AccountModelCreateForm(
141
- user_model=self.request.user,
142
- entity_model=self.AUTHORIZED_ENTITY_MODEL,
143
100
  coa_model=self.get_coa_model(),
144
101
  **self.get_form_kwargs()
145
102
  )
146
103
 
147
104
  def get_context_data(self, *args, **kwargs):
148
105
  context = super().get_context_data(*args, **kwargs)
149
- context['header_subtitle'] = f'CoA: {context["coa_model"].name}'
106
+ coa_model = self.get_coa_model()
107
+ context['coa_model'] = coa_model
108
+ context['header_subtitle'] = f'CoA: {coa_model.name}'
150
109
  return context
151
110
 
152
- def form_valid(self, form):
153
- entity_model = self.AUTHORIZED_ENTITY_MODEL
111
+ def form_valid(self, form: AccountModelCreateForm):
154
112
  account_model: AccountModel = form.save(commit=False)
155
-
156
- if not entity_model.has_default_coa():
157
- entity_model.create_chart_of_accounts(assign_as_default=True, commit=True)
158
-
159
- coa_model = self.get_coa_model()
113
+ coa_model = account_model.coa_model
160
114
  coa_model.insert_account(account_model=account_model)
161
115
  return HttpResponseRedirect(self.get_success_url())
162
116
 
163
117
  def get_success_url(self):
164
- entity_slug = self.kwargs.get('entity_slug')
165
- coa_slug = self.kwargs.get('coa_slug')
166
- if coa_slug:
167
- return reverse('django_ledger:account-list-coa',
168
- kwargs={
169
- 'entity_slug': entity_slug,
170
- 'coa_slug': coa_slug
171
- })
172
- return reverse('django_ledger:account-list',
173
- kwargs={
174
- 'entity_slug': entity_slug,
175
- })
118
+ coa_model: ChartOfAccountModel = self.get_coa_model()
119
+ return coa_model.get_account_list_url()
120
+
121
+
122
+ class AccountModelUpdateView(BaseAccountModelBaseView, UpdateView):
123
+ context_object_name = 'account'
124
+ template_name = 'django_ledger/account/account_update.html'
125
+ slug_url_kwarg = 'account_pk'
126
+ slug_field = 'uuid'
127
+ form_class = AccountModelUpdateForm
128
+
129
+ def get_context_data(self, **kwargs):
130
+ context = super().get_context_data(**kwargs)
131
+ context['page_title'] = _('Update Account')
132
+ context['header_title'] = _(f'Update Account: {self.object.code} - {self.object.name}')
133
+ context['header_subtitle_icon'] = 'ic:twotone-account-tree'
134
+ return context
135
+
136
+ def get_success_url(self):
137
+ coa_model: ChartOfAccountModel = self.get_coa_model()
138
+ return coa_model.get_account_list_url()
176
139
 
177
140
 
178
- class AccountModelDetailView(DjangoLedgerSecurityMixIn, BaseAccountModelViewQuerySetMixIn, RedirectView):
141
+ class AccountModelDetailView(BaseAccountModelBaseView, RedirectView):
179
142
 
180
143
  def get_redirect_url(self, *args, **kwargs):
181
144
  loc_date = get_localdate()
145
+ entity_model: EntityModel = self.get_authorized_entity_instance()
182
146
  return reverse('django_ledger:account-detail-month',
183
147
  kwargs={
184
- 'entity_slug': self.kwargs['entity_slug'],
148
+ 'entity_slug': entity_model.slug,
185
149
  'account_pk': self.kwargs['account_pk'],
150
+ 'coa_slug': self.kwargs['coa_slug'],
186
151
  'year': loc_date.year,
187
152
  'month': loc_date.month,
188
153
  })
189
154
 
190
155
 
191
- class AccountModelYearDetailView(DjangoLedgerSecurityMixIn,
192
- BaseAccountModelViewQuerySetMixIn,
156
+ class AccountModelYearDetailView(BaseAccountModelBaseView,
193
157
  BaseDateNavigationUrlMixIn,
194
158
  EntityUnitMixIn,
195
159
  YearlyReportMixIn,
@@ -205,23 +169,23 @@ class AccountModelYearDetailView(DjangoLedgerSecurityMixIn,
205
169
  }
206
170
 
207
171
  def get_context_data(self, **kwargs):
208
- account = self.object
209
172
  context = super().get_context_data(**kwargs)
210
- context['header_title'] = f'Account {account.code} - {account.name}'
211
- context['page_title'] = f'Account {account.code} - {account.name}'
212
- account_model: AccountModel = self.object
173
+ account_model: AccountModel = context['object']
174
+ context['header_title'] = f'Account {account_model.code} - {account_model.name}'
175
+ context['page_title'] = f'Account {account_model.code} - {account_model.name}'
213
176
  txs_qs = account_model.transactionmodel_set.all().posted().order_by(
214
- 'journal_entry__timestamp').select_related(
215
- 'journal_entry', 'journal_entry__entity_unit')
177
+ 'journal_entry__timestamp'
178
+ ).select_related(
179
+ 'journal_entry',
180
+ 'journal_entry__entity_unit',
181
+ 'journal_entry__ledger__billmodel',
182
+ 'journal_entry__ledger__invoicemodel',
183
+ )
216
184
  txs_qs = txs_qs.from_date(self.get_from_date())
217
185
  txs_qs = txs_qs.to_date(self.get_to_date())
218
186
  context['transactions'] = txs_qs
219
187
  return context
220
188
 
221
- def get_queryset(self):
222
- qs = super().get_queryset()
223
- return qs.prefetch_related('transactionmodel_set')
224
-
225
189
 
226
190
  class AccountModelQuarterDetailView(QuarterlyReportMixIn, AccountModelYearDetailView):
227
191
  """
@@ -242,9 +206,8 @@ class AccountModelDateDetailView(DateReportMixIn, AccountModelYearDetailView):
242
206
 
243
207
 
244
208
  # ACTIONS...
245
- class AccountModelModelActionView(DjangoLedgerSecurityMixIn,
209
+ class AccountModelModelActionView(BaseAccountModelBaseView,
246
210
  RedirectView,
247
- BaseAccountModelViewQuerySetMixIn,
248
211
  SingleObjectMixin):
249
212
  http_method_names = ['get']
250
213
  pk_url_kwarg = 'account_pk'
@@ -252,11 +215,8 @@ class AccountModelModelActionView(DjangoLedgerSecurityMixIn,
252
215
  commit = True
253
216
 
254
217
  def get_redirect_url(self, *args, **kwargs):
255
- return reverse('django_ledger:account-list',
256
- kwargs={
257
- 'entity_slug': kwargs['entity_slug'],
258
- # 'account_pk': kwargs['account_pk']
259
- })
218
+ account_model: AccountModel = self.get_object()
219
+ return account_model.get_coa_account_list_url()
260
220
 
261
221
  def get(self, request, *args, **kwargs):
262
222
  kwargs['user_model'] = self.request.user
@@ -268,8 +228,10 @@ class AccountModelModelActionView(DjangoLedgerSecurityMixIn,
268
228
  try:
269
229
  getattr(account_model, self.action_name)(commit=self.commit, **kwargs)
270
230
  except ValidationError as e:
271
- messages.add_message(request,
272
- message=e.message,
273
- level=messages.ERROR,
274
- extra_tags='is-danger')
231
+ messages.add_message(
232
+ request,
233
+ message=e.message,
234
+ level=messages.ERROR,
235
+ extra_tags='is-danger'
236
+ )
275
237
  return response
@@ -3,7 +3,7 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
5
  Contributions to this module:
6
- Miguel Sanda <msanda@arrobalytics.com>
6
+ * Miguel Sanda <msanda@arrobalytics.com>
7
7
  """
8
8
 
9
9
  from django.contrib.auth.views import LoginView, LogoutView
@@ -3,7 +3,7 @@ Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
3
3
  Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
4
4
 
5
5
  Contributions to this module:
6
- Miguel Sanda <msanda@arrobalytics.com>
6
+ * Miguel Sanda <msanda@arrobalytics.com>
7
7
  """
8
8
  from django.contrib import messages
9
9
  from django.core.exceptions import ImproperlyConfigured, ValidationError
@@ -13,23 +13,22 @@ from django.views.generic import ListView, CreateView, UpdateView, RedirectView
13
13
  from django.views.generic.detail import SingleObjectMixin
14
14
 
15
15
  from django_ledger.forms.bank_account import BankAccountCreateForm, BankAccountUpdateForm
16
+ from django_ledger.models import EntityModel
16
17
  from django_ledger.models.bank_account import BankAccountModel
17
18
  from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
18
19
 
19
20
 
20
- class BankAccountModelModelViewQuerySetMixIn:
21
+ class BankAccountModelModelBaseView(DjangoLedgerSecurityMixIn):
21
22
  queryset = None
22
23
 
23
24
  def get_queryset(self):
24
25
  if self.queryset is None:
25
- self.queryset = BankAccountModel.objects.for_entity(
26
- entity_slug=self.kwargs['entity_slug'],
27
- user_model=self.request.user
28
- ).select_related('cash_account', 'entity_model')
26
+ entity_model: EntityModel = self.get_authorized_entity_instance()
27
+ self.queryset = entity_model.bankaccountmodel_set.select_related('cash_account', 'entity_model')
29
28
  return super().get_queryset()
30
29
 
31
30
 
32
- class BankAccountModelListView(DjangoLedgerSecurityMixIn, BankAccountModelModelViewQuerySetMixIn, ListView):
31
+ class BankAccountModelListView(BankAccountModelModelBaseView, ListView):
33
32
  template_name = 'django_ledger/bank_account/bank_account_list.html'
34
33
  PAGE_TITLE = _('Bank Accounts')
35
34
  context_object_name = 'bank_accounts'
@@ -40,7 +39,7 @@ class BankAccountModelListView(DjangoLedgerSecurityMixIn, BankAccountModelModelV
40
39
  }
41
40
 
42
41
 
43
- class BankAccountModelCreateView(DjangoLedgerSecurityMixIn, BankAccountModelModelViewQuerySetMixIn, CreateView):
42
+ class BankAccountModelCreateView(BankAccountModelModelBaseView, CreateView):
44
43
  template_name = 'django_ledger/bank_account/bank_account_create.html'
45
44
  PAGE_TITLE = _('Create Bank Account')
46
45
  extra_context = {
@@ -71,7 +70,7 @@ class BankAccountModelCreateView(DjangoLedgerSecurityMixIn, BankAccountModelMode
71
70
  return super(BankAccountModelCreateView, self).form_valid(form)
72
71
 
73
72
 
74
- class BankAccountModelUpdateView(DjangoLedgerSecurityMixIn, BankAccountModelModelViewQuerySetMixIn, UpdateView):
73
+ class BankAccountModelUpdateView(BankAccountModelModelBaseView, UpdateView):
75
74
  template_name = 'django_ledger/bank_account/bank_account_update.html'
76
75
  pk_url_kwarg = 'bank_account_pk'
77
76
  PAGE_TITLE = _('Update Bank Account')
@@ -97,8 +96,7 @@ class BankAccountModelUpdateView(DjangoLedgerSecurityMixIn, BankAccountModelMode
97
96
 
98
97
 
99
98
  # ACTION VIEWS...
100
- class BaseBankAccountModelActionView(DjangoLedgerSecurityMixIn,
101
- BankAccountModelModelViewQuerySetMixIn,
99
+ class BaseBankAccountModelActionView(BankAccountModelModelBaseView,
102
100
  RedirectView,
103
101
  SingleObjectMixin):
104
102
  http_method_names = ['get']