From 6731bc1b066a40826d72b139c5982b7a9da1a1b6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 18 Mar 2020 22:22:40 +1100 Subject: [PATCH 1/6] Implement auto-rounding decimal field Ref: https://stackoverflow.com/questions/37958130/automatically-round-djangos-decimalfield-according-to-the-max-digits-and-decima --- InvenTree/InvenTree/fields.py | 32 +++++++++++++++++++ InvenTree/company/forms.py | 5 +++ .../migrations/0011_auto_20200318_1114.py | 20 ++++++++++++ .../migrations/0012_auto_20200318_1114.py | 20 ++++++++++++ InvenTree/company/models.py | 6 ++-- 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 InvenTree/company/migrations/0011_auto_20200318_1114.py create mode 100644 InvenTree/company/migrations/0012_auto_20200318_1114.py diff --git a/InvenTree/InvenTree/fields.py b/InvenTree/InvenTree/fields.py index 7a2fa4b89a..b3393ef1eb 100644 --- a/InvenTree/InvenTree/fields.py +++ b/InvenTree/InvenTree/fields.py @@ -8,6 +8,8 @@ from .validators import allowable_url_schemes from django.forms.fields import URLField as FormURLField from django.db import models as models from django.core import validators +from django import forms +from decimal import Decimal class InvenTreeURLFormField(FormURLField): @@ -25,3 +27,33 @@ class InvenTreeURLField(models.URLField): return super().formfield(**{ 'form_class': InvenTreeURLFormField }) + + +def round_decimal(value, places): + """ + Round value to the specified number of places. + """ + + if value is not None: + # see https://docs.python.org/2/library/decimal.html#decimal.Decimal.quantize for options + return value.quantize(Decimal(10) ** -places) + return value + + +class RoundingDecimalFormField(forms.DecimalField): + def to_python(self, value): + value = super(RoundingDecimalFormField, self).to_python(value) + return round_decimal(value, self.decimal_places) + + +class RoundingDecimalField(models.DecimalField): + def to_python(self, value): + value = super(RoundingDecimalField, self).to_python(value) + return round_decimal(value, self.decimal_places) + + def formfield(self, **kwargs): + defaults = { + 'form_class': RoundingDecimalFormField + } + defaults.update(kwargs) + return super(RoundingDecimalField, self).formfield(**kwargs) diff --git a/InvenTree/company/forms.py b/InvenTree/company/forms.py index 71b870497a..3190149e24 100644 --- a/InvenTree/company/forms.py +++ b/InvenTree/company/forms.py @@ -6,6 +6,7 @@ Django Forms for interacting with Company app from __future__ import unicode_literals from InvenTree.forms import HelperForm +from InvenTree.fields import RoundingDecimalFormField from .models import Company from .models import SupplierPart @@ -64,6 +65,10 @@ class EditSupplierPartForm(HelperForm): class EditPriceBreakForm(HelperForm): """ Form for creating / editing a supplier price break """ + quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5) + + cost = RoundingDecimalFormField(max_digits=10, decimal_places=5) + class Meta: model = SupplierPriceBreak fields = [ diff --git a/InvenTree/company/migrations/0011_auto_20200318_1114.py b/InvenTree/company/migrations/0011_auto_20200318_1114.py new file mode 100644 index 0000000000..cbfa73942b --- /dev/null +++ b/InvenTree/company/migrations/0011_auto_20200318_1114.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.9 on 2020-03-18 11:14 + +import InvenTree.fields +import django.core.validators +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('company', '0010_auto_20200201_1231'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierpricebreak', + name='cost', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, max_digits=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + ] diff --git a/InvenTree/company/migrations/0012_auto_20200318_1114.py b/InvenTree/company/migrations/0012_auto_20200318_1114.py new file mode 100644 index 0000000000..ff99b91cab --- /dev/null +++ b/InvenTree/company/migrations/0012_auto_20200318_1114.py @@ -0,0 +1,20 @@ +# Generated by Django 2.2.9 on 2020-03-18 11:14 + +import InvenTree.fields +import django.core.validators +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('company', '0011_auto_20200318_1114'), + ] + + operations = [ + migrations.AlterField( + model_name='supplierpricebreak', + name='quantity', + field=InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, max_digits=15, validators=[django.core.validators.MinValueValidator(1)]), + ), + ] diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index a5e56f99fb..b079c084fc 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -21,7 +21,7 @@ from django.conf import settings from markdownx.models import MarkdownxField -from InvenTree.fields import InvenTreeURLField +from InvenTree.fields import InvenTreeURLField, RoundingDecimalField from InvenTree.status_codes import OrderStatus from common.models import Currency @@ -381,9 +381,9 @@ class SupplierPriceBreak(models.Model): part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks') - quantity = models.DecimalField(max_digits=15, decimal_places=5, default=1, validators=[MinValueValidator(1)]) + quantity = RoundingDecimalField(max_digits=15, decimal_places=5, default=1, validators=[MinValueValidator(1)]) - cost = models.DecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)]) + cost = RoundingDecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)]) currency = models.ForeignKey(Currency, blank=True, null=True, on_delete=models.SET_NULL) From 3fd0cf67b6fa4460aa687339382ae76c15e655f4 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 19 Mar 2020 09:01:22 +1100 Subject: [PATCH 2/6] Fix summation of build allocation items --- InvenTree/InvenTree/static/script/inventree/build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/static/script/inventree/build.js b/InvenTree/InvenTree/static/script/inventree/build.js index aa938ecd53..8ec872bbcb 100644 --- a/InvenTree/InvenTree/static/script/inventree/build.js +++ b/InvenTree/InvenTree/static/script/inventree/build.js @@ -71,7 +71,7 @@ function loadAllocationTable(table, part_id, part, url, required, button) { var count = 0; for (var i = 0; i < results.length; i++) { - count += results[i].quantity; + count += parseFloat(results[i].quantity); } updateAllocationTotal(part_id, count, required); From 15bc457714714c09bfb8b826deda07bf8c5f941d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 19 Mar 2020 10:15:43 +1100 Subject: [PATCH 3/6] Improve calculation of BOM item overage --- InvenTree/part/models.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 39c61079ea..d8cb8b07e0 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -1198,9 +1198,9 @@ class BomItem(models.Model): overage = str(self.overage).strip() - # Is the overage an integer value? + # Is the overage a numerical value? try: - ovg = int(overage) + ovg = float(overage) if ovg < 0: ovg = 0 @@ -1223,7 +1223,7 @@ class BomItem(models.Model): # Must be represented as a decimal percent = Decimal(percent) - return int(percent * quantity) + return float(percent * quantity) except ValueError: pass @@ -1245,7 +1245,12 @@ class BomItem(models.Model): # Base quantity requirement base_quantity = self.quantity * build_quantity - return base_quantity + self.get_overage_quantity(base_quantity) + # Overage requiremet + ovrg_quantity = self.get_overage_quantity(base_quantity) + + required = float(base_quantity) + float(ovrg_quantity) + + return required @property def price_range(self): From 6fd03801963ed5cc447f740ab28c0e250a68b84a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 19 Mar 2020 10:16:58 +1100 Subject: [PATCH 4/6] Display item overage in the allocation list --- InvenTree/build/templates/build/allocation_item.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/build/templates/build/allocation_item.html b/InvenTree/build/templates/build/allocation_item.html index 87b6ba1340..0492e44280 100644 --- a/InvenTree/build/templates/build/allocation_item.html +++ b/InvenTree/build/templates/build/allocation_item.html @@ -18,7 +18,7 @@ {% decimal item.sub_part.total_stock %}
- {% multiply build.quantity item.quantity %} + {% multiply build.quantity item.quantity %}{% if item.overage %} (+ {{ item.overage }}){% endif %}
{% part_allocation_count build item.sub_part %} From 5207b2ba2176b0e7589792cda2c97278d2fef467 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 19 Mar 2020 10:20:09 +1100 Subject: [PATCH 5/6] Add build status label to part detail --- InvenTree/part/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index c7a1b959fa..1752607d2a 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -38,7 +38,7 @@ from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDelete from InvenTree.views import QRCodeView from InvenTree.helpers import DownloadFile, str2bool -from InvenTree.status_codes import OrderStatus +from InvenTree.status_codes import OrderStatus, BuildStatus class PartIndex(ListView): @@ -593,6 +593,7 @@ class PartDetail(DetailView): context['disabled'] = not part.active context['OrderStatus'] = OrderStatus + context['BuildStatus'] = BuildStatus return context From 01f1ac49e32f9a1d07293b6f6cd163302b536fa7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 19 Mar 2020 10:23:41 +1100 Subject: [PATCH 6/6] Improve SupplierPart detail page --- .../templates/company/supplier_part_base.html | 46 +++++++++++++++---- .../templates/company/supplier_part_tabs.html | 3 -- InvenTree/company/urls.py | 2 +- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/InvenTree/company/templates/company/supplier_part_base.html b/InvenTree/company/templates/company/supplier_part_base.html index f7e6ce88cc..76f75094f7 100644 --- a/InvenTree/company/templates/company/supplier_part_base.html +++ b/InvenTree/company/templates/company/supplier_part_base.html @@ -21,19 +21,49 @@ InvenTree | {% trans "Supplier Part" %}
- -
- +
+
+

{% trans "Supplier Part Details" %}

+ + + + + + + + {% if part.URL %} + + {% endif %} + {% if part.description %} + + {% endif %} + {% if part.manufacturer %} + + + {% endif %} + {% if part.note %} + + {% endif %} +
{% trans "Internal Part" %} + {% if part.part %} + {{ part.part.full_name }} + {% endif %} +
{% trans "Supplier" %}{{ part.supplier.name }}
{% trans "SKU" %}{{ part.SKU }}
{% trans "URL" %}{{ part.URL }}
{% trans "Description" %}{{ part.description }}
{% trans "Manufacturer" %}{{ part.manufacturer }}
{% trans "MPN" %}{{ part.MPN }}
{% trans "Note" %}{{ part.note }}
+
+ + + +
diff --git a/InvenTree/company/templates/company/supplier_part_tabs.html b/InvenTree/company/templates/company/supplier_part_tabs.html index 0bcd8aa4fa..b3a4389bbd 100644 --- a/InvenTree/company/templates/company/supplier_part_tabs.html +++ b/InvenTree/company/templates/company/supplier_part_tabs.html @@ -1,9 +1,6 @@ {% load i18n %}