diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 04d1978745..18267f33c4 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -177,7 +177,10 @@ class AjaxCreateView(AjaxMixin, CreateView): # Return the PK of the newly-created object data['pk'] = obj.pk - data['url'] = obj.get_absolute_url() + try: + data['url'] = obj.get_absolute_url() + except AttributeError: + pass return self.renderJsonResponse(request, form, data) @@ -223,7 +226,11 @@ class AjaxUpdateView(AjaxMixin, UpdateView): # Include context data about the updated object data['pk'] = obj.id - data['url'] = obj.get_absolute_url() + + try: + data['url'] = obj.get_absolute_url() + except AttributeError: + pass return self.renderJsonResponse(request, form, data) diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index bba8c3e973..0363b4ab37 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -9,7 +9,8 @@ from InvenTree.forms import HelperForm from django import forms -from .models import Part, PartCategory, BomItem +from .models import Part, PartCategory, PartAttachment +from .models import BomItem from .models import SupplierPart @@ -44,6 +45,18 @@ class BomExportForm(HelperForm): ] +class EditPartAttachmentForm(HelperForm): + """ Form for editing a PartAttachment object """ + + class Meta: + model = PartAttachment + fields = [ + 'part', + 'attachment', + 'comment' + ] + + class EditPartForm(HelperForm): """ Form for editing a Part object """ diff --git a/InvenTree/part/migrations/0012_part_active.py b/InvenTree/part/migrations/0012_part_active.py index c2f3b55f6c..87929e2f85 100644 --- a/InvenTree/part/migrations/0012_part_active.py +++ b/InvenTree/part/migrations/0012_part_active.py @@ -14,5 +14,10 @@ class Migration(migrations.Migration): model_name='part', name='active', field=models.BooleanField(default=True, help_text='Is this part active?'), + ), + migrations.AddField( + model_name='partattachment', + name='comment', + field=models.CharField(blank=True, help_text='File comment', max_length=100), ), ] diff --git a/InvenTree/part/migrations/0012_partattachment_comment.py b/InvenTree/part/migrations/0012_partattachment_comment.py deleted file mode 100644 index a1a0057d1b..0000000000 --- a/InvenTree/part/migrations/0012_partattachment_comment.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-28 12:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0011_auto_20190428_0841'), - ] - - operations = [ - migrations.AddField( - model_name='partattachment', - name='comment', - field=models.CharField(blank=True, help_text='File comment', max_length=100), - ), - ] diff --git a/InvenTree/part/migrations/0014_partattachment_comment.py b/InvenTree/part/migrations/0014_partattachment_comment.py deleted file mode 100644 index a51f588a59..0000000000 --- a/InvenTree/part/migrations/0014_partattachment_comment.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2 on 2019-04-30 23:34 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('part', '0013_auto_20190429_2229'), - ] - - operations = [ - migrations.AddField( - model_name='partattachment', - name='comment', - field=models.CharField(blank=True, help_text='Attachment description', max_length=100), - ), - ] diff --git a/InvenTree/part/templates/part/attachment_delete.html b/InvenTree/part/templates/part/attachment_delete.html new file mode 100644 index 0000000000..db98b7f6d6 --- /dev/null +++ b/InvenTree/part/templates/part/attachment_delete.html @@ -0,0 +1,3 @@ +Are you sure you wish to delete this attachment? +
+This will remove the file '{{ attachment.basename }}'. \ No newline at end of file diff --git a/InvenTree/part/templates/part/attachments.html b/InvenTree/part/templates/part/attachments.html index 2188a89528..24c69f23f7 100644 --- a/InvenTree/part/templates/part/attachments.html +++ b/InvenTree/part/templates/part/attachments.html @@ -7,7 +7,8 @@

Attachments

-
+
+
@@ -21,8 +22,10 @@ {% endfor %} @@ -33,4 +36,27 @@ {% block js_ready %} {{ block.super }} + $("#new-attachment").click(function() { + launchModalForm("{% url 'part-attachment-create' %}?part={{ part.id }}"); + }); + + $("#attachment-table").on('click', '.attachment-edit-button', function() { + var button = $(this); + + launchModalForm(button.attr('url'), + { + success: function() { + } + }); + }); + + $("#attachment-table").on('click', '.attachment-delete-button', function() { + var button = $(this); + + launchDeleteForm(button.attr('url'), { + success: function() { + } + }); + }); + {% endblock %} \ No newline at end of file diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index 2218b7b8a1..401a038193 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -1,5 +1,11 @@ """ -URL lookup for Part app +URL lookup for Part app. Provides URL endpoints for: + +- Display / Create / Edit / Delete PartCategory +- Display / Create / Edit / Delete Part +- Create / Edit / Delete PartAttachment +- Display / Create / Edit / Delete SupplierPart + """ from django.conf.urls import url, include @@ -19,6 +25,12 @@ supplier_part_urls = [ url(r'^(?P\d+)/', include(supplier_part_detail_urls)), ] +part_attachment_urls = [ + url('^new/?', views.PartAttachmentCreate.as_view(), name='part-attachment-create'), + url(r'^(?P\d+)/edit/?', views.PartAttachmentEdit.as_view(), name='part-attachment-edit'), + url(r'^(?P\d+)/delete/?', views.PartAttachmentDelete.as_view(), name='part-attachment-delete'), +] + part_detail_urls = [ url(r'^edit/?', views.PartEdit.as_view(), name='part-edit'), url(r'^delete/?', views.PartDelete.as_view(), name='part-delete'), @@ -70,6 +82,10 @@ part_urls = [ # Part category url(r'^category/(?P\d+)/', include(part_category_urls)), + # Part attachments + url(r'^attachment/', include(part_attachment_urls)), + + # Bom Items url(r'^bom/(?P\d+)/', include(part_bom_urls)), # Top level part list (display top level parts and categories) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 7ea8e5efbf..bdc3147e9b 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -13,11 +13,13 @@ from django.forms.models import model_to_dict from django.forms import HiddenInput from company.models import Company -from .models import PartCategory, Part, BomItem +from .models import PartCategory, Part, PartAttachment +from .models import BomItem from .models import SupplierPart from .forms import PartImageForm from .forms import EditPartForm +from .forms import EditPartAttachmentForm from .forms import EditCategoryForm from .forms import EditBomItemForm from .forms import BomExportForm @@ -51,6 +53,81 @@ class PartIndex(ListView): return context +class PartAttachmentCreate(AjaxCreateView): + """ View for creating a new PartAttachment object + + - The view only makes sense if a Part object is passed to it + """ + model = PartAttachment + form_class = EditPartAttachmentForm + ajax_form_title = "Add part attachment" + ajax_template_name = "modal_form.html" + + def get_data(self): + return { + 'success': 'Added attachment' + } + + def get_initial(self): + """ Get initial data for new PartAttachment object. + + - Client should have requested this form with a parent part in mind + - e.g. ?part= + """ + + initials = super(AjaxCreateView, self).get_initial() + + # TODO - If the proper part was not sent, return an error message + initials['part'] = Part.objects.get(id=self.request.GET.get('part')) + + return initials + + def get_form(self): + """ Create a form to upload a new PartAttachment + + - Hide the 'part' field + """ + + form = super(AjaxCreateView, self).get_form() + + form.fields['part'].widget = HiddenInput() + + return form + + +class PartAttachmentEdit(AjaxUpdateView): + """ View for editing a PartAttachment object """ + model = PartAttachment + form_class = EditPartAttachmentForm + ajax_template_name = 'modal_form.html' + ajax_form_title = 'Edit attachment' + + def get_data(self): + return { + 'success': 'Part attachment updated' + } + + def get_form(self): + form = super(AjaxUpdateView, self).get_form() + + form.fields['part'].widget = HiddenInput() + + return form + + +class PartAttachmentDelete(AjaxDeleteView): + """ View for deleting a PartAttachment """ + + model = PartAttachment + ajax_template_name = "part/attachment_delete.html" + context_object_name = "attachment" + + def get_data(self): + return { + 'danger': 'Deleted part attachment' + } + + class PartCreate(AjaxCreateView): """ View for creating a new Part object. @@ -102,7 +179,6 @@ class PartCreate(AjaxCreateView): return form - # Pre-fill the category field if a valid category is provided def get_initial(self): """ Get initial data for the new Part object:
{{ attachment.basename }} {{ attachment.comment }} - - +
+ + +