significant rework of the build allocation tables / views

This commit is contained in:
Oliver Walters 2021-04-20 13:21:23 +10:00
parent f0595cc052
commit 16b01ed772
10 changed files with 176 additions and 148 deletions

View File

@ -314,6 +314,42 @@ class Build(MPTTModel):
'sub_part'
)
@property
def tracked_bom_items(self):
"""
Returns the "trackable" BOM items for this BuildOrder
"""
items = self.bom_items
items = items.filter(sub_part__trackable=True)
return items
def has_tracked_bom_items(self):
"""
Returns True if this BuildOrder has trackable BomItems
"""
return self.tracked_bom_items.count() > 0
@property
def untracked_bom_items(self):
"""
Returns the "non trackable" BOM items for this BuildOrder
"""
items = self.bom_items
items = items.filter(sub_part__trackable=False)
return items
def has_untracked_bom_items(self):
"""
Returns True if this BuildOrder has non trackable BomItems
"""
return self.untracked_bom_items.count() > 0
@property
def remaining(self):
"""

View File

@ -12,83 +12,32 @@
{% endblock %}
{% block heading %}
{% trans "Incomplete Build Ouputs" %}
{% trans "Allocate Stock to Build" %}
{% endblock %}
{% block details %}
{% if build.is_complete %}
<div class='alert alert-block alert-success'>
{% trans "Build order has been completed" %}
</div>
{% else %}
{% if build.has_untracked_bom_items %}
{% if build.active %}
<div class='btn-group' role='group'>
{% if build.active %}
<button class='btn btn-primary' type='button' id='btn-create-output' title='{% trans "Create new build output" %}'>
<span class='fas fa-plus-circle'></span> {% trans "Create New Output" %}
<button class='btn btn-success' type='button' id='btn-auto-allocate' title='{% trans "Allocate stock to build" %}'>
<span class='fas fa-magic'></span> {% trans "Auto Allocate" %}
</button>
<!--
<button class='btn btn-primary' type='button' id='btn-order-parts' title='{% trans "Order required parts" %}'>
<span class='fas fa-shopping-cart'></span> {% trans "Order Parts" %}
</button>
-->
<button class='btn btn-danger' type='button' id='btn-unallocate' title='{% trans "Unallocate stock" %}'>
<span class='fas fa-minus-circle'></span> {% trans "Unallocate Stock" %}
</button>
{% endif %}
</div>
<hr>
<div class="panel panel-default" id='allocation-panel-untracked'>
<div class="panel-heading" role="tab" id="heading-untracked">
<div class="panel-title">
<div class='row'>
<a class='collapsed' aria-expanded='false' role="button" data-toggle="collapse" data-parent="#build-output-accordion" href="#collapse-untracked" aria-controls="collapse-untracked">
<div class='col-sm-6'>
<span class='fas fa-caret-right'></span>
{% trans "Allocated Stock" %}
</div>
</a>
<div class='col-sm-3'>
<div>
<div id='output-progress-untracked'>
<span class='fas fa-spin fa-spinner'></span>
</div>
</div>
</div>
<div class='col-sm-3'>
<div class='btn-group float-right' id='output-actions-untracked'>
<span class='fas fa-spin fa-spinner'></span>
</div>
</div>
</div>
</div>
</div>
<div id="collapse-untracked" class="panel-collapse collapse" role="tabpanel" aria-labelledby="heading-untracked">
<div class="panel-body">
<table class='table table-striped table-condensed' id='allocation-table-untracked'></table>
</div>
</div>
</div>
<hr>
{% if build.incomplete_outputs %}
<div class="panel-group" id="build-output-accordion" role="tablist" aria-multiselectable="true">
{% for item in build.incomplete_outputs %}
{% include "build/allocation_card.html" with item=item %}
{% endfor %}
<!--
<button class='btn btn-primary' type='button' id='btn-order-parts' title='{% trans "Order required parts" %}'>
<span class='fas fa-shopping-cart'></span> {% trans "Order Parts" %}
</button>
-->
</div>
{% endif %}
<table class='table table-striped table-condensed' id='allocation-table-untracked'></table>
{% else %}
<div class='alert alert-block alert-info'>
<b>{% trans "Create a new build output" %}</b><br>
{% trans "No incomplete build outputs remain." %}<br>
{% trans "Create a new build output using the button above" %}
{% trans "This Build Order does not have any associated untracked BOM items" %}
</div>
{% endif %}
{% endif %}
{% endblock %}
{% block js_ready %}
@ -101,22 +50,17 @@
part: {{ build.part.pk }},
};
{% if build.has_untracked_bom_items %}
// Load allocation table for un-tracked parts
loadBuildOutputAllocationTable(buildInfo, null);
{% endif %}
{% for item in build.incomplete_outputs %}
// Get the build output as a javascript object
inventreeGet('{% url 'api-stock-detail' item.pk %}', {},
{
success: function(response) {
loadBuildOutputAllocationTable(buildInfo, response);
}
}
);
{% endfor %}
function reloadTable() {
$('#allocation-table-untracked').bootstrapTable('refresh');
}
{% if build.active %}
$("#btn-allocate").on('click', function() {
$("#btn-auto-allocate").on('click', function() {
launchModalForm(
"{% url 'build-auto-allocate' build.id %}",
{
@ -129,15 +73,7 @@
launchModalForm(
"{% url 'build-unallocate' build.id %}",
{
reload: true,
}
);
});
$('#btn-create-output').click(function() {
launchModalForm('{% url "build-output-create" build.id %}',
{
reload: true,
success: reloadTable,
}
);
});

View File

@ -7,23 +7,31 @@
<div class="panel-heading" role="tab" id="heading-{{ pk }}">
<div class="panel-title">
<div class='row'>
{% if tracked_items %}
<a class='collapsed' aria-expanded='false' role="button" data-toggle="collapse" data-parent="#build-output-accordion" href="#collapse-{{ pk }}" aria-controls="collapse-{{ pk }}">
{% endif %}
<div class='col-sm-4'>
{% if tracked_items %}
<span class='fas fa-caret-right'></span>
{% endif %}
{{ item.part.full_name }}
</div>
<div class='col-sm-2'>
{% if item.serial %}
# {{ item.serial }}
{% trans "Serial Number" %}: {{ item.serial }}
{% else %}
{% decimal item.quantity %}
{% trans "Quantity" %}: {% decimal item.quantity %}
{% endif %}
</div>
{% if tracked_items %}
</a>
{% endif %}
<div class='col-sm-3'>
<div>
<div id='output-progress-{{ pk }}'>
{% if tracked_items %}
<span class='fas fa-spin fa-spinner'></span>
{% endif %}
</div>
</div>
</div>

View File

@ -6,19 +6,68 @@
{% include "build/navbar.html" with tab='output' %}
{% endblock %}
{% block heading %}
{% trans "Build Outputs" %}
{% endblock %}
{% block content_panels %}
{% block details %}
{% if not build.is_complete %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>
{% trans "Incomplete Build Outputs" %}
</h4>
</div>
{% include "stock_table.html" with read_only=True %}
<div class='panel-content'>
<div class='btn-group' role='group'>
{% if build.active %}
<button class='btn btn-primary' type='button' id='btn-create-output' title='{% trans "Create new build output" %}'>
<span class='fas fa-plus-circle'></span> {% trans "Create New Output" %}
</button>
{% endif %}
</div>
{% if build.incomplete_outputs %}
<div class="panel-group" id="build-output-accordion" role="tablist" aria-multiselectable="true">
{% for item in build.incomplete_outputs %}
{% include "build/allocation_card.html" with item=item tracked_items=build.has_tracked_bom_items %}
{% endfor %}
</div>
{% else %}
<div class='alert alert-block alert-info'>
<b>{% trans "Create a new build output" %}</b><br>
{% trans "No incomplete build outputs remain." %}<br>
{% trans "Create a new build output using the button above" %}
</div>
{% endif %}
</div>
</div>
{% endif %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>
{% trans "Completed Build Outputs" %}
</h4>
</div>
<div class='panel-content'>
{% include "stock_table.html" with read_only=True %}
</div>
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#btn-create-output').click(function() {
launchModalForm('{% url "build-output-create" build.id %}',
{
reload: true,
}
);
});
loadStockTable($("#stock-table"), {
params: {
location_detail: true,
@ -32,4 +81,23 @@ loadStockTable($("#stock-table"), {
url: "{% url 'api-stock-list' %}",
});
var buildInfo = {
pk: {{ build.pk }},
quantity: {{ build.quantity }},
completed: {{ build.completed }},
part: {{ build.part.pk }},
};
{% for item in build.incomplete_outputs %}
// Get the build output as a javascript object
inventreeGet('{% url 'api-stock-detail' item.pk %}', {},
{
success: function(response) {
loadBuildOutputAllocationTable(buildInfo, response);
}
}
);
{% endfor %}
{% endblock %}

View File

@ -17,12 +17,6 @@
</li>
{% if build.active %}
<li class='list-group-item {% if tab == "parts" %}active{% endif %}' title='{% trans "Required Parts" %}'>
<a href='{% url "build-parts" build.id %}'>
<span class='fas fa-shapes'></span>
{% trans "Required Parts" %}
</a>
</li>
<li class='list-group-item {% if tab == "allocate" %}active{% endif %}' title='{% trans "In Progress" %}'>
<a href='{% url "build-allocate" build.id %}'>

View File

@ -1,30 +0,0 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% load status_codes %}
{% block menubar %}
{% include "build/navbar.html" with tab='parts' %}
{% endblock %}
{% block heading %}
{% trans "Required Parts" %}
{% endblock %}
{% block details %}
<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

@ -21,7 +21,6 @@ build_detail_urls = [
url(r'^notes/', views.BuildNotes.as_view(), name='build-notes'),
url(r'^children/', views.BuildDetail.as_view(template_name='build/build_children.html'), name='build-children'),
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

@ -620,10 +620,12 @@ class BuildAllocate(InvenTreeRoleMixin, DetailView):
build = self.get_object()
part = build.part
bom_items = part.bom_items
bom_items = build.bom_items
context['part'] = part
context['bom_items'] = bom_items
context['has_tracked_bom_items'] = build.has_tracked_bom_items()
context['has_untracked_bom_items'] = build.has_untracked_bom_items()
context['BuildStatus'] = BuildStatus
context['bom_price'] = build.part.get_price_info(build.quantity, buy=False)

View File

@ -32,7 +32,7 @@ function newBuildOrder(options={}) {
}
function makeBuildOutputActionButtons(output, buildInfo) {
function makeBuildOutputActionButtons(output, buildInfo, lines) {
/* Generate action buttons for a build output.
*/
@ -56,18 +56,20 @@ function makeBuildOutputActionButtons(output, buildInfo) {
var html = `<div class='btn-group float-right' role='group'>`;
// "Auto" allocation only works for untracked stock items
if (!output) {
if (!output && lines > 0) {
html += makeIconButton(
'fa-magic icon-blue', 'button-output-auto', outputId,
'{% trans "Auto-allocate stock items to this output" %}',
);
}
// Add a button to "cancel" the particular build output (unallocate)
html += makeIconButton(
'fa-minus-circle icon-red', 'button-output-unallocate', outputId,
'{% trans "Unallocate stock from build output" %}',
);
if (lines > 0) {
// Add a button to "cancel" the particular build output (unallocate)
html += makeIconButton(
'fa-minus-circle icon-red', 'button-output-unallocate', outputId,
'{% trans "Unallocate stock from build output" %}',
);
}
if (output) {
@ -407,16 +409,21 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
// Update the total progress for this build output
var buildProgress = $(`#allocation-panel-${outputId}`).find($(`#output-progress-${outputId}`));
var progress = makeProgressBar(
allocatedLines,
totalLines
);
if (totalLines > 0) {
buildProgress.html(progress);
var progress = makeProgressBar(
allocatedLines,
totalLines
);
buildProgress.html(progress);
} else {
buildProgress.html('');
}
// Update the available actions for this build output
makeBuildOutputActionButtons(output, buildInfo);
makeBuildOutputActionButtons(output, buildInfo, totalLines);
}
}
);
@ -640,6 +647,9 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
},
]
});
// Initialize the action buttons
makeBuildOutputActionButtons(output, buildInfo, 0);
}
@ -694,7 +704,7 @@ function loadBuildTable(table, options) {
field: 'reference',
title: '{% trans "Build" %}',
sortable: true,
switchable: false,
switchable: true,
formatter: function(value, row, index, field) {
var prefix = "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}";
@ -715,6 +725,7 @@ function loadBuildTable(table, options) {
{
field: 'title',
title: '{% trans "Description" %}',
switchable: true,
},
{
field: 'part',

View File

@ -32,6 +32,7 @@
</div>
</div>
{% block content_panels %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>
@ -41,12 +42,15 @@
</h4>
</div>
{% block details_panel %}
<div class='panel-content'>
{% block details %}
<!-- Particular page detail views go here -->
{% endblock %}
</div>
{% endblock %}
</div>
{% endblock %}
{% endblock %}