607 lines
22 KiB
HTML
607 lines
22 KiB
HTML
{% extends "part/part_app_base.html" %}
|
|
|
|
{% load static %}
|
|
{% load i18n %}
|
|
{% load inventree_extras %}
|
|
|
|
{% block sidebar %}
|
|
{% include "part/part_sidebar.html" %}
|
|
{% endblock %}
|
|
|
|
{% block thumbnail %}
|
|
{% include "part/part_thumb.html" %}
|
|
{% endblock %}
|
|
|
|
{% block heading %}
|
|
{{ part.full_name }}
|
|
{% endblock %}
|
|
|
|
{% block actions %}
|
|
<!-- Admin View -->
|
|
{% if user.is_staff and roles.part.change %}
|
|
{% url 'admin:part_part_change' part.pk as url %}
|
|
{% include "admin_button.html" with url=url %}
|
|
{% endif %}
|
|
|
|
{% if starred_directly %}
|
|
<button type='button' class='btn btn-outline-secondary' id='toggle-starred' title='{% trans "You are subscribed to notifications for this part" %}'>
|
|
<span id='part-star-icon' class='fas fa-bell icon-green'/>
|
|
</button>
|
|
{% elif starred %}
|
|
<button type='button' class='btn btn-outline-secondary' title='{% trans "You are subscribed to notifications for this part" %}' disabled='true'>
|
|
<span class='fas fa-bell icon-green'></span>
|
|
</button>
|
|
{% else %}
|
|
<button type='button' class='btn btn-outline-secondary' id='toggle-starred' title='{% trans "Subscribe to notifications for this part" %}'>
|
|
<span id='part-star-icon' class='fa fa-bell-slash'/>
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if barcodes %}
|
|
<!-- Barcode actions menu -->
|
|
<div class='btn-group'>
|
|
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button>
|
|
<ul class='dropdown-menu'>
|
|
<li><a class='dropdown-item' href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
|
<li><a class='dropdown-item' href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
{% if part.active %}
|
|
<button type='button' class='btn btn-outline-secondary' id='price-button' title='{% trans "Show pricing information" %}'>
|
|
<span id='part-price-icon' class='fas fa-dollar-sign'/>
|
|
</button>
|
|
{% if roles.stock.change %}
|
|
<div class='btn-group'>
|
|
<button id='stock-actions' title='{% trans "Stock actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
|
<span class='fas fa-boxes'></span> <span class='caret'></span>
|
|
</button>
|
|
<ul class='dropdown-menu'>
|
|
<li>
|
|
<a class='dropdown-item' href='#' id='part-count'>
|
|
<span class='fas fa-check-circle icon-green'></span>
|
|
{% trans "Count part stock" %}
|
|
</a>
|
|
</li>
|
|
<li>
|
|
<a class='dropdown-item' href='#' id='part-move'>
|
|
<span class='fas fa-exchange-alt icon-blue'></span>
|
|
{% trans "Transfer part stock" %}
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
{% if part.purchaseable and roles.purchase_order.add %}
|
|
<button type='button' class='btn btn-outline-secondary' id='part-order' title='{% trans "Order part" %}'>
|
|
<span id='part-order-icon' class='fas fa-shopping-cart'/>
|
|
</button>
|
|
{% endif %}
|
|
{% endif %}
|
|
<!-- Part actions -->
|
|
{% if roles.part.add or roles.part.change or roles.part.delete %}
|
|
<div class='btn-group'>
|
|
<button id='part-actions' title='{% trans "Part actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'> <span class='fas fa-shapes'></span> <span class='caret'></span></button>
|
|
<ul class='dropdown-menu'>
|
|
{% if roles.part.add %}
|
|
<li><a class='dropdown-item' href='#' id='part-duplicate'><span class='fas fa-copy'></span> {% trans "Duplicate part" %}</a></li>
|
|
{% endif %}
|
|
{% if roles.part.change %}
|
|
<li><a class='dropdown-item' href='#' id='part-edit'><span class='fas fa-edit icon-blue'></span> {% trans "Edit part" %}</a></li>
|
|
{% endif %}
|
|
{% if roles.part.delete %}
|
|
<li><a class='dropdown-item' href='#' id='part-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete part" %}</a></li>
|
|
{% endif %}
|
|
</ul>
|
|
</div>
|
|
{% endif %}
|
|
{% endblock %}
|
|
|
|
{% block details %}
|
|
|
|
<!-- Properties -->
|
|
<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 %}
|
|
 
|
|
<span class='fas fa-clone' title='{% trans "Part is a template part (variants can be made from this part)" %}'></span>
|
|
{% endif %}
|
|
{% if part.assembly %}
|
|
 
|
|
<span class='fas fa-tools' title='{% trans "Part can be assembled from other parts" %}'></span>
|
|
{% endif %}
|
|
{% if part.component %}
|
|
 
|
|
<span class='fas fa-th' title='{% trans "Part can be used in assemblies" %}'></span>
|
|
{% endif %}
|
|
{% if part.trackable %}
|
|
 
|
|
<span class='fas fa-directions' title='{% trans "Part stock is tracked by serial number" %}'></span>
|
|
{% endif %}
|
|
{% if part.purchaseable %}
|
|
 
|
|
<span class='fas fa-shopping-cart' title='{% trans "Part can be purchased from external suppliers" %}'></span>
|
|
{% endif %}
|
|
{% if part.salable %}
|
|
 
|
|
<span class='fas fa-dollar-sign' title='{% trans "Part can be sold to customers" %}'></span>
|
|
{% endif %}
|
|
<!-- Part active -->
|
|
{% if not part.active %}
|
|
 
|
|
<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 %}
|
|
 
|
|
<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'>
|
|
{% if part.variant_of %}
|
|
<div class='alert alert-info alert-block' style='padding: 10px;'>
|
|
{% object_link 'part-detail' part.variant_of.id part.variant_of.full_name as link %}
|
|
{% blocktrans %}This part is a variant of {{link}}{% endblocktrans %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
{% endblock details %}
|
|
|
|
{% block details_right %}
|
|
<table class='table table-condensed table-striped'>
|
|
<col width='25'>
|
|
<tr>
|
|
<td><h5><span class='fas fa-boxes'></span></h5></td>
|
|
<td><h5>{% trans "Available Stock" %}</h5></td>
|
|
<td><h5>{% decimal available %}{% if part.units %} {{ part.units }}{% endif %}</h5></td>
|
|
</tr>
|
|
<tr>
|
|
<td><span class='fas fa-map-marker-alt'></span></td>
|
|
<td>{% trans "In Stock" %}</td>
|
|
<td>{% include "part/stock_count.html" %}</td>
|
|
</tr>
|
|
{% if part.minimum_stock %}
|
|
<tr>
|
|
<td><span class='fas fa-flag'></span></td>
|
|
<td>{% trans "Minimum Stock" %}</td>
|
|
<td>{{ part.minimum_stock }}</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% if on_order > 0 %}
|
|
<tr>
|
|
<td><span class='fas fa-shopping-cart'></span></td>
|
|
<td>{% trans "On Order" %}</td>
|
|
<td>{% decimal on_order %}</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% if part.component %}
|
|
{% if required_build_order_quantity > 0 %}
|
|
<tr>
|
|
<td><span class='fas fa-clipboard-list'></span></td>
|
|
<td>{% trans "Required for Build Orders" %}</td>
|
|
<td>{% decimal required_build_order_quantity %}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><span class='fas fa-dolly'></span></td>
|
|
<td>{% trans "Allocated to Build Orders" %}</td>
|
|
<td>
|
|
{% decimal allocated_build_order_quantity %}
|
|
{% if allocated_build_order_quantity < required_build_order_quantity %}
|
|
<span class='fas fa-times-circle icon-red float-right' title='{% trans "Required quantity has not been allocated" %}'></span>
|
|
{% else %}
|
|
<span class='fas fa-check-circle icon-green float-right' title='{% trans "Required quantity has been allocated" %}'></span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% if part.salable %}
|
|
{% if required_sales_order_quantity > 0 %}
|
|
<tr>
|
|
<td><span class='fas fa-clipboard-list'></span></td>
|
|
<td>{% trans "Required for Sales Orders" %}</td>
|
|
<td>
|
|
{% decimal required_sales_order_quantity %}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td><span class='fas fa-dolly'></span></td>
|
|
<td>{% trans "Allocated to Sales Orders" %}</td>
|
|
<td>
|
|
{% decimal allocated_sales_order_quantity %}
|
|
{% if allocated_sales_order_quantity < required_sales_order_quantity %}
|
|
<span class='fas fa-times-circle icon-red float-right' title='{% trans "Required quantity has not been allocated" %}'></span>
|
|
{% else %}
|
|
<span class='fas fa-check-circle icon-green float-right' title='{% trans "Required quantity has been allocated" %}'></span>
|
|
{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% if not part.is_template %}
|
|
{% if part.assembly %}
|
|
<tr>
|
|
<td><span class='fas fa-tools'></span></td>
|
|
<td>{% trans "Can Build" %}</td>
|
|
<td>{% decimal part.can_build %}</td>
|
|
</tr>
|
|
{% if quantity_being_built > 0 %}
|
|
<tr>
|
|
<td><span class='fas fa-tools'></span></td>
|
|
<td>{% trans "Building" %}</td>
|
|
<td>{% decimal quantity_being_built %}</td>
|
|
</tr>
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endif %}
|
|
</table>
|
|
{% 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 }}
|
|
|
|
{% if part.image %}
|
|
$('#part-thumb').click(function() {
|
|
showModalImage('{{ part.image.url }}');
|
|
});
|
|
{% endif %}
|
|
|
|
function reloadImage(data) {
|
|
// If image / thumbnail data present, live update
|
|
if (data.image) {
|
|
$('#part-image').attr('src', data.image);
|
|
|
|
// Reset the "modal image" view
|
|
$('#part-thumb').click(function() {
|
|
showModalImage(data.image);
|
|
});
|
|
|
|
} else {
|
|
// Otherwise, reload the page
|
|
location.reload();
|
|
}
|
|
}
|
|
|
|
enableDragAndDrop(
|
|
'#part-thumb',
|
|
"{% url 'api-part-detail' part.id %}",
|
|
{
|
|
label: 'image',
|
|
method: 'PATCH',
|
|
success: function(data, status, xhr) {
|
|
reloadImage(data);
|
|
}
|
|
}
|
|
);
|
|
|
|
$("#show-qr-code").click(function() {
|
|
launchModalForm(
|
|
"{% url 'part-qr' part.id %}",
|
|
{
|
|
no_post: true,
|
|
}
|
|
);
|
|
});
|
|
|
|
$('#print-label').click(function() {
|
|
printPartLabels([{{ part.pk }}]);
|
|
});
|
|
|
|
function adjustPartStock(action) {
|
|
inventreeGet(
|
|
'{% url "api-stock-list" %}',
|
|
{
|
|
part: {{ part.id }},
|
|
in_stock: true,
|
|
allow_variants: true,
|
|
part_detail: true,
|
|
location_detail: true,
|
|
},
|
|
{
|
|
success: function(items) {
|
|
adjustStock(action, items, {
|
|
success: function() {
|
|
location.reload();
|
|
}
|
|
});
|
|
},
|
|
}
|
|
);
|
|
}
|
|
|
|
$("#part-move").click(function() {
|
|
adjustPartStock('move');
|
|
});
|
|
|
|
$("#part-count").click(function() {
|
|
adjustPartStock('count');
|
|
});
|
|
|
|
$("#price-button").click(function() {
|
|
launchModalForm(
|
|
"{% url 'part-pricing' part.id %}",
|
|
{
|
|
submit_text: '{% trans "Calculate" %}',
|
|
hideErrorMessage: true,
|
|
}
|
|
);
|
|
});
|
|
|
|
$("#toggle-starred").click(function() {
|
|
toggleStar({
|
|
url: '{% url "api-part-detail" part.pk %}',
|
|
button: '#part-star-icon',
|
|
});
|
|
});
|
|
|
|
$("#part-image-upload").click(function() {
|
|
|
|
constructForm(
|
|
'{% url "api-part-detail" part.pk %}',
|
|
{
|
|
method: 'PATCH',
|
|
fields: {
|
|
image: {},
|
|
},
|
|
title: '{% trans "Upload Image" %}',
|
|
onSuccess: function(data) {
|
|
reloadImage(data);
|
|
}
|
|
}
|
|
)
|
|
});
|
|
|
|
|
|
function onSelectImage(response) {
|
|
// Callback when the image-selection modal form is displayed
|
|
// Populate the form with image data (requested via AJAX)
|
|
|
|
$("#modal-form").find("#image-select-table").inventreeTable({
|
|
url: "{% url 'api-part-thumbs' %}",
|
|
showHeader: false,
|
|
showColumns: false,
|
|
clickToSelect: true,
|
|
sidePagination: 'server',
|
|
singleSelect: true,
|
|
formatNoMatches: function() {
|
|
return '{% trans "No matching images found" %}';
|
|
},
|
|
columns: [
|
|
{
|
|
checkbox: true,
|
|
},
|
|
{
|
|
field: 'image',
|
|
title: 'Image',
|
|
searchable: true,
|
|
formatter: function(value, row, index, field) {
|
|
return "<img src='/media/" + value + "' class='grid-image'/>"
|
|
}
|
|
}
|
|
],
|
|
onCheck: function(row, element) {
|
|
|
|
// Update the selected image in the form
|
|
var ipt = $("#modal-form").find("#image-input");
|
|
ipt.val(row.image);
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
{% if roles.part.change %}
|
|
|
|
if (global_settings.INVENTREE_DOWNLOAD_FROM_URL) {
|
|
$("#part-image-url").click(function() {
|
|
launchModalForm(
|
|
'{% url "part-image-download" part.id %}',
|
|
{
|
|
reload: true,
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
$("#part-image-select").click(function() {
|
|
launchModalForm("{% url 'part-image-select' part.id %}",
|
|
{
|
|
reload: true,
|
|
after_render: onSelectImage
|
|
});
|
|
});
|
|
|
|
$("#part-edit").click(function() {
|
|
|
|
editPart({{ part.pk }});
|
|
});
|
|
{% endif %}
|
|
|
|
$("#part-order").click(function() {
|
|
launchModalForm("{% url 'order-parts' %}", {
|
|
data: {
|
|
part: {{ part.id }},
|
|
},
|
|
reload: true,
|
|
});
|
|
});
|
|
|
|
{% if roles.part.add %}
|
|
$("#part-duplicate").click(function() {
|
|
duplicatePart({{ part.pk }});
|
|
});
|
|
{% endif %}
|
|
|
|
{% if roles.part.delete %}
|
|
$("#part-delete").click(function() {
|
|
launchModalForm(
|
|
"{% url 'part-delete' part.id %}",
|
|
{
|
|
redirect: {% if part.category %}"{% url 'category-detail' part.category.id %}"{% else %}"{% url 'part-index' %}"{% endif %},
|
|
no_post: {% if part.active %}true{% else %}false{% endif %},
|
|
}
|
|
);
|
|
});
|
|
{% 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 %} |