"""Establishment models.""" from functools import reduce from django.contrib.contenttypes import fields as generic from django.core.exceptions import ValidationError from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ from location.models import Address from utils.models import (ProjectBaseMixin, ImageMixin, TJSONField, TraslatedFieldsMixin, BaseAttributes) # todo: establishment type&subtypes check class EstablishmentType(ProjectBaseMixin, TraslatedFieldsMixin): """Establishment type model.""" name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'), help_text='{"en":"some text"}') use_subtypes = models.BooleanField(_('Use subtypes'), default=True) class Meta: """Meta class.""" verbose_name = _('Establishment type') verbose_name_plural = _('Establishment types') class EstablishmentSubTypeManager(models.Manager): """Extended manager for establishment subtype.""" def make(self, name, establishment_type): obj = self.model(name=name, establishment_type=establishment_type) obj.full_clean() obj.save() return obj class EstablishmentSubType(ProjectBaseMixin, TraslatedFieldsMixin): """Establishment type model.""" name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'), help_text='{"en":"some text"}') establishment_type = models.ForeignKey(EstablishmentType, on_delete=models.CASCADE, verbose_name=_('Type')) objects = EstablishmentSubTypeManager() class Meta: """Meta class.""" verbose_name = _('Establishment subtype') verbose_name_plural = _('Establishment subtypes') def clean_fields(self, exclude=None): if not self.establishment_type.use_subtypes: raise ValidationError(_('Establishment type is not use subtypes.')) class EstablishmentQuerySet(models.QuerySet): """Extended queryset for Establishment model.""" def search(self, value, locale=None): """Search text in JSON fields.""" if locale is not None: filters = [ {f'name__{locale}__icontains': value}, {f'description__{locale}__icontains': value} ] return self.filter(reduce(lambda x, y: x | y, [models.Q(**i) for i in filters])) else: return self.none() def prefetch_actual_employees(self): """Prefetch actual employees.""" return self.prefetch_related( models.Prefetch('establishmentemployee_set', queryset=EstablishmentEmployee.objects.actual().select_related( 'position'), to_attr='actual_establishment_employees')) class Establishment(ProjectBaseMixin, ImageMixin, TraslatedFieldsMixin): """Establishment model.""" name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Name'), help_text='{"en":"some text"}') description = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'), help_text='{"en":"some text"}') public_mark = models.PositiveIntegerField(blank=True, null=True, default=None, verbose_name=_('Public mark'),) toque_number = models.PositiveIntegerField(blank=True, null=True, default=None, verbose_name=_('Toque number'),) establishment_type = models.ForeignKey(EstablishmentType, related_name='establishment', on_delete=models.PROTECT, verbose_name=_('Type')) establishment_subtypes = models.ManyToManyField(EstablishmentSubType, related_name='subtype_establishment', verbose_name=_('Subtype')) address = models.ForeignKey(Address, blank=True, null=True, default=None, on_delete=models.PROTECT, verbose_name=_('Address')) price_level = models.PositiveIntegerField(blank=True, null=True, default=None, verbose_name=_('Price level')) awards = generic.GenericRelation(to='main.Award') tags = generic.GenericRelation(to='main.MetaDataContent') objects = EstablishmentQuerySet.as_manager() class Meta: """Meta class.""" verbose_name = _('Establishment') verbose_name_plural = _('Establishments') # todo: recalculate toque_number def recalculate_toque_number(self): self.toque_number = 4 def recalculate_price_level(self, low_price=None, high_price=None): if low_price is None or high_price is None: low_price, high_price = self.get_price_level() # todo: calculate price level self.price_level = 3 def get_price_level(self): country = self.address.city.country return country.low_price, country.high_price # todo: make via prefetch @property def subtypes(self): return EstablishmentSubType.objects.filter( subtype_establishment=self, establishment_type=self.establishment_type, establishment_type__use_subtypes=True) def set_establishment_type(self, establishment_type): self.establishment_type = establishment_type self.establishment_subtypes.exclude( establishement_type=establishment_type).delete() def add_establishment_subtype(self, establishment_subtype): if establishment_subtype.establishment_type != self.establishment_type: raise ValidationError('Establishment type of subtype does not match') self.establishment_subtypes.add(establishment_subtype) class Position(BaseAttributes, TraslatedFieldsMixin): """Position model.""" name = TJSONField(blank=True, null=True, default=None, verbose_name=_('Description'), help_text='{"en":"some text"}') class Meta: """Meta class.""" verbose_name = _('Position') verbose_name_plural = _('Positions') class EstablishmentEmployeeQuerySet(models.QuerySet): """Extended queryset for EstablishmEntemployee model.""" def actual(self): """Actual objects..""" now = timezone.now() return self.filter(models.Q(from_date__lte=now), (models.Q(to_date__gte=now) | models.Q(to_date__isnull=True))) class EstablishmentEmployee(BaseAttributes): """EstablishmentEmployee model.""" establishment = models.ForeignKey(Establishment, on_delete=models.PROTECT, verbose_name=_('Establishment')) employee = models.ForeignKey('establishment.Employee', on_delete=models.PROTECT, verbose_name=_('Employee')) from_date = models.DateTimeField(default=timezone.now, verbose_name=_('From date')) to_date = models.DateTimeField(blank=True, null=True, default=None, verbose_name=_('To date')) position = models.ForeignKey(Position, on_delete=models.PROTECT, verbose_name=_('Position')) objects = EstablishmentEmployeeQuerySet.as_manager() class Employee(BaseAttributes): """Employee model.""" user = models.OneToOneField('account.User', on_delete=models.PROTECT, null=True, blank=True, default=None, verbose_name=_('User')) name = models.CharField(max_length=255, verbose_name=_('Last name')) establishments = models.ManyToManyField(Establishment, related_name='employees', through=EstablishmentEmployee) awards = generic.GenericRelation(to='main.Award') tags = generic.GenericRelation(to='main.MetaDataContent') class Meta: """Meta class.""" verbose_name = _('Employee') verbose_name_plural = _('Employees') class EstablishmentScheduleQuerySet(models.QuerySet): """QuerySet for model EstablishmentSchedule""" class EstablishmentSchedule(BaseAttributes): """Establishment schedule model.""" establishment = models.OneToOneField(Establishment, related_name='schedule', on_delete=models.CASCADE, verbose_name=_('Establishment')) schedule = models.ManyToManyField(to='timetable.Timetable', verbose_name=_('Establishment schedule')) objects = EstablishmentScheduleQuerySet.as_manager() class Meta: """Meta class""" verbose_name = _('Establishment schedule') verbose_name_plural = _('Establishment schedules')