diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py
index 295937af08..2843dc4eb8 100644
--- a/InvenTree/InvenTree/helpers.py
+++ b/InvenTree/InvenTree/helpers.py
@@ -3,6 +3,8 @@ Provides helper functions used throughout the InvenTree project
"""
import io
+import json
+from datetime import datetime
from wsgiref.util import FileWrapper
from django.http import StreamingHttpResponse
@@ -44,6 +46,29 @@ def WrapWithQuotes(text, quote='"'):
return text
+def MakeBarcode(object_type, object_id, object_url, data={}):
+ """ Generate a string for a barcode. Adds some global InvenTree parameters.
+
+ Args:
+ object_type: string describing the object type e.g. 'StockItem'
+ object_id: ID (Primary Key) of the object in the database
+ object_url: url for JSON API detail view of the object
+ data: Python dict object containing extra datawhich will be rendered to string (must only contain stringable values)
+
+ Returns:
+ json string of the supplied data plus some other data
+ """
+
+ # Add in some generic InvenTree data
+ data['type'] = object_type
+ data['id'] = object_id
+ data['url'] = object_url
+ data['tool'] = 'InvenTree'
+ data['generated'] = str(datetime.now().date())
+
+ return json.dumps(data, sort_keys=True)
+
+
def DownloadFile(data, filename, content_type='application/text'):
""" Create a dynamic file for the user to download.
diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py
index 18267f33c4..9a388c5b2c 100644
--- a/InvenTree/InvenTree/views.py
+++ b/InvenTree/InvenTree/views.py
@@ -15,6 +15,8 @@ from django.views import View
from django.views.generic import UpdateView, CreateView, DeleteView
from django.views.generic.base import TemplateView
+from part.models import Part
+
from rest_framework import views
@@ -287,6 +289,21 @@ class IndexView(TemplateView):
template_name = 'InvenTree/index.html'
+ def get_context_data(self, **kwargs):
+
+ context = super(TemplateView, self).get_context_data(**kwargs)
+
+ # Generate a list of orderable parts which have stock below their minimum values
+ 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
+ context['to_build'] = [part for part in Part.objects.filter(buildable=True) if part.need_to_restock()]
+
+ print("order:", len(context['to_order']))
+ print("build:", len(context['to_build']))
+
+ return context
+
class SearchView(TemplateView):
""" View for InvenTree search page.
diff --git a/InvenTree/build/templates/build/allocate.html b/InvenTree/build/templates/build/allocate.html
index c3cda38429..0a3867af8e 100644
--- a/InvenTree/build/templates/build/allocate.html
+++ b/InvenTree/build/templates/build/allocate.html
@@ -11,12 +11,9 @@
{% for bom_item in bom_items.all %}
-{% include "build/allocation_item.html" with item=bom_item build=build %}
+{% include "build/allocation_item.html" with item=bom_item build=build collapse_id=bom_item.id %}
{% endfor %}
-
+{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/part/migrations/0014_auto_20190502_2039.py b/InvenTree/part/migrations/0014_auto_20190502_2039.py
new file mode 100644
index 0000000000..c32c3afe32
--- /dev/null
+++ b/InvenTree/part/migrations/0014_auto_20190502_2039.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2 on 2019-05-02 10:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('part', '0013_auto_20190429_2229'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='part',
+ name='URL',
+ field=models.URLField(blank=True, help_text='Link to extenal URL'),
+ ),
+ ]
diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index ab83b584cd..7bce91c26c 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -21,6 +21,7 @@ from django.core.validators import MinValueValidator
from django.db.models.signals import pre_delete
from django.dispatch import receiver
+from InvenTree import helpers
from InvenTree.models import InvenTreeTree
from company.models import Company
@@ -179,6 +180,16 @@ class Part(models.Model):
def __str__(self):
return "{n} - {d}".format(n=self.name, d=self.description)
+ @property
+ def format_barcode(self):
+ """ Return a JSON string for formatting a barcode for this Part object """
+
+ return helpers.MakeBarcode(
+ "Part",
+ self.id,
+ reverse('api-part-detail', kwargs={'pk': self.id}),
+ )
+
class Meta:
verbose_name = "Part"
verbose_name_plural = "Parts"
@@ -193,14 +204,25 @@ class Part(models.Model):
def available_stock(self):
"""
Return the total available stock.
- This subtracts stock which is already allocated
+
+ - This subtracts stock which is already allocated to builds
"""
total = self.total_stock
total -= self.allocation_count
- return max(total, 0)
+ return total
+
+ def need_to_restock(self):
+ """ Return True if this part needs to be restocked
+ (either by purchasing or building).
+
+ If the allocated_stock exceeds the total_stock,
+ then we need to restock.
+ """
+
+ return (self.total_stock - self.allocation_count) < self.minimum_stock
@property
def can_build(self):
@@ -307,6 +329,12 @@ class Part(models.Model):
def used_in_count(self):
return self.used_in.count()
+ def required_parts(self):
+ parts = []
+ for bom in self.bom_items.all():
+ parts.append(bom.sub_part)
+ return parts
+
@property
def supplier_count(self):
# Return the number of supplier parts available for this part
diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html
index 3d514389bc..f199cc5981 100644
--- a/InvenTree/part/templates/part/category.html
+++ b/InvenTree/part/templates/part/category.html
@@ -33,9 +33,9 @@
-{% if category %}
-{% include "part/subcategories.html" with children=category.children.all %}
-{% else %}
+{% if category and category.children.all|length > 0 %}
+{% include "part/subcategories.html" with children=category.children.all collapse_id="children"%}
+{% elif children|length > 0 %}
{% include "part/subcategories.html" with children=children %}
{% endif %}
diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html
index 7b148318c9..f5941a7870 100644
--- a/InvenTree/part/templates/part/detail.html
+++ b/InvenTree/part/templates/part/detail.html
@@ -1,5 +1,6 @@
{% extends "part/part_base.html" %}
{% load static %}
+{% load qr_code %}
{% block details %}
{% include 'part/tabs.html' with tab='detail' %}
@@ -115,6 +116,8 @@
{% endif %}
+{% qr_from_text part.format_barcode size="s" image_format="png" error_correction="L" %}
+
{% endblock %}
{% block js_load %}
diff --git a/InvenTree/part/templates/part/subcategories.html b/InvenTree/part/templates/part/subcategories.html
index 5dcfa426ad..abf61d743e 100644
--- a/InvenTree/part/templates/part/subcategories.html
+++ b/InvenTree/part/templates/part/subcategories.html
@@ -1,27 +1,19 @@
-{% if children|length > 0 %}
-
-
\ No newline at end of file
diff --git a/InvenTree/templates/required_part_table.html b/InvenTree/templates/required_part_table.html
new file mode 100644
index 0000000000..4ae1441f11
--- /dev/null
+++ b/InvenTree/templates/required_part_table.html
@@ -0,0 +1,18 @@
+