Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters 2020-10-01 14:10:24 +10:00
commit bbb6319792
20 changed files with 190 additions and 32 deletions

View File

@ -807,7 +807,19 @@ function launchModalForm(url, options = {}) {
}
},
error: function (xhr, ajaxOptions, thrownError) {
$(modal).modal('hide');
// Permission denied!
if (xhr.status == 403) {
showAlertDialog(
"Permission Denied",
"You do not have the required permissions to access this function"
);
return;
}
showAlertDialog('Error requesting form data', renderErrorMessage(xhr));
}
};

View File

@ -7,7 +7,7 @@ import django
import common.models
INVENTREE_SW_VERSION = "0.1.3 pre"
INVENTREE_SW_VERSION = "0.1.4 pre"
def inventreeInstanceName():

View File

@ -13,6 +13,8 @@ from django.template.loader import render_to_string
from django.http import JsonResponse, HttpResponseRedirect
from django.urls import reverse_lazy
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views import View
from django.views.generic import UpdateView, CreateView, FormView
from django.views.generic.base import TemplateView
@ -105,12 +107,32 @@ class TreeSerializer(views.APIView):
return JsonResponse(response, safe=False)
class AjaxMixin(object):
class AjaxMixin(PermissionRequiredMixin):
""" AjaxMixin provides basic functionality for rendering a Django form to JSON.
Handles jsonResponse rendering, and adds extra data for the modal forms to process
on the client side.
Any view which inherits the AjaxMixin will need
correct permissions set using the 'permission_required' attribute
"""
# By default, allow *any* permissions
permission_required = '*'
def has_permission(self):
"""
Override the default behaviour of has_permission from PermissionRequiredMixin.
Basically, if permission_required attribute = '*',
no permissions are actually required!
"""
if self.permission_required == '*':
return True
else:
return super().has_permission()
# By default, point to the modal_form template
# (this can be overridden by a child class)
ajax_template_name = 'modal_form.html'

View File

@ -20,6 +20,12 @@ class BuildAdmin(ImportExportModelAdmin):
'notes',
)
search_fields = [
'title',
'part__name',
'part__description',
]
class BuildItemAdmin(admin.ModelAdmin):

View File

@ -33,7 +33,12 @@ src="{% static 'img/blank_image.png' %}"
{% block page_data %}
<h3>{% trans "Build" %} {% build_status_label build.status large=True %}</h3>
<hr>
<h4>{{ build.quantity }} x {{ build.part.full_name }}</h4>
<h4>
{{ build.quantity }} x {{ build.part.full_name }}
{% if user.is_staff and perms.build.change_build %}
<a href="{% url 'admin:build_build_change' build.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
{% endif %}
</h4>
<div class='btn-row'>
<div class='btn-group action-buttons'>
<button type='button' class='btn btn-default' id='build-edit' title='Edit Build'>

View File

@ -32,6 +32,11 @@ class CompanyAdmin(ImportExportModelAdmin):
list_display = ('name', 'website', 'contact')
search_fields = [
'name',
'description',
]
class SupplierPartResource(ModelResource):
""" Class for managing SupplierPart data import/export """
@ -57,6 +62,13 @@ class SupplierPartAdmin(ImportExportModelAdmin):
list_display = ('part', 'supplier', 'SKU')
search_fields = [
'company__name',
'part__name',
'MPN',
'SKU',
]
class SupplierPriceBreakResource(ModelResource):
""" Class for managing SupplierPriceBreak data import/export """

View File

@ -21,7 +21,12 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
{% block page_data %}
<h3>{% trans "Company" %}</h3>
<hr>
<h4>{{ company.name }}</h4>
<h4>
{{ company.name }}
{% if user.is_staff and perms.company.change_company %}
<a href="{% url 'admin:company_company_change' company.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
{% endif %}
</h4>
<p>{{ company.description }}</p>
<div class='btn-group action-buttons'>
{% if company.is_supplier %}

View File

@ -23,6 +23,12 @@ class PurchaseOrderAdmin(ImportExportModelAdmin):
'creation_date'
)
search_fields = [
'reference',
'supplier__name',
'description',
]
class SalesOrderAdmin(ImportExportModelAdmin):
@ -34,6 +40,12 @@ class SalesOrderAdmin(ImportExportModelAdmin):
'creation_date',
)
search_fields = [
'reference',
'customer__name',
'description',
]
class POLineItemResource(ModelResource):
""" Class for managing import / export of POLineItem data """

View File

@ -22,7 +22,12 @@ src="{% static 'img/blank_image.png' %}"
{% block page_data %}
<h3>{% trans "Purchase Order" %} {% purchase_order_status_label order.status large=True %}</h3>
<hr>
<h4>{{ order }}</h4>
<h4>
{{ order }}
{% if user.is_staff and perms.order.change_purchaseorder %}
<a href="{% url 'admin:order_purchaseorder_change' order.pk %}"><span title='{% trans "Admin view" %}' class='fas fa-user-shield'></span></a>
{% endif %}
</h4>
<p>{{ order.description }}</p>
<p>
<div class='btn-row'>

View File

@ -32,7 +32,12 @@ src="{% static 'img/blank_image.png' %}"
<h3>{% trans "Sales Order" %} {% sales_order_status_label order.status large=True %}</h3>
<hr>
<h4>{{ order }}</h4>
<h4>
{{ order }}
{% if user.is_staff and perms.order.change_salesorder %}
<a href="{% url 'admin:order_salesorder_change' order.pk %}"><span title='{% trans "Admin view" %}' class='fas fa-user-shield'></span></a>
{% endif %}
</h4>
<p>{{ order.description }}</p>
<div class='btn-row'>
<div class='btn-group action-buttons'>

View File

@ -430,6 +430,17 @@ class PartList(generics.ListCreateAPIView):
except (ValueError, Part.DoesNotExist):
pass
# Filter by whether the part has an IPN (internal part number) defined
has_ipn = params.get('has_ipn', None)
if has_ipn is not None:
has_ipn = str2bool(has_ipn)
if has_ipn:
queryset = queryset.exclude(IPN='')
else:
queryset = queryset.filter(IPN='')
# Filter by whether the BOM has been validated (or not)
bom_valid = params.get('bom_valid', None)

View File

@ -7,7 +7,12 @@
<div class='row'>
<div class='col-sm-6'>
{% if category %}
<h3>{{ category.name }}</h3>
<h3>
{{ category.name }}
{% if user.is_staff and perms.part.change_partcategory %}
<a href="{% url 'admin:part_partcategory_change' category.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
{% endif %}
</h3>
<p>{{ category.description }}</p>
{% else %}
<h3>{% trans "Part Categories" %}</h3>
@ -109,10 +114,10 @@
</ul>
</div>
</div>
</div>
<div class='filter-list' id='filter-list-parts'>
<!-- Empty div -->
</div>
</div>
</div>
<table class='table table-striped table-condensed' data-toolbar='#button-toolbar' id='part-table'>

View File

@ -28,6 +28,9 @@
<div class="media-body">
<h3>
{{ part.full_name }}
{% if user.is_staff and perms.part.change_part %}
<a href="{% url 'admin:part_part_change' part.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
{% endif %}
{% if not part.active %}
<div class='label label-large label-large-red'>
{% trans 'Inactive' %}

View File

@ -117,6 +117,14 @@ class StockItemAdmin(ImportExportModelAdmin):
list_display = ('part', 'quantity', 'location', 'status', 'updated')
# A list of search fields which can be used for lookup on matching 'autocomplete' fields
search_fields = [
'part__name',
'part__description',
'serial',
'batch',
]
class StockAttachmentAdmin(admin.ModelAdmin):

View File

@ -61,9 +61,12 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
<hr>
<h4>
{% if item.serialized %}
<a href='{% url "part-detail" item.part.pk %}'>{{ item.part.full_name}}</a> # {{ item.serial }}
<a href='{% url "part-detail" item.part.pk %}'>{{ item.part.full_name}}</a> # {{ item.serial }}
{% else %}
<a href='{% url "part-detail" item.part.pk %}'>{{ item.part.full_name }}</a> &times {% decimal item.quantity %}
<a href='{% url "part-detail" item.part.pk %}'>{{ item.part.full_name }}</a> &times {% decimal item.quantity %}
{% endif %}
{% if user.is_staff and perms.stock.change_stockitem %}
<a href="{% url 'admin:stock_stockitem_change' item.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
{% endif %}
</h4>
@ -107,6 +110,11 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
{% if item.customer %}
<li><a href='#' id='stock-return-from-customer' title='{% trans "Return to stock" %}'><span class='fas fa-undo'></span> {% trans "Return to stock" %}</a></li>
{% endif %}
{% if item.belongs_to %}
<li>
<a href='#' id='stock-uninstall' title='{% trans "Uninstall stock item" %}'><span class='fas fa-unlink'></span> {% trans "Uninstall" %}</a>
</li>
{% endif %}
</ul>
</div>
<!-- Edit stock item -->
@ -165,8 +173,12 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
{% if item.belongs_to %}
<tr>
<td><span class='fas fa-box'></span></td>
<td>{% trans "Belongs To" %}</td>
<td><a href="{% url 'stock-item-detail' item.belongs_to.id %}">{{ item.belongs_to }}</a></td>
<td>
{% trans "Installed In" %}
</td>
<td>
<a href="{% url 'stock-item-detail' item.belongs_to.id %}">{{ item.belongs_to }}</a>
</td>
</tr>
{% elif item.sales_order %}
<tr>
@ -301,6 +313,19 @@ $("#stock-serialize").click(function() {
);
});
$('#stock-uninstall').click(function() {
launchModalForm(
"{% url 'stock-item-uninstall' %}",
{
data: {
'items[]': [{{ item.pk}}],
},
reload: true,
}
);
});
$("#stock-test-report").click(function() {
launchModalForm(
"{% url 'stock-item-test-report-select' item.id %}",

View File

@ -128,7 +128,7 @@ $('#installed-table').inventreeTable({
var html = `<div class='btn-group float-right' role='group'>`;
html += makeIconButton('fa-trash-alt icon-red', 'button-uninstall', pk, '{% trans "Uninstall item" %}');
html += makeIconButton('fa-unlink', 'button-uninstall', pk, '{% trans "Uninstall item" %}');
html += `</div>`;

View File

@ -6,7 +6,12 @@
<div class='row'>
<div class='col-sm-6'>
{% if location %}
<h3>{{ location.name }}</h3>
<h3>
{{ location.name }}
{% if user.is_staff and perms.stock.change_stocklocation %}
<a href="{% url 'admin:stock_stocklocation_change' location.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
{% endif %}
</h3>
<p>{{ location.description }}</p>
{% else %}
<h3>{% trans "Stock" %}</h3>

View File

@ -18,8 +18,10 @@ InvenTree | {% trans "Search Results" %}
<br><br>
<hr>
{% if query %}
<div id='no-search-results'>
<h4><i>{% trans "No results found" %}</i></h4>
<h4><i>{% trans "No results found for " %}'{{ query }}'</i></h4>
</div>
{% include "InvenTree/search_part_category.html" with collapse_id="categories" %}
@ -34,6 +36,14 @@ InvenTree | {% trans "Search Results" %}
{% include "InvenTree/search_stock_items.html" with collapse_id="stock" %}
{% else %}
<div id='empty-search-query'>
<h4><i>{% trans "Enter a search query" %}</i></h4>
</div>
{% endif %}
{% endblock %}
{% block js_load %}

View File

@ -173,6 +173,11 @@ function getAvailableTableFilters(tableKey) {
title: '{% trans "Include subcategories" %}',
description: '{% trans "Include parts in subcategories" %}',
},
has_ipn: {
type: 'bool',
title: '{% trans "Has IPN" %}',
description: '{% trans "Part has internal part number" %}',
},
active: {
type: 'bool',
title: '{% trans "Active" %}',

View File

@ -2,6 +2,7 @@
<div id='button-toolbar'>
<div class='button-toolbar container-fluid' style='float: right;'>
<div class='btn-group'>
<button class='btn btn-default' id='stock-export' title='{% trans "Export Stock Information" %}'>{% trans "Export" %}</button>
{% if read_only %}
{% else %}
@ -18,6 +19,7 @@
</ul>
</div>
{% endif %}
</div>
<div class='filter-list' id='filter-list-stock'>
<!-- An empty div in which the filter list will be constructed -->
</div>