diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index acfdad80fc..bbac3a9b03 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -38,7 +38,7 @@ class InvenTreeTree(models.Model): abstract = True 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) parent = models.ForeignKey('self', on_delete=models.CASCADE, @@ -126,10 +126,11 @@ class InvenTreeTree(models.Model): @property def path(self): - if self.parent: - return "/".join([p.name for p in self.parentpath]) + "/" + self.name - else: - return self.name + return self.parentpath + [self] + + @property + def pathstring(self): + return '/'.join([item.name for item in self.path]) def __setattr__(self, attrname, val): """ Custom Attribute Setting function @@ -174,7 +175,7 @@ class InvenTreeTree(models.Model): This is recursive - Make it not so. """ - return self.path + return self.pathstring def FilterChildren(queryset, parent): diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 7038fcbb19..2917ca1baf 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -3,7 +3,8 @@ from django.contrib import admin 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 stock.urls import stock_urls, stock_loc_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 . import views + admin.site.site_header = "InvenTree Admin" apipatterns = [ @@ -22,8 +25,8 @@ apipatterns = [ url(r'^stock-location/', include(stock_loc_urls)), # Part URLs - url(r'^part/', include(part_urls)), - url(r'^part-category/', include(part_cat_urls)), + url(r'^part/', include(part_api_urls)), + url(r'^part-category/', include(part_cat_api_urls)), #url(r'^part-param/', include(part_param_urls)), #url(r'^part-param-template/', include(part_param_template_urls)), @@ -52,9 +55,12 @@ apipatterns = [ ] urlpatterns = [ + # API URL url(r'^api/', include(apipatterns)), + url(r'^part/', include(part_urls)), + url(r'^api-doc/', include_docs_urls(title='InvenTree API')), url(r'^admin/', admin.site.urls), diff --git a/InvenTree/bom/serializers.py b/InvenTree/bom/serializers.py index 925f0f05d4..adabb99fc6 100644 --- a/InvenTree/bom/serializers.py +++ b/InvenTree/bom/serializers.py @@ -3,7 +3,7 @@ from rest_framework import serializers from .models import BomItem -class BomItemSerializer(serializers.HyperlinkedModelSerializer): +class BomItemSerializer(serializers.ModelSerializer): class Meta: model = BomItem diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 10b5b0d415..70a4728776 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -4,12 +4,12 @@ from .models import PartCategory, Part class PartAdmin(admin.ModelAdmin): - list_display = ('name', 'IPN', 'stock', 'category') + list_display = ('name', 'IPN', 'description', 'stock', 'category') class PartCategoryAdmin(admin.ModelAdmin): - list_display = ('name', 'path', 'description') + list_display = ('name', 'pathstring', 'description') """ class ParameterTemplateAdmin(admin.ModelAdmin): diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 3ca4ebab0c..866dc18f86 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -36,7 +36,7 @@ class Part(models.Model): IPN = models.CharField(max_length=100, blank=True) # 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_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)]) @@ -75,11 +75,21 @@ class Part(models.Model): result = stocks.aggregate(total=Sum('quantity')) return result['total'] + @property + def bomItemCount(self): + return self.bom_items.all().count() + + + @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. + " 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_parts = self.projectpart_set.all() @@ -92,6 +102,6 @@ class Part(models.Model): projects.append(pp.project) return projects - + """ diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index d1ef66f3e2..34d8c98b8e 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -17,7 +17,8 @@ class PartParameterSerializer(serializers.HyperlinkedModelSerializer): 'units') """ -class PartSerializer(serializers.HyperlinkedModelSerializer): +#class PartSerializer(serializers.HyperlinkedModelSerializer): +class PartSerializer(serializers.ModelSerializer): """ Serializer for complete detail information of a part. Used when displaying all details of a single component. """ @@ -31,7 +32,8 @@ class PartSerializer(serializers.HyperlinkedModelSerializer): 'category', 'stock', 'units', - 'trackable') + 'trackable', + ) class PartCategorySerializer(serializers.HyperlinkedModelSerializer): @@ -42,7 +44,7 @@ class PartCategorySerializer(serializers.HyperlinkedModelSerializer): 'name', 'description', 'parent', - 'path') + 'pathstring') """ class PartTemplateSerializer(serializers.HyperlinkedModelSerializer): diff --git a/InvenTree/part/templates/cat_link.html b/InvenTree/part/templates/cat_link.html new file mode 100644 index 0000000000..aff374a4d7 --- /dev/null +++ b/InvenTree/part/templates/cat_link.html @@ -0,0 +1,7 @@ +
+All > +{% for path_item in category.parentpath %} +{{ path_item.name }} > +{% endfor %} +{{ category.name }} +
\ No newline at end of file diff --git a/InvenTree/part/templates/detail.html b/InvenTree/part/templates/detail.html new file mode 100644 index 0000000000..145f0a6982 --- /dev/null +++ b/InvenTree/part/templates/detail.html @@ -0,0 +1,30 @@ +

Part details for {{ part.name }}

+ +{% include "cat_link.html" with category=part.category %} + +
+Part name: {{ part.name }} +
+Description: {{ part.description }} +
+IPN: {% if part.IPN %}{{ part.IPN }}{% else %}N/A{% endif %} +
+Stock: {{ part.stock }} + +

+BOM items: {{ part.bomItemCount }}
+Used in {{ part.usedInCount }} other parts.
+ +

BOM

+ + +

Used to make

+ \ No newline at end of file diff --git a/InvenTree/part/templates/index.html b/InvenTree/part/templates/index.html new file mode 100644 index 0000000000..96796f017c --- /dev/null +++ b/InvenTree/part/templates/index.html @@ -0,0 +1,27 @@ +

Parts page!

+ +{% if category %} +{% include "cat_link.html" with category=category %} +

Child categories

+ +{% else %} +No category! +{% endif %} + +Here is a list of all the parts: + + + + +{% for part in parts %} + + + + +{% endfor %} +
{{ part.name }}{{ part.description }}
+ diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 9237aa8f51..2ce4d3784c 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -1,8 +1,12 @@ from django.conf.urls import url +from django.views.generic.base import RedirectView from . import views -part_cat_urls = [ +app_nam='part' + +# URL list for part category API +part_cat_api_urls = [ # Part category detail url(r'^(?P[0-9]+)/?$', views.PartCategoryDetail.as_view(), name='partcategory-detail'), @@ -12,7 +16,9 @@ part_cat_urls = [ url(r'^$', views.PartCategoryList.as_view()) ] -part_urls = [ + +# URL list for part API +part_api_urls = [ # Individual part url(r'^(?P[0-9]+)/?$', views.PartDetail.as_view(), name='part-detail'), @@ -22,6 +28,17 @@ part_urls = [ url(r'^$', views.PartList.as_view()), ] +# URL list for part web interface +part_urls = [ + # Individual + url(r'^(?P\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 = [ # Detail of a single part parameter diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 54dc39c1a2..7bfa0ea931 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1,15 +1,72 @@ 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 InvenTree.models import FilterChildren 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 PartCategorySerializer #from .serializers import PartParameterSerializer #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): """ diff --git a/InvenTree/project/admin.py b/InvenTree/project/admin.py index 9224385d9a..4d6ccb520a 100644 --- a/InvenTree/project/admin.py +++ b/InvenTree/project/admin.py @@ -4,7 +4,7 @@ from .models import ProjectCategory, Project, ProjectPart, ProjectRun class ProjectCategoryAdmin(admin.ModelAdmin): - list_display = ('name', 'path', 'description') + list_display = ('name', 'pathstring', 'description') class ProjectAdmin(admin.ModelAdmin): diff --git a/InvenTree/project/serializers.py b/InvenTree/project/serializers.py index 2088fc5f52..9186813457 100644 --- a/InvenTree/project/serializers.py +++ b/InvenTree/project/serializers.py @@ -32,7 +32,7 @@ class ProjectCategorySerializer(serializers.HyperlinkedModelSerializer): 'name', 'description', 'parent', - 'path') + 'pathstring') class ProjectRunSerializer(serializers.HyperlinkedModelSerializer): diff --git a/InvenTree/stock/admin.py b/InvenTree/stock/admin.py index c5f560f7f2..99d70ff9f4 100644 --- a/InvenTree/stock/admin.py +++ b/InvenTree/stock/admin.py @@ -5,7 +5,7 @@ from .models import StockLocation, StockItem class LocationAdmin(admin.ModelAdmin): - list_display = ('name', 'path', 'description') + list_display = ('name', 'pathstring', 'description') class StockItemAdmin(SimpleHistoryAdmin): diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 3502348298..423714eca2 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -48,4 +48,4 @@ class LocationSerializer(serializers.HyperlinkedModelSerializer): 'name', 'description', 'parent', - 'path') + 'pathstring')