mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #1902 from SchrodingersGat/bom-item-form
Use API forms for creating and editing BomItem objects
This commit is contained in:
commit
5aa111b0aa
@ -18,7 +18,6 @@ import common.models
|
||||
from common.forms import MatchItemForm
|
||||
|
||||
from .models import Part, PartCategory, PartRelated
|
||||
from .models import BomItem
|
||||
from .models import PartParameterTemplate, PartParameter
|
||||
from .models import PartCategoryParameterTemplate
|
||||
from .models import PartSellPriceBreak, PartInternalPriceBreak
|
||||
@ -317,33 +316,6 @@ class EditCategoryParameterTemplateForm(HelperForm):
|
||||
]
|
||||
|
||||
|
||||
class EditBomItemForm(HelperForm):
|
||||
""" Form for editing a BomItem object """
|
||||
|
||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
||||
|
||||
sub_part = PartModelChoiceField(queryset=Part.objects.all(), label=_('Sub part'))
|
||||
|
||||
class Meta:
|
||||
model = BomItem
|
||||
fields = [
|
||||
'part',
|
||||
'sub_part',
|
||||
'quantity',
|
||||
'reference',
|
||||
'overage',
|
||||
'note',
|
||||
'allow_variants',
|
||||
'inherited',
|
||||
'optional',
|
||||
]
|
||||
|
||||
# Prevent editing of the part associated with this BomItem
|
||||
widgets = {
|
||||
'part': forms.HiddenInput()
|
||||
}
|
||||
|
||||
|
||||
class PartPriceForm(forms.Form):
|
||||
""" Simple form for viewing part pricing information """
|
||||
|
||||
|
@ -440,22 +440,22 @@
|
||||
});
|
||||
|
||||
$("#bom-item-new").click(function () {
|
||||
launchModalForm(
|
||||
"{% url 'bom-item-create' %}?parent={{ part.id }}",
|
||||
{
|
||||
success: function() {
|
||||
$("#bom-table").bootstrapTable('refresh');
|
||||
},
|
||||
secondary: [
|
||||
{
|
||||
field: 'sub_part',
|
||||
label: '{% trans "New Part" %}',
|
||||
title: '{% trans "Create New Part" %}',
|
||||
url: "{% url 'part-create' %}",
|
||||
},
|
||||
]
|
||||
|
||||
var fields = bomItemFields();
|
||||
|
||||
fields.part.value = {{ part.pk }};
|
||||
fields.sub_part.filters = {
|
||||
active: true,
|
||||
};
|
||||
|
||||
constructForm('{% url "api-bom-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Create BOM Item" %}',
|
||||
onSuccess: function() {
|
||||
$('#bom-table').bootstrapTable('refresh');
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
{% else %}
|
||||
|
@ -259,22 +259,3 @@ class CategoryTest(PartViewTestCase):
|
||||
|
||||
response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class BomItemTests(PartViewTestCase):
|
||||
""" Tests for BomItem related views """
|
||||
|
||||
def test_create_valid_parent(self):
|
||||
""" Create a BomItem for a valid part """
|
||||
response = self.client.get(reverse('bom-item-create'), {'parent': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_create_no_parent(self):
|
||||
""" Create a BomItem without a parent """
|
||||
response = self.client.get(reverse('bom-item-create'), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_create_invalid_parent(self):
|
||||
""" Create a BomItem with an invalid parent """
|
||||
response = self.client.get(reverse('bom-item-create'), {'parent': 99999}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
@ -78,10 +78,6 @@ category_urls = [
|
||||
]))
|
||||
]
|
||||
|
||||
part_bom_urls = [
|
||||
url(r'^edit/?', views.BomItemEdit.as_view(), name='bom-item-edit'),
|
||||
]
|
||||
|
||||
# URL list for part web interface
|
||||
part_urls = [
|
||||
|
||||
@ -92,9 +88,6 @@ part_urls = [
|
||||
url(r'^import/', views.PartImport.as_view(), name='part-import'),
|
||||
url(r'^import-api/', views.PartImportAjax.as_view(), name='api-part-import'),
|
||||
|
||||
# Create a new BOM item
|
||||
url(r'^bom/new/?', views.BomItemCreate.as_view(), name='bom-item-create'),
|
||||
|
||||
# Download a BOM upload template
|
||||
url(r'^bom_template/?', views.BomUploadTemplate.as_view(), name='bom-upload-template'),
|
||||
|
||||
@ -122,9 +115,6 @@ part_urls = [
|
||||
# Change category for multiple parts
|
||||
url(r'^set-category/?', views.PartSetCategory.as_view(), name='part-set-category'),
|
||||
|
||||
# Bom Items
|
||||
url(r'^bom/(?P<pk>\d+)/', include(part_bom_urls)),
|
||||
|
||||
# Individual part using IPN as slug
|
||||
url(r'^(?P<slug>[-\w]+)/', views.PartDetailFromIPN.as_view(), name='part-detail-from-ipn'),
|
||||
|
||||
|
@ -2078,134 +2078,6 @@ class CategoryParameterTemplateDelete(AjaxDeleteView):
|
||||
return self.object
|
||||
|
||||
|
||||
class BomItemCreate(AjaxCreateView):
|
||||
"""
|
||||
Create view for making a new BomItem object
|
||||
"""
|
||||
|
||||
model = BomItem
|
||||
form_class = part_forms.EditBomItemForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Create BOM Item')
|
||||
|
||||
def get_form(self):
|
||||
""" Override get_form() method to reduce Part selection options.
|
||||
|
||||
- Do not allow part to be added to its own BOM
|
||||
- Remove any Part items that are already in the BOM
|
||||
"""
|
||||
|
||||
form = super(AjaxCreateView, self).get_form()
|
||||
|
||||
part_id = form['part'].value()
|
||||
|
||||
# Construct a queryset for the part field
|
||||
part_query = Part.objects.filter(active=True)
|
||||
|
||||
# Construct a queryset for the sub_part field
|
||||
sub_part_query = Part.objects.filter(
|
||||
component=True,
|
||||
active=True
|
||||
)
|
||||
|
||||
try:
|
||||
part = Part.objects.get(id=part_id)
|
||||
|
||||
# Hide the 'part' field
|
||||
form.fields['part'].widget = HiddenInput()
|
||||
|
||||
# Exclude the part from its own BOM
|
||||
sub_part_query = sub_part_query.exclude(id=part.id)
|
||||
|
||||
# Eliminate any options that are already in the BOM!
|
||||
sub_part_query = sub_part_query.exclude(id__in=[item.id for item in part.getRequiredParts()])
|
||||
|
||||
except (ValueError, Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
# Set the querysets for the fields
|
||||
form.fields['part'].queryset = part_query
|
||||
form.fields['sub_part'].queryset = sub_part_query
|
||||
|
||||
return form
|
||||
|
||||
def get_initial(self):
|
||||
""" Provide initial data for the BomItem:
|
||||
|
||||
- If 'parent' provided, set the parent part field
|
||||
"""
|
||||
|
||||
# Look for initial values
|
||||
initials = super(BomItemCreate, self).get_initial().copy()
|
||||
|
||||
# Parent part for this item?
|
||||
parent_id = self.request.GET.get('parent', None)
|
||||
|
||||
if parent_id:
|
||||
try:
|
||||
initials['part'] = Part.objects.get(pk=parent_id)
|
||||
except Part.DoesNotExist:
|
||||
pass
|
||||
|
||||
return initials
|
||||
|
||||
|
||||
class BomItemEdit(AjaxUpdateView):
|
||||
""" Update view for editing BomItem """
|
||||
|
||||
model = BomItem
|
||||
form_class = part_forms.EditBomItemForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Edit BOM item')
|
||||
|
||||
def get_form(self):
|
||||
""" Override get_form() method to filter part selection options
|
||||
|
||||
- Do not allow part to be added to its own BOM
|
||||
- Remove any part items that are already in the BOM
|
||||
"""
|
||||
|
||||
item = self.get_object()
|
||||
|
||||
form = super().get_form()
|
||||
|
||||
part_id = form['part'].value()
|
||||
|
||||
try:
|
||||
part = Part.objects.get(pk=part_id)
|
||||
|
||||
# Construct a queryset
|
||||
query = Part.objects.filter(component=True)
|
||||
|
||||
# Limit to "active" items, *unless* the currently selected item is not active
|
||||
if item.sub_part.active:
|
||||
query = query.filter(active=True)
|
||||
|
||||
# Prevent the parent part from being selected
|
||||
query = query.exclude(pk=part_id)
|
||||
|
||||
# Eliminate any options that are already in the BOM,
|
||||
# *except* for the item which is already selected
|
||||
try:
|
||||
sub_part_id = int(form['sub_part'].value())
|
||||
except ValueError:
|
||||
sub_part_id = -1
|
||||
|
||||
existing = [item.pk for item in part.getRequiredParts()]
|
||||
|
||||
if sub_part_id in existing:
|
||||
existing.remove(sub_part_id)
|
||||
|
||||
query = query.exclude(id__in=existing)
|
||||
|
||||
form.fields['sub_part'].queryset = query
|
||||
|
||||
except (ValueError, Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
return form
|
||||
|
||||
|
||||
class PartSalePriceBreakCreate(AjaxCreateView):
|
||||
"""
|
||||
View for creating a sale price break for a part
|
||||
|
@ -8,6 +8,26 @@
|
||||
*/
|
||||
|
||||
|
||||
function bomItemFields() {
|
||||
|
||||
return {
|
||||
part: {
|
||||
hidden: true,
|
||||
},
|
||||
sub_part: {
|
||||
},
|
||||
quantity: {},
|
||||
reference: {},
|
||||
overage: {},
|
||||
note: {},
|
||||
allow_variants: {},
|
||||
inherited: {},
|
||||
optional: {},
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
function reloadBomTable(table, options) {
|
||||
|
||||
table.bootstrapTable('refresh');
|
||||
@ -528,14 +548,15 @@ function loadBomTable(table, options) {
|
||||
var pk = $(this).attr('pk');
|
||||
var url = `/part/bom/${pk}/edit/`;
|
||||
|
||||
launchModalForm(
|
||||
url,
|
||||
{
|
||||
success: function() {
|
||||
reloadBomTable(table);
|
||||
}
|
||||
var fields = bomItemFields();
|
||||
|
||||
constructForm(`/api/bom/${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit BOM Item" %}',
|
||||
onSuccess: function() {
|
||||
reloadBomTable(table);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
table.on('click', '.bom-validate-button', function() {
|
||||
|
Loading…
Reference in New Issue
Block a user