Converting more forms to the API (#3181)

* Delete category via the API

* Delete StockLocation via the API

* Delete StockItem via the API

- Removes the final instance of AjaxDelete

* Remove URL path

* Add missing code
This commit is contained in:
Oliver 2022-06-11 21:53:26 +10:00 committed by GitHub
parent 63f1e58ca9
commit 090f4f4387
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 100 additions and 249 deletions

View File

@ -108,24 +108,6 @@ class HelperForm(forms.ModelForm):
self.helper.layout = Layout(*layouts)
class DeleteForm(forms.Form):
"""Generic deletion form which provides simple user confirmation."""
confirm_delete = forms.BooleanField(
required=False,
initial=False,
label=_('Confirm delete'),
help_text=_('Confirm item deletion')
)
class Meta:
"""Metaclass options."""
fields = [
'confirm_delete'
]
class EditUserForm(HelperForm):
"""Form for editing user information."""

View File

@ -34,8 +34,7 @@ from common.settings import currency_code_default, currency_codes
from part.models import PartCategory
from users.models import RuleSet, check_user_role
from .forms import DeleteForm, EditUserForm, SetPasswordForm
from .helpers import str2bool
from .forms import EditUserForm, SetPasswordForm
def auth_request(request):
@ -510,74 +509,6 @@ class AjaxUpdateView(AjaxMixin, UpdateView):
return self.renderJsonResponse(request, form, data)
class AjaxDeleteView(AjaxMixin, UpdateView):
"""An 'AJAXified DeleteView for removing an object from the DB.
- Returns a HTML object (not a form!) in JSON format (for delivery to a modal window)
- Handles deletion
"""
form_class = DeleteForm
ajax_form_title = _("Delete Item")
ajax_template_name = "modal_delete_form.html"
context_object_name = 'item'
def get_object(self):
"""Return object matched to the model of the calling class."""
try:
self.object = self.model.objects.get(pk=self.kwargs['pk'])
except Exception:
return None
return self.object
def get_form(self):
"""Returns a form instance for the form_class of the calling class."""
return self.form_class(self.get_form_kwargs())
def get(self, request, *args, **kwargs):
"""Respond to GET request.
- Render a DELETE confirmation form to JSON
- Return rendered form to client
"""
super(UpdateView, self).get(request, *args, **kwargs)
form = self.get_form()
context = self.get_context_data()
context[self.context_object_name] = self.get_object()
return self.renderJsonResponse(request, form, context=context)
def post(self, request, *args, **kwargs):
"""Respond to POST request.
- DELETE the object
- Render success message to JSON and return to client
"""
obj = self.get_object()
pk = obj.id
form = self.get_form()
confirmed = str2bool(request.POST.get('confirm_delete', False))
context = self.get_context_data()
if confirmed:
obj.delete()
else:
form.add_error('confirm_delete', _('Check box to confirm item deletion'))
context[self.context_object_name] = self.get_object()
data = {
'id': pk,
'form_valid': confirmed
}
return self.renderJsonResponse(request, form, data=data, context=context)
class EditUserView(AjaxUpdateView):
"""View for editing user information."""

View File

@ -347,23 +347,18 @@
{% if category %}
$("#cat-edit").click(function () {
editCategory({{ category.pk }});
});
{% if category.parent %}
var redirect = "{% url 'category-detail' category.parent.id %}";
{% else %}
var redirect = "{% url 'part-index' %}";
{% endif %}
$('#cat-delete').click(function() {
launchModalForm(
"{% url 'category-delete' category.id %}",
{
redirect: redirect
}
);
deletePartCategory({{ category.pk }}, {
{% if category.parent %}
redirect: "{% url 'category-detail' category.parent.id %}",
{% else %}
redirect: "{% url 'part-index' %}",
{% endif %}
});
});
{% endif %}

View File

@ -1,32 +0,0 @@
{% extends "modal_delete_form.html" %}
{% load i18n %}
{% block pre_form_content %}
<div class='alert alert-block alert-danger'>
{% trans "Are you sure you want to delete this part category?" %}
</div>
{% if category.children.all|length > 0 %}
<div class='alert alert-block alert-warning'>
{% blocktrans with n=category.children.all|length %}This category contains {{ n }} child categories{% endblocktrans %}.<br>
{% if category.parent %}
{% blocktrans with category=category.parent.name %}If this category is deleted, these child categories will be moved to {{ category }}{% endblocktrans %}.
{% else %}
{% trans "If this category is deleted, these child categories will be moved to the top level part category" %}.
{% endif %}
</div>
{% endif %}
{% if category.parts.all|length > 0 %}
<div class='alert alert-block alert-warning'>
{% blocktrans with n=category.parts.all|length %}This category contains {{ n }} parts{% endblocktrans %}.<br>
{% if category.parent %}
{% blocktrans with category=category.parent.name %}If this category is deleted, these parts will be moved to {{ category }}{% endblocktrans %}.
{% else %}
{% trans "If this category is deleted, these parts will be moved to the top level part category" %}.
{% endif %}
</div>
{% endif %}
{% endblock %}

View File

@ -33,11 +33,7 @@ category_urls = [
re_path(r'^subcategory/', views.PartIndex.as_view(template_name='part/subcategory.html'), name='category-index-subcategory'),
# Category detail views
re_path(r'(?P<pk>\d+)/', include([
re_path(r'^delete/', views.CategoryDelete.as_view(), name='category-delete'),
# Anything else
re_path(r'^.*$', views.CategoryDetail.as_view(), name='category-detail'),
]))
re_path(r'(?P<pk>\d+)/', views.CategoryDetail.as_view(), name='category-detail'),
]
# URL list for part web interface

View File

@ -24,8 +24,8 @@ from common.models import InvenTreeSetting
from common.views import FileManagementAjaxView, FileManagementFormView
from company.models import SupplierPart
from InvenTree.helpers import str2bool
from InvenTree.views import (AjaxDeleteView, AjaxUpdateView, AjaxView,
InvenTreeRoleMixin, QRCodeView)
from InvenTree.views import (AjaxUpdateView, AjaxView, InvenTreeRoleMixin,
QRCodeView)
from order.models import PurchaseOrderLineItem
from plugin.views import InvenTreePluginViewMixin
from stock.models import StockItem, StockLocation
@ -875,18 +875,3 @@ class CategoryDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
context['starred'] = category.is_starred_by(self.request.user)
return context
class CategoryDelete(AjaxDeleteView):
"""Delete view to delete a PartCategory."""
model = PartCategory
ajax_template_name = 'part/category_delete.html'
ajax_form_title = _('Delete Part Category')
context_object_name = 'category'
success_url = '/part/'
def get_data(self):
"""Return custom context data when the category is deleted"""
return {
'danger': _('Part category was deleted'),
}

View File

@ -581,12 +581,9 @@ $('#stock-add').click(function() {
});
$("#stock-delete").click(function () {
launchModalForm(
"{% url 'stock-item-delete' item.id %}",
{
redirect: "{% url 'part-detail' item.part.id %}"
}
);
deleteStockItem({{ item.pk }}, {
redirect: '{% url "part-detail" item.part.pk %}',
});
});
{% if item.part.can_convert %}
@ -599,7 +596,6 @@ $("#stock-convert").click(function() {
});
{% endif %}
{% if item.in_stock %}
$("#stock-assign-to-customer").click(function() {

View File

@ -1,15 +0,0 @@
{% extends "modal_delete_form.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block pre_form_content %}
<div class='alert alert-danger alert-block'>
{% trans "Are you sure you want to delete this stock item?" %}
<br>
{% decimal item.quantity as qty %}
{% blocktrans with full_name=item.part.full_name %}This will remove <strong>{{qty}}</strong> units of <strong>{{full_name}}</strong> from stock.{% endblocktrans %}
</div>
{% endblock %}

View File

@ -291,14 +291,15 @@
});
$('#location-delete').click(function() {
launchModalForm("{% url 'stock-location-delete' location.id %}",
{
redirect: "{% url 'stock-index' %}"
});
return false;
});
{% if location %}
deleteStockLocation({{ location.pk }}, {
{% if location.parent %}
redirect: '{% url "stock-location-detail" location.parent.pk %}',
{% else %}
redirect: '{% url "stock-index" %}',
{% endif %}
});
});
function adjustLocationStock(action) {
inventreeGet(
@ -329,8 +330,6 @@
adjustLocationStock('move');
});
{% endif %}
$('#show-qr-code').click(function() {
launchModalForm("{% url 'stock-location-qr' location.id %}",
{

View File

@ -1,34 +0,0 @@
{% extends "modal_delete_form.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block pre_form_content %}
<div class='alert alert-block alert-danger'>
{% trans "Are you sure you want to delete this stock location?" %}
</div>
{% if location.children.all|length > 0 %}
<div class='alert alert-block alert-warning'>
{% blocktrans with n=location.children.all|length %}This location contains {{ n }} child locations{% endblocktrans %}.<br>
{% if location.parent %}
{% blocktrans with location=location.parent.name %}If this location is deleted, these child locations will be moved to {{ location }}{% endblocktrans %}.
{% else %}
{% trans "If this location is deleted, these child locations will be moved to the top level stock location" %}.
{% endif %}
</div>
{% endif %}
{% if location.stock_items.all|length > 0 %}
<div class='alert alert-block alert-warning'>
{% blocktrans with n=location.stock_items.all|length %}This location contains {{ n }} stock items{% endblocktrans %}.<br>
{% if location.parent %}
{% blocktrans with location=location.parent.name %}If this location is deleted, these stock items will be moved to {{ location }}{% endblocktrans %}.
{% else %}
{% trans "If this location is deleted, these stock items will be moved to the top level stock location" %}.
{% endif %}
</div>
{% endif %}
{% endblock %}

View File

@ -7,7 +7,6 @@ from stock import views
location_urls = [
re_path(r'^(?P<pk>\d+)/', include([
re_path(r'^delete/?', views.StockLocationDelete.as_view(), name='stock-location-delete'),
re_path(r'^qr_code/?', views.StockLocationQRCode.as_view(), name='stock-location-qr'),
# Anything else - direct to the location detail view
@ -18,7 +17,6 @@ location_urls = [
stock_item_detail_urls = [
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'^qr_code/', views.StockItemQRCode.as_view(), name='stock-item-qr'),
# Anything else - direct to the item detail view

View File

@ -6,8 +6,7 @@ from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView, ListView
import common.settings
from InvenTree.views import (AjaxDeleteView, AjaxUpdateView,
InvenTreeRoleMixin, QRCodeView)
from InvenTree.views import AjaxUpdateView, InvenTreeRoleMixin, QRCodeView
from plugin.views import InvenTreePluginViewMixin
from . import forms as StockForms
@ -163,29 +162,3 @@ class StockItemConvert(AjaxUpdateView):
stock_item.convert_to_variant(variant, user=self.request.user)
return stock_item
class StockLocationDelete(AjaxDeleteView):
"""View to delete a StockLocation.
Presents a deletion confirmation form to the user
"""
model = StockLocation
success_url = '/stock'
ajax_template_name = 'stock/location_delete.html'
context_object_name = 'location'
ajax_form_title = _('Delete Stock Location')
class StockItemDelete(AjaxDeleteView):
"""View to delete a StockItem.
Presents a deletion confirmation form to the user
"""
model = StockItem
success_url = '/stock/'
ajax_template_name = 'stock/item_delete.html'
context_object_name = 'item'
ajax_form_title = _('Delete Stock Item')

View File

@ -21,6 +21,7 @@
/* exported
deletePart,
deletePartCategory,
duplicateBom,
duplicatePart,
editCategory,
@ -317,7 +318,31 @@ function editCategory(pk) {
title: '{% trans "Edit Part Category" %}',
reload: true,
});
}
/*
* Delete a PartCategory via the API
*/
function deletePartCategory(pk, options={}) {
var url = `/api/part/category/${pk}/`;
var html = `
<div class='alert alert-block alert-danger'>
{% trans "Are you sure you want to delete this part category?" %}
<ul>
<li>{% trans "Any child categories will be moved to the parent of this category" %}</li>
<li>{% trans "Any parts in this category will be moved to the parent of this category" %}</li>
</ul>
</div>`;
constructForm(url, {
title: '{% trans "Delete Part Category" %}',
method: 'DELETE',
preFormContent: html,
onSuccess: function(response) {
handleFormSuccess(response, options);
}
});
}

View File

@ -39,6 +39,8 @@
assignStockToCustomer,
createNewStockItem,
createStockLocation,
deleteStockItem,
deleteStockLocation,
duplicateStockItem,
editStockItem,
editStockLocation,
@ -156,6 +158,34 @@ function createStockLocation(options={}) {
}
/*
* Launch an API form to delete a StockLocation
*/
function deleteStockLocation(pk, options={}) {
var url = `/api/stock/location/${pk}/`;
var html = `
<div class='alert alert-block alert-danger'>
{% trans "Are you sure you want to delete this stock location?" %}
<ul>
<li>{% trans "Any child locations will be moved to the parent of this location" %}</li>
<li>{% trans "Any stock items in this location will be moved to the parent of this location" %}</li>
</ul>
</div>
`;
constructForm(url, {
title: '{% trans "Delete Stock Location" %}',
method: 'DELETE',
preFormContent: html,
onSuccess: function(response) {
handleFormSuccess(response, options);
}
});
}
function stockItemFields(options={}) {
var fields = {
part: {
@ -328,6 +358,28 @@ function duplicateStockItem(pk, options) {
}
/*
* Launch a modal form to delete a given StockItem
*/
function deleteStockItem(pk, options={}) {
var url = `/api/stock/${pk}/`;
var html = `
<div class='alert alert-block alert-danger'>
{% trans "Are you sure you want to delete this stock item?" %}
</div>`;
constructForm(url, {
method: 'DELETE',
title: '{% trans "Delete Stock Item" %}',
preFormContent: html,
onSuccess: function(response) {
handleFormSuccess(response, options);
}
});
}
/*
* Launch a modal form to edit a given StockItem
*/