mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 15:15:42 +00:00 
			
		
		
		
	"Validate BOM" now uses the API also
This commit is contained in:
		@@ -474,6 +474,56 @@ class PartCopyBOM(generics.CreateAPIView):
 | 
				
			|||||||
        return ctx
 | 
					        return ctx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PartValidateBOM(generics.RetrieveUpdateAPIView):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    API endpoint for 'validating' the BOM for a given Part
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class BOMValidateSerializer(serializers.ModelSerializer):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        class Meta:
 | 
				
			||||||
 | 
					            model = Part
 | 
				
			||||||
 | 
					            fields = [
 | 
				
			||||||
 | 
					                'checksum',
 | 
				
			||||||
 | 
					                'valid',
 | 
				
			||||||
 | 
					            ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        checksum = serializers.CharField(
 | 
				
			||||||
 | 
					            read_only=True,
 | 
				
			||||||
 | 
					            source='bom_checksum',
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        valid = serializers.BooleanField(
 | 
				
			||||||
 | 
					            write_only=True,
 | 
				
			||||||
 | 
					            default=False,
 | 
				
			||||||
 | 
					            label=_('Valid'),
 | 
				
			||||||
 | 
					            help_text=_('Validate entire Bill of Materials'),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        def validate_valid(self, valid):
 | 
				
			||||||
 | 
					            if not valid:
 | 
				
			||||||
 | 
					                raise ValidationError(_('This option must be selected'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    queryset = Part.objects.all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    serializer_class = BOMValidateSerializer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, request, *args, **kwargs):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        part = self.get_object()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        partial = kwargs.pop('partial', False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        serializer = self.get_serializer(part, data=request.data, partial=partial)
 | 
				
			||||||
 | 
					        serializer.is_valid(raise_exception=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        part.validate_bom(request.user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return Response({
 | 
				
			||||||
 | 
					            'checksum': part.bom_checksum,
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
 | 
					class PartDetail(generics.RetrieveUpdateDestroyAPIView):
 | 
				
			||||||
    """ API endpoint for detail view of a single Part object """
 | 
					    """ API endpoint for detail view of a single Part object """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1605,8 +1655,11 @@ part_api_urls = [
 | 
				
			|||||||
        # Endpoint for extra serial number information
 | 
					        # Endpoint for extra serial number information
 | 
				
			||||||
        url(r'^serial-numbers/', PartSerialNumberDetail.as_view(), name='api-part-serial-number-detail'),
 | 
					        url(r'^serial-numbers/', PartSerialNumberDetail.as_view(), name='api-part-serial-number-detail'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Endpoint for duplicating a BOM
 | 
					        # Endpoint for duplicating a BOM for the specific Part
 | 
				
			||||||
        url(r'^copy-bom/', PartCopyBOM.as_view(), name='api-part-copy-bom'),
 | 
					        url(r'^bom-copy/', PartCopyBOM.as_view(), name='api-part-bom-copy'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Endpoint for validating a BOM for the specific Part
 | 
				
			||||||
 | 
					        url(r'^bom-validate/', PartValidateBOM.as_view(), name='api-part-bom-validate'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Part detail endpoint
 | 
					        # Part detail endpoint
 | 
				
			||||||
        url(r'^.*$', PartDetail.as_view(), name='api-part-detail'),
 | 
					        url(r'^.*$', PartDetail.as_view(), name='api-part-detail'),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,21 +55,6 @@ class PartImageDownloadForm(HelperForm):
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BomValidateForm(HelperForm):
 | 
					 | 
				
			||||||
    """ Simple confirmation form for BOM validation.
 | 
					 | 
				
			||||||
    User is presented with a single checkbox input,
 | 
					 | 
				
			||||||
    to confirm that the BOM for this part is valid
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    validate = forms.BooleanField(required=False, initial=False, label=_('validate'), help_text=_('Confirm that the BOM is correct'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Meta:
 | 
					 | 
				
			||||||
        model = Part
 | 
					 | 
				
			||||||
        fields = [
 | 
					 | 
				
			||||||
            'validate'
 | 
					 | 
				
			||||||
        ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class BomMatchItemForm(MatchItemForm):
 | 
					class BomMatchItemForm(MatchItemForm):
 | 
				
			||||||
    """ Override MatchItemForm fields """
 | 
					    """ Override MatchItemForm fields """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,12 +0,0 @@
 | 
				
			|||||||
{% extends "modal_form.html" %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% load i18n %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% block pre_form_content %}
 | 
					 | 
				
			||||||
{% blocktrans with part.full_name as part %}Confirm that the Bill of Materials (BOM) is valid for:<br><em>{{ part }}</em>{% endblocktrans %}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div class='alert alert-warning alert-block'>
 | 
					 | 
				
			||||||
    {% trans 'This will validate each line in the BOM.' %}
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
{% endblock %}
 | 
					 | 
				
			||||||
@@ -609,12 +609,10 @@
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $("#validate-bom").click(function() {
 | 
					        $("#validate-bom").click(function() {
 | 
				
			||||||
            launchModalForm(
 | 
					
 | 
				
			||||||
                "{% url 'bom-validate' part.id %}",
 | 
					            validateBom({{ part.id }}, {
 | 
				
			||||||
                {
 | 
					                reload: true
 | 
				
			||||||
                    reload: true,
 | 
					            });
 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $("#download-bom").click(function () {
 | 
					        $("#download-bom").click(function () {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,7 +35,6 @@ part_detail_urls = [
 | 
				
			|||||||
    url(r'^delete/?', views.PartDelete.as_view(), name='part-delete'),
 | 
					    url(r'^delete/?', views.PartDelete.as_view(), name='part-delete'),
 | 
				
			||||||
    url(r'^bom-export/?', views.BomExport.as_view(), name='bom-export'),
 | 
					    url(r'^bom-export/?', views.BomExport.as_view(), name='bom-export'),
 | 
				
			||||||
    url(r'^bom-download/?', views.BomDownload.as_view(), name='bom-download'),
 | 
					    url(r'^bom-download/?', views.BomDownload.as_view(), name='bom-download'),
 | 
				
			||||||
    url(r'^validate-bom/', views.BomValidate.as_view(), name='bom-validate'),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'),
 | 
					    url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -694,48 +694,6 @@ class PartImageSelect(AjaxUpdateView):
 | 
				
			|||||||
        return self.renderJsonResponse(request, form, data)
 | 
					        return self.renderJsonResponse(request, form, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BomValidate(AjaxUpdateView):
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
    Modal form view for validating a part BOM
 | 
					 | 
				
			||||||
    """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    model = Part
 | 
					 | 
				
			||||||
    ajax_form_title = _("Validate BOM")
 | 
					 | 
				
			||||||
    ajax_template_name = 'part/bom_validate.html'
 | 
					 | 
				
			||||||
    context_object_name = 'part'
 | 
					 | 
				
			||||||
    form_class = part_forms.BomValidateForm
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_context(self):
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            'part': self.get_object(),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get(self, request, *args, **kwargs):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        form = self.get_form()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return self.renderJsonResponse(request, form, context=self.get_context())
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def validate(self, part, form, **kwargs):
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        confirm = str2bool(form.cleaned_data.get('validate', False))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if not confirm:
 | 
					 | 
				
			||||||
            form.add_error('validate', _('Confirm that the BOM is valid'))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def save(self, part, form, **kwargs):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Mark the BOM as validated
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        part.validate_bom(self.request.user)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_data(self):
 | 
					 | 
				
			||||||
        return {
 | 
					 | 
				
			||||||
            'success': _('Validated Bill of Materials')
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class BomUpload(InvenTreeRoleMixin, FileManagementFormView):
 | 
					class BomUpload(InvenTreeRoleMixin, FileManagementFormView):
 | 
				
			||||||
    """ View for uploading a BOM file, and handling BOM data importing.
 | 
					    """ View for uploading a BOM file, and handling BOM data importing.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -207,6 +207,11 @@ function showApiError(xhr, url) {
 | 
				
			|||||||
        title = '{% trans "Error 404: Resource Not Found" %}';
 | 
					        title = '{% trans "Error 404: Resource Not Found" %}';
 | 
				
			||||||
        message = '{% trans "The requested resource could not be located on the server" %}';
 | 
					        message = '{% trans "The requested resource could not be located on the server" %}';
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					    // Method not allowed
 | 
				
			||||||
 | 
					    case 405:
 | 
				
			||||||
 | 
					        title = '{% trans "Error 405: Method Not Allowed" %}';
 | 
				
			||||||
 | 
					        message = '{% trans "HTTP method not allowed at URL" %}';
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
    // Timeout
 | 
					    // Timeout
 | 
				
			||||||
    case 408:
 | 
					    case 408:
 | 
				
			||||||
        title = '{% trans "Error 408: Timeout" %}';
 | 
					        title = '{% trans "Error 408: Timeout" %}';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,6 +40,7 @@
 | 
				
			|||||||
    loadStockPricingChart,
 | 
					    loadStockPricingChart,
 | 
				
			||||||
    partStockLabel,
 | 
					    partStockLabel,
 | 
				
			||||||
    toggleStar,
 | 
					    toggleStar,
 | 
				
			||||||
 | 
					    validateBom,
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Part API functions
 | 
					/* Part API functions
 | 
				
			||||||
@@ -429,9 +430,34 @@ function toggleStar(options) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Validate a BOM */
 | 
				
			||||||
 | 
					function validateBom(part_id, options={}) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var html = `
 | 
				
			||||||
 | 
					    <div class='alert alert-block alert-success'>
 | 
				
			||||||
 | 
					    {% trans "Validating the BOM will mark each line item as valid" %}
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					    `;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructForm(`/api/part/${part_id}/bom-validate/`, {
 | 
				
			||||||
 | 
					        method: 'PUT',
 | 
				
			||||||
 | 
					        fields: {
 | 
				
			||||||
 | 
					            valid: {},
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        preFormContent: html,
 | 
				
			||||||
 | 
					        title: '{% trans "Validate Bill of Materials" %}',
 | 
				
			||||||
 | 
					        reload: options.reload,
 | 
				
			||||||
 | 
					        onSuccess: function(response) {
 | 
				
			||||||
 | 
					            showMessage('{% trans "Validated Bill of Materials" %}');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Duplicate a BOM */
 | 
					/* Duplicate a BOM */
 | 
				
			||||||
function duplicateBom(part_id, options={}) {
 | 
					function duplicateBom(part_id, options={}) {
 | 
				
			||||||
    constructForm(`/api/part/${part_id}/copy-bom/`, {
 | 
					
 | 
				
			||||||
 | 
					    constructForm(`/api/part/${part_id}/bom-copy/`, {
 | 
				
			||||||
        method: 'POST',
 | 
					        method: 'POST',
 | 
				
			||||||
        fields: {
 | 
					        fields: {
 | 
				
			||||||
            part: {
 | 
					            part: {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user