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/urls/__init__.py
CHANGED
|
@@ -35,6 +35,7 @@ urlpatterns = [
|
|
|
35
35
|
path('feedback/', include('django_ledger.urls.feedback')),
|
|
36
36
|
path('inventory/', include('django_ledger.urls.inventory')),
|
|
37
37
|
path('home/', include('django_ledger.urls.home')),
|
|
38
|
+
path('receipt/', include('django_ledger.urls.receipt')),
|
|
38
39
|
path('api/', include('django_ledger.urls.djl_api')),
|
|
39
40
|
path('', views.RootUrlView.as_view(), name='root'),
|
|
40
41
|
]
|
django_ledger/urls/customer.py
CHANGED
|
@@ -7,4 +7,7 @@ urlpatterns = [
|
|
|
7
7
|
path('<slug:entity_slug>/update/<uuid:customer_pk>/',
|
|
8
8
|
views.CustomerModelUpdateView.as_view(),
|
|
9
9
|
name='customer-update'),
|
|
10
|
+
path('<slug:entity_slug>/detail/<uuid:customer_pk>/',
|
|
11
|
+
views.CustomerModelDetailView.as_view(),
|
|
12
|
+
name='customer-detail'),
|
|
10
13
|
]
|
|
@@ -18,4 +18,7 @@ urlpatterns = [
|
|
|
18
18
|
path('<slug:entity_slug>/jobs/<uuid:job_pk>/txs/',
|
|
19
19
|
views.DataImportJobDetailView.as_view(),
|
|
20
20
|
name='data-import-job-txs'),
|
|
21
|
+
path('<slug:entity_slug>/jobs/<uuid:job_pk>/txs/<uuid:staged_tx_pk>/undo/',
|
|
22
|
+
views.StagedTransactionUndoView.as_view(),
|
|
23
|
+
name='data-import-staged-tx-undo'),
|
|
21
24
|
]
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Django Ledger created by Miguel Sanda <msanda@arrobalytics.com>.
|
|
3
|
+
Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
4
|
+
|
|
5
|
+
Contributions to this module:
|
|
6
|
+
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from django.urls import path
|
|
10
|
+
|
|
11
|
+
from django_ledger import views
|
|
12
|
+
|
|
13
|
+
urlpatterns = [
|
|
14
|
+
path(
|
|
15
|
+
'<slug:entity_slug>/latest/',
|
|
16
|
+
views.ReceiptModelListView.as_view(),
|
|
17
|
+
name='receipt-list',
|
|
18
|
+
),
|
|
19
|
+
path(
|
|
20
|
+
'<slug:entity_slug>/year/<int:year>/',
|
|
21
|
+
views.ReceiptModelYearListView.as_view(),
|
|
22
|
+
name='receipt-list-year',
|
|
23
|
+
),
|
|
24
|
+
path(
|
|
25
|
+
'<slug:entity_slug>/quarter/<int:year>/q<int:quarter>/',
|
|
26
|
+
views.ReceiptModelQuarterListView.as_view(),
|
|
27
|
+
name='receipt-list-quarter',
|
|
28
|
+
),
|
|
29
|
+
path(
|
|
30
|
+
'<slug:entity_slug>/month/<int:year>/<int:month>/',
|
|
31
|
+
views.ReceiptModelMonthListView.as_view(),
|
|
32
|
+
name='receipt-list-month',
|
|
33
|
+
),
|
|
34
|
+
path(
|
|
35
|
+
'<slug:entity_slug>/detail/<uuid:receipt_pk>/',
|
|
36
|
+
views.ReceiptModelDetailView.as_view(),
|
|
37
|
+
name='receipt-detail',
|
|
38
|
+
),
|
|
39
|
+
path(
|
|
40
|
+
'<slug:entity_slug>/delete/<uuid:receipt_pk>/',
|
|
41
|
+
views.ReceiptModelDeleteView.as_view(),
|
|
42
|
+
name='receipt-delete',
|
|
43
|
+
),
|
|
44
|
+
# Filtered lists
|
|
45
|
+
path(
|
|
46
|
+
'<slug:entity_slug>/type/<str:receipt_type>/',
|
|
47
|
+
views.ReceiptModelListView.as_view(),
|
|
48
|
+
name='receipt-list-type',
|
|
49
|
+
),
|
|
50
|
+
path(
|
|
51
|
+
'<slug:entity_slug>/vendor/<uuid:vendor_pk>/',
|
|
52
|
+
views.ReceiptModelListView.as_view(),
|
|
53
|
+
name='receipt-list-vendor',
|
|
54
|
+
),
|
|
55
|
+
path(
|
|
56
|
+
'<slug:entity_slug>/customer/<uuid:customer_pk>/',
|
|
57
|
+
views.ReceiptModelListView.as_view(),
|
|
58
|
+
name='receipt-list-customer',
|
|
59
|
+
),
|
|
60
|
+
# Reports: Vendor
|
|
61
|
+
path(
|
|
62
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/latest/',
|
|
63
|
+
views.VendorReceiptReportListView.as_view(),
|
|
64
|
+
name='receipt-report-vendor',
|
|
65
|
+
),
|
|
66
|
+
path(
|
|
67
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/year/<int:year>/',
|
|
68
|
+
views.VendorReceiptReportYearListView.as_view(),
|
|
69
|
+
name='receipt-report-vendor-year',
|
|
70
|
+
),
|
|
71
|
+
path(
|
|
72
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/quarter/<int:year>/q<int:quarter>/',
|
|
73
|
+
views.VendorReceiptReportQuarterListView.as_view(),
|
|
74
|
+
name='receipt-report-vendor-quarter',
|
|
75
|
+
),
|
|
76
|
+
path(
|
|
77
|
+
'<slug:entity_slug>/report/vendor/<uuid:vendor_pk>/month/<int:year>/<int:month>/',
|
|
78
|
+
views.VendorReceiptReportMonthListView.as_view(),
|
|
79
|
+
name='receipt-report-vendor-month',
|
|
80
|
+
),
|
|
81
|
+
# Reports: Customer
|
|
82
|
+
path(
|
|
83
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/latest/',
|
|
84
|
+
views.CustomerReceiptReportListView.as_view(),
|
|
85
|
+
name='receipt-report-customer',
|
|
86
|
+
),
|
|
87
|
+
path(
|
|
88
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/year/<int:year>/',
|
|
89
|
+
views.CustomerReceiptReportYearListView.as_view(),
|
|
90
|
+
name='receipt-report-customer-year',
|
|
91
|
+
),
|
|
92
|
+
path(
|
|
93
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/quarter/<int:year>/q<int:quarter>/',
|
|
94
|
+
views.CustomerReceiptReportQuarterListView.as_view(),
|
|
95
|
+
name='receipt-report-customer-quarter',
|
|
96
|
+
),
|
|
97
|
+
path(
|
|
98
|
+
'<slug:entity_slug>/report/customer/<uuid:customer_pk>/month/<int:year>/<int:month>/',
|
|
99
|
+
views.CustomerReceiptReportMonthListView.as_view(),
|
|
100
|
+
name='receipt-report-customer-month',
|
|
101
|
+
),
|
|
102
|
+
]
|
django_ledger/urls/vendor.py
CHANGED
|
@@ -5,4 +5,5 @@ urlpatterns = [
|
|
|
5
5
|
path('<slug:entity_slug>/list/', views.VendorModelListView.as_view(), name='vendor-list'),
|
|
6
6
|
path('<slug:entity_slug>/create/', views.VendorModelCreateView.as_view(), name='vendor-create'),
|
|
7
7
|
path('<slug:entity_slug>/update/<uuid:vendor_pk>/', views.VendorModelUpdateView.as_view(), name='vendor-update'),
|
|
8
|
+
path('<slug:entity_slug>/detail/<uuid:vendor_pk>/', views.VendorModelDetailView.as_view(), name='vendor-detail'),
|
|
8
9
|
]
|
django_ledger/views/__init__.py
CHANGED
django_ledger/views/customer.py
CHANGED
|
@@ -5,15 +5,24 @@ Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
|
5
5
|
Contributions to this module:
|
|
6
6
|
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
from typing import Optional
|
|
9
10
|
|
|
10
11
|
from django.urls import reverse
|
|
11
12
|
from django.utils.translation import gettext_lazy as _
|
|
12
|
-
from django.views.generic import
|
|
13
|
+
from django.views.generic import (
|
|
14
|
+
CreateView,
|
|
15
|
+
DeleteView,
|
|
16
|
+
DetailView,
|
|
17
|
+
ListView,
|
|
18
|
+
UpdateView,
|
|
19
|
+
)
|
|
13
20
|
|
|
14
21
|
from django_ledger.forms.customer import CustomerModelForm
|
|
15
22
|
from django_ledger.models.customer import CustomerModel, CustomerModelQueryset
|
|
16
23
|
from django_ledger.models.entity import EntityModel
|
|
24
|
+
from django_ledger.models.invoice import InvoiceModel
|
|
25
|
+
from django_ledger.models.receipt import ReceiptModel
|
|
17
26
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
18
27
|
|
|
19
28
|
|
|
@@ -34,7 +43,7 @@ class CustomerModelListView(CustomerModelModelViewQuerySetMixIn, ListView):
|
|
|
34
43
|
extra_context = {
|
|
35
44
|
'page_title': PAGE_TITLE,
|
|
36
45
|
'header_title': PAGE_TITLE,
|
|
37
|
-
'header_subtitle_icon': 'dashicons:businesswoman'
|
|
46
|
+
'header_subtitle_icon': 'dashicons:businesswoman',
|
|
38
47
|
}
|
|
39
48
|
context_object_name = 'customers'
|
|
40
49
|
|
|
@@ -47,20 +56,20 @@ class CustomerModelCreateView(CustomerModelModelViewQuerySetMixIn, CreateView):
|
|
|
47
56
|
extra_context = {
|
|
48
57
|
'page_title': PAGE_TITLE,
|
|
49
58
|
'header_title': PAGE_TITLE,
|
|
50
|
-
'header_subtitle_icon': 'dashicons:businesswoman'
|
|
59
|
+
'header_subtitle_icon': 'dashicons:businesswoman',
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
def get_success_url(self):
|
|
54
|
-
return reverse(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
63
|
+
return reverse(
|
|
64
|
+
'django_ledger:customer-list',
|
|
65
|
+
kwargs={'entity_slug': self.kwargs['entity_slug']},
|
|
66
|
+
)
|
|
58
67
|
|
|
59
68
|
def form_valid(self, form):
|
|
60
69
|
customer_model: CustomerModel = form.save(commit=False)
|
|
61
|
-
entity_model = EntityModel.objects.for_user(
|
|
62
|
-
|
|
63
|
-
)
|
|
70
|
+
entity_model = EntityModel.objects.for_user(user_model=self.request.user).get(
|
|
71
|
+
slug__exact=self.kwargs['entity_slug']
|
|
72
|
+
)
|
|
64
73
|
customer_model.entity_model = entity_model
|
|
65
74
|
customer_model.save()
|
|
66
75
|
return super().form_valid(form)
|
|
@@ -84,10 +93,10 @@ class CustomerModelUpdateView(CustomerModelModelViewQuerySetMixIn, UpdateView):
|
|
|
84
93
|
return context
|
|
85
94
|
|
|
86
95
|
def get_success_url(self):
|
|
87
|
-
return reverse(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
96
|
+
return reverse(
|
|
97
|
+
'django_ledger:customer-list',
|
|
98
|
+
kwargs={'entity_slug': self.kwargs['entity_slug']},
|
|
99
|
+
)
|
|
91
100
|
|
|
92
101
|
def form_valid(self, form):
|
|
93
102
|
form.save()
|
|
@@ -96,3 +105,36 @@ class CustomerModelUpdateView(CustomerModelModelViewQuerySetMixIn, UpdateView):
|
|
|
96
105
|
|
|
97
106
|
class CustomerModelDeleteView(CustomerModelModelViewQuerySetMixIn, DeleteView):
|
|
98
107
|
pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class CustomerModelDetailView(CustomerModelModelViewQuerySetMixIn, DetailView):
|
|
111
|
+
template_name = 'django_ledger/customer/customer_detail.html'
|
|
112
|
+
context_object_name = 'customer'
|
|
113
|
+
PAGE_TITLE = _('Customer Details')
|
|
114
|
+
slug_url_kwarg = 'customer_pk'
|
|
115
|
+
slug_field = 'uuid'
|
|
116
|
+
|
|
117
|
+
def get_context_data(self, **kwargs):
|
|
118
|
+
context = super().get_context_data(**kwargs)
|
|
119
|
+
customer: CustomerModel = self.object
|
|
120
|
+
receipts_qs = (
|
|
121
|
+
ReceiptModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
|
|
122
|
+
.for_customer(customer_model=customer)
|
|
123
|
+
.order_by('-updated')
|
|
124
|
+
)
|
|
125
|
+
invoices_qs = (
|
|
126
|
+
InvoiceModel.objects.for_entity(entity_model=self.AUTHORIZED_ENTITY_MODEL)
|
|
127
|
+
.filter(customer=customer)
|
|
128
|
+
.order_by('-updated')
|
|
129
|
+
)
|
|
130
|
+
context.update(
|
|
131
|
+
{
|
|
132
|
+
'page_title': self.PAGE_TITLE,
|
|
133
|
+
'header_title': self.PAGE_TITLE,
|
|
134
|
+
'header_subtitle': f'{customer.customer_name} · {customer.customer_number}',
|
|
135
|
+
'header_subtitle_icon': 'dashicons:businesswoman',
|
|
136
|
+
'receipts': receipts_qs,
|
|
137
|
+
'invoices': invoices_qs,
|
|
138
|
+
}
|
|
139
|
+
)
|
|
140
|
+
return context
|
|
@@ -5,17 +5,25 @@ Copyright© EDMA Group Inc licensed under the GPLv3 Agreement.
|
|
|
5
5
|
Contributions to this module:
|
|
6
6
|
* Miguel Sanda <msanda@arrobalytics.com>
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
from datetime import datetime, time
|
|
10
|
+
from typing import Optional
|
|
9
11
|
|
|
10
12
|
from django.contrib import messages
|
|
13
|
+
from django.shortcuts import get_object_or_404, redirect
|
|
11
14
|
from django.urls import reverse
|
|
12
15
|
from django.utils.timezone import make_aware
|
|
13
16
|
from django.utils.translation import gettext_lazy as _
|
|
14
|
-
from django.views
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
from django_ledger.forms.data_import import
|
|
17
|
+
from django.views import View
|
|
18
|
+
from django.views.generic import DeleteView, DetailView, FormView, ListView, UpdateView
|
|
19
|
+
|
|
20
|
+
from django_ledger.forms.data_import import (
|
|
21
|
+
ImportJobModelCreateForm,
|
|
22
|
+
ImportJobModelUpdateForm,
|
|
23
|
+
StagedTransactionModelFormSet,
|
|
24
|
+
)
|
|
18
25
|
from django_ledger.io.ofx import OFXFileManager
|
|
26
|
+
from django_ledger.models import StagedTransactionModelValidationError
|
|
19
27
|
from django_ledger.models.data_import import ImportJobModel, StagedTransactionModel
|
|
20
28
|
from django_ledger.views.mixins import DjangoLedgerSecurityMixIn
|
|
21
29
|
|
|
@@ -25,13 +33,17 @@ class ImportJobModelViewBaseView(DjangoLedgerSecurityMixIn):
|
|
|
25
33
|
|
|
26
34
|
def get_queryset(self):
|
|
27
35
|
if self.queryset is None:
|
|
28
|
-
self.queryset =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
self.queryset = (
|
|
37
|
+
ImportJobModel.objects.for_entity(
|
|
38
|
+
entity_model=self.AUTHORIZED_ENTITY_MODEL,
|
|
39
|
+
)
|
|
40
|
+
.order_by('-created')
|
|
41
|
+
.select_related(
|
|
42
|
+
'bank_account_model',
|
|
43
|
+
'bank_account_model__entity_model',
|
|
44
|
+
'bank_account_model__account_model',
|
|
45
|
+
'bank_account_model__account_model__coa_model',
|
|
46
|
+
)
|
|
35
47
|
)
|
|
36
48
|
return self.queryset
|
|
37
49
|
|
|
@@ -39,23 +51,19 @@ class ImportJobModelViewBaseView(DjangoLedgerSecurityMixIn):
|
|
|
39
51
|
class ImportJobModelCreateView(ImportJobModelViewBaseView, FormView):
|
|
40
52
|
template_name = 'django_ledger/data_import/import_job_create.html'
|
|
41
53
|
PAGE_TITLE = _('Create Import Job')
|
|
42
|
-
extra_context = {
|
|
43
|
-
'page_title': PAGE_TITLE,
|
|
44
|
-
'header_title': PAGE_TITLE
|
|
45
|
-
}
|
|
54
|
+
extra_context = {'page_title': PAGE_TITLE, 'header_title': PAGE_TITLE}
|
|
46
55
|
form_class = ImportJobModelCreateForm
|
|
47
56
|
|
|
48
57
|
def get_form(self, form_class=None, **kwargs):
|
|
49
58
|
return self.form_class(
|
|
50
|
-
entity_model=self.get_authorized_entity_instance(),
|
|
51
|
-
**self.get_form_kwargs()
|
|
59
|
+
entity_model=self.get_authorized_entity_instance(), **self.get_form_kwargs()
|
|
52
60
|
)
|
|
53
61
|
|
|
54
62
|
def get_success_url(self):
|
|
55
|
-
return reverse(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
return reverse(
|
|
64
|
+
'django_ledger:data-import-jobs-list',
|
|
65
|
+
kwargs={'entity_slug': self.kwargs['entity_slug']},
|
|
66
|
+
)
|
|
59
67
|
|
|
60
68
|
def form_valid(self, form):
|
|
61
69
|
ofx_manager = OFXFileManager(ofx_file_or_path=form.files['ofx_file'])
|
|
@@ -66,13 +74,16 @@ class ImportJobModelCreateView(ImportJobModelViewBaseView, FormView):
|
|
|
66
74
|
txs_to_stage = ofx_manager.get_account_txs()
|
|
67
75
|
staged_txs_model_list = [
|
|
68
76
|
StagedTransactionModel(
|
|
69
|
-
date_posted=make_aware(
|
|
77
|
+
date_posted=make_aware(
|
|
78
|
+
value=datetime.combine(date=tx.dtposted.date(), time=time.min)
|
|
79
|
+
),
|
|
70
80
|
fit_id=tx.fitid,
|
|
71
81
|
amount=tx.trnamt,
|
|
72
82
|
import_job=import_job,
|
|
73
83
|
name=tx.name,
|
|
74
|
-
memo=tx.memo
|
|
75
|
-
)
|
|
84
|
+
memo=tx.memo,
|
|
85
|
+
)
|
|
86
|
+
for tx in txs_to_stage
|
|
76
87
|
]
|
|
77
88
|
for tx in staged_txs_model_list:
|
|
78
89
|
tx.clean()
|
|
@@ -83,10 +94,7 @@ class ImportJobModelCreateView(ImportJobModelViewBaseView, FormView):
|
|
|
83
94
|
|
|
84
95
|
class ImportJobModelListView(ImportJobModelViewBaseView, ListView):
|
|
85
96
|
PAGE_TITLE = _('Data Import Jobs')
|
|
86
|
-
extra_context = {
|
|
87
|
-
'page_title': PAGE_TITLE,
|
|
88
|
-
'header_title': PAGE_TITLE
|
|
89
|
-
}
|
|
97
|
+
extra_context = {'page_title': PAGE_TITLE, 'header_title': PAGE_TITLE}
|
|
90
98
|
context_object_name = 'import_jobs'
|
|
91
99
|
template_name = 'django_ledger/data_import/data_import_job_list.html'
|
|
92
100
|
|
|
@@ -99,9 +107,10 @@ class ImportJobModelUpdateView(ImportJobModelViewBaseView, UpdateView):
|
|
|
99
107
|
|
|
100
108
|
def get_context_data(self, **kwargs):
|
|
101
109
|
ctx = super().get_context_data(**kwargs)
|
|
110
|
+
import_job_model: ImportJobModel = self.object
|
|
102
111
|
ctx['page_title'] = 'Import Job Update'
|
|
103
112
|
ctx['header_title'] = 'Import Job Update'
|
|
104
|
-
ctx['header_subtitle'] =
|
|
113
|
+
ctx['header_subtitle'] = import_job_model.description
|
|
105
114
|
ctx['header_subtitle_icon'] = 'solar:import-bold'
|
|
106
115
|
return ctx
|
|
107
116
|
|
|
@@ -109,9 +118,7 @@ class ImportJobModelUpdateView(ImportJobModelViewBaseView, UpdateView):
|
|
|
109
118
|
entity_model = self.get_authorized_entity_instance()
|
|
110
119
|
return reverse(
|
|
111
120
|
viewname='django_ledger:data-import-jobs-list',
|
|
112
|
-
kwargs={
|
|
113
|
-
'entity_slug': entity_model.slug
|
|
114
|
-
}
|
|
121
|
+
kwargs={'entity_slug': entity_model.slug},
|
|
115
122
|
)
|
|
116
123
|
|
|
117
124
|
def form_valid(self, form):
|
|
@@ -119,7 +126,7 @@ class ImportJobModelUpdateView(ImportJobModelViewBaseView, UpdateView):
|
|
|
119
126
|
self.request,
|
|
120
127
|
level=messages.SUCCESS,
|
|
121
128
|
message=_(f'Successfully updated Import Job {self.object.description}'),
|
|
122
|
-
extra_tags='is-success'
|
|
129
|
+
extra_tags='is-success',
|
|
123
130
|
)
|
|
124
131
|
return super().form_valid(form=form)
|
|
125
132
|
|
|
@@ -140,9 +147,7 @@ class ImportJobModelDeleteView(ImportJobModelViewBaseView, DeleteView):
|
|
|
140
147
|
def get_success_url(self):
|
|
141
148
|
return reverse(
|
|
142
149
|
viewname='django_ledger:data-import-jobs-list',
|
|
143
|
-
kwargs={
|
|
144
|
-
'entity_slug': self.AUTHORIZED_ENTITY_MODEL.slug
|
|
145
|
-
}
|
|
150
|
+
kwargs={'entity_slug': self.AUTHORIZED_ENTITY_MODEL.slug},
|
|
146
151
|
)
|
|
147
152
|
|
|
148
153
|
|
|
@@ -161,10 +166,10 @@ class DataImportJobDetailView(ImportJobModelViewBaseView, DetailView):
|
|
|
161
166
|
'import_job_model': self.get_object(),
|
|
162
167
|
}
|
|
163
168
|
|
|
164
|
-
def get_context_data(
|
|
169
|
+
def get_context_data(
|
|
170
|
+
self, txs_formset: Optional[StagedTransactionModelFormSet] = None, **kwargs
|
|
171
|
+
):
|
|
165
172
|
context = super().get_context_data(**kwargs)
|
|
166
|
-
|
|
167
|
-
# job_model: ImportJobModel = getattr(self, 'object', self.get_object())
|
|
168
173
|
job_model: ImportJobModel = self.object
|
|
169
174
|
|
|
170
175
|
context['page_title'] = job_model.description
|
|
@@ -172,9 +177,13 @@ class DataImportJobDetailView(ImportJobModelViewBaseView, DetailView):
|
|
|
172
177
|
context['header_subtitle'] = job_model.description
|
|
173
178
|
context['header_subtitle_icon'] = 'tabler:table-import'
|
|
174
179
|
|
|
175
|
-
staged_txs_formset =
|
|
176
|
-
|
|
177
|
-
|
|
180
|
+
staged_txs_formset = (
|
|
181
|
+
StagedTransactionModelFormSet(
|
|
182
|
+
entity_model=self.get_authorized_entity_instance(),
|
|
183
|
+
import_job_model=job_model,
|
|
184
|
+
)
|
|
185
|
+
if not txs_formset
|
|
186
|
+
else txs_formset
|
|
178
187
|
)
|
|
179
188
|
|
|
180
189
|
context['staged_txs_formset'] = staged_txs_formset
|
|
@@ -183,35 +192,79 @@ class DataImportJobDetailView(ImportJobModelViewBaseView, DetailView):
|
|
|
183
192
|
|
|
184
193
|
def post(self, request, **kwargs):
|
|
185
194
|
self.object = self.get_object()
|
|
195
|
+
import_job_model: ImportJobModel = self.object
|
|
186
196
|
|
|
187
197
|
txs_formset = StagedTransactionModelFormSet(
|
|
188
198
|
entity_model=self.get_authorized_entity_instance(),
|
|
189
|
-
import_job_model=
|
|
190
|
-
data=request.POST
|
|
199
|
+
import_job_model=import_job_model,
|
|
200
|
+
data=request.POST,
|
|
191
201
|
)
|
|
192
202
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
203
|
+
if txs_formset.has_changed():
|
|
204
|
+
if txs_formset.is_valid():
|
|
205
|
+
txs_formset.save()
|
|
206
|
+
for tx_form in txs_formset:
|
|
207
|
+
if tx_form.has_changed():
|
|
208
|
+
staged_transaction_model: StagedTransactionModel = (
|
|
209
|
+
tx_form.instance
|
|
210
|
+
)
|
|
211
|
+
is_split = tx_form.cleaned_data['tx_split'] is True
|
|
212
|
+
if is_split:
|
|
213
|
+
staged_transaction_model.add_split()
|
|
214
|
+
# import entry was selected for import...
|
|
215
|
+
is_import = tx_form.cleaned_data['tx_import']
|
|
216
|
+
if is_import:
|
|
217
|
+
# all entries in split will be going so the same journal entry... (same unit...)
|
|
218
|
+
is_bundled = tx_form.cleaned_data['bundle_split']
|
|
219
|
+
if staged_transaction_model.can_migrate_receipt():
|
|
220
|
+
staged_transaction_model.migrate_receipt(
|
|
221
|
+
receipt_date=staged_transaction_model.date_posted
|
|
222
|
+
)
|
|
223
|
+
else:
|
|
224
|
+
staged_transaction_model.migrate_transactions(
|
|
225
|
+
split_txs=not is_bundled
|
|
226
|
+
)
|
|
227
|
+
else:
|
|
228
|
+
context = self.get_context_data(txs_formset=txs_formset, **kwargs)
|
|
229
|
+
return self.render_to_response(context)
|
|
230
|
+
|
|
231
|
+
messages.add_message(
|
|
232
|
+
request,
|
|
233
|
+
messages.SUCCESS,
|
|
234
|
+
'Successfully saved transactions.',
|
|
235
|
+
extra_tags='is-success',
|
|
236
|
+
)
|
|
215
237
|
|
|
216
238
|
context = self.get_context_data(**kwargs)
|
|
217
239
|
return self.render_to_response(context)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
class StagedTransactionUndoView(ImportJobModelViewBaseView, View):
|
|
243
|
+
http_method_names = ['post']
|
|
244
|
+
|
|
245
|
+
def post(self, request, entity_slug, job_pk, staged_tx_pk, *args, **kwargs):
|
|
246
|
+
import_job_model = get_object_or_404(self.get_queryset(), uuid__exact=job_pk)
|
|
247
|
+
staged_txs_qs = import_job_model.stagedtransactionmodel_set.all()
|
|
248
|
+
staged_tx = get_object_or_404(staged_txs_qs, uuid__exact=staged_tx_pk)
|
|
249
|
+
try:
|
|
250
|
+
staged_tx.undo_import()
|
|
251
|
+
messages.add_message(
|
|
252
|
+
request,
|
|
253
|
+
messages.SUCCESS,
|
|
254
|
+
_('Successfully undone import for transaction %(name)s.')
|
|
255
|
+
% {'name': staged_tx.name or staged_tx.fit_id},
|
|
256
|
+
extra_tags='is-success',
|
|
257
|
+
)
|
|
258
|
+
except StagedTransactionModelValidationError as e:
|
|
259
|
+
messages.add_message(
|
|
260
|
+
request,
|
|
261
|
+
messages.ERROR,
|
|
262
|
+
e.message,
|
|
263
|
+
extra_tags='is-danger',
|
|
264
|
+
)
|
|
265
|
+
return redirect(
|
|
266
|
+
reverse(
|
|
267
|
+
'django_ledger:data-import-job-txs',
|
|
268
|
+
kwargs={'entity_slug': entity_slug, 'job_pk': job_pk},
|
|
269
|
+
)
|
|
270
|
+
)
|