mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
c203c3542f
@ -328,11 +328,15 @@ class IndexView(TemplateView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
||||||
context = super(TemplateView, self).get_context_data(**kwargs)
|
context = super(TemplateView, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
context['starred'] = [star.part for star in self.request.user.starred_parts.all()]
|
||||||
|
|
||||||
# Generate a list of orderable parts which have stock below their minimum values
|
# Generate a list of orderable parts which have stock below their minimum values
|
||||||
|
# TODO - Is there a less expensive way to get these from the database
|
||||||
context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()]
|
context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()]
|
||||||
|
|
||||||
# Generate a list of buildable parts which have stock below their minimum values
|
# Generate a list of buildable parts which have stock below their minimum values
|
||||||
|
# TODO - Is there a less expensive way to get these from the database
|
||||||
context['to_build'] = [part for part in Part.objects.filter(buildable=True) if part.need_to_restock()]
|
context['to_build'] = [part for part in Part.objects.filter(buildable=True) if part.need_to_restock()]
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
@ -2,7 +2,7 @@ from django.contrib import admin
|
|||||||
from import_export.admin import ImportExportModelAdmin
|
from import_export.admin import ImportExportModelAdmin
|
||||||
|
|
||||||
from .models import PartCategory, Part
|
from .models import PartCategory, Part
|
||||||
from .models import PartAttachment
|
from .models import PartAttachment, PartStar
|
||||||
from .models import SupplierPart
|
from .models import SupplierPart
|
||||||
from .models import BomItem
|
from .models import BomItem
|
||||||
|
|
||||||
@ -22,6 +22,11 @@ class PartAttachmentAdmin(admin.ModelAdmin):
|
|||||||
list_display = ('part', 'attachment', 'comment')
|
list_display = ('part', 'attachment', 'comment')
|
||||||
|
|
||||||
|
|
||||||
|
class PartStarAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
list_display = ('part', 'user')
|
||||||
|
|
||||||
|
|
||||||
class BomItemAdmin(ImportExportModelAdmin):
|
class BomItemAdmin(ImportExportModelAdmin):
|
||||||
list_display = ('part', 'sub_part', 'quantity')
|
list_display = ('part', 'sub_part', 'quantity')
|
||||||
|
|
||||||
@ -42,5 +47,6 @@ class ParameterAdmin(admin.ModelAdmin):
|
|||||||
admin.site.register(Part, PartAdmin)
|
admin.site.register(Part, PartAdmin)
|
||||||
admin.site.register(PartCategory, PartCategoryAdmin)
|
admin.site.register(PartCategory, PartCategoryAdmin)
|
||||||
admin.site.register(PartAttachment, PartAttachmentAdmin)
|
admin.site.register(PartAttachment, PartAttachmentAdmin)
|
||||||
|
admin.site.register(PartStar, PartStarAdmin)
|
||||||
admin.site.register(BomItem, BomItemAdmin)
|
admin.site.register(BomItem, BomItemAdmin)
|
||||||
admin.site.register(SupplierPart, SupplierPartAdmin)
|
admin.site.register(SupplierPart, SupplierPartAdmin)
|
||||||
|
@ -6,18 +6,22 @@ Provides a JSON API for the Part app
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
|
||||||
|
from rest_framework import status
|
||||||
|
from rest_framework.response import Response
|
||||||
from rest_framework import filters
|
from rest_framework import filters
|
||||||
from rest_framework import generics, permissions
|
from rest_framework import generics, permissions
|
||||||
|
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.conf.urls import url, include
|
from django.conf.urls import url, include
|
||||||
|
|
||||||
from .models import Part, PartCategory, BomItem
|
from .models import Part, PartCategory, BomItem, PartStar
|
||||||
from .models import SupplierPart, SupplierPriceBreak
|
from .models import SupplierPart, SupplierPriceBreak
|
||||||
|
|
||||||
from .serializers import PartSerializer, BomItemSerializer
|
from .serializers import PartSerializer, BomItemSerializer
|
||||||
from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer
|
from .serializers import SupplierPartSerializer, SupplierPriceBreakSerializer
|
||||||
from .serializers import CategorySerializer
|
from .serializers import CategorySerializer
|
||||||
|
from .serializers import PartStarSerializer
|
||||||
|
|
||||||
from InvenTree.views import TreeSerializer
|
from InvenTree.views import TreeSerializer
|
||||||
|
|
||||||
@ -150,8 +154,57 @@ class PartList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PartStarDetail(generics.RetrieveDestroyAPIView):
|
||||||
|
""" API endpoint for viewing or removing a PartStar object """
|
||||||
|
|
||||||
|
queryset = PartStar.objects.all()
|
||||||
|
serializer_class = PartStarSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class PartStarList(generics.ListCreateAPIView):
|
||||||
|
""" API endpoint for accessing a list of PartStar objects.
|
||||||
|
|
||||||
|
- GET: Return list of PartStar objects
|
||||||
|
- POST: Create a new PartStar object
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = PartStar.objects.all()
|
||||||
|
serializer_class = PartStarSerializer
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
# Override the user field (with the logged-in user)
|
||||||
|
data = request.data.copy()
|
||||||
|
data['user'] = str(request.user.id)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
|
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
self.perform_create(serializer)
|
||||||
|
headers = self.get_success_headers(serializer.data)
|
||||||
|
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
permissions.IsAuthenticatedOrReadOnly,
|
||||||
|
]
|
||||||
|
|
||||||
|
filter_backends = [
|
||||||
|
DjangoFilterBackend,
|
||||||
|
filters.SearchFilter
|
||||||
|
]
|
||||||
|
|
||||||
|
filter_fields = [
|
||||||
|
'part',
|
||||||
|
'user',
|
||||||
|
]
|
||||||
|
|
||||||
|
search_fields = [
|
||||||
|
'partname'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class BomList(generics.ListCreateAPIView):
|
class BomList(generics.ListCreateAPIView):
|
||||||
""" API endpoing for accessing a list of BomItem objects
|
""" API endpoint for accessing a list of BomItem objects.
|
||||||
|
|
||||||
- GET: Return list of BomItem objects
|
- GET: Return list of BomItem objects
|
||||||
- POST: Create a new BomItem object
|
- POST: Create a new BomItem object
|
||||||
@ -267,12 +320,21 @@ supplier_part_api_urls = [
|
|||||||
url(r'^.*$', SupplierPartList.as_view(), name='api-part-supplier-list'),
|
url(r'^.*$', SupplierPartList.as_view(), name='api-part-supplier-list'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
part_star_api_urls = [
|
||||||
|
url(r'^(?P<pk>\d+)/?', PartStarDetail.as_view(), name='api-part-star-detail'),
|
||||||
|
|
||||||
|
# Catchall
|
||||||
|
url(r'^.*$', PartStarList.as_view(), name='api-part-star-list'),
|
||||||
|
]
|
||||||
|
|
||||||
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'^category/', include(cat_api_urls)),
|
url(r'^category/', include(cat_api_urls)),
|
||||||
url(r'^supplier/', include(supplier_part_api_urls)),
|
url(r'^supplier/', include(supplier_part_api_urls)),
|
||||||
|
|
||||||
|
url(r'^star/', include(part_star_api_urls)),
|
||||||
|
|
||||||
url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'),
|
url(r'^price-break/?', SupplierPriceBreakList.as_view(), name='api-part-supplier-price'),
|
||||||
|
|
||||||
url(r'^(?P<pk>\d+)/', PartDetail.as_view(), name='api-part-detail'),
|
url(r'^(?P<pk>\d+)/', PartDetail.as_view(), name='api-part-detail'),
|
||||||
|
24
InvenTree/part/migrations/0016_partstar.py
Normal file
24
InvenTree/part/migrations/0016_partstar.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 2.2 on 2019-05-04 22:45
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('part', '0015_partcategory_default_location'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PartStar',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_users', to='part.Part')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='starred_parts', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
19
InvenTree/part/migrations/0017_auto_20190505_0848.py
Normal file
19
InvenTree/part/migrations/0017_auto_20190505_0848.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 2.2 on 2019-05-04 22:48
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
('part', '0016_partstar'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='partstar',
|
||||||
|
unique_together={('part', 'user')},
|
||||||
|
),
|
||||||
|
]
|
@ -18,6 +18,7 @@ from django.urls import reverse
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.validators import MinValueValidator
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
from django.db.models.signals import pre_delete
|
from django.db.models.signals import pre_delete
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
|
||||||
@ -246,6 +247,15 @@ class Part(models.Model):
|
|||||||
|
|
||||||
return total
|
return total
|
||||||
|
|
||||||
|
def isStarredBy(self, user):
|
||||||
|
""" Return True if this part has been starred by a particular user """
|
||||||
|
|
||||||
|
try:
|
||||||
|
PartStar.objects.get(part=self, user=user)
|
||||||
|
return True
|
||||||
|
except PartStar.DoesNotExist:
|
||||||
|
return False
|
||||||
|
|
||||||
def need_to_restock(self):
|
def need_to_restock(self):
|
||||||
""" Return True if this part needs to be restocked
|
""" Return True if this part needs to be restocked
|
||||||
(either by purchasing or building).
|
(either by purchasing or building).
|
||||||
@ -427,6 +437,21 @@ class PartAttachment(models.Model):
|
|||||||
return os.path.basename(self.attachment.name)
|
return os.path.basename(self.attachment.name)
|
||||||
|
|
||||||
|
|
||||||
|
class PartStar(models.Model):
|
||||||
|
""" A PartStar object creates a relationship between a User and a Part.
|
||||||
|
|
||||||
|
It is used to designate a Part as 'starred' (or favourited) for a given User,
|
||||||
|
so that the user can track a list of their favourite parts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='starred_users')
|
||||||
|
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='starred_parts')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
unique_together = ['part', 'user']
|
||||||
|
|
||||||
|
|
||||||
class BomItem(models.Model):
|
class BomItem(models.Model):
|
||||||
""" A BomItem links a part to its component items.
|
""" A BomItem links a part to its component items.
|
||||||
A part can have a BOM (bill of materials) which defines
|
A part can have a BOM (bill of materials) which defines
|
||||||
|
@ -4,8 +4,10 @@ JSON serializers for Part app
|
|||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .models import Part, PartCategory, BomItem
|
from .models import Part, PartStar
|
||||||
from .models import SupplierPart, SupplierPriceBreak
|
from .models import SupplierPart, SupplierPriceBreak
|
||||||
|
from .models import PartCategory
|
||||||
|
from .models import BomItem
|
||||||
|
|
||||||
from InvenTree.serializers import InvenTreeModelSerializer
|
from InvenTree.serializers import InvenTreeModelSerializer
|
||||||
|
|
||||||
@ -75,6 +77,23 @@ class PartSerializer(serializers.ModelSerializer):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class PartStarSerializer(InvenTreeModelSerializer):
|
||||||
|
""" Serializer for a PartStar object """
|
||||||
|
|
||||||
|
partname = serializers.CharField(source='part.name', read_only=True)
|
||||||
|
username = serializers.CharField(source='user.username', read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = PartStar
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'part',
|
||||||
|
'partname',
|
||||||
|
'user',
|
||||||
|
'username',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class BomItemSerializer(InvenTreeModelSerializer):
|
class BomItemSerializer(InvenTreeModelSerializer):
|
||||||
""" Serializer for BomItem object """
|
""" Serializer for BomItem object """
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<li><a href="#" id='activate-part' title='Activate part'>Activate</a></li>
|
<li><a href="#" id='activate-part' title='Activate part'>Activate</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href='#' id='show-qr-code' title='Generate QR Code'>Show QR Code</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
@ -126,15 +125,6 @@
|
|||||||
|
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
$("#show-qr-code").click(function() {
|
|
||||||
launchModalForm(
|
|
||||||
"{% url 'part-qr' part.id %}",
|
|
||||||
{
|
|
||||||
no_post: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#duplicate-part").click(function() {
|
$("#duplicate-part").click(function() {
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
{% block sidenav %}
|
{% block sidenav %}
|
||||||
<div id='part-tree'></div>
|
<div id='part-tree'></div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -14,6 +16,11 @@
|
|||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block js_load %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script type='text/javascript' src="{% static 'script/inventree/part.js' %}"></script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
loadTree("{% url 'api-part-tree' %}",
|
loadTree("{% url 'api-part-tree' %}",
|
||||||
|
@ -21,20 +21,34 @@
|
|||||||
{% endif %}/>
|
{% endif %}/>
|
||||||
</div>
|
</div>
|
||||||
<div class="media-body">
|
<div class="media-body">
|
||||||
<h4>{{ part.name }}{% if part.active == False %} <i>- INACTIVE</i>{% endif %}</h4>
|
<h4>
|
||||||
|
{{ part.name }}
|
||||||
|
</h4>
|
||||||
<p><i>{{ part.description }}</i></p>
|
<p><i>{{ part.description }}</i></p>
|
||||||
{% if part.IPN %}
|
<p>
|
||||||
<tr>
|
<div class='btn-group'>
|
||||||
<td>IPN</td>
|
{% include "qr_button.html" %}
|
||||||
<td>{{ part.IPN }}</td>
|
<button type='button' class='btn btn-default btn-glyph' id='toggle-starred' title='Star this part'>
|
||||||
</tr>
|
<span id='part-star-icon' class='starred-part glyphicon {% if starred %}glyphicon-star{% else %}glyphicon-star-empty{% endif %}'/>
|
||||||
{% endif %}
|
</button>
|
||||||
{% if part.URL %}
|
</div>
|
||||||
<tr>
|
</p>
|
||||||
<td>URL</td>
|
<table class='table table-condensed'>
|
||||||
<td><a href="{{ part.URL }}">{{ part.URL }}</a></td>
|
{% if part.IPN %}
|
||||||
</tr>
|
<tr>
|
||||||
{% endif %}
|
<td>IPN</td>
|
||||||
|
<td>{{ part.IPN }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% if part.URL %}
|
||||||
|
<tr>
|
||||||
|
<td>URL</td>
|
||||||
|
<td><a href="{{ part.URL }}">{{ part.URL }}</a></td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -82,6 +96,26 @@
|
|||||||
|
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
$("#show-qr-code").click(function() {
|
||||||
|
launchModalForm(
|
||||||
|
"{% url 'part-qr' part.id %}",
|
||||||
|
{
|
||||||
|
no_post: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#toggle-starred").click(function() {
|
||||||
|
toggleStar({
|
||||||
|
part: {{ part.id }},
|
||||||
|
user: {{ user.id }},
|
||||||
|
button: '#part-star-icon'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#toggle-starred').click(function() {
|
||||||
|
});
|
||||||
|
|
||||||
$("#part-thumb").click(function() {
|
$("#part-thumb").click(function() {
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
"{% url 'part-image' part.id %}",
|
"{% url 'part-image' part.id %}",
|
||||||
|
@ -232,6 +232,10 @@ class PartDetail(DetailView):
|
|||||||
else:
|
else:
|
||||||
context['editing_enabled'] = 0
|
context['editing_enabled'] = 0
|
||||||
|
|
||||||
|
part = self.get_object()
|
||||||
|
|
||||||
|
context['starred'] = part.isStarredBy(self.request.user)
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,6 +2,21 @@
|
|||||||
float: left;
|
float: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.glyphicon {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.starred-part {
|
||||||
|
color: #ffcc00;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-glyph {
|
||||||
|
padding-left: 6px;
|
||||||
|
padding-right: 6px;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
float: right;
|
float: right;
|
||||||
background-color: #777;
|
background-color: #777;
|
||||||
|
@ -43,9 +43,6 @@ function inventreeGet(url, filters={}, options={}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function inventreeUpdate(url, data={}, options={}) {
|
function inventreeUpdate(url, data={}, options={}) {
|
||||||
if ('final' in options && options.final) {
|
|
||||||
data["_is_final"] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var method = options.method || 'PUT';
|
var method = options.method || 'PUT';
|
||||||
|
|
||||||
@ -63,8 +60,7 @@ function inventreeUpdate(url, data={}, options={}) {
|
|||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
contentType: 'application/json',
|
contentType: 'application/json',
|
||||||
success: function(response, status) {
|
success: function(response, status) {
|
||||||
response['_status_code'] = status;
|
console.log(method + ' - ' + url + ' : result = ' + status);
|
||||||
console.log('UPDATE object to ' + url + ' - result = ' + status);
|
|
||||||
if (options.success) {
|
if (options.success) {
|
||||||
options.success(response, status);
|
options.success(response, status);
|
||||||
}
|
}
|
||||||
|
@ -16,4 +16,60 @@ function getPartList(filters={}, options={}) {
|
|||||||
|
|
||||||
function getBomList(filters={}, options={}) {
|
function getBomList(filters={}, options={}) {
|
||||||
return inventreeGet('/api/bom/', filters, options);
|
return inventreeGet('/api/bom/', filters, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleStar(options) {
|
||||||
|
/* Toggle the 'starred' status of a part.
|
||||||
|
* Performs AJAX queries and updates the display on the button.
|
||||||
|
*
|
||||||
|
* options:
|
||||||
|
* - button: ID of the button (default = '#part-star-icon')
|
||||||
|
* - part: pk of the part object
|
||||||
|
* - user: pk of the user
|
||||||
|
*/
|
||||||
|
|
||||||
|
var url = '/api/part/star/';
|
||||||
|
|
||||||
|
inventreeGet(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
part: options.part,
|
||||||
|
user: options.user,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
success: function(response) {
|
||||||
|
if (response.length == 0) {
|
||||||
|
// Zero length response = star does not exist
|
||||||
|
// So let's add one!
|
||||||
|
inventreeUpdate(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
part: options.part,
|
||||||
|
user: options.user,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
success: function(response, status) {
|
||||||
|
$(options.button).removeClass('glyphicon-star-empty').addClass('glyphicon-star');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var pk = response[0].pk;
|
||||||
|
// There IS a star (delete it!)
|
||||||
|
inventreeUpdate(
|
||||||
|
url + pk + "/",
|
||||||
|
{
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'DELETE',
|
||||||
|
success: function(response, status) {
|
||||||
|
$(options.button).removeClass('glyphicon-star').addClass('glyphicon-star-empty');
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
@ -6,6 +6,11 @@
|
|||||||
<div class='col-sm-6'>
|
<div class='col-sm-6'>
|
||||||
<h3>Stock Item Details</h3>
|
<h3>Stock Item Details</h3>
|
||||||
<p><i>{{ item.quantity }} × {{ item.part.name }}</i></p>
|
<p><i>{{ item.quantity }} × {{ item.part.name }}</i></p>
|
||||||
|
<p>
|
||||||
|
<div class='btn-group'>
|
||||||
|
{% include "qr_button.html" %}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class='col-sm-6'>
|
<div class='col-sm-6'>
|
||||||
<h3>
|
<h3>
|
||||||
@ -23,9 +28,6 @@
|
|||||||
<li><a href='#' id='stock-stocktake' title='Count stock'>Stocktake</a></li>
|
<li><a href='#' id='stock-stocktake' title='Count stock'>Stocktake</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<li><a href="#" id='stock-delete' title='Delete stock item'>Delete stock item</a></li>
|
<li><a href="#" id='stock-delete' title='Delete stock item'>Delete stock item</a></li>
|
||||||
<hr>
|
|
||||||
<li><a href="#" id='item-qr-code' title='Generate QR code'>Show QR code</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</h3>
|
</h3>
|
||||||
@ -145,7 +147,7 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#item-qr-code").click(function() {
|
$("#show-qr-code").click(function() {
|
||||||
launchModalForm("{% url 'stock-item-qr' item.id %}",
|
launchModalForm("{% url 'stock-item-qr' item.id %}",
|
||||||
{
|
{
|
||||||
no_post: true,
|
no_post: true,
|
||||||
|
@ -7,6 +7,11 @@
|
|||||||
{% if location %}
|
{% if location %}
|
||||||
<h3>{{ location.name }}</h3>
|
<h3>{{ location.name }}</h3>
|
||||||
<p>{{ location.description }}</p>
|
<p>{{ location.description }}</p>
|
||||||
|
<p>
|
||||||
|
<div class='btn-group'>
|
||||||
|
{% include "qr_button.html" %}
|
||||||
|
</div>
|
||||||
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<h3>Stock</h3>
|
<h3>Stock</h3>
|
||||||
<p>All stock items</p>
|
<p>All stock items</p>
|
||||||
@ -23,8 +28,6 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="#" id='location-edit' title='Edit stock location'>Edit</a></li>
|
<li><a href="#" id='location-edit' title='Edit stock location'>Edit</a></li>
|
||||||
<li><a href="#" id='location-delete' title='Delete stock location'>Delete</a></li>
|
<li><a href="#" id='location-delete' title='Delete stock location'>Delete</a></li>
|
||||||
<hr>
|
|
||||||
<li><a href="#" id='location-qr-code' title='Generate QR code'>Show QR code</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -101,7 +104,7 @@
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#location-qr-code').click(function() {
|
$('#show-qr-code').click(function() {
|
||||||
launchModalForm("{% url 'stock-location-qr' location.id %}",
|
launchModalForm("{% url 'stock-location-qr' location.id %}",
|
||||||
{
|
{
|
||||||
no_post: true,
|
no_post: true,
|
||||||
|
@ -3,6 +3,8 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>InvenTree</h3>
|
<h3>InvenTree</h3>
|
||||||
|
|
||||||
|
{% include "InvenTree/starred_parts.html" with collapse_id="starred" %}
|
||||||
|
|
||||||
{% if to_order %}
|
{% if to_order %}
|
||||||
{% include "InvenTree/parts_to_order.html" with collapse_id="order" %}
|
{% include "InvenTree/parts_to_order.html" with collapse_id="order" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -19,4 +21,9 @@
|
|||||||
|
|
||||||
{% block js_ready %}
|
{% block js_ready %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
|
$("#to-build-table").bootstrapTable();
|
||||||
|
$("#to-order-table").bootstrapTable();
|
||||||
|
$("#starred-parts-table").bootstrapTable();
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -1,5 +1,6 @@
|
|||||||
{% extends "collapse.html" %}
|
{% extends "collapse.html" %}
|
||||||
{% block collapse_title %}
|
{% block collapse_title %}
|
||||||
|
<span class='glyphicon glyphicon-wrench'></span>
|
||||||
Parts to Build<span class='badge'>{{ to_build | length }}</span>
|
Parts to Build<span class='badge'>{{ to_build | length }}</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
{% extends "collapse.html" %}
|
{% extends "collapse.html" %}
|
||||||
{% block collapse_title %}
|
{% block collapse_title %}
|
||||||
|
<span class='glyphicon glyphicon-shopping-cart'></span>
|
||||||
Parts to Order<span class='badge'>{{ to_order | length }}</span>
|
Parts to Order<span class='badge'>{{ to_order | length }}</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
15
InvenTree/templates/InvenTree/starred_parts.html
Normal file
15
InvenTree/templates/InvenTree/starred_parts.html
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{% extends "collapse.html" %}
|
||||||
|
{% block collapse_title %}
|
||||||
|
<span class='glyphicon glyphicon-star'></span>
|
||||||
|
Starred Parts<span class='badge'>{{ starred | length }}</span>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block collapse_heading %}
|
||||||
|
You have {{ starred | length }} favourite parts
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block collapse_content %}
|
||||||
|
|
||||||
|
{% include "required_part_table.html" with parts=starred table_id="starred-parts-table" %}
|
||||||
|
|
||||||
|
{% endblock %}
|
1
InvenTree/templates/qr_button.html
Normal file
1
InvenTree/templates/qr_button.html
Normal file
@ -0,0 +1 @@
|
|||||||
|
<button type='button' class='btn btn-default btn-glyph' id='show-qr-code' title='Show QR code'><span class='glyphicon glyphicon-qrcode'></span></button>
|
Loading…
Reference in New Issue
Block a user