Merge branch 'master' into partial-shipment

This commit is contained in:
Oliver 2021-10-27 01:05:55 +11:00
commit 75845ce1ca
6 changed files with 97 additions and 17 deletions

View File

@ -36,6 +36,13 @@ class InvenTreeMoneySerializer(MoneyField):
Ref: https://github.com/django-money/django-money/blob/master/djmoney/contrib/django_rest_framework/fields.py Ref: https://github.com/django-money/django-money/blob/master/djmoney/contrib/django_rest_framework/fields.py
""" """
def __init__(self, *args, **kwargs):
kwargs["max_digits"] = kwargs.get("max_digits", 19)
kwargs["decimal_places"] = kwargs.get("decimal_places", 4)
super().__init__(*args, **kwargs)
def get_value(self, data): def get_value(self, data):
""" """
Test that the returned amount is a valid Decimal Test that the returned amount is a valid Decimal
@ -52,7 +59,7 @@ class InvenTreeMoneySerializer(MoneyField):
amount = Decimal(amount) amount = Decimal(amount)
except: except:
raise ValidationError({ raise ValidationError({
self.field_name: _("Must be a valid number") self.field_name: [_("Must be a valid number")],
}) })
currency = data.get(get_currency_field_name(self.field_name), self.default_currency) currency = data.get(get_currency_field_name(self.field_name), self.default_currency)

View File

@ -9,6 +9,7 @@ from rest_framework import serializers
from sql_util.utils import SubqueryCount from sql_util.utils import SubqueryCount
from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeModelSerializer
from InvenTree.serializers import InvenTreeMoneySerializer
from InvenTree.serializers import InvenTreeImageSerializerField from InvenTree.serializers import InvenTreeImageSerializerField
from part.serializers import PartBriefSerializer from part.serializers import PartBriefSerializer
@ -256,7 +257,11 @@ class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
quantity = serializers.FloatField() quantity = serializers.FloatField()
price = serializers.CharField() price = InvenTreeMoneySerializer(
allow_null=True,
required=True,
label=_('Price'),
)
price_currency = serializers.ChoiceField( price_currency = serializers.ChoiceField(
choices=currency_code_mappings(), choices=currency_code_mappings(),

View File

@ -153,7 +153,6 @@ class POLineItemSerializer(InvenTreeModelSerializer):
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( purchase_price = InvenTreeMoneySerializer(
max_digits=19, decimal_places=4,
allow_null=True allow_null=True
) )
@ -556,8 +555,6 @@ class SOLineItemSerializer(InvenTreeModelSerializer):
fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True) fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True)
sale_price = InvenTreeMoneySerializer( sale_price = InvenTreeMoneySerializer(
max_digits=19,
decimal_places=4,
allow_null=True allow_null=True
) )

View File

@ -109,7 +109,6 @@ class PartSalePriceSerializer(InvenTreeModelSerializer):
quantity = serializers.FloatField() quantity = serializers.FloatField()
price = InvenTreeMoneySerializer( price = InvenTreeMoneySerializer(
max_digits=19, decimal_places=4,
allow_null=True allow_null=True
) )
@ -134,7 +133,6 @@ class PartInternalPriceSerializer(InvenTreeModelSerializer):
quantity = serializers.FloatField() quantity = serializers.FloatField()
price = InvenTreeMoneySerializer( price = InvenTreeMoneySerializer(
max_digits=19, decimal_places=4,
allow_null=True allow_null=True
) )

View File

@ -148,7 +148,6 @@ class StockItemSerializer(InvenTreeModelSerializer):
purchase_price = InvenTreeMoneySerializer( purchase_price = InvenTreeMoneySerializer(
label=_('Purchase Price'), label=_('Purchase Price'),
max_digits=19, decimal_places=4,
allow_null=True allow_null=True
) )

View File

@ -621,6 +621,10 @@ function submitFormData(fields, options) {
var has_files = false; var has_files = false;
var data_valid = true;
var data_errors = {};
// Extract values for each field // Extract values for each field
for (var idx = 0; idx < options.field_names.length; idx++) { for (var idx = 0; idx < options.field_names.length; idx++) {
@ -633,6 +637,21 @@ function submitFormData(fields, options) {
if (field) { if (field) {
switch (field.type) {
// Ensure numerical fields are "valid"
case 'integer':
case 'float':
case 'decimal':
if (!validateFormField(name, options)) {
data_valid = false;
data_errors[name] = ['{% trans "Enter a valid number" %}'];
}
break;
default:
break;
}
var value = getFormFieldValue(name, field, options); var value = getFormFieldValue(name, field, options);
// Handle file inputs // Handle file inputs
@ -662,6 +681,11 @@ function submitFormData(fields, options) {
} }
} }
if (!data_valid) {
handleFormErrors(data_errors, fields, options);
return;
}
var upload_func = inventreePut; var upload_func = inventreePut;
if (has_files) { if (has_files) {
@ -732,7 +756,8 @@ function updateFieldValues(fields, options) {
* Update the value of a named field * Update the value of a named field
*/ */
function updateFieldValue(name, value, field, options) { function updateFieldValue(name, value, field, options) {
var el = $(options.modal).find(`#id_${name}`);
var el = getFormFieldElement(name, options);
if (!el) { if (!el) {
console.log(`WARNING: updateFieldValue could not find field '${name}'`); console.log(`WARNING: updateFieldValue could not find field '${name}'`);
@ -760,6 +785,46 @@ function updateFieldValue(name, value, field, options) {
} }
// Find the named field element in the modal DOM
function getFormFieldElement(name, options) {
var el = $(options.modal).find(`#id_${name}`);
if (!el.exists) {
console.log(`ERROR: Could not find form element for field '${name}'`);
}
return el;
}
/*
* Check that a "numerical" input field has a valid number in it.
* An invalid number is expunged at the client side by the getFormFieldValue() function,
* which means that an empty string '' is sent to the server if the number is not valud.
* This can result in confusing error messages displayed under the form field.
*
* So, we can invalid numbers and display errors *before* the form is submitted!
*/
function validateFormField(name, options) {
if (getFormFieldElement(name, options)) {
var el = document.getElementById(`id_${name}`);
if (el.validity.valueMissing) {
// Accept empty strings (server will validate)
return true;
} else {
return el.validity.valid;
}
} else {
return false;
}
}
/* /*
* Extract and field value before sending back to the server * Extract and field value before sending back to the server
* *
@ -771,7 +836,7 @@ function updateFieldValue(name, value, field, options) {
function getFormFieldValue(name, field, options) { function getFormFieldValue(name, field, options) {
// Find the HTML element // Find the HTML element
var el = $(options.modal).find(`#id_${name}`); var el = getFormFieldElement(name, options);
if (!el) { if (!el) {
return null; return null;
@ -1086,7 +1151,9 @@ function addFieldCallbacks(fields, options) {
function addFieldCallback(name, field, options) { function addFieldCallback(name, field, options) {
$(options.modal).find(`#id_${name}`).change(function() { var el = getFormFieldElement(name, options);
el.change(function() {
var value = getFormFieldValue(name, field, options); var value = getFormFieldValue(name, field, options);
@ -1299,7 +1366,7 @@ function initializeRelatedField(field, fields, options) {
} }
// Find the select element and attach a select2 to it // Find the select element and attach a select2 to it
var select = $(options.modal).find(`#id_${name}`); var select = getFormFieldElement(name, options);
// Add a button to launch a 'secondary' modal // Add a button to launch a 'secondary' modal
if (field.secondary != null) { if (field.secondary != null) {
@ -1492,7 +1559,7 @@ function initializeRelatedField(field, fields, options) {
*/ */
function setRelatedFieldData(name, data, options) { function setRelatedFieldData(name, data, options) {
var select = $(options.modal).find(`#id_${name}`); var select = getFormFieldElement(name, options);
var option = new Option(name, data.pk, true, true); var option = new Option(name, data.pk, true, true);
@ -1513,9 +1580,7 @@ function setRelatedFieldData(name, data, options) {
function initializeChoiceField(field, fields, options) { function initializeChoiceField(field, fields, options) {
var name = field.name; var select = getFormFieldElement(field.name, options);
var select = $(options.modal).find(`#id_${name}`);
select.select2({ select.select2({
dropdownAutoWidth: false, dropdownAutoWidth: false,
@ -1929,8 +1994,17 @@ function constructInputOptions(name, classes, type, parameters) {
opts.push(`placeholder='${parameters.placeholder}'`); opts.push(`placeholder='${parameters.placeholder}'`);
} }
if (parameters.type == 'boolean') { switch (parameters.type) {
case 'boolean':
opts.push(`style='display: inline-block; width: 20px; margin-right: 20px;'`); opts.push(`style='display: inline-block; width: 20px; margin-right: 20px;'`);
break;
case 'integer':
case 'float':
case 'decimal':
opts.push(`step='any'`);
break;
default:
break;
} }
if (parameters.multiline) { if (parameters.multiline) {