from django.db import models from django.contrib.postgres.fields import ArrayField from decimal import * from django.db.models import Count, Min, Sum from accounts.models import Profile from main.models import STARS_CHOICES from store.alemtat import alemtat_get_cities_tuple, alemtat_get_services_tuple from store.queryset import ProductQuerySet from django_resized import ResizedImageField FIELD_TYPE_CHAR = 'char' FIELD_TYPE_INT = 'int' FIELD_TYPE_DECIMAL = 'decimal' FIELD_TYPE_SELECT = 'select' FIELD_TYPE_CHOICES = ( (FIELD_TYPE_CHAR, 'Строка'), (FIELD_TYPE_INT, 'Целое число'), (FIELD_TYPE_DECIMAL, 'Дробное число'), (FIELD_TYPE_SELECT, 'Список'), ) def photo_filename(instance, filename): from slugify import slugify_filename return 'photo_uploads/' + slugify_filename(filename) class CartModel(models.Model): cart_id = models.CharField('Сессия', max_length=256, default='') profile = models.ForeignKey(Profile, default=None, null=True, blank=False, related_name='carts') items = models.TextField('Товары', default='') weight = models.CharField('Вес', max_length=20, default='0') total = models.CharField('Итого тенге', max_length=20, default='0') class Category(models.Model): title = models.CharField('Наименование', max_length=256, default='') h1 = models.CharField('Заголовок h1', max_length=256, default='', null=True, blank=True) meta_keyw = models.TextField('Мета ключевые слова', default='', null=True, blank=True) meta_desc = models.TextField('Мета описание', default='', null=True, blank=True) meta_text = models.TextField('Мета текст', default='', null=True, blank=True) slug = models.SlugField('Ссылка', max_length=256, default='') description = models.TextField( 'Описание', default='', null=True, blank=True) parent = models.ForeignKey( 'self', default=None, null=True, blank=True, related_name='childs', verbose_name='Родительская категория') hide_products = models.BooleanField('Показывать дочерние категории вместо товаров', default=False) image = ResizedImageField('Картинка', upload_to=photo_filename, default=None, null=True, blank=True) attributes = models.ManyToManyField( 'Attribute', through='AttributeForCategory') priority = models.IntegerField('Приоритет', default=0) class Meta: verbose_name = 'категория' verbose_name_plural = 'категории' def __str__(self): titles = [] for p in self.get_parents(): titles.append(p.title) if titles: return '{} > {}'.format(' > '.join(titles), self.title) else: return self.title def get_absolute_url(self): retval = '/store/' for p in self.get_parents(): retval += p.slug + '/' retval += self.slug + '/' return retval def get_parents(self): retval = [] current_category = self.parent while current_category is not None: retval.append(current_category) current_category = current_category.parent retval.reverse() return retval def get_all_products(self): return Product.objects.filter(categories__in=self.get_all_childs()) def get_all_childs(self, include_self=True): r = () r += (self,) for c in self.childs.all(): r += c.get_all_childs(include_self=False) return r def brands(self): brand_qs = self.get_all_products().values( 'brand').annotate(count_brands=Count('brand')) brand_pks = [v['brand'] for v in brand_qs] return Brand.objects.filter(pk__in=brand_pks).order_by('title') def get_childs_ordered_by_count(self): return self.childs.annotate(Count('childs')).order_by('priority') # , '-childs__count') def get_all_products_in_stock_order_by(self, field): queryset = Product.objects.filter(categories__in=self.get_all_childs()).sorted_in_stock_by_field(field) return queryset class Attribute(models.Model): title = models.CharField('Наименование', max_length=256, default='') slug = models.SlugField('Ссылка', max_length=256, default='') class Meta: verbose_name = 'атрибут' verbose_name_plural = 'атрибуты' def __str__(self): return self.title class AttributeForCategory(models.Model): category = models.ForeignKey( Category, verbose_name='Категория', related_name='attr_cat') attribute = models.ForeignKey( Attribute, verbose_name='Атрибут в категории', related_name='for_category') field_type = models.CharField( 'Тип атрибута', choices=FIELD_TYPE_CHOICES, default='char', max_length=10) choises_array = ArrayField( models.CharField(max_length=50), blank=True, default=[]) priority = models.IntegerField('Приоритет', default=0) class Meta: verbose_name = 'атрибут в категории' verbose_name_plural = 'атрибуты в категориях' def __str__(self): if self.field_type == FIELD_TYPE_SELECT: return '{}: {} ({}: {})'.format( self.category.title, self.attribute.title, self.get_field_type_display(), self.choises_array) else: return '{}: {} ({})'.format( self.category.title, self.attribute.title, self.get_field_type_display()) def get_posible_values(self): retval = [] for val in self.in_product.all(): if self.field_type == FIELD_TYPE_INT: if int(val.value) not in retval: retval.append(int(val.value)) elif self.field_type == FIELD_TYPE_DECIMAL: if Decimal(val.value) not in retval: retval.append(Decimal(val.value)) elif self.field_type == FIELD_TYPE_CHAR: if val.value not in retval: retval.append(val.value) retval.sort() return retval class Brand(models.Model): title = models.CharField('Наименование', max_length=256, default='') slug = models.SlugField('Ссылка', max_length=256, default='') image = models.ImageField('Логотип (60px в высоту)', upload_to=photo_filename, default=None, blank=True, null=True) class Meta: verbose_name = 'бренд' verbose_name_plural = 'бренды' def __str__(self): return self.title # class Image(models.Model): # image = models.ImageField('Картинка', # upload_to=os.path.join(settings.BASE_DIR, "static", 'uploads')) class Product(models.Model): title = models.CharField('Наименование', max_length=256, default='') slug = models.SlugField('Ссылка', max_length=256, default='', unique=True) categories = models.ManyToManyField( Category, verbose_name='Категории', related_name='products') description = models.TextField( 'Описание', default='', null=True, blank=True) brand = models.ForeignKey(Brand, verbose_name='Брэнд') video = models.CharField('Код Youtube', max_length=256, default='', null=True, blank=True) attirbutes = models.ManyToManyField( AttributeForCategory, through='AttributesInProduct') is_leader = models.BooleanField('Лидер продаж', default=False) on_main = models.BooleanField('На главной', default=False) priority = models.IntegerField('Приоритет для главной', default=0) objects = ProductQuerySet.as_manager() class Meta: verbose_name = 'товар' verbose_name_plural = 'товары' def __str__(self): return self.title def main_image(self): try: return self.images.order_by('-is_main')[0].image except: return None def in_stock(self): return self.variations.filter(in_stock__gt=0).count() def delivery_date(self): try: return self.variations.filter(delivery_date__isnull=False)[0].delivery_date except: return None def min_price_variation(self): min_v = None for v in self.variations.all(): if not min_v: min_v = v else: if v.get_price() < min_v.get_price(): min_v = v return min_v def min_price(self, profile=None, currency=None): price = self.min_price_variation().get_price(profile) if currency: pass return price def is_discount(self): return self.variations.filter(discount__gt=0).count() > 0 def get_stars(self): fb_count = self.feedback.count() fb_sum = self.feedback.aggregate(Sum('stars')) if fb_count: return int(fb_sum / fb_count) else: return 0 # retval = self.variations.filter(in_stock__gt=0).aggregate( # Min('price'))['price__min'] or 0 # if not retval: # retval = self.variations.aggregate(Min('price'))['price__min'] or 0 # # if retval > 10000: # return int(retval * Decimal('.95')) # else: # return int(retval * Decimal('.93')) def get_absolute_url(self): retval = '/store/' main_category = self.categories.last() for p in main_category.get_parents(): retval += p.slug + '/' retval += main_category.slug + '/product-' + self.slug return retval class Currency(models.Model): title = models.CharField('Название', max_length=25) exchange = models.FloatField('Курс, KZT/ед.') abridgement = models.CharField('Сокращенное название', max_length=25) ISO_letter_code = models.CharField('Буквенный код', help_text='Например: KZT, USD, RUB', max_length=3) HTML_letter_code = models.CharField('HTML код', help_text='Код в HTML для вывода в шаблонах', max_length=10) class Meta: verbose_name = 'валюта' verbose_name_plural = 'валюты' ordering = ['pk'] def __str__(self): return self.title class ProductVariation(models.Model): product = models.ForeignKey( Product, verbose_name='Товар', related_name='variations') variation = models.CharField('Вариация', max_length=512, default='') price = models.IntegerField( 'Цена (тенге)', default=0, null=False, blank=False) in_stock = models.IntegerField( 'В наличии (шт.)', default=1, null=False, blank=False) article = models.CharField( 'Артикул', max_length=32, null=True, blank=True, default='') weight = models.FloatField('Вес (кг)', default=0.1, null=False, blank=False) discount = models.IntegerField('Скидка %', default=0, blank=False, null=False) delivery_date = models.CharField('Дата доставки', default=None, blank=True, null=True, max_length=20) class Meta: verbose_name = 'разновидность товара' verbose_name_plural = 'разновидности товаров' def __str__(self): return self.variation def get_price(self, profile=None): # import pdb; pdb.set_trace() # from django.core.mail import mail_admins # mail_admins('auth', '{} {}'.format(profile, self.product.brand.slug)) if profile and profile.is_authenticated() and self.product.brand.slug in ['beuchat', 'scorpena'] and profile.sale: return int(self.price - (self.price / Decimal(100) * Decimal(profile.sale))) else: if self.discount: return int(self.price - (self.price / 100 * self.discount)) else: if self.price > 10000: return int(self.price * Decimal('.95')) else: return int(self.price * Decimal('.93')) class AttributesInProduct(models.Model): attribute = models.ForeignKey( AttributeForCategory, verbose_name='Атрибут в категории', related_name='in_product') product = models.ForeignKey( Product, verbose_name='Товар', related_name='product_attributes') value = models.CharField( 'Значение', max_length=64, default=None, blank=True, null=True) class Meta: verbose_name = 'атрибут в товаре' verbose_name_plural = 'атрибуты в товарах' class SlugImageField(models.ImageField): pass class ImageInProduct(models.Model): image = ResizedImageField('Картинка', upload_to=photo_filename, size=[1280, 1280], crop=['middle', 'center']) product = models.ForeignKey( Product, verbose_name='Товар', related_name='images') is_main = models.BooleanField('Главная', default=False) class Meta: verbose_name = 'фото товара' verbose_name_plural = 'фото товаров' def __str__(self): return '{}: {}'.format(self.pk, self.product.title) STATUS_CHOICES = ( (0, 'Ожидает оплаты'), (1, 'Формирование посылки'), (2, 'Ожидает отправки'), (3, 'Отправлено'), (4, 'Отменен'), (5, 'Выполнен'), ) KAZPOST_CITIES = ( (1, "Астана"), (2, "Актобе"), (3, "Актау"), (4, "Алматы"), (5, "Атырау"), (6, "Караганда"), (7, "Кызылорда"), (8, "Кокшетау"), (9, "Костанай"), (10, "Павлодар"), (11, "Петропавловск"), (12, "Тараз"), (13, "Усть-Каменогорск"), (14, "Уральск"), (15, "Шымкент"), (16, "Талдыкорган") ) class OrderData(models.Model): created = models.DateTimeField('Дата и время', auto_now_add=True, editable=False) edited = models.DateTimeField('Изменено', auto_now=True, editable=False) profile = models.ForeignKey(Profile, default=None, null=True, blank=False, related_name='orders') first_name = models.CharField( 'Имя', max_length=64, blank=False, null=False) last_name = models.CharField( 'Фамилия', max_length=64, blank=False, null=False) phone = models.CharField( 'Номер мобильного телефона', max_length=15, blank=False, null=False) email = models.EmailField( 'Email', blank=False, null=False) city = models.CharField( 'Город Алемтат', max_length=20, blank=True, null=True, choices=alemtat_get_cities_tuple()) deliv_service = models.CharField( 'Почтовая служба', max_length=20, blank=True, null=True, default='alemtat') kazpost_city = models.IntegerField( 'Город Казпочта', blank=True, null=True, choices=KAZPOST_CITIES) address = models.CharField( 'Адрес', max_length=100, blank=False, null=False) deliv_type = models.CharField( 'Способ Алемтат', max_length=2, blank=True, null=True, default='', choices=alemtat_get_services_tuple()) items = models.CharField( 'Товары', max_length=256, blank=False, null=False, default='') amount = models.FloatField( 'Сумма', blank=False, null=False, default=0.0) status = models.IntegerField('Статус', default=0, null=False, blank=False, choices=STATUS_CHOICES) class Meta: verbose_name = 'заказ' verbose_name_plural = 'заказы' def __str__(self): return '{} {}: {}'.format( self.first_name, self.last_name, self.amount ) def get_status(self): return 'Ожидает оплаты' def get_items(self): retval = () for item in eval(self.items): variation_id = list(item.keys())[0] variation_count = list(item.values())[0] variation = ProductVariation.objects.get(pk=variation_id) retval += ((variation, variation_count),) return retval def get_city(self): if self.city: return self.get_city_display() else: return self.get_kazpost_city_display() def get_deliv_type(self): if self.deliv_type: return self.get_deliv_type_display() else: return 'Авиапочта EMS' class ProductFeedback(models.Model): created = models.DateTimeField('Дата и время', auto_now_add=True, editable=False) name = models.CharField('Имя', max_length=45, null=False, blank=False) email = models.EmailField('Email (не будет опубликован)', null=False, blank=False) text = models.TextField('Комментарий', null=False, blank=False) stars = models.IntegerField('Оценка', default=5, choices=STARS_CHOICES) product = models.ForeignKey(Product, related_name='feedback') class Meta: verbose_name = 'отзыв о товаре' verbose_name_plural = 'отзывы о товарах' def __str__(self): return '{}: {}'.format(self.name, self.product.title)