diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py
index 571ec1dd6f..97f5b5c42e 100644
--- a/InvenTree/stock/api.py
+++ b/InvenTree/stock/api.py
@@ -35,6 +35,8 @@ from InvenTree.api import AttachmentMixin
from decimal import Decimal, InvalidOperation
+from datetime import datetime, timedelta
+
from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
from rest_framework.response import Response
@@ -342,10 +344,18 @@ class StockList(generics.ListCreateAPIView):
# A location was *not* specified - try to infer it
if 'location' not in request.data:
location = item.part.get_default_location()
+
if location is not None:
item.location = location
item.save()
+ # An expiry date was *not* specified - try to infer it!
+ if 'expiry_date' not in request.data:
+
+ if item.part.default_expiry > 0:
+ item.expiry_date = datetime.now().date() + timedelta(days=item.part.default_expiry)
+ item.save()
+
# Return a response
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html
index 4d773b047d..b8c143fb4f 100644
--- a/InvenTree/stock/templates/stock/item_base.html
+++ b/InvenTree/stock/templates/stock/item_base.html
@@ -70,9 +70,10 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
{% block page_data %}
{% trans "Stock Item" %}
- {% stock_status_label item.status large=True %}
{% if item.is_expired %}
{% trans "Expired" %}
+ {% else %}
+ {% stock_status_label item.status large=True %}
{% endif %}
diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py
index ecca10ba0a..520110469c 100644
--- a/InvenTree/stock/test_api.py
+++ b/InvenTree/stock/test_api.py
@@ -368,6 +368,56 @@ class StockItemTest(StockAPITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ def test_default_expiry(self):
+ """
+ Test that the "default_expiry" functionality works via the API.
+
+ - If an expiry_date is specified, use that
+ - Otherwise, check if the referenced part has a default_expiry defined
+ - If so, use that!
+ - Otherwise, no expiry
+
+ Notes:
+ - Part <25> has a default_expiry of 10 days
+
+ """
+
+ # First test - create a new StockItem without an expiry date
+ data = {
+ 'part': 4,
+ 'quantity': 10,
+ }
+
+ response = self.client.post(self.list_url, data)
+
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+ self.assertIsNone(response.data['expiry_date'])
+
+ # Second test - create a new StockItem with an explicit expiry date
+ data['expiry_date'] = '2022-12-12'
+
+ response = self.client.post(self.list_url, data)
+
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+ self.assertIsNotNone(response.data['expiry_date'])
+ self.assertEqual(response.data['expiry_date'], '2022-12-12')
+
+ # Third test - create a new StockItem for a Part which has a default expiry time
+ data = {
+ 'part': 25,
+ 'quantity': 10
+ }
+
+ response = self.client.post(self.list_url, data)
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+
+ # Expected expiry date is 10 days in the future
+ expiry = datetime.now().date() + timedelta(10)
+
+ self.assertEqual(response.data['expiry_date'], expiry.isoformat())
+
class StocktakeTest(StockAPITestCase):
"""