2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-04 12:40:57 +00:00

Refactor SupplierPartCreate form

This commit is contained in:
Oliver Walters
2021-07-18 22:21:11 +10:00
parent b1165af3c3
commit 56fa6c512b
14 changed files with 129 additions and 409 deletions

View File

@ -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')

View File

@ -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)

View File

@ -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(

View File

@ -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() {

View File

@ -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>

View File

@ -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())

View File

@ -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)),

View File

@ -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.