Rework build allocation

- Each item renders as a collapsible panel with overview data at the top
This commit is contained in:
Oliver Walters 2019-05-01 07:48:46 +10:00
parent 7e7ac60a1a
commit b6becbc570
6 changed files with 114 additions and 209 deletions

View File

@ -9,6 +9,10 @@
<hr> <hr>
{% for bom_item in bom_items.all %}
{% include "build/allocation_item.html" with item=bom_item build=build %}
{% endfor %}
<table class='table table-striped' id='build-table'> <table class='table table-striped' id='build-table'>
</table> </table>
@ -28,14 +32,18 @@
{% block js_ready %} {% block js_ready %}
{{ block.super }} {{ block.super }}
makeBuildTable($('#build-table'), {% for bom_item in bom_items.all %}
{
build: {{ build.pk }}, loadAllocationTable(
part: {{ build.part.pk }}, $("#allocate-table-id-{{ bom_item.sub_part.id }}"),
new_item_url: "{% url 'build-item-create' %}", "{% url 'api-build-item-list' %}?build={{ build.id }}&part={{ bom_item.sub_part.id }}",
} $("#new-item-{{ bom_item.sub_part.id }}")
); );
{% endfor %}
/*
$("#complete-build").on('click', function() { $("#complete-build").on('click', function() {
launchModalForm( launchModalForm(
"{% url 'build-complete' build.id %}", "{% url 'build-complete' build.id %}",
@ -45,5 +53,6 @@
} }
); );
}); });
*/
{% endblock %} {% endblock %}

View File

@ -0,0 +1,24 @@
<div class='panel-group'>
<div class='panel pane-default'>
<div class='panel panel-heading'>
<div class='row'>
<div class='col-sm-6'>
<div class='panel-title'>
<a data-toggle='collapse' href='#collapse-item-{{ item.id }}'>{{ item.sub_part.name }}</a>
</div>
</div>
<div class='col-sm-6'>
<div class='btn-group' style='float: right;'>
<button class='btn btn-success btn-sm' id='new-item-{{ item.sub_part.id }}' url="{% url 'build-item-create' %}?part={{ item.sub_part.id }}&build={{ build.id }}">Allocate Parts</button>
</div>
</div>
</div>
</div>
<div id='collapse-item-{{ item.id }}' class='panel-collapse collapse'>
<div class='panel-body'>
<table class='table table-striped table-condensed' id='allocate-table-id-{{ item.sub_part.id }}'>
</table>
</div>
</div>
</div>
</div>

View File

@ -111,6 +111,20 @@ class BuildAllocate(DetailView):
context_object_name = 'build' context_object_name = 'build'
template_name = 'build/allocate.html' template_name = 'build/allocate.html'
def get_context_data(self, **kwargs):
""" Provide extra context information for the Build allocation page """
context = super(DetailView, self).get_context_data(**kwargs)
build = self.get_object()
part = build.part
bom_items = part.bom_items
context['part'] = part
context['bom_items'] = bom_items
return context
class BuildCreate(AjaxCreateView): class BuildCreate(AjaxCreateView):
""" View to create a new Build object """ """ View to create a new Build object """

View File

@ -72,7 +72,6 @@ class PartCreate(AjaxCreateView):
def get_category_id(self): def get_category_id(self):
return self.request.GET.get('category', None) return self.request.GET.get('category', None)
# If a category is provided in the URL, pass that to the page context
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
""" Provide extra context information for the form to display: """ Provide extra context information for the form to display:

View File

@ -1,150 +1,21 @@
function makeBuildTable(build_table, options) { function loadAllocationTable(table, url, button) {
/* Construct a table for allocation items to a build.
* Each row contains a sub_part for the BOM.
* Each row can be expended to allocate stock items against that part.
*
* options:
* build - ID of the build object
* part - ID of the part object for the build
* new_item_url - URL to create a new BuildItem
*
*/
build_table.bootstrapTable({
sortable: false,
detailView: true,
showHeader: false,
detailFormatter: function(index, row, element) {
return makeAllocationTable({
part: row.pk
});
},
onExpandRow: function(index, row, $detail) {
fillAllocationTable(
$("#part-table-" + row.pk),
index,
row,
{
build: options.build
},
);
},
columns: [
{
field: 'pk',
title: 'ID',
visible: false,
},
{
field: 'sub_part_detail.name',
title: 'Part',
formatter: function(value, row, index, field) {
return renderLink(value, row.sub_part_detail.url);
}
},
{
field: 'allocated',
title: 'Allocated to Build',
formatter: function(value, row, index, field) {
var html = "Allocated ";
var url = options.new_item_url;
url += "?build=" + options.build + "&part=" + row.sub_part;
if (value) {
html += value;
} else {
html += "0";
}
html += " of ";
html += row.quantity;
html += "<div class='btn-group' style='float: right;'>";
html += "<button class='btn btn-success btn-sm new-item-button' type='button' url='" + url + "'>Allocate Parts</button>";
html += "</div>";
return html;
}
},
],
});
getBomList(
{
part: options.part
}).then(function(response) {
build_table.bootstrapTable('load', response);
});
// Button callbacks
build_table.on('click', '.new-item-button', function() {
var button = $(this);
launchModalForm(button.attr('url'), {
success: function() {
}
});
});
}
function makeAllocationTable(options) {
/* Construct an allocation table for a single row
* in the Build table.
* Each allocation table is a 'detailView' of a parent Part row
*
* Options:
* part: Primary key of the part item
*/
var table = "<table class='table table-striped table-condensed' ";
table += "id ='part-table-" + options.part + "' part-id='" + options.part + "'>";
table += "</table>";
return table;
}
function fillAllocationTable(table, index, parent_row, options) {
/* Load data into an allocation table,
* and update the total stock allocation count in the parent row.
*
* table - the part allocation table
* index - row index in the parent table
* parent_row - parent row data in the build allocation table
* parent_table - the parent build table
*
* options:
* build - pk of the Build object
*/
// Load the allocation table
table.bootstrapTable({ table.bootstrapTable({
formatNoMatches: function() { return 'No parts allocated for ' + parent_row.sub_part_detail.name; }, url: url,
sortable: false,
columns: [ columns: [
{ {
field: 'stock_item_detail', field: 'stock_item_detail',
title: 'Stock Item', title: 'Stock Item',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return '' + value.quantity + ' x ' + value.part_name; return '' + value.quantity + ' x ' + value.part_name + ' @ ' + value.location_name;
}, }
},
{
field: 'stock_item_detail.location_name',
title: 'Location',
},
{
field: 'stock_item_detail.quantity',
title: 'Available',
}, },
{ {
field: 'quantity', field: 'quantity',
title: 'Allocated', title: 'Allocated',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
var html = value; var html = value;
var bEdit = "<button class='btn btn-success item-edit-button btn-sm' type='button' url='/build/item/" + row.pk + "/edit/'>Edit</button>"; var bEdit = "<button class='btn btn-success item-edit-button btn-sm' type='button' url='/build/item/" + row.pk + "/edit/'>Edit</button>";
@ -156,7 +27,15 @@ function fillAllocationTable(table, index, parent_row, options) {
} }
} }
], ],
url: "/api/build/item?build=" + options.build + "&part=" + parent_row.sub_part, });
// Callback for 'new-item' button
button.click(function() {
launchModalForm(button.attr('url'), {
success: function() {
table.bootstrapTable('refresh');
}
});
}); });
// Button callbacks for editing and deleting the allocations // Button callbacks for editing and deleting the allocations
@ -180,24 +59,4 @@ function fillAllocationTable(table, index, parent_row, options) {
}); });
}); });
table.on('load-success.bs.table', function(data) {
var allocated = 0;
var allocationData = table.bootstrapTable('getData');
// Calculate total allocation
for (var i = 0; i < allocationData.length; i++) {
allocated += allocationData[i].quantity;
}
// Update the parent_row data
parent_row.quantity = allocated;
/*parent_table.bootstrapTable('updateRow',
{
index: index,
row: parent_row
}
);*/
});
} }