remotes/origin/HEAD
Max Yakovenko 8 years ago
parent a91413a839
commit 17b7f012dd
  1. 1
      README.md
  2. 4
      accounts_ext/admin.py
  3. 7
      accounts_ext/forms.py
  4. 5
      accounts_ext/models.py
  5. 6
      cabinet/views.py
  6. 60
      cart/models.py
  7. 3
      cart/signals.py
  8. 4
      cart/urls.py
  9. 40
      cart/views.py
  10. 5
      core/views.py
  11. 8
      eshop_project/settings/base.py
  12. 5
      products/forms.py
  13. 86
      products/models.py
  14. 5
      products/urls.py
  15. 4
      products/views.py
  16. 38
      referral/admin.py
  17. 18
      referral/forms.py
  18. 32
      referral/models.py
  19. 7
      static/css/main.fix.css
  20. 6
      templates/accounts_ext/authentication.html
  21. 2
      templates/base.html
  22. 6
      templates/cabinet/index.html
  23. 24
      templates/cart/bought_history.html
  24. 170
      templates/cart/cart.html
  25. 70
      templates/common.html
  26. 9
      templates/components/breadcrumbs.html
  27. 4
      templates/components/footer.html
  28. 4
      templates/components/header.html
  29. 7
      templates/components/navbar.html
  30. 255
      templates/products/product_category_list.html
  31. 321
      templates/products/product_search.html

@ -26,3 +26,4 @@
2. Make migrations for modules
3. Collect static
4. Restart gunicorn-eshop and celery-eshop daemons in systemd
5. Add initial migration to core app and redefine site domain name

@ -31,7 +31,7 @@ class UserAdmin(SafeModelAdmin, BaseUserAdmin):
)
fieldsets = (
(None, {'fields': ('email', 'username', 'password', 'referral_code')}),
(None, {'fields': ('email', 'username', 'password', 'referral_user')}),
(_('Permissions'), {'fields': ('status', 'is_superuser',
'groups', 'user_permissions')}),
(_('Important dates'), {'fields': ('last_login', 'confirmed_at')}),
@ -41,7 +41,7 @@ class UserAdmin(SafeModelAdmin, BaseUserAdmin):
(None, {
'classes': ('wide',),
'fields': (
'username', 'email', 'password1', 'password2', 'referral_code', 'status', 'is_superuser'
'username', 'email', 'password1', 'password2', 'referral_user', 'status', 'is_superuser'
),
}),
)

@ -21,6 +21,7 @@ from .models import Profile, Company
logger = logging.getLogger(__name__)
class RegistrationForm(RegistrationFormUniqueEmail):
email = forms.EmailField(label=_("E-mail"), widget=forms.EmailInput(attrs={'class': 'reg__text'}))
password1 = forms.CharField(
@ -261,14 +262,12 @@ class CompanyForm(forms.Form):
class UserChangeForm(UserChangeFormBase):
class Meta:
model = get_user_model()
fields = ('status','referral_code',)
fields = ('status', 'referral_user',)
class UserCreationForm(UserCreationFormBase):
def clean_email(self):
email = self.cleaned_data['email']
if DisposableEmailChecker().is_disposable(email):
@ -277,7 +276,7 @@ class UserCreationForm(UserCreationFormBase):
class Meta:
model = get_user_model()
fields = ('email', 'username', 'status', 'referral_code', 'is_superuser')
fields = ('email', 'username', 'status', 'referral_user', 'is_superuser')
field_classes = {
'email': forms.EmailField,
'username': UsernameField,

@ -46,11 +46,6 @@ class User(AbstractStatusModel, PermissionsMixin, AbstractBaseUser):
},
)
referral_code = models.ForeignKey('referral.Referral',
verbose_name=_('Реферальная ссылка'),
on_delete=models.SET_NULL,
blank=True, null=True
)
referral_user = models.ForeignKey('self',
verbose_name=_('Реферальный пользователь'),
on_delete=models.SET_NULL,

@ -29,7 +29,7 @@ class IndexView(ProtectedView):
return paginator.page(the_page)
def get_full_name(self,user):
return '{last_name}{first_name}{patronymic}'.format(**{
return '{last_name} {first_name} {patronymic}'.format(**{
'last_name': user.profile.last_name or "",
'first_name': user.profile.first_name or "",
'patronymic': user.profile.patronymic or ""
@ -43,6 +43,6 @@ class IndexView(ProtectedView):
context['full_name'] = self.get_full_name(self.request.user)
context['email'] = self.request.user.email
context['phone_number'] = self.request.user.profile.phone
context['company'] = self.request.user.company
context['profile'] = self.request.user.profile
context['company'] = self.request.user.company or None
context['profile'] = self.request.user.profile or None
return context

@ -1,12 +1,13 @@
from django.db import models
from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import HStoreField
from django.db.models import Avg
from django.utils.translation import ugettext_lazy as _
from autoslug import AutoSlugField
# Create your models here.
from core.models import AbstractStatusModel
from core.models import AbstractStatusModel, AbstractDateTimeModel
from products.models import Product
OFFER_STATUS_ACTIVE = 25
@ -20,16 +21,23 @@ OFFER_STATUS_CHOICES = (
OFFER_DEFAULT_CHOICE = OFFER_STATUS_INACTIVE
class SupplyType(AbstractDateTimeModel):
name = models.CharField(max_length=255)
term = models.ValueRange()
class Offer(AbstractStatusModel):
name = models.CharField(max_length=64, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
product = models.OneToOneField(Product, on_delete=models.CASCADE, primary_key=True)
price = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=0.00)
product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True, default=None,
related_name='variants')
attributes = HStoreField(blank=True, null=True, default={})
currency = models.CharField(max_length=64, blank=True, null=True, default=None)
with_nds = models.BooleanField(default=False)
# attributes = HStoreField(blank=True, null=True, default={})
suply_type = models.ForeignKey(SupplyType, on_delete=models.SET_NULL, blank=True, null=True)
note = models.TextField(blank=True, null=True)
status = models.SmallIntegerField(_('статус'), default=OFFER_DEFAULT_CHOICE, choices=OFFER_STATUS_CHOICES)
amount = models.IntegerField(blank=True, null=True)
def __str__(self):
return self.name
@ -44,7 +52,7 @@ BUYING_STATUS_PENDING = 50
BUYING_STATUS_BOUGHT = 75
BUYING_STATUS_CHOICES = (
(BUYING_STATUS_IN_CART, _('В корзине')),
(BUYING_STATUS_PENDING,_('Обрабатываеться')),
(BUYING_STATUS_PENDING, _('Обрабатываеться')),
(BUYING_STATUS_BOUGHT, _('Куплен'))
)
@ -52,10 +60,44 @@ BUYING_DEFAULT_CHOICE = BUYING_STATUS_IN_CART
class Buying(AbstractStatusModel):
user = models.ForeignKey(get_user_model(),verbose_name=_('пользователь'), on_delete=models.CASCADE)
user = models.ForeignKey(get_user_model(), verbose_name=_('пользователь'), on_delete=models.CASCADE)
offer = models.ForeignKey(Offer, verbose_name=_('позиция'), on_delete=models.CASCADE)
status = models.SmallIntegerField(_('статус'), default=BUYING_DEFAULT_CHOICE, choices=BUYING_STATUS_CHOICES)
amount = models.SmallIntegerField(_('колличество'), default=0)
class Meta:
verbose_name = _('Покупка')
verbose_name_plural = _('Покупки')
STATUS_GAINED = 0
STATUS_SPENT = 100
CASHBACK_STATUS_CHOICES = (
(STATUS_GAINED, _('заработанный')),
(STATUS_SPENT, _('потраченный')),
)
STATUS_DEFAULT = STATUS_GAINED
class CashBackManager(models.Manager):
def get_gained_cashback_sum(self, user):
return self.get_queryset().filter(user=user, status=STATUS_GAINED).aggregate(Avg('amount'))
def get_spent_cashback_sum(self, user):
return self.get_queryset().filter(user=user, status=STATUS_SPENT).aggregate(Avg('amount'))
class BuyingCashback(AbstractDateTimeModel):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
buying = models.OneToOneField(Buying, on_delete=models.CASCADE)
amount = models.DecimalField(_('Сумма'), 'cashback', 7, 2, default=0)
status = models.SmallIntegerField(_('статус'), default=STATUS_DEFAULT, choices=CASHBACK_STATUS_CHOICES)
objects = CashBackManager()
class Meta:
verbose_name_plural = _('cashback')
verbose_name = _('cashback')

@ -0,0 +1,3 @@
from django.dispatch import Signal
buying_bought = Signal(providing_args=['buying'])

@ -7,6 +7,6 @@ urlpatterns = [
# url(r'^$', views.CartDetail, name='CartDetail'),
# url(r'^remove/(?P<offer_slug>[-\w]+)/$', views.CartRemove, name='CartRemove'),
# url(r'^add/$', views.CartAdd, name='CartAdd'),
re_path(r'^buyings/history/', views.BuyingsHistory.as_view(), name='buyings_history')
re_path(r'^history/', views.BuyingsHistory.as_view(), name='history'),
re_path(r'^buyings/$', views.CartView.as_view(), name='buyings'),
]

@ -5,12 +5,15 @@ from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.contrib import auth
from django.views.generic import ListView
from django.utils.translation import ugettext_lazy as _
from cart.models import Buying
from core.views import ProtectedView
from core.views import ProtectedListView
from .cart import Cart
from .forms import CartAddProductForm
# from discount.layout import DiscountApllyForm
# @csrf_exempt
@ -56,16 +59,37 @@ from .forms import CartAddProductForm
# # 'discount_apply_form': discount_apply_form})
class BuyingsHistory(ListView,ProtectedView):
class CartView(ProtectedListView):
model = Buying
paginate_by = settings.DEFAULT_PAGE_AMMOUNT
context_object_name = 'bought_item_list'
ordering = '-created_at'
template_name = 'cart/bought_history.html'
paginate_by = settings.DEFAULT_PAGE_AMOUNT
context_object_name = 'cart_items'
template_name = 'cart/cart.html'
ordering = '-create_at'
title = _('Корзина')
def get_queryset(self):
self.object_list = super().get_queryset()
return self.object_list
qs = super().get_queryset()
return qs.filter(user=self.request.user)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs)
context['title'] = self.title
return context
class BuyingsHistory(ProtectedListView):
model = Buying
paginate_by = settings.DEFAULT_PAGE_AMOUNT
context_object_name = 'bought_item_list'
ordering = '-create_at'
template_name = 'cart/bought_history.html'
title = _('История покупок')
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(user=self.request.user)
def get_context_data(self, *, object_list=None, **kwargs):
context = super().get_context_data(object_list=object_list, **kwargs)
context['title'] = self.title
return context

@ -1,13 +1,16 @@
from django.contrib.auth.mixins import LoginRequiredMixin
# Create your views here.
from django.views.generic import TemplateView
from django.views.generic import TemplateView, ListView
class ProtectedView(LoginRequiredMixin, TemplateView):
pass
class ProtectedListView(LoginRequiredMixin, ListView):
pass
class Handler404View(TemplateView):
template_name = '404.html'

@ -284,4 +284,10 @@ CART_SESSION_ID = 'cart'
IMPORT_EXPORT_USE_TRANSACTIONS = True
# PAGINATION SETTINGS
DEFAULT_PAGE_AMMOUNT = 10
DEFAULT_PAGE_AMOUNT = 10
# DEFAULT REFERRAL POINTS
DEFAULT_REFERRAL_POINTS = 120
# CASHBACK RATIO IN PERCENTAGE
CASHBACK_RATIO = 4

@ -29,7 +29,7 @@
# query += u'"%s"' % sqs.query.clean(producer)
# sqs = sqs.narrow(u'brand_exact:%s' % query)
# return sqs
from crispy_forms.layout import Layout, ButtonHolder, Submit, HTML, Field, Button
from crispy_forms.layout import Layout, Field, Button
from django import forms
from crispy_forms.helper import FormHelper
from django.urls import reverse_lazy
@ -38,6 +38,9 @@ from django.utils.translation import ugettext_lazy as _
from .models import Product
class ProductFilterForm(forms.Form):
pass
class ProductSearchForm(forms.ModelForm):
field_template = 'bootstrap/forms/product_search.html'
form_action = reverse_lazy('products:product_search')

@ -1,3 +1,4 @@
from django.contrib.auth import get_user_model
from django.db import models
from django.urls import reverse_lazy
from django.contrib.postgres.fields import HStoreField
@ -11,14 +12,41 @@ from mptt import (
from mptt.models import MPTTModel, TreeForeignKey
from autoslug import AutoSlugField
from core.models import AbstractStatusModel, AbstractDateTimeModel, STATUS_ACTIVE, STATUS_DELETED
from core.models import AbstractStatusModel, AbstractDateTimeModel, STATUS_ACTIVE, STATUS_DELETED, ActualOnlyManager
# --------------------------------- PRODUCT ATTRIBUTE ------------------------------------#
PRODUCT_ATTRIBUTE_TYPE_NONE = 0
PRODUCT_ATTRIBUTE_TYPE_RANGE = 50
PRODUCT_ATTRIBUTE_TYPE_SELECT = 100
PRODUCT_ATTRIBUTE_TYPE_CHOICES = (
(PRODUCT_ATTRIBUTE_TYPE_NONE, _('никакой')),
(PRODUCT_ATTRIBUTE_TYPE_RANGE, _('диапазон')),
(PRODUCT_ATTRIBUTE_TYPE_SELECT, _('выбор'))
)
PRODUCT_ATTRIBUTE_TYPE_DEFAULT = PRODUCT_ATTRIBUTE_TYPE_NONE
class ProductAttributeManager(models.Manager):
def get_range_type_attributes(self, product=None):
pass
def get_select_type_attributes(self, product=None):
pass
def get_all_type_attributes(self):
pass
class ProductAttribute(AbstractStatusModel):
name = models.CharField(max_length=64, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
type = models.SmallIntegerField(_('тип'), choices=PRODUCT_ATTRIBUTE_TYPE_CHOICES,
default=PRODUCT_ATTRIBUTE_TYPE_DEFAULT)
main_attribute = models.BooleanField(default=False)
objects = ProductAttributeManager()
def __str__(self):
return self.name
@ -31,7 +59,7 @@ class ProductAttribute(AbstractStatusModel):
class Manufacturer(AbstractStatusModel):
name = models.CharField(max_length=64, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
image = models.ImageField(upload_to='producers', blank=True, verbose_name="image of producer")
image = models.ImageField(upload_to='producers', blank=True, null=True, verbose_name=("Изображение"))
def __str__(self):
return self.name
@ -45,7 +73,7 @@ class Manufacturer(AbstractStatusModel):
verbose_name_plural = _('Производители')
class ProductCategoryManager(mptt_managers.TreeManager):
class ProductCategoryManager(mptt_managers.TreeManager, ActualOnlyManager):
def get_root_categories(self):
return self.get_queryset().all()
@ -75,21 +103,18 @@ register(ProductCategory, order_insertion_py=['name'])
class Product(AbstractStatusModel):
name = models.CharField(max_length=64, db_index=True, blank=True, null=True, default=None)
slug = AutoSlugField(populate_from='name')
price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
description = models.TextField(db_index=True, blank=True, null=True, default=None)
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.PROTECT)
category = models.ForeignKey(ProductCategory, on_delete=models.SET_NULL, blank=True, null=True, default=None)
name = models.CharField(_('имя'), max_length=64, db_index=True)
slug = AutoSlugField(_('slug'), populate_from='name', db_index=True)
description = models.TextField(_('описание'), blank=True, null=True, default=None)
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.SET_NULL, blank=True, null=True)
category = models.ForeignKey(ProductCategory, on_delete=models.SET_NULL, blank=True, null=True)
attributes = models.ManyToManyField(ProductAttribute, blank=True)
discount_policy = HStoreField(blank=True, null=True, default={})
is_active = models.BooleanField(default=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse_lazy('products:item', args=[self.slug])
def get_absolute_url(self, request):
return request.build_absolute_uri(reverse_lazy('products:item', args=[self.slug]))
class Meta:
indexes = [
@ -112,19 +137,38 @@ class Product(AbstractStatusModel):
# pp.save()
class ProductAttributeValue(AbstractDateTimeModel):
product = models.ForeignKey(Product, on_delete=models.PROTECT)
name = models.CharField(max_length=64, blank=True, null=True, default=None)
class ProductRate(AbstractStatusModel):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE)
rate = models.IntegerField(_('оценка'), default=0)
class Meta:
verbose_name = _('Рейтинг продукта')
verbose_name_plural = _('Рейтинг продукта')
class ProductDiscount(AbstractStatusModel):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
percentage = models.DecimalField(_('процент'), max_digits=3, decimal_places=2)
status = models.BooleanField(_('статус'), default=False)
class Meta:
verbose_name = _('Дисконт')
verbose_name_plural = _('Дисконты')
class ProductAttributeValue(AbstractStatusModel):
attribute = models.ForeignKey(ProductAttribute, on_delete=models.CASCADE, related_name='value')
slug = AutoSlugField(populate_from='name')
attribute = models.ForeignKey(ProductAttribute, on_delete=models.CASCADE, related_name='values')
value = HStoreField(_('значение'), default={})
def __str__(self):
return self.name
return self.value.serialize
class Meta:
unique_together = ('name', 'attribute')
verbose_name = 'Product attribute value'
verbose_name_plural = 'Product attribute values'
unique_together = ('attribute', 'value')
verbose_name = _('Значение аттрибута')
verbose_name_plural = _('Значение аттрибутов')
# ----------------- PRODUCT IMAGE STATUS LIST ------------------

@ -20,10 +20,9 @@ from .models import ProductCategory
urlpatterns = [
re_path(r'^product/category/search/$', views.ProductSearchView.as_view(), name='product_search'),
re_path(r'^search/$', views.ProductSearchView.as_view(), name='product_search'),
re_path(r'^product/(?P<product_slug>\w+)/$', views.ProductDetailsView.as_view(), name='product_details'),
re_path(r'^product/category/list/$', views.ProductCategoryListView.as_view(), name='product_category'),
re_path(r'^category/list/$', views.ProductCategoryListView.as_view(), name='product_category'),
# Uncomment for elasticsearch
# url(r'^autocomplete/$', autocomplete),

@ -28,7 +28,7 @@ class ProductSearchView(ListView):
model = Product
template_name = 'products/product_search.html'
context_object_name = 'products'
paginate_by = settings.DEFAULT_PAGE_AMMOUNT
paginate_by = settings.DEFAULT_PAGE_AMOUNT
title = _('Поиск товара')
def get_queryset(self):
@ -47,7 +47,7 @@ class ProductListView(ListView):
model = Product
template_name = 'products/product_list.html'
context_object_name = 'products'
paginate_by = settings.DEFAULT_PAGE_AMMOUNT
paginate_by = settings.DEFAULT_PAGE_AMOUNT
title = _('Список товаров')
def get_queryset(self):

@ -8,8 +8,8 @@ from django.utils.translation import ugettext_lazy as _
from rangefilter.filter import DateRangeFilter, DateTimeRangeFilter
from core.admin import SafeModelAdmin
from .models import Referral, ReferralStats, PartnerStats
from .forms import ReferralAdminForm, ReferralStatsAdminForm, PartnerStatsAdminForm
from .models import Referral, ReferralStats
from .forms import ReferralAdminForm, ReferralStatsAdminForm
class ReferralAdminInline(admin.TabularInline):
@ -69,37 +69,3 @@ class ReferralStatsAdmin(SafeModelAdmin):
list_select_related = ('referral',)
search_fields = ('referral__name', 'referral__code',)
ordering = ('-create_at',)
@register(PartnerStats)
class PartnerStatsAdmin(SafeModelAdmin):
def has_add_permission(self, request):
return False
def stats_owner(self, stats):
try:
link = reverse_lazy(
'admin:{}_{}_change'.format(stats.user._meta.app_label, stats.user._meta.object_name.lower()),
args=(stats.user.id,)
)
name = stats.user.email
except Exception as e:
link = '#'
name = "None"
return format_html('<a href="{}">{}</a>', link, name)
stats_owner.short_description = _('User')
def formatted_total_conversion(self, stats):
return str(stats.formatted_total_conversion) + "%"
formatted_total_conversion.short_description = _('Total conversion')
def converted_total_earnings(self, stats):
return stats.total_earnings if stats.total_earnings > 0 else 0
converted_total_earnings.short_description = _('Total earnings')
form = PartnerStatsAdminForm
list_display = ('stats_owner', 'total_visits', 'total_regs')
search_fields = ('user__email',)

@ -1,7 +1,7 @@
from django import forms
from django.forms import URLInput, ALL_FIELDS
from django.forms import ALL_FIELDS
from .models import Referral, ReferralStats, PartnerStats
from .models import Referral, ReferralStats
class ReferralAdminForm(forms.ModelForm):
@ -30,17 +30,3 @@ class ReferralStatsAdminForm(forms.ModelForm):
def disable_fields(self, fields):
for field_name, field in fields.items():
field.disabled = True
class PartnerStatsAdminForm(forms.ModelForm):
class Meta:
model = PartnerStats
fields = ALL_FIELDS
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.disable_fields(self.fields)
def disable_fields(self, fields):
for field_name, field in fields.items():
field.disabled = True

@ -15,6 +15,8 @@ from django.utils.translation import ugettext_lazy as _
# Create your models here.
from registration.signals import user_activated
from cart.models import Buying
from cart.signals import buying_bought
from core.models import AbstractStatusModel, STATUS_ACTIVE, STATUS_CHOICES
from referral.mixins import StatsFormatterMixin
from referral.utils import get_cookie
@ -30,7 +32,7 @@ REFERRAL_DEFAULT_CHOICE = STATUS_ACTIVE
class Referral(AbstractStatusModel):
CODE_LENGTH = 8
referral_owner = models.OneToOneField(get_user_model(), verbose_name=_('пользователь'), on_delete=models.CASCADE,
user = models.OneToOneField(get_user_model(), verbose_name=_('пользователь'), on_delete=models.CASCADE,
primary_key=True
)
code = models.CharField(_('код'), max_length=255)
@ -38,7 +40,7 @@ class Referral(AbstractStatusModel):
@classmethod
def create(cls, user, code):
referral = cls(referral_owner=user, code=code)
referral = cls(user=user, code=code)
return referral
@staticmethod
@ -69,23 +71,12 @@ class ReferralStats(StatsFormatterMixin, AbstractStatusModel):
referral = models.OneToOneField(Referral, verbose_name=_('Реферал'), on_delete=models.CASCADE, primary_key=True)
visits = models.IntegerField(_('Посищения'), default=0)
registrations = models.IntegerField(_('Регистрации'), default=0)
points = models.IntegerField(_('партенрские балы'), default=0)
class Meta:
verbose_name = _('Реферальная статистика')
verbose_name_plural = _('Реферальная статистика')
class PartnerStats(StatsFormatterMixin, AbstractStatusModel):
user = models.OneToOneField(get_user_model(), verbose_name=_('username'), on_delete=models.CASCADE,
primary_key=True)
total_visits = models.BigIntegerField(_('Всего посещений'), default=0)
total_regs = models.BigIntegerField(_('Всего регистраций'), default=0)
class Meta:
verbose_name = _('Партнеская статистика')
verbose_name_plural = _('Партнерская статистика')
@receiver(post_save, sender=get_user_model())
def create_user_referral(sender, instance, created, **kwargs):
if created and Referral.objects.filter(user=instance).first() is None:
@ -95,8 +86,6 @@ def create_user_referral(sender, instance, created, **kwargs):
@receiver(post_save, sender=Referral)
def init_referral_stuff(sender, instance, created, *args, **kwargs):
if created:
if PartnerStats.objects.filter(user=instance.referral_owner).count() == 0:
PartnerStats.objects.create(user=instance.referral_owner).save()
if ReferralStats.active.filter(referral=instance).count() == 0:
ReferralStats.active.create(referral=instance).save()
@ -108,11 +97,14 @@ def update_ref_stats(sender, user, request, **kwargs):
referral = Referral.objects.filter(code__exact=referral_code).first()
if referral:
referral.referralstats.registrations += 1
referral.referralstats.points += settings.DEFAULT_REFERRAL_POINTS
referral.referralstats.save()
referral.user.partnerstats.total_regs += 1
referral.user.partnerstats.save()
user.referral_code = referral
user.referral_user = referral.referral_owner
user.referral_user = referral.user
user.save()
else:
logger.warning("Missing referral code in database: " + referral_code)
@receiver(buying_bought,sender=Buying)
def charge_referral_points(sender,buying,*args,**kwargs):
pass

@ -1,3 +1,10 @@
.reg__text__center {
text-align: center;
}
@media (max-width: 991.98px) {
.mb-10 {
margin-bottom: 30px !important;
}
}

@ -2,7 +2,7 @@
{% load static %}
{% load crispy_forms_tags %}
{% block title %}Вход{% endblock %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
@ -13,6 +13,10 @@
<div class="col-12 text-center">
<div class="content__title title__data">Вход</div>
</div>
<div class="col-12 text-center ">
<div class="reg title__data-mob mb-10">Вход</div>
</div>
</div>
{% crispy form %}
</div>

@ -39,6 +39,8 @@
<div class="main">
<div class="container">
<div class="row">
{% block content_message %}
{% endblock content_message %}
{% block content %}
{% endblock content %}
</div>

@ -21,7 +21,7 @@
</div>
<div class="d-sm-flex flex-row cabinet__wrap">
<div class="cabinet__title bonus__title">
Ваша ссылка: <a href=""title="{{ ref_link }}">{{ ref_link }}</a>
Ваша ссылка: <a href="#"title="{{ ref_link }}">{{ ref_link }}</a>
</div>
<div class="bonus__btn_bx">
<a href="{{ request.path }}#" id="ref-link" class="cabinet__link_fill">Скопировать</a>
@ -38,7 +38,7 @@
{% for ref_user in referral_user_list %}
<div class="d-flex flex-row friends__row">
<div class="friends__title">{{ ref_user.name }}</div>
<div class="friends__info">{{ ref_user. }}</div>
<div class="friends__info">{{ ref_user }}</div>
</div>
{% empty %}
<div class="d-flex flex-row friends__row">
@ -81,7 +81,7 @@
{% endfor %}
</table>
<div class="history__btn_bx">
<a href="{% url 'cart:buyings_history' %}" class="cabinet__link_fill">Вся история покупок</a>
<a href="{% url 'cart:history' %}" class="cabinet__link_fill">Вся история покупок</a>
</div>
</div>
<div class="cabinet__userdata">

@ -2,7 +2,12 @@
{% block content %}
<div class="col-12 ">
<div class="cabinet__history">
<div class="cabinet__title">История покупок</div>
<div class="history__btn_bx">
<a href="{% url 'cabinet:index' %}" class="cabinet__link_fill">Вернуться</a>
</div>
<div class="cabinet__title text-center">
<h3>История покупок</h3>
</div>
<table class="table">
<tr>
<th>Дата</th>
@ -29,6 +34,23 @@
</tr>
{% endfor %}
</table>
<div class="pagination">
<span class="step-links">
{% if bought_item_list.has_previous %}
<a href="?page=1">&laquo; 1</a>
<a href="?page={{ bought_item_list.previous_page_number }}">previous</a>
{% endif %}
<span class="current">
Страница {{ bought_item_list.number }} of {{ bought_item_list.paginator.num_pages }}.
</span>
{% if contacts.has_next %}
<a href="?page={{ bought_item_list.next_page_number }}">next</a>
<a href="?page={{ bought_item_list.paginator.num_pages }}">last &raquo;</a>
{% endif %}
</span>
</div>
</div>
</div>
{% endblock content %}

@ -0,0 +1,170 @@
{% extends 'base.html' %}
{% block content %}
<div class="col-12">
<div class="content">
<div class="content__title">Корзина</div>
<div class="basket">
<div class="basket__title">
<div class="row">
<div class="col-6">
Товар
</div>
<div class="col-2">
Цена
</div>
<div class="col-2">
Количество
</div>
<div class="col-2">
Сумма
</div>
</div>
</div>
<div class="basket__item">
<div class="row align-items-center">
<div class="col-md-6 col-12">
<div class="basket__good d-flex align-items-center">
<div class="basket__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="basket__info">
<ul class="basket__breadcrumbs breadcrumbs">
<li>Товары</li>
<li>Антивирусы</li>
</ul>
<div class="basket__good-name">Microsoft Windows 7 Professional SP1
(x32/x64) GGK [6PC-00024]
</div>
</div>
</div>
</div>
<div class="col-md-6 col-12 basket__option">
<div class="row">
<div class="col-4 align-self-start">
<div class="basket__title-mob">Цена</div>
<div class="basket__price">10 000 ₽</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob align-self-start">Количество</div>
<div class="basket__count">2 шт</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob ">Сумма</div>
<div class="basket__sum">20 000 ₽</div>
</div>
</div>
</div>
</div>
</div>
<div class="basket__item">
<div class="row align-items-center">
<div class="col-md-6 col-12">
<div class="basket__good d-flex align-items-center">
<div class="basket__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="basket__info">
<ul class="basket__breadcrumbs breadcrumbs">
<li>Товары</li>
<li>Антивирусы</li>
</ul>
<div class="basket__good-name">Microsoft Windows 7 Professional SP1
(x32/x64) GGK [6PC-00024]
</div>
</div>
</div>
</div>
<div class="col-md-6 col-12 basket__option">
<div class="row">
<div class="col-4 align-self-start">
<div class="basket__title-mob">Цена</div>
<div class="basket__price">10 000 ₽</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob align-self-start">Количество</div>
<div class="basket__count">2 шт</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob ">Сумма</div>
<div class="basket__sum">20 000 ₽</div>
</div>
</div>
</div>
</div>
</div>
<div class="basket__item">
<div class="row align-items-center">
<div class="col-md-6 col-12">
<div class="basket__good d-flex align-items-center">
<div class="basket__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="basket__info">
<ul class="basket__breadcrumbs breadcrumbs">
<li>Товары</li>
<li>Антивирусы</li>
</ul>
<div class="basket__good-name">Microsoft Windows 7 Professional SP1
(x32/x64) GGK [6PC-00024]
</div>
</div>
</div>
</div>
<div class="col-md-6 col-12 basket__option">
<div class="row">
<div class="col-4 align-self-start">
<div class="basket__title-mob">Цена</div>
<div class="basket__price">10 000 ₽</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob align-self-start">Количество</div>
<div class="basket__count">2 шт</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob ">Сумма</div>
<div class="basket__sum">20 000 ₽</div>
</div>
</div>
</div>
</div>
</div>
<div class="basket__item">
<div class="row align-items-center">
<div class="col-md-6 col-12">
<div class="basket__good d-flex align-items-center">
<div class="basket__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="basket__info">
<ul class="basket__breadcrumbs breadcrumbs">
<li>Товары</li>
<li>Антивирусы</li>
</ul>
<div class="basket__good-name">Microsoft Windows 7 Professional SP1
(x32/x64) GGK [6PC-00024]
</div>
</div>
</div>
</div>
<div class="col-md-6 col-12 basket__option">
<div class="row">
<div class="col-4 align-self-start">
<div class="basket__title-mob">Цена</div>
<div class="basket__price">10 000 ₽</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob align-self-start">Количество</div>
<div class="basket__count">2 шт</div>
</div>
<div class="col-4 align-self-start">
<div class="basket__title-mob ">Сумма</div>
<div class="basket__sum">20 000 ₽</div>
</div>
</div>
</div>
</div>
</div>
<div class="basket__result">
<div class="result__sum">Итого: 20 000 ₽</div>
<div class="result__cashback">Кэшбек 800 ₽</div>
</div>
<div class="basket__controls">
<a class="basket__btn-fill" href="#">Оформить</a>
<a class="basket__btn" href="#">Продолжить покупки</a>
</div>
</div>
</div>
</div>
{% endblock content %}

@ -4,47 +4,42 @@
{% block content %}
<div class="col-4 left-menu">
<div class="">
{% if left_product_search_form %}
<div class="left-menu__search">
{% crispy left_product_search_form %}
</div>
{% endif %}
{% if left_product_search_form %}
<div class="left-menu__search">
{% crispy left_product_search_form %}
</div>
{% endif %}
{% block extra_left_menu_items %}
{% endblock extra_left_menu_items %}
{% if product_root_categories %}
<div class="left-menu__category">
{% for category in product_root_categories %}
<a class="left-menu__category-item"
href="{% url "products:category" slug=category.slug %}">
{% block extra_left_menu_items %}
{% endblock extra_left_menu_items %}
{% if product_root_categories %}
<div class="left-menu__category">
{% for category in product_root_categories %}
<a class="left-menu__category-item"
href="{% url "products:category" slug=category.slug %}">
<span class="category-item__image">
<img src="{% static category.img %}" alt="{{ category.alt }}">
</span>
<span class="category-item__name">{{ category.name }}</span>
</a>
{% endfor %}
</div>
{% endif %}
{% if contact_us_form %}
<span class="category-item__name">{{ category.name }}</span>
</a>
{% endfor %}
</div>
{% endif %}
{% if contact_us_form %}
<div class="left-menu__contact">
<div class="left-menu__title">
Не нашли нужную программу или разработчика?
Заполните форму и с Вами свяжется наш специалист.
</div>
{% if contact_us_form_message %}
{% if "success" in contact_us_form_message.tags %}
<div class="text-success">
{% elif "error" in contact_us_form_message.tags %}
<div class="text-danger">
{% endif %}
{{ contact_us_form_message }}
</div>
<div class="text-{% if "success" in contact_us_form_message.tags %}success{% elif "error" in contact_us_form_message.tags %}danger{% endif %}">
{{ contact_us_form_message }}
</div>
{% endif %}
{% crispy contact_us_form %}
</div>
{% endif %}
</div>
{% endif %}
</div>
<div class="col-lg-8 col-md-12">
<div class="content">
@ -62,6 +57,25 @@
{% endif %}
</div>
</div>
{% if product_root_categories %}
<div class="content__category">
<ul class="content__category-list">
{% for category in product_root_categories %}
<li>
<a href="{% url "products:category" slug=category.slug %}">
<span class="category-item__image">
<img src="{% static category.img %}" alt="{{ category.alt }}">
</span>
<span class="category-item__name">{{ category.name }}</span>
</a>
</li>
{% empty %}
<li><a href="#">Microsoft</a></li>
{% endfor %}
</ul>
</div>
{% endif %}
{% include 'components/breadcrumbs.html' %}
{% block right_common_content %}
{% endblock right_common_content %}

@ -1,2 +1,7 @@
{% block breadcrumbs %}
{% endblock breadcrumbs %}
{% if request.resolver_match.url_name != "index" %}
<ul class="breadcrumbs">
<li><a href="{% url 'index:index' %}">Главная страница</a></li>
{% block breadcrumbs %}
{% endblock breadcrumbs %}
</ul>
{% endif %}

@ -4,12 +4,12 @@
<div class="row align-items-center">
<div class="col-lg-3 col-md-12">
<div class="footer__about">
&copy; Компания «Русские Программы» 2015–2018
&copy; Компания «Русские Программы» 2015–{% now 'Y' %}
</div>
</div>
<div class="col-md col-md-2 col-5">
<ul class="footer__menu">
<li><a href="catalog.html">Каталог</a></li>
<li><a href="{% url 'products:product_category' %}">Каталог</a></li>
<li><a href="news.html">Новости</a></li>
<li><a href="cashback.html">Акции</a></li>
<li><a href="about.html">О компании</a></li>

@ -29,7 +29,7 @@
<div class="col-lg-2 col align-self-center">
<div class="row">
<div class="header__contact">
<a href="#">8 (495) 799-65-54</a>
<a href="call:84957996554">8 (495) 799-65-54</a>
</div>
</div>
<div class="row">
@ -49,7 +49,7 @@
<a href="{% url 'cabinet:index' %}" title="{{ request.user.username }}" class="header__link">Личный кабинет</a>
</div>
{% endif %}
<a href="basket.html" class="header__cart">
<a href="{% url 'cart:buyings' %}" class="header__cart">
{% if request.user.is_authenticated %}
<img src="{% static "img/header/cart-icon.svg" %}"
alt="Перейти в корзину"><span class="cart__count">{{ "5" }}</span>

@ -7,6 +7,13 @@
<li><a href="/flatpages_ext">Новости</a></li>
<li><a href="/flatpages_ext/cashback">Акции</a></li>
<li><a href="/flatpages_ext/about">О компании</a></li>
{% if not user.is_authenticated %}
<li><a href="{% url 'accounts_ext:register' %}">Регистрация</a></li>
<li><a href="{% url 'accounts_ext:login' %}">Войти</a></li>
{% else %}
<li><a href="{% url "cabinet:index" %}">Личный кабинет</a></li>
<li><a href="{% url "accounts_ext:logout" %}">Выйти</a></li>
{% endif %}
</ul>
</div>
</div>

@ -1,26 +1,58 @@
{% extends 'common.html' %}
{% block extra_left_menu_items %}
{% load static %}
{% block extra_left_menu_items %}
<div class="left-menu__filter">
<div class="filter__title">
Фильтр товаров
</div>
<div class="filter__subtitle">
Цена
</div>
<form action="">
<div class="filter__controls">
<span>от</span>
<input type="text" class="filter__min" value="">
<span>до</span>
<input type="text" class="filter__max" value="">
</div>
<div id="range" data-min="20" data-max="9999"></div>
</form>
<div class="filter__category">
<div class="filter__category-item">
<div class="category__title">Производитель</div>
<ul>
<li><a href="#">Nokia</a></li>
<li><a href="#">Apple</a></li>
<li><a href="#">Samsung</a></li>
<li><a href="#">Dexp</a></li>
<li><a href="#">Seven</a></li>
</ul>
</div>
<div class="filter__category-item">
<div class="category__title">Тип поставки</div>
<ul>
<li><a href="#">Nokia</a></li>
<li><a href="#">Apple</a></li>
<li><a href="#">Samsung</a></li>
<li><a href="#">Dexp</a></li>
<li><a href="#">Seven</a></li>
</ul>
</div>
<div class="filter__category-item">
<div class="category__title">Назначение</div>
<ul>
<li><a href="#">Nokia</a></li>
<li><a href="#">Apple</a></li>
<li><a href="#">Samsung</a></li>
<li><a href="#">Dexp</a></li>
<li><a href="#">Seven</a></li>
</ul>
</div>
</div>
</div>
{% endblock extra_left_menu_items %}
{% block right_common_content %}
<div class="content__category">
<ul class="content__category-list">
<li><a href="#">Microsoft</a></li>
<li><a href="#">Антивирусы</a></li>
<li><a href="#">1C</a></li>
<li><a href="#">Графика и видео</a></li>
<li><a href="#">Проектирование</a></li>
<li><a href="#">Офисные программы</a></li>
<li><a href="#">Резервное копирование</a></li>
<li><a href="#">Виртуализация</a></li>
</ul>
</div>
<ul class="breadcrumbs">
<li><a href="#">Главная страница</a></li>
<li><a href="#">Ещё страница</a></li>
<li><a href="#">Товары</a></li>
<li>Антивирусы</li>
</ul>
<div class="content__subtitle">Производители</div>
<ul class="content__maker-list">
<li><a href="#">Avast</a></li>
@ -42,142 +74,73 @@
</form>
</div>
<div class="catalog">
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
{% for product in products %}
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image">
<img src="{% static product.product_image.url %}" alt="{{ product.name }}">
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
<div class="catalog__dec">
<div class="catalog__title">
<a href="{% url 'products:product' slug=product.slug %}">
{{ product.name }}
</a>
</div>
<div class="catalog__price">
{{ product.offer.price }} {{ product.offer.currency }}
</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
</div>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
{% empty %}
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
</div>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
<div class="catalog__btn">
<a href="basket.html">Купить</a>
</div>
</div>
<div class="catalog__btn">
<a href="basket.html">Купить</a>
</div>
</div>
{% endfor %}
</div>
<div class="contact">
<form action="">

@ -1,27 +1,58 @@
{% extends 'common.html' %}
{% block title %}{{ title }}{% endblock title %}
{% block extra_left_menu_items %}
<div class="left-menu__filter">
<div class="filter__title">
Фильтр товаров
</div>
<div class="filter__subtitle">
Цена
</div>
<form action="">
<div class="filter__controls">
<span>от</span>
<input type="text" class="filter__min" value="">
<span>до</span>
<input type="text" class="filter__max" value="">
</div>
<div id="range" data-min="20" data-max="9999"></div>
</form>
<div class="filter__category">
<div class="filter__category-item">
<div class="category__title">Производитель</div>
<ul>
<li><a href="#">Nokia</a></li>
<li><a href="#">Apple</a></li>
<li><a href="#">Samsung</a></li>
<li><a href="#">Dexp</a></li>
<li><a href="#">Seven</a></li>
</ul>
</div>
<div class="filter__category-item">
<div class="category__title">Тип поставки</div>
<ul>
<li><a href="#">Nokia</a></li>
<li><a href="#">Apple</a></li>
<li><a href="#">Samsung</a></li>
<li><a href="#">Dexp</a></li>
<li><a href="#">Seven</a></li>
</ul>
</div>
<div class="filter__category-item">
<div class="category__title">Назначение</div>
<ul>
<li><a href="#">Nokia</a></li>
<li><a href="#">Apple</a></li>
<li><a href="#">Samsung</a></li>
<li><a href="#">Dexp</a></li>
<li><a href="#">Seven</a></li>
</ul>
</div>
</div>
</div>
{% endblock extra_left_menu_items %}
{% block right_common_content %}
<div class="content__category">
<ul class="content__category-list">
<li><a href="#">Microsoft</a></li>
<li><a href="#">Антивирусы</a></li>
<li><a href="#">1C</a></li>
<li><a href="#">Графика и видео</a></li>
<li><a href="#">Проектирование</a></li>
<li><a href="#">Офисные программы</a></li>
<li><a href="#">Резервное копирование</a></li>
<li><a href="#">Виртуализация</a></li>
</ul>
</div>
<ul class="breadcrumbs">
<li><a href="#">Главная страница</a></li>
<li><a href="#">Ещё страница</a></li>
<li><a href="#">Товары</a></li>
<li>Антивирусы</li>
</ul>
<div class="content__subtitle">Производители</div>
<ul class="content__maker-list">
<li><a href="#">Avast</a></li>
@ -43,142 +74,144 @@
</form>
</div>
<div class="catalog">
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
{% for product in products %}
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__btn">
<a href="#">Купить</a>
</div>
</div>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
<div class="catalog__item">
<div class="catalog__information">
<div class="catalog__image"><img src="./img/item-image-min.png" alt=""></div>
<div class="catalog__dec">
<div class="catalog__title"><a href="#">ESET NOD32 Secure Enterprise новая лицензия
для 26 компьютеров</a></div>
<div class="catalog__star">
<form action="">
<select class="stars" data-rating="2">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</form>
</div>
<div class="catalog__price">79 400 ₽ / 1398 $</div>
<div class="catalog__dec-list">
<ul>
<li><span>Артикул:</span>057J1-WWW8695-T548</li>
<li><span>НДС:</span>Включен</li>
<li><span>Платформа:</span>Windows</li>
<li><span>Тип поставки:</span>Любой из доступных</li>
<li><span>Срок поставки:</span>3-14 дней</li>
<li><span>Примечание:</span>Временная лицензия, 1 год – новая покупка, локальная</li>
</ul>
</div>
</div>
</div>
<div class="catalog__btn">
<a href="basket.html">Купить</a>
</div>
</div>
<div class="catalog__btn">
<a href="basket.html">Купить</a>
</div>
</div>
{% endfor %}
</div>
<div class="contact">
<form action="">

Loading…
Cancel
Save