Merge branch 'master' of gitlab.com:lilcity/backend into feature/LIL-684

remotes/origin/feature/LIL-711
gzbender 7 years ago
commit abbfc01d8f
  1. 9
      api/v1/serializers/content.py
  2. 19
      api/v1/serializers/course.py
  3. 21
      api/v1/views.py
  4. 1
      apps/course/templates/course/_items.html
  5. 64
      apps/course/templates/course/course.html
  6. 42
      apps/course/templates/course/course_only_lessons.html
  7. 3
      apps/course/templates/course/lesson.html
  8. 15
      apps/notification/tasks.py
  9. 83
      apps/notification/templates/notification/email/certificate.html
  10. 112
      apps/notification/templates/notification/email/gift_certificate.html
  11. 23
      apps/notification/utils.py
  12. 7
      apps/payment/admin.py
  13. 34
      apps/payment/migrations/0025_giftcertificate_usergiftcertificate.py
  14. 39
      apps/payment/migrations/0026_auto_20181101_1546.py
  15. 22
      apps/payment/migrations/0027_auto_20181109_1402.py
  16. 21
      apps/payment/migrations/0028_add_gift_certificates.py
  17. 38
      apps/payment/models.py
  18. 17
      apps/payment/templates/payment/gift_certificate.html
  19. 15
      apps/payment/templates/payment/gift_certificate_get.html
  20. 36
      apps/payment/templates/payment/gift_certificate_item.html
  21. 4
      apps/payment/templates/payment/gift_certificate_items.html
  22. 13
      apps/payment/templates/payment/gift_certificate_payment_success.html
  23. 35
      apps/payment/templates/payment/gift_certificates.html
  24. 11
      apps/payment/templates/payment/payment_success.html
  25. 101
      apps/payment/views.py
  26. 12
      apps/school/templates/school/livelesson_detail.html
  27. 27
      apps/school/templates/school/livelesson_detail_unauth.html
  28. 3
      apps/school/templates/summer/promo.html
  29. 30
      apps/school/views.py
  30. 13
      apps/user/templates/user/profile.html
  31. 3
      apps/user/views.py
  32. 2
      docker/.env.example
  33. 2
      docker/.env.review
  34. 4
      project/settings.py
  35. 6
      project/templates/blocks/baner.html
  36. 4
      project/templates/blocks/header.html
  37. 1
      project/templates/blocks/lil_store_js.html
  38. 24
      project/templates/blocks/popup_enter_gift_code.html
  39. 31
      project/templates/blocks/popup_gift_certificate.html
  40. 9
      project/templates/blocks/promo.html
  41. 10
      project/templates/lilcity/index.html
  42. 9
      project/urls.py
  43. 13
      project/views.py
  44. 1
      requirements.txt
  45. BIN
      web/src/img/clock.png
  46. BIN
      web/src/img/done.png
  47. BIN
      web/src/img/fb.png
  48. BIN
      web/src/img/gift-certificates/1000.jpg
  49. BIN
      web/src/img/gift-certificates/10000.jpg
  50. BIN
      web/src/img/gift-certificates/2000.jpg
  51. BIN
      web/src/img/gift-certificates/3000.jpg
  52. BIN
      web/src/img/gift-certificates/5000.jpg
  53. BIN
      web/src/img/instagram.png
  54. BIN
      web/src/img/twitter.png
  55. 23
      web/src/js/modules/popup.js
  56. 104
      web/src/sass/_common.sass
  57. 9
      web/webpack.config.js

@ -1,5 +1,6 @@
from urllib.parse import urlparse, urlunparse from urllib.parse import urlparse, urlunparse
from rest_framework import serializers from rest_framework import serializers
from django.conf import settings
from apps.content.models import ( from apps.content.models import (
Baner, Content, Image, Text, ImageText, Video, Baner, Content, Image, Text, ImageText, Video,
@ -46,12 +47,8 @@ class BanerSerializer(serializers.ModelSerializer):
) )
def get_image(self, baner): def get_image(self, baner):
request = self.context.get('request') if baner.image:
http_host = request.META.get('HTTP_ORIGIN') return 'http://' + settings.MAIN_HOST + '/' + baner.image.url
if http_host and baner.image:
domain = urlparse(http_host).netloc.split(':')[0]
baner_url = 'http://' + domain + baner.image.url
return baner_url
else: else:
return None return None

@ -1,9 +1,8 @@
from django.contrib.auth import get_user_model
from ipware import get_client_ip from ipware import get_client_ip
from rest_framework import serializers from rest_framework import serializers
from rest_framework.validators import UniqueValidator from rest_framework.validators import UniqueValidator
from drf_dynamic_fields import DynamicFieldsMixin
from django.contrib.auth import get_user_model
from apps.course.models import ( from apps.course.models import (
Category, Course, Category, Course,
@ -13,17 +12,9 @@ from apps.course.models import (
LiveLessonComment) LiveLessonComment)
from .content import ( from .content import (
ImageObjectSerializer, ContentSerializer, ContentCreateSerializer, ImageObjectSerializer, ContentSerializer, ContentCreateSerializer,
GallerySerializer, GalleryImageSerializer, GallerySerializer, )
)
from apps.content.models import (
Content, Image, Text, ImageText, Video,
Gallery, GalleryImage, ImageObject,
ContestWork)
from .user import UserSerializer
from .mixins import DispatchContentMixin, DispatchGalleryMixin, DispatchMaterialMixin from .mixins import DispatchContentMixin, DispatchGalleryMixin, DispatchMaterialMixin
from .user import UserSerializer
User = get_user_model() User = get_user_model()
@ -269,7 +260,7 @@ class LessonSerializer(serializers.ModelSerializer):
) )
class CourseSerializer(serializers.ModelSerializer): class CourseSerializer(DynamicFieldsMixin, serializers.ModelSerializer):
author = UserSerializer() author = UserSerializer()
category = CategorySerializer() category = CategorySerializer()
materials = MaterialSerializer(many=True) materials = MaterialSerializer(many=True)

@ -19,7 +19,7 @@ from .serializers.course import (
MaterialSerializer, MaterialCreateSerializer, MaterialSerializer, MaterialCreateSerializer,
LessonSerializer, LessonCreateSerializer, LessonSerializer, LessonCreateSerializer,
LikeCreateSerializer, CourseCommentSerializer, LessonCommentSerializer, LikeCreateSerializer, CourseCommentSerializer, LessonCommentSerializer,
LiveLessonCommentSerializer) LiveLessonCommentSerializer,)
from .serializers.content import ( from .serializers.content import (
BanerSerializer, BanerSerializer,
ImageSerializer, ImageCreateSerializer, ImageSerializer, ImageCreateSerializer,
@ -37,8 +37,7 @@ from .serializers.school import (
) )
from .serializers.payment import ( from .serializers.payment import (
AuthorBalanceSerializer, AuthorBalanceCreateSerializer, AuthorBalanceSerializer, AuthorBalanceCreateSerializer,
PaymentSerializer, CoursePaymentSerializer, PaymentSerializer,
SchoolPaymentSerializer,
CoursePaymentCreateSerializer, SchoolPaymentCreateSerializer) CoursePaymentCreateSerializer, SchoolPaymentCreateSerializer)
from .serializers.user import ( from .serializers.user import (
AuthorRequestSerializer, AuthorRequestSerializer,
@ -49,7 +48,7 @@ from .serializers.contest import (
) )
from .permissions import ( from .permissions import (
IsAdmin, IsAdminOrIsSelf, IsAdmin,
IsAuthorOrAdmin, IsAuthorObjectOrAdmin, IsAuthorOrAdmin, IsAuthorObjectOrAdmin,
IsTeacherOrAdmin, IsTeacherOrAdmin,
) )
@ -242,6 +241,20 @@ class CourseViewSet(ExtendedModelViewSet):
# 'delete': IsAdmin, # 'delete': IsAdmin,
# } # }
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
if request.query_params.get('page'):
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
else:
return Response({'results': []})
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
@list_route(methods=['get']) @list_route(methods=['get'])
def draft(self, request): def draft(self, request):
drafts = Course.objects.filter(author=request.user, status=Course.DRAFT) drafts = Course.objects.filter(author=request.user, status=Course.DRAFT)

@ -73,7 +73,6 @@
<div class="user__name">{{ course.author.get_full_name }}</div> <div class="user__name">{{ course.author.get_full_name }}</div>
</a> </a>
<div class="user__meta"> <div class="user__meta">
<div class="user__date">{{ course.created_at_humanize }}</div>
{% data_liked user course as liked %} {% data_liked user course as liked %}
<a class="user__likes likes{% if liked %} active{% endif %}" <a class="user__likes likes{% if liked %} active{% endif %}"
{% if not user.is_authenticated %}data-popup=".js-popup-auth"{% endif %} {% if not user.is_authenticated %}data-popup=".js-popup-auth"{% endif %}

@ -8,9 +8,18 @@
{% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %} {% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %}
{% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %} {% block ogtitle %}{{ course.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %} {% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% if course.cover and course.cover.image %}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{% if course.cover %}{{ course.cover.image.url }}{% else %}{% static 'img/og_courses.jpg' %}{% endif %}{% endblock ogimage %} {% block ogimage %}
{% endif %} http://{{request.META.HTTP_HOST}}{% if course.cover and course.cover.image %}{{ course.cover.image.url }}{% else %}{% static 'img/og_courses.jpg' %}{% endif %}
{% endblock ogimage %}
{% block ogimage-width %}
{% if course.cover and course.cover.image %}{{ course.cover.image.width }}{% else %}1024{% endif %}
{% endblock ogimage-width %}
{% block ogimage-height %}
{% if course.cover and course.cover.image %}{{ course.cover.image.height }}{% else %}512{% endif %}
{% endblock ogimage-height %}
{% block ogdescription %}{{ course.short_description | striptags }}{% endblock ogdescription %} {% block ogdescription %}{{ course.short_description | striptags }}{% endblock ogdescription %}
{% block content %} {% block content %}
@ -29,17 +38,22 @@
<a class="go__btn btn btn_light-gray" href="{% url 'course_edit' course.id %}">Редактировать</a> <a class="go__btn btn btn_light-gray" href="{% url 'course_edit' course.id %}">Редактировать</a>
{% endif %} {% endif %}
{% if course.author != request.user and not paid and course.price %} {% if course.author != request.user and not paid and course.price %}
<a href="#" <div>
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md" <a href="#"
{% if user.is_authenticated %} class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"
{% if not pending %} {% if user.is_authenticated %}
data-course-buy {% if not pending %}
data-popup=".js-popup-course-buy" data-course-buy
{% endif %} data-popup=".js-popup-course-buy"
{% else %} {% endif %}
data-popup=".js-popup-auth" {% else %}
data-popup=".js-popup-auth"
{% endif %}
>{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}</a>
{% if not paid %}
<a class="main__btn btn btn_stroke-black" href="{% url 'gift-certificates' %}">Подарить другу</a>
{% endif %} {% endif %}
>{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}</a> </div>
{% endif %} {% endif %}
</div> </div>
<div <div
@ -86,22 +100,19 @@
{% endif %} {% endif %}
<div class="user__info"> <div class="user__info">
<div class="user__name">{{ course.author.get_full_name }}</div> <div class="user__name">{{ course.author.get_full_name }}</div>
<div class="user__meta">
<div class="user__date">{{ course.created_at_humanize }}</div>
</div>
</div> </div>
</div> </div>
</a> </a>
<div class="course__metas"> <div class="course__metas">
<div class="course__meta meta"> <div class="course__meta meta">
<div class="meta__item"> <a class="meta__item" title="Продолжительность курса">
<div class="meta__icon"> <div class="meta__icon">
<svg class="icon icon-time"> <svg class="icon icon-time">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-time"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-time"></use>
</svg> </svg>
</div> </div>
<div class="meta__title">{{ course.duration | rupluralize:"день,дня,дней" }}</div> <div class="meta__title">{{ course.duration | rupluralize:"день,дня,дней" }}</div>
</div> </a>
{% if course.price %} {% if course.price %}
<div class="meta__item"> <div class="meta__item">
<div class="meta__icon"> <div class="meta__icon">
@ -112,8 +123,6 @@
<div class="meta__title">{{ course.price|floatformat:"-2" }}₽</div> <div class="meta__title">{{ course.price|floatformat:"-2" }}₽</div>
</div> </div>
{% endif %} {% endif %}
</div>
<div class="course__meta meta">
<div class="meta__item"> <div class="meta__item">
<div class="meta__icon"> <div class="meta__icon">
<svg class="icon icon-showcase"> <svg class="icon icon-showcase">
@ -281,30 +290,19 @@
{% endif %} {% endif %}
<div class="user__info"> <div class="user__info">
<div class="user__name">{{ course.author.get_full_name }}</div> <div class="user__name">{{ course.author.get_full_name }}</div>
<div class="user__meta">
<div class="user__date">{{ course.created_at_humanize }}</div>
</div>
</div> </div>
</div> </div>
</a> </a>
<div class="course__info"> <div class="course__info">
<div class="course__meta meta meta_white"> <div class="course__meta meta meta_white">
<div class="meta__item"> <a class="meta__item" title="Продолжительность курса">
<div class="meta__icon"> <div class="meta__icon">
<svg class="icon icon-time"> <svg class="icon icon-time">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-time"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-time"></use>
</svg> </svg>
</div> </div>
<div class="meta__title">{{ course.duration | rupluralize:"день,дня,дней" }}</div> <div class="meta__title">{{ course.duration | rupluralize:"день,дня,дней" }}</div>
</div> </a>
<div class="meta__item">
<div class="meta__icon">
<svg class="icon icon-date">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-date"></use>
</svg>
</div>
<div class="meta__title">{{ course.created_at | date:"d F Yг." }}</div>
</div>
{% if course.price %} {% if course.price %}
<div class="meta__item"> <div class="meta__item">
<div class="meta__icon"> <div class="meta__icon">

@ -29,17 +29,22 @@
<a class="go__btn btn" href="{% url 'course_edit' course.id %}">Редактировать</a> <a class="go__btn btn" href="{% url 'course_edit' course.id %}">Редактировать</a>
{% endif %} {% endif %}
{% if not paid and course.price and not has_full_access %} {% if not paid and course.price and not has_full_access %}
<a <div>
class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md" <a
{% if user.is_authenticated %} class="go__btn btn{% if pending %} btn_gray{% endif %} btn_md"
{% if not pending %} {% if user.is_authenticated %}
href="{% url 'course-checkout' course.id %}" {% if not pending %}
{% endif %} href="{% url 'course-checkout' course.id %}"
{% else %} {% endif %}
data-popup=".js-popup-auth" {% else %}
href="#" data-popup=".js-popup-auth"
href="#"
{% endif %}
>{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}</a>
{% if not paid %}
<a class="main__btn btn btn_stroke-black" href="{% url 'gift-certificates' %}">Подарить другу</a>
{% endif %} {% endif %}
>{% if pending %}ОЖИДАЕТСЯ ПОДТВЕРЖДЕНИЕ ОПЛАТЫ{% else %}КУПИТЬ КУРС{% endif %}</a> </div>
{% endif %} {% endif %}
</div> </div>
<div <div
@ -85,30 +90,19 @@
{% endif %} {% endif %}
<div class="user__info"> <div class="user__info">
<div class="user__name">{{ course.author.get_full_name }}</div> <div class="user__name">{{ course.author.get_full_name }}</div>
<div class="user__meta">
<div class="user__date">{{ course.created_at_humanize }}</div>
</div>
</div> </div>
</div> </div>
</a> </a>
<div class="course__metas"> <div class="course__metas">
<div class="course__meta meta"> <div class="course__meta meta">
<div class="meta__item"> <a class="meta__item" title="Продолжительность курса">
<div class="meta__icon"> <div class="meta__icon">
<svg class="icon icon-time"> <svg class="icon icon-time">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-time"></use> <use xlink:href="{% static 'img/sprite.svg' %}#icon-time"></use>
</svg> </svg>
</div> </div>
<div class="meta__title">{{ course.duration | rupluralize:"день,дня,дней" }}</div> <div class="meta__title">{{ course.duration | rupluralize:"день,дня,дней" }}</div>
</div> </a>
<div class="meta__item">
<div class="meta__icon">
<svg class="icon icon-date">
<use xlink:href="{% static 'img/sprite.svg' %}#icon-date"></use>
</svg>
</div>
<div class="meta__title">{{ course.created_at | date:"d F Yг." }}</div>
</div>
{% if course.price %} {% if course.price %}
<div class="meta__item"> <div class="meta__item">
<div class="meta__icon"> <div class="meta__icon">
@ -119,8 +113,6 @@
<div class="meta__title">{{ course.price|floatformat:"-2" }}₽</div> <div class="meta__title">{{ course.price|floatformat:"-2" }}₽</div>
</div> </div>
{% endif %} {% endif %}
</div>
<div class="course__meta meta">
<div class="meta__item"> <div class="meta__item">
<div class="meta__icon"> <div class="meta__icon">
<svg class="icon icon-showcase"> <svg class="icon icon-showcase">

@ -47,9 +47,6 @@
{% endif %} {% endif %}
<div class="user__info"> <div class="user__info">
<div class="user__name">{{ lesson.author.get_full_name }}</div> <div class="user__name">{{ lesson.author.get_full_name }}</div>
<div class="user__meta">
<div class="user__date">{{ lesson.created_at_humanize }}</div>
</div>
</div> </div>
</div> </div>
</a> </a>

@ -8,7 +8,7 @@ from django.db.models import Max
from apps.notification.models import UserNotification from apps.notification.models import UserNotification
from apps.notification.utils import send_email from apps.notification.utils import send_email
from apps.payment.models import SchoolPayment, CoursePayment, Payment from apps.payment.models import SchoolPayment, CoursePayment, Payment, UserGiftCertificate
from project.celery import app from project.celery import app
from project.utils.db import format_sql, execute_sql from project.utils.db import format_sql, execute_sql
from project.sengrid import get_sendgrid_client from project.sengrid import get_sendgrid_client
@ -33,7 +33,7 @@ def send_certificates(email=None, date=None, dry_run=False):
file.close() file.close()
return return
date = datetime.strptime(date, '%d-%m-%Y') if date else now().date() date = datetime.strptime(date, '%d-%m-%Y').date() if date else now().date()
today = now().date() today = now().date()
users = set(list(SchoolPayment.objects.filter(date_end=date, add_days=False).values_list('user_id', flat=True))) users = set(list(SchoolPayment.objects.filter(date_end=date, add_days=False).values_list('user_id', flat=True)))
user_notifications_qs = UserNotification.objects.filter(user_id__in=users) user_notifications_qs = UserNotification.objects.filter(user_id__in=users)
@ -42,6 +42,7 @@ def send_certificates(email=None, date=None, dry_run=False):
'user_id', flat=True).distinct() 'user_id', flat=True).distinct()
for user_id in users: for user_id in users:
if user_id in notified_users: if user_id in notified_users:
print('skip', user_id)
continue continue
un = user_notifications.get(user_id, UserNotification(user_id=user_id)) un = user_notifications.get(user_id, UserNotification(user_id=user_id))
print(un.user.email) print(un.user.email)
@ -54,7 +55,7 @@ def send_certificates(email=None, date=None, dry_run=False):
file = open(staticfiles_storage.path(path_pattern % un.certificate_number), 'rb') file = open(staticfiles_storage.path(path_pattern % un.certificate_number), 'rb')
try: try:
send_email('Грамота от Lil School', un.user.email, 'notification/email/certificate.html', send_email('Грамота от Lil School', un.user.email, 'notification/email/certificate.html',
attachments=[(file.name, file.read(), 'image/jpeg')]) attachments=[(file.name, file.read(), 'image/jpeg')], user_notification=un)
except: except:
print('Not OK') print('Not OK')
continue continue
@ -98,3 +99,11 @@ def sendgrid_update_recipients():
sg = get_sendgrid_client() sg = get_sendgrid_client()
response = sg.client.contactdb.recipients.patch(request_body=data) response = sg.client.contactdb.recipients.patch(request_body=data)
print(response.body) print(response.body)
@app.task
def send_gift_certificate(user_gift_certificate):
user_gift_certificate = UserGiftCertificate.objects.get(id=user_gift_certificate)
send_email('Подарочный сертификат от Lil School', user_gift_certificate.user.email, 'notification/email/gift_certificate.html',
inline_images=[('twitter_icon', 'img/twitter.png'), ('fb_icon', 'img/fb.png'), ('instagram_icon', 'img/instagram.png'),],
user_gift_certificate=user_gift_certificate, gift_certificate=user_gift_certificate.gift_certificate)

@ -1,28 +1,71 @@
{% extends "notification/email/_base.html" %} {% extends "notification/email/_base.html" %}
{% block content %} {% block content %}
<p style="margin: 0 0 20px">Дорогие родители!</p> <p style="margin: 0 0 20px">Привет!</p>
<div style="margin-bottom: 10px;"> <div style="margin-bottom: 10px;">
<p>Это письмо адресовано вашим детям - ученикам Lil School. {% if not user_notification or user_notification.certificate_number == 1 %}
Если они еще не умеют читать, прочитайте им наше послание, громко и с выражением.<br> <p>
Спасибо! Поздравляем! Вы прошли месяц обучения в Lil School.<br>
К письму прикреплена грамота. Распечатайте её и вручите вашим детям.<br>
Ждём вас в следующем месяце на наших творческих занятиях!
</p> </p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 2 %}
<p>
Вы помните, что каждый месяц вам приходит грамота за прекрасную учебу в нашей творческой школе?<br>
Скачивайте. Распечатывайте. И соберите свою коллекцию!
</p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 3 %}
<p>
Вам понравился наш творческий месяц?<br>
В письме вы найдёте грамоту, она для вашей семьи.<br>
Как здорово, что у нас есть такие ученики!<br>
Ждём вас в следующем месяце.
</p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 4 %}
<p>
Прошёл целый месяц обучения на платформе Lil School - месяц творчества, креатива и невероятных идей.<br>
Во вложении вас ждёт грамота.<br>
До встречи на занятиях!
</p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 5 %}
<p>
Ваша грамота за успехи в учебе в Lil School ждёт вас во вложении.<br>
Скорее распечатайте её!<br>
Вам есть чем гордится!<br>
До встречи в следующем месяце!
</p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 6 %}
<p>
Как здорово вы потрудились на занятиях в этом месяце!<br>
И наша грамота уже ждёт вас!<br>
Спасибо за творчество и креатив.<br>
Ждём вас в следующем месяце!
</p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 7 %}
<p>
Какой классный месяц у нас был! Вместе мы очень здорово и креативно потрудились.<br>
Во вложении вас ждёт заслуженная грамота!<br>
До встречи на уроках!
</p>
{% endif %}
{% if user_notification and user_notification.certificate_number == 8 %}
<p>
Месяц творчества и креатива пролетел как один миг! А как много работ мы вместе сделали!<br>
Вы - большие молодцы.<br>
Во вложении ваш ждёт грамота!<br>
До встречи на занятиях.
</p>
{% endif %}
</div> </div>
<div style="margin-bottom: 10px; font-style: italic;"> <div style="margin-bottom: 10px;">
Привет, друг!<br> <p>
Вот и прошёл месяц обучения на платформе «Lil School» - месяц удивительных, творческих приключений и открытий. Команда «Lil School».
Ты так много узнал и столько всего нарисовал! </p>
Как же здорово у тебя все получается!
</div>
<div style="margin-bottom: 10px; font-style: italic;">
Скорее смотри что прикреплено к письму. Да это же ГРАМОТА! Ее можно распечатать и повесить
на видное место в твоей комнате.
Можно показать ее друзьям вместе со всеми работами, над которыми ты так трудился.
</div>
<div style="margin-bottom: 10px; font-style: italic;">
Поздравляем! Мы ждём тебя в новом месяце в рядах наших учеников.
</div>
<div style="margin-bottom: 10px; font-style: italic;">
Команда «Lil School».
</div> </div>
{% endblock content %} {% endblock content %}

@ -0,0 +1,112 @@
{% extends "notification/email/_base.html" %}
{% load settings %}
{% block content %}
<p style="text-align: center;font-size: 2em;font-weight: bold;line-height: 1.09375;">Поздравляем с успешной
покупкой!</p>
<div style="margin-bottom:30px;text-align: center;">
<p>Вы получаете <strong>{{ gift_certificate.price }}</strong> лиликов на счет! <strong>1 LIL = 1 руб.</strong><br>
Накапливайте монеты и тратьте их на оплату школы и курсов.
</p>
</div>
<div style="margin-bottom: 30px;width: 100%;">
<div style="
margin: 0 auto;
width: 300px;
">
<div style="
width: 300px;
height: 300px;
background-color: #FEB9B9;
"></div>
<table style="
width: 100%;
font-size: 12px;
font-weight: bold;
">
<tbody>
<tr>
<td style="
letter-spacing: 2px;
text-transform: uppercase;
">Подарочный сертификат
</td>
<td style="
text-align: right;
">{{ gift_certificate.price }} р.
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div style="margin-bottom:10px;text-align: center;">Чтобы воспользоваться сертификатом, перейдите по ссылке</div>
<div style="margin-bottom: 30px;text-align: center;">
<a href="https://{% setting 'MAIN_HOST' %}{% url 'index' %}?gift-certificate={{ user_gift_certificate.code }}" style="
color: #FF9393;
font-weight: bold;
">
{% setting 'MAIN_HOST' %}{% url 'index' %}?gift-certificate={{ user_gift_certificate.code }}</a>
</div>
<div style="margin-bottom:10px;text-align: center;">
Или воспользуйтесь сертификатом, введя уникальный код на в разделе
вашего профиля на сайте <a href="https://lil.school" style="
color: #FF9393;
">lil.school</a>
</div>
<div style="
text-align: center;
font-size: 1.4em;
font-weight: bold;
margin-bottom: 10px;
">Ваш код
</div>
<div style="
text-align: center;
font-size: 1.4em;
font-weight: bold;
margin-bottom: 30px;
">
<div style="
display: inline-block;
border: 1px solid black;
padding: 5px 40px;
border-radius: 3px;
">{{ user_gift_certificate.code }}
</div>
</div>
<div style="
margin-bottom: 10px;
text-align: center;
">
Вы так же можете отправить это письмо, ссылку или код вашему другу, чтобы подарить ему этот сертификат.
</div>
<div style="
margin-bottom: 10px;
text-align: center;
font-size: 1.4em;
font-weight: bold;
">
Присоединяйтесь к нам!
</div>
<div style="
margin-bottom: 10px;
text-align: center;">
{% if config.SERVICE_TWITTER_URL %}
<a href="{{ config.SERVICE_TWITTER_URL }}">
<img src="cid:{{ twitter_icon }}" />
</a>
{% endif %}
{% if config.SERVICE_FB_URL %}
<a href="{{ config.SERVICE_FB_URL }}">
<img src="cid:{{ fb_icon }}" />
</a>
{% endif %}
{% if config.SERVICE_INSTAGRAM_URL %}
<a href="{{ config.SERVICE_INSTAGRAM_URL }}">
<img src="cid:{{ instagram_icon }}" />
</a>
{% endif %}
</div>
{% endblock content %}

@ -1,16 +1,27 @@
from django.contrib.staticfiles.storage import staticfiles_storage
from twilio.rest import Client from twilio.rest import Client
from django.core.mail import EmailMessage, EmailMultiAlternatives
from django.core.mail import EmailMessage from anymail.message import attach_inline_image_file
from django.conf import settings from django.conf import settings
from django.template.loader import get_template from django.template.loader import get_template
from project.celery import app from project.celery import app
@app.task @app.task
def send_email(subject, to_email, template_name, attachments=[], **kwargs): def send_email(subject, to_email, template_name, attachments=[], inline_images=[], **kwargs):
html = get_template(template_name).render(kwargs) if inline_images:
email = EmailMessage(subject, html, to=[to_email], attachments=attachments) email = EmailMultiAlternatives(subject, '', to=[to_email], attachments=attachments)
email.content_subtype = 'html' 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() email.send()

@ -5,7 +5,7 @@ from polymorphic.admin import (
PolymorphicChildModelFilter, PolymorphicChildModelFilter,
) )
from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment from .models import AuthorBalance, CoursePayment, SchoolPayment, Payment, GiftCertificate
@admin.register(AuthorBalance) @admin.register(AuthorBalance)
@ -61,3 +61,8 @@ class PaymentAdmin(PolymorphicParentModelAdmin):
CoursePayment, CoursePayment,
SchoolPayment, SchoolPayment,
) )
@admin.register(GiftCertificate)
class GiftCertificateAdmin(admin.ModelAdmin):
pass

@ -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)),
],
),
]

@ -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='Подарочный сертификат'),
),
]

@ -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),
),
]

@ -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),
]

@ -1,5 +1,6 @@
from decimal import Decimal from decimal import Decimal
import arrow import arrow
import short_url
from django.db.models import Func, F from django.db.models import Func, F
from paymentwall import Pingback from paymentwall import Pingback
@ -13,6 +14,7 @@ from django.core.validators import RegexValidator
from django.utils.timezone import now from django.utils.timezone import now
from django.conf import settings from django.conf import settings
from apps.content.models import ImageObject
from project.utils import weekdays_in_date_range from project.utils import weekdays_in_date_range
from apps.course.models import Course from apps.course.models import Course
@ -147,6 +149,8 @@ class Payment(PolymorphicModel):
referrer_bonus = user.referral.referrer_bonus referrer_bonus = user.referral.referrer_bonus
if payment and payment.is_paid(): if payment and payment.is_paid():
price = payment.amount price = payment.amount
elif isinstance(payment, GiftCertificatePayment):
price = payment.gift_certificate.price
elif course: elif course:
price = course.price price = course.price
else: else:
@ -244,6 +248,12 @@ class Payment(PolymorphicModel):
else: else:
author_balance.amount = self.amount author_balance.amount = self.amount
author_balance.save() 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(): 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') 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): class UserBonus(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='bonuses') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='bonuses')
amount = models.DecimalField(max_digits=8, decimal_places=2, default=0, editable=False) amount = models.DecimalField(max_digits=8, decimal_places=2, default=0, editable=False)
@ -292,3 +311,22 @@ class UserBonus(models.Model):
class Meta: class Meta:
ordering = ('created_at',) 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

@ -0,0 +1,17 @@
{% extends "templates/lilcity/index.html" %} {% load static %}
{% block content %}
<div class="section">
<div class="section__center center">
<div class="title title_center">Вам подарок!</div>
<div class="text">
Пользователь ХХХ дарит вам сертификат на сумму ХХХ руб
</div>
<div class="center">
<a href="{% url '' %}" class="btn">
получить подарок
</a>
</div>
</div>
</div>
{% endblock content %}

@ -0,0 +1,15 @@
{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %}
<div class="section">
<div class="section__center center center_xs">
<div class="done">
<div class="done__title title">Бонусы зачислены на ваш счет!</div>
<div>Вы можете оплатить с их помощью курс или онлайн-школу</div>
<div class="done__foot">
<a class="done__btn btn btn_md btn_stroke" href="{% url 'school:school' %}"
style="margin-bottom: 10px;">Записаться в школу</a>
<a class="done__btn btn btn_md btn_stroke" href="{% url 'courses' %}">Купить курсы</a>
</div>
</div>
</div>
</div>
{% endblock content %}

@ -0,0 +1,36 @@
{% load thumbnail %}
{% load static %}
{% load data_liked from data_liked %}
<div class="gift-certificates__item">
<div class="gift-certificates__preview {{ theme_color }}">
<img class="gift-certificates__cover" src="{{ gift_certificate.cover }}" />
</div>
<div class="gift-certificates__details">
<span class="gift-certificates__title theme {{ theme_color }}">
{% if user_gift_certificate and not user_gift_certificate.bonuses_sent %}
<a href="{% url 'gift-certificate-get' user_gift_certificate.code %}">подарочный сертификат</a>
{% else %}подарочный сертификат{% endif %}</span>
<div class="gift-certificates__price">{{ gift_certificate.price|floatformat:"-2" }}₽</div>
</div>
{% if user_gift_certificate %}
{% if user_gift_certificate.bonuses_sent %}
<div class="gift-certificates__status">
<img class="icon" src="{% static 'img/done.png' %}" />
Получено
</div>
{% else %}
<div class="gift-certificates__status">
<img class="icon" style="margin-top: -1px; height: auto;" src="{% static 'img/clock.png' %}" />
Ожидает получения
</div>
{% endif %}
{% else %}
<a class="gift-certificates__buy-btn btn"
{% if request.user.is_authenticated %}
href="{% url 'gift-certificate-checkout' gift_certificate.id %}"
{% else %}
href="#" data-popup=".js-popup-auth"
{% endif %}>Купить сертификат</a>
{% endif %}
</div>

@ -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 %}

@ -0,0 +1,13 @@
{% extends "templates/lilcity/index.html" %} {% load static %} {% block content %}
<div class="section">
<div class="section__center center center_xs">
<div class="done">
<div class="done__title title">Вы успешно приобрели подарочный сертификат!</div>
<div>Мы отправили письмо с сертификатом на вашу почту.</div>
<div class="done__foot">
<a class="done__btn btn btn_md btn_stroke" href="{% url 'index' %}">СПАСИБО</a>
</div>
</div>
</div>
</div>
{% endblock content %}

@ -0,0 +1,35 @@
{% extends "templates/lilcity/index.html" %} {% load static %}
{% block content %}
<div
class="main main_default"
{% if config.MAIN_PAGE_TOP_IMAGE %}
style="background-image: url({{ config.MAIN_PAGE_TOP_IMAGE.url }});"
{% else %}
style="background-image: url({% static 'img/bg-1.jpg' %});"
{% endif %}
>
<div class="main__center center" style="margin-top: 110px;">
<div class="main__title">
<span class="main__bold">Подарочные сертификаты</span>
</div>
</div>
</div>
<div class="section section_courses">
<div class="section__center center">
<div class="text text_courses">Подарочный сертификат Lil City - отличный презент на любой праздник.<br>
А также сертификат можно подарить без повода - развитие <br>творческого потенциала всегда уместно и актуально.
При покупке подарочного сертификата мы высылаем письмо,<br>
где будет уникальный код и уникальная ссылка. Письмо или ссылку<br>
на него вы можете переслать другому. Сертификатом можно<br>
воспользоваться, перейдя по ссылке.
<img src="/static/img/curve-1.svg" class="text__curve text__curve_one">
</div>
<div class="title title_center">Сертификаты</div>
<div class="gift-certificates">
{% include "payment/gift_certificate_items.html" %}
</div>
</div>
</div>
{% endblock content %}

@ -7,10 +7,17 @@
<div class="done__foot"> <div class="done__foot">
<a class="done__btn btn btn_md btn_stroke" href="{% url 'school:school' %}">ПЕРЕЙТИ К ШКОЛЕ</a> <a class="done__btn btn btn_md btn_stroke" href="{% url 'school:school' %}">ПЕРЕЙТИ К ШКОЛЕ</a>
</div> </div>
{% else %} {% endif %}
{% if course %}
<div class="done__title title">Вы успешно приобрели курс!</div> <div class="done__title title">Вы успешно приобрели курс!</div>
<div class="done__foot"> <div class="done__foot">
<a class="done__btn btn btn_md btn_stroke" href="/">ПЕРЕЙТИ К ГЛАВНОЙ</a> <a class="done__btn btn btn_md btn_stroke" href="{% url 'course' course.id %}">ПЕРЕЙТИ К КУРСУ</a>
</div>
{% endif %}
{% if gift_certificate %}
<div class="done__title title">Вы успешно приобрели подарочный сертификат!</div>
<div class="done__foot">
<a class="done__btn btn btn_md btn_stroke" href="{% url 'index' %}">ПЕРЕЙТИ НА ГЛАВНУЮ</a>
</div> </div>
{% endif %} {% endif %}
</div> </div>

@ -1,5 +1,5 @@
from decimal import Decimal from decimal import Decimal
import short_url
import arrow import arrow
import json import json
import logging import logging
@ -12,9 +12,9 @@ import calendar
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import login_required 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.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.views.decorators.csrf import csrf_exempt
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.decorators import method_decorator 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.school.models import SchoolSchedule
from apps.payment.tasks import transaction_to_mixpanel, product_payment_to_mixpanel, transaction_to_roistat 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') logger = logging.getLogger('django')
@ -216,6 +217,8 @@ class PaymentwallCallbackView(View):
product_payment_class = CoursePayment product_payment_class = CoursePayment
elif product_type_name == 'school': elif product_type_name == 'school':
product_payment_class = SchoolPayment product_payment_class = SchoolPayment
elif product_type_name == 'gift_certificate':
product_payment_class = GiftCertificatePayment
else: else:
return HttpResponse(status=403) return HttpResponse(status=403)
@ -264,6 +267,15 @@ class PaymentwallCallbackView(View):
'created_at': payment.created_at, 'created_at': payment.created_at,
'update_at': payment.update_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() payment.save()
product_payment_to_mixpanel.delay( product_payment_to_mixpanel.delay(
@ -297,3 +309,84 @@ class PaymentwallCallbackView(View):
else: else:
raise DisallowedPingbackHost raise DisallowedPingbackHost
return HttpResponse(status=403) 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)

@ -5,9 +5,15 @@
{% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %} {% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %}
{% block ogtitle %}{{ livelesson.title }} - {{ block.super }}{% endblock ogtitle %} {% block ogtitle %}{{ livelesson.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %} {% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% if livelesson.cover and livelesson.cover.image %} {% block ogimage %}
{% block ogimage %}http://{{request.META.HTTP_HOST}}{{ livelesson.cover.image.url }}{% endblock ogimage %} {% if livelesson.cover and livelesson.cover.image %}http://{{request.META.HTTP_HOST}}{{ livelesson.cover.image.url }}{% else %}{{ block.super }}{% endif %}
{% endif %} {% endblock ogimage %}
{% block ogimage-width %}
{% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.width }}{% else %}{{ block.super }}{% endif %}
{% endblock ogimage-width %}
{% block ogimage-height %}
{% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.height }}{% else %}{{ block.super }}{% endif %}
{% endblock ogimage-height %}
{% block content %} {% block content %}
<div class="section" style="margin-bottom:0;padding-bottom:0"> <div class="section" style="margin-bottom:0;padding-bottom:0">

@ -0,0 +1,27 @@
{% extends "templates/lilcity/index.html" %}
{% load static %}
{% block title %}{{ livelesson.title }} - {{ block.super }}{% endblock title %}
{% block twurl %}{{ request.build_absolute_uri }}{% endblock twurl %}
{% block ogtitle %}{{ livelesson.title }} - {{ block.super }}{% endblock ogtitle %}
{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}
{% block ogimage %}
{% if livelesson.cover and livelesson.cover.image %}http://{{request.META.HTTP_HOST}}{{ livelesson.cover.image.url }}{% else %}{{ block.super }}{% endif %}
{% endblock ogimage %}
{% block ogimage-width %}
{% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.width }}{% else %}{{ block.super }}{% endif %}
{% endblock ogimage-width %}
{% block ogimage-height %}
{% if livelesson.cover and livelesson.cover.image %}{{ livelesson.cover.image.height }}{% else %}{{ block.super }}{% endif %}
{% endblock ogimage-height %}
{% block content %}
<div class="section">
<div class="section__center center center_sm">
<div class="title">
Чтобы посмотреть урок необходимо авторизоваться
</div>
<button class="btn" data-popup=".js-popup-auth">Войти</button>
</div>
</div>
{% endblock content %}

@ -20,6 +20,9 @@
{% if is_purchased %}ваша подписка истекает {{ subscription_ends_humanize }}<br/>перейти к оплате{% endif %} {% if is_purchased %}ваша подписка истекает {{ subscription_ends_humanize }}<br/>перейти к оплате{% endif %}
</a> </a>
{% if not is_purchased and not is_purchased_future %}
<a class="main__btn btn btn_stroke-black" href="{% url 'gift-certificates' %}">Подарить другу</a>
{% endif %}
</div> </div>
<div class="main__school school school_main"> <div class="main__school school school_main">
<div class="school__col"> <div class="school__col">

@ -57,26 +57,28 @@ class LiveLessonsView(ListView):
return queryset return queryset
@method_decorator(login_required, name='dispatch')
class LiveLessonsDetailView(DetailView): class LiveLessonsDetailView(DetailView):
model = LiveLesson model = LiveLesson
template_name = 'school/livelesson_detail.html' template_name = 'school/livelesson_detail.html'
def get(self, request, pk=None): def get(self, request, pk=None):
self.object = self.get_object() self.object = self.get_object()
is_purchased = SchoolPayment.objects.filter( if request.user.is_authenticated:
user=request.user, is_purchased = SchoolPayment.objects.filter(
date_start__lte=now(), user=request.user,
date_end__gte=now() - timedelta(days=7), date_start__lte=now(),
status__in=[ date_end__gte=now() - timedelta(days=7),
Pingback.PINGBACK_TYPE_REGULAR, status__in=[
Pingback.PINGBACK_TYPE_GOODWILL, Pingback.PINGBACK_TYPE_REGULAR,
Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED, Pingback.PINGBACK_TYPE_GOODWILL,
], Pingback.PINGBACK_TYPE_RISK_REVIEWED_ACCEPTED,
weekdays__contains=[self.object.date.weekday() + 1], ],
).exists() weekdays__contains=[self.object.date.weekday() + 1],
if not is_purchased and request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE]: ).exists()
raise Http404 if not is_purchased and request.user.role not in [User.ADMIN_ROLE, User.TEACHER_ROLE]:
raise Http404
else:
self.template_name = 'school/livelesson_detail_unauth.html'
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
return self.render_to_response(context) return self.render_to_response(context)

@ -2,7 +2,10 @@
<div class="section"> <div class="section">
<div class="section__center center"> <div class="section__center center">
<div class="profile"> <div class="profile">
<a class="profile__btn profile__btn_edit btn" href="{% url 'user-edit-profile' %}">Редактировать</a> <div class="profile__btns">
<a class="profile__btn profile__btn_edit btn" href="{% url 'user-edit-profile' %}">Редактировать</a>
<a data-popup=".js-popup-enter-gift-code" href="#" class="profile__btn btn btn_stroke-black">Ввести код</a>
</div>
<div class="profile__ava ava"> <div class="profile__ava ava">
{% thumbnail user.photo "120x120" crop="center" as im %} {% thumbnail user.photo "120x120" crop="center" as im %}
<img class="ava__pic" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" /> <img class="ava__pic" src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}" />
@ -91,10 +94,16 @@
<a class="btn" href="{% url 'school:school' %}">Перейти в онлайн-школу</a> <a class="btn" href="{% url 'school:school' %}">Перейти в онлайн-школу</a>
</div> </div>
{% endif %} {% 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 %} {% if paid.exists %}
{% include "course/course_items.html" with course_items=paid %} {% include "course/course_items.html" with course_items=paid %}
{% endif %} {% 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 %}
<div class="center center_xs"> <div class="center center_xs">
<div class="done"> <div class="done">
<div class="done__title">Вы пока ничего не приобрели...</div> <div class="done__title">Вы пока ничего не приобрели...</div>

@ -24,7 +24,7 @@ from apps.config.models import Config
from apps.course.models import Course from apps.course.models import Course
from apps.notification.utils import send_email from apps.notification.utils import send_email
from apps.school.models import SchoolSchedule 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 apps.user.models import AuthorRequest, EmailSubscription, SubscriptionCategory
from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm from .forms import AuthorRequesForm, UserEditForm, WithdrawalForm
@ -59,6 +59,7 @@ class ProfileView(TemplateView):
author=self.object, author=self.object,
) )
context['is_author'] = context['published'] or self.request.user.role == User.AUTHOR_ROLE 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( context['paid'] = Course.objects.filter(
payments__in=CoursePayment.objects.filter( payments__in=CoursePayment.objects.filter(
user=self.object, user=self.object,

@ -9,7 +9,7 @@ POSTGRES_PASSWORD=GPVs/E/{5&qe
DJANGO_SETTINGS_MODULE=project.settings DJANGO_SETTINGS_MODULE=project.settings
DATABASE_SERVICE_HOST=db DATABASE_SERVICE_HOST=db
SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j
MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390cb MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390c
MAILGUN_SENDER_DOMAIN=mail.9ev.ru MAILGUN_SENDER_DOMAIN=mail.9ev.ru
DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru
TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550 TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550

@ -9,7 +9,7 @@ POSTGRES_PASSWORD=GPVs/E/{5&qe
DJANGO_SETTINGS_MODULE=project.settings DJANGO_SETTINGS_MODULE=project.settings
DATABASE_SERVICE_HOST=db DATABASE_SERVICE_HOST=db
SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j SECRET_KEY=jelm*91lj(_-o20+6^a+bgv!4s6e_efry^#+f#=1ak&s1xr-2j
MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390cb MAILGUN_API_KEY=key-ec6af2d43d031d59bff6b1c8fb9390c
MAILGUN_SENDER_DOMAIN=mail.9ev.ru MAILGUN_SENDER_DOMAIN=mail.9ev.ru
DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru DEFAULT_FROM_EMAIL=postmaster@mail.9ev.ru
TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550 TWILIO_ACCOUNT=ACdf4a96b776cc764bc3ec0f0e136ba550

@ -185,8 +185,8 @@ LOGIN_URL = '/'
# https://github.com/anymail/django-anymail # https://github.com/anymail/django-anymail
ANYMAIL = { ANYMAIL = {
'MAILGUN_API_KEY': os.getenv('MAILGUN_API_KEY', 'key-ec6af2d43d031d59bff6b1c8fb9390cb'), 'MAILGUN_API_KEY': os.getenv('MAILGUN_API_KEY', ''),
'MAILGUN_SENDER_DOMAIN': os.getenv('MAILGUN_SENDER_DOMAIN', 'mail.9ev.ru'), 'MAILGUN_SENDER_DOMAIN': os.getenv('MAILGUN_SENDER_DOMAIN', ''),
} }
EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend' EMAIL_BACKEND = 'anymail.backends.mailgun.EmailBackend'
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'postmaster@mail.9ev.ru') DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'postmaster@mail.9ev.ru')

@ -1,11 +1,13 @@
{% if baner %} {% if baner %}
<div class="banner" style="/* background-image: url(); */ display: none;" data-banner="{{baner.id}}"> <div class="banner" style="/* background-image: url(); */ display: none;" data-banner="{{baner.id}}">
{% if request.user_agent.is_mobile %} {% if request.user_agent.is_mobile %}
<a href="{{ baner.url }}"> <a href="{{ baner.url }}" class="banner__image-wrap">
<img class="banner__image" src="{{ baner.image.url }}" /> <img class="banner__image" src="{{ baner.image.url }}" />
</a> </a>
{% else %} {% else %}
<img class="banner__image" src="{{ baner.image.url }}" /> <div class="banner__image-wrap">
<img class="banner__image" src="{{ baner.image.url }}" />
</div>
<div class="banner__content"> <div class="banner__content">
<span class="banner__text">{{ baner.text }}</span> <span class="banner__text">{{ baner.text }}</span>
<a href="{{ baner.url }}" class="banner__link">{{ baner.button_text }}</a> <a href="{{ baner.url }}" class="banner__link">{{ baner.button_text }}</a>

@ -41,6 +41,10 @@
<div class="header__group"> <div class="header__group">
<a class="header__section" href="https://blog.lil.school">БЛОГ</a> <a class="header__section" href="https://blog.lil.school">БЛОГ</a>
</div> </div>
<div class="header__group">
<a class="header__section {% active_link 'gift-certificates' %}"
href="{% url 'gift-certificates' %}">СЕРТИФИКАТЫ</a>
</div>
</nav> </nav>
</div> </div>
{% include 'templates/blocks/user_menu.html' %} {% include 'templates/blocks/user_menu.html' %}

@ -28,6 +28,7 @@
referrer: '{{ referrer.id|default:'' }}', referrer: '{{ referrer.id|default:'' }}',
referrerName: '{% if referrer %}{{ referrer.get_full_name }}{% endif %}', referrerName: '{% if referrer %}{{ referrer.get_full_name }}{% endif %}',
isReferralUrl: {{ is_referral_url|yesno:"true,false" }}, isReferralUrl: {{ is_referral_url|yesno:"true,false" }},
isGiftCertificateUrl: {{ is_gift_certificate_url|yesno:"true,false" }},
}, },
}; };
</script> </script>

@ -0,0 +1,24 @@
{% load static %}
<div class="popup js-popup-enter-gift-code">
<div class="popup__wrap popup__wrap_sm js-popup-wrap">
<button class="popup__close js-popup-close">
<svg class="icon icon-close">
<use xlink:href={% static "img/sprite.svg" %}#icon-close></use>
</svg>
</button>
<div class="popup__body">
<div class="enter-gift-code" style="padding:20px 20px 25px;">
<div class="subtitle2">Введите код из подарочного сертификата</div>
<div class="field">
<div class="field__label">КОД</div>
<div class="field__wrap"><input class="field__input enter-gift-code__code" type="text"
placeholder="" tabindex="1"></div>
<div class="enter-gift-code__error field__error"></div>
</div>
<div style="text-align: center;">
<a href="#" class="enter-gift-code__btn btn btn_light" tabindex="3">Подтвердить</a>
</div>
</div>
</div>
</div>
</div>

@ -0,0 +1,31 @@
{% load static %}
<div class="popup js-popup-gift-certificate">
<div class="popup__wrap popup__wrap_sm js-popup-wrap">
<button class="popup__close js-popup-close">
<svg class="icon icon-close">
<use xlink:href={% static "img/sprite.svg" %}#icon-close></use>
</svg>
</button>
<div class="popup__body" style="padding: 20px;">
{% if user_gift_certificate.user.id != request.user.id %}
<div class="subtitle2">{{ user_gift_certificate.user.get_full_name }}
подарил{% if user_gift_certificate.user.gender == 'f' %}а{% endif %} вам Сертификат!</div>
{% else %}
<div class="subtitle2">Получите подарочный сертификат на сумму {{ gift_certificate.price|floatformat:"-2" }}₽!</div>
{% endif %}
<div class="gift-certificates__item" style="margin: 0;">
<div class="gift-certificates__preview theme_pink2"></div>
<div class="gift-certificates__details">
<span class="gift-certificates__title theme theme_pink2">подарочный сертификат</span>
<div class="gift-certificates__price">{{ gift_certificate.price|floatformat:"-2" }}₽</div>
</div>
<a class="gift-certificates__buy-btn btn"
{% if request.user.is_authenticated %}
href="{% url 'gift-certificate-get' user_gift_certificate.code %}"
{% else %}
href="#" data-popup=".js-popup-auth" data-auth-next-url="{% url 'gift-certificate-get' user_gift_certificate.code %}"
{% endif %}>Получить сертификат</a>
</div>
</div>
</div>
</div>

@ -26,6 +26,10 @@
{% endif %} {% endif %}
class="main__btn btn" class="main__btn btn"
>{% if not school_schedule.weekday in school_schedules_purchased %}Получить доступ{% else %}Смотреть урок{% endif %}</a> >{% if not school_schedule.weekday in school_schedules_purchased %}Получить доступ{% else %}Смотреть урок{% endif %}</a>
{% if not is_purchased and not is_purchased_future %}
<a class="main__btn btn btn_stroke-black" href="{% url 'gift-certificates' %}">Подарить другу</a>
{% endif %}
</div> </div>
{% elif user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %} {% elif user.is_authenticated and online_coming_soon and school_schedule and school_schedule.start_at_humanize %}
<div class=""> <div class="">
@ -45,6 +49,10 @@
{% endif %} {% endif %}
class="main__btn btn" class="main__btn btn"
>{% if not school_schedule.weekday in school_schedules_purchased %}Получить доступ{% else %}Смотреть урок{% endif %}</a> >{% if not school_schedule.weekday in school_schedules_purchased %}Получить доступ{% else %}Смотреть урок{% endif %}</a>
{% if not is_purchased and not is_purchased_future %}
<a class="main__btn btn btn_stroke-black" href="{% url 'gift-certificates' %}">Подарить другу</a>
{% endif %}
</div> </div>
{% else %} {% else %}
<div class="main__subtitle"> <div class="main__subtitle">
@ -59,6 +67,7 @@
> >
купить доступ от {{ min_school_price }} руб./месяц купить доступ от {{ min_school_price }} руб./месяц
</a> </a>
<a class="main__btn btn btn_stroke-black" href="{% url 'gift-certificates' %}">Подарить другу</a>
{% else %} {% else %}
<a class="main__btn btn btn_white" href="{% url 'school:school' %}">Подробнее</a> <a class="main__btn btn btn_white" href="{% url 'school:school' %}">Подробнее</a>
{% endif %} {% endif %}

@ -18,9 +18,9 @@
<meta property="og:title" content="{% block ogtitle %}Онлайн-курсы Lil School{% endblock ogtitle %}"> <meta property="og:title" content="{% block ogtitle %}Онлайн-курсы Lil School{% endblock ogtitle %}">
{% comment %} <meta property="og:type" content="article"> {% endcomment %} {% comment %} <meta property="og:type" content="article"> {% endcomment %}
<meta property="og:url" content="{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}"> <meta property="og:url" content="{% block ogurl %}{{ request.build_absolute_uri }}{% endblock ogurl %}">
<meta property="og:image" content="http://lil.school/static/img/video-1.jpg"> <meta property="og:image" content="{% block ogimage %}http://{% setting 'MAIN_HOST' %}{% static 'img/video-1.jpg' %}{% endblock ogimage %}">
<meta property="og:image:width" content="597" /> <meta property="og:image:width" content="{% block ogimage-width %}597{% endblock ogimage-width %}" />
<meta property="og:image:height" content="318" /> <meta property="og:image:height" content="{% block ogimage-height %}318{% endblock ogimage-height %}" />
<meta property="og:description" content="{% block ogdescription %}Lil School — первая образовательная онлайн-платформа креативного мышления для детей{% endblock ogdescription %}"> <meta property="og:description" content="{% block ogdescription %}Lil School — первая образовательная онлайн-платформа креативного мышления для детей{% endblock ogdescription %}">
<meta property="og:site_name" content="Онлайн-курсы Lil School"> <meta property="og:site_name" content="Онлайн-курсы Lil School">
<meta property="og:locale" content="ru_RU"> <meta property="og:locale" content="ru_RU">
@ -145,6 +145,10 @@
{% if course %} {% if course %}
{% include "templates/blocks/popup_course_buy.html" %} {% include "templates/blocks/popup_course_buy.html" %}
{% endif %} {% 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_course_lock.html" %}
{% include "templates/blocks/popup_subscribe.html" %} {% include "templates/blocks/popup_subscribe.html" %}
{% include "templates/blocks/popup_capture_email.html" %} {% include "templates/blocks/popup_capture_email.html" %}

@ -34,8 +34,8 @@ from apps.user.views import (
from apps.payment.views import ( from apps.payment.views import (
CourseBuySuccessView, CourseBuyView, CourseBuySuccessView, CourseBuyView,
PaymentwallCallbackView, SchoolBuySuccessView, PaymentwallCallbackView, SchoolBuySuccessView,
SchoolBuyView, SchoolBuyView, GiftCertificatesView, GiftCertificateBuyView,
) GiftCertificateBuySuccessView, GiftCertificateGetView)
from .views import AboutView, IndexView, SchoolSchedulesView from .views import AboutView, IndexView, SchoolSchedulesView
@ -91,6 +91,11 @@ urlpatterns = [
path('contest/<str:slug>/', ContestView.as_view(), name='contest'), path('contest/<str:slug>/', ContestView.as_view(), name='contest'),
path('contest-work/<int:pk>/', ContestWorkView.as_view(), name='contest_work'), path('contest-work/<int:pk>/', ContestWorkView.as_view(), name='contest_work'),
path('contest-work/<int:contest_work_id>/comment', contest_work_comment, name='contest_work_comment'), path('contest-work/<int:contest_work_id>/comment', contest_work_comment, name='contest_work_comment'),
path('gift-certificates', GiftCertificatesView.as_view(), name='gift-certificates'),
path('gift-certificate/<int:pk>/checkout', GiftCertificateBuyView.as_view(), name='gift-certificate-checkout'),
path('payments/gift-certificate/<int:payment_id>/success', GiftCertificateBuySuccessView.as_view(),
name='gift-certificate-payment-success'),
path('gift-certificate/<str:slug>/get', GiftCertificateGetView.as_view(), name='gift-certificate-get'),
] ]

@ -9,7 +9,7 @@ from paymentwall.pingback import Pingback
from apps.course.models import Course from apps.course.models import Course
from apps.school.models import SchoolSchedule from apps.school.models import SchoolSchedule
from apps.payment.models import SchoolPayment from apps.payment.models import SchoolPayment, UserGiftCertificate
User = get_user_model() User = get_user_model()
@ -23,6 +23,7 @@ class IndexView(TemplateView):
def get_context_data(self): def get_context_data(self):
referrer = self.request.GET.get('referrer') referrer = self.request.GET.get('referrer')
user_gift_certificate = self.request.GET.get('gift-certificate')
is_referral_url = bool(referrer) is_referral_url = bool(referrer)
context = super().get_context_data() context = super().get_context_data()
@ -77,7 +78,17 @@ class IndexView(TemplateView):
else: else:
referrer = None 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({ 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, 'referrer': referrer,
'is_referral_url': is_referral_url, 'is_referral_url': is_referral_url,
'online': online, 'online': online,

@ -33,3 +33,4 @@ django-imagekit
pusher==2.0.1 pusher==2.0.1
short_url short_url
sendgrid sendgrid
drf_dynamic_fields

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -245,6 +245,21 @@ $(document).ready(function () {
} }
popup.data('next-url', nextUrl); 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){ $('.js-popup-close').on('click', function(e){
@ -271,7 +286,13 @@ $(document).ready(function () {
} }
}); });
function showPopup(popupName){
if(window.LIL_STORE.flags.isGiftCertificateUrl){
popup = $('.js-popup-gift-certificate');
showPopup();
}
function showPopup(){
if(! popup && popupName){ if(! popup && popupName){
popup = $(popupName); popup = $(popupName);
} }

@ -26,12 +26,15 @@
$pink: #FF9393 $pink: #FF9393
$pink-light: #FDF8F9 $pink-light: #FDF8F9
$pink2: #FEB9B9
$purple: #B995D9 $purple: #B995D9
$green: #8ECFC0 $green: #8ECFC0
$green-light: #5BD700 $green-light: #5BD700
$cyan: #B6DFD6
$gray: #A7A7A7 $gray: #A7A7A7
$blue: #4A90E2 $blue: #4A90E2
$viol: #B995D9 $viol: #B995D9
$viol2: #A186BD
$bg: #f8f8f8 $bg: #f8f8f8
$border: #E6E6E6 $border: #E6E6E6
$cl: #191919 $cl: #191919
@ -206,6 +209,10 @@ button
&_pink &_pink
background: #ff9393 background: #ff9393
color: white color: white
&_stroke-black
background: none
border: 1px solid $cl
color: $cl
&_md &_md
padding: 18px 24px 17px padding: 18px 24px 17px
+m +m
@ -1063,6 +1070,7 @@ a[name]
flex: 0 0 40px flex: 0 0 40px
&__info &__info
flex: 0 0 calc(100% - 50px) flex: 0 0 calc(100% - 50px)
display: flex
&__name, &__name,
&__date &__date
+fb +fb
@ -1070,9 +1078,8 @@ a[name]
text-transform: uppercase text-transform: uppercase
letter-spacing: 0.5px letter-spacing: 0.5px
line-height: 1.1 line-height: 1.1
&__name
margin-bottom: 5px
&__meta &__meta
flex: 1
display: flex display: flex
align-items: center align-items: center
&__date &__date
@ -1635,6 +1642,12 @@ a.grey-link
color: $green color: $green
&_violet &_violet
color: $viol color: $viol
&_violet2
color: $viol2
&_pink2
color: $pink2
&_cyan
color: $cyan
&_lg &_lg
font-size: 20px font-size: 20px
letter-spacing: 4px letter-spacing: 4px
@ -2546,6 +2559,7 @@ a.grey-link
&__item &__item
display: flex display: flex
align-items: center align-items: center
color: inherit
&__item &__item
//&:not(:last-child) //&:not(:last-child)
margin-right: 40px margin-right: 40px
@ -3202,12 +3216,16 @@ a.grey-link
background: transparent background: transparent
border: 1px solid $gray border: 1px solid $gray
color: $gray color: $gray
&_edit margin-bottom: 10px
position: absolute width: 100%
top: 0 &__btns
right: 0 position: absolute
+m top: 0
display: none !important right: 0
display: flex
flex-direction: column
+m
display: none !important
&__row &__row
display: flex display: flex
margin-bottom: 25px margin-bottom: 25px
@ -3987,7 +4005,7 @@ a.grey-link
padding-right: 20px padding-right: 20px
flex: 0 0 90px flex: 0 0 90px
&--content &--content
flex: 0 0 calc(100% - 254px) flex: 1
+m +m
flex: 0 0 calc(100% - 114px) flex: 0 0 calc(100% - 114px)
&--toggle &--toggle
@ -4306,6 +4324,13 @@ a
background: white background: white
+m +m
height: auto height: auto
&__image-wrap
background: -moz-linear-gradient(top, rgba(0,0,0,0) 0%, rgba(159,159,159,0) 72%, rgba(221,221,221,0.65) 100%)
background: -webkit-linear-gradient(top, rgba(0,0,0,0) 0%,rgba(159,159,159,0) 72%,rgba(221,221,221,0.65) 100%)
background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(159,159,159,0) 72%,rgba(221,221,221,0.65) 100%)
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00000000', endColorstr='#a6dddddd',GradientType=0 )
width: 100%
height: 100%
&__image &__image
height: 100% height: 100%
+m +m
@ -4410,3 +4435,64 @@ a
&__lil-coin-img &__lil-coin-img
margin-bottom: -5px margin-bottom: -5px
margin-right: 4px 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

@ -14,6 +14,7 @@ module.exports = {
sprite: glob('./src/icons/*.svg'), sprite: glob('./src/icons/*.svg'),
images: glob('./src/img/*.*'), images: glob('./src/img/*.*'),
imagesCertificates: glob('./src/img/user-certificates/*'), imagesCertificates: glob('./src/img/user-certificates/*'),
imagesGiftCertificates: glob('./src/img/gift-certificates/*'),
fonts: glob('./src/fonts/*') fonts: glob('./src/fonts/*')
}, },
output: { output: {
@ -85,7 +86,8 @@ module.exports = {
}, },
{ {
test: /\.(png|gif|jpg|svg)$/, 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/' loader: 'file-loader?name=[name].[ext]&outputPath=./img/'
}, },
{ {
@ -93,6 +95,11 @@ module.exports = {
include: path.resolve(__dirname, 'src/img/user-certificates'), include: path.resolve(__dirname, 'src/img/user-certificates'),
loader: 'file-loader?name=[name].[ext]&outputPath=./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]+)?$/, test: /\.(ttf|otf|eot|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader?name=[name].[ext]' loader: 'file-loader?name=[name].[ext]'

Loading…
Cancel
Save