mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
Fix logic for completing builds
- Display better messages to the user, too
This commit is contained in:
parent
4420557863
commit
76e2b67f36
@ -485,6 +485,9 @@ class Build(MPTTModel):
|
|||||||
if self.completed < self.quantity:
|
if self.completed < self.quantity:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not self.areUntrackedPartsFullyAllocated():
|
||||||
|
return False
|
||||||
|
|
||||||
# No issues!
|
# No issues!
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -494,7 +497,7 @@ class Build(MPTTModel):
|
|||||||
Mark this build as complete
|
Mark this build as complete
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.can_complete:
|
if self.incomplete_count > 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.completion_date = datetime.now().date()
|
self.completion_date = datetime.now().date()
|
||||||
@ -502,6 +505,9 @@ class Build(MPTTModel):
|
|||||||
self.status = BuildStatus.COMPLETE
|
self.status = BuildStatus.COMPLETE
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
# Remove untracked allocated stock
|
||||||
|
self.subtractUntrackedStock(user)
|
||||||
|
|
||||||
# Ensure that there are no longer any BuildItem objects
|
# Ensure that there are no longer any BuildItem objects
|
||||||
# which point to thie Build Order
|
# which point to thie Build Order
|
||||||
self.allocated_stock.all().delete()
|
self.allocated_stock.all().delete()
|
||||||
@ -775,6 +781,24 @@ class Build(MPTTModel):
|
|||||||
|
|
||||||
build_item.save()
|
build_item.save()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def subtractUntrackedStock(self, user):
|
||||||
|
"""
|
||||||
|
Called when the Build is marked as "complete",
|
||||||
|
this function removes the allocated untracked items from stock.
|
||||||
|
"""
|
||||||
|
|
||||||
|
items = self.allocated_stock.filter(
|
||||||
|
stock_item__part__trackable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove stock
|
||||||
|
for item in items:
|
||||||
|
item.complete_allocation(user)
|
||||||
|
|
||||||
|
# Delete allocation
|
||||||
|
items.all().delete()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def completeBuildOutput(self, output, user, **kwargs):
|
def completeBuildOutput(self, output, user, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -790,9 +814,6 @@ class Build(MPTTModel):
|
|||||||
# List the allocated BuildItem objects for the given output
|
# List the allocated BuildItem objects for the given output
|
||||||
allocated_items = output.items_to_install.all()
|
allocated_items = output.items_to_install.all()
|
||||||
|
|
||||||
# Ensure that only "trackable" parts are installed in the particular build output
|
|
||||||
allocated_items = allocated_items.filter(part__trackable=True)
|
|
||||||
|
|
||||||
for build_item in allocated_items:
|
for build_item in allocated_items:
|
||||||
|
|
||||||
# TODO: This is VERY SLOW as each deletion from the database takes ~1 second to complete
|
# TODO: This is VERY SLOW as each deletion from the database takes ~1 second to complete
|
||||||
@ -915,6 +936,13 @@ class Build(MPTTModel):
|
|||||||
# All parts must be fully allocated!
|
# All parts must be fully allocated!
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def areUntrackedPartsFullyAllocated(self):
|
||||||
|
"""
|
||||||
|
Returns True if the un-tracked parts are fully allocated for this BuildOrder
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.isFullyAllocated(None)
|
||||||
|
|
||||||
def allocatedParts(self, output):
|
def allocatedParts(self, output):
|
||||||
"""
|
"""
|
||||||
Return a list of parts which have been fully allocated against a particular output
|
Return a list of parts which have been fully allocated against a particular output
|
||||||
|
@ -31,6 +31,15 @@
|
|||||||
</button>
|
</button>
|
||||||
-->
|
-->
|
||||||
</div>
|
</div>
|
||||||
|
{% if build.areUntrackedPartsFullyAllocated %}
|
||||||
|
<div class='alert alert-block alert-success'>
|
||||||
|
{% trans "Untracked stock has been fully allocated for this Build Order" %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<div class='alert alert-block alert-danger'>
|
||||||
|
{% trans "Untracked stock has not been fully allocated for this Build Order" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<table class='table table-striped table-condensed' id='allocation-table-untracked'></table>
|
<table class='table table-striped table-condensed' id='allocation-table-untracked'></table>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
{% inventree_title %} | {% trans "Build Order" %} - {{ build }}
|
{% inventree_title %} | {% trans "Build Order" %} - {{ build }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block pre_content %}
|
{% block header_pre_content %}
|
||||||
{% if build.sales_order %}
|
{% if build.sales_order %}
|
||||||
<div class='alert alert-block alert-info'>
|
<div class='alert alert-block alert-info'>
|
||||||
{% object_link 'so-detail' build.sales_order.id build.sales_order as link %}
|
{% object_link 'so-detail' build.sales_order.id build.sales_order as link %}
|
||||||
@ -24,6 +24,31 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header_post_content %}
|
||||||
|
{% if build.active %}
|
||||||
|
{% if build.can_complete %}
|
||||||
|
<div class='alert alert-block alert-success'>
|
||||||
|
{% trans "Build Order is ready to mark as completed" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if build.incomplete_count > 0 %}
|
||||||
|
<div class='alert alert-block alert-danger'>
|
||||||
|
{% trans "Build Order cannot be completed as outstanding outputs remain" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if build.completed < build.quantity %}
|
||||||
|
<div class='alert alert-block alert-warning'>
|
||||||
|
{% trans "Required build quantity has not yet been completed" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if not build.areUntrackedPartsFullyAllocated %}
|
||||||
|
<div class='alert alert-block alert-warning'>
|
||||||
|
{% trans "Stock has not been fully allocated to this Build Order" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block thumbnail %}
|
{% block thumbnail %}
|
||||||
<img class="part-thumb"
|
<img class="part-thumb"
|
||||||
{% if build.part.image %}
|
{% if build.part.image %}
|
||||||
@ -61,6 +86,11 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
</div>
|
</div>
|
||||||
<!-- Build actions -->
|
<!-- Build actions -->
|
||||||
{% if roles.build.change %}
|
{% if roles.build.change %}
|
||||||
|
{% if build.active %}
|
||||||
|
<button id='build-complete' title='{% trans "Complete Build" %}' class='btn btn-success'>
|
||||||
|
<span class='fas fa-paper-plane'></span>
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
<div class='btn-group'>
|
<div class='btn-group'>
|
||||||
<button id='build-options' title='{% trans "Build actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'>
|
<button id='build-options' title='{% trans "Build actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'>
|
||||||
<span class='fas fa-tools'></span> <span class='caret'></span>
|
<span class='fas fa-tools'></span> <span class='caret'></span>
|
||||||
@ -68,12 +98,11 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
<ul class='dropdown-menu' role='menu'>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
<li><a href='#' id='build-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit Build" %}</a></li>
|
<li><a href='#' id='build-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit Build" %}</a></li>
|
||||||
{% if build.is_active %}
|
{% if build.is_active %}
|
||||||
<li><a href='#' id='build-complete'><span class='fas fa-tools'></span> {% trans "Complete Build" %}</a></li>
|
|
||||||
<li><a href='#' id='build-cancel'><span class='fas fa-times-circle icon-red'></span> {% trans "Cancel Build" %}</a></li>
|
<li><a href='#' id='build-cancel'><span class='fas fa-times-circle icon-red'></span> {% trans "Cancel Build" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if build.status == BuildStatus.CANCELLED and roles.build.delete %}
|
{% if build.status == BuildStatus.CANCELLED and roles.build.delete %}
|
||||||
<li><a href='#' id='build-delete'><span class='fas fa-trash-alt'></span> {% trans "Delete Build"% }</a>
|
<li><a href='#' id='build-delete'><span class='fas fa-trash-alt'></span> {% trans "Delete Build"% }</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -172,6 +201,13 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#build-complete").on('click', function() {
|
$("#build-complete").on('click', function() {
|
||||||
|
|
||||||
|
{% if build.incomplete_count > 0 %}
|
||||||
|
showAlertDialog(
|
||||||
|
'{% trans "Incomplete Outputs" %}',
|
||||||
|
'{% trans "Build Order cannot be completed as incomplete build outputs remain" %}'
|
||||||
|
);
|
||||||
|
{% else %}
|
||||||
launchModalForm(
|
launchModalForm(
|
||||||
"{% url 'build-complete' build.id %}",
|
"{% url 'build-complete' build.id %}",
|
||||||
{
|
{
|
||||||
@ -179,6 +215,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
submit_text: '{% trans "Complete Build" %}',
|
submit_text: '{% trans "Complete Build" %}',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
{% endif %}
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#print-build-report').click(function() {
|
$('#print-build-report').click(function() {
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
|
|
||||||
{% if build.can_complete %}
|
{% if build.can_complete %}
|
||||||
<div class='alert alert-block alert-success'>
|
<div class='alert alert-block alert-success'>
|
||||||
{% trans "Build can be completed" %}
|
{% trans "Build Order is complete" %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class='alert alert-block alert-danger'>
|
<div class='alert alert-block alert-danger'>
|
||||||
<b>{% trans "Build cannot be completed" %}</b><br>
|
<b>{% trans "Build Order is incomplete" %}</b><br>
|
||||||
<ul>
|
<ul>
|
||||||
{% if build.incomplete_count > 0 %}
|
{% if build.incomplete_count > 0 %}
|
||||||
<li>{% trans "Incompleted build outputs remain" %}</li>
|
<li>{% trans "Incompleted build outputs remain" %}</li>
|
||||||
@ -17,6 +17,9 @@
|
|||||||
{% if build.completed < build.quantity %}
|
{% if build.completed < build.quantity %}
|
||||||
<li>{% trans "Required build quantity has not been completed" %}</li>
|
<li>{% trans "Required build quantity has not been completed" %}</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if not build.areUntrackedPartsFullyAllocated %}
|
||||||
|
<li>{% trans "Required stock has not been fully allocated" %}</li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -109,8 +109,6 @@ class BuildAutoAllocate(AjaxUpdateView):
|
|||||||
|
|
||||||
build = self.get_object()
|
build = self.get_object()
|
||||||
|
|
||||||
form = self.get_form()
|
|
||||||
|
|
||||||
context['allocations'] = build.getAutoAllocations()
|
context['allocations'] = build.getAutoAllocations()
|
||||||
|
|
||||||
context['build'] = build
|
context['build'] = build
|
||||||
@ -398,8 +396,8 @@ class BuildComplete(AjaxUpdateView):
|
|||||||
|
|
||||||
def validate(self, build, form, **kwargs):
|
def validate(self, build, form, **kwargs):
|
||||||
|
|
||||||
if not build.can_complete:
|
if build.incomplete_count > 0:
|
||||||
form.add_error(None, _('Build order cannot be completed'))
|
form.add_error(None, _('Build order cannot be completed - incomplete outputs remain'))
|
||||||
|
|
||||||
def save(self, build, form, **kwargs):
|
def save(self, build, form, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -9,8 +9,12 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
{% block header_panel %}
|
||||||
<div class='panel panel-default panel-inventree'>
|
<div class='panel panel-default panel-inventree'>
|
||||||
|
|
||||||
|
{% block header_pre_content %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='col-sm-6'>
|
<div class='col-sm-6'>
|
||||||
<div class='media-left'>
|
<div class='media-left'>
|
||||||
@ -30,7 +34,12 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% block header_post_content %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content_panels %}
|
{% block content_panels %}
|
||||||
<div class='panel panel-default panel-inventree'>
|
<div class='panel panel-default panel-inventree'>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user