mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Client-side grouping
- Cusomizations made to bootstrap-table-group-by.js - Group by part name - Display total stock count per group - Only group if there are more than 1 item in the group - Groups send checkbox signals through appropriately!
This commit is contained in:
parent
64403f824a
commit
4218cf8b45
263
InvenTree/static/script/bootstrap/bootstrap-table-group-by.js
vendored
Normal file
263
InvenTree/static/script/bootstrap/bootstrap-table-group-by.js
vendored
Normal file
@ -0,0 +1,263 @@
|
||||
(function (global, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define([], factory);
|
||||
} else if (typeof exports !== "undefined") {
|
||||
factory();
|
||||
} else {
|
||||
var mod = {
|
||||
exports: {}
|
||||
};
|
||||
factory();
|
||||
global.bootstrapTableGroupBy = mod.exports;
|
||||
}
|
||||
})(this, function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @author: Yura Knoxville
|
||||
* @version: v1.1.0
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var initBodyCaller, tableGroups;
|
||||
|
||||
// it only does '%s', and return '' when arguments are undefined
|
||||
var sprintf = function sprintf(str) {
|
||||
var args = arguments,
|
||||
flag = true,
|
||||
i = 1;
|
||||
|
||||
str = str.replace(/%s/g, function () {
|
||||
var arg = args[i++];
|
||||
|
||||
if (typeof arg === 'undefined') {
|
||||
flag = false;
|
||||
return '';
|
||||
}
|
||||
return arg;
|
||||
});
|
||||
return flag ? str : '';
|
||||
};
|
||||
|
||||
var groupBy = function groupBy(array, f) {
|
||||
var groups = {};
|
||||
array.forEach(function (o) {
|
||||
var group = f(o);
|
||||
groups[group] = groups[group] || [];
|
||||
groups[group].push(o);
|
||||
});
|
||||
|
||||
return groups;
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
groupBy: false,
|
||||
groupByField: '',
|
||||
groupByFormatter: undefined
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initSort = BootstrapTable.prototype.initSort,
|
||||
_initBody = BootstrapTable.prototype.initBody,
|
||||
_updateSelected = BootstrapTable.prototype.updateSelected;
|
||||
|
||||
BootstrapTable.prototype.initSort = function () {
|
||||
_initSort.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
var that = this;
|
||||
tableGroups = [];
|
||||
|
||||
console.log('Sorting...');
|
||||
|
||||
console.log(typeof this.options.groupByField);
|
||||
|
||||
if (this.options.groupBy && this.options.groupByField !== '') {
|
||||
|
||||
if (1 || (this.options.sortName != this.options.groupByField)) {
|
||||
this.data.sort(function (a, b) {
|
||||
console.log('x');
|
||||
return a[that.options.groupByField].localeCompare(b[that.options.groupByField]);
|
||||
});
|
||||
}
|
||||
|
||||
var that = this;
|
||||
var groups = groupBy(that.data, function (item) {
|
||||
return [item[that.options.groupByField]];
|
||||
});
|
||||
|
||||
var index = 0;
|
||||
$.each(groups, function (key, value) {
|
||||
tableGroups.push({
|
||||
id: index,
|
||||
name: key,
|
||||
data: value
|
||||
});
|
||||
|
||||
value.forEach(function (item) {
|
||||
if (!item._data) {
|
||||
item._data = {};
|
||||
}
|
||||
|
||||
item._data['parent-index'] = index;
|
||||
});
|
||||
|
||||
index++;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initBody = function () {
|
||||
initBodyCaller = true;
|
||||
|
||||
_initBody.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (this.options.groupBy && this.options.groupByField !== '') {
|
||||
var that = this,
|
||||
checkBox = false,
|
||||
visibleColumns = 0;
|
||||
|
||||
var cols = [];
|
||||
|
||||
this.columns.forEach(function (column) {
|
||||
if (column.checkbox) {
|
||||
checkBox = true;
|
||||
} else {
|
||||
if (column.visible) {
|
||||
visibleColumns += 1;
|
||||
cols.push(column);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (this.options.detailView && !this.options.cardView) {
|
||||
visibleColumns += 1;
|
||||
}
|
||||
|
||||
tableGroups.forEach(function (item) {
|
||||
var html = [];
|
||||
|
||||
html.push(sprintf('<tr class="info groupBy expanded" data-group-index="%s">', item.id));
|
||||
|
||||
if (that.options.detailView && !that.options.cardView) {
|
||||
html.push('<td class="detail"></td>');
|
||||
}
|
||||
|
||||
if (checkBox) {
|
||||
html.push('<td class="bs-checkbox">', '<input name="btSelectGroup" type="checkbox" />', '</td>');
|
||||
}
|
||||
|
||||
cols.forEach(function(col) {
|
||||
var cell = '<td>';
|
||||
|
||||
if (typeof that.options.groupByFormatter == 'function') {
|
||||
cell += that.options.groupByFormatter(col.title, item.id, item.data);
|
||||
}
|
||||
|
||||
cell += "</td>";
|
||||
|
||||
html.push(cell);
|
||||
});
|
||||
|
||||
/*
|
||||
var formattedValue = item.name;
|
||||
if (typeof that.options.groupByFormatter == "function") {
|
||||
formattedValue = that.options.groupByFormatter(item.name, item.id, item.data);
|
||||
}
|
||||
html.push('<td', sprintf(' colspan="%s"', visibleColumns), '>', formattedValue, '</td>');
|
||||
|
||||
cols.forEach(function(col) {
|
||||
html.push('<td>' + item.data[0][col.field] + '</td>');
|
||||
});
|
||||
*/
|
||||
|
||||
html.push('</tr>');
|
||||
|
||||
if(item.data.length > 1) {
|
||||
that.$body.find('tr[data-parent-index=' + item.id + ']:first').before($(html.join('')));
|
||||
}
|
||||
});
|
||||
|
||||
this.$selectGroup = [];
|
||||
this.$body.find('[name="btSelectGroup"]').each(function () {
|
||||
var self = $(this);
|
||||
|
||||
that.$selectGroup.push({
|
||||
group: self,
|
||||
item: that.$selectItem.filter(function () {
|
||||
return $(this).closest('tr').data('parent-index') === self.closest('tr').data('group-index');
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
this.$container.off('click', '.groupBy').on('click', '.groupBy', function () {
|
||||
$(this).toggleClass('expanded');
|
||||
that.$body.find('tr[data-parent-index=' + $(this).closest('tr').data('group-index') + ']').toggleClass('hidden');
|
||||
});
|
||||
|
||||
this.$container.off('click', '[name="btSelectGroup"]').on('click', '[name="btSelectGroup"]', function (event) {
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
var self = $(this);
|
||||
var checked = self.prop('checked');
|
||||
that[checked ? 'checkGroup' : 'uncheckGroup']($(this).closest('tr').data('group-index'));
|
||||
});
|
||||
}
|
||||
|
||||
initBodyCaller = false;
|
||||
this.updateSelected();
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.updateSelected = function () {
|
||||
if (!initBodyCaller) {
|
||||
_updateSelected.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
if (this.options.groupBy && this.options.groupByField !== '') {
|
||||
this.$selectGroup.forEach(function (item) {
|
||||
var checkGroup = item.item.filter(':enabled').length === item.item.filter(':enabled').filter(':checked').length;
|
||||
|
||||
item.group.prop('checked', checkGroup);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.getGroupSelections = function (index) {
|
||||
var that = this;
|
||||
|
||||
return $.grep(this.data, function (row) {
|
||||
return row[that.header.stateField] && row._data['parent-index'] === index;
|
||||
});
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.checkGroup = function (index) {
|
||||
this.checkGroup_(index, true);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.uncheckGroup = function (index) {
|
||||
this.checkGroup_(index, false);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.checkGroup_ = function (index, checked) {
|
||||
var rows;
|
||||
var filter = function filter() {
|
||||
return $(this).closest('tr').data('parent-index') === index;
|
||||
};
|
||||
|
||||
if (!checked) {
|
||||
rows = this.getGroupSelections(index);
|
||||
}
|
||||
|
||||
this.$selectItem.filter(filter).prop('checked', checked);
|
||||
|
||||
this.updateRows();
|
||||
this.updateSelected();
|
||||
if (checked) {
|
||||
rows = this.getGroupSelections(index);
|
||||
}
|
||||
this.trigger(checked ? 'check-all' : 'uncheck-all', rows);
|
||||
};
|
||||
})(jQuery);
|
||||
});
|
@ -372,14 +372,45 @@ function moveStockItems(items, options) {
|
||||
|
||||
function loadStockTable(table, options) {
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
// Aggregate stock items
|
||||
//params.aggregate = true;
|
||||
|
||||
table.bootstrapTable({
|
||||
sortable: true,
|
||||
search: true,
|
||||
method: 'get',
|
||||
pagination: true,
|
||||
pageSize: 50,
|
||||
pageSize: 25,
|
||||
rememberOrder: true,
|
||||
queryParams: options.params,
|
||||
groupBy: true,
|
||||
groupByField: 'part_name',
|
||||
groupByFields: ['part_name', 'test'],
|
||||
groupByFormatter: function(field, id, data) {
|
||||
|
||||
if (field == 'Part') {
|
||||
return imageHoverIcon(data[0].part_detail.image_url) +
|
||||
data[0].part_detail.full_name +
|
||||
' <i>(' + data.length + ' items)</i>';
|
||||
}
|
||||
else if (field == 'Description') {
|
||||
return data[0].part_detail.description;
|
||||
}
|
||||
else if (field == 'Stock') {
|
||||
var stock = 0;
|
||||
|
||||
data.forEach(function(item) {
|
||||
stock += item.quantity;
|
||||
});
|
||||
|
||||
return stock;
|
||||
}
|
||||
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
checkbox: true,
|
||||
@ -419,7 +450,7 @@ function loadStockTable(table, options) {
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: 'Quantity',
|
||||
title: 'Stock',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
var text = renderLink(value, row.url);
|
||||
@ -433,6 +464,7 @@ function loadStockTable(table, options) {
|
||||
}
|
||||
],
|
||||
url: options.url,
|
||||
queryParams: params,
|
||||
});
|
||||
|
||||
if (options.buttons) {
|
||||
|
@ -183,6 +183,9 @@ class StockItem(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse('stock-item-detail', kwargs={'pk': self.id})
|
||||
|
||||
def get_part_name(self):
|
||||
return self.part.full_name
|
||||
|
||||
class Meta:
|
||||
unique_together = [
|
||||
('part', 'serial'),
|
||||
|
@ -57,6 +57,8 @@ class StockItemSerializer(serializers.ModelSerializer):
|
||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
|
||||
part_name = serializers.CharField(source='get_part_name', read_only=True)
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||
location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
|
||||
|
||||
@ -79,6 +81,7 @@ class StockItemSerializer(serializers.ModelSerializer):
|
||||
'pk',
|
||||
'url',
|
||||
'part',
|
||||
'part_name',
|
||||
'part_detail',
|
||||
'supplier_part',
|
||||
'location',
|
||||
|
@ -89,6 +89,7 @@ InvenTree
|
||||
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-treeview.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table-en-US.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table-group-by.js' %}"></script>
|
||||
|
||||
<script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user