mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Merge remote-tracking branch 'inventree/master'
This commit is contained in:
		| @@ -65,7 +65,12 @@ function updateStock(items, options={}) { | |||||||
|         html += '<tr>'; |         html += '<tr>'; | ||||||
|  |  | ||||||
|         html += '<td>' + item.part.name + '</td>'; |         html += '<td>' + item.part.name + '</td>'; | ||||||
|  |  | ||||||
|  |         if (item.location) { | ||||||
|             html += '<td>' + item.location.name + '</td>'; |             html += '<td>' + item.location.name + '</td>'; | ||||||
|  |         } else { | ||||||
|  |             html += '<td><i>No location set</i></td>'; | ||||||
|  |         } | ||||||
|         html += "<td><input class='form-control' "; |         html += "<td><input class='form-control' "; | ||||||
|         html += "value='" + vCur + "' "; |         html += "value='" + vCur + "' "; | ||||||
|         html += "min='" + vMin + "' "; |         html += "min='" + vMin + "' "; | ||||||
| @@ -144,9 +149,7 @@ function updateStock(items, options={}) { | |||||||
|                             method: 'post', |                             method: 'post', | ||||||
|                         }).then(function(response) { |                         }).then(function(response) { | ||||||
|                             closeModal(modal); |                             closeModal(modal); | ||||||
|                             if (options.success) { |                             afterForm(response, options); | ||||||
|                                 options.success(); |  | ||||||
|                             } |  | ||||||
|                         }).fail(function(xhr, status, error) { |                         }).fail(function(xhr, status, error) { | ||||||
|                             alert(error); |                             alert(error); | ||||||
|                         }); |                         }); | ||||||
| @@ -220,34 +223,28 @@ function moveStockItems(items, options) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     function doMove(location, parts) { |     function doMove(location, parts, notes) { | ||||||
|         inventreeUpdate("/api/stock/move/", |         inventreeUpdate("/api/stock/move/", | ||||||
|             { |             { | ||||||
|                 location: location, |                 location: location, | ||||||
|                             'parts[]': parts |                 'parts[]': parts, | ||||||
|  |                 'notes': notes, | ||||||
|             }, |             }, | ||||||
|             { |             { | ||||||
|                             success: function(response) { |                 method: 'post', | ||||||
|  |             }).then(function(response) { | ||||||
|                 closeModal(modal); |                 closeModal(modal); | ||||||
|                                 if (options.success) { |                 afterForm(response, options); | ||||||
|                                     options.success(); |             }).fail(function(xhr, status, error) { | ||||||
|                                 } |                 alert(error); | ||||||
|                             }, |  | ||||||
|                             error: function(error) { |  | ||||||
|                                 alert('error!:\n' + error); |  | ||||||
|                             }, |  | ||||||
|                             method: 'post' |  | ||||||
|             }); |             }); | ||||||
|     } |     } | ||||||
|          |          | ||||||
|  |  | ||||||
|     getStockLocations({}, |     getStockLocations({}, | ||||||
|     { |     { | ||||||
|         success: function(response) { |         success: function(response) { | ||||||
|             openModal({ |              | ||||||
|                 modal: modal, |  | ||||||
|                 title: "Move " + items.length + " stock items", |  | ||||||
|                 submit_text: "Move" |  | ||||||
|             }); |  | ||||||
|  |  | ||||||
|             // Extact part row info |             // Extact part row info | ||||||
|             var parts = []; |             var parts = []; | ||||||
| @@ -262,9 +259,13 @@ function moveStockItems(items, options) { | |||||||
|                 html += makeOption(loc.pk, loc.name + ' - <i>' + loc.description + '</i>'); |                 html += makeOption(loc.pk, loc.name + ' - <i>' + loc.description + '</i>'); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             html += "</select><br><hr>"; |             html += "</select><br>"; | ||||||
|  |  | ||||||
|             html += "The following stock items will be moved:<br><ul class='list-group'>\n"; |             html += "<hr><input type='text' id='notes' placeholder='Notes'/>"; | ||||||
|  |  | ||||||
|  |             html += "<p class='warning-msg' id='note-warning'><i>Note field must be filled</i></p>"; | ||||||
|  |  | ||||||
|  |             html += "<hr>The following stock items will be moved:<br><ul class='list-group'>\n"; | ||||||
|  |  | ||||||
|             for (i = 0; i < items.length; i++) { |             for (i = 0; i < items.length; i++) { | ||||||
|                 parts.push(items[i].pk); |                 parts.push(items[i].pk); | ||||||
| @@ -280,13 +281,29 @@ function moveStockItems(items, options) { | |||||||
|  |  | ||||||
|             html += "</ul>\n"; |             html += "</ul>\n"; | ||||||
|  |  | ||||||
|             modalSetContent(modal, html); |             openModal({ | ||||||
|  |                 modal: modal, | ||||||
|  |                 title: "Move " + items.length + " stock items", | ||||||
|  |                 submit_text: "Move", | ||||||
|  |                 content: html | ||||||
|  |             }); | ||||||
|  |  | ||||||
|  |             //modalSetContent(modal, html); | ||||||
|             attachSelect(modal); |             attachSelect(modal); | ||||||
|  |  | ||||||
|  |             $(modal).find('#note-warning').hide(); | ||||||
|  |  | ||||||
|             modalSubmit(modal, function() { |             modalSubmit(modal, function() { | ||||||
|                 var locId = $(modal).find("#stock-location").val(); |                 var locId = $(modal).find("#stock-location").val(); | ||||||
|  |  | ||||||
|                 doMove(locId, parts); |                 var notes = $(modal).find('#notes').val(); | ||||||
|  |  | ||||||
|  |                 if (!notes) { | ||||||
|  |                     $(modal).find('#note-warning').show(); | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 doMove(locId, parts, notes); | ||||||
|             }); |             }); | ||||||
|         }, |         }, | ||||||
|         error: function(error) { |         error: function(error) { | ||||||
| @@ -358,7 +375,7 @@ function loadStockTable(table, options) { | |||||||
|                         return renderLink(row.location.pathstring, row.location.url); |                         return renderLink(row.location.pathstring, row.location.url); | ||||||
|                     } |                     } | ||||||
|                     else { |                     else { | ||||||
|                         return ''; |                         return '<i>No stock location set</i>'; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
| @@ -383,4 +400,94 @@ function loadStockTable(table, options) { | |||||||
|     if (options.buttons) { |     if (options.buttons) { | ||||||
|         linkButtonsToSelection(table, options.buttons); |         linkButtonsToSelection(table, options.buttons); | ||||||
|     } |     } | ||||||
| }; | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function loadStockTrackingTable(table, options) { | ||||||
|  |  | ||||||
|  |     var cols = [ | ||||||
|  |         { | ||||||
|  |             field: 'pk', | ||||||
|  |             visible: false, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |             field: 'date', | ||||||
|  |             title: 'Date', | ||||||
|  |             sortable: true, | ||||||
|  |             formatter: function(value, row, index, field) { | ||||||
|  |                 var m = moment(value); | ||||||
|  |                 if (m.isValid()) { | ||||||
|  |                     var html = m.format('dddd MMMM Do YYYY') + '<br>' + m.format('h:mm a'); | ||||||
|  |                     return html; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 return 'N/A'; | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |     ]; | ||||||
|  |  | ||||||
|  |     // If enabled, provide a link to the referenced StockItem | ||||||
|  |     if (options.partColumn) { | ||||||
|  |         cols.push({ | ||||||
|  |             field: 'item', | ||||||
|  |             title: 'Stock Item', | ||||||
|  |             sortable: true, | ||||||
|  |             formatter: function(value, row, index, field) { | ||||||
|  |                 return renderLink(value.part_name, value.url); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Stock transaction description | ||||||
|  |     cols.push({ | ||||||
|  |         field: 'title', | ||||||
|  |         title: 'Description', | ||||||
|  |         sortable: true, | ||||||
|  |         formatter: function(value, row, index, field) { | ||||||
|  |             var html = "<b>" + value + "</b>"; | ||||||
|  |  | ||||||
|  |             if (row.notes) { | ||||||
|  |                 html += "<br><i>" + row.notes + "</i>"; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return html; | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     cols.push({ | ||||||
|  |         field: 'quantity', | ||||||
|  |         title: 'Quantity', | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     cols.push({ | ||||||
|  |         sortable: true, | ||||||
|  |         field: 'user', | ||||||
|  |         title: 'User', | ||||||
|  |         formatter: function(value, row, index, field) { | ||||||
|  |             if (value) | ||||||
|  |             { | ||||||
|  |                 // TODO - Format the user's first and last names | ||||||
|  |                 return value.username; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return "No user information"; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     table.bootstrapTable({ | ||||||
|  |         sortable: true, | ||||||
|  |         search: true, | ||||||
|  |         method: 'get', | ||||||
|  |         rememberOrder: true, | ||||||
|  |         queryParams: options.params, | ||||||
|  |         columns: cols, | ||||||
|  |         pagination: true, | ||||||
|  |         url: options.url, | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  |     if (options.buttons) { | ||||||
|  |         linkButtonsToSelection(table, options.buttons); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -111,17 +111,22 @@ class StockStocktake(APIView): | |||||||
|         if 'notes' in request.data: |         if 'notes' in request.data: | ||||||
|             notes = request.data['notes'] |             notes = request.data['notes'] | ||||||
|  |  | ||||||
|  |         n = 0 | ||||||
|  |  | ||||||
|         for item in items: |         for item in items: | ||||||
|             quantity = int(item['quantity']) |             quantity = int(item['quantity']) | ||||||
|  |  | ||||||
|             if action == u'stocktake': |             if action == u'stocktake': | ||||||
|                 item['item'].stocktake(quantity, request.user, notes=notes) |                 if item['item'].stocktake(quantity, request.user, notes=notes): | ||||||
|  |                     n += 1 | ||||||
|             elif action == u'remove': |             elif action == u'remove': | ||||||
|                 item['item'].take_stock(quantity, request.user, notes=notes) |                 if item['item'].take_stock(quantity, request.user, notes=notes): | ||||||
|  |                     n += 1 | ||||||
|             elif action == u'add': |             elif action == u'add': | ||||||
|                 item['item'].add_stock(quantity, request.user, notes=notes) |                 if item['item'].add_stock(quantity, request.user, notes=notes): | ||||||
|  |                     n += 1 | ||||||
|  |  | ||||||
|         return Response({'success': 'success'}) |         return Response({'success': 'Updated stock for {n} items'.format(n=n)}) | ||||||
|  |  | ||||||
|  |  | ||||||
| class StockMove(APIView): | class StockMove(APIView): | ||||||
| @@ -153,6 +158,9 @@ class StockMove(APIView): | |||||||
|  |  | ||||||
|         errors = [] |         errors = [] | ||||||
|  |  | ||||||
|  |         if u'notes' not in data: | ||||||
|  |             errors.append({'notes': 'Notes field must be supplied'}) | ||||||
|  |  | ||||||
|         for pid in part_list: |         for pid in part_list: | ||||||
|             try: |             try: | ||||||
|                 part = StockItem.objects.get(pk=pid) |                 part = StockItem.objects.get(pk=pid) | ||||||
| @@ -163,12 +171,15 @@ class StockMove(APIView): | |||||||
|         if len(errors) > 0: |         if len(errors) > 0: | ||||||
|             raise ValidationError(errors) |             raise ValidationError(errors) | ||||||
|  |  | ||||||
|  |         n = 0 | ||||||
|  |  | ||||||
|         for part in parts: |         for part in parts: | ||||||
|             part.move(location, request.user) |             if part.move(location, data.get('notes'), request.user): | ||||||
|  |                 n += 1 | ||||||
|  |  | ||||||
|         return Response({'success': 'Moved {n} parts to {loc}'.format( |         return Response({'success': 'Moved {n} parts to {loc}'.format( | ||||||
|             n=len(parts), |             n=n, | ||||||
|             loc=location.name |             loc=str(location) | ||||||
|         )}) |         )}) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -64,7 +64,9 @@ class EditStockItemForm(HelperForm): | |||||||
|         model = StockItem |         model = StockItem | ||||||
|  |  | ||||||
|         fields = [ |         fields = [ | ||||||
|  |             'supplier_part', | ||||||
|             'batch', |             'batch', | ||||||
|             'status', |             'status', | ||||||
|             'notes' |             'notes', | ||||||
|  |             'URL', | ||||||
|         ] |         ] | ||||||
|   | |||||||
| @@ -67,6 +67,7 @@ class StockItem(models.Model): | |||||||
|             self.add_transaction_note( |             self.add_transaction_note( | ||||||
|                 'Created stock item', |                 'Created stock item', | ||||||
|                 None, |                 None, | ||||||
|  |                 notes="Created new stock item for part '{p}'".format(p=str(self.part)), | ||||||
|                 system=True |                 system=True | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
| @@ -220,13 +221,17 @@ class StockItem(models.Model): | |||||||
|     @transaction.atomic |     @transaction.atomic | ||||||
|     def move(self, location, notes, user): |     def move(self, location, notes, user): | ||||||
|  |  | ||||||
|         if location.pk == self.location.pk: |         if location is None: | ||||||
|             return False  # raise forms.ValidationError("Cannot move item to its current location") |             # TODO - Raise appropriate error (cannot move to blank location) | ||||||
|  |             return False | ||||||
|  |         elif self.location and (location.pk == self.location.pk): | ||||||
|  |             # TODO - Raise appropriate error (cannot move to same location) | ||||||
|  |             return False | ||||||
|  |  | ||||||
|         msg = "Moved to {loc} (from {src})".format( |         msg = "Moved to {loc}".format(loc=str(location)) | ||||||
|             loc=location.name, |  | ||||||
|             src=self.location.name |         if self.location: | ||||||
|         ) |             msg += " (from {loc})".format(loc=str(self.location)) | ||||||
|  |  | ||||||
|         self.location = location |         self.location = location | ||||||
|         self.save() |         self.save() | ||||||
| @@ -329,7 +334,8 @@ class StockItemTracking(models.Model): | |||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     def get_absolute_url(self): |     def get_absolute_url(self): | ||||||
|         return reverse('stock-tracking-detail', kwargs={'pk': self.id}) |         return '/stock/track/{pk}'.format(pk=self.id) | ||||||
|  |         # return reverse('stock-tracking-detail', kwargs={'pk': self.id}) | ||||||
|  |  | ||||||
|     # Stock item |     # Stock item | ||||||
|     item = models.ForeignKey(StockItem, on_delete=models.CASCADE, |     item = models.ForeignKey(StockItem, on_delete=models.CASCADE, | ||||||
|   | |||||||
| @@ -24,31 +24,22 @@ class LocationBriefSerializer(serializers.ModelSerializer): | |||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| class StockTrackingSerializer(serializers.ModelSerializer): | class StockItemSerializerBrief(serializers.ModelSerializer): | ||||||
|  |     """ | ||||||
|  |     Provide a brief serializer for StockItem | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     url = serializers.CharField(source='get_absolute_url', read_only=True) |     url = serializers.CharField(source='get_absolute_url', read_only=True) | ||||||
|  |  | ||||||
|     user = UserSerializerBrief(many=False, read_only=True) |     part_name = serializers.CharField(source='part.name', read_only=True) | ||||||
|  |  | ||||||
|     class Meta: |     class Meta: | ||||||
|         model = StockItemTracking |         model = StockItem | ||||||
|         fields = [ |         fields = [ | ||||||
|             'pk', |             'pk', | ||||||
|  |             'uuid', | ||||||
|             'url', |             'url', | ||||||
|             'item', |             'part_name', | ||||||
|             'date', |  | ||||||
|             'title', |  | ||||||
|             'notes', |  | ||||||
|             'quantity', |  | ||||||
|             'user', |  | ||||||
|             'system', |  | ||||||
|         ] |  | ||||||
|  |  | ||||||
|         read_only_fields = [ |  | ||||||
|             'date', |  | ||||||
|             'user', |  | ||||||
|             'system', |  | ||||||
|             'quantity', |  | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -118,3 +109,33 @@ class LocationSerializer(serializers.ModelSerializer): | |||||||
|             'parent', |             'parent', | ||||||
|             'pathstring' |             'pathstring' | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StockTrackingSerializer(serializers.ModelSerializer): | ||||||
|  |  | ||||||
|  |     url = serializers.CharField(source='get_absolute_url', read_only=True) | ||||||
|  |  | ||||||
|  |     user = UserSerializerBrief(many=False, read_only=True) | ||||||
|  |  | ||||||
|  |     item = StockItemSerializerBrief(many=False, read_only=True) | ||||||
|  |  | ||||||
|  |     class Meta: | ||||||
|  |         model = StockItemTracking | ||||||
|  |         fields = [ | ||||||
|  |             'pk', | ||||||
|  |             'url', | ||||||
|  |             'item', | ||||||
|  |             'date', | ||||||
|  |             'title', | ||||||
|  |             'notes', | ||||||
|  |             'quantity', | ||||||
|  |             'user', | ||||||
|  |             'system', | ||||||
|  |         ] | ||||||
|  |  | ||||||
|  |         read_only_fields = [ | ||||||
|  |             'date', | ||||||
|  |             'user', | ||||||
|  |             'system', | ||||||
|  |             'quantity', | ||||||
|  |         ] | ||||||
|   | |||||||
| @@ -121,22 +121,11 @@ | |||||||
|  |  | ||||||
| {% if item.has_tracking_info %} | {% if item.has_tracking_info %} | ||||||
|  |  | ||||||
| <hr> | <div id='table-toolbar'>     | ||||||
| <div class="panel-group"> |     <h4>Stock Tracking Information</h4> | ||||||
|  <div class="panel panel-default"> |  | ||||||
|    <div class="panel-heading"> |  | ||||||
|      <h4 class="panel-title"> |  | ||||||
|        <a data-toggle="collapse" href="#collapse1">Stock Tracking</a><span class='badge'>{{ item.tracking_info.all|length }}</span> |  | ||||||
|      </h4> |  | ||||||
|    </div> |  | ||||||
|    <div id="collapse1" class="panel-collapse collapse"> |  | ||||||
|      <div class="panel-body"> |  | ||||||
|          <table class='table table-condensed table-striped' id='track-table'> |  | ||||||
|          </table> |  | ||||||
|      </div> |  | ||||||
|    </div> |  | ||||||
|  </div> |  | ||||||
| </div> | </div> | ||||||
|  | <table class='table table-condensed table-striped' id='track-table' data-toolbar='#table-toolbar'> | ||||||
|  | </table> | ||||||
| {% endif %} | {% endif %} | ||||||
| {% endblock %} | {% endblock %} | ||||||
| {% block js_ready %} | {% block js_ready %} | ||||||
| @@ -210,66 +199,14 @@ | |||||||
|                          }); |                          }); | ||||||
|     }); |     }); | ||||||
|  |  | ||||||
|     $('#track-table').bootstrapTable({ |     loadStockTrackingTable($("#track-table"), { | ||||||
|         sortable: true, |         params: function(p) { | ||||||
|         search: true, |  | ||||||
|         method: 'get', |  | ||||||
|         queryParams: function(p) { |  | ||||||
|             return { |             return { | ||||||
|  |                 ordering: '-date', | ||||||
|                 item: {{ item.pk }}, |                 item: {{ item.pk }}, | ||||||
|             } |             }; | ||||||
|         }, |         }, | ||||||
|         columns: [ |  | ||||||
|             { |  | ||||||
|                 field: 'date', |  | ||||||
|                 title: 'Date', |  | ||||||
|                 sortable: true, |  | ||||||
|                 formatter: function(value, row, index, field) { |  | ||||||
|                     var m = moment(value); |  | ||||||
|                     if (m.isValid()) { |  | ||||||
|                         var html = m.format('dddd MMMM Do YYYY') + '<br>' + m.format('h:mm a'); |  | ||||||
|                         return html; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     return 'N/A'; |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 field: 'title', |  | ||||||
|                 title: 'Description', |  | ||||||
|                 sortable: true, |  | ||||||
|                 formatter: function(value, row, index, field) { |  | ||||||
|                     var html = "<b>" + value + "</b>"; |  | ||||||
|  |  | ||||||
|                     if (row.notes) { |  | ||||||
|                         html += "<br><i>" + row.notes + "</i>"; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     return html; |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 field: 'quantity', |  | ||||||
|                 title: 'Quantity', |  | ||||||
|             }, |  | ||||||
|             { |  | ||||||
|                 sortable: true, |  | ||||||
|                 field: 'user', |  | ||||||
|                 title: 'User', |  | ||||||
|                 formatter: function(value, row, index, field) { |  | ||||||
|                     if (value) |  | ||||||
|                     { |  | ||||||
|                         // TODO - Format the user's first and last names |  | ||||||
|                         return value.username; |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                         return "No user information"; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         ], |  | ||||||
|         url: "{% url 'api-stock-track' %}",  |         url: "{% url 'api-stock-track' %}",  | ||||||
|     }) |     }); | ||||||
|  |  | ||||||
| {% endblock %} | {% endblock %} | ||||||
							
								
								
									
										28
									
								
								InvenTree/stock/templates/stock/tracking.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								InvenTree/stock/templates/stock/tracking.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | {% extends "stock/stock_app_base.html" %} | ||||||
|  | {% load static %} | ||||||
|  |  | ||||||
|  | {% block content %} | ||||||
|  |  | ||||||
|  | <h3>Stock list here!</h3> | ||||||
|  |  | ||||||
|  | <table class='table table-striped table-condensed' data-toolbar='#button-toolbar' id='tracking-table'> | ||||||
|  | </table> | ||||||
|  |  | ||||||
|  | {% include 'modals.html' %} | ||||||
|  |  | ||||||
|  | {% endblock %} | ||||||
|  |  | ||||||
|  | {% block js_ready %} | ||||||
|  | {{ block.super }} | ||||||
|  |  | ||||||
|  |     loadStockTrackingTable($("#tracking-table"), { | ||||||
|  |         params: function(p) { | ||||||
|  |             return { | ||||||
|  |                 ordering: '-date', | ||||||
|  |             }; | ||||||
|  |         }, | ||||||
|  |         partColumn: true, | ||||||
|  |         url: "{% url 'api-stock-track' %}",  | ||||||
|  |     }); | ||||||
|  |  | ||||||
|  | {% endblock %} | ||||||
| @@ -28,6 +28,8 @@ stock_urls = [ | |||||||
|  |  | ||||||
|     url(r'^item/new/?', views.StockItemCreate.as_view(), name='stock-item-create'), |     url(r'^item/new/?', views.StockItemCreate.as_view(), name='stock-item-create'), | ||||||
|  |  | ||||||
|  |     url(r'^track/?', views.StockTrackingIndex.as_view(), name='stock-tracking-list'), | ||||||
|  |  | ||||||
|     # Individual stock items |     # Individual stock items | ||||||
|     url(r'^item/(?P<pk>\d+)/', include(stock_item_detail_urls)), |     url(r'^item/(?P<pk>\d+)/', include(stock_item_detail_urls)), | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ from django.forms.models import model_to_dict | |||||||
| from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView | from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView | ||||||
|  |  | ||||||
| from part.models import Part | from part.models import Part | ||||||
| from .models import StockItem, StockLocation | from .models import StockItem, StockLocation, StockItemTracking | ||||||
|  |  | ||||||
| from .forms import EditStockLocationForm | from .forms import EditStockLocationForm | ||||||
| from .forms import CreateStockItemForm | from .forms import CreateStockItemForm | ||||||
| @@ -248,3 +248,13 @@ class StockItemStocktake(AjaxUpdateView): | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return self.renderJsonResponse(request, form, data) |         return self.renderJsonResponse(request, form, data) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class StockTrackingIndex(ListView): | ||||||
|  |     """ | ||||||
|  |     StockTrackingIndex provides a page to display StockItemTracking objects | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     model = StockItemTracking | ||||||
|  |     template_name = 'stock/tracking.html' | ||||||
|  |     context_object_name = 'items' | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @@ -8,7 +8,7 @@ clean: | |||||||
| 	rm -f .coverage | 	rm -f .coverage | ||||||
|  |  | ||||||
| style: | style: | ||||||
| 	flake8 InvenTree --ignore=C901,E501 | 	flake8 InvenTree | ||||||
|  |  | ||||||
| test: | test: | ||||||
| 	python InvenTree/manage.py check | 	python InvenTree/manage.py check | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user