mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
commit
ba72ced00c
@ -23,7 +23,10 @@ def DownloadFile(data, filename, content_type='application/text'):
|
||||
|
||||
filename = WrapWithQuotes(filename)
|
||||
|
||||
wrapper = FileWrapper(io.StringIO(data))
|
||||
if type(data) == str:
|
||||
wrapper = FileWrapper(io.StringIO(data))
|
||||
else:
|
||||
wrapper = FileWrapper(io.BytesIO(data))
|
||||
|
||||
response = StreamingHttpResponse(wrapper, content_type=content_type)
|
||||
response['Content-Length'] = len(data)
|
||||
|
@ -57,7 +57,6 @@ INSTALLED_APPS = [
|
||||
# Third part add-ons
|
||||
'django_filters',
|
||||
'rest_framework',
|
||||
'simple_history',
|
||||
'crispy_forms',
|
||||
'import_export',
|
||||
'django_cleanup',
|
||||
@ -72,7 +71,6 @@ MIDDLEWARE = [
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'simple_history.middleware.HistoryRequestMiddleware',
|
||||
'InvenTree.middleware.AuthRequiredMiddleware'
|
||||
]
|
||||
|
||||
|
@ -12,7 +12,7 @@ class PartAdmin(ImportExportModelAdmin):
|
||||
list_display = ('name', 'IPN', 'description', 'total_stock', 'category')
|
||||
|
||||
|
||||
class PartCategoryAdmin(admin.ModelAdmin):
|
||||
class PartCategoryAdmin(ImportExportModelAdmin):
|
||||
|
||||
list_display = ('name', 'pathstring', 'description')
|
||||
|
||||
|
@ -87,13 +87,12 @@ class PartList(generics.ListCreateAPIView):
|
||||
childs = category.getUniqueChildren()
|
||||
for child in childs:
|
||||
# Ignore the top-level category (already filtered)
|
||||
if child == cat_id:
|
||||
if str(child) == str(cat_id):
|
||||
continue
|
||||
flt |= Q(category=child)
|
||||
|
||||
parts_list = parts_list.filter(flt)
|
||||
|
||||
# Default - return all parts
|
||||
return parts_list
|
||||
|
||||
permission_classes = [
|
||||
|
@ -25,74 +25,82 @@
|
||||
|
||||
<hr>
|
||||
|
||||
<table class='table table-striped'>
|
||||
<tr>
|
||||
<td>Part name</td>
|
||||
<td>{{ part.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ part.description }}</td>
|
||||
</tr>
|
||||
{% if part.IPN %}
|
||||
<tr>
|
||||
<td>IPN</td>
|
||||
<td>{{ part.IPN }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>Category</td>
|
||||
<td>
|
||||
{% if part.category %}
|
||||
<a href="{% url 'category-detail' part.category.id %}">{{ part.category.pathstring }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if part.default_location %}
|
||||
<tr>
|
||||
<td>Default Location</td>
|
||||
<td><a href="{% url 'stock-location-detail' part.default_location.id %}">{{ part.default_location.pathstring }}</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if part.default_supplier %}
|
||||
<tr>
|
||||
<td>Default Supplier</td>
|
||||
<td><a href="{% url 'supplier-part-detail' part.default_supplier.id %}">
|
||||
{{ part.default_supplier.supplier.name }} | {{ part.default_supplier.SKU }}
|
||||
</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>Units</td>
|
||||
<td>{{ part.units }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Buildable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.buildable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Consumable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.consumable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Trackable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.trackable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Purchaseable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.purchaseable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Salable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.salable %}</td>
|
||||
</tr>
|
||||
{% if part.minimum_stock > 0 %}
|
||||
<tr>
|
||||
<td>Minimum Stock</td>
|
||||
<td>{{ part.minimum_stock }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
<div class='row'>
|
||||
<div class='col-sm-6'>
|
||||
<table class='table table-striped'>
|
||||
<tr>
|
||||
<td>Part name</td>
|
||||
<td>{{ part.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td>{{ part.description }}</td>
|
||||
</tr>
|
||||
{% if part.IPN %}
|
||||
<tr>
|
||||
<td>IPN</td>
|
||||
<td>{{ part.IPN }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>Category</td>
|
||||
<td>
|
||||
{% if part.category %}
|
||||
<a href="{% url 'category-detail' part.category.id %}">{{ part.category.pathstring }}</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% if part.default_location %}
|
||||
<tr>
|
||||
<td>Default Location</td>
|
||||
<td><a href="{% url 'stock-location-detail' part.default_location.id %}">{{ part.default_location.pathstring }}</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if part.default_supplier %}
|
||||
<tr>
|
||||
<td>Default Supplier</td>
|
||||
<td><a href="{% url 'supplier-part-detail' part.default_supplier.id %}">
|
||||
{{ part.default_supplier.supplier.name }} | {{ part.default_supplier.SKU }}
|
||||
</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<td>Units</td>
|
||||
<td>{{ part.units }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
<table class='table table-striped'>
|
||||
<tr>
|
||||
<td>Buildable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.buildable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Consumable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.consumable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Trackable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.trackable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Purchaseable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.purchaseable %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Salable</td>
|
||||
<td>{% include "yesnolabel.html" with value=part.salable %}</td>
|
||||
</tr>
|
||||
{% if part.minimum_stock > 0 %}
|
||||
<tr>
|
||||
<td>Minimum Stock</td>
|
||||
<td>{{ part.minimum_stock }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if part.notes %}
|
||||
<div class="panel panel-default">
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 98 KiB |
@ -21,7 +21,10 @@ function downloadBom(options = {}) {
|
||||
<div class='controls'>
|
||||
<select id='bom-format' class='select'>
|
||||
<option value='csv'>CSV</option>
|
||||
<option value='xls'>XLSX</option>
|
||||
<option value='tsv'>TSV</option>
|
||||
<option value='xls'>XLS</option>
|
||||
<option value='xlsx'>XLSX</option>
|
||||
<option value='ods'>ODS</option>
|
||||
<option value='yaml'>YAML</option>
|
||||
<option value='json'>JSON</option>
|
||||
<option value='xml'>XML</option>
|
||||
@ -72,20 +75,15 @@ function loadBomTable(table, options) {
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
{
|
||||
checkbox: true,
|
||||
title: 'Select',
|
||||
searchable: false,
|
||||
sortable: false,
|
||||
},
|
||||
];
|
||||
|
||||
if (options.editable) {
|
||||
cols.push({
|
||||
formatter: function(value, row, index, field) {
|
||||
var bEdit = "<button class='btn btn-success bom-edit-button btn-sm' type='button' url='" + row.url + "edit'>Edit</button>";
|
||||
var bDelt = "<button class='btn btn-danger bom-delete-button btn-sm' type='button' url='" + row.url + "delete'>Delete</button>";
|
||||
|
||||
return "<div class='btn-group'>" + bEdit + bDelt + "</div>";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Part column
|
||||
cols.push(
|
||||
{
|
||||
@ -126,8 +124,17 @@ function loadBomTable(table, options) {
|
||||
}
|
||||
);
|
||||
|
||||
// If we are NOT editing, display the available stock
|
||||
if (!options.editable) {
|
||||
if (options.editable) {
|
||||
cols.push({
|
||||
formatter: function(value, row, index, field) {
|
||||
var bEdit = "<button class='btn btn-success bom-edit-button btn-sm' type='button' url='" + row.url + "edit'>Edit</button>";
|
||||
var bDelt = "<button class='btn btn-danger bom-delete-button btn-sm' type='button' url='" + row.url + "delete'>Delete</button>";
|
||||
|
||||
return "<div class='btn-group'>" + bEdit + bDelt + "</div>";
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
cols.push(
|
||||
{
|
||||
field: 'sub_part.available_stock',
|
||||
|
@ -1,19 +1,19 @@
|
||||
from django.contrib import admin
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from import_export.admin import ImportExportModelAdmin
|
||||
|
||||
from .models import StockLocation, StockItem
|
||||
from .models import StockItemTracking
|
||||
|
||||
|
||||
class LocationAdmin(admin.ModelAdmin):
|
||||
class LocationAdmin(ImportExportModelAdmin):
|
||||
list_display = ('name', 'pathstring', 'description')
|
||||
|
||||
|
||||
class StockItemAdmin(SimpleHistoryAdmin):
|
||||
class StockItemAdmin(ImportExportModelAdmin):
|
||||
list_display = ('part', 'quantity', 'location', 'status', 'updated')
|
||||
|
||||
|
||||
class StockTrackingAdmin(admin.ModelAdmin):
|
||||
class StockTrackingAdmin(ImportExportModelAdmin):
|
||||
list_display = ('item', 'date', 'title')
|
||||
|
||||
|
||||
|
@ -2,6 +2,8 @@ from django_filters.rest_framework import FilterSet, DjangoFilterBackend
|
||||
from django_filters import NumberFilter
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from .models import StockLocation, StockItem
|
||||
from .models import StockItemTracking
|
||||
@ -202,7 +204,36 @@ class StockList(generics.ListCreateAPIView):
|
||||
Create a new StockItem
|
||||
"""
|
||||
|
||||
queryset = StockItem.objects.all()
|
||||
def get_queryset(self):
|
||||
"""
|
||||
If the query includes a particular location,
|
||||
we may wish to also request stock items from all child locations.
|
||||
This is set by the optional param 'include_child_categories'
|
||||
"""
|
||||
|
||||
# Does the client wish to filter by category?
|
||||
loc_id = self.request.query_params.get('location', None)
|
||||
|
||||
# Start with all objects
|
||||
stock_list = StockItem.objects.all()
|
||||
|
||||
if loc_id:
|
||||
location = get_object_or_404(StockLocation, pk=loc_id)
|
||||
|
||||
# Filter by the supplied category
|
||||
flt = Q(location=loc_id)
|
||||
|
||||
if self.request.query_params.get('include_child_locations', None):
|
||||
childs = location.getUniqueChildren()
|
||||
for child in childs:
|
||||
# Ignore the top-level category (already filtered!)
|
||||
if str(child) == str(loc_id):
|
||||
continue
|
||||
flt |= Q(location=child)
|
||||
|
||||
stock_list = stock_list.filter(flt)
|
||||
|
||||
return stock_list
|
||||
|
||||
serializer_class = StockItemSerializer
|
||||
|
||||
@ -219,7 +250,6 @@ class StockList(generics.ListCreateAPIView):
|
||||
filter_fields = [
|
||||
'part',
|
||||
'uuid',
|
||||
'location',
|
||||
'supplier_part',
|
||||
'customer',
|
||||
'belongs_to',
|
||||
|
@ -160,7 +160,8 @@
|
||||
loadStockTable($("#stock-table"), {
|
||||
params: {
|
||||
{% if location %}
|
||||
location: {{ location.id }}
|
||||
location: {{ location.id }},
|
||||
include_child_locations: true,
|
||||
{% endif %}
|
||||
},
|
||||
url: "{% url 'api-stock-list' %}",
|
||||
|
@ -3,7 +3,7 @@
|
||||
<nav class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container-fluid">
|
||||
<div class="navbar-header clearfix content-heading">
|
||||
<a class="navbar-brand" id='logo' href="{% url 'index' %}"><img src="{% static 'img/inventree.png' %}" width="40" height="40"/></a>
|
||||
<a class="navbar-brand" id='logo' href="{% url 'index' %}" style="padding-top: 7px; padding-bottom: 5px;"><img src="{% static 'img/inventree.png' %}" width="40" height="40" style="display:block; margin: auto;"/></a>
|
||||
</div>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{% url 'part-index' %}">Parts</a></li>
|
||||
|
@ -10,7 +10,7 @@
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
sodipodi:docname="inventree.svg"
|
||||
inkscape:version="0.91 r13725"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
version="1.1"
|
||||
id="svg2"
|
||||
viewBox="0 0 400 400.00001"
|
||||
@ -21,16 +21,16 @@
|
||||
inkscape:export-ydpi="115.2">
|
||||
<sodipodi:namedview
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-x="65"
|
||||
inkscape:window-height="1056"
|
||||
inkscape:window-width="1855"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-height="1137"
|
||||
inkscape:window-width="1920"
|
||||
showgrid="false"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:cy="237.03857"
|
||||
inkscape:cx="195.55918"
|
||||
inkscape:zoom="1.4"
|
||||
inkscape:cy="521.06718"
|
||||
inkscape:cx="-180.35636"
|
||||
inkscape:zoom="0.35"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
borderopacity="1.0"
|
||||
@ -90,7 +90,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@ -158,14 +158,14 @@
|
||||
inkscape:export-ydpi="299.91324" />
|
||||
<g
|
||||
id="text4273"
|
||||
style="font-style:normal;font-weight:normal;font-size:217.54554749px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;opacity:0.76999996;fill:#ffeeaa;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="font-style:normal;font-weight:normal;font-size:217.54554749px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;opacity:0.76999996;fill:#335d88;fill-opacity:1;stroke:#ede3d8;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="matrix(1.1055685,0.12077953,0.13042915,-1.1045144,260.24897,1649.2316)"
|
||||
inkscape:export-xdpi="299.91324"
|
||||
inkscape:export-ydpi="299.91324">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4577"
|
||||
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:217.55203247px;line-height:125%;font-family:Brussels;-inkscape-font-specification:'Brussels, Light';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffeeaa;fill-opacity:1;stroke:#000000;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
style="font-style:normal;font-variant:normal;font-weight:300;font-stretch:normal;font-size:217.55203247px;line-height:125%;font-family:Brussels;-inkscape-font-specification:'Brussels, Light';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#335d88;fill-opacity:1;stroke:#ede3d8;stroke-width:3;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m -117.40075,599.71964 c 5.27286,1.59179 8.93096,3.72644 11.01731,6.40882 2.16744,2.68089 3.19894,6.45951 3.10336,11.34732 0,0 -1.08272,55.36631 -1.08272,55.36631 -0.21906,11.20195 -7.38435,16.55674 -21.8761,16.0485 0,0 -2.92196,-0.10247 -2.92196,-0.10247 0,0 -0.15971,8.03102 -0.15971,8.03102 0,0 39.16281,5.47451 39.16281,5.47451 0,0 12.568242,0.24647 12.568242,0.24647 0,0 1.566329,-81.64215 1.566329,-81.64215 0.09195,-4.79265 1.142855,-8.19902 3.144966,-10.21177 1.990467,-1.93135 5.341631,-2.94441 10.01779,-3.04567 0,0 8.490412,-0.0219 8.490412,-0.0219 0,0 0.13793,-7.29971 0.13793,-7.29971 0,0 -72.942109,-11.42831 -72.942109,-11.42831 0,0 -0.15928,8.00932 -0.15928,8.00932 0,0 9.93273,2.8197 9.93273,2.8197 0,0 0,0 0,0 m 26.057646,150.04813 c 4.860402,-0.19831 8.905931,-1.73322 12.161092,-4.58865 3.313681,-2.83452 4.997535,-6.2059 5.072638,-10.13186 0.07509,-3.92554 -1.480873,-7.22278 -4.687942,-9.90881 -3.147235,-2.70919 -7.135665,-4.06169 -11.990765,-4.04062 -5.005803,0.0217 -9.283919,1.44206 -12.809479,4.27943 -3.55676,2.86247 -5.38718,6.35506 -5.46763,10.45908 -0.0805,4.10447 1.61494,7.51856 5.06366,10.22424 3.41899,2.68235 7.647131,3.91166 12.658426,3.70719 0,0 0,0 0,0" />
|
||||
</g>
|
||||
</g>
|
||||
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 8.2 KiB |
@ -3,7 +3,6 @@ psycopg2>=2.8.1
|
||||
pillow>=5.0.0
|
||||
djangorestframework>=3.6.2
|
||||
django_filter>=1.0.2
|
||||
django-simple-history>=1.8.2
|
||||
coreapi>=2.3.0
|
||||
pygments>=2.2.0
|
||||
tablib>=0.13.0
|
||||
|
Loading…
Reference in New Issue
Block a user