diff --git a/apps/course/templates/course/course.html b/apps/course/templates/course/course.html index 7ab898b1..72b3df68 100644 --- a/apps/course/templates/course/course.html +++ b/apps/course/templates/course/course.html @@ -29,17 +29,22 @@ Редактировать {% endif %} {% if course.author != request.user and not paid and course.price %} - + {% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} + {% if not paid %} + Подарить другу {% endif %} - >{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} + {% endif %}
Редактировать {% endif %} {% if not paid and course.price and not has_full_access %} - + {% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} + {% if not paid %} + Подарить другу {% endif %} - >{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %} +
{% endif %}
Поздравляем с успешной + покупкой!

+
+

Вы получаете {{ gift_certificate.price }} лиликов на счет! 1 LIL = 1 руб.
+ Накапливайте монеты и тратьте их на оплату школы и курсов. +

+
+
+
+
+ + + + + + + +
Подарочный сертификат + {{ gift_certificate.price }} р. +
+
+
+
Чтобы воспользоваться сертификатом, перейдите по ссылке
+
+ + {% setting 'MAIN_HOST' %}{% url 'index' %}?gift-certificate={{ user_gift_certificate.code }} +
+
+ Или воспользуйтесь сертификатом, введя уникальный код на в разделе + вашего профиля на сайте lil.school +
+
Ваш код +
+
+
{{ user_gift_certificate.code }} +
+
+
+ Вы так же можете отправить это письмо, ссылку или код вашему другу, чтобы подарить ему этот сертификат. +
+
+ Присоединяйтесь к нам! +
+
+ {% if config.SERVICE_TWITTER_URL %} + + + + {% endif %} + {% if config.SERVICE_FB_URL %} + + + + {% endif %} + {% if config.SERVICE_INSTAGRAM_URL %} + + + + {% endif %} +
+ +{% endblock content %} diff --git a/apps/notification/utils.py b/apps/notification/utils.py index d556bd96..07510c41 100644 --- a/apps/notification/utils.py +++ b/apps/notification/utils.py @@ -1,16 +1,27 @@ +from django.contrib.staticfiles.storage import staticfiles_storage from twilio.rest import Client - -from django.core.mail import EmailMessage +from django.core.mail import EmailMessage, EmailMultiAlternatives +from anymail.message import attach_inline_image_file from django.conf import settings from django.template.loader import get_template + from project.celery import app @app.task -def send_email(subject, to_email, template_name, attachments=[], **kwargs): - html = get_template(template_name).render(kwargs) - email = EmailMessage(subject, html, to=[to_email], attachments=attachments) - email.content_subtype = 'html' +def send_email(subject, to_email, template_name, attachments=[], inline_images=[], **kwargs): + if inline_images: + email = EmailMultiAlternatives(subject, '', to=[to_email], attachments=attachments) + context = kwargs + for name, path in inline_images: + cid = attach_inline_image_file(email, staticfiles_storage.path(path)) + context[name] = cid + html = get_template(template_name).render(context) + email.attach_alternative(html, "text/html") + else: + html = get_template(template_name).render(kwargs) + email = EmailMessage(subject, html, to=[to_email], attachments=attachments) + email.content_subtype = 'html' email.send() diff --git a/apps/payment/admin.py b/apps/payment/admin.py index 19166563..b26b03a2 100644 --- a/apps/payment/admin.py +++ b/apps/payment/admin.py @@ -5,7 +5,7 @@ from polymorphic.admin import ( PolymorphicChildModelFilter, ) -from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment +from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, GiftCertificate @admin.register(AuthorBalance) @@ -61,3 +61,8 @@ class PaymentAdmin(PolymorphicParentModelAdmin): CoursePayment, SchoolPayment, ) + + +@admin.register(GiftCertificate) +class GiftCertificateAdmin(admin.ModelAdmin): + pass diff --git a/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py b/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py new file mode 100644 index 00000000..3f9dcc34 --- /dev/null +++ b/apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py @@ -0,0 +1,34 @@ +# Generated by Django 2.0.6 on 2018-10-29 14:36 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('payment', '0024_auto_20181002_0338'), + ] + + operations = [ + migrations.CreateModel( + name='GiftCertificate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('price', models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=8)), + ], + ), + migrations.CreateModel( + name='UserGiftCertificate', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('recipient', models.EmailField(max_length=254, blank=True, null=True,)), + ('bonuses_sent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='payment.UserBonus')), + ('gift_certificate', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='payment.GiftCertificate')), + ('payment', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='payment.Payment')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='gift_certificates', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/apps/payment/migrations/0026_auto_20181101_1546.py b/apps/payment/migrations/0026_auto_20181101_1546.py new file mode 100644 index 00000000..45cd9906 --- /dev/null +++ b/apps/payment/migrations/0026_auto_20181101_1546.py @@ -0,0 +1,39 @@ +# Generated by Django 2.0.6 on 2018-11-01 15:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0025_giftcertificate_usergiftcertificate'), + ] + + operations = [ + migrations.CreateModel( + name='GiftCertificatePayment', + fields=[ + ('payment_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='payment.Payment')), + ], + options={ + 'verbose_name': 'Платеж за подарочный сертификат', + 'verbose_name_plural': 'Платежи за подарочные сертификаты', + }, + bases=('payment.payment',), + ), + migrations.AlterModelOptions( + name='giftcertificate', + options={'ordering': ('price',)}, + ), + migrations.AlterField( + model_name='giftcertificate', + name='price', + field=models.DecimalField(decimal_places=2, default=0, max_digits=8), + ), + migrations.AddField( + model_name='giftcertificatepayment', + name='gift_certificate', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='payment.GiftCertificate', verbose_name='Подарочный сертификат'), + ), + ] diff --git a/apps/payment/migrations/0027_auto_20181109_1402.py b/apps/payment/migrations/0027_auto_20181109_1402.py new file mode 100644 index 00000000..a228e06b --- /dev/null +++ b/apps/payment/migrations/0027_auto_20181109_1402.py @@ -0,0 +1,22 @@ +# Generated by Django 2.0.6 on 2018-11-09 14:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0026_auto_20181101_1546'), + ] + + operations = [ + migrations.RemoveField( + model_name='usergiftcertificate', + name='recipient', + ), + migrations.AddField( + model_name='giftcertificate', + name='cover', + field=models.CharField(blank=True, default='', max_length=255), + ), + ] diff --git a/apps/payment/migrations/0028_add_gift_certificates.py b/apps/payment/migrations/0028_add_gift_certificates.py new file mode 100644 index 00000000..18c0ac4f --- /dev/null +++ b/apps/payment/migrations/0028_add_gift_certificates.py @@ -0,0 +1,21 @@ +# Generated by Django 2.0.6 on 2018-11-09 14:03 + +from django.db import migrations +from django.contrib.staticfiles.storage import staticfiles_storage + + +def add_gift_certificates(apps, schema_editor): + GiftCertificate = apps.get_model('payment', 'GiftCertificate') + for price in [1000, 2000, 3000, 5000, 10000]: + GiftCertificate.objects.create(price=price, + cover=staticfiles_storage.url('img/gift-certificates/%d.jpg' % price)) + +class Migration(migrations.Migration): + + dependencies = [ + ('payment', '0027_auto_20181109_1402'), + ] + + operations = [ + migrations.RunPython(add_gift_certificates), + ] diff --git a/apps/payment/models.py b/apps/payment/models.py index a953d5e8..413e8bf8 100644 --- a/apps/payment/models.py +++ b/apps/payment/models.py @@ -1,5 +1,6 @@ from decimal import Decimal import arrow +import short_url from django.db.models import Func, F from paymentwall import Pingback @@ -13,6 +14,7 @@ from django.core.validators import RegexValidator from django.utils.timezone import now from django.conf import settings +from apps.content.models import ImageObject from project.utils import weekdays_in_date_range from apps.course.models import Course @@ -147,6 +149,8 @@ class Payment(PolymorphicModel): referrer_bonus = user.referral.referrer_bonus if payment and payment.is_paid(): price = payment.amount + elif isinstance(payment, GiftCertificatePayment): + price = payment.gift_certificate.price elif course: price = course.price else: @@ -244,6 +248,12 @@ class Payment(PolymorphicModel): else: author_balance.amount = self.amount author_balance.save() + if isinstance(self, GiftCertificatePayment) and self.is_paid(): + ugs, created = UserGiftCertificate.objects.get_or_create(user=self.user, gift_certificate=self.gift_certificate, + payment=self) + if created: + from apps.notification.tasks import send_gift_certificate + send_gift_certificate(ugs.id) # Если юзер реферал и нет платежа, где применялась скидка if hasattr(self.user, 'referral') and not self.user.referral.payment and self.is_paid(): # Платеж - как сигнал, что скидка применилась @@ -283,6 +293,15 @@ class SchoolPayment(Payment): return arrow.get(self.date_end, settings.TIME_ZONE).humanize(locale='ru') +class GiftCertificatePayment(Payment): + gift_certificate = models.ForeignKey('GiftCertificate', on_delete=models.CASCADE, + verbose_name='Подарочный сертификат', related_name='payments') + + class Meta: + verbose_name = 'Платеж за подарочный сертификат' + verbose_name_plural = 'Платежи за подарочные сертификаты' + + class UserBonus(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='bonuses') amount = models.DecimalField(max_digits=8, decimal_places=2, default=0, editable=False) @@ -292,3 +311,22 @@ class UserBonus(models.Model): class Meta: ordering = ('created_at',) + + +class GiftCertificate(models.Model): + price = models.DecimalField(max_digits=8, decimal_places=2, default=0) + cover = models.CharField(max_length=255, blank=True, default='') + + class Meta: + ordering = ('price',) + + +class UserGiftCertificate(models.Model): + gift_certificate = models.ForeignKey(GiftCertificate, on_delete=models.CASCADE,) + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='gift_certificates') + payment = models.ForeignKey(Payment, on_delete=models.SET_NULL, null=True) + bonuses_sent = models.ForeignKey(UserBonus, on_delete=models.CASCADE, blank=True, null=True) + + @property + def code(self): + return short_url.encode_url(self.id) if self.id else None diff --git a/apps/payment/templates/payment/gift_certificate.html b/apps/payment/templates/payment/gift_certificate.html new file mode 100644 index 00000000..7d291137 --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate.html @@ -0,0 +1,17 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} + +{% block content %} +
+
+
Вам подарок!
+
+ Пользователь ХХХ дарит вам сертификат на сумму ХХХ руб +
+ +
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/gift_certificate_get.html b/apps/payment/templates/payment/gift_certificate_get.html new file mode 100644 index 00000000..6242f060 --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_get.html @@ -0,0 +1,15 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} +
+
+
+
Бонусы зачислены на ваш счет!
+
Вы можете оплатить с их помощью курс или онлайн-школу
+ +
+
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/gift_certificate_item.html b/apps/payment/templates/payment/gift_certificate_item.html new file mode 100644 index 00000000..5f4a318e --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_item.html @@ -0,0 +1,36 @@ +{% load thumbnail %} +{% load static %} +{% load data_liked from data_liked %} + +
+
+ +
+
+ + {% if user_gift_certificate and not user_gift_certificate.bonuses_sent %} + подарочный сертификат + {% else %}подарочный сертификат{% endif %} +
{{ gift_certificate.price|floatformat:"-2" }}₽
+
+ {% if user_gift_certificate %} + {% if user_gift_certificate.bonuses_sent %} +
+ + Получено +
+ {% else %} +
+ + Ожидает получения +
+ {% endif %} + {% else %} + Купить сертификат + {% endif %} +
diff --git a/apps/payment/templates/payment/gift_certificate_items.html b/apps/payment/templates/payment/gift_certificate_items.html new file mode 100644 index 00000000..05730083 --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_items.html @@ -0,0 +1,4 @@ +{% for gift_certificate in gift_certificates %} + {% cycle 'theme_pink2' 'theme_cyan' 'theme_violet2' as theme_color silent %} + {% include "payment/gift_certificate_item.html" %} +{% endfor %} diff --git a/apps/payment/templates/payment/gift_certificate_payment_success.html b/apps/payment/templates/payment/gift_certificate_payment_success.html new file mode 100644 index 00000000..793eb25e --- /dev/null +++ b/apps/payment/templates/payment/gift_certificate_payment_success.html @@ -0,0 +1,13 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %} +
+
+
+
Вы успешно приобрели подарочный сертификат!
+
Мы отправили письмо с сертификатом на вашу почту.
+ +
+
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/gift_certificates.html b/apps/payment/templates/payment/gift_certificates.html new file mode 100644 index 00000000..5fa4bf2e --- /dev/null +++ b/apps/payment/templates/payment/gift_certificates.html @@ -0,0 +1,35 @@ +{% extends "templates/lilcity/index.html" %} {% load static %} + +{% block content %} +
+
+
+ Подарочные сертификаты +
+
+
+ +
+
+
Подарочный сертификат Lil City - отличный презент на любой праздник.
+А также сертификат можно подарить без повода - развитие
творческого потенциала всегда уместно и актуально. +При покупке подарочного сертификата мы высылаем письмо,
+где будет уникальный код и уникальная ссылка. Письмо или ссылку
+на него вы можете переслать другому. Сертификатом можно
+воспользоваться, перейдя по ссылке. + +
+
Сертификаты
+
+ {% include "payment/gift_certificate_items.html" %} +
+
+
+{% endblock content %} diff --git a/apps/payment/templates/payment/payment_success.html b/apps/payment/templates/payment/payment_success.html index 7d174018..9d256634 100644 --- a/apps/payment/templates/payment/payment_success.html +++ b/apps/payment/templates/payment/payment_success.html @@ -7,10 +7,17 @@
ПЕРЕЙТИ К ШКОЛЕ
- {% else %} + {% endif %} + {% if course %}
Вы успешно приобрели курс!
- ПЕРЕЙТИ К ГЛАВНОЙ + ПЕРЕЙТИ К КУРСУ +
+ {% endif %} + {% if gift_certificate %} +
Вы успешно приобрели подарочный сертификат!
+
+ ПЕРЕЙТИ НА ГЛАВНУЮ
{% endif %}
diff --git a/apps/payment/views.py b/apps/payment/views.py index 6be7734f..02a2b874 100644 --- a/apps/payment/views.py +++ b/apps/payment/views.py @@ -1,5 +1,5 @@ from decimal import Decimal - +import short_url import arrow import json import logging @@ -12,9 +12,9 @@ import calendar from django.contrib import messages from django.contrib.auth.decorators import login_required -from django.http import HttpResponse +from django.http import HttpResponse, Http404 from django.shortcuts import redirect, get_object_or_404 -from django.views.generic import View, TemplateView +from django.views.generic import View, TemplateView, DetailView from django.views.decorators.csrf import csrf_exempt from django.urls import reverse_lazy from django.utils.decorators import method_decorator @@ -27,7 +27,8 @@ from apps.course.models import Course from apps.school.models import SchoolSchedule from apps.payment.tasks import transaction_to_mixpanel, product_payment_to_mixpanel, transaction_to_roistat -from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus +from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserBonus, GiftCertificate, \ + GiftCertificatePayment, UserGiftCertificate logger = logging.getLogger('django') @@ -216,6 +217,8 @@ class PaymentwallCallbackView(View): product_payment_class = CoursePayment elif product_type_name == 'school': product_payment_class = SchoolPayment + elif product_type_name == 'gift_certificate': + product_payment_class = GiftCertificatePayment else: return HttpResponse(status=403) @@ -264,6 +267,15 @@ class PaymentwallCallbackView(View): 'created_at': payment.created_at, 'update_at': payment.update_at, } + elif product_type_name == 'gift_certificate': + properties = { + 'payment_id': payment.id, + 'amount': payment.amount, + 'status': payment.status, + 'gift_certificate': payment.gift_certificate.id, + 'created_at': payment.created_at, + 'update_at': payment.update_at, + } payment.save() product_payment_to_mixpanel.delay( @@ -297,3 +309,84 @@ class PaymentwallCallbackView(View): else: raise DisallowedPingbackHost return HttpResponse(status=403) + + +class GiftCertificatesView(TemplateView): + model = GiftCertificate + template_name = 'payment/gift_certificates.html' + + def get(self, request, *args, **kwargs): + gift_certificates = GiftCertificate.objects.all() + context = self.get_context_data(**kwargs) + context['gift_certificates'] = gift_certificates + return self.render_to_response(context) + + +@method_decorator(login_required, name='dispatch') +class GiftCertificateBuyView(TemplateView): + model = GiftCertificate + template_name = 'payment/paymentwall_widget.html' + + def get(self, request, pk, *args, **kwargs): + gift_certificate = get_object_or_404(GiftCertificate, pk=pk) + roistat_visit = request.COOKIES.get('roistat_visit', None) + gift_certificate_payment = GiftCertificatePayment.objects.create( + user=request.user, + gift_certificate=gift_certificate, + roistat_visit=roistat_visit,) + context = self.get_context_data(**kwargs) + product = Product( + f'gift_certificate_{gift_certificate_payment.id}', + gift_certificate_payment.amount, + 'RUB', + 'Подарочный сертификат', + ) + host = urlsplit(self.request.META.get('HTTP_REFERER')) + host = str(host[0]) + '://' + str(host[1]) + widget = Widget( + str(request.user.id), + 'p1_1', + [product], + extra_params={ + 'lang': 'ru', + 'evaluation': 1, + 'demo': 1, + 'test_mode': 1, + 'success_url': host + str(reverse_lazy('gift-certificate-payment-success', args=[gift_certificate_payment.id])), + 'failure_url': host + str(reverse_lazy('payment-error')), + } + ) + context['widget'] = widget.get_html_code() + return self.render_to_response(context) + + +@method_decorator(login_required, name='dispatch') +class GiftCertificateBuySuccessView(TemplateView): + template_name = 'payment/gift_certificate_payment_success.html' + + def get(self, request, payment_id=None, *args, **kwargs): + try: + GiftCertificatePayment.objects.get(id=payment_id) + except: + raise Http404() + return self.render_to_response(context={'gift_certificate': True}) + + +@method_decorator(login_required, name='dispatch') +class GiftCertificateGetView(TemplateView): + template_name = 'payment/gift_certificate_get.html' + + def get(self, request, slug, *args, **kwargs): + try: + ugs = get_object_or_404(UserGiftCertificate, pk=short_url.decode_url(slug)) + except: + raise Http404() + if UserBonus.objects.filter(payment=ugs.payment).exists(): + raise Http404() + bonuses = UserBonus.objects.create(user=request.user, amount=ugs.gift_certificate.price, + payment=ugs.payment) + ugs.bonuses_sent = bonuses + ugs.save() + context = self.get_context_data(**kwargs) + return self.render_to_response(context) + diff --git a/apps/school/templates/summer/promo.html b/apps/school/templates/summer/promo.html index 8b1ed12e..7d12fac4 100644 --- a/apps/school/templates/summer/promo.html +++ b/apps/school/templates/summer/promo.html @@ -20,6 +20,9 @@ {% if is_purchased %}ваша подписка истекает {{ subscription_ends_humanize }}
перейти к оплате{% endif %} + {% if not is_purchased and not is_purchased_future %} + Подарить другу + {% endif %}
diff --git a/apps/user/templates/user/profile.html b/apps/user/templates/user/profile.html index 9ad4d552..b8e19a19 100644 --- a/apps/user/templates/user/profile.html +++ b/apps/user/templates/user/profile.html @@ -2,7 +2,10 @@
- Редактировать +
{% thumbnail user.photo "120x120" crop="center" as im %} @@ -91,10 +94,16 @@ Перейти в онлайн-школу
{% endif %} + {% if user_gift_certificates.exists %} + {% for ugs in user_gift_certificates %} + {% cycle 'theme_pink2' 'theme_cyan' 'theme_violet2' as theme_color silent %} + {% include "payment/gift_certificate_item.html" with gift_certificate=ugs.gift_certificate user_gift_certificate=ugs %} + {% endfor %} + {% endif %} {% if paid.exists %} {% include "course/course_items.html" with course_items=paid %} {% endif %} - {% if not is_school_purchased and not paid.exists %} + {% if not is_school_purchased and not paid.exists and not user_gift_certificates.exists %}
Вы пока ничего не приобрели...
diff --git a/apps/user/views.py b/apps/user/views.py index f4ea0ebb..e456af1c 100644 --- a/apps/user/views.py +++ b/apps/user/views.py @@ -24,7 +24,7 @@ from apps.config.models import Config from apps.course.models import Course from apps.notification.utils import send_email from apps.school.models import SchoolSchedule -from apps.payment.models import AuthorBalance, CoursePayment, SchoolPayment, Payment +from apps.payment.models import AuthorBalance, CoursePayment, SchoolPayment, Payment, UserGiftCertificate from apps.user.models import AuthorRequest, EmailSubscription, SubscriptionCategory from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm @@ -59,6 +59,7 @@ class ProfileView(TemplateView): author=self.object, ) context['is_author'] = context['published'] or self.request.user.role == User.AUTHOR_ROLE + context['user_gift_certificates'] = UserGiftCertificate.objects.filter(user=self.request.user) context['paid'] = Course.objects.filter( payments__in=CoursePayment.objects.filter( user=self.object, diff --git a/project/templates/blocks/header.html b/project/templates/blocks/header.html index 312d06ec..5ad39c12 100644 --- a/project/templates/blocks/header.html +++ b/project/templates/blocks/header.html @@ -41,6 +41,10 @@ +
{% include 'templates/blocks/user_menu.html' %} diff --git a/project/templates/blocks/lil_store_js.html b/project/templates/blocks/lil_store_js.html index 7df6d16d..244eb875 100644 --- a/project/templates/blocks/lil_store_js.html +++ b/project/templates/blocks/lil_store_js.html @@ -28,6 +28,7 @@ referrer: '{{ referrer.id|default:'' }}', referrerName: '{% if referrer %}{{ referrer.get_full_name }}{% endif %}', isReferralUrl: {{ is_referral_url|yesno:"true,false" }}, + isGiftCertificateUrl: {{ is_gift_certificate_url|yesno:"true,false" }}, }, }; diff --git a/project/templates/blocks/popup_enter_gift_code.html b/project/templates/blocks/popup_enter_gift_code.html new file mode 100644 index 00000000..7ad5c256 --- /dev/null +++ b/project/templates/blocks/popup_enter_gift_code.html @@ -0,0 +1,24 @@ +{% load static %} + diff --git a/project/templates/blocks/popup_gift_certificate.html b/project/templates/blocks/popup_gift_certificate.html new file mode 100644 index 00000000..f1d551a8 --- /dev/null +++ b/project/templates/blocks/popup_gift_certificate.html @@ -0,0 +1,31 @@ +{% load static %} + diff --git a/project/templates/blocks/promo.html b/project/templates/blocks/promo.html index 18ad35d2..1dcb87d2 100644 --- a/project/templates/blocks/promo.html +++ b/project/templates/blocks/promo.html @@ -26,6 +26,10 @@ {% endif %} class="main__btn btn" >{% if not school_schedule.weekday in school_schedules_purchased %}Получить доступ{% else %}Смотреть урок{% endif %} + + {% if not is_purchased and not is_purchased_future %} + Подарить другу + {% endif %}
{% elif user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %}
@@ -45,6 +49,10 @@ {% endif %} class="main__btn btn" >{% if not school_schedule.weekday in school_schedules_purchased %}Получить доступ{% else %}Смотреть урок{% endif %} + + {% if not is_purchased and not is_purchased_future %} + Подарить другу + {% endif %}
{% else %}
@@ -59,6 +67,7 @@ > купить доступ от {{ min_school_price }} руб./месяц + Подарить другу {% else %} Подробнее {% endif %} diff --git a/project/templates/lilcity/index.html b/project/templates/lilcity/index.html index 70189e29..385b7da0 100644 --- a/project/templates/lilcity/index.html +++ b/project/templates/lilcity/index.html @@ -145,6 +145,10 @@ {% if course %} {% include "templates/blocks/popup_course_buy.html" %} {% endif %} + {% if is_gift_certificate_url %} + {% include "templates/blocks/popup_gift_certificate.html" %} + {% endif %} + {% include "templates/blocks/popup_enter_gift_code.html" %} {% include "templates/blocks/popup_course_lock.html" %} {% include "templates/blocks/popup_subscribe.html" %}
diff --git a/project/urls.py b/project/urls.py index 38773131..4946c577 100644 --- a/project/urls.py +++ b/project/urls.py @@ -34,8 +34,8 @@ from apps.user.views import ( from apps.payment.views import ( CourseBuySuccessView, CourseBuyView, PaymentwallCallbackView, SchoolBuySuccessView, - SchoolBuyView, -) + SchoolBuyView, GiftCertificatesView, GiftCertificateBuyView, + GiftCertificateBuySuccessView, GiftCertificateGetView) from .views import AboutView, IndexView, SchoolSchedulesView @@ -91,6 +91,11 @@ urlpatterns = [ path('contest//', ContestView.as_view(), name='contest'), path('contest-work//', ContestWorkView.as_view(), name='contest_work'), path('contest-work//comment', contest_work_comment, name='contest_work_comment'), + path('gift-certificates', GiftCertificatesView.as_view(), name='gift-certificates'), + path('gift-certificate//checkout', GiftCertificateBuyView.as_view(), name='gift-certificate-checkout'), + path('payments/gift-certificate//success', GiftCertificateBuySuccessView.as_view(), + name='gift-certificate-payment-success'), + path('gift-certificate//get', GiftCertificateGetView.as_view(), name='gift-certificate-get'), ] diff --git a/project/views.py b/project/views.py index 29189994..d563f0c6 100644 --- a/project/views.py +++ b/project/views.py @@ -9,7 +9,7 @@ from paymentwall.pingback import Pingback from apps.course.models import Course from apps.school.models import SchoolSchedule -from apps.payment.models import SchoolPayment +from apps.payment.models import SchoolPayment, UserGiftCertificate User = get_user_model() @@ -23,6 +23,7 @@ class IndexView(TemplateView): def get_context_data(self): referrer = self.request.GET.get('referrer') + user_gift_certificate = self.request.GET.get('gift-certificate') is_referral_url = bool(referrer) context = super().get_context_data() @@ -77,7 +78,17 @@ class IndexView(TemplateView): else: referrer = None + if user_gift_certificate: + try: + user_gift_certificate = short_url.decode_url(user_gift_certificate) + user_gift_certificate = UserGiftCertificate.objects.get(pk=user_gift_certificate, bonuses_sent__isnull=True) + except: + user_gift_certificate = None + context.update({ + 'gift_certificate': user_gift_certificate.gift_certificate if user_gift_certificate else None, + 'user_gift_certificate': user_gift_certificate, + 'is_gift_certificate_url': bool(user_gift_certificate), 'referrer': referrer, 'is_referral_url': is_referral_url, 'online': online, diff --git a/web/src/img/clock.png b/web/src/img/clock.png new file mode 100644 index 00000000..dd49d20c Binary files /dev/null and b/web/src/img/clock.png differ diff --git a/web/src/img/done.png b/web/src/img/done.png new file mode 100644 index 00000000..28a2934a Binary files /dev/null and b/web/src/img/done.png differ diff --git a/web/src/img/fb.png b/web/src/img/fb.png new file mode 100644 index 00000000..a31cdcd3 Binary files /dev/null and b/web/src/img/fb.png differ diff --git a/web/src/img/gift-certificates/1000.jpg b/web/src/img/gift-certificates/1000.jpg new file mode 100644 index 00000000..9d682d7f Binary files /dev/null and b/web/src/img/gift-certificates/1000.jpg differ diff --git a/web/src/img/gift-certificates/10000.jpg b/web/src/img/gift-certificates/10000.jpg new file mode 100644 index 00000000..9695841d Binary files /dev/null and b/web/src/img/gift-certificates/10000.jpg differ diff --git a/web/src/img/gift-certificates/2000.jpg b/web/src/img/gift-certificates/2000.jpg new file mode 100644 index 00000000..ec0483ac Binary files /dev/null and b/web/src/img/gift-certificates/2000.jpg differ diff --git a/web/src/img/gift-certificates/3000.jpg b/web/src/img/gift-certificates/3000.jpg new file mode 100644 index 00000000..bb6f3970 Binary files /dev/null and b/web/src/img/gift-certificates/3000.jpg differ diff --git a/web/src/img/gift-certificates/5000.jpg b/web/src/img/gift-certificates/5000.jpg new file mode 100644 index 00000000..5bb83064 Binary files /dev/null and b/web/src/img/gift-certificates/5000.jpg differ diff --git a/web/src/img/instagram.png b/web/src/img/instagram.png new file mode 100644 index 00000000..da7043cc Binary files /dev/null and b/web/src/img/instagram.png differ diff --git a/web/src/img/twitter.png b/web/src/img/twitter.png new file mode 100644 index 00000000..9ce49587 Binary files /dev/null and b/web/src/img/twitter.png differ diff --git a/web/src/js/modules/common.js b/web/src/js/modules/common.js index a2bc6999..b03ad0cc 100644 --- a/web/src/js/modules/common.js +++ b/web/src/js/modules/common.js @@ -55,9 +55,9 @@ $(document).ready(function () { $('.js-header-enter').click(); }); } + $popupAuth.find(authPopupActiveTab == 'login' ? '.js-auth-type_login' : '.js-auth-type_registration').addClass('active'); + $popupAuth.find(authPopupActiveTab == 'login' ? '.js-auth-tab_login' : '.js-auth-tab_registration').show(); } - $popupAuth.find(authPopupActiveTab == 'login' ? '.js-auth-type_login' : '.js-auth-type_registration').addClass('active'); - $popupAuth.find(authPopupActiveTab == 'login' ? '.js-auth-tab_login' : '.js-auth-tab_registration').show(); }); function updateHeader() { diff --git a/web/src/js/modules/popup.js b/web/src/js/modules/popup.js index 286797c6..46be2206 100644 --- a/web/src/js/modules/popup.js +++ b/web/src/js/modules/popup.js @@ -137,6 +137,21 @@ $(document).ready(function () { } popup.data('next-url', nextUrl); } + + if( data === '.js-popup-enter-gift-code') { + const $giftCode = popup.find('.enter-gift-code__code'); + const $giftError = popup.find('.enter-gift-code__error'); + $giftCode.val(''); + $giftError.text(''); + popup.find('.enter-gift-code__btn').one('click', () => { + const code = $giftCode.val(); + if(! code){ + $giftError.text('Укажите код'); + return false; + } + window.location.href = `/gift-certificate/${code}/get`; + }); + } }); $('.js-popup-close').on('click', function(e){ @@ -158,6 +173,12 @@ $(document).ready(function () { if (e.keyCode === 27) hidePopup(); }); + + if(window.LIL_STORE.flags.isGiftCertificateUrl){ + popup = $('.js-popup-gift-certificate'); + showPopup(); + } + function showPopup(){ body.addClass('no-scroll'); popup.addClass('open'); diff --git a/web/src/sass/_common.sass b/web/src/sass/_common.sass index 28847a1c..3041bed7 100755 --- a/web/src/sass/_common.sass +++ b/web/src/sass/_common.sass @@ -26,12 +26,15 @@ $pink: #FF9393 $pink-light: #FDF8F9 +$pink2: #FEB9B9 $purple: #B995D9 $green: #8ECFC0 $green-light: #5BD700 +$cyan: #B6DFD6 $gray: #A7A7A7 $blue: #4A90E2 $viol: #B995D9 +$viol2: #A186BD $bg: #f8f8f8 $border: #E6E6E6 $cl: #191919 @@ -206,6 +209,10 @@ button &_pink background: #ff9393 color: white + &_stroke-black + background: none + border: 1px solid $cl + color: $cl &_md padding: 18px 24px 17px +m @@ -827,6 +834,11 @@ a[name] +t text-align: center +.subtitle2 + margin-bottom: 20px + font-size: 18px + font-weight: bold + .text position: relative max-width: 620px @@ -1630,6 +1642,12 @@ a.grey-link color: $green &_violet color: $viol + &_violet2 + color: $viol2 + &_pink2 + color: $pink2 + &_cyan + color: $cyan &_lg font-size: 20px letter-spacing: 4px @@ -3196,12 +3214,16 @@ a.grey-link background: transparent border: 1px solid $gray color: $gray - &_edit - position: absolute - top: 0 - right: 0 - +m - display: none !important + margin-bottom: 10px + width: 100% + &__btns + position: absolute + top: 0 + right: 0 + display: flex + flex-direction: column + +m + display: none !important &__row display: flex margin-bottom: 25px @@ -4404,3 +4426,64 @@ a &__lil-coin-img margin-bottom: -5px margin-right: 4px + + +.gift-certificates + display: flex + margin: 0 -10px + flex-wrap: wrap + +m + display: block + margin: 0 + &__item + display: block + margin: 0 10px 60px + color: $cl + flex: 0 0 calc(33.33% - 20px) + +t + margin-bottom: 50px !important + +m + margin: 0 0 30px + &__preview + display: block + position: relative + margin-bottom: 15px + border-radius: 2px + color: $cl + overflow: hidden + width: 300px + height: 200px + +t + margin-bottom: 10px + &__cover + object-fit: cover; + width: 100%; + &__details + display: flex + margin-bottom: 10px + &__price + margin-left: auto + +fb + font-size: 12px + letter-spacing: 2px + color: $cl + &__title + text-transform: uppercase + &__status + font-family: 'ProximaNova-Bold', serif + font-size: 12px + letter-spacing: 2px + text-transform: uppercase + & .icon + width: 16px + display: inline-block + height: 16px + margin-bottom: -4px + &__buy-btn + width: 100% + &__preview.theme_pink2 + background: $pink2 + &__preview.theme_cyan + background: $cyan + &__preview.theme_violet2 + background: $viol2 diff --git a/web/webpack.config.js b/web/webpack.config.js index 7455fec9..8156ab03 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -14,6 +14,7 @@ module.exports = { sprite: glob('./src/icons/*.svg'), images: glob('./src/img/*.*'), imagesCertificates: glob('./src/img/user-certificates/*'), + imagesGiftCertificates: glob('./src/img/gift-certificates/*'), fonts: glob('./src/fonts/*') }, output: { @@ -85,7 +86,8 @@ module.exports = { }, { test: /\.(png|gif|jpg|svg)$/, - exclude: [path.resolve(__dirname, 'src/icons'), path.resolve(__dirname, 'src/img/user-certificates')], + exclude: [path.resolve(__dirname, 'src/icons'), path.resolve(__dirname, 'src/img/user-certificates'), + path.resolve(__dirname, 'src/img/gift-certificates')], loader: 'file-loader?name=[name].[ext]&outputPath=./img/' }, { @@ -93,6 +95,11 @@ module.exports = { include: path.resolve(__dirname, 'src/img/user-certificates'), loader: 'file-loader?name=[name].[ext]&outputPath=./img/user-certificates/' }, + { + test: /\.(png|jpg)$/, + include: path.resolve(__dirname, 'src/img/gift-certificates'), + loader: 'file-loader?name=[name].[ext]&outputPath=./img/gift-certificates/' + }, { test: /\.(ttf|otf|eot|woff(2)?)(\?[a-z0-9]+)?$/, loader: 'file-loader?name=[name].[ext]'