From 03276629c2c40215a9a6793b75a4633f5be4030b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 2 Jan 2021 21:41:47 +1100 Subject: [PATCH 1/4] CSS tweaks --- InvenTree/InvenTree/static/css/inventree.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css index 92568eeb11..78c1808adf 100644 --- a/InvenTree/InvenTree/static/css/inventree.css +++ b/InvenTree/InvenTree/static/css/inventree.css @@ -748,3 +748,6 @@ input[type="submit"] { to { transform: scale(1) rotate(360deg);} } +input[type="date"].form-control, input[type="time"].form-control, input[type="datetime-local"].form-control, input[type="month"].form-control { + line-height: unset; +} \ No newline at end of file From 2b57ffeb083c373b7517d0912b9fc7c1deca2d5d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 2 Jan 2021 21:50:10 +1100 Subject: [PATCH 2/4] Custom date picker field - Prevents picker from being "required" by the form --- InvenTree/InvenTree/fields.py | 29 +++++++++++++++++++++++++++++ InvenTree/build/forms.py | 13 ++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/InvenTree/InvenTree/fields.py b/InvenTree/InvenTree/fields.py index 884bd0c49f..b11b443645 100644 --- a/InvenTree/InvenTree/fields.py +++ b/InvenTree/InvenTree/fields.py @@ -5,10 +5,13 @@ from __future__ import unicode_literals from .validators import allowable_url_schemes +from django.utils.translation import ugettext as _ + 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 import InvenTree.helpers @@ -31,6 +34,32 @@ class InvenTreeURLField(models.URLField): }) +class InvenTreeDatePickerFormField(forms.DateField): + """ + Custom date-picker field + """ + + def __init__(self, **kwargs): + + help_text = kwargs.get('help_text', _('Enter date')) + required = kwargs.get('required', False) + initial = kwargs.get('initial', None) + + widget = forms.DateInput( + attrs={ + 'type': 'date', + } + ) + + forms.DateField.__init__( + self, + required=required, + initial=initial, + help_text=help_text, + widget=widget + ) + + def round_decimal(value, places): """ Round value to the specified number of places. diff --git a/InvenTree/build/forms.py b/InvenTree/build/forms.py index 70deeef7b1..cd3d4bbcfb 100644 --- a/InvenTree/build/forms.py +++ b/InvenTree/build/forms.py @@ -10,6 +10,7 @@ from django import forms from InvenTree.forms import HelperForm from InvenTree.fields import RoundingDecimalFormField +from InvenTree.fields import InvenTreeDatePickerFormField from .models import Build, BuildItem, BuildOrderAttachment @@ -26,18 +27,16 @@ class EditBuildForm(HelperForm): 'batch': 'fa-layer-group', 'serial-numbers': 'fa-hashtag', 'location': 'fa-map-marker-alt', + 'target_date': 'fa-calendar-alt', } field_placeholder = { - 'reference': _('Build Order reference') + 'reference': _('Build Order reference'), + 'target_date': _('Order target date'), } - # TODO: Make this a more "presentable" date picker - # TODO: Currently does not render super nicely with crispy forms - target_date = forms.DateField( - widget=forms.DateInput( - attrs={'type': 'date'} - ) + target_date = InvenTreeDatePickerFormField( + help_text=_('Target date for build completion. Build will be overdue after this date.') ) class Meta: From b2a732197bbad3f4a3cde1ee792fafc674ea303f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 2 Jan 2021 21:54:07 +1100 Subject: [PATCH 3/4] Fix target date form field for SalesOrder --- InvenTree/InvenTree/fields.py | 2 +- InvenTree/build/forms.py | 4 ++-- InvenTree/order/forms.py | 9 ++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/InvenTree/InvenTree/fields.py b/InvenTree/InvenTree/fields.py index b11b443645..c0e1633ac7 100644 --- a/InvenTree/InvenTree/fields.py +++ b/InvenTree/InvenTree/fields.py @@ -34,7 +34,7 @@ class InvenTreeURLField(models.URLField): }) -class InvenTreeDatePickerFormField(forms.DateField): +class DatePickerFormField(forms.DateField): """ Custom date-picker field """ diff --git a/InvenTree/build/forms.py b/InvenTree/build/forms.py index cd3d4bbcfb..136d21d553 100644 --- a/InvenTree/build/forms.py +++ b/InvenTree/build/forms.py @@ -10,7 +10,7 @@ from django import forms from InvenTree.forms import HelperForm from InvenTree.fields import RoundingDecimalFormField -from InvenTree.fields import InvenTreeDatePickerFormField +from InvenTree.fields import DatePickerFormField from .models import Build, BuildItem, BuildOrderAttachment @@ -35,7 +35,7 @@ class EditBuildForm(HelperForm): 'target_date': _('Order target date'), } - target_date = InvenTreeDatePickerFormField( + target_date = DatePickerFormField( help_text=_('Target date for build completion. Build will be overdue after this date.') ) diff --git a/InvenTree/order/forms.py b/InvenTree/order/forms.py index d797a4e42d..6db51b55e6 100644 --- a/InvenTree/order/forms.py +++ b/InvenTree/order/forms.py @@ -12,6 +12,7 @@ from mptt.fields import TreeNodeChoiceField from InvenTree.forms import HelperForm from InvenTree.fields import RoundingDecimalFormField +from InvenTree.fields import DatePickerFormField from stock.models import StockLocation from .models import PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderAttachment @@ -120,6 +121,7 @@ class EditSalesOrderForm(HelperForm): self.field_prefix = { 'reference': 'SO', 'link': 'fa-link', + 'target_date': 'fa-calendar-alt', } self.field_placeholder = { @@ -128,11 +130,8 @@ class EditSalesOrderForm(HelperForm): super().__init__(*args, **kwargs) - # TODO: Improve this using a better date picker - target_date = forms.DateField( - widget=forms.DateInput( - attrs={'type': 'date'}, - ) + target_date = DatePickerFormField( + help_text=_('Target date for order completion. Order will be overdue after this date.'), ) class Meta: From 725a70327a1243777d954df6afe326308e7b9eb8 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 2 Jan 2021 22:21:37 +1100 Subject: [PATCH 4/4] Add some unit testing --- InvenTree/order/test_views.py | 83 +++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/InvenTree/order/test_views.py b/InvenTree/order/test_views.py index 2f014e751f..51981af105 100644 --- a/InvenTree/order/test_views.py +++ b/InvenTree/order/test_views.py @@ -11,6 +11,7 @@ from django.contrib.auth.models import Group from InvenTree.status_codes import PurchaseOrderStatus from .models import PurchaseOrder, PurchaseOrderLineItem +from .models import SalesOrder import json @@ -59,6 +60,88 @@ class OrderListTest(OrderViewTestCase): self.assertEqual(response.status_code, 200) +class SalesOrderCreate(OrderViewTestCase): + """ + Create a SalesOrder using the form view + """ + + URL = reverse('so-create') + + def test_create_view(self): + """ + Retrieve the view for creating a sales order' + """ + + response = self.client.get(self.URL, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + self.assertEqual(response.status_code, 200) + + def post(self, data, **kwargs): + + return self.client.post(self.URL, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest', **kwargs) + + def test_post_error(self): + """ + POST with errors + """ + + n = SalesOrder.objects.count() + + data = { + 'reference': '12345678', + } + + response = self.post(data) + + data = json.loads(response.content) + + self.assertIn('form_valid', data.keys()) + + # Customer is not specified - should return False + self.assertFalse(data['form_valid']) + + errors = json.loads(data['form_errors']) + + self.assertIn('customer', errors.keys()) + self.assertIn('description', errors.keys()) + + # No new SalesOrder objects should have been created + self.assertEqual(SalesOrder.objects.count(), n) + + def test_post_valid(self): + """ + POST a valid SalesOrder + """ + + n = SalesOrder.objects.count() + + data = { + 'reference': '12345678', + 'customer': 4, + 'description': 'A description', + } + + response = self.post(data) + + json_data = json.loads(response.content) + + self.assertTrue(json_data['form_valid']) + + # Create another SalesOrder, this time with a target date + data = { + 'reference': '12345679', + 'customer': 4, + 'description': 'Another order, this one with a target date!', + 'target_date': '2020-12-25', + } + + response = self.post(data) + + json_data = json.loads(response.content) + + self.assertEqual(SalesOrder.objects.count(), n + 2) + + class POTests(OrderViewTestCase): """ Tests for PurchaseOrder views """