2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 04:55:44 +00:00

Merge branch 'master' of https://github.com/inventree/InvenTree into part-import

This commit is contained in:
2021-06-02 00:36:15 +02:00
143 changed files with 53382 additions and 16913 deletions

View File

@ -13,40 +13,43 @@
{% block settings %}
<table class='table table-striped table-condensed'>
{% include "InvenTree/settings/header.html" %}
<tbody>
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DEFAULT_CURRENCY" icon="fa-dollar-sign" %}
{% include "InvenTree/settings/setting.html" with key="CUSTOM_EXCHANGE_RATES" icon="fa-edit" %}
<tr>
<th>{% trans "Base Currency" %}</th>
<th>{{ base_currency }}</th>
</tr>
<tr>
<th colspan='2'>{% trans "Exchange Rates" %}</th>
</tr>
{% for rate in rates %}
<tr>
<td>{{ rate.currency }}</td>
<td>{{ rate.value }}</td>
</tr>
{% endfor %}
<tr>
<th>
{% trans "Last Update" %}
</th>
<td>
{% if rates_updated %}
{{ rates_updated }}
{% else %}
<i>{% trans "Never" %}</i>
{% endif %}
<form action='{% url "settings-currencies-refresh" %}' method='post'>
<div id='refresh-rates-form'>
{% csrf_token %}
<button type='submit' id='update-rates' class='btn btn-default float-right'>{% trans "Update Now" %}</button>
</div>
</form>
</td>
</tr>
</tbody>
</table>
<div class='row'>
<div class='col-sm-6'>
<h4>{% blocktrans with cur=default_currency %}Exchange Rates - Convert to {{cur}}{% endblocktrans %}</h4>
</div>
</div>
<form action="{% url 'settings-currencies' %}" method="post">
<div id='exchange_rate_form'>
{% csrf_token %}
{% load crispy_forms_tags %}
{% crispy form %}
{% if custom_rates is False %}
<button type="submit" class='btn btn-primary'>{% trans "Refresh Exchange Rates" %}</button>
{% else %}
<button type="submit" class='btn btn-primary'>{% trans "Update Exchange Rates" %}</button>
{% endif %}
</div>
</form>
{% endblock %}
{% block js_ready %}
{{ block.super }}
{% if api_rates_success is False %}
var alert_msg = {% blocktrans %}"Failed to refresh exchange rates" {% endblocktrans %};
showAlertOrCache("alert-danger", alert_msg, null, 5000);
{% endif %}
{% endblock %}

View File

@ -15,6 +15,9 @@
<li {% if tab == 'global' %} class='active' {% endif %}>
<a href='{% url "settings-global" %}'><span class='fas fa-globe'></span> {% trans "Global" %}</a>
</li>
<li {% if tab == 'currencies' %} class='active'{% endif %}>
<a href="{% url 'settings-currencies' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currencies" %}</a>
</li>
<li {% if tab == 'report' %} class='active' {% endif %}>
<a href='{% url "settings-report" %}'><span class='fas fa-file-pdf'></span> {% trans "Report" %}</a>
</li>
@ -36,8 +39,5 @@
<li {% if tab == 'so' %} class='active'{% endif %}>
<a href="{% url 'settings-so' %}"><span class='fas fa-truck'></span> {% trans "Sales Orders" %}</a>
</li>
<li {% if tab == 'currencies' %} class='active'{% endif %}>
<a href="{% url 'settings-currencies' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currencies" %}</a>
</li>
</ul>
{% endif %}

View File

@ -37,12 +37,12 @@
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table-group-by.css' %}">
<link rel="stylesheet" href="{% static 'bootstrap-table/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css' %}">
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/select2-bootstrap.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-toggle.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table-filter-control.css' %}">
<link rel="stylesheet" href="{% static 'fullcalendar/main.css' %}">
<link rel="stylesheet" href="{% static 'script/jquery-ui/jquery-ui.min.css' %}">
@ -119,26 +119,28 @@
<script type="text/javascript" src="{% static 'script/bootstrap/bootstrap.min.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-treeview.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table-en-US.min.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table-group-by.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-toggle.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap/bootstrap-table-filter-control.js' %}"></script>
<!-- <script type='text/javascript' src="{% static 'script/bootstrap/filter-control-utils.js' %}"></script> -->
<script type='text/javascript' src="{% static 'bootstrap-table/bootstrap-table.js' %}"></script>
<script type='text/javascript' src="{% static 'bootstrap-table/bootstrap-table-en-US.min.js' %}"></script>
<!-- jquery-treegrid -->
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.js" %}'></script>
<script type='text/javascript' src='{% static "treegrid/js/jquery.treegrid.bootstrap3.js" %}'></script>
<!-- boostrap-table-treegrid -->
<!-- boostrap-table extensions -->
<script type='text/javascript' src='{% static "bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.js" %}'></script>
<script type='text/javascript' src='{% static "bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.js" %}'></script>
<script type='text/javascript' src='{% static "bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.js" %}'></script>
<script type='text/javascript' src='{% static "bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.js" %}'></script>
<!-- 3rd party general js -->
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
<script type="text/javascript" src="{% static 'fullcalendar/locales-all.js' %}"></script>
<script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script>
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
<script type='text/javascript' src="{% static 'script/chart.min.js' %}"></script>
<script type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
<script type='text/javascript' src="{% static 'script/randomColor.min.js' %}"></script>
<!-- general InvenTree -->
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>

View File

@ -243,6 +243,22 @@ function loadBomTable(table, options) {
}
});
cols.push(
{
field: 'purchase_price_range',
title: '{% trans "Purchase Price Range" %}',
searchable: false,
sortable: true,
});
cols.push(
{
field: 'purchase_price_avg',
title: '{% trans "Purchase Price Average" %}',
searchable: false,
sortable: true,
});
/*
// TODO - Re-introduce the pricing column at a later stage,
@ -269,11 +285,18 @@ function loadBomTable(table, options) {
title: '{% trans "Optional" %}',
searchable: false,
formatter: function(value) {
if (value == '1') return '{% trans "true" %}';
if (value == '0') return '{% trans "false" %}';
return yesNoLabel(value);
}
});
cols.push({
field: 'allow_variants',
title: '{% trans "Allow Variants" %}',
formatter: function(value) {
return yesNoLabel(value);
}
})
cols.push({
field: 'inherited',
title: '{% trans "Inherited" %}',
@ -281,7 +304,7 @@ function loadBomTable(table, options) {
formatter: function(value, row, index, field) {
// This BOM item *is* inheritable, but is defined for this BOM
if (!row.inherited) {
return "-";
return yesNoLabel(false);
} else if (row.part == options.parent_id) {
return '{% trans "Inherited" %}';
} else {

View File

@ -372,7 +372,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
data.forEach(function(item) {
// Group BuildItem objects by part
var part = item.part;
var part = item.bom_part || item.part;
var key = parseInt(part);
if (!(key in allocations)) {
@ -461,6 +461,16 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
data: row.allocations,
showHeader: true,
columns: [
{
field: 'part',
title: '{% trans "Part" %}',
formatter: function(value, row) {
var html = imageHoverIcon(row.part_thumb);
html += renderLink(row.part_name, `/part/${value}/`);
return html;
}
},
{
width: '50%',
field: 'quantity',

View File

@ -978,6 +978,7 @@ function showModalImage(image_url) {
// Set image content
$('#modal-image').attr('src', image_url);
modal.hide();
modal.show();
modal.animate({

View File

@ -5,6 +5,14 @@
* Requires api.js to be loaded first
*/
function yesNoLabel(value) {
if (value) {
return `<span class='label label-green'>{% trans "YES" %}</span>`;
} else {
return `<span class='label label-yellow'>{% trans "NO" %}</span>`;
}
}
function toggleStar(options) {
/* Toggle the 'starred' status of a part.
* Performs AJAX queries and updates the display on the button.
@ -278,6 +286,64 @@ function loadParametricPartTable(table, options={}) {
}
function partGridTile(part) {
// Generate a "grid tile" view for a particular part
// Rows for table view
var rows = '';
if (part.IPN) {
rows += `<tr><td><b>{% trans "IPN" %}</b></td><td>${part.IPN}</td></tr>`;
}
var stock = `${part.in_stock}`;
if (!part.in_stock) {
stock = `<span class='label label-red'>{% trans "No Stock" %}</label>`;
}
rows += `<tr><td><b>{% trans "Stock" %}</b></td><td>${stock}</td></tr>`;
if (part.on_order) {
rows += `<tr><td><b>{$ trans "On Order" %}</b></td><td>${part.on_order}</td></tr>`;
}
if (part.building) {
rows += `<tr><td><b>{% trans "Building" %}</b></td><td>${part.building}</td></tr>`;
}
var html = `
<div class='col-sm-3 card'>
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<a href='/part/${part.pk}/'>
<b>${part.full_name}</b>
</a>
${makePartIcons(part)}
<br>
<i>${part.description}</i>
</div>
<div class='panel-content'>
<div class='row'>
<div class='col-sm-6'>
<img src='${part.thumbnail}' class='card-thumb' onclick='showModalImage("${part.image}")'>
</div>
<div class='col-sm-6'>
<table class='table table-striped table-condensed'>
${rows}
</table>
</div>
</div>
</div>
</div>
</div>
`;
return html;
}
function loadPartTable(table, url, options={}) {
/* Load part listing data into specified table.
*
@ -452,8 +518,30 @@ function loadPartTable(table, url, options={}) {
formatNoMatches: function() { return '{% trans "No parts found" %}'; },
columns: columns,
showColumns: true,
});
showCustomView: false,
showCustomViewButton: false,
customView: function(data) {
var html = '';
html = `<div class='row'>`;
data.forEach(function(row, index) {
// Force a new row every 4 columns, to prevent visual issues
if ((index > 0) && (index % 4 == 0) && (index < data.length)) {
html += `</div><div class='row'>`;
}
html += partGridTile(row);
});
html += `</div>`;
return html;
}
});
if (options.buttons) {
linkButtonsToSelection($(table), options.buttons);
}
@ -582,16 +670,6 @@ function loadPartCategoryTable(table, options) {
});
}
function yesNoLabel(value) {
if (value) {
return `<span class='label label-green'>{% trans "YES" %}</span>`;
} else {
return `<span class='label label-yellow'>{% trans "NO" %}</span>`;
}
}
function loadPartTestTemplateTable(table, options) {
/*
* Load PartTestTemplate table.
@ -688,3 +766,60 @@ function loadPartTestTemplateTable(table, options) {
]
});
}
function loadStockPricingChart(context, data) {
return new Chart(context, {
type: 'bar',
data: data,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {legend: {position: 'bottom'}},
scales: {
y: {
type: 'linear',
position: 'left',
grid: {display: false},
title: {
display: true,
text: '{% trans "Single Price" %}'
}
},
y1: {
type: 'linear',
position: 'right',
grid: {display: false},
titel: {
display: true,
text: '{% trans "Quantity" %}',
position: 'right'
}
},
y2: {
type: 'linear',
position: 'left',
grid: {display: false},
title: {
display: true,
text: '{% trans "Single Price Difference" %}'
}
}
},
}
});
}
function loadBomChart(context, data) {
return new Chart(context, {
type: 'doughnut',
data: data,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {legend: {position: 'bottom'},
scales: {xAxes: [{beginAtZero: true, ticks: {autoSkip: false}}]}}
}
});
}

View File

@ -32,17 +32,32 @@ function removeStockRow(e) {
}
function passFailBadge(result) {
function passFailBadge(result, align='float-right') {
if (result) {
return `<span class='label label-green float-right'>{% trans "PASS" %}</span>`;
return `<span class='label label-green ${align}'>{% trans "PASS" %}</span>`;
} else {
return `<span class='label label-red float-right'>{% trans "FAIL" %}</span>`;
return `<span class='label label-red ${align}'>{% trans "FAIL" %}</span>`;
}
}
function noResultBadge() {
return `<span class='label label-blue float-right'>{% trans "NO RESULT" %}</span>`;
function noResultBadge(align='float-right') {
return `<span class='label label-blue ${align}'>{% trans "NO RESULT" %}</span>`;
}
function formatDate(row) {
// Function for formatting date field
var html = row.date;
if (row.user_detail) {
html += `<span class='badge'>${row.user_detail.username}</span>`;
}
if (row.attachment) {
html += `<a href='${row.attachment}'><span class='fas fa-file-alt label-right'></span></a>`;
}
return html;
}
function loadStockTestResultsTable(table, options) {
@ -50,21 +65,6 @@ function loadStockTestResultsTable(table, options) {
* Load StockItemTestResult table
*/
function formatDate(row) {
// Function for formatting date field
var html = row.date;
if (row.user_detail) {
html += `<span class='badge'>${row.user_detail.username}</span>`;
}
if (row.attachment) {
html += `<a href='${row.attachment}'><span class='fas fa-file-alt label-right'></span></a>`;
}
return html;
}
function makeButtons(row, grouped) {
var html = `<div class='btn-group float-right' role='group'>`;
@ -81,17 +81,30 @@ function loadStockTestResultsTable(table, options) {
return html;
}
// First, load all the test templates
var parent_node = "parent node";
table.inventreeTable({
url: "{% url 'api-part-test-template-list' %}",
method: 'get',
name: 'testresult',
treeEnable: true,
rootParentId: parent_node,
parentIdField: 'parent',
idField: 'pk',
uniqueId: 'key',
treeShowField: 'test_name',
formatNoMatches: function() {
return "{% trans 'No test results found' %}";
return '{% trans "No test results found" %}';
},
queryParams: {
part: options.part,
},
onPostBody: function() {
table.treegrid({
treeColumn: 0,
});
table.treegrid("collapseAll");
},
columns: [
{
field: 'pk',
@ -130,100 +143,85 @@ function loadStockTestResultsTable(table, options) {
{
field: 'date',
title: '{% trans "Test Date" %}',
sortable: true,
formatter: function(value, row) {
return formatDate(row);
}
},
},
{
field: 'buttons',
formatter: function(value, row) {
return makeButtons(row, false);
}
},
],
groupBy: true,
groupByField: 'test_name',
groupByFormatter: function(field, id, data) {
// Extract the "latest" row (data are returned in date order from the server)
var latest = data[data.length-1];
switch (field) {
case 'test_name':
return latest.test_name + ` <i>(${data.length})</i>` + passFailBadge(latest.result);
case 'value':
return latest.value;
case 'notes':
return latest.notes;
case 'date':
return formatDate(latest);
case 'buttons':
// Buttons are done differently for grouped rows
return makeButtons(latest, true);
default:
return "---";
}
},
],
onLoadSuccess: function(tableData) {
// Once the test template data are loaded, query for results
// Set "parent" for each existing row
tableData.forEach(function(item, idx) {
tableData[idx].parent = options.stock_item;
});
// Once the test template data are loaded, query for test results
inventreeGet(
"{% url 'api-stock-test-result-list' %}",
'{% url "api-stock-test-result-list" %}',
{
stock_item: options.stock_item,
user_detail: true,
attachment_detail: true,
ordering: "-date",
},
{
success: function(data) {
// Iterate through the returned test data
data.forEach(function(item, index) {
// Iterate through the returned test result data, and group by test
data.forEach(function(item) {
var match = false;
var override = false;
var key = item.key;
// Try to associate this result with a test row
// Attempt to associate this result with an existing test
tableData.forEach(function(row, index) {
// The result matches the test template row
if (key == row.key) {
// Force the names to be the same!
item.test_name = row.test_name;
item.required = row.required;
match = true;
if (row.result == null) {
// The original row has not recorded a result - override!
item.parent = parent_node;
tableData[index] = item;
override = true;
} else {
item.parent = row.pk;
}
match = true;
}
});
// No match could be found (this is a new test!)
// No match could be found
if (!match) {
item.test_name = item.test;
item.parent = parent_node;
}
if (!override) {
tableData.push(item);
}
});
// Finally, push the data back into the table!
// Push data back into the table
table.bootstrapTable("load", tableData);
}
},
);
}
)
}
});
}
}
function loadStockTable(table, options) {
/* Load data into a stock table with adjustable options.
@ -1306,33 +1304,6 @@ function createNewStockItem(options) {
function loadInstalledInTable(table, options) {
/*
* Display a table showing the stock items which are installed in this stock item.
* This is a multi-level tree table, where the "top level" items are Part objects,
* and the children of each top-level item are the associated installed stock items.
*
* The process for retrieving data and displaying the table is as follows:
*
* A) Get BOM data for the stock item
* - It is assumed that the stock item will be for an assembly
* (otherwise why are we installing stuff anyway?)
* - Request BOM items for stock_item.part (and only for trackable sub items)
*
* B) Add parts to table
* - Create rows for each trackable sub-part in the table
*
* C) Gather installed stock item data
* - Get the list of installed stock items via the API
* - If the Part reference is already in the table, add the sub-item as a child
* - If this is a stock item for a *new* part, request that part from the API,
* and add that part as a new row, then add the stock item as a child of that part
*
* D) Enjoy!
*
*
* And the options object contains the following things:
*
* - stock_item: The PK of the master stock_item object
* - part: The PK of the Part reference of the stock_item object
* - quantity: The quantity of the stock item
*/
function updateCallbacks() {
@ -1355,246 +1326,88 @@ function loadInstalledInTable(table, options) {
});
}
table.inventreeTable(
{
url: "{% url 'api-bom-list' %}",
queryParams: {
part: options.part,
sub_part_trackable: true,
sub_part_detail: true,
},
showColumns: false,
name: 'installed-in',
detailView: true,
detailViewByClick: true,
detailFilter: function(index, row) {
return row.installed_count && row.installed_count > 0;
},
detailFormatter: function(index, row, element) {
var subTableId = `installed-table-${row.sub_part}`;
table.inventreeTable({
url: "{% url 'api-stock-list' %}",
queryParams: {
installed_in: options.stock_item,
part_detail: true,
},
formatNoMatches: function() {
return '{% trans "No installed items" %}';
},
columns: [
{
field: 'part',
title: '{% trans "Part" %}',
formatter: function(value, row) {
var html = '';
var html = `<div class='sub-table'><table class='table table-condensed table-striped' id='${subTableId}'></table></div>`;
html += imageHoverIcon(row.part_detail.thumbnail);
html += renderLink(row.part_detail.full_name, `/stock/item/${row.pk}/`);
element.html(html);
var subTable = $(`#${subTableId}`);
// Display a "sub table" showing all the linked stock items
subTable.bootstrapTable({
data: row.installed_items,
showHeader: true,
columns: [
{
field: 'item',
title: '{% trans "Stock Item" %}',
formatter: function(value, subrow, index, field) {
var pk = subrow.pk;
var html = '';
if (subrow.serial && subrow.quantity == 1) {
html += `{% trans "Serial" %}: ${subrow.serial}`;
} else {
html += `{% trans "Quantity" %}: ${subrow.quantity}`;
}
return renderLink(html, `/stock/item/${subrow.pk}/`);
},
},
{
field: 'status',
title: '{% trans "Status" %}',
formatter: function(value, subrow, index, field) {
return stockStatusDisplay(value);
}
},
{
field: 'batch',
title: '{% trans "Batch" %}',
},
{
field: 'actions',
title: '',
formatter: function(value, subrow, index) {
var pk = subrow.pk;
var html = '';
// Add some buttons yo!
html += `<div class='btn-group float-right' role='group'>`;
html += makeIconButton('fa-unlink', 'button-uninstall', pk, "{% trans 'Uninstall stock item' %}");
html += `</div>`;
return html;
}
}
],
onPostBody: function() {
// Setup button callbacks
subTable.find('.button-uninstall').click(function() {
var pk = $(this).attr('pk');
launchModalForm(
"{% url 'stock-item-uninstall' %}",
{
data: {
'items[]': [pk],
},
success: function() {
// Refresh entire table!
table.bootstrapTable('refresh');
}
}
);
});
}
});
},
columns: [
{
checkbox: true,
title: '{% trans "Select" %}',
searchable: false,
switchable: false,
},
{
field: 'pk',
title: 'ID',
visible: false,
switchable: false,
},
{
field: 'part',
title: '{% trans "Part" %}',
sortable: true,
formatter: function(value, row, index, field) {
var url = `/part/${row.sub_part}/`;
var thumb = row.sub_part_detail.thumbnail;
var name = row.sub_part_detail.full_name;
html = imageHoverIcon(thumb) + renderLink(name, url);
if (row.not_in_bom) {
html = `<i>${html}</i>`
}
return html;
}
},
{
field: 'installed',
title: '{% trans "Installed" %}',
sortable: false,
formatter: function(value, row, index, field) {
// Construct a progress showing how many items have been installed
var installed = row.installed_count || 0;
var required = row.quantity || 0;
required *= options.quantity;
var progress = makeProgressBar(installed, required, {
id: row.sub_part.pk,
});
return progress;
}
},
{
field: 'actions',
switchable: false,
formatter: function(value, row) {
var pk = row.sub_part;
var html = `<div class='btn-group float-right' role='group'>`;
html += makeIconButton('fa-link', 'button-install', pk, '{% trans "Install item" %}');
html += `</div>`;
return html;
}
return html;
}
],
onLoadSuccess: function() {
// Grab a list of parts which are actually installed in this stock item
},
{
field: 'quantity',
title: '{% trans "Quantity" %}',
formatter: function(value, row) {
inventreeGet(
"{% url 'api-stock-list' %}",
var html = '';
if (row.serial && row.quantity == 1) {
html += `{% trans "Serial" %}: ${row.serial}`;
} else {
html += `${row.quantity}`;
}
return renderLink(html, `/stock/item/${row.pk}/`);
}
},
{
field: 'status',
title: '{% trans "Status" %}',
formatter: function(value, row) {
return stockStatusDisplay(value);
}
},
{
field: 'batch',
title: '{% trans "Batch" %}',
},
{
field: 'buttons',
title: '',
switchable: false,
formatter: function(value, row) {
var pk = row.pk;
var html = '';
html += `<div class='btn-group float-right' role='group'>`;
html += makeIconButton('fa-unlink', 'button-uninstall', pk, '{% trans "Uninstall Stock Item" %}');
html += `</div>`;
return html;
}
}
],
onPostBody: function() {
// Assign callbacks to the buttons
table.find('.button-uninstall').click(function() {
var pk = $(this).attr('pk');
launchModalForm(
'{% url "stock-item-uninstall" %}',
{
installed_in: options.stock_item,
part_detail: true,
},
{
success: function(stock_items) {
var table_data = table.bootstrapTable('getData');
stock_items.forEach(function(item) {
var match = false;
for (var idx = 0; idx < table_data.length; idx++) {
var row = table_data[idx];
// Check each row in the table to see if this stock item matches
table_data.forEach(function(row) {
// Match on "sub_part"
if (row.sub_part == item.part) {
// First time?
if (row.installed_count == null) {
row.installed_count = 0;
row.installed_items = [];
}
row.installed_count += item.quantity;
row.installed_items.push(item);
// Push the row back into the table
table.bootstrapTable('updateRow', idx, row, true);
match = true;
}
});
if (match) {
break;
}
}
if (!match) {
// The stock item did *not* match any items in the BOM!
// Add a new row to the table...
// Contruct a new "row" to add to the table
var new_row = {
sub_part: item.part,
sub_part_detail: item.part_detail,
not_in_bom: true,
installed_count: item.quantity,
installed_items: [item],
};
table.bootstrapTable('append', [new_row]);
}
});
// Update button callback links
updateCallbacks();
data: {
'items[]': pk,
},
success: function() {
table.bootstrapTable('refresh');
}
}
);
updateCallbacks();
},
)
});
}
);
});
}

View File

@ -49,6 +49,10 @@ function getAvailableTableFilters(tableKey) {
inherited: {
type: 'bool',
title: '{% trans "Inherited" %}',
},
allow_variants: {
type: 'bool',
title: '{% trans "Allow Variant Stock" %}',
}
};
}

View File

@ -14,7 +14,6 @@
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">

View File

@ -13,7 +13,6 @@
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">

View File

@ -14,7 +14,6 @@
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">

View File

@ -14,7 +14,6 @@
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">

View File

@ -14,7 +14,6 @@
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">

View File

@ -14,7 +14,6 @@
<!-- CSS -->
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-table.css' %}">
<link rel="stylesheet" href="{% static 'css/inventree.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">