mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-06 15:28:49 +00:00
Refactor stock item test result form for API (#3143)
* Fix "polarity" of modal form submit button (cherry picked from commit 0e4550f28895af626715723e450792881f7fd882) * Use existing API functionality to delete all test results for a particular StockItem * Remove outdated forms / views
This commit is contained in:
parent
121c5d107e
commit
ea83d4b290
@ -16,13 +16,12 @@ from allauth.exceptions import ImmediateHttpResponse
|
|||||||
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
||||||
from allauth_2fa.adapter import OTPAdapter
|
from allauth_2fa.adapter import OTPAdapter
|
||||||
from allauth_2fa.utils import user_has_valid_totp_device
|
from allauth_2fa.utils import user_has_valid_totp_device
|
||||||
from crispy_forms.bootstrap import (AppendedText, Div, PrependedAppendedText,
|
from crispy_forms.bootstrap import (AppendedText, PrependedAppendedText,
|
||||||
PrependedText, StrictButton)
|
PrependedText)
|
||||||
from crispy_forms.helper import FormHelper
|
from crispy_forms.helper import FormHelper
|
||||||
from crispy_forms.layout import Field, Layout
|
from crispy_forms.layout import Field, Layout
|
||||||
|
|
||||||
from common.models import InvenTreeSetting
|
from common.models import InvenTreeSetting
|
||||||
from part.models import PartCategory
|
|
||||||
|
|
||||||
logger = logging.getLogger('inventree')
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
@ -109,22 +108,6 @@ class HelperForm(forms.ModelForm):
|
|||||||
self.helper.layout = Layout(*layouts)
|
self.helper.layout = Layout(*layouts)
|
||||||
|
|
||||||
|
|
||||||
class ConfirmForm(forms.Form):
|
|
||||||
"""Generic confirmation form."""
|
|
||||||
|
|
||||||
confirm = forms.BooleanField(
|
|
||||||
required=False, initial=False,
|
|
||||||
help_text=_("Confirm")
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'confirm'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class DeleteForm(forms.Form):
|
class DeleteForm(forms.Form):
|
||||||
"""Generic deletion form which provides simple user confirmation."""
|
"""Generic deletion form which provides simple user confirmation."""
|
||||||
|
|
||||||
@ -185,39 +168,6 @@ class SetPasswordForm(HelperForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SettingCategorySelectForm(forms.ModelForm):
|
|
||||||
"""Form for setting category settings."""
|
|
||||||
|
|
||||||
category = forms.ModelChoiceField(queryset=PartCategory.objects.all())
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = PartCategory
|
|
||||||
fields = [
|
|
||||||
'category'
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Setup form layout."""
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.helper = FormHelper()
|
|
||||||
# Form rendering
|
|
||||||
self.helper.form_show_labels = False
|
|
||||||
self.helper.layout = Layout(
|
|
||||||
Div(
|
|
||||||
Div(Field('category'),
|
|
||||||
css_class='col-sm-6',
|
|
||||||
style='width: 70%;'),
|
|
||||||
Div(StrictButton(_('Select Category'), css_class='btn btn-primary', type='submit'),
|
|
||||||
css_class='col-sm-6',
|
|
||||||
style='width: 30%; padding-left: 0;'),
|
|
||||||
css_class='row',
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# override allauth
|
# override allauth
|
||||||
class CustomSignupForm(SignupForm):
|
class CustomSignupForm(SignupForm):
|
||||||
"""Override to use dynamic settings."""
|
"""Override to use dynamic settings."""
|
||||||
|
@ -37,7 +37,7 @@ from .views import (AppearanceSelectView, CurrencyRefreshView,
|
|||||||
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
CustomSessionDeleteOtherView, CustomSessionDeleteView,
|
||||||
DatabaseStatsView, DynamicJsView, EditUserView, IndexView,
|
DatabaseStatsView, DynamicJsView, EditUserView, IndexView,
|
||||||
NotificationsView, SearchView, SetPasswordView,
|
NotificationsView, SearchView, SetPasswordView,
|
||||||
SettingCategorySelectView, SettingsView, auth_request)
|
SettingsView, auth_request)
|
||||||
|
|
||||||
admin.site.site_header = "InvenTree Admin"
|
admin.site.site_header = "InvenTree Admin"
|
||||||
|
|
||||||
@ -74,8 +74,6 @@ settings_urls = [
|
|||||||
re_path(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
|
re_path(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
|
||||||
re_path(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),
|
re_path(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),
|
||||||
|
|
||||||
re_path(r'^category/', SettingCategorySelectView.as_view(), name='settings-category'),
|
|
||||||
|
|
||||||
# Catch any other urls
|
# Catch any other urls
|
||||||
re_path(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'),
|
re_path(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'),
|
||||||
]
|
]
|
||||||
|
@ -17,8 +17,8 @@ from django.urls import reverse_lazy
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from django.views.generic import (CreateView, DeleteView, DetailView, FormView,
|
from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
|
||||||
ListView, UpdateView)
|
UpdateView)
|
||||||
from django.views.generic.base import RedirectView, TemplateView
|
from django.views.generic.base import RedirectView, TemplateView
|
||||||
|
|
||||||
from allauth.account.forms import AddEmailForm
|
from allauth.account.forms import AddEmailForm
|
||||||
@ -34,8 +34,7 @@ from common.settings import currency_code_default, currency_codes
|
|||||||
from part.models import PartCategory
|
from part.models import PartCategory
|
||||||
from users.models import RuleSet, check_user_role
|
from users.models import RuleSet, check_user_role
|
||||||
|
|
||||||
from .forms import (DeleteForm, EditUserForm, SetPasswordForm,
|
from .forms import DeleteForm, EditUserForm, SetPasswordForm
|
||||||
SettingCategorySelectForm)
|
|
||||||
from .helpers import str2bool
|
from .helpers import str2bool
|
||||||
|
|
||||||
|
|
||||||
@ -801,40 +800,6 @@ class AppearanceSelectView(RedirectView):
|
|||||||
return redirect(reverse_lazy('settings'))
|
return redirect(reverse_lazy('settings'))
|
||||||
|
|
||||||
|
|
||||||
class SettingCategorySelectView(FormView):
|
|
||||||
"""View for selecting categories in settings."""
|
|
||||||
|
|
||||||
form_class = SettingCategorySelectForm
|
|
||||||
success_url = reverse_lazy('settings-category')
|
|
||||||
template_name = "InvenTree/settings/category.html"
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
"""Set category selection."""
|
|
||||||
initial = super().get_initial()
|
|
||||||
|
|
||||||
category = self.request.GET.get('category', None)
|
|
||||||
if category:
|
|
||||||
initial['category'] = category
|
|
||||||
|
|
||||||
return initial
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
"""Handle POST request (which contains category selection).
|
|
||||||
|
|
||||||
Pass the selected category to the page template
|
|
||||||
"""
|
|
||||||
form = self.get_form()
|
|
||||||
|
|
||||||
if form.is_valid():
|
|
||||||
context = self.get_context_data()
|
|
||||||
|
|
||||||
context['category'] = form.cleaned_data['category']
|
|
||||||
|
|
||||||
return super(SettingCategorySelectView, self).render_to_response(context)
|
|
||||||
|
|
||||||
return self.form_invalid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseStatsView(AjaxView):
|
class DatabaseStatsView(AjaxView):
|
||||||
"""View for displaying database statistics."""
|
"""View for displaying database statistics."""
|
||||||
|
|
||||||
|
@ -265,10 +265,49 @@
|
|||||||
|
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
$("#delete-test-results").click(function() {
|
$("#delete-test-results").click(function() {
|
||||||
launchModalForm(
|
|
||||||
"{% url 'stock-item-delete-test-data' item.id %}",
|
var url = '{% url "api-stock-test-result-list" %}';
|
||||||
|
|
||||||
|
inventreeGet(
|
||||||
|
url,
|
||||||
{
|
{
|
||||||
success: reloadTable,
|
stock_item: {{ item.pk }},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
success: function(response) {
|
||||||
|
|
||||||
|
var results = [];
|
||||||
|
|
||||||
|
// Ensure that we are only deleting the correct test results
|
||||||
|
response.forEach(function(item) {
|
||||||
|
if (item.stock_item == {{ item.pk }}) {
|
||||||
|
results.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var html = `
|
||||||
|
<div class='alert alert-block alert-danger'>
|
||||||
|
{% trans "Delete all test results for this stock item" %}
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
constructFormBody({}, {
|
||||||
|
method: 'DELETE',
|
||||||
|
title: '{% trans "Delete Test Data" %}',
|
||||||
|
preFormContent: html,
|
||||||
|
onSubmit: function(fields, opts) {
|
||||||
|
inventreeMultiDelete(
|
||||||
|
url,
|
||||||
|
results,
|
||||||
|
{
|
||||||
|
modal: opts.modal,
|
||||||
|
success: function() {
|
||||||
|
reloadTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -20,7 +20,6 @@ stock_item_detail_urls = [
|
|||||||
re_path(r'^convert/', views.StockItemConvert.as_view(), name='stock-item-convert'),
|
re_path(r'^convert/', views.StockItemConvert.as_view(), name='stock-item-convert'),
|
||||||
re_path(r'^delete/', views.StockItemDelete.as_view(), name='stock-item-delete'),
|
re_path(r'^delete/', views.StockItemDelete.as_view(), name='stock-item-delete'),
|
||||||
re_path(r'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'),
|
re_path(r'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'),
|
||||||
re_path(r'^delete_test_data/', views.StockItemDeleteTestData.as_view(), name='stock-item-delete-test-data'),
|
|
||||||
|
|
||||||
# Anything else - direct to the item detail view
|
# Anything else - direct to the item detail view
|
||||||
re_path('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'),
|
re_path('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'),
|
||||||
|
@ -6,8 +6,6 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
|
|
||||||
import common.settings
|
import common.settings
|
||||||
from InvenTree.forms import ConfirmForm
|
|
||||||
from InvenTree.helpers import str2bool
|
|
||||||
from InvenTree.views import (AjaxDeleteView, AjaxUpdateView,
|
from InvenTree.views import (AjaxDeleteView, AjaxUpdateView,
|
||||||
InvenTreeRoleMixin, QRCodeView)
|
InvenTreeRoleMixin, QRCodeView)
|
||||||
from plugin.views import InvenTreePluginViewMixin
|
from plugin.views import InvenTreePluginViewMixin
|
||||||
@ -123,42 +121,6 @@ class StockLocationQRCode(QRCodeView):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
class StockItemDeleteTestData(AjaxUpdateView):
|
|
||||||
"""View for deleting all test data."""
|
|
||||||
|
|
||||||
model = StockItem
|
|
||||||
form_class = ConfirmForm
|
|
||||||
ajax_form_title = _("Delete All Test Data")
|
|
||||||
|
|
||||||
role_required = ['stock.change', 'stock.delete']
|
|
||||||
|
|
||||||
def get_form(self):
|
|
||||||
"""Require confirm."""
|
|
||||||
return ConfirmForm()
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
"""Delete test data."""
|
|
||||||
valid = False
|
|
||||||
|
|
||||||
stock_item = StockItem.objects.get(pk=self.kwargs['pk'])
|
|
||||||
form = self.get_form()
|
|
||||||
|
|
||||||
confirm = str2bool(request.POST.get('confirm', False))
|
|
||||||
|
|
||||||
if confirm is not True:
|
|
||||||
form.add_error('confirm', _('Confirm test data deletion'))
|
|
||||||
form.add_error(None, _('Check the confirmation box'))
|
|
||||||
else:
|
|
||||||
stock_item.test_results.all().delete()
|
|
||||||
valid = True
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'form_valid': valid,
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data)
|
|
||||||
|
|
||||||
|
|
||||||
class StockItemQRCode(QRCodeView):
|
class StockItemQRCode(QRCodeView):
|
||||||
"""View for displaying a QR code for a StockItem object."""
|
"""View for displaying a QR code for a StockItem object."""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user