mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Some small style fixes (#6916)
* fix dockerfile syntax * remove unused import * Merge unnecessary if statements * fix PUI package name * remove unused vars * Remove unneeded pass * merge if to reduce likelyhood of future errors * add ignroe script to secure against shell scripts * fix possible collisions * export strings * fix types
This commit is contained in:
parent
6be2ede5e8
commit
85e672831b
@ -101,7 +101,7 @@ RUN ./install_build_packages.sh --no-cache --virtual .build-deps && \
|
|||||||
# Frontend builder image:
|
# Frontend builder image:
|
||||||
FROM prebuild AS frontend
|
FROM prebuild AS frontend
|
||||||
|
|
||||||
RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22
|
RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 --ignore-scripts
|
||||||
RUN yarn config set network-timeout 600000 -g
|
RUN yarn config set network-timeout 600000 -g
|
||||||
COPY src ${INVENTREE_HOME}/src
|
COPY src ${INVENTREE_HOME}/src
|
||||||
COPY tasks.py ${INVENTREE_HOME}/tasks.py
|
COPY tasks.py ${INVENTREE_HOME}/tasks.py
|
||||||
@ -145,7 +145,7 @@ RUN pip install uv==0.1.26 --no-cache-dir && pip install -r base_requirements.tx
|
|||||||
|
|
||||||
# Install nodejs / npm / yarn
|
# Install nodejs / npm / yarn
|
||||||
|
|
||||||
RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22
|
RUN apk add --no-cache --update nodejs npm && npm install -g yarn@v1.22.22 --ignore-scripts
|
||||||
RUN yarn config set network-timeout 600000 -g
|
RUN yarn config set network-timeout 600000 -g
|
||||||
|
|
||||||
# The development image requires the source code to be mounted to /home/inventree/
|
# The development image requires the source code to be mounted to /home/inventree/
|
||||||
|
@ -125,8 +125,8 @@ def generate_index_file(version: str):
|
|||||||
f.write(output)
|
f.write(output)
|
||||||
|
|
||||||
|
|
||||||
def extract_refs(data: dict, components: dict) -> list:
|
def extract_refs(data: dict, components: dict) -> dict:
|
||||||
"""Extract a list of refs from the provided paths dict.
|
"""Extract a dict of refs from the provided paths dict.
|
||||||
|
|
||||||
The refs are located like so:
|
The refs are located like so:
|
||||||
<path>:<method>:responses:<status>:content:application/json:schema:$ref
|
<path>:<method>:responses:<status>:content:application/json:schema:$ref
|
||||||
|
@ -91,10 +91,13 @@ class InvenTreeResource(ModelResource):
|
|||||||
"""
|
"""
|
||||||
# We can automatically determine which fields might need such a conversion
|
# We can automatically determine which fields might need such a conversion
|
||||||
for field in self.Meta.model._meta.fields:
|
for field in self.Meta.model._meta.fields:
|
||||||
if isinstance(field, CharField):
|
if (
|
||||||
if field.blank and not field.null:
|
isinstance(field, CharField)
|
||||||
if field.name not in self.CONVERT_NULL_FIELDS:
|
and field.blank
|
||||||
self.CONVERT_NULL_FIELDS.append(field.name)
|
and not field.null
|
||||||
|
and field.name not in self.CONVERT_NULL_FIELDS
|
||||||
|
):
|
||||||
|
self.CONVERT_NULL_FIELDS.append(field.name)
|
||||||
|
|
||||||
for field in self.CONVERT_NULL_FIELDS:
|
for field in self.CONVERT_NULL_FIELDS:
|
||||||
if field in row and row[field] is None:
|
if field in row and row[field] is None:
|
||||||
|
@ -443,10 +443,10 @@ def get_frontend_settings(debug=True):
|
|||||||
if 'environment' not in settings:
|
if 'environment' not in settings:
|
||||||
settings['environment'] = 'development' if debug else 'production'
|
settings['environment'] = 'development' if debug else 'production'
|
||||||
|
|
||||||
if debug and 'show_server_selector' not in settings:
|
if (debug and 'show_server_selector' not in settings) or len(
|
||||||
|
settings['server_list']
|
||||||
|
) == 0:
|
||||||
# In debug mode, show server selector by default
|
# In debug mode, show server selector by default
|
||||||
settings['show_server_selector'] = True
|
|
||||||
elif len(settings['server_list']) == 0:
|
|
||||||
# If no servers are specified, show server selector
|
# If no servers are specified, show server selector
|
||||||
settings['show_server_selector'] = True
|
settings['show_server_selector'] = True
|
||||||
|
|
||||||
|
@ -38,10 +38,9 @@ class InvenTreeRestURLField(RestURLField):
|
|||||||
'INVENTREE_STRICT_URLS', True, cache=False
|
'INVENTREE_STRICT_URLS', True, cache=False
|
||||||
)
|
)
|
||||||
|
|
||||||
if not strict_urls and data is not empty:
|
if not strict_urls and data is not empty and '://' not in data:
|
||||||
if '://' not in data:
|
# Validate as if there were a schema provided
|
||||||
# Validate as if there were a schema provided
|
data = 'http://' + data
|
||||||
data = 'http://' + data
|
|
||||||
|
|
||||||
return super().run_validation(data=data)
|
return super().run_validation(data=data)
|
||||||
|
|
||||||
|
@ -458,9 +458,8 @@ class ReferenceIndexingMixin(models.Model):
|
|||||||
|
|
||||||
reference_int = InvenTree.helpers.extract_int(reference)
|
reference_int = InvenTree.helpers.extract_int(reference)
|
||||||
|
|
||||||
if validate:
|
if validate and reference_int > models.BigIntegerField.MAX_BIGINT:
|
||||||
if reference_int > models.BigIntegerField.MAX_BIGINT:
|
raise ValidationError({'reference': _('Reference number is too large')})
|
||||||
raise ValidationError({'reference': _('Reference number is too large')})
|
|
||||||
|
|
||||||
return reference_int
|
return reference_int
|
||||||
|
|
||||||
|
@ -603,7 +603,7 @@ class DataFileUploadSerializer(serializers.Serializer):
|
|||||||
"""Perform validation checks on the uploaded data file."""
|
"""Perform validation checks on the uploaded data file."""
|
||||||
self.filename = data_file.name
|
self.filename = data_file.name
|
||||||
|
|
||||||
name, ext = os.path.splitext(data_file.name)
|
_name, ext = os.path.splitext(data_file.name)
|
||||||
|
|
||||||
# Remove the leading . from the extension
|
# Remove the leading . from the extension
|
||||||
ext = ext[1:]
|
ext = ext[1:]
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
"""JSON API for the Build app."""
|
"""JSON API for the Build app."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
from django.db.models import F, Q
|
from django.db.models import F, Q
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
@ -363,7 +364,7 @@ class BuildLineList(BuildLineEndpoint, ListCreateAPI):
|
|||||||
'bom_item__reference',
|
'bom_item__reference',
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_source_build(self) -> Build:
|
def get_source_build(self) -> Build | None:
|
||||||
"""Return the target build for the BuildLine queryset."""
|
"""Return the target build for the BuildLine queryset."""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -379,7 +380,7 @@ class BuildLineList(BuildLineEndpoint, ListCreateAPI):
|
|||||||
class BuildLineDetail(BuildLineEndpoint, RetrieveUpdateDestroyAPI):
|
class BuildLineDetail(BuildLineEndpoint, RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a BuildLine object."""
|
"""API endpoint for detail view of a BuildLine object."""
|
||||||
|
|
||||||
def get_source_build(self) -> Build:
|
def get_source_build(self) -> Build | None:
|
||||||
"""Return the target source location for the BuildLine queryset."""
|
"""Return the target source location for the BuildLine queryset."""
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
@ -1467,25 +1467,23 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
valid = self.bom_item.is_stock_item_valid(self.stock_item)
|
valid = self.bom_item.is_stock_item_valid(self.stock_item)
|
||||||
|
|
||||||
# If the existing BomItem is *not* valid, try to find a match
|
# If the existing BomItem is *not* valid, try to find a match
|
||||||
if not valid:
|
if not valid and self.build and self.stock_item:
|
||||||
|
ancestors = self.stock_item.part.get_ancestors(include_self=True, ascending=True)
|
||||||
|
|
||||||
if self.build and self.stock_item:
|
for idx, ancestor in enumerate(ancestors):
|
||||||
ancestors = self.stock_item.part.get_ancestors(include_self=True, ascending=True)
|
|
||||||
|
|
||||||
for idx, ancestor in enumerate(ancestors):
|
build_line = BuildLine.objects.filter(
|
||||||
|
build=self.build,
|
||||||
|
bom_item__part=ancestor,
|
||||||
|
)
|
||||||
|
|
||||||
build_line = BuildLine.objects.filter(
|
if build_line.exists():
|
||||||
build=self.build,
|
line = build_line.first()
|
||||||
bom_item__part=ancestor,
|
|
||||||
)
|
|
||||||
|
|
||||||
if build_line.exists():
|
if idx == 0 or line.bom_item.allow_variants:
|
||||||
line = build_line.first()
|
valid = True
|
||||||
|
self.build_line = line
|
||||||
if idx == 0 or line.bom_item.allow_variants:
|
break
|
||||||
valid = True
|
|
||||||
self.build_line = line
|
|
||||||
break
|
|
||||||
|
|
||||||
# BomItem did not exist or could not be validated.
|
# BomItem did not exist or could not be validated.
|
||||||
# Search for a new one
|
# Search for a new one
|
||||||
|
@ -374,7 +374,7 @@ class BuildTest(BuildAPITest):
|
|||||||
self.assertEqual(n_outputs, bo.output_count)
|
self.assertEqual(n_outputs, bo.output_count)
|
||||||
|
|
||||||
# Now, create with *good* data
|
# Now, create with *good* data
|
||||||
response = self.post(
|
self.post(
|
||||||
create_url,
|
create_url,
|
||||||
{
|
{
|
||||||
'quantity': 5,
|
'quantity': 5,
|
||||||
@ -444,7 +444,7 @@ class BuildTest(BuildAPITest):
|
|||||||
self.assertEqual(1, bo.complete_count)
|
self.assertEqual(1, bo.complete_count)
|
||||||
|
|
||||||
# Let's delete 2 build outputs
|
# Let's delete 2 build outputs
|
||||||
response = self.post(
|
self.post(
|
||||||
delete_url,
|
delete_url,
|
||||||
{
|
{
|
||||||
'outputs': [
|
'outputs': [
|
||||||
@ -479,7 +479,7 @@ class BuildTest(BuildAPITest):
|
|||||||
output.refresh_from_db()
|
output.refresh_from_db()
|
||||||
self.assertTrue(output.is_building)
|
self.assertTrue(output.is_building)
|
||||||
|
|
||||||
response = self.post(
|
self.post(
|
||||||
complete_url,
|
complete_url,
|
||||||
{
|
{
|
||||||
'outputs': [
|
'outputs': [
|
||||||
@ -837,7 +837,7 @@ class BuildAllocationTest(BuildAPITest):
|
|||||||
si.quantity = 100
|
si.quantity = 100
|
||||||
si.save()
|
si.save()
|
||||||
|
|
||||||
response = self.post(
|
self.post(
|
||||||
self.url,
|
self.url,
|
||||||
{
|
{
|
||||||
"items": [
|
"items": [
|
||||||
@ -860,7 +860,7 @@ class BuildAllocationTest(BuildAPITest):
|
|||||||
lft=0, rght=0
|
lft=0, rght=0
|
||||||
)
|
)
|
||||||
|
|
||||||
response = self.post(
|
self.post(
|
||||||
self.url,
|
self.url,
|
||||||
{
|
{
|
||||||
"items": [
|
"items": [
|
||||||
|
@ -254,7 +254,6 @@ class BaseInvenTreeSetting(models.Model):
|
|||||||
logger.exception(
|
logger.exception(
|
||||||
'Failed to build default values for %s (%s)', str(cls), str(type(exc))
|
'Failed to build default values for %s (%s)', str(cls), str(type(exc))
|
||||||
)
|
)
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cache.set(cache_key, True, timeout=3600)
|
cache.set(cache_key, True, timeout=3600)
|
||||||
@ -753,13 +752,11 @@ class BaseInvenTreeSetting(models.Model):
|
|||||||
except (OperationalError, ProgrammingError):
|
except (OperationalError, ProgrammingError):
|
||||||
logger.warning("Database is locked, cannot set setting '%s'", key)
|
logger.warning("Database is locked, cannot set setting '%s'", key)
|
||||||
# Likely the DB is locked - not much we can do here
|
# Likely the DB is locked - not much we can do here
|
||||||
pass
|
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
# Some other error
|
# Some other error
|
||||||
logger.exception(
|
logger.exception(
|
||||||
"Error setting setting '%s' for %s: %s", key, str(cls), str(type(exc))
|
"Error setting setting '%s' for %s: %s", key, str(cls), str(type(exc))
|
||||||
)
|
)
|
||||||
pass
|
|
||||||
|
|
||||||
key = models.CharField(
|
key = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
@ -2924,7 +2921,7 @@ class NotificationEntry(MetaMixin):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def notify(cls, key: str, uid: int):
|
def notify(cls, key: str, uid: int):
|
||||||
"""Notify the database that a particular notification has been sent out."""
|
"""Notify the database that a particular notification has been sent out."""
|
||||||
entry, created = cls.objects.get_or_create(key=key, uid=uid)
|
entry, _ = cls.objects.get_or_create(key=key, uid=uid)
|
||||||
|
|
||||||
entry.save()
|
entry.save()
|
||||||
|
|
||||||
|
@ -9,4 +9,4 @@ States can be extended with custom options for each InvenTree instance - those o
|
|||||||
from .states import StatusCode
|
from .states import StatusCode
|
||||||
from .transition import StateTransitionMixin, TransitionMethod, storage
|
from .transition import StateTransitionMixin, TransitionMethod, storage
|
||||||
|
|
||||||
__all__ = [StatusCode, storage, TransitionMethod, StateTransitionMixin]
|
__all__ = ['StatusCode', 'storage', 'TransitionMethod', 'StateTransitionMixin']
|
||||||
|
@ -188,7 +188,7 @@ class LabelTest(InvenTreeAPITestCase):
|
|||||||
self.assertGreaterEqual(n, 1)
|
self.assertGreaterEqual(n, 1)
|
||||||
|
|
||||||
# Delete the last report
|
# Delete the last report
|
||||||
response = self.delete(
|
self.delete(
|
||||||
reverse(self.detail_url, kwargs={'pk': labels[n - 1].pk}), expected_code=204
|
reverse(self.detail_url, kwargs={'pk': labels[n - 1].pk}), expected_code=204
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -208,10 +208,10 @@ class LabelTest(InvenTreeAPITestCase):
|
|||||||
url = reverse(self.print_url, kwargs={'pk': labels[0].pk})
|
url = reverse(self.print_url, kwargs={'pk': labels[0].pk})
|
||||||
|
|
||||||
# Try to print without providing a valid item
|
# Try to print without providing a valid item
|
||||||
response = self.get(url, expected_code=400)
|
self.get(url, expected_code=400)
|
||||||
|
|
||||||
# Try to print with an invalid item
|
# Try to print with an invalid item
|
||||||
response = self.get(url, {self.print_itemname: 9999}, expected_code=400)
|
self.get(url, {self.print_itemname: 9999}, expected_code=400)
|
||||||
|
|
||||||
# Now print with a valid item
|
# Now print with a valid item
|
||||||
print(f'{self.print_itemmodel = }')
|
print(f'{self.print_itemmodel = }')
|
||||||
|
@ -345,7 +345,7 @@ class BaseMachineType(ClassValidationMixin, ClassProviderMixin):
|
|||||||
|
|
||||||
missing_settings: dict[MachineSetting.ConfigType, list[str]] = {}
|
missing_settings: dict[MachineSetting.ConfigType, list[str]] = {}
|
||||||
for settings, config_type in self.setting_types:
|
for settings, config_type in self.setting_types:
|
||||||
is_valid, missing = MachineSetting.check_all_settings(
|
_nbr, missing = MachineSetting.check_all_settings(
|
||||||
settings_definition=settings,
|
settings_definition=settings,
|
||||||
machine_config=self.machine_config,
|
machine_config=self.machine_config,
|
||||||
config_type=config_type,
|
config_type=config_type,
|
||||||
|
@ -151,7 +151,7 @@ class TotalPriceMixin(models.Model):
|
|||||||
total += line.quantity * convert_money(line.price, target_currency)
|
total += line.quantity * convert_money(line.price, target_currency)
|
||||||
except MissingRate:
|
except MissingRate:
|
||||||
# Record the error, try to press on
|
# Record the error, try to press on
|
||||||
kind, info, data = sys.exc_info()
|
_1, _2, _3 = sys.exc_info()
|
||||||
|
|
||||||
log_error('order.calculate_total_price')
|
log_error('order.calculate_total_price')
|
||||||
logger.exception("Missing exchange rate for '%s'", target_currency)
|
logger.exception("Missing exchange rate for '%s'", target_currency)
|
||||||
|
@ -3845,16 +3845,19 @@ class PartCategoryParameterTemplate(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
'' if self.default_value is None else str(self.default_value.strip())
|
'' if self.default_value is None else str(self.default_value.strip())
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.default_value and InvenTreeSetting.get_setting(
|
if (
|
||||||
'PART_PARAMETER_ENFORCE_UNITS', True, cache=False, create=False
|
self.default_value
|
||||||
|
and InvenTreeSetting.get_setting(
|
||||||
|
'PART_PARAMETER_ENFORCE_UNITS', True, cache=False, create=False
|
||||||
|
)
|
||||||
|
and self.parameter_template.units
|
||||||
):
|
):
|
||||||
if self.parameter_template.units:
|
try:
|
||||||
try:
|
InvenTree.conversion.convert_physical_value(
|
||||||
InvenTree.conversion.convert_physical_value(
|
self.default_value, self.parameter_template.units
|
||||||
self.default_value, self.parameter_template.units
|
)
|
||||||
)
|
except ValidationError as e:
|
||||||
except ValidationError as e:
|
raise ValidationError({'default_value': e.message})
|
||||||
raise ValidationError({'default_value': e.message})
|
|
||||||
|
|
||||||
category = models.ForeignKey(
|
category = models.ForeignKey(
|
||||||
PartCategory,
|
PartCategory,
|
||||||
|
@ -202,9 +202,8 @@ class SupplierBarcodeMixin(BarcodeMixin):
|
|||||||
|
|
||||||
purchase_order = matching_orders.first()
|
purchase_order = matching_orders.first()
|
||||||
|
|
||||||
if supplier and purchase_order:
|
if supplier and purchase_order and purchase_order.supplier != supplier:
|
||||||
if purchase_order.supplier != supplier:
|
return {'error': _('Purchase order does not match supplier')}
|
||||||
return {'error': _('Purchase order does not match supplier')}
|
|
||||||
|
|
||||||
return self.receive_purchase_order_item(
|
return self.receive_purchase_order_item(
|
||||||
supplier_part,
|
supplier_part,
|
||||||
@ -329,7 +328,7 @@ class SupplierBarcodeMixin(BarcodeMixin):
|
|||||||
|
|
||||||
# Check that the barcode starts with the necessary header
|
# Check that the barcode starts with the necessary header
|
||||||
if not barcode_data.startswith(HEADER):
|
if not barcode_data.startswith(HEADER):
|
||||||
return
|
return []
|
||||||
|
|
||||||
return SupplierBarcodeMixin.split_fields(
|
return SupplierBarcodeMixin.split_fields(
|
||||||
barcode_data, delimiter=DELIMITER, header=HEADER, trailer=TRAILER
|
barcode_data, delimiter=DELIMITER, header=HEADER, trailer=TRAILER
|
||||||
|
@ -104,7 +104,7 @@ def process_event(plugin_slug, event, *args, **kwargs):
|
|||||||
# Log the exception to the database
|
# Log the exception to the database
|
||||||
InvenTree.exceptions.log_error(f'plugins.{plugin_slug}.process_event')
|
InvenTree.exceptions.log_error(f'plugins.{plugin_slug}.process_event')
|
||||||
# Re-throw the exception so that the background worker tries again
|
# Re-throw the exception so that the background worker tries again
|
||||||
raise Exception
|
raise e
|
||||||
|
|
||||||
|
|
||||||
def allow_table_event(table_name):
|
def allow_table_event(table_name):
|
||||||
|
@ -69,11 +69,10 @@ class ScheduleMixin:
|
|||||||
'ENABLE_PLUGINS_SCHEDULE'
|
'ENABLE_PLUGINS_SCHEDULE'
|
||||||
):
|
):
|
||||||
for _key, plugin in plugins:
|
for _key, plugin in plugins:
|
||||||
if plugin.mixin_enabled('schedule'):
|
if plugin.mixin_enabled('schedule') and plugin.is_active():
|
||||||
if plugin.is_active():
|
# Only active tasks for plugins which are enabled
|
||||||
# Only active tasks for plugins which are enabled
|
plugin.register_tasks()
|
||||||
plugin.register_tasks()
|
task_keys += plugin.get_task_names()
|
||||||
task_keys += plugin.get_task_names()
|
|
||||||
|
|
||||||
if len(task_keys) > 0:
|
if len(task_keys) > 0:
|
||||||
logger.info('Activated %s scheduled tasks', len(task_keys))
|
logger.info('Activated %s scheduled tasks', len(task_keys))
|
||||||
|
@ -58,7 +58,7 @@ class LabelPrintingMixin:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return label.render(request)
|
return label.render(request)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
log_error('label.render_to_pdf')
|
log_error('label.render_to_pdf')
|
||||||
raise ValidationError(_('Error rendering label to PDF'))
|
raise ValidationError(_('Error rendering label to PDF'))
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ class LabelPrintingMixin:
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return label.render_as_string(request)
|
return label.render_as_string(request)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
log_error('label.render_to_html')
|
log_error('label.render_to_html')
|
||||||
raise ValidationError(_('Error rendering label to HTML'))
|
raise ValidationError(_('Error rendering label to HTML'))
|
||||||
|
|
||||||
@ -106,7 +106,7 @@ class LabelPrintingMixin:
|
|||||||
# Convert to png data
|
# Convert to png data
|
||||||
try:
|
try:
|
||||||
return pdf2image.convert_from_bytes(pdf_data, **pdf2image_kwargs)[0]
|
return pdf2image.convert_from_bytes(pdf_data, **pdf2image_kwargs)[0]
|
||||||
except Exception as e:
|
except Exception:
|
||||||
log_error('label.render_to_png')
|
log_error('label.render_to_png')
|
||||||
raise ValidationError(_('Error rendering label to PNG'))
|
raise ValidationError(_('Error rendering label to PNG'))
|
||||||
|
|
||||||
|
@ -58,7 +58,6 @@ class LocateMixin:
|
|||||||
|
|
||||||
except StockItem.DoesNotExist: # pragma: no cover
|
except StockItem.DoesNotExist: # pragma: no cover
|
||||||
logger.warning('LocateMixin: StockItem pk={item_pk} not found')
|
logger.warning('LocateMixin: StockItem pk={item_pk} not found')
|
||||||
pass
|
|
||||||
|
|
||||||
def locate_stock_location(self, location_pk):
|
def locate_stock_location(self, location_pk):
|
||||||
"""Attempt to location a particular StockLocation.
|
"""Attempt to location a particular StockLocation.
|
||||||
|
@ -41,7 +41,7 @@ class InvenTreeCurrencyExchange(APICallMixin, CurrencyExchangeMixin, InvenTreePl
|
|||||||
self.api_url,
|
self.api_url,
|
||||||
response.status_code,
|
response.status_code,
|
||||||
)
|
)
|
||||||
return None
|
return {}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def api_url(self):
|
def api_url(self):
|
||||||
|
@ -145,7 +145,7 @@ def get_git_log(path):
|
|||||||
datetime.datetime.fromtimestamp(commit.author_time).isoformat(),
|
datetime.datetime.fromtimestamp(commit.author_time).isoformat(),
|
||||||
commit.message.decode().split('\n')[0],
|
commit.message.decode().split('\n')[0],
|
||||||
]
|
]
|
||||||
except KeyError as err:
|
except KeyError:
|
||||||
logger.debug('No HEAD tag found in git repo at path %s', path)
|
logger.debug('No HEAD tag found in git repo at path %s', path)
|
||||||
except NotGitRepository:
|
except NotGitRepository:
|
||||||
pass
|
pass
|
||||||
|
@ -139,11 +139,10 @@ class PluginConfig(InvenTree.models.MetadataMixin, models.Model):
|
|||||||
# Force active if builtin
|
# Force active if builtin
|
||||||
self.active = True
|
self.active = True
|
||||||
|
|
||||||
if not reload:
|
if not reload and self.active != self.__org_active:
|
||||||
if self.active != self.__org_active:
|
if settings.PLUGIN_TESTING:
|
||||||
if settings.PLUGIN_TESTING:
|
warnings.warn('A reload was triggered', stacklevel=2)
|
||||||
warnings.warn('A reload was triggered', stacklevel=2)
|
registry.reload_plugins()
|
||||||
registry.reload_plugins()
|
|
||||||
|
|
||||||
@admin.display(boolean=True, description=_('Installed'))
|
@admin.display(boolean=True, description=_('Installed'))
|
||||||
def is_installed(self) -> bool:
|
def is_installed(self) -> bool:
|
||||||
|
@ -95,9 +95,8 @@ class PluginsRegistry:
|
|||||||
|
|
||||||
plg = self.plugins[slug]
|
plg = self.plugins[slug]
|
||||||
|
|
||||||
if active is not None:
|
if active is not None and active != plg.is_active():
|
||||||
if active != plg.is_active():
|
return None
|
||||||
return None
|
|
||||||
|
|
||||||
return plg
|
return plg
|
||||||
|
|
||||||
@ -130,7 +129,7 @@ class PluginsRegistry:
|
|||||||
try:
|
try:
|
||||||
cfg.name = name
|
cfg.name = name
|
||||||
cfg.save()
|
cfg.save()
|
||||||
except Exception as e:
|
except Exception:
|
||||||
logger.exception('Failed to update plugin name')
|
logger.exception('Failed to update plugin name')
|
||||||
|
|
||||||
return cfg
|
return cfg
|
||||||
|
@ -142,9 +142,8 @@ class SampleValidatorPlugin(SettingsMixin, ValidationMixin, InvenTreePlugin):
|
|||||||
"""
|
"""
|
||||||
prefix = self.get_setting('BATCH_CODE_PREFIX')
|
prefix = self.get_setting('BATCH_CODE_PREFIX')
|
||||||
|
|
||||||
if len(batch_code) > 0:
|
if len(batch_code) > 0 and prefix and not batch_code.startswith(prefix):
|
||||||
if prefix and not batch_code.startswith(prefix):
|
self.raise_error(f"Batch code must start with '{prefix}'")
|
||||||
self.raise_error(f"Batch code must start with '{prefix}'")
|
|
||||||
|
|
||||||
def generate_batch_code(self):
|
def generate_batch_code(self):
|
||||||
"""Generate a new batch code."""
|
"""Generate a new batch code."""
|
||||||
|
@ -642,13 +642,10 @@ class StockItem(
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Trackable parts must have integer values for quantity field!
|
# Trackable parts must have integer values for quantity field!
|
||||||
if self.part.trackable:
|
if self.part.trackable and self.quantity != int(self.quantity):
|
||||||
if self.quantity != int(self.quantity):
|
raise ValidationError({
|
||||||
raise ValidationError({
|
'quantity': _('Quantity must be integer value for trackable parts')
|
||||||
'quantity': _(
|
})
|
||||||
'Quantity must be integer value for trackable parts'
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
# Virtual parts cannot have stock items created against them
|
# Virtual parts cannot have stock items created against them
|
||||||
if self.part.virtual:
|
if self.part.virtual:
|
||||||
@ -2396,17 +2393,15 @@ class StockItemTestResult(InvenTree.models.InvenTreeMetadataModel):
|
|||||||
|
|
||||||
for template in templates:
|
for template in templates:
|
||||||
if key == template.key:
|
if key == template.key:
|
||||||
if template.requires_value:
|
if template.requires_value and not self.value:
|
||||||
if not self.value:
|
raise ValidationError({
|
||||||
raise ValidationError({
|
'value': _('Value must be provided for this test')
|
||||||
'value': _('Value must be provided for this test')
|
})
|
||||||
})
|
|
||||||
|
|
||||||
if template.requires_attachment:
|
if template.requires_attachment and not self.attachment:
|
||||||
if not self.attachment:
|
raise ValidationError({
|
||||||
raise ValidationError({
|
'attachment': _('Attachment must be uploaded for this test')
|
||||||
'attachment': _('Attachment must be uploaded for this test')
|
})
|
||||||
})
|
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "InvenTreeUI",
|
"name": "inventreeui",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Container, Flex, Space } from '@mantine/core';
|
import { Container, Flex, Space } from '@mantine/core';
|
||||||
import { Navigate, Outlet, useLocation, useNavigate } from 'react-router-dom';
|
import { Navigate, Outlet, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
import { InvenTreeStyle } from '../../globalStyle';
|
import { InvenTreeStyle } from '../../globalStyle';
|
||||||
import { useSessionState } from '../../states/SessionState';
|
import { useSessionState } from '../../states/SessionState';
|
||||||
|
Loading…
Reference in New Issue
Block a user