2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-08-28 14:25:56 +00:00

Merged master

This commit is contained in:
eeintech
2021-01-07 13:50:29 -05:00
145 changed files with 23900 additions and 2396 deletions

View File

@@ -27,9 +27,12 @@ from mptt.models import MPTTModel, TreeForeignKey
from djmoney.models.fields import MoneyField
from decimal import Decimal, InvalidOperation
from datetime import datetime
from datetime import datetime, timedelta
from InvenTree import helpers
import common.models
import report.models
from InvenTree.status_codes import StockStatus
from InvenTree.models import InvenTreeTree, InvenTreeAttachment
from InvenTree.fields import InvenTreeURLField
@@ -129,6 +132,7 @@ class StockItem(MPTTModel):
serial: Unique serial number for this StockItem
link: Optional URL to link to external resource
updated: Date that this stock item was last updated (auto)
expiry_date: Expiry date of the StockItem (optional)
stocktake_date: Date of last stocktake for this item
stocktake_user: User that performed the most recent stocktake
review_needed: Flag if StockItem needs review
@@ -153,6 +157,9 @@ class StockItem(MPTTModel):
status__in=StockStatus.AVAILABLE_CODES
)
# A query filter which can be used to filter StockItem objects which have expired
EXPIRED_FILTER = IN_STOCK_FILTER & ~Q(expiry_date=None) & Q(expiry_date__lt=datetime.now().date())
def save(self, *args, **kwargs):
"""
Save this StockItem to the database. Performs a number of checks:
@@ -432,11 +439,19 @@ class StockItem(MPTTModel):
related_name='stock_items',
null=True, blank=True)
# last time the stock was checked / counted
expiry_date = models.DateField(
blank=True, null=True,
verbose_name=_('Expiry Date'),
help_text=_('Expiry date for stock item. Stock will be considered expired after this date'),
)
stocktake_date = models.DateField(blank=True, null=True)
stocktake_user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True,
related_name='stocktake_stock')
stocktake_user = models.ForeignKey(
User, on_delete=models.SET_NULL,
blank=True, null=True,
related_name='stocktake_stock'
)
review_needed = models.BooleanField(default=False)
@@ -467,6 +482,55 @@ class StockItem(MPTTModel):
help_text='Owner (User)',
related_name='owner_stockitems')
def is_stale(self):
"""
Returns True if this Stock item is "stale".
To be "stale", the following conditions must be met:
- Expiry date is not None
- Expiry date will "expire" within the configured stale date
- The StockItem is otherwise "in stock"
"""
if self.expiry_date is None:
return False
if not self.in_stock:
return False
today = datetime.now().date()
stale_days = common.models.InvenTreeSetting.get_setting('STOCK_STALE_DAYS')
if stale_days <= 0:
return False
expiry_date = today + timedelta(days=stale_days)
return self.expiry_date < expiry_date
def is_expired(self):
"""
Returns True if this StockItem is "expired".
To be "expired", the following conditions must be met:
- Expiry date is not None
- Expiry date is "in the past"
- The StockItem is otherwise "in stock"
"""
if self.expiry_date is None:
return False
if not self.in_stock:
return False
today = datetime.now().date()
return self.expiry_date < today
def clearAllocations(self):
"""
Clear all order allocations for this StockItem:
@@ -729,36 +793,16 @@ class StockItem(MPTTModel):
@property
def in_stock(self):
"""
Returns True if this item is in stock
Returns True if this item is in stock.
See also: IN_STOCK_FILTER
"""
# Quantity must be above zero (unless infinite)
if self.quantity <= 0 and not self.infinite:
return False
query = StockItem.objects.filter(pk=self.pk)
# Not 'in stock' if it has been installed inside another StockItem
if self.belongs_to is not None:
return False
# Not 'in stock' if it has been sent to a customer
if self.sales_order is not None:
return False
query = query.filter(StockItem.IN_STOCK_FILTER)
# Not 'in stock' if it has been assigned to a customer
if self.customer is not None:
return False
# Not 'in stock' if it is building
if self.is_building:
return False
# Not 'in stock' if the status code makes it unavailable
if self.status in StockStatus.UNAVAILABLE_CODES:
return False
return True
return query.exists()
@property
def tracking_info_count(self):
@@ -1271,6 +1315,41 @@ class StockItem(MPTTModel):
return status['passed'] >= status['total']
def available_test_reports(self):
"""
Return a list of TestReport objects which match this StockItem.
"""
reports = []
item_query = StockItem.objects.filter(pk=self.pk)
for test_report in report.models.TestReport.objects.filter(enabled=True):
filters = helpers.validateFilterString(test_report.filters)
if item_query.filter(**filters).exists():
reports.append(test_report)
return reports
@property
def has_test_reports(self):
"""
Return True if there are test reports available for this stock item
"""
return len(self.available_test_reports()) > 0
@property
def has_labels(self):
"""
Return True if there are any label templates available for this stock item
"""
# TODO - Implement this
return True
@receiver(pre_delete, sender=StockItem, dispatch_uid='stock_item_pre_delete_log')
def before_delete_stock_item(sender, instance, using, **kwargs):