Prune a lot of dead code

This commit is contained in:
Oliver Walters 2022-05-04 17:00:37 +10:00
parent bd3d6f47a1
commit 5cf30a850d
4 changed files with 0 additions and 1045 deletions

View File

@ -95,24 +95,6 @@ class EditPartParameterTemplateForm(HelperForm):
]
class EditCategoryForm(HelperForm):
""" Form for editing a PartCategory object """
field_prefix = {
'default_keywords': 'fa-key',
}
class Meta:
model = PartCategory
fields = [
'parent',
'name',
'description',
'default_location',
'default_keywords',
]
class EditCategoryParameterTemplateForm(HelperForm):
""" Form for editing a PartCategoryParameterTemplate object """

View File

@ -1001,45 +1001,6 @@ class CategoryDetail(InvenTreeRoleMixin, DetailView):
return context
class CategoryEdit(AjaxUpdateView):
"""
Update view to edit a PartCategory
"""
model = PartCategory
form_class = part_forms.EditCategoryForm
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Part Category')
def get_context_data(self, **kwargs):
context = super(CategoryEdit, self).get_context_data(**kwargs).copy()
try:
context['category'] = self.get_object()
except:
pass
return context
def get_form(self):
""" Customize form data for PartCategory editing.
Limit the choices for 'parent' field to those which make sense
"""
form = super(AjaxUpdateView, self).get_form()
category = self.get_object()
# Remove any invalid choices for the parent category part
parent_choices = PartCategory.objects.all()
parent_choices = parent_choices.exclude(id__in=category.getUniqueChildren())
form.fields['parent'].queryset = parent_choices
return form
class CategoryDelete(AjaxDeleteView):
"""
Delete view to delete a PartCategory

View File

@ -32,23 +32,6 @@ class ReturnStockItemForm(HelperForm):
]
class EditStockLocationForm(HelperForm):
"""
Form for editing a StockLocation
TODO: Migrate this form to the modern API forms interface
"""
class Meta:
model = StockLocation
fields = [
'name',
'parent',
'description',
'owner',
]
class ConvertStockItemForm(HelperForm):
"""
Form for converting a StockItem to a variant of its current part.
@ -63,159 +46,6 @@ class ConvertStockItemForm(HelperForm):
]
class CreateStockItemForm(HelperForm):
"""
Form for creating a new StockItem
TODO: Migrate this form to the modern API forms interface
"""
expiry_date = DatePickerFormField(
label=_('Expiry Date'),
help_text=_('Expiration date for this stock item'),
)
serial_numbers = forms.CharField(label=_('Serial Numbers'), required=False, help_text=_('Enter unique serial numbers (or leave blank)'))
def __init__(self, *args, **kwargs):
self.field_prefix = {
'serial_numbers': 'fa-hashtag',
'link': 'fa-link',
}
super().__init__(*args, **kwargs)
class Meta:
model = StockItem
fields = [
'part',
'supplier_part',
'location',
'quantity',
'batch',
'serial_numbers',
'packaging',
'purchase_price',
'expiry_date',
'link',
'delete_on_deplete',
'status',
'owner',
]
# Custom clean to prevent complex StockItem.clean() logic from running (yet)
def full_clean(self):
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
# Don't run _post_clean() as this will run StockItem.clean()
self._clean_fields()
self._clean_form()
class SerializeStockForm(HelperForm):
"""
Form for serializing a StockItem.
TODO: Migrate this form to the modern API forms interface
"""
destination = TreeNodeChoiceField(queryset=StockLocation.objects.all(), label=_('Destination'), required=True, help_text=_('Destination for serialized stock (by default, will remain in current location)'))
serial_numbers = forms.CharField(label=_('Serial numbers'), required=True, help_text=_('Unique serial numbers (must match quantity)'))
note = forms.CharField(label=_('Notes'), required=False, help_text=_('Add transaction note (optional)'))
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
def __init__(self, *args, **kwargs):
# Extract the stock item
item = kwargs.pop('item', None)
if item:
self.field_placeholder['serial_numbers'] = item.part.getSerialNumberString(item.quantity)
super().__init__(*args, **kwargs)
class Meta:
model = StockItem
fields = [
'quantity',
'serial_numbers',
'destination',
'note',
]
class UninstallStockForm(forms.ModelForm):
"""
Form for uninstalling a stock item which is installed in another item.
TODO: Migrate this form to the modern API forms interface
"""
location = TreeNodeChoiceField(queryset=StockLocation.objects.all(), label=_('Location'), help_text=_('Destination location for uninstalled items'))
note = forms.CharField(label=_('Notes'), required=False, help_text=_('Add transaction note (optional)'))
confirm = forms.BooleanField(required=False, initial=False, label=_('Confirm uninstall'), help_text=_('Confirm removal of installed stock items'))
class Meta:
model = StockItem
fields = [
'location',
'note',
'confirm',
]
class EditStockItemForm(HelperForm):
""" Form for editing a StockItem object.
Note that not all fields can be edited here (even if they can be specified during creation.
location - Must be updated in a 'move' transaction
quantity - Must be updated in a 'stocktake' transaction
part - Cannot be edited after creation
TODO: Migrate this form to the modern API forms interface
"""
expiry_date = DatePickerFormField(
label=_('Expiry Date'),
help_text=_('Expiration date for this stock item'),
)
class Meta:
model = StockItem
fields = [
'supplier_part',
'serial',
'batch',
'status',
'expiry_date',
'purchase_price',
'packaging',
'link',
'delete_on_deplete',
'owner',
]
class TrackingEntryForm(HelperForm):
"""
Form for creating / editing a StockItemTracking object.

View File

@ -19,7 +19,6 @@ from django.utils.translation import gettext_lazy as _
from moneyed import CURRENCIES
from InvenTree.views import AjaxView
from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView
from InvenTree.views import QRCodeView
from InvenTree.views import InvenTreeRoleMixin
@ -135,139 +134,6 @@ class StockItemDetail(InvenTreeRoleMixin, DetailView):
return super().get(request, *args, **kwargs)
class StockLocationEdit(AjaxUpdateView):
"""
View for editing details of a StockLocation.
This view is used with the EditStockLocationForm to deliver a modal form to the web view
TODO: Remove this code as location editing has been migrated to the API forms
- Have to still validate that all form functionality (as below) as been ported
"""
model = StockLocation
form_class = StockForms.EditStockLocationForm
context_object_name = 'location'
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Stock Location')
def get_form(self):
""" Customize form data for StockLocation editing.
Limit the choices for 'parent' field to those which make sense.
If ownership control is enabled and location has parent, disable owner field.
"""
form = super(AjaxUpdateView, self).get_form()
location = self.get_object()
# Remove any invalid choices for the 'parent' field
parent_choices = StockLocation.objects.all()
parent_choices = parent_choices.exclude(id__in=location.getUniqueChildren())
form.fields['parent'].queryset = parent_choices
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if not stock_ownership_control:
# Hide owner field
form.fields['owner'].widget = HiddenInput()
else:
# Get location's owner
location_owner = location.owner
if location_owner:
if location.parent:
try:
# If location has parent and owner: automatically select parent's owner
parent_owner = location.parent.owner
form.fields['owner'].initial = parent_owner
except AttributeError:
pass
else:
# If current owner exists: automatically select it
form.fields['owner'].initial = location_owner
# Update queryset or disable field (only if not admin)
if not self.request.user.is_superuser:
if type(location_owner.owner) is Group:
user_as_owner = Owner.get_owner(self.request.user)
queryset = location_owner.get_related_owners(include_group=True)
if user_as_owner not in queryset:
# Only owners or admin can change current owner
form.fields['owner'].disabled = True
else:
form.fields['owner'].queryset = queryset
return form
def save(self, object, form, **kwargs):
""" If location has children and ownership control is enabled:
- update owner of all children location of this location
- update owner for all stock items at this location
"""
self.object = form.save()
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if stock_ownership_control and self.object.owner:
# Get authorized users
authorized_owners = self.object.owner.get_related_owners()
# Update children locations
children_locations = self.object.get_children()
for child in children_locations:
# Check if current owner is subset of new owner
if child.owner and authorized_owners:
if child.owner in authorized_owners:
continue
child.owner = self.object.owner
child.save()
# Update stock items
stock_items = self.object.get_stock_items()
for stock_item in stock_items:
# Check if current owner is subset of new owner
if stock_item.owner and authorized_owners:
if stock_item.owner in authorized_owners:
continue
stock_item.owner = self.object.owner
stock_item.save()
return self.object
def validate(self, item, form):
""" Check that owner is set if stock ownership control is enabled """
parent = form.cleaned_data.get('parent', None)
owner = form.cleaned_data.get('owner', None)
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if stock_ownership_control:
if not owner and not self.request.user.is_superuser:
form.add_error('owner', _('Owner is required (ownership control is enabled)'))
else:
try:
if parent.owner:
if parent.owner != owner:
error = f'Owner requires to be equivalent to parent\'s owner ({parent.owner})'
form.add_error('owner', error)
except AttributeError:
# No parent
pass
class StockLocationQRCode(QRCodeView):
""" View for displaying a QR code for a StockLocation object """
@ -366,261 +232,6 @@ class StockItemQRCode(QRCodeView):
return None
class StockItemUninstall(AjaxView, FormMixin):
"""
View for uninstalling one or more StockItems,
which are installed in another stock item.
Stock items are uninstalled into a location,
defaulting to the location that they were "in" before they were installed.
If multiple default locations are detected,
leave the final location up to the user.
"""
ajax_template_name = 'stock/stock_uninstall.html'
ajax_form_title = _('Uninstall Stock Items')
form_class = StockForms.UninstallStockForm
role_required = 'stock.change'
# List of stock items to uninstall (initially empty)
stock_items = []
def get_stock_items(self):
return self.stock_items
def get_initial(self):
initials = super().get_initial().copy()
# Keep track of the current locations of stock items
current_locations = set()
# Keep track of the default locations for stock items
default_locations = set()
for item in self.stock_items:
if item.location:
current_locations.add(item.location)
if item.part.default_location:
default_locations.add(item.part.default_location)
if len(current_locations) == 1:
# If the selected stock items are currently in a single location,
# select that location as the destination.
initials['location'] = next(iter(current_locations))
elif len(current_locations) == 0:
# There are no current locations set
if len(default_locations) == 1:
# Select the single default location
initials['location'] = next(iter(default_locations))
return initials
def get(self, request, *args, **kwargs):
""" Extract list of stock items, which are supplied as a list,
e.g. items[]=1,2,3
"""
if 'items[]' in request.GET:
self.stock_items = StockItem.objects.filter(id__in=request.GET.getlist('items[]'))
else:
self.stock_items = []
return self.renderJsonResponse(request, self.get_form())
def post(self, request, *args, **kwargs):
"""
Extract a list of stock items which are included as hidden inputs in the form data.
"""
items = []
for item in self.request.POST:
if item.startswith('stock-item-'):
pk = item.replace('stock-item-', '')
try:
stock_item = StockItem.objects.get(pk=pk)
items.append(stock_item)
except (ValueError, StockItem.DoesNotExist):
pass
self.stock_items = items
# Assume the form is valid, until it isn't!
valid = True
confirmed = str2bool(request.POST.get('confirm'))
note = request.POST.get('note', '')
location = request.POST.get('location', None)
if location:
try:
location = StockLocation.objects.get(pk=location)
except (ValueError, StockLocation.DoesNotExist):
location = None
if not location:
# Location is required!
valid = False
form = self.get_form()
if not confirmed:
valid = False
form.add_error('confirm', _('Confirm stock adjustment'))
data = {
'form_valid': valid,
}
if valid:
# Ok, now let's actually uninstall the stock items
for item in self.stock_items:
item.uninstallIntoLocation(location, request.user, note)
data['success'] = _('Uninstalled stock items')
return self.renderJsonResponse(request, form=form, data=data)
def get_context_data(self):
context = super().get_context_data()
context['stock_items'] = self.get_stock_items()
return context
class StockItemEdit(AjaxUpdateView):
"""
View for editing details of a single StockItem
"""
model = StockItem
form_class = StockForms.EditStockItemForm
context_object_name = 'item'
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Stock Item')
def get_form(self):
""" Get form for StockItem editing.
Limit the choices for supplier_part
"""
form = super(AjaxUpdateView, self).get_form()
# Hide the "expiry date" field if the feature is not enabled
if not common.settings.stock_expiry_enabled():
form.fields['expiry_date'].widget = HiddenInput()
item = self.get_object()
# If the part cannot be purchased, hide the supplier_part field
if not item.part.purchaseable:
form.fields['supplier_part'].widget = HiddenInput()
form.fields.pop('purchase_price')
else:
query = form.fields['supplier_part'].queryset
query = query.filter(part=item.part.id)
form.fields['supplier_part'].queryset = query
# Hide the serial number field if it is not required
if not item.part.trackable and not item.serialized:
form.fields['serial'].widget = HiddenInput()
location = item.location
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if not stock_ownership_control:
form.fields['owner'].widget = HiddenInput()
else:
try:
location_owner = location.owner
except AttributeError:
location_owner = None
# Check if location has owner
if location_owner:
form.fields['owner'].initial = location_owner
# Check location's owner type and filter potential owners
if type(location_owner.owner) is Group:
user_as_owner = Owner.get_owner(self.request.user)
queryset = location_owner.get_related_owners(include_group=True)
if user_as_owner in queryset:
form.fields['owner'].initial = user_as_owner
form.fields['owner'].queryset = queryset
elif type(location_owner.owner) is get_user_model():
# If location's owner is a user: automatically set owner field and disable it
form.fields['owner'].disabled = True
form.fields['owner'].initial = location_owner
try:
item_owner = item.owner
except AttributeError:
item_owner = None
# Check if item has owner
if item_owner:
form.fields['owner'].initial = item_owner
# Check item's owner type and filter potential owners
if type(item_owner.owner) is Group:
user_as_owner = Owner.get_owner(self.request.user)
queryset = item_owner.get_related_owners(include_group=True)
if user_as_owner in queryset:
form.fields['owner'].initial = user_as_owner
form.fields['owner'].queryset = queryset
elif type(item_owner.owner) is get_user_model():
# If item's owner is a user: automatically set owner field and disable it
form.fields['owner'].disabled = True
form.fields['owner'].initial = item_owner
return form
def validate(self, item, form):
""" Check that owner is set if stock ownership control is enabled """
owner = form.cleaned_data.get('owner', None)
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if stock_ownership_control:
if not owner and not self.request.user.is_superuser:
form.add_error('owner', _('Owner is required (ownership control is enabled)'))
def save(self, object, form, **kwargs):
"""
Override the save method, to track the user who updated the model
"""
item = form.save(commit=False)
item.save(user=self.request.user)
return item
class StockItemConvert(AjaxUpdateView):
"""
View for 'converting' a StockItem to a variant of its current part.
@ -655,435 +266,6 @@ class StockItemConvert(AjaxUpdateView):
return stock_item
class StockLocationCreate(AjaxCreateView):
"""
View for creating a new StockLocation
A parent location (another StockLocation object) can be passed as a query parameter
TODO: Remove this class entirely, as it has been migrated to the API forms
- Still need to check that all the functionality (as below) has been implemented
"""
model = StockLocation
form_class = StockForms.EditStockLocationForm
context_object_name = 'location'
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Create new Stock Location')
def get_initial(self):
initials = super(StockLocationCreate, self).get_initial().copy()
loc_id = self.request.GET.get('location', None)
if loc_id:
try:
initials['parent'] = StockLocation.objects.get(pk=loc_id)
except StockLocation.DoesNotExist:
pass
return initials
def get_form(self):
""" Disable owner field when:
- creating child location
- and stock ownership control is enable
"""
form = super().get_form()
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if not stock_ownership_control:
# Hide owner field
form.fields['owner'].widget = HiddenInput()
else:
# If user did not selected owner: automatically match to parent's owner
if not form['owner'].data:
try:
parent_id = form['parent'].value()
parent = StockLocation.objects.get(pk=parent_id)
if parent:
form.fields['owner'].initial = parent.owner
if not self.request.user.is_superuser:
form.fields['owner'].disabled = True
except StockLocation.DoesNotExist:
pass
except ValueError:
pass
return form
def save(self, form):
""" If parent location exists then use it to set the owner """
self.object = form.save(commit=False)
parent = form.cleaned_data.get('parent', None)
if parent:
# Select parent's owner
self.object.owner = parent.owner
self.object.save()
return self.object
def validate(self, item, form):
""" Check that owner is set if stock ownership control is enabled """
parent = form.cleaned_data.get('parent', None)
owner = form.cleaned_data.get('owner', None)
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if stock_ownership_control:
if not owner and not self.request.user.is_superuser:
form.add_error('owner', _('Owner is required (ownership control is enabled)'))
else:
try:
if parent.owner:
if parent.owner != owner:
error = f'Owner requires to be equivalent to parent\'s owner ({parent.owner})'
form.add_error('owner', error)
except AttributeError:
# No parent
pass
class StockItemCreate(AjaxCreateView):
"""
View for creating a new StockItem
Parameters can be pre-filled by passing query items:
- part: The part of which the new StockItem is an instance
- location: The location of the new StockItem
If the parent part is a "tracked" part, provide an option to create uniquely serialized items
rather than a bulk quantity of stock items
"""
model = StockItem
form_class = StockForms.CreateStockItemForm
context_object_name = 'item'
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Create new Stock Item')
def get_part(self, form=None):
"""
Attempt to get the "part" associted with this new stockitem.
- May be passed to the form as a query parameter (e.g. ?part=<id>)
- May be passed via the form field itself.
"""
# Try to extract from the URL query
part_id = self.request.GET.get('part', None)
if part_id:
try:
part = Part.objects.get(pk=part_id)
return part
except (Part.DoesNotExist, ValueError):
pass
# Try to get from the form
if form:
try:
part_id = form['part'].value()
part = Part.objects.get(pk=part_id)
return part
except (Part.DoesNotExist, ValueError):
pass
# Could not extract a part object
return None
def get_form(self):
""" Get form for StockItem creation.
Overrides the default get_form() method to intelligently limit
ForeignKey choices based on other selections
"""
form = super().get_form()
# Hide the "expiry date" field if the feature is not enabled
if not common.settings.stock_expiry_enabled():
form.fields['expiry_date'].widget = HiddenInput()
part = self.get_part(form=form)
if part is not None:
# Add placeholder text for the serial number field
form.field_placeholder['serial_numbers'] = part.getSerialNumberString()
form.rebuild_layout()
if not part.purchaseable:
form.fields.pop('purchase_price')
# Hide the 'part' field (as a valid part is selected)
# form.fields['part'].widget = HiddenInput()
# Trackable parts get special consideration:
if part.trackable:
form.fields['delete_on_deplete'].disabled = True
else:
form.fields['serial_numbers'].disabled = True
# If the part is NOT purchaseable, hide the supplier_part field
if not part.purchaseable:
form.fields['supplier_part'].widget = HiddenInput()
else:
# Pre-select the allowable SupplierPart options
parts = form.fields['supplier_part'].queryset
parts = parts.filter(part=part.id)
form.fields['supplier_part'].queryset = parts
# If there is one (and only one) supplier part available, pre-select it
all_parts = parts.all()
if len(all_parts) == 1:
# TODO - This does NOT work for some reason? Ref build.views.BuildItemCreate
form.fields['supplier_part'].initial = all_parts[0].id
else:
# No Part has been selected!
# We must not provide *any* options for SupplierPart
form.fields['supplier_part'].queryset = SupplierPart.objects.none()
form.fields['serial_numbers'].disabled = True
# Otherwise if the user has selected a SupplierPart, we know what Part they meant!
if form['supplier_part'].value() is not None:
pass
location = None
try:
loc_id = form['location'].value()
location = StockLocation.objects.get(pk=loc_id)
except StockLocation.DoesNotExist:
pass
except ValueError:
pass
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if not stock_ownership_control:
form.fields['owner'].widget = HiddenInput()
else:
try:
location_owner = location.owner
except AttributeError:
location_owner = None
if location_owner:
# Check location's owner type and filter potential owners
if type(location_owner.owner) is Group:
user_as_owner = Owner.get_owner(self.request.user)
queryset = location_owner.get_related_owners()
if user_as_owner in queryset:
form.fields['owner'].initial = user_as_owner
form.fields['owner'].queryset = queryset
elif type(location_owner.owner) is get_user_model():
# If location's owner is a user: automatically set owner field and disable it
form.fields['owner'].disabled = True
form.fields['owner'].initial = location_owner
return form
def get_initial(self):
""" Provide initial data to create a new StockItem object
"""
# Is the client attempting to copy an existing stock item?
item_to_copy = self.request.GET.get('copy', None)
if item_to_copy:
try:
original = StockItem.objects.get(pk=item_to_copy)
initials = model_to_dict(original)
self.ajax_form_title = _("Duplicate Stock Item")
except StockItem.DoesNotExist:
initials = super(StockItemCreate, self).get_initial().copy()
else:
initials = super(StockItemCreate, self).get_initial().copy()
part = self.get_part()
loc_id = self.request.GET.get('location', None)
sup_part_id = self.request.GET.get('supplier_part', None)
location = None
supplier_part = None
if part is not None:
initials['part'] = part
initials['location'] = part.get_default_location()
initials['supplier_part'] = part.default_supplier
# If the part has a defined expiry period, extrapolate!
if part.default_expiry > 0:
expiry_date = datetime.now().date() + timedelta(days=part.default_expiry)
initials['expiry_date'] = expiry_date
currency_code = common.settings.currency_code_default()
# SupplierPart field has been specified
# It must match the Part, if that has been supplied
if sup_part_id:
try:
supplier_part = SupplierPart.objects.get(pk=sup_part_id)
if part is None or supplier_part.part == part:
initials['supplier_part'] = supplier_part
currency_code = supplier_part.supplier.currency_code
except (ValueError, SupplierPart.DoesNotExist):
pass
# Location has been specified
if loc_id:
try:
location = StockLocation.objects.get(pk=loc_id)
initials['location'] = location
except (ValueError, StockLocation.DoesNotExist):
pass
currency = CURRENCIES.get(currency_code, None)
if currency:
initials['purchase_price'] = (None, currency)
return initials
def validate(self, item, form):
"""
Extra form validation steps
"""
data = form.cleaned_data
part = data.get('part', None)
quantity = data.get('quantity', None)
owner = data.get('owner', None)
if not part:
return
if not quantity:
return
try:
quantity = Decimal(quantity)
except (ValueError, InvalidOperation):
form.add_error('quantity', _('Invalid quantity provided'))
return
if quantity < 0:
form.add_error('quantity', _('Quantity cannot be negative'))
# Trackable parts are treated differently
if part.trackable:
sn = data.get('serial_numbers', '')
sn = str(sn).strip()
if len(sn) > 0:
try:
serials = extract_serial_numbers(sn, quantity, part.getLatestSerialNumberInt())
except ValidationError as e:
serials = None
form.add_error('serial_numbers', e.messages)
if serials is not None:
existing = part.find_conflicting_serial_numbers(serials)
if len(existing) > 0:
exists = ','.join([str(x) for x in existing])
form.add_error(
'serial_numbers',
_('Serial numbers already exist') + ': ' + exists
)
# Is ownership control enabled?
stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')
if stock_ownership_control:
# Check if owner is set
if not owner and not self.request.user.is_superuser:
form.add_error('owner', _('Owner is required (ownership control is enabled)'))
return
def save(self, form, **kwargs):
"""
Create a new StockItem based on the provided form data.
"""
data = form.cleaned_data
part = data['part']
quantity = data['quantity']
if part.trackable:
sn = data.get('serial_numbers', '')
sn = str(sn).strip()
# Create a single stock item for each provided serial number
if len(sn) > 0:
serials = extract_serial_numbers(sn, quantity, part.getLatestSerialNumberInt())
for serial in serials:
item = StockItem(
part=part,
quantity=1,
serial=serial,
supplier_part=data.get('supplier_part', None),
location=data.get('location', None),
batch=data.get('batch', None),
delete_on_deplete=False,
status=data.get('status'),
link=data.get('link', ''),
)
item.save(user=self.request.user)
# Create a single StockItem of the specified quantity
else:
form._post_clean()
item = form.save(commit=False)
item.user = self.request.user
item.save(user=self.request.user)
return item
# Non-trackable part
else:
form._post_clean()
item = form.save(commit=False)
item.user = self.request.user
item.save(user=self.request.user)
return item
class StockLocationDelete(AjaxDeleteView):
"""
View to delete a StockLocation