mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-01 13:06:45 +00:00
Merge branch 'master' into scheduling
This commit is contained in:
commit
954f0afb85
@ -383,9 +383,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
Returns the BOM items for the part referenced by this BuildOrder
|
Returns the BOM items for the part referenced by this BuildOrder
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self.part.bom_items.all().prefetch_related(
|
return self.part.get_bom_items()
|
||||||
'sub_part'
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tracked_bom_items(self):
|
def tracked_bom_items(self):
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1453,7 +1453,9 @@ class Part(MPTTModel):
|
|||||||
By default, will include inherited BOM items
|
By default, will include inherited BOM items
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return BomItem.objects.filter(self.get_bom_item_filter(include_inherited=include_inherited))
|
queryset = BomItem.objects.filter(self.get_bom_item_filter(include_inherited=include_inherited))
|
||||||
|
|
||||||
|
return queryset.prefetch_related('sub_part')
|
||||||
|
|
||||||
def get_installed_part_options(self, include_inherited=True, include_variants=True):
|
def get_installed_part_options(self, include_inherited=True, include_variants=True):
|
||||||
"""
|
"""
|
||||||
@ -1906,6 +1908,9 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
include_inherited = kwargs.get('include_inherited', False)
|
include_inherited = kwargs.get('include_inherited', False)
|
||||||
|
|
||||||
|
# Should substitute parts be duplicated?
|
||||||
|
copy_substitutes = kwargs.get('copy_substitutes', True)
|
||||||
|
|
||||||
# Copy existing BOM items from another part
|
# Copy existing BOM items from another part
|
||||||
# Note: Inherited BOM Items will *not* be duplicated!!
|
# Note: Inherited BOM Items will *not* be duplicated!!
|
||||||
for bom_item in other.get_bom_items(include_inherited=include_inherited).all():
|
for bom_item in other.get_bom_items(include_inherited=include_inherited).all():
|
||||||
@ -1928,11 +1933,22 @@ class Part(MPTTModel):
|
|||||||
if not bom_item.sub_part.check_add_to_bom(self, raise_error=raise_error):
|
if not bom_item.sub_part.check_add_to_bom(self, raise_error=raise_error):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Obtain a list of direct substitute parts against this BomItem
|
||||||
|
substitutes = BomItemSubstitute.objects.filter(bom_item=bom_item)
|
||||||
|
|
||||||
# Construct a new BOM item
|
# Construct a new BOM item
|
||||||
bom_item.part = self
|
bom_item.part = self
|
||||||
bom_item.pk = None
|
bom_item.pk = None
|
||||||
|
|
||||||
bom_item.save()
|
bom_item.save()
|
||||||
|
bom_item.refresh_from_db()
|
||||||
|
|
||||||
|
if copy_substitutes:
|
||||||
|
for sub in substitutes:
|
||||||
|
# Duplicate the substitute (and point to the *new* BomItem object)
|
||||||
|
sub.pk = None
|
||||||
|
sub.bom_item = bom_item
|
||||||
|
sub.save()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def copy_parameters_from(self, other, **kwargs):
|
def copy_parameters_from(self, other, **kwargs):
|
||||||
|
@ -656,6 +656,9 @@ class PartCopyBOMSerializer(serializers.Serializer):
|
|||||||
fields = [
|
fields = [
|
||||||
'part',
|
'part',
|
||||||
'remove_existing',
|
'remove_existing',
|
||||||
|
'copy_substitutes',
|
||||||
|
'include_inherited',
|
||||||
|
'skip_invalid',
|
||||||
]
|
]
|
||||||
|
|
||||||
part = serializers.PrimaryKeyRelatedField(
|
part = serializers.PrimaryKeyRelatedField(
|
||||||
@ -692,6 +695,12 @@ class PartCopyBOMSerializer(serializers.Serializer):
|
|||||||
default=False,
|
default=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
copy_substitutes = serializers.BooleanField(
|
||||||
|
label=_('Copy Substitute Parts'),
|
||||||
|
help_text=_('Copy substitute parts when duplicate BOM items'),
|
||||||
|
default=True,
|
||||||
|
)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""
|
"""
|
||||||
Actually duplicate the BOM
|
Actually duplicate the BOM
|
||||||
@ -706,6 +715,7 @@ class PartCopyBOMSerializer(serializers.Serializer):
|
|||||||
clear=data.get('remove_existing', True),
|
clear=data.get('remove_existing', True),
|
||||||
skip_invalid=data.get('skip_invalid', False),
|
skip_invalid=data.get('skip_invalid', False),
|
||||||
include_inherited=data.get('include_inherited', False),
|
include_inherited=data.get('include_inherited', False),
|
||||||
|
copy_substitutes=data.get('copy_substitutes', True),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -137,7 +137,13 @@
|
|||||||
<h4>{% trans "Sales Order Allocations" %}</h4>
|
<h4>{% trans "Sales Order Allocations" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class='panel-content'>
|
<div class='panel-content'>
|
||||||
<table class='table table-striped table-condensed' id='sales-order-allocation-table'></table>
|
|
||||||
|
<div id='sales-order-allocation-button-toolbar'>
|
||||||
|
<div class='btn-group' role='group'>
|
||||||
|
{% include "filter_list.html" with id="salesorderallocation" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class='table table-striped table-condensed' id='sales-order-allocation-table' data-toolbar='#sales-order-allocation-button-toolbar'></table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -357,7 +363,12 @@
|
|||||||
<h4>{% trans "Build Order Allocations" %}</h4>
|
<h4>{% trans "Build Order Allocations" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
<div class='panel-content'>
|
<div class='panel-content'>
|
||||||
<table class='table table-striped table-condensed' id='build-order-allocation-table'></table>
|
<div id='build-allocation-button-toolbar'>
|
||||||
|
<div class='btn-group' role='group'>
|
||||||
|
{% include "filter_list.html" with id="buildorderallocation" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<table class='table table-striped table-condensed' id='build-order-allocation-table' data-toolbar='#build-allocation-button-toolbar'></table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -742,6 +753,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Load the BOM table data in the pricing view
|
// Load the BOM table data in the pricing view
|
||||||
|
{% if part.has_bom and roles.sales_order.view %}
|
||||||
loadBomTable($("#bom-pricing-table"), {
|
loadBomTable($("#bom-pricing-table"), {
|
||||||
editable: false,
|
editable: false,
|
||||||
bom_url: "{% url 'api-bom-list' %}",
|
bom_url: "{% url 'api-bom-list' %}",
|
||||||
@ -749,6 +761,7 @@
|
|||||||
parent_id: {{ part.id }} ,
|
parent_id: {{ part.id }} ,
|
||||||
sub_part_detail: true,
|
sub_part_detail: true,
|
||||||
});
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
onPanelLoad("purchase-orders", function() {
|
onPanelLoad("purchase-orders", function() {
|
||||||
loadPartPurchaseOrderTable(
|
loadPartPurchaseOrderTable(
|
||||||
|
@ -59,13 +59,13 @@
|
|||||||
<ul class='dropdown-menu'>
|
<ul class='dropdown-menu'>
|
||||||
<li>
|
<li>
|
||||||
<a class='dropdown-item' href='#' id='part-count'>
|
<a class='dropdown-item' href='#' id='part-count'>
|
||||||
<span class='fas fa-clipboard-list'></span>
|
<span class='fas fa-check-circle icon-green'></span>
|
||||||
{% trans "Count part stock" %}
|
{% trans "Count part stock" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a class='dropdown-item' href='#' id='part-move'>
|
<a class='dropdown-item' href='#' id='part-move'>
|
||||||
<span class='fas fa-exchange-alt'></span>
|
<span class='fas fa-exchange-alt icon-blue'></span>
|
||||||
{% trans "Transfer part stock" %}
|
{% trans "Transfer part stock" %}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -909,7 +909,6 @@ class StockItem(MPTTModel):
|
|||||||
""" Can this stock item be deleted? It can NOT be deleted under the following circumstances:
|
""" Can this stock item be deleted? It can NOT be deleted under the following circumstances:
|
||||||
|
|
||||||
- Has installed stock items
|
- Has installed stock items
|
||||||
- Has a serial number and is tracked
|
|
||||||
- Is installed inside another StockItem
|
- Is installed inside another StockItem
|
||||||
- It has been assigned to a SalesOrder
|
- It has been assigned to a SalesOrder
|
||||||
- It has been assigned to a BuildOrder
|
- It has been assigned to a BuildOrder
|
||||||
@ -918,9 +917,6 @@ class StockItem(MPTTModel):
|
|||||||
if self.installed_item_count() > 0:
|
if self.installed_item_count() > 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.part.trackable and self.serial is not None:
|
|
||||||
return False
|
|
||||||
|
|
||||||
if self.sales_order is not None:
|
if self.sales_order is not None:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@
|
|||||||
<ul class='dropdown-menu' role='menu'>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
{% if not item.serialized %}
|
{% if not item.serialized %}
|
||||||
{% if item.in_stock %}
|
{% if item.in_stock %}
|
||||||
<li><a class='dropdown-item' href='#' id='stock-count' title='{% trans "Count stock" %}'><span class='fas fa-clipboard-list'></span> {% trans "Count stock" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='stock-count' title='{% trans "Count stock" %}'><span class='fas fa-check-circle icon-green'></span> {% trans "Count stock" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not item.customer %}
|
{% if not item.customer %}
|
||||||
<li><a class='dropdown-item' href='#' id='stock-add' title='{% trans "Add stock" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='stock-add' title='{% trans "Add stock" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
|
||||||
|
@ -671,9 +671,7 @@ function loadBomTable(table, options={}) {
|
|||||||
// Do we show part pricing in the BOM table?
|
// Do we show part pricing in the BOM table?
|
||||||
var show_pricing = global_settings.PART_SHOW_PRICE_IN_BOM;
|
var show_pricing = global_settings.PART_SHOW_PRICE_IN_BOM;
|
||||||
|
|
||||||
if (!show_pricing) {
|
params.include_pricing = show_pricing == true;
|
||||||
params.include_pricing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.part_detail) {
|
if (options.part_detail) {
|
||||||
params.part_detail = true;
|
params.part_detail = true;
|
||||||
@ -989,32 +987,40 @@ function loadBomTable(table, options={}) {
|
|||||||
|
|
||||||
// Function to request BOM data for sub-items
|
// Function to request BOM data for sub-items
|
||||||
// This function may be called recursively for multi-level BOMs
|
// This function may be called recursively for multi-level BOMs
|
||||||
function requestSubItems(bom_pk, part_pk) {
|
function requestSubItems(bom_pk, part_pk, depth=0) {
|
||||||
|
|
||||||
// TODO: 2022-02-03 Currently, multi-level BOMs are not actually displayed.
|
// Prevent multi-level recursion
|
||||||
|
const MAX_BOM_DEPTH = 25;
|
||||||
|
|
||||||
// Re-enable this function once multi-level display has been re-deployed
|
if (depth >= MAX_BOM_DEPTH) {
|
||||||
|
console.log(`Maximum BOM depth (${MAX_BOM_DEPTH}) reached!`);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
inventreeGet(
|
inventreeGet(
|
||||||
options.bom_url,
|
options.bom_url,
|
||||||
{
|
{
|
||||||
part: part_pk,
|
part: part_pk,
|
||||||
sub_part_detail: true,
|
sub_part_detail: true,
|
||||||
|
include_pricing: show_pricing == true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
|
||||||
|
// Add the returned sub-items to the table
|
||||||
for (var idx = 0; idx < response.length; idx++) {
|
for (var idx = 0; idx < response.length; idx++) {
|
||||||
|
|
||||||
response[idx].parentId = bom_pk;
|
response[idx].parentId = bom_pk;
|
||||||
|
|
||||||
if (response[idx].sub_part_detail.assembly) {
|
|
||||||
requestSubItems(response[idx].pk, response[idx].sub_part);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table.bootstrapTable('append', response);
|
table.bootstrapTable('append', response);
|
||||||
|
|
||||||
|
// Next, re-iterate and check if the new items also have sub items
|
||||||
|
response.forEach(function(bom_item) {
|
||||||
|
if (bom_item.sub_part_detail.assembly) {
|
||||||
|
requestSubItems(bom_item.pk, bom_item.sub_part, depth + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
table.treegrid('collapseAll');
|
table.treegrid('collapseAll');
|
||||||
},
|
},
|
||||||
error: function(xhr) {
|
error: function(xhr) {
|
||||||
@ -1026,7 +1032,7 @@ function loadBomTable(table, options={}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table.inventreeTable({
|
table.inventreeTable({
|
||||||
treeEnable: !options.editable,
|
treeEnable: true,
|
||||||
rootParentId: parent_id,
|
rootParentId: parent_id,
|
||||||
idField: 'pk',
|
idField: 'pk',
|
||||||
uniqueId: 'pk',
|
uniqueId: 'pk',
|
||||||
@ -1066,19 +1072,19 @@ function loadBomTable(table, options={}) {
|
|||||||
url: options.bom_url,
|
url: options.bom_url,
|
||||||
onPostBody: function() {
|
onPostBody: function() {
|
||||||
|
|
||||||
if (!options.editable) {
|
|
||||||
table.treegrid({
|
table.treegrid({
|
||||||
treeColumn: 0,
|
treeColumn: 1,
|
||||||
onExpand: function() {
|
onExpand: function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
table.treegrid('collapseAll');
|
||||||
},
|
},
|
||||||
onLoadSuccess: function() {
|
onLoadSuccess: function() {
|
||||||
|
|
||||||
if (options.editable) {
|
if (options.editable) {
|
||||||
table.bootstrapTable('uncheckAll');
|
table.bootstrapTable('uncheckAll');
|
||||||
} else {
|
}
|
||||||
|
|
||||||
var data = table.bootstrapTable('getData');
|
var data = table.bootstrapTable('getData');
|
||||||
|
|
||||||
@ -1099,7 +1105,6 @@ function loadBomTable(table, options={}) {
|
|||||||
requestSubItems(row.pk, row.sub_part);
|
requestSubItems(row.pk, row.sub_part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -475,6 +475,7 @@ function duplicateBom(part_id, options={}) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
include_inherited: {},
|
include_inherited: {},
|
||||||
|
copy_substitutes: {},
|
||||||
remove_existing: {},
|
remove_existing: {},
|
||||||
skip_invalid: {},
|
skip_invalid: {},
|
||||||
},
|
},
|
||||||
|
@ -278,7 +278,7 @@ $.fn.inventreeTable = function(options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log(`Could not get list of visible columns for column '${tableName}'`);
|
console.log(`Could not get list of visible columns for table '${tableName}'`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,16 +46,16 @@
|
|||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{% if roles.stock.change %}
|
{% if roles.stock.change %}
|
||||||
<li><a class='dropdown-item' href="#" id='multi-item-add' title='{% trans "Add to selected stock items" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
|
<li><a class='dropdown-item' href="#" id='multi-item-add' title='{% trans "Add to selected stock items" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
|
||||||
<li><a class='dropdown-item' href="#" id='multi-item-remove' title='{% trans "Remove from selected stock items" %}'><span class='fas fa-minus-circle'></span> {% trans "Remove stock" %}</a></li>
|
<li><a class='dropdown-item' href="#" id='multi-item-remove' title='{% trans "Remove from selected stock items" %}'><span class='fas fa-minus-circle icon-red'></span> {% trans "Remove stock" %}</a></li>
|
||||||
<li><a class='dropdown-item' href="#" id='multi-item-stocktake' title='{% trans "Stocktake selected stock items" %}'><span class='fas fa-check-circle'></span> {% trans "Count stock" %}</a></li>
|
<li><a class='dropdown-item' href="#" id='multi-item-stocktake' title='{% trans "Stocktake selected stock items" %}'><span class='fas fa-check-circle icon-green'></span> {% trans "Count stock" %}</a></li>
|
||||||
<li><a class='dropdown-item' href='#' id='multi-item-move' title='{% trans "Move selected stock items" %}'><span class='fas fa-exchange-alt'></span> {% trans "Move stock" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-item-move' title='{% trans "Move selected stock items" %}'><span class='fas fa-exchange-alt icon-blue'></span> {% trans "Transfer stock" %}</a></li>
|
||||||
<li><a class='dropdown-item' href='#' id='multi-item-merge' title='{% trans "Merge selected stock items" %}'><span class='fas fa-object-group'></span> {% trans "Merge stock" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-item-merge' title='{% trans "Merge selected stock items" %}'><span class='fas fa-object-group'></span> {% trans "Merge stock" %}</a></li>
|
||||||
<li><a class='dropdown-item' href='#' id='multi-item-order' title='{% trans "Order selected items" %}'><span class='fas fa-shopping-cart'></span> {% trans "Order stock" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-item-order' title='{% trans "Order selected items" %}'><span class='fas fa-shopping-cart'></span> {% trans "Order stock" %}</a></li>
|
||||||
<li><a class='dropdown-item' href='#' id='multi-item-assign' title='{% trans "Assign to customer" %}'><span class='fas fa-user-tie'></span> {% trans "Assign to customer" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-item-assign' title='{% trans "Assign to customer" %}'><span class='fas fa-user-tie'></span> {% trans "Assign to customer" %}</a></li>
|
||||||
<li><a class='dropdown-item' href='#' id='multi-item-set-status' title='{% trans "Change status" %}'><span class='fas fa-exclamation-circle'></span> {% trans "Change stock status" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-item-set-status' title='{% trans "Change status" %}'><span class='fas fa-exclamation-circle'></span> {% trans "Change stock status" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if roles.stock.delete %}
|
{% if roles.stock.delete %}
|
||||||
<li><a class='dropdown-item' href='#' id='multi-item-delete' title='{% trans "Delete selected items" %}'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete Stock" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='multi-item-delete' title='{% trans "Delete selected items" %}'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete stock" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user