django-ledger 0.8.0__py3-none-any.whl → 0.8.2__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 +1 -1
- django_ledger/forms/account.py +45 -46
- django_ledger/forms/data_import.py +182 -64
- django_ledger/io/io_core.py +507 -374
- django_ledger/migrations/0026_stagedtransactionmodel_customer_model_and_more.py +56 -0
- django_ledger/models/__init__.py +2 -1
- django_ledger/models/bill.py +337 -300
- django_ledger/models/customer.py +47 -34
- django_ledger/models/data_import.py +770 -289
- django_ledger/models/entity.py +882 -637
- django_ledger/models/mixins.py +421 -282
- django_ledger/models/receipt.py +1083 -0
- django_ledger/models/transactions.py +105 -41
- django_ledger/models/unit.py +42 -30
- django_ledger/models/utils.py +12 -2
- django_ledger/models/vendor.py +85 -66
- django_ledger/settings.py +1 -0
- django_ledger/static/django_ledger/bundle/djetler.bundle.js +1 -1
- django_ledger/static/django_ledger/bundle/djetler.bundle.js.LICENSE.txt +1 -13
- django_ledger/templates/django_ledger/bills/bill_update.html +1 -1
- django_ledger/templates/django_ledger/components/period_navigator.html +5 -3
- django_ledger/templates/django_ledger/customer/customer_detail.html +87 -0
- django_ledger/templates/django_ledger/customer/customer_list.html +0 -1
- django_ledger/templates/django_ledger/customer/tags/customer_table.html +3 -1
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_imported.html +24 -3
- django_ledger/templates/django_ledger/data_import/tags/data_import_job_txs_table.html +26 -10
- django_ledger/templates/django_ledger/entity/entity_dashboard.html +2 -2
- django_ledger/templates/django_ledger/invoice/invoice_update.html +1 -1
- django_ledger/templates/django_ledger/layouts/base.html +3 -1
- django_ledger/templates/django_ledger/layouts/content_layout_1.html +1 -1
- django_ledger/templates/django_ledger/receipt/customer_receipt_report.html +115 -0
- django_ledger/templates/django_ledger/receipt/receipt_delete.html +30 -0
- django_ledger/templates/django_ledger/receipt/receipt_detail.html +89 -0
- django_ledger/templates/django_ledger/receipt/receipt_list.html +134 -0
- django_ledger/templates/django_ledger/receipt/vendor_receipt_report.html +115 -0
- django_ledger/templates/django_ledger/vendor/tags/vendor_table.html +3 -2
- django_ledger/templates/django_ledger/vendor/vendor_detail.html +86 -0
- django_ledger/templatetags/django_ledger.py +338 -191
- django_ledger/urls/__init__.py +1 -0
- django_ledger/urls/customer.py +3 -0
- django_ledger/urls/data_import.py +3 -0
- django_ledger/urls/receipt.py +102 -0
- django_ledger/urls/vendor.py +1 -0
- django_ledger/views/__init__.py +1 -0
- django_ledger/views/customer.py +56 -14
- django_ledger/views/data_import.py +119 -66
- django_ledger/views/mixins.py +112 -86
- django_ledger/views/receipt.py +294 -0
- django_ledger/views/vendor.py +53 -14
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/METADATA +1 -1
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/RECORD +55 -45
- django_ledger/static/django_ledger/bundle/styles.bundle.js +0 -1
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/WHEEL +0 -0
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/licenses/AUTHORS.md +0 -0
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/licenses/LICENSE +0 -0
- {django_ledger-0.8.0.dist-info → django_ledger-0.8.2.dist-info}/top_level.txt +0 -0
django_ledger/views/mixins.py
CHANGED
|
@@ -11,9 +11,13 @@ 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
|
|
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
|
|
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 _
|
|
@@ -35,15 +39,18 @@ class ContextFromToDateMixin:
|
|
|
35
39
|
return self.TO_DATE_CONTEXT_NAME
|
|
36
40
|
|
|
37
41
|
|
|
38
|
-
class YearlyReportMixIn(
|
|
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(
|
|
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(
|
|
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(_(
|
|
101
|
+
raise Http404(_('Invalid quarter number'))
|
|
93
102
|
except ValueError:
|
|
94
|
-
raise Http404(
|
|
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(_(
|
|
117
|
+
raise Http404(_('No quarter specified'))
|
|
107
118
|
quarter = self.parse_quarter(quarter)
|
|
108
119
|
return quarter
|
|
109
120
|
|
|
110
|
-
def get_from_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(
|
|
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(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
to_date = self.get_to_date(
|
|
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(
|
|
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(
|
|
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(
|
|
173
|
-
|
|
174
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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,8 @@ 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
|
-
|
|
423
|
-
from_date=None,
|
|
424
|
-
to_date=None,
|
|
425
|
-
**kwargs):
|
|
426
|
-
|
|
427
|
-
if any([
|
|
428
|
-
self.IO_DIGEST_UNBOUNDED,
|
|
429
|
-
self.IO_DIGEST_BOUNDED
|
|
430
|
-
]):
|
|
431
|
-
|
|
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]):
|
|
432
441
|
by_period = self.request.GET.get('by_period')
|
|
433
442
|
io_model: EntityModel | LedgerModel = self.object
|
|
434
443
|
if not to_date:
|
|
@@ -445,17 +454,21 @@ class DigestContextMixIn:
|
|
|
445
454
|
if self.IO_DIGEST_UNBOUNDED:
|
|
446
455
|
io_digest = io_model.digest(
|
|
447
456
|
user_model=self.request.user,
|
|
448
|
-
entity_slug=io_model.entity_slug
|
|
457
|
+
entity_slug=io_model.entity_slug
|
|
458
|
+
if isinstance(io_model, LedgerModel)
|
|
459
|
+
else None,
|
|
449
460
|
to_date=to_date,
|
|
450
461
|
unit_slug=unit_slug,
|
|
451
462
|
by_period=True if by_period else False,
|
|
452
463
|
process_ratios=True,
|
|
453
464
|
process_roles=True,
|
|
454
|
-
process_groups=True
|
|
465
|
+
process_groups=True,
|
|
455
466
|
)
|
|
456
467
|
|
|
457
468
|
context[self.get_io_manager_unbounded_context_name()] = io_digest
|
|
458
|
-
context[self.get_io_digest_unbounded_context_name()] =
|
|
469
|
+
context[self.get_io_digest_unbounded_context_name()] = (
|
|
470
|
+
io_digest.get_io_data()
|
|
471
|
+
)
|
|
459
472
|
|
|
460
473
|
if self.IO_DIGEST_BOUNDED:
|
|
461
474
|
io_digest_equity = io_model.digest(
|
|
@@ -467,11 +480,13 @@ class DigestContextMixIn:
|
|
|
467
480
|
by_period=True if by_period else False,
|
|
468
481
|
process_ratios=True,
|
|
469
482
|
process_roles=True,
|
|
470
|
-
process_groups=True
|
|
483
|
+
process_groups=True,
|
|
471
484
|
)
|
|
472
485
|
|
|
473
486
|
context[self.get_io_manager_bounded_context_name()] = io_digest_equity
|
|
474
|
-
context[self.get_io_digest_bounded_context_name()] =
|
|
487
|
+
context[self.get_io_digest_bounded_context_name()] = (
|
|
488
|
+
io_digest_equity.get_io_data()
|
|
489
|
+
)
|
|
475
490
|
|
|
476
491
|
# todo: how is this used??....
|
|
477
492
|
context['date_filter'] = to_date
|
|
@@ -493,18 +508,20 @@ class UnpaidElementsMixIn:
|
|
|
493
508
|
from_date = context['from_date'] if not from_date else from_date
|
|
494
509
|
to_date = context['to_date'] if not to_date else to_date
|
|
495
510
|
|
|
496
|
-
qs =
|
|
497
|
-
entity_model=self.kwargs['entity_slug']
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
)
|
|
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
|
+
)
|
|
504
519
|
|
|
505
520
|
unit_slug = self.get_unit_slug()
|
|
506
521
|
if unit_slug:
|
|
507
|
-
qs = qs.filter(
|
|
522
|
+
qs = qs.filter(
|
|
523
|
+
ledger__journal_entries__entity_unit__slug__exact=unit_slug
|
|
524
|
+
)
|
|
508
525
|
|
|
509
526
|
return qs
|
|
510
527
|
|
|
@@ -513,19 +530,21 @@ class UnpaidElementsMixIn:
|
|
|
513
530
|
from_date = context['from_date'] if not from_date else from_date
|
|
514
531
|
to_date = context['to_date'] if not to_date else to_date
|
|
515
532
|
|
|
516
|
-
qs =
|
|
517
|
-
entity_model=self.kwargs['entity_slug']
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
)
|
|
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
|
+
)
|
|
524
541
|
|
|
525
542
|
unit_slug = self.get_unit_slug()
|
|
526
543
|
|
|
527
544
|
if unit_slug:
|
|
528
|
-
qs = qs.filter(
|
|
545
|
+
qs = qs.filter(
|
|
546
|
+
ledger__journal_entries__entity_unit__slug__exact=unit_slug
|
|
547
|
+
)
|
|
529
548
|
|
|
530
549
|
return qs
|
|
531
550
|
|
|
@@ -536,7 +555,7 @@ class BaseDateNavigationUrlMixIn:
|
|
|
536
555
|
'unit_slug',
|
|
537
556
|
'ledger_pk',
|
|
538
557
|
'account_pk',
|
|
539
|
-
'coa_slug'
|
|
558
|
+
'coa_slug',
|
|
540
559
|
)
|
|
541
560
|
|
|
542
561
|
def get_context_data(self, **kwargs):
|
|
@@ -550,9 +569,9 @@ class BaseDateNavigationUrlMixIn:
|
|
|
550
569
|
context['date_navigation_url'] = reverse(
|
|
551
570
|
f'django_ledger:{view_name_base}',
|
|
552
571
|
kwargs={
|
|
553
|
-
k: v for k, v in self.kwargs.items() if
|
|
554
|
-
|
|
555
|
-
|
|
572
|
+
k: v for k, v in self.kwargs.items() if k in self.BASE_DATE_URL_KWARGS
|
|
573
|
+
},
|
|
574
|
+
)
|
|
556
575
|
|
|
557
576
|
|
|
558
577
|
class PDFReportMixIn:
|
|
@@ -574,7 +593,9 @@ class PDFReportMixIn:
|
|
|
574
593
|
|
|
575
594
|
def get_pdf_func_name(self):
|
|
576
595
|
if not self.pdf_report_type:
|
|
577
|
-
raise NotImplementedError(
|
|
596
|
+
raise NotImplementedError(
|
|
597
|
+
f'Must define pdf_report_type from {self.PDFReportEnum.__name__}'
|
|
598
|
+
)
|
|
578
599
|
return self.pdf_io_mixin_function_map[self.pdf_report_type]
|
|
579
600
|
|
|
580
601
|
def get_pdf(self):
|
|
@@ -586,7 +607,7 @@ class PDFReportMixIn:
|
|
|
586
607
|
from_date=self.get_pdf_from_date(),
|
|
587
608
|
to_date=self.get_pdf_to_date(),
|
|
588
609
|
user_model=self.request.user,
|
|
589
|
-
subtitle=self.get_pdf_subtitle()
|
|
610
|
+
subtitle=self.get_pdf_subtitle(),
|
|
590
611
|
)
|
|
591
612
|
pdf.create_pdf_report()
|
|
592
613
|
return pdf
|
|
@@ -606,12 +627,17 @@ class PDFReportMixIn:
|
|
|
606
627
|
pdf = self.get_pdf()
|
|
607
628
|
response = HttpResponse(
|
|
608
629
|
bytes(pdf.output()),
|
|
609
|
-
content_type=
|
|
630
|
+
content_type='application/pdf',
|
|
631
|
+
)
|
|
632
|
+
response.headers['Content-Disposition'] = (
|
|
633
|
+
f'attachment; filename={pdf.get_pdf_filename()}'
|
|
610
634
|
)
|
|
611
|
-
response.headers['Content-Disposition'] = f'attachment; filename={pdf.get_pdf_filename()}'
|
|
612
635
|
return response
|
|
613
636
|
|
|
614
637
|
def get(self, request, **kwargs):
|
|
615
|
-
if
|
|
638
|
+
if (
|
|
639
|
+
request.GET.get(self.pdf_format_query_param)
|
|
640
|
+
== self.pdf_format_query_param_value
|
|
641
|
+
):
|
|
616
642
|
return self.get_pdf_response()
|
|
617
643
|
return super().get(request, **kwargs)
|