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

View File

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

View File

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

View File

@ -3,7 +3,21 @@ from rest_framework import serializers
from .models import Company 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: class Meta:
model = Company model = Company

View File

@ -6,29 +6,7 @@
<h3>Company Parts</h3> <h3>Company Parts</h3>
<table class='table table-striped' id='part-list' data-sorting='true' data-filtering='true'> <table clas='table table-striped table-condensed' id='part-table'>
<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> </table>
<div class='container-fluid'> <div class='container-fluid'>
@ -40,11 +18,9 @@
{% endblock %} {% endblock %}
{% block js_load %} {% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script> <script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}
$("#part-list").footable();
$("#part-create").click(function () { $("#part-create").click(function () {
launchModalForm("#modal-form", launchModalForm("#modal-form",
@ -56,4 +32,39 @@
reload: true, 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 %} {% endblock %}

View File

@ -6,26 +6,7 @@
<h3>Companies</h3> <h3>Companies</h3>
<table class='table table-striped' id='company-table'>
<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> </table>
<div class='container-fluid'> <div class='container-fluid'>
@ -37,11 +18,9 @@
{% endblock %} {% endblock %}
{% block js_load %} {% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script> <script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}
$('#company-table').footable();
$('#new-company').click(function () { $('#new-company').click(function () {
launchModalForm('#modal-form', launchModalForm('#modal-form',
@ -50,4 +29,40 @@
follow: true 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 %} {% endblock %}

View File

@ -7,8 +7,11 @@ from rest_framework import generics, permissions
from django.conf.urls import url from django.conf.urls import url
from .models import Part, PartCategory from .models import Part, PartCategory, BomItem
from .serializers import PartSerializer from .models import SupplierPart
from .serializers import PartSerializer, BomItemSerializer
from .serializers import SupplierPartSerializer
from InvenTree.views import TreeSerializer 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 = [ part_api_urls = [
url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'), 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'), url(r'^.*$', PartList.as_view(), name='api-part-list'),
] ]

View File

@ -1,6 +1,37 @@
from rest_framework import serializers 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): class PartSerializer(serializers.ModelSerializer):
@ -8,16 +39,19 @@ class PartSerializer(serializers.ModelSerializer):
Used when displaying all details of a single component. 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: class Meta:
model = Part model = Part
fields = [ fields = [
'pk',
'url', # Link to the part detail page 'url', # Link to the part detail page
'name', 'name',
'IPN', 'IPN',
'URL', # Link to an external URL (optional) 'URL', # Link to an external URL (optional)
'description', 'description',
'category', 'category',
'category_path',
'total_stock', 'total_stock',
'available_stock', 'available_stock',
'units', 'units',
@ -26,3 +60,41 @@ class PartSerializer(serializers.ModelSerializer):
'trackable', 'trackable',
'salable', '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 %} {% block css %}
<link rel='stylesheet' href="{% static 'css/footable.bootstrap.css' %}">
{% endblock %} {% endblock %}
{% block details %} {% block details %}
@ -13,30 +11,7 @@
<h3>Bill of Materials</h3> <h3>Bill of Materials</h3>
<table class="table table-striped" id='bom-table' data-filtering='true' data-sorting='true'> <table class='table table-striped table-condensed' id='bom-table'>
<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> </table>
<div class='container-fluid'> <div class='container-fluid'>
@ -46,27 +21,32 @@
{% endblock %} {% endblock %}
{% block js_load %} {% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script> <script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %} {% endblock %}
{% block js_ready %} {% 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); var button = $(this);
launchDeleteForm("#modal-delete", launchDeleteForm("#modal-delete",
button.attr('url'), 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); var button = $(this);
launchModalForm("#modal-form", 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 %} {% endblock %}

View File

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

View File

@ -1,24 +1,63 @@
<table class="table table-striped" data-sorting='true' data-filtering='true' id="part-list"> $("#part-table").bootstrapTable({
<thead> sortable: true,
<tr> search: true,
<th>Part</th> sortName: 'description',
<th>Description</th> idField: 'pk',
<th>Category</th> method: 'get',
<th data-type='number'>Stock</th> pagination: true,
</tr> rememberOrder: true,
</thead> {% if category %}
<tbody> queryParams: function(p) {
{% for part in parts %} return {
<tr> category: {{ category.id }},
<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>
{% endif %} {% endif %}
</td> columns: [
<td><a href="{% url 'part-stock' part.id %}">{{ part.total_stock }}</a></td> {
</tr> checkbox: true,
{% endfor %} title: 'Select',
</tbody> searchable: false,
</table> },
{
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 %} {% include "part/category_subcategories.html" with children=children %}
{% endif %} {% endif %}
<h4>Parts</h4> <table class='table table-striped table-condensed' id='part-table'>
{% include "part/category_parts.html" with parts=parts %}
<div class='container-fluid'> <div class='container-fluid'>
<button type='button' class='btn btn-primary' id='create-cat'> <button type='button' class='btn btn-primary' id='create-cat'>
@ -29,11 +28,9 @@
{% block js_load %} {% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script> <script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}
$('#part-list').footable();
$("#create-cat").click(function() { $("#create-cat").click(function() {
launchModalForm("#modal-form", launchModalForm("#modal-form",
@ -73,6 +70,8 @@
launchModalForm("#modal-form", "{% url 'part-create' %}"); launchModalForm("#modal-form", "{% url 'part-create' %}");
}); });
{% include "part/category_parts.html" %}
loadTree(); loadTree();
{% endblock %} {% endblock %}

View File

@ -6,50 +6,8 @@
<h3>Part Stock</h3> <h3>Part Stock</h3>
<table class="table table-striped" id='stock-table' data-sorting='true'> <table class='table table-striped table-condensed' id='stock-table'>
<thead> </table>
<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
<div class='container-fluid'> <div class='container-fluid'>
<button class='btn btn-success' id='add-stock-item'>Add new Stock Item</button> <button class='btn btn-success' id='add-stock-item'>Add new Stock Item</button>
@ -59,11 +17,9 @@
{% block js_load %} {% block js_load %}
{{ block.super }} {{ block.super }}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}
{{ block.super }} {{ block.super }}
$('#stock-table').footable();
$('#add-stock-item').click(function () { $('#add-stock-item').click(function () {
launchModalForm("#modal-form", 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 %} {% endblock %}

View File

@ -6,30 +6,14 @@
<h3>Part Suppliers</h3> <h3>Part Suppliers</h3>
<table class="table table-striped" id='supplier-table' data-sorting='true'> {% if part.supplier_count > 0 %}
<thead> <p><b>{{ part.name }}</b> is available from {{ part.supplier_count }} suppliers.</p>
<tr>
<th>SKU</th> <table class="table table-striped table-condensed" id='supplier-table'>
<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>
</table> </table>
{% else %}
<p><b>{{ part.name }}</b> is not available from any suppliers.</p>
{% endif %}
<div class='container-fluid'> <div class='container-fluid'>
<button class="btn btn-success" id='supplier-create'>New Supplier Part</button> <button class="btn btn-success" id='supplier-create'>New Supplier Part</button>
@ -39,11 +23,9 @@
{% block js_load %} {% block js_load %}
{{ block.super }} {{ block.super }}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>``
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}
{{ block.super }} {{ block.super }}
$('#supplier-table').footable();
$('#supplier-create').click(function () { $('#supplier-create').click(function () {
launchModalForm("#modal-form", launchModalForm("#modal-form",
@ -53,4 +35,39 @@
data: {part: {{ part.id }} } 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 %} {% endblock %}

View File

@ -6,19 +6,58 @@
<h3>Used In</h3> <h3>Used In</h3>
<table class="table table-striped"> {% if part.used_in_count > 0 %}
<tr>
<th>Part</th> <p>
<th>Uses</th> <b>{{ part.name }}</b> is used to make {{ part.used_in_count }} other parts.
<th>Description</th> </p>
</tr>
{% for item in part.used_in.all %} <table class="table table-striped table-condensed" id='used-table'>
<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 %}
</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 %} {% 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 .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): class StockItemSerializer(serializers.ModelSerializer):
""" Serializer for a StockItem """ 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: class Meta:
model = StockItem model = StockItem
@ -16,17 +36,17 @@ class StockItemSerializer(serializers.ModelSerializer):
'supplier_part', 'supplier_part',
'location', 'location',
'in_stock', 'in_stock',
'belongs_to', #'belongs_to',
'customer', #'customer',
'quantity', 'quantity',
'serial', 'serial',
'batch', 'batch',
'status', 'status',
'notes', 'notes',
'updated', #'updated',
'stocktake_date', #'stocktake_date',
'stocktake_user', #'stocktake_user',
'review_needed', #'review_needed',
] ]
""" These fields are read-only in this context. """ These fields are read-only in this context.

View File

@ -9,35 +9,7 @@
{% include "stock/location_list.html" with locations=locations %} {% include "stock/location_list.html" with locations=locations %}
{% endif %} {% endif %}
<table class="table table-striped" id='stock-table' data-filtering='true' data-sorting='true'> <table class="table table-striped" id='stock-table'>
<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> </table>
<div class='container-fluid'> <div class='container-fluid'>
@ -49,12 +21,9 @@
{% endblock %} {% endblock %}
{% block js_load %} {% block js_load %}
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script> <script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}
$('#stock-table').footable();
$('#location-create').click(function () { $('#location-create').click(function () {
launchModalForm("#modal-form", launchModalForm("#modal-form",
@ -63,4 +32,7 @@
follow: true follow: true
}); });
}); });
{% include "stock/stock_table.html" %}
{% endblock %} {% endblock %}

View File

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

View File

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

View File

@ -1,22 +1,60 @@
<table class="table table-striped" id='stock-table' data-filtering='true' data-sorting='true'> $("#stock-table").bootstrapTable({
<thead> sortable: true,
<tr> search: true,
<th>Part</th> method: 'get',
<th data-type='number'>Stock</th> pagination: true,
<th>Status</th> rememberOrder: true,
<th>Stocktake</th> {% if location %}
<th data-sortable='false'></th> queryParams: function(p) {
</tr> return {
</thead> location: {{ location.id }}
<tbody> }
{% for item in items.all %} },
<tr> {% endif %}
<td><a href="{% url 'part-stock' item.part.id %}">{{ item.part.name }}</a></td> columns: [
<td>{{ item.quantity }}</td> {
<td>{{ item.get_status_display }}</td> checkbox: true,
<td>{{ item.stocktake_date }}</td> title: 'Select',
<td><a href="{% url 'stock-item-detail' item.id %}">Click</a></td> searchable: false,
</tr> },
{% endfor %} {
</tbody> field: 'pk',
</table> 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 --> <!-- 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/inventree.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.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 %} {% block css %}
{% endblock %} {% endblock %}
@ -47,6 +48,12 @@ InvenTree
<script type="text/javascript" src="{% static 'script/bootstrap.min.js' %}"></script> <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/select2/select2.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap-treeview.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/trees.js' %}"></script>
<script type='text/javascript' src="{% static 'script/sidenav.js' %}"></script> <script type='text/javascript' src="{% static 'script/sidenav.js' %}"></script>
<script type='text/javascript' src="{% static 'script/notification.js' %}"></script> <script type='text/javascript' src="{% static 'script/notification.js' %}"></script>