From 8e6de1b832afe321bdcc4f3d63a8d54958f5227c Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 16 Apr 2018 00:30:57 +1000 Subject: [PATCH] Add pages for part tracking - Edit / Delete / Create tracking info - Improvements to many pages --- InvenTree/part/forms.py | 1 + .../part/migrations/0017_part_purchaseable.py | 20 ++++++ InvenTree/part/models.py | 7 +++ InvenTree/part/templates/part/detail.html | 36 +++++++++-- InvenTree/part/templates/part/part_base.html | 61 +++++++++++++------ InvenTree/part/templates/part/supplier.html | 10 +-- InvenTree/part/templates/part/tabs.html | 18 ++++-- InvenTree/part/templates/part/track.html | 12 +++- InvenTree/part/templates/part/used_in.html | 2 - InvenTree/stock/forms.py | 1 + InvenTree/stock/models.py | 9 ++- InvenTree/stock/templates/stock/index.html | 6 ++ InvenTree/stock/templates/stock/item.html | 2 +- InvenTree/stock/templates/stock/loc_link.html | 2 +- .../supplier/templates/supplier/detail.html | 9 +-- .../templates/supplier/partdetail.html | 10 +-- InvenTree/supplier/views.py | 8 +++ InvenTree/track/forms.py | 28 +++++++++ InvenTree/track/models.py | 3 + InvenTree/track/templates/track/create.html | 5 ++ InvenTree/track/templates/track/delete.html | 11 ++++ InvenTree/track/templates/track/detail.html | 37 ++++++++++- InvenTree/track/templates/track/edit.html | 8 +-- InvenTree/track/templates/track/index.html | 6 ++ InvenTree/track/urls.py | 10 ++- InvenTree/track/views.py | 38 ++++++++++++ 26 files changed, 302 insertions(+), 58 deletions(-) create mode 100644 InvenTree/part/migrations/0017_part_purchaseable.py create mode 100644 InvenTree/track/forms.py create mode 100644 InvenTree/track/templates/track/create.html create mode 100644 InvenTree/track/templates/track/delete.html diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 379682e3dd..306e49bb99 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -28,6 +28,7 @@ class EditPartForm(forms.ModelForm): 'URL', 'minimum_stock', 'trackable', + 'purchaseable', ] diff --git a/InvenTree/part/migrations/0017_part_purchaseable.py b/InvenTree/part/migrations/0017_part_purchaseable.py new file mode 100644 index 0000000000..b3f6860a38 --- /dev/null +++ b/InvenTree/part/migrations/0017_part_purchaseable.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2018-04-15 14:21 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0016_auto_20180415_0316'), + ] + + operations = [ + migrations.AddField( + model_name='part', + name='purchaseable', + field=models.BooleanField(default=True), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 4c2b8967f5..cd57521dd8 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -115,6 +115,9 @@ class Part(models.Model): # and can have their movements tracked trackable = models.BooleanField(default=False) + # Is this part "purchaseable"? + purchaseable = models.BooleanField(default=True) + def __str__(self): if self.IPN: return "{name} ({ipn})".format( @@ -128,6 +131,10 @@ class Part(models.Model): verbose_name_plural = "Parts" #unique_together = (("name", "category"),) + @property + def tracked_parts(self): + return self.serials.order_by('serial') + @property def stock(self): """ Return the total stock quantity for this part. diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index dc2629ae47..3d38521dca 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -4,12 +4,40 @@ {% include 'part/tabs.html' with tab='detail' %} + + + + + + + + + + + + + + + + + + + + + + + + + +
Name{{ part.name }}
Description{{ part.decription }}
Category + {% if part.category %} + {{ part.category.name }} + {% endif %} +
Units{{ part.units }}
Trackable{{ part.trackable }}
Purchaseable{{ part.purchaseable }}
-Part details go here... -
- +
- +
{% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index c5202c3fa6..7cd4120f44 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -6,33 +6,54 @@ {% include "part/cat_link.html" with category=part.category %} -
-
- -
-
-

{{ part.name }}

- {% if part.description %} -

{{ part.description }}

- {% endif %} - {% if part.IPN %} -

IPN: {{ part.IPN }}

- {% endif %} - {% if part.URL %} -

{% include 'url.html' with url=part.URL %}

- {% endif %} +
+
+
+
+ +
+
+

{{ part.name }}

+ {% if part.description %} +

{{ part.description }}

+ {% endif %} +
+
+
+
+ + {% if part.IPN %} + + + + + {% endif %} + {% if part.URL %} + + + + + {% endif %} + + + + +
IPN{{ part.IPN }}
URL{{ part.URL }}
Stock{{ part.stock }}
+
+
{% block details %} {% endblock %} +
{% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/supplier.html b/InvenTree/part/templates/part/supplier.html index 425c98b3f3..a4a76f55da 100644 --- a/InvenTree/part/templates/part/supplier.html +++ b/InvenTree/part/templates/part/supplier.html @@ -4,7 +4,6 @@ {% include 'part/tabs.html' with tab='suppliers' %} -{% if part.supplier_parts.all|length > 0 %} @@ -23,8 +22,11 @@ {% endfor %}
SKU
-{% else %} -There are no suppliers defined for this part, sorry! -{% endif %} + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index 1c244c40b2..64e5089f10 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -1,15 +1,25 @@ \ No newline at end of file diff --git a/InvenTree/part/templates/part/track.html b/InvenTree/part/templates/part/track.html index 6ce8c8429e..01e174eef0 100644 --- a/InvenTree/part/templates/part/track.html +++ b/InvenTree/part/templates/part/track.html @@ -11,12 +11,18 @@ Part tracking for {{ part.name }} Serial Status -{% for track in part.serials.all %} +{% for track in part.tracked_parts.all %} - {{ track.serial }} - {{ track.status }} + {{ track.serial }} + {{ track.get_status_display }} {% endfor %} + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/templates/part/used_in.html b/InvenTree/part/templates/part/used_in.html index 2b3f55ef19..df6bdb1f24 100644 --- a/InvenTree/part/templates/part/used_in.html +++ b/InvenTree/part/templates/part/used_in.html @@ -4,8 +4,6 @@ {% include 'part/tabs.html' with tab='used' %} -This part is used to make the following parts: - diff --git a/InvenTree/stock/forms.py b/InvenTree/stock/forms.py index 606b41db9f..fd124cf837 100644 --- a/InvenTree/stock/forms.py +++ b/InvenTree/stock/forms.py @@ -48,4 +48,5 @@ class EditStockItemForm(forms.ModelForm): 'supplier_part', 'location', 'quantity', + 'status' ] \ No newline at end of file diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 32fca52124..3a86e3d835 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -34,8 +34,13 @@ def before_delete_stock_location(sender, instance, using, **kwargs): # Update each part in the stock location for item in instance.items.all(): - item.location = instance.parent - item.save() + # If this location has a parent, move the child stock items to the parent + if instance.parent: + item.location = instance.parent + item.save() + # No parent location? Delete the stock items + else: + item.delete() # Update each child category for child in instance.children.all(): diff --git a/InvenTree/stock/templates/stock/index.html b/InvenTree/stock/templates/stock/index.html index 230b37604e..9d89718bb1 100644 --- a/InvenTree/stock/templates/stock/index.html +++ b/InvenTree/stock/templates/stock/index.html @@ -12,4 +12,10 @@ {% include "stock/stock_table.html" with items=items %} {% endif %} + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index 1f4e63cdb4..92fb3258cb 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -37,7 +37,7 @@ {% endif %} - + {% if item.notes %} diff --git a/InvenTree/stock/templates/stock/loc_link.html b/InvenTree/stock/templates/stock/loc_link.html index 099c88e8fc..34f10f33f7 100644 --- a/InvenTree/stock/templates/stock/loc_link.html +++ b/InvenTree/stock/templates/stock/loc_link.html @@ -1,7 +1,7 @@
Part
Status{{ item.status }}{{ item.get_status_display }}
@@ -71,5 +71,6 @@ + {% endblock %} \ No newline at end of file diff --git a/InvenTree/supplier/templates/supplier/partdetail.html b/InvenTree/supplier/templates/supplier/partdetail.html index ccee02fe71..26fa19c328 100644 --- a/InvenTree/supplier/templates/supplier/partdetail.html +++ b/InvenTree/supplier/templates/supplier/partdetail.html @@ -29,12 +29,14 @@
-

+

{% endblock %} \ No newline at end of file diff --git a/InvenTree/supplier/views.py b/InvenTree/supplier/views.py index d3e03e1b66..704644bee5 100644 --- a/InvenTree/supplier/views.py +++ b/InvenTree/supplier/views.py @@ -5,6 +5,7 @@ from django.urls import reverse from django.views.generic import DetailView, ListView from django.views.generic.edit import UpdateView, DeleteView, CreateView +from part.models import Part from .models import Supplier, SupplierPart from .forms import EditSupplierForm @@ -76,9 +77,16 @@ class SupplierPartCreate(CreateView): initials = super(SupplierPartCreate, self).get_initial().copy() supplier_id = self.request.GET.get('supplier', None) + part_id = self.request.GET.get('part', None) if supplier_id: initials['supplier'] = get_object_or_404(Supplier, pk=supplier_id) + # TODO + # self.fields['supplier'].disabled = True + if part_id: + initials['part'] = get_object_or_404(Part, pk=part_id) + # TODO + # self.fields['part'].disabled = True return initials diff --git a/InvenTree/track/forms.py b/InvenTree/track/forms.py new file mode 100644 index 0000000000..0738210c56 --- /dev/null +++ b/InvenTree/track/forms.py @@ -0,0 +1,28 @@ +from django import forms +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Submit + +from .models import UniquePart + + +class EditTrackedPartForm(forms.ModelForm): + + def __init__(self, *args, **kwargs): + super(EditTrackedPartForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + + self.helper.form_id = 'id-edit-part-form' + self.helper.form_class = 'blueForms' + self.helper.form_method = 'post' + #self.helper.form_action = 'submit' + + self.helper.add_input(Submit('submit', 'Submit')) + + class Meta: + model = UniquePart + fields = [ + 'part', + 'serial', + 'customer', + 'status' + ] \ No newline at end of file diff --git a/InvenTree/track/models.py b/InvenTree/track/models.py index bb13c43734..ff341aca97 100644 --- a/InvenTree/track/models.py +++ b/InvenTree/track/models.py @@ -15,6 +15,9 @@ class UniquePart(models.Model): and tracking all events in the life of a part """ + def get_absolute_url(self): + return "/track/{id}/".format(id=self.id) + class Meta: # Cannot have multiple parts with same serial number unique_together = ('part', 'serial') diff --git a/InvenTree/track/templates/track/create.html b/InvenTree/track/templates/track/create.html new file mode 100644 index 0000000000..0760c37871 --- /dev/null +++ b/InvenTree/track/templates/track/create.html @@ -0,0 +1,5 @@ +{% extends "create_edit_obj.html" %} + +{% block obj_title %} +Create a new tracked part +{% endblock %} diff --git a/InvenTree/track/templates/track/delete.html b/InvenTree/track/templates/track/delete.html new file mode 100644 index 0000000000..13490c000c --- /dev/null +++ b/InvenTree/track/templates/track/delete.html @@ -0,0 +1,11 @@ +{% extends "delete_obj.html" %} + +{% block del_title %} +Are you sure you want to delete tracking info for this part? +{% endblock %} + +{% block del_body %} + +All tracking information for part {{ track.part.name }} SN-{{ track.serial }} will be deleted. + +{% endblock %} \ No newline at end of file diff --git a/InvenTree/track/templates/track/detail.html b/InvenTree/track/templates/track/detail.html index ce1f30ecda..1a7488be6f 100644 --- a/InvenTree/track/templates/track/detail.html +++ b/InvenTree/track/templates/track/detail.html @@ -2,8 +2,32 @@ {% block content %} -Part: {{ part.part.name }}
-Serial number: {{ part.serial }} +

Part tracking information

+ +
+ + + + + + + + + + + + + {% if part.customer %} + + + + + {% endif %} + + + + +
Part{{ part.part.name }}
Serial Number{{ part.serial }}
Creation Date{{ part.creation_date }}
Customer{{ part.customer }}
Status{{ part.get_status_display }}
{% if part.tracking_info.all|length > 0 %}

Tracking information:

@@ -23,4 +47,13 @@ Serial number: {{ part.serial }} {% endif %} + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/track/templates/track/edit.html b/InvenTree/track/templates/track/edit.html index 8d0fb09346..9f23c94e9d 100644 --- a/InvenTree/track/templates/track/edit.html +++ b/InvenTree/track/templates/track/edit.html @@ -1,5 +1,5 @@ -{% extends "base.html"% } +{% extends "create_edit_obj.html" %} -{% block content %} - -{% endblock %} \ No newline at end of file +{% block obj_title %} +Edit tracked part information +{% endblock %} diff --git a/InvenTree/track/templates/track/index.html b/InvenTree/track/templates/track/index.html index b98d815a5a..94ed748a4a 100644 --- a/InvenTree/track/templates/track/index.html +++ b/InvenTree/track/templates/track/index.html @@ -30,4 +30,10 @@
{% endif %} + + {% endblock %} \ No newline at end of file diff --git a/InvenTree/track/urls.py b/InvenTree/track/urls.py index 6b3319b049..23526a48ff 100644 --- a/InvenTree/track/urls.py +++ b/InvenTree/track/urls.py @@ -24,6 +24,9 @@ unique_api_urls = [ """ track_detail_urls = [ + url(r'^edit/?', views.TrackEdit.as_view(), name='track-edit'), + url(r'^delete/?', views.TrackDelete.as_view(), name='track-delete'), + url('^.*$', views.TrackDetail.as_view(), name='track-detail'), ] @@ -31,8 +34,9 @@ tracking_urls = [ # Detail view url(r'^(?P\d+)/', include(track_detail_urls)), - # List ALL tracked items - url('', views.TrackIndex.as_view(), name='track-index'), + # Create a new tracking item + url(r'^new/?', views.TrackCreate.as_view(), name='track-create'), - url(r'^.*$', RedirectView.as_view(url='', permanent=False), name='track-index'), + # List ALL tracked items + url(r'^.*$', views.TrackIndex.as_view(), name='track-index'), ] \ No newline at end of file diff --git a/InvenTree/track/views.py b/InvenTree/track/views.py index 26a0d06ed2..6cde97c1fb 100644 --- a/InvenTree/track/views.py +++ b/InvenTree/track/views.py @@ -5,8 +5,10 @@ from django.urls import reverse from django.views.generic import DetailView, ListView from django.views.generic.edit import UpdateView, DeleteView, CreateView +from part.models import Part from .models import UniquePart, PartTrackingInfo +from .forms import EditTrackedPartForm class TrackIndex(ListView): model = UniquePart @@ -23,3 +25,39 @@ class TrackDetail(DetailView): template_name = 'track/detail.html' context_object_name='part' + +class TrackCreate(CreateView): + model = UniquePart + form_class = EditTrackedPartForm + template_name = 'track/create.html' + context_object_name = 'part' + + def get_initial(self): + initials = super(TrackCreate, self).get_initial().copy() + + part_id = self.request.GET.get('part', None) + + if part_id: + initials['part'] = get_object_or_404(Part, pk=part_id) + + return initials + + +class TrackEdit(UpdateView): + model = UniquePart + form_class = EditTrackedPartForm + template_name = 'track/edit.html' + context_obect_name = 'part' + + +class TrackDelete(DeleteView): + model = UniquePart + success_url = '/track' + template_name = 'track/delete.html' + context_object_name = 'track' + + def post(self, request, *args, **kwargs): + if 'confirm' in request.POST: + return super(TrackDelete, self).post(request, *args, **kwargs) + else: + return HttpResponseRedirect(self.get_object().get_absolute_url()) \ No newline at end of file