from hashlib import sha256 from django.core.exceptions import * from django.core.validators import RegexValidator from django.db import models from django.db.models import Q from account.models import SiteUser from datetime import datetime import os class City(models.Model): code = models.CharField(primary_key=True, max_length=20, verbose_name="Код города", validators=[ RegexValidator(regex="^[0-9a-zA-Z_]*$"), ]) name = models.CharField(unique=True, max_length=50, verbose_name="Название города") def __str__(self): return f"{self.name} ({self.code})" @staticmethod def to_choices(): return City.objects.order_by('name').values_list('code', 'name') def _phone_validate(phone, order=None): if phone is not None: err_obj = {"phone": f'Телефон {phone} уже используется другим аккаунтом'} if SiteUser.objects.filter(phone=phone).count() > 0: raise ValidationError( err_obj, code='permission' ) if order is None: if Order.objects.filter(phone=phone).count() > 0: raise ValidationError( err_obj, code='permission' ) else: if Order.objects.filter(~Q(id=order.id), phone=phone).count() > 0: raise ValidationError( err_obj, code='permission' ) def _email_validate(email, order=None): if email is not None: err_obj = {'email': f'Почта {email} уже используется другим аккаунтом'} if SiteUser.objects.filter(email=email).count(): raise ValidationError( err_obj, code='permission' ) if order is None: if Order.objects.filter(email=email).count() > 0: raise ValidationError( err_obj, code='permission' ) else: if Order.objects.filter(~Q(id=order.id), email=email).count() > 0: raise ValidationError( err_obj, code='permission' ) class Order(models.Model): # основные поля: название и описание name = models.CharField(max_length=200, verbose_name="Название заказа") description = models.TextField(blank=True, verbose_name="Описание") # площадь в квадратных метрах square = models.DecimalField(max_digits=7, decimal_places=2, blank=False, verbose_name="Площадь в м²") work_time = models.CharField(max_length=100, blank=True, verbose_name="Рабочее время") # дальше вид дома, тип ремонта, тип квартиры, требуется дизайн проект, закуп материала, тип исполнителя CHOICE_UNDEFINED = '' # тип ремонта TYPE_OF_RENOVATION_OVERHAUL = 'overhaul' TYPE_OF_RENOVATION_PARTIAL = 'partial' TYPE_OF_RENOVATION_REDECOR = 'redecor' TYPE_OF_RENOVATION_PREMIUM = 'premium' TYPE_OF_RENOVATION_DESIGN = 'design' TYPE_OF_RENOVATION_CHOICES = [ (TYPE_OF_RENOVATION_OVERHAUL, 'Капитальный'), (TYPE_OF_RENOVATION_PARTIAL, 'Частичный'), (TYPE_OF_RENOVATION_REDECOR, 'Косметический'), (TYPE_OF_RENOVATION_PREMIUM, 'Премиальный'), (TYPE_OF_RENOVATION_DESIGN, 'Дизайнерский'), ] type_of_renovation = models.CharField(max_length=10, choices=TYPE_OF_RENOVATION_CHOICES, default=CHOICE_UNDEFINED, blank=True, verbose_name="Тип ремонта") # тип дома TYPE_OF_HOUSE_BLOCK = 'block' TYPE_OF_HOUSE_BRICK = 'brick' TYPE_OF_HOUSE_MONOLITH = 'monolith' TYPE_OF_HOUSE_PANEL = 'panel' TYPE_OF_HOUSE_CHOICES = [ (TYPE_OF_HOUSE_BLOCK, 'Блочный'), (TYPE_OF_HOUSE_BRICK, 'Кирпичный'), (TYPE_OF_HOUSE_MONOLITH, 'Монолит'), (TYPE_OF_HOUSE_PANEL, 'Панельный'), ] type_of_house = models.CharField(max_length=10, choices=TYPE_OF_HOUSE_CHOICES, blank=True, default=CHOICE_UNDEFINED, verbose_name="Тип дома") # тип квартиры TYPE_OF_ROOM_PRIMARY = 'primary' TYPE_OF_ROOM_SECONDARY = 'secondary' TYPE_OF_ROOM_CHOICES = [ (TYPE_OF_ROOM_PRIMARY, 'Первичка'), (TYPE_OF_ROOM_SECONDARY, 'Вторичка') ] type_of_room = models.CharField(max_length=10, choices=TYPE_OF_ROOM_CHOICES, blank=True, default=CHOICE_UNDEFINED, verbose_name="Тип квартиры") # требуется дизайн проект REQUIRED_DESIGN_CHOICES = ((True, 'Да'), (False, 'Нет')) is_require_design = models.BooleanField(default=None, blank=True, null=True, choices=REQUIRED_DESIGN_CHOICES, verbose_name="Требуется дизайн проект") # закуп материала PURCHASE_OF_MATERIAL_EXECUTOR = 'executor' PURCHASE_OF_MATERIAL_CUSTOMER = 'customer' PURCHASE_OF_MATERIAL_CHOICES = [ (PURCHASE_OF_MATERIAL_EXECUTOR, 'Исполнитель'), (PURCHASE_OF_MATERIAL_CUSTOMER, 'Заказчик') ] purchase_of_material = models.CharField(max_length=10, choices=PURCHASE_OF_MATERIAL_CHOICES, blank=True, default=CHOICE_UNDEFINED, verbose_name="Закуп материала") # тип исполнителя TYPE_OF_EXECUTOR_INDIVIDUAL = 'individual' TYPE_OF_EXECUTOR_COMPANY = 'company' TYPE_OF_EXECUTOR_CHOICES = [ (TYPE_OF_EXECUTOR_INDIVIDUAL, 'Самозанятый/бригада'), (TYPE_OF_EXECUTOR_COMPANY, 'Компания') ] type_of_executor = models.CharField(max_length=10, choices=TYPE_OF_EXECUTOR_CHOICES, blank=True, default=CHOICE_UNDEFINED, verbose_name="Тип исполнителя") # дальше отдельные параметры is_with_warranty = models.BooleanField(default=True, verbose_name="С гарантией") is_with_contract = models.BooleanField(default=False, verbose_name="Работа по договору") is_with_trade = models.BooleanField(default=False, verbose_name="Возможен торг") is_with_cleaning = models.BooleanField(default=False, verbose_name="С уборкой") is_with_garbage_removal = models.BooleanField(default=False, verbose_name="С вывозом мусора") # примерная цена approximate_price = models.DecimalField(max_digits=12, decimal_places=2, blank=False, verbose_name="Цена") date_start = models.DateField(null=True, blank=True, default=None, verbose_name="Дата начала") date_end = models.DateField(null=True, blank=True, default=None, verbose_name="Дата окончания") address_city = models.ForeignKey(City, on_delete=models.CASCADE, blank=False, related_name="address_city", verbose_name="Город") address_text = models.CharField(max_length=70, blank=True, verbose_name="Улица, дом") owner = models.ForeignKey(SiteUser, on_delete=models.CASCADE, blank=True, null=True, related_name="owner", verbose_name="Владелец") email = models.EmailField(null=True, blank=True, verbose_name="Email") phone = models.CharField(null=True, blank=True, max_length=16, verbose_name="Телефон", validators=[ RegexValidator(regex="^\\+7[0-9]{10}$") ]) create_time = models.DateTimeField(default=datetime.now, editable=False, verbose_name="Время создания") moderated = models.BooleanField(default=True, verbose_name="Модерирован") published = models.BooleanField(default=False, verbose_name="Опубликован") def clean(self): errors = {} try: if self.pk is None: _phone_validate(self.phone) else: _phone_validate(self.phone, self) except ValidationError as e: errors["phone"] = [e] try: if self.pk is None: _email_validate(self.email) else: _email_validate(self.email, self) except ValidationError as e: errors["email"] = [e] if len(errors) > 0: raise ValidationError(errors) def save(self, *args, **kwargs): if self.pk is None: if self.owner is None: if self.phone is None or self.email is None: raise Exception("Could not save: need contact information") if self.owner is not None: self.phone = None self.email = None self.full_clean() super().save(*args, **kwargs) def __str__(self): return self.name @staticmethod def get_all_for_user(user): if user.is_staff: return Order.objects.filter().order_by('create_time') else: return Order.objects.filter(published=True, moderated=True).order_by('create_time') @staticmethod def get_for_user_by_id(user, order_id): q = Order.get_all_for_user(user).filter(id=order_id) if len(q) == 0: return None else: return q[0] def _upload_image_filename(instance, filename): name, ext = os.path.splitext(filename) fn = sha256(str(datetime.now()).encode('utf-8')).hexdigest() + ext return "order-images/" + fn class OrderImage(models.Model): order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="order", verbose_name="Заказ") image = models.ImageField(upload_to=_upload_image_filename, verbose_name="Картинка", width_field=None, height_field=None) def __str__(self): return f"{self.id}: {self.order}" class OrderRespond(models.Model): create_time = models.DateTimeField(default=datetime.now, editable=False, verbose_name="Время отклика") user = models.ForeignKey(SiteUser, on_delete=models.CASCADE, related_name="respond_user", verbose_name="Пользователь") order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name="respond_order", verbose_name="Заказ") class Meta: constraints = [ models.UniqueConstraint( fields=['user', 'order'], name='unique_order_respond_user_order' ) ] def __str__(self): return f"{self.order}: {self.user}" def save(self, *args, **kwargs): if Order.objects.get(id=self.order.id) == self.user.id: raise Exception("User can't respond to self order") self.full_clean() super().save(*args, **kwargs)