mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
Begin to add views for part models
- List BOM items - Show category listing as linked items - Fix some pathing issues with InvenTreeTree class
This commit is contained in:
parent
77fe0dc542
commit
bd46f66d6b
@ -38,7 +38,7 @@ class InvenTreeTree(models.Model):
|
|||||||
abstract = True
|
abstract = True
|
||||||
unique_together = ('name', 'parent')
|
unique_together = ('name', 'parent')
|
||||||
|
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100, unique=True)
|
||||||
description = models.CharField(max_length=250, blank=True)
|
description = models.CharField(max_length=250, blank=True)
|
||||||
parent = models.ForeignKey('self',
|
parent = models.ForeignKey('self',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
@ -126,10 +126,11 @@ class InvenTreeTree(models.Model):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def path(self):
|
def path(self):
|
||||||
if self.parent:
|
return self.parentpath + [self]
|
||||||
return "/".join([p.name for p in self.parentpath]) + "/" + self.name
|
|
||||||
else:
|
@property
|
||||||
return self.name
|
def pathstring(self):
|
||||||
|
return '/'.join([item.name for item in self.path])
|
||||||
|
|
||||||
def __setattr__(self, attrname, val):
|
def __setattr__(self, attrname, val):
|
||||||
""" Custom Attribute Setting function
|
""" Custom Attribute Setting function
|
||||||
@ -174,7 +175,7 @@ class InvenTreeTree(models.Model):
|
|||||||
This is recursive - Make it not so.
|
This is recursive - Make it not so.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.path
|
return self.pathstring
|
||||||
|
|
||||||
|
|
||||||
def FilterChildren(queryset, parent):
|
def FilterChildren(queryset, parent):
|
||||||
|
@ -3,7 +3,8 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from rest_framework.documentation import include_docs_urls
|
from rest_framework.documentation import include_docs_urls
|
||||||
|
|
||||||
from part.urls import part_urls, part_cat_urls
|
from part.urls import part_api_urls, part_cat_api_urls
|
||||||
|
from part.urls import part_urls
|
||||||
from bom.urls import bom_urls
|
from bom.urls import bom_urls
|
||||||
from stock.urls import stock_urls, stock_loc_urls
|
from stock.urls import stock_urls, stock_loc_urls
|
||||||
from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_urls, supplier_urls
|
from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_urls, supplier_urls
|
||||||
@ -13,6 +14,8 @@ from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_
|
|||||||
|
|
||||||
from users.urls import user_urls
|
from users.urls import user_urls
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
admin.site.site_header = "InvenTree Admin"
|
admin.site.site_header = "InvenTree Admin"
|
||||||
|
|
||||||
apipatterns = [
|
apipatterns = [
|
||||||
@ -22,8 +25,8 @@ apipatterns = [
|
|||||||
url(r'^stock-location/', include(stock_loc_urls)),
|
url(r'^stock-location/', include(stock_loc_urls)),
|
||||||
|
|
||||||
# Part URLs
|
# Part URLs
|
||||||
url(r'^part/', include(part_urls)),
|
url(r'^part/', include(part_api_urls)),
|
||||||
url(r'^part-category/', include(part_cat_urls)),
|
url(r'^part-category/', include(part_cat_api_urls)),
|
||||||
#url(r'^part-param/', include(part_param_urls)),
|
#url(r'^part-param/', include(part_param_urls)),
|
||||||
#url(r'^part-param-template/', include(part_param_template_urls)),
|
#url(r'^part-param-template/', include(part_param_template_urls)),
|
||||||
|
|
||||||
@ -52,9 +55,12 @@ apipatterns = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|
||||||
# API URL
|
# API URL
|
||||||
url(r'^api/', include(apipatterns)),
|
url(r'^api/', include(apipatterns)),
|
||||||
|
|
||||||
|
url(r'^part/', include(part_urls)),
|
||||||
|
|
||||||
url(r'^api-doc/', include_docs_urls(title='InvenTree API')),
|
url(r'^api-doc/', include_docs_urls(title='InvenTree API')),
|
||||||
|
|
||||||
url(r'^admin/', admin.site.urls),
|
url(r'^admin/', admin.site.urls),
|
||||||
|
@ -3,7 +3,7 @@ from rest_framework import serializers
|
|||||||
from .models import BomItem
|
from .models import BomItem
|
||||||
|
|
||||||
|
|
||||||
class BomItemSerializer(serializers.HyperlinkedModelSerializer):
|
class BomItemSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = BomItem
|
model = BomItem
|
||||||
|
@ -4,12 +4,12 @@ from .models import PartCategory, Part
|
|||||||
|
|
||||||
class PartAdmin(admin.ModelAdmin):
|
class PartAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
list_display = ('name', 'IPN', 'stock', 'category')
|
list_display = ('name', 'IPN', 'description', 'stock', 'category')
|
||||||
|
|
||||||
|
|
||||||
class PartCategoryAdmin(admin.ModelAdmin):
|
class PartCategoryAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
list_display = ('name', 'path', 'description')
|
list_display = ('name', 'pathstring', 'description')
|
||||||
|
|
||||||
"""
|
"""
|
||||||
class ParameterTemplateAdmin(admin.ModelAdmin):
|
class ParameterTemplateAdmin(admin.ModelAdmin):
|
||||||
|
@ -36,7 +36,7 @@ class Part(models.Model):
|
|||||||
IPN = models.CharField(max_length=100, blank=True)
|
IPN = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
# Part category - all parts must be assigned to a category
|
# Part category - all parts must be assigned to a category
|
||||||
category = models.ForeignKey(PartCategory, on_delete=models.CASCADE)
|
category = models.ForeignKey(PartCategory, on_delete=models.CASCADE, related_name='parts')
|
||||||
|
|
||||||
# Minimum "allowed" stock level
|
# Minimum "allowed" stock level
|
||||||
minimum_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)])
|
minimum_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)])
|
||||||
@ -76,10 +76,20 @@ class Part(models.Model):
|
|||||||
return result['total']
|
return result['total']
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def projects(self):
|
def bomItemCount(self):
|
||||||
""" Return a list of unique projects that this part is associated with.
|
return self.bom_items.all().count()
|
||||||
A part may be used in zero or more projects.
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def usedInCount(self):
|
||||||
|
return self.used_in.all().count()
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@property
|
||||||
|
def projects(self):
|
||||||
|
" Return a list of unique projects that this part is associated with.
|
||||||
|
A part may be used in zero or more projects.
|
||||||
|
"
|
||||||
|
|
||||||
project_ids = set()
|
project_ids = set()
|
||||||
project_parts = self.projectpart_set.all()
|
project_parts = self.projectpart_set.all()
|
||||||
@ -92,6 +102,6 @@ class Part(models.Model):
|
|||||||
projects.append(pp.project)
|
projects.append(pp.project)
|
||||||
|
|
||||||
return projects
|
return projects
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ class PartParameterSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'units')
|
'units')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class PartSerializer(serializers.HyperlinkedModelSerializer):
|
#class PartSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
class PartSerializer(serializers.ModelSerializer):
|
||||||
""" Serializer for complete detail information of a part.
|
""" Serializer for complete detail information of a part.
|
||||||
Used when displaying all details of a single component.
|
Used when displaying all details of a single component.
|
||||||
"""
|
"""
|
||||||
@ -31,7 +32,8 @@ class PartSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'category',
|
'category',
|
||||||
'stock',
|
'stock',
|
||||||
'units',
|
'units',
|
||||||
'trackable')
|
'trackable',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PartCategorySerializer(serializers.HyperlinkedModelSerializer):
|
class PartCategorySerializer(serializers.HyperlinkedModelSerializer):
|
||||||
@ -42,7 +44,7 @@ class PartCategorySerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'parent',
|
'parent',
|
||||||
'path')
|
'pathstring')
|
||||||
|
|
||||||
"""
|
"""
|
||||||
class PartTemplateSerializer(serializers.HyperlinkedModelSerializer):
|
class PartTemplateSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
7
InvenTree/part/templates/cat_link.html
Normal file
7
InvenTree/part/templates/cat_link.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<div id="category-path" background="#99F">
|
||||||
|
<a href="/part/list/">All</a> >
|
||||||
|
{% for path_item in category.parentpath %}
|
||||||
|
<a href="/part/list/?category={{ path_item.id }}">{{ path_item.name }}</a> >
|
||||||
|
{% endfor %}
|
||||||
|
<a href="/part/list/?category={{ category.id }}">{{ category.name }}</a>
|
||||||
|
</div>
|
30
InvenTree/part/templates/detail.html
Normal file
30
InvenTree/part/templates/detail.html
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<h1>Part details for {{ part.name }}</h1>
|
||||||
|
|
||||||
|
{% include "cat_link.html" with category=part.category %}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
Part name: {{ part.name }}
|
||||||
|
<br>
|
||||||
|
Description: {{ part.description }}
|
||||||
|
<br>
|
||||||
|
IPN: {% if part.IPN %}{{ part.IPN }}{% else %}N/A{% endif %}
|
||||||
|
<br>
|
||||||
|
Stock: {{ part.stock }}
|
||||||
|
|
||||||
|
<br><br>
|
||||||
|
BOM items: {{ part.bomItemCount }}<br>
|
||||||
|
Used in {{ part.usedInCount }} other parts.<br>
|
||||||
|
|
||||||
|
<h2>BOM</h2>
|
||||||
|
<ul>
|
||||||
|
{% for bom in part.bom_items.all %}
|
||||||
|
<li><a href="{% url 'detail' bom.sub_part.id %}">{{ bom.sub_part.name }}</a> ({{ bom.quantity }})</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Used to make</h2>
|
||||||
|
<ul>
|
||||||
|
{% for p in part.used_in.all %}
|
||||||
|
<li><a href="{% url 'detail' p.part.id %}">{{ p.part.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
27
InvenTree/part/templates/index.html
Normal file
27
InvenTree/part/templates/index.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<h1>Parts page!</h1>
|
||||||
|
|
||||||
|
{% if category %}
|
||||||
|
{% include "cat_link.html" with category=category %}
|
||||||
|
<h3>Child categories</h3>
|
||||||
|
<ul>
|
||||||
|
{% for child in category.children.all %}
|
||||||
|
<li><a href="/part/list?category={{ child.id }}">{{ child.name }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
No category!
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<b>Here is a list of all the parts:</b>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<table>
|
||||||
|
{% for part in parts %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{% url 'detail' part.id %}">{{ part.name }}</a></td>
|
||||||
|
<td>{{ part.description }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
|
@ -1,8 +1,12 @@
|
|||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
|
from django.views.generic.base import RedirectView
|
||||||
|
|
||||||
from . import views
|
from . import views
|
||||||
|
|
||||||
part_cat_urls = [
|
app_nam='part'
|
||||||
|
|
||||||
|
# URL list for part category API
|
||||||
|
part_cat_api_urls = [
|
||||||
|
|
||||||
# Part category detail
|
# Part category detail
|
||||||
url(r'^(?P<pk>[0-9]+)/?$', views.PartCategoryDetail.as_view(), name='partcategory-detail'),
|
url(r'^(?P<pk>[0-9]+)/?$', views.PartCategoryDetail.as_view(), name='partcategory-detail'),
|
||||||
@ -12,7 +16,9 @@ part_cat_urls = [
|
|||||||
url(r'^$', views.PartCategoryList.as_view())
|
url(r'^$', views.PartCategoryList.as_view())
|
||||||
]
|
]
|
||||||
|
|
||||||
part_urls = [
|
|
||||||
|
# URL list for part API
|
||||||
|
part_api_urls = [
|
||||||
|
|
||||||
# Individual part
|
# Individual part
|
||||||
url(r'^(?P<pk>[0-9]+)/?$', views.PartDetail.as_view(), name='part-detail'),
|
url(r'^(?P<pk>[0-9]+)/?$', views.PartDetail.as_view(), name='part-detail'),
|
||||||
@ -22,6 +28,17 @@ part_urls = [
|
|||||||
url(r'^$', views.PartList.as_view()),
|
url(r'^$', views.PartList.as_view()),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# URL list for part web interface
|
||||||
|
part_urls = [
|
||||||
|
# Individual
|
||||||
|
url(r'^(?P<pk>\d+)/$', views.detail, name='detail'),
|
||||||
|
# ex: /part/
|
||||||
|
url('list', views.index, name='index'),
|
||||||
|
# ex: /part/5/
|
||||||
|
|
||||||
|
url(r'^.*$', RedirectView.as_view(url='list', permanent=False), name='index'),
|
||||||
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
part_param_urls = [
|
part_param_urls = [
|
||||||
# Detail of a single part parameter
|
# Detail of a single part parameter
|
||||||
|
@ -1,15 +1,72 @@
|
|||||||
from django_filters.rest_framework import FilterSet, DjangoFilterBackend
|
from django_filters.rest_framework import FilterSet, DjangoFilterBackend
|
||||||
|
|
||||||
|
# Template stuff (WIP)
|
||||||
|
from django.http import HttpResponse
|
||||||
|
from django.template import loader
|
||||||
|
|
||||||
from rest_framework import generics, permissions
|
from rest_framework import generics, permissions
|
||||||
|
|
||||||
from InvenTree.models import FilterChildren
|
from InvenTree.models import FilterChildren
|
||||||
from .models import PartCategory, Part
|
from .models import PartCategory, Part
|
||||||
|
|
||||||
|
from django.shortcuts import get_object_or_404, render
|
||||||
|
from django.http import HttpResponseRedirect
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.views import generic
|
||||||
|
|
||||||
from .serializers import PartSerializer
|
from .serializers import PartSerializer
|
||||||
from .serializers import PartCategorySerializer
|
from .serializers import PartCategorySerializer
|
||||||
#from .serializers import PartParameterSerializer
|
#from .serializers import PartParameterSerializer
|
||||||
#from .serializers import PartTemplateSerializer
|
#from .serializers import PartTemplateSerializer
|
||||||
|
|
||||||
|
"""
|
||||||
|
class IndexView(generic.ListView):
|
||||||
|
template_name = 'index.html'
|
||||||
|
context_object_name = 'parts'
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
"Return the last five published questions."
|
||||||
|
return Part.objects.all()
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
template = loader.get_template('index.html')
|
||||||
|
|
||||||
|
parts = Part.objects.all()
|
||||||
|
|
||||||
|
cat = None
|
||||||
|
|
||||||
|
if 'category' in request.GET:
|
||||||
|
cat_id = request.GET['category']
|
||||||
|
|
||||||
|
cat = get_object_or_404(PartCategory, pk=cat_id)
|
||||||
|
#cat = PartCategory.objects.get(pk=cat_id)
|
||||||
|
parts = parts.filter(category = cat_id)
|
||||||
|
|
||||||
|
context = {
|
||||||
|
'parts' : parts.order_by('category__name'),
|
||||||
|
}
|
||||||
|
|
||||||
|
if cat:
|
||||||
|
context['category'] = cat
|
||||||
|
|
||||||
|
return HttpResponse(template.render(context, request))
|
||||||
|
|
||||||
|
|
||||||
|
def detail(request, pk):
|
||||||
|
#template = loader.get_template('detail.html')
|
||||||
|
|
||||||
|
part = get_object_or_404(Part, pk=pk)
|
||||||
|
|
||||||
|
return render(request, 'detail.html', {'part' : part})
|
||||||
|
|
||||||
|
#return HttpResponse("You're looking at part %s." % pk)
|
||||||
|
|
||||||
|
#def results(request, question_id):
|
||||||
|
# response = "You're looking at the results of question %s."
|
||||||
|
# return HttpResponse(response % question_id)
|
||||||
|
|
||||||
|
|
||||||
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""
|
"""
|
||||||
|
@ -4,7 +4,7 @@ from .models import ProjectCategory, Project, ProjectPart, ProjectRun
|
|||||||
|
|
||||||
|
|
||||||
class ProjectCategoryAdmin(admin.ModelAdmin):
|
class ProjectCategoryAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'path', 'description')
|
list_display = ('name', 'pathstring', 'description')
|
||||||
|
|
||||||
|
|
||||||
class ProjectAdmin(admin.ModelAdmin):
|
class ProjectAdmin(admin.ModelAdmin):
|
||||||
|
@ -32,7 +32,7 @@ class ProjectCategorySerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'parent',
|
'parent',
|
||||||
'path')
|
'pathstring')
|
||||||
|
|
||||||
|
|
||||||
class ProjectRunSerializer(serializers.HyperlinkedModelSerializer):
|
class ProjectRunSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
@ -5,7 +5,7 @@ from .models import StockLocation, StockItem
|
|||||||
|
|
||||||
|
|
||||||
class LocationAdmin(admin.ModelAdmin):
|
class LocationAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'path', 'description')
|
list_display = ('name', 'pathstring', 'description')
|
||||||
|
|
||||||
|
|
||||||
class StockItemAdmin(SimpleHistoryAdmin):
|
class StockItemAdmin(SimpleHistoryAdmin):
|
||||||
|
@ -48,4 +48,4 @@ class LocationSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'parent',
|
'parent',
|
||||||
'path')
|
'pathstring')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user