From dedc25d68155b26d5b44ed5d36e2e8fd2f6edfee Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 13 Jun 2020 20:46:19 +1000 Subject: [PATCH 1/4] Update verbose names for some fields --- .../migrations/0022_auto_20200613_1045.py | 55 +++++++++++++++++++ InvenTree/company/models.py | 13 +++-- 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 InvenTree/company/migrations/0022_auto_20200613_1045.py diff --git a/InvenTree/company/migrations/0022_auto_20200613_1045.py b/InvenTree/company/migrations/0022_auto_20200613_1045.py new file mode 100644 index 0000000000..66cd1231b8 --- /dev/null +++ b/InvenTree/company/migrations/0022_auto_20200613_1045.py @@ -0,0 +1,55 @@ +# Generated by Django 3.0.7 on 2020-06-13 10:45 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0045_auto_20200605_0932'), + ('company', '0021_remove_supplierpart_manufacturer_name'), + ] + + operations = [ + migrations.AlterField( + model_name='company', + name='address', + field=models.CharField(blank=True, help_text='Company address', max_length=200, verbose_name='Address'), + ), + migrations.AlterField( + model_name='company', + name='contact', + field=models.CharField(blank=True, help_text='Point of contact', max_length=100, verbose_name='Contact'), + ), + migrations.AlterField( + model_name='company', + name='description', + field=models.CharField(help_text='Description of the company', max_length=500, verbose_name='Company description'), + ), + migrations.AlterField( + model_name='company', + name='email', + field=models.EmailField(blank=True, help_text='Contact email address', max_length=254, verbose_name='Email'), + ), + migrations.AlterField( + model_name='company', + name='name', + field=models.CharField(help_text='Company name', max_length=100, unique=True, verbose_name='Company name'), + ), + migrations.AlterField( + model_name='company', + name='phone', + field=models.CharField(blank=True, help_text='Contact phone number', max_length=50, verbose_name='Phone number'), + ), + migrations.AlterField( + model_name='company', + name='website', + field=models.URLField(blank=True, help_text='Company website URL', verbose_name='Website'), + ), + migrations.AlterField( + model_name='supplierpart', + name='part', + field=models.ForeignKey(help_text='Select part', limit_choices_to={'is_template': False, 'purchaseable': True}, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part', verbose_name='Base Part'), + ), + ] diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index ec87619cdb..2179897263 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -80,21 +80,25 @@ class Company(models.Model): """ name = models.CharField(max_length=100, blank=False, unique=True, - help_text=_('Company name')) + help_text=_('Company name'), + verbose_name=_('Company name')) - description = models.CharField(max_length=500, help_text=_('Description of the company')) + description = models.CharField(max_length=500, verbose_name=_('Company description'), help_text=_('Description of the company')) - website = models.URLField(blank=True, help_text=_('Company website URL')) + website = models.URLField(blank=True, verbose_name=_('Website'), help_text=_('Company website URL')) address = models.CharField(max_length=200, + verbose_name=_('Address'), blank=True, help_text=_('Company address')) phone = models.CharField(max_length=50, + verbose_name=_('Phone number'), blank=True, help_text=_('Contact phone number')) - email = models.EmailField(blank=True, help_text=_('Contact email address')) + email = models.EmailField(blank=True, verbose_name=_('Email'), help_text=_('Contact email address')) contact = models.CharField(max_length=100, + verbose_name=_('Contact'), blank=True, help_text=_('Point of contact')) link = InvenTreeURLField(blank=True, help_text=_('Link to external company information')) @@ -269,6 +273,7 @@ class SupplierPart(models.Model): part = models.ForeignKey('part.Part', on_delete=models.CASCADE, related_name='supplier_parts', + verbose_name=_('Base Part'), limit_choices_to={ 'purchaseable': True, 'is_template': False, From 930f903f5d8c4139d2f151d6a51c425430a79537 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 22 Jun 2020 10:48:41 +1000 Subject: [PATCH 2/4] Add ability to display "out of stock" items against a part --- InvenTree/InvenTree/static/script/inventree/filters.js | 2 +- InvenTree/part/templates/part/stock.html | 1 - InvenTree/part/templates/part/tabs.html | 9 --------- InvenTree/stock/serializers.py | 2 ++ InvenTree/stock/templates/stock/location.html | 1 - InvenTree/templates/js/stock.html | 9 +++++++-- InvenTree/templates/js/table_filters.html | 5 +++++ 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/InvenTree/InvenTree/static/script/inventree/filters.js b/InvenTree/InvenTree/static/script/inventree/filters.js index 3209ba3beb..df767441d4 100644 --- a/InvenTree/InvenTree/static/script/inventree/filters.js +++ b/InvenTree/InvenTree/static/script/inventree/filters.js @@ -14,7 +14,7 @@ function defaultFilters() { return { - stock: "cascade=1", + stock: "cascade=1&in_stock=1", build: "", parts: "cascade=1", company: "", diff --git a/InvenTree/part/templates/part/stock.html b/InvenTree/part/templates/part/stock.html index 03174f45c3..3030363476 100644 --- a/InvenTree/part/templates/part/stock.html +++ b/InvenTree/part/templates/part/stock.html @@ -40,7 +40,6 @@ part: {{ part.id }}, location_detail: true, part_detail: true, - in_stock: true, }, groupByField: 'location', buttons: [ diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index 82d727eeb3..ffb92cf191 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -51,15 +51,6 @@ {% endif %} {% if part.trackable %} - {% if 0 %} - - - {% trans "Tracking" %} - {% if parts.serials.all|length > 0 %} - {{ part.serials.all|length }} - {% endif %} - - {% endif %} {% trans "Tests" %} {% if part.getTestTemplates.count > 0 %}{{ part.getTestTemplates.count }}{% endif %} diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index bde9b2b25b..07fdd7952e 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -71,6 +71,7 @@ class StockItemSerializer(InvenTreeModelSerializer): 'belongs_to', 'build', 'build_order', + 'customer', 'sales_order', 'supplier_part', 'supplier_part__supplier', @@ -141,6 +142,7 @@ class StockItemSerializer(InvenTreeModelSerializer): 'batch', 'build_order', 'belongs_to', + 'customer', 'in_stock', 'link', 'location', diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 20032e557c..7e6a4fab42 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -248,7 +248,6 @@ {% endif %} part_detail: true, location_detail: true, - in_stock: true, }, url: "{% url 'api-stock-list' %}", }); diff --git a/InvenTree/templates/js/stock.html b/InvenTree/templates/js/stock.html index 789667b006..2600a1635d 100644 --- a/InvenTree/templates/js/stock.html +++ b/InvenTree/templates/js/stock.html @@ -494,10 +494,15 @@ function loadStockTable(table, options) { sortable: true, formatter: function(value, row, index, field) { if (value) { - return renderLink(value, '/stock/location/' + row.location + '/'); + return renderLink(value, `/stock/location/${row.location}/`); } else { - return '{% trans "No stock location set" %}'; + if (row.customer) { + var text = "{% trans "Shipped to customer" %}"; + return renderLink(text, `/company/${row.customer}/assigned-stock/`); + } else { + return '{% trans "No stock location set" %}'; + } } } }, diff --git a/InvenTree/templates/js/table_filters.html b/InvenTree/templates/js/table_filters.html index bec29b0487..3c9381331c 100644 --- a/InvenTree/templates/js/table_filters.html +++ b/InvenTree/templates/js/table_filters.html @@ -14,6 +14,11 @@ function getAvailableTableFilters(tableKey) { // Filters for the "Stock" table if (tableKey == 'stock') { return { + in_stock: { + type: 'bool', + title: '{% trans "In Stock" %}', + description: '{% trans "Show items which are in stock" %}', + }, cascade: { type: 'bool', title: '{% trans "Include sublocations" %}', From fa3d2d005a60dc7366b87c5625e394974eed4003 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 22 Jun 2020 11:27:59 +1000 Subject: [PATCH 3/4] "Active" part toggle is always enabled --- InvenTree/part/templates/part/detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 1e7d36e127..18970fe373 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -191,7 +191,7 @@ {% trans "Active" %} - {% include "slide.html" with state=part.active field='active' %} + {% include "slide.html" with state=part.active field='active' disabled=False %} {% if part.active %} {% trans "Part is active" %} {% else %} From 3d245c7ce3c6e75beb63878a71734333ef4a7281 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Jun 2020 19:14:51 +1000 Subject: [PATCH 4/4] Improve logic of "quantity_to_order" function --- InvenTree/part/models.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 47e22fc938..550645cb68 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -594,7 +594,16 @@ class Part(MPTTModel): def quantity_to_order(self): """ Return the quantity needing to be ordered for this part. """ - required = -1 * self.net_stock + # How many do we need to have "on hand" at any point? + required = self.net_stock - self.minimum_stock + + if required < 0: + return abs(required) + + # Do not need to order any + return 0 + + required = self.net_stock return max(required, 0) @property