mirror of
https://github.com/pretix/pretix.git
synced 2026-08-13 11:17:01 +00:00
Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac0bf2d5d4 | ||
|
|
90e4d5305f | ||
|
|
12f279e00f | ||
|
|
4cfcb9ede1 | ||
|
|
003652d564 | ||
|
|
1f5a66005b | ||
|
|
1e01c7ff78 | ||
|
|
5e55f53650 | ||
|
|
b9e10251bc | ||
|
|
0472101c81 | ||
|
|
b0a34fa196 | ||
|
|
99d9d65521 | ||
|
|
599318e7c7 | ||
|
|
b7dc8ad75d | ||
|
|
027aceaf6c | ||
|
|
23e37ada59 | ||
|
|
62917da9a5 | ||
|
|
d68c6415f3 | ||
|
|
4271928df2 | ||
|
|
21fc8e4dec | ||
|
|
f0d1d7ca5a | ||
|
|
b2fd6a9c1e | ||
|
|
27966969f1 | ||
|
|
64e301ef47 | ||
|
|
582cb90bb0 | ||
|
|
4db98def79 | ||
|
|
8adc649c4c | ||
|
|
8692222bd3 |
@@ -314,3 +314,18 @@ class BusinessBooleanRadio(forms.RadioSelect):
|
|||||||
'False': False,
|
'False': False,
|
||||||
False: False,
|
False: False,
|
||||||
}.get(value)
|
}.get(value)
|
||||||
|
|
||||||
|
|
||||||
|
class OptionAttrsSelect(forms.Select):
|
||||||
|
def __init__(self, *args, option_attrs=None, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.option_attrs = option_attrs or {}
|
||||||
|
|
||||||
|
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
|
||||||
|
option = super().create_option(
|
||||||
|
name, value, label, selected, index, subindex=subindex, attrs=attrs
|
||||||
|
)
|
||||||
|
extra = self.option_attrs.get(str(value))
|
||||||
|
if extra:
|
||||||
|
option["attrs"].update(extra)
|
||||||
|
return option
|
||||||
|
|||||||
+373
-155
@@ -19,30 +19,245 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
|
import os
|
||||||
|
import warnings
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from typing import Union
|
from dataclasses import dataclass
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING, Iterable, List, Literal, Optional, Tuple, Union, cast,
|
||||||
|
)
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.forms.widgets import Widget
|
||||||
from django.utils.formats import get_format
|
from django.utils.formats import get_format
|
||||||
from django.utils.functional import lazy
|
from django.utils.functional import Promise, lazy
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
BASE_CHOICES = (
|
from pretix.base.forms.widgets import OptionAttrsSelect
|
||||||
('date_from', _('Event start')),
|
|
||||||
('date_to', _('Event end')),
|
|
||||||
('date_admission', _('Event admission')),
|
|
||||||
('presale_start', _('Presale start')),
|
|
||||||
('presale_end', _('Presale end')),
|
|
||||||
)
|
|
||||||
|
|
||||||
RelativeDate = namedtuple('RelativeDate', ['days', 'minutes', 'time', 'is_after', 'base_date_name'], defaults=(0, None, None, False, 'date_from'))
|
if TYPE_CHECKING:
|
||||||
|
from .models import Event, Order, SubEvent
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class BaseChoice:
|
||||||
|
base: Literal["event", "order"]
|
||||||
|
attribute: str
|
||||||
|
text: Promise
|
||||||
|
supports_before: bool
|
||||||
|
supports_after: bool
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key(self) -> str:
|
||||||
|
key = f"{self.base}__{self.attribute}"
|
||||||
|
return key
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def find(objects: Iterable["BaseChoice"], key: str) -> "BaseChoice":
|
||||||
|
if "__" in key:
|
||||||
|
choice = next((obj for obj in objects if obj.key == key), None)
|
||||||
|
else:
|
||||||
|
# fallback for RelativeDateFields stored, before support for bases other than event was added
|
||||||
|
choice = next((obj for obj in objects if obj.attribute == key and obj.base == "event"), None)
|
||||||
|
|
||||||
|
if choice is None:
|
||||||
|
raise TypeError(f"key {key} must be a valid key in BASE_CHOICES")
|
||||||
|
|
||||||
|
return choice
|
||||||
|
|
||||||
|
|
||||||
|
BASE_CHOICES: List[BaseChoice] = [
|
||||||
|
BaseChoice('event', 'date_from', _('Event start'), True, True),
|
||||||
|
BaseChoice('event', 'date_to', _('Event end'), True, True),
|
||||||
|
BaseChoice('event', 'date_admission', _('Event admission'), True, True),
|
||||||
|
BaseChoice('event', 'presale_start', _('Presale start'), True, True),
|
||||||
|
BaseChoice('event', 'presale_end', _('Presale end'), True, True),
|
||||||
|
BaseChoice('order', 'datetime', _('Order creation'), False, True),
|
||||||
|
BaseChoice('order', 'expires', _('Order expiry'), True, True),
|
||||||
|
]
|
||||||
|
|
||||||
|
EVENT_BASE_CHOICES = [
|
||||||
|
x for x in BASE_CHOICES if x.base == 'event'
|
||||||
|
]
|
||||||
|
|
||||||
|
ORDER_BASE_CHOICES = [
|
||||||
|
x for x in BASE_CHOICES if x.base == 'order'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class RelativeDate:
|
||||||
|
"""
|
||||||
|
This contains information on a date that is defined in relation to a fixed base point.
|
||||||
|
This means that the underlying data is a fixed date as the base point and a number of days or a time interval
|
||||||
|
to calculate the date.
|
||||||
|
|
||||||
|
The list of valid base date choices is defined in BASE_CHOICES.
|
||||||
|
If the base_date_name is not set, the date_from attribute of Event is used.
|
||||||
|
"""
|
||||||
|
|
||||||
|
days: int = 0
|
||||||
|
minutes: Optional[int] = None
|
||||||
|
time: Optional[datetime.time] = None
|
||||||
|
is_after: bool = False
|
||||||
|
base_date_name: str = 'event__date_from__'
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
if self.is_after and not self._choice.supports_after:
|
||||||
|
raise ValueError(
|
||||||
|
"The selected base date and attribute combination does not support relative dates placed after the base date"
|
||||||
|
)
|
||||||
|
if not self.is_after and not self._choice.supports_before:
|
||||||
|
raise ValueError(
|
||||||
|
"The selected base date and attribute combination does not support relative dates placed before the base date")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _choice(self):
|
||||||
|
return BaseChoice.find(BASE_CHOICES, self.base_date_name)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def key(self):
|
||||||
|
return self._choice.key
|
||||||
|
|
||||||
|
def __eq__(self, o: object) -> bool:
|
||||||
|
if not isinstance(o, RelativeDate):
|
||||||
|
return False
|
||||||
|
return self.to_string() == o.to_string()
|
||||||
|
|
||||||
|
def _resolve_date(self, base: "Event | Order | SubEvent") -> Tuple[datetime.datetime, ZoneInfo]:
|
||||||
|
"""
|
||||||
|
Resolves the datetime and timezone information of this RelativeDate object in relation to the provided reference.
|
||||||
|
|
||||||
|
:param base: the reference which should be used to resolve the relative date
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
from .models import Event, Order, SubEvent
|
||||||
|
|
||||||
|
choice = self._choice
|
||||||
|
|
||||||
|
if choice.base == "order" and isinstance(base, Order):
|
||||||
|
event = base.event
|
||||||
|
base_date = getattr(base, choice.attribute)
|
||||||
|
elif choice.base == "event" and isinstance(base, SubEvent):
|
||||||
|
event = base.event
|
||||||
|
base_date = (getattr(base, choice.attribute) or
|
||||||
|
getattr(base.event, choice.attribute) or
|
||||||
|
base.date_from)
|
||||||
|
elif choice.base == "event" and isinstance(base, Event):
|
||||||
|
event = base
|
||||||
|
base_date = getattr(base, choice.attribute) or event.date_from
|
||||||
|
else:
|
||||||
|
raise TypeError("The base defined by data does not match the passed in base")
|
||||||
|
|
||||||
|
tz = ZoneInfo(event.settings.timezone)
|
||||||
|
return base_date, tz
|
||||||
|
|
||||||
|
def date(self, base: "Event | Order | SubEvent") -> datetime.date:
|
||||||
|
"""
|
||||||
|
Resolves the effective date of this RelativeDate object in relation to the provided reference.
|
||||||
|
|
||||||
|
:param base: the reference which should be used to resolve the date
|
||||||
|
:return: datetime.date
|
||||||
|
"""
|
||||||
|
if self.minutes is not None:
|
||||||
|
raise ValueError('A minute-based relative datetime can not be used as a date')
|
||||||
|
|
||||||
|
base_date, tz = self._resolve_date(base)
|
||||||
|
|
||||||
|
if self.is_after:
|
||||||
|
new_date = base_date.astimezone(tz) + datetime.timedelta(days=self.days)
|
||||||
|
else:
|
||||||
|
new_date = base_date.astimezone(tz) - datetime.timedelta(days=self.days)
|
||||||
|
return new_date.date()
|
||||||
|
|
||||||
|
def datetime(self, base: "Event | Order | SubEvent") -> datetime.datetime:
|
||||||
|
"""
|
||||||
|
Resolves the effective datetime of this RelativeDate object in relation to the provided reference.
|
||||||
|
|
||||||
|
:param base: the reference which should be used to resolve the datetime
|
||||||
|
:return: datetime.datetime
|
||||||
|
"""
|
||||||
|
base_date, tz = self._resolve_date(base)
|
||||||
|
|
||||||
|
if self.minutes is not None:
|
||||||
|
if self.is_after:
|
||||||
|
return base_date.astimezone(tz) + datetime.timedelta(minutes=self.minutes)
|
||||||
|
else:
|
||||||
|
return base_date.astimezone(tz) - datetime.timedelta(minutes=self.minutes)
|
||||||
|
else:
|
||||||
|
if self.is_after:
|
||||||
|
new_date = (base_date.astimezone(tz) + datetime.timedelta(days=self.days)).astimezone(tz)
|
||||||
|
else:
|
||||||
|
new_date = (base_date.astimezone(tz) - datetime.timedelta(days=self.days)).astimezone(tz)
|
||||||
|
if self.time:
|
||||||
|
new_date = new_date.replace(
|
||||||
|
hour=self.time.hour,
|
||||||
|
minute=self.time.minute,
|
||||||
|
second=self.time.second
|
||||||
|
)
|
||||||
|
new_date = new_date.astimezone(tz)
|
||||||
|
return new_date
|
||||||
|
|
||||||
|
def to_string(self) -> str:
|
||||||
|
if self.minutes is not None:
|
||||||
|
return 'RELDATE/minutes/{}/{}/{}'.format( #
|
||||||
|
self.minutes,
|
||||||
|
self._choice.key,
|
||||||
|
'after' if self.is_after else '',
|
||||||
|
)
|
||||||
|
return 'RELDATE/{}/{}/{}/{}'.format( #
|
||||||
|
self.days,
|
||||||
|
self.time.strftime('%H:%M:%S') if self.time else '-',
|
||||||
|
self._choice.key,
|
||||||
|
'after' if self.is_after else '',
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_string(cls, input: str):
|
||||||
|
if not input.startswith('RELDATE/'):
|
||||||
|
raise TypeError("Invalid input for RelativeDate.from_string()")
|
||||||
|
|
||||||
|
parts = input.split('/')
|
||||||
|
if parts[1] == 'minutes':
|
||||||
|
data = RelativeDate(
|
||||||
|
days=0,
|
||||||
|
minutes=int(parts[2]),
|
||||||
|
base_date_name=parts[3],
|
||||||
|
time=None,
|
||||||
|
is_after=len(parts) > 4 and parts[4] == "after",
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if parts[2] == '-':
|
||||||
|
time = None
|
||||||
|
else:
|
||||||
|
timeparts = parts[2].split(':')
|
||||||
|
time = datetime.time(hour=int(timeparts[0]), minute=int(timeparts[1]), second=int(timeparts[2]))
|
||||||
|
try:
|
||||||
|
data = RelativeDate(
|
||||||
|
days=int(parts[1] or 0),
|
||||||
|
base_date_name=parts[3],
|
||||||
|
time=time,
|
||||||
|
minutes=None,
|
||||||
|
is_after=len(parts) > 4 and parts[4] == "after",
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
data = RelativeDate(
|
||||||
|
days=0,
|
||||||
|
base_date_name=parts[3],
|
||||||
|
time=time,
|
||||||
|
minutes=None,
|
||||||
|
is_after=len(parts) > 4 and parts[4] == "after",
|
||||||
|
)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
class RelativeDateWrapper:
|
class RelativeDateWrapper:
|
||||||
@@ -50,130 +265,53 @@ class RelativeDateWrapper:
|
|||||||
This contains information on a date that might be relative to an event. This means
|
This contains information on a date that might be relative to an event. This means
|
||||||
that the underlying data is either a fixed date or a number of days and a wall clock
|
that the underlying data is either a fixed date or a number of days and a wall clock
|
||||||
time to calculate the date based on a base point.
|
time to calculate the date based on a base point.
|
||||||
|
|
||||||
The base point can be the date_from, date_to, date_admission, presale_start or presale_end
|
|
||||||
attribute of an event or subevent. If the respective attribute is not set, ``date_from``
|
|
||||||
will be used.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, data: Union[datetime.datetime, RelativeDate]):
|
def __init__(self, data: Union[datetime.datetime, RelativeDate]):
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
def date(self, event) -> datetime.date:
|
def date(self, base: "Event | Order | SubEvent") -> datetime.date:
|
||||||
from .models import SubEvent
|
"""
|
||||||
|
If the RelativeDateWrapper wraps a RelativeDate object:
|
||||||
|
Resolves the effective date of this object in relation to the provided reference.
|
||||||
|
If the RelativeDateWrapper wraps an absolute date or datetime:
|
||||||
|
Returns the wrapped absolute date.
|
||||||
|
|
||||||
|
:param base: the reference which should be used to resolve the date in case it is a relative date.
|
||||||
|
:return: datetime.date
|
||||||
|
"""
|
||||||
if isinstance(self.data, datetime.datetime):
|
if isinstance(self.data, datetime.datetime):
|
||||||
return self.data.date()
|
return self.data.date()
|
||||||
elif isinstance(self.data, datetime.date):
|
elif isinstance(self.data, datetime.date):
|
||||||
return self.data
|
return self.data
|
||||||
else:
|
else:
|
||||||
if self.data.minutes is not None:
|
return self.data.date(base)
|
||||||
raise ValueError('A minute-based relative datetime can not be used as a date')
|
|
||||||
|
|
||||||
tz = ZoneInfo(event.settings.timezone)
|
def datetime(self, base: "Event | Order | SubEvent") -> datetime.datetime:
|
||||||
if isinstance(event, SubEvent):
|
"""
|
||||||
base_date = (
|
If the RelativeDateWrapper wraps a RelativeDate object:
|
||||||
getattr(event, self.data.base_date_name)
|
Resolves the effective datetime of this object in relation to the provided reference.
|
||||||
or getattr(event.event, self.data.base_date_name)
|
If the RelativeDateWrapper wraps an absolute date or datetime:
|
||||||
or event.date_from
|
Returns the wrapped absolute datetime.
|
||||||
)
|
|
||||||
else:
|
|
||||||
base_date = getattr(event, self.data.base_date_name) or event.date_from
|
|
||||||
|
|
||||||
if self.data.is_after:
|
|
||||||
new_date = base_date.astimezone(tz) + datetime.timedelta(days=self.data.days)
|
|
||||||
else:
|
|
||||||
new_date = base_date.astimezone(tz) - datetime.timedelta(days=self.data.days)
|
|
||||||
return new_date.date()
|
|
||||||
|
|
||||||
def datetime(self, event) -> datetime.datetime:
|
|
||||||
from .models import SubEvent
|
|
||||||
|
|
||||||
|
:param base: the reference which should be used to resolve the datetime in case it is a relative datetime.
|
||||||
|
:return: datetime.datetime
|
||||||
|
"""
|
||||||
if isinstance(self.data, (datetime.datetime, datetime.date)):
|
if isinstance(self.data, (datetime.datetime, datetime.date)):
|
||||||
return self.data
|
return self.data
|
||||||
else:
|
else:
|
||||||
tz = ZoneInfo(event.settings.timezone)
|
return self.data.datetime(base)
|
||||||
if isinstance(event, SubEvent):
|
|
||||||
base_date = (
|
|
||||||
getattr(event, self.data.base_date_name)
|
|
||||||
or getattr(event.event, self.data.base_date_name)
|
|
||||||
or event.date_from
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
base_date = getattr(event, self.data.base_date_name) or event.date_from
|
|
||||||
|
|
||||||
if self.data.minutes is not None:
|
|
||||||
if self.data.is_after:
|
|
||||||
return base_date.astimezone(tz) + datetime.timedelta(minutes=self.data.minutes)
|
|
||||||
else:
|
|
||||||
return base_date.astimezone(tz) - datetime.timedelta(minutes=self.data.minutes)
|
|
||||||
else:
|
|
||||||
if self.data.is_after:
|
|
||||||
new_date = (base_date.astimezone(tz) + datetime.timedelta(days=self.data.days)).astimezone(tz)
|
|
||||||
else:
|
|
||||||
new_date = (base_date.astimezone(tz) - datetime.timedelta(days=self.data.days)).astimezone(tz)
|
|
||||||
if self.data.time:
|
|
||||||
new_date = new_date.replace(
|
|
||||||
hour=self.data.time.hour,
|
|
||||||
minute=self.data.time.minute,
|
|
||||||
second=self.data.time.second
|
|
||||||
)
|
|
||||||
new_date = new_date.astimezone(tz)
|
|
||||||
return new_date
|
|
||||||
|
|
||||||
def to_string(self) -> str:
|
def to_string(self) -> str:
|
||||||
if isinstance(self.data, (datetime.datetime, datetime.date)):
|
if isinstance(self.data, (datetime.datetime, datetime.date)):
|
||||||
return self.data.isoformat()
|
return self.data.isoformat()
|
||||||
else:
|
else:
|
||||||
if self.data.minutes is not None:
|
return self.data.to_string()
|
||||||
return 'RELDATE/minutes/{}/{}/{}'.format( #
|
|
||||||
self.data.minutes,
|
|
||||||
self.data.base_date_name,
|
|
||||||
'after' if self.data.is_after else '',
|
|
||||||
)
|
|
||||||
return 'RELDATE/{}/{}/{}/{}'.format( #
|
|
||||||
self.data.days,
|
|
||||||
self.data.time.strftime('%H:%M:%S') if self.data.time else '-',
|
|
||||||
self.data.base_date_name,
|
|
||||||
'after' if self.data.is_after else '',
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_string(cls, input: str):
|
def from_string(cls, input: str):
|
||||||
if input.startswith('RELDATE/'):
|
if input.startswith('RELDATE/'):
|
||||||
parts = input.split('/')
|
data = RelativeDate.from_string(input)
|
||||||
if parts[1] == 'minutes':
|
|
||||||
data = RelativeDate(
|
|
||||||
days=0,
|
|
||||||
minutes=int(parts[2]),
|
|
||||||
base_date_name=parts[3],
|
|
||||||
time=None,
|
|
||||||
is_after=len(parts) > 4 and parts[4] == "after",
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
if parts[2] == '-':
|
|
||||||
time = None
|
|
||||||
else:
|
|
||||||
timeparts = parts[2].split(':')
|
|
||||||
time = datetime.time(hour=int(timeparts[0]), minute=int(timeparts[1]), second=int(timeparts[2]))
|
|
||||||
try:
|
|
||||||
data = RelativeDate(
|
|
||||||
days=int(parts[1] or 0),
|
|
||||||
base_date_name=parts[3],
|
|
||||||
time=time,
|
|
||||||
minutes=None,
|
|
||||||
is_after=len(parts) > 4 and parts[4] == "after",
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
data = RelativeDate(
|
|
||||||
days=0,
|
|
||||||
base_date_name=parts[3],
|
|
||||||
time=time,
|
|
||||||
minutes=None,
|
|
||||||
is_after=len(parts) > 4 and parts[4] == "after",
|
|
||||||
)
|
|
||||||
if data.base_date_name not in [k[0] for k in BASE_CHOICES]:
|
|
||||||
raise ValueError('{} is not a valid base date'.format(data.base_date_name))
|
|
||||||
else:
|
else:
|
||||||
data = parser.parse(input)
|
data = parser.parse(input)
|
||||||
return RelativeDateWrapper(data)
|
return RelativeDateWrapper(data)
|
||||||
@@ -187,7 +325,6 @@ BEFORE_AFTER_CHOICE = (
|
|||||||
('after', _('after')),
|
('after', _('after')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
reldatetimeparts = namedtuple('reldatetimeparts', (
|
reldatetimeparts = namedtuple('reldatetimeparts', (
|
||||||
"status", # 0
|
"status", # 0
|
||||||
"absolute", # 1
|
"absolute", # 1
|
||||||
@@ -202,6 +339,14 @@ reldatetimeparts = namedtuple('reldatetimeparts', (
|
|||||||
reldatetimeparts.indizes = reldatetimeparts(*range(9))
|
reldatetimeparts.indizes = reldatetimeparts(*range(9))
|
||||||
|
|
||||||
|
|
||||||
|
def _get_choices(base_choices: List[BaseChoice]) -> List[Tuple[str, Promise]]:
|
||||||
|
return [(c.key, c.text) for c in base_choices]
|
||||||
|
|
||||||
|
|
||||||
|
def _get_choice_validation_obj(choices: List[BaseChoice]):
|
||||||
|
return {c.key: {"data-supports-before": c.supports_before, "data-supports-after": c.supports_after} for c in choices}
|
||||||
|
|
||||||
|
|
||||||
class RelativeDateTimeWidget(forms.MultiWidget):
|
class RelativeDateTimeWidget(forms.MultiWidget):
|
||||||
template_name = 'pretixbase/forms/widgets/reldatetime.html'
|
template_name = 'pretixbase/forms/widgets/reldatetime.html'
|
||||||
parts = reldatetimeparts
|
parts = reldatetimeparts
|
||||||
@@ -209,6 +354,7 @@ class RelativeDateTimeWidget(forms.MultiWidget):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.status_choices = kwargs.pop('status_choices')
|
self.status_choices = kwargs.pop('status_choices')
|
||||||
base_choices = kwargs.pop('base_choices')
|
base_choices = kwargs.pop('base_choices')
|
||||||
|
choices = _get_choices(base_choices)
|
||||||
|
|
||||||
def placeholder_datetime_format():
|
def placeholder_datetime_format():
|
||||||
df = get_format('DATETIME_INPUT_FORMATS')[0]
|
df = get_format('DATETIME_INPUT_FORMATS')[0]
|
||||||
@@ -220,21 +366,29 @@ class RelativeDateTimeWidget(forms.MultiWidget):
|
|||||||
tf = get_format('TIME_INPUT_FORMATS')[0]
|
tf = get_format('TIME_INPUT_FORMATS')[0]
|
||||||
return datetime.time(8, 30, 0).strftime(tf)
|
return datetime.time(8, 30, 0).strftime(tf)
|
||||||
|
|
||||||
widgets = reldatetimeparts(
|
widgets = cast(dict[str, Widget | type[Widget]], cast(object, reldatetimeparts(
|
||||||
status=forms.RadioSelect(choices=self.status_choices),
|
status=forms.RadioSelect(choices=self.status_choices),
|
||||||
absolute=forms.DateTimeInput(
|
absolute=forms.DateTimeInput(
|
||||||
attrs={'placeholder': lazy(placeholder_datetime_format, str), 'class': 'datetimepicker'}
|
attrs={'placeholder': lazy(placeholder_datetime_format, str), 'class': 'datetimepicker'}
|
||||||
),
|
),
|
||||||
rel_days_number=forms.NumberInput(),
|
rel_days_number=forms.NumberInput(),
|
||||||
rel_mins_relationto=forms.Select(choices=base_choices),
|
rel_mins_relationto=OptionAttrsSelect(
|
||||||
|
attrs={'data-relative-choice': True},
|
||||||
|
choices=choices,
|
||||||
|
option_attrs=_get_choice_validation_obj(base_choices)
|
||||||
|
),
|
||||||
rel_days_timeofday=forms.TimeInput(
|
rel_days_timeofday=forms.TimeInput(
|
||||||
attrs={'placeholder': lazy(placeholder_time_format, str), 'class': 'timepickerfield'}
|
attrs={'placeholder': lazy(placeholder_time_format, str), 'class': 'timepickerfield'}
|
||||||
),
|
),
|
||||||
rel_mins_number=forms.NumberInput(),
|
rel_mins_number=forms.NumberInput(),
|
||||||
rel_days_relationto=forms.Select(choices=base_choices),
|
rel_days_relationto=OptionAttrsSelect(
|
||||||
rel_mins_relation=forms.Select(choices=BEFORE_AFTER_CHOICE),
|
attrs={'data-relative-choice': True},
|
||||||
rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE),
|
choices=choices,
|
||||||
)
|
option_attrs=_get_choice_validation_obj(base_choices)
|
||||||
|
),
|
||||||
|
rel_mins_relation=forms.Select(attrs={'data-relation-choice': True}, choices=BEFORE_AFTER_CHOICE),
|
||||||
|
rel_days_relation=forms.Select(attrs={'data-relation-choice': True}, choices=BEFORE_AFTER_CHOICE),
|
||||||
|
)))
|
||||||
super().__init__(widgets=widgets, *args, **kwargs)
|
super().__init__(widgets=widgets, *args, **kwargs)
|
||||||
|
|
||||||
def decompress(self, value):
|
def decompress(self, value):
|
||||||
@@ -247,10 +401,10 @@ class RelativeDateTimeWidget(forms.MultiWidget):
|
|||||||
status="unset",
|
status="unset",
|
||||||
absolute=None,
|
absolute=None,
|
||||||
rel_days_number=1,
|
rel_days_number=1,
|
||||||
rel_mins_relationto="date_from",
|
rel_mins_relationto="event__date_from",
|
||||||
rel_days_timeofday=None,
|
rel_days_timeofday=None,
|
||||||
rel_mins_number=0,
|
rel_mins_number=0,
|
||||||
rel_days_relationto="date_from",
|
rel_days_relationto="event__date_from",
|
||||||
rel_mins_relation="before",
|
rel_mins_relation="before",
|
||||||
rel_days_relation="before"
|
rel_days_relation="before"
|
||||||
)
|
)
|
||||||
@@ -259,10 +413,10 @@ class RelativeDateTimeWidget(forms.MultiWidget):
|
|||||||
status="absolute",
|
status="absolute",
|
||||||
absolute=value.data,
|
absolute=value.data,
|
||||||
rel_days_number=1,
|
rel_days_number=1,
|
||||||
rel_mins_relationto="date_from",
|
rel_mins_relationto="event__date_from",
|
||||||
rel_days_timeofday=None,
|
rel_days_timeofday=None,
|
||||||
rel_mins_number=0,
|
rel_mins_number=0,
|
||||||
rel_days_relationto="date_from",
|
rel_days_relationto="event__date_from",
|
||||||
rel_mins_relation="before",
|
rel_mins_relation="before",
|
||||||
rel_days_relation="before"
|
rel_days_relation="before"
|
||||||
)
|
)
|
||||||
@@ -271,10 +425,10 @@ class RelativeDateTimeWidget(forms.MultiWidget):
|
|||||||
status="relative_minutes",
|
status="relative_minutes",
|
||||||
absolute=None,
|
absolute=None,
|
||||||
rel_days_number=None,
|
rel_days_number=None,
|
||||||
rel_mins_relationto=value.data.base_date_name,
|
rel_mins_relationto=value.data.key,
|
||||||
rel_days_timeofday=None,
|
rel_days_timeofday=None,
|
||||||
rel_mins_number=value.data.minutes,
|
rel_mins_number=value.data.minutes,
|
||||||
rel_days_relationto=value.data.base_date_name,
|
rel_days_relationto=value.data.key,
|
||||||
rel_mins_relation="after" if value.data.is_after else "before",
|
rel_mins_relation="after" if value.data.is_after else "before",
|
||||||
rel_days_relation="after" if value.data.is_after else "before"
|
rel_days_relation="after" if value.data.is_after else "before"
|
||||||
)
|
)
|
||||||
@@ -282,10 +436,10 @@ class RelativeDateTimeWidget(forms.MultiWidget):
|
|||||||
status="relative",
|
status="relative",
|
||||||
absolute=None,
|
absolute=None,
|
||||||
rel_days_number=value.data.days,
|
rel_days_number=value.data.days,
|
||||||
rel_mins_relationto=value.data.base_date_name,
|
rel_mins_relationto=value.data.key,
|
||||||
rel_days_timeofday=value.data.time,
|
rel_days_timeofday=value.data.time,
|
||||||
rel_mins_number=0,
|
rel_mins_number=0,
|
||||||
rel_days_relationto=value.data.base_date_name,
|
rel_days_relationto=value.data.key,
|
||||||
rel_mins_relation="after" if value.data.is_after else "before",
|
rel_mins_relation="after" if value.data.is_after else "before",
|
||||||
rel_days_relation="after" if value.data.is_after else "before"
|
rel_days_relation="after" if value.data.is_after else "before"
|
||||||
)
|
)
|
||||||
@@ -309,17 +463,41 @@ class RelativeDateTimeField(forms.MultiValueField):
|
|||||||
('relative', _('Relative date:')),
|
('relative', _('Relative date:')),
|
||||||
('relative_minutes', _('Relative time:')),
|
('relative_minutes', _('Relative time:')),
|
||||||
]
|
]
|
||||||
|
self.relative_to_order = kwargs.pop('relative_to_order', False)
|
||||||
|
|
||||||
|
possible_choices = copy.deepcopy(EVENT_BASE_CHOICES)
|
||||||
|
if self.relative_to_order:
|
||||||
|
possible_choices.extend(ORDER_BASE_CHOICES)
|
||||||
|
|
||||||
if kwargs.get('limit_choices'):
|
if kwargs.get('limit_choices'):
|
||||||
limit = kwargs.pop('limit_choices')
|
limit = kwargs.pop('limit_choices')
|
||||||
choices = [(k, v) for k, v in BASE_CHOICES if k in limit]
|
if any(["__" not in l for l in limit]):
|
||||||
else:
|
_warn_skips = (str(os.path.dirname(__file__)),)
|
||||||
choices = BASE_CHOICES
|
|
||||||
|
warnings.warn(
|
||||||
|
message="Please prefix limit_choices with the base the attributes refer to, for example event__date_from",
|
||||||
|
category=DeprecationWarning,
|
||||||
|
skip_file_prefixes=_warn_skips
|
||||||
|
)
|
||||||
|
|
||||||
|
possible_choices = [
|
||||||
|
c for c in possible_choices if
|
||||||
|
# new base case as we want limit_choices to be expressed as base__attribute
|
||||||
|
(c.key in limit) or
|
||||||
|
# fallback for old event based entries
|
||||||
|
# if the base is an event, then using only attribute is fine
|
||||||
|
(c.base == "event" and c.attribute in limit)
|
||||||
|
]
|
||||||
|
|
||||||
if not kwargs.get('required', True):
|
if not kwargs.get('required', True):
|
||||||
status_choices.insert(0, ('unset', _('Not set')))
|
status_choices.insert(0, ('unset', _('Not set')))
|
||||||
|
|
||||||
|
choices = _get_choices(possible_choices)
|
||||||
|
|
||||||
fields = reldatetimeparts(
|
fields = reldatetimeparts(
|
||||||
status=forms.ChoiceField(
|
status=forms.ChoiceField(
|
||||||
choices=status_choices,
|
choices=status_choices,
|
||||||
required=True
|
required=True,
|
||||||
),
|
),
|
||||||
absolute=forms.DateTimeField(
|
absolute=forms.DateTimeField(
|
||||||
required=False
|
required=False
|
||||||
@@ -329,7 +507,7 @@ class RelativeDateTimeField(forms.MultiValueField):
|
|||||||
),
|
),
|
||||||
rel_mins_relationto=forms.ChoiceField(
|
rel_mins_relationto=forms.ChoiceField(
|
||||||
choices=choices,
|
choices=choices,
|
||||||
required=False
|
required=False,
|
||||||
),
|
),
|
||||||
rel_days_timeofday=forms.TimeField(
|
rel_days_timeofday=forms.TimeField(
|
||||||
required=False,
|
required=False,
|
||||||
@@ -339,7 +517,7 @@ class RelativeDateTimeField(forms.MultiValueField):
|
|||||||
),
|
),
|
||||||
rel_days_relationto=forms.ChoiceField(
|
rel_days_relationto=forms.ChoiceField(
|
||||||
choices=choices,
|
choices=choices,
|
||||||
required=False
|
required=False,
|
||||||
),
|
),
|
||||||
rel_mins_relation=forms.ChoiceField(
|
rel_mins_relation=forms.ChoiceField(
|
||||||
choices=BEFORE_AFTER_CHOICE,
|
choices=BEFORE_AFTER_CHOICE,
|
||||||
@@ -350,8 +528,9 @@ class RelativeDateTimeField(forms.MultiValueField):
|
|||||||
required=False
|
required=False
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'widget' not in kwargs:
|
if 'widget' not in kwargs:
|
||||||
kwargs['widget'] = RelativeDateTimeWidget(status_choices=status_choices, base_choices=choices)
|
kwargs['widget'] = RelativeDateTimeWidget(status_choices=status_choices, base_choices=possible_choices)
|
||||||
kwargs.pop('max_length', 0)
|
kwargs.pop('max_length', 0)
|
||||||
kwargs.pop('empty_value', 0)
|
kwargs.pop('empty_value', 0)
|
||||||
super().__init__(
|
super().__init__(
|
||||||
@@ -359,21 +538,24 @@ class RelativeDateTimeField(forms.MultiValueField):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def set_event(self, event):
|
def set_event(self, event):
|
||||||
self.widget.widgets[reldatetimeparts.indizes.rel_days_relationto].choices = [
|
possible_choices = copy.deepcopy(EVENT_BASE_CHOICES)
|
||||||
(k, v) for k, v in BASE_CHOICES if getattr(event, k, None)
|
if self.relative_to_order:
|
||||||
]
|
possible_choices.extend(ORDER_BASE_CHOICES)
|
||||||
self.widget.widgets[reldatetimeparts.indizes.rel_mins_relationto].choices = [
|
|
||||||
(k, v) for k, v in BASE_CHOICES if getattr(event, k, None)
|
possible_choices = possible_choices
|
||||||
]
|
choices = _get_choices(possible_choices)
|
||||||
|
|
||||||
|
self.widget.widgets[reldatetimeparts.indizes.rel_days_relationto].choices = choices
|
||||||
|
self.widget.widgets[reldatetimeparts.indizes.rel_mins_relationto].choices = choices
|
||||||
|
|
||||||
def compress(self, data_list):
|
def compress(self, data_list):
|
||||||
if not data_list:
|
if not data_list:
|
||||||
return None
|
return None
|
||||||
data = reldatetimeparts(*data_list)
|
data = reldatetimeparts(*data_list)
|
||||||
if data.status == 'absolute':
|
if data.status == 'unset':
|
||||||
return RelativeDateWrapper(data.absolute)
|
|
||||||
elif data.status == 'unset':
|
|
||||||
return None
|
return None
|
||||||
|
elif data.status == 'absolute':
|
||||||
|
return RelativeDateWrapper(data.absolute)
|
||||||
elif data.status == 'relative_minutes':
|
elif data.status == 'relative_minutes':
|
||||||
return RelativeDateWrapper(RelativeDate(
|
return RelativeDateWrapper(RelativeDate(
|
||||||
days=0,
|
days=0,
|
||||||
@@ -404,6 +586,18 @@ class RelativeDateTimeField(forms.MultiValueField):
|
|||||||
raise ValidationError(self.error_messages['incomplete'])
|
raise ValidationError(self.error_messages['incomplete'])
|
||||||
elif data.status == 'relative_minutes' and (data.rel_mins_number is None or not data.rel_mins_relationto):
|
elif data.status == 'relative_minutes' and (data.rel_mins_number is None or not data.rel_mins_relationto):
|
||||||
raise ValidationError(self.error_messages['incomplete'])
|
raise ValidationError(self.error_messages['incomplete'])
|
||||||
|
elif data.status == 'relative':
|
||||||
|
choice = BaseChoice.find(BASE_CHOICES, data.rel_days_relationto)
|
||||||
|
if data.rel_days_relation == "before" and not choice.supports_before:
|
||||||
|
raise ValidationError(_('A relative date cannot be expressed as "before" for "{}"'.format(choice.text)))
|
||||||
|
elif data.status == 'relative' and data.rel_days_relation == "after" and not choice.supports_after:
|
||||||
|
raise ValidationError(_('A relative date cannot be expressed as "after" for "{}"'.format(choice.text)))
|
||||||
|
elif data.status == 'relative_minutes':
|
||||||
|
choice = BaseChoice.find(BASE_CHOICES, data.rel_days_relationto)
|
||||||
|
if data.rel_days_relation == "before" and not choice.supports_before:
|
||||||
|
raise ValidationError(_('A relative time cannot be expressed as "before" for "{}"'.format(choice.text)))
|
||||||
|
elif data.rel_days_relation == "after" and not choice.supports_after:
|
||||||
|
raise ValidationError(_('A relative time cannot be expressed as "after" for "{}"'.format(choice.text)))
|
||||||
|
|
||||||
return super().clean(value)
|
return super().clean(value)
|
||||||
|
|
||||||
@@ -424,15 +618,20 @@ class RelativeDateWidget(RelativeDateTimeWidget):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.status_choices = kwargs.pop('status_choices')
|
self.status_choices = kwargs.pop('status_choices')
|
||||||
widgets = reldateparts(
|
self.base_choices = kwargs.pop('base_choices')
|
||||||
|
widgets = cast(dict[str, Widget | type[Widget]], cast(object, reldateparts(
|
||||||
status=forms.RadioSelect(choices=self.status_choices),
|
status=forms.RadioSelect(choices=self.status_choices),
|
||||||
absolute=forms.DateInput(
|
absolute=forms.DateInput(
|
||||||
attrs={'class': 'datepickerfield'}
|
attrs={'class': 'datepickerfield'}
|
||||||
),
|
),
|
||||||
rel_days_number=forms.NumberInput(),
|
rel_days_number=forms.NumberInput(),
|
||||||
rel_days_relationto=forms.Select(choices=kwargs.pop('base_choices')),
|
rel_days_relationto=OptionAttrsSelect(
|
||||||
rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE),
|
choices=self.base_choices,
|
||||||
)
|
option_attrs=_get_choice_validation_obj(self.base_choices),
|
||||||
|
attrs={'data-relative-choice': True},
|
||||||
|
),
|
||||||
|
rel_days_relation=forms.Select(choices=BEFORE_AFTER_CHOICE, attrs={'data-relation-choice': True},),
|
||||||
|
)))
|
||||||
forms.MultiWidget.__init__(self, widgets=widgets, *args, **kwargs)
|
forms.MultiWidget.__init__(self, widgets=widgets, *args, **kwargs)
|
||||||
|
|
||||||
def decompress(self, value):
|
def decompress(self, value):
|
||||||
@@ -460,7 +659,7 @@ class RelativeDateWidget(RelativeDateTimeWidget):
|
|||||||
status="relative",
|
status="relative",
|
||||||
absolute=None,
|
absolute=None,
|
||||||
rel_days_number=value.data.days,
|
rel_days_number=value.data.days,
|
||||||
rel_days_relationto=value.data.base_date_name,
|
rel_days_relationto=value.data.key,
|
||||||
rel_days_relation="after" if value.data.is_after else "before"
|
rel_days_relation="after" if value.data.is_after else "before"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -474,6 +673,15 @@ class RelativeDateField(RelativeDateTimeField):
|
|||||||
]
|
]
|
||||||
if not kwargs.get('required', True):
|
if not kwargs.get('required', True):
|
||||||
status_choices.insert(0, ('unset', _('Not set')))
|
status_choices.insert(0, ('unset', _('Not set')))
|
||||||
|
|
||||||
|
self.relative_to_order = kwargs.pop('relative_to_order', False)
|
||||||
|
|
||||||
|
possible_choices = copy.deepcopy(EVENT_BASE_CHOICES)
|
||||||
|
if self.relative_to_order:
|
||||||
|
possible_choices.extend(ORDER_BASE_CHOICES)
|
||||||
|
|
||||||
|
choices = _get_choices(possible_choices)
|
||||||
|
|
||||||
fields = reldateparts(
|
fields = reldateparts(
|
||||||
status=forms.ChoiceField(
|
status=forms.ChoiceField(
|
||||||
choices=status_choices,
|
choices=status_choices,
|
||||||
@@ -486,33 +694,37 @@ class RelativeDateField(RelativeDateTimeField):
|
|||||||
required=False
|
required=False
|
||||||
),
|
),
|
||||||
rel_days_relationto=forms.ChoiceField(
|
rel_days_relationto=forms.ChoiceField(
|
||||||
choices=BASE_CHOICES,
|
choices=choices,
|
||||||
required=False
|
required=False,
|
||||||
),
|
),
|
||||||
rel_days_relation=forms.ChoiceField(
|
rel_days_relation=forms.ChoiceField(
|
||||||
choices=BEFORE_AFTER_CHOICE,
|
choices=BEFORE_AFTER_CHOICE,
|
||||||
required=False
|
required=False
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
if 'widget' not in kwargs:
|
if 'widget' not in kwargs:
|
||||||
kwargs['widget'] = RelativeDateWidget(status_choices=status_choices, base_choices=BASE_CHOICES)
|
kwargs['widget'] = RelativeDateWidget(status_choices=status_choices, base_choices=possible_choices)
|
||||||
forms.MultiValueField.__init__(
|
forms.MultiValueField.__init__(
|
||||||
self, fields=fields, require_all_fields=False, *args, **kwargs
|
self, fields=fields, require_all_fields=False, *args, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def set_event(self, event):
|
def set_event(self, event):
|
||||||
self.widget.widgets[reldateparts.indizes.rel_days_relationto].choices = [
|
choices = [
|
||||||
(k, v) for k, v in BASE_CHOICES if getattr(event, k, None)
|
(c.key, c.text) for c in EVENT_BASE_CHOICES if getattr(event, c.attribute, None)
|
||||||
]
|
]
|
||||||
|
if self.relative_to_order:
|
||||||
|
choices += [(c.key, c.text) for c in ORDER_BASE_CHOICES]
|
||||||
|
self.widget.widgets[reldateparts.indizes.rel_days_relationto].choices = choices
|
||||||
|
|
||||||
def compress(self, data_list):
|
def compress(self, data_list):
|
||||||
if not data_list:
|
if not data_list:
|
||||||
return None
|
return None
|
||||||
data = reldateparts(*data_list)
|
data = reldateparts(*data_list)
|
||||||
if data.status == 'absolute':
|
if data.status == 'unset':
|
||||||
return RelativeDateWrapper(data.absolute)
|
|
||||||
elif data.status == 'unset':
|
|
||||||
return None
|
return None
|
||||||
|
elif data.status == 'absolute':
|
||||||
|
return RelativeDateWrapper(data.absolute)
|
||||||
else:
|
else:
|
||||||
return RelativeDateWrapper(RelativeDate(
|
return RelativeDateWrapper(RelativeDate(
|
||||||
days=data.rel_days_number,
|
days=data.rel_days_number,
|
||||||
@@ -525,8 +737,14 @@ class RelativeDateField(RelativeDateTimeField):
|
|||||||
data = reldateparts(*value)
|
data = reldateparts(*value)
|
||||||
if data.status == 'absolute' and not data.absolute:
|
if data.status == 'absolute' and not data.absolute:
|
||||||
raise ValidationError(self.error_messages['incomplete'])
|
raise ValidationError(self.error_messages['incomplete'])
|
||||||
elif data.status == 'relative' and (data.rel_days_number is None or not data.rel_days_relationto):
|
if data.status == 'relative':
|
||||||
raise ValidationError(self.error_messages['incomplete'])
|
choice = BaseChoice.find(BASE_CHOICES, data.rel_days_relationto)
|
||||||
|
if data.rel_days_number is None or not data.rel_days_relationto:
|
||||||
|
raise ValidationError(self.error_messages['incomplete'])
|
||||||
|
elif data.rel_days_relation == "before" and not choice.supports_before:
|
||||||
|
raise ValidationError(_("A relative date cannot be expressed as 'before' for '{}'".format(choice.text)))
|
||||||
|
elif data.rel_days_relation == "after" and not choice.supports_after:
|
||||||
|
raise ValidationError(_("A relative date cannot be expressed as 'after' for '{}'".format(choice.text)))
|
||||||
|
|
||||||
return forms.MultiValueField.clean(self, value)
|
return forms.MultiValueField.clean(self, value)
|
||||||
|
|
||||||
|
|||||||
@@ -94,14 +94,14 @@ class SubEventBulkForm(SubEventForm):
|
|||||||
label=_('Start of presale'),
|
label=_('Start of presale'),
|
||||||
help_text=_('Optional. No products will be sold before this date.'),
|
help_text=_('Optional. No products will be sold before this date.'),
|
||||||
required=False,
|
required=False,
|
||||||
limit_choices=('date_from', 'date_to'),
|
limit_choices=('event__date_from', 'event__date_to'),
|
||||||
)
|
)
|
||||||
rel_presale_end = RelativeDateTimeField(
|
rel_presale_end = RelativeDateTimeField(
|
||||||
label=_('End of presale'),
|
label=_('End of presale'),
|
||||||
help_text=_('Optional. No products will be sold after this date. If you do not set this value, the presale '
|
help_text=_('Optional. No products will be sold after this date. If you do not set this value, the presale '
|
||||||
'will end after the end date of your event.'),
|
'will end after the end date of your event.'),
|
||||||
required=False,
|
required=False,
|
||||||
limit_choices=('date_from', 'date_to'),
|
limit_choices=('event__date_from', 'event__date_to'),
|
||||||
)
|
)
|
||||||
skip_if_overlap = forms.BooleanField(
|
skip_if_overlap = forms.BooleanField(
|
||||||
label=pgettext_lazy('subevent', 'Skip dates that overlap with any existing date'),
|
label=pgettext_lazy('subevent', 'Skip dates that overlap with any existing date'),
|
||||||
@@ -333,12 +333,12 @@ class BulkSubEventItemForm(SubEventItemForm):
|
|||||||
rel_available_from = RelativeDateTimeField(
|
rel_available_from = RelativeDateTimeField(
|
||||||
label=_('Available from'),
|
label=_('Available from'),
|
||||||
required=False,
|
required=False,
|
||||||
limit_choices=('date_from', 'date_to'),
|
limit_choices=('event__date_from', 'event__date_to'),
|
||||||
)
|
)
|
||||||
rel_available_until = RelativeDateTimeField(
|
rel_available_until = RelativeDateTimeField(
|
||||||
label=_('Available until'),
|
label=_('Available until'),
|
||||||
required=False,
|
required=False,
|
||||||
limit_choices=('date_from', 'date_to'),
|
limit_choices=('event__date_from', 'event__date_to'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@@ -355,12 +355,12 @@ class BulkSubEventItemVariationForm(SubEventItemVariationForm):
|
|||||||
rel_available_from = RelativeDateTimeField(
|
rel_available_from = RelativeDateTimeField(
|
||||||
label=_('Available from'),
|
label=_('Available from'),
|
||||||
required=False,
|
required=False,
|
||||||
limit_choices=('date_from', 'date_to'),
|
limit_choices=('event__date_from', 'event__date_to'),
|
||||||
)
|
)
|
||||||
rel_available_until = RelativeDateTimeField(
|
rel_available_until = RelativeDateTimeField(
|
||||||
label=_('Available_until'),
|
label=_('Available_until'),
|
||||||
required=False,
|
required=False,
|
||||||
limit_choices=('date_from', 'date_to'),
|
limit_choices=('event__date_from', 'event__date_to'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
<script type="text/javascript" src="{% static "leaflet/leaflet.js" %}"></script>
|
<script type="text/javascript" src="{% static "leaflet/leaflet.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/geo.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/geo.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixbase/js/details.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixbase/js/details.js" %}"></script>
|
||||||
|
<script type="text/javascript" src="{% static "pretixbase/js/reldate.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "pretixbase/js/asynctask.js" %}"></script>
|
<script type="text/javascript" src="{% static "pretixbase/js/asynctask.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "sortable/Sortable.js" %}"></script>
|
<script type="text/javascript" src="{% static "sortable/Sortable.js" %}"></script>
|
||||||
<script type="text/javascript" src="{% static "colorpicker/bootstrap-colorpicker.js" %}"></script>
|
<script type="text/javascript" src="{% static "colorpicker/bootstrap-colorpicker.js" %}"></script>
|
||||||
|
|||||||
@@ -431,7 +431,7 @@ class SubEventEditorMixin(MetaDataEditorMixin):
|
|||||||
days = (self.copy_from.date_from.astimezone(tz).date() - value.astimezone(tz).date()).days
|
days = (self.copy_from.date_from.astimezone(tz).date() - value.astimezone(tz).date()).days
|
||||||
return RelativeDateWrapper(RelativeDate(
|
return RelativeDateWrapper(RelativeDate(
|
||||||
days=abs(days),
|
days=abs(days),
|
||||||
base_date_name='date_from',
|
base_date_name='event__date_from',
|
||||||
time=value.astimezone(tz).time(),
|
time=value.astimezone(tz).time(),
|
||||||
minutes=None,
|
minutes=None,
|
||||||
is_after=days < 0,
|
is_after=days < 0,
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
$(document).on('pretix:bind-forms', () => {
|
||||||
|
document.querySelectorAll('.reldatetime, .reldate').forEach(container => {
|
||||||
|
const groups = container.querySelectorAll('.radio')
|
||||||
|
|
||||||
|
groups.forEach(group => {
|
||||||
|
const referenceSelect = group.querySelector('select[data-relative-choice]')
|
||||||
|
const beforeAfterSelect = group.querySelector('select[data-relation-choice]')
|
||||||
|
if (!referenceSelect || !beforeAfterSelect) return
|
||||||
|
|
||||||
|
const beforeOption = beforeAfterSelect.querySelector('option[value="before"]')
|
||||||
|
const afterOption = beforeAfterSelect.querySelector('option[value="after"]')
|
||||||
|
|
||||||
|
const updateBeforeOption = () => {
|
||||||
|
let supportsBefore = referenceSelect.selectedOptions[0].hasAttribute('data-supports-before')
|
||||||
|
if (beforeOption) {
|
||||||
|
beforeOption.disabled = !beforeOption.disabled && !supportsBefore
|
||||||
|
}
|
||||||
|
|
||||||
|
let supportsAfter = referenceSelect.selectedOptions[0].hasAttribute('data-supports-after')
|
||||||
|
if (afterOption) {
|
||||||
|
afterOption.disabled = !afterOption.disabled && !supportsAfter
|
||||||
|
}
|
||||||
|
|
||||||
|
let dirty = false
|
||||||
|
if (beforeOption.disabled && beforeAfterSelect.value === 'before') {
|
||||||
|
beforeAfterSelect.value = 'after'
|
||||||
|
dirty = true
|
||||||
|
}
|
||||||
|
if (afterOption.disabled && beforeAfterSelect.value === 'after') {
|
||||||
|
beforeAfterSelect.value = 'before'
|
||||||
|
dirty = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty) {
|
||||||
|
beforeAfterSelect.dispatchEvent(new Event('change', {bubbles: true}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
referenceSelect.addEventListener('change', updateBeforeOption)
|
||||||
|
updateBeforeOption()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -473,7 +473,7 @@ class QuotaTestCase(BaseQuotaTestCase):
|
|||||||
@classscope(attr='o')
|
@classscope(attr='o')
|
||||||
def test_waitinglist_auto_disable(self):
|
def test_waitinglist_auto_disable(self):
|
||||||
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
||||||
RelativeDate(days=0, time=None, base_date_name='date_from', minutes=20, is_after=True)
|
RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=20, is_after=True)
|
||||||
)
|
)
|
||||||
self.quota.items.add(self.item1)
|
self.quota.items.add(self.item1)
|
||||||
self.quota.size = 1
|
self.quota.size = 1
|
||||||
@@ -484,7 +484,7 @@ class QuotaTestCase(BaseQuotaTestCase):
|
|||||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_ORDERED, 0))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_ORDERED, 0))
|
||||||
self.assertEqual(self.item1.check_quotas(count_waitinglist=False), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(count_waitinglist=False), (Quota.AVAILABILITY_OK, 1))
|
||||||
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
||||||
RelativeDate(days=0, time=None, base_date_name='date_from', minutes=20, is_after=False)
|
RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=20, is_after=False)
|
||||||
)
|
)
|
||||||
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(), (Quota.AVAILABILITY_OK, 1))
|
||||||
self.assertEqual(self.item1.check_quotas(count_waitinglist=False), (Quota.AVAILABILITY_OK, 1))
|
self.assertEqual(self.item1.check_quotas(count_waitinglist=False), (Quota.AVAILABILITY_OK, 1))
|
||||||
@@ -1192,7 +1192,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.op2.subevent = se2
|
self.op2.subevent = se2
|
||||||
self.op2.save()
|
self.op2.save()
|
||||||
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
|
|
||||||
self.order.status = Order.STATUS_EXPIRED
|
self.order.status = Order.STATUS_EXPIRED
|
||||||
@@ -1347,7 +1347,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.op2.subevent = se2
|
self.op2.subevent = se2
|
||||||
self.op2.save()
|
self.op2.save()
|
||||||
self.event.settings.set('last_order_modification_date', RelativeDateWrapper(
|
self.event.settings.set('last_order_modification_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert self.order.can_modify_answers
|
assert self.order.can_modify_answers
|
||||||
self.op2.subevent = se3
|
self.op2.subevent = se3
|
||||||
@@ -1363,7 +1363,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc)
|
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
self.event.save()
|
self.event.save()
|
||||||
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert self.order.payment_term_last == datetime.datetime(2017, 5, 1, 23, 59, 59, tzinfo=datetime.timezone.utc)
|
assert self.order.payment_term_last == datetime.datetime(2017, 5, 1, 23, 59, 59, tzinfo=datetime.timezone.utc)
|
||||||
|
|
||||||
@@ -1379,7 +1379,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.op2.subevent = se2
|
self.op2.subevent = se2
|
||||||
self.op2.save()
|
self.op2.save()
|
||||||
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert self.order.payment_term_last > now()
|
assert self.order.payment_term_last > now()
|
||||||
self.op2.subevent = se3
|
self.op2.subevent = se3
|
||||||
@@ -1395,7 +1395,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc)
|
self.event.date_from = datetime.datetime(2017, 5, 3, 12, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
self.event.save()
|
self.event.save()
|
||||||
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
|
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert self.order.ticket_download_date == datetime.datetime(2017, 5, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
|
assert self.order.ticket_download_date == datetime.datetime(2017, 5, 1, 12, 0, 0, tzinfo=datetime.timezone.utc)
|
||||||
|
|
||||||
@@ -1411,7 +1411,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.op2.subevent = se2
|
self.op2.subevent = se2
|
||||||
self.op2.save()
|
self.op2.save()
|
||||||
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
|
self.event.settings.set('ticket_download_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert self.order.ticket_download_date > now()
|
assert self.order.ticket_download_date > now()
|
||||||
self.op2.subevent = se3
|
self.op2.subevent = se3
|
||||||
@@ -1582,13 +1582,13 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
|
|
||||||
assert self.order.user_cancel_deadline is None
|
assert self.order.user_cancel_deadline is None
|
||||||
self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper(
|
self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
self.order = Order.objects.get(pk=self.order.pk)
|
self.order = Order.objects.get(pk=self.order.pk)
|
||||||
assert self.order.user_cancel_deadline > now()
|
assert self.order.user_cancel_deadline > now()
|
||||||
assert self.order.user_cancel_allowed
|
assert self.order.user_cancel_allowed
|
||||||
self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper(
|
self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper(
|
||||||
RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None)
|
RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
self.order = Order.objects.get(pk=self.order.pk)
|
self.order = Order.objects.get(pk=self.order.pk)
|
||||||
assert self.order.user_cancel_deadline < now()
|
assert self.order.user_cancel_deadline < now()
|
||||||
@@ -1607,7 +1607,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.op2.save()
|
self.op2.save()
|
||||||
|
|
||||||
self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper(
|
self.event.settings.set('cancel_allow_user_until', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
self.order = Order.objects.get(pk=self.order.pk)
|
self.order = Order.objects.get(pk=self.order.pk)
|
||||||
assert self.order.user_cancel_deadline < now()
|
assert self.order.user_cancel_deadline < now()
|
||||||
@@ -2018,13 +2018,13 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
|
|
||||||
assert self.order.user_change_deadline is None
|
assert self.order.user_change_deadline is None
|
||||||
self.event.settings.set('change_allow_user_until', RelativeDateWrapper(
|
self.event.settings.set('change_allow_user_until', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
self.order = Order.objects.get(pk=self.order.pk)
|
self.order = Order.objects.get(pk=self.order.pk)
|
||||||
assert self.order.user_change_deadline > now()
|
assert self.order.user_change_deadline > now()
|
||||||
assert self.order.user_change_allowed
|
assert self.order.user_change_allowed
|
||||||
self.event.settings.set('change_allow_user_until', RelativeDateWrapper(
|
self.event.settings.set('change_allow_user_until', RelativeDateWrapper(
|
||||||
RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None)
|
RelativeDate(days=4, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
self.order = Order.objects.get(pk=self.order.pk)
|
self.order = Order.objects.get(pk=self.order.pk)
|
||||||
assert self.order.user_change_deadline < now()
|
assert self.order.user_change_deadline < now()
|
||||||
@@ -2046,7 +2046,7 @@ class OrderTestCase(BaseQuotaTestCase):
|
|||||||
self.op2.save()
|
self.op2.save()
|
||||||
|
|
||||||
self.event.settings.set('change_allow_user_until', RelativeDateWrapper(
|
self.event.settings.set('change_allow_user_until', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=datetime.time(14, 0, 0), base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
self.order = Order.objects.get(pk=self.order.pk)
|
self.order = Order.objects.get(pk=self.order.pk)
|
||||||
assert self.order.user_change_deadline < now()
|
assert self.order.user_change_deadline < now()
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ def test_expiry_last_relative(event):
|
|||||||
event.date_from = now() + timedelta(days=5)
|
event.date_from = now() + timedelta(days=5)
|
||||||
event.save()
|
event.save()
|
||||||
event.settings.set('payment_term_last', RelativeDateWrapper(
|
event.settings.set('payment_term_last', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
order = _create_order(event, email='dummy@example.org', positions=[],
|
order = _create_order(event, email='dummy@example.org', positions=[],
|
||||||
now_dt=today,
|
now_dt=today,
|
||||||
@@ -245,7 +245,7 @@ def test_expiry_last_relative_subevents(event):
|
|||||||
)
|
)
|
||||||
|
|
||||||
event.settings.set('payment_term_last', RelativeDateWrapper(
|
event.settings.set('payment_term_last', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
order = _create_order(event, email='dummy@example.org', positions=[cp1, cp2],
|
order = _create_order(event, email='dummy@example.org', positions=[cp1, cp2],
|
||||||
now_dt=today,
|
now_dt=today,
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ def test_availability_date_relative(event):
|
|||||||
event.save()
|
event.save()
|
||||||
prov = DummyPaymentProvider(event)
|
prov = DummyPaymentProvider(event)
|
||||||
prov.settings.set('_availability_date', RelativeDateWrapper(
|
prov.settings.set('_availability_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
|
|
||||||
utc = datetime.timezone.utc
|
utc = datetime.timezone.utc
|
||||||
@@ -150,7 +150,7 @@ def test_availability_start_relative(event):
|
|||||||
event.save()
|
event.save()
|
||||||
prov = DummyPaymentProvider(event)
|
prov = DummyPaymentProvider(event)
|
||||||
prov.settings.set('_availability_start', RelativeDateWrapper(
|
prov.settings.set('_availability_start', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=datetime.time(12, 0), base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=datetime.time(12, 0), base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
|
|
||||||
utc = datetime.timezone.utc
|
utc = datetime.timezone.utc
|
||||||
@@ -193,12 +193,12 @@ def test_availability_date_cart_relative_subevents(event):
|
|||||||
|
|
||||||
prov = DummyPaymentProvider(event)
|
prov = DummyPaymentProvider(event)
|
||||||
prov.settings.set('_availability_date', RelativeDateWrapper(
|
prov.settings.set('_availability_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=3, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=3, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert prov._is_available_by_time(cart_id="123")
|
assert prov._is_available_by_time(cart_id="123")
|
||||||
|
|
||||||
prov.settings.set('_availability_date', RelativeDateWrapper(
|
prov.settings.set('_availability_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=4, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=4, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert not prov._is_available_by_time(cart_id="123")
|
assert not prov._is_available_by_time(cart_id="123")
|
||||||
|
|
||||||
@@ -233,11 +233,11 @@ def test_availability_date_order_relative_subevents(event):
|
|||||||
|
|
||||||
prov = DummyPaymentProvider(event)
|
prov = DummyPaymentProvider(event)
|
||||||
prov.settings.set('_availability_date', RelativeDateWrapper(
|
prov.settings.set('_availability_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=3, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=3, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert prov._is_available_by_time(order=order)
|
assert prov._is_available_by_time(order=order)
|
||||||
|
|
||||||
prov.settings.set('_availability_date', RelativeDateWrapper(
|
prov.settings.set('_availability_date', RelativeDateWrapper(
|
||||||
RelativeDate(days=4, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=4, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
assert not prov._is_available_by_time(order=order)
|
assert not prov._is_available_by_time(order=order)
|
||||||
|
|||||||
@@ -19,13 +19,13 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||||
# <https://www.gnu.org/licenses/>.
|
# <https://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from datetime import datetime, time
|
from datetime import datetime, time, timedelta
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django_scopes import scope
|
from django_scopes import scope
|
||||||
|
|
||||||
from pretix.base.models import Event, Organizer
|
from pretix.base.models import Event, Order, Organizer
|
||||||
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
|
from pretix.base.reldate import RelativeDate, RelativeDateWrapper
|
||||||
|
|
||||||
TOKYO = ZoneInfo('Asia/Tokyo')
|
TOKYO = ZoneInfo('Asia/Tokyo')
|
||||||
@@ -40,7 +40,6 @@ def event():
|
|||||||
date_from=datetime(2017, 12, 27, 5, 0, 0, tzinfo=TOKYO),
|
date_from=datetime(2017, 12, 27, 5, 0, 0, tzinfo=TOKYO),
|
||||||
presale_start=datetime(2017, 12, 1, 5, 0, 0, tzinfo=TOKYO),
|
presale_start=datetime(2017, 12, 1, 5, 0, 0, tzinfo=TOKYO),
|
||||||
plugins='pretix.plugins.banktransfer'
|
plugins='pretix.plugins.banktransfer'
|
||||||
|
|
||||||
)
|
)
|
||||||
event.settings.timezone = "Asia/Tokyo"
|
event.settings.timezone = "Asia/Tokyo"
|
||||||
return event
|
return event
|
||||||
@@ -56,57 +55,57 @@ def test_absolute_date(event):
|
|||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_relative_date_without_time(event):
|
def test_relative_date_without_time(event):
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None))
|
||||||
assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
|
||||||
assert rdw.to_string() == 'RELDATE/1/-/date_from/'
|
assert rdw.to_string() == 'RELDATE/1/-/event__date_from/'
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None, is_after=True))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None, is_after=True))
|
||||||
assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 28, 5, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(event).astimezone(TOKYO) == datetime(2017, 12, 28, 5, 0, 0, tzinfo=TOKYO)
|
||||||
assert rdw.to_string() == 'RELDATE/1/-/date_from/after'
|
assert rdw.to_string() == 'RELDATE/1/-/event__date_from/after'
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_relative_date_other_base_point(event):
|
def test_relative_date_other_base_point(event):
|
||||||
with scope(organizer=event.organizer):
|
with scope(organizer=event.organizer):
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_start', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_start', minutes=None))
|
||||||
assert rdw.datetime(event) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(event) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
|
||||||
assert rdw.to_string() == 'RELDATE/1/-/presale_start/'
|
assert rdw.to_string() == 'RELDATE/1/-/event__presale_start/'
|
||||||
|
|
||||||
# presale_end is unset, defaults to date_from
|
# presale_end is unset, defaults to date_from
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_end', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_end', minutes=None))
|
||||||
assert rdw.datetime(event) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(event) == datetime(2017, 12, 26, 5, 0, 0, tzinfo=TOKYO)
|
||||||
assert rdw.to_string() == 'RELDATE/1/-/presale_end/'
|
assert rdw.to_string() == 'RELDATE/1/-/event__presale_end/'
|
||||||
|
|
||||||
# subevent base
|
# subevent base
|
||||||
se = event.subevents.create(name="SE1", date_from=datetime(2017, 11, 27, 5, 0, 0, tzinfo=TOKYO))
|
se = event.subevents.create(name="SE1", date_from=datetime(2017, 11, 27, 5, 0, 0, tzinfo=TOKYO))
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__date_from', minutes=None))
|
||||||
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
|
||||||
|
|
||||||
# presale_start is unset on subevent, default to event
|
# presale_start is unset on subevent, default to event
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_start', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_start', minutes=None))
|
||||||
assert rdw.datetime(se) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(se) == datetime(2017, 11, 30, 5, 0, 0, tzinfo=TOKYO)
|
||||||
|
|
||||||
# presale_end is unset on all, default to date_from of subevent
|
# presale_end is unset on all, default to date_from of subevent
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='presale_end', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='event__presale_end', minutes=None))
|
||||||
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(se) == datetime(2017, 11, 26, 5, 0, 0, tzinfo=TOKYO)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_relative_date_in_minutes(event):
|
def test_relative_date_in_minutes(event):
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60))
|
rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=60))
|
||||||
assert rdw.to_string() == 'RELDATE/minutes/60/date_from/'
|
assert rdw.to_string() == 'RELDATE/minutes/60/event__date_from/'
|
||||||
assert rdw.datetime(event) == datetime(2017, 12, 27, 4, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(event) == datetime(2017, 12, 27, 4, 0, 0, tzinfo=TOKYO)
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60, is_after=True))
|
rdw = RelativeDateWrapper(RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=60, is_after=True))
|
||||||
assert rdw.to_string() == 'RELDATE/minutes/60/date_from/after'
|
assert rdw.to_string() == 'RELDATE/minutes/60/event__date_from/after'
|
||||||
assert rdw.datetime(event) == datetime(2017, 12, 27, 6, 0, 0, tzinfo=TOKYO)
|
assert rdw.datetime(event) == datetime(2017, 12, 27, 6, 0, 0, tzinfo=TOKYO)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_relative_date_with_time(event):
|
def test_relative_date_with_time(event):
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='date_from', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='event__date_from', minutes=None))
|
||||||
assert rdw.to_string() == 'RELDATE/1/08:05:13/date_from/'
|
assert rdw.to_string() == 'RELDATE/1/08:05:13/event__date_from/'
|
||||||
assert rdw.datetime(event) == datetime(2017, 12, 26, 8, 5, 13, tzinfo=TOKYO)
|
assert rdw.datetime(event) == datetime(2017, 12, 26, 8, 5, 13, tzinfo=TOKYO)
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='date_from', minutes=None, is_after=True))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(8, 5, 13), base_date_name='event__date_from', minutes=None, is_after=True))
|
||||||
assert rdw.to_string() == 'RELDATE/1/08:05:13/date_from/after'
|
assert rdw.to_string() == 'RELDATE/1/08:05:13/event__date_from/after'
|
||||||
assert rdw.datetime(event) == datetime(2017, 12, 28, 8, 5, 13, tzinfo=TOKYO)
|
assert rdw.datetime(event) == datetime(2017, 12, 28, 8, 5, 13, tzinfo=TOKYO)
|
||||||
|
|
||||||
|
|
||||||
@@ -115,35 +114,94 @@ def test_relative_date_with_time_around_dst(event):
|
|||||||
event.settings.timezone = "Europe/Berlin"
|
event.settings.timezone = "Europe/Berlin"
|
||||||
event.date_from = datetime(2020, 3, 29, 18, 0, 0, tzinfo=BERLIN)
|
event.date_from = datetime(2020, 3, 29, 18, 0, 0, tzinfo=BERLIN)
|
||||||
|
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='date_from', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='event__date_from', minutes=None))
|
||||||
assert rdw.to_string() == 'RELDATE/1/18:00:00/date_from/'
|
assert rdw.to_string() == 'RELDATE/1/18:00:00/event__date_from/'
|
||||||
assert rdw.datetime(event) == datetime(2020, 3, 28, 18, 0, 0, tzinfo=BERLIN)
|
assert rdw.datetime(event) == datetime(2020, 3, 28, 18, 0, 0, tzinfo=BERLIN)
|
||||||
|
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='date_from', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='event__date_from', minutes=None))
|
||||||
assert rdw.to_string() == 'RELDATE/0/02:30:00/date_from/'
|
assert rdw.to_string() == 'RELDATE/0/02:30:00/event__date_from/'
|
||||||
assert rdw.datetime(event) == datetime(2020, 3, 29, 2, 30, 0, tzinfo=BERLIN)
|
assert rdw.datetime(event) == datetime(2020, 3, 29, 2, 30, 0, tzinfo=BERLIN)
|
||||||
|
|
||||||
event.date_from = datetime(2020, 10, 25, 18, 0, 0, tzinfo=BERLIN)
|
event.date_from = datetime(2020, 10, 25, 18, 0, 0, tzinfo=BERLIN)
|
||||||
|
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='date_from', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=time(18, 0, 0), base_date_name='event__date_from', minutes=None))
|
||||||
assert rdw.to_string() == 'RELDATE/1/18:00:00/date_from/'
|
assert rdw.to_string() == 'RELDATE/1/18:00:00/event__date_from/'
|
||||||
assert rdw.datetime(event) == datetime(2020, 10, 24, 18, 0, 0, tzinfo=BERLIN)
|
assert rdw.datetime(event) == datetime(2020, 10, 24, 18, 0, 0, tzinfo=BERLIN)
|
||||||
|
|
||||||
rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='date_from', minutes=None))
|
rdw = RelativeDateWrapper(RelativeDate(days=0, time=time(2, 30, 0), base_date_name='event__date_from', minutes=None))
|
||||||
assert rdw.to_string() == 'RELDATE/0/02:30:00/date_from/'
|
assert rdw.to_string() == 'RELDATE/0/02:30:00/event__date_from/'
|
||||||
assert rdw.datetime(event) == datetime(2020, 10, 25, 2, 30, 0, tzinfo=BERLIN)
|
assert rdw.datetime(event) == datetime(2020, 10, 25, 2, 30, 0, tzinfo=BERLIN)
|
||||||
|
|
||||||
|
|
||||||
def test_unserialize():
|
def test_unserialize_backwards_compatibility():
|
||||||
|
d = datetime(2017, 12, 25, 10, 0, 0, tzinfo=TOKYO)
|
||||||
|
rdw = RelativeDateWrapper.from_string(d.isoformat())
|
||||||
|
assert rdw.data == d
|
||||||
|
|
||||||
|
# keeping the test for the old from_string_format to ensure that we don't break anything
|
||||||
|
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_from/')
|
||||||
|
assert rdw.data == RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None)
|
||||||
|
|
||||||
|
# keeping the test for the old from_string_format to ensure that we don't break anything
|
||||||
|
rdw = RelativeDateWrapper.from_string('RELDATE/1/18:05:13/date_from/')
|
||||||
|
assert rdw.data == RelativeDate(days=1, time=time(18, 5, 13), base_date_name='date_from', minutes=None)
|
||||||
|
|
||||||
|
# keeping the test for the old from_string_format to ensure that we don't break anything
|
||||||
|
rdw = RelativeDateWrapper.from_string('RELDATE/minutes/60/date_from/')
|
||||||
|
assert rdw.data == RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60)
|
||||||
|
|
||||||
|
|
||||||
|
def test_backwards_compatibility():
|
||||||
|
# the data model of RelativeDate had to be extended to support other models as relation target
|
||||||
|
# previously only:
|
||||||
|
# - date_from
|
||||||
|
# - date_to
|
||||||
|
# - date_admission
|
||||||
|
# - presale_start
|
||||||
|
# - presale_end
|
||||||
|
# where valid values for the fourth slot (base_date_names) of the serialized form.
|
||||||
|
# the relationship in this case always pointed at event
|
||||||
|
# so any preexisting base_date_names without __ should continue to work and upgrade to event__{old_base_date_name}
|
||||||
d = datetime(2017, 12, 25, 10, 0, 0, tzinfo=TOKYO)
|
d = datetime(2017, 12, 25, 10, 0, 0, tzinfo=TOKYO)
|
||||||
rdw = RelativeDateWrapper.from_string(d.isoformat())
|
rdw = RelativeDateWrapper.from_string(d.isoformat())
|
||||||
assert rdw.data == d
|
assert rdw.data == d
|
||||||
|
|
||||||
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_from/')
|
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_from/')
|
||||||
assert rdw.data == RelativeDate(days=1, time=None, base_date_name='date_from', minutes=None)
|
assert rdw.to_string() == 'RELDATE/1/-/event__date_from/'
|
||||||
|
|
||||||
rdw = RelativeDateWrapper.from_string('RELDATE/1/18:05:13/date_from/')
|
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_to/')
|
||||||
assert rdw.data == RelativeDate(days=1, time=time(18, 5, 13), base_date_name='date_from', minutes=None)
|
assert rdw.to_string() == 'RELDATE/1/-/event__date_to/'
|
||||||
|
|
||||||
rdw = RelativeDateWrapper.from_string('RELDATE/minutes/60/date_from/')
|
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/date_admission/')
|
||||||
assert rdw.data == RelativeDate(days=0, time=None, base_date_name='date_from', minutes=60)
|
assert rdw.to_string() == 'RELDATE/1/-/event__date_admission/'
|
||||||
|
|
||||||
|
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/presale_start/')
|
||||||
|
assert rdw.to_string() == 'RELDATE/1/-/event__presale_start/'
|
||||||
|
|
||||||
|
rdw = RelativeDateWrapper.from_string('RELDATE/1/-/presale_end/')
|
||||||
|
assert rdw.to_string() == 'RELDATE/1/-/event__presale_end/'
|
||||||
|
|
||||||
|
# new order base_date_names should not work without __
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
RelativeDateWrapper.from_string('RELDATE/1/-/datetime/')
|
||||||
|
with pytest.raises(TypeError):
|
||||||
|
RelativeDateWrapper.from_string('RELDATE/1/-/expires/')
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_relative_to_order(event):
|
||||||
|
with scope(organizer=event.organizer):
|
||||||
|
order_moment = datetime(2020, 3, 29, 18, 0, 0, tzinfo=TOKYO)
|
||||||
|
|
||||||
|
order = Order.objects.create(
|
||||||
|
code='FOO', event=event, email='dummy@dummy.test',
|
||||||
|
status=Order.STATUS_PENDING, secret="k24fiuwvu8kxz3y1",
|
||||||
|
datetime=order_moment,
|
||||||
|
expires=order_moment + timedelta(days=10),
|
||||||
|
sales_channel=event.organizer.sales_channels.get(identifier="web"),
|
||||||
|
total=23, locale='en'
|
||||||
|
)
|
||||||
|
|
||||||
|
rdw = RelativeDateWrapper(RelativeDate(days=1, time=None, base_date_name='order__datetime', minutes=None, is_after=True))
|
||||||
|
assert rdw.datetime(order).astimezone(TOKYO) == datetime(2020, 3, 30, 18, 0, 0, tzinfo=TOKYO)
|
||||||
|
assert rdw.to_string() == 'RELDATE/1/-/order__datetime/after'
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ class WaitingListTestCase(TestCase):
|
|||||||
self.event.settings.set('waiting_list_enabled', True)
|
self.event.settings.set('waiting_list_enabled', True)
|
||||||
self.event.settings.set('waiting_list_auto', True)
|
self.event.settings.set('waiting_list_auto', True)
|
||||||
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
||||||
RelativeDate(days=0, time=None, base_date_name='date_from', minutes=20, is_after=False)
|
RelativeDate(days=0, time=None, base_date_name='event__date_from', minutes=20, is_after=False)
|
||||||
)
|
)
|
||||||
self.event.save()
|
self.event.save()
|
||||||
with scope(organizer=self.o):
|
with scope(organizer=self.o):
|
||||||
|
|||||||
@@ -481,7 +481,7 @@ class EventsTest(SoupTest):
|
|||||||
'payment_term_last_0': 'absolute',
|
'payment_term_last_0': 'absolute',
|
||||||
'payment_term_last_1': (self.event1.presale_end - datetime.timedelta(1)).strftime('%Y-%m-%d'),
|
'payment_term_last_1': (self.event1.presale_end - datetime.timedelta(1)).strftime('%Y-%m-%d'),
|
||||||
'payment_term_last_2': '0',
|
'payment_term_last_2': '0',
|
||||||
'payment_term_last_3': 'date_from',
|
'payment_term_last_3': 'event__date_from',
|
||||||
'tax_rule_payment': 'default',
|
'tax_rule_payment': 'default',
|
||||||
})
|
})
|
||||||
assert doc.select('.alert-danger')
|
assert doc.select('.alert-danger')
|
||||||
@@ -496,7 +496,7 @@ class EventsTest(SoupTest):
|
|||||||
'payment_term_last_0': 'relative',
|
'payment_term_last_0': 'relative',
|
||||||
'payment_term_last_1': '',
|
'payment_term_last_1': '',
|
||||||
'payment_term_last_2': '10',
|
'payment_term_last_2': '10',
|
||||||
'payment_term_last_3': 'date_from',
|
'payment_term_last_3': 'event__date_from',
|
||||||
'tax_rule_payment': 'default',
|
'tax_rule_payment': 'default',
|
||||||
})
|
})
|
||||||
assert doc.select('.alert-danger')
|
assert doc.select('.alert-danger')
|
||||||
|
|||||||
@@ -230,19 +230,19 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
'rel_presale_start_2': '1',
|
'rel_presale_start_2': '1',
|
||||||
'rel_presale_start_3': 'date_from',
|
'rel_presale_start_3': 'event__date_from',
|
||||||
'rel_presale_start_4': '',
|
'rel_presale_start_4': '',
|
||||||
'rel_presale_start_5': '',
|
'rel_presale_start_5': '',
|
||||||
'rel_presale_start_6': 'date_from',
|
'rel_presale_start_6': 'event__date_from',
|
||||||
'rel_presale_start_7': 'before',
|
'rel_presale_start_7': 'before',
|
||||||
'rel_presale_start_8': 'before',
|
'rel_presale_start_8': 'before',
|
||||||
'rel_presale_end_1': '',
|
'rel_presale_end_1': '',
|
||||||
'rel_presale_end_0': 'relative',
|
'rel_presale_end_0': 'relative',
|
||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'event__date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'rel_presale_end_5': '',
|
'rel_presale_end_5': '',
|
||||||
'rel_presale_end_6': 'date_from',
|
'rel_presale_end_6': 'event__date_from',
|
||||||
'rel_presale_end_7': 'before',
|
'rel_presale_end_7': 'before',
|
||||||
'rel_presale_end_8': 'before',
|
'rel_presale_end_8': 'before',
|
||||||
'quotas-TOTAL_FORMS': '1',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
@@ -328,19 +328,19 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
'rel_presale_start_2': '1',
|
'rel_presale_start_2': '1',
|
||||||
'rel_presale_start_3': 'date_from',
|
'rel_presale_start_3': 'event__date_from',
|
||||||
'rel_presale_start_4': '',
|
'rel_presale_start_4': '',
|
||||||
'rel_presale_start_5': '',
|
'rel_presale_start_5': '',
|
||||||
'rel_presale_start_6': 'date_from',
|
'rel_presale_start_6': 'event__date_from',
|
||||||
'rel_presale_start_7': 'before',
|
'rel_presale_start_7': 'before',
|
||||||
'rel_presale_start_8': 'before',
|
'rel_presale_start_8': 'before',
|
||||||
'rel_presale_end_1': '',
|
'rel_presale_end_1': '',
|
||||||
'rel_presale_end_0': 'relative',
|
'rel_presale_end_0': 'relative',
|
||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'event__date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'rel_presale_end_5': '',
|
'rel_presale_end_5': '',
|
||||||
'rel_presale_end_6': 'date_from',
|
'rel_presale_end_6': 'event__date_from',
|
||||||
'rel_presale_end_7': 'before',
|
'rel_presale_end_7': 'before',
|
||||||
'rel_presale_end_8': 'before',
|
'rel_presale_end_8': 'before',
|
||||||
'quotas-TOTAL_FORMS': '1',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
@@ -403,19 +403,19 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
'rel_presale_start_2': '1',
|
'rel_presale_start_2': '1',
|
||||||
'rel_presale_start_3': 'date_from',
|
'rel_presale_start_3': 'event__date_from',
|
||||||
'rel_presale_start_4': '',
|
'rel_presale_start_4': '',
|
||||||
'rel_presale_start_5': '',
|
'rel_presale_start_5': '',
|
||||||
'rel_presale_start_6': 'date_from',
|
'rel_presale_start_6': 'event__date_from',
|
||||||
'rel_presale_start_7': 'before',
|
'rel_presale_start_7': 'before',
|
||||||
'rel_presale_start_8': 'before',
|
'rel_presale_start_8': 'before',
|
||||||
'rel_presale_end_1': '',
|
'rel_presale_end_1': '',
|
||||||
'rel_presale_end_0': 'relative',
|
'rel_presale_end_0': 'relative',
|
||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'event__date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'rel_presale_end_5': '',
|
'rel_presale_end_5': '',
|
||||||
'rel_presale_end_6': 'date_from',
|
'rel_presale_end_6': 'event__date_from',
|
||||||
'rel_presale_end_7': 'before',
|
'rel_presale_end_7': 'before',
|
||||||
'rel_presale_end_8': 'before',
|
'rel_presale_end_8': 'before',
|
||||||
'quotas-TOTAL_FORMS': '1',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
@@ -479,19 +479,19 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
'rel_presale_start_2': '1',
|
'rel_presale_start_2': '1',
|
||||||
'rel_presale_start_3': 'date_from',
|
'rel_presale_start_3': 'event__date_from',
|
||||||
'rel_presale_start_4': '',
|
'rel_presale_start_4': '',
|
||||||
'rel_presale_start_5': '',
|
'rel_presale_start_5': '',
|
||||||
'rel_presale_start_6': 'date_from',
|
'rel_presale_start_6': 'event__date_from',
|
||||||
'rel_presale_start_7': 'before',
|
'rel_presale_start_7': 'before',
|
||||||
'rel_presale_start_8': 'before',
|
'rel_presale_start_8': 'before',
|
||||||
'rel_presale_end_1': '',
|
'rel_presale_end_1': '',
|
||||||
'rel_presale_end_0': 'relative',
|
'rel_presale_end_0': 'relative',
|
||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'event__date_from',
|
||||||
'rel_presale_end_4': '02:30:00',
|
'rel_presale_end_4': '02:30:00',
|
||||||
'rel_presale_end_5': '',
|
'rel_presale_end_5': '',
|
||||||
'rel_presale_end_6': 'date_from',
|
'rel_presale_end_6': 'event__date_from',
|
||||||
'rel_presale_end_7': 'before',
|
'rel_presale_end_7': 'before',
|
||||||
'rel_presale_end_8': 'before',
|
'rel_presale_end_8': 'before',
|
||||||
'quotas-TOTAL_FORMS': '1',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
@@ -567,19 +567,19 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
'rel_presale_start_2': '1',
|
'rel_presale_start_2': '1',
|
||||||
'rel_presale_start_3': 'date_from',
|
'rel_presale_start_3': 'event__date_from',
|
||||||
'rel_presale_start_4': '',
|
'rel_presale_start_4': '',
|
||||||
'rel_presale_start_5': '',
|
'rel_presale_start_5': '',
|
||||||
'rel_presale_start_6': 'date_from',
|
'rel_presale_start_6': 'event__date_from',
|
||||||
'rel_presale_start_7': 'before',
|
'rel_presale_start_7': 'before',
|
||||||
'rel_presale_start_8': 'before',
|
'rel_presale_start_8': 'before',
|
||||||
'rel_presale_end_1': '',
|
'rel_presale_end_1': '',
|
||||||
'rel_presale_end_0': 'relative',
|
'rel_presale_end_0': 'relative',
|
||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'event__date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'rel_presale_end_5': '',
|
'rel_presale_end_5': '',
|
||||||
'rel_presale_end_6': 'date_from',
|
'rel_presale_end_6': 'event__date_from',
|
||||||
'rel_presale_end_7': 'before',
|
'rel_presale_end_7': 'before',
|
||||||
'rel_presale_end_8': 'before',
|
'rel_presale_end_8': 'before',
|
||||||
'quotas-TOTAL_FORMS': '1',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
@@ -639,19 +639,19 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
'rel_presale_start_2': '1',
|
'rel_presale_start_2': '1',
|
||||||
'rel_presale_start_3': 'date_from',
|
'rel_presale_start_3': 'event__date_from',
|
||||||
'rel_presale_start_4': '',
|
'rel_presale_start_4': '',
|
||||||
'rel_presale_start_5': '',
|
'rel_presale_start_5': '',
|
||||||
'rel_presale_start_6': 'date_from',
|
'rel_presale_start_6': 'event__date_from',
|
||||||
'rel_presale_start_7': 'before',
|
'rel_presale_start_7': 'before',
|
||||||
'rel_presale_start_8': 'before',
|
'rel_presale_start_8': 'before',
|
||||||
'rel_presale_end_0': 'unset',
|
'rel_presale_end_0': 'unset',
|
||||||
'rel_presale_end_1': '',
|
'rel_presale_end_1': '',
|
||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'event__date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'rel_presale_end_5': '',
|
'rel_presale_end_5': '',
|
||||||
'rel_presale_end_6': 'date_from',
|
'rel_presale_end_6': 'event__date_from',
|
||||||
'rel_presale_end_7': 'before',
|
'rel_presale_end_7': 'before',
|
||||||
'rel_presale_end_8': 'before',
|
'rel_presale_end_8': 'before',
|
||||||
'quotas-TOTAL_FORMS': '1',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
@@ -711,19 +711,19 @@ class SubEventsTest(SoupTest):
|
|||||||
'rel_presale_start_0': 'unset',
|
'rel_presale_start_0': 'unset',
|
||||||
'rel_presale_start_1': '',
|
'rel_presale_start_1': '',
|
||||||
'rel_presale_start_2': '1',
|
'rel_presale_start_2': '1',
|
||||||
'rel_presale_start_3': 'date_from',
|
'rel_presale_start_3': 'event__date_from',
|
||||||
'rel_presale_start_4': '',
|
'rel_presale_start_4': '',
|
||||||
'rel_presale_start_5': '',
|
'rel_presale_start_5': '',
|
||||||
'rel_presale_start_6': 'date_from',
|
'rel_presale_start_6': 'event__date_from',
|
||||||
'rel_presale_start_7': 'before',
|
'rel_presale_start_7': 'before',
|
||||||
'rel_presale_start_8': 'before',
|
'rel_presale_start_8': 'before',
|
||||||
'rel_presale_end_0': 'unset',
|
'rel_presale_end_0': 'unset',
|
||||||
'rel_presale_end_1': '',
|
'rel_presale_end_1': '',
|
||||||
'rel_presale_end_2': '1',
|
'rel_presale_end_2': '1',
|
||||||
'rel_presale_end_3': 'date_from',
|
'rel_presale_end_3': 'event__date_from',
|
||||||
'rel_presale_end_4': '13:29:31',
|
'rel_presale_end_4': '13:29:31',
|
||||||
'rel_presale_end_5': '',
|
'rel_presale_end_5': '',
|
||||||
'rel_presale_end_6': 'date_from',
|
'rel_presale_end_6': 'event__date_from',
|
||||||
'rel_presale_end_7': 'before',
|
'rel_presale_end_7': 'before',
|
||||||
'rel_presale_end_8': 'before',
|
'rel_presale_end_8': 'before',
|
||||||
'quotas-TOTAL_FORMS': '1',
|
'quotas-TOTAL_FORMS': '1',
|
||||||
|
|||||||
@@ -1132,7 +1132,7 @@ class WaitingListTest(EventTestMixin, SoupTest):
|
|||||||
def test_auto_disable(self):
|
def test_auto_disable(self):
|
||||||
self.event.settings.set('waiting_list_enabled', True)
|
self.event.settings.set('waiting_list_enabled', True)
|
||||||
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
self.event.settings.waiting_list_auto_disable = RelativeDateWrapper(
|
||||||
RelativeDate(days=900, time=datetime.time(9, 0, 0), base_date_name='date_from', minutes=None, is_after=False)
|
RelativeDate(days=900, time=datetime.time(9, 0, 0), base_date_name='event__date_from', minutes=None, is_after=False)
|
||||||
)
|
)
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
'/%s/%s/' % (self.orga.slug, self.event.slug)
|
'/%s/%s/' % (self.orga.slug, self.event.slug)
|
||||||
|
|||||||
@@ -1527,7 +1527,7 @@ class OrderChangeAddonsTest(BaseOrdersTest):
|
|||||||
self.order.save()
|
self.order.save()
|
||||||
self._subevent_setup()
|
self._subevent_setup()
|
||||||
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
self.event.settings.set('payment_term_last', RelativeDateWrapper(
|
||||||
RelativeDate(days=2, time=None, base_date_name='date_from', minutes=None)
|
RelativeDate(days=2, time=None, base_date_name='event__date_from', minutes=None)
|
||||||
))
|
))
|
||||||
|
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
|
|||||||
@@ -1076,7 +1076,7 @@ class OrdersTest(BaseOrdersTest):
|
|||||||
self.event.date_from = now() + datetime.timedelta(days=3)
|
self.event.date_from = now() + datetime.timedelta(days=3)
|
||||||
self.event.save()
|
self.event.save()
|
||||||
self.event.settings.set('ticket_download_date', RelativeDateWrapper(RelativeDate(
|
self.event.settings.set('ticket_download_date', RelativeDateWrapper(RelativeDate(
|
||||||
base_date_name='date_from', days=2, time=None, minutes=None
|
base_date_name='event__date_from', days=2, time=None, minutes=None
|
||||||
)))
|
)))
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
'/%s/%s/order/%s/%s/download/%d/testdummy' % (self.orga.slug, self.event.slug, self.order.code,
|
||||||
|
|||||||
Reference in New Issue
Block a user