diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index 66c61bd0d7..2d079f8d45 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -16,10 +16,11 @@ from rest_framework.response import Response from company.models import SupplierPart from InvenTree.filters import InvenTreeOrderingFilter -from InvenTree.helpers import str2bool +from InvenTree.helpers import str2bool, DownloadFile from InvenTree.api import AttachmentMixin from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus +from order.admin import POLineItemResource import order.models as models import order.serializers as serializers from part.models import Part @@ -370,6 +371,34 @@ class POLineItemList(generics.ListCreateAPIView): return queryset + def list(self, request, *args, **kwargs): + + queryset = self.filter_queryset(self.get_queryset()) + + # Check if we wish to export the queried data to a file + 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 = POLineItemResource().export(queryset=queryset) + + filedata = dataset.export(export_format) + + filename = f"InvenTree_PurchaseOrderData.{export_format}" + + return DownloadFile(filedata, filename) + + page = self.paginate_queryset(queryset) + + if page is not None: + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + filter_backends = [ rest_filters.DjangoFilterBackend, filters.SearchFilter, diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html index b9972d73fc..53f973ee20 100644 --- a/InvenTree/order/templates/order/purchase_order_detail.html +++ b/InvenTree/order/templates/order/purchase_order_detail.html @@ -152,32 +152,16 @@ {% if order.status == PurchaseOrderStatus.PENDING %} $('#new-po-line').click(function() { + var fields = poLineItemFields({ + order: {{ order.pk }}, + supplier: {{ order.supplier.pk }}, + {% if order.supplier.currency %} + currency: '{{ order.supplier.currency }}', + {% endif %} + }); constructForm('{% url "api-po-line-list" %}', { - fields: { - order: { - value: {{ order.pk }}, - hidden: true, - }, - part: { - filters: { - part_detail: true, - supplier_detail: true, - supplier: {{ order.supplier.pk }}, - }, - }, - quantity: {}, - reference: {}, - purchase_price: {}, - purchase_price_currency: { - {% if order.supplier.currency %} - value: '{{ order.supplier.currency }}', - {% endif %} - }, - target_date: {}, - destination: {}, - notes: {}, - }, + fields: fields, method: 'POST', title: '{% trans "Add Line Item" %}', onSuccess: function() { diff --git a/InvenTree/order/templates/order/sales_order_detail.html b/InvenTree/order/templates/order/sales_order_detail.html index 3676268f5c..9797c8dedf 100644 --- a/InvenTree/order/templates/order/sales_order_detail.html +++ b/InvenTree/order/templates/order/sales_order_detail.html @@ -221,29 +221,19 @@ }, }); - function reloadTable() { - $("#so-lines-table").bootstrapTable("refresh"); - } - $("#new-so-line").click(function() { + var fields = soLineItemFields({ + order: {{ order.pk }}, + }); + constructForm('{% url "api-so-line-list" %}', { - fields: { - order: { - value: {{ order.pk }}, - hidden: true, - }, - part: {}, - quantity: {}, - reference: {}, - sale_price: {}, - sale_price_currency: {}, - target_date: {}, - notes: {}, - }, + fields: fields, method: 'POST', title: '{% trans "Add Line Item" %}', - onSuccess: reloadTable, + onSuccess: function() { + $("#so-lines-table").bootstrapTable("refresh"); + }, }); }); diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index 5f825c6738..b4757e2494 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -281,6 +281,65 @@ function createPurchaseOrder(options={}) { } +/* Construct a set of fields for the SalesOrderLineItem form */ +function soLineItemFields(options={}) { + + var fields = { + order: { + hidden: true, + }, + part: {}, + quantity: {}, + reference: {}, + sale_price: {}, + sale_price_currency: {}, + target_date: {}, + notes: {}, + }; + + if (options.order) { + fields.order.value = options.order; + } + + return fields; +} + + +/* Construct a set of fields for the PurchaseOrderLineItem form */ +function poLineItemFields(options={}) { + + var fields = { + order: { + hidden: true, + }, + part: { + filters: { + part_detail: true, + supplier_detail: true, + supplier: options.supplier, + } + }, + quantity: {}, + reference: {}, + purchase_price: {}, + purchase_price_currency: {}, + target_date: {}, + destination: {}, + notes: {}, + }; + + if (options.order) { + fields.order.value = options.order; + } + + if (options.currency) { + fields.purchase_price_currency.value = options.currency; + } + + return fields; +} + + function removeOrderRowFromOrderWizard(e) { /* Remove a part selection from an order form. */ @@ -293,6 +352,7 @@ function removeOrderRowFromOrderWizard(e) { $('#' + row).remove(); } + function newSupplierPartFromOrderWizard(e) { /* Create a new supplier part directly from an order form. * Launches a secondary modal and (if successful), @@ -991,10 +1051,36 @@ function loadPurchaseOrderLineItemTable(table, options={}) { var target = options.filter_target || '#filter-list-purchase-order-lines'; - setupFilterList('purchaseorderlineitem', $(table), target); + setupFilterList('purchaseorderlineitem', $(table), target, {download: true}); function setupCallbacks() { if (options.allow_edit) { + + // Callback for "duplicate" button + $(table).find('.button-line-duplicate').click(function() { + var pk = $(this).attr('pk'); + + inventreeGet(`/api/order/po-line/${pk}/`, {}, { + success: function(data) { + + var fields = poLineItemFields({ + supplier: options.supplier, + }); + + constructForm('{% url "api-po-line-list" %}', { + method: 'POST', + fields: fields, + data: data, + title: '{% trans "Duplicate Line Item" %}', + onSuccess: function(response) { + $(table).bootstrapTable('refresh'); + } + }); + } + }); + }); + + // Callback for "edit" button $(table).find('.button-line-edit').click(function() { var pk = $(this).attr('pk'); @@ -1022,6 +1108,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) { }); }); + // Callback for "delete" button $(table).find('.button-line-delete').click(function() { var pk = $(this).attr('pk'); @@ -1270,6 +1357,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) { } if (options.allow_edit) { + html += makeIconButton('fa-clone', 'button-line-duplicate', pk, '{% trans "Duplicate line item" %}'); html += makeIconButton('fa-edit icon-blue', 'button-line-edit', pk, '{% trans "Edit line item" %}'); html += makeIconButton('fa-trash-alt icon-red', 'button-line-delete', pk, '{% trans "Delete line item" %}'); } @@ -2449,6 +2537,7 @@ function loadSalesOrderLineItemTable(table, options={}) { html += makeIconButton('fa-dollar-sign icon-green', 'button-price', pk, '{% trans "Calculate price" %}'); } + html += makeIconButton('fa-clone', 'button-duplicate', pk, '{% trans "Duplicate line item" %}'); html += makeIconButton('fa-edit icon-blue', 'button-edit', pk, '{% trans "Edit line item" %}'); var delete_disabled = false; @@ -2480,6 +2569,28 @@ function loadSalesOrderLineItemTable(table, options={}) { // Configure callback functions once the table is loaded function setupCallbacks() { + // Callback for duplicating line items + $(table).find('.button-duplicate').click(function() { + var pk = $(this).attr('pk'); + + inventreeGet(`/api/order/so-line/${pk}/`, {}, { + success: function(data) { + + var fields = soLineItemFields(); + + constructForm('{% url "api-so-line-list" %}', { + method: 'POST', + fields: fields, + data: data, + title: '{% trans "Duplicate Line Item" %}', + onSuccess: function(response) { + $(table).bootstrapTable('refresh'); + } + }); + } + }); + }); + // Callback for editing line items $(table).find('.button-edit').click(function() { var pk = $(this).attr('pk');