diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index a49209dfe7..1ef1df6a8f 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -4,11 +4,15 @@ InvenTree API version information # InvenTree API version -INVENTREE_API_VERSION = 42 +INVENTREE_API_VERSION = 43 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about +v43 -> 2022-04-26 : https://github.com/inventree/InvenTree/pull/2875 + - Adds API detail endpoint for PartSalePrice model + - Adds API detail endpoint for PartInternalPrice model + v42 -> 2022-04-26 : https://github.com/inventree/InvenTree/pull/2833 - Adds variant stock information to the Part and BomItem serializers diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index b025791a7f..1a80c87322 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -262,6 +262,15 @@ class CategoryTree(generics.ListAPIView): ordering = ['level', 'name'] +class PartSalePriceDetail(generics.RetrieveUpdateDestroyAPIView): + """ + Detail endpoint for PartSellPriceBreak model + """ + + queryset = PartSellPriceBreak.objects.all() + serializer_class = part_serializers.PartSalePriceSerializer + + class PartSalePriceList(generics.ListCreateAPIView): """ API endpoint for list view of PartSalePriceBreak model @@ -279,6 +288,15 @@ class PartSalePriceList(generics.ListCreateAPIView): ] +class PartInternalPriceDetail(generics.RetrieveUpdateDestroyAPIView): + """ + Detail endpoint for PartInternalPriceBreak model + """ + + queryset = PartInternalPriceBreak.objects.all() + serializer_class = part_serializers.PartInternalPriceSerializer + + class PartInternalPriceList(generics.ListCreateAPIView): """ API endpoint for list view of PartInternalPriceBreak model @@ -1920,11 +1938,13 @@ part_api_urls = [ # Base URL for part sale pricing url(r'^sale-price/', include([ + url(r'^(?P\d+)/', PartSalePriceDetail.as_view(), name='api-part-sale-price-detail'), url(r'^.*$', PartSalePriceList.as_view(), name='api-part-sale-price-list'), ])), # Base URL for part internal pricing url(r'^internal-price/', include([ + url(r'^(?P\d+)/', PartInternalPriceDetail.as_view(), name='api-part-internal-price-detail'), url(r'^.*$', PartInternalPriceList.as_view(), name='api-part-internal-price-list'), ])), diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 387d3df1d5..0e865ea74b 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -17,6 +17,8 @@ from rest_framework import serializers from sql_util.utils import SubqueryCount, SubquerySum from djmoney.contrib.django_rest_framework import MoneyField +from common.settings import currency_code_default, currency_code_mappings + from InvenTree.serializers import (DataFileUploadSerializer, DataFileExtractSerializer, InvenTreeAttachmentSerializerField, @@ -148,6 +150,13 @@ class PartSalePriceSerializer(InvenTreeModelSerializer): allow_null=True ) + price_currency = serializers.ChoiceField( + choices=currency_code_mappings(), + default=currency_code_default, + label=_('Currency'), + help_text=_('Purchase currency of this stock item'), + ) + price_string = serializers.CharField(source='price', read_only=True) class Meta: @@ -157,6 +166,7 @@ class PartSalePriceSerializer(InvenTreeModelSerializer): 'part', 'quantity', 'price', + 'price_currency', 'price_string', ] @@ -172,6 +182,13 @@ class PartInternalPriceSerializer(InvenTreeModelSerializer): allow_null=True ) + price_currency = serializers.ChoiceField( + choices=currency_code_mappings(), + default=currency_code_default, + label=_('Currency'), + help_text=_('Purchase currency of this stock item'), + ) + price_string = serializers.CharField(source='price', read_only=True) class Meta: @@ -181,6 +198,7 @@ class PartInternalPriceSerializer(InvenTreeModelSerializer): 'part', 'quantity', 'price', + 'price_currency', 'price_string', ] diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index a996151879..5ec1821b3d 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -1008,7 +1008,7 @@ pb_url_slug: 'internal-price', pb_url: '{% url 'api-part-internal-price-list' %}', pb_new_btn: $('#new-internal-price-break'), - pb_new_url: '{% url 'internal-price-break-create' %}', + pb_new_url: '{% url 'api-part-internal-price-list' %}', linkedGraph: $('#InternalPriceBreakChart'), }, ); @@ -1024,7 +1024,7 @@ pb_url_slug: 'sale-price', pb_url: "{% url 'api-part-sale-price-list' %}", pb_new_btn: $('#new-price-break'), - pb_new_url: '{% url 'sale-price-break-create' %}', + pb_new_url: '{% url 'api-part-sale-price-list' %}', linkedGraph: $('#SalePriceBreakChart'), }, ); diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html index 53723b729b..d47f49ef76 100644 --- a/InvenTree/part/templates/part/prices.html +++ b/InvenTree/part/templates/part/prices.html @@ -303,3 +303,4 @@ {% endif %} {% endif %} + \ No newline at end of file diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 55a3dc52eb..04d2b0a5f8 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -13,18 +13,6 @@ from django.conf.urls import url, include from . import views -sale_price_break_urls = [ - url(r'^new/', views.PartSalePriceBreakCreate.as_view(), name='sale-price-break-create'), - url(r'^(?P\d+)/edit/', views.PartSalePriceBreakEdit.as_view(), name='sale-price-break-edit'), - url(r'^(?P\d+)/delete/', views.PartSalePriceBreakDelete.as_view(), name='sale-price-break-delete'), -] - -internal_price_break_urls = [ - url(r'^new/', views.PartInternalPriceBreakCreate.as_view(), name='internal-price-break-create'), - url(r'^(?P\d+)/edit/', views.PartInternalPriceBreakEdit.as_view(), name='internal-price-break-edit'), - url(r'^(?P\d+)/delete/', views.PartInternalPriceBreakDelete.as_view(), name='internal-price-break-delete'), -] - part_parameter_urls = [ url(r'^template/new/', views.PartParameterTemplateCreate.as_view(), name='part-param-template-create'), url(r'^template/(?P\d+)/edit/', views.PartParameterTemplateEdit.as_view(), name='part-param-template-edit'), @@ -86,12 +74,6 @@ part_urls = [ # Part category url(r'^category/', include(category_urls)), - # Part price breaks - url(r'^sale-price/', include(sale_price_break_urls)), - - # Part internal price breaks - url(r'^internal-price/', include(internal_price_break_urls)), - # Part parameters url(r'^parameter/', include(part_parameter_urls)), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index ab4b3fec72..b0633d8c32 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1230,102 +1230,3 @@ class CategoryParameterTemplateDelete(AjaxDeleteView): return None return self.object - - -class PartSalePriceBreakCreate(AjaxCreateView): - """ - View for creating a sale price break for a part - """ - - model = PartSellPriceBreak - form_class = part_forms.EditPartSalePriceBreakForm - ajax_form_title = _('Add Price Break') - - def get_data(self): - return { - 'success': _('Added new price break') - } - - def get_part(self): - try: - part = Part.objects.get(id=self.request.GET.get('part')) - except (ValueError, Part.DoesNotExist): - part = None - - if part is None: - try: - part = Part.objects.get(id=self.request.POST.get('part')) - except (ValueError, Part.DoesNotExist): - part = None - - return part - - def get_form(self): - - form = super(AjaxCreateView, self).get_form() - form.fields['part'].widget = HiddenInput() - - return form - - def get_initial(self): - - initials = super(AjaxCreateView, self).get_initial() - - initials['part'] = self.get_part() - - default_currency = inventree_settings.currency_code_default() - currency = CURRENCIES.get(default_currency, None) - - if currency is not None: - initials['price'] = [1.0, currency] - - return initials - - -class PartSalePriceBreakEdit(AjaxUpdateView): - """ View for editing a sale price break """ - - model = PartSellPriceBreak - form_class = part_forms.EditPartSalePriceBreakForm - ajax_form_title = _('Edit Price Break') - - def get_form(self): - - form = super().get_form() - form.fields['part'].widget = HiddenInput() - - return form - - -class PartSalePriceBreakDelete(AjaxDeleteView): - """ View for deleting a sale price break """ - - model = PartSellPriceBreak - ajax_form_title = _("Delete Price Break") - ajax_template_name = "modal_delete_form.html" - - -class PartInternalPriceBreakCreate(PartSalePriceBreakCreate): - """ View for creating a internal price break for a part """ - - model = PartInternalPriceBreak - form_class = part_forms.EditPartInternalPriceBreakForm - ajax_form_title = _('Add Internal Price Break') - permission_required = 'roles.sales_order.add' - - -class PartInternalPriceBreakEdit(PartSalePriceBreakEdit): - """ View for editing a internal price break """ - - model = PartInternalPriceBreak - form_class = part_forms.EditPartInternalPriceBreakForm - ajax_form_title = _('Edit Internal Price Break') - permission_required = 'roles.sales_order.change' - - -class PartInternalPriceBreakDelete(PartSalePriceBreakDelete): - """ View for deleting a internal price break """ - - model = PartInternalPriceBreak - ajax_form_title = _("Delete Internal Price Break") - permission_required = 'roles.sales_order.delete' diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index 35d5d0d5a6..d552bcb9d7 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -1930,7 +1930,9 @@ function loadPriceBreakTable(table, options) { formatNoMatches: function() { return `{% trans "No ${human_name} information found" %}`; }, - queryParams: {part: options.part}, + queryParams: { + part: options.part + }, url: options.url, onLoadSuccess: function(tableData) { if (linkedGraph) { @@ -2036,36 +2038,45 @@ function initPriceBreakSet(table, options) { } pb_new_btn.click(function() { - launchModalForm(pb_new_url, - { - success: reloadPriceBreakTable, - data: { - part: part_id, - } - } - ); + + constructForm(pb_new_url, { + fields: { + part: { + hidden: true, + value: part_id, + }, + quantity: {}, + price: {}, + price_currency: {}, + }, + method: 'POST', + title: '{% trans "Add Price Break" %}', + onSuccess: reloadPriceBreakTable, + }); }); table.on('click', `.button-${pb_url_slug}-delete`, function() { var pk = $(this).attr('pk'); - launchModalForm( - `/part/${pb_url_slug}/${pk}/delete/`, - { - success: reloadPriceBreakTable - } - ); + constructForm(`${pb_url}${pk}/`, { + method: 'DELETE', + title: '{% trans "Delete Price Break" %}', + onSuccess: reloadPriceBreakTable, + }); }); table.on('click', `.button-${pb_url_slug}-edit`, function() { var pk = $(this).attr('pk'); - launchModalForm( - `/part/${pb_url_slug}/${pk}/edit/`, - { - success: reloadPriceBreakTable - } - ); + constructForm(`${pb_url}${pk}/`, { + fields: { + quantity: {}, + price: {}, + price_currency: {}, + }, + title: '{% trans "Edit Price Break" %}', + onSuccess: reloadPriceBreakTable, + }); }); }