diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py index 44c5dd3c59..7177703afa 100644 --- a/InvenTree/InvenTree/version.py +++ b/InvenTree/InvenTree/version.py @@ -8,12 +8,16 @@ import re import common.models -INVENTREE_SW_VERSION = "0.3.0" +INVENTREE_SW_VERSION = "0.3.1" -INVENTREE_API_VERSION = 7 +INVENTREE_API_VERSION = 8 """ -Increment thi API version number whenever there is a significant change to the API that any clients need to know about +Increment this API version number whenever there is a significant change to the API that any clients need to know about + +v8 -> 2021-07-19 + - Refactors the API interface for SupplierPart and ManufacturerPart models + - ManufacturerPart objects can no longer be created via the SupplierPart API endpoint v7 -> 2021-07-03 - Introduced the concept of "API forms" in https://github.com/inventree/InvenTree/pull/1716 diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index c8a5839f4e..3d18d56880 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -233,6 +233,13 @@ class InvenTreeSetting(models.Model): 'validator': bool, }, + 'PART_CREATE_INITIAL': { + 'name': _('Create initial stock'), + 'description': _('Create initial stock on part creation'), + 'default': False, + 'validator': bool, + }, + 'PART_INTERNAL_PRICE': { 'name': _('Internal Prices'), 'description': _('Enable internal prices for parts'), 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 %}
- {% 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 }} - | -