mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-19 05:25:42 +00:00
Merge pull request #2709 from SchrodingersGat/stock-exporter
Stock export refactor
This commit is contained in:
@ -26,6 +26,8 @@ from djmoney.contrib.exchange.exceptions import MissingRate
|
||||
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
from part.admin import PartResource
|
||||
|
||||
from .models import Part, PartCategory, PartRelated
|
||||
from .models import BomItem, BomItemSubstitute
|
||||
from .models import PartParameter, PartParameterTemplate
|
||||
@ -43,6 +45,7 @@ from build.models import Build
|
||||
from . import serializers as part_serializers
|
||||
|
||||
from InvenTree.helpers import str2bool, isNull, increment
|
||||
from InvenTree.helpers import DownloadFile
|
||||
from InvenTree.api import AttachmentMixin
|
||||
|
||||
from InvenTree.status_codes import BuildStatus
|
||||
@ -726,6 +729,22 @@ class PartList(generics.ListCreateAPIView):
|
||||
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
|
||||
# Check if we wish to export the queried data to a file.
|
||||
# If so, skip pagination!
|
||||
export_format = request.query_params.get('export', None)
|
||||
|
||||
if export_format:
|
||||
export_format = str(export_format).strip().lower()
|
||||
|
||||
if export_format in ['csv', 'tsv', 'xls', 'xlsx']:
|
||||
dataset = PartResource().export(queryset=queryset)
|
||||
|
||||
filedata = dataset.export(export_format)
|
||||
|
||||
filename = f"InvenTree_Parts.{export_format}"
|
||||
|
||||
return DownloadFile(filedata, filename)
|
||||
|
||||
page = self.paginate_queryset(queryset)
|
||||
|
||||
if page is not None:
|
||||
|
@ -153,9 +153,6 @@
|
||||
<h4>{% trans "Parts" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
<button type='button' class='btn btn-outline-secondary' id='part-export' title='{% trans "Export Part Data" %}'>
|
||||
<span class='fas fa-file-download'></span> {% trans "Export" %}
|
||||
</button>
|
||||
{% if roles.part.add %}
|
||||
<button type='button' class='btn btn-success' id='part-create' title='{% trans "Create new part" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Part" %}
|
||||
@ -290,13 +287,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
$("#part-export").click(function() {
|
||||
|
||||
var url = "{% url 'part-export' %}?category={{ category.id }}";
|
||||
|
||||
location.href = url;
|
||||
});
|
||||
|
||||
{% if roles.part.add %}
|
||||
$("#part-create").click(function() {
|
||||
|
||||
|
@ -28,11 +28,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
{% if part.is_template %}
|
||||
<div class='alert alert-info alert-block'>
|
||||
{% blocktrans with full_name=part.full_name%}Showing stock for all variants of <em>{{full_name}}</em>{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "stock_table.html" %}
|
||||
</div>
|
||||
</div>
|
||||
@ -829,14 +824,7 @@
|
||||
],
|
||||
url: "{% url 'api-stock-list' %}",
|
||||
});
|
||||
|
||||
$("#stock-export").click(function() {
|
||||
|
||||
exportStock({
|
||||
part: {{ part.pk }}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('#item-create').click(function () {
|
||||
createNewStockItem({
|
||||
data: {
|
||||
|
@ -58,14 +58,6 @@ class PartListTest(PartViewTestCase):
|
||||
self.assertIn('parts', keys)
|
||||
self.assertIn('user', keys)
|
||||
|
||||
def test_export(self):
|
||||
""" Export part data to CSV """
|
||||
|
||||
response = self.client.get(reverse('part-export'), {'parts': '1,2,3,4,5,6,7,8,9,10'}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn('streaming_content', dir(response))
|
||||
|
||||
|
||||
class PartDetailTest(PartViewTestCase):
|
||||
|
||||
|
@ -80,9 +80,6 @@ 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 using pk
|
||||
url(r'^(?P<pk>\d+)/', include(part_detail_urls)),
|
||||
|
||||
|
@ -49,13 +49,11 @@ from . import settings as part_settings
|
||||
from .bom import MakeBomTemplate, ExportBom, IsValidBOMFormat
|
||||
from order.models import PurchaseOrderLineItem
|
||||
|
||||
from .admin import PartResource
|
||||
|
||||
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||
from InvenTree.views import QRCodeView
|
||||
from InvenTree.views import InvenTreeRoleMixin
|
||||
|
||||
from InvenTree.helpers import DownloadFile, str2bool
|
||||
from InvenTree.helpers import str2bool
|
||||
|
||||
|
||||
class PartIndex(InvenTreeRoleMixin, ListView):
|
||||
@ -709,69 +707,6 @@ class BomUpload(InvenTreeRoleMixin, DetailView):
|
||||
template_name = 'part/upload_bom.html'
|
||||
|
||||
|
||||
class PartExport(AjaxView):
|
||||
""" Export a CSV file containing information on multiple parts """
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
def get_parts(self, request):
|
||||
""" Extract part list from the POST parameters.
|
||||
Parts can be supplied as:
|
||||
|
||||
- Part category
|
||||
- List of part PK values
|
||||
"""
|
||||
|
||||
# Filter by part category
|
||||
cat_id = request.GET.get('category', None)
|
||||
|
||||
part_list = None
|
||||
|
||||
if cat_id is not None:
|
||||
try:
|
||||
category = PartCategory.objects.get(pk=cat_id)
|
||||
part_list = category.get_parts()
|
||||
except (ValueError, PartCategory.DoesNotExist):
|
||||
pass
|
||||
|
||||
# Backup - All parts
|
||||
if part_list is None:
|
||||
part_list = Part.objects.all()
|
||||
|
||||
# Also optionally filter by explicit list of part IDs
|
||||
part_ids = request.GET.get('parts', '')
|
||||
parts = []
|
||||
|
||||
for pk in part_ids.split(','):
|
||||
try:
|
||||
parts.append(int(pk))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if len(parts) > 0:
|
||||
part_list = part_list.filter(pk__in=parts)
|
||||
|
||||
# Prefetch related fields to reduce DB hits
|
||||
part_list = part_list.prefetch_related(
|
||||
'category',
|
||||
'used_in',
|
||||
'builds',
|
||||
'supplier_parts__purchase_order_line_items',
|
||||
'stock_items__allocations',
|
||||
)
|
||||
|
||||
return part_list
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
parts = self.get_parts(request)
|
||||
|
||||
dataset = PartResource().export(queryset=parts)
|
||||
|
||||
csv = dataset.export('csv')
|
||||
return DownloadFile(csv, 'InvenTree_Parts.csv')
|
||||
|
||||
|
||||
class BomUploadTemplate(AjaxView):
|
||||
"""
|
||||
Provide a BOM upload template file for download.
|
||||
|
Reference in New Issue
Block a user