Delete a build output entirely

TODO: Needs to describe in the confirmation dialog what is going to happen!
This commit is contained in:
Oliver Walters 2020-10-24 00:14:27 +11:00
parent a71a51b848
commit ea7b1b65d6
7 changed files with 130 additions and 12 deletions

View File

@ -78,7 +78,7 @@ function getImageUrlFromTransfer(transfer) {
return url; return url;
} }
function makeIconButton(icon, cls, pk, title) { function makeIconButton(icon, cls, pk, title, options={}) {
// Construct an 'icon button' using the fontawesome set // Construct an 'icon button' using the fontawesome set
var classes = `btn btn-default btn-glyph ${cls}`; var classes = `btn btn-default btn-glyph ${cls}`;
@ -87,7 +87,13 @@ function makeIconButton(icon, cls, pk, title) {
var html = ''; var html = '';
html += `<button pk='${pk}' id='${id}' class='${classes}' title='${title}'>`; var extraProps = '';
if (options.disabled) {
extraProps += "disabled='true' ";
}
html += `<button pk='${pk}' id='${id}' class='${classes}' title='${title}' ${extraProps}>`;
html += `<span class='fas ${icon}'></span>`; html += `<span class='fas ${icon}'></span>`;
html += `</button>`; html += `</button>`;

View File

@ -46,6 +46,29 @@ class EditBuildForm(HelperForm):
] ]
class BuildOutputDeleteForm(HelperForm):
"""
Form for deleting a build output.
"""
confirm = forms.BooleanField(
required=False,
help_text=_('Confirm deletion of build output')
)
output_id = forms.IntegerField(
required=True,
widget=forms.HiddenInput()
)
class Meta:
model = Build
fields = [
'confirm',
'output_id',
]
class UnallocateBuildForm(HelperForm): class UnallocateBuildForm(HelperForm):
""" """
Form for auto-de-allocation of stock from a build Form for auto-de-allocation of stock from a build

View File

@ -380,6 +380,29 @@ class Build(MPTTModel):
# Remove all the allocations # Remove all the allocations
allocations.delete() allocations.delete()
def deleteBuildOutput(self, output):
"""
Remove a build output from the database:
- Unallocate any build items against the output
- Delete the output StockItem
"""
if not output:
raise ValidationError(_("No build output specified"))
if not output.is_building:
raise ValidationError(_("Build output is already completed"))
if not output.build == self:
raise ValidationError(_("Build output does not match Build Order"))
# Unallocate all build items against the output
self.unallocateStock(output)
# Remove the build output from the database
output.delete()
@transaction.atomic @transaction.atomic
def autoAllocate(self): def autoAllocate(self):
""" Run auto-allocation routine to allocate StockItems to this Build. """ Run auto-allocation routine to allocate StockItems to this Build.

View File

@ -11,6 +11,7 @@ build_detail_urls = [
url(r'^allocate/', views.BuildAllocate.as_view(), name='build-allocate'), url(r'^allocate/', views.BuildAllocate.as_view(), name='build-allocate'),
url(r'^cancel/', views.BuildCancel.as_view(), name='build-cancel'), url(r'^cancel/', views.BuildCancel.as_view(), name='build-cancel'),
url(r'^delete/', views.BuildDelete.as_view(), name='build-delete'), url(r'^delete/', views.BuildDelete.as_view(), name='build-delete'),
url(r'^delete-output/', views.BuildOutputDelete.as_view(), name='build-output-delete'),
url(r'^complete/?', views.BuildComplete.as_view(), name='build-complete'), url(r'^complete/?', views.BuildComplete.as_view(), name='build-complete'),
url(r'^auto-allocate/?', views.BuildAutoAllocate.as_view(), name='build-auto-allocate'), url(r'^auto-allocate/?', views.BuildAutoAllocate.as_view(), name='build-auto-allocate'),
url(r'^unallocate/', views.BuildUnallocate.as_view(), name='build-unallocate'), url(r'^unallocate/', views.BuildUnallocate.as_view(), name='build-unallocate'),

View File

@ -141,6 +141,56 @@ class BuildAutoAllocate(AjaxUpdateView):
return self.renderJsonResponse(request, form, data, context=self.get_context_data()) return self.renderJsonResponse(request, form, data, context=self.get_context_data())
class BuildOutputDelete(AjaxUpdateView):
"""
Delete a build output (StockItem) for a given build.
Form is a simple confirmation dialog
"""
model = Build
form_class = forms.BuildOutputDeleteForm
ajax_form_title = _('Delete build output')
role_required = 'build.delete'
def get_initial(self):
initials = super().get_initial()
output = self.get_param('output')
initials['output_id'] = output
return initials
def post(self, request, *args, **kwargs):
build = self.get_object()
form = self.get_form()
confirm = request.POST.get('confirm', False)
output_id = request.POST.get('output_id', None)
try:
output = StockItem.objects.get(pk=output_id)
except (ValueError, StockItem.DoesNotExist):
output = None
valid = False
if output:
build.deleteBuildOutput(output)
valid = True
else:
form.non_field_errors = [_('Build or output not specified')]
data = {
'form_valid': valid,
}
return self.renderJsonResponse(request, form, data)
class BuildUnallocate(AjaxUpdateView): class BuildUnallocate(AjaxUpdateView):
""" View to un-allocate all parts from a build. """ View to un-allocate all parts from a build.
@ -151,7 +201,7 @@ class BuildUnallocate(AjaxUpdateView):
form_class = forms.UnallocateBuildForm form_class = forms.UnallocateBuildForm
ajax_form_title = _("Unallocate Stock") ajax_form_title = _("Unallocate Stock")
ajax_template_name = "build/unallocate.html" ajax_template_name = "build/unallocate.html"
form_required = 'build.change' role_required = 'build.change'
def get_initial(self): def get_initial(self):
@ -161,13 +211,7 @@ class BuildUnallocate(AjaxUpdateView):
output = self.get_param('output') output = self.get_param('output')
if output: if output:
try: initials['output_id'] = output
output = StockItem.objects.get(pk=output)
except (ValueError, StockItem.DoesNotExist):
output = None
if output:
initials['output_id'] = output.pk
return initials return initials

View File

@ -954,7 +954,8 @@ class Part(MPTTModel):
return parts return parts
def get_allowed_bom_items(self): def get_allowed_bom_items(self):
""" Return a list of parts which can be added to a BOM for this part. """
Return a list of parts which can be added to a BOM for this part.
- Exclude parts which are not 'component' parts - Exclude parts which are not 'component' parts
- Exclude parts which this part is in the BOM for - Exclude parts which this part is in the BOM for

View File

@ -52,13 +52,19 @@ function makeBuildOutputActionButtons(output, buildId) {
// Add a button to "auto allocate" against the particular build output // Add a button to "auto allocate" against the particular build output
html += makeIconButton( html += makeIconButton(
'fa-clipboard-check icon-blue', 'button-output-auto', outputId, 'fa-clipboard-check icon-blue', 'button-output-auto', outputId,
'{% trans "Allocate stock items to this output" %}' '{% trans "Allocate stock items to this output" %}',
{
disabled: true,
}
); );
if (output.quantity > 1) { if (output.quantity > 1) {
html += makeIconButton( html += makeIconButton(
'fa-random icon-blue', 'button-output-split', outputId, 'fa-random icon-blue', 'button-output-split', outputId,
'{% trans "Split build output into separate items" %}', '{% trans "Split build output into separate items" %}',
{
disabled: true
}
); );
} }
@ -66,6 +72,9 @@ function makeBuildOutputActionButtons(output, buildId) {
html += makeIconButton( html += makeIconButton(
'fa-tools icon-green', 'button-output-complete', outputId, 'fa-tools icon-green', 'button-output-complete', outputId,
'{% trans "Complete build output" %}', '{% trans "Complete build output" %}',
{
disabled: true
}
); );
// Add a button to "cancel" the particular build output (unallocate) // Add a button to "cancel" the particular build output (unallocate)
@ -116,6 +125,17 @@ function makeBuildOutputActionButtons(output, buildId) {
); );
}); });
$(panel).find(`#button-output-delete-${outputId}`).click(function() {
launchModalForm(
`/build/${buildId}/delete-output/`,
{
reload: true,
data: {
output: outputId
}
}
);
});
} }