diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 6ddee71682..83a1eb05af 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -322,9 +322,14 @@ class AjaxCreateView(AjaxMixin, CreateView): """ pass - def post_save(self, new_object, request, **kwargs): + def post_save(self, **kwargs): """ Hook for doing something with the created object after it is saved + + kwargs: + request - The request object + new_object - The newly created object + """ pass @@ -356,7 +361,7 @@ class AjaxCreateView(AjaxMixin, CreateView): self.pre_save(self.form, request) self.object = self.form.save() - self.post_save(self.object, request) + self.post_save(new_object=self.object, request=request) # Return the PK of the newly-created object data['pk'] = self.object.pk @@ -411,7 +416,7 @@ class AjaxUpdateView(AjaxMixin, UpdateView): # Include context data about the updated object data['pk'] = obj.id - self.post_save(obj) + self.post_save(new_object=obj, request=request) try: data['url'] = obj.get_absolute_url() @@ -420,7 +425,7 @@ class AjaxUpdateView(AjaxMixin, UpdateView): return self.renderJsonResponse(request, form, data) - def post_save(self, obj, *args, **kwargs): + def post_save(self, **kwargs): """ Hook called after the form data is saved. (Optional) diff --git a/InvenTree/build/forms.py b/InvenTree/build/forms.py index 6f02f2061f..1413eddc03 100644 --- a/InvenTree/build/forms.py +++ b/InvenTree/build/forms.py @@ -11,7 +11,7 @@ from InvenTree.forms import HelperForm from InvenTree.fields import RoundingDecimalFormField from django import forms -from .models import Build, BuildItem +from .models import Build, BuildItem, BuildOrderAttachment from stock.models import StockLocation @@ -164,3 +164,17 @@ class EditBuildItemForm(HelperForm): 'quantity', 'install_into', ] + + +class EditBuildAttachmentForm(HelperForm): + """ + Form for creating / editing a BuildAttachment object + """ + + class Meta: + model = BuildOrderAttachment + fields = [ + 'build', + 'attachment', + 'comment' + ] diff --git a/InvenTree/build/templates/build/attachments.html b/InvenTree/build/templates/build/attachments.html new file mode 100644 index 0000000000..a2dde0868c --- /dev/null +++ b/InvenTree/build/templates/build/attachments.html @@ -0,0 +1,78 @@ +{% extends "build/build_base.html" %} + +{% load static %} +{% load i18n %} +{% load markdownify %} + +{% block details %} + +{% include "build/tabs.html" with tab='attachments' %} + +

{% trans "Attachments" %}

+
+ +{% include "attachment_table.html" with attachments=build.attachments.all %} + +{% endblock %} + +{% block js_ready %} +{{ block.super }} + +enableDragAndDrop( + '#attachment-dropzone', + '{% url "build-attachment-create" %}', + { + data: { + build: {{ build.id }}, + }, + label: 'attachment', + success: function(data, status, xhr) { + location.reload(); + } + } +); + +// Callback for creating a new attachment +$('#new-attachment').click(function() { + launchModalForm( + '{% url "build-attachment-create" %}', + { + reload: true, + data: { + build: {{ build.pk }}, + } + } + ); +}); + +// Callback for editing an attachment +$("#attachment-table").on('click', '.attachment-edit-button', function() { + var pk = $(this).attr('pk'); + + var url = `/build/attachment/${pk}/edit/`; + + launchModalForm( + url, + { + reload: true, + } + ); +}); + +// Callback for deleting an attachment +$("#attachment-table").on('click', '.attachment-delete-button', function() { + var pk = $(this).attr('pk'); + + var url = `/build/attachment/${pk}/delete/`; + + launchModalForm( + url, + { + reload: true, + } + ); +}); + +$("#attachment-table").inventreeTable({}); + +{% endblock %} diff --git a/InvenTree/build/templates/build/tabs.html b/InvenTree/build/templates/build/tabs.html index ca2e92f290..8f59abc97d 100644 --- a/InvenTree/build/templates/build/tabs.html +++ b/InvenTree/build/templates/build/tabs.html @@ -13,4 +13,7 @@ {% trans "Notes" %}{% if build.notes %} {% endif %} +
  • + {% trans "Attachments" %} +
  • \ No newline at end of file diff --git a/InvenTree/build/urls.py b/InvenTree/build/urls.py index 109dd0b556..ff49fdd486 100644 --- a/InvenTree/build/urls.py +++ b/InvenTree/build/urls.py @@ -18,6 +18,7 @@ build_detail_urls = [ url(r'^notes/', views.BuildNotes.as_view(), name='build-notes'), + url(r'^attachments/', views.BuildDetail.as_view(template_name='build/attachments.html'), name='build-attachments'), url(r'^output/', views.BuildDetail.as_view(template_name='build/build_output.html'), name='build-output'), url(r'^.*$', views.BuildDetail.as_view(), name='build-detail'), @@ -32,6 +33,12 @@ build_urls = [ url('^new/', views.BuildItemCreate.as_view(), name='build-item-create'), ])), + url('^attachment/', include([ + url('^new/', views.BuildAttachmentCreate.as_view(), name='build-attachment-create'), + url(r'^(?P\d+)/edit/', views.BuildAttachmentEdit.as_view(), name='build-attachment-edit'), + url(r'^(?P\d+)/delete/', views.BuildAttachmentDelete.as_view(), name='build-attachment-delete'), + ])), + url(r'new/', views.BuildCreate.as_view(), name='build-create'), url(r'^(?P\d+)/', include(build_detail_urls)), diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index 30ea31af58..a1dd802c4a 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -12,7 +12,7 @@ from django.forms import HiddenInput from django.urls import reverse from part.models import Part -from .models import Build, BuildItem +from .models import Build, BuildItem, BuildOrderAttachment from . import forms from stock.models import StockLocation, StockItem @@ -527,12 +527,14 @@ class BuildCreate(AjaxCreateView): 'success': _('Created new build'), } - def post_save(self, new_object, request, **kwargs): + def post_save(self, **kwargs): """ Called immediately after a new Build object is created. """ - build = new_object + build = kwargs['new_object'] + request = kwargs['request'] + build.createInitialStockItem(request.user) @@ -795,3 +797,86 @@ class BuildItemEdit(AjaxUpdateView): form.fields[field].widget = HiddenInput() return form + + +class BuildAttachmentCreate(AjaxCreateView): + """ + View for creating a BuildAttachment + """ + + model = BuildOrderAttachment + form_class = forms.EditBuildAttachmentForm + ajax_form_title = _('Add Build Order Attachment') + role_required = 'build.add' + + def post_save(self, **kwargs): + self.object.user = self.request.user + self.object.save() + + def get_data(self): + return { + 'success': _('Added attachment') + } + + def get_initial(self): + """ + Get initial data for creating an attachment + """ + + initials = super().get_initial() + + try: + initials['build'] = Build.objects.get(pk=self.request.GET.get('build', -1)) + except (ValueError, Build.DoesNotExist): + pass + + return initials + + def get_form(self): + """ + Hide the 'build' field if specified + """ + + form = super().get_form() + + form.fields['build'].widget = HiddenInput() + + return form + + +class BuildAttachmentEdit(AjaxUpdateView): + """ + View for editing a BuildAttachment object + """ + + model = BuildOrderAttachment + form_class = forms.EditBuildAttachmentForm + ajax_form_title = _('Edit Attachment') + role_required = 'build.change' + + def get_form(self): + form = super().get_form() + form.fields['build'].widget = HiddenInput() + + return form + + def get_data(self): + return { + 'success': _('Attachment updated') + } + + +class BuildAttachmentDelete(AjaxDeleteView): + """ + View for deleting a BuildAttachment + """ + + model = BuildOrderAttachment + ajax_form_title = _('Delete Attachment') + context_object_name = 'attachment' + role_required = 'build.delete' + + def get_data(self): + return { + 'danger': _('Deleted attachment') + }