diff --git a/InvenTree/company/forms.py b/InvenTree/company/forms.py
index 079e871b84..506133df00 100644
--- a/InvenTree/company/forms.py
+++ b/InvenTree/company/forms.py
@@ -6,13 +6,12 @@ Django Forms for interacting with Company app
from __future__ import unicode_literals
from InvenTree.forms import HelperForm
-from InvenTree.fields import InvenTreeMoneyField, RoundingDecimalFormField
+from InvenTree.fields import RoundingDecimalFormField
from django.utils.translation import ugettext_lazy as _
import django.forms
from .models import Company
-from .models import SupplierPart
from .models import SupplierPriceBreak
@@ -34,67 +33,6 @@ class CompanyImageDownloadForm(HelperForm):
]
-class EditSupplierPartForm(HelperForm):
- """ Form for editing a SupplierPart object """
-
- field_prefix = {
- 'link': 'fa-link',
- 'SKU': 'fa-hashtag',
- 'note': 'fa-pencil-alt',
- }
-
- single_pricing = InvenTreeMoneyField(
- label=_('Single Price'),
- help_text=_('Single quantity price'),
- decimal_places=4,
- max_digits=19,
- required=False,
- )
-
- manufacturer = django.forms.ChoiceField(
- required=False,
- help_text=_('Select manufacturer'),
- choices=[],
- )
-
- MPN = django.forms.CharField(
- required=False,
- help_text=_('Manufacturer Part Number'),
- max_length=100,
- label=_('MPN'),
- )
-
- class Meta:
- model = SupplierPart
- fields = [
- 'part',
- 'supplier',
- 'SKU',
- 'manufacturer',
- 'MPN',
- 'description',
- 'link',
- 'note',
- 'single_pricing',
- # 'base_cost',
- # 'multiple',
- 'packaging',
- ]
-
- def get_manufacturer_choices(self):
- """ Returns tuples for all manufacturers """
- empty_choice = [('', '----------')]
-
- manufacturers = [(manufacturer.id, manufacturer.name) for manufacturer in Company.objects.filter(is_manufacturer=True)]
-
- return empty_choice + manufacturers
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
- self.fields['manufacturer'].choices = self.get_manufacturer_choices()
-
-
class EditPriceBreakForm(HelperForm):
""" Form for creating / editing a supplier price break """
diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py
index 778e00cde1..3b731381b8 100644
--- a/InvenTree/company/models.py
+++ b/InvenTree/company/models.py
@@ -9,9 +9,7 @@ import os
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator
-from django.core.exceptions import ValidationError
from django.db import models
-from django.db.utils import IntegrityError
from django.db.models import Sum, Q, UniqueConstraint
from django.apps import apps
@@ -475,57 +473,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')
diff --git a/InvenTree/company/serializers.py b/InvenTree/company/serializers.py
index 6e5ef08d6e..e9f13d021d 100644
--- a/InvenTree/company/serializers.py
+++ b/InvenTree/company/serializers.py
@@ -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)
@@ -229,25 +231,6 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
'supplier_detail',
]
- def create(self, validated_data):
- """ Extract manufacturer data and process ManufacturerPart """
-
- # Create SupplierPart
- supplier_part = super().create(validated_data)
-
- # Get ManufacturerPart raw data (unvalidated)
- manufacturer_id = self.initial_data.get('manufacturer', None)
- MPN = self.initial_data.get('MPN', None)
-
- if manufacturer_id and MPN:
- kwargs = {
- 'manufacturer': manufacturer_id,
- 'MPN': MPN,
- }
- supplier_part.save(**kwargs)
-
- return supplier_part
-
class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
""" Serializer for SupplierPriceBreak object """
diff --git a/InvenTree/company/templates/company/detail.html b/InvenTree/company/templates/company/detail.html
index 105e0f82ae..b45289eb8c 100644
--- a/InvenTree/company/templates/company/detail.html
+++ b/InvenTree/company/templates/company/detail.html
@@ -284,23 +284,9 @@
$("#manufacturer-part-create").click(function () {
- constructForm('{% url "api-manufacturer-part-list" %}', {
- fields: {
- part: {},
- manufacturer: {
- value: {{ company.pk }},
- },
- MPN: {
- icon: 'fa-hashtag',
- },
- description: {},
- link: {
- icon: 'fa-link',
- },
- },
- method: 'POST',
- title: '{% trans "Add Manufacturer Part" %}',
- onSuccess: function() {
+ createManufacturerPart({
+ manufacturer: {{ company.pk }},
+ onSuccess: function() {
$("#part-table").bootstrapTable("refresh");
}
});
@@ -350,28 +336,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(
@@ -390,22 +364,27 @@
{% endif %}
$("#multi-part-delete").click(function() {
- var selections = $("#part-table").bootstrapTable("getSelections");
+ var selections = $("#supplier-part-table").bootstrapTable("getSelections");
- var parts = [];
+ var requests = [];
- selections.forEach(function(item) {
- parts.push(item.pk);
- });
-
- var url = "{% url 'supplier-part-delete' %}"
-
- launchModalForm(url, {
- data: {
- parts: parts,
- },
- reload: true,
- });
+ showQuestionDialog(
+ '{% trans "Delete Supplier Parts?" %}',
+ '{% trans "All selected supplier parts will be deleted" %}',
+ {
+ accept: function() {
+ selections.forEach(function(part) {
+ var url = `/api/company/part/${part.pk}/`;
+
+ requests.push(inventreeDelete(url));
+ });
+
+ $.when.apply($, requests).then(function() {
+ $('#supplier-part-table').bootstrapTable('refresh');
+ });
+ }
+ }
+ );
});
$("#multi-part-order").click(function() {
diff --git a/InvenTree/company/templates/company/manufacturer_part.html b/InvenTree/company/templates/company/manufacturer_part.html
index da5ea36173..94ff64440f 100644
--- a/InvenTree/company/templates/company/manufacturer_part.html
+++ b/InvenTree/company/templates/company/manufacturer_part.html
@@ -178,40 +178,41 @@ $('#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() {
var selections = $("#supplier-table").bootstrapTable("getSelections");
- var parts = [];
+ var requests = [];
- selections.forEach(function(item) {
- parts.push(item.pk);
- });
-
- launchModalForm("{% url 'supplier-part-delete' %}", {
- data: {
- parts: parts,
- },
- reload: true,
- });
+ showQuestionDialog(
+ '{% trans "Delete Supplier Parts?" %}',
+ '{% trans "All selected supplier parts will be deleted" %}',
+ {
+ accept: function() {
+ selections.forEach(function(part) {
+ var url = `/api/company/part/${part.pk}/`;
+
+ requests.push(inventreeDelete(url));
+ });
+
+ $.when.apply($, requests).then(function() {
+ reloadSupplierPartTable();
+ });
+ }
+ }
+ );
});
$("#multi-parameter-delete").click(function() {
@@ -296,29 +297,19 @@ $('#order-part, #order-part2').click(function() {
$('#edit-part').click(function () {
- constructForm('{% url "api-manufacturer-part-detail" part.pk %}', {
- fields: {
- part: {},
- manufacturer: {},
- MPN: {
- icon: 'fa-hashtag',
- },
- description: {},
- link: {
- icon: 'fa-link',
- },
- },
- title: '{% trans "Edit Manufacturer Part" %}',
- reload: true,
+ editManufacturerPart({{ part.pk }}, {
+ onSuccess: function() {
+ location.reload();
+ }
});
});
$('#delete-part').click(function() {
- constructForm('{% url "api-manufacturer-part-detail" part.pk %}', {
- method: 'DELETE',
- title: '{% trans "Delete Manufacturer Part" %}',
- redirect: "{% url 'company-detail' part.manufacturer.id %}",
+ deleteManufacturerPart({{ part.pk }}, {
+ onSuccess: function() {
+ window.location.href = "{% url 'company-detail' part.manufacturer.id %}";
+ }
});
});
diff --git a/InvenTree/company/templates/company/navbar.html b/InvenTree/company/templates/company/navbar.html
index 025b1c6b4a..b652d6b603 100644
--- a/InvenTree/company/templates/company/navbar.html
+++ b/InvenTree/company/templates/company/navbar.html
@@ -18,7 +18,7 @@
{% endif %}
- {% if company.is_supplier or company.is_manufacturer %}
+ {% if company.is_supplier %}
diff --git a/InvenTree/company/templates/company/supplier_part.html b/InvenTree/company/templates/company/supplier_part.html
index c3c2f89aa7..f751665e56 100644
--- a/InvenTree/company/templates/company/supplier_part.html
+++ b/InvenTree/company/templates/company/supplier_part.html
@@ -327,21 +327,21 @@ $('#order-part, #order-part2').click(function() {
});
$('#edit-part').click(function () {
- launchModalForm(
- "{% url 'supplier-part-edit' part.id %}",
- {
- reload: true
- }
- );
+
+ editSupplierPart({{ part.pk }}, {
+ onSuccess: function() {
+ location.reload();
+ }
+ });
});
$('#delete-part').click(function() {
- launchModalForm(
- "{% url 'supplier-part-delete' %}?part={{ part.id }}",
- {
- redirect: "{% url 'company-detail' part.supplier.id %}"
+
+ deleteSupplierPart({{ part.pk }}, {
+ onSuccess: function() {
+ window.location.href = "{% url 'company-detail' part.supplier.id %}";
}
- );
+ });
});
attachNavCallbacks({
diff --git a/InvenTree/company/templates/company/supplier_part_create.html b/InvenTree/company/templates/company/supplier_part_create.html
deleted file mode 100644
index 21c23f9075..0000000000
--- a/InvenTree/company/templates/company/supplier_part_create.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% extends "modal_form.html" %}
-
-{% load i18n %}
-
-{% block pre_form_content %}
-{{ block.super }}
-
-{% if part %}
-
- {% include "hover_image.html" with image=part.image %}
- {{ part.full_name}}
-
- {{ part.description }}
-
-{% endif %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/company/templates/company/supplier_part_delete.html b/InvenTree/company/templates/company/supplier_part_delete.html
deleted file mode 100644
index 40d9ce42de..0000000000
--- a/InvenTree/company/templates/company/supplier_part_delete.html
+++ /dev/null
@@ -1,31 +0,0 @@
-{% extends "modal_delete_form.html" %}
-{% load i18n %}
-
-{% block pre_form_content %}
-{% trans "Are you sure you want to delete the following Supplier Parts?" %}
-
-
-{% endblock %}
-
-{% block form_data %}
-
-{% for part in parts %}
-
-
-
-
- {% include "hover_image.html" with image=part.part.image %}
- {{ part.part.full_name }}
- |
-
- {% include "hover_image.html" with image=part.supplier.image %}
- {{ part.supplier.name }}
- |
-
- {{ part.SKU }}
- |
-
-{% endfor %}
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/company/test_api.py b/InvenTree/company/test_api.py
index 2da6d29198..a915ad1bf5 100644
--- a/InvenTree/company/test_api.py
+++ b/InvenTree/company/test_api.py
@@ -218,14 +218,27 @@ class ManufacturerTest(InvenTreeAPITestCase):
def test_supplier_part_create(self):
url = reverse('api-supplier-part-list')
- # Create supplier part
+ # Create a manufacturer part
+ response = self.post(
+ reverse('api-manufacturer-part-list'),
+ {
+ 'part': 1,
+ 'manufacturer': 7,
+ 'MPN': 'PART_NUMBER',
+ },
+ expected_code=201
+ )
+
+ pk = response.data['pk']
+
+ # Create a supplier part (associated with the new manufacturer part)
data = {
'part': 1,
'supplier': 1,
'SKU': 'SKU_TEST',
- 'manufacturer': 7,
- 'MPN': 'PART_NUMBER',
+ 'manufacturer_part': pk,
}
+
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
diff --git a/InvenTree/company/test_views.py b/InvenTree/company/test_views.py
index 6fc4281f2b..89968081c3 100644
--- a/InvenTree/company/test_views.py
+++ b/InvenTree/company/test_views.py
@@ -10,9 +10,6 @@ from django.urls import reverse
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
-from .models import ManufacturerPart
-from .models import SupplierPart
-
class CompanyViewTestBase(TestCase):
@@ -75,108 +72,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 +82,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())
diff --git a/InvenTree/company/tests.py b/InvenTree/company/tests.py
index e4a70b077a..79ffc34af5 100644
--- a/InvenTree/company/tests.py
+++ b/InvenTree/company/tests.py
@@ -192,18 +192,14 @@ class ManufacturerPartSimpleTest(TestCase):
SKU='SKU_TEST',
)
- kwargs = {
- 'manufacturer': manufacturer.id,
- 'MPN': 'MPN_TEST',
- }
- supplier_part.save(**kwargs)
+ supplier_part.save()
def test_exists(self):
- self.assertEqual(ManufacturerPart.objects.count(), 5)
+ self.assertEqual(ManufacturerPart.objects.count(), 4)
# Check that manufacturer part was created from supplier part creation
manufacturer_parts = ManufacturerPart.objects.filter(manufacturer=1)
- self.assertEqual(manufacturer_parts.count(), 2)
+ self.assertEqual(manufacturer_parts.count(), 1)
def test_delete(self):
# Remove a part
diff --git a/InvenTree/company/urls.py b/InvenTree/company/urls.py
index 7d2b6fb609..901e7f7089 100644
--- a/InvenTree/company/urls.py
+++ b/InvenTree/company/urls.py
@@ -35,16 +35,6 @@ manufacturer_part_urls = [
])),
]
-supplier_part_detail_urls = [
- url(r'^edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'),
-
+supplier_part_urls = [
url('^.*$', views.SupplierPartDetail.as_view(template_name='company/supplier_part.html'), name='supplier-part-detail'),
]
-
-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\d+)/', include(supplier_part_detail_urls)),
-]
diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py
index ab6344e810..f3a9af7628 100644
--- a/InvenTree/company/views.py
+++ b/InvenTree/company/views.py
@@ -10,31 +10,22 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic import DetailView, ListView
from django.urls import reverse
-from django.forms import HiddenInput
from django.core.files.base import ContentFile
-from moneyed import CURRENCIES
-
from PIL import Image
import requests
import io
-from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
-from InvenTree.helpers import str2bool
+from InvenTree.views import AjaxUpdateView
from InvenTree.views import InvenTreeRoleMixin
from .models import Company
from .models import ManufacturerPart
from .models import SupplierPart
-from part.models import Part
-from .forms import EditSupplierPartForm
from .forms import CompanyImageDownloadForm
-import common.models
-import common.settings
-
class CompanyIndex(InvenTreeRoleMixin, ListView):
""" View for displaying list of companies
@@ -231,272 +222,3 @@ class SupplierPartDetail(DetailView):
ctx = super().get_context_data(**kwargs)
return ctx
-
-
-class SupplierPartEdit(AjaxUpdateView):
- """ Update view for editing SupplierPart """
-
- model = SupplierPart
- context_object_name = 'part'
- form_class = EditSupplierPartForm
- ajax_template_name = 'modal_form.html'
- ajax_form_title = _('Edit Supplier Part')
-
- def save(self, supplier_part, form, **kwargs):
- """ Process ManufacturerPart data """
-
- manufacturer = form.cleaned_data.get('manufacturer', None)
- MPN = form.cleaned_data.get('MPN', None)
- kwargs = {'manufacturer': manufacturer,
- 'MPN': MPN,
- }
- supplier_part.save(**kwargs)
-
- def get_form(self):
- form = super().get_form()
-
- supplier_part = self.get_object()
-
- # Hide Manufacturer fields
- form.fields['manufacturer'].widget = HiddenInput()
- form.fields['MPN'].widget = HiddenInput()
-
- # It appears that hiding a MoneyField fails validation
- # Therefore the idea to set the value before hiding
- if form.is_valid():
- form.cleaned_data['single_pricing'] = supplier_part.unit_pricing
- # Hide the single-pricing field (only for creating a new SupplierPart!)
- form.fields['single_pricing'].widget = HiddenInput()
-
- return form
-
- def get_initial(self):
- """ Fetch data from ManufacturerPart """
-
- initials = super(SupplierPartEdit, self).get_initial().copy()
-
- supplier_part = self.get_object()
-
- if supplier_part.manufacturer_part:
- if supplier_part.manufacturer_part.manufacturer:
- initials['manufacturer'] = supplier_part.manufacturer_part.manufacturer.id
- initials['MPN'] = supplier_part.manufacturer_part.MPN
-
- 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.
-
- SupplierParts can be deleted using a variety of 'selectors'.
-
- - ?part= -> Delete a single SupplierPart object
- - ?parts=[] -> Delete a list of SupplierPart objects
-
- """
-
- success_url = '/supplier/'
- ajax_template_name = 'company/supplier_part_delete.html'
- ajax_form_title = _('Delete Supplier Part')
-
- role_required = 'purchase_order.delete'
-
- parts = []
-
- def get_context_data(self):
- ctx = {}
-
- ctx['parts'] = self.parts
-
- return ctx
-
- def get_parts(self):
- """ Determine which SupplierPart object(s) the user wishes to delete.
- """
-
- self.parts = []
-
- # User passes a single SupplierPart ID
- if 'part' in self.request.GET:
- try:
- self.parts.append(SupplierPart.objects.get(pk=self.request.GET.get('part')))
- except (ValueError, SupplierPart.DoesNotExist):
- pass
-
- elif 'parts[]' in self.request.GET:
-
- part_id_list = self.request.GET.getlist('parts[]')
-
- self.parts = SupplierPart.objects.filter(id__in=part_id_list)
-
- def get(self, request, *args, **kwargs):
- self.request = request
- self.get_parts()
-
- return self.renderJsonResponse(request, form=self.get_form())
-
- def post(self, request, *args, **kwargs):
- """ Handle the POST action for deleting supplier parts.
- """
-
- self.request = request
- self.parts = []
-
- for item in self.request.POST:
- if item.startswith('supplier-part-'):
- pk = item.replace('supplier-part-', '')
-
- try:
- self.parts.append(SupplierPart.objects.get(pk=pk))
- except (ValueError, SupplierPart.DoesNotExist):
- pass
-
- confirm = str2bool(self.request.POST.get('confirm_delete', False))
-
- data = {
- 'form_valid': confirm,
- }
-
- if confirm:
- for part in self.parts:
- part.delete()
-
- return self.renderJsonResponse(self.request, data=data, form=self.get_form())
diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html
index 0ba539baea..0666108e0b 100644
--- a/InvenTree/part/templates/part/detail.html
+++ b/InvenTree/part/templates/part/detail.html
@@ -801,45 +801,41 @@
)
});
+ function reloadSupplierPartTable() {
+ $('#supplier-part-table').bootstrapTable('refresh');
+ }
+
$('#supplier-create').click(function () {
- launchModalForm(
- "{% url 'supplier-part-create' %}",
- {
- reload: true,
- data: {
- part: {{ part.id }}
- },
- secondary: [
- {
- field: 'supplier',
- label: '{% trans "New Supplier" %}',
- title: '{% trans "Create new supplier" %}',
- },
- {
- field: 'manufacturer',
- label: '{% trans "New Manufacturer" %}',
- title: '{% trans "Create new manufacturer" %}',
- }
- ]
- });
+
+ createSupplierPart({
+ part: {{ part.pk }},
+ onSuccess: reloadSupplierPartTable,
+ });
});
$("#supplier-part-delete").click(function() {
var selections = $("#supplier-part-table").bootstrapTable("getSelections");
- var parts = [];
+ var requests = [];
- selections.forEach(function(item) {
- parts.push(item.pk);
- });
-
- launchModalForm("{% url 'supplier-part-delete' %}", {
- data: {
- parts: parts,
- },
- reload: true,
- });
+ showQuestionDialog(
+ '{% trans "Delete Supplier Parts?" %}',
+ '{% trans "All selected supplier parts will be deleted" %}',
+ {
+ accept: function() {
+ selections.forEach(function(part) {
+ var url = `/api/company/part/${part.pk}/`;
+
+ requests.push(inventreeDelete(url));
+ });
+
+ $.when.apply($, requests).then(function() {
+ reloadSupplierPartTable();
+ });
+ }
+ }
+ );
});
loadSupplierPartTable(
@@ -883,21 +879,10 @@
});
$('#manufacturer-create').click(function () {
-
- constructForm('{% url "api-manufacturer-part-list" %}', {
- fields: {
- part: {
- value: {{ part.pk }},
- hidden: true,
- },
- manufacturer: {},
- MPN: {},
- description: {},
- link: {},
- },
- method: 'POST',
- title: '{% trans "Add Manufacturer Part" %}',
- onSuccess: function() {
+
+ createManufacturerPart({
+ part: {{ part.pk }},
+ onSuccess: function() {
$("#manufacturer-part-table").bootstrapTable("refresh");
}
});
diff --git a/InvenTree/templates/js/company.js b/InvenTree/templates/js/company.js
index 7daa99e1c6..b202fbcd52 100644
--- a/InvenTree/templates/js/company.js
+++ b/InvenTree/templates/js/company.js
@@ -1,6 +1,142 @@
{% load i18n %}
+function manufacturerPartFields() {
+
+ return {
+ part: {},
+ manufacturer: {},
+ MPN: {
+ icon: 'fa-hashtag',
+ },
+ description: {},
+ link: {
+ icon: 'fa-link',
+ }
+ };
+}
+
+
+function createManufacturerPart(options={}) {
+
+ var fields = manufacturerPartFields();
+
+ if (options.part) {
+ fields.part.value = options.part;
+ fields.part.hidden = true;
+ }
+
+ if (options.manufacturer) {
+ fields.manufacturer.value = options.manufacturer;
+ }
+
+ constructForm('{% url "api-manufacturer-part-list" %}', {
+ fields: fields,
+ method: 'POST',
+ title: '{% trans "Add Manufacturer Part" %}',
+ onSuccess: options.onSuccess
+ });
+}
+
+
+function editManufacturerPart(part, options={}) {
+
+ var url = `/api/company/part/manufacturer/${part}/`;
+
+ constructForm(url, {
+ fields: manufacturerPartFields(),
+ title: '{% trans "Edit Manufacturer Part" %}',
+ onSuccess: options.onSuccess
+ });
+}
+
+function deleteManufacturerPart(part, options={}) {
+
+ constructForm(`/api/company/part/manufacturer/${part}/`, {
+ method: 'DELETE',
+ title: '{% trans "Delete Manufacturer Part" %}',
+ onSuccess: options.onSuccess,
+ });
+}
+
+
+function supplierPartFields() {
+
+ return {
+ part: {},
+ supplier: {},
+ SKU: {
+ icon: 'fa-hashtag',
+ },
+ manufacturer_part: {
+ filters: {
+ part_detail: true,
+ manufacturer_detail: true,
+ }
+ },
+ description: {},
+ link: {
+ icon: 'fa-link',
+ },
+ note: {
+ icon: 'fa-pencil-alt',
+ },
+ packaging: {
+ icon: 'fa-box',
+ }
+ };
+}
+
+/*
+ * Launch a form to create a new ManufacturerPart
+ */
+function createSupplierPart(options={}) {
+
+ var fields = supplierPartFields();
+
+ if (options.part) {
+ fields.manufacturer_part.filters.part = options.part;
+ fields.part.hidden = true;
+ fields.part.value = options.part;
+ }
+
+ if (options.supplier) {
+ fields.supplier.value = options.supplier;
+ }
+
+ if (options.manufacturer_part) {
+ fields.manufacturer_part.value = options.manufacturer_part;
+ }
+
+ constructForm('{% url "api-supplier-part-list" %}', {
+ fields: fields,
+ method: 'POST',
+ title: '{% trans "Add Supplier Part" %}',
+ onSuccess: options.onSuccess,
+ });
+}
+
+
+function editSupplierPart(part, options={}) {
+
+ constructForm(`/api/company/part/${part}/`, {
+ fields: supplierPartFields(),
+ title: '{% trans "Edit Supplier Part" %}',
+ onSuccess: options.onSuccess
+ });
+}
+
+
+function deleteSupplierPart(part, options={}) {
+
+ constructForm(`/api/company/part/${part}/`, {
+ method: 'DELETE',
+ title: '{% trans "Delete Supplier Part" %}',
+ onSuccess: options.onSuccess,
+ });
+}
+
+
// Returns a default form-set for creating / editing a Company object
function companyFormFields(options={}) {
@@ -323,8 +459,52 @@ function loadManufacturerPartTable(table, url, options) {
title: '{% trans "Description" %}',
sortable: false,
switchable: true,
+ },
+ {
+ field: 'actions',
+ title: '',
+ sortable: false,
+ switchable: false,
+ formatter: function(value, row) {
+ var pk = row.pk;
+
+ var html = ``;
+
+ html += makeIconButton('fa-edit icon-blue', 'button-manufacturer-part-edit', pk, '{% trans "Edit manufacturer part" %}');
+ html += makeIconButton('fa-trash-alt icon-red', 'button-manufacturer-part-delete', pk, '{% trans "Delete manufacturer part" %}');
+
+ html += '
';
+
+ return html;
+ }
}
],
+ onPostBody: function() {
+ // Callbacks
+ $(table).find('.button-manufacturer-part-edit').click(function() {
+ var pk = $(this).attr('pk');
+
+ editManufacturerPart(
+ pk,
+ {
+ onSuccess: function() {
+ $(table).bootstrapTable('refresh');
+ }
+ });
+ });
+
+ $(table).find('.button-manufacturer-part-delete').click(function() {
+ var pk = $(this).attr('pk');
+
+ deleteManufacturerPart(
+ pk,
+ {
+ onSuccess: function() {
+ $(table).bootstrapTable('refresh');
+ }
+ });
+ })
+ }
});
}
@@ -570,7 +750,51 @@ function loadSupplierPartTable(table, url, options) {
field: 'packaging',
title: '{% trans "Packaging" %}',
sortable: false,
+ },
+ {
+ field: 'actions',
+ title: '',
+ sortable: false,
+ switchable: false,
+ formatter: function(value, row) {
+ var pk = row.pk;
+
+ var html = ``;
+
+ html += makeIconButton('fa-edit icon-blue', 'button-supplier-part-edit', pk, '{% trans "Edit supplier part" %}');
+ html += makeIconButton('fa-trash-alt icon-red', 'button-supplier-part-delete', pk, '{% trans "Delete supplier part" %}');
+
+ html += '
';
+
+ return html;
+ }
}
],
+ onPostBody: function() {
+ // Callbacks
+ $(table).find('.button-supplier-part-edit').click(function() {
+ var pk = $(this).attr('pk');
+
+ editSupplierPart(
+ pk,
+ {
+ onSuccess: function() {
+ $(table).bootstrapTable('refresh');
+ }
+ });
+ });
+
+ $(table).find('.button-supplier-part-delete').click(function() {
+ var pk = $(this).attr('pk');
+
+ deleteSupplierPart(
+ pk,
+ {
+ onSuccess: function() {
+ $(table).bootstrapTable('refresh');
+ }
+ });
+ })
+ }
});
}
\ No newline at end of file
diff --git a/InvenTree/templates/js/forms.js b/InvenTree/templates/js/forms.js
index 6dd7dbd968..568ca5ac58 100644
--- a/InvenTree/templates/js/forms.js
+++ b/InvenTree/templates/js/forms.js
@@ -1113,7 +1113,7 @@ function initializeRelatedField(name, field, options) {
var pk = field.value;
var url = `${field.api_url}/${pk}/`.replace('//', '/');
- inventreeGet(url, {}, {
+ inventreeGet(url, field.filters || {}, {
success: function(data) {
setRelatedFieldData(name, data, options);
}
@@ -1211,6 +1211,9 @@ function renderModelData(name, model, data, parameters, options) {
case 'partparametertemplate':
renderer = renderPartParameterTemplate;
break;
+ case 'manufacturerpart':
+ renderer = renderManufacturerPart;
+ break;
case 'supplierpart':
renderer = renderSupplierPart;
break;
diff --git a/InvenTree/templates/js/model_renderers.js b/InvenTree/templates/js/model_renderers.js
index ddb4897d8d..12e00cab2c 100644
--- a/InvenTree/templates/js/model_renderers.js
+++ b/InvenTree/templates/js/model_renderers.js
@@ -174,7 +174,35 @@ function renderPartParameterTemplate(name, data, parameters, options) {
}
-// Rendered for "SupplierPart" model
+// Renderer for "ManufacturerPart" model
+function renderManufacturerPart(name, data, parameters, options) {
+
+ var manufacturer_image = null;
+ var part_image = null;
+
+ if (data.manufacturer_detail) {
+ manufacturer_image = data.manufacturer_detail.image;
+ }
+
+ if (data.part_detail) {
+ part_image = data.part_detail.thumbnail || data.part_detail.image;
+ }
+
+ var html = '';
+
+ html += select2Thumbnail(manufacturer_image);
+ html += select2Thumbnail(part_image);
+
+ html += ` ${data.manufacturer_detail.name} - ${data.MPN}`;
+ html += ` - ${data.part_detail.full_name}`;
+
+ html += `{% trans "Manufacturer Part ID" %}: ${data.pk}`;
+
+ return html;
+}
+
+
+// Renderer for "SupplierPart" model
function renderSupplierPart(name, data, parameters, options) {
var supplier_image = null;
diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js
index 754d3d5b59..1d38b631a5 100644
--- a/InvenTree/templates/js/stock.js
+++ b/InvenTree/templates/js/stock.js
@@ -1616,27 +1616,6 @@ function createNewStockItem(options) {
},
];
- options.secondary = [
- {
- field: 'part',
- label: '{% trans "New Part" %}',
- title: '{% trans "Create New Part" %}',
- url: "{% url 'part-create' %}",
- },
- {
- field: 'supplier_part',
- label: '{% trans "New Supplier Part" %}',
- title: '{% trans "Create new Supplier Part" %}',
- url: "{% url 'supplier-part-create' %}"
- },
- {
- field: 'location',
- label: '{% trans "New Location" %}',
- title: '{% trans "Create New Location" %}',
- url: "{% url 'stock-location-create' %}",
- },
- ];
-
launchModalForm("{% url 'stock-item-create' %}", options);
}
diff --git a/InvenTree/templates/js/tables.js b/InvenTree/templates/js/tables.js
index 8fedeb8f9e..afe1fefbc9 100644
--- a/InvenTree/templates/js/tables.js
+++ b/InvenTree/templates/js/tables.js
@@ -1,5 +1,11 @@
{% load i18n %}
+
+function reloadtable(table) {
+ $(table).bootstrapTable('refresh');
+}
+
+
function editButton(url, text='Edit') {
return "";
}