mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
significant rework of the build allocation tables / views
This commit is contained in:
parent
f0595cc052
commit
16b01ed772
@ -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):
|
||||
"""
|
||||
|
@ -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-danger' type='button' id='btn-unallocate' title='{% trans "Unallocate stock" %}'>
|
||||
<span class='fas fa-minus-circle'></span> {% trans "Unallocate Stock" %}
|
||||
</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 %}
|
||||
</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);
|
||||
function reloadTable() {
|
||||
$('#allocation-table-untracked').bootstrapTable('refresh');
|
||||
}
|
||||
}
|
||||
);
|
||||
{% endfor %}
|
||||
|
||||
{% 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,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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 %}
|
@ -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 %}'>
|
||||
|
@ -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 %}
|
@ -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'),
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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" %}',
|
||||
);
|
||||
}
|
||||
|
||||
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}`));
|
||||
|
||||
if (totalLines > 0) {
|
||||
|
||||
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',
|
||||
|
@ -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 %}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user