mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
f7a1fb0a9d
@ -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
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
|
||||||
|
@ -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 %}
|
@ -130,9 +130,9 @@ 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 %}"
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
64
InvenTree/company/test_views.py
Normal file
64
InvenTree/company/test_views.py
Normal 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())
|
@ -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)),
|
||||||
]
|
]
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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',
|
||||||
|
@ -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 %}
|
||||||
|
Loading…
Reference in New Issue
Block a user