diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py
index ba3f3525d5..a492ca353d 100644
--- a/InvenTree/InvenTree/helpers.py
+++ b/InvenTree/InvenTree/helpers.py
@@ -4,6 +4,15 @@ from wsgiref.util import FileWrapper
from django.http import StreamingHttpResponse
+def str2bool(text, test=True):
+ """ Test if a string 'looks' like a boolean value
+ """
+ if test:
+ return str(text).lower() in ['1', 'y', 'yes', 't', 'true', 'ok', ]
+ else:
+ return str(text).lower() in ['0', 'n', 'no', 'none', 'f', 'false', ]
+
+
def WrapWithQuotes(text):
# TODO - Make this better
if not text.startswith('"'):
diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py
index 4c25be2fe6..94dee8e95c 100644
--- a/InvenTree/InvenTree/models.py
+++ b/InvenTree/InvenTree/models.py
@@ -3,6 +3,7 @@ from __future__ import unicode_literals
from django.db import models
from django.contrib.contenttypes.models import ContentType
from rest_framework.exceptions import ValidationError
+from .helpers import str2bool
from django.db.models.signals import pre_delete
from django.dispatch import receiver
@@ -183,7 +184,7 @@ def FilterChildren(queryset, parent):
if not parent:
return queryset
- elif str(parent).lower() in ['none', 'false', 'null', 'top', '0']:
+ elif str2bool(parent, False):
return queryset.filter(parent=None)
else:
try:
diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html
index c97423ed86..df19003b21 100644
--- a/InvenTree/part/templates/part/part_base.html
+++ b/InvenTree/part/templates/part/part_base.html
@@ -32,7 +32,7 @@
{{ part.URL }} |
{% endif %}
-
+
@@ -95,4 +95,17 @@
}
);
});
+
+ $("#duplicate-part").click(function() {
+ launchModalForm(
+ "{% url 'part-create' %}",
+ {
+ follow: true,
+ data: {
+ copy: {{ part.id }},
+ },
+ }
+ );
+ });
+
{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index 31bedc5c56..4f7175301a 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -5,6 +5,7 @@ from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from django.views.generic import DetailView, ListView
+from django.forms.models import model_to_dict
from company.models import Company
from .models import PartCategory, Part, BomItem
@@ -49,7 +50,6 @@ class PartCreate(AjaxCreateView):
"""
model = Part
form_class = EditPartForm
- template_name = 'part/create.html'
ajax_form_title = 'Create new part'
ajax_template_name = 'modal_form.html'
@@ -77,7 +77,19 @@ class PartCreate(AjaxCreateView):
# Pre-fill the category field if a valid category is provided
def get_initial(self):
- initials = super(PartCreate, self).get_initial().copy()
+ # Is the client attempting to copy an existing part?
+ part_to_copy = self.request.GET.get('copy', None)
+
+ if part_to_copy:
+ try:
+ original = Part.objects.get(pk=part_to_copy)
+ initials = model_to_dict(original)
+ self.ajax_form_title = "Copy Part '{p}'".format(p=original.name)
+ except Part.DoesNotExist:
+ initials = super(PartCreate, self).get_initial()
+
+ else:
+ initials = super(PartCreate, self).get_initial()
if self.get_category_id():
initials['category'] = get_object_or_404(PartCategory, pk=self.get_category_id())
diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html
index f1f0a5db1d..aee40d513d 100644
--- a/InvenTree/stock/templates/stock/item.html
+++ b/InvenTree/stock/templates/stock/item.html
@@ -13,6 +13,7 @@
+