Timestamp issues (#6867)

* Adjust default values for test result fields

* Add helper functions:

- current_time()
- current_date()

Handles timezone "awareness"

* Use new helper function widely

* Update defaults - do not use None

* Allow null field values
This commit is contained in:
Oliver 2024-03-27 16:57:59 +11:00 committed by GitHub
parent cd0d35047d
commit 4059d9ffeb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 116 additions and 53 deletions

View File

@ -872,6 +872,29 @@ def hash_file(filename: Union[str, Path], storage: Union[Storage, None] = None):
return hashlib.md5(content).hexdigest() return hashlib.md5(content).hexdigest()
def current_time(local=True):
"""Return the current date and time as a datetime object.
- If timezone support is active, returns a timezone aware time
- If timezone support is not active, returns a timezone naive time
Arguments:
local: Return the time in the local timezone, otherwise UTC (default = True)
"""
if settings.USE_TZ:
now = timezone.now()
now = to_local_time(now, target_tz=server_timezone() if local else 'UTC')
return now
else:
return datetime.datetime.now()
def current_date(local=True):
"""Return the current date."""
return current_time(local=local).date()
def server_timezone() -> str: def server_timezone() -> str:
"""Return the timezone of the server as a string. """Return the timezone of the server as a string.

View File

@ -952,6 +952,8 @@ USE_I18N = True
# It generates a *lot* of cruft in the logs # It generates a *lot* of cruft in the logs
if not TESTING: if not TESTING:
USE_TZ = True # pragma: no cover USE_TZ = True # pragma: no cover
else:
USE_TZ = False
DATE_INPUT_FORMATS = ['%Y-%m-%d'] DATE_INPUT_FORMATS = ['%Y-%m-%d']

View File

@ -74,7 +74,7 @@ class Build(InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeNo
verbose_name = _("Build Order") verbose_name = _("Build Order")
verbose_name_plural = _("Build Orders") verbose_name_plural = _("Build Orders")
OVERDUE_FILTER = Q(status__in=BuildStatusGroups.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date()) OVERDUE_FILTER = Q(status__in=BuildStatusGroups.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=InvenTree.helpers.current_date())
# Global setting for specifying reference pattern # Global setting for specifying reference pattern
REFERENCE_PATTERN_SETTING = 'BUILDORDER_REFERENCE_PATTERN' REFERENCE_PATTERN_SETTING = 'BUILDORDER_REFERENCE_PATTERN'
@ -546,7 +546,7 @@ class Build(InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeNo
if self.incomplete_count > 0: if self.incomplete_count > 0:
return return
self.completion_date = datetime.now().date() self.completion_date = InvenTree.helpers.current_date()
self.completed_by = user self.completed_by = user
self.status = BuildStatus.COMPLETE.value self.status = BuildStatus.COMPLETE.value
self.save() self.save()
@ -628,7 +628,7 @@ class Build(InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeNo
output.delete() output.delete()
# Date of 'completion' is the date the build was cancelled # Date of 'completion' is the date the build was cancelled
self.completion_date = datetime.now().date() self.completion_date = InvenTree.helpers.current_date()
self.completed_by = user self.completed_by = user
self.status = BuildStatus.CANCELLED.value self.status = BuildStatus.CANCELLED.value

View File

@ -1,6 +1,6 @@
"""Background task definitions for the BuildOrder app""" """Background task definitions for the BuildOrder app"""
from datetime import datetime, timedelta from datetime import timedelta
from decimal import Decimal from decimal import Decimal
import logging import logging
@ -14,6 +14,7 @@ from plugin.events import trigger_event
import common.notifications import common.notifications
import build.models import build.models
import InvenTree.email import InvenTree.email
import InvenTree.helpers
import InvenTree.helpers_model import InvenTree.helpers_model
import InvenTree.tasks import InvenTree.tasks
from InvenTree.status_codes import BuildStatusGroups from InvenTree.status_codes import BuildStatusGroups
@ -222,7 +223,7 @@ def check_overdue_build_orders():
- Look at the 'target_date' of any outstanding BuildOrder objects - Look at the 'target_date' of any outstanding BuildOrder objects
- If the 'target_date' expired *yesterday* then the order is just out of date - If the 'target_date' expired *yesterday* then the order is just out of date
""" """
yesterday = datetime.now().date() - timedelta(days=1) yesterday = InvenTree.helpers.current_date() - timedelta(days=1)
overdue_orders = build.models.Build.objects.filter( overdue_orders = build.models.Build.objects.filter(
target_date=yesterday, target_date=yesterday,

View File

@ -13,7 +13,7 @@ import math
import os import os
import re import re
import uuid import uuid
from datetime import datetime, timedelta, timezone from datetime import timedelta, timezone
from enum import Enum from enum import Enum
from secrets import compare_digest from secrets import compare_digest
from typing import Any, Callable, TypedDict, Union from typing import Any, Callable, TypedDict, Union
@ -2915,7 +2915,7 @@ class NotificationEntry(MetaMixin):
@classmethod @classmethod
def check_recent(cls, key: str, uid: int, delta: timedelta): def check_recent(cls, key: str, uid: int, delta: timedelta):
"""Test if a particular notification has been sent in the specified time period.""" """Test if a particular notification has been sent in the specified time period."""
since = datetime.now().date() - delta since = InvenTree.helpers.current_date() - delta
entries = cls.objects.filter(key=key, uid=uid, updated__gte=since) entries = cls.objects.filter(key=key, uid=uid, updated__gte=since)

View File

@ -2,7 +2,7 @@
import logging import logging
import os import os
from datetime import datetime, timedelta from datetime import timedelta
from django.conf import settings from django.conf import settings
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
@ -12,6 +12,7 @@ from django.utils import timezone
import feedparser import feedparser
import requests import requests
import InvenTree.helpers
from InvenTree.helpers_model import getModelsWithMixin from InvenTree.helpers_model import getModelsWithMixin
from InvenTree.models import InvenTreeNotesMixin from InvenTree.models import InvenTreeNotesMixin
from InvenTree.tasks import ScheduledTask, scheduled_task from InvenTree.tasks import ScheduledTask, scheduled_task
@ -107,7 +108,7 @@ def delete_old_notes_images():
note.delete() note.delete()
note_classes = getModelsWithMixin(InvenTreeNotesMixin) note_classes = getModelsWithMixin(InvenTreeNotesMixin)
before = datetime.now() - timedelta(days=90) before = InvenTree.helpers.current_date() - timedelta(days=90)
for note in NotesImage.objects.filter(date__lte=before): for note in NotesImage.objects.filter(date__lte=before):
# Find any images which are no longer referenced by a note # Find any images which are no longer referenced by a note

View File

@ -901,7 +901,7 @@ class SupplierPart(
def update_available_quantity(self, quantity): def update_available_quantity(self, quantity):
"""Update the available quantity for this SupplierPart.""" """Update the available quantity for this SupplierPart."""
self.available = quantity self.available = quantity
self.availability_updated = datetime.now() self.availability_updated = InvenTree.helpers.current_time()
self.save() self.save()
@property @property

View File

@ -1,6 +1,5 @@
"""Label printing models.""" """Label printing models."""
import datetime
import logging import logging
import os import os
import sys import sys
@ -15,6 +14,7 @@ from django.urls import reverse
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
import build.models import build.models
import InvenTree.helpers
import InvenTree.models import InvenTree.models
import part.models import part.models
import stock.models import stock.models
@ -228,8 +228,8 @@ class LabelTemplate(InvenTree.models.InvenTreeMetadataModel):
# Add "basic" context data which gets passed to every label # Add "basic" context data which gets passed to every label
context['base_url'] = get_base_url(request=request) context['base_url'] = get_base_url(request=request)
context['date'] = datetime.datetime.now().date() context['date'] = InvenTree.helpers.current_date()
context['datetime'] = datetime.datetime.now() context['datetime'] = InvenTree.helpers.current_time()
context['request'] = request context['request'] = request
context['user'] = request.user context['user'] = request.user
context['width'] = self.width context['width'] = self.width

View File

@ -221,7 +221,7 @@ class Order(
""" """
self.reference_int = self.rebuild_reference_field(self.reference) self.reference_int = self.rebuild_reference_field(self.reference)
if not self.creation_date: if not self.creation_date:
self.creation_date = datetime.now().date() self.creation_date = InvenTree.helpers.current_date()
super().save(*args, **kwargs) super().save(*args, **kwargs)
@ -252,7 +252,7 @@ class Order(
It requires any subclasses to implement the get_status_class() class method It requires any subclasses to implement the get_status_class() class method
""" """
today = datetime.now().date() today = InvenTree.helpers.current_date()
return ( return (
Q(status__in=cls.get_status_class().OPEN) Q(status__in=cls.get_status_class().OPEN)
& ~Q(target_date=None) & ~Q(target_date=None)
@ -584,7 +584,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
""" """
if self.is_pending: if self.is_pending:
self.status = PurchaseOrderStatus.PLACED.value self.status = PurchaseOrderStatus.PLACED.value
self.issue_date = datetime.now().date() self.issue_date = InvenTree.helpers.current_date()
self.save() self.save()
trigger_event('purchaseorder.placed', id=self.pk) trigger_event('purchaseorder.placed', id=self.pk)
@ -604,7 +604,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
""" """
if self.status == PurchaseOrderStatus.PLACED: if self.status == PurchaseOrderStatus.PLACED:
self.status = PurchaseOrderStatus.COMPLETE.value self.status = PurchaseOrderStatus.COMPLETE.value
self.complete_date = datetime.now().date() self.complete_date = InvenTree.helpers.current_date()
self.save() self.save()
@ -1030,7 +1030,7 @@ class SalesOrder(TotalPriceMixin, Order):
"""Change this order from 'PENDING' to 'IN_PROGRESS'.""" """Change this order from 'PENDING' to 'IN_PROGRESS'."""
if self.status == SalesOrderStatus.PENDING: if self.status == SalesOrderStatus.PENDING:
self.status = SalesOrderStatus.IN_PROGRESS.value self.status = SalesOrderStatus.IN_PROGRESS.value
self.issue_date = datetime.now().date() self.issue_date = InvenTree.helpers.current_date()
self.save() self.save()
trigger_event('salesorder.issued', id=self.pk) trigger_event('salesorder.issued', id=self.pk)
@ -1044,7 +1044,7 @@ class SalesOrder(TotalPriceMixin, Order):
self.status = SalesOrderStatus.SHIPPED.value self.status = SalesOrderStatus.SHIPPED.value
self.shipped_by = user self.shipped_by = user
self.shipment_date = datetime.now() self.shipment_date = InvenTree.helpers.current_date()
self.save() self.save()
@ -1346,7 +1346,7 @@ class PurchaseOrderLineItem(OrderLineItem):
OVERDUE_FILTER = ( OVERDUE_FILTER = (
Q(received__lt=F('quantity')) Q(received__lt=F('quantity'))
& ~Q(target_date=None) & ~Q(target_date=None)
& Q(target_date__lt=datetime.now().date()) & Q(target_date__lt=InvenTree.helpers.current_date())
) )
@staticmethod @staticmethod
@ -1505,7 +1505,7 @@ class SalesOrderLineItem(OrderLineItem):
OVERDUE_FILTER = ( OVERDUE_FILTER = (
Q(shipped__lt=F('quantity')) Q(shipped__lt=F('quantity'))
& ~Q(target_date=None) & ~Q(target_date=None)
& Q(target_date__lt=datetime.now().date()) & Q(target_date__lt=InvenTree.helpers.current_date())
) )
@staticmethod @staticmethod
@ -1748,7 +1748,9 @@ class SalesOrderShipment(
allocation.complete_allocation(user) allocation.complete_allocation(user)
# Update the "shipment" date # Update the "shipment" date
self.shipment_date = kwargs.get('shipment_date', datetime.now()) self.shipment_date = kwargs.get(
'shipment_date', InvenTree.helpers.current_date()
)
self.shipped_by = user self.shipped_by = user
# Was a tracking number provided? # Was a tracking number provided?
@ -2076,7 +2078,7 @@ class ReturnOrder(TotalPriceMixin, Order):
"""Complete this ReturnOrder (if not already completed).""" """Complete this ReturnOrder (if not already completed)."""
if self.status == ReturnOrderStatus.IN_PROGRESS: if self.status == ReturnOrderStatus.IN_PROGRESS:
self.status = ReturnOrderStatus.COMPLETE.value self.status = ReturnOrderStatus.COMPLETE.value
self.complete_date = datetime.now().date() self.complete_date = InvenTree.helpers.current_date()
self.save() self.save()
trigger_event('returnorder.completed', id=self.pk) trigger_event('returnorder.completed', id=self.pk)
@ -2089,7 +2091,7 @@ class ReturnOrder(TotalPriceMixin, Order):
"""Issue this ReturnOrder (if currently pending).""" """Issue this ReturnOrder (if currently pending)."""
if self.status == ReturnOrderStatus.PENDING: if self.status == ReturnOrderStatus.PENDING:
self.status = ReturnOrderStatus.IN_PROGRESS.value self.status = ReturnOrderStatus.IN_PROGRESS.value
self.issue_date = datetime.now().date() self.issue_date = InvenTree.helpers.current_date()
self.save() self.save()
trigger_event('returnorder.issued', id=self.pk) trigger_event('returnorder.issued', id=self.pk)
@ -2162,7 +2164,7 @@ class ReturnOrder(TotalPriceMixin, Order):
) )
# Update the LineItem # Update the LineItem
line.received_date = datetime.now().date() line.received_date = InvenTree.helpers.current_date()
line.save() line.save()
trigger_event('returnorder.received', id=self.pk) trigger_event('returnorder.received', id=self.pk)

View File

@ -34,7 +34,13 @@ from company.serializers import (
ContactSerializer, ContactSerializer,
SupplierPartSerializer, SupplierPartSerializer,
) )
from InvenTree.helpers import extract_serial_numbers, hash_barcode, normalize, str2bool from InvenTree.helpers import (
current_date,
extract_serial_numbers,
hash_barcode,
normalize,
str2bool,
)
from InvenTree.serializers import ( from InvenTree.serializers import (
InvenTreeAttachmentSerializer, InvenTreeAttachmentSerializer,
InvenTreeCurrencySerializer, InvenTreeCurrencySerializer,
@ -1140,11 +1146,12 @@ class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer):
user = request.user user = request.user
# Extract shipping date (defaults to today's date) # Extract shipping date (defaults to today's date)
shipment_date = data.get('shipment_date', datetime.now()) now = current_date()
shipment_date = data.get('shipment_date', now)
if shipment_date is None: if shipment_date is None:
# Shipment date should not be None - check above only # Shipment date should not be None - check above only
# checks if shipment_date exists in data # checks if shipment_date exists in data
shipment_date = datetime.now() shipment_date = now
shipment.complete_shipment( shipment.complete_shipment(
user, user,

View File

@ -37,6 +37,7 @@ import common.models
import common.settings import common.settings
import InvenTree.conversion import InvenTree.conversion
import InvenTree.fields import InvenTree.fields
import InvenTree.helpers
import InvenTree.models import InvenTree.models
import InvenTree.ready import InvenTree.ready
import InvenTree.tasks import InvenTree.tasks
@ -1728,7 +1729,7 @@ class Part(
self.bom_checksum = self.get_bom_hash() self.bom_checksum = self.get_bom_hash()
self.bom_checked_by = user self.bom_checked_by = user
self.bom_checked_date = datetime.now().date() self.bom_checked_date = InvenTree.helpers.current_date()
self.save() self.save()
@ -2715,7 +2716,7 @@ class PartPricing(common.models.MetaMixin):
) )
if days > 0: if days > 0:
date_threshold = datetime.now().date() - timedelta(days=days) date_threshold = InvenTree.helpers.current_date() - timedelta(days=days)
items = items.filter(updated__gte=date_threshold) items = items.filter(updated__gte=date_threshold)
for item in items: for item in items:

View File

@ -266,7 +266,7 @@ def generate_stocktake_report(**kwargs):
buffer = io.StringIO() buffer = io.StringIO()
buffer.write(dataset.export('csv')) buffer.write(dataset.export('csv'))
today = datetime.now().date().isoformat() today = InvenTree.helpers.current_date().isoformat()
filename = f'InvenTree_Stocktake_{today}.csv' filename = f'InvenTree_Stocktake_{today}.csv'
report_file = ContentFile(buffer.getvalue(), name=filename) report_file = ContentFile(buffer.getvalue(), name=filename)

View File

@ -18,6 +18,7 @@ from django.utils.translation import gettext_lazy as _
import build.models import build.models
import common.models import common.models
import InvenTree.exceptions import InvenTree.exceptions
import InvenTree.helpers
import InvenTree.models import InvenTree.models
import order.models import order.models
import part.models import part.models
@ -250,8 +251,8 @@ class ReportTemplateBase(MetadataMixin, ReportBase):
context = self.get_context_data(request) context = self.get_context_data(request)
context['base_url'] = get_base_url(request=request) context['base_url'] = get_base_url(request=request)
context['date'] = datetime.datetime.now().date() context['date'] = InvenTree.helpers.current_date()
context['datetime'] = datetime.datetime.now() context['datetime'] = InvenTree.helpers.current_time()
context['page_size'] = self.get_report_size() context['page_size'] = self.get_report_size()
context['report_template'] = self context['report_template'] = self
context['report_description'] = self.description context['report_description'] = self.description

View File

@ -19,6 +19,7 @@ from rest_framework.serializers import ValidationError
import common.models import common.models
import common.settings import common.settings
import InvenTree.helpers
import stock.serializers as StockSerializers import stock.serializers as StockSerializers
from build.models import Build from build.models import Build
from build.serializers import BuildSerializer from build.serializers import BuildSerializer
@ -810,7 +811,7 @@ class StockFilter(rest_filters.FilterSet):
# No filtering, does not make sense # No filtering, does not make sense
return queryset return queryset
stale_date = datetime.now().date() + timedelta(days=stale_days) stale_date = InvenTree.helpers.current_date() + timedelta(days=stale_days)
stale_filter = ( stale_filter = (
StockItem.IN_STOCK_FILTER StockItem.IN_STOCK_FILTER
& ~Q(expiry_date=None) & ~Q(expiry_date=None)
@ -906,7 +907,7 @@ class StockList(APIDownloadMixin, ListCreateDestroyAPIView):
# An expiry date was *not* specified - try to infer it! # An expiry date was *not* specified - try to infer it!
if expiry_date is None and part.default_expiry > 0: if expiry_date is None and part.default_expiry > 0:
data['expiry_date'] = datetime.now().date() + timedelta( data['expiry_date'] = InvenTree.helpers.current_date() + timedelta(
days=part.default_expiry days=part.default_expiry
) )
@ -1050,7 +1051,7 @@ class StockList(APIDownloadMixin, ListCreateDestroyAPIView):
filedata = dataset.export(export_format) filedata = dataset.export(export_format)
filename = f'InvenTree_StockItems_{datetime.now().strftime("%d-%b-%Y")}.{export_format}' filename = f'InvenTree_StockItems_{InvenTree.helpers.current_date().strftime("%d-%b-%Y")}.{export_format}'
return DownloadFile(filedata, filename) return DownloadFile(filedata, filename)

View File

@ -1,6 +1,5 @@
# Generated by Django 3.2.23 on 2023-12-18 18:52 # Generated by Django 3.2.23 on 2023-12-18 18:52
import datetime
from django.db import migrations, models from django.db import migrations, models
@ -14,12 +13,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='stockitemtestresult', model_name='stockitemtestresult',
name='finished_datetime', name='finished_datetime',
field=models.DateTimeField(blank=True, default=datetime.datetime.now, help_text='The timestamp of the test finish', verbose_name='Finished'), field=models.DateTimeField(blank=True, help_text='The timestamp of the test finish', verbose_name='Finished'),
), ),
migrations.AddField( migrations.AddField(
model_name='stockitemtestresult', model_name='stockitemtestresult',
name='started_datetime', name='started_datetime',
field=models.DateTimeField(blank=True, default=datetime.datetime.now, help_text='The timestamp of the test start', verbose_name='Started'), field=models.DateTimeField(blank=True, help_text='The timestamp of the test start', verbose_name='Started'),
), ),
migrations.AddField( migrations.AddField(
model_name='stockitemtestresult', model_name='stockitemtestresult',

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2.11 on 2024-03-27 04:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0109_add_additional_test_fields'),
]
operations = [
migrations.AlterField(
model_name='stockitemtestresult',
name='finished_datetime',
field=models.DateTimeField(blank=True, help_text='The timestamp of the test finish', null=True, verbose_name='Finished'),
),
migrations.AlterField(
model_name='stockitemtestresult',
name='started_datetime',
field=models.DateTimeField(blank=True, help_text='The timestamp of the test start', null=True, verbose_name='Started'),
),
]

View File

@ -4,7 +4,7 @@ from __future__ import annotations
import logging import logging
import os import os
from datetime import datetime, timedelta from datetime import timedelta
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from django.conf import settings from django.conf import settings
@ -319,9 +319,9 @@ def generate_batch_code():
'STOCK_BATCH_CODE_TEMPLATE', '' 'STOCK_BATCH_CODE_TEMPLATE', ''
) )
now = datetime.now() now = InvenTree.helpers.current_time()
# Pass context data through to the template randering. # Pass context data through to the template rendering.
# The following context variables are available for custom batch code generation # The following context variables are available for custom batch code generation
context = { context = {
'date': now, 'date': now,
@ -412,7 +412,7 @@ class StockItem(
EXPIRED_FILTER = ( EXPIRED_FILTER = (
IN_STOCK_FILTER IN_STOCK_FILTER
& ~Q(expiry_date=None) & ~Q(expiry_date=None)
& Q(expiry_date__lt=datetime.now().date()) & Q(expiry_date__lt=InvenTree.helpers.current_date())
) )
def update_serial_number(self): def update_serial_number(self):
@ -1011,7 +1011,7 @@ class StockItem(
if not self.in_stock: if not self.in_stock:
return False return False
today = datetime.now().date() today = InvenTree.helpers.current_date()
stale_days = common.models.InvenTreeSetting.get_setting('STOCK_STALE_DAYS') stale_days = common.models.InvenTreeSetting.get_setting('STOCK_STALE_DAYS')
@ -1036,7 +1036,7 @@ class StockItem(
if not self.in_stock: if not self.in_stock:
return False return False
today = datetime.now().date() today = InvenTree.helpers.current_date()
return self.expiry_date < today return self.expiry_date < today
@ -1438,7 +1438,7 @@ class StockItem(
item=self, item=self,
tracking_type=entry_type.value, tracking_type=entry_type.value,
user=user, user=user,
date=datetime.now(), date=InvenTree.helpers.current_time(),
notes=notes, notes=notes,
deltas=deltas, deltas=deltas,
) )
@ -1962,7 +1962,7 @@ class StockItem(
if count < 0: if count < 0:
return False return False
self.stocktake_date = datetime.now().date() self.stocktake_date = InvenTree.helpers.current_date()
self.stocktake_user = user self.stocktake_user = user
if self.updateQuantity(count): if self.updateQuantity(count):
@ -2464,15 +2464,15 @@ class StockItemTestResult(InvenTree.models.InvenTreeMetadataModel):
) )
started_datetime = models.DateTimeField( started_datetime = models.DateTimeField(
default=datetime.now,
blank=True, blank=True,
null=True,
verbose_name=_('Started'), verbose_name=_('Started'),
help_text=_('The timestamp of the test start'), help_text=_('The timestamp of the test start'),
) )
finished_datetime = models.DateTimeField( finished_datetime = models.DateTimeField(
default=datetime.now,
blank=True, blank=True,
null=True,
verbose_name=_('Finished'), verbose_name=_('Finished'),
help_text=_('The timestamp of the test finish'), help_text=_('The timestamp of the test finish'),
) )

View File

@ -345,7 +345,7 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeTagModelSerializer):
# Add flag to indicate if the StockItem is stale # Add flag to indicate if the StockItem is stale
stale_days = common.models.InvenTreeSetting.get_setting('STOCK_STALE_DAYS') stale_days = common.models.InvenTreeSetting.get_setting('STOCK_STALE_DAYS')
stale_date = datetime.now().date() + timedelta(days=stale_days) stale_date = InvenTree.helpers.current_date() + timedelta(days=stale_days)
stale_filter = ( stale_filter = (
StockItem.IN_STOCK_FILTER StockItem.IN_STOCK_FILTER
& ~Q(expiry_date=None) & ~Q(expiry_date=None)
@ -826,7 +826,7 @@ class StockChangeStatusSerializer(serializers.Serializer):
deltas = {'status': status} deltas = {'status': status}
now = datetime.now() now = InvenTree.helpers.current_time()
# Instead of performing database updates for each item, # Instead of performing database updates for each item,
# perform bulk database updates (much more efficient) # perform bulk database updates (much more efficient)

View File

@ -53,7 +53,7 @@ def default_token_expiry():
"""Generate an expiry date for a newly created token.""" """Generate an expiry date for a newly created token."""
# TODO: Custom value for default expiry timeout # TODO: Custom value for default expiry timeout
# TODO: For now, tokens last for 1 year # TODO: For now, tokens last for 1 year
return datetime.datetime.now().date() + datetime.timedelta(days=365) return InvenTree.helpers.current_date() + datetime.timedelta(days=365)
class ApiToken(AuthToken, InvenTree.models.MetadataMixin): class ApiToken(AuthToken, InvenTree.models.MetadataMixin):
@ -163,7 +163,9 @@ class ApiToken(AuthToken, InvenTree.models.MetadataMixin):
@admin.display(boolean=True, description=_('Expired')) @admin.display(boolean=True, description=_('Expired'))
def expired(self): def expired(self):
"""Test if this token has expired.""" """Test if this token has expired."""
return self.expiry is not None and self.expiry < datetime.datetime.now().date() return (
self.expiry is not None and self.expiry < InvenTree.helpers.current_date()
)
@property @property
@admin.display(boolean=True, description=_('Active')) @admin.display(boolean=True, description=_('Active'))