From bb799d98be9cefffee6a717711b57b101a2cd364 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@gmail.com>
Date: Tue, 16 Jul 2019 00:10:24 +1000
Subject: [PATCH] Add ability to create new stock tracking note for a stock
 item

---
 .../static/script/inventree/stock.js          |  4 ++
 InvenTree/stock/forms.py                      | 16 ++++++-
 InvenTree/stock/models.py                     |  3 +-
 InvenTree/stock/serializers.py                |  1 +
 InvenTree/stock/templates/stock/item.html     | 31 ++++++++----
 InvenTree/stock/urls.py                       | 12 ++++-
 InvenTree/stock/views.py                      | 47 +++++++++++++++++++
 7 files changed, 101 insertions(+), 13 deletions(-)

diff --git a/InvenTree/InvenTree/static/script/inventree/stock.js b/InvenTree/InvenTree/static/script/inventree/stock.js
index 268b8ed42f..73a97b0b63 100644
--- a/InvenTree/InvenTree/static/script/inventree/stock.js
+++ b/InvenTree/InvenTree/static/script/inventree/stock.js
@@ -308,6 +308,10 @@ function loadStockTrackingTable(table, options) {
                 html += "<br><i>" + row.notes + "</i>";
             }
 
+            if (row.URL) {
+                html += "<br><a href='" + row.URL + "'>" + row.URL + "</a>";
+            }
+
             return html;
         }
     });
diff --git a/InvenTree/stock/forms.py b/InvenTree/stock/forms.py
index d7d74e45de..d1fd841612 100644
--- a/InvenTree/stock/forms.py
+++ b/InvenTree/stock/forms.py
@@ -8,7 +8,7 @@ from __future__ import unicode_literals
 from django import forms
 from InvenTree.forms import HelperForm
 
-from .models import StockLocation, StockItem
+from .models import StockLocation, StockItem, StockItemTracking
 
 
 class EditStockLocationForm(HelperForm):
@@ -104,3 +104,17 @@ class EditStockItemForm(HelperForm):
             'notes',
             'URL',
         ]
+
+
+class TrackingEntryForm(HelperForm):
+    """ Form for creating / editing a StockItemTracking object.
+    """
+
+    class Meta:
+        model = StockItemTracking
+
+        fields = [
+            'title',
+            'notes',
+            'URL',
+        ]
diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py
index 53abe2de95..11a588075d 100644
--- a/InvenTree/stock/models.py
+++ b/InvenTree/stock/models.py
@@ -298,7 +298,7 @@ class StockItem(models.Model):
     def has_tracking_info(self):
         return self.tracking_info.count() > 0
 
-    def addTransactionNote(self, title, user, notes='', system=True):
+    def addTransactionNote(self, title, user, notes='', url='', system=True):
         """ Generation a stock transaction note for this item.
 
         Brief automated note detailing a movement or quantity change.
@@ -310,6 +310,7 @@ class StockItem(models.Model):
             quantity=self.quantity,
             date=datetime.now().date(),
             notes=notes,
+            URL=url,
             system=system
         )
 
diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py
index 4ec0530bf4..14fe043564 100644
--- a/InvenTree/stock/serializers.py
+++ b/InvenTree/stock/serializers.py
@@ -149,6 +149,7 @@ class StockTrackingSerializer(InvenTreeModelSerializer):
             'date',
             'title',
             'notes',
+            'URL',
             'quantity',
             'user',
             'system',
diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html
index 28c54965bc..f6847d5ae1 100644
--- a/InvenTree/stock/templates/stock/item.html
+++ b/InvenTree/stock/templates/stock/item.html
@@ -125,19 +125,29 @@
     </div>
 </div>
 
-
-{% if item.has_tracking_info %}
 <hr>
-<div id='table-toolbar'>    
-    <h4>Stock Tracking Information</h4>
+<h4>Stock Tracking Information</h4>
+<div id='table-toolbar'>
+    <div class='btn-group'>
+        <button class='btn btn-success' type='button' title='New tracking entry' id='new-entry'>New Entry</button>
+    </div>    
 </div>
 <table class='table table-condensed table-striped' id='track-table' data-toolbar='#table-toolbar'>
 </table>
-{% endif %}
+
 {% endblock %}
 {% block js_ready %}
 {{ block.super }}
 
+    $("#new-entry").click(function() {
+        launchModalForm(
+            "{% url 'stock-tracking-create' item.id %}",
+            {
+                reload: true,
+            }
+        );
+    }); 
+
     $("#stock-duplicate").click(function() {
         launchModalForm(
             "{% url 'stock-item-create' %}",
@@ -152,11 +162,12 @@
 
     $("#stock-edit").click(function () {
         launchModalForm(
-                        "{% url 'stock-item-edit' item.id %}",
-                        {
-                            reload: true,
-                            submit_text: "Save",
-                        });
+            "{% url 'stock-item-edit' item.id %}",
+            {
+                reload: true,
+                submit_text: "Save",
+            }
+        );
     });
 
     $("#show-qr-code").click(function() {
diff --git a/InvenTree/stock/urls.py b/InvenTree/stock/urls.py
index 4666eb05dc..d5c8b428f0 100644
--- a/InvenTree/stock/urls.py
+++ b/InvenTree/stock/urls.py
@@ -21,9 +21,19 @@ stock_item_detail_urls = [
     url(r'^delete/?', views.StockItemDelete.as_view(), name='stock-item-delete'),
     url(r'^qr_code/?', views.StockItemQRCode.as_view(), name='stock-item-qr'),
 
+    url(r'^add_tracking/?', views.StockItemTrackingCreate.as_view(), name='stock-tracking-create'),
+
     url('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'),
 ]
 
+stock_tracking_urls = [
+
+    # edit
+
+    # list
+    url('^.*$', views.StockTrackingIndex.as_view(), name='stock-tracking-list')
+]
+
 stock_urls = [
     # Stock location
     url(r'^location/(?P<pk>\d+)/', include(stock_location_detail_urls)),
@@ -32,7 +42,7 @@ stock_urls = [
 
     url(r'^item/new/?', views.StockItemCreate.as_view(), name='stock-item-create'),
 
-    url(r'^track/?', views.StockTrackingIndex.as_view(), name='stock-tracking-list'),
+    url(r'^track/', include(stock_tracking_urls)),
 
     url(r'^adjust/?', views.StockAdjust.as_view(), name='stock-adjust'),
 
diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py
index 3ede00697e..a2a6403913 100644
--- a/InvenTree/stock/views.py
+++ b/InvenTree/stock/views.py
@@ -17,6 +17,7 @@ from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView
 from InvenTree.views import QRCodeView
 
 from InvenTree.helpers import str2bool
+from datetime import datetime
 
 from part.models import Part
 from .models import StockItem, StockLocation, StockItemTracking
@@ -25,6 +26,7 @@ from .forms import EditStockLocationForm
 from .forms import CreateStockItemForm
 from .forms import EditStockItemForm
 from .forms import AdjustStockForm
+from .forms import TrackingEntryForm
 
 
 class StockIndex(ListView):
@@ -588,3 +590,48 @@ class StockTrackingIndex(ListView):
     model = StockItemTracking
     template_name = 'stock/tracking.html'
     context_object_name = 'items'
+
+
+class StockItemTrackingCreate(AjaxCreateView):
+    """ View for creating a new StockItemTracking object.
+    """
+
+    model = StockItemTracking
+    ajax_form_title = "Add Stock Tracking Entry"
+    form_class = TrackingEntryForm
+
+    def post(self, request, *args, **kwargs):
+
+        self.request = request
+        self.form = self.get_form()
+
+        valid = False
+
+        if self.form.is_valid():
+            stock_id = self.kwargs['pk']
+
+            if stock_id:
+                try:
+                    stock_item = StockItem.objects.get(id=stock_id)
+
+                    # Save new tracking information
+                    tracking = self.form.save(commit=False)
+                    tracking.item = stock_item
+                    tracking.user = self.request.user
+                    tracking.quantity = stock_item.quantity
+                    tracking.date=datetime.now().date()
+                    tracking.system = False
+
+                    tracking.save()
+
+                    valid = True
+
+                except (StockItem.DoesNotExist, ValueError):
+                    pass
+
+        data = {
+            'form_valid': valid
+        }
+
+        return self.renderJsonResponse(request, self.form, data=data)
+        
\ No newline at end of file