mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Refactor forms for sales orders
This commit is contained in:
		| @@ -42,15 +42,10 @@ | ||||
|     }); | ||||
|  | ||||
|     $("#new-sales-order").click(function() { | ||||
|         launchModalForm( | ||||
|             "{% url 'so-create' %}", | ||||
|             { | ||||
|                 data: { | ||||
|                     customer: {{ company.id }}, | ||||
|                 }, | ||||
|                 follow: true, | ||||
|             }, | ||||
|         ); | ||||
|  | ||||
|         createSalesOrder({ | ||||
|             customer: {{ company.pk }}, | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
| {% endblock %} | ||||
| @@ -12,7 +12,6 @@ from mptt.fields import TreeNodeChoiceField | ||||
|  | ||||
| from InvenTree.forms import HelperForm | ||||
| from InvenTree.fields import InvenTreeMoneyField, RoundingDecimalFormField | ||||
| from InvenTree.fields import DatePickerFormField | ||||
|  | ||||
| from InvenTree.helpers import clean_decimal | ||||
|  | ||||
| @@ -97,41 +96,6 @@ class ReceivePurchaseOrderForm(HelperForm): | ||||
|         ] | ||||
|  | ||||
|  | ||||
| class EditSalesOrderForm(HelperForm): | ||||
|     """ Form for editing a SalesOrder object """ | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|  | ||||
|         self.field_prefix = { | ||||
|             'reference': 'SO', | ||||
|             'link': 'fa-link', | ||||
|             'target_date': 'fa-calendar-alt', | ||||
|         } | ||||
|  | ||||
|         self.field_placeholder = { | ||||
|             'reference': _('Enter sales order number'), | ||||
|         } | ||||
|  | ||||
|         super().__init__(*args, **kwargs) | ||||
|  | ||||
|     target_date = DatePickerFormField( | ||||
|         label=_('Target Date'), | ||||
|         help_text=_('Target date for order completion. Order will be overdue after this date.'), | ||||
|     ) | ||||
|  | ||||
|     class Meta: | ||||
|         model = SalesOrder | ||||
|         fields = [ | ||||
|             'reference', | ||||
|             'customer', | ||||
|             'customer_reference', | ||||
|             'description', | ||||
|             'target_date', | ||||
|             'link', | ||||
|             'responsible', | ||||
|         ] | ||||
|  | ||||
|  | ||||
| class EditPurchaseOrderLineItemForm(HelperForm): | ||||
|     """ Form for editing a PurchaseOrderLineItem object """ | ||||
|  | ||||
|   | ||||
| @@ -231,6 +231,7 @@ class SalesOrderSerializer(InvenTreeModelSerializer): | ||||
|             'notes', | ||||
|             'overdue', | ||||
|             'reference', | ||||
|             'responsible', | ||||
|             'status', | ||||
|             'status_text', | ||||
|             'shipment_date', | ||||
|   | ||||
| @@ -153,7 +153,28 @@ enableNavbar({ | ||||
| }); | ||||
|  | ||||
| $("#edit-order").click(function() { | ||||
|     launchModalForm("{% url 'so-edit' order.id %}", { | ||||
|  | ||||
|     constructForm('{% url "api-so-detail" order.pk %}', { | ||||
|         fields: { | ||||
|             reference: { | ||||
|                 prefix: "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}", | ||||
|             }, | ||||
|             {% if order.lines.count == 0 and order.status == SalesOrderStatus.PENDING %} | ||||
|             customer: { | ||||
|             }, | ||||
|             {% endif %} | ||||
|             description: {}, | ||||
|             target_date: { | ||||
|                 icon: 'fa-calendar-alt', | ||||
|             }, | ||||
|             link: { | ||||
|                 icon: 'fa-link', | ||||
|             }, | ||||
|             responsible: { | ||||
|                 icon: 'fa-user', | ||||
|             }, | ||||
|         }, | ||||
|         title: '{% trans "Edit Sales Order" %}', | ||||
|         reload: true, | ||||
|     }); | ||||
| }); | ||||
|   | ||||
| @@ -178,18 +178,7 @@ $("#order-print").click(function() { | ||||
| }) | ||||
|  | ||||
| $("#so-create").click(function() { | ||||
|     launchModalForm("{% url 'so-create' %}", | ||||
|         { | ||||
|             follow: true, | ||||
|             secondary: [ | ||||
|                 { | ||||
|                     field: 'customer', | ||||
|                     label: '{% trans "New Customer" %}', | ||||
|                     title: '{% trans "Create new Customer" %}', | ||||
|                 } | ||||
|             ] | ||||
|         } | ||||
|     ); | ||||
|     createSalesOrder(); | ||||
| }); | ||||
|  | ||||
| {% endblock %} | ||||
| @@ -11,7 +11,6 @@ from django.contrib.auth.models import Group | ||||
| from InvenTree.status_codes import PurchaseOrderStatus | ||||
|  | ||||
| from .models import PurchaseOrder, PurchaseOrderLineItem | ||||
| from .models import SalesOrder | ||||
|  | ||||
| import json | ||||
|  | ||||
| @@ -60,88 +59,6 @@ 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 """ | ||||
|  | ||||
|   | ||||
| @@ -47,8 +47,6 @@ purchase_order_urls = [ | ||||
| ] | ||||
|  | ||||
| sales_order_detail_urls = [ | ||||
|  | ||||
|     url(r'^edit/', views.SalesOrderEdit.as_view(), name='so-edit'), | ||||
|     url(r'^cancel/', views.SalesOrderCancel.as_view(), name='so-cancel'), | ||||
|     url(r'^ship/', views.SalesOrderShip.as_view(), name='so-ship'), | ||||
|  | ||||
| @@ -61,8 +59,6 @@ sales_order_detail_urls = [ | ||||
|  | ||||
| sales_order_urls = [ | ||||
|  | ||||
|     url(r'^new/', views.SalesOrderCreate.as_view(), name='so-create'), | ||||
|  | ||||
|     url(r'^line/', include([ | ||||
|         url(r'^new/', views.SOLineItemCreate.as_view(), name='so-line-item-create'), | ||||
|         url(r'^(?P<pk>\d+)/', include([ | ||||
|   | ||||
| @@ -42,7 +42,8 @@ from InvenTree.helpers import DownloadFile, str2bool | ||||
| from InvenTree.helpers import extract_serial_numbers | ||||
| from InvenTree.views import InvenTreeRoleMixin | ||||
|  | ||||
| from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus | ||||
| from InvenTree.status_codes import PurchaseOrderStatus, StockStatus | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger("inventree") | ||||
|  | ||||
| @@ -143,57 +144,6 @@ class SalesOrderNotes(InvenTreeRoleMixin, UpdateView): | ||||
|         return ctx | ||||
|  | ||||
|  | ||||
| class SalesOrderCreate(AjaxCreateView): | ||||
|     """ View for creating a new SalesOrder object """ | ||||
|  | ||||
|     model = SalesOrder | ||||
|     ajax_form_title = _("Create Sales Order") | ||||
|     form_class = order_forms.EditSalesOrderForm | ||||
|  | ||||
|     def get_initial(self): | ||||
|         initials = super().get_initial().copy() | ||||
|  | ||||
|         initials['reference'] = SalesOrder.getNextOrderNumber() | ||||
|         initials['status'] = SalesOrderStatus.PENDING | ||||
|  | ||||
|         customer_id = self.request.GET.get('customer', None) | ||||
|  | ||||
|         if customer_id is not None: | ||||
|             try: | ||||
|                 customer = Company.objects.get(id=customer_id) | ||||
|                 initials['customer'] = customer | ||||
|             except (Company.DoesNotExist, ValueError): | ||||
|                 pass | ||||
|  | ||||
|         return initials | ||||
|  | ||||
|     def save(self, form, **kwargs): | ||||
|         """ | ||||
|         Record the user who created this SalesOrder | ||||
|         """ | ||||
|  | ||||
|         order = form.save(commit=False) | ||||
|         order.created_by = self.request.user | ||||
|  | ||||
|         return super().save(form) | ||||
|  | ||||
|  | ||||
| class SalesOrderEdit(AjaxUpdateView): | ||||
|     """ View for editing a SalesOrder """ | ||||
|  | ||||
|     model = SalesOrder | ||||
|     ajax_form_title = _('Edit Sales Order') | ||||
|     form_class = order_forms.EditSalesOrderForm | ||||
|  | ||||
|     def get_form(self): | ||||
|         form = super().get_form() | ||||
|  | ||||
|         # Prevent user from editing customer | ||||
|         form.fields['customer'].widget = HiddenInput() | ||||
|  | ||||
|         return form | ||||
|  | ||||
|  | ||||
| class PurchaseOrderCancel(AjaxUpdateView): | ||||
|     """ View for cancelling a purchase order """ | ||||
|  | ||||
|   | ||||
| @@ -369,6 +369,75 @@ | ||||
|     }); | ||||
|  | ||||
|     $("#part-edit").click(function() { | ||||
|  | ||||
|         constructForm('{% url "api-part-detail" part.id %}', { | ||||
|             focus: 'name', | ||||
|             fields: { | ||||
|                 category: { | ||||
|                     secondary: { | ||||
|                         label: '{% trans "New Category" %}', | ||||
|                         title: '{% trans "Create New Part Category" %}', | ||||
|                         api_url: '{% url "api-part-category-list" %}', | ||||
|                         method: 'POST', | ||||
|                         fields: { | ||||
|                             name: {}, | ||||
|                             description: {}, | ||||
|                             parent: { | ||||
|                                 secondary: { | ||||
|                                     title: '{% trans "New Parent" %}', | ||||
|                                     api_url: '{% url "api-part-category-list" %}', | ||||
|                                     method: 'POST', | ||||
|                                     fields: { | ||||
|                                         name: {}, | ||||
|                                         description: {}, | ||||
|                                         parent: {}, | ||||
|                                     } | ||||
|                                 } | ||||
|                             }, | ||||
|                         } | ||||
|                     }, | ||||
|                 }, | ||||
|                 name: { | ||||
|                     placeholder: 'part name', | ||||
|                 }, | ||||
|                 IPN: {}, | ||||
|                 description: {}, | ||||
|                 revision: {}, | ||||
|                 keywords: { | ||||
|                     icon: 'fa-key', | ||||
|                 }, | ||||
|                 variant_of: {}, | ||||
|                 link: { | ||||
|                     icon: 'fa-link', | ||||
|                 }, | ||||
|                 default_location: { | ||||
|                     secondary: { | ||||
|                         label: '{% trans "New Location" %}', | ||||
|                         title: '{% trans "Create new stock location" %}', | ||||
|                          | ||||
|                     }, | ||||
|                 }, | ||||
|                 default_supplier: { | ||||
|                     filters: { | ||||
|                         part: {{ part.pk }}, | ||||
|                         part_detail: true, | ||||
|                         manufacturer_detail: true, | ||||
|                         supplier_detail: true, | ||||
|                     }, | ||||
|                     secondary: { | ||||
|                         label: '{% trans "New Supplier Part" %}', | ||||
|                         title: '{% trans "Create new supplier part" %}', | ||||
|                     } | ||||
|                 }, | ||||
|                 units: {}, | ||||
|                 minimum_stock: {}, | ||||
|             }, | ||||
|             title: '{% trans "Edit Part" %}', | ||||
|             reload: true, | ||||
|         }); | ||||
|  | ||||
|         return; | ||||
|  | ||||
|         launchModalForm( | ||||
|             "{% url 'part-edit' part.id %}", | ||||
|             { | ||||
|   | ||||
| @@ -2,7 +2,37 @@ | ||||
| {% load inventree_extras %} | ||||
|  | ||||
|  | ||||
| // Create a new purchase order | ||||
| // Create a new SalesOrder | ||||
| function createSalesOrder(options={}) { | ||||
|  | ||||
|     constructForm('{% url "api-so-list" %}', { | ||||
|         method: 'POST', | ||||
|         fields: { | ||||
|             reference: { | ||||
|                 prefix: '{% settings_value "SALESORDER_REFERENCE_PREFIX" %}', | ||||
|             }, | ||||
|             customer: { | ||||
|                 value: options.customer, | ||||
|             }, | ||||
|             description: {}, | ||||
|             target_date: { | ||||
|                 icon: 'fa-calendar-alt', | ||||
|             }, | ||||
|             link: { | ||||
|                 icon: 'fa-link', | ||||
|             }, | ||||
|             responsible: { | ||||
|                 icon: 'fa-user', | ||||
|             } | ||||
|         }, | ||||
|         onSuccess: function(data) { | ||||
|             location.href = `/order/sales-order/${data.pk}/`; | ||||
|         }, | ||||
|         title: '{% trans "Create Sales Order" %}', | ||||
|     }); | ||||
| } | ||||
|  | ||||
| // Create a new PurchaseOrder | ||||
| function createPurchaseOrder(options={}) { | ||||
|  | ||||
|     constructForm('{% url "api-po-list" %}', { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user