From 7c70b31af483cb4d72a437d6d77ba7b5349ceea6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 6 May 2019 07:55:39 +1000 Subject: [PATCH 01/26] Build no longer auto-allocates on creation - Provide 'auto allocation' function which can be run by the user --- InvenTree/build/models.py | 101 +++++++++++++++++++++----------------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index e81ae6a45d..9f54713706 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -32,50 +32,7 @@ class Build(models.Model): URL: External URL for extra information notes: Text notes """ - - def save(self, *args, **kwargs): - """ Called when the Build model is saved to the database. - - If this is a new Build, try to allocate StockItem objects automatically. - - - If there is only one StockItem for a Part, use that one. - - If there are multiple StockItem objects, leave blank and let the user decide - """ - - allocate_parts = False - - # If there is no PK yet, then this is the first time the Build has been saved - if not self.pk: - allocate_parts = True - - # Save this Build first - super(Build, self).save(*args, **kwargs) - - if allocate_parts: - for item in self.part.bom_items.all(): - part = item.sub_part - # Number of parts required for this build - q_req = item.quantity * self.quantity - - stock = StockItem.objects.filter(part=part) - - if len(stock) == 1: - stock_item = stock[0] - - # Are there any parts available? - if stock_item.quantity > 0: - # If there are not enough parts, reduce the amount we will take - if stock_item.quantity < q_req: - q_req = stock_item.quantity - - # Allocate parts to this build - build_item = BuildItem( - build=self, - stock_item=stock_item, - quantity=q_req) - - build_item.save() - + def __str__(self): return "Build {q} x {part}".format(q=self.quantity, part=str(self.part)) @@ -153,6 +110,62 @@ class Build(models.Model): self.status = self.CANCELLED self.save() + def getAutoAllocations(self): + """ Return a list of parts which will be allocated + using the 'AutoAllocate' function. + + For each item in the BOM for the attached Part: + + - If there is a single StockItem, use that StockItem + - Take as many parts as available (up to the quantity required for the BOM) + - If there are multiple StockItems available, ignore (leave up to the user) + + Returns: + A dict object containing the StockItem objects to be allocated (and the quantities) + """ + + allocations = {} + + for item in self.part.bom_items.all(): + + # How many parts required for this build? + q_required = item.quantity * self.quantity + + stock = StockItem.objects.filter(part=item.sub_part) + + # Only one StockItem to choose from? Default to that one! + if len(stock) == 1: + stock_item = stock[0] + + # Are there any parts available? + if stock_item.quantity > 0: + # Only take as many as are available + if stock_item.quantity < q_required: + q_required = stock_item.quantity + + # Add the item to the allocations list + allocations[stock_item] = q_required + + return allocations + + @transaction.atomic + def autoAllocate(self): + """ Run auto-allocation routine to allocate StockItems to this Build. + + See: getAutoAllocations() + """ + + allocations = self.getAutoAllocations() + + for item in allocations: + # Create a new allocation + build_item = BuildItem( + build=self, + stock_item=item, + quantity=allocations[item]) + + build_item.save() + @transaction.atomic def completeBuild(self, location, user): """ Mark the Build as COMPLETE From ff14b0b36323ede020647ccb4395badfb95c5102 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 6 May 2019 07:58:20 +1000 Subject: [PATCH 02/26] Ensure that a StockItem is not already allocated to a Build --- InvenTree/build/models.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 9f54713706..504b57cc75 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -32,7 +32,7 @@ class Build(models.Model): URL: External URL for extra information notes: Text notes """ - + def __str__(self): return "Build {q} x {part}".format(q=self.quantity, part=str(self.part)) @@ -137,6 +137,12 @@ class Build(models.Model): if len(stock) == 1: stock_item = stock[0] + # Check that we have not already allocated this stock-item against this build + build_items = BuildItem.objects.filter(build=self, stock_item=stock_item) + + if len(build_items) > 0: + continue + # Are there any parts available? if stock_item.quantity > 0: # Only take as many as are available From c10ddab30c0e037bf926bdcd74ad7a0bf3f42a29 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 6 May 2019 08:09:29 +1000 Subject: [PATCH 03/26] Improve display of Build index --- .../build/templates/build/build_list.html | 10 ++++ InvenTree/build/templates/build/index.html | 60 ++++++++++--------- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/InvenTree/build/templates/build/build_list.html b/InvenTree/build/templates/build/build_list.html index 890dd551b1..b423d214d9 100644 --- a/InvenTree/build/templates/build/build_list.html +++ b/InvenTree/build/templates/build/build_list.html @@ -12,6 +12,11 @@ Part Quantity Status + {% if completed %} + Completed + {% else %} + Created + {% endif %} @@ -21,6 +26,11 @@ {{ build.part.name }} {{ build.quantity }} {% include "build_status.html" with build=build %} + {% if completed %} + {{ build.completion_date }} + {% else %} + {{ build.creation_date }} + {% endif %} {% endfor %} diff --git a/InvenTree/build/templates/build/index.html b/InvenTree/build/templates/build/index.html index 46d3533651..f49499ab66 100644 --- a/InvenTree/build/templates/build/index.html +++ b/InvenTree/build/templates/build/index.html @@ -3,39 +3,26 @@ {% block content %} -

Active Builds

- -
-
- +
+
+

Part Builds

+
+
+
+
+ +
+
- - - - - - - - - - -{% for build in active %} - - - - - -{% endfor %} - -
BuildPartQuantityStatus
{{ build.title }}{{ build.part.name }}{{ build.quantity }}{% include "build_status.html" with build=build %} -
+
-

-{% include "build/build_list.html" with builds=completed title="Completed Builds" collapse_id="complete" %} -

-{% include "build/build_list.html" with builds=cancelled title="Cancelled Builds" collapse_id="cancelled" %} +{% include "build/build_list.html" with builds=active title="Active Builds" completed=False collapse_id='active' %} + +{% include "build/build_list.html" with builds=completed completed=True title="Completed Builds" collapse_id="complete" %} + +{% include "build/build_list.html" with builds=cancelled title="Cancelled Builds" completed=False collapse_id="cancelled" %} {% include 'modals.html' %} @@ -75,6 +62,21 @@ title: 'Status', sortable: true, } + { + title: 'Created', + sortable: true, + }, + { + title: 'Completed', + sortable: true, + formatter: function(value, row, index, field) { + if (value) { + return 'hello'; + } else { + return "---"; + } + }, + } ] }); From 7e81d9021f283bc5f06719211d51c49482f21609 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 6 May 2019 08:13:59 +1000 Subject: [PATCH 04/26] Auto-show the active build table --- InvenTree/build/templates/build/index.html | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/InvenTree/build/templates/build/index.html b/InvenTree/build/templates/build/index.html index f49499ab66..5163905f83 100644 --- a/InvenTree/build/templates/build/index.html +++ b/InvenTree/build/templates/build/index.html @@ -30,6 +30,9 @@ {% block js_ready %} {{ block.super }} + + $("#collapse-item-active").collapse().show(); + $("#new-build").click(function() { launchModalForm( "{% url 'build-create' %}", @@ -61,22 +64,10 @@ { title: 'Status', sortable: true, - } - { - title: 'Created', - sortable: true, }, { - title: 'Completed', sortable: true, - formatter: function(value, row, index, field) { - if (value) { - return 'hello'; - } else { - return "---"; - } - }, - } + }, ] }); From e58bed41b0cf9de78250e18880c5ffe795115fdb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 6 May 2019 08:35:02 +1000 Subject: [PATCH 05/26] Visual style improvements --- InvenTree/build/templates/build/index.html | 4 +-- .../company/templates/company/index.html | 16 +++++++-- .../part/templates/part/attachments.html | 15 +++++--- InvenTree/part/templates/part/detail.html | 34 +++++++++---------- InvenTree/part/templates/part/stock.html | 9 ++++- InvenTree/part/templates/part/supplier.html | 10 +++++- InvenTree/part/templates/part/tabs.html | 4 ++- InvenTree/part/templates/part/used_in.html | 12 ++++++- InvenTree/templates/InvenTree/index.html | 2 +- 9 files changed, 74 insertions(+), 32 deletions(-) diff --git a/InvenTree/build/templates/build/index.html b/InvenTree/build/templates/build/index.html index 5163905f83..af24a6ca53 100644 --- a/InvenTree/build/templates/build/index.html +++ b/InvenTree/build/templates/build/index.html @@ -5,12 +5,12 @@
-

Part Builds

+

Part Builds

- +
diff --git a/InvenTree/company/templates/company/index.html b/InvenTree/company/templates/company/index.html index 7cfd7d18de..4fa19e9eb6 100644 --- a/InvenTree/company/templates/company/index.html +++ b/InvenTree/company/templates/company/index.html @@ -4,11 +4,21 @@ {% block content %} -

Companies

-
- +
+
+

Company List

+
+
+
+
+ +
+
+
+
+
diff --git a/InvenTree/part/templates/part/attachments.html b/InvenTree/part/templates/part/attachments.html index d1aa037870..77ffa8dbff 100644 --- a/InvenTree/part/templates/part/attachments.html +++ b/InvenTree/part/templates/part/attachments.html @@ -5,12 +5,19 @@ {% include 'part/tabs.html' with tab='attachments' %} -

Attachments

- -
- +
+
+

Part Attachments

+
+
+
+ +
+
+
+ diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index a59b97ce47..419409a95d 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -6,26 +6,24 @@
-

Part Details

+

Part Details

-

- -

+
+ + +
diff --git a/InvenTree/part/templates/part/stock.html b/InvenTree/part/templates/part/stock.html index fb620c1b7b..a329a25249 100644 --- a/InvenTree/part/templates/part/stock.html +++ b/InvenTree/part/templates/part/stock.html @@ -4,7 +4,14 @@ {% include 'part/tabs.html' with tab='stock' %} -

Part Stock

+
+
+

Part Stock

+
+
+
+
+
{% if part.active %} diff --git a/InvenTree/part/templates/part/supplier.html b/InvenTree/part/templates/part/supplier.html index 0f417eb650..00f7fa41d0 100644 --- a/InvenTree/part/templates/part/supplier.html +++ b/InvenTree/part/templates/part/supplier.html @@ -4,7 +4,15 @@ {% include 'part/tabs.html' with tab='suppliers' %} -

Part Suppliers

+
+
+

Part Suppliers

+
+
+
+
+ +
diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index 2bcf894b2a..d8e84a6f09 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -37,4 +37,6 @@ Attachments {% if part.attachments.all|length > 0 %}{{ part.attachments.all|length }}{% endif %} - \ No newline at end of file + + +
\ 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 118f335468..eca07a585d 100644 --- a/InvenTree/part/templates/part/used_in.html +++ b/InvenTree/part/templates/part/used_in.html @@ -4,7 +4,17 @@ {% include 'part/tabs.html' with tab='used' %} -

Used In

+
+
+

Used to Build

+
+
+
+
+
+
+ +
File
diff --git a/InvenTree/templates/InvenTree/index.html b/InvenTree/templates/InvenTree/index.html index 12019ca7b0..89b412bdcc 100644 --- a/InvenTree/templates/InvenTree/index.html +++ b/InvenTree/templates/InvenTree/index.html @@ -2,7 +2,7 @@ {% block content %}

InvenTree

- +
{% include "InvenTree/starred_parts.html" with collapse_id="starred" %} {% if to_order %} From 4f84febbd11abc7852b14542a3f362e18526e84b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 7 May 2019 22:46:37 +1000 Subject: [PATCH 06/26] More intelligent BuildItem allocation - Set initial value for quantity based on how many parts are left to allocate - Auto select the StockItem to take from (if there is only one) --- InvenTree/build/models.py | 34 +++++++++++++++++++++++++++++ InvenTree/build/views.py | 21 +++++++++++++++++- InvenTree/templates/modal_form.html | 3 +++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 504b57cc75..2a998dc26e 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -13,9 +13,11 @@ from django.core.exceptions import ValidationError from django.urls import reverse from django.db import models, transaction +from django.db.models import Sum from django.core.validators import MinValueValidator from stock.models import StockItem +from part.models import BomItem class Build(models.Model): @@ -219,6 +221,38 @@ class Build(models.Model): self.status = self.COMPLETE self.save() + def getAllocatedQuantity(self, part): + """ Calculate the total number of currently allocated to this build + """ + + allocated = BuildItem.objects.filter(build=self.id, stock_item__part=part.id).aggregate(Sum('quantity')) + + q = allocated['quantity__sum'] + + if q: + return int(q) + else: + return 0 + + def getUnallocatedQuantity(self, part): + """ Calculate the quantity of which still needs to be allocated to this build. + + Args: + Part - the part to be tested + + Returns: + The remaining allocated quantity + """ + + try: + bom_item = BomItem.objects.get(part=self.part.id, sub_part=part.id) + except BomItem.DoesNotExist: + return 0 + + quantity = bom_item.quantity * self.quantity + + return quantity - self.getAllocatedQuantity(part) + @property def required_parts(self): """ Returns a dict of parts required to build this part (BOM) """ diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index cd2c87e814..cccd705e43 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -289,6 +289,12 @@ class BuildItemCreate(AjaxCreateView): query = query.exclude(id__in=[item.stock_item.id for item in BuildItem.objects.filter(build=build_id, stock_item__part=part_id)]) form.fields['stock_item'].queryset = query + + stocks = query.all() + # If there is only one item selected, select it + if len(stocks) == 1: + form.fields['stock_item'].initial = stocks[0].id + except Part.DoesNotExist: pass @@ -303,10 +309,23 @@ class BuildItemCreate(AjaxCreateView): initials = super(AjaxCreateView, self).get_initial().copy() build_id = self.get_param('build') + part_id = self.get_param('part') + + if part_id: + try: + part = Part.objects.get(pk=part_id) + except Part.DoesNotExist: + part = None if build_id: try: - initials['build'] = Build.objects.get(pk=build_id) + build = Build.objects.get(pk=build_id) + initials['build'] = build + + # Try to work out how many parts to allocate + if part: + initials['quantity'] = build.getUnallocatedQuantity(part) + except Build.DoesNotExist: pass diff --git a/InvenTree/templates/modal_form.html b/InvenTree/templates/modal_form.html index 566671c657..3c0674fec8 100644 --- a/InvenTree/templates/modal_form.html +++ b/InvenTree/templates/modal_form.html @@ -14,6 +14,9 @@ {% crispy form %} + + {% block form_data %} + {% endblock %} {% block post_form_content %} From 7dd7b68ed6df0909bbcf050964f8df20ed51a785 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 7 May 2019 23:03:05 +1000 Subject: [PATCH 07/26] Improve functions --- InvenTree/build/models.py | 19 +++++++++++-------- InvenTree/build/views.py | 7 ++++++- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 2a998dc26e..64cf8105c4 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -221,6 +221,16 @@ class Build(models.Model): self.status = self.COMPLETE self.save() + def getRequiredQuantity(self, part): + """ Calculate the quantity of required to make this build. + """ + + try: + item = BomItem.objects.get(part=self.part.id, sub_part=part.id) + return item.quantity * self.quantity + except BomItem.DoesNotExist: + return 0 + def getAllocatedQuantity(self, part): """ Calculate the total number of currently allocated to this build """ @@ -244,14 +254,7 @@ class Build(models.Model): The remaining allocated quantity """ - try: - bom_item = BomItem.objects.get(part=self.part.id, sub_part=part.id) - except BomItem.DoesNotExist: - return 0 - - quantity = bom_item.quantity * self.quantity - - return quantity - self.getAllocatedQuantity(part) + return max(self.getRequiredQuantity(part) - self.getAllocatedQuantity(part), 0) @property def required_parts(self): diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index cccd705e43..19af4b6458 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -294,6 +294,10 @@ class BuildItemCreate(AjaxCreateView): # If there is only one item selected, select it if len(stocks) == 1: form.fields['stock_item'].initial = stocks[0].id + # There is no stock available + elif len(stocks) == 0: + # TODO - Add a message to the form describing the problem + pass except Part.DoesNotExist: pass @@ -324,7 +328,8 @@ class BuildItemCreate(AjaxCreateView): # Try to work out how many parts to allocate if part: - initials['quantity'] = build.getUnallocatedQuantity(part) + unallocated = build.getUnallocatedQuantity(part) + initials['quantity'] = unallocated except Build.DoesNotExist: pass From 7101d9cb7e33eb491e59d9e6c5a995bd0a055984 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 7 May 2019 23:09:36 +1000 Subject: [PATCH 08/26] Add part description to build allocation table --- InvenTree/build/templates/build/allocate.html | 17 ++++++++++++----- .../build/templates/build/allocation_item.html | 3 ++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/InvenTree/build/templates/build/allocate.html b/InvenTree/build/templates/build/allocate.html index 0a3867af8e..d6657ea196 100644 --- a/InvenTree/build/templates/build/allocate.html +++ b/InvenTree/build/templates/build/allocate.html @@ -6,17 +6,24 @@

Allocate Parts for Build

-

{{ build.title }}

-{{ build.quantity }} x {{ build.part.name }} +
+
+ +

{{ build.title }}

+ {{ build.quantity }} x {{ build.part.name }} +
+
+
+ +
+
+

{% for bom_item in bom_items.all %} {% include "build/allocation_item.html" with item=bom_item build=build collapse_id=bom_item.id %} {% endfor %} -
- -
{% endblock %} diff --git a/InvenTree/build/templates/build/allocation_item.html b/InvenTree/build/templates/build/allocation_item.html index f9e92a63a8..2b8767a32c 100644 --- a/InvenTree/build/templates/build/allocation_item.html +++ b/InvenTree/build/templates/build/allocation_item.html @@ -3,7 +3,8 @@ {% load inventree_extras %} {% block collapse_title %} -{{ item.sub_part.name }} +{{ item.sub_part.name }}
+{{ item.sub_part.description }} {% endblock %} {% block collapse_heading %} From 29b94c91f386916931a3ec983b87e89a7ecb5c16 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 7 May 2019 23:28:46 +1000 Subject: [PATCH 09/26] Display part images in build allocation list (CSS needs some work) --- .../build/templates/build/allocation_item.html | 17 +++++++++++++++-- InvenTree/static/css/inventree.css | 8 ++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/InvenTree/build/templates/build/allocation_item.html b/InvenTree/build/templates/build/allocation_item.html index 2b8767a32c..3468d6d794 100644 --- a/InvenTree/build/templates/build/allocation_item.html +++ b/InvenTree/build/templates/build/allocation_item.html @@ -1,10 +1,23 @@ {% extends "collapse.html" %} +{% load static %} {% load inventree_extras %} {% block collapse_title %} -{{ item.sub_part.name }}
-{{ item.sub_part.description }} +
+
+ {{ item.sub_part.name }} + {% else %} + src="{% static 'img/blank_image.png' %}" alt='No image'> + {% endif %} +
+
+ {{ item.sub_part.name }}
+ {{ item.sub_part.description }} +
+
{% endblock %} {% block collapse_heading %} diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 78483daac5..924d4b65d7 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -159,6 +159,14 @@ margin-bottom: 5px; } +.panel-group .panel { + border-radius: 8px; +} + +.panel-heading { + padding: 2px 10px; +} + .float-right { float: right; } From 85869c07f62ab3143a431ec98e45128874839458 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 7 May 2019 23:31:41 +1000 Subject: [PATCH 10/26] Add 'allocated' column to Build detail view --- InvenTree/build/models.py | 3 ++- InvenTree/build/templates/build/detail.html | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 64cf8105c4..56f63db2e7 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -264,7 +264,8 @@ class Build(models.Model): for item in self.part.bom_items.all(): part = {'part': item.sub_part, 'per_build': item.quantity, - 'quantity': item.quantity * self.quantity + 'quantity': item.quantity * self.quantity, + 'allocated': self.getAllocatedQuantity(item.sub_part) } parts.append(part) diff --git a/InvenTree/build/templates/build/detail.html b/InvenTree/build/templates/build/detail.html index 96fad2caa5..9ca444f516 100644 --- a/InvenTree/build/templates/build/detail.html +++ b/InvenTree/build/templates/build/detail.html @@ -85,7 +85,8 @@ Part Required - Stock + Available + Allocated @@ -94,6 +95,7 @@ {{ item.part.name }} {{ item.quantity }} {{ item.part.total_stock }} + {{ item.allocated }} {% endfor %} From cd514bf1dba5ceb5c57f388ab271ff9c6f834dd7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 7 May 2019 23:39:42 +1000 Subject: [PATCH 11/26] Mark who a build was completed by --- InvenTree/build/templates/build/build_list.html | 2 +- InvenTree/static/css/inventree.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/build/templates/build/build_list.html b/InvenTree/build/templates/build/build_list.html index b423d214d9..6c28f47eda 100644 --- a/InvenTree/build/templates/build/build_list.html +++ b/InvenTree/build/templates/build/build_list.html @@ -27,7 +27,7 @@ {{ build.quantity }} {% include "build_status.html" with build=build %} {% if completed %} - {{ build.completion_date }} + {{ build.completion_date }}{{ build.completed_by.username }} {% else %} {{ build.creation_date }} {% endif %} diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 924d4b65d7..2104bf92af 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -164,7 +164,7 @@ } .panel-heading { - padding: 2px 10px; + padding: 5px 10px; } .float-right { From 40d8332e7f30d91f5fb477ae8c7b5816b933982f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 07:48:39 +1000 Subject: [PATCH 12/26] Add 'allocated' status code to build - This means that parts have been taken from stock --- .../migrations/0011_auto_20190508_0748.py | 19 +++++++++++++++++++ InvenTree/build/models.py | 2 ++ InvenTree/templates/build_status.html | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 InvenTree/build/migrations/0011_auto_20190508_0748.py diff --git a/InvenTree/build/migrations/0011_auto_20190508_0748.py b/InvenTree/build/migrations/0011_auto_20190508_0748.py new file mode 100644 index 0000000000..e4c0ec5fa7 --- /dev/null +++ b/InvenTree/build/migrations/0011_auto_20190508_0748.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-05-07 21:48 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('build', '0010_auto_20190505_2233'), + ] + + operations = [ + migrations.AlterField( + model_name='build', + name='status', + field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Allocated'), (30, 'Cancelled'), (40, 'Complete')], default=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + ] diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 56f63db2e7..d4a8e70448 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -62,11 +62,13 @@ class Build(models.Model): # Build status codes PENDING = 10 # Build is pending / active + ALLOCATED = 20 # Parts have been removed from stock CANCELLED = 30 # Build was cancelled COMPLETE = 40 # Build is complete #: Build status codes BUILD_STATUS_CODES = {PENDING: _("Pending"), + ALLOCATED: _("Allocated"), CANCELLED: _("Cancelled"), COMPLETE: _("Complete"), } diff --git a/InvenTree/templates/build_status.html b/InvenTree/templates/build_status.html index 1702ceccae..3fc5f6086c 100644 --- a/InvenTree/templates/build_status.html +++ b/InvenTree/templates/build_status.html @@ -1,7 +1,7 @@ {% if build.status == build.PENDING %} -{% elif build.status == build.HOLDING %} - +{% elif build.status == build.ALLOCATED %} + {% elif build.status == build.CANCELLED %} {% elif build.status == build.COMPLETE %} From d96989f6405f20f2648c6ae00183e28726a7924d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 13:42:20 +1000 Subject: [PATCH 13/26] Reorder BOM table --- InvenTree/static/script/inventree/bom.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/InvenTree/static/script/inventree/bom.js b/InvenTree/static/script/inventree/bom.js index 777acbd4ef..55a629fada 100644 --- a/InvenTree/static/script/inventree/bom.js +++ b/InvenTree/static/script/inventree/bom.js @@ -113,6 +113,16 @@ function loadBomTable(table, options) { sortable: true, } ); + + // Part notes + cols.push( + { + field: 'note', + title: 'Notes', + searchable: true, + sortable: false, + } + ); if (options.editable) { cols.push({ @@ -124,6 +134,7 @@ function loadBomTable(table, options) { } }); } + else { cols.push( { @@ -149,16 +160,6 @@ function loadBomTable(table, options) { ); } - // Part notes - cols.push( - { - field: 'note', - title: 'Notes', - searchable: true, - sortable: false, - } - ); - // Configure the table (bootstrap-table) table.bootstrapTable({ From 6a04e8b059830139339fc4f1ba791e5824251498 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 14:14:46 +1000 Subject: [PATCH 14/26] Allow sorting by BOM notes --- InvenTree/static/script/inventree/bom.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/static/script/inventree/bom.js b/InvenTree/static/script/inventree/bom.js index 55a629fada..b96ae49050 100644 --- a/InvenTree/static/script/inventree/bom.js +++ b/InvenTree/static/script/inventree/bom.js @@ -120,7 +120,7 @@ function loadBomTable(table, options) { field: 'note', title: 'Notes', searchable: true, - sortable: false, + sortable: true, } ); @@ -159,7 +159,7 @@ function loadBomTable(table, options) { } ); } - + // Configure the table (bootstrap-table) table.bootstrapTable({ From 416b5c982072f4b9084132e3b12f5d88de8406b8 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 15:25:28 +1000 Subject: [PATCH 15/26] Include part image URL in Part API --- InvenTree/part/models.py | 10 ++++++++++ InvenTree/part/serializers.py | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 2a3c31552e..3bca8cd445 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -14,6 +14,7 @@ import tablib from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from django.urls import reverse +from django.conf import settings from django.db import models from django.core.validators import MinValueValidator @@ -120,8 +121,17 @@ class Part(models.Model): """ def get_absolute_url(self): + """ Return the web URL for viewing this part """ return reverse('part-detail', kwargs={'pk': self.id}) + def get_image_url(self): + """ Return the URL of the image for this part """ + + if self.image: + return os.path.join(settings.MEDIA_URL, str(self.image.url)) + else: + return '' + # Short name of the part name = models.CharField(max_length=100, unique=True, blank=False, help_text='Part name (must be unique)') diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 847b3bb1a8..4621816343 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -33,6 +33,7 @@ class PartBriefSerializer(serializers.ModelSerializer): """ Serializer for Part (brief detail) """ url = serializers.CharField(source='get_absolute_url', read_only=True) + image_url = serializers.CharField(source='get_image_url', read_only=True) class Meta: model = Part @@ -40,6 +41,7 @@ class PartBriefSerializer(serializers.ModelSerializer): 'pk', 'url', 'name', + 'image_url', 'description', 'available_stock', ] @@ -51,6 +53,7 @@ class PartSerializer(serializers.ModelSerializer): """ url = serializers.CharField(source='get_absolute_url', read_only=True) + image_url = serializers.CharField(source='get_image_url', read_only=True) category_name = serializers.CharField(source='category_path', read_only=True) class Meta: @@ -60,6 +63,7 @@ class PartSerializer(serializers.ModelSerializer): 'pk', 'url', # Link to the part detail page 'name', + 'image_url', 'IPN', 'URL', # Link to an external URL (optional) 'description', From ab76525da212317415012597c94d52c8f64f8a95 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 15:53:03 +1000 Subject: [PATCH 16/26] Initially sort part list by 'name' --- InvenTree/part/templates/part/category.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index 7dad872114..220ebc9d65 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -122,7 +122,7 @@ $("#part-table").bootstrapTable({ sortable: true, search: true, - sortName: 'description', + sortName: 'name', idField: 'pk', method: 'get', pagination: true, From fb38ddb1b3b263cbbfcc3e3b0629ade631c226e1 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 17:57:31 +1000 Subject: [PATCH 17/26] Limit choices for 'parent' field when editing PartCategory --- InvenTree/part/views.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index c97808888e..a018d578bf 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -404,12 +404,30 @@ class CategoryEdit(AjaxUpdateView): context = super(CategoryEdit, self).get_context_data(**kwargs).copy() try: - context['category'] = PartCategory.objects.get(pk=self.kwargs['pk']) + context['category'] = self.get_object() except: pass return context + def get_form(self): + """ Customize form data for PartCategory editing. + + Limit the choices for 'parent' field to those which make sense + """ + + form = super(AjaxUpdateView, self).get_form() + + category = self.get_object() + + # Remove any invalid choices for the parent category part + parent_choices = PartCategory.objects.all() + parent_choices = parent_choices.exclude(id__in=category.getUniqueChildren()) + + form.fields['parent'].queryset = parent_choices + + return form + class CategoryDelete(AjaxDeleteView): """ Delete view to delete a PartCategory """ From afffd06fb8d9833432fe08ddde295517d9ecb082 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 18:00:34 +1000 Subject: [PATCH 18/26] Limit choices for 'parent' field when editing StockLocation --- InvenTree/stock/views.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 1f6f85ea1a..e618be1ee8 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -75,6 +75,24 @@ class StockLocationEdit(AjaxUpdateView): ajax_template_name = 'modal_form.html' ajax_form_title = 'Edit Stock Location' + def get_form(self): + """ Customize form data for StockLocation editing. + + Limit the choices for 'parent' field to those which make sense. + """ + + form = super(AjaxUpdateView, self).get_form() + + location = self.get_object() + + # Remove any invalid choices for the 'parent' field + parent_choices = StockLocation.objects.all() + parent_choices = parent_choices.exclude(id__in=location.getUniqueChildren()) + + form.fields['parent'].queryset = parent_choices + + return form + class StockLocationQRCode(QRCodeView): """ View for displaying a QR code for a StockLocation object """ From 2a662249524839515f68176928d0e0feb60be8ab Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 19:15:41 +1000 Subject: [PATCH 19/26] Part image hover preview working in part-category-list - Yay for CSS! --- InvenTree/part/templates/part/category.html | 2 +- InvenTree/static/css/inventree.css | 24 +++++++++++++++++++ .../static/script/inventree/inventree.js | 15 ++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index 220ebc9d65..b0b7fe44a1 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -152,7 +152,7 @@ title: 'Part', sortable: true, formatter: function(value, row, index, field) { - return renderLink(value, row.url); + return imageHoverIcon(row.image_url) + renderLink(value, row.url); } }, { diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 2104bf92af..17802bd58e 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -10,6 +10,30 @@ color: #ffcc00; } +/* Part image icons with full-display on mouse hover */ + +.hover-img-thumb { + width: 28px; + height: 28px; + border: 1px solid #cce; +} + +.hover-img-large { + display: none; + position: absolute; + z-index: 999; + border: 1px solid #555; + max-width: 250px; +} + +.hover-icon { + margin-right: 10px; +} + +.hover-icon:hover > .hover-img-large { + display: block; +} + /* dropzone class - for Drag-n-Drop file uploads */ .dropzone { border: 1px solid #555; diff --git a/InvenTree/static/script/inventree/inventree.js b/InvenTree/static/script/inventree/inventree.js index cc025477c0..48ba874262 100644 --- a/InvenTree/static/script/inventree/inventree.js +++ b/InvenTree/static/script/inventree/inventree.js @@ -117,4 +117,19 @@ function enableDragAndDrop(element, url, options) { console.log('Ignoring drag-and-drop event (not a file)'); } }); +} + +function imageHoverIcon(url) { + /* Render a small thumbnail icon for an image. + * On mouseover, display a full-size version of the image + */ + + var html = ` + + + + + `; + + return html; } \ No newline at end of file From 0058207fadfbc0a407c5dc924b295aa1259da22b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 19:31:43 +1000 Subject: [PATCH 20/26] Functionalize loadPartTable --- InvenTree/part/templates/part/category.html | 77 ++----------------- InvenTree/static/script/inventree/part.js | 85 +++++++++++++++++++++ 2 files changed, 92 insertions(+), 70 deletions(-) diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index b0b7fe44a1..b9695ea6f2 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -119,81 +119,18 @@ {% endif %} - $("#part-table").bootstrapTable({ - sortable: true, - search: true, - sortName: 'name', - idField: 'pk', - method: 'get', - pagination: true, - rememberOrder: true, - queryParams: function(p) { - return { - active: true, + loadPartTable( + "#part-table", + "{% url 'api-part-list' %}", + { + query: { {% if category %} category: {{ category.id }}, include_child_categories: true, {% endif %} - } + }, + buttons: ['#part-options'], }, - columns: [ - { - checkbox: true, - title: 'Select', - searchable: false, - }, - { - field: 'pk', - title: 'ID', - visible: false, - }, - { - field: 'name', - title: 'Part', - sortable: true, - formatter: function(value, row, index, field) { - return imageHoverIcon(row.image_url) + renderLink(value, row.url); - } - }, - { - sortable: true, - field: 'description', - title: 'Description', - }, - { - sortable: true, - field: 'category_name', - title: 'Category', - formatter: function(value, row, index, field) { - if (row.category) { - return renderLink(row.category_name, "/part/category/" + row.category + "/"); - } - else { - return ''; - } - } - }, - { - field: 'total_stock', - title: 'Stock', - searchable: false, - sortable: true, - formatter: function(value, row, index, field) { - if (value) { - return renderLink(value, row.url + 'stock/'); - } - else { - return "No stock"; - } - } - } - ], - url: "{% url 'api-part-list' %}", - }); - - linkButtonsToSelection( - $("#part-table"), - ['#part-options'] ); {% endblock %} \ No newline at end of file diff --git a/InvenTree/static/script/inventree/part.js b/InvenTree/static/script/inventree/part.js index c3b0f8182d..41098ba71b 100644 --- a/InvenTree/static/script/inventree/part.js +++ b/InvenTree/static/script/inventree/part.js @@ -72,4 +72,89 @@ function toggleStar(options) { }, } ); +} + +function loadPartTable(table, url, options={}) { + /* Load part listing data into specified table. + * + * Args: + * - table: HTML reference to the table + * - url: Base URL for API query + * - options: object containing following (optional) fields + * query: extra query params for API request + * buttons: If provided, link buttons to selection status of this table + */ + + // Default query params + options.active = true; + + $(table).bootstrapTable({ + url: url, + sortable: true, + search: true, + sortName: 'name', + method: 'get', + pagination: true, + pageSize: 25, + rememberOrder: true, + queryParams: function(p) { + return options; + }, + columns: [ + { + checkbox: true, + title: 'Select', + searchable: false, + }, + { + field: 'pk', + title: 'ID', + visible: false, + }, + { + field: 'name', + title: 'Part', + sortable: true, + formatter: function(value, row, index, field) { + return imageHoverIcon(row.image_url) + renderLink(value, row.url); + } + }, + { + sortable: true, + field: 'description', + title: 'Description', + }, + { + sortable: true, + field: 'category_name', + title: 'Category', + formatter: function(value, row, index, field) { + if (row.category) { + return renderLink(row.category_name, "/part/category/" + row.category + "/"); + } + else { + return ''; + } + } + }, + { + field: 'total_stock', + title: 'Stock', + searchable: false, + sortable: true, + formatter: function(value, row, index, field) { + if (value) { + return renderLink(value, row.url + 'stock/'); + } + else { + return "No stock"; + } + } + } + ], + }); + + if (options.buttons) { + linkButtonsToSelection($(table), options.buttons); + } } \ No newline at end of file From 88a1df75d2f4a771d9ffc1bdc0566f0346193d4f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 19:32:00 +1000 Subject: [PATCH 21/26] Render part image in the stock location list display --- InvenTree/static/script/inventree/stock.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/InvenTree/static/script/inventree/stock.js b/InvenTree/static/script/inventree/stock.js index 71bbed2ab3..f2cd50c731 100644 --- a/InvenTree/static/script/inventree/stock.js +++ b/InvenTree/static/script/inventree/stock.js @@ -347,6 +347,7 @@ function loadStockTable(table, options) { search: true, method: 'get', pagination: true, + pageSize: 25, rememberOrder: true, queryParams: options.params, columns: [ @@ -365,7 +366,7 @@ function loadStockTable(table, options) { title: 'Part', sortable: true, formatter: function(value, row, index, field) { - return renderLink(value, row.part.url); + return imageHoverIcon(row.part.image_url) + renderLink(value, row.part.url); } }, { From 0754afd5f54e4bf4140a11ed34744bf123d3eaf4 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 20:16:53 +1000 Subject: [PATCH 22/26] Display icons in supplier_part list - Bug fix for part list API query --- InvenTree/company/templates/company/detail_part.html | 4 ++-- InvenTree/part/serializers.py | 4 ++-- InvenTree/static/script/inventree/part.js | 6 ++++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/InvenTree/company/templates/company/detail_part.html b/InvenTree/company/templates/company/detail_part.html index 797446472f..24982c021f 100644 --- a/InvenTree/company/templates/company/detail_part.html +++ b/InvenTree/company/templates/company/detail_part.html @@ -51,10 +51,10 @@ }, { sortable: true, - field: 'part_name', + field: 'part_detail.name', title: 'Part', formatter: function(value, row, index, field) { - return renderLink(value, '/part/' + row.part + '/suppliers/'); + return imageHoverIcon(row.part_detail.image_url) + renderLink(value, '/part/' + row.part + '/suppliers/'); } }, { diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 4621816343..7a12243a81 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -125,7 +125,7 @@ class SupplierPartSerializer(serializers.ModelSerializer): url = serializers.CharField(source='get_absolute_url', read_only=True) - part_name = serializers.CharField(source='part.name', read_only=True) + part_detail = PartBriefSerializer(source='part', many=False, read_only=True) supplier_name = serializers.CharField(source='supplier.name', read_only=True) @@ -135,7 +135,7 @@ class SupplierPartSerializer(serializers.ModelSerializer): 'pk', 'url', 'part', - 'part_name', + 'part_detail', 'supplier', 'supplier_name', 'SKU', diff --git a/InvenTree/static/script/inventree/part.js b/InvenTree/static/script/inventree/part.js index 41098ba71b..811f1987ab 100644 --- a/InvenTree/static/script/inventree/part.js +++ b/InvenTree/static/script/inventree/part.js @@ -86,7 +86,9 @@ function loadPartTable(table, url, options={}) { */ // Default query params - options.active = true; + query = options.query; + + query.active = true; $(table).bootstrapTable({ url: url, @@ -98,7 +100,7 @@ function loadPartTable(table, url, options={}) { pageSize: 25, rememberOrder: true, queryParams: function(p) { - return options; + return query; }, columns: [ { From 58e5b10d26f01a30ca2dc8042d4e00ac1d220112 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 20:23:54 +1000 Subject: [PATCH 23/26] Display supplier logo in part suppliers list - Also add a background colour for popup image in case of transparent image file --- InvenTree/company/models.py | 9 +++++++++ InvenTree/part/serializers.py | 2 ++ InvenTree/part/templates/part/supplier.html | 2 +- InvenTree/static/css/inventree.css | 2 ++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 1d119ddf51..bb86230b70 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -9,6 +9,7 @@ import os from django.db import models from django.urls import reverse +from django.conf import settings def rename_company_image(instance, filename): @@ -78,6 +79,14 @@ class Company(models.Model): """ Get the web URL for the detail view for this Company """ return reverse('company-detail', kwargs={'pk': self.id}) + def get_image_url(self): + """ Return the URL of the image for this company """ + + if self.image: + return os.path.join(settings.MEDIA_URL, str(self.image.url)) + else: + return '' + @property def part_count(self): """ The number of parts supplied by this company """ diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 7a12243a81..97588650cb 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -128,6 +128,7 @@ class SupplierPartSerializer(serializers.ModelSerializer): part_detail = PartBriefSerializer(source='part', many=False, read_only=True) supplier_name = serializers.CharField(source='supplier.name', read_only=True) + supplier_logo = serializers.CharField(source='supplier.get_image_url', read_only=True) class Meta: model = SupplierPart @@ -138,6 +139,7 @@ class SupplierPartSerializer(serializers.ModelSerializer): 'part_detail', 'supplier', 'supplier_name', + 'supplier_logo', 'SKU', 'manufacturer', 'MPN', diff --git a/InvenTree/part/templates/part/supplier.html b/InvenTree/part/templates/part/supplier.html index 00f7fa41d0..4999694651 100644 --- a/InvenTree/part/templates/part/supplier.html +++ b/InvenTree/part/templates/part/supplier.html @@ -63,7 +63,7 @@ field: 'supplier_name', title: 'Supplier', formatter: function(value, row, index, field) { - return renderLink(value, '/company/' + row.supplier + '/'); + return imageHoverIcon(row.supplier_logo) + renderLink(value, '/company/' + row.supplier + '/'); } }, { diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 17802bd58e..5147547275 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -13,12 +13,14 @@ /* Part image icons with full-display on mouse hover */ .hover-img-thumb { + background: #eee; width: 28px; height: 28px; border: 1px solid #cce; } .hover-img-large { + background: #eee; display: none; position: absolute; z-index: 999; From 1f9aa7a8fc6cc3712792fa89e9be725b81ac98f0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 20:26:39 +1000 Subject: [PATCH 24/26] Display thumbnail in Part 'used_in' detail view --- InvenTree/part/templates/part/used_in.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/templates/part/used_in.html b/InvenTree/part/templates/part/used_in.html index eca07a585d..f1a055695e 100644 --- a/InvenTree/part/templates/part/used_in.html +++ b/InvenTree/part/templates/part/used_in.html @@ -43,7 +43,7 @@ field: 'part_detail', title: 'Part', formatter: function(value, row, index, field) { - return renderLink(value.name, value.url + 'bom/'); + return imageHoverIcon(row.part_detail.image_url) + renderLink(value.name, value.url + 'bom/'); } }, { From b4b9d1514e6ac379deb021fec146e30545f619a5 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 22:03:59 +1000 Subject: [PATCH 25/26] Mark 'inactive' parts in part list table - Fix part display in search results page --- InvenTree/static/script/inventree/part.js | 15 ++++-- InvenTree/templates/InvenTree/search.html | 60 ++++------------------- InvenTree/templates/base.html | 1 + 3 files changed, 22 insertions(+), 54 deletions(-) diff --git a/InvenTree/static/script/inventree/part.js b/InvenTree/static/script/inventree/part.js index 811f1987ab..a991cb232c 100644 --- a/InvenTree/static/script/inventree/part.js +++ b/InvenTree/static/script/inventree/part.js @@ -81,14 +81,18 @@ function loadPartTable(table, url, options={}) { * - table: HTML reference to the table * - url: Base URL for API query * - options: object containing following (optional) fields + * allowInactive: If true, allow display of inactive parts * query: extra query params for API request * buttons: If provided, link buttons to selection status of this table */ // Default query params query = options.query; - - query.active = true; + + if (!options.allowInactive) { + // Only display active parts + query.active = true; + } $(table).bootstrapTable({ url: url, @@ -99,6 +103,7 @@ function loadPartTable(table, url, options={}) { pagination: true, pageSize: 25, rememberOrder: true, + formatNoMatches: function() { return "No parts found"; }, queryParams: function(p) { return query; }, @@ -118,7 +123,11 @@ function loadPartTable(table, url, options={}) { title: 'Part', sortable: true, formatter: function(value, row, index, field) { - return imageHoverIcon(row.image_url) + renderLink(value, row.url); + var display = imageHoverIcon(row.image_url) + renderLink(value, row.url); + if (!row.active) { + display = display + "INACTIVE"; + } + return display; } }, { diff --git a/InvenTree/templates/InvenTree/search.html b/InvenTree/templates/InvenTree/search.html index e868378ee4..cd700facc8 100644 --- a/InvenTree/templates/InvenTree/search.html +++ b/InvenTree/templates/InvenTree/search.html @@ -22,57 +22,15 @@ var n = $("#part-results-table").bootstrapTable('getData').length; $("#part-result-count").html("(found " + n + " results)"); }); - - $("#part-results-table").bootstrapTable({ - sortable: true, - search: true, - pagination: true, - formatNoMatches: function() { return "No parts found matching search query"; }, - queryParams: function(p) { - return { + + loadPartTable("#part-results-table", + "{% url 'api-part-list' %}", + { + query: { search: "{{ query }}", - } - }, - columns: [ - { - field: 'pk', - title: 'ID', - visible: false, }, - { - field: 'name', - title: 'Name', - sortable: true, - searchable: true, - formatter: function(value, row, index, field) { - return renderLink(value, row.url); - } - }, - { - field: 'IPN', - title: 'Internal Part Number', - searchable: true, - }, - { - field: 'description', - title: 'Description', - searchable: true, - }, - { - field: 'available_stock', - title: 'Stock', - formatter: function(value, row, index, field) { - if (value) { - return renderLink(value, row.url + 'stock/'); - } else { - return renderLink('No stock', row.url + 'stock/'); - } - } - }, - ], - url: "{% url 'api-part-list' %}" - }); - - - + allowInactive: true, + } + ); + {% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 84e2e10963..90925cfe7e 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -86,6 +86,7 @@ InvenTree + From 8fbba6846f856b26ede2a79a9f712176d7f1c8c1 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 8 May 2019 22:05:46 +1000 Subject: [PATCH 26/26] Display part description in stock table --- InvenTree/static/script/inventree/stock.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/InvenTree/static/script/inventree/stock.js b/InvenTree/static/script/inventree/stock.js index f2cd50c731..2f55456cdb 100644 --- a/InvenTree/static/script/inventree/stock.js +++ b/InvenTree/static/script/inventree/stock.js @@ -369,6 +369,11 @@ function loadStockTable(table, options) { return imageHoverIcon(row.part.image_url) + renderLink(value, row.part.url); } }, + { + field: 'part.description', + title: 'Description', + sortable: true, + }, { field: 'location', title: 'Location',