Skip to content

Commit

Permalink
Timestamp issues (#6867)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
SchrodingersGat committed Mar 27, 2024
1 parent cd0d350 commit 4059d9f
Show file tree
Hide file tree
Showing 19 changed files with 116 additions and 53 deletions.
23 changes: 23 additions & 0 deletions InvenTree/InvenTree/helpers.py
Expand Up @@ -872,6 +872,29 @@ def hash_file(filename: Union[str, Path], storage: Union[Storage, None] = None):
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:
"""Return the timezone of the server as a string.
Expand Down
2 changes: 2 additions & 0 deletions InvenTree/InvenTree/settings.py
Expand Up @@ -952,6 +952,8 @@
# It generates a *lot* of cruft in the logs
if not TESTING:
USE_TZ = True # pragma: no cover
else:
USE_TZ = False

DATE_INPUT_FORMATS = ['%Y-%m-%d']

Expand Down
6 changes: 3 additions & 3 deletions InvenTree/build/models.py
Expand Up @@ -74,7 +74,7 @@ class Meta:
verbose_name = _("Build Order")
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
REFERENCE_PATTERN_SETTING = 'BUILDORDER_REFERENCE_PATTERN'
Expand Down Expand Up @@ -546,7 +546,7 @@ def complete_build(self, user):
if self.incomplete_count > 0:
return

self.completion_date = datetime.now().date()
self.completion_date = InvenTree.helpers.current_date()
self.completed_by = user
self.status = BuildStatus.COMPLETE.value
self.save()
Expand Down Expand Up @@ -628,7 +628,7 @@ def cancel_build(self, user, **kwargs):
output.delete()

# 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.status = BuildStatus.CANCELLED.value
Expand Down
5 changes: 3 additions & 2 deletions InvenTree/build/tasks.py
@@ -1,6 +1,6 @@
"""Background task definitions for the BuildOrder app"""

from datetime import datetime, timedelta
from datetime import timedelta
from decimal import Decimal
import logging

Expand All @@ -14,6 +14,7 @@
import common.notifications
import build.models
import InvenTree.email
import InvenTree.helpers
import InvenTree.helpers_model
import InvenTree.tasks
from InvenTree.status_codes import BuildStatusGroups
Expand Down Expand Up @@ -222,7 +223,7 @@ def check_overdue_build_orders():
- Look at the 'target_date' of any outstanding BuildOrder objects
- 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(
target_date=yesterday,
Expand Down
4 changes: 2 additions & 2 deletions InvenTree/common/models.py
Expand Up @@ -13,7 +13,7 @@
import os
import re
import uuid
from datetime import datetime, timedelta, timezone
from datetime import timedelta, timezone
from enum import Enum
from secrets import compare_digest
from typing import Any, Callable, TypedDict, Union
Expand Down Expand Up @@ -2915,7 +2915,7 @@ class Meta:
@classmethod
def check_recent(cls, key: str, uid: int, delta: timedelta):
"""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)

Expand Down
5 changes: 3 additions & 2 deletions InvenTree/common/tasks.py
Expand Up @@ -2,7 +2,7 @@

import logging
import os
from datetime import datetime, timedelta
from datetime import timedelta

from django.conf import settings
from django.core.exceptions import AppRegistryNotReady
Expand All @@ -12,6 +12,7 @@
import feedparser
import requests

import InvenTree.helpers
from InvenTree.helpers_model import getModelsWithMixin
from InvenTree.models import InvenTreeNotesMixin
from InvenTree.tasks import ScheduledTask, scheduled_task
Expand Down Expand Up @@ -107,7 +108,7 @@ def delete_old_notes_images():
note.delete()

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):
# Find any images which are no longer referenced by a note
Expand Down
2 changes: 1 addition & 1 deletion InvenTree/company/models.py
Expand Up @@ -901,7 +901,7 @@ def base_quantity(self, quantity=1) -> Decimal:
def update_available_quantity(self, quantity):
"""Update the available quantity for this SupplierPart."""
self.available = quantity
self.availability_updated = datetime.now()
self.availability_updated = InvenTree.helpers.current_time()
self.save()

@property
Expand Down
6 changes: 3 additions & 3 deletions InvenTree/label/models.py
@@ -1,6 +1,5 @@
"""Label printing models."""

import datetime
import logging
import os
import sys
Expand All @@ -15,6 +14,7 @@
from django.utils.translation import gettext_lazy as _

import build.models
import InvenTree.helpers
import InvenTree.models
import part.models
import stock.models
Expand Down Expand Up @@ -228,8 +228,8 @@ def context(self, request, **kwargs):

# Add "basic" context data which gets passed to every label
context['base_url'] = get_base_url(request=request)
context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now()
context['date'] = InvenTree.helpers.current_date()
context['datetime'] = InvenTree.helpers.current_time()
context['request'] = request
context['user'] = request.user
context['width'] = self.width
Expand Down
26 changes: 14 additions & 12 deletions InvenTree/order/models.py
Expand Up @@ -221,7 +221,7 @@ def save(self, *args, **kwargs):
"""
self.reference_int = self.rebuild_reference_field(self.reference)
if not self.creation_date:
self.creation_date = datetime.now().date()
self.creation_date = InvenTree.helpers.current_date()

super().save(*args, **kwargs)

Expand Down Expand Up @@ -252,7 +252,7 @@ def overdue_filter(cls):
It requires any subclasses to implement the get_status_class() class method
"""
today = datetime.now().date()
today = InvenTree.helpers.current_date()
return (
Q(status__in=cls.get_status_class().OPEN)
& ~Q(target_date=None)
Expand Down Expand Up @@ -584,7 +584,7 @@ def _action_place(self, *args, **kwargs):
"""
if self.is_pending:
self.status = PurchaseOrderStatus.PLACED.value
self.issue_date = datetime.now().date()
self.issue_date = InvenTree.helpers.current_date()
self.save()

trigger_event('purchaseorder.placed', id=self.pk)
Expand All @@ -604,7 +604,7 @@ def _action_complete(self, *args, **kwargs):
"""
if self.status == PurchaseOrderStatus.PLACED:
self.status = PurchaseOrderStatus.COMPLETE.value
self.complete_date = datetime.now().date()
self.complete_date = InvenTree.helpers.current_date()

self.save()

Expand Down Expand Up @@ -1030,7 +1030,7 @@ def _action_place(self, *args, **kwargs):
"""Change this order from 'PENDING' to 'IN_PROGRESS'."""
if self.status == SalesOrderStatus.PENDING:
self.status = SalesOrderStatus.IN_PROGRESS.value
self.issue_date = datetime.now().date()
self.issue_date = InvenTree.helpers.current_date()
self.save()

trigger_event('salesorder.issued', id=self.pk)
Expand All @@ -1044,7 +1044,7 @@ def _action_complete(self, *args, **kwargs):

self.status = SalesOrderStatus.SHIPPED.value
self.shipped_by = user
self.shipment_date = datetime.now()
self.shipment_date = InvenTree.helpers.current_date()

self.save()

Expand Down Expand Up @@ -1346,7 +1346,7 @@ class PurchaseOrderLineItem(OrderLineItem):
OVERDUE_FILTER = (
Q(received__lt=F('quantity'))
& ~Q(target_date=None)
& Q(target_date__lt=datetime.now().date())
& Q(target_date__lt=InvenTree.helpers.current_date())
)

@staticmethod
Expand Down Expand Up @@ -1505,7 +1505,7 @@ class SalesOrderLineItem(OrderLineItem):
OVERDUE_FILTER = (
Q(shipped__lt=F('quantity'))
& ~Q(target_date=None)
& Q(target_date__lt=datetime.now().date())
& Q(target_date__lt=InvenTree.helpers.current_date())
)

@staticmethod
Expand Down Expand Up @@ -1748,7 +1748,9 @@ def complete_shipment(self, user, **kwargs):
allocation.complete_allocation(user)

# 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

# Was a tracking number provided?
Expand Down Expand Up @@ -2076,7 +2078,7 @@ def _action_complete(self, *args, **kwargs):
"""Complete this ReturnOrder (if not already completed)."""
if self.status == ReturnOrderStatus.IN_PROGRESS:
self.status = ReturnOrderStatus.COMPLETE.value
self.complete_date = datetime.now().date()
self.complete_date = InvenTree.helpers.current_date()
self.save()

trigger_event('returnorder.completed', id=self.pk)
Expand All @@ -2089,7 +2091,7 @@ def _action_place(self, *args, **kwargs):
"""Issue this ReturnOrder (if currently pending)."""
if self.status == ReturnOrderStatus.PENDING:
self.status = ReturnOrderStatus.IN_PROGRESS.value
self.issue_date = datetime.now().date()
self.issue_date = InvenTree.helpers.current_date()
self.save()

trigger_event('returnorder.issued', id=self.pk)
Expand Down Expand Up @@ -2162,7 +2164,7 @@ def receive_line_item(self, line, location, user, note=''):
)

# Update the LineItem
line.received_date = datetime.now().date()
line.received_date = InvenTree.helpers.current_date()
line.save()

trigger_event('returnorder.received', id=self.pk)
Expand Down
13 changes: 10 additions & 3 deletions InvenTree/order/serializers.py
Expand Up @@ -34,7 +34,13 @@
ContactSerializer,
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 (
InvenTreeAttachmentSerializer,
InvenTreeCurrencySerializer,
Expand Down Expand Up @@ -1140,11 +1146,12 @@ def save(self):
user = request.user

# 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:
# Shipment date should not be None - check above only
# checks if shipment_date exists in data
shipment_date = datetime.now()
shipment_date = now

shipment.complete_shipment(
user,
Expand Down
5 changes: 3 additions & 2 deletions InvenTree/part/models.py
Expand Up @@ -37,6 +37,7 @@
import common.settings
import InvenTree.conversion
import InvenTree.fields
import InvenTree.helpers
import InvenTree.models
import InvenTree.ready
import InvenTree.tasks
Expand Down Expand Up @@ -1728,7 +1729,7 @@ def validate_bom(self, user):

self.bom_checksum = self.get_bom_hash()
self.bom_checked_by = user
self.bom_checked_date = datetime.now().date()
self.bom_checked_date = InvenTree.helpers.current_date()

self.save()

Expand Down Expand Up @@ -2715,7 +2716,7 @@ def update_purchase_cost(self, save=True):
)

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)

for item in items:
Expand Down
2 changes: 1 addition & 1 deletion InvenTree/part/stocktake.py
Expand Up @@ -266,7 +266,7 @@ def generate_stocktake_report(**kwargs):
buffer = io.StringIO()
buffer.write(dataset.export('csv'))

today = datetime.now().date().isoformat()
today = InvenTree.helpers.current_date().isoformat()
filename = f'InvenTree_Stocktake_{today}.csv'
report_file = ContentFile(buffer.getvalue(), name=filename)

Expand Down
5 changes: 3 additions & 2 deletions InvenTree/report/models.py
Expand Up @@ -18,6 +18,7 @@
import build.models
import common.models
import InvenTree.exceptions
import InvenTree.helpers
import InvenTree.models
import order.models
import part.models
Expand Down Expand Up @@ -250,8 +251,8 @@ def context(self, request):
context = self.get_context_data(request)

context['base_url'] = get_base_url(request=request)
context['date'] = datetime.datetime.now().date()
context['datetime'] = datetime.datetime.now()
context['date'] = InvenTree.helpers.current_date()
context['datetime'] = InvenTree.helpers.current_time()
context['page_size'] = self.get_report_size()
context['report_template'] = self
context['report_description'] = self.description
Expand Down
7 changes: 4 additions & 3 deletions InvenTree/stock/api.py
Expand Up @@ -19,6 +19,7 @@

import common.models
import common.settings
import InvenTree.helpers
import stock.serializers as StockSerializers
from build.models import Build
from build.serializers import BuildSerializer
Expand Down Expand Up @@ -810,7 +811,7 @@ def filter_stale(self, queryset, name, value):
# No filtering, does not make sense
return queryset

stale_date = datetime.now().date() + timedelta(days=stale_days)
stale_date = InvenTree.helpers.current_date() + timedelta(days=stale_days)
stale_filter = (
StockItem.IN_STOCK_FILTER
& ~Q(expiry_date=None)
Expand Down Expand Up @@ -906,7 +907,7 @@ def create(self, request, *args, **kwargs):

# An expiry date was *not* specified - try to infer it!
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
)

Expand Down Expand Up @@ -1050,7 +1051,7 @@ def download_queryset(self, queryset, 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)

Expand Down

0 comments on commit 4059d9f

Please sign in to comment.