diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index cc07dd3fea..eabc5e630e 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -246,6 +246,7 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer): total_price = serializers.FloatField(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) purchase_price = InvenTreeMoneySerializer( diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index 01f9e162eb..cc138052ef 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -1218,29 +1218,26 @@ function handleFormErrors(errors, fields={}, options={}) { for (var field_name in errors) { - if (field_name in fields) { + var field = fields[field_name] || {}; - var field = fields[field_name]; + if ((field.type == 'field') && ('child' in field)) { + // This is a "nested" field + handleNestedErrors(errors, field_name, options); + } else { + // This is a "simple" field - if ((field.type == 'field') && ('child' in field)) { - // This is a "nested" field - handleNestedErrors(errors, field_name, options); - } else { - // This is a "simple" field + var field_errors = errors[field_name]; - var field_errors = errors[field_name]; + if (field_errors && !first_error_field && isFieldVisible(field_name, options)) { + first_error_field = field_name; + } - if (field_errors && !first_error_field && isFieldVisible(field_name, options)) { - first_error_field = field_name; - } + // Add an entry for each returned error message + for (var ii = field_errors.length-1; ii >= 0; ii--) { - // Add an entry for each returned error message - for (var ii = field_errors.length-1; ii >= 0; ii--) { + var error_text = field_errors[ii]; - var error_text = field_errors[ii]; - - addFieldErrorMessage(field_name, error_text, ii, options); - } + addFieldErrorMessage(field_name, error_text, ii, options); } } } @@ -1929,6 +1926,10 @@ function renderModelData(name, model, data, parameters, options) { function getFieldName(name, options={}) { var field_name = name; + if (options.field_suffix) { + field_name += options.field_suffix; + } + if (options && options.depth) { field_name += `_${options.depth}`; } diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index 882657bb0a..042e5b785b 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -514,7 +514,7 @@ function orderParts(parts_list, options={}) { } var quantity_input = constructField( - `items_quantity_${pk}`, + `quantity_${pk}`, { type: 'decimal', min_value: 0, @@ -552,7 +552,7 @@ function orderParts(parts_list, options={}) { `; var purchase_order_input = constructField( - `purchase_order_${pk}`, + `order_${pk}`, { type: 'related field', required: true, @@ -565,16 +565,6 @@ function orderParts(parts_list, options={}) { var buttons = `
`; - buttons += makeIconButton( - 'fa-layer-group', - 'button-row-expand', - pk, - '{% trans "Expand Row" %}', - { - collapseTarget: `order_row_expand_${pk}`, - } - ); - if (parts.length > 1) { buttons += makeIconButton( 'fa-times icon-red', @@ -584,26 +574,23 @@ function orderParts(parts_list, options={}) { ); } + // Button to add row to purchase order + buttons += makeIconButton( + 'fa-shopping-cart icon-blue', + 'button-row-add', + pk, + '{% trans "Add to purchase order" %}', + ); + buttons += `
`; var html = ` - ${thumb} ${part.full_name} - ${supplier_part_input} - ${purchase_order_input} - ${quantity_input} - ${buttons} - `; - - // Add a second row "underneath" the first one, but collapsed - // Allows extra data to be added if required, but hidden by default - html += ` - - - reference goes here - - - + ${thumb} ${part.full_name} + ${supplier_part_input} + ${purchase_order_input} + ${quantity_input} + ${buttons} `; return html; @@ -662,7 +649,7 @@ function orderParts(parts_list, options={}) { // Configure the "purchase order" field initializeRelatedField({ - name: `purchase_order_${part.pk}`, + name: `order_${part.pk}`, model: 'purchaseorder', api_url: '{% url "api-po-list" %}', required: true, @@ -678,6 +665,50 @@ function orderParts(parts_list, options={}) { }, null, opts); }); + // Add callback for "add to purchase order" button + $(opts.modal).find('.button-row-add').click(function() { + var pk = $(this).attr('pk'); + + opts.field_suffix = null; + + // Extract information from the row + var data = { + quantity: getFormFieldValue(`quantity_${pk}`, {type: 'decimal',}, opts), + supplier_part: getFormFieldValue(`supplier_part_${pk}`, {}, opts), + order: getFormFieldValue(`order_${pk}`, {}, opts), + } + + // $(opts.modal).find(`#order_row_${pk}`).disable(); + // $(this).disable(); + + // Duplicate the form options, to prevent 'field_suffix' override + var row_opts = Object.assign(opts); + row_opts.field_suffix = `_${pk}`; + + inventreePut( + '{% url "api-po-line-list" %}', + data, + { + method: 'POST', + success: function(response) { + // Remove the row + $(opts.modal).find(`#order_row_${pk}`).remove(); + }, + error: function(xhr) { + switch (xhr.status) { + case 400: + handleFormErrors(xhr.responseJSON, fields, row_opts); + break; + default: + console.error(`Error adding line to purchase order`); + showApiError(xhr, options.url); + break; + } + } + } + ); + }); + // Add callback for "remove row" button $(opts.modal).find('.button-row-remove').click(function() { var pk = $(this).attr('pk'); @@ -710,7 +741,7 @@ function orderParts(parts_list, options={}) { createPurchaseOrder({ onSuccess: function(response) { setRelatedFieldData( - `purchase_order_${pk}`, + `order_${pk}`, response, opts );