Merge branch 'inventree:master' into webhooks

This commit is contained in:
Matthias Mair 2021-12-02 09:22:48 +01:00 committed by GitHub
commit fcf7f615aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 14255 additions and 13007 deletions

View File

@ -257,7 +257,7 @@ INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'user_sessions', # db user sessions
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
@ -299,7 +299,7 @@ INSTALLED_APPS = [
MIDDLEWARE = CONFIG.get('middleware', [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'user_sessions.middleware.SessionMiddleware', # db user sessions
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
@ -626,6 +626,12 @@ if _cache_host:
# as well
Q_CLUSTER["django_redis"] = "worker"
# database user sessions
SESSION_ENGINE = 'user_sessions.backends.db'
LOGOUT_REDIRECT_URL = 'index'
SILENCED_SYSTEM_CHECKS = [
'admin.E410',
]
# Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators

View File

@ -781,6 +781,7 @@ input[type="submit"] {
.btn-small {
padding: 3px;
padding-left: 5px;
padding-right: 5px;
}
.btn-remove {

View File

@ -38,6 +38,7 @@ from rest_framework.documentation import include_docs_urls
from .views import auth_request
from .views import IndexView, SearchView, DatabaseStatsView
from .views import SettingsView, EditUserView, SetPasswordView, CustomEmailView, CustomConnectionsView, CustomPasswordResetFromKeyView
from .views import CustomSessionDeleteView, CustomSessionDeleteOtherView
from .views import CurrencyRefreshView
from .views import AppearanceSelectView, SettingCategorySelectView
from .views import DynamicJsView
@ -159,6 +160,10 @@ urlpatterns = [
url(r'^markdownx/', include('markdownx.urls')),
# DB user sessions
url(r'^accounts/sessions/other/delete/$', view=CustomSessionDeleteOtherView.as_view(), name='session_delete_other', ),
url(r'^accounts/sessions/(?P<pk>\w+)/delete/$', view=CustomSessionDeleteView.as_view(), name='session_delete', ),
# Single Sign On / allauth
# overrides of urlpatterns
url(r'^accounts/email/', CustomEmailView.as_view(), name='account_email'),

View File

@ -12,11 +12,15 @@ import common.models
INVENTREE_SW_VERSION = "0.6.0 dev"
# InvenTree API version
INVENTREE_API_VERSION = 18
INVENTREE_API_VERSION = 19
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
v19 -> 2021-12-02
- Adds the ability to filter the StockItem API by "part_tree"
- Returns only stock items which match a particular part.tree_id field
v18 -> 2021-11-15
- Adds the ability to filter BomItem API by "uses" field
- This returns a list of all BomItems which "use" the specified part

View File

@ -14,6 +14,7 @@ from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from django.urls import reverse_lazy
from django.utils.timezone import now
from django.shortcuts import redirect
from django.conf import settings
@ -29,6 +30,7 @@ from allauth.socialaccount.forms import DisconnectForm
from allauth.account.models import EmailAddress
from allauth.account.views import EmailView, PasswordResetFromKeyView
from allauth.socialaccount.views import ConnectionsView
from user_sessions.views import SessionDeleteView, SessionDeleteOtherView
from common.settings import currency_code_default, currency_codes
@ -733,6 +735,10 @@ class SettingsView(TemplateView):
ctx["request"] = self.request
ctx['social_form'] = DisconnectForm(request=self.request)
# user db sessions
ctx['session_key'] = self.request.session.session_key
ctx['session_list'] = self.request.user.session_set.filter(expire_date__gt=now()).order_by('-last_activity')
return ctx
@ -766,6 +772,20 @@ class CustomPasswordResetFromKeyView(PasswordResetFromKeyView):
success_url = reverse_lazy("account_login")
class UserSessionOverride():
"""overrides sucessurl to lead to settings"""
def get_success_url(self):
return str(reverse_lazy('settings'))
class CustomSessionDeleteView(UserSessionOverride, SessionDeleteView):
pass
class CustomSessionDeleteOtherView(UserSessionOverride, SessionDeleteOtherView):
pass
class CurrencyRefreshView(RedirectView):
"""
POST endpoint to refresh / update exchange rates

View File

@ -12,7 +12,7 @@
{% block breadcrumbs %}
<li class='breadcrumb-item'><a href='{% url "build-index" %}'>{% trans "Build Orders" %}</a></li>
<li class="breadcrumb-item active" aria-current="page"><a href='{% url "build-detail" build.id %}'>{{ build }}</a></li>
{% endblock %}
{% endblock breadcrumbs %}
{% block thumbnail %}
<img class="part-thumb"
@ -21,7 +21,7 @@ src="{{ build.part.image.url }}"
{% else %}
src="{% static 'img/blank_image.png' %}"
{% endif %}/>
{% endblock %}
{% endblock thumbnail %}
{% block heading %}
{% trans "Build Order" %} {{ build }}
@ -66,11 +66,23 @@ src="{% static 'img/blank_image.png' %}"
</button>
{% endif %}
{% endif %}
{% endblock %}
{% endblock actions %}
{% block details %}
<p>{{ build.title }}</p>
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Part" %}</td>
<td><a href="{% url 'part-detail' build.part.id %}?display=build-orders">{{ build.part.full_name }}</a></td>
</tr>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Build Description" %}</td>
<td>{{ build.title }}</td>
</tr>
</table>
<div class='info-messages'>
{% if build.sales_order %}
@ -114,11 +126,7 @@ src="{% static 'img/blank_image.png' %}"
{% block details_right %}
<table class='table table-striped table-condensed'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Part" %}</td>
<td><a href="{% url 'part-detail' build.part.id %}?display=build-orders">{{ build.part.full_name }}</a></td>
</tr>
<col width='25'>
<tr>
<td></td>
<td>{% trans "Quantity" %}</td>

View File

@ -19,21 +19,26 @@
{% include "admin_button.html" with url=url %}
{% endif %}
{% if company.is_supplier and roles.purchase_order.add %}
<button type='button' class='btn btn-outline-secondary' id='company-order-2' title='{% trans "Create Purchase Order" %}'>
<button type='button' class='btn btn-outline-primary' id='company-order-2' title='{% trans "Create Purchase Order" %}'>
<span class='fas fa-shopping-cart'/>
</button>
{% endif %}
{% if perms.company.change_company %}
<button type='button' class='btn btn-outline-secondary' id='company-edit' title='{% trans "Edit company information" %}'>
<span class='fas fa-edit icon-green'/>
<button id='company-edit-actions' title='{% trans "Company actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
<span class='fas fa-tools'></span> <span class='caret'></span>
</button>
{% endif %}
{% if perms.company.delete_company %}
<button type='button' class='btn btn-outline-secondary' id='company-delete' title='{% trans "Delete Company" %}'>
<span class='fas fa-trash-alt icon-red'/>
</button>
{% endif %}
{% endblock %}
<ul class='dropdown-menu' role='menu'>
{% if perms.company.change_company %}
<li><a class='dropdown-item' href='#' id='company-edit' title='{% trans "Edit company information" %}'>
<span class='fas fa-edit icon-green'></span> {% trans "Edit Company" %}
</a></li>
{% endif %}
{% if perms.company.delete_company %}
<li><a class='dropdown-item' href='#' id='company-delete' title='{% trans "Delete company" %}'>
<span class='fas fa-trash-alt icon-red'></span> {% trans "Delete Company" %}
</a></li>
{% endif %}
</ul>
{% endblock actions %}
{% block thumbnail %}
<div class='dropzone part-thumb-container' id='company-thumb'>
@ -56,7 +61,29 @@
{% endblock %}
{% block details %}
<p>{{ company.description }}</p>
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ company.description }}</td>
</tr>
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{%trans "Manufacturer" %}</td>
<td>{% include "yesnolabel.html" with value=company.is_manufacturer %}</td>
</tr>
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Supplier" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_supplier %}</td>
</tr>
<tr>
<td><span class='fas fa-user-tie'></span></td>
<td>{% trans "Customer" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_customer %}</td>
</tr>
</table>
{% endblock %}
@ -110,22 +137,6 @@
<td>{{ company.contact }}{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{%trans "Manufacturer" %}</td>
<td>{% include "yesnolabel.html" with value=company.is_manufacturer %}</td>
</tr>
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Supplier" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_supplier %}</td>
</tr>
<tr>
<td><span class='fas fa-user-tie'></span></td>
<td>{% trans "Customer" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_customer %}</td>
</tr>
</table>
{% endblock %}

View File

@ -8,7 +8,7 @@ InvenTree | {% trans "Manufacturer Part" %}
{% block sidebar %}
{% include "company/manufacturer_part_sidebar.html" %}
{% endblock %}
{% endblock sidebar %}
{% block breadcrumbs %}
<li class='breadcrumb-item'><a href='{% url "manufacturer-index" %}'>{% trans "Manufacturers" %}</a></li>
@ -16,13 +16,13 @@ InvenTree | {% trans "Manufacturer Part" %}
<li class='breadcrumb-item'><a href='{% url "company-detail" part.manufacturer.id %}'>{{ part.manufacturer.name }}</a></li>
{% endif %}
<li class="breadcrumb-item active" aria-current="page"><a href='{% url "manufacturer-part-detail" part.id %}'>{{ part.MPN }}</a></li>
{% endblock %}
{% endblock breadcrumbs %}
{% block heading %}
<h4>
{% trans "Manufacturer Part" %}: {{ part.part.full_name }}
</h4>
{% endblock %}
{% endblock heading %}
{% block actions %}
{% if user.is_staff and perms.company.change_company %}
@ -46,7 +46,7 @@ InvenTree | {% trans "Manufacturer Part" %}
</button>
{% endif %}
{% endif %}
{% endblock %}
{% endblock actions %}
{% block thumbnail %}
<img class='part-thumb'
@ -55,50 +55,56 @@ src='{{ part.part.image.url }}'
{% else %}
src="{% static 'img/blank_image.png' %}"
{% endif %}/>
{% endblock %}
{% endblock thumbnail %}
{% block details %}
{% endblock %}
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-detail' part.part.id %}?display=part-suppliers">{{ part.part.full_name }}</a>{% include "clip.html"%}
{% endif %}
</td>
</tr>
{% if part.description %}
<tr>
<td></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
{% endif %}
</table>
{% endblock details %}
{% block details_right %}
<table class="table table-striped table-condensed">
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-detail' part.part.id %}?display=part-suppliers">{{ part.part.full_name }}</a>{% include "clip.html"%}
{% endif %}
</td>
</tr>
{% if part.description %}
<tr>
<td></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{% trans "Manufacturer" %}</td>
<td><a href="{% url 'company-detail' part.manufacturer.id %}">{{ part.manufacturer.name }}</a>{% include "clip.html"%}</td></tr>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "MPN" %}</td>
<td>{{ part.MPN }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{% trans "Manufacturer" %}</td>
<td><a href="{% url 'company-detail' part.manufacturer.id %}">{{ part.manufacturer.name }}</a>{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "MPN" %}</td>
<td>{{ part.MPN }}{% include "clip.html"%}</td>
</tr>
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
</table>
{% endblock %}
{% endblock details_right %}
{% block page_content %}

View File

@ -5,11 +5,11 @@
{% block page_title %}
{% inventree_title %} | {% trans "Supplier Part" %}
{% endblock %}
{% endblock page_title %}
{% block sidebar %}
{% include "company/supplier_part_sidebar.html" %}
{% endblock %}
{% endblock sidebar %}
{% block breadcrumbs %}
<li class='breadcrumb-item'><a href='{% url "supplier-index" %}'>{% trans "Suppliers" %}</a></li>
@ -17,13 +17,13 @@
<li class='breadcrumb-item'><a href='{% url "company-detail" part.supplier.id %}'>{{ part.supplier.name }}</a></li>
{% endif %}
<li class="breadcrumb-item active" aria-current="page"><a href='{% url "supplier-part-detail" part.id %}'>{{ part.SKU }}</a></li>
{% endblock %}
{% endblock breadcrumbs %}
{% block heading %}
<h4>
{% trans "Supplier Part" %}: {{ part.SKU }}
</h4>
{% endblock %}
{% endblock heading %}
{% block actions %}
{% if user.is_staff and perms.company.change_company %}
@ -43,7 +43,7 @@
<span class='fas fa-trash-alt icon-red'/>
</button>
{% endif %}
{% endblock %}
{% endblock actions %}
{% block thumbnail %}
<img class='part-thumb'
@ -56,39 +56,32 @@ src="{% static 'img/blank_image.png' %}"
{% block details %}
<p>
{{ part.part.full_name }}
</p>
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-detail' part.part.id %}?display=part-suppliers">{{ part.part.full_name }}</a>{% include "clip.html"%}
{% endif %}
</td>
</tr>
{% if part.description %}
<tr>
<td></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
{% endif %}
</table>
{% endblock %}
{% endblock details %}
{% block details_right %}
<table class="table table-striped table-condensed">
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-detail' part.part.id %}?display=part-suppliers">{{ part.part.full_name }}</a>{% include "clip.html"%}
{% endif %}
</td>
</tr>
{% if part.description %}
<tr>
<td></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Supplier" %}</td>
@ -127,6 +120,13 @@ src="{% static 'img/blank_image.png' %}"
<td>{{ part.note }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
</table>
{% endblock %}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -53,15 +53,17 @@
<span class='fas fa-shopping-cart icon-blue'></span>
</button>
{% elif order.status == PurchaseOrderStatus.PLACED %}
<button type='button' class='btn btn-outline-secondary' id='receive-order' title='{% trans "Receive items" %}'>
<span class='fas fa-sign-in-alt icon-blue'></span>
<button type='button' class='btn btn-primary' id='receive-order' title='{% trans "Receive items" %}'>
<span class='fas fa-sign-in-alt'></span>
{% trans "Receive Items" %}
</button>
<button type='button' class='btn btn-outline-secondary' id='complete-order' title='{% trans "Mark order as complete" %}'>
<span class='fas fa-check-circle icon-green'></span>
<button type='button' class='btn btn-success' id='complete-order' title='{% trans "Mark order as complete" %}'>
<span class='fas fa-check-circle'></span>
{% trans "Complete Order" %}
</button>
{% endif %}
{% endif %}
{% endblock %}
{% endblock actions %}
{% block thumbnail %}
<img class='part-thumb'
@ -75,24 +77,18 @@ src="{% static 'img/blank_image.png' %}"
{% block details %}
<h4>
{% purchase_order_status_label order.status large=True %}
{% if order.is_overdue %}
<span class='badge rounded-pill bg-danger'>{% trans "Overdue" %}</span>
{% endif %}
</h4>
<p>{{ order.description }}{% include "clip.html"%}</p>
{% endblock %}
{% block details_right %}
<table class='table'>
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Order Reference" %}</td>
<td>{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}{{ order.reference }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Order Description" %}</td>
<td>{{ order.description }}{% include "clip.html" %}</td>
</tr>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Order Status" %}</td>
@ -103,6 +99,14 @@ src="{% static 'img/blank_image.png' %}"
{% endif %}
</td>
</tr>
</table>
{% endblock %}
{% block details_right %}
<table class='table table-condensed table-striped'>
<col width='25'>
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Supplier" %}</td>

View File

@ -27,7 +27,7 @@
<span class='fas fa-plus-circle'></span> {% trans "Add Line Item" %}
</button>
{% elif order.status == PurchaseOrderStatus.PLACED %}
<button type='button' class='btn btn-success' id='receive-selected-items' title='{% trans "Receive selected items" %}'>
<button type='button' class='btn btn-primary' id='receive-selected-items' title='{% trans "Receive selected items" %}'>
<span class='fas fa-sign-in-alt'></span> {% trans "Receive Items" %}
</button>
{% endif %}

View File

@ -68,17 +68,33 @@ src="{% static 'img/blank_image.png' %}"
</button>
{% endif %}
{% endif %}
{% endblock %}
{% endblock actions %}
{% block details %}
<h4>
{% sales_order_status_label order.status large=True %}
{% if order.is_overdue %}
<span class='badge rounded-pill bg-danger'>{% trans "Overdue" %}</span>
{% endif %}
</h4>
<p>{{ order.description }}{% include "clip.html"%}</p>
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Order Reference" %}</td>
<td>{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}{{ order.reference }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Order Description" %}</td>
<td>{{ order.description }}{% include "clip.html" %}</td>
</tr>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Order Status" %}</td>
<td>
{% sales_order_status_label order.status %}
{% if order.is_overdue %}
<span class='badge rounded-pill bg-danger'>{% trans "Overdue" %}</span>
{% endif %}
</td>
</tr>
</table>
<div class='info-messages'>
{% if order.status == SalesOrderStatus.PENDING and not order.is_fully_allocated %}
@ -93,21 +109,6 @@ src="{% static 'img/blank_image.png' %}"
{% block details_right %}
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Order Reference" %}</td>
<td>{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}{{ order.reference }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Order Status" %}</td>
<td>
{% sales_order_status_label order.status %}
{% if order.is_overdue %}
<span class='badge rounded-pill bg-danger'>{% trans "Overdue" %}</span>
{% endif %}
</td>
</tr>
{% if order.customer %}
<tr>
<td><span class='fas fa-building'></span></td>

View File

@ -1075,6 +1075,7 @@ class PartList(generics.ListCreateAPIView):
'revision',
'keywords',
'category__name',
'manufacturer_parts__MPN',
]

View File

@ -61,29 +61,43 @@
{% endblock %}
{% block details_left %}
{% if category %}
<p>{{ category.description }}</p>
{% else %}
<p>{% trans "Top level part category" %}</p>
{% endif %}
{% endblock %}
<table class='table table-striped table-condensed'>
<col width='25'>
{% if category %}
{% if category.description %}
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ category.description }}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Category Path" %}</td>
<td>{{ category.pathstring }}</td>
</tr>
{% if category.default_keywords %}
<tr>
<td><span class='fas fa-key'></span></td>
<td>{% trans "Keywords" %}</td>
<td>{{ category.default_keywords }}</td>
</tr>
{% endif %}
{% else %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Category Path" %}</td>
<td><em>{% trans "Top level part category" %}</em></td>
</tr>
{% endif %}
</table>
{% endblock details_left %}
{% block details_right %}
{% if category %}
<table class='table table-condensed table-striped'>
<col width='25'>
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Category Path" %}</td>
<td>{{ category.pathstring }}</td>
</tr>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Category Description" %}</td>
<td>{{ category.description }}</td>
</tr>
{% if category.default_location %}
<tr>
<td><span class='fas fa-map-marker-alt'></span></td>
@ -91,13 +105,6 @@
<td><a href="{% url 'stock-location-detail' category.default_location.pk %}">{{ category.default_location.pathstring }}</a></td>
</tr>
{% endif %}
{% if category.default_keywords %}
<tr>
<td><span class='fas fa-key'></span></td>
<td>{% trans "Keywords" %}</td>
<td>{{ category.default_keywords }}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Subcategories" %}</td>
@ -124,7 +131,7 @@
</tr>
</table>
{% endif %}
{% endblock %}
{% endblock details_right %}
{% block page_content %}

View File

@ -11,113 +11,6 @@
{% block page_content %}
<div class='panel panel-hidden' id='panel-part-details'>
<div class='panel-heading'>
<h4>{% trans "Part Details" %}</h4>
</div>
<div class='panel-content'>
<!-- Details Table -->
<table class="table table-striped table-condensed">
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Name" %}</td>
<td>{{ part.name }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
{% if part.category %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Category" %}</td>
<td>
<a href='{% url "category-detail" part.category.pk %}'>{{ part.category.name }}</a>
</td>
</tr>
{% endif %}
{% if part.IPN %}
<tr>
<td><span class='fas fa-tag'></span></td>
<td>{% trans "IPN" %}</td>
<td>{{ part.IPN }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.revision %}
<tr>
<td><span class='fas fa-code-branch'></span></td>
<td>{% trans "Revision" %}</td>
<td>{{ part.revision }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.units %}
<tr>
<td></td>
<td>{% trans "Units" %}</td>
<td>{{ part.units }}</td>
</tr>
{% endif %}
{% if part.minimum_stock %}
<tr>
<td><span class='fas fa-flag'></span></td>
<td>{% trans "Minimum stock level" %}</td>
<td>{{ part.minimum_stock }}</td>
</tr>
{% endif %}
{% if part.keywords %}
<tr>
<td><span class='fas fa-key'></span></td>
<td>{% trans "Keywords" %}</td>
<td>{{ part.keywords }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Creation Date" %}</td>
<td>
{{ part.creation_date }}
{% if part.creation_user %}
<span class='badge badge-right rounded-pill bg-dark'>{{ part.creation_user }}</span>
{% endif %}
</td>
</tr>
{% if part.trackable and part.getLatestSerialNumber %}
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Latest Serial Number" %}</td>
<td>{{ part.getLatestSerialNumber }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.default_location %}
<tr>
<td><span class='fas fa-search-location'></span></td>
<td>{% trans "Default Location" %}</td>
<td>
<a href='{% url "stock-location-detail" part.default_location.pk %}'>{{ part.default_location }}</a>
</td>
</tr>
{% endif %}
{% if part.default_supplier %}
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Default Supplier" %}</td>
<td>{{ part.default_supplier }}</td>
</tr>
{% endif %}
</table>
</div>
</div>
<div class='panel panel-hidden' id='panel-part-stock'>
<div class='panel-heading'>
<div class='d-flex flex-wrap'>

View File

@ -99,53 +99,71 @@
{% block details %}
</h4>
<!-- Properties -->
<h4>
<div id='part-properties' class='btn-group'>
{% if part.is_template %}
&ensp;
<span class='fas fa-clone' title='{% trans "Part is a template part (variants can be made from this part)" %}'></span>
{% endif %}
{% if part.assembly %}
&ensp;
<span class='fas fa-tools' title='{% trans "Part can be assembled from other parts" %}'></span>
{% endif %}
{% if part.component %}
&ensp;
<span class='fas fa-th' title='{% trans "Part can be used in assemblies" %}'></span>
{% endif %}
{% if part.trackable %}
&ensp;
<span class='fas fa-directions' title='{% trans "Part stock is tracked by serial number" %}'></span>
{% endif %}
{% if part.purchaseable %}
&ensp;
<span class='fas fa-shopping-cart' title='{% trans "Part can be purchased from external suppliers" %}'></span>
{% endif %}
{% if part.salable %}
&ensp;
<span class='fas fa-dollar-sign' title='{% trans "Part can be sold to customers" %}'></span>
{% endif %}
<!-- Part active -->
{% if not part.active %}
&ensp;
<div class='badge rounded-pill bg-danger'>
<span class='fas fa-skull-crossbones' title='{% trans "Part is virtual (not a physical part)" %}'></span>
{% trans 'Inactive' %}
</div>
{% endif %}
<!-- Part virtual -->
{% if part.virtual and part.active %}
&ensp;
<div class='badge rounded-pill bg-warning'>
<span class='fas fa-ghost' title='{% trans "Part is virtual (not a physical part)" %}'></span>
{% trans 'Virtual' %}
</div>
{% endif %}
</div>
</h4>
<table class='table table-striped table-condensed' id='part-info-table'>
<col width='25'>
<tr>
<td colspan='3' style='padding: 3px;'>
<div id='part-properties-wrapper' class='d-flex flex-wrap'>
<div id='part-properties' class='btn-group' role='group';'>
<h5>
{% if part.is_template %}
&ensp;
<span class='fas fa-clone' title='{% trans "Part is a template part (variants can be made from this part)" %}'></span>
{% endif %}
{% if part.assembly %}
&ensp;
<span class='fas fa-tools' title='{% trans "Part can be assembled from other parts" %}'></span>
{% endif %}
{% if part.component %}
&ensp;
<span class='fas fa-th' title='{% trans "Part can be used in assemblies" %}'></span>
{% endif %}
{% if part.trackable %}
&ensp;
<span class='fas fa-directions' title='{% trans "Part stock is tracked by serial number" %}'></span>
{% endif %}
{% if part.purchaseable %}
&ensp;
<span class='fas fa-shopping-cart' title='{% trans "Part can be purchased from external suppliers" %}'></span>
{% endif %}
{% if part.salable %}
&ensp;
<span class='fas fa-dollar-sign' title='{% trans "Part can be sold to customers" %}'></span>
{% endif %}
<!-- Part active -->
{% if not part.active %}
&ensp;
<div class='badge rounded-pill bg-danger'>
<span class='fas fa-skull-crossbones' title='{% trans "Part is virtual (not a physical part)" %}'></span>
{% trans 'Inactive' %}
</div>
{% endif %}
<!-- Part virtual -->
{% if part.virtual and part.active %}
&ensp;
<div class='badge rounded-pill bg-warning'>
<span class='fas fa-ghost' title='{% trans "Part is virtual (not a physical part)" %}'></span>
{% trans 'Virtual' %}
</div>
{% endif %}
</h5>
</div>
{% include "spacer.html" %}
<button type='button' class='btn btn-outline-secondary' data-bs-toggle='collapse' href='#collapse-part-details' role='button' id='toggle-details-button'>
{% trans "Show Part Details" %}
</button>
</div>
</td>
</tr>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
</table>
<!-- Part info messages -->
<div class='info-messages'>
@ -157,7 +175,7 @@
{% endif %}
</div>
{% endblock %}
{% endblock details %}
{% block details_right %}
<table class='table table-condensed table-striped'>
@ -231,7 +249,118 @@
{% endif %}
{% endif %}
</table>
{% endblock %}
{% endblock details_right %}
{% block details_below %}
<!-- Part Details -->
<div class='collapse' id='collapse-part-details'>
<div class='row flex-wrap'>
<div class='col-sm-6'>
<!-- Details Table -->
<table class="table table-striped table-condensed">
<col width='25'>
{% if part.category %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Category" %}</td>
<td>
<a href='{% url "category-detail" part.category.pk %}'>{{ part.category.name }}</a>
</td>
</tr>
{% endif %}
{% if part.IPN %}
<tr>
<td><span class='fas fa-tag'></span></td>
<td>{% trans "IPN" %}</td>
<td>{{ part.IPN }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.revision %}
<tr>
<td><span class='fas fa-code-branch'></span></td>
<td>{% trans "Revision" %}</td>
<td>{{ part.revision }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.units %}
<tr>
<td></td>
<td>{% trans "Units" %}</td>
<td>{{ part.units }}</td>
</tr>
{% endif %}
{% if part.minimum_stock %}
<tr>
<td><span class='fas fa-flag'></span></td>
<td>{% trans "Minimum stock level" %}</td>
<td>{{ part.minimum_stock }}</td>
</tr>
{% endif %}
{% if part.keywords %}
<tr>
<td><span class='fas fa-key'></span></td>
<td>{% trans "Keywords" %}</td>
<td>{{ part.keywords }}{% include "clip.html"%}</td>
</tr>
{% endif %}
</table>
</div>
<div class='col-sm-6'>
<table class="table table-striped table-condensed">
<col width='25'>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Creation Date" %}</td>
<td>
{{ part.creation_date }}
{% if part.creation_user %}
<span class='badge badge-right rounded-pill bg-dark'>{{ part.creation_user }}</span>
{% endif %}
</td>
</tr>
{% if part.trackable and part.getLatestSerialNumber %}
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Latest Serial Number" %}</td>
<td>
{{ part.getLatestSerialNumber }}
<div class='btn-group float-right' role='group'>
<a class='btn btn-small btn-outline-secondary text-sm' href='#' id='serial-number-search' title='{% trans "Search for serial number" %}'>
<span class='fas fa-search'></span>
</a>
</div>
</td>
</tr>
{% endif %}
{% if part.default_location %}
<tr>
<td><span class='fas fa-search-location'></span></td>
<td>{% trans "Default Location" %}</td>
<td>
<a href='{% url "stock-location-detail" part.default_location.pk %}'>{{ part.default_location }}</a>
</td>
</tr>
{% endif %}
{% if part.default_supplier %}
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Default Supplier" %}</td>
<td>{{ part.default_supplier }}</td>
</tr>
{% endif %}
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
</table>
</div>
</div>
</div>
{% endblock details_below %}
{% block js_ready %}
{{ block.super }}
@ -439,4 +568,24 @@
});
{% endif %}
// Callback function when the "part details" panel is shown
$('#collapse-part-details').on('show.bs.collapse', function() {
$('#toggle-details-button').html('{% trans "Hide Part Details" %}');
inventreeSave('show-part-details', true);
});
// Callback function when the "part details" panel is hidden
$('#collapse-part-details').on('hide.bs.collapse', function() {
$('#toggle-details-button').html('{% trans "Show Part Details" %}');
inventreeSave('show-part-details', false);
});
if (inventreeLoad('show-part-details', false).toString() == 'true') {
$('#collapse-part-details').collapse('show');
}
$('#serial-number-search').click(function() {
findStockItemBySerialNumber({{ part.pk }});
});
{% endblock %}

View File

@ -5,8 +5,6 @@
{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %}
{% settings_value 'PART_SHOW_RELATED' as show_related %}
{% trans "Details" as text %}
{% include "sidebar_item.html" with label="part-details" text=text icon="fa-shapes" %}
{% trans "Parameters" as text %}
{% include "sidebar_item.html" with label="part-parameters" text=text icon="fa-th-list" %}
{% if part.is_template %}

View File

@ -439,10 +439,14 @@ class PartDetail(InvenTreeRoleMixin, DetailView):
line['price_part'] = stock_item.supplier_part.unit_pricing
# set date for graph labels
if stock_item.purchase_order:
if stock_item.purchase_order and stock_item.purchase_order.issue_date:
line['date'] = stock_item.purchase_order.issue_date.strftime('%d.%m.%Y')
else:
elif stock_item.tracking_info.count() > 0:
line['date'] = stock_item.tracking_info.first().date.strftime('%d.%m.%Y')
else:
# Not enough information
continue
price_history.append(line)
ctx['price_history'] = price_history

View File

@ -313,7 +313,7 @@ class StockFilter(rest_filters.FilterSet):
# Serial number filtering
serial_gte = rest_filters.NumberFilter(label='Serial number GTE', field_name='serial', lookup_expr='gte')
serial_lte = rest_filters.NumberFilter(label='Serial number LTE', field_name='serial', lookup_expr='lte')
serial = rest_filters.NumberFilter(label='Serial number', field_name='serial', lookup_expr='exact')
serial = rest_filters.CharFilter(label='Serial number', field_name='serial', lookup_expr='exact')
serialized = rest_filters.BooleanFilter(label='Has serial number', method='filter_serialized')
@ -703,6 +703,18 @@ class StockList(generics.ListCreateAPIView):
except (ValueError, StockItem.DoesNotExist):
pass
# Filter by "part tree" - only allow parts within a given variant tree
part_tree = params.get('part_tree', None)
if part_tree is not None:
try:
part = Part.objects.get(pk=part_tree)
if part.tree_id is not None:
queryset = queryset.filter(part__tree_id=part.tree_id)
except:
pass
# Filter by 'allocated' parts?
allocated = params.get('allocated', None)

View File

@ -14,7 +14,7 @@
{% block heading %}
{% trans "Stock Item" %}: {{ item.part.full_name}}
{% endblock %}
{% endblock heading %}
{% block actions %}
@ -100,7 +100,9 @@
<!-- Edit stock item -->
{% if roles.stock.change and not item.is_building %}
<div class='btn-group'>
<button id='stock-edit-actions' title='{% trans "Stock actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'><span class='fas fa-tools'></span> <span class='caret'></span></button>
<button id='stock-edit-actions' title='{% trans "Stock actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
<span class='fas fa-tools'></span> <span class='caret'></span>
</button>
<ul class='dropdown-menu' role='menu'>
{% if item.part.can_convert %}
<li><a class='dropdown-item' href='#' id='stock-convert' title='{% trans "Convert to variant" %}'><span class='fas fa-screwdriver'></span> {% trans "Convert to variant" %}</a></li>
@ -118,38 +120,101 @@
</div>
{% endif %}
{% endif %}
{% endblock %}
{% endblock actions %}
{% block thumbnail %}
<img class='part-thumb' {% if item.part.image %}src="{{ item.part.image.url }}"{% else %}src="{% static 'img/blank_image.png' %}"{% endif %}/>
{% endblock %}
{% endblock thumbnail %}
{% block details %}
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Base Part" %}</td>
<td>
{% if roles.part.view %}
<a href="{% url 'part-detail' item.part.id %}">
{% endif %}
{{ item.part.full_name }}
{% if roles.part.view %}
</a>
{% endif %}
</td>
</tr>
{% if item.serialized %}
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Serial Number" %}</td>
<td>
{{ item.serial }}
<div class='btn-group float-right' role='group'>
{% if previous %}
<a class="btn btn-small btn-outline-secondary" aria-label="{% trans 'previous page' %}" href="{% url request.resolver_match.url_name previous.id %}" title='{% trans "Navigate to previous serial number" %}'>
<span class='fas fa-angle-left'></span>
<small>{{ previous.serial }}</small>
</a>
{% endif %}
<a class='btn btn-small btn-outline-secondary text-sm' href='#' id='serial-number-search' title='{% trans "Search for serial number" %}'>
<span class='fas fa-search'></span>
</a>
{% if next %}
<a class="btn btn-small btn-outline-secondary text-sm" aria-label="{% trans 'next page' %}" href="{% url request.resolver_match.url_name next.id %}" title='{% trans "Navigate to next serial number" %}'>
<small>{{ next.serial }}</small>
<span class='fas fa-angle-right'></span>
</a>
{% endif %}
</div>
</td>
</tr>
{% else %}
<tr>
<td></td>
<td>{% trans "Quantity" %}</td>
<td>{% decimal item.quantity %} {% if item.part.units %}{{ item.part.units }}{% endif %}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>{% stock_status_label item.status %}</td>
</tr>
{% if item.expiry_date %}
<tr>
<td><span class='fas fa-calendar-alt{% if item.is_expired %} icon-red{% endif %}'></span></td>
<td>{% trans "Expiry Date" %}</td>
<td>
{{ item.expiry_date }}
{% if item.is_expired %}
<span title='{% blocktrans %}This StockItem expired on {{ item.expiry_date }}{% endblocktrans %}' class='badge rounded-pill bg-danger badge-right'>{% trans "Expired" %}</span>
{% elif item.is_stale %}
<span title='{% blocktrans %}This StockItem expires on {{ item.expiry_date }}{% endblocktrans %}' class='badge rounded-pill bg-warning badge-right'>{% trans "Stale" %}</span>
{% endif %}
</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Last Updated" %}</td>
<td>{{ item.updated }}</td>
</tr>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Last Stocktake" %}</td>
{% if item.stocktake_date %}
<td>{{ item.stocktake_date }} <span class='badge badge-right rounded-pill bg-dark'>{{ item.stocktake_user }}</span></td>
{% else %}
<td><em>{% trans "No stocktake performed" %}</em></td>
{% endif %}
</tr>
</table>
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
{% if owner_control.value == "True" %}
{% authorized_owners item.owner as owners %}
{% endif %}
<h4>
{% if item.is_expired %}
<span class='badge rounded-pill bg-danger'>{% trans "Expired" %}</span>
{% else %}
{% if roles.stock.change %}
<a href='#' id='stock-edit-status'>
{% endif %}
{% stock_status_label item.status large=True %}
{% if roles.stock.change %}
</a>
{% endif %}
{% if item.is_stale %}
<span class='badge rounded-pill bg-warning'>{% trans "Stale" %}</span>
{% endif %}
{% endif %}
</h4>
<div class='info-messages'>
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
@ -214,49 +279,12 @@
{% endif %}
</div>
{% endblock %}
{% endblock details %}
{% block details_right %}
<table class="table table-striped">
<table class="table table-striped table-condensed">
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Base Part" %}</td>
<td>
{% if roles.part.view %}
<a href="{% url 'part-detail' item.part.id %}">
{% endif %}
{{ item.part.full_name }}
{% if roles.part.view %}
</a>
{% endif %}
</td>
</tr>
{% if item.serialized %}
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "Serial Number" %}</td>
<td>
{% if previous %}
<a class="btn btn-outline-secondary" aria-label="{% trans 'previous page' %}" href="{% url request.resolver_match.url_name previous.id %}">
<small>{{ previous.serial }}</small>
</a>
{% endif %}
{{ item.serial }}
{% if next %}
<a class="btn btn-outline-secondary text-sm" aria-label="{% trans 'next page' %}" href="{% url request.resolver_match.url_name next.id %}">
<small>{{ next.serial }}</small>
</a>
{% endif %}
</td>
</tr>
{% else %}
<tr>
<td></td>
<td>{% trans "Quantity" %}</td>
<td>{% decimal item.quantity %} {% if item.part.units %}{{ item.part.units }}{% endif %}</td>
</tr>
{% endif %}
{% if item.customer %}
<tr>
<td><span class='fas fa-user-tie'></span></td>
@ -376,39 +404,6 @@
<td><a href="{% url 'supplier-part-detail' item.supplier_part.id %}">{{ item.supplier_part.SKU }}</a></td>
</tr>
{% endif %}
{% if item.expiry_date %}
<tr>
<td><span class='fas fa-calendar-alt{% if item.is_expired %} icon-red{% endif %}'></span></td>
<td>{% trans "Expiry Date" %}</td>
<td>
{{ item.expiry_date }}
{% if item.is_expired %}
<span title='{% blocktrans %}This StockItem expired on {{ item.expiry_date }}{% endblocktrans %}' class='badge rounded-pill bg-danger'>{% trans "Expired" %}</span>
{% elif item.is_stale %}
<span title='{% blocktrans %}This StockItem expires on {{ item.expiry_date }}{% endblocktrans %}' class='badge rounded-pill bg-warning'>{% trans "Stale" %}</span>
{% endif %}
</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Last Updated" %}</td>
<td>{{ item.updated }}</td>
</tr>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Last Stocktake" %}</td>
{% if item.stocktake_date %}
<td>{{ item.stocktake_date }} <span class='badge badge-right rounded-pill bg-dark'>{{ item.stocktake_user }}</span></td>
{% else %}
<td><em>{% trans "No stocktake performed" %}</em></td>
{% endif %}
</tr>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>{% stock_status_label item.status %}</td>
</tr>
{% if item.hasRequiredTests %}
<tr>
<td><span class='fas fa-vial'></span></td>
@ -604,4 +599,8 @@ $("#stock-return-from-customer").click(function() {
{% endif %}
$('#serial-number-search').click(function() {
findStockItemBySerialNumber({{ item.part.pk }});
});
{% endblock %}

View File

@ -80,12 +80,32 @@
{% endblock %}
{% block details_left %}
{% if location %}
<p>{{ location.description }}</p>
{% else %}
<p>{% trans "Top level stock location" %}</p>
{% endif %}
<table class='table table-striped table-condensed'>
<col width='25'>
{% if location %}
{% if location.description %}
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ location.description }}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Location Path" %}</td>
<td>{{ location.pathstring }}</td>
</tr>
{% else %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Location Path" %}</td>
<td><em>{% trans "Top level stock location" %}</em></td>
</tr>
{% endif %}
</table>
{% endblock details_left %}
{% block details_below %}
{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %}
{% if owner_control.value == "True" %}
{% authorized_owners location.owner as owners %}
@ -97,17 +117,12 @@
{% endif %}
{% endif %}
{% endblock %}
{% endblock details_below %}
{% block details_right %}
{% if location %}
<table class='table table-striped table-condensed'>
<col width='25'>
<tr>
<td><span class='fas fa-info-circle'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ location.description }}</td>
</tr>
<tr>
<td><span class='fas fa-map-marker-alt'></span></td>
<td>{% trans "Sublocations" %}</td>
@ -134,7 +149,7 @@
</tr>
</table>
{% endif %}
{% endblock %}
{% endblock details_right %}
{% block page_content %}

View File

@ -4,6 +4,7 @@
{% load inventree_extras %}
{% load socialaccount %}
{% load crispy_forms_tags %}
{% load user_sessions i18n %}
{% block label %}account{% endblock %}
@ -14,12 +15,12 @@
{% block actions %}
{% inventree_demo_mode as demo %}
{% if not demo %}
<div class='btn btn-outline-primary' type='button' id='edit-password' title='{% trans "Change Password" %}'>
<span class='fas fa-key'></span> {% trans "Set Password" %}
</div>
<div class='btn btn-primary' type='button' id='edit-user' title='{% trans "Edit User Information" %}'>
<span class='fas fa-user-cog'></span> {% trans "Edit" %}
</div>
<div class='btn btn-primary' type='button' id='edit-password' title='{% trans "Change Password" %}'>
<span class='fas fa-key'></span> {% trans "Set Password" %}
</div>
{% endif %}
{% endblock %}
@ -174,58 +175,48 @@
</div>
<div class='panel-heading'>
<h4>{% trans "Language Settings" %}</h4>
</div>
<div class="row">
<div class="col">
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{% url 'settings' %}">
<label for='language' class=' requiredField'>
{% trans "Select language" %}
</label>
<div class='form-group input-group mb-3'>
<select name="language" class="select form-control w-25">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% if 'alllang' in request.GET %}{% define True as ALL_LANG %}{% endif %}
{% for language in languages %}
{% define language.code as lang_code %}
{% define locale_stats|keyvalue:lang_code as lang_translated %}
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
{% if ALL_LANG or use_lang %}
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
{{ language.name_local }} ({{ lang_code }})
{% if lang_translated %}
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
{% else %}
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
{% endif %}
</option>
{% endif %}
{% endfor %}
</select>
<div class='input-group-append'>
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
</div>
</div>
<p>{% trans "Some languages are not complete" %}
{% if ALL_LANG %}
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
{% else %}
{% trans "and hidden." %} <a href="?alllang">{% trans "Show them too" %}</a>
<div class='d-flex flex-wrap'>
<h4>{% trans "Active Sessions" %}</h4>
{% include "spacer.html" %}
<div class='btn-group' role='group'>
{% if session_list.count > 1 %}
<form method="post" action="{% url 'session_delete_other' %}">
{% csrf_token %}
<button type="submit" class="btn btn-sm btn-default btn-danger" title='{% trans "Log out active sessions (except this one)" %}'>
{% trans "Log Out Active Sessions" %}
</button>
</form>
{% endif %}
</p>
</form>
</div>
<div class="col-sm-6">
<h4>{% trans "Help the translation efforts!" %}</h4>
<p>{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the InvenTree web application is <a href="{{link}}">community contributed via crowdin</a>. Contributions are welcomed and encouraged.{% endblocktrans %}</p>
</div>
</div>
</div>
<div>
{% trans "<em>unknown on unknown</em>" as unknown_on_unknown %}
{% trans "<em>unknown</em>" as unknown %}
<table class="table table-striped table-condensed">
<thead>
<tr>
<th>{% trans "IP Address" %}</th>
<th>{% trans "Device" %}</th>
<th>{% trans "Last Activity" %}</th>
</tr>
</thead>
{% for object in session_list %}
<tr {% if object.session_key == session_key %}class="active"{% endif %}>
<td>{{ object.ip }}</td>
<td>{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }}</td>
<td>
{% if object.session_key == session_key %}
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago (this session){% endblocktrans %}
{% else %}
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %}
{% endif %}
</td>
</tr>
{% endfor %}
</table>
</div>
{% endblock %}
{% block js_ready %}

View File

@ -50,4 +50,57 @@
</div>
</div>
<div class='panel-heading'>
<h4>{% trans "Language Settings" %}</h4>
</div>
<div class="row">
<div class="col">
<form action="{% url 'set_language' %}" method="post">
{% csrf_token %}
<input name="next" type="hidden" value="{% url 'settings' %}">
<label for='language' class=' requiredField'>
{% trans "Select language" %}
</label>
<div class='form-group input-group mb-3'>
<select name="language" class="select form-control w-25">
{% get_current_language as LANGUAGE_CODE %}
{% get_available_languages as LANGUAGES %}
{% get_language_info_list for LANGUAGES as languages %}
{% if 'alllang' in request.GET %}{% define True as ALL_LANG %}{% endif %}
{% for language in languages %}
{% define language.code as lang_code %}
{% define locale_stats|keyvalue:lang_code as lang_translated %}
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
{% if ALL_LANG or use_lang %}
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
{{ language.name_local }} ({{ lang_code }})
{% if lang_translated %}
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
{% else %}
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
{% endif %}
</option>
{% endif %}
{% endfor %}
</select>
<div class='input-group-append'>
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
</div>
</div>
<p>{% trans "Some languages are not complete" %}
{% if ALL_LANG %}
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
{% else %}
{% trans "and hidden." %} <a href="?alllang">{% trans "Show them too" %}</a>
{% endif %}
</p>
</form>
</div>
<div class="col-sm-6">
<h4>{% trans "Help the translation efforts!" %}</h4>
<p>{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the InvenTree web application is <a href="{{link}}">community contributed via crowdin</a>. Contributions are welcomed and encouraged.{% endblocktrans %}</p>
</div>
</div>
{% endblock %}

View File

@ -180,9 +180,9 @@
<script type='text/javascript' src="{% i18n_static 'tables.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'table_filters.js' %}"></script>
<script type='text/javascript' src="{% static 'fontawesome/js/solid.js' %}"></script>
<script type='text/javascript' src="{% static 'fontawesome/js/brands.js' %}"></script>
<script type='text/javascript' src="{% static 'fontawesome/js/fontawesome.js' %}"></script>
<script type='text/javascript' src="{% static 'fontawesome/js/solid.min.js' %}"></script>
<script type='text/javascript' src="{% static 'fontawesome/js/brands.min.js' %}"></script>
<script type='text/javascript' src="{% static 'fontawesome/js/fontawesome.min.js' %}"></script>
{% block js_load %}
{% endblock %}

View File

@ -701,6 +701,11 @@ function loadPurchaseOrderTable(table, options) {
switchable: true,
sortable: false,
formatter: function(value, row) {
if (!row.responsible_detail) {
return '-';
}
var html = row.responsible_detail.name;
if (row.responsible_detail.label == 'group') {

View File

@ -44,6 +44,7 @@
editStockItem,
editStockLocation,
exportStock,
findStockItemBySerialNumber,
loadInstalledInTable,
loadStockLocationTable,
loadStockTable,
@ -394,6 +395,87 @@ function createNewStockItem(options={}) {
constructForm(url, options);
}
/*
* Launch a modal form to find a particular stock item by serial number.
* Arguments:
* - part: ID (PK) of the part in question
*/
function findStockItemBySerialNumber(part_id) {
constructFormBody({}, {
title: '{% trans "Find Serial Number" %}',
fields: {
serial: {
label: '{% trans "Serial Number" %}',
help_text: '{% trans "Enter serial number" %}',
placeholder: '{% trans "Enter serial number" %}',
required: true,
type: 'string',
value: '',
}
},
onSubmit: function(fields, opts) {
var serial = getFormFieldValue('serial', fields['serial'], opts);
serial = serial.toString().trim();
if (!serial) {
handleFormErrors(
{
'serial': [
'{% trans "Enter a serial number" %}',
]
}, fields, opts
);
return;
}
inventreeGet(
'{% url "api-stock-list" %}',
{
part_tree: part_id,
serial: serial,
},
{
success: function(response) {
if (response.length == 0) {
// No results!
handleFormErrors(
{
'serial': [
'{% trans "No matching serial number" %}',
]
}, fields, opts
);
} else if (response.length > 1) {
// Too many results!
handleFormErrors(
{
'serial': [
'{% trans "More than one matching result found" %}',
]
}, fields, opts
);
} else {
$(opts.modal).modal('hide');
// Redirect
var pk = response[0].pk;
location.href = `/stock/item/${pk}/`;
}
},
error: function(xhr) {
showApiError(xhr, opts.url);
$(opts.modal).modal('hide');
}
}
);
}
});
}
/* Stock API functions
* Requires api.js to be loaded first

View File

@ -24,29 +24,35 @@
{% block page_info %}
<div class='panel-content'>
<div class='row'>
<div class='col-sm-6' id='detail-panel-left'>
<div class='card'>
{% block details_left %}
<div class='row g-0'>
<div class='col-md-4'>
{% block thumbnail %}
{% endblock %}
</div>
<div class='col-md-8'>
{% block details %}
{% endblock %}
{% block details_above %}
{% endblock details_above %}
<div class='container' style='max-width: 100%; padding: 5px;'>
<div class='row'>
<div class='col' id='detail-panel-left'>
<div class='card'>
{% block details_left %}
<div class='row'>
<div class='col' style='max-width: 220px;'>
{% block thumbnail %}
{% endblock thumbnail %}
</div>
<div class='col'>
{% block details %}
{% endblock details %}
</div>
</div>
{% endblock details_left %}
</div>
{% endblock %}
</div>
</div>
<div class='col-sm-6' id='detail-panel-right'>
<div class='card'>
{% block details_right %}
block details_right
{% endblock %}
<div class='col' id='detail-panel-right'>
<div class='card'>
{% block details_right %}
block details_right
{% endblock details_right %}
</div>
</div>
{% block details_below %}
{% endblock details_below %}
</div>
</div>
</div>

View File

@ -145,7 +145,6 @@ class RuleSet(models.Model):
# Core django models (not user configurable)
'admin_logentry',
'contenttypes_contenttype',
'sessions_session',
# Models which currently do not require permissions
'common_colortheme',
@ -161,6 +160,7 @@ class RuleSet(models.Model):
'error_report_error',
'exchange_rate',
'exchange_exchangebackend',
'user_sessions_session',
# Django-q
'django_q_ormq',

View File

@ -23,6 +23,7 @@ django-q==1.3.4 # Background task scheduling
django-sql-utils==0.5.0 # Advanced query annotation / aggregation
django-stdimage==5.1.1 # Advanced ImageField management
django-test-migrations==1.1.0 # Unit testing for database migrations
django-user-sessions==1.7.1 # user sessions in DB
django-weasyprint==1.0.1 # django weasyprint integration
djangorestframework==3.12.4 # DRF framework
flake8==3.8.3 # PEP checking

View File

@ -279,7 +279,6 @@ def content_excludes():
excludes = [
"contenttypes",
"sessions.session",
"auth.permission",
"authtoken.token",
"error_report.error",
@ -291,6 +290,7 @@ def content_excludes():
"exchange.rate",
"exchange.exchangebackend",
"common.notificationentry",
"user_sessions.session",
]
output = ""