mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 07:05:41 +00:00 
			
		
		
		
	Merge branch 'master' of git://github.com/inventree/InvenTree into parametric_part_tables
This commit is contained in:
		@@ -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));
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 
 | 
			
		||||
@@ -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():
 | 
			
		||||
 
 | 
			
		||||
@@ -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'
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,12 @@ class BuildAdmin(ImportExportModelAdmin):
 | 
			
		||||
        'notes',
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    search_fields = [
 | 
			
		||||
        'title',
 | 
			
		||||
        'part__name',
 | 
			
		||||
        'part__description',
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BuildItemAdmin(admin.ModelAdmin):
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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'>
 | 
			
		||||
 
 | 
			
		||||
@@ -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 """
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 """
 | 
			
		||||
 
 | 
			
		||||
@@ -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'>
 | 
			
		||||
 
 | 
			
		||||
@@ -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'>
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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' %}
 | 
			
		||||
 
 | 
			
		||||
@@ -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):
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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> × {% decimal item.quantity %}
 | 
			
		||||
    <a href='{% url "part-detail" item.part.pk %}'>{{ item.part.full_name }}</a> × {% 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 %}",
 | 
			
		||||
 
 | 
			
		||||
@@ -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>`;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
@@ -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 %}
 | 
			
		||||
 
 | 
			
		||||
@@ -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" %}',
 | 
			
		||||
 
 | 
			
		||||
@@ -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>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user