mirror of
https://github.com/inventree/InvenTree.git
synced 2026-10-05 09:55:28 +00:00
Merge pull request #375 from SchrodingersGat/group-stock-items
Group stock items
This commit is contained in:
14 files changed
+4586
-44
No files matched your search
@@ -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:
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
location_detail: true,
|
location_detail: true,
|
||||||
part_detail: true,
|
part_detail: true,
|
||||||
},
|
},
|
||||||
|
groupByField: 'location',
|
||||||
buttons: [
|
buttons: [
|
||||||
'#stock-options',
|
'#stock-options',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -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
@@ -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 */
|
||||||
@@ -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);
|
||||||
|
});
|
||||||
+3770
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,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/`/g,"`"):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:"‹",paginationNextText:"›",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
|
|
||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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'),
|
||||||
|
|||||||
@@ -57,17 +57,10 @@ 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):
|
||||||
|
|
||||||
@@ -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',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in new issue
Block a user