Adds "required parts" tab to build view

This commit is contained in:
Oliver Walters 2021-02-18 17:14:57 +11:00
parent 8cb3d6ab0a
commit bfbcbe252b
7 changed files with 189 additions and 16 deletions

View File

@ -1,9 +1,10 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% block details %}
{% load status_codes %}
{% block details %}
{% include "build/tabs.html" with tab='details' %}
<h4>{% trans "Build Details" %}</h4>

View File

@ -0,0 +1,28 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% load status_codes %}
{% block details %}
{% include "build/tabs.html" with tab='parts' %}
<h4>{% trans "Build Parts" %}</h4>
<hr>
<table class='table table-striped table-condensed' id='parts-table'></table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadBuildPartsTable($('#parts-table'), {
part: {{ build.part.pk }},
build: {{ build.pk }},
build_quantity: {{ build.quantity }},
build_remaining: {{ build.remaining }},
});
{% endblock %}

View File

@ -5,16 +5,22 @@
<a href="{% url 'build-detail' build.id %}">{% trans "Details" %}</a>
</li>
{% if build.active %}
<li {% if tab == 'parts' %} class='active'{% endif %}>
<a href='{% url "build-parts" build.id %}'>
{% trans "Required Parts" %}
<span class='badge'>{{ build.part.bom_count }}</span>
</a>
</li>
<li{% if tab == 'allocate' %} class='active'{% endif %}>
<a href="{% url 'build-allocate' build.id %}">
{% trans "Incomplete" %}
{% trans "In Progress" %}
<span class='badge'>{{ build.incomplete_outputs.count }}</span>
</a>
</li>
{% endif %}
<li{% if tab == 'output' %} class='active'{% endif %}>
<a href="{% url 'build-output' build.id %}">
{% trans "Build Outputs" %}
{% trans "Completed Outputs" %}
<span class='badge'>{{ build.output_count }}</span>
</a>
</li>

View File

@ -20,6 +20,7 @@ build_detail_urls = [
url(r'^notes/', views.BuildNotes.as_view(), name='build-notes'),
url(r'^parts/', views.BuildDetail.as_view(template_name='build/parts.html'), name='build-parts'),
url(r'^attachments/', views.BuildDetail.as_view(template_name='build/attachments.html'), name='build-attachments'),
url(r'^output/', views.BuildDetail.as_view(template_name='build/build_output.html'), name='build-output'),

View File

@ -77,19 +77,6 @@
{% endblock %}
{% block js_load %}
{{ block.super }}
<!-- jquery-treegrid -->
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.js" %}'></script>
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.bootstrap3.js" %}'></script>
<!-- boostrap-table-treegrid -->
<script type='text/javascript' src='{% static "bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js" %}'></script>
{% endblock %}
{% block js_ready %}
{{ block.super }}

View File

@ -107,6 +107,13 @@ InvenTree
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table-filter-control.js' %}"></script>
<!-- <script type='text/javascript' src="{% static 'script/bootstrap/filter-control-utils.js' %}"></script> -->
<!-- jquery-treegrid -->
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.js" %}'></script>
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.bootstrap3.js" %}'></script>
<!-- boostrap-table-treegrid -->
<script type='text/javascript' src='{% static "bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js" %}'></script>
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
<script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script>
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>

View File

@ -833,4 +833,147 @@ function loadAllocationTable(table, part_id, part, url, required, button) {
});
});
}
function loadBuildPartsTable(table, options={}) {
/**
* Display a "required parts" table for build view.
*
* This is a simplified BOM view:
* - Does not display sub-bom items
* - Does not allow editing of BOM items
*
* Options:
*
* part: Part ID
* build: Build ID
* build_quantity: Total build quantity
* build_remaining: Number of items remaining
*/
// Query params
var params = {
sub_part_detail: true,
part: options.part,
};
var filters = {};
if (!options.disableFilters) {
filters = loadTableFilters('bom');
}
setupFilterList('bom', $(table));
for (var key in params) {
filters[key] = params[key];
}
var columns = [
{
field: 'sub_part',
title: '{% trans "Part" %}',
switchable: false,
sortable: true,
formatter: function(value, row, index, field) {
var url = `/part/${row.sub_part}/`;
var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url);
var sub_part = row.sub_part_detail;
html += makePartIcons(row.sub_part_detail);
// Display an extra icon if this part is an assembly
if (sub_part.assembly) {
var text = `<span title='{% trans "Open subassembly" %}' class='fas fa-stream label-right'></span>`;
html += renderLink(text, `/part/${row.sub_part}/bom/`);
}
return html;
}
},
{
field: 'sub_part_detail.description',
title: '{% trans "Description" %}',
},
{
field: 'reference',
title: '{% trans "Reference" %}',
searchable: true,
sortable: true,
},
{
field: 'quantity',
title: '{% trans "Quantity" %}',
sortable: true
},
{
sortable: true,
switchable: false,
field: 'sub_part_detail.stock',
title: '{% trans "Available" %}',
formatter: function(value, row, index, field) {
return makeProgressBar(
value,
row.quantity * options.build_remaining,
{
id: `part-progress-${row.part}`
}
);
},
sorter: function(valA, valB, rowA, rowB) {
if (rowA.received == 0 && rowB.received == 0) {
return (rowA.quantity > rowB.quantity) ? 1 : -1;
}
var progressA = parseFloat(rowA.sub_part_detail.stock) / (rowA.quantity * options.build_remaining);
var progressB = parseFloat(rowB.sub_part_detail.stock) / (rowB.quantity * options.build_remaining);
return (progressA < progressB) ? 1 : -1;
}
},
{
field: 'actions',
title: '{% trans "Actions" %}',
switchable: false,
formatter: function(value, row, index, field) {
// TODO - Add actions to build / order stock
}
}
];
table.inventreeTable({
url: '{% url "api-bom-list" %}',
showColumns: true,
name: 'build-parts',
sortable: true,
search: true,
rowStyle: function(row, index) {
var classes = [];
// Shade rows differently if they are for different parent parts
if (row.part != options.part) {
classes.push('rowinherited');
}
if (row.validated) {
classes.push('rowvalid');
} else {
classes.push('rowinvalid');
}
return {
classes: classes.join(' '),
};
},
formatNoMatches: function() {
return '{% trans "No BOM items found" %}';
},
clickToSelect: true,
queryParams: filters,
original: params,
columns: columns,
});
}