Merge branch 'master' into scheduling

This commit is contained in:
Oliver 2022-03-03 09:15:08 +11:00
commit 954f0afb85
31 changed files with 24552 additions and 23313 deletions

View File

@ -383,9 +383,7 @@ class Build(MPTTModel, ReferenceIndexingMixin):
Returns the BOM items for the part referenced by this BuildOrder
"""
return self.part.bom_items.all().prefetch_related(
'sub_part'
)
return self.part.get_bom_items()
@property
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

View File

@ -1453,7 +1453,9 @@ class Part(MPTTModel):
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):
"""
@ -1906,6 +1908,9 @@ class Part(MPTTModel):
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
# Note: Inherited BOM Items will *not* be duplicated!!
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):
continue
# Obtain a list of direct substitute parts against this BomItem
substitutes = BomItemSubstitute.objects.filter(bom_item=bom_item)
# Construct a new BOM item
bom_item.part = self
bom_item.pk = None
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
def copy_parameters_from(self, other, **kwargs):

View File

@ -656,6 +656,9 @@ class PartCopyBOMSerializer(serializers.Serializer):
fields = [
'part',
'remove_existing',
'copy_substitutes',
'include_inherited',
'skip_invalid',
]
part = serializers.PrimaryKeyRelatedField(
@ -692,6 +695,12 @@ class PartCopyBOMSerializer(serializers.Serializer):
default=False,
)
copy_substitutes = serializers.BooleanField(
label=_('Copy Substitute Parts'),
help_text=_('Copy substitute parts when duplicate BOM items'),
default=True,
)
def save(self):
"""
Actually duplicate the BOM
@ -706,6 +715,7 @@ class PartCopyBOMSerializer(serializers.Serializer):
clear=data.get('remove_existing', True),
skip_invalid=data.get('skip_invalid', False),
include_inherited=data.get('include_inherited', False),
copy_substitutes=data.get('copy_substitutes', True),
)

View File

@ -137,7 +137,13 @@
<h4>{% trans "Sales Order Allocations" %}</h4>
</div>
<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>
@ -357,7 +363,12 @@
<h4>{% trans "Build Order Allocations" %}</h4>
</div>
<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>
@ -742,6 +753,7 @@
});
// Load the BOM table data in the pricing view
{% if part.has_bom and roles.sales_order.view %}
loadBomTable($("#bom-pricing-table"), {
editable: false,
bom_url: "{% url 'api-bom-list' %}",
@ -749,6 +761,7 @@
parent_id: {{ part.id }} ,
sub_part_detail: true,
});
{% endif %}
onPanelLoad("purchase-orders", function() {
loadPartPurchaseOrderTable(

View File

@ -59,13 +59,13 @@
<ul class='dropdown-menu'>
<li>
<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" %}
</a>
</li>
<li>
<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" %}
</a>
</li>

View File

@ -909,7 +909,6 @@ class StockItem(MPTTModel):
""" Can this stock item be deleted? It can NOT be deleted under the following circumstances:
- Has installed stock items
- Has a serial number and is tracked
- Is installed inside another StockItem
- It has been assigned to a SalesOrder
- It has been assigned to a BuildOrder
@ -918,9 +917,6 @@ class StockItem(MPTTModel):
if self.installed_item_count() > 0:
return False
if self.part.trackable and self.serial is not None:
return False
if self.sales_order is not None:
return False

View File

@ -66,7 +66,7 @@
<ul class='dropdown-menu' role='menu'>
{% if not item.serialized %}
{% 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 %}
{% 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>

View File

@ -671,9 +671,7 @@ function loadBomTable(table, options={}) {
// Do we show part pricing in the BOM table?
var show_pricing = global_settings.PART_SHOW_PRICE_IN_BOM;
if (!show_pricing) {
params.include_pricing = false;
}
params.include_pricing = show_pricing == true;
if (options.part_detail) {
params.part_detail = true;
@ -989,32 +987,40 @@ function loadBomTable(table, options={}) {
// Function to request BOM data for sub-items
// 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
return;
if (depth >= MAX_BOM_DEPTH) {
console.log(`Maximum BOM depth (${MAX_BOM_DEPTH}) reached!`);
return;
}
inventreeGet(
options.bom_url,
{
part: part_pk,
sub_part_detail: true,
include_pricing: show_pricing == true,
},
{
success: function(response) {
// Add the returned sub-items to the table
for (var idx = 0; idx < response.length; idx++) {
response[idx].parentId = bom_pk;
if (response[idx].sub_part_detail.assembly) {
requestSubItems(response[idx].pk, response[idx].sub_part);
}
}
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');
},
error: function(xhr) {
@ -1026,7 +1032,7 @@ function loadBomTable(table, options={}) {
}
table.inventreeTable({
treeEnable: !options.editable,
treeEnable: true,
rootParentId: parent_id,
idField: 'pk',
uniqueId: 'pk',
@ -1066,38 +1072,37 @@ function loadBomTable(table, options={}) {
url: options.bom_url,
onPostBody: function() {
if (!options.editable) {
table.treegrid({
treeColumn: 0,
onExpand: function() {
}
});
}
table.treegrid({
treeColumn: 1,
onExpand: function() {
}
});
table.treegrid('collapseAll');
},
onLoadSuccess: function() {
if (options.editable) {
table.bootstrapTable('uncheckAll');
} else {
}
var data = table.bootstrapTable('getData');
var data = table.bootstrapTable('getData');
for (var idx = 0; idx < data.length; idx++) {
var row = data[idx];
for (var idx = 0; idx < data.length; idx++) {
var row = data[idx];
// If a row already has a parent ID set, it's already been updated!
if (row.parentId) {
continue;
}
// If a row already has a parent ID set, it's already been updated!
if (row.parentId) {
continue;
}
// Set the parent ID of the top-level rows
row.parentId = parent_id;
// Set the parent ID of the top-level rows
row.parentId = parent_id;
table.bootstrapTable('updateRow', idx, row, true);
table.bootstrapTable('updateRow', idx, row, true);
if (row.sub_part_detail.assembly) {
requestSubItems(row.pk, row.sub_part);
}
if (row.sub_part_detail.assembly) {
requestSubItems(row.pk, row.sub_part);
}
}
},

View File

@ -475,6 +475,7 @@ function duplicateBom(part_id, options={}) {
}
},
include_inherited: {},
copy_substitutes: {},
remove_existing: {},
skip_invalid: {},
},

View File

@ -278,7 +278,7 @@ $.fn.inventreeTable = function(options) {
}
});
} 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}'`);
}
}

View File

@ -46,16 +46,16 @@
<ul class="dropdown-menu">
{% 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-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-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-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-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 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 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-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-set-status' title='{% trans "Change status" %}'><span class='fas fa-exclamation-circle'></span> {% trans "Change stock status" %}</a></li>
{% endif %}
{% 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 %}
</ul>
</div>