mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Refactor SupplierPartCreate form
This commit is contained in:
		| @@ -475,57 +475,6 @@ class SupplierPart(models.Model): | ||||
|     def get_absolute_url(self): | ||||
|         return reverse('supplier-part-detail', kwargs={'pk': self.id}) | ||||
|  | ||||
|     def save(self, *args, **kwargs): | ||||
|         """ Overriding save method to process the linked ManufacturerPart | ||||
|         """ | ||||
|  | ||||
|         if 'manufacturer' in kwargs: | ||||
|             manufacturer_id = kwargs.pop('manufacturer') | ||||
|  | ||||
|             try: | ||||
|                 manufacturer = Company.objects.get(pk=int(manufacturer_id)) | ||||
|             except (ValueError, Company.DoesNotExist): | ||||
|                 manufacturer = None | ||||
|         else: | ||||
|             manufacturer = None | ||||
|         if 'MPN' in kwargs: | ||||
|             MPN = kwargs.pop('MPN') | ||||
|         else: | ||||
|             MPN = None | ||||
|  | ||||
|         if manufacturer or MPN: | ||||
|             if not self.manufacturer_part: | ||||
|                 # Create ManufacturerPart | ||||
|                 manufacturer_part = ManufacturerPart.create(part=self.part, | ||||
|                                                             manufacturer=manufacturer, | ||||
|                                                             mpn=MPN, | ||||
|                                                             description=self.description) | ||||
|                 self.manufacturer_part = manufacturer_part | ||||
|             else: | ||||
|                 # Update ManufacturerPart (if ID exists) | ||||
|                 try: | ||||
|                     manufacturer_part_id = self.manufacturer_part.id | ||||
|                 except AttributeError: | ||||
|                     manufacturer_part_id = None | ||||
|  | ||||
|                 if manufacturer_part_id: | ||||
|                     try: | ||||
|                         (manufacturer_part, created) = ManufacturerPart.objects.update_or_create(part=self.part, | ||||
|                                                                                                  manufacturer=manufacturer, | ||||
|                                                                                                  MPN=MPN) | ||||
|                     except IntegrityError: | ||||
|                         manufacturer_part = None | ||||
|                         raise ValidationError(f'ManufacturerPart linked to {self.part} from manufacturer {manufacturer.name}' | ||||
|                                               f'with part number {MPN} already exists!') | ||||
|  | ||||
|                 if manufacturer_part: | ||||
|                     self.manufacturer_part = manufacturer_part | ||||
|  | ||||
|         self.clean() | ||||
|         self.validate_unique() | ||||
|  | ||||
|         super().save(*args, **kwargs) | ||||
|  | ||||
|     class Meta: | ||||
|         unique_together = ('part', 'supplier', 'SKU') | ||||
|  | ||||
|   | ||||
| @@ -96,7 +96,9 @@ class CompanySerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|  | ||||
| class ManufacturerPartSerializer(InvenTreeModelSerializer): | ||||
|     """ Serializer for ManufacturerPart object """ | ||||
|     """ | ||||
|     Serializer for ManufacturerPart object | ||||
|     """ | ||||
|  | ||||
|     part_detail = PartBriefSerializer(source='part', many=False, read_only=True) | ||||
|  | ||||
| @@ -106,8 +108,8 @@ class ManufacturerPartSerializer(InvenTreeModelSerializer): | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|  | ||||
|         part_detail = kwargs.pop('part_detail', False) | ||||
|         manufacturer_detail = kwargs.pop('manufacturer_detail', False) | ||||
|         part_detail = kwargs.pop('part_detail', True) | ||||
|         manufacturer_detail = kwargs.pop('manufacturer_detail', True) | ||||
|         prettify = kwargs.pop('pretty', False) | ||||
|  | ||||
|         super(ManufacturerPartSerializer, self).__init__(*args, **kwargs) | ||||
|   | ||||
| @@ -350,28 +350,16 @@ | ||||
|  | ||||
|     {% if company.is_supplier %} | ||||
|  | ||||
|     function reloadSupplierPartTable() { | ||||
|         $('#supplier-part-table').bootstrapTable('refresh'); | ||||
|     } | ||||
|  | ||||
|     $("#supplier-part-create").click(function () { | ||||
|         launchModalForm( | ||||
|             "{% url 'supplier-part-create' %}", | ||||
|             { | ||||
|                 data: { | ||||
|                     supplier: {{ company.id }}, | ||||
|                 }, | ||||
|                 reload: true, | ||||
|                 secondary: [ | ||||
|                     { | ||||
|                         field: 'part', | ||||
|                         label: '{% trans "New Part" %}', | ||||
|                         title: '{% trans "Create new Part" %}', | ||||
|                         url: "{% url 'part-create' %}" | ||||
|                     }, | ||||
|                     { | ||||
|                         field: 'supplier', | ||||
|                         label: "{% trans 'New Supplier' %}", | ||||
|                         title: "{% trans 'Create new Supplier' %}", | ||||
|                     }, | ||||
|                 ] | ||||
|             }); | ||||
|  | ||||
|         createSupplierPart({ | ||||
|             supplier: {{ company.pk }}, | ||||
|             onSuccess: reloadSupplierPartTable, | ||||
|         }); | ||||
|     }); | ||||
|  | ||||
|     loadSupplierPartTable( | ||||
|   | ||||
| @@ -178,22 +178,16 @@ $('#parameter-create').click(function() { | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| function reloadSupplierPartTable() { | ||||
|     $('#supplier-table').bootstrapTable('refresh'); | ||||
| } | ||||
|  | ||||
| $('#supplier-create').click(function () { | ||||
|     launchModalForm( | ||||
|         "{% url 'supplier-part-create' %}", | ||||
|         { | ||||
|             reload: true, | ||||
|             data: { | ||||
|                 manufacturer_part: {{ part.id }} | ||||
|             }, | ||||
|             secondary: [ | ||||
|                 { | ||||
|                     field: 'supplier', | ||||
|                     label: '{% trans "New Supplier" %}', | ||||
|                     title: '{% trans "Create new supplier" %}', | ||||
|                 }, | ||||
|             ] | ||||
|         }); | ||||
|     createSupplierPart({ | ||||
|         manufacturer_part: {{ part.pk }}, | ||||
|         part: {{ part.part.pk }}, | ||||
|         onSuccess: reloadSupplierPartTable, | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| $("#supplier-part-delete").click(function() { | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
|     </li> | ||||
|     {% endif %} | ||||
|  | ||||
|     {% if company.is_supplier or company.is_manufacturer %} | ||||
|     {% if company.is_supplier %} | ||||
|     <li class='list-group-item' title='{% trans "Supplied Parts" %}'> | ||||
|         <a href='#' id='select-supplier-parts' class='nav-toggle'> | ||||
|             <span class='fas fa-building sidebar-icon'></span> | ||||
|   | ||||
| @@ -75,108 +75,6 @@ class CompanyViewTestBase(TestCase): | ||||
|         return json_data, form_errors | ||||
|  | ||||
|  | ||||
| class SupplierPartViewTests(CompanyViewTestBase): | ||||
|     """ | ||||
|     Tests for the SupplierPart views. | ||||
|     """ | ||||
|  | ||||
|     def test_supplier_part_create(self): | ||||
|         """ | ||||
|         Test the SupplierPartCreate view. | ||||
|  | ||||
|         This view allows some additional functionality, | ||||
|         specifically it allows the user to create a single-quantity price break | ||||
|         automatically, when saving the new SupplierPart model. | ||||
|         """ | ||||
|  | ||||
|         url = reverse('supplier-part-create') | ||||
|  | ||||
|         # First check that we can GET the form | ||||
|         response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|         # How many supplier parts are already in the database? | ||||
|         n = SupplierPart.objects.all().count() | ||||
|  | ||||
|         data = { | ||||
|             'part': 1, | ||||
|             'supplier': 1, | ||||
|         } | ||||
|  | ||||
|         # SKU is required! (form should fail) | ||||
|         (response, errors) = self.post(url, data, valid=False) | ||||
|  | ||||
|         self.assertIsNotNone(errors.get('SKU', None)) | ||||
|  | ||||
|         data['SKU'] = 'TEST-ME-123' | ||||
|  | ||||
|         (response, errors) = self.post(url, data, valid=True) | ||||
|  | ||||
|         # Check that the SupplierPart was created! | ||||
|         self.assertEqual(n + 1, SupplierPart.objects.all().count()) | ||||
|  | ||||
|         # Check that it was created *without* a price-break | ||||
|         supplier_part = SupplierPart.objects.get(pk=response['pk']) | ||||
|  | ||||
|         self.assertEqual(supplier_part.price_breaks.count(), 0) | ||||
|  | ||||
|         # Duplicate SKU is prohibited | ||||
|         (response, errors) = self.post(url, data, valid=False) | ||||
|  | ||||
|         self.assertIsNotNone(errors.get('__all__', None)) | ||||
|  | ||||
|         # Add with a different SKU, *and* a single-quantity price | ||||
|         data['SKU'] = 'TEST-ME-1234' | ||||
|         data['single_pricing_0'] = '123.4' | ||||
|         data['single_pricing_1'] = 'CAD' | ||||
|  | ||||
|         (response, errors) = self.post(url, data, valid=True) | ||||
|  | ||||
|         pk = response.get('pk') | ||||
|  | ||||
|         # Check that *another* SupplierPart was created | ||||
|         self.assertEqual(n + 2, SupplierPart.objects.all().count()) | ||||
|  | ||||
|         supplier_part = SupplierPart.objects.get(pk=pk) | ||||
|  | ||||
|         # Check that a price-break has been created! | ||||
|         self.assertEqual(supplier_part.price_breaks.count(), 1) | ||||
|  | ||||
|         price_break = supplier_part.price_breaks.first() | ||||
|  | ||||
|         self.assertEqual(price_break.quantity, 1) | ||||
|  | ||||
|     def test_supplier_part_delete(self): | ||||
|         """ | ||||
|         Test the SupplierPartDelete view | ||||
|         """ | ||||
|  | ||||
|         url = reverse('supplier-part-delete') | ||||
|  | ||||
|         # Get form using 'part' argument | ||||
|         response = self.client.get(url, {'part': '1'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|         # Get form using 'parts' argument | ||||
|         response = self.client.get(url + '?parts[]=1&parts[]=2', HTTP_X_REQUESTED_WITH='XMLHttpRequest') | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|         # POST to delete two parts | ||||
|         n = SupplierPart.objects.count() | ||||
|         response = self.client.post( | ||||
|             url, | ||||
|             { | ||||
|                 'supplier-part-2': 'supplier-part-2', | ||||
|                 'supplier-part-3': 'supplier-part-3', | ||||
|                 'confirm_delete': True | ||||
|             }, | ||||
|             HTTP_X_REQUESTED_WITH='XMLHttpRequest') | ||||
|  | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|         self.assertEqual(n - 2, SupplierPart.objects.count()) | ||||
|  | ||||
|  | ||||
| class CompanyViewTest(CompanyViewTestBase): | ||||
|     """ | ||||
|     Tests for various 'Company' views | ||||
| @@ -187,36 +85,3 @@ class CompanyViewTest(CompanyViewTestBase): | ||||
|  | ||||
|         response = self.client.get(reverse('company-index')) | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|  | ||||
| class ManufacturerPartViewTests(CompanyViewTestBase): | ||||
|     """ | ||||
|     Tests for the ManufacturerPart views. | ||||
|     """ | ||||
|  | ||||
|     def test_supplier_part_create(self): | ||||
|         """ | ||||
|         Test that the SupplierPartCreate view creates Manufacturer Part. | ||||
|         """ | ||||
|  | ||||
|         url = reverse('supplier-part-create') | ||||
|  | ||||
|         # First check that we can GET the form | ||||
|         response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest') | ||||
|         self.assertEqual(response.status_code, 200) | ||||
|  | ||||
|         # How many manufacturer parts are already in the database? | ||||
|         n = ManufacturerPart.objects.all().count() | ||||
|  | ||||
|         data = { | ||||
|             'part': 1, | ||||
|             'supplier': 1, | ||||
|             'SKU': 'SKU_TEST', | ||||
|             'manufacturer': 6, | ||||
|             'MPN': 'MPN_TEST', | ||||
|         } | ||||
|  | ||||
|         (response, errors) = self.post(url, data, valid=True) | ||||
|  | ||||
|         # Check that the ManufacturerPart was created! | ||||
|         self.assertEqual(n + 1, ManufacturerPart.objects.all().count()) | ||||
|   | ||||
| @@ -42,8 +42,6 @@ supplier_part_detail_urls = [ | ||||
| ] | ||||
|  | ||||
| supplier_part_urls = [ | ||||
|     url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'), | ||||
|  | ||||
|     url(r'delete/', views.SupplierPartDelete.as_view(), name='supplier-part-delete'), | ||||
|  | ||||
|     url(r'^(?P<pk>\d+)/', include(supplier_part_detail_urls)), | ||||
|   | ||||
| @@ -285,144 +285,6 @@ class SupplierPartEdit(AjaxUpdateView): | ||||
|         return initials | ||||
|  | ||||
|  | ||||
| class SupplierPartCreate(AjaxCreateView): | ||||
|     """ Create view for making new SupplierPart """ | ||||
|  | ||||
|     model = SupplierPart | ||||
|     form_class = EditSupplierPartForm | ||||
|     ajax_template_name = 'company/supplier_part_create.html' | ||||
|     ajax_form_title = _('Create new Supplier Part') | ||||
|     context_object_name = 'part' | ||||
|  | ||||
|     def validate(self, part, form): | ||||
|  | ||||
|         single_pricing = form.cleaned_data.get('single_pricing', None) | ||||
|  | ||||
|         if single_pricing: | ||||
|             # TODO - What validation steps can be performed on the single_pricing field? | ||||
|             pass | ||||
|  | ||||
|     def get_context_data(self): | ||||
|         """ | ||||
|         Supply context data to the form | ||||
|         """ | ||||
|  | ||||
|         ctx = super().get_context_data() | ||||
|  | ||||
|         # Add 'part' object | ||||
|         form = self.get_form() | ||||
|  | ||||
|         part = form['part'].value() | ||||
|  | ||||
|         try: | ||||
|             part = Part.objects.get(pk=part) | ||||
|         except (ValueError, Part.DoesNotExist): | ||||
|             part = None | ||||
|  | ||||
|         ctx['part'] = part | ||||
|  | ||||
|         return ctx | ||||
|  | ||||
|     def save(self, form): | ||||
|         """ | ||||
|         If single_pricing is defined, add a price break for quantity=1 | ||||
|         """ | ||||
|  | ||||
|         # Save the supplier part object | ||||
|         supplier_part = super().save(form) | ||||
|  | ||||
|         # Process manufacturer data | ||||
|         manufacturer = form.cleaned_data.get('manufacturer', None) | ||||
|         MPN = form.cleaned_data.get('MPN', None) | ||||
|         kwargs = {'manufacturer': manufacturer, | ||||
|                   'MPN': MPN, | ||||
|                   } | ||||
|         supplier_part.save(**kwargs) | ||||
|  | ||||
|         single_pricing = form.cleaned_data.get('single_pricing', None) | ||||
|  | ||||
|         if single_pricing: | ||||
|  | ||||
|             supplier_part.add_price_break(1, single_pricing) | ||||
|  | ||||
|         return supplier_part | ||||
|  | ||||
|     def get_form(self): | ||||
|         """ Create Form instance to create a new SupplierPart object. | ||||
|         Hide some fields if they are not appropriate in context | ||||
|         """ | ||||
|         form = super(AjaxCreateView, self).get_form() | ||||
|  | ||||
|         if form.initial.get('part', None): | ||||
|             # Hide the part field | ||||
|             form.fields['part'].widget = HiddenInput() | ||||
|  | ||||
|         if form.initial.get('manufacturer', None): | ||||
|             # Hide the manufacturer field | ||||
|             form.fields['manufacturer'].widget = HiddenInput() | ||||
|             # Hide the MPN field | ||||
|             form.fields['MPN'].widget = HiddenInput() | ||||
|  | ||||
|         return form | ||||
|  | ||||
|     def get_initial(self): | ||||
|         """ Provide initial data for new SupplierPart: | ||||
|  | ||||
|         - If 'supplier_id' provided, pre-fill supplier field | ||||
|         - If 'part_id' provided, pre-fill part field | ||||
|         """ | ||||
|         initials = super(SupplierPartCreate, self).get_initial().copy() | ||||
|  | ||||
|         manufacturer_id = self.get_param('manufacturer') | ||||
|         supplier_id = self.get_param('supplier') | ||||
|         part_id = self.get_param('part') | ||||
|         manufacturer_part_id = self.get_param('manufacturer_part') | ||||
|  | ||||
|         supplier = None | ||||
|  | ||||
|         if supplier_id: | ||||
|             try: | ||||
|                 supplier = Company.objects.get(pk=supplier_id) | ||||
|                 initials['supplier'] = supplier | ||||
|             except (ValueError, Company.DoesNotExist): | ||||
|                 supplier = None | ||||
|  | ||||
|         if manufacturer_id: | ||||
|             try: | ||||
|                 initials['manufacturer'] = Company.objects.get(pk=manufacturer_id) | ||||
|             except (ValueError, Company.DoesNotExist): | ||||
|                 pass | ||||
|  | ||||
|         if manufacturer_part_id: | ||||
|             try: | ||||
|                 # Get ManufacturerPart instance information | ||||
|                 manufacturer_part_obj = ManufacturerPart.objects.get(pk=manufacturer_part_id) | ||||
|                 initials['part'] = Part.objects.get(pk=manufacturer_part_obj.part.id) | ||||
|                 initials['manufacturer'] = manufacturer_part_obj.manufacturer.id | ||||
|                 initials['MPN'] = manufacturer_part_obj.MPN | ||||
|             except (ValueError, ManufacturerPart.DoesNotExist, Part.DoesNotExist, Company.DoesNotExist): | ||||
|                 pass | ||||
|  | ||||
|         if part_id: | ||||
|             try: | ||||
|                 initials['part'] = Part.objects.get(pk=part_id) | ||||
|             except (ValueError, Part.DoesNotExist): | ||||
|                 pass | ||||
|  | ||||
|         # Initial value for single pricing | ||||
|         if supplier: | ||||
|             currency_code = supplier.currency_code | ||||
|         else: | ||||
|             currency_code = common.settings.currency_code_default() | ||||
|  | ||||
|         currency = CURRENCIES.get(currency_code, None) | ||||
|  | ||||
|         if currency_code: | ||||
|             initials['single_pricing'] = ('', currency) | ||||
|  | ||||
|         return initials | ||||
|  | ||||
|  | ||||
| class SupplierPartDelete(AjaxDeleteView): | ||||
|     """ Delete view for removing a SupplierPart. | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user