Merge branch 'bootstrap-table'

This commit is contained in:
Oliver 2018-05-03 00:47:18 +10:00
commit ffdcfdf51f
27 changed files with 619 additions and 7985 deletions

View File

@ -34,8 +34,6 @@ apipatterns = [
urlpatterns = [
# API URL
url(r'^api/', include(apipatterns)),
# url(r'^api-doc/', include_docs_urls(title='InvenTree API')),
url(r'^part/', include(part_urls)),
@ -50,6 +48,8 @@ urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api/', include(apipatterns)),
]
# Static file access

View File

@ -81,14 +81,11 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$("#build-list").footable();
$("#edit-build").click(function () {
launchModalForm("#modal-form",
"{% url 'build-edit' build.id %}",

View File

@ -29,12 +29,10 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$('#build-list').footable();
$("#new-build").click(function() {
launchModalForm("#modal-form",
"{% url 'build-create' %}",

View File

@ -3,7 +3,21 @@ from rest_framework import serializers
from .models import Company
class CompanySerializer(serializers.HyperlinkedModelSerializer):
class CompanyBriefSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
class Meta:
model = Company
fields = [
'pk',
'url',
'name'
]
class CompanySerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
class Meta:
model = Company

View File

@ -6,29 +6,7 @@
<h3>Company Parts</h3>
<table class='table table-striped' id='part-list' data-sorting='true' data-filtering='true'>
<thead>
<tr>
<th>SKU</th>
<th>Part</th>
<th>MPN</th>
<th>URL</th>
</tr>
</thead>
<tbody>
{% for part in company.parts.all %}
<tr>
<td><a href="{% url 'supplier-part-detail' part.id %}">{{ part.SKU }}</a></td>
<td>
{% if part.part %}
<a href="{% url 'part-suppliers' part.part.id %}">{{ part.part.name }}</a>
{% endif %}
</td>
<td>{{ part.manufacturer_string }}</td>
<td>{{ part.URL }}</td>
</tr>
{% endfor %}
</tbody>
<table clas='table table-striped table-condensed' id='part-table'>
</table>
<div class='container-fluid'>
@ -40,11 +18,9 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$("#part-list").footable();
$("#part-create").click(function () {
launchModalForm("#modal-form",
@ -56,4 +32,39 @@
reload: true,
});
});
$("#part-table").bootstrapTable({
sortable: true,
search: true,
queryParams: function(p) {
return {
supplier: {{ company.id }}
}
},
columns: [
{
sortable: true,
field: 'part',
title: 'Part',
formatter: function(value, row, index, field) {
return renderLink(value.name, value.url);
}
},
{
sortable: true,
field: 'SKU',
title: 'SKU',
formatter: function(value, row, index, field) {
return renderLink(value, row.url);
}
},
{
sortable: true,
field: 'manufacturer',
title: 'Manufacturer',
}
],
url: "{% url 'api-part-supplier-list' %}"
});
{% endblock %}

View File

@ -6,26 +6,7 @@
<h3>Companies</h3>
<table class='table table-striped' id='company-table' data-sorting='true' data-filtering='true'>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for company in companies %}
<tr>
<td>
<a href="{% url 'company-detail' company.id %}">
{{ company.name }}
</a>
</td>
<td>{{ company.description }}</td>
</tr>
{% endfor %}
</tbody>
<table class='table table-striped' id='company-table'>
</table>
<div class='container-fluid'>
@ -37,11 +18,9 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$('#company-table').footable();
$('#new-company').click(function () {
launchModalForm('#modal-form',
@ -50,4 +29,40 @@
follow: true
});
});
$("#company-table").bootstrapTable({
sortable: true,
search: true,
columns: [
{
field: 'pk',
title: 'ID',
visible: false,
},
{
field: 'name',
title: 'Company',
sortable: true,
formatter: function(value, row, index, field) {
return renderLink(value, row.url);
}
},
{
field: 'description',
title: 'Description',
},
{
field: 'website',
title: 'Website',
formatter: function(value, row, index, field) {
if (value) {
return renderLink(value, value);
}
return '';
}
}
],
url: "{% url 'api-company-list' %}"
});
{% endblock %}

View File

@ -7,8 +7,11 @@ from rest_framework import generics, permissions
from django.conf.urls import url
from .models import Part, PartCategory
from .serializers import PartSerializer
from .models import Part, PartCategory, BomItem
from .models import SupplierPart
from .serializers import PartSerializer, BomItemSerializer
from .serializers import SupplierPartSerializer
from InvenTree.views import TreeSerializer
@ -49,9 +52,53 @@ class PartList(generics.ListCreateAPIView):
]
class BomList(generics.ListAPIView):
queryset = BomItem.objects.all()
serializer_class = BomItemSerializer
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filter_fields = [
'part',
'sub_part'
]
class SupplierPartList(generics.ListAPIView):
queryset = SupplierPart.objects.all()
serializer_class = SupplierPartSerializer
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
]
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filter_fields = [
'part',
'supplier'
]
part_api_urls = [
url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'),
url(r'^supplier/?', SupplierPartList.as_view(), name='api-part-supplier-list'),
url(r'^bom/?', BomList.as_view(), name='api-bom-list'),
url(r'^.*$', PartList.as_view(), name='api-part-list'),
]

View File

@ -1,6 +1,37 @@
from rest_framework import serializers
from .models import Part
from .models import Part, PartCategory, BomItem
from .models import SupplierPart
from company.serializers import CompanyBriefSerializer
class CategoryBriefSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
class Meta:
model = PartCategory
fields = [
'pk',
'name',
'description',
'pathstring',
'url',
]
class PartBriefSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
class Meta:
model = Part
fields = [
'pk',
'url',
'name',
'description',
]
class PartSerializer(serializers.ModelSerializer):
@ -8,16 +39,19 @@ class PartSerializer(serializers.ModelSerializer):
Used when displaying all details of a single component.
"""
url = serializers.CharField(source='get_absolute_url', read_only=True)
category = CategoryBriefSerializer(many=False, read_only=True)
class Meta:
model = Part
fields = [
'pk',
'url', # Link to the part detail page
'name',
'IPN',
'URL', # Link to an external URL (optional)
'description',
'category',
'category_path',
'total_stock',
'available_stock',
'units',
@ -26,3 +60,41 @@ class PartSerializer(serializers.ModelSerializer):
'trackable',
'salable',
]
class BomItemSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
part = PartBriefSerializer(many=False, read_only=True)
sub_part = PartBriefSerializer(many=False, read_only=True)
class Meta:
model = BomItem
fields = [
'pk',
'url',
'part',
'sub_part',
'quantity'
]
class SupplierPartSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
part = PartBriefSerializer(many=False, read_only=True)
supplier = CompanyBriefSerializer(many=False, read_only=True)
class Meta:
model = SupplierPart
fields = [
'pk',
'url',
'part',
'supplier',
'SKU',
'manufacturer',
'MPN',
]

View File

@ -3,8 +3,6 @@
{% block css %}
<link rel='stylesheet' href="{% static 'css/footable.bootstrap.css' %}">
{% endblock %}
{% block details %}
@ -13,30 +11,7 @@
<h3>Bill of Materials</h3>
<table class="table table-striped" id='bom-table' data-filtering='true' data-sorting='true'>
<thead>
<tr>
<th>Part</th>
<th>Description</th>
<th data-type='number'>Quantity</th>
<th data-sortable='false'></th>
</tr>
</thead>
<tbody>
{% for bom_item in part.bom_items.all %}
{% with sub_part=bom_item.sub_part %}
<tr>
<td><a href="{% url 'part-detail' sub_part.id %}">{{ sub_part.name }}</a></td>
<td>{{ sub_part.description }}</td>
<td>{{ bom_item.quantity }}</td>
<td>
<button type='button' url="{% url 'bom-item-edit' bom_item.id %}" class='btn btn-success edit-row-button'>Edit</button>
<button type='button' url="{% url 'bom-item-delete' bom_item.id %}" class='btn btn-danger delete-row-button'>Delete</button>
</td>
</tr>
{% endwith %}
{% endfor %}
</tbody>
<table class='table table-striped table-condensed' id='bom-table'>
</table>
<div class='container-fluid'>
@ -46,27 +21,32 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$('#bom-table').footable();
$('#bom-table').on('click', '.delete-row-button', function () {
function reloadBom() {
$("#bom-table").bootstrapTable('refresh');
}
$('#bom-table').on('click', '.delete-button', function () {
var button = $(this);
launchDeleteForm("#modal-delete",
button.attr('url'),
{
reload: true
success: reloadBom
});
});
$('#bom-table').on('click', '.edit-row-button', function () {
$("#bom-table").on('click', '.edit-button', function () {
var button = $(this);
launchModalForm("#modal-form",
button.attr('url'));
button.attr('url'),
{
success: reloadBom
});
});
@ -80,4 +60,45 @@
}
});
});
$("#bom-table").bootstrapTable({
sortable: true,
search: true,
queryParams: function(p) {
return {
part: {{ part.id }}
}
},
columns: [
{
field: 'pk',
title: 'ID',
visible: false,
},
{
field: 'sub_part',
title: 'Part',
sortable: true,
formatter: function(value, row, index, field) {
return renderLink(value.name, value.url);
}
},
{
field: 'sub_part.description',
title: 'Description',
},
{
field: 'quantity',
title: 'Quantity',
searchable: false,
sortable: true
},
{
formatter: function(value, row, index, field) {
return editButton(row.url + 'edit') + ' ' + deleteButton(row.url + 'delete');
}
}
],
url: "{% url 'api-bom-list' %}"
});
{% endblock %}

View File

@ -17,8 +17,8 @@
{% endif %}
{% if category.has_parts %}
<h4>Parts</h4>
{% include "part/category_parts.html" with parts=category.parts.all %}
<table class='table table-striped table-condensed' id='part-table'>
</table>
{% endif %}
<div class='container-fluid'>
@ -29,6 +29,7 @@
<button class="btn btn-success" id='create-part'>New Part</button>
<button class="btn btn-danger" id='delete-category'>Delete Category</button>
<button class='btn btn-primary' id='get-rows'>Do thing</button>
</div>
{% include 'modals.html' %}
@ -36,11 +37,9 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>``
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$('#part-list').footable();
$("#edit-category").click(function () {
launchModalForm("#modal-form",
@ -84,4 +83,11 @@
follow: true
});
});
{% include "part/category_parts.html" with category=category %}
$("#get-rows").click( function() {
alert($("#part-table").bootstrapTable('getSelections'));
});
{% endblock %}

View File

@ -1,24 +1,63 @@
<table class="table table-striped" data-sorting='true' data-filtering='true' id="part-list">
<thead>
<tr>
<th>Part</th>
<th>Description</th>
<th>Category</th>
<th data-type='number'>Stock</th>
</tr>
</thead>
<tbody>
{% for part in parts %}
<tr>
<td><a href="{% url 'part-detail' part.id %}">{{ part.name }}</a></td>
<td>{{ part.description }}</td>
<td>
{% if part.category %}
<a href="{% url 'category-detail' part.category.id %}">{{ part.category_path }}</a>
$("#part-table").bootstrapTable({
sortable: true,
search: true,
sortName: 'description',
idField: 'pk',
method: 'get',
pagination: true,
rememberOrder: true,
{% if category %}
queryParams: function(p) {
return {
category: {{ category.id }},
}
},
{% endif %}
</td>
<td><a href="{% url 'part-stock' part.id %}">{{ part.total_stock }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
columns: [
{
checkbox: true,
title: 'Select',
searchable: false,
},
{
field: 'pk',
title: 'ID',
visible: false,
},
{
field: 'name',
title: 'Part',
sortable: true,
formatter: function(value, row, index, field) {
return renderLink(value, row.url);
}
},
{
sortable: true,
field: 'description',
title: 'Description',
},
{% if category == None %}
{
sortable: true,
field: 'category',
title: 'Category',
formatter: function(value, row, index, field) {
if (row.category) {
return renderLink(row.category.name, row.category.url);
}
else {
return '';
}
}
},
{% endif %}
{
field: 'total_stock',
title: 'Stock',
searchable: false,
sortable: true,
}
],
url: "{% url 'api-part-list' %}",
});

View File

@ -16,8 +16,7 @@
{% include "part/category_subcategories.html" with children=children %}
{% endif %}
<h4>Parts</h4>
{% include "part/category_parts.html" with parts=parts %}
<table class='table table-striped table-condensed' id='part-table'>
<div class='container-fluid'>
<button type='button' class='btn btn-primary' id='create-cat'>
@ -29,11 +28,9 @@
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$('#part-list').footable();
$("#create-cat").click(function() {
launchModalForm("#modal-form",
@ -73,6 +70,8 @@
launchModalForm("#modal-form", "{% url 'part-create' %}");
});
{% include "part/category_parts.html" %}
loadTree();
{% endblock %}

View File

@ -6,50 +6,8 @@
<h3>Part Stock</h3>
<table class="table table-striped" id='stock-table' data-sorting='true'>
<thead>
<tr>
<th data-sortable='false'>Link</th>
{% if part.trackable %}
<th>Serial Number</th>
{% else %}
<th>Quantity</th>
{% endif %}
<th>Location</th>
<th>Supplier part</th>
<th>Stocktake</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
{% for stock in part.stock_entries %}
<tr>
<td><a href="{% url 'stock-item-detail' stock.id %}">Click</a></td>
<td>
{% if part.trackable %}
{{ stock.serial }}
{% else %}
{{ stock.quantity }}
{% endif %}
</td>
<td>
{% if stock.location %}
<a href="{% url 'stock-location-detail' stock.location.id %}">{{ stock.location.name }}</a>
{% endif %}
</td>
<td>
{% if stock.supplier_part %}
<a href="{% url 'supplier-part-detail' stock.supplier_part.id %}">
{{ stock.supplier_part.supplier.name }} | {{ stock.supplier_part.SKU }}
</a>
{% endif %}
</td>
<td>{% if stock.stocktake_date %}{{ stock.stocktake_date }}{% endif %}</td>
<td>{{ stock.notes }}</td>
</tr>
{% endfor %}
</tbody>
</table
<table class='table table-striped table-condensed' id='stock-table'>
</table>
<div class='container-fluid'>
<button class='btn btn-success' id='add-stock-item'>Add new Stock Item</button>
@ -59,11 +17,9 @@
{% block js_load %}
{{ block.super }}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#stock-table').footable();
$('#add-stock-item').click(function () {
launchModalForm("#modal-form",
@ -75,4 +31,41 @@
}
});
});
$("#stock-table").bootstrapTable({
sortable: true,
search: true,
queryParams: function(p) {
return {
part: {{ part.id }},
in_stock: true,
}
},
columns: [
{
field: 'pk',
title: 'ID',
visible: false,
},
{
checkbox: true,
},
{
field: 'location',
title: 'Location',
sortable: true,
formatter: function(value, row, index, field){
return renderLink(value.pathstring, value.url);
}
},
{
field: 'quantity',
title: 'Stock',
searchable: false,
sortable: true,
}
],
url: "{% url 'api-stock-list' %}"
});
{% endblock %}

View File

@ -6,30 +6,14 @@
<h3>Part Suppliers</h3>
<table class="table table-striped" id='supplier-table' data-sorting='true'>
<thead>
<tr>
<th>SKU</th>
<th>Supplier</th>
<th>MPN</th>
<th>URL</th>
</tr>
</thead>
<tbody>
{% for spart in part.supplier_parts.all %}
<tr>
<td><a href="{% url 'supplier-part-detail' spart.id %}">{{ spart.SKU }}</a></td>
<td><a href="{% url 'company-detail' spart.supplier.id %}">{{ spart.supplier.name }}</a></td>
<td>{{ spart.manufacturer_string }}</td>
<td>
{% if spart.URL %}
<a href="{{ spart.URL }}">{{ spart.URL }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
{% if part.supplier_count > 0 %}
<p><b>{{ part.name }}</b> is available from {{ part.supplier_count }} suppliers.</p>
<table class="table table-striped table-condensed" id='supplier-table'>
</table>
{% else %}
<p><b>{{ part.name }}</b> is not available from any suppliers.</p>
{% endif %}
<div class='container-fluid'>
<button class="btn btn-success" id='supplier-create'>New Supplier Part</button>
@ -39,11 +23,9 @@
{% block js_load %}
{{ block.super }}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>``
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#supplier-table').footable();
$('#supplier-create').click(function () {
launchModalForm("#modal-form",
@ -53,4 +35,39 @@
data: {part: {{ part.id }} }
});
});
$("#supplier-table").bootstrapTable({
sortable: true,
search: true,
queryParams: function(p) {
return {
part: {{ part.id }}
}
},
columns: [
{
sortable: true,
field: 'supplier',
title: 'Supplier',
formatter: function(value, row, index, field) {
return renderLink(value.name, value.url);
}
},
{
sortable: true,
field: 'SKU',
title: 'SKU',
formatter: function(value, row, index, field) {
return renderLink(value, row.url);
}
},
{
sortable: true,
field: 'manufacturer',
title: 'Manufacturer',
}
],
url: "{% url 'api-part-supplier-list' %}"
});
{% endblock %}

View File

@ -6,19 +6,58 @@
<h3>Used In</h3>
<table class="table table-striped">
<tr>
<th>Part</th>
<th>Uses</th>
<th>Description</th>
</tr>
{% for item in part.used_in.all %}
<tr>
<td><a href="{% url 'part-bom' item.part.id %}">{{ item.part.name }}</a></td>
<td>{{ item.quantity }}</td>
<td>{{ item.part.description }}</td>
</tr>
{% endfor %}
{% if part.used_in_count > 0 %}
<p>
<b>{{ part.name }}</b> is used to make {{ part.used_in_count }} other parts.
</p>
<table class="table table-striped table-condensed" id='used-table'>
</table>
{% else %}
<p>
{{ part.name }} is not used to make any other parts.
</p>
{% endif %}
{% endblock %}
{% block js_ready %}
{{ block.super }}
$("#used-table").bootstrapTable({
sortable: true,
search: true,
queryParams: function(p) {
return {
sub_part: {{ part.id }}
}
},
columns: [
{
field: 'pk',
title: 'ID',
visible: false,
},
{
field: 'part',
title: 'Part',
formatter: function(value, row, index, field) {
return renderLink(value.name, value.url);
}
},
{
field: 'part.description',
title: 'Description',
},
{
field: 'quantity',
title: 'Uses',
}
],
url: "{% url 'api-bom-list' %}"
})
{% endblock %}

File diff suppressed because one or more lines are too long

View File

@ -1,341 +0,0 @@
/*
* FooTable v3 - FooTable is a jQuery plugin that aims to make HTML tables on smaller devices look awesome.
* @version 3.1.5
* @link http://fooplugins.com
* @copyright Steven Usher & Brad Vincent 2015
* @license Released under the GPLv3 license.
*/
table.footable,
table.footable-details {
position: relative;
width: 100%;
border-spacing: 0;
border-collapse: collapse;
}
table.footable-details {
margin-bottom: 0;
}
table.footable-hide-fouc {
display: none;
}
table > tbody > tr > td > span.footable-toggle {
margin-right: 8px;
opacity: 0.3;
}
table > tbody > tr > td > span.footable-toggle.last-column {
margin-left: 8px;
float: right;
}
table.table-condensed > tbody > tr > td > span.footable-toggle {
margin-right: 5px;
}
table.footable-details > tbody > tr > th:nth-child(1) {
min-width: 40px;
width: 120px;
}
table.footable-details > tbody > tr > td:nth-child(2) {
word-break: break-all;
}
table.footable-details > thead > tr:first-child > th,
table.footable-details > thead > tr:first-child > td,
table.footable-details > tbody > tr:first-child > th,
table.footable-details > tbody > tr:first-child > td,
table.footable-details > tfoot > tr:first-child > th,
table.footable-details > tfoot > tr:first-child > td {
border-top-width: 0;
}
table.footable-details.table-bordered > thead > tr:first-child > th,
table.footable-details.table-bordered > thead > tr:first-child > td,
table.footable-details.table-bordered > tbody > tr:first-child > th,
table.footable-details.table-bordered > tbody > tr:first-child > td,
table.footable-details.table-bordered > tfoot > tr:first-child > th,
table.footable-details.table-bordered > tfoot > tr:first-child > td {
border-top-width: 1px;
}
div.footable-loader {
vertical-align: middle;
text-align: center;
height: 300px;
position: relative;
}
div.footable-loader > span.fooicon {
display: inline-block;
opacity: 0.3;
font-size: 30px;
line-height: 32px;
width: 32px;
height: 32px;
margin-top: -16px;
margin-left: -16px;
position: absolute;
top: 50%;
left: 50%;
-webkit-animation: fooicon-spin-r 2s infinite linear;
animation: fooicon-spin-r 2s infinite linear;
}
table.footable > tbody > tr.footable-empty > td {
vertical-align: middle;
text-align: center;
font-size: 30px;
}
table.footable > tbody > tr > td,
table.footable > tbody > tr > th {
display: none;
}
table.footable > tbody > tr.footable-empty > td,
table.footable > tbody > tr.footable-empty > th,
table.footable > tbody > tr.footable-detail-row > td,
table.footable > tbody > tr.footable-detail-row > th {
display: table-cell;
}
@-webkit-keyframes fooicon-spin-r {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
@keyframes fooicon-spin-r {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
.fooicon {
position: relative;
top: 1px;
display: inline-block;
font-family: 'Glyphicons Halflings' !important;
font-style: normal;
font-weight: 400;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.fooicon:before,
.fooicon:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.fooicon-loader:before {
content: "\e030";
}
.fooicon-plus:before {
content: "\2b";
}
.fooicon-minus:before {
content: "\2212";
}
.fooicon-search:before {
content: "\e003";
}
.fooicon-remove:before {
content: "\e014";
}
.fooicon-sort:before {
content: "\e150";
}
.fooicon-sort-asc:before {
content: "\e155";
}
.fooicon-sort-desc:before {
content: "\e156";
}
.fooicon-pencil:before {
content: "\270f";
}
.fooicon-trash:before {
content: "\e020";
}
.fooicon-eye-close:before {
content: "\e106";
}
.fooicon-flash:before {
content: "\e162";
}
.fooicon-cog:before {
content: "\e019";
}
.fooicon-stats:before {
content: "\e185";
}
table.footable > thead > tr.footable-filtering > th {
border-bottom-width: 1px;
font-weight: normal;
}
.footable-filtering-external.footable-filtering-right,
table.footable > thead > tr.footable-filtering > th,
table.footable.footable-filtering-right > thead > tr.footable-filtering > th {
text-align: right;
}
.footable-filtering-external.footable-filtering-left,
table.footable.footable-filtering-left > thead > tr.footable-filtering > th {
text-align: left;
}
.footable-filtering-external.footable-filtering-center,
table.footable.footable-filtering-center > thead > tr.footable-filtering > th {
text-align: center;
}
table.footable > thead > tr.footable-filtering > th div.form-group {
margin-bottom: 0;
}
table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group {
margin-top: 5px;
}
table.footable > thead > tr.footable-filtering > th div.input-group {
width: 100%;
}
.footable-filtering-external ul.dropdown-menu > li > a.checkbox,
table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox {
margin: 0;
display: block;
position: relative;
}
.footable-filtering-external ul.dropdown-menu > li > a.checkbox > label,
table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox > label {
display: block;
padding-left: 20px;
}
.footable-filtering-external ul.dropdown-menu > li > a.checkbox input[type="checkbox"],
table.footable > thead > tr.footable-filtering > th ul.dropdown-menu > li > a.checkbox input[type="checkbox"] {
position: absolute;
margin-left: -20px;
}
@media (min-width: 768px) {
table.footable > thead > tr.footable-filtering > th div.input-group {
width: auto;
}
table.footable > thead > tr.footable-filtering > th div.form-group {
margin-left: 2px;
margin-right: 2px;
}
table.footable > thead > tr.footable-filtering > th div.form-group+div.form-group {
margin-top: 0;
}
}
table.footable > thead > tr > td.footable-sortable,
table.footable > thead > tr > th.footable-sortable,
table.footable > tbody > tr > td.footable-sortable,
table.footable > tbody > tr > th.footable-sortable,
table.footable > tfoot > tr > td.footable-sortable,
table.footable > tfoot > tr > th.footable-sortable {
position: relative;
padding-right: 30px;
cursor: pointer;
}
td.footable-sortable > span.fooicon,
th.footable-sortable > span.fooicon {
position: absolute;
right: 6px;
top: 50%;
margin-top: -7px;
opacity: 0;
transition: opacity 0.3s ease-in;
}
td.footable-sortable:hover > span.fooicon,
th.footable-sortable:hover > span.fooicon {
opacity: 1;
}
td.footable-sortable.footable-asc > span.fooicon,
th.footable-sortable.footable-asc > span.fooicon,
td.footable-sortable.footable-desc > span.fooicon,
th.footable-sortable.footable-desc > span.fooicon {
opacity: 1;
}
/* hides the sort icons when sorting is not allowed */
table.footable-sorting-disabled td.footable-sortable.footable-asc > span.fooicon,
table.footable-sorting-disabled td.footable-sortable.footable-desc > span.fooicon,
table.footable-sorting-disabled td.footable-sortable:hover > span.fooicon,
table.footable-sorting-disabled th.footable-sortable.footable-asc > span.fooicon,
table.footable-sorting-disabled th.footable-sortable.footable-desc > span.fooicon,
table.footable-sorting-disabled th.footable-sortable:hover > span.fooicon {
opacity: 0;
visibility: hidden;
}
.footable-paging-external ul.pagination,
table.footable > tfoot > tr.footable-paging > td > ul.pagination {
margin: 10px 0 0 0;
}
.footable-paging-external span.label,
table.footable > tfoot > tr.footable-paging > td > span.label {
display: inline-block;
margin: 0 0 10px 0;
padding: 4px 10px;
}
.footable-paging-external.footable-paging-center,
table.footable > tfoot > tr.footable-paging > td,
table.footable-paging-center > tfoot > tr.footable-paging > td {
text-align: center;
}
.footable-paging-external.footable-paging-left,
table.footable-paging-left > tfoot > tr.footable-paging > td {
text-align: left;
}
.footable-paging-external.footable-paging-right,
table.footable-paging-right > tfoot > tr.footable-paging > td {
text-align: right;
}
ul.pagination > li.footable-page {
display: none;
}
ul.pagination > li.footable-page.visible {
display: inline;
}
td.footable-editing {
width: 90px;
max-width: 90px;
}
table.footable-editing-no-edit td.footable-editing,
table.footable-editing-no-delete td.footable-editing,
table.footable-editing-no-view td.footable-editing {
width: 70px;
max-width: 70px;
}
table.footable-editing-no-edit.footable-editing-no-delete td.footable-editing,
table.footable-editing-no-edit.footable-editing-no-view td.footable-editing,
table.footable-editing-no-delete.footable-editing-no-view td.footable-editing {
width: 50px;
max-width: 50px;
}
table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view td.footable-editing,
table.footable-editing-no-edit.footable-editing-no-delete.footable-editing-no-view th.footable-editing {
width: 0;
max-width: 0;
display: none !important;
}
table.footable-editing-right td.footable-editing,
table.footable-editing-right tr.footable-editing {
text-align: right;
}
table.footable-editing-left td.footable-editing,
table.footable-editing-left tr.footable-editing {
text-align: left;
}
table.footable-editing button.footable-add,
table.footable-editing button.footable-hide,
table.footable-editing-show button.footable-show,
table.footable-editing.footable-editing-always-show button.footable-show,
table.footable-editing.footable-editing-always-show button.footable-hide,
table.footable-editing.footable-editing-always-show.footable-editing-no-add tr.footable-editing {
display: none;
}
table.footable-editing.footable-editing-show button.footable-add,
table.footable-editing.footable-editing-show button.footable-hide,
table.footable-editing.footable-editing-always-show button.footable-add {
display: inline-block;
}

View File

@ -0,0 +1,7 @@
/*
* 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";a.fn.bootstrapTable.locales["en-US"]={formatLoadingMessage:function(){return"Loading, please wait..."},formatRecordsPerPage:function(a){return a+" rows per page"},formatShowingRows:function(a,b,c){return"Showing "+a+" to "+b+" of "+c+" rows"},formatSearch:function(){return"Search"},formatNoMatches:function(){return"No matching records found"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatRefresh:function(){return"Refresh"},formatToggle:function(){return"Toggle"},formatColumns:function(){return"Columns"},formatAllRows:function(){return"All"},formatExport:function(){return"Export data"},formatClearFilters:function(){return"Clear filters"}},a.extend(a.fn.bootstrapTable.defaults,a.fn.bootstrapTable.locales["en-US"])}(jQuery);

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
function editButton(url, text='Edit') {
return "<button class='btn btn-success edit-button' type='button' url='" + url + "'>" + text + "</button>";
}
function deleteButton(url, text='Delete') {
return "<button class='btn btn-danger delete-button' type='button' url='" + url + "'>" + text + "</button>";
}
function renderLink(text, url) {
if (text && url) {
return '<a href="' + url + '">' + text + '</a>';
}
else if (text) {
return text;
}
else {
return '';
}
}

View File

@ -2,10 +2,30 @@ from rest_framework import serializers
from .models import StockItem, StockLocation
from part.serializers import PartBriefSerializer
class LocationBriefSerializer(serializers.ModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True)
class Meta:
model = StockLocation
fields = [
'pk',
'name',
'pathstring',
'url',
]
class StockItemSerializer(serializers.ModelSerializer):
""" Serializer for a StockItem
"""
url = serializers.CharField(source='get_absolute_url', read_only=True)
part = PartBriefSerializer(many=False, read_only=True)
location = LocationBriefSerializer(many=False, read_only=True)
class Meta:
model = StockItem
@ -16,17 +36,17 @@ class StockItemSerializer(serializers.ModelSerializer):
'supplier_part',
'location',
'in_stock',
'belongs_to',
'customer',
#'belongs_to',
#'customer',
'quantity',
'serial',
'batch',
'status',
'notes',
'updated',
'stocktake_date',
'stocktake_user',
'review_needed',
#'updated',
#'stocktake_date',
#'stocktake_user',
#'review_needed',
]
""" These fields are read-only in this context.

View File

@ -9,35 +9,7 @@
{% include "stock/location_list.html" with locations=locations %}
{% endif %}
<table class="table table-striped" id='stock-table' data-filtering='true' data-sorting='true'>
<thead>
<tr>
<th>Part</th>
<th>Location</th>
<th data-type='number'>Stock</th>
<th>Status</th>
<th data-type='date'>Stocktake</th>
<th data-sortable='false'></th>
</tr>
</thead>
<tbody>
{% for item in items.all %}
<tr>
<td><a href="{% url 'part-stock' item.part.id %}">{{ item.part.name }}</a></td>
<td>
{% if item.location %}
<a href="{% url 'stock-location-detail' item.location.id %}">
{{ item.location.pathstring }}
</a>
{% endif %}
</td>
<td>{{ item.quantity }}</td>
<td>{{ item.get_status_display }}</td>
<td>{{ item.stocktake_date }}</td>
<td><a href="{% url 'stock-item-detail' item.id %}">Click</a></td>
</tr>
{% endfor %}
</tbody>
<table class="table table-striped" id='stock-table'>
</table>
<div class='container-fluid'>
@ -49,12 +21,9 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$('#stock-table').footable();
$('#location-create').click(function () {
launchModalForm("#modal-form",
@ -63,4 +32,7 @@
follow: true
});
});
{% include "stock/stock_table.html" %}
{% endblock %}

View File

@ -114,8 +114,6 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}

View File

@ -12,8 +12,10 @@
{% include "stock/location_list.html" with locations=location.children %}
{% endif %}
<h4>Stock Items</h4>
{% include "stock/stock_table.html" with items=location.items %}
{% if location.has_items %}
<table class='table table-striped table-condensed' id='stock-table'>
</table>
{% endif %}
<div class='container-fluid'>
<button class='btn btn-success' id='location-create'>New Stock Location</button>
@ -28,12 +30,9 @@
{% endblock %}
{% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %}
{% block js_ready %}
$('#stock-table').footable();
$('#location-create').click(function () {
launchModalForm("#modal-form",
@ -72,4 +71,7 @@
}
});
});
{% include 'stock/stock_table.html' with location=location %}
{% endblock %}

View File

@ -1,22 +1,60 @@
<table class="table table-striped" id='stock-table' data-filtering='true' data-sorting='true'>
<thead>
<tr>
<th>Part</th>
<th data-type='number'>Stock</th>
<th>Status</th>
<th>Stocktake</th>
<th data-sortable='false'></th>
</tr>
</thead>
<tbody>
{% for item in items.all %}
<tr>
<td><a href="{% url 'part-stock' item.part.id %}">{{ item.part.name }}</a></td>
<td>{{ item.quantity }}</td>
<td>{{ item.get_status_display }}</td>
<td>{{ item.stocktake_date }}</td>
<td><a href="{% url 'stock-item-detail' item.id %}">Click</a></td>
</tr>
{% endfor %}
</tbody>
</table>
$("#stock-table").bootstrapTable({
sortable: true,
search: true,
method: 'get',
pagination: true,
rememberOrder: true,
{% if location %}
queryParams: function(p) {
return {
location: {{ location.id }}
}
},
{% endif %}
columns: [
{
checkbox: true,
title: 'Select',
searchable: false,
},
{
field: 'pk',
title: 'ID',
visible: false,
},
{
field: 'part.name',
title: 'Part',
sortable: true,
formatter: function(value, row, index, field) {
return renderLink(value, row.part.url);
}
},
{% if location == None %}
{
field: 'location',
title: 'Location',
sortable: true,
formatter: function(value, row, index, field) {
if (row.location) {
return renderLink(row.location.name, row.location.url);
}
else {
return '';
}
}
},
{% endif %}
{
field: 'quantity',
title: 'Stock',
sortable: true,
},
{
field: 'status',
title: 'Status',
sortable: true,
}
],
url: "{% url 'api-stock-list' %}",
});

View File

@ -10,8 +10,9 @@
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
{% block css %}
{% endblock %}
@ -47,6 +48,12 @@ InvenTree
<script type="text/javascript" src="{% static 'script/bootstrap.min.js' %}"></script>
<script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap-treeview.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap-table.min.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap-table-en-US.min.js' %}"></script>
<script type='text/javascript' src="{% static 'script/tables.js' %}"></script>
<script type='text/javascript' src="{% static 'script/trees.js' %}"></script>
<script type='text/javascript' src="{% static 'script/sidenav.js' %}"></script>
<script type='text/javascript' src="{% static 'script/notification.js' %}"></script>