Refactoring forms for order line items

- Required some fixes for money serializer
-
This commit is contained in:
Oliver 2021-07-03 01:17:29 +10:00
parent 1cdf03ef49
commit 8c3a4b60ab
8 changed files with 76 additions and 46 deletions

View File

@ -5,16 +5,58 @@ Serializers used in various InvenTree apps
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
import os import os
from decimal import Decimal
from django.conf import settings from django.conf import settings
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
from django.utils.translation import ugettext_lazy as _
from djmoney.contrib.django_rest_framework.fields import MoneyField
from djmoney.money import Money
from djmoney.utils import MONEY_CLASSES, get_currency_field_name
from rest_framework import serializers from rest_framework import serializers
from rest_framework.utils import model_meta from rest_framework.utils import model_meta
from rest_framework.fields import empty from rest_framework.fields import empty
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from rest_framework.serializers import DecimalField
class InvenTreeMoneySerializer(MoneyField):
"""
Custom serializer for 'MoneyField',
which ensures that passed values are numerically valid
Ref: https://github.com/django-money/django-money/blob/master/djmoney/contrib/django_rest_framework/fields.py
"""
def get_value(self, data):
"""
Test that the returned amount is a valid Decimal
"""
amount = super(DecimalField, self).get_value(data)
# Convert an empty string to None
if len(str(amount).strip()) == 0:
amount = None
try:
if amount is not None:
amount = Decimal(amount)
except:
raise ValidationError(_("Must be a valid number"))
currency = data.get(get_currency_field_name(self.field_name), self.default_currency)
if currency and amount is not None and not isinstance(amount, MONEY_CLASSES) and amount is not empty:
return Money(amount, currency)
return amount
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):

View File

@ -514,7 +514,7 @@ class SOLineItemList(generics.ListCreateAPIView):
] ]
class SOLineItemDetail(generics.RetrieveUpdateAPIView): class SOLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
""" API endpoint for detail view of a SalesOrderLineItem object """ """ API endpoint for detail view of a SalesOrderLineItem object """
queryset = SalesOrderLineItem.objects.all() queryset = SalesOrderLineItem.objects.all()

View File

@ -698,7 +698,13 @@ class OrderLineItem(models.Model):
class Meta: class Meta:
abstract = True abstract = True
quantity = RoundingDecimalField(max_digits=15, decimal_places=5, validators=[MinValueValidator(0)], default=1, verbose_name=_('Quantity'), help_text=_('Item quantity')) quantity = RoundingDecimalField(
verbose_name=_('Quantity'),
help_text=_('Item quantity'),
default=1,
max_digits=15, decimal_places=5,
validators=[MinValueValidator(0)],
)
reference = models.CharField(max_length=100, blank=True, verbose_name=_('Reference'), help_text=_('Line item reference')) reference = models.CharField(max_length=100, blank=True, verbose_name=_('Reference'), help_text=_('Line item reference'))

View File

@ -16,6 +16,7 @@ from sql_util.utils import SubqueryCount
import djmoney.settings import djmoney.settings
from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeModelSerializer
from InvenTree.serializers import InvenTreeMoneySerializer
from InvenTree.serializers import InvenTreeAttachmentSerializerField from InvenTree.serializers import InvenTreeAttachmentSerializerField
from company.serializers import CompanyBriefSerializer, SupplierPartSerializer from company.serializers import CompanyBriefSerializer, SupplierPartSerializer
@ -124,6 +125,8 @@ class POLineItemSerializer(InvenTreeModelSerializer):
part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True) part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True)
supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True) supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True)
purchase_price = InvenTreeMoneySerializer(max_digits=19, decimal_places=4)
purchase_price_string = serializers.CharField(source='purchase_price', read_only=True) purchase_price_string = serializers.CharField(source='purchase_price', read_only=True)
destination = LocationBriefSerializer(source='get_destination', read_only=True) destination = LocationBriefSerializer(source='get_destination', read_only=True)
@ -335,13 +338,20 @@ class SOLineItemSerializer(InvenTreeModelSerializer):
part_detail = PartBriefSerializer(source='part', many=False, read_only=True) part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
allocations = SalesOrderAllocationSerializer(many=True, read_only=True) allocations = SalesOrderAllocationSerializer(many=True, read_only=True)
# TODO: Once https://github.com/inventree/InvenTree/issues/1687 is fixed, remove default values quantity = serializers.FloatField()
quantity = serializers.FloatField(default=1)
allocated = serializers.FloatField(source='allocated_quantity', read_only=True) allocated = serializers.FloatField(source='allocated_quantity', read_only=True)
fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True) fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True)
sale_price = InvenTreeMoneySerializer(max_digits=19, decimal_places=4)
sale_price_string = serializers.CharField(source='sale_price', read_only=True) sale_price_string = serializers.CharField(source='sale_price', read_only=True)
sale_price_currency = serializers.ChoiceField(
choices=djmoney.settings.CURRENCY_CHOICES,
help_text=_('Sale price currency'),
)
class Meta: class Meta:
model = SalesOrderLineItem model = SalesOrderLineItem

View File

@ -377,16 +377,26 @@ function setupCallbacks() {
var pk = $(this).attr('pk'); var pk = $(this).attr('pk');
launchModalForm(`/order/sales-order/line/${pk}/edit/`, { constructForm(`/api/order/so-line/${pk}/`, {
success: reloadTable, fields: {
quantity: {},
reference: {},
sale_price: {},
sale_price_currency: {},
notes: {},
},
title: '{% trans "Edit Line Item" %}',
onSuccess: reloadTable,
}); });
}); });
table.find(".button-delete").click(function() { table.find(".button-delete").click(function() {
var pk = $(this).attr('pk'); var pk = $(this).attr('pk');
launchModalForm(`/order/sales-order/line/${pk}/delete/`, { constructForm(`/api/order/so-line/${pk}/`, {
success: reloadTable, method: 'DELETE',
title: '{% trans "Delete Line Item" %}',
onSuccess: reloadTable,
}); });
}); });

View File

@ -1,6 +0,0 @@
{% extends "modal_delete_form.html" %}
{% load i18n %}
{% block pre_form_content %}
{% trans "Are you sure you wish to delete this line item?" %}
{% endblock %}

View File

@ -57,10 +57,6 @@ sales_order_urls = [
url(r'^line/', include([ url(r'^line/', include([
url(r'^new/', views.SOLineItemCreate.as_view(), name='so-line-item-create'), url(r'^new/', views.SOLineItemCreate.as_view(), name='so-line-item-create'),
url(r'^(?P<pk>\d+)/', include([
url(r'^edit/', views.SOLineItemEdit.as_view(), name='so-line-item-edit'),
url(r'^delete/', views.SOLineItemDelete.as_view(), name='so-line-item-delete'),
])),
])), ])),
# URLs for sales order allocations # URLs for sales order allocations

View File

@ -1197,34 +1197,6 @@ class SOLineItemCreate(AjaxCreateView):
return ret return ret
class SOLineItemEdit(AjaxUpdateView):
""" View for editing a SalesOrderLineItem """
model = SalesOrderLineItem
form_class = order_forms.EditSalesOrderLineItemForm
ajax_form_title = _('Edit Line Item')
def get_form(self):
form = super().get_form()
form.fields.pop('order')
form.fields.pop('part')
return form
class SOLineItemDelete(AjaxDeleteView):
model = SalesOrderLineItem
ajax_form_title = _("Delete Line Item")
ajax_template_name = "order/so_lineitem_delete.html"
def get_data(self):
return {
'danger': _('Deleted line item'),
}
class SalesOrderAssignSerials(AjaxView, FormMixin): class SalesOrderAssignSerials(AjaxView, FormMixin):
""" """
View for assigning stock items to a sales order, View for assigning stock items to a sales order,