diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py
index afcb7ea22e..7b003d9406 100644
--- a/InvenTree/InvenTree/api_version.py
+++ b/InvenTree/InvenTree/api_version.py
@@ -2,11 +2,14 @@
# InvenTree API version
-INVENTREE_API_VERSION = 99
+INVENTREE_API_VERSION = 100
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
+v100 -> 2023-03-04 : https://github.com/inventree/InvenTree/pull/4452
+ - Adds bulk delete of PurchaseOrderLineItems to API
+
v99 -> 2023-03-03 : https://github.com/inventree/InvenTree/pull/4445
- Adds sort by "responsible" to PurchaseOrderAPI
diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py
index 83ca354df1..b4a77fa34d 100644
--- a/InvenTree/order/api.py
+++ b/InvenTree/order/api.py
@@ -495,7 +495,7 @@ class PurchaseOrderLineItemFilter(rest_filters.FilterSet):
return queryset
-class PurchaseOrderLineItemList(APIDownloadMixin, ListCreateAPI):
+class PurchaseOrderLineItemList(APIDownloadMixin, ListCreateDestroyAPIView):
"""API endpoint for accessing a list of PurchaseOrderLineItem objects.
- GET: Return a list of PurchaseOrder Line Item objects
diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html
index 914c97d6ab..a7cceb0d8a 100644
--- a/InvenTree/order/templates/order/purchase_order_detail.html
+++ b/InvenTree/order/templates/order/purchase_order_detail.html
@@ -38,6 +38,22 @@
diff --git a/InvenTree/order/test_api.py b/InvenTree/order/test_api.py
index f37937d7ce..976f198cad 100644
--- a/InvenTree/order/test_api.py
+++ b/InvenTree/order/test_api.py
@@ -603,6 +603,26 @@ class PurchaseOrderLineItemTest(OrderTest):
self.filter({'has_pricing': 1}, 0)
self.filter({'has_pricing': 0}, 5)
+ def test_po_line_bulk_delete(self):
+ """Test that we can bulk delete multiple PurchaseOrderLineItems via the API."""
+ n = models.PurchaseOrderLineItem.objects.count()
+
+ self.assignRole('purchase_order.delete')
+
+ url = reverse('api-po-line-list')
+
+ # Try to delete a set of line items via their IDs
+ self.delete(
+ url,
+ {
+ 'items': [1, 2],
+ },
+ expected_code=204,
+ )
+
+ # We should have 2 less PurchaseOrderLineItems after deletign them
+ self.assertEqual(models.PurchaseOrderLineItem.objects.count(), n - 2)
+
class PurchaseOrderDownloadTest(OrderTest):
"""Unit tests for downloading PurchaseOrder data via the API endpoint."""
diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js
index 55b99f7d40..1ec8b48702 100644
--- a/InvenTree/templates/js/translated/order.js
+++ b/InvenTree/templates/js/translated/order.js
@@ -914,6 +914,7 @@ function poLineItemFields(options={}) {
// Returned prices are in increasing order of quantity
if (response.length > 0) {
var idx = 0;
+ var index = 0;
for (var idx = 0; idx < response.length; idx++) {
if (response[idx].quantity > quantity) {
@@ -2213,6 +2214,71 @@ function loadPurchaseOrderTable(table, options) {
}
+/*
+ * Delete the selected Purchase Order Line Items from the database
+ */
+function deletePurchaseOrderLineItems(items, options={}) {
+
+ function renderItem(item, opts={}) {
+
+ var part = item.part_detail;
+ var thumb = thumbnailImage(item.part_detail.thumbnail || item.part_detail.image);
+ var MPN = item.supplier_part_detail.manufacturer_part_detail ? item.supplier_part_detail.manufacturer_part_detail.MPN : '-';
+
+ var html = `
+
+ ${thumb} ${part.full_name} |
+ ${part.description} |
+ ${item.supplier_part_detail.SKU} |
+ ${MPN} |
+ ${item.quantity}
+ |
+ `;
+
+ return html;
+ }
+
+ var rows = '';
+ var ids = [];
+
+ items.forEach(function(item) {
+ rows += renderItem(item);
+ ids.push(item.pk);
+ });
+
+ var html = `
+
+ {% trans "All selected Line items will be deleted" %}
+
+
+
+
+ {% trans "Part" %} |
+ {% trans "Description" %} |
+ {% trans "SKU" %} |
+ {% trans "MPN" %} |
+ {% trans "Quantity" %} |
+
+ ${rows}
+
+ `;
+
+ constructForm('{% url "api-po-line-list" %}', {
+ method: 'DELETE',
+ multi_delete: true,
+ title: '{% trans "Delete selected Line items?" %}',
+ form_data: {
+ items: ids,
+ },
+ preFormContent: html,
+ onSuccess: function() {
+ // Refresh the table once the line items are deleted
+ $('#po-line-table').bootstrapTable('refresh');
+ },
+ });
+}
+
+
/**
* Load a table displaying line items for a particular PurchasesOrder
* @param {String} table - HTML ID tag e.g. '#table'
@@ -2305,6 +2371,13 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
}
});
});
+
+ // Callback for bulk deleting mutliple lines
+ $('#po-lines-bulk-delete').off('click').on('click', function() {
+ var rows = getTableData(' #po-line-table');
+
+ deletePurchaseOrderLineItems(rows);
+ });
}
if (options.allow_receive) {
@@ -2569,6 +2642,13 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
]
});
+ linkButtonsToSelection(
+ table,
+ [
+ '#multi-select-options',
+ ]
+ );
+
}