django-ledger 0.7.11__py3-none-any.whl → 0.8.1__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 (139) hide show
  1. django_ledger/__init__.py +1 -1
  2. django_ledger/context.py +12 -0
  3. django_ledger/forms/account.py +45 -46
  4. django_ledger/forms/bill.py +0 -4
  5. django_ledger/forms/closing_entry.py +13 -1
  6. django_ledger/forms/data_import.py +182 -63
  7. django_ledger/forms/estimate.py +3 -6
  8. django_ledger/forms/invoice.py +3 -7
  9. django_ledger/forms/item.py +10 -18
  10. django_ledger/forms/purchase_order.py +2 -4
  11. django_ledger/io/io_core.py +515 -400
  12. django_ledger/io/io_generator.py +7 -6
  13. django_ledger/io/io_library.py +1 -2
  14. django_ledger/migrations/0025_alter_billmodel_cash_account_and_more.py +70 -0
  15. django_ledger/migrations/0026_stagedtransactionmodel_customer_model_and_more.py +56 -0
  16. django_ledger/models/__init__.py +2 -1
  17. django_ledger/models/accounts.py +109 -69
  18. django_ledger/models/bank_account.py +40 -23
  19. django_ledger/models/bill.py +386 -333
  20. django_ledger/models/chart_of_accounts.py +173 -105
  21. django_ledger/models/closing_entry.py +99 -48
  22. django_ledger/models/customer.py +100 -66
  23. django_ledger/models/data_import.py +818 -323
  24. django_ledger/models/deprecations.py +61 -0
  25. django_ledger/models/entity.py +891 -644
  26. django_ledger/models/estimate.py +57 -28
  27. django_ledger/models/invoice.py +46 -26
  28. django_ledger/models/items.py +503 -142
  29. django_ledger/models/journal_entry.py +61 -47
  30. django_ledger/models/ledger.py +106 -42
  31. django_ledger/models/mixins.py +424 -281
  32. django_ledger/models/purchase_order.py +39 -17
  33. django_ledger/models/receipt.py +1083 -0
  34. django_ledger/models/transactions.py +242 -139
  35. django_ledger/models/unit.py +93 -54
  36. django_ledger/models/utils.py +12 -2
  37. django_ledger/models/vendor.py +121 -70
  38. django_ledger/report/core.py +2 -14
  39. django_ledger/settings.py +57 -71
  40. django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
  41. django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +25 -0
  42. django_ledger/static/django_ledger/bundle/styles.bundle.js +1 -1
  43. django_ledger/static/django_ledger/css/djl_styles.css +273 -0
  44. django_ledger/templates/django_ledger/bills/includes/card_bill.html +2 -2
  45. django_ledger/templates/django_ledger/components/menu.html +41 -26
  46. django_ledger/templates/django_ledger/components/period_navigator.html +5 -3
  47. django_ledger/templates/django_ledger/customer/customer_detail.html +87 -0
  48. django_ledger/templates/django_ledger/customer/customer_list.html +0 -1
  49. django_ledger/templates/django_ledger/customer/tags/customer_table.html +8 -6
  50. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +24 -3
  51. django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +26 -10
  52. django_ledger/templates/django_ledger/entity/entity_dashboard.html +2 -2
  53. django_ledger/templates/django_ledger/entity/includes/card_entity.html +12 -6
  54. django_ledger/templates/django_ledger/financial_statements/balance_sheet.html +1 -1
  55. django_ledger/templates/django_ledger/financial_statements/cash_flow.html +4 -1
  56. django_ledger/templates/django_ledger/financial_statements/income_statement.html +4 -1
  57. django_ledger/templates/django_ledger/financial_statements/tags/balance_sheet_statement.html +27 -3
  58. django_ledger/templates/django_ledger/financial_statements/tags/cash_flow_statement.html +16 -4
  59. django_ledger/templates/django_ledger/financial_statements/tags/income_statement.html +73 -18
  60. django_ledger/templates/django_ledger/includes/widget_ratios.html +18 -24
  61. django_ledger/templates/django_ledger/invoice/includes/card_invoice.html +3 -3
  62. django_ledger/templates/django_ledger/layouts/base.html +7 -2
  63. django_ledger/templates/django_ledger/layouts/content_layout_1.html +1 -1
  64. django_ledger/templates/django_ledger/receipt/customer_receipt_report.html +115 -0
  65. django_ledger/templates/django_ledger/receipt/receipt_delete.html +30 -0
  66. django_ledger/templates/django_ledger/receipt/receipt_detail.html +89 -0
  67. django_ledger/templates/django_ledger/receipt/receipt_list.html +134 -0
  68. django_ledger/templates/django_ledger/receipt/vendor_receipt_report.html +115 -0
  69. django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +12 -7
  70. django_ledger/templates/django_ledger/vendor/vendor_detail.html +86 -0
  71. django_ledger/templatetags/django_ledger.py +338 -191
  72. django_ledger/tests/test_accounts.py +1 -2
  73. django_ledger/tests/test_io.py +17 -0
  74. django_ledger/tests/test_purchase_order.py +3 -3
  75. django_ledger/tests/test_transactions.py +1 -2
  76. django_ledger/urls/__init__.py +1 -4
  77. django_ledger/urls/customer.py +3 -0
  78. django_ledger/urls/data_import.py +3 -0
  79. django_ledger/urls/receipt.py +102 -0
  80. django_ledger/urls/vendor.py +1 -0
  81. django_ledger/views/__init__.py +1 -0
  82. django_ledger/views/bill.py +8 -11
  83. django_ledger/views/chart_of_accounts.py +6 -4
  84. django_ledger/views/closing_entry.py +11 -7
  85. django_ledger/views/customer.py +68 -30
  86. django_ledger/views/data_import.py +120 -66
  87. django_ledger/views/djl_api.py +3 -5
  88. django_ledger/views/entity.py +2 -4
  89. django_ledger/views/estimate.py +3 -7
  90. django_ledger/views/inventory.py +3 -5
  91. django_ledger/views/invoice.py +4 -6
  92. django_ledger/views/item.py +7 -11
  93. django_ledger/views/journal_entry.py +1 -2
  94. django_ledger/views/mixins.py +125 -93
  95. django_ledger/views/purchase_order.py +24 -35
  96. django_ledger/views/receipt.py +294 -0
  97. django_ledger/views/unit.py +1 -2
  98. django_ledger/views/vendor.py +54 -16
  99. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/METADATA +43 -75
  100. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/RECORD +104 -122
  101. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info}/WHEEL +1 -1
  102. django_ledger-0.8.1.dist-info/top_level.txt +1 -0
  103. django_ledger/contrib/django_ledger_graphene/__init__.py +0 -0
  104. django_ledger/contrib/django_ledger_graphene/accounts/schema.py +0 -33
  105. django_ledger/contrib/django_ledger_graphene/api.py +0 -42
  106. django_ledger/contrib/django_ledger_graphene/apps.py +0 -6
  107. django_ledger/contrib/django_ledger_graphene/auth/mutations.py +0 -49
  108. django_ledger/contrib/django_ledger_graphene/auth/schema.py +0 -6
  109. django_ledger/contrib/django_ledger_graphene/bank_account/mutations.py +0 -61
  110. django_ledger/contrib/django_ledger_graphene/bank_account/schema.py +0 -34
  111. django_ledger/contrib/django_ledger_graphene/bill/mutations.py +0 -0
  112. django_ledger/contrib/django_ledger_graphene/bill/schema.py +0 -34
  113. django_ledger/contrib/django_ledger_graphene/coa/mutations.py +0 -0
  114. django_ledger/contrib/django_ledger_graphene/coa/schema.py +0 -30
  115. django_ledger/contrib/django_ledger_graphene/customers/__init__.py +0 -0
  116. django_ledger/contrib/django_ledger_graphene/customers/mutations.py +0 -71
  117. django_ledger/contrib/django_ledger_graphene/customers/schema.py +0 -43
  118. django_ledger/contrib/django_ledger_graphene/data_import/mutations.py +0 -0
  119. django_ledger/contrib/django_ledger_graphene/data_import/schema.py +0 -0
  120. django_ledger/contrib/django_ledger_graphene/entity/mutations.py +0 -0
  121. django_ledger/contrib/django_ledger_graphene/entity/schema.py +0 -94
  122. django_ledger/contrib/django_ledger_graphene/item/mutations.py +0 -0
  123. django_ledger/contrib/django_ledger_graphene/item/schema.py +0 -31
  124. django_ledger/contrib/django_ledger_graphene/journal_entry/mutations.py +0 -0
  125. django_ledger/contrib/django_ledger_graphene/journal_entry/schema.py +0 -35
  126. django_ledger/contrib/django_ledger_graphene/ledger/mutations.py +0 -0
  127. django_ledger/contrib/django_ledger_graphene/ledger/schema.py +0 -32
  128. django_ledger/contrib/django_ledger_graphene/purchase_order/mutations.py +0 -0
  129. django_ledger/contrib/django_ledger_graphene/purchase_order/schema.py +0 -31
  130. django_ledger/contrib/django_ledger_graphene/transaction/mutations.py +0 -0
  131. django_ledger/contrib/django_ledger_graphene/transaction/schema.py +0 -36
  132. django_ledger/contrib/django_ledger_graphene/unit/mutations.py +0 -0
  133. django_ledger/contrib/django_ledger_graphene/unit/schema.py +0 -27
  134. django_ledger/contrib/django_ledger_graphene/vendor/mutations.py +0 -0
  135. django_ledger/contrib/django_ledger_graphene/vendor/schema.py +0 -37
  136. django_ledger/contrib/django_ledger_graphene/views.py +0 -12
  137. django_ledger-0.7.11.dist-info/top_level.txt +0 -4
  138. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info/licenses}/AUTHORS.md +0 -0
  139. {django_ledger-0.7.11.dist-info → django_ledger-0.8.1.dist-info/licenses}/LICENSE +0 -0
@@ -11,17 +11,21 @@ from datetime import timedelta, date
11
11
  from typing import Tuple, Optional
12
12
 
13
13
  from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
14
- from django.core.exceptions import ValidationError, ObjectDoesNotExist, ImproperlyConfigured
14
+ from django.core.exceptions import (
15
+ ValidationError,
16
+ ObjectDoesNotExist,
17
+ ImproperlyConfigured,
18
+ )
15
19
  from django.db.models import Q
16
- from django.http import Http404, HttpResponse, HttpResponseNotFound
20
+ from django.http import Http404, HttpResponse
17
21
  from django.urls import reverse
18
22
  from django.utils.dateparse import parse_date
19
23
  from django.utils.translation import gettext_lazy as _
20
24
  from django.views.generic.dates import YearMixin, MonthMixin, DayMixin
21
25
 
22
- from django_ledger.models import EntityModel, InvoiceModel, BillModel
26
+ from django_ledger.models import EntityModel, InvoiceModel, BillModel, LedgerModel
23
27
  from django_ledger.models.entity import EntityModelFiscalPeriodMixIn
24
- from django_ledger.settings import DJANGO_LEDGER_PDF_SUPPORT_ENABLED, DJANGO_LEDGER_AUTHORIZED_SUPERUSER
28
+ from django_ledger.settings import DJANGO_LEDGER_AUTHORIZED_SUPERUSER
25
29
 
26
30
 
27
31
  class ContextFromToDateMixin:
@@ -35,15 +39,18 @@ class ContextFromToDateMixin:
35
39
  return self.TO_DATE_CONTEXT_NAME
36
40
 
37
41
 
38
- class YearlyReportMixIn(YearMixin, ContextFromToDateMixin, EntityModelFiscalPeriodMixIn):
39
-
42
+ class YearlyReportMixIn(
43
+ YearMixin, ContextFromToDateMixin, EntityModelFiscalPeriodMixIn
44
+ ):
40
45
  def get_from_date(self, year: int = None, fy_start: int = None, **kwargs) -> date:
41
46
  return self.get_year_start_date(year, fy_start)
42
47
 
43
48
  def get_to_date(self, year: int = None, fy_start: int = None, **kwargs) -> date:
44
49
  return self.get_year_end_date(year, fy_start)
45
50
 
46
- def get_from_to_dates(self, year: int = None, fy_start: int = None, **kwargs) -> Tuple[date, date]:
51
+ def get_from_to_dates(
52
+ self, year: int = None, fy_start: int = None, **kwargs
53
+ ) -> Tuple[date, date]:
47
54
  from_date = self.get_from_date(year, fy_start, **kwargs)
48
55
  to_date = self.get_to_date(year, fy_start, **kwargs)
49
56
  return from_date, to_date
@@ -78,7 +85,9 @@ class YearlyReportMixIn(YearMixin, ContextFromToDateMixin, EntityModelFiscalPeri
78
85
  return context
79
86
 
80
87
 
81
- class QuarterlyReportMixIn(YearMixin, ContextFromToDateMixin, EntityModelFiscalPeriodMixIn):
88
+ class QuarterlyReportMixIn(
89
+ YearMixin, ContextFromToDateMixin, EntityModelFiscalPeriodMixIn
90
+ ):
82
91
  quarter = None
83
92
  quarter_url_kwarg = 'quarter'
84
93
 
@@ -89,9 +98,11 @@ class QuarterlyReportMixIn(YearMixin, ContextFromToDateMixin, EntityModelFiscalP
89
98
  try:
90
99
  self.validate_quarter(quarter)
91
100
  except ValidationError:
92
- raise Http404(_("Invalid quarter number"))
101
+ raise Http404(_('Invalid quarter number'))
93
102
  except ValueError:
94
- raise Http404(_(f"Invalid quarter format. Cannot parse {quarter} into integer."))
103
+ raise Http404(
104
+ _(f'Invalid quarter format. Cannot parse {quarter} into integer.')
105
+ )
95
106
  return quarter
96
107
 
97
108
  def get_quarter(self) -> int:
@@ -103,33 +114,43 @@ class QuarterlyReportMixIn(YearMixin, ContextFromToDateMixin, EntityModelFiscalP
103
114
  try:
104
115
  quarter = self.request.GET[self.quarter_url_kwarg]
105
116
  except KeyError:
106
- raise Http404(_("No quarter specified"))
117
+ raise Http404(_('No quarter specified'))
107
118
  quarter = self.parse_quarter(quarter)
108
119
  return quarter
109
120
 
110
- def get_from_date(self, quarter: int = None, year: int = None, fy_start: int = None, **kwargs) -> date:
121
+ def get_from_date(
122
+ self, quarter: int = None, year: int = None, fy_start: int = None, **kwargs
123
+ ) -> date:
111
124
  return self.get_quarter_start_date(quarter, year, fy_start)
112
125
 
113
- def get_to_date(self, quarter: int = None, year: int = None, fy_start: int = None, **kwargs) -> date:
126
+ def get_to_date(
127
+ self, quarter: int = None, year: int = None, fy_start: int = None, **kwargs
128
+ ) -> date:
114
129
  return self.get_quarter_end_date(quarter, year, fy_start)
115
130
 
116
- def get_from_to_dates(self,
117
- quarter: int = None,
118
- year: int = None,
119
- fy_start: int = None,
120
- **kwargs) -> Tuple[date, date]:
121
- from_date = self.get_from_date(quarter=quarter, year=year, fy_start=fy_start, **kwargs)
122
- to_date = self.get_to_date(quarter=quarter, year=year, fy_start=fy_start, **kwargs)
131
+ def get_from_to_dates(
132
+ self, quarter: int = None, year: int = None, fy_start: int = None, **kwargs
133
+ ) -> Tuple[date, date]:
134
+ from_date = self.get_from_date(
135
+ quarter=quarter, year=year, fy_start=fy_start, **kwargs
136
+ )
137
+ to_date = self.get_to_date(
138
+ quarter=quarter, year=year, fy_start=fy_start, **kwargs
139
+ )
123
140
  return from_date, to_date
124
141
 
125
- def get_quarter_start_date(self, quarter: int = None, year: int = None, fy_start: int = None) -> date:
142
+ def get_quarter_start_date(
143
+ self, quarter: int = None, year: int = None, fy_start: int = None
144
+ ) -> date:
126
145
  if not year:
127
146
  year = self.get_year()
128
147
  if not quarter:
129
148
  quarter = self.get_quarter()
130
149
  return self.get_quarter_start(year, quarter, fy_start)
131
150
 
132
- def get_quarter_end_date(self, quarter: int = None, year: int = None, fy_start: int = None) -> date:
151
+ def get_quarter_end_date(
152
+ self, quarter: int = None, year: int = None, fy_start: int = None
153
+ ) -> date:
133
154
  if not year:
134
155
  year = self.get_year()
135
156
  if not quarter:
@@ -162,17 +183,15 @@ class QuarterlyReportMixIn(YearMixin, ContextFromToDateMixin, EntityModelFiscalP
162
183
 
163
184
 
164
185
  class MonthlyReportMixIn(YearlyReportMixIn, ContextFromToDateMixin, MonthMixin):
165
-
166
186
  def get_from_date(self, month: int = None, year: int = None, **kwargs) -> date:
167
187
  return self.get_month_start_date(month=month, year=year)
168
188
 
169
189
  def get_to_date(self, month: int = None, year: int = None, **kwargs) -> date:
170
190
  return self.get_month_end_date(month=month, year=year)
171
191
 
172
- def get_from_to_dates(self,
173
- month: int = None,
174
- year: int = None,
175
- **kwargs) -> Tuple[date, date]:
192
+ def get_from_to_dates(
193
+ self, month: int = None, year: int = None, **kwargs
194
+ ) -> Tuple[date, date]:
176
195
  from_date = self.get_from_date(month=month, year=year, **kwargs)
177
196
  to_date = self.get_to_date(month=month, year=year, **kwargs)
178
197
  return from_date, to_date
@@ -220,7 +239,6 @@ class MonthlyReportMixIn(YearlyReportMixIn, ContextFromToDateMixin, MonthMixin):
220
239
 
221
240
 
222
241
  class DateReportMixIn(MonthlyReportMixIn, ContextFromToDateMixin, DayMixin):
223
-
224
242
  def get_context_data(self, **kwargs):
225
243
  context = super(MonthlyReportMixIn, self).get_context_data(**kwargs)
226
244
  view_date = self.get_date()
@@ -233,11 +251,7 @@ class DateReportMixIn(MonthlyReportMixIn, ContextFromToDateMixin, DayMixin):
233
251
  return context
234
252
 
235
253
  def get_date(self) -> date:
236
- return date(
237
- year=self.get_year(),
238
- month=self.get_month(),
239
- day=self.get_day()
240
- )
254
+ return date(year=self.get_year(), month=self.get_month(), day=self.get_day())
241
255
 
242
256
  def get_from_date(self, month: int = None, year: int = None, **kwargs) -> date:
243
257
  return self.get_date()
@@ -245,7 +259,9 @@ class DateReportMixIn(MonthlyReportMixIn, ContextFromToDateMixin, DayMixin):
245
259
  def get_to_date(self, month: int = None, year: int = None, **kwargs) -> date:
246
260
  return self.get_date()
247
261
 
248
- def get_from_to_dates(self, month: int = None, year: int = None, **kwargs) -> Tuple[date, date]:
262
+ def get_from_to_dates(
263
+ self, month: int = None, year: int = None, **kwargs
264
+ ) -> Tuple[date, date]:
249
265
  dt = self.get_from_date(month=month, year=year, **kwargs)
250
266
  return dt, dt
251
267
 
@@ -289,7 +305,6 @@ class FromToDatesParseMixIn:
289
305
 
290
306
 
291
307
  class SuccessUrlNextMixIn:
292
-
293
308
  def has_next_url(self):
294
309
  return self.request.GET.get('next') is not None
295
310
 
@@ -312,7 +327,9 @@ class DjangoLedgerSecurityMixIn(LoginRequiredMixin, PermissionRequiredMixin):
312
327
 
313
328
  def get_context_data(self, **kwargs):
314
329
  context = super().get_context_data(**kwargs)
315
- context[self.ENTITY_MODEL_CONTEXT_NAME] = self.get_authorized_entity_instance(raise_exception=False)
330
+ context[self.ENTITY_MODEL_CONTEXT_NAME] = self.get_authorized_entity_instance(
331
+ raise_exception=False
332
+ )
316
333
  return context
317
334
 
318
335
  def get_login_url(self):
@@ -323,9 +340,7 @@ class DjangoLedgerSecurityMixIn(LoginRequiredMixin, PermissionRequiredMixin):
323
340
 
324
341
  def get_entity_slug_kwarg(self):
325
342
  if self.ENTITY_SLUG_URL_KWARG is None:
326
- raise ImproperlyConfigured(
327
- _('ENTITY_SLUG_URL_KWARG must be provided.')
328
- )
343
+ raise ImproperlyConfigured(_('ENTITY_SLUG_URL_KWARG must be provided.'))
329
344
  return self.ENTITY_SLUG_URL_KWARG
330
345
 
331
346
  def get_superuser_authorization(self):
@@ -342,7 +357,9 @@ class DjangoLedgerSecurityMixIn(LoginRequiredMixin, PermissionRequiredMixin):
342
357
  if self.request.user.is_authenticated:
343
358
  if entity_slug_kwarg in self.kwargs:
344
359
  try:
345
- self.AUTHORIZED_ENTITY_MODEL = entity_model_qs.get(slug__exact=self.kwargs[entity_slug_kwarg])
360
+ self.AUTHORIZED_ENTITY_MODEL = entity_model_qs.get(
361
+ slug__exact=self.kwargs[entity_slug_kwarg]
362
+ )
346
363
  except ObjectDoesNotExist:
347
364
  return False
348
365
  return True
@@ -354,7 +371,9 @@ class DjangoLedgerSecurityMixIn(LoginRequiredMixin, PermissionRequiredMixin):
354
371
  authorized_superuser=self.get_superuser_authorization(),
355
372
  )
356
373
 
357
- def get_authorized_entity_instance(self, raise_exception: bool = True) -> Optional[EntityModel]:
374
+ def get_authorized_entity_instance(
375
+ self, raise_exception: bool = True
376
+ ) -> Optional[EntityModel]:
358
377
  if self.AUTHORIZED_ENTITY_MODEL is None:
359
378
  if raise_exception:
360
379
  raise Http404()
@@ -383,10 +402,9 @@ class EntityUnitMixIn:
383
402
  unit_slug = self.get_unit_slug()
384
403
  context['unit_slug'] = unit_slug
385
404
 
386
- by_unit = any([
387
- True if unit_slug else False,
388
- self.request.GET.get('by_unit') is not None
389
- ])
405
+ by_unit = any(
406
+ [True if unit_slug else False, self.request.GET.get('by_unit') is not None]
407
+ )
390
408
 
391
409
  context['by_unit'] = by_unit
392
410
  return context
@@ -418,17 +436,10 @@ class DigestContextMixIn:
418
436
  context = super(DigestContextMixIn, self).get_context_data(**kwargs)
419
437
  return self.get_io_digest(context=context, **kwargs)
420
438
 
421
- def get_io_digest(self,
422
- context,
423
- from_date=None,
424
- to_date=None,
425
- **kwargs):
426
-
427
- if any([self.IO_DIGEST_UNBOUNDED,
428
- self.IO_DIGEST_BOUNDED]):
429
-
439
+ def get_io_digest(self, context, from_date=None, to_date=None, **kwargs):
440
+ if any([self.IO_DIGEST_UNBOUNDED, self.IO_DIGEST_BOUNDED]):
430
441
  by_period = self.request.GET.get('by_period')
431
- entity_model: EntityModel = self.object
442
+ io_model: EntityModel | LedgerModel = self.object
432
443
  if not to_date:
433
444
  to_date = context['to_date']
434
445
  if not from_date:
@@ -441,19 +452,26 @@ class DigestContextMixIn:
441
452
  unit_slug = None
442
453
 
443
454
  if self.IO_DIGEST_UNBOUNDED:
444
- io_digest = entity_model.digest(user_model=self.request.user,
445
- to_date=to_date,
446
- unit_slug=unit_slug,
447
- by_period=True if by_period else False,
448
- process_ratios=True,
449
- process_roles=True,
450
- process_groups=True)
455
+ io_digest = io_model.digest(
456
+ user_model=self.request.user,
457
+ entity_slug=io_model.entity_slug
458
+ if isinstance(io_model, LedgerModel)
459
+ else None,
460
+ to_date=to_date,
461
+ unit_slug=unit_slug,
462
+ by_period=True if by_period else False,
463
+ process_ratios=True,
464
+ process_roles=True,
465
+ process_groups=True,
466
+ )
451
467
 
452
468
  context[self.get_io_manager_unbounded_context_name()] = io_digest
453
- context[self.get_io_digest_unbounded_context_name()] = io_digest.get_io_data()
469
+ context[self.get_io_digest_unbounded_context_name()] = (
470
+ io_digest.get_io_data()
471
+ )
454
472
 
455
473
  if self.IO_DIGEST_BOUNDED:
456
- io_digest_equity = entity_model.digest(
474
+ io_digest_equity = io_model.digest(
457
475
  user_model=self.request.user,
458
476
  equity_only=True,
459
477
  to_date=to_date,
@@ -462,11 +480,13 @@ class DigestContextMixIn:
462
480
  by_period=True if by_period else False,
463
481
  process_ratios=True,
464
482
  process_roles=True,
465
- process_groups=True
483
+ process_groups=True,
466
484
  )
467
485
 
468
486
  context[self.get_io_manager_bounded_context_name()] = io_digest_equity
469
- context[self.get_io_digest_bounded_context_name()] = io_digest_equity.get_io_data()
487
+ context[self.get_io_digest_bounded_context_name()] = (
488
+ io_digest_equity.get_io_data()
489
+ )
470
490
 
471
491
  # todo: how is this used??....
472
492
  context['date_filter'] = to_date
@@ -488,17 +508,20 @@ class UnpaidElementsMixIn:
488
508
  from_date = context['from_date'] if not from_date else from_date
489
509
  to_date = context['to_date'] if not to_date else to_date
490
510
 
491
- qs = InvoiceModel.objects.for_entity(
492
- user_model=self.request.user,
493
- entity_slug=self.kwargs['entity_slug']
494
- ).approved().filter(
495
- Q(date_approved__gte=from_date) &
496
- Q(date_approved__lte=to_date)
497
- ).select_related('customer').order_by('date_due')
511
+ qs = (
512
+ InvoiceModel.objects.for_entity(entity_model=self.kwargs['entity_slug'])
513
+ .for_user(user_model=self.request.user)
514
+ .approved()
515
+ .filter(Q(date_approved__gte=from_date) & Q(date_approved__lte=to_date))
516
+ .select_related('customer')
517
+ .order_by('date_due')
518
+ )
498
519
 
499
520
  unit_slug = self.get_unit_slug()
500
521
  if unit_slug:
501
- qs = qs.filter(ledger__journal_entries__entity_unit__slug__exact=unit_slug)
522
+ qs = qs.filter(
523
+ ledger__journal_entries__entity_unit__slug__exact=unit_slug
524
+ )
502
525
 
503
526
  return qs
504
527
 
@@ -507,17 +530,21 @@ class UnpaidElementsMixIn:
507
530
  from_date = context['from_date'] if not from_date else from_date
508
531
  to_date = context['to_date'] if not to_date else to_date
509
532
 
510
- qs = BillModel.objects.for_entity(
511
- user_model=self.request.user,
512
- entity_slug=self.kwargs['entity_slug']
513
- ).unpaid().filter(
514
- Q(date_approved__gte=from_date) &
515
- Q(date_approved__lte=to_date)
516
- ).select_related('vendor').order_by('date_due')
533
+ qs = (
534
+ BillModel.objects.for_entity(entity_model=self.kwargs['entity_slug'])
535
+ .for_user(user_model=self.request.user)
536
+ .unpaid()
537
+ .filter(Q(date_approved__gte=from_date) & Q(date_approved__lte=to_date))
538
+ .select_related('vendor')
539
+ .order_by('date_due')
540
+ )
517
541
 
518
542
  unit_slug = self.get_unit_slug()
543
+
519
544
  if unit_slug:
520
- qs = qs.filter(ledger__journal_entries__entity_unit__slug__exact=unit_slug)
545
+ qs = qs.filter(
546
+ ledger__journal_entries__entity_unit__slug__exact=unit_slug
547
+ )
521
548
 
522
549
  return qs
523
550
 
@@ -528,7 +555,7 @@ class BaseDateNavigationUrlMixIn:
528
555
  'unit_slug',
529
556
  'ledger_pk',
530
557
  'account_pk',
531
- 'coa_slug'
558
+ 'coa_slug',
532
559
  )
533
560
 
534
561
  def get_context_data(self, **kwargs):
@@ -542,9 +569,9 @@ class BaseDateNavigationUrlMixIn:
542
569
  context['date_navigation_url'] = reverse(
543
570
  f'django_ledger:{view_name_base}',
544
571
  kwargs={
545
- k: v for k, v in self.kwargs.items() if
546
- k in self.BASE_DATE_URL_KWARGS
547
- })
572
+ k: v for k, v in self.kwargs.items() if k in self.BASE_DATE_URL_KWARGS
573
+ },
574
+ )
548
575
 
549
576
 
550
577
  class PDFReportMixIn:
@@ -566,7 +593,9 @@ class PDFReportMixIn:
566
593
 
567
594
  def get_pdf_func_name(self):
568
595
  if not self.pdf_report_type:
569
- raise NotImplementedError(f'Must define pdf_report_type from {self.PDFReportEnum.__name__}')
596
+ raise NotImplementedError(
597
+ f'Must define pdf_report_type from {self.PDFReportEnum.__name__}'
598
+ )
570
599
  return self.pdf_io_mixin_function_map[self.pdf_report_type]
571
600
 
572
601
  def get_pdf(self):
@@ -578,7 +607,7 @@ class PDFReportMixIn:
578
607
  from_date=self.get_pdf_from_date(),
579
608
  to_date=self.get_pdf_to_date(),
580
609
  user_model=self.request.user,
581
- subtitle=self.get_pdf_subtitle()
610
+ subtitle=self.get_pdf_subtitle(),
582
611
  )
583
612
  pdf.create_pdf_report()
584
613
  return pdf
@@ -595,17 +624,20 @@ class PDFReportMixIn:
595
624
  return ctx['to_date']
596
625
 
597
626
  def get_pdf_response(self) -> HttpResponse:
598
- if not DJANGO_LEDGER_PDF_SUPPORT_ENABLED:
599
- return HttpResponseNotFound(content='PDF format is not supported')
600
627
  pdf = self.get_pdf()
601
628
  response = HttpResponse(
602
629
  bytes(pdf.output()),
603
- content_type="application/pdf",
630
+ content_type='application/pdf',
631
+ )
632
+ response.headers['Content-Disposition'] = (
633
+ f'attachment; filename={pdf.get_pdf_filename()}'
604
634
  )
605
- response.headers['Content-Disposition'] = f'attachment; filename={pdf.get_pdf_filename()}'
606
635
  return response
607
636
 
608
637
  def get(self, request, **kwargs):
609
- if request.GET.get(self.pdf_format_query_param) == self.pdf_format_query_param_value:
638
+ if (
639
+ request.GET.get(self.pdf_format_query_param)
640
+ == self.pdf_format_query_param_value
641
+ ):
610
642
  return self.get_pdf_response()
611
643
  return super().get(request, **kwargs)
@@ -23,21 +23,18 @@ from django_ledger.models import PurchaseOrderModel, ItemTransactionModel, Estim
23
23
  from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
24
24
 
25
25
 
26
- class PurchaseOrderModelModelViewQuerySetMixIn:
26
+ class PurchaseOrderModelModelViewQuerySetMixIn(DjangoLedgerSecurityMixIn):
27
27
  queryset = None
28
28
 
29
29
  def get_queryset(self):
30
30
  if self.queryset is None:
31
31
  self.queryset = PurchaseOrderModel.objects.for_entity(
32
- entity_slug=self.kwargs['entity_slug'],
33
- user_model=self.request.user
32
+ entity_model=self.AUTHORIZED_ENTITY_MODEL,
34
33
  ).select_related('entity', 'ce_model')
35
34
  return super().get_queryset()
36
35
 
37
36
 
38
- class PurchaseOrderModelListView(DjangoLedgerSecurityMixIn,
39
- PurchaseOrderModelModelViewQuerySetMixIn,
40
- ArchiveIndexView):
37
+ class PurchaseOrderModelListView(PurchaseOrderModelModelViewQuerySetMixIn, ArchiveIndexView):
41
38
  template_name = 'django_ledger/purchase_order/po_list.html'
42
39
  context_object_name = 'po_list'
43
40
  PAGE_TITLE = _('PO List')
@@ -76,9 +73,7 @@ class PurchaseOrderModelMonthListView(MonthArchiveView,
76
73
  date_list_period = 'year'
77
74
 
78
75
 
79
- class PurchaseOrderModelCreateView(DjangoLedgerSecurityMixIn,
80
- PurchaseOrderModelModelViewQuerySetMixIn,
81
- CreateView):
76
+ class PurchaseOrderModelCreateView(PurchaseOrderModelModelViewQuerySetMixIn, CreateView):
82
77
  template_name = 'django_ledger/purchase_order/po_create.html'
83
78
  PAGE_TITLE = _('Create Purchase Order')
84
79
  extra_context = {
@@ -92,8 +87,7 @@ class PurchaseOrderModelCreateView(DjangoLedgerSecurityMixIn,
92
87
  response = super(PurchaseOrderModelCreateView, self).get(request, entity_slug, **kwargs)
93
88
  if self.for_estimate and 'ce_pk' in self.kwargs:
94
89
  estimate_qs = EstimateModel.objects.for_entity(
95
- entity_slug=entity_slug,
96
- user_model=self.request.user
90
+ entity_model=self.AUTHORIZED_ENTITY_MODEL,
97
91
  )
98
92
  estimate_model: EstimateModel = get_object_or_404(estimate_qs, uuid__exact=self.kwargs['ce_pk'])
99
93
  if not estimate_model.can_bind():
@@ -110,8 +104,7 @@ class PurchaseOrderModelCreateView(DjangoLedgerSecurityMixIn,
110
104
  'ce_pk': self.kwargs['ce_pk']
111
105
  })
112
106
  estimate_qs = EstimateModel.objects.for_entity(
113
- entity_slug=self.kwargs['entity_slug'],
114
- user_model=self.request.user
107
+ entity_model=self.AUTHORIZED_ENTITY_MODEL
115
108
  ).select_related('customer')
116
109
  estimate_model = get_object_or_404(estimate_qs, uuid__exact=self.kwargs['ce_pk'])
117
110
  context['estimate_model'] = estimate_model
@@ -125,23 +118,25 @@ class PurchaseOrderModelCreateView(DjangoLedgerSecurityMixIn,
125
118
 
126
119
  def get_form(self, form_class=None):
127
120
  entity_slug = self.kwargs['entity_slug']
128
- form = PurchaseOrderModelCreateForm(entity_slug=entity_slug,
129
- user_model=self.request.user,
130
- **self.get_form_kwargs())
121
+ form = PurchaseOrderModelCreateForm(
122
+ entity_slug=entity_slug,
123
+ user_model=self.request.user,
124
+ **self.get_form_kwargs()
125
+ )
131
126
  return form
132
127
 
133
128
  def form_valid(self, form):
134
129
  po_model: PurchaseOrderModel = form.save(commit=False)
135
130
  po_model = po_model.configure(
136
131
  entity_slug=self.kwargs['entity_slug'],
137
- user_model=self.request.user)
132
+ user_model=self.request.user
133
+ )
138
134
 
139
135
  if self.for_estimate:
140
136
  ce_pk = self.kwargs['ce_pk']
141
137
  estimate_model_qs = EstimateModel.objects.for_entity(
142
- entity_slug=self.kwargs['entity_slug'],
143
- user_model=self.request.user
144
- )
138
+ entity_model=self.AUTHORIZED_ENTITY_MODEL,
139
+ ).for_user(user_model=self.request.user)
145
140
  estimate_model = get_object_or_404(estimate_model_qs, uuid__exact=ce_pk)
146
141
  po_model.action_bind_estimate(estimate_model=estimate_model, commit=False)
147
142
  return super().form_valid(form=form)
@@ -162,9 +157,7 @@ class PurchaseOrderModelCreateView(DjangoLedgerSecurityMixIn,
162
157
  })
163
158
 
164
159
 
165
- class PurchaseOrderModelUpdateView(DjangoLedgerSecurityMixIn,
166
- PurchaseOrderModelModelViewQuerySetMixIn,
167
- UpdateView):
160
+ class PurchaseOrderModelUpdateView(PurchaseOrderModelModelViewQuerySetMixIn, UpdateView):
168
161
  slug_url_kwarg = 'po_pk'
169
162
  slug_field = 'uuid'
170
163
  context_object_name = 'po_model'
@@ -322,8 +315,7 @@ class PurchaseOrderModelUpdateView(DjangoLedgerSecurityMixIn,
322
315
 
323
316
  if form.has_changed():
324
317
  po_items_qs = ItemTransactionModel.objects.for_po(
325
- entity_slug=self.kwargs['entity_slug'],
326
- user_model=self.request.user,
318
+ entity_model=self.kwargs['entity_slug'],
327
319
  po_pk=po_model.uuid,
328
320
  ).select_related('bill_model')
329
321
 
@@ -360,9 +352,7 @@ class PurchaseOrderModelUpdateView(DjangoLedgerSecurityMixIn,
360
352
  return super().form_valid(form)
361
353
 
362
354
 
363
- class PurchaseOrderModelDetailView(DjangoLedgerSecurityMixIn,
364
- PurchaseOrderModelModelViewQuerySetMixIn,
365
- DetailView):
355
+ class PurchaseOrderModelDetailView(PurchaseOrderModelModelViewQuerySetMixIn, DetailView):
366
356
  slug_url_kwarg = 'po_pk'
367
357
  slug_field = 'uuid'
368
358
  context_object_name = 'po_model'
@@ -390,9 +380,7 @@ class PurchaseOrderModelDetailView(DjangoLedgerSecurityMixIn,
390
380
  return context
391
381
 
392
382
 
393
- class PurchaseOrderModelDeleteView(DjangoLedgerSecurityMixIn,
394
- PurchaseOrderModelModelViewQuerySetMixIn,
395
- DeleteView):
383
+ class PurchaseOrderModelDeleteView(PurchaseOrderModelModelViewQuerySetMixIn, DeleteView):
396
384
  slug_url_kwarg = 'po_pk'
397
385
  slug_field = 'uuid'
398
386
  context_object_name = 'po_model'
@@ -436,10 +424,11 @@ class PurchaseOrderModelDeleteView(DjangoLedgerSecurityMixIn,
436
424
 
437
425
 
438
426
  # ACTIONS...
439
- class BasePurchaseOrderActionActionView(DjangoLedgerSecurityMixIn,
440
- PurchaseOrderModelModelViewQuerySetMixIn,
441
- RedirectView,
442
- SingleObjectMixin):
427
+ class BasePurchaseOrderActionActionView(
428
+ PurchaseOrderModelModelViewQuerySetMixIn,
429
+ RedirectView,
430
+ SingleObjectMixin
431
+ ):
443
432
  http_method_names = ['get']
444
433
  pk_url_kwarg = 'po_pk'
445
434
  action_name = None