Filter StockItem API by staleness

This commit is contained in:
Oliver Walters 2021-01-06 22:20:54 +11:00
parent 33d6396a4e
commit ba915da22b
5 changed files with 86 additions and 29 deletions

View File

@ -27,7 +27,7 @@ from InvenTree.helpers import increment, getSetting, normalize
from InvenTree.validators import validate_build_order_reference from InvenTree.validators import validate_build_order_reference
from InvenTree.models import InvenTreeAttachment from InvenTree.models import InvenTreeAttachment
from common.models import InvenTreeSetting import common.models
import InvenTree.fields import InvenTree.fields
@ -822,7 +822,7 @@ class Build(MPTTModel):
) )
# Exclude expired stock items # 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) items = items.exclude(StockModels.StockItem.EXPIRED_FILTER)
return items return items

View File

@ -174,6 +174,14 @@ class InvenTreeSetting(models.Model):
'validator': bool, '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': { 'STOCK_ALLOW_EXPIRED_BUILD': {
'name': _('Build Expired Stock'), 'name': _('Build Expired Stock'),
'description': _('Allow building with expired stock'), 'description': _('Allow building with expired stock'),
@ -381,8 +389,10 @@ class InvenTreeSetting(models.Model):
value = InvenTree.helpers.str2bool(value) value = InvenTree.helpers.str2bool(value)
if setting.is_int(): if setting.is_int():
# TODO - Coerce to an integer value try:
pass value = int(value)
except (ValueError, TypeError):
value = backup_value
else: else:
value = backup_value value = backup_value
@ -472,18 +482,26 @@ class InvenTreeSetting(models.Model):
return return
# Check if a 'type' has been specified for this value # Boolean validator
if type(validator) == type: 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: # Integer validator
# Value must "look like" a boolean value if validator == int:
if InvenTree.helpers.is_bool(self.value): try:
# Coerce into either "True" or "False" # Coerce into an integer value
self.value = str(InvenTree.helpers.str2bool(self.value)) self.value = str(int(self.value))
else: except (ValueError, TypeError):
raise ValidationError({ raise ValidationError({
'value': _('Value must be a boolean value') 'value': _('Value must be an integer value'),
}) })
def validate_unique(self, exclude=None): def validate_unique(self, exclude=None):
""" Ensure that the key:value pair is unique. """ Ensure that the key:value pair is unique.
@ -528,15 +546,29 @@ class InvenTreeSetting(models.Model):
def is_int(self): def is_int(self):
""" """
Check if the setting is required to be an integer value: 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) 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): class PriceBreak(models.Model):

View File

@ -23,6 +23,9 @@ from part.serializers import PartBriefSerializer
from company.models import SupplierPart from company.models import SupplierPart
from company.serializers import SupplierPartSerializer from company.serializers import SupplierPartSerializer
import common.settings
import common.models
from .serializers import StockItemSerializer from .serializers import StockItemSerializer
from .serializers import LocationSerializer, LocationBriefSerializer from .serializers import LocationSerializer, LocationBriefSerializer
from .serializers import StockTrackingSerializer from .serializers import StockTrackingSerializer
@ -535,16 +538,37 @@ class StockList(generics.ListCreateAPIView):
# Exclude items which are instaled in another item # Exclude items which are instaled in another item
queryset = queryset.filter(belongs_to=None) queryset = queryset.filter(belongs_to=None)
# Filter by 'expired' status if common.settings.stock_expiry_enabled():
expired = params.get('expired', None)
if expired is not None: # Filter by 'expired' status
expired = str2bool(expired) expired = params.get('expired', None)
if expired: if expired is not None:
queryset = queryset.filter(StockItem.EXPIRED_FILTER) expired = str2bool(expired)
else:
queryset = queryset.exclude(StockItem.EXPIRED_FILTER) 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 # Filter by customer
customer = params.get('customer', None) customer = params.get('customer', None)

View File

@ -17,7 +17,7 @@
{% else %} {% else %}
{% if setting.value %} {% if setting.value %}
<i><b> <i><b>
{{ setting.value }}</b>{{ setting.units }} {{ setting.value }}</b> {{ setting.units }}
</i> </i>
{% else %} {% else %}
<i>{% trans "No value set" %}</i> <i>{% trans "No value set" %}</i>

View File

@ -16,6 +16,7 @@
{% include "InvenTree/settings/header.html" %} {% include "InvenTree/settings/header.html" %}
<tbody> <tbody>
{% include "InvenTree/settings/setting.html" with key="STOCK_ENABLE_EXPIRY" %} {% 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_SALE" %}
{% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_BUILD" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_BUILD" %}
</tbody> </tbody>