Merge pull request #375 from SchrodingersGat/group-stock-items

Group stock items
This commit is contained in:
Oliver authored and GitHub committed 2019-05-29 01:05:57 +10:00
commit da4f68e5a5
14 files changed
+4586 -44

No files matched your search

-1
View File
@@ -104,7 +104,6 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'InvenTree.middleware.AuthRequiredMiddleware', 'InvenTree.middleware.AuthRequiredMiddleware',
'InvenTree.middleware.QueryCountMiddleware',
] ]
if DEBUG: if DEBUG:
+1
View File
@@ -46,6 +46,7 @@
location_detail: true, location_detail: true,
part_detail: true, part_detail: true,
}, },
groupByField: 'location',
buttons: [ buttons: [
'#stock-options', '#stock-options',
], ],
+11
View File
@@ -0,0 +1,11 @@
.bootstrap-table .table > tbody > tr.groupBy {
cursor: pointer;
}
.bootstrap-table .table > tbody > tr.groupBy.expanded {
}
.bootstrap-table .table > tbody > tr.hidden + tr.detail-view {
display: none;
}
+282 -1
View File
@@ -1 +1,282 @@
.fixed-table-container .bs-checkbox,.fixed-table-container .no-records-found{text-align:center}.fixed-table-body thead th .th-inner,.table td,.table th{box-sizing:border-box}.bootstrap-table .table{margin-bottom:0!important;border-bottom:1px solid #ddd;border-collapse:collapse!important;border-radius:1px}.bootstrap-table .table:not(.table-condensed),.bootstrap-table .table:not(.table-condensed)>tbody>tr>td,.bootstrap-table .table:not(.table-condensed)>tbody>tr>th,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>td,.bootstrap-table .table:not(.table-condensed)>tfoot>tr>th,.bootstrap-table .table:not(.table-condensed)>thead>tr>td{padding:8px}.bootstrap-table .table.table-no-bordered>tbody>tr>td,.bootstrap-table .table.table-no-bordered>thead>tr>th{border-right:2px solid transparent}.bootstrap-table .table.table-no-bordered>tbody>tr>td:last-child{border-right:none}.fixed-table-container{position:relative;clear:both;border:1px solid #ddd;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px}.fixed-table-container.table-no-bordered{border:1px solid transparent}.fixed-table-footer,.fixed-table-header{overflow:hidden}.fixed-table-footer{border-top:1px solid #ddd}.fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.fixed-table-container table{width:100%}.fixed-table-container thead th{height:0;padding:0;margin:0;border-left:1px solid #ddd}.fixed-table-container thead th:focus{outline:transparent solid 0}.fixed-table-container thead th:first-child:not([data-not-first-th]){border-left:none;border-top-left-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px}.fixed-table-container tbody td .th-inner,.fixed-table-container thead th .th-inner{padding:8px;line-height:24px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fixed-table-container thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px}.fixed-table-container thead th .both{background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC')}.fixed-table-container thead th .asc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==)}.fixed-table-container thead th .desc{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII=)}.fixed-table-container th.detail{width:30px}.fixed-table-container tbody td{border-left:1px solid #ddd}.fixed-table-container tbody tr:first-child td{border-top:none}.fixed-table-container tbody td:first-child{border-left:none}.fixed-table-container tbody .selected td{background-color:#f5f5f5}.fixed-table-container input[type=radio],.fixed-table-container input[type=checkbox]{margin:0 auto!important}.fixed-table-pagination .pagination-detail,.fixed-table-pagination div.pagination{margin-top:10px;margin-bottom:10px}.fixed-table-pagination div.pagination .pagination{margin:0}.fixed-table-pagination .pagination a{padding:6px 12px;line-height:1.428571429}.fixed-table-pagination .pagination-info{line-height:34px;margin-right:5px}.fixed-table-pagination .btn-group{position:relative;display:inline-block;vertical-align:middle}.fixed-table-pagination .dropup .dropdown-menu{margin-bottom:0}.fixed-table-pagination .page-list{display:inline-block}.fixed-table-toolbar .columns-left{margin-right:5px}.fixed-table-toolbar .columns-right{margin-left:5px}.fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.fixed-table-toolbar .bs-bars,.fixed-table-toolbar .columns,.fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px;line-height:34px}.fixed-table-pagination li.disabled a{pointer-events:none;cursor:default}.fixed-table-loading{display:none;position:absolute;top:42px;right:0;bottom:0;left:0;z-index:99;background-color:#fff;text-align:center}.fixed-table-body .card-view .title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.table td,.table th{vertical-align:middle}.fixed-table-toolbar .dropdown-menu{text-align:left;max-height:300px;overflow:auto}.fixed-table-toolbar .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.fixed-table-toolbar .btn-group>.btn-group>.btn{border-radius:0}.fixed-table-toolbar .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.fixed-table-toolbar .btn-group>.btn-group:last-child>.bLine truncated @charset "UTF-8";
/**
* @author zhixin wen <wenzhixin2010@gmail.com>
* version: 1.14.2
* https://github.com/wenzhixin/bootstrap-table/
*/
.bootstrap-table .fixed-table-toolbar:after {
content: "";
display: block;
clear: both;
}
.bootstrap-table .fixed-table-toolbar .bs-bars,
.bootstrap-table .fixed-table-toolbar .search,
.bootstrap-table .fixed-table-toolbar .columns {
position: relative;
margin-top: 10px;
margin-bottom: 10px;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group {
display: inline-block;
margin-left: -1px !important;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:first-child > .btn {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:last-child > .btn {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group > .btn {
border-radius: 0;
}
.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu {
text-align: left;
max-height: 300px;
overflow: auto;
}
.bootstrap-table .fixed-table-toolbar .columns label {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
line-height: 1.428571429;
}
.bootstrap-table .fixed-table-toolbar .columns-left {
margin-right: 5px;
}
.bootstrap-table .fixed-table-toolbar .columns-right {
margin-left: 5px;
}
.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu {
right: 0;
left: auto;
}
.bootstrap-table .fixed-table-container {
position: relative;
clear: both;
}
.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer) {
border-bottom: 1px solid #dee2e6;
}
.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border {
border-left: 1px solid #dee2e6;
border-right: 1px solid #dee2e6;
}
.bootstrap-table .fixed-table-container.fixed-height .table thead th {
border-bottom: 1px solid #dee2e6;
}
.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th {
border-bottom: 1px solid #32383e;
}
.bootstrap-table .fixed-table-container .fixed-table-header {
overflow: hidden;
}
.bootstrap-table .fixed-table-container .fixed-table-body {
overflow-x: auto;
overflow-y: auto;
height: 100%;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
align-items: center;
background: #fff;
display: none;
justify-content: center;
position: absolute;
bottom: 0;
width: 100%;
z-index: 1000;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap {
align-items: baseline;
display: flex;
justify-content: center;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text {
font-size: 2rem;
margin-right: 6px;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap {
align-items: center;
display: flex;
justify-content: center;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap:after,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap:before {
content: "";
animation-duration: 1.5s;
animation-iteration-count: infinite;
animation-name: LOADING;
background: #212529;
border-radius: 50%;
display: block;
height: 5px;
margin: 0 4px;
opacity: 0;
width: 5px;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot {
animation-delay: 0.3s;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap:after {
animation-delay: 0.6s;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark {
background: #212529;
}
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap:after,
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap:before {
background: #fff;
}
.bootstrap-table .fixed-table-container .table {
width: 100%;
margin-bottom: 0 !important;
}
.bootstrap-table .fixed-table-container .table th,
.bootstrap-table .fixed-table-container .table td {
vertical-align: middle;
box-sizing: border-box;
}
.bootstrap-table .fixed-table-container .table thead th {
vertical-align: bottom;
padding: 0;
margin: 0;
}
.bootstrap-table .fixed-table-container .table thead th:focus {
outline: 0 solid transparent;
}
.bootstrap-table .fixed-table-container .table thead th.detail {
width: 30px;
}
.bootstrap-table .fixed-table-container .table thead th .th-inner {
padding: 0.75rem;
vertical-align: bottom;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.bootstrap-table .fixed-table-container .table thead th .sortable {
cursor: pointer;
background-position: right;
background-repeat: no-repeat;
padding-right: 30px;
}
.bootstrap-table .fixed-table-container .table thead th .both {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC");
}
.bootstrap-table .fixed-table-container .table thead th .asc {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==");
}
.bootstrap-table .fixed-table-container .table thead th .desc {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ");
}
.bootstrap-table .fixed-table-container .table tbody tr.selected td {
background-color: rgba(0, 0, 0, 0.075);
}
.bootstrap-table .fixed-table-container .table tbody tr.no-records-found {
text-align: center;
}
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title {
font-weight: bold;
display: inline-block;
min-width: 30%;
text-align: left !important;
}
.bootstrap-table .fixed-table-container .table .bs-checkbox {
text-align: center;
}
.bootstrap-table .fixed-table-container .table input[type=radio],
.bootstrap-table .fixed-table-container .table input[type=checkbox] {
margin: 0 auto !important;
}
.bootstrap-table .fixed-table-container .table.table-sm .th-inner {
padding: 0.3rem;
}
.bootstrap-table .fixed-table-container .fixed-table-footer {
overflow: hidden;
}
.bootstrap-table .fixed-table-pagination:after {
content: "";
display: block;
clear: both;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail,
.bootstrap-table .fixed-table-pagination > .pagination {
margin-top: 10px;
margin-bottom: 10px;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .pagination-info {
line-height: 34px;
margin-right: 5px;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list {
display: inline-block;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group {
position: relative;
display: inline-block;
vertical-align: middle;
}
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group .dropdown-menu {
margin-bottom: 0;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination {
margin: 0;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination a {
padding: 6px 12px;
line-height: 1.428571429;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a {
color: #c8c8c8;
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a:before {
content: "⬅";
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a:after {
content: "➡";
}
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.disabled a {
pointer-events: none;
cursor: default;
}
.bootstrap-table.fullscreen {
position: fixed;
top: 0;
left: 0;
z-index: 1050;
width: 100% !important;
background: #FFF;
}
/* calculate scrollbar width */
div.fixed-table-scroll-inner {
width: 100%;
height: 200px;
}
div.fixed-table-scroll-outer {
top: 0;
left: 0;
visibility: hidden;
width: 200px;
height: 150px;
overflow: hidden;
}
@keyframes LOADING {
0% {
opacity: 0;
}
50% {
opacity: 1;
}
to {
opacity: 0;
}
}
/*# sourceMappingURL=bootstrap-table.css.map */
+6
View File
@@ -43,6 +43,12 @@
font-size: 100%; font-size: 100%;
} }
/* Bootstrap table overrides */
.stock-sub-group td {
background-color: #ebf4f4;
}
/* Force select2 elements in modal forms to be full width */ /* Force select2 elements in modal forms to be full width */
.select-full-width { .select-full-width {
width: 100%; width: 100%;
@@ -0,0 +1,269 @@
(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;
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
BootstrapTable.prototype.initSort = function () {
_initSort.apply(this, Array.prototype.slice.apply(arguments));
var that = this;
tableGroups = [];
/* Sort the items into groups */
if (this.options.groupBy && this.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 = {};
}
if (value.length > 1) {
item._data['parent-index'] = index;
} else {
item._data['parent-index'] = null;
}
item._data['group-data'] = value;
item._data['table'] = that;
});
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 += '<i>' + that.options.groupByFormatter(col.field, item.id, item.data) + "</i>";
}
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 + ']').addClass('hidden stock-sub-group');
// Insert the group header row before the first item
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);
});
File diff suppressed because it is too large. Load diff
@@ -1,9 +0,0 @@
/*
* bootstrap-table - v1.12.1 - 2018-03-12
* https://github.com/wenzhixin/bootstrap-table
* Copyright (c) 2018 zhixin wen
* Licensed MIT License
*/
!function(a){"use strict";var b=3;try{b=parseInt(a.fn.dropdown.Constructor.VERSION,10)}catch(c){}var d={3:{buttonsClass:"default",iconsPrefix:"glyphicon",icons:{paginationSwitchDown:"glyphicon-collapse-down icon-chevron-down",paginationSwitchUp:"glyphicon-collapse-up icon-chevron-up",refresh:"glyphicon-refresh icon-refresh",toggleOff:"glyphicon-list-alt icon-list-alt",toggleOn:"glyphicon-list-alt icon-list-alt",columns:"glyphicon-th icon-th",detailOpen:"glyphicon-plus icon-plus",detailClose:"glyphicon-minus icon-minus",fullscreen:"glyphicon-fullscreen"},pullClass:"pull",toobarDropdowHtml:['<ul class="dropdown-menu" role="menu">',"</ul>"],toobarDropdowItemHtml:'<li role="menuitem"><label>%s</label></li>',pageDropdownHtml:['<ul class="dropdown-menu" role="menu">',"</ul>"],pageDropdownItemHtml:'<li role="menuitem" class="%s"><a href="#">%s</a></li>'},4:{buttonsClass:"secondary",iconsPrefix:"fa",icons:{paginationSwitchDown:"fa-toggle-down",paginationSwitchUp:"fa-toggle-up",refresh:"fa-refresh",toggleOff:"fa-toggle-off",toggleOn:"fa-toggle-on",columns:"fa-th-list",detailOpen:"fa-plus",detailClose:"fa-minus",fullscreen:"fa-arrows-alt"},pullClass:"float",toobarDropdowHtml:['<div class="dropdown-menu dropdown-menu-right">',"</div>"],toobarDropdowItemHtml:'<label class="dropdown-item">%s</label>',pageDropdownHtml:['<div class="dropdown-menu">',"</div>"],pageDropdownItemHtml:'<a class="dropdown-item %s" href="#">%s</a>'}}[b],e=null,f=function(a){var b=arguments,c=!0,d=1;return a=a.replace(/%s/g,function(){var a=b[d++];return"undefined"==typeof a?(c=!1,""):a}),c?a:""},g=function(b,c,d,e){var f="";return a.each(b,function(a,b){return b[c]===e?(f=b[d],!1):!0}),f},h=function(b){var c,d,e,f=0,g=[];for(c=0;c<b[0].length;c++)f+=b[0][c].colspan||1;for(c=0;c<b.length;c++)for(g[c]=[],d=0;f>d;d++)g[c][d]=!1;for(c=0;c<b.length;c++)for(d=0;d<b[c].length;d++){var h=b[c][d],i=h.rowspan||1,j=h.colspan||1,k=a.inArray(!1,g[c]);for(1===j&&(h.fieldIndex=k,"undefined"==typeof h.field&&(h.field=k)),e=0;i>e;e++)g[c+e][k]=!0;for(e=0;j>e;e++)g[c][k+e]=!0}},i=function(){if(null===e){var b,c,d=a("<p/>").addClass("fixed-table-scroll-inner"),f=a("<div/>").addClass("fixed-table-scroll-outer");f.append(d),a("body").append(f),b=d[0].offsetWidth,f.css("overflow","scroll"),c=d[0].offsetWidth,b===c&&(c=f[0].clientWidth),f.remove(),e=b-c}return e},j=function(b,c,d,e){var g=c;if("string"==typeof c){var h=c.split(".");h.length>1?(g=window,a.each(h,function(a,b){g=g[b]})):g=window[c]}return"object"==typeof g?g:"function"==typeof g?g.apply(b,d||[]):!g&&"string"==typeof c&&f.apply(this,[c].concat(d))?f.apply(this,[c].concat(d)):e},k=function(b,c,d){var e=Object.getOwnPropertyNames||function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b},f=e(b),g=e(c),h="";if(d&&f.length!==g.length)return!1;for(var i=0;i<f.length;i++)if(h=f[i],a.inArray(h,g)>-1&&b[h]!==c[h])return!1;return!0},l=function(a){return"string"==typeof a?a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;").replace(/`/g,"&#x60;"):a},m=function(a){for(var b in a){var c=b.split(/(?=[A-Z])/).join("-").toLowerCase();c!==b&&(a[c]=a[b],delete a[b])}return a},n=function(a,b,c){var d=a;if("string"!=typeof b||a.hasOwnProperty(b))return c?l(a[b]):a[b];var e=b.split(".");for(var f in e)e.hasOwnProperty(f)&&(d=d&&d[e[f]]);return c?l(d):d},o=function(){return!!(navigator.userAgent.indexOf("MSIE ")>0||navigator.userAgent.match(/Trident.*rv\:11\./))},p=function(){Object.keys||(Object.keys=function(){var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;return function(e){if("object"!=typeof e&&("function"!=typeof e||null===e))throw new TypeError("Object.keys called on non-object");var f,g,h=[];for(f in e)a.call(e,f)&&h.push(f);if(b)for(g=0;d>g;g++)a.call(e,c[g])&&h.push(c[g]);return h}}())},q=function(b,c){this.options=c,this.$el=a(b),this.$el_=this.$el.clone(),this.timeoutId_=0,this.timeoutFooter_=0,this.init()};q.DEFAULTS={classes:"table table-hover",sortClass:void 0,locale:void 0,height:void 0,undefinedText:"-",sortName:void 0,sortOrder:"asc",sortStable:!1,rememberOrder:!1,striped:!1,columns:[[]],data:[],totalField:"total",dataField:"rows",method:"get",url:void 0,ajax:void 0,cache:!0,contentType:"application/json",dataType:"json",ajaxOptions:{},queryParams:function(a){return a},queryParamsType:"limit",responseHandler:function(a){return a},pagination:!1,onlyInfoPagination:!1,paginationLoop:!0,sidePagination:"client",totalRows:0,pageNumber:1,pageSize:10,pageList:[10,25,50,100],paginationHAlign:"right",paginationVAlign:"bottom",paginationDetailHAlign:"left",paginationPreText:"&lsaquo;",paginationNextText:"&rsaquo;",search:!1,searchOnEnterKey:!1,strictSearch:!1,searchAlign:"right",selectItemName:"btSelectItem",showHeader:!0,showFooter:!1,showColumns:!1,showPaginationSwitch:!1,shoLine truncated
if((!e.fromHtml||"undefined"!=typeof p||z.checkbox||z.radio)&&z.visible&&(!e.options.cardView||z.cardVisible)){if(z.escape&&(p=l(p)),i=f('style="%s"',k.concat(e.header.styles[d]).join("; ")),b["_"+m+"_id"]&&(t=f(' id="%s"',b["_"+m+"_id"])),b["_"+m+"_class"]&&(u=f(' class="%s"',b["_"+m+"_class"])),b["_"+m+"_rowspan"]&&(w=f(' rowspan="%s"',b["_"+m+"_rowspan"])),b["_"+m+"_colspan"]&&(x=f(' colspan="%s"',b["_"+m+"_colspan"])),b["_"+m+"_title"]&&(y=f(' title="%s"',b["_"+m+"_title"])),s=j(e.header,e.header.cellStyles[d],[p,b,c,m],s),s.classes&&(u=f(' class="%s"',s.classes)),s.css){var A=[];for(var B in s.css)A.push(B+": "+s.css[B]);i=f('style="%s"',A.concat(e.header.styles[d]).join("; "))}q=j(z,e.header.formatters[d],[p,b,c,m],p),b["_"+m+"_data"]&&!a.isEmptyObject(b["_"+m+"_data"])&&a.each(b["_"+m+"_data"],function(a,b){"index"!==a&&(v+=f(' data-%s="%s"',a,b))}),z.checkbox||z.radio?(r=z.checkbox?"checkbox":r,r=z.radio?"radio":r,o=[f(e.options.cardView?'<div class="card-view %s">':'<td class="bs-checkbox %s">',z["class"]||""),"<input"+f(' data-index="%s"',c)+f(' name="%s"',e.options.selectItemName)+f(' type="%s"',r)+f(' value="%s"',b[e.options.idField])+f(' checked="%s"',q===!0||p||q&&q.checked?"checked":void 0)+f(' disabled="%s"',!z.checkboxEnabled||q&&q.disabled?"disabled":void 0)+" />",e.header.formatters[d]&&"string"==typeof q?q:"",e.options.cardView?"</div>":"</td>"].join(""),b[e.header.stateField]=q===!0||!!p||q&&q.checked):(q="undefined"==typeof q||null===q?e.options.undefinedText:q,o=e.options.cardView?['<div class="card-view">',e.options.showHeader?f('<span class="title" %s>%s</span>',i,g(e.columns,"field","title",m)):"",f('<span class="value">%s</span>',q),"</div>"].join(""):[f("<td%s %s %s %s %s %s %s>",t,u,i,v,w,x,y),q,"</td>"].join(""),e.options.cardView&&e.options.smartDisplay&&""===q&&(o='<div class="card-view"></div>')),h.push(o)}}),this.options.cardView&&h.push("</div></td>"),h.push("</tr>"),h.join(" ")}},q.prototype.initBody=function(b){var c=this,d=this.getData();this.trigger("pre-body",d),this.$body=this.$el.find(">tbody"),this.$body.length||(this.$body=a("<tbody></tbody>").appendTo(this.$el)),this.options.pagination&&"server"!==this.options.sidePagination||(this.pageFrom=1,this.pageTo=d.length);for(var e,g=a(document.createDocumentFragment()),h=this.pageFrom-1;h<this.pageTo;h++){var i=d[h],k=this.initRow(i,h,d,g);e=e||!!k,k&&k!==!0&&g.append(k)}e||g.append('<tr class="no-records-found">'+f('<td colspan="%s">%s</td>',this.$header.find("th").length,this.options.formatNoMatches())+"</tr>"),this.$body.html(g),b||this.scrollTo(0),this.$body.find("> tr[data-index] > td").off("click dblclick").on("click dblclick",function(b){var d=a(this),e=d.parent(),g=c.data[e.data("index")],h=d[0].cellIndex,i=c.getVisibleFields(),j=i[c.options.detailView&&!c.options.cardView?h-1:h],k=c.columns[c.fieldsColumnsIndex[j]],l=n(g,j,c.options.escape);if(!d.find(".detail-icon").length&&(c.trigger("click"===b.type?"click-cell":"dbl-click-cell",j,l,g,d),c.trigger("click"===b.type?"click-row":"dbl-click-row",g,e,j),"click"===b.type&&c.options.clickToSelect&&k.clickToSelect&&c.options.ignoreClickToSelectOn(b.target))){var m=e.find(f('[name="%s"]',c.options.selectItemName));m.length&&m[0].click()}}),this.$body.find("> tr[data-index] > td > .detail-icon").off("click").on("click",function(b){b.preventDefault();var e=a(this),g=e.parent().parent(),h=g.data("index"),i=d[h];if(g.next().is("tr.detail-view"))e.find("i").attr("class",f("%s %s",c.options.iconsPrefix,c.options.icons.detailOpen)),c.trigger("collapse-row",h,i,g.next()),g.next().remove();else{e.find("i").attr("class",f("%s %s",c.options.iconsPrefix,c.options.icons.detailClose)),g.after(f('<tr class="detail-view"><td colspan="%s"></td></tr>',g.find("td").length));var k=g.next().find("td"),l=j(c.options,c.options.detailFormatter,[h,i,k],"");1===k.length&&k.append(l),c.trigger("expand-row",h,i,k)}return c.resetView(),!1}),this.$selectItem=this.$body.find(f('[name="%s"]',this.options.selectItemName)),this.$selectItem.off("click").on("click",function(b){b.stopImmediatePropagation();var d=a(this),e=d.prop("checked"),f=c.data[d.data("index")];(a(this).is(":radio")||c.options.singleSelect)&&a.each(c.options.data,function(a,b){b[c.header.stateField]=!1}),f[c.header.stateField]=e,c.options.singleSelect&&(c.$selectItem.not(this).each(function(){c.data[a(this).data("index")][c.header.stateField]=!1}),c.$selectItem.filter(":checked").not(this).prop("checked",!1)),c.updateSelected(),c.trigger(e?"check":"uncheck",f,d)}),a.each(this.header.events,function(b,d){if(d){"string"==typeof d&&(d=j(null,d));var e=c.header.fields[b],f=a.inArray(e,c.getVisibleFields());if(-1!==f){c.options.detailView&&!c.options.cardView&&(f+=1);for(var g in d)c.$body.find(">tr:not(.no-records-found)").each(function(){var b=a(this),h=b.find(c.options.cardView?".card-view":"td").eq(f),i=g.indexOf(" "),j=g.substring(0,i),k=g.substring(i+1),l=d[g];h.find(k).off(j).on(j,function(a){var d=b.data("index"),f=c.data[d],g=fLine truncated
+107 -20
View File
@@ -282,7 +282,7 @@ function moveStockItems(items, options) {
for (i = 0; i < response.length; i++) { for (i = 0; i < response.length; i++) {
var loc = response[i]; var loc = response[i];
html += makeOption(loc.pk, loc.name + ' - <i>' + loc.description + '</i>'); html += makeOption(loc.pk, loc.pathstring + ' - <i>' + loc.description + '</i>');
} }
html += "</select><br>"; html += "</select><br>";
@@ -312,10 +312,18 @@ function moveStockItems(items, options) {
var item = items[i]; var item = items[i];
var name = item.part__IPN;
if (name) {
name += ' | ';
}
name += item.part__name;
html += "<tr>"; html += "<tr>";
html += "<td>" + item.part.full_name + "</td>"; html += "<td>" + name + "</td>";
html += "<td>" + item.location.pathstring + "</td>"; html += "<td>" + item.location__path + "</td>";
html += "<td>" + item.quantity + "</td>"; html += "<td>" + item.quantity + "</td>";
html += "<td>"; html += "<td>";
@@ -372,14 +380,74 @@ function moveStockItems(items, options) {
function loadStockTable(table, options) { function loadStockTable(table, options) {
var params = options.params || {};
// Aggregate stock items
//params.aggregate = true;
table.bootstrapTable({ table.bootstrapTable({
sortable: true, sortable: true,
search: true, search: true,
method: 'get', method: 'get',
pagination: true, pagination: true,
pageSize: 50, pageSize: 25,
rememberOrder: true, rememberOrder: true,
queryParams: options.params, formatNoMatches: function() {
return 'No stock items matching query';
},
customSort: customGroupSorter,
groupBy: true,
groupByField: options.groupByField || 'part',
groupByFormatter: function(field, id, data) {
var row = data[0];
if (field == 'part__name') {
var name = row.part__IPN;
if (name) {
name += ' | ';
}
name += row.part__name;
return imageHoverIcon(row.part__image) + name + ' <i>(' + data.length + ' items)</i>';
}
else if (field == 'part__description') {
return row.part__description;
}
else if (field == 'quantity') {
var stock = 0;
data.forEach(function(item) {
stock += item.quantity;
});
return stock;
} else if (field == 'location__path') {
/* Determine how many locations */
var locations = [];
data.forEach(function(item) {
var loc = item.location;
if (!locations.includes(loc)) {
locations.push(loc);
}
});
if (locations.length > 1) {
return "In " + locations.length + " locations";
} else {
// A single location!
return renderLink(row.location__path, '/stock/location/' + row.location + '/')
}
}
else {
return '';
}
},
columns: [ columns: [
{ {
checkbox: true, checkbox: true,
@@ -392,47 +460,66 @@ function loadStockTable(table, options) {
visible: false, visible: false,
}, },
{ {
field: 'part_detail', field: 'part__name',
title: 'Part', title: 'Part',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return imageHoverIcon(value.image_url) + renderLink(value.full_name, value.url + 'stock/');
var name = row.part__IPN;
if (name) {
name += ' | ';
}
name += row.part__name;
return imageHoverIcon(row.part__image) + renderLink(name, '/part/' + row.part + '/stock/');
} }
}, },
{ {
field: 'part_detail.description', field: 'part__description',
title: 'Description', title: 'Description',
sortable: true, sortable: true,
}, },
{ {
field: 'location_detail', field: 'quantity',
title: 'Stock',
sortable: true,
formatter: function(value, row, index, field) {
var val = value;
// If there is a single unit with a serial number, use the serial number
if (row.serial && row.quantity == 1) {
val = '# ' + row.serial;
}
var text = renderLink(val, '/stock/item/' + row.pk + '/');
text = text + "<span class='badge'>" + row.status_text + "</span>";
return text;
}
},
{
field: 'location__path',
title: 'Location', title: 'Location',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
if (value) { if (value) {
return renderLink(value.pathstring, value.url); return renderLink(value, '/stock/location/' + row.location + '/');
} }
else { else {
return '<i>No stock location set</i>'; return '<i>No stock location set</i>';
} }
} }
}, },
{
field: 'quantity',
title: 'Quantity',
sortable: true,
formatter: function(value, row, index, field) {
var text = renderLink(value, row.url);
text = text + "<span class='badge'>" + row.status_text + "</span>";
return text;
}
},
{ {
field: 'notes', field: 'notes',
title: 'Notes', title: 'Notes',
} }
], ],
url: options.url, url: options.url,
queryParams: params,
}); });
if (options.buttons) { if (options.buttons) {
@@ -37,3 +37,74 @@ function linkButtonsToSelection(table, buttons) {
enableButtons(buttons, table.bootstrapTable('getSelections').length > 0); enableButtons(buttons, table.bootstrapTable('getSelections').length > 0);
}); });
} }
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function customGroupSorter(sortName, sortOrder, sortData) {
console.log('got here');
var order = sortOrder === 'desc' ? -1 : 1;
sortData.sort(function(a, b) {
// Extract default field values
var aa = a[sortName];
var bb = b[sortName];
// Extract parent information
var aparent = a._data && a._data['parent-index'];
var bparent = b._data && b._data['parent-index'];
// If either of the comparisons are in a group
if (aparent || bparent) {
// If the parents are different (or one item does not have a parent,
// then we need to extract the parent value for the selected column.
if (aparent != bparent) {
if (aparent) {
aa = a._data['table'].options.groupByFormatter(sortName, 0, a._data['group-data']);
}
if (bparent) {
bb = b._data['table'].options.groupByFormatter(sortName, 0, b._data['group-data']);
}
}
}
if (aa === undefined || aa === null) {
aa = '';
}
if (bb === undefined || bb === null) {
bb = '';
}
if (isNumeric(aa) && isNumeric(bb)) {
if (aa < bb) {
return order * -1;
} else if (aa > bb) {
return order;
} else {
return 0;
}
}
aa = aa.toString();
bb = bb.toString();
var cmp = aa.localeCompare(bb);
if (cmp === -1) {
return order * -1;
} else if (cmp === 1) {
return order;
} else {
return 0;
}
});
}
+60 -3
View File
@@ -5,6 +5,7 @@ JSON API for the Stock app
from django_filters.rest_framework import FilterSet, DjangoFilterBackend from django_filters.rest_framework import FilterSet, DjangoFilterBackend
from django_filters import NumberFilter from django_filters import NumberFilter
from django.conf import settings
from django.conf.urls import url, include from django.conf.urls import url, include
from django.urls import reverse from django.urls import reverse
@@ -20,6 +21,8 @@ from .serializers import StockTrackingSerializer
from InvenTree.views import TreeSerializer from InvenTree.views import TreeSerializer
from InvenTree.helpers import str2bool from InvenTree.helpers import str2bool
import os
from rest_framework.serializers import ValidationError from rest_framework.serializers import ValidationError
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.response import Response from rest_framework.response import Response
@@ -241,6 +244,7 @@ class StockList(generics.ListCreateAPIView):
- POST: Create a new StockItem - POST: Create a new StockItem
Additional query parameters are available: Additional query parameters are available:
- aggregate: If 'true' then stock items are aggregated by Part and Location
- location: Filter stock by location - location: Filter stock by location
- category: Filter by parts belonging to a certain category - category: Filter by parts belonging to a certain category
- supplier: Filter by supplier - supplier: Filter by supplier
@@ -257,6 +261,53 @@ class StockList(generics.ListCreateAPIView):
kwargs['context'] = self.get_serializer_context() kwargs['context'] = self.get_serializer_context()
return self.serializer_class(*args, **kwargs) return self.serializer_class(*args, **kwargs)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
# Instead of using the DRF serializer to LIST,
# we will serialize the objects manually.
# This is significantly faster
data = queryset.values(
'pk',
'quantity',
'serial',
'batch',
'status',
'notes',
'location',
'location__name',
'location__description',
'part',
'part__IPN',
'part__name',
'part__description',
'part__image',
'part__category',
'part__category__name',
'part__category__description',
)
# Reduce the number of lookups we need to do for categories
# Cache location lookups for this query
locations = {}
for item in data:
item['part__image'] = os.path.join(settings.MEDIA_URL, item['part__image'])
loc_id = item['location']
if loc_id:
if loc_id not in locations:
locations[loc_id] = StockLocation.objects.get(pk=loc_id).pathstring
item['location__path'] = locations[loc_id]
else:
item['location__path'] = None
return Response(data)
def get_queryset(self): def get_queryset(self):
""" """
If the query includes a particular location, If the query includes a particular location,
@@ -264,7 +315,7 @@ class StockList(generics.ListCreateAPIView):
""" """
# Start with all objects # Start with all objects
stock_list = StockItem.objects.all() stock_list = StockItem.objects.filter(customer=None, belongs_to=None)
# Does the client wish to filter by the Part ID? # Does the client wish to filter by the Part ID?
part_id = self.request.query_params.get('part', None) part_id = self.request.query_params.get('part', None)
@@ -310,8 +361,14 @@ class StockList(generics.ListCreateAPIView):
if supplier_id: if supplier_id:
stock_list = stock_list.filter(supplier_part__supplier=supplier_id) stock_list = stock_list.filter(supplier_part__supplier=supplier_id)
# Pre-fetch related objects for better response time # Also ensure that we pre-fecth all the related items
stock_list = self.get_serializer_class().setup_eager_loading(stock_list) stock_list = stock_list.prefetch_related(
'part',
'part__category',
'location'
)
stock_list = stock_list.order_by('part__name')
return stock_list return stock_list
+3
View File
@@ -183,6 +183,9 @@ class StockItem(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse('stock-item-detail', kwargs={'pk': self.id}) return reverse('stock-item-detail', kwargs={'pk': self.id})
def get_part_name(self):
return self.part.full_name
class Meta: class Meta:
unique_together = [ unique_together = [
('part', 'serial'), ('part', 'serial'),
+3 -9
View File
@@ -57,18 +57,11 @@ class StockItemSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True) url = serializers.CharField(source='get_absolute_url', read_only=True)
status_text = serializers.CharField(source='get_status_display', 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) part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
location_detail = LocationBriefSerializer(source='location', many=False, read_only=True) location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
@staticmethod
def setup_eager_loading(queryset):
queryset = queryset.prefetch_related('part')
queryset = queryset.prefetch_related('part__stock_items')
queryset = queryset.prefetch_related('part__category')
queryset = queryset.prefetch_related('location')
return queryset
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
part_detail = kwargs.pop('part_detail', False) part_detail = kwargs.pop('part_detail', False)
@@ -88,6 +81,7 @@ class StockItemSerializer(serializers.ModelSerializer):
'pk', 'pk',
'url', 'url',
'part', 'part',
'part_name',
'part_detail', 'part_detail',
'supplier_part', 'supplier_part',
'location', 'location',
+3 -1
View File
@@ -30,6 +30,7 @@
<!-- CSS --> <!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}"> <link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}"> <link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table-group-by.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}"> <link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/select2-bootstrap.css' %}"> <link rel="stylesheet" href="{% static 'css/select2-bootstrap.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}"> <link rel="stylesheet" href="{% static 'css/inventree.css' %}">
@@ -87,8 +88,9 @@ InvenTree
<script type="text/javascript" src="{% static 'script/bootstrap/bootstrap.min.js' %}"></script> <script type="text/javascript" src="{% static 'script/bootstrap/bootstrap.min.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-treeview.js' %}"></script> <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.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-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/select2/select2.js' %}"></script>
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script> <script type='text/javascript' src="{% static 'script/moment.js' %}"></script>