diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html
index 8e7f77bad1..02a4cd35f1 100644
--- a/InvenTree/order/templates/order/purchase_order_detail.html
+++ b/InvenTree/order/templates/order/purchase_order_detail.html
@@ -207,251 +207,24 @@ $('#new-po-line').click(function() {
{% endif %}
-function reloadTable() {
- $("#po-line-table").bootstrapTable("refresh");
-}
-
-function setupCallbacks() {
- // Setup callbacks for the line buttons
-
- var table = $("#po-line-table");
-
+loadPurchaseOrderLineItemTable('#po-line-table', {
+ order: {{ order.pk }},
+ supplier: {{ order.supplier.pk }},
{% if order.status == PurchaseOrderStatus.PENDING %}
- table.find(".button-line-edit").click(function() {
- var pk = $(this).attr('pk');
-
- constructForm(`/api/order/po-line/${pk}/`, {
- fields: {
- part: {
- filters: {
- part_detail: true,
- supplier_detail: true,
- supplier: {{ order.supplier.pk }},
- }
- },
- quantity: {},
- reference: {},
- purchase_price: {},
- purchase_price_currency: {},
- destination: {},
- notes: {},
- },
- title: '{% trans "Edit Line Item" %}',
- onSuccess: reloadTable,
- });
- });
-
- table.find(".button-line-delete").click(function() {
- var pk = $(this).attr('pk');
-
- constructForm(`/api/order/po-line/${pk}/`, {
- method: 'DELETE',
- title: '{% trans "Delete Line Item" %}',
- onSuccess: reloadTable,
- });
- });
+ allow_edit: true,
+ {% else %}
+ allow_edit: false,
+ {% endif %}
+ {% if order.status == PurchaseOrderStatus.PLACED and roles.purchase_order.change %}
+ allow_receive: true,
+ {% else %}
+ allow_receive: false,
{% endif %}
-
- table.find(".button-line-receive").click(function() {
- var pk = $(this).attr('pk');
-
- launchModalForm("{% url 'po-receive' order.id %}", {
- success: reloadTable,
- data: {
- line: pk,
- },
- secondary: [
- {
- field: 'location',
- label: '{% trans "New Location" %}',
- title: '{% trans "Create new stock location" %}',
- url: "{% url 'stock-location-create' %}",
- },
- ]
- });
- });
-
-}
-
-$("#po-line-table").inventreeTable({
- onPostBody: setupCallbacks,
- name: 'purchaseorderlines',
- sidePagination: 'server',
- formatNoMatches: function() { return "{% trans 'No line items found' %}"; },
- queryParams: {
- order: {{ order.id }},
- part_detail: true,
- },
- url: "{% url 'api-po-line-list' %}",
- showFooter: true,
- columns: [
- {
- field: 'pk',
- title: 'ID',
- visible: false,
- switchable: false,
- },
- {
- field: 'part',
- sortable: true,
- sortName: 'part_name',
- title: '{% trans "Part" %}',
- switchable: false,
- formatter: function(value, row, index, field) {
- if (row.part) {
- return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${row.part_detail.pk}/`);
- } else {
- return '-';
- }
- },
- footerFormatter: function() {
- return '{% trans "Total" %}'
- }
- },
- {
- field: 'part_detail.description',
- title: '{% trans "Description" %}',
- },
- {
- sortable: true,
- sortName: 'SKU',
- field: 'supplier_part_detail.SKU',
- title: '{% trans "SKU" %}',
- formatter: function(value, row, index, field) {
- if (value) {
- return renderLink(value, `/supplier-part/${row.part}/`);
- } else {
- return '-';
- }
- },
- },
- {
- sortable: true,
- sortName: 'MPN',
- field: 'supplier_part_detail.manufacturer_part_detail.MPN',
- title: '{% trans "MPN" %}',
- formatter: function(value, row, index, field) {
- if (row.supplier_part_detail && row.supplier_part_detail.manufacturer_part) {
- return renderLink(value, `/manufacturer-part/${row.supplier_part_detail.manufacturer_part}/`);
- } else {
- return "-";
- }
- },
- },
- {
- sortable: true,
- field: 'reference',
- title: '{% trans "Reference" %}',
- },
- {
- sortable: true,
- field: 'quantity',
- title: '{% trans "Quantity" %}',
- footerFormatter: function(data) {
- return data.map(function (row) {
- return +row['quantity']
- }).reduce(function (sum, i) {
- return sum + i
- }, 0)
- }
- },
- {
- sortable: true,
- field: 'purchase_price',
- title: '{% trans "Unit Price" %}',
- formatter: function(value, row) {
- return row.purchase_price_string || row.purchase_price;
- }
- },
- {
- field: 'total_price',
- sortable: true,
- field: 'total_price',
- title: '{% trans "Total price" %}',
- formatter: function(value, row) {
- var total = row.purchase_price * row.quantity;
- var formatter = new Intl.NumberFormat('en-US', {style: 'currency', currency: row.purchase_price_currency});
- return formatter.format(total)
- },
- footerFormatter: function(data) {
- var total = data.map(function (row) {
- return +row['purchase_price']*row['quantity']
- }).reduce(function (sum, i) {
- return sum + i
- }, 0)
- var currency = (data.slice(-1)[0] && data.slice(-1)[0].purchase_price_currency) || 'USD';
- var formatter = new Intl.NumberFormat('en-US', {style: 'currency', currency: currency});
- return formatter.format(total)
- }
- },
- {
- sortable: false,
- field: 'received',
- switchable: false,
- title: '{% trans "Received" %}',
- formatter: function(value, row, index, field) {
- return makeProgressBar(row.received, row.quantity, {
- id: `order-line-progress-${row.pk}`,
- });
- },
- sorter: function(valA, valB, rowA, rowB) {
-
- if (rowA.received == 0 && rowB.received == 0) {
- return (rowA.quantity > rowB.quantity) ? 1 : -1;
- }
-
- var progressA = parseFloat(rowA.received) / rowA.quantity;
- var progressB = parseFloat(rowB.received) / rowB.quantity;
-
- return (progressA < progressB) ? 1 : -1;
- }
- },
- {
- field: 'destination',
- title: '{% trans "Destination" %}',
- formatter: function(value, row) {
- if (value) {
- return renderLink(row.destination_detail.pathstring, `/stock/location/${value}/`);
- } else {
- return '-';
- }
- }
- },
- {
- field: 'notes',
- title: '{% trans "Notes" %}',
- },
- {
- switchable: false,
- field: 'buttons',
- title: '',
- formatter: function(value, row, index, field) {
- var html = `
`;
-
- var pk = row.pk;
-
- {% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.delete %}
- 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" %}');
- {% endif %}
-
- {% if order.status == PurchaseOrderStatus.PLACED and roles.purchase_order.change %}
- if (row.received < row.quantity) {
- html += makeIconButton('fa-clipboard-check', 'button-line-receive', pk, '{% trans "Receive line item" %}');
- }
- {% endif %}
-
- html += `
`;
-
- return html;
- },
- }
- ]
});
- attachNavCallbacks({
- name: 'purchase-order',
- default: 'order-items'
- });
+attachNavCallbacks({
+ name: 'purchase-order',
+ default: 'order-items'
+});
{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js
index 7ebe000f64..4ab6d5b815 100644
--- a/InvenTree/templates/js/translated/order.js
+++ b/InvenTree/templates/js/translated/order.js
@@ -144,7 +144,6 @@ function newSupplierPartFromOrderWizard(e) {
if (!part) {
part = $(src).closest('button').attr('part');
- console.log('parent: ' + part);
}
createSupplierPart({
@@ -367,6 +366,262 @@ function loadPurchaseOrderTable(table, options) {
});
}
+
+/**
+ * Load a table displaying line items for a particular PurchasesOrder
+ * @param {String} table - HTML ID tag e.g. '#table'
+ * @param {Object} options - options which must provide:
+ * - order (integer PK)
+ * - supplier (integer PK)
+ * - allow_edit (boolean)
+ * - allow_receive (boolean)
+ */
+function loadPurchaseOrderLineItemTable(table, options={}) {
+
+ function setupCallbacks() {
+ if (options.allow_edit) {
+ $(table).find('.button-line-edit').click(function() {
+ var pk = $(this).attr('pk');
+
+ constructForm(`/api/order/po-line/${pk}/`, {
+ fields: {
+ part: {
+ filters: {
+ part_detail: true,
+ supplier_detail: true,
+ supplier: options.supplier,
+ }
+ },
+ quantity: {},
+ reference: {},
+ purchase_price: {},
+ purchase_price_currency: {},
+ destination: {},
+ notes: {},
+ },
+ title: '{% trans "Edit Line Item" %}',
+ onSuccess: function() {
+ $(table).bootstrapTable('refresh');
+ }
+ });
+ });
+
+ $(table).find('.button-line-delete').click(function() {
+ var pk = $(this).attr('pk');
+
+ constructForm(`/api/order/po-line/${pk}/`, {
+ method: 'DELETE',
+ title: '{% trans "Delete Line Item" %}',
+ onSuccess: function() {
+ $(table).bootstrapTable('refresh');
+ }
+ });
+ });
+ }
+
+ if (options.allow_receive) {
+ $(table).find('.button-line-receive').click(function() {
+ var pk = $(this).attr('pk');
+
+ launchModalForm(`/order/purchase-order/${options.order}/receive/`, {
+ success: function() {
+ $(table).bootstrapTable('refresh');
+ },
+ data: {
+ line: pk,
+ },
+ secondary: [
+ {
+ field: 'location',
+ label: '{% trans "New Location" %}',
+ title: '{% trans "Create new stock location" %}',
+ url: "{% url 'stock-location-create' %}",
+ },
+ ]
+ });
+ });
+ }
+ }
+
+ $(table).inventreeTable({
+ onPostBody: setupCallbacks,
+ name: 'purchaseorderlines',
+ sidePagination: 'server',
+ formatNoMatches: function() {
+ return '{% trans "No line items found" %}';
+ },
+ queryParams: {
+ order: options.order,
+ part_detail: true
+ },
+ url: "{% url 'api-po-line-list' %}",
+ showFooter: true,
+ columns: [
+ {
+ field: 'pk',
+ title: 'ID',
+ visible: false,
+ switchable: false,
+ },
+ {
+ field: 'part',
+ sortable: true,
+ sortName: 'part_name',
+ title: '{% trans "Part" %}',
+ switchable: false,
+ formatter: function(value, row, index, field) {
+ if (row.part) {
+ return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${row.part_detail.pk}/`);
+ } else {
+ return '-';
+ }
+ },
+ footerFormatter: function() {
+ return '{% trans "Total" %}'
+ }
+ },
+ {
+ field: 'part_detail.description',
+ title: '{% trans "Description" %}',
+ },
+ {
+ sortable: true,
+ sortName: 'SKU',
+ field: 'supplier_part_detail.SKU',
+ title: '{% trans "SKU" %}',
+ formatter: function(value, row, index, field) {
+ if (value) {
+ return renderLink(value, `/supplier-part/${row.part}/`);
+ } else {
+ return '-';
+ }
+ },
+ },
+ {
+ sortable: true,
+ sortName: 'MPN',
+ field: 'supplier_part_detail.manufacturer_part_detail.MPN',
+ title: '{% trans "MPN" %}',
+ formatter: function(value, row, index, field) {
+ if (row.supplier_part_detail && row.supplier_part_detail.manufacturer_part) {
+ return renderLink(value, `/manufacturer-part/${row.supplier_part_detail.manufacturer_part}/`);
+ } else {
+ return "-";
+ }
+ },
+ },
+ {
+ sortable: true,
+ field: 'reference',
+ title: '{% trans "Reference" %}',
+ },
+ {
+ sortable: true,
+ field: 'quantity',
+ title: '{% trans "Quantity" %}',
+ footerFormatter: function(data) {
+ return data.map(function (row) {
+ return +row['quantity']
+ }).reduce(function (sum, i) {
+ return sum + i
+ }, 0)
+ }
+ },
+ {
+ sortable: true,
+ field: 'purchase_price',
+ title: '{% trans "Unit Price" %}',
+ formatter: function(value, row) {
+ return row.purchase_price_string || row.purchase_price;
+ }
+ },
+ {
+ field: 'total_price',
+ sortable: true,
+ field: 'total_price',
+ title: '{% trans "Total price" %}',
+ formatter: function(value, row) {
+ var total = row.purchase_price * row.quantity;
+ var formatter = new Intl.NumberFormat('en-US', {style: 'currency', currency: row.purchase_price_currency});
+ return formatter.format(total)
+ },
+ footerFormatter: function(data) {
+ var total = data.map(function (row) {
+ return +row['purchase_price']*row['quantity']
+ }).reduce(function (sum, i) {
+ return sum + i
+ }, 0)
+ var currency = (data.slice(-1)[0] && data.slice(-1)[0].purchase_price_currency) || 'USD';
+ var formatter = new Intl.NumberFormat('en-US', {style: 'currency', currency: currency});
+ return formatter.format(total)
+ }
+ },
+ {
+ sortable: false,
+ field: 'received',
+ switchable: false,
+ title: '{% trans "Received" %}',
+ formatter: function(value, row, index, field) {
+ return makeProgressBar(row.received, row.quantity, {
+ id: `order-line-progress-${row.pk}`,
+ });
+ },
+ sorter: function(valA, valB, rowA, rowB) {
+
+ if (rowA.received == 0 && rowB.received == 0) {
+ return (rowA.quantity > rowB.quantity) ? 1 : -1;
+ }
+
+ var progressA = parseFloat(rowA.received) / rowA.quantity;
+ var progressB = parseFloat(rowB.received) / rowB.quantity;
+
+ return (progressA < progressB) ? 1 : -1;
+ }
+ },
+ {
+ field: 'destination',
+ title: '{% trans "Destination" %}',
+ formatter: function(value, row) {
+ if (value) {
+ return renderLink(row.destination_detail.pathstring, `/stock/location/${value}/`);
+ } else {
+ return '-';
+ }
+ }
+ },
+ {
+ field: 'notes',
+ title: '{% trans "Notes" %}',
+ },
+ {
+ switchable: false,
+ field: 'buttons',
+ title: '',
+ formatter: function(value, row, index, field) {
+ var html = ``;
+
+ var pk = row.pk;
+
+ if (options.allow_edit) {
+ 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" %}');
+ }
+
+ if (options.allow_receive && row.received < row.quantity) {
+ html += makeIconButton('fa-clipboard-check', 'button-line-receive', pk, '{% trans "Receive line item" %}');
+ }
+
+ html += `
`;
+
+ return html;
+ },
+ }
+ ]
+ });
+
+}
+
+
function loadSalesOrderTable(table, options) {
options.params = options.params || {};