From 6731bc1b066a40826d72b139c5982b7a9da1a1b6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 18 Mar 2020 22:22:40 +1100 Subject: [PATCH] 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)