From ba915da22b8013f9bdb548a4d15dfbc8ed5b5def Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 6 Jan 2021 22:20:54 +1100 Subject: [PATCH] Filter StockItem API by staleness --- InvenTree/build/models.py | 4 +- InvenTree/common/models.py | 68 ++++++++++++++----- InvenTree/stock/api.py | 40 ++++++++--- .../templates/InvenTree/settings/setting.html | 2 +- .../templates/InvenTree/settings/stock.html | 1 + 5 files changed, 86 insertions(+), 29 deletions(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 96101adafe..c3b399d820 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -27,7 +27,7 @@ from InvenTree.helpers import increment, getSetting, normalize from InvenTree.validators import validate_build_order_reference from InvenTree.models import InvenTreeAttachment -from common.models import InvenTreeSetting +import common.models import InvenTree.fields @@ -822,7 +822,7 @@ class Build(MPTTModel): ) # Exclude expired stock items - if not InvenTreeSetting.get_setting('STOCK_ALLOW_EXPIRED_BUILD'): + if not common.models.InvenTreeSetting.get_setting('STOCK_ALLOW_EXPIRED_BUILD'): items = items.exclude(StockModels.StockItem.EXPIRED_FILTER) return items diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 69c499f3b8..5651a401de 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -174,6 +174,14 @@ class InvenTreeSetting(models.Model): 'validator': bool, }, + 'STOCK_STALE_DAYS': { + 'name': _('Stock Stale Time'), + 'description': _('Number of days stock items are considered stale before expiring'), + 'default': 0, + 'units': _('days'), + 'validator': [int], + }, + 'STOCK_ALLOW_EXPIRED_BUILD': { 'name': _('Build Expired Stock'), 'description': _('Allow building with expired stock'), @@ -381,8 +389,10 @@ class InvenTreeSetting(models.Model): value = InvenTree.helpers.str2bool(value) if setting.is_int(): - # TODO - Coerce to an integer value - pass + try: + value = int(value) + except (ValueError, TypeError): + value = backup_value else: value = backup_value @@ -472,18 +482,26 @@ class InvenTreeSetting(models.Model): return - # Check if a 'type' has been specified for this value - if type(validator) == type: + # Boolean validator + if validator == bool: + # Value must "look like" a boolean value + if InvenTree.helpers.is_bool(self.value): + # Coerce into either "True" or "False" + self.value = str(InvenTree.helpers.str2bool(self.value)) + else: + raise ValidationError({ + 'value': _('Value must be a boolean value') + }) - if validator == bool: - # Value must "look like" a boolean value - if InvenTree.helpers.is_bool(self.value): - # Coerce into either "True" or "False" - self.value = str(InvenTree.helpers.str2bool(self.value)) - else: - raise ValidationError({ - 'value': _('Value must be a boolean value') - }) + # Integer validator + if validator == int: + try: + # Coerce into an integer value + self.value = str(int(self.value)) + except (ValueError, TypeError): + raise ValidationError({ + 'value': _('Value must be an integer value'), + }) def validate_unique(self, exclude=None): """ Ensure that the key:value pair is unique. @@ -528,15 +546,29 @@ class InvenTreeSetting(models.Model): def is_int(self): """ Check if the setting is required to be an integer value: - - - int / 'int' = any integer value - - 'pos' / 'positive' = any positive integer value (including zero) - - 'neg' / 'negative' = any negative integer value (including zero) """ validator = InvenTreeSetting.get_setting_validator(self.key) - return validator in [int, 'int', 'pos', 'positive', 'neg', 'negative'] + if validator == int: + return True + + if type(validator) in [list, tuple]: + for v in validator: + if v == int: + return True + + def as_int(self): + """ + Return the value of this setting converted to a boolean value. + + If an error occurs, return the default value + """ + + try: + value = int() + except (ValueError, TypeError): + return self.default_value() class PriceBreak(models.Model): diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 97f5b5c42e..9f0a4278f5 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -23,6 +23,9 @@ from part.serializers import PartBriefSerializer from company.models import SupplierPart from company.serializers import SupplierPartSerializer +import common.settings +import common.models + from .serializers import StockItemSerializer from .serializers import LocationSerializer, LocationBriefSerializer from .serializers import StockTrackingSerializer @@ -535,16 +538,37 @@ class StockList(generics.ListCreateAPIView): # Exclude items which are instaled in another item queryset = queryset.filter(belongs_to=None) - # Filter by 'expired' status - expired = params.get('expired', None) + if common.settings.stock_expiry_enabled(): - if expired is not None: - expired = str2bool(expired) + # Filter by 'expired' status + expired = params.get('expired', None) - if expired: - queryset = queryset.filter(StockItem.EXPIRED_FILTER) - else: - queryset = queryset.exclude(StockItem.EXPIRED_FILTER) + if expired is not None: + expired = str2bool(expired) + + if expired: + queryset = queryset.filter(StockItem.EXPIRED_FILTER) + else: + queryset = queryset.exclude(StockItem.EXPIRED_FILTER) + + # Filter by 'stale' status + stale = params.get('stale', None) + + if stale is not None: + stale = str2bool(stale) + + # How many days to account for "staleness"? + stale_days = common.models.InvenTreeSetting.get_setting('STOCK_STALE_DAYS') + + if stale_days > 0: + stale_date = datetime.now().date() + timedelta(days=stale_days) + + stale_filter = StockItem.IN_STOCK_FILTER & ~Q(expiry_date=None) & Q(expiry_date__lt=stale_date) + + if stale: + queryset = queryset.filter(stale_filter) + else: + queryset = queryset.exclude(stale_filter) # Filter by customer customer = params.get('customer', None) diff --git a/InvenTree/templates/InvenTree/settings/setting.html b/InvenTree/templates/InvenTree/settings/setting.html index b08f4aeb3a..b7932fc30a 100644 --- a/InvenTree/templates/InvenTree/settings/setting.html +++ b/InvenTree/templates/InvenTree/settings/setting.html @@ -17,7 +17,7 @@ {% else %} {% if setting.value %} - {{ setting.value }}{{ setting.units }} + {{ setting.value }} {{ setting.units }} {% else %} {% trans "No value set" %} diff --git a/InvenTree/templates/InvenTree/settings/stock.html b/InvenTree/templates/InvenTree/settings/stock.html index 051a251c24..5ad308decc 100644 --- a/InvenTree/templates/InvenTree/settings/stock.html +++ b/InvenTree/templates/InvenTree/settings/stock.html @@ -16,6 +16,7 @@ {% include "InvenTree/settings/header.html" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ENABLE_EXPIRY" %} + {% include "InvenTree/settings/setting.html" with key="STOCK_STALE_DAYS" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_SALE" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_BUILD" %}