diff --git a/InvenTree/InvenTree/static/script/inventree/part.js b/InvenTree/InvenTree/static/script/inventree/part.js
index 71f78855f7..18909fff0d 100644
--- a/InvenTree/InvenTree/static/script/inventree/part.js
+++ b/InvenTree/InvenTree/static/script/inventree/part.js
@@ -241,4 +241,17 @@ function loadPartTable(table, url, options={}) {
reload: true,
});
});
+
+ $('#multi-part-export').click(function() {
+ var selections = $(table).bootstrapTable("getSelections");
+
+ var parts = '';
+
+ selections.forEach(function(item) {
+ parts += item.pk;
+ parts += ',';
+ });
+
+ location.href = '/part/export/?parts=' + parts;
+ });
}
\ No newline at end of file
diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html
index 4bbb606bec..343d7a8c40 100644
--- a/InvenTree/part/templates/part/category.html
+++ b/InvenTree/part/templates/part/category.html
@@ -49,7 +49,7 @@
diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py
index e39375f032..3ec99e7255 100644
--- a/InvenTree/part/urls.py
+++ b/InvenTree/part/urls.py
@@ -78,6 +78,9 @@ part_urls = [
# Download a BOM upload template
url(r'^bom_template/?', views.BomUploadTemplate.as_view(), name='bom-upload-template'),
+ # Export data for multiple parts
+ url(r'^export/', views.PartExport.as_view(), name='part-export'),
+
# Individual part
url(r'^(?P\d+)/', include(part_detail_urls)),
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index fae82bfc72..74adc490b8 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -14,6 +14,8 @@ from django.views.generic import DetailView, ListView, FormView
from django.forms.models import model_to_dict
from django.forms import HiddenInput, CheckboxInput
+import tablib
+
from fuzzywuzzy import fuzz
from .models import PartCategory, Part, PartAttachment
@@ -1159,6 +1161,107 @@ class BomUpload(FormView):
return self.render_to_response(self.get_context_data(form=self.form))
+class PartExport(AjaxView):
+ """ Export a CSV file containing information on multiple parts """
+
+ def get(self, request, *args, **kwargs):
+ part = request.GET.get('parts', '')
+ parts = []
+
+ for pk in part.split(','):
+ try:
+ parts.append(Part.objects.get(pk=int(pk)))
+ except (Part.DoesNotExist, ValueError):
+ continue
+
+ headers = [
+ 'ID',
+ 'Name',
+ 'Description',
+ 'Category',
+ 'Category ID',
+ 'IPN',
+ 'Revision',
+ 'URL',
+ 'Keywords',
+ 'Notes',
+ 'Assembly',
+ 'Component',
+ 'Template',
+ 'Trackable',
+ 'Salable',
+ 'Active',
+ 'Virtual',
+ 'Stock Info', # Spacer between part data and stock data
+ 'In Stock',
+ 'Allocated',
+ 'Building',
+ 'On Order',
+ ]
+
+ # Construct list of suppliers for each part
+ supplier_names = set()
+
+ for part in parts:
+ supplier_parts = part.supplier_parts.all()
+ part.suppliers = {}
+
+ for sp in supplier_parts:
+ name = sp.supplier.name
+ supplier_names.add(name)
+ part.suppliers[name] = sp
+
+ if len(supplier_names) > 0:
+ headers.append('Suppliers')
+ for name in supplier_names:
+ headers.append(name)
+
+ data = tablib.Dataset(headers=headers)
+
+ for part in parts:
+ line = []
+
+ line.append(part.pk)
+ line.append(part.name)
+ line.append(part.description)
+ line.append(str(part.category))
+ line.append(part.category.pk)
+ line.append(part.IPN)
+ line.append(part.revision)
+ line.append(part.URL)
+ line.append(part.keywords)
+ line.append(part.notes)
+ line.append(part.assembly)
+ line.append(part.component)
+ line.append(part.is_template)
+ line.append(part.trackable)
+ line.append(part.salable)
+ line.append(part.active)
+ line.append(part.virtual)
+
+ # Stock information
+ line.append('')
+ line.append(part.total_stock)
+ line.append(part.allocation_count)
+ line.append(part.quantity_being_built)
+ line.append(part.on_order)
+
+ if len(supplier_names) > 0:
+ line.append('')
+
+ for name in supplier_names:
+ sp = part.suppliers.get(name, None)
+ if sp:
+ line.append(sp.SKU)
+ else:
+ line.append('')
+
+ data.append(line)
+
+ csv = data.export('csv')
+ return DownloadFile(csv, 'InvenTree_Parts.csv')
+
+
class BomUploadTemplate(AjaxView):
"""
Provide a BOM upload template file for download.