Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Oliver Walters 2019-09-30 13:54:49 +10:00
commit f7a1fb0a9d
10 changed files with 209 additions and 16 deletions

View File

@ -164,6 +164,8 @@ class AjaxMixin(object):
if form: if form:
context['form'] = form context['form'] = form
else:
context['form'] = None
data['title'] = self.ajax_form_title data['title'] = self.ajax_form_title

View File

@ -15,6 +15,7 @@
<span class="caret"></span></button> <span class="caret"></span></button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href='#' id='multi-part-order' title='Order parts'>Order Parts</a></li> <li><a href='#' id='multi-part-order' title='Order parts'>Order Parts</a></li>
<li><a href='#' id='multi-part-delete' title='Delete parts'>Delete Parts</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -98,6 +99,23 @@
url: "{% url 'api-part-supplier-list' %}" url: "{% url 'api-part-supplier-list' %}"
}); });
$("#multi-part-delete").click(function() {
var selections = $("#part-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.pk);
});
launchModalForm("{% url 'supplier-part-delete' %}", {
data: {
parts: parts,
},
reload: true,
});
});
$("#multi-part-order").click(function() { $("#multi-part-order").click(function() {
var selections = $("#part-table").bootstrapTable("getSelections"); var selections = $("#part-table").bootstrapTable("getSelections");

View File

@ -1,5 +1,28 @@
{% extends "modal_delete_form.html" %} {% extends "modal_delete_form.html" %}
{% load i18n %}
{% block pre_form_content %} {% block pre_form_content %}
Are you sure you want to delete this supplier part? {% trans "Are you sure you want to delete the following Supplier Parts?" %}
<hr>
{% endblock %}
{% block form_data %}
<table class='table table-striped table-condensed'>
{% for part in parts %}
<tr>
<input type='hidden' name='supplier-part-{{ part.id}}' value='supplier-part-{{ part.id }}'/>
<td>
{% include "hover_image.html" with image=part.supplier.image %}
{{ part.supplier.name }}
</td>
<td>
{% include "hover_image.html" with image=part.part.image %}
{{ part.part.full_name }}
</td>
</tr>
{% endfor %}
</table>
{% endblock %} {% endblock %}

View File

@ -130,10 +130,10 @@ InvenTree | {{ company.name }} - Parts
$('#delete-part').click(function() { $('#delete-part').click(function() {
launchModalForm( launchModalForm(
"{% url 'supplier-part-delete' part.id %}", "{% url 'supplier-part-delete' %}?part={{ part.id }}",
{ {
redirect: "{% url 'company-index' %}" redirect: "{% url 'company-detail-parts' part.supplier.id %}"
} }
); );
}); });

View File

@ -0,0 +1,64 @@
""" Unit tests for Company views (see views.py) """
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.test import TestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
from .models import SupplierPart
class CompanyViewTest(TestCase):
fixtures = [
'category',
'part',
'location',
'company',
'supplier_part',
]
def setUp(self):
super().setUp()
# Create a user
User = get_user_model()
User.objects.create_user('username', 'user@email.com', 'password')
self.client.login(username='username', password='password')
def test_company_index(self):
""" Test the company index """
response = self.client.get(reverse('company-index'))
self.assertEqual(response.status_code, 200)
def test_supplier_part_delete(self):
""" Test the SupplierPartDelete view """
url = reverse('supplier-part-delete')
# Get form using 'part' argument
response = self.client.get(url, {'part': '1'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
# Get form using 'parts' argument
response = self.client.get(url + '?parts[]=1&parts[]=2', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
# POST to delete two parts
n = SupplierPart.objects.count()
response = self.client.post(
url,
{
'supplier-part-2': 'supplier-part-2',
'supplier-part-3': 'supplier-part-3',
'confirm_delete': True
},
HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
self.assertEqual(n - 2, SupplierPart.objects.count())

View File

@ -47,7 +47,6 @@ price_break_urls = [
supplier_part_detail_urls = [ supplier_part_detail_urls = [
url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'), url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'),
url(r'delete/?', views.SupplierPartDelete.as_view(), name='supplier-part-delete'),
url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'), url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'),
] ]
@ -55,5 +54,7 @@ supplier_part_detail_urls = [
supplier_part_urls = [ supplier_part_urls = [
url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'), url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'),
url(r'delete/', views.SupplierPartDelete.as_view(), name='supplier-part-delete'),
url(r'^(?P<pk>\d+)/', include(supplier_part_detail_urls)), url(r'^(?P<pk>\d+)/', include(supplier_part_detail_urls)),
] ]

View File

@ -12,6 +12,7 @@ from django.forms import HiddenInput
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.status_codes import OrderStatus from InvenTree.status_codes import OrderStatus
from InvenTree.helpers import str2bool
from .models import Company from .models import Company
from .models import SupplierPart from .models import SupplierPart
@ -197,12 +198,80 @@ class SupplierPartCreate(AjaxCreateView):
class SupplierPartDelete(AjaxDeleteView): class SupplierPartDelete(AjaxDeleteView):
""" Delete view for removing a SupplierPart """ """ Delete view for removing a SupplierPart.
model = SupplierPart
SupplierParts can be deleted using a variety of 'selectors'.
- ?part=<pk> -> Delete a single SupplierPart object
- ?parts=[] -> Delete a list of SupplierPart objects
"""
success_url = '/supplier/' success_url = '/supplier/'
ajax_template_name = 'company/partdelete.html' ajax_template_name = 'company/partdelete.html'
ajax_form_title = 'Delete Supplier Part' ajax_form_title = 'Delete Supplier Part'
context_object_name = 'supplier_part'
parts = []
def get_context_data(self):
ctx = {}
ctx['parts'] = self.parts
return ctx
def get_parts(self):
""" Determine which SupplierPart object(s) the user wishes to delete.
"""
self.parts = []
# User passes a single SupplierPart ID
if 'part' in self.request.GET:
try:
self.parts.append(SupplierPart.objects.get(pk=self.request.GET.get('part')))
except (ValueError, SupplierPart.DoesNotExist):
pass
elif 'parts[]' in self.request.GET:
part_id_list = self.request.GET.getlist('parts[]')
self.parts = SupplierPart.objects.filter(id__in=part_id_list)
def get(self, request, *args, **kwargs):
self.request = request
self.get_parts()
return self.renderJsonResponse(request, form=self.get_form())
def post(self, request, *args, **kwargs):
""" Handle the POST action for deleting supplier parts.
"""
self.request = request
self.parts = []
for item in self.request.POST:
if item.startswith('supplier-part-'):
pk = item.replace('supplier-part-', '')
try:
self.parts.append(SupplierPart.objects.get(pk=pk))
except (ValueError, SupplierPart.DoesNotExist):
pass
confirm = str2bool(self.request.POST.get('confirm_delete', False))
data = {
'form_valid': confirm,
}
if confirm:
for part in self.parts:
part.delete()
return self.renderJsonResponse(self.request, data=data, form=self.get_form())
class PriceBreakCreate(AjaxCreateView): class PriceBreakCreate(AjaxCreateView):

View File

@ -1,5 +1,8 @@
""" Unit tests for Order views (see views.py) """ """ Unit tests for Order views (see views.py) """
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
from django.urls import reverse from django.urls import reverse
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model

View File

@ -9,7 +9,6 @@
<div id='button-toolbar'> <div id='button-toolbar'>
<button class="btn btn-success" id='supplier-create'>New Supplier Part</button> <button class="btn btn-success" id='supplier-create'>New Supplier Part</button>
{% if 0 %}
<div id='opt-dropdown' class="dropdown" style='float: right;'> <div id='opt-dropdown' class="dropdown" style='float: right;'>
<button id='supplier-part-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Options <button id='supplier-part-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Options
<span class="caret"></span></button> <span class="caret"></span></button>
@ -17,7 +16,6 @@
<li><a href='#' id='supplier-part-delete' title='Delete supplier parts'>Delete</a></li> <li><a href='#' id='supplier-part-delete' title='Delete supplier parts'>Delete</a></li>
</ul> </ul>
</div> </div>
{% endif %}
</div> </div>
<table class="table table-striped table-condensed" id='supplier-table' data-toolbar='#button-toolbar'> <table class="table table-striped table-condensed" id='supplier-table' data-toolbar='#button-toolbar'>
@ -50,6 +48,24 @@
}); });
}); });
$("#supplier-part-delete").click(function() {
var selections = $("#supplier-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.pk);
});
launchModalForm("{% url 'supplier-part-delete' %}", {
data: {
parts: parts,
},
reload: true,
});
});
$("#supplier-table").inventreeTable({ $("#supplier-table").inventreeTable({
formatNoMatches: function() { return "No supplier parts available for {{ part.full_name }}"; }, formatNoMatches: function() { return "No supplier parts available for {{ part.full_name }}"; },
queryParams: function(p) { queryParams: function(p) {
@ -58,12 +74,9 @@
} }
}, },
columns: [ columns: [
/*
// TODO - Re-enable the checkbox column for performing actions on multiple supplier parts
{ {
checkbox: true, checkbox: true,
}, },
*/
{ {
sortable: true, sortable: true,
field: 'supplier_name', field: 'supplier_name',

View File

@ -27,11 +27,11 @@
{% csrf_token %} {% csrf_token %}
{% load crispy_forms_tags %} {% load crispy_forms_tags %}
{% block form_data %}
{% endblock %}
{% crispy form %} {% crispy form %}
{% block form_data %}
{% endblock %}
</form> </form>
{% endblock %} {% endblock %}